#include <windows.h>
#include <ascii.h>
#include <dos.h>

/* (from SCREEN2.ASM) */

/* struct _cell far *cdecl toaddr(unsigned short, unsigned short);
struct _cell far *cdecl rptchar(struct _cell far *, char, char, unsigned short);
*/

/*
	Tom Jennings
	new:	18 Jun 90
	mru:	 8 Jan 92

NOTE: Compiled using PASCAL parameter passing convention.

Basic screen support routines, generally ones that address
the hardware directly, and other low-level routines.


box_window(w)
WINDOW *w;
	Draws the appropriate box around the window and adjusts the
	window size as indicated in the window structure.

void w_copy(w)
WINDOW *w;
	Copy the data from the screen "under" the window into the
	buffer for this window.

void w_cursor(w)
WINDOW *w;
	Display the cursor at the current writing point and sets the cursor
	shape.

void cursoroff()
	Remove the cursor.

void highlight(w)
void unhighlight(w)
WINDOW *w;
	Highlight or unhighlight the row in the window specified by line, col. 

void vhighlight(w,n)
	Highlight highlight starting at line/col, for
	N columns.

void w_dim(w)
void w_bright(w)
void w_reverse(w)
void w_unhighlight(w)
WINDOW *w;
	Window attribute controls. Dim changes all cells in the window to
	normal (low) intensity; brighten sets high intensity; highlight 
	reverses; unhighlight restores to window-default attribute.

void w_blink(w)
void w_unblink(w)
WINDOW *w;
	Sets/unsets the 'blink' bit in the window attribute so that
	subsequent writes to the window use it.

struct _cell far *toaddr(line,col)
short line,col;
	Convert line/column info to screen cell pointer.

void w_char(w,c)
WINDOW *w;
char c;
	Write a character in the window at the current line/column,
	and advance line/column. Columns wraps to 0 when the right edge
	is written, and the window scrolls up when the last cell is
	written. Processes control characters.

	Checks 'active' before writing.

void w_chara(w,c,a)
WINDOW *w;
char c,a;
	Same as w_char(), except writes the specified attribute.

void w_scrollup(w,l)
WINDOW *w;
int l;
	Scroll the window up one line starting at the given line.
	This does a 'delete line', clipping out the given line and
	scrolling all lines below it up one. The line at the bottom 
	is cleared.

void w_scrolldn(w,l)
WINDOW *w;
int l;
	Scroll the window down one line, starting at the given line.
	This does an 'insert line', shifting the line and all below it
	down one line. The new line is cleared.

*/

/* Draw the box around a window, and clear the text area. This is coded 
for speed. Most work is done with two pointers, and loops are coded to 
eliminate multiplies etc. */

