/*
 * Windows H19 Terminal Emulator
 * 
 * Written by William S. Hall
 *	      3665 Benton Street, #66
 *	      Santa Clara, CA 95051
 *
 * Terminal and status window module
 */

#define NOKANJI
#define NOATOM
#include <string.h>
#include <windows.h>

#include "winasc.h"
#include "winh19.h"
#include "win19d.h"

/* local functions */
static void NEAR MoveCursorLeft(PTWND pTW, WORD count);
static void NEAR MoveCursorRight(PTWND pTW, WORD count);
static void NEAR SetScroll(void);
static void NEAR MoveCursorUp(PTWND pTW, WORD count);
static void NEAR MoveCursorDown(PTWND pTW, WORD count);
static void NEAR PositionCursorRow(PTWND, short);
static void NEAR PositionCursorCol(PTWND, short);
static void NEAR CursorHome(PTWND);
static void NEAR DeleteChar(PTWND, WORD);
static void NEAR ClearStatusLine(PTWND);
static void NEAR ClearTermWindow(PTWND);
static void NEAR SlapScreenToClip(PTWND);
static void NEAR SetAttrib(HDC, int);
static void NEAR H19HideCaret(HWND hWnd);
static void NEAR H19ShowCaret(HWND hWnd);
static void NEAR SaveCursor(short line, short column);
static void NEAR GetCursor(short lines, short cols, LONG lParam);
static int NEAR H19TerminalDisplay(PTWND, BYTE *, short);
static void NEAR DoCR(PTWND);
static void NEAR DoBS(PTWND);
static void NEAR MakeCaret(PTWND);
static void NEAR DoHT(PTWND);
static void NEAR ClearToEndOfLine(PTWND);
static void NEAR ClearToEndOfScreen(PTWND);
static void NEAR StatWndPaint(PTWND, LPPAINTSTRUCT);
static void NEAR ClearToScreenTop(PTWND);
static void NEAR ClearToLineStart(PTWND);
static void NEAR EraseEntireLine(PTWND);
static void NEAR TermWndPaint(PTWND, LPPAINTSTRUCT);
static void NEAR AdjustWindowToCursor(PTWND);
static void NEAR ShowLineOfText(HDC, short, short, BYTE *, short, short);
static void NEAR InsertLine(PTWND, WORD);
static void NEAR DeleteLine(PTWND, WORD);
static void NEAR ReverseLF(PTWND);
static void NEAR DoLF(PTWND);

