view dep/animia/src/win/x11.cc @ 156:cdf79282d647

dep/animia: add VERY early x11 window stuff
author Paper <mrpapersonic@gmail.com>
date Wed, 15 Nov 2023 18:04:04 -0500
parents
children 18c8eb5d1edc
line wrap: on
line source

#include <X11/Xlib.h>

/* 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 {

static bool GetWindowPropertyAsString(::Display* display, ::Window window, const char* atom, std::string& result, ::Atom reqtype = AnyPropertyType) {
	int format;
	unsigned long leftover_bytes, num_of_items;
	::Atom type;
	void* data;

	if (!::XGetWindowProperty(display, root, ::XInternAtom(display, atom, true), 0L, (~0L), false, reqtype,
	                            &type, &format, &num_of_items, &leftover_bytes, &data))
		return false;

	result = std::string(data, num_of_items);

	::XFree(data);

	return true;
}

static bool GetWindowPID(::Display* display, ::Window window, pid_t& result) {
	int format;
	unsigned long leftover_bytes, num_of_items;
	::Atom type;
	void* data;

	if (!::XGetWindowProperty(display, root, ::XInternAtom(display, atom, true), 0L, (~0L), false, reqtype,
	                            &type, &format, &num_of_items, &leftover_bytes, &data))
		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, "_NET_WM_NAME", result, ::XInternAtom(display, "UTF8_STRING", false)))
		return true;

	/* Fallback to XGetWMName() */
	XTextProperty text;

	{
		int status = ::XGetWMName(display, window, &text);
		if (!status || !text_prop.value || !text_prop.nitems)
			return false;
	}

	char** list;

	{
		int num;

		int status = ::XmbTextPropertyToTextList(display, &text, &list, &num);
		if (status < Success || !num || !*list)
			return false;
	}

	::XFree(text.value);

	result = *list;

	::XFreeStringList(list);

	return true;
}

bool X11WinTools::EnumerateWindows(window_proc_t window_proc) {
	auto get_window_property = [&](Display* display, Window window, Atom atom, unsigned long& num_of_items, void*& data) -> int {
		int format;
		unsigned long leftover_bytes;
		::Atom realtype;

		return ::XGetWindowProperty(display, root, atom, 0L, (~0L), false, AnyPropertyType,
		                            &realtype, &format, &num_of_items, &leftover_bytes, &data);
	}

	::Display* display = ::XOpenDisplay(nullptr);
	::Window root = ::DefaultRootWindow(display);

	unsigned long num_windows;
	::Window* windows = nullptr;

	int status = get_window_property(display, root, ::XInternAtom(display, "_NET_CLIENT_LIST", true), num_windows, (void*)windows);

	if (status < Success)
		return false;

	for (long k = 0; k < num_windows; k++) {
		const ::Window window = windows[k];

		Window win;
		win.id = (long)windows[k];
		GetWindowPropertyAsString(display, window, "_NET_ACTIVE_WINDOW", win.class_name, ::XInternAtom(display, "STRING", false));
		win.title = FetchName(display, windows[k]);

		Process proc;
		GetWindowPID(display, window, proc.pid);

		if (!window_proc(proc, win))
			return false;
	}

	return true;
}

}