10bbfda8aSnia/*
20bbfda8aSnia * Captive ctwm handling bits.
30bbfda8aSnia *
40bbfda8aSnia * Captive support makes use of several X properties on various windows.
50bbfda8aSnia *
60bbfda8aSnia * The WM_CTWMSLIST property is set on the root window (of the
70bbfda8aSnia * appropriate Screen) containing a \0-separated list of the names of the
80bbfda8aSnia * captive windows inside that ctwm.  So this would show up in the root
90bbfda8aSnia * window of a captive ctwm as well, if it had more captives inside it.
100bbfda8aSnia *
110bbfda8aSnia * A WM_CTWM_ROOT_<captive_name> property is set on the root window (see
120bbfda8aSnia * previous) for each of the captive ctwm's, holding the Window XID for
130bbfda8aSnia * that captive's internal root window.  The combination of WM_CTWMSLIST
140bbfda8aSnia * and WM_CTWM_ROOT_<name> can be used to find the windows each of the
150bbfda8aSnia * captive ctwms inside us.
160bbfda8aSnia *
170bbfda8aSnia * A WM_CTWM_ROOT is set by the captive ctwm on its created root window,
180bbfda8aSnia * holding the XID of itself.  The same property is also set by the
190bbfda8aSnia * 'outside' ctwm on the frame of that window.  These are used in the
200bbfda8aSnia * f.hypermove process, to find the window ID to move stuff into.  I'm
210bbfda8aSnia * not quite sure why we're setting it on both; perhaps so the border
220bbfda8aSnia * counts as part of the inner window.
230bbfda8aSnia */
240bbfda8aSnia
250bbfda8aSnia#include "ctwm.h"
260bbfda8aSnia
270bbfda8aSnia#include <stdio.h>
280bbfda8aSnia#include <string.h>
290bbfda8aSnia#include <stdlib.h>
300bbfda8aSnia
310bbfda8aSnia#include <X11/Xatom.h>
320bbfda8aSnia#include <X11/Xmu/WinUtil.h>
330bbfda8aSnia
340bbfda8aSnia#include "captive.h"
350bbfda8aSnia#include "events.h"
360bbfda8aSnia#include "screen.h"
370bbfda8aSnia#include "ctwm_atoms.h"
380bbfda8aSnia#include "util.h"
390bbfda8aSnia#include "vscreen.h"
400bbfda8aSnia
410bbfda8aSnia
420bbfda8aSniastatic char **GetCaptivesList(int scrnum);
430bbfda8aSniastatic void SetCaptivesList(int scrnum, char **clist);
440bbfda8aSniastatic void freeCaptivesList(char **clist);
450bbfda8aSniastatic Window CaptiveCtwmRootWindow(Window window);
460bbfda8aSniastatic bool DontRedirect(Window window);
470bbfda8aSnia
480bbfda8aSniastatic Atom XA_WM_CTWM_ROOT_our_name;
490bbfda8aSnia
500bbfda8aSnia/* XXX Share with occupation.c? */
510bbfda8aSniastatic XrmOptionDescRec table [] = {
520bbfda8aSnia	{"-xrm",            NULL,           XrmoptionResArg, (XPointer) NULL},
530bbfda8aSnia};
540bbfda8aSnia
550bbfda8aSnia
560bbfda8aSnia/*
570bbfda8aSnia * Reparent a window over to a captive ctwm, if we should.
580bbfda8aSnia */
590bbfda8aSniabool
600bbfda8aSniaRedirectToCaptive(Window window)
610bbfda8aSnia{
620bbfda8aSnia	unsigned long       nitems, bytesafter;
630bbfda8aSnia	Atom                actual_type;
640bbfda8aSnia	int                 actual_format;
650bbfda8aSnia	Bool                status;
660bbfda8aSnia	char                *str_type;
670bbfda8aSnia	XrmValue            value;
680bbfda8aSnia	bool                ret;
690bbfda8aSnia	char                *atomname;
700bbfda8aSnia	XrmDatabase         db = NULL;
710bbfda8aSnia
720bbfda8aSnia	/* NOREDIRECT property set?  Leave it alone. */
730bbfda8aSnia	if(DontRedirect(window)) {
740bbfda8aSnia		return false;
750bbfda8aSnia	}
760bbfda8aSnia
770bbfda8aSnia	/* Figure out what sort of -xrm stuff it might have */
780bbfda8aSnia	{
790bbfda8aSnia		char **cliargv = NULL;
800bbfda8aSnia		int  cliargc;
810bbfda8aSnia
820bbfda8aSnia		/* Get its command-line */
830bbfda8aSnia		if(!XGetCommand(dpy, window, &cliargv, &cliargc)) {
840bbfda8aSnia			/* Can't tell, bail */
850bbfda8aSnia			return false;
860bbfda8aSnia		}
870bbfda8aSnia
880bbfda8aSnia		XrmParseCommand(&db, table, 1, "ctwm", &cliargc, cliargv);
890bbfda8aSnia		if(cliargv) {
900bbfda8aSnia			XFreeStringList(cliargv);
910bbfda8aSnia		}
920bbfda8aSnia	}
930bbfda8aSnia
940bbfda8aSnia	/* Bail if we didn't get any info */
950bbfda8aSnia	if(db == NULL) {
960bbfda8aSnia		return false;
970bbfda8aSnia	}
980bbfda8aSnia
990bbfda8aSnia	ret = false;
1000bbfda8aSnia
1010bbfda8aSnia	/*
1020bbfda8aSnia	 * Check "-xrm ctwm.redirect" to see if that says what to do.  It
1030bbfda8aSnia	 * should contain a captive name.  e.g., what ctwm was started with
1040bbfda8aSnia	 * via --name, or an autogen'd name if no --name was given.
1050bbfda8aSnia	 * */
1060bbfda8aSnia	status = XrmGetResource(db, "ctwm.redirect", "Ctwm.Redirect", &str_type,
1070bbfda8aSnia	                        &value);
1080bbfda8aSnia	if((status == True) && (value.size != 0)) {
1090bbfda8aSnia		/* Yep, we're asked for one.  Find it. */
1100bbfda8aSnia		Window  *prop;
1110bbfda8aSnia		Atom    XA_WM_CTWM_ROOT_name;
1120bbfda8aSnia		int     gpret;
1130bbfda8aSnia
1140bbfda8aSnia		asprintf(&atomname, "WM_CTWM_ROOT_%s", value.addr);
1150bbfda8aSnia		/*
1160bbfda8aSnia		 * Set only_if_exists to True: the atom for the requested
1170bbfda8aSnia		 * captive ctwm won't exist if the captive ctwm itself does not exist.
1180bbfda8aSnia		 * There is no reason to go and create random atoms just to
1190bbfda8aSnia		 * check.
1200bbfda8aSnia		 */
1210bbfda8aSnia		XA_WM_CTWM_ROOT_name = XInternAtom(dpy, atomname, True);
1220bbfda8aSnia		free(atomname);
1230bbfda8aSnia
1240bbfda8aSnia		/*
1250bbfda8aSnia		 * Got the atom?  Lookup the property it keys for, which holds a
1260bbfda8aSnia		 * Window identifier.
1270bbfda8aSnia		 * */
1280bbfda8aSnia		gpret = !Success;
1290bbfda8aSnia		prop = NULL;
1300bbfda8aSnia		if(XA_WM_CTWM_ROOT_name != None) {
1310bbfda8aSnia			gpret = XGetWindowProperty(dpy, Scr->Root, XA_WM_CTWM_ROOT_name,
1320bbfda8aSnia			                           0L, 1L, False, AnyPropertyType,
1330bbfda8aSnia			                           &actual_type, &actual_format,
1340bbfda8aSnia			                           &nitems, &bytesafter,
1350bbfda8aSnia			                           (unsigned char **)&prop);
1360bbfda8aSnia		}
1370bbfda8aSnia
1380bbfda8aSnia		/*
1390bbfda8aSnia		 * Got the property?  Make sure it's the right type.  If so, make
1400bbfda8aSnia		 * sure the window it points at exists.
1410bbfda8aSnia		 */
1420bbfda8aSnia		if(gpret == Success
1430bbfda8aSnia		                && actual_type == XA_WINDOW && actual_format == 32 &&
1440bbfda8aSnia		                nitems == 1 /*&& bytesafter == 0*/) {
1450bbfda8aSnia			Window newroot = *prop;
1460bbfda8aSnia			XWindowAttributes dummy_wa;
1470bbfda8aSnia
1480bbfda8aSnia			if(XGetWindowAttributes(dpy, newroot, &dummy_wa)) {
1490bbfda8aSnia				/* Well, it must be where we should redirect to, so do it */
1500bbfda8aSnia				XReparentWindow(dpy, window, newroot, 0, 0);
1510bbfda8aSnia				XMapWindow(dpy, window);
1520bbfda8aSnia				ret = true;
1530bbfda8aSnia			}
1540bbfda8aSnia		}
1550bbfda8aSnia		if(prop != NULL) {
1560bbfda8aSnia			XFree(prop);
1570bbfda8aSnia		}
1580bbfda8aSnia
1590bbfda8aSnia		/* XXX Should we return here if we did the Reparent? */
1600bbfda8aSnia	}
1610bbfda8aSnia
1620bbfda8aSnia
1630bbfda8aSnia	/*
1640bbfda8aSnia	 * Check ctwm.rootWindow; it may contain a (hex) X window identifier,
1650bbfda8aSnia	 * which we should parent into.
1660bbfda8aSnia	 * */
1670bbfda8aSnia	status = XrmGetResource(db, "ctwm.rootWindow", "Ctwm.RootWindow", &str_type,
1680bbfda8aSnia	                        &value);
1690bbfda8aSnia	if((status == True) && (value.size != 0)) {
1700bbfda8aSnia		char rootw [32];
1710bbfda8aSnia		unsigned long int scanned;
1720bbfda8aSnia
1730bbfda8aSnia		safe_strncpy(rootw, value.addr, sizeof(rootw));
1740bbfda8aSnia		if(sscanf(rootw, "%lx", &scanned) == 1) {
1750bbfda8aSnia			Window newroot = scanned;
1760bbfda8aSnia			XWindowAttributes dummy_wa;
1770bbfda8aSnia
1780bbfda8aSnia			if(XGetWindowAttributes(dpy, newroot, &dummy_wa)) {
1790bbfda8aSnia				XReparentWindow(dpy, window, newroot, 0, 0);
1800bbfda8aSnia				XMapWindow(dpy, window);
1810bbfda8aSnia				ret = true;
1820bbfda8aSnia			}
1830bbfda8aSnia		}
1840bbfda8aSnia	}
1850bbfda8aSnia
1860bbfda8aSnia	/* Cleanup xrm bits */
1870bbfda8aSnia	XrmDestroyDatabase(db);
1880bbfda8aSnia
1890bbfda8aSnia	/* Whatever we found */
1900bbfda8aSnia	return ret;
1910bbfda8aSnia}
1920bbfda8aSnia
1930bbfda8aSnia
1940bbfda8aSnia/*
1950bbfda8aSnia * Get the list of captive ctwm's we know about on a screen.
1960bbfda8aSnia *
1970bbfda8aSnia * Use freeCaptivesList() to clean up the return value.
1980bbfda8aSnia */
1990bbfda8aSniastatic char **
2000bbfda8aSniaGetCaptivesList(int scrnum)
2010bbfda8aSnia{
2020bbfda8aSnia	unsigned char       *prop, *p;
2030bbfda8aSnia	unsigned long       bytesafter;
2040bbfda8aSnia	unsigned long       len;
2050bbfda8aSnia	Atom                actual_type;
2060bbfda8aSnia	int                 actual_format;
2070bbfda8aSnia	char                **ret;
2080bbfda8aSnia	int                 count;
2090bbfda8aSnia	int                 i, l;
2100bbfda8aSnia	Window              root;
2110bbfda8aSnia
2120bbfda8aSnia	root = RootWindow(dpy, scrnum);
2130bbfda8aSnia	if(XGetWindowProperty(dpy, root, XA_WM_CTWMSLIST, 0L, 512,
2140bbfda8aSnia	                      False, XA_STRING, &actual_type, &actual_format, &len,
2150bbfda8aSnia	                      &bytesafter, &prop) != Success) {
2160bbfda8aSnia		return NULL;
2170bbfda8aSnia	}
2180bbfda8aSnia	if(len == 0) {
2190bbfda8aSnia		return NULL;
2200bbfda8aSnia	}
2210bbfda8aSnia
2220bbfda8aSnia	count = 0;
2230bbfda8aSnia	p = prop;
2240bbfda8aSnia	l = 0;
2250bbfda8aSnia	while(l < len) {
2260bbfda8aSnia		l += strlen((char *)p) + 1;
2270bbfda8aSnia		p += strlen((char *)p) + 1;
2280bbfda8aSnia		count++;
2290bbfda8aSnia	}
2300bbfda8aSnia	ret = calloc(count + 1, sizeof(char *));
2310bbfda8aSnia
2320bbfda8aSnia	p = prop;
2330bbfda8aSnia	l = 0;
2340bbfda8aSnia	i = 0;
2350bbfda8aSnia	while(l < len) {
2360bbfda8aSnia		ret [i++] = strdup((char *) p);
2370bbfda8aSnia		l += strlen((char *)p) + 1;
2380bbfda8aSnia		p += strlen((char *)p) + 1;
2390bbfda8aSnia	}
2400bbfda8aSnia	ret [i] = NULL;
2410bbfda8aSnia	XFree(prop);
2420bbfda8aSnia
2430bbfda8aSnia	return ret;
2440bbfda8aSnia}
2450bbfda8aSnia
2460bbfda8aSnia
2470bbfda8aSnia/*
2480bbfda8aSnia * Free GetCaptivesList() return.
2490bbfda8aSnia */
2500bbfda8aSniastatic void
2510bbfda8aSniafreeCaptivesList(char **clist)
2520bbfda8aSnia{
2530bbfda8aSnia	if(clist == NULL) {
2540bbfda8aSnia		return;
2550bbfda8aSnia	}
2560bbfda8aSnia
2570bbfda8aSnia	for(char **tmp = clist ; *tmp != NULL ; tmp++) {
2580bbfda8aSnia		free(*tmp);
2590bbfda8aSnia	}
2600bbfda8aSnia
2610bbfda8aSnia	free(clist);
2620bbfda8aSnia}
2630bbfda8aSnia
2640bbfda8aSnia
2650bbfda8aSnia/*
2660bbfda8aSnia * Set the WM_CTWMSLIST property with a set of captive ctwm's, so it can
2670bbfda8aSnia * be retrieved from there later (say, by a GetCaptivesList() call).
2680bbfda8aSnia */
2690bbfda8aSniastatic void
2700bbfda8aSniaSetCaptivesList(int scrnum, char **clist)
2710bbfda8aSnia{
2720bbfda8aSnia	unsigned long       len;
2730bbfda8aSnia	char                **cl;
2740bbfda8aSnia	char                *s, *slist;
2750bbfda8aSnia	Window              root = RootWindow(dpy, scrnum);
2760bbfda8aSnia
2770bbfda8aSnia	cl  = clist;
2780bbfda8aSnia	len = 0;
2790bbfda8aSnia	while(*cl) {
2800bbfda8aSnia		len += strlen(*cl++) + 1;
2810bbfda8aSnia	}
2820bbfda8aSnia	if(len == 0) {
2830bbfda8aSnia		XDeleteProperty(dpy, root, XA_WM_CTWMSLIST);
2840bbfda8aSnia		return;
2850bbfda8aSnia	}
2860bbfda8aSnia	slist = calloc(len, sizeof(char));
2870bbfda8aSnia	cl = clist;
2880bbfda8aSnia	s  = slist;
2890bbfda8aSnia	while(*cl) {
2900bbfda8aSnia		strcpy(s, *cl);
2910bbfda8aSnia		s += strlen(*cl);
2920bbfda8aSnia		*s++ = '\0';
2930bbfda8aSnia		cl++;
2940bbfda8aSnia	}
2950bbfda8aSnia	XChangeProperty(dpy, root, XA_WM_CTWMSLIST, XA_STRING, 8,
2960bbfda8aSnia	                PropModeReplace, (unsigned char *) slist, len);
2970bbfda8aSnia	free(slist);
2980bbfda8aSnia}
2990bbfda8aSnia
3000bbfda8aSnia
3010bbfda8aSnia/*
3020bbfda8aSnia * Add ourselves to the list of captive ctwms.  Called during startup
3030bbfda8aSnia * when --window is given.  Returns the captive name, because we may not
3040bbfda8aSnia * have been given one explicitly (in cptname), and so it may have been
3050bbfda8aSnia * autogen'd.
3060bbfda8aSnia */
3070bbfda8aSniachar *
3080bbfda8aSniaAddToCaptiveList(const char *cptname)
3090bbfda8aSnia{
3100bbfda8aSnia	int         i, count;
3110bbfda8aSnia	char        **clist, **cl, **newclist;
3120bbfda8aSnia	int         busy [32];
3130bbfda8aSnia	char        *atomname;
3140bbfda8aSnia	int         scrnum = Scr->screen;
3150bbfda8aSnia	Window      croot  = Scr->Root;
3160bbfda8aSnia	Window      root;
3170bbfda8aSnia	char        *rcname;
3180bbfda8aSnia
3190bbfda8aSnia	for(i = 0; i < 32; i++) {
3200bbfda8aSnia		busy [i] = 0;
3210bbfda8aSnia	}
3220bbfda8aSnia
3230bbfda8aSnia	/*
3240bbfda8aSnia	 * Go through our current captives to see what's taken
3250bbfda8aSnia	 */
3260bbfda8aSnia	clist = GetCaptivesList(scrnum);
3270bbfda8aSnia	cl = clist;
3280bbfda8aSnia	count = 0;
3290bbfda8aSnia	while(cl && *cl) {
3300bbfda8aSnia		count++;
3310bbfda8aSnia
3320bbfda8aSnia		/*
3330bbfda8aSnia		 * If we're not given a cptname, we use this loop to mark up
3340bbfda8aSnia		 * which auto-gen'd names have been used.
3350bbfda8aSnia		 */
3360bbfda8aSnia		if(!cptname) {
3370bbfda8aSnia			if(!strncmp(*cl, "ctwm-", 5)) {
3380bbfda8aSnia				int r, n;
3390bbfda8aSnia				r = sscanf(*cl, "ctwm-%d", &n);
3400bbfda8aSnia				cl++;
3410bbfda8aSnia				if(r != 1) {
3420bbfda8aSnia					continue;
3430bbfda8aSnia				}
3440bbfda8aSnia				if((n < 0) || (n > 31)) {
3450bbfda8aSnia					continue;
3460bbfda8aSnia				}
3470bbfda8aSnia				busy [n] = 1;
3480bbfda8aSnia			}
3490bbfda8aSnia			else {
3500bbfda8aSnia				cl++;
3510bbfda8aSnia			}
3520bbfda8aSnia			continue;
3530bbfda8aSnia		}
3540bbfda8aSnia
3550bbfda8aSnia		/*
3560bbfda8aSnia		 * If we do have a cptname, and a captive already has the
3570bbfda8aSnia		 * requested name, bomb
3580bbfda8aSnia		 */
3590bbfda8aSnia		if(!strcmp(*cl, cptname)) {
3600bbfda8aSnia			fprintf(stderr, "A captive ctwm with name %s is already running\n",
3610bbfda8aSnia			        cptname);
3620bbfda8aSnia			exit(1);
3630bbfda8aSnia		}
3640bbfda8aSnia		cl++;
3650bbfda8aSnia	}
3660bbfda8aSnia
3670bbfda8aSnia
3680bbfda8aSnia	/*
3690bbfda8aSnia	 * If we weren't given a name, find an available autogen one.  If we
3700bbfda8aSnia	 * were, just dup it for our return value.
3710bbfda8aSnia	 */
3720bbfda8aSnia	if(!cptname) {
3730bbfda8aSnia		for(i = 0; i < 32; i++) {
3740bbfda8aSnia			if(!busy [i]) {
3750bbfda8aSnia				break;
3760bbfda8aSnia			}
3770bbfda8aSnia		}
3780bbfda8aSnia		if(i == 32) {  /* no one can tell we didn't try hard */
3790bbfda8aSnia			fprintf(stderr, "Cannot find a suitable name for captive ctwm\n");
3800bbfda8aSnia			exit(1);
3810bbfda8aSnia		}
3820bbfda8aSnia		asprintf(&rcname, "ctwm-%d", i);
3830bbfda8aSnia	}
3840bbfda8aSnia	else {
3850bbfda8aSnia		rcname = strdup(cptname);
3860bbfda8aSnia		if(rcname == NULL) {
3870bbfda8aSnia			fprintf(stderr, "strdup() for rcname failed!\n");
3880bbfda8aSnia			abort();
3890bbfda8aSnia		}
3900bbfda8aSnia	}
3910bbfda8aSnia
3920bbfda8aSnia
3930bbfda8aSnia	/* Put together new list of captives */
3940bbfda8aSnia	newclist = calloc(count + 2, sizeof(char *));
3950bbfda8aSnia	for(i = 0; i < count; i++) {
3960bbfda8aSnia		newclist[i] = strdup(clist[i]);
3970bbfda8aSnia	}
3980bbfda8aSnia	newclist[count] = strdup(rcname);
3990bbfda8aSnia	newclist[count + 1] = NULL;
4000bbfda8aSnia	SetCaptivesList(scrnum, newclist);
4010bbfda8aSnia	freeCaptivesList(clist);
4020bbfda8aSnia	freeCaptivesList(newclist);
4030bbfda8aSnia
4040bbfda8aSnia	/* Stash property/atom of our captivename */
4050bbfda8aSnia	root = RootWindow(dpy, scrnum);
4060bbfda8aSnia	asprintf(&atomname, "WM_CTWM_ROOT_%s", rcname);
4070bbfda8aSnia	XA_WM_CTWM_ROOT_our_name = XInternAtom(dpy, atomname, False);
4080bbfda8aSnia	free(atomname);
4090bbfda8aSnia	XChangeProperty(dpy, root, XA_WM_CTWM_ROOT_our_name, XA_WINDOW, 32,
4100bbfda8aSnia	                PropModeReplace, (unsigned char *) &croot, 1);
4110bbfda8aSnia
4120bbfda8aSnia	/*
4130bbfda8aSnia	 * Tell our caller the name we wound up with, in case they didn't
4140bbfda8aSnia	 * give us one we could use.
4150bbfda8aSnia	 */
4160bbfda8aSnia	return rcname;
4170bbfda8aSnia}
4180bbfda8aSnia
4190bbfda8aSnia
4200bbfda8aSnia/*
4210bbfda8aSnia * Take something (in practice, always ourselves) out of the list of
4220bbfda8aSnia * running captives.  Called during shutdown.
4230bbfda8aSnia */
4240bbfda8aSniavoid
4250bbfda8aSniaRemoveFromCaptiveList(const char *cptname)
4260bbfda8aSnia{
4270bbfda8aSnia	char **clist;
4280bbfda8aSnia	int scrnum = Scr->screen;
4290bbfda8aSnia	Window root = RootWindow(dpy, scrnum);
4300bbfda8aSnia
4310bbfda8aSnia	/* If we're not apparently captive, there's nothing to do */
4320bbfda8aSnia	if(!cptname || XA_WM_CTWM_ROOT_our_name == None) {
4330bbfda8aSnia		return;
4340bbfda8aSnia	}
4350bbfda8aSnia
4360bbfda8aSnia	/* Take us out of the captives list in WM_CTWMSLIST */
4370bbfda8aSnia	clist = GetCaptivesList(scrnum);
4380bbfda8aSnia	if(clist && *clist) {
4390bbfda8aSnia		char **cl = clist;
4400bbfda8aSnia		char **newclist;
4410bbfda8aSnia		int  count;
4420bbfda8aSnia		bool found;
4430bbfda8aSnia
4440bbfda8aSnia		/* How many are there? */
4450bbfda8aSnia		count = 0;
4460bbfda8aSnia		found = false;
4470bbfda8aSnia		while(*cl) {
4480bbfda8aSnia			if(strcmp(*cl, cptname) == 0) {
4490bbfda8aSnia				found = true;
4500bbfda8aSnia			}
4510bbfda8aSnia			count++;
4520bbfda8aSnia			cl++;
4530bbfda8aSnia		}
4540bbfda8aSnia
4550bbfda8aSnia		/* If we weren't there, there's nothing to do */
4560bbfda8aSnia		if(!found) {
4570bbfda8aSnia			freeCaptivesList(clist);
4580bbfda8aSnia			return;
4590bbfda8aSnia		}
4600bbfda8aSnia
4610bbfda8aSnia		/*
4620bbfda8aSnia		 * Make a new list without cptname in it.  A list with (count)
4630bbfda8aSnia		 * items needs (count+1) for the trailing NULL, but we know we're
4640bbfda8aSnia		 * in it and removing ourself, so we only need ((count-1)+1).
4650bbfda8aSnia		 *
4660bbfda8aSnia		 * Note that we're _not_ strdup()'ing into newclist, just
4670bbfda8aSnia		 * sticking a pointer to the existing string inside clist.  Then
4680bbfda8aSnia		 * we only have to free() newclist itself, because there's
4690bbfda8aSnia		 * nothing new inside it.  We explicitly do _NOT_ want to
4700bbfda8aSnia		 * freeCaptivesList() it, since that would free the internals,
4710bbfda8aSnia		 * and then when we fCL(clist) it would try to double-free them.
4720bbfda8aSnia		 */
4730bbfda8aSnia		newclist = calloc(count, sizeof(char *));
4740bbfda8aSnia		cl = clist;
4750bbfda8aSnia		count = 0;
4760bbfda8aSnia		while(*cl) {
4770bbfda8aSnia			if(!strcmp(*cl, cptname)) {
4780bbfda8aSnia				cl++;
4790bbfda8aSnia				continue;
4800bbfda8aSnia			}
4810bbfda8aSnia			newclist [count++] = *cl;
4820bbfda8aSnia			cl++;
4830bbfda8aSnia		}
4840bbfda8aSnia		newclist [count] = NULL;
4850bbfda8aSnia		SetCaptivesList(scrnum, newclist);
4860bbfda8aSnia		free(newclist);
4870bbfda8aSnia	}
4880bbfda8aSnia	freeCaptivesList(clist);
4890bbfda8aSnia
4900bbfda8aSnia	/* And delete our CTWM_ROOT_x property */
4910bbfda8aSnia	XDeleteProperty(dpy, root, XA_WM_CTWM_ROOT_our_name);
4920bbfda8aSnia}
4930bbfda8aSnia
4940bbfda8aSnia
4950bbfda8aSnia/*
4960bbfda8aSnia * Call from AddWindow() on the 'outside'; if this new window is a
4970bbfda8aSnia * captive ctwm running inside us, copy its WM_CTWM_ROOT property to the
4980bbfda8aSnia * frame window we're creating around it.  It's a little unclear why
4990bbfda8aSnia * we're doing this; x-ref comment at top of file.
5000bbfda8aSnia */
5010bbfda8aSniavoid
5020bbfda8aSniaSetPropsIfCaptiveCtwm(TwmWindow *win)
5030bbfda8aSnia{
5040bbfda8aSnia	Window      window = win->w;
5050bbfda8aSnia	Window      frame  = win->frame;
5060bbfda8aSnia
5070bbfda8aSnia	if(!CaptiveCtwmRootWindow(window)) {
5080bbfda8aSnia		return;
5090bbfda8aSnia	}
5100bbfda8aSnia
5110bbfda8aSnia	XChangeProperty(dpy, frame, XA_WM_CTWM_ROOT, XA_WINDOW, 32,
5120bbfda8aSnia	                PropModeReplace, (unsigned char *) &window, 1);
5130bbfda8aSnia}
5140bbfda8aSnia
5150bbfda8aSnia
5160bbfda8aSnia/*
5170bbfda8aSnia * Get the WM_CTWM_ROOT property of a window; that tells us whether it
5180bbfda8aSnia * thinks it's a captive ctwm, and if so, what it thinks its root window
5190bbfda8aSnia * is.
5200bbfda8aSnia */
5210bbfda8aSniastatic Window
5220bbfda8aSniaCaptiveCtwmRootWindow(Window window)
5230bbfda8aSnia{
5240bbfda8aSnia	Window             *prop;
5250bbfda8aSnia	Window              w;
5260bbfda8aSnia	unsigned long       bytesafter;
5270bbfda8aSnia	unsigned long       len;
5280bbfda8aSnia	Atom                actual_type;
5290bbfda8aSnia	int                 actual_format;
5300bbfda8aSnia
5310bbfda8aSnia	if(XGetWindowProperty(dpy, window, XA_WM_CTWM_ROOT, 0L, 1L,
5320bbfda8aSnia	                      False, XA_WINDOW, &actual_type, &actual_format, &len,
5330bbfda8aSnia	                      &bytesafter, (unsigned char **)&prop) != Success) {
5340bbfda8aSnia		return ((Window)0);
5350bbfda8aSnia	}
5360bbfda8aSnia	if(len == 0) {
5370bbfda8aSnia		return ((Window)0);
5380bbfda8aSnia	}
5390bbfda8aSnia	w = *prop;
5400bbfda8aSnia	XFree(prop);
5410bbfda8aSnia	return w;
5420bbfda8aSnia}
5430bbfda8aSnia
5440bbfda8aSnia
5450bbfda8aSnia/*
5460bbfda8aSnia * Get info about the captive CTWM instance under the cursor.  Called
5470bbfda8aSnia * during the f.hypermove process.
5480bbfda8aSnia */
5490bbfda8aSniaCaptiveCTWM
5500bbfda8aSniaGetCaptiveCTWMUnderPointer(void)
5510bbfda8aSnia{
5520bbfda8aSnia	Window      root;
5530bbfda8aSnia	Window      child, croot;
5540bbfda8aSnia	CaptiveCTWM cctwm;
5550bbfda8aSnia	char        *rname;
5560bbfda8aSnia
5570bbfda8aSnia	root = RootWindow(dpy, Scr->screen);
5580bbfda8aSnia	while(1) {
5590bbfda8aSnia		XQueryPointer(dpy, root, &JunkRoot, &child,
5600bbfda8aSnia		              &JunkX, &JunkY, &JunkX, &JunkY, &JunkMask);
5610bbfda8aSnia		if(child && (croot = CaptiveCtwmRootWindow(child))) {
5620bbfda8aSnia			root = croot;
5630bbfda8aSnia			continue;
5640bbfda8aSnia		}
5650bbfda8aSnia		cctwm.root = root;
5660bbfda8aSnia
5670bbfda8aSnia		/*
5680bbfda8aSnia		 * We indirect through the extra var here for probably
5690bbfda8aSnia		 * unnecessary reasons; X resources (like that from XFetchName)
5700bbfda8aSnia		 * are specified to be freed via XFree(), not via free().  And we
5710bbfda8aSnia		 * don't want our callers to have to know that (or worse, know to
5720bbfda8aSnia		 * do it SOMEtimes, since we also might create it ourselves with
5730bbfda8aSnia		 * strdup()).  So eat the extra allocation/copy and insulate
5740bbfda8aSnia		 * callers.
5750bbfda8aSnia		 */
5760bbfda8aSnia		XFetchName(dpy, root, &rname);
5770bbfda8aSnia		if(rname) {
5780bbfda8aSnia			cctwm.name = strdup(rname);
5790bbfda8aSnia			XFree(rname);
5800bbfda8aSnia		}
5810bbfda8aSnia		else {
5820bbfda8aSnia			cctwm.name = strdup("Root");
5830bbfda8aSnia		}
5840bbfda8aSnia
5850bbfda8aSnia		return (cctwm);
5860bbfda8aSnia	}
5870bbfda8aSnia}
5880bbfda8aSnia
5890bbfda8aSnia
5900bbfda8aSnia/*
5910bbfda8aSnia * We set a NOREDIRECT property on windows in certain situations as a
5920bbfda8aSnia * result of a f.hypermove.  That gets checked during
5930bbfda8aSnia * RedirectToCaptive(), causing it to to not mess with the window.
5940bbfda8aSnia *
5950bbfda8aSnia * XXX I'm not sure this actually makes any sense; RTC() only gets called
5960bbfda8aSnia * at the beginning of AddWindow(), only if ctwm isn't running captive.
5970bbfda8aSnia * So the upshot is that this causes AddWindow() to do nothing and return
5980bbfda8aSnia * NULL, in the case that a window was hypermoved from a captive ctwm
5990bbfda8aSnia * into a non-captive ctwm.
6000bbfda8aSnia *
6010bbfda8aSnia * That's OK I think, because all the AddWindow() stuff would have
6020bbfda8aSnia * already been done for it, so there's nothing to do?  But this suggests
6030bbfda8aSnia * that there's leakage happening; we keep a TwmWindow struct around in
6040bbfda8aSnia * the "old" ctwm when it's moved into a new one, and since AddWindow()
6050bbfda8aSnia * only does the condition if we're a non-captive ctwm, it means the
6060bbfda8aSnia * _captive_ ctwm recreates a new one every time it's hypermoved in?
6070bbfda8aSnia */
6080bbfda8aSniavoid
6090bbfda8aSniaSetNoRedirect(Window window)
6100bbfda8aSnia{
6110bbfda8aSnia	XChangeProperty(dpy, window, XA_WM_NOREDIRECT, XA_STRING, 8,
6120bbfda8aSnia	                PropModeReplace, (unsigned char *) "Yes", 4);
6130bbfda8aSnia}
6140bbfda8aSnia
6150bbfda8aSniastatic bool
6160bbfda8aSniaDontRedirect(Window window)
6170bbfda8aSnia{
6180bbfda8aSnia	unsigned char       *prop;
6190bbfda8aSnia	unsigned long       bytesafter;
6200bbfda8aSnia	unsigned long       len;
6210bbfda8aSnia	Atom                actual_type;
6220bbfda8aSnia	int                 actual_format;
6230bbfda8aSnia
6240bbfda8aSnia	if(XGetWindowProperty(dpy, window, XA_WM_NOREDIRECT, 0L, 1L,
6250bbfda8aSnia	                      False, XA_STRING, &actual_type, &actual_format, &len,
6260bbfda8aSnia	                      &bytesafter, &prop) != Success) {
6270bbfda8aSnia		return false;
6280bbfda8aSnia	}
6290bbfda8aSnia	if(len == 0) {
6300bbfda8aSnia		return false;
6310bbfda8aSnia	}
6320bbfda8aSnia	XFree(prop);
6330bbfda8aSnia	return true;
6340bbfda8aSnia}
6350bbfda8aSnia
6360bbfda8aSnia
6370bbfda8aSnia/*
6380bbfda8aSnia * Handling of a ConfigureNotify for a captive root window.
6390bbfda8aSnia */
6400bbfda8aSniavoid
6410bbfda8aSniaConfigureCaptiveRootWindow(XEvent *ev)
6420bbfda8aSnia{
6430bbfda8aSnia	Window       root, child;
6440bbfda8aSnia	int          x, y;
6450bbfda8aSnia	unsigned int w, h, bw, d, oldw, oldh;
6460bbfda8aSnia
6470bbfda8aSnia	/* Guard */
6480bbfda8aSnia	if(!CLarg.is_captive) {
6490bbfda8aSnia		fprintf(stderr, "BUG: %s(): Shouldn't get called unless captive.\n",
6500bbfda8aSnia		        __func__);
6510bbfda8aSnia		return;
6520bbfda8aSnia	}
6530bbfda8aSnia
6540bbfda8aSnia	XGetGeometry(dpy, Scr->CaptiveRoot, &root, &x, &y, &w, &h, &bw, &d);
6550bbfda8aSnia	XTranslateCoordinates(dpy, Scr->CaptiveRoot, root, 0, 0, &Scr->crootx,
6560bbfda8aSnia	                      &Scr->crooty, &child);
6570bbfda8aSnia
6580bbfda8aSnia	oldw = Scr->crootw;
6590bbfda8aSnia	oldh = Scr->crooth;
6600bbfda8aSnia	Scr->crootw = ev->xconfigure.width;
6610bbfda8aSnia	Scr->crooth = ev->xconfigure.height;
6620bbfda8aSnia#if 0
6630bbfda8aSnia	fprintf(stderr, "%s(): cx = %d, cy = %d, cw = %d, ch = %d\n",
6640bbfda8aSnia	        __func__, Scr->crootx, Scr->crooty, Scr->crootw, Scr->crooth);
6650bbfda8aSnia#endif
6660bbfda8aSnia	if(Scr->currentvs) {
6670bbfda8aSnia		Scr->rootx = Scr->crootx + Scr->currentvs->x;
6680bbfda8aSnia		Scr->rooty = Scr->crooty + Scr->currentvs->y;
6690bbfda8aSnia	}
6700bbfda8aSnia	Scr->rootw = Scr->crootw;
6710bbfda8aSnia	Scr->rooth = Scr->crooth;
6720bbfda8aSnia
6730bbfda8aSnia	/*
6740bbfda8aSnia	 * XXX This is a little weird, and in my experience _always_ triggers
6750bbfda8aSnia	 * when a captive window starts up.  So what's the point?
6760bbfda8aSnia	 */
6770bbfda8aSnia	if((Scr->crootw != oldw) || (Scr->crooth != oldh)) {
6780bbfda8aSnia		fprintf(stderr, "%s: You cannot change root window geometry "
6790bbfda8aSnia		        "with virtual screens active,\n"
6800bbfda8aSnia		        "from now on, the ctwm behaviour is unpredictable.\n",
6810bbfda8aSnia		        ProgramName);
6820bbfda8aSnia	}
6830bbfda8aSnia}
6840bbfda8aSnia
6850bbfda8aSnia
6860bbfda8aSnia/*
6870bbfda8aSnia * Adopt a window into the calling captive ctwm.  Backend for
6880bbfda8aSnia * f.adoptwindow function.
6890bbfda8aSnia */
6900bbfda8aSniavoid AdoptWindow(void)
6910bbfda8aSnia{
6920bbfda8aSnia	unsigned long       data [2];
6930bbfda8aSnia	Window              localroot, w;
6940bbfda8aSnia	unsigned char       *prop;
6950bbfda8aSnia	unsigned long       bytesafter;
6960bbfda8aSnia	unsigned long       len;
6970bbfda8aSnia	Atom                actual_type;
6980bbfda8aSnia	int                 actual_format;
6990bbfda8aSnia	XEvent              event;
7000bbfda8aSnia	Window              root, parent, child, *children;
7010bbfda8aSnia	unsigned int        nchildren, key_buttons;
7020bbfda8aSnia	int                 root_x, root_y, win_x, win_y;
7030bbfda8aSnia	int                 ret;
7040bbfda8aSnia	bool                savedRestartPreviousState;
7050bbfda8aSnia
7060bbfda8aSnia	localroot = w = RootWindow(dpy, Scr->screen);
7070bbfda8aSnia	XGrabPointer(dpy, localroot, False,
7080bbfda8aSnia	             ButtonPressMask | ButtonReleaseMask,
7090bbfda8aSnia	             GrabModeAsync, GrabModeAsync,
7100bbfda8aSnia	             None, Scr->SelectCursor, CurrentTime);
7110bbfda8aSnia
7120bbfda8aSnia	XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask, &event);
7130bbfda8aSnia	child = event.xbutton.subwindow;
7140bbfda8aSnia	while(1) {
7150bbfda8aSnia		if(child == (Window) 0) {
7160bbfda8aSnia			break;
7170bbfda8aSnia		}
7180bbfda8aSnia
7190bbfda8aSnia		w = XmuClientWindow(dpy, child);
7200bbfda8aSnia		ret = XGetWindowProperty(dpy, w, XA_WM_WORKSPACESLIST, 0L, 512,
7210bbfda8aSnia		                         False, XA_STRING, &actual_type, &actual_format, &len,
7220bbfda8aSnia		                         &bytesafter, &prop);
7230bbfda8aSnia		XFree(prop);  /* Don't ever do anything with it */
7240bbfda8aSnia		if(ret != Success) {
7250bbfda8aSnia			break;
7260bbfda8aSnia		}
7270bbfda8aSnia		if(len == 0) { /* it is not a local root window */
7280bbfda8aSnia			break;        /* it is not a local root window */
7290bbfda8aSnia		}
7300bbfda8aSnia		localroot = w;
7310bbfda8aSnia		XQueryPointer(dpy, localroot, &root, &child, &root_x, &root_y,
7320bbfda8aSnia		              &win_x, &win_y, &key_buttons);
7330bbfda8aSnia	}
7340bbfda8aSnia	XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask, &event);
7350bbfda8aSnia	XUngrabPointer(dpy, CurrentTime);
7360bbfda8aSnia
7370bbfda8aSnia	if(localroot == Scr->Root) {
7380bbfda8aSnia		return;
7390bbfda8aSnia	}
7400bbfda8aSnia	if(w == localroot) {   /* try to not adopt an ancestor */
7410bbfda8aSnia		XQueryTree(dpy, Scr->Root, &root, &parent, &children, &nchildren);
7420bbfda8aSnia		while(parent != (Window) 0) {
7430bbfda8aSnia			XFree(children);
7440bbfda8aSnia			if(w == parent) {
7450bbfda8aSnia				return;
7460bbfda8aSnia			}
7470bbfda8aSnia			XQueryTree(dpy, parent, &root, &parent, &children, &nchildren);
7480bbfda8aSnia		}
7490bbfda8aSnia		XFree(children);
7500bbfda8aSnia		if(w == root) {
7510bbfda8aSnia			return;
7520bbfda8aSnia		}
7530bbfda8aSnia	}
7540bbfda8aSnia	if(localroot == RootWindow(dpy, Scr->screen)) {
7550bbfda8aSnia		XWithdrawWindow(dpy, w, Scr->screen);
7560bbfda8aSnia	}
7570bbfda8aSnia	else {
7580bbfda8aSnia		XUnmapWindow(dpy, w);
7590bbfda8aSnia	}
7600bbfda8aSnia	XReparentWindow(dpy, w, Scr->Root, 0, 0);
7610bbfda8aSnia
7620bbfda8aSnia	data [0] = (unsigned long) NormalState;
7630bbfda8aSnia	data [1] = (unsigned long) None;
7640bbfda8aSnia
7650bbfda8aSnia	XChangeProperty(dpy, w, XA_WM_STATE, XA_WM_STATE, 32,
7660bbfda8aSnia	                PropModeReplace, (unsigned char *) data, 2);
7670bbfda8aSnia	XFlush(dpy);
7680bbfda8aSnia	/*
7690bbfda8aSnia	 * We don't want to "restore" the occupation that the window had
7700bbfda8aSnia	 * in its former environment. For one, the names of the workspaces
7710bbfda8aSnia	 * may be different. And if not, the window will initially be
7720bbfda8aSnia	 * shown in the current workspace, which may be at odds with that
7730bbfda8aSnia	 * occupation (and confusion ensues).
7740bbfda8aSnia	 *
7750bbfda8aSnia	 * Hypermove has the same problem, but that is a "push" operation
7760bbfda8aSnia	 * (initiated by the originating window manager) so we don't know
7770bbfda8aSnia	 * when it happens...
7780bbfda8aSnia	 */
7790bbfda8aSnia	savedRestartPreviousState = RestartPreviousState;
7800bbfda8aSnia	RestartPreviousState = false;
7810bbfda8aSnia	SimulateMapRequest(w);
7820bbfda8aSnia	RestartPreviousState = savedRestartPreviousState;
7830bbfda8aSnia	return;
7840bbfda8aSnia}
785