diff options
| -rw-r--r-- | src/client/input/windows_vti.rs | 37 |
1 files changed, 28 insertions, 9 deletions
diff --git a/src/client/input/windows_vti.rs b/src/client/input/windows_vti.rs index 2b92c167..0deb3185 100644 --- a/src/client/input/windows_vti.rs +++ b/src/client/input/windows_vti.rs @@ -1353,7 +1353,9 @@ fn ctrl_key_code(vk: u16, u: u16, oem: Option<char>) -> Option<crate::protocol:: use crate::protocol::ClientKeyCode; Some(match (vk, u) { (0xbf, 0x00) => ClientKeyCode::Char(oem?), - (_, 0x00) => ClientKeyCode::Char(' '), + // Keep Ctrl+Break and Ctrl+Space mappings. Other physical keys carrying + // Unicode zero are not evidence of a Space character. + (0x03 | 0x20, 0x00) => ClientKeyCode::Char(' '), (_, 0x1b) => ClientKeyCode::Char('['), (_, 0x1c) => ClientKeyCode::Char('\\'), (_, 0x1d) => ClientKeyCode::Char(']'), @@ -1595,6 +1597,30 @@ mod tests { .collect() } + #[test] + fn vti_ctrl_win_does_not_emit_ctrl_space() { + // Exact host sequences captured in issue #4470: Ctrl down, Win down, + // Ctrl up, Win up. Non-text keys must not become the Space prefix. + let input = + "\x1b[17;29;0;1;8;1_\x1b[91;91;0;1;264;1_\x1b[17;29;0;0;0;1_\x1b[91;91;0;0;256;1_"; + let mut translator = WindowsInputTranslator::default(); + let events: Vec<_> = input + .chars() + .flat_map(|ch| translator.translate(key_char(ch))) + .collect(); + assert!(events.is_empty(), "unexpected input events: {events:?}"); + assert!(translator.idle().is_empty()); + + // Ignoring the chord must not consume or defer the following input. + for record in [key_char('c'), key_vk_with_utf16_mods(0x20, 0, 0x0008)] { + assert_eq!( + translator.translate(record), + translate_with_provenance([record]) + ); + } + assert!(translator.idle().is_empty()); + } + #[cfg(windows)] #[test] fn windows_input_trace_preserves_mapped_event_groups() { @@ -1932,6 +1958,7 @@ mod tests { #[test] fn vti_control_records_keep_physical_digit_identity() { let cases = [ + (0x03, 0x00, ' '), (0x20, 0x00, ' '), (0x31, 0x00, '1'), (0xdc, 0x1c, '\\'), @@ -2126,14 +2153,6 @@ mod tests { } #[test] - fn vti_ctrl_break_record_keeps_semantic_path() { - assert!(matches!( - translate_with_provenance([key_vk(0x03, 0x0008)]).as_slice(), - [crate::protocol::ClientInputEvent::Key { .. }] - )); - } - - #[test] fn vti_ime_virtual_keys_keep_semantic_path() { for vk in [0xe5, 0xe7] { assert!(matches!( |
