/*
 * main.cc
 *
 * Copyright (C) 1995-2001 Kenichi Kourai
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with qvwm; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xlocale.h>
#include <X11/Xresource.h>
#include <X11/Xproto.h>
#include <X11/xpm.h>
#ifdef USE_XSMP
#include <X11/SM/SMlib.h>
#endif
#ifdef USE_SHAPE
#include <X11/extensions/shape.h>
#endif
#include "misc.h"
#include "qvwm.h"
#include "fbutton.h"
#include "tbutton.h"
#include "taskbar.h"
#include "menu.h"
#include "util.h"
#include "event.h"
#include "parse.h"
#include "main.h"
#include "qvwmrc.h"
#include "icon.h"
#include "switcher.h"
#include "paging.h"
#include "pager.h"
#include "mini.h"
#include "timer.h"
#include "key.h"
#include "debug.h"
#include "ver.h"
#include "focus_mgr.h"
#include "desktop.h"
#include "dialog.h"
#include "indicator.h"
#include "mwm.h"
#include "gnome.h"
#include "info_display.h"
#include "pixmap_image.h"
#include "tooltip.h"
#include "accessory.h"
#include "startmenu.h"
#include "radio_button.h"
#ifdef USE_IMLIB
#include "extra_image.h"
#endif
#include "session.h"
#include "screen_saver.h"
#include "remote_cmd.h"
#include "function.h"
#include "bitmaps/logo16.xpm"
#include "bitmaps/logo32.xpm"
#include "bitmaps/cursor_arrow.xbm"
#include "bitmaps/csmask_arrow.xbm"
#include "bitmaps/cursor_size_h.xbm"
#include "bitmaps/csmask_size_h.xbm"
#include "bitmaps/cursor_size_v.xbm"
#include "bitmaps/csmask_size_v.xbm"
#include "bitmaps/cursor_size_l.xbm"
#include "bitmaps/csmask_size_l.xbm"
#include "bitmaps/cursor_size_r.xbm"
#include "bitmaps/csmask_size_r.xbm"
#include "bitmaps/cursor_busy.xbm"
#include "bitmaps/csmask_busy.xbm"
#include "bitmaps/cursor_move.xbm"
#include "bitmaps/csmask_move.xbm"
#include "bitmaps/cursor_wait.xbm"
#include "bitmaps/csmask_wait.xbm"
#include "bitmaps/cursor_no.xbm"
#include "bitmaps/csmask_no.xbm"

static char*	rcFileName = NULL;     /* Rc file name */
char*		displayName = NULL;    /* Display name */
Display*	display;               /* Display structure */
int		screen;                /* Default screen */
unsigned int	depth;                 /* Default depth */
Colormap	colormap;              /* Default colormap */
Window		root;                  /* Root window */
Qvwm*		rootQvwm;              /* Virtual qvwm for root window */
Rect		rcScreen;              /* Screen size (not include taskbar) */
GC		gc, gcXor, gcDash;
QvImage		*imgLogo, *imgLargeLogo;
Cursor		cursor[9];
XFontSet	fsDefault;
XFontSet	fsTitle, fsTaskbar, fsBoldTaskbar, fsIcon;
XFontSet	fsCtrlMenu, fsCascadeMenu, fsStartMenu, fsDialog;
XColor		black, white;
XColor		gray, darkGray, grey, darkGrey, blue, lightBlue, royalBlue;
XColor		yellow, lightYellow;
XColor		gray95, darkGray95, lightGray95, grey95, blue95, lightBlue95;
XColor		green95, yellow95;
Bool		synch = False;
Bool		shapeSupport;
char**          qvArgv;
Bool		start = False;
Bool restart = False;  // ignore [Startup] section
Bool noParse = False;  // not parse ~/.qvwmrc and system.qvwmrc
Event event;
FocusMgr focusMgr;
Desktop desktop;
Pixmap pixWallPaper;
int failCount = 0;
#ifdef USE_XSMP
char* previousId = NULL; // The client Id from the previous session
Session* session = NULL;
#endif
Bool enableTaskbar = True, enablePager = True;