/* status window proc */
long FAR PASCAL StatWndProc(hWnd,message,wParam,lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{

    PAINTSTRUCT ps;
    int count;

    register PTWND pSW = (PTWND)GetWindowWord(hWnd, 0);

    switch(message) {
	case WH19_COMMAND:
	    if (CD.StatOpen) {
	        H19HideCaret(hWnd);
	        switch (wParam) {
		    case H19_MOVECURSORRIGHT:
			MoveCursorRight(pSW, LOWORD(lParam));
		        break;
		    case H19_MOVECURSORLEFT:
                        MoveCursorLeft(pSW, LOWORD(lParam));
		        break;
		    case H19_POSITIONCURSORCOL:
		        PositionCursorCol(pSW, (short)LOWORD(lParam));
		        break;
		    case H19_DELETECHAR:
		        DeleteChar(pSW, LOWORD(lParam));
		        break;
		    case H19_CLRSCREEN:
		        ClearStatusLine(pSW);
		        break;
		    case H19_CLRTOENDOFLINE:
		    case H19_CLRTOENDOFSCREEN:
		        ClearToEndOfLine(pSW);
		        break;
		    case H19_CLRTOSTARTOFLINE:
		    case H19_CLRTOTOPOFSCREEN:
		        ClearToLineStart(pSW);
			break;
		    case H19_INSERTLINE:
		    case H19_DELETELINE:
		        pSW->Xpos = pSW->CurLineOffset = 0;
		    case H19_ERASELINE:
		        EraseEntireLine(pSW);
		        break;
	        }
	        SetCaretPos(pSW->Xpos, pSW->CharHeight);
	        H19ShowCaret(hWnd);
	    }
	    break;

	case WH19_CARETFUNCTION:
	    switch(wParam) {	
		case H19_SHOWCARET:
	            H19ShowCaret(hWnd);
		    break;

	        case H19_HIDECARET:
		    H19HideCaret(hWnd);
	            break;

		case H19_CREATECARET:
		    if (lParam) {
	                MakeCaret(pSW);
			SetCaretPos(pSW->Xpos, pSW->Ypos + pSW->CharHeight);
			if (CD.StatOpen)
			    H19ShowCaret(hWnd);
	            }
	            break;

		case H19_DESTROYCARET:
		    if (lParam) {
			H19HideCaret(hWnd);
			DestroyCaret();
		    }
		    break;
	    }
	    break;

	case WH19_STRINGINPUT:
	    count = 0;
	    if (CD.StatOpen) {
	        H19HideCaret(hWnd);
		count = H19TerminalDisplay(pSW, (BYTE *)LOWORD(lParam),
							(short)wParam);
		SetCaretPos(pSW->Xpos, pSW->CharHeight);
		H19ShowCaret(hWnd);
	    }
	    return((LONG)count);
	    
        case WH19_CURSORPOSITION:
	    if (wParam == H19_SAVECURSOR)
	        SaveCursor(TW.MaxLines, pSW->CurLineOffset);
	    else
		GetCursor(TW.MaxLines, pSW->CurLineOffset, lParam);
	    break;

	case WM_CREATE:
	    TermWndCreate(hWnd, lParam);
	    break;

	case WM_PAINT:
	    BeginPaint(hWnd, (LPPAINTSTRUCT)&ps);
            if (!IsRectEmpty((LPRECT)&ps.rcPaint))
	        StatWndPaint(pSW, (LPPAINTSTRUCT)&ps);
	    EndPaint(hWnd, (LPPAINTSTRUCT)&ps);
	    break;

	default:
	    return ((long)DefWindowProc(hWnd,message,wParam,lParam));
    }
    return(0L);
}

/* terminal window procedure */
long FAR PASCAL TermWndProc(HWND hWnd,unsigned message,WORD wParam,LONG lParam)
{

    PAINTSTRUCT ps;
    short lines;
    int count;

    register PTWND pTW = (PTWND)GetWindowWord(hWnd, 0);

    switch(message) {

	case WH19_COMMAND:
	    H19HideCaret(hWnd);
	    switch (wParam) {
		case H19_MOVECURSORRIGHT:
		    MoveCursorRight(pTW, LOWORD(lParam));
		    break;
		case H19_MOVECURSORLEFT:
                    MoveCursorLeft(pTW, LOWORD(lParam));
		    break;
		case H19_MOVECURSORUP:
		    MoveCursorUp(pTW, LOWORD(lParam));
		    break;
		case H19_MOVECURSORDOWN:
		    MoveCursorDown(pTW, LOWORD(lParam));
		    break;
		case H19_POSITIONCURSORROW:
		    PositionCursorRow(pTW, (short)LOWORD(lParam));
		    break;
		case H19_POSITIONCURSORCOL:
		    PositionCursorCol(pTW, (short)LOWORD(lParam));
		    break;
		case H19_ADJUSTWINDOW:
		    AdjustWindowToCursor(pTW);
		    break;
		case H19_CURSORHOME:
		    CursorHome(pTW);
		    break;
		case H19_DELETECHAR:
		    DeleteChar(pTW, LOWORD(lParam));
		    break;
		case H19_CLRSCREEN:
		    ClearTermWindow(pTW);
		    break;
		case H19_CLRTOENDOFSCREEN:
		    ClearToEndOfScreen(pTW);
		case H19_CLRTOENDOFLINE:
		    ClearToEndOfLine(pTW);
		    break;
		case H19_CLRTOTOPOFSCREEN:
		    ClearToScreenTop(pTW);
		case H19_CLRTOSTARTOFLINE:
		    ClearToLineStart(pTW);
		    break;
		case H19_ERASELINE:
		    EraseEntireLine(pTW);
		    break;
		case H19_REVERSELINEFEED:
		    ReverseLF(pTW);
		    break;
		case H19_INSERTLINE:
		    InsertLine(pTW, LOWORD(lParam));
		    break;
		case H19_DELETELINE:
		    DeleteLine(pTW, LOWORD(lParam));
		    break;
	    }
	    SetCaretPos(pTW->Xpos, pTW->Ypos + pTW->CharHeight);
	    H19ShowCaret(hWnd);
	    break;

	case WH19_CARETFUNCTION:
	    switch(wParam) {	
		case H19_SHOWCARET:
	            H19ShowCaret(hWnd);
		    break;

	        case H19_HIDECARET:
		    H19HideCaret(hWnd);
	            break;

		case H19_CREATECARET:
		    if (lParam) {
			AdjustWindowToCursor(pTW);
			MakeCaret(pTW);
			SetCaretPos(pTW->Xpos, pTW->Ypos + pTW->CharHeight);
			H19ShowCaret(hWnd);
		    }
		    break;

		case H19_DESTROYCARET:
		    if (lParam) {
			H19HideCaret(hWnd);
			DestroyCaret();
		    }
		    break;
	    }
	    break;

	case WH19_STRINGINPUT:
	    H19HideCaret(hWnd);
	    count = H19TerminalDisplay(pTW, (BYTE *)LOWORD(lParam),
							(short)wParam);
	    SetCaretPos(pTW->Xpos, pTW->Ypos + pTW->CharHeight);
	    H19ShowCaret(hWnd);
	    return((LONG)count);
	    
        case WH19_CURSORPOSITION:
	    lines = (pTW->oCurrentLine - pTW->oTopLine) / pTW->MaxCols;
	    if (lines < 0)
		lines += pTW->MaxLines;
	    if (wParam == H19_SAVECURSOR)
	        SaveCursor(lines, pTW->CurLineOffset);
	    else
	        GetCursor(lines, pTW->CurLineOffset, lParam);
	    break;

	case WH19_SLAPSCREEN:
	    SlapScreenToClip(pTW);
	    break;

	case WM_CREATE:
	    TermWndCreate(hWnd, lParam);
	    break;

	case WM_PAINT:
	    BeginPaint(hWnd, (LPPAINTSTRUCT)&ps);
            if (!IsRectEmpty((LPRECT)&ps.rcPaint))
	        TermWndPaint(pTW, (LPPAINTSTRUCT)&ps);
	    EndPaint(hWnd, (LPPAINTSTRUCT)&ps);
	    break;

	default:
	    return ((long)DefWindowProc(hWnd,message,wParam,lParam));
    }
    return(0L);
}

/* copy screen to clipboard */
static void NEAR SlapScreenToClip(PTWND pTW)
{

    short cols = pTW->MaxCols;
    short lines = pTW->MaxLines;
    HANDLE hGlob = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 
					(LONG)((cols + 2) * lines + 2));

    if (hGlob != NULL) {
	RECT rect;
	BYTE *pTop = pTW->pVidBuffer;
	BYTE *lineptr = pTop + pTW->oTopLine;
	BYTE *pEnd = pTop + pTW->oVidLastLine;
	BYTE FAR *pGlob = GlobalLock(hGlob);
        short count;
	register int i, j;
        BYTE ch;
	BYTE *pSource;

	GetClientRect(pTW->hWnd, &rect);
	InvertRect(pTW->hDC, &rect);
        for (i = 0; i < lines; i++) {
	    count = cols;
	    while ((*(lineptr + count - 1) == SP) && (count > 0))
		count--;
            pSource = lineptr;
            for (j = 0; j < count; j++) {
		ch = (*pSource++) & 0x7f;
		if (ch == DEL)
		    ch -= 1;
		else if (ch < SP)
		    ch += '^';
		*pGlob++ =  ch;
	    }
	    *pGlob++ = '\r';
	    *pGlob++ = '\n';
	    if ((lineptr += cols) > pEnd)
    	        lineptr = pTop;
        }
	*pGlob = NUL;
	GlobalUnlock(hGlob);
	if (OpenClipboard(pTW->hWnd)) {
	    EmptyClipboard();
	    SetClipboardData(CF_TEXT, hGlob);
	    CloseClipboard();
	}
	else
   	    GlobalFree(hGlob);
	InvertRect(pTW->hDC, &rect);
    }
}

