/* Copyright 1988 by the Massachusetts Institute of Technology.
 * All rights reserved.
 *
 * $Source: /afs/sipb.mit.edu/project/sipbsrc/src/webster/src/xwebster/RCS/init.c,v $
 * $Header: /afs/sipb.mit.edu/project/sipbsrc/src/webster/src/xwebster/RCS/init.c,v 1.16 94/07/06 01:32:53 svalente Exp $
 * $Author: svalente $
 *
 * Some portions of this code (derived from xyow.c):
 * Copyright (c) 1987 Siemens Corporate Research and Support, Inc.,
 * Princeton, NJ.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby
 * granted, provided that the above copyright notice appear in all
 * copies and that both that copyright notice and this permission
 * notice appear in supporting documentation, and that the name of
 * Siemens not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior
 * permission.  Siemens makes no representations about the
 * suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 */

#ifndef lint
static char rcsid_init_c[] = "$Header: /afs/sipb.mit.edu/project/sipbsrc/src/webster/src/xwebster/RCS/init.c,v 1.16 94/07/06 01:32:53 svalente Exp $";
#endif lint

#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <xwebster.h>
#include <X11/Xresource.h>

#define DEFAULT_FONT "8x13"
#define IS_NULL(s)  ((s) == (char *) 0)
#define CLASS "XWebster"
     
/* static string & hint storage */
     
static unsigned char host_str[100];
static XSizeHints size_hint;
static char *resource_class;

/* possible command line args */

static char *display = (char *) NULL;
static char *resource_name = (char *) NULL;
static char *window_name = (char *) NULL;
static char *icon_name = (char *) NULL;
static char *foreground_color = (char *) NULL;
static char *background_color = (char *) NULL;
static char *border_color = (char *) NULL;
static char *border_width = (char *) NULL;
static char *font = (char *) NULL;
static char *parent = (char *) NULL;
static char *geometry = (char *) NULL;

unsigned long fg_pixel, bg_pixel, border_pixel;
char *get_resource();

static XrmDatabase resource_database;

/* initialize it */

int	rvflag = False;
int	nohelpflag = False;

/* from xweb.c */
extern int	fontwidth;
extern int	fontheight;


static XrmOptionDescRec opTable[] = {
{"-nohelp", 		".displayHelp",	XrmoptionNoArg,		(caddr_t) "False"},
{"-name",		".name",	XrmoptionSepArg, 	(caddr_t) NULL},
{"-title",		".title",	XrmoptionSepArg,	(caddr_t) NULL},
{"-iconName",		".iconName",	XrmoptionSepArg,	(caddr_t) NULL},
{"-fg",			"*foreground",	XrmoptionSepArg,	(caddr_t) NULL},
{"-foreground",		"*foreground",	XrmoptionSepArg,	(caddr_t) NULL},
{"-bg",			"*background",	XrmoptionSepArg,	(caddr_t) NULL},
{"-background",		"*background", 	XrmoptionSepArg,	(caddr_t) NULL},
{"-bd",			"*borderColor",	XrmoptionSepArg,	(caddr_t) NULL},
{"-borderColor",	"*borderColor",	XrmoptionSepArg,	(caddr_t) NULL},
{"-bw",			"*borderWidth",	XrmoptionSepArg,	(caddr_t) NULL},
{"-borderwidth",	"*borderWidth",	XrmoptionSepArg,	(caddr_t) NULL},
{"-parent",		".parent",	XrmoptionSepArg,	(caddr_t) NULL},
{"-fn",			"*font",	XrmoptionSepArg,	(caddr_t) NULL},
{"-font",		"*font",	XrmoptionSepArg,	(caddr_t) NULL},
{"-display",		".display",	XrmoptionSepArg,	(caddr_t) NULL},
{"-reverseVideo",	"*reverseVideo",XrmoptionNoArg,		(caddr_t) "True"},
{"-rv",			"*reverseVideo",XrmoptionNoArg,		(caddr_t) "True"},
{"+rv",			"*reverseVideo",XrmoptionNoArg,		(caddr_t) "False"},
{"+reverseVideo",	"*reverseVideo",XrmoptionNoArg,		(caddr_t) "False"},
{"-geometry",		"*geometry",	XrmoptionSepArg,	(caddr_t) NULL},
};     
     

extern char *getenv();


     

