static char rcsid[] = "$Header: vt100.c,v 800.0 85/08/06 14:18:43 root Exp $";
static char sccsid[] = "%W% %Y% %Q% %G%";

/************************************************************************
 *									*
 *	vt100.c -- procedures to emulate a DEC VT100.			*
 *									*
 *	(See main.c for general remarks and documentation)		*
 *									*
 *	Justin Douglas Tygar		September 3, 1982		*
 *	Continuation by Justin Douglas Tygar and Steven Sargent		*
 *	Window package by J. D. Tygar	January 31, 1984		*
 *	Continuation by John Oswalt					*
 *									*
 ************************************************************************/

/************************************************************************
 *                                                                      *
 *			Copyright 1982, 1983, 1984			*
 *		    VALID LOGIC SYSTEMS INCORPORATED			*
 *									*
 * 	This listing contains confidential proprietary information	*
 *	which is not to be disclosed to unauthorized persons without	*
 *	the written consent of an officer of Valid Logic Systems	*
 *	Incorporated.							*
 *									*
 *	The copyright notice appearing above is included to provide	*
 *	statutory protection in the event of unauthorized or            *
 *      unintentional public disclosure.				*
 *									*
 ************************************************************************/

/*
 * Added to WHITE kernel 841220 by Preston Gardner
 */

#ifdef KERNEL
#include "../h/param.h"
#endif KERNEL

#include "../white/dispmgr.h"		/*  General Definitions  */
#include "../white/vt100cook.h"
#include "../white/vt100pager.h"
#include "../white/ds.h"

extern short vt100debug;
extern struct dswnd *dswnd;




#ifdef WHITE
wvt100init(d,w,lines,cols,xpix,ypix)
pdisp d;
windowp w;
int lines,cols,xpix,ypix;		/* these numbers one less than total */
{
	d->puckdrawn = FALSE;
	d->curwindow = d->select = w;

	w->charatts = NORMAL;
	w->oldcharatts = NORMAL;

	w->ansi = TRUE;		/*  ANSI or VT52 flag  */
	w->curdrwn = FALSE;	/*  Is the cursor drawn?  */
	w->om = FALSE;		/*  origin mode -- if TRUE, count cursor
				    addressing from top of scrolling region
				    rather than top of screen */
	w->puck = FALSE;	/*  Should puck sequences be sent? */
	w->wrap = TRUE;		/*  Wraparound flag  */
	w->wrapln = FALSE;	/*  Is the current line about to be wraped? */

	w->col = cols; w->line = lines; /*  The width and length of the display  */
	w->curcol = 0; w->curline = 0;/*  The line & col of the cursor (VT100) */
	w->oldcol = 0; w->oldline = 0;/*  Saved cursor positions  */
	w->passthrough = UNIXPASS;	/*  Type of pass through */
	w->startcol = 0;	/*  The "true" (e.g. character) margins of */
	w->startline = 0;	/*     the window we are manipulating  */
	w->topsc = 0; w->botsc = lines;	/*  Top and bottom of scrolling region  */
	w->wxl = 0; w->wxr = xpix;	/*  Margins for the windows  */
	w->wyb = 0; w->wyt = ypix;

	w->seq[0] = 0;	/*  Escape sequence  */
}
#endif WHITE



/*
 *  screen_vt100() put a character on the screen
 */

