gardesk/ers / bc82199

Browse files

filter proxy windows from border targets

Authored by espadonne
SHA
bc82199b2aba47292e0a6dea2cb468fcc33cee55
Parents
2a15052
Tree
9141983

1 changed file

StatusFile+-
M src/main.rs 119 3
src/main.rsmodified
@@ -16,6 +16,13 @@ static SIGNAL_STOP_REQUESTED: AtomicBool = AtomicBool::new(false);
1616
 const MIN_TRACKED_WINDOW_SIZE: f64 = 4.0;
1717
 const GEOMETRY_EPSILON: f64 = 0.5;
1818
 const SCALE_EPSILON: f64 = 0.01;
19
+const WINDOW_ATTRIBUTE_REAL: u64 = 1 << 1;
20
+const WINDOW_TAG_DOCUMENT: u64 = 1 << 0;
21
+const WINDOW_TAG_FLOATING: u64 = 1 << 1;
22
+const WINDOW_TAG_ATTACHED: u64 = 1 << 7;
23
+const WINDOW_TAG_IGNORES_CYCLE: u64 = 1 << 18;
24
+const WINDOW_TAG_MODAL: u64 = 1 << 31;
25
+const WINDOW_TAG_REAL_SURFACE: u64 = 1 << 58;
1926
 
2027
 /// Per-overlay state: the connection it was created on + its wid.
