/*--------------------------------*-C-*---------------------------------*
 * File:	xsetup.c
 *
 * Copyright 1992 John Bovey, University of Kent at Canterbury.
 *
 * You can do what you like with this source code as long as
 * you don't try to make money out of it and you include an
 * unaltered copy of this message (including the copyright).
 *
 * This module has heavily modified by R. Nation
 * <nation@rocket.sanders.lockheed.com>
 * No additional restrictions are applied
 *
 * Additional modifications by Garrett D'Amore <garrett@netcom.com>
 * No additional restrictions are applied.
 *
 * Extensive modifications by mj olesen <olesen@me.QueensU.CA>
 * No additional restrictions are applied.
 *
 * As usual, the author accepts no responsibility for anything, nor does
 * he guarantee anything whatsoever.
 *----------------------------------------------------------------------*/
#include "rxvt.h"

#include <ctype.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>

#include "command.h"
#include "debug.h"
#include "graphics.h"
#include "sbar.h"
#include "xsetup.h"
#include "xdefaults.h"
#include "screen.h"
#include "defaults.h"

/*----------------------------------------------------------------------*
 * extern functions referenced
 */
#ifdef DISPLAY_IS_IP
extern char * network_display (const char *display);
#endif

/*----------------------------------------------------------------------*
 * extern variables referenced
 */
/*----------------------------------------------------------------------*
 * extern variables declared here
 */
TermWin_t	TermWin;
Display		*Xdisplay;	/* display */

const char *rs_color [NCOLORS + NCURSOR + NSCROLLCOLORS];
Pixel pixel_colors [NCOLORS + NCURSOR + NSCROLLCOLORS];

#ifndef NO_SCROLLBAR
sbar_t sbar;
#endif

unsigned long Options = (Opt_scrollBar);

#ifdef MAPALERT_OPTION
KeySym ks_alert = DEF_KS_ALERT;
#endif

const char *display_name = NULL;
const char *rs_name = NULL;	/* client instance (resource name) */

const char *rs_font [NFONTS];
#ifdef KANJI
const char *rs_kfont [NFONTS];
#endif
KeySym ks_bigfont = DEF_KS_BIGFONT;
KeySym ks_smallfont = DEF_KS_SMALLFONT;

#ifdef PRINTPIPE
const char *rs_print_pipe = NULL;
KeySym ks_printscreen = DEF_KS_PRINTSCREEN;
#endif

KeySym ks_pageup   = DEF_KS_PAGEUP;
KeySym ks_pagedown = DEF_KS_PAGEDOWN;

#ifndef NO_MULTIPLE_CLICK
const char *rs_cutchars = NULL;
#endif

/*----------------------------------------------------------------------*
 * local variables
 */
static XSizeHints szHint = {
   PMinSize | PResizeInc | PBaseSize | PWinGravity,
   0, 0, 80, 24,	/* x, y, width, height */
   1, 1,		/* Min width, height */
   0, 0,		/* Max width, height - unused*/
   1, 1,		/* increments: width, height */
   {1, 1},		/* increments: x, y */
   {0, 0},		/* Aspect ratio - unused */
   0, 0,		/* base size: width, height */
   NorthWestGravity	/* gravity */
};

