/* Graphics support

   Most of this code is based on information from IBM 3179-G/3192-G
   Color Graphics Display Station Description (GA18-2589) and the
   Graphics Object Content Architecture Reference (SC31-6804).

   Note:  This code is still under development.  It works okay for
   some applications, but a lot remains to be done.  Lots of graphics
   functions do not work, or work incorrectly.				*/
   

#include <stdio.h>
#include <memory.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include "Gr3270.h"


/* Outbound structured field IDs */
#define SF_TWO		0x0F	/* Start of two-byte ID field */
#define SF2_OBJCONTROL	0x11	/* Object Control */
#define SF2_OBJPICT	0x10	/* Object Picture */
#define SF2_OBJDATA	0x0F	/* Object Data */

/* Graphics command codes */
#define GC_PROC	0x30		/* Begin Procedure */
#define GC_SEG	0x70		/* Begin Segment */
#define GC_DATA	0x40		/* Begin Data Unit */

/* Procedural instructions */
#define GERASE	0x0A		/* Erase Graphic Presentation Space */
#define GSTOPDR	0x0F		/* Stop Draw */
#define GATTCUR	0x08		/* Attach Graphic Cursor */
#define GDETCUR	0x09		/* Detach Graphic Cursor */
#define GSETCUR	0x31		/* Set Graphic Cursor Position */
#define GSCUDEF	0x21		/* Set Current Defaults */
#define GNOP1	0x00		/* No Operation */
#define GCOMT	0x01		/* Comment */

/* Graphics drawing orders */
#define GNOP1	0x00		/* No Operation */
#define GCOMT	0x01		/* Comment */
#define GPSCC	0x03		/* Push-and-Set Character Cell */
#define GSGCH	0x04		/* Segment Characteristics */
#define GCALLS	0x07		/* Call Segment */
#define GSPS	0x08		/* Set Pattern Set */
#define GPSPT	0x09		/* Push-and-Set Pattern Symbol */
#define GSCOL	0x0A		/* Set Color */
#define GSMX	0x0C		/* Set Mix */
#define GSBMX	0x0D		/* Set Background Mix */
#define GSFLW	0x11		/* Set Fractional Line Width */
#define GSIA	0x14		/* Set Individual Attribute */
#define GSSLW	0x15		/* Set Stroke Line Width */
#define GSLT	0x18		/* Set Line Type */
#define GSLW	0x19		/* Set Line Width */
#define GSLE	0x1A		/* Set Line End */
#define GSLJ	0x1B		/* Set Line Join */
#define GSCP	0x21		/* Set Current Position */
#define GSAP	0x22		/* Set Arc Parameters */
#define GPSPIK	0x23		/* Push-and-Set Pick Identifier */
#define GSTM	0x24		/* Set Model Transform */
#define GSBCOL	0x25		/* Set Background Color */
#define GSECOL	0x26		/* Set Extended Color */
#define GSVW	0x27		/* Set Viewing Window */
#define GSPT	0x28		/* Set Pattern Symbol */
#define GSMT	0x29		/* Set Marker Symbol */
#define GSTV	0x31		/* Set Viewing Transform */
#define GSSB	0x32		/* Set Segment Boundary */
#define GSCC	0x33		/* Set Character Cell */
#define GSCA	0x34		/* Set Character Angle */
#define GSCH	0x35		/* Set Character Shear */
#define GSCBS	0x36		/* Set Character-Box Spacing (1) */
#define GSMC	0x37		/* Set Marker Cell */
#define GSCS	0x38		/* Set Character Set */
#define GSCR	0x39		/* Set Character Precision */
#define GSCD	0x3A		/* Set Character Direction */
#define GSMP	0x3B		/* Set Marker Precision */
#define GSMS	0x3C		/* Set Marker Set */
#define GEPROL	0x3E		/* End Prologue */
#define GPOP	0x3F		/* Pop */
#define GSMSC	0x41		/* Marker Scale (1) */
#define GSPIK	0x43		/* Set Pick Identifier */
#define GPSPS	0x48		/* Push-and-Set Pattern Set */
#define GEEL	0x49		/* End Element */
#define GPSCOL	0x4A		/* Push-and-Set Color */
#define GPSMX	0x4C		/* Push-and-Set Mix */
#define GPSBMX	0x4D		/* Push-and-Set Background Mix */
#define GPSTA	0x50		/* Push-and-Set Text Alignment (1) */
#define GPSFLW	0x51		/* Push-and-Set Fractional Line Width */
#define GSSPOS	0x53		/* Segment Position (1) */
#define GPSIA	0x54		/* Push-and-Set Individual Attribute */
#define GPSSLW	0x55		/* Push-and-Set Stroke Line Width */
#define GPSLT	0x58		/* Push-and-Set Line Type */
#define GPSLW	0x59		/* Push-and-Set Line Width */
#define GPSLE	0x5A		/* Push-and-Set Line End */
#define GPSLJ	0x5B		/* Push-and-Set Line Join */
#define GEAR	0x60		/* End Area */
#define GPSCP	0x61		/* Push-and-Set Current Position */
#define GPSAP	0x62		/* Push-and-Set Arc Parameters */
#define GPSTM	0x64		/* Push-and-Set Model Transform */
#define GPSBCOL	0x65		/* Push-and-Set Background Color */
#define GPSECOL	0x66		/* Push-and-Set Extended Color */
#define GPSVW	0x67		/* Push-and-Set Viewing Window */
#define GBAR	0x68		/* Begin Area */
#define GPSMT	0x69		/* Push-and-Set Marker Symbol */
#define GBSEG	0x70		/* Segment Start (1) */
#define GESEG	0x71		/* Segment End (1) */
#define GISAT	0x72		/* Segment Attribute (1) */
#define GMSAT	0x73		/* Segment Attribute Modify (1) */
#define GPSCA	0x74		/* Push-and-Set Character Angle */
#define GPSCH	0x75		/* Push-and-Set Character Shear */
#define GPSMC	0x77		/* Push-and-Set Marker Cell */
#define GPSCS	0x78		/* Push-and-Set Character Set */
#define GPSCR	0x79		/* Push-and-Set Character Precision */
#define GPSCD	0x7A		/* Push-and-Set Character Direction */
#define GPSMP	0x7B		/* Push-and-Set Marker Precision */
#define GPSMS	0x7C		/* Push-and-Set Marker Set */
#define GCLFIG	0x7D		/* Close Figure */
#define GEPTH	0x7F		/* End Path */
#define GCBOX	0x80		/* Box at CP */
#define GCLINE	0x81		/* Line at CP */
#define GCMRK	0x82		/* Marker at CP */
#define GCHSTC	0x83		/* Character String at CP */
#define GCFLT	0x85		/* Fillet at CP */
#define GCARC	0x86		/* Arc at CP */
#define GCFARC	0x87		/* Full Arc at CP */
#define GCBIMG	0x91		/* Begin Image at CP */
#define GIMD	0x92		/* Image Data */
#define GEIMG	0x93		/* End Image */
#define GSPRP	0xA0		/* Set Pattern Reference Point */
#define GCRLINE	0xA1		/* Relative Line at CP */
#define GCPARC	0xA3		/* Partial Arc at CP */
#define GCSFLT	0xA4		/* Sharp Fillet at CP */
#define GCBEZ	0xA5		/* Bezier Curve at CP */
#define GSICOL	0xA6		/* Set Indexed Color */
#define GSBICOL	0xA7		/* Set Background Indexed Color */
#define GCCHSTM	0xB1		/* Character String Move at CP */
#define GSCPTH	0xB4		/* Set Clip Path */
#define GBOX	0xC0		/* Box */
#define GLINE	0xC1		/* Line */
#define GMRK	0xC2		/* Marker */
#define GCHST	0xC3		/* Character String */
#define GFLT	0xC5		/* Fillet */
#define GARC	0xC6		/* Arc */
#define GFARC	0xC7		/* Full Arc */
#define GBPTH	0xD0		/* Begin Path */
#define GBIMG	0xD1		/* Begin Image */
#define GBEL	0xD2		/* Begin Element */
#define GLBL	0xD3		/* Label */
#define GBBLT	0xD6		/* Bitblt */
#define GFPTH	0xD7		/* Fill Path */
#define GMPTH	0xD8		/* Modify Path */
#define GPSPRP	0xE0		/* Push-and-Set Pattern Reference Point */
#define GRLINE	0xE1		/* Relative Line */
#define GPARC	0xE3		/* Partial Arc */
#define GSFLT	0xE4		/* Sharp Fillet */
#define GBEZ	0xE5		/* Bezier Curve */
#define GPSICOL	0xE6		/* Push-and-Set Indexed Color */
#define GPSBICO 0xE7		/* Push-and-Set Background Indexed Color */
#define GCHSTM	0xF1		/* Character String Move */
#define GESD	0xFF		/* End of Symbol Definition */
/* Note (1):  Not defined in GOCA manual.  I got them from the GDDM manual.  */

