list windows: first version, --list-windows v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Mon, 05 Apr 2021 15:37:28 +0200
branchv_0
changeset 7 039b3f8a3442
parent 6 1b17b8cdbfc3
child 8 b498160cc2ab
list windows: first version, --list-windows
bash-completion.sh
src/CLIParser.h
src/Configuration.h
src/X11Command.h
--- a/bash-completion.sh	Wed Apr 07 22:55:00 2021 +0200
+++ b/bash-completion.sh	Mon Apr 05 15:37:28 2021 +0200
@@ -29,10 +29,12 @@
 
 	  if [[ "$w1" == "--list-input-devices"                             ]];    then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0"))
 	elif [[ "$w1" == "--list-input-events"                              ]];    then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0"))
+	elif [[ "$w1" == "--list-windows"                                   ]];    then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0"))
 	else
 		OPTIONS=(
 			"--list-input-devices"
 			"--list-input-events"
+			"--list-windows"
 		)
 		COMPREPLY=($(compgen -W "${OPTIONS[*]}" -- "$w0"))
 	fi
--- a/src/CLIParser.h	Wed Apr 07 22:55:00 2021 +0200
+++ b/src/CLIParser.h	Mon Apr 05 15:37:28 2021 +0200
@@ -50,6 +50,7 @@
 
 	static const relpipe::common::type::StringX OPTION_LIST_INPUT_DEVICES;
 	static const relpipe::common::type::StringX OPTION_LIST_INPUT_EVENTS;
+	static const relpipe::common::type::StringX OPTION_LIST_WINDOWS;
 
 	Configuration parse(const std::vector<relpipe::common::type::StringX>& arguments) {
 		Configuration c;
@@ -63,6 +64,8 @@
 				c.listInputDevices = parseBoolean(readNext(arguments, i));
 			} else if (option == OPTION_LIST_INPUT_EVENTS) {
 				c.listInputEvents = parseBoolean(readNext(arguments, i));
+			} else if (option == OPTION_LIST_WINDOWS) {
+				c.listWindows = parseBoolean(readNext(arguments, i));
 			} else throw relpipe::cli::RelpipeCLIException(L"Unsupported CLI option: " + option, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
 		}
 
@@ -75,6 +78,7 @@
 
 const relpipe::common::type::StringX CLIParser::OPTION_LIST_INPUT_DEVICES = L"--list-input-devices";
 const relpipe::common::type::StringX CLIParser::OPTION_LIST_INPUT_EVENTS = L"--list-input-events";
+const relpipe::common::type::StringX CLIParser::OPTION_LIST_WINDOWS = L"--list-windows";
 
 }
 }
--- a/src/Configuration.h	Wed Apr 07 22:55:00 2021 +0200
+++ b/src/Configuration.h	Mon Apr 05 15:37:28 2021 +0200
@@ -30,6 +30,7 @@
 public:
 	bool listInputDevices = false;
 	bool listInputEvents = false;
+	bool listWindows = false;
 
 	virtual ~Configuration() {
 	}
--- a/src/X11Command.h	Wed Apr 07 22:55:00 2021 +0200
+++ b/src/X11Command.h	Mon Apr 05 15:37:28 2021 +0200
@@ -23,6 +23,7 @@
 #include <stdexcept>
 
 #include <X11/extensions/XInput.h>
+#include <X11/Xutil.h>
 
 #include "Configuration.h"
 
@@ -262,6 +263,79 @@
 		}
 	}
 
