/*************************************************************************
* TinyFugue - programmable mud client
* Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2003, 2004, 2005, 2006-2007 Ken Keys
*
* TinyFugue (aka "tf") is protected under the terms of the GNU
* General Public License. See the file "COPYING" for details.
************************************************************************/
static const char RCSid[] = "$Id: tty.c,v 35004.38 2007/01/13 23:12:39 kkeys Exp $";
/*
* TTY driver routines.
*/
#include "tfconfig.h"
#include "port.h"
#ifdef EMXANSI
# define INCL_VIO
# include <os2.h>
#endif
#if HAVE_TERMIOS_H /* POSIX is the way to go. */
# define USE_TERMIOS
# include <termios.h>
/* # ifndef TIOCGWINSZ */
# include <sys/ioctl.h> /* BSD needs this for TIOCGWINSZ */
/* # endif */
# define tty_struct struct termios
# define insetattr(buf) (tcsetattr(STDIN_FILENO, TCSAFLUSH, (buf)))
# define ingetattr(buf) (tcgetattr(STDIN_FILENO, (buf)))
# define insetattr_error "tcsetattr"
# define ingetattr_error "tcgetattr"
#endif
#if HAVE_TERMIO_H /* with a few macros, this looks just like USE_TERMIOS */
# ifdef hpux /* hpux's termio is different. */
# define USE_HPUX_TERMIO
# include <sys/ioctl.h>
# include <termio.h>
# include <bsdtty.h>
# else
# define USE_TERMIOS
# include <termio.h>
# endif
# define tty_struct struct termio
# define insetattr(buf) (ioctl(STDIN_FILENO, TCSETAF, (buf)))
# define ingetattr(buf) (ioctl(STDIN_FILENO, TCGETA, (buf)))
# define insetattr_error "TCSETAF ioctl"
# define ingetattr_error "TCGETA ioctl"
#endif
#if NEED_PTEM_H /* Xenix, maybe others */
# include <sys/types.h> /* Needed for sys/stream.h, which is... */
# include <sys/stream.h> /* needed for sys/ptem.h, which is... */
# include <sys/ptem.h> /* needed for struct winsize. Ugh. */
#endif
#if HAVE_SGTTY_H
# define USE_SGTTY
# include <sys/ioctl.h>
# include <sgtty.h> /* BSD's old "new" terminal driver. */
# define tty_struct struct sgttyb
# define insetattr(buf) (ioctl(STDIN_FILENO, TIOCSETP, (buf)))
# define ingetattr(buf) (ioctl(STDIN_FILENO, TIOCGETP, (buf)))
# define insetattr_error "TIOCSETP ioctl"
# define ingetattr_error "TIOCGETP ioctl"
#endif
static tty_struct old_tty;
static int is_custom_tty = 0; /* is tty in customized mode? */
int no_tty = 1;
#include "tf.h"
#include "util.h" /* for macro.h */
#include "search.h" /* for variable.h */
#include "tty.h"
#include "output.h" /* ch_visual() */
#include "macro.h" /* add_ibind() */
#include "variable.h" /* set_var_by_*() */
#define DEFAULT_COLUMNS 80
void init_tty(void)
{
#ifdef USE_HPUX_TERMIO
struct ltchars chars;
#endif
#ifdef USE_SGTTY
struct ltchars chars;
#endif
char bs[2], dline[2], bword[2], refresh[2], lnext[2];
bs[0] = dline[0] = bword[0] = refresh[0] = lnext[0] = '\0';
no_tty = !isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO);
cbreak_noecho_mode();
if (!no_tty) {
#ifdef USE_TERMIOS
*bs = old_tty.c_cc[VERASE];
*dline = old_tty.c_cc[VKILL];
# ifdef VWERASE /* Not POSIX, but many systems have it. */
*bword = old_tty.c_cc[VWERASE];
# endif
# ifdef VREPRINT /* Not POSIX, but many systems have it. */
*refresh = old_tty.c_cc[VREPRINT];
# endif
# ifdef VLNEXT /* Not POSIX, but many systems have it. */
*lnext = old_tty.c_cc[VLNEXT];
# endif
#endif
#ifdef USE_HPUX_TERMIO
*bs = old_tty.c_cc[VERASE];
*dline = old_tty.c_cc[VKILL];
if (ioctl(STDIN_FILENO, TIOCGLTC, &chars) < 0) perror("TIOCGLTC ioctl");
else {
*bword = chars.t_werasc;
*refresh = chars.t_rprntc;
/* *lnext = chars.t_lnextc; */ /* ?? Screw it, use default. */
}
#endif
#ifdef USE_SGTTY
*bs = old_tty.sg_erase;
*dline = old_tty.sg_kill;
if (ioctl(STDIN_FILENO, TIOCGLTC, &chars) < 0) perror("TIOCGLTC ioctl");
else {
*bword = chars.t_werasc;
*refresh = chars.t_rprntc;
*lnext = chars.t_lnextc;
}
#endif
}
bs[1] = dline[1] = bword[1] = refresh[1] = lnext[1] = '\0';
/* Note that some systems use \0 to disable, others use \377; we must
* check both. Also, some seem to leave garbage in some of the fields,
* so we'll ignore anything that isn't a control character.
*/
if (is_cntrl(*bs) && *bs && *bs != '\b' && *bs != '\177')
add_ibind(bs, "/DOKEY BSPC");
if (is_cntrl(*bword) && *bword) add_ibind(bword, "/DOKEY BWORD");
/* if (is_cntrl(*dline) && *dline) add_ibind(dline, "/DOKEY DLINE"); */
if (is_cntrl(*refresh) && *refresh) add_ibind(refresh, "/DOKEY REFRESH");
if (is_cntrl(*lnext) && *lnext) add_ibind(lnext, "/DOKEY LNEXT");
}
#ifdef EMXANSI
# define CAN_GET_WINSIZE
# undef TIOCGWINSZ
#endif
#ifdef TIOCGWINSZ
# define CAN_GET_WINSIZE
#endif
int get_window_size(void)
{
#ifdef CAN_GET_WINSIZE
int ocol = columns, oline = lines;
int new_wrapsize;
# ifdef TIOCGWINSZ
struct winsize size;
int first = 1;
retry:
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &size) < 0) return 0;
if (size.ws_col > 0) columns = size.ws_col;
if (size.ws_row > 0) lines = size.ws_row;
if (first && lines < 3) {
/* Konsole sometimes sends an incorrect resize followed by a correct
* resize. The incorrect one would make tf disable visual mode. So
* if the resize looks fishy, wait briefly for a second resize, and
* if we get it, ignore the first. */
struct timeval timeout = {0,10000};
first = 0;
select(0, NULL, NULL, NULL, &timeout);
goto retry;
}
# endif
# ifdef EMXANSI
static VIOMODEINFO info; /* must be static for thunking (16 bit func) */
info.cb = sizeof(info);
VioGetMode(&info,(HVIO)0);
if (info.col > 0) columns = info.col;
if (info.row > 0) lines = info.row;
# endif
if (columns == ocol && lines == oline) return 1;
new_wrapsize = columns - (ocol - wrapsize);
if (new_wrapsize < 1)
new_wrapsize = columns > 1 ? columns - 1 : 1;
/* set_int_var_direct avoids ch_wrap() */
set_int_var_direct(&special_var[VAR_wrapsize], TYPE_INT, new_wrapsize);
ch_visual(NULL);
do_hook(H_RESIZE, NULL, "%d %d", columns, lines);
return 1;
#else
return 0;
#endif
}
void cbreak_noecho_mode(void)
{
tty_struct tty;
if (no_tty) return;
if (ingetattr(&tty) < 0) die(ingetattr_error, errno);
old_tty = tty;
#ifdef USE_TERMIOS
tty.c_lflag &= ~(ECHO | ICANON);
tty.c_lflag |= ISIG;
tty.c_iflag |= IGNBRK | IGNPAR;
tty.c_iflag &= ~(ICRNL | INLCR | ISTRIP | IGNCR);
# ifdef OCRNL
tty.c_oflag &= ~OCRNL;
# else
/* OCRNL should already be off anyway */
# endif
/* Leave ONLCR on, so write(1) and other things that blast onto the screen
* without asking look at least somewhat sane.
*/
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
tty.c_cc[VSTOP] = 0; /* disable useless key */
tty.c_cc[VSTART] = 0; /* disable useless key */
#if 0
# ifdef VLNEXT
tty.c_cc[VLNEXT] = 0; /* don't let useless key get caught by tty */
# endif
#endif
# ifdef VDSUSP
tty.c_cc[VDSUSP] = 0; /* disable this useless and confusing key */
# endif
#endif /* USE_TERMIOS */
#ifdef USE_HPUX_TERMIO
tty.c_lflag &= ~(ECHO | ECHOE | ICANON);
tty.c_iflag &= ~ICRNL;
tty.c_oflag &= ~OCRNL;
/* Leave ONLCR on, so write(1) and other things that blast onto the screen
* without asking look at least somewhat sane.
*/
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
#endif
#ifdef USE_SGTTY
tty.sg_flags |= CBREAK;
tty.sg_flags &= ~(ECHO | CRMOD);
/* Sgtty's CRMOD is equivalent to termios' (ICRNL | OCRNL | ONLCR).
* So to turn off icrnl and ocrnl we must also turn off onlcr.
* This means we'll have to print '\r' ourselves in output.c, and
* we can't do anything about making external screen writers look sane.
*/
#endif
if (insetattr(&tty) < 0) die(insetattr_error, errno);
is_custom_tty = 1;
}
void reset_tty(void)
{
if (is_custom_tty) {
if (insetattr(&old_tty) < 0) perror(insetattr_error);
else is_custom_tty = 0;
}
}
syntax highlighted by Code2HTML, v. 0.9.1