/*
 * Internal atoms.
 */
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_WM_ICON_SIZE;
#ifdef USE_XSMP
Atom _XA_WM_WINDOW_ROLE;
Atom _XA_WM_CLIENT_LEADER;
Atom _XA_SM_CLIENT_ID;
#endif //USE_XSMP

Taskbar*	taskBar      = NULL;
StartMenu*	startMenu    = NULL;
TaskSwitcher*	taskSwitcher = NULL;
Paging*		paging;
Pager*		pager        = NULL;
Menu*		ctrlMenu;
ExitDialog*	exitDlg      = NULL;
ConfirmDialog*	confirmDlg   = NULL;
Timer*		timer;
InfoDisplay*    infoDisp;
#ifdef USE_SS
ScreenSaver*    scrSaver = NULL;
#endif
#ifdef ALLOW_RMTCMD
RemoteCommand*  remoteCmd = NULL;
#endif

/*
 * Local function prototype declaration.
 */
void AnalizeOptions(int argc, char** argv);
void Usage();
void DisplayVersion();
void DisplayConfig();
void InitQvwm();
void SetSignal();
void SetGC();
void SetColor();
void CreateLogoMarkPixmap();
void CreateCursor();
void CreateInternAtom();
int CatchRedirectError(Display* display, XErrorEvent *ev);
int CatchFatalError(Display *display);
int QvwmErrorHandler(Display* display, XErrorEvent* ev);
void TrapSignal(int sig);
void TrapChild(int sig);
void TrapAlarm(int sig);
void TrapHup(int sig);

int main(int argc, char** argv)
{
  qvArgv = argv;

  AnalizeOptions(argc, argv);

  /*
   * Open the display.
   */
  if ((display = XOpenDisplay(displayName)) == NULL) {
    QvwmError("Can't open display %s", XDisplayName(displayName));
    exit(1);
  }

  if (displayName)
    SetDisplayEnv(displayName);

  /*
   * Synchronize X events.
   */
  if (synch) {
    XSynchronize(display, True);
    QvwmError("Synchronize mode");
  }

  /*
   * Set system defaults.
   */
  screen = DefaultScreen(display);
  root = RootWindow(display, screen);
  depth = DefaultDepth(display, screen);
  colormap = DefaultColormap(display, screen);
  rcScreen = Rect(0, 0,
		  DisplayWidth(display, screen),
		  DisplayHeight(display, screen));

  /*
   * Select inputs for window manager.
   */
  XSetErrorHandler(CatchRedirectError);
  XSetIOErrorHandler((XIOErrorHandler)CatchFatalError);
  XSelectInput(display, root, LeaveWindowMask | EnterWindowMask |
	       PropertyChangeMask | SubstructureRedirectMask | KeyPressMask |
	       ButtonPressMask | ButtonReleaseMask | SubstructureNotifyMask);
  XSync(display, 0);
  XSetErrorHandler(NULL);
  
#ifdef USE_SHAPE
  int shapeErrorBase;

  shapeSupport = XShapeQueryExtension(display, &event.shapeEventBase,
				      &shapeErrorBase);
#endif

  SetSignal();

  /*
   * Cleanup leftovers from previous session (after restart)
   */
  if (restart)
    while (waitpid(-1, NULL, WNOHANG) > 0)
      ;

  /*
   * Set resources for qvwm.
   */
  CreateInternAtom();
  ShortCutKey::SetModifier();
  Indicator::Initialize();
  QvFunction::initialize();

  SetColor();
  SetGC();

  // used in ExecCommand for indicators
  timer = new Timer();

  scKey = new SCKeyTable();
  menuKey = new SCKeyTable();

  desktop.CreateTopWindow();

  ParseQvwmrc(rcFileName);

  if (XSupportsLocale() == False)
    QvwmError("X does not support the locale");

  InitQvwm();
  start = True;

  event.MainLoop();
}

/*
 * AnalizeOptions --
 *   Analize command line options.
 */
