/* $Id: tek.c,v 1.52 2006/05/24 00:58:19 airwin Exp $

	PLplot tektronix device & emulators driver.
*/
#include "plDevs.h"

#if defined(PLD_xterm) ||		/* xterm */ \
    defined(PLD_tek4010) ||		/* TEK 4010 */ \
    defined(PLD_tek4010f) ||		/* ditto, file */ \
    defined(PLD_tek4107) ||		/* TEK 4107 */ \
    defined(PLD_tek4107f) ||		/* ditto, file */ \
    defined(PLD_mskermit) ||		/* MS-kermit emulator */ \
    defined(PLD_versaterm) ||		/* Versaterm emulator */ \
    defined(PLD_vlt) ||			/* VLT emulator */ \
    defined(PLD_conex)			/* conex emulator 4010/4014/4105 */

#define NEED_PLDEBUG
#include "plplotP.h"
#include "drivers.h"
#include "plevent.h"

#include <ctype.h>

/* Device info */

char* plD_DEVICE_INFO_tek = 
#if defined(PLD_conex)
  "conex:Conex vt320/tek emulator:1:tek:24:conex\n"
#endif
#if defined(PLD_mskermit)
  "mskermit:MS-Kermit emulator:1:tek:21:mskermit\n"
#endif
#if defined(PLD_tek4107t)
  "tek4107t:Tektronix Terminal (4105/4107):1:tek:20:tek4107t\n"
#endif
#if defined(PLD_tek4107f)
  "tek4107f:Tektronix File (4105/4107):0:tek:28:tek4107f\n"
#endif
#if defined(PLD_tekt)
  "tekt:Tektronix Terminal (4010):1:tek:19:tekt\n"
#endif
#if defined(PLD_tekf)
  "tekf:Tektronix File (4010):0:tek:27:tekf\n"
#endif
#if defined(PLD_versaterm)
  "versaterm:Versaterm vt100/tek emulator:1:tek:22:versaterm\n"
#endif
#if defined(PLD_vlt)
  "vlt:VLT vt100/tek emulator:1:tek:23:vlt\n"
#endif
#if defined(PLD_xterm)
  "xterm:Xterm Window:1:tek:18:xterm"
#endif
;

/* Prototype the driver entry points that will be used to initialize the
   dispatch table entries. */

void plD_init_xterm		(PLStream *);
void plD_init_tekt		(PLStream *);
void plD_init_tekf		(PLStream *);
void plD_init_tek4107t		(PLStream *);
void plD_init_tek4107f		(PLStream *);
void plD_init_mskermit		(PLStream *);
void plD_init_versaterm		(PLStream *);
void plD_init_vlt		(PLStream *);
void plD_init_conex		(PLStream *);

/* External generic entry points */

void plD_line_tek		(PLStream *, short, short, short, short);
void plD_polyline_tek		(PLStream *, short *, short *, PLINT);
void plD_eop_tek		(PLStream *);
void plD_bop_tek		(PLStream *);
void plD_tidy_tek		(PLStream *);
void plD_state_tek		(PLStream *, PLINT);
void plD_esc_tek		(PLStream *, PLINT, void *);

/* Static function prototypes */

static void  WaitForPage	(PLStream *pls);
static void  tek_init		(PLStream *pls);
static void  tek_text		(PLStream *pls);
static void  tek_graph		(PLStream *pls);
static void  fill_polygon	(PLStream *pls);
static void  GetCursor		(PLStream *pls, PLGraphicsIn *ptr);
static void  encode_int		(char *c, int i);
static void  encode_vector	(char *c, int x, int y);
static void  decode_gin		(char *c, PLGraphicsIn *gin);
static void  tek_vector		(PLStream *pls, int x, int y);
static void  scolor		(PLStream *pls, int icol, int r, int g, int b);
static void  setcmap		(PLStream *pls);

static void  LookupEvent	(PLStream *pls);
static void  InputEH		(PLStream *pls);
static void  LocateEH		(PLStream *pls);

/* Stuff for handling tty cbreak mode */

#ifdef HAVE_TERMIOS_H
#include <termios.h>
#include <unistd.h>
static struct termios	termios_cbreak, termios_reset;
static enum { RESET, CBREAK } ttystate = RESET;
static void tty_setup	(void);
static int  tty_cbreak	(void);
static int  tty_reset	(void);
static void tty_atexit	(void);
#else
static void tty_setup	(void) {}
static int  tty_cbreak	(void) {return 0;}
static int  tty_reset	(void) {return 0;}
static void tty_atexit	(void) {}
#endif

/* Pixel settings */

#define TEKX   1023
#define TEKY    779

/* Graphics control characters. */

#define RING_BELL	"\007"		/* ^G = 7 */
#define CLEAR_VIEW	"\033\f"	/* clear the view = ESC FF */

