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 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 Windows, fix `RedrawRequested` not being delivered when calling `Window::request_redraw` from `RedrawRequested`.
# 0.29.1-beta

View file

@ -1168,19 +1168,28 @@ unsafe fn public_window_callback_inner<T: 'static>(
}
WM_PAINT => {
if userdata.event_loop_runner.should_buffer() {
// this branch can happen in response to `UpdateWindow`, if win32 decides to
// redraw the window outside the normal flow of the event loop.
unsafe { RedrawWindow(window, ptr::null(), 0, RDW_INTERNALPAINT) };
} else {
userdata.window_state_lock().redraw_requested =
userdata.event_loop_runner.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() {
userdata.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
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 => {
let mut window_state = userdata.window_state_lock();
if let Some(ref mut fullscreen) = window_state.fullscreen {

View file

@ -149,6 +149,8 @@ impl Window {
#[inline]
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 {
RedrawWindow(self.hwnd(), ptr::null(), 0, RDW_INTERNALPAINT);
}

View file

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

View file

@ -585,8 +585,7 @@ impl Window {
/// ## Platform-specific
///
/// - **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`
/// event handler won't produce a `RedrawRequested` event**.
/// is emitted in sync with any `WM_PAINT` messages.
/// - **iOS:** Can only be called on the main thread.
/// - **Wayland:** The events are aligned with the frame callbacks when [`Window::pre_present_notify`]
/// is used.