PAGESUBR(screen_vt100)
screen_vt100(d, w, c, drawcursor)
register pdisp d;
register windowp w;
uchar c;			/*  the input character  */
short drawcursor;		/*  draw cursor after op  */
{

	/*  Notes on ll and cc:
	 *
 	 *  These variables are needed because of the weird way the vt100
	 *  moves its cursors.  If the user is typing on the terminal and
	 *  reaches the end of a line, then the cursor remains at that position
	 *  until the next character is typed.  Note that this is contrary
	 *  to its standard procedure of keeping the cursor one character ahead
	 *  of the last character typed.  After another character is typed, the
	 *  cursor leaps to the second column of the next line.  Of course if
	 *  the cursor is moved during this time, the whole deal is off, when
	 *  the cursor is moved back to its end of line position and a character
	 *  is typed, the cursor will overwrite the last character at the end
	 *  of the line. (By the way, this is not documented in the VT-100 
	 *  manual.)
	 *
	 *  At the begining of this main loop, we erase the cursor.  At the
	 *  end, when the cursor may be moved, we redraw it.  By comparing
	 *  the value of ll and cc with the value of w->curline and w->curcol,
	 *  we can tell if the cursor has moved.  If it has, we set the VT-100
	 *  weird end of line variable, w->wrapln, to zero.  The meaning of 
	 *  w->wrapln, then, is that we have displayed a character on the last
	 *  column of the line, but didn't move the cursor.  The next character
	 *  we'll display on the first position of the next line, and move
	 *  the cursor to the column after that.  Rubout(), in cook.c, does
	 *  the same sort of thing, only backwards.
	 */

	short	ll, cc;

	/*
	 *  We still need this because bits with parity
	 *  can come in on the pass_through ports.
	 */

	c &= 0x7f;		/*  Throw away parity bits. */

	if (vt100debug & 0x80) printf("%x",c);

	ll = w->curline; cc = w->curcol;

	/*
	 * To avoid mess on screen, erase the puck and character cursors
	 * (unless they are already erased).  They will be redrawn at
	 * the end of this routine when we are done with this spurt of
	 * output (drawcursor TRUE).
	 */

	offcursor(d, w);

 	if (w->t_local&LFLUSHO)
		drawcursor = FALSE;

	/*
	 *  There are two types of data that might come in -- ordinary 
         *  characters to be displayed and escape sequences.
	 *  If the input is an escape sequence it can be in two modes:
	 *  ANSI or VT52.  In either case the data are represented in
	 *  the seq array.  (Each window has its own seq array).
	 *  In either case, the fact that the terminal is in the middle
	 *  of receiving an escape sequence is indicated by setting 
	 *  seq[NUMBER][0] to a nonzero value (this variable 
	 *  contains the size of the seq array.)
	 *
	 *  Even in the middle of an escape sequence, control characters
	 *  are processed normally.  ESC starts a new escape sequence,
	 *  SUB and CAN cancel an escape sequence.
	 */

	if (!w->seq[0] && ' ' <= c && c <= '~') {
		print(d, w, c);
	}

	else if (c == ESCAPE)			/*  ESC  */
		w->seq[0] = 1;

	else if (c == SUB || c == CAN)		/*  SUB, CAN  */
		w->seq[0] = 0;

	else if (c < ' ')			/*  Other control chars  */
		docontrol(d, w, c);

	else if (w->seq[0] && w->ansi) {

		if (w->seq[0] == 1) 
			ansichar(d, w, c);

#ifndef WHITE
		else if (w->seq[1] == ')') {

			if (c == 'B')
				w->g1charset = ASCIISET;
			else if (c == '0')
				w->g1charset = GRAPHSET;
			if (w->whichcharset == G1SELECTED)
				w->graphics = w->g1charset;
			w->seq[0] = 0;
		}

		else if (w->seq[1] == '(') {

			if (c == 'B')
				w->g0charset = ASCIISET;

			else if (c == '0')
				w->g0charset = GRAPHSET;

			if (w->whichcharset == G0SELECTED)
				w->graphics = w->g0charset;

			w->seq[0] = 0;
		}
#endif WHITE

		else if (w->seq[1] != '[')
			w->seq[0] = 0;

		/*
		 *  The only remaining possibility is that the sequence is of
		 *  the form ESC [ <paramseq> <char>  or
		 *	     ESC : <paramseq> <char>  where
		 *  <paramseq> ::= <param> | <param> ; <paramseq>
		 *  <param> ::== <digit>*
		 *  or of the form ESC [ ? <digit> {h|l} 
		 *
		 *  In the former case the parameters are stored as follows:
		 *  seq[0] = size of seq array
		 *  seq[1] = '['
		 *  seq[2] = subsequent chars in display
		 *  . . .
		 *  seq[n] = a letter (a-z,A-Z)
		 *
		 *  There is one incompatibility with the DEC VT100. After
		 *  ESSIZE - 1, characters not including the final letter
		 *  are received, subsequent non-letters are thrown away.
		 */

		else
			if (('A' <= c && c <= ']') || ('a' <= c && c <= 'z')) {

				if (w->seq[1] == '[')
					ansiseq(d, w, c);
#ifdef CHECK
				else
					fatal("screen_vt100 found c == %d", c);
#endif
			} else
				if (w->seq[0] < ESSIZE)
					w->seq[ (w->seq[0]++) ] = c;
	}

	/*
	 *  The format for escape sequences in VT 52 mode is considerably
	 *  simpler than the format for ANSI mode.  All sequences are of
	 *  the form ^[<char>, except Direct Cursor Adress which is of
	 *  the form ^[Y<line><col> .
	 */

	else if (w->seq[0] == 1) /*  and !w->ansi  */ 
		vt52seq (d, c);

	else if (w->seq[0] == 2) {	/*  and !w->ansi (^[Y<line><col>)  */

		w->seq[2] = (c<=' '+w->line)? c-' ' : w->curline; 
		w->seq[0] = 3;

	} else if (w->seq[0] == 3) {	/*  and !w->ansi (^[Y<line><col>)  */

		w->curline = w->seq[2];
		if (c <= ' ' + w->col)
			w->curcol =  c - ' ';

		w->seq[0] = 0;
	}
#ifdef CHECK
	else if (c != DEL)
		fatal("screen_vt100 found c == %d (2)", c);
#endif

	/*
	 *  Redraw cursors: If there are no more characters comming down
	 *  the pike, redraw the puck cursor.  Since we may be displaying
	 *  characters from unix for a non-selected window, the criteria
	 *  for drawing the regular cursor are more complicated: this must
	 *  also be the selected window.
	 *  If there are more characters comming, we don't have to draw
	 *  any cursors, since they would just get erased next time 
	 *  anyway.
	 */

	if (drawcursor) oncursor(d, w);

	/*  Adjust w->wrapln (see comment at beginning of procedure)  */
	if (ll != w->curline || cc != w->curcol)
		w->wrapln = FALSE;

	if (vt100debug & 0x40)
		printf("col = %x, line = %x\n",
			w->curcol, w->curline);
}

/*
 *  Handle a single char ansi sequence
 */
