1 /* $FreeBSD: src/contrib/texinfo/info/terminal.c,v 1.6.2.1 2002/03/30 17:09:19 ru Exp $ */
2 /* $DragonFly: src/contrib/texinfo/info/Attic/terminal.c,v 1.2 2003/06/17 04:24:07 dillon Exp $ */
3 /* terminal.c -- How to handle the physical terminal for Info.
4 $Id: terminal.c,v 1.23 2001/11/16 23:16:04 karl Exp $
6 Copyright (C) 1988, 89, 90, 91, 92, 93, 96, 97, 98, 99, 2001
7 Free Software Foundation, Inc.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 Written by Brian Fox (bfox@ai.mit.edu). */
29 #include <sys/types.h>
32 /* The Unix termcap interface code. */
33 #ifdef HAVE_NCURSES_TERMCAP_H
34 #include <ncurses/termcap.h>
39 /* On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC.
40 Unfortunately, PC is a global variable used by the termcap library. */
43 /* Termcap requires these variables, whether we access them or not. */
45 char PC; /* Pad character */
46 short ospeed; /* Terminal output baud rate */
47 extern int tgetnum (), tgetflag (), tgetent ();
48 extern char *tgetstr (), *tgoto ();
50 #endif /* not HAVE_TERMCAP_H */
51 #endif /* not HAVE_NCURSES_TERMCAP_H */
53 /* Function "hooks". If you make one of these point to a function, that
54 function is called when appropriate instead of its namesake. Your
55 function is called with exactly the same arguments that were passed
56 to the namesake function. */
57 VFunction *terminal_begin_inverse_hook = (VFunction *)NULL;
58 VFunction *terminal_end_inverse_hook = (VFunction *)NULL;
59 VFunction *terminal_prep_terminal_hook = (VFunction *)NULL;
60 VFunction *terminal_unprep_terminal_hook = (VFunction *)NULL;
61 VFunction *terminal_up_line_hook = (VFunction *)NULL;
62 VFunction *terminal_down_line_hook = (VFunction *)NULL;
63 VFunction *terminal_clear_screen_hook = (VFunction *)NULL;
64 VFunction *terminal_clear_to_eol_hook = (VFunction *)NULL;
65 VFunction *terminal_get_screen_size_hook = (VFunction *)NULL;
66 VFunction *terminal_goto_xy_hook = (VFunction *)NULL;
67 VFunction *terminal_initialize_terminal_hook = (VFunction *)NULL;
68 VFunction *terminal_new_terminal_hook = (VFunction *)NULL;
69 VFunction *terminal_put_text_hook = (VFunction *)NULL;
70 VFunction *terminal_ring_bell_hook = (VFunction *)NULL;
71 VFunction *terminal_write_chars_hook = (VFunction *)NULL;
72 VFunction *terminal_scroll_terminal_hook = (VFunction *)NULL;
74 /* **************************************************************** */
76 /* Terminal and Termcap */
78 /* **************************************************************** */
80 /* A buffer which holds onto the current terminal description, and a pointer
81 used to float within it. And the name of the terminal. */
82 static char *term_buffer = NULL;
83 static char *term_string_buffer = NULL;
84 static char *term_name;
86 /* Some strings to control terminal actions. These are output by tputs (). */
87 static char *term_goto, *term_clreol, *term_cr, *term_clrpag;
88 static char *term_begin_use, *term_end_use;
89 static char *term_AL, *term_DL, *term_al, *term_dl;
91 static char *term_keypad_on, *term_keypad_off;
93 /* How to go up a line. */
96 /* How to go down a line. */
99 /* An audible bell, if the terminal can be made to make noise. */
100 static char *audible_bell;
102 /* A visible bell, if the terminal can be made to flash the screen. */
103 static char *visible_bell;
105 /* The string to write to turn on the meta key, if this term has one. */
106 static char *term_mm;
108 /* The string to write to turn off the meta key, if this term has one. */
109 static char *term_mo;
111 /* The string to turn on inverse mode, if this term has one. */
112 static char *term_invbeg;
114 /* The string to turn off inverse mode, if this term has one. */
115 static char *term_invend;
117 /* Although I can't find any documentation that says this is supposed to
118 return its argument, all the code I've looked at (termutils, less)
121 output_character_function (c)
128 /* Macro to send STRING to the terminal. */
129 #define send_to_terminal(string) \
132 tputs (string, 1, output_character_function); \
135 /* Tell the terminal that we will be doing cursor addressable motion. */
137 terminal_begin_using_terminal ()
139 RETSIGTYPE (*sigsave) ();
142 send_to_terminal (term_keypad_on);
144 if (!term_begin_use || !*term_begin_use)
148 sigsave = signal (SIGWINCH, SIG_IGN);
151 send_to_terminal (term_begin_use);
153 if (STREQ (term_name, "sun-cmd"))
154 /* Without this fflush and sleep, running info in a shelltool or
155 cmdtool (TERM=sun-cmd) with scrollbars loses -- the scrollbars are
156 not restored properly.
157 From: strube@physik3.gwdg.de (Hans Werner Strube). */
161 signal (SIGWINCH, sigsave);
165 /* Tell the terminal that we will not be doing any more cursor
166 addressable motion. */
168 terminal_end_using_terminal ()
170 RETSIGTYPE (*sigsave) ();
173 send_to_terminal (term_keypad_off);
175 if (!term_end_use || !*term_end_use)
179 sigsave = signal (SIGWINCH, SIG_IGN);
182 send_to_terminal (term_end_use);
184 if (STREQ (term_name, "sun-cmd"))
185 /* See comments at other sleep. */
189 signal (SIGWINCH, sigsave);
193 /* **************************************************************** */
195 /* Necessary Terminal Functions */
197 /* **************************************************************** */
199 /* The functions and variables on this page implement the user visible
200 portion of the terminal interface. */
202 /* The width and height of the terminal. */
203 int screenwidth, screenheight;
205 /* Non-zero means this terminal can't really do anything. */
206 int terminal_is_dumb_p = 0;
208 /* Non-zero means that this terminal has a meta key. */
209 int terminal_has_meta_p = 0;
211 /* Non-zero means that this terminal can produce a visible bell. */
212 int terminal_has_visible_bell_p = 0;
214 /* Non-zero means to use that visible bell if at all possible. */
215 int terminal_use_visible_bell_p = 0;
217 /* Non-zero means that the terminal can do scrolling. */
218 int terminal_can_scroll = 0;
220 /* The key sequences output by the arrow keys, if this terminal has any. */
221 char *term_ku = NULL;
222 char *term_kd = NULL;
223 char *term_kr = NULL;
224 char *term_kl = NULL;
225 char *term_kP = NULL; /* page-up */
226 char *term_kN = NULL; /* page-down */
227 char *term_kh = NULL; /* home */
228 char *term_ke = NULL; /* end */
229 char *term_kD = NULL; /* delete */
230 char *term_ki = NULL; /* ins */
231 char *term_kx = NULL; /* del */
233 /* Move the cursor to the terminal location of X and Y. */
235 terminal_goto_xy (x, y)
238 if (terminal_goto_xy_hook)
239 (*terminal_goto_xy_hook) (x, y);
243 tputs (tgoto (term_goto, x, y), 1, output_character_function);
247 /* Print STRING to the terminal at the current position. */
249 terminal_put_text (string)
252 if (terminal_put_text_hook)
253 (*terminal_put_text_hook) (string);
256 printf ("%s", string);
260 /* Print NCHARS from STRING to the terminal at the current position. */
262 terminal_write_chars (string, nchars)
266 if (terminal_write_chars_hook)
267 (*terminal_write_chars_hook) (string, nchars);
271 fwrite (string, 1, nchars, stdout);
275 /* Clear from the current position of the cursor to the end of the line. */
277 terminal_clear_to_eol ()
279 if (terminal_clear_to_eol_hook)
280 (*terminal_clear_to_eol_hook) ();
283 send_to_terminal (term_clreol);
287 /* Clear the entire terminal screen. */
289 terminal_clear_screen ()
291 if (terminal_clear_screen_hook)
292 (*terminal_clear_screen_hook) ();
295 send_to_terminal (term_clrpag);
299 /* Move the cursor up one line. */
303 if (terminal_up_line_hook)
304 (*terminal_up_line_hook) ();
307 send_to_terminal (term_up);
311 /* Move the cursor down one line. */
313 terminal_down_line ()
315 if (terminal_down_line_hook)
316 (*terminal_down_line_hook) ();
319 send_to_terminal (term_dn);
323 /* Turn on reverse video if possible. */
325 terminal_begin_inverse ()
327 if (terminal_begin_inverse_hook)
328 (*terminal_begin_inverse_hook) ();
331 send_to_terminal (term_invbeg);
335 /* Turn off reverse video if possible. */
337 terminal_end_inverse ()
339 if (terminal_end_inverse_hook)
340 (*terminal_end_inverse_hook) ();
343 send_to_terminal (term_invend);
347 /* Ring the terminal bell. The bell is run visibly if it both has one and
348 terminal_use_visible_bell_p is non-zero. */
350 terminal_ring_bell ()
352 if (terminal_ring_bell_hook)
353 (*terminal_ring_bell_hook) ();
356 if (terminal_has_visible_bell_p && terminal_use_visible_bell_p)
357 send_to_terminal (visible_bell);
359 send_to_terminal (audible_bell);
363 /* At the line START, delete COUNT lines from the terminal display. */
365 terminal_delete_lines (start, count)
370 /* Normalize arguments. */
374 lines = screenheight - start;
375 terminal_goto_xy (0, start);
377 tputs (tgoto (term_DL, 0, count), lines, output_character_function);
381 tputs (term_dl, lines, output_character_function);
387 /* At the line START, insert COUNT lines in the terminal display. */
389 terminal_insert_lines (start, count)
394 /* Normalize arguments. */
398 lines = screenheight - start;
399 terminal_goto_xy (0, start);
402 tputs (tgoto (term_AL, 0, count), lines, output_character_function);
406 tputs (term_al, lines, output_character_function);
412 /* Scroll an area of the terminal, starting with the region from START
413 to END, AMOUNT lines. If AMOUNT is negative, the lines are scrolled
414 towards the top of the screen, else they are scrolled towards the
415 bottom of the screen. */
417 terminal_scroll_terminal (start, end, amount)
418 int start, end, amount;
420 if (!terminal_can_scroll)
423 /* Any scrolling at all? */
427 if (terminal_scroll_terminal_hook)
428 (*terminal_scroll_terminal_hook) (start, end, amount);
431 /* If we are scrolling down, delete AMOUNT lines at END. Then insert
432 AMOUNT lines at START. */
435 terminal_delete_lines (end, amount);
436 terminal_insert_lines (start, amount);
439 /* If we are scrolling up, delete AMOUNT lines before START. This
440 actually does the upwards scroll. Then, insert AMOUNT lines
441 after the already scrolled region (i.e., END - AMOUNT). */
444 int abs_amount = -amount;
445 terminal_delete_lines (start - abs_amount, abs_amount);
446 terminal_insert_lines (end - abs_amount, abs_amount);
451 /* Re-initialize the terminal considering that the TERM/TERMCAP variable
454 terminal_new_terminal (terminal_name)
457 if (terminal_new_terminal_hook)
458 (*terminal_new_terminal_hook) (terminal_name);
461 terminal_initialize_terminal (terminal_name);
465 /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */
467 terminal_get_screen_size ()
469 if (terminal_get_screen_size_hook)
470 (*terminal_get_screen_size_hook) ();
473 screenwidth = screenheight = 0;
475 #if defined (TIOCGWINSZ)
477 struct winsize window_size;
479 if (ioctl (fileno (stdout), TIOCGWINSZ, &window_size) == 0)
481 screenwidth = (int) window_size.ws_col;
482 screenheight = (int) window_size.ws_row;
485 #endif /* TIOCGWINSZ */
487 /* Environment variable COLUMNS overrides setting of "co". */
488 if (screenwidth <= 0)
490 char *sw = getenv ("COLUMNS");
493 screenwidth = atoi (sw);
495 if (screenwidth <= 0)
496 screenwidth = tgetnum ("co");
499 /* Environment variable LINES overrides setting of "li". */
500 if (screenheight <= 0)
502 char *sh = getenv ("LINES");
505 screenheight = atoi (sh);
507 if (screenheight <= 0)
508 screenheight = tgetnum ("li");
511 /* If all else fails, default to 80x24 terminal. */
512 if (screenwidth <= 0)
515 if (screenheight <= 0)
520 /* Initialize the terminal which is known as TERMINAL_NAME. If this
521 terminal doesn't have cursor addressability, `terminal_is_dumb_p'
522 becomes nonzero. The variables SCREENHEIGHT and SCREENWIDTH are set
523 to the dimensions that this terminal actually has. The variable
524 TERMINAL_HAS_META_P becomes nonzero if this terminal supports a Meta
525 key. Finally, the terminal screen is cleared. */
527 terminal_initialize_terminal (terminal_name)
532 terminal_is_dumb_p = 0;
534 if (terminal_initialize_terminal_hook)
536 (*terminal_initialize_terminal_hook) (terminal_name);
540 term_name = terminal_name ? terminal_name : getenv ("TERM");
544 if (!term_string_buffer)
545 term_string_buffer = xmalloc (2048);
548 term_buffer = xmalloc (2048);
550 buffer = term_string_buffer;
552 term_clrpag = term_cr = term_clreol = NULL;
554 /* HP-UX 11.x returns 0 for OK --jeff.hull@state.co.us. */
555 if (tgetent (term_buffer, term_name) < 0)
557 terminal_is_dumb_p = 1;
561 term_up = term_dn = audible_bell = visible_bell = NULL;
562 term_ku = term_kd = term_kl = term_kr = NULL;
563 term_kP = term_kN = NULL;
564 term_kh = term_ke = NULL;
569 BC = tgetstr ("pc", &buffer);
572 #if defined (HAVE_TERMIOS_H)
575 if (tcgetattr (fileno(stdout), &ti) != -1)
576 ospeed = cfgetospeed (&ti);
581 # if defined (TIOCGETP)
585 if (ioctl (fileno (stdout), TIOCGETP, &sg) != -1)
586 ospeed = sg.sg_ospeed;
592 # endif /* !TIOCGETP */
595 term_cr = tgetstr ("cr", &buffer);
596 term_clreol = tgetstr ("ce", &buffer);
597 term_clrpag = tgetstr ("cl", &buffer);
598 term_goto = tgetstr ("cm", &buffer);
600 /* Find out about this terminal's scrolling capability. */
601 term_AL = tgetstr ("AL", &buffer);
602 term_DL = tgetstr ("DL", &buffer);
603 term_al = tgetstr ("al", &buffer);
604 term_dl = tgetstr ("dl", &buffer);
606 terminal_can_scroll = ((term_AL || term_al) && (term_DL || term_dl));
608 term_invbeg = tgetstr ("mr", &buffer);
610 term_invend = tgetstr ("me", &buffer);
617 terminal_get_screen_size ();
619 term_up = tgetstr ("up", &buffer);
620 term_dn = tgetstr ("dn", &buffer);
621 visible_bell = tgetstr ("vb", &buffer);
622 terminal_has_visible_bell_p = (visible_bell != NULL);
623 audible_bell = tgetstr ("bl", &buffer);
625 audible_bell = "\007";
626 term_begin_use = tgetstr ("ti", &buffer);
627 term_end_use = tgetstr ("te", &buffer);
629 term_keypad_on = tgetstr ("ks", &buffer);
630 term_keypad_off = tgetstr ("ke", &buffer);
632 /* Check to see if this terminal has a meta key. */
633 terminal_has_meta_p = (tgetflag ("km") || tgetflag ("MT"));
634 if (terminal_has_meta_p)
636 term_mm = tgetstr ("mm", &buffer);
637 term_mo = tgetstr ("mo", &buffer);
645 /* Attempt to find the arrow keys. */
646 term_ku = tgetstr ("ku", &buffer);
647 term_kd = tgetstr ("kd", &buffer);
648 term_kr = tgetstr ("kr", &buffer);
649 term_kl = tgetstr ("kl", &buffer);
651 term_kP = tgetstr ("kP", &buffer);
652 term_kN = tgetstr ("kN", &buffer);
655 term_kh = tgetstr ("kh", &buffer);
656 term_ke = tgetstr ("@7", &buffer);
657 term_ki = tgetstr ("kI", &buffer);
658 term_kx = tgetstr ("kD", &buffer);
659 #endif /* defined(INFOKEY) */
661 /* Home and end keys. */
662 term_kh = tgetstr ("kh", &buffer);
663 term_ke = tgetstr ("@7", &buffer);
665 term_kD = tgetstr ("kD", &buffer);
667 /* If this terminal is not cursor addressable, then it is really dumb. */
669 terminal_is_dumb_p = 1;
672 /* How to read characters from the terminal. */
674 #if defined (HAVE_TERMIOS_H)
675 struct termios original_termios, ttybuff;
677 # if defined (HAVE_TERMIO_H)
678 /* A buffer containing the terminal mode flags upon entry to info. */
679 struct termio original_termio, ttybuff;
680 # else /* !HAVE_TERMIO_H */
681 /* Buffers containing the terminal mode flags upon entry to info. */
682 int original_tty_flags = 0;
684 struct sgttyb ttybuff;
686 # if defined(TIOCGETC) && defined(M_XENIX)
687 /* SCO 3.2v5.0.2 defines but does not support TIOCGETC. Gak. Maybe
688 better fix would be to use Posix termios in preference. --gildea,
693 # if defined (TIOCGETC)
694 /* A buffer containing the terminal interrupt characters upon entry
696 struct tchars original_tchars;
699 # if defined (TIOCGLTC)
700 /* A buffer containing the local terminal mode characters upon entry
702 struct ltchars original_ltchars;
704 # endif /* !HAVE_TERMIO_H */
705 #endif /* !HAVE_TERMIOS_H */
707 /* Prepare to start using the terminal to read characters singly. */
709 terminal_prep_terminal ()
713 if (terminal_prep_terminal_hook)
715 (*terminal_prep_terminal_hook) ();
719 terminal_begin_using_terminal ();
721 tty = fileno (stdin);
723 #if defined (HAVE_TERMIOS_H)
724 tcgetattr (tty, &original_termios);
725 tcgetattr (tty, &ttybuff);
727 # if defined (HAVE_TERMIO_H)
728 ioctl (tty, TCGETA, &original_termio);
729 ioctl (tty, TCGETA, &ttybuff);
733 #if defined (HAVE_TERMIOS_H) || defined (HAVE_TERMIO_H)
734 ttybuff.c_iflag &= (~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL & ~IXON);
735 /* These output flags are not part of POSIX, so only use them if they
738 ttybuff.c_oflag &= ~ONLCR ;
741 ttybuff.c_oflag &= ~OCRNL;
743 ttybuff.c_lflag &= (~ICANON & ~ECHO);
745 ttybuff.c_cc[VMIN] = 1;
746 ttybuff.c_cc[VTIME] = 0;
748 if (ttybuff.c_cc[VINTR] == '\177')
749 ttybuff.c_cc[VINTR] = -1;
751 if (ttybuff.c_cc[VQUIT] == '\177')
752 ttybuff.c_cc[VQUIT] = -1;
755 if (ttybuff.c_cc[VLNEXT] == '\026')
756 ttybuff.c_cc[VLNEXT] = -1;
758 #endif /* TERMIOS or TERMIO */
760 /* cf. emacs/src/sysdep.c for being sure output is on. */
761 #if defined (HAVE_TERMIOS_H)
762 /* linux kernel 2.2.x needs a TCOFF followed by a TCOON to turn output
763 back on if the user presses ^S at the very beginning; just a TCOON
764 doesn't work. --Kevin Ryde <user42@zip.com.au>, 16jun2000. */
765 tcsetattr (tty, TCSANOW, &ttybuff);
767 tcflow (tty, TCOOFF);
771 # if defined (HAVE_TERMIO_H)
772 ioctl (tty, TCSETA, &ttybuff);
774 ioctl (tty, TCXONC, 1);
779 #if !defined (HAVE_TERMIOS_H) && !defined (HAVE_TERMIO_H)
780 ioctl (tty, TIOCGETP, &ttybuff);
782 if (!original_tty_flags)
783 original_tty_flags = ttybuff.sg_flags;
785 /* Make this terminal pass 8 bits around while we are using it. */
787 ttybuff.sg_flags |= PASS8;
790 # if defined (TIOCLGET) && defined (LPASS8)
793 ioctl (tty, TIOCLGET, &flags);
794 original_lmode = flags;
796 ioctl (tty, TIOCLSET, &flags);
798 # endif /* TIOCLGET && LPASS8 */
800 # if defined (TIOCGETC)
804 ioctl (tty, TIOCGETC, &original_tchars);
805 temp = original_tchars;
808 temp.t_startc = temp.t_stopc = -1;
810 /* Often set to C-d. */
813 /* If the a quit or interrupt character conflicts with one of our
814 commands, then make it go away. */
815 if (temp.t_intrc == '\177')
818 if (temp.t_quitc == '\177')
821 ioctl (tty, TIOCSETC, &temp);
823 # endif /* TIOCGETC */
825 # if defined (TIOCGLTC)
829 ioctl (tty, TIOCGLTC, &original_ltchars);
830 temp = original_ltchars;
832 /* Make the interrupt keys go away. Just enough to make people happy. */
833 temp.t_lnextc = -1; /* C-v. */
834 temp.t_dsuspc = -1; /* C-y. */
835 temp.t_flushc = -1; /* C-o. */
836 ioctl (tty, TIOCSLTC, &temp);
838 # endif /* TIOCGLTC */
840 ttybuff.sg_flags &= ~ECHO;
841 ttybuff.sg_flags |= CBREAK;
842 ioctl (tty, TIOCSETN, &ttybuff);
843 #endif /* !HAVE_TERMIOS_H && !HAVE_TERMIO_H */
846 /* Restore the tty settings back to what they were before we started using
849 terminal_unprep_terminal ()
853 if (terminal_unprep_terminal_hook)
855 (*terminal_unprep_terminal_hook) ();
859 tty = fileno (stdin);
861 #if defined (HAVE_TERMIOS_H)
862 tcsetattr (tty, TCSANOW, &original_termios);
864 # if defined (HAVE_TERMIO_H)
865 ioctl (tty, TCSETA, &original_termio);
866 # else /* !HAVE_TERMIO_H */
867 ioctl (tty, TIOCGETP, &ttybuff);
868 ttybuff.sg_flags = original_tty_flags;
869 ioctl (tty, TIOCSETN, &ttybuff);
871 # if defined (TIOCGETC)
872 ioctl (tty, TIOCSETC, &original_tchars);
873 # endif /* TIOCGETC */
875 # if defined (TIOCGLTC)
876 ioctl (tty, TIOCSLTC, &original_ltchars);
877 # endif /* TIOCGLTC */
879 # if defined (TIOCLGET) && defined (LPASS8)
880 ioctl (tty, TIOCLSET, &original_lmode);
881 # endif /* TIOCLGET && LPASS8 */
883 # endif /* !HAVE_TERMIO_H */
884 #endif /* !HAVE_TERMIOS_H */
885 terminal_end_using_terminal ();