#define ALPHA_MODE	"\037"		/* Enter Alpha  mode:  US */
#define VECTOR_MODE	"\035"		/* Enter Vector mode:  GS */
#define GIN_MODE	"\033\032"	/* Enter GIN    mode:  ESC SUB */
#define BYPASS_MODE	"\033\030"	/* Enter Bypass mode:  ESC CAN */
#define XTERM_VTMODE	"\033\003"	/* End xterm-Tek mode: ESC ETX */
#define CANCEL		"\033KC"	/* Cancel */	

/* Static vars */

enum {tek4010, tek4105, tek4107, xterm, mskermit, vlt, versaterm};

/* One of these holds the tek driver state information */

typedef struct {
    PLINT	xold, yold;	/* Coordinates of last point plotted */
    int		exit_eventloop;	/* Break out of event loop */
    int		locate_mode;	/* Set while in locate (pick) mode */
    int		curcolor;	/* Current color index */
    PLGraphicsIn gin;		/* Graphics input structure */
} TekDev;

/* color for MS-DOS Kermit v2.31 (and up) tektronix emulator 
 *	0 = normal, 1 = bright 
 *	foreground color (30-37) = 30 + colors
 *		where colors are   1=red, 2=green, 4=blue 
 */
#ifdef PLD_mskermit
static char *kermit_color[15]= {
   "0;30","0;37",
   "0;32","0;36","0;31","0;35",
   "1;34","1;33","1;31","1;37",
   "1;35","1;32","1;36","0;34",
   "0;33"};
#endif

static void tek_dispatch_init_helper( PLDispatchTable *pdt,
                                      char *menustr, char *devnam,
                                      int type, int seq, plD_init_fp init )
{
#ifndef ENABLE_DYNDRIVERS
    pdt->pl_MenuStr = menustr;
    pdt->pl_DevName = devnam;
#endif
    pdt->pl_type = type;
    pdt->pl_seq = seq;
    pdt->pl_init     = init;
    pdt->pl_line     = (plD_line_fp)     plD_line_tek;
    pdt->pl_polyline = (plD_polyline_fp) plD_polyline_tek;
    pdt->pl_eop      = (plD_eop_fp)      plD_eop_tek;
    pdt->pl_bop      = (plD_bop_fp)      plD_bop_tek;
    pdt->pl_tidy     = (plD_tidy_fp)     plD_tidy_tek;
    pdt->pl_state    = (plD_state_fp)    plD_state_tek;
    pdt->pl_esc      = (plD_esc_fp)      plD_esc_tek;
}

void plD_dispatch_init_xterm	( PLDispatchTable *pdt )
{
    tek_dispatch_init_helper( pdt,
                              "Xterm Window", "xterm",
                              plDevType_Interactive, 18,
                              (plD_init_fp) plD_init_xterm );
}

void plD_dispatch_init_tekt	( PLDispatchTable *pdt )
{
    tek_dispatch_init_helper( pdt,
                              "Tektronix Terminal (4010)", "tekt",
                              plDevType_Interactive, 19,
                              (plD_init_fp) plD_init_tekt );
}

void plD_dispatch_init_tek4107t	( PLDispatchTable *pdt )
{
    tek_dispatch_init_helper( pdt,
                              "Tektronix Terminal (4105/4107)", "tek4107t",
                              plDevType_Interactive, 20,
                              (plD_init_fp) plD_init_tek4107t );
}

void plD_dispatch_init_mskermit	( PLDispatchTable *pdt )
{
    tek_dispatch_init_helper( pdt,
                              "MS-Kermit emulator", "mskermit",
                              plDevType_Interactive, 21,
                              (plD_init_fp) plD_init_mskermit );
}

void plD_dispatch_init_versaterm( PLDispatchTable *pdt )
{
    tek_dispatch_init_helper( pdt,
                              "Versaterm vt100/tek emulator", "versaterm",
                              plDevType_Interactive, 22,
                              (plD_init_fp) plD_init_versaterm );
}

void plD_dispatch_init_vlt	( PLDispatchTable *pdt )
{
    tek_dispatch_init_helper( pdt,
                              "VLT vt100/tek emulator", "vlt",
                              plDevType_Interactive, 23,
                              (plD_init_fp) plD_init_vlt );
}

void plD_dispatch_init_conex	( PLDispatchTable *pdt )
{
    tek_dispatch_init_helper( pdt,
                              "Conex vt320/tek emulator", "conex",
                              plDevType_Interactive, 24,
                              (plD_init_fp) plD_init_conex );
}

void plD_dispatch_init_tekf	( PLDispatchTable *pdt )
{
    tek_dispatch_init_helper( pdt,
                              "Tektronix File (4010)", "tekf",
                              plDevType_FileOriented, 27,
                              (plD_init_fp) plD_init_tekf );
}

