From aabf0e13b78e156aac3037c9051f49534195e489 Mon Sep 17 00:00:00 2001 From: Osspial Date: Tue, 20 Nov 2018 15:57:06 -0500 Subject: [PATCH] On Windows, fix window shrinking when leaving fullscreen in some situations (#718) * Fix resize border appearing in some cases after leaving fullscreen. * On fullscreen, save client rect instead of window rect * Add CHANGELOG entry * Revert test changes to fullscreen example * Update panic message when unable to get client area --- CHANGELOG.md | 1 + src/platform/windows/events_loop.rs | 2 +- src/platform/windows/util.rs | 13 ++++++ src/platform/windows/window.rs | 63 +++++++++-------------------- 4 files changed, 33 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fd80b8e..c756381c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - On Windows, catch panics in event loop child thread and forward them to the parent thread. This prevents an invocation of undefined behavior due to unwinding into foreign code. - On Windows, fix issue where resizing or moving window combined with grabbing the cursor would freeze program. - On Windows, fix issue where resizing or moving window would eat `Awakened` events. +- On Windows, exiting fullscreen after entering fullscreen with disabled decorations no longer shrinks window. - On X11, fixed a segfault when using virtual monitors with XRandR. - Derive `Ord` and `PartialOrd` for `VirtualKeyCode` enum. - On Windows, fix issue where hovering or dropping a non file item would create a panic. diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index d94b1bfc..de4a4326 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -72,7 +72,7 @@ pub struct SavedWindowInfo { /// Window ex-style pub ex_style: LONG, /// Window position and size - pub rect: RECT, + pub client_rect: RECT, // Since a window can be fullscreened to a different monitor, a DPI change can be triggered. This could result in // the window being automitcally resized to smaller/larger than it was supposed to be restored to, so we thus must // check if the post-fullscreen DPI matches the pre-fullscreen DPI. diff --git a/src/platform/windows/util.rs b/src/platform/windows/util.rs index 0d0a2efc..a9f00eca 100644 --- a/src/platform/windows/util.rs +++ b/src/platform/windows/util.rs @@ -55,6 +55,19 @@ pub fn get_window_rect(hwnd: HWND) -> Option { unsafe { status_map(|rect| winuser::GetWindowRect(hwnd, rect)) } } +pub fn get_client_rect(hwnd: HWND) -> Option { + unsafe { status_map(|rect| { + let mut top_left = mem::zeroed(); + if 0 == winuser::ClientToScreen(hwnd, &mut top_left) {return 0;}; + if 0 == winuser::GetClientRect(hwnd, rect) {return 0}; + rect.left += top_left.x; + rect.top += top_left.y; + rect.right += top_left.x; + rect.bottom += top_left.y; + 1 + }) } +} + // This won't be needed anymore if we just add a derive to winapi. pub fn rect_eq(a: &RECT, b: &RECT) -> bool { let left_eq = a.left == b.left; diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs index d7d789a1..e7727429 100644 --- a/src/platform/windows/window.rs +++ b/src/platform/windows/window.rs @@ -49,28 +49,6 @@ pub struct Window { events_loop_proxy: events_loop::EventsLoopProxy, } -// https://blogs.msdn.microsoft.com/oldnewthing/20131017-00/?p=2903 -// The idea here is that we use the Adjust­Window­Rect­Ex function to calculate how much additional -// non-client area gets added due to the styles we passed. To make the math simple, -// we ask for a zero client rectangle, so that the resulting window is all non-client. -// And then we pass in the empty rectangle represented by the dot in the middle, -// and the Adjust­Window­Rect­Ex expands the rectangle in all dimensions. -// We see that it added ten pixels to the left, right, and bottom, -// and it added fifty pixels to the top. -// From this we can perform the reverse calculation: Instead of expanding the rectangle, we shrink it. -unsafe fn unjust_window_rect(prc: &mut RECT, style: DWORD, ex_style: DWORD) -> BOOL { - let mut rc: RECT = mem::uninitialized(); - winuser::SetRectEmpty(&mut rc); - let status = winuser::AdjustWindowRectEx(&mut rc, style, 0, ex_style); - if status != 0 { - prc.left -= rc.left; - prc.top -= rc.top; - prc.right -= rc.right; - prc.bottom -= rc.bottom; - } - status -} - impl Window { pub fn new( events_loop: &EventsLoop, @@ -477,12 +455,12 @@ impl Window { unsafe fn set_fullscreen_style(&self, window_state: &mut WindowState) -> (LONG, LONG) { if window_state.fullscreen.is_none() || window_state.saved_window_info.is_none() { - let rect = util::get_window_rect(self.window.0).expect("`GetWindowRect` failed"); + let client_rect = util::get_client_rect(self.window.0).expect("client rect retrieval failed"); let dpi_factor = Some(window_state.dpi_factor); window_state.saved_window_info = Some(events_loop::SavedWindowInfo { style: winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE), ex_style: winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE), - rect, + client_rect, is_fullscreen: true, dpi_factor, }); @@ -499,7 +477,7 @@ impl Window { } unsafe fn restore_saved_window(&self, window_state_lock: &mut WindowState) { - let (rect, mut style, ex_style) = { + let (client_rect, mut style, ex_style) = { // 'saved_window_info' can be None if the window has never been // in fullscreen mode before this method gets called. if window_state_lock.saved_window_info.is_none() { @@ -513,14 +491,15 @@ impl Window { // repainted. Better-looking methods welcome. saved_window_info.is_fullscreen = false; - let rect = saved_window_info.rect.clone(); + let client_rect = saved_window_info.client_rect.clone(); let (style, ex_style) = (saved_window_info.style, saved_window_info.ex_style); - (rect, style, ex_style) + (client_rect, style, ex_style) }; let window = self.window.clone(); let window_state = Arc::clone(&self.window_state); let resizable = window_state_lock.resizable; + let decorations = window_state_lock.decorations; let maximized = window_state_lock.maximized; // We're restoring the window to its size and position from before being fullscreened. @@ -528,7 +507,7 @@ impl Window { self.events_loop_proxy.execute_in_thread(move |_| { let _ = Self::grab_cursor_inner(&window, false); - if resizable { + if resizable && decorations { style |= WS_RESIZABLE as LONG; } else { style &= !WS_RESIZABLE as LONG; @@ -536,6 +515,9 @@ impl Window { winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style); winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style); + let mut rect = client_rect; + winuser::AdjustWindowRectEx(&mut rect, style as _, 0, ex_style as _); + winuser::SetWindowPos( window.0, ptr::null_mut(), @@ -629,17 +611,14 @@ impl Window { pub fn set_decorations(&self, decorations: bool) { let mut window_state = self.window_state.lock().unwrap(); if mem::replace(&mut window_state.decorations, decorations) != decorations { - let style_flags = (winuser::WS_CAPTION | winuser::WS_THICKFRAME) as LONG; - let ex_style_flags = (winuser::WS_EX_WINDOWEDGE) as LONG; + let style_flags = (winuser::WS_CAPTION | winuser::WS_THICKFRAME) as LONG; + let ex_style_flags = (winuser::WS_EX_WINDOWEDGE) as LONG; // if we are in fullscreen mode, we only change the saved window info if window_state.fullscreen.is_some() { + let resizable = window_state.resizable; let saved = window_state.saved_window_info.as_mut().unwrap(); - unsafe { - unjust_window_rect(&mut saved.rect, saved.style as _, saved.ex_style as _); - } - if decorations { saved.style = saved.style | style_flags; saved.ex_style = saved.ex_style | ex_style_flags; @@ -647,23 +626,17 @@ impl Window { saved.style = saved.style & !style_flags; saved.ex_style = saved.ex_style & !ex_style_flags; } - - unsafe { - winuser::AdjustWindowRectEx( - &mut saved.rect, - saved.style as _, - 0, - saved.ex_style as _, - ); + if resizable { + saved.style |= WS_RESIZABLE as LONG; + } else { + saved.style &= !WS_RESIZABLE as LONG; } } else { unsafe { - let mut rect: RECT = mem::zeroed(); - winuser::GetWindowRect(self.window.0, &mut rect); + let mut rect = util::get_client_rect(self.window.0).expect("Get client rect failed!"); let mut style = winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE); let mut ex_style = winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE); - unjust_window_rect(&mut rect, style as _, ex_style as _); if decorations { style = style | style_flags;