/* Other definitions */
#define HWORD(a, n) ((a[n] << 8) + a[(n) + 1])
#define OFFSET(n) (buff[n] & 0x80 ? buff[n] - 256 : buff[n])
#define COORD(n) (buff[n] & 0x80 ? \
		  (buff[n] << 8) + buff[n + 1] - 65536 : \
		  (buff[n] << 8) + buff[n + 1])
#define storew(a, i, val) { a[i] = (val) >> 8; a[(i)+1] = (val) & 0xff; }
#define MAXSTRING 500		/* Maximum length of a graphic string */


/* Stored segments aren't supported.  Only chained segments in immediate
   mode can be used.  However, the segment data is saved in a linked list
   so that the picture can be re-drawn after an expose event.  We could
   use some other representation to save the picture, but it's simplest
   to just save the original segment data.				*/

typedef struct _Segment {
  struct _Segment *next;	/* Link to next segment */
  int len;			/* Length of data */
  unsigned char *data;		/* Pointer to data */
  Boolean append;		/* True if this is an append to previous seg */
  int color;			/* Default drawing color */
  int linet;			/* Default line type */
  int linew;			/* Default line width */
  char pattern;			/* Default pattern symbol */
  char marker;			/* Default marker symbol */

  /*...Other things that might go in here:
          Extent data for comparing with expose event
	  Flag to show that segment needs to be redrawn
  */
} Segment;    


/* Types of dashed lines */
static char
  dotted[] =          { 1, 3 },
  short_dashed[] =    { 4, 4 },
  dash_dot[] =        { 4, 3, 1, 3 },
  double_dot[] =      { 1, 3, 1, 7 },
  long_dashed[] =     { 7, 4 },
  dash_double_dot[] = { 4, 3, 1, 3, 1, 3 };

static char *dashes[] =
{
  NULL, dotted, short_dashed, dash_dot, double_dot, long_dashed,
  dash_double_dot
};

static int dash_len[] =
{
  0, sizeof dotted, sizeof short_dashed, sizeof dash_dot, sizeof double_dot,
  sizeof long_dashed, sizeof dash_double_dot
};


/* Marker bitmaps */
#define MWIDTH 7
#define MHEIGHT 7
 
