1/*
2 * Copyright (C) 2011 Samsung Electronics
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "EflKeyboardUtilities.h"
30
31#include "KeyboardEvent.h"
32#include "WindowsKeyboardCodes.h"
33#include <wtf/HashMap.h>
34#include <wtf/HexNumber.h>
35#include <wtf/NeverDestroyed.h>
36#include <wtf/text/StringBuilder.h>
37#include <wtf/text/StringHash.h>
38#include <wtf/text/WTFString.h>
39
40namespace WebCore {
41
42static HashMap<String, String>& keyMap()
43{
44    static NeverDestroyed<HashMap<String, String>> keyMap;
45    return keyMap;
46}
47
48static HashMap<String, int>& windowsKeyMap()
49{
50    static NeverDestroyed<HashMap<String, int>> windowsKeyMap;
51    return windowsKeyMap;
52}
53
54static HashMap<int, const char*>& keyDownCommandsMap()
55{
56    static NeverDestroyed<HashMap<int, const char*>> keyDownCommandsMap;
57    return keyDownCommandsMap;
58}
59
60static HashMap<int, const char*>& keyPressCommandsMap()
61{
62    static NeverDestroyed<HashMap<int, const char*>> keyPressCommandsMap;
63    return keyPressCommandsMap;
64}
65
66static inline void addCharactersToKeyMap(const char from, const char to)
67{
68    for (char c = from; c <= to; c++) {
69        StringBuilder builder;
70        builder.appendLiteral("U+");
71        appendUnsignedAsHexFixedSize(c, builder, 4, WTF::Uppercase);
72        keyMap().set(String(&c, 1), builder.toString());
73    }
74}
75
76static void createKeyMap()
77{
78    for (unsigned int i = 1; i < 25; i++) {
79        StringBuilder builder;
80        builder.append('F');
81        builder.appendNumber(i);
82        String key = builder.toString();
83        keyMap().set(key, key);
84    }
85    keyMap().set(ASCIILiteral("Alt_L"), ASCIILiteral("Alt"));
86    keyMap().set(ASCIILiteral("ISO_Level3_Shift"), ASCIILiteral("Alt"));
87    keyMap().set(ASCIILiteral("Menu"), ASCIILiteral("Alt"));
88    keyMap().set(ASCIILiteral("Shift_L"), ASCIILiteral("Shift"));
89    keyMap().set(ASCIILiteral("Shift_R"), ASCIILiteral("Shift"));
90    keyMap().set(ASCIILiteral("Down"), ASCIILiteral("Down"));
91    keyMap().set(ASCIILiteral("End"), ASCIILiteral("End"));
92    keyMap().set(ASCIILiteral("Return"), ASCIILiteral("Enter"));
93    keyMap().set(ASCIILiteral("KP_Enter"), ASCIILiteral("Enter"));
94    keyMap().set(ASCIILiteral("Home"), ASCIILiteral("Home"));
95    keyMap().set(ASCIILiteral("Insert"), ASCIILiteral("Insert"));
96    keyMap().set(ASCIILiteral("Left"), ASCIILiteral("Left"));
97    keyMap().set(ASCIILiteral("Next"), ASCIILiteral("PageDown"));
98    keyMap().set(ASCIILiteral("Prior"), ASCIILiteral("PageUp"));
99    keyMap().set(ASCIILiteral("Right"), ASCIILiteral("Right"));
100    keyMap().set(ASCIILiteral("Up"), ASCIILiteral("Up"));
101    keyMap().set(ASCIILiteral("Delete"), ASCIILiteral("U+007F"));
102    keyMap().set(ASCIILiteral("Tab"), ASCIILiteral("U+0009"));
103    keyMap().set(ASCIILiteral("ISO_Left_Tab"), ASCIILiteral("U+0009"));
104    keyMap().set(ASCIILiteral("BackSpace"), ASCIILiteral("U+0008"));
105    keyMap().set(ASCIILiteral("space"), ASCIILiteral("U+0020"));
106    keyMap().set(ASCIILiteral("Escape"), ASCIILiteral("U+001B"));
107    keyMap().set(ASCIILiteral("Print"), ASCIILiteral("PrintScreen"));
108    // Keypad location
109    keyMap().set(ASCIILiteral("KP_Left"), ASCIILiteral("Left"));
110    keyMap().set(ASCIILiteral("KP_Right"), ASCIILiteral("Right"));
111    keyMap().set(ASCIILiteral("KP_Up"), ASCIILiteral("Up"));
112    keyMap().set(ASCIILiteral("KP_Down"), ASCIILiteral("Down"));
113    keyMap().set(ASCIILiteral("KP_Prior"), ASCIILiteral("PageUp"));
114    keyMap().set(ASCIILiteral("KP_Next"), ASCIILiteral("PageDown"));
115    keyMap().set(ASCIILiteral("KP_Home"), ASCIILiteral("Home"));
116    keyMap().set(ASCIILiteral("KP_End"), ASCIILiteral("End"));
117    keyMap().set(ASCIILiteral("KP_Insert"), ASCIILiteral("Insert"));
118    keyMap().set(ASCIILiteral("KP_Delete"), ASCIILiteral("U+007F"));
119
120    addCharactersToKeyMap('a', 'z');
121    addCharactersToKeyMap('A', 'Z');
122    addCharactersToKeyMap('0', '9');
123}
124
125static inline void addCharactersToWinKeyMap(const char from, const char to, const int baseCode)
126{
127    int i = 0;
128    for (char c = from; c <= to; c++, i++)
129        windowsKeyMap().set(String(&c, 1), baseCode + i);
130}
131
132static void createWindowsKeyMap()
133{
134    windowsKeyMap().set(ASCIILiteral("Return"), VK_RETURN);
135    windowsKeyMap().set(ASCIILiteral("KP_Return"), VK_RETURN);
136    windowsKeyMap().set(ASCIILiteral("Alt_L"), VK_LMENU);
137    windowsKeyMap().set(ASCIILiteral("Alt_R"), VK_RMENU);
138    windowsKeyMap().set(ASCIILiteral("ISO_Level3_Shift"), VK_MENU);
139    windowsKeyMap().set(ASCIILiteral("Menu"), VK_MENU);
140    windowsKeyMap().set(ASCIILiteral("Shift_L"), VK_LSHIFT);
141    windowsKeyMap().set(ASCIILiteral("Shift_R"), VK_RSHIFT);
142    windowsKeyMap().set(ASCIILiteral("Control_L"), VK_LCONTROL);
143    windowsKeyMap().set(ASCIILiteral("Control_R"), VK_RCONTROL);
144    windowsKeyMap().set(ASCIILiteral("Pause"), VK_PAUSE);
145    windowsKeyMap().set(ASCIILiteral("Break"), VK_PAUSE);
146    windowsKeyMap().set(ASCIILiteral("Caps_Lock"), VK_CAPITAL);
147    windowsKeyMap().set(ASCIILiteral("Scroll_Lock"), VK_SCROLL);
148    windowsKeyMap().set(ASCIILiteral("Num_Lock"), VK_NUMLOCK);
149    windowsKeyMap().set(ASCIILiteral("Escape"), VK_ESCAPE);
150    windowsKeyMap().set(ASCIILiteral("Tab"), VK_TAB);
151    windowsKeyMap().set(ASCIILiteral("ISO_Left_Tab"), VK_TAB);
152    windowsKeyMap().set(ASCIILiteral("BackSpace"), VK_BACK);
153    windowsKeyMap().set(ASCIILiteral("space"), VK_SPACE);
154    windowsKeyMap().set(ASCIILiteral("Next"), VK_NEXT);
155    windowsKeyMap().set(ASCIILiteral("Prior"), VK_PRIOR);
156    windowsKeyMap().set(ASCIILiteral("Home"), VK_HOME);
157    windowsKeyMap().set(ASCIILiteral("End"), VK_END);
158    windowsKeyMap().set(ASCIILiteral("Right"), VK_RIGHT);
159    windowsKeyMap().set(ASCIILiteral("Left"), VK_LEFT);
160    windowsKeyMap().set(ASCIILiteral("Up"), VK_UP);
161    windowsKeyMap().set(ASCIILiteral("Down"), VK_DOWN);
162    windowsKeyMap().set(ASCIILiteral("Print"), VK_SNAPSHOT);
163    windowsKeyMap().set(ASCIILiteral("Insert"), VK_INSERT);
164    windowsKeyMap().set(ASCIILiteral("Delete"), VK_DELETE);
165
166    windowsKeyMap().set(ASCIILiteral("comma"), VK_OEM_COMMA);
167    windowsKeyMap().set(ASCIILiteral("less"), VK_OEM_COMMA);
168    windowsKeyMap().set(ASCIILiteral("period"), VK_OEM_PERIOD);
169    windowsKeyMap().set(ASCIILiteral("greater"), VK_OEM_PERIOD);
170    windowsKeyMap().set(ASCIILiteral("semicolon"), VK_OEM_1);
171    windowsKeyMap().set(ASCIILiteral("colon"), VK_OEM_1);
172    windowsKeyMap().set(ASCIILiteral("slash"), VK_OEM_2);
173    windowsKeyMap().set(ASCIILiteral("question"), VK_OEM_2);
174    windowsKeyMap().set(ASCIILiteral("grave"), VK_OEM_3);
175    windowsKeyMap().set(ASCIILiteral("asciitilde"), VK_OEM_3);
176    windowsKeyMap().set(ASCIILiteral("bracketleft"), VK_OEM_4);
177    windowsKeyMap().set(ASCIILiteral("braceleft"), VK_OEM_4);
178    windowsKeyMap().set(ASCIILiteral("backslash"), VK_OEM_5);
179    windowsKeyMap().set(ASCIILiteral("bar"), VK_OEM_5);
180    windowsKeyMap().set(ASCIILiteral("bracketright"), VK_OEM_6);
181    windowsKeyMap().set(ASCIILiteral("braceright"), VK_OEM_6);
182    windowsKeyMap().set(ASCIILiteral("apostrophe"), VK_OEM_7);
183    windowsKeyMap().set(ASCIILiteral("quotedbl"), VK_OEM_7);
184    // Keypad location
185    windowsKeyMap().set(ASCIILiteral("KP_Left"), VK_LEFT);
186    windowsKeyMap().set(ASCIILiteral("KP_Right"), VK_RIGHT);
187    windowsKeyMap().set(ASCIILiteral("KP_Up"), VK_UP);
188    windowsKeyMap().set(ASCIILiteral("KP_Down"), VK_DOWN);
189    windowsKeyMap().set(ASCIILiteral("KP_Prior"), VK_PRIOR);
190    windowsKeyMap().set(ASCIILiteral("KP_Next"), VK_NEXT);
191    windowsKeyMap().set(ASCIILiteral("KP_Home"), VK_HOME);
192    windowsKeyMap().set(ASCIILiteral("KP_End"), VK_END);
193    windowsKeyMap().set(ASCIILiteral("KP_Insert"), VK_INSERT);
194    windowsKeyMap().set(ASCIILiteral("KP_Delete"), VK_DELETE);
195    windowsKeyMap().set(ASCIILiteral("KP_Multiply"), VK_MULTIPLY);
196    windowsKeyMap().set(ASCIILiteral("KP_Subtract"), VK_SUBTRACT);
197    windowsKeyMap().set(ASCIILiteral("KP_Decimal"), VK_DECIMAL);
198    windowsKeyMap().set(ASCIILiteral("KP_Devide"), VK_DIVIDE);
199
200    // Set alphabet to the windowsKeyMap.
201    addCharactersToWinKeyMap('a', 'z', VK_A);
202    addCharactersToWinKeyMap('A', 'Z', VK_A);
203
204    // Set digits to the windowsKeyMap.
205    addCharactersToWinKeyMap('0', '9', VK_0);
206
207    // Set digits of keypad to the windowsKeyMap.
208    for (unsigned i = 0; i < 10; ++i) {
209        StringBuilder builder;
210        builder.appendLiteral("KP_");
211        builder.appendNumber(i);
212        windowsKeyMap().set(builder.toString(), VK_NUMPAD0 + i);
213    }
214
215    // Set shifted digits to the windowsKeyMap.
216    windowsKeyMap().set(ASCIILiteral("exclam"), VK_1);
217    windowsKeyMap().set(ASCIILiteral("at"), VK_2);
218    windowsKeyMap().set(ASCIILiteral("numbersign"), VK_3);
219    windowsKeyMap().set(ASCIILiteral("dollar"), VK_4);
220    windowsKeyMap().set(ASCIILiteral("percent"), VK_5);
221    windowsKeyMap().set(ASCIILiteral("asciicircum"), VK_6);
222    windowsKeyMap().set(ASCIILiteral("ampersand"), VK_7);
223    windowsKeyMap().set(ASCIILiteral("asterisk"), VK_8);
224    windowsKeyMap().set(ASCIILiteral("parenleft"), VK_9);
225    windowsKeyMap().set(ASCIILiteral("parenright"), VK_0);
226    windowsKeyMap().set(ASCIILiteral("minus"), VK_OEM_MINUS);
227    windowsKeyMap().set(ASCIILiteral("underscore"), VK_OEM_MINUS);
228    windowsKeyMap().set(ASCIILiteral("equal"), VK_OEM_PLUS);
229    windowsKeyMap().set(ASCIILiteral("plus"), VK_OEM_PLUS);
230
231    // Set F_XX keys to the windowsKeyMap.
232    for (unsigned int i = 1; i < 25; i++) {
233        StringBuilder builder;
234        builder.append('F');
235        builder.appendNumber(i);
236        windowsKeyMap().set(builder.toString(), VK_F1 + i - 1);
237    }
238}
239
240static const unsigned CtrlKey = 1 << 0;
241static const unsigned AltKey = 1 << 1;
242static const unsigned ShiftKey = 1 << 2;
243
244struct KeyDownEntry {
245    unsigned virtualKey;
246    unsigned modifiers;
247    const char* name;
248};
249
250struct KeyPressEntry {
251    unsigned charCode;
252    unsigned modifiers;
253    const char* name;
254};
255
256static const KeyDownEntry keyDownEntries[] = {
257    { VK_LEFT,   0,                  "MoveLeft"                                    },
258    { VK_LEFT,   ShiftKey,           "MoveLeftAndModifySelection"                  },
259    { VK_LEFT,   CtrlKey,            "MoveWordLeft"                                },
260    { VK_LEFT,   CtrlKey | ShiftKey, "MoveWordLeftAndModifySelection"              },
261    { VK_RIGHT,  0,                  "MoveRight"                                   },
262    { VK_RIGHT,  ShiftKey,           "MoveRightAndModifySelection"                 },
263    { VK_RIGHT,  CtrlKey,            "MoveWordRight"                               },
264    { VK_RIGHT,  CtrlKey | ShiftKey, "MoveWordRightAndModifySelection"             },
265    { VK_UP,     0,                  "MoveUp"                                      },
266    { VK_UP,     ShiftKey,           "MoveUpAndModifySelection"                    },
267    { VK_PRIOR,  ShiftKey,           "MovePageUpAndModifySelection"                },
268    { VK_DOWN,   0,                  "MoveDown"                                    },
269    { VK_DOWN,   ShiftKey,           "MoveDownAndModifySelection"                  },
270    { VK_NEXT,   ShiftKey,           "MovePageDownAndModifySelection"              },
271    { VK_PRIOR,  0,                  "MovePageUp"                                  },
272    { VK_NEXT,   0,                  "MovePageDown"                                },
273    { VK_HOME,   0,                  "MoveToBeginningOfLine"                       },
274    { VK_HOME,   ShiftKey,           "MoveToBeginningOfLineAndModifySelection"     },
275    { VK_HOME,   CtrlKey,            "MoveToBeginningOfDocument"                   },
276    { VK_HOME,   CtrlKey | ShiftKey, "MoveToBeginningOfDocumentAndModifySelection" },
277
278    { VK_END,    0,                  "MoveToEndOfLine"                             },
279    { VK_END,    ShiftKey,           "MoveToEndOfLineAndModifySelection"           },
280    { VK_END,    CtrlKey,            "MoveToEndOfDocument"                         },
281    { VK_END,    CtrlKey | ShiftKey, "MoveToEndOfDocumentAndModifySelection"       },
282
283    { VK_BACK,   0,                  "DeleteBackward"                              },
284    { VK_BACK,   ShiftKey,           "DeleteBackward"                              },
285    { VK_DELETE, 0,                  "DeleteForward"                               },
286    { VK_BACK,   CtrlKey,            "DeleteWordBackward"                          },
287    { VK_DELETE, CtrlKey,            "DeleteWordForward"                           },
288
289    { 'B',       CtrlKey,            "ToggleBold"                                  },
290    { 'I',       CtrlKey,            "ToggleItalic"                                },
291
292    { VK_ESCAPE, 0,                  "Cancel"                                      },
293    { VK_OEM_PERIOD, CtrlKey,        "Cancel"                                      },
294    { VK_TAB,    0,                  "InsertTab"                                   },
295    { VK_TAB,    ShiftKey,           "InsertBacktab"                               },
296    { VK_RETURN, 0,                  "InsertNewline"                               },
297    { VK_RETURN, CtrlKey,            "InsertNewline"                               },
298    { VK_RETURN, AltKey,             "InsertNewline"                               },
299    { VK_RETURN, AltKey | ShiftKey,  "InsertNewline"                               },
300
301    { 'C',       CtrlKey,            "Copy"                                        },
302    { 'V',       CtrlKey,            "Paste"                                       },
303    { 'X',       CtrlKey,            "Cut"                                         },
304
305    { 'A',       CtrlKey,            "SelectAll"                                   },
306    { 'Z',       CtrlKey,            "Undo"                                        },
307    { 'Z',       CtrlKey | ShiftKey, "Redo"                                        },
308    { 'Y',       CtrlKey,            "Redo"                                        },
309};
310
311static const KeyPressEntry keyPressEntries[] = {
312    { '\t',   0,                  "InsertTab"                                   },
313    { '\t',   ShiftKey,           "InsertBacktab"                               },
314    { '\r',   0,                  "InsertNewline"                               },
315    { '\r',   CtrlKey,            "InsertNewline"                               },
316    { '\r',   AltKey,             "InsertNewline"                               },
317    { '\r',   AltKey | ShiftKey,  "InsertNewline"                               },
318};
319
320static void createKeyDownCommandMap()
321{
322    for (size_t i = 0; i < WTF_ARRAY_LENGTH(keyDownEntries); ++i)
323        keyDownCommandsMap().set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);
324}
325
326static void createKeyPressCommandMap()
327{
328    for (size_t i = 0; i < WTF_ARRAY_LENGTH(keyPressEntries); ++i)
329        keyPressCommandsMap().set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name);
330}
331
332String keyIdentifierForEvasKeyName(const String& keyName)
333{
334    if (keyMap().isEmpty())
335        createKeyMap();
336
337    if (keyMap().contains(keyName))
338        return keyMap().get(keyName);
339
340    return keyName;
341}
342
343int windowsKeyCodeForEvasKeyName(const String& keyName)
344{
345    if (windowsKeyMap().isEmpty())
346        createWindowsKeyMap();
347
348    if (windowsKeyMap().contains(keyName))
349        return windowsKeyMap().get(keyName);
350
351    return 0;
352}
353
354const char* getKeyDownCommandName(const KeyboardEvent* event)
355{
356    unsigned modifiers = 0;
357    if (event->shiftKey())
358        modifiers |= ShiftKey;
359    if (event->altKey())
360        modifiers |= AltKey;
361    if (event->ctrlKey())
362        modifiers |= CtrlKey;
363
364    int mapKey = modifiers << 16 | event->keyCode();
365    if (!mapKey)
366        return 0;
367
368    if (keyDownCommandsMap().isEmpty())
369        createKeyDownCommandMap();
370
371    return keyDownCommandsMap().get(mapKey);
372}
373
374const char* getKeyPressCommandName(const KeyboardEvent* event)
375{
376    unsigned modifiers = 0;
377    if (event->shiftKey())
378        modifiers |= ShiftKey;
379    if (event->altKey())
380        modifiers |= AltKey;
381    if (event->ctrlKey())
382        modifiers |= CtrlKey;
383
384    int mapKey = modifiers << 16 | event->charCode();
385    if (!mapKey)
386        return 0;
387
388    if (keyPressCommandsMap().isEmpty())
389        createKeyPressCommandMap();
390
391    return keyPressCommandsMap().get(mapKey);
392}
393
394} // namespace WebCore
395