windows: fix freeze on keyboard layout switch

Any winit app on Windows freezes when the keyboard layout is switched by
tools such as Punto Switcher. A minidump of the frozen process shows the
main thread blocked in MsgWaitForMultipleObjectsEx (winit's event-loop
wait), re-entered during message dispatch through the switcher's injected
global hook (pshook64.dll, via CallNextHookEx) — a re-entrant Win32
message-loop wait, not a mutex deadlock (no LAYOUT_CACHE frames present).

Handle WM_INPUTLANGCHANGE to refresh the cached keyboard layout, then
defer to DefWindowProc so the message still propagates to first-level
child windows as the Win32 docs require. The cache refresh is the minimal
change that stops the freeze (verified on Windows 11 by isolation
variants); update_modifiers and swallowing the message are not needed.
This commit is contained in:
Sergey Kikevich
2026-06-14 21:29:35 +03:00
committed by GitHub
parent 850d5f59a7
commit fb78ecbb03
2 changed files with 25 additions and 6 deletions

View File

@@ -51,12 +51,12 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
SystemParametersInfoW, TranslateMessage, WHEEL_DELTA, WINDOWPOS, 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_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, WMSZ_BOTTOM,
WM_INPUT, WM_INPUTLANGCHANGE, 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, WMSZ_BOTTOM,
WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT,
WMSZ_TOPRIGHT, WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW,
WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
@@ -1508,6 +1508,21 @@ unsafe fn public_window_callback_inner(
result = ProcResult::Value(unsafe { DefWindowProcW(window, msg, wparam, lparam) });
},
WM_INPUTLANGCHANGE => {
// Refresh the cached keyboard layout for the newly activated input
// language. This message is sent (by Windows or by layout switchers
// such as Punto Switcher) after the layout changes. Refreshing the
// cache here prevents a freeze that otherwise occurs when switching
// layout via such tools. We still defer to `DefWindowProc` so the
// message keeps propagating to first-level child windows, as the
// Win32 documentation requires.
{
let mut layouts = LAYOUT_CACHE.lock().unwrap();
layouts.get_current_layout();
}
result = ProcResult::DefWindowProc(wparam);
},
// this is necessary for us to maintain minimize/restore state
WM_SYSCOMMAND => {
if wparam == SC_RESTORE as usize {

View File

@@ -58,6 +58,10 @@ changelog entry.
### Fixed
- On Windows, fix a freeze that occurs when the keyboard layout is switched by
tools such as Punto Switcher. The `WM_INPUTLANGCHANGE` message is now handled
to refresh the cached keyboard layout, while still deferring to
`DefWindowProc` for normal propagation.
- On Redox, handle `EINTR` when reading from `event_socket` instead of panicking.
- On Wayland, switch from using the `ahash` hashing algorithm to `foldhash`.
- On macOS, fix borderless game presentation options not sticking after switching spaces.