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