[net.sources] Vnews interface for vt100

sch@linus.UUCP (Stephen C. Hemminger) (06/07/83)

	This is a terminal interface for Vnews which uses the Vt100
terminal.  It works, but here are some comments.

   1) I would rather have a termcap version, I am working on it.
   2) It was made in one day, so don't expect it to be absolutely
      perfect and throughly tested.
   3) Sorry, but I think the vnews code is a mess.
   4) I also have the job control fixes necessary for 4.1Bsd implemented.

Please send me any bug fixes or ideas for improvement.

This file is a replacement for virtterm.c in the distribution. Actually,
I call the original termHP.c and the new one termVT.c.

-----------------------------------
/*
 *  Virtual terminal handler for the HP-2621 terminal.
 *  Written by Kenneth Almquist, AGS Computers  (HO 4C601, X7105).
 *  Modified by Stephen Hemminger, MITRE for Vt100 terminals (in ANSI mode)
 */

#include <stdio.h>


#define PAGLEN 24
#define BOTLINE (PAGLEN - 1)
#define PAGWID 80
#define DIRTY 01

/* terminal escape sequences */
#define SHOME "\033[H"		/* move cursor to (0, 0) */
#define HOMELEN 3		/* length of SHOME */
#define SCLEAR "\033[H\033[2J"	/* clear screen and move cursor to (0, 0) */
#define SCLRLINE "\033[K"	/* clear to end of line */
#define CLRLINELEN 3		/* length of SCLRLINE */
#define SUP "\033[A"		/* move cursor up one line */
#define ULON "\033[4m"		/* turn underlining on */
#define ULOFF "\033[0m"		/* turn underlining off */
#define SREGION "\033[%d;%dr"	/* scrolling region */
#define SREGLEN 8
#define RINDEX	"\033M"		/* move up one line and scroll */

#define ULINE 0200
#define CURSEEN 1
#define putch(c) vputc(c)

/* Constants accessable by user */
int hasscroll = 1;			/* terminal has scrolling regions */
int LINES = PAGLEN;			/* number of lines on screen */
int COLS = PAGWID;			/* width of screen */

struct line {
	char len;
	char flags;
	char l[PAGWID];
};

int _row, _col;
int _srow, _scol;
struct line _virt[PAGLEN], _actual[PAGLEN];
int _uline = 0;
int _junked = 1;
int _curjunked;
int _dir = 1;



/*
 * Scrolling commands.  These have immediate effect on the screen.
 * It is up to the caller to decide whether scrolling will help.
 */

#ifdef SREGION

dshift(top, bot, count)
{
	register i;
	char buf[SREGLEN];

	if (count < 0) {	/* actually scroll up! */
		ushift(top, bot, -count);
		return;
	}
	if (_junked || count >= bot - top)
		return;
	for (i = bot - count ; _actual[i].len == 0 ; i--)
		if (i == top)
			return;
	for (i = top ; i <= bot ; i++)
		_virt[i].flags |= DIRTY;
	for (i = bot ; i >= top + count ; i--)
		_actual[i] = _actual[i - count];
	for ( ; i >= top ; i--)
		_actual[i].len = 0;

	_putstr(sprintf(buf, SREGION, top+1,bot+1));
	_curjunked = 1;
	_amove(top, 0);
	for (i = count ; --i >= 0 ; )
		_putstr(RINDEX);
	_putstr(sprintf(buf, SREGION, 1,PAGLEN));
	_curjunked = 1;
	_dir = -1;
}


ushift(top, bot, count)
{
	char buf[SREGLEN];
	register i;

	if (count < 0) {
		dshift(top, bot, -count);
		return;
	}
	if (_junked || count >= bot - top)
		return;
	for (i = top + count ; _actual[i].len == 0 ; i++)
		if (i == bot)
			return;
	for (i = top ; i <= bot ; i++)
		_virt[i].flags |= DIRTY;
	for (i = top ; i <= bot - count ; i++)
		_actual[i] = _actual[i + count];
	for ( ; i <= bot ; i++)
		_actual[i].len = 0;

	_putstr( sprintf(buf,SREGION,top+1,bot+1));
	_curjunked = 1;
	_amove(bot, 0);		/* move to bottom */
	for (i = 0 ; i < count ; i++)
		putch('\n');
	_putstr(sprintf(buf, SREGION, 1,PAGLEN));
	_curjunked = 1;
}

#else

dshift(top, bot, count) {
	if (count < 0) {
		ushift(top, bot, -count);
		return;
	}
	/* downward shift not implemented */
}


ushift(top, bot, count) {
	register i;

	if (count < 0) {
		dshift(top, bot, -count);
		return;
	}
	if (_junked || count >= bot - top)
		return;
	for (i = top + count ; _actual[i].len == 0 ; i++)
		if (i == bot)
			return;
	/* we cheat and shift the entire screen */
	/* be sure we are shifting more lines into than out of position */
	if ((bot - top + 1) - count <= PAGLEN - (bot - top + 1))
		return;
	for (i = 0 ; i <= BOTLINE ; i++)
		_virt[i].flags |= DIRTY;
	for (i = 0 ; i <= BOTLINE - count ; i++)
		_actual[i] = _actual[i + count];
	for ( ; i <= BOTLINE ; i++)
		_actual[i].len = 0;
	_amove(BOTLINE, 0);
	for (i = 0 ; i < count ; i++)
		putch('\n');
}