void plD_dispatch_init_tek4107f	( PLDispatchTable *pdt )
{
    tek_dispatch_init_helper( pdt,
                              "Tektronix File (4105/4107)", "tek4107f",
                              plDevType_FileOriented, 28,
                              (plD_init_fp) plD_init_tek4107f );
}

/*--------------------------------------------------------------------------*\
 * plD_init_xterm()	xterm 
 * plD_init_tekt()	Tek 4010 terminal
 * plD_init_tekf()	Tek 4010 file
 * plD_init_tek4107t()	Tek 4105/4107 terminal
 * plD_init_tek4107f()	Tek 4105/4107 file
 * plD_init_mskermit()	MS-Kermit emulator (DOS)
 * plD_init_vlt()	VLT emulator (Amiga)
 * plD_init_versaterm()	VersaTerm emulator (Mac)
 * plD_init_conex()	Conex vt320/Tek 4105 emulator (DOS)
 *
 * These just set attributes for the particular tektronix device, then call
 * tek_init().  The following attributes can be set:
 *
 * pls->termin		if a terminal device
 * pls->color		if color (1), if only fixed colors (2)
 * pls->dev_fill0	if can handle solid area fill
 * pls->dev_fill1	if can handle pattern area fill
\*--------------------------------------------------------------------------*/

void 
plD_init_xterm(PLStream *pls)
{
    pls->dev_minor = xterm;
    pls->termin = 1;
    tek_init(pls);
}

void
plD_init_tekt(PLStream *pls)
{
    pls->termin = 1;
    plD_init_tekf(pls);
}

void
plD_init_tekf(PLStream *pls)
{
    pls->dev_minor = tek4010;
    tek_init(pls);
}

void
plD_init_tek4107t(PLStream *pls)
{
    pls->termin = 1;
    plD_init_tek4107f(pls);
}

void
plD_init_tek4107f(PLStream *pls)
{
    pls->dev_minor = tek4107;
    pls->color = 1;
    pls->dev_fill0 = 1;
    tek_init(pls);
}

void
plD_init_mskermit(PLStream *pls)
{
    pls->dev_minor = mskermit;
    pls->termin = 1;
    pls->color = 1;
    pls->dev_fill0 = 1;
    tek_init(pls);
}

void
plD_init_vlt(PLStream *pls)
{
    pls->dev_minor = vlt;
    pls->termin = 1;
    pls->color = 1;
    pls->dev_fill0 = 1;
    tek_init(pls);
}

void
plD_init_versaterm(PLStream *pls)
{
    pls->dev_minor = versaterm;
    pls->termin = 1;
    pls->color = 1;
    pls->dev_fill0 = 1;
    tek_init(pls);
}

void
plD_init_conex(PLStream *pls)
{
    pls->dev_minor = xterm;		/* responds to xterm escape codes */
    pls->termin = 1;
    pls->color = 2;			/* only fixed colours */
    tek_init(pls);
}

/*--------------------------------------------------------------------------*\
 * tek_init()
 *
 * Generic tektronix device initialization.
\*--------------------------------------------------------------------------*/

static void
tek_init(PLStream *pls)
{
    TekDev *dev;
    int xmin = 0;
    int xmax = TEKX;
    int ymin = 0;
    int ymax = TEKY;

    PLFLT pxlx = 4.771;
    PLFLT pxly = 4.653;

    pls->graphx = TEXT_MODE;

/* Allocate and initialize device-specific data */

    pls->dev = calloc(1, (size_t) sizeof(TekDev));
    if (pls->dev == NULL)
	plexit("tek_init: Out of memory.");

    dev = (TekDev *) pls->dev;

    dev->curcolor = 1;
    dev->xold = PL_UNDEFINED;
    dev->yold = PL_UNDEFINED;

    plP_setpxl(pxlx, pxly);
    plP_setphy(xmin, xmax, ymin, ymax);

/* Terminal/file initialization */

    if (pls->termin) {
	pls->OutFile = stdout;
	tty_setup();
    }
    else {
	plFamInit(pls);
	plOpenFile(pls);
    }

    switch (pls->dev_minor) {
#ifdef PLD_tek4107
    case tek4107:
	pls->graphx = GRAPHICS_MODE;
	fprintf(pls->OutFile, "\033%%!0");	/* set tek mode */
	fprintf(pls->OutFile, "\033KN1");	/* clear the view */
	fprintf(pls->OutFile, "\033LZ");	/* clear dialog buffer */
	fprintf(pls->OutFile, "\033ML1");	/* set default color */
	break;
#endif	/* PLD_tek4107 */

/* A sneaky hack: VLT sometimes has leftover panel information, causing 
 * garbage at the beginning of a sequence of color fills.  Since 
 * there is no clear panel command, instead I set the fill color to the
 * same as background and issue an end panel command.
 */
#ifdef PLD_vlt
    case vlt:{
	char fillcol[4];
	tek_graph(pls);
	encode_int(fillcol, 0);
	fprintf(pls->OutFile, "\033MP%s\033LE", fillcol);
	break;
    }
#endif	/* PLD_vlt */

    default:
	tek_graph(pls);
    }

/* Initialize palette */

   if ( pls->color & 0x01 ) {
       printf("\033TM111");	/* Switch to RGB colors */
       setcmap(pls);
   }

/* Finish initialization */

    fprintf(pls->OutFile, VECTOR_MODE);	/* Enter vector mode */
    if (pls->termin)
	fprintf(pls->OutFile, CLEAR_VIEW);/* erase and home */

    fflush(pls->OutFile);
}

