/*****	IBM Internal Use Only *****/

/*-----------------------------------------------------------------*/
/*  Graphics support for 3277-GA emulator			   */
/*-----------------------------------------------------------------*/

/*
   This code was adapted from the 3277-GA emulator written by Michael
   Goodfellow, IBM Almaden Research Center, for the Quicksilver
   operating system.

   Support for line type and intensity was adapted from code written
   by Brian Taylor and George Sax.
								   */

#undef BSD_INCLUDES
#define BSD_INCLUDES
#include <stdio.h>
#include <memory.h>
#include <X11/Intrinsic.h>
#include <X11/Xos.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include "Ga3270.h"
#include "s3270.h"


/*-----------------------------------------------------------------*/
/*  Important GA ordinals					   */
/*-----------------------------------------------------------------*/

#define ESC 0xF8
#define LPG 0xF9
#define SVM 0xFA
#define SAM 0xFB
#define EGS 0xFC
#define ECH 0xFD
#define REFR 0xFE
#define BGS 0xFF
#define SC0 0x40
#define SC63 0x7F
#define ITR 0x30
#define RTE 0x31
#define DTR 0x32
#define MTR 0x33
#define RTN 0x34
#define CRX 0x20
#define ERS 0x21
#define HCY 0x22
#define ALR 0x23
#define CRY 0x24
#define SNZ 0x25
#define SDZ 0x26
#define SWZ 0x27
#define LT1 0x10
#define SDV 0x18
#define LT15 0x1F

#define sim_wd 4096
#define sim_ht 3200
#define sim_maxy 4096


/*-----------------------------------------------------------------*/
/*  Global variables						   */
/*-----------------------------------------------------------------*/

#define GE 0x08

#define TBUF_LEN 1200
#define TMOVE_FLAG  0x4000
#define TMOVE_CHECK 0xC000
#define TMOVE_POS   0x4000
#define TMOVE_NEG   0x8000

struct point
{
  int x;
  int y;
};

extern Boolean debug;		/* Debugging flag */
static Display *dpy;		/* The current display */
static int scr;			/* Default screen */
static char title[100];		/* Title string for window */
static int scrsize;		/* Screen size */
static char *ga_geometry;	/* Geometry specification */
static Widget toplevel;		/* Our top-level shell widget */
static Widget win;		/* The graphics window */
static Dimension width, height;	/* Size of graphics window */
static int xpos, ypos;		/* Start position of graphics within window */
static Boolean inited;		/* True if initialization was done */
static struct point startpt;	/* Starting point for a line */
static enum draw_types dt;	/* What type of drawing to do */


/* Forward and external routine declarations */
static void do_init(), graphics_state(), dokey(), resize(), expose(),
  action(), destroy(), newsize(), track_cursor(), draw_buffer(),
  graphics_erase(), moveto(), drawto(), wrapdraw(), write_trace(),
  replay_trace(), alfmd(), lpgmd(), itrmd();
static XtEventHandler graph_event();
extern Boolean s3270_key();


/* Trace file */
static Boolean topen;		/* True if trace file open */
static int tfile;		/* File descriptor for trace file */

static short tracebuf[TBUF_LEN]; /* Trace buffer */
static int tbufposn;		/* Position in trace */


/* State of simulated GA */
static struct {
  unsigned char *refbuf;	/* Refresh buffer */
  Boolean refresh;		/* True for refresh buffer */
  Boolean first;		/* True for first pass */
  Boolean gin;			/* Graphics input enabled */

  int ppg[2048];		/* Pattern generator word */
  int nxtppg;			/* Next pattern gen addr */
  int ppgcnt;			/* Number of bytes in word */

  Boolean esced;		/* True if last ordinal esc */
  Boolean loaded;		/* True if ppg loaded */
  int ltype;			/* Line type */
  int inten;			/* Line intensity */
  int scale;			/* Vector scale */
  int zaxis;			/* Write-thru mode */
  int crdir;			/* Cr direction */
  int gmode;			/* Graphics mode */
  struct point treg;		/* Track register */
  struct point oldtreg;		/* Previous track register */
  struct point last;		/* Last position */
  struct point posn;		/* Current position */
  int itrsave;			/* Saved mode */
  int longvec;			/* Long vector format */
  int veccnt;			/* Vector order byte count */
  int vecwrd[4];		/* Vector word */
} graphics;


