/* X Window System interface for the XANT program */

#undef BSD_INCLUDES
#define BSD_INCLUDES
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <X11/Intrinsic.h>
#include <X11/Xos.h>
#include <X11/StringDefs.h>
#include <X11/Xatom.h>
#include <X11/Shell.h>
#include <X11/Xmd.h>
#include <X11/keysym.h>
#include "Tform.h"
#include "X3270.h"
#include "Gr3270.h"
#include "Title3270.h"
#include "keys.h"
#include "s3270.h"
#include "icon-big.bm"
#include "icon-small.bm"


/* Resources:

 Name		     Class		RepType		Default Value
 ----		     -----		-------		-------------
 extended	     Boolean		Boolean		True
 bracket	     Boolean		Boolean		True
 altcr		     Boolean		Boolean		False
 gaSupport	     Boolean		Boolean		True
 wmTitle	     Boolean		Boolean		True
 pfkeys		     Boolean		Boolean		False
 graphics	     Boolean		Boolean		True
 pvm		     Boolean		Boolean		False
 port	     	     Port		int		0
 title	             Title	        String		NULL
 iconGeometry        Geometry	        String		NULL
 zoomGeometry        Geometry	        String		NULL
 gaGeometry          Geometry	        String		NULL
 imageGeometry       Geometry	        String		NULL
 printCommand	     PrintCommand	String		NULL
 gateway	     Gateway		String		NULL
 secondary	     Secondary		String		NULL
 workstation	     Workstation	String		NULL
 adapter	     Adapter		int		0
 session	     Session		String		NULL
 hostname	     Hostname		String		NULL
*/

#define XtNextended	"extended"
#define XtNbracket	"bracket"
#define XtNaltcr	"altcr"
#define XtNgaSupport	"gaSupport"
#define XtNwmTitle	"wmTitle"
#define XtNpfkeys	"pfkeys"
#define XtNgraphics	"graphics"
#define XtNpvm		"pvm"
#define XtNport		"port"
#define XtNtitle	"title"
#define XtNiconGeometry "iconGeometry"
#define XtNzoomGeometry "zoomGeometry"
#define XtNgaGeometry	"gaGeometry"
#define XtNimageGeometry "imageGeometry"
#define XtNprintCommand "printCommand"
#define XtNgateway	"gateway"
#define XtNsecondary	"secondary"
#define XtNworkstation	"workstation"
#define XtNadapter	"adapter"
#define XtNsession	"session"
#define XtNhostname	"hostname"

#define XtCPort		"Port"
#define XtCTitle	"Title"
#define XtCGeometry	"Geometry"
#define XtCPrintCommand	"PrintCommand"
#define XtCGateway	"Gateway"
#define XtCSecondary	"Secondary"
#define XtCWorkstation	"Workstation"
#define XtCAdapter	"Adapter"
#define XtCSession	"Session"
#define XtCHostname	"Hostname"

#ifndef PRINTCMD
#ifdef _IBMR2
#define PRINTCMD "qprt -c"
#else
#ifdef AIX
#define PRINTCMD "print -cp"
#else
#define PRINTCMD "lpr"
#endif
#endif
#endif


/* External variables */
extern Boolean debug;		/* Debugging flag */
extern Boolean trace;		/* Tracing flag */
extern char etoa[];		/* EBCDIC to ASCII translation table */
extern char *progname;		/* The name of this program */

/* Global variables */
#ifdef blah
static XtAppContext context;	/* Application context */
#endif
static Display *dpy;		/* The current display */
static int scr;			/* Default screen */
static Atom wm_change_state;	/* Atom for WM_CHANGE_STATE */
static Widget toplevel;		/* Top level shell widget */
static Window topwin;		/* Top level window */
static Widget outer;		/* Outer form widget */
static Widget title;		/* Title bar */
static Widget text;		/* Main text widget */
static Window twin;		/* Main text window */
static Window iconwin;		/* Window for icon */
static Dimension icon_width, icon_height; /* Size of icon bitmap */
static char *icon_bits;		/* Data for icon bitmap */
static Pixmap icon;		/* Pixmap for icon */
static Cursor standard_curs;	/* Normal cursor for text window */
static Cursor special_curs;	/* Cursor for special mouse mode */
static char *screen;		/* Saved screen image */
static char *attrib;		/* Screen attributes */
static char *newscreen;		/* New screen data */
static char *newattrib;		/* New attribute data */
static int maxcols, maxrows;	/* Maximum screen size */
static int scols, srows, scrsize; /* Current size of the text area */
static int cursor;		/* Cursor position */
static Boolean cursvis;		/* True if cursor is visible */
static int minpos, maxpos;	/* Limits of screen update since last flush */
static int startcut, endcut;	/* Cut area */
static int mincut, maxcut;	/* Range of cut area */
static Boolean ding;		/* Ding pending flag */
static Boolean quitflag;	/* Quit requested */
static Boolean zoompend;	/* Zoom pending flag */
static Position zoomx, zoomy;	/* Location of zoomed window */
static Dimension zoomw, zoomh;	/* Size of zoomed window */
static int zoombits;		/* Bit values from XParseGeometry */
static Boolean mousing;		/* Special mouse mode flag */
static Boolean iconified;	/* Keeps track of iconified state */
static Boolean outputpending;	/* Output occurred while iconified */
static Boolean xsys;		/* True if "X System" should be indicated */
static Boolean inserting;	/* True if insert mode should be indicated */
static Boolean apl_mode;	/* True if keyboard is in APL mode */
static Boolean apl_paste;	/* True if pasting should use APL characters */
static Boolean indpending;	/* Indicator update pending */
static Boolean inwindow;	/* True if the pointer is in text window */
static Boolean focused;		/* True if we have the keyboard focus */
static Boolean mapped;		/* Turned on when top window is first mapped */
static long autotime;		/* Time when autoraise should be done */
static Modifiers mod_locks;	/* "Locked" Modifiers for KeyPress */
static Position xpos, ypos;	/* Position of text within the text window */
static Dimension cwidth, cheight; /* Size of a character cell */
static int un_position;		/* Offset of underline from top of cell */
static int un_thickness;	/* Thickness of underline */
static int m_specmouse, m_zoom, m_trace, m_ga; /* Menu numbers */
static Position def_position = 65535; /* A position that hasn't been set */
static Dimension def_dimension = ~0; /* A dimension that hasn't been set */

#define MINCOLS 80
#define MINROWS 24
#define IBORD 1
#define PEEKLEN 2

/* Application option settings */
static Boolean extended;	/* Use extended data streams */
static Boolean bracket;		/* Change square bracket translation */
static Boolean altcr;		/* Use alternate cursor */
static Boolean ga_support;	/* Use 3277-GA support */
static Boolean wmTitle;		/* Enable window manager title bars */
static Boolean pfkeys;		/* Display PF key menu */
static Boolean graphics;	/* Include 3179G-style graphics support */
static Boolean pvm;		/* Use a PVM/PC gateway instead of TELNET */
static int port;		/* Telnet port */
static char *titlestring;	/* String to use for window manager title */
static char *icon_geometry;	/* Icon geometry specification */
static char *zoom_geometry;	/* Zoomed window geometry specification */
static char *ga_geometry;	/* 3277-GA window geometry specification */
static char *image_geometry;	/* Image window geometry specification */
static char *print_command;	/* Print command to use for print-screen */
static char *session;		/* Session ID letter for API */
static char *dhostname;		/* Default host name to connect to */
static char *gateway;		/* PVM network gateway */
static char *secondary;		/* PVM secondary route */
static char *workstation;	/* Workstation name for PVM */
static int adapter;		/* LAN adapter number for PVM */

/* Option settings from X3270 widget */
static Boolean autoraise;	/* Auto raise flag */
static Boolean warp;		/* Warp at startup flag */
static Boolean dwarp;		/* De-iconify warp flag */
static Boolean zooming;		/* True if zoomed in */
static Dimension ucols, urows;	/* User-specified screen size */
static Pixel foreground, background;	 	    /* Colors */
static Pixel curscolor, backColor, foreColor; 	    /* More colors */
static Pixel pnColor, piColor, unColor, uiColor;    /* More colors */
static Pixel extDefault, extDefIntense, extBlue, extRed, extPink;
static Pixel extGreen, extTurquoise, extYellow, extWhite;
static char *tune;		/* Tune to play when beeping */
static XFontStruct *fontinfo;	/* Text font information */
static XFontStruct *bfontinfo;	/* Bold text font information */
static XFontStruct *zfontinfo;	/* Zoomed text font information */
static XFontStruct *zbfontinfo;	/* Zoomed bold text font information */
static XFontStruct *iconfont;	/* Font for icon window */
static char *nmstring;		/* String to use for internal titles */

static GC attgc[NUM_IC];	/* Graphics context for each attribute */
static GC attrevgc[NUM_IC];	/* Reverse video graphics contexts */
static GC cursgc;		/* Graphics context for drawing cursor */
static GC icongc, iconrevgc;	/* Graphics contexts for icon */

/* Forward and external routine declarations */
static void updscr(), updcut(), clearcurs(), drawcurs(), creategc(),
  redrawicon(), redrawind(), output_happened(), dozoom(), setmsize(),
  dokey(), resize(), expose(), action(), iconify(), newrange(), storecut(),
  play(), top_event(), outer_event(), icon_event(), text_event(), specmouse(),
  zoom(), settrace(), ga_switch(), quit();
static int toggle_mod(), calcpos(), getnum(), error_handler();
static char *cut();
extern Boolean s3270_key();


