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
31 static xcb_atom_t atom_wm_state = XCB_ATOM_NONE;
32
33 /*
34 * Check if window has given property
35 */
36 static Bool
Window_Has_Property(xcb_connection_t * dpy,xcb_window_t win,xcb_atom_t atom)37 Window_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 */
60 static Bool
Window_Is_Viewable(xcb_connection_t * dpy,xcb_window_t win)61 Window_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 */
85 static xcb_window_t
Find_Client_In_Children(xcb_connection_t * dpy,xcb_window_t win)86 Find_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 */
139 static xcb_window_t *
Find_Roots(xcb_connection_t * dpy,xcb_window_t root,unsigned int * num)140 Find_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 */
178 static xcb_window_t
Find_Child_At_Pointer(xcb_connection_t * dpy,xcb_window_t win)179 Find_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 */
209 xcb_window_t
Find_Client(xcb_connection_t * dpy,xcb_window_t root,xcb_window_t subwin)210 Find_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