PAGESUBR(ansichar)
ansichar(d, w, c)
register pdisp d;
register windowp w;
char c;
{
	char message[5];
	char *i, *j;

	w->seq[0] = 0; 
	switch(c) {

		case 'D':
			scroll(d, w); break;

		case 'E':			/*  New Line  */
			docontrol(d, w, LF); 
			docontrol(d, w, CR); 
			break;

		case 'M':
			backscroll(d, w); break;

		case 'Z':			/*  Identify Term  */
			sendoff(d, w, "\033[?l;0c");
			break;

		case '7':			/*  Save Cursor  */
			w->oldcol = w->curcol; 
			w->oldline = w->curline; 
#ifndef WHITE
			w->oldgraphics = w->graphics;
#endif WHITE
			w->oldcharatts = w->charatts;
			break;

		case '8':			/*  Restore Cursor  */
			w->curcol = w->oldcol; 
			w->curline = w->oldline; 
#ifndef WHITE
			w->graphics = w->oldgraphics;
#endif WHITE
			w->charatts = w->oldcharatts;
			break;

#ifndef WHITE
		case '=':			/*  Application  */
			w->application = TRUE; 
			break;

		case '>':			/*  Numeric  */
			w->application = FALSE; 
			break;

#else WHITE
		/* ESC < used to put us in GED one-line mode and ESC <
		   used to take us out.  Nobody should use them anymore --
		   ESC < is now ESC S and ESC > is now ESC s.  */
		case '>':			/*  Numeric  */
#ifdef notdef
			if (vt100debug&1)
				printf("Mommy! somebody said ESC>!\n");
#endif notdef
			break;

		case '<':			/*  Nothing  */
#ifdef notdef
			if (vt100debug&1)
				printf("Mommy! somebody said ESC<!\n");
#endif notdef
			break;
#endif WHITE

		/*  NEW VALID SEQUENCES  */

#ifndef WHITE
		case 'b':			/*  Report baud rate */
			i = "\033[xb";
			j = message; 
			while (*j++ = *i++);
			message[2] = d->baudrate + ' ';
			sendoff(d, w, message);
			break;
#endif WHITE

#ifndef WHITE
		case 'j':			/*  Report jumpscroll */
			i = "\033[xj";
			j = message; 
			while (*j++ = *i++);
			message[2] = w->jumpscroll + ' ';
			sendoff(d, w, message);
			break;
#endif WHITE

#ifndef WHITE
		case 'O':
		/* Lock out every queue except the one on which this came in.
		   This is used by hardcopy to avoid messes.  This mode
		   will end upon receipt of esc o (below) or after 700
		   main loops with nothing comming in on this port. */

			if (w->passthrough == GRAPHPASS) {
				d->lockcount = 0;
				d->savewait = d->waitfor;
				d->waitfor = 1 << w->port;
				d->fastwait = FALSE;
				puckcursor(0);
				d->puckdrawn = FALSE;
			}
			break;

		case 'o': /* Lockout done, enable queues */

			d->waitfor = d->savewait;
			d->fastwait = !d->doauto;
			oncursor(d, w);
			break;

#endif WHITE
		case 'P':			/*  Send puck sequences */
			w->puck = TRUE;
			break;

		case 'p':			/*  Suppress puck sequences */
			w->puck = FALSE;
			break;

#ifndef WHITE
		case 'q':
			if (d->peacock) setcolor(colortable);
			break;
#endif WHITE

		case 'v':
			puckcursor(0);
			blankout(MODELINE,  0, ACTUALWIDTH-1, NORMAL);
			blankout(TFKEYLINE, 0, ACTUALWIDTH-1, NORMAL);
			printit (MODELINE, 3, version);
			puckcursor(1);
			d->puckdrawn = TRUE;
			break;

#ifdef NOT_YET
		case 'W':			/*  do chuck the duck  */
			duck_mode(w, message);	/*  (or other graphics) */
			break;
#endif NOT_YET



#ifdef WHITE
		case 'S':			/* -> single line mode(WHITE)*/
			/* This only exists on WHITE systems.  In single line
			   mode all UNIX output goes to one line.  */
			dssetone(1);
			break;
		case 's':			/* <- single line mode(WHITE)*/
			/* Get out of one-line mode. */
			dssetone(0);
			break;
#endif WHITE



#ifdef NOT_YET
		case 'Y':			/* -> Graphics mode (almost)  */
			w->passthrough = SIMULATOR;
			goto gmode;
#endif NOT_YET

#ifdef NOT_YET
		case 'X':			/* -> Graphics mode  */
			w->passthrough = GRAPHPASS;
gmode:
			w->puck = TRUE;
			startdisp(0, 1);
#ifndef WHITE
			if (w->fullscreen) clearscreen();
			else blackout(d, w);
#else WHITE
			clearscreen();
#endif WHITE
			break;

		case 'x':			/*  exit graphics mode  */
#ifndef WHITE
			if (w->fullscreen) {
#endif WHITE

				startdisp(0, 0);
				clearscreen();

#ifndef WHITE
			} else blackout(d, w);
#endif WHITE

			w->passthrough = UNIXPASS;
			w->puck = FALSE;
			w->curcol = 0; 
			w->curline = 0; 
			break;
#endif NOT_YET

		/*
		 *  If the sequence begins with a [, (, ), :, or #
		 *  then it is part of a longer sequence, otherwise
		 *  the sequence is of the form ESC <char>.
		 */
		case '[': 
		case ':':
		case '(': 
		case ')': 
		case '#':
			w->seq[1] = c; 
			w->seq[0] = 2; 
			break;

		default:
			break;
	}
}

/*
 *  ansiseq -- handle an ansi sequence of the form ESC [ param1, param2 ... c
 */

