DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T s

⟦d76ccbd4b⟧ TextFile

    Length: 46584 (0xb5f8)
    Types: TextFile
    Names: »screen.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/General/Cubes/screen.c« 

TextFile

/*	vi:set sw=4 ts=4: */
#ifndef	lint
static char	sccsid[] = "@(#)screen.c 5.1 (G.M. Paris) 89/01/22";
#endif	lint

/*
**
**	cubes 5.1  Copyright 1989 Gregory M. Paris
**		Permission granted to redistribute on a no charge basis.
**		All other rights are reserved.
**
*/

/*
XXX	Curses is too much overhead.  I've tried to limit the problem with
XXX	a bunch of booleans, but the real solution is to scrap curses and
XXX	write a special purpose screen driver.  Someday...
*/

/*
XXX	Note that the value of DISPLEN in cubes.h is determined by this
XXX	code though it is not referenced here.
*/

#include	<stdio.h>
#include	<curses.h>
#include	<sys/types.h>
#include	<sys/time.h>
#include	<sys/ioctl.h>
#include	<signal.h>
#include	<setjmp.h>
#include	<strings.h>
#include	<ctype.h>
#include	"cubes.h"

#define		DICEROW			2				/* top line for dice */
#define		DICECOL			0				/* dice begin at left edge */
#define		DIELINES		5				/* height of a single die */
#define		DIEWID		(wide==True?16:10)	/* width of a single die */
WINDOW	   *die[NDICE];						/* subwindow for each die */

#define		ALLDICEWID		(NDICE*DIEWID)	/* width of all dice together */

#define		DNUMROW			(DICEROW-2)		/* dice numbers at top edge */
#define		DNUMCOL			DICECOL			/* dnumwin aligns with dice */
#define		DNUMLINES		1				/* one line of dice numbers */
#define		DNUMWID			ALLDICEWID		/* dnumwin aligns with dice */
WINDOW	   *dnumwin;						/* dice number subwindow */

#define		DFACEROW		(DICEROW-1)		/* die face names above dice */
#define		DFACECOL		DICECOL			/* dfacewin aligns with dice */
#define		DFACELINES		1				/* one line of die face names */
#define		DFACEWID		ALLDICEWID		/* dfacewin aligns with dice */
WINDOW	   *dfacewin;						/* die face names subwindow */

#define		DSTATROW	(DICEROW+DIELINES)	/* top of dstat subwindow */
#define		DSTATCOL		DICECOL			/* first col of dstat subwindow */
#define		DSTATLINES		5				/* height of dstat subwindow */
#define		DSTATWID		ALLDICEWID		/* width of dstat subwindow */
WINDOW	   *dstatwin;						/* dstat subwindow */

/*
**	There's an unused window at line 12.  Extends to scorewin.
*/

#define		INFOROW	(DSTATROW+DSTATLINES+1)	/* where info messages appear */
#define		INFOCOL			0				/* where info messages appear */
#define		INFOLINES		1				/* lines in subwindow */
#define		INFOWID			0				/* extends to edge of screen */
WINDOW	   *infowin;						/* informational subwindow */

#define		PROMPTROW		(INFOROW+1)		/* where prompt appears */
#define		PROMPTCOL		0				/* where prompt appears */
#define		PROMPTLINES		1				/* one line of prompt */
#define		PROMPTWID		0				/* extends to edge */
WINDOW	   *promptwin;						/* prompt subwindow */

#define		HELPROW			(LINES-9)		/* where help subwindow begins */
#define		HELPCOL			0				/* where help subwindow begins */
#define		HELPLINES		0				/* extends to edge of screen */
#define		HELPWID			0				/* extends to edge of screen */
WINDOW	   *helpwin;						/* help message subwindow */

#define		SCOREROW		0				/* where score subwindow begins */
#define		SCORECOL		(ALLDICEWID+1)	/* where score subwindow begins */
#define		SCORELINES		(PLAYERS+3)		/* XXX: PLAYERS must be <= 10 */
#define		SCOREWID		0				/* extends to edge of screen */
WINDOW	   *scorewin;						/* player scores subwindow */

#define		CLEARDIE		(JOKER+1)		/* symbolic for cleared die */
#define		FACES			(2*(CLEARDIE+1))/* number of die faces */

/*
**	Symbolics for drawing characters.  Values are for DEC.
*/
#define	Dot		'`'
#define	Vert	'x'
#define	Horiz	'q'
#define	UpLeft	'l'
#define	UpRite	'k'
#define	LoLeft	'm'
#define	LoRite	'j'

struct tchars			tty_ch;				/* tty characters */
struct ltchars			tty_lt;				/* local tty characters */
static char			   *AS, *AE;			/* alternate (graphics) */
graphtype				graphics = Nographics;	/* terminal has no graphics */
extern cstat			mystat;				/* current playing status */
extern boolean			quiet;				/* in quiet mode (background) */
static boolean			drawhdr	= True;		/* force redraw of score header */
static boolean			drawnum	= True;		/* force redraw of dice numbers */
static boolean			infoclr	= True;		/* infowin is clear */
static boolean			defhelp	= False;	/* default help is showing */
static boolean			wide	= False;	/* wide terminal (110+ x 24) */
static boolean			sqndr	= False;	/* displaying p_squander vals */
static int				mypgrp;				/* process group */
static int				currup	= -1;		/* current player up */
static int				lastup	= -1;		/* last player up */
static char	dieface[FACES][DIELINES][16];	/* die faces for this term */

static int				refdummy;
#define	REFRESH()		(refdummy = (quiet == False) ? refresh() : 0)
#define	WREFRESH(win)	(refdummy = (quiet == False) ? wrefresh(win) : 0)

extern short			ospeed;
#define	SLOW			B1200				/* slow data path */

extern boolean			jokermode;
extern boolean			inprogress;
extern int				winscore;
extern int				mypnum;
extern int				turnnum;
extern int				nobs;
extern boolean			gotintr;
extern player			plr[];
extern diceset			dice;
static int				outc();
static int				suspend();
static int				unsuspend();
static int				sigttin();
static boolean			inbackground();
extern int				redraw();
extern int				mybeep();
static char			   *facename();
static char			   *combname();
extern char			   *tgetstr();
extern char			   *getenv();

/*
**	faces: die faces for each die using DEC line drawing characters
*/
static char	   *faces[][DIELINES]	= {
/*B*/	{ "lqqqqqqqk","x       x","x       x","x       x","mqqqqqqqj" },
/*B*/	{ "lqqqqqqqk","x       x","x       x","x       x","mqqqqqqqj" },
/*A*/	{ "lqqqqqqqk","x       x","x   `   x","x       x","mqqqqqqqj" },
/*A*/	{ "lqqqqqqqk","x       x","x   `   x","x       x","mqqqqqqqj" },
/*2*/	{ "lqqqqqqqk","x `     x","x       x","x     ` x","mqqqqqqqj" },
/*2*/	{ "lqqqqqqqk","x     ` x","x       x","x `     x","mqqqqqqqj" },
/*3*/	{ "lqqqqqqqk","x     ` x","x   `   x","x `     x","mqqqqqqqj" },
/*3*/	{ "lqqqqqqqk","x `     x","x   `   x","x     ` x","mqqqqqqqj" },
/*4*/	{ "lqqqqqqqk","x `   ` x","x       x","x `   ` x","mqqqqqqqj" },
/*4*/	{ "lqqqqqqqk","x `   ` x","x       x","x `   ` x","mqqqqqqqj" },
/*F*/	{ "lqqqqqqqk","x `   ` x","x   `   x","x `   ` x","mqqqqqqqj" },
/*F*/	{ "lqqqqqqqk","x `   ` x","x   `   x","x `   ` x","mqqqqqqqj" },
/*6*/	{ "lqqqqqqqk","x `   ` x","x `   ` x","x `   ` x","mqqqqqqqj" },
/*6*/	{ "lqqqqqqqk","x ` ` ` x","x       x","x ` ` ` x","mqqqqqqqj" },
/*7*/	{ "lqqqqqqqk","x   ` ` x","x ` ` ` x","x ` `   x","mqqqqqqqj" },
/*7*/	{ "lqqqqqqqk","x ` `   x","x ` ` ` x","x   ` ` x","mqqqqqqqj" },
/*8*/	{ "lqqqqqqqk","x ` ` ` x","x `   ` x","x ` ` ` x","mqqqqqqqj" },
/*8*/	{ "lqqqqqqqk","x ` ` ` x","x `   ` x","x ` ` ` x","mqqqqqqqj" },
/*J*/	{ "lqqqqqqqk","x lqqqk x","x x   x x","x mqqqj x","mqqqqqqqj" },
/*J*/	{ "lqqqqqqqk","x lqqqk x","x x   x x","x mqqqj x","mqqqqqqqj" },
/*B*/	{ "         ","         ","         ","         ","         " },
/*B*/	{ "         ","         ","         ","         ","         " },
};

