Mercurial > minori
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 { | 
