ctwm_main.c revision b18c2d1e
1/*
2 *       Copyright 1988 by Evans & Sutherland Computer Corporation,
3 *                          Salt Lake City, Utah
4 *  Portions Copyright 1989 by the Massachusetts Institute of Technology
5 *                        Cambridge, Massachusetts
6 *
7 * $XConsortium: twm.c,v 1.124 91/05/08 11:01:54 dave Exp $
8 *
9 * twm - "Tom's Window Manager"
10 *
11 * 27-Oct-87 Thomas E. LaStrange        File created
12 * 10-Oct-90 David M. Sternlicht        Storing saved colors on root
13 *
14 * Copyright 1992 Claude Lecommandeur.
15 *
16 * Do the necessary modification to be integrated in ctwm.
17 * Can no longer be used for the standard twm.
18 *
19 * 22-April-92 Claude Lecommandeur.
20 */
21
22#include "ctwm.h"
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <unistd.h>
27#include <locale.h>
28
29#include <fcntl.h>
30#include <X11/Xatom.h>
31#include <X11/extensions/shape.h>
32
33
34#include "ctwm_atoms.h"
35#include "ctwm_main.h"
36#include "ctwm_takeover.h"
37#include "clargs.h"
38#include "add_window.h"
39#include "gc.h"
40#include "parse.h"
41#include "version.h"
42#include "colormaps.h"
43#include "events.h"
44#include "util.h"
45#include "mask_screen.h"
46#include "animate.h"
47#include "screen.h"
48#include "icons.h"
49#include "iconmgr.h"
50#include "list.h"
51#ifdef SESSION
52#include "session.h"
53#endif
54#include "occupation.h"
55#include "otp.h"
56#include "cursor.h"
57#ifdef WINBOX
58#include "windowbox.h"
59#endif
60#ifdef CAPTIVE
61#include "captive.h"
62#endif
63#ifdef XRANDR
64#include "xrandr.h"
65#endif
66#include "r_area.h"
67#include "r_area_list.h"
68#include "r_layout.h"
69#include "signals.h"
70#include "vscreen.h"
71#include "win_decorations_init.h"
72#include "win_ops.h"
73#include "win_regions.h"
74#include "win_utils.h"
75#include "workspace_manager.h"
76#ifdef SOUNDS
77#  include "sound.h"
78#endif
79
80#include "gram.tab.h"
81
82
83XtAppContext appContext;        /* Xt application context */
84Display *dpy;                   /* which display are we talking to */
85Window ResizeWindow;            /* the window we are resizing */
86
87Atom XCTWMAtom[NUM_CTWM_XATOMS]; ///< Our various common atoms
88
89int NumScreens;                 /* number of screens in ScreenList */
90bool HasShape;                  /* server supports shape extension? */
91int ShapeEventBase, ShapeErrorBase;
92ScreenInfo **ScreenList;        /* structures for each screen */
93ScreenInfo *Scr = NULL;         /* the cur and prev screens */
94int PreviousScreen;             /* last screen that we were on */
95static bool cfgerrs = false;    ///< Whether there were config parsing errors
96
97#ifdef CAPTIVE
98static Window CreateCaptiveRootWindow(int x, int y,
99                                      unsigned int width, unsigned int height);
100#endif
101ScreenInfo *InitScreenInfo(int scrnum, Window croot, int crootx, int crooty,
102                           unsigned int crootw, unsigned int crooth);
103static bool MappedNotOverride(Window w);
104
105Cursor  UpperLeftCursor;
106Cursor  TopRightCursor,
107        TopLeftCursor,
108        BottomRightCursor,
109        BottomLeftCursor,
110        LeftCursor,
111        RightCursor,
112        TopCursor,
113        BottomCursor;
114
115Cursor RightButt;
116Cursor MiddleButt;
117Cursor LeftButt;
118
119XContext TwmContext;            /* context for twm windows */
120XContext MenuContext;           /* context for all menu windows */
121XContext ScreenContext;         /* context to get screen data */
122XContext ColormapContext;       /* context for colormap operations */
123
124XClassHint NoClass;             /* for applications with no class */
125
126XGCValues Gcv;
127
128char *Home;                     /* the HOME environment variable */
129int HomeLen;                    /* length of Home */
130
131bool HandlingEvents = false;    /* are we handling events yet? */
132
133/*
134 * Various junk vars for xlib calls.  Many calls have to get passed these
135 * pointers to return values into, but in a lot of cases we don't care
136 * about some/all of them, and since xlib blindly derefs and stores into
137 * them, we can't just pass NULL for the ones we don't care about.  So we
138 * make this set of globals to use as standin.  These should never be
139 * used or read in our own code; use real vars for the values we DO use
140 * from the calls.
141 */
142Window JunkRoot, JunkChild;
143int JunkX, JunkY;
144unsigned int JunkWidth, JunkHeight, JunkBW, JunkDepth, JunkMask;
145
146char *ProgramName;
147size_t ProgramNameLen;
148int Argc;
149char **Argv;
150
151bool RestartPreviousState = true;      /* try to restart in previous state */
152
153
154/// Magic flag for tests.  Nothing else should touch this!
155bool ctwm_test = false;
156
157/// Magic callback for tests.  This will trigger right after config file
158/// parsing if it's set, and then exit.  Nothing else should ever touch
159/// this!
160int (*ctwm_test_postparse)(void) = NULL;
161
162
163
164
165/**
166 * Start up ctwm.  This is effectively main(), just wrapped for various
167 * unimportant reasons.
168 */
169int
170ctwm_main(int argc, char *argv[])
171{
172	int numManaged, firstscrn, lastscrn;
173	bool FirstScreen;
174	bool takeover = true;
175	bool nodpyok = false;
176
177	setlocale(LC_ALL, "");
178
179	ProgramName = argv[0];
180	ProgramNameLen = strlen(ProgramName);
181	Argc = argc;
182	Argv = argv;
183
184
185	/*
186	 * Run consistency check.  This is mostly to keep devs from
187	 * accidental screwups; if a user ever sees anything from this,
188	 * something went very very wrong.
189	 */
190	chk_keytable_order();
191
192	/*
193	 * Parse-out command line args, and check the results.
194	 */
195	clargs_parse(argc, argv);
196	clargs_check();
197	/* If we get this far, it was all good */
198
199	/* Some clargs mean we're not actually trying to take over the screen */
200	if(CLarg.cfgchk) {
201		takeover = false;
202	}
203#ifdef CAPTIVE
204	if(CLarg.is_captive) {
205		takeover = false;
206	}
207#endif
208
209	/* And some mean we actually don't care if we lack an X server */
210	if(CLarg.cfgchk) {
211		nodpyok = true;
212	}
213
214	/* Support for tests: be ready to fake everything */
215	if(ctwm_test) {
216		takeover = false;
217		nodpyok  = true;
218	}
219
220
221	/*
222	 * Hook up signal handlers
223	 */
224	setup_signal_handlers();
225
226
227	// Various bits of code care about $HOME
228	Home = getenv("HOME");
229	if(Home == NULL) {
230		Home = "./";
231	}
232	HomeLen = strlen(Home);
233
234
235	// XXX This is only used in AddWindow(), and is probably bogus to
236	// have globally....
237	NoClass.res_name = NoName;
238	NoClass.res_class = NoName;
239
240
241	/*
242	 * Initialize our X connection and state bits.
243	 */
244	{
245		int zero = 0; // Fakey
246
247		XtToolkitInitialize();
248		appContext = XtCreateApplicationContext();
249
250		// Tests don't talk to a real X server.
251		// XXX This needs revisiting if we ever get one that _does_.
252		// We'll have to add another flag...
253		if(!ctwm_test) {
254			// Connect
255			dpy = XtOpenDisplay(appContext, CLarg.display_name, "twm", "twm",
256			                    NULL, 0, &zero, NULL);
257		}
258
259		// Failed?  Usually a problem, but somethings we allow faking...
260		if(!dpy && !nodpyok) {
261			fprintf(stderr, "%s:  unable to open display \"%s\"\n",
262			        ProgramName, XDisplayName(CLarg.display_name));
263			exit(1);
264		}
265
266		if(dpy && fcntl(ConnectionNumber(dpy), F_SETFD, FD_CLOEXEC) == -1) {
267			fprintf(stderr,
268			        "%s:  unable to mark display connection as close-on-exec\n",
269			        ProgramName);
270			exit(1);
271		}
272
273		if(!dpy && !ctwm_test) {
274			// At least warn, except for tests
275			fprintf(stderr, "%s: Can't connect to X server, proceeding anyway...\n",
276			        ProgramName);
277		}
278	}
279
280
281#ifdef SESSION
282	// Load session stuff
283	if(CLarg.restore_filename) {
284		ReadWinConfigFile(CLarg.restore_filename);
285	}
286#endif
287
288
289	if(dpy) {
290		// Load up info about X extensions
291		HasShape = XShapeQueryExtension(dpy, &ShapeEventBase, &ShapeErrorBase);
292
293		// Allocate contexts/atoms/etc we use
294		TwmContext = XUniqueContext();
295		MenuContext = XUniqueContext();
296		ScreenContext = XUniqueContext();
297		ColormapContext = XUniqueContext();
298		InitWorkSpaceManagerContext();
299
300		// Load up our standard set of atoms
301		XInternAtoms(dpy, XCTWMAtomNames, NUM_CTWM_XATOMS, False, XCTWMAtom);
302
303		NumScreens = ScreenCount(dpy);
304		PreviousScreen = DefaultScreen(dpy);
305	}
306	else {
307		NumScreens = 1;
308		PreviousScreen = 0;
309	}
310
311	// Allocate/define common cursors
312	NewFontCursor(&TopLeftCursor, "top_left_corner");
313	NewFontCursor(&TopRightCursor, "top_right_corner");
314	NewFontCursor(&BottomLeftCursor, "bottom_left_corner");
315	NewFontCursor(&BottomRightCursor, "bottom_right_corner");
316	NewFontCursor(&LeftCursor, "left_side");
317	NewFontCursor(&RightCursor, "right_side");
318	NewFontCursor(&TopCursor, "top_side");
319	NewFontCursor(&BottomCursor, "bottom_side");
320
321	NewFontCursor(&UpperLeftCursor, "top_left_corner");
322	NewFontCursor(&RightButt, "rightbutton");
323	NewFontCursor(&LeftButt, "leftbutton");
324	NewFontCursor(&MiddleButt, "middlebutton");
325
326
327	// Prep up the per-screen global info
328	if(CLarg.MultiScreen) {
329		firstscrn = 0;
330		lastscrn = NumScreens - 1;
331	}
332	else if(!dpy) {
333		firstscrn = lastscrn = 0;
334	}
335	else {
336		firstscrn = lastscrn = DefaultScreen(dpy);
337	}
338
339	// For simplicity, pre-allocate NumScreens ScreenInfo struct pointers
340	ScreenList = calloc(NumScreens, sizeof(ScreenInfo *));
341	if(ScreenList == NULL) {
342		fprintf(stderr, "%s: Unable to allocate memory for screen list, exiting.\n",
343		        ProgramName);
344		exit(1);
345	}
346
347
348	// Do a little early initialization
349#ifdef EWMH
350	if(dpy) {
351		EwmhInit();
352	}
353#endif /* EWMH */
354#ifdef SOUNDS
355	// Needs init'ing before we get to config parsing
356	sound_init();
357#endif
358	InitEvents();
359
360
361
362	// Start looping over the screens
363	numManaged = 0;
364	FirstScreen = true;
365	for(int scrnum = firstscrn ; scrnum <= lastscrn; scrnum++) {
366		Window croot;
367		int crootx, crooty;
368		unsigned int crootw, crooth;
369		bool screenmasked;
370		char *welcomefile;
371
372
373		/*
374		 * First, setup the root window for the screen.
375		 */
376		if(0) {
377			// Dummy
378		}
379#ifdef CAPTIVE
380		else if(CLarg.is_captive) {
381			// Captive ctwm.  We make a fake root.
382			XWindowAttributes wa;
383			if(CLarg.capwin && XGetWindowAttributes(dpy, CLarg.capwin, &wa)) {
384				Window junk;
385				croot  = CLarg.capwin;
386				crootw = wa.width;
387				crooth = wa.height;
388				XTranslateCoordinates(dpy, CLarg.capwin, wa.root, 0, 0, &crootx, &crooty,
389				                      &junk);
390			}
391			else {
392				// Fake up default size.  Probably ideally should be
393				// configurable, but even more ideally we wouldn't have
394				// captive...
395				crootx = crooty = 100;
396				crootw = 1280;
397				crooth = 768;
398				croot = CreateCaptiveRootWindow(crootx, crooty, crootw, crooth);
399			}
400		}
401#endif
402		else {
403			// Normal; get the real display's root.
404			crootx = 0;
405			crooty = 0;
406
407			if(dpy) {
408				croot  = RootWindow(dpy, scrnum);
409				crootw = DisplayWidth(dpy, scrnum);
410				crooth = DisplayHeight(dpy, scrnum);
411			}
412			else {
413				croot = None;
414				crootw = 1280;
415				crooth = 768;
416			}
417		}
418
419
420
421		/*
422		 * Create ScreenInfo for this Screen, and populate various
423		 * default/initial config.
424		 */
425		Scr = ScreenList[scrnum] = InitScreenInfo(scrnum, croot,
426		                           crootx, crooty, crootw, crooth);
427		if(Scr == NULL) {
428			fprintf(stderr,
429			        "%s: unable to allocate memory for ScreenInfo structure"
430			        " for screen %d.\n",
431			        ProgramName, scrnum);
432			continue;
433		}
434
435		// Other misc adjustments to default config.
436		Scr->ShowWelcomeWindow = CLarg.ShowWelcomeWindow;
437
438
439
440		/*
441		 * Figure out the layout of our various monitors if RANDR is
442		 * around and can tell us.
443		 */
444#ifdef XRANDR
445		if(dpy) {
446			Scr->Layout = XrandrNewLayout(dpy, Scr->XineramaRoot);
447		}
448#endif
449		if(Scr->Layout == NULL) {
450			// No RANDR, so as far as we know, the layout is just one
451			// monitor with our full size.
452			RArea *fs;
453			RAreaList *fsl;
454
455			fs = RAreaNewStatic(Scr->rootx, Scr->rooty, Scr->rootw, Scr->rooth);
456			fsl = RAreaListNew(1, fs, NULL);
457			Scr->Layout = RLayoutNew(fsl);
458		}
459#ifdef DEBUG
460		fprintf(stderr, "Layout: ");
461		RLayoutPrint(Scr->Layout);
462#endif
463		if(RLayoutNumMonitors(Scr->Layout) < 1) {
464			fprintf(stderr, "Error: No monitors found on screen %d!\n", scrnum);
465			continue;
466		}
467
468
469
470		// Now we can stash some info about the screen
471		if(dpy) {
472			Scr->d_depth = DefaultDepth(dpy, scrnum);
473			Scr->d_visual = DefaultVisual(dpy, scrnum);
474			Scr->RealRoot = RootWindow(dpy, scrnum);
475			{
476				// Stash these for m4
477				Screen *tscr = ScreenOfDisplay(dpy, scrnum);
478				Scr->mm_w = tscr->mwidth;
479				Scr->mm_h = tscr->mheight;
480			}
481		}
482		else {
483			// Standin; fake the values we need in m4 parsing
484			Scr->d_visual = calloc(1, sizeof(Visual));
485			Scr->d_visual->bits_per_rgb = 8;
486			Scr->d_visual->class = TrueColor;
487		}
488
489
490		// Now that we have d_depth...
491		Scr->XORvalue = (((unsigned long) 1) << Scr->d_depth) - 1;
492
493#ifdef CAPTIVE
494		// Init captive bits.  We stick this name into m4 props, so do it
495		// before config processing.
496		if(CLarg.is_captive) {
497			Scr->CaptiveRoot = croot;
498			Scr->captivename = AddToCaptiveList(CLarg.captivename);
499			if(Scr->captivename) {
500				XmbSetWMProperties(dpy, croot,
501				                   Scr->captivename, Scr->captivename,
502				                   NULL, 0, NULL, NULL, NULL);
503			}
504		}
505#endif
506
507
508		// Init some colormap bits.  We need this before we get into the
509		// config parsing, since various things in there poke into
510		// colormaps.
511		{
512			// 1 on the root
513			Scr->RootColormaps.number_cwins = 1;
514			Scr->RootColormaps.cwins = malloc(sizeof(ColormapWindow *));
515			Scr->RootColormaps.cwins[0] = CreateColormapWindow(Scr->Root, true,
516			                              false);
517			Scr->RootColormaps.cwins[0]->visibility = VisibilityPartiallyObscured;
518
519			// Initialize storage for all maps the Screen can hold
520			Scr->cmapInfo.cmaps = NULL;
521			if(dpy) {
522				Scr->cmapInfo.maxCmaps = MaxCmapsOfScreen(ScreenOfDisplay(dpy,
523				                         Scr->screen));
524			}
525			Scr->cmapInfo.root_pushes = 0;
526			InstallColormaps(0, &Scr->RootColormaps);
527
528			// Setup which we're using
529			Scr->StdCmapInfo.head = Scr->StdCmapInfo.tail
530			                        = Scr->StdCmapInfo.mru = NULL;
531			Scr->StdCmapInfo.mruindex = 0;
532			if(dpy) {
533				LocateStandardColormaps();
534			}
535		}
536
537
538		// Are we monochrome?  Or do we care this millennium?
539		if(CLarg.Monochrome || (dpy && DisplayCells(dpy, scrnum) < 3)) {
540			Scr->Monochrome = MONOCHROME;
541		}
542		else {
543			Scr->Monochrome = COLOR;
544		}
545
546
547		// With the colormap/monochrome bits set, we can setup our
548		// default color bits.
549		GetColor(Scr->Monochrome, &(Scr->Black), "black");
550		GetColor(Scr->Monochrome, &(Scr->White), "white");
551
552		Scr->MenuShadowColor = Scr->Black;
553		Scr->IconBorderColor = Scr->Black;
554		Scr->IconManagerHighlight = Scr->Black;
555
556#define SETFB(fld) Scr->fld.fore = Scr->Black; Scr->fld.back = Scr->White;
557		SETFB(DefaultC)
558		SETFB(BorderColorC)
559		SETFB(BorderTileC)
560		SETFB(TitleC)
561		SETFB(MenuC)
562		SETFB(MenuTitleC)
563		SETFB(IconC)
564		SETFB(IconManagerC)
565		SETFB(workSpaceMgr.windowcp)
566		SETFB(workSpaceMgr.curColors)
567		SETFB(workSpaceMgr.defColors)
568#undef SETFB
569
570
571		// The first time around, we focus onto the root [of the first
572		// Screen].  Maybe we should revisit this...
573		if(dpy && FirstScreen) {
574			// XXX This func also involves a lot of stuff that isn't
575			// setup yet, and probably only works by accident.  Maybe we
576			// should just manually extract out the couple bits we
577			// actually want to run?
578			SetFocus(NULL, CurrentTime);
579			FirstScreen = false;
580		}
581
582		// Create default icon manager memory bits (in the first
583		// workspace)
584		AllocateIconManager("TWM", "Icons", "", 1);
585
586
587		/*
588		 * Mask over the screen with our welcome window stuff if we were
589		 * asked to on the command line/environment; too early to get
590		 * info from config file about it.
591		 */
592		screenmasked = false;
593		if(dpy && takeover && Scr->ShowWelcomeWindow
594		                && (welcomefile = getenv("CTWM_WELCOME_FILE"))) {
595			screenmasked = true;
596			MaskScreen(welcomefile);
597		}
598
599
600
601		/*
602		 * Load up config file
603		 */
604		{
605			bool ok = LoadTwmrc(CLarg.InitFile);
606
607			// cfgchk just displays whether there are errors, then moves
608			// on.
609			if(CLarg.cfgchk) {
610				if(ok) {
611					fprintf(stderr, "%d: No errors found\n", scrnum);
612				}
613				else {
614					fprintf(stderr, "%d: Errors found\n", scrnum);
615					cfgerrs = true;
616				}
617				continue;
618			}
619
620			// In non-config-check mode, we historically proceed even if
621			// there were errors, so keep doing that...
622		}
623
624
625		// For testing, it's useful to do all that initial setup up
626		// through parsing, and then inspect Scr and the like.
627		// Long-term, IWBNI we had a better way to do all the necessary
628		// initialization and then call the parse ourselves at that
629		// level.  But for now, provide a callback func that can pass
630		// control back to the test code, then just exits.
631		if(ctwm_test_postparse != NULL) {
632			exit(ctwm_test_postparse());
633		}
634
635
636
637		/*
638		 * Since we've loaded the config, go ahead and take over the
639		 * screen.
640		 */
641		if(takeover) {
642			if(takeover_screen(Scr) != true) {
643				// Well, move on to the next one, maybe we'll get it...
644				if(screenmasked) {
645					UnmaskScreen();
646				}
647				continue;
648			}
649
650			// Well, we got this one
651			numManaged++;
652		}
653
654		// If the config wants us to show the splash screen and we
655		// haven't already, do it now.
656		if(Scr->ShowWelcomeWindow && !screenmasked) {
657			MaskScreen(NULL);
658		}
659
660
661
662		/*
663		 * Do various setup based on the results from the config file.
664		 */
665
666		// Few simple var defaults
667		if(Scr->ClickToFocus) {
668			Scr->FocusRoot  = false;
669			Scr->TitleFocus = false;
670		}
671
672		if(Scr->use3Dborders) {
673			Scr->ClientBorderWidth = false;
674		}
675
676
677		// Now that we know what Border's there may be, create our
678		// BorderedLayout.
679		Scr->BorderedLayout = RLayoutCopyCropped(Scr->Layout,
680		                      Scr->BorderLeft, Scr->BorderRight,
681		                      Scr->BorderTop, Scr->BorderBottom);
682		if(Scr->BorderedLayout == NULL) {
683			Scr->BorderedLayout = Scr->Layout;        // nothing to crop
684		}
685		else if(Scr->BorderedLayout->monitors->len == 0) {
686			fprintf(stderr,
687			        "Borders too large! correct BorderLeft, BorderRight, BorderTop and/or BorderBottom parameters\n");
688			exit(1);
689		}
690#ifdef DEBUG
691		fprintf(stderr, "Bordered: ");
692		RLayoutPrint(Scr->BorderedLayout);
693#endif
694
695
696		/*
697		 * Setup stuff relating to VirtualScreens.  If something to do
698		 * with it is set in the config, this all implements stuff needed
699		 * for that.  If not, InitVirtualScreens() creates a single one
700		 * mirroring our real root.
701		 */
702		InitVirtualScreens(Scr);
703#ifdef VSCREEN
704#ifdef EWMH
705		EwmhInitVirtualRoots(Scr);
706#endif /* EWMH */
707#endif // vscreen
708
709		// Setup WSM[s] (per-vscreen).  This also sets up the about the
710		// workspaces for each vscreen and which is currently displayed.
711		ConfigureWorkSpaceManager(Scr);
712
713
714		/*
715		 * Various decoration default overrides for 3d/2d.  Values that
716		 * [presumtively] look "nice" on 75/100dpi displays.  -100 is a
717		 * sentinel value we set before the config file parsing; since
718		 * these defaults differ for 3d vs not, we can't just set them as
719		 * default before the parse.
720		 */
721#define SETDEF(fld, num) if(Scr->fld == -100) { Scr->fld = num; }
722		if(Scr->use3Dtitles) {
723			SETDEF(FramePadding,  0);
724			SETDEF(TitlePadding,  0);
725			SETDEF(ButtonIndent,  0);
726			SETDEF(TBInfo.border, 0);
727		}
728		else {
729			SETDEF(FramePadding,  2);
730			SETDEF(TitlePadding,  8);
731			SETDEF(ButtonIndent,  1);
732			SETDEF(TBInfo.border, 1);
733		}
734#undef SETDEF
735
736		// These values are meaningless in !3d cases, so always zero them
737		// out.
738		if(! Scr->use3Dtitles) {
739			Scr->TitleShadowDepth       = 0;
740			Scr->TitleButtonShadowDepth = 0;
741		}
742		if(! Scr->use3Dborders) {
743			Scr->BorderShadowDepth = 0;
744		}
745		if(! Scr->use3Dmenus) {
746			Scr->MenuShadowDepth = 0;
747		}
748		if(! Scr->use3Diconmanagers) {
749			Scr->IconManagerShadowDepth = 0;
750		}
751		if(! Scr->use3Dborders) {
752			Scr->ThreeDBorderWidth = 0;
753		}
754
755		// Setup colors stuff
756		if(!Scr->BeNiceToColormap) {
757			// Default pair
758			GetShadeColors(&Scr->DefaultC);
759
760			// Various conditionally 3d bits
761			if(Scr->use3Dtitles) {
762				GetShadeColors(&Scr->TitleC);
763			}
764			if(Scr->use3Dmenus) {
765				GetShadeColors(&Scr->MenuC);
766			}
767			if(Scr->use3Dmenus) {
768				GetShadeColors(&Scr->MenuTitleC);
769			}
770			if(Scr->use3Dborders) {
771				GetShadeColors(&Scr->BorderColorC);
772			}
773		}
774
775		// Defaults for IconRegion bits that aren't set.
776		for(IconRegion *ir = Scr->FirstRegion; ir; ir = ir->next) {
777			if(ir->TitleJustification == TJ_UNDEF) {
778				ir->TitleJustification = Scr->IconJustification;
779			}
780			if(ir->Justification == IRJ_UNDEF) {
781				ir->Justification = Scr->IconRegionJustification;
782			}
783			if(ir->Alignement == IRA_UNDEF) {
784				ir->Alignement = Scr->IconRegionAlignement;
785			}
786		}
787
788		// Put the results of SaveColor{} into _MIT_PRIORITY_COLORS.
789		assign_var_savecolor();
790
791		// Setup cursor values that weren't give in the config
792#define DEFCURSOR(name, val) if(!Scr->name) NewFontCursor(&Scr->name, val)
793		DEFCURSOR(FrameCursor,   "top_left_arrow");
794		DEFCURSOR(TitleCursor,   "top_left_arrow");
795		DEFCURSOR(IconCursor,    "top_left_arrow");
796		DEFCURSOR(IconMgrCursor, "top_left_arrow");
797		DEFCURSOR(MoveCursor,    "fleur");
798		DEFCURSOR(ResizeCursor,  "fleur");
799		DEFCURSOR(MenuCursor,    "sb_left_arrow");
800		DEFCURSOR(ButtonCursor,  "hand2");
801		DEFCURSOR(WaitCursor,    "watch");
802		DEFCURSOR(SelectCursor,  "dot");
803		DEFCURSOR(DestroyCursor, "pirate");
804		DEFCURSOR(AlterCursor,   "question_arrow");
805#undef DEFCURSOR
806
807		// Load up fonts for the screen.
808		//
809		// XXX HaveFonts is kinda stupid, however it gets useful in one
810		// place: when loading button bindings, we make some sort of
811		// "menu" for things (x-ref GotButton()), and the menu gen code
812		// needs to load font stuff, so if that happened in the config
813		// process, we would have already run CreateFonts().  Of course,
814		// that's a order-dependent bit of the config file parsing too;
815		// if you define the fonts too late, they wouldn't have been set
816		// by then, and we won't [re]try them now...    arg.
817		if(!Scr->HaveFonts) {
818			CreateFonts(Scr);
819		}
820
821		// Adjust settings for titlebar.  Must follow CreateFonts() call
822		// so we know these bits are populated
823		Scr->TitleBarFont.y += Scr->FramePadding;
824		Scr->TitleHeight = Scr->TitleBarFont.height + Scr->FramePadding * 2;
825		if(Scr->use3Dtitles) {
826			Scr->TitleHeight += 2 * Scr->TitleShadowDepth;
827		}
828		/* make title height be odd so buttons look nice and centered */
829		if(!(Scr->TitleHeight & 1)) {
830			Scr->TitleHeight++;
831		}
832
833
834
835		/*
836		 * Now we can start making various things.
837		 */
838
839		// Stash up a ref to our Scr on the root, so we can find the
840		// right Scr for events etc.
841		XSaveContext(dpy, Scr->Root, ScreenContext, (XPointer) Scr);
842
843		// Setup GC's for drawing, so we can start making stuff we have
844		// to actually draw.  Could move earlier, has to preceed a lot of
845		// following.
846		CreateGCs();
847
848		// Create and draw the menus we config'd
849		MakeMenus();
850
851		// Load up the images for titlebar buttons
852		InitTitlebarButtons();
853
854		// Allocate controls for WindowRegion's.  Has to follow
855		// workspaces setup, but doesn't talk to X.
856		CreateWindowRegions();
857
858		// Copy the icon managers over to workspaces past the first as
859		// necessary.  AllocateIconManager() and the config parsing
860		// already made them on the first WS.
861		AllocateOtherIconManagers();
862
863		// Create the windows for our icon managers now that all our
864		// tracking for it is setup.
865		CreateIconManagers();
866
867		// Create the WSM window (per-vscreen) and stash info on the root
868		// about our WS's.
869		CreateWorkSpaceManager();
870
871		// Create the f.occupy window
872		CreateOccupyWindow();
873
874		// Setup TwmWorkspaces menu.  Needs workspaces setup, as well as
875		// menus made.
876		MakeWorkspacesMenu();
877
878#ifdef WINBOX
879		// setup WindowBox's
880		createWindowBoxes();
881#endif
882
883		// Initialize Xrm stuff; things with setting occupation etc use
884		// Xrm bits.
885		XrmInitialize();
886
887#ifdef EWMH
888		// Set EWMH-related properties on various root-ish windows, for
889		// other programs to read to find out how we view the world.
890		EwmhInitScreenLate(Scr);
891#endif /* EWMH */
892
893
894		/*
895		 * Look up and handle all the windows on the screen.
896		 */
897		{
898			Window parent, *children;
899			unsigned int nchildren;
900
901			XQueryTree(dpy, Scr->Root, &croot, &parent, &children, &nchildren);
902
903			/* Weed out icon windows */
904			for(int i = 0; i < nchildren; i++) {
905				if(children[i]) {
906					XWMHints *wmhintsp = XGetWMHints(dpy, children[i]);
907
908					if(wmhintsp) {
909						if(wmhintsp->flags & IconWindowHint) {
910							for(int j = 0; j < nchildren; j++) {
911								if(children[j] == wmhintsp->icon_window) {
912									children[j] = None;
913									break;
914								}
915							}
916						}
917						XFree(wmhintsp);
918					}
919				}
920			}
921
922			/*
923			 * Map all of the non-override windows.  This winds down
924			 * into AddWindow() and friends through SimulateMapRequest(),
925			 * so this is where we actually adopt the windows on the
926			 * screen.
927			 */
928			for(int i = 0; i < nchildren; i++) {
929				if(children[i] && MappedNotOverride(children[i])) {
930					XUnmapWindow(dpy, children[i]);
931					SimulateMapRequest(children[i]);
932				}
933			}
934
935			/*
936			 * At this point, we've adopted all the windows currently on
937			 * the screen (aside from those we're intentionally not).
938			 * Note that this happens _before_ the various other windows
939			 * we create below, which is why they don't wind up getting
940			 * TwmWindow's tied to them or show up in icon managers, etc.
941			 * We'd need to actually make it _explicit_ that those
942			 * windows aren't tracked by us if we changed that order...
943			 */
944		}
945
946
947		// Show the WSM window if we should
948		if(Scr->ShowWorkspaceManager && Scr->workSpaceManagerActive) {
949			VirtualScreen *vs;
950			if(Scr->WindowMask) {
951				XRaiseWindow(dpy, Scr->WindowMask);
952			}
953			for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
954				SetMapStateProp(vs->wsw->twm_win, NormalState);
955				XMapWindow(dpy, vs->wsw->twm_win->frame);
956				if(vs->wsw->twm_win->StartSqueezed) {
957					Squeeze(vs->wsw->twm_win);
958				}
959				else {
960					XMapWindow(dpy, vs->wsw->w);
961				}
962				vs->wsw->twm_win->mapped = true;
963			}
964		}
965
966
967		/*
968		 * Setup the Info window, used for f.identify and f.version.
969		 */
970		{
971			unsigned long valuemask;
972			XSetWindowAttributes attributes;
973
974			attributes.border_pixel = Scr->DefaultC.fore;
975			attributes.background_pixel = Scr->DefaultC.back;
976			attributes.event_mask = (ExposureMask | ButtonPressMask |
977			                         KeyPressMask | ButtonReleaseMask);
978			NewFontCursor(&attributes.cursor, "hand2");
979			valuemask = (CWBorderPixel | CWBackPixel | CWEventMask | CWCursor);
980			Scr->InfoWindow.win =
981			        XCreateWindow(dpy, Scr->Root, 0, 0,
982			                      5, 5,
983			                      0, 0,
984			                      CopyFromParent, CopyFromParent,
985			                      valuemask, &attributes);
986		}
987
988
989		/*
990		 * Setup the Size/Position window for showing during resize/move
991		 * operations.
992		 */
993		{
994			// Stick the SizeWindow at the top left of the first monitor
995			// we found on this Screen.  That _may_ not be (0,0) (imagine
996			// a shorter left and taller right monitor, with their bottom
997			// edges lined up instead of top), so we have to look up what
998			// that coordinate is.  If we're CenterFeedbackWindow'ing,
999			// the window will have to move between monitors depending on
1000			// where the window we're moving is (starts), but
1001			// MoveResizeSizeWindow() will handle that.  If not, it
1002			// always stays in the top-left of the first display.
1003			RArea area = RLayoutGetAreaIndex(Scr->Layout, 0);
1004			XRectangle ink_rect;
1005			XRectangle logical_rect;
1006			unsigned long valuemask;
1007			XSetWindowAttributes attributes;
1008
1009			XmbTextExtents(Scr->SizeFont.font_set,
1010			               " 8888 x 8888 ", 13,
1011			               &ink_rect, &logical_rect);
1012			Scr->SizeStringWidth = logical_rect.width;
1013			valuemask = (CWBorderPixel | CWBackPixel | CWBitGravity);
1014			attributes.bit_gravity = NorthWestGravity;
1015
1016			if(Scr->SaveUnder) {
1017				attributes.save_under = True;
1018				valuemask |= CWSaveUnder;
1019			}
1020
1021			Scr->SizeWindow = XCreateWindow(dpy, Scr->Root,
1022			                                area.x, area.y,
1023			                                Scr->SizeStringWidth,
1024			                                (Scr->SizeFont.height +
1025			                                 SIZE_VINDENT * 2),
1026			                                0, 0,
1027			                                CopyFromParent,
1028			                                CopyFromParent,
1029			                                valuemask, &attributes);
1030		}
1031
1032		// Create util window used in animation
1033		Scr->ShapeWindow = XCreateSimpleWindow(dpy, Scr->Root, 0, 0,
1034		                                       Scr->rootw, Scr->rooth, 0, 0, 0);
1035
1036
1037		// Clear out the splash screen if we had one
1038		if(Scr->ShowWelcomeWindow) {
1039			UnmaskScreen();
1040		}
1041
1042		// Done setting up this Screen.  x-ref XXX's about whether this
1043		// element is worth anything...
1044		Scr->FirstTime = false;
1045	} // for each screen on display
1046
1047
1048	// If we're just checking the config, there's nothing more to do.
1049	if(CLarg.cfgchk) {
1050		exit(cfgerrs);
1051	}
1052
1053
1054	// We're not much of a window manager if we didn't get stuff to
1055	// manage...
1056	if(numManaged == 0) {
1057		if(CLarg.MultiScreen && NumScreens > 0)
1058			fprintf(stderr, "%s:  unable to find any unmanaged screens\n",
1059			        ProgramName);
1060		exit(1);
1061	}
1062
1063#ifdef SESSION
1064	// Hook up session
1065	ConnectToSessionManager(CLarg.client_id);
1066#endif
1067
1068#ifdef SOUNDS
1069	// Announce ourselves
1070	sound_load_list();
1071	play_startup_sound();
1072#endif
1073
1074	// Hard-reset this flag.
1075	// XXX This doesn't seem right?
1076	RestartPreviousState = true;
1077
1078	// Set vars to enable animation bits
1079	StartAnimation();
1080
1081	// Main loop.
1082	HandlingEvents = true;
1083	HandleEvents();
1084
1085	// Should never get here...
1086	fprintf(stderr, "Shouldn't return from HandleEvents()!\n");
1087	exit(1);
1088}
1089
1090
1091
1092/**
1093 * Initialize ScreenInfo for a Screen.  This allocates the struct,
1094 * assigns in the info we pass it about the screen and dimensions, and
1095 * then puts in our various default/fallback/sentinel/etc values to
1096 * prepare it for later use.
1097 *
1098 * It is intentional that this doesn't do any of the initialization that
1099 * involves calling out to X functions; it operates as a pure function.
1100 * This makes it easier to use it to fake up a ScreenInfo for something
1101 * that isn't actually an X Screen, for testing etc.
1102 *
1103 * \param scrnum The Screen number (e.g, :0.0 -> 0)
1104 * \param croot  The X Window for the Screen's root window
1105 * \param crootx Root X coordinate
1106 * \param crooty Root Y coordinate
1107 * \param crootw Root width
1108 * \param crooth Root height
1109 * \return Allocated and populated ScreenInfo
1110 */
1111ScreenInfo *
1112InitScreenInfo(int scrnum, Window croot, int crootx, int crooty,
1113               unsigned int crootw, unsigned int crooth)
1114{
1115	ScreenInfo *scr;
1116	scr = calloc(1, sizeof(ScreenInfo));
1117	if(scr == NULL) {
1118		return NULL;
1119	}
1120	// Because of calloc(), it's already all 0 bytes, which are NULL and
1121	// false and 0 and similar.  Some following initializations are
1122	// nugatory because of that, but are left for clarity.
1123
1124	// Poison the global Scr to protect against typos
1125#define Scr StupidProgrammer
1126
1127
1128	// Basic pieces about the X screen we're talking about, and some
1129	// derived dimension-related bits.
1130	scr->screen = scrnum;
1131	scr->XineramaRoot = scr->Root = croot;
1132	scr->rootx = crootx;
1133	scr->rooty = crooty;
1134	scr->rootw = crootw;
1135	scr->rooth = crooth;
1136
1137#ifdef CAPTIVE
1138	scr->crootx = crootx;
1139	scr->crooty = crooty;
1140	scr->crootw = crootw;
1141	scr->crooth = crooth;
1142#endif
1143
1144	// Don't allow icon titles wider than the screen
1145	scr->MaxIconTitleWidth = scr->rootw;
1146
1147	// Attempt to come up with a sane default for the max sizes.  Start
1148	// by limiting so that a window with its left/top on the right/bottom
1149	// edge of the screen can't extend further than X can address (signed
1150	// 16-bit).  However, when your screen size starts approaching that
1151	// limit, reducing the max window sizes too much gets stupid too, so
1152	// set an arbitrary floor on how low this will take it.
1153	// MaxWindowSize in the config will override whatever's here anyway.
1154	scr->MaxWindowWidth  = 32767 - (scr->rootx + scr->rootw);
1155	scr->MaxWindowHeight = 32767 - (scr->rooty + scr->rooth);
1156	if(scr->MaxWindowWidth < 4096) {
1157		scr->MaxWindowWidth = 4096;
1158	}
1159	if(scr->MaxWindowHeight < 4096) {
1160		scr->MaxWindowHeight = 4096;
1161	}
1162
1163
1164	// Flags used in the code to keep track of where in various processes
1165	// (especially startup) we are.
1166	scr->HaveFonts = false;
1167
1168	// Flag which basically means "initial screen setup time".
1169	// XXX Not clear to what extent this should even exist; a lot of
1170	// uses are fairly bogus.
1171	scr->FirstTime = true;
1172
1173	// Sentinel values for defaulting config values
1174	scr->FramePadding = -100;
1175	scr->TitlePadding = -100;
1176	scr->ButtonIndent = -100;
1177	scr->TBInfo.border = -100;
1178
1179	// Default values for all sorts of config params
1180	scr->SizeStringOffset = 0;
1181	scr->ThreeDBorderWidth = 6;
1182	scr->BorderWidth = BW;
1183	scr->IconBorderWidth = BW;
1184	scr->NumAutoRaises = 0;
1185	scr->NumAutoLowers = 0;
1186	scr->TransientOnTop = 30;
1187	scr->NoDefaults = false;
1188	scr->UsePPosition = PPOS_OFF;
1189	scr->UseSunkTitlePixmap = false;
1190	scr->FocusRoot = true;
1191	scr->WarpCursor = false;
1192	scr->ForceIcon = false;
1193	scr->NoGrabServer = true;
1194	scr->NoRaiseMove = false;
1195	scr->NoRaiseResize = false;
1196	scr->NoRaiseDeicon = false;
1197	scr->RaiseOnWarp = true;
1198	scr->DontMoveOff = false;
1199	scr->DoZoom = false;
1200	scr->TitleFocus = true;
1201	scr->IconManagerFocus = true;
1202	scr->StayUpMenus = false;
1203	scr->WarpToDefaultMenuEntry = false;
1204	scr->ClickToFocus = false;
1205	scr->SloppyFocus = false;
1206	scr->SaveWorkspaceFocus = false;
1207	scr->NoIconTitlebar = false;
1208	scr->NoTitlebar = false;
1209	scr->DecorateTransients = true;
1210	scr->IconifyByUnmapping = false;
1211	scr->ShowIconManager = false;
1212	scr->ShowWorkspaceManager = false;
1213	scr->WMgrButtonShadowDepth = 2;
1214	scr->WMgrVertButtonIndent  = 5;
1215	scr->WMgrHorizButtonIndent = 5;
1216	scr->BorderShadowDepth = 2;
1217	scr->TitleShadowDepth = 2;
1218	scr->TitleButtonShadowDepth = 2;
1219	scr->MenuShadowDepth = 2;
1220	scr->IconManagerShadowDepth = 2;
1221	scr->AutoOccupy = false;
1222	scr->TransientHasOccupation = false;
1223	scr->DontPaintRootWindow = false;
1224	scr->IconManagerDontShow = false;
1225	scr->BackingStore = false;
1226	scr->SaveUnder = true;
1227	scr->RandomPlacement = RP_ALL;
1228	scr->RandomDisplacementX = 30;
1229	scr->RandomDisplacementY = 30;
1230	scr->DoOpaqueMove = true;
1231	scr->OpaqueMove = false;
1232	scr->OpaqueMoveThreshold = 200;
1233	scr->OpaqueResize = false;
1234	scr->DoOpaqueResize = true;
1235	scr->OpaqueResizeThreshold = 1000;
1236	scr->Highlight = true;
1237	scr->StackMode = true;
1238	scr->TitleHighlight = true;
1239	scr->MoveDelta = 1;
1240	scr->MoveOffResistance = -1;
1241	scr->MovePackResistance = 20;
1242	scr->ZoomCount = 8;
1243	scr->SortIconMgr = true;
1244	scr->Shadow = true;
1245	scr->InterpolateMenuColors = false;
1246	scr->NoIconManagers = false;
1247	scr->ClientBorderWidth = false;
1248	scr->SqueezeTitle = false;
1249	scr->FirstTime = true;
1250	scr->CaseSensitive = true;
1251	scr->WarpUnmapped = false;
1252	scr->WindowRingAll = false;
1253	scr->WarpRingAnyWhere = true;
1254	scr->ShortAllWindowsMenus = false;
1255	scr->use3Diconmanagers = false;
1256	scr->use3Dmenus = false;
1257	scr->use3Dtitles = false;
1258	scr->use3Dborders = false;
1259	scr->use3Dwmap = false;
1260	scr->SunkFocusWindowTitle = false;
1261	scr->ClearShadowContrast = 50;
1262	scr->DarkShadowContrast  = 40;
1263	scr->BeNiceToColormap = false;
1264	scr->BorderCursors = false;
1265	scr->IconJustification = TJ_CENTER;
1266	scr->IconRegionJustification = IRJ_CENTER;
1267	scr->IconRegionAlignement = IRA_CENTER;
1268	scr->TitleJustification = TJ_LEFT;
1269	scr->IconifyStyle = ICONIFY_NORMAL;
1270	scr->ReallyMoveInWorkspaceManager = false;
1271	scr->ShowWinWhenMovingInWmgr = false;
1272	scr->ReverseCurrentWorkspace = false;
1273	scr->DontWarpCursorInWMap = false;
1274	scr->XMoveGrid = 1;
1275	scr->YMoveGrid = 1;
1276	scr->CenterFeedbackWindow = false;
1277	scr->ShrinkIconTitles = false;
1278	scr->AutoRaiseIcons = false;
1279	scr->AutoFocusToTransients = false;
1280	scr->OpenWindowTimeout = 0;
1281	scr->RaiseWhenAutoUnSqueeze = false;
1282	scr->RaiseOnClick = false;
1283	scr->RaiseOnClickButton = 1;
1284	scr->IgnoreModifier = 0;
1285	scr->IgnoreCaseInMenuSelection = false;
1286	scr->PackNewWindows = false;
1287	scr->AlwaysSqueezeToGravity = false;
1288	scr->NoWarpToMenuTitle = false;
1289	scr->DontToggleWorkspaceManagerState = false;
1290	scr->NameDecorations = true;
1291	scr->ForceFocus = false;
1292	scr->BorderTop    = 0;
1293	scr->BorderBottom = 0;
1294	scr->BorderLeft   = 0;
1295	scr->BorderRight  = 0;
1296	scr->PixmapDirectory   = PIXMAP_DIRECTORY;
1297#ifdef EWMH
1298	scr->PreferredIconWidth = 48;
1299	scr->PreferredIconHeight = 48;
1300
1301	scr->ewmh_CLIENT_LIST_used = 0;
1302	scr->ewmh_CLIENT_LIST_size = 16;
1303	scr->ewmh_CLIENT_LIST = calloc(scr->ewmh_CLIENT_LIST_size,
1304	                               sizeof(scr->ewmh_CLIENT_LIST[0]));
1305	if(scr->ewmh_CLIENT_LIST == NULL) {
1306		free(scr);
1307		return NULL;
1308	}
1309#endif
1310
1311	// OTP structure bits
1312	OtpScrInitData(scr);
1313
1314
1315	// WorkSpaceManager stuff
1316	scr->workSpaceMgr.initialstate  = WMS_map;
1317	scr->workSpaceMgr.buttonStyle   = STYLE_NORMAL;
1318	scr->workSpaceMgr.vspace        = scr->WMgrVertButtonIndent;
1319	scr->workSpaceMgr.hspace        = scr->WMgrHorizButtonIndent;
1320
1321	scr->workSpaceMgr.occupyWindow = calloc(1, sizeof(OccupyWindow));
1322	scr->workSpaceMgr.occupyWindow->vspace    = scr->WMgrVertButtonIndent;
1323	scr->workSpaceMgr.occupyWindow->hspace    = scr->WMgrHorizButtonIndent;
1324	scr->workSpaceMgr.occupyWindow->name      = "Occupy Window";
1325	scr->workSpaceMgr.occupyWindow->icon_name = "Occupy Window Icon";
1326
1327	scr->workSpaceMgr.name      = "WorkSpaceManager";
1328	scr->workSpaceMgr.icon_name = "WorkSpaceManager Icon";
1329
1330
1331	// Setup default fonts in case the config file doesn't
1332#define DEFAULT_NICE_FONT "-*-helvetica-bold-r-normal-*-*-120-*"
1333#define DEFAULT_FAST_FONT "-misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-*"
1334#define SETFONT(fld, var) (scr->fld##Font.basename = DEFAULT_##var##_FONT)
1335
1336	SETFONT(TitleBar,    NICE);
1337	SETFONT(Menu,        NICE);
1338	SETFONT(Icon,        NICE);
1339	SETFONT(Size,        FAST);
1340	SETFONT(IconManager, NICE);
1341	SETFONT(Default,     FAST);
1342	scr->workSpaceMgr.windowFont.basename =
1343	        "-adobe-courier-medium-r-normal--10-100-75-75-m-60-iso8859-1";
1344
1345#undef SETFONT
1346#undef DEFAULT_FAST_FONT
1347#undef DEFAULT_NICE_FONT
1348
1349
1350	// Set some fallback values that we set from the X server, for
1351	// special cases where we may not actually be talking to one.
1352	scr->d_depth = 24;
1353	scr->RealRoot = croot;
1354	scr->mm_w = 406; // 16 in
1355	scr->mm_h = 229; // 9 in
1356	scr->Monochrome = COLOR;
1357
1358	// Cleanup poisoning
1359#undef Scr
1360	return scr;
1361}
1362
1363
1364
1365
1366#ifdef CAPTIVE
1367/**
1368 * Create a new window to use for a captive ctwm.
1369 */
1370static Window
1371CreateCaptiveRootWindow(int x, int y,
1372                        unsigned int width, unsigned int height)
1373{
1374	int         scrnum;
1375	Window      ret;
1376	XWMHints    wmhints;
1377
1378	scrnum = DefaultScreen(dpy);
1379	ret = XCreateSimpleWindow(dpy, RootWindow(dpy, scrnum),
1380	                          x, y, width, height, 2, WhitePixel(dpy, scrnum),
1381	                          BlackPixel(dpy, scrnum));
1382	wmhints.initial_state = NormalState;
1383	wmhints.input         = True;
1384	wmhints.flags         = InputHint | StateHint;
1385
1386	XmbSetWMProperties(dpy, ret, "Captive ctwm", NULL, NULL, 0, NULL,
1387	                   &wmhints, NULL);
1388	XChangeProperty(dpy, ret, XA_WM_CTWM_ROOT, XA_WINDOW, 32,
1389	                PropModeReplace, (unsigned char *) &ret, 1);
1390	XSelectInput(dpy, ret, StructureNotifyMask);
1391	XMapWindow(dpy, ret);
1392	return (ret);
1393}
1394#endif
1395
1396
1397
1398/**
1399 * Return true if a window is not set to override_redirect ("Hey!  WM!
1400 * Leave those wins alone!"), and isn't unmapped.  Used during startup to
1401 * fake mapping for wins that should be up.
1402 */
1403static bool
1404MappedNotOverride(Window w)
1405{
1406	XWindowAttributes wa;
1407
1408	XGetWindowAttributes(dpy, w, &wa);
1409	return ((wa.map_state != IsUnmapped) && (wa.override_redirect != True));
1410}
1411