static char
  mk1_bits[] = { 0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41 }, /* Cross */
  mk2_bits[] = { 0x08, 0x08, 0x08, 0x7f, 0x08, 0x08, 0x08 }, /* Plus */
  mk3_bits[] = { 0x08, 0x14, 0x22, 0x41, 0x22, 0x14, 0x08 }, /* Diamond */
  mk4_bits[] = { 0x7f, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7f }, /* Square */
  mk5_bits[] = { 0x08, 0x49, 0x2a, 0x1c, 0x2a, 0x49, 0x08 }, /* 6-point star */
  mk6_bits[] = { 0x08, 0x2a, 0x1c, 0x7f, 0x1c, 0x2a, 0x08 }, /* 8-point star */
  mk7_bits[] = { 0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08 }, /* Full diamond */
  mk8_bits[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, /* Full square */
  mk9_bits[] = { 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x00, 0x00 }, /* Dot */
  mk10_bits[]= { 0x00, 0x1c, 0x22, 0x22, 0x22, 0x1c, 0x00 }; /* Small circle */
 
static char *mk_bits[] =
{
  mk1_bits, mk2_bits, mk3_bits, mk4_bits, mk5_bits, mk6_bits, mk7_bits,
  mk8_bits, mk9_bits, mk10_bits
};
static Pixmap mkpix[10];
 

/* Fill patterns */
#define PWIDTH 16
#define PHEIGHT 16
static char bit1[] = {
   0x24, 0x49, 0xdb, 0xb6, 0xdb, 0xb6, 0x24, 0x49, 0x24, 0x49, 0xdb, 0xb6,
   0xdb, 0xb6, 0x24, 0x49, 0x24, 0x49, 0xdb, 0xb6, 0xdb, 0xb6, 0x24, 0x49,
   0x24, 0x49, 0xdb, 0xb6, 0xdb, 0xb6, 0x24, 0x49};
static char bit2[] = {
   0x24, 0x91, 0x92, 0x4a, 0x49, 0x24, 0x49, 0x24, 0x24, 0x91, 0x92, 0x4a,
   0x49, 0x24, 0x49, 0x24, 0x24, 0x91, 0x92, 0x4a, 0x49, 0x24, 0x49, 0x24,
   0x24, 0x91, 0x92, 0x4a, 0x49, 0x24, 0x49, 0x24};
static char bit3[] = {
   0x42, 0x84, 0x11, 0x22, 0x84, 0x08, 0x11, 0x22, 0x44, 0x88, 0x09, 0x12,
   0xa0, 0x40, 0x0a, 0x14, 0x20, 0x41, 0x05, 0x0a, 0xa0, 0x40, 0x08, 0x11,
   0x42, 0x84, 0x11, 0x22, 0x84, 0x08, 0x11, 0x22};
static char bit4[] = {
   0x12, 0x12, 0x03, 0x03, 0x21, 0x21, 0x30, 0x30, 0x12, 0x12, 0x03, 0x03,
   0x21, 0x21, 0x30, 0x30, 0x12, 0x12, 0x03, 0x03, 0x21, 0x21, 0x30, 0x30,
   0x12, 0x12, 0x03, 0x03, 0x21, 0x21, 0x30, 0x30};
static char bit5[] = {
   0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x00,
   0x00, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x10,
   0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x10};
static char bit6[] = {
   0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static char bit7[] = {
   0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10,
   0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
   0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00};
static char bit8[] = {
   0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static char bit9[] = {
   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
static char bit10[] = {
   0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static char bit11[] = {
   0x80, 0x80, 0x80, 0x80, 0x40, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20,
   0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
   0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01};
static char bit12[] = {
   0x00, 0xc0, 0x00, 0x30, 0x00, 0x0c, 0x00, 0x03, 0xc0, 0x00, 0x30, 0x00,
   0x0c, 0x00, 0x03, 0x00, 0x00, 0xc0, 0x00, 0x30, 0x00, 0x0c, 0x00, 0x03,
   0xc0, 0x00, 0x30, 0x00, 0x0c, 0x00, 0x03, 0x00};
static char bit13[] = {
   0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04,
   0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20,
   0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x80, 0x80};
static char bit14[] = {
   0x03, 0x00, 0x0c, 0x00, 0x30, 0x00, 0xc0, 0x00, 0x00, 0x03, 0x00, 0x0c,
   0x00, 0x30, 0x00, 0xc0, 0x03, 0x00, 0x0c, 0x00, 0x30, 0x00, 0xc0, 0x00,
   0x00, 0x03, 0x00, 0x0c, 0x00, 0x30, 0x00, 0xc0};


/* Here are the 9x12 fill patterns used by a real 3179G.  They are a
   lot slower than 16x16 patterns, so they aren't used.  */

#ifdef old_fill_patterns
#define PWIDTH 9
#define PHEIGHT 12
static char bit1[] = {
   0x24, 0x01, 0xdb, 0x00, 0xdb, 0x00, 0x24, 0x01, 0x24, 0x01, 0xdb, 0x00,
   0xdb, 0x00, 0x24, 0x01, 0x24, 0x01, 0xdb, 0x00, 0xdb, 0x00, 0x24, 0x01};
static char bit2[] = {
   0x24, 0x01, 0x92, 0x00, 0x49, 0x00, 0x49, 0x00, 0x24, 0x01, 0x92, 0x00,
   0x49, 0x00, 0x49, 0x00, 0x24, 0x01, 0x92, 0x00, 0x49, 0x00, 0x49, 0x00};
static char bit3[] = {
   0x42, 0x00, 0x11, 0x00, 0x84, 0x00, 0x11, 0x00, 0x44, 0x00, 0x09, 0x00,
   0xa0, 0x00, 0x0a, 0x00, 0x20, 0x01, 0x05, 0x00, 0xa0, 0x00, 0x08, 0x01};
static char bit4[] = {
   0x42, 0x00, 0x03, 0x00, 0x11, 0x00, 0x18, 0x00, 0x88, 0x00, 0xc0, 0x00,
   0x42, 0x00, 0x03, 0x00, 0x11, 0x00, 0x18, 0x00, 0x88, 0x00, 0xc0, 0x00};
static char bit5[] = {
   0x01, 0x00, 0x08, 0x00, 0x40, 0x00, 0x01, 0x00, 0x08, 0x00, 0x40, 0x00,
   0x01, 0x00, 0x08, 0x00, 0x40, 0x00, 0x01, 0x00, 0x08, 0x00, 0x40, 0x00};
static char bit6[] = {
   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00};
static char bit7[] = {
   0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
   0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static char bit8[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static char bit9[] = {
   0x49, 0x00, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00,
   0x49, 0x00, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00};
static char bit10[] = {
   0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00};
static char bit11[] = {
   0x00, 0x01, 0x80, 0x00, 0x40, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00,
   0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00};
static char bit12[] = {
   0x80, 0x01, 0xc0, 0x00, 0x30, 0x00, 0x18, 0x00, 0x06, 0x00, 0x03, 0x00,
   0x80, 0x01, 0xc0, 0x00, 0x30, 0x00, 0x18, 0x00, 0x06, 0x00, 0x03, 0x00};
static char bit13[] = {
   0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00,
   0x20, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x01};
static char bit14[] = {
   0x03, 0x00, 0x06, 0x00, 0x18, 0x00, 0x30, 0x00, 0xc0, 0x00, 0x80, 0x01,
   0x03, 0x00, 0x06, 0x00, 0x18, 0x00, 0x30, 0x00, 0xc0, 0x00, 0x80, 0x01};
#endif

static char *bits[] =
{
  bit1, bit2, bit3, bit4, bit5, bit6, bit7, bit8, bit9, bit10, bit11,
  bit12, bit13, bit14
};
static Pixmap bitpix[14];


enum states
{
  st_init, st_parmlen, st_parms, st_data, st_skip
};


/* Global variables */
static Display *dpy;		/* The current display */
static int scr;			/* Screen to use */
static Colormap cmap;		/* Color map */
static Widget graphw;		/* Widget where we do graphics */
static Window gwin;		/* The window ID of graphw */
static enum states state;	/* Input state */
static unsigned char cur_id;	/* ID of current structured field */
static Boolean last_part;	/* True if this is last part of data */
static Segment *seglist;	/* Tail pointer to segment list */
static Segment *nextseg;	/* Next segment waiting to be drawn */
static Segment *freeseg;	/* Free segment list */
static GC gc;			/* Graphics context for drawing */
static GC cursgc;		/* Graphics context for cursor */
static GC cleargc;		/* Graphics context for clearing cursor */
static Position xorig, yorig;	/* Graphics origin within the window */
static Pixel colortab[17];	/* Color table */
static XColor colordefs[17];	/* RGB values of colors */
static unsigned char colorinfo[16][3]; /* Color info for query reply */
static char *trbuff;		/* A buffer for translating strings */
static int area_len;		/* Maximum number of points in area */
static Boolean gcursor;		/* True if graphic cursor is attached */
static int xcurs, ycurs;	/* Position of the graphic cursor */

/* Drawing defaults */
static int def_color;		/* Default drawing color */
static int def_linet;		/* Default line type */
static int def_linew;		/* Default line width */
static char def_pattern;	/* Default pattern symbol */
static char def_marker;		/* Default marker symbol */

/* Drawing context */
static short xcur, ycur;	/* Current x,y drawing position */
static int draw_color;		/* Current drawing color */
static int linet;		/* Line type */
static int linew;		/* Line width */
static XPoint *area;		/* A list of points making up an area */
static int aindex;		/* Index of current point (-1 = no area) */
static int afigure;		/* Index of start of current figure */
static int acolor;		/* Drawing color of area */
static char pattern;		/* Fill pattern symbol */
static char marker;		/* Marker symbol */
static int im_width, im_height;	/* Size of image */
static int im_bpl;		/* Number of bytes per line in image */
static int im_row;		/* Current row being processed in image */
static char *im_data;		/* Image data */

/* What graphic data should be sent inbound (specified by request block) */
static Boolean send_request;	/* Request Block */
static Boolean send_user;	/* User Area */
static Boolean send_control;	/* Control Record */
static Boolean send_log;	/* Log Record */

static void reset(), doproc(), dodata(), draw(), draw_line(), end_figure(),
  draw_string(), draw_mark(), draw_curs(), clear_curs();

/* Externally defined variables */
extern Boolean debug;		/* Debugging flag */
extern char etoa[];		/* EBCDIC to ASCII translation table */


/* Initialization */
graph_init(display, screen, graph_widget, fontid)
     Display *display;
     int screen;
     Widget graph_widget;
     Font fontid;
{
  int i;
  Arg arglist[30];
  Dimension width, height;
  unsigned long valuemask;
  XGCValues values;

  dpy = display;
  scr = screen;
  graphw = graph_widget;

  gwin = XtWindow(graphw);
  state = st_init;
  last_part = False;
  seglist = nextseg = freeseg = NULL;
  area_len = 100;
  if (!(area = (XPoint *) malloc(area_len * sizeof(XPoint))) ||
      !(trbuff = (char *) malloc(MAXSTRING)))
    error("Not enough memory");

  /* Create Pixmaps for pattern filling */
  for (i = 0; i < sizeof bits / sizeof(char *); i++) 
    {
      bitpix[i] = XCreateBitmapFromData(dpy, gwin, bits[i], PWIDTH, PHEIGHT);
      if (!bitpix[i]) error("Can't create Pixmap for bitmap");
    }

  /* Create Pixmaps for polymarkers */
  for (i = 0; i < sizeof mk_bits / sizeof(char *); i++)
    {
      mkpix[i] = XCreateBitmapFromData(dpy, gwin, mk_bits[i], MWIDTH, MHEIGHT);
      if (!mkpix[i]) error("Can't create Pixmap for marker symbol");
    }
 
  /* Initialize color values */
  i = 0;
  if (DisplayCells(dpy, scr) > 2)
    {
      XtSetArg(arglist[i], XtNforeColor,&colortab[0]);  i++;
      XtSetArg(arglist[i], XtNgColor1,	&colortab[1]);	i++;
      XtSetArg(arglist[i], XtNgColor2,	&colortab[2]);  i++;
      XtSetArg(arglist[i], XtNgColor3,	&colortab[3]);  i++;
      XtSetArg(arglist[i], XtNgColor4,	&colortab[4]);  i++;
      XtSetArg(arglist[i], XtNgColor5,	&colortab[5]);  i++;
      XtSetArg(arglist[i], XtNgColor6,	&colortab[6]);  i++;
      XtSetArg(arglist[i], XtNgColor7,	&colortab[7]);  i++;
      XtSetArg(arglist[i], XtNgColor8,	&colortab[8]);  i++;
      XtSetArg(arglist[i], XtNgColor9,	&colortab[9]);  i++;
      XtSetArg(arglist[i], XtNgColor10,	&colortab[10]); i++;
      XtSetArg(arglist[i], XtNgColor11,	&colortab[11]); i++;
      XtSetArg(arglist[i], XtNgColor12,	&colortab[12]); i++;
      XtSetArg(arglist[i], XtNgColor13,	&colortab[13]); i++;
      XtSetArg(arglist[i], XtNgColor14,	&colortab[14]); i++;
      XtSetArg(arglist[i], XtNgColor15,	&colortab[15]); i++;
      XtSetArg(arglist[i], XtNgColor16,	&colortab[16]); i++;
    }
  else
    {
      XtSetArg(arglist[i], XtNbackground, &colortab[0]);  i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[1]);  i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[2]);  i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[3]);  i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[4]);  i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[5]);  i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[6]);  i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[7]);  i++;
      XtSetArg(arglist[i], XtNbackground, &colortab[8]);  i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[9]);  i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[10]); i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[11]); i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[12]); i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[13]); i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[14]); i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[15]); i++;
      XtSetArg(arglist[i], XtNforeground, &colortab[16]); i++;
    }
  XtSetArg(arglist[i], XtNcolormap, &cmap); i++;
  XtGetValues(graphw, arglist, i);

  for (i = 0; i < 17; i++)
    colordefs[i].pixel = colortab[i];
  XQueryColors(dpy, cmap, colordefs, 17);

  for (i = 1; i < 17; i++)
    {
      int j;
      /* Move position 8 to position 0 and position 16 to position 8 */
      if (i == 8) j = 0;
      else if (i == 16) j = 8;
      else j = i;

      colorinfo[j][0] = colordefs[i].red >> 8;
      colorinfo[j][1] = colordefs[i].blue >> 8;
      colorinfo[j][2] = colordefs[i].green >> 8;
    }

  valuemask = GCForeground | GCBackground | GCFont;
  values.foreground = colortab[0];
  values.background = colortab[8];
  values.font = fontid;
  gc = XCreateGC(dpy, gwin, valuemask, &values);

  valuemask = GCForeground | GCBackground;
  values.foreground = colortab[0];
  values.background = colortab[8];
  cursgc = XtGetGC(graphw, valuemask, &values);

  valuemask = GCForeground | GCBackground;
  values.foreground = colortab[8];
  values.background = colortab[8];
  cleargc = XtGetGC(graphw, valuemask, &values);

  /* Set drawing origin to center of window */
  i = 0;
  XtSetArg(arglist[i], XtNwidth, &width); i++;
  XtSetArg(arglist[i], XtNheight, &height); i++;
  XtGetValues(graphw, arglist, i);
  xorig = width / 2;
  yorig = height / 2;

  /* Set initial drawing defaults */
  def_color = 7;		/* White */
  def_linet = 7;		/* Solid line */
  def_linew = 1;		/* Normal thickness */
  def_pattern = 16;		/* Solid shading */
  def_marker = 1;		/* Cross */

  /* Initialize drawing context */
  draw_color = def_color;
  linet = def_linet;
  linew = def_linew;
  pattern = def_pattern;
  marker = def_marker;
  im_data = NULL;
  reset();
  gcursor = False;
  xcurs = ycurs = 0;

  /* Initialize graphic data requested */
  send_request = send_user = send_control = send_log = False;
}


