1 /* terminal.c -- how to handle the physical terminal for Info.
2 $Id: terminal.c,v 1.7 2008/06/11 09:55:43 gray Exp $
4 Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1996, 1997, 1998,
5 1999, 2001, 2002, 2004, 2007, 2008 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). */
26 #include <sys/types.h>
29 /* The Unix termcap interface code. */
30 #ifdef HAVE_NCURSES_TERMCAP_H
31 #include <ncurses/termcap.h>
36 /* On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC.
37 Unfortunately, PC is a global variable used by the termcap library. */
40 /* Termcap requires these variables, whether we access them or not. */
42 char PC; /* Pad character */
43 short ospeed; /* Terminal output baud rate */
44 extern int tgetnum (), tgetflag (), tgetent ();
45 extern char *tgetstr (), *tgoto ();
47 #endif /* not HAVE_TERMCAP_H */
48 #endif /* not HAVE_NCURSES_TERMCAP_H */
50 /* Function "hooks". If you make one of these point to a function, that
51 function is called when appropriate instead of its namesake. Your
52 function is called with exactly the same arguments that were passed
53 to the namesake function. */
54 VFunction *terminal_begin_inverse_hook = NULL;
55 VFunction *terminal_end_inverse_hook = NULL;
56 VFunction *terminal_prep_terminal_hook = NULL;
57 VFunction *terminal_unprep_terminal_hook = NULL;
58 VFunction *terminal_up_line_hook = NULL;
59 VFunction *terminal_down_line_hook = NULL;
60 VFunction *terminal_clear_screen_hook = NULL;
61 VFunction *terminal_clear_to_eol_hook = NULL;
62 VFunction *terminal_get_screen_size_hook = NULL;
63 VFunction *terminal_goto_xy_hook = NULL;
64 VFunction *terminal_initialize_terminal_hook = NULL;
65 VFunction *terminal_new_terminal_hook = NULL;
66 VFunction *terminal_put_text_hook = NULL;
67 VFunction *terminal_ring_bell_hook = NULL;
68 VFunction *terminal_write_chars_hook = NULL;
69 VFunction *terminal_scroll_terminal_hook = NULL;
71 /* **************************************************************** */
73 /* Terminal and Termcap */
75 /* **************************************************************** */
77 /* A buffer which holds onto the current terminal description, and a pointer
78 used to float within it. And the name of the terminal. */
79 static char *term_buffer = NULL;
80 static char *term_string_buffer = NULL;
81 static char *term_name;
83 /* Some strings to control terminal actions. These are output by tputs (). */
84 static char *term_goto, *term_clreol, *term_cr, *term_clrpag;
85 static char *term_begin_use, *term_end_use;
86 static char *term_AL, *term_DL, *term_al, *term_dl;
88 static char *term_keypad_on, *term_keypad_off;
90 /* How to go up a line. */
93 /* How to go down a line. */
96 /* An audible bell, if the terminal can be made to make noise. */
97 static char *audible_bell;
99 /* A visible bell, if the terminal can be made to flash the screen. */
100 static char *visible_bell;
102 /* The string to write to turn on the meta key, if this term has one. */
103 static char *term_mm;
105 /* The string to turn on inverse mode, if this term has one. */
106 static char *term_invbeg;
108 /* The string to turn off inverse mode, if this term has one. */
109 static char *term_invend;
111 /* Although I can't find any documentation that says this is supposed to
112 return its argument, all the code I've looked at (termutils, less)
115 output_character_function (int c)
121 /* Macro to send STRING to the terminal. */
122 #define send_to_terminal(string) \
125 tputs (string, 1, output_character_function); \
128 /* Tell the terminal that we will be doing cursor addressable motion. */
130 terminal_begin_using_terminal (void)
132 RETSIGTYPE (*sigsave) (int signum);
135 send_to_terminal (term_keypad_on);
137 if (!term_begin_use || !*term_begin_use)
141 sigsave = signal (SIGWINCH, SIG_IGN);
144 send_to_terminal (term_begin_use);
146 if (STREQ (term_name, "sun-cmd"))
147 /* Without this fflush and sleep, running info in a shelltool or
148 cmdtool (TERM=sun-cmd) with scrollbars loses -- the scrollbars are
149 not restored properly.
150 From: strube@physik3.gwdg.de (Hans Werner Strube). */
154 signal (SIGWINCH, sigsave);
158 /* Tell the terminal that we will not be doing any more cursor
159 addressable motion. */
161 terminal_end_using_terminal (void)
163 RETSIGTYPE (*sigsave) (int signum);
166 send_to_terminal (term_keypad_off);
168 if (!term_end_use || !*term_end_use)
172 sigsave = signal (SIGWINCH, SIG_IGN);
175 send_to_terminal (term_end_use);
177 if (STREQ (term_name, "sun-cmd"))
178 /* See comments at other sleep. */
182 signal (SIGWINCH, sigsave);
186 /* **************************************************************** */
188 /* Necessary Terminal Functions */
190 /* **************************************************************** */
192 /* The functions and variables on this page implement the user visible
193 portion of the terminal interface. */
195 /* The width and height of the terminal. */
196 int screenwidth, screenheight;
198 /* Non-zero means this terminal can't really do anything. */
199 int terminal_is_dumb_p = 0;
201 /* Non-zero means that this terminal has a meta key. */
202 int terminal_has_meta_p = 0;
204 /* Non-zero means that this terminal can produce a visible bell. */
205 int terminal_has_visible_bell_p = 0;
207 /* Non-zero means to use that visible bell if at all possible. */
208 int terminal_use_visible_bell_p = 0;
210 /* Non-zero means that the terminal can do scrolling. */
211 int terminal_can_scroll = 0;
213 /* The key sequences output by the arrow keys, if this terminal has any. */
214 char *term_ku = NULL;
215 char *term_kd = NULL;
216 char *term_kr = NULL;
217 char *term_kl = NULL;
218 char *term_kP = NULL; /* page-up */
219 char *term_kN = NULL; /* page-down */
220 char *term_kh = NULL; /* home */
221 char *term_ke = NULL; /* end */
222 char *term_kD = NULL; /* delete */
223 char *term_ki = NULL; /* ins */
224 char *term_kx = NULL; /* del */
226 /* Move the cursor to the terminal location of X and Y. */
228 terminal_goto_xy (int x, int y)
230 if (terminal_goto_xy_hook)
231 (*terminal_goto_xy_hook) (x, y);
235 tputs (tgoto (term_goto, x, y), 1, output_character_function);
239 /* Print STRING to the terminal at the current position. */
241 terminal_put_text (char *string)
243 if (terminal_put_text_hook)
244 (*terminal_put_text_hook) (string);
247 printf ("%s", string);
251 /* Print NCHARS from STRING to the terminal at the current position. */
253 terminal_write_chars (char *string, int nchars)
255 if (terminal_write_chars_hook)
256 (*terminal_write_chars_hook) (string, nchars);
260 fwrite (string, 1, nchars, stdout);
264 /* Clear from the current position of the cursor to the end of the line. */
266 terminal_clear_to_eol (void)
268 if (terminal_clear_to_eol_hook)
269 (*terminal_clear_to_eol_hook) ();
272 send_to_terminal (term_clreol);
276 /* Clear the entire terminal screen. */
278 terminal_clear_screen (void)
280 if (terminal_clear_screen_hook)
281 (*terminal_clear_screen_hook) ();
284 send_to_terminal (term_clrpag);
288 /* Move the cursor up one line. */
290 terminal_up_line (void)
292 if (terminal_up_line_hook)
293 (*terminal_up_line_hook) ();
296 send_to_terminal (term_up);
300 /* Move the cursor down one line. */
302 terminal_down_line (void)
304 if (terminal_down_line_hook)
305 (*terminal_down_line_hook) ();
308 send_to_terminal (term_dn);
312 /* Turn on reverse video if possible. */
314 terminal_begin_inverse (void)
316 if (terminal_begin_inverse_hook)
317 (*terminal_begin_inverse_hook) ();
320 send_to_terminal (term_invbeg);
324 /* Turn off reverse video if possible. */
326 terminal_end_inverse (void)
328 if (terminal_end_inverse_hook)
329 (*terminal_end_inverse_hook) ();
332 send_to_terminal (term_invend);
336 /* Ring the terminal bell. The bell is run visibly if it both has one and
337 terminal_use_visible_bell_p is non-zero. */
339 terminal_ring_bell (void)
341 if (terminal_ring_bell_hook)
342 (*terminal_ring_bell_hook) ();
345 if (terminal_has_visible_bell_p && terminal_use_visible_bell_p)
346 send_to_terminal (visible_bell);
348 send_to_terminal (audible_bell);
352 /* At the line START, delete COUNT lines from the terminal display. */
354 terminal_delete_lines (int start, int count)
358 /* Normalize arguments. */
362 lines = screenheight - start;
363 terminal_goto_xy (0, start);
365 tputs (tgoto (term_DL, 0, count), lines, output_character_function);
369 tputs (term_dl, lines, output_character_function);
375 /* At the line START, insert COUNT lines in the terminal display. */
377 terminal_insert_lines (int start, int count)
381 /* Normalize arguments. */
385 lines = screenheight - start;
386 terminal_goto_xy (0, start);
389 tputs (tgoto (term_AL, 0, count), lines, output_character_function);
393 tputs (term_al, lines, output_character_function);
399 /* Scroll an area of the terminal, starting with the region from START
400 to END, AMOUNT lines. If AMOUNT is negative, the lines are scrolled
401 towards the top of the screen, else they are scrolled towards the
402 bottom of the screen. */
404 terminal_scroll_terminal (int start, int end, int amount)
406 if (!terminal_can_scroll)
409 /* Any scrolling at all? */
413 if (terminal_scroll_terminal_hook)
414 (*terminal_scroll_terminal_hook) (start, end, amount);
417 /* If we are scrolling down, delete AMOUNT lines at END. Then insert
418 AMOUNT lines at START. */
421 terminal_delete_lines (end, amount);
422 terminal_insert_lines (start, amount);
425 /* If we are scrolling up, delete AMOUNT lines before START. This
426 actually does the upwards scroll. Then, insert AMOUNT lines
427 after the already scrolled region (i.e., END - AMOUNT). */
430 int abs_amount = -amount;
431 terminal_delete_lines (start - abs_amount, abs_amount);
432 terminal_insert_lines (end - abs_amount, abs_amount);
437 /* Re-initialize the terminal considering that the TERM/TERMCAP variable
440 terminal_new_terminal (char *terminal_name)
442 if (terminal_new_terminal_hook)
443 (*terminal_new_terminal_hook) (terminal_name);
446 terminal_initialize_terminal (terminal_name);
450 /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */
452 terminal_get_screen_size (void)
454 if (terminal_get_screen_size_hook)
455 (*terminal_get_screen_size_hook) ();
458 screenwidth = screenheight = 0;
460 #if defined (TIOCGWINSZ)
462 struct winsize window_size;
464 if (ioctl (fileno (stdout), TIOCGWINSZ, &window_size) == 0)
466 screenwidth = (int) window_size.ws_col;
467 screenheight = (int) window_size.ws_row;
470 #endif /* TIOCGWINSZ */
472 /* Environment variable COLUMNS overrides setting of "co". */
473 if (screenwidth <= 0)
475 char *sw = getenv ("COLUMNS");
478 screenwidth = atoi (sw);
480 if (screenwidth <= 0)
481 screenwidth = tgetnum ("co");
484 /* Environment variable LINES overrides setting of "li". */
485 if (screenheight <= 0)
487 char *sh = getenv ("LINES");
490 screenheight = atoi (sh);
492 if (screenheight <= 0)
493 screenheight = tgetnum ("li");
496 /* If all else fails, default to 80x24 terminal. */
497 if (screenwidth <= 0)
500 if (screenheight <= 0)
505 /* Initialize the terminal which is known as TERMINAL_NAME. If this
506 terminal doesn't have cursor addressability, `terminal_is_dumb_p'
507 becomes nonzero. The variables SCREENHEIGHT and SCREENWIDTH are set
508 to the dimensions that this terminal actually has. The variable
509 TERMINAL_HAS_META_P becomes nonzero if this terminal supports a Meta
510 key. Finally, the terminal screen is cleared. */
512 terminal_initialize_terminal (char *terminal_name)
516 terminal_is_dumb_p = 0;
518 if (terminal_initialize_terminal_hook)
520 (*terminal_initialize_terminal_hook) (terminal_name);
524 term_name = terminal_name ? terminal_name : getenv ("TERM");
528 if (!term_string_buffer)
529 term_string_buffer = xmalloc (2048);
532 term_buffer = xmalloc (2048);
534 buffer = term_string_buffer;
536 term_clrpag = term_cr = term_clreol = NULL;
538 /* HP-UX 11.x returns 0 for OK --jeff.hull@state.co.us. */
539 if (tgetent (term_buffer, term_name) < 0)
541 terminal_is_dumb_p = 1;
545 term_up = term_dn = audible_bell = visible_bell = NULL;
546 term_ku = term_kd = term_kl = term_kr = NULL;
547 term_kP = term_kN = NULL;
548 term_kh = term_ke = NULL;
553 BC = tgetstr ("pc", &buffer);
556 #if defined (HAVE_TERMIOS_H)
559 if (tcgetattr (fileno(stdout), &ti) != -1)
560 ospeed = cfgetospeed (&ti);
565 # if defined (TIOCGETP)
569 if (ioctl (fileno (stdout), TIOCGETP, &sg) != -1)
570 ospeed = sg.sg_ospeed;
576 # endif /* !TIOCGETP */
579 term_cr = tgetstr ("cr", &buffer);
580 term_clreol = tgetstr ("ce", &buffer);
581 term_clrpag = tgetstr ("cl", &buffer);
582 term_goto = tgetstr ("cm", &buffer);
584 /* Find out about this terminal's scrolling capability. */
585 term_AL = tgetstr ("AL", &buffer);
586 term_DL = tgetstr ("DL", &buffer);
587 term_al = tgetstr ("al", &buffer);
588 term_dl = tgetstr ("dl", &buffer);
590 terminal_can_scroll = ((term_AL || term_al) && (term_DL || term_dl));
592 term_invbeg = tgetstr ("mr", &buffer);
594 term_invend = tgetstr ("me", &buffer);
601 terminal_get_screen_size ();
603 term_up = tgetstr ("up", &buffer);
604 term_dn = tgetstr ("dn", &buffer);
605 visible_bell = tgetstr ("vb", &buffer);
606 terminal_has_visible_bell_p = (visible_bell != NULL);
607 audible_bell = tgetstr ("bl", &buffer);
609 audible_bell = "\007";
610 term_begin_use = tgetstr ("ti", &buffer);
611 term_end_use = tgetstr ("te", &buffer);
613 term_keypad_on = tgetstr ("ks", &buffer);
614 term_keypad_off = tgetstr ("ke", &buffer);
616 /* Check to see if this terminal has a meta key. */
617 terminal_has_meta_p = (tgetflag ("km") || tgetflag ("MT"));
618 if (terminal_has_meta_p)
620 term_mm = tgetstr ("mm", &buffer);
627 /* Attempt to find the arrow keys. */
628 term_ku = tgetstr ("ku", &buffer);
629 term_kd = tgetstr ("kd", &buffer);
630 term_kr = tgetstr ("kr", &buffer);
631 term_kl = tgetstr ("kl", &buffer);
633 term_kP = tgetstr ("kP", &buffer);
634 term_kN = tgetstr ("kN", &buffer);
637 term_kh = tgetstr ("kh", &buffer);
638 term_ke = tgetstr ("@7", &buffer);
639 term_ki = tgetstr ("kI", &buffer);
640 term_kx = tgetstr ("kD", &buffer);
641 #endif /* defined(INFOKEY) */
643 /* Home and end keys. */
644 term_kh = tgetstr ("kh", &buffer);
645 term_ke = tgetstr ("@7", &buffer);
647 term_kD = tgetstr ("kD", &buffer);
649 /* If this terminal is not cursor addressable, then it is really dumb. */
651 terminal_is_dumb_p = 1;
654 /* How to read characters from the terminal. */
656 #if defined (HAVE_TERMIOS_H)
657 struct termios original_termios, ttybuff;
659 # if defined (HAVE_TERMIO_H)
660 /* A buffer containing the terminal mode flags upon entry to info. */
661 struct termio original_termio, ttybuff;
662 # else /* !HAVE_TERMIO_H */
663 /* Buffers containing the terminal mode flags upon entry to info. */
664 int original_tty_flags = 0;
666 struct sgttyb ttybuff;
668 # if defined(TIOCGETC) && defined(M_XENIX)
669 /* SCO 3.2v5.0.2 defines but does not support TIOCGETC. Gak. Maybe
670 better fix would be to use Posix termios in preference. --gildea,
675 # if defined (TIOCGETC)
676 /* A buffer containing the terminal interrupt characters upon entry
678 struct tchars original_tchars;
681 # if defined (TIOCGLTC)
682 /* A buffer containing the local terminal mode characters upon entry
684 struct ltchars original_ltchars;
686 # endif /* !HAVE_TERMIO_H */
687 #endif /* !HAVE_TERMIOS_H */
689 /* Prepare to start using the terminal to read characters singly. */
691 terminal_prep_terminal (void)
695 if (terminal_prep_terminal_hook)
697 (*terminal_prep_terminal_hook) ();
701 terminal_begin_using_terminal ();
703 tty = fileno (stdin);
705 #if defined (HAVE_TERMIOS_H)
706 tcgetattr (tty, &original_termios);
707 tcgetattr (tty, &ttybuff);
709 # if defined (HAVE_TERMIO_H)
710 ioctl (tty, TCGETA, &original_termio);
711 ioctl (tty, TCGETA, &ttybuff);
715 #if defined (HAVE_TERMIOS_H) || defined (HAVE_TERMIO_H)
716 ttybuff.c_iflag &= (~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL & ~IXON);
717 /* These output flags are not part of POSIX, so only use them if they
720 ttybuff.c_oflag &= ~ONLCR ;
723 ttybuff.c_oflag &= ~OCRNL;
725 ttybuff.c_lflag &= (~ICANON & ~ECHO);
727 ttybuff.c_cc[VMIN] = 1;
728 ttybuff.c_cc[VTIME] = 0;
730 if (ttybuff.c_cc[VINTR] == '\177')
731 ttybuff.c_cc[VINTR] = -1;
733 if (ttybuff.c_cc[VQUIT] == '\177')
734 ttybuff.c_cc[VQUIT] = -1;
737 if (ttybuff.c_cc[VLNEXT] == '\026')
738 ttybuff.c_cc[VLNEXT] = -1;
740 #endif /* TERMIOS or TERMIO */
742 /* cf. emacs/src/sysdep.c for being sure output is on. */
743 #if defined (HAVE_TERMIOS_H)
744 /* linux kernel 2.2.x needs a TCOFF followed by a TCOON to turn output
745 back on if the user presses ^S at the very beginning; just a TCOON
746 doesn't work. --Kevin Ryde <user42@zip.com.au>, 16jun2000. */
747 tcsetattr (tty, TCSANOW, &ttybuff);
749 tcflow (tty, TCOOFF);
753 # if defined (HAVE_TERMIO_H)
754 ioctl (tty, TCSETA, &ttybuff);
756 ioctl (tty, TCXONC, 1);
761 #if !defined (HAVE_TERMIOS_H) && !defined (HAVE_TERMIO_H)
762 ioctl (tty, TIOCGETP, &ttybuff);
764 if (!original_tty_flags)
765 original_tty_flags = ttybuff.sg_flags;
767 /* Make this terminal pass 8 bits around while we are using it. */
769 ttybuff.sg_flags |= PASS8;
772 # if defined (TIOCLGET) && defined (LPASS8)
775 ioctl (tty, TIOCLGET, &flags);
776 original_lmode = flags;
778 ioctl (tty, TIOCLSET, &flags);
780 # endif /* TIOCLGET && LPASS8 */
782 # if defined (TIOCGETC)
786 ioctl (tty, TIOCGETC, &original_tchars);
787 temp = original_tchars;
790 temp.t_startc = temp.t_stopc = -1;
792 /* Often set to C-d. */
795 /* If the a quit or interrupt character conflicts with one of our
796 commands, then make it go away. */
797 if (temp.t_intrc == '\177')
800 if (temp.t_quitc == '\177')
803 ioctl (tty, TIOCSETC, &temp);
805 # endif /* TIOCGETC */
807 # if defined (TIOCGLTC)
811 ioctl (tty, TIOCGLTC, &original_ltchars);
812 temp = original_ltchars;
814 /* Make the interrupt keys go away. Just enough to make people happy. */
815 temp.t_lnextc = -1; /* C-v. */
816 temp.t_dsuspc = -1; /* C-y. */
817 temp.t_flushc = -1; /* C-o. */
818 ioctl (tty, TIOCSLTC, &temp);
820 # endif /* TIOCGLTC */
822 ttybuff.sg_flags &= ~ECHO;
823 ttybuff.sg_flags |= CBREAK;
824 ioctl (tty, TIOCSETN, &ttybuff);
825 #endif /* !HAVE_TERMIOS_H && !HAVE_TERMIO_H */
828 /* Restore the tty settings back to what they were before we started using
831 terminal_unprep_terminal (void)
835 if (terminal_unprep_terminal_hook)
837 (*terminal_unprep_terminal_hook) ();
841 tty = fileno (stdin);
843 #if defined (HAVE_TERMIOS_H)
844 tcsetattr (tty, TCSANOW, &original_termios);
846 # if defined (HAVE_TERMIO_H)
847 ioctl (tty, TCSETA, &original_termio);
848 # else /* !HAVE_TERMIO_H */
849 ioctl (tty, TIOCGETP, &ttybuff);
850 ttybuff.sg_flags = original_tty_flags;
851 ioctl (tty, TIOCSETN, &ttybuff);
853 # if defined (TIOCGETC)
854 ioctl (tty, TIOCSETC, &original_tchars);
855 # endif /* TIOCGETC */
857 # if defined (TIOCGLTC)
858 ioctl (tty, TIOCSLTC, &original_ltchars);
859 # endif /* TIOCGLTC */
861 # if defined (TIOCLGET) && defined (LPASS8)
862 ioctl (tty, TIOCLSET, &original_lmode);
863 # endif /* TIOCLGET && LPASS8 */
865 # endif /* !HAVE_TERMIO_H */
866 #endif /* !HAVE_TERMIOS_H */
867 terminal_end_using_terminal ();