mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
720 lines
17 KiB
TypeScript
720 lines
17 KiB
TypeScript
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import {PUNCTUATION_KEY_CODES} from '@fluxer/desktop_ipc/src/GlobalShortcutKeyNames';
|
|
|
|
const EVDEV_KEY_CODES: ReadonlyMap<number, string> = new Map([
|
|
[0x001, 'Escape'],
|
|
[0x002, 'Digit1'],
|
|
[0x003, 'Digit2'],
|
|
[0x004, 'Digit3'],
|
|
[0x005, 'Digit4'],
|
|
[0x006, 'Digit5'],
|
|
[0x007, 'Digit6'],
|
|
[0x008, 'Digit7'],
|
|
[0x009, 'Digit8'],
|
|
[0x00a, 'Digit9'],
|
|
[0x00b, 'Digit0'],
|
|
[0x00c, 'Minus'],
|
|
[0x00d, 'Equal'],
|
|
[0x00e, 'Backspace'],
|
|
[0x00f, 'Tab'],
|
|
[0x010, 'KeyQ'],
|
|
[0x011, 'KeyW'],
|
|
[0x012, 'KeyE'],
|
|
[0x013, 'KeyR'],
|
|
[0x014, 'KeyT'],
|
|
[0x015, 'KeyY'],
|
|
[0x016, 'KeyU'],
|
|
[0x017, 'KeyI'],
|
|
[0x018, 'KeyO'],
|
|
[0x019, 'KeyP'],
|
|
[0x01a, 'BracketLeft'],
|
|
[0x01b, 'BracketRight'],
|
|
[0x01c, 'Enter'],
|
|
[0x01d, 'ControlLeft'],
|
|
[0x01e, 'KeyA'],
|
|
[0x01f, 'KeyS'],
|
|
[0x020, 'KeyD'],
|
|
[0x021, 'KeyF'],
|
|
[0x022, 'KeyG'],
|
|
[0x023, 'KeyH'],
|
|
[0x024, 'KeyJ'],
|
|
[0x025, 'KeyK'],
|
|
[0x026, 'KeyL'],
|
|
[0x027, 'Semicolon'],
|
|
[0x028, 'Quote'],
|
|
[0x029, 'Backquote'],
|
|
[0x02a, 'ShiftLeft'],
|
|
[0x02b, 'Backslash'],
|
|
[0x02c, 'KeyZ'],
|
|
[0x02d, 'KeyX'],
|
|
[0x02e, 'KeyC'],
|
|
[0x02f, 'KeyV'],
|
|
[0x030, 'KeyB'],
|
|
[0x031, 'KeyN'],
|
|
[0x032, 'KeyM'],
|
|
[0x033, 'Comma'],
|
|
[0x034, 'Period'],
|
|
[0x035, 'Slash'],
|
|
[0x036, 'ShiftRight'],
|
|
[0x037, 'NumpadMultiply'],
|
|
[0x038, 'AltLeft'],
|
|
[0x039, 'Space'],
|
|
[0x03a, 'CapsLock'],
|
|
[0x03b, 'F1'],
|
|
[0x03c, 'F2'],
|
|
[0x03d, 'F3'],
|
|
[0x03e, 'F4'],
|
|
[0x03f, 'F5'],
|
|
[0x040, 'F6'],
|
|
[0x041, 'F7'],
|
|
[0x042, 'F8'],
|
|
[0x043, 'F9'],
|
|
[0x044, 'F10'],
|
|
[0x045, 'NumLock'],
|
|
[0x046, 'ScrollLock'],
|
|
[0x047, 'Numpad7'],
|
|
[0x048, 'Numpad8'],
|
|
[0x049, 'Numpad9'],
|
|
[0x04a, 'NumpadSubtract'],
|
|
[0x04b, 'Numpad4'],
|
|
[0x04c, 'Numpad5'],
|
|
[0x04d, 'Numpad6'],
|
|
[0x04e, 'NumpadAdd'],
|
|
[0x04f, 'Numpad1'],
|
|
[0x050, 'Numpad2'],
|
|
[0x051, 'Numpad3'],
|
|
[0x052, 'Numpad0'],
|
|
[0x053, 'NumpadDecimal'],
|
|
[0x055, 'Lang5'],
|
|
[0x056, 'IntlBackslash'],
|
|
[0x057, 'F11'],
|
|
[0x058, 'F12'],
|
|
[0x059, 'IntlRo'],
|
|
[0x05a, 'Lang3'],
|
|
[0x05b, 'Lang4'],
|
|
[0x05c, 'Convert'],
|
|
[0x05d, 'KanaMode'],
|
|
[0x05e, 'NonConvert'],
|
|
[0x060, 'NumpadEnter'],
|
|
[0x061, 'ControlRight'],
|
|
[0x062, 'NumpadDivide'],
|
|
[0x063, 'PrintScreen'],
|
|
[0x064, 'AltRight'],
|
|
[0x066, 'Home'],
|
|
[0x067, 'ArrowUp'],
|
|
[0x068, 'PageUp'],
|
|
[0x069, 'ArrowLeft'],
|
|
[0x06a, 'ArrowRight'],
|
|
[0x06b, 'End'],
|
|
[0x06c, 'ArrowDown'],
|
|
[0x06d, 'PageDown'],
|
|
[0x06e, 'Insert'],
|
|
[0x06f, 'Delete'],
|
|
[0x071, 'AudioVolumeMute'],
|
|
[0x072, 'AudioVolumeDown'],
|
|
[0x073, 'AudioVolumeUp'],
|
|
[0x074, 'Power'],
|
|
[0x075, 'NumpadEqual'],
|
|
[0x077, 'Pause'],
|
|
[0x078, 'ShowAllWindows'],
|
|
[0x079, 'NumpadComma'],
|
|
[0x07a, 'Lang1'],
|
|
[0x07b, 'Lang2'],
|
|
[0x07c, 'IntlYen'],
|
|
[0x07d, 'MetaLeft'],
|
|
[0x07e, 'MetaRight'],
|
|
[0x07f, 'ContextMenu'],
|
|
[0x080, 'BrowserStop'],
|
|
[0x081, 'Again'],
|
|
[0x083, 'Undo'],
|
|
[0x084, 'Select'],
|
|
[0x085, 'Copy'],
|
|
[0x086, 'Open'],
|
|
[0x087, 'Paste'],
|
|
[0x088, 'Find'],
|
|
[0x089, 'Cut'],
|
|
[0x08a, 'Help'],
|
|
[0x08c, 'LaunchApp2'],
|
|
[0x08e, 'Sleep'],
|
|
[0x08f, 'WakeUp'],
|
|
[0x090, 'LaunchApp1'],
|
|
[0x094, 'LaunchApp1'],
|
|
[0x095, 'LaunchApp2'],
|
|
[0x09b, 'LaunchMail'],
|
|
[0x09c, 'BrowserFavorites'],
|
|
[0x09e, 'BrowserBack'],
|
|
[0x09f, 'BrowserForward'],
|
|
[0x0a1, 'Eject'],
|
|
[0x0a3, 'MediaTrackNext'],
|
|
[0x0a4, 'MediaPlayPause'],
|
|
[0x0a5, 'MediaTrackPrevious'],
|
|
[0x0a6, 'MediaStop'],
|
|
[0x0a7, 'MediaRecord'],
|
|
[0x0a8, 'MediaRewind'],
|
|
[0x0ab, 'MediaSelect'],
|
|
[0x0ac, 'BrowserHome'],
|
|
[0x0ad, 'BrowserRefresh'],
|
|
[0x0b3, 'NumpadParenLeft'],
|
|
[0x0b4, 'NumpadParenRight'],
|
|
[0x0b7, 'F13'],
|
|
[0x0b8, 'F14'],
|
|
[0x0b9, 'F15'],
|
|
[0x0ba, 'F16'],
|
|
[0x0bb, 'F17'],
|
|
[0x0bc, 'F18'],
|
|
[0x0bd, 'F19'],
|
|
[0x0be, 'F20'],
|
|
[0x0bf, 'F21'],
|
|
[0x0c0, 'F22'],
|
|
[0x0c1, 'F23'],
|
|
[0x0c2, 'F24'],
|
|
[0x0c9, 'MediaPause'],
|
|
[0x0cf, 'MediaPlay'],
|
|
[0x0d0, 'MediaFastForward'],
|
|
[0x0d9, 'BrowserSearch'],
|
|
[0x0e0, 'BrightnessDown'],
|
|
[0x0e1, 'BrightnessUp'],
|
|
[0x0e2, 'MediaSelect'],
|
|
[0x0e3, 'DisplayToggleIntExt'],
|
|
[0x0e7, 'MailSend'],
|
|
[0x0e8, 'MailReply'],
|
|
[0x0e9, 'MailForward'],
|
|
[0x16c, 'BrowserFavorites'],
|
|
[0x174, 'ZoomToggle'],
|
|
[0x1d0, 'Fn'],
|
|
[0x243, 'LaunchControlPanel'],
|
|
[0x244, 'SelectTask'],
|
|
[0x245, 'LaunchScreenSaver'],
|
|
[0x247, 'LaunchAssistant'],
|
|
[0x248, 'KeyboardLayoutSelect'],
|
|
]);
|
|
|
|
const WINDOWS_SCAN_CODES: ReadonlyMap<number, string> = new Map([
|
|
[0x0001, 'Escape'],
|
|
[0x0002, 'Digit1'],
|
|
[0x0003, 'Digit2'],
|
|
[0x0004, 'Digit3'],
|
|
[0x0005, 'Digit4'],
|
|
[0x0006, 'Digit5'],
|
|
[0x0007, 'Digit6'],
|
|
[0x0008, 'Digit7'],
|
|
[0x0009, 'Digit8'],
|
|
[0x000a, 'Digit9'],
|
|
[0x000b, 'Digit0'],
|
|
[0x000c, 'Minus'],
|
|
[0x000d, 'Equal'],
|
|
[0x000e, 'Backspace'],
|
|
[0x000f, 'Tab'],
|
|
[0x0010, 'KeyQ'],
|
|
[0x0011, 'KeyW'],
|
|
[0x0012, 'KeyE'],
|
|
[0x0013, 'KeyR'],
|
|
[0x0014, 'KeyT'],
|
|
[0x0015, 'KeyY'],
|
|
[0x0016, 'KeyU'],
|
|
[0x0017, 'KeyI'],
|
|
[0x0018, 'KeyO'],
|
|
[0x0019, 'KeyP'],
|
|
[0x001a, 'BracketLeft'],
|
|
[0x001b, 'BracketRight'],
|
|
[0x001c, 'Enter'],
|
|
[0x001d, 'ControlLeft'],
|
|
[0x001e, 'KeyA'],
|
|
[0x001f, 'KeyS'],
|
|
[0x0020, 'KeyD'],
|
|
[0x0021, 'KeyF'],
|
|
[0x0022, 'KeyG'],
|
|
[0x0023, 'KeyH'],
|
|
[0x0024, 'KeyJ'],
|
|
[0x0025, 'KeyK'],
|
|
[0x0026, 'KeyL'],
|
|
[0x0027, 'Semicolon'],
|
|
[0x0028, 'Quote'],
|
|
[0x0029, 'Backquote'],
|
|
[0x002a, 'ShiftLeft'],
|
|
[0x002b, 'Backslash'],
|
|
[0x002c, 'KeyZ'],
|
|
[0x002d, 'KeyX'],
|
|
[0x002e, 'KeyC'],
|
|
[0x002f, 'KeyV'],
|
|
[0x0030, 'KeyB'],
|
|
[0x0031, 'KeyN'],
|
|
[0x0032, 'KeyM'],
|
|
[0x0033, 'Comma'],
|
|
[0x0034, 'Period'],
|
|
[0x0035, 'Slash'],
|
|
[0x0036, 'ShiftRight'],
|
|
[0x0037, 'NumpadMultiply'],
|
|
[0x0038, 'AltLeft'],
|
|
[0x0039, 'Space'],
|
|
[0x003a, 'CapsLock'],
|
|
[0x003b, 'F1'],
|
|
[0x003c, 'F2'],
|
|
[0x003d, 'F3'],
|
|
[0x003e, 'F4'],
|
|
[0x003f, 'F5'],
|
|
[0x0040, 'F6'],
|
|
[0x0041, 'F7'],
|
|
[0x0042, 'F8'],
|
|
[0x0043, 'F9'],
|
|
[0x0044, 'F10'],
|
|
[0x0045, 'Pause'],
|
|
[0x0046, 'ScrollLock'],
|
|
[0x0047, 'Numpad7'],
|
|
[0x0048, 'Numpad8'],
|
|
[0x0049, 'Numpad9'],
|
|
[0x004a, 'NumpadSubtract'],
|
|
[0x004b, 'Numpad4'],
|
|
[0x004c, 'Numpad5'],
|
|
[0x004d, 'Numpad6'],
|
|
[0x004e, 'NumpadAdd'],
|
|
[0x004f, 'Numpad1'],
|
|
[0x0050, 'Numpad2'],
|
|
[0x0051, 'Numpad3'],
|
|
[0x0052, 'Numpad0'],
|
|
[0x0053, 'NumpadDecimal'],
|
|
[0x0054, 'PrintScreen'],
|
|
[0x0056, 'IntlBackslash'],
|
|
[0x0057, 'F11'],
|
|
[0x0058, 'F12'],
|
|
[0x0059, 'NumpadEqual'],
|
|
[0x0064, 'F13'],
|
|
[0x0065, 'F14'],
|
|
[0x0066, 'F15'],
|
|
[0x0067, 'F16'],
|
|
[0x0068, 'F17'],
|
|
[0x0069, 'F18'],
|
|
[0x006a, 'F19'],
|
|
[0x006b, 'F20'],
|
|
[0x006c, 'F21'],
|
|
[0x006d, 'F22'],
|
|
[0x006e, 'F23'],
|
|
[0x0070, 'KanaMode'],
|
|
[0x0071, 'Lang2'],
|
|
[0x0072, 'Lang1'],
|
|
[0x0073, 'IntlRo'],
|
|
[0x0076, 'F24'],
|
|
[0x0077, 'Lang4'],
|
|
[0x0078, 'Lang3'],
|
|
[0x0079, 'Convert'],
|
|
[0x007b, 'NonConvert'],
|
|
[0x007d, 'IntlYen'],
|
|
[0x007e, 'NumpadComma'],
|
|
[0xe008, 'Undo'],
|
|
[0xe00a, 'Paste'],
|
|
[0xe010, 'MediaTrackPrevious'],
|
|
[0xe017, 'Cut'],
|
|
[0xe018, 'Copy'],
|
|
[0xe019, 'MediaTrackNext'],
|
|
[0xe01c, 'NumpadEnter'],
|
|
[0xe01d, 'ControlRight'],
|
|
[0xe020, 'AudioVolumeMute'],
|
|
[0xe021, 'LaunchApp2'],
|
|
[0xe022, 'MediaPlayPause'],
|
|
[0xe024, 'MediaStop'],
|
|
[0xe02c, 'Eject'],
|
|
[0xe02e, 'AudioVolumeDown'],
|
|
[0xe030, 'AudioVolumeUp'],
|
|
[0xe032, 'BrowserHome'],
|
|
[0xe035, 'NumpadDivide'],
|
|
[0xe037, 'PrintScreen'],
|
|
[0xe038, 'AltRight'],
|
|
[0xe03b, 'Help'],
|
|
[0xe045, 'NumLock'],
|
|
[0xe046, 'Pause'],
|
|
[0xe047, 'Home'],
|
|
[0xe048, 'ArrowUp'],
|
|
[0xe049, 'PageUp'],
|
|
[0xe04b, 'ArrowLeft'],
|
|
[0xe04d, 'ArrowRight'],
|
|
[0xe04f, 'End'],
|
|
[0xe050, 'ArrowDown'],
|
|
[0xe051, 'PageDown'],
|
|
[0xe052, 'Insert'],
|
|
[0xe053, 'Delete'],
|
|
[0xe05b, 'MetaLeft'],
|
|
[0xe05c, 'MetaRight'],
|
|
[0xe05d, 'ContextMenu'],
|
|
[0xe05e, 'Power'],
|
|
[0xe05f, 'Sleep'],
|
|
[0xe063, 'WakeUp'],
|
|
[0xe065, 'BrowserSearch'],
|
|
[0xe066, 'BrowserFavorites'],
|
|
[0xe067, 'BrowserRefresh'],
|
|
[0xe068, 'BrowserStop'],
|
|
[0xe069, 'BrowserForward'],
|
|
[0xe06a, 'BrowserBack'],
|
|
[0xe06b, 'LaunchApp1'],
|
|
[0xe06c, 'LaunchMail'],
|
|
[0xe06d, 'MediaSelect'],
|
|
]);
|
|
|
|
const WINDOWS_VIRTUAL_KEY_CODES: ReadonlyMap<number, string> = new Map([
|
|
[0x08, 'Backspace'],
|
|
[0x09, 'Tab'],
|
|
[0x0c, 'Numpad5'],
|
|
[0x0d, 'Enter'],
|
|
[0x10, 'ShiftLeft'],
|
|
[0x11, 'ControlLeft'],
|
|
[0x12, 'AltLeft'],
|
|
[0x13, 'Pause'],
|
|
[0x14, 'CapsLock'],
|
|
[0x15, 'KanaMode'],
|
|
[0x1b, 'Escape'],
|
|
[0x1c, 'Convert'],
|
|
[0x1d, 'NonConvert'],
|
|
[0x20, 'Space'],
|
|
[0x21, 'PageUp'],
|
|
[0x22, 'PageDown'],
|
|
[0x23, 'End'],
|
|
[0x24, 'Home'],
|
|
[0x25, 'ArrowLeft'],
|
|
[0x26, 'ArrowUp'],
|
|
[0x27, 'ArrowRight'],
|
|
[0x28, 'ArrowDown'],
|
|
[0x2c, 'PrintScreen'],
|
|
[0x2d, 'Insert'],
|
|
[0x2e, 'Delete'],
|
|
...Array.from({length: 10}, (_, index): [number, string] => [0x30 + index, `Digit${index}`]),
|
|
...Array.from({length: 26}, (_, index): [number, string] => [
|
|
0x41 + index,
|
|
`Key${String.fromCharCode(0x41 + index)}`,
|
|
]),
|
|
[0x5b, 'MetaLeft'],
|
|
[0x5c, 'MetaRight'],
|
|
[0x5d, 'ContextMenu'],
|
|
[0x5f, 'Sleep'],
|
|
...Array.from({length: 10}, (_, index): [number, string] => [0x60 + index, `Numpad${index}`]),
|
|
[0x6a, 'NumpadMultiply'],
|
|
[0x6b, 'NumpadAdd'],
|
|
[0x6c, 'NumpadComma'],
|
|
[0x6d, 'NumpadSubtract'],
|
|
[0x6e, 'NumpadDecimal'],
|
|
[0x6f, 'NumpadDivide'],
|
|
...Array.from({length: 24}, (_, index): [number, string] => [0x70 + index, `F${index + 1}`]),
|
|
[0x90, 'NumLock'],
|
|
[0x91, 'ScrollLock'],
|
|
[0x92, 'NumpadEqual'],
|
|
[0xa0, 'ShiftLeft'],
|
|
[0xa1, 'ShiftRight'],
|
|
[0xa2, 'ControlLeft'],
|
|
[0xa3, 'ControlRight'],
|
|
[0xa4, 'AltLeft'],
|
|
[0xa5, 'AltRight'],
|
|
[0xa6, 'BrowserBack'],
|
|
[0xa7, 'BrowserForward'],
|
|
[0xa8, 'BrowserRefresh'],
|
|
[0xa9, 'BrowserStop'],
|
|
[0xaa, 'BrowserSearch'],
|
|
[0xab, 'BrowserFavorites'],
|
|
[0xac, 'BrowserHome'],
|
|
[0xad, 'AudioVolumeMute'],
|
|
[0xae, 'AudioVolumeDown'],
|
|
[0xaf, 'AudioVolumeUp'],
|
|
[0xb0, 'MediaTrackNext'],
|
|
[0xb1, 'MediaTrackPrevious'],
|
|
[0xb2, 'MediaStop'],
|
|
[0xb3, 'MediaPlayPause'],
|
|
[0xb4, 'LaunchMail'],
|
|
[0xb5, 'MediaSelect'],
|
|
[0xb6, 'LaunchApp1'],
|
|
[0xb7, 'LaunchApp2'],
|
|
[0xba, 'Semicolon'],
|
|
[0xbb, 'Equal'],
|
|
[0xbc, 'Comma'],
|
|
[0xbd, 'Minus'],
|
|
[0xbe, 'Period'],
|
|
[0xbf, 'Slash'],
|
|
[0xc0, 'Backquote'],
|
|
[0xdb, 'BracketLeft'],
|
|
[0xdc, 'Backslash'],
|
|
[0xdd, 'BracketRight'],
|
|
[0xde, 'Quote'],
|
|
[0xe2, 'IntlBackslash'],
|
|
]);
|
|
|
|
const MACOS_KEY_CODES: ReadonlyMap<number, string> = new Map([
|
|
[0x00, 'KeyA'],
|
|
[0x01, 'KeyS'],
|
|
[0x02, 'KeyD'],
|
|
[0x03, 'KeyF'],
|
|
[0x04, 'KeyH'],
|
|
[0x05, 'KeyG'],
|
|
[0x06, 'KeyZ'],
|
|
[0x07, 'KeyX'],
|
|
[0x08, 'KeyC'],
|
|
[0x09, 'KeyV'],
|
|
[0x0a, 'IntlBackslash'],
|
|
[0x0b, 'KeyB'],
|
|
[0x0c, 'KeyQ'],
|
|
[0x0d, 'KeyW'],
|
|
[0x0e, 'KeyE'],
|
|
[0x0f, 'KeyR'],
|
|
[0x10, 'KeyY'],
|
|
[0x11, 'KeyT'],
|
|
[0x12, 'Digit1'],
|
|
[0x13, 'Digit2'],
|
|
[0x14, 'Digit3'],
|
|
[0x15, 'Digit4'],
|
|
[0x16, 'Digit6'],
|
|
[0x17, 'Digit5'],
|
|
[0x18, 'Equal'],
|
|
[0x19, 'Digit9'],
|
|
[0x1a, 'Digit7'],
|
|
[0x1b, 'Minus'],
|
|
[0x1c, 'Digit8'],
|
|
[0x1d, 'Digit0'],
|
|
[0x1e, 'BracketRight'],
|
|
[0x1f, 'KeyO'],
|
|
[0x20, 'KeyU'],
|
|
[0x21, 'BracketLeft'],
|
|
[0x22, 'KeyI'],
|
|
[0x23, 'KeyP'],
|
|
[0x24, 'Enter'],
|
|
[0x25, 'KeyL'],
|
|
[0x26, 'KeyJ'],
|
|
[0x27, 'Quote'],
|
|
[0x28, 'KeyK'],
|
|
[0x29, 'Semicolon'],
|
|
[0x2a, 'Backslash'],
|
|
[0x2b, 'Comma'],
|
|
[0x2c, 'Slash'],
|
|
[0x2d, 'KeyN'],
|
|
[0x2e, 'KeyM'],
|
|
[0x2f, 'Period'],
|
|
[0x30, 'Tab'],
|
|
[0x31, 'Space'],
|
|
[0x32, 'Backquote'],
|
|
[0x33, 'Backspace'],
|
|
[0x35, 'Escape'],
|
|
[0x36, 'MetaRight'],
|
|
[0x37, 'MetaLeft'],
|
|
[0x38, 'ShiftLeft'],
|
|
[0x39, 'CapsLock'],
|
|
[0x3a, 'AltLeft'],
|
|
[0x3b, 'ControlLeft'],
|
|
[0x3c, 'ShiftRight'],
|
|
[0x3d, 'AltRight'],
|
|
[0x3e, 'ControlRight'],
|
|
[0x40, 'F17'],
|
|
[0x41, 'NumpadDecimal'],
|
|
[0x43, 'NumpadMultiply'],
|
|
[0x45, 'NumpadAdd'],
|
|
[0x47, 'NumLock'],
|
|
[0x48, 'AudioVolumeUp'],
|
|
[0x49, 'AudioVolumeDown'],
|
|
[0x4a, 'AudioVolumeMute'],
|
|
[0x4b, 'NumpadDivide'],
|
|
[0x4c, 'NumpadEnter'],
|
|
[0x4e, 'NumpadSubtract'],
|
|
[0x4f, 'F18'],
|
|
[0x50, 'F19'],
|
|
[0x51, 'NumpadEqual'],
|
|
[0x52, 'Numpad0'],
|
|
[0x53, 'Numpad1'],
|
|
[0x54, 'Numpad2'],
|
|
[0x55, 'Numpad3'],
|
|
[0x56, 'Numpad4'],
|
|
[0x57, 'Numpad5'],
|
|
[0x58, 'Numpad6'],
|
|
[0x59, 'Numpad7'],
|
|
[0x5a, 'F20'],
|
|
[0x5b, 'Numpad8'],
|
|
[0x5c, 'Numpad9'],
|
|
[0x5d, 'IntlYen'],
|
|
[0x5e, 'IntlRo'],
|
|
[0x5f, 'NumpadComma'],
|
|
[0x60, 'F5'],
|
|
[0x61, 'F6'],
|
|
[0x62, 'F7'],
|
|
[0x63, 'F3'],
|
|
[0x64, 'F8'],
|
|
[0x65, 'F9'],
|
|
[0x66, 'Lang2'],
|
|
[0x67, 'F11'],
|
|
[0x68, 'Lang1'],
|
|
[0x69, 'F13'],
|
|
[0x6a, 'F16'],
|
|
[0x6b, 'F14'],
|
|
[0x6d, 'F10'],
|
|
[0x6e, 'ContextMenu'],
|
|
[0x6f, 'F12'],
|
|
[0x71, 'F15'],
|
|
[0x72, 'Insert'],
|
|
[0x73, 'Home'],
|
|
[0x74, 'PageUp'],
|
|
[0x75, 'Delete'],
|
|
[0x76, 'F4'],
|
|
[0x77, 'End'],
|
|
[0x78, 'F2'],
|
|
[0x79, 'PageDown'],
|
|
[0x7a, 'F1'],
|
|
[0x7b, 'ArrowLeft'],
|
|
[0x7c, 'ArrowRight'],
|
|
[0x7d, 'ArrowDown'],
|
|
[0x7e, 'ArrowUp'],
|
|
]);
|
|
|
|
const X11_KEYCODE_OFFSET = 8;
|
|
const WINDOWS_EXTENDED_SCAN_PREFIX = 0xe000;
|
|
|
|
const NAMED_KEY_CODES: Readonly<Record<string, string>> = {
|
|
Spacebar: 'Space',
|
|
Esc: 'Escape',
|
|
Return: 'Enter',
|
|
Break: 'Pause',
|
|
Clear: 'NumLock',
|
|
Apps: 'ContextMenu',
|
|
Up: 'ArrowUp',
|
|
Down: 'ArrowDown',
|
|
Left: 'ArrowLeft',
|
|
Right: 'ArrowRight',
|
|
Del: 'Delete',
|
|
LaunchMediaPlayer: 'MediaSelect',
|
|
};
|
|
|
|
const KNOWN_DOM_CODES: ReadonlySet<string> = new Set([
|
|
...EVDEV_KEY_CODES.values(),
|
|
...WINDOWS_SCAN_CODES.values(),
|
|
...MACOS_KEY_CODES.values(),
|
|
]);
|
|
|
|
export const MODIFIER_KEY_CODES = {
|
|
ctrl: ['ControlLeft', 'ControlRight'],
|
|
alt: ['AltLeft', 'AltRight'],
|
|
shift: ['ShiftLeft', 'ShiftRight'],
|
|
meta: ['MetaLeft', 'MetaRight'],
|
|
} as const;
|
|
|
|
export type ModifierKind = keyof typeof MODIFIER_KEY_CODES;
|
|
|
|
export function modifierKindForCode(code: string | null): ModifierKind | null {
|
|
if (code === null) return null;
|
|
for (const kind of Object.keys(MODIFIER_KEY_CODES) as Array<ModifierKind>) {
|
|
if ((MODIFIER_KEY_CODES[kind] as ReadonlyArray<string>).includes(code)) return kind;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function evdevKeyToDomCode(keycode: number): string | null {
|
|
return EVDEV_KEY_CODES.get(keycode) ?? null;
|
|
}
|
|
|
|
export function x11KeycodeToDomCode(keycode: number): string | null {
|
|
if (keycode < X11_KEYCODE_OFFSET) return null;
|
|
return evdevKeyToDomCode(keycode - X11_KEYCODE_OFFSET);
|
|
}
|
|
|
|
export function windowsScanCodeKey(scanCode: number, extended: boolean): number {
|
|
return extended ? WINDOWS_EXTENDED_SCAN_PREFIX | scanCode : scanCode;
|
|
}
|
|
|
|
export function windowsKeyToDomCode(scanCode: number, extended: boolean, virtualKey: number): string | null {
|
|
const fromScanCode = scanCode === 0 ? undefined : WINDOWS_SCAN_CODES.get(windowsScanCodeKey(scanCode, extended));
|
|
return fromScanCode ?? WINDOWS_VIRTUAL_KEY_CODES.get(virtualKey) ?? null;
|
|
}
|
|
|
|
export function macosKeycodeToDomCode(keycode: number): string | null {
|
|
return MACOS_KEY_CODES.get(keycode) ?? null;
|
|
}
|
|
|
|
export function hookKeyNameToDomCode(name: string): string | null {
|
|
if (/^[A-Z]$/.test(name)) return `Key${name}`;
|
|
if (/^[0-9]$/.test(name)) return `Digit${name}`;
|
|
const named = NAMED_KEY_CODES[name];
|
|
if (named) return named;
|
|
return KNOWN_DOM_CODES.has(name) ? name : null;
|
|
}
|
|
|
|
export function usLayoutKeyToDomCode(key: string): string | null {
|
|
if (key.length === 1) {
|
|
const punctuation = PUNCTUATION_KEY_CODES[key];
|
|
if (punctuation) return punctuation;
|
|
const upper = key.toUpperCase();
|
|
if (/^[A-Z]$/.test(upper)) return `Key${upper}`;
|
|
if (/^[0-9]$/.test(upper)) return `Digit${upper}`;
|
|
return null;
|
|
}
|
|
return hookKeyNameToDomCode(key);
|
|
}
|
|
|
|
const XDG_CODE_KEYSYMS: ReadonlyMap<string, string> = new Map([
|
|
...Array.from({length: 26}, (_, index): [string, string] => [
|
|
`Key${String.fromCharCode(0x41 + index)}`,
|
|
String.fromCharCode(0x61 + index),
|
|
]),
|
|
...Array.from({length: 10}, (_, index): [string, string] => [`Digit${index}`, `${index}`]),
|
|
...Array.from({length: 24}, (_, index): [string, string] => [`F${index + 1}`, `F${index + 1}`]),
|
|
['Space', 'space'],
|
|
['Enter', 'Return'],
|
|
['Tab', 'Tab'],
|
|
['Escape', 'Escape'],
|
|
['Backspace', 'BackSpace'],
|
|
['Minus', 'minus'],
|
|
['Equal', 'equal'],
|
|
['BracketLeft', 'bracketleft'],
|
|
['BracketRight', 'bracketright'],
|
|
['Backslash', 'backslash'],
|
|
['Semicolon', 'semicolon'],
|
|
['Quote', 'apostrophe'],
|
|
['Backquote', 'grave'],
|
|
['Comma', 'comma'],
|
|
['Period', 'period'],
|
|
['Slash', 'slash'],
|
|
['Home', 'Home'],
|
|
['End', 'End'],
|
|
['PageUp', 'Prior'],
|
|
['PageDown', 'Next'],
|
|
['Insert', 'Insert'],
|
|
['Delete', 'Delete'],
|
|
['ArrowLeft', 'Left'],
|
|
['ArrowRight', 'Right'],
|
|
['ArrowUp', 'Up'],
|
|
['ArrowDown', 'Down'],
|
|
['PrintScreen', 'Print'],
|
|
['ScrollLock', 'Scroll_Lock'],
|
|
['Pause', 'Pause'],
|
|
['ContextMenu', 'Menu'],
|
|
['MediaPlayPause', 'XF86AudioPlay'],
|
|
['MediaTrackNext', 'XF86AudioNext'],
|
|
['MediaTrackPrevious', 'XF86AudioPrev'],
|
|
['MediaStop', 'XF86AudioStop'],
|
|
['AudioVolumeMute', 'XF86AudioMute'],
|
|
]);
|
|
|
|
const XDG_KEYSYM_PATTERN = /^[A-Za-z0-9_]+$/;
|
|
|
|
function xdgKeysymForCombo(combo: {key: string; code?: string}): string | null {
|
|
if (/^[a-z]$/i.test(combo.key) && !combo.code?.startsWith('Numpad')) return combo.key.toLowerCase();
|
|
const code = combo.code ?? usLayoutKeyToDomCode(combo.key);
|
|
if (code === null) return null;
|
|
const keysym = XDG_CODE_KEYSYMS.get(code) ?? null;
|
|
return keysym !== null && XDG_KEYSYM_PATTERN.test(keysym) ? keysym : null;
|
|
}
|
|
|
|
export interface XdgTriggerCombo {
|
|
key: string;
|
|
code?: string;
|
|
ctrl: boolean;
|
|
alt: boolean;
|
|
shift: boolean;
|
|
meta: boolean;
|
|
mouseButton?: number;
|
|
modifierOnly?: boolean;
|
|
bothSides?: boolean;
|
|
}
|
|
|
|
export function comboToXdgTrigger(combo: XdgTriggerCombo): string | null {
|
|
if (combo.mouseButton !== undefined || combo.modifierOnly || combo.bothSides) return null;
|
|
const keysym = xdgKeysymForCombo(combo);
|
|
if (keysym === null) return null;
|
|
const parts: Array<string> = [];
|
|
if (combo.ctrl) parts.push('CTRL');
|
|
if (combo.alt) parts.push('ALT');
|
|
if (combo.shift) parts.push('SHIFT');
|
|
if (combo.meta) parts.push('LOGO');
|
|
parts.push(keysym);
|
|
return parts.join('+');
|
|
}
|