/****************************************************************************
 * This module is all original code 
 * by Rob Nation 
 * Copyright 1993, Robert Nation
 *     You may use this code for any purpose, as long as the original
 *     copyright remains in the source code and all documentation
 ****************************************************************************/

/***********************************************************************
 * fvwm - "F? Virtual Window Manager"
 ***********************************************************************/

#include <FVWMconfig.h>

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#include "fvwm.h"
#include "menus.h"
#include "misc.h"
#include "screen.h"
#include "parse.h"
#include "module.h"

#include <X11/Xproto.h>
#include <X11/Xatom.h>
/* need to get prototype for XrmUniqueQuark for XUniqueContext call */
#include <X11/Xresource.h>

#ifdef SHAPE
#include <X11/extensions/shape.h>
#endif	/* SHAPE */

#if defined (sparc) && defined (SVR4)
/* Solaris has sysinfo instead of gethostname.  */
#include <sys/systeminfo.h>
#endif

#define MAXHOSTNAME 255

#include <fvwm/version.h>

#include <X11/xpm.h>
#include "buttons.h"

int master_pid;			/* process number of 1st fvwm process */

ScreenInfo Scr;			/* structures for the screen */
Display *dpy;			/* which display are we talking to */

Window BlackoutWin = None;	/* window to hide window captures */

#ifdef FVWMRC
char *config_command = "Read " FVWMRC;
#else
char *config_command = "Read .fvwm95rc";
#endif

static Boolean free_config_command = False;

char *output_file = NULL;

XErrorHandler FvwmErrorHandler(Display *, XErrorEvent *);
XIOErrorHandler CatchFatal(Display *);
XErrorHandler CatchRedirectError(Display *, XErrorEvent *);
void newhandler(int sig);
void CreateCursors(void);
void ChildDied(int nonsense);
void SaveDesktopState(void);
void SetMWM_INFO(Window window);
void SetRCDefaults(void);
void StartupStuff(void);

XContext FvwmContext;		/* context for fvwm windows */
XContext MenuContext;		/* context for fvwm menus */

int    JunkX = 0, JunkY = 0;
Window JunkRoot, JunkChild;	/* junk window */
unsigned int JunkWidth, JunkHeight, JunkBW, JunkDepth, JunkMask;

Boolean debugging = False, PPosOverride, Blackout = False;

char **g_argv;
int  g_argc;

/* assorted gray bitmaps for decorative borders */

#define g_width  2
#define g_height 2
static char g_bits[] = { 0x02, 0x01 };

#define l_g_width  4
#define l_g_height 2
static char l_g_bits[] = { 0x08, 0x02 };

#define s_g_width  4
#define s_g_height 4
static char s_g_bits[] = { 0x01, 0x02, 0x04, 0x08 };

#ifdef SHAPE
int     ShapeEventBase, ShapeErrorBase;
Boolean ShapesSupported = False;
#endif

long isIconicState = 0;
extern XEvent Event;
Bool Restarting = False;
int  fd_width, x_fd;
char *display_name = NULL;

/***********************************************************************
 *
 *  Procedure:
 *	main - start of fvwm
 *
 ***********************************************************************/