/*-----------------------------------------------------------------*/
/*  Initialization						   */
/*-----------------------------------------------------------------*/

ga_init(display, screen, titlestring, size, geometry)
     Display *display;
     int screen;
     char *titlestring;
     int size;
     char *geometry;
{
  dpy = display;
  scr = screen;
  (void) strcpy(title, "Graphics-");
  (void) strncat(title, titlestring, sizeof title - 10);
  scrsize = size;
  ga_geometry = geometry;
  inited = False;
}


/*-----------------------------------------------------------------*/
/*  Process a graphics buffer					   */
/*-----------------------------------------------------------------*/

ga_write(buff, len)
     char *buff;
     int len;
{
  if (!inited) do_init();

  /* If there's already a refresh pattern up, take it down */
  if (graphics.refresh)
    {
      draw_buffer(graphics.refbuf, graphics.refresh, False);
      graphics.refresh = False;
    }

  /* If input was being requested, reset msg */
  if (graphics.gin)
    {
      /*** graphics_msg(False); ***/
      graphics.gin = False;	/* Disable graphics input */
    }

  /* Record old treg position */
  graphics.oldtreg.x = graphics.treg.x;
  graphics.oldtreg.y = graphics.treg.y;

  /* Draw the buffered data */
  graphics.first = True;
  graphics.refresh = (buff[len-1] == REFR);
  draw_buffer((unsigned char *) buff, graphics.refresh, True);

  /* If this is a refresh buffer, copy it to saved area */
  if (graphics.refresh)
    memcpy(graphics.refbuf, buff, len);

  /* If graphics input has come on, display msg on alpha */
  /*** if (graphics.gin) graphics_msg(True); ***/
}


/*-----------------------------------------------------------------*/
/*  Initialize the X window					   */
/*-----------------------------------------------------------------*/

static void do_init()
{
  register int i;
  Arg arglist[20];

  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 XtCallbackRec destroycalls[] =
    { {(XtCallbackProc) destroy, NULL}, {NULL, NULL} };

  /* Initialize state */
  graphics_state();

  /* Initialize size of graphics window */
  width = height = xpos = ypos = 0;

  /* Create a top-level shell widget */
  i = 0;
  XtSetArg(arglist[i], XtNtitle, title); i++;
  XtSetArg(arglist[i], XtNiconName, title); i++;
  if (ga_geometry) { XtSetArg(arglist[i], XtNgeometry, ga_geometry); i++; }
  toplevel = XtAppCreateShell((String) NULL, "Xant", topLevelShellWidgetClass,
			      dpy, arglist, i);

  /* Create the graphics window as a child of the shell */
  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++;
  XtSetArg(arglist[i], XtNdestroyProc, destroycalls); i++;
  win = XtCreateManagedWidget("ga3270", ga3270WidgetClass, toplevel,
			      arglist, i);
  XtAddEventHandler(win, EnterWindowMask | LeaveWindowMask, False,
		    graph_event, NULL);

  XtRealizeWidget(toplevel);
  inited = True;
}


/*-----------------------------------------------------------------*/
/*  Initialize state						   */
/*-----------------------------------------------------------------*/

static void graphics_state()
{
  /* Initialize trace file */
  topen = False;
  tbufposn = 0;

  /* Initialize simulated graphics state */
  if (!(graphics.refbuf = (unsigned char *) malloc(scrsize)))
    error("Not enough memory");
  graphics.refresh = False;	/* No refresh pattern */
  graphics.esced = False;
  graphics.ltype = 0;
  graphics.inten = 0;
  graphics.scale = 1;
  graphics.gmode = 0;
  graphics.crdir = 0;
  graphics.zaxis = 0;
  graphics.posn.x = 0;
  graphics.posn.y = 0;
  graphics.last.x = 0;
  graphics.last.y = 0;
  graphics.treg.x = 2048;
  graphics.treg.y = 1536;
  graphics.ppgcnt = 0;
  graphics.veccnt = 0;
}


