/* Copyright 1989 GROUPE BULL -- See license conditions in file COPYRIGHT
 * Copyright 1989 Massachusetts Institute of Technology
 */
/******************************\
* 			       *
* BULL WINDOW MANAGER for X11  *
* 			       *
* 	main body	       *
* 			       *
\******************************/

/* include */
#include	<stdio.h>
#include        <stdlib.h>
#include        <string.h>
#include	"EXTERN.h"
#include	"wool.h"
#include	"wl_atom.h"
#include	"wl_string.h"
#include	"wl_list.h"
#include	"wl_client.h"
#include 	"wl_func.h"
#include	"yacc.h"
#include "wl_number.h"
#include <X11/Xlib.h>
#include "wl_pixmap.h"
#include	"INTERN.h"
#include 	"gwm.h"
#include 	<signal.h> 
#include 	<ctype.h> 
#include <sys/time.h>
#ifdef NEED_SELECT_H
#include <sys/select.h>
#endif

/* external */

#ifdef NOXEHTYPE
typedef int (*XErrorHandler) ();
extern XErrorHandler XSetErrorHandler ();
#endif

#ifdef NO_GWM_LOG
#define GWM_log()
#endif
#ifndef GWM_LOG_ID
#define GWM_LOG_ID 0
#endif

/* a comment string to tell "what" is this binary (precompiled? etc...) 
 * e.g, in makefile: C_COMPILER= cc -DGWM_LOG_NAME='\"-D-alpha\"'
 */
#ifndef GWM_LOG_NAME
#define GWM_LOG_NAME ""
#endif

extern int XError();
extern int      InitWool();
extern Wob      LookUpWob();
extern          InitScreens();
extern Wob      NewClientWindow();
extern void     ScreenClose();
extern void	DecorateWindows();
extern char    *wool_fix_path();
extern 		NoXError();
extern GWMScreenContext RegisterScreen();
extern void GWM_BusErrorSignalHandler();
extern ClientWindow LookUpClient();
DECLARE_strchr;

/* local */

static char     err[] = "\007GWM:";
static int GWM_Decorate_one_window=0;
static int GWM_Decorate_all_windows=0;
static int GWM_is_initialized = 0;
static int GWM_RetryStart = 0;

/* routines */

int 		GWM_Initialize();


int gwm_stdin = 0, gwm_prompt = 0;


/*
 * Main body of GWM:
 * 	- parse arguments
 * 	- initialize X
 * 	- initialize Wool
 * 	- read user profile
 * 	- decorate all already present windows
 * 	- enter main loop: GWM_ProcessEvents
 */

int
main(argc, argv)
int             argc;
char           *argv[];
{
    malloc_init();			/* FIRST THING TO DO!!! */
#ifdef MONITOR
    moncontrol(0);			/* do not trace profile read */
#endif /* MONITOR */
    GWM_argc = argc;
    GWM_argv = argv;
    GWM_getopts(argc, argv);		/* options & open display */
    if (gwm_stdin && gwm_prompt) {
	printf("> ");
	fflush(stdout);
    }
    RegisterScreens();

    if (wool_error_occurred(GWM_Initialize))	/* Init wool and display */
	GWM_Abort("cannot initialize");

    if (wool_error_in_profile && !wool_continue_reading_on_error)
	GWM_Abort("error while reading profile");

    if (wool_error_occurred(InitScreens))	/* screen and syswd */
	GWM_Abort("cannot set up");

    if (wool_error_occurred(DecorateWindows) &&
	!wool_continue_reading_on_error)/* existing windows */
	GWM_Abort("cannot decorate windows");

    FOR_ALL_SCREENS {			/* call opening on all screens */
	SetTarget(Context -> rootWob);
	WOOL_send(WOOL_eval, Context -> rootWob -> opening,
		  (Context -> rootWob -> opening));
	decrease_reference(Context -> rootWob -> opening);
	Context -> rootWob -> opening = NULL;
    } END_OF_ALL_SCREENS;

    /* send signal if a process is waiting for gwm to come up */
    if (GWM_kill_pid) {
	kill(GWM_kill_pid, GWM_kill_pid_sig);
    }

    GWM_is_initialized = 1;
    GWM_log();
    if (set_wool_error_resume_point()) { /* wool_error will jump here */
	if (GWM_is_restarting || GWM_is_ending)
	    GWM_BusErrorSignalHandler(0); /* skip wool when error in ending */
	zrt_gc(0);
	if (GWM_window_being_decorated) {
	    Window w = GWM_window_being_decorated -> client;
	    ClientWindowClose(GWM_window_being_decorated);
	    GWM_window_being_decorated = 0;
	    XMapWindow(dpy, w);
	}
    }

#ifdef MONITOR
    moncontrol(1);			/* only trace main loop */
#endif /* MONITOR */
    GWM_ProcessEvents(1);		/* MAIN LOOP (never returns!) */
    return 0;
}