/* Parse X-related user options */
x_options(argc, argv, port_ret, extended_ret, graphics_ret, session_ret,
	  hostname_ret, pvm_ret, gateway_ret, secondary_ret, workstation_ret,
	  adapter_ret)
     int *argc;
     char *argv[];
     int *port_ret;
     Boolean *extended_ret, *graphics_ret, *pvm_ret;
     char **session_ret, **hostname_ret, **gateway_ret, **secondary_ret,
       **workstation_ret;
     int *adapter_ret;
{
#ifdef blah
  int targc;
  char **targv;
#endif
  register int i;
  Arg arglist[50];
  static XtCallbackRec keycalls[] =
    { {(XtCallbackProc) dokey, NULL}, {NULL, NULL} };
  static XtCallbackRec resizecalls[] =
    { {(XtCallbackProc) resize, NULL}, {NULL, NULL} };
  static XtCallbackRec exposecalls[] =
    { {(XtCallbackProc) expose, NULL}, {NULL, NULL} };
  static XtCallbackRec actioncalls[] =
    { {(XtCallbackProc) action, NULL}, {NULL, NULL} };

  static XrmOptionDescRec options[] = 
  {
    {"-ext",	"extended",		XrmoptionNoArg,  (caddr_t) "on"},
    {"-noext",	"extended",		XrmoptionNoArg,  (caddr_t) "off"},
    {"-bracket","bracket",		XrmoptionNoArg,  (caddr_t) "on"},
    {"-nobracket","bracket",		XrmoptionNoArg,  (caddr_t) "off"},
    {"-altcr",  "altcr",		XrmoptionNoArg,  (caddr_t) "on"},
    {"-noaltcr","altcr",		XrmoptionNoArg,  (caddr_t) "off"},
    {"-ga",	"gaSupport",		XrmoptionNoArg,  (caddr_t) "on"},
    {"-noga",	"gaSupport",		XrmoptionNoArg,  (caddr_t) "off"},
    {"-wmtitle","wmTitle",		XrmoptionNoArg,  (caddr_t) "on"},
    {"-nowmtitle","wmTitle",		XrmoptionNoArg,  (caddr_t) "off"},
    {"-pfkeys",	"pfkeys",		XrmoptionNoArg,  (caddr_t) "on"},
    {"-nopfkeys", "pfkeys",		XrmoptionNoArg,  (caddr_t) "off"},
    {"-graphics", "graphics",		XrmoptionNoArg,  (caddr_t) "on"},
    {"-nographics", "graphics",		XrmoptionNoArg,  (caddr_t) "off"},
    {"-pvm",	"pvm",			XrmoptionNoArg,	 (caddr_t) "on"},
    {"-nopvm",	"pvm",			XrmoptionNoArg,	 (caddr_t) "off"},
    {"-port",	"port",			XrmoptionSepArg, (caddr_t) NULL},
    {"-igeom",	"iconGeometry",		XrmoptionSepArg, (caddr_t) NULL},
    {"#",	"iconGeometry",		XrmoptionIsArg,  (caddr_t) NULL},
    {"-zgeom",	"zoomGeometry",		XrmoptionSepArg, (caddr_t) NULL},
    {"-gageom",	"gaGeometry",		XrmoptionSepArg, (caddr_t) NULL},
    {"-imgeom",	"imageGeometry",	XrmoptionSepArg, (caddr_t) NULL},
    {"-printcmd","printCommand",	XrmoptionSepArg, (caddr_t) NULL},
    {"-session", "session",		XrmoptionSepArg, (caddr_t) NULL},
    {"-gateway", "gateway",		XrmoptionSepArg, (caddr_t) NULL},
    {"-secondary", "secondary",		XrmoptionSepArg, (caddr_t) NULL},
    {"-workstation", "workstation",	XrmoptionSepArg, (caddr_t) NULL},
    {"-adapter", "adapter",		XrmoptionSepArg, (caddr_t) NULL},

    {"-bc",	"*backColor",		XrmoptionSepArg, (caddr_t) NULL},
    {"-fc",	"*foreColor",		XrmoptionSepArg, (caddr_t) NULL},
    {"-ms",	"*mouseColor",		XrmoptionSepArg, (caddr_t) NULL},
    {"-cursor",	"*cursor",		XrmoptionSepArg, (caddr_t) NULL},
    {"-sk",	"*speckey",		XrmoptionNoArg,  (caddr_t) "on"},
    {"-nosk",	"*speckey",		XrmoptionNoArg,  (caddr_t) "off"},
    {"-keyfile","*keyfile",		XrmoptionSepArg, (caddr_t) NULL},

    {"-cols",	"*x3270.cols",		XrmoptionSepArg, (caddr_t) NULL},
    {"-rows",	"*x3270.rows",		XrmoptionSepArg, (caddr_t) NULL},
    {"-pn",	"*x3270.pnColor",	XrmoptionSepArg, (caddr_t) NULL},
    {"-pi",	"*x3270.piColor",	XrmoptionSepArg, (caddr_t) NULL},
    {"-un",	"*x3270.unColor",	XrmoptionSepArg, (caddr_t) NULL},
    {"-ui",	"*x3270.uiColor",	XrmoptionSepArg, (caddr_t) NULL},
    {"-cr",	"*x3270.cursorColor",	XrmoptionSepArg, (caddr_t) NULL},
    {"-fxn",	"*x3270.font",		XrmoptionSepArg, (caddr_t) NULL},
    {"-fb",	"*x3270.boldFont",	XrmoptionSepArg, (caddr_t) NULL},
    {"-fzn",	"*x3270.zoomFont",	XrmoptionSepArg, (caddr_t) NULL},
    {"-fzb",	"*x3270.boldZoomFont",	XrmoptionSepArg, (caddr_t) NULL},
    {"-fi",	"*x3270.iconFont",	XrmoptionSepArg, (caddr_t) NULL},
    {"-ar",	"*x3270.autoRaise",	XrmoptionNoArg,  (caddr_t) "on"},
    {"-noar",	"*x3270.autoRaise",	XrmoptionNoArg,  (caddr_t) "off"},
    {"-W",	"*x3270.warp",		XrmoptionNoArg,  (caddr_t) "on"},
    {"-noW",	"*x3270.warp",		XrmoptionNoArg,  (caddr_t) "off"},
    {"-dw",	"*x3270.deIconifyWarp",	XrmoptionNoArg,  (caddr_t) "on"},
    {"-nodw",	"*x3270.deIconifyWarp",	XrmoptionNoArg,  (caddr_t) "off"},
    {"-z",	"*x3270.zoom",		XrmoptionNoArg,  (caddr_t) "on"},
    {"-noz",	"*x3270.zoom",		XrmoptionNoArg,  (caddr_t) "off"},
    {"-tune",	"*x3270.tune",		XrmoptionSepArg, (caddr_t) NULL},
    {"-scursor","*x3270.specialCursor",	XrmoptionSepArg, (caddr_t) NULL},
    {"-gcursor","*ga3270.gaCursor",	XrmoptionSepArg, (caddr_t) NULL},
    {"-nm",	"*title.title",		XrmoptionSepArg, (caddr_t) NULL},
  };

  static XtResource resources[] =
    {
      {XtNextended, XtCBoolean, XtRBoolean, sizeof(Boolean),
	 (Cardinal) &extended, XtRImmediate, (caddr_t) True},
      {XtNbracket, XtCBoolean, XtRBoolean, sizeof(Boolean),
	 (Cardinal) &bracket, XtRImmediate, (caddr_t) True},
      {XtNaltcr, XtCBoolean, XtRBoolean, sizeof(Boolean),
	 (Cardinal) &altcr, XtRImmediate, (caddr_t) False},
      {XtNgaSupport, XtCBoolean, XtRBoolean, sizeof(Boolean),
	 (Cardinal) &ga_support, XtRImmediate, (caddr_t) True},
      {XtNwmTitle, XtCBoolean, XtRBoolean, sizeof(Boolean),
	 (Cardinal) &wmTitle, XtRImmediate, (caddr_t) True},
      {XtNpfkeys, XtCBoolean, XtRBoolean, sizeof(Boolean),
	 (Cardinal) &pfkeys, XtRImmediate, (caddr_t) False},
      {XtNgraphics, XtCBoolean, XtRBoolean, sizeof(Boolean),
	 (Cardinal) &graphics, XtRImmediate, (caddr_t) True},
      {XtNpvm, XtCBoolean, XtRBoolean, sizeof(Boolean),
	 (Cardinal) &pvm, XtRImmediate, (caddr_t) False},
      {XtNport, XtCPort, XtRInt, sizeof(int),
	 (Cardinal) &port, XtRImmediate, (caddr_t) 0},
      {XtNtitle, XtCTitle, XtRString, sizeof(caddr_t),
	 (Cardinal) &titlestring, XtRString, NULL},
      {XtNiconGeometry, XtCGeometry, XtRString, sizeof(caddr_t),
	 (Cardinal) &icon_geometry, XtRString, NULL},
      {XtNzoomGeometry, XtCGeometry, XtRString, sizeof(caddr_t),
	 (Cardinal) &zoom_geometry, XtRString, NULL},
      {XtNgaGeometry, XtCGeometry, XtRString, sizeof(caddr_t),
	 (Cardinal) &ga_geometry, XtRString, NULL},
      {XtNimageGeometry, XtCGeometry, XtRString, sizeof(caddr_t),
	 (Cardinal) &image_geometry, XtRString, NULL},
      {XtNprintCommand, XtCPrintCommand, XtRString, sizeof(caddr_t),
	 (Cardinal) &print_command, XtRString, NULL},
      {XtNsession, XtCSession, XtRString, sizeof(caddr_t),
	 (Cardinal) &session, XtRString, NULL},
      {XtNhostname, XtCHostname, XtRString, sizeof(caddr_t),
	 (Cardinal) &dhostname, XtRString, NULL},
      {XtNgateway, XtCGateway, XtRString, sizeof(caddr_t),
	 (Cardinal) &gateway, XtRString, NULL},
      {XtNsecondary, XtCSecondary, XtRString, sizeof(caddr_t),
	 (Cardinal) &secondary, XtRString, NULL},
      {XtNworkstation, XtCWorkstation, XtRString, sizeof(caddr_t),
	 (Cardinal) &workstation, XtRString, NULL},
      {XtNadapter, XtCAdapter, XtRInt, sizeof(int),
	 (Cardinal) &adapter, XtRImmediate, (caddr_t) 0},
    };

#ifdef blah
  /*...The following code doesn't work because the Toolkit's popup
    initialization routine calls XtAddActions instead of XtAppAddActions.
    This causes the MenuPopup action to be registered in the global
    default context instead of in our application context.  */

  /* Copy the argument list */
  targc = *argc;
  if (!(targv = (char **) malloc((targc + 1) * sizeof(*targv))))
    error("Not enough memory");
  for (i = 0; i < targc; i++)
    targv[i] = argv[i];
  targv[targc] = NULL;

  /* Initialize the X toolkit */
  XtToolkitInitialize();
  context = XtCreateApplicationContext();
  dpy = XtOpenDisplay(context, (String) NULL, progname, "Xant", options,
		      XtNumber(options), argc, argv);
  if (!dpy) error("Can't open display");

  i = 0;
  XtSetArg(arglist[i], XtNargc, targc); i++;
  XtSetArg(arglist[i], XtNargv, targv); i++;
  toplevel = XtAppCreateShell((String) NULL, "Xant",
			      applicationShellWidgetClass, dpy, arglist, i);
#else
  toplevel = XtInitialize("xant", "Xant", options, XtNumber(options),
			  argc, argv);
  dpy = XtDisplay(toplevel);
#endif

  scr = DefaultScreen(dpy);
  topwin = twin = (Window) NULL;
  XSetErrorHandler(error_handler);
  wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", False);

  /* Get the application resources */
  XtGetApplicationResources(toplevel, (caddr_t) NULL, resources,
			    XtNumber(resources), NULL, 0);

#ifdef TESTING
  /* This is a quick-and-dirty way to simulate mono mode for testing */
  if (gateway && !strcmp(gateway, "mono"))
    DefaultVisual(dpy, scr)->map_entries = 2;
#endif

  /* Create an outer form to hold the other widgets */
  outer = XtCreateManagedWidget("form", tformWidgetClass, toplevel, NULL, 0);

  /* Make a title bar */
  title = XtCreateManagedWidget("title", title3270WidgetClass, outer, NULL, 0);

  /* Put the main text window below it */
  i = 0;
  XtSetArg(arglist[i], XtNkeyProc, keycalls); i++;
  XtSetArg(arglist[i], XtNresizeProc, resizecalls); i++;
  XtSetArg(arglist[i], XtNexposeProc, exposecalls); i++;
  XtSetArg(arglist[i], XtNactionProc, actioncalls); i++;
  text = graphics ? XtCreateManagedWidget("x3270", gr3270WidgetClass, outer,
					  arglist, i)
    : XtCreateManagedWidget("x3270", x3270WidgetClass, outer, arglist, i);
  XtSetKeyboardFocus(outer, text);

  /* Read all the resources from the text widget */
  i = 0;
  XtSetArg(arglist[i], XtNbackground,	&background);	i++;
  XtSetArg(arglist[i], XtNforeground,	&foreground);	i++;
  XtSetArg(arglist[i], XtNbackColor,	&backColor);	i++;
  XtSetArg(arglist[i], XtNforeColor,	&foreColor);	i++;
  XtSetArg(arglist[i], XtNpnColor,	&pnColor);	i++;
  XtSetArg(arglist[i], XtNpiColor,	&piColor);	i++;
  XtSetArg(arglist[i], XtNunColor,	&unColor);	i++;
  XtSetArg(arglist[i], XtNuiColor,	&uiColor);	i++;
  XtSetArg(arglist[i], XtNextDefault,	&extDefault);	i++;
  XtSetArg(arglist[i], XtNextDefIntense,&extDefIntense);i++;
  XtSetArg(arglist[i], XtNextBlue,	&extBlue);	i++;
  XtSetArg(arglist[i], XtNextRed,	&extRed);	i++;
  XtSetArg(arglist[i], XtNextPink,	&extPink);	i++;
  XtSetArg(arglist[i], XtNextGreen,	&extGreen);	i++;
  XtSetArg(arglist[i], XtNextTurquoise,	&extTurquoise);	i++;
  XtSetArg(arglist[i], XtNextYellow,	&extYellow);	i++;
  XtSetArg(arglist[i], XtNextWhite,	&extWhite);	i++;
  XtSetArg(arglist[i], XtNcursorColor,	&curscolor);	i++;
  XtSetArg(arglist[i], XtNfont,		&fontinfo);	i++;
  XtSetArg(arglist[i], XtNboldFont,	&bfontinfo);	i++;
  XtSetArg(arglist[i], XtNzoomFont,	&zfontinfo);	i++;
  XtSetArg(arglist[i], XtNboldZoomFont,	&zbfontinfo);	i++;
  XtSetArg(arglist[i], XtNiconFont,	&iconfont);	i++;
  XtSetArg(arglist[i], XtNcols,		&ucols);	i++;
  XtSetArg(arglist[i], XtNrows,		&urows);	i++;
  XtSetArg(arglist[i], XtNautoRaise,	&autoraise);	i++;
  XtSetArg(arglist[i], XtNwarp,		&warp);		i++;
  XtSetArg(arglist[i], XtNdeIconifyWarp,&dwarp);	i++;
  XtSetArg(arglist[i], XtNzoom,		&zooming);	i++;
  XtSetArg(arglist[i], XtNtune,		&tune);		i++;
  XtSetArg(arglist[i], XtNcursor,	&standard_curs);i++;
  XtSetArg(arglist[i], XtNspecialCursor,&special_curs); i++;
  XtGetValues(text, arglist, i);

  s3270_ga(ga_support);
  if (bracket) 
    {
      etoa[0xAD] = '[';
      etoa[0xBD] = ']';
    }

  /* Make outer form background the same color as text background */
  XtSetArg(arglist[0], XtNbackground, background);
  XtSetValues(outer, arglist, 1);

  /* Make sure the fonts were found */
  if (!fontinfo)   error("Could not find normal font");
  if (!bfontinfo)  error("Could not find bold font");
  if (!zfontinfo)  error("Could not find zoomed font");
  if (!zbfontinfo) error("Could not find bold zoomed font");
  if (!iconfont)   error("Could not find icon font");

  /* Create a pop-up menu */
  menu_init(title);
  m_specmouse = menu_add("  Special Mouse", specmouse);
  m_zoom = menu_add("  Zoom", zoom);
  if (debug && trace) m_trace = menu_add("+ Trace", settrace);
  else if (debug) m_trace = menu_add("  Trace", settrace);
  if (ga_support) m_ga = menu_add("+ 3277-GA", ga_switch);
  else m_ga = menu_add("  3277-GA", ga_switch);
  (void) menu_add("  Quit", quit);
  menu_done();

  /* Pass back resource values */
  *port_ret = port;
  *extended_ret = extended;
  *graphics_ret = graphics;
  *session_ret = session;
  *hostname_ret = dhostname;
  *pvm_ret = pvm;
  *gateway_ret = gateway;
  *secondary_ret = secondary;
  *workstation_ret = workstation;
  *adapter_ret = adapter;
}  