int main(int argc, char **argv)
{
  unsigned long valuemask;		/* mask for create windows */
  XSetWindowAttributes attributes;	/* attributes for create windows */
  void InternUsefulAtoms(void);
  void InitVariables(void);
  int  i, len;
  extern int x_fd;
  char *display_string;
  char message[255];
  Bool single = False;
  Bool option_error = FALSE;
  MenuRoot *mr;

  g_argv = argv;
  g_argc = argc;

  OpenConsole();
  DBUG("main", "Entered, about to parse args");

  for (i = 1; i < argc; i++) {
    if (strncasecmp(argv[i], "-debug", 6) == 0) {
      debugging = True;
    } else if (strncasecmp(argv[i], "-s", 2) == 0) {
      single = True;
    } else if (strncasecmp(argv[i], "-d", 2) == 0) {
      if (++i >= argc)
	usage();
      display_name = argv[i];
    } else if (strncasecmp(argv[i], "-f", 2) == 0) {
      if (++i >= argc)
	usage();
      config_command = (char *) malloc(6 + strlen(argv[i]));
      strcpy(config_command, "Read ");
      strcat(config_command, argv[i]);
      free_config_command = True;
    } else if (strncasecmp(argv[i], "-cmd", 4) == 0) {
      if (++i >= argc)
	usage();
      config_command = argv[i];
    } else if (strncasecmp(argv[i], "-file", 5) == 0) {
      if (++i >= argc)
	usage();
      output_file = argv[i];
    } else if (strncasecmp(argv[i], "-h", 2) == 0) {
      usage();
      exit(0);
    } else if (strncasecmp(argv[i], "-blackout", 9) == 0) {
      Blackout = True;
    } else if (strncasecmp(argv[i], "-version", 8) == 0) {
      fvwm_msg(INFO, "main", "Fvwm95 Version %s compiled on %s at %s\n",
	       VERSION, __DATE__, __TIME__);
    } else {
      fvwm_msg(ERR, "main", "Unknown option: `%s'\n", argv[i]);
      option_error = TRUE;
    }
  }

  DBUG("main", "Done parsing args");

  if (option_error) {
    usage();
  }

  DBUG("main", "Installing signal handlers");

  newhandler(SIGINT);
  newhandler(SIGHUP);
  newhandler(SIGQUIT);
  newhandler(SIGTERM);
  signal(SIGUSR1, Restart);
  signal(SIGPIPE, DeadPipe);

  ReapChildren();

  if (!(dpy = XOpenDisplay(display_name))) {
    fvwm_msg(ERR, "main", "can't open display %s",
	     XDisplayName(display_name));
    exit(1);
  }
  Scr.screen = DefaultScreen(dpy);
  Scr.NumberOfScreens = ScreenCount(dpy);

  master_pid = getpid();

  if (!single) {
    int myscreen = 0;
    char *cp;

    strcpy(message, XDisplayString(dpy));

    XCloseDisplay(dpy);

    for (i = 1; i < Scr.NumberOfScreens; i++) {
      if (fork() == 0) {
	myscreen = i;
	break;
      }
    }
    /*
     * Truncate the string 'whatever:n.n' to 'whatever:n',
     * and then append the screen number.
     */
    cp = strchr(message, ':');
    if (cp != NULL) {
      cp = strchr(cp, '.');
      if (cp != NULL)
	*cp = '\0';		/* truncate at display part */
    }
    sprintf(message + strlen(message), ".%d", myscreen);
    dpy = XOpenDisplay(message);
    Scr.screen = myscreen;
    Scr.NumberOfScreens = ScreenCount(dpy);
  }

  x_fd = XConnectionNumber(dpy);
  fd_width = GetFdWidth();

  if (fcntl(x_fd, F_SETFD, 1) == -1) {
    fvwm_msg(ERR, "main", "close-on-exec failed");
    exit(1);
  }

  /*  Add a DISPLAY entry to the environment, in case we were started
   *  with fvwm -display term:0.0
   */
  len = strlen(XDisplayString(dpy));
  display_string = safemalloc(len + 10);
  sprintf(display_string, "DISPLAY=%s", XDisplayString(dpy));
  putenv(display_string);

  /* Add a HOSTDISPLAY environment variable, which is the same as
   * DISPLAY, unless display = :0.0 or unix:0.0, in which case the full
   * host name will be used for ease in networking . */

  /* Note: Can't free the rdisplay_string after putenv, because it
   * becomes part of the environment! */

  if (strncmp(display_string, "DISPLAY=:", 9) == 0) {
    char client[MAXHOSTNAME], *rdisplay_string;

    mygethostname(client, MAXHOSTNAME);
    rdisplay_string = safemalloc(len + 14 + strlen(client));
    sprintf(rdisplay_string, "HOSTDISPLAY=%s:%s", client,
	    &display_string[9]);
    putenv(rdisplay_string);
  } else if (strncmp(display_string, "DISPLAY=unix:", 13) == 0) {
    char client[MAXHOSTNAME], *rdisplay_string;

    mygethostname(client, MAXHOSTNAME);
    rdisplay_string = safemalloc(len + 14 + strlen(client));
    sprintf(rdisplay_string, "HOSTDISPLAY=%s:%s", client,
	    &display_string[13]);
    putenv(rdisplay_string);
  } else {
    char *rdisplay_string;

    rdisplay_string = safemalloc(len + 14);
    sprintf(rdisplay_string, "HOSTDISPLAY=%s", XDisplayString(dpy));
    putenv(rdisplay_string);
  }

  Scr.Root = RootWindow(dpy, Scr.screen);
  if (Scr.Root == None) {
    fvwm_msg(ERR, "main", "Screen %d is not a valid screen",
	     (char *) Scr.screen);
    exit(1);
  }

#ifdef SHAPE
  ShapesSupported =
      XShapeQueryExtension(dpy, &ShapeEventBase, &ShapeErrorBase);
#endif	/* SHAPE */

  InternUsefulAtoms();

  /* Make sure property priority colors is empty */
  XChangeProperty(dpy, Scr.Root, _XA_MIT_PRIORITY_COLORS,
		  XA_CARDINAL, 32, PropModeReplace, NULL, 0);

  XSetErrorHandler((XErrorHandler) CatchRedirectError);
  XSetIOErrorHandler((XIOErrorHandler) CatchFatal);

  XSelectInput(dpy, Scr.Root,
	       LeaveWindowMask | EnterWindowMask | PropertyChangeMask |
	       SubstructureRedirectMask | KeyPressMask |
	       SubstructureNotifyMask |
	       ButtonPressMask | ButtonReleaseMask);

  XSync(dpy, 0);

  XSetErrorHandler((XErrorHandler) FvwmErrorHandler);

  BlackoutScreen();

  CreateCursors();
  InitVariables();
  InitEventHandlerJumpTable();
  initModules();

  InitPictureCMap(dpy, Scr.Root);	/* for the pixmap cache... */

  Scr.gray_bitmap =
      XCreateBitmapFromData(dpy, Scr.Root, g_bits, g_width, g_height);

  DBUG("main", "Setting up rc file defaults...");
  SetRCDefaults();

  DBUG("main", "Running config_command...");
  ExecuteFunction(config_command, NULL, &Event, C_ROOT, -1);
  DBUG("main", "Done running config_command");

/*
  CaptureAllWindows();
  MakeMenus();
*/

#if 0	/* this seems to cause problems for FvwmCpp/M4 startup actually */
  /* if not a direct 'Read', we'll capture all windows here, in case cmd
     fails we'll still have defaults */
  if (strncasecmp(config_command, "Read", 4) != 0 &&
      strncasecmp(config_command, "PipeRead", 8) != 0) {
    /* so if cmd (FvwmM4/Cpp most likely) fails, we can still have
       borders & stuff... */
    StartupStuff();
  }
#endif	/* 0 */

  if (free_config_command) {
    free(config_command);
  }

  if (Scr.d_depth < 2) {
    Scr.gray_pixmap =
	XCreatePixmapFromBitmapData(dpy, Scr.Root, g_bits, g_width,
				    g_height, Scr.WinColors.fore,
				    Scr.WinColors.back, Scr.d_depth);
    Scr.light_gray_pixmap =
	XCreatePixmapFromBitmapData(dpy, Scr.Root, l_g_bits, l_g_width,
				    l_g_height, Scr.WinColors.fore,
				    Scr.WinColors.back, Scr.d_depth);
  }

  /* create a window which will accept the keyboard focus when no other 
     windows have it */
  attributes.event_mask = KeyPressMask | FocusChangeMask;
  attributes.override_redirect = True;
  Scr.NoFocusWin = XCreateWindow(dpy, Scr.Root, -10, -10, 10, 10, 0, 0,
				 InputOnly, CopyFromParent,
				 CWEventMask | CWOverrideRedirect,
				 &attributes);
  XMapWindow(dpy, Scr.NoFocusWin);

  SetMWM_INFO(Scr.NoFocusWin);

  XSetInputFocus(dpy, Scr.NoFocusWin, RevertToParent, CurrentTime);

  XSync(dpy, 0);
  if (debugging)
    XSynchronize(dpy, 1);

  Scr.SizeStringWidth = XTextWidth(Scr.StdFont.font,
				   " +8888 x +8888 ", 15);
  attributes.border_pixel = Scr.WinColors.fore;
  attributes.background_pixel = Scr.WinColors.back;
  attributes.bit_gravity = NorthWestGravity;
  attributes.save_under = True;
  valuemask = (CWBorderPixel | CWBackPixel | CWBitGravity | CWSaveUnder);

  /* create the window for coordinates */
  Scr.SizeWindow = XCreateWindow(dpy, Scr.Root,
				 Scr.MyDisplayWidth / 2 -
				 (Scr.SizeStringWidth +
				  SIZE_HINDENT * 2) / 2,
				 Scr.MyDisplayHeight / 2 -
				 (Scr.StdFont.height +
				  SIZE_VINDENT * 2) / 2,
				 (unsigned int) (Scr.SizeStringWidth +
						 SIZE_HINDENT * 2),
				 (unsigned int) (Scr.StdFont.height +
						 SIZE_VINDENT * 2),
				 (unsigned int) 0, 0,
				 (unsigned int) CopyFromParent,
				 (Visual *) CopyFromParent,
				 valuemask, &attributes);

#ifndef NON_VIRTUAL
  initPanFrames();
#endif

  XGrabServer(dpy);

#ifndef NON_VIRTUAL
  checkPanFrames();
#endif
  XUngrabServer(dpy);
  UnBlackoutScreen();
  DBUG("main", "Entering HandleEvents loop...");
  HandleEvents();
  DBUG("main", "Back from HandleEvents loop?  Exiting...");

  return 0;
}


