Update `display` info when window moved to another monitor (#9360)

张小白 created

Release Notes:

- N/A

Change summary

crates/gpui/src/platform/windows/display.rs | 41 ++++++++++++++++------
crates/gpui/src/platform/windows/window.rs  | 31 ++++++++++++++--
2 files changed, 56 insertions(+), 16 deletions(-)

Detailed changes

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

@@ -11,6 +11,7 @@ use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Point, Size};
 
 #[derive(Debug)]
 pub(crate) struct WindowsDisplay {
+    pub handle: HMONITOR,
     pub display_id: DisplayId,
     bounds: Bounds<GlobalPixels>,
     uuid: Uuid,
@@ -28,6 +29,7 @@ impl WindowsDisplay {
         let uuid = generate_uuid(&info.szDevice);
 
         Some(WindowsDisplay {
+            handle: screen,
             display_id,
             bounds: Bounds {
                 origin: Point {
@@ -43,12 +45,39 @@ 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 uuid = generate_uuid(&info.szDevice);
+        let display_id = available_monitors()
+            .iter()
+            .position(|handle| handle.0 == monitor.0)
+            .unwrap();
+
+        WindowsDisplay {
+            handle: monitor,
+            display_id: DisplayId(display_id as _),
+            bounds: Bounds {
+                origin: Point {
+                    x: GlobalPixels(size.left as f32),
+                    y: GlobalPixels(size.top as f32),
+                },
+                size: Size {
+                    width: GlobalPixels((size.right - size.left) as f32),
+                    height: GlobalPixels((size.bottom - size.top) as f32),
+                },
+            },
+            uuid,
+        }
+    }
+
     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 uuid = generate_uuid(&info.szDevice);
 
         WindowsDisplay {
+            handle,
             display_id,
             bounds: Bounds {
                 origin: Point {
@@ -75,17 +104,7 @@ impl WindowsDisplay {
             );
             return None;
         }
-        let Some(display_id) = available_monitors()
-            .iter()
-            .position(|handle| handle.0 == monitor.0)
-        else {
-            return None;
-        };
-
-        Some(WindowsDisplay::new_with_handle_and_id(
-            monitor,
-            DisplayId(display_id as _),
-        ))
+        Some(WindowsDisplay::new_with_handle(monitor))
     }
 
     pub fn displays() -> Vec<Rc<dyn PlatformDisplay>> {

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

@@ -49,6 +49,7 @@ pub(crate) struct WindowsWindowInner {
     platform_inner: Rc<WindowsPlatformInner>,
     pub(crate) handle: AnyWindowHandle,
     scale_factor: f32,
+    display: RefCell<Rc<WindowsDisplay>>,
 }
 
 impl WindowsWindowInner {
@@ -57,6 +58,7 @@ impl WindowsWindowInner {
         cs: &CREATESTRUCTW,
         platform_inner: Rc<WindowsPlatformInner>,
         handle: AnyWindowHandle,
+        display: Rc<WindowsDisplay>,
     ) -> Self {
         let origin = Cell::new(Point::new((cs.x as f64).into(), (cs.y as f64).into()));
         let size = Cell::new(Size {
@@ -101,6 +103,7 @@ impl WindowsWindowInner {
         };
         let renderer = RefCell::new(BladeRenderer::new(gpu, extent));
         let callbacks = RefCell::new(Callbacks::default());
+        let display = RefCell::new(display);
         Self {
             hwnd,
             origin,
@@ -112,6 +115,7 @@ impl WindowsWindowInner {
             platform_inner,
             handle,
             scale_factor: 1.0,
+            display,
         }
     }
 
@@ -251,6 +255,22 @@ impl WindowsWindowInner {
         let x = lparam.signed_loword() as f64;
         let y = lparam.signed_hiword() as f64;
         self.origin.set(Point::new(x.into(), y.into()));
+        let size = self.size.get();
+        let center_x = x as f32 + size.width.0 / 2.0;
+        let center_y = y as f32 + size.height.0 / 2.0;
+        let monitor_bounds = self.display.borrow().bounds();
+        if center_x < monitor_bounds.left().0
+            || center_x > monitor_bounds.right().0
+            || center_y < monitor_bounds.top().0
+            || center_y > monitor_bounds.bottom().0
+        {
+            // center of the window may have moved to another monitor
+            let monitor = unsafe { MonitorFromWindow(self.hwnd, MONITOR_DEFAULTTONULL) };
+            if !monitor.is_invalid() && self.display.borrow().handle != monitor {
+                // we will get the same monitor if we only have one
+                (*self.display.borrow_mut()) = Rc::new(WindowsDisplay::new_with_handle(monitor));
+            }
+        }
         let mut callbacks = self.callbacks.borrow_mut();
         if let Some(callback) = callbacks.moved.as_mut() {
             callback()
@@ -1015,13 +1035,13 @@ struct Callbacks {
 pub(crate) struct WindowsWindow {
     inner: Rc<WindowsWindowInner>,
     drag_drop_handler: IDropTarget,
-    display: Rc<WindowsDisplay>,
 }
 
 struct WindowCreateContext {
     inner: Option<Rc<WindowsWindowInner>>,
     platform_inner: Rc<WindowsPlatformInner>,
     handle: AnyWindowHandle,
+    display: Rc<WindowsDisplay>,
 }
 
 impl WindowsWindow {
@@ -1051,6 +1071,9 @@ impl WindowsWindow {
             inner: None,
             platform_inner: platform_inner.clone(),
             handle,
+            // todo(windows) move window to target monitor
+            // options.display_id
+            display: Rc::new(WindowsDisplay::primary_monitor().unwrap()),
         };
         let lpparam = Some(&context as *const _ as *const _);
         unsafe {
@@ -1079,12 +1102,9 @@ impl WindowsWindow {
             };
             drag_drop_handler
         };
-        // todo(windows) move window to target monitor
-        // options.display_id
         let wnd = Self {
             inner: context.inner.unwrap(),
             drag_drop_handler,
-            display: Rc::new(WindowsDisplay::primary_monitor().unwrap()),
         };
         platform_inner
             .raw_window_handles
@@ -1161,7 +1181,7 @@ impl PlatformWindow for WindowsWindow {
     }
 
     fn display(&self) -> Rc<dyn PlatformDisplay> {
-        self.display.clone()
+        self.inner.display.borrow().clone()
     }
 
     fn mouse_position(&self) -> Point<Pixels> {
@@ -1504,6 +1524,7 @@ unsafe extern "system" fn wnd_proc(
             cs,
             ctx.platform_inner.clone(),
             ctx.handle,
+            ctx.display.clone(),
         ));
         let weak = Box::new(Rc::downgrade(&inner));
         unsafe { set_window_long(hwnd, GWLP_USERDATA, Box::into_raw(weak) as isize) };