/* Initialize X environment.  This is separate from x_options so that a
   window isn't created until after all the options have been examined.  
   The number of columns and rows in the text area is returned in the
   ncols and nrows variables, and a file descriptor for select() calls
   is returned as the function value.  */

int x_init(hostname, ncols, nrows)
     char *hostname;
     int *ncols, *nrows;
{
  register int i;
  Arg arglist[10];
  Boolean iconic;
  int iconx, icony, format, nitems, bytes_after, max_size;
  Atom type;
  CARD32 *icon_size;
  Dimension bord, width, height, oldwidth, oldheight, theight;
  unsigned long valuemask;
  XSetWindowAttributes wa;
  unsigned long gcmask;
  XGCValues gcv;

  /* If title string was specified using the -nm option, it will be
     stored as the value of the "title" resource in the title3270
     widget.  If no value was specified, then set it to the host
     name.  This value will be used for the title in XANT's title
     bar and in the string that is drawn at the bottom of the icon
     window (by XANT, not by the window manager).  */

  XtSetArg(arglist[0], XtNtitle, &nmstring);
  XtGetValues(title, arglist, 1);
  if (!nmstring)
    {
      nmstring = hostname;
      XtSetArg(arglist[0], XtNtitle, nmstring);
      XtSetValues(title, arglist, 1);
    }

  /* If -title was specified, the value will already be loaded in the
     "titlestring" variable.  If not specified, then combine the name
     used by the shell widget (probably "xant") with nmstring. If the
     shell widget value is null (unlikely), use the program name.  */

  if (!titlestring)
    {
      char *oldtitle;
      XtSetArg(arglist[0], XtNtitle, &titlestring);
      XtGetValues(toplevel, arglist, 1);
      oldtitle = titlestring ? titlestring : progname;
      titlestring = XtMalloc(strlen(oldtitle) + strlen(nmstring) + 2);
      sprintf(titlestring, "%s %s", oldtitle, nmstring);
    }
  
  /* Initialize some global variables */
  maxcols = ucols < MINCOLS ? MINCOLS : ucols;
  maxrows = urows < MINROWS ? MINROWS : urows;
  scols = srows = scrsize = 0;
  cursor = 0;
  cursvis = False;
  minpos = maxpos = 0;
  startcut = endcut = 0;
  mincut = maxcut = -1;
  ding = False;
  quitflag = False;
  zoompend = False;
  zoomx = zoomy = def_position;
  zoomw = zoomh = def_dimension;
  zoombits = 0;
  mousing = False;
  outputpending = False;
  xsys = True;
  inserting = False;
  apl_mode = False;
  apl_paste = False;
  indpending = False;
  inwindow = False;
  focused = False;
  mapped = False;
  autotime = 0;
  mod_locks = 0;
  xpos = ypos = IBORD;

  XtSetArg(arglist[0], XtNiconic, &iconic);
  XtGetValues(toplevel, arglist, 1);
  iconified = iconic;

  /* If zoom option was specified, then swap fonts */
  if (zooming) 
    {
      register XFontStruct *tfont;
      tfont = fontinfo; fontinfo = zfontinfo; zfontinfo = tfont;
      tfont = bfontinfo; bfontinfo = zbfontinfo; zbfontinfo = tfont;
      menu_change(m_zoom, "+ Zoom");
    }
  creategc(False);

  /* Parse zoom geometry specifications */
  if (zoom_geometry)
    {
      /* Position and Dimension are short in X11R4, so we can't pass
	 them to XParseGeometry which is expecting ints.  */

      int zoom_x, zoom_y;
      unsigned int zoom_w, zoom_h;
      zoom_x = zoomx; zoom_y = zoomy; zoom_w = zoomw; zoom_h = zoomh;
      zoombits = XParseGeometry(zoom_geometry, &zoom_x, &zoom_y,
				&zoom_w, &zoom_h);
      zoomx = zoom_x; zoomy = zoom_y; zoomw = zoom_w; zoomh = zoom_h;
    }

  /* Get maximum icon size from window manager */
  nitems = 0;
  (void) XGetWindowProperty(dpy, RootWindow(dpy, scr), XA_WM_ICON_SIZE,
			    2L, 2L, False, AnyPropertyType,
			    &type, &format, &nitems, &bytes_after,
			    (char **) &icon_size);

  max_size = icon_big_width;
  if (nitems > 1)
    {
      if (icon_size[0] < max_size) max_size = icon_size[0];
      if (icon_size[1] < max_size) max_size = icon_size[1];
    }

  /* If icon bitmap is too big, use the small version */
  if (icon_big_width > max_size || icon_big_height > max_size)
    {
      icon_width = icon_small_width;
      icon_height = icon_small_height;
      icon_bits = icon_small_bits;
    }
  else
    {
      icon_width = icon_big_width;
      icon_height = icon_big_height;
      icon_bits = icon_big_bits;
    }
  
  /* Check for icon geometry specification */
  iconx = -1;
  icony = -1;
  if (icon_geometry)
    {
      int gbits;
      char default_geometry[40];
      if (icon_geometry[0] == '#') icon_geometry++;
      sprintf(default_geometry, "%dx%d+0+0", icon_width, icon_height);
      gbits = XGeometry(dpy, scr, icon_geometry, default_geometry,
			1, 1, 1, 0, 0, &iconx, &icony,
			(int *) &width, (int *) &height);
      if (!(gbits & XValue)) iconx = -1;
      if (!(gbits & YValue)) icony = -1;
    }

  /* Create the icon window */
  if (!(icon = XCreatePixmapFromBitmapData(dpy, RootWindow(dpy, scr),
					   icon_bits, icon_width, icon_height,
					   foreground, background,
					   DefaultDepth(dpy, scr))))
    error("Can't make a pixmap for icon");
  
  wa.background_pixel = background;
  wa.border_pixel = BlackPixel(dpy, scr);
  wa.override_redirect = True;
  wa.event_mask = ButtonPressMask | ExposureMask | StructureNotifyMask;
  valuemask = CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWEventMask;

  iconwin = XCreateWindow(dpy, RootWindow(dpy, scr), iconx < 0 ? 0 : iconx,
			  icony < 0 ? 0 : icony,
			  icon_width, icon_height, 1,
			  DefaultDepth(dpy, scr), InputOutput,
			  CopyFromParent, valuemask, &wa);

  gcmask = GCForeground | GCBackground | GCFont;
  gcv.foreground = foreground;
  gcv.background = background;
  gcv.font = iconfont->fid;
  icongc = XCreateGC(dpy, iconwin, gcmask, &gcv);

  gcv.foreground = background;
  gcv.background = foreground;
  iconrevgc = XCreateGC(dpy, iconwin, gcmask, &gcv);

  gcmask = GCForeground | GCBackground;
  gcv.foreground = curscolor;
  gcv.background = background;
  cursgc = XtGetGC(text, gcmask, &gcv);

  /* Set top level resource values */
  i = 0;
  XtSetArg(arglist[i], XtNiconWindow, iconwin); i++;
  XtSetArg(arglist[i], XtNiconName, titlestring); i++;
  if (iconx >= 0) { XtSetArg(arglist[i], XtNiconX, iconx); i++; }
  if (icony >= 0) { XtSetArg(arglist[i], XtNiconY, icony); i++; }
  XtSetArg(arglist[i], XtNallowShellResize, True); i++;
  XtSetArg(arglist[i], XtNinput, True); i++;
  XtSetArg(arglist[i], XtNtitle, wmTitle ? titlestring : ""); i++;
  XtSetValues(toplevel, arglist, i);
  setmsize(&width, &height);
  i = 0;
  XtSetArg(arglist[i], XtNwidth, width); i++;
  XtSetArg(arglist[i], XtNheight, height); i++;
  XtSetValues(toplevel, arglist, i);
  XtSetValues(outer, arglist, i);
  
  /* Handle some window events */
  XtAddEventHandler(toplevel, StructureNotifyMask, False, top_event, NULL);

  XtAddEventHandler(outer, FocusChangeMask, False, outer_event, NULL);

  XtAddEventHandler(text, EnterWindowMask | LeaveWindowMask, False,
		    text_event, NULL);
  
  /* If the geometry wasn't specified, the window manager may create
     a rubber band outline when the window is first mapped and allow
     the user to set the size.  We need the window size to determine
     the number of rows and columns to pass on the initial telnet
     connection.  Therefore, we must wait until the window is mapped
     before we can go any further.  However, if the "-iconic" option
     was specified, it may be a long time before the first mapping,
     and we can't wait that long.  So, if the geometry wasn't specified,
     but "-iconic" was specified, we'll use the size specified by the
     "-rows" and "-cols" variables (or the minimum size if that wasn't
     specified).  */

  XtRealizeWidget(toplevel);
  if (!iconic)
    while (!mapped)
      {
	XEvent event;
	XtNextEvent(&event);
	if (event.xany.window == iconwin) icon_event(&event);
	else XtDispatchEvent(&event);
      }

  twin = XtWindow(text);
  if (!twin) error("Text window didn't get created");
  if (!topwin) 
    {
      topwin = XtWindow(toplevel);
      if (!topwin) error("Top level window didn't get created");
    }

  /* Now that window is mapped, get its size (possibly chosen interactively) */
  oldwidth = width;
  oldheight = height;
  i = 0;
  XtSetArg(arglist[i], XtNwidth, &width); i++;
  XtSetArg(arglist[i], XtNheight, &height); i++;
  XtGetValues(outer, arglist, i);
  if (width < oldwidth) width = oldwidth;
  if (height < oldheight) height = oldheight;

  /* Figure out how much is left for text by subtracting title and borders */
  i = 0;
  XtSetArg(arglist[i], XtNheight, &theight); i++;
  XtSetArg(arglist[i], XtNborderWidth, &bord); i++;
  XtGetValues(title, arglist, i);
  if (theight && height > theight + 2 * bord) height -= theight + 2 * bord;

  XtSetArg(arglist[0], XtNborderWidth, &bord);
  XtGetValues(text, arglist, 1);
  if (height > 2 * bord) height -= 2 * bord;
  if (width > 2 * bord) width -= 2 * bord;

  /* Finally, compute how many columns and rows of characters will fit */
  *ncols = width / cwidth;
  if (*ncols < MINCOLS) *ncols = MINCOLS;
  *nrows = height / cheight;
  if (*nrows < MINROWS) *nrows = MINROWS;

  return ConnectionNumber(dpy);
}