void box_window(w)
WINDOW *w;
{
struct _cell far *p;			/* for filling/clearing window area */
struct _cell far *q;
char a;					/* attribute, from window struct (addr speed) */
char ul,ur,lr,ll,lvert,rvert,thoriz,bhoriz; /* box drawing characters */
register short i;
short j,k;

	p= toaddr(w-> top_line,w-> left_col);	/* precalc top corner ptr */
	a= w-> attribute;			/* save address calc time */
	w-> lines= w-> window_lines;		/* set text window size */
	w-> cols= w-> window_cols;		/* to full window */
	w-> line_off= w-> col_off= 0;

	switch (w-> box_type & W_BOXTYPEMASK) {
		case W_FEDGES:
			lvert= rvert= C_FBOXVERT;
			w-> cols -= 2;		/* edges consume two cols */
			w-> col_off= 1;
			goto vert_only;

		case W_EDGES:			/* thick edges only */
			lvert= C_THICKLEDGE;
edges:			rvert= C_THICKREDGE;
			w-> cols -= 2;		/* edges consume two cols */
			w-> col_off= 1;
			goto vert_only;

		case W_EDGES2:			/* oddball edges (assays) */
			lvert= C_THINVERT;
			goto edges;

		case W_DOUBLEBOX:
			ul= C_DBOXUL;		/* set boundary characters */
			ur= C_DBOXUR;
			lr= C_DBOXLR;
			ll= C_DBOXLL;
			thoriz= bhoriz= C_DBOXHORIZ;
			lvert= rvert= C_DBOXVERT;
twolc:			w-> lines -= 2; w-> line_off= 1;/* box consumes 2 lines */
			w-> cols -= 2; w-> col_off= 2;	/* and 2 columns */
			goto box_it;

		case W_HBARS:
			lvert= rvert= C_FBOXVERT;
bars:			ul= ur= thoriz= C_FBOXTOP; /* set boundary characters */
			ll= lr= bhoriz= C_FBOXBOT;
			w-> lines -= 2; w-> line_off= 1;/* box consumes 2 lines */
			w-> cols -= 2; w-> col_off= 1;	/* and 1 column */
			goto box_it;

		case W_NOTSIMBOX:
			lvert= rvert= 
			ul= ur= thoriz= 
			ll= lr= bhoriz= C_DOTS;
			w-> lines -= 2; w-> line_off= 1;/* box consumes 2 lines */
			w-> cols -= 2; w-> col_off= 1;	/* and 1 column */
			goto box_it;

		case W_SIMBOX:
			lvert= rvert= C_DOTS;
			w-> cols -= 2;		/* edges consume two cols */
			w-> col_off= 1;
			goto vert_only;

/* If box_type is W_???BOX, draw a box around the window. The box is within the
window -- hence it consumes two lines (top and bottom) and 2 columns (left 
and right edges, plus blank after left edge). col_off is 2, so that we don't 
put the first character of each line right after the box character. (Visually 
too tight.) */

/* Set the pointers to the start of the horizontal lines. Draw in the
left corners, then the horiz. lines, then with the pointers remaining,
the right corners. */

box_it:			q= p + ((w-> window_lines - 1) * COLUMNS); /* q= &bottom line */
			p-> character= ul;		/* UPPER LEFT corner, */
			p++-> attribute= a;
			q-> character= ll;		/* LOWER LEFT corner, */
			q++-> attribute= a;

			for (i= w-> cols; i--;) {	/* then top and bottom */
				p-> character= thoriz;	/* HORIZ BARS */
				q-> character= bhoriz;
				p++-> attribute= q++-> attribute= a;
			}				/* (points to last column) */
			p-> character= ur;		/* UPPER RIGHT corner, */
			p-> attribute= a;
			q-> character= lr;		/* LOWER RIGHT corner, */
			q-> attribute= a;

/* Draw the vertical bars and clear the text area in one pass. Set the 
pointer to the left edge, write the vert bar, then for each active cell
write the blank character. At the end of the line write the (right hand)
vertical bar. */
			p += (COLUMNS - w-> cols - 1);	/* p= 2nd line, left edge */

vert_only:		i= w-> lines;			/* # of lines */
			k= w-> cols;
			j= COLUMNS - k - 1;

			while (i--) {
				p-> character= lvert;	/* VERTICAL BAR left, */
				p++-> attribute= a;	/* note plus one */
				rptchar(p,' ',a,w-> cols); /* blank text area, */
				p += k;
				p-> character= rvert;	/* VERTICAL BAR right */
				p-> attribute= a;
				p += j;
			}

/* If FULLBOX is set, we make the active box be the whole box -- the
borders are no longer outside the active area. This is for the various
utility windows with drawn characters that reach the edges of the box. */

			if (w-> box_type & W_FULLBOX) {
				w-> lines= w-> window_lines;
				w-> cols= w-> window_cols;
				w-> line_off= w-> col_off= 0;
			}
			break;

/* No box type -- just a plain text window. There are no boundaries. */

		case 0:
			for (i= w-> lines, j= w-> cols; i--;) {
				rptchar(p,' ',a,j);
				p += COLUMNS;
			}
			break;

/* If not a specific box type, then assume it is a left-edge character. This
is explicit control over the left edge of the window, rather than the default 
action as in the box case. */

		default:
			--w-> cols; ++w-> col_off;	/* 1 col consumed by edge */
			for (i= w-> lines, j= w-> cols; i--;) {
				p-> character= w-> box_type;
				p-> attribute= a;	/* write the edge char */
				rptchar(p + 1,' ',a,j);
				p += COLUMNS;		/* clear text cells */
			}
			break;
	}
}

/* Copy the data from the window to the allocated buffer. */