/* Start of graphics data (procedure, segment, or data unit) */
graph_start(id, first, last)
     unsigned char id;
     Boolean first, last;
{
  cur_id = id;
  if (first && state != st_init)
    {
      if (debug) printf("Last part of graphics data stream missing\n");
      state = st_init;
    }
  last_part = last;
}


/* Process graphics data */
graph_write(buff, len)
     register unsigned char *buff;
     register int len;
{
  register unsigned char c;
  int j;
  Segment *seg;
  static int cmdlen, cindex, datalen, dindex;
  static unsigned char cmd[14], *data;
    
  while (len > 0)
    {
      c = *buff++;
      len--;

      switch (state) 
	{
	case st_init:
	  /* +--------------+ */
	  /* | Command code | */
	  /* +--------------+ */
	  if (debug &&
	      (cur_id == SF2_OBJCONTROL && c != GC_PROC ||
	       cur_id == SF2_OBJPICT && c != GC_SEG ||
	       cur_id == SF2_OBJDATA && c != GC_DATA))
	    printf("Graphic command code doesn't match WSF ID\n");
	  cmd[0] = c;
	  state = st_parmlen;
	  break;
	  
	case st_parmlen:
	  /* +--------------------------+ */
	  /* | Get length of parameters | */
	  /* +--------------------------+ */
	  cmd[1] = c;
	  cmdlen = c + 2;
	  if (cmdlen < 10)
	    {
	      if (debug) printf("Graphics parameter length is too short\n");
	      state = st_skip;
	    }
	  if (debug && cmdlen > sizeof cmd)
	    printf("Graphics parameter length is too long\n");
	  cindex = 2;
	  state = st_parms;
	  break;
	  
	case st_parms:
	  /* +-----------------+ */
	  /* | Read parameters | */
	  /* +-----------------+ */
	  if (cindex < sizeof cmd) cmd[cindex] = c;
	  if (++cindex < cmdlen) break;

	  /* All parameters have been read */
	  datalen = HWORD(cmd, 8);
	  data = (unsigned char *) malloc(datalen);
	  if (!data) error("Not enough memory");
	  dindex = 0;
	  state = st_data;
	  break;
	  
	case st_data:
	  /* +-----------+ */
	  /* | Read data | */
	  /* +-----------+ */
	  buff--; len++;	/* Put back current character */
	  j = datalen - dindex;	/* Get length remaining to be filled */

	  if (len < j)
	    {
	      /* More data will be needed.  Copy this and wait for the rest. */
	      memcpy(&data[dindex], buff, len);
	      dindex += len;
	      len = 0;
	      break;
	    }
	  memcpy(&data[dindex], buff, j);
	  len -= j;
	  buff += j;
	  state = st_init;

	  /* Procedural instructions and request blocks are handled
	     immediately.  Drawing orders are saved and processed at
	     flush time or after expose events.  */

	  if (cmd[0] == GC_PROC)
	    {
	      doproc(data, datalen);
	      break;
	    }
	  if (cmd[0] == GC_DATA)
	    {
	      dodata(data, datalen);
	      break;
	    }

	  /* Allocate space for segment header */
	  if (freeseg) 
	    {
	      seg = freeseg;
	      freeseg = freeseg->next;
	    }
	  else if (!(seg = (Segment *) malloc(sizeof(Segment))))
	    error("Not enough memory");

	  /* Create a new segment */
	  seg->len = datalen;
	  seg->data = data;
	  if (!seglist) 
	    seglist = seg->next = nextseg = seg;
	  else
	    {
	      seg->next = seglist->next;
	      seglist->next = seg;
	      seglist = seg;
	      if (!nextseg) nextseg = seg;
	    }

	  /* Check for append flag */
	  j = (cmd[7] & 6) >> 1;
	  seg->append = (j == 3 && seglist);
	  if (seg->append) break;
	  
	  /* The drawing routine needs to know the default values that 
	     were in effect when the segment was created, so save them.  */

	  seg->color = def_color;
	  seg->linet = def_linet;
	  seg->linew = def_linew;
	  seg->pattern = def_pattern;
	  seg->marker = def_marker;
	  break;

	case st_skip:
	  /* +---------------------+ */
	  /* | Skip to end of data | */
	  /* +---------------------+ */
	  len = 0;
	  break;
	}
    }
}


