1 /* gnome.c-- support for the GNOME Hints
3 * Window Maker window manager
5 * Copyright (c) 1998, 1999 Alfredo K. Kojima
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,
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.
34 #include <X11/Xutil.h>
35 #include <X11/Xatom.h>
42 #include "WindowMaker.h"
47 #include "workspace.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
;
106 wGNOMEInitStuff(WScreen
*scr
)
108 Atom supportedStuff
[10];
111 if (!_XA_WIN_SUPPORTING_WM_CHECK
) {
113 _XA_WIN_SUPPORTING_WM_CHECK
=
114 XInternAtom(dpy
, "_WIN_SUPPORTING_WM_CHECK", False
);
116 _XA_WIN_PROTOCOLS
= XInternAtom(dpy
, "_WIN_PROTOCOLS", False
);
118 _XA_WIN_LAYER
= XInternAtom(dpy
, "_WIN_LAYER", False
);
120 _XA_WIN_STATE
= XInternAtom(dpy
, "_WIN_STATE", False
);
122 _XA_WIN_HINTS
= XInternAtom(dpy
, "_WIN_HINTS", False
);
124 _XA_WIN_APP_STATE
= XInternAtom(dpy
, "_WIN_APP_STATE", False
);
126 _XA_WIN_EXPANDED_SIZE
= XInternAtom(dpy
, "_WIN_EXPANDED_SIZE", False
);
128 _XA_WIN_ICONS
= XInternAtom(dpy
, "_WIN_ICONS", False
);
130 _XA_WIN_WORKSPACE
= XInternAtom(dpy
, "_WIN_WORKSPACE", False
);
132 _XA_WIN_WORKSPACE_COUNT
=
133 XInternAtom(dpy
, "_WIN_WORKSPACE_COUNT", False
);
135 _XA_WIN_WORKSPACE_NAMES
=
136 XInternAtom(dpy
, "_WIN_WORKSPACE_NAMES", False
);
138 _XA_WIN_CLIENT_LIST
= XInternAtom(dpy
, "_WIN_CLIENT_LIST", False
);
140 _XA_WIN_DESKTOP_BUTTON_PROXY
=
141 XInternAtom(dpy
, "_WIN_DESKTOP_BUTTON_PROXY", False
);
144 /* I'd rather use the ICCCM 2.0 mechanisms, but
145 * since some people prefer to reinvent the wheel instead of
146 * conforming to standards... */
148 /* setup the "We're compliant, you idiot!" hint */
150 /* why XA_CARDINAL instead of XA_WINDOW? */
151 XChangeProperty(dpy
, scr
->root_win
, _XA_WIN_SUPPORTING_WM_CHECK
,
152 XA_CARDINAL
, 32, PropModeReplace
,
153 (unsigned char*)&scr
->no_focus_win
, 1);
155 XChangeProperty(dpy
, scr
->no_focus_win
, _XA_WIN_SUPPORTING_WM_CHECK
,
156 XA_CARDINAL
, 32, PropModeReplace
,
157 (unsigned char*)&scr
->no_focus_win
, 1);
160 /* setup the "desktop button proxy" thing */
161 XChangeProperty(dpy
, scr
->root_win
, _XA_WIN_DESKTOP_BUTTON_PROXY
,
162 XA_CARDINAL
, 32, PropModeReplace
,
163 (unsigned char*)&scr
->no_focus_win
, 1);
164 XChangeProperty(dpy
, scr
->no_focus_win
, _XA_WIN_DESKTOP_BUTTON_PROXY
,
165 XA_CARDINAL
, 32, PropModeReplace
,
166 (unsigned char*)&scr
->no_focus_win
, 1);
169 /* setup the list of supported protocols */
171 supportedStuff
[count
++] = _XA_WIN_LAYER
;
172 supportedStuff
[count
++] = _XA_WIN_STATE
;
173 supportedStuff
[count
++] = _XA_WIN_HINTS
;
174 supportedStuff
[count
++] = _XA_WIN_APP_STATE
;
175 supportedStuff
[count
++] = _XA_WIN_EXPANDED_SIZE
;
176 supportedStuff
[count
++] = _XA_WIN_ICONS
;
177 supportedStuff
[count
++] = _XA_WIN_WORKSPACE
;
178 supportedStuff
[count
++] = _XA_WIN_WORKSPACE_COUNT
;
179 supportedStuff
[count
++] = _XA_WIN_WORKSPACE_NAMES
;
180 supportedStuff
[count
++] = _XA_WIN_CLIENT_LIST
;
182 XChangeProperty(dpy
, scr
->root_win
, _XA_WIN_PROTOCOLS
, XA_ATOM
, 32,
183 PropModeReplace
, (unsigned char*)supportedStuff
, count
);
190 wGNOMEUpdateClientListHint(WScreen
*scr
)
196 windows
= malloc(sizeof(Window
)*scr
->window_count
);
198 wwarning(_("out of memory while updating GNOME hints"));
203 wwin
= scr
->focused_window
;
205 if (!wwin
->flags
.internal_window
&&
206 !wwin
->client_flags
.skip_window_list
) {
208 windows
[count
++] = wwin
->client_win
;
214 XChangeProperty(dpy
, scr
->root_win
, _XA_WIN_CLIENT_LIST
, XA_CARDINAL
, 32,
215 PropModeReplace
, (unsigned char *)windows
, count
);
223 wGNOMEUpdateWorkspaceHints(WScreen
*scr
)
227 val
= scr
->workspace_count
;
229 XChangeProperty(dpy
, scr
->root_win
, _XA_WIN_WORKSPACE_COUNT
, XA_CARDINAL
,
230 32, PropModeReplace
, (unsigned char*)&val
, 1);
232 wGNOMEUpdateWorkspaceNamesHint(scr
);
237 wGNOMEUpdateWorkspaceNamesHint(WScreen
*scr
)
239 char *wsNames
[MAX_WORKSPACES
];
240 XTextProperty textProp
;
243 for (i
= 0; i
< scr
->workspace_count
; i
++) {
244 wsNames
[i
] = scr
->workspaces
[i
]->name
;
247 if (XStringListToTextProperty(wsNames
, scr
->workspace_count
, &textProp
)) {
248 XSetTextProperty(dpy
, scr
->root_win
, &textProp
,
249 _XA_WIN_WORKSPACE_NAMES
);
250 XFree(textProp
.value
);
256 wGNOMEUpdateCurrentWorkspaceHint(WScreen
*scr
)
260 val
= scr
->current_workspace
;
262 XChangeProperty(dpy
, scr
->root_win
, _XA_WIN_WORKSPACE
, XA_CARDINAL
,
263 32, PropModeReplace
, (unsigned char*)&val
, 1);
268 getWindowLevel(int layer
)
272 if (layer
<= WIN_LAYER_DESKTOP
)
273 level
= WMDesktopLevel
;
274 else if (layer
<= WIN_LAYER_BELOW
)
275 level
= WMSunkenLevel
;
276 else if (layer
<= WIN_LAYER_NORMAL
)
277 level
= WMNormalLevel
;
278 else if (layer
<= WIN_LAYER_ONTOP
)
279 level
= WMFloatingLevel
;
280 else if (layer
<= WIN_LAYER_DOCK
)
282 else if (layer
<= WIN_LAYER_ABOVE_DOCK
)
283 level
= WMSubmenuLevel
;
284 else if (layer
<= WIN_LAYER_MENU
)
285 level
= WMMainMenuLevel
;
287 level
= WMOuterSpaceLevel
;
294 wGNOMECheckClientHints(WWindow
*wwin
, int *layer
, int *workspace
)
298 unsigned long nitems_ret
;
299 unsigned long bytes_after_ret
;
300 long flags
, val
, *data
= 0;
301 Bool hasHints
= False
;
305 if (XGetWindowProperty(dpy
, wwin
->client_win
, _XA_WIN_HINTS
, 0, 1, False
,
306 /* should be XA_INTEGER, but spec is broken */
307 XA_CARDINAL
, &type_ret
, &fmt_ret
, &nitems_ret
,
309 (unsigned char**)&data
)==Success
&& data
) {
314 if (flags
& (WIN_HINTS_SKIP_FOCUS
|WIN_HINTS_SKIP_WINLIST
315 |WIN_HINTS_SKIP_TASKBAR
)) {
316 wwin
->client_flags
.skip_window_list
= 1;
319 /* client reserved area, for the panel */
320 if (flags
& (WIN_HINTS_DO_NOT_COVER
)) {
323 area
= malloc(sizeof(WReservedArea
));
325 wwarning(_("out of memory while updating GNOME hints"));
327 XWindowAttributes wattribs
;
329 XGetWindowAttributes(dpy
, wwin
->client_win
, &wattribs
);
330 wClientGetNormalHints(wwin
, &wattribs
, False
,
331 &area
->area
.x1
, &area
->area
.y1
,
332 &area
->area
.x2
, &area
->area
.y2
);
333 area
->area
.x2
= area
->area
.x2
+ area
->area
.x1
;
334 area
->area
.y2
= area
->area
.y2
+ area
->area
.y1
;
336 area
->window
= wwin
->client_win
;
338 area
->next
= wwin
->screen_ptr
->reservedAreas
;
339 wwin
->screen_ptr
->reservedAreas
= area
;
341 wScreenUpdateUsableArea(wwin
->screen_ptr
);
348 if (XGetWindowProperty(dpy
, wwin
->client_win
, _XA_WIN_LAYER
, 0, 1, False
,
349 XA_CARDINAL
, &type_ret
, &fmt_ret
, &nitems_ret
,
351 (unsigned char**)&data
)==Success
&& data
) {
356 *layer
= getWindowLevel(val
);
361 if (XGetWindowProperty(dpy
, wwin
->client_win
, _XA_WIN_WORKSPACE
, 0, 1,
362 False
, XA_CARDINAL
, &type_ret
, &fmt_ret
,
363 &nitems_ret
, &bytes_after_ret
,
364 (unsigned char**)&data
)==Success
&& data
) {
375 if (XGetWindowProperty(dpy
, wwin
->client_win
, _XA_WIN_EXPANDED_SIZE
, 0, 1,
376 False
, XA_CARDINAL
, &type_ret
, &fmt_ret
,
377 &nitems_ret
, &bytes_after_ret
,
378 (unsigned char**)&data
)==Success
&& data
) {
381 area
= malloc(sizeof(WReservedArea
));
383 wwarning(_("out of memory while updating GNOME hints"));
385 area
->area
.x1
= data
[0];
386 area
->area
.y1
= data
[1];
387 area
->area
.x2
= data
[2] - data
[0];
388 area
->area
.y2
= data
[3] - data
[1];
391 area
->window
= wwin
->client_win
;
394 area
->next
= wwin
->screen_ptr
->reservedAreas
;
395 wwin
->screen_ptr
->reservedAreas
= area
;
397 wScreenUpdateUsableArea(wwin
->screen_ptr
);
406 wGNOMECheckInitialClientState(WWindow
*wwin
)
410 unsigned long nitems_ret
;
411 unsigned long bytes_after_ret
;
412 long flags
, *data
= 0;
414 if (XGetWindowProperty(dpy
, wwin
->client_win
, _XA_WIN_STATE
, 0, 1, False
,
415 XA_CARDINAL
, &type_ret
, &fmt_ret
, &nitems_ret
,
417 (unsigned char**)&data
)!=Success
|| !data
)
424 if (flags
& WIN_STATE_STICKY
)
425 wwin
->client_flags
.omnipresent
= 1;
427 if (flags
& (WIN_STATE_MAXIMIZED_VERT
|WIN_STATE_MAXIMIZED_HORIZ
)) {
429 if (flags
& WIN_STATE_MAXIMIZED_VERT
)
430 wwin
->flags
.maximized
|= MAX_VERTICAL
;
432 if (flags
& WIN_STATE_MAXIMIZED_HORIZ
)
433 wwin
->flags
.maximized
|= MAX_HORIZONTAL
;
436 if (flags
& WIN_STATE_SHADED
)
437 wwin
->flags
.shaded
= 1;
444 wGNOMEUpdateClientStateHint(WWindow
*wwin
, Bool changedWorkspace
)
449 if (changedWorkspace
) {
450 val
= wwin
->frame
->workspace
;
452 XChangeProperty(dpy
, wwin
->client_win
, _XA_WIN_WORKSPACE
, XA_CARDINAL
,
453 32, PropModeReplace
, (unsigned char*)&val
, 1);
455 if (val
!= wwin
->screen_ptr
->current_workspace
)
456 flags
|= WIN_STATE_HID_WORKSPACE
;
459 if (IS_OMNIPRESENT(wwin
))
460 flags
|= WIN_STATE_STICKY
;
462 if (wwin
->flags
.miniaturized
)
463 flags
|= WIN_STATE_MINIMIZED
;
465 if (wwin
->flags
.maximized
& MAX_VERTICAL
)
466 flags
|= WIN_STATE_MAXIMIZED_VERT
;
468 if (wwin
->flags
.maximized
& MAX_HORIZONTAL
)
469 flags
|= WIN_STATE_MAXIMIZED_HORIZ
;
471 if (wwin
->flags
.shaded
)
472 flags
|= WIN_STATE_SHADED
;
474 if (wwin
->transient_for
!= None
) {
475 WWindow
*owner
= wWindowFor(wwin
->transient_for
);
477 if (owner
&& !owner
->flags
.mapped
)
478 flags
|= WIN_STATE_HID_TRANSIENT
;
482 if (wwin
->flags
.hidden
)
483 flags
|= WIN_STATE_HIDDEN
;
485 XChangeProperty(dpy
, wwin
->client_win
, _XA_WIN_STATE
, XA_CARDINAL
,
486 32, PropModeReplace
, (unsigned char*)&flags
, 1);
491 wGNOMEProcessClientMessage(XClientMessageEvent
*event
)
497 scr
= wScreenForWindow(event
->window
);
499 /* generic client messages */
500 if (event
->message_type
== _XA_WIN_WORKSPACE
) {
501 wWorkspaceChange(scr
, event
->data
.l
[0]);
510 /* window specific client messages */
512 wwin
= wWindowFor(event
->window
);
516 if (event
->message_type
== _XA_WIN_LAYER
) {
517 int level
= getWindowLevel(event
->data
.l
[0]);
519 if (WINDOW_LEVEL(wwin
) != level
) {
520 ChangeStackingLevel(wwin
->frame
->core
, level
);
522 } else if (event
->message_type
== _XA_WIN_STATE
) {
524 Bool updateWindowList
= False
;
527 mask
= event
->data
.l
[0];
528 flags
= event
->data
.l
[1];
530 if (mask
& WIN_STATE_STICKY
) {
531 if ((flags
& WIN_STATE_STICKY
) != WFLAGP(wwin
, omnipresent
)) {
532 wwin
->client_flags
.omnipresent
= (flags
& WIN_STATE_STICKY
)!=0;
533 wGNOMEUpdateClientStateHint(wwin
, False
);
534 updateWindowList
= True
;
538 if (mask
& WIN_STATE_MAXIMIZED_VERT
) {
539 if (flags
& WIN_STATE_MAXIMIZED_VERT
)
540 maximize
= MAX_VERTICAL
;
544 maximize
= wwin
->flags
.maximized
& MAX_VERTICAL
;
547 if (mask
& WIN_STATE_MAXIMIZED_HORIZ
) {
548 if (flags
& WIN_STATE_MAXIMIZED_HORIZ
)
549 maximize
|= MAX_HORIZONTAL
;
553 maximize
|= wwin
->flags
.maximized
& MAX_HORIZONTAL
;
556 if (maximize
!= wwin
->flags
.maximized
) {
557 #define both (MAX_HORIZONTAL|MAX_VERTICAL)
558 if (!(maximize
& both
) && (wwin
->flags
.maximized
& both
)) {
559 wUnmaximizeWindow(wwin
);
561 if ((maximize
& both
) && !(wwin
->flags
.maximized
& both
)) {
562 wMaximizeWindow(wwin
, maximize
);
564 updateWindowList
= False
;
568 if (mask
& WIN_STATE_SHADED
) {
569 if ((flags
& WIN_STATE_SHADED
) != wwin
->flags
.shaded
) {
570 if (wwin
->flags
.shaded
)
571 wUnshadeWindow(wwin
);
574 updateWindowList
= False
;
578 if (updateWindowList
) {
579 UpdateSwitchMenu(wwin
->screen_ptr
, wwin
, ACTION_CHANGE_STATE
);
581 } else if (event
->message_type
== _XA_WIN_WORKSPACE
) {
583 if (event
->data
.l
[0] != wwin
->frame
->workspace
) {
584 wWindowChangeWorkspace(wwin
, event
->data
.l
[0]);
595 wGNOMEProxyizeButtonEvent(WScreen
*scr
, XEvent
*event
)
597 if (event
->type
== ButtonPress
)
598 XUngrabPointer(dpy
, CurrentTime
);
599 XSendEvent(dpy
, scr
->no_focus_win
, False
, SubstructureNotifyMask
, event
);
606 wGNOMERemoveClient(WWindow
*wwin
)
611 wGNOMEUpdateClientListHint(wwin
->screen_ptr
);
613 area
= wwin
->screen_ptr
->reservedAreas
;
616 if (area
->window
== wwin
->client_win
) {
617 wwin
->screen_ptr
->reservedAreas
= area
->next
;
621 while (area
->next
&& area
->next
->window
!= wwin
->client_win
)
627 next
= area
->next
->next
;
637 wScreenUpdateUsableArea(wwin
->screen_ptr
);
642 #endif /* GNOME_STUFF */