/* Open a connection to the X server
 * A <display:screen> argument on the command line
 *   (as saved in "display") indicates which screen on
 *   which display controlled by the server should be used.
 * If there was no such argument, display will be NULL, and
 *   XOpenDisplay will use the contents of the DISPLAY
 *   environment variable to indicate the display and sceen.
 */

void
     RTLib_Open_Display()
{
     if (!(dpy = XOpenDisplay(display)))
     {
	  fprintf(stderr, "Failed to open display%s%s...\n",
		  display ? " " : "",
		  display ? display : "");
	  exit(1);
     }

#ifdef X_DEBUG
     XSynchronize(dpy, True);
#endif

     screen = DefaultScreen(dpy);
}

static Bool  errorStatus;

int
     RTLib_Error_Handler(dpy, error)
Display  *dpy;
XErrorEvent  *error;
{
     char buffer[BUFSIZ];
     
     XGetErrorText(dpy, error->error_code, buffer, (int) BUFSIZ);
     
     fprintf(stderr, "X Error <%s>\n", buffer);
     fprintf(stderr, "  Request Major code: %d\n", error->request_code);
     fprintf(stderr, "  Request Minor code: %d\n", error->minor_code);
     fprintf(stderr, "  ResourceId 0x%x\n", error->resourceid);
     fprintf(stderr, "  Error Serial #%d\n", error->serial);
#ifdef HORRIBLY_BLOW_AWAY_ABSTRACTION_BARRIERS
     fprintf(stderr, "  Current Serial #%d\n", dpy->request);
#endif
     
     errorStatus = True;
     
     return 0;
}    

/* Determines whether the window "w" exists on "dpy"
 *   by trying to get its window attributes, and determining if
 *   the error handler was invoked because the window was not there
 */

Bool
     RTLib_Window_Exists(w)
Window w;
{
     XWindowAttributes  xwa;
     
     errorStatus = False;
     XGetWindowAttributes(dpy, w, &xwa);
     return !errorStatus;
}


/* Use resource_name to get resources from .Xdefaults file
 * Get any resource not already set by a command-line
 * argument.
 */

void
     RTLib_Get_Resources()
{
     extern char *whoami;

     char *rvname;
     char *help;
     char *name;

     name = get_resource(resource_database, "name", "Name");
     if (! name)
	  name = whoami;
     window_name = get_resource(resource_database, "title", "Title");
     if (! window_name)
	  window_name = name;
     icon_name = get_resource(resource_database, "iconName", "IconName");
     if (! icon_name)
	  icon_name = name;
     foreground_color = get_resource(resource_database, "foreground",
				     "Foreground");
     background_color = get_resource(resource_database, "background",
				     "Background");
     border_color = get_resource(resource_database, "borderColor",
				 "Foreground");
     border_width = get_resource(resource_database, "borderWidth",
				 "BorderWidth");
     font = get_resource(resource_database, "font", "Font");
     rvname = get_resource(resource_database, "reverseVideo",
			   "ReverseVideo");
     help = get_resource(resource_database, "displayHelp", "DisplayHelp");
     geometry = get_resource(resource_database, "geometry", "Geometry");
     
     if (rvname) {
	  if (!strcasecmp("on", rvname) || !strcasecmp("true", rvname))
	       rvflag = 1;
	  else
	       rvflag = 0;
     }
     else
	  rvflag = 0;

     if (help) {
	  if (!strcasecmp("on", help) || !strcasecmp("true", help))
	       nohelpflag = 0;
	  else
	       nohelpflag = 1;
     }
     else
	  nohelpflag = 0;
}



/* Load the font */

void
     RTLib_Load_Font()
{
     char  *font_name;
     
     font_name = (IS_NULL(font)) ? DEFAULT_FONT : font;
     
     if ((fontp = XLoadQueryFont(dpy, font_name)) == (XFontStruct *)NULL)
     {
	  fprintf(stderr, "Cannot load font %s, exiting!\n", font_name);
	  exit(-2);
     }
     fontwidth = fontp->max_bounds.width;
     fontheight = fontp->max_bounds.ascent + fontp->max_bounds.descent;
}

/* Determine the parent window specified either via the
 *   "-p" command line argument, or via the PARENT_DESKTOP
 *   environment variable.
 * If neither, it is the root window.
 *
 */