/* update the status window */
static void NEAR StatWndPaint(PTWND pSW, LPPAINTSTRUCT ps)
{

    BYTE *pBuf = pSW->pVidBuffer;
    short xpos = 0;
    short count = pSW->MaxCols;
    register short left = ps->rcPaint.left;
    register short right = pSW->Width - ps->rcPaint.right;
    short cwidth = pSW->CharWidth;
    short countchange;

    if (ps->rcPaint.top < pSW->CharHeight) {
	if (left >= cwidth) {
	    xpos = left - left % cwidth;
	    countchange = left / cwidth;
	    pBuf += countchange;
	    count -= countchange;
	}
	if (right >= cwidth)
	    count -= right / cwidth;
	ShowLineOfText(ps->hdc,xpos,0,pBuf,count,cwidth);
    }
}

/* update the terminal window */
static void NEAR TermWndPaint(PTWND pTW, LPPAINTSTRUCT ps)
{

    register int i;
    BYTE *pTop = pTW->pVidBuffer;
    BYTE *lineptr = pTop + pTW->oTopLine;
    BYTE *pEnd = pTop + pTW->oVidLastLine;
    short cwidth = pTW->CharWidth;
    short cheight = pTW->CharHeight;
    short line1 = 0;
    short line2 = pTW->MaxLines;
    short cols = pTW->MaxCols;
    short count = cols;
    register short base = 0;
    short left = ps->rcPaint.left;
    short right = pTW->Width - ps->rcPaint.right;
    short top = ps->rcPaint.top;
    short bottom = pTW->Height - ps->rcPaint.bottom;

    short xpos = 0;
    short offset = 0;
    short countchange;

    if (left >= cwidth) {
        xpos = left - left % cwidth;
	countchange = left / cwidth;
	offset += countchange;
	count -= countchange;
    }

    if (right >= cwidth) 
        count -= right / cwidth;

    if (top >= cheight) {
	line1 = top / cheight;
	base += line1 * cheight;
	lineptr += line1 * cols;
	if (lineptr > pEnd)
	    lineptr = pTop + (lineptr - pEnd) - cols;
    }

    if (bottom >= cheight)
	line2 -= bottom / cheight;

    for (i = line1; i < line2; i++) {
	ShowLineOfText(ps->hdc,xpos,base,lineptr+offset,count,cwidth);
        base += cheight;
	if ((lineptr += cols) > pEnd)
    	    lineptr = pTop;
    }
}