PAGESUBR(ansiseq)
ansiseq (d, w, c)
register pdisp d;
register windowp w;
char c;
{
	short params[MAXPARAMS];	/*  To get parameters for ESC seqs  */
	short i;			/*  An index var  */
	short delta;			/*  direction of change in N/F seq, */
					/*  and general purpose utility var */
	static char Wanswer[] = "n0123456789ab";

	/*
	 *  Getparams will set
	 *  params[0] = no of params;
	 *  params[n] = nth parameter
	 */

	getparams(params, w->seq, FALSE);
	w->seq[0] = 0;

	switch (c) {
/*
 *  VALID EXTENSIONS
 *
 *	The following are VALID extensions to the VT100 terminal character
 *	set:
 *
 *		ESC [ a;b;c;dq	(Peacock only) Sets colortable[a] to
 *				(b, c, d).
 *		ESC [ x L	Set number of lines to x where x > 2 and 
 *				x < HEIGHT.
 *		ESC [ x b       Set baudrate to x (baudrate[] index)
 *		ESC [ x j       Set jumpscroll
 *				if x = 0, set to 1/2 screen height
 *				if x > screen height, set to screen height
 *				if 0 < x < screen height, set to x.
 *		ESC [ x w       Window control:
 *				x=1: delete current window
 *				x=2: create initial window
 *		ESC [ x y W     What window is at (x,y)?
 *		ESC [ x; ... N	Turn params x ... on (see below)
 *		ESC [ x; ... F	Turn params x ... off (see below)
 *		ESC [ 0 <N|F>	Query parameters.  The parameters are
 *				encoded into printing ASCII by adding ' '.
 *
 *	Params:
 *
 *		No	if set means		if not set means
 *		--	------------		----------------
 *		1	ansi mode		vt52 mode
 *		2	key application mode	key numeric mode
 *		3	cursor key mode		cursor application mode
 *		4	wrap around mode	no wrap around mode
 *		5	parity on		parity off
 *		6	odd parity		even parity
 *		7	BS -> DEL (host mode)	normal
 *		8	RtCtl -> CR		normal
 *		9	underscore cursor	block cursor
 *		10	Graphics character set	ASCII character set
 *		11	Do auto repeat		Don't do auto repeat
 *		12	(Report only) Number of lines
 */

#ifdef NOT_YET
	case 'z':
	if (params[0] > 3) {

		bbcomm	pattern;
		short i;

		if (params[0] == 4) params[5] = 0;
		pattern.windxr = MAX(params[1], params[3]);
		if (pattern.windxr >= XSIZE) break;
		pattern.windyt = MAX(params[2], params[4]);
		if (pattern.windyt >= YSIZE) break;
		pattern.windxl = MIN(params[1], params[3]);
		pattern.windyb = MIN(params[2], params[4]);

		for (i=0; i < PATNSIZE; i++) pattern.patn[i] = 0xffff;

		texture (params[5], &pattern, BOLD);
	}
		break;
#endif NOT_YET

#ifndef WHITE
	case 'q':
		if (d->peacock) {
			delta = params[1];
			colortable[delta][0] = params[2];
			colortable[delta][1] = params[3];
			colortable[delta][2] = params[4];
		}
		break;
#endif WHITE

#ifndef WHITE
	case 'j':
		delta = params[1];

		if (delta > 0) {	/* Set jumpscroll */
			if (delta > w->line) delta = w->line;
			w->oldjump = w->jumpscroll;
			w->jumpscroll = delta;

		} else {	/* Special meaning */

			if (params[0] == 1) {
				w->oldjump = w->jumpscroll;
				w->jumpscroll = w->line / 2;
			} else {
				if (params[1] == 0)
					w->jumpscroll = w->oldjump;
			}
		}
		break;
#endif WHITE

#ifndef WHITE
	case 'b':
		delta = params[1];
		if (delta >= 0 && delta <= 15) {
			d->baudrate = delta;
			setbaud(delta);
		}
		break;
#endif WHITE

#ifndef WHITE
	case 'W':
		if (params[0] == 2)
			delta = findwindow(d, params[1], params[2]);
		else delta = NOPORT;
		shoveunix(w->port, 1, &Wanswer[delta + 1]);
		break;
#endif WHITE

#ifndef WHITE
	case 'w':
		for (delta = 1; delta <= params[0]; delta++) {

			int i;

			switch (params[delta]) {

			case 1:		/* delete the current window */
				deletewindow(d, -1, w->port);
				break;

			case 2:
				/* Create a 24 by 80 character
				   window in upper left corner,
				   but only if no windows exist. */

				for (i = 0; i <= VAXQUEUE; i++)
					if (d->www[i].inuse) break;

				if (i ==  NUMUNIX)
					createwindow(d, 0, 81*FONTWIDTH, 795,
					    799-26*FONTHEIGHT, UNIXPASS, 0);

				break;

			default:
				break;

			} /* switch */
		} /* for delta */
		break;
#endif WHITE


#ifndef WHITE
	case 'L':				/*  Set Line Numbers  */
		if (w->fullscreen) {

			if (!params[0])		/* use the default value */
				params[1] = HEIGHT;

			if (2 <= params[1] && params[1] <= HEIGHT) {

				w->line = params[1] - 1;
				w->curcol = w->curline = 0;
				startdisp(0, 0);
				clearscreen();
			}
		}
		break;
#endif WHITE

#ifndef WHITE
	case 'N':				/*  Valid Variable On	*/
	case 'F':				/*  Valid Variable Off  */
		delta = (c == 'N');		/*  set direction	*/
		for (i = params[0]; i; i--)
			switch (params[i]) {

			case 0:
				sendchar(d, w, ESCAPE);
				sendchar(d, w, (char)(' '+w->ansi));
				sendchar(d, w, (char)(' '+w->application));
				sendchar(d, w, (char)(' '+w->curkey));
				sendchar(d, w, (char)(' '+w->wrap));
				sendchar(d, w, (char)(' '+d->parity));
				sendchar(d, w, (char)(' '+d->odd));
				sendchar(d, w, (char)(' '+d->map));
				sendchar(d, w, (char)(' '+d->mapRCtoCR));
				sendchar(d, w, (char)(' '+w->under));
				sendchar(d, w, (char)(' '+w->graphics));
				sendchar(d, w, (char)(' '+d->doauto));
				sendchar(d, w, (char)(' '+1+w->line));
				sendchar(d, w, LF);
				break;

			case 1:	w->ansi = delta;
				break;

			case 2:	w->application = delta;
				break;

			case 3:	w->curkey = delta;
				break;

			case 4: w->wrap = delta;
				break;

			case 5:	d->parity = delta;
				break;

			case 6:	d->odd = delta;
				break;

			case 7:	d->map = delta;
				break;

			case 8:	d->mapRCtoCR = delta;
				break;

			case 9:	w->under = delta;
				break;

				/* This makes an assumption that
				 * TRUE==GRAPHSET and FALSE==ASCIISET
				 * which may not always be valid.  I
				 * don't plan to change it.  If you do
				 * change it the correct code is
				 * w->graphics = (delta)? GRAPHSET:ASCIISET;
				 * See also setup.c  (sbs/jao)
				 */
			case 10:
				w->graphics = delta;
				break;

			case 11:
				d->doauto = delta;
				d->fastwait = !delta;
				break;
			}
		break;
#endif WHITE

/*	Beginning of ANSI-standard VT100 sequences	*/

	case 'r':					/*  Set Margins  */
		if (params[0] == 0 || params[1] == 0 || params[1] > w->line)
			params[1] = 1;
		if (params[0] <= 1 || params[2] == 0 || params[2] > w->line)
			params[2] = w->line + 1;
		if (params[1] < params[2]) {

			w->topsc = params[1]-1;
			w->botsc = params[2]-1;
		}
		w->curline = w->curcol = 0;
		break;

	case 'D':					/*  Cursor Backwards  */
		if (params[0] == 0 || params[1] == 0)
			params[1] = 1;
		while (params[1]--)
			back (w);
		break;

	case 'B':					/*  Cursor Down  */
		if (params[0] == 0 || params[1] == 0)
			params[1] = 1;
		if (params[1] + w->curline > w->botsc)
			params[1] = w->botsc - w->curline;
		while (params[1]--)
			scroll(d, w);
		break;

	case 'C':					/*  Cursor Forward  */
		if (params[0] == 0 || params[1] == 0)
			params[1] = 1;
		while (params[1]--)
			forward (w);
		break;

	case 'f':					/*  Horz & Vert Pstn */
	case 'H':					
		if (params[0] == 0 || params[1] == 0)
			params[1] = 1;
		if (params[0] <= 1 || params[2] == 0)
			params[2] = 1;

		if (w->om) {
			if ((delta = params[1]-1 + w->topsc) <= w->botsc)
				w->curline = delta;
			else w->curline = w->botsc;
		} else
			w->curline = MIN(w->line, params[1]-1);

		w->curcol = MIN(w->col, params[2] - 1);
		break;

	case 'A':					/*  Cursor Up */
		if (params[0] == 0 || params[1] == 0)
			params[1] = 1;
		if (params[1] > w->curline - w->topsc)
			params[1] = w->curline - w->topsc;
		while (params[1]--)
			backscroll(d, w);
		break;

	case 'c':					/*  Device Attrbts  */
		/*  we respond with "no options" */
		sendoff(d, w, "\033[?1;0c");
		break;

	case 'J':					/*  Erase in Display */
		if (params[0] == 0 || params[1] == 0)
			erase(d, w, w->curcol, w->curline, w->col, w->line);
		else if (params[1] == 1)
			erase(d, w, 0, 0, w->curcol, w->curline);
		else if (params[1] == 2)
#ifndef WHITE
			if (w->fullscreen) {
				startdisp(0, 0);
				clearscreen();
			} else
				blackout(d, w);
#else WHITE
			clearscreen();
#endif WHITE
		break;

	case 'K':					/*  Erase in Line  */
		if (params[0] == 0 || params[1] == 0)
			eline(d, w, w->curline, w->curcol, w->col);
		else if (params[1] == 1) 
			eline(d, w, w->curline, 0, w->curcol);
		else if (params[1] == 2)
			eline(d, w, w->curline, 0, w->col);
		break; 

	case 'h':					/*  Set Mode  */
	case 'l':					/*  Reset Mode  */
		delta = (c == 'h');		/* set direction of change */
		for (i = params[0]; i;)
			switch(params[i--]) {

#ifndef WHITE
			case 1:
				w->curkey = delta;
				break;
#endif WHITE
			case 2:
				w->ansi = delta;
				break;
			case 6:
				w->om = delta;
				w->curline = delta ? w->topsc : 0;
				w->curcol = 0;
				break;
			case 7:
				w->wrap = delta;
				break;
			}
		break;

	case 'm':	/*  Character attributes */

		if (params[0] == 0) {

			params[0] = 1;
			params[1] = 0;
		}

		for (i = 1; i <= params[0]; i++)

			switch (params[i]) {

			case 0:	
				w->charatts = NORMAL; 
				break;

			case 1:
				/* w->charatts |= BOLD;
				w->charatts &= ~NORMAL;*/
				break;

			case 4:
				w->charatts |= ULBIT;
				break;

			case 7:
				w->charatts |= RVBIT;
				break;
		}
		break;

	case 'n':				/*  Device Status Report  */
		if (params[0] == 0) 
			params[0] = 1; params [1] = 0;

		for (i = 1; i <= params [0]; i++)

			switch (params[i]) {

			case 5:
				sendoff(d, w, "\033[0n");
				break;

			case 6:
				sendchar(d, w, '\033');
				sendchar(d, w, '[');
				delta = w->curline+1+'0';
				if (w->om) delta -= w->topsc;
				sendchar(d, w,  delta / 10);
				sendchar(d, w,  delta % 10);
				sendchar(d, w, ';');
				sendchar(d, w, ('0' + (w->curcol+1) / 10));
				sendchar(d, w,  ('0' + (w->curcol+1) % 10));
				sendchar(d, w, 'R');
				break;
			}

	default:
		break;

	}
}