/*
**	wfaces: wide die faces for each die using DEC line drawing characters
*/
static char	   *wfaces[][DIELINES]	= {
/*B*/	{ " lqqqqqqqqqqqk"," x           x"," x           x"," x           x"," mqqqqqqqqqqqj" },
/*B*/	{ " lqqqqqqqqqqqk"," x           x"," x           x"," x           x"," mqqqqqqqqqqqj" },
/*A*/	{ " lqqqqqqqqqqqk"," x           x"," x     `     x"," x           x"," mqqqqqqqqqqqj" },
/*A*/	{ " lqqqqqqqqqqqk"," x           x"," x     `     x"," x           x"," mqqqqqqqqqqqj" },
/*2*/	{ " lqqqqqqqqqqqk"," x  `        x"," x           x"," x        `  x"," mqqqqqqqqqqqj" },
/*2*/	{ " lqqqqqqqqqqqk"," x        `  x"," x           x"," x  `        x"," mqqqqqqqqqqqj" },
/*3*/	{ " lqqqqqqqqqqqk"," x        `  x"," x     `     x"," x  `        x"," mqqqqqqqqqqqj" },
/*3*/	{ " lqqqqqqqqqqqk"," x  `        x"," x     `     x"," x        `  x"," mqqqqqqqqqqqj" },
/*4*/	{ " lqqqqqqqqqqqk"," x  `     `  x"," x           x"," x  `     `  x"," mqqqqqqqqqqqj" },
/*4*/	{ " lqqqqqqqqqqqk"," x  `     `  x"," x           x"," x  `     `  x"," mqqqqqqqqqqqj" },
/*F*/	{ " lqqqqqqqqqqqk"," x  `     `  x"," x     `     x"," x  `     `  x"," mqqqqqqqqqqqj" },
/*F*/	{ " lqqqqqqqqqqqk"," x  `     `  x"," x     `     x"," x  `     `  x"," mqqqqqqqqqqqj" },
/*6*/	{ " lqqqqqqqqqqqk"," x  `     `  x"," x  `     `  x"," x  `     `  x"," mqqqqqqqqqqqj" },
/*6*/	{ " lqqqqqqqqqqqk"," x  `  `  `  x"," x           x"," x  `  `  `  x"," mqqqqqqqqqqqj" },
/*7*/	{ " lqqqqqqqqqqqk"," x     `  `  x"," x  `  `  `  x"," x  `  `     x"," mqqqqqqqqqqqj" },
/*7*/	{ " lqqqqqqqqqqqk"," x  `  `     x"," x  `  `  `  x"," x     `  `  x"," mqqqqqqqqqqqj" },
/*8*/	{ " lqqqqqqqqqqqk"," x  `  `  `  x"," x  `     `  x"," x  `  `  `  x"," mqqqqqqqqqqqj" },
/*8*/	{ " lqqqqqqqqqqqk"," x  `  `  `  x"," x  `     `  x"," x  `  `  `  x"," mqqqqqqqqqqqj" },
/*J*/	{ " lqqqqqqqqqqqk"," x  lqqqqqk  x"," x  x     x  x"," x  mqqqqqj  x"," mqqqqqqqqqqqj" },
/*J*/	{ " lqqqqqqqqqqqk"," x  lqqqqqk  x"," x  x     x  x"," x  mqqqqqj  x"," mqqqqqqqqqqqj" },
/*B*/	{ "              ","              ","              ","              ","              " },
/*B*/	{ "              ","              ","              ","              ","              " },
};

/*
**	initdieface: initialize dieface array
*/
initdieface()
{
	register int	line, face;
	register char  *s;

	for(face = 0;face < FACES;++face) {
		for(line = 0;line < DIELINES;++line) {
			s = &dieface[face][line][0];
			strcpy(s, (wide==True ? wfaces : faces)[face][line]);
			if(graphics == Digital)
				continue;
			if(graphics == Zenith) {
				for(;*s != '\0';++s) {
					switch(*s) {
					case Dot:    *s = '^'; break;
					case Vert:   *s = '`'; break;
					case Horiz:  *s = 'a'; break;
					case UpLeft: *s = 'f'; break;
					case UpRite: *s = 'c'; break;
					case LoLeft: *s = 'e'; break;
					case LoRite: *s = 'd'; break;
					default: break;
					}
				}
				continue;
			}
			for(;*s != '\0';++s) {		/* Nographics */
				switch(*s) {
				case Dot:    *s = '*'; break;
				case Vert:   *s = '|'; break;
				case Horiz:  *s = '-'; break;
				case UpLeft: *s = '.'; break;
				case UpRite: *s = '.'; break;
				case LoLeft: *s = '`'; break;
				case LoRite: *s ='\''; break;
				default: break;
				}
			}
		}
	}
}

/*
**	interrupt: handle interrupt signal
*/
static int
interrupt()
{
	gotintr = True;
}

/*
**	killed: handle terminate signal
*/
static int
killed(sig)
{
	cleanup(sig|0x10, "killed");
}

/*
**	reallyquit: when prudent, ask player if they really want to quit
*/
reallyquit()
{
	char   *mesg;
	char	ans[256];

	gotintr = False;
	switch(mystat) {
	case Spider:
		mesg = "Later, web head...";
		break;
	case Watching:
		mesg = "Ta ta, benchwarmer...";
		break;
	case Waiting:
		mesg = "Sorry you couldn't wait...";
		break;
	default:
		if(prompt("Do you really want to quit? [ny]", ans, True) <= 0)
			return;
		if(ans[0] != 'y' && ans[0] != 'Y')
			return;
		mesg = "So long, quitter...";
		break;
	}

	(void) infomesg(mesg);
	sprintf(ans, "%cq", ASYNCMARK);		/* async quit command */
	respond(ans);						/* hope server's not jammed */
	sleep(1);
	cleanup(1, "");
}