/* fatal init error handler */

GWM_Abort(message)
char * message;
{
    fprintf(stderr, "%s -- %s, aborting\n", err, message);
    exit(-1);
}


void read_stdin()
{
    static int bi, len, pn, is;
    static char *b = NULL;
    char buf[1024], c;
    int n, i;

    if (!b) {
	b = Malloc(len = 1024);
	pn = is = 0;
	strcpy(b, "(print ");
	bi = 7;
    }

    if ((n = read(0, buf, 1024)) > 0) {
	for (i = 0; i < n; i++) {
	    c = b[bi++] = buf[i];
	    if (c == '(' && !is) pn++;
	    else if (c == ')' && !is) pn--;
	    else if (c ==  '"') is ^= 1;
	    else if (c == '\n' && !pn && !is) {
		b[bi-1] = '\0';
		strcat(b, " \"\\n\")");
		wool_execute_string(b);
		bi = 7;
	    }
	    if (bi == len - 10)
		b = Realloc(b, len += 1024);
	}
	if (buf[i-1] == '\n' && gwm_prompt) {
	    if (pn)
		printf("%d> ", pn);
	    else
		printf("> ");
	    fflush(stdout);
	}
    }
}


/* The main loop of GWM:
 * 	- wait for an event
 * 	- look if it is for the hook (X window) of a wob
 * 	  (or the client of a window), then dispatch it to the concerned
 * 	  wob
 * 	- if it is a new window being mapped, decorate it
 * 	- the server is grabbed, redirect events to grabbing wob
 * 
 * If blocking is 1, the loop never ends, whereas if it is 0, it returns as
 * soons as the queue is empty (via the XPending call)
 */