/*
 *  vt52seq -- handle sequnces for vt52 mode
 */

PAGESUBR(vt52seq)
vt52seq(d, c)
pdisp d;
register char c;
{ 
	register windowp w = d->curwindow;

	w->seq[0]=0; 
	switch(c) {

	case 'Y':			/*  Direct Cursor Address  */
		w->seq[0] = 2;  
		return;

	case 'A':				/*  Cursor Up */
		if (w->curline > w->om ? w->topsc : 0) 
			backscroll(d, w);	
		break;

	case 'B':				/*  Cursor Down  */
		if (w->curline < w->om ? w->botsc : w->line) 
			scroll(d, w); 
		break;

	case 'C':				/*  Cursor Right  */
		forward(w); 
		break;

	case 'D':				/*  Cursor Left  */
		back(w); 
		break;

#ifndef WHITE
	case 'F':				/*  Enter Graphics  */
		w->graphics = GRAPHSET;
		break;

	case 'G':				/*  Exit Graphics  */
		w->graphics = ASCIISET;
		break;
#endif WHITE

	case 'H':				/*  Cursor to Home  */
		w->curline = w->curcol = 0;
		break;

	case 'I':				/*  Reverse Line Feed  */
		backscroll(d, w);
		break;

	case 'J':				/*  Erase to End of Screen  */
		erase(d, w, w->curcol, w->curline, w->col, w->line); 
		break;

	case 'K':				/*  Erase to End of Line  */
		eline(d, w, w->curline, w->curcol, w->col); 
		break;

	case 'Z':				/*  Identify  */
		sendoff(d, w, "\033/Z"); break;

#ifndef WHITE
	case '=':				/*  Enter Alt Keypad Mode  */
		w->application = TRUE; break;

	case '>':				/*  Exit Alt Keypad Mode  */
		w->application = FALSE; break;
#endif WHITE

	case '<':				/*  Enter ANSI Mode  */
		w->ansi = TRUE;
		break;

	default:
		break;

	}
}