void w_save(w)
WINDOW *w;
{
struct _cell far *p;
char far *cp;
register i;
int l;

	cp= w-> buff;					/* ptr to our buffer */
	for (l= 0; l < w-> window_lines; l++) {		/* for all lines... */
		p= toaddr(w-> top_line + l,w-> left_col); /* ptr to the line start */
		for (i= w-> window_cols; i--; ) {	/* save all cells/line */
			*cp++= p-> character;		/* both bytes */
			*cp++= p++-> attribute;		/* of each screen cell */
		}
	}
}

/* Restore the original screen contents under this window (for when we're
removing a window.) */

void w_restore(w)
WINDOW *w;
{
struct _cell far *p;
char far *cp;
register i;
int l;

	cp= w-> buff;					/* ptr to save buffer */
	for (l= 0; l < w-> window_lines; l++) {		/* for all lines... */
		p= toaddr(w-> top_line + l,w-> left_col); /* ptr to the line start */
		for (i= w-> window_cols; i--; ) {	/* save all cells/line */
			p-> character= *cp++;		/* restore character */
			p++-> attribute= *cp++;		/* and attribute */
		}
	}
	w-> cursor_height= w-> orig_height;
	w_cursor(w);					/* restore cursor shape */
}

/* Highlight (reverse) one line in a window. */

void highlight(w)
WINDOW *w;
{
struct _cell far *p;
int i;

	p= toaddr(w-> top_line + w-> line + w-> line_off,
	    w-> left_col + w-> col_off);

	for (i= w-> cols; i--;) 
		p++-> attribute= w-> def_attribute ^ (A_BBG | A_WBG);
}

/* Unhighlight one line in a window. */

void unhighlight(w)
WINDOW *w;
{
struct _cell far *p;
int i;

	p= toaddr(w-> top_line + w-> line + w-> line_off,
	    w-> left_col + w-> col_off);

	for (i= w-> cols; i--;) 
		p++-> attribute= w-> def_attribute;
}

/* Highlight starting at active_line/active_col for N columns. */

void vhighlight(w,n)
WINDOW *w;
int n;
{
struct _cell far *p;

	p= toaddr(w-> top_line + w-> line + w-> line_off,
	    w-> left_col + w-> col + w-> col_off);

	while (n--)
		p++-> attribute= w-> attribute ^ (A_BBG | A_WBG);
}

/* Dim out a window. */

void w_dim(w)
WINDOW *w;
{
	w_attr(w,w-> attribute= A_BRIGHT);
}

/* Brighten a window. */

void w_bright(w)
WINDOW *w;
{
	w_attr(w,w-> attribute= w-> def_attribute | A_BRIGHT);
}

/* Highlight (reverse vid) or unhighlight (normal) a window. */

void w_reverse(w)
WINDOW *w;
{
	w_attr(w,w-> attribute= w-> def_attribute ^ (A_BBG | A_WBG));
}

void w_unhighlight(w)
WINDOW *w;
{
	w_attr(w,w-> attribute= w-> def_attribute);
}

/* Set the attributes of all charcter positions in the window. */

void w_attr(w,a)
WINDOW *w;
char a;			/* attribute */
{
struct _cell far *p;
register i;
int l;

	for (l= 0; l < w-> window_lines; l++) {		/* for all lines, */
		p= toaddr(w-> top_line + l,w-> left_col);
		for (i= w-> window_cols; i--;) 
			p++-> attribute= a;
	}
}

/* Write a character in a window at the current line/column, and advance
line/column. Wrap at line ends, and scroll lines up when we write the last 
cell. Keeps text within the box or left edge, if present. */

void w_char(w,c)
WINDOW *w;
char c;
{
	w_charx(w,c,w-> attribute,1);
}

/* Write a character in a window at the current line/column, and advance
line/column. Wrap at line ends, and scroll lines up when we write the last 
cell. Keeps text within the box or left edge, if present. */

void w_chara(w,c,a)
WINDOW *w;
char c,a;
{
	w_charx(w,c,w-> attribute,1);
}

/* As above, except no processing of control characters. */

void w_charal(w,c,a)
WINDOW *w;
char c,a;
{
	w_charx(w,c,w-> attribute,0);
}