/***********************************************************************
 *
 * StartupStuff
 *
 * Does initial window captures and runs init/restart function
 *
 ***********************************************************************/

void StartupStuff(void)
{
  MenuRoot *mr;

  CaptureAllWindows();
  MakeMenus();

  if (Restarting) {
    mr = FindPopup("RestartFunction");
    if (mr != NULL)
      ExecuteFunction("Function RestartFunction", NULL, &Event, C_ROOT, -1);
  } else {
    mr = FindPopup("InitFunction");
    if (mr != NULL)
      ExecuteFunction("Function InitFunction", NULL, &Event, C_ROOT, -1);
  }
}


/***********************************************************************
 *
 * SetRCDefaults
 *
 * Sets some initial style values & such
 *
 ***********************************************************************/

void SetRCDefaults()
{
  /* set up default colors, menus, fonts, etc... */
  ExecuteFunction("MenuFont fixed", NULL, &Event, C_ROOT, -1);
  ExecuteFunction("HilightColors white black", NULL, &Event, C_ROOT, -1);
  ExecuteFunction("StickyColors white grey", NULL, &Event, C_ROOT, -1);
  ExecuteFunction("DefaultColors black grey grey white", NULL, &Event, C_ROOT, -1);
  ExecuteFunction("MenuColors black grey white black", NULL, &Event, C_ROOT, -1);
  ExecuteFunction("XORValue 255", NULL, &Event, C_ROOT, -1);
  ExecuteFunction("IconFont fixed", NULL, &Event, C_ROOT, -1);
  ExecuteFunction("WindowFont fixed", NULL, &Event, C_ROOT, -1);
  ExecuteFunction("Style \"*\" ClickToFocus", NULL, &Event, C_ROOT, -1);
  ExecuteFunction("Style \"*\" DecorateTransient", NULL, &Event, C_ROOT, -1);
  ExecuteFunction("AddToMenu default_menu \"Default Menu\" Title", NULL, &Event, C_ROOT, -1);
  ExecuteFunction("+ \"Exit\" Quit", NULL, &Event, C_ROOT, -1);
  ExecuteFunction("Mouse 1 R N Popup default_menu", NULL, &Event, C_ROOT, -1);
}