/*
 *  Control -- handle control characters
 */

PAGESUBR(docontrol)
docontrol(d, w, c)
pdisp d;
register windowp w;
register char c;
{
	switch (c) {

	case BEL:
		bell();
		break;

	case BACKSP:
		back(w);
		break;

	case LF:
	case VT:
	case FF:
		scroll(d, w); 
		break;

	case CR:	
		w->curcol = 0;
		break;

#ifdef WHITE
#ifndef KERNEL
	case '\003': raw(0); exit(1); break;	/* ^C */
	case '\034': raw(0); abort(); break;	/* ^\ */
#endif KERNEL
#endif WHITE

#ifndef WHITE
	case SI:
		w->graphics = w->g0charset; 
		w->whichcharset = G0SELECTED;
		break;

	case SO:
		w->graphics = w->g1charset; 
		w->whichcharset = G1SELECTED;
		break;
#endif WHITE

	default:
		break;
	}
}

/*
 *  Forward -- move cursor forward one space, but not past end of line
 */

PAGESUBR(forward)
forward(w)
windowp	w;
{
	if (w->curcol < w->col)
		w->curcol++;
	else if (w->curline < w->line) {
		w->curcol = 0;
		w->curline++;
	}
}

/*
 *  Back -- move cursor back one space.
 */

PAGESUBR(back)
back(w)
windowp w;
{
	if (w->curcol)
		w->curcol--;
	else if (w->curline) {
		w->curline--;
		w->curcol = w->col;
	}
}

/*
 *  erase -- erase from (col1, line1) to (col2, line2) inclusive
 */

PAGESUBR(erase)
erase(d, w, col1, line1, col2, line2)
register pdisp d;
register windowp w;
short	col1, line1, col2, line2;
{
	short	col = w->col;

#ifdef CHECK
	short	line = w->line;

	if (col1<0 || col2<0 || line1<0 || line2<0 || 
		col1>col || col2>col || line1>line || line2>line)
		fatal("erase col1=%d line1=%d col2=%d line2=%d",
		    col1, line1, col2, line2);
#endif

	for (;line1 < line2; line1++, col1 = 0)
		eline(d, w, line1, col1, col);

	eline(d, w, line2, col1, col2);
}

/*
 *  eline -- erase line
 */

PAGESUBR(eline)
eline(d, w, line, start, stop)
register pdisp d;
register windowp w;
short line, start, stop;
{
	if (d->puckdrawn) puckcursor(0);

	blankout(line + w->startline, start + w->startcol,
	    stop + w->startcol, NORMAL);

	puckcursor(1);
	d->puckdrawn = TRUE;
}

