--- /dev/null
+/*
+ * 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;
*/
/*
- * 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',
};
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,
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
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.
*
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){
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);
}
});
});
- 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);
});
});
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);
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) {
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({
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}));
});
});
}
}});
// 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);
}
}});
// 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'}));
}
}});
// 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);
});
});