1 /* display.c -- readline redisplay facility. */
3 /* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc.
5 This file is part of the GNU Readline Library, a library for
6 reading lines of text with interactive input and history editing.
8 The GNU Readline Library is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 1, or
11 (at your option) any later version.
13 The GNU Readline Library is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 The GNU General Public License is often shipped with GNU software, and
19 is generally kept in a file called COPYING or LICENSE. If you do not
20 have a copy of the license, write to the Free Software Foundation,
21 675 Mass Ave, Cambridge, MA 02139, USA. */
24 #include <sys/types.h>
26 /* System-specific feature definitions and include files. */
29 /* Some standard library routines. */
33 #if !defined (strrchr)
34 extern char *strrchr ();
37 /* Global and pseudo-global variables and functions
38 imported from readline.c. */
39 extern char *rl_prompt;
40 extern int readline_echoing_p;
41 extern char *term_clreol, *term_im, *term_ic, *term_ei, *term_DC;
42 /* Termcap variables. */
43 extern char *term_up, *term_dc, *term_cr, *term_IC;
44 extern int screenheight, screenwidth, terminal_can_insert, term_xn;
46 extern void _rl_output_some_chars ();
47 extern int _rl_output_character_function ();
49 extern int _rl_convert_meta_chars_to_ascii;
50 extern int _rl_horizontal_scroll_mode;
51 extern int _rl_mark_modified_lines;
52 extern int _rl_prefer_visible_bell;
54 /* Pseudo-global functions (local to the readline library) exported
56 void _rl_move_cursor_relative (), _rl_output_some_chars ();
57 void _rl_move_vert ();
59 static void update_line (), clear_to_eol ();
60 static void delete_chars (), insert_some_chars ();
62 extern char *xmalloc (), *xrealloc ();
64 /* **************************************************************** */
68 /* **************************************************************** */
70 /* This is the stuff that is hard for me. I never seem to write good
71 display routines in C. Let's see how I do this time. */
73 /* (PWP) Well... Good for a simple line updater, but totally ignores
74 the problems of input lines longer than the screen width.
76 update_line and the code that calls it makes a multiple line,
77 automatically wrapping line update. Carefull attention needs
78 to be paid to the vertical position variables. */
80 /* Keep two buffers; one which reflects the current contents of the
81 screen, and the other to draw what we think the new contents should
82 be. Then compare the buffers, and make whatever changes to the
83 screen itself that we should. Finally, make the buffer that we
84 just drew into be the one which reflects the current contents of the
85 screen, and place the cursor where it belongs.
87 Commands that want to can fix the display themselves, and then let
88 this function know that the display has been fixed by setting the
89 RL_DISPLAY_FIXED variable. This is good for efficiency. */
91 /* What YOU turn on when you have handled all redisplay yourself. */
92 int rl_display_fixed = 0;
94 /* The stuff that gets printed out before the actual text of the line.
95 This is usually pointing to rl_prompt. */
96 char *rl_display_prompt = (char *)NULL;
98 /* Pseudo-global variables declared here. */
99 /* The visible cursor position. If you print some text, adjust this. */
100 int _rl_last_c_pos = 0;
101 int _rl_last_v_pos = 0;
103 /* Number of lines currently on screen minus 1. */
104 int _rl_vis_botlin = 0;
106 /* Variables used only in this file. */
107 /* The last left edge of text that was displayed. This is used when
108 doing horizontal scrolling. It shifts in thirds of a screenwidth. */
109 static int last_lmargin = 0;
111 /* The line display buffers. One is the line currently displayed on
112 the screen. The other is the line about to be displayed. */
113 static char *visible_line = (char *)NULL;
114 static char *invisible_line = (char *)NULL;
116 /* A buffer for `modeline' messages. */
117 static char msg_buf[128];
119 /* Non-zero forces the redisplay even if we thought it was unnecessary. */
120 static int forced_display = 0;
122 /* Default and initial buffer size. Can grow. */
123 static int line_size = 1024;
125 /* Basic redisplay algorithm. */
128 register int in, out, c, linenum;
129 register char *line = invisible_line;
130 char *prompt_this_line;
132 int inv_botlin = 0; /* Number of lines in newly drawn buffer. */
134 if (!readline_echoing_p)
137 if (!rl_display_prompt)
138 rl_display_prompt = "";
142 visible_line = (char *)xmalloc (line_size);
143 invisible_line = (char *)xmalloc (line_size);
144 line = invisible_line;
145 for (in = 0; in < line_size; in++)
147 visible_line[in] = 0;
148 invisible_line[in] = 1;
153 /* Draw the line into the buffer. */
156 /* Mark the line as modified or not. We only do this for history
159 if (_rl_mark_modified_lines && current_history () && rl_undo_list)
165 /* If someone thought that the redisplay was handled, but the currently
166 visible line has a different modification state than the one about
167 to become visible, then correct the caller's misconception. */
168 if (visible_line[0] != invisible_line[0])
169 rl_display_fixed = 0;
171 prompt_this_line = strrchr (rl_display_prompt, '\n');
172 if (!prompt_this_line)
173 prompt_this_line = rl_display_prompt;
178 _rl_output_some_chars
179 (rl_display_prompt, prompt_this_line - rl_display_prompt);
182 strncpy (line + out, prompt_this_line, strlen (prompt_this_line));
183 out += strlen (prompt_this_line);
186 for (in = 0; in < rl_end; in++)
188 c = (unsigned char)rl_line_buffer[in];
190 if (out + 8 >= line_size) /* XXX - 8 for \t */
193 visible_line = (char *)xrealloc (visible_line, line_size);
194 invisible_line = (char *)xrealloc (invisible_line, line_size);
195 line = invisible_line;
203 if (_rl_convert_meta_chars_to_ascii)
205 sprintf (line + out, "\\%o", c);
212 #if defined (DISPLAY_TABS)
215 register int newout = (out | (int)7) + 1;
223 line[out++] = UNCTRL (c); /* XXX was c ^ 0x40 */
237 /* PWP: now is when things get a bit hairy. The visible and invisible
238 line buffers are really multiple lines, which would wrap every
239 screenwidth characters. Go through each in turn, finding
240 the changed region and updating it. The line order is top to bottom. */
242 /* If we can move the cursor up and down, then use multiple lines,
243 otherwise, let long lines display in a single terminal line, and
244 horizontally scroll it. */
246 if (!_rl_horizontal_scroll_mode && term_up && *term_up)
248 int total_screen_chars = (screenwidth * screenheight);
250 if (!rl_display_fixed || forced_display)
254 /* If we have more than a screenful of material to display, then
255 only display a screenful. We should display the last screen,
256 not the first. I'll fix this in a minute. */
257 if (out >= total_screen_chars)
258 out = total_screen_chars - 1;
260 /* Number of screen lines to display. */
261 inv_botlin = out / screenwidth;
263 /* For each line in the buffer, do the updating display. */
264 for (linenum = 0; linenum <= inv_botlin; linenum++)
265 update_line (linenum > _rl_vis_botlin ? ""
266 : &visible_line[linenum * screenwidth],
267 &invisible_line[linenum * screenwidth],
270 /* We may have deleted some lines. If so, clear the left over
271 blank ones at the bottom out. */
272 if (_rl_vis_botlin > inv_botlin)
275 for (; linenum <= _rl_vis_botlin; linenum++)
277 tt = &visible_line[linenum * screenwidth];
278 _rl_move_vert (linenum);
279 _rl_move_cursor_relative (0, tt);
281 ((linenum == _rl_vis_botlin) ? strlen (tt) : screenwidth);
284 _rl_vis_botlin = inv_botlin;
286 /* Move the cursor where it should be. */
287 _rl_move_vert (c_pos / screenwidth);
288 _rl_move_cursor_relative (c_pos % screenwidth,
289 &invisible_line[(c_pos / screenwidth) * screenwidth]);
292 else /* Do horizontal scrolling. */
296 /* Always at top line. */
299 /* If the display position of the cursor would be off the edge
300 of the screen, start the display of this line at an offset that
301 leaves the cursor on the screen. */
302 if (c_pos - last_lmargin > screenwidth - 2)
303 lmargin = (c_pos / (screenwidth / 3) - 2) * (screenwidth / 3);
304 else if (c_pos - last_lmargin < 1)
305 lmargin = ((c_pos - 1) / (screenwidth / 3)) * (screenwidth / 3);
307 lmargin = last_lmargin;
309 /* If the first character on the screen isn't the first character
310 in the display line, indicate this with a special character. */
314 if (lmargin + screenwidth < out)
315 line[lmargin + screenwidth - 1] = '>';
317 if (!rl_display_fixed || forced_display || lmargin != last_lmargin)
320 update_line (&visible_line[last_lmargin],
321 &invisible_line[lmargin], 0);
323 _rl_move_cursor_relative (c_pos - lmargin, &invisible_line[lmargin]);
324 last_lmargin = lmargin;
327 fflush (rl_outstream);
329 /* Swap visible and non-visible lines. */
331 char *temp = visible_line;
332 visible_line = invisible_line;
333 invisible_line = temp;
334 rl_display_fixed = 0;
338 /* PWP: update_line() is based on finding the middle difference of each
339 line on the screen; vis:
341 /old first difference
342 /beginning of line | /old last same /old EOL
344 old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as
345 new: eddie> Oh, my little buggy says to me, as lurgid as
347 \beginning of line | \new last same \new end of line
348 \new first difference
350 All are character pointers for the sake of speed. Special cases for
351 no differences, as well as for end of line additions must be handeled.
353 Could be made even smarter, but this works well enough */
355 update_line (old, new, current_line)
356 register char *old, *new;
359 register char *ofd, *ols, *oe, *nfd, *nls, *ne;
360 int lendiff, wsatend;
362 if (_rl_last_c_pos == screenwidth && term_xn && new[0])
364 putc (new[0], rl_outstream);
371 /* Find first difference. */
372 for (ofd = old, nfd = new;
373 (ofd - old < screenwidth) && *ofd && (*ofd == *nfd);
377 /* Move to the end of the screen line. */
378 for (oe = ofd; ((oe - old) < screenwidth) && *oe; oe++);
379 for (ne = nfd; ((ne - new) < screenwidth) && *ne; ne++);
381 /* If no difference, continue to next line. */
382 if (ofd == oe && nfd == ne)
385 wsatend = 1; /* flag for trailing whitespace */
386 ols = oe - 1; /* find last same */
388 while ((ols > ofd) && (nls > nfd) && (*ols == *nls))
401 else if (*ols != *nls)
403 if (*ols) /* don't step past the NUL */
409 _rl_move_vert (current_line);
410 _rl_move_cursor_relative (ofd - old, old);
412 /* if (len (new) > len (old)) */
413 lendiff = (nls - nfd) - (ols - ofd);
415 /* Insert (diff (len (old), len (new)) ch. */
418 if (terminal_can_insert)
420 /* Sometimes it is cheaper to print the characters rather than
421 use the terminal's capabilities. */
422 if ((2 * (ne - nfd)) < lendiff && !term_IC)
424 _rl_output_some_chars (nfd, (ne - nfd));
425 _rl_last_c_pos += (ne - nfd);
431 insert_some_chars (nfd, lendiff);
432 _rl_last_c_pos += lendiff;
436 /* At the end of a line the characters do not have to
437 be "inserted". They can just be placed on the screen. */
438 _rl_output_some_chars (nfd, lendiff);
439 _rl_last_c_pos += lendiff;
441 /* Copy (new) chars to screen from first diff to last match. */
442 if (((nls - nfd) - lendiff) > 0)
444 _rl_output_some_chars (&nfd[lendiff], ((nls - nfd) - lendiff));
445 _rl_last_c_pos += ((nls - nfd) - lendiff);
450 { /* cannot insert chars, write to EOL */
451 _rl_output_some_chars (nfd, (ne - nfd));
452 _rl_last_c_pos += (ne - nfd);
455 else /* Delete characters from line. */
457 /* If possible and inexpensive to use terminal deletion, then do so. */
458 if (term_dc && (2 * (ne - nfd)) >= (-lendiff))
461 delete_chars (-lendiff); /* delete (diff) characters */
463 /* Copy (new) chars to screen from first diff to last match */
466 _rl_output_some_chars (nfd, (nls - nfd));
467 _rl_last_c_pos += (nls - nfd);
470 /* Otherwise, print over the existing material. */
473 _rl_output_some_chars (nfd, (ne - nfd));
474 _rl_last_c_pos += (ne - nfd);
475 clear_to_eol ((oe - old) - (ne - new));
480 /* Tell the update routines that we have moved onto a new (empty) line. */
484 visible_line[0] = '\0';
486 _rl_last_c_pos = _rl_last_v_pos = 0;
487 _rl_vis_botlin = last_lmargin = 0;
490 /* Actually update the display, period. */
491 rl_forced_update_display ()
495 register char *temp = visible_line;
497 while (*temp) *temp++ = '\0';
504 /* Move the cursor from _rl_last_c_pos to NEW, which are buffer indices.
505 DATA is the contents of the screen line of interest; i.e., where
506 the movement is being done. */
508 _rl_move_cursor_relative (new, data)
514 /* It may be faster to output a CR, and then move forwards instead
515 of moving backwards. */
516 if (new + 1 < _rl_last_c_pos - new)
519 putc('\r', rl_outstream);
521 tputs (term_cr, 1, _rl_output_character_function);
526 if (_rl_last_c_pos == new) return;
528 if (_rl_last_c_pos < new)
530 /* Move the cursor forward. We do it by printing the command
531 to move the cursor forward if there is one, else print that
532 portion of the output buffer again. Which is cheaper? */
534 /* The above comment is left here for posterity. It is faster
535 to print one character (non-control) than to print a control
536 sequence telling the terminal to move forward one character.
537 That kind of control is for people who don't know what the
538 data is underneath the cursor. */
539 #if defined (HACK_TERMCAP_MOTION)
540 extern char *term_forward_char;
542 if (term_forward_char)
543 for (i = _rl_last_c_pos; i < new; i++)
544 tputs (term_forward_char, 1, _rl_output_character_function);
546 for (i = _rl_last_c_pos; i < new; i++)
547 putc (data[i], rl_outstream);
549 for (i = _rl_last_c_pos; i < new; i++)
550 putc (data[i], rl_outstream);
551 #endif /* HACK_TERMCAP_MOTION */
554 backspace (_rl_last_c_pos - new);
555 _rl_last_c_pos = new;
558 /* PWP: move the cursor up or down. */
563 register int delta, i;
565 if (_rl_last_v_pos == to || to > screenheight)
571 ScreenGetCursor (&row, &col);
572 ScreenSetCursor ((row + to - _rl_last_v_pos), col);
575 if ((delta = to - _rl_last_v_pos) > 0)
577 for (i = 0; i < delta; i++)
578 putc ('\n', rl_outstream);
579 tputs (term_cr, 1, _rl_output_character_function);
584 if (term_up && *term_up)
585 for (i = 0; i < -delta; i++)
586 tputs (term_up, 1, _rl_output_character_function);
588 #endif /* !__GO32__ */
589 _rl_last_v_pos = to; /* Now TO is here */
592 /* Physically print C on rl_outstream. This is for functions which know
593 how to optimize the display. */
597 if (META_CHAR (c) && _rl_convert_meta_chars_to_ascii)
599 fprintf (rl_outstream, "M-");
603 #if defined (DISPLAY_TABS)
604 if (c < 32 && c != '\t')
607 #endif /* !DISPLAY_TABS */
613 putc (c, rl_outstream);
614 fflush (rl_outstream);
618 rl_character_len (c, pos)
622 return (_rl_convert_meta_chars_to_ascii ? 4 : 1);
626 #if defined (DISPLAY_TABS)
627 return (((pos | (int)7) + 1) - pos);
630 #endif /* !DISPLAY_TABS */
639 /* How to print things in the "echo-area". The prompt is treated as a
642 #if defined (HAVE_VARARGS_H)
643 rl_message (va_alist)
650 format = va_arg (args, char *);
651 vsprintf (msg_buf, format, args);
654 rl_display_prompt = msg_buf;
657 #else /* !HAVE_VARARGS_H */
658 rl_message (format, arg1, arg2)
661 sprintf (msg_buf, format, arg1, arg2);
662 rl_display_prompt = msg_buf;
665 #endif /* !HAVE_VARARGS_H */
667 /* How to clear things from the "echo-area". */
670 rl_display_prompt = rl_prompt;
674 rl_reset_line_state ()
678 rl_display_prompt = rl_prompt ? rl_prompt : "";
682 /* Quick redisplay hack when erasing characters at the end of the line. */
684 _rl_erase_at_end_of_line (l)
690 for (i = 0; i < l; i++)
691 putc (' ', rl_outstream);
693 for (i = 0; i < l; i++)
694 visible_line[--_rl_last_c_pos] = '\0';
698 /* Clear to the end of the line. COUNT is the minimum
699 number of character spaces to clear, */
707 tputs (term_clreol, 1, _rl_output_character_function);
710 #endif /* !__GO32__ */
714 /* Do one more character space. */
717 for (i = 0; i < count; i++)
718 putc (' ', rl_outstream);
723 /* Insert COUNT characters from STRING to the output stream. */
725 insert_some_chars (string, count)
733 ScreenGetCursor (&row, &col);
734 width = ScreenCols ();
735 row_start = ScreenPrimary + (row * width);
736 memcpy (row_start + col + count, row_start + col, width - col - count);
737 /* Place the text on the screen. */
738 _rl_output_some_chars (string, count);
740 /* If IC is defined, then we do not have to "enter" insert mode. */
743 char *tgoto (), *buffer;
744 buffer = tgoto (term_IC, 0, count);
745 tputs (buffer, 1, _rl_output_character_function);
746 _rl_output_some_chars (string, count);
752 /* If we have to turn on insert-mode, then do so. */
753 if (term_im && *term_im)
754 tputs (term_im, 1, _rl_output_character_function);
756 /* If there is a special command for inserting characters, then
757 use that first to open up the space. */
758 if (term_ic && *term_ic)
760 for (i = count; i--; )
761 tputs (term_ic, 1, _rl_output_character_function);
764 /* Print the text. */
765 _rl_output_some_chars (string, count);
767 /* If there is a string to turn off insert mode, we had best use
769 if (term_ei && *term_ei)
770 tputs (term_ei, 1, _rl_output_character_function);
772 #endif /* __GO32__ */
775 /* Delete COUNT characters from the display line. */
780 #if defined (__GO32__)
784 ScreenGetCursor (&row, &col);
785 width = ScreenCols ();
786 row_start = ScreenPrimary + (row * width);
787 memcpy (row_start + col, row_start + col + count, width - col - count);
788 memset (row_start + width - count, 0, count * 2);
789 #else /* !__GO32__ */
790 if (count > screenwidth)
793 if (term_DC && *term_DC)
795 char *tgoto (), *buffer;
796 buffer = tgoto (term_DC, 0, count);
797 tputs (buffer, 1, _rl_output_character_function);
801 if (term_dc && *term_dc)
803 tputs (term_dc, 1, _rl_output_character_function);
805 #endif /* !__GO32__ */