#ifdef UNIX
/*
 * Intelligent session manager using curses.  Options include ANSI X3.64
 * emulation with both VTx00 and PC graphics.
 *
 * Brandon S. Allbery KF8NH
 * No copyright, copyleft, copysideways, copytwisted, ....
 */

#include <sys/types.h>
#include <errno.h>
#include <sys/param.h>
#include "system.h"
/*lint -save -e659 */
#ifdef HAVE_NCURSES_H
#include <ncurses.h>
#else
#include <curses.h>
#endif
/*lint -restore */
#ifndef MSDOS
#include <term.h>
#endif
#undef FALSE
#undef TRUE

#ifndef _lint
static char rcsid[] OPTIONAL = "$Id: curses.c,v 1.21 2000/05/09 16:48:25 brian Exp $";
#endif

#if 0 /* unless ur using *really* old ncurses libs - pre 1.9 or something */

# ifndef	wbkgdset
#  define wbkgdset(w,attr) ((w)->_bkgd = (attr))
# endif
#endif



# ifndef getattrs
#  define getattrs(w) ((w)->_attrs)
# endif


#ifdef memcpy
#undef memcpy
#endif

#include "global.h"
#include "hardware.h"
#include "proc.h"
#undef tputs
#include "tty.h"
/*lint -e123 */
#include "sessmgr.h"

/* This nonsense WILL go away... */
#ifdef NO_CURSES_TMARG
# define _tmarg _regtop
# define _bmarg _regbottom
#endif

#ifdef SM_CURSES

#ifdef SCREENSAVER
extern void sskick (void);
#endif

extern struct sessmgr_sw curses_sessmgr; /* forward declaration */

static int Suspense, initted;
#ifdef RAW_SESSMGR
static TERMINAL *my_term;
#endif
static void *curscreen;
extern int Numrows, Numcols;
extern int Keyboard;
extern int SYSback, SYSfore;

#ifdef PATCURSES /* VE1TTL */

static void curses_clrscr (const struct sessmgr_sw *sm OPTIONAL, void *dp);

short init_pairs(void);

short STATback = COLOR_BLUE;	/* statline colors */

short STATfore = COLOR_WHITE;

short SPLITfore = COLOR_WHITE;	/* chatline colors */

short SPLITback = COLOR_BLUE;

static chtype colorsplit;

static chtype colorstat;

static short pair_main;  /* holds current fg/bg colorpair */

static short colors[8] =

{

	COLOR_BLACK,

	COLOR_RED,

	COLOR_GREEN,

	COLOR_YELLOW,

	COLOR_BLUE,

	COLOR_MAGENTA,

	COLOR_CYAN,

	COLOR_WHITE

};

#endif /* PATCURSES */


#ifndef PATCURSES

/* colors - this is a mess */
static int *color_pair_map;
#endif


struct keytrie
{
    enum {KT_DEF, KT_TRIE, KT_TVAL, KT_VAL} kt_type; /* type of entry */
    int kt_tval;		/* if a TVAL, the timed-out value */
    union
    {
	struct keytrie *ktu_trie; /* sub-trie */
#define kt_trie kt_u.ktu_trie
	int ktu_val;		/* value */
#define kt_val kt_u.ktu_val
    } kt_u;
};


struct curses_data
{
    WINDOW *win;
    struct
    {
	int x, y, tmarg, bmarg, valid;
	chtype attr, bkgrnd;
    } save;
    char parm[8];
    char nparm;
#ifdef PATCURSES

    attr_t s_attrs;		/* current attribute */

    chtype s_bkgd;		/* current background char/attrib pair */ 

#endif

    char bg;			/* current background */
    char fg;			/* current foreground */
    char flags;
#define C_ESC		0x01
#define C_CSI		0x02
#define C_INS		0x04
#define C_TTY		0x08
#define C_BOLD		0x10
};


static struct keytrie *keys;

#ifndef PATCURSES

/* mapping of IBM line-drawing characters to curses line drawing */
/* this is evil: it doesn't use e.g. ACS_HLINE because that's an array ref */
static chtype ibm_map[128] =
{
     0,   0,   0,   0,   0,   0,   0,   0,
     0,   0,   0,   0,   0,   0,   0,   0,
     0,   0,   0,   0,   0,   0,   0,   0,
     0,   0,   0,   0,   0,   0,   0,   0,
     0,   0,   0,   0,   0,   0,   0,   0,
     0,   0,   0,   0,   0,   0,   0,   0,
    'a', 'a', 'a', 'x', 'u', 'u', 'u', 'k',
    'k', 'u', 'x', 'k', 'j', 'j', 'j', 'k',
    'm', 'v', 'w', 'u', 'q', 'n', 'u', 'u',
    'j', 'l', 'v', 'w', 'u', 'q', 'n', 'v',
    'v', 'w', 'w', 'm', 'm', 'l', 'l', 'n',
    'n', 'j', 'l',  0,   0,   0,   0,   0,
     0,   0,   0,   0,   0,   0,   0,   0,
     0,   0,   0,   0,   0,   0,   0,   0,
     0,   0,   0,   0,   0,   0,   0,   0,
     0,   0,   0,   0,   0,   0,   0,   0,
};
#endif



