Mercurial > minori
changeset 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 | 6b08fbd7f206 |
children | 8d35061e7505 |
files | dep/animia/src/win/x11.cc |
diffstat | 1 files changed, 110 insertions(+), 23 deletions(-) [+] |
line wrap: on
line diff
--- a/dep/animia/src/win/x11.cc Sun Jan 07 09:54:50 2024 -0500 +++ b/dep/animia/src/win/x11.cc Sun Jan 07 11:11:27 2024 -0500 @@ -13,10 +13,6 @@ #include <string> #include <set> -/* The code for this is very fugly because X11 uses lots of generic type names - (i.e., Window, Display), so I have to use :: when defining vars to distinguish - between Animia's types and X11's types */ - namespace animia::internal::x11 { /* specify that these are X types. */ @@ -26,6 +22,9 @@ /* should return UTF8_STRING or STRING */ static bool GetWindowPropertyAsString(XDisplay* display, XWindow window, XAtom atom, std::string& result, XAtom reqtype = AnyPropertyType) { + if (atom == None || reqtype == None) + return false; + int format; unsigned long leftover_bytes, num_of_items; XAtom type; @@ -70,12 +69,16 @@ } #endif + XAtom Atom__NET_WM_PID = ::XInternAtom(display, "_NET_WM_PID", True); + if (Atom__NET_WM_PID == None) + return false; + int format; unsigned long leftover_bytes, num_of_items; - XAtom atom = ::XInternAtom(display, "_NET_WM_PID", False), type; + XAtom type; unsigned char* data; - int status = ::XGetWindowProperty(display, window, atom, 0L, (~0L), False, XA_CARDINAL, + int status = ::XGetWindowProperty(display, window, Atom__NET_WM_PID, 0L, (~0L), False, XA_CARDINAL, &type, &format, &num_of_items, &leftover_bytes, &data); if (status != Success || type != XA_CARDINAL || num_of_items < 1) return false; @@ -89,11 +92,11 @@ static bool FetchName(XDisplay* display, XWindow window, std::string& result) { /* TODO: Check if XInternAtom created None or not... */ - if (GetWindowPropertyAsString(display, window, ::XInternAtom(display, "_NET_WM_NAME", False), - result, ::XInternAtom(display, "UTF8_STRING", False))) + if (GetWindowPropertyAsString(display, window, ::XInternAtom(display, "_NET_WM_NAME", True), + result, ::XInternAtom(display, "UTF8_STRING", True))) return true; - if (GetWindowPropertyAsString(display, window, ::XInternAtom(display, "WM_NAME", False), + if (GetWindowPropertyAsString(display, window, ::XInternAtom(display, "WM_NAME", True), result, XA_STRING)) return true; @@ -125,12 +128,74 @@ return true; } -static bool WalkWindows(XDisplay* display, std::set<XWindow>& children, const std::set<XWindow>& windows) { - /* This can take a VERY long time if many windows are open. */ - if (windows.empty()) +static bool GetAllTopLevelWindowsEWMH(XDisplay* display, XWindow root, std::set<XWindow>& result) { + XAtom Atom__NET_CLIENT_LIST = XInternAtom(display, "_NET_CLIENT_LIST", True); + if (Atom__NET_CLIENT_LIST == None) return false; - for (const XWindow& window : windows) { + XAtom actual_type; + int format; + unsigned long num_of_items, bytes_after; + unsigned char* data = nullptr; + + { + int status = ::XGetWindowProperty( + display, root, Atom__NET_CLIENT_LIST, + 0L, (~0L), false, AnyPropertyType, &actual_type, + &format, &num_of_items, &bytes_after, &data + ); + + if (status < Success || !num_of_items) + return false; + + if (format != 32) { + ::XFree(data); + return false; + } + } + + XWindow* arr = (XWindow*)data; + + for (uint32_t i = 0; i < num_of_items; i++) + result.insert(arr[i]); + + ::XFree(data); + + return true; +} + +static bool GetAllTopLevelWindows(XDisplay* display, XWindow root, std::set<XWindow>& result) { + // EWMH. Takes about 15 ms on a fairly good PC. + if (GetAllTopLevelWindowsEWMH(display, root, result)) + return true; + + // Fallback to ICCCM. Takes about the same time on a good PC. + XAtom Atom_WM_STATE = XInternAtom(display, "WM_STATE", True); + if (Atom_WM_STATE == None) + return false; + + auto window_has_wm_state = [&](XWindow window) -> bool { + int format; + Atom actual_type; + unsigned long num_of_items, bytes_after; + unsigned char* data = nullptr; + + int status = ::XGetWindowProperty( + display, window, Atom_WM_STATE, + 0L, (~0L), false, AnyPropertyType, &actual_type, + &format, &num_of_items, &bytes_after, &data + ); + + ::XFree(data); + + return !(actual_type == None && !format && !bytes_after); + }; + + std::function<bool(XWindow, XWindow&)> immediate_child_get_toplevel = [&](XWindow window, XWindow& result) { + result = window; + if (window_has_wm_state(window)) + return true; + unsigned int num_children = 0; XWindow* children_arr = nullptr; @@ -139,25 +204,47 @@ int status = ::XQueryTree(display, window, &root_return, &parent_return, &children_arr, &num_children); if (!status || !children_arr) - continue; + return false; if (num_children < 1) { ::XFree(children_arr); - continue; + return false; + } + + for (unsigned int i = 0; i < num_children; i++) { + if (immediate_child_get_toplevel(children_arr[i], result)) { + ::XFree(children_arr); + return true; + } } - for (int i = 0; i < num_children; i++) - if (!children.count(children_arr[i])) - children.insert(children_arr[i]); + ::XFree(children_arr); + return false; + }; - ::XFree(children_arr); + unsigned int num_children = 0; + XWindow* children_arr = nullptr; + + XWindow root_return; + XWindow parent_return; - std::set<XWindow> children_children; + int status = ::XQueryTree(display, root, &root_return, &parent_return, &children_arr, &num_children); + if (!status || !children_arr) + return false; // how - if (WalkWindows(display, children_children, children)) - children.insert(children_children.begin(), children_children.end()); + if (num_children < 1) { + ::XFree(children_arr); + return false; } + for (unsigned int i = 0; i < num_children; i++) { + XWindow res; + if (immediate_child_get_toplevel(children_arr[i], res)) + result.insert(res); + } + + ::XFree(children_arr); + return true; } @@ -172,7 +259,7 @@ XWindow root = ::XDefaultRootWindow(display); std::set<XWindow> windows; - WalkWindows(display, windows, {root}); + GetAllTopLevelWindows(display, root, windows); for (const auto& window : windows) { Window win;