Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  * Captive ctwm handling bits.
      3  *
      4  * Captive support makes use of several X properties on various windows.
      5  *
      6  * The WM_CTWMSLIST property is set on the root window (of the
      7  * appropriate Screen) containing a \0-separated list of the names of the
      8  * captive windows inside that ctwm.  So this would show up in the root
      9  * window of a captive ctwm as well, if it had more captives inside it.
     10  *
     11  * A WM_CTWM_ROOT_<captive_name> property is set on the root window (see
     12  * previous) for each of the captive ctwm's, holding the Window XID for
     13  * that captive's internal root window.  The combination of WM_CTWMSLIST
     14  * and WM_CTWM_ROOT_<name> can be used to find the windows each of the
     15  * captive ctwms inside us.
     16  *
     17  * A WM_CTWM_ROOT is set by the captive ctwm on its created root window,
     18  * holding the XID of itself.  The same property is also set by the
     19  * 'outside' ctwm on the frame of that window.  These are used in the
     20  * f.hypermove process, to find the window ID to move stuff into.  I'm
     21  * not quite sure why we're setting it on both; perhaps so the border
     22  * counts as part of the inner window.
     23  */
     24 
     25 #include "ctwm.h"
     26 
     27 #include <stdio.h>
     28 #include <string.h>
     29 #include <stdlib.h>
     30 
     31 #include <X11/Xatom.h>
     32 #include <X11/Xmu/WinUtil.h>
     33 
     34 #include "captive.h"
     35 #include "events.h"
     36 #include "screen.h"
     37 #include "ctwm_atoms.h"
     38 #include "util.h"
     39 #include "vscreen.h"
     40 
     41 
     42 static char **GetCaptivesList(int scrnum);
     43 static void SetCaptivesList(int scrnum, char **clist);
     44 static void freeCaptivesList(char **clist);
     45 static Window CaptiveCtwmRootWindow(Window window);
     46 static bool DontRedirect(Window window);
     47 
     48 static Atom XA_WM_CTWM_ROOT_our_name;
     49 
     50 /* XXX Share with occupation.c? */
     51 static XrmOptionDescRec table [] = {
     52 	{"-xrm",            NULL,           XrmoptionResArg, (XPointer) NULL},
     53 };
     54 
     55 
     56 /*
     57  * Reparent a window over to a captive ctwm, if we should.
     58  */
     59 bool
     60 RedirectToCaptive(Window window)
     61 {
     62 	unsigned long       nitems, bytesafter;
     63 	Atom                actual_type;
     64 	int                 actual_format;
     65 	Bool                status;
     66 	char                *str_type;
     67 	XrmValue            value;
     68 	bool                ret;
     69 	char                *atomname;
     70 	XrmDatabase         db = NULL;
     71 
     72 	/* NOREDIRECT property set?  Leave it alone. */
     73 	if(DontRedirect(window)) {
     74 		return false;
     75 	}
     76 
     77 	/* Figure out what sort of -xrm stuff it might have */
     78 	{
     79 		char **cliargv = NULL;
     80 		int  cliargc;
     81 
     82 		/* Get its command-line */
     83 		if(!XGetCommand(dpy, window, &cliargv, &cliargc)) {
     84 			/* Can't tell, bail */
     85 			return false;
     86 		}
     87 
     88 		XrmParseCommand(&db, table, 1, "ctwm", &cliargc, cliargv);
     89 		if(cliargv) {
     90 			XFreeStringList(cliargv);
     91 		}
     92 	}
     93 
     94 	/* Bail if we didn't get any info */
     95 	if(db == NULL) {
     96 		return false;
     97 	}
     98 
     99 	ret = false;
    100 
    101 	/*
    102 	 * Check "-xrm ctwm.redirect" to see if that says what to do.  It
    103 	 * should contain a captive name.  e.g., what ctwm was started with
    104 	 * via --name, or an autogen'd name if no --name was given.
    105 	 * */
    106 	status = XrmGetResource(db, "ctwm.redirect", "Ctwm.Redirect", &str_type,
    107 	                        &value);
    108 	if((status == True) && (value.size != 0)) {
    109 		/* Yep, we're asked for one.  Find it. */
    110 		Window  *prop;
    111 		Atom    XA_WM_CTWM_ROOT_name;
    112 		int     gpret;
    113 
    114 		asprintf(&atomname, "WM_CTWM_ROOT_%s", value.addr);
    115 		/*
    116 		 * Set only_if_exists to True: the atom for the requested
    117 		 * captive ctwm won't exist if the captive ctwm itself does not exist.
    118 		 * There is no reason to go and create random atoms just to
    119 		 * check.
    120 		 */
    121 		XA_WM_CTWM_ROOT_name = XInternAtom(dpy, atomname, True);
    122 		free(atomname);
    123 
    124 		/*
    125 		 * Got the atom?  Lookup the property it keys for, which holds a
    126 		 * Window identifier.
    127 		 * */
    128 		gpret = !Success;
    129 		prop = NULL;
    130 		if(XA_WM_CTWM_ROOT_name != None) {
    131 			gpret = XGetWindowProperty(dpy, Scr->Root, XA_WM_CTWM_ROOT_name,
    132 			                           0L, 1L, False, AnyPropertyType,
    133 			                           &actual_type, &actual_format,
    134 			                           &nitems, &bytesafter,
    135 			                           (unsigned char **)&prop);
    136 		}
    137 
    138 		/*
    139 		 * Got the property?  Make sure it's the right type.  If so, make
    140 		 * sure the window it points at exists.
    141 		 */
    142 		if(gpret == Success
    143 		                && actual_type == XA_WINDOW && actual_format == 32 &&
    144 		                nitems == 1 /*&& bytesafter == 0*/) {
    145 			Window newroot = *prop;
    146 			XWindowAttributes dummy_wa;
    147 
    148 			if(XGetWindowAttributes(dpy, newroot, &dummy_wa)) {
    149 				/* Well, it must be where we should redirect to, so do it */
    150 				XReparentWindow(dpy, window, newroot, 0, 0);
    151 				XMapWindow(dpy, window);
    152 				ret = true;
    153 			}
    154 		}
    155 		if(prop != NULL) {
    156 			XFree(prop);
    157 		}
    158 
    159 		/* XXX Should we return here if we did the Reparent? */
    160 	}
    161 
    162 
    163 	/*
    164 	 * Check ctwm.rootWindow; it may contain a (hex) X window identifier,
    165 	 * which we should parent into.
    166 	 * */
    167 	status = XrmGetResource(db, "ctwm.rootWindow", "Ctwm.RootWindow", &str_type,
    168 	                        &value);
    169 	if((status == True) && (value.size != 0)) {
    170 		char rootw [32];
    171 		unsigned long int scanned;
    172 
    173 		safe_strncpy(rootw, value.addr, sizeof(rootw));
    174 		if(sscanf(rootw, "%lx", &scanned) == 1) {
    175 			Window newroot = scanned;
    176 			XWindowAttributes dummy_wa;
    177 
    178 			if(XGetWindowAttributes(dpy, newroot, &dummy_wa)) {
    179 				XReparentWindow(dpy, window, newroot, 0, 0);
    180 				XMapWindow(dpy, window);
    181 				ret = true;
    182 			}
    183 		}
    184 	}
    185 
    186 	/* Cleanup xrm bits */
    187 	XrmDestroyDatabase(db);
    188 
    189 	/* Whatever we found */
    190 	return ret;
    191 }
    192 
    193 
    194 /*
    195  * Get the list of captive ctwm's we know about on a screen.
    196  *
    197  * Use freeCaptivesList() to clean up the return value.
    198  */
    199 static char **
    200 GetCaptivesList(int scrnum)
    201 {
    202 	unsigned char       *prop, *p;
    203 	unsigned long       bytesafter;
    204 	unsigned long       len;
    205 	Atom                actual_type;
    206 	int                 actual_format;
    207 	char                **ret;
    208 	int                 count;
    209 	int                 i, l;
    210 	Window              root;
    211 
    212 	root = RootWindow(dpy, scrnum);
    213 	if(XGetWindowProperty(dpy, root, XA_WM_CTWMSLIST, 0L, 512,
    214 	                      False, XA_STRING, &actual_type, &actual_format, &len,
    215 	                      &bytesafter, &prop) != Success) {
    216 		return NULL;
    217 	}
    218 	if(len == 0) {
    219 		return NULL;
    220 	}
    221 
    222 	count = 0;
    223 	p = prop;
    224 	l = 0;
    225 	while(l < len) {
    226 		l += strlen((char *)p) + 1;
    227 		p += strlen((char *)p) + 1;
    228 		count++;
    229 	}
    230 	ret = calloc(count + 1, sizeof(char *));
    231 
    232 	p = prop;
    233 	l = 0;
    234 	i = 0;
    235 	while(l < len) {
    236 		ret [i++] = strdup((char *) p);
    237 		l += strlen((char *)p) + 1;
    238 		p += strlen((char *)p) + 1;
    239 	}
    240 	ret [i] = NULL;
    241 	XFree(prop);
    242 
    243 	return ret;
    244 }
    245 
    246 
    247 /*
    248  * Free GetCaptivesList() return.
    249  */
    250 static void
    251 freeCaptivesList(char **clist)
    252 {
    253 	if(clist == NULL) {
    254 		return;
    255 	}
    256 
    257 	for(char **tmp = clist ; *tmp != NULL ; tmp++) {
    258 		free(*tmp);
    259 	}
    260 
    261 	free(clist);
    262 }
    263 
    264 
    265 /*
    266  * Set the WM_CTWMSLIST property with a set of captive ctwm's, so it can
    267  * be retrieved from there later (say, by a GetCaptivesList() call).
    268  */
    269 static void
    270 SetCaptivesList(int scrnum, char **clist)
    271 {
    272 	unsigned long       len;
    273 	char                **cl;
    274 	char                *s, *slist;
    275 	Window              root = RootWindow(dpy, scrnum);
    276 
    277 	cl  = clist;
    278 	len = 0;
    279 	while(*cl) {
    280 		len += strlen(*cl++) + 1;
    281 	}
    282 	if(len == 0) {
    283 		XDeleteProperty(dpy, root, XA_WM_CTWMSLIST);
    284 		return;
    285 	}
    286 	slist = calloc(len, sizeof(char));
    287 	cl = clist;
    288 	s  = slist;
    289 	while(*cl) {
    290 		strcpy(s, *cl);
    291 		s += strlen(*cl);
    292 		*s++ = '\0';
    293 		cl++;
    294 	}
    295 	XChangeProperty(dpy, root, XA_WM_CTWMSLIST, XA_STRING, 8,
    296 	                PropModeReplace, (unsigned char *) slist, len);
    297 	free(slist);
    298 }
    299 
    300 
    301 /*
    302  * Add ourselves to the list of captive ctwms.  Called during startup
    303  * when --window is given.  Returns the captive name, because we may not
    304  * have been given one explicitly (in cptname), and so it may have been
    305  * autogen'd.
    306  */
    307 char *
    308 AddToCaptiveList(const char *cptname)
    309 {
    310 	int         i, count;
    311 	char        **clist, **cl, **newclist;
    312 	int         busy [32];
    313 	char        *atomname;
    314 	int         scrnum = Scr->screen;
    315 	Window      croot  = Scr->Root;
    316 	Window      root;
    317 	char        *rcname;
    318 
    319 	for(i = 0; i < 32; i++) {
    320 		busy [i] = 0;
    321 	}
    322 
    323 	/*
    324 	 * Go through our current captives to see what's taken
    325 	 */
    326 	clist = GetCaptivesList(scrnum);
    327 	cl = clist;
    328 	count = 0;
    329 	while(cl && *cl) {
    330 		count++;
    331 
    332 		/*
    333 		 * If we're not given a cptname, we use this loop to mark up
    334 		 * which auto-gen'd names have been used.
    335 		 */
    336 		if(!cptname) {
    337 			if(!strncmp(*cl, "ctwm-", 5)) {
    338 				int r, n;
    339 				r = sscanf(*cl, "ctwm-%d", &n);
    340 				cl++;
    341 				if(r != 1) {
    342 					continue;
    343 				}
    344 				if((n < 0) || (n > 31)) {
    345 					continue;
    346 				}
    347 				busy [n] = 1;
    348 			}
    349 			else {
    350 				cl++;
    351 			}
    352 			continue;
    353 		}
    354 
    355 		/*
    356 		 * If we do have a cptname, and a captive already has the
    357 		 * requested name, bomb
    358 		 */
    359 		if(!strcmp(*cl, cptname)) {
    360 			fprintf(stderr, "A captive ctwm with name %s is already running\n",
    361 			        cptname);
    362 			exit(1);
    363 		}
    364 		cl++;
    365 	}
    366 
    367 
    368 	/*
    369 	 * If we weren't given a name, find an available autogen one.  If we
    370 	 * were, just dup it for our return value.
    371 	 */
    372 	if(!cptname) {
    373 		for(i = 0; i < 32; i++) {
    374 			if(!busy [i]) {
    375 				break;
    376 			}
    377 		}
    378 		if(i == 32) {  /* no one can tell we didn't try hard */
    379 			fprintf(stderr, "Cannot find a suitable name for captive ctwm\n");
    380 			exit(1);
    381 		}
    382 		asprintf(&rcname, "ctwm-%d", i);
    383 	}
    384 	else {
    385 		rcname = strdup(cptname);
    386 		if(rcname == NULL) {
    387 			fprintf(stderr, "strdup() for rcname failed!\n");
    388 			abort();
    389 		}
    390 	}
    391 
    392 
    393 	/* Put together new list of captives */
    394 	newclist = calloc(count + 2, sizeof(char *));
    395 	for(i = 0; i < count; i++) {
    396 		newclist[i] = strdup(clist[i]);
    397 	}
    398 	newclist[count] = strdup(rcname);
    399 	newclist[count + 1] = NULL;
    400 	SetCaptivesList(scrnum, newclist);
    401 	freeCaptivesList(clist);
    402 	freeCaptivesList(newclist);
    403 
    404 	/* Stash property/atom of our captivename */
    405 	root = RootWindow(dpy, scrnum);
    406 	asprintf(&atomname, "WM_CTWM_ROOT_%s", rcname);
    407 	XA_WM_CTWM_ROOT_our_name = XInternAtom(dpy, atomname, False);
    408 	free(atomname);
    409 	XChangeProperty(dpy, root, XA_WM_CTWM_ROOT_our_name, XA_WINDOW, 32,
    410 	                PropModeReplace, (unsigned char *) &croot, 1);
    411 
    412 	/*
    413 	 * Tell our caller the name we wound up with, in case they didn't
    414 	 * give us one we could use.
    415 	 */
    416 	return rcname;
    417 }
    418 
    419 
    420 /*
    421  * Take something (in practice, always ourselves) out of the list of
    422  * running captives.  Called during shutdown.
    423  */
    424 void
    425 RemoveFromCaptiveList(const char *cptname)
    426 {
    427 	char **clist;
    428 	int scrnum = Scr->screen;
    429 	Window root = RootWindow(dpy, scrnum);
    430 
    431 	/* If we're not apparently captive, there's nothing to do */
    432 	if(!cptname || XA_WM_CTWM_ROOT_our_name == None) {
    433 		return;
    434 	}
    435 
    436 	/* Take us out of the captives list in WM_CTWMSLIST */
    437 	clist = GetCaptivesList(scrnum);
    438 	if(clist && *clist) {
    439 		char **cl = clist;
    440 		char **newclist;
    441 		int  count;
    442 		bool found;
    443 
    444 		/* How many are there? */
    445 		count = 0;
    446 		found = false;
    447 		while(*cl) {
    448 			if(strcmp(*cl, cptname) == 0) {
    449 				found = true;
    450 			}
    451 			count++;
    452 			cl++;
    453 		}
    454 
    455 		/* If we weren't there, there's nothing to do */
    456 		if(!found) {
    457 			freeCaptivesList(clist);
    458 			return;
    459 		}
    460 
    461 		/*
    462 		 * Make a new list without cptname in it.  A list with (count)
    463 		 * items needs (count+1) for the trailing NULL, but we know we're
    464 		 * in it and removing ourself, so we only need ((count-1)+1).
    465 		 *
    466 		 * Note that we're _not_ strdup()'ing into newclist, just
    467 		 * sticking a pointer to the existing string inside clist.  Then
    468 		 * we only have to free() newclist itself, because there's
    469 		 * nothing new inside it.  We explicitly do _NOT_ want to
    470 		 * freeCaptivesList() it, since that would free the internals,
    471 		 * and then when we fCL(clist) it would try to double-free them.
    472 		 */
    473 		newclist = calloc(count, sizeof(char *));
    474 		cl = clist;
    475 		count = 0;
    476 		while(*cl) {
    477 			if(!strcmp(*cl, cptname)) {
    478 				cl++;
    479 				continue;
    480 			}
    481 			newclist [count++] = *cl;
    482 			cl++;
    483 		}
    484 		newclist [count] = NULL;
    485 		SetCaptivesList(scrnum, newclist);
    486 		free(newclist);
    487 	}
    488 	freeCaptivesList(clist);
    489 
    490 	/* And delete our CTWM_ROOT_x property */
    491 	XDeleteProperty(dpy, root, XA_WM_CTWM_ROOT_our_name);
    492 }
    493 
    494 
    495 /*
    496  * Call from AddWindow() on the 'outside'; if this new window is a
    497  * captive ctwm running inside us, copy its WM_CTWM_ROOT property to the
    498  * frame window we're creating around it.  It's a little unclear why
    499  * we're doing this; x-ref comment at top of file.
    500  */
    501 void
    502 SetPropsIfCaptiveCtwm(TwmWindow *win)
    503 {
    504 	Window      window = win->w;
    505 	Window      frame  = win->frame;
    506 
    507 	if(!CaptiveCtwmRootWindow(window)) {
    508 		return;
    509 	}
    510 
    511 	XChangeProperty(dpy, frame, XA_WM_CTWM_ROOT, XA_WINDOW, 32,
    512 	                PropModeReplace, (unsigned char *) &window, 1);
    513 }
    514 
    515 
    516 /*
    517  * Get the WM_CTWM_ROOT property of a window; that tells us whether it
    518  * thinks it's a captive ctwm, and if so, what it thinks its root window
    519  * is.
    520  */
    521 static Window
    522 CaptiveCtwmRootWindow(Window window)
    523 {
    524 	Window             *prop;
    525 	Window              w;
    526 	unsigned long       bytesafter;
    527 	unsigned long       len;
    528 	Atom                actual_type;
    529 	int                 actual_format;
    530 
    531 	if(XGetWindowProperty(dpy, window, XA_WM_CTWM_ROOT, 0L, 1L,
    532 	                      False, XA_WINDOW, &actual_type, &actual_format, &len,
    533 	                      &bytesafter, (unsigned char **)&prop) != Success) {
    534 		return ((Window)0);
    535 	}
    536 	if(len == 0) {
    537 		return ((Window)0);
    538 	}
    539 	w = *prop;
    540 	XFree(prop);
    541 	return w;
    542 }
    543 
    544 
    545 /*
    546  * Get info about the captive CTWM instance under the cursor.  Called
    547  * during the f.hypermove process.
    548  */
    549 CaptiveCTWM
    550 GetCaptiveCTWMUnderPointer(void)
    551 {
    552 	Window      root;
    553 	Window      child, croot;
    554 	CaptiveCTWM cctwm;
    555 	char        *rname;
    556 
    557 	root = RootWindow(dpy, Scr->screen);
    558 	while(1) {
    559 		XQueryPointer(dpy, root, &JunkRoot, &child,
    560 		              &JunkX, &JunkY, &JunkX, &JunkY, &JunkMask);
    561 		if(child && (croot = CaptiveCtwmRootWindow(child))) {
    562 			root = croot;
    563 			continue;
    564 		}
    565 		cctwm.root = root;
    566 
    567 		/*
    568 		 * We indirect through the extra var here for probably
    569 		 * unnecessary reasons; X resources (like that from XFetchName)
    570 		 * are specified to be freed via XFree(), not via free().  And we
    571 		 * don't want our callers to have to know that (or worse, know to
    572 		 * do it SOMEtimes, since we also might create it ourselves with
    573 		 * strdup()).  So eat the extra allocation/copy and insulate
    574 		 * callers.
    575 		 */
    576 		XFetchName(dpy, root, &rname);
    577 		if(rname) {
    578 			cctwm.name = strdup(rname);
    579 			XFree(rname);
    580 		}
    581 		else {
    582 			cctwm.name = strdup("Root");
    583 		}
    584 
    585 		return (cctwm);
    586 	}
    587 }
    588 
    589 
    590 /*
    591  * We set a NOREDIRECT property on windows in certain situations as a
    592  * result of a f.hypermove.  That gets checked during
    593  * RedirectToCaptive(), causing it to to not mess with the window.
    594  *
    595  * XXX I'm not sure this actually makes any sense; RTC() only gets called
    596  * at the beginning of AddWindow(), only if ctwm isn't running captive.
    597  * So the upshot is that this causes AddWindow() to do nothing and return
    598  * NULL, in the case that a window was hypermoved from a captive ctwm
    599  * into a non-captive ctwm.
    600  *
    601  * That's OK I think, because all the AddWindow() stuff would have
    602  * already been done for it, so there's nothing to do?  But this suggests
    603  * that there's leakage happening; we keep a TwmWindow struct around in
    604  * the "old" ctwm when it's moved into a new one, and since AddWindow()
    605  * only does the condition if we're a non-captive ctwm, it means the
    606  * _captive_ ctwm recreates a new one every time it's hypermoved in?
    607  */
    608 void
    609 SetNoRedirect(Window window)
    610 {
    611 	XChangeProperty(dpy, window, XA_WM_NOREDIRECT, XA_STRING, 8,
    612 	                PropModeReplace, (unsigned char *) "Yes", 4);
    613 }
    614 
    615 static bool
    616 DontRedirect(Window window)
    617 {
    618 	unsigned char       *prop;
    619 	unsigned long       bytesafter;
    620 	unsigned long       len;
    621 	Atom                actual_type;
    622 	int                 actual_format;
    623 
    624 	if(XGetWindowProperty(dpy, window, XA_WM_NOREDIRECT, 0L, 1L,
    625 	                      False, XA_STRING, &actual_type, &actual_format, &len,
    626 	                      &bytesafter, &prop) != Success) {
    627 		return false;
    628 	}
    629 	if(len == 0) {
    630 		return false;
    631 	}
    632 	XFree(prop);
    633 	return true;
    634 }
    635 
    636 
    637 /*
    638  * Handling of a ConfigureNotify for a captive root window.
    639  */
    640 void
    641 ConfigureCaptiveRootWindow(XEvent *ev)
    642 {
    643 	Window       root, child;
    644 	int          x, y;
    645 	unsigned int w, h, bw, d, oldw, oldh;
    646 
    647 	/* Guard */
    648 	if(!CLarg.is_captive) {
    649 		fprintf(stderr, "BUG: %s(): Shouldn't get called unless captive.\n",
    650 		        __func__);
    651 		return;
    652 	}
    653 
    654 	XGetGeometry(dpy, Scr->CaptiveRoot, &root, &x, &y, &w, &h, &bw, &d);
    655 	XTranslateCoordinates(dpy, Scr->CaptiveRoot, root, 0, 0, &Scr->crootx,
    656 	                      &Scr->crooty, &child);
    657 
    658 	oldw = Scr->crootw;
    659 	oldh = Scr->crooth;
    660 	Scr->crootw = ev->xconfigure.width;
    661 	Scr->crooth = ev->xconfigure.height;
    662 #if 0
    663 	fprintf(stderr, "%s(): cx = %d, cy = %d, cw = %d, ch = %d\n",
    664 	        __func__, Scr->crootx, Scr->crooty, Scr->crootw, Scr->crooth);
    665 #endif
    666 	if(Scr->currentvs) {
    667 		Scr->rootx = Scr->crootx + Scr->currentvs->x;
    668 		Scr->rooty = Scr->crooty + Scr->currentvs->y;
    669 	}
    670 	Scr->rootw = Scr->crootw;
    671 	Scr->rooth = Scr->crooth;
    672 
    673 	/*
    674 	 * XXX This is a little weird, and in my experience _always_ triggers
    675 	 * when a captive window starts up.  So what's the point?
    676 	 */
    677 	if((Scr->crootw != oldw) || (Scr->crooth != oldh)) {
    678 		fprintf(stderr, "%s: You cannot change root window geometry "
    679 		        "with virtual screens active,\n"
    680 		        "from now on, the ctwm behaviour is unpredictable.\n",
    681 		        ProgramName);
    682 	}
    683 }
    684 
    685 
    686 /*
    687  * Adopt a window into the calling captive ctwm.  Backend for
    688  * f.adoptwindow function.
    689  */
    690 void AdoptWindow(void)
    691 {
    692 	unsigned long       data [2];
    693 	Window              localroot, w;
    694 	unsigned char       *prop;
    695 	unsigned long       bytesafter;
    696 	unsigned long       len;
    697 	Atom                actual_type;
    698 	int                 actual_format;
    699 	XEvent              event;
    700 	Window              root, parent, child, *children;
    701 	unsigned int        nchildren, key_buttons;
    702 	int                 root_x, root_y, win_x, win_y;
    703 	int                 ret;
    704 	bool                savedRestartPreviousState;
    705 
    706 	localroot = w = RootWindow(dpy, Scr->screen);
    707 	XGrabPointer(dpy, localroot, False,
    708 	             ButtonPressMask | ButtonReleaseMask,
    709 	             GrabModeAsync, GrabModeAsync,
    710 	             None, Scr->SelectCursor, CurrentTime);
    711 
    712 	XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask, &event);
    713 	child = event.xbutton.subwindow;
    714 	while(1) {
    715 		if(child == (Window) 0) {
    716 			break;
    717 		}
    718 
    719 		w = XmuClientWindow(dpy, child);
    720 		ret = XGetWindowProperty(dpy, w, XA_WM_WORKSPACESLIST, 0L, 512,
    721 		                         False, XA_STRING, &actual_type, &actual_format, &len,
    722 		                         &bytesafter, &prop);
    723 		XFree(prop);  /* Don't ever do anything with it */
    724 		if(ret != Success) {
    725 			break;
    726 		}
    727 		if(len == 0) { /* it is not a local root window */
    728 			break;        /* it is not a local root window */
    729 		}
    730 		localroot = w;
    731 		XQueryPointer(dpy, localroot, &root, &child, &root_x, &root_y,
    732 		              &win_x, &win_y, &key_buttons);
    733 	}
    734 	XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask, &event);
    735 	XUngrabPointer(dpy, CurrentTime);
    736 
    737 	if(localroot == Scr->Root) {
    738 		return;
    739 	}
    740 	if(w == localroot) {   /* try to not adopt an ancestor */
    741 		XQueryTree(dpy, Scr->Root, &root, &parent, &children, &nchildren);
    742 		while(parent != (Window) 0) {
    743 			XFree(children);
    744 			if(w == parent) {
    745 				return;
    746 			}
    747 			XQueryTree(dpy, parent, &root, &parent, &children, &nchildren);
    748 		}
    749 		XFree(children);
    750 		if(w == root) {
    751 			return;
    752 		}
    753 	}
    754 	if(localroot == RootWindow(dpy, Scr->screen)) {
    755 		XWithdrawWindow(dpy, w, Scr->screen);
    756 	}
    757 	else {
    758 		XUnmapWindow(dpy, w);
    759 	}
    760 	XReparentWindow(dpy, w, Scr->Root, 0, 0);
    761 
    762 	data [0] = (unsigned long) NormalState;
    763 	data [1] = (unsigned long) None;
    764 
    765 	XChangeProperty(dpy, w, XA_WM_STATE, XA_WM_STATE, 32,
    766 	                PropModeReplace, (unsigned char *) data, 2);
    767 	XFlush(dpy);
    768 	/*
    769 	 * We don't want to "restore" the occupation that the window had
    770 	 * in its former environment. For one, the names of the workspaces
    771 	 * may be different. And if not, the window will initially be
    772 	 * shown in the current workspace, which may be at odds with that
    773 	 * occupation (and confusion ensues).
    774 	 *
    775 	 * Hypermove has the same problem, but that is a "push" operation
    776 	 * (initiated by the originating window manager) so we don't know
    777 	 * when it happens...
    778 	 */
    779 	savedRestartPreviousState = RestartPreviousState;
    780 	RestartPreviousState = false;
    781 	SimulateMapRequest(w);
    782 	RestartPreviousState = savedRestartPreviousState;
    783 	return;
    784 }
    785