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