/*
* Project : tin - a Usenet reader
* Module : curses.c
* Author : D.Taylor & I.Lea
* Created : 01-01-86
* Updated : 22-09-93
* Notes : This is a screen management library borrowed with permission
* from the Elm mail system (a great mailer--I highly recommend
* it!).This library was hacked to provide what tin needs.
* Copyright : Copyright (c) 1986-93 Dave Taylor & Iain Lea
* The Elm Mail System - $Revision: 2.1 $ $State: Exp $
*/
#include "tin.h"
#ifndef ns32000
# undef sinix
#endif
#ifdef HAVE_ERRNO_H
# include <errno.h>
#else
# include <sys/errno.h>
#endif
#define DEFAULT_LINES_ON_TERMINAL 24
#define DEFAULT_COLUMNS_ON_TERMINAL 80
int cLINES = DEFAULT_LINES_ON_TERMINAL - 1;
int cCOLS = DEFAULT_COLUMNS_ON_TERMINAL;
int inverse_okay = TRUE;
static int _inraw = FALSE; /* are we IN rawmode? */
int _hp_glitch = FALSE; /* standout not erased by overwriting on HP terms */
static int xclicks=FALSE; /* do we have an xterm? */
#ifndef INDEX_DAEMON
#define BACKSPACE '\b'
#define VERY_LONG_STRING 2500
#if (defined(M_AMIGA) && !defined(__SASC)) || defined(COHERENT) || defined(BSD)
# ifndef BSD4_1
# include <sgtty.h>
# else
# include <termio.h>
# endif
#else
# ifndef SYSV
# ifndef MINIX
# ifdef sinix
# include <termios.h>
# else
# include <termio.h>
# endif
# else
# include <sgtty.h>
# endif
# else
# if defined(__hpux) || (defined(sun) && defined(SVR4))
# include <termio.h>
# endif
# endif
#endif
#define TTYIN 0
#ifdef SHORTNAMES
# define _clearinverse _clrinv
# define _cleartoeoln _clrtoeoln
# define _cleartoeos _clr2eos
#endif
#if (defined(M_AMIGA) && !defined(__SASC)) || defined(BSD) || defined(MINIX)
# ifdef TCGETA
# undef TCGETA
# endif
# define TCGETA TIOCGETP
# ifdef TCSETAW
# undef TCSETAW
# endif
# define TCSETAW TIOCSETP
struct sgttyb _raw_tty,
_original_tty;
#else
# if !defined(M_AMIGA)
# if defined(HAVE_TERMIOS_H) || defined(sinix)
# ifndef TCGETA
# define TCGETA STCGETA
# endif
# ifndef TCSETA
# define TCSETAW STCSETAW
# endif
struct termios _raw_tty,
_original_tty;
# else
# ifndef M_OS2
struct termio _raw_tty,
_original_tty;
# endif
# endif
# endif
#endif
static char *_clearscreen, *_moveto, *_cleartoeoln, *_cleartoeos,
*_setinverse, *_clearinverse, *_setunderline, *_clearunderline,
*_xclickinit, *_xclickend,
*_terminalinit, *_terminalend, *_keypadlocal, *_keypadxmit;
static int _columns, _line, _lines;
#ifdef M_UNIX
static char _terminal[1024]; /* Storage for terminal entry */
static char _capabilities[1024]; /* String for cursor motion */
static char *ptr = _capabilities; /* for buffering */
extern char *tgetstr (); /* Get termcap capability (string) */
extern char *tgoto (); /* and the goto stuff */
extern int tgetflag (); /* Get termcap capability (boolean) */
extern int tgetnum (); /* Get termcap capability (number) */
#endif /* M_UNIX */
static int in_inverse; /* 1 when in inverse, 0 otherwise */
#if __STDC__
int outchar (int c); /* char output for tputs */
#else
int outchar ();
#endif
#endif /* INDEX_DAEMON */
void
setup_screen ()
{
#ifndef INDEX_DAEMON
/*
* get screen size from termcap entry & setup sizes
*/
_line = 1;
ScreenSize (&cLINES, &cCOLS);
cmd_line = FALSE;
Raw (TRUE);
set_win_size (&cLINES, &cCOLS);
#endif /* INDEX_DAEMON */
}
#ifdef M_UNIX
int
InitScreen ()
{
#ifndef INDEX_DAEMON
extern int tgetent(); /* get termcap entry */
char termname[40], *p;
if ((p = (char *) getenv ("TERM")) == NULL) {
fprintf (stderr, "%s: TERM variable must be set to use screen capabilities\n", progname);
return (FALSE);
}
if (strcpy (termname, p) == NULL) {
fprintf (stderr,"%s: Can't get TERM variable\n", progname);
return (FALSE);
}
if (tgetent (_terminal, termname) != 1) {
fprintf (stderr,"%s: Can't get entry for TERM\n", progname);
return (FALSE);
}
/* load in all those pesky values */
_clearscreen = tgetstr ("cl", &ptr);
_moveto = tgetstr ("cm", &ptr);
_cleartoeoln = tgetstr ("ce", &ptr);
_cleartoeos = tgetstr ("cd", &ptr);
_lines = tgetnum ("li");
_columns = tgetnum ("co");
_setinverse = tgetstr ("so", &ptr);
_clearinverse = tgetstr ("se", &ptr);
_setunderline = tgetstr ("us", &ptr);
_clearunderline = tgetstr ("ue", &ptr);
_hp_glitch = tgetflag ("xs");
#ifdef HAVE_BROKEN_TGETSTR
_terminalinit = "";
_terminalend = "";
_keypadlocal = "";
_keypadxmit = "";
#else
_terminalinit = tgetstr ("ti", &ptr);
_terminalend = tgetstr ("te", &ptr);
_keypadlocal = tgetstr ("ke", &ptr);
_keypadxmit = tgetstr ("ks", &ptr);
#endif
if (strcmp (termname, "xterm") == 0) {
xclicks = TRUE;
_xclickinit = "\033[?9h";
_xclickend = "\033[?9l";
}
InitWin ();
if (!_clearscreen) {
fprintf (stderr,
"%s: Terminal must have clearscreen (cl) capability\n",progname);
return (FALSE);
}
if (!_moveto) {
fprintf (stderr,
"%s: Terminal must have cursor motion (cm)\n", progname);
return (FALSE);
}
if (!_cleartoeoln) {
fprintf (stderr,
"%s: Terminal must have clear to end-of-line (ce)\n", progname);
return (FALSE);
}
if (!_cleartoeos) {
fprintf (stderr,
"%s: Terminal must have clear to end-of-screen (cd)\n", progname);
return (FALSE);
}
if (_lines == -1)
_lines = DEFAULT_LINES_ON_TERMINAL;
if (_columns == -1)
_columns = DEFAULT_COLUMNS_ON_TERMINAL;
/*
* kludge to workaround no inverse
*/
if (_setinverse == 0) {
_setinverse = _setunderline;
_clearinverse = _clearunderline;
if (_setinverse == 0)
draw_arrow_mark = 1;
}
return (TRUE);
#else
return (FALSE);
#endif /* INDEX_DAEMON */
}
#else /* ! M_UNIX */
int
InitScreen ()
{
#ifndef INDEX_DAEMON
char c, *ptr, buf[32];
/*
* we're going to assume a terminal here...
*/
_clearscreen = "\033[1;1H\033[J";
_moveto = "\033[%d;%dH"; /* not a termcap string! */
_cleartoeoln = "\033[K";
_setinverse = "\033[7m";
_clearinverse = "\033[0m";
_setunderline = "\033[4m";
_clearunderline = "\033[0m";
_keypadlocal = "";
_keypadxmit = "";
#ifdef M_AMIGA
_terminalinit = "\033c";
_terminalend = "\033c";
_cleartoeos = "\033[J";
#endif
#ifdef M_OS2
_cleartoeos = NULL;
_terminalinit = NULL;
_terminalend = "";
initscr ();
#endif
InitWin ();
_lines = _columns = -1;
/*
* Get lines and columns from environment settings - useful when
* you're using something other than an Amiga window
*/
if (ptr = getenv ("LINES")) {
_lines = atol (ptr);
}
if (ptr = getenv ("COLUMNS")) {
_columns = atol (ptr);
}
/*
* If that failed, try get a response from the console itself
*/
#ifdef M_AMIGA
if (_lines == -1 || _columns == -1) {
Raw (TRUE);
tputs ("\2330 q",1,outchar); /* identify yourself */
fflush (stdout);
getsize:
while (ReadCh () != 0x9b) {
; /* Look for escape */
}
/* get top */
ptr = buf;
do {
c = *ptr++ = ReadCh ();
} while (isdigit(c));
if (c != ';') {
goto getsize;
}
/* get right */
ptr = buf;
do {
c = *ptr++ = ReadCh ();
} while (isdigit(c));
if (c != ';') {
goto getsize;
}
/* get bottom */
ptr = buf;
do {
c = *ptr++ = ReadCh ();
} while (isdigit(c));
if (c != ';') {
goto getsize;
}
*ptr = 0;
_lines = atol (buf);
/* get right */
ptr = buf;
do {
c = *ptr++ = ReadCh ();
} while (isdigit(c));
if (c != ' ') {
goto getsize;
}
if (ReadCh () != 'r') {
goto getsize;
}
*ptr = 0;
_columns = atol (buf);
}
#endif /* M_AMIGA */
#ifdef M_OS2
if (_lines == -1 || _columns == -1) {
_lines = LINES;
_columns = COLS;
}
#endif /* M_OS2 */
Raw (FALSE);
return (TRUE);
#else
return (FALSE);
#endif /* INDEX_DAEMON */
}
#endif /* M_UNIX */
/*
* returns the number of lines and columns on the display.
*/
void
ScreenSize (num_lines, num_columns)
int *num_lines, *num_columns;
{
#ifndef INDEX_DAEMON
if (_lines == 0) _lines = DEFAULT_LINES_ON_TERMINAL;
if (_columns == 0) _columns = DEFAULT_COLUMNS_ON_TERMINAL;
*num_lines = _lines - 1; /* assume index from zero*/
*num_columns = _columns; /* assume index from one */
#endif /* INDEX_DAEMON */
}
void
InitWin ()
{
#ifndef INDEX_DAEMON
if (_terminalinit) {
tputs (_terminalinit, 1, outchar);
fflush (stdout);
}
set_keypad_on ();
set_xclick_on ();
#endif /* INDEX_DAEMON */
}
void
EndWin ()
{
#ifndef INDEX_DAEMON
if (_terminalend) {
tputs (_terminalend, 1, outchar);
fflush (stdout);
}
set_keypad_off ();
set_xclick_off ();
#endif /* INDEX_DAEMON */
}
void
set_keypad_on ()
{
#ifndef INDEX_DAEMON
# ifdef HAVE_KEYPAD
if (use_keypad && _keypadxmit) {
tputs (_keypadxmit, 1, outchar);
fflush (stdout);
}
# endif
#endif /* INDEX_DAEMON */
}
void
set_keypad_off ()
{
#ifndef INDEX_DAEMON
# ifdef HAVE_KEYPAD
if (use_keypad && _keypadlocal) {
tputs (_keypadlocal, 1, outchar);
fflush (stdout);
}
# endif
#endif /* INDEX_DAEMON */
}
/*
* clear the screen: returns -1 if not capable
*/
void
ClearScreen ()
{
#ifndef INDEX_DAEMON
tputs (_clearscreen, 1, outchar);
fflush (stdout); /* clear the output buffer */
_line = 1;
#endif /* INDEX_DAEMON */
}
/*
* move cursor to the specified row column on the screen.
* 0,0 is the top left!
*/
#ifdef M_UNIX
void
MoveCursor (row, col)
int row, col;
{
#ifndef INDEX_DAEMON
char *stuff, *tgoto();
stuff = tgoto (_moveto, col, row);
tputs (stuff, 1, outchar);
fflush (stdout);
_line = row + 1;
#endif /* INDEX_DAEMON */
}
#else /* ! M_UNIX */
void
MoveCursor (row, col)
int row, col;
{
#ifndef INDEX_DAEMON
char stuff[12], *tgoto();
sprintf (stuff, _moveto, row+1, col+1);
tputs (stuff, 1, outchar);
fflush (stdout);
_line = row + 1;
#endif /* INDEX_DAEMON */
}
#endif /* M_UNIX */
/*
* clear to end of line
*/
void
CleartoEOLN ()
{
#ifndef INDEX_DAEMON
tputs (_cleartoeoln, 1, outchar);
fflush (stdout); /* clear the output buffer */
#endif /* INDEX_DAEMON */
}
/*
* clear to end of screen
*/
void
CleartoEOS ()
{
#ifndef INDEX_DAEMON
int i;
if (_cleartoeos) {
tputs (_cleartoeos, 1, outchar);
} else {
for (i=_line - 1 ; i < _lines ; i++) {
MoveCursor (i, 0);
CleartoEOLN ();
}
}
fflush (stdout); /* clear the output buffer */
#endif /* INDEX_DAEMON */
}
/*
* set inverse video mode
*/
void
StartInverse ()
{
#ifndef INDEX_DAEMON
in_inverse = 1;
if (_setinverse && inverse_okay)
tputs (_setinverse, 1, outchar);
fflush (stdout);
#endif /* INDEX_DAEMON */
}
/*
* compliment of startinverse
*/
void
EndInverse ()
{
#ifndef INDEX_DAEMON
in_inverse = 0;
if (_clearinverse && inverse_okay)
tputs (_clearinverse, 1, outchar);
fflush (stdout);
#endif /* INDEX_DAEMON */
}
/*
* toggle inverse video mode
*/
void
ToggleInverse ()
{
#ifndef INDEX_DAEMON
if (in_inverse == 0)
StartInverse();
else
EndInverse();
#endif /* INDEX_DAEMON */
}
/*
* returns either 1 or 0, for ON or OFF
*/
int
RawState()
{
return (_inraw);
}
/*
* state is either TRUE or FALSE, as indicated by call
*/
void
Raw (state)
int state;
{
#if !defined(INDEX_DAEMON) && !defined(M_OS2)
#if defined(M_AMIGA) && defined(__SASC)
rawcon (state);
#else
if (state == FALSE && _inraw) {
#ifdef HAVE_TCSETATTR
(void) tcsetattr (TTYIN, TCSANOW, &_original_tty);
#else
(void) ioctl (TTYIN, TCSETAW, &_original_tty);
#endif
_inraw = 0;
} else if (state == TRUE && ! _inraw) {
#ifdef HAVE_TCGETATTR
(void) tcgetattr (TTYIN, &_original_tty);
(void) tcgetattr (TTYIN, &_raw_tty);
#else
(void) ioctl (TTYIN, TCGETA, &_original_tty); /* current setting */
(void) ioctl (TTYIN, TCGETA, &_raw_tty); /* again! */
#endif
#if defined(BSD) || defined(M_AMIGA) || defined(MINIX)
_raw_tty.sg_flags &= ~(ECHO | CRMOD); /* echo off */
_raw_tty.sg_flags |= CBREAK; /* raw on */
#ifdef M_AMIGA
_raw_tty.sg_flags |= RAW; /* Manx-C 5.2 does not support CBREAK */
#endif
#else
#ifdef QNX4
/* noecho raw mode */
_raw_tty.c_lflag &= ~(ICANON |ISIG| ECHO |ECHOK | ECHONL);
_raw_tty.c_oflag &= ~(OPOST);
_raw_tty.c_cc[VMIN] = 1; /* minimum # of chars to queue */
_raw_tty.c_cc[VTIME] = 0; /* minimum time to wait for input */
#else
_raw_tty.c_lflag &= ~(ICANON | ECHO); /* noecho raw mode */
_raw_tty.c_cc[VMIN] = '\01'; /* minimum # of chars to queue */
_raw_tty.c_cc[VTIME] = '\0'; /* minimum time to wait for input */
#endif /* QNX4 */
#endif
#ifdef HAVE_TCSETATTR
(void) tcsetattr (TTYIN, TCSANOW, &_raw_tty);
#else
(void) ioctl (TTYIN, TCSETAW, &_raw_tty);
#endif
_inraw = 1;
}
#endif /* M_AMIGA */
#endif /* INDEX_DAEMON */
}
/*
* read a character with Raw mode set!
*/
#ifdef M_OS2
int
ReadCh ()
{
#ifndef INDEX_DAEMON
extern int errno;
char ch;
KBDKEYINFO os2key;
int rc;
register int result = 0;
static secondkey = 0;
if (secondkey) {
result = secondkey;
secondkey = 0;
} else {
rc = KbdCharIn((PKBDKEYINFO)&os2key, IO_WAIT, 0);
result = os2key.chChar;
if (result == 0xe0) {
result = 0x1b;
switch (os2key.chScan) {
case 'H':
secondkey = 'A';
break;
case 'P':
secondkey = 'B';
break;
case 'K':
secondkey = 'D';
break;
case 'M':
secondkey = 'C';
break;
case 'I':
secondkey = 'I';
break;
case 'Q':
secondkey = 'G';
break;
case 'G':
secondkey = 'H';
break;
case 'O':
secondkey = 'F';
break;
default:
secondkey = '?';
}
} else if (result == 0x0d) {
result = 0x0a;
}
}
return ((result == EOF) ? EOF : result & 0xFF);
#endif /* INDEX_DAEMON */
}
#else /* ! M_OS2 */
int
ReadCh ()
{
#ifndef INDEX_DAEMON
extern int errno;
char ch;
register int result = 0;
#ifdef READ_CHAR_HACK
#undef getc
while ((result = getc(stdin)) == EOF) {
if (feof(stdin))
break;
#ifdef EINTR
if (ferror(stdin) && errno != EINTR)
#else
if (ferror(stdin))
#endif
break;
clearerr(stdin);
}
return ((result == EOF) ? EOF : result & 0xFF);
#else
# ifdef EINTR
while ((result = read (0, &ch, 1)) < 0 && errno == EINTR)
; /* spin on signal interrupts */
# else
result = read (0, &ch, 1);
# endif
return((result <= 0 ) ? EOF : ch & 0xFF);
#endif
#endif /* INDEX_DAEMON */
}
#endif /* M_OS2 */
/*
* output a character. From tputs... (Note: this CANNOT be a macro!)
*/
int
outchar (c)
int c;
{
return fputc (c, stdout);
}
/*
* setup to monitor mouse buttons if running in a xterm
*/
void
xclick (state)
int state;
{
#ifndef INDEX_DAEMON
static int prev_state = 999;
if (xclicks && prev_state != state) {
if (state == TRUE) {
tputs (_xclickinit, 1, outchar);
} else {
tputs (_xclickend, 1, outchar);
}
fflush (stdout);
prev_state = state;
}
#endif /* INDEX_DAEMON */
}
/*
* switch on monitoring of mouse buttons
*/
void
set_xclick_on ()
{
xclick (TRUE);
}
/*
* switch off monitoring of mouse buttons
*/
void
set_xclick_off ()
{
xclick (FALSE);
}