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