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