/*
 *  getparams -- translate raw escape sequences into a list of parameters.
 *
 *	ANSI standard escape sequences are of the following form:
 *	
 *	ESCSEQ ::= HEADER BODY FINAL
 *	HEADER ::= \033 FLAG
 *	FLAG   ::= '[' | ':' | '[?' #standard seqs start with '[', BBN with ':'
 *	BODY   ::= NUMBER | NUMBER ';' BODY
 *	NUMBER ::= DIGIT NUMBER | _/\_
 *	DIGIT  ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
 *	FINAL  ::= 'a' | . . . | 'z'| 'A' | . . . | 'Z' | '[' | ']'
 *
 *	this procedure parses these sequences which are stored in the
 *	array escseq in the form:
 *		escseq[0] == number of chars in escseq
 *		escseq[1] == '[' | ':'
 *		escseq[2] == first char of body
 *			.
 *			.
 *			.
 *		escseq[escseq[0]] == FINAL
 *
 *	the parsed procedure will be of the form:
 *		params[0] = number of parameters in escseq body
 *		params[1] = first parameter in escseq body
 *			.
 *			.
 *			.
 *	if checkdefault is true, then the parameter checking sets defaults to
 *	the value UNDECL.
 */

PAGESUBR(getparams)
getparams (params, escseq, checkdefault)
register char escseq[];
short params[];
bool checkdefault;
{
	register short n;	/*  Escseq index. */
	short sum;		/*  Cumulative value of parameter  */

#ifdef CHECK
	if (escseq[0] < 1 || escseq[0] > ESSIZE) { 

		int i; 

		debug("escseq[0] = %d\n", escseq[0]);
		for (i=1; i<=escseq[0]; i++)
			debug("escseq[%d] = %d\n", i, escseq[i]);
		fatal("\nBad escape sequence");
	}

#endif

	n = 1; params[0] = 0;
	while (n < escseq[0]) {

		while (++n<=escseq[0] && (escseq[n] > '9' || escseq[n] < '0') 
			&& escseq[n] != ';')
			;

		if (n < escseq[0]) {

			if (escseq[n+1] == ';' && checkdefault)
				params[ (++params[0]) ] = UNDECL;

			else {

				for (sum=0; n<escseq[0] && escseq[n]!=';'; n++)

					if ('0'<=escseq[n] && escseq[n]<='9')
						sum = sum*10 + escseq[n] - '0';

				params[ (++params[0]) ] = sum;
			}
		}
	}
}

/*
 *  Print -- prints a character at a certain place handling wraparound
 */

PAGESUBR(print)
print(d, w, c)
pdisp d;
register windowp w;
register char c;
{
	/*  Handle special graphics set -- see p. 39 of VT100 manual  */

#ifndef WHITE
	if (w->graphics == GRAPHSET && '_' <= c && c <= '~') c -= '_';
#endif WHITE

	if (w->curcol < w->col) { 		/*  Not end of line  */
 		if ((w->t_local&LFLUSHO) == 0) 
			writechar(c, w->curline + w->startline,
			    w->curcol + w->startcol, w->charatts);
		w->curcol++;
						/*  End of line -- no wrap  */
	} else if (!w->wrap || !w->wrapln || 
	    (w->curline == w->line && w->curline != w->botsc)) {

 		if ((w->t_local&LFLUSHO) == 0) 
			writechar(c, w->curline + w->startline,
			    w->curcol + w->startcol, w->charatts);

		w->wrapln = TRUE;

	} else {

		scroll(d, w);
 		if ((w->t_local&LFLUSHO) == 0) 
			writechar(c, w->curline + w->startline,
			    w->startcol, w->charatts);

		w->curcol = 1;				/* update the column */
	}
}

PAGESUBR(backscroll)
backscroll(d, w)
register pdisp d;
register windowp w;
{
	if (w->curline == 0 && w->curline != w->topsc) return;
#ifdef WHITE
	if (w->curline == w->topsc) {
		if (w->passthrough >= GRAPHPASS) return;
		dscroll(dstoy(w->topsc,dswnd), (w->botsc-w->topsc+1)*DSCELLH, -1);
	} else {
		w->curline--;
	}
#else WHITE
	if (w->fullscreen) {

		if (w->curline)
			w->curline--;
		else {
			if (w->passthrough >= GRAPHPASS) return;
			blankout (w->line, 0, ACTUALWIDTH, NORMAL);
			startdisp(-1); /*  hardware scroll */
			erase(d, w, 0, 0, ACTUALWIDTH-1, 0);
		}

	} else {

		if (w->curline == w->topsc) {

			bbcomm	scrollit;

			if (w->passthrough == GRAPHPASS) return;
			scrollit.dstx = scrollit.windxl = w->wxl + XOFFSET;
			scrollit.windxr = w->wxr - XOFFSET;
			scrollit.windyb = w->wyt - YOFFSET -
			    FONTHEIGHT * (w->botsc-1);
			scrollit.windyt = w->wyt - YOFFSET + 1 -
			    FONTHEIGHT * w->topsc;
			scrollit.dsty = scrollit.windyt - FONTHEIGHT;
			bitblt (REPLACE, &scrollit);
			eline(d, w, w->topsc, 0, w->col);
		} else
			w->curline--;
	}
#endif WHITE
}

/*
 *  scroll forward -- a vertical tab (FF, LF, VT, or line too long)
 */

