clientwin.c revision c175af84
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 <xcb/xcb.h> 23#include <xcb/xproto.h> 24 25#include <stdlib.h> 26#include <string.h> 27 28#include "clientwin.h" 29#include "dsimple.h" 30 31static xcb_atom_t atom_wm_state = XCB_ATOM_NONE; 32 33/* 34 * Check if window has given property 35 */ 36static Bool 37Window_Has_Property(xcb_connection_t * dpy, xcb_window_t win, xcb_atom_t atom) 38{ 39 xcb_get_property_cookie_t prop_cookie; 40 xcb_get_property_reply_t *prop_reply; 41 42 prop_cookie = xcb_get_property (dpy, False, win, atom, 43 XCB_GET_PROPERTY_TYPE_ANY, 0, 0); 44 45 prop_reply = xcb_get_property_reply (dpy, prop_cookie, NULL); 46 47 if (prop_reply) { 48 xcb_atom_t reply_type = prop_reply->type; 49 free (prop_reply); 50 if (reply_type != XCB_NONE) 51 return True; 52 } 53 54 return False; 55} 56 57/* 58 * Check if window is viewable 59 */ 60static Bool 61Window_Is_Viewable(xcb_connection_t * dpy, xcb_window_t win) 62{ 63 Bool ok = False; 64 xcb_get_window_attributes_cookie_t attr_cookie; 65 xcb_get_window_attributes_reply_t *xwa; 66 67 attr_cookie = xcb_get_window_attributes (dpy, win); 68 xwa = xcb_get_window_attributes_reply (dpy, attr_cookie, NULL); 69 70 if (xwa) { 71 ok = (xwa->_class == XCB_WINDOW_CLASS_INPUT_OUTPUT) && 72 (xwa->map_state == XCB_MAP_STATE_VIEWABLE); 73 free (xwa); 74 } 75 76 return ok; 77} 78 79/* 80 * Find a window that has WM_STATE set in the window tree below win. 81 * Unmapped/unviewable windows are not considered valid matches. 82 * Children are searched in top-down stacking order. 83 * The first matching window is returned, None if no match is found. 84 */ 85static xcb_window_t 86Find_Client_In_Children(xcb_connection_t * dpy, xcb_window_t win) 87{ 88 xcb_query_tree_cookie_t qt_cookie; 89 xcb_query_tree_reply_t *tree; 90 xcb_window_t *children; 91 unsigned int n_children; 92 int i; 93 94 qt_cookie = xcb_query_tree (dpy, win); 95 tree = xcb_query_tree_reply (dpy, qt_cookie, NULL); 96 if (!tree) 97 return XCB_WINDOW_NONE; 98 n_children = xcb_query_tree_children_length (tree); 99 if (!n_children) { 100 free (tree); 101 return XCB_WINDOW_NONE; 102 } 103 children = xcb_query_tree_children (tree); 104 105 /* Check each child for WM_STATE and other validity */ 106 win = XCB_WINDOW_NONE; 107 for (i = (int) n_children - 1; i >= 0; i--) { 108 if (!Window_Is_Viewable(dpy, children[i])) { 109 /* Don't bother descending into this one */ 110 children[i] = XCB_WINDOW_NONE; 111 continue; 112 } 113 if (!Window_Has_Property(dpy, children[i], atom_wm_state)) 114 continue; 115 116 /* Got one */ 117 win = children[i]; 118 goto done; 119 } 120 121 /* No children matched, now descend into each child */ 122 for (i = (int) n_children - 1; i >= 0; i--) { 123 if (children[i] == XCB_WINDOW_NONE) 124 continue; 125 win = Find_Client_In_Children(dpy, children[i]); 126 if (win != XCB_WINDOW_NONE) 127 break; 128 } 129 130 done: 131 free (tree); /* includes children */ 132 133 return win; 134} 135 136/* 137 * Find virtual roots (_NET_VIRTUAL_ROOTS) 138 */ 139static xcb_window_t * 140Find_Roots(xcb_connection_t * dpy, xcb_window_t root, unsigned int *num) 141{ 142 xcb_atom_t atom_virtual_root; 143 144 xcb_get_property_cookie_t prop_cookie; 145 xcb_get_property_reply_t *prop_reply; 146 147 xcb_window_t *prop_ret = NULL; 148 149 *num = 0; 150 151 atom_virtual_root = Get_Atom (dpy, "_NET_VIRTUAL_ROOTS"); 152 if (atom_virtual_root == XCB_ATOM_NONE) 153 return NULL; 154 155 prop_cookie = xcb_get_property (dpy, False, root, atom_virtual_root, 156 XCB_ATOM_WINDOW, 0, 0x7fffffff); 157 prop_reply = xcb_get_property_reply (dpy, prop_cookie, NULL); 158 if (!prop_reply) 159 return NULL; 160 161 if ((prop_reply->value_len > 0) && (prop_reply->type == XCB_ATOM_WINDOW) 162 && (prop_reply->format == 32)) { 163 int length = xcb_get_property_value_length (prop_reply); 164 prop_ret = malloc(length); 165 if (prop_ret) { 166 memcpy (prop_ret, xcb_get_property_value(prop_reply), length); 167 *num = prop_reply->value_len; 168 } 169 } 170 free (prop_reply); 171 172 return prop_ret; 173} 174 175/* 176 * Find child window at pointer location 177 */ 178static xcb_window_t 179Find_Child_At_Pointer(xcb_connection_t * dpy, xcb_window_t win) 180{ 181 xcb_window_t child_return = XCB_WINDOW_NONE; 182 183 xcb_query_pointer_cookie_t qp_cookie; 184 xcb_query_pointer_reply_t *qp_reply; 185 186 qp_cookie = xcb_query_pointer (dpy, win); 187 qp_reply = xcb_query_pointer_reply (dpy, qp_cookie, NULL); 188 189 if (qp_reply) { 190 child_return = qp_reply->child; 191 free (qp_reply); 192 } 193 194 return child_return; 195} 196 197/* 198 * Find client window at pointer location 199 * 200 * root is the root window. 201 * subwin is the subwindow reported by a ButtonPress event on root. 202 * 203 * If the WM uses virtual roots subwin may be a virtual root. 204 * If so, we descend the window stack at the pointer location and assume the 205 * child is the client or one of its WM frame windows. 206 * This will of course work only if the virtual roots are children of the real 207 * root. 208 */ 209xcb_window_t 210Find_Client(xcb_connection_t * dpy, xcb_window_t root, xcb_window_t subwin) 211{ 212 xcb_window_t *roots; 213 unsigned int i, n_roots; 214 xcb_window_t win; 215 216 /* Check if subwin is a virtual root */ 217 roots = Find_Roots(dpy, root, &n_roots); 218 for (i = 0; i < n_roots; i++) { 219 if (subwin != roots[i]) 220 continue; 221 win = Find_Child_At_Pointer(dpy, subwin); 222 if (win == XCB_WINDOW_NONE) { 223 free (roots); 224 return subwin; /* No child - Return virtual root. */ 225 } 226 subwin = win; 227 break; 228 } 229 free (roots); 230 231 if (atom_wm_state == XCB_ATOM_NONE) { 232 atom_wm_state = Get_Atom(dpy, "WM_STATE"); 233 if (atom_wm_state == XCB_ATOM_NONE) 234 return subwin; 235 } 236 237 /* Check if subwin has WM_STATE */ 238 if (Window_Has_Property(dpy, subwin, atom_wm_state)) 239 return subwin; 240 241 /* Attempt to find a client window in subwin's children */ 242 win = Find_Client_In_Children(dpy, subwin); 243 if (win != XCB_WINDOW_NONE) 244 return win; /* Found a client */ 245 246 /* Did not find a client */ 247 return subwin; 248} 249