GWM_ProcessEvents(blocking)
int	blocking;
{
    EventData       evt;
    Wob             wob;
    struct timeval to;
    fd_set fds;
    int s = 0;
    int local_zrt_size = zrt_size;

#ifdef DEBUG
    wob = 0;			/* entry point for dbx */
#endif /* DEBUG */

    if (gwm_stdin) {
	FD_ZERO(&fds);
	FD_SET(0, &fds);
	to.tv_sec = to.tv_usec = 0;
	s = select(1, &fds, NULL, NULL, &to);
    }
    while (blocking || XPending(dpy) || s > 0) {

	if (gwm_stdin && !XPending(dpy)) {
	    FD_ZERO(&fds);
	    FD_SET(0, &fds);
	    FD_SET(ConnectionNumber(dpy), &fds);
	    to.tv_sec = to.tv_usec = 0;
	    s = select(FD_SETSIZE, &fds, NULL, NULL, blocking ? NULL : &to);

	    if (s > 0 && FD_ISSET(0, &fds))
		read_stdin();
	    s = 0;
	    goto next_event;
	}

	XNextEvent(dpy, &evt);
	TraceDo('e',GWM_prettyprint_event("XEV: ", &evt, "\n"));

	if (EventProperties[evt.xany.type] & EPTime) {
	    Time newtime = *((Time *)(((char *)(&evt)) +
				      EventTimeFieldOffset[evt.xany.type]));
	    if (newtime) {		/* sometimes the X Server goofs up */
		GWMTime = newtime;
	    }
	}

	if (wob = LookUpWob(evt.xany.window)) {
	    if (GWM_ServerGrabbed) {
		if(event_is_grabbable(evt)) {
		    if (GWM_GrabChildEvents
			|| !IsAnAncestor(GWM_ServerGrabbed, wob))
			wob = GWM_ServerGrabbed;
		    WOOL_send(WOOL_process_event, wob, (wob, &evt));
		} else if (event_is_redirectable(evt)) {
		    if (wob ==  GWM_ServerGrabbed
			|| (!GWM_GrabChildEvents
			    && IsAnAncestor(GWM_ServerGrabbed, wob))
			|| IsNotGrabDiscarded(&evt)) {
			WOOL_send(WOOL_process_event, wob, (wob, &evt));
		    }
		} else {
		    WOOL_send(WOOL_process_event, wob, (wob, &evt));
		}
	    } else {
		WOOL_send(WOOL_process_event, wob, (wob, &evt));
	    }
	} else if (wob = (Wob) LookUpClient(evt.xany.window)) {
 		WOOL_send(WOOL_process_event, wob, (wob, &evt));
	} else if (evt.xany.type == MappingNotify) {
	    XRefreshKeyboardMapping(&evt.xmapping);
	} else if (evt.xany.type == ColormapNotify) {
	    Update_colormap_in_colormap_windows_list(&evt);
	} else if (MeterEventHandler(&evt)) {
	    ;
#ifdef DEBUG
	} else {
	    wool_printf("Received event %s", eventtype[evt.type]);
	    wool_printf(" for unknown window 0x%x\n", evt.xany.window);
#endif /* DEBUG */
	}
	if (event_is_key_or_button(evt))/* store last button */
	    bcopy(&evt, &GWM_LastEvent, sizeof(XEvent));

      next_event:
	zrt_gc(local_zrt_size);
	if (blocking)
	    dft_gc();
    }
}

#ifndef NO_GWM_LOG
/* sends an UDP packet to keep count of all GWM running, if you want to
 * sends your host name, or GWM_LOG_ID (string) if you dont want to send me
 * this info
 */

GWM_log()
{
    char buf[256];

    if (getenv("NO_GWM_LOG"))
	return;
    sprintf(buf, "%s%s STARTED", GWM_version_name, GWM_LOG_NAME);
#ifndef DEBUG
#endif /* DEBUG */
}
#endif /* !NO_GWM_LOG */

/*
 * Initialize GCs used in wool
 */

InitGC()
{
    XGCValues       gc_values;

    FOR_ALL_SCREENS {
	gc_values.graphics_exposures = False;
	gc_values.function = GXinvert;
	Context -> gc.Invert = XCreateGC(dpy, Context -> root,
			      GCGraphicsExposures | GCFunction, &gc_values);
	gc_values.foreground = Context -> pixel.Fore;
	gc_values.background = Context -> pixel.Back;
	gc_values.function = GXcopy;
	Context -> gc.Work = XCreateGC(dpy, Context -> root,
	     GCGraphicsExposures | GCForeground | GCBackground | GCFunction,
				       &gc_values);
	gc_values.ts_x_origin = 0;
	gc_values.ts_y_origin = 0;
	gc_values.function = GXcopy;
	gc_values.fill_style = FillStippled;
	Context -> gc.Set = XCreateGC(dpy, Context -> root,
			GCFunction | GCTileStipXOrigin | GCGraphicsExposures
			     | GCTileStipYOrigin | GCFillStyle, &gc_values);
	gc_values.ts_x_origin = 0;
	gc_values.ts_y_origin = 0;
	gc_values.function = GXcopy;
	gc_values.fill_style = FillTiled;
	Context -> gc.Tile = XCreateGC(dpy, Context -> root,
			GCFunction | GCTileStipXOrigin | GCGraphicsExposures
			     | GCTileStipYOrigin | GCFillStyle, &gc_values);
	gc_values.function = GXinvert;
	Context -> gc.Back = XCreateGC(dpy, Context -> root,
			      GCGraphicsExposures | GCFunction, &gc_values);
	gc_values.line_width = 0;
	gc_values.foreground = 0xfd;
	gc_values.function = GXxor;
	gc_values.subwindow_mode = IncludeInferiors;
	Context -> gc.Draw = XCreateGC(dpy, Context -> root,
	       GCGraphicsExposures | GCLineWidth | GCForeground | GCFunction
				       | GCSubwindowMode, &gc_values);
	MakeDefaultBitmap();
	gc_values.function = GXinvert;
	Context -> gc.Bitmap = XCreateGC(dpy, Context -> DefaultBitmap,
			      GCGraphicsExposures | GCFunction, &gc_values);
    }END_OF_ALL_SCREENS;
}

