|
|
coherent
/*
* The functions in this file handle redisplay.
* There are two halves, the ones that update the virtual display screen,
* and the ones that make the physical display screen the same as the virtual
* display screen. These functions use hints that are left in the windows by
* the commands.
*/
#include <stdio.h>
#include "ed.h"
#define WFDEBUG 0 /* Window flag debug. */
#define FASTHACK 1 /* Slightly faster update code */
typedef struct VIDEO {
short v_flag; /* Flags */
uchar v_text[]; /* Screen data. */
} VIDEO;
#define VFCHG 0x0001 /* Changed. */
#define VFSTD 0x0002 /* Standout. */
int sgarbf = TRUE; /* TRUE if screen is garbage */
int mpresf = FALSE; /* TRUE if message in last line */
int vtrow = 0; /* Row location of SW cursor */
int vtcol = 0; /* Column location of SW cursor */
int ttrow = HUGE; /* Row location of HW cursor */
int ttcol = HUGE; /* Column location of HW cursor */
VIDEO **vscreen; /* Virtual screen. */
VIDEO **pscreen; /* Physical screen. */
/*
* Initialize the data structures used by the display code.
* The edge vectors used to access the screens are set up.
* The operating system's terminal I/O channel is set up.
* All the other things get initialized at compile time.
* The original window has "WFCHG" set, so that it will get
* completely redrawn on the first call to "update".
*/
vtinit()
{
register int i;
register VIDEO *vp;
vscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
if (vscreen == NULL)
abort();
pscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
if (pscreen == NULL)
abort();
for (i=0; i<term.t_nrow; ++i) {
vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
if (vp == NULL)
abort();
vscreen[i] = vp;
vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
if (vp == NULL)
abort();
pscreen[i] = vp;
}
}
/*
* Clean up the virtual terminal system, in anticipation for a return to the
* operating system. Move down to the last line and clear it out (the next
* system prompt will be written in the line). Shut down the channel to the
* terminal.
*/
vttidy()
{
movecursor(term.t_nrow, 0);
teeol();
tstand(0);
tclose();
}
/*
* Set the virtual cursor to the specified row and column on the
* virtual screen. There is no checking for nonsense values; this might
* be a good idea during the early stages.
*/
vtmove(row, col)
{
vtrow = row;
vtcol = col;
}
/*
* If bind.dispmode == 1 chars < ' ' are displayed directly
* else they are displayed whit preceeding ^ and ^= '@'
*/
displaymod()
{
bind.dispmode ^= 1;
}
/*
* Return 2 if char is tab 1 if it needs ^ for ansi emulation.
*/
dblchr(c)
register unsigned c;
{
if (bind.dispmode) {
switch(c) {
case '\n':
case '\f':
case '\r':
case 0x1b:
return (1);
case '\t':
return (2);
}
return (0);
}
return (((c < ' ') || (c == 0x7f)) ? ((c == '\t') ? 2 : 1) : 0);
}
/*
* Write a character to the virtual screen. The virtual row and
* column are updated. If the line is too long put a "$" in the last column.
* This routine only puts printing characters into the virtual terminal buffers.
* Only column overflow is checked.
*/
vtputc(c)
unsigned c;
{
register VIDEO *vp;
vp = vscreen[vtrow];
switch (dblchr(c)) {
case 0: /* normal character */
if (vtcol >= term.t_ncol)
vp->v_text[term.t_ncol - 1] = '$';
else
vp->v_text[vtcol++] = c;
break;
case 2: /* tab */
do {
if (vtcol >= term.t_ncol) {
vp->v_text[term.t_ncol - 1] = '$';
break;
} else
vp->v_text[vtcol++] = ' ';
} while (vtcol % bind.tabsiz);
break;
case 1: /* needs ^ */
vtputc('^');
vtputc(c ^ 0x40);
break;
}
}
/*
* Erase from the end of the software cursor to the end of the line on which
* the software cursor is located.
*/
vteeol()
{
register VIDEO *vp;
vp = vscreen[vtrow];
while (vtcol < term.t_ncol)
vp->v_text[vtcol++] = ' ';
}
/*
* Make sure that the display is right. This is a three part process.
* First, scan through all of the windows looking for dirty ones.
* Check the framing, and refresh the screen.
* Second, make sure that "currow" and "curcol" are correct for the current
* window.
* Third, make the virtual and physical screens the same.
*/
update()
{
register LINE *lp;
LINE *xlp;
register WINDOW *wp;
register VIDEO *vp1;
register VIDEO *vp2;
register int i;
register int j;
wp = wheadp;
while (wp != NULL) {
/* Look at any window with update flags set on. */
if (wp->w_flag != 0) {
/* If not force reframe, check the framing. */
if ((wp->w_flag&WFFORCE) == 0) {
lp = wp->w_linep;
for (i=0; i<wp->w_ntrows; ++i) {
if (lp == wp->w_dotp)
goto out;
if (lp == wp->w_bufp->b_linep)
break;
lp = lforw(lp);
}
}
/* Not acceptable, better compute a new value */
/* for the line at the top of the window. Then */
/* set the "WFHARD" flag to force full redraw. */
i = wp->w_force;
if (i > 0) {
--i;
if (i >= wp->w_ntrows)
i = wp->w_ntrows-1;
} else if (i < 0) {
i += wp->w_ntrows;
if (i < 0)
i = 0;
} else
i = wp->w_ntrows/2;
lp = wp->w_dotp;
while (i!=0 && lback(lp)!=wp->w_bufp->b_linep) {
--i;
lp = lback(lp);
}
wp->w_linep = lp;
wp->w_flag |= WFHARD; /* Force full. */
out:
/* Try to use reduced update. Mode line update */
/* has its own special flag. The fast update is */
/* used if the only thing to do is within the */
/* line editing. */
lp = wp->w_linep;
i = wp->w_toprow;
if ((wp->w_flag&~WFMODE) == WFEDIT) {
while (lp != wp->w_dotp) {
++i;
lp = lforw(lp);
}
#ifdef FASTHACK
vscreen[i]->v_flag = VFCHG;
#else
vscreen[i]->v_flag |= VFCHG;
vscreen[i]->v_flag &= ~VFSTD;
#endif
vtmove(i, 0);
for (j=0; j<llength(lp); ++j)
vtputc(lgetc(lp, j));
vteeol();
} else if ((wp->w_flag&(WFEDIT|WFHARD)) != 0) {
while (i < wp->w_toprow+wp->w_ntrows) {
#ifdef FASTHACK
vscreen[i]->v_flag = VFCHG;
#else
vscreen[i]->v_flag |= VFCHG;
vscreen[i]->v_flag &= ~VFSTD;
#endif
vtmove(i, 0);
if (lp != wp->w_bufp->b_linep) {
for (j=0; j<llength(lp); ++j)
vtputc(lgetc(lp, j));
lp = lforw(lp);
}
vteeol();
++i;
}
}
#if !WFDEBUG
if ((wp->w_flag&WFMODE) != 0)
modeline(wp);
wp->w_flag = 0;
wp->w_force = 0;
#endif
}
#if WFDEBUG
modeline(wp);
wp->w_flag = 0;
wp->w_force = 0;
#endif
/* Set standout mode on status line... */
vscreen[wp->w_toprow+wp->w_ntrows]->v_flag |= VFSTD;
wp = wp->w_wndp;
}
/* Always recompute the row and column number of the hardware */
/* cursor. This is the only update for simple moves. */
xlp = lp = curwp->w_linep;
currow = curwp->w_toprow;
while (lp != curwp->w_dotp) {
++currow;
if (xlp == (lp = lforw(lp))) { /* Fix infinite loop problem */
currow = curwp->w_toprow;
curwp->w_dotp = lp = xlp;
curwp->w_doto = 0;
break;
}
}
curcol = truecol(lp, curwp->w_doto);
#if GEM && NATIVE
/* Special preparation for screen update on ATARI ST native screen */
/* We shut off the cursor -- This speeds up the writing of text */
/* quite a bit. */
astcursor(0); /* Turn cursor off for update */
#endif
/* Special hacking if the screen is garbage. Clear the hardware */
/* screen, and update your copy to agree with it. Set all the */
/* virtual screen change bits, to force a full update. */
if (sgarbf != FALSE) {
for (i=0; i<term.t_nrow; ++i) {
vscreen[i]->v_flag |= VFCHG;
vp1 = pscreen[i];
for (j=0; j<term.t_ncol; ++j)
vp1->v_text[j] = ' ';
}
teeop(); /* Erase the screen. */
sgarbf = FALSE; /* Erase-page clears */
mpresf = FALSE; /* the message area. */
}
/* Make sure that the physical and virtual displays agree. */
/* Unlike before, the "updateline" code is only called with a */
/* line that has been updated for sure. */
for (i=0; i<term.t_nrow; ++i) {
vp1 = vscreen[i];
if ((vp1->v_flag&VFCHG) != 0) {
vp2 = pscreen[i];
if ((vp1->v_flag&VFSTD) != 0) { /* Standout mode */
#ifndef FASTHACK
vp1->v_flag &= ~VFSTD;
#endif
tstand(1);
updateline(i, &vp1->v_text[0], &vp2->v_text[0]);
tstand(0);
} else
updateline(i, &vp1->v_text[0], &vp2->v_text[0]);
#ifdef FASTHACK
vp1->v_flag = 0;
#else
vp1->v_flag &= ~VFCHG;
#endif
}
}
/* Finally, update the hardware cursor and flush out buffers. */
bracketmode(currow, curcol);
#if GEM && NATIVE
astcursor(1); /* Turn cursor back on... */
#endif
tflush();
}
/*
* Update a single line. This does not know how to use insert or delete
* character sequences; we are using VT52 functionality. Update the physical
* row and column variables. It does try and exploit erase to end of line.
* The RAINBOW version of this routine uses fast video.
*/
updateline(row, vline, pline)
uchar vline[];
uchar pline[];
{
#if RAINBOW|IBM
register uchar *cp1;
register uchar *cp2;
register int nch;
cp1 = &vline[0]; /* Use fast video. */
cp2 = &pline[0];
putline(row+1, 1, cp1);
nch = term.t_ncol;
do {
*cp2 = *cp1;
++cp2;
++cp1;
} while (--nch);
#else
register uchar *cp1;
register uchar *cp2;
register uchar *cp3;
register uchar *cp4;
register uchar *cp5;
register int nbflag;
cp1 = &vline[0]; /* Compute left match. */
cp2 = &pline[0];
while (cp1!=&vline[term.t_ncol] && cp1[0]==cp2[0]) {
++cp1;
++cp2;
}
/* This can still happen, even though we only call this routine */
/* on changed lines. A hard update is always done when a line */
/* splits, a massive change is done, or a buffer is displayed */
/* twice. This optimizes out most of the excess updating. A lot */
/* of computes are used, but these tend to be hard operations */
/* that do a lot of update, so I don't really care. */
if (cp1 == &vline[term.t_ncol]) /* All equal. */
return;
nbflag = FALSE;
cp3 = &vline[term.t_ncol]; /* Compute right match. */
cp4 = &pline[term.t_ncol];
while (cp3[-1] == cp4[-1]) {
--cp3;
--cp4;
if (cp3[0] != ' ') /* Note if any nonblank */
nbflag = TRUE; /* in right match. */
}
cp5 = cp3;
if (nbflag == FALSE) { /* Erase to EOL ? */
while (cp5!=cp1 && cp5[-1]==' ')
--cp5;
if (cp3-cp5 <= 3) /* Use only if erase is */
cp5 = cp3; /* fewer characters. */
}
movecursor(row, (int)(cp1-&vline[0])); /* Go to start of line. */
while (cp1 != cp5) { /* Ordinary. */
tputc(*cp1);
++ttcol;
*cp2++ = *cp1++;
}
if (cp5 != cp3) { /* Erase. */
teeol();
while (cp1 != cp3)
*cp2++ = *cp1++;
}
#endif
}
/*
* Redisplay the mode line for the window pointed to by the "wp".
* This is the only routine that has any idea of how the modeline is formatted.
* You can change the modeline format by hacking at this routine.
* Called by "update" any time there is a dirty window.
*/
modeline(wp)
register WINDOW *wp;
{
register uchar *cp;
register int c;
register int n;
register BUFFER *bp;
n = wp->w_toprow+wp->w_ntrows; /* Location. */
vscreen[n]->v_flag |= VFCHG; /* Redraw next time. */
vtmove(n, 0); /* Seek to right line. */
vtputc('-');
bp = wp->w_bufp;
if ((bp->b_flag&BFCHG) != 0) /* "*" if changed. */
vtputc('*');
else
vtputc('-');
n = 2;
cp = PROMPT; /* Buffer name. */
while ((c = *cp++) != 0) {
vtputc(c);
++n;
}
cp = &bp->b_bname[0];
while ((c = *cp++) != 0) {
vtputc(c);
++n;
}
vtputc(' ');
++n;
if (bp->b_fname[0] != 0) { /* File name. */
#if LIBHELP
if (bp->b_flag & BFHELP)
cp = "- Subject: ";
else
#endif
cp = "-- File: ";
while ((c = *cp++) != 0) {
vtputc(c);
++n;
}
cp = &bp->b_fname[0];
while ((c = *cp++) != 0) {
vtputc(c);
++n;
}
vtputc(' ');
++n;
}
#if WFDEBUG
vtputc('-');
vtputc((wp->w_flag&WFMODE)!=0 ? 'M' : '-');
vtputc((wp->w_flag&WFHARD)!=0 ? 'H' : '-');
vtputc((wp->w_flag&WFEDIT)!=0 ? 'E' : '-');
vtputc((wp->w_flag&WFMOVE)!=0 ? 'V' : '-');
vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : '-');
n += 6;
#endif
while (n < term.t_ncol) { /* Pad to full width. */
vtputc('-');
++n;
}
}
/*
* Send a command to the terminal to move the hardware cursor to row "row"
* and column "col". The row and column arguments are origin 0.
* Optimize out random calls. Update "ttrow" and "ttcol".
*/
movecursor(row, col)
{
if (row!=ttrow || col!=ttcol) {
ttrow = row;
ttcol = col;
tmove(row, col);
}
}
/*
* Erase the message line.
* This is a special routine because the message line is not considered to be
* part of the virtual screen. It always works immediately; the terminal
* buffer is flushed via a call to the flusher.
*/
mlerase()
{
movecursor(term.t_nrow, 0);
teeol();
tflush();
mpresf = FALSE;
}
/*
* Ask a yes or no question in the message line.
* Return either TRUE, FALSE, or ABORT. The ABORT status is returned
* if the user bumps out of the question with a ^G.
* Used any time a confirmation is required.
*/
mlyesno(prompt)
uchar *prompt;
{
register int s;
uchar buf[64];
for (;;) {
strcpy(buf, prompt);
strcat(buf, " [y/n]? ");
s = mlreply(buf, buf, sizeof(buf));
if (s == ABORT)
return (ABORT);
if (s != FALSE) {
if (buf[0]=='y' || buf[0]=='Y')
return (TRUE);
if (buf[0]=='n' || buf[0]=='N')
return (FALSE);
}
}
}
/*
* Write a prompt into the message line, then read back a response.
* Keep track of the physical position of the cursor.
* If we are in a keyboard macro throw the prompt away, and return
* the remembered response. This lets macros run at full speed.
* The reply is always terminated by a carriage return.
* Handle erase, kill, and abort keys.
*/
mlreply(prompt, buf, nbuf)
uchar *prompt;
uchar *buf;
{
register int cpos;
register int i;
register int c;
cpos = 0;
if (kbdmop != NULL) {
while ((c = *kbdmop++) != '\0')
buf[cpos++] = c;
buf[cpos] = 0;
if (buf[0] == 0)
return (FALSE);
return (TRUE);
}
mlwrite(prompt);
for (;;) {
c = tgetc();
switch (c) {
case 0x0D: /* Return, end of line */
buf[cpos++] = 0;
if (kbdmip != NULL) {
if ((kbdmip+cpos) > (kbdm + ((NKBDM - 3)/2))) {
ctrlg(FALSE, 0);
tflush();
return (ABORT);
}
for (i=0; i<cpos; ++i)
*kbdmip++ = buf[i];
}
tputc('\r');
ttcol = 0;
tflush();
if (buf[0] == 0)
return (FALSE);
return (TRUE);
case 0x07: /* Bell, abort */
tputc('^');
tputc('G');
ttcol += 2;
ctrlg(FALSE, 0);
tflush();
return (ABORT);
case 0x7f: /* Rubout, erase */
case 0x08: /* Backspace, erase */
if (cpos != 0) {
tputc('\b');
tputc(' ');
tputc('\b');
--ttcol;
if (buf[--cpos] < 0x20) {
tputc('\b');
tputc(' ');
tputc('\b');
--ttcol;
}
tflush();
}
break;
case 0x15: /* C-U, kill */
while (cpos != 0) {
tputc('\b');
tputc(' ');
tputc('\b');
--ttcol;
if (buf[--cpos] < 0x20) {
tputc('\b');
tputc(' ');
tputc('\b');
--ttcol;
}
}
tflush();
break;
default:
if (cpos < nbuf-1) {
buf[cpos++] = c;
if (c < ' ') {
tputc('^');
++ttcol;
c ^= 0x40;
}
tputc(c);
++ttcol;
tflush();
}
}
}
}
/*
* Write a message into the message line.
* Keep track of the physical cursor position.
* A small class of printf like format items is handled.
* Assumes the stack grows down; this assumption is made by the "++"
* in the argument scan loop. Set the "message line" flag TRUE.
*/
mlwrite(fmt)
uchar *fmt;
{
register int c;
uchar buf[NPAT * 2];
movecursor(term.t_nrow, 0);
sprintf(buf, "%r", &fmt);
if (strlen(buf) > (term.t_ncol - 1))
buf[term.t_ncol - 1] = 0;
for (fmt = buf; c = *fmt; fmt++) {
tputc(c);
++ttcol;
}
teeol();
tflush();
mpresf = TRUE;
}
/*
* Write out a string.
* Update the physical cursor position. This assumes that the characters in the
* string all have width "1"; if this is not the case things will get screwed up
* a little.
*/
mlputs(s)
register uchar *s;
{
register int c;
while ((c = *s++) != 0) {
tputc(c);
++ttcol;
}
}
/*
* find the logical cursor. Return line from top of
* window.
*/
locatecursor(what)
LINE *what;
{
int row;
LINE *clp;
row = 0;
for (clp = curwp->w_linep; clp != what; clp = lforw(clp)) {
if ((clp == curbp->b_linep) || (row > curwp->w_ntrows))
return (-1);
row++;
}
return (row + curwp->w_toprow);
}
/*
* turn LINE and char pointer into display col.
*/
truecol(clp, col)
LINE *clp;
{
register int i, tcol;
unsigned c;
for (i = tcol = 0; i < col; i++) {
c = lgetc(clp, i);
switch (dblchr(c)) {
case 2:
taber(tcol);
break;
case 1:
tcol++;
}
tcol++;
}
if (tcol >= term.t_ncol) /* too far out */
tcol = term.t_ncol - 1;
return (tcol);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.