/*lint -save -e550 */
static int
Getmaxy (WINDOW *w)
{
int y, x;

	getmaxyx(w, y, x);
	return y;
}

static int
Getmaxx (WINDOW *w)
{
int y, x;

	getmaxyx(w, y, x);
	return x;
}
/*lint -restore */



static struct keytrie *
key_init (void)
{
struct keytrie *k;
int i;

	k = mallocw(256 * sizeof *keys);
	for (i = 256; i--; )    {
		k[i].kt_type = KT_DEF;
		k[i].kt_val = i;
	}
	return k;
}



static void
key_add (const char *str, int val)
{
struct keytrie *t;
int c;

	/*
	 * Follow the trie until we get to the right subtrie for the string, then
	 * add the value.  If we hit a KT_DEF, expand the trie.  If we hit a KT_VAL
	 * change it to a KT_TVAL, which times out as a KT_VAL but continues as a
	 * KT_TRIE.  If given a value for a KT_TRIE, change it to a KT_TVAL.
	 */
	if (!str || !*str)
		return;			/* no key to define */
	t = keys;
	while (str[1])    {
		c = uchar(*str++);
		if (t[c].kt_type != KT_TRIE)	{
			if (t[c].kt_type == KT_DEF)
				t[c].kt_type = KT_TRIE;
			else	    {
				t[c].kt_type = KT_TVAL;
				t[c].kt_tval = t[c].kt_val;
			}
			t[c].kt_trie = key_init();
		}
		t = t[c].kt_trie;
	}
	c = uchar(*str);
	if (t[c].kt_type == KT_TRIE)    {
		t[c].kt_type = KT_TVAL;
		t[c].kt_tval = val;
	} else if (t[c].kt_type == KT_DEF)    {
		t[c].kt_type = KT_VAL;
		t[c].kt_val = val;
	}
}

/*lint -restore */

#ifndef PATCURSES

static int
map_colors(int bg, int fg)
{
int cp, cmask;

	if (!color_pair_map)
		return 0;
#ifdef DEBUGCOLOR
	fprintf (stderr, "map_colors %d %d", bg, fg);
#endif
	cmask = (int) ((unsigned int) bg << 4) | fg;
	for (cp = 0; cp < COLOR_PAIRS; cp++)    {
		if (color_pair_map[cp] == -1 || color_pair_map[cp] == cmask)
			break;
	}

	if (cp == COLOR_PAIRS)	{
#ifdef DEBUGCOLOR
		fprintf(stderr, " - out of pairs!\n");
#endif
		return 0;
	}

	if (color_pair_map[cp] == -1)	{
		if (init_pair((short)(cp + 1), (short) fg, (short) bg) == ERR)	{
#ifdef DEBUGCOLOR
			fprintf(stderr, "can't create pair %d\n", cp);
#endif
			cp = 0;
		} else	{
#ifdef DEBUGCOLOR
			fprintf(stderr, " - new pair %d\n", cp);
#endif
			color_pair_map[cp] = cmask;
		}
	}
#ifdef DEBUGCOLOR
	else
		fprintf(stderr, " is pair %d\n", cp);
#endif
	return ((int) COLOR_PAIR((unsigned int)(cp + 1)));
}
#endif /* PATCURSES */



static int
curses_init(const struct sessmgr_sw *sm OPTIONAL)
{
	if (!isatty(0))
		return 0;
	if (initted)    {
#ifdef RAW_SESSMGR
		set_curterm(my_term);
#endif
		(void) refresh();		/* bring curses back to life */
	} else    {
		(void) initscr();			/* be nice to trap errors... */
		/*
		 * I assume the curses manpage tells the truth when it claims that
		 * colors are initialized to RGB defaults.  If not, I may need to set
		 * up the colors in question...
		 */
		if (has_colors() && start_color() != ERR && COLORS >= 8)	{
#ifdef PATCURSES

			(void) init_pairs(); /* initialize color pairs */

#else

			color_pair_map = mallocw((unsigned int)COLOR_PAIRS * sizeof(int));
			memset(color_pair_map, -1, ((unsigned int) COLOR_PAIRS) * sizeof(int));
			(void) map_colors(SYSback, SYSfore); /* default color pair */
#endif /* PATCURSES */

		}
#ifdef RAW_SESSMGR
		my_term = cur_term;
#endif
		(void) noecho();
		(void) nonl();
		(void) raw();
		keys = key_init();
		key_add(key_down, DNARROW);
		key_add(key_f1, -3);
		key_add(key_f2, -4);
		key_add(key_f3, -5);
		key_add(key_f4, -6);
		key_add(key_f5, -7);
		key_add(key_f6, -8);
		key_add(key_f7, -9);
		key_add(key_f8, -10);
		key_add(key_f9, -11);
		key_add(key_ic, -105);
		key_add(key_dc, -106);
		key_add(key_home, -107);
		key_add(key_end, -108);
		key_add(key_ppage, -109);
		key_add(key_npage, -110);
		key_add(key_f10, -2);
		key_add(key_left, LTARROW);
		key_add(key_right, RTARROW);
		key_add(key_up, UPARROW);
		key_add("\177", '\b');	/* so DEL behaves as BS */
		initted = 1;
	}
	Suspense = 0;
	Numrows = LINES;
	Numcols = COLS;
	return 1;
}