/* End of graphics data */
graph_end()
{
  if (last_part && state != st_init)
    {
      if (debug) printf("Last graphics data stream was incomplete\n");
      state = st_init;
    }
}


/* Delete all saved graphic data */
graph_delete()
{
  Segment *s, *t;
  
  clear_curs();
  gcursor = False;
  xcurs = ycurs = 0;
  send_request = send_user = send_control = send_log = False;
  if (!seglist) return 0;

  /* Free all the segments */
  s = seglist->next;
  do
    {
      free(s->data);
      t = s->next;
      s->next = freeseg;
      freeseg = s;
      s = t;
    }
  while (s != seglist);

  seglist = nextseg = NULL;
  return 0;
}


/* Flush out some pending graphics.  Return 1 if any work was done.  */
int graph_flush()
{
  if (!nextseg) return 0;
  draw(nextseg);
  if (nextseg == seglist) nextseg = NULL;
  else nextseg = nextseg->next;
  return 1;
}


/* Change window size */
graph_resize(width, height)
     Dimension width, height;
{
  xorig = width / 2;
  yorig = height / 2;
}


/* Re-draw exposed area */
/*ARGSUSED*/ graph_expose(x, y, width, height)
     int x, y, width, height;
{
  /*...For now, just redraw everything */
  if (seglist) nextseg = seglist->next;
  draw_curs();
}


/* Set new text font */
graph_setfont(fontid)
     Font fontid;
{
  XSetFont(dpy, gc, fontid);
}


/* Set graphic cursor position.  Returns false if cursor is not attached.  */
int graph_cursor(x, y)
     int x, y;
{
  static unsigned char objdata[] =
    {
      0x88,			/* AID = Structured field */

      /* Graphic data SF */
      0, 52,			/* Length */
      SF_TWO, SF2_OBJDATA,	/* Structured field ID */
      0,			/* Partition identifier */
      0xC0,			/* Spanning flag = First and last */
      0,			/* Object type = Graphics */

      /* Begin data unit */
      GC_DATA,			/* Command */
      8,			/* Length of following parameters */
      0, 0, 0, 0, 0, 0,		/* Reserved */
      0, 35,			/* Length of data unit */

      /* Log record header */
      0, 35,			/* Offset to next available entry */
      0, 0,			/* Reserved */
      
      /* Log record entry */
      0, 31,			/* Length of entry */
      1,			/* Type = Control record */
      0, 0,			/* X locator value (filled in) */
      0, 0,			/* Y locator value (filled in) */
      0, 0,			/* Not used */
      0, 4,			/* Current port ID = Mouse button */
      0, 4,			/* Last button port ID */
      0, 1,			/* Last button value */
      0, 0, 0, 0, 0, 0, 0, 0,	/* Reserved */
      0, 0, 0, 0, 0, 0, 0, 0, 

      /* Inbound 3270DS SF */
      0, 7,			/* Length */
      0x80,			/* Inbound 3270DS */
      0,			/* PID */
      0x7D,			/* AID */
      0x40, 0x40,		/* Cursor address (filled in) */
    };


  if (!gcursor) return False;
  clear_curs();
  xcurs = x - xorig;
  ycurs = yorig - y;
  draw_curs();

  storew(objdata, 25, xcurs);
  storew(objdata, 27, ycurs);
  /*** storew(objdata, 58, cursaddr); ***/
  /*...We should fill in the real cursor address, but this is good
       enough for now...*/
  net_put(objdata, sizeof objdata);
  net_eor();
  return True;
}


/* Send inbound graphic data if host asked for it using a request block */
int graph_data(aid)
     unsigned char aid;
{
  static unsigned char grdata[] =
    {
      0x88,			/* AID = Structured field */

      /* Graphic data SF */
      0, 52,			/* Length */
      SF_TWO, SF2_OBJDATA,	/* Structured field ID */
      0,			/* Partition identifier */
      0xC0,			/* Spanning flag = First and last */
      0,			/* Object type = Graphics */

      /* Begin data unit */
      GC_DATA,			/* Command */
      8,			/* Length of following parameters */
      0, 0, 0, 0, 0, 0,		/* Reserved */
      0, 35,			/* Length of data unit */

      /* Log record header */
      0, 35,			/* Offset to next available entry */
      0, 0,			/* Reserved */
      
      /* Log record entry */
      0, 31,			/* Length of entry */
      1,			/* Type = Control record */
      0, 0,			/* X locator value (filled in) */
      0, 0,			/* Y locator value (filled in) */
      0, 0,			/* Not used */
      0, 7,			/* Current port ID = AID key button */
      0, 7,			/* Last button port ID */
      0xFF, 0,			/* Last button value (filled in with AID) */
      0, 0, 0, 0, 0, 0, 0, 0,	/* Reserved */
      0, 0, 0, 0, 0, 0, 0, 0, 

      /* Inbound 3270DS SF */
      0, 0,			/* Length */
      0x80,			/* Inbound 3270DS */
      0				/* PID */
    };

  /* Exit if graphics data not requested */
  if (!send_log) return 0;

  send_log = False;
  storew(grdata, 25, xcurs);
  storew(grdata, 27, ycurs);
  grdata[36] = aid;
  net_put(grdata, sizeof grdata);
  return 0;
}


