gardesk/ers / e9322b0

Browse files

Use CGDisplayBounds(CGMainDisplayID) for primary height

NSScreen.screens caches and only refreshes on certain notifications, so
plugging an external monitor while ers is running left primary_height
stuck at the pre-hotplug value. Every CG-to-Cocoa Y conversion was
therefore off by the difference between the old primary height and the
new one — visible as borders drawn far from their windows on the
external display.

CGDisplayBounds(CGMainDisplayID()) reflects the current state on every
call and matches what tarmac's display module already uses.
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
e9322b044ba1b0e565984377712a96fefd238d27
Parents
03f53bc
Tree
23796fc

2 changed files

StatusFile+-
M Cargo.toml 1 0
M src/nswindow_overlay.rs 25 27
Cargo.tomlmodified
@@ -40,4 +40,5 @@ objc2-core-graphics = { version = "0.3", default-features = false, features = [
4040
     "CGColor",
4141
     "CGColorSpace",
4242
     "CGPath",
43
+    "CGDirectDisplay",
4344
 ] }
src/nswindow_overlay.rsmodified
@@ -28,31 +28,21 @@ const NS_FLOATING_WINDOW_LEVEL: isize = 3;
2828
 /// Top-left Y in CG global coordinates becomes bottom-left Y in Cocoa
2929
 /// global coordinates by subtracting from the primary screen height.
3030
 ///
31
-/// The "primary" screen is the one whose Cocoa frame origin is (0, 0) —
32
-/// by definition the bottom-left of the menu-bar screen, which is the
33
-/// shared anchor between CG (top-left) and Cocoa (bottom-left) global
34
-/// coordinates. `[NSScreen screens][0]` is documented as the primary
35
-/// but isn't reliable on every macOS version, so we look it up by
36
-/// origin instead.
37
-fn primary_screen_height(mtm: MainThreadMarker) -> f64 {
38
-    let screens = NSScreen::screens(mtm);
39
-    let count = screens.count();
40
-    for i in 0..count {
41
-        let s = screens.objectAtIndex(i);
42
-        let f = s.frame();
43
-        if f.origin.x.abs() < 0.5 && f.origin.y.abs() < 0.5 {
44
-            return f.size.height;
45
-        }
46
-    }
47
-    if count > 0 {
48
-        screens.objectAtIndex(0).frame().size.height
49
-    } else {
50
-        0.0
31
+/// We use the main CGDisplay's bounds rather than `NSScreen.screens`
32
+/// because NSScreen caches and only refreshes on certain notifications
33
+/// — when a monitor is plugged or unplugged, NSScreen.screens can
34
+/// return stale primary-height values, causing every cocoa Y on the
35
+/// new layout to be off by the difference. CGDisplayBounds reflects
36
+/// the current state immediately.
37
+fn primary_screen_height() -> f64 {
38
+    unsafe {
39
+        let main_id = objc2_core_graphics::CGMainDisplayID();
40
+        objc2_core_graphics::CGDisplayBounds(main_id).size.height
5141
     }
5242
 }
5343
 
54
-fn cg_to_cocoa_frame(cg: CGRect, mtm: MainThreadMarker) -> CGRect {
55
-    let primary_height = primary_screen_height(mtm);
44
+fn cg_to_cocoa_frame(cg: CGRect, _mtm: MainThreadMarker) -> CGRect {
45
+    let primary_height = primary_screen_height();
5646
     let cocoa_y = primary_height - cg.origin.y - cg.size.height;
5747
     CGRect::new(
5848
         CGPoint::new(cg.origin.x, cocoa_y),
@@ -64,11 +54,19 @@ fn cg_to_cocoa_frame(cg: CGRect, mtm: MainThreadMarker) -> CGRect {
6454
 /// multi-monitor coordinate issues.
6555
 pub fn log_screens(mtm: MainThreadMarker) {
6656
     let screens = NSScreen::screens(mtm);
67
-    let primary_h = primary_screen_height(mtm);
57
+    let primary_h = primary_screen_height();
58
+    let cg_main_bounds = unsafe {
59
+        let id = objc2_core_graphics::CGMainDisplayID();
60
+        objc2_core_graphics::CGDisplayBounds(id)
61
+    };
6862
     tracing::debug!(
69
-        primary_height = primary_h,
70
-        count = screens.count(),
71
-        "NSScreen layout"
63
+        cg_primary_height = primary_h,
64
+        cg_main_x = cg_main_bounds.origin.x,
65
+        cg_main_y = cg_main_bounds.origin.y,
66
+        cg_main_w = cg_main_bounds.size.width,
67
+        cg_main_h = cg_main_bounds.size.height,
68
+        nsscreen_count = screens.count(),
69
+        "screen layout"
7270
     );
7371
     for i in 0..screens.count() {
7472
         let s = screens.objectAtIndex(i);
@@ -79,7 +77,7 @@ pub fn log_screens(mtm: MainThreadMarker) {
7977
             cocoa_y = f.origin.y,
8078
             w = f.size.width,
8179
             h = f.size.height,
82
-            "screen"
80
+            "nsscreen"
8381
         );
8482
     }
8583
 }