1 /* terminal.c -- how to handle the physical terminal for Info.
2 $Id: terminal.c,v 1.3 2004/04/11 17:56:46 karl Exp $
4 Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1996, 1997, 1998,
5 1999, 2001, 2002, 2004 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 2, or (at your option)
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, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 Originally written by Brian Fox (bfox@ai.mit.edu). */
27 #include <sys/types.h>
30 /* The Unix termcap interface code. */
31 #ifdef HAVE_NCURSES_TERMCAP_H
32 #include <ncurses/termcap.h>
37 /* On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC.
38 Unfortunately, PC is a global variable used by the termcap library. */
41 /* Termcap requires these variables, whether we access them or not. */
43 char PC; /* Pad character */
44 short ospeed; /* Terminal output baud rate */
45 extern int tgetnum (), tgetflag (), tgetent ();
46 extern char *tgetstr (), *tgoto ();
48 #endif /* not HAVE_TERMCAP_H */
49 #endif /* not HAVE_NCURSES_TERMCAP_H */
51 /* Function "hooks". If you make one of these point to a function, that
52 function is called when appropriate instead of its namesake. Your
53 function is called with exactly the same arguments that were passed
54 to the namesake function. */
55 VFunction *terminal_begin_inverse_hook = (VFunction *)NULL;
56 VFunction *terminal_end_inverse_hook = (VFunction *)NULL;
57 VFunction *terminal_prep_terminal_hook = (VFunction *)NULL;
58 VFunction *terminal_unprep_terminal_hook = (VFunction *)NULL;
59 VFunction *terminal_up_line_hook = (VFunction *)NULL;
60 VFunction *terminal_down_line_hook = (VFunction *)NULL;
61 VFunction *terminal_clear_screen_hook = (VFunction *)NULL;
62 VFunction *terminal_clear_to_eol_hook = (VFunction *)NULL;
63 VFunction *terminal_get_screen_size_hook = (VFunction *)NULL;
64 VFunction *terminal_goto_xy_hook = (VFunction *)NULL;
65 VFunction *terminal_initialize_terminal_hook = (VFunction *)NULL;
66 VFunction *terminal_new_terminal_hook = (VFunction *)NULL;
67 VFunction *terminal_put_text_hook = (VFunction *)NULL;
68 VFunction *terminal_ring_bell_hook = (VFunction *)NULL;
69 VFunction *terminal_write_chars_hook = (VFunction *)NULL;
70 VFunction *terminal_scroll_terminal_hook = (VFunction *)NULL;
72 /* **************************************************************** */
74 /* Terminal and Termcap */
76 /* **************************************************************** */
78 /* A buffer which holds onto the current terminal description, and a pointer
79 used to float within it. And the name of the terminal. */
80 static char *term_buffer = NULL;
81 static char *term_string_buffer = NULL;
82 static char *term_name;
84 /* Some strings to control terminal actions. These are output by tputs (). */
85 static char *term_goto, *term_clreol, *term_cr, *term_clrpag;
86 static char *term_begin_use, *term_end_use;
87 static char *term_AL, *term_DL, *term_al, *term_dl;
89 static char *term_keypad_on, *term_keypad_off;
91 /* How to go up a line. */
94 /* How to go down a line. */
97 /* An audible bell, if the terminal can be made to make noise. */
98 static char *audible_bell;
100 /* A visible bell, if the terminal can be made to flash the screen. */
101 static char *visible_bell;
103 /* The string to write to turn on the meta key, if this term has one. */
104 static char *term_mm;
106 /* The string to turn on inverse mode, if this term has one. */
107 static char *term_invbeg;
109 /* The string to turn off inverse mode, if this term has one. */
110 static char *term_invend;
112 /* Although I can't find any documentation that says this is supposed to
113 return its argument, all the code I've looked at (termutils, less)
116 output_character_function (int c)
122 /* Macro to send STRING to the terminal. */
123 #define send_to_terminal(string) \
126 tputs (string, 1, output_character_function); \
129 /* Tell the terminal that we will be doing cursor addressable motion. */
131 terminal_begin_using_terminal (void)
133 RETSIGTYPE (*sigsave) (int signum);
136 send_to_terminal (term_keypad_on);
138 if (!term_begin_use || !*term_begin_use)
142 sigsave = signal (SIGWINCH, SIG_IGN);
145 send_to_terminal (term_begin_use);
147 if (STREQ (term_name, "sun-cmd"))
148 /* Without this fflush and sleep, running info in a shelltool or
149 cmdtool (TERM=sun-cmd) with scrollbars loses -- the scrollbars are
150 not restored properly.
151 From: strube@physik3.gwdg.de (Hans Werner Strube). */
155 signal (SIGWINCH, sigsave);
159 /* Tell the terminal that we will not be doing any more cursor
160 addressable motion. */
162 terminal_end_using_terminal (void)
164 RETSIGTYPE (*sigsave) (int signum);
167 send_to_terminal (term_keypad_off);
169 if (!term_end_use || !*term_end_use)
173 sigsave = signal (SIGWINCH, SIG_IGN);
176 send_to_terminal (term_end_use);
178 if (STREQ (term_name, "sun-cmd"))
179 /* See comments at other sleep. */
183 signal (SIGWINCH, sigsave);
187 /* **************************************************************** */
189 /* Necessary Terminal Functions */
191 /* **************************************************************** */
193 /* The functions and variables on this page implement the user visible
194 portion of the terminal interface. */
196 /* The width and height of the terminal. */
197 int screenwidth, screenheight;
199 /* Non-zero means this terminal can't really do anything. */
200 int terminal_is_dumb_p = 0;
202 /* Non-zero means that this terminal has a meta key. */
203 int terminal_has_meta_p = 0;
205 /* Non-zero means that this terminal can produce a visible bell. */
206 int terminal_has_visible_bell_p = 0;
208 /* Non-zero means to use that visible bell if at all possible. */
209 int terminal_use_visible_bell_p = 0;
211 /* Non-zero means that the terminal can do scrolling. */
212 int terminal_can_scroll = 0;
214 /* The key sequences output by the arrow keys, if this terminal has any. */
215 char *term_ku = NULL;
216 char *term_kd = NULL;
217 char *term_kr = NULL;
218 char *term_kl = NULL;
219 char *term_kP = NULL; /* page-up */
220 char *term_kN = NULL; /* page-down */
221 char *term_kh = NULL; /* home */
222 char *term_ke = NULL; /* end */
223 char *term_kD = NULL; /* delete */
224 char *term_ki = NULL; /* ins */
225 char *term_kx = NULL; /* del */
227 /* Move the cursor to the terminal location of X and Y. */
229 terminal_goto_xy (int x, int y)
231 if (terminal_goto_xy_hook)
232 (*terminal_goto_xy_hook) (x, y);
236 tputs (tgoto (term_goto, x, y), 1, output_character_function);
240 /* Print STRING to the terminal at the current position. */
242 terminal_put_text (char *string)
244 if (terminal_put_text_hook)
245 (*terminal_put_text_hook) (string);
248 printf ("%s", string);
252 /* Print NCHARS from STRING to the terminal at the current position. */
254 terminal_write_chars (char *string, int nchars)
256 if (terminal_write_chars_hook)
257 (*terminal_write_chars_hook) (string, nchars);
261 fwrite (string, 1, nchars, stdout);
265 /* Clear from the current position of the cursor to the end of the line. */
267 terminal_clear_to_eol (void)
269 if (terminal_clear_to_eol_hook)
270 (*terminal_clear_to_eol_hook) ();
273 send_to_terminal (term_clreol);
277 /* Clear the entire terminal screen. */
279 terminal_clear_screen (void)
281 if (terminal_clear_screen_hook)
282 (*terminal_clear_screen_hook) ();
285 send_to_terminal (term_clrpag);
289 /* Move the cursor up one line. */
291 terminal_up_line (void)
293 if (terminal_up_line_hook)
294 (*terminal_up_line_hook) ();
297 send_to_terminal (term_up);
301 /* Move the cursor down one line. */
303 terminal_down_line (void)
305 if (terminal_down_line_hook)
306 (*terminal_down_line_hook) ();
309 send_to_terminal (term_dn);
313 /* Turn on reverse video if possible. */
315 terminal_begin_inverse (void)
317 if (terminal_begin_inverse_hook)
318 (*terminal_begin_inverse_hook) ();
321 send_to_terminal (term_invbeg);
325 /* Turn off reverse video if possible. */
327 terminal_end_inverse (void)
329 if (terminal_end_inverse_hook)
330 (*terminal_end_inverse_hook) ();
333 send_to_terminal (term_invend);
337 /* Ring the terminal bell. The bell is run visibly if it both has one and
338 terminal_use_visible_bell_p is non-zero. */
340 terminal_ring_bell (void)
342 if (terminal_ring_bell_hook)
343 (*terminal_ring_bell_hook) ();
346 if (terminal_has_visible_bell_p && terminal_use_visible_bell_p)
347 send_to_terminal (visible_bell);
349 send_to_terminal (audible_bell);
353 /* At the line START, delete COUNT lines from the terminal display. */
355 terminal_delete_lines (int start, int count)
359 /* Normalize arguments. */
363 lines = screenheight - start;
364 terminal_goto_xy (0, start);
366 tputs (tgoto (term_DL, 0, count), lines, output_character_function);
370 tputs (term_dl, lines, output_character_function);
376 /* At the line START, insert COUNT lines in the terminal display. */
378 terminal_insert_lines (int start, int count)
382 /* Normalize arguments. */
386 lines = screenheight - start;
387 terminal_goto_xy (0, start);
390 tputs (tgoto (term_AL, 0, count), lines, output_character_function);
394 tputs (term_al, lines, output_character_function);
400 /* Scroll an area of the terminal, starting with the region from START
401 to END, AMOUNT lines. If AMOUNT is negative, the lines are scrolled
402 towards the top of the screen, else they are scrolled towards the
403 bottom of the screen. */
405 terminal_scroll_terminal (int start, int end, int amount)
407 if (!terminal_can_scroll)
410 /* Any scrolling at all? */
414 if (terminal_scroll_terminal_hook)
415 (*terminal_scroll_terminal_hook) (start, end, amount);
418 /* If we are scrolling down, delete AMOUNT lines at END. Then insert
419 AMOUNT lines at START. */
422 terminal_delete_lines (end, amount);
423 terminal_insert_lines (start, amount);
426 /* If we are scrolling up, delete AMOUNT lines before START. This
427 actually does the upwards scroll. Then, insert AMOUNT lines
428 after the already scrolled region (i.e., END - AMOUNT). */
431 int abs_amount = -amount;
432 terminal_delete_lines (start - abs_amount, abs_amount);
433 terminal_insert_lines (end - abs_amount, abs_amount);
438 /* Re-initialize the terminal considering that the TERM/TERMCAP variable
441 terminal_new_terminal (char *terminal_name)
443 if (terminal_new_terminal_hook)
444 (*terminal_new_terminal_hook) (terminal_name);
447 terminal_initialize_terminal (terminal_name);
451 /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */
453 terminal_get_screen_size (void)
455 if (terminal_get_screen_size_hook)
456 (*terminal_get_screen_size_hook) ();
459 screenwidth = screenheight = 0;
461 #if defined (TIOCGWINSZ)
463 struct winsize window_size;
465 if (ioctl (fileno (stdout), TIOCGWINSZ, &window_size) == 0)
467 screenwidth = (int) window_size.ws_col;
468 screenheight = (int) window_size.ws_row;
471 #endif /* TIOCGWINSZ */
473 /* Environment variable COLUMNS overrides setting of "co". */
474 if (screenwidth <= 0)
476 char *sw = getenv ("COLUMNS");
479 screenwidth = atoi (sw);
481 if (screenwidth <= 0)
482 screenwidth = tgetnum ("co");
485 /* Environment variable LINES overrides setting of "li". */
486 if (screenheight <= 0)
488 char *sh = getenv ("LINES");
491 screenheight = atoi (sh);
493 if (screenheight <= 0)
494 screenheight = tgetnum ("li");
497 /* If all else fails, default to 80x24 terminal. */
498 if (screenwidth <= 0)
501 if (screenheight <= 0)
506 /* Initialize the terminal which is known as TERMINAL_NAME. If this
507 terminal doesn't have cursor addressability, `terminal_is_dumb_p'
508 becomes nonzero. The variables SCREENHEIGHT and SCREENWIDTH are set
509 to the dimensions that this terminal actually has. The variable
510 TERMINAL_HAS_META_P becomes nonzero if this terminal supports a Meta
511 key. Finally, the terminal screen is cleared. */
513 terminal_initialize_terminal (char *terminal_name)
517 terminal_is_dumb_p = 0;
519 if (terminal_initialize_terminal_hook)
521 (*terminal_initialize_terminal_hook) (terminal_name);
525 term_name = terminal_name ? terminal_name : getenv ("TERM");
529 if (!term_string_buffer)
530 term_string_buffer = xmalloc (2048);
533 term_buffer = xmalloc (2048);
535 buffer = term_string_buffer;
537 term_clrpag = term_cr = term_clreol = NULL;
539 /* HP-UX 11.x returns 0 for OK --jeff.hull@state.co.us. */
540 if (tgetent (term_buffer, term_name) < 0)
542 terminal_is_dumb_p = 1;
546 term_up = term_dn = audible_bell = visible_bell = NULL;
547 term_ku = term_kd = term_kl = term_kr = NULL;
548 term_kP = term_kN = NULL;
549 term_kh = term_ke = NULL;
554 BC = tgetstr ("pc", &buffer);
557 #if defined (HAVE_TERMIOS_H)
560 if (tcgetattr (fileno(stdout), &ti) != -1)
561 ospeed = cfgetospeed (&ti);
566 # if defined (TIOCGETP)
570 if (ioctl (fileno (stdout), TIOCGETP, &sg) != -1)
571 ospeed = sg.sg_ospeed;
577 # endif /* !TIOCGETP */
580 term_cr = tgetstr ("cr", &buffer);
581 term_clreol = tgetstr ("ce", &buffer);
582 term_clrpag = tgetstr ("cl", &buffer);
583 term_goto = tgetstr ("cm", &buffer);
585 /* Find out about this terminal's scrolling capability. */
586 term_AL = tgetstr ("AL", &buffer);
587 term_DL = tgetstr ("DL", &buffer);
588 term_al = tgetstr ("al", &buffer);
589 term_dl = tgetstr ("dl", &buffer);
591 terminal_can_scroll = ((term_AL || term_al) && (term_DL || term_dl));
593 term_invbeg = tgetstr ("mr", &buffer);
595 term_invend = tgetstr ("me", &buffer);
602 terminal_get_screen_size ();
604 term_up = tgetstr ("up", &buffer);
605 term_dn = tgetstr ("dn", &buffer);
606 visible_bell = tgetstr ("vb", &buffer);
607 terminal_has_visible_bell_p = (visible_bell != NULL);
608 audible_bell = tgetstr ("bl", &buffer);
610 audible_bell = "\007";
611 term_begin_use = tgetstr ("ti", &buffer);
612 term_end_use = tgetstr ("te", &buffer);
614 term_keypad_on = tgetstr ("ks", &buffer);
615 term_keypad_off = tgetstr ("ke", &buffer);
617 /* Check to see if this terminal has a meta key. */
618 terminal_has_meta_p = (tgetflag ("km") || tgetflag ("MT"));
619 if (terminal_has_meta_p)
621 term_mm = tgetstr ("mm", &buffer);
628 /* Attempt to find the arrow keys. */
629 term_ku = tgetstr ("ku", &buffer);
630 term_kd = tgetstr ("kd", &buffer);
631 term_kr = tgetstr ("kr", &buffer);
632 term_kl = tgetstr ("kl", &buffer);
634 term_kP = tgetstr ("kP", &buffer);
635 term_kN = tgetstr ("kN", &buffer);
638 term_kh = tgetstr ("kh", &buffer);
639 term_ke = tgetstr ("@7", &buffer);
640 term_ki = tgetstr ("kI", &buffer);
641 term_kx = tgetstr ("kD", &buffer);
642 #endif /* defined(INFOKEY) */
644 /* Home and end keys. */
645 term_kh = tgetstr ("kh", &buffer);
646 term_ke = tgetstr ("@7", &buffer);
648 term_kD = tgetstr ("kD", &buffer);
650 /* If this terminal is not cursor addressable, then it is really dumb. */
652 terminal_is_dumb_p = 1;
655 /* How to read characters from the terminal. */
657 #if defined (HAVE_TERMIOS_H)
658 struct termios original_termios, ttybuff;
660 # if defined (HAVE_TERMIO_H)
661 /* A buffer containing the terminal mode flags upon entry to info. */
662 struct termio original_termio, ttybuff;
663 # else /* !HAVE_TERMIO_H */
664 /* Buffers containing the terminal mode flags upon entry to info. */
665 int original_tty_flags = 0;
667 struct sgttyb ttybuff;
669 # if defined(TIOCGETC) && defined(M_XENIX)
670 /* SCO 3.2v5.0.2 defines but does not support TIOCGETC. Gak. Maybe
671 better fix would be to use Posix termios in preference. --gildea,
676 # if defined (TIOCGETC)
677 /* A buffer containing the terminal interrupt characters upon entry
679 struct tchars original_tchars;
682 # if defined (TIOCGLTC)
683 /* A buffer containing the local terminal mode characters upon entry
685 struct ltchars original_ltchars;
687 # endif /* !HAVE_TERMIO_H */
688 #endif /* !HAVE_TERMIOS_H */
690 /* Prepare to start using the terminal to read characters singly. */
692 terminal_prep_terminal (void)
696 if (terminal_prep_terminal_hook)
698 (*terminal_prep_terminal_hook) ();
702 terminal_begin_using_terminal ();
704 tty = fileno (stdin);
706 #if defined (HAVE_TERMIOS_H)
707 tcgetattr (tty, &original_termios);
708 tcgetattr (tty, &ttybuff);
710 # if defined (HAVE_TERMIO_H)
711 ioctl (tty, TCGETA, &original_termio);
712 ioctl (tty, TCGETA, &ttybuff);
716 #if defined (HAVE_TERMIOS_H) || defined (HAVE_TERMIO_H)
717 ttybuff.c_iflag &= (~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL & ~IXON);
718 /* These output flags are not part of POSIX, so only use them if they
721 ttybuff.c_oflag &= ~ONLCR ;
724 ttybuff.c_oflag &= ~OCRNL;
726 ttybuff.c_lflag &= (~ICANON & ~ECHO);
728 ttybuff.c_cc[VMIN] = 1;
729 ttybuff.c_cc[VTIME] = 0;
731 if (ttybuff.c_cc[VINTR] == '\177')
732 ttybuff.c_cc[VINTR] = -1;
734 if (ttybuff.c_cc[VQUIT] == '\177')
735 ttybuff.c_cc[VQUIT] = -1;
738 if (ttybuff.c_cc[VLNEXT] == '\026')
739 ttybuff.c_cc[VLNEXT] = -1;
741 #endif /* TERMIOS or TERMIO */
743 /* cf. emacs/src/sysdep.c for being sure output is on. */
744 #if defined (HAVE_TERMIOS_H)
745 /* linux kernel 2.2.x needs a TCOFF followed by a TCOON to turn output
746 back on if the user presses ^S at the very beginning; just a TCOON
747 doesn't work. --Kevin Ryde <user42@zip.com.au>, 16jun2000. */
748 tcsetattr (tty, TCSANOW, &ttybuff);
750 tcflow (tty, TCOOFF);
754 # if defined (HAVE_TERMIO_H)
755 ioctl (tty, TCSETA, &ttybuff);
757 ioctl (tty, TCXONC, 1);
762 #if !defined (HAVE_TERMIOS_H) && !defined (HAVE_TERMIO_H)
763 ioctl (tty, TIOCGETP, &ttybuff);
765 if (!original_tty_flags)
766 original_tty_flags = ttybuff.sg_flags;
768 /* Make this terminal pass 8 bits around while we are using it. */
770 ttybuff.sg_flags |= PASS8;
773 # if defined (TIOCLGET) && defined (LPASS8)
776 ioctl (tty, TIOCLGET, &flags);
777 original_lmode = flags;
779 ioctl (tty, TIOCLSET, &flags);
781 # endif /* TIOCLGET && LPASS8 */
783 # if defined (TIOCGETC)
787 ioctl (tty, TIOCGETC, &original_tchars);
788 temp = original_tchars;
791 temp.t_startc = temp.t_stopc = -1;
793 /* Often set to C-d. */
796 /* If the a quit or interrupt character conflicts with one of our
797 commands, then make it go away. */
798 if (temp.t_intrc == '\177')
801 if (temp.t_quitc == '\177')
804 ioctl (tty, TIOCSETC, &temp);
806 # endif /* TIOCGETC */
808 # if defined (TIOCGLTC)
812 ioctl (tty, TIOCGLTC, &original_ltchars);
813 temp = original_ltchars;
815 /* Make the interrupt keys go away. Just enough to make people happy. */
816 temp.t_lnextc = -1; /* C-v. */
817 temp.t_dsuspc = -1; /* C-y. */
818 temp.t_flushc = -1; /* C-o. */
819 ioctl (tty, TIOCSLTC, &temp);
821 # endif /* TIOCGLTC */
823 ttybuff.sg_flags &= ~ECHO;
824 ttybuff.sg_flags |= CBREAK;
825 ioctl (tty, TIOCSETN, &ttybuff);
826 #endif /* !HAVE_TERMIOS_H && !HAVE_TERMIO_H */
829 /* Restore the tty settings back to what they were before we started using
832 terminal_unprep_terminal (void)
836 if (terminal_unprep_terminal_hook)
838 (*terminal_unprep_terminal_hook) ();
842 tty = fileno (stdin);
844 #if defined (HAVE_TERMIOS_H)
845 tcsetattr (tty, TCSANOW, &original_termios);
847 # if defined (HAVE_TERMIO_H)
848 ioctl (tty, TCSETA, &original_termio);
849 # else /* !HAVE_TERMIO_H */
850 ioctl (tty, TIOCGETP, &ttybuff);
851 ttybuff.sg_flags = original_tty_flags;
852 ioctl (tty, TIOCSETN, &ttybuff);
854 # if defined (TIOCGETC)
855 ioctl (tty, TIOCSETC, &original_tchars);
856 # endif /* TIOCGETC */
858 # if defined (TIOCGLTC)
859 ioctl (tty, TIOCSLTC, &original_ltchars);
860 # endif /* TIOCGLTC */
862 # if defined (TIOCLGET) && defined (LPASS8)
863 ioctl (tty, TIOCLSET, &original_lmode);
864 # endif /* TIOCLGET && LPASS8 */
866 # endif /* !HAVE_TERMIO_H */
867 #endif /* !HAVE_TERMIOS_H */
868 terminal_end_using_terminal ();