Update for 0.51.0
[wmaker-crm.git] / src / gnome.c
blob12ae5320f1f0bb61dcf88366a7ad0c021f00470f
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*/
66 #define WIN_STATE_STICKY (1<<0) /*everyone knows sticky*/
67 #define WIN_STATE_MINIMIZED (1<<1) /*Reserved - definition is unclear*/
68 #define WIN_STATE_MAXIMIZED_VERT (1<<2) /*window in maximized V state*/
69 #define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /*window in maximized H state*/
70 #define WIN_STATE_HIDDEN (1<<4) /*not on taskbar but window visible*/
71 #define WIN_STATE_SHADED (1<<5) /*shaded (MacOS / Afterstep style)*/
72 /* these are bogus states defined in "the spec" */
73 #define WIN_STATE_HID_WORKSPACE (1<<6) /*not on current desktop*/
74 #define WIN_STATE_HID_TRANSIENT (1<<7) /*owner of transient is hidden*/
75 #define WIN_STATE_FIXED_POSITION (1<<8) /*window is fixed in position even*/
76 #define WIN_STATE_ARRANGE_IGNORE (1<<9) /*ignore for auto arranging*/
79 #define WIN_LAYER_DESKTOP 0
80 #define WIN_LAYER_BELOW 2
81 #define WIN_LAYER_NORMAL 4
82 #define WIN_LAYER_ONTOP 6
83 #define WIN_LAYER_DOCK 8
84 #define WIN_LAYER_ABOVE_DOCK 10
85 #define WIN_LAYER_MENU 12
89 static Atom _XA_WIN_SUPPORTING_WM_CHECK = 0;
90 static Atom _XA_WIN_PROTOCOLS;
91 static Atom _XA_WIN_LAYER;
92 static Atom _XA_WIN_STATE;
93 static Atom _XA_WIN_HINTS;
94 static Atom _XA_WIN_APP_STATE;
95 static Atom _XA_WIN_EXPANDED_SIZE;
96 static Atom _XA_WIN_ICONS;
97 static Atom _XA_WIN_WORKSPACE;
98 static Atom _XA_WIN_WORKSPACE_COUNT;
99 static Atom _XA_WIN_WORKSPACE_NAMES;
100 static Atom _XA_WIN_CLIENT_LIST;
101 static Atom _XA_WIN_DESKTOP_BUTTON_PROXY;
104 void
105 wGNOMEInitStuff(WScreen *scr)
107 Atom supportedStuff[10];
108 int count;
110 if (!_XA_WIN_SUPPORTING_WM_CHECK) {
112 _XA_WIN_SUPPORTING_WM_CHECK =
113 XInternAtom(dpy, "_WIN_SUPPORTING_WM_CHECK", False);
115 _XA_WIN_PROTOCOLS = XInternAtom(dpy, "_WIN_PROTOCOLS", False);
117 _XA_WIN_LAYER = XInternAtom(dpy, "_WIN_LAYER", False);
119 _XA_WIN_STATE = XInternAtom(dpy, "_WIN_STATE", False);
121 _XA_WIN_HINTS = XInternAtom(dpy, "_WIN_HINTS", False);
123 _XA_WIN_APP_STATE = XInternAtom(dpy, "_WIN_APP_STATE", False);
125 _XA_WIN_EXPANDED_SIZE = XInternAtom(dpy, "_WIN_EXPANDED_SIZE", False);
127 _XA_WIN_ICONS = XInternAtom(dpy, "_WIN_ICONS", False);
129 _XA_WIN_WORKSPACE = XInternAtom(dpy, "_WIN_WORKSPACE", False);
131 _XA_WIN_WORKSPACE_COUNT =
132 XInternAtom(dpy, "_WIN_WORKSPACE_COUNT", False);
134 _XA_WIN_WORKSPACE_NAMES =
135 XInternAtom(dpy, "_WIN_WORKSPACE_NAMES", False);
137 _XA_WIN_CLIENT_LIST = XInternAtom(dpy, "_WIN_CLIENT_LIST", False);
139 _XA_WIN_DESKTOP_BUTTON_PROXY =
140 XInternAtom(dpy, "_WIN_DESKTOP_BUTTON_PROXY", False);
143 /* I'd rather use the ICCCM 2.0 mechanisms, but
144 * since some people prefer to reinvent the wheel instead of
145 * conforming to standards... */
147 /* setup the "We're compliant, you idiot!" hint */
149 /* why XA_CARDINAL instead of XA_WINDOW? */
150 XChangeProperty(dpy, scr->root_win, _XA_WIN_SUPPORTING_WM_CHECK,
151 XA_CARDINAL, 32, PropModeReplace,
152 (unsigned char*)&scr->no_focus_win, 1);
154 XChangeProperty(dpy, scr->no_focus_win, _XA_WIN_SUPPORTING_WM_CHECK,
155 XA_CARDINAL, 32, PropModeReplace,
156 (unsigned char*)&scr->no_focus_win, 1);
159 /* setup the "desktop button proxy" thing */
160 XChangeProperty(dpy, scr->root_win, _XA_WIN_DESKTOP_BUTTON_PROXY,
161 XA_CARDINAL, 32, PropModeReplace,
162 (unsigned char*)&scr->no_focus_win, 1);
163 XChangeProperty(dpy, scr->no_focus_win, _XA_WIN_DESKTOP_BUTTON_PROXY,
164 XA_CARDINAL, 32, PropModeReplace,
165 (unsigned char*)&scr->no_focus_win, 1);
168 /* setup the list of supported protocols */
169 count = 0;
170 supportedStuff[count++] = _XA_WIN_LAYER;
171 supportedStuff[count++] = _XA_WIN_STATE;
172 supportedStuff[count++] = _XA_WIN_HINTS;
173 supportedStuff[count++] = _XA_WIN_APP_STATE;
174 supportedStuff[count++] = _XA_WIN_EXPANDED_SIZE;
175 supportedStuff[count++] = _XA_WIN_ICONS;
176 supportedStuff[count++] = _XA_WIN_WORKSPACE;
177 supportedStuff[count++] = _XA_WIN_WORKSPACE_COUNT;
178 supportedStuff[count++] = _XA_WIN_WORKSPACE_NAMES;
179 supportedStuff[count++] = _XA_WIN_CLIENT_LIST;
181 XChangeProperty(dpy, scr->root_win, _XA_WIN_PROTOCOLS, XA_ATOM, 32,
182 PropModeReplace, (unsigned char*)supportedStuff, count);
184 XFlush(dpy);
188 void
189 wGNOMEUpdateClientListHint(WScreen *scr)
191 WWindow *wwin;
192 Window *windows;
193 int count;
195 windows = malloc(sizeof(Window)*scr->window_count);
196 if (!windows) {
197 wwarning(_("out of memory while updating GNOME hints"));
198 return;
201 count = 0;
202 wwin = scr->focused_window;
203 while (wwin) {
204 if (!wwin->flags.internal_window) {
206 windows[count++] = wwin->client_win;
209 wwin = wwin->prev;
212 XChangeProperty(dpy, scr->root_win, _XA_WIN_CLIENT_LIST, XA_CARDINAL, 32,
213 PropModeReplace, (unsigned char *)windows, count);
215 free(windows);
216 XFlush(dpy);
220 void
221 wGNOMEUpdateWorkspaceHints(WScreen *scr)
223 long val;
225 val = scr->workspace_count;
227 XChangeProperty(dpy, scr->root_win, _XA_WIN_WORKSPACE_COUNT, XA_CARDINAL,
228 32, PropModeReplace, (unsigned char*)&val, 1);
230 wGNOMEUpdateWorkspaceNamesHint(scr);
234 void
235 wGNOMEUpdateWorkspaceNamesHint(WScreen *scr)
237 char *wsNames[MAX_WORKSPACES];
238 XTextProperty textProp;
239 int i;
241 for (i = 0; i < scr->workspace_count; i++) {
242 wsNames[i] = scr->workspaces[i]->name;
245 if (XStringListToTextProperty(wsNames, scr->workspace_count, &textProp)) {
246 XSetTextProperty(dpy, scr->root_win, &textProp,
247 _XA_WIN_WORKSPACE_NAMES);
248 XFree(textProp.value);
253 void
254 wGNOMEUpdateCurrentWorkspaceHint(WScreen *scr)
256 long val;
258 val = scr->current_workspace;
260 XChangeProperty(dpy, scr->root_win, _XA_WIN_WORKSPACE, XA_CARDINAL,
261 32, PropModeReplace, (unsigned char*)&val, 1);
265 static int
266 getWindowLevel(int layer)
268 int level;
270 if (layer <= WIN_LAYER_DESKTOP)
271 level = WMDesktopLevel;
272 else if (layer <= WIN_LAYER_BELOW)
273 level = WMSunkenLevel;
274 else if (layer <= WIN_LAYER_NORMAL)
275 level = WMNormalLevel;
276 else if (layer <= WIN_LAYER_ONTOP)
277 level = WMFloatingLevel;
278 else if (layer <= WIN_LAYER_DOCK)
279 level = WMDockLevel;
280 else if (layer <= WIN_LAYER_ABOVE_DOCK)
281 level = WMSubmenuLevel;
282 else if (layer <= WIN_LAYER_MENU)
283 level = WMMainMenuLevel;
284 else
285 level = WMOuterSpaceLevel;
287 return level;
291 void
292 wGNOMECheckClientHints(WWindow *wwin, int *layer, int *workspace)
294 Atom type_ret;
295 int fmt_ret;
296 unsigned long nitems_ret;
297 unsigned long bytes_after_ret;
298 long flags, val, *data = 0;
300 /* hints */
302 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_HINTS, 0, 1, False,
303 /* should be XA_INTEGER, but spec is broken */
304 XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
305 &bytes_after_ret,
306 (unsigned char**)&data)==Success && data) {
307 flags = *data;
309 XFree(data);
311 if (flags & (WIN_HINTS_SKIP_FOCUS|WIN_HINTS_SKIP_WINLIST)) {
312 wwin->client_flags.skip_window_list = 1;
316 /* layer */
317 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_LAYER, 0, 1, False,
318 XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
319 &bytes_after_ret,
320 (unsigned char**)&data)==Success && data) {
321 val = *data;
323 XFree(data);
325 *layer = getWindowLevel(val);
328 /* workspace */
329 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_WORKSPACE, 0, 1,
330 False, XA_CARDINAL, &type_ret, &fmt_ret,
331 &nitems_ret, &bytes_after_ret,
332 (unsigned char**)&data)==Success && data) {
333 val = *data;
335 XFree(data);
337 if (val > 0)
338 *workspace = val;
341 /* reserved area */
342 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_EXPANDED_SIZE, 0, 1,
343 False, XA_CARDINAL, &type_ret, &fmt_ret,
344 &nitems_ret, &bytes_after_ret,
345 (unsigned char**)&data)==Success && data) {
346 WReservedArea *area;
348 area = malloc(sizeof(WReservedArea));
349 if (!area) {
350 wwarning(_("out of memory while updating GNOME hints"));
351 } else {
352 area->area.x1 = data[0];
353 area->area.y1 = data[1];
354 area->area.x2 = data[2] - data[0];
355 area->area.y2 = data[3] - data[1];
356 XFree(data);
358 area->window = wwin->client_win;
361 area->next = wwin->screen_ptr->reservedAreas;
362 wwin->screen_ptr->reservedAreas = area;
364 wScreenUpdateUsableArea(wwin->screen_ptr);
369 void
370 wGNOMECheckInitialClientState(WWindow *wwin)
372 Atom type_ret;
373 int fmt_ret;
374 unsigned long nitems_ret;
375 unsigned long bytes_after_ret;
376 long flags, *data = 0;
378 if (XGetWindowProperty(dpy, wwin->client_win, _XA_WIN_STATE, 0, 1, False,
379 XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
380 &bytes_after_ret,
381 (unsigned char**)&data)!=Success || !data)
382 return;
384 flags = *data;
386 XFree(data);
388 if (flags & WIN_STATE_STICKY)
389 wwin->client_flags.omnipresent = 1;
391 if (flags & (WIN_STATE_MAXIMIZED_VERT|WIN_STATE_MAXIMIZED_HORIZ)) {
393 if (flags & WIN_STATE_MAXIMIZED_VERT)
394 wwin->flags.maximized |= MAX_VERTICAL;
396 if (flags & WIN_STATE_MAXIMIZED_HORIZ)
397 wwin->flags.maximized |= MAX_HORIZONTAL;
400 if (flags & WIN_STATE_SHADED)
401 wwin->flags.shaded = 1;
405 void
406 wGNOMEUpdateClientStateHint(WWindow *wwin, Bool changedWorkspace)
408 long val;
409 long flags = 0;
411 if (changedWorkspace) {
412 val = wwin->frame->workspace;
414 XChangeProperty(dpy, wwin->client_win, _XA_WIN_WORKSPACE, XA_CARDINAL,
415 32, PropModeReplace, (unsigned char*)&val, 1);
417 if (val != wwin->screen_ptr->current_workspace)
418 flags |= WIN_STATE_HID_WORKSPACE;
421 if (IS_OMNIPRESENT(wwin))
422 flags |= WIN_STATE_STICKY;
424 if (wwin->flags.miniaturized)
425 flags |= WIN_STATE_MINIMIZED;
427 if (wwin->flags.maximized & MAX_VERTICAL)
428 flags |= WIN_STATE_MAXIMIZED_VERT;
430 if (wwin->flags.maximized & MAX_HORIZONTAL)
431 flags |= WIN_STATE_MAXIMIZED_HORIZ;
433 if (wwin->flags.shaded)
434 flags |= WIN_STATE_SHADED;
436 if (wwin->transient_for != None) {
437 WWindow *owner = wWindowFor(wwin->transient_for);
439 if (owner && !owner->flags.mapped)
440 flags |= WIN_STATE_HID_TRANSIENT;
443 /* ? */
444 if (wwin->flags.hidden)
445 flags |= WIN_STATE_HIDDEN;
447 XChangeProperty(dpy, wwin->client_win, _XA_WIN_STATE, XA_CARDINAL,
448 32, PropModeReplace, (unsigned char*)&flags, 1);
452 Bool
453 wGNOMEProcessClientMessage(XClientMessageEvent *event)
455 WScreen *scr;
456 WWindow *wwin;
457 Bool done = True;
459 scr = wScreenForRootWindow(event->window);
460 if (scr) {
461 /* generic client messages */
462 if (event->message_type == _XA_WIN_WORKSPACE) {
463 wWorkspaceChange(scr, event->data.l[0]);
464 } else {
465 done = False;
468 if (done)
469 return True;
472 /* window specific client messages */
474 wwin = wWindowFor(event->window);
475 if (!wwin)
476 return False;
478 if (event->message_type == _XA_WIN_LAYER) {
479 int level = getWindowLevel(event->data.l[0]);
481 if (WINDOW_LEVEL(wwin) != level) {
482 ChangeStackingLevel(wwin->frame->core, level);
484 } else if (event->message_type == _XA_WIN_STATE) {
485 int flags, mask;
486 Bool updateWindowList = False;
487 int maximize = 0;
489 mask = event->data.l[0];
490 flags = event->data.l[1];
492 if (mask & WIN_STATE_STICKY) {
493 if ((flags & WIN_STATE_STICKY) != WFLAGP(wwin, omnipresent)) {
494 wwin->client_flags.omnipresent = 1;
495 updateWindowList = True;
499 if (mask & WIN_STATE_MAXIMIZED_VERT) {
500 if (flags & WIN_STATE_MAXIMIZED_VERT)
501 maximize = MAX_VERTICAL;
502 else
503 maximize = 0;
504 } else {
505 maximize = wwin->flags.maximized & MAX_VERTICAL;
508 if (mask & WIN_STATE_MAXIMIZED_HORIZ) {
509 if (flags & WIN_STATE_MAXIMIZED_HORIZ)
510 maximize |= MAX_HORIZONTAL;
511 else
512 maximize |= 0;
513 } else {
514 maximize |= wwin->flags.maximized & MAX_HORIZONTAL;
517 if (maximize != wwin->flags.maximized) {
518 #define both (MAX_HORIZONTAL|MAX_VERTICAL)
519 if (!(maximize & both) && (wwin->flags.maximized & both)) {
520 wUnmaximizeWindow(wwin);
522 if ((maximize & both) && !(wwin->flags.maximized & both)) {
523 wMaximizeWindow(wwin, maximize);
525 updateWindowList = False;
526 #undef both
529 if (mask & WIN_STATE_SHADED) {
530 if ((flags & WIN_STATE_SHADED) != wwin->flags.shaded) {
531 if (wwin->flags.shaded)
532 wUnshadeWindow(wwin);
533 else
534 wShadeWindow(wwin);
535 updateWindowList = False;
539 if (updateWindowList) {
540 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE);
542 } else if (event->message_type == _XA_WIN_WORKSPACE) {
544 if (event->data.l[0] != wwin->frame->workspace) {
545 wWindowChangeWorkspace(wwin, event->data.l[0]);
547 } else {
548 done = False;
551 return done;
555 Bool
556 wGNOMEProxyizeButtonEvent(WScreen *scr, XEvent *event)
558 #ifndef MOUSE_WS_SWITCH
559 if (event->xbutton.button <= Button3
560 && (event->xbutton.state & ValidModMask) == 0)
561 return False;
562 #else
563 if ((event->xbutton.state & ValidModMask) == 0)
564 return False;
565 #endif
567 if (event->type == ButtonPress)
568 XUngrabPointer(dpy, CurrentTime);
569 XSendEvent(dpy, scr->no_focus_win, False, SubstructureNotifyMask, event);
571 return True;
575 void
576 wGNOMERemoveClient(WWindow *wwin)
578 int flag = 0;
579 WReservedArea *area;
581 wGNOMEUpdateClientListHint(wwin->screen_ptr);
583 area = wwin->screen_ptr->reservedAreas;
585 if (area) {
586 if (area->window == wwin->client_win) {
587 wwin->screen_ptr->reservedAreas = area->next;
588 free(area);
589 flag = 1;
590 } else {
591 while (area->next && area->next->window != wwin->client_win)
592 area = area->next;
594 if (area->next) {
595 WReservedArea *next;
597 next = area->next->next;
598 free(area->next);
599 area->next = next;
601 flag = 1;
606 if (flag) {
607 wScreenUpdateUsableArea(wwin->screen_ptr);
612 #endif /* GNOME_STUFF */