void AnalizeOptions(int argc, char** argv)
{
  for (int i = 1; i < argc; i++) {
    if (argv[i][0] == '-') {
      if (argv[i][1] == '-') {  // long-style options
	if (strcmp(&argv[i][2], "display") == 0)
	  displayName = argv[++i];
	else if (strcmp(&argv[i][2], "rcfile") == 0)
	  rcFileName = argv[++i];
	else if (strcmp(&argv[i][2], "noparse") == 0)
	  noParse = True;
	else if (strcmp(&argv[i][2], "restart") == 0)
	  restart = True;
#ifdef USE_XSMP
	else if (strcmp(&argv[i][2], "clientid") == 0)
	  previousId = argv[++i];
#endif
	else if (strcmp(&argv[i][2], "synch") == 0)
	  synch = True;
	else if (strcmp(&argv[i][2], "version") == 0)
	  DisplayVersion();
	else if (strcmp(&argv[i][2], "config") == 0)
	  DisplayConfig();
	else if (strcmp(&argv[i][2], "help") == 0)
	  Usage();
	else {
	  printf("invalid option: %s\n", argv[i]);
	  Usage();
	}
      }
      else {
	if (strcmp(&argv[i][1], "d") == 0 ||
	    strcmp(&argv[i][1], "display") == 0)
	  displayName = argv[++i];
	else if (strcmp(&argv[i][1], "f") == 0)
	  rcFileName = argv[++i];
	else if (strcmp(&argv[i][1], "n") == 0)
	  noParse = True;
	else if (strcmp(&argv[i][1], "r") == 0)
	  restart = True;
#ifdef USE_XSMP
	else if (strcmp(&argv[i][1], "id") == 0 ||
		 strcmp(&argv[i][1], "clientId") == 0)
	  previousId = argv[++i];
#endif
	else if (strcmp(&argv[i][1], "s") == 0 ||
		 strcmp(&argv[i][1], "synch") == 0)
	  synch = True;
	else if (strcmp(&argv[i][1], "c") == 0)
	  failCount = atoi(argv[++i]);
	else if (strcmp(&argv[i][1], "v") == 0)
	  DisplayVersion();
	else if (strcmp(&argv[i][1], "cfg") == 0)
	  DisplayConfig();
	else
	  Usage();
      }
    }
    else
      Usage();
  }
}

void Usage()
{
  printf("Usage:\n");
  printf("  qvwm [-cfg] [-d display_name] [-f rcfile_name] [-id client_id] [-n] [-r]\n");
  printf("       [-s] [-v] [long-style options]\n");
  printf("Long-style options:\n");
  printf("  --clientid client_id    specify a client ID for session manager\n");
  printf("  --config                display compilation options\n");
  printf("  --display display_name  specify a display name\n");
  printf("  --help                  display this help\n");
  printf("  --noparse               parse no configuration files\n");
  printf("  --rcfile rcfile_name    specify a qvwmrc file\n");
  printf("  --restart               start without executing [Startup] section\n");
  printf("  --synch                 run qvwm in synchronized mode\n");
  printf("  --version               display version information\n");
  exit(0);
}

void DisplayVersion()
{
  printf("qvwm version %s (%s)\n", Version, Codename);
  printf("Copyright (C) 1995-2001 Kenichi Kourai.\n");
  printf("E-mail: kourai@qvwm.org\n");
  printf("URL: http://www.qvwm.org/\n");
  exit(0);
}

void DisplayConfig()
{
  printf("Config:\n");
  printf("  qvwm directory: %s\n", QVWMDIR);
  printf("  image directory: %s\n", IMGDIR);
  printf("  sound directory: %s\n", SNDDIR);
  printf("  options:");
#ifdef DEBUG
  printf(" [DEBUG]");
#endif
#ifdef USE_IMLIB
  printf(" [IMLIB]");
#endif
#ifdef USE_SHAPE
  printf(" [SHAPE]");
#endif
#ifdef USE_XSMP
  printf(" [XSMP]");
#endif
#ifdef USE_ALSA
  printf(" [ALSA]");
#endif
#ifdef USE_ESD
  printf(" [ESD]");
#endif
#ifdef USE_SS
  printf(" [SS]");
#ifdef USE_SSEXT
  printf(" [SSEXT]");
#endif
#endif /* USE_SS */
#ifdef ALLOW_RMTCMD
  printf(" [RMTCMD]");
#endif
  printf("\n");
  exit(0);
}

/*
 * Initialize qvwm.
 */
