1 /* gnome.c-- support for the GNOME Hints
3 * Window Maker window manager
5 * Copyright (c) 1998-2003 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"
61 #define WIN_HINTS_SKIP_FOCUS (1<<0) /*"alt-tab" skips this win*/
62 #define WIN_HINTS_SKIP_WINLIST (1<<1) /*do not show in window list*/
63 #define WIN_HINTS_SKIP_TASKBAR (1<<2) /*do not show on taskbar*/
64 #define WIN_HINTS_GROUP_TRANSIENT (1<<3) /*Reserved - definition is unclear*/
65 #define WIN_HINTS_FOCUS_ON_CLICK (1<<4) /*app only accepts focus if clicked*/
66 #define WIN_HINTS_DO_NOT_COVER (1<<5) /* attempt to not cover this window */
69 #define WIN_STATE_STICKY (1<<0) /*everyone knows sticky*/
70 #define WIN_STATE_MINIMIZED (1<<1) /*Reserved - definition is unclear*/
71 #define WIN_STATE_MAXIMIZED_VERT (1<<2) /*window in maximized V state*/
72 #define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /*window in maximized H state*/
73 #define WIN_STATE_HIDDEN (1<<4) /*not on taskbar but window visible*/
74 #define WIN_STATE_SHADED (1<<5) /*shaded (MacOS / Afterstep style)*/
75 /* these are bogus states defined in "the spec" */
76 #define WIN_STATE_HID_WORKSPACE (1<<6) /*not on current desktop*/
77 #define WIN_STATE_HID_TRANSIENT (1<<7) /*owner of transient is hidden*/
78 #define WIN_STATE_FIXED_POSITION (1<<8) /*window is fixed in position even*/
79 #define WIN_STATE_ARRANGE_IGNORE (1<<9) /*ignore for auto arranging*/
82 #define WIN_LAYER_DESKTOP 0
83 #define WIN_LAYER_BELOW 2
84 #define WIN_LAYER_NORMAL 4
85 #define WIN_LAYER_ONTOP 6
86 #define WIN_LAYER_DOCK 8
87 #define WIN_LAYER_ABOVE_DOCK 10
88 #define WIN_LAYER_MENU 12
92 static Atom _XA_WIN_SUPPORTING_WM_CHECK
= 0;
93 static Atom _XA_WIN_PROTOCOLS
;
94 static Atom _XA_WIN_LAYER
;
95 static Atom _XA_WIN_STATE
;
96 static Atom _XA_WIN_HINTS
;
97 static Atom _XA_WIN_APP_STATE
;
98 static Atom _XA_WIN_EXPANDED_SIZE
;
99 static Atom _XA_WIN_ICONS
;
100 static Atom _XA_WIN_WORKSPACE
;
101 static Atom _XA_WIN_WORKSPACE_COUNT
;
102 static Atom _XA_WIN_WORKSPACE_NAMES
;
103 static Atom _XA_WIN_CLIENT_LIST
;
104 static Atom _XA_WIN_DESKTOP_BUTTON_PROXY
;
107 static void observer(void *self
, WMNotification
*notif
);
108 static void wsobserver(void *self
, WMNotification
*notif
);
112 wGNOMEInitStuff(WScreen
*scr
)
114 Atom supportedStuff
[10];
117 if (!_XA_WIN_SUPPORTING_WM_CHECK
) {
119 _XA_WIN_SUPPORTING_WM_CHECK
=
120 XInternAtom(dpy
, "_WIN_SUPPORTING_WM_CHECK", False
);
122 _XA_WIN_PROTOCOLS
= XInternAtom(dpy
, "_WIN_PROTOCOLS", False
);
124 _XA_WIN_LAYER
= XInternAtom(dpy
, "_WIN_LAYER", False
);
126 _XA_WIN_STATE
= XInternAtom(dpy
, "_WIN_STATE", False
);
128 _XA_WIN_HINTS
= XInternAtom(dpy
, "_WIN_HINTS", False
);
130 _XA_WIN_APP_STATE
= XInternAtom(dpy
, "_WIN_APP_STATE", False
);
132 _XA_WIN_EXPANDED_SIZE
= XInternAtom(dpy
, "_WIN_EXPANDED_SIZE", False
);
134 _XA_WIN_ICONS
= XInternAtom(dpy
, "_WIN_ICONS", False
);
136 _XA_WIN_WORKSPACE
= XInternAtom(dpy
, "_WIN_WORKSPACE", False
);
138 _XA_WIN_WORKSPACE_COUNT
=
139 XInternAtom(dpy
, "_WIN_WORKSPACE_COUNT", False
);
141 _XA_WIN_WORKSPACE_NAMES
=
142 XInternAtom(dpy
, "_WIN_WORKSPACE_NAMES", False
);
144 _XA_WIN_CLIENT_LIST
= XInternAtom(dpy
, "_WIN_CLIENT_LIST", False
);
146 _XA_WIN_DESKTOP_BUTTON_PROXY
=
147 XInternAtom(dpy
, "_WIN_DESKTOP_BUTTON_PROXY", False
);
150 /* I'd rather use the ICCCM 2.0 mechanisms, but
151 * since some people prefer to reinvent the wheel instead of
152 * conforming to standards... */
154 /* setup the "We're compliant, you idiot!" hint */
156 /* why XA_CARDINAL instead of XA_WINDOW? */
157 XChangeProperty(dpy
, scr
->root_win
, _XA_WIN_SUPPORTING_WM_CHECK
,
158 XA_CARDINAL
, 32, PropModeReplace
,
159 (unsigned char*)&scr
->no_focus_win
, 1);
161 XChangeProperty(dpy
, scr
->no_focus_win
, _XA_WIN_SUPPORTING_WM_CHECK
,
162 XA_CARDINAL
, 32, PropModeReplace
,
163 (unsigned char*)&scr
->no_focus_win
, 1);
166 /* setup the "desktop button proxy" thing */
167 XChangeProperty(dpy
, scr
->root_win
, _XA_WIN_DESKTOP_BUTTON_PROXY
,
168 XA_CARDINAL
, 32, PropModeReplace
,
169 (unsigned char*)&scr
->no_focus_win
, 1);
170 XChangeProperty(dpy
, scr
->no_focus_win
, _XA_WIN_DESKTOP_BUTTON_PROXY
,
171 XA_CARDINAL
, 32, PropModeReplace
,
172 (unsigned char*)&scr
->no_focus_win
, 1);
175 /* setup the list of supported protocols */
177 supportedStuff
[count
++] = _XA_WIN_LAYER
;
178 supportedStuff
[count
++] = _XA_WIN_STATE
;
179 supportedStuff
[count
++] = _XA_WIN_HINTS
;
180 supportedStuff
[count
++] = _XA_WIN_APP_STATE
;
181 supportedStuff
[count
++] = _XA_WIN_EXPANDED_SIZE
;
182 supportedStuff
[count
++] = _XA_WIN_ICONS
;
183 supportedStuff
[count
++] = _XA_WIN_WORKSPACE
;
184 supportedStuff
[count
++] = _XA_WIN_WORKSPACE_COUNT
;
185 supportedStuff
[count
++] = _XA_WIN_WORKSPACE_NAMES
;
186 supportedStuff
[count
++] = _XA_WIN_CLIENT_LIST
;
188 XChangeProperty(dpy
, scr
->root_win
, _XA_WIN_PROTOCOLS
, XA_ATOM
, 32,
189 PropModeReplace
, (unsigned char*)supportedStuff
, count
);
193 WMAddNotificationObserver(observer
, NULL
, WMNManaged
, NULL
);
194 WMAddNotificationObserver(observer
, NULL
, WMNUnmanaged
, NULL
);
195 WMAddNotificationObserver(observer
, NULL
, WMNChangedWorkspace
, NULL
);
196 WMAddNotificationObserver(observer
, NULL
, WMNChangedState
, NULL
);
197 WMAddNotificationObserver(observer
, NULL
, WMNChangedFocus
, NULL
);
198 WMAddNotificationObserver(observer
, NULL
, WMNChangedStacking
, NULL
);
199 WMAddNotificationObserver(observer
, NULL
, WMNChangedName
, NULL
);
201 WMAddNotificationObserver(wsobserver
, NULL
, WMNWorkspaceCreated
, NULL
);
202 WMAddNotificationObserver(wsobserver
, NULL
, WMNWorkspaceDestroyed
, NULL
);
203 WMAddNotificationObserver(wsobserver
, NULL
, WMNWorkspaceChanged
, NULL
);
204 WMAddNotificationObserver(wsobserver
, NULL
, WMNWorkspaceNameChanged
, NULL
);
209 wGNOMEUpdateClientListHint(WScreen
*scr
)
215 windows
= malloc(sizeof(Window
)*scr
->window_count
);
217 wwarning(_("out of memory while updating GNOME hints"));
222 wwin
= scr
->focused_window
;
224 if (!wwin
->flags
.internal_window
&&
225 !wwin
->client_flags
.skip_window_list
) {
227 windows
[count
++] = wwin
->client_win
;
233 XChangeProperty(dpy
, scr
->root_win
, _XA_WIN_CLIENT_LIST
, XA_CARDINAL
, 32,
234 PropModeReplace
, (unsigned char *)windows
, count
);
242 wGNOMEUpdateWorkspaceHints(WScreen
*scr
)
246 val
= scr
->workspace_count
;
248 XChangeProperty(dpy
, scr
->root_win
, _XA_WIN_WORKSPACE_COUNT
, XA_CARDINAL
,
249 32, PropModeReplace
, (unsigned char*)&val
, 1);
251 wGNOMEUpdateWorkspaceNamesHint(scr
);
256 wGNOMEUpdateWorkspaceNamesHint(WScreen
*scr
)
258 char *wsNames
[MAX_WORKSPACES
];
259 XTextProperty textProp
;
262 for (i
= 0; i
< scr
->workspace_count
; i
++) {
263 wsNames
[i
] = scr
->workspaces
[i
]->name
;
266 if (XStringListToTextProperty(wsNames
, scr
->workspace_count
, &textProp
)) {
267 XSetTextProperty(dpy
, scr
->root_win
, &textProp
,
268 _XA_WIN_WORKSPACE_NAMES
);
269 XFree(textProp
.value
);
275 wGNOMEUpdateCurrentWorkspaceHint(WScreen
*scr
)
279 val
= scr
->current_workspace
;
281 XChangeProperty(dpy
, scr
->root_win
, _XA_WIN_WORKSPACE
, XA_CARDINAL
,
282 32, PropModeReplace
, (unsigned char*)&val
, 1);
287 getWindowLevel(int layer
)
291 if (layer
<= WIN_LAYER_DESKTOP
)
292 level
= WMDesktopLevel
;
293 else if (layer
<= WIN_LAYER_BELOW
)
294 level
= WMSunkenLevel
;
295 else if (layer
<= WIN_LAYER_NORMAL
)
296 level
= WMNormalLevel
;
297 else if (layer
<= WIN_LAYER_ONTOP
)
298 level
= WMFloatingLevel
;
299 else if (layer
<= WIN_LAYER_DOCK
)
301 else if (layer
<= WIN_LAYER_ABOVE_DOCK
)
302 level
= WMSubmenuLevel
;
303 else if (layer
<= WIN_LAYER_MENU
)
304 level
= WMMainMenuLevel
;
306 level
= WMOuterSpaceLevel
;
313 wGNOMECheckClientHints(WWindow
*wwin
, int *layer
, int *workspace
)
317 unsigned long nitems_ret
;
318 unsigned long bytes_after_ret
;
319 long flags
, val
, *data
= 0;
320 Bool hasHints
= False
;
324 if (XGetWindowProperty(dpy
, wwin
->client_win
, _XA_WIN_HINTS
, 0, 1, False
,
325 /* should be XA_INTEGER, but spec is broken */
326 XA_CARDINAL
, &type_ret
, &fmt_ret
, &nitems_ret
,
328 (unsigned char**)&data
)==Success
&& data
) {
333 if (flags
& (WIN_HINTS_SKIP_FOCUS
|WIN_HINTS_SKIP_WINLIST
334 |WIN_HINTS_SKIP_TASKBAR
)) {
335 wwin
->client_flags
.skip_window_list
= 1;
338 /* client reserved area, for the panel */
339 if (flags
& (WIN_HINTS_DO_NOT_COVER
)) {
342 area
= malloc(sizeof(WReservedArea
));
344 wwarning(_("out of memory while updating GNOME hints"));
346 XWindowAttributes wattribs
;
348 XGetWindowAttributes(dpy
, wwin
->client_win
, &wattribs
);
349 wClientGetNormalHints(wwin
, &wattribs
, False
,
350 &area
->area
.x1
, &area
->area
.y1
,
351 &area
->area
.x2
, &area
->area
.y2
);
352 area
->area
.x2
= area
->area
.x2
+ area
->area
.x1
;
353 area
->area
.y2
= area
->area
.y2
+ area
->area
.y1
;
355 area
->window
= wwin
->client_win
;
357 area
->next
= wwin
->screen_ptr
->reservedAreas
;
358 wwin
->screen_ptr
->reservedAreas
= area
;
360 wScreenUpdateUsableArea(wwin
->screen_ptr
);
367 if (XGetWindowProperty(dpy
, wwin
->client_win
, _XA_WIN_LAYER
, 0, 1, False
,
368 XA_CARDINAL
, &type_ret
, &fmt_ret
, &nitems_ret
,
370 (unsigned char**)&data
)==Success
&& data
) {
375 *layer
= getWindowLevel(val
);
380 if (XGetWindowProperty(dpy
, wwin
->client_win
, _XA_WIN_WORKSPACE
, 0, 1,
381 False
, XA_CARDINAL
, &type_ret
, &fmt_ret
,
382 &nitems_ret
, &bytes_after_ret
,
383 (unsigned char**)&data
)==Success
&& data
) {
394 if (XGetWindowProperty(dpy
, wwin
->client_win
, _XA_WIN_EXPANDED_SIZE
, 0, 1,
395 False
, XA_CARDINAL
, &type_ret
, &fmt_ret
,
396 &nitems_ret
, &bytes_after_ret
,
397 (unsigned char**)&data
)==Success
&& data
) {
400 area
= malloc(sizeof(WReservedArea
));
402 wwarning(_("out of memory while updating GNOME hints"));
404 area
->area
.x1
= data
[0];
405 area
->area
.y1
= data
[1];
406 area
->area
.x2
= data
[2] - data
[0];
407 area
->area
.y2
= data
[3] - data
[1];
410 area
->window
= wwin
->client_win
;
413 area
->next
= wwin
->screen_ptr
->reservedAreas
;
414 wwin
->screen_ptr
->reservedAreas
= area
;
416 wScreenUpdateUsableArea(wwin
->screen_ptr
);
425 wGNOMEGetUsableArea(WScreen
*scr
, int head
, WArea
*area
)
430 if(!scr
->reservedAreas
)
433 area
->x1
= area
->y1
= area
->x2
= area
->y2
= 0;
435 for(cur
= scr
->reservedAreas
; cur
; cur
= cur
->next
) {
436 WWindow
* wwin
= wWindowFor(cur
->window
);
437 if (wWindowTouchesHead(wwin
, head
)) {
438 if(cur
->area
.x1
> area
->x1
)
439 area
->x1
= cur
->area
.x1
;
440 if(cur
->area
.y1
> area
->y1
)
441 area
->y1
= cur
->area
.y1
;
442 if(cur
->area
.x2
> area
->x2
)
443 area
->x2
= cur
->area
.x2
;
444 if(cur
->area
.y2
> area
->y2
)
445 area
->y2
= cur
->area
.y2
;
449 if (area
->x1
==0 && area
->x2
==0 &&
450 area
->y1
==0 && area
->y2
==0) return False
;
452 rect
= wGetRectForHead(scr
, head
);
454 area
->x1
= rect
.pos
.x
+ area
->x1
;
455 area
->x2
= rect
.pos
.x
+ rect
.size
.width
- area
->x2
;
456 area
->y1
= rect
.pos
.y
+ area
->y1
;
457 area
->y2
= rect
.pos
.y
+ rect
.size
.height
- area
->y2
;
464 wGNOMECheckInitialClientState(WWindow
*wwin
)
468 unsigned long nitems_ret
;
469 unsigned long bytes_after_ret
;
470 long flags
, *data
= 0;
472 if (XGetWindowProperty(dpy
, wwin
->client_win
, _XA_WIN_STATE
, 0, 1, False
,
473 XA_CARDINAL
, &type_ret
, &fmt_ret
, &nitems_ret
,
475 (unsigned char**)&data
)!=Success
|| !data
)
482 if (flags
& WIN_STATE_STICKY
)
483 wwin
->client_flags
.omnipresent
= 1;
485 if (flags
& (WIN_STATE_MAXIMIZED_VERT
|WIN_STATE_MAXIMIZED_HORIZ
)) {
487 if (flags
& WIN_STATE_MAXIMIZED_VERT
)
488 wwin
->flags
.maximized
|= MAX_VERTICAL
;
490 if (flags
& WIN_STATE_MAXIMIZED_HORIZ
)
491 wwin
->flags
.maximized
|= MAX_HORIZONTAL
;
494 if (flags
& WIN_STATE_SHADED
)
495 wwin
->flags
.shaded
= 1;
502 wGNOMEUpdateClientStateHint(WWindow
*wwin
, Bool changedWorkspace
)
507 if (changedWorkspace
) {
508 val
= wwin
->frame
->workspace
;
510 XChangeProperty(dpy
, wwin
->client_win
, _XA_WIN_WORKSPACE
, XA_CARDINAL
,
511 32, PropModeReplace
, (unsigned char*)&val
, 1);
513 if (val
!= wwin
->screen_ptr
->current_workspace
)
514 flags
|= WIN_STATE_HID_WORKSPACE
;
517 if (IS_OMNIPRESENT(wwin
))
518 flags
|= WIN_STATE_STICKY
;
520 if (wwin
->flags
.miniaturized
)
521 flags
|= WIN_STATE_MINIMIZED
;
523 if (wwin
->flags
.maximized
& MAX_VERTICAL
)
524 flags
|= WIN_STATE_MAXIMIZED_VERT
;
526 if (wwin
->flags
.maximized
& MAX_HORIZONTAL
)
527 flags
|= WIN_STATE_MAXIMIZED_HORIZ
;
529 if (wwin
->flags
.shaded
)
530 flags
|= WIN_STATE_SHADED
;
532 if (wwin
->transient_for
!= None
) {
533 WWindow
*owner
= wWindowFor(wwin
->transient_for
);
535 if (owner
&& !owner
->flags
.mapped
)
536 flags
|= WIN_STATE_HID_TRANSIENT
;
540 if (wwin
->flags
.hidden
)
541 flags
|= WIN_STATE_HIDDEN
;
543 XChangeProperty(dpy
, wwin
->client_win
, _XA_WIN_STATE
, XA_CARDINAL
,
544 32, PropModeReplace
, (unsigned char*)&flags
, 1);
549 wGNOMEProcessClientMessage(XClientMessageEvent
*event
)
555 scr
= wScreenForWindow(event
->window
);
557 /* generic client messages */
558 if (event
->message_type
== _XA_WIN_WORKSPACE
) {
559 wWorkspaceChange(scr
, event
->data
.l
[0]);
568 /* window specific client messages */
570 wwin
= wWindowFor(event
->window
);
574 if (event
->message_type
== _XA_WIN_LAYER
) {
575 int level
= getWindowLevel(event
->data
.l
[0]);
577 if (WINDOW_LEVEL(wwin
) != level
) {
578 ChangeStackingLevel(wwin
->frame
->core
, level
);
580 } else if (event
->message_type
== _XA_WIN_STATE
) {
584 mask
= event
->data
.l
[0];
585 flags
= event
->data
.l
[1];
587 if (mask
& WIN_STATE_STICKY
) {
588 if ((flags
& WIN_STATE_STICKY
) != WFLAGP(wwin
, omnipresent
)) {
589 wWindowSetOmnipresent(wwin
, (flags
& WIN_STATE_STICKY
)!=0);
593 if (mask
& WIN_STATE_MAXIMIZED_VERT
) {
594 if (flags
& WIN_STATE_MAXIMIZED_VERT
)
595 maximize
= MAX_VERTICAL
;
599 maximize
= wwin
->flags
.maximized
& MAX_VERTICAL
;
602 if (mask
& WIN_STATE_MAXIMIZED_HORIZ
) {
603 if (flags
& WIN_STATE_MAXIMIZED_HORIZ
)
604 maximize
|= MAX_HORIZONTAL
;
608 maximize
|= wwin
->flags
.maximized
& MAX_HORIZONTAL
;
611 if (maximize
!= wwin
->flags
.maximized
) {
613 wMaximizeWindow(wwin
, maximize
);
615 wUnmaximizeWindow(wwin
);
619 if (mask
& WIN_STATE_SHADED
) {
620 if ((flags
& WIN_STATE_SHADED
) != wwin
->flags
.shaded
) {
621 if (wwin
->flags
.shaded
)
622 wUnshadeWindow(wwin
);
627 } else if (event
->message_type
== _XA_WIN_WORKSPACE
) {
629 if (event
->data
.l
[0] != wwin
->frame
->workspace
) {
630 wWindowChangeWorkspace(wwin
, event
->data
.l
[0]);
641 wGNOMEProxyizeButtonEvent(WScreen
*scr
, XEvent
*event
)
643 if (event
->type
== ButtonPress
)
644 XUngrabPointer(dpy
, CurrentTime
);
645 XSendEvent(dpy
, scr
->no_focus_win
, False
, SubstructureNotifyMask
, event
);
652 wGNOMERemoveClient(WWindow
*wwin
)
657 wGNOMEUpdateClientListHint(wwin
->screen_ptr
);
659 area
= wwin
->screen_ptr
->reservedAreas
;
662 if (area
->window
== wwin
->client_win
) {
663 wwin
->screen_ptr
->reservedAreas
= area
->next
;
667 while (area
->next
&& area
->next
->window
!= wwin
->client_win
)
673 next
= area
->next
->next
;
683 wScreenUpdateUsableArea(wwin
->screen_ptr
);
691 observer(void *self
, WMNotification
*notif
)
693 WWindow
*wwin
= (WWindow
*)WMGetNotificationObject(notif
);
694 const char *name
= WMGetNotificationName(notif
);
696 if (strcmp(name
, WMNManaged
) == 0 && wwin
) {
697 wGNOMEUpdateClientStateHint(wwin
, True
);
699 wGNOMEUpdateClientListHint(wwin
->screen_ptr
);
700 } else if (strcmp(name
, WMNUnmanaged
) == 0 && wwin
) {
701 wGNOMERemoveClient(wwin
);
702 } else if (strcmp(name
, WMNChangedWorkspace
) == 0 && wwin
) {
703 wGNOMEUpdateClientStateHint(wwin
, True
);
704 } else if (strcmp(name
, WMNChangedState
) == 0 && wwin
) {
705 wGNOMEUpdateClientStateHint(wwin
, False
);
711 wsobserver(void *self
, WMNotification
*notif
)
713 WScreen
*scr
= (WScreen
*)WMGetNotificationObject(notif
);
714 const char *name
= WMGetNotificationName(notif
);
716 if (strcmp(name
, WMNWorkspaceCreated
) == 0) {
717 wGNOMEUpdateWorkspaceHints(scr
);
718 } else if (strcmp(name
, WMNWorkspaceDestroyed
) == 0) {
719 wGNOMEUpdateWorkspaceHints(scr
);
720 } else if (strcmp(name
, WMNWorkspaceNameChanged
) == 0) {
721 wGNOMEUpdateWorkspaceNamesHint(scr
);
722 } else if (strcmp(name
, WMNWorkspaceChanged
) == 0) {
723 wGNOMEUpdateCurrentWorkspaceHint(scr
);
724 } else if (strcmp(name
, WMNResetStacking
) == 0) {
730 #endif /* GNOME_STUFF */