mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-03-30 07:06:38 +08:00
refactor(hid): 统一 HID 键盘 CanonicalKey 语义并清理前端布局与输入链路冗余代码
This commit is contained in:
@@ -1,129 +1,130 @@
|
||||
// Character to HID usage mapping for text paste functionality.
|
||||
// The table follows US QWERTY layout semantics.
|
||||
|
||||
import { type CanonicalKey } from '@/types/generated'
|
||||
import { keys } from '@/lib/keyboardMappings'
|
||||
|
||||
export interface CharKeyMapping {
|
||||
hidCode: number // USB HID usage code
|
||||
key: CanonicalKey
|
||||
shift: boolean // Whether Shift modifier is needed
|
||||
}
|
||||
|
||||
const charToKeyMap: Record<string, CharKeyMapping> = {
|
||||
// Lowercase letters
|
||||
a: { hidCode: keys.KeyA, shift: false },
|
||||
b: { hidCode: keys.KeyB, shift: false },
|
||||
c: { hidCode: keys.KeyC, shift: false },
|
||||
d: { hidCode: keys.KeyD, shift: false },
|
||||
e: { hidCode: keys.KeyE, shift: false },
|
||||
f: { hidCode: keys.KeyF, shift: false },
|
||||
g: { hidCode: keys.KeyG, shift: false },
|
||||
h: { hidCode: keys.KeyH, shift: false },
|
||||
i: { hidCode: keys.KeyI, shift: false },
|
||||
j: { hidCode: keys.KeyJ, shift: false },
|
||||
k: { hidCode: keys.KeyK, shift: false },
|
||||
l: { hidCode: keys.KeyL, shift: false },
|
||||
m: { hidCode: keys.KeyM, shift: false },
|
||||
n: { hidCode: keys.KeyN, shift: false },
|
||||
o: { hidCode: keys.KeyO, shift: false },
|
||||
p: { hidCode: keys.KeyP, shift: false },
|
||||
q: { hidCode: keys.KeyQ, shift: false },
|
||||
r: { hidCode: keys.KeyR, shift: false },
|
||||
s: { hidCode: keys.KeyS, shift: false },
|
||||
t: { hidCode: keys.KeyT, shift: false },
|
||||
u: { hidCode: keys.KeyU, shift: false },
|
||||
v: { hidCode: keys.KeyV, shift: false },
|
||||
w: { hidCode: keys.KeyW, shift: false },
|
||||
x: { hidCode: keys.KeyX, shift: false },
|
||||
y: { hidCode: keys.KeyY, shift: false },
|
||||
z: { hidCode: keys.KeyZ, shift: false },
|
||||
a: { key: keys.KeyA, shift: false },
|
||||
b: { key: keys.KeyB, shift: false },
|
||||
c: { key: keys.KeyC, shift: false },
|
||||
d: { key: keys.KeyD, shift: false },
|
||||
e: { key: keys.KeyE, shift: false },
|
||||
f: { key: keys.KeyF, shift: false },
|
||||
g: { key: keys.KeyG, shift: false },
|
||||
h: { key: keys.KeyH, shift: false },
|
||||
i: { key: keys.KeyI, shift: false },
|
||||
j: { key: keys.KeyJ, shift: false },
|
||||
k: { key: keys.KeyK, shift: false },
|
||||
l: { key: keys.KeyL, shift: false },
|
||||
m: { key: keys.KeyM, shift: false },
|
||||
n: { key: keys.KeyN, shift: false },
|
||||
o: { key: keys.KeyO, shift: false },
|
||||
p: { key: keys.KeyP, shift: false },
|
||||
q: { key: keys.KeyQ, shift: false },
|
||||
r: { key: keys.KeyR, shift: false },
|
||||
s: { key: keys.KeyS, shift: false },
|
||||
t: { key: keys.KeyT, shift: false },
|
||||
u: { key: keys.KeyU, shift: false },
|
||||
v: { key: keys.KeyV, shift: false },
|
||||
w: { key: keys.KeyW, shift: false },
|
||||
x: { key: keys.KeyX, shift: false },
|
||||
y: { key: keys.KeyY, shift: false },
|
||||
z: { key: keys.KeyZ, shift: false },
|
||||
|
||||
// Uppercase letters
|
||||
A: { hidCode: keys.KeyA, shift: true },
|
||||
B: { hidCode: keys.KeyB, shift: true },
|
||||
C: { hidCode: keys.KeyC, shift: true },
|
||||
D: { hidCode: keys.KeyD, shift: true },
|
||||
E: { hidCode: keys.KeyE, shift: true },
|
||||
F: { hidCode: keys.KeyF, shift: true },
|
||||
G: { hidCode: keys.KeyG, shift: true },
|
||||
H: { hidCode: keys.KeyH, shift: true },
|
||||
I: { hidCode: keys.KeyI, shift: true },
|
||||
J: { hidCode: keys.KeyJ, shift: true },
|
||||
K: { hidCode: keys.KeyK, shift: true },
|
||||
L: { hidCode: keys.KeyL, shift: true },
|
||||
M: { hidCode: keys.KeyM, shift: true },
|
||||
N: { hidCode: keys.KeyN, shift: true },
|
||||
O: { hidCode: keys.KeyO, shift: true },
|
||||
P: { hidCode: keys.KeyP, shift: true },
|
||||
Q: { hidCode: keys.KeyQ, shift: true },
|
||||
R: { hidCode: keys.KeyR, shift: true },
|
||||
S: { hidCode: keys.KeyS, shift: true },
|
||||
T: { hidCode: keys.KeyT, shift: true },
|
||||
U: { hidCode: keys.KeyU, shift: true },
|
||||
V: { hidCode: keys.KeyV, shift: true },
|
||||
W: { hidCode: keys.KeyW, shift: true },
|
||||
X: { hidCode: keys.KeyX, shift: true },
|
||||
Y: { hidCode: keys.KeyY, shift: true },
|
||||
Z: { hidCode: keys.KeyZ, shift: true },
|
||||
A: { key: keys.KeyA, shift: true },
|
||||
B: { key: keys.KeyB, shift: true },
|
||||
C: { key: keys.KeyC, shift: true },
|
||||
D: { key: keys.KeyD, shift: true },
|
||||
E: { key: keys.KeyE, shift: true },
|
||||
F: { key: keys.KeyF, shift: true },
|
||||
G: { key: keys.KeyG, shift: true },
|
||||
H: { key: keys.KeyH, shift: true },
|
||||
I: { key: keys.KeyI, shift: true },
|
||||
J: { key: keys.KeyJ, shift: true },
|
||||
K: { key: keys.KeyK, shift: true },
|
||||
L: { key: keys.KeyL, shift: true },
|
||||
M: { key: keys.KeyM, shift: true },
|
||||
N: { key: keys.KeyN, shift: true },
|
||||
O: { key: keys.KeyO, shift: true },
|
||||
P: { key: keys.KeyP, shift: true },
|
||||
Q: { key: keys.KeyQ, shift: true },
|
||||
R: { key: keys.KeyR, shift: true },
|
||||
S: { key: keys.KeyS, shift: true },
|
||||
T: { key: keys.KeyT, shift: true },
|
||||
U: { key: keys.KeyU, shift: true },
|
||||
V: { key: keys.KeyV, shift: true },
|
||||
W: { key: keys.KeyW, shift: true },
|
||||
X: { key: keys.KeyX, shift: true },
|
||||
Y: { key: keys.KeyY, shift: true },
|
||||
Z: { key: keys.KeyZ, shift: true },
|
||||
|
||||
// Number row
|
||||
'0': { hidCode: keys.Digit0, shift: false },
|
||||
'1': { hidCode: keys.Digit1, shift: false },
|
||||
'2': { hidCode: keys.Digit2, shift: false },
|
||||
'3': { hidCode: keys.Digit3, shift: false },
|
||||
'4': { hidCode: keys.Digit4, shift: false },
|
||||
'5': { hidCode: keys.Digit5, shift: false },
|
||||
'6': { hidCode: keys.Digit6, shift: false },
|
||||
'7': { hidCode: keys.Digit7, shift: false },
|
||||
'8': { hidCode: keys.Digit8, shift: false },
|
||||
'9': { hidCode: keys.Digit9, shift: false },
|
||||
'0': { key: keys.Digit0, shift: false },
|
||||
'1': { key: keys.Digit1, shift: false },
|
||||
'2': { key: keys.Digit2, shift: false },
|
||||
'3': { key: keys.Digit3, shift: false },
|
||||
'4': { key: keys.Digit4, shift: false },
|
||||
'5': { key: keys.Digit5, shift: false },
|
||||
'6': { key: keys.Digit6, shift: false },
|
||||
'7': { key: keys.Digit7, shift: false },
|
||||
'8': { key: keys.Digit8, shift: false },
|
||||
'9': { key: keys.Digit9, shift: false },
|
||||
|
||||
// Shifted number row symbols
|
||||
')': { hidCode: keys.Digit0, shift: true },
|
||||
'!': { hidCode: keys.Digit1, shift: true },
|
||||
'@': { hidCode: keys.Digit2, shift: true },
|
||||
'#': { hidCode: keys.Digit3, shift: true },
|
||||
'$': { hidCode: keys.Digit4, shift: true },
|
||||
'%': { hidCode: keys.Digit5, shift: true },
|
||||
'^': { hidCode: keys.Digit6, shift: true },
|
||||
'&': { hidCode: keys.Digit7, shift: true },
|
||||
'*': { hidCode: keys.Digit8, shift: true },
|
||||
'(': { hidCode: keys.Digit9, shift: true },
|
||||
')': { key: keys.Digit0, shift: true },
|
||||
'!': { key: keys.Digit1, shift: true },
|
||||
'@': { key: keys.Digit2, shift: true },
|
||||
'#': { key: keys.Digit3, shift: true },
|
||||
'$': { key: keys.Digit4, shift: true },
|
||||
'%': { key: keys.Digit5, shift: true },
|
||||
'^': { key: keys.Digit6, shift: true },
|
||||
'&': { key: keys.Digit7, shift: true },
|
||||
'*': { key: keys.Digit8, shift: true },
|
||||
'(': { key: keys.Digit9, shift: true },
|
||||
|
||||
// Punctuation and symbols
|
||||
'-': { hidCode: keys.Minus, shift: false },
|
||||
'=': { hidCode: keys.Equal, shift: false },
|
||||
'[': { hidCode: keys.BracketLeft, shift: false },
|
||||
']': { hidCode: keys.BracketRight, shift: false },
|
||||
'\\': { hidCode: keys.Backslash, shift: false },
|
||||
';': { hidCode: keys.Semicolon, shift: false },
|
||||
"'": { hidCode: keys.Quote, shift: false },
|
||||
'`': { hidCode: keys.Backquote, shift: false },
|
||||
',': { hidCode: keys.Comma, shift: false },
|
||||
'.': { hidCode: keys.Period, shift: false },
|
||||
'/': { hidCode: keys.Slash, shift: false },
|
||||
'-': { key: keys.Minus, shift: false },
|
||||
'=': { key: keys.Equal, shift: false },
|
||||
'[': { key: keys.BracketLeft, shift: false },
|
||||
']': { key: keys.BracketRight, shift: false },
|
||||
'\\': { key: keys.Backslash, shift: false },
|
||||
';': { key: keys.Semicolon, shift: false },
|
||||
"'": { key: keys.Quote, shift: false },
|
||||
'`': { key: keys.Backquote, shift: false },
|
||||
',': { key: keys.Comma, shift: false },
|
||||
'.': { key: keys.Period, shift: false },
|
||||
'/': { key: keys.Slash, shift: false },
|
||||
|
||||
// Shifted punctuation and symbols
|
||||
_: { hidCode: keys.Minus, shift: true },
|
||||
'+': { hidCode: keys.Equal, shift: true },
|
||||
'{': { hidCode: keys.BracketLeft, shift: true },
|
||||
'}': { hidCode: keys.BracketRight, shift: true },
|
||||
'|': { hidCode: keys.Backslash, shift: true },
|
||||
':': { hidCode: keys.Semicolon, shift: true },
|
||||
'"': { hidCode: keys.Quote, shift: true },
|
||||
'~': { hidCode: keys.Backquote, shift: true },
|
||||
'<': { hidCode: keys.Comma, shift: true },
|
||||
'>': { hidCode: keys.Period, shift: true },
|
||||
'?': { hidCode: keys.Slash, shift: true },
|
||||
_: { key: keys.Minus, shift: true },
|
||||
'+': { key: keys.Equal, shift: true },
|
||||
'{': { key: keys.BracketLeft, shift: true },
|
||||
'}': { key: keys.BracketRight, shift: true },
|
||||
'|': { key: keys.Backslash, shift: true },
|
||||
':': { key: keys.Semicolon, shift: true },
|
||||
'"': { key: keys.Quote, shift: true },
|
||||
'~': { key: keys.Backquote, shift: true },
|
||||
'<': { key: keys.Comma, shift: true },
|
||||
'>': { key: keys.Period, shift: true },
|
||||
'?': { key: keys.Slash, shift: true },
|
||||
|
||||
// Whitespace and control
|
||||
' ': { hidCode: keys.Space, shift: false },
|
||||
'\t': { hidCode: keys.Tab, shift: false },
|
||||
'\n': { hidCode: keys.Enter, shift: false },
|
||||
'\r': { hidCode: keys.Enter, shift: false },
|
||||
' ': { key: keys.Space, shift: false },
|
||||
'\t': { key: keys.Tab, shift: false },
|
||||
'\n': { key: keys.Enter, shift: false },
|
||||
'\r': { key: keys.Enter, shift: false },
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HID usage code and modifier state for a character
|
||||
* Get canonical key and modifier state for a character
|
||||
* @param char - Single character to convert
|
||||
* @returns CharKeyMapping or null if character is not mappable
|
||||
*/
|
||||
|
||||
@@ -1,77 +1,15 @@
|
||||
// Keyboard layout definitions for virtual keyboard
|
||||
// Virtual keyboard layout data shared by the on-screen keyboard.
|
||||
|
||||
export interface KeyboardLayout {
|
||||
id: string
|
||||
name: string
|
||||
// Key display labels
|
||||
keyLabels: Record<string, string>
|
||||
// Shift variant labels (key in parentheses)
|
||||
shiftLabels: Record<string, string>
|
||||
// Virtual keyboard layout rows
|
||||
layout: {
|
||||
main: {
|
||||
macros: string[]
|
||||
functionRow: string[]
|
||||
default: string[][]
|
||||
shift: string[][]
|
||||
}
|
||||
control: string[][]
|
||||
arrows: string[][]
|
||||
media: string[] // Media keys row
|
||||
}
|
||||
}
|
||||
|
||||
// OS-specific keyboard layout type
|
||||
export type KeyboardOsType = 'windows' | 'mac' | 'android'
|
||||
|
||||
// Bottom row layouts for different OS
|
||||
export const osBottomRows: Record<KeyboardOsType, string[]> = {
|
||||
// Windows: Ctrl - Win - Alt - Space - Alt - Win - Menu - Ctrl
|
||||
windows: ['ControlLeft', 'MetaLeft', 'AltLeft', 'Space', 'AltRight', 'MetaRight', 'Menu', 'ControlRight'],
|
||||
// Mac: Ctrl - Option - Cmd - Space - Cmd - Option - Ctrl
|
||||
windows: ['ControlLeft', 'MetaLeft', 'AltLeft', 'Space', 'AltRight', 'MetaRight', 'ContextMenu', 'ControlRight'],
|
||||
mac: ['ControlLeft', 'AltLeft', 'MetaLeft', 'Space', 'MetaRight', 'AltRight', 'ControlRight'],
|
||||
// Android: simplified layout
|
||||
android: ['ControlLeft', 'AltLeft', 'Space', 'AltRight', 'ControlRight'],
|
||||
}
|
||||
|
||||
// OS-specific modifier display names
|
||||
export const osModifierLabels: Record<KeyboardOsType, Record<string, string>> = {
|
||||
windows: {
|
||||
ControlLeft: '^Ctrl',
|
||||
ControlRight: 'Ctrl^',
|
||||
MetaLeft: '⊞Win',
|
||||
MetaRight: 'Win⊞',
|
||||
AltLeft: 'Alt',
|
||||
AltRight: 'Alt',
|
||||
AltGr: 'AltGr',
|
||||
Menu: 'Menu',
|
||||
},
|
||||
mac: {
|
||||
ControlLeft: '^Ctrl',
|
||||
ControlRight: 'Ctrl^',
|
||||
MetaLeft: '⌘Cmd',
|
||||
MetaRight: 'Cmd⌘',
|
||||
AltLeft: '⌥Opt',
|
||||
AltRight: 'Opt⌥',
|
||||
AltGr: '⌥Opt',
|
||||
Menu: 'Menu',
|
||||
},
|
||||
android: {
|
||||
ControlLeft: 'Ctrl',
|
||||
ControlRight: 'Ctrl',
|
||||
MetaLeft: 'Meta',
|
||||
MetaRight: 'Meta',
|
||||
AltLeft: 'Alt',
|
||||
AltRight: 'Alt',
|
||||
AltGr: 'Alt',
|
||||
Menu: 'Menu',
|
||||
},
|
||||
}
|
||||
|
||||
// Media keys (Consumer Control)
|
||||
export const mediaKeys = ['PrevTrack', 'PlayPause', 'NextTrack', 'Stop', 'Mute', 'VolumeDown', 'VolumeUp']
|
||||
|
||||
// Media key display names
|
||||
export const mediaKeyLabels: Record<string, string> = {
|
||||
PlayPause: '⏯',
|
||||
Stop: '⏹',
|
||||
@@ -81,158 +19,3 @@ export const mediaKeyLabels: Record<string, string> = {
|
||||
VolumeUp: '🔊',
|
||||
VolumeDown: '🔉',
|
||||
}
|
||||
|
||||
// English US Layout
|
||||
export const enUSLayout: KeyboardLayout = {
|
||||
id: 'en-US',
|
||||
name: 'English (US)',
|
||||
keyLabels: {
|
||||
// Macros
|
||||
CtrlAltDelete: 'Ctrl+Alt+Del',
|
||||
AltMetaEscape: 'Alt+Meta+Esc',
|
||||
CtrlAltBackspace: 'Ctrl+Alt+Back',
|
||||
|
||||
// Modifiers
|
||||
ControlLeft: 'Ctrl',
|
||||
ControlRight: 'Ctrl',
|
||||
ShiftLeft: 'Shift',
|
||||
ShiftRight: 'Shift',
|
||||
AltLeft: 'Alt',
|
||||
AltRight: 'Alt',
|
||||
AltGr: 'AltGr',
|
||||
MetaLeft: 'Meta',
|
||||
MetaRight: 'Meta',
|
||||
|
||||
// Special keys
|
||||
Escape: 'Esc',
|
||||
Backspace: 'Back',
|
||||
Tab: 'Tab',
|
||||
CapsLock: 'Caps',
|
||||
Enter: 'Enter',
|
||||
Space: ' ',
|
||||
Menu: 'Menu',
|
||||
|
||||
// Navigation
|
||||
Insert: 'Ins',
|
||||
Delete: 'Del',
|
||||
Home: 'Home',
|
||||
End: 'End',
|
||||
PageUp: 'PgUp',
|
||||
PageDown: 'PgDn',
|
||||
|
||||
// Arrows
|
||||
ArrowUp: '\u2191',
|
||||
ArrowDown: '\u2193',
|
||||
ArrowLeft: '\u2190',
|
||||
ArrowRight: '\u2192',
|
||||
|
||||
// Control cluster
|
||||
PrintScreen: 'PrtSc',
|
||||
ScrollLock: 'ScrLk',
|
||||
Pause: 'Pause',
|
||||
|
||||
// Function keys
|
||||
F1: 'F1', F2: 'F2', F3: 'F3', F4: 'F4',
|
||||
F5: 'F5', F6: 'F6', F7: 'F7', F8: 'F8',
|
||||
F9: 'F9', F10: 'F10', F11: 'F11', F12: 'F12',
|
||||
|
||||
// Letters
|
||||
KeyA: 'a', KeyB: 'b', KeyC: 'c', KeyD: 'd', KeyE: 'e',
|
||||
KeyF: 'f', KeyG: 'g', KeyH: 'h', KeyI: 'i', KeyJ: 'j',
|
||||
KeyK: 'k', KeyL: 'l', KeyM: 'm', KeyN: 'n', KeyO: 'o',
|
||||
KeyP: 'p', KeyQ: 'q', KeyR: 'r', KeyS: 's', KeyT: 't',
|
||||
KeyU: 'u', KeyV: 'v', KeyW: 'w', KeyX: 'x', KeyY: 'y',
|
||||
KeyZ: 'z',
|
||||
|
||||
// Numbers
|
||||
Digit1: '1', Digit2: '2', Digit3: '3', Digit4: '4', Digit5: '5',
|
||||
Digit6: '6', Digit7: '7', Digit8: '8', Digit9: '9', Digit0: '0',
|
||||
|
||||
// Symbols
|
||||
Minus: '-',
|
||||
Equal: '=',
|
||||
BracketLeft: '[',
|
||||
BracketRight: ']',
|
||||
Backslash: '\\',
|
||||
Semicolon: ';',
|
||||
Quote: "'",
|
||||
Backquote: '`',
|
||||
Comma: ',',
|
||||
Period: '.',
|
||||
Slash: '/',
|
||||
},
|
||||
shiftLabels: {
|
||||
// Capital letters
|
||||
KeyA: 'A', KeyB: 'B', KeyC: 'C', KeyD: 'D', KeyE: 'E',
|
||||
KeyF: 'F', KeyG: 'G', KeyH: 'H', KeyI: 'I', KeyJ: 'J',
|
||||
KeyK: 'K', KeyL: 'L', KeyM: 'M', KeyN: 'N', KeyO: 'O',
|
||||
KeyP: 'P', KeyQ: 'Q', KeyR: 'R', KeyS: 'S', KeyT: 'T',
|
||||
KeyU: 'U', KeyV: 'V', KeyW: 'W', KeyX: 'X', KeyY: 'Y',
|
||||
KeyZ: 'Z',
|
||||
|
||||
// Shifted numbers
|
||||
Digit1: '!', Digit2: '@', Digit3: '#', Digit4: '$', Digit5: '%',
|
||||
Digit6: '^', Digit7: '&', Digit8: '*', Digit9: '(', Digit0: ')',
|
||||
|
||||
// Shifted symbols
|
||||
Minus: '_',
|
||||
Equal: '+',
|
||||
BracketLeft: '{',
|
||||
BracketRight: '}',
|
||||
Backslash: '|',
|
||||
Semicolon: ':',
|
||||
Quote: '"',
|
||||
Backquote: '~',
|
||||
Comma: '<',
|
||||
Period: '>',
|
||||
Slash: '?',
|
||||
},
|
||||
layout: {
|
||||
main: {
|
||||
macros: ['CtrlAltDelete', 'AltMetaEscape', 'CtrlAltBackspace'],
|
||||
functionRow: ['Escape', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12'],
|
||||
default: [
|
||||
['Backquote', 'Digit1', 'Digit2', 'Digit3', 'Digit4', 'Digit5', 'Digit6', 'Digit7', 'Digit8', 'Digit9', 'Digit0', 'Minus', 'Equal', 'Backspace'],
|
||||
['Tab', 'KeyQ', 'KeyW', 'KeyE', 'KeyR', 'KeyT', 'KeyY', 'KeyU', 'KeyI', 'KeyO', 'KeyP', 'BracketLeft', 'BracketRight', 'Backslash'],
|
||||
['CapsLock', 'KeyA', 'KeyS', 'KeyD', 'KeyF', 'KeyG', 'KeyH', 'KeyJ', 'KeyK', 'KeyL', 'Semicolon', 'Quote', 'Enter'],
|
||||
['ShiftLeft', 'KeyZ', 'KeyX', 'KeyC', 'KeyV', 'KeyB', 'KeyN', 'KeyM', 'Comma', 'Period', 'Slash', 'ShiftRight'],
|
||||
['ControlLeft', 'MetaLeft', 'AltLeft', 'Space', 'AltRight', 'MetaRight', 'Menu', 'ControlRight'],
|
||||
],
|
||||
shift: [
|
||||
['Backquote', 'Digit1', 'Digit2', 'Digit3', 'Digit4', 'Digit5', 'Digit6', 'Digit7', 'Digit8', 'Digit9', 'Digit0', 'Minus', 'Equal', 'Backspace'],
|
||||
['Tab', 'KeyQ', 'KeyW', 'KeyE', 'KeyR', 'KeyT', 'KeyY', 'KeyU', 'KeyI', 'KeyO', 'KeyP', 'BracketLeft', 'BracketRight', 'Backslash'],
|
||||
['CapsLock', 'KeyA', 'KeyS', 'KeyD', 'KeyF', 'KeyG', 'KeyH', 'KeyJ', 'KeyK', 'KeyL', 'Semicolon', 'Quote', 'Enter'],
|
||||
['ShiftLeft', 'KeyZ', 'KeyX', 'KeyC', 'KeyV', 'KeyB', 'KeyN', 'KeyM', 'Comma', 'Period', 'Slash', 'ShiftRight'],
|
||||
['ControlLeft', 'MetaLeft', 'AltLeft', 'Space', 'AltRight', 'MetaRight', 'Menu', 'ControlRight'],
|
||||
],
|
||||
},
|
||||
control: [
|
||||
['PrintScreen', 'ScrollLock', 'Pause'],
|
||||
['Insert', 'Home', 'PageUp'],
|
||||
['Delete', 'End', 'PageDown'],
|
||||
],
|
||||
arrows: [
|
||||
['ArrowUp'],
|
||||
['ArrowLeft', 'ArrowDown', 'ArrowRight'],
|
||||
],
|
||||
media: ['PrevTrack', 'PlayPause', 'NextTrack', 'Stop', 'Mute', 'VolumeDown', 'VolumeUp'],
|
||||
},
|
||||
}
|
||||
|
||||
// All available layouts
|
||||
export const keyboardLayouts: Record<string, KeyboardLayout> = {
|
||||
'en-US': enUSLayout,
|
||||
}
|
||||
|
||||
// Get layout by ID or return default
|
||||
export function getKeyboardLayout(id: string): KeyboardLayout {
|
||||
return keyboardLayouts[id] || enUSLayout
|
||||
}
|
||||
|
||||
// Get key label for display
|
||||
export function getKeyLabel(layout: KeyboardLayout, keyName: string, isShift: boolean): string {
|
||||
if (isShift && layout.shiftLabels[keyName]) {
|
||||
return layout.shiftLabels[keyName]
|
||||
}
|
||||
return layout.keyLabels[keyName] || keyName
|
||||
}
|
||||
|
||||
@@ -1,157 +1,128 @@
|
||||
// Key codes and modifiers correspond to definitions in the
|
||||
// [Linux USB HID gadget driver](https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt)
|
||||
// [Universal Serial Bus HID Usage Tables: Section 10](https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf)
|
||||
import { CanonicalKey } from '@/types/generated'
|
||||
|
||||
export const keys = {
|
||||
// Letters
|
||||
KeyA: 0x04,
|
||||
KeyB: 0x05,
|
||||
KeyC: 0x06,
|
||||
KeyD: 0x07,
|
||||
KeyE: 0x08,
|
||||
KeyF: 0x09,
|
||||
KeyG: 0x0a,
|
||||
KeyH: 0x0b,
|
||||
KeyI: 0x0c,
|
||||
KeyJ: 0x0d,
|
||||
KeyK: 0x0e,
|
||||
KeyL: 0x0f,
|
||||
KeyM: 0x10,
|
||||
KeyN: 0x11,
|
||||
KeyO: 0x12,
|
||||
KeyP: 0x13,
|
||||
KeyQ: 0x14,
|
||||
KeyR: 0x15,
|
||||
KeyS: 0x16,
|
||||
KeyT: 0x17,
|
||||
KeyU: 0x18,
|
||||
KeyV: 0x19,
|
||||
KeyW: 0x1a,
|
||||
KeyX: 0x1b,
|
||||
KeyY: 0x1c,
|
||||
KeyZ: 0x1d,
|
||||
|
||||
// Numbers
|
||||
Digit1: 0x1e,
|
||||
Digit2: 0x1f,
|
||||
Digit3: 0x20,
|
||||
Digit4: 0x21,
|
||||
Digit5: 0x22,
|
||||
Digit6: 0x23,
|
||||
Digit7: 0x24,
|
||||
Digit8: 0x25,
|
||||
Digit9: 0x26,
|
||||
Digit0: 0x27,
|
||||
|
||||
// Control keys
|
||||
Enter: 0x28,
|
||||
Escape: 0x29,
|
||||
Backspace: 0x2a,
|
||||
Tab: 0x2b,
|
||||
Space: 0x2c,
|
||||
|
||||
// Symbols
|
||||
Minus: 0x2d,
|
||||
Equal: 0x2e,
|
||||
BracketLeft: 0x2f,
|
||||
BracketRight: 0x30,
|
||||
Backslash: 0x31,
|
||||
Semicolon: 0x33,
|
||||
Quote: 0x34,
|
||||
Backquote: 0x35,
|
||||
Comma: 0x36,
|
||||
Period: 0x37,
|
||||
Slash: 0x38,
|
||||
|
||||
// Lock keys
|
||||
CapsLock: 0x39,
|
||||
|
||||
// Function keys
|
||||
F1: 0x3a,
|
||||
F2: 0x3b,
|
||||
F3: 0x3c,
|
||||
F4: 0x3d,
|
||||
F5: 0x3e,
|
||||
F6: 0x3f,
|
||||
F7: 0x40,
|
||||
F8: 0x41,
|
||||
F9: 0x42,
|
||||
F10: 0x43,
|
||||
F11: 0x44,
|
||||
F12: 0x45,
|
||||
|
||||
// Control cluster
|
||||
PrintScreen: 0x46,
|
||||
ScrollLock: 0x47,
|
||||
Pause: 0x48,
|
||||
Insert: 0x49,
|
||||
Home: 0x4a,
|
||||
PageUp: 0x4b,
|
||||
Delete: 0x4c,
|
||||
End: 0x4d,
|
||||
PageDown: 0x4e,
|
||||
|
||||
// Arrow keys
|
||||
ArrowRight: 0x4f,
|
||||
ArrowLeft: 0x50,
|
||||
ArrowDown: 0x51,
|
||||
ArrowUp: 0x52,
|
||||
|
||||
// Numpad
|
||||
NumLock: 0x53,
|
||||
NumpadDivide: 0x54,
|
||||
NumpadMultiply: 0x55,
|
||||
NumpadSubtract: 0x56,
|
||||
NumpadAdd: 0x57,
|
||||
NumpadEnter: 0x58,
|
||||
Numpad1: 0x59,
|
||||
Numpad2: 0x5a,
|
||||
Numpad3: 0x5b,
|
||||
Numpad4: 0x5c,
|
||||
Numpad5: 0x5d,
|
||||
Numpad6: 0x5e,
|
||||
Numpad7: 0x5f,
|
||||
Numpad8: 0x60,
|
||||
Numpad9: 0x61,
|
||||
Numpad0: 0x62,
|
||||
NumpadDecimal: 0x63,
|
||||
|
||||
// Non-US keys
|
||||
IntlBackslash: 0x64,
|
||||
ContextMenu: 0x65,
|
||||
Menu: 0x65,
|
||||
Application: 0x65,
|
||||
|
||||
// Extended function keys
|
||||
F13: 0x68,
|
||||
F14: 0x69,
|
||||
F15: 0x6a,
|
||||
F16: 0x6b,
|
||||
F17: 0x6c,
|
||||
F18: 0x6d,
|
||||
F19: 0x6e,
|
||||
F20: 0x6f,
|
||||
F21: 0x70,
|
||||
F22: 0x71,
|
||||
F23: 0x72,
|
||||
F24: 0x73,
|
||||
|
||||
// Modifiers (these are special - HID codes 0xE0-0xE7)
|
||||
ControlLeft: 0xe0,
|
||||
ShiftLeft: 0xe1,
|
||||
AltLeft: 0xe2,
|
||||
MetaLeft: 0xe3,
|
||||
ControlRight: 0xe4,
|
||||
ShiftRight: 0xe5,
|
||||
AltRight: 0xe6,
|
||||
AltGr: 0xe6,
|
||||
MetaRight: 0xe7,
|
||||
KeyA: CanonicalKey.KeyA,
|
||||
KeyB: CanonicalKey.KeyB,
|
||||
KeyC: CanonicalKey.KeyC,
|
||||
KeyD: CanonicalKey.KeyD,
|
||||
KeyE: CanonicalKey.KeyE,
|
||||
KeyF: CanonicalKey.KeyF,
|
||||
KeyG: CanonicalKey.KeyG,
|
||||
KeyH: CanonicalKey.KeyH,
|
||||
KeyI: CanonicalKey.KeyI,
|
||||
KeyJ: CanonicalKey.KeyJ,
|
||||
KeyK: CanonicalKey.KeyK,
|
||||
KeyL: CanonicalKey.KeyL,
|
||||
KeyM: CanonicalKey.KeyM,
|
||||
KeyN: CanonicalKey.KeyN,
|
||||
KeyO: CanonicalKey.KeyO,
|
||||
KeyP: CanonicalKey.KeyP,
|
||||
KeyQ: CanonicalKey.KeyQ,
|
||||
KeyR: CanonicalKey.KeyR,
|
||||
KeyS: CanonicalKey.KeyS,
|
||||
KeyT: CanonicalKey.KeyT,
|
||||
KeyU: CanonicalKey.KeyU,
|
||||
KeyV: CanonicalKey.KeyV,
|
||||
KeyW: CanonicalKey.KeyW,
|
||||
KeyX: CanonicalKey.KeyX,
|
||||
KeyY: CanonicalKey.KeyY,
|
||||
KeyZ: CanonicalKey.KeyZ,
|
||||
Digit1: CanonicalKey.Digit1,
|
||||
Digit2: CanonicalKey.Digit2,
|
||||
Digit3: CanonicalKey.Digit3,
|
||||
Digit4: CanonicalKey.Digit4,
|
||||
Digit5: CanonicalKey.Digit5,
|
||||
Digit6: CanonicalKey.Digit6,
|
||||
Digit7: CanonicalKey.Digit7,
|
||||
Digit8: CanonicalKey.Digit8,
|
||||
Digit9: CanonicalKey.Digit9,
|
||||
Digit0: CanonicalKey.Digit0,
|
||||
Enter: CanonicalKey.Enter,
|
||||
Escape: CanonicalKey.Escape,
|
||||
Backspace: CanonicalKey.Backspace,
|
||||
Tab: CanonicalKey.Tab,
|
||||
Space: CanonicalKey.Space,
|
||||
Minus: CanonicalKey.Minus,
|
||||
Equal: CanonicalKey.Equal,
|
||||
BracketLeft: CanonicalKey.BracketLeft,
|
||||
BracketRight: CanonicalKey.BracketRight,
|
||||
Backslash: CanonicalKey.Backslash,
|
||||
Semicolon: CanonicalKey.Semicolon,
|
||||
Quote: CanonicalKey.Quote,
|
||||
Backquote: CanonicalKey.Backquote,
|
||||
Comma: CanonicalKey.Comma,
|
||||
Period: CanonicalKey.Period,
|
||||
Slash: CanonicalKey.Slash,
|
||||
CapsLock: CanonicalKey.CapsLock,
|
||||
F1: CanonicalKey.F1,
|
||||
F2: CanonicalKey.F2,
|
||||
F3: CanonicalKey.F3,
|
||||
F4: CanonicalKey.F4,
|
||||
F5: CanonicalKey.F5,
|
||||
F6: CanonicalKey.F6,
|
||||
F7: CanonicalKey.F7,
|
||||
F8: CanonicalKey.F8,
|
||||
F9: CanonicalKey.F9,
|
||||
F10: CanonicalKey.F10,
|
||||
F11: CanonicalKey.F11,
|
||||
F12: CanonicalKey.F12,
|
||||
PrintScreen: CanonicalKey.PrintScreen,
|
||||
ScrollLock: CanonicalKey.ScrollLock,
|
||||
Pause: CanonicalKey.Pause,
|
||||
Insert: CanonicalKey.Insert,
|
||||
Home: CanonicalKey.Home,
|
||||
PageUp: CanonicalKey.PageUp,
|
||||
Delete: CanonicalKey.Delete,
|
||||
End: CanonicalKey.End,
|
||||
PageDown: CanonicalKey.PageDown,
|
||||
ArrowRight: CanonicalKey.ArrowRight,
|
||||
ArrowLeft: CanonicalKey.ArrowLeft,
|
||||
ArrowDown: CanonicalKey.ArrowDown,
|
||||
ArrowUp: CanonicalKey.ArrowUp,
|
||||
NumLock: CanonicalKey.NumLock,
|
||||
NumpadDivide: CanonicalKey.NumpadDivide,
|
||||
NumpadMultiply: CanonicalKey.NumpadMultiply,
|
||||
NumpadSubtract: CanonicalKey.NumpadSubtract,
|
||||
NumpadAdd: CanonicalKey.NumpadAdd,
|
||||
NumpadEnter: CanonicalKey.NumpadEnter,
|
||||
Numpad1: CanonicalKey.Numpad1,
|
||||
Numpad2: CanonicalKey.Numpad2,
|
||||
Numpad3: CanonicalKey.Numpad3,
|
||||
Numpad4: CanonicalKey.Numpad4,
|
||||
Numpad5: CanonicalKey.Numpad5,
|
||||
Numpad6: CanonicalKey.Numpad6,
|
||||
Numpad7: CanonicalKey.Numpad7,
|
||||
Numpad8: CanonicalKey.Numpad8,
|
||||
Numpad9: CanonicalKey.Numpad9,
|
||||
Numpad0: CanonicalKey.Numpad0,
|
||||
NumpadDecimal: CanonicalKey.NumpadDecimal,
|
||||
IntlBackslash: CanonicalKey.IntlBackslash,
|
||||
ContextMenu: CanonicalKey.ContextMenu,
|
||||
F13: CanonicalKey.F13,
|
||||
F14: CanonicalKey.F14,
|
||||
F15: CanonicalKey.F15,
|
||||
F16: CanonicalKey.F16,
|
||||
F17: CanonicalKey.F17,
|
||||
F18: CanonicalKey.F18,
|
||||
F19: CanonicalKey.F19,
|
||||
F20: CanonicalKey.F20,
|
||||
F21: CanonicalKey.F21,
|
||||
F22: CanonicalKey.F22,
|
||||
F23: CanonicalKey.F23,
|
||||
F24: CanonicalKey.F24,
|
||||
ControlLeft: CanonicalKey.ControlLeft,
|
||||
ShiftLeft: CanonicalKey.ShiftLeft,
|
||||
AltLeft: CanonicalKey.AltLeft,
|
||||
MetaLeft: CanonicalKey.MetaLeft,
|
||||
ControlRight: CanonicalKey.ControlRight,
|
||||
ShiftRight: CanonicalKey.ShiftRight,
|
||||
AltRight: CanonicalKey.AltRight,
|
||||
MetaRight: CanonicalKey.MetaRight,
|
||||
} as const
|
||||
|
||||
export type KeyName = keyof typeof keys
|
||||
|
||||
// Consumer Control Usage codes (for multimedia keys)
|
||||
// These are sent via a separate Consumer Control HID report
|
||||
export const consumerKeys = {
|
||||
PlayPause: 0x00cd,
|
||||
Stop: 0x00b7,
|
||||
@@ -164,69 +135,153 @@ export const consumerKeys = {
|
||||
|
||||
export type ConsumerKeyName = keyof typeof consumerKeys
|
||||
|
||||
// Modifier bitmasks for HID report byte 0
|
||||
export const modifiers = {
|
||||
ControlLeft: 0x01,
|
||||
ShiftLeft: 0x02,
|
||||
AltLeft: 0x04,
|
||||
MetaLeft: 0x08,
|
||||
ControlRight: 0x10,
|
||||
ShiftRight: 0x20,
|
||||
AltRight: 0x40,
|
||||
AltGr: 0x40,
|
||||
MetaRight: 0x80,
|
||||
} as const
|
||||
|
||||
export type ModifierName = keyof typeof modifiers
|
||||
|
||||
// Map HID key codes to modifier bitmasks
|
||||
export const hidKeyToModifierMask: Record<number, number> = {
|
||||
0xe0: 0x01, // ControlLeft
|
||||
0xe1: 0x02, // ShiftLeft
|
||||
0xe2: 0x04, // AltLeft
|
||||
0xe3: 0x08, // MetaLeft
|
||||
0xe4: 0x10, // ControlRight
|
||||
0xe5: 0x20, // ShiftRight
|
||||
0xe6: 0x40, // AltRight
|
||||
0xe7: 0x80, // MetaRight
|
||||
export const modifiers: Partial<Record<CanonicalKey, number>> = {
|
||||
[CanonicalKey.ControlLeft]: 0x01,
|
||||
[CanonicalKey.ShiftLeft]: 0x02,
|
||||
[CanonicalKey.AltLeft]: 0x04,
|
||||
[CanonicalKey.MetaLeft]: 0x08,
|
||||
[CanonicalKey.ControlRight]: 0x10,
|
||||
[CanonicalKey.ShiftRight]: 0x20,
|
||||
[CanonicalKey.AltRight]: 0x40,
|
||||
[CanonicalKey.MetaRight]: 0x80,
|
||||
}
|
||||
|
||||
// Update modifier mask when a HID modifier key is pressed/released.
|
||||
export function updateModifierMaskForHidKey(mask: number, hidKey: number, press: boolean): number {
|
||||
const bit = hidKeyToModifierMask[hidKey] ?? 0
|
||||
export const keyToHidUsage = {
|
||||
[CanonicalKey.KeyA]: 0x04,
|
||||
[CanonicalKey.KeyB]: 0x05,
|
||||
[CanonicalKey.KeyC]: 0x06,
|
||||
[CanonicalKey.KeyD]: 0x07,
|
||||
[CanonicalKey.KeyE]: 0x08,
|
||||
[CanonicalKey.KeyF]: 0x09,
|
||||
[CanonicalKey.KeyG]: 0x0a,
|
||||
[CanonicalKey.KeyH]: 0x0b,
|
||||
[CanonicalKey.KeyI]: 0x0c,
|
||||
[CanonicalKey.KeyJ]: 0x0d,
|
||||
[CanonicalKey.KeyK]: 0x0e,
|
||||
[CanonicalKey.KeyL]: 0x0f,
|
||||
[CanonicalKey.KeyM]: 0x10,
|
||||
[CanonicalKey.KeyN]: 0x11,
|
||||
[CanonicalKey.KeyO]: 0x12,
|
||||
[CanonicalKey.KeyP]: 0x13,
|
||||
[CanonicalKey.KeyQ]: 0x14,
|
||||
[CanonicalKey.KeyR]: 0x15,
|
||||
[CanonicalKey.KeyS]: 0x16,
|
||||
[CanonicalKey.KeyT]: 0x17,
|
||||
[CanonicalKey.KeyU]: 0x18,
|
||||
[CanonicalKey.KeyV]: 0x19,
|
||||
[CanonicalKey.KeyW]: 0x1a,
|
||||
[CanonicalKey.KeyX]: 0x1b,
|
||||
[CanonicalKey.KeyY]: 0x1c,
|
||||
[CanonicalKey.KeyZ]: 0x1d,
|
||||
[CanonicalKey.Digit1]: 0x1e,
|
||||
[CanonicalKey.Digit2]: 0x1f,
|
||||
[CanonicalKey.Digit3]: 0x20,
|
||||
[CanonicalKey.Digit4]: 0x21,
|
||||
[CanonicalKey.Digit5]: 0x22,
|
||||
[CanonicalKey.Digit6]: 0x23,
|
||||
[CanonicalKey.Digit7]: 0x24,
|
||||
[CanonicalKey.Digit8]: 0x25,
|
||||
[CanonicalKey.Digit9]: 0x26,
|
||||
[CanonicalKey.Digit0]: 0x27,
|
||||
[CanonicalKey.Enter]: 0x28,
|
||||
[CanonicalKey.Escape]: 0x29,
|
||||
[CanonicalKey.Backspace]: 0x2a,
|
||||
[CanonicalKey.Tab]: 0x2b,
|
||||
[CanonicalKey.Space]: 0x2c,
|
||||
[CanonicalKey.Minus]: 0x2d,
|
||||
[CanonicalKey.Equal]: 0x2e,
|
||||
[CanonicalKey.BracketLeft]: 0x2f,
|
||||
[CanonicalKey.BracketRight]: 0x30,
|
||||
[CanonicalKey.Backslash]: 0x31,
|
||||
[CanonicalKey.Semicolon]: 0x33,
|
||||
[CanonicalKey.Quote]: 0x34,
|
||||
[CanonicalKey.Backquote]: 0x35,
|
||||
[CanonicalKey.Comma]: 0x36,
|
||||
[CanonicalKey.Period]: 0x37,
|
||||
[CanonicalKey.Slash]: 0x38,
|
||||
[CanonicalKey.CapsLock]: 0x39,
|
||||
[CanonicalKey.F1]: 0x3a,
|
||||
[CanonicalKey.F2]: 0x3b,
|
||||
[CanonicalKey.F3]: 0x3c,
|
||||
[CanonicalKey.F4]: 0x3d,
|
||||
[CanonicalKey.F5]: 0x3e,
|
||||
[CanonicalKey.F6]: 0x3f,
|
||||
[CanonicalKey.F7]: 0x40,
|
||||
[CanonicalKey.F8]: 0x41,
|
||||
[CanonicalKey.F9]: 0x42,
|
||||
[CanonicalKey.F10]: 0x43,
|
||||
[CanonicalKey.F11]: 0x44,
|
||||
[CanonicalKey.F12]: 0x45,
|
||||
[CanonicalKey.PrintScreen]: 0x46,
|
||||
[CanonicalKey.ScrollLock]: 0x47,
|
||||
[CanonicalKey.Pause]: 0x48,
|
||||
[CanonicalKey.Insert]: 0x49,
|
||||
[CanonicalKey.Home]: 0x4a,
|
||||
[CanonicalKey.PageUp]: 0x4b,
|
||||
[CanonicalKey.Delete]: 0x4c,
|
||||
[CanonicalKey.End]: 0x4d,
|
||||
[CanonicalKey.PageDown]: 0x4e,
|
||||
[CanonicalKey.ArrowRight]: 0x4f,
|
||||
[CanonicalKey.ArrowLeft]: 0x50,
|
||||
[CanonicalKey.ArrowDown]: 0x51,
|
||||
[CanonicalKey.ArrowUp]: 0x52,
|
||||
[CanonicalKey.NumLock]: 0x53,
|
||||
[CanonicalKey.NumpadDivide]: 0x54,
|
||||
[CanonicalKey.NumpadMultiply]: 0x55,
|
||||
[CanonicalKey.NumpadSubtract]: 0x56,
|
||||
[CanonicalKey.NumpadAdd]: 0x57,
|
||||
[CanonicalKey.NumpadEnter]: 0x58,
|
||||
[CanonicalKey.Numpad1]: 0x59,
|
||||
[CanonicalKey.Numpad2]: 0x5a,
|
||||
[CanonicalKey.Numpad3]: 0x5b,
|
||||
[CanonicalKey.Numpad4]: 0x5c,
|
||||
[CanonicalKey.Numpad5]: 0x5d,
|
||||
[CanonicalKey.Numpad6]: 0x5e,
|
||||
[CanonicalKey.Numpad7]: 0x5f,
|
||||
[CanonicalKey.Numpad8]: 0x60,
|
||||
[CanonicalKey.Numpad9]: 0x61,
|
||||
[CanonicalKey.Numpad0]: 0x62,
|
||||
[CanonicalKey.NumpadDecimal]: 0x63,
|
||||
[CanonicalKey.IntlBackslash]: 0x64,
|
||||
[CanonicalKey.ContextMenu]: 0x65,
|
||||
[CanonicalKey.F13]: 0x68,
|
||||
[CanonicalKey.F14]: 0x69,
|
||||
[CanonicalKey.F15]: 0x6a,
|
||||
[CanonicalKey.F16]: 0x6b,
|
||||
[CanonicalKey.F17]: 0x6c,
|
||||
[CanonicalKey.F18]: 0x6d,
|
||||
[CanonicalKey.F19]: 0x6e,
|
||||
[CanonicalKey.F20]: 0x6f,
|
||||
[CanonicalKey.F21]: 0x70,
|
||||
[CanonicalKey.F22]: 0x71,
|
||||
[CanonicalKey.F23]: 0x72,
|
||||
[CanonicalKey.F24]: 0x73,
|
||||
[CanonicalKey.ControlLeft]: 0xe0,
|
||||
[CanonicalKey.ShiftLeft]: 0xe1,
|
||||
[CanonicalKey.AltLeft]: 0xe2,
|
||||
[CanonicalKey.MetaLeft]: 0xe3,
|
||||
[CanonicalKey.ControlRight]: 0xe4,
|
||||
[CanonicalKey.ShiftRight]: 0xe5,
|
||||
[CanonicalKey.AltRight]: 0xe6,
|
||||
[CanonicalKey.MetaRight]: 0xe7,
|
||||
} as const satisfies Record<CanonicalKey, number>
|
||||
|
||||
export function canonicalKeyToHidUsage(key: CanonicalKey): number {
|
||||
return keyToHidUsage[key]
|
||||
}
|
||||
|
||||
export function updateModifierMaskForKey(mask: number, key: CanonicalKey, press: boolean): number {
|
||||
const bit = modifiers[key] ?? 0
|
||||
if (bit === 0) return mask
|
||||
return press ? (mask | bit) : (mask & ~bit)
|
||||
}
|
||||
|
||||
// Keys that latch (toggle state) instead of being held
|
||||
export const latchingKeys = ['CapsLock', 'ScrollLock', 'NumLock'] as const
|
||||
|
||||
// Modifier key names
|
||||
export const modifierKeyNames = [
|
||||
'ControlLeft',
|
||||
'ControlRight',
|
||||
'ShiftLeft',
|
||||
'ShiftRight',
|
||||
'AltLeft',
|
||||
'AltRight',
|
||||
'AltGr',
|
||||
'MetaLeft',
|
||||
'MetaRight',
|
||||
export const latchingKeys = [
|
||||
CanonicalKey.CapsLock,
|
||||
CanonicalKey.ScrollLock,
|
||||
CanonicalKey.NumLock,
|
||||
] as const
|
||||
|
||||
// Check if a key is a modifier
|
||||
export function isModifierKey(keyName: string): keyName is ModifierName {
|
||||
return keyName in modifiers
|
||||
}
|
||||
|
||||
// Get modifier bitmask for a key name
|
||||
export function getModifierMask(keyName: string): number {
|
||||
if (keyName in modifiers) {
|
||||
return modifiers[keyName as ModifierName]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Normalize browser-specific KeyboardEvent.code variants.
|
||||
export function normalizeKeyboardCode(code: string, key: string): string {
|
||||
if (code === 'IntlBackslash' && (key === '`' || key === '~')) return 'Backquote'
|
||||
@@ -238,18 +293,10 @@ export function normalizeKeyboardCode(code: string, key: string): string {
|
||||
return code
|
||||
}
|
||||
|
||||
// Convert KeyboardEvent.code/key to USB HID usage code.
|
||||
export function keyboardEventToHidCode(code: string, key: string): number | undefined {
|
||||
export function keyboardEventToCanonicalKey(code: string, key: string): CanonicalKey | undefined {
|
||||
const normalizedCode = normalizeKeyboardCode(code, key)
|
||||
return keys[normalizedCode as KeyName]
|
||||
}
|
||||
|
||||
// Decode modifier byte into individual states
|
||||
export function decodeModifiers(modifier: number) {
|
||||
return {
|
||||
isShiftActive: (modifier & 0x22) !== 0, // ShiftLeft | ShiftRight
|
||||
isControlActive: (modifier & 0x11) !== 0, // ControlLeft | ControlRight
|
||||
isAltActive: (modifier & 0x44) !== 0, // AltLeft | AltRight
|
||||
isMetaActive: (modifier & 0x88) !== 0, // MetaLeft | MetaRight
|
||||
if (normalizedCode in keys) {
|
||||
return keys[normalizedCode as KeyName]
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user