/* display a line of text on terminal or status window */
static void NEAR ShowLineOfText(hDC, Xpos, Ypos, lineptr, linewidth, charwidth)
HDC hDC;
short Xpos, Ypos;
BYTE *lineptr;
short linewidth, charwidth;
{

    register int i,j;
    short X;
    BYTE *sptr, *eptr, ch;
    BYTE pBuf[TERMMAXCOLS];

    i = 0;
    X = Xpos;
    sptr = eptr = lineptr;

    while (i < linewidth) {
	for (j = 0; j < linewidth - i; j++)
	    if (*eptr++ > DEL)
		break;
	SetAttrib(hDC, NORMAL);
	TextOut(hDC,X,Ypos,(LPSTR)sptr, j);
	if ((i += j) >= linewidth)
	    break;
	sptr = --eptr;
	X += j * charwidth;
	for (j = 0; j < linewidth - i; j++)
	    if ((ch = *eptr++) <= DEL)
		break;
	    else
		pBuf[j] = ch & 0x7f;
	SetAttrib(hDC, REVERSE);
	TextOut(hDC,X,Ypos,(LPSTR)pBuf,j);
	if ((i += j) < linewidth) {
	    X += j * charwidth;
	    sptr = --eptr;
	}
    }
}

/* cursor support routines */
static void NEAR MoveCursorRight(PTWND pTW, WORD count)
{

    register short *offset = &pTW->CurLineOffset;
    register short *xpos = &pTW->Xpos;
    short cols = pTW->MaxCols - 1;
    short width = pTW->CharWidth;

    while ((*offset < cols) && (count--)) {
	*offset += 1;
	*xpos += width;
    }
}

static void NEAR MoveCursorLeft(PTWND pTW, WORD count)
{

    register short *offset = &pTW->CurLineOffset;
    register short *xpos = &pTW->Xpos;
    short width = pTW->CharWidth;

    while((*offset > 0) && (count--)) {
        *offset -= 1;
        *xpos -= width;
    }
}

static void NEAR MoveCursorUp(PTWND pTW, WORD count)
{
    register short *oCurLine = &pTW->oCurrentLine;
    short top = pTW->oTopLine;

    if (*oCurLine != top) {
        register short *ypos = &pTW->Ypos;
        short cols  = pTW->MaxCols;
	short cheight  = pTW->CharHeight;

	while ((*oCurLine != top) && (count--)) {
	    *ypos -= cheight;
	    if ((*oCurLine -= cols) < 0)
	        *oCurLine = pTW->oVidLastLine;
        }
        AdjustWindowToCursor(pTW);
    }
}

static void NEAR MoveCursorDown(PTWND pTW, WORD count)
{
    register short *oCurLine = &pTW->oCurrentLine;
    short bottom = pTW->oBottomLine;

    if (*oCurLine != bottom) {
	register short *ypos = &pTW->Ypos;
	short cols  = pTW->MaxCols;
	short cheight  = pTW->CharHeight;
	short last =  pTW->oVidLastLine;

	while ((*oCurLine != bottom) && (count--)) {
	    *ypos += cheight;
	    if ((*oCurLine += cols) > last)
	        *oCurLine = 0;
	}
        AdjustWindowToCursor(pTW);
    }
}

