1/* 2 * Copyright 2007 Kim woelders 3 * 4 * Permission to use, copy, modify, distribute, and sell this software and its 5 * documentation for any purpose is hereby granted without fee, provided that 6 * the above copyright notice appear in all copies and that both that copyright 7 * notice and this permission notice appear in supporting documentation, and 8 * that the name of the copyright holders not be used in advertising or 9 * publicity pertaining to distribution of the software without specific, 10 * written prior permission. The copyright holders make no representations 11 * about the suitability of this software for any purpose. It is provided "as 12 * is" without express or implied warranty. 13 * 14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20 * OF THIS SOFTWARE. 21 */ 22#include <X11/Xatom.h> 23#include <X11/Xlib.h> 24 25#include "clientwin.h" 26 27static Atom atom_wm_state = None; 28 29/* 30 * Check if window has given property 31 */ 32static Bool 33Window_Has_Property(Display * dpy, Window win, Atom atom) 34{ 35 Atom type_ret; 36 int format_ret; 37 unsigned char *prop_ret; 38 unsigned long bytes_after, num_ret; 39 40 type_ret = None; 41 prop_ret = NULL; 42 XGetWindowProperty(dpy, win, atom, 0, 0, False, AnyPropertyType, 43 &type_ret, &format_ret, &num_ret, 44 &bytes_after, &prop_ret); 45 if (prop_ret) 46 XFree(prop_ret); 47 48 return (type_ret != None) ? True : False; 49} 50 51/* 52 * Check if window is viewable 53 */ 54static Bool 55Window_Is_Viewable(Display * dpy, Window win) 56{ 57 Bool ok; 58 XWindowAttributes xwa; 59 60 XGetWindowAttributes(dpy, win, &xwa); 61 62 ok = (xwa.class == InputOutput) && (xwa.map_state == IsViewable); 63 64 return ok; 65} 66 67/* 68 * Find a window that has WM_STATE set in the window tree below win. 69 * Unmapped/unviewable windows are not considered valid matches. 70 * Children are searched in top-down stacking order. 71 * The first matching window is returned, None if no match is found. 72 */ 73static Window 74Find_Client_In_Children(Display * dpy, Window win) 75{ 76 Window root, parent; 77 Window *children; 78 unsigned int n_children; 79 int i; 80 81 if (!XQueryTree(dpy, win, &root, &parent, &children, &n_children)) 82 return None; 83 if (!children) 84 return None; 85 86 /* Check each child for WM_STATE and other validity */ 87 win = None; 88 for (i = (int) n_children - 1; i >= 0; i--) { 89 if (!Window_Is_Viewable(dpy, children[i])) { 90 children[i] = None; /* Don't bother descending into this one */ 91 continue; 92 } 93 if (!Window_Has_Property(dpy, children[i], atom_wm_state)) 94 continue; 95 96 /* Got one */ 97 win = children[i]; 98 goto done; 99 } 100 101 /* No children matched, now descend into each child */ 102 for (i = (int) n_children - 1; i >= 0; i--) { 103 if (children[i] == None) 104 continue; 105 win = Find_Client_In_Children(dpy, children[i]); 106 if (win != None) 107 break; 108 } 109 110 done: 111 XFree(children); 112 113 return win; 114} 115 116/* 117 * Find virtual roots (_NET_VIRTUAL_ROOTS) 118 */ 119static unsigned long * 120Find_Roots(Display * dpy, Window root, unsigned int *num) 121{ 122 Atom type_ret; 123 int format_ret; 124 unsigned char *prop_ret; 125 unsigned long bytes_after, num_ret; 126 Atom atom; 127 128 *num = 0; 129 atom = XInternAtom(dpy, "_NET_VIRTUAL_ROOTS", False); 130 if (!atom) 131 return NULL; 132 133 type_ret = None; 134 prop_ret = NULL; 135 if (XGetWindowProperty(dpy, root, atom, 0, 0x7fffffff, False, 136 XA_WINDOW, &type_ret, &format_ret, &num_ret, 137 &bytes_after, &prop_ret) != Success) 138 return NULL; 139 140 if (prop_ret && type_ret == XA_WINDOW && format_ret == 32) { 141 *num = num_ret; 142 return ((unsigned long *) prop_ret); 143 } 144 if (prop_ret) 145 XFree(prop_ret); 146 147 return NULL; 148} 149 150/* 151 * Find child window at pointer location 152 */ 153static Window 154Find_Child_At_Pointer(Display * dpy, Window win) 155{ 156 Window root_return, child_return; 157 int dummyi; 158 unsigned int dummyu; 159 160 XQueryPointer(dpy, win, &root_return, &child_return, 161 &dummyi, &dummyi, &dummyi, &dummyi, &dummyu); 162 163 return child_return; 164} 165 166/* 167 * Find client window at pointer location 168 * 169 * root is the root window. 170 * subwin is the subwindow reported by a ButtonPress event on root. 171 * 172 * If the WM uses virtual roots subwin may be a virtual root. 173 * If so, we descend the window stack at the pointer location and assume the 174 * child is the client or one of its WM frame windows. 175 * This will of course work only if the virtual roots are children of the real 176 * root. 177 */ 178Window 179Find_Client(Display * dpy, Window root, Window subwin) 180{ 181 unsigned long *roots; 182 unsigned int i, n_roots; 183 Window win; 184 185 /* Check if subwin is a virtual root */ 186 roots = Find_Roots(dpy, root, &n_roots); 187 for (i = 0; i < n_roots; i++) { 188 if (subwin != roots[i]) 189 continue; 190 win = Find_Child_At_Pointer(dpy, subwin); 191 if (win == None) 192 return subwin; /* No child - Return virtual root. */ 193 subwin = win; 194 break; 195 } 196 if (roots) 197 XFree(roots); 198 199 if (atom_wm_state == None) { 200 atom_wm_state = XInternAtom(dpy, "WM_STATE", False); 201 if (!atom_wm_state) 202 return subwin; 203 } 204 205 /* Check if subwin has WM_STATE */ 206 if (Window_Has_Property(dpy, subwin, atom_wm_state)) 207 return subwin; 208 209 /* Attempt to find a client window in subwin's children */ 210 win = Find_Client_In_Children(dpy, subwin); 211 if (win != None) 212 return win; /* Found a client */ 213 214 /* Did not find a client */ 215 return subwin; 216} 217