Window
     RTLib_Parent_Window()
{
     Window parent_window;
     
     if (IS_NULL(parent))
{
     parent = getenv("PARENT_DESKTOP");
}

if (IS_NULL(parent))
{
     parent_window = RootWindow(dpy, screen);
}
else
{
     parent_window = atoi(parent);
     if (!RTLib_Window_Exists(parent_window))
	  parent_window = RootWindow(dpy, screen);
}
return(parent_window);
}

/* Returns the parent window's width and height */

void
     RTLib_Parent_Size(win, pwidth, pheight)
Window win;
unsigned int *pwidth;
unsigned int *pheight;
{
     XWindowAttributes xwa;
     
     XGetWindowAttributes(dpy, win, &xwa);
     
     *pwidth = xwa.width;
     *pheight = xwa.height;
}

/* Called after the application has set the minimum size
 *   and default initial size and position of the window.
 *
 * If no geometry argument has been provided, then
 *   leave the defaults set by the application alone.
 *
 * Set the size_hint structure appropriately.
 *
 */

void
     RTLib_Set_Initial_Geometry(stash)
struct sizes *stash;
{
     size_hint.flags = PMinSize; /* minimum size set */
     size_hint.min_width = stash->min_width;
     size_hint.min_height = stash->min_height;
     
     /*
      * size & position set by program; gotta lie to the X server
      * to make this work.
      */
     size_hint.flags |= PSize | PPosition | USSize | USPosition;
     
     size_hint.width = stash->cur_width = stash->desired_width;
     size_hint.height = stash->cur_height = stash->desired_height;
     
     size_hint.x = stash->desired_x;
     size_hint.y = stash->desired_y;
}

/* Returns pixel corresponding to "color",
 *  or if that fails, "default_color"
 */

unsigned long
     RTLib_Get_Color(color, default_color)
char *color;
unsigned long default_color;
{
     XColor  xcolor;
     
     if (!IS_NULL(color) &&
	 XParseColor(dpy, DefaultColormap(dpy, screen),
		     color, &xcolor) &&
	 XAllocColor(dpy, DefaultColormap(dpy, screen), &xcolor))
{
     return xcolor.pixel;
}
else
     return default_color;
}

/* Create the window that the application will use
 *   as a subwindow of that parent window.
 * Set WM_NORMAL_HINTS from the initial size_hint
 */

void
     RTLib_Create_Window(stash)
struct sizes *stash;
{
     unsigned int  border_width_val;
     extern Window  XCreateSimpleWindow();
     Window window;
     Cursor hand;
     
     if (IS_NULL(border_width))
	  border_width_val = DEFAULT_BORDER_WIDTH;
     else
	  border_width_val = atoi(border_width);
     
     window = XCreateSimpleWindow(dpy, parents.win,
				  stash->desired_x, stash->desired_y,
				  stash->desired_width,
				  stash->desired_height,
				  border_width_val,
				  border_pixel,
				  bg_pixel);
     
     XSetNormalHints(dpy, window, &size_hint);
     hand = XCreateFontCursor(dpy, XC_hand2);
     XDefineCursor(dpy, window, hand);
     stash->win = window;
     XSaveContext(dpy, window, defwindow, (char *)stash);
}

/* Determine the host on which the application is running
 * and use it to set the WM_CLIENT_MACHINE property.
 *
 */

void
     RTLib_Set_Host(window)
Window window;
{
     gethostname(host_str, 100);
     host_str[99] = '\0';
     
     XChangeProperty(dpy, window, XA_WM_CLIENT_MACHINE, XA_STRING, 8,
		     PropModeReplace, host_str, strlen(host_str));
}

/* Set WM_CLASS_HINTS from the resource_name and resource_class */

void
     RTLib_Set_Class_Hints(window)
Window window;
{
     XClassHint class_hint;
     
     class_hint.res_name = resource_name;	
     class_hint.res_class = resource_class;
     
     XSetClassHint(dpy, window, &class_hint);
}

/*
 * Set WM_NAME
 */

void
     RTLib_Set_Name(stash, override)
struct sizes *stash;
int override;
{
     if (! override)
	  stash->win_name = window_name;
     else if (! stash->win_name)
	  stash->win_name = window_name;
     
     XStoreName(dpy, stash->win, override ? stash->win_name : window_name);
}

/*
 * Set WM_ICON_NAME
 */

