updated code to use new bags
[wmaker-crm.git] / src / stacking.c
blob4ac9434120f8837aaa6c36e5d392947c158ca466
1 /*
2 * Window Maker window manager
3 *
4 * Copyright (c) 1997, 1998 Alfredo K. Kojima
5 * Copyright (c) 1998 Dan Pascu
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.
23 #include "wconfig.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <X11/Xlib.h>
29 #include <X11/Xutil.h>
31 #include "WindowMaker.h"
32 #include "screen.h"
33 #include "window.h"
34 #include "funcs.h"
35 #include "actions.h"
36 #include "properties.h"
37 #include "stacking.h"
38 #ifdef KWM_HINTS
39 #include "kwm.h"
40 #endif
42 /*** Global Variables ***/
43 extern XContext wStackContext;
45 extern WPreferences wPreferences;
52 *----------------------------------------------------------------------
53 * RemakeStackList--
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.
58 * Side effects:
59 * The stacking order list and the actual window stacking
60 * may be changed (corrected)
62 *----------------------------------------------------------------------
64 void
65 RemakeStackList(WScreen *scr)
67 Window *windows;
68 unsigned int nwindows;
69 Window junkr, junkp;
70 WCoreWindow *frame;
71 WCoreWindow *tmp;
72 int level;
73 int i, c;
75 if (!XQueryTree(dpy, scr->root_win, &junkr, &junkp, &windows, &nwindows)) {
76 wwarning(_("could not get window list!!"));
77 return;
78 } else {
79 WMEmptyBag(scr->stacking_list);
81 /* verify list integrity */
82 c=0;
83 for (i=0; i<nwindows; i++) {
84 if (XFindContext(dpy, windows[i], wStackContext, (XPointer*)&frame)
85 ==XCNOENT) {
86 continue;
88 if (!frame) continue;
89 c++;
90 level = frame->stacking->window_level;
91 tmp = WMGetFromBag(scr->stacking_list, level);
92 if (tmp)
93 tmp->stacking->above = frame;
94 frame->stacking->under = tmp;
95 frame->stacking->above = NULL;
96 WMSetInBag(scr->stacking_list, level, frame);
98 XFree(windows);
99 #ifdef DEBUG
100 if (c!=scr->window_count) {
101 puts("Found different number of windows than in window lists!!!");
103 #endif
104 scr->window_count = c;
107 CommitStacking(scr);
111 *----------------------------------------------------------------------
112 * CommitStacking--
113 * Reorders the actual window stacking, so that it has the stacking
114 * order in the internal window stacking lists. It does the opposite
115 * of RemakeStackList().
117 * Side effects:
118 * Windows may be restacked.
119 *----------------------------------------------------------------------
121 void
122 CommitStacking(WScreen *scr)
124 WCoreWindow *tmp;
125 int nwindows, i;
126 Window *windows;
127 WMBagIterator iter;
129 nwindows = scr->window_count;
130 windows = wmalloc(sizeof(Window)*nwindows);
132 for (tmp = WMBagLast(scr->stacking_list, &iter), i = 0;
133 iter != NULL;
134 WMBagNext(scr->stacking_list, &iter)) {
135 while (tmp) {
136 #ifdef DEBUG
137 if (i>=nwindows) {
138 puts("Internal inconsistency! window_count is incorrect!!!");
139 printf("window_count says %i windows\n", nwindows);
140 free(windows);
141 return;
143 #endif
144 windows[i++] = tmp->window;
145 tmp = tmp->stacking->under;
148 XRestackWindows(dpy, windows, i);
149 free(windows);
151 #ifdef KWM_HINTS
152 wKWMBroadcastStacking(scr);
153 #endif
157 *----------------------------------------------------------------------
158 * moveFrameToUnder--
159 * Reestacks windows so that "frame" is under "under".
161 * Returns:
162 * None
164 * Side effects:
165 * Changes the stacking order of frame.
166 *----------------------------------------------------------------------
168 static void
169 moveFrameToUnder(WCoreWindow *under, WCoreWindow *frame)
171 Window wins[2];
173 wins[0] = under->window;
174 wins[1] = frame->window;
175 XRestackWindows(dpy, wins, 2);
177 #ifdef KWM_HINTS
178 wKWMBroadcastStacking(under->screen_ptr);
179 #endif
183 *----------------------------------------------------------------------
184 * wRaiseFrame--
185 * Raises a frame taking the window level and the on_top flag
186 * into account.
188 * Returns:
189 * None
191 * Side effects:
192 * Window stacking order and window list is changed.
194 *----------------------------------------------------------------------
196 void
197 wRaiseFrame(WCoreWindow *frame)
199 WCoreWindow *wlist = frame, *wlist_above;
200 int level = frame->stacking->window_level;
201 WScreen *scr = frame->screen_ptr;
203 /* already on top */
204 if (frame->stacking->above == NULL) {
205 return;
208 /* insert on top of other windows */
210 while (wlist) {
211 if (wlist == (wlist_above = wlist->stacking->above)) {
212 wwarning("You just found a bug in wmaker. Please try to figure what type of raising/lowering operations you did with which applications and report. Please give complete information about how to reproduce it.");
213 break;
214 } else {
215 wlist=wlist_above;
219 /* window is inserted before the point found */
220 if (wlist==NULL) {
221 /* top most window (last on the list) */
222 if (frame->stacking->under)
223 frame->stacking->under->stacking->above = frame->stacking->above;
224 if (frame->stacking->above)
225 frame->stacking->above->stacking->under = frame->stacking->under;
227 frame->stacking->above = NULL;
228 frame->stacking->under = WMGetFromBag(scr->stacking_list, level);
229 frame->stacking->under->stacking->above = frame;
230 WMSetInBag(scr->stacking_list, level, frame);
232 } else if (frame!=wlist) {
233 if (frame->stacking->under)
234 frame->stacking->under->stacking->above = frame->stacking->above;
235 if (frame->stacking->above)
236 frame->stacking->above->stacking->under = frame->stacking->under;
238 frame->stacking->above = wlist;
239 frame->stacking->under = wlist->stacking->under;
240 if (wlist->stacking->under)
241 wlist->stacking->under->stacking->above = frame;
242 wlist->stacking->under = frame;
244 #ifdef removed
245 if (wPreferences.on_top_transients)
246 #endif
248 /* raise transients under us from bottom to top
249 * so that the order is kept */
250 again:
251 wlist = frame->stacking->under;
252 while (wlist && wlist->stacking->under)
253 wlist = wlist->stacking->under;
254 while (wlist && wlist!=frame) {
255 if (wlist->stacking->child_of == frame) {
256 wRaiseFrame(wlist);
257 goto again;
259 wlist = wlist->stacking->above;
261 # if 0
262 again:
263 wlist = frame->stacking->under;
264 while (wlist) {
265 if (wlist->stacking->child_of == frame) {
266 /* transient for us */
267 wRaiseFrame(wlist);
268 goto again; /* need this or we'll get in a loop */
270 wlist = wlist->stacking->under;
272 #endif
274 /* try to optimize things a little */
275 if (frame->stacking->above == NULL) {
276 WCoreWindow *above=NULL;
277 WMBagIterator iter;
279 for (above = WMBagIteratorAtIndex(scr->stacking_list, level+1, &iter);
280 above != NULL;
281 above = WMBagNext(scr->stacking_list, &iter)) {
283 /* can't optimize */
284 while (above->stacking->under)
285 above = above->stacking->under;
286 break;
288 if (above == NULL) {
289 XRaiseWindow(dpy, frame->window);
290 } else {
291 moveFrameToUnder(above, frame);
293 } else {
294 moveFrameToUnder(frame->stacking->above, frame);
296 #ifdef KWM_HINTS
298 WWindow *wwin = wWindowFor(frame->window);
300 if (wwin != NULL)
301 wKWMSendEventMessage(wwin, WKWMRaiseWindow);
303 #endif
304 #ifdef VIRTUAL_DESKTOP
305 wWorkspaceRaiseEdge(scr);
306 #endif
310 void
311 wRaiseLowerFrame(WCoreWindow *frame)
313 if (!frame->stacking->above
314 ||(frame->stacking->window_level
315 !=frame->stacking->above->stacking->window_level)) {
317 wLowerFrame(frame);
318 } else {
319 WCoreWindow *scan = frame->stacking->above;
320 WWindow *frame_wwin = (WWindow*) frame->descriptor.parent;
322 while (scan) {
324 if (scan->descriptor.parent_type == WCLASS_WINDOW) {
325 WWindow *scan_wwin = (WWindow*)scan->descriptor.parent;
327 if (wWindowObscuresWindow(scan_wwin, frame_wwin)
328 && scan_wwin->flags.mapped) {
329 break;
332 scan = scan->stacking->above;
335 if (scan) {
336 wRaiseFrame(frame);
337 } else {
338 wLowerFrame(frame);
344 void
345 wLowerFrame(WCoreWindow *frame)
347 WScreen *scr = frame->screen_ptr;
348 WCoreWindow *prev, *wlist=frame;
349 int level = frame->stacking->window_level;
351 /* already in bottom */
352 if (wlist->stacking->under == NULL) {
353 return;
355 #ifdef removed
356 if (wPreferences.on_top_transients &&
357 wlist->stacking->under==wlist->stacking->child_of) {
358 return;
360 #else
361 if (wlist->stacking->under==wlist->stacking->child_of) {
362 return;
364 #endif
365 prev = wlist;
366 /* remove from the list */
367 if (WMGetFromBag(scr->stacking_list, level) == frame) {
368 /* it was the top window */
369 WMSetInBag(scr->stacking_list, level, frame->stacking->under);
370 frame->stacking->under->stacking->above = NULL;
371 } else {
372 if (frame->stacking->under)
373 frame->stacking->under->stacking->above = frame->stacking->above;
374 if (frame->stacking->above)
375 frame->stacking->above->stacking->under = frame->stacking->under;
377 wlist = WMGetFromBag(scr->stacking_list, level);
378 /* look for place to put this window */
379 #ifdef removed
380 if (wPreferences.on_top_transients)
381 #endif
383 WCoreWindow *owner = frame->stacking->child_of;
385 if (owner != wlist) {
386 while (wlist->stacking->under) {
387 /* if this is a transient, it should not be placed under
388 * it's owner */
389 if (owner == wlist->stacking->under)
390 break;
391 wlist = wlist->stacking->under;
395 #ifdef removed
396 else {
397 while (wlist->stacking->under) {
398 wlist = wlist->stacking->under;
401 #endif
402 /* insert under the place found */
403 frame->stacking->above = wlist;
404 frame->stacking->under = wlist->stacking->under;
405 if (wlist->stacking->under)
406 wlist->stacking->under->stacking->above = frame;
407 wlist->stacking->under = frame;
409 /* try to optimize things a little */
410 if (frame->stacking->above == NULL) {
411 WCoreWindow *above = NULL;
412 WMBagIterator iter;
414 for (above = WMBagIteratorAtIndex(scr->stacking_list, level-1, &iter);
415 above != NULL;
416 above = WMBagPrevious(scr->stacking_list, &iter)) {
418 /* can't optimize */
419 while (above->stacking->under)
420 above = above->stacking->under;
421 break;
423 if (!above) {
424 XLowerWindow(dpy, frame->window);
425 } else {
426 moveFrameToUnder(above, frame);
428 } else {
429 moveFrameToUnder(frame->stacking->above, frame);
431 #ifdef KWM_HINTS
433 WWindow *wwin = wWindowFor(frame->window);
435 if (wwin)
436 wKWMSendEventMessage(wwin, WKWMLowerWindow);
438 #endif
443 *----------------------------------------------------------------------
444 * AddToStackList--
445 * Inserts the frame in the top of the stacking list. The
446 * stacking precedence is obeyed.
448 * Returns:
449 * None
451 * Side effects:
452 * The frame is added to it's screen's window list.
453 *----------------------------------------------------------------------
455 void
456 AddToStackList(WCoreWindow *frame)
458 WCoreWindow *prev, *tmpw, *wlist;
459 int index = frame->stacking->window_level;
460 WScreen *scr = frame->screen_ptr;
462 frame->screen_ptr->window_count++;
463 XSaveContext(dpy, frame->window, wStackContext, (XPointer)frame);
464 tmpw = WMGetFromBag(scr->stacking_list, index);
465 if (!tmpw) {
466 WMSetInBag(scr->stacking_list, index, frame);
467 frame->stacking->above = NULL;
468 frame->stacking->under = NULL;
469 CommitStacking(scr);
470 return;
472 prev = tmpw;
473 /* check if this is a transient owner */
474 #ifdef removed
475 if (wPreferences.on_top_transients)
476 #endif
478 WCoreWindow *trans = NULL;
480 wlist = WMGetFromBag(scr->stacking_list, index);
481 while (wlist) {
482 if (wlist->stacking->child_of == frame)
483 trans = wlist;
484 wlist = wlist->stacking->under;
487 frame->stacking->above = trans;
488 if (trans) {
489 frame->stacking->under = trans->stacking->under;
490 if (trans->stacking->under) {
491 trans->stacking->under->stacking->above = frame;
493 trans->stacking->under = frame;
494 } else {
495 frame->stacking->under = tmpw;
496 tmpw->stacking->above = frame;
497 WMSetInBag(scr->stacking_list, index, frame);
500 #ifdef removed
501 else {
502 /* put on top of the stacking list */
503 frame->stacking->above = NULL;
504 frame->stacking->under = tmpw;
505 tmpw->stacking->above = frame;
506 WMSetInBag(scr->stacking_list, index, frame);
508 #endif
509 CommitStacking(scr);
514 *----------------------------------------------------------------------
515 * MoveInStackListAbove--
516 * Moves the frame above "next".
518 * Returns:
519 * None
521 * Side effects:
522 * Stacking order may be changed.
523 * Window level for frame may be changed.
524 *----------------------------------------------------------------------
526 void
527 MoveInStackListAbove(WCoreWindow *next, WCoreWindow *frame)
529 WCoreWindow *tmpw;
530 WScreen *scr = frame->screen_ptr;
531 int index;
533 if (!next || frame->stacking->under == next)
534 return;
536 if (frame->stacking->window_level != next->stacking->window_level)
537 ChangeStackingLevel(frame, next->stacking->window_level);
539 index = frame->stacking->window_level;
541 tmpw = WMGetFromBag(scr->stacking_list, index);
542 if (tmpw == frame)
543 WMSetInBag(scr->stacking_list, index, frame->stacking->under);
544 if (frame->stacking->under)
545 frame->stacking->under->stacking->above = frame->stacking->above;
546 if (frame->stacking->above)
547 frame->stacking->above->stacking->under = frame->stacking->under;
548 if (next->stacking->above)
549 next->stacking->above->stacking->under = frame;
550 frame->stacking->under = next;
551 frame->stacking->above = next->stacking->above;
552 next->stacking->above = frame;
553 if (tmpw == next)
554 WMSetInBag(scr->stacking_list, index, frame);
556 /* try to optimize things a little */
557 if (frame->stacking->above == NULL) {
558 WCoreWindow *above = NULL;
559 WMBagIterator iter;
561 for (above = WMBagIteratorAtIndex(scr->stacking_list, index+1, &iter);
562 above != NULL;
563 above = WMBagNext(scr->stacking_list, &iter)) {
565 /* can't optimize */
566 while (above->stacking->under)
567 above = above->stacking->under;
568 break;
570 if (above == NULL) {
571 XRaiseWindow(dpy, frame->window);
572 } else {
573 moveFrameToUnder(above, frame);
575 } else {
576 moveFrameToUnder(frame->stacking->above, frame);
582 *----------------------------------------------------------------------
583 * MoveInStackListUnder--
584 * Moves the frame to under "prev".
586 * Returns:
587 * None
589 * Side effects:
590 * Stacking order may be changed.
591 * Window level for frame may be changed.
592 *----------------------------------------------------------------------
594 void
595 MoveInStackListUnder(WCoreWindow *prev, WCoreWindow *frame)
597 WCoreWindow *tmpw;
598 int index;
599 WScreen *scr = frame->screen_ptr;
601 if (!prev || frame->stacking->above == prev)
602 return;
604 if (frame->stacking->window_level != prev->stacking->window_level)
605 ChangeStackingLevel(frame, prev->stacking->window_level);
607 index = frame->stacking->window_level;
609 tmpw = WMGetFromBag(scr->stacking_list, index);
610 if (tmpw == frame)
611 WMSetInBag(scr->stacking_list, index, frame->stacking->under);
612 if (frame->stacking->under)
613 frame->stacking->under->stacking->above = frame->stacking->above;
614 if (frame->stacking->above)
615 frame->stacking->above->stacking->under = frame->stacking->under;
616 if (prev->stacking->under)
617 prev->stacking->under->stacking->above = frame;
618 frame->stacking->above = prev;
619 frame->stacking->under = prev->stacking->under;
620 prev->stacking->under = frame;
621 moveFrameToUnder(prev, frame);
625 void
626 RemoveFromStackList(WCoreWindow *frame)
628 int index = frame->stacking->window_level;
630 if (XDeleteContext(dpy, frame->window, wStackContext)==XCNOENT) {
631 #ifdef DEBUG0
632 wwarning("RemoveFromStackingList(): window not in list ");
633 #endif
634 return;
636 /* remove from the window stack list */
637 if (frame->stacking->under)
638 frame->stacking->under->stacking->above = frame->stacking->above;
639 if (frame->stacking->above)
640 frame->stacking->above->stacking->under = frame->stacking->under;
641 else /* this was the first window on the list */
642 WMSetInBag(frame->screen_ptr->stacking_list, index,
643 frame->stacking->under);
645 frame->screen_ptr->window_count--;
649 void
650 ChangeStackingLevel(WCoreWindow *frame, int new_level)
652 int old_level;
653 if (frame->stacking->window_level == new_level)
654 return;
655 old_level = frame->stacking->window_level;
657 RemoveFromStackList(frame);
658 frame->stacking->window_level = new_level;
659 AddToStackList(frame);
660 if (old_level > new_level) {
661 wRaiseFrame(frame);
662 } else {
663 wLowerFrame(frame);