|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T s
Length: 46584 (0xb5f8) Types: TextFile Names: »screen.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Cubes/screen.c«
/* 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(); }