/* This routine is called after the actual screen size is determined */
x_setup(ncols, nrows)
     int ncols, nrows;
{
  register int i;
  Arg arglist[10];
  Dimension width, height;

  scols = maxcols = ncols;
  srows = maxrows = nrows;
  scrsize = scols * srows;
  image_init(dpy, scr, nmstring, image_geometry);
  ga_init(dpy, scr, nmstring, scrsize, ga_geometry);
  graph_init(dpy, scr, text, fontinfo->fid);

  /* Make sure that the size of the text window is correct */
  width = maxcols * cwidth + 2 * IBORD;
  height = maxrows * cheight + 2 * IBORD;
  i = 0;
  XtSetArg(arglist[i], XtNwidth, width); i++;
  XtSetArg(arglist[i], XtNheight, height); i++;
  XtSetValues(text, arglist, i);

  setmsize(&width, &height);
  xpos = IBORD;
  ypos = IBORD;

  /* Warp the mouse if requested */
  if (warp) XWarpPointer(dpy, None, twin, 0, 0, 0, 0, width / 2, height / 2);

  /* Allocate some storage for screen and initialize it */
  if (!(screen = (char *) malloc(scrsize)) ||
      !(attrib = (char *) malloc(scrsize)) ||
      !(newscreen = (char *) malloc(scrsize)) ||
      !(newattrib = (char *) malloc(scrsize)))
    error("Not enough memory");
  memset(screen, ' ', scrsize);
  memset(attrib, DEFAULT_CA, scrsize);
  memset(newscreen, ' ', scrsize);
  memset(newattrib, DEFAULT_CA, scrsize);
  minpos = scrsize;
}


/* Process any pending X events.  Return -1 if exit requested, 1 if
   something was found to process, and 0 otherwise.   */

int x_check() 
{
  XEvent event;
  int retval;

  retval = 0;
  while (XtPending()) 
    {
      XtNextEvent(&event);
      if (event.xany.window == iconwin) icon_event(&event);
      else
        {
	  /* Add "locked" modifiers to get additional translations */
          if (event.xany.type == KeyPress) event.xkey.state ^= mod_locks;
          XtDispatchEvent(&event);
        }
      retval = 1;
    }
  if (quitflag) return -1;
  return retval;
}


/* Write text at some position on the screen */
x_write(pos, string, attr, length, capresent)
     int pos;			/* Screen position */
     char *string;		/* Text string */
     char *attr;		/* Attributes */
     int length;		/* Length of text and attributes */
     Boolean capresent;		/* True if character attributes are present */
{
  int len2;
  register int j;
  char mask;

  if (pos >= scrsize) return 0;
  output_happened();
  if (length > scrsize) length = scrsize;
  len2 = pos + length - scrsize;
  if (len2 > 0)			/* Wrap past end of screen */
    {
      length -= len2;
      x_write(0, string ? &string[length] : NULL,
	      capresent ? &attr[length] : &attr[0],
	      len2, capresent);
    }
  if (string) memcpy(&newscreen[pos], string, length);
  else memset(&newscreen[pos], ' ', length);

  /* Throw away color info on monochrome displays, leaving just the intensity
     part.  This makes updscr more efficient.  */

  if (DisplayCells(dpy, scr) < 3) mask = HI_MASK | IC_INTENSE;
  else mask = HI_MASK | COLOR_MASK;

  if (capresent)
    for (j = 0; j < length; j++) newattrib[pos + j] = attr[j] & mask;
  else memset(&newattrib[pos], attr[0] & mask, length);

  if (pos < minpos) minpos = pos;
  if (pos + length > maxpos) maxpos = pos + length;
  return 0;
}


/* Clear the screen and set new size */
x_clear(ncols, nrows)
     int ncols, nrows;
{
  if (ncols > maxcols || nrows > maxrows) error("Screen size is too big");
  output_happened();
  scols = ncols;
  srows = nrows;
  scrsize = scols * srows;
  memset(screen, ' ', scrsize);
  memset(attrib, DEFAULT_CA, scrsize);
  memset(newscreen, ' ', scrsize);
  memset(newattrib, DEFAULT_CA, scrsize);
  minpos = scrsize;
  maxpos = 0;
  cursvis = False;
  XClearWindow(dpy, twin);
  graph_expose(0, 0, 65535, 65535);
}


/* Move the cursor to a new position */
x_cursor(pos)
     int pos;
{
  /* Remove cursor from old position if necessary */
  if (pos >= scrsize) return 0;
  if (cursvis && pos != cursor) clearcurs();

  /* Remember new cursor position */
  cursor = pos;
  return 0;
}


/* Set system indicator */
x_sys(val)
     int val;
{
  if (xsys == val) return 0;
  xsys = val;
  indpending = True;
  clearcurs();
  return 0;
}


/* Set inserting indicator */
x_inserting(val)
     Boolean val;
{
  if (inserting == val) return 0;
  inserting = val;
  redrawind();
  if (!xsys) drawcurs();
  XFlush(dpy);
  return 0;
}


/* Indicate whether the cursor is in a window that will accept keystrokes */
x_inwindow(val)
     int val;
{
  if (inwindow == val) return 0;
  inwindow = val;
  drawcurs();
  return 0;
}


/* Flush out display requests.  Return -1 if work was done, positive value
   for number for seconds to wait before next call, or 0 otherwise.  */