#ifdef PATCURSES

/* _ve1ttl - define pairs (1-8) standard colors, and 9 (main pair),

 * 10 (status pair), 11 (splitline pair).

 * called initially and after any SYSback change for

 * the current screen colors to be updated by ncurses

 */

#define NUMPAIRS	11

short init_pairs(void)

{

	short x;

	for (x = 1; x <= 8 && x < COLOR_PAIRS; x++)

		if (init_pair(x, colors[x - 1], (short) SYSback) == ERR)

			break;

	if (x != 9) {

		fprintf(stderr, "Curses error: only able to initialize to colorpair (%d) !\n", x);

		return 1;

	}

	if (init_pair(x, (short) SYSfore, (short) SYSback) == ERR) {

		fprintf(stderr, "Curses error: init_pair(%d) main color failed !\n"

				"SYSfore=%d, SYSback=%d\n", SYSfore, SYSback, x);

		pair_main = (SYSfore == 7)? 1 : 8;

		x--;

	}

	else

		pair_main = x;

	/* status color */

	if (init_pair(++x, STATfore, STATback) == ERR) {

		fprintf(stderr, "Curses error: init_pair(%d) status color failed !\n"

				"STATfore=%d, STATback=%d\n", STATfore, STATback, x);

		colorstat = (COLOR_PAIR(pair_main) | A_REVERSE);

		x--;

	}

	else

		colorstat = COLOR_PAIR(x);



	/* splitline color */

	if (init_pair(++x, SPLITfore, SPLITback) == ERR) {

		fprintf(stderr, "Curses error: init_pair(%d) splitscreen color failed !\n"

				"SPLITfore=%d, SPLITback=%d\n", SPLITfore, SPLITback, x);

		colorsplit = (COLOR_PAIR(pair_main) | A_REVERSE);

		x--;

	}

	else

		colorsplit = COLOR_PAIR(x);



	return (x == NUMPAIRS)? 0 : x;

}

#endif



static void
curses_end (const struct sessmgr_sw *sm OPTIONAL)
{
	if (!Suspense)
		(void) endwin();
}



static void
curses_suspend (const struct sessmgr_sw *sm OPTIONAL)
{
	if (!Suspense++)    {
		/* note that we use stdscr for this, since it's otherwise unoccupied */
		(void) clear();
		(void) refresh();
		(void) endwin();
	}
}



static void
curses_resume(const struct sessmgr_sw *sm OPTIONAL)
{
	if (!--Suspense)    {
#ifdef RAW_SESSMGR
		set_curterm(my_term);
#endif
 		(void) clearok(((struct curses_data *) curscreen)->win, TRUE);
		(void) wrefresh(((struct curses_data *) curscreen)->win);
		Numrows = LINES;
		Numcols = COLS;
	}
}



/*
 * Options supported:
 *
 * tty, ansi
 *	Select old glass-tty emulation or new ANSI X3.64 emulation
 *
 * bold
 *	Specify style of highlight used
 */

