diff --git a/CHANGELOG.md b/CHANGELOG.md index f77c7da9..78882ff6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and `WindowEvent::HoveredFile`. - Revert the use of invisible surfaces in Wayland, which introduced graphical glitches with OpenGL (#835) - On X11, implement `_NET_WM_PING` to allow desktop environment to kill unresponsive programs. - On Windows, when a window is initially invisible, it won't take focus from the existing visible windows. +- On Windows, fix multiple calls to `request_redraw` during `EventsCleared` sending multiple `RedrawRequested events.` +- On Windows, fix edge case where `RedrawRequested` could be dispatched before input events in event loop iteration. +- On Windows, fix timing issue that could cause events to be improperly dispatched after `RedrawRequested` but before `EventsCleared`. - On macOS, drop unused Metal dependency. # 0.20.0 Alpha 1 diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 3eac2910..b53d3add 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -195,6 +195,7 @@ impl EventLoop { } winuser::TranslateMessage(&mut msg); winuser::DispatchMessageW(&mut msg); + msg_unprocessed = false; } runner!().events_cleared(); @@ -202,19 +203,21 @@ impl EventLoop { panic::resume_unwind(payload); } - let control_flow = runner!().control_flow; - match control_flow { - ControlFlow::Exit => break 'main, - ControlFlow::Wait => { - if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) { - break 'main; + if !msg_unprocessed { + let control_flow = runner!().control_flow; + match control_flow { + ControlFlow::Exit => break 'main, + ControlFlow::Wait => { + if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) { + break 'main; + } + msg_unprocessed = true; } - msg_unprocessed = true; + ControlFlow::WaitUntil(resume_time) => { + wait_until_time_or_msg(resume_time); + } + ControlFlow::Poll => (), } - ControlFlow::WaitUntil(resume_time) => { - wait_until_time_or_msg(resume_time); - } - ControlFlow::Poll => (), } } } @@ -253,6 +256,7 @@ pub(crate) struct EventLoopRunner { runner_state: RunnerState, modal_redraw_window: HWND, in_modal_loop: bool, + in_repaint: bool, event_handler: Box, &mut ControlFlow)>, panic_error: Option, } @@ -316,6 +320,7 @@ impl EventLoopRunner { control_flow: ControlFlow::default(), runner_state: RunnerState::New, in_modal_loop: false, + in_repaint: false, modal_redraw_window: event_loop.window_target.p.thread_msg_target, event_handler: mem::transmute::< Box, &mut ControlFlow)>, @@ -429,10 +434,26 @@ impl EventLoopRunner { } self.runner_state = RunnerState::HandlingEvents; - self.call_event_handler(event); + match (self.in_repaint, &event) { + ( + true, + Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + }, + ) + | (false, _) => self.call_event_handler(event), + (true, _) => { + self.events_cleared(); + self.new_events(); + self.process_event(event); + } + } } fn events_cleared(&mut self) { + self.in_repaint = false; + match self.runner_state { // If we were handling events, send the EventsCleared message. RunnerState::HandlingEvents => { @@ -483,6 +504,10 @@ impl EventLoopRunner { Event::EventsCleared => self .trigger_newevents_on_redraw .store(false, Ordering::Relaxed), + Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + } => self.in_repaint = true, _ => (), } @@ -871,12 +896,12 @@ unsafe extern "system" fn public_window_callback( _ if msg == *REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID => { use crate::event::WindowEvent::RedrawRequested; let mut runner = subclass_input.event_loop_runner.runner.borrow_mut(); + subclass_input.window_state.lock().queued_out_of_band_redraw = false; if let Some(ref mut runner) = *runner { // This check makes sure that calls to `request_redraw()` during `EventsCleared` // handling dispatch `RedrawRequested` immediately after `EventsCleared`, without // spinning up a new event loop iteration. We do this because that's what the API // says to do. - let control_flow = runner.control_flow; let runner_state = runner.runner_state; let mut request_redraw = || { runner.call_event_handler(Event::WindowEvent { @@ -886,15 +911,14 @@ unsafe extern "system" fn public_window_callback( }; match runner_state { RunnerState::Idle(..) | RunnerState::DeferredNewEvents(..) => request_redraw(), - RunnerState::HandlingEvents => match control_flow { - ControlFlow::Poll => request_redraw(), - ControlFlow::WaitUntil(resume_time) => { - if resume_time <= Instant::now() { - request_redraw() - } - } - _ => (), - }, + RunnerState::HandlingEvents => { + winuser::RedrawWindow( + window, + ptr::null(), + ptr::null_mut(), + winuser::RDW_INTERNALPAINT, + ); + } _ => (), } } diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 4e67d5c4..577ecfd7 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -149,7 +149,11 @@ impl Window { winuser::RDW_INTERNALPAINT, ); } else { - winuser::PostMessageW(self.window.0, *REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID, 0, 0); + let mut window_state = self.window_state.lock(); + if !window_state.queued_out_of_band_redraw { + window_state.queued_out_of_band_redraw = true; + winuser::PostMessageW(self.window.0, *REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID, 0, 0); + } } } } diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 14cc7144..8d807b54 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -30,6 +30,9 @@ pub struct WindowState { pub dpi_factor: f64, pub fullscreen: Option, + /// Used to supress duplicate redraw attempts when calling `request_redraw` multiple + /// times in `EventsCleared`. + pub queued_out_of_band_redraw: bool, window_flags: WindowFlags, } @@ -110,6 +113,7 @@ impl WindowState { dpi_factor, fullscreen: None, + queued_out_of_band_redraw: false, window_flags: WindowFlags::empty(), } }