247 lines
7.2 KiB
C
247 lines
7.2 KiB
C
/*
|
|
* Copyright 2007 Kim woelders
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
|
* documentation for any purpose is hereby granted without fee, provided that
|
|
* the above copyright notice appear in all copies and that both that copyright
|
|
* notice and this permission notice appear in supporting documentation, and
|
|
* that the name of the copyright holders not be used in advertising or
|
|
* publicity pertaining to distribution of the software without specific,
|
|
* written prior permission. The copyright holders make no representations
|
|
* about the suitability of this software for any purpose. It is provided "as
|
|
* is" without express or implied warranty.
|
|
*
|
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
|
* OF THIS SOFTWARE.
|
|
*/
|
|
#include <xcb/xcb.h>
|
|
#include <xcb/xproto.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "clientwin.h"
|
|
#include "dsimple.h"
|
|
|
|
static xcb_atom_t atom_wm_state = XCB_ATOM_NONE;
|
|
|
|
/*
|
|
* Check if window has given property
|
|
*/
|
|
static Bool
|
|
Window_Has_Property(xcb_connection_t * dpy, xcb_window_t win, xcb_atom_t atom)
|
|
{
|
|
xcb_get_property_cookie_t prop_cookie;
|
|
xcb_get_property_reply_t *prop_reply;
|
|
|
|
prop_cookie = xcb_get_property (dpy, False, win, atom,
|
|
XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
|
|
|
|
prop_reply = xcb_get_property_reply (dpy, prop_cookie, NULL);
|
|
|
|
if (prop_reply) {
|
|
xcb_atom_t reply_type = prop_reply->type;
|
|
free (prop_reply);
|
|
if (reply_type != XCB_NONE)
|
|
return True;
|
|
}
|
|
|
|
return False;
|
|
}
|
|
|
|
/*
|
|
* Check if window is viewable
|
|
*/
|
|
static Bool
|
|
Window_Is_Viewable(xcb_connection_t * dpy, xcb_window_t win)
|
|
{
|
|
Bool ok = False;
|
|
xcb_get_window_attributes_cookie_t attr_cookie;
|
|
xcb_get_window_attributes_reply_t *xwa;
|
|
|
|
attr_cookie = xcb_get_window_attributes (dpy, win);
|
|
xwa = xcb_get_window_attributes_reply (dpy, attr_cookie, NULL);
|
|
|
|
if (xwa) {
|
|
ok = (xwa->_class == XCB_WINDOW_CLASS_INPUT_OUTPUT) &&
|
|
(xwa->map_state == XCB_MAP_STATE_VIEWABLE);
|
|
free (xwa);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
/*
|
|
* Find a window that has WM_STATE set in the window tree below win.
|
|
* Unmapped/unviewable windows are not considered valid matches.
|
|
* Children are searched in top-down stacking order.
|
|
* The first matching window is returned, None if no match is found.
|
|
*/
|
|
static xcb_window_t
|
|
Find_Client_In_Children(xcb_connection_t * dpy, xcb_window_t win)
|
|
{
|
|
xcb_query_tree_cookie_t qt_cookie;
|
|
xcb_query_tree_reply_t *tree;
|
|
xcb_window_t *children;
|
|
unsigned int n_children;
|
|
int i;
|
|
|
|
qt_cookie = xcb_query_tree (dpy, win);
|
|
tree = xcb_query_tree_reply (dpy, qt_cookie, NULL);
|
|
if (!tree)
|
|
return XCB_WINDOW_NONE;
|
|
n_children = xcb_query_tree_children_length (tree);
|
|
if (!n_children) {
|
|
free (tree);
|
|
return XCB_WINDOW_NONE;
|
|
}
|
|
children = xcb_query_tree_children (tree);
|
|
|
|
/* Check each child for WM_STATE and other validity */
|
|
win = XCB_WINDOW_NONE;
|
|
for (i = (int) n_children - 1; i >= 0; i--) {
|
|
if (!Window_Is_Viewable(dpy, children[i])) {
|
|
/* Don't bother descending into this one */
|
|
children[i] = XCB_WINDOW_NONE;
|
|
continue;
|
|
}
|
|
if (!Window_Has_Property(dpy, children[i], atom_wm_state))
|
|
continue;
|
|
|
|
/* Got one */
|
|
win = children[i];
|
|
goto done;
|
|
}
|
|
|
|
/* No children matched, now descend into each child */
|
|
for (i = (int) n_children - 1; i >= 0; i--) {
|
|
if (children[i] == XCB_WINDOW_NONE)
|
|
continue;
|
|
win = Find_Client_In_Children(dpy, children[i]);
|
|
if (win != XCB_WINDOW_NONE)
|
|
break;
|
|
}
|
|
|
|
done:
|
|
free (tree); /* includes children */
|
|
|
|
return win;
|
|
}
|
|
|
|
/*
|
|
* Find virtual roots (_NET_VIRTUAL_ROOTS)
|
|
*/
|
|
static xcb_window_t *
|
|
Find_Roots(xcb_connection_t * dpy, xcb_window_t root, unsigned int *num)
|
|
{
|
|
xcb_atom_t atom_virtual_root;
|
|
|
|
xcb_get_property_cookie_t prop_cookie;
|
|
xcb_get_property_reply_t *prop_reply;
|
|
|
|
xcb_window_t *prop_ret = NULL;
|
|
|
|
*num = 0;
|
|
|
|
atom_virtual_root = Get_Atom (dpy, "_NET_VIRTUAL_ROOTS");
|
|
if (atom_virtual_root == XCB_ATOM_NONE)
|
|
return NULL;
|
|
|
|
prop_cookie = xcb_get_property (dpy, False, root, atom_virtual_root,
|
|
XCB_ATOM_WINDOW, 0, 0x7fffffff);
|
|
prop_reply = xcb_get_property_reply (dpy, prop_cookie, NULL);
|
|
if (!prop_reply)
|
|
return NULL;
|
|
|
|
if ((prop_reply->value_len > 0) && (prop_reply->type == XCB_ATOM_WINDOW)
|
|
&& (prop_reply->format == 32)) {
|
|
int length = xcb_get_property_value_length (prop_reply);
|
|
prop_ret = malloc(length);
|
|
if (prop_ret) {
|
|
memcpy (prop_ret, xcb_get_property_value(prop_reply), length);
|
|
*num = prop_reply->value_len;
|
|
}
|
|
}
|
|
free (prop_reply);
|
|
|
|
return prop_ret;
|
|
}
|
|
|
|
/*
|
|
* Find child window at pointer location
|
|
*/
|
|
static xcb_window_t
|
|
Find_Child_At_Pointer(xcb_connection_t * dpy, xcb_window_t win)
|
|
{
|
|
xcb_window_t child_return = XCB_WINDOW_NONE;
|
|
|
|
xcb_query_pointer_cookie_t qp_cookie;
|
|
xcb_query_pointer_reply_t *qp_reply;
|
|
|
|
qp_cookie = xcb_query_pointer (dpy, win);
|
|
qp_reply = xcb_query_pointer_reply (dpy, qp_cookie, NULL);
|
|
|
|
if (qp_reply) {
|
|
child_return = qp_reply->child;
|
|
free (qp_reply);
|
|
}
|
|
|
|
return child_return;
|
|
}
|
|
|
|
/*
|
|
* Find client window at pointer location
|
|
*
|
|
* root is the root window.
|
|
* subwin is the subwindow reported by a ButtonPress event on root.
|
|
*
|
|
* If the WM uses virtual roots subwin may be a virtual root.
|
|
* If so, we descend the window stack at the pointer location and assume the
|
|
* child is the client or one of its WM frame windows.
|
|
* This will of course work only if the virtual roots are children of the real
|
|
* root.
|
|
*/
|
|
xcb_window_t
|
|
Find_Client(xcb_connection_t * dpy, xcb_window_t root, xcb_window_t subwin)
|
|
{
|
|
xcb_window_t *roots;
|
|
unsigned int i, n_roots;
|
|
xcb_window_t win;
|
|
|
|
/* Check if subwin is a virtual root */
|
|
roots = Find_Roots(dpy, root, &n_roots);
|
|
for (i = 0; i < n_roots; i++) {
|
|
if (subwin != roots[i])
|
|
continue;
|
|
win = Find_Child_At_Pointer(dpy, subwin);
|
|
if (win == XCB_WINDOW_NONE)
|
|
return subwin; /* No child - Return virtual root. */
|
|
subwin = win;
|
|
break;
|
|
}
|
|
free (roots);
|
|
|
|
if (atom_wm_state == XCB_ATOM_NONE) {
|
|
atom_wm_state = Get_Atom(dpy, "WM_STATE");
|
|
if (atom_wm_state == XCB_ATOM_NONE)
|
|
return subwin;
|
|
}
|
|
|
|
/* Check if subwin has WM_STATE */
|
|
if (Window_Has_Property(dpy, subwin, atom_wm_state))
|
|
return subwin;
|
|
|
|
/* Attempt to find a client window in subwin's children */
|
|
win = Find_Client_In_Children(dpy, subwin);
|
|
if (win != XCB_WINDOW_NONE)
|
|
return win; /* Found a client */
|
|
|
|
/* Did not find a client */
|
|
return subwin;
|
|
}
|