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