From 0496636dc75db14a99aa71a7cadb006dc027d5c6 Mon Sep 17 00:00:00 2001 From: JJ Liebig Date: Thu, 3 Sep 2026 17:20:57 +0200 Subject: fix(windows): preserve ctrl+slash key identity (#3578) refs #2954 --- src/platform/windows.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) (limited to 'src/platform') diff --git a/src/platform/windows.rs b/src/platform/windows.rs index b05b9e6f..890c24ca 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -105,8 +105,8 @@ use windows_sys::{ Input::{ Ime::ImmGetDefaultIMEWnd, KeyboardAndMouse::{ - GetKeyboardLayout, SendInput, INPUT, INPUT_0, INPUT_KEYBOARD, KEYBDINPUT, - KEYEVENTF_KEYUP, + GetKeyboardLayout, SendInput, ToUnicodeEx, INPUT, INPUT_0, INPUT_KEYBOARD, + KEYBDINPUT, KEYEVENTF_KEYUP, }, }, Shell::{ @@ -134,6 +134,32 @@ pub(crate) fn terminal_title_for_presentation(title: &str) -> &str { title.strip_prefix("Administrator: ").unwrap_or(title) } +/// Resolves against the current foreground layout because asynchronous console +/// records do not retain the layout that was active when the key was pressed. +pub(crate) fn resolve_base_printable_key(vk: u16, scan: u16) -> Option { + // SAFETY: Win32 owns the handles; the fixed buffers match the API lengths. + unsafe { + let thread_id = GetWindowThreadProcessId(GetForegroundWindow(), null_mut()); + let layout = GetKeyboardLayout(thread_id); + + let key_state = [0u8; 256]; + let mut output = [0u16; 2]; + let written = ToUnicodeEx( + vk.into(), + scan.into(), + key_state.as_ptr(), + output.as_mut_ptr(), + output.len() as i32, + 0x4, + layout, + ); + let units = output.get(..usize::try_from(written).ok()?)?; + let mut chars = char::decode_utf16(units.iter().copied()); + let ch = chars.next()?.ok()?; + (chars.next().is_none() && !ch.is_control()).then_some(ch) + } +} + const MAX_PROCESS_ENVIRONMENT_BYTES: usize = 256 * 1024; const PROCESS_ENVIRONMENT_READ_CHUNK_BYTES: usize = 16 * 1024; const PROCESS_RUNTIME_MARKER_CACHE_CAPACITY: usize = 1_024; -- cgit v1.2.3