/*--------------------------------------------------------------------------*\
 * plD_line_tek()
 *
 * Draw a line from (x1,y1) to (x2,y2).
\*--------------------------------------------------------------------------*/

void
plD_line_tek(PLStream *pls, short x1, short y1, short x2, short y2)
{
    TekDev *dev = (TekDev *) pls->dev;

    tek_graph(pls);

/* If not continuation of previous line, begin a new one */

    if (x1 != dev->xold || y1 != dev->yold) {
	pls->bytecnt += fprintf(pls->OutFile, VECTOR_MODE);
	tek_vector(pls, x1, y1);
    }

/* Now send following point to complete line draw */

    tek_vector(pls, x2, y2);

    dev->xold = x2;
    dev->yold = y2;
}

/*--------------------------------------------------------------------------*\
 * plD_polyline_tek()
 *
 * Draw a polyline in the current color.
\*--------------------------------------------------------------------------*/

void
plD_polyline_tek(PLStream *pls, short *xa, short *ya, PLINT npts)
{
    PLINT i;
    TekDev *dev = (TekDev *) pls->dev;
    short x = xa[0], y = ya[0];

    tek_graph(pls);

/* If not continuation of previous line, begin a new one */

    if ( x != dev->xold || y != dev->yold ) {
	pls->bytecnt += fprintf(pls->OutFile, VECTOR_MODE);
	tek_vector(pls, x, y);
    }

/* Now send following points to complete polyline draw */

    for (i = 1; i < npts; i++) 
	tek_vector(pls, xa[i], ya[i]);

    dev->xold = xa[npts-1];
    dev->yold = ya[npts-1];
}

/*--------------------------------------------------------------------------*\
 * plD_eop_tek()
 *
 * End of page.  User must hit a <CR> to continue (terminal output).
\*--------------------------------------------------------------------------*/

void
plD_eop_tek(PLStream *pls)
{
    tek_graph(pls);

    if (pls->termin) {
	if ( ! pls->nopause) 
	    WaitForPage(pls);
    }
    fprintf(pls->OutFile, CLEAR_VIEW);		/* erase and home */
}

/*--------------------------------------------------------------------------*\
 * plD_bop_tek()
 *
 * Set up for the next page.  Advance to next family file if necessary
 * (file output).  Devices that share graphics/alpha screens need a page
 * clear.
\*--------------------------------------------------------------------------*/

void
plD_bop_tek(PLStream *pls)
{
   TekDev *dev = (TekDev *) pls->dev;

   dev->xold = PL_UNDEFINED;
   dev->yold = PL_UNDEFINED;

   if (pls->termin) {
       switch (pls->dev_minor) {
       case mskermit:
	   fprintf(pls->OutFile, CLEAR_VIEW);	/* erase and home */
	   break;
       }
   } else {
       plGetFam(pls);
   }
   pls->page++;

/* Initialize palette */

   if ( pls->color & 0x01 )
       setcmap(pls);
}

/*--------------------------------------------------------------------------*\
 * plD_tidy_tek()
 *
 * Close graphics file or otherwise clean up.
\*--------------------------------------------------------------------------*/

void
plD_tidy_tek(PLStream *pls)
{
    if ( ! pls->termin) {
	fclose(pls->OutFile);
    } else {
	tek_text(pls);
	fflush(pls->OutFile);
    }
}

/*--------------------------------------------------------------------------*\
 * tek_color()
 *
 * Change to specified color index.
\*--------------------------------------------------------------------------*/

static void 
tek_color(PLStream *pls, int icol)
{
    switch (pls->dev_minor) {
#ifdef PLD_mskermit			/* Is this really necessary? */
    case mskermit:
	printf("\033[%sm", kermit_color[icol % 14] );
	break;
#endif
    default:
	pls->bytecnt += fprintf(pls->OutFile, "\033ML%c", icol + '0');
    }
}

/*--------------------------------------------------------------------------*\
 * plD_state_tek()
 *
 * Handle change in PLStream state (color, pen width, fill attribute,
 * etc).
\*--------------------------------------------------------------------------*/

