From ef16713025a10d6df4a0cc8c72fb6575d1a28ebe Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 2 Jun 2019 15:59:36 -0400 Subject: [PATCH 1/2] Prevent event loop pause while a caption button is pressed --- src/platform_impl/windows/event_loop.rs | 56 ++++++++++++++++++++----- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 3eac291099..377b33957b 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -833,17 +833,53 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_NCLBUTTONDOWN => { - // jumpstart the modal loop - winuser::RedrawWindow( - window, - ptr::null(), - ptr::null_mut(), - winuser::RDW_INTERNALPAINT, - ); - if wparam == winuser::HTCAPTION as _ { - winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, 0); + match wparam as isize { + winuser::HTMAXBUTTON | winuser::HTMINBUTTON | winuser::HTCLOSE => { + // Discard the non-client button-down event and defer the processing until + // the non-client button-up event. This prevents a pause in the message loop + // while the user has the mouse button pressed on one of these buttons. + 0 + } + _ => { + // jumpstart the modal loop + winuser::RedrawWindow( + window, + ptr::null(), + ptr::null_mut(), + winuser::RDW_INTERNALPAINT, + ); + if wparam == winuser::HTCAPTION as _ { + winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, 0); + } + commctrl::DefSubclassProc(window, msg, wparam, lparam) + } + } + } + + winuser::WM_NCLBUTTONUP => { + // Manually apply min, max, restore, and close since we discarded the button down event. + match wparam as isize { + winuser::HTMAXBUTTON => { + let window_state = subclass_input.window_state.lock(); + let window_flags = window_state.window_flags(); + drop(window_state); + if window_flags.contains(WindowFlags::MAXIMIZED) { + winuser::ShowWindow(window, winuser::SW_RESTORE); + } else { + winuser::ShowWindow(window, winuser::SW_MAXIMIZE); + } + 0 + } + winuser::HTMINBUTTON => { + winuser::ShowWindow(window, winuser::SW_MINIMIZE); + 0 + } + winuser::HTCLOSE => { + winuser::PostMessageW(window, winuser::WM_CLOSE, 0, 0); + 0 + } + _ => commctrl::DefSubclassProc(window, msg, wparam, lparam), } - commctrl::DefSubclassProc(window, msg, wparam, lparam) } winuser::WM_CLOSE => { From 6394fb46cc63ab5cbd6617b3551c90eeb221ca87 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Mon, 3 Jun 2019 18:20:46 -0400 Subject: [PATCH 2/2] Check the deferred caption button before execution. The user may cancel the button press on a caption button by moving the mouse away before releasing. --- src/platform_impl/windows/event_loop.rs | 27 ++++++++++++++++++----- src/platform_impl/windows/window_state.rs | 4 +++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 377b33957b..27e760d6d4 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -838,6 +838,8 @@ unsafe extern "system" fn public_window_callback( // Discard the non-client button-down event and defer the processing until // the non-client button-up event. This prevents a pause in the message loop // while the user has the mouse button pressed on one of these buttons. + let mut window_state = subclass_input.window_state.lock(); + window_state.deferred_nclbuttondown_wparam = Some(wparam); 0 } _ => { @@ -857,9 +859,18 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_NCLBUTTONUP => { - // Manually apply min, max, restore, and close since we discarded the button down event. - match wparam as isize { - winuser::HTMAXBUTTON => { + let deferred_wparam = subclass_input + .window_state + .lock() + .deferred_nclbuttondown_wparam + .filter(|deferred_wparam| *deferred_wparam == wparam) + .map(|wparam| wparam as isize) + .take(); + // Manually apply min, max, restore, and close since we discarded the button down event, + // but only in the event that the user did not cancel the operation. If the user canelled + // the operation, WM_LBUTTONUP will fire and we'll clear the deferred state there as well. + match deferred_wparam { + Some(winuser::HTMAXBUTTON) => { let window_state = subclass_input.window_state.lock(); let window_flags = window_state.window_flags(); drop(window_state); @@ -870,11 +881,11 @@ unsafe extern "system" fn public_window_callback( } 0 } - winuser::HTMINBUTTON => { + Some(winuser::HTMINBUTTON) => { winuser::ShowWindow(window, winuser::SW_MINIMIZE); 0 } - winuser::HTCLOSE => { + Some(winuser::HTCLOSE) => { winuser::PostMessageW(window, winuser::WM_CLOSE, 0, 0); 0 } @@ -1185,6 +1196,12 @@ unsafe extern "system" fn public_window_callback( ElementState::Released, MouseButton::Left, WindowEvent::MouseInput, }; + subclass_input + .window_state + .lock() + .deferred_nclbuttondown_wparam + .take(); + release_mouse(&mut *subclass_input.window_state.lock()); subclass_input.send_event(Event::WindowEvent { diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 14cc71445d..b7201a2753 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -8,7 +8,7 @@ use parking_lot::MutexGuard; use std::{io, ptr}; use winapi::{ shared::{ - minwindef::DWORD, + minwindef::{DWORD, WPARAM}, windef::{HWND, RECT}, }, um::winuser, @@ -30,6 +30,7 @@ pub struct WindowState { pub dpi_factor: f64, pub fullscreen: Option, + pub deferred_nclbuttondown_wparam: Option, window_flags: WindowFlags, } @@ -111,6 +112,7 @@ impl WindowState { fullscreen: None, window_flags: WindowFlags::empty(), + deferred_nclbuttondown_wparam: None, } }