/* ENIC CP/M-68K 8.4.1997. * 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 #include "ed.h" #define WFDEBUG 0 /* Window flag debug. */ typedef struct VIDEO { short v_flag; /* Flags */ char v_text[]; /* 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; (*term.t_open)(); 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_ncol) vp->v_text[term.t_ncol-1] = '$'; else if (c == '\t') { do { vtputc(' '); } while ((vtcol&0x07) != 0); } 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 < 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; register WINDOW *wp; register VIDEO *vp1; register VIDEO *vp2; register int i; register int j; register int c; 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; iw_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); } vscreen[i]->v_flag |= VFCHG; vtmove(i, 0); for (j=0; jw_flag&(WFEDIT|WFHARD)) != 0) { 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; jw_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 wp = wp->w_wndp; } /* 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 = 0; 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 >= term.t_ncol) /* Long line. */ curcol = term.t_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 != FALSE) { for (i=0; iv_flag |= VFCHG; vp1 = pscreen[i]; for (j=0; jv_text[j] = ' '; } movecursor(0, 0); /* Erase the screen. */ (*term.t_eeop)(); 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; iv_flag&VFCHG) != 0) { vp1->v_flag &= ~VFCHG; vp2 = pscreen[i]; updateline(i, &vp1->v_text[0], &vp2->v_text[0]); } } /* Finally, update the hardware cursor and flush out buffers. */ movecursor(currow, curcol); (*term.t_flush)(); } /* * 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. The RAINBOW version * of this routine uses fast video. */ updateline(row, vline, pline) char vline[]; char pline[]; { #if RAINBOW register char *cp1; register char *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 char *cp1; register char *cp2; register char *cp3; register char *cp4; register char *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. */ (*term.t_putchar)(*cp1); ++ttcol; *cp2++ = *cp1++; } if (cp5 != cp3) { /* Erase. */ (*term.t_eeol)(); 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 char *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 = " MicroEMACS -- "; /* 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. */ 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; (*term.t_move)(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); (*term.t_eeol)(); (*term.t_flush)(); 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 != 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) 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; if (buf[0] == 0) return (FALSE); return (TRUE); } mlwrite(prompt); for (;;) { c = (*term.t_getchar)(); switch (c) { case 0x0D: /* Return, end of line */ buf[cpos++] = 0; if (kbdmip != NULL) { if (kbdmip+cpos > &kbdm[NKBDM-3]) { ctrlg(FALSE, 0); (*term.t_flush)(); return (ABORT); } for (i=0; i