1 /* terminal.c -- How to handle the physical terminal for Info. */
3 /* This file is part of GNU Info, a program for reading online documentation
6 This file has appeared in prior works by the Free Software Foundation;
7 thus it carries copyright dates from 1988 through 1993.
9 Copyright (C) 1988, 89, 90, 91, 92, 93, 96 Free Software Foundation, Inc.
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2, or (at your option)
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 Written by Brian Fox (bfox@ai.mit.edu). */
28 #include <sys/types.h>
32 extern void *xmalloc (), *xrealloc ();
34 /* The Unix termcap interface code. */
36 extern int tgetnum (), tgetflag (), tgetent ();
37 extern char *tgetstr (), *tgoto ();
38 extern char *getenv ();
41 /* Function "hooks". If you make one of these point to a function, that
42 function is called when appropriate instead of its namesake. Your
43 function is called with exactly the same arguments that were passed
44 to the namesake function. */
45 VFunction *terminal_begin_inverse_hook = (VFunction *)NULL;
46 VFunction *terminal_end_inverse_hook = (VFunction *)NULL;
47 VFunction *terminal_prep_terminal_hook = (VFunction *)NULL;
48 VFunction *terminal_unprep_terminal_hook = (VFunction *)NULL;
49 VFunction *terminal_up_line_hook = (VFunction *)NULL;
50 VFunction *terminal_down_line_hook = (VFunction *)NULL;
51 VFunction *terminal_clear_screen_hook = (VFunction *)NULL;
52 VFunction *terminal_clear_to_eol_hook = (VFunction *)NULL;
53 VFunction *terminal_get_screen_size_hook = (VFunction *)NULL;
54 VFunction *terminal_goto_xy_hook = (VFunction *)NULL;
55 VFunction *terminal_initialize_terminal_hook = (VFunction *)NULL;
56 VFunction *terminal_new_terminal_hook = (VFunction *)NULL;
57 VFunction *terminal_put_text_hook = (VFunction *)NULL;
58 VFunction *terminal_ring_bell_hook = (VFunction *)NULL;
59 VFunction *terminal_write_chars_hook = (VFunction *)NULL;
60 VFunction *terminal_scroll_terminal_hook = (VFunction *)NULL;
62 /* **************************************************************** */
64 /* Terminal and Termcap */
66 /* **************************************************************** */
68 /* On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC.
69 Unfortunately, PC is a global variable used by the termcap library. */
72 /* TERMCAP requires these variables, whether we access them or not. */
77 /* A buffer which holds onto the current terminal description, and a pointer
78 used to float within it. */
79 static char *term_buffer = (char *)NULL;
80 static char *term_string_buffer = (char *)NULL;
82 /* Some strings to control terminal actions. These are output by tputs (). */
83 static char *term_goto, *term_clreol, *term_cr, *term_clrpag;
84 static char *term_begin_use, *term_end_use;
85 static char *term_AL, *term_DL, *term_al, *term_dl;
87 /* How to go up a line. */
90 /* How to go down a line. */
93 /* An audible bell, if the terminal can be made to make noise. */
94 static char *audible_bell;
96 /* A visible bell, if the terminal can be made to flash the screen. */
97 static char *visible_bell;
99 /* The string to write to turn on the meta key, if this term has one. */
100 static char *term_mm;
102 /* The string to write to turn off the meta key, if this term has one. */
103 static char *term_mo;
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;
112 output_character_function (c)
118 /* Macro to send STRING to the terminal. */
119 #define send_to_terminal(string) \
122 tputs (string, 1, output_character_function); \
125 /* Tell the terminal that we will be doing cursor addressable motion. */
127 terminal_begin_using_terminal ()
129 send_to_terminal (term_begin_use);
132 /* Tell the terminal that we will not be doing any more cursor addressable
135 terminal_end_using_terminal ()
137 send_to_terminal (term_end_use);
140 /* **************************************************************** */
142 /* Necessary Terminal Functions */
144 /* **************************************************************** */
146 /* The functions and variables on this page implement the user visible
147 portion of the terminal interface. */
149 /* The width and height of the terminal. */
150 int screenwidth, screenheight;
152 /* Non-zero means this terminal can't really do anything. */
153 int terminal_is_dumb_p = 0;
155 /* Non-zero means that this terminal has a meta key. */
156 int terminal_has_meta_p = 0;
158 /* Non-zero means that this terminal can produce a visible bell. */
159 int terminal_has_visible_bell_p = 0;
161 /* Non-zero means to use that visible bell if at all possible. */
162 int terminal_use_visible_bell_p = 0;
164 /* Non-zero means that the terminal can do scrolling. */
165 int terminal_can_scroll = 0;
167 /* The key sequences output by the arrow keys, if this terminal has any. */
168 char *term_ku = (char *)NULL;
169 char *term_kd = (char *)NULL;
170 char *term_kr = (char *)NULL;
171 char *term_kl = (char *)NULL;
173 /* Move the cursor to the terminal location of X and Y. */
175 terminal_goto_xy (x, y)
178 if (terminal_goto_xy_hook)
179 (*terminal_goto_xy_hook) (x, y);
183 tputs (tgoto (term_goto, x, y), 1, output_character_function);
187 /* Print STRING to the terminal at the current position. */
189 terminal_put_text (string)
192 if (terminal_put_text_hook)
193 (*terminal_put_text_hook) (string);
196 printf ("%s", string);
200 /* Print NCHARS from STRING to the terminal at the current position. */
202 terminal_write_chars (string, nchars)
206 if (terminal_write_chars_hook)
207 (*terminal_write_chars_hook) (string, nchars);
211 fwrite (string, 1, nchars, stdout);
215 /* Clear from the current position of the cursor to the end of the line. */
217 terminal_clear_to_eol ()
219 if (terminal_clear_to_eol_hook)
220 (*terminal_clear_to_eol_hook) ();
223 send_to_terminal (term_clreol);
227 /* Clear the entire terminal screen. */
229 terminal_clear_screen ()
231 if (terminal_clear_screen_hook)
232 (*terminal_clear_screen_hook) ();
235 send_to_terminal (term_clrpag);
239 /* Move the cursor up one line. */
243 if (terminal_up_line_hook)
244 (*terminal_up_line_hook) ();
247 send_to_terminal (term_up);
251 /* Move the cursor down one line. */
253 terminal_down_line ()
255 if (terminal_down_line_hook)
256 (*terminal_down_line_hook) ();
259 send_to_terminal (term_dn);
263 /* Turn on reverse video if possible. */
265 terminal_begin_inverse ()
267 if (terminal_begin_inverse_hook)
268 (*terminal_begin_inverse_hook) ();
271 send_to_terminal (term_invbeg);
275 /* Turn off reverse video if possible. */
277 terminal_end_inverse ()
279 if (terminal_end_inverse_hook)
280 (*terminal_end_inverse_hook) ();
283 send_to_terminal (term_invend);
287 /* Ring the terminal bell. The bell is run visibly if it both has one and
288 terminal_use_visible_bell_p is non-zero. */
290 terminal_ring_bell ()
292 if (terminal_ring_bell_hook)
293 (*terminal_ring_bell_hook) ();
296 if (terminal_has_visible_bell_p && terminal_use_visible_bell_p)
297 send_to_terminal (visible_bell);
299 send_to_terminal (audible_bell);
303 /* At the line START, delete COUNT lines from the terminal display. */
305 terminal_delete_lines (start, count)
310 /* Normalize arguments. */
314 lines = screenheight - start;
315 terminal_goto_xy (0, start);
317 tputs (tgoto (term_DL, 0, count), lines, output_character_function);
321 tputs (term_dl, lines, output_character_function);
327 /* At the line START, insert COUNT lines in the terminal display. */
329 terminal_insert_lines (start, count)
334 /* Normalize arguments. */
338 lines = screenheight - start;
339 terminal_goto_xy (0, start);
342 tputs (tgoto (term_AL, 0, count), lines, output_character_function);
346 tputs (term_al, lines, output_character_function);
352 /* Scroll an area of the terminal, starting with the region from START
353 to END, AMOUNT lines. If AMOUNT is negative, the lines are scrolled
354 towards the top of the screen, else they are scrolled towards the
355 bottom of the screen. */
357 terminal_scroll_terminal (start, end, amount)
358 int start, end, amount;
360 if (!terminal_can_scroll)
363 /* Any scrolling at all? */
367 if (terminal_scroll_terminal_hook)
368 (*terminal_scroll_terminal_hook) (start, end, amount);
371 /* If we are scrolling down, delete AMOUNT lines at END. Then insert
372 AMOUNT lines at START. */
375 terminal_delete_lines (end, amount);
376 terminal_insert_lines (start, amount);
379 /* If we are scrolling up, delete AMOUNT lines before START. This
380 actually does the upwards scroll. Then, insert AMOUNT lines
381 after the already scrolled region (i.e., END - AMOUNT). */
384 int abs_amount = -amount;
385 terminal_delete_lines (start - abs_amount, abs_amount);
386 terminal_insert_lines (end - abs_amount, abs_amount);
391 /* Re-initialize the terminal considering that the TERM/TERMCAP variable
394 terminal_new_terminal (terminal_name)
397 if (terminal_new_terminal_hook)
398 (*terminal_new_terminal_hook) (terminal_name);
401 terminal_initialize_terminal (terminal_name);
405 /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */
407 terminal_get_screen_size ()
409 if (terminal_get_screen_size_hook)
410 (*terminal_get_screen_size_hook) ();
413 screenwidth = screenheight = 0;
415 #if defined (TIOCGWINSZ)
417 struct winsize window_size;
419 if (ioctl (fileno (stdout), TIOCGWINSZ, &window_size) == 0)
421 screenwidth = (int) window_size.ws_col;
422 screenheight = (int) window_size.ws_row;
425 #endif /* TIOCGWINSZ */
427 /* Environment variable COLUMNS overrides setting of "co". */
428 if (screenwidth <= 0)
430 char *sw = getenv ("COLUMNS");
433 screenwidth = atoi (sw);
435 if (screenwidth <= 0)
436 screenwidth = tgetnum ("co");
439 /* Environment variable LINES overrides setting of "li". */
440 if (screenheight <= 0)
442 char *sh = getenv ("LINES");
445 screenheight = atoi (sh);
447 if (screenheight <= 0)
448 screenheight = tgetnum ("li");
451 /* If all else fails, default to 80x24 terminal. */
452 if (screenwidth <= 0)
455 if (screenheight <= 0)
460 /* Initialize the terminal which is known as TERMINAL_NAME. If this terminal
461 doesn't have cursor addressability, TERMINAL_IS_DUMB_P becomes non-zero.
462 The variables SCREENHEIGHT and SCREENWIDTH are set to the dimensions that
463 this terminal actually has. The variable TERMINAL_HAS_META_P becomes non-
464 zero if this terminal supports a Meta key. Finally, the terminal screen is
467 terminal_initialize_terminal (terminal_name)
472 terminal_is_dumb_p = 0;
474 if (terminal_initialize_terminal_hook)
476 (*terminal_initialize_terminal_hook) (terminal_name);
480 term = terminal_name ? terminal_name : getenv ("TERM");
482 if (!term_string_buffer)
483 term_string_buffer = (char *)xmalloc (2048);
486 term_buffer = (char *)xmalloc (2048);
488 buffer = term_string_buffer;
490 term_clrpag = term_cr = term_clreol = (char *)NULL;
495 if (tgetent (term_buffer, term) <= 0)
497 terminal_is_dumb_p = 1;
501 term_up = term_dn = audible_bell = visible_bell = (char *)NULL;
502 term_ku = term_kd = term_kl = term_kr = (char *)NULL;
506 BC = tgetstr ("pc", &buffer);
509 #if defined (TIOCGETP)
513 if (ioctl (fileno (stdout), TIOCGETP, &sg) != -1)
514 ospeed = sg.sg_ospeed;
520 #endif /* !TIOCGETP */
522 term_cr = tgetstr ("cr", &buffer);
523 term_clreol = tgetstr ("ce", &buffer);
524 term_clrpag = tgetstr ("cl", &buffer);
525 term_goto = tgetstr ("cm", &buffer);
527 /* Find out about this terminals scrolling capability. */
528 term_AL = tgetstr ("AL", &buffer);
529 term_DL = tgetstr ("DL", &buffer);
530 term_al = tgetstr ("al", &buffer);
531 term_dl = tgetstr ("dl", &buffer);
533 terminal_can_scroll = ((term_AL || term_al) && (term_DL || term_dl));
535 term_invbeg = tgetstr ("mr", &buffer);
537 term_invend = tgetstr ("me", &buffer);
539 term_invend = (char *)NULL;
544 terminal_get_screen_size ();
546 term_up = tgetstr ("up", &buffer);
547 term_dn = tgetstr ("dn", &buffer);
548 visible_bell = tgetstr ("vb", &buffer);
549 terminal_has_visible_bell_p = (visible_bell != (char *)NULL);
550 audible_bell = tgetstr ("bl", &buffer);
552 audible_bell = "\007";
553 term_begin_use = tgetstr ("ti", &buffer);
554 term_end_use = tgetstr ("te", &buffer);
556 /* Check to see if this terminal has a meta key. */
557 terminal_has_meta_p = (tgetflag ("km") || tgetflag ("MT"));
558 if (terminal_has_meta_p)
560 term_mm = tgetstr ("mm", &buffer);
561 term_mo = tgetstr ("mo", &buffer);
565 term_mm = (char *)NULL;
566 term_mo = (char *)NULL;
569 /* Attempt to find the arrow keys. */
570 term_ku = tgetstr ("ku", &buffer);
571 term_kd = tgetstr ("kd", &buffer);
572 term_kr = tgetstr ("kr", &buffer);
573 term_kl = tgetstr ("kl", &buffer);
575 /* If this terminal is not cursor addressable, then it is really dumb. */
577 terminal_is_dumb_p = 1;
579 terminal_begin_using_terminal ();
582 /* **************************************************************** */
584 /* How to Read Characters From the Terminal */
586 /* **************************************************************** */
588 #if defined (TIOCGETC)
589 /* A buffer containing the terminal interrupt characters upon entry
591 struct tchars original_tchars;
594 #if defined (TIOCGLTC)
595 /* A buffer containing the local terminal mode characters upon entry
597 struct ltchars original_ltchars;
600 #if defined (HAVE_TERMIOS_H)
601 struct termios original_termios, ttybuff;
603 # if defined (HAVE_TERMIO_H)
604 /* A buffer containing the terminal mode flags upon entry to info. */
605 struct termio original_termio, ttybuff;
606 # else /* !HAVE_TERMIO_H */
607 /* Buffers containing the terminal mode flags upon entry to info. */
608 int original_tty_flags = 0;
610 struct sgttyb ttybuff;
611 # endif /* !HAVE_TERMIO_H */
612 #endif /* !HAVE_TERMIOS_H */
614 /* Prepare to start using the terminal to read characters singly. */
616 terminal_prep_terminal ()
620 if (terminal_prep_terminal_hook)
622 (*terminal_prep_terminal_hook) ();
626 tty = fileno (stdin);
628 #if defined (HAVE_TERMIOS_H)
629 tcgetattr (tty, &original_termios);
630 tcgetattr (tty, &ttybuff);
632 # if defined (HAVE_TERMIO_H)
633 ioctl (tty, TCGETA, &original_termio);
634 ioctl (tty, TCGETA, &ttybuff);
638 #if defined (HAVE_TERMIOS_H) || defined (HAVE_TERMIO_H)
639 ttybuff.c_iflag &= (~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL & ~IXON);
640 ttybuff.c_oflag &= (~ONLCR & ~OCRNL);
641 ttybuff.c_lflag &= (~ICANON & ~ECHO);
643 ttybuff.c_cc[VMIN] = 1;
644 ttybuff.c_cc[VTIME] = 0;
646 if (ttybuff.c_cc[VINTR] == '\177')
647 ttybuff.c_cc[VINTR] = -1;
649 if (ttybuff.c_cc[VQUIT] == '\177')
650 ttybuff.c_cc[VQUIT] = -1;
653 #if defined (HAVE_TERMIOS_H)
654 tcsetattr (tty, TCSANOW, &ttybuff);
656 # if defined (HAVE_TERMIO_H)
657 ioctl (tty, TCSETA, &ttybuff);
661 #if !defined (HAVE_TERMIOS_H) && !defined (HAVE_TERMIO_H)
662 ioctl (tty, TIOCGETP, &ttybuff);
664 if (!original_tty_flags)
665 original_tty_flags = ttybuff.sg_flags;
667 /* Make this terminal pass 8 bits around while we are using it. */
669 ttybuff.sg_flags |= PASS8;
672 # if defined (TIOCLGET) && defined (LPASS8)
675 ioctl (tty, TIOCLGET, &flags);
676 original_lmode = flags;
678 ioctl (tty, TIOCLSET, &flags);
680 # endif /* TIOCLGET && LPASS8 */
682 # if defined (TIOCGETC)
686 ioctl (tty, TIOCGETC, &original_tchars);
687 temp = original_tchars;
690 temp.t_startc = temp.t_stopc = -1;
692 /* Often set to C-d. */
695 /* If the a quit or interrupt character conflicts with one of our
696 commands, then make it go away. */
697 if (temp.t_intrc == '\177')
700 if (temp.t_quitc == '\177')
703 ioctl (tty, TIOCSETC, &temp);
705 # endif /* TIOCGETC */
707 # if defined (TIOCGLTC)
711 ioctl (tty, TIOCGLTC, &original_ltchars);
712 temp = original_ltchars;
714 /* Make the interrupt keys go away. Just enough to make people happy. */
715 temp.t_lnextc = -1; /* C-v. */
716 temp.t_dsuspc = -1; /* C-y. */
717 temp.t_flushc = -1; /* C-o. */
718 ioctl (tty, TIOCSLTC, &temp);
720 # endif /* TIOCGLTC */
722 ttybuff.sg_flags &= ~ECHO;
723 ttybuff.sg_flags |= CBREAK;
724 ioctl (tty, TIOCSETN, &ttybuff);
725 #endif /* !HAVE_TERMIOS_H && !HAVE_TERMIO_H */
728 /* Restore the tty settings back to what they were before we started using
731 terminal_unprep_terminal ()
735 if (terminal_unprep_terminal_hook)
737 (*terminal_unprep_terminal_hook) ();
741 tty = fileno (stdin);
743 #if defined (HAVE_TERMIOS_H)
744 tcsetattr (tty, TCSANOW, &original_termios);
746 # if defined (HAVE_TERMIO_H)
747 ioctl (tty, TCSETA, &original_termio);
748 # else /* !HAVE_TERMIO_H */
749 ioctl (tty, TIOCGETP, &ttybuff);
750 ttybuff.sg_flags = original_tty_flags;
751 ioctl (tty, TIOCSETN, &ttybuff);
753 # if defined (TIOCGETC)
754 ioctl (tty, TIOCSETC, &original_tchars);
755 # endif /* TIOCGETC */
757 # if defined (TIOCGLTC)
758 ioctl (tty, TIOCSLTC, &original_ltchars);
759 # endif /* TIOCGLTC */
761 # if defined (TIOCLGET) && defined (LPASS8)
762 ioctl (tty, TIOCLSET, &original_lmode);
763 # endif /* TIOCLGET && LPASS8 */
765 # endif /* !HAVE_TERMIO_H */
766 #endif /* !HAVE_TERMIOS_H */
767 terminal_end_using_terminal ();