Initial revision
[wmaker-crm.git] / src / stacking.c
blob902caec315ea9bfe96e3961b10e8130258a2b5d9
1 /*
2 * WindowMaker window manager
3 *
4 * Copyright (c) 1997, 1998 Alfredo K. Kojima
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 * USA.
22 #include "wconfig.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
30 #include "WindowMaker.h"
31 #include "screen.h"
32 #include "window.h"
33 #include "funcs.h"
34 #include "actions.h"
35 #include "properties.h"
36 #include "stacking.h"
38 /*** Global Variables ***/
39 extern XContext wStackContext;
41 extern WPreferences wPreferences;
44 static int
45 levelToIndex(int level)
47 switch (level) {
48 case WMNormalWindowLevel:
49 return 0;
50 case WMFloatingWindowLevel:
51 return 1;
52 case WMDockWindowLevel:
53 return 2;
54 case WMSubmenuWindowLevel:
55 return 3;
56 case WMMainMenuWindowLevel:
57 return 4;
58 default:
59 return 0;
64 *----------------------------------------------------------------------
65 * RemakeStackList--
66 * Remakes the stacking_list for the screen, getting the real
67 * stacking order from the server and reordering windows that are not
68 * in the correct stacking.
70 * Side effects:
71 * The stacking order list and the actual window stacking
72 * may be changed (corrected)
74 *----------------------------------------------------------------------
76 void
77 RemakeStackList(WScreen *scr)
79 Window *windows;
80 unsigned int nwindows;
81 Window junkr, junkp;
82 WCoreWindow *frame;
83 WCoreWindow *onbotw[MAX_WINDOW_LEVELS];
84 int level;
85 int i, c;
87 if (!XQueryTree(dpy, scr->root_win, &junkr, &junkp, &windows, &nwindows)) {
88 wwarning(_("could not get window list!!"));
89 return;
90 } else {
91 for (i=0; i<MAX_WINDOW_LEVELS; i++) {
92 scr->stacking_list[i] = NULL;
93 onbotw[i] = NULL;
94 scr->window_level_count[i] = 0;
96 /* verify list integrity */
97 c=0;
98 for (i=0; i<nwindows; i++) {
99 if (XFindContext(dpy, windows[i], wStackContext, (XPointer*)&frame)
100 ==XCNOENT) {
101 continue;
103 if (!frame) continue;
104 c++;
105 level = levelToIndex(frame->stacking->window_level);
106 if (onbotw[level])
107 onbotw[level]->stacking->above = frame;
108 frame->stacking->under = onbotw[level];
109 frame->stacking->above = NULL;
110 onbotw[level] = frame;
111 scr->window_level_count[level]++;
113 XFree(windows);
114 #ifdef DEBUG0
115 if (c!=scr->window_count) {
116 puts("Found different number of windows than in window lists!!!");
118 #endif
119 scr->window_count = c;
121 /* now, just concatenate the lists */
122 for (i=0; i<MAX_WINDOW_LEVELS; i++) {
123 scr->stacking_list[i] = onbotw[i];
124 if (onbotw[i])
125 onbotw[i]->stacking->above = NULL;
128 CommitStacking(scr);
132 *----------------------------------------------------------------------
133 * CommitStacking--
134 * Reorders the actual window stacking, so that it has the stacking
135 * order in the internal window stacking lists. It does the opposite
136 * of RemakeStackList().
138 * Side effects:
139 * Windows may be restacked.
140 *----------------------------------------------------------------------
142 void
143 CommitStacking(WScreen *scr)
145 WCoreWindow *tmp;
146 int nwindows;
147 Window *windows;
148 int i, level;
150 nwindows = scr->window_count;
151 windows = wmalloc(sizeof(Window)*nwindows);
152 i=0;
153 for (level=MAX_WINDOW_LEVELS-1; level>=0; level--) {
154 tmp = scr->stacking_list[level];
155 while (tmp) {
156 #ifndef DEBUG
157 if (i>=nwindows) {
158 puts("Internal inconsistency! window_count is incorrect!!!");
159 printf("window_count says %i windows\n", nwindows);
160 free(windows);
161 return;
163 #endif
164 windows[i++] = tmp->window;
165 tmp = tmp->stacking->under;
168 XRestackWindows(dpy, windows, i);
169 free(windows);
173 *----------------------------------------------------------------------
174 * moveFrameToUnder--
175 * Reestacks windows so that "frame" is under "under".
177 * Returns:
178 * None
180 * Side effects:
181 * Changes the stacking order of frame.
182 *----------------------------------------------------------------------
184 static void
185 moveFrameToUnder(WCoreWindow *under, WCoreWindow *frame)
187 Window wins[2];
189 wins[0] = under->window;
190 wins[1] = frame->window;
191 XRestackWindows(dpy, wins, 2);
195 *----------------------------------------------------------------------
196 * wRaiseFrame--
197 * Raises a frame taking the window level and the on_top flag
198 * into account.
200 * Returns:
201 * None
203 * Side effects:
204 * Window stacking order and window list is changed.
206 *----------------------------------------------------------------------
208 void
209 wRaiseFrame(WCoreWindow *frame)
211 WCoreWindow *wlist=frame;
212 int level = levelToIndex(frame->stacking->window_level);
213 int i;
215 /* already on top */
216 if (frame->stacking->above == NULL) {
217 return;
220 /* insert on top of other windows */
221 while (wlist)
222 wlist = wlist->stacking->above;
224 /* window is inserted before the point found */
225 if (wlist==NULL) {
226 /* top most window (last on the list) */
227 if (frame->stacking->under)
228 frame->stacking->under->stacking->above = frame->stacking->above;
229 if (frame->stacking->above)
230 frame->stacking->above->stacking->under = frame->stacking->under;
232 frame->stacking->above = NULL;
233 frame->stacking->under = frame->screen_ptr->stacking_list[level];
234 frame->screen_ptr->stacking_list[level]->stacking->above=frame;
235 frame->screen_ptr->stacking_list[level] = frame;
236 } else if (frame!=wlist) {
237 if (frame->stacking->under)
238 frame->stacking->under->stacking->above = frame->stacking->above;
239 if (frame->stacking->above)
240 frame->stacking->above->stacking->under = frame->stacking->under;
242 frame->stacking->above = wlist;
243 frame->stacking->under = wlist->stacking->under;
244 if (wlist->stacking->under)
245 wlist->stacking->under->stacking->above = frame;
246 wlist->stacking->under = frame;
248 if (wPreferences.on_top_transients) {
249 /* raise transients under us from bottom to top
250 * so that the order is kept */
251 again:
252 wlist = frame->stacking->under;
253 while (wlist && wlist->stacking->under)
254 wlist = wlist->stacking->under;
255 while (wlist && wlist!=frame) {
256 if (wlist->stacking->child_of == frame) {
257 wRaiseFrame(wlist);
258 goto again;
260 wlist = wlist->stacking->above;
262 # if 0
263 again:
264 wlist = frame->stacking->under;
265 while (wlist) {
266 if (wlist->stacking->child_of == frame) {
267 /* transient for us */
268 wRaiseFrame(wlist);
269 goto again; /* need this or we'll get in a loop */
271 wlist = wlist->stacking->under;
273 #endif
275 /* try to optimize things a little */
276 if (frame->stacking->above == NULL) {
277 WCoreWindow *above=NULL;
279 for (i=level+1; i<MAX_WINDOW_LEVELS; i++) {
280 if (frame->screen_ptr->stacking_list[i]!=NULL) {
281 /* can't optimize */
282 above = frame->screen_ptr->stacking_list[i];
283 while (above->stacking->under)
284 above = above->stacking->under;
285 break;
288 if (!above) {
289 XRaiseWindow(dpy, frame->window);
290 } else {
291 moveFrameToUnder(above, frame);
293 } else {
294 moveFrameToUnder(frame->stacking->above, frame);
298 void
299 wRaiseLowerFrame(WCoreWindow *frame)
301 if (!frame->stacking->above
302 ||(frame->stacking->window_level
303 !=frame->stacking->above->stacking->window_level))
304 wLowerFrame(frame);
305 else
306 wRaiseFrame(frame);
310 void
311 wLowerFrame(WCoreWindow *frame)
313 WScreen *scr=frame->screen_ptr;
314 WCoreWindow *prev, *wlist=frame;
315 int level = levelToIndex(frame->stacking->window_level);
316 int i;
318 /* already in bottom */
319 if (wlist->stacking->under==NULL) {
320 return;
322 if (wPreferences.on_top_transients &&
323 wlist->stacking->under==wlist->stacking->child_of) {
324 return;
326 prev = wlist;
327 /* remove from the list */
328 if (scr->stacking_list[level] == frame) {
329 /* it was the top window */
330 scr->stacking_list[level] = frame->stacking->under;
331 scr->stacking_list[level]->stacking->above = NULL;
332 } else {
333 if (frame->stacking->under)
334 frame->stacking->under->stacking->above = frame->stacking->above;
335 if (frame->stacking->above)
336 frame->stacking->above->stacking->under = frame->stacking->under;
338 wlist = scr->stacking_list[level];
339 /* look for place to put this window */
340 if (wPreferences.on_top_transients) {
341 WCoreWindow *owner = frame->stacking->child_of;
343 if (owner != wlist) {
344 while (wlist->stacking->under) {
345 /* if this is a transient, it should not be placed under
346 * it's owner */
347 if (owner == wlist->stacking->under)
348 break;
349 wlist = wlist->stacking->under;
352 } else {
353 while (wlist->stacking->under) {
354 wlist = wlist->stacking->under;
357 /* insert under the place found */
358 frame->stacking->above = wlist;
359 frame->stacking->under = wlist->stacking->under;
360 if (wlist->stacking->under)
361 wlist->stacking->under->stacking->above = frame;
362 wlist->stacking->under = frame;
364 /* try to optimize things a little */
365 if (frame->stacking->above == NULL) {
366 WCoreWindow *above = NULL;
367 for (i=level-1; i>=0; i--) {
368 if (scr->stacking_list[i]!=NULL) {
369 /* can't optimize */
370 above = scr->stacking_list[i];
371 while (above->stacking->under)
372 above = above->stacking->under;
373 break;
376 if (!above) {
377 XLowerWindow(dpy, frame->window);
378 } else {
379 moveFrameToUnder(above, frame);
381 } else {
382 moveFrameToUnder(frame->stacking->above, frame);
388 *----------------------------------------------------------------------
389 * AddToStackList--
390 * Inserts the frame in the top of the stacking list. The
391 * stacking precedence is obeyed.
393 * Returns:
394 * None
396 * Side effects:
397 * The frame is added to it's screen's window list.
398 *----------------------------------------------------------------------
400 void
401 AddToStackList(WCoreWindow *frame)
403 WCoreWindow *prev, *tmpw, *wlist;
404 int index = levelToIndex(frame->stacking->window_level);
406 frame->screen_ptr->window_count++;
407 frame->screen_ptr->window_level_count[index]++;
408 XSaveContext(dpy, frame->window, wStackContext, (XPointer)frame);
409 tmpw = frame->screen_ptr->stacking_list[index];
410 if (!tmpw) {
411 frame->screen_ptr->stacking_list[index] = frame;
412 frame->stacking->above = NULL;
413 frame->stacking->under = NULL;
414 CommitStacking(frame->screen_ptr);
415 return;
417 prev = tmpw;
418 /* check if this is a transient owner */
419 if (wPreferences.on_top_transients) {
420 WCoreWindow *trans = NULL;
422 wlist = frame->screen_ptr->stacking_list[index];
423 while (wlist) {
424 if (wlist->stacking->child_of == frame)
425 trans = wlist;
426 wlist = wlist->stacking->under;
429 frame->stacking->above = trans;
430 if (trans) {
431 frame->stacking->under = trans->stacking->under;
432 if (trans->stacking->under) {
433 trans->stacking->under->stacking->above = frame;
435 trans->stacking->under = frame;
436 } else {
437 frame->stacking->under = tmpw;
438 tmpw->stacking->above = frame;
439 frame->screen_ptr->stacking_list[index] = frame;
441 } else {
442 /* put on top of the stacking list */
443 frame->stacking->above = NULL;
444 frame->stacking->under = tmpw;
445 tmpw->stacking->above = frame;
446 frame->screen_ptr->stacking_list[index] = frame;
448 CommitStacking(frame->screen_ptr);
453 *----------------------------------------------------------------------
454 * MoveInStackListAbove--
455 * Moves the frame above "next".
457 * Returns:
458 * None
460 * Side effects:
461 * Stacking order may be changed.
462 * Window level for frame may be changed.
463 *----------------------------------------------------------------------
465 void
466 MoveInStackListAbove(WCoreWindow *next, WCoreWindow *frame)
468 WCoreWindow *tmpw;
469 int index;
471 if (!next || frame->stacking->under == next)
472 return;
474 if (frame->stacking->window_level != next->stacking->window_level)
475 ChangeStackingLevel(frame, next->stacking->window_level);
477 index = levelToIndex(frame->stacking->window_level);
479 tmpw = frame->screen_ptr->stacking_list[index];
480 if (tmpw == frame)
481 frame->screen_ptr->stacking_list[index] = frame->stacking->under;
482 if (frame->stacking->under)
483 frame->stacking->under->stacking->above = frame->stacking->above;
484 if (frame->stacking->above)
485 frame->stacking->above->stacking->under = frame->stacking->under;
486 if (next->stacking->above)
487 next->stacking->above->stacking->under = frame;
488 frame->stacking->under = next;
489 frame->stacking->above = next->stacking->above;
490 next->stacking->above = frame;
491 if (tmpw == next)
492 frame->screen_ptr->stacking_list[index] = frame;
494 /* try to optimize things a little */
495 if (frame->stacking->above == NULL) {
496 WCoreWindow *above=NULL;
497 int i;
499 for (i=index+1; i<MAX_WINDOW_LEVELS; i++) {
500 if (frame->screen_ptr->stacking_list[i]!=NULL) {
501 /* can't optimize */
502 above = frame->screen_ptr->stacking_list[i];
503 while (above->stacking->under)
504 above = above->stacking->under;
505 break;
508 if (!above) {
509 XRaiseWindow(dpy, frame->window);
510 } else {
511 moveFrameToUnder(above, frame);
513 } else {
514 moveFrameToUnder(frame->stacking->above, frame);
520 *----------------------------------------------------------------------
521 * MoveInStackListUnder--
522 * Moves the frame under "prev".
524 * Returns:
525 * None
527 * Side effects:
528 * Stacking order may be changed.
529 * Window level for frame may be changed.
530 *----------------------------------------------------------------------
532 void
533 MoveInStackListUnder(WCoreWindow *prev, WCoreWindow *frame)
535 WCoreWindow *tmpw;
536 int index;
538 if (!prev || frame->stacking->above == prev)
539 return;
541 if (frame->stacking->window_level != prev->stacking->window_level)
542 ChangeStackingLevel(frame, prev->stacking->window_level);
544 index = levelToIndex(frame->stacking->window_level);
546 tmpw = frame->screen_ptr->stacking_list[index];
547 if (tmpw == frame)
548 frame->screen_ptr->stacking_list[index] = frame->stacking->under;
549 if (frame->stacking->under)
550 frame->stacking->under->stacking->above = frame->stacking->above;
551 if (frame->stacking->above)
552 frame->stacking->above->stacking->under = frame->stacking->under;
553 if (prev->stacking->under)
554 prev->stacking->under->stacking->above = frame;
555 frame->stacking->above = prev;
556 frame->stacking->under = prev->stacking->under;
557 prev->stacking->under = frame;
558 moveFrameToUnder(prev, frame);
562 void
563 RemoveFromStackList(WCoreWindow *frame)
565 int index = levelToIndex(frame->stacking->window_level);
567 if (XDeleteContext(dpy, frame->window, wStackContext)==XCNOENT) {
568 #ifdef DEBUG0
569 wwarning("RemoveFromStackingList(): window not in list ");
570 #endif
571 return;
573 /* remove from the window stack list */
574 if (frame->stacking->under)
575 frame->stacking->under->stacking->above = frame->stacking->above;
576 if (frame->stacking->above)
577 frame->stacking->above->stacking->under = frame->stacking->under;
578 else /* this was the first window on the list */
579 frame->screen_ptr->stacking_list[index] = frame->stacking->under;
581 frame->screen_ptr->window_count--;
582 frame->screen_ptr->window_level_count[index]--;
586 void
587 ChangeStackingLevel(WCoreWindow *frame, int new_level)
589 int old_level;
590 if (frame->stacking->window_level == new_level)
591 return;
592 old_level = frame->stacking->window_level;
594 RemoveFromStackList(frame);
595 frame->stacking->window_level = new_level;
596 AddToStackList(frame);
597 if (old_level > new_level) {
598 wRaiseFrame(frame);
599 } else {
600 wLowerFrame(frame);