diff --git a/Makefile.am b/Makefile.am index e4fc5ad..d3fc24a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,6 +27,8 @@ xprop_LDADD = $(XPROP_LIBS) xprop_SOURCES = \ dsimple.c \ dsimple.h \ + clientwin.c \ + clientwin.h \ xprop.c appman_PRE = \ diff --git a/clientwin.c b/clientwin.c new file mode 100644 index 0000000..471aed0 --- /dev/null +++ b/clientwin.c @@ -0,0 +1,214 @@ +/* + * 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 +#include + +static Atom atom_wm_state = None; + +/* + * Check if window has given property + */ +static Bool +Window_Has_Property(Display * dpy, Window win, Atom atom) +{ + Atom type_ret; + int format_ret; + unsigned char *prop_ret; + unsigned long bytes_after, num_ret; + + type_ret = None; + prop_ret = NULL; + XGetWindowProperty(dpy, win, atom, 0, 0, False, AnyPropertyType, + &type_ret, &format_ret, &num_ret, + &bytes_after, &prop_ret); + if (prop_ret) + XFree(prop_ret); + + return (type_ret != None) ? True : False; +} + +/* + * Check if window is viewable + */ +static Bool +Window_Is_Viewable(Display * dpy, Window win) +{ + Bool ok; + XWindowAttributes xwa; + + XGetWindowAttributes(dpy, win, &xwa); + + ok = (xwa.class == InputOutput) && (xwa.map_state == IsViewable); + + 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. + */ +Window +Find_Client_In_Children(Display * dpy, Window win) +{ + Window root, parent; + Window *children; + unsigned int n_children; + int i; + + if (!XQueryTree(dpy, win, &root, &parent, &children, &n_children)) + return None; + if (!children) + return None; + + /* Check each child for WM_STATE and other validity */ + win = None; + for (i = (int) n_children - 1; i >= 0; i--) { + if (!Window_Is_Viewable(dpy, children[i])) { + children[i] = None; /* Don't bother descending into this one */ + 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] == None) + continue; + win = Find_Client_In_Children(dpy, children[i]); + if (win != None) + break; + } + + done: + XFree(children); + + return win; +} + +/* + * Find virtual roots (_NET_VIRTUAL_ROOTS) + */ +unsigned long * +Find_Roots(Display * dpy, Window root, unsigned int *num) +{ + Atom type_ret; + int format_ret; + unsigned char *prop_ret; + unsigned long bytes_after, num_ret; + Atom atom; + + *num = 0; + atom = XInternAtom(dpy, "_NET_VIRTUAL_ROOTS", False); + if (!atom) + return NULL; + + type_ret = None; + prop_ret = NULL; + if (XGetWindowProperty(dpy, root, atom, 0, 0x7fffffff, False, + XA_WINDOW, &type_ret, &format_ret, &num_ret, + &bytes_after, &prop_ret) != Success) + return NULL; + + if (prop_ret && type_ret == XA_WINDOW && format_ret == 32) { + *num = num_ret; + return ((unsigned long *) prop_ret); + } + if (prop_ret) + XFree(prop_ret); + + return NULL; +} + +/* + * Find child window at pointer location + */ +static Window +Find_Child_At_Pointer(Display * dpy, Window win) +{ + Window root_return, child_return; + int dummyi; + unsigned int dummyu; + + XQueryPointer(dpy, win, &root_return, &child_return, + &dummyi, &dummyi, &dummyi, &dummyi, &dummyu); + + 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. + */ +Window +Find_Client(Display * dpy, Window root, Window subwin) +{ + unsigned long *roots; + unsigned int i, n_roots; + Window 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 == None) + return subwin; /* No child - Return virtual root. */ + subwin = win; + break; + } + if (roots) + XFree(roots); + + if (atom_wm_state == None) { + atom_wm_state = XInternAtom(dpy, "WM_STATE", False); + if (!atom_wm_state) + 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 != None) + return win; /* Found a client */ + + /* Did not find a client */ + return subwin; +} diff --git a/clientwin.h b/clientwin.h new file mode 100644 index 0000000..3abdcea --- /dev/null +++ b/clientwin.h @@ -0,0 +1,29 @@ +/* + * 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. + */ +#ifndef _CLIENTWIN_H_ +#define _CLIENTWIN_H_ + +#include + +extern Window Find_Client(Display * dpy, Window root, Window target_win); + +#endif diff --git a/configure.ac b/configure.ac index da8ebb8..78e4049 100644 --- a/configure.ac +++ b/configure.ac @@ -38,7 +38,7 @@ AC_PROG_INSTALL AC_CHECK_HEADERS_ONCE([wchar.h wctype.h]) # Checks for pkg-config packages -PKG_CHECK_MODULES(XPROP, xmuu x11) +PKG_CHECK_MODULES(XPROP, x11) AC_SUBST(XPROP_CFLAGS) AC_SUBST(XPROP_LIBS) diff --git a/dsimple.c b/dsimple.c index 0c9fce8..2dc8c18 100644 --- a/dsimple.c +++ b/dsimple.c @@ -41,6 +41,7 @@ from The Open Group. * Written by Mark Lillibridge. Last updated 7/1/87 */ +#include "clientwin.h" #include "dsimple.h" /* @@ -263,7 +264,7 @@ Window Select_Window_Args( * Routine to let user select a window using the mouse */ -Window Select_Window(Display *dpy) +Window Select_Window(Display *dpy, int descend) { int status; Cursor cursor; @@ -302,6 +303,11 @@ Window Select_Window(Display *dpy) XUngrabPointer(dpy, CurrentTime); /* Done with pointer */ + if (!descend || (target_win == root)) + return(target_win); + + target_win = Find_Client(dpy, root, target_win); + return(target_win); } diff --git a/dsimple.h b/dsimple.h index 7b53c3c..7557571 100644 --- a/dsimple.h +++ b/dsimple.h @@ -78,7 +78,7 @@ void usage(void); * Send bugs, etc. to chariot@athena.mit.edu. */ -Window Select_Window(Display *); +Window Select_Window(Display *, int descend); Window Window_With_Name(Display *, Window, const char *); #ifdef __GNUC__ void Fatal_Error(char *, ...) __attribute__((__noreturn__)); diff --git a/xprop.c b/xprop.c index c489486..f1bf02a 100644 --- a/xprop.c +++ b/xprop.c @@ -51,7 +51,6 @@ from The Open Group. #endif #include -#include #include "dsimple.h" @@ -1702,19 +1701,8 @@ main (int argc, char **argv) if ((remove_props != NULL || set_props != NULL) && argc > 0) usage(); - if (target_win == None) { - target_win = Select_Window(dpy); - if (target_win != None && !frame_only) { - Window root; - int dummyi; - unsigned int dummy; - - if (XGetGeometry (dpy, target_win, &root, &dummyi, &dummyi, - &dummy, &dummy, &dummy, &dummy) - && target_win != root) - target_win = XmuClientWindow (dpy, target_win); - } - } + if (target_win == None) + target_win = Select_Window(dpy, !frame_only); if (remove_props != NULL) { int count;