/*-----------------------------------------------------------------*/
/*  Handle graphics window events				   */
/*-----------------------------------------------------------------*/

/*ARGSUSED*/ static XtEventHandler graph_event(w, client_data, event)
     Widget w;
     caddr_t client_data;
     XEvent *event;
{
  switch (event->type)
    {
    case EnterNotify:
      x_inwindow(True);
      break;

    case LeaveNotify:
      x_inwindow(False);
      break;
    }
  return (XtEventHandler) NULL;
}


/*-----------------------------------------------------------------*/
/*  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 window resize				   */
/*-----------------------------------------------------------------*/

/*ARGSUSED*/ static void resize(w, client_data, dummy)
     Widget w;
     caddr_t client_data;
     caddr_t dummy;
{
  newsize();

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


/*-----------------------------------------------------------------*/
/*  Callback routine for window expose				   */
/*-----------------------------------------------------------------*/

/*ARGSUSED*/ static void expose(w, client_data, event)
     Widget w;
     caddr_t client_data;
     XEvent *event;
{
  if (height == 0) newsize();

  /* Replay any saved graphics */
  replay_trace();

  /* If there's a refresh buffer, display it */
  if (graphics.refresh)
    draw_buffer(graphics.refbuf, graphics.refresh, True);
}


/*-----------------------------------------------------------------*/
/*  Callback routine for window actions				   */
/*-----------------------------------------------------------------*/

/*ARGSUSED*/ static void action(w, client_data, call_data)
     Widget w;
     caddr_t client_data;
     caddr_t call_data;
{
  XtWin3270DataPtr data = (XtWin3270Data *) call_data;

  switch (data->function)
    {
    case XtFgaClear:
      /* Clear the window (button2) */
      XClearWindow(dpy, XtWindow(w));
      break;

    case XtFgaRefresh:
      /* Redraw the window (button3) */
      XClearWindow(dpy, XtWindow(w));
      replay_trace();
      if (graphics.refresh)
	draw_buffer(graphics.refbuf, graphics.refresh, True);
      break;

    case XtFgaSetCursor:
      /* Track the cursor (button1 down or button1 motion) */
      if (graphics.refresh)
	track_cursor(data->x, data->y);
      break;
    }
}


/*-----------------------------------------------------------------*/
/*  Callback routine for window destroy				   */
/*-----------------------------------------------------------------*/

/*ARGSUSED*/ static void destroy(w, client_data, event)
     Widget w;
     caddr_t client_data;
     XEvent *event;
{
  inited = False;
}


/*-----------------------------------------------------------------*/
/*  Compute window starting position and size			   */
/*-----------------------------------------------------------------*/

static void newsize()
{
  register int i;
  Arg arglist[10];
  Dimension wd, ht;

  /* Get the new size */
  i = 0;
  XtSetArg(arglist[i], XtNwidth, &width); i++;
  XtSetArg(arglist[i], XtNheight, &height); i++;
  XtGetValues(win, arglist, i);
  wd = width;
  ht = height;

  /* Figure largest graphics area (must be 3/4 aspect) */

  /* Figure if width or height is more constrained. */
  if (ht < sim_ht * wd / sim_wd)
    wd = sim_wd * ht / sim_ht;	/* Base on height */
  else
    ht = sim_ht * wd / sim_wd;	/* Base on width */

  /* Refigure graphics bounds */
  xpos = (width - wd) / 2;
  ypos = (height - ht) / 2;
  width = wd;
  height = ht;
}


/*-----------------------------------------------------------------*/
/*  Track the cursor when refresh graphics present		   */
/*-----------------------------------------------------------------*/

static void track_cursor(x, y)
     int x, y;			/* Point within window */
{
  int tx, ty;			/* New track register value */

  /* Figure new track register value */
  tx = (x - xpos) * sim_wd / (int) width;
  ty = (int) height - 1 - (y - ypos);
  ty = ty * sim_ht / (int) height;

  /* If changed, update screen */
  if (tx != graphics.treg.x || ty != graphics.treg.y)
    {
      /* Remove current graphics */
      draw_buffer(graphics.refbuf, graphics.refresh, False);

      /* No long first pass over refresh */
      graphics.first = False;

      /* Set simulated track register */
      graphics.treg.x = tx;
      graphics.treg.y = ty;

      /* Draw new graphics */
      draw_buffer(graphics.refbuf, graphics.refresh, True);
    }
}

/*-----------------------------------------------------------------*/
/*  Handle press of keystroke in gin mode.			   */
/*-----------------------------------------------------------------*/

ga_key(buff)
     unsigned char buff[4];
{
  /* If not in graphics mode, ignore key */
  if (!graphics.gin) return False;

  /* Take down current graphics */
  if (graphics.refresh)		/* Should always be true */
    {
      draw_buffer(graphics.refbuf, graphics.refresh, False);
      graphics.refresh = False;
    }

  /* Take down message on alpha screen */
  /*** graphics_msg(False); ***/

  /* Reset graphics input mode */
  graphics.gin = False;

  /* Type track register to buffer */
  buff[0] = 0x3F & (graphics.treg.x >> 6);
  buff[1] = 0x3F & graphics.treg.x;
  buff[2] = 0x3F & (graphics.treg.y >> 6);
  buff[3] = 0x3F & graphics.treg.y;

  return True;
}


/*-----------------------------------------------------------------*/
/*  Draw a graphics buffer in the window			   */
/*-----------------------------------------------------------------*/

#define BIT2	0x40
#define BIT3	0x20
#define L5BITS	0x1F
#define L6BITS	0x3F

static void draw_buffer(buffer, refresh, addref)
     unsigned char *buffer;	/* Buffer to process */
     Boolean refresh;		/* True for refresh buffer */
     Boolean addref;		/* True to add refresh */

{
  unsigned char *ordinal;	/* Ordinal in buffer */
  int x, y;			/* X and Y values */
  Boolean done;			/* True when done with buffer */

  /* If this is the first time a refresh buffer is drawn,
     it might change the cursor position, so record it */
  if (graphics.first)
    {
      /* Restore old position, since it might change */
      graphics.treg.x = graphics.oldtreg.x;
      graphics.treg.y = graphics.oldtreg.y;
    }

  if (refresh)
    dt = addref ? dt_refresh : dt_refclear;
  else
    dt = dt_draw;

  /* Restore the current position */
  moveto(graphics.posn.x, graphics.posn.y, False, False);

  /* Process a buffer of graphics (already translated to ordinals) */
  ordinal = buffer;
  done = False;
  while (!done)
    {
      switch (*ordinal)
	{
	case BGS:		/* Begin graphics stream */
	  break;

	case ECH:		/* Echo */
	  done = True;
	  break;

	case EGS:		/* End graphics stream */
	  done = True;
	  break;

	case REFR:		/* Refresh stream */
	  done = True;
	  break;

	case SAM:		/* Set alpha mode */
	  graphics.gmode = 0;
	  break;

	case SVM:
	  if (graphics.gmode != 2) /* If not in lpg mode */
	    {
	      graphics.gmode = 1; /* Set vector mode */
	      graphics.veccnt = 0; /* Reset vector word */
	    }
	  break;

	case LPG:		/* Load pattern generator */
	  graphics.loaded = True;
	  graphics.gmode = 2;
	  graphics.nxtppg = 0;
	  graphics.ppgcnt = 0;
	  break;

	case ESC:		/* Escape character */
	  graphics.esced = True;
	  break;

	default:
	  /* If last character was ESCAPE, process an escape order */
	  if (graphics.esced)
	    {
	      graphics.esced = False;
	      switch (*ordinal)
		{
		case ALR:	/* Alarm */
		  break;

		case DTR:	/* Draw to track register */
		  drawto(graphics.treg.x, graphics.treg.y, !refresh, False);
		  break;

		case ERS:	/* Erase the screen */
		  graphics_erase();
		  break;

		case HCY:	/* Hard copy */
		  break;

		case ITR:	/* Initialize track */
		  graphics.itrsave = graphics.gmode;
		  graphics.gmode = 3;
		  break;

		case MTR:	/* Move to track */
		  moveto(graphics.treg.x, graphics.treg.y, !refresh, False);
		  break;

		case RTE:	/* Read track register and enter */
		  graphics.gin = True; /* Enable graphics input */
		  break;

		case RTN:	/* Read track without enter */
		  break;

		case CRX:	/* Set cr-dir X */
		  graphics.crdir = 0;
		  break;

		case CRY:	/* Set cr-dir Y */
		  graphics.crdir = 1;
		  break;

		case SDV:	/* Set default values */
		  graphics.ltype = 0;
		  graphics.gmode = 0;
		  graphics.crdir = 0;
		  graphics.scale = 1;
		  graphics.inten = 0;
		  break;

		case SNZ:	/* Set normal beam */
		  graphics.inten = 0;
		  break;

		case SDZ:	/* Set defocused beam */
		  graphics.inten = 2;
		  break;

		case SWZ:	/* Set writethru beam */
		  if (!refresh && debug)
		    printf("SWZ in normal buffer.\n");
		  graphics.inten = 0;
		  break;

		default:
		  /* Check for line type commands */
		  if (*ordinal >= LT1 && *ordinal <= LT15)
		    {
		      graphics.ltype = (*ordinal) - LT1;
		      if (graphics.ltype > 8)
			graphics.ltype -= 1;
		    }
		  /* Else check for scale commands */
		  else if (*ordinal >= SC0 && *ordinal <= SC63)
		    graphics.scale = *ordinal - SC0;
		}		/* Switch escape character of */
	    }			/* An escape character */
	  else
	    {
	      switch (graphics.gmode)
		{
		case 0:
		  if (graphics.loaded)
		    alfmd(*ordinal, !refresh);
		  break;

		case 1:
		  /* If C had opened procedures, the code below
		     would be one */

		  /* Handle vector mode ordinal */

		  /* Save the ordinal */
		  graphics.vecwrd[graphics.veccnt] = *ordinal;

		  /* If first byte, get long or short indicator */
		  if (graphics.veccnt == 0)
		    graphics.longvec = (*ordinal & BIT2) != 0;

		  if (!graphics.longvec && graphics.veccnt == 1)
		    {
		      /* Process short relative vector */
		      x = graphics.vecwrd[0] & L5BITS;
		      if ((graphics.vecwrd[0] & BIT3) != 0)
			x = -x;

		      y = graphics.vecwrd[1] & L5BITS;
		      if ((graphics.vecwrd[1] & BIT3) != 0)
			y = -y;

		      if ((graphics.vecwrd[1] & BIT2) != 0)
			moveto(graphics.posn.x + graphics.scale * x,
			       graphics.posn.y + graphics.scale * y,
			       !refresh, False);
		      else drawto(graphics.posn.x + graphics.scale * x,
				  graphics.posn.y + graphics.scale * y,
				  !refresh, False);

		      graphics.veccnt = -1;
		    }

		  /* Check for long format */
		  if (graphics.longvec && graphics.veccnt == 3)
		    {
		      if ((graphics.vecwrd[1] & BIT2) != 0)
			{
			  /* Relative format */
			  x = ((graphics.vecwrd[0] & L5BITS) << 6) +
			    (graphics.vecwrd[1] & L6BITS);
			  if ((graphics.vecwrd[0] & BIT3) != 0)
			    x = -x;

			  y = ((graphics.vecwrd[2] & L5BITS) << 6) +
			    (graphics.vecwrd[3] & L6BITS);
			  if ((graphics.vecwrd[2] & BIT3) != 0)
			    y = -y;

			  x = graphics.posn.x + graphics.scale*x;
			  y = graphics.posn.y + graphics.scale*y;
			}
		      else
			{
			  /* Absolute format */
			  x = ((graphics.vecwrd[0] & L6BITS) << 6) +
			    (graphics.vecwrd[1] & L6BITS);

			  y = ((graphics.vecwrd[2] & L6BITS) << 6) +
			    (graphics.vecwrd[3] & L6BITS);
			}

		      if ((graphics.vecwrd[2] & BIT2) != 0)
			moveto(x, y, !refresh, False);
		      else drawto(x, y, !refresh, False);

		      graphics.veccnt = -1;
		    }

		  /* Advance to next byte */
		  graphics.veccnt++;
		  break;

		case 2:
		  lpgmd(*ordinal);
		  break;

		case 3:
		  itrmd(*ordinal, graphics.first);
		  break;
		}
	    }
	}			/* Case of char in rpq buffer */
      ordinal++;
    }				/* While !done */
}


/*-----------------------------------------------------------------*/
/*  Erase the graphics screen					   */
/*-----------------------------------------------------------------*/

static void graphics_erase()
{
  /* Reset journal file */
  if (topen)
    {
      close(tfile);
      topen = False;
    }
  tbufposn = 0;
  moveto(graphics.posn.x, graphics.posn.y, True, False);

  /* Clear the window */
  if (!XtIsRealized(win)) return;
  XClearWindow(dpy, XtWindow(win));
}


/*-----------------------------------------------------------------*/
/*  Move to indicated position					   */
/*-----------------------------------------------------------------*/

static void moveto(x, y, journal, wrapping)
     int x, y;			/* Position in simulated GA */
     Boolean journal;		/* True if should be recorded */
     Boolean wrapping;		/* True if called by wrapdraw */

{
  if (!wrapping)
    {
      x = x & 4095;
      y = y & 4095;
    }

  graphics.posn.x = x;
  graphics.posn.y = y;

  if (journal)
    {
      tracebuf[tbufposn++] = graphics.posn.x ^ TMOVE_FLAG;
      tracebuf[tbufposn++] = graphics.posn.y;
      tracebuf[tbufposn++] = (graphics.inten << 8) + graphics.ltype;

      if (tbufposn == TBUF_LEN) write_trace();
    }

  startpt.x = xpos + (x * (int) width) / sim_wd;
  startpt.y = ypos + (int) height - 1 - (y * (int) height) / sim_ht;
}


/*-----------------------------------------------------------------*/
/*  Draw to indicated position					   */
/*-----------------------------------------------------------------*/

static void drawto(x, y, journal, wrapping)
     int x, y;			/* Position in simulated GA */
     Boolean journal;		/* True if should be recorded */
     Boolean wrapping;		/* True if called by wrapdraw */

{
  /* See if vector needs to be clipped */
  if (!wrapping && (x < 0 || x > 4095 || y < 0 || y > 4095))
    {
      /* Wrap the vector around the screen.  May call drawto.  */
      wrapdraw(x, y, journal);
      return;
    }

  /* Set new position */
  graphics.posn.x = x;
  graphics.posn.y = y;

  if (journal)
    {
      tracebuf[tbufposn++] = graphics.posn.x;
      tracebuf[tbufposn++] = graphics.posn.y;
      tracebuf[tbufposn++] = (graphics.inten << 8) + graphics.ltype;
      if (tbufposn == TBUF_LEN) write_trace();
    }

  x = xpos + (x * (int) width) / sim_wd;
  y = ypos + (int) height - 1 - (y * (int) height) / sim_ht;

  XtGa3270DrawLine(win, dt, graphics.ltype, graphics.inten,
		   startpt.x, startpt.y, x, y);
  startpt.x = x;
  startpt.y = y;
}


/*-----------------------------------------------------------------*/
/*  Draw vector that wraps the screen				   */
/*-----------------------------------------------------------------*/

static void wrapdraw(x, y, journal)
     int x, y;			/* Position in simulated GA */
     Boolean journal;		/* True if should be recorded */

{
  int fromx, fromy;		/* Original position */
  int tox, toy;			/* Wrapped endpoint */

  fromx = graphics.posn.x;
  fromy = graphics.posn.y;

  /* Do the normal vector */
  drawto(x, y, journal, True);

  /* Get wrapped endpoint */
  tox = x & 4095;
  toy = y & 4095;

  if (x < 0)			/* Off left */
    {
      moveto(fromx+sim_wd, fromy, journal, True);
      drawto(tox, toy, journal, True);
    }
  if (x > 4095)			/* Off right */
    {
      moveto(fromx-sim_wd, fromy, journal, True);
      drawto(tox, toy, journal, True);
    }
  if (y < 0)			/* Off bottom */
    {
      moveto(fromx, fromy+sim_wd, journal, True);
      drawto(tox, toy, journal, True);
    }
  if (y > 4095)			/* Off bottom */
    {
      moveto(fromx, fromy-sim_wd, journal, True);
      drawto(tox, toy, journal, True);
    }
  if (x < 0 && y < 0)		/* Bottom-left */
    {
      moveto(fromx+sim_wd, fromy+sim_wd, journal, True);
      drawto(tox, toy, journal, True);
    }
  if (x < 0 && y > 4095)	/* Top-left */
    {
      moveto(fromx+sim_wd, fromy-sim_wd, journal, True);
      drawto(tox, toy, journal, True);
    }
  if (x > 4095 && y > 4095)	/* Top-right */
    {
      moveto(fromx-sim_wd, fromy-sim_wd, journal, True);
      drawto(tox, toy, journal, True);
    }
  if (x > 4095 && y < 0)	/* Bottom-right */
    {
      moveto(fromx-sim_wd, fromy+sim_wd, journal, True);
      drawto(tox, toy, journal, True);
    }

  /* Modify position */
  graphics.posn.x = tox;
  graphics.posn.y = toy;
}


/*-----------------------------------------------------------------*/
/*  Write a buffer of trace to the tracefile.			   */
/*-----------------------------------------------------------------*/

static void write_trace()
{
  if (tbufposn == 0) return;

  if (!topen)
    {
      char tempfile[25], *tempname, *mktemp();

      /* Create a temporary file */
      (void) strcpy(tempfile, "/tmp/xantXXXXXX");
      tempname = mktemp(tempfile);
      tfile = open(tempname, O_RDWR | O_CREAT | O_TRUNC, 0666);
      if (tfile < 0) errorm("Can't open trace file");
      topen = True;

      /* Unlink the file so that it will be deleted when closed */
      unlink(tempname);
    }

  if (write(tfile, tracebuf, 2 * tbufposn) < 0)
    errorm("Write to trace file failed");
  tbufposn = 0;
}


/*-----------------------------------------------------------------*/
/*  Replay the trace file into the window			   */
/*-----------------------------------------------------------------*/

static void replay_trace()
{
  int bufposn;			/* Position in buffer */
  int x, y;			/* Point from buffer */
  int saveinten, saveltype;	/* Temporary save vals */
  int size;			/* Size to read in */
  if (!XtIsRealized(win)) return;

  /* If there is any outstanding trace, write it */
  write_trace();

  /* If no trace, nothing to do */
  if (!topen) return;

  /* Position trace file to beginning */
  if (lseek(tfile, 0, 0) < 0) errorm("lseek failed");

  /* Save current intensity and line type */
  saveinten = graphics.inten;
  saveltype = graphics.ltype;

  /* Replay the trace file */
  dt = dt_draw;
  while (True)
    {
      size = read(tfile, tracebuf, TBUF_LEN * 2);
      if (size < 0) errorm("Read from trace file failed");
      if (!size) break;
      size /= 2;

      bufposn = 0;
      while (bufposn < size)
	{
	  x = tracebuf[bufposn++];
	  y = tracebuf[bufposn++];
	  graphics.ltype = tracebuf[bufposn] & 0xFF;
	  graphics.inten = tracebuf[bufposn++] >> 8;
	  if ((x & TMOVE_CHECK) == TMOVE_POS)
	    moveto(0x3FFF & x, y, False, True);
	  else if ((x & TMOVE_CHECK) == TMOVE_NEG)
	    moveto(x ^ TMOVE_FLAG, y, False, True);
	  else
	    drawto(x, y, False, True);
	}
    }

  /* Restore line settings */
  graphics.inten = saveinten;
  graphics.ltype = saveltype;
}



/*-----------------------------------------------------------------*/
/*  Process an alpha-mode ordinal				   */
/*-----------------------------------------------------------------*/

#define BRANCH	0x00010000
#define BRADDR	0x000007FF
#define ENDBIT	0x00020000
#define CRBIT	0x00004000
#define XNEG	0x00002000
#define XCORD	0x00001F80
#define YNEG	0x00000040
#define YCORD	0x0000003F
#define MBIT	0x00008000

static void alfmd(ordinal, journal)
     unsigned char ordinal;	/* Alpha mode ordinal */
     Boolean journal;		/* True if journalling */

{
  int x, y;			/* Delta x, y */
  Boolean done;			/* True when done */
  int ltsave;			/* Old line type */
  int word;			/* Word of pattern generator */
  int ppgaddr;			/* Address in graphics.ppg */

  /* Use line-type 0 when drawing characters */
  ltsave = graphics.ltype;
  graphics.ltype = 0;

  /* Scan pattern generator until char complete */
  ppgaddr = ordinal;
  done = False;
  while (!done)
    {
      word = graphics.ppg[ppgaddr];
      if ((word & BRANCH) != 0)
	ppgaddr = word & BRADDR;
      else
	{
	  /* Do a stroke of a character */
	  if ((word & ENDBIT) != 0)
	    done = True;

	  /* If cr bit on, move to margin */
	  if ((word & CRBIT) != 0)
	    {
	      if (graphics.crdir)
		moveto(graphics.posn.x, 0, journal, False);
	      else
		moveto(0, graphics.posn.y, journal, False);
	    }

	  /* Get x delta */
	  x = (word & XCORD) >> 7;
	  if ((word & XNEG) != 0)
	    x = -x;

	  /* Get y delta */
	  y = word & YCORD;
	  if ((word & YNEG) != 0)
	    y = -y;

	  /* Move or draw */
	  if ((word & MBIT) != 0)
	    moveto(graphics.posn.x + graphics.scale * x,
		   graphics.posn.y + graphics.scale * y,
		   journal, False);
	  else
	    drawto(graphics.posn.x + graphics.scale * x,
		   graphics.posn.y + graphics.scale * y,
		   journal, False);

	  /* Go to next ppg address */
	  ppgaddr = (ppgaddr+1) & 0x7FF;
	}
    }

  /* Restore original line type */
  graphics.ltype = ltsave;
}


/*-----------------------------------------------------------------*/
/*  Process an lpg-mode ordinal					   */
/*-----------------------------------------------------------------*/

static void lpgmd(ordinal)
     unsigned char ordinal;	/* Vector mode ordinal */

{
  switch (graphics.ppgcnt)
    {
    case 0:
      graphics.ppg[graphics.nxtppg] = (ordinal & 0x3F) << 14;
      graphics.ppgcnt++;
      break;
    case 1:
      graphics.ppg[graphics.nxtppg] |= (ordinal & 0x7F) << 7;
      graphics.ppgcnt++;
      break;
    case 2:
      graphics.ppg[graphics.nxtppg] |= ordinal & 0x7F;
      graphics.nxtppg = (graphics.nxtppg+1) & 0x7FF;
      graphics.ppgcnt = 0;
      break;
    }
}


/*-----------------------------------------------------------------*/
/*  Process an itr-mode ordinal					   */
/*-----------------------------------------------------------------*/

static void itrmd(ordinal, first)
     unsigned char ordinal;	/* Vector mode ordinal */
     Boolean first;		/* First time for refresh buffer */

{
  graphics.vecwrd[graphics.veccnt++] = ordinal;
  if (graphics.veccnt == 4)
    {
      if (first)
	{
	  graphics.treg.x = ((graphics.vecwrd[0] & L6BITS) << 6) +
	    (graphics.vecwrd[1] & L6BITS);
	  graphics.treg.y = ((graphics.vecwrd[2] & L6BITS) << 6) +
	    (graphics.vecwrd[3] & L6BITS);
	}
      graphics.veccnt = 0;
      graphics.gmode = graphics.itrsave;
    }
}