/*
**	startup: initialize the screen
*/
startup()
{
	register int	d;
	static char		capbuf[256];		/* room for as, ae, and Cg */
	char		   *pcap = &capbuf[0];
	char		   *term, *Cg;

	if(inbackground() == True) {
		fprintf(stderr, "cubes: can't run in background\n");
		exit(1);
	}

	/*
	**	Call setterm explicitly because initscr doesn't return ERR
	**	if the terminal type doesn't exist.  How stupid.
	*/
	if((term = getenv("TERM")) == 0) {
		fprintf(stderr, "cubes: TERM not set, sorry\n");
		exit(1);
	}
	if(setterm(term) == ERR) {
		fprintf(stderr, "cubes: no termcap entry for %s\n", term);
		exit(1);
	}
	if(initscr() == ERR) {
		fprintf(stderr, "cubes: can't initialize screen\n");
		exit(1);
	}

	/*
	**	Check for goofy aspect ratio.
	*/
	if(LINES == 24 && COLS > 110)
		wide = True;

#ifdef	LONGNAMEBUG
	/*
	**	If this system has the longname bug, then we must re-get the
	**	terminal description because the longname call in setterm
	**	blows away the buffer.  Pretty nice, eh?
	*/
	{
		static char	termbuf[1024];

		if(tgetent(termbuf, term) != 1)
			cleanup(1, "tgetent failed");
	}
#endif	LONGNAMEBUG

	/*
	**	If graphics type is unspecified we look in the terminal's
	**	termcap entry to see if it has a Cg (Cubes graphics) entry.
	**	If so, and if it's valid, we use that value.
	*/
	if(graphics == Nographics) {
		if((Cg = tgetstr("Cg", &pcap)) != 0) {
			switch(*Cg) {
			case 'd': case 'D': graphics = Digital; break;
			case 'r': case 'R': graphics = Digital; break;	/* no ReGIS */
			case 'z': case 'Z': graphics = Zenith; break;
			default: graphics = Nographics; break;
			}
		}
	}

	/*
	**	Subsume SO and SE as necessary for nicer looking dice.
	*/
	switch(graphics) {
	case Digital:
	case Zenith:
		AS = tgetstr("as", &pcap);
		AE = tgetstr("ae", &pcap);
		if(AS == 0 || AE == 0) {
			graphics = Nographics;
			break;
		}
		SO = AS, SE = AE;
		break;
	case Nographics:
		break;
	default:
		cleanup(1, "bad graphics mode");
		break;
	}

	/*
	**	Generate die faces for this terminal.
	*/
	initdieface();

	(void) signal(SIGHUP, SIG_DFL);
	gotintr = False;
	if(signal(SIGINT, SIG_IGN) != SIG_IGN)
		(void) signal(SIGINT, interrupt);
	if(signal(SIGTSTP, SIG_IGN) != SIG_IGN) {
		(void) signal(SIGTSTP, suspend);
		(void) signal(SIGCONT, unsuspend);
		(void) signal(SIGTTIN, sigttin);	/* not supposed to happen */
		(void) signal(SIGTTOU, SIG_IGN);	/* for beeps */
	}

	/*
	**	Need local characters to get reprint character.
	**	If reprint isn't set, pretend it is set to form feed.
	*/
	(void) ioctl(_tty_ch, TIOCGLTC, (char *)&tty_lt);
	if(tty_lt.t_rprntc == '\0')
		tty_lt.t_rprntc = '\f';

	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
		(void) signal(SIGTERM, killed);

	scrollok(stdscr, FALSE);
	leaveok(stdscr, FALSE);
	crmode();
	noecho();
	REFRESH();

	dnumwin = subwin(stdscr, DNUMLINES, DNUMWID, DNUMROW, DNUMCOL);
	if(dnumwin == 0)
		cleanup(1, "can't create dnumwin subwindow");

	dfacewin = subwin(stdscr, DFACELINES, DFACEWID, DFACEROW, DFACECOL);
	if(dfacewin == 0)
		cleanup(1, "can't create dfacewin subwindow");

	for(d = 0;d < NDICE;++d) {
		die[d] = subwin(stdscr, DIELINES, DIEWID, DICEROW, d*DIEWID);
		if(die[d] == 0)
			cleanup(1, "can't create die subwindow");
	}

	dstatwin = subwin(stdscr, DSTATLINES, DSTATWID, DSTATROW, DSTATCOL);
	if(dstatwin == 0)
		cleanup(1, "can't create dstatwin subwindow");

	infowin = subwin(stdscr, INFOLINES, INFOWID, INFOROW, INFOCOL);
	if(infowin == 0)
		cleanup(1, "can't create infowin subwindow");

	promptwin = subwin(stdscr, PROMPTLINES, PROMPTWID, PROMPTROW, PROMPTCOL);
	if(promptwin == 0)
		cleanup(1, "can't create promptwin subwindow");

	scorewin = subwin(stdscr, SCORELINES, SCOREWID, SCOREROW, SCORECOL);
	if(scorewin == 0)
		cleanup(1, "can't create scorewin subwindow");

	helpwin = subwin(stdscr, HELPLINES, HELPWID, HELPROW, HELPCOL);
	if(helpwin == 0)
		cleanup(1, "can't create helpwin subwindow");

	defhelpmesg();	/* initially wrong -- parameters will be corrected later */

	diceinit();
	neutral();
}

/*
**	diceinit: shouldn't be here, but we want to draw the dice right away
*/
diceinit()
{
	register int	d;

	dice.d_mesg[0] = '\0';
	dice.d_pts_dice = 0;
	dice.d_pts_roll = 0;
	dice.d_pts_turn = 0;
	dice.d_pts_max = 0;
	dice.d_rolling = NDICE;
	dice.d_again = True;
	dice.d_best = Nothing;
	for(d = 0;d < NDICE;++d) {
		dice.d_stat[d] = Free;
		dice.d_comb[d] = Nothing;
		dice.d_face[d] = BADFACE;
	}
}

/*
**	cleanup: cleanup the screen and exit
*/
cleanup(ex, message)
int		ex;
char   *message;
{
	if(quiet == False) {
		endwin();
		tputs(CL, LINES, outc);
		if(graphics != Nographics)
			tputs(SE, 1, outc);
	}

	if(*message != '\0')
		fprintf(stderr, "cubes: %s\n", message);
	exit(ex);
}

/*
**	wflush: flush stdout, then wait for it to drain
*/
wflush()
{
	(void) fflush(stdout);
#ifdef	TIOCWAIT
	(void) ioctl(fileno(stdout), TIOCWAIT, (char *)0);
#else	TIOCWAIT
#ifdef	TIOCOUTQ
	{
		long			q;
		struct timeval	tv;

		while(ioctl(fileno(stdout), TIOCOUTQ, (char *)&q) == 0 && q > 0) {
			switch(ospeed) {
			case B1200: q *= 8333L; break;	/* lowest speed */
			case B1800: q *= 5556L; break;
			case B2400: q *= 4167L; break;
			case B4800: q *= 2083L; break;
			case B9600: q *= 1041L; break;
			default:    q *=  521L; break;	/* highest, 19.2 and above */
			}
			tv.tv_sec =  q / 1000000L;	/* seconds */
			tv.tv_usec = q % 1000000L;	/* microseconds */
			(void) select(32, NOSEL, NOSEL, NOSEL, &tv);
		}
	}
#endif	TIOCOUTQ
#endif	TIOCWAIT
}

/*
**	infomesg: display an informational or status message
**		Try to let the player see it...
*/
infomesg(mesg)
char   *mesg;
{
	if(quiet == False)
		(void) fflush(stdout);

	wclear(infowin);
	if(*mesg == '\0')
		infoclr = True;
	else {
		waddstr(infowin, mesg);
		infoclr = False;
	}
	WREFRESH(infowin);
	neutral();

	if(quiet == False)
		wflush();
	return 0;
}

/*
**	clearinfo: clear info message
*/
clearinfo()
{
	wclear(infowin);
	WREFRESH(infowin);
	infoclr = True;
	return 0;
}

