1 /* $FreeBSD: src/contrib/texinfo/info/terminal.c,v 1.6.2.1 2002/03/30 17:09:19 ru Exp $ */
2 /* terminal.c -- How to handle the physical terminal for Info.
3 $Id: terminal.c,v 1.23 2001/11/16 23:16:04 karl Exp $
5 Copyright (C) 1988, 89, 90, 91, 92, 93, 96, 97, 98, 99, 2001
6 Free Software Foundation, Inc.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 Written by Brian Fox (bfox@ai.mit.edu). */
28 #include <sys/types.h>
31 /* The Unix termcap interface code. */
32 #ifdef HAVE_NCURSES_TERMCAP_H
33 #include <ncurses/termcap.h>
38 /* On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC.
39 Unfortunately, PC is a global variable used by the termcap library. */
42 /* Termcap requires these variables, whether we access them or not. */
44 char PC; /* Pad character */
45 short ospeed; /* Terminal output baud rate */
46 extern int tgetnum (), tgetflag (), tgetent ();
47 extern char *tgetstr (), *tgoto ();
49 #endif /* not HAVE_TERMCAP_H */
50 #endif /* not HAVE_NCURSES_TERMCAP_H */
52 /* Function "hooks". If you make one of these point to a function, that
53 function is called when appropriate instead of its namesake. Your
54 function is called with exactly the same arguments that were passed
55 to the namesake function. */
56 VFunction *terminal_begin_inverse_hook = (VFunction *)NULL;
57 VFunction *terminal_end_inverse_hook = (VFunction *)NULL;
58 VFunction *terminal_prep_terminal_hook = (VFunction *)NULL;
59 VFunction *terminal_unprep_terminal_hook = (VFunction *)NULL;
60 VFunction *terminal_up_line_hook = (VFunction *)NULL;
61 VFunction *terminal_down_line_hook = (VFunction *)NULL;
62 VFunction *terminal_clear_screen_hook = (VFunction *)NULL;
63 VFunction *terminal_clear_to_eol_hook = (VFunction *)NULL;
64 VFunction *terminal_get_screen_size_hook = (VFunction *)NULL;
65 VFunction *terminal_goto_xy_hook = (VFunction *)NULL;
66 VFunction *terminal_initialize_terminal_hook = (VFunction *)NULL;
67 VFunction *terminal_new_terminal_hook = (VFunction *)NULL;
68 VFunction *terminal_put_text_hook = (VFunction *)NULL;
69 VFunction *terminal_ring_bell_hook = (VFunction *)NULL;
70 VFunction *terminal_write_chars_hook = (VFunction *)NULL;
71 VFunction *terminal_scroll_terminal_hook = (VFunction *)NULL;
73 /* **************************************************************** */
75 /* Terminal and Termcap */
77 /* **************************************************************** */
79 /* A buffer which holds onto the current terminal description, and a pointer
80 used to float within it. And the name of the terminal. */
81 static char *term_buffer = NULL;
82 static char *term_string_buffer = NULL;
83 static char *term_name;
85 /* Some strings to control terminal actions. These are output by tputs (). */
86 static char *term_goto, *term_clreol, *term_cr, *term_clrpag;
87 static char *term_begin_use, *term_end_use;
88 static char *term_AL, *term_DL, *term_al, *term_dl;
90 static char *term_keypad_on, *term_keypad_off;
92 /* How to go up a line. */
95 /* How to go down a line. */
98 /* An audible bell, if the terminal can be made to make noise. */
99 static char *audible_bell;
101 /* A visible bell, if the terminal can be made to flash the screen. */
102 static char *visible_bell;
104 /* The string to write to turn on the meta key, if this term has one. */
105 static char *term_mm;
107 /* The string to write to turn off the meta key, if this term has one. */
108 static char *term_mo;
110 /* The string to turn on inverse mode, if this term has one. */
111 static char *term_invbeg;
113 /* The string to turn off inverse mode, if this term has one. */
114 static char *term_invend;
116 /* Although I can't find any documentation that says this is supposed to
117 return its argument, all the code I've looked at (termutils, less)
120 output_character_function (c)
127 /* Macro to send STRING to the terminal. */
128 #define send_to_terminal(string) \
131 tputs (string, 1, output_character_function); \
134 /* Tell the terminal that we will be doing cursor addressable motion. */
136 terminal_begin_using_terminal ()
138 RETSIGTYPE (*sigsave) ();
141 send_to_terminal (term_keypad_on);
143 if (!term_begin_use || !*term_begin_use)
147 sigsave = signal (SIGWINCH, SIG_IGN);
150 send_to_terminal (term_begin_use);
152 if (STREQ (term_name, "sun-cmd"))
153 /* Without this fflush and sleep, running info in a shelltool or
154 cmdtool (TERM=sun-cmd) with scrollbars loses -- the scrollbars are
155 not restored properly.
156 From: strube@physik3.gwdg.de (Hans Werner Strube). */
160 signal (SIGWINCH, sigsave);
164 /* Tell the terminal that we will not be doing any more cursor
165 addressable motion. */
167 terminal_end_using_terminal ()
169 RETSIGTYPE (*sigsave) ();
172 send_to_terminal (term_keypad_off);
174 if (!term_end_use || !*term_end_use)
178 sigsave = signal (SIGWINCH, SIG_IGN);
181 send_to_terminal (term_end_use);
183 if (STREQ (term_name, "sun-cmd"))
184 /* See comments at other sleep. */
188 signal (SIGWINCH, sigsave);
192 /* **************************************************************** */
194 /* Necessary Terminal Functions */
196 /* **************************************************************** */
198 /* The functions and variables on this page implement the user visible
199 portion of the terminal interface. */
201 /* The width and height of the terminal. */
202 int screenwidth, screenheight;
204 /* Non-zero means this terminal can't really do anything. */
205 int terminal_is_dumb_p = 0;
207 /* Non-zero means that this terminal has a meta key. */
208 int terminal_has_meta_p = 0;
210 /* Non-zero means that this terminal can produce a visible bell. */
211 int terminal_has_visible_bell_p = 0;
213 /* Non-zero means to use that visible bell if at all possible. */
214 int terminal_use_visible_bell_p = 0;
216 /* Non-zero means that the terminal can do scrolling. */
217 int terminal_can_scroll = 0;
219 /* The key sequences output by the arrow keys, if this terminal has any. */
220 char *term_ku = NULL;
221 char *term_kd = NULL;
222 char *term_kr = NULL;
223 char *term_kl = NULL;
224 char *term_kP = NULL; /* page-up */
225 char *term_kN = NULL; /* page-down */
226 char *term_kh = NULL; /* home */
227 char *term_ke = NULL; /* end */
228 char *term_kD = NULL; /* delete */
229 char *term_ki = NULL; /* ins */
230 char *term_kx = NULL; /* del */
232 /* Move the cursor to the terminal location of X and Y. */
234 terminal_goto_xy (x, y)
237 if (terminal_goto_xy_hook)
238 (*terminal_goto_xy_hook) (x, y);
242 tputs (tgoto (term_goto, x, y), 1, output_character_function);
246 /* Print STRING to the terminal at the current position. */
248 terminal_put_text (string)
251 if (terminal_put_text_hook)
252 (*terminal_put_text_hook) (string);
255 printf ("%s", string);
259 /* Print NCHARS from STRING to the terminal at the current position. */
261 terminal_write_chars (string, nchars)
265 if (terminal_write_chars_hook)
266 (*terminal_write_chars_hook) (string, nchars);
270 fwrite (string, 1, nchars, stdout);
274 /* Clear from the current position of the cursor to the end of the line. */
276 terminal_clear_to_eol ()
278 if (terminal_clear_to_eol_hook)
279 (*terminal_clear_to_eol_hook) ();
282 send_to_terminal (term_clreol);
286 /* Clear the entire terminal screen. */
288 terminal_clear_screen ()
290 if (terminal_clear_screen_hook)
291 (*terminal_clear_screen_hook) ();
294 send_to_terminal (term_clrpag);
298 /* Move the cursor up one line. */
302 if (terminal_up_line_hook)
303 (*terminal_up_line_hook) ();
306 send_to_terminal (term_up);
310 /* Move the cursor down one line. */
312 terminal_down_line ()
314 if (terminal_down_line_hook)
315 (*terminal_down_line_hook) ();
318 send_to_terminal (term_dn);
322 /* Turn on reverse video if possible. */
324 terminal_begin_inverse ()
326 if (terminal_begin_inverse_hook)
327 (*terminal_begin_inverse_hook) ();
330 send_to_terminal (term_invbeg);
334 /* Turn off reverse video if possible. */
336 terminal_end_inverse ()
338 if (terminal_end_inverse_hook)
339 (*terminal_end_inverse_hook) ();
342 send_to_terminal (term_invend);
346 /* Ring the terminal bell. The bell is run visibly if it both has one and
347 terminal_use_visible_bell_p is non-zero. */
349 terminal_ring_bell ()
351 if (terminal_ring_bell_hook)
352 (*terminal_ring_bell_hook) ();
355 if (terminal_has_visible_bell_p && terminal_use_visible_bell_p)
356 send_to_terminal (visible_bell);
358 send_to_terminal (audible_bell);
362 /* At the line START, delete COUNT lines from the terminal display. */
364 terminal_delete_lines (start, count)
369 /* Normalize arguments. */
373 lines = screenheight - start;
374 terminal_goto_xy (0, start);
376 tputs (tgoto (term_DL, 0, count), lines, output_character_function);
380 tputs (term_dl, lines, output_character_function);
386 /* At the line START, insert COUNT lines in the terminal display. */
388 terminal_insert_lines (start, count)
393 /* Normalize arguments. */
397 lines = screenheight - start;
398 terminal_goto_xy (0, start);
401 tputs (tgoto (term_AL, 0, count), lines, output_character_function);
405 tputs (term_al, lines, output_character_function);
411 /* Scroll an area of the terminal, starting with the region from START
412 to END, AMOUNT lines. If AMOUNT is negative, the lines are scrolled
413 towards the top of the screen, else they are scrolled towards the
414 bottom of the screen. */
416 terminal_scroll_terminal (start, end, amount)
417 int start, end, amount;
419 if (!terminal_can_scroll)
422 /* Any scrolling at all? */
426 if (terminal_scroll_terminal_hook)
427 (*terminal_scroll_terminal_hook) (start, end, amount);
430 /* If we are scrolling down, delete AMOUNT lines at END. Then insert
431 AMOUNT lines at START. */
434 terminal_delete_lines (end, amount);
435 terminal_insert_lines (start, amount);
438 /* If we are scrolling up, delete AMOUNT lines before START. This
439 actually does the upwards scroll. Then, insert AMOUNT lines
440 after the already scrolled region (i.e., END - AMOUNT). */
443 int abs_amount = -amount;
444 terminal_delete_lines (start - abs_amount, abs_amount);
445 terminal_insert_lines (end - abs_amount, abs_amount);
450 /* Re-initialize the terminal considering that the TERM/TERMCAP variable
453 terminal_new_terminal (terminal_name)
456 if (terminal_new_terminal_hook)
457 (*terminal_new_terminal_hook) (terminal_name);
460 terminal_initialize_terminal (terminal_name);
464 /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */
466 terminal_get_screen_size ()
468 if (terminal_get_screen_size_hook)
469 (*terminal_get_screen_size_hook) ();
472 screenwidth = screenheight = 0;
474 #if defined (TIOCGWINSZ)
476 struct winsize window_size;
478 if (ioctl (fileno (stdout), TIOCGWINSZ, &window_size) == 0)
480 screenwidth = (int) window_size.ws_col;
481 screenheight = (int) window_size.ws_row;
484 #endif /* TIOCGWINSZ */
486 /* Environment variable COLUMNS overrides setting of "co". */
487 if (screenwidth <= 0)
489 char *sw = getenv ("COLUMNS");
492 screenwidth = atoi (sw);
494 if (screenwidth <= 0)
495 screenwidth = tgetnum ("co");
498 /* Environment variable LINES overrides setting of "li". */
499 if (screenheight <= 0)
501 char *sh = getenv ("LINES");
504 screenheight = atoi (sh);
506 if (screenheight <= 0)
507 screenheight = tgetnum ("li");
510 /* If all else fails, default to 80x24 terminal. */
511 if (screenwidth <= 0)
514 if (screenheight <= 0)
519 /* Initialize the terminal which is known as TERMINAL_NAME. If this
520 terminal doesn't have cursor addressability, `terminal_is_dumb_p'
521 becomes nonzero. The variables SCREENHEIGHT and SCREENWIDTH are set
522 to the dimensions that this terminal actually has. The variable
523 TERMINAL_HAS_META_P becomes nonzero if this terminal supports a Meta
524 key. Finally, the terminal screen is cleared. */
526 terminal_initialize_terminal (terminal_name)
531 terminal_is_dumb_p = 0;
533 if (terminal_initialize_terminal_hook)
535 (*terminal_initialize_terminal_hook) (terminal_name);
539 term_name = terminal_name ? terminal_name : getenv ("TERM");
543 if (!term_string_buffer)
544 term_string_buffer = xmalloc (2048);
547 term_buffer = xmalloc (2048);
549 buffer = term_string_buffer;
551 term_clrpag = term_cr = term_clreol = NULL;
553 /* HP-UX 11.x returns 0 for OK --jeff.hull@state.co.us. */
554 if (tgetent (term_buffer, term_name) < 0)
556 terminal_is_dumb_p = 1;
560 term_up = term_dn = audible_bell = visible_bell = NULL;
561 term_ku = term_kd = term_kl = term_kr = NULL;
562 term_kP = term_kN = NULL;
563 term_kh = term_ke = NULL;
568 BC = tgetstr ("pc", &buffer);
571 #if defined (HAVE_TERMIOS_H)
574 if (tcgetattr (fileno(stdout), &ti) != -1)
575 ospeed = cfgetospeed (&ti);
580 # if defined (TIOCGETP)
584 if (ioctl (fileno (stdout), TIOCGETP, &sg) != -1)
585 ospeed = sg.sg_ospeed;
591 # endif /* !TIOCGETP */
594 term_cr = tgetstr ("cr", &buffer);
595 term_clreol = tgetstr ("ce", &buffer);
596 term_clrpag = tgetstr ("cl", &buffer);
597 term_goto = tgetstr ("cm", &buffer);
599 /* Find out about this terminal's scrolling capability. */
600 term_AL = tgetstr ("AL", &buffer);
601 term_DL = tgetstr ("DL", &buffer);
602 term_al = tgetstr ("al", &buffer);
603 term_dl = tgetstr ("dl", &buffer);
605 terminal_can_scroll = ((term_AL || term_al) && (term_DL || term_dl));
607 term_invbeg = tgetstr ("mr", &buffer);
609 term_invend = tgetstr ("me", &buffer);
616 terminal_get_screen_size ();
618 term_up = tgetstr ("up", &buffer);
619 term_dn = tgetstr ("dn", &buffer);
620 visible_bell = tgetstr ("vb", &buffer);
621 terminal_has_visible_bell_p = (visible_bell != NULL);
622 audible_bell = tgetstr ("bl", &buffer);
624 audible_bell = "\007";
625 term_begin_use = tgetstr ("ti", &buffer);
626 term_end_use = tgetstr ("te", &buffer);
628 term_keypad_on = tgetstr ("ks", &buffer);
629 term_keypad_off = tgetstr ("ke", &buffer);
631 /* Check to see if this terminal has a meta key. */
632 terminal_has_meta_p = (tgetflag ("km") || tgetflag ("MT"));
633 if (terminal_has_meta_p)
635 term_mm = tgetstr ("mm", &buffer);
636 term_mo = tgetstr ("mo", &buffer);
644 /* Attempt to find the arrow keys. */
645 term_ku = tgetstr ("ku", &buffer);
646 term_kd = tgetstr ("kd", &buffer);
647 term_kr = tgetstr ("kr", &buffer);
648 term_kl = tgetstr ("kl", &buffer);
650 term_kP = tgetstr ("kP", &buffer);
651 term_kN = tgetstr ("kN", &buffer);
654 term_kh = tgetstr ("kh", &buffer);
655 term_ke = tgetstr ("@7", &buffer);
656 term_ki = tgetstr ("kI", &buffer);
657 term_kx = tgetstr ("kD", &buffer);
658 #endif /* defined(INFOKEY) */
660 /* Home and end keys. */
661 term_kh = tgetstr ("kh", &buffer);
662 term_ke = tgetstr ("@7", &buffer);
664 term_kD = tgetstr ("kD", &buffer);
666 /* If this terminal is not cursor addressable, then it is really dumb. */
668 terminal_is_dumb_p = 1;
671 /* How to read characters from the terminal. */
673 #if defined (HAVE_TERMIOS_H)
674 struct termios original_termios, ttybuff;
676 # if defined (HAVE_TERMIO_H)
677 /* A buffer containing the terminal mode flags upon entry to info. */
678 struct termio original_termio, ttybuff;
679 # else /* !HAVE_TERMIO_H */
680 /* Buffers containing the terminal mode flags upon entry to info. */
681 int original_tty_flags = 0;
683 struct sgttyb ttybuff;
685 # if defined(TIOCGETC) && defined(M_XENIX)
686 /* SCO 3.2v5.0.2 defines but does not support TIOCGETC. Gak. Maybe
687 better fix would be to use Posix termios in preference. --gildea,
692 # if defined (TIOCGETC)
693 /* A buffer containing the terminal interrupt characters upon entry
695 struct tchars original_tchars;
698 # if defined (TIOCGLTC)
699 /* A buffer containing the local terminal mode characters upon entry
701 struct ltchars original_ltchars;
703 # endif /* !HAVE_TERMIO_H */
704 #endif /* !HAVE_TERMIOS_H */
706 /* Prepare to start using the terminal to read characters singly. */
708 terminal_prep_terminal ()
712 if (terminal_prep_terminal_hook)
714 (*terminal_prep_terminal_hook) ();
718 terminal_begin_using_terminal ();
720 tty = fileno (stdin);
722 #if defined (HAVE_TERMIOS_H)
723 tcgetattr (tty, &original_termios);
724 tcgetattr (tty, &ttybuff);
726 # if defined (HAVE_TERMIO_H)
727 ioctl (tty, TCGETA, &original_termio);
728 ioctl (tty, TCGETA, &ttybuff);
732 #if defined (HAVE_TERMIOS_H) || defined (HAVE_TERMIO_H)
733 ttybuff.c_iflag &= (~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL & ~IXON);
734 /* These output flags are not part of POSIX, so only use them if they
737 ttybuff.c_oflag &= ~ONLCR ;
740 ttybuff.c_oflag &= ~OCRNL;
742 ttybuff.c_lflag &= (~ICANON & ~ECHO);
744 ttybuff.c_cc[VMIN] = 1;
745 ttybuff.c_cc[VTIME] = 0;
747 if (ttybuff.c_cc[VINTR] == '\177')
748 ttybuff.c_cc[VINTR] = -1;
750 if (ttybuff.c_cc[VQUIT] == '\177')
751 ttybuff.c_cc[VQUIT] = -1;
754 if (ttybuff.c_cc[VLNEXT] == '\026')
755 ttybuff.c_cc[VLNEXT] = -1;
757 #endif /* TERMIOS or TERMIO */
759 /* cf. emacs/src/sysdep.c for being sure output is on. */
760 #if defined (HAVE_TERMIOS_H)
761 /* linux kernel 2.2.x needs a TCOFF followed by a TCOON to turn output
762 back on if the user presses ^S at the very beginning; just a TCOON
763 doesn't work. --Kevin Ryde <user42@zip.com.au>, 16jun2000. */
764 tcsetattr (tty, TCSANOW, &ttybuff);
766 tcflow (tty, TCOOFF);
770 # if defined (HAVE_TERMIO_H)
771 ioctl (tty, TCSETA, &ttybuff);
773 ioctl (tty, TCXONC, 1);
778 #if !defined (HAVE_TERMIOS_H) && !defined (HAVE_TERMIO_H)
779 ioctl (tty, TIOCGETP, &ttybuff);
781 if (!original_tty_flags)
782 original_tty_flags = ttybuff.sg_flags;
784 /* Make this terminal pass 8 bits around while we are using it. */
786 ttybuff.sg_flags |= PASS8;
789 # if defined (TIOCLGET) && defined (LPASS8)
792 ioctl (tty, TIOCLGET, &flags);
793 original_lmode = flags;
795 ioctl (tty, TIOCLSET, &flags);
797 # endif /* TIOCLGET && LPASS8 */
799 # if defined (TIOCGETC)
803 ioctl (tty, TIOCGETC, &original_tchars);
804 temp = original_tchars;
807 temp.t_startc = temp.t_stopc = -1;
809 /* Often set to C-d. */
812 /* If the a quit or interrupt character conflicts with one of our
813 commands, then make it go away. */
814 if (temp.t_intrc == '\177')
817 if (temp.t_quitc == '\177')
820 ioctl (tty, TIOCSETC, &temp);
822 # endif /* TIOCGETC */
824 # if defined (TIOCGLTC)
828 ioctl (tty, TIOCGLTC, &original_ltchars);
829 temp = original_ltchars;
831 /* Make the interrupt keys go away. Just enough to make people happy. */
832 temp.t_lnextc = -1; /* C-v. */
833 temp.t_dsuspc = -1; /* C-y. */
834 temp.t_flushc = -1; /* C-o. */
835 ioctl (tty, TIOCSLTC, &temp);
837 # endif /* TIOCGLTC */
839 ttybuff.sg_flags &= ~ECHO;
840 ttybuff.sg_flags |= CBREAK;
841 ioctl (tty, TIOCSETN, &ttybuff);
842 #endif /* !HAVE_TERMIOS_H && !HAVE_TERMIO_H */
845 /* Restore the tty settings back to what they were before we started using
848 terminal_unprep_terminal ()
852 if (terminal_unprep_terminal_hook)
854 (*terminal_unprep_terminal_hook) ();
858 tty = fileno (stdin);
860 #if defined (HAVE_TERMIOS_H)
861 tcsetattr (tty, TCSANOW, &original_termios);
863 # if defined (HAVE_TERMIO_H)
864 ioctl (tty, TCSETA, &original_termio);
865 # else /* !HAVE_TERMIO_H */
866 ioctl (tty, TIOCGETP, &ttybuff);
867 ttybuff.sg_flags = original_tty_flags;
868 ioctl (tty, TIOCSETN, &ttybuff);
870 # if defined (TIOCGETC)
871 ioctl (tty, TIOCSETC, &original_tchars);
872 # endif /* TIOCGETC */
874 # if defined (TIOCGLTC)
875 ioctl (tty, TIOCSLTC, &original_ltchars);
876 # endif /* TIOCGLTC */
878 # if defined (TIOCLGET) && defined (LPASS8)
879 ioctl (tty, TIOCLSET, &original_lmode);
880 # endif /* TIOCLGET && LPASS8 */
882 # endif /* !HAVE_TERMIO_H */
883 #endif /* !HAVE_TERMIOS_H */
884 terminal_end_using_terminal ();