static const char *Def_color_names [] = {
   "Black", "White",		/* fg/bg */
#ifdef NO_BOLDCOLOR
     "Black",			/* 0: black		(#000000) */
     "Red",			/* 1: red		(#FF0000) */
     "Green",			/* 2: green		(#00FF00) */
     "Yellow",			/* 3: yellow		(#FFFF00) */
     "Blue",			/* 4: blue		(#0000FF) */
     "Magenta",			/* 5: magenta		(#FF00FF) */
     "Cyan",			/* 6: cyan		(#00FFFF) */
     "White",			/* 7: white		(#FFFFFF) */
#else	/* NO_BOLDCOLOR */
     /* low-intensity colors */
     "Black",			/* 0: black		(#000000) */
     "Red3",			/* 1: red		(#CD0000) */
     "Green3",			/* 2: green		(#00CD00) */
     "Yellow3",			/* 3: yellow		(#CDCD00) */
     "Blue3",			/* 4: blue		(#0000CD) */
     "Magenta3",		/* 5: magenta		(#CD00CD) */
     "Cyan3",			/* 6: cyan		(#00CDCD) */
     "AntiqueWhite",		/* 7: white		(#FAEBD7) */
     /* high-intensity colors */
     "Grey25",			/* 10: bright black	(#404040) */
     "Red",			/* 11: bright red	(#FF0000) */
     "Green",			/* 12: bright green	(#00FF00) */
     "Yellow",			/* 13: bright yellow	(#FFFF00) */
     "Blue",			/* 14: bright blue	(#0000FF) */
     "Magenta",			/* 15: bright magenta	(#FF00FF) */
     "Cyan",			/* 16: bright cyan	(#00FFFF) */
     "White",			/* 17: bright white	(#FFFFFF) */
#endif	/* NO_BOLDCOLOR */
#ifndef NO_CURSORCOLOR
     NULL,
# ifndef NO_CURSORCOLOR2
     NULL,
# endif	/* NO_CURSORCOLOR2 */
#endif	/* NO_CURSORCOLOR */
     "#979797",			/* scrollColor: matches netscape colors */
     NULL, NULL			/* topscroll colors */
};

#ifdef KANJI
/* Kanji font names, roman fonts sized to match */
static const char *Def_kfont_names [] = {
   KFONT0, KFONT1, KFONT2, KFONT3, KFONT4
};
#endif	/* KANJI */
static const char *Def_font_names [] = {
   FONT0, FONT1, FONT2, FONT3, FONT4
};
/*----------------------------------------------------------------------*
 * local functions referenced
 */

/*----------------------------------------------------------------------*/
const char *
my_basename (const char *string)
{
   const char *base;
   return ((base = strrchr (string, '/')) == NULL ? string : base + 1);
}

static XErrorHandler
RxvtErrorHandler (Display *dpy, XErrorEvent *event)
{
   exit (EXIT_FAILURE);
   return 0;
}

static inline void
color_aliases (int index)
{
   if ((rs_color [index] != NULL) && isdigit (*rs_color [index]))
     {
	int n = atoi (rs_color [index]);
	if (n >= 10 && n <= 17)		/* bold colors */
	  {
#ifdef NO_BOLDCOLOR
	     n -= 10;
#else
	     rs_color [index] = rs_color [(n-10) + BOLDMIN];
#endif
	  }
	if (n >= 0 && n <= 7)		/* normal colors */
	  rs_color [index] = rs_color [n + COLORMIN];
     }
}

/*
 * Open and map the window
 */