/*
 * GWM_Initialize
 * initialize:
 * 	X
 * 	Wool
 * 	internal variables
 */

int
GWM_Initialize()
{
    XSetWindowAttributes attributes;

    GWM_SignalsInit();
    XrmInitialize();
    SetUpDefaults();
    InitGC();
    if (InitWool() != OK)
	return FatalError;

    /* initialize wool_host_name */
    wool_hostname_get();

    /* a hack for compilers chocking on wl_event.c initialisations on
       offsets of structures instead of instance
       code provided by:
       Rodney McDuff <mcduff@newton.physics.uq.oz.au>
       */
#ifdef NO_STRUCTURE_OFFSETS
    /* assign nonzero values to EventTimeFieldOffset[]
       since apollo compiler complains at determining structure
       sizes at compile time */
    {
	XEvent dummy_evt_ptr;
#define XEventFieldOffset(evt_type, field) \
	(((char *)(&(dummy_evt_ptr.evt_type.field))) - \
	    ((char *)(&(dummy_evt_ptr.type))))
	    ;
	EventTimeFieldOffset[2] = XEventFieldOffset(xkey, time);
	EventTimeFieldOffset[3] = XEventFieldOffset(xkey, time);
	EventTimeFieldOffset[4] = XEventFieldOffset(xbutton, time);
	EventTimeFieldOffset[5] = XEventFieldOffset(xbutton, time);
	EventTimeFieldOffset[6] = XEventFieldOffset(xmotion, time);
	EventTimeFieldOffset[7] = XEventFieldOffset(xcrossing, time);
	EventTimeFieldOffset[8] = XEventFieldOffset(xcrossing, time);
	EventTimeFieldOffset[28] = XEventFieldOffset(xproperty, time);
	EventTimeFieldOffset[29] = XEventFieldOffset(xselectionclear, time);
	EventTimeFieldOffset[30] = XEventFieldOffset(xselectionrequest, time);
	EventTimeFieldOffset[31] = XEventFieldOffset(xselection, time);    
    }
#endif /* NO_STRUCTURE_OFFSETS */

    /* Manage Windows */
    attributes.event_mask = KeyPressMask | ButtonPressMask | ButtonReleaseMask
	| EnterWindowMask | LeaveWindowMask | FocusChangeMask
	| SubstructureRedirectMask;
    XSetErrorHandler(NoXError);
    XSync(dpy, False);
    FOR_ALL_SCREENS {
	ErrorStatus = 0;
	XChangeWindowAttributes(dpy, Context -> root,
				CWEventMask, &attributes);
	XSync(dpy, False);
	if (ErrorStatus && (!GWM_Decorate_all_windows)) {
	    fprintf(stderr,
		    "GWM: cannot get control of windows on screen #%d!\n%s\n",
		    Context -> screen,
		 "Maybe another window manager is running on the display?");
	    if (GWM_RetryStart) {
		fprintf(stderr, "Retrying... kill your other window manager!\n");
		do {
		    ErrorStatus = 0;
		    XChangeWindowAttributes(dpy, Context -> root,
					    CWEventMask, &attributes);
		    XSync(dpy, False);
		} while(ErrorStatus);
		fprintf(stderr, "OK for screen %d\n", Context -> screen);
	    } else {
		exit(1);
	    }
	}
    }END_OF_ALL_SCREENS;
    GWM_re_init_PointerRoot();
    XSetErrorHandler(XError);
    return OK;
}

/* re-init pointerroot */

GWM_re_init_PointerRoot()
{
    XSelectInput(dpy, Context -> root, 0);
    XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
}

/* CloseEverything
 * n = 	0 normal (end)
 * 	1 error (we can still print messages)
 * 	-1 fatal error (connection closed)
 */

