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
This commit is contained in:
Osspial 2018-11-20 15:57:06 -05:00 committed by Francesca Plebani
parent 92873b06ed
commit aabf0e13b7
4 changed files with 33 additions and 46 deletions

View file

@ -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.

View file

@ -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.

View file

@ -55,6 +55,19 @@ pub fn get_window_rect(hwnd: HWND) -> Option<RECT> {
unsafe { status_map(|rect| winuser::GetWindowRect(hwnd, rect)) }
}
pub fn get_client_rect(hwnd: HWND) -> Option<RECT> {
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;

View file

@ -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;