void 
plD_state_tek(PLStream *pls, PLINT op)
{
    TekDev *dev = (TekDev *) pls->dev;

    switch (op) {

    case PLSTATE_WIDTH:
	break;

    case PLSTATE_COLOR0:
	if (pls->color) {
	    int icol0 = pls->icol0;
	    tek_graph(pls);
	    if (icol0 != PL_RGB_COLOR) {
		dev->curcolor = icol0;
		tek_color(pls, icol0);
	    }
	}
	break;

    case PLSTATE_COLOR1:
	if (pls->color) {
	    int icol1, ncol1;
	    tek_graph(pls);
	    if ((ncol1 = MIN(16 - pls->ncol0, pls->ncol1)) < 1)
		break;

	    icol1 = pls->ncol0 + (pls->icol1 * (ncol1-1)) / (pls->ncol1-1);
	    dev->curcolor = icol1;
	    tek_color(pls, icol1);
	}
	break;

    case PLSTATE_CMAP0:
    case PLSTATE_CMAP1:
	if ( pls->color & 0x01 )
	    setcmap(pls);    

	break;
    }
}

/*--------------------------------------------------------------------------*\
 * plD_esc_tek()
 *
 * Escape function.
\*--------------------------------------------------------------------------*/

void 
plD_esc_tek(PLStream *pls, PLINT op, void *ptr)
{
    switch (op) {

    case PLESC_TEXT:
	tek_text(pls);
	break;

    case PLESC_GRAPH:
	tek_graph(pls);
	break;

    case PLESC_FILL:
	fill_polygon(pls);
	break;

    case PLESC_GETC:
	GetCursor(pls, (PLGraphicsIn *) ptr);
	break;
    }
}

/*--------------------------------------------------------------------------*\
 * GetCursor()
 *
 * Waits for a left button mouse event and returns coordinates.
 * xterm doesn't handle GIN. I think all the rest do.
\*--------------------------------------------------------------------------*/

static void
GetCursor(PLStream *pls, PLGraphicsIn *ptr)
{
#define MAX_GIN 10
    char input_string[MAX_GIN];
    int i = 0;

    plGinInit(ptr);

    if (pls->termin && pls->dev_minor != xterm) {
	tek_graph(pls);

    /* Enter GIN mode */

	printf(GIN_MODE);
	fflush(stdout);

    /* Read & decode report */

	while (++i < MAX_GIN && (input_string[i-1] = getchar()) != '\n')
	    ;

	input_string[i-1] = '\0';
	ptr->keysym = input_string[0];
	decode_gin(&input_string[1], ptr);

    /* Switch out of GIN mode */

	printf(VECTOR_MODE);
    }
}

/*--------------------------------------------------------------------------*\
 * fill_polygon()
 *
 * Fill polygon described in points pls->dev_x[] and pls->dev_y[].
\*--------------------------------------------------------------------------*/

static void
fill_polygon(PLStream *pls)
{
    TekDev *dev = (TekDev *) pls->dev;
    int i;
    char fillcol[4], firstpoint[5];

    if (pls->dev_npts < 1)
	return;

    tek_graph(pls);

    encode_int(fillcol, -dev->curcolor);
    encode_vector(firstpoint, pls->dev_x[0], pls->dev_y[0]);

/* Select the fill pattern */

    pls->bytecnt += fprintf(pls->OutFile, "\033MP%s", fillcol);

/* Begin panel boundary */
/* Set pls->debug to see the boundary of each fill box -- cool! */

    if (pls->debug)
	pls->bytecnt += fprintf(pls->OutFile, "\033LP%s1", firstpoint);
    else
	pls->bytecnt += fprintf(pls->OutFile, "\033LP%s0", firstpoint);

/* Specify boundary (in vector mode) */

    pls->bytecnt += fprintf(pls->OutFile, VECTOR_MODE);
    for (i = 1; i < pls->dev_npts; i++) 
	tek_vector(pls, pls->dev_x[i], pls->dev_y[i]);

/* End panel */

    pls->bytecnt += fprintf(pls->OutFile, "\033LE");
}

/*--------------------------------------------------------------------------*\
 * tek_text()
 *
 * Switch to text screen (or alpha mode, for vanilla tek's).  Restore
 * terminal to its original state, to better handle user input if
 * necessary.
 *
 * Note: xterm behaves strangely in the following circumstance: switch to
 * the text screen, print a string, and switch to the graphics screen, all
 * done in quick succession.  The first character of the printed string
 * usually comes out blank -- but only apparently so, because if you force
 * a refresh of the screen in this area it will reappear.  This is a
 * reproducible bug on the HP 720 under X11R5.  If you insert a sleep(1)
 * after the switch to text screen or before the switch to graphics
 * screen, the string is printed correctly.  I've been unable to find a
 * workaround for this problem (and I've tried, you can believe eet man).
\*--------------------------------------------------------------------------*/

