tin-1.22/curses.c

/*
 *  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);
}