static void
Create_Windows (int argc, char *argv[])
{
   XColor xcol, xcolor_bg, xcolor_fg;
   XClassHint classHint;
   XWMHints wmHint;
   Cursor cursor;
   int i, x, y, flags;
   unsigned int width, height;

   /*
    * allocate colors now, before netscape gets them ;)
    */
   for (i = 0; i < NCOLORS + NCURSOR; i++)
     {
	const char * const color_msg = "can't load color \"%s\"";
	XColor * pxcol = &xcol;

	switch (i) {
	 case COLORFG:	pxcol = &xcolor_fg;	break;
	 case COLORBG:	pxcol = &xcolor_bg;	break;
	}

	if (rs_color [i] == NULL)
	  continue;

	if (!XParseColor (Xdisplay, Xcmap, rs_color [i], pxcol) ||
	    !XAllocColor (Xdisplay, Xcmap, pxcol))
	  {
	     print_error (color_msg, rs_color [i]);
	     rs_color [i] = Def_color_names [i];
	     if (!XParseColor (Xdisplay, Xcmap, rs_color [i], pxcol) ||
		 !XAllocColor (Xdisplay, Xcmap, pxcol))
	       {
		  print_error (color_msg, rs_color [i]);
		  pixel_colors [i] = 0;		/* None */
		  if (i == COLORFG || i == COLORBG)
		    goto Abort;
		  continue;
	       }
	  }
	/* success of some form */
#ifndef NO_CURSORCOLOR
	switch (i) {
	 case CURSORCOLOR: color_cursor = RS_bgMask; break;
# ifndef no_CURSORCOLOR2
	 case CURSORCOLOR2:
	   if (color_cursor)	/* cursor bg MUST be set */
	     color_cursor |= RS_fgMask;
	   break;
# endif	/* NO_CURSORCOLOR2 */
	}
#endif	/* NO_CURSORCOLOR */
	pixel_colors [i] = pxcol->pixel;
     }

#ifndef NO_BOLDCOLOR
   /*
    * find if fg/bg matches any of the normal colors
    */
   for (i = COLORMIN; i <= COLORMAX; i++)
     {
	if (xcolor_fg.pixel == pixel_colors [i])
	  color_fgbg = SET_FGCOLOR (color_fgbg, i);
	if (xcolor_bg.pixel == pixel_colors [i])
	  color_fgbg = SET_BGCOLOR (color_fgbg, i);
     }
#endif	/* NO_BOLDCOLOR */

   /*
    * get font now so we know its size
    */
     {
	const char * const font_msg = "can't load font \"%s\"";
	XFontStruct *xfont;

#ifdef KANJI
	xfont = XLoadQueryFont (Xdisplay, rs_kfont [0]);
	if (!xfont)
	  {
	     print_error (font_msg, rs_kfont [0]);
	     goto Abort;
	  }
	TermWin.kanji = xfont;
#endif

	xfont = XLoadQueryFont (Xdisplay, rs_font [0]);
	if (!xfont)
	  {
	     print_error (font_msg, rs_font [0]);
	     rs_font [0] = "fixed";
	     xfont = XLoadQueryFont (Xdisplay, rs_font [0]);
	     if (!xfont)
	       {
		  print_error (font_msg, rs_font [0]);
		  goto Abort;
	       }
	  }
	TermWin.font = xfont;
     }

   TermWin.fheight = TermWin.font->ascent + TermWin.font->descent;
   TermWin.fwidth  = XTextWidth (TermWin.font, "MMMMMMMMMM",10) / 10;
   szHint.height_inc = TermWin.fheight;
   szHint.width_inc  = TermWin.fwidth;

   flags = (rs_geometry == NULL ? 0 :
	    XParseGeometry (rs_geometry, &x, &y, &width, &height));

   if (flags & WidthValue)
     {
	szHint.width = width;
	szHint.flags |= USSize;
     }
   if (flags & HeightValue)
     {
	szHint.height = height;
	szHint.flags |= USSize;
     }

   TermWin.ncol = szHint.width;
   TermWin.nrow = szHint.height;
   TermWin.width  = TermWin.ncol * TermWin.fwidth;
   TermWin.height = TermWin.nrow * TermWin.fheight;

   szHint.base_width = (2 * TermWin_internalBorder);
   szHint.base_height = (2 * TermWin_internalBorder);

#ifndef NO_SCROLLBAR
   sbar.width = (Options & Opt_scrollBar) ? SB_WIDTH : 0;

   if (Options & Opt_scrollBar)
     szHint.base_width += sbar.width + 2 * (SB_SHADOW + SB_BORDER);
#endif	/* NO_SCROLLBAR */

   szHint.width  = szHint.base_width  + (szHint.width  * szHint.width_inc);
   szHint.height = szHint.base_height + (szHint.height * szHint.height_inc);
   szHint.min_width  = szHint.base_width  + szHint.width_inc;
   szHint.min_height = szHint.base_height + szHint.height_inc;
   if (flags & XValue)
     {
	if (flags & XNegative)
	  {
	     x += (DisplayWidth (Xdisplay, Xscreen)
		   - (szHint.width + TermWin_internalBorder));
	     szHint.win_gravity = NorthEastGravity;
	  }
	szHint.x = x;
	szHint.flags |= USPosition;
     }
   if (flags & YValue)
     {
	if (flags & YNegative)
	  {
	     y += (DisplayHeight (Xdisplay, Xscreen)
		   - (szHint.height + TermWin_internalBorder));

	     szHint.win_gravity = (szHint.win_gravity == NorthEastGravity ?
				     SouthEastGravity : SouthWestGravity);
	  }
	szHint.y = y;
	szHint.flags |= USPosition;
     }

   /* create the window - reverse video so we can see placement errors */
   TermWin.parent = XCreateSimpleWindow (Xdisplay,
					 DefaultRootWindow (Xdisplay),
					 szHint.x, szHint.y,
					 szHint.width, szHint.height,
 					 DEF_BORDERWIDTH,
					 pixel_colors [COLORFG],
					 pixel_colors [COLORFG]);

   xterm_seq (NEW_TITLE_NAME, rs_title);
   xterm_seq (NEW_ICON_NAME, rs_iconName);
   /* ignore warning about discarded `const' */
   classHint.res_name  = (char *) rs_name;
   classHint.res_class = APL_CLASS;
   wmHint.input = True;
   wmHint.initial_state = ((Options & Opt_iconic) ?
			   IconicState : NormalState);
   wmHint.flags = (InputHint | StateHint);

   XSetWMProperties (Xdisplay, TermWin.parent, NULL, NULL, argv, argc,
		     &szHint, &wmHint, &classHint);

   XSelectInput (Xdisplay, TermWin.parent,
		 (KeyPressMask|FocusChangeMask|
		  StructureNotifyMask|VisibilityChangeMask)
		 );

#ifndef NO_SCROLLBAR
   if (Options & Opt_scrollBar)
     {
	sbar.beg = 0;
	sbar.end = szHint.height - 2 * (SB_SHADOW + SB_BORDER);

	sbar.parent = XCreateSimpleWindow (Xdisplay, TermWin.parent,
					   -SB_BORDER, -SB_BORDER,
					   sbar.width + 2 * (SB_SHADOW + SB_BORDER),
					   szHint.height,
					   SB_BORDER,
					   pixel_colors [COLORFG],
					   pixel_colors [COLORBG]);

	sbar.sb = XCreateSimpleWindow (Xdisplay, sbar.parent,
				       SB_BORDER + SB_SHADOW,
				       SB_BORDER + SB_SHADOW,
				       sbar.width, sbar.end,
				       0,
				       pixel_colors [COLORFG],
				       pixel_colors [COLORBG]);
	/* cursor: Black-on-White */
	cursor = XCreateFontCursor (Xdisplay,
# ifdef XTERM_SCROLLBAR
				    (Options & Opt_sbRight) ?
				    XC_sb_right_arrow : XC_sb_left_arrow
# else
				    XC_left_ptr
# endif
				    );
	XDefineCursor (Xdisplay, sbar.parent, cursor);
	XDefineCursor (Xdisplay, sbar.sb, cursor);

	XSelectInput (Xdisplay, sbar.sb,
		      (ExposureMask|ButtonPressMask|ButtonReleaseMask|
		       Button1MotionMask|Button2MotionMask|Button3MotionMask)
		      );
# ifndef XTERM_SCROLLBAR
	if (Options & Opt_sbArrows)
	  {
	     sbar.beg += sbar.width;
	     sbar.end -= sbar.width;
	  }
# endif
	XMapWindow (Xdisplay, sbar.sb);
	XMapWindow (Xdisplay, sbar.parent);
     }
#endif	/* NO_SCROLLBAR */

   /* correct placement now, do size in resize_windows() */
   x = 0;
#ifndef NO_SCROLLBAR
   if ((Options & Opt_scrollBar) && !(Options & Opt_sbRight))
     x = sbar.width + SB_BORDER + 2 * (SB_SHADOW + SB_BORDER);
#endif
   TermWin.vt = XCreateSimpleWindow (Xdisplay, TermWin.parent,
				     x, 0,
				     szHint.width, szHint.height,
				     0,
				     pixel_colors [COLORFG],
				     pixel_colors [COLORBG]);

   /* cursor: Black-on-White is normal, but this is more popular */
   cursor = XCreateFontCursor (Xdisplay, XC_xterm);
   XRecolorCursor (Xdisplay, cursor, &xcolor_fg, &xcolor_bg);
   XDefineCursor (Xdisplay, TermWin.vt, cursor);

   XSelectInput (Xdisplay, TermWin.vt,
		 (ExposureMask|ButtonPressMask|ButtonReleaseMask|
		  Button1MotionMask|Button3MotionMask));

   XMapWindow (Xdisplay, TermWin.vt);
   XMapWindow (Xdisplay, TermWin.parent);

   return;
   Abort:
   print_error ("aborting");	/* fatal problem */
   exit (EXIT_FAILURE);
}