/***********************************************************************
 *
 *  Procedure:
 *      CaptureAllWindows
 *
 *   Decorates all windows at start-up
 *
 ***********************************************************************/

void CaptureAllWindows(void)
{
  int i, j;
  unsigned int nchildren;
  Window root, parent, *children;

  PPosOverride = TRUE;

  if (!XQueryTree(dpy, Scr.Root, &root, &parent, &children, &nchildren))
    return;

  /*
   * weed out icon windows
   */
  for (i = 0; i < nchildren; i++) {
    if (children[i]) {
      XWMHints *wmhintsp = XGetWMHints(dpy, children[i]);
      if (wmhintsp) {
	if (wmhintsp->flags & IconWindowHint) {
	  for (j = 0; j < nchildren; j++) {
	    if (children[j] == wmhintsp->icon_window) {
	      children[j] = None;
	      break;
	    }
	  }
	}
	XFree((char *) wmhintsp);
      }
    }
  }

  /*
   * map all of the non-override windows
   */

  for (i = 0; i < nchildren; i++) {
    if (children[i] && MappedNotOverride(children[i])) {
      XUnmapWindow(dpy, children[i]);
      Event.xmaprequest.window = children[i];
      HandleMapRequestKeepRaised(BlackoutWin);
    }
  }

  isIconicState = DontCareState;

  if (nchildren > 0)
    XFree((char *) children);

  /* after the windows already on the screen are in place,
   * don't use PPosition */
  PPosOverride = FALSE;
  Scr.flags |= WindowsCaptured;
  KeepOnTop();

}


/***********************************************************************
 *
 *  Procedure:
 *	MappedNotOverride - checks to see if we should really
 *		put a fvwm frame on the window
 *
 *  Returned Value:
 *	TRUE	- go ahead and frame the window
 *	FALSE	- don't frame the window
 *
 *  Inputs:
 *	w	- the window to check
 *
 ***********************************************************************/

int MappedNotOverride(Window w)
{
  XWindowAttributes wa;
  Atom atype;
  int aformat;
  unsigned long nitems, bytes_remain;
  unsigned char *prop;

  isIconicState = DontCareState;

  if ((w == Scr.NoFocusWin) || (!XGetWindowAttributes(dpy, w, &wa)))
    return False;

  if (XGetWindowProperty(dpy, w, _XA_WM_STATE, 0L, 3L, False, _XA_WM_STATE,
			 &atype, &aformat, &nitems, &bytes_remain,
			 &prop) == Success) {
    if (prop != NULL) {
      isIconicState = *(long *) prop;
      XFree(prop);
    }
  }
  if (wa.override_redirect == True) {
    XSelectInput(dpy, w, FocusChangeMask);
  }
  return (((isIconicState == IconicState) || (wa.map_state != IsUnmapped))
	  && (wa.override_redirect != True));
}


/***********************************************************************
 *
 *  Procedure:
 *	InternUsefulAtoms:
 *            Dont really know what it does
 *
 ***********************************************************************/