int x_flush()
{
  register int row, col, len;
  int endrow, maxrow, startpos, endpos;
  
  if (zoompend) dozoom();
  else if (graph_flush()) /*EMPTY*/;
  else if (minpos != scrsize) 
    {
      row = minpos / scols;
      col = minpos % scols;
      endrow = row + 2;		/* Take a rest every few rows */
      maxrow = (maxpos - 1) / scols;
      if (endrow > maxrow + 1) endrow = maxrow + 1;
  
      for (; row < endrow; row++)
	{
	  if (row == maxrow) len = (maxpos - 1) % scols - col + 1;
	  else len = scols - col;
	  startpos = col + row * scols;
	  endpos = startpos + len;

	  /* Check for overlap with cut range */
	  if (maxcut > mincut && startpos < maxcut && endpos > mincut)
	    {
	      if (startpos < mincut) 
		{
		  updscr(col, row, startpos, mincut);
		  col += mincut - startpos;
		  startpos = mincut;
		}
	      if (endpos > maxcut)
		{
		  updcut(col, row, startpos, maxcut);
		  col += maxcut - startpos;
		  updscr(col, row, maxcut, endpos);
		}
	      else updcut(col, row, startpos, endpos);
	    }
	  else updscr(col, row, startpos, endpos);
	  col = 0;
	}
      minpos = row * scols;
      if (minpos >= maxpos) 
	{
	  maxpos = 0;
	  minpos = scrsize;
	}
    }
  else if (!cursvis) drawcurs();
  else if (ding) 
    {
      if (tune) play(tune);
      else XBell(dpy, 0);
      ding = False;
    }
  else if (indpending) redrawind();
  else if (autotime) 
    {
      long raisetime;
      raisetime = autotime - time((long *) 0);
      if (raisetime > 0) return raisetime;
      autotime = 0;
      XRaiseWindow(dpy, topwin);
      if (topwin != XtWindow(toplevel))
	XRaiseWindow(dpy, XtWindow(toplevel));
    }
  else return 0;
  XFlush(dpy);
  return -1;
}


/* Ding the bell.  If "defer" is true then dinging happens at flush time.  */
x_ding(defer)
     Boolean defer;
{
  if (defer) ding = True;
  else XBell(dpy, 0);
  output_happened();
}


/* Retrieve some screen information */
x_parms(widthmm, width, heightmm, height, charwidth, charheight, mono)
     int *widthmm, *width, *heightmm, *height, *charwidth, *charheight;
     Boolean *mono;
{
  *widthmm = DisplayWidthMM(dpy, scr);
  *width = DisplayWidth(dpy, scr);
  *heightmm = DisplayHeightMM(dpy, scr);
  *height = DisplayHeight(dpy, scr);
  *charwidth = cwidth;
  *charheight = cheight;
  *mono = (DisplayCells(dpy, scr) < 3);

  /* In older versions of AIX V3, the DisplayWidthMM and DisplayHeightMM
     are set by default to 400, which is incorrect.  Also, on an Xstation
     120, the values are improperly set to 577x288.  The hft dimensions
     can be set to the correct values using smit, but that won't work for
     an Xstation.  In any case, we'll stick in some better values.  */

  if (*width == 1280 && *height == 1024 &&
      (*widthmm == 400 && *heightmm == 400 ||
       *widthmm == 577 && *heightmm == 288))
    {
      *widthmm = 355;
      *heightmm = 284;
    }
}


/* Copy ASCII screen data to a buffer for use by API */
x_getscr(startpos, len, buff)
     int startpos, len;
     char *buff;
{
  int len2;
  if (startpos < 0 || startpos >= scrsize || len <= 0) return 0;
  if (len > scrsize) len = scrsize;
  len2 = startpos + len - scrsize;
  if (len2 > 0) len -= len2;	/* Handle wrap around end of screen */
  memcpy(buff, &newscreen[startpos], len);
  if (len2 > 0) memcpy(&buff[len], newscreen, len2);
  return 0;
}


/* Print the screen */
x_print_screen()
{
  char *cutbuff, tempfile[25], *tempname, *mktemp(), prcmd[500];
  int pfile;

  cutbuff = cut(0, scrsize);
  if (!cutbuff) return 0;
  (void) strcpy(tempfile, "/tmp/xant.scrXXXXXX");
  if (print_command &&
      strlen(print_command) + strlen(tempfile) + 2 > sizeof prcmd)
    error("Print command is too long");
  tempname = mktemp(tempfile);
  pfile = open(tempname, O_RDWR | O_CREAT | O_TRUNC, 0666);
  if (pfile < 0) errorm("Can't open temporary file");
      if (write(pfile, cutbuff, strlen(cutbuff)) < 0)
    errorm("Write to print file failed");
  close(pfile);
  sprintf(prcmd, "%s %s", print_command ? print_command : PRINTCMD,
	  tempname);
  (void) system(prcmd);
  unlink(tempname);
  XtFree(cutbuff);
  return 0;
}


/* Set mouse mode */
x_mouse(val)
     Boolean val;
{
  if (mousing == val) return 0;
  mousing = val;
  if (mousing)
    {
      menu_change(m_specmouse, "+ Special Mouse");
      XDefineCursor(dpy, twin, special_curs);
    }
  else
    {
      menu_change(m_specmouse, "  Special Mouse");
      XDefineCursor(dpy, twin, standard_curs);
    }      
  return 0;
}


/* Close the X window */
x_close()
{
  if (dpy) XCloseDisplay(dpy);
}


/* Print a help message */
x_usage()
{
  fprintf(stderr, "\
usage: %s [-help] [-display hostname:number.screen] [-d] [-t] [-ext] \\\n\
  [-sk] [-ga] [-ar] [-z] [-dw] [-W] [-iconic] [-rv] [-bracket] \\\n\
  [-altcr] [-wmtitle] [-graphics] [-port port] [-bw border_width] \\\n\
  [-cols columns] [-rows rows] [-fn normal_font] [-fxn text_font] \\\n\
  [-fb bold_font] [-fzn zoomed_font] [-fzb bold_zoom_font] \\\n\
  [-fi icon_font] [-fc foreground_color] [-bc background_color] \\\n\
  [-pn protected_normal_color] [-pi protected_intense_color] \\\n\
  [-un unprotected_normal_color] [-ui unprotected_intense_color] \\\n\
  [-bd border_color] [-ms mouse_color] [-cr cursor_color] \\\n\
  [-cursor cursor_name] [-scursor special_cursor_name] \\\n\
  [-gcursor ga_cursor_name] [-geom [width]x[height][[+-]xoff[[+-]yoff]]] \\\n\
  [-igeom [+-]xoff[[+-]yoff]] [-zgeom zoom_geometry] \\\n\
  [-gageom 3277ga_geometry] [-imgeom image_geometry] \\\n\
  [-printcmd command] [-keyfile filename] [-session session_ID_letter] \\\n\
  [-title window_manager_title] [-nm xant_title] \\\n\
  [-pvm] [-gateway pvm_gateway] [-secondary secondary_route] \\\n\
  [-workstation workstation_name] [-adapter lan_adapter_number] \\\n\
  hostname\n", progname);
}



/*** Private routines ***/


/* Update a single row of the screen with new data */
static void updscr(col, row, startpos, endpos)
     int col, row, startpos, endpos;
{
#define mismatch(pos)			\
  (screen[pos] != newscreen[pos] ||	\
     (attrib[pos] != attr &&		\
        (screen[pos] != ' ' || (attr & HI_MASK) || (attrib[pos] & HI_MASK))))

  register int pos, j;
  for (pos = startpos; pos < endpos;) 
    {
      register char attr = newattrib[pos];

      /* Find a character that needs to be updated on the screen */
      if (mismatch(pos))
	{
	  /* Scan ahead for an attribute change or another match */
	  for (j = pos + 1; j < endpos; j++)
	    {
	      register int k;

	      /* Stop here if new character has a different attribute */
	      if (attr != newattrib[j]) break;

	      /* If next character doesn't match then extend the redraw area */
	      if (mismatch(j)) continue;

	      /* The next character matches, so we don't have to redraw it.
	         However, if the matched area is very short, it's more
		 efficient to redraw than break up the calls to
		 XDrawImageString.  */

	      for (k = j + 1; k < endpos && k - j <= PEEKLEN; k++)
		{
		  if (attr != newattrib[k]) break;
		  if (mismatch(k))
		    {
		      /* Found another mismatch a few characters ahead */
		      j = k;
		      goto continue_j;
		    }
		}
	      break;
	    continue_j:;
	    }

	  /* Write the new string to display */
	  XDrawImageString(dpy, twin, (attr & HI_MASK) == HI_REVERSE ?
			   attrevgc[attr & COLOR_MASK] :
			   attgc[attr & COLOR_MASK],
			   (int) (xpos + (col + pos - startpos) * cwidth),
			   (int) (ypos + row * cheight + fontinfo->ascent),
			   &newscreen[pos], j - pos);
	  if ((attr & HI_MASK) == HI_UNDER)
	    XFillRectangle(dpy, twin, attgc[attr & COLOR_MASK],
			   (int) (xpos + (col + pos - startpos) * cwidth),
			   (int) (ypos + row * cheight + un_position),
			   cwidth * (j - pos), un_thickness);
	  if (cursor >= pos && cursor < j) cursvis = False;
	  pos = j;
	}
      else pos++;
    }
  memcpy(&screen[startpos], &newscreen[startpos], endpos - startpos);
  memcpy(&attrib[startpos], &newattrib[startpos], endpos - startpos);
}


/* Same as updscr, except that the text is within the cut buffer */
static void updcut(col, row, startpos, endpos)
     int col, row, startpos, endpos;
{
  register int pos;
  for (pos = startpos; pos < endpos; pos++) 
    {
      register char attr;

      /* Force reverse video */
      attr = (newattrib[pos] & ~HI_MASK) | HI_REVERSE;

      /* Don't redisplay if it's already correct */
      if (screen[pos] != newscreen[pos] || attrib[pos] != attr) 
	{
	  XDrawImageString(dpy, twin, attrevgc[attr & COLOR_MASK],
			   (int) (xpos + (col + pos - startpos) * cwidth),
			   (int) (ypos + row * cheight + fontinfo->ascent),
			   &newscreen[pos], 1);

	  if (cursor == pos) cursvis = False;
	  screen[pos] = newscreen[pos];
	  attrib[pos] = attr;
	}
    }
}


/* Clear the cursor */
static void clearcurs()
{
  XDrawImageString(dpy, twin, (attrib[cursor] & HI_MASK) == HI_REVERSE ?
		   attrevgc[attrib[cursor] & COLOR_MASK] :
		   attgc[attrib[cursor] & COLOR_MASK],
		   (int) (xpos + (cursor % scols) * cwidth),
		   (int) (ypos + (cursor / scols) * cheight +
			  fontinfo->ascent),
		   &screen[cursor], 1);
  if ((attrib[cursor] & HI_MASK) == HI_UNDER)
    XFillRectangle(dpy, twin, attgc[attrib[cursor] & COLOR_MASK],
		   (int) (xpos + (cursor % scols) * cwidth),
		   (int) (ypos + (cursor / scols) * cheight + un_position),
		   cwidth, un_thickness);
  cursvis = False;
}