/*
 * Open display, get options, resources and create the window
 */
void
init_display (int argc, char * argv[], char *cmd_argv[])
{
   /* "WINDOWID=\0" = 10 chars, UINT_MAX = 10 chars */
   static char windowid_string [20], * display_string;
   char *val;
   XGCValues gcv;
   int i;

   if ((display_name = getenv ("DISPLAY")) == NULL)
     display_name = ":0";

   if (cmd_argv != NULL && cmd_argv[0] != NULL)
     rs_iconName = rs_title = my_basename (cmd_argv[0]);

   rs_name = my_basename (argv [0]);

   get_options (argc, argv);

   Xdisplay = XOpenDisplay (display_name);
   if (!Xdisplay)
     {
	print_error ("can't open display %s", display_name);
	exit (EXIT_FAILURE);
     }
   extract_resources (Xdisplay, rs_name);

   /*
    * set any defaults not already set
    */
   if (rs_title == NULL) rs_title = rs_name;
   if (rs_iconName == NULL) rs_iconName = rs_name;
   if (rs_saveLines == NULL || (TermWin.saveLines = atoi (rs_saveLines)) < 0)
     TermWin.saveLines = DEF_SAVELINES;

#ifdef PRINTPIPE
   if (rs_print_pipe == NULL) rs_print_pipe = PRINTPIPE;
#endif
#ifndef NO_MULTIPLE_CLICK
   if (rs_cutchars == NULL) rs_cutchars = DEF_CUTCHARS;
#endif

   for (i = 0; i < NFONTS; i++)
     {
	if (rs_font [i] == NULL) rs_font [i] = Def_font_names [i];
#ifdef KANJI
	if (rs_kfont [i] == NULL) rs_kfont [i] = Def_kfont_names [i];
#endif
     }

#ifdef XTERM_REVERSE_VIDEO
   /* this is how xterm implements reverseVideo */
   if (Options & Opt_reverseVideo)
     {
	if (rs_color [COLORFG] == NULL)
	  rs_color [COLORFG] = Def_color_names [COLORBG];
	if (rs_color [COLORBG] == NULL)
	  rs_color [COLORBG] = Def_color_names [COLORFG];
     }
#endif

   for (i = 0; i < (NCOLORS + NCURSOR + NSCROLLCOLORS); i++)
     if (rs_color [i] == NULL) rs_color [i] = Def_color_names [i];

#ifndef XTERM_REVERSE_VIDEO
   /* this is how rxvt implements reverseVideo */
   if (Options & Opt_reverseVideo)
     {
	const char * name;
	/* swap foreground/background colors */

	name = rs_color [COLORFG];
	rs_color [COLORFG] = rs_color [COLORBG];
	rs_color [COLORBG] = name;

	name = Def_color_names [COLORFG];
	Def_color_names [COLORFG] = Def_color_names [COLORBG];
	Def_color_names [COLORBG] = name;
     }
#endif

   /* convenient aliases for setting fg/bg to colors */
   color_aliases (COLORFG);
   color_aliases (COLORBG);
#ifndef NO_CURSORCOLOR
   color_aliases (CURSORCOLOR);
# ifndef NO_CURSORCOLOR2
   color_aliases (CURSORCOLOR2);
# endif	/* NO_CURSORCOLOR2 */
#endif	/* NO_CURSORCOLOR */

#ifdef DISPLAY_IS_IP
   /* Fixup display_name for export over pty to any interested terminal
    * clients via "ESC[7n" (e.g. shells).  Note we use the pure IP number
    * (for the first non-loopback interface) that we get from
    * network_display().  This is more "name-resolution-portable", if you
    * will, and probably allows for faster x-client startup if your name
    * server is beyond a slow link or overloaded at client startup.  Of
    * course that only helps the shell's child processes, not us.
    *
    * Giving out the display_name also affords a potential security hole
    */
   val = display_name = network_display (display_name);
   if (val == NULL)
#endif	/* DISPLAY_IS_IP */
     val = XDisplayString (Xdisplay);
   if (display_name == NULL)
     display_name = val;	/* use broken `:0' value */

   i = strlen (val);
   display_string = MALLOC ((i+9)*sizeof(char), "display_string");

   sprintf (display_string, "DISPLAY=%s", val);
   sprintf (windowid_string, "WINDOWID=%u", (unsigned int)TermWin.parent);

   /* add entries to the environment:
    * @ DISPLAY:   in case we started with rxvt -display
    * @ WINDOWID:  X window id number of the rxvt window
    * @ TERM:      terminal name
    * @ COLORTERM: terminal sub-name and also indicates its color
    */
   putenv (display_string);
   putenv (windowid_string);
   putenv ("TERM=" TERM_NAME);
   putenv ("COLORTERM=" APL_NAME);

   Create_Windows (argc, argv);

   scr_reset ();		/* initialize screen */
   Gr_reset ();

   /* Create the graphics contexts */
   gcv.font = TermWin.font->fid;
   gcv.foreground = pixel_colors [COLORFG];
   gcv.background = pixel_colors [COLORBG];
   TermWin.gc = XCreateGC (Xdisplay, TermWin.parent,
			   GCForeground|GCBackground|GCFont, &gcv);

   sbar_init (TermWin.parent, &gcv);	/* initialize the scrollbar */

#ifdef DEBUG_X
   XSynchronize (Xdisplay, True);
   XSetErrorHandler ((XErrorHandler)abort);
#else
   XSetErrorHandler ((XErrorHandler)RxvtErrorHandler);
#endif
}

