windows: Fix maximized window size when DPI scale changes (#40053)

Xiaobo Liu created

The WM_DPICHANGED suggested RECT is calculated for non-maximized
windows. When a maximized window's DPI changes, we now query the
monitor's work area directly to ensure the window correctly fills the
entire screen.

For non-maximized windows, the original behavior using the
system-suggested RECT is preserved.

Release Notes:

- windows: Fixed maximized window size when DPI scale changes

Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>

Change summary

crates/gpui/src/platform/windows/events.rs | 75 ++++++++++++++++-------
1 file changed, 51 insertions(+), 24 deletions(-)

Detailed changes

crates/gpui/src/platform/windows/events.rs 🔗

@@ -740,31 +740,58 @@ impl WindowsWindowInner {
         lock.border_offset.update(handle).log_err();
         drop(lock);
 
-        let rect = unsafe { &*(lparam.0 as *const RECT) };
-        let width = rect.right - rect.left;
-        let height = rect.bottom - rect.top;
-        // this will emit `WM_SIZE` and `WM_MOVE` right here
-        // even before this function returns
-        // the new size is handled in `WM_SIZE`
-        unsafe {
-            SetWindowPos(
-                handle,
-                None,
-                rect.left,
-                rect.top,
-                width,
-                height,
-                SWP_NOZORDER | SWP_NOACTIVATE,
-            )
-            .context("unable to set window position after dpi has changed")
-            .log_err();
-        }
-
-        // When maximized, SetWindowPos doesn't send WM_SIZE, so we need to manually
-        // update the size and call the resize callback
         if is_maximized {
-            let device_size = size(DevicePixels(width), DevicePixels(height));
-            self.handle_size_change(device_size, new_scale_factor, true);
+            // Get the monitor and its work area at the new DPI
+            let monitor = unsafe { MonitorFromWindow(handle, MONITOR_DEFAULTTONEAREST) };
+            let mut monitor_info: MONITORINFO = unsafe { std::mem::zeroed() };
+            monitor_info.cbSize = std::mem::size_of::<MONITORINFO>() as u32;
+            if unsafe { GetMonitorInfoW(monitor, &mut monitor_info) }.as_bool() {
+                let work_area = monitor_info.rcWork;
+                let width = work_area.right - work_area.left;
+                let height = work_area.bottom - work_area.top;
+
+                // Update the window size to match the new monitor work area
+                // This will trigger WM_SIZE which will handle the size change
+                unsafe {
+                    SetWindowPos(
+                        handle,
+                        None,
+                        work_area.left,
+                        work_area.top,
+                        width,
+                        height,
+                        SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED,
+                    )
+                    .context("unable to set maximized window position after dpi has changed")
+                    .log_err();
+                }
+
+                // SetWindowPos may not send WM_SIZE for maximized windows in some cases,
+                // so we manually update the size to ensure proper rendering
+                let device_size = size(DevicePixels(width), DevicePixels(height));
+                self.handle_size_change(device_size, new_scale_factor, true);
+            }
+        } else {
+            // For non-maximized windows, use the suggested RECT from the system
+            let rect = unsafe { &*(lparam.0 as *const RECT) };
+            let width = rect.right - rect.left;
+            let height = rect.bottom - rect.top;
+            // this will emit `WM_SIZE` and `WM_MOVE` right here
+            // even before this function returns
+            // the new size is handled in `WM_SIZE`
+            unsafe {
+                SetWindowPos(
+                    handle,
+                    None,
+                    rect.left,
+                    rect.top,
+                    width,
+                    height,
+                    SWP_NOZORDER | SWP_NOACTIVATE,
+                )
+                .context("unable to set window position after dpi has changed")
+                .log_err();
+            }
         }
 
         Some(0)