void InitQvwm()
{
  XSetErrorHandler(QvwmErrorHandler);

#ifdef USE_IMLIB
  ExtraImage::Initialize();
#endif
  Qvwm::Initialize();
  TaskbarButton::Initialize();
  Taskbar::Initialize();
  Menu::Initialize();
  Button::Initialize();
  Dialog::Initialize();
  TaskSwitcher::Initialize();
  Miniature::Initialize();
  Icon::Initialize();
  Tooltip::Initialize();
  FrameButton::Initialize();
  StartMenu::Initialize();
  RadioButton::Initialize();

  CreateLogoMarkPixmap();
  CreateCursor();
  if (GradTitlebar)
    CreateGradPattern();

  rootQvwm = new Qvwm(root);

  ctrlMenu = new Menu(CtrlMenuItem, fsCtrlMenu, NULL, NULL);

  Icon::ctrlMenu = new IconMenu(IconMenuItem, fsCtrlMenu, NULL, NULL);

  desktop.SetWallPaper();

#ifdef USE_SS
  // this must be before PlaySound() because it causes SIGCHILD
  scrSaver = new ScreenSaver(ScreenSaverProg, ScreenSaverDelay);
#endif

  if (restart)
    PlaySound(SystemRestartSound);
  else
    PlaySound(SystemStartSound);

  paging = new Paging(TopLeftPage, PagingSize);

  if (UsePager) {
    pager = new Pager(PagerGeometry);
    pager->MapPager();
  }
  
  infoDisp = new InfoDisplay();

#ifdef ALLOW_RMTCMD
  if (AllowRemoteCmd)
    remoteCmd = new RemoteCommand();
#endif

  Mwm::Init();
  Gnome::Init();
  Gnome::SetGnomeCompliant(desktop.GetTopWindow());

  taskBar->MoveTaskbar(TaskbarPosition);

#ifdef USE_XSMP
  session = new Session(previousId);
  session->LoadSessionFile();
#endif

  desktop.CaptureAllWindows();

  desktop.CreateIcons();
  desktop.RedrawAllIcons();

  desktop.CreateAccessories();

  if (UseTaskbar)
    taskBar->MapTaskbar();

  /*
   * Key bind for menu.
   */
  for (int i = 0; i < MenuKeyNum; i++)
    menuKey->AddSCKey(scMenuKey[i].sym, scMenuKey[i].mod, scMenuKey[i].func);
}

/*
 * SetSignal --
 *   Set signal handlers.
 */
void SetSignal()
{
  signal(SIGINT, TrapSignal);
  signal(SIGQUIT, TrapSignal);
  signal(SIGTERM, TrapSignal);
  signal(SIGSEGV, TrapSignal);
  signal(SIGBUS, TrapSignal);
  signal(SIGFPE, TrapSignal);
//#ifndef USE_XSMP
  signal(SIGCHLD, TrapChild);
//#endif
  signal(SIGHUP, TrapHup);
}

/*
 * SetColor --
 *   Allocate mininum colors for qvwm.
 */
