377 lines
8.1 KiB
C
377 lines
8.1 KiB
C
/* curses.c */
|
|
|
|
/* Author:
|
|
* Steve Kirkendall
|
|
* 16820 SW Tallac Way
|
|
* Beaverton, OR 97006
|
|
* kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
|
|
*/
|
|
|
|
|
|
/* This file contains the functions & variables needed for a tiny subset of
|
|
* curses. The principle advantage of this version of curses is its
|
|
* extreme speed. Disadvantages are potentially larger code, few supported
|
|
* functions, limited compatibility with full curses, and only stdscr.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include "curses.h"
|
|
#ifdef M_SYSV
|
|
# include <termio.h>
|
|
#else
|
|
# include <sgtty.h>
|
|
#endif
|
|
#include <signal.h>
|
|
|
|
extern char *malloc();
|
|
extern char *getenv();
|
|
|
|
extern trapint();
|
|
|
|
/* variables, publicly available & used in the macros */
|
|
short ospeed; /* speed of the tty, eg B2400 */
|
|
WINDOW *stdscr; /* pointer into kbuf[] */
|
|
WINDOW kbuf[KBSIZ]; /* a very large output buffer */
|
|
int LINES; /* :li#: number of rows */
|
|
int COLS; /* :co#: number of columns */
|
|
int AM; /* :am: boolean: auto margins? */
|
|
int PT; /* :pt: boolean: physical tabs? */
|
|
char *VB; /* :vb=: visible bell */
|
|
char *UP; /* :up=: move cursor up */
|
|
char *SC; /* :sc=: save cursor position & char attributes */
|
|
char *RC; /* :rc=: resore cursor position & char attributes */
|
|
char *SO; /* :so=: standout start */
|
|
char *SE; /* :se=: standout end */
|
|
char *US = ""; /* :us=: underline start */
|
|
char *UE = ""; /* :ue=: underline end */
|
|
char *VB_s = ""; /* :VB=: bold start */
|
|
char *VB_e = ""; /* :Vb=: bold end */
|
|
char *AS; /* :as=: alternate (italic) start */
|
|
char *AE; /* :ae=: alternate (italic) end */
|
|
char *CM; /* :cm=: cursor movement */
|
|
char *CE; /* :ce=: clear to end of line */
|
|
char *CL; /* :cl=: home cursor & clear screen */
|
|
char *CD; /* :cd=: clear to end of screen */
|
|
char *AL; /* :al=: add a line */
|
|
char *DL; /* :dl=: delete a line */
|
|
char *SR; /* :sr=: scroll reverse */
|
|
char *KU; /* :ku=: key sequence sent by up arrow */
|
|
char *KD; /* :kd=: key sequence sent by down arrow */
|
|
char *KL; /* :kl=: key sequence sent by left arrow */
|
|
char *KR; /* :kr=: key sequence sent by right arrow */
|
|
char *PU; /* :PU=: key sequence sent by PgUp key */
|
|
char *PD; /* :PD=: key sequence sent by PgDn key */
|
|
char *HM; /* :HM=: key sequence sent by Home key */
|
|
char *EN; /* :EN=: key sequence sent by End key */
|
|
char *IM; /* :im=: insert mode start */
|
|
char *IC = ""; /* :ic=: insert the following character */
|
|
char *EI; /* :ei=: insert mode end */
|
|
char *DC; /* :dc=: delete a character */
|
|
char *aend = ""; /* end an attribute -- either UE or VB_e */
|
|
char ERASEKEY; /* backspace key taken from ioctl structure */
|
|
|
|
#ifdef M_SYSV
|
|
static struct termio oldtermio; /* original tty mode */
|
|
static struct termio newtermio; /* raw/noecho tty mode */
|
|
#else
|
|
static struct sgttyb oldsgttyb; /* original tty mode */
|
|
static struct sgttyb newsgttyb; /* raw/nl/noecho tty mode */
|
|
#endif
|
|
|
|
static char *capbuf; /* capability string buffer */
|
|
|
|
|
|
initscr()
|
|
{
|
|
/* make sure TERM variable is set */
|
|
if (!getenv("TERM"))
|
|
{
|
|
printf("initscr: environment variable TERM must be set\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* start termcap stuff */
|
|
starttcap();
|
|
|
|
/* create stdscr and curscr */
|
|
stdscr = kbuf;
|
|
|
|
/* change the terminal mode to raw/noecho */
|
|
#ifdef M_SYSV
|
|
ioctl(2, TCGETA, &oldtermio);
|
|
#else
|
|
ioctl(2, TIOCGETP, &oldsgttyb);
|
|
#endif
|
|
resume_curses(TRUE);
|
|
}
|
|
|
|
|
|
endwin()
|
|
{
|
|
/* flush any last changes */
|
|
refresh();
|
|
|
|
/* change the terminal mode back the way it was */
|
|
suspend_curses();
|
|
}
|
|
|
|
suspend_curses()
|
|
{
|
|
/* change the terminal mode back the way it was */
|
|
#ifdef M_SYSV
|
|
ioctl(2, TCSETAF, &oldtermio);
|
|
#else
|
|
ioctl(2, TIOCSETP, &oldsgttyb);
|
|
#endif
|
|
}
|
|
|
|
resume_curses(quietly)
|
|
int quietly;
|
|
{
|
|
char *src, *dest;
|
|
|
|
/* change the terminal mode to raw/noecho */
|
|
#ifdef M_SYSV
|
|
newtermio = oldtermio;
|
|
newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK);
|
|
newtermio.c_oflag &= ~OPOST;
|
|
newtermio.c_lflag &= XCLUDE;
|
|
newtermio.c_cc[VEOF] = 1; /* minimum # characters to read */
|
|
newtermio.c_cc[VEOL] = 2; /* timeout after 0.2 seconds */
|
|
ioctl(2, TCSETAF, &newtermio);
|
|
|
|
ospeed = (oldtermio.c_cflag & CBAUD);
|
|
ERASEKEY = oldtermio.c_cc[VERASE];
|
|
#else
|
|
newsgttyb = oldsgttyb;
|
|
newsgttyb.sg_flags |= CBREAK;
|
|
newsgttyb.sg_flags &= ~(CRMOD|ECHO|XTABS);
|
|
ioctl(2, TIOCSETP, &newsgttyb);
|
|
|
|
ospeed = oldsgttyb.sg_ospeed;
|
|
ERASEKEY = oldsgttyb.sg_erase;
|
|
#endif
|
|
|
|
if (quietly) return;
|
|
|
|
signal(SIGINT, SIG_IGN);
|
|
|
|
/* Wait for a key from the user */
|
|
for (dest = kbuf, src = tgoto(CM, 0, LINES - 1); *src; )
|
|
*dest++ = *src++;
|
|
for (src = SO; src && *src; )
|
|
*dest++ = *src++;
|
|
for (src = "[Press <RETURN> to continue]"; *src; )
|
|
*dest++ = *src++;
|
|
for (src = SE; src && *src; )
|
|
*dest++ = *src++;
|
|
write(1, kbuf, (int)(dest - kbuf));
|
|
read(0, kbuf, 20); /* in RAW mode, so <20 is very likely */
|
|
|
|
|
|
/* !!! special processing of the : key for Elvis' VI mode */
|
|
if (kbuf[0] == ':')
|
|
{
|
|
ungetkey(':');
|
|
}
|
|
|
|
signal(SIGINT, trapint);
|
|
}
|
|
|
|
static lacking(s)
|
|
char *s;
|
|
{
|
|
write(2, "This termcap entry lacks the :", 30);
|
|
write(2, s, 2);
|
|
write(2, "=: capability\n", 14);
|
|
exit(1);
|
|
}
|
|
|
|
starttcap()
|
|
{
|
|
char *str;
|
|
static char cbmem[800];
|
|
#define MUSTHAVE(T,s) if (!(T = tgetstr(s, &capbuf))) lacking(s)
|
|
#define MAYHAVE(T,s) if (str = tgetstr(s, &capbuf)) T = str
|
|
#define PAIR(T,U,sT,sU) T=tgetstr(sT,&capbuf);U=tgetstr(sU,&capbuf);if (!T||!U)T=U=""
|
|
|
|
/* allocate memory for capbuf */
|
|
capbuf = cbmem;
|
|
|
|
/* get the termcap entry */
|
|
if (tgetent(kbuf, getenv("TERM")) != 1)
|
|
{
|
|
perror("tgetent");
|
|
exit(1);
|
|
}
|
|
|
|
/* get strings */
|
|
MUSTHAVE(UP, "up");
|
|
MAYHAVE(SC, "sc");
|
|
MAYHAVE(RC, "rc");
|
|
MAYHAVE(VB, "vb");
|
|
MUSTHAVE(CM, "cm");
|
|
PAIR(SO, SE, "so", "se");
|
|
if (tgetnum("ug") <= 0)
|
|
{
|
|
PAIR(US, UE, "us", "ue");
|
|
PAIR(VB_s, VB_e, "VB", "Vb");
|
|
|
|
/* get italics, or have it default to underline */
|
|
PAIR(AS, AE, "as", "ae");
|
|
if (!*AS)
|
|
{
|
|
AS = US;
|
|
AE = UE;
|
|
}
|
|
}
|
|
MAYHAVE(AL, "al");
|
|
MAYHAVE(DL, "dl");
|
|
MUSTHAVE(CE, "ce");
|
|
MUSTHAVE(CL, "cl");
|
|
MAYHAVE(CD, "cd");
|
|
MAYHAVE(SR, "sr");
|
|
PAIR(IM, EI, "im", "ei");
|
|
MAYHAVE(IC, "ic");
|
|
MAYHAVE(DC, "dc");
|
|
|
|
/* other termcap stuff */
|
|
AM = tgetflag("am");
|
|
PT = tgetflag("pt");
|
|
getsize(0);
|
|
|
|
/* Key sequences */
|
|
MAYHAVE(KU, "ku");
|
|
MAYHAVE(KD, "kd");
|
|
MAYHAVE(KL, "kl");
|
|
MAYHAVE(KR, "kr");
|
|
MAYHAVE(PU, "PU");
|
|
MAYHAVE(PD, "PD");
|
|
MAYHAVE(HM, "HM");
|
|
MAYHAVE(EN, "EN");
|
|
|
|
#undef MUSTHAVE
|
|
#undef MAYHAVE
|
|
#undef PAIR
|
|
}
|
|
|
|
|
|
/* This function gets the window size. It uses the TIOCGWINSZ ioctl call if
|
|
* your system has it, or tgetnum("li") and tgetnum("co") if it doesn't.
|
|
* This function is called once during initialization, and thereafter it is
|
|
* called whenever the SIGWINCH signal is sent to this process.
|
|
*/
|
|
getsize(signo)
|
|
int signo;
|
|
{
|
|
#ifdef TIOCGWINSZ
|
|
struct winsize size;
|
|
int lines;
|
|
int cols;
|
|
#endif
|
|
|
|
#ifdef SIGWINCH
|
|
/* reset the signal vector */
|
|
signal(SIGWINCH, getsize);
|
|
#endif
|
|
|
|
/* get the window size, one way or another. */
|
|
#ifdef TIOCGWINSZ
|
|
lines = cols = 0;
|
|
if (ioctl(2, TIOCGWINSZ, &size) >= 0)
|
|
{
|
|
lines = size.ws_row;
|
|
cols = size.ws_col;
|
|
}
|
|
if ((lines == 0 || cols == 0) && signo == 0)
|
|
{
|
|
LINES = tgetnum("li");
|
|
COLS = tgetnum("co");
|
|
}
|
|
if (lines >= 2 && cols >= 30)
|
|
{
|
|
LINES = lines;
|
|
COLS = cols;
|
|
}
|
|
#else
|
|
LINES = tgetnum("li");
|
|
COLS = tgetnum("co");
|
|
#endif
|
|
|
|
/* Make sure we got values that we can live with */
|
|
if (LINES < 2 || COLS < 30)
|
|
{
|
|
write(2, "Screen too small\n", 17);
|
|
endwin();
|
|
exit(2);
|
|
}
|
|
|
|
/* !!! copy the new values into the corresponding elvis options */
|
|
{
|
|
extern char o_columns[], o_lines[];
|
|
|
|
*o_columns = COLS;
|
|
*o_lines = LINES;
|
|
}
|
|
}
|
|
|
|
|
|
/* This is a function version of addch() -- it is used by tputs() */
|
|
int faddch(ch)
|
|
int ch;
|
|
{
|
|
addch(ch);
|
|
}
|
|
|
|
#ifdef CRUNCH
|
|
/* These functions are equivelent to the macros of the same names... */
|
|
|
|
void qaddstr(str)
|
|
char *str;
|
|
{
|
|
register char *s_, *d_;
|
|
|
|
for (s_=(str), d_=stdscr; *d_++ = *s_++; )
|
|
{
|
|
}
|
|
stdscr = d_ - 1;
|
|
}
|
|
|
|
void attrset(a)
|
|
int a;
|
|
{
|
|
tputs(aend, 1, faddch);
|
|
if (a == A_BOLD)
|
|
{
|
|
tputs(VB_s, 1, faddch);
|
|
aend = VB_e;
|
|
}
|
|
else if (a == A_UNDERLINE)
|
|
{
|
|
tputs(US, 1, faddch);
|
|
aend = UE;
|
|
}
|
|
else if (a == A_ALTCHARSET)
|
|
{
|
|
tputs(AS, 1, faddch);
|
|
aend = AE;
|
|
}
|
|
else
|
|
{
|
|
aend = "";
|
|
}
|
|
}
|
|
|
|
|
|
void insch(ch)
|
|
int ch;
|
|
{
|
|
if (IM)
|
|
tputs(IM, 1, faddch);
|
|
tputs(IC, 1, faddch);
|
|
qaddch(ch);
|
|
if (EI)
|
|
tputs(EI, 1, faddch);
|
|
}
|
|
#endif
|