Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

On Windows, fix window size for maximized, undecorated windows #2584

Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre

# Unreleased

- On Windows, fix window size for maximized, undecorated windows.
- On macOS, run most actions on the main thread, which is strictly more correct, but might make multithreaded applications block slightly more.
- On macOS, fix panic when getting current monitor without any monitor attached.
- On Windows and MacOS, add API to enable/disable window buttons (close, minimize, ...etc).
Expand Down
34 changes: 21 additions & 13 deletions src/platform_impl/windows/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -978,19 +978,27 @@ unsafe fn public_window_callback_inner<T: 'static>(
return DefWindowProcW(window, msg, wparam, lparam);
}

// Extend the client area to cover the whole non-client area.
// https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize#remarks
//
// HACK(msiglreith): To add the drop shadow we slightly tweak the non-client area.
// This leads to a small black 1px border on the top. Adding a margin manually
// on all 4 borders would result in the caption getting drawn by the DWM.
//
// Another option would be to allow the DWM to paint inside the client area.
// Unfortunately this results in janky resize behavior, where the compositor is
// ahead of the window surface. Currently, there seems no option to achieve this
// with the Windows API.
if window_flags.contains(WindowFlags::MARKER_UNDECORATED_SHADOW) {
let params = &mut *(lparam as *mut NCCALCSIZE_PARAMS);
let params = &mut *(lparam as *mut NCCALCSIZE_PARAMS);

if util::is_maximized(window) {
// Limit the window size when maximized to the current monitor.
// Otherwise it would include the non-existent decorations.
let monitor = monitor::current_monitor(window);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using monitor::current_monitor which uses MonitorFromWindow and MONITOR_DEFAULTTONEAREST is unreliable in this situation, see MicrosoftEdge/WebView2Feedback#2549 (comment), instead, MonitorFromRect should be used

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for mentioning this! I will adjust it accordingly

if let Ok(monitor_info) = monitor::get_monitor_info(monitor.hmonitor()) {
params.rgrc[0] = monitor_info.monitorInfo.rcWork;
}
} else if window_flags.contains(WindowFlags::MARKER_UNDECORATED_SHADOW) {
// Extend the client area to cover the whole non-client area.
// https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize#remarks
//
// HACK(msiglreith): To add the drop shadow we slightly tweak the non-client area.
// This leads to a small black 1px border on the top. Adding a margin manually
// on all 4 borders would result in the caption getting drawn by the DWM.
//
// Another option would be to allow the DWM to paint inside the client area.
// Unfortunately this results in janky resize behavior, where the compositor is
// ahead of the window surface. Currently, there seems no option to achieve this
// with the Windows API.
params.rgrc[0].top += 1;
params.rgrc[0].bottom += 1;
}
Expand Down
18 changes: 14 additions & 4 deletions src/platform_impl/windows/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ use windows_sys::{
HiDpi::{DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS},
Input::KeyboardAndMouse::GetActiveWindow,
WindowsAndMessaging::{
ClipCursor, GetClientRect, GetClipCursor, GetSystemMetrics, GetWindowRect,
ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM,
IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE, IDC_WAIT,
SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN,
ClipCursor, GetClientRect, GetClipCursor, GetSystemMetrics, GetWindowPlacement,
GetWindowRect, ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND,
IDC_HELP, IDC_IBEAM, IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE,
IDC_SIZEWE, IDC_WAIT, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN,
SM_YVIRTUALSCREEN, SW_MAXIMIZE, WINDOWPLACEMENT,
},
},
},
Expand Down Expand Up @@ -90,6 +91,15 @@ impl WindowArea {
}
}

pub fn is_maximized(window: HWND) -> bool {
unsafe {
let mut placement: WINDOWPLACEMENT = mem::zeroed();
placement.length = mem::size_of::<WINDOWPLACEMENT>() as u32;
GetWindowPlacement(window, &mut placement);
placement.showCmd == SW_MAXIMIZE
}
}

pub fn set_cursor_hidden(hidden: bool) {
static HIDDEN: AtomicBool = AtomicBool::new(false);
let changed = HIDDEN.swap(hidden, Ordering::SeqCst) ^ hidden;
Expand Down