home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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"
-
- typedef struct VIDEO {
- short v_flag; /* Flags */
- char v_text[1]; /* Screen data. */
- } VIDEO;
-
- #define VFCHG 0x0001 /* Changed. */
-
- 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;
-
- scr_setup();
- vscreen = (VIDEO **) malloc(NROW*sizeof(VIDEO *));
- if (vscreen == NULL)
- exit(1);
- pscreen = (VIDEO **) malloc(NROW*sizeof(VIDEO *));
- if (pscreen == NULL)
- exit(1);
- for (i=0; i<NROW; ++i) {
- vp = (VIDEO *) malloc(sizeof(VIDEO)+NCOL);
- if (vp == NULL)
- exit(1);
- vscreen[i] = vp;
- vp = (VIDEO *) malloc(sizeof(VIDEO)+NCOL);
- if (vp == NULL)
- exit(1);
- 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(NROW, 0);
- scr_clrl();
- }
-
- /*
- * 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;
- }
-
- /*
- * 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)
- register int c;
- {
- register VIDEO *vp;
-
- vp = vscreen[vtrow];
- if (vtcol >= NCOL)
- vp->v_text[NCOL-1] = '$';
- else if (c == '\t') {
- do {
- vtputc(' ');
- } while (vtcol&0x07);
- } else if (c<0x20 || c==0x7F) {
- vtputc('^');
- vtputc(c ^ 0x40);
- } else
- vp->v_text[vtcol++] = c;
- }
-
- /*
- * 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 < 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;
- register WINDOW *wp;
- register VIDEO *vp1;
- register VIDEO *vp2;
- register int i;
- register int j;
- register int c;
-
- for (wp = wheadp; wp; wp = wp->w_wndp) {
-
- /* Look at any window with update flags set on. */
-
- if (wp->w_flag) {
-
- /* If not force reframe, check the framing. */
-
- if (!(wp->w_flag&WFFORCE)) {
- 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 && 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);
- }
- vscreen[i]->v_flag |= VFCHG;
- vtmove(i, 0);
- for (j=0; j<llength(lp); ++j) vtputc(lgetc(lp, j));
- vteeol();
- } else if (wp->w_flag&(WFEDIT|WFHARD)) {
- while (i < wp->w_toprow+wp->w_ntrows) {
- vscreen[i]->v_flag |= VFCHG;
- 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 (wp->w_flag&(WFFORCE|WFHARD|WFMODE)) modeline(wp);
- wp->w_flag = wp->w_force = 0;
- }
- }
-
- /* Always recompute the row and column number of the hardware */
- /* cursor. This is the only update for simple moves. */
-
- lp = curwp->w_linep;
- currow = curwp->w_toprow;
- while (lp != curwp->w_dotp) {
- ++currow;
- lp = lforw(lp);
- }
- curcol = i = 0;
- while (i < curwp->w_doto) {
- c = lgetc(lp, i++);
- if (c == '\t') curcol |= 0x07;
- else if (c<0x20 || c==0x7F) ++curcol;
- ++curcol;
- }
- if (curcol >= NCOL) /* Long line. */
- curcol = NCOL-1;
-
- /* 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) {
- for (i=0; i<NROW; ++i) {
- vscreen[i]->v_flag |= VFCHG;
- vp1 = pscreen[i];
- for (j=0; j<NCOL; ++j) vp1->v_text[j] = ' ';
- }
- movecursor(0, 0); /* Erase the screen. */
- scr_cls();
- mpresf = sgarbf = FALSE; /* Erase-page clears */
- /* 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<NROW; ++i) {
- vp1 = vscreen[i];
- if (vp1->v_flag&VFCHG) {
- vp1->v_flag &= ~VFCHG;
- vp2 = pscreen[i];
- updateline(i, vp1->v_text, vp2->v_text);
- }
- }
-
- /* Finally, update the hardware cursor and flush out buffers. */
-
- movecursor(currow, curcol);
- }
-
- /*
- * 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 an
- * exploit erase to end of line.
- */
- updateline(row, vline, pline)
- char vline[];
- char pline[];
- {
- register char *cp1;
- register char *cp2;
- register char *cp3;
- register char *cp4;
- register char *cp5;
- register int nbflag;
-
- cp1 = vline; /* Compute left match. */
- cp2 = pline;
- while (cp1!=vline+NCOL && *cp1==*cp2) {
- ++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+NCOL) /* All equal. */
- return;
- nbflag = FALSE;
- cp3 = vline+NCOL; /* Compute right match. */
- cp4 = pline+NCOL;
- while (cp3[-1] == cp4[-1]) {
- --cp3;
- --cp4;
- if (*cp3 != ' ') /* 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, cp1-vline); /* Go to start of line. */
- while (cp1 != cp5) { /* Ordinary. */
- scr_co(*cp1);
- ++ttcol;
- *cp2++ = *cp1++;
- }
- if (cp5 != cp3) { /* Erase. */
- scr_clrl();
- while (cp1 != cp3) *cp2++ = *cp1++;
- }
- }
-
- /*
- * 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 char *cp;
- register int c;
- register int n;
- register BUFFER *bp;
- register int ratio;
- static int old_ratio = -1;
-
- ratio = percent();
- if (wp->w_flag&WFMODE==0 && ratio==old_ratio) return;
- old_ratio = ratio;
-
- 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) /* "*" if changed. */
- vtputc('*');
- else
- vtputc('-');
- n = 2;
- cp = " MicroEMACS -- "; /* Buffer name. */
- while (c = *cp++) {
- vtputc(c);
- ++n;
- }
- cp = bp->b_bname;
- while (c = *cp++) {
- vtputc(c);
- ++n;
- }
- vtputc(' ');
- ++n;
- if (*bp->b_fname) { /* File name. */
- cp = "-- File: ";
- while (c = *cp++) {
- vtputc(c);
- ++n;
- }
- cp = bp->b_fname;
- while (c = *cp++) {
- vtputc(c);
- ++n;
- }
- vtputc(' ');
- ++n;
- }
-
- /* Display the percentage position within file. */
-
- vtputc('('); n++;
- if (ratio >= 100) {
- vtputc('1');
- vtputc('0');
- vtputc('0');
- n += 3;
- }
- else {
- vtputc((ratio > 9) ? '0'+ratio/10 : ' ');
- vtputc('0'+ratio%10);
- n += 2;
- }
- vtputc('%');
- vtputc(')');
- vtputc(' ');
- n += 3;
-
- vtputc('-');
- vtputc('-');
- n += 2;
-
- /* tell the user if he is in INSert or OVRwrite mode for the
- current buffer */
- cp ="INS";
- if (curbp->b_flag & BFOVRWRITE)
- cp = "OVR";
- while( c=*cp++)
- {
- vtputc(c);
- n++;
- }
-
- while (n < NCOL) { /* Pad to full width. */
- vtputc('-');
- ++n;
-
- }
-
- }
-
-
- /*
- * Compute percent of file we are at
- */
- percent()
- {
- register LINE *clp;
- register long nch;
- register int cbo;
- register long nbc;
- register int ratio;
- register int cac;
-
- clp = lforw(curbp->b_linep); /* Grovel the data. */
- cbo = 0;
- nch = 0;
- for (;;) {
- if (clp==curwp->w_dotp && cbo==curwp->w_doto) {
- nbc = nch;
- if (cbo == llength(clp))
- cac = '\n';
- else
- cac = lgetc(clp, cbo);
- }
- if (cbo == llength(clp)) {
- if (clp == curbp->b_linep)
- break;
- clp = lforw(clp);
- cbo = 0;
- } else
- ++cbo;
- ++nch;
- }
- ratio = 0; /* Ratio before dot. */
- if (nch)
- ratio = (100L*nbc) / nch;
-
- return ratio;
- }
-
-
- /*
- * 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;
- scr_rowcol(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(NROW, 0);
- scr_clrl();
- 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)
- char *prompt;
- {
- register int s;
- char buf[64];
-
- for (;;) {
- strcpy(buf, prompt);
- strcat(buf, " [y/n]? ");
- s = mlreply(buf, buf, sizeof(buf));
- if (s == ABORT)
- return (ABORT);
- if (s) {
- if (*buf=='y' || *buf=='Y')
- return (TRUE);
- if (*buf=='n' || *buf=='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)
- char *prompt;
- char *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;
- return (!!*buf);
- }
- mlwrite(prompt);
- for (;;) {
- c = scr_ci();
- switch (c) {
- case 0x0D: /* Return, end of line */
- buf[cpos++] = 0;
- if (kbdmip != NULL) {
- if (kbdmip+cpos > kbdm+NKBDM-3) {
- ctrlg(FALSE, 0);
- return (ABORT);
- }
- for (i=0; i<cpos; ++i)
- *kbdmip++ = buf[i];
- }
- scr_co('\r');
- ttcol = 0;
- return !!*buf;
-
- case 0x07: /* Bell, abort */
- scr_co('^');
- scr_co('G');
- ttcol += 2;
- ctrlg(FALSE, 0);
- return (ABORT);
-
- case 0x7F: /* Rubout, erase */
- case 0x08: /* Backspace, erase */
- if (cpos) {
- scr_co('\b');
- scr_co(' ');
- scr_co('\b');
- --ttcol;
- if (buf[--cpos] < 0x20) {
- scr_co('\b');
- scr_co(' ');
- scr_co('\b');
- --ttcol;
- }
- }
- break;
-
- case 0x15: /* C-U, kill */
- while (cpos) {
- scr_co('\b');
- scr_co(' ');
- scr_co('\b');
- --ttcol;
- if (buf[--cpos] < 0x20) {
- scr_co('\b');
- scr_co(' ');
- scr_co('\b');
- --ttcol;
- }
- }
- break;
-
- default:
- if (cpos < nbuf-1) {
- buf[cpos++] = c;
- if (c < ' ') {
- scr_co('^');
- ++ttcol;
- c ^= 0x40;
- }
- scr_co(c);
- ++ttcol;
- }
- }
- }
- }
-
- /*
- * 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, arg)
- char *fmt;
- {
- register int c;
- register char *ap;
-
- movecursor(NROW, 0);
- ap = (char *) &arg;
- while (c = *fmt++) {
- if (c != '%') {
- scr_co(c);
- ++ttcol;
- } else {
- c = *fmt++;
- switch (c) {
- case 'd':
- mlputi(*(int *)ap, 10);
- ap += sizeof(int);
- break;
-
- case 'o':
- mlputi(*(int *)ap, 8);
- ap += sizeof(int);
- break;
-
- case 'x':
- mlputi(*(int *)ap, 16);
- ap += sizeof(int);
- break;
-
- case 'D':
- mlputli(*(long *)ap, 10);
- ap += sizeof(long);
- break;
-
- case 's':
- mlputs(*(char **)ap);
- ap += sizeof(char *);
- break;
-
- default:
- scr_co(c);
- ++ttcol;
- }
- }
- }
- scr_clrl();
- 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 char *s;
- {
- register int c;
-
- while (c = *s++) {
- scr_co(c);
- ++ttcol;
- }
- }
-
- /*
- * Write out an integer, in
- * the specified radix. Update the physical
- * cursor position. This will not handle any
- * negative numbers; maybe it should.
- */
- mlputi(i, r)
- {
- register int q;
- static char hexdigits[] = "0123456789ABCDEF";
-
- if (i < 0) {
- i = -i;
- scr_co('-');
- }
- q = i/r;
- if (q)
- mlputi(q, r);
- scr_co(hexdigits[i%r]);
- ++ttcol;
- }
-
- /*
- * do the same except as a long integer.
- */
- mlputli(l, r)
- long l;
- {
- register long q;
-
- if (l < 0) {
- l = -l;
- scr_co('-');
- }
- q = l/r;
- if (q)
- mlputli(q, r);
- scr_co((int)(l%r)+'0');
- ++ttcol;
- }