#ifdef VOID_SIGNALS
void
#endif
GWM_end(n)
int n;
{
#ifdef MONITOR
    moncontrol(0);			/* do not trace ending code */
#endif /* MONITOR */
    if (n >= 0 && GWM_is_initialized) {
	XSetErrorHandler(NoXError);
	GWM_re_init_PointerRoot();
	XSync(dpy, True);
	if (!GWM_is_ending && Context -> rootWob) {
	    GWM_is_ending = 1;
	    wool_do_print_errors = 0;
	    FOR_ALL_SCREENS {
		SetTarget(Context -> rootWob);
		ScreenClose(Context -> rootWob);
	    }END_OF_ALL_SCREENS;
	}
	XCloseDisplay(dpy);
    }
    exit(n);
}

/* wool_end is calling GWM_end */
wool_end(n)
int n;
{
    GWM_end(n);
}

/* signals management
 */

static int GWM_in_fatal_error;

/* We try to handle bus errors gracefully, i.e. by trying to trigger a
 * wool_error and going on, but aborting if we get one more
 */

void
GWM_FatalSignalHandler(sig)
int	sig;
{
    if (!GWM_in_fatal_error) {
	GWM_in_fatal_error = 1;
	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
	XSync(dpy, True);
	if (wool_is_reading_file)
	    fprintf(stderr, "\n\"%s\": line %d:\n", wool_is_reading_file,
	    yylineno);
	wool_stack_dump(1);
	fprintf(stderr,
	    "\007\nGWM: Fatal error due to UNIX signal # %d -- Aborting\n",
	    sig);
    }
    exit(sig);
}

/* Try to handle bus errors as normal errors, execpt that:
 * 	- if 2 bus errors come within 1 second, abort.
 * 	- if we were ending, just end, (in case the end code is faulty)
 * 	- ditto for restarting
 */

void
GWM_BusErrorSignalHandler(sig)
int	sig;
{
    int             current_time;
    static int      aborting;

    if (GWM_Window_being_opened)
    	XMapWindow(dpy, GWM_Window_being_opened -> hook);

    if (GWM_is_restarting) {
	XCloseDisplay(dpy);
	execvp(GWM_is_restarting[0], GWM_is_restarting);
    }
    if (GWM_is_ending)
	GWM_end(-1);
    current_time = time(0);
    if (current_time == GWM_time_of_last_bus_error) {
	if (aborting) {
	    exit(-1);
	} else {
	    aborting = 1;
	    GWM_is_ending = 1;
	    fprintf(stderr,
		    "\007\nGWM: Internal error: bus error -- restarting\n");
	    XCloseDisplay(dpy);
	    execvp(GWM_argv[0], GWM_argv);
	}
    }
    else {
	GWM_time_of_last_bus_error = current_time;
#ifdef SYSV
	signal(sig, GWM_BusErrorSignalHandler);
#endif /* SYSV */
	wool_error(INTERNAL_ERROR, "bus error");
    }
}

GWM_SignalsInit()
{
    signal(SIGHUP, SIG_IGN);
    /* keep signals for dbx debugging */
#ifndef DEBUG
    signal(SIGINT, GWM_end);
    signal(SIGQUIT, GWM_end);
    signal(SIGTERM, GWM_end);
    signal(SIGFPE, GWM_FatalSignalHandler);
    signal(SIGILL, GWM_BusErrorSignalHandler);
#ifdef SIGBUS
    signal(SIGBUS, GWM_BusErrorSignalHandler);
#endif
    signal(SIGSEGV, GWM_BusErrorSignalHandler);
#ifdef SIGSYS
    signal(SIGSYS, GWM_FatalSignalHandler);
#endif
#endif /* DEBUG     */
}

/*
 * functions to process events in the queue
 */

WOOL_OBJECT
wool_process_masked_events(mask)
int	mask;		/* such as ExposureMask, SubstructureNotifyMask */
{
    EventData       evt;
    Wob             wob;

    SAVE_EVENT_CONTEXT;
    XSync(dpy, 0);
    while (XCheckMaskEvent(dpy, mask, &evt)) {
	if ((wob = LookUpWob(evt.xany.window))
	    || (wob = (Wob) LookUpClient(evt.xany.window))) {
	    WOOL_send(WOOL_process_event, wob, (wob, &evt));
	}
    }
    RESTORE_EVENT_CONTEXT;
    return NIL;
}