void 
     RTLib_Set_Icon_Name(stash, override)
struct sizes *stash;
int override;
{
     if (override && ! stash->win_name)
	  stash->win_name = window_name;
     
     XSetIconName(dpy, stash->win, override ? stash->win_name : icon_name);
     return;
}

/* Set WM_HINTS to indicate that the client does not take
 * responsibility for the input focus (but leaves it to the wm), and
 * that when the window is initially mapped, it should be opened
 * (rather than iconized).
 *
 * If icon is a null pointer, this routine will leave it to the wm to
 * provide a default one.
 */

void
     RTLib_Set_WM_Hints(window, icon)
Window window;
Pixmap *icon;
{
     XWMHints  wmhints;
     
     wmhints.input = True;
     wmhints.initial_state = NormalState;
     wmhints.flags = InputHint | StateHint;	
     
     if (icon != (Pixmap *) NULL)
     {
	  wmhints.flags |= IconPixmapHint;
	  wmhints.icon_pixmap = *icon;
     }

     XSetWMHints(dpy, window, &wmhints);
}

/* Set the various window properties */

void 
     RTLib_Set_Properties(argv, argc, stash, icon, name_override)
char  *argv[];
int  argc;
struct sizes *stash;
Pixmap *icon;
int name_override;
{
     Window window = stash->win;
     
     XSetCommand(dpy, window, argv, argc);
     RTLib_Set_Host(window);
     RTLib_Set_Class_Hints(window);
     RTLib_Set_Name(stash, name_override);
     RTLib_Set_Icon_Name(stash, name_override);
     RTLib_Set_WM_Hints(window, icon);
}

/* Set the graphics context used for drawing in the window */

void
     RTLib_Set_GC()
{
     unsigned long  gcMask;
     XGCValues  gcv;
     
     gcMask = GCForeground | GCBackground | GCFunction | GCFont;
     gcv.font = fontp->fid;
     gcv.function = GXcopy;
     gcv.foreground = fg_pixel;
     gcv.background = bg_pixel;
     
     gc = XCreateGC(dpy, RootWindow(dpy, screen),
		    gcMask, &gcv);

     gcMask = GCForeground | GCBackground | GCFunction | GCFont;
     gcv.font = fontp->fid;
     gcv.function = GXcopy;
     gcv.foreground = bg_pixel;
     gcv.background = bg_pixel;
     
     erase_gc = XCreateGC(dpy, RootWindow(dpy, screen), gcMask, &gcv);
}


Pixmap RTLib_Create_Pixmap(data, width, height)
char *data;
unsigned int width, height;
{
     return(XCreatePixmapFromBitmapData(dpy, parents.win, data, width, height,
					fg_pixel, bg_pixel,
					DefaultDepth(dpy, screen)));
}


/*
 * Allocates pixels for the foreground, background and border colors
 * so they do not have to be reallocated over and over each time a
 * window is created.
 */
RTLib_Get_Colors()
{
     border_pixel = RTLib_Get_Color(border_color, rvflag
				    ? WhitePixel(dpy, screen)
				    : BlackPixel(dpy, screen));
     
     bg_pixel = RTLib_Get_Color(background_color, rvflag
				? BlackPixel(dpy, screen)
				: WhitePixel(dpy, screen));
     
     fg_pixel = RTLib_Get_Color(foreground_color, rvflag
				? WhitePixel(dpy, screen)
				: BlackPixel(dpy, screen));
}



char *get_resource(database, res_instance, res_class)
XrmDatabase database;
char *res_instance, *res_class;
{
     char inst_buf[80];
     char cl_buf[80];
     char *ret_type;
     XrmValue ret;
     
     sprintf(inst_buf, "%s.%s", resource_name, res_instance);
     sprintf(cl_buf, "%s.%s", resource_class, res_class);

     if (XrmGetResource(database, inst_buf, cl_buf, &ret_type, &ret))
	  return((char *) ret.addr);
     else
	  return((char *) NULL);
}


		   

