Improve lookup of special keys
Look up keys that are independent of layout and state first, followed by keys that are only mild variations in layouts. This is more robust as there might be multiple physical keys generating the same symbols, and Keysyms don't map directly to Unicode in all cases. At the same time switch over to using the modern, standardised 'code' field for lookup.
This commit is contained in:
parent
bfa1b237b9
commit
f714f7deae
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* noVNC: HTML5 VNC client
|
||||||
|
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
||||||
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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,
|
||||||
|
// 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,
|
||||||
|
};
|
|
@ -377,4 +377,188 @@ export default {
|
||||||
XK_yacute: 0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */
|
XK_yacute: 0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */
|
||||||
XK_thorn: 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
|
XK_thorn: 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
|
||||||
XK_ydiaeresis: 0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
|
XK_ydiaeresis: 0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XFree86 vendor specific keysyms.
|
||||||
|
*
|
||||||
|
* The XFree86 keysym range is 0x10080001 - 0x1008FFFF.
|
||||||
|
*/
|
||||||
|
|
||||||
|
XF86XK_ModeLock: 0x1008FF01,
|
||||||
|
XF86XK_MonBrightnessUp: 0x1008FF02,
|
||||||
|
XF86XK_MonBrightnessDown: 0x1008FF03,
|
||||||
|
XF86XK_KbdLightOnOff: 0x1008FF04,
|
||||||
|
XF86XK_KbdBrightnessUp: 0x1008FF05,
|
||||||
|
XF86XK_KbdBrightnessDown: 0x1008FF06,
|
||||||
|
XF86XK_Standby: 0x1008FF10,
|
||||||
|
XF86XK_AudioLowerVolume: 0x1008FF11,
|
||||||
|
XF86XK_AudioMute: 0x1008FF12,
|
||||||
|
XF86XK_AudioRaiseVolume: 0x1008FF13,
|
||||||
|
XF86XK_AudioPlay: 0x1008FF14,
|
||||||
|
XF86XK_AudioStop: 0x1008FF15,
|
||||||
|
XF86XK_AudioPrev: 0x1008FF16,
|
||||||
|
XF86XK_AudioNext: 0x1008FF17,
|
||||||
|
XF86XK_HomePage: 0x1008FF18,
|
||||||
|
XF86XK_Mail: 0x1008FF19,
|
||||||
|
XF86XK_Start: 0x1008FF1A,
|
||||||
|
XF86XK_Search: 0x1008FF1B,
|
||||||
|
XF86XK_AudioRecord: 0x1008FF1C,
|
||||||
|
XF86XK_Calculator: 0x1008FF1D,
|
||||||
|
XF86XK_Memo: 0x1008FF1E,
|
||||||
|
XF86XK_ToDoList: 0x1008FF1F,
|
||||||
|
XF86XK_Calendar: 0x1008FF20,
|
||||||
|
XF86XK_PowerDown: 0x1008FF21,
|
||||||
|
XF86XK_ContrastAdjust: 0x1008FF22,
|
||||||
|
XF86XK_RockerUp: 0x1008FF23,
|
||||||
|
XF86XK_RockerDown: 0x1008FF24,
|
||||||
|
XF86XK_RockerEnter: 0x1008FF25,
|
||||||
|
XF86XK_Back: 0x1008FF26,
|
||||||
|
XF86XK_Forward: 0x1008FF27,
|
||||||
|
XF86XK_Stop: 0x1008FF28,
|
||||||
|
XF86XK_Refresh: 0x1008FF29,
|
||||||
|
XF86XK_PowerOff: 0x1008FF2A,
|
||||||
|
XF86XK_WakeUp: 0x1008FF2B,
|
||||||
|
XF86XK_Eject: 0x1008FF2C,
|
||||||
|
XF86XK_ScreenSaver: 0x1008FF2D,
|
||||||
|
XF86XK_WWW: 0x1008FF2E,
|
||||||
|
XF86XK_Sleep: 0x1008FF2F,
|
||||||
|
XF86XK_Favorites: 0x1008FF30,
|
||||||
|
XF86XK_AudioPause: 0x1008FF31,
|
||||||
|
XF86XK_AudioMedia: 0x1008FF32,
|
||||||
|
XF86XK_MyComputer: 0x1008FF33,
|
||||||
|
XF86XK_VendorHome: 0x1008FF34,
|
||||||
|
XF86XK_LightBulb: 0x1008FF35,
|
||||||
|
XF86XK_Shop: 0x1008FF36,
|
||||||
|
XF86XK_History: 0x1008FF37,
|
||||||
|
XF86XK_OpenURL: 0x1008FF38,
|
||||||
|
XF86XK_AddFavorite: 0x1008FF39,
|
||||||
|
XF86XK_HotLinks: 0x1008FF3A,
|
||||||
|
XF86XK_BrightnessAdjust: 0x1008FF3B,
|
||||||
|
XF86XK_Finance: 0x1008FF3C,
|
||||||
|
XF86XK_Community: 0x1008FF3D,
|
||||||
|
XF86XK_AudioRewind: 0x1008FF3E,
|
||||||
|
XF86XK_BackForward: 0x1008FF3F,
|
||||||
|
XF86XK_Launch0: 0x1008FF40,
|
||||||
|
XF86XK_Launch1: 0x1008FF41,
|
||||||
|
XF86XK_Launch2: 0x1008FF42,
|
||||||
|
XF86XK_Launch3: 0x1008FF43,
|
||||||
|
XF86XK_Launch4: 0x1008FF44,
|
||||||
|
XF86XK_Launch5: 0x1008FF45,
|
||||||
|
XF86XK_Launch6: 0x1008FF46,
|
||||||
|
XF86XK_Launch7: 0x1008FF47,
|
||||||
|
XF86XK_Launch8: 0x1008FF48,
|
||||||
|
XF86XK_Launch9: 0x1008FF49,
|
||||||
|
XF86XK_LaunchA: 0x1008FF4A,
|
||||||
|
XF86XK_LaunchB: 0x1008FF4B,
|
||||||
|
XF86XK_LaunchC: 0x1008FF4C,
|
||||||
|
XF86XK_LaunchD: 0x1008FF4D,
|
||||||
|
XF86XK_LaunchE: 0x1008FF4E,
|
||||||
|
XF86XK_LaunchF: 0x1008FF4F,
|
||||||
|
XF86XK_ApplicationLeft: 0x1008FF50,
|
||||||
|
XF86XK_ApplicationRight: 0x1008FF51,
|
||||||
|
XF86XK_Book: 0x1008FF52,
|
||||||
|
XF86XK_CD: 0x1008FF53,
|
||||||
|
XF86XK_Calculater: 0x1008FF54,
|
||||||
|
XF86XK_Clear: 0x1008FF55,
|
||||||
|
XF86XK_Close: 0x1008FF56,
|
||||||
|
XF86XK_Copy: 0x1008FF57,
|
||||||
|
XF86XK_Cut: 0x1008FF58,
|
||||||
|
XF86XK_Display: 0x1008FF59,
|
||||||
|
XF86XK_DOS: 0x1008FF5A,
|
||||||
|
XF86XK_Documents: 0x1008FF5B,
|
||||||
|
XF86XK_Excel: 0x1008FF5C,
|
||||||
|
XF86XK_Explorer: 0x1008FF5D,
|
||||||
|
XF86XK_Game: 0x1008FF5E,
|
||||||
|
XF86XK_Go: 0x1008FF5F,
|
||||||
|
XF86XK_iTouch: 0x1008FF60,
|
||||||
|
XF86XK_LogOff: 0x1008FF61,
|
||||||
|
XF86XK_Market: 0x1008FF62,
|
||||||
|
XF86XK_Meeting: 0x1008FF63,
|
||||||
|
XF86XK_MenuKB: 0x1008FF65,
|
||||||
|
XF86XK_MenuPB: 0x1008FF66,
|
||||||
|
XF86XK_MySites: 0x1008FF67,
|
||||||
|
XF86XK_New: 0x1008FF68,
|
||||||
|
XF86XK_News: 0x1008FF69,
|
||||||
|
XF86XK_OfficeHome: 0x1008FF6A,
|
||||||
|
XF86XK_Open: 0x1008FF6B,
|
||||||
|
XF86XK_Option: 0x1008FF6C,
|
||||||
|
XF86XK_Paste: 0x1008FF6D,
|
||||||
|
XF86XK_Phone: 0x1008FF6E,
|
||||||
|
XF86XK_Q: 0x1008FF70,
|
||||||
|
XF86XK_Reply: 0x1008FF72,
|
||||||
|
XF86XK_Reload: 0x1008FF73,
|
||||||
|
XF86XK_RotateWindows: 0x1008FF74,
|
||||||
|
XF86XK_RotationPB: 0x1008FF75,
|
||||||
|
XF86XK_RotationKB: 0x1008FF76,
|
||||||
|
XF86XK_Save: 0x1008FF77,
|
||||||
|
XF86XK_ScrollUp: 0x1008FF78,
|
||||||
|
XF86XK_ScrollDown: 0x1008FF79,
|
||||||
|
XF86XK_ScrollClick: 0x1008FF7A,
|
||||||
|
XF86XK_Send: 0x1008FF7B,
|
||||||
|
XF86XK_Spell: 0x1008FF7C,
|
||||||
|
XF86XK_SplitScreen: 0x1008FF7D,
|
||||||
|
XF86XK_Support: 0x1008FF7E,
|
||||||
|
XF86XK_TaskPane: 0x1008FF7F,
|
||||||
|
XF86XK_Terminal: 0x1008FF80,
|
||||||
|
XF86XK_Tools: 0x1008FF81,
|
||||||
|
XF86XK_Travel: 0x1008FF82,
|
||||||
|
XF86XK_UserPB: 0x1008FF84,
|
||||||
|
XF86XK_User1KB: 0x1008FF85,
|
||||||
|
XF86XK_User2KB: 0x1008FF86,
|
||||||
|
XF86XK_Video: 0x1008FF87,
|
||||||
|
XF86XK_WheelButton: 0x1008FF88,
|
||||||
|
XF86XK_Word: 0x1008FF89,
|
||||||
|
XF86XK_Xfer: 0x1008FF8A,
|
||||||
|
XF86XK_ZoomIn: 0x1008FF8B,
|
||||||
|
XF86XK_ZoomOut: 0x1008FF8C,
|
||||||
|
XF86XK_Away: 0x1008FF8D,
|
||||||
|
XF86XK_Messenger: 0x1008FF8E,
|
||||||
|
XF86XK_WebCam: 0x1008FF8F,
|
||||||
|
XF86XK_MailForward: 0x1008FF90,
|
||||||
|
XF86XK_Pictures: 0x1008FF91,
|
||||||
|
XF86XK_Music: 0x1008FF92,
|
||||||
|
XF86XK_Battery: 0x1008FF93,
|
||||||
|
XF86XK_Bluetooth: 0x1008FF94,
|
||||||
|
XF86XK_WLAN: 0x1008FF95,
|
||||||
|
XF86XK_UWB: 0x1008FF96,
|
||||||
|
XF86XK_AudioForward: 0x1008FF97,
|
||||||
|
XF86XK_AudioRepeat: 0x1008FF98,
|
||||||
|
XF86XK_AudioRandomPlay: 0x1008FF99,
|
||||||
|
XF86XK_Subtitle: 0x1008FF9A,
|
||||||
|
XF86XK_AudioCycleTrack: 0x1008FF9B,
|
||||||
|
XF86XK_CycleAngle: 0x1008FF9C,
|
||||||
|
XF86XK_FrameBack: 0x1008FF9D,
|
||||||
|
XF86XK_FrameForward: 0x1008FF9E,
|
||||||
|
XF86XK_Time: 0x1008FF9F,
|
||||||
|
XF86XK_Select: 0x1008FFA0,
|
||||||
|
XF86XK_View: 0x1008FFA1,
|
||||||
|
XF86XK_TopMenu: 0x1008FFA2,
|
||||||
|
XF86XK_Red: 0x1008FFA3,
|
||||||
|
XF86XK_Green: 0x1008FFA4,
|
||||||
|
XF86XK_Yellow: 0x1008FFA5,
|
||||||
|
XF86XK_Blue: 0x1008FFA6,
|
||||||
|
XF86XK_Suspend: 0x1008FFA7,
|
||||||
|
XF86XK_Hibernate: 0x1008FFA8,
|
||||||
|
XF86XK_TouchpadToggle: 0x1008FFA9,
|
||||||
|
XF86XK_TouchpadOn: 0x1008FFB0,
|
||||||
|
XF86XK_TouchpadOff: 0x1008FFB1,
|
||||||
|
XF86XK_AudioMicMute: 0x1008FFB2,
|
||||||
|
XF86XK_Switch_VT_1: 0x1008FE01,
|
||||||
|
XF86XK_Switch_VT_2: 0x1008FE02,
|
||||||
|
XF86XK_Switch_VT_3: 0x1008FE03,
|
||||||
|
XF86XK_Switch_VT_4: 0x1008FE04,
|
||||||
|
XF86XK_Switch_VT_5: 0x1008FE05,
|
||||||
|
XF86XK_Switch_VT_6: 0x1008FE06,
|
||||||
|
XF86XK_Switch_VT_7: 0x1008FE07,
|
||||||
|
XF86XK_Switch_VT_8: 0x1008FE08,
|
||||||
|
XF86XK_Switch_VT_9: 0x1008FE09,
|
||||||
|
XF86XK_Switch_VT_10: 0x1008FE0A,
|
||||||
|
XF86XK_Switch_VT_11: 0x1008FE0B,
|
||||||
|
XF86XK_Switch_VT_12: 0x1008FE0C,
|
||||||
|
XF86XK_Ungrab: 0x1008FE20,
|
||||||
|
XF86XK_ClearGrab: 0x1008FE21,
|
||||||
|
XF86XK_Next_VMode: 0x1008FE22,
|
||||||
|
XF86XK_Prev_VMode: 0x1008FE23,
|
||||||
|
XF86XK_LogWindowTree: 0x1008FE24,
|
||||||
|
XF86XK_LogGrabInfo: 0x1008FE25,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import KeyTable from "./keysym.js";
|
import KeyTable from "./keysym.js";
|
||||||
import keysyms from "./keysymdef.js";
|
import keysyms from "./keysymdef.js";
|
||||||
import vkeys from "./vkeys.js";
|
import vkeys from "./vkeys.js";
|
||||||
|
import fixedkeys from "./fixedkeys.js";
|
||||||
|
|
||||||
function isMac() {
|
function isMac() {
|
||||||
return navigator && !!(/mac/i).exec(navigator.platform);
|
return navigator && !!(/mac/i).exec(navigator.platform);
|
||||||
|
@ -12,29 +13,6 @@ function isLinux() {
|
||||||
return navigator && !!(/linux/i).exec(navigator.platform);
|
return navigator && !!(/linux/i).exec(navigator.platform);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if a modifier which is not the specified char modifier (and is not shift) is down
|
|
||||||
export function hasShortcutModifier(charModifier, currentModifiers) {
|
|
||||||
var mods = {};
|
|
||||||
for (var key in currentModifiers) {
|
|
||||||
if (parseInt(key) !== KeyTable.XK_Shift_L) {
|
|
||||||
mods[key] = currentModifiers[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sum = 0;
|
|
||||||
for (var k in currentModifiers) {
|
|
||||||
if (mods[k]) {
|
|
||||||
++sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasCharModifier(charModifier, mods)) {
|
|
||||||
return sum > charModifier.length;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return sum > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true if the specified char modifier is currently down
|
// Return true if the specified char modifier is currently down
|
||||||
export function hasCharModifier(charModifier, currentModifiers) {
|
export function hasCharModifier(charModifier, currentModifiers) {
|
||||||
if (charModifier.length === 0) { return false; }
|
if (charModifier.length === 0) { return false; }
|
||||||
|
@ -125,8 +103,6 @@ export function ModifierSync(charModifier) {
|
||||||
// Call this with a non-keyboard event (such as mouse events) to use its modifier state to synchronize anyway
|
// Call this with a non-keyboard event (such as mouse events) to use its modifier state to synchronize anyway
|
||||||
syncAny: function(evt) { return sync(evt);},
|
syncAny: function(evt) { return sync(evt);},
|
||||||
|
|
||||||
// is a shortcut modifier down?
|
|
||||||
hasShortcutModifier: function() { return hasShortcutModifier(charModifier, state); },
|
|
||||||
// if a char modifier is down, return the keys it consists of, otherwise return null
|
// if a char modifier is down, return the keys it consists of, otherwise return null
|
||||||
activeCharModifier: function() { return hasCharModifier(charModifier, state) ? charModifier : null; }
|
activeCharModifier: function() { return hasCharModifier(charModifier, state) ? charModifier : null; }
|
||||||
};
|
};
|
||||||
|
@ -193,15 +169,83 @@ export function getKeycode(evt){
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the most reliable keysym value we can get from a key event
|
// Get the most reliable keysym value we can get from a key event
|
||||||
// if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which
|
|
||||||
export function getKeysym(evt){
|
export function getKeysym(evt){
|
||||||
|
|
||||||
|
// We start with layout independent keys
|
||||||
|
var code = getKeycode(evt);
|
||||||
|
if (code in fixedkeys) {
|
||||||
|
return fixedkeys[code];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next with mildly layout or state sensitive stuff
|
||||||
|
|
||||||
|
// Like AltGraph
|
||||||
|
if (code === 'AltRight') {
|
||||||
|
if (evt.key === 'AltGraph') {
|
||||||
|
return KeyTable.XK_ISO_Level3_Shift;
|
||||||
|
} else {
|
||||||
|
return KeyTable.XK_Alt_R;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or the numpad
|
||||||
|
if (evt.location === 3) {
|
||||||
|
var key = evt.key;
|
||||||
|
|
||||||
|
// 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 doesn't support KeyboardEvent.key yet
|
||||||
|
if ((key === undefined) && (evt.charCode)) {
|
||||||
|
key = String.fromCharCode(evt.charCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we need to look at the Unicode symbol instead
|
||||||
|
|
||||||
var codepoint;
|
var codepoint;
|
||||||
|
|
||||||
if ('key' in evt) {
|
if ('key' in evt) {
|
||||||
// Ignore special keys
|
// Special key? (FIXME: Should have been caught earlier)
|
||||||
if (evt.key.length === 1) {
|
if (evt.key.length !== 1) {
|
||||||
codepoint = evt.key.charCodeAt();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
codepoint = evt.key.charCodeAt();
|
||||||
} else if ('charCode' in evt) {
|
} else if ('charCode' in evt) {
|
||||||
codepoint = evt.charCode;
|
codepoint = evt.charCode;
|
||||||
}
|
}
|
||||||
|
@ -210,94 +254,9 @@ export function getKeysym(evt){
|
||||||
return keysyms.lookup(codepoint);
|
return keysyms.lookup(codepoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evt.keyCode) {
|
|
||||||
return keysymFromKeyCode(evt.keyCode, evt.shiftKey);
|
|
||||||
}
|
|
||||||
if (evt.which) {
|
|
||||||
return keysymFromKeyCode(evt.which, evt.shiftKey);
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given a keycode, try to predict which keysym it might be.
|
|
||||||
// If the keycode is unknown, null is returned.
|
|
||||||
function keysymFromKeyCode(keycode, shiftPressed) {
|
|
||||||
if (typeof(keycode) !== 'number') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// won't be accurate for azerty
|
|
||||||
if (keycode >= 0x30 && keycode <= 0x39) {
|
|
||||||
return keycode; // digit
|
|
||||||
}
|
|
||||||
if (keycode >= 0x41 && keycode <= 0x5a) {
|
|
||||||
// remap to lowercase unless shift is down
|
|
||||||
return shiftPressed ? keycode : keycode + 32; // A-Z
|
|
||||||
}
|
|
||||||
if (keycode >= 0x60 && keycode <= 0x69) {
|
|
||||||
return KeyTable.XK_KP_0 + (keycode - 0x60); // numpad 0-9
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(keycode) {
|
|
||||||
case 0x20: return KeyTable.XK_space;
|
|
||||||
case 0x6a: return KeyTable.XK_KP_Multiply;
|
|
||||||
case 0x6b: return KeyTable.XK_KP_Add;
|
|
||||||
case 0x6c: return KeyTable.XK_KP_Separator;
|
|
||||||
case 0x6d: return KeyTable.XK_KP_Subtract;
|
|
||||||
case 0x6e: return KeyTable.XK_KP_Decimal;
|
|
||||||
case 0x6f: return KeyTable.XK_KP_Divide;
|
|
||||||
case 0xbb: return KeyTable.XK_plus;
|
|
||||||
case 0xbc: return KeyTable.XK_comma;
|
|
||||||
case 0xbd: return KeyTable.XK_minus;
|
|
||||||
case 0xbe: return KeyTable.XK_period;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nonCharacterKey({keyCode: keycode});
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the key is a known non-character key (any key which doesn't generate character data)
|
|
||||||
// return its keysym value. Otherwise return null
|
|
||||||
function nonCharacterKey(evt) {
|
|
||||||
// evt.key not implemented yet
|
|
||||||
if (!evt.keyCode) { return null; }
|
|
||||||
var keycode = evt.keyCode;
|
|
||||||
|
|
||||||
if (keycode >= 0x70 && keycode <= 0x87) {
|
|
||||||
return KeyTable.XK_F1 + keycode - 0x70; // F1-F24
|
|
||||||
}
|
|
||||||
switch (keycode) {
|
|
||||||
|
|
||||||
case 8 : return KeyTable.XK_BackSpace;
|
|
||||||
case 13 : return KeyTable.XK_Return;
|
|
||||||
|
|
||||||
case 9 : return KeyTable.XK_Tab;
|
|
||||||
|
|
||||||
case 27 : return KeyTable.XK_Escape;
|
|
||||||
case 46 : return KeyTable.XK_Delete;
|
|
||||||
|
|
||||||
case 36 : return KeyTable.XK_Home;
|
|
||||||
case 35 : return KeyTable.XK_End;
|
|
||||||
case 33 : return KeyTable.XK_Page_Up;
|
|
||||||
case 34 : return KeyTable.XK_Page_Down;
|
|
||||||
case 45 : return KeyTable.XK_Insert;
|
|
||||||
|
|
||||||
case 37 : return KeyTable.XK_Left;
|
|
||||||
case 38 : return KeyTable.XK_Up;
|
|
||||||
case 39 : return KeyTable.XK_Right;
|
|
||||||
case 40 : return KeyTable.XK_Down;
|
|
||||||
|
|
||||||
case 16 : return KeyTable.XK_Shift_L;
|
|
||||||
case 17 : return KeyTable.XK_Control_L;
|
|
||||||
case 18 : return KeyTable.XK_Alt_L; // also: Option-key on Mac
|
|
||||||
|
|
||||||
case 224 : return KeyTable.XK_Meta_L;
|
|
||||||
case 225 : return KeyTable.XK_ISO_Level3_Shift; // AltGr
|
|
||||||
case 91 : return KeyTable.XK_Super_L; // also: Windows-key
|
|
||||||
case 92 : return KeyTable.XK_Super_R; // also: Windows-key
|
|
||||||
case 93 : return KeyTable.XK_Menu; // also: Windows-Menu, Command on Mac
|
|
||||||
default: return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function QEMUKeyEventDecoder (modifierState, next) {
|
export function QEMUKeyEventDecoder (modifierState, next) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
@ -341,10 +300,7 @@ export function QEMUKeyEventDecoder (modifierState, next) {
|
||||||
result.keysym = getNumPadKeySym(evt);
|
result.keysym = getNumPadKeySym(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
var suppress = type !== 'keydown' || !!getKeysym(evt);
|
||||||
var isShift = result.code === 'ShiftLeft' || result.code === 'ShiftRight';
|
|
||||||
|
|
||||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!nonCharacterKey(evt));
|
|
||||||
|
|
||||||
next(result);
|
next(result);
|
||||||
return suppress;
|
return suppress;
|
||||||
|
@ -457,20 +413,17 @@ export function KeyEventDecoder (modifierState, next) {
|
||||||
|
|
||||||
var keysym = getKeysym(evt);
|
var keysym = getKeysym(evt);
|
||||||
|
|
||||||
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
|
||||||
// Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
|
// Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
|
||||||
// "special" keys like enter, tab or backspace don't send keypress events,
|
// "special" keys like enter, tab or backspace don't send keypress events,
|
||||||
// and some browsers don't send keypresses at all if a modifier is down
|
// and some browsers don't send keypresses at all if a modifier is down
|
||||||
if (keysym && (type !== 'keydown' || nonCharacterKey(evt) || hasModifier)) {
|
if (keysym) {
|
||||||
result.keysym = keysym;
|
result.keysym = keysym;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isShift = code === 'ShiftLeft' || code === 'ShiftRight';
|
|
||||||
|
|
||||||
// Should we prevent the browser from handling the event?
|
// Should we prevent the browser from handling the event?
|
||||||
// Doing so on a keydown (in most browsers) prevents keypress from being generated
|
// Doing so on a keydown (in most browsers) prevents keypress from being generated
|
||||||
// so only do that if we have to.
|
// so only do that if we have to.
|
||||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!nonCharacterKey(evt));
|
var suppress = type !== 'keydown' || !!keysym;
|
||||||
|
|
||||||
// if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
|
// if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
|
||||||
var active = modifierState.activeCharModifier();
|
var active = modifierState.activeCharModifier();
|
||||||
|
|
|
@ -108,26 +108,50 @@ describe('Helpers', function() {
|
||||||
|
|
||||||
describe('Non-character keys', function() {
|
describe('Non-character keys', function() {
|
||||||
it('should recognize the right keys', function() {
|
it('should recognize the right keys', function() {
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 0x0d})).to.be.equal(0xFF0D);
|
expect(KeyboardUtil.getKeysym({code: 'Enter'})).to.be.equal(0xFF0D);
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 0x08})).to.be.equal(0xFF08);
|
expect(KeyboardUtil.getKeysym({code: 'Backspace'})).to.be.equal(0xFF08);
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 0x09})).to.be.equal(0xFF09);
|
expect(KeyboardUtil.getKeysym({code: 'Tab'})).to.be.equal(0xFF09);
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 0x10})).to.be.equal(0xFFE1);
|
expect(KeyboardUtil.getKeysym({code: 'ShiftLeft'})).to.be.equal(0xFFE1);
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 0x11})).to.be.equal(0xFFE3);
|
expect(KeyboardUtil.getKeysym({code: 'ControlLeft'})).to.be.equal(0xFFE3);
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 0x12})).to.be.equal(0xFFE9);
|
expect(KeyboardUtil.getKeysym({code: 'AltLeft'})).to.be.equal(0xFFE9);
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 0xe0})).to.be.equal(0xFFE7);
|
expect(KeyboardUtil.getKeysym({code: 'MetaLeft'})).to.be.equal(0xFFEB);
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 0xe1})).to.be.equal(0xFE03);
|
expect(KeyboardUtil.getKeysym({code: 'Escape'})).to.be.equal(0xFF1B);
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 0x1b})).to.be.equal(0xFF1B);
|
expect(KeyboardUtil.getKeysym({code: 'ArrowUp'})).to.be.equal(0xFF52);
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 0x26})).to.be.equal(0xFF52);
|
|
||||||
});
|
});
|
||||||
it('should return null for unknown keycodes', function() {
|
it('should handle AltGraph', function() {
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 0xc0})).to.be.null;
|
expect(KeyboardUtil.getKeysym({code: 'AltRight', key: 'AltRight'})).to.be.equal(0xFFEA);
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 0xde})).to.be.null;
|
expect(KeyboardUtil.getKeysym({code: 'AltRight', key: 'AltGraph'})).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 not recognize character keys', function() {
|
it('should not recognize character keys', function() {
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: 'A'})).to.be.null;
|
expect(KeyboardUtil.getKeysym({code: 'KeyA'})).to.be.null;
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: '1'})).to.be.null;
|
expect(KeyboardUtil.getKeysym({code: 'Digit1'})).to.be.null;
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: '.'})).to.be.null;
|
expect(KeyboardUtil.getKeysym({code: 'Period'})).to.be.null;
|
||||||
expect(KeyboardUtil.getKeysym({keyCode: ' '})).to.be.null;
|
expect(KeyboardUtil.getKeysym({code: 'Numpad1'})).to.be.null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Numpad', function() {
|
||||||
|
it('should handle Numpad numbers', function() {
|
||||||
|
expect(KeyboardUtil.getKeysym({code: 'Digit5', key: '5', location: 0})).to.be.equal(0x0035);
|
||||||
|
expect(KeyboardUtil.getKeysym({code: 'Numpad5', key: '5', location: 3})).to.be.equal(0xFFB5);
|
||||||
|
});
|
||||||
|
it('should handle Numpad non-character keys', function() {
|
||||||
|
expect(KeyboardUtil.getKeysym({code: 'Home', key: 'Home', location: 0})).to.be.equal(0xFF50);
|
||||||
|
expect(KeyboardUtil.getKeysym({code: 'Numpad5', key: 'Home', location: 3})).to.be.equal(0xFF95);
|
||||||
|
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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -137,7 +161,7 @@ describe('Helpers', function() {
|
||||||
var sync = KeyboardUtil.ModifierSync();
|
var sync = KeyboardUtil.ModifierSync();
|
||||||
it ('should do nothing if all modifiers are up as expected', function() {
|
it ('should do nothing if all modifiers are up as expected', function() {
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
ctrlKey: false,
|
ctrlKey: false,
|
||||||
altKey: false,
|
altKey: false,
|
||||||
altGraphKey: false,
|
altGraphKey: false,
|
||||||
|
@ -147,7 +171,7 @@ describe('Helpers', function() {
|
||||||
});
|
});
|
||||||
it ('should synthesize events if all keys are unexpectedly down', function() {
|
it ('should synthesize events if all keys are unexpectedly down', function() {
|
||||||
var result = sync.keydown({
|
var result = sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
ctrlKey: true,
|
ctrlKey: true,
|
||||||
altKey: true,
|
altKey: true,
|
||||||
altGraphKey: true,
|
altGraphKey: true,
|
||||||
|
@ -167,7 +191,7 @@ describe('Helpers', function() {
|
||||||
});
|
});
|
||||||
it ('should do nothing if all modifiers are down as expected', function() {
|
it ('should do nothing if all modifiers are down as expected', function() {
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
ctrlKey: true,
|
ctrlKey: true,
|
||||||
altKey: true,
|
altKey: true,
|
||||||
altGraphKey: true,
|
altGraphKey: true,
|
||||||
|
@ -180,13 +204,13 @@ describe('Helpers', function() {
|
||||||
var sync = KeyboardUtil.ModifierSync();
|
var sync = KeyboardUtil.ModifierSync();
|
||||||
it('should sync if modifier is suddenly down', function() {
|
it('should sync if modifier is suddenly down', function() {
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
ctrlKey: true,
|
ctrlKey: true,
|
||||||
})).to.be.deep.equal([{keysym: 0xffe3, type: 'keydown'}]);
|
})).to.be.deep.equal([{keysym: 0xffe3, type: 'keydown'}]);
|
||||||
});
|
});
|
||||||
it('should sync if modifier is suddenly up', function() {
|
it('should sync if modifier is suddenly up', function() {
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
ctrlKey: false
|
ctrlKey: false
|
||||||
})).to.be.deep.equal([{keysym: 0xffe3, type: 'keyup'}]);
|
})).to.be.deep.equal([{keysym: 0xffe3, type: 'keyup'}]);
|
||||||
});
|
});
|
||||||
|
@ -195,13 +219,13 @@ describe('Helpers', function() {
|
||||||
var sync = KeyboardUtil.ModifierSync();
|
var sync = KeyboardUtil.ModifierSync();
|
||||||
it('should sync if modifier is suddenly down', function() {
|
it('should sync if modifier is suddenly down', function() {
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
altKey: true,
|
altKey: true,
|
||||||
})).to.be.deep.equal([{keysym: 0xffe9, type: 'keydown'}]);
|
})).to.be.deep.equal([{keysym: 0xffe9, type: 'keydown'}]);
|
||||||
});
|
});
|
||||||
it('should sync if modifier is suddenly up', function() {
|
it('should sync if modifier is suddenly up', function() {
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
altKey: false
|
altKey: false
|
||||||
})).to.be.deep.equal([{keysym: 0xffe9, type: 'keyup'}]);
|
})).to.be.deep.equal([{keysym: 0xffe9, type: 'keyup'}]);
|
||||||
});
|
});
|
||||||
|
@ -210,13 +234,13 @@ describe('Helpers', function() {
|
||||||
var sync = KeyboardUtil.ModifierSync();
|
var sync = KeyboardUtil.ModifierSync();
|
||||||
it('should sync if modifier is suddenly down', function() {
|
it('should sync if modifier is suddenly down', function() {
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
altGraphKey: true,
|
altGraphKey: true,
|
||||||
})).to.be.deep.equal([{keysym: 0xfe03, type: 'keydown'}]);
|
})).to.be.deep.equal([{keysym: 0xfe03, type: 'keydown'}]);
|
||||||
});
|
});
|
||||||
it('should sync if modifier is suddenly up', function() {
|
it('should sync if modifier is suddenly up', function() {
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
altGraphKey: false
|
altGraphKey: false
|
||||||
})).to.be.deep.equal([{keysym: 0xfe03, type: 'keyup'}]);
|
})).to.be.deep.equal([{keysym: 0xfe03, type: 'keyup'}]);
|
||||||
});
|
});
|
||||||
|
@ -225,13 +249,13 @@ describe('Helpers', function() {
|
||||||
var sync = KeyboardUtil.ModifierSync();
|
var sync = KeyboardUtil.ModifierSync();
|
||||||
it('should sync if modifier is suddenly down', function() {
|
it('should sync if modifier is suddenly down', function() {
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
shiftKey: true,
|
shiftKey: true,
|
||||||
})).to.be.deep.equal([{keysym: 0xffe1, type: 'keydown'}]);
|
})).to.be.deep.equal([{keysym: 0xffe1, type: 'keydown'}]);
|
||||||
});
|
});
|
||||||
it('should sync if modifier is suddenly up', function() {
|
it('should sync if modifier is suddenly up', function() {
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
shiftKey: false
|
shiftKey: false
|
||||||
})).to.be.deep.equal([{keysym: 0xffe1, type: 'keyup'}]);
|
})).to.be.deep.equal([{keysym: 0xffe1, type: 'keyup'}]);
|
||||||
});
|
});
|
||||||
|
@ -240,13 +264,13 @@ describe('Helpers', function() {
|
||||||
var sync = KeyboardUtil.ModifierSync();
|
var sync = KeyboardUtil.ModifierSync();
|
||||||
it('should sync if modifier is suddenly down', function() {
|
it('should sync if modifier is suddenly down', function() {
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
metaKey: true,
|
metaKey: true,
|
||||||
})).to.be.deep.equal([{keysym: 0xffe7, type: 'keydown'}]);
|
})).to.be.deep.equal([{keysym: 0xffe7, type: 'keydown'}]);
|
||||||
});
|
});
|
||||||
it('should sync if modifier is suddenly up', function() {
|
it('should sync if modifier is suddenly up', function() {
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
metaKey: false
|
metaKey: false
|
||||||
})).to.be.deep.equal([{keysym: 0xffe7, type: 'keyup'}]);
|
})).to.be.deep.equal([{keysym: 0xffe7, type: 'keyup'}]);
|
||||||
});
|
});
|
||||||
|
@ -254,27 +278,27 @@ describe('Helpers', function() {
|
||||||
describe('Modifier keyevents', function() {
|
describe('Modifier keyevents', function() {
|
||||||
it('should not sync a modifier on its own events', function() {
|
it('should not sync a modifier on its own events', function() {
|
||||||
expect(KeyboardUtil.ModifierSync().keydown({
|
expect(KeyboardUtil.ModifierSync().keydown({
|
||||||
keyCode: 0x11,
|
code: 'ControlLeft',
|
||||||
ctrlKey: false
|
ctrlKey: false
|
||||||
})).to.be.deep.equal([]);
|
})).to.be.deep.equal([]);
|
||||||
expect(KeyboardUtil.ModifierSync().keydown({
|
expect(KeyboardUtil.ModifierSync().keydown({
|
||||||
keyCode: 0x11,
|
code: 'ControlLeft',
|
||||||
ctrlKey: true
|
ctrlKey: true
|
||||||
}), 'B').to.be.deep.equal([]);
|
}), 'B').to.be.deep.equal([]);
|
||||||
})
|
})
|
||||||
it('should update state on modifier keyevents', function() {
|
it('should update state on modifier keyevents', function() {
|
||||||
var sync = KeyboardUtil.ModifierSync();
|
var sync = KeyboardUtil.ModifierSync();
|
||||||
sync.keydown({
|
sync.keydown({
|
||||||
keyCode: 0x11,
|
code: 'ControlLeft',
|
||||||
});
|
});
|
||||||
expect(sync.keydown({
|
expect(sync.keydown({
|
||||||
keyCode: 0x41,
|
code: 'KeyA',
|
||||||
ctrlKey: true,
|
ctrlKey: true,
|
||||||
})).to.be.deep.equal([]);
|
})).to.be.deep.equal([]);
|
||||||
});
|
});
|
||||||
it('should sync other modifiers on ctrl events', function() {
|
it('should sync other modifiers on ctrl events', function() {
|
||||||
expect(KeyboardUtil.ModifierSync().keydown({
|
expect(KeyboardUtil.ModifierSync().keydown({
|
||||||
keyCode: 0x11,
|
code: 'ControlLeft',
|
||||||
altKey: true
|
altKey: true
|
||||||
})).to.be.deep.equal([{keysym: 0xffe9, type: 'keydown'}]);
|
})).to.be.deep.equal([{keysym: 0xffe9, type: 'keydown'}]);
|
||||||
})
|
})
|
||||||
|
@ -287,9 +311,6 @@ describe('Helpers', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('do not treat shift as a modifier key', function() {
|
describe('do not treat shift as a modifier key', function() {
|
||||||
it('should not treat shift as a shortcut modifier', function() {
|
|
||||||
expect(KeyboardUtil.hasShortcutModifier([], {0xffe1 : true})).to.be.false;
|
|
||||||
});
|
|
||||||
it('should not treat shift as a char modifier', function() {
|
it('should not treat shift as a char modifier', function() {
|
||||||
expect(KeyboardUtil.hasCharModifier([], {0xffe1 : true})).to.be.false;
|
expect(KeyboardUtil.hasCharModifier([], {0xffe1 : true})).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -51,7 +51,7 @@ describe('Key Event Pipeline Stages', function() {
|
||||||
});
|
});
|
||||||
it('should forward keydown events with the right type', function(done) {
|
it('should forward keydown events with the right type', function(done) {
|
||||||
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
||||||
expect(evt).to.be.deep.equal({code: 'KeyA', type: 'keydown'});
|
expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keydown'});
|
||||||
done();
|
done();
|
||||||
}).keydown({code: 'KeyA', key: 'a'});
|
}).keydown({code: 'KeyA', key: 'a'});
|
||||||
});
|
});
|
||||||
|
@ -71,65 +71,57 @@ describe('Key Event Pipeline Stages', function() {
|
||||||
it('should suppress anything while a shortcut modifier is down', function() {
|
it('should suppress anything while a shortcut modifier is down', function() {
|
||||||
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
||||||
|
|
||||||
obj.keydown({keyCode: 0x11}); // press ctrl
|
obj.keydown({code: 'ControlLeft'});
|
||||||
expect(obj.keydown({key: 'A'})).to.be.true;
|
expect(obj.keydown({code: 'KeyA', key: 'a'})).to.be.true;
|
||||||
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.true;
|
expect(obj.keydown({code: 'Space', key: ' '})).to.be.true;
|
||||||
expect(obj.keydown({key: '1'})).to.be.true;
|
expect(obj.keydown({code: 'Digit1', key: '1'})).to.be.true;
|
||||||
expect(obj.keydown({key: '<'})).to.be.true;
|
expect(obj.keydown({code: 'IntlBackslash', key: '<'})).to.be.true;
|
||||||
expect(obj.keydown({key: 'ø'})).to.be.true;
|
expect(obj.keydown({code: 'Semicolon', key: 'ø'})).to.be.true;
|
||||||
});
|
});
|
||||||
it('should suppress non-character keys', function() {
|
it('should suppress non-character keys', function() {
|
||||||
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
||||||
|
|
||||||
expect(obj.keydown({keyCode: 0x08}), 'a').to.be.true;
|
expect(obj.keydown({code: 'Backspace'}), 'a').to.be.true;
|
||||||
expect(obj.keydown({keyCode: 0x09}), 'b').to.be.true;
|
expect(obj.keydown({code: 'Tab'}), 'b').to.be.true;
|
||||||
expect(obj.keydown({keyCode: 0x11}), 'd').to.be.true;
|
expect(obj.keydown({code: 'ControlLeft'}), 'd').to.be.true;
|
||||||
expect(obj.keydown({keyCode: 0x12}), 'e').to.be.true;
|
expect(obj.keydown({code: 'AltLeft'}), 'e').to.be.true;
|
||||||
});
|
|
||||||
it('should not suppress shift', function() {
|
|
||||||
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
|
||||||
|
|
||||||
expect(obj.keydown({keyCode: 0x10}), 'd').to.be.false;
|
|
||||||
});
|
});
|
||||||
it('should generate event for shift keydown', function() {
|
it('should generate event for shift keydown', function() {
|
||||||
var called = false;
|
var called = false;
|
||||||
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
||||||
expect(evt).to.have.property('keysym');
|
expect(evt).to.have.property('keysym');
|
||||||
called = true;
|
called = true;
|
||||||
}).keydown({keyCode: 0x10});
|
}).keydown({code: 'ShiftLeft'});
|
||||||
expect(called).to.be.true;
|
expect(called).to.be.true;
|
||||||
});
|
});
|
||||||
it('should not suppress character keys', function() {
|
it('should suppress character keys with key', function() {
|
||||||
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
||||||
|
|
||||||
expect(obj.keydown({key: 'A'})).to.be.false;
|
expect(obj.keydown({code: 'KeyA', key: 'a'})).to.be.true;
|
||||||
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false;
|
expect(obj.keydown({code: 'Digit1', key: '1'})).to.be.true;
|
||||||
expect(obj.keydown({key: '1'})).to.be.false;
|
expect(obj.keydown({code: 'IntlBackslash', key: '<'})).to.be.true;
|
||||||
expect(obj.keydown({key: '<'})).to.be.false; // < key on DK Windows
|
expect(obj.keydown({code: 'Semicolon', key: 'ø'})).to.be.true;
|
||||||
expect(obj.keydown({key: 'ø'})).to.be.false; // Ø key on DK
|
|
||||||
});
|
});
|
||||||
it('should not suppress if a char modifier is down', function() {
|
it('should not suppress character keys without key', function() {
|
||||||
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {});
|
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
||||||
|
|
||||||
obj.keydown({keyCode: 0xe1}); // press altgr
|
expect(obj.keydown({code: 'KeyA'})).to.be.false;
|
||||||
expect(obj.keydown({key: 'A'})).to.be.false;
|
expect(obj.keydown({code: 'Digit1'})).to.be.false;
|
||||||
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false;
|
expect(obj.keydown({code: 'IntlBackslash'})).to.be.false;
|
||||||
expect(obj.keydown({key: '1'})).to.be.false;
|
expect(obj.keydown({code: 'Semicolon'})).to.be.false;
|
||||||
expect(obj.keydown({key: '<'})).to.be.false;
|
|
||||||
expect(obj.keydown({key: 'ø'})).to.be.false;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('Keypress and keyup events', function() {
|
describe('Keypress and keyup events', function() {
|
||||||
it('should always suppress event propagation', function() {
|
it('should always suppress event propagation', function() {
|
||||||
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
||||||
|
|
||||||
expect(obj.keypress({key: 'A'})).to.be.true;
|
expect(obj.keypress({code: 'KeyA', key: 'a'})).to.be.true;
|
||||||
expect(obj.keypress({key: '<'})).to.be.true;
|
expect(obj.keypress({code: 'IntlBackslash', key: '<'})).to.be.true;
|
||||||
expect(obj.keypress({keyCode: 0x11})).to.be.true;
|
expect(obj.keypress({code: 'ControlLeft', key: 'Control'})).to.be.true;
|
||||||
|
|
||||||
expect(obj.keyup({key: 'A'})).to.be.true;
|
expect(obj.keyup({code: 'KeyA', key: 'a'})).to.be.true;
|
||||||
expect(obj.keyup({key: '<'})).to.be.true;
|
expect(obj.keyup({code: 'IntlBackslash', key: '<'})).to.be.true;
|
||||||
expect(obj.keyup({keyCode: 0x11})).to.be.true;
|
expect(obj.keyup({code: 'ControlLeft', key: 'Control'})).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('mark events if a char modifier is down', function() {
|
describe('mark events if a char modifier is down', function() {
|
||||||
|
@ -145,7 +137,7 @@ describe('Key Event Pipeline Stages', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
obj.keydown({keyCode: 0xe1}); // press altgr
|
obj.keydown({code: 'AltRight', key: 'AltGraph'})
|
||||||
obj.keydown({code: 'KeyA', key: 'a'});
|
obj.keydown({code: 'KeyA', key: 'a'});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -167,7 +159,7 @@ describe('Key Event Pipeline Stages', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
obj.keydown({keyCode: 0xe1}); // press altgr
|
obj.keydown({code: 'AltRight', key: 'AltGraph'})
|
||||||
obj.keypress({code: 'KeyA', key: 'a'});
|
obj.keypress({code: 'KeyA', key: 'a'});
|
||||||
});
|
});
|
||||||
it('should indicate on events if a multi-key char modifier is down', function(done) {
|
it('should indicate on events if a multi-key char modifier is down', function(done) {
|
||||||
|
@ -190,8 +182,8 @@ describe('Key Event Pipeline Stages', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
obj.keydown({keyCode: 0x11}); // press ctrl
|
obj.keydown({code: 'ControlLeft'});
|
||||||
obj.keydown({keyCode: 0x12}); // press alt
|
obj.keydown({code: 'AltLeft'});
|
||||||
obj.keypress({code: 'KeyA', key: 'a'});
|
obj.keypress({code: 'KeyA', key: 'a'});
|
||||||
});
|
});
|
||||||
it('should not consider a char modifier to be down on the modifier key itself', function() {
|
it('should not consider a char modifier to be down on the modifier key itself', function() {
|
||||||
|
@ -199,60 +191,10 @@ describe('Key Event Pipeline Stages', function() {
|
||||||
expect(evt).to.not.have.property('escape');
|
expect(evt).to.not.have.property('escape');
|
||||||
});
|
});
|
||||||
|
|
||||||
obj.keydown({keyCode: 0xe1}); // press altgr
|
obj.keydown({code: 'AltRight', key: 'AltGraph'})
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('add/remove keysym', function() {
|
|
||||||
it('should remove keysym from keydown if a char key and no modifier', function() {
|
|
||||||
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
||||||
expect(evt).to.be.deep.equal({code: 'KeyA', type: 'keydown'});
|
|
||||||
}).keydown({code: 'KeyA', key: 'a'});
|
|
||||||
});
|
|
||||||
it('should not remove keysym from keydown if a shortcut modifier is down', function() {
|
|
||||||
var times_called = 0;
|
|
||||||
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
||||||
switch (times_called++) {
|
|
||||||
case 1:
|
|
||||||
expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keydown'});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}).keydown({code: 'KeyA', key: 'a', ctrlKey: true});
|
|
||||||
expect(times_called).to.be.equal(2);
|
|
||||||
});
|
|
||||||
it('should not remove keysym from keydown if a char modifier is down', function() {
|
|
||||||
var times_called = 0;
|
|
||||||
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {
|
|
||||||
switch (times_called++) {
|
|
||||||
case 1:
|
|
||||||
expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keydown'});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}).keydown({code: 'KeyA', key: 'a', altGraphKey: true});
|
|
||||||
expect(times_called).to.be.equal(2);
|
|
||||||
});
|
|
||||||
it('should not remove keysym from keydown if key is noncharacter', function() {
|
|
||||||
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
||||||
expect(evt, 'tab').to.be.deep.equal({code: 'Tab', keysym: 0xff09, type: 'keydown'});
|
|
||||||
}).keydown({keyCode: 0x09});
|
|
||||||
|
|
||||||
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
||||||
expect(evt, 'ctrl').to.be.deep.equal({code: 'ControlLeft', keysym: 0xffe3, type: 'keydown'});
|
|
||||||
}).keydown({keyCode: 0x11});
|
|
||||||
});
|
|
||||||
it('should never remove keysym from keypress', function() {
|
|
||||||
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
||||||
expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keypress'});
|
|
||||||
}).keypress({code: 'KeyA', key: 'a'});
|
|
||||||
});
|
|
||||||
it('should never remove keysym from keyup', function() {
|
|
||||||
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
||||||
expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keyup'});
|
|
||||||
}).keyup({code: 'KeyA', key: 'a'});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// on keypress, keyup(?), always set keysym
|
|
||||||
// on keydown, only do it if we don't expect a keypress: if noncharacter OR modifier is down
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Track Key State', function() {
|
describe('Track Key State', function() {
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
'core': ["base64.js", "websock.js", "des.js", "input/keysym.js",
|
'core': ["base64.js", "websock.js", "des.js", "input/keysym.js",
|
||||||
"input/keysymdef.js", "input/xtscancodes.js", "input/util.js",
|
"input/keysymdef.js", "input/xtscancodes.js", "input/util.js",
|
||||||
"input/devices.js", "display.js", "rfb.js", "inflator.js",
|
"input/devices.js", "display.js", "rfb.js", "inflator.js",
|
||||||
"input/vkeys.js"],
|
"input/vkeys.js", "input/fixedkeys.js"],
|
||||||
'tests': ["playback.js"],
|
'tests': ["playback.js"],
|
||||||
'recordings': [fname]});
|
'recordings': [fname]});
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue