diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-08-01 04:27:50 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-01 04:27:50 +0300 |
| commit | b76adc155d979705a353fd6dc87c702f9dfc6c22 (patch) | |
| tree | f613254e0cfbfd2b4450a6a5f3ee7c0ba8136976 /src/platform | |
| parent | 8d190eddda2c74afa3dc043f8ec8020ffd7b5a94 (diff) | |
fix(input): preserve native key lifecycle across routing (#2142)
refs #2077
Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com>
Diffstat (limited to 'src/platform')
| -rw-r--r-- | src/platform/windows.rs | 77 |
1 files changed, 71 insertions, 6 deletions
diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 35ecfd82..6cc642eb 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -64,15 +64,33 @@ use super::{ClipboardImage, ForegroundJob, Signal}; const STILL_ACTIVE: u32 = 259; const FOREGROUND_PROCESS_SNAPSHOT_CACHE_TTL: Duration = Duration::from_millis(250); -pub(crate) fn encode_windows_conpty_shift_enter(key: crate::input::TerminalKey) -> Option<Vec<u8>> { +/// Encode native or targeted semantic Win32 input for a compatible ConPTY destination. +pub(crate) fn encode_windows_conpty_fallback(key: &crate::input::TerminalKey) -> Option<Vec<u8>> { use crossterm::event::{KeyCode, KeyEventKind, KeyModifiers}; - if key.code != KeyCode::Enter || key.modifiers != KeyModifiers::SHIFT { - return None; - } + let (virtual_key_code, virtual_scan_code, unicode, control_key_state) = + if let Some(record) = key.windows_record() { + ( + record.virtual_key_code, + record.virtual_scan_code, + record.unicode, + record.control_key_state, + ) + } else if key.code == KeyCode::Enter && key.modifiers == KeyModifiers::SHIFT { + (13, 28, 13, 16) + } else { + return None; + }; + let key_down = key.kind != KeyEventKind::Release; + let repeat_count = if key_down { key.repeat_count.max(1) } else { 1 }; - let key_down = !matches!(key.kind, KeyEventKind::Release); - Some(format!("\x1b[13;28;13;{};16;1_", u8::from(key_down)).into_bytes()) + Some( + format!( + "\x1b[{virtual_key_code};{virtual_scan_code};{unicode};{};{control_key_state};{repeat_count}_", + u8::from(key_down), + ) + .into_bytes(), + ) } #[derive(Debug)] @@ -1390,6 +1408,53 @@ mod tests { }; #[test] + fn windows_conpty_native_encoder_uses_canonical_phase_and_repeat_count() { + let key = crate::input::TerminalKey::new( + crossterm::event::KeyCode::Esc, + crossterm::event::KeyModifiers::empty(), + ) + .with_windows_record(crate::input::WindowsKeyRecord { + key_down: true, + repeat_count: 3, + virtual_key_code: 27, + virtual_scan_code: 1, + unicode: 27, + control_key_state: 0, + }); + + assert_eq!( + super::encode_windows_conpty_fallback(&key), + Some(b"\x1b[27;1;27;1;0;3_".to_vec()) + ); + let mut release = key.with_kind(crossterm::event::KeyEventKind::Release); + release.repeat_count = 3; + assert_eq!( + super::encode_windows_conpty_fallback(&release), + Some(b"\x1b[27;1;27;0;0;1_".to_vec()) + ); + } + + #[test] + fn windows_conpty_native_encoder_preserves_semantic_shift_enter_fallback() { + let shift_enter = crate::input::TerminalKey::new( + crossterm::event::KeyCode::Enter, + crossterm::event::KeyModifiers::SHIFT, + ); + + assert_eq!( + super::encode_windows_conpty_fallback(&shift_enter), + Some(b"\x1b[13;28;13;1;16;1_".to_vec()) + ); + assert_eq!( + super::encode_windows_conpty_fallback(&crate::input::TerminalKey::new( + crossterm::event::KeyCode::Enter, + crossterm::event::KeyModifiers::empty(), + )), + None + ); + } + + #[test] fn windows_notification_text_is_null_terminated_and_unicode_safe() { let mut destination = [u16::MAX; 6]; super::copy_wide_truncated(&mut destination, "abc😀def"); |
