fix: correct keyboard scancode mapping from KeyTranslator.java
All checks were successful
CI / fmt (push) Successful in 39s
Publish / frontend (push) Successful in 45s
CI / check (push) Successful in 1m22s
CI / clippy (push) Successful in 1m38s
Publish / backend (push) Successful in 2m48s

The mapping was built assuming keynr followed physical keyboard order
with Escape=0. In reality, KeyTranslator.java maps Java VK_* codes to
keynr values with a different layout:

- keynr 0 = Backquote (not Escape)
- keynr 59 = Escape
- keynr 27 = Enter (not 40)
- keynr 40 = Backslash (not 42)

This caused the number row and QWERTY row to be off by 1 (Escape
was inserted at position 0, pushing everything). The home row
(CapsLock=28, A=29...L=37) happened to align by coincidence, which
is why 'g'(33) worked but 'r'(should be 18, was 19=T) didn't.

Also fixes: F1-F12 off by 1, PrintScreen/ScrollLock/Pause off by 1,
numpad operator keys swapped with numpad 7/8/9.

Corrected both Rust (input.rs) and TypeScript (input.ts) mappings
to match the authoritative KeyTranslator.java VK→keynr table.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-07 10:42:02 +03:00
parent 4f7d69c75a
commit 75a51def79
2 changed files with 111 additions and 88 deletions

View File

