tsst
[skippy-xd] / wm.c
1 /* Skippy - Seduces Kids Into Perversion
2  *
3  * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include "skippy.h"
21
22 #define WM_PERSONALITY_NETWM 0
23 #define WM_PERSONALITY_GNOME 1
24
25 /* From WindowMaker's gnome.c */
26 #define WIN_HINTS_SKIP_FOCUS      (1<<0) /*"alt-tab" skips this win*/
27 #define WIN_HINTS_SKIP_WINLIST    (1<<1) /*do not show in window list*/
28 #define WIN_HINTS_SKIP_TASKBAR    (1<<2) /*do not show on taskbar*/
29 #define WIN_HINTS_GROUP_TRANSIENT (1<<3) /*Reserved - definition is unclear*/
30 #define WIN_HINTS_FOCUS_ON_CLICK  (1<<4) /*app only accepts focus if clicked*/
31 #define WIN_HINTS_DO_NOT_COVER    (1<<5) /* attempt to not cover this window */
32
33
34 #define WIN_STATE_STICKY          (1<<0) /*everyone knows sticky*/
35 #define WIN_STATE_MINIMIZED       (1<<1) /*Reserved - definition is unclear*/
36 #define WIN_STATE_MAXIMIZED_VERT  (1<<2) /*window in maximized V state*/
37 #define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /*window in maximized H state*/
38 #define WIN_STATE_HIDDEN          (1<<4) /*not on taskbar but window visible*/
39 #define WIN_STATE_SHADED          (1<<5) /*shaded (MacOS / Afterstep style)*/
40 /* these are bogus states defined in "the spec" */
41 #define WIN_STATE_HID_WORKSPACE   (1<<6) /*not on current desktop*/
42 #define WIN_STATE_HID_TRANSIENT   (1<<7) /*owner of transient is hidden*/
43 #define WIN_STATE_FIXED_POSITION  (1<<8) /*window is fixed in position even*/
44 #define WIN_STATE_ARRANGE_IGNORE  (1<<9) /*ignore for auto arranging*/
45
46
47 static int WM_PERSONALITY = WM_PERSONALITY_NETWM,
48            NETWM_HAS_FULLSCREEN = 0,
49            IGNORE_SKIP_TASKBAR = 0;
50
51 void
52 wm_get_atoms(Display *dpy)
53 {
54         XA_WM_STATE = XInternAtom(dpy, "WM_STATE", 0);
55         WM_CLIENT_LEADER = XInternAtom(dpy, "WM_CLIENT_LEADER", 0);
56         XA_UTF8_STRING = XInternAtom(dpy, "UTF8_STRING", 0);
57         
58         _XROOTPMAP_ID = XInternAtom(dpy, "_XROOTPMAP_ID", 0);
59         ESETROOT_PMAP_ID = XInternAtom(dpy, "ESETROOT_PMAP_ID", 0);
60         
61         _NET_SUPPORTING_WM_CHECK = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", 0);
62         _NET_SUPPORTED = XInternAtom(dpy, "_NET_SUPPORTED", 0);
63         _NET_NUMBER_OF_DESKTOPS = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", 0);
64         _NET_CLIENT_LIST = XInternAtom(dpy, "_NET_CLIENT_LIST", 0);
65         _NET_CLIENT_LIST_STACKING = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", 0);
66         _NET_CURRENT_DESKTOP = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", 0);
67         _NET_WM_DESKTOP = XInternAtom(dpy, "_NET_WM_DESKTOP", 0);
68         _NET_WM_STATE = XInternAtom(dpy, "_NET_WM_STATE", 0);
69         _NET_WM_STATE_HIDDEN = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", 0);
70         _NET_WM_STATE_SKIP_TASKBAR = XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", 0);
71         _NET_WM_STATE_SKIP_PAGER = XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", 0);
72         _NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", 0);
73         _NET_WM_STATE_SHADED = XInternAtom(dpy, "_NET_WM_STATE_SHADED", 0);
74         _NET_WM_STATE_ABOVE = XInternAtom(dpy, "_NET_WM_STATE_ABOVE", 0);
75         _NET_WM_STATE_STICKY = XInternAtom(dpy, "_NET_WM_STATE_STICKY", 0);
76         _NET_WM_WINDOW_TYPE = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", 0);
77         _NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", 0);
78         _NET_WM_WINDOW_TYPE_DOCK = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", 0);
79         _NET_WM_VISIBLE_NAME = XInternAtom(dpy, "_NET_WM_VISIBLE_NAME", 0);
80         _NET_WM_NAME = XInternAtom(dpy, "_NET_WM_VISIBLE_NAME", 0);
81         
82         _WIN_SUPPORTING_WM_CHECK = XInternAtom(dpy, "_WIN_SUPPORTING_WM_CHECK", 0);
83         _WIN_WORKSPACE = XInternAtom(dpy, "_WIN_WORKSPACE", 0);
84         _WIN_WORKSPACE_COUNT = XInternAtom(dpy, "_WIN_WORKSPACE_COUNT", 0);
85         _WIN_PROTOCOLS = XInternAtom(dpy, "_WIN_PROTOCOLS", 0);
86         _WIN_CLIENT_LIST = XInternAtom(dpy, "_WIN_CLIENT_LIST", 0);
87         _WIN_STATE = XInternAtom(dpy, "_WIN_STATE", 0);
88         _WIN_HINTS = XInternAtom(dpy, "_WIN_HINTS", 0);
89 }
90
91 char
92 wm_check_netwm(Display *dpy)
93 {
94         Window wm_check;
95         unsigned char *data, *data2;
96         
97         int status, real_format;
98         Atom real_type;
99         unsigned long items_read, items_left, i;
100         
101         char req = 0;
102         
103         status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _NET_SUPPORTING_WM_CHECK,
104                           0L, 1L, False, XA_WINDOW, &real_type, &real_format,
105                           &items_read, &items_left, &data);
106         if(status != Success || ! items_read) {
107                 if(status == Success)
108                         XFree(data);
109                 return 0;
110         }
111         
112         wm_check = ((Window*)data)[0];
113         XFree(data);
114         
115         status = XGetWindowProperty(dpy, wm_check, _NET_SUPPORTING_WM_CHECK,
116                           0L, 1L, False, XA_WINDOW, &real_type, &real_format,
117                           &items_read, &items_left, &data);
118         
119         if(status != Success && ! items_read) {
120                 if(status == Success)
121                         XFree(data);
122                 return 0;
123         }
124         
125         if(wm_check != ((Window*)data)[0]) {
126                 XFree(data);
127                 return 0;
128         }
129         
130         XFree(data);
131         
132         status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _NET_SUPPORTED,
133                           0L, 8192L, False, XA_ATOM, &real_type, &real_format,
134                           &items_read, &items_left, &data2);
135         
136         if(status != Success || ! items_read) {
137                 if(status == Success)
138                         XFree(data2);
139                 return 0;
140         }
141         
142         for(i = 0; i < items_read; i++) {
143                 if(((Atom*)data2)[i] == _NET_NUMBER_OF_DESKTOPS)
144                         req |= 1;
145                 else if(((Atom*)data2)[i] == _NET_CURRENT_DESKTOP)
146                         req |= 2;
147                 else if(((Atom*)data2)[i] == _NET_WM_STATE)
148                         req |= 4;
149                 else if(((Atom*)data2)[i] == _NET_CLIENT_LIST)
150                         req |= 8;
151                 else if(((Atom*)data2)[i] == _NET_CLIENT_LIST_STACKING)
152                         req |= 16;
153                 else if(((Atom*)data2)[i] == _NET_WM_STATE_FULLSCREEN)
154                         NETWM_HAS_FULLSCREEN = 1;
155         }
156         XFree(data2);
157         if(req & 16) {
158                 req |= 8;
159                 _NET_CLIENT_LIST = _NET_CLIENT_LIST_STACKING;
160         } 
161         
162         return ((req & 15) == 15);
163 }
164
165 char
166 wm_check_gnome(Display *dpy)
167 {
168         Window wm_check;
169         unsigned char *data, *data2;
170         
171         int status, real_format;
172         Atom real_type;
173         unsigned long items_read, items_left, i;
174         
175         char req = 0;
176         
177         WM_PERSONALITY = WM_PERSONALITY_GNOME;
178         
179         status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _WIN_SUPPORTING_WM_CHECK,
180                           0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
181                           &items_read, &items_left, &data);
182         if(status != Success || ! items_read) {
183                 if(status == Success)
184                         XFree(data);
185                 return 0;
186         }
187         
188         wm_check = ((Window*)data)[0];
189         XFree(data);
190         
191         status = XGetWindowProperty(dpy, wm_check, _WIN_SUPPORTING_WM_CHECK,
192                           0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
193                           &items_read, &items_left, &data);
194         
195         if(status != Success && ! items_read) {
196                 if(status == Success)
197                         XFree(data);
198                 return 0;
199         }
200         
201         if(wm_check != ((Window*)data)[0]) {
202                 XFree(data);
203                 return 0;
204         }
205         
206         XFree(data);
207         
208         status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _WIN_PROTOCOLS,
209                           0L, 8192L, False, XA_ATOM, &real_type, &real_format,
210                           &items_read, &items_left, &data2);
211         
212         if(status != Success || ! items_read) {
213                 if(status == Success)
214                         XFree(data2);
215                 return 0;
216         }
217         
218         for(i = 0; i < items_read; i++) {
219                 if(((Atom*)data2)[i] == _WIN_WORKSPACE)
220                         req |= 1;
221                 else if(((Atom*)data2)[i] == _WIN_WORKSPACE_COUNT)
222                         req |= 2;
223                 else if(((Atom*)data2)[i] == _WIN_STATE)
224                         req |= 4;
225                 else if(((Atom*)data2)[i] == _WIN_CLIENT_LIST)
226                         req |= 8;
227         }
228         XFree(data2);
229         
230         return ((req & 15) == 15);
231 }
232
233 char
234 wm_check(Display *dpy)
235 {
236         return wm_check_netwm(dpy) || wm_check_gnome(dpy);
237 }
238
239 dlist *
240 wm_get_stack(Display *dpy)
241 {
242         dlist *l = 0;
243         unsigned char *data;
244         int status, real_format;
245         Atom real_type;
246         unsigned long items_read, items_left, i;
247         
248         if(WM_PERSONALITY == WM_PERSONALITY_NETWM)
249                 status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _NET_CLIENT_LIST,
250                                   0L, 8192L, False, XA_WINDOW, &real_type, &real_format,
251                                   &items_read, &items_left, &data);
252         else
253                 status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _WIN_CLIENT_LIST,
254                                   0L, 8192L, False, XA_CARDINAL, &real_type, &real_format,
255                                   &items_read, &items_left, &data);
256         
257         if(status != Success)
258                 return 0;
259         
260         for(i = 0; i < items_read; i++)
261                 l = dlist_add(l, (void*)((CARD32*)data)[i]);
262         
263         XFree(data);
264         
265         return l;
266 }
267
268 Pixmap
269 wm_get_root_pmap(Display *dpy)
270 {
271         Pixmap rootpmap = None;
272         unsigned char *data;
273         int status, real_format;
274         Atom real_type;
275         unsigned long items_read, items_left;
276         
277         status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _XROOTPMAP_ID,
278                           0L, 1L, False, XA_PIXMAP, &real_type, &real_format,
279                           &items_read, &items_left, &data);
280         if(status != Success) {
281                 status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), ESETROOT_PMAP_ID,
282                                   0L, 1L, False, XA_PIXMAP, &real_type, &real_format,
283                                   &items_read, &items_left, &data);
284                 if(status != Success)
285                         return None;
286         }
287         
288         if(items_read)
289                 rootpmap = ((Pixmap*)data)[0];
290         
291         XFree(data);
292         
293         return rootpmap;
294 }
295
296 CARD32
297 wm_get_current_desktop(Display *dpy)
298 {
299         CARD32 desktop = 1;
300         /*unsigned char *data;
301         int status, real_format;
302         Atom real_type;
303         unsigned long items_read, items_left;
304         
305         status = XGetWindowProperty(dpy, DefaultRootWindow(dpy),
306                           (WM_PERSONALITY == WM_PERSONALITY_NETWM) ?  _NET_CURRENT_DESKTOP : _WIN_WORKSPACE,
307                           0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
308                           &items_read, &items_left, &data);
309         if(status != Success)
310                 return 0;
311         if(items_read)
312                 desktop = ((CARD32*)data)[0];
313         XFree(data);*/
314         
315         return desktop;
316 }
317
318 FcChar8 *
319 wm_get_window_title(Display *dpy, Window window, int *length_return)
320 {
321         unsigned char *data;
322         FcChar8 *ret = 0;
323         int status, real_format;
324         Atom real_type;
325         unsigned long items_read, items_left;
326         
327         *length_return = 0;
328         
329         status = XGetWindowProperty(dpy, window, _NET_WM_VISIBLE_NAME,
330                           0, 8192, False, XA_UTF8_STRING, &real_type, &real_format,
331                           &items_read, &items_left, &data);
332         if(status != Success || items_read == 0)
333         {
334                 if(status == Success)
335                         XFree(data);
336                 status = XGetWindowProperty(dpy, window, _NET_WM_NAME,
337                                   0, 8192, False, XA_UTF8_STRING, &real_type, &real_format,
338                                   &items_read, &items_left, &data);
339         }
340         if(status != Success || items_read == 0)
341         {
342                 if(status == Success)
343                         XFree(data);
344                 status = XGetWindowProperty(dpy, window, XA_WM_NAME,
345                                   0, 8192, False, XA_STRING, &real_type, &real_format,
346                                   &items_read, &items_left, &data);
347         }
348         if(status != Success)
349                 return 0;
350         
351         if(items_read)
352         {
353                 ret = (FcChar8 *)malloc(items_read);
354                 memcpy(ret, data, items_read);
355                 *length_return = items_read;
356         }
357         
358         XFree(data);
359         
360         return ret;
361 }
362
363 Window
364 wm_get_group_leader(Display *dpy, Window window)
365 {
366         unsigned char *data;
367         int status, real_format;
368         Atom real_type;
369         unsigned long items_read, items_left;
370         Window leader = None;
371         
372         status = XGetWindowProperty(dpy, window, WM_CLIENT_LEADER,
373                           0, 1, False, XA_WINDOW, &real_type, &real_format,
374                           &items_read, &items_left, &data);
375         
376         if(status != Success)
377         {
378                 XWMHints *hints = XGetWMHints(dpy, window);
379                 if(! hints)
380                         return None;
381                 
382                 if(hints->flags & WindowGroupHint)
383                         leader = hints->window_group;
384                 
385                 return leader;
386         }
387         
388         if(items_read)
389                 leader = ((Window*)data)[0];
390         
391         XFree(data);
392         
393         return leader;
394 }
395
396 void
397 wm_use_netwm_fullscreen(Bool b)
398 {
399         NETWM_HAS_FULLSCREEN = b ? NETWM_HAS_FULLSCREEN : False;
400 }
401
402 void
403 wm_ignore_skip_taskbar(Bool b)
404 {
405         IGNORE_SKIP_TASKBAR = b;
406 }
407
408 void
409 wm_set_fullscreen(Display *dpy, Window window, int x, int y, unsigned int width, unsigned int height)
410 {
411         if(WM_PERSONALITY == WM_PERSONALITY_NETWM && NETWM_HAS_FULLSCREEN)
412         {
413                 Atom props[6];
414                 CARD32 desktop = (CARD32)0;
415                 
416                 props[0] = _NET_WM_STATE_FULLSCREEN;
417                 props[1] = _NET_WM_STATE_SKIP_TASKBAR;
418                 props[2] = _NET_WM_STATE_SKIP_PAGER;
419                 props[3] = _NET_WM_STATE_ABOVE;
420                 props[4] = _NET_WM_STATE_STICKY;
421                 props[5] = 0;
422                 XChangeProperty(dpy, window, _NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char*)props, 5);
423                 XChangeProperty(dpy, window, _NET_WM_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&desktop, 1);
424         }
425         else
426         {
427                 XSetWindowAttributes wattr;
428                 wattr.override_redirect = True;
429                 XChangeWindowAttributes(dpy, window, CWOverrideRedirect, &wattr);
430                 XMoveResizeWindow(dpy, window, x, y, width, height);
431         }
432 }
433
434 int
435 wm_validate_window(Display *dpy, Window win)
436 {
437         unsigned char *data;
438         Atom *atoms;
439         int status, real_format;
440         Atom real_type;
441         unsigned long items_read, items_left, i;
442         int result = 1;
443         
444         if(WM_PERSONALITY == WM_PERSONALITY_NETWM)
445         {
446                 status = XGetWindowProperty(dpy, win, _NET_WM_STATE,
447                                   0L, 8192L, False, XA_ATOM, &real_type, &real_format,
448                                   &items_read, &items_left, &data);
449                 
450                 if(status != Success)
451                         return 0;
452                 
453                 atoms = (Atom *)data;
454                 
455                 for(i = 0; result && i < items_read; i++) {
456                         if(atoms[i] == _NET_WM_STATE_HIDDEN)
457                                 result = 0;
458                         else if(! IGNORE_SKIP_TASKBAR && atoms[i] == _NET_WM_STATE_SKIP_TASKBAR)
459                                 result = 0;
460                         else if(atoms[i] == _NET_WM_STATE_SHADED)
461                                 result = 0;
462                         if(! result)
463                                 break;
464                 }
465                 XFree(data);
466                 
467                 if(! result)
468                         return 0;
469                 
470                 status = XGetWindowProperty(dpy, win, _NET_WM_WINDOW_TYPE,
471                                             0L, 1L, False, XA_ATOM, &real_type, &real_format,
472                                             &items_read, &items_left, &data);
473                 if(status != Success)
474                         return 1;
475                 
476                 atoms = (Atom *)data;
477                 
478                 if(items_read && (atoms[0] == _NET_WM_WINDOW_TYPE_DESKTOP || atoms[0] == _NET_WM_WINDOW_TYPE_DOCK))
479                         result = 0;
480                 
481                 XFree(data);
482                 
483                 return result;
484         } else {
485                 CARD32 attr;
486                 
487                 status = XGetWindowProperty(dpy, win, _WIN_STATE,
488                                   0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
489                                   &items_read, &items_left, &data);
490                 if(status != Success || ! items_read)
491                 {
492                         if(status == Success)
493                                 XFree(data);
494                         return 0;
495                 }
496                 attr = (((CARD32*)data)[0]) & (WIN_STATE_MINIMIZED |
497                                              WIN_STATE_SHADED |
498                                              WIN_STATE_HIDDEN);
499                 if(attr)
500                         result = 0;
501                 XFree(data);
502                 if(! result)
503                         return 0;
504                 
505                 if(! IGNORE_SKIP_TASKBAR)
506                 {
507                         status = XGetWindowProperty(dpy, win, _WIN_HINTS,
508                                           0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
509                                           &items_read, &items_left, &data);
510                         if(status != Success || ! items_read)
511                         {
512                                 if(status == Success)
513                                         XFree(data);
514                                 return 1; /* If there's no _WIN_HINTS, assume it's 0, thus valid */
515                         }
516                         attr = ((CARD32*)data)[0];
517                         if(attr & WIN_HINTS_SKIP_TASKBAR)
518                                 result = 0;
519                         XFree(data);
520                 }
521                 
522                 return result;
523         }
524 }
525
526 CARD32
527 wm_get_window_desktop(Display *dpy, Window win)
528 {
529         int status, real_format;
530         Atom real_type;
531         unsigned long items_read, items_left;
532         unsigned char *data;
533         CARD32 desktop = 0;
534         
535         if(WM_PERSONALITY == WM_PERSONALITY_GNOME)
536         {
537                 status = XGetWindowProperty(dpy, win, _WIN_STATE,
538                                   0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
539                                   &items_read, &items_left, &data);
540                 if(status == Success)
541                 {
542                         if(items_read)
543                                 desktop = (((CARD32*)data)[0] & WIN_STATE_STICKY) ? (CARD32)-1 : 0;
544                         
545                         XFree(data);
546                         
547                         if(desktop)
548                                 return desktop;
549                 }
550         }
551         
552         status = XGetWindowProperty(dpy, win,
553                           (WM_PERSONALITY == WM_PERSONALITY_NETWM) ? _NET_WM_DESKTOP : _WIN_WORKSPACE,
554                           0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
555                           &items_read, &items_left, &data);
556         
557         if(status != Success)
558                 return wm_get_current_desktop(dpy);
559         
560         if(items_read)
561                 desktop = ((CARD32*)data)[0];
562         else
563                 desktop = wm_get_current_desktop(dpy);
564         
565         XFree(data);
566         
567         return desktop;
568 }
569
570 /* Get focused window and traverse towards the root window until a window with WM_STATE is found */
571 Window
572 wm_get_focused(Display *dpy)
573 {
574         Window focused = None, root = None, *children;
575         unsigned int tmp_u;
576         int revert_to, status, real_format;
577         Atom real_type;
578         unsigned long items_read, items_left;
579         unsigned char *data;
580         
581         XGetInputFocus(dpy, &focused, &revert_to);
582         
583         /*printf("%x\n",focused);*/
584
585         while(focused != None && focused != root)
586         {
587                 status = XGetWindowProperty(dpy, focused, XA_WM_STATE,
588                                             0L, 1L, False, XA_WM_STATE, &real_type, &real_format,
589                                             &items_read, &items_left, &data);
590                 if(status == Success)
591                 {
592                         XFree(data);
593                         if(items_read)
594                                 break;
595                 }
596                 XQueryTree(dpy, focused, &root, &focused, &children, &tmp_u);
597                 if(children)
598                         XFree(children);
599         }
600         
601         return focused;
602 }