void SetColor()
{
  if (CreateColor(0x0000, 0x0000, 0x0000, &black) == 0) {
    QvwmError("cannot allocate color: black");
    exit(1);
  }
  if (CreateColor(0xffff, 0xffff, 0xffff, &white) == 0) {
    QvwmError("cannot allocate color: white");
    exit(1);
  }

  CreateColor(0xd4d4, 0xd0d0, 0xc8c8, &gray, &white, "gray");
  CreateColor(0x8080, 0x8080, 0x8080, &darkGray, &black, "dark gray");
  CreateColor(0xc0c0, 0xc0c0, 0xc0c0, &grey, &gray, "grey");
  CreateColor(0x4040, 0x4040, 0x4040, &darkGrey, &black, "dark grey");
  CreateColor(0x0a0a, 0x2424, 0x6a6a, &blue, &black, "blue");
  CreateColor(0xa6a6, 0xcaca, 0xf0f0, &lightBlue, &blue, "light blue");
  CreateColor(0x3a3a, 0x6e6e, 0xa5a5, &royalBlue, &blue, "royal blue");
  CreateColor(0xf5f5, 0xdbdb, 0x9595, &yellow, &white, "yellow");
  CreateColor(0xffff, 0xffff, 0xe1e1, &lightYellow, &white, "lightYellow");

  // backward compatibility
  gray95.pixel = 0;
  darkGray95.pixel = 0;
  lightGray95.pixel = 0;
  grey95.pixel = 0;
  blue95.pixel = 0;
  lightBlue95.pixel = 0;
  green95.pixel = 0;
  yellow95.pixel = 0;

  IconForeColor = black;
  IconBackColor = white;
  IconStringColor = white;
  IconStringActiveColor = white;
  MiniatureColor = black;
  MiniatureActiveColor = white;
  TitlebarColor = darkGray;
  TitlebarColor2 = grey;
  TitlebarActiveColor = blue;
  TitlebarActiveColor2 = lightBlue;
  TitleStringColor = gray;
  TitleStringActiveColor = white;
  MenuColor = gray;
  MenuActiveColor = blue;
  MenuStringColor = black;
  MenuStringActiveColor = white;
  DialogColor = gray;
  DialogStringColor = black;
  SwitcherColor = gray;
  SwitcherActiveColor = blue;
  SwitcherStringColor = black;
  FrameColor = gray;
  FrameActiveColor = gray;
  PagerColor = gray;
  PagerActiveColor = darkGray;
  ButtonColor = gray;
  ButtonActiveColor = gray;
  ButtonStringColor = black;
  ButtonStringActiveColor = black;
  TaskbarColor = gray;
  ClockStringColor = black;
  DesktopColor = royalBlue;
  DesktopActiveColor = blue;
  StartMenuLogoColor = black;
  CursorColor = white;
  TooltipColor = lightYellow;
  TooltipStringColor = black;
}

/*
 * SetGC --
 *   Set GC for normal, xor and dash.
 */
void SetGC()
{
  XGCValues gcv;
  unsigned long gcm;
  char dash[] = {1, 1};
  
  gc = XCreateGC(display, root, 0, 0);

  gcv.function = GXxor;
  gcv.line_width = 4;
  gcv.foreground = darkGray.pixel;
  gcv.subwindow_mode = IncludeInferiors;
  gcm = GCFunction | GCLineWidth | GCForeground | GCSubwindowMode;
  gcXor = XCreateGC(display, root, gcm, &gcv);

  gcDash = XCreateGC(display, root, 0, 0);
  XSetDashes(display, gcDash, 0, dash, 2);
  XSetLineAttributes(display, gcDash, 1, LineOnOffDash, CapButt, JoinMiter);
}

/*
 * CreateCursor --
 *   Create cursors.
 */
void CreateCursor()
{
  Pixmap pixCursor, maskCursor;
  char* curbits[9] = { cursor_arrow_bits, cursor_size_h_bits,
		       cursor_size_v_bits, cursor_size_l_bits,
		       cursor_size_r_bits, cursor_busy_bits,
		       cursor_move_bits, cursor_wait_bits,
		       cursor_no_bits };
  char* maskbits[9] = { csmask_arrow_bits, csmask_size_h_bits,
			csmask_size_v_bits, csmask_size_l_bits,
			csmask_size_r_bits, csmask_busy_bits,
			csmask_move_bits, csmask_wait_bits,
			csmask_no_bits };
  Dim size[9] = { Dim(cursor_arrow_width, cursor_arrow_height),
		  Dim(cursor_size_h_width, cursor_size_h_height),
		  Dim(cursor_size_v_width, cursor_size_v_height),
		  Dim(cursor_size_l_width, cursor_size_l_height),
		  Dim(cursor_size_r_width, cursor_size_r_height),
		  Dim(cursor_busy_width, cursor_busy_height),
		  Dim(cursor_move_width, cursor_move_height),
		  Dim(cursor_wait_width, cursor_wait_height),
		  Dim(cursor_no_width, cursor_no_height) };
  Point spot[9] = { Point(10, 6), Point(15, 15), Point(14, 15),
		    Point(14, 15), Point(14, 14), Point(15, 15),
		    Point(16, 16), Point(4, 5), Point(17, 17) };

  for (int i = 0; i < 9; i++) {
    pixCursor = XCreateBitmapFromData(display, root, curbits[i],
				      size[i].width, size[i].height);
    maskCursor = XCreateBitmapFromData(display, root, maskbits[i],
				       size[i].width, size[i].height);
    cursor[i] = XCreatePixmapCursor(display, pixCursor, maskCursor,
				    &black, &CursorColor,
				    spot[i].x, spot[i].y);

    XFreePixmap(display, pixCursor);
    XFreePixmap(display, maskCursor);
  }

  XDefineCursor(display, root, cursor[SYS]);
}