@@ -49,54 +49,41 @@ pub const HOTKEY_CTRL_ALT_DEL: &str = "36 f0 37 f0 4e";
/// Map a JavaScript `KeyboardEvent.code` string to an e-RIC scancode.
/// Returns `None` for unmapped keys.
pub fn js_code_to_scancode(code: &str) -> Option<u8> {
// Mapping derived from KeyTranslator.java line 14 (Java VK_* → keynr table).
Some(match code {
// Function row
"Escape" => 0,
"F1" => 59,
"F2" => 60,
"F3" => 61,
"F4" => 62,
"F5" => 63,
"F6" => 64,
"F7" => 65,
"F8" => 66,
"F9" => 67,
"F10" => 68,
"F11" => 69,
"F12" => 70,
// Number row (keynr 0-13)
"Backquote" => 0,
"Digit1" => 1,
"Digit2" => 2,
"Digit3" => 3,
"Digit4" => 4,
"Digit5" => 5,
"Digit6" => 6,
"Digit7" => 7,
"Digit8" => 8,
"Digit9" => 9,
"Digit0" => 10,
"Minus" => 11,
"Equal" => 12,
"Backspace" => 13,
// Number row
"Backquote" => 1,
"Digit1" => 2,
"Digit2" => 3,
"Digit3" => 4,
"Digit4" => 5,
"Digit5" => 6,
"Digit6" => 7,
"Digit7" => 8,
"Digit8" => 9,
"Digit9" => 10,
"Digit0" => 11,
"Minus" => 12,
"Equal" => 13,
"Backspace" => 14,
// QWERTY row (keynr 14-27)
"Tab" => 14,
"KeyQ" => 15,
"KeyW" => 16,
"KeyE" => 17,
"KeyR" => 18,
"KeyT" => 19,
"KeyY" => 20,
"KeyU" => 21,
"KeyI" => 22,
"KeyO" => 23,
"KeyP" => 24,
"BracketLeft" => 25,
"BracketRight" => 26,
"Enter" => 27,
// QWERTY row
"Tab" => 15,
"KeyQ" => 16,
"KeyW" => 17,
"KeyE" => 18,
"KeyR" => 19,
"KeyT" => 20,
"KeyY" => 21,
"KeyU" => 22,
"KeyI" => 23,
"KeyO" => 24,
"KeyP" => 25,
"BracketLeft" => 26,
"BracketRight" => 27,
// Home row
// Home row (keynr 28-40)
"CapsLock" => 28,
"KeyA" => 29,
"KeyS" => 30,
@@ -109,11 +96,10 @@ pub fn js_code_to_scancode(code: &str) -> Option<u8> {
"KeyL" => 37,
"Semicolon" => 38,
"Quote" => 39,
"Enter" => 40,
"Backslash" => 40,
// Bottom row
// Bottom row (keynr 41-53)
"ShiftLeft" => 41,
"Backslash" => 42,
"KeyZ" => 43,
"KeyX" => 44,
"KeyC" => 45,
@@ -126,51 +112,66 @@ pub fn js_code_to_scancode(code: &str) -> Option<u8> {
"Slash" => 52,
"ShiftRight" => 53,
// Modifier / bottom row
// Modifiers (keynr 54-58)
"ControlLeft" => 54,
"MetaLeft" => 105,
"AltLeft" => 55,
"Space" => 56,
"AltRight" => 57,
"MetaRight" => 106,
"ControlRight" => 58,
// Navigation cluster
"PrintScreen" => 71,
"ScrollLock" => 72,
"Pause" => 73,
// Escape + Function keys (keynr 59-71)
"Escape" => 59,
"F1" => 60,
"F2" => 61,
"F3" => 62,
"F4" => 63,
"F5" => 64,
"F6" => 65,
"F7" => 66,
"F8" => 67,
"F9" => 68,
"F10" => 69,
"F11" => 70,
"F12" => 71,
// Navigation cluster (keynr 72-84)
"PrintScreen" => 72,
"ScrollLock" => 73,
"Pause" => 74,
"Insert" => 75,
"Home" => 76,
"PageUp" => 77,
"Delete" => 78,
"End" => 79,
"PageDown" => 80,
// Arrow keys
"ArrowUp" => 81,
"ArrowLeft" => 82,
"ArrowDown" => 83,
"ArrowRight" => 84,
// Numpad
// Numpad (keynr 85-101)
"NumLock" => 85,
"NumpadDivide" => 86,
"NumpadMultiply" => 87,
"NumpadSubtract" => 88,
"Numpad7" => 86,
"Numpad8" => 87,
"Numpad9" => 88,
"NumpadAdd" => 89,
"NumpadEnter" => 98,
"Numpad7" => 90,
"Numpad8" => 94,
"Numpad9" => 99,
"NumpadDivide" => 90,
"Numpad4" => 91,
"Numpad5" => 92,
"Numpad6" => 93,
"NumpadMultiply" => 94,
"Numpad1" => 95,
"Numpad2" => 96,
"Numpad3" => 97,
"NumpadEnter" => 98,
"NumpadSubtract" => 99,
"Numpad0" => 100,
"NumpadDecimal" => 101,
// Windows/Meta keys (keynr 105-106)
"MetaLeft" => 105,
"MetaRight" => 106,
_ => return None,
})
}
@@ -214,10 +215,20 @@ mod tests {
#[test]
fn test_js_code_mapping() {
// From KeyTranslator.java: VK_A(65)→29, VK_ESCAPE(27)→59, etc.
assert_eq!(js_code_to_scancode("KeyA"), Some(29));
assert_eq!(js_code_to_scancode("Escape"), Some(0));
assert_eq!(js_code_to_scancode("KeyR"), Some(18));
assert_eq!(js_code_to_scancode("KeyG"), Some(33));
assert_eq!(js_code_to_scancode("Escape"), Some(59));
assert_eq!(js_code_to_scancode("Backspace"), Some(13));
assert_eq!(js_code_to_scancode("Enter"), Some(27));
assert_eq!(js_code_to_scancode("Backslash"), Some(40));
assert_eq!(js_code_to_scancode("Backquote"), Some(0));
assert_eq!(js_code_to_scancode("F1"), Some(60));
assert_eq!(js_code_to_scancode("ControlLeft"), Some(54));
assert_eq!(js_code_to_scancode("Delete"), Some(78));
assert_eq!(js_code_to_scancode("Numpad7"), Some(86));
assert_eq!(js_code_to_scancode("NumpadMultiply"), Some(94));
assert_eq!(js_code_to_scancode("Unknown"), None);
}
}