home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / x / xvisrc.zoo / screen.c < prev    next >
C/C++ Source or Header  |  1992-07-28  |  33KB  |  1,420 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)screen.c    2.3 (Chris & John Downey) 9/4/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     screen.c
  14. * module function:
  15.     Screen handling functions.
  16. * history:
  17.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  18.     Originally by Tim Thompson (twitch!tjt)
  19.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  20.     Heavily modified by Chris & John Downey
  21.  
  22. ***/
  23.  
  24. #include "xvi.h"
  25.  
  26. /*
  27.  * Size of command buffer - we won't allow anything more
  28.  * to be typed when we get to this limit.
  29.  */
  30. #define    CMDSZ    80
  31.  
  32. /*
  33.  * The following is used to optimise screen updating; we
  34.  * keep a record of the real screen state and compare it
  35.  * with the new version before actually doing any updating.
  36.  *
  37.  * The l_line part is guaranteed to be always null-terminated.
  38.  */
  39. typedef    struct    line_struct    {
  40.     char        *l_line;    /* storage for characters in line */
  41.     int            l_used;        /* number of bytes actually used */
  42.     unsigned int    l_flags;    /* information bits */
  43. } Sline;
  44.  
  45. /*
  46.  * Bit definitions for l_flags.
  47.  */
  48. #define    L_TEXT        0x01        /* is an ordinary text line */
  49. #define    L_MARKER    0x02        /* is a marker line ('@' or '~') */
  50. #define    L_DIRTY        0x04        /* has been modified */
  51. #define    L_MESSAGE    0x08        /* is a message line */
  52. #define    L_COMMAND    0x10        /* is a command line */
  53. #define    L_READONLY    0x20        /* message line for readonly buffer */
  54.  
  55. #define    L_STATUS    (L_MESSAGE | L_COMMAND)        /* is a status line */
  56.  
  57. static    Sline    *new_screen;        /* screen being updated */
  58. static    Sline    *real_screen;        /* state of real screen */
  59.  
  60. /*
  61.  * Status line glitch handling.
  62.  *
  63.  * Some terminals leave a space when changing colour. The number of spaces
  64.  * left is returned by the v_colour_cost() method within the VirtScr, and
  65.  * stored in the colour_cost variable herein - this is not perfect, it should
  66.  * really be in the Xviwin structure, but what the hell.
  67.  *
  68.  * "st_spare_cols" is the number of columns which are not used at the
  69.  * end of the status line; this is to prevent wrapping on this line,
  70.  * as this can do strange things to some terminals.
  71.  */
  72.  
  73. static    int    colour_cost = 0;
  74. static    int    st_spare_cols = 1;
  75.  
  76. static    int    line_to_new P((Xviwin *, Line *, int, long));
  77. static    void    file_to_new P((Xviwin *));
  78. static    void    new_to_screen P((VirtScr *, int, int));
  79. static    void    do_sline P((Xviwin *));
  80. static    void    clrline P((int));
  81.  
  82. /*
  83.  * This routine must be called to set up the screen memory -
  84.  * if it is not, we will probably get a core dump.
  85.  *
  86.  * Note that, at the moment, it must be called with a whole-screen
  87.  * window, i.e. the first window, and only that window, so that the
  88.  * nrows and ncols fields represent the whole screen.
  89.  */
  90. /*ARGSUSED*/
  91. void
  92. init_screen(win)
  93. Xviwin    *win;
  94. {
  95.     static char        *real_area, *new_area;
  96.     register int    count;
  97.     VirtScr        *vs;
  98.  
  99.     vs = win->w_vs;
  100.  
  101.     colour_cost = VScolour_cost(vs);
  102.     st_spare_cols = 1 + (colour_cost * 2);
  103.  
  104.     /*
  105.      * If we're changing the size of the screen, free the old stuff.
  106.      */
  107.     if (real_screen != NULL) {
  108.     free((char *) real_screen);
  109.     free((char *) new_screen);
  110.     free(real_area);
  111.     free(new_area);
  112.     }
  113.  
  114.     /*
  115.      * Allocate space for the lines, and for the structure holding
  116.      * information about each line. Notice that we allocate an
  117.      * extra byte at the end of each line for null termination.
  118.      */
  119.     real_screen = (Sline *) malloc((unsigned) VSrows(vs) * sizeof(Sline));
  120.     new_screen = (Sline *) malloc((unsigned) VSrows(vs) * sizeof(Sline));
  121.     real_area = malloc((unsigned) VSrows(vs) * (VScols(vs) + 1));
  122.     new_area = malloc((unsigned) VSrows(vs) * (VScols(vs) + 1));
  123.  
  124.     if (real_screen == NULL || new_screen == NULL ||
  125.     real_area == NULL || new_area == NULL) {
  126.     /* What to do now? */
  127.     sys_exit(0);
  128.     }
  129.  
  130.     /*
  131.      * Now assign all the rows ...
  132.      */
  133.     for (count = 0; count < VSrows(vs); count++) {
  134.     register Sline    *rp, *np;
  135.     register int    offset;
  136.  
  137.     rp = &real_screen[count];
  138.     np = &new_screen[count];
  139.  
  140.     offset = count * (VScols(vs) + 1);
  141.  
  142.     rp->l_line = real_area + offset;
  143.     np->l_line = new_area + offset;
  144.     rp->l_line[0] = np->l_line[0] = '\0';
  145.     rp->l_used = np->l_used = 0;
  146.     rp->l_flags = np->l_flags = 0;
  147.     }
  148. }
  149.  
  150. /*
  151.  * Set the L_DIRTY bit for a given line in both real_screen &
  152.  * new_screen if the stored representations are in fact different:
  153.  * otherwise clear it.
  154.  */
  155. static void
  156. mark_dirty(row)
  157.     int        row;
  158. {
  159.     Sline    *rp;
  160.     Sline    *np;
  161.     int        used;
  162.  
  163.     rp = &real_screen[row];
  164.     np = &new_screen[row];
  165.     if (
  166.  
  167.     (rp->l_flags & ~L_DIRTY) != (np->l_flags & ~L_DIRTY)
  168.     ||
  169.     (used = rp->l_used) != np->l_used
  170.     ||
  171.     strncmp(rp->l_line, np->l_line, used) != 0
  172.     ) {
  173.     /*
  174.      * The lines are different.
  175.      */
  176.     np->l_flags |= L_DIRTY;
  177.     rp->l_flags |= L_DIRTY;
  178.     } else {
  179.     rp->l_flags = (np->l_flags &= ~L_DIRTY);
  180.     }
  181. }
  182.  
  183. /*
  184.  * Transfer the specified window line into the "new" screen array, at
  185.  * the given row. Returns the number of screen lines taken up by the
  186.  * logical buffer line lp, or 0 if the line would not fit; this happens
  187.  * with longlines at the end of the screen. In this case, the lines
  188.  * which could not be displayed will have been marked with an '@'.
  189.  */
  190. static int
  191. line_to_new(window, lp, start_row, line)
  192. Xviwin        *window;
  193. Line        *lp;
  194. int        start_row;
  195. long        line;
  196. {
  197.     register unsigned    c;    /* next character from file */
  198.     register Sline    *curr_line;    /* output line - used for efficiency */
  199.     register char    *ltext;        /* pointer to text of line */
  200.     register int    curr_index;    /* current index in line */
  201.     bool_t        eoln;        /* true when line is done */
  202.     char        extra[MAX_TABSTOP];
  203.                     /* Stack for extra characters. */
  204.     int            nextra = 0;    /* index into stack */
  205.     int            srow, scol;    /* current screen row and column */
  206.     int            vcol;        /* virtual column */
  207.  
  208.     ltext = lp->l_text;
  209.     srow = start_row;
  210.     scol = vcol = 0;
  211.     curr_line = &new_screen[srow];
  212.     curr_index = 0;
  213.     eoln = FALSE;
  214.  
  215.     if (Pb(P_number)) {
  216.     static Flexbuf    ftmp;
  217.  
  218.     flexclear(&ftmp);
  219.     (void) lformat(&ftmp, NUM_FMT, line);
  220.     (void) strcpy(curr_line->l_line, flexgetstr(&ftmp));
  221.     scol += NUM_SIZE;
  222.     }
  223.  
  224.     while (!eoln) {
  225.     /*
  226.      * Get the next character to put on the screen.
  227.      */
  228.  
  229.     /*
  230.      * "extra" is a stack containing any extra characters
  231.      * we have to put on the screen - this is for chars
  232.      * which have a multi-character representation, and
  233.      * for the $ at end-of-line in list mode.
  234.      */
  235.  
  236.     if (nextra > 0) {
  237.         c = extra[--nextra];
  238.     } else {
  239.         unsigned    n;
  240.  
  241.         c = (unsigned char) (ltext[curr_index++]);
  242.  
  243.         /*
  244.          * Deal with situations where it is not
  245.          * appropriate just to copy characters
  246.          * straight onto the screen.
  247.          */
  248.         if (c == '\0') {
  249.  
  250.         if (Pb(P_list)) {
  251.             /*
  252.              * Have to show a '$' sign in list mode.
  253.              */
  254.             extra[nextra++] = '\0';
  255.             c = '$';
  256.         }
  257.  
  258.         } else {
  259.         char    *p;
  260.  
  261.         n = vischar((int) c, &p, vcol);
  262.         /*
  263.          * This is a bit paranoid assuming
  264.          * that Pn(P_tabstop) can never be
  265.          * greater than sizeof (extra), but
  266.          * so what.
  267.          */
  268.         if (nextra + n > sizeof extra)
  269.             n = (sizeof extra - nextra);
  270.         /*
  271.          * Stack the extra characters so that
  272.          * they appear in the right order.
  273.          */
  274.         while (n > 1) {
  275.             extra[nextra++] = p[--n];
  276.         }
  277.         c = p[0];
  278.         }
  279.     }
  280.  
  281.     if (c == '\0') {
  282.         /*
  283.          * End of line. Terminate it and finish.
  284.          */
  285.         eoln = TRUE;
  286.         curr_line->l_flags = L_TEXT;
  287.         curr_line->l_used = scol;
  288.         curr_line->l_line[scol] = '\0';
  289.         mark_dirty(srow);
  290.         break;
  291.     } else {
  292.         /*
  293.          * Sline folding.
  294.          */
  295.         if (scol >= window->w_ncols) {
  296.         curr_line->l_flags = L_TEXT;
  297.         curr_line->l_used = scol;
  298.         curr_line->l_line[scol] = '\0';
  299.         mark_dirty(srow);
  300.         srow += 1;
  301.         scol = 0;
  302.         curr_line = &new_screen[srow];
  303.         }
  304.  
  305.         if (srow >= window->w_cmdline) {
  306.         for (srow = start_row; srow < window->w_cmdline; srow++) {
  307.             curr_line = &new_screen[srow];
  308.  
  309.             curr_line->l_flags = L_MARKER;
  310.             curr_line->l_used = 1;
  311.             curr_line->l_line[0] = '@';
  312.             curr_line->l_line[1] = '\0';
  313.             mark_dirty(srow);
  314.         }
  315.         return(0);
  316.         }
  317.  
  318.         /*
  319.          * Store the character in new_screen.
  320.          */
  321.         curr_line->l_line[scol++] = c;
  322.         vcol++;
  323.     }
  324.     }
  325.  
  326.     return((srow - start_row) + 1);
  327. }
  328.  
  329. /*
  330.  * file_to_new()
  331.  *
  332.  * Based on the current value of topline, transfer a screenful
  333.  * of stuff from file to new_screen, and update botline.
  334.  */
  335. static void
  336. file_to_new(win)
  337. register Xviwin    *win;
  338. {
  339.     register int    row;
  340.     register Line    *line;
  341.     register Buffer    *buffer;
  342.     long        lnum;
  343.  
  344.     buffer = win->w_buffer;
  345.     row = win->w_winpos;
  346.     line = win->w_topline;
  347.     lnum = lineno(buffer, line);
  348.  
  349.     while (row < win->w_cmdline && line != buffer->b_lastline) {
  350.     int nlines;
  351.  
  352.     nlines = line_to_new(win, line, row, lnum);
  353.     if (nlines == 0) {
  354.         /*
  355.          * Make it look like we have updated
  356.          * all the screen lines, since they
  357.          * have '@' signs on them.
  358.          */
  359.         row = win->w_cmdline;
  360.         break;
  361.     } else {
  362.         row += nlines;
  363.         line = line->l_next;
  364.         lnum++;
  365.     }
  366.     }
  367.  
  368.     win->w_botline = line;
  369.  
  370.     /*
  371.      * If there are any lines remaining, fill them in
  372.      * with '~' characters.
  373.      */
  374.     for ( ; row < win->w_cmdline; row++) {
  375.     register Sline    *curr_line;
  376.  
  377.     curr_line = &new_screen[row];
  378.  
  379.     curr_line->l_flags = L_MARKER;
  380.     curr_line->l_used = 1;
  381.     curr_line->l_line[0] = '~';
  382.     curr_line->l_line[1] = '\0';
  383.     mark_dirty(row);
  384.     }
  385. }
  386.  
  387. /*
  388.  * new_to_screen
  389.  *
  390.  * Transfer the contents of new_screen to the screen,
  391.  * starting at "start_row", for "nlines" lines,
  392.  * using real_screen to avoid unnecessary output.
  393.  */
  394. static void
  395. new_to_screen(vs, start_row, nlines)
  396. VirtScr        *vs;
  397. int        start_row;
  398. int        nlines;
  399. {
  400.     int         row;                    /* current row */
  401.     int         end_row;                /* row after last one to be updated */
  402.     int         columns;
  403.  
  404.     columns = VScols(vs);
  405.  
  406.     if (!(echo & e_CHARUPDATE)) {
  407.     return;
  408.     }
  409.  
  410.     end_row = start_row + nlines;
  411.  
  412.     VSset_colour(vs, Pn(P_colour));
  413.  
  414.     for (row = start_row; row < end_row; row++) {
  415.     register int            ncol;   /* current column in new_screen */
  416.     register Sline          *new,
  417.                 *real;  /* pointers to current lines */
  418.     register unsigned       nflags;
  419.     unsigned                rflags; /* flags for current lines */
  420.     register char           *ntextp,
  421.                 *rtextp;
  422.                     /* pointers to line text */
  423.     register int            nc;     /* current character in new_screen */
  424.     int            n_used,
  425.                 r_used;
  426.  
  427.     nflags = (new = &new_screen[row])->l_flags;
  428.     rflags = (real = &real_screen[row])->l_flags;
  429.  
  430.     /*
  431.      * If the real and new screens are both "clean",
  432.      * don't bother.
  433.      */
  434.     if (!((nflags & L_DIRTY) || (rflags & L_DIRTY))) {
  435.         continue;
  436.     }
  437.  
  438.     ntextp = new->l_line;
  439.     rtextp = real->l_line;
  440.  
  441.     n_used = new->l_used;
  442.     r_used = real->l_used;
  443.  
  444.     if ((nflags & L_MESSAGE) ||
  445.                 (rflags & L_STATUS) != (nflags & L_STATUS)) {
  446.         /*
  447.          * If it's a message line, or its status (text line,
  448.          * command line or message line) has changed, and either
  449.          * the real line or the new line is "dirty", better update
  450.          * the whole thing; if any colour changes are required,
  451.          * the effects of cursor movements may not be predictable
  452.          * on some terminals.
  453.          */
  454.         VSgoto(vs, row, 0);
  455.         if (nflags & L_STATUS) {
  456.         VSset_colour(vs, (nflags & L_READONLY) ? Pn(P_roscolour) :
  457.                         Pn(P_statuscolour));
  458.         }
  459.         if ((nc = ntextp[0]) != '\0') {
  460.         VSputc(vs, row, 0, nc);
  461.         }
  462.         /*
  463.          * For command lines, only the first character should be
  464.          * highlighted.
  465.          */
  466.         if (nflags & L_COMMAND) {
  467.         VSset_colour(vs, Pn(P_colour));
  468.         }
  469.         if (nc != '\0') {
  470.         VSwrite(vs, row, 1, &ntextp[1]);
  471.         }
  472.  
  473.         /*
  474.          * Clear the rest of the line, if
  475.          * there is any left to be cleared.
  476.          */
  477.         if (n_used < columns) {
  478.         VSclear_line(vs, row, n_used);
  479.         }
  480.  
  481.         /*
  482.          * Change back to text colour if we have to.
  483.          */
  484.         if ((nflags & L_MESSAGE) != 0) {
  485.         VSset_colour(vs, Pn(P_colour));
  486.         }
  487.  
  488.         (void) strncpy(rtextp, ntextp, (int) (columns - st_spare_cols));
  489.     } else {
  490.         /*
  491.          * Look at each character in the line, comparing
  492.          * the new version with the one on the screen.
  493.          * If they differ, put it out.
  494.          *
  495.          * There is some optimisation here to avoid large
  496.          * use of tty_goto.
  497.          */
  498.         register int        scol;
  499.                 /* current column on physical screen */
  500.         register int        last_ncol;
  501.                 /* last column to be updated */
  502.  
  503.         for (ncol = scol = last_ncol = 0;
  504.                  ncol < n_used && ncol < r_used;
  505.                  (ncol++, scol++)) {
  506.         if ((nc = ntextp[ncol]) != rtextp[ncol]) {
  507.             /*
  508.              * They are different. Get to the right
  509.              * place before putting out the char.
  510.              */
  511.             if (ncol != 0) {
  512.             VSadvise(vs, row, last_ncol + 1,
  513.                         ncol - last_ncol - 1,
  514.                         ntextp + last_ncol + 1);
  515.             } else {
  516.             VSgoto(vs, row, scol);
  517.             /*
  518.              * A command line should have the first character
  519.              * - and only the first character - highlighted.
  520.              */
  521.             if (ncol == 0 && (nflags & L_STATUS) != 0) {
  522.                 VSset_colour(vs, (nflags & L_READONLY) ?
  523.                     Pn(P_roscolour) : Pn(P_statuscolour));
  524.             }
  525.             }
  526.  
  527.             VSputc(vs, row, ncol, nc);
  528.  
  529.             if (ncol == 0 && (nflags & L_COMMAND) != 0) {
  530.             VSset_colour(vs, Pn(P_colour));
  531.             }
  532.             last_ncol = ncol;
  533.             rtextp[ncol] = nc;
  534.         }
  535.         if (ncol == 0 && (nflags & L_COMMAND) != 0) {
  536.             scol += (colour_cost * 2);
  537.         }
  538.         }
  539.  
  540.         if (n_used > r_used) {
  541.         /*
  542.          * We have got to the end of the previous
  543.          * screen line; if there is anything left,
  544.          * we should just display it.
  545.          */
  546.         (void) strcpy(&rtextp[ncol], &ntextp[ncol]);
  547.         if (ncol == 0 && (nflags & L_COMMAND) != 0) {
  548.             /*
  549.              * A command line should have the first character
  550.              * - and only the first character - highlighted.
  551.              */
  552.             VSgoto(vs, row, 0);
  553.             VSset_colour(vs, Pn(P_statuscolour));
  554.             VSputc(vs, row, 0, ntextp[0]);
  555.             VSset_colour(vs, Pn(P_colour));
  556.             ncol = 1;
  557.         } else {
  558.             /*
  559.              * Skip over initial whitespace.
  560.              */
  561.             while (ntextp[ncol] == ' ') {
  562.             ncol++;
  563.             scol++;
  564.             }
  565.         }
  566.         if (ncol < columns)
  567.             VSwrite(vs, row, scol, &ntextp[ncol]);
  568.         } else if (r_used > n_used) {
  569.         /*
  570.          * We have got to the end of the new screen
  571.          * line, but the old one still has stuff on
  572.          * it. We must therefore clear it.
  573.          */
  574.         VSclear_line(vs, row, scol);
  575.         }
  576.     }
  577.  
  578.     real->l_line[n_used] = '\0';
  579.     real->l_used = n_used;
  580.  
  581.     /*
  582.      * The real screen line is a message or command line if the
  583.      * newly-updated one was. Otherwise, it isn't.
  584.      *
  585.      * Both the new and real screens may now be considered
  586.      * "clean".
  587.      */
  588.     real->l_flags = (
  589.               /*
  590.                * Turn these flags off first ...
  591.                */
  592.               (rflags & ~(L_STATUS | L_DIRTY))
  593.               /*
  594.                * ... then set whatever L_STATUS flags are
  595.                * set in new_screen.
  596.                */
  597.               | (nflags & L_STATUS)
  598.             );
  599.     new->l_flags &= ~L_DIRTY;
  600.     }
  601. }
  602.  
  603. /*
  604.  * Update the status line of the given window, and cause the status
  605.  * line to be written out. Note that we call new_to_screen() to cause
  606.  * the output to be generated; since there will be no other changes,
  607.  * only the status line will be changed on the screen.
  608.  */
  609. void
  610. update_sline(win)
  611. Xviwin    *win;
  612. {
  613.     do_sline(win);
  614.     new_to_screen(win->w_vs, (int) win->w_cmdline, 1);
  615.     VSflush(win->w_vs);
  616. }
  617.  
  618. /*
  619.  * Update the status line of the given window,
  620.  * from the one in win->w_statusline.
  621.  */
  622. static void
  623. do_sline(win)
  624. Xviwin    *win;
  625. {
  626.     register char    *from;
  627.     register char    *to;
  628.     register char    *end;
  629.     Sline        *slp;
  630.  
  631.     from = flexgetstr(&win->w_statusline);
  632.     slp = &new_screen[win->w_cmdline];
  633.     to = slp->l_line;
  634.     end = to + win->w_ncols - st_spare_cols;
  635.  
  636.     while (*from != '\0' && to < end) {
  637.     *to++ = *from++;
  638.     }
  639.  
  640.     /*
  641.      * Fill with spaces, and null-terminate.
  642.      */
  643.     while (to < end) {
  644.     *to++ = ' ';
  645.     }
  646.     *to = '\0';
  647.  
  648.     slp->l_used = win->w_ncols - st_spare_cols;
  649.     slp->l_flags = L_MESSAGE;
  650.     if (is_readonly(win->w_buffer)) {
  651.     slp->l_flags |= L_READONLY;
  652.     }
  653.     mark_dirty(win->w_cmdline);
  654. }
  655.  
  656. void
  657. update_cline(win)
  658. Xviwin    *win;
  659. {
  660.     Sline    *clp;
  661.     unsigned width, maxwidth;
  662.  
  663.     clp = &new_screen[win->w_cmdline];
  664.  
  665.     maxwidth = win->w_ncols - st_spare_cols;
  666.     if ((width = flexlen(&win->w_statusline)) > maxwidth) {
  667.     width = maxwidth;
  668.     }
  669.     (void) strncpy(clp->l_line, flexgetstr(&win->w_statusline),
  670.                 (int) width);
  671.     clp->l_used = width;
  672.     clp->l_line[width] = '\0';
  673.     clp->l_flags = (L_COMMAND | L_DIRTY);
  674.     /*
  675.      * We don't bother calling mark_dirty() here: it isn't worth
  676.      * it because the line's contents have almost certainly
  677.      * changed.
  678.      */
  679.     new_to_screen(win->w_vs, (int) win->w_cmdline, 1);
  680.     VSflush(win->w_vs);
  681. }
  682.  
  683. /*
  684.  * updateline() - update the line the cursor is on
  685.  *
  686.  * Updateline() is called after changes that only affect the line that
  687.  * the cursor is on. This improves performance tremendously for normal
  688.  * insert mode operation. The only thing we have to watch for is when
  689.  * the cursor line grows or shrinks around a row boundary. This means
  690.  * we have to repaint other parts of the screen appropriately.
  691.  */
  692. void
  693. updateline(window)
  694. Xviwin    *window;
  695. {
  696.     Line    *currline;
  697.     int        nlines;
  698.     int        curs_row;
  699.  
  700.     currline = window->w_cursor->p_line;
  701.  
  702.     /*
  703.      * Find out which screen line the cursor line starts on.
  704.      * This is not necessarily the same as window->w_row,
  705.      * because longlines are different.
  706.      */
  707.     if (plines(window, currline) > 1) {
  708.     curs_row = (int) cntplines(window, window->w_topline, currline);
  709.     } else {
  710.     curs_row = window->w_row;
  711.     }
  712.  
  713.     nlines = line_to_new(window, currline,
  714.             (int) (curs_row + window->w_winpos),
  715.             (long) lineno(window->w_buffer, currline));
  716.  
  717.     if (nlines != window->w_c_line_size) {
  718.     update_buffer(window->w_buffer);
  719.     } else {
  720.     new_to_screen(window->w_vs,
  721.             (int) (curs_row + window->w_winpos), nlines);
  722.     VSflush(window->w_vs);
  723.     }
  724. }
  725.  
  726. /*
  727.  * Completely update the representation of the given window.
  728.  */
  729. void
  730. update_window(window)
  731. Xviwin    *window;
  732. {
  733.     if (window->w_nrows > 1) {
  734.     file_to_new(window);
  735.     new_to_screen(window->w_vs,
  736.             (int) window->w_winpos, (int) window->w_nrows);
  737.     VSflush(window->w_vs);
  738.     }
  739. }
  740.  
  741. /*
  742.  * Update all windows.
  743.  */
  744. void
  745. update_all()
  746. {
  747.     Xviwin    *w = curwin;
  748.  
  749.     do {
  750.     if (w->w_nrows > 1) {
  751.         file_to_new(w);
  752.     }
  753.     if (w->w_nrows > 0) {
  754.         do_sline(w);
  755.     }
  756.     } while ((w = next_window(w)) != curwin);
  757.  
  758.     new_to_screen(w->w_vs, 0, (int) VSrows(w->w_vs));
  759.     VSflush(w->w_vs);
  760. }
  761.  
  762. /*
  763.  * Totally redraw the screen.
  764.  */
  765. void
  766. redraw_screen()
  767. {
  768.     if (curwin != NULL) {
  769.     clear(curwin);
  770.     update_all();
  771.     }
  772. }
  773.  
  774. void
  775. clear(win)
  776. Xviwin    *win;
  777. {
  778.     register int    row;
  779.     int        nrows;
  780.  
  781.     nrows = VSrows(win->w_vs);
  782.  
  783.     VSset_colour(win->w_vs, Pn(P_colour));
  784.     VSclear_all(win->w_vs);
  785.  
  786.     /*
  787.      * Clear the real screen lines, and mark them as modified.
  788.      */
  789.     for (row = 0; row < nrows; row++) {
  790.     clrline(row);
  791.     }
  792. }
  793.  
  794. /*
  795.  * The rest of the routines in this file perform screen manipulations.
  796.  * The given operation is performed physically on the screen. The
  797.  * corresponding change is also made to the internal screen image. In
  798.  * this way, the editor anticipates the effect of editing changes on
  799.  * the appearance of the screen. That way, when we call screenupdate a
  800.  * complete redraw isn't usually necessary. Another advantage is that
  801.  * we can keep adding code to anticipate screen changes, and in the
  802.  * meantime, everything still works.
  803.  */
  804.  
  805. /*
  806.  * s_ins(win, row, nlines) - insert 'nlines' lines at 'row'
  807.  */
  808. void
  809. s_ins(win, row, nlines)
  810. Xviwin        *win;
  811. register int    row;
  812. int        nlines;
  813. {
  814.     register int    from, to;
  815.     int            count;
  816.     VirtScr        *vs;
  817.  
  818.     if (!(echo & e_SCROLL))
  819.     return;
  820.  
  821.     /*
  822.      * There's no point in scrolling more lines than there are
  823.      * (below row) in the window, or in scrolling 0 lines.
  824.      */
  825.     if (nlines == 0 || nlines + row >= win->w_nrows - 1)
  826.     return;
  827.  
  828.     /*
  829.      * The row specified is relative to the top of the window;
  830.      * add the appropriate offset to make it into a screen row.
  831.      */
  832.     row += win->w_winpos;
  833.  
  834.     /*
  835.      * Note that we avoid the use of 1-line scroll regions; these
  836.      * only ever occur at the bottom of a window, and it is better
  837.      * just to leave the line to be updated in the best way by
  838.      * update{line,screen}.
  839.      */
  840.     if (nlines == 1 && row + 1 == win->w_cmdline) {
  841.     return;
  842.     }
  843.  
  844.     vs = win->w_vs;
  845.  
  846.     if (vs->v_scroll != NULL) {
  847.     if (!VSscroll(vs, row, (int) win->w_cmdline - 1, -nlines)) {
  848.         /*
  849.          * Can't scroll what we were asked to - try scrolling
  850.          * the whole window including the status line.
  851.          */
  852.         VSclear_line(vs, (int) win->w_cmdline, 0);
  853.         clrline(win->w_cmdline);
  854.         if (!VSscroll(vs, row, (int) win->w_cmdline, -nlines)) {
  855.         /*
  856.          * Failed.
  857.          */
  858.         return;
  859.         }
  860.     }
  861.     } else {
  862.     return;
  863.     }
  864.  
  865.     /*
  866.      * Update the stored screen image so it matches what has
  867.      * happened on the screen.
  868.      */
  869.  
  870.     /*
  871.      * Move section of text down to the bottom.
  872.      *
  873.      * We do this by rearranging the pointers within the Slines,
  874.      * rather than copying the characters.
  875.      */
  876.     for (to = win->w_cmdline - 1, from = to - nlines; from >= row;
  877.                             --from, --to) {
  878.     register char    *temp;
  879.  
  880.     temp = real_screen[to].l_line;
  881.     real_screen[to].l_line = real_screen[from].l_line;
  882.     real_screen[from].l_line = temp;
  883.     real_screen[to].l_used = real_screen[from].l_used;
  884.     }
  885.  
  886.     /*
  887.      * Clear the newly inserted lines.
  888.      */
  889.     for (count = row; count < row + nlines; count++) {
  890.     clrline(count);
  891.     }
  892. }
  893.  
  894. /*
  895.  * s_del(win, row, nlines) - delete 'nlines' lines starting at 'row'.
  896.  */
  897. void
  898. s_del(win, row, nlines)
  899. register Xviwin        *win;
  900. int            row;
  901. int            nlines;
  902. {
  903.     register int    from, to;
  904.     int            count;
  905.     VirtScr        *vs;
  906.  
  907.     if (!(echo & e_SCROLL))
  908.     return;
  909.  
  910.     /*
  911.      * There's no point in scrolling more lines than there are
  912.      * (below row) in the window, or in scrolling 0 lines.
  913.      */
  914.     if (nlines == 0 || nlines + row >= win->w_nrows - 1)
  915.     return;
  916.  
  917.     /*
  918.      * The row specified is relative to the top of the window;
  919.      * add the appropriate offset to make it into a screen row.
  920.      */
  921.     row += win->w_winpos;
  922.  
  923.     /*
  924.      * We avoid the use of 1-line scroll regions, since they don't
  925.      * work with many terminals, especially if we are using
  926.      * (termcap) DO to scroll the region.
  927.      */
  928.     if (nlines == 1 && row + 1 == win->w_cmdline) {
  929.     return;
  930.     }
  931.  
  932.     vs = win->w_vs;
  933.  
  934.     if (vs->v_scroll != NULL) {
  935.     if (!VSscroll(vs, row, (int) win->w_cmdline - 1, nlines)) {
  936.         /*
  937.          * Can't scroll what we were asked to - try scrolling
  938.          * the whole window including the status line.
  939.          */
  940.         VSclear_line(vs, (int) win->w_cmdline, 0);
  941.         clrline(win->w_cmdline);
  942.         if (!VSscroll(vs, row, (int) win->w_cmdline, nlines)) {
  943.         /*
  944.          * Failed.
  945.          */
  946.         return;
  947.         }
  948.     }
  949.     } else {
  950.     return;
  951.     }
  952.  
  953.     /*
  954.      * Update the stored screen image so it matches what has
  955.      * happened on the screen.
  956.      */
  957.  
  958.     /*
  959.      * Move section of text up from the bottom.
  960.      *
  961.      * We do this by rearranging the pointers within the Slines,
  962.      * rather than copying the characters.
  963.      */
  964.     for (to = row, from = to + nlines;
  965.      from < win->w_cmdline;
  966.      from++, to++) {
  967.     register char    *temp;
  968.  
  969.     temp = real_screen[to].l_line;
  970.     real_screen[to].l_line = real_screen[from].l_line;
  971.     real_screen[from].l_line = temp;
  972.     real_screen[to].l_used = real_screen[from].l_used;
  973.     }
  974.  
  975.     /*
  976.      * Clear the deleted lines.
  977.      */
  978.     for (count = win->w_cmdline - nlines; count < win->w_cmdline; count++) {
  979.     clrline(count);
  980.     }
  981. }
  982.  
  983. /*
  984.  * Insert a character at the cursor position, updating the screen as
  985.  * necessary. Note that this routine doesn't have to do anything, as
  986.  * the screen will eventually be correctly updated anyway; it's just
  987.  * here for speed of screen updating.
  988.  */
  989. void
  990. s_inschar(window, newchar)
  991. Xviwin            *window;
  992. int            newchar;
  993. {
  994.     register char    *curp;
  995.     register char    *cp;
  996.     register char    *sp;
  997.     Sline        *rp;
  998.     Posn        *pp;
  999.     VirtScr        *vs;        /* the VirtScr for this window */
  1000.     char        *newstr;    /* printable string for newchar */
  1001.     register unsigned    nchars;        /* number of  chars in newstr */
  1002.     unsigned        currow;
  1003.     unsigned        curcol;
  1004.     unsigned        columns;
  1005.  
  1006.     vs = window->w_vs;
  1007.     if (vs->v_insert == NULL)
  1008.     return;
  1009.  
  1010.     if (!(echo & e_CHARUPDATE))
  1011.     return;
  1012.  
  1013.     pp = window->w_cursor;
  1014.  
  1015.     /*
  1016.      * If we are at (or near) the end of the line, it's not worth
  1017.      * the bother. Define near as 0 or 1 characters to be moved.
  1018.      */
  1019.     cp = pp->p_line->l_text + pp->p_index;
  1020.     if (*cp == '\0' || *(cp+1) == '\0')
  1021.     return;
  1022.  
  1023.     curcol = window->w_col;
  1024.  
  1025.     /*
  1026.      * If the cursor is on a longline, and not on the last actual
  1027.      * screen line of that longline, we can't do it.
  1028.      */
  1029.     if (window->w_c_line_size > 1 && curcol != window->w_virtcol)
  1030.     return;
  1031.  
  1032.     nchars = vischar(newchar, &newstr, curcol);
  1033.  
  1034.     /*
  1035.      * And don't bother if we are (or will be) at the last screen column.
  1036.      */
  1037.     columns = window->w_ncols;
  1038.     if (curcol + nchars >= columns)
  1039.     return;
  1040.  
  1041.     /*
  1042.      * Also, trying to push tabs rightwards doesn't work very
  1043.      * well. It's usually better not to use the insert character
  1044.      * sequence because in most cases we'll only have to update
  1045.      * the line as far as the tab anyway.
  1046.      */
  1047.     if ((!Pb(P_list) && Pb(P_tabs)) && strchr(cp, '\t') != NULL) {
  1048.     return;
  1049.     }
  1050.  
  1051.     /*
  1052.      * Okay, we can do it.
  1053.      */
  1054.     currow = window->w_row;
  1055.  
  1056.     VSinsert(vs, window->w_winpos + currow, curcol, newstr);
  1057.  
  1058.     /*
  1059.      * Update real_screen.
  1060.      */
  1061.     rp = &real_screen[window->w_winpos + currow];
  1062.     curp = &rp->l_line[curcol];
  1063.     if ((rp->l_used += nchars) > columns)
  1064.     rp->l_used = columns;
  1065.     cp = &rp->l_line[rp->l_used - 1];
  1066.     cp[1] = '\0';
  1067.     if (cp - curp >= nchars)
  1068.     {
  1069.     sp = cp - nchars;
  1070.     for (;;) {
  1071.         *cp-- = *sp;
  1072.         if (sp-- <= curp)
  1073.         break;
  1074.     }
  1075.     }
  1076.  
  1077.     /*
  1078.      * This is the string we've just inserted.
  1079.      */
  1080.     sp = newstr;
  1081.     while (nchars-- > 0) {
  1082.     *curp++ = *sp++;
  1083.     }
  1084. }
  1085.  
  1086. void
  1087. wind_goto(win)
  1088. Xviwin    *win;
  1089. {
  1090.     VirtScr    *vs;
  1091.  
  1092.     if (echo & e_CHARUPDATE) {
  1093.     vs = win->w_vs;
  1094.     VSgoto(vs, (int) win->w_winpos + win->w_row, win->w_col);
  1095.     VSflush(vs);
  1096.     }
  1097. }
  1098.  
  1099. static    char        inbuf[CMDSZ];        /* command input buffer */
  1100. static    unsigned int    inpos = 0;        /* posn of next input char */
  1101. static    unsigned char    colposn[CMDSZ];        /* holds n chars per char */
  1102.  
  1103. /*
  1104.  * cmd_init(window, firstch)
  1105.  *
  1106.  * Initialise command line input.
  1107.  */
  1108. void
  1109. cmd_init(win, firstch)
  1110. Xviwin    *win;
  1111. int    firstch;
  1112. {
  1113.     if (inpos > 0) {
  1114.     show_error(win, "Internal error: re-entered command line input mode");
  1115.     return;
  1116.     }
  1117.  
  1118.     State = CMDLINE;
  1119.  
  1120.     flexclear(&win->w_statusline);
  1121.     (void) flexaddch(&win->w_statusline, firstch);
  1122.     inbuf[0] = firstch;
  1123.     inpos = 1;
  1124.     update_cline(win);
  1125.     colposn[0] = 0;
  1126. }
  1127.  
  1128. /*
  1129.  * cmd_input(window, character)
  1130.  *
  1131.  * Deal with command line input. Takes an input character and returns
  1132.  * one of cmd_CANCEL (meaning they typed ESC or deleted past the
  1133.  * prompt character), cmd_COMPLETE (indicating that the command has
  1134.  * been completely input), or cmd_INCOMPLETE (indicating that command
  1135.  * line is still the right mode to be in).
  1136.  *
  1137.  * Once cmd_COMPLETE has been returned, it is possible to call
  1138.  * get_cmd(win) to obtain the command line.
  1139.  */
  1140. Cmd_State
  1141. cmd_input(win, ch)
  1142. Xviwin    *win;
  1143. int    ch;
  1144. {
  1145.     static bool_t    literal_next = FALSE;
  1146.  
  1147.     if (!literal_next) {
  1148.     switch (ch) {
  1149.     case CTRL('V'):
  1150.         literal_next = TRUE;
  1151.         return(cmd_INCOMPLETE);
  1152.  
  1153.     case '\n':        /* end of line */
  1154.     case '\r':
  1155.         inbuf[inpos] = '\0';    /* terminate input line */
  1156.         inpos = 0;
  1157.         State = NORMAL;        /* return state to normal */
  1158.         do_sline(win);        /* line is now a message line */
  1159.         return(cmd_COMPLETE);    /* and indicate we are done */
  1160.  
  1161.     case '\b':        /* backspace or delete */
  1162.     case DEL:
  1163.     {
  1164.         unsigned len;
  1165.  
  1166.         inbuf[--inpos] = '\0';
  1167.         len = colposn[inpos - 1] + 1;
  1168.         while (flexlen(&win->w_statusline) > len)
  1169.         (void) flexrmchar(&win->w_statusline);
  1170.         update_cline(win);
  1171.         if (inpos == 0) {
  1172.         /*
  1173.          * Deleted past first char;
  1174.          * go back to normal mode.
  1175.          */
  1176.         State = NORMAL;
  1177.         return(cmd_CANCEL);
  1178.         }
  1179.         return(cmd_INCOMPLETE);
  1180.     }
  1181.  
  1182.     case '\033':
  1183.     case EOF:
  1184.     case CTRL('U'):        /* line kill */
  1185.         inpos = 1;
  1186.         inbuf[1] = '\0';
  1187.         flexclear(&win->w_statusline);
  1188.         (void) flexaddch(&win->w_statusline, inbuf[0]);
  1189.         update_cline(win);
  1190.         return(cmd_INCOMPLETE);
  1191.  
  1192.     default:
  1193.         break;
  1194.     }
  1195.     }
  1196.  
  1197.     literal_next = FALSE;
  1198.  
  1199.     if (inpos >= sizeof(inbuf) - 1) {
  1200.     /*
  1201.      * Must not overflow buffer.
  1202.      */
  1203.     beep(win);
  1204.     } else {
  1205.     unsigned    curposn;
  1206.     unsigned    w;
  1207.     char        *p;
  1208.  
  1209.     curposn = colposn[inpos - 1];
  1210.     w = vischar(ch, &p, (int) curposn);
  1211.     if (curposn + w >= win->w_ncols - 1) {
  1212.         beep(win);
  1213.     } else {
  1214.         colposn[inpos] = curposn + w;
  1215.         inbuf[inpos++] = ch;
  1216.         (void) lformat(&win->w_statusline, "%s", p);
  1217.         update_cline(win);
  1218.     }
  1219.     }
  1220.  
  1221.     return(cmd_INCOMPLETE);
  1222. }
  1223.  
  1224. /*ARGSUSED*/
  1225. char *
  1226. get_cmd(win)
  1227. Xviwin    *win;
  1228. {
  1229.     return(inbuf);
  1230. }
  1231.  
  1232. /*ARGSUSED*/
  1233. void
  1234. gotocmd(win, clr)
  1235. Xviwin    *win;
  1236. bool_t    clr;
  1237. {
  1238.     VirtScr    *vs;
  1239.  
  1240.     vs = win->w_vs;
  1241.     if (clr) {
  1242.     VSclear_line(vs, (int) win->w_cmdline, 0);
  1243.     }
  1244.     VSgoto(vs, (int) win->w_cmdline, 0);
  1245.     VSflush(vs);
  1246. }
  1247.  
  1248. /*
  1249.  * Display a prompt on the bottom line of the screen.
  1250.  */
  1251. void
  1252. prompt(message)
  1253.     char    *message;
  1254. {
  1255.     VirtScr    *vs;
  1256.     int    row;
  1257.  
  1258.     vs = curwin->w_vs;
  1259.  
  1260.     row = VSrows(vs) - 1;
  1261.     VSgoto(vs, row, 0);
  1262.     VSset_colour(vs, Pn(P_statuscolour));
  1263.     VSwrite(vs, row, 0, message);
  1264.     VSset_colour(vs, Pn(P_colour));
  1265.     VSgoto(vs, row, strlen(message));
  1266.     VSflush(vs);
  1267. }
  1268.  
  1269. /*
  1270.  * Sound the alert.
  1271.  */
  1272. void
  1273. beep(window)
  1274. register Xviwin *window;
  1275. {
  1276.     VSbeep(window->w_vs);
  1277. }
  1278.  
  1279. static char    *(*disp_func) P((void));
  1280. static int    disp_colwidth;
  1281. static int    disp_maxcol;
  1282. static bool_t    disp_listmode;
  1283.  
  1284. /*
  1285.  * Start off "display" mode. The "func" argument is a function pointer
  1286.  * which will be called to obtain each subsequent string to display.
  1287.  * The function returns NULL when no more lines are available.
  1288.  */
  1289. void
  1290. disp_init(win, func, colwidth, listmode)
  1291. Xviwin        *win;
  1292. char        *(*func) P((void));
  1293. int        colwidth;
  1294. bool_t        listmode;
  1295. {
  1296.     State = DISPLAY;
  1297.     disp_func = func;
  1298.     if (colwidth > win->w_ncols)
  1299.     colwidth = win->w_ncols;
  1300.     disp_colwidth = colwidth;
  1301.     disp_maxcol = (win->w_ncols / colwidth) * colwidth;
  1302.     disp_listmode = listmode;
  1303.     (void) disp_screen(win);
  1304. }
  1305.  
  1306. /*
  1307.  * Display text in glass-teletype mode, in approximately the style of
  1308.  * the more(1) program.
  1309.  *
  1310.  * If the return value from (*disp_func)() is NULL, it means we've got
  1311.  * to the end of the text to be displayed, so we wait for another key
  1312.  * before redisplaying our editing screen.
  1313.  */
  1314. bool_t
  1315. disp_screen(win)
  1316. Xviwin    *win;
  1317. {
  1318.     int        row;    /* current screen row */
  1319.     int        col;    /* current screen column */
  1320.     static bool_t    finished = FALSE;
  1321.     VirtScr        *vs;
  1322.  
  1323.     vs = win->w_vs;
  1324.  
  1325.     if (finished || kbdintr) {
  1326.     /*
  1327.      * Clear the screen, and then ensure that the window
  1328.      * on the current buffer is in the right place and
  1329.      * updated; finally update the whole screen.
  1330.      */
  1331.     clear(win);
  1332.     move_window_to_cursor(win);
  1333.     update_all();
  1334.     State = NORMAL;
  1335.     finished = FALSE;
  1336.     if (kbdintr) {
  1337.         imessage = TRUE;
  1338.     }
  1339.     return(TRUE);
  1340.     }
  1341.  
  1342.     VSclear_all(vs);
  1343.  
  1344.     for (col = 0; col < disp_maxcol; col += disp_colwidth) {
  1345.     for (row = 0; row < VSrows(vs) - 1; row++) {
  1346.         static char    *line;
  1347.         int        width;
  1348.  
  1349.         if (line == NULL && (line = (*disp_func)()) == NULL) {
  1350.         /*
  1351.          * We've got to the end.
  1352.          */
  1353.         prompt("[Hit return to continue] ");
  1354.         finished = TRUE;
  1355.         return(FALSE);
  1356.         }
  1357.  
  1358.         for (width = 0; *line != '\0'; line++) {
  1359.         char        *p;
  1360.         unsigned    w;
  1361.  
  1362.         w = vischar(*line, &p, disp_listmode ? -1 : width);
  1363.  
  1364.         if ((width += w) <= disp_colwidth) {
  1365.             VSwrite(vs, row, col + width - w, p);
  1366.         } else {
  1367.             /*
  1368.              * The line is too long, so we
  1369.              * have to wrap around to the
  1370.              * next screen line.
  1371.              */
  1372.             break;
  1373.         }
  1374.         }
  1375.  
  1376.         if (*line == '\0') {
  1377.         if (disp_listmode) {
  1378.             /*
  1379.              * In list mode, we have to
  1380.              * display a '$' to show the
  1381.              * end of a line.
  1382.              */
  1383.             if (width < disp_colwidth) {
  1384.             VSputc(vs, row, col + width, '$');
  1385.             } else {
  1386.             /*
  1387.              * We have to wrap it
  1388.              * to the next screen
  1389.              * line.
  1390.              */
  1391.             continue;
  1392.             }
  1393.         }
  1394.         /*
  1395.          * If we're not in list mode, or we
  1396.          * were able to display the '$', we've
  1397.          * finished with this line.
  1398.          */
  1399.         line = NULL;
  1400.         }
  1401.     }
  1402.     }
  1403.  
  1404.     prompt("[More] ");
  1405.  
  1406.     return(FALSE);
  1407. }
  1408.  
  1409. /*
  1410.  * Clear the given line, marking it as dirty.
  1411.  */
  1412. static void
  1413. clrline(line)
  1414. int    line;
  1415. {
  1416.     real_screen[line].l_used = 0;
  1417.     real_screen[line].l_line[0] = '\0';
  1418.     mark_dirty(line);
  1419. }
  1420.