static char *
curses_opts(const struct sessmgr_sw *sm OPTIONAL, void *sp, const char *opts)
{
struct curses_data *cp;
static char buf[1024];
char const *ep;
char const *xp;
size_t l;

	cp = (struct curses_data *) sp;
	if (!opts)    {
		/* return printable version of options */
		buf[0] = '\0';
#ifdef PATCURSES

		/* "ansi" default */

		if (!(cp->flags & C_TTY))

			strcpy (buf, "ansi");

		else

#else

		if (cp->flags & C_TTY)
#endif

			strcpy(buf, "tty");
		if ((cp->flags & (C_TTY|C_BOLD)) == (C_TTY|C_BOLD))
			strcat(buf, ",");
		if (cp->flags & C_BOLD)
			strcat(buf, "bold");
		return buf;
	}

	/* parse and set options */
	xp = opts;
	while (*xp)    {
		while (isspace(*xp) || *xp == ',')
			xp++;
		if (!*xp)
			break;
		l = 0;
		for (ep = xp; *ep && *ep != ',' && !isspace(*ep); ep++)
			l++;
		memcpy(buf, xp, l);
		buf[l] = '\0';
		if (strcasecmp(buf, "tty") == 0)
			cp->flags |= C_TTY;
		else if (strcasecmp(buf, "ansi") == 0)
			cp->flags &= ~C_TTY;
		else if (strcasecmp(buf, "notty") == 0)
			cp->flags &= ~C_TTY;
		else if (strcasecmp(buf, "noansi") == 0)
			cp->flags |= C_TTY;
		else if (strcasecmp(buf, "bold") == 0)
			cp->flags |= C_BOLD;
		else if (strcasecmp(buf, "nobold") == 0)
			cp->flags &= ~C_BOLD;
		else
			tprintf("curses: unknown option \"%s\"\n", buf);
		xp = ep;
	}
	
	return 0;
}



static int
curses_swap (const struct sessmgr_sw *sm OPTIONAL, void *old OPTIONAL, void *new)
{
	if (new)    {
		(void) clearok(((struct curses_data *) new)->win, TRUE);
		(void) touchwin(((struct curses_data *) new)->win);
		curscreen = new;
	}
	return 1;			/* can always run concurrent output */
}



static void
wiach (WINDOW *win, chtype ch, int insert)
{
	if (insert)
		(void) winsch(win, (chtype) ch);
	else
		(void) waddch(win, (chtype) ch);
}



static void
curses_flush (const struct sessmgr_sw *sm OPTIONAL, void *dp)
{
	if (!Suspense && curscreen == dp)
		(void) wrefresh(((struct curses_data *) dp)->win);
}



