1 /* display.c -- How to display Info windows.
2 $Id: display.c,v 1.16 2008/06/11 09:55:41 gray Exp $
4 Copyright (C) 1993, 1997, 2003, 2004, 2006, 2007, 2008
5 Free Software Foundation, Inc.
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 Originally written by Brian Fox (bfox@ai.mit.edu). */
25 extern int info_any_buffered_input_p (void); /* Found in session.c. */
27 static void free_display (DISPLAY_LINE **display);
28 static DISPLAY_LINE **make_display (int width, int height);
30 void handle_tag (char *tag);
31 void handle_tag_start (char *tag);
32 void handle_tag_end (char *tag);
34 /* An array of display lines which tell us what is currently visible on
36 DISPLAY_LINE **the_display = NULL;
38 /* Non-zero means do no output. */
39 int display_inhibited = 0;
41 /* Initialize THE_DISPLAY to WIDTH and HEIGHT, with nothing in it. */
43 display_initialize_display (int width, int height)
45 free_display (the_display);
46 the_display = make_display (width, height);
47 display_clear_display (the_display);
50 /* Clear all of the lines in DISPLAY making the screen blank. */
52 display_clear_display (DISPLAY_LINE **display)
56 for (i = 0; display[i]; i++)
58 display[i]->text[0] = '\0';
59 display[i]->textlen = 0;
60 display[i]->inverse = 0;
64 /* Non-zero if we didn't completely redisplay a window. */
65 int display_was_interrupted_p = 0;
67 /* Update the windows pointed to by WINDOW in the_display. This actually
68 writes the text on the screen. */
70 display_update_display (WINDOW *window)
74 display_was_interrupted_p = 0;
76 /* For every window in the list, check contents against the display. */
77 for (win = window; win; win = win->next)
79 /* Only re-display visible windows which need updating. */
80 if (((win->flags & W_WindowVisible) == 0) ||
81 ((win->flags & W_UpdateWindow) == 0) ||
85 display_update_one_window (win);
86 if (display_was_interrupted_p)
90 /* Always update the echo area. */
91 display_update_one_window (the_echo_area);
95 handle_tag_start (char *tag)
97 /* TODO really handle this tag. */
102 handle_tag_end (char *tag)
104 /* TODO really handle this tag. */
109 handle_tag (char *tag)
114 handle_tag_end (tag);
117 handle_tag_start (tag);
121 struct display_node_closure {
123 DISPLAY_LINE **display;
127 find_diff (const char *a, size_t alen, const char *b, size_t blen, int *ppos)
129 mbi_iterator_t itra, itrb;
133 for (i = 0, mbi_init (itra, a, alen), mbi_init (itrb, b, blen);
134 mbi_avail (itra) && mbi_avail (itrb);
135 mbi_advance (itra), mbi_advance (itrb))
137 if (mb_cmp (mbi_cur (itra), mbi_cur (itrb)))
139 pos += mb_len (mbi_cur (itra));
146 display_node_text(void *closure, size_t line_index,
147 const char *src_line,
148 char *printed_line, size_t pl_index, size_t pl_count)
150 struct display_node_closure *dn = closure;
151 WINDOW *win = dn->win;
152 DISPLAY_LINE **display = dn->display;
153 DISPLAY_LINE *entry = display[win->first_row + line_index];
155 /* We have the exact line as it should appear on the screen.
156 Check to see if this line matches the one already appearing
159 /* If the window is very small, entry might be NULL. */
164 /* If the screen line is inversed, then we have to clear
165 the line from the screen first. Why, I don't know.
166 (But don't do this if we have no visible entries, as can
167 happen if the window is shrunk very small.) */
169 /* Need to erase the line if it has escape sequences. */
170 || (raw_escapes_p && mbschr (entry->text, '\033') != 0))
172 terminal_goto_xy (0, win->first_row + line_index);
173 terminal_clear_to_eol ();
175 entry->text[0] = '\0';
179 i = find_diff (printed_line, pl_index,
180 entry->text, strlen (entry->text), &off);
182 /* If the lines are not the same length, or if they differed
183 at all, we must do some redrawing. */
184 if (i != pl_count || pl_count != entry->textlen)
186 /* Move to the proper point on the terminal. */
187 terminal_goto_xy (i, win->first_row + line_index);
188 /* If there is any text to print, print it. */
190 terminal_put_text (printed_line + i);
192 /* If the printed text didn't extend all the way to the edge
193 of the window, and text was appearing between here and the
194 edge of the window, clear from here to the end of the
196 if ((pl_count < win->width && pl_count < entry->textlen)
198 terminal_clear_to_eol ();
202 /* Update the display text buffer. */
203 if (strlen (printed_line) > (unsigned int) screenwidth)
204 /* printed_line[] can include more than screenwidth
205 characters, e.g. if multibyte encoding is used or
206 if we are under -R and there are escape sequences
207 in it. However, entry->text was allocated (in
208 display_initialize_display) for screenwidth
210 entry->text = xrealloc (entry->text, strlen (printed_line) + 1);
211 strcpy (entry->text + off, printed_line + off);
212 entry->textlen = pl_count;
214 /* Lines showing node text are not in inverse. Only modelines
215 have that distinction. */
220 /* A line has been displayed, and the screen reflects that state.
221 If there is typeahead pending, then let that typeahead be read
222 now, instead of continuing with the display. */
223 if (info_any_buffered_input_p ())
225 display_was_interrupted_p = 1;
229 if (line_index + 1 == win->height)
236 display_update_one_window (WINDOW *win)
238 size_t line_index = 0;
239 DISPLAY_LINE **display = the_display;
241 /* If display is inhibited, that counts as an interrupted display. */
242 if (display_inhibited)
243 display_was_interrupted_p = 1;
245 /* If the window has no height, or display is inhibited, quit now.
246 Strictly speaking, it should only be necessary to test if the
247 values are equal to zero, since window_new_screen_size should
248 ensure that the window height/width never becomes negative, but
249 since historically this has often been the culprit for crashes, do
250 our best to be doubly safe. */
251 if (win->height <= 0 || win->width <= 0 || display_inhibited)
254 /* If the window's first row doesn't appear in the_screen, then it
255 cannot be displayed. This can happen when the_echo_area is the
256 window to be displayed, and the screen has shrunk to less than one
258 if ((win->first_row < 0) || (win->first_row > the_screen->height))
261 if (win->node && win->line_starts)
263 struct display_node_closure dnc;
266 dnc.display = the_display;
268 line_index = process_node_text (win, win->line_starts[win->pagetop],
272 if (display_was_interrupted_p)
276 /* We have reached the end of the node or the end of the window. If it
277 is the end of the node, then clear the lines of the window from here
278 to the end of the window. */
279 for (; line_index < win->height; line_index++)
281 DISPLAY_LINE *entry = display[win->first_row + line_index];
283 /* If this line has text on it then make it go away. */
284 if (entry && entry->textlen)
287 entry->text[0] = '\0';
289 terminal_goto_xy (0, win->first_row + line_index);
290 terminal_clear_to_eol ();
294 /* Finally, if this window has a modeline it might need to be redisplayed.
295 Check the window's modeline against the one in the display, and update
297 if (!(win->flags & W_InhibitMode))
299 window_make_modeline (win);
300 line_index = win->first_row + win->height;
302 /* This display line must both be in inverse, and have the same
304 if ((!display[line_index]->inverse) ||
305 (strcmp (display[line_index]->text, win->modeline) != 0))
307 terminal_goto_xy (0, line_index);
308 terminal_begin_inverse ();
309 terminal_put_text (win->modeline);
310 terminal_end_inverse ();
311 strcpy (display[line_index]->text, win->modeline);
312 display[line_index]->inverse = 1;
313 display[line_index]->textlen = strlen (win->modeline);
320 /* Okay, this window doesn't need updating anymore. */
321 win->flags &= ~W_UpdateWindow;
324 /* Scroll the region of the_display starting at START, ending at END, and
325 moving the lines AMOUNT lines. If AMOUNT is less than zero, the lines
326 are moved up in the screen, otherwise down. Actually, it is possible
327 for no scrolling to take place in the case that the terminal doesn't
328 support it. This doesn't matter to us. */
330 display_scroll_display (int start, int end, int amount)
332 register int i, last;
335 /* If this terminal cannot do scrolling, give up now. */
336 if (!terminal_can_scroll)
339 /* If there isn't anything displayed on the screen because it is too
344 /* If there is typeahead pending, then don't actually do any scrolling. */
345 if (info_any_buffered_input_p ())
348 /* Do it on the screen. */
349 terminal_scroll_terminal (start, end, amount);
351 /* Now do it in the display buffer so our contents match the screen. */
356 /* Shift the lines to scroll right into place. */
357 for (i = 0; i < (end - start); i++)
359 temp = the_display[last - i];
360 the_display[last - i] = the_display[end - i];
361 the_display[end - i] = temp;
364 /* The lines have been shifted down in the buffer. Clear all of the
365 lines that were vacated. */
366 for (i = start; i != (start + amount); i++)
368 the_display[i]->text[0] = '\0';
369 the_display[i]->textlen = 0;
370 the_display[i]->inverse = 0;
376 last = start + amount;
377 for (i = 0; i < (end - start); i++)
379 temp = the_display[last + i];
380 the_display[last + i] = the_display[start + i];
381 the_display[start + i] = temp;
384 /* The lines have been shifted up in the buffer. Clear all of the
385 lines that are left over. */
386 for (i = end + amount; i != end; i++)
388 the_display[i]->text[0] = '\0';
389 the_display[i]->textlen = 0;
390 the_display[i]->inverse = 0;
395 /* Try to scroll lines in WINDOW. OLD_PAGETOP is the pagetop of WINDOW before
396 having had its line starts recalculated. OLD_STARTS is the list of line
397 starts that used to appear in this window. OLD_COUNT is the number of lines
398 that appear in the OLD_STARTS array. */
400 display_scroll_line_starts (WINDOW *window, int old_pagetop,
401 char **old_starts, int old_count)
403 register int i, old, new; /* Indices into the line starts arrays. */
404 int last_new, last_old; /* Index of the last visible line. */
405 int old_first, new_first; /* Index of the first changed line. */
406 int unchanged_at_top = 0;
407 int already_scrolled = 0;
409 /* Locate the first line which was displayed on the old window. */
410 old_first = old_pagetop;
411 new_first = window->pagetop;
413 /* Find the last line currently visible in this window. */
414 last_new = window->pagetop + (window->height - 1);
415 if (last_new > window->line_count)
416 last_new = window->line_count - 1;
418 /* Find the last line which used to be currently visible in this window. */
419 last_old = old_pagetop + (window->height - 1);
420 if (last_old > old_count)
421 last_old = old_count - 1;
423 for (old = old_first, new = new_first;
424 old < last_old && new < last_new;
426 if (old_starts[old] != window->line_starts[new])
431 /* Loop through the old lines looking for a match in the new lines. */
432 for (old = old_first + unchanged_at_top; old < last_old; old++)
434 for (new = new_first; new < last_new; new++)
435 if (old_starts[old] == window->line_starts[new])
437 /* Find the extent of the matching lines. */
438 for (i = 0; (old + i) < last_old; i++)
439 if (old_starts[old + i] != window->line_starts[new + i])
442 /* Scroll these lines if there are enough of them. */
444 int start, end, amount;
446 start = (window->first_row
447 + ((old + already_scrolled) - old_pagetop));
448 amount = new - (old + already_scrolled);
449 end = window->first_row + window->height;
451 /* If we are shifting the block of lines down, then the last
452 AMOUNT lines will become invisible. Thus, don't bother
457 if ((end - start) > 0)
459 display_scroll_display (start, end, amount);
461 /* Some lines have been scrolled. Simulate the scrolling
462 by offsetting the value of the old index. */
464 already_scrolled += amount;
471 /* Move the screen cursor to directly over the current character in WINDOW. */
473 display_cursor_at_point (WINDOW *window)
477 vpos = window_line_of_point (window) - window->pagetop + window->first_row;
478 hpos = window_get_cursor_column (window);
479 terminal_goto_xy (hpos, vpos);
483 /* **************************************************************** */
485 /* Functions Static to this File */
487 /* **************************************************************** */
489 /* Make a DISPLAY_LINE ** with width and height. */
490 static DISPLAY_LINE **
491 make_display (int width, int height)
494 DISPLAY_LINE **display;
496 display = xmalloc ((1 + height) * sizeof (DISPLAY_LINE *));
498 for (i = 0; i < height; i++)
500 display[i] = xmalloc (sizeof (DISPLAY_LINE));
501 display[i]->text = xmalloc (1 + width);
502 display[i]->textlen = 0;
503 display[i]->inverse = 0;
509 /* Free the storage allocated to DISPLAY. */
511 free_display (DISPLAY_LINE **display)
514 register DISPLAY_LINE *display_line;
519 for (i = 0; (display_line = display[i]); i++)
521 free (display_line->text);