comparison dep/animia/src/win/x11.cc @ 213:58e81b42a0d6

dep/animia: win/x11: find toplevels WAY faster completely different approach. oops!
author Paper <mrpapersonic@gmail.com>
date Sun, 07 Jan 2024 11:11:27 -0500
parents 9f3534f6b8c4
children 8d35061e7505
comparison
equal deleted inserted replaced
212:6b08fbd7f206 213:58e81b42a0d6
11 11
12 #include <cstdint> 12 #include <cstdint>
13 #include <string> 13 #include <string>
14 #include <set> 14 #include <set>
15 15
16 /* The code for this is very fugly because X11 uses lots of generic type names
17 (i.e., Window, Display), so I have to use :: when defining vars to distinguish
18 between Animia's types and X11's types */
19
20 namespace animia::internal::x11 { 16 namespace animia::internal::x11 {
21 17
22 /* specify that these are X types. */ 18 /* specify that these are X types. */
23 typedef ::Window XWindow; 19 typedef ::Window XWindow;
24 typedef ::Display XDisplay; 20 typedef ::Display XDisplay;
25 typedef ::Atom XAtom; 21 typedef ::Atom XAtom;
26 22
27 /* should return UTF8_STRING or STRING */ 23 /* should return UTF8_STRING or STRING */
28 static bool GetWindowPropertyAsString(XDisplay* display, XWindow window, XAtom atom, std::string& result, XAtom reqtype = AnyPropertyType) { 24 static bool GetWindowPropertyAsString(XDisplay* display, XWindow window, XAtom atom, std::string& result, XAtom reqtype = AnyPropertyType) {
25 if (atom == None || reqtype == None)
26 return false;
27
29 int format; 28 int format;
30 unsigned long leftover_bytes, num_of_items; 29 unsigned long leftover_bytes, num_of_items;
31 XAtom type; 30 XAtom type;
32 unsigned char* data; 31 unsigned char* data;
33 32
68 67
69 return false; 68 return false;
70 } 69 }
71 #endif 70 #endif
72 71
72 XAtom Atom__NET_WM_PID = ::XInternAtom(display, "_NET_WM_PID", True);
73 if (Atom__NET_WM_PID == None)
74 return false;
75
73 int format; 76 int format;
74 unsigned long leftover_bytes, num_of_items; 77 unsigned long leftover_bytes, num_of_items;
75 XAtom atom = ::XInternAtom(display, "_NET_WM_PID", False), type; 78 XAtom type;
76 unsigned char* data; 79 unsigned char* data;
77 80
78 int status = ::XGetWindowProperty(display, window, atom, 0L, (~0L), False, XA_CARDINAL, 81 int status = ::XGetWindowProperty(display, window, Atom__NET_WM_PID, 0L, (~0L), False, XA_CARDINAL,
79 &type, &format, &num_of_items, &leftover_bytes, &data); 82 &type, &format, &num_of_items, &leftover_bytes, &data);
80 if (status != Success || type != XA_CARDINAL || num_of_items < 1) 83 if (status != Success || type != XA_CARDINAL || num_of_items < 1)
81 return false; 84 return false;
82 85
83 result = static_cast<pid_t>(*reinterpret_cast<uint32_t*>(data)); 86 result = static_cast<pid_t>(*reinterpret_cast<uint32_t*>(data));
87 return true; 90 return true;
88 } 91 }
89 92
90 static bool FetchName(XDisplay* display, XWindow window, std::string& result) { 93 static bool FetchName(XDisplay* display, XWindow window, std::string& result) {
91 /* TODO: Check if XInternAtom created None or not... */ 94 /* TODO: Check if XInternAtom created None or not... */
92 if (GetWindowPropertyAsString(display, window, ::XInternAtom(display, "_NET_WM_NAME", False), 95 if (GetWindowPropertyAsString(display, window, ::XInternAtom(display, "_NET_WM_NAME", True),
93 result, ::XInternAtom(display, "UTF8_STRING", False))) 96 result, ::XInternAtom(display, "UTF8_STRING", True)))
94 return true; 97 return true;
95 98
96 if (GetWindowPropertyAsString(display, window, ::XInternAtom(display, "WM_NAME", False), 99 if (GetWindowPropertyAsString(display, window, ::XInternAtom(display, "WM_NAME", True),
97 result, XA_STRING)) 100 result, XA_STRING))
98 return true; 101 return true;
99 102
100 /* Fallback to XGetWMName() */ 103 /* Fallback to XGetWMName() */
101 XTextProperty text; 104 XTextProperty text;
123 ::XFreeStringList(list); 126 ::XFreeStringList(list);
124 127
125 return true; 128 return true;
126 } 129 }
127 130
128 static bool WalkWindows(XDisplay* display, std::set<XWindow>& children, const std::set<XWindow>& windows) { 131 static bool GetAllTopLevelWindowsEWMH(XDisplay* display, XWindow root, std::set<XWindow>& result) {
129 /* This can take a VERY long time if many windows are open. */ 132 XAtom Atom__NET_CLIENT_LIST = XInternAtom(display, "_NET_CLIENT_LIST", True);
130 if (windows.empty()) 133 if (Atom__NET_CLIENT_LIST == None)
131 return false; 134 return false;
132 135
133 for (const XWindow& window : windows) { 136 XAtom actual_type;
137 int format;
138 unsigned long num_of_items, bytes_after;
139 unsigned char* data = nullptr;
140
141 {
142 int status = ::XGetWindowProperty(
143 display, root, Atom__NET_CLIENT_LIST,
144 0L, (~0L), false, AnyPropertyType, &actual_type,
145 &format, &num_of_items, &bytes_after, &data
146 );
147
148 if (status < Success || !num_of_items)
149 return false;
150
151 if (format != 32) {
152 ::XFree(data);
153 return false;
154 }
155 }
156
157 XWindow* arr = (XWindow*)data;
158
159 for (uint32_t i = 0; i < num_of_items; i++)
160 result.insert(arr[i]);
161
162 ::XFree(data);
163
164 return true;
165 }
166
167 static bool GetAllTopLevelWindows(XDisplay* display, XWindow root, std::set<XWindow>& result) {
168 // EWMH. Takes about 15 ms on a fairly good PC.
169 if (GetAllTopLevelWindowsEWMH(display, root, result))
170 return true;
171
172 // Fallback to ICCCM. Takes about the same time on a good PC.
173 XAtom Atom_WM_STATE = XInternAtom(display, "WM_STATE", True);
174 if (Atom_WM_STATE == None)
175 return false;
176
177 auto window_has_wm_state = [&](XWindow window) -> bool {
178 int format;
179 Atom actual_type;
180 unsigned long num_of_items, bytes_after;
181 unsigned char* data = nullptr;
182
183 int status = ::XGetWindowProperty(
184 display, window, Atom_WM_STATE,
185 0L, (~0L), false, AnyPropertyType, &actual_type,
186 &format, &num_of_items, &bytes_after, &data
187 );
188
189 ::XFree(data);
190
191 return !(actual_type == None && !format && !bytes_after);
192 };
193
194 std::function<bool(XWindow, XWindow&)> immediate_child_get_toplevel = [&](XWindow window, XWindow& result) {
195 result = window;
196 if (window_has_wm_state(window))
197 return true;
198
134 unsigned int num_children = 0; 199 unsigned int num_children = 0;
135 XWindow* children_arr = nullptr; 200 XWindow* children_arr = nullptr;
136 201
137 XWindow root_return; 202 XWindow root_return;
138 XWindow parent_return; 203 XWindow parent_return;
139 204
140 int status = ::XQueryTree(display, window, &root_return, &parent_return, &children_arr, &num_children); 205 int status = ::XQueryTree(display, window, &root_return, &parent_return, &children_arr, &num_children);
141 if (!status || !children_arr) 206 if (!status || !children_arr)
142 continue; 207 return false;
143 208
144 if (num_children < 1) { 209 if (num_children < 1) {
145 ::XFree(children_arr); 210 ::XFree(children_arr);
146 continue; 211 return false;
147 } 212 }
148 213
149 for (int i = 0; i < num_children; i++) 214 for (unsigned int i = 0; i < num_children; i++) {
150 if (!children.count(children_arr[i])) 215 if (immediate_child_get_toplevel(children_arr[i], result)) {
151 children.insert(children_arr[i]); 216 ::XFree(children_arr);
217 return true;
218 }
219 }
152 220
153 ::XFree(children_arr); 221 ::XFree(children_arr);
154 222 return false;
155 std::set<XWindow> children_children; 223 };
156 224
157 if (WalkWindows(display, children_children, children)) 225 unsigned int num_children = 0;
158 children.insert(children_children.begin(), children_children.end()); 226 XWindow* children_arr = nullptr;
159 } 227
228 XWindow root_return;
229 XWindow parent_return;
230
231 int status = ::XQueryTree(display, root, &root_return, &parent_return, &children_arr, &num_children);
232 if (!status || !children_arr)
233 return false; // how
234
235 if (num_children < 1) {
236 ::XFree(children_arr);
237 return false;
238 }
239
240 for (unsigned int i = 0; i < num_children; i++) {
241 XWindow res;
242 if (immediate_child_get_toplevel(children_arr[i], res))
243 result.insert(res);
244 }
245
246 ::XFree(children_arr);
160 247
161 return true; 248 return true;
162 } 249 }
163 250
164 bool EnumerateWindows(window_proc_t window_proc) { 251 bool EnumerateWindows(window_proc_t window_proc) {
170 return false; 257 return false;
171 258
172 XWindow root = ::XDefaultRootWindow(display); 259 XWindow root = ::XDefaultRootWindow(display);
173 260
174 std::set<XWindow> windows; 261 std::set<XWindow> windows;
175 WalkWindows(display, windows, {root}); 262 GetAllTopLevelWindows(display, root, windows);
176 263
177 for (const auto& window : windows) { 264 for (const auto& window : windows) {
178 Window win; 265 Window win;
179 win.id = window; 266 win.id = window;
180 { 267 {