/* Re-draw the cursor */
static void drawcurs()
{
  int x, y, curspos, cursheight;

  if (!scrsize || cheight < 2) return;
  if (cursvis) clearcurs();
  cursvis = True;
  x = xpos + (cursor % scols) * cwidth;
  y = ypos + (cursor / scols) * cheight;

  /* Figure out the dimensions for an underline cursor */
  curspos = un_position;
  cursheight = cheight / 6;
  if (cursheight < un_thickness) cursheight = un_thickness;
  else if (cursheight > un_thickness)
    {
      curspos = cheight - cursheight;
      if (curspos > un_position) curspos = un_position;
    }
  
  /* Don't let the cursor go outside of the character cell */
  if (curspos >= cheight) curspos = cheight - 1;
  if (curspos + cursheight > cheight) cursheight = cheight - curspos;
  if (cursheight <= 0 || cursheight > cheight) cursheight = 1;
  
  /* If pointer is outside window and we're not focused, use box outline */
  if (!inwindow && !focused)
    XDrawRectangle(dpy, twin, (attrib[cursor] & HI_MASK) == HI_REVERSE ?
		   attrevgc[attrib[cursor] & COLOR_MASK] :
		   attgc[attrib[cursor] & COLOR_MASK],
		   x, y, cwidth - 1, cheight - 1);
  
  /* If locked, use double line */
  else if (xsys)
    {
      if (cursheight + 2 < cheight)
	XFillRectangle(dpy, twin, cursgc, x, y, cwidth, cursheight + 2);
      XFillRectangle(dpy, twin, cursgc, x, y + curspos, cwidth, cursheight);
    }
  
  /* In insert mode or if altcr specified, use reverse video character */
  else if (inserting || altcr)
    XDrawImageString(dpy, twin, (attrib[cursor] & HI_MASK) == HI_REVERSE ?
		     attgc[attrib[cursor] & COLOR_MASK] :
		     attrevgc[attrib[cursor] & COLOR_MASK],
		     x, y + fontinfo->ascent, &screen[cursor], 1);
  
  /* Otherwise use underline */
  else if (cursheight) 
    {
      /* Make cursor fatter if this character is underlined */
      if ((attrib[cursor] & HI_MASK) == HI_UNDER &&
	  cursheight - un_thickness < 3 && curspos > 4)
	{
	  curspos -= 3;
	  cursheight += 3;
	}
      XFillRectangle(dpy, twin, cursgc, x, y + curspos, cwidth, cursheight);
    }
}


/* Build attribute to color/font conversion arrays.  Also set font info.  */
static void creategc(destroy)
     Boolean destroy;
{
  register int icolor;
  unsigned long gcmask, prop;
  Pixel fg[NUM_IC], bg[NUM_IC];
  Font font[NUM_IC];
  XGCValues gcv;

  /* Destroy the old GCs if requested */
  if (destroy) 
    for (icolor = 0; icolor < NUM_IC; icolor++) 
      {
	XtDestroyGC(attgc[icolor]);
	XtDestroyGC(attrevgc[icolor]);
      }
  
  /* Fill in the default colors */
  gcmask = GCForeground | GCBackground | GCFont;
  for (icolor = 0; icolor < NUM_IC; icolor++)
    {
      fg[icolor] = foreground;
      bg[icolor] = background;
      font[icolor] = fontinfo->fid;

      /* Monochrome display has a different font for intense characters */
      if (DisplayCells(dpy, scr) < 3 && (icolor & IC_INTENSE))
	font[icolor] = bfontinfo->fid;
    }
  
  /* Fill in color values if this is a color display */
  if (DisplayCells(dpy, scr) > 2)
    {
      fg[IC_UNFORMAT]   = foreground;
      fg[IC_DEFINTENSE] = extDefIntense;
      fg[IC_PN]         = pnColor;
      fg[IC_PI] 	= piColor;
      fg[IC_UN] 	= unColor;
      fg[IC_UI] 	= uiColor;
      fg[IC_DEFAULT]    = extDefault;
      fg[IC_BLUE]       = extBlue;
      fg[IC_RED]        = extRed;
      fg[IC_PINK]       = extPink;
      fg[IC_GREEN]      = extGreen;
      fg[IC_TURQUOISE]  = extTurquoise;
      fg[IC_YELLOW]     = extYellow;
      fg[IC_WHITE]      = extWhite;
    }
  
  /* Create the GCs */
  for (icolor = 0; icolor < NUM_IC; icolor++)
    {
      gcv.foreground = fg[icolor];
      gcv.background = bg[icolor];
      gcv.font = font[icolor];
      attgc[icolor] = XtGetGC(text, gcmask, &gcv);

      gcv.foreground = bg[icolor];
      gcv.background = fg[icolor];
      attrevgc[icolor] = XtGetGC(text, gcmask, &gcv);
    }

  /* Initialize font info global variables */
  cwidth = fontinfo->max_bounds.width;
  cheight = fontinfo->ascent + fontinfo->descent;

  if (XGetFontProperty(fontinfo, XA_UNDERLINE_POSITION, &prop))
    un_position = fontinfo->ascent + (int) prop;
  else
    un_position = cheight - 2;

  if (XGetFontProperty(fontinfo, XA_UNDERLINE_THICKNESS, &prop))
    un_thickness = prop;
  else
    un_thickness = 2;
}


/* Re-draw the icon window */
static void redrawicon()
{
  register int x;
  
  XCopyArea(dpy, icon, iconwin, icongc, 0, 0, icon_width, icon_height, 0, 0);

  /* Display the name string */
  x = XTextWidth(iconfont, nmstring, strlen(nmstring));
  if (x > icon_width) x = 0;
  else x = (icon_width - x) / 2;

  XDrawImageString(dpy, iconwin, outputpending ? iconrevgc : icongc,
		   x, icon_height - iconfont->descent,
		   nmstring, strlen(nmstring));
}


/* Re-draw the indicators window */
static void redrawind()
{
  register int i;
  Arg arglist[10];

  i = 0;
  XtSetArg(arglist[i], XtNinserting, inserting); i++;
  XtSetArg(arglist[i], XtNsystem, xsys); i++;
  XtSetArg(arglist[i], XtNaplkeyb, apl_mode); i++;
  XtSetValues(title, arglist, i);
  indpending = False;
}


/* Update the icon window to show that output occurred */
static void output_happened()
{
  if (!outputpending && iconified) 
    {
      outputpending = True;
      redrawicon();
    }
}


/* Perform zoom operation */
static void dozoom()
{
  register int i;
  Arg arglist[10];
  XFontStruct *tfont;
  Window cwin;
  int oldx, oldy, zx, zy;
  Dimension oldw, oldh, neww, newh, zw, zh, zoombw;

  /* Swap the fonts */
  tfont = fontinfo; fontinfo = zfontinfo; zfontinfo = tfont;
  tfont = bfontinfo; bfontinfo = zbfontinfo; zbfontinfo = tfont;
  creategc(True);
  graph_setfont(fontinfo->fid);

  /* Save old position in case we unzoom.  It would be nice to use
     XtGetValues here, but that won't work because the 11.3 version
     of the X Toolkit doesn't always keep track of the position (it
     worked okay in 11.2).  */

  (void) XTranslateCoordinates(dpy, XtWindow(toplevel), RootWindow(dpy, scr),
			       0, 0, &oldx, &oldy, &cwin);

  /* Save the old size for unzoom */
  i = 0;
  XtSetArg(arglist[i], XtNwidth, &oldw); i++;
  XtSetArg(arglist[i], XtNheight, &oldh); i++;
  XtGetValues(toplevel, arglist, i);

  /* Set the new text window size */
  neww = maxcols * cwidth + 2 * IBORD;
  newh = maxrows * cheight + 2 * IBORD;
  i = 0;
  XtSetArg(arglist[i], XtNwidth, neww); i++;
  XtSetArg(arglist[i], XtNheight, newh); i++;
  XtSetValues(text, arglist, i);

  /* Adjust the minimum width and height values for top level */
  setmsize(&neww, &newh);

  /* Compute new window size and position */
  zw = (zoomw == def_dimension) ? neww : zoomw;
  zh = (zoomh == def_dimension) ? newh : zoomh;
  if ((zoombits & XValue) && (zoombits & XNegative))
    zoomx += DisplayWidth(dpy, scr) - zw;
  if ((zoombits & YValue) && (zoombits & YNegative))
    zoomy += DisplayHeight(dpy, scr) - zh;
  zoombits = 0;
  zx = (zoomx == def_position) ? oldx : zoomx;
  zy = (zoomy == def_position) ? oldy : zoomy;

  /* Reconfigure the window.  This isn't easy since we want this to
     work with lots of different window managers.  I spent a lot of
     time trying to find some method that works.  */

  i = 0;
  XtSetArg(arglist[i], XtNx, &zoomx); i++;
  XtSetArg(arglist[i], XtNy, &zoomy); i++;
  XtSetArg(arglist[i], XtNborderWidth, &zoombw); i++;
  XtGetValues(toplevel, arglist, i);
  XtConfigureWidget(toplevel, zoomx, zoomy, zw, zh, zoombw);
  XMoveResizeWindow(dpy, XtWindow(toplevel), zx, zy, zw, zh);

  /* With aixwm (and maybe other window managers) the XMoveResizeWindow won't
     have any affect if we were reparented.  We must move the parent.  */

  if (topwin != XtWindow(toplevel))
    {
      Window rootwin, parentwin, *children;
      unsigned int nchildren;
      int dx, dy;

      /* First, make sure that the top window is a child of the root.
	 If not, then this isn't aixwm, so we don't need to do anything.  */

      (void) XQueryTree(dpy, topwin, &rootwin, &parentwin, &children,
			&nchildren);
      if (parentwin == rootwin)
	{
	  /* Move parent to the proper spot so that we will be positioned
	     at zx, zy.  */

	  (void) XTranslateCoordinates(dpy, XtWindow(toplevel), topwin, 0, 0,
				       &dx, &dy, &cwin);
	  XMoveWindow(dpy, topwin, zx - dx, zy - dy);
	}
      if (nchildren) XFree((char *) children);
    }
  
  /* Save old position for next time */
  zoomx = oldx;
  zoomy = oldy;
  zoomw = oldw;
  zoomh = oldh;

  zoompend = False;
}


/* Set the minimum width and height values for top level window */
static void setmsize(ret_width, ret_height)
     Dimension *ret_width, *ret_height;
{
  register int i;
  Arg arglist[10];
  Dimension tibord, txbord;

  i = 0;
  XtSetArg(arglist[i], XtNheight, ret_height); i++;
  XtSetArg(arglist[i], XtNborderWidth, &tibord); i++;
  XtGetValues(title, arglist, i);
  if (!*ret_height) tibord = 0;

  XtSetArg(arglist[0], XtNborderWidth, &txbord);
  XtGetValues(text, arglist, 1);

  *ret_height += maxrows * cheight + 2 * IBORD + 2 * tibord + 2 * txbord;
  *ret_width = maxcols * cwidth + 2 * IBORD + 2 * txbord;

  i = 0;
  XtSetArg(arglist[i], XtNminWidth, *ret_width); i++;
  XtSetArg(arglist[i], XtNminHeight, *ret_height); i++;
  XtSetValues(toplevel, arglist, i);
}


