gardesk/ers / 72f721e

Browse files

Re-log layout and force-resync overlays on display hotplug

Register CGDisplayRegisterReconfigurationCallback so a monitor plug
or resolution change re-fetches every overlay's bounds and re-applies
set_bounds. Without this, overlays whose CG bounds didn't change but
whose cocoa frame depends on the new primary screen height stayed at
their pre-hotplug positions until the next SLS Move event.
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
72f721e7809c9a63e23e533ef91afc7ade3df6a2
Parents
e9322b0
Tree
dd4eb46

2 changed files

StatusFile+-
M src/main.rs 62 0
M src/skylight.rs 11 0
src/main.rsmodified
@@ -476,6 +476,37 @@ impl BorderMap {
476476
         changed
477477
     }
478478
 
479
+    /// Re-apply set_bounds for every tracked overlay even when the
480
+    /// stored CG bounds match the current SLS bounds. After a display
481
+    /// reconfiguration the cocoa frame depends on the (possibly new)
482
+    /// primary screen height, so unchanged CG bounds still need their
483
+    /// cocoa frame recomputed.
484
+    fn reconcile_all_force(&mut self) {
485
+        let tracked: Vec<u32> = self.overlays.keys().copied().collect();
486
+        let active_only = self.active_only;
487
+        let focused = self.focused_wid;
488
+        for wid in tracked {
489
+            let mut bounds = CGRect::default();
490
+            unsafe {
491
+                if SLSGetWindowBounds(self.main_cid, wid, &mut bounds) != kCGErrorSuccess {
492
+                    self.remove(wid);
493
+                    continue;
494
+                }
495
+            }
496
+            if let Some(overlay) = self.overlays.get_mut(&wid) {
497
+                overlay.window.set_bounds(
498
+                    bounds.origin.x,
499
+                    bounds.origin.y,
500
+                    bounds.size.width,
501
+                    bounds.size.height,
502
+                );
503
+                if !active_only || wid == focused {
504
+                    overlay.window.order_above(wid);
505
+                }
506
+            }
507
+        }
508
+    }
509
+
479510
     /// With NSWindow.setFrame_display we no longer need a destroy-and-
480511
     /// recreate path on resize. Kept as a thin alias so existing call
481512
     /// sites keep working.
@@ -838,6 +869,7 @@ fn main() {
838869
     // require a main-thread context.
839870
     let mtm = nswindow_overlay::init_application();
840871
     nswindow_overlay::log_screens(mtm);
872
+    register_display_hotplug_callback();
841873
 
842874
     let cid = unsafe { SLSMainConnectionID() };
843875
     let own_pid = unsafe {
@@ -1183,6 +1215,36 @@ fn process_event_batch(
11831215
     borders.enforce_active_only();
11841216
 }
11851217
 
1218
+/// Re-log the screen layout when the display configuration changes
1219
+/// (monitor plug/unplug, resolution change). The callback also nudges
1220
+/// every tracked overlay to re-fetch its bounds so any cached cocoa Y
1221
+/// computed against the old primary height gets refreshed.
1222
+unsafe extern "C" fn display_reconfig_callback(
1223
+    display_id: u32,
1224
+    flags: u32,
1225
+    _user_info: *mut std::ffi::c_void,
1226
+) {
1227
+    debug!(display_id, flags, "[hotplug] CGDisplay reconfiguration");
1228
+    if let Some(mtm) = objc2::MainThreadMarker::new() {
1229
+        nswindow_overlay::log_screens(mtm);
1230
+    }
1231
+    MAIN_STATE.with(|cell| {
1232
+        if let Some(s) = cell.borrow_mut().as_mut() {
1233
+            s.borders.reconcile_all_force();
1234
+        }
1235
+    });
1236
+}
1237
+
1238
+fn register_display_hotplug_callback() {
1239
+    unsafe {
1240
+        let rc = CGDisplayRegisterReconfigurationCallback(
1241
+            Some(display_reconfig_callback),
1242
+            std::ptr::null_mut(),
1243
+        );
1244
+        debug!("[hotplug] register CGDisplayReconfiguration rc={}", rc);
1245
+    }
1246
+}
1247
+
11861248
 fn setup_event_port(cid: CGSConnectionID) {
11871249
     unsafe {
11881250
         let mut port: u32 = 0;
src/skylight.rsmodified
@@ -504,6 +504,17 @@ unsafe extern "C" {
504504
     pub static mach_task_self_: u32;
505505
 }
506506
 
507
+// --- CGDisplay hotplug callback ---
508
+
509
+unsafe extern "C" {
510
+    pub fn CGDisplayRegisterReconfigurationCallback(
511
+        callback: Option<
512
+            unsafe extern "C" fn(display: u32, flags: u32, user_info: *mut std::ffi::c_void),
513
+        >,
514
+        user_info: *mut std::ffi::c_void,
515
+    ) -> i32;
516
+}
517
+
507518
 pub fn mach_task_self() -> u32 {
508519
     unsafe { mach_task_self_ }
509520
 }