/* Return color info to use in WSF Query Reply */
unsigned char *graph_colorinfo()
{
  return (unsigned char *) colorinfo;
}


/* Reset the context */
static void reset()
{
  xcur = xorig;
  ycur = yorig;
  aindex = -1;
  acolor = draw_color;
  XSetForeground(dpy, gc, colortab[draw_color]);

  if (linet == 7)	/* Solid */
    XSetLineAttributes(dpy, gc, linew == 1 ? 0 : 2,
		       LineSolid, CapButt, JoinMiter);
  else if (linet < sizeof dashes / sizeof(char *)) 
    {
      XSetDashes(dpy, gc, 0, dashes[linet], dash_len[linet]);
      XSetLineAttributes(dpy, gc, linew == 1 ? 0 : 2,
			 LineOnOffDash, CapButt, JoinMiter);
    }

  /* Free any uncompleted image */
  if (im_data)
    {
      free(im_data);
      im_data = NULL;
    }
}


/* Process procedural instructions */
static void doproc(buff, len)
     unsigned char *buff;
     int len;
{
  unsigned char c, *data;
  int parmlen, dlen;
  int mask;
  Boolean std;

  while (len > 0)
    {
      c = *buff++;
      len--;
      if (c == GNOP1) parmlen = 0;
      else if ((c >> 4) < 8 && (c & 15) >= 8) parmlen = 1;
      else
	{
	  if (!len) break;
	  parmlen = *buff++;
	  len--;
	}
      if (parmlen > len) break;

      /* At this point, "buff" points to the first character of the
	 parameters, and "parmlen" has the length.  */
      
      switch (c)
	{
	case GERASE:
	  /* Erase Graphic Presentation Space */
	  graph_delete();
	  XClearArea(dpy, gwin, 0, 0, 0, 0, True);
	  break;
	  
	case GATTCUR:
	  /* Attach Graphic Cursor */
	  gcursor = True;
	  draw_curs();
	  break;
	  
	case GDETCUR:
	  /* Detach Graphic Cursor */
	  clear_curs();
	  gcursor = False;
	  break;
	  
	case GSETCUR:
	  /* Set Graphic Cursor Position */
	  if (parmlen < 4 || (buff[1] & 15) != 15) break;
	  if (gcursor) clear_curs();
	  switch ((*buff & 0xC0) >> 6)
	    {
	    case 1:		/* Y position in data */
	      ycurs = COORD(2);
	      break;
	    case 2:		/* X position in data */
	      xcurs = COORD(2);
	      break;
	    case 3:		/* Both X and Y positions in data */
	      xcurs = COORD(2);
	      if (parmlen < 6) break;
	      ycurs = COORD(4);
	      break;
	    }
	  if (gcursor) draw_curs();
	  break;
	  
	case GSCUDEF:
	  /* Set Current Defaults */
	  if (parmlen < 4 || (buff[3] & 15) != 15) break;
	  mask = HWORD(buff, 1);
	  std = (buff[3] & 0x80) == 0;
	  data = &buff[4];
	  dlen = parmlen - 4;
	  
	  switch (*buff)
	    {
	    case 0:
	      /* Drawing Attributes */
	      if (mask & 0x8000)
		{
		  /* Set color */
		  if (std) def_color = 7;
		  else if (dlen > 1)
		    {
		      if (data[1] && data[1] < sizeof colortab / sizeof(Pixel))
			def_color = data[1];
		      else
			def_color = 7;
		      data += 2;
		      dlen -= 2;
		    }
		}
	      /* Foreground and background mix are not supported */
	      break;
	      
	    case 1:
	      /* Line Attributes */
	      if (mask & 0x8000)
		{
		  /* Set line type */
		  if (std) def_linet = 7;
		  else if (dlen > 0)
		    {
		      if (*data && *data < sizeof dashes / sizeof(char *))
			def_linet = *data;
		      else
			def_linet = 7;
		      data++;
		      dlen--;
		    }
		}
	      if (mask & 0x4000)
		{
		  /* Set line width */
		  if (std) def_linew = 1;
		  else if (dlen > 0) { def_linew = *data++; dlen--; }
		}
	      break;
	      
	    case 2:
	      /* Character Attributes */
	      /*...not supported */
	      break;
	      
	    case 3:
	      /* Marker Attributes */
	      if (mask & 0x0400)
		{
		  /* Cellsize...not supported */
		  if (!std && dlen > 4) { data += 4; dlen -= 4; }
		  /*...Should go by geometric param format instead of "4" */
		}
	      if (mask & 0x1000)
		{
		  /* Precision...not supported */
		  if (!std && dlen > 0) { data++; dlen--; }
		}
	      if (mask & 0x0400)
		{
		  /* Marker set...not supported */
		  if (!std && dlen > 0) { data++; dlen--; }
		}
	      if (mask & 0x0100)
		{
		  /* Marker symbol */
		  if (std) def_marker = 1;
		  else if (dlen > 0) { def_marker = *data++; dlen--; }
		}
	      break;
	      
	    case 4:
	      /* Pattern Attributes */
	      if (mask & 0x0800)
		{
		  /* Set symbol set...not supported */
		  if (!std && dlen > 0) { data++; dlen--; }
		}
	      if (mask & 0x0100)
		{
		  /* Set pattern symbol */
		  if (std) def_pattern = 16;
		  else if (dlen > 0)
		    {
		      def_pattern = *data++;
		      dlen--;
		    }
		}
	      break;
	      
	    case 11:
	      /* Arc Parameters */
	      /*...not supported */
	      break;
	      
	    default:
	      break;
	    }
	  break;
	  
	default:
	  break;
	}
      buff += parmlen;
      len -= parmlen;
    }
}