Atom _XA_MIT_PRIORITY_COLORS;
Atom _XA_WM_CHANGE_STATE;
Atom _XA_WM_STATE;
Atom _XA_WM_COLORMAP_WINDOWS;
Atom _XA_WM_PROTOCOLS;
Atom _XA_WM_TAKE_FOCUS;
Atom _XA_WM_DELETE_WINDOW;
Atom _XA_WM_DESKTOP;
Atom _XA_MwmAtom;
Atom _XA_MOTIF_WM;

Atom _XA_OL_WIN_ATTR;
Atom _XA_OL_WT_BASE;
Atom _XA_OL_WT_CMD;
Atom _XA_OL_WT_HELP;
Atom _XA_OL_WT_NOTICE;
Atom _XA_OL_WT_OTHER;
Atom _XA_OL_DECOR_ADD;
Atom _XA_OL_DECOR_DEL;
Atom _XA_OL_DECOR_CLOSE;
Atom _XA_OL_DECOR_RESIZE;
Atom _XA_OL_DECOR_HEADER;
Atom _XA_OL_DECOR_ICON_NAME;

void InternUsefulAtoms(void)
{
  /* 
   * Create priority colors if necessary.
   */
  _XA_MIT_PRIORITY_COLORS =
      XInternAtom(dpy, "_MIT_PRIORITY_COLORS", False);
  _XA_WM_CHANGE_STATE = XInternAtom(dpy, "WM_CHANGE_STATE", False);
  _XA_WM_STATE = XInternAtom(dpy, "WM_STATE", False);
  _XA_WM_COLORMAP_WINDOWS = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
  _XA_WM_PROTOCOLS = XInternAtom(dpy, "WM_PROTOCOLS", False);
  _XA_WM_TAKE_FOCUS = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
  _XA_WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
  _XA_WM_DESKTOP = XInternAtom(dpy, "WM_DESKTOP", False);

  _XA_MwmAtom = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
  _XA_MOTIF_WM = XInternAtom(dpy, "_MOTIF_WM_INFO", False);

  _XA_OL_WIN_ATTR = XInternAtom(dpy, "_OL_WIN_ATTR", False);
  _XA_OL_WT_BASE = XInternAtom(dpy, "_OL_WT_BASE", False);
  _XA_OL_WT_CMD = XInternAtom(dpy, "_OL_WT_CMD", False);
  _XA_OL_WT_HELP = XInternAtom(dpy, "_OL_WT_HELP", False);
  _XA_OL_WT_NOTICE = XInternAtom(dpy, "_OL_WT_NOTICE", False);
  _XA_OL_WT_OTHER = XInternAtom(dpy, "_OL_WT_OTHER", False);
  _XA_OL_DECOR_ADD = XInternAtom(dpy, "_OL_DECOR_ADD", False);
  _XA_OL_DECOR_DEL = XInternAtom(dpy, "_OL_DECOR_DEL", False);
  _XA_OL_DECOR_CLOSE = XInternAtom(dpy, "_OL_DECOR_CLOSE", False);
  _XA_OL_DECOR_RESIZE = XInternAtom(dpy, "_OL_DECOR_RESIZE", False);
  _XA_OL_DECOR_HEADER = XInternAtom(dpy, "_OL_DECOR_HEADER", False);
  _XA_OL_DECOR_ICON_NAME = XInternAtom(dpy, "_OL_DECOR_ICON_NAME", False);

  return;
}


/***********************************************************************
 *
 *  Procedure:
 *	newhandler: Installs new signal handler
 *
 ************************************************************************/

void newhandler(int sig)
{
  if (signal(sig, SIG_IGN) != SIG_IGN)
    signal(sig, SigDone);
}


/************************************************************************
 *
 *  Restart on a signal
 *
 ************************************************************************/

void Restart(int nonsense)
{
  Done(1, *g_argv);
  SIGNAL_RETURN;
}


/***********************************************************************
 *
 *  Procedure:
 *	CreateCursors - Loads fvwm cursors
 *
 ***********************************************************************/