PAGESUBR(scroll)
scroll(d, w)
pdisp d;
register windowp w;
{
#ifdef WHITE
	if (w->curline == w->line && w->curline != w->botsc) return;
	if (w->curline == w->botsc) {
		if (w->passthrough == GRAPHPASS) return;
		dscroll(dstoy(w->topsc,dswnd), (w->botsc-w->topsc+1)*DSCELLH, 1);
		w->curline--;
	}
#else WHITE
	if (w->fullscreen) {

		if ((w->topsc || w->botsc != w->line)
		    && w->curline == w->botsc) {

			bbcomm	scrollit;
			register short i;
			short lines;

			if (w->passthrough == GRAPHPASS) return;
			lines = MIN(w->jumpscroll, (w->botsc - w->topsc));
			if (!lines) lines = 1;
			scrollit.dstx = scrollit.windxl = w->wxl;
			scrollit.windxr = w->wxr;
			scrollit.windyb = w->wyb + FONTHEIGHT*
			    (w->line - w->botsc);
			scrollit.dsty = w->wyb + FONTHEIGHT*
			    (w->line - w->topsc + 1);
			scrollit.windyt = scrollit.dsty - FONTHEIGHT * lines;
			bitblt (REPLACE, &scrollit);

			for (i=0; i < lines; i++)
				eline(d, w, w->botsc-i, 0, w->col);
			w->curline -= lines;

		} else if (w->curline == w->line) {

			if (w->passthrough >= GRAPHPASS) return;
			blankout(0, 0, ACTUALWIDTH-1, NORMAL);
			startdisp(1);	/*  hardware scroll */
			erase(d, w, 0, w->line, ACTUALWIDTH-1, w->line);
			w->curline--;
		}

	} else {

		if (w->curline == w->line && w->curline != w->botsc) return;
		if (w->curline == w->botsc) {

			bbcomm	scrollit;
			register short i;
			short lines;

			if (w->passthrough == GRAPHPASS) return;
			lines = MIN(w->jumpscroll, (w->botsc - w->topsc));
			if (!lines) lines = 1;
			scrollit.dstx = scrollit.windxl = w->wxl + XOFFSET - 1;
			scrollit.windxr = w->wxr - XOFFSET;
			scrollit.windyb = w->wyb + YOFFSET + FONTHEIGHT*
			    (w->line - w->botsc);
			scrollit.dsty = w->wyb + YOFFSET + FONTHEIGHT*
			    (w->line - w->topsc + 1);
			scrollit.windyt = scrollit.dsty - FONTHEIGHT * lines;
			bitblt (REPLACE, &scrollit);

			for (i=0; i < lines; i++)
				eline(d, w, w->botsc-i, 0, w->col);
			w->curline -= lines;
		}
	}
#endif WHITE
	w->curline++;
}

/*
 *  duck_mode :  draw a sequence of line segments
 *
 *	Input format begins with ESC W, followed by an optional
 *	'u' or 'U', followed by series of four- or five-tuples
 *	of decimal numbers separated by any non digit.  Tuples are
 *	terminated by newlines or at the end of the fifth number.
 *	If the fifth number is present it is used as a line drawing
 *	pattern, otherwise the line is drawn solid.
 *	Duck mode is terminated by ESC <x> where x is any character.
 *
 *	If the 'u' or 'U' (or any sequence on non-digits beginning with
 *	'u' or 'U') is present, the line segments will not be scaled
 *	within the window, otherwise they will be.  If a line segment
 *	is not scaled and any part is outside the window, it is 
 *	not drawn.
 *
 */

PAGESUBR(duck_mode)
duck_mode(w, c)
register windowp w;
uchar *c;
{
	ushort duck[5];
	register short i;
	uchar	queues[NUMQUEUES];
	ushort	xlength = (w->wxr - w->wxl)/16;
	ushort	ylength = (w->wyt - w->wyb)/16;
#ifndef WHITE
	short port = w->port;
#else WHITE
	short port = 0;
#endif WHITE
	bool none;

	if (w->duckstate) {

		for (i = 0; i < 5; i++) {

			duck[i] = 0; none = TRUE;
			while (none || ('0' <= *c && *c <= '9')) {
				int clock;

				if (*c == ESCAPE) {
					while (!wait(queues, &junk,
					    (1<<port), 0) || !queues[port])
						;
					getdata (port, 1, c);
					w->curdrwn = FALSE;
timeout:
					w->duckstate = 0;
					return;
				}

				if ('0' <= *c && *c <= '9') {
					duck[i] = duck[i]*10 + *c - '0';
					none = FALSE;
				}
				if ((*c == '\n') && i) goto abortline;

				clock = 0;
				while (!wait (queues, &junk, (1<<port), 0) ||
				    !queues[port])
					if (clock++ > 100) {
						oncursor();
						goto timeout;
					}
				getdata (port, 1, c);
			}
		}
abortline:
		if (w->duckstate == 1) {

			if (duck[0] >= w->wxl && duck[0] <= w->wxr &&
			    duck[1] >= w->wyb && duck[1] <= w->wyt &&
			    duck[2] >= w->wxl && duck[2] <= w->wxr &&
			    duck[3] >= w->wyb && duck[3] <= w->wyt)
				drawline(duck[4], duck[0], duck[1],
				    duck[2], duck[3]);
		} else {
			short x1, y1, x2, y2;

			if (((x1 = xlength*duck[0]/64 + w->wxl) < XSIZE) &&
			    ((y1 = ylength*duck[1]/50 + w->wyb) < YSIZE) &&
			    ((x2 = xlength*duck[2]/64 + w->wxl) < XSIZE) &&
			    ((y2 = ylength*duck[3]/50 + w->wyb) < YSIZE))
				drawline (duck[4], x1, y1, x2, y2);
		}

	} else {  /* initial entry */

		while (!wait(queues, &junk, (1<<port), 0) || !queues[port]);
		getdata (port, 1, c);
		w->duckstate = (*c == 'u' || *c == 'U') ? 1 : 2;
	}
}
