diff dep/animia/src/win/x11.cc @ 236:4d461ef7d424

HUGE UPDATE: convert build system to autotools why? because cmake sucks :)
author Paper <mrpapersonic@gmail.com>
date Fri, 19 Jan 2024 00:24:02 -0500
parents 593108b3d555
children
line wrap: on
line diff
--- a/dep/animia/src/win/x11.cc	Tue Jan 16 15:22:29 2024 -0500
+++ b/dep/animia/src/win/x11.cc	Fri Jan 19 00:24:02 2024 -0500
@@ -1,11 +1,3 @@
-/*
- * win/x11.cc: provides support for X11 clients via XCB
- *
- * This code is fairly fast (I think...). As a result,
- * a lot of it is hard to read if you're unfamiliar with
- * asynchronous programming, but it works much better than
- * Xlib (which can take much longer).
-*/
 #include "animia/win/x11.h"
 #include "animia/win.h"
 #include "animia.h"
@@ -19,6 +11,10 @@
 #include <string>
 #include <set>
 
+#include <chrono>
+
+#include <iostream>
+
 static size_t str_nlen(const char* s, size_t len) {
     size_t i = 0;
     for (; i < len && s[i]; i++);
@@ -27,6 +23,32 @@
 
 namespace animia::internal::x11 {
 
+static void GetWindowPID(xcb_connection_t* connection, const std::vector<xcb_window_t>& windows, std::unordered_map<xcb_window_t, pid_t>& result) {
+	std::vector<xcb_res_query_client_ids_cookie_t> cookies;
+	cookies.reserve(windows.size());
+
+	for (const auto& window : windows) {
+		xcb_res_client_id_spec_t spec = {
+			.client = window,
+			.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID
+		};
+
+		cookies.push_back(::xcb_res_query_client_ids(connection, 1, &spec));
+	}
+
+	for (size_t i = 0; i < cookies.size(); i++) {
+		xcb_res_query_client_ids_reply_t* reply = ::xcb_res_query_client_ids_reply(connection, cookies.at(i), NULL);
+
+		xcb_res_client_id_value_iterator_t it = xcb_res_query_client_ids_ids_iterator(reply);
+		for (; it.rem; xcb_res_client_id_value_next(&it)) {
+			if (it.data->spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
+				result[windows.at(i)] = *::xcb_res_client_id_value_value(it.data);
+				continue;
+			}
+		}
+	}
+}
+
 static bool GetAllTopLevelWindowsEWMH(xcb_connection_t* connection, const std::vector<xcb_window_t>& roots, std::set<xcb_window_t>& result) {
 	const xcb_atom_t Atom__NET_CLIENT_LIST = [connection]{
 		static constexpr std::string_view name = "_NET_CLIENT_LIST";
@@ -50,25 +72,23 @@
 
 	for (const auto& cookie : cookies) {
 		xcb_get_property_reply_t* reply = ::xcb_get_property_reply(connection, cookie, NULL);
-
-		if (reply && reply->length == 32) {
+		if (reply) {
 			xcb_window_t* value = reinterpret_cast<xcb_window_t*>(::xcb_get_property_value(reply));
 			int len = ::xcb_get_property_value_length(reply);
 
 			for (size_t i = 0; i < len; i++)
 				result.insert(value[i]);
-
 			success = true;
 		}
-
 		free(reply);
 	}
 
 	return success;
 }
 
+/* I have no idea why this works. */
 static bool WalkWindows(xcb_connection_t* connection, const std::vector<xcb_window_t>& roots, std::set<xcb_window_t>& result) {
-	/* move this somewhere else pl0x */
+	/* move this somewhere */
 	xcb_atom_t Atom_WM_STATE = [connection]{
 		static constexpr std::string_view name = "WM_STATE";
 		xcb_intern_atom_cookie_t cookie = ::xcb_intern_atom(connection, true, name.size(), name.data());
@@ -81,98 +101,49 @@
 	if (Atom_WM_STATE == XCB_ATOM_NONE)
 		return false;
 
-	/* for each toplevel: search recursively for */
-	std::function<bool(const xcb_window_t*, const int, xcb_window_t&)> find_wm_state_window = [&](const xcb_window_t* wins, const int wins_len, xcb_window_t& out) {
-		/* Check for wm_state */
-		{
-			std::vector<xcb_get_property_cookie_t> property_cookies;
-			property_cookies.reserve(wins_len);
-
-			for (size_t j = 0; j < wins_len; j++)
-				property_cookies.push_back(::xcb_get_property(connection, 0, wins[j], Atom_WM_STATE, Atom_WM_STATE, 0, 0));
-
-			for (size_t j = 0; j < property_cookies.size(); j++) {
-				xcb_generic_error_t* err = NULL;
-				xcb_get_property_reply_t* reply = ::xcb_get_property_reply(connection, property_cookies.at(j), &err);
-
-				if (reply->format || reply->type || reply->length) {
-					out = wins[j];
-					free(reply);
-					return true;
-				}
-
-				free(reply);
-			}
-		}
-
-		/* Query tree for recursion */
-		{
-			std::vector<xcb_query_tree_cookie_t> cookies;
-			cookies.reserve(wins_len);
-
-			for (size_t j = 0; j < wins_len; j++)
-				cookies.push_back(::xcb_query_tree(connection, wins[j]));
-
-			for (const auto& cookie : cookies) {
-				xcb_query_tree_reply_t* reply = ::xcb_query_tree_reply(connection, cookie, NULL);
-
-				xcb_window_t* windows = ::xcb_query_tree_children(reply);
-				int len = ::xcb_query_tree_children_length(reply);
-
-				if (find_wm_state_window(windows, len, out)) {
-					free(reply);
-					return true;
-				}
-
-				free(reply);
-			}
-		}
-
-		return false;
-	};
-
-	/* Get the tree for each root */
 	std::vector<xcb_query_tree_cookie_t> cookies;
 	cookies.reserve(roots.size());
 
 	for (const auto& root : roots)
 		cookies.push_back(::xcb_query_tree(connection, root));
 
-	for (size_t i = 0; i < cookies.size(); i++) {
-		xcb_query_tree_reply_t* reply = ::xcb_query_tree_reply(connection, cookies.at(i), NULL);
+	for (const auto& cookie : cookies) {
+		xcb_query_tree_reply_t* reply = ::xcb_query_tree_reply(connection, cookie, NULL);
 
-		xcb_window_t* windows = ::xcb_query_tree_children(reply);
-		int len = ::xcb_query_tree_children_length(reply);
-		if (len < 1)
-			continue;
+		std::vector<xcb_window_t> windows = [reply]{
+			xcb_window_t* windows = ::xcb_query_tree_children(reply);
+			int len = ::xcb_query_tree_children_length(reply);
 
-		/* Then get the tree of each child window. */
-		std::vector<xcb_query_tree_cookie_t> cookies;
-		cookies.reserve(len);
+			std::vector<xcb_window_t> w;
+			w.reserve(len);
+
+			for (int i = 0; i < len; i++)
+				w.push_back(windows[i]);
 
-		for (size_t j = 0; j < len; j++)
-			cookies.push_back(::xcb_query_tree(connection, windows[j]));
+			return w;
+		}();
+
+		std::vector<xcb_get_property_cookie_t> state_property_cookies;
+		state_property_cookies.reserve(windows.size());
 
-		/* For each child window... */
-		for (size_t j = 0; j < cookies.size(); j++) {
-			xcb_query_tree_reply_t* reply = ::xcb_query_tree_reply(connection, cookies.at(j), NULL);
+		for (int i = 0; i < windows.size(); i++)
+			state_property_cookies.push_back(::xcb_get_property(connection, 0, windows[i], Atom_WM_STATE, Atom_WM_STATE, 0, 0));
 
-			xcb_window_t* children = ::xcb_query_tree_children(reply);
-			int children_len = ::xcb_query_tree_children_length(reply);
-			if (children_len < 1) {
-				result.insert(windows[j]);
+		for (size_t i = 0; i < state_property_cookies.size(); i++) {
+			xcb_generic_error_t* err = NULL;
+			xcb_get_property_reply_t* reply = ::xcb_get_property_reply(connection, state_property_cookies.at(i), &err);
+
+			if (reply->format || reply->type || reply->length) {
+				result.insert(windows[i]);
+				free(reply);
 				continue;
 			}
 
-			xcb_window_t out = windows[j];
-			/* Search recursively for a window with WM_STATE. If we don't,
-			 * just add the toplevel.
-			*/
-			find_wm_state_window(children, children_len, out);
-			result.insert(out);
+			free(reply);
 		}
 
-		free(reply);
+		if (WalkWindows(connection, windows, result))
+			continue;
 	}
 
 	return false;
@@ -186,27 +157,23 @@
 	if (!connection)
 		return false;
 
-	std::vector<xcb_screen_t> screens;
-
+	std::set<xcb_window_t> windows;
 	{
-		xcb_screen_iterator_t iter = ::xcb_setup_roots_iterator(xcb_get_setup(connection));
-		for (; iter.rem; ::xcb_screen_next(&iter))
-			screens.push_back(*iter.data);
-	}
+		std::vector<xcb_window_t> roots;
+		{
+			xcb_screen_iterator_t iter = ::xcb_setup_roots_iterator(xcb_get_setup(connection));
+			for (; iter.rem; ::xcb_screen_next(&iter))
+				roots.push_back(iter.data->root);
+		}
 
-	std::vector<xcb_window_t> roots;
-	roots.reserve(screens.size());
-
-	for (const auto& screen : screens)
-		roots.push_back(screen.root);
-
-	std::set<xcb_window_t> windows;
-	if (!GetAllTopLevelWindowsEWMH(connection, roots, windows))
-		WalkWindows(connection, roots, windows);
+		
+		if (!GetAllTopLevelWindowsEWMH(connection, roots, windows))
+			WalkWindows(connection, roots, windows);
+	}
 
 	std::vector<xcb_get_property_cookie_t> class_property_cookies;
 	std::vector<xcb_get_property_cookie_t> name_property_cookies;
-	std::vector<xcb_res_query_client_ids_cookie_t> pid_property_cookies;
+	std::vector<xcb_get_property_cookie_t> pid_property_cookies;
 	class_property_cookies.reserve(windows.size());
 	name_property_cookies.reserve(windows.size());
 	pid_property_cookies.reserve(windows.size());
@@ -214,13 +181,7 @@
 	for (const auto& window : windows) {
 		class_property_cookies.push_back(::xcb_get_property(connection, 0, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0L, 2048L));
 		name_property_cookies.push_back(::xcb_get_property(connection, 0, window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0L, UINT_MAX));
-
-		xcb_res_client_id_spec_t spec = {
-			.client = window,
-			.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID
-		};
-
-		pid_property_cookies.push_back(::xcb_res_query_client_ids(connection, 1, &spec));
+		pid_property_cookies.push_back(::xcb_get_property(connection, 0, window, XCB_ATOM_WM_NAME, XCB_ATOM_CARDINAL, 0L, 1L));
 	}
 
 	size_t i = 0;
@@ -229,7 +190,6 @@
 		win.id = window;
 		{
 			xcb_get_property_reply_t* reply = ::xcb_get_property_reply(connection, class_property_cookies.at(i), NULL);
-
 			if (reply && reply->format == 8) {
 				const char* data = reinterpret_cast<char*>(::xcb_get_property_value(reply));
 				const int data_len = ::xcb_get_property_value_length(reply);
@@ -239,38 +199,26 @@
 
 				win.class_name = std::string(class_name, str_nlen(class_name, data_len - (instance_len + 1)));
 			}
-
 			free(reply);
 		}
 		{
 			xcb_get_property_reply_t* reply = ::xcb_get_property_reply(connection, name_property_cookies.at(i), NULL);
-
-			if (reply && reply->format == 8) {
+			if (reply) {
 				const char* data = reinterpret_cast<char*>(::xcb_get_property_value(reply));
 				int len = ::xcb_get_property_value_length(reply);
 
-				win.text = std::string(data, len);
+				win.text = std::string((char*)data, len);
 			}
-
 			free(reply);
 		}
 		Process proc = {0};
 		{
-			xcb_res_query_client_ids_reply_t* reply = ::xcb_res_query_client_ids_reply(connection, pid_property_cookies.at(i), NULL);
-
-			if (reply->length) {
-				xcb_res_client_id_value_iterator_t it = ::xcb_res_query_client_ids_ids_iterator(reply);
-				for (; it.rem; ::xcb_res_client_id_value_next(&it)) {
-					if (it.data->spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
-						proc.pid = *::xcb_res_client_id_value_value(it.data);
-						break;
-					}
-				}
-			}
+			xcb_get_property_reply_t* reply = ::xcb_get_property_reply(connection, pid_property_cookies.at(i), NULL);
+			if (reply)
+				proc.pid = *reinterpret_cast<uint32_t*>(::xcb_get_property_value(reply));
 
 			free(reply);
 		}
-
 		if (!window_proc(proc, win)) {
 			::xcb_disconnect(connection);
 			return false;