/* Do the character write as above. Allow literally printable characters. */

static w_charx(w,c,a,l)
WINDOW *w;
char c,a,l;
{
struct _cell far *p;

	if (! (w-> bits & W_ACTIVE)) return;	/* MUST BE ACTIVE! */

	if (l) switch (c) {
		case CR: w-> col= 0; return;
		case LF: if (++w-> line >= w-> lines) w-> line= 0; return;
		case BS: if (w-> col) --w-> col; return;
	}
	if (w-> col >= w-> cols) {		/* if we're at end of line */
		w-> col= 0; 			/* wrap to col 0, next line */
		if (++w-> line >= w-> lines) 	/* if past the end */
			w-> line= 0;		/* wrap to the beginning */
	}
	p= toaddr(w-> top_line + w-> line + w-> line_off,
	    w-> left_col + w-> col + w-> col_off);
	p-> character= c;
	p-> attribute= a;
	++w-> col;				/* advance to next column */
}

/* Scroll the window up one line between the given lines (delete line) */

void w_scrollup(w,top,bot)
WINDOW *w;
int top,bot;	/* top == top of screen, lower number */
{
char far *fp;
char far *tp;
register i;

	for ( ; top < bot; top++) {
		tp= (char far *) toaddr(		/* tp= & line */
		    w-> top_line + w-> line_off + top,
		    w-> left_col + w-> col_off);
		fp= tp + (COLUMNS * sizeof(struct _cell)); /* fp= & line + 1 */

		i= w-> window_cols * sizeof(struct _cell);
		while (i--) *tp++= *fp++;		/* copy BYTES */
	}
	rptchar(toaddr(
	    w-> top_line + w-> line_off + bot,
	    w-> left_col + w-> col_off),
	    ' ',w-> attribute,w-> cols);		/* blank bottom line, */
}

/* Scroll the window down one line starting at the given line (insert line). */

void w_scrolldn(w,top,bot)
WINDOW *w;
int top,bot;	/* top == top of screen, lower number */
{
char far *fp;
char far *tp;
register i;

	for ( ; bot > top; bot--) {
		tp= (char far *) toaddr(		/* tp= & line */
		    w-> top_line + w-> line_off + bot,
		    w-> left_col + w-> col_off);
		fp= tp - (COLUMNS * sizeof(struct _cell)); /* fp= & line - 1 */

		i= w-> window_cols * sizeof(struct _cell);
		while (i--) *tp++= *fp++;		/* copy BYTES */
	}
	rptchar(toaddr(
	    w-> top_line + w-> line_off + top,
	    w-> left_col + w-> col_off),
	    ' ',w-> attribute,w-> cols);		/* blank top line, */
}

/* Clear to the end of the current line. */

void w_clreol(w)
WINDOW *w;
{
	rptchar(toaddr(w-> top_line + w-> line + w-> line_off,
	    w-> left_col + w-> col + w-> col_off)
	    ,' ',w-> attribute,w-> cols - w-> col);
}

/* Display the cursor at the current writing point. We calculate the screen
cell address relative to the start of the screen, then write it to the 
video controller, and set the cursor shape. */

void w_cursor(w)
WINDOW *w;
{
union REGS reg;

	reg.h.dh= w-> top_line + w-> line + w-> line_off; /* line number */
	reg.h.dl= w-> left_col + w-> col + w-> col_off;	/* column number */
	reg.h.bh= 0;				/* page number */
	reg.h.ah= 2;				/* SET CURSOR POSITION */
	int86(0x10,&reg,&reg);			/* make the INT 10h call */

	reg.h.ch= CURSOR_BOTTOM - w-> cursor_height; 
	reg.h.cl= CURSOR_BOTTOM;
	reg.h.ah= 1;				/* SET CURSOR TYPE */
	int86(0x10,&reg,&reg);
}

/* Disable the cursor -- put it past the last line. */

void cursoroff() {
union REGS reg;

	reg.h.dh= 50;				/* line number (off screen) */
	reg.h.dl= 0;				/* column number */
	reg.h.bh= 0;				/* page number */
	reg.h.ah= 2;				/* SET CURSOR POSITION */
	int86(0x10,&reg,&reg);			/* make the INT 10h call */
}
