diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-23 01:49:12 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-23 01:49:54 +0300 |
| commit | 2a20e90a026936d0d5b96823d74e2e4fe13a166f (patch) | |
| tree | 9113e23b0b740f6bd15717095cd34f36b2f28292 /src | |
| parent | 7e821ba7a362cc634a705ea56e11b070b7b95784 (diff) | |
fix: preserve physical escape on windows
refs #1736
Diffstat (limited to 'src')
| -rw-r--r-- | src/client/input/windows_vti.rs | 67 |
1 files changed, 62 insertions, 5 deletions
diff --git a/src/client/input/windows_vti.rs b/src/client/input/windows_vti.rs index 3b29e790..5742b825 100644 --- a/src/client/input/windows_vti.rs +++ b/src/client/input/windows_vti.rs @@ -389,6 +389,7 @@ impl WindowsInputMapper { key_down = key.key_down, repeat_count = key.repeat_count, virtual_key_code = key.virtual_key_code, + virtual_scan_code = key.virtual_scan_code, unicode = key.unicode, control_key_state = key.control_key_state, "windows input trace: console key record" @@ -452,8 +453,11 @@ impl WindowsInputMapper { return false; } + // Physical Escape carries a scan code. Scan-code-zero Escape can + // introduce raw VT reports and must stay in the framer. let bare_escape = modifiers.is_empty() - && (key.virtual_key_code == 0x1b || (key.virtual_key_code == 0 && key.unicode == 0x1b)); + && ((key.virtual_key_code == 0x1b && key.virtual_scan_code == 0) + || (key.virtual_key_code == 0 && key.unicode == 0x1b)); let ctrl_bracket = key.virtual_key_code == 0xdb && key.unicode == 0x1b && modifiers == crossterm::event::KeyModifiers::CONTROL; @@ -1338,9 +1342,11 @@ mod tests { } #[test] - fn vti_escape_key_record_flushes_to_escape_after_idle() { + fn vti_scan_code_zero_escape_key_record_flushes_after_idle() { let mut translator = WindowsInputTranslator::default(); - assert!(translator.translate(key_vk(0x1b, 0)).is_empty()); + assert!(translator + .translate(key_vk_with_scan_unicode(0x1b, 0, '\0', 0)) + .is_empty()); assert_eq!( translator.idle(), vec![crate::protocol::ClientInputEvent::Key { @@ -1352,6 +1358,19 @@ mod tests { } #[test] + fn vti_physical_escape_key_record_is_immediately_semantic() { + let mut translator = WindowsInputTranslator::default(); + assert_eq!( + translator.translate(key_vk_with_scan_unicode(0x1b, 0x01, '\x1b', 0)), + vec![crate::protocol::ClientInputEvent::Key { + code: crate::protocol::ClientKeyCode::Esc, + modifiers: 0, + kind: crate::protocol::ClientKeyKind::Press, + }] + ); + } + + #[test] fn vti_repeated_escape_record_stays_semantic() { assert_eq!( translate([key_vk_with_repeat(0x1b, 3)]), @@ -1752,8 +1771,8 @@ mod tests { } #[test] - fn vti_escape_key_record_without_unicode_starts_mouse_sequence() { - let records = [key_vk(0x1b, 0)] + fn vti_scan_code_zero_escape_starts_mouse_sequence() { + let records = [key_vk_with_scan_unicode(0x1b, 0, '\0', 0)] .into_iter() .chain("[<35;48;26M".chars().map(key_char)); @@ -1769,6 +1788,44 @@ mod tests { } #[test] + fn vti_nonzero_scan_escape_is_immediately_semantic() { + let mut translator = WindowsInputTranslator::default(); + assert_eq!( + translator.translate(key_vk_with_scan_unicode(0x1b, 0x02, '\0', 0)), + vec![crate::protocol::ClientInputEvent::Key { + code: crate::protocol::ClientKeyCode::Esc, + modifiers: 0, + kind: crate::protocol::ClientKeyKind::Press, + }] + ); + } + + #[test] + fn vti_win32_input_mode_physical_escape_is_immediately_semantic() { + let records = win32_input_mode_encoded_record(WindowsKeyRecord { + key_down: true, + repeat_count: 1, + virtual_key_code: 0x1b, + virtual_scan_code: 0x01, + unicode: 0x1b, + control_key_state: 0, + }); + let mut translator = WindowsInputTranslator::default(); + + assert_eq!( + records + .into_iter() + .flat_map(|record| translator.translate(record)) + .collect::<Vec<_>>(), + vec![crate::protocol::ClientInputEvent::Key { + code: crate::protocol::ClientKeyCode::Esc, + modifiers: 0, + kind: crate::protocol::ClientKeyKind::Press, + }] + ); + } + + #[test] fn vti_win32_input_mode_encoded_mouse_sequence() { assert_eq!( translate(win32_input_mode_encoded_raw_bytes(b"\x1b[<35;48;26M")), |