/* to remove unmapped events
   problem: argument w does not seem to be correctly passed, so use global
*/

static Window WindowUnmapped;

int
GWM_remove_unmap_event(d, evt, arg)
Display *d;
XEvent *evt;
XPointer arg;
{
    if (evt->xany.type == UnmapNotify
	&& evt->xunmap.window == WindowUnmapped)
	return 1;
    else
	return 0;
}

wool_process_unmap_events(w)
Window w;
{
    XEvent       evt;

    XSync(dpy, 0);
    WindowUnmapped = w;
    while (XCheckIfEvent(dpy, &evt, GWM_remove_unmap_event, (char *)w))
	;
}

WOOL_OBJECT
wool_process_exposes()
{
    return wool_process_masked_events(ExposureMask);
}

/*
 *  initialize X and Context
 */

GWM_OpenDisplay(display)
char           *display;
{
    GWM_Display = (char *) XDisplayName(display);
    if ((dpy = XOpenDisplay(display)) == NULL) {
	fprintf(stderr, "%s Cannot XOpenDisplay on %s\n",
		err, XDisplayName(display));
	exit(1);
    }
#ifdef SHAPE
    GWM_ShapeExtension = XShapeQueryExtension(dpy, &GWM_ShapeEventBase,
					      &GWM_ShapeErrorBase);
#endif /* SHAPE */
    if (GWM_Synchronize)
	XSynchronize(dpy, 1);
    GWM_ScreenCount = ScreenCount(dpy);
    wob_context = XUniqueContext();
    client_context = XUniqueContext();
    XSetErrorHandler(XError);
}

/* options handling
 */

char *
path_append(old_path, new_path)
char *old_path;
char *new_path;
{
    char *path;
    static int path_was_malloced;

    switch(*new_path){
    case '+': case '-':		/* append & prepend */
	if(path_was_malloced)
	    path = Realloc(old_path, strlen(old_path) + strlen(new_path) + 1);
	else
	    path = Malloc(strlen(old_path) + strlen(new_path) + 1);
	path_was_malloced = 1;
	strcpy(path, (*new_path == '+' ? old_path : new_path+1));
	strcat(path, ":");
	strcat(path, (*new_path == '+' ? new_path+1 : old_path));
	break;
    default:			/* replace */
	if(path_was_malloced)
            Free(old_path);
	path = new_path;
	path_was_malloced = 0;
    }
    return path;
}

usage()
{
    fprintf(stderr, "USAGE:   gwm OPTIONS [host:display]\n");
    fprintf(stderr, "OPTIONS:\n");	
    fprintf(stderr, "    -f file      use file instead of .gwmrc\n");
    fprintf(stderr, "    -p path      force GWMPATH\n");	
    fprintf(stderr, "    -p +path     appends to GWMPATH\n");	
    fprintf(stderr, "    -p -path     prepends to GWMPATH\n");	
    fprintf(stderr, "    -d display   manage display\n");
    fprintf(stderr, "    -x screens   do not manage screens e.g. -x3,2,5\n");
    fprintf(stderr, "    -k pid       send signal (def. SIGALRM) to pid when init is done\n");
    fprintf(stderr, "    -K signal    number of signal to send for -k option\n");
    fprintf(stderr, "    -1    manages only 1 screen, the default one\n");
    fprintf(stderr, "    -r    retries till it can manage screens\n");
    fprintf(stderr, "    -i    input focus remains PointerRoot\n");
    fprintf(stderr, "    -F    no freeze: never freeze server\n");
    fprintf(stderr, "    -t    to turn tracing on (as (trace 1))\n");	
    fprintf(stderr, "    -m    to map all windows on startup\n");	
    fprintf(stderr, "    -s    synchronize X calls\n");	
    fprintf(stderr, "    -q    quiet: do not print startup banner\n");
    fprintf(stderr, "    -a    asynchronously obeys button clicks\n");
    fprintf(stderr, "    -D    debug: does not abort on error in profile\n");
    fprintf(stderr, "    -W    decorate over another Window Manager\n");
    fprintf(stderr, "    -w window_id decorate ONLY one window\n");
    fprintf(stderr, "    -I    read WOOL commands from stdin\n");
    fprintf(stderr, "    -P    together with -I: print prompts\n");
#ifdef DEBUG
    fprintf(stderr, "    -T flags to trace some items:\n");
    fprintf(stderr, "          f  for fsm events\n");
    fprintf(stderr, "          e  for all X events\n");
    fprintf(stderr, "          l  for all file loads\n");
    fprintf(stderr, "          m  for malloc/frees\n");
#endif
    fprintf(stderr, "\nGWMPATH is %s\n", wool_path);
    fprintf(stderr, "built-in path was  %s\n", DEFAULT_WLPATH);
}

