home *** CD-ROM | disk | FTP | other *** search
- /* terminal.c -- How to handle the physical terminal for Info. */
-
- /* This file is part of GNU Info, a program for reading online documentation
- stored in Info format.
-
- This file has appeared in prior works by the Free Software Foundation;
- thus it carries copyright dates from 1988 through 1993.
-
- Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993 Free Software
- Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- Written by Brian Fox (bfox@ai.mit.edu). */
-
- #include <stdio.h>
- #include <sys/types.h>
- #include "terminal.h"
- #include "termdep.h"
-
- extern void *xmalloc (), *xrealloc ();
-
- /* The Unix termcap interface code. */
-
- extern int tgetnum (), tgetflag (), tgetent ();
- extern char *tgetstr (), *tgoto ();
- extern char *getenv ();
- extern void tputs ();
-
- /* Function "hooks". If you make one of these point to a function, that
- function is called when appropriate instead of its namesake. Your
- function is called with exactly the same arguments that were passed
- to the namesake function. */
- VFunction *terminal_begin_inverse_hook = (VFunction *)NULL;
- VFunction *terminal_end_inverse_hook = (VFunction *)NULL;
- VFunction *terminal_prep_terminal_hook = (VFunction *)NULL;
- VFunction *terminal_unprep_terminal_hook = (VFunction *)NULL;
- VFunction *terminal_up_line_hook = (VFunction *)NULL;
- VFunction *terminal_down_line_hook = (VFunction *)NULL;
- VFunction *terminal_clear_screen_hook = (VFunction *)NULL;
- VFunction *terminal_clear_to_eol_hook = (VFunction *)NULL;
- VFunction *terminal_get_screen_size_hook = (VFunction *)NULL;
- VFunction *terminal_goto_xy_hook = (VFunction *)NULL;
- VFunction *terminal_initialize_terminal_hook = (VFunction *)NULL;
- VFunction *terminal_new_terminal_hook = (VFunction *)NULL;
- VFunction *terminal_put_text_hook = (VFunction *)NULL;
- VFunction *terminal_ring_bell_hook = (VFunction *)NULL;
- VFunction *terminal_write_chars_hook = (VFunction *)NULL;
- VFunction *terminal_scroll_terminal_hook = (VFunction *)NULL;
-
- /* **************************************************************** */
- /* */
- /* Terminal and Termcap */
- /* */
- /* **************************************************************** */
-
- /* On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC.
- Unfortunately, PC is a global variable used by the termcap library. */
- #undef PC
-
- /* TERMCAP requires these variables, whether we access them or not. */
- char PC;
- char *BC, *UP;
- short ospeed;
-
- /* A buffer which holds onto the current terminal description, and a pointer
- used to float within it. */
- static char *term_buffer = (char *)NULL;
- static char *term_string_buffer = (char *)NULL;
-
- /* Some strings to control terminal actions. These are output by tputs (). */
- static char *term_goto, *term_clreol, *term_cr, *term_clrpag;
- static char *term_begin_use, *term_end_use;
- static char *term_AL, *term_DL, *term_al, *term_dl;
-
- /* How to go up a line. */
- static char *term_up;
-
- /* How to go down a line. */
- static char *term_dn;
-
- /* An audible bell, if the terminal can be made to make noise. */
- static char *audible_bell;
-
- /* A visible bell, if the terminal can be made to flash the screen. */
- static char *visible_bell;
-
- /* The string to write to turn on the meta key, if this term has one. */
- static char *term_mm;
-
- /* The string to write to turn off the meta key, if this term has one. */
- static char *term_mo;
-
- /* The string to turn on inverse mode, if this term has one. */
- static char *term_invbeg;
-
- /* The string to turn off inverse mode, if this term has one. */
- static char *term_invend;
-
- static void
- output_character_function (c)
- int c;
- {
- putc (c, stdout);
- }
-
- /* Macro to send STRING to the terminal. */
- #define send_to_terminal(string) \
- do { \
- if (string) \
- tputs (string, 1, output_character_function); \
- } while (0)
-
- /* Tell the terminal that we will be doing cursor addressable motion. */
- static void
- terminal_begin_using_terminal ()
- {
- send_to_terminal (term_begin_use);
- }
-
- /* Tell the terminal that we will not be doing any more cursor addressable
- motion. */
- static void
- terminal_end_using_terminal ()
- {
- send_to_terminal (term_end_use);
- }
-
- /* **************************************************************** */
- /* */
- /* Necessary Terminal Functions */
- /* */
- /* **************************************************************** */
-
- /* The functions and variables on this page implement the user visible
- portion of the terminal interface. */
-
- /* The width and height of the terminal. */
- int screenwidth, screenheight;
-
- /* Non-zero means this terminal can't really do anything. */
- int terminal_is_dumb_p = 0;
-
- /* Non-zero means that this terminal has a meta key. */
- int terminal_has_meta_p = 0;
-
- /* Non-zero means that this terminal can produce a visible bell. */
- int terminal_has_visible_bell_p = 0;
-
- /* Non-zero means to use that visible bell if at all possible. */
- int terminal_use_visible_bell_p = 0;
-
- /* Non-zero means that the terminal can do scrolling. */
- int terminal_can_scroll = 0;
-
- /* The key sequences output by the arrow keys, if this terminal has any. */
- char *term_ku, *term_kd, *term_kr, *term_kl;
-
- /* Move the cursor to the terminal location of X and Y. */
- void
- terminal_goto_xy (x, y)
- int x, y;
- {
- if (terminal_goto_xy_hook)
- (*terminal_goto_xy_hook) (x, y);
- else
- {
- if (term_goto)
- tputs (tgoto (term_goto, x, y), 1, output_character_function);
- }
- }
-
- /* Print STRING to the terminal at the current position. */
- void
- terminal_put_text (string)
- char *string;
- {
- if (terminal_put_text_hook)
- (*terminal_put_text_hook) (string);
- else
- {
- printf ("%s", string);
- }
- }
-
- /* Print NCHARS from STRING to the terminal at the current position. */
- void
- terminal_write_chars (string, nchars)
- char *string;
- int nchars;
- {
- if (terminal_write_chars_hook)
- (*terminal_write_chars_hook) (string, nchars);
- else
- {
- if (nchars)
- fwrite (string, 1, nchars, stdout);
- }
- }
-
- /* Clear from the current position of the cursor to the end of the line. */
- void
- terminal_clear_to_eol ()
- {
- if (terminal_clear_to_eol_hook)
- (*terminal_clear_to_eol_hook) ();
- else
- {
- send_to_terminal (term_clreol);
- }
- }
-
- /* Clear the entire terminal screen. */
- void
- terminal_clear_screen ()
- {
- if (terminal_clear_screen_hook)
- (*terminal_clear_screen_hook) ();
- else
- {
- send_to_terminal (term_clrpag);
- }
- }
-
- /* Move the cursor up one line. */
- void
- terminal_up_line ()
- {
- if (terminal_up_line_hook)
- (*terminal_up_line_hook) ();
- else
- {
- send_to_terminal (term_up);
- }
- }
-
- /* Move the cursor down one line. */
- void
- terminal_down_line ()
- {
- if (terminal_down_line_hook)
- (*terminal_down_line_hook) ();
- else
- {
- send_to_terminal (term_dn);
- }
- }
-
- /* Turn on reverse video if possible. */
- void
- terminal_begin_inverse ()
- {
- if (terminal_begin_inverse_hook)
- (*terminal_begin_inverse_hook) ();
- else
- {
- send_to_terminal (term_invbeg);
- }
- }
-
- /* Turn off reverse video if possible. */
- void
- terminal_end_inverse ()
- {
- if (terminal_end_inverse_hook)
- (*terminal_end_inverse_hook) ();
- else
- {
- send_to_terminal (term_invend);
- }
- }
-
- /* Ring the terminal bell. The bell is run visibly if it both has one and
- terminal_use_visible_bell_p is non-zero. */
- void
- terminal_ring_bell ()
- {
- if (terminal_ring_bell_hook)
- (*terminal_ring_bell_hook) ();
- else
- {
- if (terminal_has_visible_bell_p && terminal_use_visible_bell_p)
- send_to_terminal (visible_bell);
- else
- send_to_terminal (audible_bell);
- }
- }
-
- /* At the line START, delete COUNT lines from the terminal display. */
- static void
- terminal_delete_lines (start, count)
- int start, count;
- {
- int lines;
-
- /* Normalize arguments. */
- if (start < 0)
- start = 0;
-
- lines = screenheight - start;
- terminal_goto_xy (0, start);
- if (term_DL)
- tputs (tgoto (term_DL, 0, count), lines, output_character_function);
- else
- {
- while (count--)
- tputs (term_dl, lines, output_character_function);
- }
-
- fflush (stdout);
- }
-
- /* At the line START, insert COUNT lines in the terminal display. */
- static void
- terminal_insert_lines (start, count)
- int start, count;
- {
- int lines;
-
- /* Normalize arguments. */
- if (start < 0)
- start = 0;
-
- lines = screenheight - start;
- terminal_goto_xy (0, start);
-
- if (term_AL)
- tputs (tgoto (term_AL, 0, count), lines, output_character_function);
- else
- {
- while (count--)
- tputs (term_al, lines, output_character_function);
- }
-
- fflush (stdout);
- }
-
- /* Scroll an area of the terminal, starting with the region from START
- to END, AMOUNT lines. If AMOUNT is negative, the lines are scrolled
- towards the top of the screen, else they are scrolled towards the
- bottom of the screen. */
- void
- terminal_scroll_terminal (start, end, amount)
- int start, end, amount;
- {
- if (!terminal_can_scroll)
- return;
-
- /* Any scrolling at all? */
- if (amount == 0)
- return;
-
- if (terminal_scroll_terminal_hook)
- (*terminal_scroll_terminal_hook) (start, end, amount);
- else
- {
- /* If we are scrolling down, delete AMOUNT lines at END. Then insert
- AMOUNT lines at START. */
- if (amount > 0)
- {
- terminal_delete_lines (end, amount);
- terminal_insert_lines (start, amount);
- }
-
- /* If we are scrolling up, delete AMOUNT lines before START. This
- actually does the upwards scroll. Then, insert AMOUNT lines
- after the already scrolled region (i.e., END - AMOUNT). */
- if (amount < 0)
- {
- int abs_amount = -amount;
- terminal_delete_lines (start - abs_amount, abs_amount);
- terminal_insert_lines (end - abs_amount, abs_amount);
- }
- }
- }
-
- /* Re-initialize the terminal considering that the TERM/TERMCAP variable
- has changed. */
- void
- terminal_new_terminal (terminal_name)
- char *terminal_name;
- {
- if (terminal_new_terminal_hook)
- (*terminal_new_terminal_hook) (terminal_name);
- else
- {
- terminal_initialize_terminal (terminal_name);
- }
- }
-
- /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */
- void
- terminal_get_screen_size ()
- {
- if (terminal_get_screen_size_hook)
- (*terminal_get_screen_size_hook) ();
- else
- {
- screenwidth = screenheight = 0;
-
- #if defined (TIOCGWINSZ)
- {
- struct winsize window_size;
-
- if (ioctl (fileno (stdout), TIOCGWINSZ, &window_size) == 0)
- {
- screenwidth = (int) window_size.ws_col;
- screenheight = (int) window_size.ws_row;
- }
- }
- #endif /* TIOCGWINSZ */
-
- /* Environment variable COLUMNS overrides setting of "co". */
- if (screenwidth <= 0)
- {
- char *sw = getenv ("COLUMNS");
-
- if (sw)
- screenwidth = atoi (sw);
-
- if (screenwidth <= 0)
- screenwidth = tgetnum ("co");
- }
-
- /* Environment variable LINES overrides setting of "li". */
- if (screenheight <= 0)
- {
- char *sh = getenv ("LINES");
-
- if (sh)
- screenheight = atoi (sh);
-
- if (screenheight <= 0)
- screenheight = tgetnum ("li");
- }
-
- /* If all else fails, default to 80x24 terminal. */
- if (screenwidth <= 0)
- screenwidth = 80;
-
- if (screenheight <= 0)
- screenheight = 24;
- }
- }
-
- /* Initialize the terminal which is known as TERMINAL_NAME. If this terminal
- doesn't have cursor addressability, TERMINAL_IS_DUMB_P becomes non-zero.
- The variables SCREENHEIGHT and SCREENWIDTH are set to the dimensions that
- this terminal actually has. The variable TERMINAL_HAS_META_P becomes non-
- zero if this terminal supports a Meta key. Finally, the terminal screen is
- cleared. */
- void
- terminal_initialize_terminal (terminal_name)
- char *terminal_name;
- {
- char *term, *buffer;
-
- terminal_is_dumb_p = 0;
-
- if (terminal_initialize_terminal_hook)
- {
- (*terminal_initialize_terminal_hook) (terminal_name);
- return;
- }
-
- term = terminal_name ? terminal_name : getenv ("TERM");
-
- if (!term_string_buffer)
- term_string_buffer = (char *)xmalloc (2048);
-
- if (!term_buffer)
- term_buffer = (char *)xmalloc (2048);
-
- buffer = term_string_buffer;
-
- term_clrpag = term_cr = term_clreol = (char *)NULL;
-
- if (!term)
- term = "dumb";
-
- if (tgetent (term_buffer, term) <= 0)
- {
- terminal_is_dumb_p = 1;
- screenwidth = 80;
- screenheight = 24;
- term_cr = "\r";
- term_up = term_dn = audible_bell = visible_bell = (char *)NULL;
- term_ku = term_kd = term_kl = term_kr = (char *)NULL;
- return;
- }
-
- BC = tgetstr ("pc", &buffer);
- PC = BC ? *BC : 0;
-
- #if defined (TIOCGETP)
- {
- struct sgttyb sg;
-
- if (ioctl (fileno (stdout), TIOCGETP, &sg) != -1)
- ospeed = sg.sg_ospeed;
- else
- ospeed = B9600;
- }
- #else
- ospeed = B9600;
- #endif /* !TIOCGETP */
-
- term_cr = tgetstr ("cr", &buffer);
- term_clreol = tgetstr ("ce", &buffer);
- term_clrpag = tgetstr ("cl", &buffer);
- term_goto = tgetstr ("cm", &buffer);
-
- /* Find out about this terminals scrolling capability. */
- term_AL = tgetstr ("AL", &buffer);
- term_DL = tgetstr ("DL", &buffer);
- term_al = tgetstr ("al", &buffer);
- term_dl = tgetstr ("dl", &buffer);
-
- terminal_can_scroll = ((term_AL || term_al) && (term_DL || term_dl));
-
- term_invbeg = tgetstr ("mr", &buffer);
- if (term_invbeg)
- term_invend = tgetstr ("me", &buffer);
- else
- term_invend = (char *)NULL;
-
- if (!term_cr)
- term_cr = "\r";
-
- terminal_get_screen_size ();
-
- term_up = tgetstr ("up", &buffer);
- term_dn = tgetstr ("dn", &buffer);
- visible_bell = tgetstr ("vb", &buffer);
- terminal_has_visible_bell_p = (visible_bell != (char *)NULL);
- audible_bell = tgetstr ("bl", &buffer);
- if (!audible_bell)
- audible_bell = "\007";
- term_begin_use = tgetstr ("ti", &buffer);
- term_end_use = tgetstr ("te", &buffer);
-
- /* Check to see if this terminal has a meta key. */
- terminal_has_meta_p = (tgetflag ("km") || tgetflag ("MT"));
- if (terminal_has_meta_p)
- {
- term_mm = tgetstr ("mm", &buffer);
- term_mo = tgetstr ("mo", &buffer);
- }
- else
- {
- term_mm = (char *)NULL;
- term_mo = (char *)NULL;
- }
-
- /* Attempt to find the arrow keys. */
- term_ku = tgetstr ("ku", &buffer);
- term_kd = tgetstr ("kd", &buffer);
- term_kr = tgetstr ("kr", &buffer);
- term_kl = tgetstr ("kl", &buffer);
-
- /* If this terminal is not cursor addressable, then it is really dumb. */
- if (!term_goto)
- terminal_is_dumb_p = 1;
-
- terminal_begin_using_terminal ();
- }
-
- /* **************************************************************** */
- /* */
- /* How to Read Characters From the Terminal */
- /* */
- /* **************************************************************** */
-
- #if defined (TIOCGETC)
- /* A buffer containing the terminal interrupt characters upon entry
- to Info. */
- struct tchars original_tchars;
- #endif
-
- #if defined (TIOCGLTC)
- /* A buffer containing the local terminal mode characters upon entry
- to Info. */
- struct ltchars original_ltchars;
- #endif
-
- #if defined (HAVE_TERMIO_H)
- /* A buffer containing the terminal mode flags upon entry to info. */
- struct termio original_termio, ttybuff;
- #else /* !HAVE_TERMIO_H */
- /* Buffers containing the terminal mode flags upon entry to info. */
- int original_tty_flags = 0;
- int original_lmode;
- struct sgttyb ttybuff;
- #endif /* !HAVE_TERMIO_H */
-
- /* Prepare to start using the terminal to read characters singly. */
- void
- terminal_prep_terminal ()
- {
- int tty;
-
- if (terminal_prep_terminal_hook)
- {
- (*terminal_prep_terminal_hook) ();
- return;
- }
-
- tty = fileno (stdin);
-
- #if defined (HAVE_TERMIO_H)
- ioctl (tty, TCGETA, &original_termio);
- ioctl (tty, TCGETA, &ttybuff);
- ttybuff.c_iflag &= (~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL & ~IXON);
- ttybuff.c_oflag &= (~ONLCR & ~OCRNL);
- ttybuff.c_lflag &= (~ICANON & ~ECHO);
-
- ttybuff.c_cc[VMIN] = 1;
- ttybuff.c_cc[VTIME] = 0;
-
- if (ttybuff.c_cc[VINTR] = '\177')
- ttybuff.c_cc[VINTR] = -1;
-
- if (ttybuff.c_cc[VQUIT] = '\177')
- ttybuff.c_cc[VQUIT] = -1;
-
- ioctl (tty, TCSETA, &ttybuff);
-
- #else /* !HAVE_TERMIO_H */
-
- ioctl (tty, TIOCGETP, &ttybuff);
-
- if (!original_tty_flags)
- original_tty_flags = ttybuff.sg_flags;
-
- /* Make this terminal pass 8 bits around while we are using it. */
- #if defined (PASS8)
- ttybuff.sg_flags |= PASS8;
- #endif /* PASS8 */
-
- #if defined (TIOCLGET) && defined (LPASS8)
- {
- int flags;
- ioctl (tty, TIOCLGET, &flags);
- original_lmode = flags;
- flags |= LPASS8;
- ioctl (tty, TIOCLSET, &flags);
- }
- #endif /* TIOCLGET && LPASS8 */
-
- #if defined (TIOCGETC)
- {
- struct tchars temp;
-
- ioctl (tty, TIOCGETC, &original_tchars);
- temp = original_tchars;
-
- /* C-s and C-q. */
- temp.t_startc = temp.t_stopc = -1;
-
- /* Often set to C-d. */
- temp.t_eofc = -1;
-
- /* If the a quit or interrupt character conflicts with one of our
- commands, then make it go away. */
- if (temp.t_intrc == '\177')
- temp.t_intrc = -1;
-
- if (temp.t_quitc == '\177')
- temp.t_quitc = -1;
-
- ioctl (tty, TIOCSETC, &temp);
- }
- #endif /* TIOCGETC */
-
- #if defined (TIOCGLTC)
- {
- struct ltchars temp;
-
- ioctl (tty, TIOCGLTC, &original_ltchars);
- temp = original_ltchars;
-
- /* Make the interrupt keys go away. Just enough to make people happy. */
- temp.t_lnextc = -1; /* C-v. */
- temp.t_dsuspc = -1; /* C-y. */
- temp.t_flushc = -1; /* C-o. */
- ioctl (tty, TIOCSLTC, &temp);
- }
- #endif /* TIOCGLTC */
-
- ttybuff.sg_flags &= ~ECHO;
- ttybuff.sg_flags |= CBREAK;
- ioctl (tty, TIOCSETN, &ttybuff);
- #endif /* !HAVE_TERMIO_H */
- }
-
- /* Restore the tty settings back to what they were before we started using
- this terminal. */
- void
- terminal_unprep_terminal ()
- {
- int tty;
-
- if (terminal_unprep_terminal_hook)
- {
- (*terminal_unprep_terminal_hook) ();
- return;
- }
-
- tty = fileno (stdin);
-
- #if defined (HAVE_TERMIO_H)
- ioctl (tty, TCSETA, &original_termio);
- #else /* !HAVE_TERMIO_H */
- ioctl (tty, TIOCGETP, &ttybuff);
- ttybuff.sg_flags = original_tty_flags;
- ioctl (tty, TIOCSETN, &ttybuff);
-
- #if defined (TIOCGETC)
- ioctl (tty, TIOCSETC, &original_tchars);
- #endif /* TIOCGETC */
-
- #if defined (TIOCGLTC)
- ioctl (tty, TIOCSLTC, &original_ltchars);
- #endif /* TIOCGLTC */
-
- #if defined (TIOCLGET) && defined (LPASS8)
- ioctl (tty, TIOCLSET, &original_lmode);
- #endif /* TIOCLGET && LPASS8 */
-
- #endif /* !HAVE_TERMIO_H */
- terminal_end_using_terminal ();
- }
-
-