/*
 * CreateInternAtom --
 *   Create internal atoms of window manager.
 */
void CreateInternAtom()
{
  _XA_WM_CHANGE_STATE = XInternAtom(display, "WM_CHANGE_STATE", False);
  _XA_WM_STATE = XInternAtom(display, "WM_STATE", False);
  _XA_WM_COLORMAP_WINDOWS = XInternAtom(display, "WM_COLORMAP_WINDOWS", False);
  _XA_WM_PROTOCOLS = XInternAtom(display, "WM_PROTOCOLS", False);
  _XA_WM_TAKE_FOCUS = XInternAtom(display, "WM_TAKE_FOCUS", False);
  _XA_WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", False);
  _XA_WM_DESKTOP = XInternAtom(display, "WM_DESKTOP", False);
  _XA_WM_ICON_SIZE = XInternAtom(display, "WM_ICON_SIZE", False);
#ifdef USE_XSMP
  _XA_WM_WINDOW_ROLE = XInternAtom(display, "WM_WINDOW_ROLE", False);
  _XA_WM_CLIENT_LEADER = XInternAtom(display, "WM_CLIENT_LEADER", False);
  _XA_SM_CLIENT_ID = XInternAtom(display, "SM_CLIENT_ID", False);
#endif
}

/*
 * CreateLogoMarkPixmap --
 *   Create pixmaps for logo mark.
 */
void CreateLogoMarkPixmap()
{
  if (DefaultIcon == NULL)
    imgLogo = new PixmapImage(logo16);
  else {
    imgLogo = CreateImageFromFile(DefaultIcon, timer);
    if (imgLogo == NULL)
      imgLogo = new PixmapImage(logo16);
  }

  if (DefaultLargeIcon == NULL)
    imgLargeLogo = new PixmapImage(logo32);
  else {
    imgLargeLogo = CreateImageFromFile(DefaultLargeIcon,
				       TaskSwitcher::swTimer);
    if (imgLargeLogo == NULL)
      imgLargeLogo = new PixmapImage(logo32);
  }
}  

/*
 * CatchRedirectError --
 *   Jump here when another window manager is running.
 */
int CatchRedirectError(Display* display, XErrorEvent *ev)
{
  QvwmError("Another window manager is running");
  exit(1);
}

/*
 * CatchFatalError --
 *   Jump here when an fatal I/O error happens in X server.
 */
int CatchFatalError(Display *display)
{
  XCloseDisplay(display);
  exit(1);
}

/*
 * QvwmErrorHandler --
 *   Jump here when an error happens in X server.
 */
int QvwmErrorHandler(Display *display, XErrorEvent* ev)
{
#ifndef DEBUG
  if (ev->error_code == BadWindow ||
      ev->error_code == BadDrawable || 
      ev->request_code == X_GetGeometry ||
      ev->request_code == X_GrabButton ||
      ev->request_code == X_SetInputFocus ||
      ev->request_code == X_ChangeWindowAttributes ||
      ev->request_code == X_InstallColormap)
    return 0;
#endif

  char msg[128], number[32], request[128];

  XGetErrorText(display, ev->error_code, msg, 128);
  sprintf(number, "%d", ev->request_code);
  XGetErrorDatabaseText(display, "XRequest", number, "", request, 128);
  if (*request == '\0')
    sprintf(request, "Unknown_Code_%d", ev->request_code);
  QvwmError("%s (0x%lx): %s", request, ev->resourceid, msg);

  return 0;
}

#define DEBUGGER "gdb"

/*
 * TrapSignal --
 *   General signal handler.
 */
