From 3300a69966ed481f595ea226bf92ccb8ca928ba1 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Thu, 8 Mar 2018 21:45:17 +0100 Subject: [PATCH 1/3] Added new wmutil module Contains findWindowByClass and ownerPID for now, planning on adding more window getters --- simpledisplay.d | 12 ++++ wmutil.d | 151 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 wmutil.d diff --git a/simpledisplay.d b/simpledisplay.d index 2c645ab..d4bbd88 100644 --- a/simpledisplay.d +++ b/simpledisplay.d @@ -969,12 +969,24 @@ version(Windows) version(linux) version = with_timer; +/// If you have to get down and dirty with implementation details, this helps figure out if Windows is available you can `static if(UsingSimpledisplayWindows) ...` more reliably than `version()` because `version` is module-local. +version(Windows) + enum bool UsingSimpledisplayWindows = true; +else + enum bool UsingSimpledisplayWindows = false; + /// If you have to get down and dirty with implementation details, this helps figure out if X is available you can `static if(UsingSimpledisplayX11) ...` more reliably than `version()` because `version` is module-local. version(X11) enum bool UsingSimpledisplayX11 = true; else enum bool UsingSimpledisplayX11 = false; +/// If you have to get down and dirty with implementation details, this helps figure out if Cocoa is available you can `static if(UsingSimpledisplayCocoa) ...` more reliably than `version()` because `version` is module-local. +version(OSXCocoa) + enum bool UsingSimpledisplayCocoa = true; +else + enum bool UsingSimpledisplayCocoa = false; + /// Does this platform support multiple windows? If not, trying to create another will cause it to throw an exception. version(Windows) enum multipleWindowsSupported = true; diff --git a/wmutil.d b/wmutil.d new file mode 100644 index 0000000..eeb8171 --- /dev/null +++ b/wmutil.d @@ -0,0 +1,151 @@ +/++ + Cross platform window manager utilities for interacting with other unknown windows on the OS. + + Based on arsd.simpledisplay. ++/ +module arsd.wmutil; + +public import arsd.simpledisplay; + +static assert(UsingSimpledisplayX11 || UsingSimpledisplayWindows, "wmutil only works on X11 or Windows"); + +static if (UsingSimpledisplayX11) +{ +extern(C) nothrow @nogc { +Atom* XListProperties(Display *display, Window w, int *num_prop_return); +Status XGetTextProperty(Display *display, Window w, XTextProperty *text_prop_return, Atom property); +Status XQueryTree(Display *display, Window w, Window *root_return, Window *parent_return, Window **children_return, uint *nchildren_return); +} +} + +struct WindowChildrenIterator { + NativeWindowHandle parent; + + int opApply(int delegate(NativeWindowHandle) dg) const { + version (Windows) { + struct EnumParams { + int result; + int delegate(NativeWindowHandle) dg; + Exception ex; + } + + EnumParams params; + + EnumWindows(function (window, lparam) nothrow { + EnumParams* args = cast(EnumParams*)lparam; + try { + args.result = args.dg(window); + if (args.result) + return 0; + else + return 1; + } catch (Exception e) { + args.ex = e; + return 0; + } + }, cast(LPARAM)¶ms); + + if (params.ex) + throw params.ex; + + return params.result; + } else static if (UsingSimpledisplayX11) { + int result; + Window unusedWindow; + Window* children; + uint numChildren; + Status status = XQueryTree(XDisplayConnection.get(), RootWindow(XDisplayConnection.get, DefaultScreen(XDisplayConnection.get)), + &unusedWindow, &unusedWindow, &children, &numChildren); + if (status == 0 || children is null) + return 0; + scope (exit) + XFree(children); + + foreach (window; children[0 .. numChildren]) { + result = dg(window); + if (result) + break; + } + return result; + } else + throw new NotYetImplementedException(); + + } +} + +WindowChildrenIterator iterateWindows(NativeWindowHandle parent = NativeWindowHandle.init) { + return WindowChildrenIterator(parent); +} + +/++ + Searches for a window with the specified class name and returns the native window handle to it. + + Params: + className = the class name to check the window for, case-insensitive. ++/ +version (Windows) +NativeWindowHandle findWindowByClass(LPCTSTR className) { + return FindWindow(className, null); +} + +/// ditto +version (Windows) +NativeWindowHandle findWindowByClass(string className) { + return findWindowByClass(className.toWStringz); +} + +/// ditto +static if (UsingSimpledisplayX11) +NativeWindowHandle findWindowByClass(string className) { + import std.algorithm : splitter; + import std.uni : sicmp; + + auto classAtom = GetAtom!"WM_CLASS"(XDisplayConnection.get()); + Atom returnType; + int returnFormat; + arch_ulong numItems, bytesAfter; + char* strs; + foreach (window; iterateWindows) { + if (0 == XGetWindowProperty(XDisplayConnection.get(), window, classAtom, 0, 64, false, AnyPropertyType, &returnType, &returnFormat, &numItems, &bytesAfter, cast(void**)&strs)) { + scope (exit) + XFree(strs); + if (returnFormat == 8) { + foreach (windowClassName; strs[0 .. numItems].splitter('\0')) { + if (sicmp(windowClassName, className) == 0) + return window; + } + } + } + } + return NativeWindowHandle.init; +} + +/++ + Params: + window = The window to check who created it + Returns: the PID of the owner who created this window. On windows this will always work and be accurate. On X11 this might return -1 if none is specified and might not actually be the actual owner. ++/ +version (Windows) +int ownerPID(NativeWindowHandle window) @property { + DWORD ret; + GetWindowThreadProcessId(window, &ret); + return cast(int)ret; +} + +/// ditto +static if (UsingSimpledisplayX11) +int ownerPID(NativeWindowHandle window) @property { + auto pidAtom = GetAtom!"_NET_WM_PID"(XDisplayConnection.get()); + Atom returnType; + int returnFormat; + arch_ulong numItems, bytesAfter; + uint* ints; + if (0 == XGetWindowProperty(XDisplayConnection.get(), window, pidAtom, 0, 1, false, AnyPropertyType, &returnType, &returnFormat, &numItems, &bytesAfter, cast(void**)&ints)) { + scope (exit) + XFree(ints); + if (returnFormat < 64 && numItems > 0) { + return *ints; + } + } + return -1; +} From 652b30151f7b43298b3dac17f016a9977b298609 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Thu, 8 Mar 2018 21:48:35 +0100 Subject: [PATCH 2/3] Made iterateWindow actually use parent window --- wmutil.d | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/wmutil.d b/wmutil.d index eeb8171..e83edbd 100644 --- a/wmutil.d +++ b/wmutil.d @@ -31,7 +31,7 @@ struct WindowChildrenIterator { EnumParams params; - EnumWindows(function (window, lparam) nothrow { + EnumChildWindows(parent, function (window, lparam) nothrow { EnumParams* args = cast(EnumParams*)lparam; try { args.result = args.dg(window); @@ -54,8 +54,7 @@ struct WindowChildrenIterator { Window unusedWindow; Window* children; uint numChildren; - Status status = XQueryTree(XDisplayConnection.get(), RootWindow(XDisplayConnection.get, DefaultScreen(XDisplayConnection.get)), - &unusedWindow, &unusedWindow, &children, &numChildren); + Status status = XQueryTree(XDisplayConnection.get(), parent, &unusedWindow, &unusedWindow, &children, &numChildren); if (status == 0 || children is null) return 0; scope (exit) @@ -149,3 +148,9 @@ int ownerPID(NativeWindowHandle window) @property { } return -1; } + +unittest { + import std.stdio; + auto window = findWindowByClass("x-terminal-emulator"); + writeln("Terminal: ", window.ownerPID); +} From 8e61ca75a33501bb2a1fa9937b9575fd6b4f20d1 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Thu, 8 Mar 2018 21:51:50 +0100 Subject: [PATCH 3/3] Fix iterateWindows on X11 with no param not iterating over root --- wmutil.d | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wmutil.d b/wmutil.d index e83edbd..adbce21 100644 --- a/wmutil.d +++ b/wmutil.d @@ -73,6 +73,10 @@ struct WindowChildrenIterator { } WindowChildrenIterator iterateWindows(NativeWindowHandle parent = NativeWindowHandle.init) { + static if (UsingSimpledisplayX11) + if (parent == NativeWindowHandle.init) + parent = RootWindow(XDisplayConnection.get, DefaultScreen(XDisplayConnection.get)); + return WindowChildrenIterator(parent); } @@ -153,4 +157,6 @@ unittest { import std.stdio; auto window = findWindowByClass("x-terminal-emulator"); writeln("Terminal: ", window.ownerPID); + foreach (w; iterateWindows) + writeln(w.ownerPID); }