Fix a pause in the event loop when clicking the title bar on windows (#4136)

* Fix a pause in the event loop when clicking the title bar on windows

When clicking the title bar on Windows, to drag the window, there is
a noticible pause in continuous redraw requests. This was fixed
in #839 and then regressed in #1852. The cursor blinks in both
cases and is unrelated. The regression made the blink happen after
the pause instead of immediately.

* Update the event loop pause note on the WM_NCLBUTTONDOWN handler

The application example was also updated to optionally animate the fill color
in order to demonstrate continuous redraw without pauses in the event
loop.
This commit is contained in:
aloucks 2025-03-16 21:27:27 -04:00 committed by GitHub
parent ae28eea406
commit 2b4e8ef916
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 82 additions and 0 deletions

View file

@ -42,6 +42,9 @@ use winit::window::{
#[path = "util/tracing.rs"]
mod tracing;
#[path = "util/fill.rs"]
mod fill;
/// The amount of points to around the window for drag resize direction calculations.
const BORDER_SIZE: f64 = 20.;
@ -314,6 +317,13 @@ impl Application {
self.sender.send(Action::Message).unwrap();
event_loop.create_proxy().wake_up();
},
Action::ToggleAnimatedFillColor => {
window.animated_fill_color = !window.animated_fill_color;
},
Action::ToggleContinuousRedraw => {
window.continuous_redraw = !window.continuous_redraw;
window.window.request_redraw();
},
}
}
@ -441,6 +451,9 @@ impl ApplicationHandler for Application {
if let Err(err) = window.draw() {
error!("Error drawing window: {err}");
}
if window.continuous_redraw {
window.window.request_redraw();
}
},
WindowEvent::Occluded(occluded) => {
window.set_occluded(occluded);
@ -616,6 +629,13 @@ struct WindowState {
window: Arc<dyn Window>,
/// The window theme we're drawing with.
theme: Theme,
/// Fill the window with animated color
animated_fill_color: bool,
/// The application start time. Used for color fill animation
#[cfg(not(android_platform))]
start_time: std::time::Instant,
/// Redraw continuously
continuous_redraw: bool,
/// Cursor position over the window.
cursor_position: Option<PhysicalPosition<f64>>,
/// Window modifiers state.
@ -669,6 +689,10 @@ impl WindowState {
surface,
window,
theme,
animated_fill_color: false,
continuous_redraw: false,
#[cfg(not(android_platform))]
start_time: std::time::Instant::now(),
ime,
cursor_position: Default::default(),
cursor_hidden: Default::default(),
@ -947,6 +971,11 @@ impl WindowState {
return Ok(());
}
if self.animated_fill_color {
fill::fill_window_with_animated_color(&*self.window, self.start_time);
return Ok(());
}
let mut buffer = self.surface.buffer_mut()?;
// Draw a different color inside the safe area
@ -1038,6 +1067,8 @@ enum Action {
RequestResize,
DumpMonitors,
Message,
ToggleAnimatedFillColor,
ToggleContinuousRedraw,
}
impl Action {
@ -1082,6 +1113,8 @@ impl Action {
information"
},
Action::Message => "Prints a message through a user wake up",
Action::ToggleAnimatedFillColor => "Toggle animated fill color",
Action::ToggleContinuousRedraw => "Toggle continuous redraw",
}
}
}
@ -1196,6 +1229,7 @@ const CURSORS: &[CursorIcon] = &[
const KEY_BINDINGS: &[Binding<&'static str>] = &[
Binding::new("Q", ModifiersState::CONTROL, Action::CloseWindow),
Binding::new("H", ModifiersState::CONTROL, Action::PrintHelp),
Binding::new("F", ModifiersState::SHIFT, Action::ToggleAnimatedFillColor),
Binding::new("F", ModifiersState::CONTROL, Action::ToggleFullscreen),
#[cfg(macos_platform)]
Binding::new("F", ModifiersState::ALT, Action::ToggleSimpleFullscreen),
@ -1205,6 +1239,7 @@ const KEY_BINDINGS: &[Binding<&'static str>] = &[
Binding::new("P", ModifiersState::CONTROL, Action::ToggleResizeIncrements),
Binding::new("R", ModifiersState::CONTROL, Action::ToggleResizable),
Binding::new("R", ModifiersState::ALT, Action::RequestResize),
Binding::new("R", ModifiersState::SHIFT, Action::ToggleContinuousRedraw),
// M.
Binding::new("M", ModifiersState::CONTROL.union(ModifiersState::ALT), Action::DumpMonitors),
Binding::new("M", ModifiersState::CONTROL, Action::ToggleMaximize),

View file

@ -12,6 +12,8 @@ pub use platform::cleanup_window;
#[allow(unused_imports)]
pub use platform::fill_window;
#[allow(unused_imports)]
pub use platform::fill_window_with_animated_color;
#[allow(unused_imports)]
pub use platform::fill_window_with_color;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
@ -102,6 +104,16 @@ mod platform {
fill_window_with_color(window, 0xff181818);
}
#[allow(dead_code)]
pub fn fill_window_with_animated_color(window: &dyn Window, start: std::time::Instant) {
let time = start.elapsed().as_secs_f32() * 1.5;
let blue = (time.sin() * 255.0) as u32;
let green = ((time.cos() * 255.0) as u32) << 8;
let red = ((1.0 - time.sin() * 255.0) as u32) << 16;
let color = red | green | blue;
fill_window_with_color(window, color);
}
#[allow(dead_code)]
pub fn cleanup_window(window: &dyn Window) {
GC.with(|gc| {
@ -115,6 +127,7 @@ mod platform {
#[cfg(any(target_os = "android", target_os = "ios"))]
mod platform {
#[allow(dead_code)]
pub fn fill_window(_window: &dyn winit::window::Window) {
// No-op on mobile platforms.
}
@ -124,6 +137,14 @@ mod platform {
// No-op on mobile platforms.
}
#[allow(dead_code)]
pub fn fill_window_with_animated_color(
_window: &dyn winit::window::Window,
_start: std::time::Instant,
) {
// No-op on mobile platforms.
}
#[allow(dead_code)]
pub fn cleanup_window(_window: &dyn winit::window::Window) {
// No-op on mobile platforms.

View file

@ -236,3 +236,4 @@ changelog entry.
- On macOS, fixed the scancode conversion for audio volume keys.
- On macOS, fixed the scancode conversion for `IntlBackslash`.
- On macOS, fixed redundant `SurfaceResized` event at window creation.
- On Windows, fixed ~500 ms pause when clicking the title bar during continuous redraw.

View file

@ -1135,6 +1135,31 @@ unsafe fn public_window_callback_inner(
WM_NCLBUTTONDOWN => {
if wparam == HTCAPTION as _ {
// Prevent the user event loop from pausing when left clicking the title bar.
//
// When the user interacts with the title bar, Windows enters the modal event
// loop. Currently, a left click causes a pause for about 500ms. Sending a dummy
// mouse-move event seems to cancel the modal loop early, preventing the pause.
// The application will never see this dummy event.
//
// The mouse coordinates are encoded into the lparam value, however the WM_MOUSEMOVE
// event is not using the same coordinate system of the WM_NCLBUTTONDOWN event.
// One uses client-area coordinates and the other is screen-coordinates. In any
// case, passing the lparam as-is with the dummy event does not seem the cancel
// the modal loop.
//
// However, passing in a value of 0 has been observed to always cancel the pause.
//
// Other notes:
//
// For some unknown reason, the cursor will blink when clicking the title bar.
// Cancelling the modal loop early causes the blink to happen *immediately*.
// Otherwise, the blank happens *after* the pause.
//
// When right-click the title bar, the system window menu is presented to the user,
// and the modal event loop begins. This dummy event does *not* prevent the freeze
// in the main event loop caused by that popup menu.
let lparam = 0;
unsafe { PostMessageW(window, WM_MOUSEMOVE, 0, lparam) };
}
result = ProcResult::DefWindowProc(wparam);