From 5b9d3ea097b173202964dbdf09d6a9568d0fbab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=B0=8F=E7=99=BD?= <364772080@qq.com> Date: Thu, 5 Jun 2025 23:57:47 +0800 Subject: [PATCH] =?UTF-8?q?=EF=BD=97indows:=20Only=20call=20`TranslateMess?= =?UTF-8?q?age`=20when=20we=20can't=20handle=20the=20event=20(#32166)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR improves key handling on Windows by moving the `TranslateMessage` call from the message loop to after we handled `WM_KEYDOWN`. This brings Windows behavior more in line with macOS and gives us finer control over key events. As a result, Vim keybindings now work properly even when an IME is active. The trade-off is that it might introduce a slight delay in text input. Release Notes: - N/A --- crates/gpui/src/platform/windows/events.rs | 76 ++++++++++++-------- crates/gpui/src/platform/windows/platform.rs | 3 - crates/gpui/src/platform/windows/window.rs | 3 - 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 8c69bc9df7..1a357ddd30 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -83,11 +83,11 @@ pub(crate) fn handle_msg( WM_XBUTTONUP => handle_xbutton_msg(handle, wparam, lparam, handle_mouse_up_msg, state_ptr), WM_MOUSEWHEEL => handle_mouse_wheel_msg(handle, wparam, lparam, state_ptr), WM_MOUSEHWHEEL => handle_mouse_horizontal_wheel_msg(handle, wparam, lparam, state_ptr), - WM_SYSKEYDOWN => handle_syskeydown_msg(wparam, lparam, state_ptr), - WM_SYSKEYUP => handle_syskeyup_msg(wparam, lparam, state_ptr), + WM_SYSKEYDOWN => handle_syskeydown_msg(handle, wparam, lparam, state_ptr), + WM_SYSKEYUP => handle_syskeyup_msg(handle, wparam, lparam, state_ptr), WM_SYSCOMMAND => handle_system_command(wparam, state_ptr), - WM_KEYDOWN => handle_keydown_msg(wparam, lparam, state_ptr), - WM_KEYUP => handle_keyup_msg(wparam, lparam, state_ptr), + WM_KEYDOWN => handle_keydown_msg(handle, wparam, lparam, state_ptr), + WM_KEYUP => handle_keyup_msg(handle, wparam, lparam, state_ptr), WM_CHAR => handle_char_msg(wparam, state_ptr), WM_DEADCHAR => handle_dead_char_msg(wparam, state_ptr), WM_IME_STARTCOMPOSITION => handle_ime_position(handle, state_ptr), @@ -337,12 +337,13 @@ fn handle_mouse_leave_msg(state_ptr: Rc) -> Option } fn handle_syskeydown_msg( + handle: HWND, wparam: WPARAM, lparam: LPARAM, state_ptr: Rc, ) -> Option { let mut lock = state_ptr.state.borrow_mut(); - let input = handle_key_event(wparam, lparam, &mut lock, |keystroke| { + let input = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| { PlatformInput::KeyDown(KeyDownEvent { keystroke, is_held: lparam.0 & (0x1 << 30) > 0, @@ -358,7 +359,6 @@ fn handle_syskeydown_msg( if handled { lock.system_key_handled = true; - lock.suppress_next_char_msg = true; Some(0) } else { // we need to call `DefWindowProcW`, or we will lose the system-wide `Alt+F4`, `Alt+{other keys}` @@ -368,12 +368,13 @@ fn handle_syskeydown_msg( } fn handle_syskeyup_msg( + handle: HWND, wparam: WPARAM, lparam: LPARAM, state_ptr: Rc, ) -> Option { let mut lock = state_ptr.state.borrow_mut(); - let input = handle_key_event(wparam, lparam, &mut lock, |keystroke| { + let input = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| { PlatformInput::KeyUp(KeyUpEvent { keystroke }) })?; let mut func = lock.callbacks.input.take()?; @@ -388,12 +389,13 @@ fn handle_syskeyup_msg( // It's a known bug that you can't trigger `ctrl-shift-0`. See: // https://superuser.com/questions/1455762/ctrl-shift-number-key-combination-has-stopped-working-for-a-few-numbers fn handle_keydown_msg( + handle: HWND, wparam: WPARAM, lparam: LPARAM, state_ptr: Rc, ) -> Option { let mut lock = state_ptr.state.borrow_mut(); - let Some(input) = handle_key_event(wparam, lparam, &mut lock, |keystroke| { + let Some(input) = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| { PlatformInput::KeyDown(KeyDownEvent { keystroke, is_held: lparam.0 & (0x1 << 30) > 0, @@ -401,32 +403,42 @@ fn handle_keydown_msg( }) else { return Some(1); }; + drop(lock); - let Some(mut func) = lock.callbacks.input.take() else { + let is_composing = with_input_handler(&state_ptr, |input_handler| { + input_handler.marked_text_range() + }) + .flatten() + .is_some(); + if is_composing { + translate_message(handle, wparam, lparam); + return Some(0); + } + + let Some(mut func) = state_ptr.state.borrow_mut().callbacks.input.take() else { return Some(1); }; - drop(lock); let handled = !func(input).propagate; - let mut lock = state_ptr.state.borrow_mut(); - lock.callbacks.input = Some(func); + state_ptr.state.borrow_mut().callbacks.input = Some(func); if handled { - lock.suppress_next_char_msg = true; Some(0) } else { + translate_message(handle, wparam, lparam); Some(1) } } fn handle_keyup_msg( + handle: HWND, wparam: WPARAM, lparam: LPARAM, state_ptr: Rc, ) -> Option { let mut lock = state_ptr.state.borrow_mut(); - let Some(input) = handle_key_event(wparam, lparam, &mut lock, |keystroke| { + let Some(input) = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| { PlatformInput::KeyUp(KeyUpEvent { keystroke }) }) else { return Some(1); @@ -1213,7 +1225,23 @@ fn handle_input_language_changed( Some(0) } +#[inline] +fn translate_message(handle: HWND, wparam: WPARAM, lparam: LPARAM) { + let msg = MSG { + hwnd: handle, + message: WM_KEYDOWN, + wParam: wparam, + lParam: lparam, + // It seems like leaving the following two parameters empty doesn't break key events, they still work as expected. + // But if any bugs pop up after this PR, this is probably the place to look first. + time: 0, + pt: POINT::default(), + }; + unsafe { TranslateMessage(&msg).ok().log_err() }; +} + fn handle_key_event( + handle: HWND, wparam: WPARAM, lparam: LPARAM, state: &mut WindowsWindowState, @@ -1222,15 +1250,10 @@ fn handle_key_event( where F: FnOnce(Keystroke) -> PlatformInput, { - state.suppress_next_char_msg = false; let virtual_key = VIRTUAL_KEY(wparam.loword()); let mut modifiers = current_modifiers(); match virtual_key { - VK_PROCESSKEY => { - // IME composition - None - } VK_SHIFT | VK_CONTROL | VK_MENU | VK_LWIN | VK_RWIN => { if state .last_reported_modifiers @@ -1244,6 +1267,11 @@ where })) } vkey => { + let vkey = if vkey == VK_PROCESSKEY { + VIRTUAL_KEY(unsafe { ImmGetVirtualKey(handle) } as u16) + } else { + vkey + }; let keystroke = parse_normal_key(vkey, lparam, modifiers)?; Some(f(keystroke)) } @@ -1491,12 +1519,7 @@ fn with_input_handler(state_ptr: &Rc, f: F) -> Opti where F: FnOnce(&mut PlatformInputHandler) -> R, { - let mut lock = state_ptr.state.borrow_mut(); - if lock.suppress_next_char_msg { - return None; - } - let mut input_handler = lock.input_handler.take()?; - drop(lock); + let mut input_handler = state_ptr.state.borrow_mut().input_handler.take()?; let result = f(&mut input_handler); state_ptr.state.borrow_mut().input_handler = Some(input_handler); Some(result) @@ -1510,9 +1533,6 @@ where F: FnOnce(&mut PlatformInputHandler, f32) -> Option, { let mut lock = state_ptr.state.borrow_mut(); - if lock.suppress_next_char_msg { - return None; - } let mut input_handler = lock.input_handler.take()?; let scale_factor = lock.scale_factor; drop(lock); diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index d3fb5d326f..1fbbd7b782 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -231,9 +231,6 @@ impl WindowsPlatform { } } _ => { - // todo(windows) - // crate `windows 0.56` reports true as Err - TranslateMessage(&msg).as_bool(); DispatchMessageW(&msg); } } diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 671cf72c96..cd55a851cc 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -43,7 +43,6 @@ pub struct WindowsWindowState { pub callbacks: Callbacks, pub input_handler: Option, pub last_reported_modifiers: Option, - pub suppress_next_char_msg: bool, pub system_key_handled: bool, pub hovered: bool, @@ -103,7 +102,6 @@ impl WindowsWindowState { let callbacks = Callbacks::default(); let input_handler = None; let last_reported_modifiers = None; - let suppress_next_char_msg = false; let system_key_handled = false; let hovered = false; let click_state = ClickState::new(); @@ -123,7 +121,6 @@ impl WindowsWindowState { callbacks, input_handler, last_reported_modifiers, - suppress_next_char_msg, system_key_handled, hovered, renderer,