gardesk/ers / 2fae700

Browse files

add graceful shutdown on SIGINT, release all overlays on exit

Authored by espadonne
SHA
2fae700a74e9d9ea07568d949fa24e5720bfb814
Parents
ffb609e
Tree
8b9beb9

3 changed files

StatusFile+-
M Cargo.toml 1 0
M src/main.rs 35 4
M src/skylight.rs 2 0
Cargo.tomlmodified
@@ -5,6 +5,7 @@ edition = "2024"
55
 description = "Window border renderer for tarmac"
66
 
77
 [dependencies]
8
+libc = "0.2"
89
 serde = { version = "1", features = ["derive"] }
910
 serde_json = "1"
1011
 tracing = "0.1"
src/main.rsmodified
@@ -7,7 +7,9 @@ use events::Event;
77
 use skylight::*;
88
 use std::collections::HashMap;
99
 use std::ptr;
10
+use std::sync::atomic::{AtomicBool, Ordering};
1011
 use std::sync::mpsc;
12
+use std::sync::Arc;
1113
 
1214
 /// Per-overlay state: the connection it was created on + its wid.
1315
 struct Overlay {
@@ -88,6 +90,13 @@ impl BorderMap {
8890
         }
8991
     }
9092
 
93
+    fn remove_all(&mut self) {
94
+        let wids: Vec<u32> = self.overlays.keys().copied().collect();
95
+        for wid in wids {
96
+            self.remove(wid);
97
+        }
98
+    }
99
+
91100
     fn remove(&mut self, target_wid: u32) {
92101
         if let Some(overlay) = self.overlays.remove(&target_wid) {
93102
             unsafe {
@@ -401,17 +410,32 @@ fn main() {
401410
 
402411
     eprintln!("{} overlays tracked", borders.overlays.len());
403412
 
413
+    // SIGINT flag — background thread checks this to clean up
414
+    let running = Arc::new(AtomicBool::new(true));
415
+    unsafe {
416
+        libc::signal(libc::SIGINT, {
417
+            unsafe extern "C" fn handler(_: libc::c_int) {
418
+                // Stop CFRunLoop on main thread — this returns control to main()
419
+                CFRunLoopStop(CFRunLoopGetMain());
420
+            }
421
+            handler as libc::sighandler_t
422
+        });
423
+    }
424
+
404425
     // Process events on background thread with coalescing
405
-    std::thread::spawn(move || {
426
+    let running_bg = Arc::clone(&running);
427
+    let handle = std::thread::spawn(move || {
406428
         use std::collections::HashSet;
429
+        use std::time::Duration;
407430
 
408431
         // Persist across batches: windows we know about but haven't bordered yet
409432
         let mut pending: HashSet<u32> = HashSet::new();
410433
 
411
-        loop {
412
-            let first = match rx.recv() {
434
+        while running_bg.load(Ordering::Relaxed) {
435
+            let first = match rx.recv_timeout(Duration::from_millis(100)) {
413436
                 Ok(e) => e,
414
-                Err(_) => break,
437
+                Err(mpsc::RecvTimeoutError::Timeout) => continue,
438
+                Err(mpsc::RecvTimeoutError::Disconnected) => break,
415439
             };
416440
 
417441
             std::thread::sleep(std::time::Duration::from_millis(16));
@@ -548,9 +572,16 @@ fn main() {
548572
             // After all processing, enforce active-only visibility
549573
             borders.enforce_active_only();
550574
         }
575
+
576
+        // Clean up all overlays before exiting
577
+        borders.remove_all();
551578
     });
552579
 
553580
     unsafe { CFRunLoopRun() };
581
+
582
+    // SIGINT received — signal background thread to stop and wait
583
+    running.store(false, Ordering::Relaxed);
584
+    let _ = handle.join();
554585
 }
555586
 
556587
 fn setup_event_port(cid: CGSConnectionID) {
src/skylight.rsmodified
@@ -484,8 +484,10 @@ unsafe extern "C" {
484484
     ) -> CFRunLoopSourceRef;
485485
 
486486
     pub fn CFRunLoopGetCurrent() -> CFRunLoopRef;
487
+    pub fn CFRunLoopGetMain() -> CFRunLoopRef;
487488
     pub fn CFRunLoopAddSource(rl: CFRunLoopRef, source: CFRunLoopSourceRef, mode: CFStringRef);
488489
     pub fn CFRunLoopRun();
490
+    pub fn CFRunLoopStop(rl: CFRunLoopRef);
489491
 
490492
     pub static kCFAllocatorDefault: CFAllocatorRef;
491493
     pub static kCFTypeDictionaryKeyCallBacks: c_void;