more code for proplist handling (almost finished)
[wmaker-crm.git] / src / gnome.c
blob7fcc04d1e88f31ba8c006f10da3d2a2d2e18643f
1 /* gnome.c-- support for the GNOME Hints
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1998, 1999 Alfredo K. Kojima
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
24 * According to the author of this thing, it should not be taken seriously.
25 * IMHO, there are lot's of weirdnesses and it's quite unelegant. I'd
26 * rather not support it, but here it goes anyway.
29 #include "wconfig.h"
31 #ifdef GNOME_STUFF
33 #include <X11/Xlib.h>
34 #include <X11/Xutil.h>
35 #include <X11/Xatom.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
42 #include "WindowMaker.h"
43 #include "screen.h"
44 #include "wcore.h"
45 #include "framewin.h"
46 #include "window.h"
47 #include "workspace.h"
48 #include "funcs.h"
49 #include "actions.h"
50 #include "stacking.h"
52 #include "gnome.h"
59 #define WIN_HINTS_SKIP_FOCUS (1<<0) /*"alt-tab" skips this win*/
60 #define WIN_HINTS_SKIP_WINLIST (1<<1) /*do not show in window list*/
61 #define WIN_HINTS_SKIP_TASKBAR (1<<2) /*do not show on taskbar*/
62 #define WIN_HINTS_GROUP_TRANSIENT (1<<3) /*Reserved - definition is unclear*/
63 #define WIN_HINTS_FOCUS_ON_CLICK (1<<4) /*app only accepts focus if clicked*/
64 #define WIN_HINTS_DO_NOT_COVER (1<<5) /* attempt to not cover this window */
67 #define WIN_STATE_STICKY (1<<0) /*everyone knows sticky*/
68 #define WIN_STATE_MINIMIZED (1<<1) /*Reserved - definition is unclear*/
69 #define WIN_STATE_MAXIMIZED_VERT (1<<2) /*window in maximized V state*/
70 #define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /*window in maximized H state*/
71 #define WIN_STATE_HIDDEN (1<<4) /*not on taskbar but window visible*/
72 #define WIN_STATE_SHADED (1<<5) /*shaded (MacOS / Afterstep style)*/
73 /* these are bogus states defined in "the spec" */
74 #define WIN_STATE_HID_WORKSPACE (1<<6) /*not on current desktop*/
75 #define WIN_STATE_HID_TRANSIENT (1<<7) /*owner of transient is hidden*/
76 #define WIN_STATE_FIXED_POSITION (1<<8) /*window is fixed in position even*/
77 #define WIN_STATE_ARRANGE_IGNORE (1<<9) /*ignore for auto arranging*/
80 #define WIN_LAYER_DESKTOP 0
81 #define WIN_LAYER_BELOW 2
82 #define WIN_LAYER_NORMAL 4
83 #define WIN_LAYER_ONTOP 6
84 #define WIN_LAYER_DOCK 8
85 #define WIN_LAYER_ABOVE_DOCK 10
86 #define WIN_LAYER_MENU 12
90 static Atom _XA_WIN_SUPPORTING_WM_CHECK = 0;
91 static Atom _XA_WIN_PROTOCOLS;
92 static Atom _XA_WIN_LAYER;
93 static Atom _XA_WIN_STATE;
94 static Atom _XA_WIN_HINTS;
95 static Atom _XA_WIN_APP_STATE;
96 static Atom _XA_WIN_EXPANDED_SIZE;
97 static Atom _XA_WIN_ICONS;
98 static Atom _XA_WIN_WORKSPACE;
99 static Atom _XA_WIN_WORKSPACE_COUNT;
100 static Atom _XA_WIN_WORKSPACE_NAMES;
101 static Atom _XA_WIN_CLIENT_LIST;
102 static Atom _XA_WIN_DESKTOP_BUTTON_PROXY;
105 static void observer(void *self, WMNotification *notif);
106 static void wsobserver(void *self, WMNotification *notif);
109 void
110 wGNOMEInitStuff(WScreen *scr)
112 Atom supportedStuff[10];
113 int count;
115 if (!_XA_WIN_SUPPORTING_WM_CHECK) {
117 _XA_WIN_SUPPORTING_WM_CHECK =
118 XInternAtom(dpy, "_WIN_SUPPORTING_WM_CHECK", False);
120 _XA_WIN_PROTOCOLS = XInternAtom(dpy, "_WIN_PROTOCOLS", False);
122 _XA_WIN_LAYER = XInternAtom(dpy, "_WIN_LAYER", False);
124 _XA_WIN_STATE = XInternAtom(dpy, "_WIN_STATE", False);
126 _XA_WIN_HINTS = XInternAtom(dpy, "_WIN_HINTS", False);
128 _XA_WIN_APP_STATE = XInternAtom(dpy, "_WIN_APP_STATE", False);
130 _XA_WIN_EXPANDED_SIZE = XInternAtom(dpy, "_WIN_EXPANDED_SIZE", False);
132 _XA_WIN_ICONS = XInternAtom(dpy, "_WIN_ICONS", False);
134 _XA_WIN_WORKSPACE = XInternAtom(dpy, "_WIN_WORKSPACE", False);
136 _XA_WIN_WORKSPACE_COUNT =
137 XInternAtom(dpy, "_WIN_WORKSPACE_COUNT", False);
139 _XA_WIN_WORKSPACE_NAMES =
140 XInternAtom(dpy, "_WIN_WORKSPACE_NAMES", False);
142 _XA_WIN_CLIENT_LIST = XInternAtom(dpy, "_WIN_CLIENT_LIST", False);
144 _XA_WIN_DESKTOP_BUTTON_PROXY =
145 XInternAtom(dpy, "_WIN_DESKTOP_BUTTON_PROXY", False);
148 /* I'd rather use the ICCCM 2.0 mechanisms, but
149 * since some people prefer to reinvent the wheel instead of
150 * conforming to standards... */
152 /* setup the "We're compliant, you idiot!" hint */
154 /* why XA_CARDINAL instead of XA_WINDOW? */
155 XChangeProperty(dpy, scr->root_win, _XA_WIN_SUPPORTING_WM_CHECK,
156 XA_CARDINAL, 32, PropModeReplace,
157 (unsigned char*)&scr->no_focus_win, 1);
159 XChangeProperty(dpy, scr->no_focus_win, _XA_WIN_SUPPORTING_WM_CHECK,
160 XA_CARDINAL, 32, PropModeReplace,
161 (unsigned char*)&scr->no_focus_win, 1);
164 /* setup the "desktop button proxy" thing */
165 XChangeProperty(dpy, scr->root_win, _XA_WIN_DESKTOP_BUTTON_PROXY,
166 XA_CARDINAL, 32, PropModeReplace,
167 (unsigned char*)&scr->no_focus_win, 1);
168 XChangeProperty(dpy, scr->no_focus_win, _XA_WIN_DESKTOP_BUTTON_PROXY,
169 XA_CARDINAL, 32, PropModeReplace,
170 (unsigned char*)&scr->no_focus_win, 1);
173 /* setup the list of supported protocols */
174 count = 0;
175 supportedStuff[count++] = _XA_WIN_LAYER;
176 supportedStuff[count++] = _XA_WIN_STATE;
177 supportedStuff[count++] = _XA_WIN_HINTS;
178 supportedStuff[count++] = _XA_WIN_APP_STATE;
179 supportedStuff[count++] = _XA_WIN_EXPANDED_SIZE;
180 supportedStuff[count++] = _XA_WIN_ICONS;
181 supportedStuff[count++] = _XA_WIN_WORKSPACE;
182 supportedStuff[count++] = _XA_WIN_WORKSPACE_COUNT;
183 supportedStuff[count++] = _XA_WIN_WORKSPACE_NAMES;
184 supportedStuff[count++] = _XA_WIN_CLIENT_LIST;
186 XChangeProperty(dpy, scr->root_win, _XA_WIN_PROTOCOLS, XA_ATOM, 32,
187 PropModeReplace, (unsigned char*)supportedStuff, count);
189 XFlush(dpy);
191 WMAddNotificationObserver(observer, NULL, WMNManaged, NULL);
192 WMAddNotificationObserver(observer, NULL, WMNUnmanaged, NULL);
193 WMAddNotificationObserver(observer, NULL, WMNChangedWorkspace, NULL);
194 WMAddNotificationObserver(observer, NULL, WMNChangedState, NULL);
195 WMAddNotificationObserver(observer, NULL, WMNChangedFocus, NULL);
196 WMAddNotificationObserver(observer, NULL, WMNChangedStacking, NULL);
197 WMAddNotificationObserver(observer, NULL, WMNChangedName, NULL);
199 WMAddNotificationObserver(wsobserver, NULL, WMNWorkspaceChanged, NULL);
200 WMAddNotificationObserver(wsobserver, NULL, WMNWorkspaceNameChanged, NULL);
204 void
205 wGNOMEUpdateClientListHint(WScreen *scr)
207 WWindow *wwin;
208 Window *windows;
209 int count;
211 windows = malloc(sizeof(Window)*scr->window_count);
212 if (!windows) {
213 wwarning(_("out of memory while updating GNOME hints"));
214 return;
217 count = 0;
218 wwin = scr->focused_window;
219 while (wwin) {
220 if (!wwin->flags.internal_window &&
221 !wwin->client_flags.skip_window_list) {
223 windows[count++] = wwin->client_win;
226 wwin = wwin->prev;
229 XChangeProperty(dpy, scr->root_win, _XA_WIN_CLIENT_LIST, XA_CARDINAL, 32,
230 PropModeReplace, (unsigned char *)windows, count);
232 wfree(windows);
233 XFlush(dpy);
237 void
238 wGNOMEUpdateWorkspaceHints(WScreen *scr)
240 long val;
242 val = scr->workspace_count;
244 XChangeProperty(dpy, scr->root_win, _XA_WIN_WORKSPACE_COUNT, XA_CARDINAL,
245 32, PropModeReplace, (unsigned char*)&val, 1);
247 wGNOMEUpdateWorkspaceNamesHint(scr);
251 void
252 wGNOMEUpdateWorkspaceNamesHint(WScreen *scr)
254 char *wsNames[MAX_WORKSPACES];
255 XTextProperty textProp;
256 int i;
258 for (i = 0; i < scr->workspace_count; i++) {
259 wsNames[i] = scr->workspaces[i]->name;
262 if (XStringListToTextProperty(wsNames, scr->workspace_count, &textProp)) {
263 XSetTextProperty(dpy, scr->root_win, &textProp,
264 _XA_WIN_WORKSPACE_NAMES);
265 XFree(textProp.value);
270 void
271 wGNOMEUpdateCurrentWorkspaceHint(WScreen *scr)
273 long val;
275 val = scr->current_workspace;
277 XChangeProperty(dpy, scr->root_win, _XA_WIN_WORKSPACE, XA_CARDINAL,
278 32, PropModeReplace, (unsigned char*)&val, 1);
282 static int
283 getWindowLevel(int layer)
285 int level;
287 if (layer <= WIN_LAYER_DESKTOP)
288 level = WMDesktopLevel;
289 else if (layer <= WIN_LAYER_BELOW)
290 level = WMSunkenLevel;
291 else if (layer <= WIN_LAYER_NORMAL)
292 level = WMNormalLevel;
293 else if (layer <= WIN_LAYER_ONTOP)
294 level = WMFloatingLevel;
295 else if (layer <= WIN_LAYER_DOCK)
296 level = WMDockLevel;
297 else if (layer <= WIN_LAYER_ABOVE_DOCK)
298 level = WMSubmenuLevel;
299 else if (layer <= WIN_LAYER_MENU)
300 level = WMMainMenuLevel;
301 else
302 level = WMOuterSpaceLevel;
304 return level;
308 Bool
309 wGNOMECheckClientHints(WWindow *wwin, int *layer, int *workspace)
311 Atom type_ret;
312 int fmt_ret;
313 unsigned long nitems_ret;
314 unsigned long bytes_after_ret;
315 long flags, val, *data = 0;
316 Bool hasHints = False;
318 /* hints */
320 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_HINTS, 0, 1, False,
321 /* should be XA_INTEGER, but spec is broken */
322 XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
323 &bytes_after_ret,
324 (unsigned char**)&data)==Success && data) {
325 flags = *data;
327 XFree(data);
329 if (flags & (WIN_HINTS_SKIP_FOCUS|WIN_HINTS_SKIP_WINLIST
330 |WIN_HINTS_SKIP_TASKBAR)) {
331 wwin->client_flags.skip_window_list = 1;
334 /* client reserved area, for the panel */
335 if (flags & (WIN_HINTS_DO_NOT_COVER)) {
336 WReservedArea *area;
338 area = malloc(sizeof(WReservedArea));
339 if (!area) {
340 wwarning(_("out of memory while updating GNOME hints"));
341 } else {
342 XWindowAttributes wattribs;
344 XGetWindowAttributes(dpy, wwin->client_win, &wattribs);
345 wClientGetNormalHints(wwin, &wattribs, False,
346 &area->area.x1, &area->area.y1,
347 &area->area.x2, &area->area.y2);
348 area->area.x2 = area->area.x2 + area->area.x1;
349 area->area.y2 = area->area.y2 + area->area.y1;
351 area->window = wwin->client_win;
353 area->next = wwin->screen_ptr->reservedAreas;
354 wwin->screen_ptr->reservedAreas = area;
356 wScreenUpdateUsableArea(wwin->screen_ptr);
359 hasHints = True;
362 /* layer */
363 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_LAYER, 0, 1, False,
364 XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
365 &bytes_after_ret,
366 (unsigned char**)&data)==Success && data) {
367 val = *data;
369 XFree(data);
371 *layer = getWindowLevel(val);
372 hasHints = True;
375 /* workspace */
376 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_WORKSPACE, 0, 1,
377 False, XA_CARDINAL, &type_ret, &fmt_ret,
378 &nitems_ret, &bytes_after_ret,
379 (unsigned char**)&data)==Success && data) {
380 val = *data;
382 XFree(data);
384 if (val > 0)
385 *workspace = val;
386 hasHints = True;
389 /* reserved area */
390 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_EXPANDED_SIZE, 0, 1,
391 False, XA_CARDINAL, &type_ret, &fmt_ret,
392 &nitems_ret, &bytes_after_ret,
393 (unsigned char**)&data)==Success && data) {
394 WReservedArea *area;
396 area = malloc(sizeof(WReservedArea));
397 if (!area) {
398 wwarning(_("out of memory while updating GNOME hints"));
399 } else {
400 area->area.x1 = data[0];
401 area->area.y1 = data[1];
402 area->area.x2 = data[2] - data[0];
403 area->area.y2 = data[3] - data[1];
404 XFree(data);
406 area->window = wwin->client_win;
409 area->next = wwin->screen_ptr->reservedAreas;
410 wwin->screen_ptr->reservedAreas = area;
412 wScreenUpdateUsableArea(wwin->screen_ptr);
413 hasHints = True;
416 return hasHints;
420 Bool
421 wGNOMECheckInitialClientState(WWindow *wwin)
423 Atom type_ret;
424 int fmt_ret;
425 unsigned long nitems_ret;
426 unsigned long bytes_after_ret;
427 long flags, *data = 0;
429 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_STATE, 0, 1, False,
430 XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
431 &bytes_after_ret,
432 (unsigned char**)&data)!=Success || !data)
433 return False;
435 flags = *data;
437 XFree(data);
439 if (flags & WIN_STATE_STICKY)
440 wwin->client_flags.omnipresent = 1;
442 if (flags & (WIN_STATE_MAXIMIZED_VERT|WIN_STATE_MAXIMIZED_HORIZ)) {
444 if (flags & WIN_STATE_MAXIMIZED_VERT)
445 wwin->flags.maximized |= MAX_VERTICAL;
447 if (flags & WIN_STATE_MAXIMIZED_HORIZ)
448 wwin->flags.maximized |= MAX_HORIZONTAL;
451 if (flags & WIN_STATE_SHADED)
452 wwin->flags.shaded = 1;
454 return True;
458 void
459 wGNOMEUpdateClientStateHint(WWindow *wwin, Bool changedWorkspace)
461 long val;
462 long flags = 0;
464 if (changedWorkspace) {
465 val = wwin->frame->workspace;
467 XChangeProperty(dpy, wwin->client_win, _XA_WIN_WORKSPACE, XA_CARDINAL,
468 32, PropModeReplace, (unsigned char*)&val, 1);
470 if (val != wwin->screen_ptr->current_workspace)
471 flags |= WIN_STATE_HID_WORKSPACE;
474 if (IS_OMNIPRESENT(wwin))
475 flags |= WIN_STATE_STICKY;
477 if (wwin->flags.miniaturized)
478 flags |= WIN_STATE_MINIMIZED;
480 if (wwin->flags.maximized & MAX_VERTICAL)
481 flags |= WIN_STATE_MAXIMIZED_VERT;
483 if (wwin->flags.maximized & MAX_HORIZONTAL)
484 flags |= WIN_STATE_MAXIMIZED_HORIZ;
486 if (wwin->flags.shaded)
487 flags |= WIN_STATE_SHADED;
489 if (wwin->transient_for != None) {
490 WWindow *owner = wWindowFor(wwin->transient_for);
492 if (owner && !owner->flags.mapped)
493 flags |= WIN_STATE_HID_TRANSIENT;
496 /* ? */
497 if (wwin->flags.hidden)
498 flags |= WIN_STATE_HIDDEN;
500 XChangeProperty(dpy, wwin->client_win, _XA_WIN_STATE, XA_CARDINAL,
501 32, PropModeReplace, (unsigned char*)&flags, 1);
505 Bool
506 wGNOMEProcessClientMessage(XClientMessageEvent *event)
508 WScreen *scr;
509 WWindow *wwin;
510 Bool done = True;
512 scr = wScreenForWindow(event->window);
513 if (scr) {
514 /* generic client messages */
515 if (event->message_type == _XA_WIN_WORKSPACE) {
516 wWorkspaceChange(scr, event->data.l[0]);
517 } else {
518 done = False;
521 if (done)
522 return True;
525 /* window specific client messages */
527 wwin = wWindowFor(event->window);
528 if (!wwin)
529 return False;
531 if (event->message_type == _XA_WIN_LAYER) {
532 int level = getWindowLevel(event->data.l[0]);
534 if (WINDOW_LEVEL(wwin) != level) {
535 ChangeStackingLevel(wwin->frame->core, level);
537 } else if (event->message_type == _XA_WIN_STATE) {
538 int flags, mask;
539 int maximize = 0;
541 mask = event->data.l[0];
542 flags = event->data.l[1];
544 if (mask & WIN_STATE_STICKY) {
545 if ((flags & WIN_STATE_STICKY) != WFLAGP(wwin, omnipresent)) {
546 wWindowSetOmnipresent(wwin, (flags & WIN_STATE_STICKY)!=0);
550 if (mask & WIN_STATE_MAXIMIZED_VERT) {
551 if (flags & WIN_STATE_MAXIMIZED_VERT)
552 maximize = MAX_VERTICAL;
553 else
554 maximize = 0;
555 } else {
556 maximize = wwin->flags.maximized & MAX_VERTICAL;
559 if (mask & WIN_STATE_MAXIMIZED_HORIZ) {
560 if (flags & WIN_STATE_MAXIMIZED_HORIZ)
561 maximize |= MAX_HORIZONTAL;
562 else
563 maximize |= 0;
564 } else {
565 maximize |= wwin->flags.maximized & MAX_HORIZONTAL;
568 if (maximize != wwin->flags.maximized) {
569 #define both (MAX_HORIZONTAL|MAX_VERTICAL)
570 if (!(maximize & both) && (wwin->flags.maximized & both)) {
571 wUnmaximizeWindow(wwin);
573 if ((maximize & both) && !(wwin->flags.maximized & both)) {
574 wMaximizeWindow(wwin, maximize);
576 #undef both
579 if (mask & WIN_STATE_SHADED) {
580 if ((flags & WIN_STATE_SHADED) != wwin->flags.shaded) {
581 if (wwin->flags.shaded)
582 wUnshadeWindow(wwin);
583 else
584 wShadeWindow(wwin);
587 } else if (event->message_type == _XA_WIN_WORKSPACE) {
589 if (event->data.l[0] != wwin->frame->workspace) {
590 wWindowChangeWorkspace(wwin, event->data.l[0]);
592 } else {
593 done = False;
596 return done;
600 Bool
601 wGNOMEProxyizeButtonEvent(WScreen *scr, XEvent *event)
603 if (event->type == ButtonPress)
604 XUngrabPointer(dpy, CurrentTime);
605 XSendEvent(dpy, scr->no_focus_win, False, SubstructureNotifyMask, event);
607 return True;
611 void
612 wGNOMERemoveClient(WWindow *wwin)
614 int flag = 0;
615 WReservedArea *area;
617 wGNOMEUpdateClientListHint(wwin->screen_ptr);
619 area = wwin->screen_ptr->reservedAreas;
621 if (area) {
622 if (area->window == wwin->client_win) {
623 wwin->screen_ptr->reservedAreas = area->next;
624 wfree(area);
625 flag = 1;
626 } else {
627 while (area->next && area->next->window != wwin->client_win)
628 area = area->next;
630 if (area->next) {
631 WReservedArea *next;
633 next = area->next->next;
634 wfree(area->next);
635 area->next = next;
637 flag = 1;
642 if (flag) {
643 wScreenUpdateUsableArea(wwin->screen_ptr);
650 static void observer(void *self, WMNotification *notif)
652 WWindow *wwin = (WWindow*)WMGetNotificationObject(notif);
653 const char *name = WMGetNotificationName(notif);
655 if (strcmp(name, WMNManaged) == 0 && wwin) {
656 wGNOMEUpdateClientStateHint(wwin, True);
658 wGNOMEUpdateClientListHint(wwin->screen_ptr);
659 } else if (strcmp(name, WMNUnmanaged) == 0 && wwin) {
660 wGNOMERemoveClient(wwin);
661 } else if (strcmp(name, WMNChangedWorkspace) == 0 && wwin) {
662 wGNOMEUpdateClientStateHint(wwin, True);
663 } else if (strcmp(name, WMNChangedState) == 0 && wwin) {
664 wGNOMEUpdateClientStateHint(wwin, False);
668 static void wsobserver(void *self, WMNotification *notif)
670 WScreen *scr = (WScreen*)WMGetNotificationObject(notif);
671 const char *name = WMGetNotificationName(notif);
673 if (strcmp(name, WMNWorkspaceCreated) == 0) {
674 wGNOMEUpdateWorkspaceHints(scr);
675 } else if (strcmp(name, WMNWorkspaceDestroyed) == 0) {
676 wGNOMEUpdateWorkspaceHints(scr);
677 } else if (strcmp(name, WMNWorkspaceNameChanged) == 0) {
678 wGNOMEUpdateWorkspaceNamesHint(scr);
679 } else if (strcmp(name, WMNWorkspaceChanged) == 0) {
680 wGNOMEUpdateCurrentWorkspaceHint(scr);
683 } else if (strcmp(name, WMNResetStacking) == 0) {
689 #endif /* GNOME_STUFF */