Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,11 @@ changelog entry.

### Fixed

- On Windows, fix deadlock when switching keyboard layout via tools like PuntoSwitcher or IME
switchers. The `LAYOUT_CACHE` mutex was held across a `PeekMessageW` call in the `WM_KEYDOWN`
handler, which could dispatch re-entrant messages that tried to re-acquire the same lock.
- On Windows, handle `WM_INPUTLANGCHANGE` explicitly to refresh the layout cache and avoid
re-entrant deadlocks through `DefWindowProc`.
- On Orbital, `MonitorHandle::name()` now returns `None` instead of a dummy name.
- On iOS, fixed `SurfaceResized` and `Window::surface_size` not reporting the size of the actual surface.
- On macOS, fixed the scancode conversion for audio volume keys.
Expand Down
28 changes: 22 additions & 6 deletions src/platform_impl/windows/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
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_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_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, 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, WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW,
WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
};
Expand Down Expand Up @@ -1569,6 +1569,22 @@ unsafe fn public_window_callback_inner(
result = ProcResult::DefWindowProc(wparam);
},

WM_INPUTLANGCHANGE => {
// Refresh the layout cache so subsequent key events use the new
// layout. This message is sent by Windows (or tools like
// PuntoSwitcher) when the keyboard layout changes. We handle it
// explicitly to avoid re-entrant deadlocks: without this handler
// the message falls through to DefWindowProc which may
// synchronously dispatch IME messages while LAYOUT_CACHE is held
// by a parent WNDPROC frame.
{
let mut layouts = LAYOUT_CACHE.lock().unwrap();
layouts.get_current_layout();
}
update_modifiers(window, userdata);
result = ProcResult::Value(1);
},
Copy link
Copy Markdown
Collaborator

@acarl005 acarl005 May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure we aren't breaking something by preventing WM_INPUTLANGCHANGE from falling back to DefWindowProc? The official microsoft docs say:

"You should make any application-specific settings and pass the message to the DefWindowProc function, which passes the message to all first-level child windows"


// this is necessary for us to maintain minimize/restore state
WM_SYSCOMMAND => {
if wparam == SC_RESTORE as usize {
Expand Down
12 changes: 10 additions & 2 deletions src/platform_impl/windows/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,22 @@ impl KeyEventBuilder {
let pending_token = self.pending.add_pending();
*result = ProcResult::Value(0);

let next_msg = next_kbd_msg(hwnd);

let mut layouts = LAYOUT_CACHE.lock().unwrap();
let mut finished_event_info = Some(PartialKeyEventInfo::from_message(
wparam,
lparam,
ElementState::Pressed,
&mut layouts,
));
// We MUST release the layout lock before calling `next_kbd_msg`,
// otherwise PeekMessageW may dispatch re-entrant messages (e.g.
// WM_INPUTLANGCHANGE from PuntoSwitcher or other IME switchers)
// that try to re-acquire LAYOUT_CACHE on the same thread, causing
// a deadlock.
drop(layouts);

let next_msg = next_kbd_msg(hwnd);

let mut event_info = self.event_info.lock().unwrap();
*event_info = None;
if let Some(next_msg) = next_msg {
Expand All @@ -133,6 +140,7 @@ impl KeyEventBuilder {
// store the partial information, and add to it in the upcoming events
*event_info = finished_event_info.take();
} else {
let mut layouts = LAYOUT_CACHE.lock().unwrap();
let (_, layout) = layouts.get_current_layout();
let is_fake = {
let curr_event = finished_event_info.as_ref().unwrap();
Expand Down