/* position window so that cursor is visible */
static void NEAR AdjustWindowToCursor(PTWND pTW)
{

    POINT point;
    HWND hWnd = pTW->hWnd;
    short ypos = pTW->Ypos;
    short width = pTW->Width;
    short cheight = pTW->CharHeight;
    register short Y1 = MW.SCTopTextLine;
    register short Y2 = MW.SCBottomTextLine;
    short Y;

    if ((CD.StatOpen) && (CD.StatOverlayTerm))
	Y2 -= cheight;

    point.x = pTW->Xpos;
    point.y = ypos;

    ClientToScreen(hWnd, (POINT FAR *)&point);
    Y = point.y;

    if (Y < Y1) {
	MoveWindow(hWnd,0,-ypos, width, pTW->Height, FALSE);
	ScrollWindow(hWnd,0,Y1-Y, (LPRECT)NULL, (LPRECT)NULL);
	UpdateWindow(hWnd);
    }
    else if (Y > Y2) {
	MoveWindow(hWnd, 0, Y2-Y1-ypos, width, pTW->Height, FALSE);
	ScrollWindow(hWnd,0,Y2-Y, (LPRECT)NULL, (LPRECT)NULL);
	UpdateWindow(hWnd);
    }   
}

/* display a string */
static int NEAR H19TerminalDisplay(PTWND pTW, BYTE *str, short len)
{

    register BYTE *ptr;
    register short ctr;
    short toff, txpos;
    BYTE *tBuf;
    BYTE savebuf[TERMMAXCOLS];

    HWND hWnd = pTW->hWnd;
    HDC hDC = pTW->hDC;
    short cols = pTW->MaxCols;
    short cwidth = pTW->CharWidth;
    short width = pTW->Width;
    BYTE *pBuf = pTW->pVidBuffer;
    short more_than_one_line = pTW->MaxLines - 1;

    while (len) {
	ptr = str;
	ctr = 0;
	tBuf = pBuf + pTW->oCurrentLine;
        toff = pTW->CurLineOffset;
        txpos = pTW->Xpos;

	while ((*ptr >= SP) && (*ptr < DEL)) {
	    if ((len) && (toff < cols)) {
	        if (CD.GraphicsMode)
		    if (*ptr == '~')
		        *ptr += 1;		
		    else if (*ptr >= '^')
		        *ptr -= '^';
		if (CD.ICToggle)
		    savebuf[ctr] = *(tBuf + toff);
		*(tBuf + toff++) = *ptr++ | CD.CharAttribute;
	        ctr += 1;
		txpos += cwidth;
	        len -= 1;
	    }
	    else
		break;
	}
        if (ctr) {
	    if (CD.ICToggle) {
		RECT rect;
		short ypos = pTW->Ypos;
		short xpos = pTW->Xpos;
		short tomove, tocopy;
		tomove =  cols - toff -ctr;
		tocopy = min (ctr, cols - toff);
		if (tomove > 0) {		
		    memmove(tBuf + toff + ctr, tBuf + toff, tomove);
		if (tocopy > 0)
		    strncpy(tBuf + toff, savebuf, tocopy);
		}
		SetRect(&rect, xpos, ypos, width, ypos + pTW->CharHeight);
		ScrollWindow(hWnd, txpos - xpos, 0, &rect, &rect);
		UpdateWindow(hWnd);
	    }
	    else {
	        if (CD.InverseVideo)
		    SetAttrib(hDC, REVERSE);
	        else
		    SetAttrib(hDC, NORMAL);
                TextOut(hDC, pTW->Xpos, pTW->Ypos, (LPSTR)str, ctr);
	    }
	    if (toff < cols) {
		pTW->CurLineOffset = toff;
		pTW->Xpos = txpos;
	    }
	    else {
		if (CD.WrapAround) {
		    DoCR(pTW);
		    if (more_than_one_line)
			DoLF(pTW);
		}
		else {
		    pTW->CurLineOffset = cols - 1;
		    pTW->Xpos = width - cwidth;
		}
	    }
	}
	while ((*ptr < SP) || (*ptr >= DEL)) {
	    if (len) {
	        switch (*ptr) {
		    case BEL:
		        MessageBeep(0);
		        break;
		    case HT:
			DoHT(pTW);
			break;
		    case BS:
			DoBS(pTW);
			break;
		    case LF:
			if (CD.CRonLF)
			    DoCR(pTW);
			if (more_than_one_line)
			    DoLF(pTW);
		        break;
		    case CR:
			DoCR(pTW);
			if (CD.HoldScreen && 
				(pTW->oCurrentLine == pTW->oBottomLine)) {
			    if (CD.ReleaseCount == 0) {
			        SetScroll();
			        return(len > 0 ? len - 1 : 0);
			    }
			    else
				CD.ReleaseCount -= 1;
			}
			if (CD.LFonCR)
			    if (more_than_one_line)
			        DoLF(pTW);
		        break;
	        }
	        len -= 1;
	        ptr++;
	    }
	    else
		break;
	}
        str = ptr;
    }
    return(len);
}