static void 
tek_text(PLStream *pls)
{
    if (pls->termin && (pls->graphx == GRAPHICS_MODE)) {
	tty_reset();
	pls->graphx = TEXT_MODE;
	switch (pls->dev_minor) {
	case xterm:
	    printf("\033\003");		/* vt100 mode (xterm) = ESC ETX */
	    break;

	case mskermit:
	case vlt:
	    printf("\033[?38l");	/* vt100 screen */
	    break;

	case versaterm:
	    printf("\033%%!2");		/* vt100 screen */
	    break;

	case tek4107:
	    printf("\033LV1");		/* set dialog visible */
	    printf("\033%%!1");		/* set ansi mode */
	    break;

	default:
	    printf(ALPHA_MODE);		/* enter alpha mode */
	}
	fflush(stdout);
    }
}

/*--------------------------------------------------------------------------*\
 * tek_graph()
 *
 * Switch to graphics screen.  Also switch terminal to cbreak mode, to allow
 * single keystrokes to govern actions at end of page.
\*--------------------------------------------------------------------------*/

static void 
tek_graph(PLStream *pls)
{
    if (pls->termin && (pls->graphx == TEXT_MODE)) {
	tty_cbreak();
	pls->graphx = GRAPHICS_MODE;
	switch (pls->dev_minor) {
	case xterm:
	case mskermit:
	case vlt:
	    printf("\033[?38h");	/* switch to tek screen */
	    break;

	case versaterm:
	    printf("\033%%!0");		/* switch to tek4107 screen */
	    break;

	case tek4107:
	    printf("\033%%!0");		/* set tek mode */
   	    printf(CLEAR_VIEW);		/* clear screen */
	    printf("\033LV0");		/* set dialog invisible */
	    break;
	}
    }
}

/*--------------------------------------------------------------------------*\
 * encode_int()
 *
 * Encodes a single int into standard tek integer format, storing into a
 * NULL-terminated character string (must be length 4 or greater).  This
 * scheme does not work for negative integers less than 15.
\*--------------------------------------------------------------------------*/

static void
encode_int(char *c, int i)
{
    int negative = 0;

    if (i > 0) {
	if (i & 0x7C00)				/* are any of bits 10-14 set?*/
	    *c++ = ((i >> 10) & 0x1F) | 0x40;
	if (i & 0x03F0)				/* are any of bits 4-9 set? */
	    *c++ = ((i >> 4) & 0x3F) | 0x40;
    } else {
	i = -i;
	negative = 1;
    }

    if (i & 0x000F)				/* are any of bits 0-3 set? */
	*c = (i & 0x0F) | 0x20;
    else					/* if not, put in a space */
	*c = 0x20;
	
    if ( ! negative)				/* if positive, set sign bit */
	*c |= 0x10;

    c++; *c = '\0';				/* NULL-terminate */
    return;
}

/*--------------------------------------------------------------------------*\
 * decode_gin()
 *
 * Decodes a GIN tek vector string into an xy pair of relative device
 * coordinates.  It's best to not use absolute device coordinates since the
 * coordinate bounds are different depending on the report encoding used.
 *
 * Standard:	<HiX><LoX><HiY><LoY>
 * Extended:	<HiY><Extra><LoY><HiX><LoX>
 * 
 * where <Extra> holds the two low order bits for each coordinate.
\*--------------------------------------------------------------------------*/

static void
decode_gin(char *c, PLGraphicsIn *gin)
{
    int x, y, lc = strlen(c);

    if (lc == 4) {
	x = ((c[0] & 0x1f) << 5) +
	    ((c[1] & 0x1f)     );

	y = ((c[2] & 0x1f) << 5) +
	    ((c[3] & 0x1f)     );

	gin->pX = x;
	gin->pY = y;
	gin->dX = x / (double) TEKX;
	gin->dY = y / (double) TEKY;
    }
    else if (lc == 5) {
	y = ((c[0] & 0x1f) << 7) +
	    ((c[2] & 0x1f) << 2) +
	    ((c[1] & 0x06) >> 2);

	x = ((c[3] & 0x1f) << 7) +
	    ((c[4] & 0x1f) << 2) +
	    ((c[1] & 0x03)     );

	gin->pX = x;
	gin->pY = y;
	gin->dX = x / (double) (TEKX << 2);
	gin->dY = y / (double) (TEKY << 2);
    }
    else {			/* Illegal encoding */
	gin->pX = 0;
	gin->pY = 0;
	gin->dY = 0;
	gin->dX = 0;
    }
}

/*--------------------------------------------------------------------------*\
 * encode_vector()
 *
 * Encodes an xy vector (2 ints) into standard tek vector format, storing
 * into a NULL-terminated character string of length 5.  Note that the y
 * coordinate always comes first.
\*--------------------------------------------------------------------------*/