/* Callback routine for key press events */
/*ARGSUSED*/ static void dokey(w, client_data, keybuff)
     Widget w;
     caddr_t client_data;
     caddr_t keybuff;
{
  (void) s3270_key(&keybuff[1], (int) keybuff[0]);
}


/* Callback routine for text window resize */
/*ARGSUSED*/ static void resize(w, client_data, dummy)
     Widget w;
     caddr_t client_data;
     caddr_t dummy;
{
  register int i;
  Arg arglist[10];
  Dimension width, height;

  /* Get the new size */
  i = 0;
  XtSetArg(arglist[i], XtNwidth, &width); i++;
  XtSetArg(arglist[i], XtNheight, &height); i++;
  XtGetValues(text, arglist, i);
  
  /* Adjust graphics origin */
  graph_resize(width, height);

  /* Compute new text position */
  xpos = ((int) width - (int) (maxcols * cwidth)) / 2;
  if (xpos < 0) xpos = 0;
  ypos = ((int) height - (int) (maxrows * cheight)) / 2;
  if (ypos < 0) ypos = 0;

  /* Redraw the screen */
  if (XtIsRealized(text)) XClearArea(dpy, XtWindow(text), 0, 0, 0, 0, True);
}


/* Callback routine for text window expose */
/*ARGSUSED*/ static void expose(w, client_data, event)
     Widget w;
     caddr_t client_data;
     XEvent *event;
{
  int col, row, endcol, endrow, pos;

  /* If not finished initializing, leave */
  if (!scrsize) return;

  /* Convert pixels to text coordinates */
  col = event->xexpose.x - xpos;
  endcol = (col + event->xexpose.width - 1) / cwidth + 1;
  if (endcol < 0) return;
  if (endcol > scols) endcol = scols;
  if (col < 0) col = 0;
  col /= cwidth;
  if (col >= scols) return;

  row = event->xexpose.y - ypos;
  endrow = (row + event->xexpose.height - 1) / cheight + 1;
  if (endrow < 0) return;
  if (endrow > srows) endrow = srows;
  if (row < 0) row = 0;
  row /= cheight;
  if (row >= srows) return;

  /* Clear the exposed area in case something was drawn between
     the time of the exposure and now.  */

  XClearArea(dpy, twin, xpos + col * cwidth, ypos + row * cheight,
	     (endcol - col) * cwidth - 1, (endrow - row) * cheight - 1,
	     False);

  /* If cursor was in the exposed area, mark it invisible */
  if (cursvis && cursor % scols >= col && cursor % scols < endcol &&
      cursor / scols >= row && cursor / scols < endrow) cursvis = False;

  /* Indicate that the exposed area is blank now */
  pos = col + row * scols;
  if (pos < minpos) minpos = pos;
  pos = endcol + (endrow - 1) * scols;
  if (pos > maxpos) maxpos = pos;

  for (; row < endrow; row++) 
    {
      memset(&screen[col + row * scols], ' ', endcol - col);
      memset(&attrib[col + row * scols], DEFAULT_CA, endcol - col);
    }

  /* Redraw graphics if necessary */
  graph_expose(event->xexpose.x, event->xexpose.y, event->xexpose.width,
	       event->xexpose.height); 
}


/* Callback routine for text window actions */
static void action(w, client_data, call_data)
     Widget w;
     caddr_t client_data;
     caddr_t call_data;
{
  XtWin3270DataPtr data = (XtWin3270Data *) call_data;
  int pos, nbytes, col, row, new_mode;
  char keybuff[20], *cutbuff;

  switch (data->function)
    {
    case XtFselectStart:
      /* Start of cut (shift button1) */
      startcut = calcpos(data->x, data->y);
      break;

    case XtFselectAdjust:
      /* Change cut range (shift button1 motion) */
      pos = calcpos(data->x, data->y);
      if (pos != endcut) 
	{
	  endcut = pos;
	  newrange(startcut, endcut);
	}
      break;

    case XtFselectEnd:
      /* Copy to cut buffer (shift button1 release) */
      endcut = calcpos(data->x, data->y);
      newrange(startcut, endcut);
      cutbuff = cut(mincut, maxcut);
      storecut(cutbuff);
      XtFree(cutbuff);
      mincut = maxcut = -1;
      break;

    case XtFselectAll:
      /* Cut whole screen (shift button2) */
      cutbuff = cut(0, scrsize);
      storecut(cutbuff);
      XtFree(cutbuff);
      break;

    case XtFinsertSelection:
      /* Paste (shift button3) */
      cutbuff = XFetchBytes(dpy, &nbytes);
      if (nbytes <= 0) break;

      for (pos = 0; pos < nbytes; pos++) 
	if (cutbuff[pos] == '\n')
	  {
	    keybuff[0] = k_newline;
	    if (!s3270_key(keybuff, 1)) break;
	  }
	else
	  {
	    keybuff[0] = apl_paste ? k_stringa : k_string;
	    keybuff[1] = cutbuff[pos];
	    if (!s3270_key(keybuff, 2)) break;
	  }
      XFree(cutbuff);
      break;

    case XtFzoom:
      zoom(w, client_data, (caddr_t) NULL);
      break;

    case XtFiconify:
      iconify();
      break;

    case XtFsetCursor:
      if (graph_cursor(data->x, data->y)) break;
      col = (data->x - xpos) / (int) cwidth;
      row = (data->y - ypos) / (int) cheight;
      if (col < 0 || col >= scols || row < 0 || row >= srows) break;
      keybuff[0] = k_cursor_move;
      keybuff[1] = col >> 8;
      keybuff[2] = col & 255;
      keybuff[3] = row >> 8;
      keybuff[4] = row & 255;
      (void) s3270_key(keybuff, 5);
      break;

    case XtFspecial1:
      /* Set cursor; send F11 if special mouse (button1) */
      if (graph_cursor(data->x - xpos, data->y - ypos)) break;
      col = (data->x - xpos) / (int) cwidth;
      row = (data->y - ypos) / (int) cheight;
      if (col < 0 || col >= scols || row < 0 || row >= srows) break;
      keybuff[0] = k_cursor_move;
      keybuff[1] = col >> 8;
      keybuff[2] = col & 255;
      keybuff[3] = row >> 8;
      keybuff[4] = row & 255;
      (void) s3270_key(keybuff, 5);
      if (mousing) 
	{
	  keybuff[0] = k_pf11;
	  (void) s3270_key(keybuff, 1);
	}
      break;

    case XtFspecial2:
      /* Zoom or send F3 if special mouse (button2) */
      if (mousing) 
	{
	  keybuff[0] = k_pf3;
	  (void) s3270_key(keybuff, 1);
	}
      else
	zoom(w, client_data, (caddr_t) NULL);
      break;

    case XtFspecial3:
      /* Iconify or send F8 if special mouse (button3) */
      if (mousing) 
	{
	  keybuff[0] = k_pf8;
	  (void) s3270_key(keybuff, 1);
	}
      else iconify();
      break;

    case XtFaplonoff:
      /* Toggle APL translation.
	 
	 The built-in way to use APL is to press a button (Ctrl-Backspace)
	 to toggle the keyboard state and turn on an APL indicator.  The
	 keyboard then follows the 3179 APL keyboard definition.  Some
	 people prefer a "union" keyboard, where all the standard key
	 definitions remain, and APL characters are entered using new
	 shift combinations.  In that case, aplonoff is called with no
	 parameter, and only cut/paste action is modified (no APL indicator
	 is used).  */

      if (!data->x)
	{
	  /* Using a "union" keyboard */
	  apl_paste = !apl_paste;
	  break;
	}
      new_mode = toggle_mod(data->x);
      if (new_mode < 0) break;
      if ((Boolean) new_mode != apl_mode)
	{
	  apl_mode = (Boolean) new_mode;
	  redrawind();
	  XFlush(dpy);
	}
      apl_paste = apl_mode;
      break;

    case XtFmodlock:
      /* Toggle logically locked key modifiers */
      (void) toggle_mod(data->x);
      break;

    case XtFsystem:
      /* Run a system command */
      (void) system(data->params);
      break;
    }
}


/* Toggle logical modifier locks for Keypress */
static int toggle_mod(name)
     int name;
{
  Modifiers mask;
  switch (name)
    {
    case 1: mask = Mod1Mask; break;
    case 2: mask = Mod2Mask; break;
    case 3: mask = Mod3Mask; break;
    case 4: mask = Mod4Mask; break;
    case 5: mask = Mod5Mask; break;
    default: return -1;
    }
  mod_locks ^= mask;
  if (mod_locks & mask) return 1;
  else return 0;
}
 
 
/* Change from normal to iconic state */
static void iconify()
{
  XClientMessageEvent ev;

  /* Send a message to the window manager telling him to iconify us */
  ev.type = ClientMessage;
  ev.display = dpy;
  ev.window = XtWindow(toplevel);
  ev.message_type = wm_change_state;
  ev.format = 32;
  ev.data.l[0] = IconicState;
  (void) XSendEvent(dpy, RootWindow(dpy, scr), False,
		    (long) SubstructureRedirectMask | SubstructureNotifyMask,
		    (XEvent *) &ev);

  /* Since aixwm doesn't know about WM_CHANGE_STATE, do it manually */
  XUnmapWindow(dpy, topwin);
  XMapRaised(dpy, iconwin);
}


/* Cut text from screen (return address of a buffer containing the cut text) */
static char *cut(frompos, topos)
     int frompos, topos;
{
  int col, row, endrow, len;
  char *cutbuff, *cutptr;

  /* Make sure start position comes before end position */
  if (frompos >= topos) return NULL;

  /* Allocate memory for cut buffer, allowing room for insertion of CRs */
  cutbuff = (char *) XtMalloc(topos - frompos + srows);
  cutptr = cutbuff;

  /* Go through text buffer one row at a time */
  col = frompos % scols;
  endrow = topos / scols;
  for (row = frompos / scols; row < endrow; row++)
    {
      /* Remove trailing spaces */
      len = scols - col;
      while (len > 0) 
	{
	  if (newscreen[col + row * scols + len - 1] != ' ') break;
	  len--;
	}
      
      /* Copy row of text to buffer and insert new line at end */
      if (len > 0) 
	{
	  memcpy(cutptr, &newscreen[col + row * scols], len);
	  cutptr += len;
	}
      *cutptr++ = '\n';
      col = 0;
    }

  /* For last row, don't remove trailing spaces or insert new line */
  len = topos % scols - col;
  if (len > 0) 
    {
      memcpy(cutptr, &newscreen[col + row * scols], len);
      cutptr += len;
    }
  *cutptr = '\0';
  return cutbuff;
}


