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.
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.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * This module is all original code
20 * Copyright 1993, Robert Nation
21 * You may use this code for any purpose, as long as the original
22 * copyright remains in the source code and all documentation
25 /* ---------------------------- included header files ---------------------- */
39 #if HAVE_SYS_SYSTEMINFO_H
40 /* Solaris has sysinfo instead of gethostname. */
41 #include <sys/systeminfo.h>
44 #include <X11/Xproto.h>
45 #include <X11/Xatom.h>
47 #include "libs/fvwmlib.h"
48 #include "libs/envvar.h"
49 #include "libs/Strings.h"
50 #include "libs/System.h"
51 #include "libs/Grab.h"
52 #include "libs/ColorUtils.h"
53 #include "libs/Graphics.h"
54 #include "libs/FScreen.h"
55 #include "libs/FShape.h"
56 #include "libs/PictureBase.h"
57 #include "libs/PictureUtils.h"
58 #include "libs/Fsvg.h"
59 #include "libs/FRenderInit.h"
60 #include "libs/charmap.h"
61 #include "libs/wcontext.h"
65 #include "functions.h"
69 #include "module_list.h"
72 #include "eventhandler.h"
73 #include "eventmask.h"
77 #include "add_window.h"
78 #include "libs/fvwmsignal.h"
85 #include "move_resize.h"
88 #include "menubindings.h"
89 #include "libs/FGettext.h"
91 /* ---------------------------- local definitions -------------------------- */
93 #define MAXHOSTNAME 255
94 #define MAX_CFG_CMDS 10
95 #define MAX_ARG_SIZE 25
104 /* ---------------------------- local macros ------------------------------- */
106 /* ---------------------------- imports ------------------------------------ */
108 extern int last_event_type
;
110 /* ---------------------------- included code files ------------------------ */
112 /* ---------------------------- local types -------------------------------- */
121 /* ---------------------------- forward declarations ----------------------- */
123 /* ---------------------------- local variables ---------------------------- */
125 static char *config_commands
[MAX_CFG_CMDS
];
126 static int num_config_commands
=0;
128 /* assorted gray bitmaps for decorative borders */
129 static char g_bits
[] =
131 static char l_g_bits
[] =
133 static char s_g_bits
[] =
134 {0x01, 0x02, 0x04, 0x08};
136 static char *home_dir
;
138 static volatile sig_atomic_t fvwmRunState
= FVWM_RUNNING
;
140 static const char *init_function_names
[4] =
148 /* ---------------------------- exported variables (globals) --------------- */
150 int master_pid
; /* process number of 1st fvwm process */
152 ScreenInfo Scr
; /* structures for the screen */
153 Display
*dpy
= NULL
; /* which display are we talking to */
155 Bool fFvwmInStartup
= True
; /* Set to False when startup has finished */
156 Bool DoingCommandLine
= False
; /* Set True before each cmd line arg */
158 XContext FvwmContext
; /* context for fvwm windows */
159 XContext MenuContext
; /* context for fvwm menus */
161 int JunkX
= 0, JunkY
= 0;
162 Window JunkRoot
, JunkChild
; /* junk window */
163 int JunkWidth
, JunkHeight
, JunkBW
, JunkDepth
;
164 unsigned int JunkMask
;
166 Bool debugging
= False
;
167 Bool debugging_stack_ring
= False
;
169 Window bad_window
= None
;
174 char *state_filename
= NULL
;
175 char *restart_state_filename
= NULL
; /* $HOME/.fs-restart */
177 Bool Restarting
= False
;
179 char *display_name
= NULL
;
181 char const *Fvwm_VersionInfo
;
182 char const *Fvwm_LicenseInfo
;
183 char const *Fvwm_SupportInfo
;
185 Atom _XA_MIT_PRIORITY_COLORS
;
186 Atom _XA_WM_CHANGE_STATE
;
188 Atom _XA_WM_COLORMAP_WINDOWS
;
189 Atom _XA_WM_TAKE_FOCUS
;
190 Atom _XA_WM_DELETE_WINDOW
;
195 Atom _XA_OL_WIN_ATTR
;
199 Atom _XA_OL_WT_NOTICE
;
200 Atom _XA_OL_WT_OTHER
;
201 Atom _XA_OL_DECOR_ADD
;
202 Atom _XA_OL_DECOR_DEL
;
203 Atom _XA_OL_DECOR_CLOSE
;
204 Atom _XA_OL_DECOR_RESIZE
;
205 Atom _XA_OL_DECOR_HEADER
;
206 Atom _XA_OL_DECOR_ICON_NAME
;
208 Atom _XA_WM_WINDOW_ROLE
;
209 Atom _XA_WINDOW_ROLE
;
210 Atom _XA_WM_CLIENT_LEADER
;
211 Atom _XA_SM_CLIENT_ID
;
213 Atom _XA_XROOTPMAP_ID
;
214 Atom _XA_XSETROOT_ID
;
216 /* ---------------------------- local functions ---------------------------- */
218 static void SaveDesktopState(void)
221 unsigned long data
[1];
223 for (t
= Scr
.FvwmRoot
.next
; t
!= NULL
; t
= t
->next
)
225 data
[0] = (unsigned long) t
->Desk
;
227 dpy
, FW_W(t
), _XA_WM_DESKTOP
, _XA_WM_DESKTOP
, 32,
228 PropModeReplace
, (unsigned char *)data
, 1);
230 data
[0] = (unsigned long) Scr
.CurrentDesk
;
232 dpy
, Scr
.Root
, _XA_WM_DESKTOP
, _XA_WM_DESKTOP
, 32,
233 PropModeReplace
, (unsigned char *) data
, 1);
239 static void InternUsefulAtoms (void)
241 /* Create priority colors if necessary. */
242 _XA_MIT_PRIORITY_COLORS
= XInternAtom(
243 dpy
, "_MIT_PRIORITY_COLORS", False
);
244 _XA_WM_CHANGE_STATE
= XInternAtom(dpy
, "WM_CHANGE_STATE", False
);
245 _XA_WM_STATE
= XInternAtom(dpy
, "WM_STATE", False
);
246 _XA_WM_COLORMAP_WINDOWS
= XInternAtom(
247 dpy
, "WM_COLORMAP_WINDOWS", False
);
248 _XA_WM_PROTOCOLS
= XInternAtom(dpy
, "WM_PROTOCOLS", False
);
249 _XA_WM_TAKE_FOCUS
= XInternAtom(dpy
, "WM_TAKE_FOCUS", False
);
250 _XA_WM_DELETE_WINDOW
= XInternAtom(dpy
, "WM_DELETE_WINDOW", False
);
251 _XA_WM_DESKTOP
= XInternAtom(dpy
, "WM_DESKTOP", False
);
252 _XA_MwmAtom
=XInternAtom(dpy
, "_MOTIF_WM_HINTS",False
);
253 _XA_MOTIF_WM
=XInternAtom(dpy
, "_MOTIF_WM_INFO",False
);
254 _XA_OL_WIN_ATTR
=XInternAtom(dpy
, "_OL_WIN_ATTR",False
);
255 _XA_OL_WT_BASE
=XInternAtom(dpy
, "_OL_WT_BASE",False
);
256 _XA_OL_WT_CMD
=XInternAtom(dpy
, "_OL_WT_CMD",False
);
257 _XA_OL_WT_HELP
=XInternAtom(dpy
, "_OL_WT_HELP",False
);
258 _XA_OL_WT_NOTICE
=XInternAtom(dpy
, "_OL_WT_NOTICE",False
);
259 _XA_OL_WT_OTHER
=XInternAtom(dpy
, "_OL_WT_OTHER",False
);
260 _XA_OL_DECOR_ADD
=XInternAtom(dpy
, "_OL_DECOR_ADD",False
);
261 _XA_OL_DECOR_DEL
=XInternAtom(dpy
, "_OL_DECOR_DEL",False
);
262 _XA_OL_DECOR_CLOSE
=XInternAtom(dpy
, "_OL_DECOR_CLOSE",False
);
263 _XA_OL_DECOR_RESIZE
=XInternAtom(dpy
, "_OL_DECOR_RESIZE",False
);
264 _XA_OL_DECOR_HEADER
=XInternAtom(dpy
, "_OL_DECOR_HEADER",False
);
265 _XA_OL_DECOR_ICON_NAME
=XInternAtom(dpy
, "_OL_DECOR_ICON_NAME",False
);
266 _XA_WM_WINDOW_ROLE
=XInternAtom(dpy
, "WM_WINDOW_ROLE",False
);
267 _XA_WINDOW_ROLE
=XInternAtom(dpy
, "WINDOW_ROLE",False
);
268 _XA_WM_CLIENT_LEADER
=XInternAtom(dpy
, "WM_CLIENT_LEADER",False
);
269 _XA_SM_CLIENT_ID
=XInternAtom(dpy
, "SM_CLIENT_ID",False
);
270 _XA_XROOTPMAP_ID
=XInternAtom(dpy
, "_XROOTPMAP_ID",False
);
271 _XA_XSETROOT_ID
=XInternAtom(dpy
, "_XSETROOT_ID",False
);
276 /* exit handler that will try to release any grabs */
277 static void catch_exit(void)
281 /* Don't care if this is called from an X error handler. We
282 * *have* to try this, whatever happens. XFree 4.0 may freeze
283 * if we don't do this. */
285 XUngrabPointer(dpy
, CurrentTime
);
286 XUngrabKeyboard(dpy
, CurrentTime
);
293 * Restart on a signal
298 fvwmRunState
= FVWM_RESTART
;
300 /* This function might not return - it could "long-jump"
301 * right out, so we need to do everything we need to do
302 * BEFORE we call it ... */
303 fvwmSetTerminate(sig
);
311 fvwmRunState
= FVWM_DONE
;
313 /* This function might not return - it could "long-jump"
314 * right out, so we need to do everything we need to do
315 * BEFORE we call it ... */
316 fvwmSetTerminate(sig
);
322 * parse_command_args - parses a given command string into a given limited
323 * argument array suitable for execv*. The parsing is similar to shell's.
325 * positive number of parsed arguments - on success,
326 * 0 - on empty command (only spaces),
327 * negative - on no command or parsing error.
329 * Any character can be quoted with a backslash (even inside single quotes).
330 * Every command argument is separated by a space/tab/new-line from both sizes
331 * or is at the start/end of the command. Sequential spaces are ignored.
332 * An argument can be enclosed into single quotes (no further expanding)
333 * or double quotes (expending environmental variables $VAR or ${VAR}).
334 * The character '~' is expanded into user home directory (if not in quotes).
336 * In the current implementation, parsed arguments are stored in one
337 * large static string pointed by returned argv[0], so they will be lost
338 * on the next function call. This can be changed using dynamic allocation,
339 * in this case the caller must free the string pointed by argv[0].
341 static int parse_command_args(
342 const char *command
, char **argv
, int max_argc
, const char **error_msg
)
344 /* It is impossible to guess the exact length because of expanding */
345 #define MAX_TOTAL_ARG_LEN 256
346 /* char *arg_string = safemalloc(MAX_TOTAL_ARG_LEN); */
347 static char arg_string
[MAX_TOTAL_ARG_LEN
];
348 int total_arg_len
= 0;
351 char *aptr
= arg_string
;
352 const char *cptr
= command
;
354 #define the_char (*cptr)
355 #define adv_char (cptr++)
356 #define top_char (*cptr == '\\' ? *(cptr + 1) : *cptr)
357 #define pop_char (*(cptr++) == '\\' ? *(cptr++) : *(cptr - 1))
358 #define can_add_arg_char (total_arg_len < MAX_TOTAL_ARG_LEN-1)
359 #define add_arg_char(ch) (++total_arg_len, *(aptr++) = ch)
360 #define can_add_arg_str(str) (total_arg_len < MAX_TOTAL_ARG_LEN - strlen(str))
361 #define add_arg_str(str) \
363 const char *tmp = str;\
366 add_arg_char(*(tmp++));\
373 *error_msg
= "No command";
376 for (argc
= 0; argc
< max_argc
- 1; argc
++)
380 while (isspace(the_char
))
384 if (the_char
== '\0')
388 while ((s_quote
|| !isspace(the_char
)) &&
389 the_char
!= '\0' && can_add_arg_char
)
403 else if (!s_quote
&& the_char
== '\'')
406 while (the_char
!= '\'' && the_char
!= '\0' &&
409 add_arg_char(pop_char
);
411 if (the_char
== '\'')
415 else if (!can_add_arg_char
)
421 *error_msg
= "No closing single quote";
426 else if (!s_quote
&& the_char
== '~')
428 if (!can_add_arg_str(home_dir
))
432 add_arg_str(home_dir
);
435 else if (the_char
== '$')
438 const char *str
= getFirstEnv(cptr
, &beg
, &len
);
442 add_arg_char(the_char
);
446 if (!can_add_arg_str(str
))
455 if (add_arg_char(pop_char
) == '\0')
461 if (*(aptr
-1) == '\0')
463 *error_msg
= "Unexpected last backslash";
471 if (the_char
== '~' || the_char
== '$' || !can_add_arg_char
)
473 *error_msg
= "The command is too long";
474 error_code
= -argc
- 100;
479 *error_msg
= "No closing double quote";
489 #undef can_add_arg_char
491 #undef can_add_arg_str
494 if (argc
== 0 && !error_code
)
496 *error_msg
= "Void command";
499 return error_code
? error_code
: argc
;
506 char *get_display_name(char *display_name
, int screen_num
)
511 char string_screen_num
[32];
513 CopyString(&msg
, display_name
);
514 cp
= strchr(msg
, ':');
517 cp
= strchr(cp
, '.');
520 /* truncate at display part */
524 sprintf(string_screen_num
, ".%d", screen_num
);
526 strlen(msg
) + strlen(string_screen_num
) + 1);
529 strcat(new_dn
, string_screen_num
);
538 * Done - tells fvwm to clean up and exit
541 /* if restart is true, command must not be NULL... */
542 void Done(int restart
, char *command
)
544 const char *exit_func_name
;
548 MoveViewport(0,0,False
);
550 /* migo (03/Jul/1999): execute [Session]ExitFunction */
551 exit_func_name
= get_init_function_name(2);
552 if (functions_is_complex_function(exit_func_name
))
554 const exec_context_t
*exc
;
555 exec_context_changes_t ecc
;
557 char *action
= safestrdup(
558 CatString2("Function ", exit_func_name
));
559 ecc
.type
= restart
? EXCT_TORESTART
: EXCT_QUIT
;
560 ecc
.w
.wcontext
= C_ROOT
;
561 exc
= exc_create_context(&ecc
, ECC_TYPE
| ECC_WCONTEXT
);
562 execute_function(NULL
, exc
, action
, 0);
563 exc_destroy_context(exc
);
566 /* XFree freeze hack */
567 XUngrabPointer(dpy
, CurrentTime
);
568 XUngrabKeyboard(dpy
, CurrentTime
);
577 Bool do_preserve_state
= True
;
582 while (isspace(command
[0]))
586 if (strncmp(command
, "--dont-preserve-state", 21) == 0)
588 do_preserve_state
= False
;
590 while (isspace(command
[0])) command
++;
593 if (command
[0] == '\0')
595 command
= NULL
; /* native restart */
598 /* won't return under SM on Restart without parameters */
600 restart_state_filename
, command
== NULL
,
603 /* RBW - 06/08/1999 - without this, windows will wander to
604 * other pages on a Restart/Recapture because Restart gets the
605 * window position information out of sync. There may be a
606 * better way to do this (i.e., adjust the Restart code), but
607 * this works for now. */
608 MoveViewport(0,0,False
);
611 /* Really make sure that the connection is closed and cleared!
618 /* really need to destroy all windows, explicitly, not sleep,
619 * but this is adequate for now */
624 char *my_argv
[MAX_ARG_SIZE
];
625 const char *error_msg
;
626 int n
= parse_command_args(
627 command
, my_argv
, MAX_ARG_SIZE
, &error_msg
);
633 "Restart command parsing error in"
634 " (%s): [%s]", command
, error_msg
);
636 else if (strcmp(my_argv
[0], "--pass-args") == 0)
642 "Restart --pass-args: single"
643 " name expected. (restarting"
644 " '%s' instead)", g_argv
[0]);
650 my_argv
[0] = my_argv
[1];
651 for (i
= 1; i
< g_argc
&&
652 i
< MAX_ARG_SIZE
- 1; i
++)
654 my_argv
[i
] = g_argv
[i
];
658 execvp(my_argv
[0], my_argv
);
661 "Call of '%s' failed!"
662 " (restarting '%s' instead)",
663 my_argv
[0], g_argv
[0]);
664 perror(" system error description");
672 /* Warn against an old 'Restart fvwm2' usage */
673 if (n
== 1 && strcmp(my_argv
[0], "fvwm2") == 0)
677 /* If we are at it, warn against a 'Restart
678 * fvwm' usage as well */
680 strcmp(my_argv
[0], "fvwm") == 0)
688 "`Restart %s' might not do"
689 " what you want, see the man"
690 " page.\n\tUse Restart without"
691 " parameters if you mean to"
692 " restart the same WM.", str
);
694 execvp(my_argv
[0], my_argv
);
696 ERR
, "Done", "Call of '%s' failed!"
697 " (restarting '%s' instead)",
698 my_argv
[0], g_argv
[0]);
699 perror(" system error description");
703 execvp(g_argv
[0], g_argv
); /* that _should_ work */
704 fvwm_msg(ERR
, "Done", "Call of '%s' failed!", g_argv
[0]);
705 perror(" system error description");
715 /* dv (15-Jan-2000): This must be done after calling CloseICCCM2()!
716 * Otherwise fvwm ignores map requests while it still has
717 * SubstructureRedirect selected on the root window ==> windows end up
718 * in nirvana. This explicitly happened with windows unswallowed by
725 /***********************************************************************
728 * InstallSignals: install the signal handlers, using whatever
729 * means we have at our disposal. The more POSIXy, the better
731 ************************************************************************/
735 #ifdef HAVE_SIGACTION
736 struct sigaction sigact
;
739 * All signals whose handlers call fvwmSetTerminate()
740 * must be mutually exclusive - we mustn't receive one
741 * while processing any of the others ...
743 sigemptyset(&sigact
.sa_mask
);
744 sigaddset(&sigact
.sa_mask
, SIGINT
);
745 sigaddset(&sigact
.sa_mask
, SIGHUP
);
746 sigaddset(&sigact
.sa_mask
, SIGQUIT
);
747 sigaddset(&sigact
.sa_mask
, SIGTERM
);
748 sigaddset(&sigact
.sa_mask
, SIGUSR1
);
751 sigact
.sa_flags
= SA_RESTART
;
755 sigact
.sa_handler
= DeadPipe
;
756 sigaction(SIGPIPE
, &sigact
, NULL
);
758 sigact
.sa_handler
= Restart
;
759 sigaction(SIGUSR1
, &sigact
, NULL
);
761 sigact
.sa_handler
= SigDone
;
762 sigaction(SIGINT
, &sigact
, NULL
);
763 sigaction(SIGHUP
, &sigact
, NULL
);
764 sigaction(SIGQUIT
, &sigact
, NULL
);
765 sigaction(SIGTERM
, &sigact
, NULL
);
767 /* Reap all zombies automatically! This signal handler will only be
768 * called if a child process dies, not if someone sends a child a STOP
769 * signal. Note that none of our "terminate" signals can be delivered
770 * until the SIGCHLD handler completes, and this is a Good Thing
771 * because the terminate handlers might exit abruptly via "siglongjmp".
772 * This could potentially leave SIGCHLD handler with unfinished
775 * NOTE: We could still receive SIGPIPE signals within the SIGCHLD
776 * handler, but the SIGPIPE handler has the SA_RESTART flag set and so
777 * should not affect our "wait" system call. */
778 sigact
.sa_flags
|= SA_NOCLDSTOP
;
779 sigact
.sa_handler
= fvwmReapChildren
;
780 sigaction(SIGCHLD
, &sigact
, NULL
);
782 #ifdef USE_BSD_SIGNALS
784 sigmask(SIGUSR1
) | sigmask(SIGINT
) | sigmask(SIGHUP
) |
785 sigmask(SIGQUIT
) | sigmask(SIGTERM
) );
789 * We don't have sigaction(), so fall back on
790 * less reliable methods ...
792 signal(SIGPIPE
, DeadPipe
);
793 signal(SIGUSR1
, Restart
);
794 #ifdef HAVE_SIGINTERRUPT
795 siginterrupt(SIGUSR1
, 0);
798 signal(SIGINT
, SigDone
);
799 #ifdef HAVE_SIGINTERRUPT
800 siginterrupt(SIGINT
, 0);
802 signal(SIGHUP
, SigDone
);
803 #ifdef HAVE_SIGINTERRUPT
804 siginterrupt(SIGHUP
, 0);
806 signal(SIGQUIT
, SigDone
);
807 #ifdef HAVE_SIGINTERRUPT
808 siginterrupt(SIGQUIT
, 0);
810 signal(SIGTERM
, SigDone
);
811 #ifdef HAVE_SIGINTERRUPT
812 siginterrupt(SIGTERM
, 0);
814 signal(SIGCHLD
, fvwmReapChildren
);
815 #ifdef HAVE_SIGINTERRUPT
816 siginterrupt(SIGCHLD
, 0);
820 /* When fvwm restarts, the SIGCHLD handler is automatically reset
821 * to the default handler. This means that Zombies left over from
822 * the previous instance of fvwm could still be roaming the process
823 * table if they exited while the default handler was in place.
824 * We fix this by invoking the SIGCHLD handler NOW, so that they
825 * may finally rest in peace. */
831 void fvmm_deinstall_signals(void)
833 signal(SIGCHLD
, SIG_DFL
);
834 signal(SIGHUP
, SIG_DFL
);
835 signal(SIGINT
, SIG_DFL
);
836 signal(SIGPIPE
, SIG_DFL
);
837 signal(SIGQUIT
, SIG_DFL
);
838 signal(SIGTERM
, SIG_DFL
);
839 signal(SIGUSR1
, SIG_DFL
);
844 /***********************************************************************
846 * LoadDefaultLeftButton -- loads default left button # into
847 * assumes associated button memory is already free
849 ************************************************************************/
850 static void LoadDefaultLeftButton(DecorFace
*df
, int i
)
852 struct vector_coords
*v
= &df
->u
.vector
;
855 memset(&df
->style
, 0, sizeof(df
->style
));
856 DFS_FACE_TYPE(df
->style
) = DefaultVectorButton
;
862 v
->x
= (signed char*)safemalloc(sizeof(char) * v
->num
);
863 v
->y
= (signed char*)safemalloc(sizeof(char) * v
->num
);
864 v
->xoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
865 v
->yoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
866 v
->c
= (signed char*)safecalloc(v
->num
, sizeof(char));
883 v
->x
= (signed char*)safemalloc(sizeof(char) * v
->num
);
884 v
->y
= (signed char*)safemalloc(sizeof(char) * v
->num
);
885 v
->xoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
886 v
->yoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
887 v
->c
= (signed char*)safecalloc(v
->num
, sizeof(char));
903 v
->x
= (signed char*)safemalloc(sizeof(char) * v
->num
);
904 v
->y
= (signed char*)safemalloc(sizeof(char) * v
->num
);
905 v
->xoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
906 v
->yoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
907 v
->c
= (signed char*)safecalloc(v
->num
, sizeof(char));
924 v
->x
= (signed char*)safemalloc(sizeof(char) * v
->num
);
925 v
->y
= (signed char*)safemalloc(sizeof(char) * v
->num
);
926 v
->xoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
927 v
->yoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
928 v
->c
= (signed char*)safecalloc(v
->num
, sizeof(char));
944 /* set offsets to 0, for all buttons */
945 for(j
= 0 ; j
< v
->num
; j
++)
954 /***********************************************************************
956 * LoadDefaultRightButton -- loads default left button # into
957 * assumes associated button memory is already free
959 ************************************************************************/
960 static void LoadDefaultRightButton(DecorFace
*df
, int i
)
962 struct vector_coords
*v
= &df
->u
.vector
;
965 memset(&df
->style
, 0, sizeof(df
->style
));
966 DFS_FACE_TYPE(df
->style
) = DefaultVectorButton
;
972 v
->x
= (signed char*)safemalloc(sizeof(char) * v
->num
);
973 v
->y
= (signed char*)safemalloc(sizeof(char) * v
->num
);
974 v
->xoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
975 v
->yoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
976 v
->c
= (signed char*)safecalloc(v
->num
, sizeof(char));
993 v
->x
= (signed char*)safemalloc(sizeof(char) * v
->num
);
994 v
->y
= (signed char*)safemalloc(sizeof(char) * v
->num
);
995 v
->xoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
996 v
->yoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
997 v
->c
= (signed char*)safecalloc(v
->num
, sizeof(char));
1014 v
->x
= (signed char*)safemalloc(sizeof(char) * v
->num
);
1015 v
->y
= (signed char*)safemalloc(sizeof(char) * v
->num
);
1016 v
->xoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
1017 v
->yoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
1018 v
->c
= (signed char*)safecalloc(v
->num
, sizeof(char));
1035 v
->x
= (signed char*)safemalloc(sizeof(char) * v
->num
);
1036 v
->y
= (signed char*)safemalloc(sizeof(char) * v
->num
);
1037 v
->xoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
1038 v
->yoff
= (signed char*)safemalloc(sizeof(char) * v
->num
);
1039 v
->c
= (signed char*)safecalloc(v
->num
, sizeof(char));
1055 /* set offsets to 0, for all buttons */
1056 for(j
= 0 ; j
< v
->num
; j
++)
1065 /***********************************************************************
1068 * CreateGCs - open fonts and create all the needed GC's. I only
1069 * want to do this once, hence the first_time flag.
1071 ***********************************************************************/
1072 static void CreateGCs(void)
1078 /* create scratch GC's */
1079 gcm
= GCFunction
|GCLineWidth
;
1080 gcv
.function
= GXcopy
;
1083 Scr
.ScratchGC1
= fvwmlib_XCreateGC(dpy
, Scr
.NoFocusWin
, gcm
, &gcv
);
1084 Scr
.ScratchGC2
= fvwmlib_XCreateGC(dpy
, Scr
.NoFocusWin
, gcm
, &gcv
);
1085 Scr
.ScratchGC3
= fvwmlib_XCreateGC(dpy
, Scr
.NoFocusWin
, gcm
, &gcv
);
1086 Scr
.ScratchGC4
= fvwmlib_XCreateGC(dpy
, Scr
.NoFocusWin
, gcm
, &gcv
);
1087 Scr
.TitleGC
= fvwmlib_XCreateGC(dpy
, Scr
.NoFocusWin
, gcm
, &gcv
);
1088 Scr
.BordersGC
= fvwmlib_XCreateGC(dpy
, Scr
.NoFocusWin
, gcm
, &gcv
);
1089 Scr
.TransMaskGC
= fvwmlib_XCreateGC(dpy
, Scr
.NoFocusWin
, gcm
, &gcv
);
1090 c
.pixel
= GetColor(DEFAULT_FORE_COLOR
);
1091 Scr
.ScratchMonoPixmap
= XCreatePixmap(dpy
, Scr
.Root
, 1, 1, 1);
1092 Scr
.MonoGC
= fvwmlib_XCreateGC(dpy
, Scr
.ScratchMonoPixmap
, gcm
, &gcv
);
1093 Scr
.ScratchAlphaPixmap
= XCreatePixmap(
1094 dpy
, Scr
.Root
, 1, 1, FRenderGetAlphaDepth());
1095 Scr
.AlphaGC
= fvwmlib_XCreateGC(dpy
, Scr
.ScratchAlphaPixmap
, gcm
, &gcv
);
1099 /***********************************************************************
1102 * InitVariables - initialize fvwm variables
1104 ************************************************************************/
1105 static void InitVariables(void)
1107 FvwmContext
= XUniqueContext();
1108 MenuContext
= XUniqueContext();
1110 /* initialize some lists */
1111 Scr
.AllBindings
= NULL
;
1112 Scr
.functions
= NULL
;
1114 Scr
.last_added_item
.type
= ADDED_NONE
;
1115 Scr
.DefaultIcon
= NULL
;
1116 Scr
.DefaultColorset
= -1;
1118 Scr
.StdReliefGC
= 0;
1119 Scr
.StdShadowGC
= 0;
1121 /* zero all flags */
1122 memset(&Scr
.flags
, 0, sizeof(Scr
.flags
));
1123 /* create graphics contexts */
1126 FW_W(&Scr
.FvwmRoot
) = Scr
.Root
;
1127 Scr
.FvwmRoot
.next
= 0;
1128 init_stack_and_layers();
1129 Scr
.root_pushes
= 0;
1130 Scr
.fvwm_pushes
= 0;
1131 Scr
.pushed_window
= &Scr
.FvwmRoot
;
1132 Scr
.FvwmRoot
.number_cmap_windows
= 0;
1133 Scr
.FvwmRoot
.attr_backup
.colormap
= Pcmap
;
1135 Scr
.MyDisplayWidth
= DisplayWidth(dpy
, Scr
.screen
);
1136 Scr
.MyDisplayHeight
= DisplayHeight(dpy
, Scr
.screen
);
1137 Scr
.BusyCursor
= BUSY_NONE
;
1139 Scr
.DefaultFont
= NULL
;
1140 Scr
.VxMax
= 2*Scr
.MyDisplayWidth
;
1141 Scr
.VyMax
= 2*Scr
.MyDisplayHeight
;
1144 Scr
.SizeWindow
= None
;
1146 /* Sets the current desktop number to zero */
1147 /* Multiple desks are available even in non-virtual
1149 Scr
.CurrentDesk
= 0;
1150 Scr
.EdgeScrollX
= DEFAULT_EDGE_SCROLL
* Scr
.MyDisplayWidth
/ 100;
1151 Scr
.EdgeScrollY
= DEFAULT_EDGE_SCROLL
* Scr
.MyDisplayHeight
/ 100;
1152 Scr
.ScrollDelay
= DEFAULT_SCROLL_DELAY
;
1153 Scr
.OpaqueSize
= DEFAULT_OPAQUE_MOVE_SIZE
;
1154 Scr
.MoveThreshold
= DEFAULT_MOVE_THRESHOLD
;
1155 /* ClickTime is set to the positive value upon entering the
1157 Scr
.ClickTime
= -DEFAULT_CLICKTIME
;
1158 Scr
.ColormapFocus
= COLORMAP_FOLLOWS_MOUSE
;
1160 /* set major operating modes */
1164 /* the last Cascade placed window or NULL, we don't want NULL
1166 Scr
.cascade_window
= &Scr
.FvwmRoot
;
1167 Scr
.buttons2grab
= 0;
1168 /* initialisation of the head of the desktops info */
1169 Scr
.Desktops
= (DesktopsInfo
*)safemalloc(sizeof(DesktopsInfo
));
1170 Scr
.Desktops
->name
= NULL
;
1171 Scr
.Desktops
->desk
= 0; /* not desk 0 */
1172 Scr
.Desktops
->ewmh_dyn_working_area
.x
=
1173 Scr
.Desktops
->ewmh_working_area
.x
= 0;
1174 Scr
.Desktops
->ewmh_dyn_working_area
.y
=
1175 Scr
.Desktops
->ewmh_working_area
.y
= 0;
1176 Scr
.Desktops
->ewmh_dyn_working_area
.width
=
1177 Scr
.Desktops
->ewmh_working_area
.width
= Scr
.MyDisplayWidth
;
1178 Scr
.Desktops
->ewmh_dyn_working_area
.height
=
1179 Scr
.Desktops
->ewmh_working_area
.height
= Scr
.MyDisplayHeight
;
1180 Scr
.Desktops
->next
= NULL
;
1182 Scr
.EwmhDesktop
= NULL
;
1183 InitFvwmDecor(&Scr
.DefaultDecor
);
1185 Scr
.DefaultDecor
.tag
= "Default";
1187 /* Initialize RaiseHackNeeded by identifying X servers
1188 possibly running under NT. This is probably not an
1189 ideal solution, since eg NCD also produces X servers
1190 which do not run under NT.
1192 "Hummingbird Communications Ltd."
1193 is the ServerVendor string of the Exceed X server under NT,
1195 "Network Computing Devices Inc."
1196 is the ServerVendor string of the PCXware X server under Windows.
1199 is the ServerVendor string of the Reflection X server under Windows.
1201 Scr
.bo
.is_raise_hack_needed
=
1204 "Hummingbird Communications Ltd.") == 0) ||
1207 "Network Computing Devices Inc.") == 0) ||
1208 (strcmp (ServerVendor (dpy
), "WRQ, Inc.") == 0);
1210 Scr
.bo
.is_modality_evil
= 0;
1211 Scr
.bo
.do_disable_configure_notify
= 0;
1212 Scr
.bo
.do_install_root_cmap
= 0;
1213 Scr
.bo
.do_enable_flickering_qt_dialogs_workaround
= 1;
1214 Scr
.bo
.do_enable_qt_drag_n_drop_workaround
= 0;
1215 Scr
.bo
.do_enable_ewmh_iconic_state_workaround
= 0;
1217 Scr
.gs
.do_emulate_mwm
= DEFAULT_EMULATE_MWM
;
1218 Scr
.gs
.do_emulate_win
= DEFAULT_EMULATE_WIN
;
1219 Scr
.gs
.use_active_down_buttons
= DEFAULT_USE_ACTIVE_DOWN_BUTTONS
;
1220 Scr
.gs
.use_inactive_buttons
= DEFAULT_USE_INACTIVE_BUTTONS
;
1221 Scr
.gs
.use_inactive_down_buttons
= DEFAULT_USE_INACTIVE_DOWN_BUTTONS
;
1222 /* Not the right place for this, should only be called once
1225 /* EdgeCommands - no edge commands by default */
1226 Scr
.PanFrameTop
.command
= NULL
;
1227 Scr
.PanFrameBottom
.command
= NULL
;
1228 Scr
.PanFrameRight
.command
= NULL
;
1229 Scr
.PanFrameLeft
.command
= NULL
;
1230 /* EdgeLeaveCommands - no edge leave commands by default */
1231 Scr
.PanFrameTop
.command_leave
= NULL
;
1232 Scr
.PanFrameBottom
.command_leave
= NULL
;
1233 Scr
.PanFrameRight
.command_leave
= NULL
;
1234 Scr
.PanFrameLeft
.command_leave
= NULL
;
1235 Scr
.flags
.is_pointer_on_this_screen
= !!FQueryPointer(
1236 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
, &JunkX
, &JunkY
, &JunkX
,
1239 /* make sure colorset 0 exists */
1245 static void usage(int is_verbose
)
1247 fprintf(stderr
, "usage: %s", g_argv
[0]);
1252 " [-s [screen_num]]"
1253 " [-I vis-id | -C vis-class]"
1257 " [OTHER OPTIONS] ..."
1262 stderr
, "Try '%s --help' for more information.\n",
1267 " -A: allocate palette\n"
1268 " -c cmd: preprocess configuration file with <cmd>\n"
1269 " -C vis-class: use visual class <vis-class>\n"
1270 " -d display: run fvwm on <display>\n"
1271 " -D: enable debug oputput\n"
1272 " -f cfgfile: read configuration from <cfgfile>\n"
1273 " -F file: used internally for session management\n"
1274 " -h, -?: print this help message\n"
1275 " -i client-id: used internally for session management\n"
1276 " -I vis-id: use visual <vis-id>\n"
1277 " -l colors: try to use no more than <colors> colors\n"
1278 " -L: strict color limit\n"
1279 " -P: visual palette\n"
1280 " -r: replace running window manager\n"
1281 " -s [screen]: manage a single screen\n"
1282 " -S: static palette\n"
1283 " -V: print version information\n"
1286 stderr
, "Try 'man %s' for more information.\n",
1292 static void setVersionInfo(void)
1294 char version_str
[256];
1295 char license_str
[512];
1296 char support_str
[512] = "";
1299 /* Set version information string */
1300 sprintf(version_str
, "fvwm %s%s compiled on %s at %s",
1301 VERSION
, VERSIONINFO
, __DATE__
, __TIME__
);
1302 Fvwm_VersionInfo
= safestrdup(version_str
);
1304 sprintf(license_str
,
1305 "fvwm comes with NO WARRANTY, to the extent permitted by law. "
1306 "You may\nredistribute copies of fvwm under "
1307 "the terms of the GNU General Public License.\n"
1308 "For more information about these matters, see the file "
1310 Fvwm_LicenseInfo
= safestrdup(license_str
);
1312 #ifdef HAVE_READLINE
1313 strcat(support_str
, " ReadLine,");
1316 strcat(support_str
, " RPlay,");
1319 strcat(support_str
, " Stroke,");
1322 strcat(support_str
, " XPM,");
1325 strcat(support_str
, " PNG,");
1328 strcat(support_str
, " SVG,");
1330 if (FHaveShapeExtension
)
1331 strcat(support_str
, " Shape,");
1333 strcat(support_str
, " XShm,");
1336 strcat(support_str
, " SM,");
1339 strcat(support_str
, " Bidi text,");
1341 #ifdef HAVE_XINERAMA
1342 strcat(support_str
, " Xinerama,");
1345 strcat(support_str
, " XRender,");
1348 strcat(support_str
, " XCursor,");
1351 strcat(support_str
, " XFT,");
1354 strcat(support_str
, " NLS,");
1357 support_len
= strlen(support_str
);
1358 if (support_len
> 0)
1360 /* strip last comma */
1361 support_str
[support_len
- 1] = '\0';
1362 Fvwm_SupportInfo
= safestrdup(
1363 CatString2("with support for:", support_str
));
1367 Fvwm_SupportInfo
= "with no optional feature support";
1373 /* Sets some initial style values & such */
1374 static void SetRCDefaults(void)
1376 #define RC_DEFAULTS_COMPLETE ((char *)-1)
1378 /* set up default colors, fonts, etc */
1379 const char *defaults
[][3] = {
1380 { "XORValue 0", "", "" },
1381 { "DefaultFont", "", "" },
1382 { "DefaultColors black grey", "", "" },
1383 { DEFAULT_MENU_STYLE
, "", "" },
1384 { "TitleStyle Centered -- Raised", "", "" },
1385 { "Style * Color lightgrey/dimgrey", "", "" },
1386 { "Style * HilightFore black, HilightBack grey", "", "" },
1388 "AddToMenu MenuFvwmRoot \"",
1392 { "+ \"&1. XTerm\" Exec xterm", "", ""},
1396 "\" Module FvwmForm FvwmForm-Setup"
1400 _("Setup 95 Script"),
1401 "\" Module FvwmScript FvwmScript-Setup95"
1405 _("Issue fvwm commands"),
1406 "\" Module FvwmConsole"
1418 { "Mouse 1 R A Menu MenuFvwmRoot", "", "" },
1419 /* default menu navigation */
1420 { "Key Escape M A MenuClose", "", "" },
1421 { "Key Return M A MenuSelectItem", "", "" },
1422 { "Key Left M A MenuCursorLeft", "", "" },
1423 { "Key Right M A MenuCursorRight", "", "" },
1424 { "Key Up M A MenuMoveCursor -1", "", "" },
1425 { "Key Down M A MenuMoveCursor 1", "", "" },
1426 { "Mouse 1 MI A MenuSelectItem", "", "" },
1427 /* don't add anything below */
1428 { RC_DEFAULTS_COMPLETE
, "", "" },
1429 { "Read "FVWM_DATADIR
"/ConfigFvwmDefaults", "", "" },
1430 { NULL
, NULL
, NULL
}
1433 for (i
= 0; defaults
[i
][0] != NULL
; i
++)
1435 const exec_context_t
*exc
;
1436 exec_context_changes_t ecc
;
1439 if (defaults
[i
][0] == RC_DEFAULTS_COMPLETE
)
1441 menu_bindings_startup_complete();
1444 ecc
.type
= Restarting
? EXCT_RESTART
: EXCT_INIT
;
1445 ecc
.w
.wcontext
= C_ROOT
;
1446 exc
= exc_create_context(&ecc
, ECC_TYPE
| ECC_WCONTEXT
);
1448 defaults
[i
][0], defaults
[i
][1], defaults
[i
][2]);
1449 execute_function(NULL
, exc
, cmd
, 0);
1450 exc_destroy_context(exc
);
1452 #undef RC_DEFAULTS_COMPLETE
1457 static int CatchRedirectError(Display
*dpy
, XErrorEvent
*event
)
1459 fvwm_msg(ERR
, "CatchRedirectError", "another WM is running");
1462 /* to make insure happy */
1466 /* CatchFatal - Shuts down if the server connection is lost */
1467 static int CatchFatal(Display
*dpy
)
1469 /* No action is taken because usually this action is caused by someone
1470 using "xlogout" to be able to switch between multiple window managers
1475 /* to make insure happy */
1479 /* FvwmErrorHandler - displays info on internal errors */
1480 static int FvwmErrorHandler(Display
*dpy
, XErrorEvent
*event
)
1482 if (event
->error_code
== BadWindow
)
1484 bad_window
= event
->resourceid
;
1487 /* some errors are acceptable, mostly they're caused by
1488 * trying to update a lost window or free'ing another modules colors */
1489 if (event
->error_code
== BadWindow
||
1490 event
->request_code
== X_GetGeometry
||
1491 event
->error_code
== BadDrawable
||
1492 event
->request_code
== X_ConfigureWindow
||
1493 event
->request_code
== X_SetInputFocus
||
1494 event
->request_code
== X_GrabButton
||
1495 event
->request_code
== X_ChangeWindowAttributes
||
1496 event
->request_code
== X_InstallColormap
||
1497 event
->request_code
== X_FreePixmap
||
1498 event
->request_code
== X_FreeColors
)
1502 fvwm_msg(ERR
, "FvwmErrorHandler", "*** internal error ***");
1503 fvwm_msg(ERR
, "FvwmErrorHandler", "Request %d, Error %d, EventType: %d",
1504 event
->request_code
,
1511 /* ---------------------------- interface functions ------------------------ */
1513 /* Does initial window captures and runs init/restart function */
1514 void StartupStuff(void)
1516 #define start_func_name "StartFunction"
1517 const char *init_func_name
;
1518 const exec_context_t
*exc
;
1519 exec_context_changes_t ecc
;
1521 ecc
.type
= Restarting
? EXCT_RESTART
: EXCT_INIT
;
1522 ecc
.w
.wcontext
= C_ROOT
;
1523 exc
= exc_create_context(&ecc
, ECC_TYPE
| ECC_WCONTEXT
);
1524 CaptureAllWindows(exc
, False
);
1525 /* Turn off the SM stuff after the initial capture so that new windows
1526 * will not be matched by accident. */
1529 DisableRestoringState();
1531 /* Have to do this here too because preprocessor modules have not run
1532 * to the end when HandleEvents is entered from the main loop. */
1535 fFvwmInStartup
= False
;
1537 /* Make sure the geometry window uses the current font */
1538 resize_geometry_window();
1540 /* Make sure we have the correct click time now. */
1541 if (Scr
.ClickTime
< 0)
1543 Scr
.ClickTime
= -Scr
.ClickTime
;
1547 /* It is safe to ungrab here: if not, and one of the init functions
1548 * does not finish, we've got a complete freeze! */
1549 /* DV (15-Jul-2004): No, it is not safe to ungrab. If another
1550 * application grabs the pointer before execute_function gets it, the
1551 * start functions are not executed. And the pointer is grabbed
1552 * during function execution anyway, so releasing it here buys us
1554 UngrabEm(GRAB_STARTUP
);
1555 XUngrabPointer(dpy
, CurrentTime
);
1558 /* migo (04-Sep-1999): execute StartFunction */
1559 if (functions_is_complex_function(start_func_name
))
1561 char *action
= "Function " start_func_name
;
1563 execute_function(NULL
, exc
, action
, 0);
1566 /* migo (03-Jul-1999): execute [Session]{Init|Restart}Function */
1567 init_func_name
= get_init_function_name(Restarting
== True
);
1568 if (functions_is_complex_function(init_func_name
))
1570 char *action
= safestrdup(
1571 CatString2("Function ", init_func_name
));
1573 execute_function(NULL
, exc
, action
, 0);
1576 /* see comment above */
1577 UngrabEm(GRAB_STARTUP
);
1578 XUngrabPointer(dpy
, CurrentTime
);
1580 /* This should be done after the initialization is finished, since
1581 * it directly changes the global state. */
1582 LoadGlobalState(state_filename
);
1585 ** migo (20-Jun-1999): Remove state file after usage.
1586 ** migo (09-Jul-1999): but only on restart, otherwise it can be reused.
1590 unlink(state_filename
);
1592 exc_destroy_context(exc
);
1594 /* TA: 20091212: If we get here, we're done restarting, so reset the
1595 * flag back to False!
1602 /***********************************************************************
1604 * LoadDefaultButton -- loads default button # into button structure
1605 * assumes associated button memory is already free
1607 ************************************************************************/
1608 void LoadDefaultButton(DecorFace
*df
, int i
)
1612 LoadDefaultRightButton(df
, i
/ 2);
1616 LoadDefaultLeftButton(df
, i
/ 2);
1622 /***********************************************************************
1624 * ResetOrDestroyAllButtons -- resets all buttons to defaults
1625 * destroys existing buttons
1627 ************************************************************************/
1628 void DestroyAllButtons(FvwmDecor
*decor
)
1635 for (tbp
= decor
->buttons
, i
= 0; i
< NUMBER_OF_TITLE_BUTTONS
;
1638 for (j
= 0, face
= TB_STATE(*tbp
); j
< BS_MaxButtonState
;
1641 FreeDecorFace(dpy
, face
);
1648 void ResetAllButtons(FvwmDecor
*decor
)
1655 DestroyAllButtons(decor
);
1656 for (tbp
= decor
->buttons
, i
= 0; i
< NUMBER_OF_TITLE_BUTTONS
;
1659 memset(&TB_FLAGS(*tbp
), 0, sizeof(TB_FLAGS(*tbp
)));
1660 TB_JUSTIFICATION(*tbp
) = JUST_CENTER
;
1661 for (face
= TB_STATE(*tbp
), j
= 0; j
< BS_MaxButtonState
;
1664 LoadDefaultButton(face
, i
);
1668 /* standard MWM decoration hint assignments (veliaa@rpi.edu)
1669 [Menu] - Title Bar - [Minimize] [Maximize] */
1670 TB_MWM_DECOR_FLAGS(decor
->buttons
[0]) |= MWM_DECOR_MENU
;
1671 TB_MWM_DECOR_FLAGS(decor
->buttons
[1]) |= MWM_DECOR_MAXIMIZE
;
1672 TB_MWM_DECOR_FLAGS(decor
->buttons
[3]) |= MWM_DECOR_MINIMIZE
;
1677 void SetMWM_INFO(Window window
)
1682 /* prop[0]: flags */
1685 static char set_yorn
='n';
1692 if (Scr
.bo
.is_modality_evil
)
1694 /* Set Motif WM_INFO atom to make motif relinquish
1695 * broken handling of modal dialogs */
1696 motif_wm_info
.props
[0] = 2;
1697 motif_wm_info
.props
[1] = window
;
1699 dpy
,Scr
.Root
, _XA_MOTIF_WM
, _XA_MOTIF_WM
,32,
1700 PropModeReplace
, (unsigned char *)&motif_wm_info
, 2);
1708 * set_init_function_name - sets one of the init, restart or exit function names
1709 * get_init_function_name - gets one of the init, restart or exit function names
1711 * First parameter defines a function type: 0 - init, 1 - restart, 2 - exit.
1713 void set_init_function_name(int n
, const char *name
)
1715 init_function_names
[n
>= 0 && n
< 3? n
: 3] = name
;
1720 const char *get_init_function_name(int n
)
1722 return init_function_names
[n
>= 0 && n
< 3? n
: 3];
1725 #ifndef _PATH_DEVNULL
1726 # define _PATH_DEVNULL "/dev/null"
1728 static void reopen_fd(int fd
, char* mode
, FILE *of
)
1735 rc
= fstat(fd
, &sbuf
);
1740 else if (errno
!= EBADF
)
1744 f
= freopen(_PATH_DEVNULL
, mode
, of
);
1745 if (f
== 0 || fileno(f
) != fd
)
1753 /***********************************************************************
1756 * main - start of fvwm
1758 ***********************************************************************/
1760 int main(int argc
, char **argv
)
1762 unsigned long valuemask
;
1763 XSetWindowAttributes attributes
;
1766 char *display_string
;
1767 Bool do_force_single_screen
= False
;
1768 int single_screen_num
= -1;
1769 Bool replace_wm
= False
;
1770 int visualClass
= -1;
1772 PictureColorLimitOption colorLimitop
= {-1, -1, -1, -1, -1};
1773 const exec_context_t
*exc
;
1774 exec_context_changes_t ecc
;
1776 DBUG("main", "Entered, about to parse args");
1778 fvwmlib_init_max_fd();
1779 /* close open fds */
1780 for (i
= 3; i
< fvwmlib_max_fd
; i
++)
1784 /* reopen stdin, stdout and stderr if necessary */
1785 reopen_fd(0, "rb", stdin
);
1786 reopen_fd(1, "wb", stdout
);
1787 reopen_fd(2, "wb", stderr
);
1789 memset(&Scr
, 0, sizeof(Scr
));
1790 /* for use on restart */
1791 g_argv
= (char **)safemalloc((argc
+ 4) * sizeof(char *));
1793 for (i
= 0; i
< argc
; i
++)
1795 g_argv
[i
] = argv
[i
];
1797 g_argv
[g_argc
] = NULL
;
1799 FlocaleInit(LC_CTYPE
, "", "", "fvwm");
1800 FGettextInit("fvwm", LOCALEDIR
, "fvwm");
1803 /* Put the default module directory into the environment so it can be
1804 * used later by the config file, etc. */
1805 flib_putenv("FVWM_MODULEDIR", "FVWM_MODULEDIR=" FVWM_MODULEDIR
);
1807 /* Figure out user's home directory */
1808 home_dir
= getenv("HOME");
1809 #ifdef HAVE_GETPWUID
1810 if (home_dir
== NULL
)
1812 struct passwd
* pw
= getpwuid(getuid());
1815 home_dir
= safestrdup(pw
->pw_dir
);
1819 if (home_dir
== NULL
)
1821 home_dir
= "/"; /* give up and use root dir */
1824 /* Figure out where to read and write user's data files. */
1825 fvwm_userdir
= getenv("FVWM_USERDIR");
1826 if (fvwm_userdir
== NULL
)
1830 fvwm_userdir
= safestrdup(CatString2(home_dir
, "/.fvwm"));
1831 /* Put the user directory into the environment so it can be used
1832 * later everywhere. */
1833 s
= safestrdup(CatString2("FVWM_USERDIR=", fvwm_userdir
));
1834 flib_putenv("FVWM_USERDIR", s
);
1838 /* Create FVWM_USERDIR directory if needed */
1839 if (access(fvwm_userdir
, F_OK
) != 0)
1841 mkdir(fvwm_userdir
, 0777);
1843 if (access(fvwm_userdir
, W_OK
) != 0)
1846 ERR
, "main", "No write permissions in `%s/'.\n",
1850 for (i
= 1; i
< argc
; i
++)
1852 if (strcmp(argv
[i
], "-debug_stack_ring") == 0 ||
1853 strcmp(argv
[i
], "--debug-stack-ring") == 0)
1855 debugging_stack_ring
= True
;
1857 else if (strcmp(argv
[i
], "-D") == 0 ||
1858 strcmp(argv
[i
], "-debug") == 0 ||
1859 strcmp(argv
[i
], "--debug") == 0)
1863 else if (strcmp(argv
[i
], "-i") == 0 ||
1864 strcmp(argv
[i
], "-clientid") == 0 ||
1865 strcmp(argv
[i
], "--clientid") == 0 ||
1866 strcmp(argv
[i
], "-clientId") == 0 ||
1867 strcmp(argv
[i
], "--clientId") == 0)
1874 SetClientID(argv
[i
]);
1876 else if (strcmp(argv
[i
], "-F") == 0 ||
1877 strcmp(argv
[i
], "-restore") == 0 ||
1878 strcmp(argv
[i
], "--restore") == 0)
1885 state_filename
= argv
[i
];
1887 else if (strcmp(argv
[i
], "-s") == 0 ||
1888 strcmp(argv
[i
], "-single-screen") == 0 ||
1889 strcmp(argv
[i
], "--single-screen") == 0)
1891 do_force_single_screen
= True
;
1892 if (i
+1 < argc
&& argv
[i
+1][0] != '-')
1895 if (sscanf(argv
[i
], "%d", &single_screen_num
) ==
1903 else if (strcmp(argv
[i
], "-d") == 0 ||
1904 strcmp(argv
[i
], "-display") == 0 ||
1905 strcmp(argv
[i
], "--display") == 0)
1912 display_name
= argv
[i
];
1914 else if (strcmp(argv
[i
], "-f") == 0)
1921 if (num_config_commands
< MAX_CFG_CMDS
)
1923 config_commands
[num_config_commands
] =
1924 (char *)malloc(6+strlen(argv
[i
]));
1925 strcpy(config_commands
[num_config_commands
],
1927 strcat(config_commands
[num_config_commands
],
1929 num_config_commands
++;
1935 "only %d -f and -cmd parms allowed!",
1939 else if (strcmp(argv
[i
], "-c") == 0 ||
1940 strcmp(argv
[i
], "-cmd") == 0 ||
1941 strcmp(argv
[i
], "--cmd") == 0)
1948 if (num_config_commands
< MAX_CFG_CMDS
)
1950 config_commands
[num_config_commands
] =
1951 safestrdup(argv
[i
]);
1952 num_config_commands
++;
1958 "only %d -f and -cmd parms allowed!",
1962 else if (strcmp(argv
[i
], "-h") == 0 ||
1963 strcmp(argv
[i
], "-?") == 0 ||
1964 strcmp(argv
[i
], "--help") == 0)
1969 else if (strcmp(argv
[i
], "-blackout") == 0)
1971 /* obsolete option */
1974 "The -blackout option is obsolete, it will be "
1977 else if (strcmp(argv
[i
], "-r") == 0 ||
1978 strcmp(argv
[i
], "-replace") == 0 ||
1979 strcmp(argv
[i
], "--replace") == 0)
1983 /* check for visualId before visual to remove ambiguity */
1984 else if (strcmp(argv
[i
], "-I") == 0 ||
1985 strcmp(argv
[i
], "-visualid") == 0 ||
1986 strcmp(argv
[i
], "--visualid") == 0 ||
1987 strcmp(argv
[i
], "-visualId") == 0 ||
1988 strcmp(argv
[i
], "--visualId") == 0)
1996 if (sscanf(argv
[i
], "0x%x", &visualId
) == 0)
1998 if (sscanf(argv
[i
], "%d", &visualId
) == 0)
2005 else if (strcmp(argv
[i
], "-C") == 0 ||
2006 strcmp(argv
[i
], "-visual") == 0 ||
2007 strcmp(argv
[i
], "--visual") == 0)
2015 if (strncasecmp(argv
[i
], "staticg", 7) == 0)
2017 visualClass
= StaticGray
;
2019 else if (strncasecmp(argv
[i
], "g", 1) == 0)
2021 visualClass
= GrayScale
;
2023 else if (strncasecmp(argv
[i
], "staticc", 7) == 0)
2025 visualClass
= StaticColor
;
2027 else if (strncasecmp(argv
[i
], "p", 1) == 0)
2029 visualClass
= PseudoColor
;
2031 else if (strncasecmp(argv
[i
], "t", 1) == 0)
2033 visualClass
= TrueColor
;
2035 else if (strncasecmp(argv
[i
], "d", 1) == 0)
2037 visualClass
= DirectColor
;
2045 else if (strcmp(argv
[i
], "-l") == 0 ||
2046 strcmp(argv
[i
], "-color-limit") == 0 ||
2047 strcmp(argv
[i
], "--color-limit") == 0)
2054 colorLimitop
.color_limit
= atoi(argv
[i
]);
2056 else if (strcmp(argv
[i
], "-L") == 0 ||
2057 strcmp(argv
[i
], "-strict-color-limit") == 0 ||
2058 strcmp(argv
[i
], "--strict-color-limit") == 0)
2060 colorLimitop
.strict
= True
;
2062 else if (strcmp(argv
[i
], "-A") == 0 ||
2063 strcmp(argv
[i
], "-allocate-palette") == 0 ||
2064 strcmp(argv
[i
], "--allocate-palette") == 0)
2066 colorLimitop
.allocate
= True
;
2068 else if (strcmp(argv
[i
], "-S") == 0 ||
2069 strcmp(argv
[i
], "-static-palette") == 0 ||
2070 strcmp(argv
[i
], "--static-palette") == 0)
2072 colorLimitop
.not_dynamic
= True
;
2074 else if (strcmp(argv
[i
], "-P") == 0 ||
2075 strcmp(argv
[i
], "-visual-palette") == 0 ||
2076 strcmp(argv
[i
], "--visual-palette") == 0)
2078 colorLimitop
.use_named_table
= True
;
2080 else if (strcmp(argv
[i
], "-V") == 0 ||
2081 strcmp(argv
[i
], "-version") == 0 ||
2082 strcmp(argv
[i
], "--version") == 0)
2084 printf("%s\n%s\n\n%s\n", Fvwm_VersionInfo
,
2085 Fvwm_SupportInfo
, Fvwm_LicenseInfo
);
2091 fprintf(stderr
, "invalid option -- %s\n", argv
[i
]);
2096 DBUG("main", "Done parsing args");
2098 DBUG("main", "Installing signal handlers");
2101 if (single_screen_num
>= 0)
2111 dn
= getenv("DISPLAY");
2115 /* should never happen ? */
2116 if (!(dpy
= XOpenDisplay(dn
)))
2119 ERR
, "main", "can't open display %s"
2120 "to get the default display",
2125 dn
= XDisplayString(dpy
);
2132 "main", "couldn't find default display (%s)",
2139 new_dn
= get_display_name(dn
, single_screen_num
);
2140 if (dpy
&& strcmp(new_dn
, dn
) == 0)
2142 /* allready opened */
2150 if (!dpy
&& !(Pdpy
= dpy
= XOpenDisplay(new_dn
)))
2154 "can't open display %s, single screen "
2155 "number %d maybe not correct",
2156 new_dn
, single_screen_num
);
2158 Scr
.screen
= single_screen_num
;
2159 Scr
.NumberOfScreens
= ScreenCount(dpy
);
2166 if(!(Pdpy
= dpy
= XOpenDisplay(display_name
)))
2169 ERR
, "main", "can't open display %s",
2170 XDisplayName(display_name
));
2173 Scr
.screen
= DefaultScreen(dpy
);
2174 Scr
.NumberOfScreens
= ScreenCount(dpy
);
2178 master_pid
= getpid();
2180 if (!do_force_single_screen
)
2186 dn
= XDisplayString(dpy
);
2187 for (i
=0;i
<Scr
.NumberOfScreens
;i
++)
2189 if (i
!= Scr
.screen
&& fork() == 0)
2192 new_dn
= get_display_name(dn
, myscreen
);
2193 Pdpy
= dpy
= XOpenDisplay(new_dn
);
2194 Scr
.screen
= myscreen
;
2195 Scr
.NumberOfScreens
= ScreenCount(dpy
);
2202 g_argv
[argc
++] = "-s";
2203 g_argv
[argc
] = NULL
;
2206 x_fd
= XConnectionNumber(dpy
);
2209 if (fcntl(x_fd
, F_SETFD
, 1) == -1)
2211 fvwm_msg(ERR
, "main", "close-on-exec failed");
2216 /* Add a DISPLAY entry to the environment, incase we were started
2217 * with fvwm -display term:0.0 */
2218 len
= strlen(XDisplayString(dpy
));
2219 display_string
= safemalloc(len
+10);
2220 sprintf(display_string
, "DISPLAY=%s",XDisplayString(dpy
));
2221 flib_putenv("DISPLAY", display_string
);
2222 /* Add a HOSTDISPLAY environment variable, which is the same as
2223 * DISPLAY, unless display = :0.0 or unix:0.0, in which case the full
2224 * host name will be used for ease in networking.
2226 if (strncmp(display_string
, "DISPLAY=:",9)==0)
2228 char client
[MAXHOSTNAME
], *rdisplay_string
;
2229 gethostname(client
,MAXHOSTNAME
);
2230 rdisplay_string
= safemalloc(len
+14 + strlen(client
));
2231 sprintf(rdisplay_string
, "HOSTDISPLAY=%s:%s", client
,
2232 &display_string
[9]);
2233 flib_putenv("HOSTDISPLAY", rdisplay_string
);
2234 free(rdisplay_string
);
2236 else if (strncmp(display_string
, "DISPLAY=unix:",13)==0)
2238 char client
[MAXHOSTNAME
], *rdisplay_string
;
2239 gethostname(client
,MAXHOSTNAME
);
2240 rdisplay_string
= safemalloc(len
+14 + strlen(client
));
2241 sprintf(rdisplay_string
, "HOSTDISPLAY=%s:%s", client
,
2242 &display_string
[13]);
2243 flib_putenv("HOSTDISPLAY", rdisplay_string
);
2244 free(rdisplay_string
);
2248 char *rdisplay_string
;
2249 rdisplay_string
= safemalloc(len
+14);
2250 sprintf(rdisplay_string
, "HOSTDISPLAY=%s",XDisplayString(dpy
));
2251 flib_putenv("HOSTDISPLAY", rdisplay_string
);
2252 free(rdisplay_string
);
2254 free(display_string
);
2256 Scr
.Root
= RootWindow(dpy
, Scr
.screen
);
2257 if (Scr
.Root
== None
)
2260 ERR
, "main", "Screen %d is not a valid screen",
2266 XVisualInfo
template, *vinfo
= NULL
;
2272 template.screen
= Scr
.screen
;
2273 if (visualClass
!= -1)
2275 template.class = visualClass
;
2276 vinfo
= XGetVisualInfo(dpy
,
2277 VisualScreenMask
|VisualClassMask
,
2281 fvwm_msg(ERR
, "main",
2282 "Cannot find visual class %d",
2286 else if (visualId
!= -1)
2288 template.visualid
= visualId
;
2289 vinfo
= XGetVisualInfo(dpy
,
2290 VisualScreenMask
|VisualIDMask
,
2294 fvwm_msg(ERR
, "main",
2295 "VisualId 0x%x is not valid ",
2300 /* visualID's are unique so there will only be one.
2301 Select the visualClass with the biggest depth */
2302 for (i
= 0; i
< total
; i
++)
2304 if (vinfo
[i
].depth
> Pdepth
)
2306 Pvisual
= vinfo
[i
].visual
;
2307 Pdepth
= vinfo
[i
].depth
;
2315 /* Detection of a card with 2 hardware colormaps (8+24) which
2316 * use depth 8 for the default. We can use our own depth 24
2317 * cmap without affecting other applications. */
2318 if (Pdepth
== 0 && DefaultDepth(dpy
, Scr
.screen
) <= 8)
2320 template.class = TrueColor
;
2321 vinfo
= XGetVisualInfo(
2322 dpy
, VisualScreenMask
|VisualClassMask
,
2325 for(i
= 0; i
<total
; i
++)
2327 if (Pdepth
< vinfo
[i
].depth
&&
2330 Pvisual
= vinfo
[i
].visual
;
2331 Pdepth
= vinfo
[i
].depth
;
2340 /* have to have a colormap for non-default visual windows */
2343 if (Pvisual
->class == DirectColor
)
2345 Pcmap
= XCreateColormap(
2346 dpy
, Scr
.Root
, Pvisual
, AllocAll
);
2350 Pcmap
= XCreateColormap(
2351 dpy
, Scr
.Root
, Pvisual
, AllocNone
);
2354 /* use default visuals if none found so far */
2357 Pvisual
= DefaultVisual(dpy
, Scr
.screen
);
2358 Pdepth
= DefaultDepth(dpy
, Scr
.screen
);
2359 Pcmap
= DefaultColormap(dpy
, Scr
.screen
);
2364 PictureSetupWhiteAndBlack();
2366 /* make a copy of the visual stuff so that XorPixmap can swap with root
2368 PictureSaveFvwmVisual();
2371 PUseDynamicColors
= 0;
2372 Scr
.ColorLimit
= PictureInitColors(
2373 PICTURE_CALLED_BY_FVWM
, True
, &colorLimitop
, True
, True
);
2378 Scr
.pscreen
= XScreenOfDisplay(dpy
, Scr
.screen
);
2379 Scr
.use_backing_store
= DoesBackingStore(Scr
.pscreen
);
2380 Scr
.flags
.do_save_under
= DoesSaveUnders(Scr
.pscreen
);
2382 InternUsefulAtoms();
2384 /* Make sure property priority colors is empty */
2385 XChangeProperty(dpy
, Scr
.Root
, _XA_MIT_PRIORITY_COLORS
,
2386 XA_CARDINAL
, 32, PropModeReplace
, NULL
, 0);
2388 Scr
.FvwmCursors
= CreateCursors(dpy
);
2389 XDefineCursor(dpy
, Scr
.Root
, Scr
.FvwmCursors
[CRS_ROOT
]);
2390 /* create a window which will accept the keyboard focus when no other
2391 * windows have it */
2392 /* do this before any RC parsing as some GC's are created from this
2393 * window rather than the root window */
2394 attributes
.event_mask
= XEVMASK_NOFOCUSW
;
2395 attributes
.override_redirect
= True
;
2396 attributes
.colormap
= Pcmap
;
2397 attributes
.cursor
= Scr
.FvwmCursors
[CRS_DEFAULT
];
2398 attributes
.background_pixmap
= None
;
2399 attributes
.border_pixel
= 0;
2400 Scr
.NoFocusWin
=XCreateWindow(
2401 dpy
, Scr
.Root
, -10, -10, 10, 10, 0, Pdepth
, InputOutput
,
2402 Pvisual
, CWEventMask
| CWOverrideRedirect
| CWColormap
|
2403 CWBackPixmap
| CWBorderPixel
| CWCursor
, &attributes
);
2404 XMapWindow(dpy
, Scr
.NoFocusWin
);
2405 SetMWM_INFO(Scr
.NoFocusWin
);
2406 FOCUS_SET(Scr
.NoFocusWin
);
2415 SetupICCCM2(replace_wm
);
2416 XSetIOErrorHandler(CatchFatal
);
2418 /* We need to catch any errors of XSelectInput on the root
2419 * window here. The event mask contains
2420 * SubstructureRedirectMask which can be acquired by exactly
2421 * one client (window manager). Synchronizing is necessary
2422 * here because Neither XSetErrorHandler nor XSelectInput
2423 * generate any protocol requests.
2426 XSetErrorHandler(CatchRedirectError
);
2427 XSelectInput(dpy
, Scr
.Root
, XEVMASK_ROOTW
);
2429 XSetErrorHandler(FvwmErrorHandler
);
2432 /* do not grab the pointer earlier because if fvwm exits with
2433 * the pointer grabbed while a different display is visible,
2434 * XFree 4.0 freezes. */
2435 Cursor cursor
= XCreateFontCursor(dpy
, XC_watch
);
2437 dpy
, Scr
.Root
, 0, 0, GrabModeAsync
, GrabModeAsync
,
2438 None
, cursor
, CurrentTime
);
2443 unsigned long nitems
, bytes_remain
;
2444 unsigned char *prop
;
2446 if (XGetWindowProperty(
2447 dpy
, Scr
.Root
, _XA_WM_DESKTOP
, 0L, 1L, True
,
2448 _XA_WM_DESKTOP
, &atype
, &aformat
, &nitems
,
2449 &bytes_remain
, &prop
) == Success
)
2454 /* do_force_single_screen = True; */
2458 restart_state_filename
= safestrdup(
2459 CatString3(fvwm_userdir
, "/.fs-restart-",
2460 getenv("HOSTDISPLAY")));
2461 if (!state_filename
&& Restarting
)
2463 state_filename
= restart_state_filename
;
2466 /* This should be done early enough to have the window states loaded
2467 * before the first call to AddWindow. */
2468 LoadWindowStates(state_filename
);
2471 if (visualClass
!= -1 || visualId
!= -1)
2473 /* this is so that menus use the (non-default) fvwm colormap */
2474 FW_W(&Scr
.FvwmRoot
) = Scr
.NoFocusWin
;
2475 Scr
.FvwmRoot
.number_cmap_windows
= 1;
2476 Scr
.FvwmRoot
.cmap_windows
= &Scr
.NoFocusWin
;
2478 InitEventHandlerJumpTable();
2481 XCreateBitmapFromData(dpy
,Scr
.Root
,g_bits
, g_width
,g_height
);
2485 DBUG("main", "Setting up rc file defaults...");
2487 flush_window_updates();
2488 simplify_style_list();
2490 DBUG("main", "Running config_commands...");
2491 ecc
.type
= Restarting
? EXCT_RESTART
: EXCT_INIT
;
2492 ecc
.w
.wcontext
= C_ROOT
;
2493 exc
= exc_create_context(&ecc
, ECC_TYPE
| ECC_WCONTEXT
);
2494 if (num_config_commands
> 0)
2497 for (i
= 0; i
< num_config_commands
; i
++)
2499 DoingCommandLine
= True
;
2500 execute_function(NULL
, exc
, config_commands
[i
], 0);
2501 free(config_commands
[i
]);
2503 DoingCommandLine
= False
;
2507 /* Run startup command file in these places (default prefix):
2509 * /usr/local/share/fvwm/config
2510 * and for compatibility:
2512 * /usr/local/share/fvwm/system.fvwm2rc
2513 * and for compatibility to be discontinued:
2515 * /usr/local/share/fvwm/.fvwm2rc
2516 * /usr/local/etc/system.fvwm2rc
2519 !run_command_file(CatString3(
2520 fvwm_userdir
, "/", FVWM_CONFIG
), exc
) &&
2521 !run_command_file(CatString3(
2522 FVWM_DATADIR
, "/", FVWM_CONFIG
), exc
) &&
2523 !run_command_file(CatString3(
2524 fvwm_userdir
, "/", FVWM2RC
), exc
) &&
2525 !run_command_file(CatString3(
2526 home_dir
, "/", FVWM2RC
), exc
) &&
2527 !run_command_file(CatString3(
2528 FVWM_DATADIR
, "/", FVWM2RC
), exc
) &&
2529 !run_command_file(CatString3(
2530 FVWM_DATADIR
, "/system", FVWM2RC
), exc
) &&
2531 !run_command_file(CatString3(
2532 FVWM_CONFDIR
, "/system", FVWM2RC
), exc
))
2535 ERR
, "main", "Cannot read startup config file,"
2536 " tried: \n\t%s/%s\n\t%s/%s\n\t%s/%s\n\t"
2537 "%s/%s\n\t%s/%s\n\t%s/system%s\n\t%s/system%s",
2538 fvwm_userdir
, FVWM_CONFIG
,
2539 FVWM_DATADIR
, FVWM_CONFIG
,
2540 fvwm_userdir
, FVWM2RC
,
2542 FVWM_DATADIR
, FVWM2RC
,
2543 FVWM_DATADIR
, FVWM2RC
,
2544 FVWM_CONFDIR
, FVWM2RC
);
2547 exc_destroy_context(exc
);
2549 DBUG("main", "Done running config_commands");
2553 Scr
.gray_pixmap
= XCreatePixmapFromBitmapData(
2554 dpy
, Scr
.NoFocusWin
, g_bits
, g_width
, g_height
,
2555 PictureBlackPixel(), PictureWhitePixel(), Pdepth
);
2556 Scr
.light_gray_pixmap
= XCreatePixmapFromBitmapData(
2557 dpy
, Scr
.NoFocusWin
, l_g_bits
, l_g_width
, l_g_height
,
2558 PictureBlackPixel(), PictureWhitePixel(), Pdepth
);
2559 Scr
.sticky_gray_pixmap
= XCreatePixmapFromBitmapData(
2560 dpy
, Scr
.NoFocusWin
, s_g_bits
, s_g_width
, s_g_height
,
2561 PictureBlackPixel(), PictureWhitePixel(), Pdepth
);
2564 attributes
.background_pixel
= Scr
.StdBack
;
2565 attributes
.colormap
= Pcmap
;
2566 attributes
.border_pixel
= 0;
2567 valuemask
= CWBackPixel
| CWColormap
| CWBorderPixel
;
2569 Scr
.SizeWindow
= XCreateWindow(
2570 dpy
, Scr
.Root
, 0, 0, 1, 1, 0, Pdepth
,
2571 InputOutput
, Pvisual
, valuemask
, &attributes
);
2572 resize_geometry_window();
2576 MyXUngrabServer(dpy
);
2577 CoerceEnterNotifyOnCurrentWindow();
2580 DBUG("main", "Entering HandleEvents loop...");
2583 switch (fvwmRunState
)
2586 Done(0, NULL
); /* does not return */
2589 Done(1, ""); /* does not return */
2592 DBUG("main", "Unknown fvwm run-state");