GWM_getopts(argc, argv)
int             argc;
char           *argv[];

{
    extern int      optind;
    extern char    *optarg;
    char 	   *display_name = 0;
    char            *s;
    int             c, errflag = 0;

    wool_user_profile_name = WOOL_USER_PROFILE;
    wool_text_extension = WOOL_TEXT_EXTENSION;

    /* initialize paths (put .:$HOME before built-ins) */
    wool_path = wool_fix_path(DEFAULT_WLPATH);
    if ((s = (char *) getenv(WLPROFILE_USER_VARIABLE)) && (s[0] != '\0'))
	wool_user_profile_name = s;	     
    if ((s = (char *) getenv(WLPATH_SHELL_VARIABLE)) && (s[0] != '\0'))
	wool_path = s;
    if ((s = (char *) getenv("GWM_MONOSCREEN")) && (s[0] != '\0'))
	GWM_Monoscreen = 1;

    while ((c = getopt(argc, argv, "mtsb:p:f:DFT:w:Wd:iax:1qrk:K:IP")) != EOF) {
	switch ((char) c) {
	case 'I':
	    gwm_stdin = 1;
	    break;
	case 'P':
	    gwm_prompt = 1;
	    break;
#ifdef USER_DEBUG
	case 't':		/* set trace level */
	    wool_tracing_on = 1;
	    break;
#endif /* USER_DEBUG */
#ifdef DEBUG
	case 'T':
	    GWM_Tracelevel = atoi(optarg);
	    WlTraceFlags = optarg;
/* Current traceflags:
 * l load files
 * e All X events received 
 * f fsm MATCH/REJECTS
 * m mallocs
 * p pixmap creation
 */
	    break;
#endif /* DEBUG */
	case 'i':
	    GWM_No_set_focus = 1;
	    break;
	case 'm':		/* map all windows found on startup */
	    GWM_Mapall = 1;
	    break;
	case 's':		/* no synchro in DEBUG mode */
	    GWM_Synchronize = 1;
	    break;
	case 'r':		/* retry on StructureRedirect */
	    GWM_RetryStart = 1;
	    break;
	case 'f':
	    wool_user_profile_name = optarg;
	    break;
	case 'p':
	    wool_path = path_append(wool_path, optarg);
	    break;
	case 'D':
	    wool_continue_reading_on_error = 1;
	    break;
	case 'F':
	    GWM_GrabServer = 0;
	    break;
	case 'w':
	    GWM_Decorate_one_window = atoi(optarg);
	    break;
	case 'W':
	    GWM_Decorate_all_windows = 1;
	    break;
	case 'd':
	    display_name = optarg;
	    break;
	case 'x':
	    GWM_ScreensNotManaged = optarg;
	    break;
	case 'a':
	    GWM_UserSynchronous = 0;
	    break;
	case '1':
	    GWM_Monoscreen = 1;
	    break;
	case 'q':
	    GWM_quiet = 1;
	    break;
	case 'k':
	    GWM_kill_pid = atoi(optarg);
	    break;
	case 'K':
	    GWM_kill_pid_sig = atoi(optarg);
	    break;
	case '?':		/* usage */
	default:
	    errflag++;
	    break;
	}
    }

    if (!GWM_kill_pid_sig)
	GWM_kill_pid_sig = SIGALRM;

    if(!GWM_quiet || errflag)
	GWM_print_banner();

    if (errflag) {
	usage();
	exit(1);
    }
    GWM_OpenDisplay(optind < argc ? argv[optind] : display_name);
}