2128
 struct Overlay {
@@ -50,6 +57,13 @@ enum SurfacePreference {
5057
     ReplaceExisting,
5158
 }
5259
 
60
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
61
+struct WindowMetadata {
62
+    parent_wid: u32,
63
+    tags: u64,
64
+    attributes: u64,
65
+}
66
+
5367
 fn surface_preference(existing: CGRect, candidate: CGRect) -> Option<SurfacePreference> {
5468
     if !is_same_window_surface(existing, candidate) {
5569
         return None;
@@ -81,6 +95,72 @@ fn size_changed(a: CGRect, b: CGRect) -> bool {
8195
         || (a.size.height - b.size.height).abs() > GEOMETRY_EPSILON
8296
 }
8397
 
98
+fn is_suitable_window_metadata(metadata: WindowMetadata) -> bool {
99
+    metadata.parent_wid == 0
100
+        && ((metadata.attributes & WINDOW_ATTRIBUTE_REAL) != 0
101
+            || (metadata.tags & WINDOW_TAG_REAL_SURFACE) != 0)
102
+        && (metadata.tags & WINDOW_TAG_ATTACHED) == 0
103
+        && (metadata.tags & WINDOW_TAG_IGNORES_CYCLE) == 0
104
+        && ((metadata.tags & WINDOW_TAG_DOCUMENT) != 0
105
+            || ((metadata.tags & WINDOW_TAG_FLOATING) != 0
106
+                && (metadata.tags & WINDOW_TAG_MODAL) != 0))
107
+}
108
+
109
+fn query_window_metadata(cid: CGSConnectionID, wid: u32) -> Option<WindowMetadata> {
110
+    unsafe {
111
+        let window_ref = cfarray_of_cfnumbers(
112
+            (&wid as *const u32).cast(),
113
+            std::mem::size_of::<u32>(),
114
+            1,
115
+            kCFNumberSInt32Type,
116
+        );
117
+        if window_ref.is_null() {
118
+            return None;
119
+        }
120
+
121
+        let query = SLSWindowQueryWindows(cid, window_ref, 0x0);
122
+        CFRelease(window_ref);
123
+        if query.is_null() {
124
+            return None;
125
+        }
126
+
127
+        let iterator = SLSWindowQueryResultCopyWindows(query);
128
+        CFRelease(query);
129
+        if iterator.is_null() {
130
+            return None;
131
+        }
132
+
133
+        let metadata = if SLSWindowIteratorAdvance(iterator) {
134
+            Some(WindowMetadata {
135
+                parent_wid: SLSWindowIteratorGetParentID(iterator),
136
+                tags: SLSWindowIteratorGetTags(iterator),
137
+                attributes: SLSWindowIteratorGetAttributes(iterator),
138
+            })
139
+        } else {
140
+            None
141
+        };
142
+
143
+        CFRelease(iterator);
144
+        metadata
145
+    }
146
+}
147
+
148
+fn is_suitable_window(cid: CGSConnectionID, wid: u32) -> bool {
149
+    match query_window_metadata(cid, wid) {
150
+        Some(metadata) => {
151
+            let suitable = is_suitable_window_metadata(metadata);
152
+            if !suitable {
153
+                debug!(
154
+                    "[window_filter] rejecting wid={} parent={} tags={:#x} attributes={:#x}",
155
+                    wid, metadata.parent_wid, metadata.tags, metadata.attributes
156
+                );
157
+            }
158
+            suitable
159
+        }
160
+        None => false,
161
+    }
162
+}
163
+
84164
 fn cf_string_from_static(name: &std::ffi::CStr) -> CFStringRef {
85165
     unsafe { CFStringCreateWithCString(ptr::null(), name.as_ptr().cast(), kCFStringEncodingUTF8) }
86166
 }
@@ -236,6 +316,9 @@ impl BorderMap {
236316
             if pid == self.own_pid {
237317
                 return;
238318
             }
319
+            if !is_suitable_window(self.main_cid, target_wid) {
320
+                return;
321
+            }
239322
 
240323
             let mut bounds = CGRect::default();
241324
             SLSGetWindowBounds(self.main_cid, target_wid, &mut bounds);
@@ -315,6 +398,11 @@ impl BorderMap {
315398
                 return false;
316399
             }
317400
 
401
+            if !is_suitable_window(self.main_cid, target_wid) {
402
+                self.remove(target_wid);
403
+                return true;
404
+            }
405
+
318406
             if !is_trackable_window(bounds, self.border_width) {
319407
                 self.remove(target_wid);
320408
                 return true;
@@ -569,6 +657,10 @@ fn get_front_window(own_pid: i32) -> u32 {
569657
                 continue;
570658
             }
571659
 
660
+            if !is_suitable_window(SLSMainConnectionID(), wid) {
661
+                continue;
662
+            }
663
+
572664
             // Track first non-self window as fallback (z-order based)
573665
             if fallback_wid == 0 {
574666
                 fallback_wid = wid;
@@ -987,7 +1079,7 @@ unsafe extern "C" fn drain_events(
9871079
     }
9881080
 }
9891081
 
990
-fn discover_windows(_cid: CGSConnectionID, own_pid: i32) -> Vec<u32> {
1082
+fn discover_windows(cid: CGSConnectionID, own_pid: i32) -> Vec<u32> {
9911083
     unsafe {
9921084
         let list = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
9931085
         if list.is_null() {
@@ -1023,6 +1115,10 @@ fn discover_windows(_cid: CGSConnectionID, own_pid: i32) -> Vec<u32> {
10231115
                 continue;
10241116
             }
10251117
 
1118
+            if !is_suitable_window(cid, wid) {
1119
+                continue;
1120
+            }
1121
+
10261122
             let mut layer: i32 = -1;
10271123
             if CFDictionaryGetValueIfPresent(dict, layer_key as CFTypeRef, &mut v) {
10281124
                 CFNumberGetValue(v, kCFNumberSInt32Type, &mut layer as *mut _ as *mut _);
@@ -1193,8 +1289,8 @@ fn list_windows() {
11931289
 #[cfg(test)]
11941290
 mod tests {
11951291
     use super::{
1196
-        CGRect, SurfacePreference, intersection_area, is_same_window_surface, is_trackable_window,
1197
-        surface_preference,
1292
+        CGRect, SurfacePreference, WindowMetadata, intersection_area, is_same_window_surface,
1293
+        is_suitable_window_metadata, is_trackable_window, surface_preference,
11981294
     };
11991295
 
12001296
     #[test]
@@ -1233,4 +1329,24 @@ mod tests {
12331329
         let small = CGRect::new(100.0, 100.0, 12.0, 18.0);
12341330
         assert!(is_trackable_window(small, 4.0));
12351331
     }
1332
+
1333
+    #[test]
1334
+    fn suitable_window_metadata_matches_document_windows() {
1335
+        let metadata = WindowMetadata {
1336
+            parent_wid: 0,
1337
+            tags: super::WINDOW_TAG_DOCUMENT,
1338
+            attributes: super::WINDOW_ATTRIBUTE_REAL,
1339
+        };
1340
+        assert!(is_suitable_window_metadata(metadata));
1341
+    }
1342
+
1343
+    #[test]
1344
+    fn attached_windows_are_not_suitable_targets() {
1345
+        let metadata = WindowMetadata {
1346
+            parent_wid: 7,
1347
+            tags: super::WINDOW_TAG_DOCUMENT | super::WINDOW_TAG_ATTACHED,
1348
+            attributes: super::WINDOW_ATTRIBUTE_REAL,
1349
+        };
1350
+        assert!(!is_suitable_window_metadata(metadata));
1351
+    }
12361352
 }