-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtoon_root.c
420 lines (403 loc) · 15.1 KB
/
toon_root.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
/* 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. */
#include "toon.h"
#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;
}
}