static void
encode_vector(char *c, int x, int y)
{
    c[0] = (y >> 5)   + 0x20;		/* hy */
    c[1] = (y & 0x1f) + 0x60;		/* ly */
    c[2] = (x >> 5)   + 0x20;		/* hx */
    c[3] = (x & 0x1f) + 0x40;		/* lx */
    c[4] = '\0';			/* NULL */
}

/*--------------------------------------------------------------------------*\
 * tek_vector()
 *
 * Issues a vector draw command, assuming we are in vector plot mode.  XY
 * coordinates are encoded according to the standard xy encoding scheme.
\*--------------------------------------------------------------------------*/

static void
tek_vector(PLStream *pls, int x, int y)
{
    char c[5];

    encode_vector(c, x, y);
    pls->bytecnt += fprintf( pls->OutFile, "%s", c );
}

/*--------------------------------------------------------------------------*\
 * scolor()
 *
 * Sets a color by tek-encoded RGB values.  Need to convert PLplot RGB
 * color range (0 to 255) to Tek RGB color range (0 to 100).
\*--------------------------------------------------------------------------*/

static void
scolor(PLStream *pls, int icol, int r, int g, int b)
{
    char tek_col[4], tek_r[4], tek_g[4], tek_b[4];

    encode_int(tek_col, icol);
    encode_int(tek_r, (100*r)/255);
    encode_int(tek_g, (100*g)/255);
    encode_int(tek_b, (100*b)/255);

    pls->bytecnt += fprintf(pls->OutFile, "\033TG14%s%s%s%s",
			    tek_col, tek_r, tek_g, tek_b);
}

/*--------------------------------------------------------------------------*\
 * setcmap()
 *
 * Sets up color palette.
\*--------------------------------------------------------------------------*/

static void
setcmap(PLStream *pls)
{
    int i, ncol1 = MIN(16 - pls->ncol0, pls->ncol1);
    PLColor cmap1col;

    tek_graph(pls);

/* Initialize cmap 0 colors */

    for (i = 0; i < pls->ncol0; i++) 
	scolor(pls, i, pls->cmap0[i].r, pls->cmap0[i].g, pls->cmap0[i].b);

/* Initialize any remaining slots for cmap1 */

    for (i = 0; i < ncol1; i++) {
	plcol_interp(pls, &cmap1col, i, ncol1);
	scolor(pls, i + pls->ncol0, cmap1col.r, cmap1col.g, cmap1col.b);
    }
}

/*--------------------------------------------------------------------------*\
 * WaitForPage()
 *
 * This routine waits for the user to advance the plot, while handling
 * all other events.
\*--------------------------------------------------------------------------*/

static void
WaitForPage(PLStream *pls)
{
    TekDev *dev = (TekDev *) pls->dev;

    printf(ALPHA_MODE);		/* Switch to alpha mode (necessary) */
    printf(RING_BELL);		/* and ring bell */
    printf(VECTOR_MODE);	/* Switch out of alpha mode */
    fflush(stdout);

    while ( ! dev->exit_eventloop) {
	LookupEvent(pls);
	if (dev->locate_mode)
	    LocateEH(pls);
	else
	    InputEH(pls);
    }
    dev->exit_eventloop = FALSE;
}

/*--------------------------------------------------------------------------*\
 * LookupEvent()
 *
 * Fills in the PLGraphicsIn from an input event.  
\*--------------------------------------------------------------------------*/

static void
LookupEvent(PLStream *pls)
{
    TekDev *dev = (TekDev *) pls->dev;
    PLGraphicsIn *gin = &(dev->gin);

    if (dev->locate_mode) {
	GetCursor(pls, gin);
    }
    else {
	plGinInit(gin);
	gin->keysym = getchar();
    }

    if (isprint(gin->keysym)) {
	gin->string[0] = gin->keysym;
	gin->string[1] = '\0';
    }
    else {
	gin->string[0] = '\0';
    }

    pldebug("LookupEvent",
	    "Keycode %x, string: %s\n", gin->keysym, gin->string);
}

/*--------------------------------------------------------------------------*\
 * LocateEH()
 *
 * Handles locate mode events.
 *
 * In locate mode: move cursor to desired location and select by pressing a
 * key or by clicking on the mouse (if available).  Typically the world
 * coordinates of the selected point are reported.
 *
 * There are two ways to enter Locate mode -- via the API, or via a driver
 * command.  The API entry point is the call plGetCursor(), which initiates
 * locate mode and does not return until input has been obtained.  The
 * driver entry point is by entering a 'L' while the driver is waiting for
 * events.  
 *
 * Locate mode input is reported in one of three ways:
 * 1. Through a returned PLGraphicsIn structure, when user has specified a
 *    locate handler via (*pls->LocateEH).
 * 2. Through a returned PLGraphicsIn structure, when locate mode is invoked
 *    by a plGetCursor() call.
 * 3. Through writes to stdout, when locate mode is invoked by a driver
 *    command and the user has not supplied a locate handler.
 *
 * Hitting <Escape> will at all times end locate mode.  Other keys will
 * typically be interpreted as locator input.  Selecting a point out of
 * bounds will end locate mode unless the user overrides with a supplied
 * Locate handler.
\*--------------------------------------------------------------------------*/