/* Calculate screen position from window coordinates */
static int calcpos(x, y)
     int x, y;
{
  int col, row;
  col = (x - xpos) / (int) cwidth;
  row = (y - ypos) / (int) cheight;
  if (col < 0) col = 0;
  else if (col >= scols) { col = 0; row++; }
  if (row < 0) row = 0;
  else if (row >= srows) { col = 0; row = srows; }
  return col + row * scols;
}


/* Change the cut range */
static void newrange(frompos, topos)
     int frompos, topos;
{
  /* Remove old cut range display, if any */
  if (maxcut > mincut) 
    {
      if (mincut < minpos) minpos = mincut;
      if (maxcut > maxpos) maxpos = maxcut;
    }

  /* Set new cut range */
  if (topos > frompos)
    {
      mincut = frompos;
      maxcut = topos;
    }
  else 
    {
      mincut = topos;
      maxcut = frompos;
    }

  /* Double check that the cut range is within the screen bounds */
  if (mincut < 0) mincut = 0;
  if (mincut >= scrsize) mincut = scrsize - 1;
  if (maxcut < 0) maxcut = 0;
  if (maxcut > scrsize) maxcut = scrsize;
      
  /* Indicate that cut range needs to be re-displayed */
  if (mincut < minpos) minpos = mincut;
  if (maxcut > maxpos) maxpos = maxcut;
}


/* Store a string into the cut buffer */
static void storecut(buff)
     char *buff;
{
  static Boolean created = False;
  if (!buff) return;

  /* Be sure that all cut buffers have been created */
  if (!created)
    {
      int root = RootWindow(dpy, scr);
#define create(b) \
  XChangeProperty(dpy, root, b, XA_STRING, 8, PropModeAppend, NULL, 0);
      create(XA_CUT_BUFFER0);
      create(XA_CUT_BUFFER1);
      create(XA_CUT_BUFFER2);
      create(XA_CUT_BUFFER3);
      create(XA_CUT_BUFFER4);
      create(XA_CUT_BUFFER5);
      create(XA_CUT_BUFFER6);
      create(XA_CUT_BUFFER7);
      created = True;
    }
  XRotateBuffers(dpy, 1);
  XStoreBytes(dpy, buff, strlen(buff));
}


/* Play a tune */
static void play(tuneptr)
     char *tuneptr;
{
  char *ptr, *newptr;
  XKeyboardState cur_state;
  XKeyboardControl new_ctl;

  /* Get current keyboard settings */
  XGetKeyboardControl(dpy, &cur_state);

  /* Play the tune */
  for (ptr = tuneptr; strlen(ptr) > 0; )
    {
      new_ctl.bell_pitch = getnum(ptr, &newptr);
      if (ptr == newptr) break;
      ptr = newptr;
      new_ctl.bell_duration = getnum(ptr, &newptr);
      if (ptr == newptr) break;
      ptr = newptr;
      if (new_ctl.bell_duration == 0) new_ctl.bell_duration = 50;
      XChangeKeyboardControl(dpy, (unsigned long)
			     (KBBellPitch | KBBellDuration), &new_ctl);
      XBell(dpy, 0);
    }

  /* Restore original settings.  Even though the mask specifies that only
     pitch and duration should be changed, other settings are modified (at
     least on AIX that's what happens).  Therefore, restore click and bell
     percent also.  The LED mask that's returned by XGetKeyboardControl
     does not change if an LED is set from the keyboard, so it doesn't do
     much good to try to restore that.  */

  new_ctl.key_click_percent = cur_state.key_click_percent;
  new_ctl.bell_percent = cur_state.bell_percent;
  new_ctl.bell_pitch = cur_state.bell_pitch;
  new_ctl.bell_duration = cur_state.bell_duration;
  XChangeKeyboardControl(dpy, (unsigned long)
			 (KBKeyClickPercent | KBBellPercent | KBBellPitch |
			  KBBellDuration), &new_ctl);
}


/* Convert string to integer.  I would use strtol(), but that's not on BSD.  */
static int getnum(ptr, newptr)
     char *ptr, **newptr;
{
  int n = 0;

  /* Scan for a digit */
  while (*ptr && (*ptr < '0' || *ptr > '9')) ptr++;

  /* Convert numbers until a non-digit is reached */
  while (*ptr >= '0' && *ptr <= '9')
    {
      n = n * 10 + *ptr - '0';
      ptr++;
    }
  *newptr = ptr;
  return n;
}



/* Non-fatal X error handler */
static int error_handler(disp, event)
     Display *disp;
     XErrorEvent *event;
{
  char msg[200], code[20];

  if (!debug) return 0;

  XGetErrorDatabaseText(disp, "XlibMessage", "XError", "X Error",
			msg, sizeof msg);
  fprintf(stderr, "%s:  ", msg);

  XGetErrorText(disp, event->error_code, msg, sizeof msg);
  fprintf(stderr, "%s\n  ", msg);

  XGetErrorDatabaseText(disp, "XlibMessage", "MajorCode",
			"Failed request major op code %d", msg, sizeof msg);
  fprintf(stderr, msg, event->request_code);

  sprintf(code, "%d", event->request_code);
  XGetErrorDatabaseText(disp, "XRequest", code, "", msg, sizeof msg);
  fprintf(stderr, " %s\n  ", msg);

  XGetErrorDatabaseText(disp, "XlibMessage", "MinorCode",
			"Failed request minor op code", msg, sizeof msg);
  fprintf(stderr, msg, event->minor_code);
  fputs("\n  ", stderr);

  XGetErrorDatabaseText(disp, "XlibMessage", "ResourceID",
			"ResourceID 0x%x in failed request", msg, sizeof msg);
  fprintf(stderr, msg, event->resourceid);
  fputs("\n  ", stderr);

  XGetErrorDatabaseText(disp, "XlibMessage", "ErrorSerial",
			"Serial number of failed request %d", msg, sizeof msg);
  fprintf(stderr, msg, event->serial);
  fputs("\n  ", stderr);
  XGetErrorDatabaseText(disp, "XlibMessage", "CurrentSerial",
			"Current serial number in output stream %d",
			msg, sizeof msg);
  fprintf(stderr, msg, disp->request);
  fputs("\n", stderr);
  return 0;
}



/*********************************
 *     Window event handlers     *
 *********************************/ 


/* Event handler for the top level window */
/*ARGSUSED*/ static void top_event(w, client_data, event)
     Widget w;
     caddr_t client_data;
     XEvent *event;
{
  switch (event->type)
    {
    case MapNotify:
      mapped = True;
      if (dwarp && twin) XWarpPointer(dpy, None, twin, 0, 0, 0, 0,
				      xpos + maxcols * cwidth / 2,
				      ypos + maxrows * cheight / 2);
      break;

    case UnmapNotify:
      iconified = True;
      break;

    case ReparentNotify:
      /* If a reparenting window manager is used, we might end up
	 with a new parent.  For aixwm, we tell the window manager not
	 to decorate or reparent us by setting the title string to
	 null.  However, if the "-wmtitle" option is used, then we'll
	 get reparented.  Also, other window managers might not use
	 that convention.

	 Since aixwm doesn't follow the Inter-Client Communication
	 Conventions, we have to do our own MapWindow to de-iconify.
	 Therefore, we must keep track of the parent since it doesn't
	 do any good to map a window if the parent (created by the
	 window manager) is still unmapped.  */

      if (event->xreparent.window != XtWindow(toplevel)) break;
      topwin = event->xreparent.parent;
      break;
    }
}

  
/* Event handler for the outer form window */
/*ARGSUSED*/ static void outer_event(w, client_data, event)
     Widget w;
     caddr_t client_data;
     XEvent *event;
{
  switch (event->type)
    {
    case FocusIn:
      focused = True;
      drawcurs();
      break;

    case FocusOut:
      focused = False;
      drawcurs();
      break;
    }
}

  
/* Event handler for the icon window */
static void icon_event(event)
     XEvent *event;
{
  switch (event->type)
    {
    case ButtonPress:
      if (!XtIsRealized(toplevel)) break;

      /* ICCCM says that iconic to normal transition can be done by
        just mapping the client window.  However, with aixwm, we must
	unmap the icon window ourselves and also map the parent of our
	top level window (if we have been reparented).  */

      XUnmapWindow(dpy, iconwin);
      if (topwin != XtWindow(toplevel)) XMapRaised(dpy, XtWindow(toplevel));
      XMapRaised(dpy, topwin);
      break;

    case Expose:
      redrawicon();
      break;

    case UnmapNotify:
      iconified = False;
      outputpending = False;
      if (topwin) XMapRaised(dpy, topwin);
      break;
    }
}


/* Event handler for the text window */
/*ARGSUSED*/ static void text_event(w, client_data, event)
     Widget w;
     caddr_t client_data;
     XEvent *event;
{
  switch (event->type)
    {
    case EnterNotify:
      /* Do the raise in two seconds */
      if (autoraise) autotime = time((long *) 0) + 2;
      inwindow = True;
      drawcurs();
      break;

    case LeaveNotify:
      /* Cancel the auto raise */
      autotime = 0;
      inwindow = False;
      drawcurs();
      break;
    }
}



/*********************************
 *     Menu button callbacks     *
 *********************************/ 


/* Handle "Special Mouse" menu selection */
/*ARGSUSED*/ static void specmouse(w, client_data, call_data)
     Widget w;
     caddr_t client_data;
     caddr_t call_data;
{
  x_mouse((Boolean) (!mousing));
}


/* Handle "Zoom" menu selection */
/*ARGSUSED*/ static void zoom(w, client_data, call_data)
     Widget w;
     caddr_t client_data;
     caddr_t call_data;
{
  zoompend = TRUE;
  zooming = !zooming;
  if (zooming) menu_change(m_zoom, "+ Zoom");
  else menu_change(m_zoom, "  Zoom");
}


/* Handle "Trace" menu selection */
/*ARGSUSED*/ static void settrace(w, client_data, call_data)
     Widget w;
     caddr_t client_data;
     caddr_t call_data;
{
  trace = !trace;
  if (trace) menu_change(m_trace, "+ Trace");
  else menu_change(m_trace, "  Trace");
}


/* Handle "3277-GA" menu selection */
/*ARGSUSED*/ static void ga_switch(w, client_data, call_data)
     Widget w;
     caddr_t client_data;
     caddr_t call_data;
{
  ga_support = !ga_support;
  if (ga_support) menu_change(m_ga, "+ 3277-GA");
  else menu_change(m_ga, "  3277-GA");
  s3270_ga(ga_support);
}


/* Handle "Quit" menu selection */
/*ARGSUSED*/ static void quit(w, client_data, call_data)
     Widget w;
     caddr_t client_data;
     caddr_t call_data;
{
  quitflag = True;
}