/*
 * Redraw window
 */
static void
resize_windows (int width, int height)
{
   int old_width  = TermWin.width;
   int old_height = TermWin.height;

   TermWin.width  = TermWin.ncol * TermWin.fwidth;
   TermWin.height = TermWin.nrow * TermWin.fheight;

#ifndef NO_SCROLLBAR
   if (Options & Opt_scrollBar)
     {
	sbar.end = height - 2 * (SB_SHADOW + SB_BORDER);
# ifndef XTERM_SCROLLBAR
	if (Options & Opt_sbArrows)
	  sbar.end -= sbar.width;
# endif

	XResizeWindow (Xdisplay, sbar.parent,
		       sbar.width + 2 * (SB_SHADOW + SB_BORDER), height);

	XResizeWindow (Xdisplay, sbar.sb,
		       sbar.width, height - 2 * (SB_SHADOW + SB_BORDER));

	width -= (sbar.width + 2 * (SB_SHADOW + SB_BORDER));
	if (Options & Opt_sbRight)
	  {
	     width -= SB_BORDER;
	     XMoveWindow (Xdisplay, sbar.parent, width, -SB_BORDER);
	  }
     }
#endif	/* NO_SCROLLBAR */

   XResizeWindow (Xdisplay, TermWin.vt, width, height + 1);
   if (old_width)
     Gr_Resize (old_width, old_height);
   XClearWindow (Xdisplay, TermWin.vt);
   XSync (Xdisplay, 0);
}

