home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d3xx / d352 / mg.lha / MG / src.LZH / mg / display.c < prev    next >
C/C++ Source or Header  |  1990-05-23  |  24KB  |  904 lines

  1. /*
  2.  * The functions in this file handle redisplay. The redisplay system knows
  3.  * almost nothing about the editing process; the editing functions do,
  4.  * however, set some hints to eliminate a lot of the grinding. There is more
  5.  * that can be done; the "vtputc" interface is a real pig. Two conditional
  6.  * compilation flags; the GOSLING flag enables dynamic programming redisplay,
  7.  * using the algorithm published by Jim Gosling in SIGOA. The MEMMAP changes
  8.  * things around for memory mapped video. With both off, the terminal is a
  9.  * VT52. 
  10.  */
  11. #include    "nondynvid.h"
  12. #include    "nrow.h"
  13. #include    "ncol.h"
  14. #include    "notab.h"
  15.  
  16. #include    "def.h"
  17. #include    "line.h"
  18. #include    "kbd.h"
  19. #include    "buffer.h"
  20. #include    "window.h"
  21.  
  22. #ifdef    ANSI
  23. #include <string.h>
  24. #endif
  25.  
  26. VOID 
  27. updext(), modeline();
  28.  
  29.  
  30. #ifdef    STANDOUT_GLITCH
  31. extern int      SG;        /* number of standout glitches     */
  32. #endif
  33.  
  34. /*
  35.  * These two things are normally found in sysdef.h. They are currently
  36.  * defined to get the smallest code. They can be changed on a
  37.  * system-by-system basis to get better code generation if that happens on
  38.  * your system. For instance, VAXen prefer them both to both to be ints. 
  39.  */
  40. #ifndef    XCHAR
  41. #define XCHAR    char
  42. #endif
  43.  
  44. #ifndef    XSHORT
  45. #define XSHORT    short int
  46. #endif
  47.  
  48. /*
  49.  * A video structure always holds an array of characters whose length is
  50.  * equal to the longest line possible. Only some of this is used if "ncol"
  51.  * isn't the same as "NCOL". 
  52.  */
  53. struct video {
  54.     short           v_hash;    /* Hash code, for compares.     */
  55.     short           v_flag;    /* Flag word.             */
  56.     short           v_color;/* Color of the line.         */
  57.     XSHORT          v_cost;    /* Cost of display.         */
  58.     char            v_text[NCOL];    /* The actual characters.     */
  59. };
  60.  
  61. #define VFCHG    0x0001        /* Changed.             */
  62. #define VFHBAD    0x0002        /* Hash and cost are bad.     */
  63. #define VFEXT    0x0004        /* extended line (beond ncol)     */
  64.  
  65. /*
  66.  * SCORE structures hold the optimal trace trajectory, and the cost of
  67.  * redisplay, when the dynamic programming redisplay code is used. If no
  68.  * fancy redisplay, this isn't used. The trace index fields can be "char",
  69.  * and the score a "short", but this makes the code worse on the VAX. 
  70.  */
  71. struct score {
  72.     XCHAR           s_itrace;    /* "i" index for track back.     */
  73.     XCHAR           s_jtrace;    /* "j" index for trace back.     */
  74.     XSHORT          s_cost;    /* Display cost.         */
  75. };
  76.  
  77. int             sgarbf = TRUE;    /* TRUE if screen is garbage.     */
  78. int             vtrow = 0;    /* Virtual cursor row.         */
  79. int             vtcol = 0;    /* Virtual cursor column.     */
  80. int             tthue = CNONE;    /* Current color.         */
  81. int             ttrow = HUGE;    /* Physical cursor row.         */
  82. int             ttcol = HUGE;    /* Physical cursor column.     */
  83. int             tttop = HUGE;    /* Top of scroll region.     */
  84. int             ttbot = HUGE;    /* Bottom of scroll region.     */
  85. int             lbound = 0;    /* leftmost bound of the current line */
  86. /* being displayed         */
  87.  
  88. struct video   *vscreen[NROW - 1];    /* Edge vector, virtual.     */
  89. struct video   *pscreen[NROW - 1];    /* Edge vector, physical.     */
  90. struct video    vid[2 * (NROW - 1)];    /* Actual screen data         */
  91. struct video    blanks;        /* Blank line image.         */
  92.  
  93. /*
  94.  * Some prototypes 
  95.  */
  96. static VOID ucopy 
  97. PROTO((struct video * vvp, struct video * pvp));
  98. static VOID uline 
  99. PROTO((int row, struct video * vvp, struct video * pvp));
  100. static VOID hash 
  101. PROTO((struct video * vp));
  102. VOID setscores 
  103. PROTO((int offs, int size));
  104. VOID traceback 
  105. PROTO((int offs, int size, int i, int j));
  106.  
  107.  
  108. #ifndef    NONDYNVID
  109. /*
  110.  * This matrix is written as an array because we do funny things in the
  111.  * "setscores" routine, which is very compute intensive, to make the
  112.  * subscripts go away. It would be "SCORE    score[NROW][NROW]" in old
  113.  * speak. Look at "setscores" to understand what is up. 
  114.  */
  115. struct score    dscore[NROW * NROW];
  116. #endif
  117.  
  118. /*
  119.  * Initialize the data structures used by the display code. The edge vectors
  120.  * used to access the screens are set up. The operating system's terminal I/O
  121.  * channel is set up. Fill the "blanks" array with ASCII blanks. The rest is
  122.  * done at compile time. The original window is marked as needing full
  123.  * update, and the physical screen is marked as garbage, so all the right
  124.  * stuff happens on the first call to redisplay. 
  125.  */
  126. VOID
  127. vtinit()
  128. {
  129.     register struct video *vp;
  130.     register int    i;
  131.  
  132.     ttopen();
  133.     ttinit();
  134.     vp = &vid[0];
  135.     for (i = 0; i < NROW - 1; ++i) {
  136.         vscreen[i] = vp;
  137.         ++vp;
  138.         pscreen[i] = vp;
  139.         ++vp;
  140.     }
  141.     blanks.v_color = CTEXT;
  142.     for (i = 0; i < NCOL; ++i)
  143.         blanks.v_text[i] = ' ';
  144. }
  145.  
  146. /*
  147.  * Tidy up the virtual display system in anticipation of a return back to the
  148.  * host operating system. Right now all we do is position the cursor to the
  149.  * last line, erase the line, and close the terminal channel. 
  150.  */
  151. VOID
  152. vttidy()
  153. {
  154.     ttcolor(CTEXT);
  155.     ttnowindow();        /* No scroll window.     */
  156.     ttmove(nrow - 1, 0);    /* Echo line.         */
  157.     tteeol();
  158.     tttidy();
  159.     ttflush();
  160.     ttclose();
  161. }
  162.  
  163. /*
  164.  * Move the virtual cursor to an origin 0 spot on the virtual display screen.
  165.  * I could store the column as a character pointer to the spot on the line,
  166.  * which would make "vtputc" a little bit more efficient. No checking for
  167.  * errors. 
  168.  */
  169. VOID
  170. vtmove(row, col)
  171. {
  172.     vtrow = row;
  173.     vtcol = col;
  174. }
  175.  
  176. /*
  177.  * Write a character to the virtual display, dealing with long lines and the
  178.  * display of unprintable things like control characters. Also expand tabs
  179.  * every 8 columns. This code only puts printing characters into the virtual
  180.  * display image. Special care must be taken when expanding tabs. On a screen
  181.  * whose width is not a multiple of 8, it is possible for the virtual cursor
  182.  * to hit the right margin before the next tab stop is reached. This makes
  183.  * the tab code loop if you are not careful. Three guesses how we found this. 
  184.  */
  185. VOID
  186. vtputc(c)
  187.     register int    c;
  188. {
  189.     register struct video *vp;
  190.  
  191.     vp = vscreen[vtrow];
  192.     if (vtcol >= ncol)
  193.         vp->v_text[ncol - 1] = '$';
  194.     else if (c == '\t'
  195. #ifdef    NOTAB
  196.          && !(curbp->b_flag & BFNOTAB)
  197. #endif
  198.         ) {
  199.         do {
  200.             vtputc(' ');
  201.         } while (vtcol < ncol && (vtcol & 0x07) != 0);
  202.     } else if (ISCTRL(c)) {
  203.         vtputc('^');
  204.         vtputc(CCHR(c));
  205.     } else
  206.         vp->v_text[vtcol++] = c;
  207. }
  208.  
  209. /*
  210.  * Put a character to the virtual screen in an extended line.  If we are not
  211.  * yet on left edge, don't print it yet.  Check for overflow on the right
  212.  * margin. 
  213.  */
  214. VOID
  215. vtpute(c)
  216.     int             c;
  217. {
  218.     register struct video *vp;
  219.  
  220.     vp = vscreen[vtrow];
  221.  
  222.     if (vtcol >= ncol)
  223.         vp->v_text[ncol - 1] = '$';
  224.     else if (c == '\t'
  225. #ifdef    NOTAB
  226.          && !(curbp->b_flag & BFNOTAB)
  227. #endif
  228.         ) {
  229.         do {
  230.             vtpute(' ');
  231.         }
  232.         while (((vtcol + lbound) & 0x07) != 0 && vtcol < ncol);
  233.     } else if (ISCTRL(c) != FALSE) {
  234.         vtpute('^');
  235.         vtpute(CCHR(c));
  236.     } else {
  237.         if (vtcol >= 0)
  238.             vp->v_text[vtcol] = c;
  239.         ++vtcol;
  240.     }
  241. }
  242.  
  243. /*
  244.  * Erase from the end of the software cursor to the end of the line on which
  245.  * the software cursor is located. The display routines will decide if a
  246.  * hardware erase to end of line command should be used to display this. 
  247.  */
  248. VOID
  249. vteeol()
  250. {
  251.     register struct video *vp;
  252.  
  253.     vp = vscreen[vtrow];
  254.     while (vtcol < ncol)
  255.         vp->v_text[vtcol++] = ' ';
  256. }
  257.  
  258. /*
  259.  * Make sure that the display is right. This is a three part process. First,
  260.  * scan through all of the windows looking for dirty ones. Check the framing,
  261.  * and refresh the screen. Second, make sure that "currow" and "curcol" are
  262.  * correct for the current window. Third, make the virtual and physical
  263.  * screens the same. 
  264.  */
  265. VOID
  266. update()
  267. {
  268.     register struct line *lp;
  269.     register struct window *wp;
  270.     register struct video *vp1;
  271.     struct video   *vp2;
  272.     register int    i;
  273.     register int    j;
  274.     register int    c;
  275.     register int    hflag;
  276.     register int    currow;
  277.     register int    curcol;
  278. #ifndef    NONDYNVID
  279.     register int    offs;
  280.     register int    size;
  281. #endif
  282.     VOID            traceback();
  283.  
  284.     if (typeahead())
  285.         return;
  286.     if (sgarbf) {        /* must update everything */
  287.         wp = wheadp;
  288.         while (wp != NULL) {
  289.             wp->w_flag |= WFMODE | WFHARD;
  290.             wp = wp->w_wndp;
  291.         }
  292.     }
  293.     hflag = FALSE;        /* Not hard.         */
  294.     wp = wheadp;
  295.     while (wp != NULL) {
  296.         if (wp->w_flag != 0) {    /* Need update.         */
  297.             if ((wp->w_flag & WFFORCE) == 0) {
  298.                 lp = wp->w_linep;
  299.                 for (i = 0; i < wp->w_ntrows; ++i) {
  300.                     if (lp == wp->w_dotp)
  301.                         goto out;
  302.                     if (lp == wp->w_bufp->b_linep)
  303.                         break;
  304.                     lp = lforw(lp);
  305.                 }
  306.             }
  307.             i = wp->w_force;    /* Reframe this one.     */
  308.             if (i > 0) {
  309.                 --i;
  310.                 if (i >= wp->w_ntrows)
  311.                     i = wp->w_ntrows - 1;
  312.             } else if (i < 0) {
  313.                 i += wp->w_ntrows;
  314.                 if (i < 0)
  315.                     i = 0;
  316.             } else
  317.                 i = wp->w_ntrows / 2;
  318.             lp = wp->w_dotp;
  319.             while (i != 0 && lback(lp) != wp->w_bufp->b_linep) {
  320.                 --i;
  321.                 lp = lback(lp);
  322.             }
  323.             wp->w_linep = lp;
  324.             wp->w_flag |= WFHARD;    /* Force full.         */
  325.     out:
  326.             lp = wp->w_linep;    /* Try reduced update.     */
  327.             i = wp->w_toprow;
  328.             if ((wp->w_flag & ~WFMODE) == WFEDIT) {
  329.                 while (lp != wp->w_dotp) {
  330.                     ++i;
  331.                     lp = lforw(lp);
  332.                 }
  333.                 vscreen[i]->v_color = CTEXT;
  334.                 vscreen[i]->v_flag |= (VFCHG | VFHBAD);
  335.                 vtmove(i, 0);
  336.                 for (j = 0; j < llength(lp); ++j)
  337.                     vtputc(lgetc(lp, j));
  338.                 vteeol();
  339.             } else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0) {
  340.                 hflag = TRUE;
  341.                 while (i < wp->w_toprow + wp->w_ntrows) {
  342.                     vscreen[i]->v_color = CTEXT;
  343.                     vscreen[i]->v_flag |= (VFCHG | VFHBAD);
  344.                     vtmove(i, 0);
  345.                     if (lp != wp->w_bufp->b_linep) {
  346.                         for (j = 0; j < llength(lp); ++j)
  347.                             vtputc(lgetc(lp, j));
  348.                         lp = lforw(lp);
  349.                     }
  350.                     vteeol();
  351.                     ++i;
  352.                 }
  353.             }
  354.             if ((wp->w_flag & WFMODE) != 0)
  355.                 modeline(wp);
  356.             wp->w_flag = 0;
  357.             wp->w_force = 0;
  358.         }
  359.         wp = wp->w_wndp;
  360.     }
  361.     lp = curwp->w_linep;    /* Cursor location.     */
  362.     currow = curwp->w_toprow;
  363.     while (lp != curwp->w_dotp) {
  364.         ++currow;
  365.         lp = lforw(lp);
  366.     }
  367.     curcol = 0;
  368.     i = 0;
  369.     while (i < curwp->w_doto) {
  370.         c = lgetc(lp, i++);
  371.         if (c == '\t'
  372. #ifdef    NOTAB
  373.             && !(curbp->b_flag & BFNOTAB)
  374. #endif
  375.             )
  376.             curcol |= 0x07;
  377.         else if (ISCTRL(c) != FALSE)
  378.             ++curcol;
  379.         ++curcol;
  380.     }
  381.     if (curcol >= ncol - 1) {    /* extended line. */
  382.         /* flag we are extended and changed */
  383.         vscreen[currow]->v_flag |= VFEXT | VFCHG;
  384.         updext(currow, curcol);    /* and output extended line */
  385.     } else
  386.         lbound = 0;    /* not extended line */
  387.  
  388.     /*
  389.      * make sure no lines need to be de-extended because the cursor is no
  390.      * longer on them 
  391.      */
  392.  
  393.     wp = wheadp;
  394.  
  395.     while (wp != NULL) {
  396.         lp = wp->w_linep;
  397.         i = wp->w_toprow;
  398.         while (i < wp->w_toprow + wp->w_ntrows) {
  399.             if (vscreen[i]->v_flag & VFEXT) {
  400.                 /* always flag extended lines as changed */
  401.                 vscreen[i]->v_flag |= VFCHG;
  402.                 if ((wp != curwp) || (lp != wp->w_dotp) ||
  403.                     (curcol < ncol - 1)) {
  404.                     vtmove(i, 0);
  405.                     for (j = 0; j < llength(lp); ++j)
  406.                         vtputc(lgetc(lp, j));
  407.                     vteeol();
  408.                     /* this line no longer is extended */
  409.                     vscreen[i]->v_flag &= ~VFEXT;
  410.                 }
  411.             }
  412.             lp = lforw(lp);
  413.             ++i;
  414.         }
  415.         /* if garbaged then fix up mode lines */
  416.         if (sgarbf != FALSE)
  417.             vscreen[i]->v_flag |= VFCHG;
  418.         /* and onward to the next window */
  419.         wp = wp->w_wndp;
  420.     }
  421.  
  422.     if (sgarbf != FALSE) {    /* Screen is garbage.     */
  423.         sgarbf = FALSE;    /* Erase-page clears     */
  424.         epresf = FALSE;    /* the message area.     */
  425.         tttop = HUGE;    /* Forget where you set */
  426.         ttbot = HUGE;    /* scroll region.     */
  427.         tthue = CNONE;    /* Color unknown.     */
  428.         ttmove(0, 0);
  429.         tteeop();
  430.         for (i = 0; i < nrow - 1; ++i) {
  431.             uline(i, vscreen[i], &blanks);
  432.             ucopy(vscreen[i], pscreen[i]);
  433.         }
  434.         ttmove(currow, curcol - lbound);
  435.         ttflush();
  436.         return;
  437.     }
  438. #ifndef    NONDYNVID
  439.     if (hflag != FALSE) {    /* Hard update?         */
  440.         for (i = 0; i < nrow - 1; ++i) {    /* Compute hash data.     */
  441.             hash(vscreen[i]);
  442.             hash(pscreen[i]);
  443.         }
  444.         offs = 0;    /* Get top match.     */
  445.         while (offs != nrow - 1) {
  446.             vp1 = vscreen[offs];
  447.             vp2 = pscreen[offs];
  448.             if (vp1->v_color != vp2->v_color
  449.                 || vp1->v_hash != vp2->v_hash)
  450.                 break;
  451.             uline(offs, vp1, vp2);
  452.             ucopy(vp1, vp2);
  453.             ++offs;
  454.         }
  455.         if (offs == nrow - 1) {    /* Might get it all.     */
  456.             ttmove(currow, curcol - lbound);
  457.             ttflush();
  458.             return;
  459.         }
  460.         size = nrow - 1;/* Get bottom match.     */
  461.         while (size != offs) {
  462.             vp1 = vscreen[size - 1];
  463.             vp2 = pscreen[size - 1];
  464.             if (vp1->v_color != vp2->v_color
  465.                 || vp1->v_hash != vp2->v_hash)
  466.                 break;
  467.             uline(size - 1, vp1, vp2);
  468.             ucopy(vp1, vp2);
  469.             --size;
  470.         }
  471.         if ((size -= offs) == 0)    /* Get screen size.     */
  472.             panic("Illegal screen size in update");
  473.         setscores(offs, size);    /* Do hard update.     */
  474.         traceback(offs, size, size, size);
  475.         for (i = 0; i < size; ++i)
  476.             ucopy(vscreen[offs + i], pscreen[offs + i]);
  477.         ttmove(currow, curcol - lbound);
  478.         ttflush();
  479.         return;
  480.     }
  481. #endif
  482.     for (i = 0; i < nrow - 1; ++i) {    /* Easy update.         */
  483.         vp1 = vscreen[i];
  484.         vp2 = pscreen[i];
  485.         if ((vp1->v_flag & VFCHG) != 0) {
  486.             uline(i, vp1, vp2);
  487.             ucopy(vp1, vp2);
  488.         }
  489.     }
  490.     ttmove(currow, curcol - lbound);
  491.     ttflush();
  492. }
  493.  
  494. /*
  495.  * Update a saved copy of a line, kept in a VIDEO structure. The "vvp" is the
  496.  * one in the "vscreen". The "pvp" is the one in the "pscreen". This is
  497.  * called to make the virtual and physical screens the same when display has
  498.  * done an update. 
  499.  */
  500. static          VOID
  501. ucopy(vvp, pvp)
  502.     register struct video *vvp;
  503.     register struct video *pvp;
  504. {
  505.  
  506.     vvp->v_flag &= ~VFCHG;    /* Changes done.     */
  507.     pvp->v_flag = vvp->v_flag;    /* Update model.     */
  508.     pvp->v_hash = vvp->v_hash;
  509.     pvp->v_cost = vvp->v_cost;
  510.     pvp->v_color = vvp->v_color;
  511.     bcopy(vvp->v_text, pvp->v_text, ncol);
  512. }
  513.  
  514. /*
  515.  * updext: update the extended line which the cursor is currently on at a
  516.  * column greater than the terminal width. The line will be scrolled right or
  517.  * left to let the user see where the cursor is 
  518.  */
  519. VOID
  520. updext(currow, curcol)
  521.     int             currow, curcol;
  522. {
  523.     register struct line *lp;    /* pointer to current line */
  524.     register int    j;    /* index into line */
  525.  
  526.     /* calculate what column the left bound should be */
  527.     /* (force cursor into middle half of screen) */
  528.     lbound = curcol - (curcol % (ncol >> 1)) - (ncol >> 2);
  529.     /* scan through the line outputing characters to the virtual screen */
  530.     /* once we reach the left edge */
  531.     vtmove(currow, -lbound);/* start scanning offscreen */
  532.     lp = curwp->w_dotp;    /* line to output */
  533.     for (j = 0; j < llength(lp); ++j)    /* until the end-of-line */
  534.         vtpute(lgetc(lp, j));
  535.     vteeol();        /* truncate the virtual line */
  536.     vscreen[currow]->v_text[0] = '$';    /* and put a '$' in column 1 */
  537. }
  538.  
  539. /*
  540.  * Update a single line. This routine only uses basic functionality (no
  541.  * insert and delete character, but erase to end of line). The "vvp" points
  542.  * at the VIDEO structure for the line on the virtual screen, and the "pvp"
  543.  * is the same for the physical screen. Avoid erase to end of line when
  544.  * updating CMODE color lines, because of the way that reverse video works on
  545.  * most terminals. 
  546.  */
  547. static          VOID
  548. uline(row, vvp, pvp)
  549.     struct video   *vvp;
  550.     struct video   *pvp;
  551. {
  552. #ifdef    MEMMAP
  553.     putline(row + 1, 1, &vvp->v_text[0]);
  554. #else
  555.     register char  *cp1;
  556.     register char  *cp2;
  557.     register char  *cp3;
  558.     char           *cp4;
  559.     char           *cp5;
  560.     register int    nbflag;
  561.  
  562.     if (vvp->v_color != pvp->v_color) {    /* Wrong color, do a     */
  563.         ttmove(row, 0);    /* full redraw.         */
  564. #ifdef    STANDOUT_GLITCH
  565.         if (pvp->v_color != CTEXT && SG >= 0)
  566.             tteeol();
  567. #endif
  568.         ttcolor(vvp->v_color);
  569. #ifdef    STANDOUT_GLITCH
  570.         cp1 = &vvp->v_text[SG > 0 ? SG : 0];
  571.         /*
  572.          * the odd code for SG==0 is to avoid putting the invisable
  573.          * glitch character on the next line. (Hazeltine executive 80
  574.          * model 30) 
  575.          */
  576.         cp2 = &vvp->v_text[ncol - (SG >= 0 ? (SG != 0 ? SG : 1) : 0)];
  577. #else
  578.         cp1 = &vvp->v_text[0];
  579.         cp2 = &vvp->v_text[ncol];
  580. #endif
  581.         while (cp1 != cp2) {
  582.             ttputc(*cp1++);
  583.             ++ttcol;
  584.         }
  585. #ifndef MOVE_STANDOUT
  586.         ttcolor(CTEXT);
  587. #endif
  588.         return;
  589.     }
  590.     cp1 = &vvp->v_text[0];    /* Compute left match.     */
  591.     cp2 = &pvp->v_text[0];
  592.     while (cp1 != &vvp->v_text[ncol] && cp1[0] == cp2[0]) {
  593.         ++cp1;
  594.         ++cp2;
  595.     }
  596.     if (cp1 == &vvp->v_text[ncol])    /* All equal.         */
  597.         return;
  598.     nbflag = FALSE;
  599.     cp3 = &vvp->v_text[ncol];    /* Compute right match. */
  600.     cp4 = &pvp->v_text[ncol];
  601.     while (cp3[-1] == cp4[-1]) {
  602.         --cp3;
  603.         --cp4;
  604.         if (cp3[0] != ' ')    /* Note non-blanks in     */
  605.             nbflag = TRUE;    /* the right match.     */
  606.     }
  607.     cp5 = cp3;        /* Is erase good?     */
  608.     if (nbflag == FALSE && vvp->v_color == CTEXT) {
  609.         while (cp5 != cp1 && cp5[-1] == ' ')
  610.             --cp5;
  611.         /* Alcyon hack */
  612.         if ((int) (cp3 - cp5) <= tceeol)
  613.             cp5 = cp3;
  614.     }
  615.     /* Alcyon hack */
  616.     ttmove(row, (int) (cp1 - &vvp->v_text[0]));
  617. #ifdef    STANDOUT_GLITCH
  618.     if (vvp->v_color != CTEXT && SG > 0) {
  619.         if (cp1 < &vvp->v_text[SG])
  620.             cp1 = &vvp->v_text[SG];
  621.         if (cp5 > &vvp->v_text[ncol - SG])
  622.             cp5 = &vvp->v_text[ncol - SG];
  623.     } else if (SG < 0)
  624. #endif
  625.         ttcolor(vvp->v_color);
  626.     while (cp1 != cp5) {
  627.         ttputc(*cp1++);
  628.         ++ttcol;
  629.     }
  630.     if (cp5 != cp3)        /* Do erase.         */
  631.         tteeol();
  632. #endif
  633. }
  634.  
  635. /*
  636.  * Redisplay the mode line for the window pointed to by the "wp". This is the
  637.  * only routine that has any idea of how the modeline is formatted. You can
  638.  * change the modeline format by hacking at this routine. Called by "update"
  639.  * any time there is a dirty window. Note that if STANDOUT_GLITCH is defined,
  640.  * first and last SG characters may never be seen. 
  641.  */
  642. VOID
  643. modeline(wp)
  644.     register struct window *wp;
  645. {
  646.     register int    n;
  647.     register struct buffer *bp;
  648.     int             mode;
  649.  
  650.     n = wp->w_toprow + wp->w_ntrows;    /* Location.         */
  651.     vscreen[n]->v_color = CMODE;    /* Mode line color.     */
  652.     vscreen[n]->v_flag |= (VFCHG | VFHBAD);    /* Recompute, display.     */
  653.     vtmove(n, 0);        /* Seek to right line.     */
  654.     bp = wp->w_bufp;
  655.     vtputc('-');
  656.     vtputc('-');
  657.     if ((bp->b_flag & BFCHG) != 0) {    /* "*" if changed.     */
  658.         vtputc('*');
  659.         vtputc('*');
  660.     } else {
  661.         vtputc('-');
  662.         vtputc('-');
  663.     }
  664.     vtputc('-');
  665.     n = 5;
  666.     n += vtputs("Mg: ");
  667.     if (bp->b_bname[0] != '\0')
  668.         n += vtputs(&(bp->b_bname[0]));
  669.     while (n < 42) {    /* Pad out with blanks     */
  670.         vtputc(' ');
  671.         ++n;
  672.     }
  673.     vtputc('(');
  674.     ++n;
  675.     for (mode = 0;;) {
  676.         n += vtputs(bp->b_modes[mode]->p_name);
  677.         if (++mode > bp->b_nmodes)
  678.             break;
  679.         vtputc('-');
  680.         ++n;
  681.     }
  682.     vtputc(')');
  683.     ++n;
  684.     while (n < ncol) {    /* Pad out.         */
  685.         vtputc('-');
  686.         ++n;
  687.     }
  688. }
  689. /*
  690.  * output a string to the mode line, report how long it was. 
  691.  */
  692. vtputs(s)
  693.     register char  *s;
  694. {
  695.     register int    n = 0;
  696.  
  697.     while (*s != '\0') {
  698.         vtputc(*s++);
  699.         ++n;
  700.     }
  701.     return n;
  702. }
  703. #ifndef    NONDYNVID
  704. /*
  705.  * Compute the hash code for the line pointed to by the "vp". Recompute it if
  706.  * necessary. Also set the approximate redisplay cost. The validity of the
  707.  * hash code is marked by a flag bit. The cost understand the advantages of
  708.  * erase to end of line. Tuned for the VAX by Bob McNamara; better than it
  709.  * used to be on just about any machine. 
  710.  */
  711. static          VOID
  712. hash(vp)
  713.     register struct video *vp;
  714. {
  715.     register int    i;
  716.     register int    n;
  717.     register char  *s;
  718.  
  719.     if ((vp->v_flag & VFHBAD) != 0) {    /* Hash bad.         */
  720.         s = &vp->v_text[ncol - 1];
  721.         for (i = ncol; i != 0; --i, --s)
  722.             if (*s != ' ')
  723.                 break;
  724.         n = ncol - i;    /* Erase cheaper?     */
  725.         if (n > tceeol)
  726.             n = tceeol;
  727.         vp->v_cost = i + n;    /* Bytes + blanks.     */
  728.         for (n = 0; i != 0; --i, --s)
  729.             n = (n << 5) + n + *s;
  730.         vp->v_hash = n;    /* Hash code.         */
  731.         vp->v_flag &= ~VFHBAD;    /* Flag as all done.     */
  732.     }
  733. }
  734.  
  735. /*
  736.  * Compute the Insert-Delete cost matrix. The dynamic programming algorithm
  737.  * described by James Gosling is used. This code assumes that the line above
  738.  * the echo line is the last line involved in the scroll region. This is easy
  739.  * to arrange on the VT100 because of the scrolling region. The "offs" is the
  740.  * origin 0 offset of the first row in the virtual/physical screen that is
  741.  * being updated; the "size" is the length of the chunk of screen being
  742.  * updated. For a full screen update, use offs=0 and size=nrow-1. 
  743.  *
  744.  * Older versions of this code implemented the score matrix by a two dimensional
  745.  * array of SCORE nodes. This put all kinds of multiply instructions in the
  746.  * code! This version is written to use a linear array and pointers, and
  747.  * contains no multiplication at all. The code has been carefully looked at
  748.  * on the VAX, with only marginal checking on other machines for efficiency.
  749.  * In fact, this has been tuned twice! Bob McNamara tuned it even more for
  750.  * the VAX, which is a big issue for him because of the 66 line X displays. 
  751.  *
  752.  * On some machines, replacing the "for (i=1; i<=size; ++i)" with i = 1; do { }
  753.  * while (++i <=size)" will make the code quite a bit better; but it looks
  754.  * ugly. 
  755.  */
  756. VOID
  757. setscores(offs, size)
  758. {
  759.     register struct score *sp;
  760.     struct score   *sp1;
  761.     register int    tempcost;
  762.     register int    bestcost;
  763.     register int    j;
  764.     register int    i;
  765.     register struct video **vp;
  766.     struct video  **pp, **vbase, **pbase;
  767.  
  768.     vbase = &vscreen[offs - 1];    /* By hand CSE's.     */
  769.     pbase = &pscreen[offs - 1];
  770.     dscore[0].s_itrace = 0;    /* [0, 0]         */
  771.     dscore[0].s_jtrace = 0;
  772.     dscore[0].s_cost = 0;
  773.     sp = &dscore[1];    /* Row 0, inserts.     */
  774.     tempcost = 0;
  775.     vp = &vbase[1];
  776.     for (j = 1; j <= size; ++j) {
  777.         sp->s_itrace = 0;
  778.         sp->s_jtrace = j - 1;
  779.         tempcost += tcinsl;
  780.         tempcost += (*vp)->v_cost;
  781.         sp->s_cost = tempcost;
  782.         ++vp;
  783.         ++sp;
  784.     }
  785.     sp = &dscore[NROW];    /* Column 0, deletes.     */
  786.     tempcost = 0;
  787.     for (i = 1; i <= size; ++i) {
  788.         sp->s_itrace = i - 1;
  789.         sp->s_jtrace = 0;
  790.         tempcost += tcdell;
  791.         sp->s_cost = tempcost;
  792.         sp += NROW;
  793.     }
  794.     sp1 = &dscore[NROW + 1];/* [1, 1].         */
  795.     pp = &pbase[1];
  796.     for (i = 1; i <= size; ++i) {
  797.         sp = sp1;
  798.         vp = &vbase[1];
  799.         for (j = 1; j <= size; ++j) {
  800.             sp->s_itrace = i - 1;
  801.             sp->s_jtrace = j;
  802.             bestcost = (sp - NROW)->s_cost;
  803.             if (j != size)    /* Cd(A[i])=0 @ Dis.     */
  804.                 bestcost += tcdell;
  805.             tempcost = (sp - 1)->s_cost;
  806.             tempcost += (*vp)->v_cost;
  807.             if (i != size)    /* Ci(B[j])=0 @ Dsj.     */
  808.                 tempcost += tcinsl;
  809.             if (tempcost < bestcost) {
  810.                 sp->s_itrace = i;
  811.                 sp->s_jtrace = j - 1;
  812.                 bestcost = tempcost;
  813.             }
  814.             tempcost = (sp - NROW - 1)->s_cost;
  815.             if ((*pp)->v_color != (*vp)->v_color
  816.                 || (*pp)->v_hash != (*vp)->v_hash)
  817.                 tempcost += (*vp)->v_cost;
  818.             if (tempcost < bestcost) {
  819.                 sp->s_itrace = i - 1;
  820.                 sp->s_jtrace = j - 1;
  821.                 bestcost = tempcost;
  822.             }
  823.             sp->s_cost = bestcost;
  824.             ++sp;    /* Next column.         */
  825.             ++vp;
  826.         }
  827.         ++pp;
  828.         sp1 += NROW;    /* Next row.         */
  829.     }
  830. }
  831.  
  832. /*
  833.  * Trace back through the dynamic programming cost matrix, and update the
  834.  * screen using an optimal sequence of redraws, insert lines, and delete
  835.  * lines. The "offs" is the origin 0 offset of the chunk of the screen we are
  836.  * about to update. The "i" and "j" are always started in the lower right
  837.  * corner of the matrix, and imply the size of the screen. A full screen
  838.  * traceback is called with offs=0 and i=j=nrow-1. There is some
  839.  * do-it-yourself double subscripting here, which is acceptable because this
  840.  * routine is much less compute intensive then the code that builds the score
  841.  * matrix! 
  842.  */
  843. VOID
  844. traceback(offs, size, i, j)
  845. {
  846.     register int    itrace;
  847.     register int    jtrace;
  848.     register int    k;
  849.     register int    ninsl;
  850.     register int    ndraw;
  851.     register int    ndell;
  852.  
  853.     if (i == 0 && j == 0)    /* End of update.     */
  854.         return;
  855.     itrace = dscore[(NROW * i) + j].s_itrace;
  856.     jtrace = dscore[(NROW * i) + j].s_jtrace;
  857.     if (itrace == i) {    /* [i, j-1]         */
  858.         ninsl = 0;    /* Collect inserts.     */
  859.         if (i != size)
  860.             ninsl = 1;
  861.         ndraw = 1;
  862.         while (itrace != 0 || jtrace != 0) {
  863.             if (dscore[(NROW * itrace) + jtrace].s_itrace != itrace)
  864.                 break;
  865.             jtrace = dscore[(NROW * itrace) + jtrace].s_jtrace;
  866.             if (i != size)
  867.                 ++ninsl;
  868.             ++ndraw;
  869.         }
  870.         traceback(offs, size, itrace, jtrace);
  871.         if (ninsl != 0) {
  872.             ttcolor(CTEXT);
  873.             ttinsl(offs + j - ninsl, offs + size - 1, ninsl);
  874.         }
  875.         do {        /* B[j], A[j] blank.     */
  876.             k = offs + j - ndraw;
  877.             uline(k, vscreen[k], &blanks);
  878.         } while (--ndraw);
  879.         return;
  880.     }
  881.     if (jtrace == j) {    /* [i-1, j]         */
  882.         ndell = 0;    /* Collect deletes.     */
  883.         if (j != size)
  884.             ndell = 1;
  885.         while (itrace != 0 || jtrace != 0) {
  886.             if (dscore[(NROW * itrace) + jtrace].s_jtrace != jtrace)
  887.                 break;
  888.             itrace = dscore[(NROW * itrace) + jtrace].s_itrace;
  889.             if (j != size)
  890.                 ++ndell;
  891.         }
  892.         if (ndell != 0) {
  893.             ttcolor(CTEXT);
  894.             ttdell(offs + i - ndell, offs + size - 1, ndell);
  895.         }
  896.         traceback(offs, size, itrace, jtrace);
  897.         return;
  898.     }
  899.     traceback(offs, size, itrace, jtrace);
  900.     k = offs + j - 1;
  901.     uline(k, vscreen[k], pscreen[offs + i - 1]);
  902. }
  903. #endif
  904.