@@ -12,6 +12,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; |
| 12 | 12 | use std::sync::mpsc; |
| 13 | 13 | use tracing::debug; |
| 14 | 14 | |
| 15 | +static SIGNAL_STOP_REQUESTED: AtomicBool = AtomicBool::new(false); |
| 16 | + |
| 15 | 17 | /// Per-overlay state: the connection it was created on + its wid. |
| 16 | 18 | struct Overlay { |
| 17 | 19 | cid: CGSConnectionID, |
@@ -37,6 +39,14 @@ fn is_same_window_surface(a: CGRect, b: CGRect) -> bool { |
| 37 | 39 | smaller > 0.0 && intersection_area(a, b) / smaller >= 0.9 |
| 38 | 40 | } |
| 39 | 41 | |
| 42 | +fn cf_string_from_static(name: &std::ffi::CStr) -> CFStringRef { |
| 43 | + unsafe { CFStringCreateWithCString(ptr::null(), name.as_ptr().cast(), kCFStringEncodingUTF8) } |
| 44 | +} |
| 45 | + |
| 46 | +unsafe extern "C" fn handle_sigint(_: libc::c_int) { |
| 47 | + SIGNAL_STOP_REQUESTED.store(true, Ordering::Relaxed); |
| 48 | +} |
| 49 | + |
| 40 | 50 | /// Tracks overlays for target windows. |
| 41 | 51 | struct BorderMap { |
| 42 | 52 | overlays: HashMap<u32, Overlay>, |
@@ -360,21 +370,9 @@ fn get_front_window(own_pid: i32) -> u32 { |
| 360 | 370 | } |
| 361 | 371 | |
| 362 | 372 | let count = CFArrayGetCount(list); |
| 363 | | - let wid_key = CFStringCreateWithCString( |
| 364 | | - ptr::null(), |
| 365 | | - b"kCGWindowNumber\0".as_ptr(), |
| 366 | | - kCFStringEncodingUTF8, |
| 367 | | - ); |
| 368 | | - let pid_key = CFStringCreateWithCString( |
| 369 | | - ptr::null(), |
| 370 | | - b"kCGWindowOwnerPID\0".as_ptr(), |
| 371 | | - kCFStringEncodingUTF8, |
| 372 | | - ); |
| 373 | | - let layer_key = CFStringCreateWithCString( |
| 374 | | - ptr::null(), |
| 375 | | - b"kCGWindowLayer\0".as_ptr(), |
| 376 | | - kCFStringEncodingUTF8, |
| 377 | | - ); |
| 373 | + let wid_key = cf_string_from_static(c"kCGWindowNumber"); |
| 374 | + let pid_key = cf_string_from_static(c"kCGWindowOwnerPID"); |
| 375 | + let layer_key = cf_string_from_static(c"kCGWindowLayer"); |
| 378 | 376 | |
| 379 | 377 | let mut front_wid: u32 = 0; |
| 380 | 378 | let mut front_bounds = CGRect::default(); |
@@ -584,17 +582,34 @@ fn main() { |
| 584 | 582 | |
| 585 | 583 | debug!("{} overlays tracked", borders.overlays.len()); |
| 586 | 584 | |
| 587 | | - // SIGINT flag — background thread checks this to clean up |
| 585 | + SIGNAL_STOP_REQUESTED.store(false, Ordering::Relaxed); |
| 586 | + |
| 587 | + // Background watcher translates the signal-safe atomic into a normal |
| 588 | + // CoreFoundation shutdown request on a Rust thread. |
| 588 | 589 | let running = Arc::new(AtomicBool::new(true)); |
| 590 | + let signal_watcher = std::thread::spawn(|| { |
| 591 | + use std::time::Duration; |
| 592 | + |
| 593 | + while !SIGNAL_STOP_REQUESTED.load(Ordering::Relaxed) { |
| 594 | + std::thread::sleep(Duration::from_millis(10)); |
| 595 | + } |
| 596 | + |
| 597 | + unsafe { |
| 598 | + let run_loop = CFRunLoopGetMain(); |
| 599 | + CFRunLoopStop(run_loop); |
| 600 | + CFRunLoopWakeUp(run_loop); |
| 601 | + } |
| 602 | + }); |
| 603 | + |
| 589 | 604 | unsafe { |
| 590 | | - libc::signal(libc::SIGINT, { |
| 591 | | - unsafe extern "C" fn handler(_: libc::c_int) { |
| 592 | | - unsafe { |
| 593 | | - CFRunLoopStop(CFRunLoopGetMain()); |
| 594 | | - } |
| 595 | | - } |
| 596 | | - handler as *const () as libc::sighandler_t |
| 597 | | - }); |
| 605 | + libc::signal( |
| 606 | + libc::SIGINT, |
| 607 | + handle_sigint as *const () as libc::sighandler_t, |
| 608 | + ); |
| 609 | + libc::signal( |
| 610 | + libc::SIGTERM, |
| 611 | + handle_sigint as *const () as libc::sighandler_t, |
| 612 | + ); |
| 598 | 613 | } |
| 599 | 614 | |
| 600 | 615 | // Process events on background thread with coalescing |
@@ -767,6 +782,8 @@ fn main() { |
| 767 | 782 | |
| 768 | 783 | // SIGINT received — signal background thread to stop and wait |
| 769 | 784 | running.store(false, Ordering::Relaxed); |
| 785 | + SIGNAL_STOP_REQUESTED.store(true, Ordering::Relaxed); |
| 786 | + let _ = signal_watcher.join(); |
| 770 | 787 | let _ = handle.join(); |
| 771 | 788 | } |
| 772 | 789 | |
@@ -820,21 +837,9 @@ fn discover_windows(_cid: CGSConnectionID, own_pid: i32) -> Vec<u32> { |
| 820 | 837 | } |
| 821 | 838 | |
| 822 | 839 | let count = CFArrayGetCount(list); |
| 823 | | - let wid_key = CFStringCreateWithCString( |
| 824 | | - ptr::null(), |
| 825 | | - b"kCGWindowNumber\0".as_ptr(), |
| 826 | | - kCFStringEncodingUTF8, |
| 827 | | - ); |
| 828 | | - let pid_key = CFStringCreateWithCString( |
| 829 | | - ptr::null(), |
| 830 | | - b"kCGWindowOwnerPID\0".as_ptr(), |
| 831 | | - kCFStringEncodingUTF8, |
| 832 | | - ); |
| 833 | | - let layer_key = CFStringCreateWithCString( |
| 834 | | - ptr::null(), |
| 835 | | - b"kCGWindowLayer\0".as_ptr(), |
| 836 | | - kCFStringEncodingUTF8, |
| 837 | | - ); |
| 840 | + let wid_key = cf_string_from_static(c"kCGWindowNumber"); |
| 841 | + let pid_key = cf_string_from_static(c"kCGWindowOwnerPID"); |
| 842 | + let layer_key = cf_string_from_static(c"kCGWindowLayer"); |
| 838 | 843 | |
| 839 | 844 | let mut wids = Vec::new(); |
| 840 | 845 | for i in 0..count { |
@@ -987,16 +992,8 @@ fn list_windows() { |
| 987 | 992 | return; |
| 988 | 993 | } |
| 989 | 994 | let count = CFArrayGetCount(list); |
| 990 | | - let wid_key = CFStringCreateWithCString( |
| 991 | | - ptr::null(), |
| 992 | | - b"kCGWindowNumber\0".as_ptr(), |
| 993 | | - kCFStringEncodingUTF8, |
| 994 | | - ); |
| 995 | | - let layer_key = CFStringCreateWithCString( |
| 996 | | - ptr::null(), |
| 997 | | - b"kCGWindowLayer\0".as_ptr(), |
| 998 | | - kCFStringEncodingUTF8, |
| 999 | | - ); |
| 995 | + let wid_key = cf_string_from_static(c"kCGWindowNumber"); |
| 996 | + let layer_key = cf_string_from_static(c"kCGWindowLayer"); |
| 1000 | 997 | |
| 1001 | 998 | eprintln!( |
| 1002 | 999 | "{:>6} {:>8} {:>8} {:>6} {:>6}", |