]> git.proxmox.com Git - mirror_novnc.git/commitdiff
Use KeyboardEvent.key too look up keysyms
authorPierre Ossman <ossman@cendio.se>
Thu, 13 Apr 2017 12:50:55 +0000 (14:50 +0200)
committerPierre Ossman <ossman@cendio.se>
Thu, 4 May 2017 10:13:48 +0000 (12:13 +0200)
And emulate it on browsers where it is missing or incorrect. This
makes the code more future oriented as it primarily uses the standardised
fields.

core/input/domkeytable.js [new file with mode: 0644]
core/input/fixedkeys.js
core/input/keysym.js
core/input/util.js
tests/test.helper.js
tests/test.keyboard.js

diff --git a/core/input/domkeytable.js b/core/input/domkeytable.js
new file mode 100644 (file)
index 0000000..6758c08
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2017 Pierre Ossman for Cendio AB
+ * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
+ */
+
+import KeyTable from "./keysym.js"
+
+/*
+ * Mapping between HTML key values and VNC/X11 keysyms for "special"
+ * keys that cannot be handled via their Unicode codepoint.
+ *
+ * See https://www.w3.org/TR/uievents-key/ for possible values.
+ */
+
+var DOMKeyTable = {};
+
+function addStandard(key, standard)
+{
+    if (standard === undefined) throw "Undefined keysym for key \"" + key + "\"";
+    if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
+    DOMKeyTable[key] = [standard, standard, standard, standard];
+}
+
+function addLeftRight(key, left, right)
+{
+    if (left === undefined) throw "Undefined keysym for key \"" + key + "\"";
+    if (right === undefined) throw "Undefined keysym for key \"" + key + "\"";
+    if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
+    DOMKeyTable[key] = [left, left, right, left];
+}
+
+function addNumpad(key, standard, numpad)
+{
+    if (standard === undefined) throw "Undefined keysym for key \"" + key + "\"";
+    if (numpad === undefined) throw "Undefined keysym for key \"" + key + "\"";
+    if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
+    DOMKeyTable[key] = [standard, standard, standard, numpad];
+}
+
+// 2.2. Modifier Keys
+
+addLeftRight("Alt", KeyTable.XK_Alt_L, KeyTable.XK_Alt_R);
+addStandard("AltGraph", KeyTable.XK_ISO_Level3_Shift);
+addStandard("CapsLock", KeyTable.XK_Caps_Lock);
+addLeftRight("Control", KeyTable.XK_Control_L, KeyTable.XK_Control_R);
+// - Fn
+// - FnLock
+addLeftRight("Hyper", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
+addLeftRight("Meta", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
+addStandard("NumLock", KeyTable.XK_Num_Lock);
+addStandard("ScrollLock", KeyTable.XK_Scroll_Lock);
+addLeftRight("Shift", KeyTable.XK_Shift_L, KeyTable.XK_Shift_R);
+addLeftRight("Super", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
+// - Symbol
+// - SymbolLock
+
+// 2.3. Whitespace Keys
+
+addNumpad("Enter", KeyTable.XK_Return, KeyTable.XK_KP_Enter);
+addStandard("Tab", KeyTable.XK_Tab);
+addNumpad(" ", KeyTable.XK_space, KeyTable.XK_KP_Space);
+
+// 2.4. Navigation Keys
+
+addNumpad("ArrowDown", KeyTable.XK_Down, KeyTable.XK_KP_Down);
+addNumpad("ArrowUp", KeyTable.XK_Up, KeyTable.XK_KP_Up);
+addNumpad("ArrowLeft", KeyTable.XK_Left, KeyTable.XK_KP_Left);
+addNumpad("ArrowRight", KeyTable.XK_Right, KeyTable.XK_KP_Right);
+addNumpad("End", KeyTable.XK_End, KeyTable.XK_KP_End);
+addNumpad("Home", KeyTable.XK_Home, KeyTable.XK_KP_Home);
+addNumpad("PageDown", KeyTable.XK_Next, KeyTable.XK_KP_Next);
+addNumpad("PageUp", KeyTable.XK_Prior, KeyTable.XK_KP_Prior);
+
+// 2.5. Editing Keys
+
+addStandard("Backspace", KeyTable.XK_BackSpace);
+addStandard("Clear", KeyTable.XK_Clear);
+addStandard("Copy", KeyTable.XF86XK_Copy);
+// - CrSel
+addStandard("Cut", KeyTable.XF86XK_Cut);
+addNumpad("Delete", KeyTable.XK_Delete, KeyTable.XK_KP_Delete);
+// - EraseEof
+// - ExSel
+addNumpad("Insert", KeyTable.XK_Insert, KeyTable.XK_KP_Insert);
+addStandard("Paste", KeyTable.XF86XK_Paste);
+addStandard("Redo", KeyTable.XK_Redo);
+addStandard("Undo", KeyTable.XK_Undo);
+
+// 2.6. UI Keys
+
+// - Accept
+// - Again (could just be XK_Redo)
+// - Attn
+addStandard("Cancel", KeyTable.XK_Cancel);
+addStandard("ContextMenu", KeyTable.XK_Menu);
+addStandard("Escape", KeyTable.XK_Escape);
+addStandard("Execute", KeyTable.XK_Execute);
+addStandard("Find", KeyTable.XK_Find);
+addStandard("Help", KeyTable.XK_Help);
+addStandard("Pause", KeyTable.XK_Pause);
+// - Play
+// - Props
+addStandard("Select", KeyTable.XK_Select);
+addStandard("ZoomIn", KeyTable.XF86XK_ZoomIn);
+addStandard("ZoomOut", KeyTable.XF86XK_ZoomOut);
+
+// 2.7. Device Keys
+
+addStandard("BrightnessDown", KeyTable.XF86XK_MonBrightnessDown);
+addStandard("BrightnessUp", KeyTable.XF86XK_MonBrightnessUp);
+addStandard("Eject", KeyTable.XF86XK_Eject);
+addStandard("LogOff", KeyTable.XF86XK_LogOff);
+addStandard("Power", KeyTable.XF86XK_PowerOff);
+addStandard("PowerOff", KeyTable.XF86XK_PowerDown);
+addStandard("PrintScreen", KeyTable.XK_Print);
+addStandard("Hibernate", KeyTable.XF86XK_Hibernate);
+addStandard("Standby", KeyTable.XF86XK_Standby);
+addStandard("WakeUp", KeyTable.XF86XK_WakeUp);
+
+// 2.8. IME and Composition Keys
+
+addStandard("AllCandidates", KeyTable.XK_MultipleCandidate);
+addStandard("Alphanumeric", KeyTable.XK_Eisu_Shift); // could also be _Eisu_Toggle
+addStandard("CodeInput", KeyTable.XK_Codeinput);
+addStandard("Compose", KeyTable.XK_Multi_key);
+addStandard("Convert", KeyTable.XK_Henkan);
+// - Dead
+// - FinalMode
+addStandard("GroupFirst", KeyTable.XK_ISO_First_Group);
+addStandard("GroupLast", KeyTable.XK_ISO_Last_Group);
+addStandard("GroupNext", KeyTable.XK_ISO_Next_Group);
+addStandard("GroupPrevious", KeyTable.XK_ISO_Prev_Group);
+// - ModeChange (XK_Mode_switch is often used for AltGr)
+// - NextCandidate
+addStandard("NonConvert", KeyTable.XK_Muhenkan);
+addStandard("PreviousCandidate", KeyTable.XK_PreviousCandidate);
+// - Process
+addStandard("SingleCandidate", KeyTable.XK_SingleCandidate);
+addStandard("HangulMode", KeyTable.XK_Hangul);
+addStandard("HanjaMode", KeyTable.XK_Hangul_Hanja);
+addStandard("JunjuaMode", KeyTable.XK_Hangul_Jeonja);
+addStandard("Eisu", KeyTable.XK_Eisu_toggle);
+addStandard("Hankaku", KeyTable.XK_Hankaku);
+addStandard("Hiragana", KeyTable.XK_Hiragana);
+addStandard("HiraganaKatakana", KeyTable.XK_Hiragana_Katakana);
+addStandard("KanaMode", KeyTable.XK_Kana_Shift); // could also be _Kana_Lock
+addStandard("KanjiMode", KeyTable.XK_Kanji);
+addStandard("Katakana", KeyTable.XK_Katakana);
+addStandard("Romaji", KeyTable.XK_Romaji);
+addStandard("Zenkaku", KeyTable.XK_Zenkaku);
+addStandard("ZenkakuHanaku", KeyTable.XK_Zenkaku_Hankaku);
+
+// 2.9. General-Purpose Function Keys
+
+addStandard("F1", KeyTable.XK_F1);
+addStandard("F2", KeyTable.XK_F2);
+addStandard("F3", KeyTable.XK_F3);
+addStandard("F4", KeyTable.XK_F4);
+addStandard("F5", KeyTable.XK_F5);
+addStandard("F6", KeyTable.XK_F6);
+addStandard("F7", KeyTable.XK_F7);
+addStandard("F8", KeyTable.XK_F8);
+addStandard("F9", KeyTable.XK_F9);
+addStandard("F10", KeyTable.XK_F10);
+addStandard("F11", KeyTable.XK_F11);
+addStandard("F12", KeyTable.XK_F12);
+addStandard("F13", KeyTable.XK_F13);
+addStandard("F14", KeyTable.XK_F14);
+addStandard("F15", KeyTable.XK_F15);
+addStandard("F16", KeyTable.XK_F16);
+addStandard("F17", KeyTable.XK_F17);
+addStandard("F18", KeyTable.XK_F18);
+addStandard("F19", KeyTable.XK_F19);
+addStandard("F20", KeyTable.XK_F20);
+addStandard("F21", KeyTable.XK_F21);
+addStandard("F22", KeyTable.XK_F22);
+addStandard("F23", KeyTable.XK_F23);
+addStandard("F24", KeyTable.XK_F24);
+addStandard("F25", KeyTable.XK_F25);
+addStandard("F26", KeyTable.XK_F26);
+addStandard("F27", KeyTable.XK_F27);
+addStandard("F28", KeyTable.XK_F28);
+addStandard("F29", KeyTable.XK_F29);
+addStandard("F30", KeyTable.XK_F30);
+addStandard("F31", KeyTable.XK_F31);
+addStandard("F32", KeyTable.XK_F32);
+addStandard("F33", KeyTable.XK_F33);
+addStandard("F34", KeyTable.XK_F34);
+addStandard("F35", KeyTable.XK_F35);
+// - Soft1...
+
+// 2.10. Multimedia Keys
+
+// - ChannelDown
+// - ChannelUp
+addStandard("Close", KeyTable.XF86XK_Close);
+addStandard("MailForward", KeyTable.XF86XK_MailForward);
+addStandard("MailReply", KeyTable.XF86XK_Reply);
+addStandard("MainSend", KeyTable.XF86XK_Send);
+addStandard("MediaFastForward", KeyTable.XF86XK_AudioForward);
+addStandard("MediaPause", KeyTable.XF86XK_AudioPause);
+addStandard("MediaPlay", KeyTable.XF86XK_AudioPlay);
+addStandard("MediaRecord", KeyTable.XF86XK_AudioRecord);
+addStandard("MediaRewind", KeyTable.XF86XK_AudioRewind);
+addStandard("MediaStop", KeyTable.XF86XK_AudioStop);
+addStandard("MediaTrackNext", KeyTable.XF86XK_AudioNext);
+addStandard("MediaTrackPrevious", KeyTable.XF86XK_AudioPrev);
+addStandard("New", KeyTable.XF86XK_New);
+addStandard("Open", KeyTable.XF86XK_Open);
+addStandard("Print", KeyTable.XK_Print);
+addStandard("Save", KeyTable.XF86XK_Save);
+addStandard("SpellCheck", KeyTable.XF86XK_Spell);
+
+// 2.11. Multimedia Numpad Keys
+
+// - Key11
+// - Key12
+
+// 2.12. Audio Keys
+
+// - AudioBalanceLeft
+// - AudioBalanceRight
+// - AudioBassDown
+// - AudioBassBoostDown
+// - AudioBassBoostToggle
+// - AudioBassBoostUp
+// - AudioBassUp
+// - AudioFaderFront
+// - AudioFaderRear
+// - AudioSurroundModeNext
+// - AudioTrebleDown
+// - AudioTrebleUp
+addStandard("AudioVolumeDown", KeyTable.XF86XK_AudioLowerVolume);
+addStandard("AudioVolumeUp", KeyTable.XF86XK_AudioRaiseVolume);
+addStandard("AudioVolumeMute", KeyTable.XF86XK_AudioMute);
+// - MicrophoneToggle
+// - MicrophoneVolumeDown
+// - MicrophoneVolumeUp
+addStandard("MicrophoneVolumeMute", KeyTable.XF86XK_AudioMicMute);
+
+// 2.13. Speech Keys
+
+// - SpeechCorrectionList
+// - SpeechInputToggle
+
+// 2.14. Application Keys
+
+addStandard("LaunchCalculator", KeyTable.XF86XK_Calculator);
+addStandard("LaunchCalendar", KeyTable.XF86XK_Calendar);
+addStandard("LaunchMail", KeyTable.XF86XK_Mail);
+addStandard("LaunchMediaPlayer", KeyTable.XF86XK_AudioMedia);
+addStandard("LaunchMusicPlayer", KeyTable.XF86XK_Music);
+addStandard("LaunchMyComputer", KeyTable.XF86XK_MyComputer);
+addStandard("LaunchPhone", KeyTable.XF86XK_Phone);
+addStandard("LaunchScreenSaver", KeyTable.XF86XK_ScreenSaver);
+addStandard("LaunchSpreadsheet", KeyTable.XF86XK_Excel);
+addStandard("LaunchWebBrowser", KeyTable.XF86XK_WWW);
+addStandard("LaunchWebCam", KeyTable.XF86XK_WebCam);
+addStandard("LaunchWordProcessor", KeyTable.XF86XK_Word);
+
+// 2.15. Browser Keys
+
+addStandard("BrowserBack", KeyTable.XF86XK_Back);
+addStandard("BrowserFavorites", KeyTable.XF86XK_Favorites);
+addStandard("BrowserForward", KeyTable.XF86XK_Forward);
+addStandard("BrowserHome", KeyTable.XF86XK_HomePage);
+addStandard("BrowserRefresh", KeyTable.XF86XK_Refresh);
+addStandard("BrowserSearch", KeyTable.XF86XK_Search);
+addStandard("BrowserStop", KeyTable.XF86XK_Stop);
+
+// 2.16. Mobile Phone Keys
+
+// - A whole bunch...
+
+// 2.17. TV Keys
+
+// - A whole bunch...
+
+// 2.18. Media Controller Keys
+
+// - A whole bunch...
+addStandard("Dimmer", KeyTable.XF86XK_BrightnessAdjust);
+addStandard("MediaAudioTrack", KeyTable.XF86XK_AudioCycleTrack);
+addStandard("RandomToggle", KeyTable.XF86XK_AudioRandomPlay);
+addStandard("SplitScreenToggle", KeyTable.XF86XK_SplitScreen);
+addStandard("Subtitle", KeyTable.XF86XK_Subtitle);
+addStandard("VideoModeNext", KeyTable.XF86XK_Next_VMode);
+
+// Extra: Numpad
+
+addNumpad("=", KeyTable.XK_equal, KeyTable.XK_KP_Equal);
+addNumpad("+", KeyTable.XK_plus, KeyTable.XK_KP_Add);
+addNumpad("-", KeyTable.XK_minus, KeyTable.XK_KP_Subtract);
+addNumpad("*", KeyTable.XK_asterisk, KeyTable.XK_KP_Multiply);
+addNumpad("/", KeyTable.XK_slash, KeyTable.XK_KP_Divide);
+addNumpad(".", KeyTable.XK_period, KeyTable.XK_KP_Decimal);
+addNumpad(",", KeyTable.XK_comma, KeyTable.XK_KP_Separator);
+addNumpad("0", KeyTable.XK_0, KeyTable.XK_KP_0);
+addNumpad("1", KeyTable.XK_1, KeyTable.XK_KP_1);
+addNumpad("2", KeyTable.XK_2, KeyTable.XK_KP_2);
+addNumpad("3", KeyTable.XK_3, KeyTable.XK_KP_3);
+addNumpad("4", KeyTable.XK_4, KeyTable.XK_KP_4);
+addNumpad("5", KeyTable.XK_5, KeyTable.XK_KP_5);
+addNumpad("6", KeyTable.XK_6, KeyTable.XK_KP_6);
+addNumpad("7", KeyTable.XK_7, KeyTable.XK_KP_7);
+addNumpad("8", KeyTable.XK_8, KeyTable.XK_KP_8);
+addNumpad("9", KeyTable.XK_9, KeyTable.XK_KP_9);
+
+export default DOMKeyTable;
index 2a2594e8bea18f33653129a9ff04c62b7d239b3e..6dd4222349b64f2dbf5d03229e7e9dc69b6fbb08 100644 (file)
  */
 
 /*
- * Mapping between HTML key codes and VNC/X11 keysyms for the
- * subset of keys that have the same mapping on every keyboard
- * layout. Keys that vary between layouts must never be included
- * in this list.
+ * Fallback mapping between HTML key codes (physical keys) and
+ * HTML key values. This only works for keys that don't vary
+ * between layouts. We also omit those who manage fine by mapping the
+ * Unicode representation.
+ *
+ * See https://www.w3.org/TR/uievents-code/ for possible codes.
+ * See https://www.w3.org/TR/uievents-key/ for possible values.
  */
 
-import KeyTable from "./keysym.js";
-
 export default {
-    'Backspace':       KeyTable.XK_BackSpace,
-    'AltLeft':         KeyTable.XK_Alt_L,
-    // AltRight is special
-    'CapsLock':        KeyTable.XK_Caps_Lock,
-    'ContextMenu':     KeyTable.XK_Menu,
-    'ControlLeft':     KeyTable.XK_Control_L,
-    'ControlRight':    KeyTable.XK_Control_R,
-    'Enter':           KeyTable.XK_Return,
-    'MetaLeft':        KeyTable.XK_Super_L,
-    'MetaRight':       KeyTable.XK_Super_R,
-    'ShiftLeft':       KeyTable.XK_Shift_L,
-    'ShiftRight':      KeyTable.XK_Shift_R,
-    'Space':           KeyTable.XK_space,
-    'Tab':             KeyTable.XK_Tab,
+
+// 3.1.1.1. Writing System Keys
+
+    'Backspace':        'Backspace',
+
+// 3.1.1.2. Functional Keys
+
+    'AltLeft':          'Alt',
+    'AltRight':         'Alt', // This could also be 'AltGraph'
+    'CapsLock':         'CapsLock',
+    'ContextMenu':      'ContextMenu',
+    'ControlLeft':      'Control',
+    'ControlRight':     'Control',
+    'Enter':            'Enter',
+    'MetaLeft':         'Meta',
+    'MetaRight':        'Meta',
+    'ShiftLeft':        'Shift',
+    'ShiftRight':       'Shift',
+    'Tab':              'Tab',
     // FIXME: Japanese/Korean keys
-    'Delete':          KeyTable.XK_Delete,
-    'End':             KeyTable.XK_End,
-    'Help':            KeyTable.XK_Help,
-    'Home':            KeyTable.XK_Home,
-    'Insert':          KeyTable.XK_Insert,
-    'PageDown':        KeyTable.XK_Next,
-    'PageUp':          KeyTable.XK_Prior,
-    'ArrowDown':       KeyTable.XK_Down,
-    'ArrowLeft':       KeyTable.XK_Left,
-    'ArrowRight':      KeyTable.XK_Right,
-    'ArrowUp':         KeyTable.XK_Up,
-    'NumLock':         KeyTable.XK_Num_Lock,
-    'NumpadAdd':       KeyTable.XK_KP_Add,
-    'NumpadBackspace': KeyTable.XK_KP_Delete,
-    'NumpadClear':     KeyTable.XK_Clear,
-    // NumpadDecimal is special
-    'NumpadDivide':    KeyTable.XK_KP_Divide,
-    'NumpadEnter':     KeyTable.XK_KP_Enter,
-    'NumpadEqual':     KeyTable.XK_KP_Equal,
-    'NumpadMultiply':  KeyTable.XK_KP_Multiply,
-    'NumpadSubtract':  KeyTable.XK_KP_Subtract,
-    'Escape':          KeyTable.XK_Escape,
-    'F1':              KeyTable.XK_F1,
-    'F2':              KeyTable.XK_F2,
-    'F3':              KeyTable.XK_F3,
-    'F4':              KeyTable.XK_F4,
-    'F5':              KeyTable.XK_F5,
-    'F6':              KeyTable.XK_F6,
-    'F7':              KeyTable.XK_F7,
-    'F8':              KeyTable.XK_F8,
-    'F9':              KeyTable.XK_F9,
-    'F10':             KeyTable.XK_F10,
-    'F11':             KeyTable.XK_F11,
-    'F12':             KeyTable.XK_F12,
-    'F13':             KeyTable.XK_F13,
-    'F14':             KeyTable.XK_F14,
-    'F15':             KeyTable.XK_F15,
-    'F16':             KeyTable.XK_F16,
-    'F17':             KeyTable.XK_F17,
-    'F18':             KeyTable.XK_F18,
-    'F19':             KeyTable.XK_F19,
-    'F20':             KeyTable.XK_F20,
-    'F21':             KeyTable.XK_F21,
-    'F22':             KeyTable.XK_F22,
-    'F23':             KeyTable.XK_F23,
-    'F24':             KeyTable.XK_F24,
-    'F25':             KeyTable.XK_F25,
-    'F26':             KeyTable.XK_F26,
-    'F27':             KeyTable.XK_F27,
-    'F28':             KeyTable.XK_F28,
-    'F29':             KeyTable.XK_F29,
-    'F30':             KeyTable.XK_F30,
-    'F31':             KeyTable.XK_F31,
-    'F32':             KeyTable.XK_F32,
-    'F33':             KeyTable.XK_F33,
-    'F34':             KeyTable.XK_F34,
-    'F35':             KeyTable.XK_F35,
-    'PrintScreen':     KeyTable.XK_Print,
-    'ScrollLock':      KeyTable.XK_Scroll_Lock,
-    'Pause':           KeyTable.XK_Pause,
-    'BrowserBack':     KeyTable.XF86XK_Back,
-    'BrowserFavorites': KeyTable.XF86XK_Favorites,
-    'BrowserForward':  KeyTable.XF86XK_Forward,
-    'BrowserHome':     KeyTable.XF86XK_HomePage,
-    'BrowserRefresh':  KeyTable.XF86XK_Refresh,
-    'BrowserSearch':   KeyTable.XF86XK_Search,
-    'BrowserStop':     KeyTable.XF86XK_Stop,
-    'LaunchApp1':      KeyTable.XF86XK_Explorer,
-    'LaunchApp2':      KeyTable.XF86XK_Calculator,
-    'LaunchMail':      KeyTable.XF86XK_Mail,
-    'MediaPlayPause':  KeyTable.XF86XK_AudioPlay,
-    'MediaStop':       KeyTable.XF86XK_AudioStop,
-    'MediaTrackNext':  KeyTable.XF86XK_AudioNext,
-    'MediaTrackPrevious': KeyTable.XF86XK_AudioPrev,
-    'Power':           KeyTable.XF86XK_PowerOff,
-    'Sleep':           KeyTable.XF86XK_Sleep,
-    'AudioVolumeDown': KeyTable.XF86XK_AudioLowerVolume,
-    'AudioVolumeMute': KeyTable.XF86XK_AudioMute,
-    'AudioVolumeUp':   KeyTable.XF86XK_AudioRaiseVolume,
-    'WakeUp':          KeyTable.XF86XK_WakeUp,
+
+// 3.1.2. Control Pad Section
+
+    'Delete':           'Delete',
+    'End':              'End',
+    'Help':             'Help',
+    'Home':             'Home',
+    'Insert':           'Insert',
+    'PageDown':         'PageDown',
+    'PageUp':           'PageUp',
+
+// 3.1.3. Arrow Pad Section
+
+    'ArrowDown':        'ArrowDown',
+    'ArrowLeft':        'ArrowLeft',
+    'ArrowRight':       'ArrowRight',
+    'ArrowUp':          'ArrowUp',
+
+// 3.1.4. Numpad Section
+
+    'NumLock':          'NumLock',
+    'NumpadBackspace':  'Backspace',
+    'NumpadClear':      'Clear',
+
+// 3.1.5. Function Section
+
+    'Escape':           'Escape',
+    'F1':               'F1',
+    'F2':               'F2',
+    'F3':               'F3',
+    'F4':               'F4',
+    'F5':               'F5',
+    'F6':               'F6',
+    'F7':               'F7',
+    'F8':               'F8',
+    'F9':               'F9',
+    'F10':              'F10',
+    'F11':              'F11',
+    'F12':              'F12',
+    'F13':              'F13',
+    'F14':              'F14',
+    'F15':              'F15',
+    'F16':              'F16',
+    'F17':              'F17',
+    'F18':              'F18',
+    'F19':              'F19',
+    'F20':              'F20',
+    'F21':              'F21',
+    'F22':              'F22',
+    'F23':              'F23',
+    'F24':              'F24',
+    'F25':              'F25',
+    'F26':              'F26',
+    'F27':              'F27',
+    'F28':              'F28',
+    'F29':              'F29',
+    'F30':              'F30',
+    'F31':              'F31',
+    'F32':              'F32',
+    'F33':              'F33',
+    'F34':              'F34',
+    'F35':              'F35',
+    'PrintScreen':      'PrintScreen',
+    'ScrollLock':       'ScrollLock',
+    'Pause':            'Pause',
+
+// 3.1.6. Media Keys
+
+    'BrowserBack':      'BrowserBack',
+    'BrowserFavorites': 'BrowserFavorites',
+    'BrowserForward':   'BrowserForward',
+    'BrowserHome':      'BrowserHome',
+    'BrowserRefresh':   'BrowserRefresh',
+    'BrowserSearch':    'BrowserSearch',
+    'BrowserStop':      'BrowserStop',
+    'Eject':            'Eject',
+    'LaunchApp1':       'LaunchMyComputer',
+    'LaunchApp2':       'LaunchCalendar',
+    'LaunchMail':       'LaunchMail',
+    'MediaPlayPause':   'MediaPlay',
+    'MediaStop':        'MediaStop',
+    'MediaTrackNext':   'MediaTrackNext',
+    'MediaTrackPrevious': 'MediaTrackPrevious',
+    'Power':            'Power',
+    'Sleep':            'Sleep',
+    'AudioVolumeDown':  'AudioVolumeDown',
+    'AudioVolumeMute':  'AudioVolumeMute',
+    'AudioVolumeUp':    'AudioVolumeUp',
+    'WakeUp':           'WakeUp',
 };
index 06ea70f815fcf71b90cc8b366ef0f66c118c4daf..ba58be6848b307b54feeb11001edf36aa8821d14 100644 (file)
@@ -12,6 +12,37 @@ export default {
     XK_Escape:                      0xff1b,
     XK_Delete:                      0xffff, /* Delete, rubout */
 
+    /* International & multi-key character composition */
+
+    XK_Multi_key:                   0xff20, /* Multi-key character compose */
+    XK_Codeinput:                   0xff37,
+    XK_SingleCandidate:             0xff3c,
+    XK_MultipleCandidate:           0xff3d,
+    XK_PreviousCandidate:           0xff3e,
+
+    /* Japanese keyboard support */
+
+    XK_Kanji:                       0xff21, /* Kanji, Kanji convert */
+    XK_Muhenkan:                    0xff22, /* Cancel Conversion */
+    XK_Henkan_Mode:                 0xff23, /* Start/Stop Conversion */
+    XK_Henkan:                      0xff23, /* Alias for Henkan_Mode */
+    XK_Romaji:                      0xff24, /* to Romaji */
+    XK_Hiragana:                    0xff25, /* to Hiragana */
+    XK_Katakana:                    0xff26, /* to Katakana */
+    XK_Hiragana_Katakana:           0xff27, /* Hiragana/Katakana toggle */
+    XK_Zenkaku:                     0xff28, /* to Zenkaku */
+    XK_Hankaku:                     0xff29, /* to Hankaku */
+    XK_Zenkaku_Hankaku:             0xff2a, /* Zenkaku/Hankaku toggle */
+    XK_Touroku:                     0xff2b, /* Add to Dictionary */
+    XK_Massyo:                      0xff2c, /* Delete from Dictionary */
+    XK_Kana_Lock:                   0xff2d, /* Kana Lock */
+    XK_Kana_Shift:                  0xff2e, /* Kana Shift */
+    XK_Eisu_Shift:                  0xff2f, /* Alphanumeric Shift */
+    XK_Eisu_toggle:                 0xff30, /* Alphanumeric toggle */
+    XK_Kanji_Bangou:                0xff37, /* Codeinput */
+    XK_Zen_Koho:                    0xff3d, /* Multiple/All Candidate(s) */
+    XK_Mae_Koho:                    0xff3e, /* Previous Candidate */
+
     /* Cursor control & motion */
 
     XK_Home:                        0xff50,
@@ -171,7 +202,17 @@ export default {
     XK_Hyper_L:                     0xffed, /* Left hyper */
     XK_Hyper_R:                     0xffee, /* Right hyper */
 
+    /*
+     * Keyboard (XKB) Extension function and modifier keys
+     * (from Appendix C of "The X Keyboard Extension: Protocol Specification")
+     * Byte 3 = 0xfe
+     */
+
     XK_ISO_Level3_Shift:            0xfe03, /* AltGr */
+    XK_ISO_Next_Group:              0xfe08,
+    XK_ISO_Prev_Group:              0xfe0a,
+    XK_ISO_First_Group:             0xfe0c,
+    XK_ISO_Last_Group:              0xfe0e,
 
     /*
      * Latin 1
@@ -378,6 +419,15 @@ export default {
     XK_thorn:                       0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
     XK_ydiaeresis:                  0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
 
+    /*
+     * Korean
+     * Byte 3 = 0x0e
+     */
+
+    XK_Hangul:                      0xff31, /* Hangul start/stop(toggle) */
+    XK_Hangul_Hanja:                0xff34, /* Start Hangul->Hanja Conversion */
+    XK_Hangul_Jeonja:               0xff38, /* Jeonja mode */
+
     /*
      * XFree86 vendor specific keysyms.
      *
index d755c20f7a74f40832a204f17d871a3d76b2736a..4335b0bfe48d59eb4daf9e4eca23157a3141480d 100644 (file)
@@ -2,10 +2,17 @@ import KeyTable from "./keysym.js";
 import keysyms from "./keysymdef.js";
 import vkeys from "./vkeys.js";
 import fixedkeys from "./fixedkeys.js";
+import DOMKeyTable from "./domkeytable.js";
 
 function isMac() {
     return navigator && !!(/mac/i).exec(navigator.platform);
 }
+function isIE() {
+    return navigator && !!(/trident/i).exec(navigator.userAgent);
+}
+function isEdge() {
+    return navigator && !!(/edge/i).exec(navigator.userAgent);
+}
 
 // Get 'KeyboardEvent.code', handling legacy browsers
 export function getKeycode(evt){
@@ -67,88 +74,91 @@ export function getKeycode(evt){
     return 'Unidentified';
 }
 
-// Get the most reliable keysym value we can get from a key event
-export function getKeysym(evt){
+// Get 'KeyboardEvent.key', handling legacy browsers
+export function getKey(evt) {
+    // Are we getting a proper key value?
+    if (evt.key !== undefined) {
+        // IE and Edge use some ancient version of the spec
+        // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8860571/
+        switch (evt.key) {
+            case 'Spacebar': return ' ';
+            case 'Esc': return 'Escape';
+            case 'Scroll': return 'ScrollLock';
+            case 'Win': return 'Meta';
+            case 'Apps': return 'ContextMenu';
+            case 'Up': return 'ArrowUp';
+            case 'Left': return 'ArrowLeft';
+            case 'Right': return 'ArrowRight';
+            case 'Down': return 'ArrowDown';
+            case 'Del': return 'Delete';
+            case 'Divide': return '/';
+            case 'Multiply': return '*';
+            case 'Subtract': return '-';
+            case 'Add': return '+';
+            case 'Decimal': return evt.char;
+        }
+
+        // Mozilla isn't fully in sync with the spec yet
+        switch (evt.key) {
+            case 'OS': return 'Meta';
+        }
+
+        // IE and Edge have broken handling of AltGraph so we cannot
+        // trust them for printable characters
+        if ((evt.key.length !== 1) || (!isIE() && !isEdge())) {
+            return evt.key;
+        }
+    }
 
-    // We start with layout independent keys
+    // Try to deduce it based on the physical key
     var code = getKeycode(evt);
     if (code in fixedkeys) {
         return fixedkeys[code];
     }
 
-    // Next with mildly layout or state sensitive stuff
+    // If that failed, then see if we have a printable character
+    if (evt.charCode) {
+        return String.fromCharCode(evt.charCode);
+    }
 
-    // Like AltGraph
-    if (code === 'AltRight') {
-        if (evt.key === 'AltGraph') {
-            return KeyTable.XK_ISO_Level3_Shift;
-        } else {
-            return KeyTable.XK_Alt_R;
-        }
+    // At this point we have nothing left to go on
+    return 'Unidentified';
+}
+
+// Get the most reliable keysym value we can get from a key event
+export function getKeysym(evt){
+    var key = getKey(evt);
+
+    if (key === 'Unidentified') {
+        return null;
     }
 
-    // Or the numpad
-    if (evt.location === 3) {
-        var key = evt.key;
+    // First look up special keys
+    if (key in DOMKeyTable) {
+        var location = evt.location;
 
-        // IE and Edge use some ancient version of the spec
-        // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8860571/
-        switch (key) {
-            case 'Up': key = 'ArrowUp'; break;
-            case 'Left': key = 'ArrowLeft'; break;
-            case 'Right': key = 'ArrowRight'; break;
-            case 'Down': key = 'ArrowDown'; break;
-            case 'Del': key = 'Delete'; break;
+        // Safari screws up location for the right cmd key
+        if ((key === 'Meta') && (location === 0)) {
+            location = 2;
         }
 
-        // Safari doesn't support KeyboardEvent.key yet
-        if ((key === undefined) && (evt.charCode)) {
-            key = String.fromCharCode(evt.charCode);
+        if ((location === undefined) || (location > 3)) {
+            location = 0;
         }
 
-        switch (key) {
-            case '0': return KeyTable.XK_KP_0;
-            case '1': return KeyTable.XK_KP_1;
-            case '2': return KeyTable.XK_KP_2;
-            case '3': return KeyTable.XK_KP_3;
-            case '4': return KeyTable.XK_KP_4;
-            case '5': return KeyTable.XK_KP_5;
-            case '6': return KeyTable.XK_KP_6;
-            case '7': return KeyTable.XK_KP_7;
-            case '8': return KeyTable.XK_KP_8;
-            case '9': return KeyTable.XK_KP_9;
-            // There is utter mayhem in the world when it comes to which
-            // character to use as a decimal separator...
-            case '.': return KeyTable.XK_KP_Decimal;
-            case ',': return KeyTable.XK_KP_Separator;
-            case 'Home': return KeyTable.XK_KP_Home;
-            case 'End': return KeyTable.XK_KP_End;
-            case 'PageUp': return KeyTable.XK_KP_Prior;
-            case 'PageDown': return KeyTable.XK_KP_Next;
-            case 'Insert': return KeyTable.XK_KP_Insert;
-            case 'Delete': return KeyTable.XK_KP_Delete;
-            case 'ArrowUp': return KeyTable.XK_KP_Up;
-            case 'ArrowLeft': return KeyTable.XK_KP_Left;
-            case 'ArrowRight': return KeyTable.XK_KP_Right;
-            case 'ArrowDown': return KeyTable.XK_KP_Down;
-        }
+        return DOMKeyTable[key][location];
     }
 
     // Now we need to look at the Unicode symbol instead
 
     var codepoint;
 
-    if ('key' in evt) {
-        // Special key? (FIXME: Should have been caught earlier)
-        if (evt.key.length !== 1) {
-            return null;
-        }
-
-        codepoint = evt.key.charCodeAt();
-    } else if ('charCode' in evt) {
-        codepoint = evt.charCode;
+    // Special key? (FIXME: Should have been caught earlier)
+    if (key.length !== 1) {
+        return null;
     }
 
+    codepoint = key.charCodeAt();
     if (codepoint) {
         return keysyms.lookup(codepoint);
     }
index f705cff00bf65001089562879d54d02d8208259c..e0ae80ac0a29f25164837ca85d49542529ddc971 100644 (file)
@@ -98,39 +98,104 @@ describe('Helpers', function() {
         });
     });
 
-    describe('getKeysym', function() {
+    describe('getKey', function() {
         it('should prefer key', function() {
-            expect(KeyboardUtil.getKeysym({key: 'a', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.be.equal(0x61);
+            expect(KeyboardUtil.getKey({key: 'a', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.be.equal('a');
+        });
+        it('should map legacy values', function() {
+            expect(KeyboardUtil.getKey({key: 'Spacebar'})).to.be.equal(' ');
+            expect(KeyboardUtil.getKey({key: 'Left'})).to.be.equal('ArrowLeft');
+            expect(KeyboardUtil.getKey({key: 'OS'})).to.be.equal('Meta');
+            expect(KeyboardUtil.getKey({key: 'Win'})).to.be.equal('Meta');
+        });
+        it('should use code if no key', function() {
+            expect(KeyboardUtil.getKey({code: 'NumpadBackspace'})).to.be.equal('Backspace');
+        });
+        it('should not use code fallback for character keys', function() {
+            expect(KeyboardUtil.getKey({code: 'KeyA'})).to.be.equal('Unidentified');
+            expect(KeyboardUtil.getKey({code: 'Digit1'})).to.be.equal('Unidentified');
+            expect(KeyboardUtil.getKey({code: 'Period'})).to.be.equal('Unidentified');
+            expect(KeyboardUtil.getKey({code: 'Numpad1'})).to.be.equal('Unidentified');
         });
         it('should use charCode if no key', function() {
-            expect(KeyboardUtil.getKeysym({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.be.equal(0x01a9);
+            expect(KeyboardUtil.getKey({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.be.equal('Š');
         });
+        it('should return Unidentified when it cannot map the key', function() {
+            expect(KeyboardUtil.getKey({keycode: 0x42})).to.be.equal('Unidentified');
+        });
+
+        describe('Broken key AltGraph on IE/Edge', function() {
+            var origNavigator;
+            beforeEach(function () {
+                // window.navigator is a protected read-only property in many
+                // environments, so we need to redefine it whilst running these
+                // tests.
+                origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
+                if (origNavigator === undefined) {
+                    // Object.getOwnPropertyDescriptor() doesn't work
+                    // properly in any version of IE
+                    this.skip();
+                }
 
+                Object.defineProperty(window, "navigator", {value: {}});
+                if (window.navigator.platform !== undefined) {
+                    // Object.defineProperty() doesn't work properly in old
+                    // versions of Chrome
+                    this.skip();
+                }
+            });
+            afterEach(function () {
+                Object.defineProperty(window, "navigator", origNavigator);
+            });
+
+            it('should ignore printable character key on IE', function() {
+                window.navigator.userAgent = "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
+                expect(KeyboardUtil.getKey({key: 'a'})).to.be.equal('Unidentified');
+            });
+            it('should ignore printable character key on Edge', function() {
+                window.navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
+                expect(KeyboardUtil.getKey({key: 'a'})).to.be.equal('Unidentified');
+            });
+            it('should allow non-printable character key on IE', function() {
+                window.navigator.userAgent = "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
+                expect(KeyboardUtil.getKey({key: 'Shift'})).to.be.equal('Shift');
+            });
+            it('should allow non-printable character key on Edge', function() {
+                window.navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
+                expect(KeyboardUtil.getKey({key: 'Shift'})).to.be.equal('Shift');
+            });
+        });
+    });
+
+    describe('getKeysym', function() {
         describe('Non-character keys', function() {
             it('should recognize the right keys', function() {
-                expect(KeyboardUtil.getKeysym({code: 'Enter'})).to.be.equal(0xFF0D);
-                expect(KeyboardUtil.getKeysym({code: 'Backspace'})).to.be.equal(0xFF08);
-                expect(KeyboardUtil.getKeysym({code: 'Tab'})).to.be.equal(0xFF09);
-                expect(KeyboardUtil.getKeysym({code: 'ShiftLeft'})).to.be.equal(0xFFE1);
-                expect(KeyboardUtil.getKeysym({code: 'ControlLeft'})).to.be.equal(0xFFE3);
-                expect(KeyboardUtil.getKeysym({code: 'AltLeft'})).to.be.equal(0xFFE9);
-                expect(KeyboardUtil.getKeysym({code: 'MetaLeft'})).to.be.equal(0xFFEB);
-                expect(KeyboardUtil.getKeysym({code: 'Escape'})).to.be.equal(0xFF1B);
-                expect(KeyboardUtil.getKeysym({code: 'ArrowUp'})).to.be.equal(0xFF52);
+                expect(KeyboardUtil.getKeysym({key: 'Enter'})).to.be.equal(0xFF0D);
+                expect(KeyboardUtil.getKeysym({key: 'Backspace'})).to.be.equal(0xFF08);
+                expect(KeyboardUtil.getKeysym({key: 'Tab'})).to.be.equal(0xFF09);
+                expect(KeyboardUtil.getKeysym({key: 'Shift'})).to.be.equal(0xFFE1);
+                expect(KeyboardUtil.getKeysym({key: 'Control'})).to.be.equal(0xFFE3);
+                expect(KeyboardUtil.getKeysym({key: 'Alt'})).to.be.equal(0xFFE9);
+                expect(KeyboardUtil.getKeysym({key: 'Meta'})).to.be.equal(0xFFEB);
+                expect(KeyboardUtil.getKeysym({key: 'Escape'})).to.be.equal(0xFF1B);
+                expect(KeyboardUtil.getKeysym({key: 'ArrowUp'})).to.be.equal(0xFF52);
+            });
+            it('should map left/right side', function() {
+                expect(KeyboardUtil.getKeysym({key: 'Shift', location: 1})).to.be.equal(0xFFE1);
+                expect(KeyboardUtil.getKeysym({key: 'Shift', location: 2})).to.be.equal(0xFFE2);
+                expect(KeyboardUtil.getKeysym({key: 'Control', location: 1})).to.be.equal(0xFFE3);
+                expect(KeyboardUtil.getKeysym({key: 'Control', location: 2})).to.be.equal(0xFFE4);
             });
             it('should handle AltGraph', function() {
-                expect(KeyboardUtil.getKeysym({code: 'AltRight', key: 'AltRight'})).to.be.equal(0xFFEA);
-                expect(KeyboardUtil.getKeysym({code: 'AltRight', key: 'AltGraph'})).to.be.equal(0xFE03);
+                expect(KeyboardUtil.getKeysym({code: 'AltRight', key: 'Alt', location: 2})).to.be.equal(0xFFEA);
+                expect(KeyboardUtil.getKeysym({code: 'AltRight', key: 'AltGraph', location: 2})).to.be.equal(0xFE03);
             });
-            it('should return null for unknown codes', function() {
-                expect(KeyboardUtil.getKeysym({code: 'Semicolon'})).to.be.null;
-                expect(KeyboardUtil.getKeysym({code: 'BracketRight'})).to.be.null;
+            it('should return null for unknown keys', function() {
+                expect(KeyboardUtil.getKeysym({key: 'Semicolon'})).to.be.null;
+                expect(KeyboardUtil.getKeysym({key: 'BracketRight'})).to.be.null;
             });
-            it('should not recognize character keys', function() {
-                expect(KeyboardUtil.getKeysym({code: 'KeyA'})).to.be.null;
-                expect(KeyboardUtil.getKeysym({code: 'Digit1'})).to.be.null;
-                expect(KeyboardUtil.getKeysym({code: 'Period'})).to.be.null;
-                expect(KeyboardUtil.getKeysym({code: 'Numpad1'})).to.be.null;
+            it('should handle remappings', function() {
+                expect(KeyboardUtil.getKeysym({code: 'ControlLeft', key: 'Tab'})).to.be.equal(0xFF09);
             });
         });
 
@@ -145,10 +210,6 @@ describe('Helpers', function() {
                 expect(KeyboardUtil.getKeysym({code: 'Delete', key: 'Delete', location: 0})).to.be.equal(0xFFFF);
                 expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: 'Delete', location: 3})).to.be.equal(0xFF9F);
             });
-            it('should handle IE/Edge key names', function() {
-                expect(KeyboardUtil.getKeysym({code: 'Numpad6', key: 'Right', location: 3})).to.be.equal(0xFF98);
-                expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: 'Del', location: 3})).to.be.equal(0xFF9F);
-            });
             it('should handle Numpad Decimal key', function() {
                 expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: '.', location: 3})).to.be.equal(0xFFAE);
                 expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: ',', location: 3})).to.be.equal(0xFFAC);
index 1ebbbd4a3479c9a225f67fbb48780279e3e73911..a4ac63042c6d9283a1139998b3a9761ca802aac4 100644 (file)
@@ -187,8 +187,8 @@ describe('Key Event Handling', function() {
                         break;
                 }
             }});
-            kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt'}));
-            kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt'}));
+            kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1}));
+            kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
             expect(count).to.be.equal(2);
         });
         it('should change left Super to Alt', function(done) {
@@ -198,7 +198,7 @@ describe('Key Event Handling', function() {
                 expect(code).to.be.equal('MetaLeft');
                 done();
             }});
-            kbd._handleKeyDown(keyevent('keydown', {code: 'MetaLeft', key: 'Meta'}));
+            kbd._handleKeyDown(keyevent('keydown', {code: 'MetaLeft', key: 'Meta', location: 1}));
         });
         it('should change right Super to left Super', function(done) {
             var kbd = new Keyboard({
@@ -207,7 +207,7 @@ describe('Key Event Handling', function() {
                 expect(code).to.be.equal('MetaRight');
                 done();
             }});
-            kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta'}));
+            kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2}));
         });
     });
 
@@ -280,8 +280,8 @@ describe('Key Event Handling', function() {
                 }
             }});
             // First the modifier combo
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control'}));
-            kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt'}));
+            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
+            kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
             // Next a normal character
             kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
             expect(times_called).to.be.equal(7);
@@ -299,8 +299,8 @@ describe('Key Event Handling', function() {
                 }
             }});
             // First the modifier combo
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control'}));
-            kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt'}));
+            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
+            kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
             // Next a normal character
             kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
             kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
@@ -329,10 +329,10 @@ describe('Key Event Handling', function() {
                 }
             }});
             // First the modifier combo
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control'}));
-            kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt'}));
+            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
+            kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1}));
             // Then one of the keys again
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control'}));
+            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
             expect(times_called).to.be.equal(3);
         });
     });