ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/root-tail/toon_root.c
Revision: 1.1
Committed: Sun Apr 11 01:02:05 2004 UTC (22 years, 5 months ago) by chris_moore
Content type: text/plain
Branch: MAIN
CVS Tags: rel-1_3, rel-1_2, HEAD
Log Message:
Use code from XPenguins to find the root window under GNOME, KDE, etc.

File Contents

# User Rev Content
1 chris_moore 1.1 /* toon_root.c - finding the correct background window / virtual root
2     * Copyright (C) 1999-2001 Robin Hogan
3     *
4     * This program is free software; you can redistribute it and/or modify
5     * it under the terms of the GNU General Public License as published by
6     * the Free Software Foundation; either version 2 of the License, or
7     * (at your option) any later version.
8     *
9     * This program is distributed in the hope that it will be useful,
10     * but WITHOUT ANY WARRANTY; without even the implied warranty of
11     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12     * GNU General Public License for more details.
13     *
14     * You should have received a copy of the GNU General Public License
15     * along with this program; if not, write to the Free Software
16     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17     */
18    
19     /* Since xpenguins version 2.1, the ToonGetRootWindow() function
20     * attempts to find the window IDs of
21     *
22     * 1) The background window that is behind the toplevel client
23     * windows; this is the window that we draw the toons on.
24     *
25     * 2) The parent window of the toplevel client windows; this is used
26     * by ToonLocateWindows() to build up a map of the space that the
27     * toons can occupy.
28     *
29     * In simple (sensible?) window managers (e.g. blackbox, sawfish, fvwm
30     * and countless others), both of these are the root window. The other
31     * more complex scenarios that ToonGetRootWindow() attempts to cope
32     * with are:
33     *
34     * Some `virtual' window managers (e.g. amiwm, swm and tvtwm) that
35     * reparent all client windows to a desktop window that sits on top of
36     * the root window. This desktop window is easy to find - we just look
37     * for a property __SWM_VROOT in the immediate children of the root
38     * window that contains the window ID of this desktop window. The
39     * desktop plays both roles (1 and 2 above). This functionality was
40     * detected in xpenguins 1.x with the vroot.h header file.
41     *
42     * Enlightenment (0.16) can have a number of desktops with different
43     * backgrounds; client windows on these are reparented, except for
44     * Desktop 0 which is the root window. Therefore versions less than
45     * 2.1 of xpenguins worked on Desktop 0 but not on any others. To fix
46     * this we look for a root-window property _WIN_WORKSPACE which
47     * contains the numerical index of the currently active desktop. The
48     * active desktop is then simply the immediate child of the root
49     * window that has a property ENLIGHTENMENT_DESKTOP set to this value.
50     *
51     * KDE 2.0: Oh dear. The kdesktop is a program separate from the
52     * window manager that launches a window which sits behind all the
53     * other client windows and has all the icons on it. Thus the other
54     * client windows are still children of the root window, but we want
55     * to draw to the uppermost window of the kdesktop. This is difficult
56     * to find - it is the great-great-grandchild of the root window and
57     * in KDE 2.0 has nothing to identify it from its siblings other than
58     * its size. KDE 2.1+ usefully implements the __SWM_VROOT property in
59     * a child of the root window, but the client windows are still
60     * children of the root window. A problem is that the penguins erase
61     * the desktop icons when they walk which is a bit messy. The icons
62     * are not lost - they reappear when the desktop window gets an expose
63     * event (i.e. move some windows over where they were and back again).
64     *
65     * Nautilus (GNOME 1.4+): Creates a background window to draw icons
66     * on, but does not reparent the client windows. The toplevel window
67     * of the desktop is indicated by the root window property
68     * NAUTILUS_DESKTOP_WINDOW_ID, but then we must descend down the tree
69     * from this toplevel window looking for subwindows that are the same
70     * size as the screen. The bottom one is the one to draw to. Hopefully
71     * one day Nautilus will implement __SWM_VROOT in exactly the same way
72     * as KDE 2.1+.
73     *
74     * Other cases: CDE, the common desktop environment. This is a
75     * commercial product that has been packaged with Sun (and other)
76     * workstations. It typically implements four virtual desktops but
77     * provides NO properties at all for apps such as xpenguins to use to
78     * work out where to draw to. Seeing as Sun are moving over to GNOME,
79     * CDE use is on the decline so I don't have any current plans to try
80     * and get xpenguins to work with it.
81     *
82     * As a note to developers of window managers and big screen hoggers
83     * like kdesktop, please visit www.freedesktop.org and implement their
84     * Extended Window Manager Hints spec that help pagers and apps like
85     * xpenguins and xearth to find their way around. In particular,
86     * please use the _NET_CURRENT_DESKTOP and _NET_VIRTUAL_ROOTS
87     * properties if you reparent any windows (e.g. Enlightenment). Since
88     * no window managers that I know yet use these particular hints, I
89     * haven't yet added any code to parse them. */
90    
91    
92     /* #include "toon.h" */
93     #include <X11/Xlib.h>
94     #include <X11/Xatom.h>
95     #include <X11/Xproto.h>
96     #include <stdio.h>
97     #include <string.h>
98    
99     /* Time to throw up. Here is a kludgey function that recursively calls
100     * itself (up to a limit) to find the window ID of the KDE desktop to
101     * draw on. It works with KDE 2.0, but since KDE 2.0 is less stable
102     * than Windows 95, I don't expect many people to remain using it now
103     * that 2.1 is available, which implements __SWM_VROOT and makes this
104     * function redundant. This is the hierarchy we're trying to traverse:
105     *
106     * -> The root window
107     * 0 -> window with name="KDE Desktop"
108     * 1 -> window with no name
109     * 2 -> window with name="KDE Desktop" & _NET_WM_WINDOW_TYPE_DESKTOP
110     * 3 -> window with no name and width >= width of screen
111     *
112     * The last window in the hierarchy is the one to draw to. The
113     * numbers show the value of the `depth' argument. */
114     static
115     Window
116     __ToonGetKDEDesktop(Display *display, int screen, Window window,
117     Atom atom, char *atomname, int depth)
118     {
119     char *name = NULL;
120     Atom *wintype = NULL;
121     Window winreturn = 0;
122     unsigned long nitems, bytesafter;
123     Atom actual_type;
124     int actual_format;
125     Window rootReturn, parentReturn, *children;
126     unsigned int nChildren;
127     char go_deeper = 0;
128    
129     if (XFetchName(display, window, &name)) {
130     if (strcasecmp(name, "KDE Desktop") == 0) {
131     /* Presumably either at depth 0 or 2 */
132     if (XGetWindowProperty(display, window, atom, 0, 1,
133     False, XA_ATOM,
134     &actual_type, &actual_format,
135     &nitems, &bytesafter,
136     (unsigned char **) &wintype) == Success
137     && wintype) {
138     char *tmpatomname = XGetAtomName(display, *wintype);
139     if (tmpatomname) {
140     if (strcmp(atomname, tmpatomname) == 0 && depth == 2) {
141     /* OK, at depth 2 */
142     go_deeper = 1;
143     }
144     XFree((char *) tmpatomname);
145     }
146     }
147     else if (depth < 2) {
148     go_deeper = 1;
149     }
150     }
151     else if (depth == 1) {
152     go_deeper = 1;
153     }
154     XFree((char *) name);
155     }
156     else if (depth == 1) {
157     go_deeper = 1;
158     }
159    
160     /* If go_deeper is 1 then there is a possibility that the background
161     * window is a descendant of the current window; otherwise we're
162     * barking up the wrong tree. */
163     if (go_deeper && XQueryTree(display, window, &rootReturn,
164     &parentReturn, &children,
165     &nChildren)) {
166     int i;
167     for (i = 0; i < nChildren; ++i) {
168     /* children[i] is now at depth 3 */
169     if (depth == 2) {
170     XWindowAttributes attributes;
171     if (XGetWindowAttributes(display, children[i], &attributes)) {
172     if (attributes.width >= DisplayWidth(display, screen)/2
173     && attributes.height > 0) {
174     /* Found it! */
175     winreturn = children[i];
176     break;
177     }
178     }
179     }
180     else if ((winreturn = __ToonGetKDEDesktop(display, screen,
181     children[i],
182     atom, atomname,
183     depth+1))) {
184     break;
185     }
186     }
187     XFree((char *) children);
188     }
189    
190     return winreturn;
191     }
192    
193     /* Look for the Nautilus desktop window to draw to, given the toplevel
194     * window of the Nautilus desktop. Basically recursively calls itself
195     * looking for subwindows the same size as the root window. */
196     static
197     Window
198     __ToonGetNautilusDesktop(Display *display, int screen, Window window,
199     int depth)
200     {
201     Window rootReturn, parentReturn, *children;
202     Window winreturn = window;
203     unsigned int nChildren;
204    
205     if (depth > 5) {
206     return ((Window) 0);
207     }
208     else if (XQueryTree(display, window, &rootReturn, &parentReturn,
209     &children, &nChildren)) {
210     int i;
211     for (i = 0; i < nChildren; ++i) {
212     XWindowAttributes attributes;
213     if (XGetWindowAttributes(display, children[i], &attributes)) {
214     if (attributes.width == DisplayWidth(display, screen)
215     && attributes.height == DisplayHeight(display, screen)) {
216     /* Found a possible desktop window */
217     winreturn = __ToonGetNautilusDesktop(display, screen,
218     children[i], depth+1);
219     }
220     }
221     }
222     XFree((char *) children);
223     }
224     return winreturn;
225     }
226    
227    
228     /*
229     * Returns the window ID of the `background' window on to which the
230     * toons should be drawn. Also returned (in clientparent) is the ID of
231     * the parent of all the client windows, since this may not be the
232     * same as the background window. If no recognised virtual window
233     * manager or desktop environment is found then the root window is
234     * returned in both cases. The string toon_message contains
235     * information about the window manager that was found.
236     */
237     Window
238     ToonGetRootWindow(Display *display, int screen, Window *clientparent)
239     {
240     Window background = 0; /* The return value */
241     Window root = RootWindow(display, screen);
242     Window rootReturn, parentReturn, *children;
243     Window *toplevel = (Window *) 0;
244     unsigned int nChildren;
245     unsigned long nitems, bytesafter;
246     Atom actual_type;
247     int actual_format;
248     unsigned long *workspace = NULL;
249     unsigned long *desktop = NULL;
250     Atom NAUTILUS_DESKTOP_WINDOW_ID = XInternAtom(display,
251     "NAUTILUS_DESKTOP_WINDOW_ID",
252     False);
253    
254     *clientparent = root;
255    
256     if (XGetWindowProperty(display, root,
257     NAUTILUS_DESKTOP_WINDOW_ID,
258     0, 1, False, XA_WINDOW,
259     &actual_type, &actual_format,
260     &nitems, &bytesafter,
261     (unsigned char **) &toplevel) == Success
262     && toplevel) {
263     /* Nautilus is running */
264     background = __ToonGetNautilusDesktop(display, screen,
265     *toplevel, 0);
266     XFree((char *) toplevel);
267     if (background) {
268     printf("Drawing to Nautilus Desktop\n");
269     }
270     }
271    
272     /* Next look for a virtual root or a KDE Desktop */
273     if (!background
274     && XQueryTree(display, root, &rootReturn, &parentReturn,
275     &children, &nChildren)) {
276     int i;
277     Atom _NET_WM_WINDOW_TYPE = XInternAtom(display,
278     "_NET_WM_WINDOW_TYPE",
279     False);
280     Atom __SWM_VROOT = XInternAtom(display, "__SWM_VROOT", False);
281    
282     for (i = 0; i < nChildren && !background; ++i) {
283     Window *newroot = (Window *) 0;
284     if (XGetWindowProperty(display, children[i],
285     __SWM_VROOT, 0, 1, False, XA_WINDOW,
286     &actual_type, &actual_format,
287     &nitems, &bytesafter,
288     (unsigned char **) &newroot) == Success
289     && newroot) {
290     /* Found a window with a __SWM_VROOT property that contains
291     * the window ID of the virtual root. Now we must check
292     * whether it is KDE (2.1+) or not. If it is KDE then it does
293     * not reparent the clients. If the root window has the
294     * _NET_SUPPORTED property but not the _NET_VIRTUAL_ROOTS
295     * property then we assume it is KDE. */
296     Atom _NET_SUPPORTED = XInternAtom(display,
297     "_NET_SUPPORTED",
298     False);
299     Atom *tmpatom;
300     if (XGetWindowProperty(display, root,
301     _NET_SUPPORTED, 0, 1, False,
302     XA_ATOM, &actual_type, &actual_format,
303     &nitems, &bytesafter,
304     (unsigned char **) &tmpatom) == Success
305     && tmpatom) {
306     Window *tmpwindow = (Window *) 0;
307     Atom _NET_VIRTUAL_ROOTS = XInternAtom(display,
308     "_NET_VIRTUAL_ROOTS",
309     False);
310     XFree((char *) tmpatom);
311     if (XGetWindowProperty(display, root,
312     _NET_VIRTUAL_ROOTS, 0, 1, False,
313     XA_WINDOW, &actual_type, &actual_format,
314     &nitems, &bytesafter,
315     (unsigned char **) &tmpwindow) != Success
316     || !tmpwindow) {
317     /* Must be KDE 2.1+ */
318     printf("Drawing to KDE Desktop\n");
319     background = *newroot;
320     }
321     else if (tmpwindow) {
322     XFree((char *) tmpwindow);
323     }
324     }
325    
326     if (!background) {
327     /* Not KDE: assume windows are reparented */
328     printf("Drawing to virtual root window\n");
329     background = *clientparent = *newroot;
330     }
331     XFree((char *) newroot);
332     }
333     else if ((background = __ToonGetKDEDesktop(display, screen, children[i],
334     _NET_WM_WINDOW_TYPE,
335     "_NET_WM_WINDOW_TYPE_DESKTOP",
336     0))) {
337     /* Found a KDE 2.0 desktop and located the background window */
338     /* Note that the clientparent is still the root window */
339     printf( "Drawing to KDE desktop\n");
340     }
341     }
342     XFree((char *) children);
343     }
344    
345     if (!background) {
346     /* Look for a _WIN_WORKSPACE property, used by Enlightenment */
347     Atom _WIN_WORKSPACE = XInternAtom(display, "_WIN_WORKSPACE", False);
348     if (XGetWindowProperty(display, root, _WIN_WORKSPACE,
349     0, 1, False, XA_CARDINAL,
350     &actual_type, &actual_format,
351     &nitems, &bytesafter,
352     (unsigned char **) &workspace) == Success
353     && workspace) {
354     /* Found a _WIN_WORKSPACE property - this is the desktop to look for.
355     * For now assume that this is Enlightenment.
356     * We're looking for a child of the root window that has an
357     * ENLIGHTENMENT_DESKTOP atom with a value equal to the root window's
358     * _WIN_WORKSPACE atom. */
359     Atom ENLIGHTENMENT_DESKTOP = XInternAtom(display,
360     "ENLIGHTENMENT_DESKTOP",
361     False);
362     /* First check to see if the root window is the current desktop... */
363     if (XGetWindowProperty(display, root,
364     ENLIGHTENMENT_DESKTOP, 0, 1,
365     False, XA_CARDINAL,
366     &actual_type, &actual_format,
367     &nitems, &bytesafter,
368     (unsigned char **) &desktop) == Success
369     && desktop && *desktop == *workspace) {
370     /* The root window is the current Enlightenment desktop */
371     printf( "Drawing to Enlightenment Desktop %lu (the root window)\n",
372     *desktop);
373     background = root;
374     XFree((char *) desktop);
375     }
376     /* Now look at each immediate child window of root to see if it is
377     * the current desktop */
378     else if (XQueryTree(display, root, &rootReturn, &parentReturn,
379     &children, &nChildren)) {
380     int i;
381     for (i = 0; i < nChildren; ++i) {
382     if (XGetWindowProperty(display, children[i],
383     ENLIGHTENMENT_DESKTOP, 0, 1,
384     False, XA_CARDINAL,
385     &actual_type, &actual_format,
386     &nitems, &bytesafter,
387     (unsigned char **) &desktop) == Success
388     && desktop && *desktop == *workspace) {
389     /* Found current Enlightenment desktop */
390     printf("Drawing to Enlightenment Desktop %lu\n",
391     *desktop);
392     background = *clientparent = children[i];
393     XFree((char *) desktop);
394     }
395     }
396     XFree((char *) children);
397     }
398     XFree((char *) workspace);
399     }
400     }
401     if (background) {
402     return background;
403     }
404     else {
405     return root;
406     }
407     }