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