void CreateCursors(void)
{
  /* define cursors */
  Scr.FvwmCursors[POSITION] = XCreateFontCursor(dpy, XC_top_left_corner);
  Scr.FvwmCursors[DEFAULT] = XCreateFontCursor(dpy, XC_top_left_arrow);
  Scr.FvwmCursors[SYS] = XCreateFontCursor(dpy, XC_top_left_arrow); /*_hand2*/
  Scr.FvwmCursors[TITLE_CURSOR] =
      XCreateFontCursor(dpy, XC_top_left_arrow);
  Scr.FvwmCursors[MOVE] = XCreateFontCursor(dpy, XC_fleur);
  Scr.FvwmCursors[MENU] = XCreateFontCursor(dpy, XC_arrow);
  Scr.FvwmCursors[WAIT] = XCreateFontCursor(dpy, XC_watch);
  Scr.FvwmCursors[SELECT] = XCreateFontCursor(dpy, XC_dot);
  Scr.FvwmCursors[DESTROY] = XCreateFontCursor(dpy, XC_pirate);
  Scr.FvwmCursors[LEFT] = XCreateFontCursor(dpy, XC_left_side);
  Scr.FvwmCursors[RIGHT] = XCreateFontCursor(dpy, XC_right_side);
  Scr.FvwmCursors[TOP] = XCreateFontCursor(dpy, XC_top_side);
  Scr.FvwmCursors[BOTTOM] = XCreateFontCursor(dpy, XC_bottom_side);
  Scr.FvwmCursors[TOP_LEFT] = XCreateFontCursor(dpy, XC_top_left_corner);
  Scr.FvwmCursors[TOP_RIGHT] = XCreateFontCursor(dpy, XC_top_right_corner);
  Scr.FvwmCursors[BOTTOM_LEFT] =
      XCreateFontCursor(dpy, XC_bottom_left_corner);
  Scr.FvwmCursors[BOTTOM_RIGHT] =
      XCreateFontCursor(dpy, XC_bottom_right_corner);
}


/***********************************************************************
 *
 *  Procedure:
 *	InitVariables - initialize fvwm variables
 *
 ************************************************************************/

void InitVariables(void)
{
  FvwmContext = XUniqueContext();
  MenuContext = XUniqueContext();

  /* initialize some lists */
  Scr.AllBindings = NULL;
  Scr.AllMenus = NULL;
  Scr.TheList = NULL;

  Scr.DefaultIcon = NULL;

  Scr.d_depth = DefaultDepth(dpy, Scr.screen);
  Scr.FvwmRoot.w = Scr.Root;
  Scr.FvwmRoot.next = 0;

  /*  RBW - 05/15/1998 - 2 new fields to init - stacking order chain.  */
  Scr.FvwmRoot.stack_next = &Scr.FvwmRoot;
  Scr.FvwmRoot.stack_prev = &Scr.FvwmRoot;

  XGetWindowAttributes(dpy, Scr.Root, &(Scr.FvwmRoot.attr));
  Scr.root_pushes = 0;
  Scr.pushed_window = &Scr.FvwmRoot;
  Scr.FvwmRoot.number_cmap_windows = 0;

  /* create graphics contexts */
  CreateGCs();

  SetupButtons();		/* setup win-95 title bar buttons */

  Scr.MyDisplayWidth = DisplayWidth(dpy, Scr.screen);
  Scr.MyDisplayHeight = DisplayHeight(dpy, Scr.screen);

  Scr.NoBoundaryWidth = 1;
  Scr.BoundaryWidth = BOUNDARY_WIDTH;
  Scr.CornerWidth = CORNER_WIDTH;
  Scr.Hilite = NULL;
  Scr.Focus = NULL;
  Scr.Ungrabbed = NULL;

  Scr.StdFont.font = NULL;

#ifndef NON_VIRTUAL
  Scr.VxMax = 2 * Scr.MyDisplayWidth;
  Scr.VyMax = 2 * Scr.MyDisplayHeight;
#else
  Scr.VxMax = 0;
  Scr.VyMax = 0;
#endif
  Scr.Vx = Scr.Vy = 0;

  Scr.SizeWindow = None;

  /* Sets the current desktop number to zero */
  /* Multiple desks are available even in non-virtual
   * compilations */
  {
    Atom atype;
    int aformat;
    unsigned long nitems, bytes_remain;
    unsigned char *prop;

    Scr.CurrentDesk = 0;
    if ((XGetWindowProperty(dpy, Scr.Root, _XA_WM_DESKTOP, 0L, 1L, True,
			    _XA_WM_DESKTOP, &atype, &aformat, &nitems,
			    &bytes_remain, &prop)) == Success) {
      if (prop != NULL) {
	Restarting = True;
	Scr.CurrentDesk = *(unsigned long *) prop;
      }
    }
  }

  Scr.EdgeScrollX = Scr.EdgeScrollY = 100;
  Scr.ScrollResistance = Scr.MoveResistance = 0;
  Scr.OpaqueSize = 5;
  Scr.ClickTime = 150;
  Scr.ColormapFocus = COLORMAP_FOLLOWS_MOUSE;

  /* set major operating modes */
  Scr.NumBoxes = 0;

  Scr.randomx = Scr.randomy = 0;
  Scr.buttons2grab = 7;

  return;
}


/***********************************************************************
 *
 *  Procedure:
 *	Reborder - Removes fvwm border windows
 *
 ************************************************************************/