/* Process a request block */
static void dodata(buff, len)
     unsigned char *buff;
     int len;
{
  unsigned char *bptr;
  int rb_len, rb_num, send_len, send_num;

  /* Error if buffer is not big enough to hold a request block header */
  if (len < 20)
    {
      if (debug) printf("Data unit not big enough to hold request block\n");
      return;
    }

  /* Length in request block header should agree with data unit length */
  rb_len = HWORD(buff, 0);
  if (rb_len > len)
    {
      if (debug)
	printf("Request block length is bigger than data unit length\n");
      return;
    }

  /* Process send list, if present */
  send_len = HWORD(buff, 14);
  if (send_len)
    {
      /* Be sure that send list doesn't extend past end of request block */
      if (20 + send_len > rb_len)
	{
	  if (debug) printf("Send list is too long\n");
	  return;
	}
      
      /* Point to send list header */
      bptr = buff + 20;
      send_num = HWORD(bptr, 0);

      /* Process send list entries */
      bptr += 4;
      while (send_num-- > 0)
	{
	  if (bptr + 10 > buff + rb_len)
	    {
	      if (debug)
		printf("Send list extends past end of request block\n");
	      return;
	    }

	  /* Don't process unless type = data */
	  if (bptr[0] == 1 && bptr[4] == 0x80 && bptr[5] == 0 && bptr[6] == 0)
	    switch (bptr[7])
	      {
	      case 1:
		send_request = True;
		break;
	      case 2:
		send_user = True;
		break;
	      case 3:
		send_control = True;
		break;
	      case 4:
		send_log = True;
		break;
	      }
	  bptr += HWORD(bptr, 2) + 10;
	}
    }
  rb_num = HWORD(buff, 4);

  /*...to be continued  */
}


#define UNSUPP if (debug) printf("Unsupported drawing order (%02X)\n", c)


/* Process drawing orders from a graphics segment */
static void draw(seg)
     Segment *seg;
{
  register int j;
  unsigned char c, *buff;
  int len, parmlen;
  short x, y;
  unsigned long valuemask;
  XGCValues values;
  Boolean patterned;
  XImage *image;

  len = seg->len;
  buff = seg->data;
  if (!seg->append)
    {
      draw_color = seg->color;
      linet = seg->linet;
      linew = seg->linew;
      pattern = seg->pattern;
      reset();
    }

  while (len > 0)
    {
      c = *buff++;
      len--;
      if (c == GNOP1 || c == GESD) parmlen = 0;
      else if ((c >> 4) < 8 && (c & 15) >= 8) parmlen = 1;
      else
	{
	  if (!len) break;
	  parmlen = *buff++;
	  len--;
	}
      if (parmlen > len) break;

      /* At this point, "buff" points to the first character of the
	 parameters, and "parmlen" has the length.  */
      
      switch (c)
	{
	case GBAR:
	  /* Begin Area */
	  aindex = 0;
	  afigure = 0;
	  acolor = draw_color;
	  break;

	case GBIMG:
	  /* Begin Image */
	  if (parmlen < 10) break;
	  xcur = COORD(0);
	  ycur = COORD(2);
	  end_figure();
	  im_width = HWORD(buff, 6);
	  if (im_width > 0x7FF) break;
	  im_height = HWORD(buff, 8);
	  im_bpl = (im_width + 7) >> 3;
	  im_row = 0;

	  if (im_data) free(im_data);
	  if (!(im_data = (char *) malloc(im_bpl * im_height)))
	    error("Not enough memory");
	  memset(im_data, 0, im_bpl * im_height);
	  break;

	case GCBIMG:
	  /* Begin Image */
	  if (parmlen < 6) break;
	  im_width = HWORD(buff, 2);
	  if (im_width > 0x7FF) break;
	  im_height = HWORD(buff, 4);
	  im_bpl = (im_width + 7) >> 3;
	  im_row = 0;

	  if (im_data) free(im_data);
	  if (!(im_data = (char *) malloc(im_bpl * im_height)))
	    error("Not enough memory");
	  memset(im_data, 0, im_bpl * im_height);
	  break;

	case GCHST:
	  /* Character String */
	  if (parmlen < 5) break;
	  xcur = COORD(0);
	  ycur = COORD(2);
	  end_figure();
	  draw_string(&buff[4], parmlen - 4);
	  break;

	case GCHSTC:
	  /* Character String */
	  if (parmlen) draw_string(buff, parmlen);
	  break;

	case GCOMT:
	  /* Comment */
	  break;
	  
	case GEAR:
	  /* End Area */
	  if (aindex == 0) aindex = -1;
	  if (aindex < 0) break;
	  end_figure();
	  c = pattern ? pattern : def_pattern;
	  patterned = False;
	  if (c && c < 15)
	    {
	      XSetStipple(dpy, gc, bitpix[c - 1]);
	      XSetFillStyle(dpy, gc, FillOpaqueStippled);
	      patterned = True;
	    }
	  else if (c != 16)
	    {
	      /* No shading */
	      aindex = -1;
	      break;
	    }
	  if (acolor != draw_color)
	    XSetForeground(dpy, gc, colortab[acolor]);
	  XFillPolygon(dpy, gwin, gc, area, aindex, Complex, CoordModeOrigin);
	  if (acolor != draw_color)
	    XSetForeground(dpy, gc, colortab[draw_color]);
	  if (patterned) XSetFillStyle(dpy, gc, FillSolid);
	  aindex = -1;
	  break;

	case GEIMG:
	  /* End Image */
	  if (!im_data) break;
	  image = XCreateImage(dpy, DefaultVisual(dpy, scr), 1, XYBitmap,
			       0, im_data, im_width, im_height, 8, im_bpl);
	  image->byte_order = MSBFirst;
	  image->bitmap_bit_order = MSBFirst;

	  XPutImage(dpy, gwin, gc, image, 0, 0, xcur + xorig, yorig - ycur,
		    (unsigned int) im_width, (unsigned int) im_height);

	  XDestroyImage(image);
	  im_data = NULL;
	  break;

	case GFLT:
	  /* Fillet */
	  if (parmlen < 8) break;
          xcur = COORD(0);
          ycur = COORD(2);
	  end_figure();
	  UNSUPP;
	  break;

	case GCFLT:
	  /* Fillet */
	  if (parmlen < 4) break;
	  UNSUPP;
	  break;

	case GFARC:
	  /* Full Arc */
	  if (parmlen < 6) break;
          xcur = COORD(0);
          ycur = COORD(2);
	  end_figure();
	  UNSUPP;
	  break;

	case GCFARC:
	  /* Full Arc */
	  if (parmlen < 4) break;
	  UNSUPP;
	  break;

	case GIMD:
	  /* Image Data */
	  if (!im_data || im_row >= im_height) break;
	  memcpy(&im_data[im_row * im_bpl], buff,
		 parmlen > im_bpl ? im_bpl : parmlen);
	  im_row++;
	  break;

	case GLINE:
	  /* Line */
	  if (parmlen < 4) break;
	  xcur = COORD(0);
	  ycur = COORD(2);
	  end_figure();
	  for (j = 4; j + 3 < parmlen; j += 4)
	    {
	      x = COORD(j);
	      y = COORD(j + 2);
	      draw_line(x, y);
	      xcur = x;
	      ycur = y;
	    }
	  break;

	case GCLINE:
	  /* Line */
	  for (j = 0; j + 3 < parmlen; j += 4)
	    {
	      x = COORD(j);
	      y = COORD(j + 2);
	      draw_line(x, y);
	      xcur = x;
	      ycur = y;
	    }
	  break;

	case GMRK:
	  /* Marker */
	  if (parmlen < 4) break;
          xcur = COORD(0);
          ycur = COORD(2);
	  end_figure();
          draw_mark();
	  break;

	case GCMRK:
	  /* Marker */
          draw_mark();
	  break;

	case GRLINE:
	  /* Relative Line */
	  if (parmlen < 4) break;
	  xcur = COORD(0);
	  ycur = COORD(2);
	  end_figure();
	  for (j = 4; j + 1 < parmlen; j += 2)
	    {
	      x = xcur + OFFSET(j);
	      y = ycur + OFFSET(j + 1);
	      draw_line(x, y);
	      xcur = x;
	      ycur = y;
	    }
	  break;

	case GCRLINE:
	  /* Relative Line */
	  for (j = 0; j + 1 < parmlen; j += 2)
	    {
	      x = xcur + OFFSET(j);
	      y = ycur + OFFSET(j + 1);
	      draw_line(x, y);
	      xcur = x;
	      ycur = y;
	    }
	  break;

	case GSBMX:
	  /* Set Background Mix */
	  break;

	case GSCA:
	  /* Set Character Angle */
	  break;

	case GSCC:
	  /* Set Character Cell */
	  break;

	case GSCD:
	  /* Set Character Direction */
	  break;

	case GSCR:
	  /* Set Character Precision */
	  break;

	case GSCS:
	  /* Set Character Set */
	  break;

	case GSCH:
	  /* Set Character Shear */
	  break;

	case GSCOL:
	  /* Set Color */
	  if (*buff >= sizeof colortab / sizeof(Pixel)) break;
	  draw_color = (*buff == 0 ? def_color : *buff);
	  XSetForeground(dpy, gc, colortab[draw_color]);
	  break;

	case GSECOL:
	  /* Set Extended Color */
	  if (parmlen < 2 || buff[1] >= sizeof colortab / sizeof(Pixel)) break;
	  draw_color = (buff[1] == 0 ? def_color : buff[1]);
	  XSetForeground(dpy, gc, colortab[draw_color]);
	  break;

	case GSLT:
	  /* Set Line Type */
	  linet = (*buff == 0 || *buff > 8 ? def_linet : *buff);
	  if (linet == 7)	/* Solid */
	    {
	      XSetLineAttributes(dpy, gc, linew == 1 ? 0 : 2,
				 LineSolid, CapButt, JoinMiter);
	      break;
	    }
	  if (linet >= sizeof dashes / sizeof(char *)) break;
	  XSetDashes(dpy, gc, 0, dashes[linet], dash_len[linet]);
	  XSetLineAttributes(dpy, gc, linew == 1 ? 0 : 2,
			     LineOnOffDash, CapButt, JoinMiter);
	  break;

	case GSLW:
	  /* Set Line Width */
	  if (*buff == 0)
	    {
	      if (linew == def_linew) break;
	      linew = def_linew;
	    }
	  else
	    {
	      if (*buff > 1 && linew > 1 || *buff == linew) break;
	      linew = *buff;
	    }
	  valuemask = GCLineWidth;
	  values.line_width = (linew == 1 ? 0 : 2);
	  XChangeGC(dpy, gc, valuemask, &values);
	  break;

	case GSMC:
	  /* Set Marker Cell */
	  break;

	case GSMP:
	  /* Set Marker Precision */
	  UNSUPP;
	  break;

	case GSMS:
	  /* Set Marker Set */
	  UNSUPP;
	  break;

	case GSMT:
	  /* Set Marker Symbol */
	  marker = *buff;
	  break;

	case GSMX:
	  /* Set Mix */
	  break;

	case GSPS:
	  /* Set Pattern Set */
	  UNSUPP;
	  break;

	case GSPT:
	  /* Set Pattern Symbol */
	  pattern = *buff;
	  break;

	case GSAP:
	  /* Set Arc Parameters */
	  UNSUPP;
	  break;

	case GSCP:
	  /* Set Current Position */
	  if (parmlen < 4) break;
	  xcur = COORD(0);
	  ycur = COORD(2);
	  end_figure();
	  break;

	case GSGCH:
	  /* Segment Characteristics */
	  break;

	case GEPROL:
	  /* End Prologue */
	  break;

	case GESD:
	  /* End of Symbol Definition */
	  break;

	default:
	  break;
	}
      buff += parmlen;
      len -= parmlen;
    }
}