/*
 * Redraw window after exposure or size change
 */
void
resize_window (void)
{
   static int old_ncol = -1, old_nrow = -1;
   int new_ncol, new_nrow;
   int x, y;
   unsigned int border, depth, width, height;

   Window root;
   XEvent dummy;

   while (XCheckTypedWindowEvent (Xdisplay, TermWin.parent,
				  ConfigureNotify, &dummy));
   XGetGeometry (Xdisplay, TermWin.parent,
		 &root, &x, &y, &width, &height, &border, &depth);

   new_ncol = (width  - szHint.base_width)  / TermWin.fwidth;
   new_nrow = (height - szHint.base_height) / TermWin.fheight;

   if ((new_ncol != old_ncol) || (new_ncol != TermWin.ncol) ||
       (new_nrow != old_nrow) || (new_nrow != TermWin.nrow) )
     {
	int curr_screen  = -1;

	/* scr_reset only works on the primary screen */
	if (old_ncol >= 0)	/* not first time thru */
	  {
	     selection_clear ();
	     curr_screen = scr_change_screen (PRIMARY);
	  }

	old_ncol = new_ncol;	/* save for next time */
	old_nrow = new_nrow;

	TermWin.ncol = new_ncol;
	TermWin.nrow = new_nrow;

	resize_windows (width, height);
	scr_reset ();

	if (curr_screen >= 0)	/* not first time thru */
	  scr_change_screen (curr_screen);
     }
}

/*
 * good for toggling 80/132 columns
 */