void TrapSignal(int sig)
{
  if (sig == SIGSEGV || sig == SIGBUS) {
    if (sig == SIGSEGV) {
      signal(SIGSEGV, SIG_DFL);
      QvwmError("Segmentation fault");
    }
    else {
      signal(SIGBUS, SIG_DFL);
      QvwmError("Bus error");
    }
    
    if (UseDebugger) {
      Debug debug;
      QvwmError("Starting %s...", DEBUGGER);
      debug.TraceStack(DEBUGGER, qvArgv[0]);
    }

    // terminate accessory processes
    //   This is done in FinishQvwm, but it is not called from here...
    List<Accessory>::Iterator i(&desktop.GetAccList());
    for (Accessory* acc = i.GetHead(); acc; acc = i.Remove())
      delete acc;

    if (RestartOnFailure) {
      timer->ResetAllTimeouts();

      XSetInputFocus(display, root, RevertToNone, CurrentTime);
      if (start)
	paging->PagingProc(Point(0, 0));

      // Exit qvwm if failure coutinues
      if (failCount < MAX_FAILURE_COUNT) {
	QvwmError("Automatically restarting...");
	RestartQvwm(False, failCount + 1, False);
      }
      else {
	QvwmError("Failure count reaches maximum.  Start qvwm again.");
	XCloseDisplay(display);
	exit(1);
      }
    }
    else {
      XSetInputFocus(display, root, RevertToNone, CurrentTime);
      if (start)
	paging->PagingProc(Point(0, 0));
      XCloseDisplay(display);

      abort();
    }
  }

  FinishQvwm();
}

/*
 * TrapChild --
 *   Signal handler for child termination.
 */
void TrapChild(int sig)
{
  pid_t pid;

  while (1) {
    pid = waitpid(-1, NULL, WNOHANG);
    if (pid <= 0)
      break;
#ifdef USE_SS
    if (scrSaver && scrSaver->NotifyDeadPid(pid))
      continue;
#endif
    if (Indicator::NotifyDeadPid(pid))
      continue;
  }

  signal(SIGCHLD, TrapChild);
}

void TrapHup(int sig)
{
  RestartQvwm();
}

#ifdef HAVE_VPRINTF
/*
 * QvwmError --
 *   Display an error in qvwm.
 */
void QvwmError(const char* fmt, ...)
{
  va_list args;
  char buf[256];
  
  va_start(args, fmt);
  
  sprintf(buf, "qvwm: %s\n", fmt);
  vfprintf(stderr, buf, args);

  va_end(args);
}
#endif // HAVE_VPRINTF
  
void FinishQvwm()
{
#ifdef USE_XSMP
  if (session->GetStatus()) {
    if (!session->RequestQuit())
      desktop.FinishQvwm(False);
  }
  else
#endif // USE_XSMP
    desktop.FinishQvwm(False);
}

void RestartQvwm(Bool minimumRestart, int count, Bool cleanup)
{
  char *p, *argv[20], buf[8];
  int i, j;

  if (cleanup)
    desktop.FinishQvwm(True);

  // Fvwm family sets "fvwm" to argument 0 of qvwm.
  // I think that it is a bug of fvwm...
  for (p = qvArgv[0]; *p; p++)
    ;
  while (--p >= qvArgv[0] && *p != '/')
    ;

  // Allows qvwm* (e.g. "qvwm", "qvwm-1.0b11a")
  // If you want another name (e.g. "Qvwm"), use "EXEC ..." instead.
  if (strncmp(++p, "qvwm", 4) != 0) {
    QvwmError("qvwm's 1st argument is not 'qvwm*' but '%s'", p);
    QvwmError("restart 'qvwm'");
    qvArgv[0] = "qvwm";
  }

  for (i = 0, j = 0; qvArgv[i] != NULL && j < 16; i++) {
    // read through a restart option
    if (strcmp(qvArgv[i], "-r") == 0)
      continue;
    // read through a failure count option
    if (strcmp(qvArgv[i], "-c") == 0) {
      i++;
      continue;
    }
    
    argv[j++] = qvArgv[i];
  }

  if (minimumRestart)
    argv[j++] = "-n";
  else {
    argv[j++] = "-r";

    // add an option if from FailureOnRestart
    if (count > 0) {
      argv[j++] = "-c";
      sprintf(buf, "%d", count);
      argv[j++] = buf;
    }
  }

  argv[j] = NULL;
  
  execvp(argv[0], argv);
}


syntax highlighted by Code2HTML, v. 0.9.1