captive.c revision 0bbfda8a
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
42static char **GetCaptivesList(int scrnum);
43static void SetCaptivesList(int scrnum, char **clist);
44static void freeCaptivesList(char **clist);
45static Window CaptiveCtwmRootWindow(Window window);
46static bool DontRedirect(Window window);
47
48static Atom XA_WM_CTWM_ROOT_our_name;
49
50/* XXX Share with occupation.c? */
51static 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 */
59bool
60RedirectToCaptive(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 */
199static char **
200GetCaptivesList(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 */
250static void
251freeCaptivesList(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 */
269static void
270SetCaptivesList(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 */
307char *
308AddToCaptiveList(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 */
424void
425RemoveFromCaptiveList(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 */
501void
502SetPropsIfCaptiveCtwm(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 */
521static Window
522CaptiveCtwmRootWindow(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 */
549CaptiveCTWM
550GetCaptiveCTWMUnderPointer(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 */
608void
609SetNoRedirect(Window window)
610{
611	XChangeProperty(dpy, window, XA_WM_NOREDIRECT, XA_STRING, 8,
612	                PropModeReplace, (unsigned char *) "Yes", 4);
613}
614
615static bool
616DontRedirect(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 */
640void
641ConfigureCaptiveRootWindow(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 */
690void 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