/* hold screen support */
static void NEAR SetScroll()
{

    BYTE keystate[256];

    GetKeyboardState((LPSTR)keystate);
    keystate[VK_SCROLL] |= 1;
    SetKeyboardState((LPSTR)keystate);
    CD.ScrollLock = TRUE;

}

/* control character modules */
static void NEAR DoLF(PTWND pTW)
{

    HWND hWnd = pTW->hWnd;
    register short cols = pTW->MaxCols;
    register oEnd = pTW->oVidLastLine;

    if ( (pTW->oCurrentLine += cols) > oEnd)
	pTW->oCurrentLine = 0;

    if (pTW->oCurrentLine == pTW->oTopLine) {
	if ((pTW->oTopLine += cols) > oEnd)
	    pTW->oTopLine = 0;
	if ((pTW->oBottomLine += cols) > oEnd)
	    pTW->oBottomLine = 0;
	memset(pTW->pVidBuffer + pTW->oCurrentLine, SP, cols);
	ScrollWindow(hWnd,0,-pTW->CharHeight, (LPRECT)NULL, (LPRECT)NULL);
	UpdateWindow(hWnd);
    }
    else
	pTW->Ypos += pTW->CharHeight;

    AdjustWindowToCursor(pTW);

}

static void NEAR DoCR(PTWND pTW)
{

    pTW->Xpos = pTW->CurLineOffset = 0;
}

static void NEAR DoBS(PTWND pTW)
{
    if (pTW->CurLineOffset) {
	pTW->CurLineOffset -= 1;
	pTW->Xpos -= pTW->CharWidth;
    }
}

static void NEAR DoHT(PTWND pTW)
{

    register short offset = pTW->CurLineOffset;

    if (offset < pTW->MaxCols - 1) {
	short charwidth = pTW->CharWidth;
	short limit = pTW->TabLimit;
	register short xpos = pTW->Xpos;
        do {
	    xpos += charwidth;
        } while((++offset % DEF_TABSTOP != 0) && (offset < limit));
        pTW->Xpos = xpos;
	pTW->CurLineOffset = offset;
    }
}

/* caret creation and deletion routines */
static void NEAR MakeCaret(PTWND pTW)
{

    register short height;

    if (CD.BlockCursor)
	height = -pTW->CharHeight;
    else
	height = -2;

    CreateCaret(pTW->hWnd, (HBITMAP)NULL, pTW->CharWidth, height);
}

static void NEAR H19ShowCaret(HWND hWnd)
{

    if (!CD.CursorOff)
	ShowCaret(hWnd);
}

static void NEAR H19HideCaret(HWND hWnd)
{

    if (!CD.CursorOff)
	HideCaret(hWnd);

}

/* set text attributes */
static void NEAR SetAttrib(HDC hDC, int type)
{

    switch (type) {
        case NORMAL:
	    SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
	    SetBkColor(hDC, GetSysColor(COLOR_WINDOW));
	    break;
	case REVERSE:
            SetTextColor(hDC, GetSysColor(COLOR_WINDOW));
	    SetBkColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
	    break;
    }
}

/* cursor positioning routines */
static void NEAR PositionCursorRow(PTWND pTW, short y)
{

    register short cols = pTW->MaxCols;
    register short oCurr = pTW->oTopLine;

    pTW->Ypos = y * pTW->CharHeight;
    oCurr += y * cols;
    if (oCurr > pTW->oVidLastLine)
        oCurr -= pTW->ScreenSize;
    pTW->oCurrentLine = oCurr;
    AdjustWindowToCursor(pTW);
}

static void NEAR PositionCursorCol(PTWND pTW, short x)
{
/*    
    if (x >= pTW->MaxCols)
	x = pTW->MaxCols - 1;
*/
    pTW->Xpos = x * pTW->CharWidth;
    pTW->CurLineOffset = x;

}

static void NEAR CursorHome(PTWND pTW)
{

    pTW->Xpos = pTW->Ypos = pTW->CurLineOffset = 0;
    pTW->oCurrentLine = pTW->oTopLine;
    AdjustWindowToCursor(pTW);

}

/* character deletion */
static void NEAR DeleteChar(PTWND pTW, WORD count)
{

    RECT rect;
    short cols = pTW->MaxCols;
    short offset = pTW->CurLineOffset;
    BYTE *dest = pTW->pVidBuffer + pTW->oCurrentLine + offset;
    HWND hWnd = pTW->hWnd;
    short ypos = pTW->Ypos;

    register short copycount;
    register short maxdelchars;

    if (count > (maxdelchars = cols - offset))
	count = maxdelchars;
    copycount = maxdelchars - count;
    memmove(dest, dest + count, copycount);
    memset(dest+copycount, SP, count);

    SetRect(&rect, pTW->Xpos, ypos, pTW->Width, ypos + pTW->CharHeight);
    ScrollWindow(hWnd,-pTW->CharWidth * count,0, &rect, &rect);
    UpdateWindow(hWnd);
}

/* clear screen routines */
static void NEAR ClearStatusLine(PTWND pSW)
{

    RECT rect;
    register HWND hWnd = pSW->hWnd;

    memset(pSW->pVidBuffer, SP, pSW->MaxCols);
    SetRect((LPRECT)&rect,0,0,pSW->Width,pSW->CharHeight);
    InvalidateRect(hWnd, (LPRECT)&rect, TRUE);
    UpdateWindow(hWnd);
}

static void NEAR ClearTermWindow(PTWND pTW)
{

    RECT rect;
    register HWND hWnd = pTW->hWnd;

    memset(pTW->pVidBuffer, SP, pTW->ScreenSize);
    pTW->Xpos = pTW->Ypos = pTW->CurLineOffset = 0;
    pTW->oCurrentLine = pTW->oTopLine = 0;
    pTW->oBottomLine = pTW->oVidLastLine;

    SetRect((LPRECT)&rect,0,0,pTW->Width,pTW->Height);
    InvalidateRect(hWnd, (LPRECT)&rect, TRUE);
    UpdateWindow(hWnd);

    AdjustWindowToCursor(pTW);

}

static void NEAR ClearToEndOfLine(PTWND pTW)
{

    RECT rect;
    HWND hWnd = pTW->hWnd;
    register offset = pTW->CurLineOffset;
    register char *src = pTW->pVidBuffer + pTW->oCurrentLine + offset;

    memset(src, SP, pTW->MaxCols - offset);
    SetRect(&rect, pTW->Xpos, pTW->Ypos, pTW->Width, pTW->Ypos+pTW->CharHeight);
    InvalidateRect(hWnd, (LPRECT)&rect, TRUE);
    UpdateWindow(hWnd);

}

static void NEAR ClearToEndOfScreen(PTWND pTW)
{

    RECT rect;

    char *pBuf = pTW->pVidBuffer;
    char *pEnd = pBuf + pTW->oVidLastLine;
    char *pBottom = pBuf + pTW->oBottomLine;
    register char *base = pBuf + pTW->oCurrentLine;
    register short cols = pTW->MaxCols;

    do {
        if ((base += cols) > pEnd)
	    base = pBuf;
	memset(base, SP, cols);
    } while (base != pBottom);

    SetRect(&rect, 0, pTW->Ypos + pTW->CharHeight, pTW->Width, pTW->Height);
    InvalidateRect(pTW->hWnd, (LPRECT)&rect, TRUE);
    UpdateWindow(pTW->hWnd);

}

static void NEAR ClearToLineStart(PTWND pTW)
{

    HWND hWnd = pTW->hWnd;
    BYTE *pCurr = pTW->pVidBuffer + pTW->oCurrentLine;
    RECT rect;
    register short ypos = pTW->Ypos;
    register offset = pTW->CurLineOffset + 1;

    memset(pCurr, SP, offset);

    SetRect(&rect, 0, ypos, offset * pTW->CharWidth, ypos + pTW->CharHeight); 
    InvalidateRect(hWnd, (LPRECT)&rect, TRUE);
    UpdateWindow(hWnd);
}

static void NEAR ClearToScreenTop(PTWND pTW)
{

    RECT rect;
    HWND hWnd = pTW->hWnd;
    BYTE *pBuf = pTW->pVidBuffer;
    BYTE *pTop = pBuf + pTW->oTopLine;
    BYTE *pEnd = pBuf + pTW->oVidLastLine;
    register BYTE *base = pBuf + pTW->oCurrentLine;
    register short cols = pTW->MaxCols;

    do {
        if ((base -= cols) < pBuf)
	    base = pEnd;
	memset(base, SP, cols);
    } while (base != pTop);
    SetRect((LPRECT)&rect, 0, 0, pTW->Width, pTW->Ypos);
    InvalidateRect(hWnd, (LPRECT)&rect, TRUE);
    UpdateWindow(hWnd);
}

static void NEAR EraseEntireLine(PTWND pTW)
{

    RECT rect;
    register HWND hWnd = pTW->hWnd;
    register short ypos = pTW->Ypos;
    
    memset(pTW->pVidBuffer + pTW->oCurrentLine, SP, pTW->MaxCols);
    SetRect((LPRECT)&rect, 0, ypos, pTW->Width, ypos + pTW->CharHeight);
    InvalidateRect(hWnd, (LPRECT)&rect, TRUE);
    UpdateWindow(hWnd);

}

/* reverse index */
static void NEAR ReverseLF(PTWND pTW)
{

    register short cols = pTW->MaxCols;
    register short oEnd = pTW->oVidLastLine;

    if ((pTW->oCurrentLine -= cols) < 0)
	pTW->oCurrentLine = oEnd;

    if (pTW->oCurrentLine == pTW->oBottomLine) {
	if ((pTW->oTopLine -= cols) < 0)
	    pTW->oTopLine = oEnd;
	if ((pTW->oBottomLine -= cols) < 0)
	    pTW->oBottomLine = oEnd;
	memset(pTW->pVidBuffer + pTW->oCurrentLine, SP, cols);
        ScrollWindow(pTW->hWnd,0,pTW->CharHeight,(LPRECT)NULL,(LPRECT)NULL);
        UpdateWindow(pTW->hWnd);
    }
    else 
	pTW->Ypos -= pTW->CharHeight;

    AdjustWindowToCursor(pTW);
}

/* line insertion and deletion routines */
static void NEAR InsertLine(PTWND pTW, WORD count)
{

    RECT rect;
    int i;
    HWND hWnd = pTW->hWnd;
    short cols = pTW->MaxCols;
    short lines = pTW->MaxLines;
    BYTE *pBuf = pTW->pVidBuffer;
    BYTE *pCurr = pBuf + pTW->oCurrentLine;
    BYTE *pEnd = pBuf + pTW->oVidLastLine;
    register BYTE *dest = pBuf + pTW->oBottomLine;
    register BYTE *src;

    if (count > lines)
	count = lines;
    if ((src = dest - count * cols) < pBuf)
	src += pTW->ScreenSize;
    while (dest != pCurr) {
	memmove(dest, src, cols);
	if ((src -= cols) < pBuf)
	    src = pEnd;
	if ((dest -= cols) < pBuf)
	    dest = pEnd;
    }
    src = pCurr;
    for (i = 0; i < count; i++) {
        memset(src, SP, cols);
	if ((src += cols) > pEnd)
    	    src = pBuf;
    }

    pTW->CurLineOffset = pTW->Xpos = 0;

    SetRect((LPRECT)&rect, 0, pTW->Ypos, pTW->Width, pTW->Height);
    ScrollWindow(hWnd, 0, count * pTW->CharHeight, &rect, &rect);
    UpdateWindow(hWnd);

}

static void NEAR DeleteLine(PTWND pTW, WORD count)
{

    RECT rect;
    HWND hWnd = pTW->hWnd;
    short cols = pTW->MaxCols;
    BYTE *pBuf = pTW->pVidBuffer;
    BYTE *dest = pBuf + pTW->oCurrentLine;
    BYTE *pBottom = pBuf + pTW->oBottomLine;
    register BYTE *pEnd = pBuf + pTW->oVidLastLine;
    register BYTE *src;
    int i;

    if (count > pTW->MaxLines)
	count = pTW->MaxLines;

    if ((src = dest + count * cols) > pEnd)
	src -= pTW->ScreenSize;
    while (dest != pBottom) {
	memmove(dest, src, cols);
        if ((src += cols) > pEnd)
	    src = pBuf;
        if ((dest += cols) > pEnd)
	    dest = pBuf;
    }
    src = pBottom;
    for (i = 0; i < count; i++) {
        memset(src, SP, cols);
	if ((src -= cols) < pBuf)
    	    src = pEnd;
    }

    pTW->CurLineOffset = pTW->Xpos = 0;

    SetRect((LPRECT)&rect,0,pTW->Ypos,pTW->Width,pTW->Height);
    ScrollWindow(hWnd,0, -count * pTW->CharHeight, &rect, &rect);
    UpdateWindow(hWnd);
}

/* cursor save and restore */
static void NEAR SaveCursor(short line, short column)
{

    CD.CurSaveRow =  line;
    CD.CurSaveCol =  column;
}

static void NEAR GetCursor(short lines, short cols, LONG lParam)
{

    short *x, *y;

    x = (short *)LOWORD(lParam);
    y = (short *)HIWORD(lParam);

    *x = lines;
    *y = cols;

}
