diff dep/animia/src/win/x11.cc @ 166:54c5d80a737e

dep/animia: add libutil method I changed the "linux" method to be "proc", because it isn't exactly Linux specific this commit also has some changes to the x11 stuff: instead of enumerating over only top-level windows, we iterate over ALL of them this is because many X11 apps actually use multiple windows for some reason, I still can't get it to work with VLC, but it picks up Firefox...
author paper@DavesDouble.local
date Sun, 19 Nov 2023 04:21:56 -0500
parents 80d6b28eb29f
children 31735c8592bc
line wrap: on
line diff
--- a/dep/animia/src/win/x11.cc	Sat Nov 18 01:12:02 2023 -0500
+++ b/dep/animia/src/win/x11.cc	Sun Nov 19 04:21:56 2023 -0500
@@ -8,7 +8,6 @@
 
 #include <cstdint>
 #include <string>
-#include <memory>
 #include <set>
 
 /* The code for this is very fugly because X11 uses lots of generic type names
@@ -23,12 +22,16 @@
 	unsigned long leftover_bytes, num_of_items;
 	::Atom type;
 	unsigned char* data;
-	if (!::XGetWindowProperty(display, window, atom, 0L, (~0L), False, reqtype,
-	                       &type, &format, &num_of_items, &leftover_bytes, &data))
+
+	int status = ::XGetWindowProperty(display, window, atom, 0L, (~0L), False, reqtype,
+	                                  &type, &format, &num_of_items, &leftover_bytes, &data);
+	if (status != Success || !(reqtype == AnyPropertyType || type == reqtype) || !num_of_items)
 		return false;
 
 	result = std::string((char*)data, num_of_items);
 
+	::XFree(data);
+
 	return true;
 }
 
@@ -39,19 +42,26 @@
 	::Atom atom = ::XInternAtom(display, "_NET_WM_PID", False), type;
 	unsigned char* data;
 
-	if (!::XGetWindowProperty(display, window, atom, 0L, (~0L), False, XA_CARDINAL,
-	                       &type, &format, &num_of_items, &leftover_bytes, &data))
+	int status = ::XGetWindowProperty(display, window, atom, 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;
 
 	result = static_cast<pid_t>(*(uint32_t*)data);
 
+	::XFree(data);
+
 	return true;
 }
 
 static bool FetchName(::Display* display, ::Window 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)))
+		                          result, ::XInternAtom(display, "UTF8_STRING", False)))
+		return true;
+
+	if (GetWindowPropertyAsString(display, window, ::XInternAtom(display, "WM_NAME", False),
+		                           result, XA_STRING))
 		return true;
 
 	/* Fallback to XGetWMName() */
@@ -69,7 +79,7 @@
 		int count;
 
 		int status = ::XmbTextPropertyToTextList(display, &text, &list, &count);
-		if (status < Success || !count || !*list)
+		if (status != Success || !count || !*list)
 			return false;
 	}
 
@@ -82,28 +92,55 @@
 	return true;
 }
 
+static bool WalkWindows(::Display* display, std::set<::Window>& children, const std::set<::Window>& windows) {
+	if (windows.empty())
+		return false;
+
+	for (const ::Window& window : windows) {
+		unsigned int num_children = 0;
+		::Window* children_arr = nullptr;
+
+		::Window root_return;
+		::Window parent_return;
+
+		int status = ::XQueryTree(display, window, &root_return, &parent_return, &children_arr, &num_children);
+		if (status < Success)
+			continue;
+
+		if (num_children < 1) {
+			::XFree(children_arr);
+			continue;
+		}
+
+		for (int i = 0; i < num_children; i++)
+			if (!children.count(children_arr[i]))
+				children.insert(children_arr[i]);
+
+		::XFree(children_arr);
+
+		std::set<::Window> children_children;
+
+		if (!WalkWindows(display, children_children, children))
+			children.insert(children_children.begin(), children_children.end());
+	}
+
+	return true;
+}
+
 bool X11WinTools::EnumerateWindows(window_proc_t window_proc) {
 	if (!window_proc)
 		return false;
 
 	::Display* display = ::XOpenDisplay(nullptr);
+	if (!display)
+		return false;
+
 	::Window root = DefaultRootWindow(display);
 
-	unsigned int num_windows = 0;
-	::Window* windows = nullptr;
-
-	{
-		::Window root_return;
-		::Window parent_return;
+	std::set<::Window> windows;
+	WalkWindows(display, windows, {root});
 
-		int status = ::XQueryTree(display, root, &root_return, &parent_return, &windows, &num_windows);
-		if (status < Success)
-			return false;
-	}
-
-	for (long k = 0; k < num_windows; k++) {
-		const ::Window window = windows[k];
-
+	for (const auto& window : windows) {
 		Window win;
 		win.id = window;
 		{
@@ -113,7 +150,7 @@
 				::XFree(hint);
 			}
 		}
-		FetchName(display, windows[k], win.text);
+		FetchName(display, window, win.text);
 
 		Process proc;
 		GetWindowPID(display, window, proc.pid);
@@ -122,8 +159,6 @@
 			return false;
 	}
 
-	::XFree(windows);
-
 	return true;
 }