2 * Window Maker window manager
4 * Copyright (c) 1997-2003 Alfredo K. Kojima
5 * Copyright (c) 1998-2003 Dan Pascu
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 along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include <X11/Xutil.h>
30 #include "WindowMaker.h"
35 #include "properties.h"
37 #include "workspace.h"
39 /*** Global Variables ***/
40 extern XContext wStackContext
;
42 extern WPreferences wPreferences
;
44 static void notifyStackChange(WCoreWindow
* frame
, char *detail
)
46 WWindow
*wwin
= wWindowFor(frame
->window
);
48 WMPostNotificationName(WMNChangedStacking
, wwin
, detail
);
52 *----------------------------------------------------------------------
54 * Remakes the stacking_list for the screen, getting the real
55 * stacking order from the server and reordering windows that are not
56 * in the correct stacking.
59 * The stacking order list and the actual window stacking
60 * may be changed (corrected)
62 *----------------------------------------------------------------------
64 void RemakeStackList(WScreen
* scr
)
67 unsigned int nwindows
;
74 if (!XQueryTree(dpy
, scr
->root_win
, &junkr
, &junkp
, &windows
, &nwindows
)) {
75 wwarning(_("could not get window list!!"));
78 WMEmptyBag(scr
->stacking_list
);
80 /* verify list integrity */
82 for (i
= 0; i
< nwindows
; i
++) {
83 if (XFindContext(dpy
, windows
[i
], wStackContext
, (XPointer
*) & frame
)
90 level
= frame
->stacking
->window_level
;
91 tmp
= WMGetFromBag(scr
->stacking_list
, level
);
93 tmp
->stacking
->above
= frame
;
94 frame
->stacking
->under
= tmp
;
95 frame
->stacking
->above
= NULL
;
96 WMSetInBag(scr
->stacking_list
, level
, frame
);
99 scr
->window_count
= c
;
106 *----------------------------------------------------------------------
108 * Reorders the actual window stacking, so that it has the stacking
109 * order in the internal window stacking lists. It does the opposite
110 * of RemakeStackList().
113 * Windows may be restacked.
114 *----------------------------------------------------------------------
116 void CommitStacking(WScreen
* scr
)
123 nwindows
= scr
->window_count
;
124 windows
= wmalloc(sizeof(Window
) * nwindows
);
127 WM_ETARETI_BAG(scr
->stacking_list
, tmp
, iter
) {
129 windows
[i
++] = tmp
->window
;
130 tmp
= tmp
->stacking
->under
;
133 XRestackWindows(dpy
, windows
, i
);
135 WMPostNotificationName(WMNResetStacking
, scr
, NULL
);
139 *----------------------------------------------------------------------
141 * Reestacks windows so that "frame" is under "under".
147 * Changes the stacking order of frame.
148 *----------------------------------------------------------------------
150 static void moveFrameToUnder(WCoreWindow
* under
, WCoreWindow
* frame
)
154 wins
[0] = under
->window
;
155 wins
[1] = frame
->window
;
156 XRestackWindows(dpy
, wins
, 2);
160 *----------------------------------------------------------------------
161 * CommitStackingForWindow--
162 * Reorders the stacking for the specified window, so that it has the
163 * stacking order in the internal window stacking lists.
166 * Windows may be restacked.
167 *----------------------------------------------------------------------
169 void CommitStackingForWindow(WCoreWindow
* frame
)
171 int level
= frame
->stacking
->window_level
;
172 WScreen
*scr
= frame
->screen_ptr
;
174 if (frame
->stacking
->above
== NULL
) {
176 WCoreWindow
*above
= WMBagLast(scr
->stacking_list
, &iter
);
177 int i
, last
= above
->stacking
->window_level
;
179 /* find the 1st level above us which has windows in it */
180 for (i
= level
+ 1, above
= NULL
; i
<= last
; i
++) {
181 above
= WMGetFromBag(scr
->stacking_list
, i
);
186 if (above
!= frame
&& above
!= NULL
) {
187 while (above
->stacking
->under
)
188 above
= above
->stacking
->under
;
189 moveFrameToUnder(above
, frame
);
191 /* no window above us */
192 XRaiseWindow(dpy
, frame
->window
);
195 moveFrameToUnder(frame
->stacking
->above
, frame
);
200 *----------------------------------------------------------------------
202 * Raises a frame taking the window level into account.
208 * Window stacking order and stacking list are changed.
210 *----------------------------------------------------------------------
212 void wRaiseFrame(WCoreWindow
* frame
)
214 WCoreWindow
*wlist
= frame
;
215 int level
= frame
->stacking
->window_level
;
216 WScreen
*scr
= frame
->screen_ptr
;
219 if (frame
->stacking
->above
== NULL
) {
223 /* insert it on top of other windows on the same level */
224 if (frame
->stacking
->under
)
225 frame
->stacking
->under
->stacking
->above
= frame
->stacking
->above
;
226 if (frame
->stacking
->above
)
227 frame
->stacking
->above
->stacking
->under
= frame
->stacking
->under
;
229 frame
->stacking
->above
= NULL
;
230 frame
->stacking
->under
= WMGetFromBag(scr
->stacking_list
, level
);
231 if (frame
->stacking
->under
) {
232 frame
->stacking
->under
->stacking
->above
= frame
;
234 WMSetInBag(scr
->stacking_list
, level
, frame
);
236 /* raise transients under us from bottom to top
237 * so that the order is kept */
239 wlist
= frame
->stacking
->under
;
240 while (wlist
&& wlist
->stacking
->under
)
241 wlist
= wlist
->stacking
->under
;
242 while (wlist
&& wlist
!= frame
) {
243 if (wlist
->stacking
->child_of
== frame
) {
247 wlist
= wlist
->stacking
->above
;
250 /* try to optimize things a little */
251 if (frame
->stacking
->above
== NULL
) {
253 WCoreWindow
*above
= WMBagLast(scr
->stacking_list
, &iter
);
254 int i
, last
= above
->stacking
->window_level
;
256 /* find the 1st level above us which has windows in it */
257 for (i
= level
+ 1, above
= NULL
; i
<= last
; i
++) {
258 above
= WMGetFromBag(scr
->stacking_list
, i
);
263 if (above
!= frame
&& above
!= NULL
) {
264 while (above
->stacking
->under
)
265 above
= above
->stacking
->under
;
266 moveFrameToUnder(above
, frame
);
268 /* no window above us */
270 XRaiseWindow(dpy
, frame
->window
);
273 moveFrameToUnder(frame
->stacking
->above
, frame
);
276 notifyStackChange(frame
, "raise");
279 void wRaiseLowerFrame(WCoreWindow
* frame
)
281 if (!frame
->stacking
->above
282 || (frame
->stacking
->window_level
!= frame
->stacking
->above
->stacking
->window_level
)) {
286 WCoreWindow
*scan
= frame
->stacking
->above
;
287 WWindow
*frame_wwin
= (WWindow
*) frame
->descriptor
.parent
;
291 if (scan
->descriptor
.parent_type
== WCLASS_WINDOW
) {
292 WWindow
*scan_wwin
= (WWindow
*) scan
->descriptor
.parent
;
294 if (wWindowObscuresWindow(scan_wwin
, frame_wwin
)
295 && scan_wwin
->flags
.mapped
) {
299 scan
= scan
->stacking
->above
;
310 void wLowerFrame(WCoreWindow
* frame
)
312 WScreen
*scr
= frame
->screen_ptr
;
313 WCoreWindow
*wlist
= frame
;
314 int level
= frame
->stacking
->window_level
;
316 /* already in bottom */
317 if (wlist
->stacking
->under
== NULL
) {
320 /* cant lower transient below below its owner */
321 if (wlist
->stacking
->under
== wlist
->stacking
->child_of
) {
324 /* remove from the list */
325 if (WMGetFromBag(scr
->stacking_list
, level
) == frame
) {
326 /* it was the top window */
327 WMSetInBag(scr
->stacking_list
, level
, frame
->stacking
->under
);
328 frame
->stacking
->under
->stacking
->above
= NULL
;
330 if (frame
->stacking
->under
)
331 frame
->stacking
->under
->stacking
->above
= frame
->stacking
->above
;
332 if (frame
->stacking
->above
)
333 frame
->stacking
->above
->stacking
->under
= frame
->stacking
->under
;
335 wlist
= WMGetFromBag(scr
->stacking_list
, level
);
337 /* look for place to put this window */
339 WCoreWindow
*owner
= frame
->stacking
->child_of
;
341 if (owner
!= wlist
) {
342 while (wlist
->stacking
->under
) {
343 /* if this is a transient, it should not be placed under
345 if (owner
== wlist
->stacking
->under
)
347 wlist
= wlist
->stacking
->under
;
351 /* insert under the place found */
352 frame
->stacking
->above
= wlist
;
354 frame
->stacking
->under
= wlist
->stacking
->under
;
355 if (wlist
->stacking
->under
)
356 wlist
->stacking
->under
->stacking
->above
= frame
;
357 wlist
->stacking
->under
= frame
;
359 frame
->stacking
->under
= NULL
;
362 if (frame
->stacking
->above
== NULL
) {
364 WCoreWindow
*above
= WMBagLast(scr
->stacking_list
, &iter
);
365 int i
, last
= above
->stacking
->window_level
;
367 /* find the 1st level above us which has windows in it */
368 for (i
= level
+ 1, above
= NULL
; i
<= last
; i
++) {
369 above
= WMGetFromBag(scr
->stacking_list
, i
);
374 if (above
!= frame
&& above
!= NULL
) {
375 while (above
->stacking
->under
)
376 above
= above
->stacking
->under
;
377 moveFrameToUnder(above
, frame
);
379 /* no window below us */
380 XLowerWindow(dpy
, frame
->window
);
383 moveFrameToUnder(frame
->stacking
->above
, frame
);
386 notifyStackChange(frame
, "lower");
390 *----------------------------------------------------------------------
392 * Inserts the frame in the top of the stacking list. The
393 * stacking precedence is obeyed.
399 * The frame is added to it's screen's window list.
400 *----------------------------------------------------------------------
402 void AddToStackList(WCoreWindow
* frame
)
404 WCoreWindow
*curtop
, *wlist
;
405 int index
= frame
->stacking
->window_level
;
406 WScreen
*scr
= frame
->screen_ptr
;
407 WCoreWindow
*trans
= NULL
;
409 frame
->screen_ptr
->window_count
++;
410 XSaveContext(dpy
, frame
->window
, wStackContext
, (XPointer
) frame
);
411 curtop
= WMGetFromBag(scr
->stacking_list
, index
);
413 /* first window in this level */
414 if (curtop
== NULL
) {
415 WMSetInBag(scr
->stacking_list
, index
, frame
);
416 frame
->stacking
->above
= NULL
;
417 frame
->stacking
->under
= NULL
;
422 /* check if this is a transient owner */
425 if (wlist
->stacking
->child_of
== frame
)
427 wlist
= wlist
->stacking
->under
;
429 /* trans will hold the transient in the lowest position
430 * in stacking list */
432 frame
->stacking
->above
= trans
;
434 /* window is owner of a transient.. put it below
435 * the lowest transient */
436 frame
->stacking
->under
= trans
->stacking
->under
;
437 if (trans
->stacking
->under
) {
438 trans
->stacking
->under
->stacking
->above
= frame
;
440 trans
->stacking
->under
= frame
;
442 /* window is not owner of transients.. just put it in the
443 * top of other windows */
444 frame
->stacking
->under
= curtop
;
445 curtop
->stacking
->above
= frame
;
446 WMSetInBag(scr
->stacking_list
, index
, frame
);
452 *----------------------------------------------------------------------
453 * MoveInStackListAbove--
454 * Moves the frame above "next".
460 * Stacking order may be changed.
461 * Window level for frame may be changed.
462 *----------------------------------------------------------------------
464 void MoveInStackListAbove(WCoreWindow
* next
, WCoreWindow
* frame
)
467 WScreen
*scr
= frame
->screen_ptr
;
470 if (!next
|| frame
->stacking
->under
== next
)
473 if (frame
->stacking
->window_level
!= next
->stacking
->window_level
)
474 ChangeStackingLevel(frame
, next
->stacking
->window_level
);
476 index
= frame
->stacking
->window_level
;
478 tmpw
= WMGetFromBag(scr
->stacking_list
, index
);
480 WMSetInBag(scr
->stacking_list
, index
, frame
->stacking
->under
);
481 if (frame
->stacking
->under
)
482 frame
->stacking
->under
->stacking
->above
= frame
->stacking
->above
;
483 if (frame
->stacking
->above
)
484 frame
->stacking
->above
->stacking
->under
= frame
->stacking
->under
;
485 if (next
->stacking
->above
)
486 next
->stacking
->above
->stacking
->under
= frame
;
487 frame
->stacking
->under
= next
;
488 frame
->stacking
->above
= next
->stacking
->above
;
489 next
->stacking
->above
= frame
;
491 WMSetInBag(scr
->stacking_list
, index
, frame
);
493 /* try to optimize things a little */
494 if (frame
->stacking
->above
== NULL
) {
495 WCoreWindow
*above
= NULL
;
498 for (above
= WMBagIteratorAtIndex(scr
->stacking_list
, index
+ 1, &iter
);
499 above
!= NULL
; above
= WMBagNext(scr
->stacking_list
, &iter
)) {
502 while (above
->stacking
->under
)
503 above
= above
->stacking
->under
;
507 XRaiseWindow(dpy
, frame
->window
);
509 moveFrameToUnder(above
, frame
);
512 moveFrameToUnder(frame
->stacking
->above
, frame
);
515 WMPostNotificationName(WMNResetStacking
, scr
, NULL
);
519 *----------------------------------------------------------------------
520 * MoveInStackListUnder--
521 * Moves the frame to under "prev".
527 * Stacking order may be changed.
528 * Window level for frame may be changed.
529 *----------------------------------------------------------------------
531 void MoveInStackListUnder(WCoreWindow
* prev
, WCoreWindow
* frame
)
535 WScreen
*scr
= frame
->screen_ptr
;
537 if (!prev
|| frame
->stacking
->above
== prev
)
540 if (frame
->stacking
->window_level
!= prev
->stacking
->window_level
)
541 ChangeStackingLevel(frame
, prev
->stacking
->window_level
);
543 index
= frame
->stacking
->window_level
;
545 tmpw
= WMGetFromBag(scr
->stacking_list
, index
);
547 WMSetInBag(scr
->stacking_list
, index
, frame
->stacking
->under
);
548 if (frame
->stacking
->under
)
549 frame
->stacking
->under
->stacking
->above
= frame
->stacking
->above
;
550 if (frame
->stacking
->above
)
551 frame
->stacking
->above
->stacking
->under
= frame
->stacking
->under
;
552 if (prev
->stacking
->under
)
553 prev
->stacking
->under
->stacking
->above
= frame
;
554 frame
->stacking
->above
= prev
;
555 frame
->stacking
->under
= prev
->stacking
->under
;
556 prev
->stacking
->under
= frame
;
557 moveFrameToUnder(prev
, frame
);
559 WMPostNotificationName(WMNResetStacking
, scr
, NULL
);
562 void RemoveFromStackList(WCoreWindow
* frame
)
564 int index
= frame
->stacking
->window_level
;
566 if (XDeleteContext(dpy
, frame
->window
, wStackContext
) == XCNOENT
) {
567 wwarning("RemoveFromStackingList(): window not in list ");
570 /* remove from the window stack list */
571 if (frame
->stacking
->under
)
572 frame
->stacking
->under
->stacking
->above
= frame
->stacking
->above
;
573 if (frame
->stacking
->above
)
574 frame
->stacking
->above
->stacking
->under
= frame
->stacking
->under
;
575 else /* this was the first window on the list */
576 WMSetInBag(frame
->screen_ptr
->stacking_list
, index
, frame
->stacking
->under
);
578 frame
->screen_ptr
->window_count
--;
580 WMPostNotificationName(WMNResetStacking
, frame
->screen_ptr
, NULL
);
583 void ChangeStackingLevel(WCoreWindow
* frame
, int new_level
)
587 if (frame
->stacking
->window_level
== new_level
)
589 old_level
= frame
->stacking
->window_level
;
591 RemoveFromStackList(frame
);
592 frame
->stacking
->window_level
= new_level
;
593 AddToStackList(frame
);
594 if (old_level
> new_level
) {