void Reborder(void)
{
  FvwmWindow *tmp;		/* temp fvwm window structure */

  /* put a border back around all windows */
  XGrabServer(dpy);

  InstallWindowColormaps(&Scr.FvwmRoot);	/* force reinstall */

  /*
     RBW - 05/15/1998
     Grab the last window and work backwards: preserve stacking order
     on restart.
  */
  for (tmp = Scr.FvwmRoot.stack_prev;
       tmp != &Scr.FvwmRoot;
       tmp = tmp->stack_prev) {
    RestoreWithdrawnLocation(tmp, True);
    XUnmapWindow(dpy, tmp->frame);
    XDestroyWindow(dpy, tmp->frame);
  }

  XUngrabServer(dpy);
  XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
  XSync(dpy, 0);
}


/***********************************************************************
 *
 *  Procedure:
 *	Done - cleanup and exit fvwm
 *
 ***********************************************************************/

void SigDone(int nonsense)
{
  Done(0, NULL);
  SIGNAL_RETURN;
}

void Done(int restart, char *command)
{
  MenuRoot *mr;

#ifndef NON_VIRTUAL
  MoveViewport(0, 0, False);
#endif

  mr = FindPopup("ExitFunction");
  if (mr != NULL)
    ExecuteFunction("Function ExitFunction", NULL, &Event, C_ROOT, -1);

  /* Close all my pipes */
  ClosePipes();

  Reborder();

  if (restart) {
    SaveDesktopState();		/* I wonder why ... */

    /* Really make sure that the connection is closed and cleared! */
    XSelectInput(dpy, Scr.Root, 0);
    XSync(dpy, 0);
    XCloseDisplay(dpy);

    {
      char *my_argv[10];
      int i, done, j;

      i = 0;
      j = 0;
      done = 0;
      while ((g_argv[j] != NULL) && (i < 8)) {
	if (strcmp(g_argv[j], "-s") != 0) {
	  my_argv[i] = g_argv[j];
	  i++;
	  j++;
	} else
	  j++;
      }
      if (strstr(command, "fvwm") != NULL)
	my_argv[i++] = "-s";
      while (i < 10)
	my_argv[i++] = NULL;

      /* really need to destroy all windows, explicitly,
       * not sleep, but this is adequate for now */
      sleep(1);
      ReapChildren();
      execvp(command, my_argv);
    }
    fvwm_msg(ERR, "Done", "Call of '%s' failed!!!!", command);
    execvp(g_argv[0], g_argv);	/* that _should_ work */
    fvwm_msg(ERR, "Done", "Call of '%s' failed!!!!", g_argv[0]);
  } else {
    XCloseDisplay(dpy);
    exit(0);
  }
}


XErrorHandler CatchRedirectError(Display *dpy, XErrorEvent *event)
{
  fvwm_msg(ERR, "CatchRedirectError", "another WM is running");
  exit(1);
}


/***********************************************************************
 *
 *  Procedure:
 *	CatchFatal - Shuts down if the server connection is lost
 *
 ***********************************************************************/

XIOErrorHandler CatchFatal(Display *dpy)
{
  /* No action is taken because usually this action is caused by someone
     using "xlogout" to be able to switch between multiple window managers
   */
#if 0
  Done(0, NULL);
#else
  ClosePipes();
  exit(0);
#endif
}


/***********************************************************************
 *
 *  Procedure:
 *	FvwmErrorHandler - displays info on internal errors
 *
 ***********************************************************************/

XErrorHandler FvwmErrorHandler(Display *dpy, XErrorEvent *event)
{
  extern int last_event_type;

  /* some errors are acceptable, mostly they're caused by 
   * trying to update a lost  window */
  if ((event->error_code == BadWindow)
      || (event->request_code == X_GetGeometry)
      || (event->error_code == BadDrawable)
      || (event->request_code == X_SetInputFocus)
      || (event->request_code == X_GrabButton)
      || (event->request_code == X_ChangeWindowAttributes)
      || (event->request_code == X_InstallColormap))
    return 0;


  fvwm_msg(ERR, "FvwmErrorHandler", "*** internal error ***");
  fvwm_msg(ERR, "FvwmErrorHandler", "Request %d, Error %d, EventType: %d",
	   event->request_code, event->error_code, last_event_type);
  return 0;
}

void usage(void)
{
#if 0
  fvwm_msg(INFO, "usage", "\nFvwm95 Version %s Usage:\n\n", VERSION);
  fvwm_msg(INFO, "usage",
	   "  %s [-d dpy] [-debug] [-f config_cmd] [-s] [-blackout] [-version] [-h]\n",
	   g_argv[0]);
#else
  fprintf(stderr, "\nFvwm95 Version %s Usage:\n\n", VERSION);
  fprintf(stderr,
	  "  %s [-d dpy] [-debug] [-f config_cmd] [-s] [-blackout] [-version] [-h]\n\n",
	  g_argv[0]);
#endif
}


/**************************************************************************
 *
 * Save Desktop State
 *
 **************************************************************************/