#endif SREGION



/*
 * generate a beep on the terminal
 */

beep() {
	putch('\7');
}



/*
 * Move to one line below the bottom of the screen.
 */

botscreen() {
	_amove(BOTLINE, 0);
	putch('\n');
	vflush();
}



move(row, col) {
	if (row < 0 || row >= PAGLEN || col < 0 || col >= PAGWID)
		return;
	_row = row;
	_col = col;
}



/*
 * Output string at specified location.
 */

mvaddstr(row, col, str)
	char *str;
	{
	move(row, col);
	addstr(str);
}



addstr(s)
	char *s;
	{
	register char *p;
	register struct line *lp;
	register int col = _col;

	lp = &_virt[_row];
	if (lp->len < col) {
		p = &lp->l[lp->len];
		while (lp->len < col) {
			*p++ = ' ';
			lp->len++;
		}
	}
	for (p = s ; *p != '\0' ; p++) {
		if (*p == '\n') {
			lp->len = col;
			lp->flags |= DIRTY;
			col = 0;
			if (++_row >= PAGLEN)
				_row = 0;
			lp = &_virt[_row];
		} else {
			lp->l[col] = *p;
			lp->flags |= DIRTY;
			if (++col >= PAGWID) {
				lp->len = PAGWID;
				col = 0;
				if (++_row >= PAGLEN)
					_row = 0;
				lp = &_virt[_row];
			}
		}
	}
	if (lp->len <= col)
		lp->len = col;
	_col = col;
}



addch(c) {
	register struct line *lp;
	register char *p;

	lp = &_virt[_row];
	if (lp->len < _col) {
		p = &lp->l[lp->len];
		while (lp->len < _col) {
			*p++ = ' ';
			lp->len++;
		}
	}
	lp->l[_col] = c;
	if (lp->len == _col)
		lp->len++;
	if (++_col >= PAGWID) {
		_col = 0;
		if (++_row >= PAGLEN)
			_row = 0;
	}
	lp->flags |= DIRTY;
}



clrtoeol() {
	register struct line *lp;

	lp = &_virt[_row];
	if (lp->len > _col) {
		lp->len = _col;
		lp->flags |= DIRTY;
	}
}



/*
 * Clear an entire line.
 */

clrline(row) {
	register struct line *lp;

	lp = &_virt[row];
	if (lp->len > 0) {
		lp->len = 0;
		lp->flags |= DIRTY;
	}
}



clear() {
	erase();
	_junked++;
}



erase() {
	register i;

	for (i = 0 ; i < PAGLEN ; i++) {
		_virt[i].len = 0;
		_virt[i].flags |= DIRTY;
	}
}



refresh() {
	register i;
	int j, len;
	register char *p, *q;

	if (checkin())  return;
	if (_junked) {
		_sclear();
		_junked = 0;
	}
	_fixlines();
	for (i = _dir > 0? 0 : BOTLINE ; i >= 0 && i < PAGLEN ; i += _dir) {
		if ((_virt[i].flags & DIRTY) == 0)
			continue;
		_ckclrlin(i);		/* decide whether to do a clear line */
		len = _virt[i].len;
		if (_actual[i].len < len)  len = _actual[i].len;
		p = _virt[i].l;
		q = _actual[i].l;
		for (j = 0 ; j < len ; j++) {
			if (*p != *q) {
				_amove(i, j);
				_aputc(*p);
				*q = *p;
			}
			p++, q++;
		}
		len = _virt[i].len;
		if (_actual[i].len > len) {
			_clrtoeol(i, len);
		} else {
			for ( ; j < len ; j++) {
				if (*p != ' ') {
					_amove(i, j);
					_aputc(*p);
				}
				*q++ = *p++;
			}
			_actual[i].len = len;
		}
		if (checkin())  return;
	}
	_dir = 1;
	if (CURSEEN)
		_amove(_row, _col);
	vflush();		/* flush output buffer */
}


_sclear() {
	register struct line *lp;

	_putstr(SCLEAR);
	_srow = _scol = 0;
	for (lp = _actual ; lp < &_actual[PAGLEN] ; lp++) {
		lp->len = 0;
	}
	for (lp = _virt ; lp < &_virt[PAGLEN] ; lp++) {
		if (lp->len != 0)
			lp->flags |= DIRTY;
	}
}



#ifdef notdef		/* included to simplify conversion to new terminal */

_clrtoeol(row, col) {
	register struct line *lp = &_actual[row];
	register i;

	for (i = col ; i < lp->len ; i++) {
		if (lp->l[i] != ' ') {
			_amove(row, i);
			_aputc(' ');
		}
	}
	lp->len = col;
}

#else	/* HP version */

_clrtoeol(row, col) {
	_amove(row, col);
	if (_actual[row].len == col + 1)
		_aputc(' ');
	else
		_putstr(SCLRLINE);
	_actual[row].len = col;
}

