On Windows, fix RedrawRequested delivery

When calling `Window::request_redraw` from the `RedrawRequested`
handler the `RedrawWindow` won't result in `WM_PAINT` being delivered
due since user callback is run before `DefWindowProcW` is called.

Track whether the user called `Window::request_redraw` and ask for
`RedrawWindow` after running the said function during `WM_PAINT`
handling.

Fixes #3150.
This commit is contained in:
Kirill Chibisov 2023-10-20 14:52:01 +04:00 committed by GitHub
parent c0db53a516
commit 98b3508aca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 24 additions and 9 deletions

View file

@ -41,6 +41,7 @@ And please only add new entries to the top of this list, right below the `# Unre
- On macOS, fix assertion when pressing `Globe` key. - On macOS, fix assertion when pressing `Globe` key.
- On Windows, updated `WM_MOUSEMOVE` to detect when cursor Enter or Leave window client area while captured and send the corresponding events. (#3153) - On Windows, updated `WM_MOUSEMOVE` to detect when cursor Enter or Leave window client area while captured and send the corresponding events. (#3153)
- On macOS, fix crash when accessing tabbing APIs. - On macOS, fix crash when accessing tabbing APIs.
- On Windows, fix `RedrawRequested` not being delivered when calling `Window::request_redraw` from `RedrawRequested`.
# 0.29.1-beta # 0.29.1-beta

View file

@ -1168,19 +1168,28 @@ unsafe fn public_window_callback_inner<T: 'static>(
} }
WM_PAINT => { WM_PAINT => {
if userdata.event_loop_runner.should_buffer() { userdata.window_state_lock().redraw_requested =
// this branch can happen in response to `UpdateWindow`, if win32 decides to userdata.event_loop_runner.should_buffer();
// redraw the window outside the normal flow of the event loop.
unsafe { RedrawWindow(window, ptr::null(), 0, RDW_INTERNALPAINT) }; // We'll buffer only in response to `UpdateWindow`, if win32 decides to redraw the
} else { // 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() {
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)), window_id: RootWindowId(WindowId(window)),
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
}); });
} }
result = ProcResult::DefWindowProc(wparam);
}
// NOTE: calling `RedrawWindow` during `WM_PAINT` does nothing, since to mark
// `WM_PAINT` as handled we should call the `DefWindowProcW`. Call it and check whether
// 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) {
unsafe { RedrawWindow(window, ptr::null(), 0, RDW_INTERNALPAINT) };
}
}
WM_WINDOWPOSCHANGING => { WM_WINDOWPOSCHANGING => {
let mut window_state = userdata.window_state_lock(); let mut window_state = userdata.window_state_lock();
if let Some(ref mut fullscreen) = window_state.fullscreen { if let Some(ref mut fullscreen) = window_state.fullscreen {

View file

@ -149,6 +149,8 @@ impl Window {
#[inline] #[inline]
pub fn request_redraw(&self) { pub fn request_redraw(&self) {
// NOTE: mark that we requested a redraw to handle requests during `WM_PAINT` handling.
self.window_state.lock().unwrap().redraw_requested = true;
unsafe { unsafe {
RedrawWindow(self.hwnd(), ptr::null(), 0, RDW_INTERNALPAINT); RedrawWindow(self.hwnd(), ptr::null(), 0, RDW_INTERNALPAINT);
} }

View file

@ -52,6 +52,9 @@ pub(crate) struct WindowState {
pub is_active: bool, pub is_active: bool,
pub is_focused: bool, pub is_focused: bool,
// Flag whether redraw was requested.
pub redraw_requested: bool,
pub dragging: bool, pub dragging: bool,
pub skip_taskbar: bool, pub skip_taskbar: bool,
@ -166,6 +169,7 @@ impl WindowState {
is_active: false, is_active: false,
is_focused: false, is_focused: false,
redraw_requested: false,
dragging: false, dragging: false,

View file

@ -585,8 +585,7 @@ impl Window {
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Windows** This API uses `RedrawWindow` to request a `WM_PAINT` message and `RedrawRequested` /// - **Windows** This API uses `RedrawWindow` to request a `WM_PAINT` message and `RedrawRequested`
/// is emitted in sync with any `WM_PAINT` messages. **Calling this method from `RedrawRequested` /// is emitted in sync with any `WM_PAINT` messages.
/// event handler won't produce a `RedrawRequested` event**.
/// - **iOS:** Can only be called on the main thread. /// - **iOS:** Can only be called on the main thread.
/// - **Wayland:** The events are aligned with the frame callbacks when [`Window::pre_present_notify`] /// - **Wayland:** The events are aligned with the frame callbacks when [`Window::pre_present_notify`]
/// is used. /// is used.