void SaveDesktopState()
{
  FvwmWindow *t;
  unsigned long data[1];

  for (t = Scr.FvwmRoot.next; t != NULL; t = t->next) {
    data[0] = (unsigned long) t->Desk;
    XChangeProperty(dpy, t->w, _XA_WM_DESKTOP, _XA_WM_DESKTOP, 32,
		    PropModeReplace, (unsigned char *) data, 1);
  }

  data[0] = (unsigned long) Scr.CurrentDesk;
  XChangeProperty(dpy, Scr.Root, _XA_WM_DESKTOP, _XA_WM_DESKTOP, 32,
		  PropModeReplace, (unsigned char *) data, 1);

  XSync(dpy, 0);
}


void SetMWM_INFO(Window window)
{
#ifdef MODALITY_IS_EVIL
  struct mwminfo {
    long flags;
    Window win;
  } motif_wm_info;

  /* Set Motif WM_INFO atom to make motif relinquish 
   * broken handling of modal dialogs */
  motif_wm_info.flags = 2;
  motif_wm_info.win = window;

  XChangeProperty(dpy, Scr.Root, _XA_MOTIF_WM, _XA_MOTIF_WM, 32,
		  PropModeReplace, (char *) &motif_wm_info, 2);
#endif
}

void BlackoutScreen()
{
  XSetWindowAttributes attributes;
  unsigned long valuemask;

  if (Blackout && (BlackoutWin == None) && !debugging) {
    DBUG("BlackoutScreen", "Blacking out screen during init...");
    /* blackout screen */
    attributes.border_pixel = BlackPixel(dpy, Scr.screen);
    attributes.background_pixel = BlackPixel(dpy, Scr.screen);
    attributes.bit_gravity = NorthWestGravity;
    attributes.override_redirect = True;	/* is override redirect needed? */
    valuemask = CWBorderPixel |
	CWBackPixel | CWBitGravity | CWOverrideRedirect;
    BlackoutWin = XCreateWindow(dpy, Scr.Root, 0, 0,
				DisplayWidth(dpy, Scr.screen),
				DisplayHeight(dpy, Scr.screen), 0,
				CopyFromParent,
				CopyFromParent, CopyFromParent,
				valuemask, &attributes);
    XMapWindow(dpy, BlackoutWin);
    XSync(dpy, 0);
  }
}

void UnBlackoutScreen()
{
  if (Blackout && (BlackoutWin != None) && !debugging) {
    DBUG("UnBlackoutScreen", "UnBlacking out screen");
    XDestroyWindow(dpy, BlackoutWin);	/* unblacken the screen */
    XSync(dpy, 0);
    BlackoutWin = None;
  }
}

SetupButtons()
{
  XWindowAttributes root_attr;
  XpmAttributes xpm_attributes;

  XGetWindowAttributes(dpy, Scr.Root, &root_attr);
  xpm_attributes.colormap = root_attr.colormap;
  xpm_attributes.closeness = 40000;
  xpm_attributes.valuemask =
      XpmSize | XpmReturnPixels | XpmColormap | XpmCloseness;

  if (XpmCreatePixmapFromData(dpy, Scr.Root, sysmenu_xpm,
			      &Scr.button_pixmap[0], &Scr.button_mask[0],
			      &xpm_attributes) != XpmSuccess) {
    fvwm_msg(ERR, "SetupButtons", "Can't make system menu button");
    return;
  }

  if (XpmCreatePixmapFromData(dpy, Scr.Root, close_xpm,
			      &Scr.button_pixmap[1], &Scr.button_mask[1],
			      &xpm_attributes) != XpmSuccess) {
    fvwm_msg(ERR, "SetupButtons", "Can't make close button");
    return;
  }

  if (XpmCreatePixmapFromData(dpy, Scr.Root, normalize_xpm,
			      &Scr.button_pixmap[2], &Scr.button_mask[2],
			      &xpm_attributes) != XpmSuccess) {
    fvwm_msg(ERR, "SetupButtons", "Can't make maximized-state button");
    return;
  }

  if (XpmCreatePixmapFromData(dpy, Scr.Root, maximize_xpm,
			      &Scr.button_pixmap[3], &Scr.button_mask[3],
			      &xpm_attributes) != XpmSuccess) {
    fvwm_msg(ERR, "SetupButtons", "Can't make maximize button");
    return;
  }

  if (XpmCreatePixmapFromData(dpy, Scr.Root, minimize_xpm,
			      &Scr.button_pixmap[5], &Scr.button_mask[5],
			      &xpm_attributes) != XpmSuccess) {
    fvwm_msg(ERR, "SetupButtons", "Can't make minimize button");
    return;
  }

  Scr.button_width = xpm_attributes.width + 3;
  Scr.button_height = xpm_attributes.height + 3;
}