static void
curses_putch (const struct sessmgr_sw *sm OPTIONAL, void *dp, int c)
{
register struct curses_data *sp;
int x, y, ox, oy;
chtype attrs;

	sp = dp;
	if (c == 7)    {
		write(1, "\7", 1);
		return;
	}

#ifdef SCREENSAVER
	sskick();
#endif

	if (c == '\r' || c == '\b')    {
		(void) waddch(sp->win, (chtype) c);
		return;
	}
	if (c == '\t')    {
		wiach(sp->win, (chtype) c, sp->flags & C_INS);
		return;
	}
#ifdef PATCURSES

	if (c == 12)    {  /* ctrl-l */

		(void) wrefresh(curscr);

		return;

	}

#endif

	if (c == '\n')    {
		/* waddch() does clrtoeol() implicitly.  This breaks us. */
		getyx(sp->win, y, x);
		if (++y > sp->win->_bmarg)	{
			y--;
			(void) scroll(sp->win);
#if 1
			if (Curproc->session->screen->flags & SMS_ACTIVE)
#if 0
				(void) wrefresh (sp->win);
#else
				curses_flush (sm, dp);
#endif
#endif
		}
		(void) wmove(sp->win, y, 0);
		return;
	}
	if (sp->flags & C_TTY)    {
		if (c >= 32 && c < 127)
			wiach(sp->win, (chtype) c, sp->flags & C_INS);
		return;
	}
	if (c == 24 || c == 26)    {
		sp->flags &= ~(C_ESC|C_CSI);
		return;
	}
#ifdef PATCURSES

	if (c == 27 && !(sp->flags & C_TTY))    { 

#else

	if (c == 27)    {
#endif

		sp->flags |= C_ESC;
		return;
	}
	if (c == '\016')    {
		(void) wattron(sp->win, A_ALTCHARSET);
		return;
	}
	if (c == '\017')    {
		(void) wattroff(sp->win, A_ALTCHARSET);
		return;
	}
	if (c < 32)
		return;
#ifdef PATCURSES

	if (!(sp->flags & (C_ESC|C_CSI|C_TTY))) {

		wiach(sp->win, (chtype) c, sp->flags & C_INS);

		return;

	}

#else

	if (!(sp->flags & (C_ESC|C_CSI)))    {
		if (c > 127 && ibm_map[c - 128])
			wiach(sp->win, ibm_map[c - 128] | A_ALTCHARSET, sp->flags & C_INS);
		else if (c < 127)
			wiach(sp->win, (chtype) c, sp->flags & C_INS);
		return;
	}
#endif

	if (sp->flags & C_ESC)    {
		/* we only handle '[' */
		switch (c)	{
			case '[':
			    sp->flags &= ~C_ESC;
			    sp->flags |= C_CSI;
			    sp->nparm = 0;
			    memset(sp->parm, 0, sizeof sp->parm);
			    break;
			case '7':
			    getyx(sp->win, sp->save.y, sp->save.x);
			    sp->save.attr = getattrs(sp->win);
			    sp->save.tmarg = sp->win->_tmarg;
			    sp->save.bmarg = sp->win->_bmarg;
			    sp->save.bkgrnd = sp->win->_bkgd; /* should be a macro for this */
			    sp->save.valid = 1;
			    sp->flags &= ~(C_ESC|C_CSI);
			    break;
			case '8':
			    if (sp->save.valid)	    {
				(void) wmove(sp->win, sp->save.y, sp->save.x);
				wattrset(sp->win, sp->save.attr);
				wbkgdset(sp->win, sp->save.bkgrnd);
#ifdef linux
				(void) werase(sp->win); /* wbkgdset() doesn't do anything */
#endif
				(void) wsetscrreg(sp->win, sp->save.tmarg, sp->save.bmarg);
				sp->save.valid = 0;
			    }
			    sp->flags &= ~(C_ESC|C_CSI);
			    break;
			default:
			    sp->flags &= ~(C_ESC|C_CSI);
			    break;
		}
		return;
	}

	/* handle CSI-sequences */
	switch (c)    {
	    case '0':
	    case '1':
	    case '2':
	    case '3':
	    case '4':
	    case '5':
	    case '6':
	    case '7':
	    case '8':
	    case '9':
		sp->parm[(int)sp->nparm] = (char) (sp->parm[(int)sp->nparm] * 10 + c - '0');
		break;
	    case ';':
#ifdef PATCURSES

		sp->parm[(int)(++sp->nparm)] = 0;

#else

		sp->parm[(int)(sp->nparm++)] = 0;
#endif

		break;
	    case '?':
		/* just ignore it for now */
		break;
	    /* action character */
	    case 'H':
	    case 'f':
		(void) wmove(sp->win, sp->parm[1] - 1, sp->parm[0] - 1);
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 'J':
		switch (sp->parm[0])	{
			case 0:
			    attrs = getattrs(sp->win);
			    wattrset(sp->win, A_NORMAL);
			    (void) wclrtobot(sp->win);
			    wattrset(sp->win, attrs);
			    break;
			case 1:
			    getyx(sp->win, oy, ox);
			    attrs = getattrs(sp->win);
			    wattrset(sp->win, A_NORMAL);
			    for (y = 0; y < oy; y++)	    {
				(void) wmove(sp->win, y, 0);
				for (x = Getmaxx(sp->win); x--; )
				    (void) waddch(sp->win, ' ');
			    }
			    (void) wmove(sp->win, oy, 0);
			    for (x = 0; x < ox; x++)
				(void) waddch(sp->win, ' ');
			    wattrset(sp->win, attrs);
			    break;
			case 2:
#ifdef PATCURSES /* lets not clear everything! */

				curses_clrscr(sm, sp);

#else

			    (void) wclear(sp->win);
#endif

			    break;
			default:
			    break;
		}
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 'K':
		switch (sp->parm[0])	{
			case 0:
			    (void) clrtoeol();
			    break;
			case 1:
			    getyx(sp->win, oy, ox);
			    (void) wmove(sp->win, oy, 0);
			    attrs = getattrs(sp->win);
			    wattrset(sp->win, A_NORMAL);
			    for (x = 0; x < ox; x++)
				(void) waddch(sp->win, ' ');
			    wattrset(sp->win, attrs);
			    break;
			case 2:
			    getyx(sp->win, oy, ox);
			    (void) wmove(sp->win, oy, 0);
			    attrs = getattrs(sp->win);
			    wattrset(sp->win, A_NORMAL);
			    for (x = Getmaxx(sp->win); x--; )
				(void) waddch(sp->win, ' ');
			    (void) wmove(sp->win, oy, ox);
			    wattrset(sp->win, attrs);
			    break;
			default:
			    break;
		}
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 'm':
		sp->flags &= ~(C_ESC|C_CSI);
#ifndef PATCURSES

		if (!sp->nparm)	{
		    (void) wattroff(sp->win, A_BOLD|A_DIM|A_UNDERLINE|A_BLINK|A_REVERSE);
		    break;
		}
		for (x = 0; x < sp->nparm; x++)	{
#else

		for (x = 0; x <= sp->nparm; x++)	{

#endif

		    switch (sp->parm[x])    {
			    case 0:
				(void) wattroff(sp->win, A_BOLD|A_DIM|A_UNDERLINE|A_BLINK|A_REVERSE);
#ifdef PATCURSES

				wattrset(sp->win, COLOR_PAIR(0));

#endif

				break;
			    case 1:
				(void) wattron(sp->win, A_BOLD);
				break;
			    case 2:
				(void) wattron(sp->win, A_DIM);
				break;
			    case 4:
				(void) wattron(sp->win, A_UNDERLINE);
				break;
			    case 5:
				(void) wattron(sp->win, A_BLINK);
				break;
			    case 7:
				(void) wattron(sp->win, A_REVERSE);
				break;
			    case 21:
				(void) wattroff(sp->win, A_BOLD);
				break;
			    case 22:
				(void) wattroff(sp->win, A_DIM);
				break;
			    case 24:
				(void) wattroff(sp->win, A_UNDERLINE);
				break;
			    case 25:
				(void) wattroff(sp->win, A_BLINK);
				break;
			    case 27:
				(void) wattroff(sp->win, A_REVERSE);
				break;
			    /* ANSI color sequences? */


#ifdef PATCURSES

					/* _ve1ttl background == SYSback */

					case 30:

					case 31:

					case 32:

					case 33:

					case 34:

					case 35:

					case 36:

					case 37:

#ifndef NCURSES_VERSION_MAJOR

				wbkgdset(sp->win, COLOR_PAIR((sp->parm[x] - 29)));

#endif

				wattron(sp->win, COLOR_PAIR((sp->parm[x] - 29)));

				break;

#endif

			    default:
			        break;
		    }
		}
		break;
	    case 'A':
		getyx(sp->win, y, x);
		if ((y -= (sp->nparm? sp->parm[0]: 1)) < 0)
		    y = 0;
		(void) wmove(sp->win, y, x);
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 'B':
		getyx(sp->win, y, x);
		if ((y += (sp->nparm? sp->parm[0]: 1)) >= Getmaxy(sp->win))
		    y = Getmaxy(sp->win) - 1;
		(void) wmove(sp->win, y, x);
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 'C':
		getyx(sp->win, y, x);
		if ((x += (sp->nparm? sp->parm[0]: 1)) >= Getmaxx(sp->win))
		    x = Getmaxx(sp->win) - 1;
		(void) wmove(sp->win, y, x);
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 'D':
		getyx(sp->win, y, x);
		if ((x -= (sp->nparm? sp->parm[0]: 1)) < 0)
		    x = 0;
		(void) wmove(sp->win, y, x);
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 'r':
		(void) wsetscrreg(sp->win, (sp->nparm? sp->parm[0] - 1: 0),
			   (sp->nparm > 1? sp->parm[1] - 1: Getmaxy(sp->win) - 1));
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 's':
		getyx(sp->win, sp->save.y, sp->save.x);
		sp->save.attr = getattrs(sp->win);
		sp->save.tmarg = sp->win->_tmarg;
		sp->save.bmarg = sp->win->_bmarg;
		sp->save.bkgrnd = sp->win->_bkgd; /* should be a macro for this */
		sp->save.valid = 1;
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 'u':
		if (sp->save.valid)	{
		    (void) wmove(sp->win, sp->save.y, sp->save.x);
		    wattrset(sp->win, sp->save.attr);
		    wbkgdset(sp->win, sp->save.bkgrnd);
		    (void) wsetscrreg(sp->win, sp->save.tmarg, sp->save.bmarg);
		    sp->save.valid = 0;
		}
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 'L':
		if (!sp->nparm)
		    sp->parm[0] = 1;
		while (sp->parm[0]--)
		    (void) winsertln(sp->win);
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 'M':
		if (!sp->nparm)
		    sp->parm[0] = 1;
		while (sp->parm[0]--)
		    (void) wdeleteln(sp->win);
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case '@':
		if (!sp->nparm)
		    sp->parm[0] = 1;
		while (sp->parm[0]--)
		    (void) winsch(sp->win, ' ');
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 'P':
		if (!sp->nparm)
		    sp->parm[0] = 1;
		while (sp->parm[0]--)
		    (void) wdelch(sp->win);
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    case 'h':
	    case 'l':
		for (x = 0; x < sp->nparm; x++)	{
		    switch (sp->parm[x])    {
			    case 4:
				if (c == 'h')
				    sp->flags |= C_INS;
				else
				    sp->flags &= ~C_INS;
				break;
			    /* insert mode is all we handle right now */
			    default:
			        break;
		    }
		}
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	    default:
		sp->flags &= ~(C_ESC|C_CSI);
		break;
	}
}



static void
curses_clreol (const struct sessmgr_sw *sm OPTIONAL, void *dp)
{
#ifdef SCREENSAVER
	sskick();
#endif
	(void) wclrtoeol(((struct curses_data *) dp)->win);
}



static void
curses_rflush (const struct sessmgr_sw *sm OPTIONAL, void *dp OPTIONAL)
{
	/* stub - later we'll update status here */
}



static void *
curses_create (const struct sessmgr_sw *sm OPTIONAL, struct session *sp OPTIONAL)
{
struct curses_data *ssp;

	ssp = mallocw(sizeof *ssp);
	ssp->win = newwin(LINES, COLS, 0, 0);
	(void) scrollok(ssp->win, TRUE);

	ssp->bg = (char) SYSback;	/* witness help 'em if they're in an xterm */
	ssp->fg = (char) SYSfore;
#ifdef PATCURSES

	(void) wbkgd(ssp->win, COLOR_PAIR(pair_main));

	wattrset(ssp->win, COLOR_PAIR(pair_main));

#else

	(void) wbkgd(ssp->win, COLOR_PAIR(1));
	wattrset(ssp->win, COLOR_PAIR(1));
#endif

	ssp->flags = 0;
	ssp->save.valid = 0;
	curscreen = ssp;
#ifdef PATCURSES

	ssp->s_attrs = ssp->win->_attrs;

	ssp->s_bkgd = ssp->win->_bkgd;

#endif

	return ssp;
}



static void
curses_destroy (const struct sessmgr_sw *sm OPTIONAL, void *dp)
{
	(void) delwin(((struct curses_data *) dp)->win);
	j_free(dp);
}



static void
curses_clrscr (const struct sessmgr_sw *sm OPTIONAL, void *dp)
{
WINDOW *w;
int l;

#ifdef SCREENSAVER
	sskick();
#endif
	w = ((struct curses_data *) dp)->win;

	/* This CANNOT be a simple 'wclear (w)', as that clears
	   the current 'window' AND the rest of the screen. This
	   code properly honors the active 'window'.
	 */
	for (l = w->_tmarg; l <= w->_bmarg; l++)    {
		(void) wmove(w, l, 0);
		(void) wclrtoeol(w);
	}

	(void) wmove(w, w->_tmarg, 0);
}



/*lint -save -e550 */
static int
curses_wherex (const struct sessmgr_sw *sm OPTIONAL, void *dp)
{
int x, y;

	getyx(((struct curses_data *) dp)->win, y, x);
	return x + 1;
}



static int
curses_wherey (const struct sessmgr_sw *sm OPTIONAL, void *dp)
{
WINDOW *w;
int x, y;

	w = ((struct curses_data *) dp)->win;
	getyx(w, y, x);
	return y - w->_tmarg + 1;
}

/*lint -restore */



static void
curses_window (const struct sessmgr_sw *sm OPTIONAL, void *dp, int x1 OPTIONAL,
	int y1, int x2 OPTIONAL, int y2)
{
#ifdef PATCURSES

	/* _ve1ttl, pay attention to where we are and set the appropriate

	 * color attributes.

	 */

register struct curses_data *sp;

	sp = dp;

	y1--; /* top _marg */

	y2--; /* bottom _marg */

	if (sp->win->_tmarg != Numrows-2 && sp->win->_bmarg > 3) {

		/* we're in the main window */

		if (y1 != Numrows-2 && y2 >3)

			return;

		sp->s_attrs = sp->win->_attrs;

		sp->s_bkgd = sp->win->_bkgd;

	}



	(void) wsetscrreg(sp->win, y1, y2);

	(void) wmove(sp->win, y1, 0);



	if (y1 == Numrows-2) { 	

		wbkgdset(sp->win, colorsplit);

		wattrset(sp->win, colorsplit | A_BOLD);

	}	

	else if (y2 > 3) {

		/* we're in the main window, restore saved ch/attrs */

		sp->win->_bkgd = sp->s_bkgd;

		sp->win->_attrs = sp->s_attrs;

	}

	else {  

#ifndef NCURSES_VERSION_MAJOR

		wbkgdset(sp->win, colorstat);

#endif

		wattrset(sp->win, colorstat | A_BOLD);

	}

	return;

#else

WINDOW *w;

	w = ((struct curses_data *) dp)->win;
	(void) wsetscrreg(w, y1 - 1, y2 - 1);
	/* ttydriv() assumes the cursor is placed in this window somewhere */
	(void) wmove(w, y1 - 1, 0);
#endif /* PATCURSES */

}



static void
curses_gotoxy (const struct sessmgr_sw *sm OPTIONAL, void *dp, int x, int y)
{
WINDOW *w;

	w = ((struct curses_data *) dp)->win;
	(void) wmove(w, y + w->_tmarg - 1, x - 1);
}



static void
curses_high (const struct sessmgr_sw *sm OPTIONAL, void *dp)
{
	(void) wattron(((struct curses_data *) dp)->win,
	    (unsigned)((((struct curses_data *) dp)->flags & C_BOLD)? A_BOLD: A_REVERSE));
}

static void
curses_norm (const struct sessmgr_sw *sm OPTIONAL, void *dp)
{
	(void) wattroff(((struct curses_data *) dp)->win,
	     (((struct curses_data *) dp)->flags & C_BOLD)? A_BOLD: A_REVERSE);
}



static void
curses_txtattr (const struct sessmgr_sw *sm OPTIONAL, void *dp, int color)
{
#ifdef PATCURSES

	(void) init_pair (pair_main, color & 0x0f, ((unsigned int) color >> 4) & 0x0f);

	wattrset (((struct curses_data *) dp)->win, COLOR_PAIR(pair_main));

#else

	(void) init_pair (1, color & 0x0f, ((unsigned int) color >> 4) & 0x0f);
	wattrset (((struct curses_data *) dp)->win, COLOR_PAIR(1));
#endif

}



static void
curses_refresh (const struct sessmgr_sw *sm OPTIONAL, void *dp)
{
	(void) clearok(((struct curses_data *) dp)->win, TRUE);
	(void) wrefresh (((struct curses_data *) dp)->win);
}



static void
curses_bground (const struct sessmgr_sw *sm OPTIONAL, void *dp, int color)
{
	((struct curses_data *) dp)->bg = (char) color;
	curses_txtattr (sm, dp, ((int) (((unsigned int)color) << 4) | (int)((struct curses_data *) dp)->fg));
#ifdef PATCURSES

	(void) wbkgd (((struct curses_data *) dp)->win, COLOR_PAIR(pair_main));

#else

	(void) wbkgd (((struct curses_data *) dp)->win, COLOR_PAIR(1));
#endif

}


static void
curses_fground (const struct sessmgr_sw *sm OPTIONAL, void *dp, int color)
{
	((struct curses_data *) dp)->fg = (char) color;
#ifdef PATCURSES

	wattrset (((struct curses_data *) dp)->win, COLOR_PAIR((color <= 7)? (short) color+1 : pair_main ));

#else

	curses_txtattr (sm, dp, (int) ((int) (((unsigned int) (int) ((struct curses_data *) dp)->bg) << 4) | color));
	(void) wbkgd (((struct curses_data *) dp)->win, COLOR_PAIR(1));
#endif

}



static void
curses_cursor (const struct sessmgr_sw *sm OPTIONAL, void *dp OPTIONAL, int c)
{
	(void) curs_set(c);
}



static int
kbchar(int ir)
{
unsigned char ch;
int i;

	if (ir)
		kalarm(500);
	do    {
		if (kwait(&Keyboard) != 0 && ir)
		    return -1;
	}
	while ((i = read(0, &ch, 1)) == 0 || (i == -1 && errno == EWOULDBLOCK))
		;
	kalarm(0);
	if (i < 0)    {
		tprintf("NOS PANIC: Lost keyboard\n");
		where_outta_here(1, "kbchar");
	}
	return ch;
}



/*
 * This remains incorrect.  The keyboard process must be recreated as part of
 * the session manager instead of being independent; this process must be
 * capable of distributing input from several sources, including the real
 * keyboard and external sessions.
 */

static int
curses_kbread(const struct sessmgr_sw *sm OPTIONAL, void *dp OPTIONAL)
{
static int ungets[10];
struct keytrie *t;
static int unget = 0;
int theungetc[10];
int c, i, u;

	i = 0;
	u = 0;
	if (unget > i)
		c = ungets[i++];	/*lint !e727 */
	else
		c = kbchar(0);
	theungetc[u++] = c;
	t = keys;
	while (t[c].kt_type == KT_TRIE || t[c].kt_type == KT_TVAL)    {
		t = t[c].kt_trie;
		if (unget > i)
		    c = ungets[i++];
		else
		    c = kbchar(1);
		if (c == -1)
		    break;
		theungetc[u++] = c;
	}

	if (t[c].kt_type == KT_VAL)    {
		u = 0;
		c = t[c].kt_val;
	} else if (t[c].kt_type == KT_TVAL)   {
		u = 0;
		c = t[c].kt_tval;
	}

	while (i < unget)
		theungetc[u++] = ungets[i++];
	if (u)    {
		c = theungetc[0];
		for (i = u; i; i--)
		    ungets[i - 1] = theungetc[i];
		unget = u - 1;
	}
	return c;
}



struct sessmgr_sw curses_sessmgr =
{
	"curses",
	SM_SPLIT|SM_STDIO,
	curses_init,
	(char *(*)(const struct sessmgr_sw *, char *)) 0,
	curses_create,
	curses_opts,
	curses_swap,
	curses_putch,
	curses_clreol,
	curses_clrscr,
	curses_wherex,
	curses_wherey,
	curses_window,
	curses_gotoxy,
	curses_high,
	curses_norm,
	curses_bground,
	curses_fground,
	curses_txtattr,
	curses_refresh,
	curses_cursor,
	curses_kbread,
	curses_destroy,
	0,
	curses_rflush,
	curses_flush,
	curses_suspend,
	curses_resume,
	curses_end,
	0
};

#endif

#endif		/* UNIX */
