windows: Fix regression introduced by a prev PR (#13090)

张小白 created

Fix regression introduced by #12991 

### Before

The re-position and re-size of a window is broken.


https://github.com/zed-industries/zed/assets/14981363/d4fb9dce-707e-4ab1-9ff5-f355b7fdd8a8

### After



https://github.com/zed-industries/zed/assets/14981363/7fd232e6-ff6c-4b7f-ad32-c284acd4f6db




Release Notes:

- N/A

Change summary

crates/gpui/src/platform/windows/display.rs | 117 +++++++++++++++-------
crates/gpui/src/platform/windows/events.rs  |  19 ++-
crates/gpui/src/platform/windows/util.rs    |   8 -
crates/gpui/src/platform/windows/window.rs  |  77 ++++++++-------
4 files changed, 132 insertions(+), 89 deletions(-)

Detailed changes

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

@@ -5,42 +5,55 @@ use util::ResultExt;
 use uuid::Uuid;
 use windows::{
     core::*,
-    Win32::{Foundation::*, Graphics::Gdi::*},
+    Win32::{
+        Foundation::*,
+        Graphics::Gdi::*,
+        UI::{
+            HiDpi::{GetDpiForMonitor, MDT_EFFECTIVE_DPI},
+            WindowsAndMessaging::USER_DEFAULT_SCREEN_DPI,
+        },
+    },
 };
 
-use crate::{px, Bounds, DisplayId, Pixels, PlatformDisplay, Point, Size};
+use crate::{logical_point, point, size, Bounds, DevicePixels, DisplayId, Pixels, PlatformDisplay};
 
 #[derive(Debug, Clone, Copy)]
 pub(crate) struct WindowsDisplay {
     pub handle: HMONITOR,
     pub display_id: DisplayId,
+    scale_factor: f32,
     bounds: Bounds<Pixels>,
+    physical_bounds: Bounds<DevicePixels>,
     uuid: Uuid,
 }
 
 impl WindowsDisplay {
     pub(crate) fn new(display_id: DisplayId) -> Option<Self> {
-        let Some(screen) = available_monitors().into_iter().nth(display_id.0 as _) else {
-            return None;
-        };
-        let Ok(info) = get_monitor_info(screen).inspect_err(|e| log::error!("{}", e)) else {
-            return None;
-        };
-        let size = info.monitorInfo.rcMonitor;
+        let screen = available_monitors().into_iter().nth(display_id.0 as _)?;
+        let info = get_monitor_info(screen).log_err()?;
+        let monitor_size = info.monitorInfo.rcMonitor;
         let uuid = generate_uuid(&info.szDevice);
+        let scale_factor = get_scale_factor_for_monitor(screen).log_err()?;
+        let physical_size = size(
+            (monitor_size.right - monitor_size.left).into(),
+            (monitor_size.bottom - monitor_size.top).into(),
+        );
 
         Some(WindowsDisplay {
             handle: screen,
             display_id,
+            scale_factor,
             bounds: Bounds {
-                origin: Point {
-                    x: px(size.left as f32),
-                    y: px(size.top as f32),
-                },
-                size: Size {
-                    width: px((size.right - size.left) as f32),
-                    height: px((size.bottom - size.top) as f32),
-                },
+                origin: logical_point(
+                    monitor_size.left as f32,
+                    monitor_size.top as f32,
+                    scale_factor,
+                ),
+                size: physical_size.to_pixels(scale_factor),
+            },
+            physical_bounds: Bounds {
+                origin: point(monitor_size.left.into(), monitor_size.top.into()),
+                size: physical_size,
             },
             uuid,
         })
@@ -48,25 +61,34 @@ impl WindowsDisplay {
 
     pub fn new_with_handle(monitor: HMONITOR) -> Self {
         let info = get_monitor_info(monitor).expect("unable to get monitor info");
-        let size = info.monitorInfo.rcMonitor;
+        let monitor_size = info.monitorInfo.rcMonitor;
         let uuid = generate_uuid(&info.szDevice);
         let display_id = available_monitors()
             .iter()
             .position(|handle| handle.0 == monitor.0)
             .unwrap();
+        let scale_factor =
+            get_scale_factor_for_monitor(monitor).expect("unable to get scale factor for monitor");
+        let physical_size = size(
+            (monitor_size.right - monitor_size.left).into(),
+            (monitor_size.bottom - monitor_size.top).into(),
+        );
 
         WindowsDisplay {
             handle: monitor,
             display_id: DisplayId(display_id as _),
+            scale_factor,
             bounds: Bounds {
-                origin: Point {
-                    x: px(size.left as f32),
-                    y: px(size.top as f32),
-                },
-                size: Size {
-                    width: px((size.right - size.left) as f32),
-                    height: px((size.bottom - size.top) as f32),
-                },
+                origin: logical_point(
+                    monitor_size.left as f32,
+                    monitor_size.top as f32,
+                    scale_factor,
+                ),
+                size: physical_size.to_pixels(scale_factor),
+            },
+            physical_bounds: Bounds {
+                origin: point(monitor_size.left.into(), monitor_size.top.into()),
+                size: physical_size,
             },
             uuid,
         }
@@ -74,21 +96,30 @@ impl WindowsDisplay {
 
     fn new_with_handle_and_id(handle: HMONITOR, display_id: DisplayId) -> Self {
         let info = get_monitor_info(handle).expect("unable to get monitor info");
-        let size = info.monitorInfo.rcMonitor;
+        let monitor_size = info.monitorInfo.rcMonitor;
         let uuid = generate_uuid(&info.szDevice);
+        let scale_factor =
+            get_scale_factor_for_monitor(handle).expect("unable to get scale factor for monitor");
+        let physical_size = size(
+            (monitor_size.right - monitor_size.left).into(),
+            (monitor_size.bottom - monitor_size.top).into(),
+        );
 
         WindowsDisplay {
             handle,
             display_id,
+            scale_factor,
             bounds: Bounds {
-                origin: Point {
-                    x: px(size.left as f32),
-                    y: px(size.top as f32),
-                },
-                size: Size {
-                    width: px((size.right - size.left) as f32),
-                    height: px((size.bottom - size.top) as f32),
-                },
+                origin: logical_point(
+                    monitor_size.left as f32,
+                    monitor_size.top as f32,
+                    scale_factor,
+                ),
+                size: physical_size.to_pixels(scale_factor),
+            },
+            physical_bounds: Bounds {
+                origin: point(monitor_size.left.into(), monitor_size.top.into()),
+                size: physical_size,
             },
             uuid,
         }
@@ -112,8 +143,8 @@ impl WindowsDisplay {
     pub fn check_given_bounds(&self, bounds: Bounds<Pixels>) -> bool {
         let center = bounds.center();
         let center = POINT {
-            x: center.x.0 as i32,
-            y: center.y.0 as i32,
+            x: (center.x.0 * self.scale_factor) as i32,
+            y: (center.y.0 * self.scale_factor) as i32,
         };
         let monitor = unsafe { MonitorFromPoint(center, MONITOR_DEFAULTTONULL) };
         if monitor.is_invalid() {
@@ -156,6 +187,10 @@ impl WindowsDisplay {
     pub fn is_connected(hmonitor: HMONITOR) -> bool {
         available_monitors().iter().contains(&hmonitor)
     }
+
+    pub fn physical_bounds(&self) -> Bounds<DevicePixels> {
+        self.physical_bounds
+    }
 }
 
 impl PlatformDisplay for WindowsDisplay {
@@ -221,3 +256,11 @@ fn generate_uuid(device_name: &[u16]) -> Uuid {
         .collect_vec();
     Uuid::new_v5(&Uuid::NAMESPACE_DNS, &name)
 }
+
+fn get_scale_factor_for_monitor(monitor: HMONITOR) -> Result<f32> {
+    let mut dpi_x = 0;
+    let mut dpi_y = 0;
+    unsafe { GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) }?;
+    assert_eq!(dpi_x, dpi_y);
+    Ok(dpi_x as f32 / USER_DEFAULT_SCREEN_DPI as f32)
+}

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

@@ -98,13 +98,16 @@ fn handle_move_msg(
     lparam: LPARAM,
     state_ptr: Rc<WindowsWindowStatePtr>,
 ) -> Option<isize> {
-    let x = lparam.signed_loword() as f32;
-    let y = lparam.signed_hiword() as f32;
     let mut lock = state_ptr.state.borrow_mut();
-    lock.origin = point(px(x), px(y));
-    let size = lock.physical_size;
-    let center_x = x + size.width.0 / 2.;
-    let center_y = y + size.height.0 / 2.;
+    let origin = logical_point(
+        lparam.signed_loword() as f32,
+        lparam.signed_hiword() as f32,
+        lock.scale_factor,
+    );
+    lock.origin = origin;
+    let size = lock.logical_size;
+    let center_x = origin.x.0 + size.width.0 / 2.;
+    let center_y = origin.y.0 + size.height.0 / 2.;
     let monitor_bounds = lock.display.bounds();
     if center_x < monitor_bounds.left().0
         || center_x > monitor_bounds.right().0
@@ -135,8 +138,8 @@ fn handle_size_msg(lparam: LPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -> Opti
     let new_size = size(DevicePixels(width), DevicePixels(height));
     let scale_factor = lock.scale_factor;
     lock.renderer.update_drawable_size(new_size);
-    let new_size = new_size.to_pixels(lock.scale_factor);
-    lock.physical_size = new_size;
+    let new_size = new_size.to_pixels(scale_factor);
+    lock.logical_size = new_size;
     if let Some(mut callback) = lock.callbacks.resize.take() {
         drop(lock);
         callback(new_size, scale_factor);

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

@@ -111,14 +111,6 @@ pub(crate) fn load_cursor(style: CursorStyle) -> HCURSOR {
     })
 }
 
-#[inline]
-pub(crate) fn logical_size(physical_size: Size<DevicePixels>, scale_factor: f32) -> Size<Pixels> {
-    Size {
-        width: px(physical_size.width.0 as f32 / scale_factor),
-        height: px(physical_size.height.0 as f32 / scale_factor),
-    }
-}
-
 #[inline]
 pub(crate) fn logical_point(x: f32, y: f32, scale_factor: f32) -> Point<Pixels> {
     Point {

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

@@ -27,13 +27,13 @@ use windows::{
 };
 
 use crate::platform::blade::BladeRenderer;
-use crate::{Pixels, *};
+use crate::*;
 
 pub(crate) struct WindowsWindow(pub Rc<WindowsWindowStatePtr>);
 
 pub struct WindowsWindowState {
     pub origin: Point<Pixels>,
-    pub physical_size: Size<Pixels>,
+    pub logical_size: Size<Pixels>,
     pub fullscreen_restore_bounds: Bounds<Pixels>,
     pub scale_factor: f32,
 
@@ -68,16 +68,19 @@ impl WindowsWindowState {
         current_cursor: HCURSOR,
         display: WindowsDisplay,
     ) -> Self {
-        let origin = point(px(cs.x as f32), px(cs.y as f32));
-        let physical_size = size(px(cs.cx as f32), px(cs.cy as f32));
-        let fullscreen_restore_bounds = Bounds {
-            origin,
-            size: physical_size,
-        };
         let scale_factor = {
             let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32;
             monitor_dpi / USER_DEFAULT_SCREEN_DPI as f32
         };
+        let origin = logical_point(cs.x as f32, cs.y as f32, scale_factor);
+        let logical_size = {
+            let physical_size = size(DevicePixels(cs.cx), DevicePixels(cs.cy));
+            physical_size.to_pixels(scale_factor)
+        };
+        let fullscreen_restore_bounds = Bounds {
+            origin,
+            size: logical_size,
+        };
         let renderer = windows_renderer::windows_renderer(hwnd, transparent);
         let callbacks = Callbacks::default();
         let input_handler = None;
@@ -88,7 +91,7 @@ impl WindowsWindowState {
 
         Self {
             origin,
-            physical_size,
+            logical_size,
             fullscreen_restore_bounds,
             scale_factor,
             callbacks,
@@ -116,7 +119,7 @@ impl WindowsWindowState {
     fn bounds(&self) -> Bounds<Pixels> {
         Bounds {
             origin: self.origin,
-            size: self.physical_size,
+            size: self.logical_size,
         }
     }
 
@@ -129,15 +132,17 @@ impl WindowsWindowState {
             GetWindowPlacement(self.hwnd, &mut placement).log_err();
             placement
         };
+        let physical_size = size(
+            DevicePixels(placement.rcNormalPosition.right - placement.rcNormalPosition.left),
+            DevicePixels(placement.rcNormalPosition.bottom - placement.rcNormalPosition.top),
+        );
         let bounds = Bounds {
-            origin: point(
-                px(placement.rcNormalPosition.left as f32),
-                px(placement.rcNormalPosition.top as f32),
-            ),
-            size: size(
-                px((placement.rcNormalPosition.right - placement.rcNormalPosition.left) as f32),
-                px((placement.rcNormalPosition.bottom - placement.rcNormalPosition.top) as f32),
+            origin: logical_point(
+                placement.rcNormalPosition.left as f32,
+                placement.rcNormalPosition.top as f32,
+                self.scale_factor,
             ),
+            size: physical_size.to_pixels(self.scale_factor),
         };
 
         if self.is_fullscreen() {
@@ -154,7 +159,7 @@ impl WindowsWindowState {
     /// Currently, GPUI uses logical size of the app to handle mouse interactions (such as
     /// whether the mouse collides with other elements of GPUI).
     fn content_size(&self) -> Size<Pixels> {
-        self.physical_size
+        self.logical_size
     }
 
     fn title_bar_padding(&self) -> Pixels {
@@ -538,7 +543,7 @@ impl PlatformWindow for WindowsWindow {
                 let mut lock = state_ptr.state.borrow_mut();
                 lock.fullscreen_restore_bounds = Bounds {
                     origin: lock.origin,
-                    size: lock.physical_size,
+                    size: lock.logical_size,
                 };
                 let StyleAndBounds {
                     style,
@@ -555,10 +560,10 @@ impl PlatformWindow for WindowsWindow {
                     unsafe { GetWindowRect(state_ptr.hwnd, &mut rc) }.log_err();
                     let _ = lock.fullscreen.insert(StyleAndBounds {
                         style,
-                        x: px(rc.left as f32),
-                        y: px(rc.top as f32),
-                        cx: px((rc.right - rc.left) as f32),
-                        cy: px((rc.bottom - rc.top) as f32),
+                        x: rc.left,
+                        y: rc.top,
+                        cx: rc.right - rc.left,
+                        cy: rc.bottom - rc.top,
                     });
                     let style = style
                         & !(WS_THICKFRAME
@@ -566,13 +571,13 @@ impl PlatformWindow for WindowsWindow {
                             | WS_MAXIMIZEBOX
                             | WS_MINIMIZEBOX
                             | WS_CAPTION);
-                    let bounds = lock.display.bounds();
+                    let physical_bounds = lock.display.physical_bounds();
                     StyleAndBounds {
                         style,
-                        x: bounds.left(),
-                        y: bounds.top(),
-                        cx: bounds.size.width,
-                        cy: bounds.size.height,
+                        x: physical_bounds.left().0,
+                        y: physical_bounds.top().0,
+                        cx: physical_bounds.size.width.0,
+                        cy: physical_bounds.size.height.0,
                     }
                 };
                 drop(lock);
@@ -581,10 +586,10 @@ impl PlatformWindow for WindowsWindow {
                     SetWindowPos(
                         state_ptr.hwnd,
                         HWND::default(),
-                        x.0 as i32,
-                        y.0 as i32,
-                        cx.0 as i32,
-                        cy.0 as i32,
+                        x,
+                        y,
+                        cx,
+                        cy,
                         SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER,
                     )
                 }
@@ -837,10 +842,10 @@ impl ClickState {
 
 struct StyleAndBounds {
     style: WINDOW_STYLE,
-    x: Pixels,
-    y: Pixels,
-    cx: Pixels,
-    cy: Pixels,
+    x: i32,
+    y: i32,
+    cx: i32,
+    cy: i32,
 }
 
 fn register_wnd_class(icon_handle: HICON) -> PCWSTR {