From ef5910bed665736d07056ca530cab7a8b4ba8a9e Mon Sep 17 00:00:00 2001 From: rhysd Date: Mon, 29 Jun 2026 20:33:34 +0900 Subject: [PATCH] winit-win32: fix `redraw_requested` can be ignored while the system popup is shown. --- winit-win32/src/event_loop.rs | 53 ++++++++++++++++++++++--------- winit-win32/src/window_state.rs | 2 ++ winit/src/changelog/unreleased.md | 1 + 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/winit-win32/src/event_loop.rs b/winit-win32/src/event_loop.rs index 4be6164354..0d7e6e930b 100644 --- a/winit-win32/src/event_loop.rs +++ b/winit-win32/src/event_loop.rs @@ -49,17 +49,17 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED, SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, SetCursor, SetWindowPos, 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_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, + WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERMENULOOP, WM_ENTERSIZEMOVE, WM_EXITMENULOOP, + WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, + 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, 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, }; use winit_core::application::ApplicationHandler; use winit_core::cursor::{CustomCursor, CustomCursorSource}; @@ -1177,14 +1177,33 @@ unsafe fn public_window_callback_inner( result = ProcResult::Value(0); }, + WM_ENTERMENULOOP => { + userdata.window_state_lock().menu_loop_depth += 1; + result = ProcResult::Value(0); + }, + + WM_EXITMENULOOP => { + // Redraw requests made during the menu loop can be dropped by the system (#4608). + // Call pending redraw once the loop has exited. + let should_redraw = { + let mut state = userdata.window_state_lock(); + state.menu_loop_depth = state.menu_loop_depth.saturating_sub(1); + state.menu_loop_depth == 0 && mem::take(&mut state.redraw_requested) + }; + if should_redraw { + unsafe { RedrawWindow(window, ptr::null(), ptr::null_mut(), RDW_INTERNALPAINT) }; + } + result = ProcResult::Value(0); + }, + WM_PAINT => { - userdata.window_state_lock().redraw_requested = - userdata.event_loop_runner.should_buffer(); + let should_buffer = userdata.event_loop_runner.should_buffer(); + userdata.window_state_lock().redraw_requested = should_buffer; // We'll buffer only in response to `UpdateWindow`, if win32 decides to redraw the // window outside the normal flow of the event loop. This way mark event as handled // and request a normal redraw with `RedrawWindow`. - if !userdata.event_loop_runner.should_buffer() { + if !should_buffer { userdata.send_window_event(window, WindowEvent::RedrawRequested); } @@ -1193,7 +1212,11 @@ unsafe fn public_window_callback_inner( // user asked for redraw during `RedrawRequested` event handling and request it again // after marking `WM_PAINT` as handled. result = ProcResult::Value(unsafe { DefWindowProcW(window, msg, wparam, lparam) }); - if std::mem::take(&mut userdata.window_state_lock().redraw_requested) { + let should_redraw = { + let mut state = userdata.window_state_lock(); + state.menu_loop_depth == 0 && mem::take(&mut state.redraw_requested) + }; + if should_redraw { unsafe { RedrawWindow(window, ptr::null(), ptr::null_mut(), RDW_INTERNALPAINT) }; } }, diff --git a/winit-win32/src/window_state.rs b/winit-win32/src/window_state.rs index ed06d76042..2635fa00c6 100644 --- a/winit-win32/src/window_state.rs +++ b/winit-win32/src/window_state.rs @@ -60,6 +60,7 @@ pub(crate) struct WindowState { // Flag whether redraw was requested. pub redraw_requested: bool, + pub menu_loop_depth: u32, pub dragging: bool, @@ -191,6 +192,7 @@ impl WindowState { is_active: false, is_focused: false, redraw_requested: false, + menu_loop_depth: 0, dragging: false, diff --git a/winit/src/changelog/unreleased.md b/winit/src/changelog/unreleased.md index 1c58b725c0..7622305ead 100644 --- a/winit/src/changelog/unreleased.md +++ b/winit/src/changelog/unreleased.md @@ -67,3 +67,4 @@ changelog entry. - On macOS, fix borderless game presentation options not sticking after switching spaces. - On macOS, fix IME being locked on (regardless of requests to disable) after being enabled once. - On macOS, fix a panic and incorrect cursor position in Ime::Preedit when the preedit string contains special characters (ie. emojis) caused by incorrect UTF-16 to UTF-8 offset conversion. +- On Windows, fix `request_redraw` calls are ignored while the system popup menu is shown.