/*
**	helpline: structure for constructing help messages
*/
typedef struct {
	char	   *he_fmt;
	int			he_1, he_2, he_3;
} helpline;

/*
**	lshelp: long, standard help message
*/
static helpline	lshelp[]	= {
/*"12345678901234567890123456789012345678901234567890123456789012345678901234567890"*/
{ "| 5o'kind    face * %3d, held dice count        | ** Turn Point Thresholds ** |", P_AOKMULT, },
{ "| 4o'kind    face * %3d                         | To get on board       %5d |", P_4OKMULT, ONBOARD },
{ "| 3o'kind    face * %3d                         | To go off board       %5d |", P_3OKMULT, OFFBOARD },
#ifdef	ASMSTR
{ "| Straight         %4d, %3d using held dice    |-----------------------------|", P_STRAIGHT, P_ASMSTR, },
#else	ASMSTR
{ "| Straight         %4d                         |-----------------------------|", P_STRAIGHT, },
#endif	ASMSTR
{ "| Small Str.       %4d                         | ** Game Point Thresholds ** |", P_SMSTR, },
{ "| One              %4d (face for ones is ten)  | Margin to win by      %5d |", P_ACE, WINMARGIN },
{ "| Five             %4d                         | Total to win          %5d |", P_FIVE, WINSCORE },
#define	lswinslot	lshelp[6].he_2
{ (char *)0, }
};

/*
**	ljhelp: long, joker help message
*/
static helpline	ljhelp[]	= {
/*"12345678901234567890123456789012345678901234567890123456789012345678901234567890"*/
{ "| 5o'kind    face * %3d, held dice count        | ** Turn Point Thresholds ** |", P_AOKMULT, },
{ "| 4o'kind    face * %3d                         | To get on board       %5d |", P_4OKMULT, ONBOARD },
{ "| 3o'kind    face * %3d                         | To go off board       %5d |", P_3OKMULT, OFFBOARD },
#ifdef	ASMSTR
{ "| Straight         %4d, %3d using held dice    |-----------------------------|", P_STRAIGHT, P_ASMSTR, },
#else	ASMSTR
{ "| Straight         %4d                         |-----------------------------|", P_STRAIGHT, },
#endif	ASMSTR
{ "| Small Str.       %4d                         | ** Game Point Thresholds ** |", P_SMSTR, },
{ "| Five, One    %3d, %3d (face for ones is 10)   | Margin to win by      %5d |", P_FIVE, P_ACE, WINMARGIN },
{ "| Joker            %4d (face for jokers is %2d) | Total to win          %5d |", P_JOKER, P_JOKERMULT, WINSCORE },
#define	ljwinslot	ljhelp[6].he_3
{ (char *)0, }
};

/*
**	defhelpmesg: display default help message
*/
defhelpmesg()
{
	register int	row;
	helpline	   *phe;

	wclear(helpwin);

	lswinslot = ljwinslot = winscore;
	phe = (jokermode == True) ? &ljhelp[0] : &lshelp[0];

	for(row = 1;phe->he_fmt != 0;++row, ++phe) {
		wmove(helpwin, row, 0);
		wprintw(helpwin, phe->he_fmt, phe->he_1, phe->he_2, phe->he_3);
	}

	wmove(helpwin, 0, 0);
	wbox(helpwin, 9, 79);	/* XXX: assumes mimimum 24x80 */
	if(graphics != Nographics) {
		wmove(helpwin, 1, 48);
		wvbar(helpwin, row-1);
		wmove(helpwin, row/2, 49);
		whbar(helpwin, 29);
	}

	WREFRESH(helpwin);
	defhelp = True;
}

/*
**	helpmesg: display a help message
*/
helpmesg(mesg)
char   *mesg;
{
	static int	row	= -1;

	if(defhelp == True || row == -1) {
		wclear(helpwin);
		wmove(helpwin, 0, 0);
		wbox(helpwin, 9, 79);	/* XXX: assumes 24x80 */
		defhelp = False;
		row = 1;
	}

	wmove(helpwin, row, 2);
	waddstr(helpwin, mesg);
	++row;

	/*
	**	Peek behind the message and see if this was the last one.
	**	If so, refresh the window.  Set row to -1 to ensure that
	**	the next help message will be printed properly.
	*/
	if(mesg[-1] == ' ') {
		WREFRESH(helpwin);
		row = -1;
	}

	return 0;
}

/*
**	urgjmp, urgent: SIGURG handler
*/
static jmp_buf	urgjmp;
static int
urgent()
{
	(void) signal(SIGALRM, SIG_IGN);	/* ignore alarm */
	mybeep(1);							/* alert the player */
	longjmp(urgjmp, 1);					/* abort read */
}

/*
**	wakeup: beep the terminal and reset the alarm
*/
#define	WAKES	3
static int	wakecount;
static int
wakeup()
{

	if(++wakecount == WAKES) {			/* last warning */
		(void) signal(SIGURG, SIG_IGN);	/* ignore SIGURG */
		mybeep(1);						/* alert the player */
		longjmp(urgjmp, 2);				/* abort read */
	}

	mybeep(wakecount+1);				/* alert the player */
	(void) alarm(READTIMO/WAKES);		/* again */
}

/*
**	tstpjmp, tstp4prompt: SIGTSTP handler for prompt routine
*/
static jmp_buf	tstpjmp;
static int
tstp4prompt()
{
	suspend();							/* invoke normal TSTP handler */
	longjmp(tstpjmp, 1);				/* abort read */
}

/*
**	intjmp, int4prompt: SIGINT handler for prompt routine
*/
static jmp_buf	intjmp;
static int
int4prompt()
{
	longjmp(intjmp, 1);
}

/*
**	flushinput: handle asynchronous input
*/
flushinput()
{
	long	q;
	char	abuf[BUFSIZ];

	/*
	**	Read all pending input and scan it for async commands.
	*/
	while(ioctl(_tty_ch, FIONREAD, (char *)&q) == 0 && q > 0) {
		abuf[0] = ASYNCMARK;	/* marks as asynchronous */
		if(prompt("", abuf+1, True) > 0 && abuf[1] != '\0') {
			if(abuf[1] == 's' || abuf[1] == 'S')
				togglesqndr();
			else
				respond(abuf);
		}
		neutral();
	}
}