static void
LocateEH(PLStream *pls)
{
    TekDev *dev = (TekDev *) pls->dev;
    PLGraphicsIn *gin = &(dev->gin);

/* End locate mode on <Escape> */

    if (gin->keysym == PLK_Escape) {
	dev->locate_mode = 0;
	return;
    }

/* Call user locate mode handler if provided */

    if (pls->LocateEH != NULL)
	(*pls->LocateEH) (gin, pls->LocateEH_data, &dev->locate_mode);

/* Use default procedure */

    else {

    /* Try to locate cursor */

	if (plTranslateCursor(gin)) {

	/* Successful, so send report to stdout */

	    pltext();
	    if (isprint(gin->keysym)) 
		printf("%f %f %c\n", gin->wX, gin->wY, gin->keysym);
	    else
		printf("%f %f\n", gin->wX, gin->wY);

	    plgra();
	}
	else {

	/* Selected point is out of bounds, so end locate mode */

	    dev->locate_mode = 0;
	}
    }
}

/*--------------------------------------------------------------------------*\
 * InputEH()
 *
 * Event handler routine for xterm.  Just reacts to keyboard input.
 *
 * In locate mode: move cursor to desired location and select by pressing a
 * key or by clicking on the mouse (if available).  The world coordinates of
 * the selected point are output on the text screen.  Terminate by picking a
 * point out of bounds, hitting page advance, or the escape key.  If you
 * want to customize this, write an event handler to do it.
\*--------------------------------------------------------------------------*/

static void
InputEH(PLStream *pls)
{
    TekDev *dev = (TekDev *) pls->dev;
    PLGraphicsIn *gin = &(dev->gin);

/* Call user event handler.
 * Since this is called first, the user can disable all PLplot internal
 * event handling by setting gin->keysym to 0 and gin->string to '\0'.
*/
    if (pls->KeyEH != NULL)
	(*pls->KeyEH) (gin, pls->KeyEH_data, &dev->exit_eventloop);

/* Remaining internal event handling */

    switch (gin->keysym) {

    case PLK_Linefeed:
    /* Advance to next page (i.e. terminate event loop) on a <eol> */
	dev->exit_eventloop = TRUE;
	break;

    case 'Q':
    /* Terminate on a 'Q' (not 'q', since it's too easy to hit by mistake) */
	pls->nopause = TRUE;
	plexit("");
	break;

    case 'L':
    /* Begin locate mode */
	dev->locate_mode = 1;
	break;
    }
}

/*--------------------------------------------------------------------------*\
 * tty cbreak-mode handlers
 *
 * Taken from "Advanced Programming in the UNIX(R) Environment", 
 * by W. Richard Stevens.
\*--------------------------------------------------------------------------*/

#ifdef HAVE_TERMIOS_H

static void
tty_setup(void)				/* setup for terminal operations */
{
    if (tcgetattr(STDIN_FILENO, &termios_reset) < 0) {
	fprintf(stderr, "Unable to set up cbreak mode.\n");
	return;
    }

    termios_cbreak = termios_reset;		/* structure copy */

    termios_cbreak.c_lflag &= ~(ICANON);	/* canonical mode off */
    termios_cbreak.c_cc[VMIN] = 1;		/* 1 byte at a time */
    termios_cbreak.c_cc[VTIME] = 0;		/* no timer */

#ifdef STDC_HEADERS
    if (atexit(tty_atexit))
	fprintf(stderr, "Unable to set up atexit handler.\n");
#endif
    return;
}

static int
tty_cbreak(void)			/* put terminal into a cbreak mode */
{
    if (ttystate != CBREAK) {
	if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios_cbreak) < 0)
	    return -1;

	ttystate = CBREAK;
    }
    return 0;
}

static int
tty_reset(void)				/* restore terminal's mode */
{
    if (ttystate != RESET) {
	if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios_reset) < 0)
	    return -1;

	ttystate = RESET;
    }
    return 0;
}

static void
tty_atexit(void)			/* exit handler */
{
    tty_reset();
}

#endif			/* HAVE_TERMIOS_H */

#else
int pldummy_tek() {return 0;}

#endif	/*  defined(PLD_xterm) || ... */


syntax highlighted by Code2HTML, v. 0.9.1