void
set_width (unsigned int width)
{
   unsigned int height = TermWin.nrow;

   if (width == TermWin.ncol)
     return;

   width  = szHint.base_width  + width  * TermWin.fwidth;
   height = szHint.base_height + height * TermWin.fheight;

   XResizeWindow (Xdisplay, TermWin.parent, width, height);
   resize_window ();
}

/*
 * Print an error message
 */
void
print_error (const char *fmt, ...)
{
   va_list arg_ptr;

   va_start (arg_ptr, fmt);
   fprintf (stderr, "%s: ", rs_name);
   vfprintf (stderr, fmt, arg_ptr);
   fprintf (stderr, "\n");
   va_end (arg_ptr);
}

/*
 * change title/icon name
 */
void
xterm_seq (int op, const char *string)
{
   XTextProperty name;
   /* ignore warning about discarded `const' */
   char * str = (char *) string;

   if (!XStringListToTextProperty (&str, 1, &name))
     {
	print_error ("can't allocate title/icon name");
	return;
     }

   switch (op) {
    case NEW_NAMES:
      XSetWMName (Xdisplay, TermWin.parent, &name);	/* drop */
    case NEW_ICON_NAME:
      XSetWMIconName (Xdisplay, TermWin.parent, &name);	break;
    case NEW_TITLE_NAME:
      XSetWMName (Xdisplay, TermWin.parent, &name);	break;
   }
   XFree (name.value);
}

/*
 * Switch to a new font
 */
void
new_font (int dirn)
{
   static int font_num = NORM_FONT;
   int num;

   switch (dirn) {
    case UP:
      if (++font_num >= NFONTS)
	{
	   font_num = (NFONTS - 1);
	   return;
	}
      break;

    case DOWN:
      if (--font_num < 0)
	{
	   font_num = 0;
	   return;
	}
      break;
   }

   /* take case of shuffling font numbers */

   num = font_num;
   if (num == NORM_FONT)	/* position of the normal font */
     num = 0;
   else if (num < NORM_FONT)
     num++;

     {
	const char * const font_msg = "can't load font \"%s\"";
	XFontStruct * xfont;

# ifdef KANJI
	XFreeFont (Xdisplay, TermWin.kanji);
	xfont = XLoadQueryFont (Xdisplay, rs_kfont [num]);
	if (!xfont)
	  {
	     print_error (font_msg, rs_kfont [num]);
	     xfont = XLoadQueryFont (Xdisplay, rs_kfont [0]);
	     rs_kfont [num] = rs_kfont [0];
	  }
	TermWin.kanji = xfont;
# endif	/* KANJI */
	XFreeFont (Xdisplay, TermWin.font);
	xfont = XLoadQueryFont (Xdisplay, rs_font [num]);
	if (!xfont)
	  {
	     print_error (font_msg, rs_font [num]);
	     xfont = XLoadQueryFont (Xdisplay, rs_font [0]);
	     rs_font [num] = rs_font [0];
	  }
	TermWin.font = xfont;
     }
   XSetFont (Xdisplay, TermWin.gc, TermWin.font->fid);

     {
	int fwidth  = XTextWidth (TermWin.font, "MMMMMMMMMM", 10) / 10;
	int fheight = TermWin.font->ascent + TermWin.font->descent;

	if (fwidth == TermWin.fwidth && fheight == TermWin.fheight)
	  return;

	szHint.width_inc  = TermWin.fwidth  = fwidth;
	szHint.height_inc = TermWin.fheight = fheight;
     }

   TermWin.width  = TermWin.ncol * TermWin.fwidth;
   TermWin.height = TermWin.nrow * TermWin.fheight;

   szHint.min_width  = szHint.base_width  + szHint.width_inc;
   szHint.min_height = szHint.base_height + szHint.height_inc;

   szHint.width  = szHint.base_width  + TermWin.width;
   szHint.height = szHint.base_height + TermWin.height;

   szHint.flags = PMinSize|PResizeInc|PBaseSize|PWinGravity;

   XSetWMNormalHints (Xdisplay, TermWin.parent, &szHint);
   XResizeWindow (Xdisplay, TermWin.parent, szHint.width, szHint.height);

   resize_windows (szHint.width, szHint.height);
}
/*----------------------- end-of-file (C source) -----------------------*/
