diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index a5597e8f3d..d11ba17498 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -53,14 +53,15 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ WMSZ_BOTTOM, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT, WMSZ_TOPRIGHT, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, - WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_INPUT_DEVICE_CHANGE, WM_KEYDOWN, - WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, - WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, - WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP, - WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SETTINGCHANGE, - WM_SIZE, WM_SIZING, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED, - WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSEXW, WS_EX_LAYERED, - WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE, + WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_INPUTLANGCHANGE, + WM_INPUT_DEVICE_CHANGE, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, + WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, + WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, + WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, + WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE, WM_SIZING, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, + WM_TOUCH, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSEXW, + WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, + WS_VISIBLE, }; use crate::dpi::{PhysicalPosition, PhysicalSize}; @@ -1778,6 +1779,16 @@ unsafe fn public_window_callback_inner( } }, + // The active input layout changed (e.g. the user switched keyboard layout/IME, or a + // layout-switching tool such as Punto Switcher activated one). `LAYOUT_CACHE` is keyed + // by `HKL`, and Windows can reuse an `HKL` value after the layout it identified is + // unloaded and a different one is loaded in its place, so re-derive the layout instead + // of trusting whatever is already cached for it. + WM_INPUTLANGCHANGE => { + LAYOUT_CACHE.lock().unwrap().refresh_current_layout(); + result = ProcResult::DefWindowProc(wparam); + }, + WM_LBUTTONDOWN => { use crate::event::ElementState::Pressed; use crate::event::MouseButton::Left; diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 5340e43d8b..db7c39fd4f 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -272,6 +272,13 @@ impl LayoutCache { } } + pub fn refresh_current_layout(&mut self) -> (u64, &Layout) { + let locale_id = unsafe { GetKeyboardLayout(0) } as u64; + let layout = Self::prepare_layout(locale_id); + self.layouts.insert(locale_id, layout); + (locale_id, self.layouts.get(&locale_id).unwrap()) + } + pub fn get_agnostic_mods(&mut self) -> ModifiersState { let (_, layout) = self.get_current_layout(); let filter_out_altgr = layout.has_alt_graph && key_pressed(VK_RMENU); @@ -984,3 +991,22 @@ fn vkey_to_non_char_key( _ => Key::Unidentified(native_code), } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn refresh_current_layout_replaces_a_stale_entry() { + let mut cache = LayoutCache::default(); + let (locale_id, _) = cache.get_current_layout(); + + let poisoned_hkl = !locale_id; + cache.layouts.get_mut(&locale_id).unwrap().hkl = poisoned_hkl; + assert_eq!(cache.get_current_layout().1.hkl, poisoned_hkl); + + let (refreshed_id, refreshed_layout) = cache.refresh_current_layout(); + assert_eq!(refreshed_id, locale_id); + assert_eq!(refreshed_layout.hkl, locale_id); + } +}