1 /* display.c -- How to display Info windows.
2
3 Copyright 1993-2023 Free Software Foundation, Inc.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 Originally written by Brian Fox. */
19
20 #include "info.h"
21 #include "display.h"
22 #include "session.h"
23 #include "util.h"
24 #include "tag.h"
25 #include "signals.h"
26 #include "variables.h"
27
28 static void free_display (DISPLAY_LINE **display);
29 static DISPLAY_LINE **make_display (int width, int height);
30
31 /* An array of display lines which tell us what is currently visible on
32 the display. */
33 DISPLAY_LINE **the_display = NULL;
34
35 /* Non-zero means do no output. */
36 int display_inhibited = 0;
37
38 /* Initialize THE_DISPLAY to WIDTH and HEIGHT, with nothing in it. */
39 void
40 display_initialize_display (int width, int height)
41 {
42 free_display (the_display);
43 the_display = make_display (width, height);
44 display_clear_display (the_display);
45 }
46
47 /* Clear all of the lines in DISPLAY making the screen blank. */
48 void
49 display_clear_display (DISPLAY_LINE **display)
50 {
51 register int i;
52
53 signal_block_winch ();
54 for (i = 0; display[i]; i++)
55 {
56 display[i]->text[0] = '\0';
57 display[i]->textlen = 0;
58 display[i]->inverse = 0;
59 }
60 signal_unblock_winch ();
61 }
62
63 /* Non-zero if we didn't completely redisplay a window. */
64 int display_was_interrupted_p = 0;
65
66 /* Check each window on the screen, and update it if it needs updating. */
67 void
68 display_update_display (void)
69 {
70 register WINDOW *win;
71
72 /* Block window resize signals (SIGWINCH) while accessing the the_display
73 object, because the signal handler may reallocate it out from under our
74 feet. */
75 signal_block_winch ();
76 display_was_interrupted_p = 0;
77
78 for (win = windows; win; win = win->next)
79 {
80 /* Only re-display visible windows which need updating. */
81 if ((win->flags & W_WindowVisible) == 0
82 || (win->flags & W_UpdateWindow) == 0
83 || win->height == 0)
84 continue;
85
86 display_update_one_window (win);
87 if (display_was_interrupted_p)
88 break;
89 }
90
91 /* Always update the echo area. */
92 display_update_one_window (the_echo_area);
93 signal_unblock_winch ();
94 }
95
96 /* Return the screen column of where to write to screen to update line to
97 match A, given that B contains the current state of the line. *PPOS gets
98 the offset into the string A to write from. */
99 static int
100 find_diff (const char *a, size_t alen, const char *b, size_t blen, int *ppos)
101 {
102 mbi_iterator_t itra, itrb;
103 int i;
104 int pos = 0;
105 int first_escape = -1;
106 int escape_pos = -1;
107
108 for (i = 0, mbi_init (itra, a, alen), mbi_init (itrb, b, blen);
109 mbi_avail (itra) && mbi_avail (itrb);
110 i += wcwidth (itra.cur.wc), mbi_advance (itra), mbi_advance (itrb))
111 {
112 if (mb_cmp (mbi_cur (itra), mbi_cur (itrb)))
113 break;
114
115 if (first_escape == -1 && *mbi_cur_ptr (itra) == '\033')
116 {
117 first_escape = i;
118 escape_pos = pos;
119 }
120 pos += mb_len (mbi_cur (itra));
121 }
122
123 if (mbi_avail (itra) || mbi_avail (itrb))
124 {
125 if (first_escape != -1)
126 {
127 *ppos = escape_pos;
128 return first_escape;
129 }
130 else
131 {
132 /* If there was a difference in the line, and there was an escape
133 character, return the position of the escape character, as it could
134 start a terminal escape sequence. */
135 *ppos = pos;
136 return i;
137 }
138 }
139
140 /* Otherwise, no redrawing is required. */
141 return -1;
142 }
143
144 /* Update line PL_NUM of the screen to be PRINTED_LINE, which is PL_BYTES long
145 and takes up PL_CHARS columns. */
146 static int
147 display_update_line (long pl_num, char *printed_line,
148 long pl_bytes, long pl_chars)
149 {
150 DISPLAY_LINE **display = the_display;
151 DISPLAY_LINE *entry;
152
153 entry = display[pl_num];
154
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
157 on the screen. */
158
159 /* If the window is very small, entry might be NULL. */
160 if (entry)
161 {
162 int i, off;
163
164 /* If the screen line is inversed, or if the entry is marked as
165 invalid, then clear the line from the screen first. */
166 if (entry->inverse)
167 {
168 terminal_goto_xy (0, pl_num);
169 terminal_clear_to_eol ();
170 entry->inverse = 0;
171 entry->text[0] = '\0';
172 entry->textlen = 0;
173 }
174
175 i = find_diff (printed_line, pl_bytes,
176 entry->text, strlen (entry->text), &off);
177
178 /* If the lines differed at all, we must do some redrawing. */
179 if (i != -1)
180 {
181 /* Move to the proper point on the terminal. */
182 terminal_goto_xy (i, pl_num);
183
184 /* If there is any text to print, print it. */
185 terminal_put_text (printed_line + off);
186
187 /* If the printed text didn't extend all the way to the edge
188 of the screen, and text was appearing between here and the
189 edge of the screen, clear from here to the end of the
190 line. */
191 if ((pl_chars < screenwidth && pl_chars < entry->textlen)
192 || entry->inverse)
193 terminal_clear_to_eol ();
194
195 fflush (stdout);
196
197 /* Update the display text buffer. */
198 if (strlen (printed_line) > (unsigned int) screenwidth)
199 /* printed_line[] can include more than screenwidth
200 characters, e.g. if multibyte encoding is used or
201 if we are under -R and there are escape sequences
202 in it. However, entry->text was allocated (in
203 display_initialize_display) for screenwidth
204 bytes only. */
205 entry->text = xrealloc (entry->text, strlen (printed_line) + 1);
206 strcpy (entry->text + off, printed_line + off);
207 entry->textlen = pl_chars;
208
209 /* Lines showing node text are not in inverse. Only modelines
210 have that distinction. */
211 entry->inverse = 0;
212 }
213 }
214
215 /* A line has been displayed, and the screen reflects that state.
216 If there is typeahead pending, then let that typeahead be read
217 now, instead of continuing with the display. */
218 if (info_any_buffered_input_p ())
219 {
220 display_was_interrupted_p = 1;
221 return 1;
222 }
223 return 0;
224 }
225
226
227 /* Similar to decide_if_in_match, but used for reference highlighting.
228 Given an array REFERENCES with regions, starting at *REF_INDEX decide
229 if we are inside a region at offset OFF. The regions are assumed not
230 to overlap and to be in order. */
231 static void
232 decide_if_in_reference (long off, int *in_ref, REFERENCE **references,
233 int *ref_index)
234 {
235 int i = *ref_index;
236 int m = *in_ref;
237
238 for (; (references[i]); i++)
239 {
240 if (references[i]->start > off)
241 break;
242
243 m = 1;
244
245 if (references[i]->end > off)
246 break;
247
248 m = 0;
249 }
250
251 *ref_index = i;
252 *in_ref = m;
253 }
254
255 /* Used when processing a line to be displayed from a node. DEFAULT is the
256 value when the line has no special styles like underlined references or
257 highlighted search matches. Otherwise, a line is processed once with
258 COLLECT as the value, and if it differs to what is on the display already,
259 it is processed with WRITEOUT and written to the display. */
260 static int writing_out;
261 #define DEFAULT 0
262 #define COLLECT 1
263 #define WRITEOUT 2 /* Values for writing_out global. */
264
265 /* Combine rendition masks that are active, in order of priority,
266 then check what's currently active on the display, and output
267 the necessary codes to switch. The list of rendition masks is
268 the complete information about what the style should now be.
269 RENDITION3 takes priority over RENDITION2, which in turn takes
270 priority over RENDITION1. */
271 static void
272 wrap_terminal_switch_rendition (struct text_buffer *printed_line,
273 RENDITION rendition1,
274 RENDITION rendition2,
275 RENDITION rendition3)
276 {
277 long int desired_rendition = 0;
278 desired_rendition = rendition1.value;
279 desired_rendition &= ~rendition2.mask;
280 desired_rendition |= rendition2.value;
281 desired_rendition &= ~rendition3.mask;
282 desired_rendition |= rendition3.value;
283
284 if (writing_out == WRITEOUT)
285 terminal_switch_rendition (desired_rendition);
286 else
287 {
288 /* Guarantee that each byte is non-zero, by having at least one
289 non-zero bit in it. See ZERO1_MASK symbol in display.c. */
290 desired_rendition = ~desired_rendition;
291
292 /* The text added here is only used internally to see when the
293 display has changed, and is not output to the terminal. */
294 text_buffer_add_string (printed_line, "\033", 1);
295 text_buffer_add_string (printed_line, (char *) &desired_rendition,
296 sizeof (long));
297 }
298 }
299
300 /* Set in display_update_node_text if matches or references are to be
301 distinguished with terminal appearance modes. */
302 static MATCH_STATE *matches;
303 static REFERENCE **refs;
304 static size_t match_index;
305 static int ref_index;
306
307 /* Number of screen columns output so far in a line. */
308 static int pl_chars;
309
310 /* Whether we are currently outputting a highlighted reference. This can be
311 carried over from one line to another. */
312 static int ref_highlighted;
313
314 static int pl_num; /* Number of printed lines done so far. */
315
316 RENDITION ref_rendition = {UNDERLINE_MASK, UNDERLINE_MASK};
317 RENDITION hl_ref_rendition = {BOLD_MASK, BOLD_MASK};
318 RENDITION match_rendition = {STANDOUT_MASK, STANDOUT_MASK};
319
320
321 /* Process a line from the node in WIN starting at ITER, and advancing ITER
322 to the end of the line. What is done with the line depends on the value
323 of WRITING_OUT.
324 If the line ends in a newline character, set *DELIM to 1. */
325 static void
326 display_process_line (WINDOW *win,
327 mbi_iterator_t *iter_inout,
328 struct text_buffer *tb_printed_line,
329 int *delim)
330 {
331 mbi_iterator_t iter;
332 const char *cur_ptr;
333 size_t pchars = 0; /* Printed chars */
334 size_t pbytes = 0; /* Bytes to output. */
335 char *rep;
336 int in_match = 0;
337 int in_ref = 0, in_ref_proper = 0;
338 RENDITION empty = {0, 0};
339
340 int point_in_line;
341
342 if (win->point >= win->line_starts[win->pagetop + pl_num]
343 && win->point < win->line_starts[win->pagetop + pl_num + 1])
344 point_in_line = 1;
345 else
346 point_in_line = 0;
347
348 iter = *iter_inout;
349
350 while (1)
351 {
352 int was_in_ref_proper = in_ref_proper;
353 int was_in_match = in_match;
354
355 if (!mbi_avail (iter))
356 break;
357 cur_ptr = mbi_cur_ptr (iter);
358
359 if (matches && matches_ready (matches)
360 && !at_end_of_matches (matches, match_index))
361 {
362 int was_in_match = in_match;
363 decide_if_in_match (cur_ptr - win->node->contents,
364 &in_match, matches, &match_index);
365
366 if (!was_in_match && in_match && writing_out == DEFAULT)
367 writing_out = COLLECT;
368 }
369
370 if (refs && refs[ref_index])
371 {
372 int was_in_ref = in_ref;
373 decide_if_in_reference (cur_ptr - win->node->contents,
374 &in_ref, refs, &ref_index);
375
376 if (was_in_ref && !in_ref)
377 {
378 in_ref_proper = ref_highlighted = 0;
379 }
380 else if (!was_in_ref && in_ref)
381 {
382 if (writing_out == DEFAULT)
383 writing_out = COLLECT;
384
385 /* Decide if this reference should be highlighted. */
386 if (point_in_line && win->point < refs[ref_index]->end)
387 {
388 /* The reference in is the part of the line after
389 the cursor, or the reference contains the cursor. */
390 point_in_line = 0;
391 ref_highlighted = 1;
392 }
393 else if (point_in_line
394 && (!refs[ref_index + 1]
395 || refs[ref_index + 1]->start
396 >= win->line_starts[win->pagetop + pl_num + 1]))
397 {
398 /* The reference label is before the cursor in
399 the current line and none occurs after it in
400 the current line. */
401 point_in_line = 0;
402 ref_highlighted = 1;
403 }
404 else if (win->point >= refs[ref_index]->start
405 && win->point < refs[ref_index]->end)
406 {
407 /* The point is in a cross-reference, but not in the
408 current line. */
409 ref_highlighted = 1;
410 }
411 else if (win->point >= win->line_starts
412 [win->pagetop + pl_num + 1]
413 && win->point < win->line_starts
414 [win->pagetop + pl_num + 2]
415 && refs[ref_index]->end
416 >= win->line_starts[win->pagetop + pl_num + 1]
417 && (!refs[ref_index + 1]
418 || refs[ref_index + 1]->start
419 >= win->line_starts[win->pagetop + pl_num + 2]))
420 {
421 /* Point is in the next line, not inside this reference,
422 but this reference continues onto the next line and
423 no other reference follows it in the line. */
424 ref_highlighted = 1;
425 }
426 }
427 }
428
429 if (in_ref && !in_ref_proper && !strchr (" \t", *cur_ptr))
430 in_ref_proper = 1;
431
432 if (was_in_ref_proper != in_ref_proper || was_in_match != in_match)
433 {
434 /* Calculate the new rendition for output characters, and call
435 the function to switch to it. */
436 RENDITION ref = {0, 0};
437 RENDITION match = {0, 0};
438
439 if (in_ref_proper)
440 ref = ref_highlighted && hl_ref_rendition.mask
441 ? hl_ref_rendition : ref_rendition;
442 if (in_match)
443 match = match_rendition;
444 if (!ref_highlighted)
445 {
446 wrap_terminal_switch_rendition (tb_printed_line,
447 ref, match, empty);
448 }
449 else
450 {
451 wrap_terminal_switch_rendition (tb_printed_line,
452 match, ref, empty);
453 }
454 }
455
456 rep = printed_representation (&iter, delim, pl_chars,
457 &pchars, &pbytes);
458
459 /* If a newline character has been seen, or we have reached the
460 edge of the display. */
461 if (*delim || pl_chars + pchars >= win->width)
462 break;
463
464 if (rep)
465 {
466 if (writing_out != WRITEOUT)
467 text_buffer_add_string (tb_printed_line, rep, pbytes);
468 else
469 terminal_write_chars (rep, pbytes);
470
471 pl_chars += pchars;
472 }
473 mbi_advance (iter);
474 }
475
476 if (writing_out != DEFAULT)
477 wrap_terminal_switch_rendition (tb_printed_line, empty, empty, empty);
478
479 *iter_inout = iter;
480 }
481
482 /* Update the part of WIN containing text from a node, i.e. not the blank
483 part at the end or a modeline.
484 Print each line in the window into our local buffer, and then
485 check the contents of that buffer against the display. If they
486 differ, update the display. Return number of lines printed. */
487 int
488 display_update_node_text (WINDOW *win)
489 {
490 static struct text_buffer tb_printed_line; /* Buffer for a printed line. */
491
492 mbi_iterator_t iter; /* Used to iterate through part of node displayed. */
493 mbi_iterator_t bol_iter; /* Keep reference to beginning of each line. */
494 int bol_ref_index = 0, bol_match_index = 0;
495 int bol_ref_highlighted;
496
497 int finish;
498
499 matches = 0;
500 refs = 0;
501 if (match_rendition.mask)
502 matches = &win->matches;
503 if (ref_rendition.mask || hl_ref_rendition.mask)
504 refs = win->node->references;
505
506 pl_num = 0;
507
508 ref_highlighted = 0;
509
510 writing_out = DEFAULT; /* Global variable, declared above. */
511 ref_index = match_index = 0;
512
513 mbi_init (iter, win->node->contents + win->line_starts[win->pagetop],
514 win->node->nodelen - win->line_starts[win->pagetop]);
515 mbi_avail (iter);
516 while (1)
517 {
518 int delim;
519 mbi_copy (&bol_iter, &iter);
520 bol_ref_index = ref_index;
521 bol_match_index = match_index;
522 bol_ref_highlighted = ref_highlighted;
523
524 /* Come back here at end of line when write_out == COLLECT */
525 start_of_line:
526 pl_chars = 0;
527
528 text_buffer_reset (&tb_printed_line);
529
530 delim = 0;
531 /* Check if we have processed all the lines in the window. */
532 if (pl_num == win->height)
533 break;
534
535 /* Check if this line of the window is off the screen. This might
536 happen if the screen was resized very small. */
537 if (win->first_row + pl_num >= screenheight)
538 break;
539
540 display_process_line (win, &iter, &tb_printed_line, &delim);
541
542 /* End of printed line. */
543 text_buffer_add_char (&tb_printed_line, '\0');
544
545 finish = 0;
546 /* If there are no highlighted regions in a line, we output the line with
547 display_update_line, which does some optimization of the redisplay.
548 Otherwise, the entire line is output in this function. */
549 if (writing_out == DEFAULT)
550 {
551 finish = display_update_line (win->first_row + pl_num,
552 text_buffer_base (&tb_printed_line),
553 text_buffer_off (&tb_printed_line) - 1,
554 pl_chars);
555 }
556 else if (writing_out == COLLECT)
557 {
558 /* Check if the line differs from what is already on the display,
559 and if so, go back to the start of the line and display it for
560 real. */
561 DISPLAY_LINE *entry = the_display[win->first_row + pl_num];
562 if (strcmp (tb_printed_line.base,
563 the_display[win->first_row + pl_num]->text))
564 {
565 if (tb_printed_line.off > screenwidth)
566 {
567 entry->text = xrealloc (entry->text,
568 tb_printed_line.off + 1);
569 }
570 strcpy (entry->text, tb_printed_line.base);
571 /* Record that the contents of this DISPLAY_LINE isn't
572 literally what is on the display. */
573 entry->textlen = 0;
574 entry->inverse = 1;
575 mbi_copy (&iter, &bol_iter);
576 mbi_avail (bol_iter);
577 ref_index = bol_ref_index;
578 match_index = bol_match_index;
579 terminal_goto_xy (0, win->first_row + pl_num);
580 ref_highlighted = bol_ref_highlighted;
581 writing_out = WRITEOUT;
582 goto start_of_line;
583 }
584 else
585 writing_out = DEFAULT;
586 }
587 else /* writing_out == WRITEOUT */
588 {
589 /* We have just written out this line to the display. */
590 terminal_clear_to_eol ();
591 writing_out = DEFAULT;
592 }
593
594 /* Check if a line continuation character should be displayed.
595 Don't print one on the very last line of the display, as this could
596 cause it to scroll. */
597 if (delim)
598 mbi_advance (iter);
599 else if (win->first_row + pl_num <= the_screen->height - 2)
600 {
601 terminal_goto_xy (win->width - 1, win->first_row + pl_num);
602
603 if (!(win->flags & W_NoWrap))
604 terminal_put_text ("\\");
605 else
606 {
607 terminal_put_text ("$");
608
609 /* If this window has chosen not to wrap lines, skip to the
610 end of the logical line in the buffer, and start a new
611 line here. */
612 for (; mbi_avail (iter); mbi_advance (iter))
613 if (mb_len (mbi_cur (iter)) == 1
614 && *mbi_cur_ptr (iter) == '\n')
615 {
616 mbi_advance (iter);
617 break;
618 }
619 }
620 fflush (stdout);
621 }
622
623 pl_num++;
624 if (finish)
625 break; /* Display was interrupted by typed input. */
626
627 if (!mbi_avail (iter))
628 break;
629 }
630
631 /* Unlike search match highlighting, we always turn reference highlighting
632 off at the end of each line, so the following isn't needed. */
633 /* terminal_end_underline (); */
634
635 return pl_num;
636 }
637 #undef DEFAULT
638 #undef COLLECT
639 #undef WRITEOUT /* values for writing_out global */
640
641 /* Update one window on the screen. */
642 void
643 display_update_one_window (WINDOW *win)
644 {
645 size_t line_index = 0;
646 DISPLAY_LINE **display = the_display;
647
648 signal_block_winch ();
649
650 /* If display is inhibited, that counts as an interrupted display. */
651 if (display_inhibited)
652 {
653 display_was_interrupted_p = 1;
654 goto funexit;
655 }
656
657 /* If the window has no height, quit now. Strictly speaking, it
658 should only be necessary to test if the values are equal to zero, since
659 window_new_screen_size should ensure that the window height/width never
660 becomes negative, but since historically this has often been the culprit
661 for crashes, do our best to be doubly safe. */
662 if (win->height <= 0 || win->width <= 0)
663 goto funexit;
664
665 /* If the window's first row doesn't appear in the_screen, then it
666 cannot be displayed. This can happen when the_echo_area is the
667 window to be displayed, and the screen has shrunk to less than one
668 line. */
669 if ((win->first_row < 0) || (win->first_row > the_screen->height))
670 goto funexit;
671
672 /* If this window has a modeline, it might need to be redisplayed. Do
673 this before the rest of the window to aid in navigation in case the
674 rest of the window is slow to update (for example, if it has lots of
675 search matches to be displayed). */
676 if (!(win->flags & W_InhibitMode))
677 {
678 window_make_modeline (win);
679 line_index = win->first_row + win->height;
680
681 /* This display line must both be in inverse, and have the same
682 contents. */
683 if ((!display[line_index]->inverse
684 || (strcmp (display[line_index]->text, win->modeline) != 0))
685 /* Check screen isn't very small. */
686 && line_index < the_screen->height)
687 {
688 terminal_goto_xy (0, line_index);
689 terminal_begin_inverse ();
690 terminal_put_text (win->modeline);
691 terminal_end_inverse ();
692 strcpy (display[line_index]->text, win->modeline);
693 display[line_index]->inverse = 1;
694 display[line_index]->textlen = strlen (win->modeline);
695 }
696 }
697
698 if (win->node)
699 {
700 if (!win->line_starts)
701 calculate_line_starts (win);
702 line_index = display_update_node_text (win);
703
704 if (display_was_interrupted_p)
705 goto funexit;
706 }
707
708 /* We have reached the end of the node or the end of the window. If it
709 is the end of the node, then clear the lines of the window from here
710 to the end of the window. */
711 for (; line_index < win->height; line_index++)
712 {
713 DISPLAY_LINE *entry = display[win->first_row + line_index];
714
715 /* If this line has text on it, or if we don't know what is on the line,
716 clear this line. */
717 if (entry->textlen || entry->inverse)
718 {
719 entry->textlen = 0;
720 entry->text[0] = '\0';
721 entry->inverse = 0;
722
723 terminal_goto_xy (0, win->first_row + line_index);
724 terminal_clear_to_eol ();
725 fflush (stdout);
726
727 if (info_any_buffered_input_p ())
728 {
729 display_was_interrupted_p = 1;
730 goto funexit;
731 }
732 }
733 }
734
735 fflush (stdout);
736
737 /* Okay, this window doesn't need updating anymore. */
738 win->flags &= ~W_UpdateWindow;
739 funexit:
740 signal_unblock_winch ();
741 }
742
743 /* Scroll screen lines from START inclusive to END exclusive down
744 by AMOUNT lines. Negative AMOUNT means move them up. */
745 static void
746 display_scroll_region (int start, int end, int amount)
747 {
748 int i;
749 DISPLAY_LINE *temp;
750
751 /* Do it on the screen. */
752 terminal_scroll_region (start, end, amount);
753
754 /* Now do it in the display buffer so our contents match the screen. */
755 if (amount > 0)
756 {
757 for (i = end - 1; i >= start + amount; i--)
758 {
759 /* Swap rows i and (i - amount). */
760 temp = the_display[i];
761 the_display[i] = the_display[i - amount];
762 the_display[i - amount] = temp;
763 }
764
765 /* Clear vacated lines */
766 for (i = start; i < start + amount && i < end; i++)
767 {
768 the_display[i]->text[0] = '\0';
769 the_display[i]->textlen = 0;
770 the_display[i]->inverse = 0;
771 }
772 }
773 else
774 {
775 amount *= -1;
776 for (i = start; i <= end - 1 - amount; i++)
777 {
778 /* Swap rows i and (i + amount). */
779 temp = the_display[i];
780 the_display[i] = the_display[i + amount];
781 the_display[i + amount] = temp;
782 }
783
784 /* Clear vacated lines */
785 for (i = end - 1; i >= end - amount && i >= start; i--)
786 {
787 the_display[i]->text[0] = '\0';
788 the_display[i]->textlen = 0;
789 the_display[i]->inverse = 0;
790 }
791 }
792 }
793
794 /* Scroll the region of the_display starting at START, ending at END, and
795 moving the lines AMOUNT lines. If AMOUNT is less than zero, the lines
796 are moved up in the screen, otherwise down. Actually, it is possible
797 for no scrolling to take place in the case that the terminal doesn't
798 support it. This doesn't matter to us. */
799 void
800 display_scroll_display (int start, int end, int amount)
801 {
802 register int i, last;
803 DISPLAY_LINE *temp;
804
805 /* If this terminal cannot do scrolling, give up now. */
806 if (!terminal_can_scroll && !terminal_can_scroll_region)
807 return;
808
809 /* If there isn't anything displayed on the screen because it is too
810 small, quit now. */
811 if (!the_display[0])
812 return;
813
814 /* If there is typeahead pending, then don't actually do any scrolling. */
815 if (info_any_buffered_input_p ())
816 return;
817
818 /* Use scrolling region if we can because it doesn't affect the area
819 below the area we want to scroll. */
820 if (terminal_can_scroll_region)
821 {
822 display_scroll_region (start, end, amount);
823 return;
824 }
825
826 /* Otherwise scroll by deleting and inserting lines. */
827
828 if (amount < 0)
829 start -= amount;
830 else
831 end -= amount;
832
833 /* Do it on the screen. */
834 terminal_scroll_terminal (start, end, amount);
835
836 /* Now do it in the display buffer so our contents match the screen. */
837 if (amount > 0)
838 {
839 last = end + amount;
840
841 /* Shift the lines to scroll right into place. */
842 for (i = 1; i <= (end - start); i++)
843 {
844 temp = the_display[last - i];
845 the_display[last - i] = the_display[end - i];
846 the_display[end - i] = temp;
847 }
848
849 /* The lines have been shifted down in the buffer. Clear all of the
850 lines that were vacated. */
851 for (i = start; i != (start + amount); i++)
852 {
853 the_display[i]->text[0] = '\0';
854 the_display[i]->textlen = 0;
855 the_display[i]->inverse = 0;
856 }
857 }
858 else
859 {
860 last = start + amount;
861 for (i = 0; i < (end - start); i++)
862 {
863 temp = the_display[last + i];
864 the_display[last + i] = the_display[start + i];
865 the_display[start + i] = temp;
866 }
867
868 /* The lines have been shifted up in the buffer. Clear all of the
869 lines that are left over. */
870 for (i = end + amount; i != end; i++)
871 {
872 the_display[i]->text[0] = '\0';
873 the_display[i]->textlen = 0;
874 the_display[i]->inverse = 0;
875 }
876 }
877 }
878
879 /* Try to scroll lines in WINDOW. OLD_PAGETOP is the pagetop of WINDOW before
880 having had its line starts recalculated. OLD_STARTS is the list of line
881 starts that used to appear in this window. OLD_COUNT is the number of lines
882 that appear in the OLD_STARTS array. */
883 void
884 display_scroll_line_starts (WINDOW *window, int old_pagetop,
885 long *old_starts, int old_count)
886 {
887 register int i, old, new; /* Indices into the line starts arrays. */
888 int last_new, last_old; /* Index of the last visible line. */
889 int old_first, new_first; /* Index of the first changed line. */
890 int unchanged_at_top = 0;
891 int already_scrolled = 0;
892
893 /* Locate the first line which was displayed on the old window. */
894 old_first = old_pagetop;
895 new_first = window->pagetop;
896
897 /* Find the last line currently visible in this window. */
898 last_new = window->pagetop + (window->height - 1);
899 if (last_new > window->line_count)
900 last_new = window->line_count - 1;
901
902 /* Find the last line which used to be currently visible in this window. */
903 last_old = old_pagetop + (window->height - 1);
904 if (last_old > old_count)
905 last_old = old_count - 1;
906
907 for (old = old_first, new = new_first;
908 old < last_old && new < last_new;
909 old++, new++)
910 if (old_starts[old] != window->line_starts[new])
911 break;
912 else
913 unchanged_at_top++;
914
915 /* Loop through the old lines looking for a match in the new lines. */
916 for (old = old_first + unchanged_at_top; old < last_old; old++)
917 {
918 for (new = new_first; new < last_new; new++)
919 if (old_starts[old] == window->line_starts[new])
920 {
921 /* Find the extent of the matching lines. */
922 for (i = 0; (old + i) < last_old; i++)
923 if (old_starts[old + i] != window->line_starts[new + i])
924 break;
925
926 /* Scroll these lines if there are enough of them. */
927 {
928 int start, end, amount;
929
930 start = (window->first_row
931 + ((old + already_scrolled) - old_pagetop));
932 amount = new - (old + already_scrolled);
933 end = window->first_row + window->height;
934
935 /* If we are shifting the block of lines down, then the last
936 AMOUNT lines will become invisible. Thus, don't bother
937 scrolling them. */
938 if (amount > 0)
939 end -= amount;
940
941 if ((end - start) > 0)
942 {
943 display_scroll_display (start, end, amount);
944
945 /* Some lines have been scrolled. Simulate the scrolling
946 by offsetting the value of the old index. */
947 old += i;
948 already_scrolled += amount;
949 }
950 }
951 }
952 }
953 }
954
955 /* Move the screen cursor to directly over the current character in WINDOW. */
956 void
957 display_cursor_at_point (WINDOW *window)
958 {
959 int vpos, hpos;
960
961 vpos = window_line_of_point (window) - window->pagetop + window->first_row;
962 hpos = window_get_cursor_column (window);
963 terminal_goto_xy (hpos, vpos);
964 fflush (stdout);
965 }
966
967 /* **************************************************************** */
968 /* */
969 /* Functions Static to this File */
970 /* */
971 /* **************************************************************** */
972
973 /* Make a DISPLAY_LINE ** with width and height. */
974 static DISPLAY_LINE **
975 make_display (int width, int height)
976 {
977 register int i;
978 DISPLAY_LINE **display;
979
980 display = xmalloc ((1 + height) * sizeof (DISPLAY_LINE *));
981
982 for (i = 0; i < height; i++)
983 {
984 display[i] = xmalloc (sizeof (DISPLAY_LINE));
985 display[i]->text = xmalloc (1 + width);
986 display[i]->textlen = 0;
987 display[i]->inverse = 0;
988 }
989 display[i] = NULL;
990 return display;
991 }
992
993 /* Free the storage allocated to DISPLAY. */
994 static void
995 free_display (DISPLAY_LINE **display)
996 {
997 register int i;
998 register DISPLAY_LINE *display_line;
999
1000 if (!display)
1001 return;
1002
1003 for (i = 0; (display_line = display[i]); i++)
1004 {
1005 free (display_line->text);
1006 free (display_line);
1007 }
1008 free (display);
1009 }
1010