/* Draw a line starting at current drawing position.  If it's within an
   area, then accumulate in area array instead.  */

static void draw_line(x, y)
     short x, y;
{
  /* If we're not within an area, then just draw the line */
  if (aindex < 0)
    {
      if (linet == 8) return;	/* Check for invisible type lines */
      XDrawLine(dpy, gwin, gc, xcur + xorig, yorig - ycur,
		x + xorig, yorig - y);
      return;
    }

  /* If area array is full, get some more space */
  if (aindex + 1 >= area_len)	/* Allow room for two more points */
    {
      area_len += 100;
      if (!(area = (XPoint *) realloc(area, area_len * sizeof(XPoint))))
	error("Not enough memory");
    }

  /* If this is the start of a new figure, store current position */
  if (aindex == afigure)
    {
      area[aindex].x = xcur + xorig;
      area[aindex].y = yorig - ycur;
      aindex++;
    }

  /* Add new point */
  area[aindex].x = x + xorig;
  area[aindex].y = yorig - y;
  aindex++;
}


/* End the current figure */
static void end_figure()
{
  if (aindex < 1 || aindex == afigure) return;
  
  /* If area array is full, get some more space */
  if (aindex >= area_len)
    {
      area_len += 100;
      if (!(area = (XPoint *) realloc(area, area_len * sizeof(XPoint))))
	error("Not enough memory");
    }

  /* Close this figure by adding starting point to polygon path */
  area[aindex].x = area[afigure].x;
  area[aindex].y = area[afigure].y;
  aindex++;
  afigure = aindex;
}


/* Draw a string at current drawing position */
static void draw_string(buff, len)
     unsigned char *buff;
     int len;
{
  register int j;
  
  if (len > MAXSTRING) len = MAXSTRING;
  for (j = 0; j < len; j++)
    trbuff[j] = etoa[buff[j]];
  XDrawString(dpy, gwin, gc, xcur + xorig, yorig - ycur, trbuff, len);
}


/* Draw a marker symbol at current drawing position */
static void draw_mark()
{
  if (marker <= sizeof mk_bits / sizeof(char *))
    XCopyPlane(dpy, mkpix[marker ? marker - 1 : 0], gwin, gc, 0, 0,
	       MWIDTH, MHEIGHT, xcur + xorig - 3, yorig - (ycur + 3), 1);
  else
    XDrawString(dpy, gwin, gc, xcur + xorig, yorig - ycur, &etoa[marker], 1);
  /*...Should use marker set attribute to decide what type of symbol to use */
}
 
 
/* Draw the graphics cursor */
static void draw_curs()
{
  if (!gcursor) return;
  XDrawLine(dpy, gwin, cursgc, xcurs + xorig - 20, yorig - ycurs,
	    xcurs + xorig + 20, yorig - ycurs);
  XDrawLine(dpy, gwin, cursgc, xcurs + xorig, yorig - ycurs - 20,
	    xcurs + xorig, yorig - ycurs + 20);
}


/* Clear old graphics cursor */
static void clear_curs()
{
  if (!gcursor) return;
  XDrawLine(dpy, gwin, cleargc, xcurs + xorig - 20, yorig - ycurs,
	    xcurs + xorig + 20, yorig - ycurs);
  XDrawLine(dpy, gwin, cleargc, xcurs + xorig, yorig - ycurs - 20,
	    xcurs + xorig, yorig - ycurs + 20);
}
