File toon_root.c of Package xroach
/* toon_root.c - finding the correct background window / virtual root
* Copyright (C) 1999-2001 Robin Hogan
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Since xpenguins version 2.1, the ToonGetRootWindow() function
* attempts to find the window IDs of
*
* 1) The background window that is behind the toplevel client
* windows; this is the window that we draw the toons on.
*
* 2) The parent window of the toplevel client windows; this is used
* by ToonLocateWindows() to build up a map of the space that the
* toons can occupy.
*
* In simple (sensible?) window managers (e.g. blackbox, sawfish, fvwm
* and countless others), both of these are the root window. The other
* more complex scenarios that ToonGetRootWindow() attempts to cope
* with are:
*
* Some `virtual' window managers (e.g. amiwm, swm and tvtwm) that
* reparent all client windows to a desktop window that sits on top of
* the root window. This desktop window is easy to find - we just look
* for a property __SWM_VROOT in the immediate children of the root
* window that contains the window ID of this desktop window. The
* desktop plays both roles (1 and 2 above). This functionality was
* detected in xpenguins 1.x with the vroot.h header file.
*
* Enlightenment (0.16) can have a number of desktops with different
* backgrounds; client windows on these are reparented, except for
* Desktop 0 which is the root window. Therefore versions less than
* 2.1 of xpenguins worked on Desktop 0 but not on any others. To fix
* this we look for a root-window property _WIN_WORKSPACE which
* contains the numerical index of the currently active desktop. The
* active desktop is then simply the immediate child of the root
* window that has a property ENLIGHTENMENT_DESKTOP set to this value.
*
* KDE 2.0: Oh dear. The kdesktop is a program separate from the
* window manager that launches a window which sits behind all the
* other client windows and has all the icons on it. Thus the other
* client windows are still children of the root window, but we want
* to draw to the uppermost window of the kdesktop. This is difficult
* to find - it is the great-great-grandchild of the root window and
* in KDE 2.0 has nothing to identify it from its siblings other than
* its size. KDE 2.1+ usefully implements the __SWM_VROOT property in
* a child of the root window, but the client windows are still
* children of the root window. A problem is that the penguins erase
* the desktop icons when they walk which is a bit messy. The icons
* are not lost - they reappear when the desktop window gets an expose
* event (i.e. move some windows over where they were and back again).
*
* Nautilus (GNOME 1.4+): Creates a background window to draw icons
* on, but does not reparent the client windows. The toplevel window
* of the desktop is indicated by the root window property
* NAUTILUS_DESKTOP_WINDOW_ID, but then we must descend down the tree
* from this toplevel window looking for subwindows that are the same
* size as the screen. The bottom one is the one to draw to. Hopefully
* one day Nautilus will implement __SWM_VROOT in exactly the same way
* as KDE 2.1+.
*
* Other cases: CDE, the common desktop environment. This is a
* commercial product that has been packaged with Sun (and other)
* workstations. It typically implements four virtual desktops but
* provides NO properties at all for apps such as xpenguins to use to
* work out where to draw to. Seeing as Sun are moving over to GNOME,
* CDE use is on the decline so I don't have any current plans to try
* and get xpenguins to work with it.
*
* As a note to developers of window managers and big screen hoggers
* like kdesktop, please visit www.freedesktop.org and implement their
* Extended Window Manager Hints spec that help pagers and apps like
* xpenguins and xearth to find their way around. In particular,
* please use the _NET_CURRENT_DESKTOP and _NET_VIRTUAL_ROOTS
* properties if you reparent any windows (e.g. Enlightenment). Since
* no window managers that I know yet use these particular hints, I
* haven't yet added any code to parse them. */
/* things from toon.h */
#define _(string) string
#define TOON_MESSAGE_LENGTH 128
char toon_message[TOON_MESSAGE_LENGTH] = "";
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xproto.h>
#include <stdio.h>
#include <string.h>
/* Time to throw up. Here is a kludgey function that recursively calls
* itself (up to a limit) to find the window ID of the KDE desktop to
* draw on. It works with KDE 2.0, but since KDE 2.0 is less stable
* than Windows 95, I don't expect many people to remain using it now
* that 2.1 is available, which implements __SWM_VROOT and makes this
* function redundant. This is the hierarchy we're trying to traverse:
*
* -> The root window
* 0 -> window with name="KDE Desktop"
* 1 -> window with no name
* 2 -> window with name="KDE Desktop" & _NET_WM_WINDOW_TYPE_DESKTOP
* 3 -> window with no name and width >= width of screen
*
* The last window in the hierarchy is the one to draw to. The
* numbers show the value of the `depth' argument. */
static
Window
__ToonGetKDEDesktop(Display *display, int screen, Window window,
Atom atom, char *atomname, int depth)
{
char *name = NULL;
Atom *wintype = NULL;
Window winreturn = 0;
unsigned long nitems, bytesafter;
Atom actual_type;
int actual_format;
Window rootReturn, parentReturn, *children;
unsigned int nChildren;
char go_deeper = 0;
if (XFetchName(display, window, &name)) {
if (strcasecmp(name, "KDE Desktop") == 0) {
/* Presumably either at depth 0 or 2 */
if (XGetWindowProperty(display, window, atom, 0, 1,
False, XA_ATOM,
&actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &wintype) == Success
&& wintype) {
char *tmpatomname = XGetAtomName(display, *wintype);
if (tmpatomname) {
if (strcmp(atomname, tmpatomname) == 0 && depth == 2) {
/* OK, at depth 2 */
go_deeper = 1;
}
XFree((char *) tmpatomname);
}
}
else if (depth < 2) {
go_deeper = 1;
}
}
else if (depth == 1) {
go_deeper = 1;
}
XFree((char *) name);
}
else if (depth == 1) {
go_deeper = 1;
}
/* If go_deeper is 1 then there is a possibility that the background
* window is a descendant of the current window; otherwise we're
* barking up the wrong tree. */
if (go_deeper && XQueryTree(display, window, &rootReturn,
&parentReturn, &children,
&nChildren)) {
int i;
for (i = 0; i < nChildren; ++i) {
/* children[i] is now at depth 3 */
if (depth == 2) {
XWindowAttributes attributes;
if (XGetWindowAttributes(display, children[i], &attributes)) {
if (attributes.width >= DisplayWidth(display, screen)/2
&& attributes.height > 0) {
/* Found it! */
winreturn = children[i];
break;
}
}
}
else if ((winreturn = __ToonGetKDEDesktop(display, screen,
children[i],
atom, atomname,
depth+1))) {
break;
}
}
XFree((char *) children);
}
return winreturn;
}
/* Look for the Nautilus desktop window to draw to, given the toplevel
* window of the Nautilus desktop. Basically recursively calls itself
* looking for subwindows the same size as the root window. */
static
Window
__ToonGetNautilusDesktop(Display *display, int screen, Window window,
int depth)
{
Window rootReturn, parentReturn, *children;
Window winreturn = window;
unsigned int nChildren;
if (depth > 5) {
return ((Window) 0);
}
else if (XQueryTree(display, window, &rootReturn, &parentReturn,
&children, &nChildren)) {
int i;
for (i = 0; i < nChildren; ++i) {
XWindowAttributes attributes;
if (XGetWindowAttributes(display, children[i], &attributes)) {
if (attributes.width == DisplayWidth(display, screen)
&& attributes.height == DisplayHeight(display, screen)) {
/* Found a possible desktop window */
winreturn = __ToonGetNautilusDesktop(display, screen,
children[i], depth+1);
}
}
}
XFree((char *) children);
}
return winreturn;
}
/*
* Returns the window ID of the `background' window on to which the
* toons should be drawn. Also returned (in clientparent) is the ID of
* the parent of all the client windows, since this may not be the
* same as the background window. If no recognised virtual window
* manager or desktop environment is found then the root window is
* returned in both cases. The string toon_message contains
* information about the window manager that was found.
*/
Window
ToonGetRootWindow(Display *display, int screen, Window *clientparent)
{
Window background = 0; /* The return value */
Window root = RootWindow(display, screen);
Window rootReturn, parentReturn, *children;
Window *toplevel = (Window *) 0;
unsigned int nChildren;
unsigned long nitems, bytesafter;
Atom actual_type;
int actual_format;
unsigned long *workspace = NULL;
unsigned long *desktop = NULL;
Atom NAUTILUS_DESKTOP_WINDOW_ID = XInternAtom(display,
"NAUTILUS_DESKTOP_WINDOW_ID",
False);
*clientparent = root;
if (XGetWindowProperty(display, root,
NAUTILUS_DESKTOP_WINDOW_ID,
0, 1, False, XA_WINDOW,
&actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &toplevel) == Success
&& toplevel) {
/* Nautilus is running */
background = __ToonGetNautilusDesktop(display, screen,
*toplevel, 0);
XFree((char *) toplevel);
if (background) {
snprintf(toon_message, TOON_MESSAGE_LENGTH,
_("Drawing to Nautilus Desktop"));
toon_message[TOON_MESSAGE_LENGTH-1] = '\0';
}
}
/* Next look for a virtual root or a KDE Desktop */
if (!background
&& XQueryTree(display, root, &rootReturn, &parentReturn,
&children, &nChildren)) {
int i;
Atom _NET_WM_WINDOW_TYPE = XInternAtom(display,
"_NET_WM_WINDOW_TYPE",
False);
Atom __SWM_VROOT = XInternAtom(display, "__SWM_VROOT", False);
for (i = 0; i < nChildren && !background; ++i) {
Window *newroot = (Window *) 0;
if (XGetWindowProperty(display, children[i],
__SWM_VROOT, 0, 1, False, XA_WINDOW,
&actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &newroot) == Success
&& newroot) {
/* Found a window with a __SWM_VROOT property that contains
* the window ID of the virtual root. Now we must check
* whether it is KDE (2.1+) or not. If it is KDE then it does
* not reparent the clients. If the root window has the
* _NET_SUPPORTED property but not the _NET_VIRTUAL_ROOTS
* property then we assume it is KDE. */
Atom _NET_SUPPORTED = XInternAtom(display,
"_NET_SUPPORTED",
False);
Atom *tmpatom;
if (XGetWindowProperty(display, root,
_NET_SUPPORTED, 0, 1, False,
XA_ATOM, &actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &tmpatom) == Success
&& tmpatom) {
Window *tmpwindow = (Window *) 0;
Atom _NET_VIRTUAL_ROOTS = XInternAtom(display,
"_NET_VIRTUAL_ROOTS",
False);
XFree((char *) tmpatom);
if (XGetWindowProperty(display, root,
_NET_VIRTUAL_ROOTS, 0, 1, False,
XA_WINDOW, &actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &tmpwindow) != Success
|| !tmpwindow) {
/* Must be KDE 2.1+ */
snprintf(toon_message, TOON_MESSAGE_LENGTH,
_("Drawing to KDE Desktop"));
toon_message[TOON_MESSAGE_LENGTH-1] = '\0';
background = *newroot;
}
else if (tmpwindow) {
XFree((char *) tmpwindow);
}
}
if (!background) {
/* Not KDE: assume windows are reparented */
snprintf(toon_message, TOON_MESSAGE_LENGTH,
_("Drawing to virtual root window"));
toon_message[TOON_MESSAGE_LENGTH-1] = '\0';
background = *clientparent = *newroot;
}
XFree((char *) newroot);
}
else if ((background = __ToonGetKDEDesktop(display, screen, children[i],
_NET_WM_WINDOW_TYPE,
"_NET_WM_WINDOW_TYPE_DESKTOP",
0))) {
/* Found a KDE 2.0 desktop and located the background window */
/* Note that the clientparent is still the root window */
snprintf(toon_message, TOON_MESSAGE_LENGTH,
_("Drawing to KDE desktop"));
toon_message[TOON_MESSAGE_LENGTH-1] = '\0';
}
}
XFree((char *) children);
}
if (!background) {
/* Look for a _WIN_WORKSPACE property, used by Enlightenment */
Atom _WIN_WORKSPACE = XInternAtom(display, "_WIN_WORKSPACE", False);
if (XGetWindowProperty(display, root, _WIN_WORKSPACE,
0, 1, False, XA_CARDINAL,
&actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &workspace) == Success
&& workspace) {
/* Found a _WIN_WORKSPACE property - this is the desktop to look for.
* For now assume that this is Enlightenment.
* We're looking for a child of the root window that has an
* ENLIGHTENMENT_DESKTOP atom with a value equal to the root window's
* _WIN_WORKSPACE atom. */
Atom ENLIGHTENMENT_DESKTOP = XInternAtom(display,
"ENLIGHTENMENT_DESKTOP",
False);
/* First check to see if the root window is the current desktop... */
if (XGetWindowProperty(display, root,
ENLIGHTENMENT_DESKTOP, 0, 1,
False, XA_CARDINAL,
&actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &desktop) == Success
&& desktop && *desktop == *workspace) {
/* The root window is the current Enlightenment desktop */
snprintf(toon_message, TOON_MESSAGE_LENGTH,
_("Drawing to Enlightenment Desktop %lu (the root window)"),
*desktop);
toon_message[TOON_MESSAGE_LENGTH-1] = '\0';
background = root;
XFree((char *) desktop);
}
/* Now look at each immediate child window of root to see if it is
* the current desktop */
else if (XQueryTree(display, root, &rootReturn, &parentReturn,
&children, &nChildren)) {
int i;
for (i = 0; i < nChildren; ++i) {
if (XGetWindowProperty(display, children[i],
ENLIGHTENMENT_DESKTOP, 0, 1,
False, XA_CARDINAL,
&actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &desktop) == Success
&& desktop && *desktop == *workspace) {
/* Found current Enlightenment desktop */
snprintf(toon_message, TOON_MESSAGE_LENGTH,
_("Drawing to Enlightenment Desktop %lu"),
*desktop);
toon_message[TOON_MESSAGE_LENGTH-1] = '\0';
background = *clientparent = children[i];
XFree((char *) desktop);
}
}
XFree((char *) children);
}
XFree((char *) workspace);
}
}
if (background) {
return background;
}
else {
toon_message[0] = '\0';
return root;
}
}