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 (20 years 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

# Content
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 }