#endif



_fixlines() {
	register struct line *lp;
	register char *p;
	register int i;

	for (i = 0 ; i < PAGLEN ; i++) {
		lp = &_virt[i];
		if (lp->flags & DIRTY) {
			lp = &_virt[i];
			for (p = &lp->l[lp->len] ; --p >= lp->l && *p == ' ' ; );
			lp->len = p + 1 - lp->l;
			if (lp->len == _actual[i].len && strncmp(lp->l, _actual[i].l, lp->len) == 0)
				lp->flags &=~ DIRTY;
		}
	}
}



_ckclrlin(i) {
	int eval;
	int len;
	int first;
	register struct line *vp, *ap;
	register int j;

	ap = &_actual[i];
	vp = &_virt[i];
	len = ap->len;
	eval = -2;
	if (len > vp->len) {
		len = vp->len;
		eval = 0;
	}
	for (j = 0 ; j < len && vp->l[j] == ap->l[j] ; j++);
	if (j == len)
		return;
	first = j;
	while (j < len) {
		if (vp->l[j] == ' ') {
			if (ap->l[j] != ' ') {
				while (++j < len
				    && vp->l[j] == ' ' && ap->l[j] != ' ') {
					eval++;
				}
				if (j == len)
					eval++;
				continue;
			}
		} else {
			if (vp->l[j] == ap->l[j]) {
				while (++j < len && vp->l[j] == ap->l[j]) {
					eval--;
				}
				continue;
			}
		}
		j++;
	}
	for (j = first ; --j >= 0 ; )
		if (vp->l[j] != ' ')
			break;
	if (j < 0)
		first = 0;
	if (eval > 0) {
		_amove(i, first);
		_putstr(SCLRLINE);
		_actual[i].len = first ;
	}
}



_amove(row, col) {
	char s[20];
	register int cost, i;

	if (row == _srow && col == _scol && _curjunked == 0)
		return;
	_setul(0);
	sprintf(s, "\033[%d;%dH", row+1, col+1);

	cost = strlen(s);
	if (_curjunked == 0 && (i = _rmcost(_srow, _scol, row, col)) < cost) {
		_relmove(s, _srow, _scol, row, col);
		cost = i;
	}
	if (_curjunked == 0 && col < _scol) {
		if (_scol < 72 || _srow <= row) {
			if ((i = _rmcost(_srow, 0, row, col) + 1) < cost) {
				s[0] = '\r';
				_relmove(s + 1, _srow, 0, row, col);
				cost = i;
			}
		} else {
			if ((i = _rmcost(_srow + 1, 0, row, col) + 1) < cost) {
				s[0] = '\t';
				_relmove(s + 1, _srow + 1, 0, row, col);
				cost = i;
			}
		}
	}
	if (row < cost && (i = _rmcost(0, 0, row, col) + HOMELEN) < cost) {
		strcpy(s, SHOME);
		_relmove(s + HOMELEN, 0, 0, row, col);
		cost = i;
	}
	_putstr(s);
	_srow = row, _scol = col;
	_curjunked = 0;
}



_rmcost(orow, ocol, nrow, ncol) {
	char s[200];

	_relmove(s, orow, ocol, nrow, ncol);
	return strlen(s);
}



_relmove(s, orow, ocol, nrow, ncol)
	char *s;
{
	register char *p, *q;
	register int i;

	p = s;

	if( (i = nrow - orow) != 0) {
		if( i < 0) {
			if( i == -1)
				for (q = SUP ; *p = *q++ ; p++);
			else {
				sprintf(p, "\033[%dA", -i); /* up */
				while(*p) p++;
			}
		}
		else if( i < 3) {
			while(i-- > 0)
				*p++ = '\n';
		}
		else {
			sprintf(p, "\033[%dB", i); /* down */
			while(*p) p++;
		}
	}

	if( (i = ncol - ocol) != 0) {
		if( i < 0) {
			i = -i;
			if( i < 4) 
				while(i-- > 0)
					*p++ = '\b';
			else {
				sprintf(p, "\033[%dD", i); /* left */
				while(*p) p++;
			}
		}
		else {
			if( i < 4) {
				register struct line *lp = &_actual[nrow];
				while (ocol < ncol) {
					if (ocol < lp->len)
						*p++ = lp->l[ocol];
					else
						*p++ = ' ';
					ocol++;
				}
			}
			else {
				sprintf(p, "\033[%dC", i); /* right */
				while(*p) p++;
			}
		}
	}
	*p++ = '\0';
}



_aputc(c) {
	_setul(c & ULINE);		
	putch(c &~ ULINE);
	if (++_scol > PAGWID) {
		_scol = 0;
		if (++_srow >= PAGLEN) {
			_srow = 0;
			_putstr(SHOME);
		}
	}
}


_setul(on) {
	if (on) {
		if (_uline == 0) {
			_putstr(ULON);
			_uline = 1;
		}
	} else {
		if (_uline != 0) {
			_putstr(ULOFF);
			_uline = 0;
		}
	}
}


_putstr(s)
	char *s;
	{
	register char *p;

	for (p = s ; *p ; p++) {
		putch(*p);
	}
}