1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, see: <http://www.gnu.org/licenses/>
14  */
15 
16 /* ---------------------------- included header files ---------------------- */
17 
18 #include "config.h"
19 
20 #include <stdio.h>
21 #include <signal.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #ifdef HAVE_FCNTL_H
25 #include <fcntl.h>
26 #endif
27 #if HAVE_SYS_SYSTEMINFO_H
28 /* Solaris has sysinfo instead of gethostname.  */
29 #include <sys/systeminfo.h>
30 #endif
31 
32 #include "libs/fvwm_x11.h"
33 #include "libs/fvwmlib.h"
34 #include "libs/envvar.h"
35 #include "libs/Strings.h"
36 #include "libs/System.h"
37 #include "libs/Grab.h"
38 #include "libs/ColorUtils.h"
39 #include "libs/Graphics.h"
40 #include "libs/FShape.h"
41 #include "libs/FEvent.h"
42 #include "libs/PictureBase.h"
43 #include "libs/PictureUtils.h"
44 #include "libs/Fsvg.h"
45 #include "libs/FRenderInit.h"
46 #include "libs/charmap.h"
47 #include "libs/wcontext.h"
48 #include "libs/getpwuid.h"
49 #include "fvwm.h"
50 #include "externs.h"
51 #include "cursor.h"
52 #include "functions.h"
53 #include "misc.h"
54 #include "screen.h"
55 #include "builtins.h"
56 #include "module_list.h"
57 #include "colorset.h"
58 #include "events.h"
59 #include "eventhandler.h"
60 #include "eventmask.h"
61 #include "icccm2.h"
62 #include "ewmh.h"
63 #include "add_window.h"
64 #include "libs/fvwmsignal.h"
65 #include "stack.h"
66 #include "virtual.h"
67 #include "session.h"
68 #include "read.h"
69 #include "focus.h"
70 #include "update.h"
71 #include "move_resize.h"
72 #include "frame.h"
73 #include "menus.h"
74 #include "menubindings.h"
75 #include "libs/FGettext.h"
76 
77 /* ---------------------------- local definitions -------------------------- */
78 
79 #define MAXHOSTNAME 255
80 #define MAX_CFG_CMDS 10
81 #define MAX_ARG_SIZE 25
82 
83 #define g_width 2
84 #define g_height 2
85 #define l_g_width 4
86 #define l_g_height 2
87 #define s_g_width 4
88 #define s_g_height 4
89 
90 /* ---------------------------- local macros ------------------------------- */
91 
92 /* ---------------------------- imports ------------------------------------ */
93 
94 extern int last_event_type;
95 
96 /* ---------------------------- included code files ------------------------ */
97 
98 /* ---------------------------- local types -------------------------------- */
99 
100 typedef enum
101 {
102 	FVWM_RUNNING = 0,
103 	FVWM_DONE,
104 	FVWM_RESTART
105 } fvwm_run_state_t;
106 
107 /* ---------------------------- forward declarations ----------------------- */
108 
109 /* ---------------------------- local variables ---------------------------- */
110 
111 static char *config_commands[MAX_CFG_CMDS];
112 static int num_config_commands=0;
113 
114 /* assorted gray bitmaps for decorative borders */
115 static char g_bits[] =
116 {0x02, 0x01};
117 static char l_g_bits[] =
118 {0x08, 0x02};
119 static char s_g_bits[] =
120 {0x01, 0x02, 0x04, 0x08};
121 
122 static const char *home_dir;
123 
124 static volatile sig_atomic_t fvwmRunState = FVWM_RUNNING;
125 
126 static const char *init_function_names[4] =
127 {
128 	"InitFunction",
129 	"RestartFunction",
130 	"ExitFunction",
131 	"Nop"
132 };
133 
134 /* ---------------------------- exported variables (globals) --------------- */
135 
136 int master_pid;                 /* process number of 1st fvwm process */
137 
138 ScreenInfo Scr;                 /* structures for the screen */
139 Display *dpy = NULL;            /* which display are we talking to */
140 
141 Bool fFvwmInStartup = True;     /* Set to False when startup has finished */
142 Bool DoingCommandLine = False;  /* Set True before each cmd line arg */
143 
144 XContext FvwmContext;           /* context for fvwm windows */
145 XContext MenuContext;           /* context for fvwm menus */
146 
147 int JunkX = 0, JunkY = 0;
148 Window JunkRoot, JunkChild;             /* junk window */
149 int JunkWidth, JunkHeight, JunkBW, JunkDepth;
150 unsigned int JunkMask;
151 
152 Bool debugging = False;
153 Bool debugging_stack_ring = False;
154 
155 Window bad_window = None;
156 
157 char **g_argv;
158 int g_argc;
159 
160 char *state_filename = NULL;
161 char *restart_state_filename = NULL;  /* $HOME/.fs-restart */
162 
163 Bool Restarting = False;
164 int x_fd;
165 char *display_name = NULL;
166 char *fvwm_userdir;
167 char const *Fvwm_VersionInfo;
168 char const *Fvwm_LicenseInfo;
169 char *Fvwm_SupportInfo;
170 
171 Atom _XA_MIT_PRIORITY_COLORS;
172 Atom _XA_WM_CHANGE_STATE;
173 Atom _XA_WM_STATE;
174 Atom _XA_WM_COLORMAP_WINDOWS;
175 Atom _XA_WM_TAKE_FOCUS;
176 Atom _XA_WM_DELETE_WINDOW;
177 Atom _XA_WM_DESKTOP;
178 Atom _XA_MwmAtom;
179 Atom _XA_MOTIF_WM;
180 
181 Atom _XA_OL_WIN_ATTR;
182 Atom _XA_OL_WT_BASE;
183 Atom _XA_OL_WT_CMD;
184 Atom _XA_OL_WT_HELP;
185 Atom _XA_OL_WT_NOTICE;
186 Atom _XA_OL_WT_OTHER;
187 Atom _XA_OL_DECOR_ADD;
188 Atom _XA_OL_DECOR_DEL;
189 Atom _XA_OL_DECOR_CLOSE;
190 Atom _XA_OL_DECOR_RESIZE;
191 Atom _XA_OL_DECOR_HEADER;
192 Atom _XA_OL_DECOR_ICON_NAME;
193 
194 Atom _XA_WM_WINDOW_ROLE;
195 Atom _XA_WINDOW_ROLE;
196 Atom _XA_WM_CLIENT_LEADER;
197 Atom _XA_SM_CLIENT_ID;
198 
199 Atom _XA_XROOTPMAP_ID;
200 Atom _XA_XSETROOT_ID;
201 
202 /* ---------------------------- local functions ---------------------------- */
203 
SaveDesktopState(void)204 static void SaveDesktopState(void)
205 {
206 	FvwmWindow *t;
207 	unsigned long data[1];
208 
209 	for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
210 	{
211 		data[0] = (unsigned long) t->Desk;
212 		XChangeProperty(
213 			dpy, FW_W(t), _XA_WM_DESKTOP, _XA_WM_DESKTOP, 32,
214 			PropModeReplace, (unsigned char *)data, 1);
215 	}
216 	data[0] = (unsigned long) monitor_get_current()->virtual_scr.CurrentDesk;
217 	XChangeProperty(
218 		dpy, Scr.Root, _XA_WM_DESKTOP, _XA_WM_DESKTOP, 32,
219 		PropModeReplace, (unsigned char *) data, 1);
220 	XFlush(dpy);
221 
222 	return;
223 }
224 
InternUsefulAtoms(void)225 static void InternUsefulAtoms (void)
226 {
227 	/* Create priority colors if necessary. */
228 	_XA_MIT_PRIORITY_COLORS = XInternAtom(
229 		dpy, "_MIT_PRIORITY_COLORS", False);
230 	_XA_WM_CHANGE_STATE = XInternAtom(dpy, "WM_CHANGE_STATE", False);
231 	_XA_WM_STATE = XInternAtom(dpy, "WM_STATE", False);
232 	_XA_WM_COLORMAP_WINDOWS = XInternAtom(
233 		dpy, "WM_COLORMAP_WINDOWS", False);
234 	_XA_WM_PROTOCOLS = XInternAtom(dpy, "WM_PROTOCOLS", False);
235 	_XA_WM_TAKE_FOCUS = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
236 	_XA_WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
237 	_XA_WM_DESKTOP = XInternAtom(dpy, "WM_DESKTOP", False);
238 	_XA_MwmAtom=XInternAtom(dpy, "_MOTIF_WM_HINTS",False);
239 	_XA_MOTIF_WM=XInternAtom(dpy, "_MOTIF_WM_INFO",False);
240 	_XA_OL_WIN_ATTR=XInternAtom(dpy, "_OL_WIN_ATTR",False);
241 	_XA_OL_WT_BASE=XInternAtom(dpy, "_OL_WT_BASE",False);
242 	_XA_OL_WT_CMD=XInternAtom(dpy, "_OL_WT_CMD",False);
243 	_XA_OL_WT_HELP=XInternAtom(dpy, "_OL_WT_HELP",False);
244 	_XA_OL_WT_NOTICE=XInternAtom(dpy, "_OL_WT_NOTICE",False);
245 	_XA_OL_WT_OTHER=XInternAtom(dpy, "_OL_WT_OTHER",False);
246 	_XA_OL_DECOR_ADD=XInternAtom(dpy, "_OL_DECOR_ADD",False);
247 	_XA_OL_DECOR_DEL=XInternAtom(dpy, "_OL_DECOR_DEL",False);
248 	_XA_OL_DECOR_CLOSE=XInternAtom(dpy, "_OL_DECOR_CLOSE",False);
249 	_XA_OL_DECOR_RESIZE=XInternAtom(dpy, "_OL_DECOR_RESIZE",False);
250 	_XA_OL_DECOR_HEADER=XInternAtom(dpy, "_OL_DECOR_HEADER",False);
251 	_XA_OL_DECOR_ICON_NAME=XInternAtom(dpy, "_OL_DECOR_ICON_NAME",False);
252 	_XA_WM_WINDOW_ROLE=XInternAtom(dpy, "WM_WINDOW_ROLE",False);
253 	_XA_WINDOW_ROLE=XInternAtom(dpy, "WINDOW_ROLE",False);
254 	_XA_WM_CLIENT_LEADER=XInternAtom(dpy, "WM_CLIENT_LEADER",False);
255 	_XA_SM_CLIENT_ID=XInternAtom(dpy, "SM_CLIENT_ID",False);
256 	_XA_XROOTPMAP_ID=XInternAtom(dpy, "_XROOTPMAP_ID",False);
257 	_XA_XSETROOT_ID=XInternAtom(dpy, "_XSETROOT_ID",False);
258 
259 	return;
260 }
261 
262 /* exit handler that will try to release any grabs */
catch_exit(void)263 static void catch_exit(void)
264 {
265 	if (dpy != NULL)
266 	{
267 		/* Don't care if this is called from an X error handler. We
268 		 * *have* to try this, whatever happens. XFree 4.0 may freeze
269 		 * if we don't do this. */
270 		XUngrabServer(dpy);
271 		XUngrabPointer(dpy, CurrentTime);
272 		XUngrabKeyboard(dpy, CurrentTime);
273 	}
274 
275 	return;
276 }
277 
278 /*
279  * Restart on a signal
280  */
281 static RETSIGTYPE
Restart(int sig)282 Restart(int sig)
283 {
284 	fvwmRunState = FVWM_RESTART;
285 
286 	/* This function might not return - it could "long-jump"
287 	 * right out, so we need to do everything we need to do
288 	 * BEFORE we call it ... */
289 	fvwmSetTerminate(sig);
290 
291 	SIGNAL_RETURN;
292 }
293 
294 static RETSIGTYPE
ToggleLogging(int sig)295 ToggleLogging(int sig)
296 {
297 	log_toggle(fvwm_userdir);
298 
299 	SIGNAL_RETURN;
300 }
301 
302 static RETSIGTYPE
SigDone(int sig)303 SigDone(int sig)
304 {
305 	fvwmRunState = FVWM_DONE;
306 
307 	/* This function might not return - it could "long-jump"
308 	 * right out, so we need to do everything we need to do
309 	 * BEFORE we call it ... */
310 	fvwmSetTerminate(sig);
311 
312 	SIGNAL_RETURN;
313 }
314 
315 /*
316  * parse_command_args - parses a given command string into a given limited
317  * argument array suitable for execv*. The parsing is similar to shell's.
318  * Returns:
319  *   positive number of parsed arguments - on success,
320  *   0 - on empty command (only spaces),
321  *   negative - on no command or parsing error.
322  *
323  * Any character can be quoted with a backslash (even inside single quotes).
324  * Every command argument is separated by a space/tab/new-line from both sizes
325  * or is at the start/end of the command. Sequential spaces are ignored.
326  * An argument can be enclosed into single quotes (no further expanding)
327  * or double quotes (expending environmental variables $VAR or ${VAR}).
328  * The character '~' is expanded into user home directory (if not in quotes).
329  *
330  * In the current implementation, parsed arguments are stored in one
331  * large static string pointed by returned argv[0], so they will be lost
332  * on the next function call. This can be changed using dynamic allocation,
333  * in this case the caller must free the string pointed by argv[0].
334  */
parse_command_args(const char * command,char ** argv,int max_argc,const char ** error_msg)335 static int parse_command_args(
336 	const char *command, char **argv, int max_argc, const char **error_msg)
337 {
338 	/* It is impossible to guess the exact length because of expanding */
339 #define MAX_TOTAL_ARG_LEN 256
340 	/* char *arg_string = fxmalloc(MAX_TOTAL_ARG_LEN); */
341 	static char arg_string[MAX_TOTAL_ARG_LEN];
342 	int total_arg_len = 0;
343 	int error_code = 0;
344 	int argc;
345 	char *aptr = arg_string;
346 	const char *cptr = command;
347 
348 #define the_char (*cptr)
349 #define adv_char (cptr++)
350 #define top_char (*cptr     == '\\' ? *(cptr + 1) : *cptr)
351 #define pop_char (*(cptr++) == '\\' ? *(cptr++) : *(cptr - 1))
352 #define can_add_arg_char (total_arg_len < MAX_TOTAL_ARG_LEN-1)
353 #define add_arg_char(ch) (++total_arg_len, *(aptr++) = ch)
354 #define can_add_arg_str(str) (total_arg_len < MAX_TOTAL_ARG_LEN - strlen(str))
355 #define add_arg_str(str) \
356 {\
357 	const char *tmp = str;\
358 	while (*tmp)\
359 	{\
360 		add_arg_char(*(tmp++));\
361 	}\
362 }
363 
364 	*error_msg = "";
365 	if (!command)
366 	{
367 		*error_msg = "No command";
368 		return -1;
369 	}
370 	for (argc = 0; argc < max_argc - 1; argc++)
371 	{
372 		int s_quote = 0;
373 		argv[argc] = aptr;
374 		while (isspace(the_char))
375 		{
376 			adv_char;
377 		}
378 		if (the_char == '\0')
379 		{
380 			break;
381 		}
382 		while ((s_quote || !isspace(the_char)) &&
383 		       the_char != '\0' && can_add_arg_char)
384 		{
385 			if (the_char == '"')
386 			{
387 				if (s_quote)
388 				{
389 					s_quote = 0;
390 				}
391 				else
392 				{
393 					s_quote = 1;
394 				}
395 				adv_char;
396 			}
397 			else if (!s_quote && the_char == '\'')
398 			{
399 				adv_char;
400 				while (the_char != '\'' && the_char != '\0' &&
401 				       can_add_arg_char)
402 				{
403 					add_arg_char(pop_char);
404 				}
405 				if (the_char == '\'')
406 				{
407 					adv_char;
408 				}
409 				else if (!can_add_arg_char)
410 				{
411 					break;
412 				}
413 				else
414 				{
415 					*error_msg = "No closing single quote";
416 					error_code = -3;
417 					break;
418 				}
419 			}
420 			else if (!s_quote && the_char == '~')
421 			{
422 				if (!can_add_arg_str(home_dir))
423 				{
424 					break;
425 				}
426 				add_arg_str(home_dir);
427 				adv_char;
428 			}
429 			else if (the_char == '$')
430 			{
431 				int beg, len;
432 				const char *str = getFirstEnv(cptr, &beg, &len);
433 
434 				if (!str || beg)
435 				{
436 					add_arg_char(the_char);
437 					adv_char;
438 					continue;
439 				}
440 				if (!can_add_arg_str(str))
441 				{
442 					break;
443 				}
444 				add_arg_str(str);
445 				cptr += len;
446 			}
447 			else
448 			{
449 				if (add_arg_char(pop_char) == '\0')
450 				{
451 					break;
452 				}
453 			}
454 		}
455 		if (*(aptr-1) == '\0')
456 		{
457 			*error_msg = "Unexpected last backslash";
458 			error_code = -2;
459 			break;
460 		}
461 		if (error_code)
462 		{
463 			break;
464 		}
465 		if (the_char == '~' || the_char == '$' || !can_add_arg_char)
466 		{
467 			*error_msg = "The command is too long";
468 			error_code = -argc - 100;
469 			break;
470 		}
471 		if (s_quote)
472 		{
473 			*error_msg = "No closing double quote";
474 			error_code = -4;
475 			break;
476 		}
477 		add_arg_char('\0');
478 	}
479 #undef the_char
480 #undef adv_char
481 #undef top_char
482 #undef pop_char
483 #undef can_add_arg_char
484 #undef add_arg_char
485 #undef can_add_arg_str
486 #undef add_arg_str
487 	argv[argc] = NULL;
488 	if (argc == 0 && !error_code)
489 	{
490 		*error_msg = "Void command";
491 	}
492 
493 	return error_code ? error_code : argc;
494 }
495 
496 /*
497  *
498  */
499 static
get_display_name(char * display_name,int screen_num)500 char *get_display_name(char *display_name, int screen_num)
501 {
502 	char *msg;
503 	char *new_dn;
504 	char *cp;
505 	char string_screen_num[32];
506 
507 	CopyString(&msg, display_name);
508 	cp = strchr(msg, ':');
509 	if (cp != NULL)
510 	{
511 		cp = strchr(cp, '.');
512 		if (cp != NULL)
513 		{
514 			/* truncate at display part */
515 			*cp = '\0';
516 		}
517 	}
518 	sprintf(string_screen_num, ".%d", screen_num);
519 	/* TA:  FIXME!  Use asprintF() */
520 	new_dn = fxmalloc(strlen(msg) + strlen(string_screen_num) + 1);
521 	*new_dn = '\0';
522 	strcat(new_dn, msg);
523 	strcat(new_dn, string_screen_num);
524 	free(msg);
525 
526 	return new_dn;
527 }
528 
529 /*
530  *
531  *  Procedure:
532  *      Done - tells fvwm to clean up and exit
533  *
534  */
535 /* if restart is true, command must not be NULL... */
Done(int restart,char * command)536 void Done(int restart, char *command)
537 {
538 	const char *exit_func_name;
539 	struct monitor	*m = monitor_get_current();
540 
541 	if (!restart)
542 	{
543 		MoveViewport(m, 0,0,False);
544 	}
545 	/* migo (03/Jul/1999): execute [Session]ExitFunction */
546 	exit_func_name = get_init_function_name(2);
547 	if (functions_is_complex_function(exit_func_name))
548 	{
549 		const exec_context_t *exc;
550 		exec_context_changes_t ecc;
551 
552 		char *action;
553 
554 		xasprintf(&action, "Function ", exit_func_name);
555 
556 		ecc.type = restart ? EXCT_TORESTART : EXCT_QUIT;
557 		ecc.w.wcontext = C_ROOT;
558 		exc = exc_create_context(&ecc, ECC_TYPE | ECC_WCONTEXT);
559 		execute_function(NULL, exc, action, 0);
560 		exc_destroy_context(exc);
561 		free(action);
562 	}
563 	/* XFree freeze hack */
564 	XUngrabPointer(dpy, CurrentTime);
565 	XUngrabKeyboard(dpy, CurrentTime);
566 	XUngrabServer(dpy);
567 	if (!restart)
568 	{
569 		Reborder();
570 	}
571 	EWMH_ExitStuff();
572 	if (restart)
573 	{
574 		Bool do_preserve_state = True;
575 		SaveDesktopState();
576 
577 		if (command)
578 		{
579 			while (isspace(command[0]))
580 			{
581 				command++;
582 			}
583 			if (strncmp(command, "--dont-preserve-state", 21) == 0)
584 			{
585 				do_preserve_state = False;
586 				command += 21;
587 				while (isspace(command[0])) command++;
588 			}
589 		}
590 		if (command[0] == '\0')
591 		{
592 			command = NULL; /* native restart */
593 		}
594 
595 		/* won't return under SM on Restart without parameters */
596 		RestartInSession(
597 			restart_state_filename, command == NULL,
598 			do_preserve_state);
599 
600 		/* RBW - 06/08/1999 - without this, windows will wander to
601 		 * other pages on a Restart/Recapture because Restart gets the
602 		 * window position information out of sync. There may be a
603 		 * better way to do this (i.e., adjust the Restart code), but
604 		 * this works for now. */
605 		MoveViewport(m, 0,0,False);
606 		Reborder();
607 
608 		/* Really make sure that the connection is closed and cleared!
609 		 */
610 		CloseICCCM2();
611 		catch_exit();
612 		XCloseDisplay(dpy);
613 		dpy = NULL;
614 
615 		/* really need to destroy all windows, explicitly, not sleep,
616 		 * but this is adequate for now */
617 		sleep(1);
618 
619 		if (command)
620 		{
621 			char *my_argv[MAX_ARG_SIZE];
622 			const char *error_msg;
623 			int n = parse_command_args(
624 				command, my_argv, MAX_ARG_SIZE, &error_msg);
625 
626 			if (n <= 0)
627 			{
628 				fvwm_debug(__func__,
629 					   "Restart command parsing error in"
630 					   " (%s): [%s]", command, error_msg);
631 			}
632 			else if (strcmp(my_argv[0], "--pass-args") == 0)
633 			{
634 				if (n != 2)
635 				{
636 					fvwm_debug(__func__,
637 						   "Restart --pass-args: single"
638 						   " name expected. (restarting"
639 						   " '%s' instead)",
640 						   g_argv[0]);
641 
642 				}
643 				else
644 				{
645 					int i;
646 					my_argv[0] = my_argv[1];
647 					for (i = 1; i < g_argc &&
648 						     i < MAX_ARG_SIZE - 1; i++)
649 					{
650 						my_argv[i] = g_argv[i];
651 					}
652 					my_argv[i] = NULL;
653 
654 					execvp(my_argv[0], my_argv);
655 					fvwm_debug(__func__,
656 						   "Call of '%s' failed!"
657 						   " (restarting '%s' instead)",
658 						   my_argv[0], g_argv[0]);
659 					perror("  system error description");
660 				}
661 
662 			}
663 			else
664 			{
665 				char *str = NULL;
666 
667 				/* Warn against an old 'Restart fvwm2' usage */
668 				if (n == 1 && strcmp(my_argv[0], "fvwm2") == 0)
669 				{
670 					str = "fvwm2";
671 				}
672 				/* If we are at it, warn against a 'Restart
673 				 * fvwm' usage as well */
674 				else if (n == 1 &&
675 					 strcmp(my_argv[0], "fvwm") == 0)
676 				{
677 					str = "fvwm";
678 				}
679 				if (str)
680 				{
681 					fvwm_debug(__func__,
682 						   "`Restart %s' might not do"
683 						   " what you want, see the man"
684 						   " page.\n\tUse Restart without"
685 						   " parameters if you mean to"
686 						   " restart the same WM.",
687 						   str);
688 				}
689 				execvp(my_argv[0], my_argv);
690 				fvwm_debug(__func__, "Call of '%s' failed!"
691 					   " (restarting '%s' instead)",
692 					   my_argv[0], g_argv[0]);
693 				perror("  system error description");
694 			}
695 		}
696 
697 		execvp(g_argv[0], g_argv);    /* that _should_ work */
698 		fvwm_debug(__func__, "Call of '%s' failed!", g_argv[0]);
699 		perror("  system error description");
700 	}
701 	else
702 	{
703 		CloseICCCM2();
704 		catch_exit();
705 		XCloseDisplay(dpy);
706 		dpy = NULL;
707 	}
708 
709 	/* dv (15-Jan-2000): This must be done after calling CloseICCCM2()!
710 	 * Otherwise fvwm ignores map requests while it still has
711 	 * SubstructureRedirect selected on the root window ==> windows end up
712 	 * in nirvana. This explicitly happened with windows unswallowed by
713 	 * FvwmButtons. */
714 	module_kill_all();
715 
716 	exit(0);
717 }
718 
719 /***********************************************************************
720  *
721  *  Procedure:
722  *      InstallSignals: install the signal handlers, using whatever
723  *         means we have at our disposal. The more POSIXy, the better
724  *
725  ************************************************************************/
726 static void
InstallSignals(void)727 InstallSignals(void)
728 {
729 #ifdef HAVE_SIGACTION
730 	struct sigaction  sigact;
731 
732 	/*
733 	 * All signals whose handlers call fvwmSetTerminate()
734 	 * must be mutually exclusive - we mustn't receive one
735 	 * while processing any of the others ...
736 	 */
737 	sigemptyset(&sigact.sa_mask);
738 	sigaddset(&sigact.sa_mask, SIGINT);
739 	sigaddset(&sigact.sa_mask, SIGHUP);
740 	sigaddset(&sigact.sa_mask, SIGQUIT);
741 	sigaddset(&sigact.sa_mask, SIGTERM);
742 	sigaddset(&sigact.sa_mask, SIGUSR1);
743 	sigaddset(&sigact.sa_mask, SIGUSR2);
744 
745 #ifdef SA_RESTART
746 	sigact.sa_flags = SA_RESTART;
747 #else
748 	sigact.sa_flags = 0;
749 #endif
750 	sigact.sa_handler = DeadPipe;
751 	sigaction(SIGPIPE, &sigact, NULL);
752 
753 	sigact.sa_handler = Restart;
754 	sigaction(SIGUSR1, &sigact, NULL);
755 
756 	sigact.sa_handler = ToggleLogging;
757 	sigaction(SIGUSR2, &sigact, NULL);
758 
759 	sigact.sa_handler = SigDone;
760 	sigaction(SIGINT,  &sigact, NULL);
761 	sigaction(SIGHUP,  &sigact, NULL);
762 	sigaction(SIGQUIT, &sigact, NULL);
763 	sigaction(SIGTERM, &sigact, NULL);
764 
765 	/* Unblock these signals so that we can process them again. */
766 	sigprocmask(SIG_UNBLOCK, &sigact.sa_mask, NULL);
767 
768 	/* Reap all zombies automatically! This signal handler will only be
769 	 * called if a child process dies, not if someone sends a child a STOP
770 	 * signal. Note that none of our "terminate" signals can be delivered
771 	 * until the SIGCHLD handler completes, and this is a Good Thing
772 	 * because the terminate handlers might exit abruptly via "siglongjmp".
773 	 * This could potentially leave SIGCHLD handler with unfinished
774 	 * business ...
775 	 *
776 	 * NOTE:  We could still receive SIGPIPE signals within the SIGCHLD
777 	 * handler, but the SIGPIPE handler has the SA_RESTART flag set and so
778 	 * should not affect our "wait" system call. */
779 	sigact.sa_flags |= SA_NOCLDSTOP;
780 	sigact.sa_handler = fvwmReapChildren;
781 	sigaction(SIGCHLD, &sigact, NULL);
782 #else
783 #ifdef USE_BSD_SIGNALS
784 	fvwmSetSignalMask(
785 		sigmask(SIGUSR1) | sigmask(SIGUSR2) | sigmask(SIGINT) |
786 		sigmask(SIGHUP)  | sigmask(SIGQUIT) | sigmask(SIGTERM) );
787 #endif
788 
789 	/*
790 	 * We don't have sigaction(), so fall back on
791 	 * less reliable methods ...
792 	 */
793 	signal(SIGPIPE, DeadPipe);
794 	signal(SIGUSR1, Restart);
795 	signal(SIGUSR2, ToggleLogging);
796 #ifdef HAVE_SIGINTERRUPT
797 	siginterrupt(SIGUSR1, 0);
798 #endif
799 
800 	signal(SIGINT, SigDone);
801 #ifdef HAVE_SIGINTERRUPT
802 	siginterrupt(SIGINT, 0);
803 #endif
804 	signal(SIGHUP, SigDone);
805 #ifdef HAVE_SIGINTERRUPT
806 	siginterrupt(SIGHUP, 0);
807 #endif
808 	signal(SIGQUIT, SigDone);
809 #ifdef HAVE_SIGINTERRUPT
810 	siginterrupt(SIGQUIT, 0);
811 #endif
812 	signal(SIGTERM, SigDone);
813 #ifdef HAVE_SIGINTERRUPT
814 	siginterrupt(SIGTERM, 0);
815 #endif
816 	signal(SIGCHLD, fvwmReapChildren);
817 #ifdef HAVE_SIGINTERRUPT
818 	siginterrupt(SIGCHLD, 0);
819 #endif
820 #endif
821 
822 	/* When fvwm restarts, the SIGCHLD handler is automatically reset
823 	 * to the default handler. This means that Zombies left over from
824 	 * the previous instance of fvwm could still be roaming the process
825 	 * table if they exited while the default handler was in place.
826 	 * We fix this by invoking the SIGCHLD handler NOW, so that they
827 	 * may finally rest in peace. */
828 	fvwmReapChildren(0);
829 
830 	return;
831 }
832 
fvmm_deinstall_signals(void)833 void fvmm_deinstall_signals(void)
834 {
835 	signal(SIGCHLD, SIG_DFL);
836 	signal(SIGHUP, SIG_DFL);
837 	signal(SIGINT, SIG_DFL);
838 	signal(SIGPIPE, SIG_DFL);
839 	signal(SIGQUIT, SIG_DFL);
840 	signal(SIGTERM, SIG_DFL);
841 	signal(SIGUSR1, SIG_DFL);
842 	signal(SIGUSR2, SIG_DFL);
843 
844 	return;
845 }
846 
847 /***********************************************************************
848  *
849  *  LoadDefaultLeftButton -- loads default left button # into
850  *              assumes associated button memory is already free
851  *
852  ************************************************************************/
LoadDefaultLeftButton(DecorFace * df,int i)853 static void LoadDefaultLeftButton(DecorFace *df, int i)
854 {
855 	struct vector_coords *v = &df->u.vector;
856 	int j = 0;
857 
858 	memset(&df->style, 0, sizeof(df->style));
859 	DFS_FACE_TYPE(df->style) = DefaultVectorButton;
860 	switch (i % 5)
861 	{
862 	case 0:
863 	case 4:
864 		v->num = 5;
865 		v->x = fxmalloc(sizeof(char) * v->num);
866 		v->y = fxmalloc(sizeof(char) * v->num);
867 		v->xoff = fxmalloc(sizeof(char) * v->num);
868 		v->yoff = fxmalloc(sizeof(char) * v->num);
869 		v->c = fxcalloc(v->num, sizeof(char));
870 		v->x[0] = 22;
871 		v->y[0] = 39;
872 		v->c[0] = 1;
873 		v->x[1] = 78;
874 		v->y[1] = 39;
875 		v->c[1] = 1;
876 		v->x[2] = 78;
877 		v->y[2] = 61;
878 		v->x[3] = 22;
879 		v->y[3] = 61;
880 		v->x[4] = 22;
881 		v->y[4] = 39;
882 		v->c[4] = 1;
883 		break;
884 	case 1:
885 		v->num = 5;
886 		v->x = fxmalloc(sizeof(char) * v->num);
887 		v->y = fxmalloc(sizeof(char) * v->num);
888 		v->xoff = fxmalloc(sizeof(char) * v->num);
889 		v->yoff = fxmalloc(sizeof(char) * v->num);
890 		v->c = fxcalloc(v->num, sizeof(char));
891 		v->x[0] = 32;
892 		v->y[0] = 45;
893 		v->x[1] = 68;
894 		v->y[1] = 45;
895 		v->x[2] = 68;
896 		v->y[2] = 55;
897 		v->c[2] = 1;
898 		v->x[3] = 32;
899 		v->y[3] = 55;
900 		v->c[3] = 1;
901 		v->x[4] = 32;
902 		v->y[4] = 45;
903 		break;
904 	case 2:
905 		v->num = 5;
906 		v->x = fxmalloc(sizeof(char) * v->num);
907 		v->y = fxmalloc(sizeof(char) * v->num);
908 		v->xoff = fxmalloc(sizeof(char) * v->num);
909 		v->yoff = fxmalloc(sizeof(char) * v->num);
910 		v->c = fxcalloc(v->num, sizeof(char));
911 		v->x[0] = 49;
912 		v->y[0] = 49;
913 		v->c[0] = 1;
914 		v->x[1] = 51;
915 		v->y[1] = 49;
916 		v->c[1] = 1;
917 		v->x[2] = 51;
918 		v->y[2] = 51;
919 		v->x[3] = 49;
920 		v->y[3] = 51;
921 		v->x[4] = 49;
922 		v->y[4] = 49;
923 		v->c[4] = 1;
924 		break;
925 	case 3:
926 		v->num = 5;
927 		v->x = fxmalloc(sizeof(char) * v->num);
928 		v->y = fxmalloc(sizeof(char) * v->num);
929 		v->xoff = fxmalloc(sizeof(char) * v->num);
930 		v->yoff = fxmalloc(sizeof(char) * v->num);
931 		v->c = fxcalloc(v->num, sizeof(char));
932 		v->x[0] = 32;
933 		v->y[0] = 45;
934 		v->c[0] = 1;
935 		v->x[1] = 68;
936 		v->y[1] = 45;
937 		v->c[1] = 1;
938 		v->x[2] = 68;
939 		v->y[2] = 55;
940 		v->x[3] = 32;
941 		v->y[3] = 55;
942 		v->x[4] = 32;
943 		v->y[4] = 45;
944 		v->c[4] = 1;
945 		break;
946 	}
947 	/* set offsets to 0, for all buttons */
948 	for(j = 0 ; j < v->num ; j++)
949 	{
950 	  v->xoff[j] = 0;
951 	  v->yoff[j] = 0;
952 	}
953 
954 	return;
955 }
956 
957 /***********************************************************************
958  *
959  *  LoadDefaultRightButton -- loads default left button # into
960  *              assumes associated button memory is already free
961  *
962  ************************************************************************/
LoadDefaultRightButton(DecorFace * df,int i)963 static void LoadDefaultRightButton(DecorFace *df, int i)
964 {
965 	struct vector_coords *v = &df->u.vector;
966 	int j = 0;
967 
968 	memset(&df->style, 0, sizeof(df->style));
969 	DFS_FACE_TYPE(df->style) = DefaultVectorButton;
970 	switch (i % 5)
971 	{
972 	case 0:
973 	case 3:
974 		v->num = 5;
975 		v->x = fxmalloc(sizeof(char) * v->num);
976 		v->y = fxmalloc(sizeof(char) * v->num);
977 		v->xoff = fxmalloc(sizeof(char) * v->num);
978 		v->yoff = fxmalloc(sizeof(char) * v->num);
979 		v->c = fxcalloc(v->num, sizeof(char));
980 		v->x[0] = 25;
981 		v->y[0] = 25;
982 		v->c[0] = 1;
983 		v->x[1] = 75;
984 		v->y[1] = 25;
985 		v->c[1] = 1;
986 		v->x[2] = 75;
987 		v->y[2] = 75;
988 		v->x[3] = 25;
989 		v->y[3] = 75;
990 		v->x[4] = 25;
991 		v->y[4] = 25;
992 		v->c[4] = 1;
993 		break;
994 	case 1:
995 		v->num = 5;
996 		v->x = fxmalloc(sizeof(char) * v->num);
997 		v->y = fxmalloc(sizeof(char) * v->num);
998 		v->xoff = fxmalloc(sizeof(char) * v->num);
999 		v->yoff = fxmalloc(sizeof(char) * v->num);
1000 		v->c = fxcalloc(v->num, sizeof(char));
1001 		v->x[0] = 39;
1002 		v->y[0] = 39;
1003 		v->c[0] = 1;
1004 		v->x[1] = 61;
1005 		v->y[1] = 39;
1006 		v->c[1] = 1;
1007 		v->x[2] = 61;
1008 		v->y[2] = 61;
1009 		v->x[3] = 39;
1010 		v->y[3] = 61;
1011 		v->x[4] = 39;
1012 		v->y[4] = 39;
1013 		v->c[4] = 1;
1014 		break;
1015 	case 2:
1016 		v->num = 5;
1017 		v->x = fxmalloc(sizeof(char) * v->num);
1018 		v->y = fxmalloc(sizeof(char) * v->num);
1019 		v->xoff = fxmalloc(sizeof(char) * v->num);
1020 		v->yoff = fxmalloc(sizeof(char) * v->num);
1021 		v->c = fxcalloc(v->num, sizeof(char));
1022 		v->x[0] = 49;
1023 		v->y[0] = 49;
1024 		v->c[0] = 1;
1025 		v->x[1] = 51;
1026 		v->y[1] = 49;
1027 		v->c[1] = 1;
1028 		v->x[2] = 51;
1029 		v->y[2] = 51;
1030 		v->x[3] = 49;
1031 		v->y[3] = 51;
1032 		v->x[4] = 49;
1033 		v->y[4] = 49;
1034 		v->c[4] = 1;
1035 		break;
1036 	case 4:
1037 		v->num = 5;
1038 		v->x = fxmalloc(sizeof(char) * v->num);
1039 		v->y = fxmalloc(sizeof(char) * v->num);
1040 		v->xoff = fxmalloc(sizeof(char) * v->num);
1041 		v->yoff = fxmalloc(sizeof(char) * v->num);
1042 		v->c = fxcalloc(v->num, sizeof(char));
1043 		v->x[0] = 36;
1044 		v->y[0] = 36;
1045 		v->c[0] = 1;
1046 		v->x[1] = 64;
1047 		v->y[1] = 36;
1048 		v->c[1] = 1;
1049 		v->x[2] = 64;
1050 		v->y[2] = 64;
1051 		v->x[3] = 36;
1052 		v->y[3] = 64;
1053 		v->x[4] = 36;
1054 		v->y[4] = 36;
1055 		v->c[4] = 1;
1056 		break;
1057 	}
1058 	/* set offsets to 0, for all buttons */
1059 	for(j = 0 ; j < v->num ; j++)
1060 	{
1061 	  v->xoff[j] = 0;
1062 	  v->yoff[j] = 0;
1063 	}
1064 
1065 	return;
1066 }
1067 
1068 /***********************************************************************
1069  *
1070  *  Procedure:
1071  *      CreateGCs - open fonts and create all the needed GC's.  I only
1072  *                  want to do this once, hence the first_time flag.
1073  *
1074  ***********************************************************************/
CreateGCs(void)1075 static void CreateGCs(void)
1076 {
1077 	XGCValues gcv;
1078 	unsigned long gcm;
1079 
1080 	/* create scratch GC's */
1081 	gcm = GCFunction|GCLineWidth;
1082 	gcv.function = GXcopy;
1083 	gcv.line_width = 0;
1084 
1085 	Scr.ScratchGC1 = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
1086 	Scr.ScratchGC2 = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
1087 	Scr.ScratchGC3 = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
1088 	Scr.ScratchGC4 = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
1089 	Scr.TitleGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
1090 	Scr.BordersGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
1091 	Scr.TransMaskGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
1092 	Scr.ScratchMonoPixmap = XCreatePixmap(dpy, Scr.Root, 1, 1, 1);
1093 	Scr.MonoGC = fvwmlib_XCreateGC(dpy, Scr.ScratchMonoPixmap, gcm, &gcv);
1094 	Scr.ScratchAlphaPixmap = XCreatePixmap(
1095 		dpy, Scr.Root, 1, 1, FRenderGetAlphaDepth());
1096 	Scr.AlphaGC = fvwmlib_XCreateGC(dpy, Scr.ScratchAlphaPixmap, gcm, &gcv);
1097 	return;
1098 }
1099 
1100 /***********************************************************************
1101  *
1102  *  Procedure:
1103  *      InitVariables - initialize fvwm variables
1104  *
1105  ************************************************************************/
InitVariables(void)1106 static void InitVariables(void)
1107 {
1108 	FvwmContext = XUniqueContext();
1109 	MenuContext = XUniqueContext();
1110 
1111 	/* initialize some lists */
1112 	Scr.AllBindings = NULL;
1113 	Scr.functions = NULL;
1114 	menus_init();
1115 	Scr.last_added_item.type = ADDED_NONE;
1116 	Scr.DefaultIcon = NULL;
1117 	Scr.DefaultColorset = -1;
1118 	Scr.StdGC = 0;
1119 	Scr.StdReliefGC = 0;
1120 	Scr.StdShadowGC = 0;
1121 	Scr.XorGC = 0;
1122 	/* zero all flags */
1123 	memset(&Scr.flags, 0, sizeof(Scr.flags));
1124 	/* create graphics contexts */
1125 	CreateGCs();
1126 
1127 	FW_W(&Scr.FvwmRoot) = Scr.Root;
1128 	Scr.FvwmRoot.next = 0;
1129 	init_stack_and_layers();
1130 	Scr.root_pushes = 0;
1131 	Scr.fvwm_pushes = 0;
1132 	Scr.pushed_window = &Scr.FvwmRoot;
1133 	Scr.FvwmRoot.number_cmap_windows = 0;
1134 	Scr.FvwmRoot.attr_backup.colormap = Pcmap;
1135 
1136 	Scr.BusyCursor = BUSY_NONE;
1137 	Scr.Hilite = NULL;
1138 	Scr.DefaultFont = NULL;
1139 
1140 	memset(&Scr.SizeWindow, 0, sizeof Scr.SizeWindow);
1141 	Scr.SizeWindow.m = NULL;
1142 	Scr.SizeWindow.cset = -1;
1143 
1144 	Scr.ScrollDelay = DEFAULT_SCROLL_DELAY;
1145 	Scr.OpaqueSize = DEFAULT_OPAQUE_MOVE_SIZE;
1146 	Scr.MoveThreshold = DEFAULT_MOVE_THRESHOLD;
1147 	/* ClickTime is set to the positive value upon entering the
1148 	 * event loop. */
1149 	Scr.ClickTime = -DEFAULT_CLICKTIME;
1150 	Scr.ColormapFocus = COLORMAP_FOLLOWS_MOUSE;
1151 
1152 	/* set major operating modes */
1153 	Scr.NumBoxes = 0;
1154 	Scr.cascade_x = 0;
1155 	Scr.cascade_y = 0;
1156 	/* the last Cascade placed window or NULL, we don't want NULL
1157 	 * initially */
1158 	Scr.cascade_window = &Scr.FvwmRoot;
1159 	Scr.buttons2grab = 0;
1160 	/* ewmh desktop */
1161 	InitFvwmDecor(&Scr.DefaultDecor);
1162 	Scr.DefaultDecor.tag = "Default";
1163 	/* Initialize RaiseHackNeeded by identifying X servers
1164 	   possibly running under NT. This is probably not an
1165 	   ideal solution, since eg NCD also produces X servers
1166 	   which do not run under NT.
1167 
1168 	   "Hummingbird Communications Ltd."
1169 	   is the ServerVendor string of the Exceed X server under NT,
1170 
1171 	   "Network Computing Devices Inc."
1172 	   is the ServerVendor string of the PCXware X server under Windows.
1173 
1174 	   "WRQ, Inc."
1175 	   is the ServerVendor string of the Reflection X server under Windows.
1176 	*/
1177 	Scr.bo.is_raise_hack_needed =
1178 		(strcmp (
1179 			ServerVendor (dpy),
1180 			"Hummingbird Communications Ltd.") == 0) ||
1181 		(strcmp (
1182 			ServerVendor (dpy),
1183 			"Network Computing Devices Inc.") == 0) ||
1184 		(strcmp (ServerVendor (dpy), "WRQ, Inc.") == 0);
1185 
1186 	Scr.bo.is_modality_evil = 0;
1187 	Scr.bo.do_disable_configure_notify = 0;
1188 	Scr.bo.do_install_root_cmap = 0;
1189 	Scr.bo.do_enable_flickering_qt_dialogs_workaround = 1;
1190 	Scr.bo.do_enable_qt_drag_n_drop_workaround = 0;
1191 	Scr.bo.do_enable_ewmh_iconic_state_workaround = 0;
1192 
1193 	Scr.gs.do_emulate_mwm = DEFAULT_EMULATE_MWM;
1194 	Scr.gs.do_emulate_win = DEFAULT_EMULATE_WIN;
1195 	Scr.gs.use_active_down_buttons = DEFAULT_USE_ACTIVE_DOWN_BUTTONS;
1196 	Scr.gs.use_inactive_buttons = DEFAULT_USE_INACTIVE_BUTTONS;
1197 	Scr.gs.use_inactive_down_buttons = DEFAULT_USE_INACTIVE_DOWN_BUTTONS;
1198 	/* Not the right place for this, should only be called once
1199 	 * somewhere .. */
1200 
1201 	Scr.flags.is_pointer_on_this_screen = !!FQueryPointer(
1202 		dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY, &JunkX,
1203 		&JunkY, &JunkMask);
1204 
1205 	/* make sure colorset 0 exists */
1206 	alloc_colorset(0);
1207 
1208 	return;
1209 }
1210 
usage(int is_verbose)1211 static void usage(int is_verbose)
1212 {
1213 	fprintf(stderr, "usage: %s", g_argv[0]);
1214 	fprintf(stderr,
1215 		   " [-d display]"
1216 		   " [-f cfgfile]"
1217 		   " [-c cmd]"
1218 		   " [-s [screen_num]]"
1219 		   " [-I vis-id | -C vis-class]"
1220 		   " [-l colors"
1221 		   " [-L|A|S|P] ...]"
1222 		   " [-r]"
1223 		   " [OTHER OPTIONS] ..."
1224 		   "\n");
1225 	if (!is_verbose)
1226 	{
1227 		fprintf(stderr,
1228 		           "Try '%s --help' for more information.\n",
1229 		           g_argv[0]);
1230 		return;
1231 	}
1232 	fprintf(stderr,
1233 		   " -A:           allocate palette\n"
1234 		   " -c cmd:       preprocess configuration file with <cmd>\n"
1235 		   " -C vis-class: use visual class <vis-class>\n"
1236 		   " -d display:   run fvwm on <display>\n"
1237 		   " -D:           enable debug oputput\n"
1238 		   " -f cfgfile:   read configuration from <cfgfile>\n"
1239 		   " -F file:      used internally for session management\n"
1240 		   " -h, -?:       print this help message\n"
1241 		   " -i client-id: used internally for session management\n"
1242 		   " -I vis-id:    use visual <vis-id>\n"
1243 		   " -l colors:    try to use no more than <colors> colors\n"
1244 		   " -L:           strict color limit\n"
1245 		   " -P:           visual palette\n"
1246 		   " -r:           replace running window manager\n"
1247 		   " -s [screen]:  manage a single screen\n"
1248 		   " -S:           static palette\n"
1249 		   " -V:           print version information\n"
1250 		);
1251 	fprintf(stderr, "Try 'man %s' for more information.\n",
1252 		   PACKAGE);
1253 
1254 		return;
1255 }
1256 
setVersionInfo(void)1257 static void setVersionInfo(void)
1258 {
1259 	char version_str[256];
1260 	char license_str[512];
1261 	char support_str[512] = "";
1262 	int support_len;
1263 
1264 	/* Set version information string */
1265 	sprintf(version_str, "fvwm3 %s (%s)", VERSION, VERSIONINFO);
1266 	Fvwm_VersionInfo = fxstrdup(version_str);
1267 
1268 	sprintf(license_str,
1269 		"fvwm3 comes with NO WARRANTY, to the extent permitted by law. "
1270 		"You may\nredistribute copies of fvwm under "
1271 		"the terms of the GNU General Public License.\n"
1272 		"For more information about these matters, see the file "
1273 		"named COPYING.");
1274 	Fvwm_LicenseInfo = fxstrdup(license_str);
1275 
1276 #ifdef HAVE_READLINE
1277 	strcat(support_str, " ReadLine,");
1278 #endif
1279 #ifdef XPM
1280 	strcat(support_str, " XPM,");
1281 #endif
1282 #if PngSupport
1283 	strcat(support_str, " PNG,");
1284 #endif
1285 #ifdef HAVE_RSVG
1286 	strcat(support_str, " SVG,");
1287 #endif
1288 	if (FHaveShapeExtension)
1289 		strcat(support_str, " Shape,");
1290 #ifdef HAVE_XSHM
1291 	strcat(support_str, " XShm,");
1292 #endif
1293 #ifdef SESSION
1294 	strcat(support_str, " SM,");
1295 #endif
1296 #ifdef HAVE_BIDI
1297 	strcat(support_str, " Bidi text,");
1298 #endif
1299 	strcat(support_str, " XRandR,");
1300 #ifdef HAVE_XRENDER
1301 	strcat(support_str, " XRender,");
1302 #endif
1303 #ifdef HAVE_XCURSOR
1304 	strcat(support_str, " XCursor,");
1305 #endif
1306 #ifdef HAVE_XFT
1307 	strcat(support_str, " XFT,");
1308 #endif
1309 #ifdef HAVE_NLS
1310 	strcat(support_str, " NLS,");
1311 #endif
1312 
1313 	support_len = strlen(support_str);
1314 	if (support_len > 0)
1315 	{
1316 		/* strip last comma */
1317 		support_str[support_len - 1] = '\0';
1318 		xasprintf(&Fvwm_SupportInfo, "with support for: %s", support_str);
1319 	}
1320 	else
1321 	{
1322 		Fvwm_SupportInfo = fxstrdup("with no optional feature support");
1323 	}
1324 
1325 	return;
1326 }
1327 
1328 /* Sets some initial style values & such */
SetRCDefaults(void)1329 static void SetRCDefaults(void)
1330 {
1331 #define RC_DEFAULTS_COMPLETE ((char *)-1)
1332 	int i;
1333 	/* set up default colors, fonts, etc */
1334 	const char *defaults[][3] = {
1335 		{ "XORValue 0", "", "" },
1336 		{ "ImagePath "FVWM_DATADIR"/default-config/images", "", "" },
1337 		{ "SetEnv FVWM_DATADIR "FVWM_DATADIR"", "", "" },
1338 		{ "SetEnv FVWM_IS_FVWM3 1", "", "" },
1339 		/* The below is historical -- before we had a default
1340 		 * configuration which defines these and more.
1341 		 */
1342 		{ "DefaultFont", "", "" },
1343 		{ "DefaultColors black grey", "", "" },
1344 		{ DEFAULT_MENU_STYLE, "", "" },
1345 		{ "TitleStyle Centered -- Raised", "", "" },
1346 		{ "Style * Color lightgrey/dimgrey", "", "" },
1347 		{ "Style * HilightFore black, HilightBack grey", "", "" },
1348 		{ "AddToFunc LoadDefaultConfig", "", "" },
1349 		{ "+ I Read "FVWM_DATADIR"/default-config/config", "", "" },
1350 		{ "+ I StartFunction", "", "" },
1351 		{
1352 			"AddToMenu MenuFvwmRoot \"",
1353 			_("Builtin Menu"),
1354 			"\" Title"
1355 		},
1356 		{ "+ \"&1. XTerm\" Exec xterm", "", "" },
1357 		{ "+ \"&2. FvwmConsole\" Module FvwmConsole", "", "" },
1358 		{
1359 			"+ \"&3. ",
1360 			_("Load Default Config"),
1361 			"\" LoadDefaultConfig"
1362 		},
1363 		{
1364 			"+ \"&4. ",
1365 			_("Restart"),
1366 			" Fvwm\" Restart"
1367 		},
1368 		{
1369 			"+ \"&5. ",
1370 			_("Quit"),
1371 			" Fvwm\" Quit"
1372 		},
1373 		{ "Mouse 1 R A Menu MenuFvwmRoot", "", "" },
1374 		/* default menu navigation */
1375 		{ "Key Escape M A MenuClose", "", "" },
1376 		{ "Key Return M A MenuSelectItem", "", "" },
1377 		{ "Key Left M A MenuCursorLeft", "", "" },
1378 		{ "Key Right M A MenuCursorRight", "", "" },
1379 		{ "Key Up M A MenuMoveCursor -1", "", "" },
1380 		{ "Key Down M A MenuMoveCursor 1", "", "" },
1381 		{ "Mouse 1 MI A MenuSelectItem", "", "" },
1382 		/* don't add anything below */
1383 		{ RC_DEFAULTS_COMPLETE, "", "" },
1384 		{ "Read "FVWM_DATADIR"/ConfigFvwmDefaults", "", "" },
1385 		{ NULL, NULL, NULL }
1386 	};
1387 
1388 	for (i = 0; defaults[i][0] != NULL; i++)
1389 	{
1390 		const exec_context_t *exc;
1391 		exec_context_changes_t ecc;
1392 		char *cmd;
1393 
1394 		if (defaults[i][0] == RC_DEFAULTS_COMPLETE)
1395 		{
1396 			menu_bindings_startup_complete();
1397 			continue;
1398 		}
1399 		ecc.type = Restarting ? EXCT_RESTART : EXCT_INIT;
1400 		ecc.w.wcontext = C_ROOT;
1401 		exc = exc_create_context(&ecc, ECC_TYPE | ECC_WCONTEXT);
1402 		xasprintf(&cmd, "%s%s%s", defaults[i][0], defaults[i][1],
1403 			defaults[i][2]);
1404 		execute_function(NULL, exc, cmd, 0);
1405 		free(cmd);
1406 		exc_destroy_context(exc);
1407 	}
1408 #undef RC_DEFAULTS_COMPLETE
1409 
1410 	return;
1411 }
1412 
CatchRedirectError(Display * dpy,XErrorEvent * event)1413 static int CatchRedirectError(Display *dpy, XErrorEvent *event)
1414 {
1415 	fvwm_debug(__func__, "another WM is running");
1416 	exit(1);
1417 
1418 	/* to make insure happy */
1419 	return 0;
1420 }
1421 
1422 /* CatchFatal - Shuts down if the server connection is lost */
CatchFatal(Display * dpy)1423 static int CatchFatal(Display *dpy)
1424 {
1425 	/* No action is taken because usually this action is caused by someone
1426 	   using "xlogout" to be able to switch between multiple window managers
1427 	*/
1428 	module_kill_all();
1429 	exit(1);
1430 
1431 	/* to make insure happy */
1432 	return 0;
1433 }
1434 
1435 /* FvwmErrorHandler - displays info on internal errors */
FvwmErrorHandler(Display * dpy,XErrorEvent * event)1436 static int FvwmErrorHandler(Display *dpy, XErrorEvent *event)
1437 {
1438 	char errtext[512];
1439 
1440 	if (event->error_code == BadWindow)
1441 	{
1442 		bad_window = event->resourceid;
1443 		return 0;
1444 	}
1445 	/* some errors are acceptable, mostly they're caused by
1446 	 * trying to update a lost  window or free'ing another modules colors */
1447 	if (event->error_code == BadWindow ||
1448 	   event->request_code == X_GetGeometry ||
1449 	   event->error_code == BadDrawable ||
1450 	   event->request_code == X_ConfigureWindow ||
1451 	   event->request_code == X_SetInputFocus||
1452 	   event->request_code == X_GrabButton ||
1453 	   event->request_code == X_ChangeWindowAttributes ||
1454 	   event->request_code == X_InstallColormap ||
1455 	   event->request_code == X_FreePixmap ||
1456 	   event->request_code == X_FreeColors)
1457 	{
1458 		return 0;
1459 	}
1460 
1461 	XGetErrorText(dpy, event->error_code, errtext, 512);
1462 
1463 	fvwm_debug(__func__, "*** internal error ***");
1464 	fvwm_debug(__func__, "Request %d, Error %d, Text %s, "
1465 		   "EventType: %d",
1466 		   event->request_code,
1467 		   event->error_code,
1468 		   errtext,
1469 		   last_event_type);
1470 
1471 	return 0;
1472 }
1473 
1474 /* ---------------------------- interface functions ------------------------ */
1475 
1476 /* Does initial window captures and runs init/restart function */
StartupStuff(void)1477 void StartupStuff(void)
1478 {
1479 #define start_func_name "StartFunction"
1480 	const char *init_func_name;
1481 	const exec_context_t *exc;
1482 	exec_context_changes_t ecc;
1483 
1484 	ecc.type = Restarting ? EXCT_RESTART : EXCT_INIT;
1485 	ecc.w.wcontext = C_ROOT;
1486 	exc = exc_create_context(&ecc, ECC_TYPE | ECC_WCONTEXT);
1487 	CaptureAllWindows(exc, False);
1488 	/* Turn off the SM stuff after the initial capture so that new windows
1489 	 * will not be matched by accident. */
1490 	if (Restarting)
1491 	{
1492 		DisableRestoringState();
1493 	}
1494 	/* Have to do this here too because preprocessor modules have not run
1495 	 * to the end when HandleEvents is entered from the main loop. */
1496 	struct monitor *m;
1497 	TAILQ_FOREACH(m, &monitor_q, entry)
1498 		checkPanFrames(m);
1499 
1500 	fFvwmInStartup = False;
1501 
1502 	/* Make sure the geometry window uses the current font */
1503 	resize_geometry_window();
1504 
1505 	/* Make sure we have the correct click time now. */
1506 	if (Scr.ClickTime < 0)
1507 	{
1508 		Scr.ClickTime = -Scr.ClickTime;
1509 	}
1510 
1511 # if 0
1512 	/* It is safe to ungrab here: if not, and one of the init functions
1513 	 * does not finish, we've got a complete freeze! */
1514 	/* DV (15-Jul-2004): No, it is not safe to ungrab.  If another
1515 	 * application grabs the pointer before execute_function gets it, the
1516 	 * start functions are not executed.  And the pointer is grabbed
1517 	 * during function execution anyway, so releasing it here buys us
1518 	 * nothing. */
1519 	UngrabEm(GRAB_STARTUP);
1520 	XUngrabPointer(dpy, CurrentTime);
1521 #endif
1522 
1523 	/* migo (04-Sep-1999): execute StartFunction */
1524 	if (functions_is_complex_function(start_func_name))
1525 	{
1526 		char *action = "Function " start_func_name;
1527 
1528 		execute_function(NULL, exc, action, 0);
1529 	}
1530 
1531 	/* migo (03-Jul-1999): execute [Session]{Init|Restart}Function */
1532 	init_func_name = get_init_function_name(Restarting == True);
1533 	if (functions_is_complex_function(init_func_name))
1534 	{
1535 		char *action;
1536 
1537 		xasprintf(&action, "Function %s", init_func_name);
1538 		execute_function(NULL, exc, action, 0);
1539 		free(action);
1540 	}
1541 	/* see comment above */
1542 	UngrabEm(GRAB_STARTUP);
1543 	XUngrabPointer(dpy, CurrentTime);
1544 
1545 	/* This should be done after the initialization is finished, since
1546 	 * it directly changes the global state. */
1547 	LoadGlobalState(state_filename);
1548 
1549 	/*
1550 	** migo (20-Jun-1999): Remove state file after usage.
1551 	** migo (09-Jul-1999): but only on restart, otherwise it can be reused.
1552 	*/
1553 	if (Restarting)
1554 	{
1555 		unlink(state_filename);
1556 	}
1557 	exc_destroy_context(exc);
1558 
1559 	/* TA:  20091212:  If we get here, we're done restarting, so reset the
1560 	 * flag back to False!
1561 	 */
1562 	Restarting = False;
1563 
1564 	return;
1565 }
1566 
1567 /***********************************************************************
1568  *
1569  *  LoadDefaultButton -- loads default button # into button structure
1570  *              assumes associated button memory is already free
1571  *
1572  ************************************************************************/
LoadDefaultButton(DecorFace * df,int i)1573 void LoadDefaultButton(DecorFace *df, int i)
1574 {
1575 	if (i & 1)
1576 	{
1577 		LoadDefaultRightButton(df, i / 2);
1578 	}
1579 	else
1580 	{
1581 		LoadDefaultLeftButton(df, i / 2);
1582 	}
1583 
1584 	return;
1585 }
1586 
1587 /***********************************************************************
1588  *
1589  *  ResetOrDestroyAllButtons -- resets all buttons to defaults
1590  *                              destroys existing buttons
1591  *
1592  ************************************************************************/
DestroyAllButtons(FvwmDecor * decor)1593 void DestroyAllButtons(FvwmDecor *decor)
1594 {
1595 	TitleButton *tbp;
1596 	DecorFace *face;
1597 	int i;
1598 	int j;
1599 
1600 	for (tbp = decor->buttons, i = 0; i < NUMBER_OF_TITLE_BUTTONS;
1601 	     i++, tbp++)
1602 	{
1603 		for (j = 0, face = TB_STATE(*tbp); j < BS_MaxButtonState;
1604 		     j++, face++)
1605 		{
1606 			FreeDecorFace(dpy, face);
1607 		}
1608 	}
1609 
1610 	return;
1611 }
1612 
ResetAllButtons(FvwmDecor * decor)1613 void ResetAllButtons(FvwmDecor *decor)
1614 {
1615 	TitleButton *tbp;
1616 	DecorFace *face;
1617 	int i;
1618 	int j;
1619 
1620 	DestroyAllButtons(decor);
1621 	for (tbp = decor->buttons, i = 0; i < NUMBER_OF_TITLE_BUTTONS;
1622 	     i++, tbp++)
1623 	{
1624 		memset(&TB_FLAGS(*tbp), 0, sizeof(TB_FLAGS(*tbp)));
1625 		TB_JUSTIFICATION(*tbp) = JUST_CENTER;
1626 		for (face = TB_STATE(*tbp), j = 0; j < BS_MaxButtonState;
1627 		     j++, face++)
1628 		{
1629 			LoadDefaultButton(face, i);
1630 		}
1631 	}
1632 
1633 	/* standard MWM decoration hint assignments (veliaa@rpi.edu)
1634 	   [Menu]  - Title Bar - [Minimize] [Maximize] */
1635 	TB_MWM_DECOR_FLAGS(decor->buttons[0]) |= MWM_DECOR_MENU;
1636 	TB_MWM_DECOR_FLAGS(decor->buttons[1]) |= MWM_DECOR_MAXIMIZE;
1637 	TB_MWM_DECOR_FLAGS(decor->buttons[3]) |= MWM_DECOR_MINIMIZE;
1638 
1639 	return;
1640 }
1641 
SetMWM_INFO(Window window)1642 void SetMWM_INFO(Window window)
1643 {
1644 	struct mwminfo
1645 	{
1646 		long props[2];
1647 		/* prop[0]: flags */
1648 		/* prop[1]: win */
1649 	}  motif_wm_info;
1650 	static char set_yorn='n';
1651 
1652 	if (set_yorn=='y')
1653 	{
1654 		return;
1655 	}
1656 
1657 	if (Scr.bo.is_modality_evil)
1658 	{
1659 		/* Set Motif WM_INFO atom to make motif relinquish
1660 		 * broken handling of modal dialogs */
1661 		motif_wm_info.props[0] = 2;
1662 		motif_wm_info.props[1] = window;
1663 		XChangeProperty(
1664 			dpy,Scr.Root, _XA_MOTIF_WM, _XA_MOTIF_WM,32,
1665 			PropModeReplace, (unsigned char *)&motif_wm_info, 2);
1666 		set_yorn='y';
1667 	}
1668 
1669 	return;
1670 }
1671 
1672 /*
1673  * set_init_function_name - sets one of the init, restart or exit function names
1674  * get_init_function_name - gets one of the init, restart or exit function names
1675  *
1676  * First parameter defines a function type: 0 - init, 1 - restart, 2 - exit.
1677  */
set_init_function_name(int n,const char * name)1678 void set_init_function_name(int n, const char *name)
1679 {
1680 	init_function_names[n >= 0 && n < 3? n: 3] = name;
1681 
1682 	return;
1683 }
1684 
get_init_function_name(int n)1685 const char *get_init_function_name(int n)
1686 {
1687 	return init_function_names[n >= 0 && n < 3? n: 3];
1688 }
1689 
1690 #ifndef _PATH_DEVNULL
1691 #	define _PATH_DEVNULL "/dev/null"
1692 #endif
reopen_fd(int fd,char * mode,FILE * of)1693 static void reopen_fd(int fd, char* mode, FILE *of)
1694 {
1695 	struct stat sbuf;
1696 	FILE *f;
1697 	int rc;
1698 
1699 	errno = 0;
1700 	rc = fstat(fd, &sbuf);
1701 	if (rc == 0)
1702 	{
1703 		return;
1704 	}
1705 	else if (errno != EBADF)
1706 	{
1707 		exit(77);
1708 	}
1709 	f = freopen(_PATH_DEVNULL, mode, of);
1710 	if (f == 0 || fileno(f) != fd)
1711 	{
1712 		exit(88);
1713 	}
1714 
1715 	return;
1716 }
1717 
1718 /***********************************************************************
1719  *
1720  *  Procedure:
1721  *      main - start of fvwm
1722  *
1723  ***********************************************************************/
1724 
main(int argc,char ** argv)1725 int main(int argc, char **argv)
1726 {
1727 	unsigned long valuemask;
1728 	XSetWindowAttributes attributes;
1729 	int i;
1730 	char *display_string;
1731 	Bool do_force_single_screen = False;
1732 	int single_screen_num = -1;
1733 	Bool replace_wm = False;
1734 	int visualClass = -1;
1735 	int visualId = -1;
1736 	PictureColorLimitOption colorLimitop = {-1, -1, -1, -1, -1};
1737 	const exec_context_t *exc;
1738 	exec_context_changes_t ecc;
1739 	struct monitor	*m = NULL;
1740 
1741 	fvwmlib_init_max_fd();
1742 	/* Tell the FEvent module an event type that is not used by fvwm. */
1743 	fev_init_invalid_event_type(KeymapNotify);
1744 
1745 	/* close open fds */
1746 	for (i = 3; i < fvwmlib_max_fd; i++)
1747 	{
1748 		close(i);
1749 	}
1750 	/* reopen stdin, stdout and stderr if necessary */
1751 	reopen_fd(0, "rb", stdin);
1752 	reopen_fd(1, "wb", stdout);
1753 	reopen_fd(2, "wb", stderr);
1754 
1755 	memset(&Scr, 0, sizeof(Scr));
1756 	/* for use on restart */
1757 	g_argv = fxmalloc((argc + 4) * sizeof(char *));
1758 	g_argc = argc;
1759 	for (i = 0; i < argc; i++)
1760 	{
1761 		g_argv[i] = argv[i];
1762 	}
1763 	g_argv[g_argc] = NULL;
1764 
1765 	FlocaleInit(LC_CTYPE, "", "", "fvwm");
1766 	FGettextInit("fvwm", LOCALEDIR, "fvwm");
1767 
1768 	setVersionInfo();
1769 	/* Put the default module directory into the environment so it can be
1770 	 * used later by the config file, etc.  */
1771 	flib_putenv("FVWM_MODULEDIR", "FVWM_MODULEDIR=" FVWM_MODULEDIR);
1772 
1773 	/* Figure out user's home directory */
1774 	home_dir = find_home_dir();
1775 	if (home_dir == NULL)
1776 	{
1777 		home_dir = "/"; /* give up and use root dir */
1778 	}
1779 
1780 	/* Figure out where to read and write user's data files. */
1781 	fvwm_userdir = getenv("FVWM_USERDIR");
1782 	if (fvwm_userdir == NULL)
1783 	{
1784 		char *s;
1785 
1786 		xasprintf(&fvwm_userdir, "%s/.fvwm", home_dir);
1787 		/* Put the user directory into the environment so it can be used
1788 		 * later everywhere. */
1789 		xasprintf(&s, "FVWM_USERDIR=%s", fvwm_userdir);
1790 		flib_putenv("FVWM_USERDIR", s);
1791 		free(s);
1792 	}
1793 
1794 	/* Create FVWM_USERDIR directory if needed */
1795 	if (access(fvwm_userdir, F_OK) != 0)
1796 	{
1797 		mkdir(fvwm_userdir, 0777);
1798 	}
1799 	if (access(fvwm_userdir, W_OK) != 0)
1800 	{
1801 		fvwm_debug(__func__, "No write permissions in `%s/'.\n",
1802 			   fvwm_userdir);
1803 	}
1804 
1805 	for (i = 1; i < argc; i++)
1806 	{
1807 		if (strcmp(argv[i], "-debug_stack_ring") == 0 ||
1808 		    strcmp(argv[i], "--debug-stack-ring") == 0)
1809 		{
1810 			debugging_stack_ring = True;
1811 		}
1812 		else if (strcmp(argv[i], "-D") == 0 ||
1813 			 strcmp(argv[i], "-debug") == 0 ||
1814 			 strcmp(argv[i], "--debug") == 0)
1815 		{
1816 			debugging = True;
1817 		}
1818 		else if (strcmp(argv[i], "-i") == 0 ||
1819 			 strcmp(argv[i], "-clientid") == 0 ||
1820 			 strcmp(argv[i], "--clientid") == 0 ||
1821 			 strcmp(argv[i], "-clientId") == 0 ||
1822 			 strcmp(argv[i], "--clientId") == 0)
1823 		{
1824 			if (++i >= argc)
1825 			{
1826 				usage(0);
1827 				exit(1);
1828 			}
1829 			SetClientID(argv[i]);
1830 		}
1831 		else if (strcmp(argv[i], "-F") == 0 ||
1832 			 strcmp(argv[i], "-restore") == 0 ||
1833 			 strcmp(argv[i], "--restore") == 0)
1834 		{
1835 			if (++i >= argc)
1836 			{
1837 				usage(0);
1838 				exit(1);
1839 			}
1840 			state_filename = argv[i];
1841 		}
1842 		else if (strcmp(argv[i], "-s") == 0 ||
1843 			 strcmp(argv[i], "-single-screen") == 0 ||
1844 			 strcmp(argv[i], "--single-screen") == 0)
1845 		{
1846 			do_force_single_screen = True;
1847 			if (i+1 < argc && argv[i+1][0] != '-')
1848 			{
1849 				i++;
1850 				if (sscanf(argv[i], "%d", &single_screen_num) ==
1851 				    0)
1852 				{
1853 					usage(0);
1854 					exit(1);
1855 				}
1856 			}
1857 		}
1858 		else if (strcmp(argv[i], "-d") == 0 ||
1859 			 strcmp(argv[i], "-display") == 0 ||
1860 			 strcmp(argv[i], "--display") == 0)
1861 		{
1862 			if (++i >= argc)
1863 			{
1864 				usage(0);
1865 				exit(1);
1866 			}
1867 			display_name = argv[i];
1868 		}
1869 		else if (strcmp(argv[i], "-f") == 0)
1870 		{
1871 			if (++i >= argc)
1872 			{
1873 				usage(0);
1874 				exit(1);
1875 			}
1876 			if (num_config_commands < MAX_CFG_CMDS)
1877 			{
1878 				config_commands[num_config_commands] =
1879 					(char *)malloc(6+strlen(argv[i]));
1880 				strcpy(config_commands[num_config_commands],
1881 				       "Read ");
1882 				strcat(config_commands[num_config_commands],
1883 				       argv[i]);
1884 
1885 				/* Check to see if the file requested exists.
1886 				 * If it doesn't, use the default config
1887 				 * instead.
1888 				 */
1889 
1890 				if (access(argv[i], F_OK) != 0) {
1891 					free(config_commands[num_config_commands]);
1892 					config_commands[num_config_commands] =
1893 						fxstrdup("Read " FVWM_DATADIR
1894 							"/default-config/config");
1895 				}
1896 				num_config_commands++;
1897 			}
1898 			else
1899 			{
1900 				fvwm_debug(__func__,
1901 					   "only %d -f and -cmd parms allowed!",
1902 					   MAX_CFG_CMDS);
1903 			}
1904 		}
1905 		else if (strcmp(argv[i], "-c") == 0 ||
1906 			 strcmp(argv[i], "-cmd") == 0 ||
1907 			 strcmp(argv[i], "--cmd") == 0)
1908 		{
1909 			if (++i >= argc)
1910 			{
1911 				usage(0);
1912 				exit(1);
1913 			}
1914 			if (num_config_commands < MAX_CFG_CMDS)
1915 			{
1916 				config_commands[num_config_commands] =
1917 					fxstrdup(argv[i]);
1918 				num_config_commands++;
1919 			}
1920 			else
1921 			{
1922 				fvwm_debug(__func__,
1923 					   "only %d -f and -cmd parms allowed!",
1924 					   MAX_CFG_CMDS);
1925 			}
1926 		}
1927 		else if (strcmp(argv[i], "-h") == 0 ||
1928 			 strcmp(argv[i], "-?") == 0 ||
1929 			 strcmp(argv[i], "--help") == 0)
1930 		{
1931 			usage(1);
1932 			exit(0);
1933 		}
1934 		else if (strcmp(argv[i], "-blackout") == 0)
1935 		{
1936 			/* obsolete option */
1937 			fvwm_debug(__func__,
1938 				   "The -blackout option is obsolete, it will be "
1939 				   "removed in 3.0.");
1940 		}
1941 		else if (strcmp(argv[i], "-r") == 0 ||
1942 			 strcmp(argv[i], "-replace") == 0 ||
1943 			 strcmp(argv[i], "--replace") == 0)
1944 		{
1945 			replace_wm = True;
1946 		}
1947 		/* check for visualId before visual to remove ambiguity */
1948 		else if (strcmp(argv[i], "-I") == 0 ||
1949 			 strcmp(argv[i], "-visualid") == 0 ||
1950 			 strcmp(argv[i], "--visualid") == 0 ||
1951 			 strcmp(argv[i], "-visualId") == 0 ||
1952 			 strcmp(argv[i], "--visualId") == 0)
1953 		{
1954 			visualClass = -1;
1955 			if (++i >= argc)
1956 			{
1957 				usage(0);
1958 				exit(1);
1959 			}
1960 			if (sscanf(argv[i], "0x%x", &visualId) == 0)
1961 			{
1962 				if (sscanf(argv[i], "%d", &visualId) == 0)
1963 				{
1964 					usage(0);
1965 					exit(1);
1966 				}
1967 			}
1968 		}
1969 		else if (strcmp(argv[i], "-C") == 0 ||
1970 			 strcmp(argv[i], "-visual") == 0 ||
1971 			 strcmp(argv[i], "--visual") == 0)
1972 		{
1973 			visualId = None;
1974 			if (++i >= argc)
1975 			{
1976 				usage(0);
1977 				exit(1);
1978 			}
1979 			if (strncasecmp(argv[i], "staticg", 7) == 0)
1980 			{
1981 				visualClass = StaticGray;
1982 			}
1983 			else if (strncasecmp(argv[i], "g", 1) == 0)
1984 			{
1985 				visualClass = GrayScale;
1986 			}
1987 			else if (strncasecmp(argv[i], "staticc", 7) == 0)
1988 			{
1989 				visualClass = StaticColor;
1990 			}
1991 			else if (strncasecmp(argv[i], "p", 1) == 0)
1992 			{
1993 				visualClass = PseudoColor;
1994 			}
1995 			else if (strncasecmp(argv[i], "t", 1) == 0)
1996 			{
1997 				visualClass = TrueColor;
1998 			}
1999 			else if (strncasecmp(argv[i], "d", 1) == 0)
2000 			{
2001 				visualClass = DirectColor;
2002 			}
2003 			else
2004 			{
2005 				usage(0);
2006 				exit(1);
2007 			}
2008 		}
2009 		else if (strcmp(argv[i], "-l") == 0 ||
2010 			 strcmp(argv[i], "-color-limit") == 0 ||
2011 			 strcmp(argv[i], "--color-limit") == 0)
2012 		{
2013 			if (++i >= argc)
2014 			{
2015 				usage(0);
2016 				exit(1);
2017 			}
2018 			colorLimitop.color_limit = atoi(argv[i]);
2019 		}
2020 		else if (strcmp(argv[i], "-L") == 0 ||
2021 			 strcmp(argv[i], "-strict-color-limit") == 0 ||
2022 			 strcmp(argv[i], "--strict-color-limit") == 0)
2023 		{
2024 			colorLimitop.strict = True;
2025 		}
2026 		else if (strcmp(argv[i], "-A") == 0 ||
2027 			 strcmp(argv[i], "-allocate-palette") == 0 ||
2028 			 strcmp(argv[i], "--allocate-palette") == 0)
2029 		{
2030 			colorLimitop.allocate = True;
2031 		}
2032 		else if (strcmp(argv[i], "-S") == 0 ||
2033 			 strcmp(argv[i], "-static-palette") == 0 ||
2034 			 strcmp(argv[i], "--static-palette") == 0)
2035 		{
2036 			colorLimitop.not_dynamic = True;
2037 		}
2038 		else if (strcmp(argv[i], "-P") == 0 ||
2039 			 strcmp(argv[i], "-visual-palette") == 0 ||
2040 			 strcmp(argv[i], "--visual-palette") == 0)
2041 		{
2042 			colorLimitop.use_named_table = True;
2043 		}
2044 		else if (strcmp(argv[i], "-V") == 0 ||
2045 			 strcmp(argv[i], "-version") == 0 ||
2046 			 strcmp(argv[i], "--version") == 0)
2047 		{
2048 			printf("%s\n%s\n\n%s\n", Fvwm_VersionInfo,
2049 			       Fvwm_SupportInfo, Fvwm_LicenseInfo);
2050 			free(Fvwm_SupportInfo);
2051 			exit(0);
2052 		}
2053 		else if (strcmp(argv[i], "-v") == 0)
2054 		{
2055 			log_set_level(1);
2056 			log_open(fvwm_userdir);
2057 		}
2058 		else
2059 		{
2060 			usage(0);
2061 			fprintf(stderr, "invalid option -- %s\n",
2062 				   argv[i]);
2063 			exit(1);
2064 		}
2065 	}
2066 
2067 	InstallSignals();
2068 
2069 	if (single_screen_num >= 0)
2070 	{
2071 		char *dn = NULL;
2072 
2073 		if (display_name)
2074 		{
2075 			dn = display_name;
2076 		}
2077 		if (!dn)
2078 		{
2079 			dn = getenv("DISPLAY");
2080 		}
2081 		if (!dn)
2082 		{
2083 			/* should never happen ? */
2084 			if (!(dpy = XOpenDisplay(dn)))
2085 			{
2086 				fvwm_debug(__func__, "can't open display %s"
2087 					   "to get the default display",
2088 					   XDisplayName(dn));
2089 			}
2090 			else
2091 			{
2092 				dn = XDisplayString(dpy);
2093 			}
2094 		}
2095 		if (dn == NULL)
2096 		{
2097 			fvwm_debug(__func__,
2098 				   "couldn't find default display (%s)",
2099 				   XDisplayName(dn));
2100 		}
2101 		else
2102 		{
2103 			char *new_dn;
2104 
2105 			new_dn = get_display_name(dn, single_screen_num);
2106 			if (dpy && strcmp(new_dn, dn) == 0)
2107 			{
2108 				/* allready opened */
2109 				Pdpy = dpy;
2110 			}
2111 			else if (dpy)
2112 			{
2113 				XCloseDisplay(dpy);
2114 				dpy = NULL;
2115 			}
2116 			if (!dpy && !(Pdpy = dpy = XOpenDisplay(new_dn)))
2117 			{
2118 				fvwm_debug(__func__,
2119 					   "can't open display %s, single screen "
2120 					   "number %d maybe not correct",
2121 					   new_dn, single_screen_num);
2122 			}
2123 			Scr.screen = single_screen_num;
2124 			Scr.NumberOfScreens = ScreenCount(dpy);
2125 			free(new_dn);
2126 		}
2127 	}
2128 
2129 	if (!dpy)
2130 	{
2131 		if(!(Pdpy = dpy = XOpenDisplay(display_name)))
2132 		{
2133 			fvwm_debug(__func__, "can't open display %s",
2134 				   XDisplayName(display_name));
2135 			exit (1);
2136 		}
2137 		Scr.screen= DefaultScreen(dpy);
2138 		Scr.NumberOfScreens = ScreenCount(dpy);
2139 	}
2140 
2141 	atexit(catch_exit);
2142 	master_pid = getpid();
2143 
2144 	if (!do_force_single_screen)
2145 	{
2146 		int myscreen = 0;
2147 		char *new_dn;
2148 		char *dn;
2149 
2150 		dn = XDisplayString(dpy);
2151 		for (i=0;i<Scr.NumberOfScreens;i++)
2152 		{
2153 			if (i != Scr.screen && fork() == 0)
2154 			{
2155 				myscreen = i;
2156 				new_dn = get_display_name(dn, myscreen);
2157 				Pdpy = dpy = XOpenDisplay(new_dn);
2158 				Scr.screen = myscreen;
2159 				Scr.NumberOfScreens = ScreenCount(dpy);
2160 				free(new_dn);
2161 
2162 				break;
2163 			}
2164 		}
2165 
2166 		g_argv[argc++] = "-s";
2167 		g_argv[argc] = NULL;
2168 	}
2169 
2170 	monitor_mode = MONITOR_TRACKING_G;
2171 	FScreenInit(dpy);
2172 	FScreenSelect(dpy);
2173 	x_fd = XConnectionNumber(dpy);
2174 
2175 #ifdef HAVE_X11_FD
2176 	if (fcntl(x_fd, F_SETFD, 1) == -1)
2177 	{
2178 		fvwm_debug(__func__, "close-on-exec failed");
2179 		exit (1);
2180 	}
2181 #endif
2182 
2183 	/* Add a DISPLAY entry to the environment, incase we were started
2184 	 * with fvwm -display term:0.0 */
2185 	xasprintf(&display_string, "DISPLAY=%s",XDisplayString(dpy));
2186 	flib_putenv("DISPLAY", display_string);
2187 	/* Add a HOSTDISPLAY environment variable, which is the same as
2188 	 * DISPLAY, unless display = :0.0 or unix:0.0, in which case the full
2189 	 * host name will be used for ease in networking.
2190 	 */
2191 	if (strncmp(display_string, "DISPLAY=:",9)==0)
2192 	{
2193 		char client[MAXHOSTNAME], *rdisplay_string;
2194 		gethostname(client,MAXHOSTNAME);
2195 		xasprintf(&rdisplay_string, "HOSTDISPLAY=%s:%s", client,
2196 			&display_string[9]);
2197 		flib_putenv("HOSTDISPLAY", rdisplay_string);
2198 		free(rdisplay_string);
2199 	}
2200 	else if (strncmp(display_string, "DISPLAY=unix:",13)==0)
2201 	{
2202 		char client[MAXHOSTNAME], *rdisplay_string;
2203 		gethostname(client,MAXHOSTNAME);
2204 		xasprintf(&rdisplay_string, "HOSTDISPLAY=%s:%s", client,
2205 			&display_string[13]);
2206 		flib_putenv("HOSTDISPLAY", rdisplay_string);
2207 		free(rdisplay_string);
2208 	}
2209 	else
2210 	{
2211 		char *rdisplay_string;
2212 		xasprintf(&rdisplay_string, "HOSTDISPLAY=%s",
2213 		    XDisplayString(dpy));
2214 		flib_putenv("HOSTDISPLAY", rdisplay_string);
2215 		free(rdisplay_string);
2216 	}
2217 	free(display_string);
2218 
2219 	Scr.Root = RootWindow(dpy, Scr.screen);
2220 	if (Scr.Root == None)
2221 	{
2222 		fvwm_debug(__func__, "Screen %d is not a valid screen",
2223 			   (int)Scr.screen);
2224 		exit(1);
2225 	}
2226 
2227 	{
2228 		XVisualInfo template, *vinfo = NULL;
2229 		int total, i;
2230 
2231 		Pdepth = 0;
2232 		Pdefault = False;
2233 		total = 0;
2234 		template.screen = Scr.screen;
2235 		if (visualClass != -1)
2236 		{
2237 			template.class = visualClass;
2238 			vinfo = XGetVisualInfo(dpy,
2239 					VisualScreenMask|VisualClassMask,
2240 					&template, &total);
2241 			if (!total)
2242 			{
2243 				fvwm_debug(__func__,
2244 					   "Cannot find visual class %d",
2245 					   visualClass);
2246 			}
2247 		}
2248 		else if (visualId != -1)
2249 		{
2250 			template.visualid = visualId;
2251 			vinfo = XGetVisualInfo(dpy,
2252 					VisualScreenMask|VisualIDMask,
2253 					&template, &total);
2254 			if (!total)
2255 			{
2256 				fvwm_debug(__func__,
2257 					   "VisualId 0x%x is not valid ",
2258 					   visualId);
2259 			}
2260 		}
2261 
2262 		/* visualID's are unique so there will only be one.
2263 		   Select the visualClass with the biggest depth */
2264 		for (i = 0; i < total; i++)
2265 		{
2266 			if (vinfo[i].depth > Pdepth)
2267 			{
2268 				Pvisual = vinfo[i].visual;
2269 				Pdepth = vinfo[i].depth;
2270 			}
2271 		}
2272 		if (vinfo)
2273 		{
2274 			XFree(vinfo);
2275 		}
2276 
2277 		/* Detection of a card with 2 hardware colormaps (8+24) which
2278 		 * use depth 8 for the default. We can use our own depth 24
2279 		 * cmap without affecting other applications. */
2280 		if (Pdepth == 0 && DefaultDepth(dpy, Scr.screen) <= 8)
2281 		{
2282 			template.class = TrueColor;
2283 			vinfo = XGetVisualInfo(
2284 				dpy, VisualScreenMask|VisualClassMask,
2285 				&template, &total);
2286 
2287 			for(i = 0; i<total; i++)
2288 			{
2289 				if (Pdepth < vinfo[i].depth &&
2290 				    vinfo[i].depth > 8)
2291 				{
2292 					Pvisual = vinfo[i].visual;
2293 					Pdepth = vinfo[i].depth;
2294 				}
2295 			}
2296 			if (vinfo)
2297 			{
2298 				XFree(vinfo);
2299 			}
2300 		}
2301 
2302 		/* have to have a colormap for non-default visual windows */
2303 		if (Pdepth > 0)
2304 		{
2305 			if (Pvisual->class == DirectColor)
2306 			{
2307 				Pcmap = XCreateColormap(
2308 					dpy, Scr.Root, Pvisual, AllocAll);
2309 			}
2310 			else
2311 			{
2312 				Pcmap = XCreateColormap(
2313 					dpy, Scr.Root, Pvisual, AllocNone);
2314 			}
2315 		}
2316 		/* use default visuals if none found so far */
2317 		else
2318 		{
2319 			Pvisual = DefaultVisual(dpy, Scr.screen);
2320 			Pdepth = DefaultDepth(dpy, Scr.screen);
2321 			Pcmap = DefaultColormap(dpy, Scr.screen);
2322 			Pdefault = True;
2323 		}
2324 	}
2325 
2326 	PictureSetupWhiteAndBlack();
2327 
2328 	/* make a copy of the visual stuff so that XorPixmap can swap with root
2329 	 */
2330 	PictureSaveFvwmVisual();
2331 
2332 	Scr.ColorLimit = 0;
2333 	PUseDynamicColors = 0;
2334 	Scr.ColorLimit = PictureInitColors(
2335 		PICTURE_CALLED_BY_FVWM, True, &colorLimitop, True, True);
2336 
2337 #ifdef Frsvg_init
2338 	Frsvg_init();
2339 #endif
2340 	FShapeInit(dpy);
2341 	FRenderInit(dpy);
2342 
2343 	Scr.pscreen = XScreenOfDisplay(dpy, Scr.screen);
2344 	Scr.use_backing_store = DoesBackingStore(Scr.pscreen);
2345 	Scr.flags.do_save_under = DoesSaveUnders(Scr.pscreen);
2346 
2347 	InternUsefulAtoms();
2348 
2349 	/* Make sure property priority colors is empty */
2350 	XChangeProperty(dpy, Scr.Root, _XA_MIT_PRIORITY_COLORS,
2351 			XA_CARDINAL, 32, PropModeReplace, NULL, 0);
2352 
2353 	Scr.FvwmCursors = CreateCursors(dpy);
2354 	XDefineCursor(dpy, Scr.Root, Scr.FvwmCursors[CRS_ROOT]);
2355 	/* create a window which will accept the keyboard focus when no other
2356 	 * windows have it */
2357 	/* do this before any RC parsing as some GC's are created from this
2358 	 * window rather than the root window */
2359 	attributes.event_mask = XEVMASK_NOFOCUSW;
2360 	attributes.override_redirect = True;
2361 	attributes.colormap = Pcmap;
2362 	attributes.cursor = Scr.FvwmCursors[CRS_DEFAULT];
2363 	attributes.background_pixmap = None;
2364 	attributes.border_pixel = 0;
2365 	Scr.NoFocusWin=XCreateWindow(
2366 		dpy, Scr.Root, -10, -10, 10, 10, 0, Pdepth, InputOutput,
2367 		Pvisual, CWEventMask | CWOverrideRedirect | CWColormap |
2368 		CWBackPixmap | CWBorderPixel | CWCursor, &attributes);
2369 	XMapWindow(dpy, Scr.NoFocusWin);
2370 	SetMWM_INFO(Scr.NoFocusWin);
2371 	FOCUS_SET(Scr.NoFocusWin, NULL);
2372 
2373 	frame_init();
2374 	XFlush(dpy);
2375 	if (debugging)
2376 	{
2377 		sync_server(1);
2378 	}
2379 
2380 	SetupICCCM2(replace_wm);
2381 	XSetIOErrorHandler(CatchFatal);
2382 	{
2383 		/* We need to catch any errors of XSelectInput on the root
2384 		 * window here.  The event mask contains
2385 		 * SubstructureRedirectMask which can be acquired by exactly
2386 		 * one client (window manager).  Synchronizing is necessary
2387 		 * here because Neither XSetErrorHandler nor XSelectInput
2388 		 * generate any protocol requests.
2389 		 */
2390 		XSync(dpy, 0);
2391 		XSetErrorHandler(CatchRedirectError);
2392 		XSelectInput(dpy, Scr.Root, XEVMASK_ROOTW);
2393 		XSync(dpy, 0);
2394 		XSetErrorHandler(FvwmErrorHandler);
2395 	}
2396 	{
2397 		/* do not grab the pointer earlier because if fvwm exits with
2398 		 * the pointer grabbed while a different display is visible,
2399 		 * XFree 4.0 freezes. */
2400 		Cursor cursor = XCreateFontCursor(dpy, XC_watch);
2401 		XGrabPointer(
2402 			dpy, Scr.Root, 0, 0, GrabModeAsync, GrabModeAsync,
2403 			None, cursor, CurrentTime);
2404 	}
2405 	{
2406 		Atom atype;
2407 		int aformat;
2408 		unsigned long nitems, bytes_remain;
2409 		unsigned char *prop;
2410 
2411 		if (XGetWindowProperty(
2412 			    dpy, Scr.Root, _XA_WM_DESKTOP, 0L, 1L, True,
2413 			    _XA_WM_DESKTOP, &atype, &aformat, &nitems,
2414 			    &bytes_remain, &prop) == Success)
2415 		{
2416 			if (prop != NULL)
2417 			{
2418 				Restarting = True;
2419 				/* do_force_single_screen = True; */
2420 			}
2421 		}
2422 	}
2423 	xasprintf(&restart_state_filename, "%s/.fs-restart-%s", fvwm_userdir,
2424 	    getenv("HOSTDISPLAY"));
2425 	if (!state_filename && Restarting)
2426 	{
2427 		state_filename = restart_state_filename;
2428 	}
2429 
2430 	InitVariables();
2431 	if (visualClass != -1 || visualId != -1)
2432 	{
2433 		/* this is so that menus use the (non-default) fvwm colormap */
2434 		FW_W(&Scr.FvwmRoot) = Scr.NoFocusWin;
2435 		Scr.FvwmRoot.number_cmap_windows = 1;
2436 		Scr.FvwmRoot.cmap_windows = &Scr.NoFocusWin;
2437 	}
2438 	InitEventHandlerJumpTable();
2439 
2440 	Scr.gray_bitmap =
2441 		XCreateBitmapFromData(dpy,Scr.Root,g_bits, g_width,g_height);
2442 
2443 	/* This should be done early enough to have the window states loaded
2444 	 * before the first call to AddWindow. */
2445 	fvwm_debug(__func__, "Loading window states via %s", state_filename);
2446 	LoadWindowStates(state_filename);
2447 
2448 	is_tracking_shared = false;
2449 	TAILQ_FOREACH(m, &monitor_q, entry)
2450 		EWMH_Init(m);
2451 
2452 	SetRCDefaults();
2453 	flush_window_updates();
2454 	simplify_style_list();
2455 
2456 	ecc.type = Restarting ? EXCT_RESTART : EXCT_INIT;
2457 	ecc.w.wcontext = C_ROOT;
2458 	exc = exc_create_context(&ecc, ECC_TYPE | ECC_WCONTEXT);
2459 	if (num_config_commands > 0)
2460 	{
2461 		int i;
2462 		for (i = 0; i < num_config_commands; i++)
2463 		{
2464 			DoingCommandLine = True;
2465 			execute_function(NULL, exc, config_commands[i], 0);
2466 			free(config_commands[i]);
2467 		}
2468 		DoingCommandLine = False;
2469 	}
2470 	else
2471 	{
2472 		/* Run startup command file in these places (default prefix):
2473 		 *   ~/.fvwm/config
2474 		 *   /usr/local/share/fvwm/config
2475 		 * and for compatibility:
2476 		 *   ~/.fvwm/.fvwm2rc
2477 		 *   /usr/local/share/fvwm/system.fvwm2rc
2478 		 * and for compatibility to be discontinued:
2479 		 *   ~/.fvwm2rc,
2480 		 *   /usr/local/share/fvwm/.fvwm2rc
2481 		 *   /usr/local/etc/system.fvwm2rc
2482 		 */
2483 		int upper = 8;
2484 		int nl = -1, tries = 0;
2485 		char *cfg_loc[upper];
2486 
2487 		xasprintf(&cfg_loc[++nl], "%s/%s", fvwm_userdir, FVWM_CONFIG);
2488 		xasprintf(&cfg_loc[++nl], "%s/%s", FVWM_DATADIR, FVWM_CONFIG);
2489 		xasprintf(&cfg_loc[++nl], "%s/%s", fvwm_userdir, FVWM2RC);
2490 		xasprintf(&cfg_loc[++nl], "%s/%s", home_dir, FVWM2RC);
2491 		xasprintf(&cfg_loc[++nl], "%s/%s", FVWM_DATADIR, FVWM2RC);
2492 		xasprintf(&cfg_loc[++nl], "%s/system%s", FVWM_DATADIR, FVWM2RC);
2493 		xasprintf(&cfg_loc[++nl], "%s/system%s", FVWM_CONFDIR, FVWM2RC);
2494 		xasprintf(&cfg_loc[++nl], "%s/default-config/config", FVWM_DATADIR);
2495 
2496 		for (nl = 0; nl < upper; nl++) {
2497 			if (!run_command_file(cfg_loc[nl], exc)) {
2498 				fvwm_debug(__func__,
2499 				    "couldn't find/load [%d]: %s\n", nl, cfg_loc[nl]);
2500 				tries++;
2501 			} else {
2502 				fprintf(stderr, "loaded [%d]: %s\n", nl, cfg_loc[nl]);
2503 				break;
2504 			}
2505 		}
2506 
2507 		if (tries == upper) {
2508 			fvwm_debug(__func__,
2509 				   "Cannot read startup config file,"
2510 				   " tried: \n\t%s/%s\n\t%s/%s\n\t%s/%s\n\t"
2511 				   "%s/%s\n\t%s/%s\n\t%s/system%s\n\t%s/system%s",
2512 				   fvwm_userdir, FVWM_CONFIG,
2513 				   FVWM_DATADIR, FVWM_CONFIG,
2514 				   fvwm_userdir, FVWM2RC,
2515 				   home_dir, FVWM2RC,
2516 				   FVWM_DATADIR, FVWM2RC,
2517 				   FVWM_DATADIR, FVWM2RC,
2518 				   FVWM_CONFDIR, FVWM2RC);
2519 		}
2520 		for (nl = 0; nl < upper; nl++)
2521 			free(cfg_loc[nl]);
2522 	}
2523 	exc_destroy_context(exc);
2524 
2525 	if (Pdepth<2)
2526 	{
2527 		Scr.gray_pixmap = XCreatePixmapFromBitmapData(
2528 			dpy, Scr.NoFocusWin, g_bits, g_width, g_height,
2529 			PictureBlackPixel(), PictureWhitePixel(), Pdepth);
2530 		Scr.light_gray_pixmap = XCreatePixmapFromBitmapData(
2531 			dpy, Scr.NoFocusWin, l_g_bits, l_g_width, l_g_height,
2532 			PictureBlackPixel(), PictureWhitePixel(), Pdepth);
2533 		Scr.sticky_gray_pixmap = XCreatePixmapFromBitmapData(
2534 			dpy, Scr.NoFocusWin, s_g_bits, s_g_width, s_g_height,
2535 			PictureBlackPixel(), PictureWhitePixel(), Pdepth);
2536 	}
2537 
2538 	attributes.background_pixel = Scr.StdBack;
2539 	attributes.colormap = Pcmap;
2540 	attributes.border_pixel = 0;
2541 	valuemask = CWBackPixel | CWColormap | CWBorderPixel;
2542 
2543 	Scr.SizeWindow.win = XCreateWindow(
2544 		dpy, Scr.Root, 0, 0, 1, 1, 0, Pdepth,
2545 		InputOutput, Pvisual, valuemask, &attributes);
2546 	resize_geometry_window();
2547 	CoerceEnterNotifyOnCurrentWindow();
2548 	SessionInit();
2549 
2550 	HandleEvents();
2551 	switch (fvwmRunState)
2552 	{
2553 	case FVWM_DONE:
2554 		Done(0, NULL);     /* does not return */
2555 
2556 	case FVWM_RESTART:
2557 		Done(1, "");       /* does not return */
2558 
2559 	default:
2560 		break;
2561 	}
2562 
2563 	exit(0);
2564 }
2565