- Fixed crashing bug in menu.c
[wmaker-crm.git] / src / gnome.c
blob05f3c244e6f542657e94dd912596c5d7451b4b62
1 /* gnome.c-- support for the GNOME Hints
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1998-2003 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"
51 #include "client.h"
53 #include "gnome.h"
60 #define WIN_HINTS_SKIP_FOCUS (1<<0) /*"alt-tab" skips this win*/
61 #define WIN_HINTS_SKIP_WINLIST (1<<1) /*do not show in window list*/
62 #define WIN_HINTS_SKIP_TASKBAR (1<<2) /*do not show on taskbar*/
63 #define WIN_HINTS_GROUP_TRANSIENT (1<<3) /*Reserved - definition is unclear*/
64 #define WIN_HINTS_FOCUS_ON_CLICK (1<<4) /*app only accepts focus if clicked*/
65 #define WIN_HINTS_DO_NOT_COVER (1<<5) /* attempt to not cover this window */
68 #define WIN_STATE_STICKY (1<<0) /*everyone knows sticky*/
69 #define WIN_STATE_MINIMIZED (1<<1) /*Reserved - definition is unclear*/
70 #define WIN_STATE_MAXIMIZED_VERT (1<<2) /*window in maximized V state*/
71 #define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /*window in maximized H state*/
72 #define WIN_STATE_HIDDEN (1<<4) /*not on taskbar but window visible*/
73 #define WIN_STATE_SHADED (1<<5) /*shaded (MacOS / Afterstep style)*/
74 /* these are bogus states defined in "the spec" */
75 #define WIN_STATE_HID_WORKSPACE (1<<6) /*not on current desktop*/
76 #define WIN_STATE_HID_TRANSIENT (1<<7) /*owner of transient is hidden*/
77 #define WIN_STATE_FIXED_POSITION (1<<8) /*window is fixed in position even*/
78 #define WIN_STATE_ARRANGE_IGNORE (1<<9) /*ignore for auto arranging*/
81 #define WIN_LAYER_DESKTOP 0
82 #define WIN_LAYER_BELOW 2
83 #define WIN_LAYER_NORMAL 4
84 #define WIN_LAYER_ONTOP 6
85 #define WIN_LAYER_DOCK 8
86 #define WIN_LAYER_ABOVE_DOCK 10
87 #define WIN_LAYER_MENU 12
91 static Atom _XA_WIN_SUPPORTING_WM_CHECK = 0;
92 static Atom _XA_WIN_PROTOCOLS;
93 static Atom _XA_WIN_LAYER;
94 static Atom _XA_WIN_STATE;
95 static Atom _XA_WIN_HINTS;
96 static Atom _XA_WIN_APP_STATE;
97 static Atom _XA_WIN_EXPANDED_SIZE;
98 static Atom _XA_WIN_ICONS;
99 static Atom _XA_WIN_WORKSPACE;
100 static Atom _XA_WIN_WORKSPACE_COUNT;
101 static Atom _XA_WIN_WORKSPACE_NAMES;
102 static Atom _XA_WIN_CLIENT_LIST;
103 static Atom _XA_WIN_DESKTOP_BUTTON_PROXY;
106 static void observer(void *self, WMNotification *notif);
107 static void wsobserver(void *self, WMNotification *notif);
110 void
111 wGNOMEInitStuff(WScreen *scr)
113 Atom supportedStuff[10];
114 int count;
116 if (!_XA_WIN_SUPPORTING_WM_CHECK) {
118 _XA_WIN_SUPPORTING_WM_CHECK =
119 XInternAtom(dpy, "_WIN_SUPPORTING_WM_CHECK", False);
121 _XA_WIN_PROTOCOLS = XInternAtom(dpy, "_WIN_PROTOCOLS", False);
123 _XA_WIN_LAYER = XInternAtom(dpy, "_WIN_LAYER", False);
125 _XA_WIN_STATE = XInternAtom(dpy, "_WIN_STATE", False);
127 _XA_WIN_HINTS = XInternAtom(dpy, "_WIN_HINTS", False);
129 _XA_WIN_APP_STATE = XInternAtom(dpy, "_WIN_APP_STATE", False);
131 _XA_WIN_EXPANDED_SIZE = XInternAtom(dpy, "_WIN_EXPANDED_SIZE", False);
133 _XA_WIN_ICONS = XInternAtom(dpy, "_WIN_ICONS", False);
135 _XA_WIN_WORKSPACE = XInternAtom(dpy, "_WIN_WORKSPACE", False);
137 _XA_WIN_WORKSPACE_COUNT =
138 XInternAtom(dpy, "_WIN_WORKSPACE_COUNT", False);
140 _XA_WIN_WORKSPACE_NAMES =
141 XInternAtom(dpy, "_WIN_WORKSPACE_NAMES", False);
143 _XA_WIN_CLIENT_LIST = XInternAtom(dpy, "_WIN_CLIENT_LIST", False);
145 _XA_WIN_DESKTOP_BUTTON_PROXY =
146 XInternAtom(dpy, "_WIN_DESKTOP_BUTTON_PROXY", False);
149 /* I'd rather use the ICCCM 2.0 mechanisms, but
150 * since some people prefer to reinvent the wheel instead of
151 * conforming to standards... */
153 /* setup the "We're compliant, you idiot!" hint */
155 /* why XA_CARDINAL instead of XA_WINDOW? */
156 XChangeProperty(dpy, scr->root_win, _XA_WIN_SUPPORTING_WM_CHECK,
157 XA_CARDINAL, 32, PropModeReplace,
158 (unsigned char*)&scr->no_focus_win, 1);
160 XChangeProperty(dpy, scr->no_focus_win, _XA_WIN_SUPPORTING_WM_CHECK,
161 XA_CARDINAL, 32, PropModeReplace,
162 (unsigned char*)&scr->no_focus_win, 1);
165 /* setup the "desktop button proxy" thing */
166 XChangeProperty(dpy, scr->root_win, _XA_WIN_DESKTOP_BUTTON_PROXY,
167 XA_CARDINAL, 32, PropModeReplace,
168 (unsigned char*)&scr->no_focus_win, 1);
169 XChangeProperty(dpy, scr->no_focus_win, _XA_WIN_DESKTOP_BUTTON_PROXY,
170 XA_CARDINAL, 32, PropModeReplace,
171 (unsigned char*)&scr->no_focus_win, 1);
174 /* setup the list of supported protocols */
175 count = 0;
176 supportedStuff[count++] = _XA_WIN_LAYER;
177 supportedStuff[count++] = _XA_WIN_STATE;
178 supportedStuff[count++] = _XA_WIN_HINTS;
179 supportedStuff[count++] = _XA_WIN_APP_STATE;
180 supportedStuff[count++] = _XA_WIN_EXPANDED_SIZE;
181 supportedStuff[count++] = _XA_WIN_ICONS;
182 supportedStuff[count++] = _XA_WIN_WORKSPACE;
183 supportedStuff[count++] = _XA_WIN_WORKSPACE_COUNT;
184 supportedStuff[count++] = _XA_WIN_WORKSPACE_NAMES;
185 supportedStuff[count++] = _XA_WIN_CLIENT_LIST;
187 XChangeProperty(dpy, scr->root_win, _XA_WIN_PROTOCOLS, XA_ATOM, 32,
188 PropModeReplace, (unsigned char*)supportedStuff, count);
190 XFlush(dpy);
192 WMAddNotificationObserver(observer, NULL, WMNManaged, NULL);
193 WMAddNotificationObserver(observer, NULL, WMNUnmanaged, NULL);
194 WMAddNotificationObserver(observer, NULL, WMNChangedWorkspace, NULL);
195 WMAddNotificationObserver(observer, NULL, WMNChangedState, NULL);
196 WMAddNotificationObserver(observer, NULL, WMNChangedFocus, NULL);
197 WMAddNotificationObserver(observer, NULL, WMNChangedStacking, NULL);
198 WMAddNotificationObserver(observer, NULL, WMNChangedName, NULL);
200 WMAddNotificationObserver(wsobserver, NULL, WMNWorkspaceCreated, NULL);
201 WMAddNotificationObserver(wsobserver, NULL, WMNWorkspaceDestroyed, NULL);
202 WMAddNotificationObserver(wsobserver, NULL, WMNWorkspaceChanged, NULL);
203 WMAddNotificationObserver(wsobserver, NULL, WMNWorkspaceNameChanged, NULL);
207 void
208 wGNOMEUpdateClientListHint(WScreen *scr)
210 WWindow *wwin;
211 Window *windows;
212 int count;
214 windows = malloc(sizeof(Window)*scr->window_count);
215 if (!windows) {
216 wwarning(_("out of memory while updating GNOME hints"));
217 return;
220 count = 0;
221 wwin = scr->focused_window;
222 while (wwin) {
223 if (!wwin->flags.internal_window &&
224 !wwin->client_flags.skip_window_list) {
226 windows[count++] = wwin->client_win;
229 wwin = wwin->prev;
232 XChangeProperty(dpy, scr->root_win, _XA_WIN_CLIENT_LIST, XA_CARDINAL, 32,
233 PropModeReplace, (unsigned char *)windows, count);
235 wfree(windows);
236 XFlush(dpy);
240 void
241 wGNOMEUpdateWorkspaceHints(WScreen *scr)
243 long val;
245 val = scr->workspace_count;
247 XChangeProperty(dpy, scr->root_win, _XA_WIN_WORKSPACE_COUNT, XA_CARDINAL,
248 32, PropModeReplace, (unsigned char*)&val, 1);
250 wGNOMEUpdateWorkspaceNamesHint(scr);
254 void
255 wGNOMEUpdateWorkspaceNamesHint(WScreen *scr)
257 char *wsNames[MAX_WORKSPACES];
258 XTextProperty textProp;
259 int i;
261 for (i = 0; i < scr->workspace_count; i++) {
262 wsNames[i] = scr->workspaces[i]->name;
265 if (XStringListToTextProperty(wsNames, scr->workspace_count, &textProp)) {
266 XSetTextProperty(dpy, scr->root_win, &textProp,
267 _XA_WIN_WORKSPACE_NAMES);
268 XFree(textProp.value);
273 void
274 wGNOMEUpdateCurrentWorkspaceHint(WScreen *scr)
276 long val;
278 val = scr->current_workspace;
280 XChangeProperty(dpy, scr->root_win, _XA_WIN_WORKSPACE, XA_CARDINAL,
281 32, PropModeReplace, (unsigned char*)&val, 1);
285 static int
286 getWindowLevel(int layer)
288 int level;
290 if (layer <= WIN_LAYER_DESKTOP)
291 level = WMDesktopLevel;
292 else if (layer <= WIN_LAYER_BELOW)
293 level = WMSunkenLevel;
294 else if (layer <= WIN_LAYER_NORMAL)
295 level = WMNormalLevel;
296 else if (layer <= WIN_LAYER_ONTOP)
297 level = WMFloatingLevel;
298 else if (layer <= WIN_LAYER_DOCK)
299 level = WMDockLevel;
300 else if (layer <= WIN_LAYER_ABOVE_DOCK)
301 level = WMSubmenuLevel;
302 else if (layer <= WIN_LAYER_MENU)
303 level = WMMainMenuLevel;
304 else
305 level = WMOuterSpaceLevel;
307 return level;
311 Bool
312 wGNOMECheckClientHints(WWindow *wwin, int *layer, int *workspace)
314 Atom type_ret;
315 int fmt_ret;
316 unsigned long nitems_ret;
317 unsigned long bytes_after_ret;
318 long flags, val, *data = 0;
319 Bool hasHints = False;
321 /* hints */
323 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_HINTS, 0, 1, False,
324 /* should be XA_INTEGER, but spec is broken */
325 XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
326 &bytes_after_ret,
327 (unsigned char**)&data)==Success && data) {
328 flags = *data;
330 XFree(data);
332 if (flags & (WIN_HINTS_SKIP_FOCUS|WIN_HINTS_SKIP_WINLIST
333 |WIN_HINTS_SKIP_TASKBAR)) {
334 wwin->client_flags.skip_window_list = 1;
337 /* client reserved area, for the panel */
338 if (flags & (WIN_HINTS_DO_NOT_COVER)) {
339 WReservedArea *area;
341 area = malloc(sizeof(WReservedArea));
342 if (!area) {
343 wwarning(_("out of memory while updating GNOME hints"));
344 } else {
345 XWindowAttributes wattribs;
347 XGetWindowAttributes(dpy, wwin->client_win, &wattribs);
348 wClientGetNormalHints(wwin, &wattribs, False,
349 &area->area.x1, &area->area.y1,
350 &area->area.x2, &area->area.y2);
351 area->area.x2 = area->area.x2 + area->area.x1;
352 area->area.y2 = area->area.y2 + area->area.y1;
354 area->window = wwin->client_win;
356 area->next = wwin->screen_ptr->reservedAreas;
357 wwin->screen_ptr->reservedAreas = area;
359 wScreenUpdateUsableArea(wwin->screen_ptr);
362 hasHints = True;
365 /* layer */
366 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_LAYER, 0, 1, False,
367 XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
368 &bytes_after_ret,
369 (unsigned char**)&data)==Success && data) {
370 val = *data;
372 XFree(data);
374 *layer = getWindowLevel(val);
375 hasHints = True;
378 /* workspace */
379 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_WORKSPACE, 0, 1,
380 False, XA_CARDINAL, &type_ret, &fmt_ret,
381 &nitems_ret, &bytes_after_ret,
382 (unsigned char**)&data)==Success && data) {
383 val = *data;
385 XFree(data);
387 if (val > 0)
388 *workspace = val;
389 hasHints = True;
392 /* reserved area */
393 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_EXPANDED_SIZE, 0, 1,
394 False, XA_CARDINAL, &type_ret, &fmt_ret,
395 &nitems_ret, &bytes_after_ret,
396 (unsigned char**)&data)==Success && data) {
397 WReservedArea *area;
399 area = malloc(sizeof(WReservedArea));
400 if (!area) {
401 wwarning(_("out of memory while updating GNOME hints"));
402 } else {
403 area->area.x1 = data[0];
404 area->area.y1 = data[1];
405 area->area.x2 = data[2] - data[0];
406 area->area.y2 = data[3] - data[1];
407 XFree(data);
409 area->window = wwin->client_win;
412 area->next = wwin->screen_ptr->reservedAreas;
413 wwin->screen_ptr->reservedAreas = area;
415 wScreenUpdateUsableArea(wwin->screen_ptr);
416 hasHints = True;
419 return hasHints;
423 Bool
424 wGNOMECheckInitialClientState(WWindow *wwin)
426 Atom type_ret;
427 int fmt_ret;
428 unsigned long nitems_ret;
429 unsigned long bytes_after_ret;
430 long flags, *data = 0;
432 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_STATE, 0, 1, False,
433 XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
434 &bytes_after_ret,
435 (unsigned char**)&data)!=Success || !data)
436 return False;
438 flags = *data;
440 XFree(data);
442 if (flags & WIN_STATE_STICKY)
443 wwin->client_flags.omnipresent = 1;
445 if (flags & (WIN_STATE_MAXIMIZED_VERT|WIN_STATE_MAXIMIZED_HORIZ)) {
447 if (flags & WIN_STATE_MAXIMIZED_VERT)
448 wwin->flags.maximized |= MAX_VERTICAL;
450 if (flags & WIN_STATE_MAXIMIZED_HORIZ)
451 wwin->flags.maximized |= MAX_HORIZONTAL;
454 if (flags & WIN_STATE_SHADED)
455 wwin->flags.shaded = 1;
457 return True;
461 void
462 wGNOMEUpdateClientStateHint(WWindow *wwin, Bool changedWorkspace)
464 long val;
465 long flags = 0;
467 if (changedWorkspace) {
468 val = wwin->frame->workspace;
470 XChangeProperty(dpy, wwin->client_win, _XA_WIN_WORKSPACE, XA_CARDINAL,
471 32, PropModeReplace, (unsigned char*)&val, 1);
473 if (val != wwin->screen_ptr->current_workspace)
474 flags |= WIN_STATE_HID_WORKSPACE;
477 if (IS_OMNIPRESENT(wwin))
478 flags |= WIN_STATE_STICKY;
480 if (wwin->flags.miniaturized)
481 flags |= WIN_STATE_MINIMIZED;
483 if (wwin->flags.maximized & MAX_VERTICAL)
484 flags |= WIN_STATE_MAXIMIZED_VERT;
486 if (wwin->flags.maximized & MAX_HORIZONTAL)
487 flags |= WIN_STATE_MAXIMIZED_HORIZ;
489 if (wwin->flags.shaded)
490 flags |= WIN_STATE_SHADED;
492 if (wwin->transient_for != None) {
493 WWindow *owner = wWindowFor(wwin->transient_for);
495 if (owner && !owner->flags.mapped)
496 flags |= WIN_STATE_HID_TRANSIENT;
499 /* ? */
500 if (wwin->flags.hidden)
501 flags |= WIN_STATE_HIDDEN;
503 XChangeProperty(dpy, wwin->client_win, _XA_WIN_STATE, XA_CARDINAL,
504 32, PropModeReplace, (unsigned char*)&flags, 1);
508 Bool
509 wGNOMEProcessClientMessage(XClientMessageEvent *event)
511 WScreen *scr;
512 WWindow *wwin;
513 Bool done = True;
515 scr = wScreenForWindow(event->window);
516 if (scr) {
517 /* generic client messages */
518 if (event->message_type == _XA_WIN_WORKSPACE) {
519 wWorkspaceChange(scr, event->data.l[0]);
520 } else {
521 done = False;
524 if (done)
525 return True;
528 /* window specific client messages */
530 wwin = wWindowFor(event->window);
531 if (!wwin)
532 return False;
534 if (event->message_type == _XA_WIN_LAYER) {
535 int level = getWindowLevel(event->data.l[0]);
537 if (WINDOW_LEVEL(wwin) != level) {
538 ChangeStackingLevel(wwin->frame->core, level);
540 } else if (event->message_type == _XA_WIN_STATE) {
541 int flags, mask;
542 int maximize = 0;
544 mask = event->data.l[0];
545 flags = event->data.l[1];
547 if (mask & WIN_STATE_STICKY) {
548 if ((flags & WIN_STATE_STICKY) != WFLAGP(wwin, omnipresent)) {
549 wWindowSetOmnipresent(wwin, (flags & WIN_STATE_STICKY)!=0);
553 if (mask & WIN_STATE_MAXIMIZED_VERT) {
554 if (flags & WIN_STATE_MAXIMIZED_VERT)
555 maximize = MAX_VERTICAL;
556 else
557 maximize = 0;
558 } else {
559 maximize = wwin->flags.maximized & MAX_VERTICAL;
562 if (mask & WIN_STATE_MAXIMIZED_HORIZ) {
563 if (flags & WIN_STATE_MAXIMIZED_HORIZ)
564 maximize |= MAX_HORIZONTAL;
565 else
566 maximize |= 0;
567 } else {
568 maximize |= wwin->flags.maximized & MAX_HORIZONTAL;
571 if (maximize != wwin->flags.maximized) {
572 #define both (MAX_HORIZONTAL|MAX_VERTICAL)
573 if (!(maximize & both) && (wwin->flags.maximized & both)) {
574 wUnmaximizeWindow(wwin);
576 if ((maximize & both) && !(wwin->flags.maximized & both)) {
577 wMaximizeWindow(wwin, maximize);
579 #undef both
582 if (mask & WIN_STATE_SHADED) {
583 if ((flags & WIN_STATE_SHADED) != wwin->flags.shaded) {
584 if (wwin->flags.shaded)
585 wUnshadeWindow(wwin);
586 else
587 wShadeWindow(wwin);
590 } else if (event->message_type == _XA_WIN_WORKSPACE) {
592 if (event->data.l[0] != wwin->frame->workspace) {
593 wWindowChangeWorkspace(wwin, event->data.l[0]);
595 } else {
596 done = False;
599 return done;
603 Bool
604 wGNOMEProxyizeButtonEvent(WScreen *scr, XEvent *event)
606 if (event->type == ButtonPress)
607 XUngrabPointer(dpy, CurrentTime);
608 XSendEvent(dpy, scr->no_focus_win, False, SubstructureNotifyMask, event);
610 return True;
614 void
615 wGNOMERemoveClient(WWindow *wwin)
617 int flag = 0;
618 WReservedArea *area;
620 wGNOMEUpdateClientListHint(wwin->screen_ptr);
622 area = wwin->screen_ptr->reservedAreas;
624 if (area) {
625 if (area->window == wwin->client_win) {
626 wwin->screen_ptr->reservedAreas = area->next;
627 wfree(area);
628 flag = 1;
629 } else {
630 while (area->next && area->next->window != wwin->client_win)
631 area = area->next;
633 if (area->next) {
634 WReservedArea *next;
636 next = area->next->next;
637 wfree(area->next);
638 area->next = next;
640 flag = 1;
645 if (flag) {
646 wScreenUpdateUsableArea(wwin->screen_ptr);
653 static void
654 observer(void *self, WMNotification *notif)
656 WWindow *wwin = (WWindow*)WMGetNotificationObject(notif);
657 const char *name = WMGetNotificationName(notif);
659 if (strcmp(name, WMNManaged) == 0 && wwin) {
660 wGNOMEUpdateClientStateHint(wwin, True);
662 wGNOMEUpdateClientListHint(wwin->screen_ptr);
663 } else if (strcmp(name, WMNUnmanaged) == 0 && wwin) {
664 wGNOMERemoveClient(wwin);
665 } else if (strcmp(name, WMNChangedWorkspace) == 0 && wwin) {
666 wGNOMEUpdateClientStateHint(wwin, True);
667 } else if (strcmp(name, WMNChangedState) == 0 && wwin) {
668 wGNOMEUpdateClientStateHint(wwin, False);
673 static void
674 wsobserver(void *self, WMNotification *notif)
676 WScreen *scr = (WScreen*)WMGetNotificationObject(notif);
677 const char *name = WMGetNotificationName(notif);
679 if (strcmp(name, WMNWorkspaceCreated) == 0) {
680 wGNOMEUpdateWorkspaceHints(scr);
681 } else if (strcmp(name, WMNWorkspaceDestroyed) == 0) {
682 wGNOMEUpdateWorkspaceHints(scr);
683 } else if (strcmp(name, WMNWorkspaceNameChanged) == 0) {
684 wGNOMEUpdateWorkspaceNamesHint(scr);
685 } else if (strcmp(name, WMNWorkspaceChanged) == 0) {
686 wGNOMEUpdateCurrentWorkspaceHint(scr);
687 } else if (strcmp(name, WMNResetStacking) == 0) {
693 #endif /* GNOME_STUFF */