/*
**	prompt: print a prompt message and read an answer
**		If we're async, we don't flush pending input
**		and we allow backspace to cancel us.
*/
prompt(mesg, answer, isasync)
char   *mesg, *answer;
boolean	isasync;
{
	register int	c;
	register char  *s;
	long			q;
	char			junk[64];
	int			  (*sigurg)();
	int			  (*sigalrm)();
	int			  (*sigint)();
	int			  (*sigtstp)();

	/*
	**	No answer yet.
	*/
	answer[0] = '\0';

	/*
	**	Don't allow asynchronous prompts in quiet mode.
	*/
	if(quiet == True && isasync == True)
		return 0;

	/*
	**	Save current TSTP signal handler.
	*/
	if((sigtstp = signal(SIGTSTP, SIG_IGN)) != SIG_IGN)
		(void) signal(SIGTSTP, sigtstp);

	/*
	**	Disallow interrupts for now.
	*/
	if((sigint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
		(void) signal(SIGINT, mybeep);	/* eeek -- beeps SIGINT times! */
	
	/*
	**	Truly flush any pending input.
	*/
	if(quiet == False && isasync == False) {
		while(ioctl(_tty_ch, FIONREAD, (char *)&q) == 0 && q > 0) {
			(void) read(_tty_ch, junk, sizeof junk);
			rewind(stdin);
		}
	}

	/*
	**	If server times out on us it sends out of band data, which should
	**	cause us to receive a SIGURG.  Unfortunately, this signal might
	**	not be delivered while the read is pending (depends on implementation
	**	and phase of the moon).  Therefore, we use an alarm to provide
	**	intermediate alerts and a backup timeout.  The SIGALRM and SIGURG
	**	handlers work in tandem (hopefully).
	*/
	sigurg = signal(SIGURG,  SIG_IGN);
	sigalrm = signal(SIGALRM, SIG_IGN);
	if(setjmp(urgjmp) != 0) {
		(void) signal(SIGINT,  sigint);
		(void) signal(SIGALRM, sigalrm);
		(void) signal(SIGURG,  sigurg);
		(void) signal(SIGTSTP, sigtstp);
		wclear(promptwin);
		WREFRESH(promptwin);
		return -2;	/* timeout */
	}

	/*
	**	If we're in an asynchronous prompt, let interrupt cancel.
	*/
	if(isasync == True && sigint != SIG_IGN) {
		(void) signal(SIGINT, int4prompt);
		if(setjmp(intjmp) != 0) {
			(void) alarm(0);
			answer[0] = '\0';
			(void) signal(SIGINT,  sigint);
			(void) signal(SIGALRM, sigalrm);
			(void) signal(SIGURG,  sigurg);
			(void) signal(SIGTSTP, sigtstp);
			mybeep(1);
			wclear(promptwin);
			WREFRESH(promptwin);
			neutral();
			return 0;
		}
	}

	/*
	**	Handle timeout signals.
	*/
	wakecount = 0;
	(void) alarm(0);
	(void) signal(SIGURG,  urgent);
	if(isasync == False)
		(void) signal(SIGALRM, wakeup);		/* wakeup resets alarm */
	else
		(void) signal(SIGALRM, int4prompt);	/* timeout cancels prompt */
	(void) alarm(READTIMO/WAKES);			/* set for first alert */

reprompt:
	/*
	**	Display the prompt (if any) and partial answer (if any).
	**	We hang here if we're in quiet mode (puts cursor in correct place).
	*/
	while(quiet == True)
		pause();
	wmove(promptwin, 0, 0);
	wclear(promptwin);
	if(*mesg != '\0') {
		waddstr(promptwin, mesg);
		waddch(promptwin, ' ');
	}
	for(s = answer;*s != '\0';++s)
		waddch(promptwin, *s);
	WREFRESH(promptwin);

	/*
	**	Loop until we get carriage return or some sort of abort.
	*/
	for(;;) {
		/*
		**	We have a problem here if somebody tries to suspend while being
		**	prompted.  The read call is resumed, so a character gets stolen
		**	from the foreground process.  In order to avoid this, we have
		**	to abort the read by using longjmp.  We do this in the loop
		**	so the appropriate values of s and c get restored.
		**
		**	If we're suspended in an asynchronous prompt, we abort it.
		*/
		if(sigtstp != SIG_IGN) {
			(void) signal(SIGTSTP, tstp4prompt);
			if(setjmp(tstpjmp) != 0 && isasync == True) {
				(void) alarm(0);
				answer[0] = '\0';
				(void) signal(SIGINT,  sigint);
				(void) signal(SIGALRM, sigalrm);
				(void) signal(SIGURG,  sigurg);
				(void) signal(SIGTSTP, sigtstp);
				wclear(promptwin);
				WREFRESH(promptwin);
				neutral();
				return 0;
			}
		}

		/*
		**	If we're in quiet mode, we can't do a read, or we might
		**	steal characters from foreground processes.  Instead, we'll
		**	jump back to reprompt and hang there.  This also causes
		**	the cursor to be positioned properly.
		*/
		if(quiet == True)
			goto reprompt;

		/*
		**	Read input, handling various special characters.
		*/
		c = wgetch(promptwin);
		if(c == EOF || c == '\r' || c == '\n')
			break;
		if(c == _tty.sg_erase) {
			if(--s < answer) {
				s = answer;
				if(isasync == True)
					break;
				mybeep(1);
				continue;
			}
			waddch(promptwin, '\b');
			wdelch(promptwin);
			WREFRESH(promptwin);
			continue;
		}
		if(c == _tty.sg_kill || c == tty_lt.t_werasc) {
			if(isasync == True)
				break;
			answer[0] = '\0';
			goto reprompt;
		}
		if(c == tty_lt.t_rprntc || c == '\f') {
			if(isasync == True && s == answer) {
				wclear(promptwin);
				redraw();
				break;
			}
			redraw();
			touchwin(promptwin);
			WREFRESH(promptwin);
			continue;
		}
		if(iscntrl(c)) {
			mybeep(1);
			continue;
		}
		waddch(promptwin, c);
		WREFRESH(promptwin);
		*s++ = c;
	}
	*s = '\0';
	(void) alarm(0);
	(void) signal(SIGALRM, sigalrm);
	(void) signal(SIGURG,  sigurg);
	(void) signal(SIGTSTP, sigtstp);

	wmove(promptwin, 0, 0);
	wclear(promptwin);
	WREFRESH(promptwin);

	if(infoclr == False)
		clearinfo();

	(void) signal(SIGINT, sigint);

	if(defhelp == False)
		defhelpmesg();

	return (int)(s - answer);
}

/*
**	showobs: show the number of observers
*/
showobs()
{
	register int	c;

	/*
	**	Find the last active player.
	*/
	for(c = PLAYERS-1;c > 0;--c)
		if(plr[c].p_stat != Inactive)
			break;
	if(c++ < 0)
		return;		/* huh? */

	/*
	**	Update observer(s) display.  We place it beyond the last player.
	*/
	if(nobs > 0) {
		wmove(scorewin, c+2, 0);
		wprintw(scorewin, "    (plus %d observer%s) ",
			nobs, nobs == 1 ? "" : "s");
		++c;
	}
	wmove(scorewin, c+2, 0);
	wclrtobot(scorewin);
}

/*
**	nowup: mark player as being up
**		Updates number of observers.
**		Updates turn number and player average turn score.
*/
nowup(num)
int		num;
{
	/*
	**	Set the turn number and call markhigh after roster redraw
	**	and at the beginning of the turn order.
	*/
	if(currup == -1) {
		currup = num;
		markhigh();
	} else if((currup = num) == 0)
		markhigh();

	/*
	**	If it's our turn and squander values are showing,
	**	switch back to displaying score values.
	*/
	if(num == mypnum && sqndr == True)
		togglesqndr();

	/*
	**	Clear info message and update observers.
	*/
	if(infoclr == False)
		(void) clearinfo();
	showobs();

	/*
	**	Update turn number.  Mark turn average.
	*/
	wmove(scorewin, 0, 11);
	wprintw(scorewin, "%2d", turnnum);
	markturnavg(num);

	/*
	**	Clear old arrow.
	*/
	if(lastup != -1) {
		wmove(scorewin, lastup+2, 0);
		waddstr(scorewin, "   ");
	}

	/*
	**	Draw new arrow.
	*/
	wmove(scorewin, num+2, 0);
	switch(graphics) {
	case Zenith:
		wstandout(scorewin);
		waddstr(scorewin, "hhh");
		wstandend(scorewin);
		WREFRESH(scorewin);
		break;
	case Digital:
		wstandout(scorewin);
		waddstr(scorewin, "=->");
		wstandend(scorewin);
		WREFRESH(scorewin);
		break;
	default:
		waddstr(scorewin, "=->");
		WREFRESH(scorewin);
		break;
	}

	/*
	**	If it's our turn, make some noise.
	*/
	lastup = num;
	if(num == mypnum) {
		if(defhelp == False)
			defhelpmesg();
		if(mystat != Computer)
			mybeep(quiet == True ? 3 : 1);	/* noisier when quiet! */
		else if(quiet == False)
			(void) infomesg(
				"Autopilot engaged; type g<return> to get control.");
	}

	return 0;
}

/*
**	whbar: draw a horizontal bar of specified length in specified window
*/
whbar(win, len)
WINDOW *win;
int		len;
{
	switch(graphics) {
	case Digital:
		wstandout(win);
		while(len-- > 0)
			waddch(win, Horiz);
		wstandend(win);
		break;
	case Zenith:
		wstandout(win);
		while(len-- > 0)
			waddch(win, 'a');
		wstandend(win);
		break;
	default:
		while(len-- > 0)
			waddch(win, '-');
		break;
	}
}

/*
**	wvbar: draw a vertical bar of specified length in specified window
*/
wvbar(win, len)
WINDOW *win;
int		len;
{
	int		x, y;

	getyx(win, y, x);

	switch(graphics) {
	case Digital:
		wstandout(win);
		while(len-- > 0) {
			wmove(win, y + len, x);
			waddch(win, Vert);
		}
		wstandend(win);
		break;
	case Zenith:
		wstandout(win);
		while(len-- > 0) {
			wmove(win, y + len, x);
			waddch(win, '`');
		}
		wstandend(win);
		break;
	default:
		while(len-- > 0) {
			wmove(win, y + len, x);
			waddch(win, '|');
		}
		break;
	}
}

/*
**	wcorner: draw a corner character in a window
*/
wcorner(win, c)
WINDOW *win;
int		c;
{
	char	x;

	switch(graphics) {
	case Digital:
		wstandout(win);
		waddch(win, c);	/* defined Digital */
		wstandend(win);
		break;
	case Zenith:
		switch(c) {
		case UpLeft: x = 'f'; break;
		case UpRite: x = 'c'; break;
		case LoLeft: x = 'e'; break;
		case LoRite: x = 'd'; break;
		}
		wstandout(win);
		waddch(win, x);
		wstandend(win);
		break;
	default:
		waddch(win, '+');
		break;
	}
}

/*
**	wblank: draw a blank in the graphics character set
*/
wblank(win)
WINDOW *win;
{
	switch(graphics) {
	case Digital:
	case Zenith:
		wstandout(win);
		waddch(win, ' ');
		wstandend(win);
		break;
	default:
		waddch(win, ' ');
		break;
	}
}

/*
**	wside: draw a side character in a window
*/
wside(win)
WINDOW *win;
{
	switch(graphics) {
	case Digital:
		wstandout(win);
		waddch(win, Vert);
		wstandend(win);
		break;
	case Zenith:
		wstandout(win);
		waddch(win, '`');
		wstandend(win);
		break;
	default:
		waddch(win, '|');
		break;
	}
}

/*
**	wbox: draw a box in a window
*/
wbox(win, lines, cols)
WINDOW *win;
int		lines, cols;
{
	int		row;
	int		x, y;

	getyx(win, y, x);

	wcorner(win, UpLeft);
	whbar(win, cols-2);
	wcorner(win, UpRite);

	for(row = 1;row < lines-1;++row) {
		wmove(win, y+row, x);
		wside(win);
		wmove(win, y+row, x+cols-1);
		wside(win);
	}

	wmove(win, y+lines-1, x);
	wcorner(win, LoLeft);
	whbar(win, cols-2);
	wcorner(win, LoRite);
}

/*
**	togglesqndr: toggle sqndr value and redraw score window
*/
togglesqndr()
{
	int		c;

	/*
	**	Toggle squander value.
	*/
	sqndr = (sqndr == True) ? False : True;

	/*
	**	If drawhdr is False, we need to update the score window.  
	**	Otherwise, we can wait for the server to request redraw.
	*/
	if(drawhdr == False) {
		markturnavg(currup);
		wmove(scorewin, 0, 23);
		waddstr(scorewin, sqndr == True ? "Sqndr" : "Score");
		for(c = 0;c < PLAYERS;++c) {
			if(plr[c].p_stat == Active || plr[c].p_stat == Computer) {
				wmove(scorewin, c+2, 22);
				wprintw(scorewin, "%6d",
					sqndr == True ? -plr[c].p_squander : plr[c].p_score);
			}
		}
		markhigh();
		WREFRESH(scorewin);
	}
}

/*
**	showplr: update player info
*/
showplr(c)
int		c;
{
	/*
	**	Draw the header when necessary.
	*/
	if(drawhdr == True) {
		wmove(scorewin, 0, 0);
		wclrtoeol(scorewin);
		wprintw(scorewin,
			"%-3s %-18.18s %5s", "Up", "Player",
			sqndr == True ? "Sqndr" : "Score");

		wmove(scorewin, 1, 0);
		wclrtoeol(scorewin);
		whbar(scorewin, 3);
		wblank(scorewin);
		whbar(scorewin, 18);
		wblank(scorewin);
		whbar(scorewin, 5);
		drawhdr = False;
	}

	/*
	**	Update a specific player's entry.  Calling markhigh here
	**	is what causes its excessive behavior, but we must...
	*/
	wmove(scorewin, c+2, 0);
	wclrtoeol(scorewin);
	if(plr[c].p_stat == Active || plr[c].p_stat == Computer)
		wprintw(scorewin, "    %-18.18s%6d", plr[c].p_name,
			sqndr == True ? -plr[c].p_squander : plr[c].p_score);
	if(currup != -1)
		markhigh();

	WREFRESH(scorewin);

	return 0;
}

/*
**	clearplr: clear player info
*/
clearplr(c)
int		c;
{
	if(c == lastup)
		lastup = -1;

	wmove(scorewin, c+2, 0);
	wclrtoeol(scorewin);
	markhigh();
	WREFRESH(scorewin);

	return 0;
}

/*
**	clearscores: blank the entire score window
*/
clearscores()
{
	wclear(scorewin);
	WREFRESH(scorewin);
	drawhdr = True;
	currup = lastup = -1;

	return 0;
}

/*
**	markturnavg: display player's turn point average
*/
markturnavg(num)
int		num;
{
	int		numer, denom;

	wmove(scorewin, 0, 17);
	if(turnnum <= 0 || (turnnum == 1 && inprogress == True))
		wprintw(scorewin, "%5s", "*");		/* no average yet */
	else {
		numer = sqndr == True ? -plr[num].p_squander : plr[num].p_score;
		denom = inprogress == True ? (turnnum - 1) : turnnum;
		wprintw(scorewin, "%5d", numer / denom);
	}
}

/*
**	markhigh: mark player(s) with high score, clearing all others
**		Updates the turns left estimate.
*/
markhigh()
{
	register int	c, h, hc, oh;
	long			avg, left;
	char			ccode, turnbuf[16];

	/*
	**	Find high score.  Save the index of the last player
	**	that holds the high score.  That's why >= h.
	**	Also find the high score amongst other players.
	*/
	h = oh = 0, hc = -1;
	for(c = 0;c < PLAYERS;++c) {
		if(plr[c].p_stat == Inactive)
			continue;
		if(plr[c].p_score >= h)
			h = plr[(hc = c)].p_score;
		if(c != mypnum && plr[c].p_score > oh)
			oh = plr[c].p_score;
	}

	/*
	**	Mark those that have high score, clear others.
	*/
	for(c = 0;c < PLAYERS;++c) {
		wmove(scorewin, c+2, 28);
		if(h == 0 || plr[c].p_stat == Inactive || plr[c].p_score < h)
			waddch(scorewin, ' ');
		else
			waddch(scorewin, '*');
	}

	/*
	**	Estimate number of turns left in game.  Display in header.
	XXX	This seems like an excessive amount of work to do here,
	XXX	but this is the only place where it can be done correctly.
	*/
	if(inprogress == False)
		turnbuf[0] = '\0';
	else if(hc == -1 || h == 0 || turnnum <= 0)
		strcpy(turnbuf, "+?");
	else {
		/*
		**	Determine condition code.  This code is based on
		**	how close the leading competitor is to winning.
		XXX	Code depends on relationship between defined values.
		**	Apologies to Roy G. Biv...
		*/
		left = winscore - oh;
		     if(left <= OFFBOARD)				ccode = 'r';	/* red */
		else if(left <= P_ACEMULT * P_3OKMULT)	ccode = 'o';	/* orange */
		else if(left <= P_STRAIGHT)				ccode = 'y';	/* yellow */
		else if(left <= P_ACEMULT * P_4OKMULT)	ccode = 'g';	/* green */
		else if(left <= P_ACEMULT * P_AOKMULT)	ccode = 'b';	/* blue */
		else									ccode = ' ';	/* no alert */

		/*
		**	This routine is called from nowup which is called before
		**	each player's turn, and again when the turn is over
		**	by showplr (for score updating purposes).  We detect the
		**	second call by noting when currup is the same as lastup.
		**	The leader has taken this turn when currup is greater than
		**	hc or when currup equals hc and currup equals lastup.
		*/
		if(currup > hc || (currup == hc && currup == lastup))
			avg = h / turnnum;			/* leader has taken turn */
		else
			avg = h / (turnnum - 1);	/* leader has yet to take turn */
		if(avg <= 0)					/* never happen? */
			avg = 1;					/* just in case */

		/*
		**	Calculate number of points left, accounting for end conditions.
		**	In the case that the high score is within OFFBOARD of winscore,
		**	if we're within avg, we'll say we can get it on the next roll.
		*/
		if((left = winscore - h) <= 0)
			left = 0;					/* XXX: doesn't account for overtime */
		else if(left < OFFBOARD && left > avg)
			left = OFFBOARD;			/* need this, not avg */

		left = (left + avg - 1) / avg;	/* rounds up */
		sprintf(turnbuf, "+%ld%c", left, ccode);
	}

	/*
	**	Display estimated turns left and condition code in header.
	*/
	wmove(scorewin, 0, 13);
	wprintw(scorewin, "%-4.4s", turnbuf);
}

/*
**	cleardice: remove the dice from the screen
*/
cleardice()
{
	register int	d;

	wclear(dstatwin);
	WREFRESH(dstatwin);

	for(d = 0;d < NDICE;++d) {
		dice.d_face[d] = CLEARDIE;
		draw1die(d);
	}
	diceinit();

	wclear(dnumwin);
	WREFRESH(dnumwin);
	drawnum = True;

	return 0;
}

/*
**	facetbl: comparative values of faces
XXX		assumes much, especially about the value of JOKER
*/
static int	facetbl[][JOKER+1] = {
/*		   B   A   2   3   4   F   6   -   -   J */
/* B */	{  0,  1,  1,  1,  1,  1,  1,  0,  0,  1 },
/* A */	{ -1,  0, -1, -1, -1, -1, -1, -1, -1, -1 },
/* 2 */	{ -1,  1,  0,  0,  0,  1,  0, -1, -1,  1 },
/* 3 */	{ -1,  1,  0,  0,  0,  1,  0, -1, -1,  1 },
/* 4 */	{ -1,  1,  0,  0,  0,  1,  0, -1, -1,  1 },
/* F */	{ -1,  1, -1, -1, -1,  0, -1, -1, -1, -1 },
/* 6 */	{ -1,  1,  0,  0,  0,  1,  0, -1, -1,  1 },
/* - */	{  0,  1,  1,  1,  1,  1,  1,  0,  0,  1 },
/* - */	{  0,  1,  1,  1,  1,  1,  1,  0,  0,  1 },
/* J */	{ -1,  1, -1, -1, -1,  1, -1, -1, -1,  0 }
};

/*
**	dicecmp: compare dice indices, least valuable first
*/
static int
dicecmp(p1, p2)
int	   *p1, *p2;
{
#ifndef	lint
	int		diff;

	if((diff = (int)dice.d_comb[*p1] - (int)dice.d_comb[*p2]) != 0)
		return diff;
	if((diff = (int)dice.d_stat[*p1] - (int)dice.d_stat[*p2]) != 0)
		return diff;
#endif	lint

	return facetbl[dice.d_face[*p2]][dice.d_face[*p1]];
}

/*
**	drawdice: show dice status and faces and scoring summary
*/
drawdice()
{
	register int	d;
	char			msgbuf[MESGLEN];
	int				dlist[NDICE];

	/*
	**	Update dnumwin when necessary.
	*/
	if(drawnum == True) {
		for(d = 0;d < NDICE;++d) {
			wmove(dnumwin, 0, d * DIEWID);
			wprintw(dnumwin, "%*d", (DIEWID+1)/2, d+1);
		}
		WREFRESH(dnumwin);
		drawnum = False;
	}

	/*
	**	Draw the dice in a way that will maximize suspense,
	**	that is, draw the least valuable first.
	*/
	for(d = 0;d < NDICE;++d)
		dlist[d] = d;
	qsort((char *)dlist, NDICE, sizeof dlist[0], dicecmp);
	for(d = 0;d < NDICE;++d)
		(void) draw1die(dlist[d]);
	
	/*
	**	Show the combinations.
	*/
	wmove(dstatwin, 0, 0);
	wclrtoeol(dstatwin);
	for(d = 0;d < NDICE;++d) {
		wmove(dstatwin, 0, d * DIEWID);
		if(dice.d_stat[d] != Free && dice.d_face[d] != BADFACE)
			waddstr(dstatwin, combname(dice.d_comb[d]));
	}

/*
**	CENTER: macro to center messages under the dice
*/
#define	CENTER(m) {										\
	if((d = (DSTATWID-1)/2 - strlen(m)/2) < 0) d = 0;	\
	wprintw(dstatwin, "%*s%s", d, "", m);				\
}

	wmove(dstatwin, 2, 0);
	wclrtoeol(dstatwin);
	if(dice.d_mesg[0] != '\0')
		CENTER(dice.d_mesg);

	wmove(dstatwin, 3, 0);
	wclrtoeol(dstatwin);
	if(dice.d_pts_turn > 0) {
		if(dice.d_mesg[0] == '\0' || strcmp(dice.d_mesg, "nothing") == 0)
			/* do nothing */;
		else if(dice.d_pts_turn < dice.d_pts_max
				&& strcmp(dice.d_mesg, "held") == 0) {
			sprintf(msgbuf, "%d saved, %d given away",
				dice.d_pts_turn, dice.d_pts_max - dice.d_pts_turn);
			CENTER(msgbuf);
		} else {
			sprintf(msgbuf, "plus %d saved", dice.d_pts_turn);
			CENTER(msgbuf);
		}
	} else if(dice.d_pts_roll < 0) {
		if(dice.d_pts_max > -dice.d_pts_roll)
			sprintf(msgbuf, "rolled away %d points", dice.d_pts_max);
		else
			sprintf(msgbuf, "threw away %d points", -dice.d_pts_roll);
		CENTER(msgbuf);
	}

	wmove(dstatwin, 4, 0);
	wclrtoeol(dstatwin);
	for(d = 0;d < NDICE;++d)
		if(dice.d_comb[d] == Nothing)
			break;
	if(d == NDICE) {
		strcpy(msgbuf, "scored with all dice");
		CENTER(msgbuf);
	} else if(dice.d_pts_roll < 0 && dice.d_pts_max > 0 && currup >= 0) {
		sprintf(msgbuf, "squandered %d so far",
			dice.d_pts_max + plr[currup].p_squander);
		CENTER(msgbuf);
	}

	WREFRESH(dstatwin);
	return 0;

#undef	CENTER
}

/*
**	facename: names for the die faces
**		All names centered in nine or fifteen spaces.
*/
static char *
facename(face)
int		face;
{
	switch(face) {
	/*				return wide==True ? "123456789012345" : "123456789";	*/
	case BADFACE:	return wide==True ? "    in hand    " : " in hand ";
	case ACE:		return wide==True ? "      one      " : "   one   ";
	case 2:			return wide==True ? "      two      " : "   two   ";
	case 3:			return wide==True ? "     three     " : "  three  ";
	case 4:			return wide==True ? "      four     " : "   four  ";
	case FIVE:		return wide==True ? "      five     " : "   five  ";
	case 6:			return wide==True ? "      six      " : "   six   ";
	case JOKER:		return wide==True ? "     joker     " : "  joker  ";
	case CLEARDIE:	return wide==True ? "               " : "         ";
	default:		return wide==True ? "       ?       " : "    ?    ";
	}
};

/*
**	combname: return pointer to string describing combination
**		all names are centered in nine or fifteen spaces
*/
static char *
combname(comb)
combination	comb;
{
	switch(comb) {
	/*					 return wide==True ? "123456789012345" : "123456789"; */
	case Previous:		 return wide==True ? "       +       " : "    +    ";
	case Nothing:		 return wide==True ? "               " : "         ";
	case Joker:			 return wide==True ? "     joker     " : "  joker  ";
	case Five:			 return wide==True ? "      five     " : "   five  ";
	case Ace:			 return wide==True ? "      one      " : "   one   ";
	case Three_of_a_kind:return wide==True ? "    3o'kind    " : " 3o'kind ";
	case Small_straight: return wide==True ? "     small     " : "  small  ";
	case Four_of_a_kind: return wide==True ? "    4o'kind    " : " 4o'kind ";
	case Asm_straight:	 return wide==True ? "    straight   " : " straight";
	case Straight:		 return wide==True ? "    straight   " : " straight";
	case All_of_a_kind:	 return wide==True ? "    5o'kind    " : " 5o'kind ";
	default:			 return wide==True ? "       ?       " : "    ?    ";
	}
}

/*
**	draw1die: draw die number d
*/
draw1die(d)
int		d;
{
	register int	row, w, s;
	static int		showing[NDICE] = {	CLEARDIE, CLEARDIE, CLEARDIE,
	/* XXX: assumes NDICE==5 */			CLEARDIE, CLEARDIE };

	s = dice.d_face[d];

	/*
	**	If this die face is already showing, there's nothing more to do.
	*/
	if(showing[d] == s)
		return;
	showing[d] = s;

	/*
	**	Draw die and refresh it.  Two possible orientations per die.
	*/
	if(graphics != Nographics)
		wstandout(die[d]);
	w = 2 * s;
	if(randint(128) > 64)
		++w;
	for(row = 0;row < DIELINES;++row) {
		wmove(die[d], row, 0);
		waddstr(die[d], dieface[w][row]);
	}
	WREFRESH(die[d]);

	/*
	**	Update die face name.
	*/
	wmove(dfacewin, 0, d * DIEWID);
	waddstr(dfacewin, facename(s));
	WREFRESH(dfacewin);
}

/*
**	outc: write a single character
*/
static
outc(c)
char	c;
{
	return write(_tty_ch, &c, 1);
}

/*
**	mybeep: send a number of beeps
*/
mybeep(num)
{
	while(num-- > 0) {
		(void) outc('\007');
		if(num > 0) {
			static struct timeval	tv = { 0L, 250000L };
			(void) select(32, NOSEL, NOSEL, NOSEL, &tv);
		}
	}
}

/*
**	neutral: return cursor to a neutral position
*/
neutral()
{
	wmove(promptwin, 0, 0);
	WREFRESH(promptwin);
}

/*
**	redraw: cause the screen to be redrawn
*/
redraw()
{
	/*
	**	Reinitialize the terminal for curses mode.
	*/
	tputs(TI, 1, outc);
	tputs(VS, 1, outc);

	/*
	**	Ensure that the screen redraws.
	*/
	touchwin(curscr);
	clearok(curscr, TRUE);

	/*
	**	Since we're redrawing, we have to assume that the screen got
	**	messed up.  This could mean that the screen is in standout
	**	mode when curses thinks it isn't, or vice versa.  We try to
	**	synchronize by sending the standout-end sequence and by
	**	drawing a non-graphics character at position 0,0.
	*/
	if(graphics != Nographics) {
		int	y, x;

		tputs(SE, 1, outc);
		standend();
		getyx(stdscr, y, x);
		mvaddch(0, 0, '*');
		REFRESH();
		mvaddch(0, 0, ' ');
		move(y, x);
	}

	/*
	**	Refresh the screen.
	*/
	REFRESH();
}

/*
**	sigttin: handle SIGTTIN signal
*/
static int
sigttin()
{
	static int	bgreads	= 0;

	/*
	**	We shouldn't get this signal, but I have seen a burst of
	**	noise cause a screwup where the shell thought cubes was
	**	suspended, but cubes thought it wasn't.  Very bad.  We
	**	try to handle this situation here.
	*/
	if(quiet == False) {
		(void) kill(0, SIGTSTP);	/* suspend process group */
		return;
	}

	/*
	**	Reaching here can only happen if we are erroneously doing
	**	reads while in the background.  This would be a problem with
	**	the code.  What to do?  Let's ignore it a few times then
	**	exit prematurely.  Sorry, folks.
	*/
	if(bgreads++ > 5)
		cleanup(1, "too many background reads");
}

/*
**	suspend: catch suspend signal
*/
static int
suspend()
{
	if(quiet == False)
		beginquiet();
}

/*
**	unsuspend: catch continue signal
*/
static int
unsuspend()
{
	if(quiet == False)
		return;

	/*
	**	If we got put in the background (erroneously) by the user,
	**	then kill the process group as if output attempted from the
	**	background with "stty tstp" set.
	*/
	if(inbackground() == True) {
		(void) killpg(mypgrp, SIGTTOU);
		return;
	}

	endquiet();
}

/*
**	inbackground: returns True if process is in background
**		side effect: sets mypgrp
*/
static boolean
inbackground()
{
	int		ttypgrp;

	mypgrp = getpgrp(0);

	if(ioctl(_tty_ch, TIOCGPGRP, (char *)&ttypgrp) < 0) {
		perror("ioctl: TIOCGPGRP");
		return True;	/* error */
	}

	return ttypgrp != mypgrp ? True : False;
}

static int	ttyflags;	/* set in beginquiet, used in endquiet */

/*
**	beginquiet: enter quiet mode
*/
static int
beginquiet()
{
	quiet = True;
	ttyflags = _tty.sg_flags;
	endwin();
	if(graphics != Nographics)
		tputs(SE, 1, outc);
	tputs(VE, 1, outc);
	tputs(TE, 1, outc);
	tputs(CL, LINES, outc);
}

/*
**	endquiet: leave quiet mode
*/
static int
endquiet()
{
	quiet = False;

	/*
	**	Return terminal settings to what we want.
	*/
	savetty();
	_tty.sg_flags = ttyflags;
	(void) ioctl(_tty_ch, TIOCSETN, (char *)&_tty);
	redraw();
	neutral();
}