/*
 * executes a function. Returns 1 on first wool_error, and aborts function.
 */

int
wool_error_occurred(function)
int	(*function)();
{
    if (set_wool_error_resume_point()) {
	return 1;
    }
    ASSERT(function);
    (*function)();
    return 0;
}

void
DecorateWindows()
/*
 * called to make wobs other all pre-existing windows
 */
{
    Window          dummywin, parent;	/* dummy parent */
    Window         *children;		/* list of root sons */
    unsigned int    nchildren;		/* number of children */
    ClientWindow    wob;
    int             i;

    FOR_ALL_SCREENS {
	if (XQueryTree(dpy, Context -> root, &dummywin, &parent,
		       &children, &nchildren)) {
	    SetTarget(Context -> rootWob);
	    GWM_ProcessingExistingWindows = 1;
	    for (i = 0; i < nchildren; i++) {
		if (wob = LookUpClient(children[i]))
		    ClientWindowInitialMap(wob);
		else if ((Mapped(children[i]) || GWM_Mapall)
		    && ((!GWM_Decorate_one_window)
			|| (GWM_Decorate_one_window == children[i]))) {
		    if (wob = (ClientWindow)
			DecorateWindow(children[i],
				       Context -> rootWob, 1, 1)) {
			ClientWindowInitialMap(wob);
		    }
		}
	    }
	    GWM_ProcessingExistingWindows = 0;
	    XFreeN(children);
	}
    }END_OF_ALL_SCREENS;
}

/* initialise managed screens */

InitScreens()
{
    FOR_ALL_SCREENS {
	SetTarget(Context -> rootWob);
	SetUpScreen(Context -> rootWob,
		    WOOL_send(WOOL_eval, WL_describe_screen_call,
			      (WL_describe_screen_call)));
	ScreenOpen(Context -> rootWob);
    }END_OF_ALL_SCREENS;
}

/* set up the list of managed screens: GWMManagedScreens */

RegisterScreens()   
{
    int             screen_num;
    int             number_of_managed_screens = 0;

    GWMManagedScreens = (GWMScreenContext *)
	Calloc(sizeof(GWMScreenContext), ScreenCount(dpy));

    /* parse list of non-managed screens and pokes them */
    mark_numbers_in_list(ScreenCount(dpy), GWM_ScreensNotManaged,
			 GWMManagedScreens);

    /* poke 1 in used screens, 0 in unused  and count them
     * manage only DefaultScreen if we are in Monoscreen mode
     */
    for (screen_num = 0; screen_num < ScreenCount(dpy); screen_num++) {
	if (GWMManagedScreens[screen_num]) {
	    GWMManagedScreens[screen_num] = NULL;
	} else if ((!GWM_Monoscreen) || screen_num == DefaultScreen(dpy)) {
	    GWMManagedScreens[screen_num] = (GWMScreenContext) 1;
	    number_of_managed_screens++;
	}
    }

    /* (abort if none left) */
    if (!number_of_managed_screens)
	GWM_Abort("no screen left to manage");

    /* allocates and registers the managed screens */
    for (screen_num = 0; screen_num < ScreenCount(dpy); screen_num++) {
	if (GWMManagedScreens[screen_num]) {
	    GWMManagedScreens[screen_num] = RegisterScreen(screen_num);
	}
    }
}


/* parses a comma-separated list of numbers (string) and pokes 1 in the list
 * if comprised beetween 0 and max_n
 */

mark_numbers_in_list(max_n, string, list)
int	max_n;
char   *string;
WOOL_OBJECT   *list;
{
    int             n;

    while (string && *string) {
	n = atoi(string);
	if (*string >= '0' && *string <= '9'
	    && n >= 0 && n < ScreenCount(dpy))
	    list[n] = (WOOL_OBJECT) 1;
	if (string = strchr(string, ','))
	    string++;
	else
	    return;
    }
}

