1 /++ 2 Cross platform window manager utilities for interacting with other unknown windows on the OS. 3 4 Based on [arsd.simpledisplay]. 5 +/ 6 module arsd.wmutil; 7 8 public import arsd.simpledisplay; 9 10 version(Windows) 11 import core.sys.windows.windows; 12 13 static assert(UsingSimpledisplayX11 || UsingSimpledisplayWindows, "wmutil only works on X11 or Windows"); 14 15 static if (UsingSimpledisplayX11) { 16 extern(C) nothrow @nogc { 17 Atom* XListProperties(Display *display, Window w, int *num_prop_return); 18 Status XGetTextProperty(Display *display, Window w, XTextProperty *text_prop_return, Atom property); 19 Status XQueryTree(Display *display, Window w, Window *root_return, Window *parent_return, Window **children_return, uint *nchildren_return); 20 } 21 } 22 23 /// A foreachable object that iterates window children 24 struct WindowChildrenIterator { 25 NativeWindowHandle parent; 26 27 version(Windows) 28 struct EnumParams { 29 int result; 30 int delegate(NativeWindowHandle) dg; 31 Exception ex; 32 } 33 34 35 36 version(Windows) 37 extern(Windows) 38 nothrow private static int helper(HWND window, LPARAM lparam) { 39 EnumParams* args = cast(EnumParams*)lparam; 40 try { 41 args.result = args.dg(window); 42 if (args.result) 43 return 0; 44 else 45 return 1; 46 } catch (Exception e) { 47 args.ex = e; 48 return 0; 49 } 50 } 51 52 /// 53 int opApply(int delegate(NativeWindowHandle) dg) const { 54 version (Windows) { 55 EnumParams params; 56 57 // the cast is cuz druntime seems to have a wrong definition here, missing the const 58 EnumChildWindows(cast(void*) parent, &helper, cast(LPARAM)¶ms); 59 60 if (params.ex) 61 throw params.ex; 62 63 return params.result; 64 } else static if (UsingSimpledisplayX11) { 65 int result; 66 Window unusedWindow; 67 Window* children; 68 uint numChildren; 69 Status status = XQueryTree(XDisplayConnection.get(), parent, &unusedWindow, &unusedWindow, &children, &numChildren); 70 if (status == 0 || children is null) 71 return 0; 72 scope (exit) 73 XFree(children); 74 75 foreach (window; children[0 .. numChildren]) { 76 result = dg(window); 77 if (result) 78 break; 79 } 80 return result; 81 } else 82 static assert(0); 83 84 } 85 } 86 87 /// 88 WindowChildrenIterator iterateWindows(NativeWindowHandle parent = NativeWindowHandle.init) { 89 static if (UsingSimpledisplayX11) 90 if (parent == NativeWindowHandle.init) 91 parent = RootWindow(XDisplayConnection.get, DefaultScreen(XDisplayConnection.get)); 92 93 return WindowChildrenIterator(parent); 94 } 95 96 /++ 97 Searches for a window with the specified class name and returns the native window handle to it. 98 99 Params: 100 className = the class name to check the window for, case-insensitive. 101 +/ 102 NativeWindowHandle findWindowByClass(string className) { 103 version (Windows) 104 return findWindowByClass(className.toWStringz); 105 else static if (UsingSimpledisplayX11) { 106 import std.algorithm : splitter; 107 import std.uni : sicmp; 108 109 auto classAtom = GetAtom!"WM_CLASS"(XDisplayConnection.get()); 110 Atom returnType; 111 int returnFormat; 112 arch_ulong numItems, bytesAfter; 113 char* strs; 114 foreach (window; iterateWindows) { 115 if (0 == XGetWindowProperty(XDisplayConnection.get(), window, classAtom, 0, 64, false, AnyPropertyType, &returnType, &returnFormat, &numItems, &bytesAfter, cast(void**)&strs)) { 116 scope (exit) 117 XFree(strs); 118 if (returnFormat == 8) { 119 foreach (windowClassName; strs[0 .. numItems].splitter('\0')) { 120 if (sicmp(windowClassName, className) == 0) 121 return window; 122 } 123 } 124 } 125 } 126 return NativeWindowHandle.init; 127 128 } 129 } 130 131 /// ditto 132 version (Windows) 133 NativeWindowHandle findWindowByClass(LPCTSTR className) { 134 return FindWindow(className, null); 135 } 136 137 /++ 138 Get the PID that owns the window. 139 140 Params: 141 window = The window to check who created it 142 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. 143 +/ 144 int ownerPID(NativeWindowHandle window) @property { 145 version (Windows) { 146 DWORD ret; 147 GetWindowThreadProcessId(window, &ret); 148 return cast(int) ret; 149 } else static if (UsingSimpledisplayX11) { 150 auto pidAtom = GetAtom!"_NET_WM_PID"(XDisplayConnection.get()); 151 Atom returnType; 152 int returnFormat; 153 arch_ulong numItems, bytesAfter; 154 uint* ints; 155 if (0 == XGetWindowProperty(XDisplayConnection.get(), window, pidAtom, 0, 1, false, AnyPropertyType, &returnType, &returnFormat, &numItems, &bytesAfter, cast(void**)&ints)) { 156 scope (exit) 157 XFree(ints); 158 if (returnFormat < 64 && numItems > 0) { 159 return *ints; 160 } 161 } 162 return -1; 163 } 164 } 165 166 unittest { 167 import std.stdio; 168 auto window = findWindowByClass("x-terminal-emulator"); 169 writeln("Terminal: ", window.ownerPID); 170 foreach (w; iterateWindows) 171 writeln(w.ownerPID); 172 }