1 /*
2 * Copyright 1992, 2005 Stefan Monnier.
3 *
4 * Author: Stefan Monnier [ monnier@lia.di.epfl.ch ]
5 * Adapted for use with more than one virtual screen by
6 * Olaf "Rhialto" Seibert <rhialto@falu.nl>.
7 *
8 * $Id: otp.c,v 1.9 2005/04/08 16:59:25 monnier Exp $
9 *
10 * handles all the OnTopPriority-related issues.
11 *
12 */
13
14 #include "ctwm.h"
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <assert.h>
19 #include <X11/Xatom.h>
20
21 #include "otp.h"
22 #include "ctwm_atoms.h"
23 #include "screen.h"
24 #include "util.h"
25 #include "icons.h"
26 #include "list.h"
27 #include "events.h"
28 #include "event_handlers.h"
29 #include "vscreen.h"
30 #include "win_utils.h"
31
32 #define DEBUG_OTP 0
33 #if DEBUG_OTP
34 #define DPRINTF(x) fprintf x
35 #else
36 #define DPRINTF(x)
37 #endif
38
39 #if defined(NDEBUG)
40 # define CHECK_OTP 0
41 #else
42 # define CHECK_OTP 1
43 #endif
44
45 /* number of priorities known to ctwm: [0..ONTOP_MAX] */
46 #define OTP_ZERO 8
47 #define OTP_MAX (OTP_ZERO * 2)
48
49 /* Shorten code a little */
50 #define PRI(owl) OwlEffectivePriority(owl)
51 #define PRI_CP(from, to) do { \
52 to->pri_base = from->pri_base; \
53 to->pri_aflags = from->pri_aflags; \
54 } while(0)
55
56 struct OtpWinList {
57 OtpWinList *above;
58 OtpWinList *below;
59 TwmWindow *twm_win;
60 WinType type;
61 bool switching;
62 int pri_base; // Base priority
63 unsigned pri_aflags; // Flags that might alter it; OTP_AFLAG_*
64 bool stashed_aflags;
65 };
66
67 struct OtpPreferences {
68 name_list *priorityL[OTP_MAX + 1];
69 int priority;
70 name_list *switchingL;
71 bool switching;
72 };
73
74 typedef struct Box {
75 int x;
76 int y;
77 int width;
78 int height;
79 } Box;
80
81
82 static bool OtpCheckConsistencyVS(VirtualScreen *currentvs, Window vroot);
83 static void OwlSetAflagMask(OtpWinList *owl, unsigned mask, unsigned setto);
84 static void OwlSetAflag(OtpWinList *owl, unsigned flag);
85 static void OwlClearAflag(OtpWinList *owl, unsigned flag);
86 static void OwlStashAflags(OtpWinList *owl);
87 static unsigned OwlGetStashedAflags(OtpWinList *owl, bool *gotit);
88 static int OwlEffectivePriority(OtpWinList *owl);
89
BoxOfOwl(OtpWinList * owl)90 static Box BoxOfOwl(OtpWinList *owl)
91 {
92 Box b;
93
94 switch(owl->type) {
95 case IconWin: {
96 Icon *icon = owl->twm_win->icon;
97
98 b.x = icon->w_x;
99 b.y = icon->w_y;
100 b.width = icon->w_width;
101 b.height = icon->w_height;
102 break;
103 }
104 case WinWin: {
105 TwmWindow *twm_win = owl->twm_win;
106
107 b.x = twm_win->frame_x;
108 b.y = twm_win->frame_y;
109 b.width = twm_win->frame_width;
110 b.height = twm_win->frame_height;
111 break;
112 }
113 default:
114 assert(false);
115 }
116 return b;
117 }
118
119
BoxesIntersect(Box * b1,Box * b2)120 static bool BoxesIntersect(Box *b1, Box *b2)
121 {
122 bool interX = (b1->x + b1->width > b2->x) && (b2->x + b2->width > b1->x);
123 bool interY = (b1->y + b1->height > b2->y) && (b2->y + b2->height > b1->y);
124
125 return (interX && interY);
126 }
127
128
isIntersectingWith(OtpWinList * owl1,OtpWinList * owl2)129 static bool isIntersectingWith(OtpWinList *owl1, OtpWinList *owl2)
130 {
131 Box b1 = BoxOfOwl(owl1);
132 Box b2 = BoxOfOwl(owl2);
133
134 return BoxesIntersect(&b1, &b2);
135 }
136
137
isOnScreen(OtpWinList * owl)138 static bool isOnScreen(OtpWinList *owl)
139 {
140 TwmWindow *twm_win = owl->twm_win;
141
142 return (((owl->type == IconWin) ? twm_win->iconified : twm_win->mapped)
143 && OCCUPY(twm_win, Scr->currentvs->wsw->currentwspc));
144 }
145
146
isTransientOf(TwmWindow * trans,TwmWindow * main)147 bool isTransientOf(TwmWindow *trans, TwmWindow *main)
148 {
149 return (trans->istransient && trans->transientfor == main->w);
150 }
151
isGroupLeader(TwmWindow * twm_win)152 bool isGroupLeader(TwmWindow *twm_win)
153 {
154 return ((twm_win->group == 0)
155 || (twm_win->group == twm_win->w));
156 }
157
isGroupLeaderOf(TwmWindow * leader,TwmWindow * twm_win)158 bool isGroupLeaderOf(TwmWindow *leader, TwmWindow *twm_win)
159 {
160 return (isGroupLeader(leader)
161 && !isGroupLeader(twm_win)
162 && (leader->group == twm_win->group));
163 }
164
isSmallTransientOf(TwmWindow * trans,TwmWindow * main)165 bool isSmallTransientOf(TwmWindow *trans, TwmWindow *main)
166 {
167 int trans_area, main_area;
168
169 if(isTransientOf(trans, main)) {
170 assert(trans->frame);
171 trans_area = trans->frame_width * trans->frame_height;
172 main_area = main->frame_width * main->frame_height;
173
174 return (trans_area < ((main_area * Scr->TransientOnTop) / 100));
175 }
176 else {
177 return false;
178 }
179 }
180
WindowOfOwl(OtpWinList * owl)181 static Window WindowOfOwl(OtpWinList *owl)
182 {
183 return (owl->type == IconWin)
184 ? owl->twm_win->icon->w : owl->twm_win->frame;
185 }
186
OtpCheckConsistency(void)187 bool OtpCheckConsistency(void)
188 {
189 #if DEBUG_OTP
190 VirtualScreen *tvs;
191 bool result = true;
192
193 for(tvs = Scr->vScreenList; tvs != NULL; tvs = tvs->next) {
194 fprintf(stderr, "OtpCheckConsistencyVS: vs:(x,y)=(%d,%d)\n",
195 tvs->x, tvs->y);
196 result = result && OtpCheckConsistencyVS(tvs, tvs->window);
197 }
198 return result;
199 #else
200 return OtpCheckConsistencyVS(Scr->currentvs, Scr->Root);
201 #endif
202 }
203
OtpCheckConsistencyVS(VirtualScreen * currentvs,Window vroot)204 static bool OtpCheckConsistencyVS(VirtualScreen *currentvs, Window vroot)
205 {
206 #if CHECK_OTP
207 OtpWinList *owl;
208 TwmWindow *twm_win;
209 Window root, parent, *children;
210 unsigned int nchildren;
211 int priority = 0;
212 int stack = -1;
213 int nwins = 0;
214
215 XQueryTree(dpy, vroot, &root, &parent, &children, &nchildren);
216
217 #if DEBUG_OTP
218 {
219 int i;
220 fprintf(stderr, "XQueryTree: %d children:\n", nchildren);
221
222 for(i = 0; i < nchildren; i++) {
223 fprintf(stderr, "[%d]=%x ", i, (unsigned int)children[i]);
224 }
225 fprintf(stderr, "\n");
226 }
227 #endif
228
229 for(owl = Scr->bottomOwl; owl != NULL; owl = owl->above) {
230 twm_win = owl->twm_win;
231
232 /* check the back arrows are correct */
233 assert(((owl->type == IconWin) && (owl == twm_win->icon->otp))
234 || ((owl->type == WinWin) && (owl == twm_win->otp)));
235
236 /* check the doubly linked list's consistency */
237 if(owl->below == NULL) {
238 assert(owl == Scr->bottomOwl);
239 }
240 else {
241 assert(owl->below->above == owl);
242 }
243
244 /* Code already ensures this */
245 assert(owl->pri_base <= OTP_MAX);
246
247 /* List should be bottom->top, so effective pri better ascend */
248 assert(PRI(owl) >= priority);
249 priority = PRI(owl);
250
251 #if DEBUG_OTP
252
253 fprintf(stderr, "checking owl: pri %d w=%x stack=%d",
254 priority, (unsigned int)WindowOfOwl(owl), stack);
255 if(twm_win) {
256 fprintf(stderr, " title=%s occupation=%x ",
257 twm_win->name,
258 (unsigned int)twm_win->occupation);
259 if(owl->twm_win->vs) {
260 fprintf(stderr, " vs:(x,y)=(%d,%d)",
261 twm_win->vs->x,
262 twm_win->vs->y);
263 }
264 else {
265 fprintf(stderr, " vs:NULL");
266 }
267 if(owl->twm_win->parent_vs) {
268 fprintf(stderr, " parent_vs:(x,y)=(%d,%d)",
269 twm_win->parent_vs->x,
270 twm_win->parent_vs->y);
271 }
272 else {
273 fprintf(stderr, " parent_vs:NULL");
274 }
275 }
276 fprintf(stderr, " %s\n", (owl->type == WinWin ? "Window" : "Icon"));
277 #endif
278
279 /* count the number of twm windows */
280 if(owl->type == WinWin) {
281 nwins++;
282 }
283
284 if(twm_win->winbox) {
285 /*
286 * We can't check windows in a WindowBox, since they are
287 * not direct children of the Root window.
288 */
289 DPRINTF((stderr, "Can't check this window, it is in a WinBox\n"));
290 continue;
291 }
292
293 /*
294 * Check only windows from the current vitual screen; the others
295 * won't show up in the tree from XQueryTree().
296 */
297 if(currentvs == twm_win->parent_vs) {
298 /* check the window's existence. */
299 Window windowOfOwl = WindowOfOwl(owl);
300
301 #if DEBUG_OTP
302 int i;
303 for(i = 0; i < nchildren && windowOfOwl != children[i];) {
304 i++;
305 }
306 fprintf(stderr, "search for owl in stack -> i=%d\n", i);
307 assert(i > stack && "Window not in good place in stack");
308 assert(i < nchildren && "Window was not found in stack");
309 if(0) {
310 char buf[128];
311 snprintf(buf, 128, "xwininfo -all -id %d", (int)windowOfOwl);
312 system(buf);
313 }
314
315 /* we know that this always increases stack (assert i>stack) */
316 stack = i;
317 #else /* DEBUG_OTP */
318 /* check against the Xserver's stack */
319 do {
320 stack++;
321 DPRINTF((stderr, "stack++: children[%d] = %x\n", stack,
322 (unsigned int)children[stack]));
323 assert(stack < nchildren);
324 }
325 while(windowOfOwl != children[stack]);
326 #endif /* DEBUG_OTP */
327 }
328 }
329
330 XFree(children);
331
332 /* by decrementing nwins, check that all the wins are in our list */
333 for(twm_win = Scr->FirstWindow; twm_win != NULL; twm_win = twm_win->next) {
334 nwins--;
335 }
336 /* if we just removed a win, it might still be somewhere, hence the -1 */
337 assert((nwins <= 0) && (nwins >= -1));
338 #endif
339 return true;
340 }
341
342
RemoveOwl(OtpWinList * owl)343 static void RemoveOwl(OtpWinList *owl)
344 {
345 if(owl->above != NULL) {
346 owl->above->below = owl->below;
347 }
348 if(owl->below != NULL) {
349 owl->below->above = owl->above;
350 }
351 else {
352 Scr->bottomOwl = owl->above;
353 }
354 owl->below = NULL;
355 owl->above = NULL;
356 }
357
358
359 /**
360 * For the purpose of putting a window above another,
361 * they need to have the same parent, i.e. be in the same
362 * VirtualScreen.
363 */
GetOwlAtOrBelowInVS(OtpWinList * owl,VirtualScreen * vs)364 static OtpWinList *GetOwlAtOrBelowInVS(OtpWinList *owl, VirtualScreen *vs)
365 {
366 while(owl != NULL && owl->twm_win->parent_vs != vs) {
367 owl = owl->below;
368 }
369
370 return owl;
371 }
372
373 /*
374 * Windows in a box don't really occur in the stacking order of the
375 * root window.
376 * In the OWL list, keep them just on top of their box, in their
377 * respective order of course.
378 * Therefore we may need to update the owl we're going to be above.
379 */
GetOwlAtOrBelowInWinbox(OtpWinList ** owlp,WindowBox * wb)380 static OtpWinList *GetOwlAtOrBelowInWinbox(OtpWinList **owlp, WindowBox *wb)
381 {
382 OtpWinList *owl = *owlp;
383
384 while(owl != NULL && owl->twm_win->winbox != wb) {
385 owl = owl->below;
386 }
387
388 if(owl == NULL) {
389 /* we have gone below the box: put it just on top of it */
390 *owlp = wb->twmwin->otp;
391 }
392 else {
393 *owlp = owl;
394 }
395 return owl;
396 }
397
398
InsertOwlAbove(OtpWinList * owl,OtpWinList * other_owl)399 static void InsertOwlAbove(OtpWinList *owl, OtpWinList *other_owl)
400 {
401 #if DEBUG_OTP
402 fprintf(stderr, "InsertOwlAbove owl->pri=%d w=0x%x parent_vs:(x,y)=(%d,%d)",
403 PRI(owl),
404 (unsigned int)WindowOfOwl(owl),
405 owl->twm_win->parent_vs->x,
406 owl->twm_win->parent_vs->y);
407 if(other_owl != NULL) {
408 fprintf(stderr, "\n other_owl->pri=%d w=0x%x parent_vs:(x,y)=(%d,%d)",
409 PRI(other_owl),
410 (unsigned int)WindowOfOwl(other_owl),
411 owl->twm_win->parent_vs->x,
412 owl->twm_win->parent_vs->y);
413 }
414 fprintf(stderr, "\n");
415 #endif
416
417 assert(owl->above == NULL);
418 assert(owl->below == NULL);
419
420
421 if(other_owl == NULL) {
422 DPRINTF((stderr, "Bottom-most window overall\n"));
423 /* special case for the lowest window overall */
424 assert(PRI(owl) <= PRI(Scr->bottomOwl));
425
426 /* pass the action to the Xserver */
427 XLowerWindow(dpy, WindowOfOwl(owl));
428
429 /* update the list */
430 owl->above = Scr->bottomOwl;
431 owl->above->below = owl;
432 Scr->bottomOwl = owl;
433 }
434 else {
435 WindowBox *winbox = owl->twm_win->winbox;
436 OtpWinList *vs_owl;
437
438 if(winbox != NULL) {
439 vs_owl = GetOwlAtOrBelowInWinbox(&other_owl, winbox);
440 }
441 else {
442
443 vs_owl = GetOwlAtOrBelowInVS(other_owl, owl->twm_win->parent_vs);
444 }
445
446 assert(PRI(owl) >= PRI(other_owl));
447 if(other_owl->above != NULL) {
448 assert(PRI(owl) <= PRI(other_owl->above));
449 }
450
451 if(vs_owl == NULL) {
452 DPRINTF((stderr, "Bottom-most window in VirtualScreen or window box\n"));
453 /* special case for the lowest window in this virtual screen or window box */
454
455 /* pass the action to the Xserver */
456 XLowerWindow(dpy, WindowOfOwl(owl));
457 }
458 else {
459 XWindowChanges xwc;
460 int xwcm;
461
462 DPRINTF((stderr, "General case\n"));
463 /* general case */
464 assert(PRI(vs_owl) <= PRI(other_owl));
465 assert(owl->twm_win->parent_vs == vs_owl->twm_win->parent_vs);
466
467 /* pass the action to the Xserver */
468 xwcm = CWStackMode | CWSibling;
469 xwc.sibling = WindowOfOwl(vs_owl);
470 xwc.stack_mode = Above;
471 XConfigureWindow(dpy, WindowOfOwl(owl), xwcm, &xwc);
472 }
473
474 /* update the list */
475 owl->below = other_owl;
476 owl->above = other_owl->above;
477 owl->below->above = owl;
478 if(owl->above != NULL) {
479 owl->above->below = owl;
480 }
481 }
482 }
483
484
485 /* should owl stay above other_owl if other_owl was raised ? */
shouldStayAbove(OtpWinList * owl,OtpWinList * other_owl)486 static bool shouldStayAbove(OtpWinList *owl, OtpWinList *other_owl)
487 {
488 return ((owl->type == WinWin)
489 && (other_owl->type == WinWin)
490 && isSmallTransientOf(owl->twm_win, other_owl->twm_win));
491 }
492
493
RaiseSmallTransientsOfAbove(OtpWinList * owl,OtpWinList * other_owl)494 static void RaiseSmallTransientsOfAbove(OtpWinList *owl, OtpWinList *other_owl)
495 {
496 OtpWinList *trans_owl, *tmp_owl;
497
498 /* the icons have no transients and we can't have windows below NULL */
499 if((owl->type != WinWin) || other_owl == NULL) {
500 return;
501 }
502
503 /* beware: we modify the list as we scan it. This is the reason for tmp */
504 for(trans_owl = other_owl->below; trans_owl != NULL; trans_owl = tmp_owl) {
505 tmp_owl = trans_owl->below;
506 if(shouldStayAbove(trans_owl, owl)) {
507 RemoveOwl(trans_owl);
508 PRI_CP(owl, trans_owl);
509 InsertOwlAbove(trans_owl, other_owl);
510 }
511 }
512 }
513
514
OwlRightBelow(int priority)515 static OtpWinList *OwlRightBelow(int priority)
516 {
517 OtpWinList *owl1, *owl2;
518
519 /* in case there isn't anything below */
520 if(priority <= PRI(Scr->bottomOwl)) {
521 return NULL;
522 }
523
524 for(owl1 = Scr->bottomOwl, owl2 = owl1->above;
525 (owl2 != NULL) && (PRI(owl2) < priority);
526 owl1 = owl2, owl2 = owl2->above) {
527 /* nada */;
528 }
529
530 assert(owl2 == owl1->above);
531 assert(PRI(owl1) < priority);
532 assert((owl2 == NULL) || (PRI(owl2) >= priority));
533
534
535 return owl1;
536 }
537
InsertOwl(OtpWinList * owl,int where)538 static void InsertOwl(OtpWinList *owl, int where)
539 {
540 OtpWinList *other_owl;
541 int priority;
542
543 DPRINTF((stderr, "InsertOwl %s\n",
544 (where == Above) ? "Above" :
545 (where == Below) ? "Below" :
546 "???"));
547 assert(owl->above == NULL);
548 assert(owl->below == NULL);
549 assert((where == Above) || (where == Below));
550
551 priority = PRI(owl) - (where == Above ? 0 : 1);
552
553 if(Scr->bottomOwl == NULL) {
554 /* for the first window: just insert it in the list */
555 Scr->bottomOwl = owl;
556 }
557 else {
558 other_owl = OwlRightBelow(priority + 1);
559
560 /* make sure other_owl is not one of the transients */
561 while((other_owl != NULL)
562 && shouldStayAbove(other_owl, owl)) {
563 PRI_CP(owl, other_owl);
564
565 other_owl = other_owl->below;
566 }
567
568 /* raise the transient windows that should stay on top */
569 RaiseSmallTransientsOfAbove(owl, other_owl);
570
571 /* now go ahead and put the window where it should go */
572 InsertOwlAbove(owl, other_owl);
573 }
574 }
575
576
SetOwlPriority(OtpWinList * owl,int new_pri,int where)577 static void SetOwlPriority(OtpWinList *owl, int new_pri, int where)
578 {
579 DPRINTF((stderr, "SetOwlPriority(%d)\n", new_pri));
580
581 /* make sure the values are within bounds */
582 if(new_pri < 0) {
583 new_pri = 0;
584 }
585 if(new_pri > OTP_MAX) {
586 new_pri = OTP_MAX;
587 }
588
589 RemoveOwl(owl);
590 owl->pri_base = new_pri;
591 InsertOwl(owl, where);
592
593 assert(owl->pri_base == new_pri);
594 }
595
596
597 /*
598 * Shift transients of a window to a new [base] priority, preparatory to
599 * moving that window itself there.
600 */
TryToMoveTransientsOfTo(OtpWinList * owl,int priority,int where)601 static void TryToMoveTransientsOfTo(OtpWinList *owl, int priority, int where)
602 {
603 OtpWinList *other_owl;
604
605 /* the icons have no transients */
606 if(owl->type != WinWin) {
607 return;
608 }
609
610 /*
611 * We start looking for transients of owl at the bottom of its OTP
612 * layer.
613 */
614 other_owl = OwlRightBelow(PRI(owl));
615 other_owl = (other_owl == NULL) ? Scr->bottomOwl : other_owl->above;
616 assert(PRI(other_owl) >= PRI(owl));
617
618 /* !beware! we're changing the list as we scan it, hence the tmp_owl */
619 while((other_owl != NULL) && (PRI(other_owl) == PRI(owl))) {
620 OtpWinList *tmp_owl = other_owl->above;
621 if((other_owl->type == WinWin)
622 && isTransientOf(other_owl->twm_win, owl->twm_win)) {
623 /* Copy in our flags so it winds up in the right place */
624 other_owl->pri_aflags = owl->pri_aflags;
625 SetOwlPriority(other_owl, priority, where);
626 }
627 other_owl = tmp_owl;
628 }
629 }
630
TryToSwitch(OtpWinList * owl,int where)631 static void TryToSwitch(OtpWinList *owl, int where)
632 {
633 int priority;
634
635 if(!owl->switching) {
636 return;
637 }
638
639 /*
640 * Switching is purely an adjustment to the base priority, so we
641 * don't need to figure stuff based on the effective.
642 */
643 priority = OTP_MAX - owl->pri_base;
644 if(((where == Above) && (priority > owl->pri_base)) ||
645 ((where == Below) && (priority < owl->pri_base))) {
646 /*
647 * TTMTOT() before changing pri_base since it uses the current
648 * state to find the transients.
649 */
650 TryToMoveTransientsOfTo(owl, priority, where);
651 owl->pri_base = priority;
652 }
653 }
654
RaiseOwl(OtpWinList * owl)655 static void RaiseOwl(OtpWinList *owl)
656 {
657 TryToSwitch(owl, Above);
658 RemoveOwl(owl);
659 InsertOwl(owl, Above);
660 }
661
662
LowerOwl(OtpWinList * owl)663 static void LowerOwl(OtpWinList *owl)
664 {
665 TryToSwitch(owl, Below);
666 RemoveOwl(owl);
667 InsertOwl(owl, Below);
668 }
669
isHiddenBy(OtpWinList * owl,OtpWinList * other_owl)670 static bool isHiddenBy(OtpWinList *owl, OtpWinList *other_owl)
671 {
672 /* doesn't check that owl is on screen */
673 return (isOnScreen(other_owl)
674 && isIntersectingWith(owl, other_owl));
675 }
676
TinyRaiseOwl(OtpWinList * owl)677 static void TinyRaiseOwl(OtpWinList *owl)
678 {
679 OtpWinList *other_owl = owl->above;
680
681 while((other_owl != NULL) && (PRI(other_owl) == PRI(owl))) {
682 if(isHiddenBy(owl, other_owl)
683 && !shouldStayAbove(other_owl, owl)) {
684 RemoveOwl(owl);
685 RaiseSmallTransientsOfAbove(owl, other_owl);
686 InsertOwlAbove(owl, other_owl);
687 return;
688 }
689 else {
690 other_owl = other_owl->above;
691 }
692 }
693 }
694
TinyLowerOwl(OtpWinList * owl)695 static void TinyLowerOwl(OtpWinList *owl)
696 {
697 OtpWinList *other_owl = owl->below;
698
699 while((other_owl != NULL) && (PRI(other_owl) == PRI(owl))) {
700 if(isHiddenBy(owl, other_owl)) {
701 RemoveOwl(owl);
702 InsertOwlAbove(owl, other_owl->below);
703 return;
704 }
705 else {
706 other_owl = other_owl->below;
707 }
708 }
709 }
710
RaiseLowerOwl(OtpWinList * owl)711 static void RaiseLowerOwl(OtpWinList *owl)
712 {
713 OtpWinList *other_owl;
714 int priority;
715
716 /*
717 * abs(effective pri)
718 *
719 * XXX Why? This seems like it's encoding the assumption
720 * "f.raiselower should assume any negative [user-level] priorities
721 * are a result of a window that should be positive being switched,
722 * and we should switch it positive before raising if we need to", or
723 * some such.
724 */
725 priority = MAX(PRI(owl), OTP_MAX - PRI(owl));
726
727 for(other_owl = owl->above;
728 (other_owl != NULL) && (PRI(other_owl) <= priority);
729 other_owl = other_owl->above) {
730 if(isHiddenBy(owl, other_owl)
731 && !shouldStayAbove(other_owl, owl)) {
732 RaiseOwl(owl);
733 return;
734 }
735 }
736 LowerOwl(owl);
737 }
738
739
OtpRaise(TwmWindow * twm_win,WinType wintype)740 void OtpRaise(TwmWindow *twm_win, WinType wintype)
741 {
742 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
743 assert(owl != NULL);
744
745 RaiseOwl(owl);
746
747 OtpCheckConsistency();
748 #ifdef EWMH
749 EwmhSet_NET_CLIENT_LIST_STACKING();
750 #endif /* EWMH */
751 }
752
753
OtpLower(TwmWindow * twm_win,WinType wintype)754 void OtpLower(TwmWindow *twm_win, WinType wintype)
755 {
756 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
757 assert(owl != NULL);
758
759 LowerOwl(owl);
760
761 OtpCheckConsistency();
762 #ifdef EWMH
763 EwmhSet_NET_CLIENT_LIST_STACKING();
764 #endif /* EWMH */
765 }
766
767
OtpRaiseLower(TwmWindow * twm_win,WinType wintype)768 void OtpRaiseLower(TwmWindow *twm_win, WinType wintype)
769 {
770 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
771 assert(owl != NULL);
772
773 RaiseLowerOwl(owl);
774
775 OtpCheckConsistency();
776 #ifdef EWMH
777 EwmhSet_NET_CLIENT_LIST_STACKING();
778 #endif /* EWMH */
779 }
780
781
OtpTinyRaise(TwmWindow * twm_win,WinType wintype)782 void OtpTinyRaise(TwmWindow *twm_win, WinType wintype)
783 {
784 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
785 assert(owl != NULL);
786
787 TinyRaiseOwl(owl);
788
789 OtpCheckConsistency();
790 #ifdef EWMH
791 EwmhSet_NET_CLIENT_LIST_STACKING();
792 #endif /* EWMH */
793 }
794
795
OtpTinyLower(TwmWindow * twm_win,WinType wintype)796 void OtpTinyLower(TwmWindow *twm_win, WinType wintype)
797 {
798 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
799 assert(owl != NULL);
800
801 TinyLowerOwl(owl);
802
803 OtpCheckConsistency();
804 #ifdef EWMH
805 EwmhSet_NET_CLIENT_LIST_STACKING();
806 #endif /* EWMH */
807 }
808
809
810 /*
811 * XCirculateSubwindows() is complicated by the fact that it restacks only
812 * in case of overlapping windows. Therefore it seems easier to not
813 * try to emulate that but to leave it to the X server.
814 *
815 * If XCirculateSubwindows() actually does something, it sends a
816 * CirculateNotify event, but you only receive it if
817 * SubstructureNotifyMask is selected on the root window.
818 * However... if that is done from the beginning, for some reason all
819 * windows disappear when ctwm starts or exits.
820 * Maybe SubstructureNotifyMask interferes with SubstructureRedirectMask?
821 *
822 * To get around that, the SubstructureNotifyMask is selected only
823 * temporarily here when wanted.
824 */
825
OtpCirculateSubwindows(VirtualScreen * vs,int direction)826 void OtpCirculateSubwindows(VirtualScreen *vs, int direction)
827 {
828 Window w = vs->window;
829 XWindowAttributes winattrs;
830 Bool circulated;
831
832 DPRINTF((stderr, "OtpCirculateSubwindows %d\n", direction));
833
834 XGetWindowAttributes(dpy, w, &winattrs);
835 XSelectInput(dpy, w, winattrs.your_event_mask | SubstructureNotifyMask);
836 XCirculateSubwindows(dpy, w, direction);
837 XSelectInput(dpy, w, winattrs.your_event_mask);
838 /*
839 * Now we should get the CirculateNotify event.
840 * It usually seems to arrive soon enough, but just to be sure, look
841 * ahead in the message queue to see if it can be expedited.
842 */
843 circulated = XCheckTypedWindowEvent(dpy, w, CirculateNotify, &Event);
844 if(circulated) {
845 HandleCirculateNotify();
846 }
847 }
848
849 /*
850 * Update our list of Owls after the Circulate action, and also
851 * enforce the priority by possibly restacking the window again.
852 */
853
OtpHandleCirculateNotify(VirtualScreen * vs,TwmWindow * twm_win,WinType wintype,int place)854 void OtpHandleCirculateNotify(VirtualScreen *vs, TwmWindow *twm_win,
855 WinType wintype, int place)
856 {
857 switch(place) {
858 case PlaceOnTop:
859 OtpRaise(twm_win, wintype);
860 break;
861 case PlaceOnBottom:
862 OtpLower(twm_win, wintype);
863 break;
864 default:
865 DPRINTF((stderr, "OtpHandleCirculateNotify: place=%d\n", place));
866 assert(0 &&
867 "OtpHandleCirculateNotify: place equals PlaceOnTop nor PlaceOnBottom");
868 }
869 }
870
OtpSetPriority(TwmWindow * twm_win,WinType wintype,int new_pri,int where)871 void OtpSetPriority(TwmWindow *twm_win, WinType wintype, int new_pri, int where)
872 {
873 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
874 int priority = OTP_ZERO + new_pri;
875
876 DPRINTF((stderr, "OtpSetPriority: new_pri=%d\n", new_pri));
877 assert(owl != NULL);
878
879 if(twm_win->winbox != NULL || twm_win->iswinbox) {
880 return;
881 }
882
883 if(ABS(new_pri) > OTP_ZERO) {
884 DPRINTF((stderr, "invalid OnTopPriority value: %d\n", new_pri));
885 }
886 else {
887 TryToMoveTransientsOfTo(owl, priority, where);
888 SetOwlPriority(owl, priority, where);
889 }
890
891 OtpCheckConsistency();
892 }
893
894
OtpChangePriority(TwmWindow * twm_win,WinType wintype,int relpriority)895 void OtpChangePriority(TwmWindow *twm_win, WinType wintype, int relpriority)
896 {
897 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
898 int priority = owl->pri_base + relpriority;
899 int where;
900
901 if(twm_win->winbox != NULL || twm_win->iswinbox) {
902 return;
903 }
904
905 where = relpriority < 0 ? Below : Above;
906
907 TryToMoveTransientsOfTo(owl, priority, where);
908 SetOwlPriority(owl, priority, where);
909
910 OtpCheckConsistency();
911 }
912
913
OtpSwitchPriority(TwmWindow * twm_win,WinType wintype)914 void OtpSwitchPriority(TwmWindow *twm_win, WinType wintype)
915 {
916 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
917 int priority = OTP_MAX - owl->pri_base;
918 int where;
919
920 assert(owl != NULL);
921
922 if(twm_win->winbox != NULL || twm_win->iswinbox) {
923 return;
924 }
925
926 where = priority < OTP_ZERO ? Below : Above;
927 TryToMoveTransientsOfTo(owl, priority, where);
928 SetOwlPriority(owl, priority, where);
929
930 OtpCheckConsistency();
931 }
932
933
OtpToggleSwitching(TwmWindow * twm_win,WinType wintype)934 void OtpToggleSwitching(TwmWindow *twm_win, WinType wintype)
935 {
936 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
937 assert(owl != NULL);
938
939 if(twm_win->winbox != NULL || twm_win->iswinbox) {
940 return;
941 }
942
943 owl->switching = !owl->switching;
944
945 OtpCheckConsistency();
946 }
947
948
949 /*
950 * This is triggered as a result of a StackMode ConfigureRequest. We
951 * choose to interpret this as restacking relative to the base
952 * priorities, since all the alterations are EWMH-related, and those
953 * should probably override.
954 *
955 * XXX Or should they? Maybe we should alter until our effective is
956 * positioned as desired relative to their effective? This may also need
957 * revisiting if we grow alterations that aren't a result of EWMH stuff.
958 */
OtpForcePlacement(TwmWindow * twm_win,int where,TwmWindow * other_win)959 void OtpForcePlacement(TwmWindow *twm_win, int where, TwmWindow *other_win)
960 {
961 OtpWinList *owl = twm_win->otp;
962 OtpWinList *other_owl = other_win->otp;
963
964 assert(twm_win->otp != NULL);
965 assert(other_win->otp != NULL);
966
967 if(where == BottomIf) {
968 where = Below;
969 }
970 if(where != Below) {
971 where = Above;
972 }
973
974 /* remove the owl to change it */
975 RemoveOwl(owl);
976
977 /*
978 * Base our priority base off that other win. Don't use PRI_CP since
979 * we shouldn't suddenly get its flags as well.
980 */
981 owl->pri_base = other_owl->pri_base;
982
983 /* put the owl back into the list */
984 if(where == Below) {
985 other_owl = other_owl->below;
986 }
987 InsertOwlAbove(owl, other_owl);
988
989 OtpCheckConsistency();
990 }
991
992
ApplyPreferences(OtpPreferences * prefs,OtpWinList * owl)993 static void ApplyPreferences(OtpPreferences *prefs, OtpWinList *owl)
994 {
995 int i;
996 TwmWindow *twm_win = owl->twm_win;
997
998 /* check PrioritySwitch */
999 if(LookInList(prefs->switchingL, twm_win->name, &twm_win->class)) {
1000 owl->switching = !prefs->switching;
1001 }
1002
1003 /* check OnTopPriority */
1004 for(i = 0; i <= OTP_MAX; i++) {
1005 if(LookInList(prefs->priorityL[i],
1006 twm_win->name, &twm_win->class)) {
1007 owl->pri_base = i;
1008 }
1009 }
1010 }
1011
1012
1013 /*
1014 * Reset stuff based on preferences; called during property changes if
1015 * AutoPriority set.
1016 */
RecomputeOwlPrefs(OtpPreferences * prefs,OtpWinList * owl)1017 static void RecomputeOwlPrefs(OtpPreferences *prefs, OtpWinList *owl)
1018 {
1019 int old_pri;
1020
1021 old_pri = owl->pri_base;
1022 ApplyPreferences(prefs, owl);
1023 if(old_pri != owl->pri_base) {
1024 RemoveOwl(owl);
1025 InsertOwl(owl, Above);
1026
1027 /*
1028 * Stash flags if we don't have any yet, since we just changed
1029 * the priority.
1030 */
1031 if(!owl->stashed_aflags) {
1032 OwlStashAflags(owl);
1033 }
1034
1035 #ifdef EWMH
1036 /* Let other things know we did something with stacking */
1037 EwmhSet_NET_WM_STATE(owl->twm_win, EWMH_STATE_ABOVE);
1038 #endif
1039 }
1040 }
1041
OtpRecomputePrefs(TwmWindow * twm_win)1042 void OtpRecomputePrefs(TwmWindow *twm_win)
1043 {
1044 assert(twm_win->otp != NULL);
1045
1046 RecomputeOwlPrefs(Scr->OTP, twm_win->otp);
1047 if(twm_win->icon != NULL) {
1048 RecomputeOwlPrefs(Scr->IconOTP, twm_win->icon->otp);
1049 }
1050
1051 OtpCheckConsistency();
1052 }
1053
1054
free_OtpWinList(OtpWinList * owl)1055 static void free_OtpWinList(OtpWinList *owl)
1056 {
1057 assert(owl->above == NULL);
1058 assert(owl->below == NULL);
1059 free(owl);
1060 }
1061
1062
OtpRemove(TwmWindow * twm_win,WinType wintype)1063 void OtpRemove(TwmWindow *twm_win, WinType wintype)
1064 {
1065 OtpWinList **owlp;
1066 owlp = (wintype == IconWin) ? &twm_win->icon->otp : &twm_win->otp;
1067
1068 assert(*owlp != NULL);
1069
1070 RemoveOwl(*owlp);
1071 free_OtpWinList(*owlp);
1072 *owlp = NULL;
1073
1074 OtpCheckConsistency();
1075 }
1076
1077
new_OtpWinList(TwmWindow * twm_win,WinType wintype,bool switching,int priority)1078 static OtpWinList *new_OtpWinList(TwmWindow *twm_win,
1079 WinType wintype,
1080 bool switching,
1081 int priority)
1082 {
1083 OtpWinList *owl = malloc(sizeof(OtpWinList));
1084
1085 owl->above = NULL;
1086 owl->below = NULL;
1087 owl->twm_win = twm_win;
1088 owl->type = wintype;
1089 owl->switching = switching;
1090 owl->pri_base = priority;
1091 owl->pri_aflags = 0;
1092
1093 /*
1094 * We never need to stash anything for icons, they don't persist
1095 * across restart anyway. So pretend we did stash already to
1096 * discourage other code from trying to stash.
1097 */
1098 owl->stashed_aflags = (wintype == WinWin ? false : true);
1099
1100 return owl;
1101 }
1102
AddNewOwl(TwmWindow * twm_win,WinType wintype,OtpWinList * parent)1103 static OtpWinList *AddNewOwl(TwmWindow *twm_win, WinType wintype,
1104 OtpWinList *parent)
1105 {
1106 OtpWinList *owl;
1107 OtpPreferences *prefs = (wintype == IconWin) ? Scr->IconOTP : Scr->OTP;
1108
1109 /* make the new owl */
1110 owl = new_OtpWinList(twm_win, wintype,
1111 prefs->switching, prefs->priority);
1112
1113 /* inherit the default attributes from the parent window if appropriate */
1114 if(parent != NULL) {
1115 PRI_CP(parent, owl);
1116 owl->switching = parent->switching;
1117 }
1118
1119 /* now see if the preferences have something to say */
1120 if(!(parent != NULL && twm_win->istransient)) {
1121 ApplyPreferences(prefs, owl);
1122 }
1123
1124 #ifdef EWMH
1125 /* If nothing came in, EWMH might have something to say */
1126 if(owl->pri_base == 0) {
1127 owl->pri_base = EwmhGetInitPriority(twm_win) + OTP_ZERO;
1128 }
1129 #endif
1130
1131 /*
1132 * Initialize flags. Right now, the only stashed flags are related
1133 * to EWMH requests, so in a sense this whole thing could be dropped
1134 * under #ifdef. But I'll assume that might not always be the case,
1135 * so for now the !(EWMH) case is just a twisty noop.
1136 */
1137 {
1138 bool gotflags = false;
1139 unsigned aflags = 0, fromstash = 0;
1140
1141 aflags = OwlGetStashedAflags(owl, &gotflags);
1142
1143 #ifdef EWMH
1144 fromstash = (OTP_AFLAG_ABOVE | OTP_AFLAG_BELOW);
1145 #endif
1146
1147 if(gotflags) {
1148 /*
1149 * Got stashed OTP flags; use 'em. Explicitly mask in only
1150 * the flags we're caring about; the others aren't telling us
1151 * info we need to persist.
1152 */
1153 aflags &= fromstash;
1154 }
1155
1156 #ifdef EWMH
1157 /* FULLSCREEN we get from the normal EWMH prop no matter what */
1158 if(twm_win->ewmhFlags & EWMH_STATE_FULLSCREEN) {
1159 aflags |= OTP_AFLAG_FULLSCREEN;
1160 }
1161
1162 if(!gotflags) {
1163 /* Nothing from OTP about above/below; check EWMH */
1164 aflags = 0;
1165 if(twm_win->ewmhFlags & EWMH_STATE_ABOVE) {
1166 aflags |= OTP_AFLAG_ABOVE;
1167 }
1168 if(twm_win->ewmhFlags & EWMH_STATE_BELOW) {
1169 aflags |= OTP_AFLAG_BELOW;
1170 }
1171 }
1172 #endif // EWMH
1173
1174 /* Set whatever we figured */
1175 owl->pri_aflags |= aflags;
1176 owl->stashed_aflags = gotflags;
1177
1178 /* If we set a priority or flags, we should stash away flags */
1179 if((PRI(owl) != OTP_ZERO || owl->pri_aflags != 0)
1180 && !owl->stashed_aflags) {
1181 OwlStashAflags(owl);
1182 }
1183 }
1184
1185 /* finally put the window where it should go */
1186 InsertOwl(owl, Above);
1187
1188 return owl;
1189 }
1190
OtpAdd(TwmWindow * twm_win,WinType wintype)1191 void OtpAdd(TwmWindow *twm_win, WinType wintype)
1192 {
1193 TwmWindow *other_win;
1194 OtpWinList *parent = NULL;
1195 OtpWinList **owlp;
1196 owlp = (wintype == IconWin) ? &twm_win->icon->otp : &twm_win->otp;
1197
1198 assert(*owlp == NULL);
1199
1200 /* windows in boxes *must* inherit priority from the box */
1201 if(twm_win->winbox) {
1202 parent = twm_win->winbox->twmwin->otp;
1203 parent->switching = false;
1204 }
1205 /* in case it's a transient, find the parent */
1206 else if(wintype == WinWin && (twm_win->istransient
1207 || !isGroupLeader(twm_win))) {
1208 other_win = Scr->FirstWindow;
1209 while(other_win != NULL
1210 && !isTransientOf(twm_win, other_win)
1211 && !isGroupLeaderOf(other_win, twm_win)) {
1212 other_win = other_win->next;
1213 }
1214 if(other_win != NULL) {
1215 parent = other_win->otp;
1216 }
1217 }
1218
1219 /* make the new owl */
1220 *owlp = AddNewOwl(twm_win, wintype, parent);
1221
1222 assert(*owlp != NULL);
1223 OtpCheckConsistency();
1224 }
1225
OtpReassignIcon(TwmWindow * twm_win,Icon * old_icon)1226 void OtpReassignIcon(TwmWindow *twm_win, Icon *old_icon)
1227 {
1228 if(old_icon != NULL) {
1229 /* Transfer OWL to new Icon */
1230 Icon *new_icon = twm_win->icon;
1231 if(new_icon != NULL) {
1232 new_icon->otp = old_icon->otp;
1233 old_icon->otp = NULL;
1234 }
1235 }
1236 else {
1237 /* Create a new OWL for this Icon */
1238 OtpAdd(twm_win, IconWin);
1239 }
1240 }
1241
OtpFreeIcon(TwmWindow * twm_win)1242 void OtpFreeIcon(TwmWindow *twm_win)
1243 {
1244 /* Remove the icon's OWL, if any */
1245 Icon *cur_icon = twm_win->icon;
1246 if(cur_icon != NULL) {
1247 OtpRemove(twm_win, IconWin);
1248 }
1249 }
1250
OtpScrSwitchingL(ScreenInfo * scr,WinType wintype)1251 name_list **OtpScrSwitchingL(ScreenInfo *scr, WinType wintype)
1252 {
1253 OtpPreferences *prefs = (wintype == IconWin) ? scr->IconOTP : scr->OTP;
1254
1255 assert(prefs != NULL);
1256
1257 return &(prefs->switchingL);
1258 }
1259
1260
OtpScrSetSwitching(ScreenInfo * scr,WinType wintype,bool switching)1261 void OtpScrSetSwitching(ScreenInfo *scr, WinType wintype, bool switching)
1262 {
1263 #ifndef NDEBUG
1264 OtpPreferences *prefs = (wintype == IconWin) ? scr->IconOTP : scr->OTP;
1265
1266 assert(prefs != NULL);
1267 #endif
1268
1269 scr->OTP->switching = switching;
1270 }
1271
1272
OtpScrSetZero(ScreenInfo * scr,WinType wintype,int priority)1273 void OtpScrSetZero(ScreenInfo *scr, WinType wintype, int priority)
1274 {
1275 OtpPreferences *prefs = (wintype == IconWin) ? scr->IconOTP : scr->OTP;
1276
1277 assert(prefs != NULL);
1278
1279 if(ABS(priority) > OTP_ZERO) {
1280 fprintf(stderr, "invalid OnTopPriority value: %d\n", priority);
1281 return;
1282 }
1283
1284 prefs->priority = priority + OTP_ZERO;
1285 }
1286
1287
OtpScrPriorityL(ScreenInfo * scr,WinType wintype,int priority)1288 name_list **OtpScrPriorityL(ScreenInfo *scr, WinType wintype, int priority)
1289 {
1290 OtpPreferences *prefs = (wintype == IconWin) ? scr->IconOTP : scr->OTP;
1291
1292 assert(prefs != NULL);
1293
1294 if(ABS(priority) > OTP_ZERO) {
1295 fprintf(stderr, "invalid OnTopPriority value: %d\n", priority);
1296 priority = 0;
1297 }
1298 return &(prefs->priorityL[priority + OTP_ZERO]);
1299 }
1300
1301
new_OtpPreferences(void)1302 static OtpPreferences *new_OtpPreferences(void)
1303 {
1304 OtpPreferences *pref = malloc(sizeof(OtpPreferences));
1305 int i;
1306
1307 /* initialize default values */
1308 for(i = 0; i <= OTP_MAX; i++) {
1309 pref->priorityL[i] = NULL;
1310 }
1311 pref->priority = OTP_ZERO;
1312 pref->switchingL = NULL;
1313 pref->switching = false;
1314
1315 return pref;
1316 }
1317
free_OtpPreferences(OtpPreferences * pref)1318 static void free_OtpPreferences(OtpPreferences *pref)
1319 {
1320 int i;
1321
1322 FreeList(&pref->switchingL);
1323 for(i = 0; i <= OTP_MAX; i++) {
1324 FreeList(&pref->priorityL[i]);
1325 }
1326
1327 free(pref);
1328 }
1329
OtpScrInitData(ScreenInfo * scr)1330 void OtpScrInitData(ScreenInfo *scr)
1331 {
1332 if(scr->OTP != NULL) {
1333 free_OtpPreferences(scr->OTP);
1334 }
1335 if(scr->IconOTP != NULL) {
1336 free_OtpPreferences(scr->IconOTP);
1337 }
1338 scr->OTP = new_OtpPreferences();
1339 scr->IconOTP = new_OtpPreferences();
1340 }
1341
ReparentWindow(Display * display,TwmWindow * twm_win,WinType wintype,Window parent,int x,int y)1342 int ReparentWindow(Display *display, TwmWindow *twm_win, WinType wintype,
1343 Window parent, int x, int y)
1344 {
1345 int result;
1346 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
1347 OtpWinList *other = owl->below;
1348 assert(owl != NULL);
1349
1350 DPRINTF((stderr, "ReparentWindow: w=%x type=%d\n",
1351 (unsigned int)WindowOfOwl(owl), wintype));
1352 result = XReparentWindow(display, WindowOfOwl(owl), parent, x, y);
1353 /* The raise was already done by XReparentWindow, so this call
1354 just re-places the window at the right spot in the list
1355 and enforces priority settings. */
1356 RemoveOwl(owl);
1357 InsertOwlAbove(owl, other);
1358 OtpCheckConsistency();
1359 return result;
1360 }
1361
1362 void
ReparentWindowAndIcon(Display * display,TwmWindow * twm_win,Window parent,int win_x,int win_y,int icon_x,int icon_y)1363 ReparentWindowAndIcon(Display *display, TwmWindow *twm_win,
1364 Window parent, int win_x, int win_y,
1365 int icon_x, int icon_y)
1366 {
1367 OtpWinList *win_owl = twm_win->otp;
1368 assert(twm_win->icon != NULL);
1369 OtpWinList *icon_owl = twm_win->icon->otp;
1370 assert(win_owl != NULL);
1371 assert(icon_owl != NULL);
1372 OtpWinList *below_win = win_owl->below;
1373 OtpWinList *below_icon = icon_owl->below;
1374
1375 DPRINTF((stderr, "ReparentWindowAndIcon %x\n", (unsigned int)twm_win->frame));
1376 XReparentWindow(display, twm_win->frame, parent, win_x, win_y);
1377 XReparentWindow(display, twm_win->icon->w, parent, icon_x, icon_y);
1378 /* The raise was already done by XReparentWindow, so this call
1379 just re-places the window at the right spot in the list
1380 and enforces priority settings. */
1381 RemoveOwl(win_owl);
1382 RemoveOwl(icon_owl);
1383 if(below_win != icon_owl) {
1384 /*
1385 * Only insert the window above something if it isn't the icon,
1386 * because that isn't back yet.
1387 */
1388 InsertOwlAbove(win_owl, below_win);
1389 InsertOwlAbove(icon_owl, below_icon);
1390 }
1391 else {
1392 /* In such a case, do it in the opposite order. */
1393 InsertOwlAbove(icon_owl, below_icon);
1394 InsertOwlAbove(win_owl, below_win);
1395 }
1396 OtpCheckConsistency();
1397 return;
1398 }
1399
1400 /* Iterators. */
OtpBottomWin()1401 TwmWindow *OtpBottomWin()
1402 {
1403 OtpWinList *owl = Scr->bottomOwl;
1404 while(owl && owl->type != WinWin) {
1405 owl = owl->above;
1406 }
1407 return owl ? owl->twm_win : NULL;
1408 }
1409
OtpTopWin()1410 TwmWindow *OtpTopWin()
1411 {
1412 OtpWinList *owl = Scr->bottomOwl, *top = NULL;
1413 while(owl) {
1414 if(owl->type == WinWin) {
1415 top = owl;
1416 }
1417 owl = owl->above;
1418 }
1419 return top ? top->twm_win : NULL;
1420 }
1421
OtpNextWinUp(TwmWindow * twm_win)1422 TwmWindow *OtpNextWinUp(TwmWindow *twm_win)
1423 {
1424 OtpWinList *owl = twm_win->otp->above;
1425 while(owl && owl->type != WinWin) {
1426 owl = owl->above;
1427 }
1428 return owl ? owl->twm_win : NULL;
1429 }
1430
OtpNextWinDown(TwmWindow * twm_win)1431 TwmWindow *OtpNextWinDown(TwmWindow *twm_win)
1432 {
1433 OtpWinList *owl = twm_win->otp->below;
1434 while(owl && owl->type != WinWin) {
1435 owl = owl->below;
1436 }
1437 return owl ? owl->twm_win : NULL;
1438 }
1439
1440
1441 /*
1442 * Stuff for messing with pri_aflags
1443 */
1444 /* Set the masked bits to exactly what's given */
1445 void
OtpSetAflagMask(TwmWindow * twm_win,unsigned mask,unsigned setto)1446 OtpSetAflagMask(TwmWindow *twm_win, unsigned mask, unsigned setto)
1447 {
1448 assert(twm_win != NULL);
1449 assert(twm_win->otp != NULL);
1450 OwlSetAflagMask(twm_win->otp, mask, setto);
1451 }
1452
1453 static void
OwlSetAflagMask(OtpWinList * owl,unsigned mask,unsigned setto)1454 OwlSetAflagMask(OtpWinList *owl, unsigned mask, unsigned setto)
1455 {
1456 assert(owl != NULL);
1457
1458 owl->pri_aflags &= ~mask;
1459 owl->pri_aflags |= (setto & mask);
1460 OwlStashAflags(owl);
1461 }
1462
1463 /* Set/clear individual ones */
1464 void
OtpSetAflag(TwmWindow * twm_win,unsigned flag)1465 OtpSetAflag(TwmWindow *twm_win, unsigned flag)
1466 {
1467 assert(twm_win != NULL);
1468 assert(twm_win->otp != NULL);
1469
1470 OwlSetAflag(twm_win->otp, flag);
1471 }
1472
1473 static void
OwlSetAflag(OtpWinList * owl,unsigned flag)1474 OwlSetAflag(OtpWinList *owl, unsigned flag)
1475 {
1476 assert(owl != NULL);
1477
1478 owl->pri_aflags |= flag;
1479 OwlStashAflags(owl);
1480 }
1481
1482 void
OtpClearAflag(TwmWindow * twm_win,unsigned flag)1483 OtpClearAflag(TwmWindow *twm_win, unsigned flag)
1484 {
1485 assert(twm_win != NULL);
1486 assert(twm_win->otp != NULL);
1487
1488 OwlClearAflag(twm_win->otp, flag);
1489 }
1490
1491 static void
OwlClearAflag(OtpWinList * owl,unsigned flag)1492 OwlClearAflag(OtpWinList *owl, unsigned flag)
1493 {
1494 assert(owl != NULL);
1495
1496 owl->pri_aflags &= ~flag;
1497 OwlStashAflags(owl);
1498 }
1499
1500 /*
1501 * Stash up flags in a property. We use this to keep track of whether we
1502 * have above/below flags set in the OTP info, so we can know what to set
1503 * when we restart. Otherwise we can't tell whether stuff like EWMH
1504 * _NET_WM_STATE flags are saying 'above' because the above flag got set
1505 * at some point, or whether other OTP config happens to have already
1506 * raised it.
1507 */
1508 void
OtpStashAflagsFirstTime(TwmWindow * twm_win)1509 OtpStashAflagsFirstTime(TwmWindow *twm_win)
1510 {
1511 if(!twm_win->otp->stashed_aflags) {
1512 OwlStashAflags(twm_win->otp);
1513 }
1514 }
1515
1516 static void
OwlStashAflags(OtpWinList * owl)1517 OwlStashAflags(OtpWinList *owl)
1518 {
1519 unsigned long of_prop = owl->pri_aflags;
1520
1521 /* Only "real" windows need stashed flags */
1522 if(owl->type != WinWin) {
1523 return;
1524 }
1525
1526 XChangeProperty(dpy, owl->twm_win->w, XA_CTWM_OTP_AFLAGS, XA_INTEGER,
1527 32, PropModeReplace, (unsigned char *)&of_prop, 1);
1528
1529 owl->stashed_aflags = true;
1530 }
1531
1532 static unsigned
OwlGetStashedAflags(OtpWinList * owl,bool * gotit)1533 OwlGetStashedAflags(OtpWinList *owl, bool *gotit)
1534 {
1535 /* Lotta dummy args */
1536 int ret;
1537 Atom act_type;
1538 int d_fmt;
1539 unsigned long nitems, d_after;
1540 unsigned long aflags, *aflags_p;
1541
1542 /* Only on real windows */
1543 if(owl->type != WinWin) {
1544 *gotit = false;
1545 return 0;
1546 }
1547
1548 ret = XGetWindowProperty(dpy, owl->twm_win->w, XA_CTWM_OTP_AFLAGS, 0, 1,
1549 False, XA_INTEGER, &act_type, &d_fmt, &nitems,
1550 &d_after, (unsigned char **)&aflags_p);
1551 if(ret == Success && act_type == XA_INTEGER && aflags_p != NULL) {
1552 aflags = *aflags_p;
1553 XFree(aflags_p);
1554 *gotit = true;
1555 }
1556 else {
1557 *gotit = false;
1558 aflags = 0;
1559 }
1560
1561 return aflags;
1562 }
1563
1564
1565 /*
1566 * Figure where a window should be stacked based on the current world,
1567 * and move it there. This function pretty much assumes it's not already
1568 * there; callers should generally be figuring that out before calling
1569 * this.
1570 */
1571 void
OtpRestackWindow(TwmWindow * twm_win)1572 OtpRestackWindow(TwmWindow *twm_win)
1573 {
1574 OtpWinList *owl = twm_win->otp;
1575
1576 RemoveOwl(owl);
1577 InsertOwl(owl, Above);
1578 OtpCheckConsistency();
1579 }
1580
1581
1582
1583 /**
1584 * Focus/unfocus backend. This is used on windows whose stacking is
1585 * focus-dependent (e.g., EWMH fullscreen), to move them and their
1586 * transients around. For these windows, getting/losing focus is
1587 * practically the same as a f.setpriority, except it's on the calculated
1588 * rather than the base parts. And it's hard to re-use our existing
1589 * functions to do it because we have to move Scr->Focus before the main
1590 * window changes, but then it's too late to see where all the transients
1591 * were.
1592 *
1593 * There are a number of unpleasant assumptions in here relating to where
1594 * the transients are, and IWBNI we could be smarter and quicker about
1595 * dealing with them. But this gets us past the simple to cause
1596 * assertion failures, anyway...
1597 */
1598 static void
OtpFocusWindowBE(TwmWindow * twm_win,int oldprio)1599 OtpFocusWindowBE(TwmWindow *twm_win, int oldprio)
1600 {
1601 OtpWinList *owl = twm_win->otp;
1602
1603 // This one comes off the list, and goes back in its new place.
1604 RemoveOwl(owl);
1605 InsertOwl(owl, Above);
1606
1607 // Now root around for any transients of it, and
1608 // nudge them into the new location. The whole Above/Below thing is
1609 // kinda a heavy-handed guess, but...
1610 //
1611 // This is nearly a reimplementation of TryToMoveTransientsOfTo(),
1612 // but the assumption that we can find the transients by starting
1613 // from where the old priority was in the list turns out to be deeply
1614 // broken. So just walk the whole thing. Which isn't ideal, but...
1615 //
1616 // We also need to do loop detection, since otherwise we'll get stuck
1617 // when a window has multiple transients to move around. Since we
1618 // read from the bottom up, if a window is moving up the stack, then
1619 // its transients move up, and we run into them again and again.
1620 //
1621 // XXX It should not be this freakin' hard to find a window's
1622 // transients. We should fix that more globally.
1623
1624 // XXX Let's just get a friggin' vector implementation already...
1625 size_t tlsz = 32; // Should hardly ever be too small
1626 size_t tlused = 0;
1627 OtpWinList **tlst = calloc(tlsz, sizeof(OtpWinList *));
1628 if(tlst == NULL) {
1629 fprintf(stderr, "%s(): realloc() failed\n", __func__);
1630 abort();
1631 }
1632
1633 // Loop through and find them all
1634 OtpWinList *trans = Scr->bottomOwl;
1635 while((trans != NULL)) {
1636 // Gotta pre-stash, since we're sometimes about to move trans.
1637 OtpWinList *next = trans->above;
1638
1639 if((trans->type == WinWin)
1640 && isTransientOf(trans->twm_win, twm_win)) {
1641 // Got one, stash it
1642 tlst[tlused++] = trans;
1643
1644 // Grow?
1645 if(tlused == tlsz) {
1646 tlsz *= 2;
1647 OtpWinList **tln = realloc(tlst, (tlsz * sizeof(OtpWinList *)));
1648 if(tln == NULL) {
1649 fprintf(stderr, "%s(): realloc() failed\n", __func__);
1650 abort();
1651 }
1652 tlst = tln;
1653 }
1654 }
1655
1656 // And onward
1657 trans = next;
1658 }
1659
1660
1661 // Now loop over them and shuffle them up
1662 for(int i = 0 ; i < tlused ; i++) {
1663 RemoveOwl(tlst[i]);
1664 InsertOwl(tlst[i], Above);
1665 }
1666
1667 free(tlst);
1668
1669 OtpCheckConsistency();
1670 }
1671
1672 /**
1673 * Unfocus a window. This needs to know internals of OTP because of
1674 * focus-dependent stacking of it and its transients.
1675 */
1676 void
OtpUnfocusWindow(TwmWindow * twm_win)1677 OtpUnfocusWindow(TwmWindow *twm_win)
1678 {
1679 // Stash where it currently appears to be. We assume all its
1680 // transients currently have the same effective priority. See also
1681 // TryToMoveTransientsOfTo() which makes the same assumption. I'm
1682 // not sure that's entirely warranted...
1683 int oldprio = PRI(twm_win->otp);
1684
1685 // Now tell ourselves it's unfocused
1686 assert(Scr->Focus == twm_win);
1687 Scr->Focus = NULL;
1688
1689 // And do the work
1690 OtpFocusWindowBE(twm_win, oldprio);
1691 }
1692
1693 /**
1694 * Focus a window. This needs to know internals of OTP because of
1695 * focus-dependent stacking of it and its transients.
1696 */
1697 void
OtpFocusWindow(TwmWindow * twm_win)1698 OtpFocusWindow(TwmWindow *twm_win)
1699 {
1700 // X-ref OtoUnfocusWindow() comments.
1701 int oldprio = PRI(twm_win->otp);
1702
1703 assert(Scr->Focus != twm_win);
1704 Scr->Focus = twm_win;
1705
1706 OtpFocusWindowBE(twm_win, oldprio);
1707 }
1708
1709
1710
1711 /*
1712 * Calculating effective priority. Take the base priority (what gets
1713 * set/altered by various OTP config and functions), and then tack on
1714 * whatever alterations more ephemeral things might apply. This
1715 * currently pretty much means EWMH bits.
1716 */
1717 int
OtpEffectiveDisplayPriority(TwmWindow * twm_win)1718 OtpEffectiveDisplayPriority(TwmWindow *twm_win)
1719 {
1720 assert(twm_win != NULL);
1721 assert(twm_win->otp != NULL);
1722
1723 return(OwlEffectivePriority(twm_win->otp) - OTP_ZERO);
1724 }
1725
1726 int
OtpEffectivePriority(TwmWindow * twm_win)1727 OtpEffectivePriority(TwmWindow *twm_win)
1728 {
1729 assert(twm_win != NULL);
1730 assert(twm_win->otp != NULL);
1731
1732 return OwlEffectivePriority(twm_win->otp);
1733 }
1734
1735 static int
OwlEffectivePriority(OtpWinList * owl)1736 OwlEffectivePriority(OtpWinList *owl)
1737 {
1738 int pri;
1739
1740 assert(owl != NULL);
1741
1742 pri = owl->pri_base;
1743
1744 #ifdef EWMH
1745 /* ABOVE/BELOW states shift a bit relative to the base */
1746 if(owl->pri_aflags & OTP_AFLAG_ABOVE) {
1747 pri += EWMH_PRI_ABOVE;
1748 }
1749 if(owl->pri_aflags & OTP_AFLAG_BELOW) {
1750 pri -= EWMH_PRI_ABOVE;
1751 }
1752
1753 /*
1754 * Special magic: EWMH says that _BELOW + _DOCK = (just _BELOW).
1755 * So if both are set, and its base is where we'd expect just a _DOCK
1756 * to be, try cancelling that out.
1757 */
1758 {
1759 EwmhWindowType ewt = owl->twm_win->ewmhWindowType;
1760 if((owl->pri_aflags & OTP_AFLAG_BELOW) && (ewt == wt_Dock) &&
1761 (owl->pri_base == EWMH_PRI_DOCK + OTP_ZERO)) {
1762 pri -= EWMH_PRI_DOCK;
1763 }
1764 }
1765
1766 /*
1767 * If FULLSCREEN and focused, jam to (nearly; let the user still win
1768 * if they try) the top. We also need to handle transients; they
1769 * might not have focus, but still need to be on top of the window
1770 * they're coming up transient for, or else they'll be hidden
1771 * forever.
1772 */
1773 if(owl->pri_aflags & OTP_AFLAG_FULLSCREEN) {
1774 if(Scr->Focus == owl->twm_win) {
1775 // It's focused, shift it up
1776 pri = EWMH_PRI_FULLSCREEN + OTP_ZERO;
1777 }
1778 else if(owl->twm_win->istransient) {
1779 // It's a transient of something else; if that something else
1780 // has the fullscreen/focus combo, we should pop this up top
1781 // too. Technically, we should perhaps test whether its
1782 // parent is also OTP_AFLAG_FULLSCREEN, but if the transient
1783 // has it, the parent probably does too. Worry about that
1784 // detail if it ever becomes a problem.
1785 TwmWindow *parent = GetTwmWindow(owl->twm_win->transientfor);
1786 if(Scr->Focus == parent) {
1787 // Shift this up so we stay on top
1788 pri = EWMH_PRI_FULLSCREEN + OTP_ZERO;
1789 }
1790 }
1791 }
1792 #endif
1793
1794 /* Constrain */
1795 pri = MAX(pri, 0);
1796 pri = MIN(pri, OTP_MAX);
1797
1798 return pri;
1799 }
1800
1801
1802 /*
1803 * Does the priority of a window depend on its focus state? External
1804 * code needs to know, to know when it might need restacking.
1805 */
1806 bool
OtpIsFocusDependent(TwmWindow * twm_win)1807 OtpIsFocusDependent(TwmWindow *twm_win)
1808 {
1809 assert(twm_win != NULL);
1810 assert(twm_win->otp != NULL);
1811
1812 #ifdef EWMH
1813 /*
1814 * EWMH says _FULLSCREEN and focused windows get shoved to the top;
1815 * this implies that _FULLSCREEN and _not_ focused don't. So if the
1816 * focus is changing, that means we may need to restack.
1817 */
1818 if(twm_win->otp->pri_aflags & OTP_AFLAG_FULLSCREEN) {
1819 return true;
1820 }
1821 #endif
1822
1823 return false;
1824 }
1825