10bbfda8aSnia/*
20bbfda8aSnia * Copyright 1992, 2005 Stefan Monnier.
30bbfda8aSnia *
40bbfda8aSnia * Author:  Stefan Monnier [ monnier@lia.di.epfl.ch ]
50bbfda8aSnia * Adapted for use with more than one virtual screen by
60bbfda8aSnia * Olaf "Rhialto" Seibert <rhialto@falu.nl>.
70bbfda8aSnia *
8b18c2d1eSnia * $Id: otp.c,v 1.1.1.2 2023/07/05 07:36:07 nia Exp $
90bbfda8aSnia *
100bbfda8aSnia * handles all the OnTopPriority-related issues.
110bbfda8aSnia *
120bbfda8aSnia */
130bbfda8aSnia
140bbfda8aSnia#include "ctwm.h"
150bbfda8aSnia
160bbfda8aSnia#include <stdio.h>
170bbfda8aSnia#include <stdlib.h>
180bbfda8aSnia#include <assert.h>
190bbfda8aSnia#include <X11/Xatom.h>
200bbfda8aSnia
210bbfda8aSnia#include "otp.h"
220bbfda8aSnia#include "ctwm_atoms.h"
230bbfda8aSnia#include "screen.h"
240bbfda8aSnia#include "util.h"
250bbfda8aSnia#include "icons.h"
260bbfda8aSnia#include "list.h"
270bbfda8aSnia#include "events.h"
280bbfda8aSnia#include "event_handlers.h"
290bbfda8aSnia#include "vscreen.h"
300bbfda8aSnia#include "win_utils.h"
310bbfda8aSnia
320bbfda8aSnia#define DEBUG_OTP       0
330bbfda8aSnia#if DEBUG_OTP
340bbfda8aSnia#define DPRINTF(x)      fprintf x
350bbfda8aSnia#else
360bbfda8aSnia#define DPRINTF(x)
370bbfda8aSnia#endif
380bbfda8aSnia
390bbfda8aSnia#if defined(NDEBUG)
400bbfda8aSnia# define CHECK_OTP      0
410bbfda8aSnia#else
420bbfda8aSnia# define CHECK_OTP      1
430bbfda8aSnia#endif
440bbfda8aSnia
450bbfda8aSnia/* number of priorities known to ctwm: [0..ONTOP_MAX] */
460bbfda8aSnia#define OTP_ZERO 8
470bbfda8aSnia#define OTP_MAX (OTP_ZERO * 2)
480bbfda8aSnia
490bbfda8aSnia/* Shorten code a little */
500bbfda8aSnia#define PRI(owl) OwlEffectivePriority(owl)
510bbfda8aSnia#define PRI_CP(from, to) do {                  \
520bbfda8aSnia            to->pri_base = from->pri_base;     \
530bbfda8aSnia            to->pri_aflags = from->pri_aflags; \
540bbfda8aSnia        } while(0)
550bbfda8aSnia
560bbfda8aSniastruct OtpWinList {
570bbfda8aSnia	OtpWinList *above;
580bbfda8aSnia	OtpWinList *below;
590bbfda8aSnia	TwmWindow  *twm_win;
600bbfda8aSnia	WinType     type;
610bbfda8aSnia	bool        switching;
620bbfda8aSnia	int         pri_base;   // Base priority
630bbfda8aSnia	unsigned    pri_aflags; // Flags that might alter it; OTP_AFLAG_*
640bbfda8aSnia	bool        stashed_aflags;
650bbfda8aSnia};
660bbfda8aSnia
670bbfda8aSniastruct OtpPreferences {
680bbfda8aSnia	name_list  *priorityL[OTP_MAX + 1];
690bbfda8aSnia	int         priority;
700bbfda8aSnia	name_list  *switchingL;
710bbfda8aSnia	bool        switching;
720bbfda8aSnia};
730bbfda8aSnia
740bbfda8aSniatypedef struct Box {
750bbfda8aSnia	int x;
760bbfda8aSnia	int y;
770bbfda8aSnia	int width;
780bbfda8aSnia	int height;
790bbfda8aSnia} Box;
800bbfda8aSnia
810bbfda8aSnia
820bbfda8aSniastatic bool OtpCheckConsistencyVS(VirtualScreen *currentvs, Window vroot);
83b18c2d1eSniastatic void OwlPrettyPrint(const OtpWinList *start);
840bbfda8aSniastatic void OwlSetAflagMask(OtpWinList *owl, unsigned mask, unsigned setto);
850bbfda8aSniastatic void OwlSetAflag(OtpWinList *owl, unsigned flag);
860bbfda8aSniastatic void OwlClearAflag(OtpWinList *owl, unsigned flag);
870bbfda8aSniastatic void OwlStashAflags(OtpWinList *owl);
880bbfda8aSniastatic unsigned OwlGetStashedAflags(OtpWinList *owl, bool *gotit);
890bbfda8aSniastatic int OwlEffectivePriority(OtpWinList *owl);
900bbfda8aSnia
910bbfda8aSniastatic Box BoxOfOwl(OtpWinList *owl)
920bbfda8aSnia{
930bbfda8aSnia	Box b;
940bbfda8aSnia
950bbfda8aSnia	switch(owl->type) {
960bbfda8aSnia		case IconWin: {
970bbfda8aSnia			Icon *icon = owl->twm_win->icon;
980bbfda8aSnia
990bbfda8aSnia			b.x = icon->w_x;
1000bbfda8aSnia			b.y = icon->w_y;
1010bbfda8aSnia			b.width = icon->w_width;
1020bbfda8aSnia			b.height = icon->w_height;
1030bbfda8aSnia			break;
1040bbfda8aSnia		}
1050bbfda8aSnia		case WinWin: {
1060bbfda8aSnia			TwmWindow *twm_win = owl->twm_win;
1070bbfda8aSnia
1080bbfda8aSnia			b.x = twm_win->frame_x;
1090bbfda8aSnia			b.y = twm_win->frame_y;
1100bbfda8aSnia			b.width = twm_win->frame_width;
1110bbfda8aSnia			b.height = twm_win->frame_height;
1120bbfda8aSnia			break;
1130bbfda8aSnia		}
1140bbfda8aSnia		default:
1150bbfda8aSnia			assert(false);
1160bbfda8aSnia	}
1170bbfda8aSnia	return b;
1180bbfda8aSnia}
1190bbfda8aSnia
1200bbfda8aSnia
1210bbfda8aSniastatic bool BoxesIntersect(Box *b1, Box *b2)
1220bbfda8aSnia{
1230bbfda8aSnia	bool interX = (b1->x + b1->width > b2->x) && (b2->x + b2->width > b1->x);
1240bbfda8aSnia	bool interY = (b1->y + b1->height > b2->y) && (b2->y + b2->height > b1->y);
1250bbfda8aSnia
1260bbfda8aSnia	return (interX && interY);
1270bbfda8aSnia}
1280bbfda8aSnia
1290bbfda8aSnia
1300bbfda8aSniastatic bool isIntersectingWith(OtpWinList *owl1, OtpWinList *owl2)
1310bbfda8aSnia{
1320bbfda8aSnia	Box b1 = BoxOfOwl(owl1);
1330bbfda8aSnia	Box b2 = BoxOfOwl(owl2);
1340bbfda8aSnia
1350bbfda8aSnia	return BoxesIntersect(&b1, &b2);
1360bbfda8aSnia}
1370bbfda8aSnia
1380bbfda8aSnia
1390bbfda8aSniastatic bool isOnScreen(OtpWinList *owl)
1400bbfda8aSnia{
1410bbfda8aSnia	TwmWindow *twm_win = owl->twm_win;
1420bbfda8aSnia
1430bbfda8aSnia	return (((owl->type == IconWin) ? twm_win->iconified : twm_win->mapped)
1440bbfda8aSnia	        && OCCUPY(twm_win, Scr->currentvs->wsw->currentwspc));
1450bbfda8aSnia}
1460bbfda8aSnia
1470bbfda8aSnia
1480bbfda8aSniabool isTransientOf(TwmWindow *trans, TwmWindow *main)
1490bbfda8aSnia{
1500bbfda8aSnia	return (trans->istransient && trans->transientfor == main->w);
1510bbfda8aSnia}
1520bbfda8aSnia
1530bbfda8aSniabool isGroupLeader(TwmWindow *twm_win)
1540bbfda8aSnia{
1550bbfda8aSnia	return ((twm_win->group == 0)
1560bbfda8aSnia	        || (twm_win->group == twm_win->w));
1570bbfda8aSnia}
1580bbfda8aSnia
1590bbfda8aSniabool isGroupLeaderOf(TwmWindow *leader, TwmWindow *twm_win)
1600bbfda8aSnia{
1610bbfda8aSnia	return (isGroupLeader(leader)
1620bbfda8aSnia	        && !isGroupLeader(twm_win)
1630bbfda8aSnia	        && (leader->group == twm_win->group));
1640bbfda8aSnia}
1650bbfda8aSnia
1660bbfda8aSniabool isSmallTransientOf(TwmWindow *trans, TwmWindow *main)
1670bbfda8aSnia{
1680bbfda8aSnia	int trans_area, main_area;
1690bbfda8aSnia
1700bbfda8aSnia	if(isTransientOf(trans, main)) {
1710bbfda8aSnia		assert(trans->frame);
1720bbfda8aSnia		trans_area = trans->frame_width * trans->frame_height;
1730bbfda8aSnia		main_area = main->frame_width * main->frame_height;
1740bbfda8aSnia
1750bbfda8aSnia		return (trans_area < ((main_area * Scr->TransientOnTop) / 100));
1760bbfda8aSnia	}
1770bbfda8aSnia	else {
1780bbfda8aSnia		return false;
1790bbfda8aSnia	}
1800bbfda8aSnia}
1810bbfda8aSnia
1820bbfda8aSniastatic Window WindowOfOwl(OtpWinList *owl)
1830bbfda8aSnia{
1840bbfda8aSnia	return (owl->type == IconWin)
1850bbfda8aSnia	       ? owl->twm_win->icon->w : owl->twm_win->frame;
1860bbfda8aSnia}
1870bbfda8aSnia
1880bbfda8aSniabool OtpCheckConsistency(void)
1890bbfda8aSnia{
1900bbfda8aSnia#if DEBUG_OTP
1910bbfda8aSnia	VirtualScreen *tvs;
1920bbfda8aSnia	bool result = true;
1930bbfda8aSnia
1940bbfda8aSnia	for(tvs = Scr->vScreenList; tvs != NULL; tvs = tvs->next) {
1950bbfda8aSnia		fprintf(stderr, "OtpCheckConsistencyVS: vs:(x,y)=(%d,%d)\n",
1960bbfda8aSnia		        tvs->x, tvs->y);
1970bbfda8aSnia		result = result && OtpCheckConsistencyVS(tvs, tvs->window);
1980bbfda8aSnia	}
1990bbfda8aSnia	return result;
2000bbfda8aSnia#else
2010bbfda8aSnia	return OtpCheckConsistencyVS(Scr->currentvs, Scr->Root);
2020bbfda8aSnia#endif
2030bbfda8aSnia}
2040bbfda8aSnia
2050bbfda8aSniastatic bool OtpCheckConsistencyVS(VirtualScreen *currentvs, Window vroot)
2060bbfda8aSnia{
2070bbfda8aSnia#if CHECK_OTP
2080bbfda8aSnia	OtpWinList *owl;
2090bbfda8aSnia	TwmWindow *twm_win;
2100bbfda8aSnia	Window root, parent, *children;
2110bbfda8aSnia	unsigned int nchildren;
2120bbfda8aSnia	int priority = 0;
2130bbfda8aSnia	int stack = -1;
2140bbfda8aSnia	int nwins = 0;
2150bbfda8aSnia
2160bbfda8aSnia	XQueryTree(dpy, vroot, &root, &parent, &children, &nchildren);
2170bbfda8aSnia
2180bbfda8aSnia#if DEBUG_OTP
2190bbfda8aSnia	{
2200bbfda8aSnia		int i;
2210bbfda8aSnia		fprintf(stderr, "XQueryTree: %d children:\n", nchildren);
2220bbfda8aSnia
2230bbfda8aSnia		for(i = 0; i < nchildren; i++) {
2240bbfda8aSnia			fprintf(stderr, "[%d]=%x ", i, (unsigned int)children[i]);
2250bbfda8aSnia		}
2260bbfda8aSnia		fprintf(stderr, "\n");
2270bbfda8aSnia	}
2280bbfda8aSnia#endif
2290bbfda8aSnia
2300bbfda8aSnia	for(owl = Scr->bottomOwl; owl != NULL; owl = owl->above) {
2310bbfda8aSnia		twm_win = owl->twm_win;
2320bbfda8aSnia
2330bbfda8aSnia		/* check the back arrows are correct */
2340bbfda8aSnia		assert(((owl->type == IconWin) && (owl == twm_win->icon->otp))
2350bbfda8aSnia		       || ((owl->type == WinWin) && (owl == twm_win->otp)));
2360bbfda8aSnia
2370bbfda8aSnia		/* check the doubly linked list's consistency */
2380bbfda8aSnia		if(owl->below == NULL) {
2390bbfda8aSnia			assert(owl == Scr->bottomOwl);
2400bbfda8aSnia		}
2410bbfda8aSnia		else {
2420bbfda8aSnia			assert(owl->below->above == owl);
2430bbfda8aSnia		}
2440bbfda8aSnia
2450bbfda8aSnia		/* Code already ensures this */
2460bbfda8aSnia		assert(owl->pri_base <= OTP_MAX);
2470bbfda8aSnia
2480bbfda8aSnia		/* List should be bottom->top, so effective pri better ascend */
249b18c2d1eSnia		{
250b18c2d1eSnia			const int nextpri = PRI(owl);
251b18c2d1eSnia			if(nextpri < priority) {
252b18c2d1eSnia				fprintf(stderr, "%s(): Priority went backward "
253b18c2d1eSnia				        "(%d:'%s' -> %d:'%s')\n",
254b18c2d1eSnia				        __func__,
255b18c2d1eSnia				        priority, owl->below->twm_win->name,
256b18c2d1eSnia				        nextpri, owl->twm_win->name);
257b18c2d1eSnia				OwlPrettyPrint(Scr->bottomOwl);
258b18c2d1eSnia				abort();
259b18c2d1eSnia			}
260b18c2d1eSnia			priority = nextpri;
261b18c2d1eSnia		}
2620bbfda8aSnia
2630bbfda8aSnia#if DEBUG_OTP
2640bbfda8aSnia
2650bbfda8aSnia		fprintf(stderr, "checking owl: pri %d w=%x stack=%d",
2660bbfda8aSnia		        priority, (unsigned int)WindowOfOwl(owl), stack);
2670bbfda8aSnia		if(twm_win) {
2680bbfda8aSnia			fprintf(stderr, " title=%s occupation=%x ",
2690bbfda8aSnia			        twm_win->name,
2700bbfda8aSnia			        (unsigned int)twm_win->occupation);
2710bbfda8aSnia			if(owl->twm_win->vs) {
2720bbfda8aSnia				fprintf(stderr, " vs:(x,y)=(%d,%d)",
2730bbfda8aSnia				        twm_win->vs->x,
2740bbfda8aSnia				        twm_win->vs->y);
2750bbfda8aSnia			}
2760bbfda8aSnia			else {
2770bbfda8aSnia				fprintf(stderr, " vs:NULL");
2780bbfda8aSnia			}
2790bbfda8aSnia			if(owl->twm_win->parent_vs) {
2800bbfda8aSnia				fprintf(stderr, " parent_vs:(x,y)=(%d,%d)",
2810bbfda8aSnia				        twm_win->parent_vs->x,
2820bbfda8aSnia				        twm_win->parent_vs->y);
2830bbfda8aSnia			}
2840bbfda8aSnia			else {
2850bbfda8aSnia				fprintf(stderr, " parent_vs:NULL");
2860bbfda8aSnia			}
2870bbfda8aSnia		}
2880bbfda8aSnia		fprintf(stderr, " %s\n", (owl->type == WinWin ? "Window" : "Icon"));
2890bbfda8aSnia#endif
2900bbfda8aSnia
2910bbfda8aSnia		/* count the number of twm windows */
2920bbfda8aSnia		if(owl->type == WinWin) {
2930bbfda8aSnia			nwins++;
2940bbfda8aSnia		}
2950bbfda8aSnia
296b18c2d1eSnia#ifdef WINBOX
2970bbfda8aSnia		if(twm_win->winbox) {
2980bbfda8aSnia			/*
2990bbfda8aSnia			 * We can't check windows in a WindowBox, since they are
3000bbfda8aSnia			 * not direct children of the Root window.
3010bbfda8aSnia			 */
3020bbfda8aSnia			DPRINTF((stderr, "Can't check this window, it is in a WinBox\n"));
3030bbfda8aSnia			continue;
3040bbfda8aSnia		}
305b18c2d1eSnia#endif
3060bbfda8aSnia
3070bbfda8aSnia		/*
3080bbfda8aSnia		 * Check only windows from the current vitual screen; the others
3090bbfda8aSnia		 * won't show up in the tree from XQueryTree().
3100bbfda8aSnia		 */
3110bbfda8aSnia		if(currentvs == twm_win->parent_vs) {
3120bbfda8aSnia			/* check the window's existence. */
3130bbfda8aSnia			Window windowOfOwl = WindowOfOwl(owl);
3140bbfda8aSnia
3150bbfda8aSnia#if DEBUG_OTP
3160bbfda8aSnia			int i;
3170bbfda8aSnia			for(i = 0; i < nchildren && windowOfOwl != children[i];) {
3180bbfda8aSnia				i++;
3190bbfda8aSnia			}
3200bbfda8aSnia			fprintf(stderr, "search for owl in stack -> i=%d\n", i);
3210bbfda8aSnia			assert(i > stack && "Window not in good place in stack");
3220bbfda8aSnia			assert(i < nchildren && "Window was not found in stack");
3230bbfda8aSnia			if(0) {
3240bbfda8aSnia				char buf[128];
3250bbfda8aSnia				snprintf(buf, 128, "xwininfo -all -id %d", (int)windowOfOwl);
3260bbfda8aSnia				system(buf);
3270bbfda8aSnia			}
3280bbfda8aSnia
3290bbfda8aSnia			/* we know that this always increases stack (assert i>stack) */
3300bbfda8aSnia			stack = i;
3310bbfda8aSnia#else /* DEBUG_OTP */
3320bbfda8aSnia			/* check against the Xserver's stack */
3330bbfda8aSnia			do {
3340bbfda8aSnia				stack++;
3350bbfda8aSnia				DPRINTF((stderr, "stack++: children[%d] = %x\n", stack,
3360bbfda8aSnia				         (unsigned int)children[stack]));
3370bbfda8aSnia				assert(stack < nchildren);
3380bbfda8aSnia			}
3390bbfda8aSnia			while(windowOfOwl != children[stack]);
3400bbfda8aSnia#endif /* DEBUG_OTP */
3410bbfda8aSnia		}
3420bbfda8aSnia	}
3430bbfda8aSnia
3440bbfda8aSnia	XFree(children);
3450bbfda8aSnia
3460bbfda8aSnia	/* by decrementing nwins, check that all the wins are in our list */
3470bbfda8aSnia	for(twm_win = Scr->FirstWindow; twm_win != NULL; twm_win = twm_win->next) {
3480bbfda8aSnia		nwins--;
3490bbfda8aSnia	}
3500bbfda8aSnia	/* if we just removed a win, it might still be somewhere, hence the -1 */
3510bbfda8aSnia	assert((nwins <= 0) && (nwins >= -1));
3520bbfda8aSnia#endif
3530bbfda8aSnia	return true;
3540bbfda8aSnia}
3550bbfda8aSnia
3560bbfda8aSnia
3570bbfda8aSniastatic void RemoveOwl(OtpWinList *owl)
3580bbfda8aSnia{
3590bbfda8aSnia	if(owl->above != NULL) {
3600bbfda8aSnia		owl->above->below = owl->below;
3610bbfda8aSnia	}
3620bbfda8aSnia	if(owl->below != NULL) {
3630bbfda8aSnia		owl->below->above = owl->above;
3640bbfda8aSnia	}
3650bbfda8aSnia	else {
3660bbfda8aSnia		Scr->bottomOwl = owl->above;
3670bbfda8aSnia	}
3680bbfda8aSnia	owl->below = NULL;
3690bbfda8aSnia	owl->above = NULL;
3700bbfda8aSnia}
3710bbfda8aSnia
3720bbfda8aSnia
3730bbfda8aSnia/**
3740bbfda8aSnia * For the purpose of putting a window above another,
3750bbfda8aSnia * they need to have the same parent, i.e. be in the same
3760bbfda8aSnia * VirtualScreen.
3770bbfda8aSnia */
3780bbfda8aSniastatic OtpWinList *GetOwlAtOrBelowInVS(OtpWinList *owl, VirtualScreen *vs)
3790bbfda8aSnia{
3800bbfda8aSnia	while(owl != NULL && owl->twm_win->parent_vs != vs) {
3810bbfda8aSnia		owl = owl->below;
3820bbfda8aSnia	}
3830bbfda8aSnia
3840bbfda8aSnia	return owl;
3850bbfda8aSnia}
3860bbfda8aSnia
387b18c2d1eSnia#ifdef WINBOX
3880bbfda8aSnia/*
3890bbfda8aSnia * Windows in a box don't really occur in the stacking order of the
3900bbfda8aSnia * root window.
3910bbfda8aSnia * In the OWL list, keep them just on top of their box, in their
3920bbfda8aSnia * respective order of course.
3930bbfda8aSnia * Therefore we may need to update the owl we're going to be above.
3940bbfda8aSnia */
3950bbfda8aSniastatic OtpWinList *GetOwlAtOrBelowInWinbox(OtpWinList **owlp, WindowBox *wb)
3960bbfda8aSnia{
3970bbfda8aSnia	OtpWinList *owl = *owlp;
3980bbfda8aSnia
3990bbfda8aSnia	while(owl != NULL && owl->twm_win->winbox != wb) {
4000bbfda8aSnia		owl = owl->below;
4010bbfda8aSnia	}
4020bbfda8aSnia
4030bbfda8aSnia	if(owl == NULL) {
4040bbfda8aSnia		/* we have gone below the box: put it just on top of it */
4050bbfda8aSnia		*owlp = wb->twmwin->otp;
4060bbfda8aSnia	}
4070bbfda8aSnia	else {
4080bbfda8aSnia		*owlp = owl;
4090bbfda8aSnia	}
4100bbfda8aSnia	return owl;
4110bbfda8aSnia}
412b18c2d1eSnia#endif
4130bbfda8aSnia
4140bbfda8aSnia
4150bbfda8aSniastatic void InsertOwlAbove(OtpWinList *owl, OtpWinList *other_owl)
4160bbfda8aSnia{
4170bbfda8aSnia#if DEBUG_OTP
4180bbfda8aSnia	fprintf(stderr, "InsertOwlAbove owl->pri=%d w=0x%x parent_vs:(x,y)=(%d,%d)",
4190bbfda8aSnia	        PRI(owl),
4200bbfda8aSnia	        (unsigned int)WindowOfOwl(owl),
4210bbfda8aSnia	        owl->twm_win->parent_vs->x,
4220bbfda8aSnia	        owl->twm_win->parent_vs->y);
4230bbfda8aSnia	if(other_owl != NULL) {
4240bbfda8aSnia		fprintf(stderr, "\n  other_owl->pri=%d w=0x%x parent_vs:(x,y)=(%d,%d)",
4250bbfda8aSnia		        PRI(other_owl),
4260bbfda8aSnia		        (unsigned int)WindowOfOwl(other_owl),
4270bbfda8aSnia		        owl->twm_win->parent_vs->x,
4280bbfda8aSnia		        owl->twm_win->parent_vs->y);
4290bbfda8aSnia	}
4300bbfda8aSnia	fprintf(stderr, "\n");
4310bbfda8aSnia#endif
4320bbfda8aSnia
4330bbfda8aSnia	assert(owl->above == NULL);
4340bbfda8aSnia	assert(owl->below == NULL);
4350bbfda8aSnia
4360bbfda8aSnia
4370bbfda8aSnia	if(other_owl == NULL) {
4380bbfda8aSnia		DPRINTF((stderr, "Bottom-most window overall\n"));
4390bbfda8aSnia		/* special case for the lowest window overall */
4400bbfda8aSnia		assert(PRI(owl) <= PRI(Scr->bottomOwl));
4410bbfda8aSnia
4420bbfda8aSnia		/* pass the action to the Xserver */
4430bbfda8aSnia		XLowerWindow(dpy, WindowOfOwl(owl));
4440bbfda8aSnia
4450bbfda8aSnia		/* update the list */
4460bbfda8aSnia		owl->above = Scr->bottomOwl;
4470bbfda8aSnia		owl->above->below = owl;
4480bbfda8aSnia		Scr->bottomOwl = owl;
4490bbfda8aSnia	}
4500bbfda8aSnia	else {
451b18c2d1eSnia#ifdef WINBOX
4520bbfda8aSnia		WindowBox *winbox = owl->twm_win->winbox;
453b18c2d1eSnia#endif
4540bbfda8aSnia		OtpWinList *vs_owl;
4550bbfda8aSnia
456b18c2d1eSnia		if(false) {
457b18c2d1eSnia			// dummy
458b18c2d1eSnia		}
459b18c2d1eSnia#ifdef WINBOX
460b18c2d1eSnia		else if(winbox != NULL) {
4610bbfda8aSnia			vs_owl = GetOwlAtOrBelowInWinbox(&other_owl, winbox);
4620bbfda8aSnia		}
463b18c2d1eSnia#endif
4640bbfda8aSnia		else {
4650bbfda8aSnia
4660bbfda8aSnia			vs_owl = GetOwlAtOrBelowInVS(other_owl, owl->twm_win->parent_vs);
4670bbfda8aSnia		}
4680bbfda8aSnia
4690bbfda8aSnia		assert(PRI(owl) >= PRI(other_owl));
4700bbfda8aSnia		if(other_owl->above != NULL) {
4710bbfda8aSnia			assert(PRI(owl) <= PRI(other_owl->above));
4720bbfda8aSnia		}
4730bbfda8aSnia
4740bbfda8aSnia		if(vs_owl == NULL) {
4750bbfda8aSnia			DPRINTF((stderr, "Bottom-most window in VirtualScreen or window box\n"));
4760bbfda8aSnia			/* special case for the lowest window in this virtual screen or window box */
4770bbfda8aSnia
4780bbfda8aSnia			/* pass the action to the Xserver */
4790bbfda8aSnia			XLowerWindow(dpy, WindowOfOwl(owl));
4800bbfda8aSnia		}
4810bbfda8aSnia		else {
4820bbfda8aSnia			XWindowChanges xwc;
4830bbfda8aSnia			int xwcm;
4840bbfda8aSnia
4850bbfda8aSnia			DPRINTF((stderr, "General case\n"));
4860bbfda8aSnia			/* general case */
4870bbfda8aSnia			assert(PRI(vs_owl) <= PRI(other_owl));
4880bbfda8aSnia			assert(owl->twm_win->parent_vs == vs_owl->twm_win->parent_vs);
4890bbfda8aSnia
4900bbfda8aSnia			/* pass the action to the Xserver */
4910bbfda8aSnia			xwcm = CWStackMode | CWSibling;
4920bbfda8aSnia			xwc.sibling = WindowOfOwl(vs_owl);
4930bbfda8aSnia			xwc.stack_mode = Above;
4940bbfda8aSnia			XConfigureWindow(dpy, WindowOfOwl(owl), xwcm, &xwc);
4950bbfda8aSnia		}
4960bbfda8aSnia
4970bbfda8aSnia		/* update the list */
4980bbfda8aSnia		owl->below = other_owl;
4990bbfda8aSnia		owl->above = other_owl->above;
5000bbfda8aSnia		owl->below->above = owl;
5010bbfda8aSnia		if(owl->above != NULL) {
5020bbfda8aSnia			owl->above->below = owl;
5030bbfda8aSnia		}
5040bbfda8aSnia	}
5050bbfda8aSnia}
5060bbfda8aSnia
5070bbfda8aSnia
5080bbfda8aSnia/* should owl stay above other_owl if other_owl was raised ? */
5090bbfda8aSniastatic bool shouldStayAbove(OtpWinList *owl, OtpWinList *other_owl)
5100bbfda8aSnia{
5110bbfda8aSnia	return ((owl->type == WinWin)
5120bbfda8aSnia	        && (other_owl->type == WinWin)
5130bbfda8aSnia	        && isSmallTransientOf(owl->twm_win, other_owl->twm_win));
5140bbfda8aSnia}
5150bbfda8aSnia
5160bbfda8aSnia
5170bbfda8aSniastatic void RaiseSmallTransientsOfAbove(OtpWinList *owl, OtpWinList *other_owl)
5180bbfda8aSnia{
5190bbfda8aSnia	OtpWinList *trans_owl, *tmp_owl;
5200bbfda8aSnia
5210bbfda8aSnia	/* the icons have no transients and we can't have windows below NULL */
5220bbfda8aSnia	if((owl->type != WinWin) || other_owl == NULL) {
5230bbfda8aSnia		return;
5240bbfda8aSnia	}
5250bbfda8aSnia
5260bbfda8aSnia	/* beware: we modify the list as we scan it. This is the reason for tmp */
5270bbfda8aSnia	for(trans_owl = other_owl->below; trans_owl != NULL; trans_owl = tmp_owl) {
5280bbfda8aSnia		tmp_owl = trans_owl->below;
5290bbfda8aSnia		if(shouldStayAbove(trans_owl, owl)) {
5300bbfda8aSnia			RemoveOwl(trans_owl);
5310bbfda8aSnia			PRI_CP(owl, trans_owl);
5320bbfda8aSnia			InsertOwlAbove(trans_owl, other_owl);
5330bbfda8aSnia		}
5340bbfda8aSnia	}
5350bbfda8aSnia}
5360bbfda8aSnia
5370bbfda8aSnia
5380bbfda8aSniastatic OtpWinList *OwlRightBelow(int priority)
5390bbfda8aSnia{
5400bbfda8aSnia	OtpWinList *owl1, *owl2;
5410bbfda8aSnia
5420bbfda8aSnia	/* in case there isn't anything below */
5430bbfda8aSnia	if(priority <= PRI(Scr->bottomOwl)) {
5440bbfda8aSnia		return NULL;
5450bbfda8aSnia	}
5460bbfda8aSnia
5470bbfda8aSnia	for(owl1 = Scr->bottomOwl, owl2 = owl1->above;
5480bbfda8aSnia	                (owl2 != NULL) && (PRI(owl2) < priority);
5490bbfda8aSnia	                owl1 = owl2, owl2 = owl2->above) {
5500bbfda8aSnia		/* nada */;
5510bbfda8aSnia	}
5520bbfda8aSnia
5530bbfda8aSnia	assert(owl2 == owl1->above);
5540bbfda8aSnia	assert(PRI(owl1) < priority);
5550bbfda8aSnia	assert((owl2 == NULL) || (PRI(owl2) >= priority));
5560bbfda8aSnia
5570bbfda8aSnia
5580bbfda8aSnia	return owl1;
5590bbfda8aSnia}
5600bbfda8aSnia
5610bbfda8aSniastatic void InsertOwl(OtpWinList *owl, int where)
5620bbfda8aSnia{
5630bbfda8aSnia	OtpWinList *other_owl;
5640bbfda8aSnia	int priority;
5650bbfda8aSnia
5660bbfda8aSnia	DPRINTF((stderr, "InsertOwl %s\n",
5670bbfda8aSnia	         (where == Above) ? "Above" :
5680bbfda8aSnia	         (where == Below) ? "Below" :
5690bbfda8aSnia	         "???"));
5700bbfda8aSnia	assert(owl->above == NULL);
5710bbfda8aSnia	assert(owl->below == NULL);
5720bbfda8aSnia	assert((where == Above) || (where == Below));
5730bbfda8aSnia
5740bbfda8aSnia	priority = PRI(owl) - (where == Above ? 0 : 1);
5750bbfda8aSnia
5760bbfda8aSnia	if(Scr->bottomOwl == NULL) {
5770bbfda8aSnia		/* for the first window: just insert it in the list */
5780bbfda8aSnia		Scr->bottomOwl = owl;
5790bbfda8aSnia	}
5800bbfda8aSnia	else {
5810bbfda8aSnia		other_owl = OwlRightBelow(priority + 1);
5820bbfda8aSnia
5830bbfda8aSnia		/* make sure other_owl is not one of the transients */
5840bbfda8aSnia		while((other_owl != NULL)
5850bbfda8aSnia		                && shouldStayAbove(other_owl, owl)) {
5860bbfda8aSnia			PRI_CP(owl, other_owl);
5870bbfda8aSnia
5880bbfda8aSnia			other_owl = other_owl->below;
5890bbfda8aSnia		}
5900bbfda8aSnia
5910bbfda8aSnia		/* raise the transient windows that should stay on top */
5920bbfda8aSnia		RaiseSmallTransientsOfAbove(owl, other_owl);
5930bbfda8aSnia
5940bbfda8aSnia		/* now go ahead and put the window where it should go */
5950bbfda8aSnia		InsertOwlAbove(owl, other_owl);
5960bbfda8aSnia	}
5970bbfda8aSnia}
5980bbfda8aSnia
5990bbfda8aSnia
6000bbfda8aSniastatic void SetOwlPriority(OtpWinList *owl, int new_pri, int where)
6010bbfda8aSnia{
6020bbfda8aSnia	DPRINTF((stderr, "SetOwlPriority(%d)\n", new_pri));
6030bbfda8aSnia
6040bbfda8aSnia	/* make sure the values are within bounds */
6050bbfda8aSnia	if(new_pri < 0) {
6060bbfda8aSnia		new_pri = 0;
6070bbfda8aSnia	}
6080bbfda8aSnia	if(new_pri > OTP_MAX) {
6090bbfda8aSnia		new_pri = OTP_MAX;
6100bbfda8aSnia	}
6110bbfda8aSnia
6120bbfda8aSnia	RemoveOwl(owl);
6130bbfda8aSnia	owl->pri_base = new_pri;
6140bbfda8aSnia	InsertOwl(owl, where);
6150bbfda8aSnia
6160bbfda8aSnia	assert(owl->pri_base == new_pri);
6170bbfda8aSnia}
6180bbfda8aSnia
6190bbfda8aSnia
6200bbfda8aSnia/*
6210bbfda8aSnia * Shift transients of a window to a new [base] priority, preparatory to
6220bbfda8aSnia * moving that window itself there.
6230bbfda8aSnia */
6240bbfda8aSniastatic void TryToMoveTransientsOfTo(OtpWinList *owl, int priority, int where)
6250bbfda8aSnia{
6260bbfda8aSnia	OtpWinList *other_owl;
6270bbfda8aSnia
6280bbfda8aSnia	/* the icons have no transients */
6290bbfda8aSnia	if(owl->type != WinWin) {
6300bbfda8aSnia		return;
6310bbfda8aSnia	}
6320bbfda8aSnia
6330bbfda8aSnia	/*
6340bbfda8aSnia	 * We start looking for transients of owl at the bottom of its OTP
6350bbfda8aSnia	 * layer.
6360bbfda8aSnia	 */
6370bbfda8aSnia	other_owl = OwlRightBelow(PRI(owl));
6380bbfda8aSnia	other_owl = (other_owl == NULL) ? Scr->bottomOwl : other_owl->above;
6390bbfda8aSnia	assert(PRI(other_owl) >= PRI(owl));
6400bbfda8aSnia
6410bbfda8aSnia	/* !beware! we're changing the list as we scan it, hence the tmp_owl */
6420bbfda8aSnia	while((other_owl != NULL) && (PRI(other_owl) == PRI(owl))) {
6430bbfda8aSnia		OtpWinList *tmp_owl = other_owl->above;
6440bbfda8aSnia		if((other_owl->type == WinWin)
6450bbfda8aSnia		                && isTransientOf(other_owl->twm_win, owl->twm_win)) {
6460bbfda8aSnia			/* Copy in our flags so it winds up in the right place */
6470bbfda8aSnia			other_owl->pri_aflags = owl->pri_aflags;
6480bbfda8aSnia			SetOwlPriority(other_owl, priority, where);
6490bbfda8aSnia		}
6500bbfda8aSnia		other_owl = tmp_owl;
6510bbfda8aSnia	}
6520bbfda8aSnia}
6530bbfda8aSnia
6540bbfda8aSniastatic void TryToSwitch(OtpWinList *owl, int where)
6550bbfda8aSnia{
6560bbfda8aSnia	int priority;
6570bbfda8aSnia
6580bbfda8aSnia	if(!owl->switching) {
6590bbfda8aSnia		return;
6600bbfda8aSnia	}
6610bbfda8aSnia
6620bbfda8aSnia	/*
6630bbfda8aSnia	 * Switching is purely an adjustment to the base priority, so we
6640bbfda8aSnia	 * don't need to figure stuff based on the effective.
6650bbfda8aSnia	 */
6660bbfda8aSnia	priority = OTP_MAX - owl->pri_base;
6670bbfda8aSnia	if(((where == Above) && (priority > owl->pri_base)) ||
6680bbfda8aSnia	                ((where == Below) && (priority < owl->pri_base))) {
6690bbfda8aSnia		/*
6700bbfda8aSnia		 * TTMTOT() before changing pri_base since it uses the current
6710bbfda8aSnia		 * state to find the transients.
6720bbfda8aSnia		 */
6730bbfda8aSnia		TryToMoveTransientsOfTo(owl, priority, where);
6740bbfda8aSnia		owl->pri_base = priority;
6750bbfda8aSnia	}
6760bbfda8aSnia}
6770bbfda8aSnia
6780bbfda8aSniastatic void RaiseOwl(OtpWinList *owl)
6790bbfda8aSnia{
6800bbfda8aSnia	TryToSwitch(owl, Above);
6810bbfda8aSnia	RemoveOwl(owl);
6820bbfda8aSnia	InsertOwl(owl, Above);
6830bbfda8aSnia}
6840bbfda8aSnia
6850bbfda8aSnia
6860bbfda8aSniastatic void LowerOwl(OtpWinList *owl)
6870bbfda8aSnia{
6880bbfda8aSnia	TryToSwitch(owl, Below);
6890bbfda8aSnia	RemoveOwl(owl);
6900bbfda8aSnia	InsertOwl(owl, Below);
6910bbfda8aSnia}
6920bbfda8aSnia
6930bbfda8aSniastatic bool isHiddenBy(OtpWinList *owl, OtpWinList *other_owl)
6940bbfda8aSnia{
6950bbfda8aSnia	/* doesn't check that owl is on screen */
6960bbfda8aSnia	return (isOnScreen(other_owl)
6970bbfda8aSnia	        && isIntersectingWith(owl, other_owl));
6980bbfda8aSnia}
6990bbfda8aSnia
7000bbfda8aSniastatic void TinyRaiseOwl(OtpWinList *owl)
7010bbfda8aSnia{
7020bbfda8aSnia	OtpWinList *other_owl = owl->above;
7030bbfda8aSnia
7040bbfda8aSnia	while((other_owl != NULL) && (PRI(other_owl) == PRI(owl))) {
7050bbfda8aSnia		if(isHiddenBy(owl, other_owl)
7060bbfda8aSnia		                && !shouldStayAbove(other_owl, owl)) {
7070bbfda8aSnia			RemoveOwl(owl);
7080bbfda8aSnia			RaiseSmallTransientsOfAbove(owl, other_owl);
7090bbfda8aSnia			InsertOwlAbove(owl, other_owl);
7100bbfda8aSnia			return;
7110bbfda8aSnia		}
7120bbfda8aSnia		else {
7130bbfda8aSnia			other_owl = other_owl->above;
7140bbfda8aSnia		}
7150bbfda8aSnia	}
7160bbfda8aSnia}
7170bbfda8aSnia
7180bbfda8aSniastatic void TinyLowerOwl(OtpWinList *owl)
7190bbfda8aSnia{
7200bbfda8aSnia	OtpWinList *other_owl = owl->below;
7210bbfda8aSnia
7220bbfda8aSnia	while((other_owl != NULL) && (PRI(other_owl) == PRI(owl))) {
7230bbfda8aSnia		if(isHiddenBy(owl, other_owl)) {
7240bbfda8aSnia			RemoveOwl(owl);
7250bbfda8aSnia			InsertOwlAbove(owl, other_owl->below);
7260bbfda8aSnia			return;
7270bbfda8aSnia		}
7280bbfda8aSnia		else {
7290bbfda8aSnia			other_owl = other_owl->below;
7300bbfda8aSnia		}
7310bbfda8aSnia	}
7320bbfda8aSnia}
7330bbfda8aSnia
7340bbfda8aSniastatic void RaiseLowerOwl(OtpWinList *owl)
7350bbfda8aSnia{
7360bbfda8aSnia	OtpWinList *other_owl;
7370bbfda8aSnia	int priority;
7380bbfda8aSnia
7390bbfda8aSnia	/*
7400bbfda8aSnia	 * abs(effective pri)
7410bbfda8aSnia	 *
7420bbfda8aSnia	 * XXX Why?  This seems like it's encoding the assumption
7430bbfda8aSnia	 * "f.raiselower should assume any negative [user-level] priorities
7440bbfda8aSnia	 * are a result of a window that should be positive being switched,
7450bbfda8aSnia	 * and we should switch it positive before raising if we need to", or
7460bbfda8aSnia	 * some such.
7470bbfda8aSnia	 */
7480bbfda8aSnia	priority = MAX(PRI(owl), OTP_MAX - PRI(owl));
7490bbfda8aSnia
7500bbfda8aSnia	for(other_owl = owl->above;
7510bbfda8aSnia	                (other_owl != NULL) && (PRI(other_owl) <= priority);
7520bbfda8aSnia	                other_owl = other_owl->above) {
7530bbfda8aSnia		if(isHiddenBy(owl, other_owl)
7540bbfda8aSnia		                && !shouldStayAbove(other_owl, owl)) {
7550bbfda8aSnia			RaiseOwl(owl);
7560bbfda8aSnia			return;
7570bbfda8aSnia		}
7580bbfda8aSnia	}
7590bbfda8aSnia	LowerOwl(owl);
7600bbfda8aSnia}
7610bbfda8aSnia
7620bbfda8aSnia
7630bbfda8aSniavoid OtpRaise(TwmWindow *twm_win, WinType wintype)
7640bbfda8aSnia{
7650bbfda8aSnia	OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
7660bbfda8aSnia	assert(owl != NULL);
7670bbfda8aSnia
7680bbfda8aSnia	RaiseOwl(owl);
7690bbfda8aSnia
7700bbfda8aSnia	OtpCheckConsistency();
7710bbfda8aSnia#ifdef EWMH
7720bbfda8aSnia	EwmhSet_NET_CLIENT_LIST_STACKING();
7730bbfda8aSnia#endif /* EWMH */
7740bbfda8aSnia}
7750bbfda8aSnia
7760bbfda8aSnia
7770bbfda8aSniavoid OtpLower(TwmWindow *twm_win, WinType wintype)
7780bbfda8aSnia{
7790bbfda8aSnia	OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
7800bbfda8aSnia	assert(owl != NULL);
7810bbfda8aSnia
7820bbfda8aSnia	LowerOwl(owl);
7830bbfda8aSnia
7840bbfda8aSnia	OtpCheckConsistency();
7850bbfda8aSnia#ifdef EWMH
7860bbfda8aSnia	EwmhSet_NET_CLIENT_LIST_STACKING();
7870bbfda8aSnia#endif /* EWMH */
7880bbfda8aSnia}
7890bbfda8aSnia
7900bbfda8aSnia
7910bbfda8aSniavoid OtpRaiseLower(TwmWindow *twm_win, WinType wintype)
7920bbfda8aSnia{
7930bbfda8aSnia	OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
7940bbfda8aSnia	assert(owl != NULL);
7950bbfda8aSnia
7960bbfda8aSnia	RaiseLowerOwl(owl);
7970bbfda8aSnia
7980bbfda8aSnia	OtpCheckConsistency();
7990bbfda8aSnia#ifdef EWMH
8000bbfda8aSnia	EwmhSet_NET_CLIENT_LIST_STACKING();
8010bbfda8aSnia#endif /* EWMH */
8020bbfda8aSnia}
8030bbfda8aSnia
8040bbfda8aSnia
8050bbfda8aSniavoid OtpTinyRaise(TwmWindow *twm_win, WinType wintype)
8060bbfda8aSnia{
8070bbfda8aSnia	OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
8080bbfda8aSnia	assert(owl != NULL);
8090bbfda8aSnia
8100bbfda8aSnia	TinyRaiseOwl(owl);
8110bbfda8aSnia
8120bbfda8aSnia	OtpCheckConsistency();
8130bbfda8aSnia#ifdef EWMH
8140bbfda8aSnia	EwmhSet_NET_CLIENT_LIST_STACKING();
8150bbfda8aSnia#endif /* EWMH */
8160bbfda8aSnia}
8170bbfda8aSnia
8180bbfda8aSnia
8190bbfda8aSniavoid OtpTinyLower(TwmWindow *twm_win, WinType wintype)
8200bbfda8aSnia{
8210bbfda8aSnia	OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
8220bbfda8aSnia	assert(owl != NULL);
8230bbfda8aSnia
8240bbfda8aSnia	TinyLowerOwl(owl);
8250bbfda8aSnia
8260bbfda8aSnia	OtpCheckConsistency();
8270bbfda8aSnia#ifdef EWMH
8280bbfda8aSnia	EwmhSet_NET_CLIENT_LIST_STACKING();
8290bbfda8aSnia#endif /* EWMH */
8300bbfda8aSnia}
8310bbfda8aSnia
8320bbfda8aSnia
8330bbfda8aSnia/*
8340bbfda8aSnia * XCirculateSubwindows() is complicated by the fact that it restacks only
8350bbfda8aSnia * in case of overlapping windows. Therefore it seems easier to not
8360bbfda8aSnia * try to emulate that but to leave it to the X server.
8370bbfda8aSnia *
8380bbfda8aSnia * If XCirculateSubwindows() actually does something, it sends a
8390bbfda8aSnia * CirculateNotify event, but you only receive it if
8400bbfda8aSnia * SubstructureNotifyMask is selected on the root window.
8410bbfda8aSnia * However... if that is done from the beginning, for some reason all
8420bbfda8aSnia * windows disappear when ctwm starts or exits.
8430bbfda8aSnia * Maybe SubstructureNotifyMask interferes with SubstructureRedirectMask?
8440bbfda8aSnia *
8450bbfda8aSnia * To get around that, the SubstructureNotifyMask is selected only
8460bbfda8aSnia * temporarily here when wanted.
8470bbfda8aSnia */
8480bbfda8aSnia
8490bbfda8aSniavoid OtpCirculateSubwindows(VirtualScreen *vs, int direction)
8500bbfda8aSnia{
8510bbfda8aSnia	Window w = vs->window;
8520bbfda8aSnia	XWindowAttributes winattrs;
8530bbfda8aSnia	Bool circulated;
8540bbfda8aSnia
8550bbfda8aSnia	DPRINTF((stderr, "OtpCirculateSubwindows %d\n", direction));
8560bbfda8aSnia
8570bbfda8aSnia	XGetWindowAttributes(dpy, w, &winattrs);
8580bbfda8aSnia	XSelectInput(dpy, w, winattrs.your_event_mask | SubstructureNotifyMask);
8590bbfda8aSnia	XCirculateSubwindows(dpy, w, direction);
8600bbfda8aSnia	XSelectInput(dpy, w, winattrs.your_event_mask);
8610bbfda8aSnia	/*
8620bbfda8aSnia	 * Now we should get the CirculateNotify event.
8630bbfda8aSnia	 * It usually seems to arrive soon enough, but just to be sure, look
8640bbfda8aSnia	 * ahead in the message queue to see if it can be expedited.
8650bbfda8aSnia	 */
8660bbfda8aSnia	circulated = XCheckTypedWindowEvent(dpy, w, CirculateNotify, &Event);
8670bbfda8aSnia	if(circulated) {
8680bbfda8aSnia		HandleCirculateNotify();
8690bbfda8aSnia	}
8700bbfda8aSnia}
8710bbfda8aSnia
8720bbfda8aSnia/*
8730bbfda8aSnia * Update our list of Owls after the Circulate action, and also
8740bbfda8aSnia * enforce the priority by possibly restacking the window again.
8750bbfda8aSnia */
8760bbfda8aSnia
8770bbfda8aSniavoid OtpHandleCirculateNotify(VirtualScreen *vs, TwmWindow *twm_win,
8780bbfda8aSnia                              WinType wintype, int place)
8790bbfda8aSnia{
8800bbfda8aSnia	switch(place) {
8810bbfda8aSnia		case PlaceOnTop:
8820bbfda8aSnia			OtpRaise(twm_win, wintype);
8830bbfda8aSnia			break;
8840bbfda8aSnia		case PlaceOnBottom:
8850bbfda8aSnia			OtpLower(twm_win, wintype);
8860bbfda8aSnia			break;
8870bbfda8aSnia		default:
8880bbfda8aSnia			DPRINTF((stderr, "OtpHandleCirculateNotify: place=%d\n", place));
8890bbfda8aSnia			assert(0 &&
8900bbfda8aSnia			       "OtpHandleCirculateNotify: place equals PlaceOnTop nor PlaceOnBottom");
8910bbfda8aSnia	}
8920bbfda8aSnia}
8930bbfda8aSnia
8940bbfda8aSniavoid OtpSetPriority(TwmWindow *twm_win, WinType wintype, int new_pri, int where)
8950bbfda8aSnia{
8960bbfda8aSnia	OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
8970bbfda8aSnia	int priority = OTP_ZERO + new_pri;
8980bbfda8aSnia
8990bbfda8aSnia	DPRINTF((stderr, "OtpSetPriority: new_pri=%d\n", new_pri));
9000bbfda8aSnia	assert(owl != NULL);
9010bbfda8aSnia
902b18c2d1eSnia#ifdef WINBOX
9030bbfda8aSnia	if(twm_win->winbox != NULL || twm_win->iswinbox) {
9040bbfda8aSnia		return;
9050bbfda8aSnia	}
906b18c2d1eSnia#endif
9070bbfda8aSnia
9080bbfda8aSnia	if(ABS(new_pri) > OTP_ZERO) {
9090bbfda8aSnia		DPRINTF((stderr, "invalid OnTopPriority value: %d\n", new_pri));
9100bbfda8aSnia	}
9110bbfda8aSnia	else {
9120bbfda8aSnia		TryToMoveTransientsOfTo(owl, priority, where);
9130bbfda8aSnia		SetOwlPriority(owl, priority, where);
9140bbfda8aSnia	}
9150bbfda8aSnia
9160bbfda8aSnia	OtpCheckConsistency();
9170bbfda8aSnia}
9180bbfda8aSnia
9190bbfda8aSnia
9200bbfda8aSniavoid OtpChangePriority(TwmWindow *twm_win, WinType wintype, int relpriority)
9210bbfda8aSnia{
9220bbfda8aSnia	OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
9230bbfda8aSnia	int priority = owl->pri_base + relpriority;
9240bbfda8aSnia	int where;
9250bbfda8aSnia
926b18c2d1eSnia#ifdef WINBOX
9270bbfda8aSnia	if(twm_win->winbox != NULL || twm_win->iswinbox) {
9280bbfda8aSnia		return;
9290bbfda8aSnia	}
930b18c2d1eSnia#endif
9310bbfda8aSnia
9320bbfda8aSnia	where = relpriority < 0 ? Below : Above;
9330bbfda8aSnia
9340bbfda8aSnia	TryToMoveTransientsOfTo(owl, priority, where);
9350bbfda8aSnia	SetOwlPriority(owl, priority, where);
9360bbfda8aSnia
9370bbfda8aSnia	OtpCheckConsistency();
9380bbfda8aSnia}
9390bbfda8aSnia
9400bbfda8aSnia
9410bbfda8aSniavoid OtpSwitchPriority(TwmWindow *twm_win, WinType wintype)
9420bbfda8aSnia{
9430bbfda8aSnia	OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
9440bbfda8aSnia	int priority = OTP_MAX - owl->pri_base;
9450bbfda8aSnia	int where;
9460bbfda8aSnia
9470bbfda8aSnia	assert(owl != NULL);
9480bbfda8aSnia
949b18c2d1eSnia#ifdef WINBOX
9500bbfda8aSnia	if(twm_win->winbox != NULL || twm_win->iswinbox) {
9510bbfda8aSnia		return;
9520bbfda8aSnia	}
953b18c2d1eSnia#endif
9540bbfda8aSnia
9550bbfda8aSnia	where = priority < OTP_ZERO ? Below : Above;
9560bbfda8aSnia	TryToMoveTransientsOfTo(owl, priority, where);
9570bbfda8aSnia	SetOwlPriority(owl, priority, where);
9580bbfda8aSnia
9590bbfda8aSnia	OtpCheckConsistency();
9600bbfda8aSnia}
9610bbfda8aSnia
9620bbfda8aSnia
9630bbfda8aSniavoid OtpToggleSwitching(TwmWindow *twm_win, WinType wintype)
9640bbfda8aSnia{
9650bbfda8aSnia	OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
9660bbfda8aSnia	assert(owl != NULL);
9670bbfda8aSnia
968b18c2d1eSnia#ifdef WINBOX
9690bbfda8aSnia	if(twm_win->winbox != NULL || twm_win->iswinbox) {
9700bbfda8aSnia		return;
9710bbfda8aSnia	}
972b18c2d1eSnia#endif
9730bbfda8aSnia
9740bbfda8aSnia	owl->switching = !owl->switching;
9750bbfda8aSnia
9760bbfda8aSnia	OtpCheckConsistency();
9770bbfda8aSnia}
9780bbfda8aSnia
9790bbfda8aSnia
9800bbfda8aSnia/*
9810bbfda8aSnia * This is triggered as a result of a StackMode ConfigureRequest.  We
9820bbfda8aSnia * choose to interpret this as restacking relative to the base
9830bbfda8aSnia * priorities, since all the alterations are EWMH-related, and those
9840bbfda8aSnia * should probably override.
9850bbfda8aSnia *
9860bbfda8aSnia * XXX Or should they?  Maybe we should alter until our effective is
9870bbfda8aSnia * positioned as desired relative to their effective?  This may also need
9880bbfda8aSnia * revisiting if we grow alterations that aren't a result of EWMH stuff.
9890bbfda8aSnia */
9900bbfda8aSniavoid OtpForcePlacement(TwmWindow *twm_win, int where, TwmWindow *other_win)
9910bbfda8aSnia{
9920bbfda8aSnia	OtpWinList *owl = twm_win->otp;
9930bbfda8aSnia	OtpWinList *other_owl = other_win->otp;
9940bbfda8aSnia
9950bbfda8aSnia	assert(twm_win->otp != NULL);
9960bbfda8aSnia	assert(other_win->otp != NULL);
9970bbfda8aSnia
9980bbfda8aSnia	if(where == BottomIf) {
9990bbfda8aSnia		where = Below;
10000bbfda8aSnia	}
10010bbfda8aSnia	if(where != Below) {
10020bbfda8aSnia		where = Above;
10030bbfda8aSnia	}
10040bbfda8aSnia
10050bbfda8aSnia	/* remove the owl to change it */
10060bbfda8aSnia	RemoveOwl(owl);
10070bbfda8aSnia
10080bbfda8aSnia	/*
10090bbfda8aSnia	 * Base our priority base off that other win.  Don't use PRI_CP since
10100bbfda8aSnia	 * we shouldn't suddenly get its flags as well.
10110bbfda8aSnia	 */
10120bbfda8aSnia	owl->pri_base = other_owl->pri_base;
10130bbfda8aSnia
10140bbfda8aSnia	/* put the owl back into the list */
10150bbfda8aSnia	if(where == Below) {
10160bbfda8aSnia		other_owl = other_owl->below;
10170bbfda8aSnia	}
10180bbfda8aSnia	InsertOwlAbove(owl, other_owl);
10190bbfda8aSnia
10200bbfda8aSnia	OtpCheckConsistency();
10210bbfda8aSnia}
10220bbfda8aSnia
10230bbfda8aSnia
10240bbfda8aSniastatic void ApplyPreferences(OtpPreferences *prefs, OtpWinList *owl)
10250bbfda8aSnia{
10260bbfda8aSnia	int i;
10270bbfda8aSnia	TwmWindow *twm_win = owl->twm_win;
10280bbfda8aSnia
10290bbfda8aSnia	/* check PrioritySwitch */
10300bbfda8aSnia	if(LookInList(prefs->switchingL, twm_win->name, &twm_win->class)) {
10310bbfda8aSnia		owl->switching = !prefs->switching;
10320bbfda8aSnia	}
10330bbfda8aSnia
10340bbfda8aSnia	/* check OnTopPriority */
10350bbfda8aSnia	for(i = 0; i <= OTP_MAX; i++) {
10360bbfda8aSnia		if(LookInList(prefs->priorityL[i],
10370bbfda8aSnia		                twm_win->name, &twm_win->class)) {
10380bbfda8aSnia			owl->pri_base = i;
10390bbfda8aSnia		}
10400bbfda8aSnia	}
10410bbfda8aSnia}
10420bbfda8aSnia
10430bbfda8aSnia
10440bbfda8aSnia/*
10450bbfda8aSnia * Reset stuff based on preferences; called during property changes if
10460bbfda8aSnia * AutoPriority set.
10470bbfda8aSnia */
10480bbfda8aSniastatic void RecomputeOwlPrefs(OtpPreferences *prefs, OtpWinList *owl)
10490bbfda8aSnia{
10500bbfda8aSnia	int old_pri;
10510bbfda8aSnia
10520bbfda8aSnia	old_pri = owl->pri_base;
10530bbfda8aSnia	ApplyPreferences(prefs, owl);
10540bbfda8aSnia	if(old_pri != owl->pri_base) {
10550bbfda8aSnia		RemoveOwl(owl);
10560bbfda8aSnia		InsertOwl(owl, Above);
10570bbfda8aSnia
10580bbfda8aSnia		/*
10590bbfda8aSnia		 * Stash flags if we don't have any yet, since we just changed
10600bbfda8aSnia		 * the priority.
10610bbfda8aSnia		 */
10620bbfda8aSnia		if(!owl->stashed_aflags) {
10630bbfda8aSnia			OwlStashAflags(owl);
10640bbfda8aSnia		}
10650bbfda8aSnia
10660bbfda8aSnia#ifdef EWMH
10670bbfda8aSnia		/* Let other things know we did something with stacking */
10680bbfda8aSnia		EwmhSet_NET_WM_STATE(owl->twm_win, EWMH_STATE_ABOVE);
10690bbfda8aSnia#endif
10700bbfda8aSnia	}
10710bbfda8aSnia}
10720bbfda8aSnia
10730bbfda8aSniavoid OtpRecomputePrefs(TwmWindow *twm_win)
10740bbfda8aSnia{
10750bbfda8aSnia	assert(twm_win->otp != NULL);
10760bbfda8aSnia
10770bbfda8aSnia	RecomputeOwlPrefs(Scr->OTP, twm_win->otp);
10780bbfda8aSnia	if(twm_win->icon != NULL) {
10790bbfda8aSnia		RecomputeOwlPrefs(Scr->IconOTP, twm_win->icon->otp);
10800bbfda8aSnia	}
10810bbfda8aSnia
10820bbfda8aSnia	OtpCheckConsistency();
10830bbfda8aSnia}
10840bbfda8aSnia
10850bbfda8aSnia
10860bbfda8aSniastatic void free_OtpWinList(OtpWinList *owl)
10870bbfda8aSnia{
10880bbfda8aSnia	assert(owl->above == NULL);
10890bbfda8aSnia	assert(owl->below == NULL);
10900bbfda8aSnia	free(owl);
10910bbfda8aSnia}
10920bbfda8aSnia
10930bbfda8aSnia
10940bbfda8aSniavoid OtpRemove(TwmWindow *twm_win, WinType wintype)
10950bbfda8aSnia{
10960bbfda8aSnia	OtpWinList **owlp;
10970bbfda8aSnia	owlp = (wintype == IconWin) ? &twm_win->icon->otp : &twm_win->otp;
10980bbfda8aSnia
10990bbfda8aSnia	assert(*owlp != NULL);
11000bbfda8aSnia
11010bbfda8aSnia	RemoveOwl(*owlp);
11020bbfda8aSnia	free_OtpWinList(*owlp);
11030bbfda8aSnia	*owlp = NULL;
11040bbfda8aSnia
11050bbfda8aSnia	OtpCheckConsistency();
11060bbfda8aSnia}
11070bbfda8aSnia
11080bbfda8aSnia
11090bbfda8aSniastatic OtpWinList *new_OtpWinList(TwmWindow *twm_win,
11100bbfda8aSnia                                  WinType wintype,
11110bbfda8aSnia                                  bool switching,
11120bbfda8aSnia                                  int priority)
11130bbfda8aSnia{
11140bbfda8aSnia	OtpWinList *owl = malloc(sizeof(OtpWinList));
11150bbfda8aSnia
11160bbfda8aSnia	owl->above = NULL;
11170bbfda8aSnia	owl->below = NULL;
11180bbfda8aSnia	owl->twm_win = twm_win;
11190bbfda8aSnia	owl->type = wintype;
11200bbfda8aSnia	owl->switching = switching;
11210bbfda8aSnia	owl->pri_base = priority;
11220bbfda8aSnia	owl->pri_aflags = 0;
11230bbfda8aSnia
11240bbfda8aSnia	/*
11250bbfda8aSnia	 * We never need to stash anything for icons, they don't persist
11260bbfda8aSnia	 * across restart anyway.  So pretend we did stash already to
11270bbfda8aSnia	 * discourage other code from trying to stash.
11280bbfda8aSnia	 */
11290bbfda8aSnia	owl->stashed_aflags = (wintype == WinWin ? false : true);
11300bbfda8aSnia
11310bbfda8aSnia	return owl;
11320bbfda8aSnia}
11330bbfda8aSnia
11340bbfda8aSniastatic OtpWinList *AddNewOwl(TwmWindow *twm_win, WinType wintype,
11350bbfda8aSnia                             OtpWinList *parent)
11360bbfda8aSnia{
11370bbfda8aSnia	OtpWinList *owl;
11380bbfda8aSnia	OtpPreferences *prefs = (wintype == IconWin) ? Scr->IconOTP : Scr->OTP;
11390bbfda8aSnia
11400bbfda8aSnia	/* make the new owl */
11410bbfda8aSnia	owl = new_OtpWinList(twm_win, wintype,
11420bbfda8aSnia	                     prefs->switching, prefs->priority);
11430bbfda8aSnia
11440bbfda8aSnia	/* inherit the default attributes from the parent window if appropriate */
11450bbfda8aSnia	if(parent != NULL) {
11460bbfda8aSnia		PRI_CP(parent, owl);
11470bbfda8aSnia		owl->switching = parent->switching;
11480bbfda8aSnia	}
11490bbfda8aSnia
11500bbfda8aSnia	/* now see if the preferences have something to say */
11510bbfda8aSnia	if(!(parent != NULL && twm_win->istransient)) {
11520bbfda8aSnia		ApplyPreferences(prefs, owl);
11530bbfda8aSnia	}
11540bbfda8aSnia
11550bbfda8aSnia#ifdef EWMH
11560bbfda8aSnia	/* If nothing came in, EWMH might have something to say */
11570bbfda8aSnia	if(owl->pri_base == 0) {
11580bbfda8aSnia		owl->pri_base = EwmhGetInitPriority(twm_win) + OTP_ZERO;
11590bbfda8aSnia	}
11600bbfda8aSnia#endif
11610bbfda8aSnia
11620bbfda8aSnia	/*
11630bbfda8aSnia	 * Initialize flags.  Right now, the only stashed flags are related
11640bbfda8aSnia	 * to EWMH requests, so in a sense this whole thing could be dropped
11650bbfda8aSnia	 * under #ifdef.  But I'll assume that might not always be the case,
11660bbfda8aSnia	 * so for now the !(EWMH) case is just a twisty noop.
11670bbfda8aSnia	 */
11680bbfda8aSnia	{
11690bbfda8aSnia		bool gotflags = false;
11700bbfda8aSnia		unsigned aflags = 0, fromstash = 0;
11710bbfda8aSnia
11720bbfda8aSnia		aflags = OwlGetStashedAflags(owl, &gotflags);
11730bbfda8aSnia
11740bbfda8aSnia#ifdef EWMH
11750bbfda8aSnia		fromstash = (OTP_AFLAG_ABOVE | OTP_AFLAG_BELOW);
11760bbfda8aSnia#endif
11770bbfda8aSnia
11780bbfda8aSnia		if(gotflags) {
11790bbfda8aSnia			/*
11800bbfda8aSnia			 * Got stashed OTP flags; use 'em.  Explicitly mask in only
11810bbfda8aSnia			 * the flags we're caring about; the others aren't telling us
11820bbfda8aSnia			 * info we need to persist.
11830bbfda8aSnia			 */
11840bbfda8aSnia			aflags &= fromstash;
11850bbfda8aSnia		}
11860bbfda8aSnia
11870bbfda8aSnia#ifdef EWMH
11880bbfda8aSnia		/* FULLSCREEN we get from the normal EWMH prop no matter what */
11890bbfda8aSnia		if(twm_win->ewmhFlags & EWMH_STATE_FULLSCREEN) {
11900bbfda8aSnia			aflags |= OTP_AFLAG_FULLSCREEN;
11910bbfda8aSnia		}
11920bbfda8aSnia
11930bbfda8aSnia		if(!gotflags) {
11940bbfda8aSnia			/* Nothing from OTP about above/below; check EWMH */
11950bbfda8aSnia			aflags = 0;
11960bbfda8aSnia			if(twm_win->ewmhFlags & EWMH_STATE_ABOVE) {
11970bbfda8aSnia				aflags |= OTP_AFLAG_ABOVE;
11980bbfda8aSnia			}
11990bbfda8aSnia			if(twm_win->ewmhFlags & EWMH_STATE_BELOW) {
12000bbfda8aSnia				aflags |= OTP_AFLAG_BELOW;
12010bbfda8aSnia			}
12020bbfda8aSnia		}
12030bbfda8aSnia#endif // EWMH
12040bbfda8aSnia
12050bbfda8aSnia		/* Set whatever we figured */
12060bbfda8aSnia		owl->pri_aflags |= aflags;
12070bbfda8aSnia		owl->stashed_aflags = gotflags;
12080bbfda8aSnia
12090bbfda8aSnia		/* If we set a priority or flags, we should stash away flags */
12100bbfda8aSnia		if((PRI(owl) != OTP_ZERO || owl->pri_aflags != 0)
12110bbfda8aSnia		                && !owl->stashed_aflags) {
12120bbfda8aSnia			OwlStashAflags(owl);
12130bbfda8aSnia		}
12140bbfda8aSnia	}
12150bbfda8aSnia
12160bbfda8aSnia	/* finally put the window where it should go */
12170bbfda8aSnia	InsertOwl(owl, Above);
12180bbfda8aSnia
12190bbfda8aSnia	return owl;
12200bbfda8aSnia}
12210bbfda8aSnia
12220bbfda8aSniavoid OtpAdd(TwmWindow *twm_win, WinType wintype)
12230bbfda8aSnia{
12240bbfda8aSnia	TwmWindow *other_win;
12250bbfda8aSnia	OtpWinList *parent = NULL;
12260bbfda8aSnia	OtpWinList **owlp;
12270bbfda8aSnia	owlp = (wintype == IconWin) ? &twm_win->icon->otp : &twm_win->otp;
12280bbfda8aSnia
12290bbfda8aSnia	assert(*owlp == NULL);
12300bbfda8aSnia
1231b18c2d1eSnia	if(false) {
1232b18c2d1eSnia		// dummy
1233b18c2d1eSnia	}
1234b18c2d1eSnia#ifdef WINBOX
1235b18c2d1eSnia	else if(twm_win->winbox) {
1236b18c2d1eSnia		/* windows in boxes *must* inherit priority from the box */
12370bbfda8aSnia		parent = twm_win->winbox->twmwin->otp;
12380bbfda8aSnia		parent->switching = false;
12390bbfda8aSnia	}
1240b18c2d1eSnia#endif
12410bbfda8aSnia	/* in case it's a transient, find the parent */
12420bbfda8aSnia	else if(wintype == WinWin && (twm_win->istransient
12430bbfda8aSnia	                              || !isGroupLeader(twm_win))) {
12440bbfda8aSnia		other_win = Scr->FirstWindow;
12450bbfda8aSnia		while(other_win != NULL
12460bbfda8aSnia		                && !isTransientOf(twm_win, other_win)
12470bbfda8aSnia		                && !isGroupLeaderOf(other_win, twm_win)) {
12480bbfda8aSnia			other_win = other_win->next;
12490bbfda8aSnia		}
12500bbfda8aSnia		if(other_win != NULL) {
12510bbfda8aSnia			parent = other_win->otp;
12520bbfda8aSnia		}
12530bbfda8aSnia	}
12540bbfda8aSnia
12550bbfda8aSnia	/* make the new owl */
12560bbfda8aSnia	*owlp = AddNewOwl(twm_win, wintype, parent);
12570bbfda8aSnia
12580bbfda8aSnia	assert(*owlp != NULL);
12590bbfda8aSnia	OtpCheckConsistency();
12600bbfda8aSnia}
12610bbfda8aSnia
12620bbfda8aSniavoid OtpReassignIcon(TwmWindow *twm_win, Icon *old_icon)
12630bbfda8aSnia{
12640bbfda8aSnia	if(old_icon != NULL) {
12650bbfda8aSnia		/* Transfer OWL to new Icon */
12660bbfda8aSnia		Icon *new_icon = twm_win->icon;
12670bbfda8aSnia		if(new_icon != NULL) {
12680bbfda8aSnia			new_icon->otp = old_icon->otp;
12690bbfda8aSnia			old_icon->otp = NULL;
12700bbfda8aSnia		}
12710bbfda8aSnia	}
12720bbfda8aSnia	else {
12730bbfda8aSnia		/* Create a new OWL for this Icon */
12740bbfda8aSnia		OtpAdd(twm_win, IconWin);
12750bbfda8aSnia	}
12760bbfda8aSnia}
12770bbfda8aSnia
12780bbfda8aSniavoid OtpFreeIcon(TwmWindow *twm_win)
12790bbfda8aSnia{
12800bbfda8aSnia	/* Remove the icon's OWL, if any */
12810bbfda8aSnia	Icon *cur_icon = twm_win->icon;
12820bbfda8aSnia	if(cur_icon != NULL) {
12830bbfda8aSnia		OtpRemove(twm_win, IconWin);
12840bbfda8aSnia	}
12850bbfda8aSnia}
12860bbfda8aSnia
12870bbfda8aSnianame_list **OtpScrSwitchingL(ScreenInfo *scr, WinType wintype)
12880bbfda8aSnia{
12890bbfda8aSnia	OtpPreferences *prefs = (wintype == IconWin) ? scr->IconOTP : scr->OTP;
12900bbfda8aSnia
12910bbfda8aSnia	assert(prefs != NULL);
12920bbfda8aSnia
12930bbfda8aSnia	return &(prefs->switchingL);
12940bbfda8aSnia}
12950bbfda8aSnia
12960bbfda8aSnia
12970bbfda8aSniavoid OtpScrSetSwitching(ScreenInfo *scr, WinType wintype, bool switching)
12980bbfda8aSnia{
12990bbfda8aSnia#ifndef NDEBUG
13000bbfda8aSnia	OtpPreferences *prefs = (wintype == IconWin) ? scr->IconOTP : scr->OTP;
13010bbfda8aSnia
13020bbfda8aSnia	assert(prefs != NULL);
13030bbfda8aSnia#endif
13040bbfda8aSnia
13050bbfda8aSnia	scr->OTP->switching = switching;
13060bbfda8aSnia}
13070bbfda8aSnia
13080bbfda8aSnia
13090bbfda8aSniavoid OtpScrSetZero(ScreenInfo *scr, WinType wintype, int priority)
13100bbfda8aSnia{
13110bbfda8aSnia	OtpPreferences *prefs = (wintype == IconWin) ? scr->IconOTP : scr->OTP;
13120bbfda8aSnia
13130bbfda8aSnia	assert(prefs != NULL);
13140bbfda8aSnia
13150bbfda8aSnia	if(ABS(priority) > OTP_ZERO) {
13160bbfda8aSnia		fprintf(stderr, "invalid OnTopPriority value: %d\n", priority);
13170bbfda8aSnia		return;
13180bbfda8aSnia	}
13190bbfda8aSnia
13200bbfda8aSnia	prefs->priority = priority + OTP_ZERO;
13210bbfda8aSnia}
13220bbfda8aSnia
13230bbfda8aSnia
13240bbfda8aSnianame_list **OtpScrPriorityL(ScreenInfo *scr, WinType wintype, int priority)
13250bbfda8aSnia{
13260bbfda8aSnia	OtpPreferences *prefs = (wintype == IconWin) ? scr->IconOTP : scr->OTP;
13270bbfda8aSnia
13280bbfda8aSnia	assert(prefs != NULL);
13290bbfda8aSnia
13300bbfda8aSnia	if(ABS(priority) > OTP_ZERO) {
13310bbfda8aSnia		fprintf(stderr, "invalid OnTopPriority value: %d\n", priority);
13320bbfda8aSnia		priority = 0;
13330bbfda8aSnia	}
13340bbfda8aSnia	return &(prefs->priorityL[priority + OTP_ZERO]);
13350bbfda8aSnia}
13360bbfda8aSnia
13370bbfda8aSnia
13380bbfda8aSniastatic OtpPreferences *new_OtpPreferences(void)
13390bbfda8aSnia{
13400bbfda8aSnia	OtpPreferences *pref = malloc(sizeof(OtpPreferences));
13410bbfda8aSnia	int i;
13420bbfda8aSnia
13430bbfda8aSnia	/* initialize default values */
13440bbfda8aSnia	for(i = 0; i <= OTP_MAX; i++) {
13450bbfda8aSnia		pref->priorityL[i] = NULL;
13460bbfda8aSnia	}
13470bbfda8aSnia	pref->priority = OTP_ZERO;
13480bbfda8aSnia	pref->switchingL = NULL;
13490bbfda8aSnia	pref->switching = false;
13500bbfda8aSnia
13510bbfda8aSnia	return pref;
13520bbfda8aSnia}
13530bbfda8aSnia
13540bbfda8aSniastatic void free_OtpPreferences(OtpPreferences *pref)
13550bbfda8aSnia{
13560bbfda8aSnia	int i;
13570bbfda8aSnia
13580bbfda8aSnia	FreeList(&pref->switchingL);
13590bbfda8aSnia	for(i = 0; i <= OTP_MAX; i++) {
13600bbfda8aSnia		FreeList(&pref->priorityL[i]);
13610bbfda8aSnia	}
13620bbfda8aSnia
13630bbfda8aSnia	free(pref);
13640bbfda8aSnia}
13650bbfda8aSnia
13660bbfda8aSniavoid OtpScrInitData(ScreenInfo *scr)
13670bbfda8aSnia{
13680bbfda8aSnia	if(scr->OTP != NULL) {
13690bbfda8aSnia		free_OtpPreferences(scr->OTP);
13700bbfda8aSnia	}
13710bbfda8aSnia	if(scr->IconOTP != NULL) {
13720bbfda8aSnia		free_OtpPreferences(scr->IconOTP);
13730bbfda8aSnia	}
13740bbfda8aSnia	scr->OTP = new_OtpPreferences();
13750bbfda8aSnia	scr->IconOTP = new_OtpPreferences();
13760bbfda8aSnia}
13770bbfda8aSnia
13780bbfda8aSniaint ReparentWindow(Display *display, TwmWindow *twm_win, WinType wintype,
13790bbfda8aSnia                   Window parent, int x, int y)
13800bbfda8aSnia{
13810bbfda8aSnia	int result;
13820bbfda8aSnia	OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
13830bbfda8aSnia	OtpWinList *other = owl->below;
13840bbfda8aSnia	assert(owl != NULL);
13850bbfda8aSnia
13860bbfda8aSnia	DPRINTF((stderr, "ReparentWindow: w=%x type=%d\n",
13870bbfda8aSnia	         (unsigned int)WindowOfOwl(owl), wintype));
13880bbfda8aSnia	result = XReparentWindow(display, WindowOfOwl(owl), parent, x, y);
13890bbfda8aSnia	/* The raise was already done by XReparentWindow, so this call
13900bbfda8aSnia	   just re-places the window at the right spot in the list
13910bbfda8aSnia	   and enforces priority settings. */
13920bbfda8aSnia	RemoveOwl(owl);
13930bbfda8aSnia	InsertOwlAbove(owl, other);
13940bbfda8aSnia	OtpCheckConsistency();
13950bbfda8aSnia	return result;
13960bbfda8aSnia}
13970bbfda8aSnia
13980bbfda8aSniavoid
13990bbfda8aSniaReparentWindowAndIcon(Display *display, TwmWindow *twm_win,
14000bbfda8aSnia                      Window parent, int win_x, int win_y,
14010bbfda8aSnia                      int icon_x, int icon_y)
14020bbfda8aSnia{
14030bbfda8aSnia	OtpWinList *win_owl = twm_win->otp;
14040bbfda8aSnia	assert(twm_win->icon != NULL);
14050bbfda8aSnia	OtpWinList *icon_owl = twm_win->icon->otp;
14060bbfda8aSnia	assert(win_owl != NULL);
14070bbfda8aSnia	assert(icon_owl != NULL);
14080bbfda8aSnia	OtpWinList *below_win = win_owl->below;
14090bbfda8aSnia	OtpWinList *below_icon = icon_owl->below;
14100bbfda8aSnia
14110bbfda8aSnia	DPRINTF((stderr, "ReparentWindowAndIcon %x\n", (unsigned int)twm_win->frame));
14120bbfda8aSnia	XReparentWindow(display, twm_win->frame, parent, win_x, win_y);
14130bbfda8aSnia	XReparentWindow(display, twm_win->icon->w, parent, icon_x, icon_y);
14140bbfda8aSnia	/* The raise was already done by XReparentWindow, so this call
14150bbfda8aSnia	   just re-places the window at the right spot in the list
14160bbfda8aSnia	   and enforces priority settings. */
14170bbfda8aSnia	RemoveOwl(win_owl);
14180bbfda8aSnia	RemoveOwl(icon_owl);
14190bbfda8aSnia	if(below_win != icon_owl) {
14200bbfda8aSnia		/*
14210bbfda8aSnia		 * Only insert the window above something if it isn't the icon,
14220bbfda8aSnia		 * because that isn't back yet.
14230bbfda8aSnia		 */
14240bbfda8aSnia		InsertOwlAbove(win_owl, below_win);
14250bbfda8aSnia		InsertOwlAbove(icon_owl, below_icon);
14260bbfda8aSnia	}
14270bbfda8aSnia	else {
14280bbfda8aSnia		/* In such a case, do it in the opposite order. */
14290bbfda8aSnia		InsertOwlAbove(icon_owl, below_icon);
14300bbfda8aSnia		InsertOwlAbove(win_owl, below_win);
14310bbfda8aSnia	}
14320bbfda8aSnia	OtpCheckConsistency();
14330bbfda8aSnia	return;
14340bbfda8aSnia}
14350bbfda8aSnia
14360bbfda8aSnia/* Iterators.  */
1437b18c2d1eSniaTwmWindow *OtpBottomWin(void)
14380bbfda8aSnia{
14390bbfda8aSnia	OtpWinList *owl = Scr->bottomOwl;
14400bbfda8aSnia	while(owl && owl->type != WinWin) {
14410bbfda8aSnia		owl = owl->above;
14420bbfda8aSnia	}
14430bbfda8aSnia	return owl ? owl->twm_win : NULL;
14440bbfda8aSnia}
14450bbfda8aSnia
1446b18c2d1eSniaTwmWindow *OtpTopWin(void)
14470bbfda8aSnia{
14480bbfda8aSnia	OtpWinList *owl = Scr->bottomOwl, *top = NULL;
14490bbfda8aSnia	while(owl) {
14500bbfda8aSnia		if(owl->type == WinWin) {
14510bbfda8aSnia			top = owl;
14520bbfda8aSnia		}
14530bbfda8aSnia		owl = owl->above;
14540bbfda8aSnia	}
14550bbfda8aSnia	return top ? top->twm_win : NULL;
14560bbfda8aSnia}
14570bbfda8aSnia
14580bbfda8aSniaTwmWindow *OtpNextWinUp(TwmWindow *twm_win)
14590bbfda8aSnia{
14600bbfda8aSnia	OtpWinList *owl = twm_win->otp->above;
14610bbfda8aSnia	while(owl && owl->type != WinWin) {
14620bbfda8aSnia		owl = owl->above;
14630bbfda8aSnia	}
14640bbfda8aSnia	return owl ? owl->twm_win : NULL;
14650bbfda8aSnia}
14660bbfda8aSnia
14670bbfda8aSniaTwmWindow *OtpNextWinDown(TwmWindow *twm_win)
14680bbfda8aSnia{
14690bbfda8aSnia	OtpWinList *owl = twm_win->otp->below;
14700bbfda8aSnia	while(owl && owl->type != WinWin) {
14710bbfda8aSnia		owl = owl->below;
14720bbfda8aSnia	}
14730bbfda8aSnia	return owl ? owl->twm_win : NULL;
14740bbfda8aSnia}
14750bbfda8aSnia
14760bbfda8aSnia
1477b18c2d1eSnia
1478b18c2d1eSnia/*
1479b18c2d1eSnia * Outputting info to understand the state of OTP stuff.
1480b18c2d1eSnia */
1481b18c2d1eSnia
1482b18c2d1eSnia/// Pretty-print a whole OWL stack.  Works upward from the arg;
1483b18c2d1eSnia/// generally, you'd call this with Scr->bottomOwl.
1484b18c2d1eSniastatic void
1485b18c2d1eSniaOwlPrettyPrint(const OtpWinList *start)
1486b18c2d1eSnia{
1487b18c2d1eSnia	fprintf(stderr, "%s():\n", __func__);
1488b18c2d1eSnia
1489b18c2d1eSnia	for(const OtpWinList *owl = start ; owl != NULL ; owl = owl->above) {
1490b18c2d1eSnia		fprintf(stderr, "  pri=%2d (%+d) %s 0x%lx:'%1.50s'\n",
1491b18c2d1eSnia		        OtpEffectivePriority(owl->twm_win),
1492b18c2d1eSnia		        OtpEffectiveDisplayPriority(owl->twm_win),
1493b18c2d1eSnia		        (owl->type == WinWin ? "win" : "ico"),
1494b18c2d1eSnia		        owl->twm_win->w, owl->twm_win->name);
1495b18c2d1eSnia		fprintf(stderr, "         basepri=%d %s%s%s\n",
1496b18c2d1eSnia		        owl->pri_base,
1497b18c2d1eSnia#ifdef EWMH
1498b18c2d1eSnia		        (owl->pri_aflags & OTP_AFLAG_ABOVE ? " _ABOVE" : ""),
1499b18c2d1eSnia		        (owl->pri_aflags & OTP_AFLAG_BELOW ? " _BELOW" : ""),
1500b18c2d1eSnia		        (owl->pri_aflags & OTP_AFLAG_FULLSCREEN ? " _FULLSCREEN" : "")
1501b18c2d1eSnia#else
1502b18c2d1eSnia		        "", "", ""
1503b18c2d1eSnia#endif
1504b18c2d1eSnia		       );
1505b18c2d1eSnia		if(owl->twm_win->istransient) {
1506b18c2d1eSnia			const TwmWindow *parent = GetTwmWindow(owl->twm_win->transientfor);
1507b18c2d1eSnia			fprintf(stderr, "         transient for 0x%lx:%1.50s\n",
1508b18c2d1eSnia			        parent->w, parent->name);
1509b18c2d1eSnia		}
1510b18c2d1eSnia	}
1511b18c2d1eSnia
1512b18c2d1eSnia	fprintf(stderr, "  Done.\n");
1513b18c2d1eSnia}
1514b18c2d1eSnia
1515b18c2d1eSnia
1516b18c2d1eSnia
15170bbfda8aSnia/*
15180bbfda8aSnia * Stuff for messing with pri_aflags
15190bbfda8aSnia */
15200bbfda8aSnia/* Set the masked bits to exactly what's given */
15210bbfda8aSniavoid
15220bbfda8aSniaOtpSetAflagMask(TwmWindow *twm_win, unsigned mask, unsigned setto)
15230bbfda8aSnia{
15240bbfda8aSnia	assert(twm_win != NULL);
15250bbfda8aSnia	assert(twm_win->otp != NULL);
15260bbfda8aSnia	OwlSetAflagMask(twm_win->otp, mask, setto);
15270bbfda8aSnia}
15280bbfda8aSnia
15290bbfda8aSniastatic void
15300bbfda8aSniaOwlSetAflagMask(OtpWinList *owl, unsigned mask, unsigned setto)
15310bbfda8aSnia{
15320bbfda8aSnia	assert(owl != NULL);
15330bbfda8aSnia
15340bbfda8aSnia	owl->pri_aflags &= ~mask;
15350bbfda8aSnia	owl->pri_aflags |= (setto & mask);
15360bbfda8aSnia	OwlStashAflags(owl);
15370bbfda8aSnia}
15380bbfda8aSnia
15390bbfda8aSnia/* Set/clear individual ones */
15400bbfda8aSniavoid
15410bbfda8aSniaOtpSetAflag(TwmWindow *twm_win, unsigned flag)
15420bbfda8aSnia{
15430bbfda8aSnia	assert(twm_win != NULL);
15440bbfda8aSnia	assert(twm_win->otp != NULL);
15450bbfda8aSnia
15460bbfda8aSnia	OwlSetAflag(twm_win->otp, flag);
15470bbfda8aSnia}
15480bbfda8aSnia
15490bbfda8aSniastatic void
15500bbfda8aSniaOwlSetAflag(OtpWinList *owl, unsigned flag)
15510bbfda8aSnia{
15520bbfda8aSnia	assert(owl != NULL);
15530bbfda8aSnia
15540bbfda8aSnia	owl->pri_aflags |= flag;
15550bbfda8aSnia	OwlStashAflags(owl);
15560bbfda8aSnia}
15570bbfda8aSnia
15580bbfda8aSniavoid
15590bbfda8aSniaOtpClearAflag(TwmWindow *twm_win, unsigned flag)
15600bbfda8aSnia{
15610bbfda8aSnia	assert(twm_win != NULL);
15620bbfda8aSnia	assert(twm_win->otp != NULL);
15630bbfda8aSnia
15640bbfda8aSnia	OwlClearAflag(twm_win->otp, flag);
15650bbfda8aSnia}
15660bbfda8aSnia
15670bbfda8aSniastatic void
15680bbfda8aSniaOwlClearAflag(OtpWinList *owl, unsigned flag)
15690bbfda8aSnia{
15700bbfda8aSnia	assert(owl != NULL);
15710bbfda8aSnia
15720bbfda8aSnia	owl->pri_aflags &= ~flag;
15730bbfda8aSnia	OwlStashAflags(owl);
15740bbfda8aSnia}
15750bbfda8aSnia
15760bbfda8aSnia/*
15770bbfda8aSnia * Stash up flags in a property.  We use this to keep track of whether we
15780bbfda8aSnia * have above/below flags set in the OTP info, so we can know what to set
15790bbfda8aSnia * when we restart.  Otherwise we can't tell whether stuff like EWMH
15800bbfda8aSnia * _NET_WM_STATE flags are saying 'above' because the above flag got set
15810bbfda8aSnia * at some point, or whether other OTP config happens to have already
15820bbfda8aSnia * raised it.
15830bbfda8aSnia */
15840bbfda8aSniavoid
15850bbfda8aSniaOtpStashAflagsFirstTime(TwmWindow *twm_win)
15860bbfda8aSnia{
15870bbfda8aSnia	if(!twm_win->otp->stashed_aflags) {
15880bbfda8aSnia		OwlStashAflags(twm_win->otp);
15890bbfda8aSnia	}
15900bbfda8aSnia}
15910bbfda8aSnia
15920bbfda8aSniastatic void
15930bbfda8aSniaOwlStashAflags(OtpWinList *owl)
15940bbfda8aSnia{
15950bbfda8aSnia	unsigned long of_prop = owl->pri_aflags;
15960bbfda8aSnia
15970bbfda8aSnia	/* Only "real" windows need stashed flags */
15980bbfda8aSnia	if(owl->type != WinWin) {
15990bbfda8aSnia		return;
16000bbfda8aSnia	}
16010bbfda8aSnia
16020bbfda8aSnia	XChangeProperty(dpy, owl->twm_win->w, XA_CTWM_OTP_AFLAGS, XA_INTEGER,
16030bbfda8aSnia	                32, PropModeReplace, (unsigned char *)&of_prop, 1);
16040bbfda8aSnia
16050bbfda8aSnia	owl->stashed_aflags = true;
16060bbfda8aSnia}
16070bbfda8aSnia
16080bbfda8aSniastatic unsigned
16090bbfda8aSniaOwlGetStashedAflags(OtpWinList *owl, bool *gotit)
16100bbfda8aSnia{
16110bbfda8aSnia	/* Lotta dummy args */
16120bbfda8aSnia	int ret;
16130bbfda8aSnia	Atom act_type;
16140bbfda8aSnia	int d_fmt;
16150bbfda8aSnia	unsigned long nitems, d_after;
16160bbfda8aSnia	unsigned long aflags, *aflags_p;
16170bbfda8aSnia
16180bbfda8aSnia	/* Only on real windows */
16190bbfda8aSnia	if(owl->type != WinWin) {
16200bbfda8aSnia		*gotit = false;
16210bbfda8aSnia		return 0;
16220bbfda8aSnia	}
16230bbfda8aSnia
16240bbfda8aSnia	ret = XGetWindowProperty(dpy, owl->twm_win->w, XA_CTWM_OTP_AFLAGS, 0, 1,
16250bbfda8aSnia	                         False, XA_INTEGER, &act_type, &d_fmt, &nitems,
16260bbfda8aSnia	                         &d_after, (unsigned char **)&aflags_p);
16270bbfda8aSnia	if(ret == Success && act_type == XA_INTEGER && aflags_p != NULL) {
16280bbfda8aSnia		aflags = *aflags_p;
16290bbfda8aSnia		XFree(aflags_p);
16300bbfda8aSnia		*gotit = true;
16310bbfda8aSnia	}
16320bbfda8aSnia	else {
16330bbfda8aSnia		*gotit = false;
16340bbfda8aSnia		aflags = 0;
16350bbfda8aSnia	}
16360bbfda8aSnia
16370bbfda8aSnia	return aflags;
16380bbfda8aSnia}
16390bbfda8aSnia
16400bbfda8aSnia
16410bbfda8aSnia/*
16420bbfda8aSnia * Figure where a window should be stacked based on the current world,
16430bbfda8aSnia * and move it there.  This function pretty much assumes it's not already
16440bbfda8aSnia * there; callers should generally be figuring that out before calling
16450bbfda8aSnia * this.
16460bbfda8aSnia */
16470bbfda8aSniavoid
16480bbfda8aSniaOtpRestackWindow(TwmWindow *twm_win)
16490bbfda8aSnia{
16500bbfda8aSnia	OtpWinList *owl = twm_win->otp;
16510bbfda8aSnia
16520bbfda8aSnia	RemoveOwl(owl);
16530bbfda8aSnia	InsertOwl(owl, Above);
16540bbfda8aSnia	OtpCheckConsistency();
16550bbfda8aSnia}
16560bbfda8aSnia
16570bbfda8aSnia
16580bbfda8aSnia
16590bbfda8aSnia/**
16600bbfda8aSnia * Focus/unfocus backend.  This is used on windows whose stacking is
16610bbfda8aSnia * focus-dependent (e.g., EWMH fullscreen), to move them and their
16620bbfda8aSnia * transients around.  For these windows, getting/losing focus is
16630bbfda8aSnia * practically the same as a f.setpriority, except it's on the calculated
16640bbfda8aSnia * rather than the base parts.  And it's hard to re-use our existing
16650bbfda8aSnia * functions to do it because we have to move Scr->Focus before the main
16660bbfda8aSnia * window changes, but then it's too late to see where all the transients
16670bbfda8aSnia * were.
16680bbfda8aSnia *
16690bbfda8aSnia * There are a number of unpleasant assumptions in here relating to where
16700bbfda8aSnia * the transients are, and IWBNI we could be smarter and quicker about
16710bbfda8aSnia * dealing with them.  But this gets us past the simple to cause
16720bbfda8aSnia * assertion failures, anyway...
16730bbfda8aSnia */
16740bbfda8aSniastatic void
16750bbfda8aSniaOtpFocusWindowBE(TwmWindow *twm_win, int oldprio)
16760bbfda8aSnia{
16770bbfda8aSnia	OtpWinList *owl = twm_win->otp;
16780bbfda8aSnia
16790bbfda8aSnia	// This one comes off the list, and goes back in its new place.
16800bbfda8aSnia	RemoveOwl(owl);
16810bbfda8aSnia	InsertOwl(owl, Above);
16820bbfda8aSnia
16830bbfda8aSnia	// Now root around for any transients of it, and
16840bbfda8aSnia	// nudge them into the new location.  The whole Above/Below thing is
16850bbfda8aSnia	// kinda a heavy-handed guess, but...
16860bbfda8aSnia	//
16870bbfda8aSnia	// This is nearly a reimplementation of TryToMoveTransientsOfTo(),
16880bbfda8aSnia	// but the assumption that we can find the transients by starting
16890bbfda8aSnia	// from where the old priority was in the list turns out to be deeply
16900bbfda8aSnia	// broken.  So just walk the whole thing.  Which isn't ideal, but...
16910bbfda8aSnia	//
16920bbfda8aSnia	// We also need to do loop detection, since otherwise we'll get stuck
16930bbfda8aSnia	// when a window has multiple transients to move around.  Since we
16940bbfda8aSnia	// read from the bottom up, if a window is moving up the stack, then
16950bbfda8aSnia	// its transients move up, and we run into them again and again.
16960bbfda8aSnia	//
16970bbfda8aSnia	// XXX It should not be this freakin' hard to find a window's
16980bbfda8aSnia	// transients.  We should fix that more globally.
1699b18c2d1eSnia
17000bbfda8aSnia	// XXX Let's just get a friggin' vector implementation already...
17010bbfda8aSnia	size_t tlsz = 32;  // Should hardly ever be too small
17020bbfda8aSnia	size_t tlused = 0;
17030bbfda8aSnia	OtpWinList **tlst = calloc(tlsz, sizeof(OtpWinList *));
17040bbfda8aSnia	if(tlst == NULL) {
17050bbfda8aSnia		fprintf(stderr, "%s(): realloc() failed\n", __func__);
17060bbfda8aSnia		abort();
17070bbfda8aSnia	}
17080bbfda8aSnia
17090bbfda8aSnia	// Loop through and find them all
17100bbfda8aSnia	OtpWinList *trans = Scr->bottomOwl;
17110bbfda8aSnia	while((trans != NULL)) {
17120bbfda8aSnia		// Gotta pre-stash, since we're sometimes about to move trans.
17130bbfda8aSnia		OtpWinList *next = trans->above;
17140bbfda8aSnia
17150bbfda8aSnia		if((trans->type == WinWin)
1716b18c2d1eSnia		                && isTransientOf(trans->twm_win, twm_win)) {
17170bbfda8aSnia			// Got one, stash it
17180bbfda8aSnia			tlst[tlused++] = trans;
17190bbfda8aSnia
17200bbfda8aSnia			// Grow?
17210bbfda8aSnia			if(tlused == tlsz) {
17220bbfda8aSnia				tlsz *= 2;
17230bbfda8aSnia				OtpWinList **tln = realloc(tlst, (tlsz * sizeof(OtpWinList *)));
17240bbfda8aSnia				if(tln == NULL) {
17250bbfda8aSnia					fprintf(stderr, "%s(): realloc() failed\n", __func__);
17260bbfda8aSnia					abort();
17270bbfda8aSnia				}
17280bbfda8aSnia				tlst = tln;
17290bbfda8aSnia			}
17300bbfda8aSnia		}
17310bbfda8aSnia
17320bbfda8aSnia		// And onward
17330bbfda8aSnia		trans = next;
17340bbfda8aSnia	}
17350bbfda8aSnia
17360bbfda8aSnia
17370bbfda8aSnia	// Now loop over them and shuffle them up
17380bbfda8aSnia	for(int i = 0 ; i < tlused ; i++) {
17390bbfda8aSnia		RemoveOwl(tlst[i]);
17400bbfda8aSnia		InsertOwl(tlst[i], Above);
17410bbfda8aSnia	}
17420bbfda8aSnia
17430bbfda8aSnia	free(tlst);
17440bbfda8aSnia
17450bbfda8aSnia	OtpCheckConsistency();
17460bbfda8aSnia}
17470bbfda8aSnia
17480bbfda8aSnia/**
17490bbfda8aSnia * Unfocus a window.  This needs to know internals of OTP because of
17500bbfda8aSnia * focus-dependent stacking of it and its transients.
17510bbfda8aSnia */
17520bbfda8aSniavoid
17530bbfda8aSniaOtpUnfocusWindow(TwmWindow *twm_win)
17540bbfda8aSnia{
17550bbfda8aSnia	// Stash where it currently appears to be.  We assume all its
17560bbfda8aSnia	// transients currently have the same effective priority.  See also
17570bbfda8aSnia	// TryToMoveTransientsOfTo() which makes the same assumption.  I'm
17580bbfda8aSnia	// not sure that's entirely warranted...
17590bbfda8aSnia	int oldprio = PRI(twm_win->otp);
17600bbfda8aSnia
17610bbfda8aSnia	// Now tell ourselves it's unfocused
17620bbfda8aSnia	assert(Scr->Focus == twm_win);
17630bbfda8aSnia	Scr->Focus = NULL;
17640bbfda8aSnia
17650bbfda8aSnia	// And do the work
17660bbfda8aSnia	OtpFocusWindowBE(twm_win, oldprio);
17670bbfda8aSnia}
17680bbfda8aSnia
17690bbfda8aSnia/**
17700bbfda8aSnia * Focus a window.  This needs to know internals of OTP because of
17710bbfda8aSnia * focus-dependent stacking of it and its transients.
17720bbfda8aSnia */
17730bbfda8aSniavoid
17740bbfda8aSniaOtpFocusWindow(TwmWindow *twm_win)
17750bbfda8aSnia{
17760bbfda8aSnia	// X-ref OtoUnfocusWindow() comments.
17770bbfda8aSnia	int oldprio = PRI(twm_win->otp);
17780bbfda8aSnia
17790bbfda8aSnia	assert(Scr->Focus != twm_win);
17800bbfda8aSnia	Scr->Focus = twm_win;
17810bbfda8aSnia
17820bbfda8aSnia	OtpFocusWindowBE(twm_win, oldprio);
17830bbfda8aSnia}
17840bbfda8aSnia
17850bbfda8aSnia
17860bbfda8aSnia
17870bbfda8aSnia/*
17880bbfda8aSnia * Calculating effective priority.  Take the base priority (what gets
17890bbfda8aSnia * set/altered by various OTP config and functions), and then tack on
17900bbfda8aSnia * whatever alterations more ephemeral things might apply.  This
17910bbfda8aSnia * currently pretty much means EWMH bits.
17920bbfda8aSnia */
17930bbfda8aSniaint
17940bbfda8aSniaOtpEffectiveDisplayPriority(TwmWindow *twm_win)
17950bbfda8aSnia{
17960bbfda8aSnia	assert(twm_win != NULL);
17970bbfda8aSnia	assert(twm_win->otp != NULL);
17980bbfda8aSnia
17990bbfda8aSnia	return(OwlEffectivePriority(twm_win->otp) - OTP_ZERO);
18000bbfda8aSnia}
18010bbfda8aSnia
18020bbfda8aSniaint
18030bbfda8aSniaOtpEffectivePriority(TwmWindow *twm_win)
18040bbfda8aSnia{
18050bbfda8aSnia	assert(twm_win != NULL);
18060bbfda8aSnia	assert(twm_win->otp != NULL);
18070bbfda8aSnia
18080bbfda8aSnia	return OwlEffectivePriority(twm_win->otp);
18090bbfda8aSnia}
18100bbfda8aSnia
18110bbfda8aSniastatic int
18120bbfda8aSniaOwlEffectivePriority(OtpWinList *owl)
18130bbfda8aSnia{
18140bbfda8aSnia	int pri;
18150bbfda8aSnia
18160bbfda8aSnia	assert(owl != NULL);
18170bbfda8aSnia
18180bbfda8aSnia	pri = owl->pri_base;
18190bbfda8aSnia
18200bbfda8aSnia#ifdef EWMH
18210bbfda8aSnia	/* ABOVE/BELOW states shift a bit relative to the base */
18220bbfda8aSnia	if(owl->pri_aflags & OTP_AFLAG_ABOVE) {
18230bbfda8aSnia		pri += EWMH_PRI_ABOVE;
18240bbfda8aSnia	}
18250bbfda8aSnia	if(owl->pri_aflags & OTP_AFLAG_BELOW) {
18260bbfda8aSnia		pri -= EWMH_PRI_ABOVE;
18270bbfda8aSnia	}
18280bbfda8aSnia
18290bbfda8aSnia	/*
18300bbfda8aSnia	 * Special magic: EWMH says that _BELOW + _DOCK = (just _BELOW).
18310bbfda8aSnia	 * So if both are set, and its base is where we'd expect just a _DOCK
18320bbfda8aSnia	 * to be, try cancelling that out.
18330bbfda8aSnia	 */
18340bbfda8aSnia	{
18350bbfda8aSnia		EwmhWindowType ewt = owl->twm_win->ewmhWindowType;
18360bbfda8aSnia		if((owl->pri_aflags & OTP_AFLAG_BELOW) && (ewt == wt_Dock) &&
18370bbfda8aSnia		                (owl->pri_base == EWMH_PRI_DOCK + OTP_ZERO)) {
18380bbfda8aSnia			pri -= EWMH_PRI_DOCK;
18390bbfda8aSnia		}
18400bbfda8aSnia	}
18410bbfda8aSnia
18420bbfda8aSnia	/*
18430bbfda8aSnia	 * If FULLSCREEN and focused, jam to (nearly; let the user still win
18440bbfda8aSnia	 * if they try) the top.  We also need to handle transients; they
18450bbfda8aSnia	 * might not have focus, but still need to be on top of the window
18460bbfda8aSnia	 * they're coming up transient for, or else they'll be hidden
18470bbfda8aSnia	 * forever.
18480bbfda8aSnia	 */
18490bbfda8aSnia	if(owl->pri_aflags & OTP_AFLAG_FULLSCREEN) {
18500bbfda8aSnia		if(Scr->Focus == owl->twm_win) {
18510bbfda8aSnia			// It's focused, shift it up
18520bbfda8aSnia			pri = EWMH_PRI_FULLSCREEN + OTP_ZERO;
18530bbfda8aSnia		}
18540bbfda8aSnia		else if(owl->twm_win->istransient) {
18550bbfda8aSnia			// It's a transient of something else; if that something else
18560bbfda8aSnia			// has the fullscreen/focus combo, we should pop this up top
18570bbfda8aSnia			// too.  Technically, we should perhaps test whether its
18580bbfda8aSnia			// parent is also OTP_AFLAG_FULLSCREEN, but if the transient
18590bbfda8aSnia			// has it, the parent probably does too.  Worry about that
18600bbfda8aSnia			// detail if it ever becomes a problem.
18610bbfda8aSnia			TwmWindow *parent = GetTwmWindow(owl->twm_win->transientfor);
18620bbfda8aSnia			if(Scr->Focus == parent) {
18630bbfda8aSnia				// Shift this up so we stay on top
18640bbfda8aSnia				pri = EWMH_PRI_FULLSCREEN + OTP_ZERO;
18650bbfda8aSnia			}
18660bbfda8aSnia		}
18670bbfda8aSnia	}
18680bbfda8aSnia#endif
18690bbfda8aSnia
18700bbfda8aSnia	/* Constrain */
18710bbfda8aSnia	pri = MAX(pri, 0);
18720bbfda8aSnia	pri = MIN(pri, OTP_MAX);
18730bbfda8aSnia
18740bbfda8aSnia	return pri;
18750bbfda8aSnia}
18760bbfda8aSnia
18770bbfda8aSnia
18780bbfda8aSnia/*
18790bbfda8aSnia * Does the priority of a window depend on its focus state?  External
18800bbfda8aSnia * code needs to know, to know when it might need restacking.
18810bbfda8aSnia */
18820bbfda8aSniabool
18830bbfda8aSniaOtpIsFocusDependent(TwmWindow *twm_win)
18840bbfda8aSnia{
18850bbfda8aSnia	assert(twm_win != NULL);
18860bbfda8aSnia	assert(twm_win->otp != NULL);
18870bbfda8aSnia
18880bbfda8aSnia#ifdef EWMH
18890bbfda8aSnia	/*
18900bbfda8aSnia	 * EWMH says _FULLSCREEN and focused windows get shoved to the top;
18910bbfda8aSnia	 * this implies that _FULLSCREEN and _not_ focused don't.  So if the
18920bbfda8aSnia	 * focus is changing, that means we may need to restack.
18930bbfda8aSnia	 */
18940bbfda8aSnia	if(twm_win->otp->pri_aflags & OTP_AFLAG_FULLSCREEN) {
18950bbfda8aSnia		return true;
18960bbfda8aSnia	}
18970bbfda8aSnia#endif
18980bbfda8aSnia
18990bbfda8aSnia	return false;
19000bbfda8aSnia}
1901