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:
parent
ae28eea406
commit
2b4e8ef916
4 changed files with 82 additions and 0 deletions
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue