|
|
/* File: D:\WACKER\tdll\termupd.c (Created: 11-Dec-1993)
* * Copyright 1994 by Hilgraeve Inc. -- Monroe, MI * All rights reserved * * $Revision: 7 $ * $Date: 3/27/02 1:35p $ */
#include <windows.h>
#pragma hdrstop
#include <stdlib.h>
#include <limits.h>
#include "stdtyp.h"
#include "assert.h"
#include "session.h"
#include <emu\emu.h>
#include <emu\emu.hh>
#include "update.h"
#include "update.hh"
#include "backscrl.h"
#include "timers.h"
#include "tdll.h"
#include "htchar.h"
#include "term.h"
#include "term.hh"
#include "mc.h"
static void termUpdate(const HHTERM hhTerm); static int termReallocBkBuf(const HHTERM hhTerm, const int iLines);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * termGetUpdate * * DESCRIPTION: * Queries the update records and emulator to update the terminal image. * * ARGUMENTS: * hhTerm - internal terminal handle. * * RETURNS: * void * */ void termGetUpdate(const HHTERM hhTerm, const int fRedraw) { ECHAR **pachTxt, *pachTermTxt, *pachEmuTxt; PSTATTR *pstAttr, pstTermAttr, pstEmuAttr; int i, j, k, m; BYTE *pabLines;
// const iRows = hhTerm->iRows;
// const iCols = hhTerm->iCols;
const iRows = MAX_EMUROWS; const iCols = MAX_EMUCOLS;
const HEMU hEmu = sessQueryEmuHdl(hhTerm->hSession); const HHUPDATE hUpd = (HHUPDATE)sessQueryUpdateHdl(hhTerm->hSession);
/* --- Lock emulators so we have execlusive access --- */
emuLock(hEmu);
pachTxt = emuGetTxtBuf(hEmu); pstAttr = emuGetAttrBuf(hEmu);
// Now check to see what needs updating...
if (hUpd->bUpdateType == UPD_LINE) { struct stLineMode *pstLine = &hUpd->stLine;
if (pstLine->iLine != -1) { // The emulators can place the cursor one past the number
// of columns. Why, I don't know, so we check and adjust
// so we don't overwrite our client arrays.
pstLine->xEnd = min(pstLine->xEnd, iCols); assert(pstLine->xBeg <= pstLine->xEnd);
k = (pstLine->iLine + hUpd->iTopline) % MAX_EMUROWS;
pachEmuTxt = pachTxt[k] + pstLine->xBeg; pachTermTxt = hhTerm->fplpstrTxt[k] + pstLine->xBeg;
pstEmuAttr = pstAttr[k] + pstLine->xBeg; pstTermAttr = hhTerm->fppstAttr[k] + pstLine->xBeg;
for (k = pstLine->xEnd - pstLine->xBeg ; k >= 0 ; --k) { // Televideo uses \xFF as a NULL character.
if (*pachEmuTxt == ETEXT('\0') || *pachEmuTxt == ETEXT('\xFF')) *pachTermTxt = ETEXT(' ');
else *pachTermTxt = *pachEmuTxt;
*pstTermAttr = *pstEmuAttr;
pachTermTxt += 1; pachEmuTxt += 1;
pstTermAttr += 1; pstEmuAttr += 1; } } }
else if (hUpd->bUpdateType == UPD_SCROLL) { struct stScrlMode *pstScrl = &hUpd->stScrl;
hUpd->iLines = hhTerm->iBkLines = backscrlGetNumLines(sessQueryBackscrlHdl(hhTerm->hSession));
/* -------------- Backscroll portion ------------- */
if ((i = min(hhTerm->iBkLines, pstScrl->iBksScrlInc)) > 0) termGetBkLines(hhTerm, i, -i, BKPOS_ABSOLUTE);
/* -------------- Terminal portion ------------- */
if (pstScrl->iScrlInc != 0) { if (pstScrl->iScrlInc > 0) { i = max(0, pstScrl->yEnd - pstScrl->iScrlInc + 1); m = pstScrl->yEnd; }
else { i = pstScrl->yBeg; m = min(iRows, pstScrl->yBeg - pstScrl->iScrlInc - 1); }
k = ((i + hUpd->iTopline) % MAX_EMUROWS);
for (; i <= m ; ++i) { // Server and Client have different size emulator images
// for historical reasons. Server side has 2 extra characters
// per row.
pachTermTxt = hhTerm->fplpstrTxt[k]; pachEmuTxt = pachTxt[k];
pstTermAttr = hhTerm->fppstAttr[k]; pstEmuAttr = pstAttr[k];
// Update the terminal buffer now.
for (j = 0 ; j < iCols ; ++j, ++pachTermTxt, ++pachEmuTxt) { // Televideo uses \xFF as a NULL character.
if (*pachEmuTxt == ETEXT('\0') || *pachEmuTxt == ETEXT('\xFF')) { *pachTermTxt = ETEXT(' '); }
else { *pachTermTxt = *pachEmuTxt; } }
MemCopy(pstTermAttr, pstEmuAttr, iCols * sizeof(STATTR));
if (++k >= MAX_EMUROWS) k = 0; } }
// Check for lines now.
k = hUpd->iTopline; pabLines = pstScrl->auchLines + pstScrl->iFirstLine;
for (j = 0 ; j < iRows ; ++j, ++pabLines) { if (*pabLines != 0) { pachEmuTxt = pachTxt[k]; pachTermTxt = hhTerm->fplpstrTxt[k];
pstEmuAttr = pstAttr[k]; pstTermAttr = hhTerm->fppstAttr[k];
// Update the terminal buffer now.
for (i = 0 ; i < iCols ; ++i, ++pachTermTxt, ++pachEmuTxt) { // Televideo uses \xFF as a NULL character.
if (*pachEmuTxt == ETEXT('\0') || *pachEmuTxt == ETEXT('\xFF')) { *pachTermTxt = ETEXT(' '); }
else { *pachTermTxt = *pachEmuTxt; } }
MemCopy(pstTermAttr, pstEmuAttr, iCols * sizeof(STATTR)); }
if (++k >= MAX_EMUROWS) k = 0; }
// Another ugly situation. Because we can mark stuff in the
// backscroll buffer and still have updates coming in, we have
// to bump the marking region by the scrollinc to keep everything
// synchronized. Note, we don't need to check for Marking locks
// because we could not have been here had any been in place.
if (hhTerm->ptBeg.y < 0 || hhTerm->ptEnd.y < 0) { hhTerm->ptBeg.y -= pstScrl->iScrlInc; hhTerm->ptEnd.y -= pstScrl->iScrlInc; }
// Update emulator's topline field now.
hhTerm->iTopline = hUpd->iTopline; }
// Save a copy of the update handle in term. This way we can
// release our lock and paint without blocking the emulator.
*(HHUPDATE)hhTerm->hUpdate = *hUpd; updateReset(hUpd);
/* --- Important to remember to unlock emulator --- */
emuUnlock(hEmu);
/* --- Now let the terminal figure out how to paint itself. --- */
if (fRedraw) termUpdate(hhTerm);
return; }
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * termUpdate * * DESCRIPTION: * Invalidates the proper portions of the terminal buffers, updates * scrollbars, and generally takes care of the busy work of keeping * the terminal screen up to date. * * ARGUMENTS: * hwnd - terminal window handle * * RETURNS: * void * */ static void termUpdate(const HHTERM hhTerm) { RECT rc; int i, j, l; BYTE *pauchLines; SCROLLINFO scrinf; const HHUPDATE hUpd = (HHUPDATE)hhTerm->hUpdate;
GetClientRect(hhTerm->hwnd, &rc);
// Adjust rectangle not to include indent/outdent areas. This
// will be ignored if we are scrolling the whole terminal.
rc.left += hhTerm->xIndent + (hhTerm->iHScrlPos ? 0 : hhTerm->xBezel); rc.right = min(((hhTerm->iCols * hhTerm->xChar) + hhTerm->xIndent + hhTerm->xBezel), rc.right);
if (hUpd->bUpdateType == UPD_LINE) { if (hUpd->stLine.iLine != -1) { rc.top = (hUpd->stLine.iLine - hhTerm->iVScrlPos + 1) * hhTerm->yChar;
rc.bottom = rc.top + hhTerm->yChar;
rc.left = ((hUpd->stLine.xBeg - hhTerm->iHScrlPos) * hhTerm->xChar) + hhTerm->xIndent + hhTerm->xBezel;
rc.right = ((hUpd->stLine.xEnd - hhTerm->iHScrlPos + 1) * hhTerm->xChar) + hhTerm->xIndent + hhTerm->xBezel;
// Invalidate entire line when doing italics
//
if (hhTerm->fItalic) { rc.left = 0; rc.right = hhTerm->cx; }
InvalidateRect(hhTerm->hwnd, &rc, FALSE); } }
else if (hUpd->bUpdateType == UPD_SCROLL) { // Scroll range will change because new text is
// scrolling into the backscroll region.
i = hhTerm->iVScrlMin; j = hhTerm->iVScrlMax;
if (i == j) hhTerm->fBump = FALSE;
l = 0;
// If bezel is drawn, make sure we have room
//
if (hhTerm->xBezel) { if ((hhTerm->cy % hhTerm->yChar) < (hhTerm->xBezel + 1)) l = 1; }
hhTerm->iVScrlMin = min(-hUpd->iLines, hhTerm->iRows - hhTerm->iTermHite + 1 + l);
// This forces the terminal paint correctly if the minimum
// number of lines changes such that the current vertical
// scrolling position is no longer valid. mrw:6/19/95
//
if (hhTerm->iVScrlPos < hhTerm->iVScrlMin) hhTerm->iVScrlPos = hhTerm->iVScrlMin;
if (i != hhTerm->iVScrlMin) { scrinf.cbSize= sizeof(scrinf); scrinf.fMask = SIF_DISABLENOSCROLL | SIF_RANGE | SIF_PAGE; scrinf.nMin = hhTerm->iVScrlMin; scrinf.nMax = hhTerm->iVScrlMax + hhTerm->iTermHite - 1; scrinf.nPage = (unsigned int)hhTerm->iTermHite; SetScrollInfo(hhTerm->hwnd, SB_VERT, &scrinf, TRUE); }
// This is subtle but necessary. When the window is
// large enough to show full terminal and backscroll
// and the backscroll is empty, iVScrlPos is 0. The
// moment the backscroll buffer becomes larger than
// the backscroll area displayed, the iVScrlPos becomes
// iVScrlMax in the case where text is coming in at the
// bottom of the terminal screen. We could update the
// scrollbar on every pass, but this causes an annoying
// flicker in the scrollbar. This next piece of code
// catches the transition from backscroll smaller than
// displayed area to backscroll greater than displayed
// area and updates the scrollbar position.
// DbgOutStr("bump=%d, Min=%d, Max=%d\r\n",
// hhTerm->fBump, hhTerm->iVScrlMin, hhTerm->iVScrlMax, 0, 0);
if (!hhTerm->fBump && hhTerm->iVScrlMin != hhTerm->iVScrlMax) { // DbgOutStr("Pos = %d\r\n", hhTerm->iVScrlPos, 0, 0, 0, 0);
scrinf.cbSize= sizeof(scrinf); scrinf.fMask = SIF_DISABLENOSCROLL | SIF_POS; scrinf.nPos = hhTerm->iVScrlPos; SetScrollInfo(hhTerm->hwnd, SB_VERT, &scrinf, TRUE); hhTerm->fBump = TRUE; }
// Scroll specified area.
rc.top = max(0, hUpd->stScrl.yBeg - hhTerm->iVScrlPos + 1) * hhTerm->yChar;
rc.bottom = min(hhTerm->iTermHite, (hUpd->stScrl.yEnd - hhTerm->iVScrlPos + 2)) * hhTerm->yChar;
// use iOffset to check for cursor erase operation.
if (!hhTerm->fBackscrlLock) { HideCursors(hhTerm);
ScrollWindow(hhTerm->hwnd, 0, -hhTerm->yChar * hUpd->stScrl.iScrlInc, (LPRECT)0, &rc);
DbgOutStr("scroll %d", -hhTerm->yChar * hUpd->stScrl.iScrlInc, 0, 0, 0, 0); }
// Examine the lines portion of update record.
// Note, this is a negative area rectangle (ie. top is larger
// than bottom).
//
rc.top = INT_MAX; rc.bottom = 0;
pauchLines = hUpd->stScrl.auchLines + hUpd->stScrl.iFirstLine;
for (j = 0 ; j < hhTerm->iRows ; ++j, ++pauchLines) { if (*pauchLines != (UCHAR)0) { //iPaintEnd = max(iPaintEnd, j+1);
DbgOutStr("pauchLines->%d\r\n", j, 0, 0, 0, 0);
// Map invalid line to terminal.
l = (j - hhTerm->iVScrlPos + 1) * hhTerm->yChar;
if (l >= 0) { rc.top = min(rc.top, l); rc.bottom = max(rc.bottom, l + hhTerm->yChar); }
InvalidateRect(hhTerm->hwnd, &rc, FALSE); } } } /* else */
// Update the host cursor position according to the update record.
// mrw:6/19/95 - comparison was backwards.
//
if (hhTerm->ptHstCur.y != hUpd->iRow || hhTerm->ptHstCur.x != hUpd->iCol) { HideCursors(hhTerm); hhTerm->ptHstCur.y = hUpd->iRow; hhTerm->ptHstCur.x = hUpd->iCol; }
// Important to paint now. If we wait, and the
// backscroll region is also invalid, Windows will take
// the union of these two rectangles and paint a much
// larger region of the screen than is needed or wanted.
// Note: UpdateWindow does nothing if the update region
// in empty.
UpdateWindow(hhTerm->hwnd);
// Now take care of the backscroll buffer.
i = hUpd->stScrl.iBksScrlInc;
if (i && hUpd->bUpdateType == UPD_SCROLL) { rc.top = 0; rc.bottom = min(hhTerm->iTermHite, -hhTerm->iVScrlPos) * hhTerm->yChar;
if (rc.bottom > rc.top && !hhTerm->fBackscrlLock) { HideCursors(hhTerm); ScrollWindow(hhTerm->hwnd, 0, -hhTerm->yChar * i, (LPRECT)0, &rc); UpdateWindow(hhTerm->hwnd); }
else if (hhTerm->fBackscrlLock) { hhTerm->iVScrlPos -= i; scrinf.cbSize= sizeof(scrinf); scrinf.fMask = SIF_DISABLENOSCROLL | SIF_POS; scrinf.nPos = hhTerm->iVScrlPos; SetScrollInfo(hhTerm->hwnd, SB_VERT, &scrinf, TRUE); } }
// Important to do this again before we turn the host cursor
// back on.
// Note: UpdateWindow does nothing if the update region
// is empty.
UpdateWindow(hhTerm->hwnd); ShowCursors(hhTerm); return; }
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * termGetBkLines * * DESCRIPTION: * This function is uglier than ulgy. This function updates the local * backscroll buffer after it has been filled by backscrlCFillLocalBk(). * This function only graps the actual number of lines it needs to update * the local backscroll page. The reason it is complex is because the * server also stores it's backscroll buffer in pages. If this routine * requires data from more than one page, it must repeatedly ask the * server until the request is satisfied. Some assumptions are made * here (and in backscrlCFillLocalBk()). First and foremost, requests * are never made beyond the backscroll region. The caller's of these * routines does this right now. Second, if x number of lines are asked * for, this routine will continue to try until it meets the request. * Again, the callers are smart enough not to exceed the backscroll ranges. * * ARGUMENTS: * HHTERM hhTerm -- internal terminal handle * int iScrlInc -- the # of lines and the direction scrolled * int yBeg -- depends on sType * int iType -- if BKPOS_THUMBPOS, yBeg is the thumb pos. * if BKPOS_ABSOLUTE, yBeg is absolute pos. * * RETURNS: * nothing * */ void termGetBkLines(const HHTERM hhTerm, const int iScrlInc, int yBeg, int sType) { int i, j, k, l; int iWant, iGot; int iOffset; ECHAR *pachTxt, // terminal buffer
*pachBkTxt; // engine buffer
const HBACKSCRL hBackscrl = sessQueryBackscrlHdl(hhTerm->hSession);
if (abs(iScrlInc) > hhTerm->iPhysicalBkRows) { termFillBk(hhTerm, yBeg); return; }
// Get needed backscroll text from server
if (iScrlInc < 0) { assert(sType != BKPOS_ABSOLUTE); i = iScrlInc;
// l is a wrap counter and is calculated for speed.
//
l = hhTerm->iNextBkLn = (hhTerm->iNextBkLn + hhTerm->iPhysicalBkRows + iScrlInc) % hhTerm->iPhysicalBkRows; }
else { if (sType == BKPOS_THUMBPOS) { yBeg += hhTerm->iTermHite - iScrlInc; assert(yBeg < 0); }
i = 0; }
// Since backscroll memory is pages we have to make multiple requests.
//
for (iWant=abs(iScrlInc), iGot=0 ; iWant > 0 ; iWant-=iGot, yBeg+=iGot) { if (backscrlGetBkLines(hBackscrl, yBeg, iWant, &iGot, &pachBkTxt, &iOffset) == FALSE) { return; }
pachBkTxt += iOffset;
// Apply text to backscroll buffer
if (iScrlInc < 0) { for (k=0 ; (i < 0) && (k < iGot) ; ++i, ++k) { pachTxt = hhTerm->fplpstrBkTxt[l];
for (j = 0 ; j < MAX_EMUCOLS && *pachBkTxt != ETEXT('\n') ; ++j) { assert(*pachBkTxt); *pachTxt++ = *pachBkTxt++; }
for ( ; j < MAX_EMUCOLS ; ++j) *pachTxt++ = ' ';
pachBkTxt += 1; // Blow past newline marker...
if (++l >= hhTerm->iPhysicalBkRows) l = 0; } }
else { for (k=0 ; (i < iScrlInc) && (k < iGot) ; ++i, ++k) { pachTxt = hhTerm->fplpstrBkTxt[hhTerm->iNextBkLn];
for (j = 0 ; j < MAX_EMUCOLS && *pachBkTxt != ETEXT('\n') ; ++j) { assert(*pachBkTxt != ETEXT('\0')); *pachTxt++ = *pachBkTxt++; }
for ( ; j < MAX_EMUCOLS ; ++j) *pachTxt++ = ETEXT(' ');
if (++hhTerm->iNextBkLn >= hhTerm->iPhysicalBkRows) hhTerm->iNextBkLn = 0;
pachBkTxt += 1; // Blow past newline marker...
} } }
return; }
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * termFillBk * * DESCRIPTION: * This routine fills an entire local (refering a view) backscroll buffer. * It is called only three times. Whenever the transistion is made from * active backscrolling to static backscrolling. When the Scroll * increment during a static backscroll operation is greater than the size * of the view (and therefore the number of lines in the backscroll buffer * as denoted by hhTerm->iPhysicalBkRows). And when the terminal window * is resized. * * ARGUMENTS: * HHTERM hhTerm -- internal terminal handle * int iTermHite -- # of rows that will fit on current terminal * int iBkPos -- where to start in backscroll * * RETURNS: * void * */ void termFillBk(const HHTERM hhTerm, const int iBkPos) { int i, j; int iWant, iGot, yBeg; int iOffset; ECHAR **fplpstrBkTxt, *pachTxt, // terminal buffer
*pachBkTxt; //
const HBACKSCRL hBackscrl = sessQueryBackscrlHdl(hhTerm->hSession);
if (hhTerm->iTermHite > hhTerm->iMaxPhysicalBkRows) { if (termReallocBkBuf(hhTerm, hhTerm->iTermHite) != 0) { assert(FALSE); return; } }
/* --- Empty the rest of terminal's backscroll buffer --- */
for (i = 0 ; i < hhTerm->iPhysicalBkRows ; ++i) ECHAR_Fill(hhTerm->fplpstrBkTxt[i], EMU_BLANK_CHAR, MAX_EMUCOLS);
/* --- Grab whatever we can from the engine's backscroll buffer --- */
hhTerm->iPhysicalBkRows = hhTerm->iTermHite; hhTerm->iNextBkLn = 0;
// mrw: 2/29/96 - moved the check for an empty buffer to past where
// the iPhyscialRows gets set.
//
if (hhTerm->iBkLines == 0) return;
//*yBeg = iBkPos;
yBeg = max(-hhTerm->iBkLines, iBkPos); iWant = min(hhTerm->iTermHite, abs(yBeg)); fplpstrBkTxt = hhTerm->fplpstrBkTxt + (hhTerm->iTermHite - iWant);
for (iGot=0 ; iWant > 0 ; iWant-=iGot, yBeg+=iGot) { if (backscrlGetBkLines(hBackscrl, yBeg, iWant, &iGot, &pachBkTxt, &iOffset) == FALSE) { return; }
pachBkTxt += iOffset;
for (i = 0 ; i < iGot ; ++i, ++fplpstrBkTxt) { pachTxt = *fplpstrBkTxt;
for (j = 0 ; j < MAX_EMUCOLS && *pachBkTxt != ETEXT('\n') ; ++j) { assert(*pachBkTxt); *pachTxt++ = *pachBkTxt++; }
for ( ; j < MAX_EMUCOLS ; ++j) *pachTxt++ = ETEXT(' ');
pachBkTxt += 1; } }
return; }
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * termReallocBkBuf * * DESCRIPTION: * This happens when the user changes to a smaller font which allows more * rows on the screen. * * ARGUMENTS: * hhTerm - private terminal handle * iLines - number of lines to realloc * * RETURNS: * 0=OK, 1=error * */ static int termReallocBkBuf(const HHTERM hhTerm, const int iLines) { int i; ECHAR** pTempfplpstrBkTxt = NULL;
if (iLines < hhTerm->iMaxPhysicalBkRows) { return 0; }
pTempfplpstrBkTxt = (ECHAR**)realloc(hhTerm->fplpstrBkTxt, (unsigned int)iLines * sizeof(ECHAR *));
if (pTempfplpstrBkTxt == NULL) { return 1; } else { hhTerm->fplpstrBkTxt = pTempfplpstrBkTxt; }
for (i = hhTerm->iMaxPhysicalBkRows ; i < iLines ; ++i) { if ((hhTerm->fplpstrBkTxt[i] = malloc(MAX_EMUCOLS * sizeof(ECHAR))) == 0) { assert(FALSE); return 1; }
ECHAR_Fill(hhTerm->fplpstrBkTxt[i], EMU_BLANK_CHAR, MAX_EMUCOLS); }
hhTerm->iMaxPhysicalBkRows = iLines; return 0; }
|