+	relpipe::common::type::StringX fetchWindowName(const Display& display, Window window) {
+		relpipe::common::type::StringX name;
+		// TODO: this does not work → use XGetWMName(), XmbTextPropertyToTextList(), XFreeStringList() instead of XFetchName()
+		// char* rawName = nullptr;
+		// if (XFetchName(display.display, window, &rawName) && rawName) 
+		// name = convertor.from_bytes(rawName); // bad encoding, std::range_error – wstring_convert::from_bytes
+		// std::cout << "\n\n\n>>> " << rawName << " <<<\n\n\n"; // bad encoding, not UTF-8
+		//XFree(rawName);
+		return name;
+	}
+
+	void listWindow(const Display& display, Window window, relpipe::common::type::Integer level, Configuration& configuration, std::shared_ptr<writer::RelationalWriter> writer, std::function<void() > relationalWriterFlush) {
+		Window rootWindow;
+		Window parentWindow;
+		unsigned int childrenCount;
+		Window* childrenList;
+		if (!XQueryTree(display.display, window, &rootWindow, &parentWindow, &childrenList, &childrenCount))
+			throw std::invalid_argument("Unable to query the tree for window: " + std::to_string(window));
+
+		XWindowAttributes windowAttributes;
+		if (!XGetWindowAttributes(display.display, window, &windowAttributes))
+			throw std::invalid_argument("Unable to get attributes for window: " + std::to_string(window));
+
+		XClassHint classHint;
+		relpipe::common::type::StringX resClass;
+		relpipe::common::type::StringX resName;
+		if (XGetClassHint(display.display, window, &classHint)) {
+			if (classHint.res_class) resClass = convertor.from_bytes(classHint.res_class);
+			if (classHint.res_name) resName = convertor.from_bytes(classHint.res_name);
+			XFree(classHint.res_class);
+			XFree(classHint.res_name);
+		}
+
+		writer->writeAttribute(std::to_wstring(window));
+		// writer->writeAttribute(std::to_wstring(screen?));
+		// writer->writeAttribute(std::to_wstring(root_win));
+		writer->writeAttribute(std::to_wstring(parentWindow));
+		writer->writeAttribute(&level, typeid (level));
+		writer->writeAttribute(fetchWindowName(display, window));
+		writer->writeAttribute(resClass);
+		writer->writeAttribute(resName);
+		writer->writeAttribute(std::to_wstring(windowAttributes.x));
+		writer->writeAttribute(std::to_wstring(windowAttributes.y));
+		writer->writeAttribute(std::to_wstring(windowAttributes.width));
+		writer->writeAttribute(std::to_wstring(windowAttributes.height));
+
+		for (unsigned int i = 0; i < childrenCount; i++) listWindow(display, childrenList[i], level + 1, configuration, writer, relationalWriterFlush);
+
+		XFree(childrenList);
+	}
+
+	void listWindows(const Display& display, Configuration& configuration, std::shared_ptr<writer::RelationalWriter> writer, std::function<void() > relationalWriterFlush) {
+		writer->startRelation(L"x11_window",{
+			{L"id", relpipe::writer::TypeId::INTEGER},
+			// {L"screen", relpipe::writer::TypeId::INTEGER},
+			// {L"root", relpipe::writer::TypeId::INTEGER},
+			{L"parent", relpipe::writer::TypeId::INTEGER},
+			{L"level", relpipe::writer::TypeId::INTEGER},
+			{L"name", relpipe::writer::TypeId::STRING},
+			{L"res_class", relpipe::writer::TypeId::STRING},
+			{L"res_name", relpipe::writer::TypeId::STRING},
+			{L"x", relpipe::writer::TypeId::INTEGER},
+			{L"y", relpipe::writer::TypeId::INTEGER},
+			{L"width", relpipe::writer::TypeId::INTEGER},
+			{L"height", relpipe::writer::TypeId::INTEGER},
+		}, true);
+
+		int screen = DefaultScreen(display.display);
+		Window window = RootWindow(display.display, screen);
+
+		listWindow(display, window, 0, configuration, writer, relationalWriterFlush);
+	}
+
 	static int handleXError(::Display* display, XErrorEvent* errorEvent) {
 		std::wcerr << L"X11 error:"
 				<< L" display=" << errorEvent->display
@@ -287,6 +361,8 @@
 		if (display.display) {
 			if (configuration.listInputDevices) listInputDevices(display, configuration, writer, relationalWriterFlush);
 			if (configuration.listInputEvents) listInputEvents(display, configuration, writer, relationalWriterFlush);
+			if (configuration.listWindows) listWindows(display, configuration, writer, relationalWriterFlush);
+			// TODO: list screens
 		} else {
 			throw std::invalid_argument("Unable to open display. Please check the $DISPLAY variable.");
 		}