Home | History | Annotate | Line # | Download | only in info
display.c revision 1.1.1.1
      1 /*	$NetBSD: display.c,v 1.1.1.1 2016/01/14 00:11:29 christos Exp $	*/
      2 
      3 /* display.c -- How to display Info windows.
      4    Id: display.c,v 1.7 2004/04/11 17:56:45 karl Exp
      5 
      6    Copyright (C) 1993, 1997, 2003, 2004 Free Software Foundation, Inc.
      7 
      8    This program is free software; you can redistribute it and/or modify
      9    it under the terms of the GNU General Public License as published by
     10    the Free Software Foundation; either version 2, or (at your option)
     11    any later version.
     12 
     13    This program is distributed in the hope that it will be useful,
     14    but WITHOUT ANY WARRANTY; without even the implied warranty of
     15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16    GNU General Public License for more details.
     17 
     18    You should have received a copy of the GNU General Public License
     19    along with this program; if not, write to the Free Software
     20    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     21 
     22    Originally written by Brian Fox (bfox (at) ai.mit.edu). */
     23 
     24 #include "info.h"
     25 #include "display.h"
     26 
     27 extern int info_any_buffered_input_p (void); /* Found in session.c. */
     28 
     29 static void free_display (DISPLAY_LINE **display);
     30 static DISPLAY_LINE **make_display (int width, int height);
     31 
     32 void handle_tag (char *tag);
     33 void handle_tag_start (char *tag);
     34 void handle_tag_end (char *tag);
     35 
     36 /* An array of display lines which tell us what is currently visible on
     37    the display.  */
     38 DISPLAY_LINE **the_display = (DISPLAY_LINE **)NULL;
     39 
     40 /* Non-zero means do no output. */
     41 int display_inhibited = 0;
     42 
     43 /* Initialize THE_DISPLAY to WIDTH and HEIGHT, with nothing in it. */
     44 void
     45 display_initialize_display (int width, int height)
     46 {
     47   free_display (the_display);
     48   the_display = make_display (width, height);
     49   display_clear_display (the_display);
     50 }
     51 
     52 /* Clear all of the lines in DISPLAY making the screen blank. */
     53 void
     54 display_clear_display (DISPLAY_LINE **display)
     55 {
     56   register int i;
     57 
     58   for (i = 0; display[i]; i++)
     59     {
     60       display[i]->text[0] = '\0';
     61       display[i]->textlen = 0;
     62       display[i]->inverse = 0;
     63     }
     64 }
     65 
     66 /* Non-zero if we didn't completely redisplay a window. */
     67 int display_was_interrupted_p = 0;
     68 
     69 /* Update the windows pointed to by WINDOW in the_display.  This actually
     70    writes the text on the screen. */
     71 void
     72 display_update_display (WINDOW *window)
     73 {
     74   register WINDOW *win;
     75 
     76   display_was_interrupted_p = 0;
     77 
     78   /* For every window in the list, check contents against the display. */
     79   for (win = window; win; win = win->next)
     80     {
     81       /* Only re-display visible windows which need updating. */
     82       if (((win->flags & W_WindowVisible) == 0) ||
     83           ((win->flags & W_UpdateWindow) == 0) ||
     84           (win->height == 0))
     85         continue;
     86 
     87       display_update_one_window (win);
     88       if (display_was_interrupted_p)
     89         break;
     90     }
     91 
     92   /* Always update the echo area. */
     93   display_update_one_window (the_echo_area);
     94 }
     95 
     96 void
     97 handle_tag_start (char *tag)
     98 {
     99   /* TODO really handle this tag.  */
    100   return;
    101 }
    102 
    103 void
    104 handle_tag_end (char *tag)
    105 {
    106   /* TODO really handle this tag.  */
    107   return;
    108 }
    109 
    110 void
    111 handle_tag (char *tag)
    112 {
    113     if (tag[0] == '/')
    114       {
    115 	tag++;
    116 	handle_tag_end (tag);
    117       }
    118     else
    119       handle_tag_start (tag);
    120 }
    121 
    122 /* Display WIN on the_display.  Unlike display_update_display (), this
    123    function only does one window. */
    124 void
    125 display_update_one_window (WINDOW *win)
    126 {
    127   register char *nodetext;      /* Current character to display. */
    128   register char *last_node_char; /* Position of the last character in node. */
    129   register int i;               /* General use index. */
    130   char *printed_line;           /* Buffer for a printed line. */
    131   int pl_index = 0;             /* Index into PRINTED_LINE. */
    132   int line_index = 0;           /* Number of lines done so far. */
    133   int pl_ignore = 0;		/* How many chars use zero width on screen. */
    134   int allocated_win_width;
    135   DISPLAY_LINE **display = the_display;
    136 
    137   /* If display is inhibited, that counts as an interrupted display. */
    138   if (display_inhibited)
    139     display_was_interrupted_p = 1;
    140 
    141   /* If the window has no height, or display is inhibited, quit now. */
    142   if (!win->height || display_inhibited)
    143     return;
    144 
    145   /* If the window's first row doesn't appear in the_screen, then it
    146      cannot be displayed.  This can happen when the_echo_area is the
    147      window to be displayed, and the screen has shrunk to less than one
    148      line. */
    149   if ((win->first_row < 0) || (win->first_row > the_screen->height))
    150     return;
    151 
    152   /* Print each line in the window into our local buffer, and then
    153      check the contents of that buffer against the display.  If they
    154      differ, update the display. */
    155   allocated_win_width = win->width + 1;
    156   printed_line = (char *)xmalloc (allocated_win_width);
    157 
    158   if (!win->node || !win->line_starts)
    159     goto done_with_node_display;
    160 
    161   nodetext = win->line_starts[win->pagetop];
    162   last_node_char = win->node->contents + win->node->nodelen;
    163 
    164   for (; nodetext < last_node_char; nodetext++)
    165     {
    166       char *rep = NULL, *rep_carried_over, rep_temp[2];
    167       int replen;
    168 
    169       if (isprint (*nodetext))
    170         {
    171           rep_temp[0] = *nodetext;
    172           replen = 1;
    173           rep_temp[1] = '\0';
    174           rep = rep_temp;
    175         }
    176       else
    177         {
    178           if (*nodetext == '\r' || *nodetext == '\n')
    179             {
    180               replen = win->width - pl_index + pl_ignore;
    181             }
    182 	  else if (*nodetext == '\0'
    183 		   && (nodetext + 2) < last_node_char
    184 		   && *(nodetext + 1) == '\b'
    185 		   && *(nodetext + 2) == '[')
    186 	    {
    187 	      /* Found new style tag/cookie \0\b[
    188 		 Read until the closing tag \0\b] */
    189 	      int element_len = 0;
    190 	      char *element;
    191 
    192 	      /* Skip the escapes.  */
    193 	      nodetext += 3;
    194 
    195 	      while (!(*nodetext == '\0'
    196 		    && *(nodetext + 1) == '\b'
    197 		    && *(nodetext + 2) == ']'))
    198 		{
    199 		  nodetext++;
    200 		  element_len++;
    201 		}
    202 
    203 	      element = (char *) malloc (element_len + 1);
    204 	      strncpy (element, nodetext - element_len, element_len);
    205 
    206 	      /* Skip the escapes.  */
    207 	      nodetext += 2;
    208 	      pl_ignore += element_len + 5;
    209 	      /* Append string terminator.  */
    210 	      element[element_len] = '\0';
    211 
    212 	      handle_tag (element);
    213 
    214 	      /* Over and out */
    215 	      free (element);
    216 
    217 	      continue;
    218 	    }
    219           else
    220             {
    221               rep = printed_representation (*nodetext, pl_index);
    222               replen = strlen (rep);
    223             }
    224         }
    225 
    226       /* Support ANSI escape sequences under -R.  */
    227       if (raw_escapes_p
    228 	  && *nodetext == '\033'
    229 	  && nodetext[1] == '['
    230 	  && isdigit (nodetext[2]))
    231 	{
    232 	  if (nodetext[3] == 'm')
    233 	    pl_ignore += 4;
    234 	  else if (isdigit (nodetext[3]) && nodetext[4] == 'm')
    235 	    pl_ignore += 5;
    236 	}
    237       while (pl_index + 2 >= allocated_win_width - 1)
    238 	{
    239 	  allocated_win_width *= 2;
    240 	  printed_line = (char *)xrealloc (printed_line, allocated_win_width);
    241 	}
    242 
    243       /* If this character can be printed without passing the width of
    244          the line, then stuff it into the line. */
    245       if (replen + pl_index < win->width + pl_ignore)
    246         {
    247           /* Optimize if possible. */
    248           if (replen == 1)
    249             {
    250               printed_line[pl_index++] = *rep;
    251             }
    252           else
    253             {
    254               for (i = 0; i < replen; i++)
    255                 printed_line[pl_index++] = rep[i];
    256             }
    257         }
    258       else
    259         {
    260           DISPLAY_LINE *entry;
    261 
    262           /* If this character cannot be printed in this line, we have
    263              found the end of this line as it would appear on the screen.
    264              Carefully print the end of the line, and then compare. */
    265           if (*nodetext == '\n' || *nodetext == '\r' || *nodetext == '\t')
    266             {
    267               printed_line[pl_index] = '\0';
    268               rep_carried_over = (char *)NULL;
    269             }
    270           else
    271             {
    272               /* The printed representation of this character extends into
    273                  the next line.  Remember the offset of the last character
    274                  printed out of REP so that we can carry the character over
    275                  to the next line. */
    276               for (i = 0; pl_index < (win->width + pl_ignore - 1);)
    277                 printed_line[pl_index++] = rep[i++];
    278 
    279               rep_carried_over = rep + i;
    280 
    281               /* If printing the last character in this window couldn't
    282                  possibly cause the screen to scroll, place a backslash
    283                  in the rightmost column. */
    284               if (1 + line_index + win->first_row < the_screen->height)
    285                 {
    286                   if (win->flags & W_NoWrap)
    287                     printed_line[pl_index++] = '$';
    288                   else
    289                     printed_line[pl_index++] = '\\';
    290                 }
    291               printed_line[pl_index] = '\0';
    292             }
    293 
    294           /* We have the exact line as it should appear on the screen.
    295              Check to see if this line matches the one already appearing
    296              on the screen. */
    297           entry = display[line_index + win->first_row];
    298 
    299           /* If the screen line is inversed, then we have to clear
    300              the line from the screen first.  Why, I don't know.
    301              (But don't do this if we have no visible entries, as can
    302              happen if the window is shrunk very small.)  */
    303           if ((entry && entry->inverse)
    304 	      /* Need to erase the line if it has escape sequences.  */
    305 	      || (raw_escapes_p && strchr (entry->text, '\033') != 0))
    306             {
    307               terminal_goto_xy (0, line_index + win->first_row);
    308               terminal_clear_to_eol ();
    309               entry->inverse = 0;
    310               entry->text[0] = '\0';
    311               entry->textlen = 0;
    312             }
    313 
    314           /* Find the offset where these lines differ. */
    315           for (i = 0; i < pl_index; i++)
    316             if (printed_line[i] != entry->text[i])
    317               break;
    318 
    319           /* If the lines are not the same length, or if they differed
    320              at all, we must do some redrawing. */
    321           if ((i != pl_index) || (pl_index != entry->textlen))
    322             {
    323               /* Move to the proper point on the terminal. */
    324               terminal_goto_xy (i, line_index + win->first_row);
    325 
    326               /* If there is any text to print, print it. */
    327               if (i != pl_index)
    328                 terminal_put_text (printed_line + i);
    329 
    330               /* If the printed text didn't extend all the way to the edge
    331                  of the window, and text was appearing between here and the
    332                  edge of the window, clear from here to the end of the line. */
    333               if ((pl_index < win->width + pl_ignore
    334 		   && pl_index < entry->textlen)
    335 		  || (entry->inverse))
    336                 terminal_clear_to_eol ();
    337 
    338               fflush (stdout);
    339 
    340               /* Update the display text buffer. */
    341 	      if (strlen (printed_line) > (unsigned int) screenwidth)
    342 		/* printed_line[] can include more than screenwidth
    343 		   characters if we are under -R and there are escape
    344 		   sequences in it.  However, entry->text was
    345 		   allocated (in display_initialize_display) for
    346 		   screenwidth characters only.  */
    347 		entry->text = xrealloc (entry->text, strlen (printed_line)+1);
    348               strcpy (entry->text + i, printed_line + i);
    349               entry->textlen = pl_index;
    350 
    351               /* Lines showing node text are not in inverse.  Only modelines
    352                  have that distinction. */
    353               entry->inverse = 0;
    354             }
    355 
    356           /* We have done at least one line.  Increment our screen line
    357              index, and check against the bottom of the window. */
    358           if (++line_index == win->height)
    359             break;
    360 
    361           /* A line has been displayed, and the screen reflects that state.
    362              If there is typeahead pending, then let that typeahead be read
    363              now, instead of continuing with the display. */
    364           if (info_any_buffered_input_p ())
    365             {
    366               free (printed_line);
    367               display_was_interrupted_p = 1;
    368               return;
    369             }
    370 
    371           /* Reset PL_INDEX to the start of the line. */
    372           pl_index = 0;
    373 	  pl_ignore = 0;	/* this is computed per line */
    374 
    375           /* If there are characters from REP left to print, stuff them
    376              into the buffer now. */
    377           if (rep_carried_over)
    378             for (; rep[pl_index]; pl_index++)
    379               printed_line[pl_index] = rep[pl_index];
    380 
    381           /* If this window has chosen not to wrap lines, skip to the end
    382              of the physical line in the buffer, and start a new line here. */
    383           if (pl_index && (win->flags & W_NoWrap))
    384             {
    385               char *begin;
    386 
    387               pl_index = 0;
    388               printed_line[0] = '\0';
    389 
    390               begin = nodetext;
    391 
    392               while ((nodetext < last_node_char) && (*nodetext != '\n'))
    393                 nodetext++;
    394             }
    395         }
    396     }
    397 
    398  done_with_node_display:
    399   /* We have reached the end of the node or the end of the window.  If it
    400      is the end of the node, then clear the lines of the window from here
    401      to the end of the window. */
    402   for (; line_index < win->height; line_index++)
    403     {
    404       DISPLAY_LINE *entry = display[line_index + win->first_row];
    405 
    406       /* If this line has text on it then make it go away. */
    407       if (entry && entry->textlen)
    408         {
    409           entry->textlen = 0;
    410           entry->text[0] = '\0';
    411 
    412           terminal_goto_xy (0, line_index + win->first_row);
    413           terminal_clear_to_eol ();
    414         }
    415     }
    416 
    417   /* Finally, if this window has a modeline it might need to be redisplayed.
    418      Check the window's modeline against the one in the display, and update
    419      if necessary. */
    420   if ((win->flags & W_InhibitMode) == 0)
    421     {
    422       window_make_modeline (win);
    423       line_index = win->first_row + win->height;
    424 
    425       /* This display line must both be in inverse, and have the same
    426          contents. */
    427       if ((!display[line_index]->inverse) ||
    428           (strcmp (display[line_index]->text, win->modeline) != 0))
    429         {
    430           terminal_goto_xy (0, line_index);
    431           terminal_begin_inverse ();
    432           terminal_put_text (win->modeline);
    433           terminal_end_inverse ();
    434           strcpy (display[line_index]->text, win->modeline);
    435           display[line_index]->inverse = 1;
    436           display[line_index]->textlen = strlen (win->modeline);
    437           fflush (stdout);
    438         }
    439     }
    440 
    441   /* Okay, this window doesn't need updating anymore. */
    442   win->flags &= ~W_UpdateWindow;
    443   free (printed_line);
    444   fflush (stdout);
    445 }
    446 
    447 /* Scroll the region of the_display starting at START, ending at END, and
    448    moving the lines AMOUNT lines.  If AMOUNT is less than zero, the lines
    449    are moved up in the screen, otherwise down.  Actually, it is possible
    450    for no scrolling to take place in the case that the terminal doesn't
    451    support it.  This doesn't matter to us. */
    452 void
    453 display_scroll_display (int start, int end, int amount)
    454 {
    455   register int i, last;
    456   DISPLAY_LINE *temp;
    457 
    458   /* If this terminal cannot do scrolling, give up now. */
    459   if (!terminal_can_scroll)
    460     return;
    461 
    462   /* If there isn't anything displayed on the screen because it is too
    463      small, quit now. */
    464   if (!the_display[0])
    465     return;
    466 
    467   /* If there is typeahead pending, then don't actually do any scrolling. */
    468   if (info_any_buffered_input_p ())
    469     return;
    470 
    471   /* Do it on the screen. */
    472   terminal_scroll_terminal (start, end, amount);
    473 
    474   /* Now do it in the display buffer so our contents match the screen. */
    475   if (amount > 0)
    476     {
    477       last = end + amount;
    478 
    479       /* Shift the lines to scroll right into place. */
    480       for (i = 0; i < (end - start); i++)
    481         {
    482           temp = the_display[last - i];
    483           the_display[last - i] = the_display[end - i];
    484           the_display[end - i] = temp;
    485         }
    486 
    487       /* The lines have been shifted down in the buffer.  Clear all of the
    488          lines that were vacated. */
    489       for (i = start; i != (start + amount); i++)
    490         {
    491           the_display[i]->text[0] = '\0';
    492           the_display[i]->textlen = 0;
    493           the_display[i]->inverse = 0;
    494         }
    495     }
    496 
    497   if (amount < 0)
    498     {
    499       last = start + amount;
    500       for (i = 0; i < (end - start); i++)
    501         {
    502           temp = the_display[last + i];
    503           the_display[last + i] = the_display[start + i];
    504           the_display[start + i] = temp;
    505         }
    506 
    507       /* The lines have been shifted up in the buffer.  Clear all of the
    508          lines that are left over. */
    509       for (i = end + amount; i != end; i++)
    510         {
    511           the_display[i]->text[0] = '\0';
    512           the_display[i]->textlen = 0;
    513           the_display[i]->inverse = 0;
    514         }
    515     }
    516 }
    517 
    518 /* Try to scroll lines in WINDOW.  OLD_PAGETOP is the pagetop of WINDOW before
    519    having had its line starts recalculated.  OLD_STARTS is the list of line
    520    starts that used to appear in this window.  OLD_COUNT is the number of lines
    521    that appear in the OLD_STARTS array. */
    522 void
    523 display_scroll_line_starts (WINDOW *window, int old_pagetop,
    524     char **old_starts, int old_count)
    525 {
    526   register int i, old, new;     /* Indices into the line starts arrays. */
    527   int last_new, last_old;       /* Index of the last visible line. */
    528   int old_first, new_first;     /* Index of the first changed line. */
    529   int unchanged_at_top = 0;
    530   int already_scrolled = 0;
    531 
    532   /* Locate the first line which was displayed on the old window. */
    533   old_first = old_pagetop;
    534   new_first = window->pagetop;
    535 
    536   /* Find the last line currently visible in this window. */
    537   last_new = window->pagetop + (window->height - 1);
    538   if (last_new > window->line_count)
    539     last_new = window->line_count - 1;
    540 
    541   /* Find the last line which used to be currently visible in this window. */
    542   last_old = old_pagetop + (window->height - 1);
    543   if (last_old > old_count)
    544     last_old = old_count - 1;
    545 
    546   for (old = old_first, new = new_first;
    547        old < last_old && new < last_new;
    548        old++, new++)
    549     if (old_starts[old] != window->line_starts[new])
    550       break;
    551     else
    552       unchanged_at_top++;
    553 
    554   /* Loop through the old lines looking for a match in the new lines. */
    555   for (old = old_first + unchanged_at_top; old < last_old; old++)
    556     {
    557       for (new = new_first; new < last_new; new++)
    558         if (old_starts[old] == window->line_starts[new])
    559           {
    560             /* Find the extent of the matching lines. */
    561             for (i = 0; (old + i) < last_old; i++)
    562               if (old_starts[old + i] != window->line_starts[new + i])
    563                 break;
    564 
    565             /* Scroll these lines if there are enough of them. */
    566             {
    567               int start, end, amount;
    568 
    569               start = (window->first_row
    570                        + ((old + already_scrolled) - old_pagetop));
    571               amount = new - (old + already_scrolled);
    572               end = window->first_row + window->height;
    573 
    574               /* If we are shifting the block of lines down, then the last
    575                  AMOUNT lines will become invisible.  Thus, don't bother
    576                  scrolling them. */
    577               if (amount > 0)
    578                 end -= amount;
    579 
    580               if ((end - start) > 0)
    581                 {
    582                   display_scroll_display (start, end, amount);
    583 
    584                   /* Some lines have been scrolled.  Simulate the scrolling
    585                      by offsetting the value of the old index. */
    586                   old += i;
    587                   already_scrolled += amount;
    588                 }
    589             }
    590           }
    591     }
    592 }
    593 
    594 /* Move the screen cursor to directly over the current character in WINDOW. */
    595 void
    596 display_cursor_at_point (WINDOW *window)
    597 {
    598   int vpos, hpos;
    599 
    600   vpos = window_line_of_point (window) - window->pagetop + window->first_row;
    601   hpos = window_get_cursor_column (window);
    602   terminal_goto_xy (hpos, vpos);
    603   fflush (stdout);
    604 }
    605 
    606 /* **************************************************************** */
    608 /*                                                                  */
    609 /*                   Functions Static to this File                  */
    610 /*                                                                  */
    611 /* **************************************************************** */
    612 
    613 /* Make a DISPLAY_LINE ** with width and height. */
    614 static DISPLAY_LINE **
    615 make_display (int width, int height)
    616 {
    617   register int i;
    618   DISPLAY_LINE **display;
    619 
    620   display = (DISPLAY_LINE **)xmalloc ((1 + height) * sizeof (DISPLAY_LINE *));
    621 
    622   for (i = 0; i < height; i++)
    623     {
    624       display[i] = (DISPLAY_LINE *)xmalloc (sizeof (DISPLAY_LINE));
    625       display[i]->text = (char *)xmalloc (1 + width);
    626       display[i]->textlen = 0;
    627       display[i]->inverse = 0;
    628     }
    629   display[i] = (DISPLAY_LINE *)NULL;
    630   return (display);
    631 }
    632 
    633 /* Free the storage allocated to DISPLAY. */
    634 static void
    635 free_display (DISPLAY_LINE **display)
    636 {
    637   register int i;
    638   register DISPLAY_LINE *display_line;
    639 
    640   if (!display)
    641     return;
    642 
    643   for (i = 0; (display_line = display[i]); i++)
    644     {
    645       free (display_line->text);
    646       free (display_line);
    647     }
    648   free (display);
    649 }
    650