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