void RTLib_Initialize(whoami, argc, argv)
char *whoami;
int *argc;
char **argv;
{
     /*
      * Algorithm:
      *
      * 1. Pull the "-name" option out of argv, if it is there.
      * 2. Pass that and argc and argv into XrmParseCommand to create
      *    a new database.
      * 3. Check to see if any arguments are left over -- error and
      *    exit if there are.
      * 4. Pull "-display" out of that database, if it is there.
      * 5. Open the resulting display, or NULL if it didn't get anything.
      * 6. Get the resource database from that display.
      * 7. Overload the XENVIRONMENT database on top of the display
      *    resource database.
      * 8. Overload the command line database on top of the
      *    XENVIRONMENT/X display database.
      * 9. Read all the local variables out of the resource database.
      * 10. Allocate colors and load fonts.
      */

     int i;
     char *found_name = NULL;
     XrmDatabase argdb = NULL, envdb = NULL;
     char *xenv;
     
     /* Step 1: Get name out of command line. */
     
     for (i = 0; i < *argc; i++) {
	  /* Hard coded assumptions about command line options, and    */
	  /* how far we have to match to get a unique match on 	       */
	  /* "-name".  If the command line options change, this has to */
	  /* change too.					       */
	  /* Also, notice that we're testing every command line      */
	  /* argument, rather than just the ones that are definitely */
	  /* flags.  Somebody doing something like specing a display */
	  /* called "-name" will lose.  How likely do you think that */
	  /* is?						     */
	  if (! strncmp("-name", argv[i], 3)) {
	       if (argv[i][3]) {
		    if (argv[i][3] != 'm')
			 continue;
		    else {
			 if (argv[i][4]) {
			      if (argv[i][4] != 'e')
				   continue;
			      else if (argv[i][5])
				   continue;
			      else if (i + 1 < *argc)
				   found_name = argv[i+1];
			 }
		    }
	       }
	       /* Continue instead of break because if there are       */
	       /* multiple -name arguments, the ones at the end of the */
	       /* command line should override the ones at the 	       */
	       /* beginning.					       */
	       continue;
	  }
     }

     resource_name = found_name ? found_name : whoami;
     resource_class = CLASS;

     /* Step 2: Parse the rest of the command line. */
     
     XrmParseCommand(&argdb, opTable, sizeof(opTable) / sizeof(opTable[0]),
		     resource_name, argc, argv);

     /* Step 3: Check for bogus arguments. */

     if (*argc > 1) {
	  fprintf(stderr, "%s: unknown option: %s\n", whoami, argv[1]);
	  fprintf(stderr,
		  "Use standard X toolkit command-line options, plus\n");
	  fprintf(stderr,
		  "\"-nohelp\" to suppress the start-up help message.\n");
	  exit(-1);
     }
     
     /* Step 4: Get the display from the argv database. */

     display = get_resource(argdb, "display", "Display");

     /* Step 5: Open the display. */

     RTLib_Open_Display();

     /* Step 6: Get the resources for that display. */

     if (xenv = XResourceManagerString(dpy)) {
	  resource_database = XrmGetStringDatabase(xenv);
     }

     /* Step 7: Overload that with the XENVIRONMENT database. */

     xenv = getenv("XENVIRONMENT");
     if (xenv) {
	  envdb = XrmGetFileDatabase(xenv);
	  if (envdb)
	       XrmMergeDatabases(envdb, &resource_database);
     }

     /* Step 8: Overload the command line database on top. */

     XrmMergeDatabases(argdb, &resource_database);

     /* Step 9: Read all the local variables out of the resource */
     /* database. */

     RTLib_Get_Resources();

     /* Step 10: Colors and Font */
     
     RTLib_Get_Colors();
     RTLib_Load_Font();
}



void
     RTLib_Get_Initial_Geometry(x_val, y_val, x_neg, y_neg)
int *x_val, *y_val;
int *x_neg, *y_neg;
{
     int x, y;
     unsigned int width, height;
     int mask;
     
     if (geometry) {
	  mask = XParseGeometry(geometry, &x, &y, &width, &height);

	  *x_val = (mask & XValue) ? ((mask & XNegative) ? -x : x) : 0;
	  *y_val = (mask & YValue) ? ((mask & YNegative) ? -y : y) : 0;
	  *x_neg = (mask & XNegative) ? True : False;
	  *y_neg = (mask & YNegative) ? True : False;
     }
     else {
	  *x_val = *y_val = 0;
	  *x_neg = False;
	  *y_neg = True;
     }
}


int RTLib_border_width()
{
     if (IS_NULL(border_width))
	  return DEFAULT_BORDER_WIDTH;
     else
	  return atoi(border_width);
}
