1*57273Sbostic /*- 2*57273Sbostic * Copyright (c) 1992 The Regents of the University of California. 3*57273Sbostic * All rights reserved. 4*57273Sbostic * 5*57273Sbostic * %sccs.include.redist.c% 6*57273Sbostic * 7*57273Sbostic * @(#)screen.c 5.1 (Berkeley) 12/22/92 8*57273Sbostic */ 9*57273Sbostic 10*57273Sbostic /* 11*57273Sbostic * Tetris screen control. 12*57273Sbostic */ 13*57273Sbostic 14*57273Sbostic #include <sgtty.h> 15*57273Sbostic #include <sys/ioctl.h> 16*57273Sbostic 17*57273Sbostic #include <setjmp.h> 18*57273Sbostic #include <signal.h> 19*57273Sbostic #include <stdio.h> 20*57273Sbostic #include <stdlib.h> 21*57273Sbostic #include <string.h> 22*57273Sbostic #include <unistd.h> 23*57273Sbostic 24*57273Sbostic #ifndef sigmask 25*57273Sbostic #define sigmask(s) (1 << ((s) - 1)) 26*57273Sbostic #endif 27*57273Sbostic 28*57273Sbostic #include "screen.h" 29*57273Sbostic #include "tetris.h" 30*57273Sbostic 31*57273Sbostic /* 32*57273Sbostic * XXX - need a <termcap.h> 33*57273Sbostic */ 34*57273Sbostic int tgetent __P((char *, const char *)); 35*57273Sbostic int tgetflag __P((const char *)); 36*57273Sbostic int tgetnum __P((const char *)); 37*57273Sbostic int tputs __P((const char *, int, int (*)(int))); 38*57273Sbostic 39*57273Sbostic static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */ 40*57273Sbostic static int curscore; 41*57273Sbostic static int isset; /* true => terminal is in game mode */ 42*57273Sbostic static struct sgttyb oldtt; 43*57273Sbostic static void (*tstp)(); 44*57273Sbostic 45*57273Sbostic char *tgetstr(), *tgoto(); 46*57273Sbostic 47*57273Sbostic 48*57273Sbostic /* 49*57273Sbostic * Capabilities from TERMCAP. 50*57273Sbostic */ 51*57273Sbostic char PC, *BC, *UP; /* tgoto requires globals: ugh! */ 52*57273Sbostic short ospeed; 53*57273Sbostic 54*57273Sbostic static char 55*57273Sbostic *bcstr, /* backspace char */ 56*57273Sbostic *CEstr, /* clear to end of line */ 57*57273Sbostic *CLstr, /* clear screen */ 58*57273Sbostic *CMstr, /* cursor motion string */ 59*57273Sbostic #ifdef unneeded 60*57273Sbostic *CRstr, /* "\r" equivalent */ 61*57273Sbostic #endif 62*57273Sbostic *HOstr, /* cursor home */ 63*57273Sbostic *LLstr, /* last line, first column */ 64*57273Sbostic *pcstr, /* pad character */ 65*57273Sbostic *TEstr, /* end cursor motion mode */ 66*57273Sbostic *TIstr; /* begin cursor motion mode */ 67*57273Sbostic char 68*57273Sbostic *SEstr, /* end standout mode */ 69*57273Sbostic *SOstr; /* begin standout mode */ 70*57273Sbostic static int 71*57273Sbostic COnum, /* co# value */ 72*57273Sbostic LInum, /* li# value */ 73*57273Sbostic MSflag; /* can move in standout mode */ 74*57273Sbostic 75*57273Sbostic 76*57273Sbostic struct tcsinfo { /* termcap string info; some abbrevs above */ 77*57273Sbostic char tcname[3]; 78*57273Sbostic char **tcaddr; 79*57273Sbostic } tcstrings[] = { 80*57273Sbostic "bc", &bcstr, 81*57273Sbostic "ce", &CEstr, 82*57273Sbostic "cl", &CLstr, 83*57273Sbostic "cm", &CMstr, 84*57273Sbostic #ifdef unneeded 85*57273Sbostic "cr", &CRstr, 86*57273Sbostic #endif 87*57273Sbostic "le", &BC, /* move cursor left one space */ 88*57273Sbostic "pc", &pcstr, 89*57273Sbostic "se", &SEstr, 90*57273Sbostic "so", &SOstr, 91*57273Sbostic "te", &TEstr, 92*57273Sbostic "ti", &TIstr, 93*57273Sbostic "up", &UP, /* cursor up */ 94*57273Sbostic 0 95*57273Sbostic }; 96*57273Sbostic 97*57273Sbostic /* This is where we will actually stuff the information */ 98*57273Sbostic 99*57273Sbostic static char combuf[1024], tbuf[1024]; 100*57273Sbostic 101*57273Sbostic 102*57273Sbostic /* 103*57273Sbostic * Routine used by tputs(). 104*57273Sbostic */ 105*57273Sbostic int 106*57273Sbostic put(c) 107*57273Sbostic int c; 108*57273Sbostic { 109*57273Sbostic 110*57273Sbostic return (putchar(c)); 111*57273Sbostic } 112*57273Sbostic 113*57273Sbostic /* 114*57273Sbostic * putstr() is for unpadded strings (either as in termcap(5) or 115*57273Sbostic * simply literal strings); putpad() is for padded strings with 116*57273Sbostic * count=1. (See screen.h for putpad().) 117*57273Sbostic */ 118*57273Sbostic #define putstr(s) (void)fputs(s, stdout) 119*57273Sbostic #define moveto(r, c) putpad(tgoto(CMstr, c, r)) 120*57273Sbostic 121*57273Sbostic /* 122*57273Sbostic * Set up from termcap. 123*57273Sbostic */ 124*57273Sbostic void 125*57273Sbostic scr_init() 126*57273Sbostic { 127*57273Sbostic static int bsflag, xsflag, sgnum; 128*57273Sbostic #ifdef unneeded 129*57273Sbostic static int ncflag; 130*57273Sbostic #endif 131*57273Sbostic char *term, *fill; 132*57273Sbostic static struct tcninfo { /* termcap numeric and flag info */ 133*57273Sbostic char tcname[3]; 134*57273Sbostic int *tcaddr; 135*57273Sbostic } tcflags[] = { 136*57273Sbostic "bs", &bsflag, 137*57273Sbostic "ms", &MSflag, 138*57273Sbostic #ifdef unneeded 139*57273Sbostic "nc", &ncflag, 140*57273Sbostic #endif 141*57273Sbostic "xs", &xsflag, 142*57273Sbostic 0 143*57273Sbostic }, tcnums[] = { 144*57273Sbostic "co", &COnum, 145*57273Sbostic "li", &LInum, 146*57273Sbostic "sg", &sgnum, 147*57273Sbostic 0 148*57273Sbostic }; 149*57273Sbostic 150*57273Sbostic if ((term = getenv("TERM")) == NULL) 151*57273Sbostic stop("you must set the TERM environment variable"); 152*57273Sbostic if (tgetent(tbuf, term) <= 0) 153*57273Sbostic stop("cannot find your termcap"); 154*57273Sbostic fill = combuf; 155*57273Sbostic { 156*57273Sbostic register struct tcsinfo *p; 157*57273Sbostic 158*57273Sbostic for (p = tcstrings; p->tcaddr; p++) 159*57273Sbostic *p->tcaddr = tgetstr(p->tcname, &fill); 160*57273Sbostic } 161*57273Sbostic { 162*57273Sbostic register struct tcninfo *p; 163*57273Sbostic 164*57273Sbostic for (p = tcflags; p->tcaddr; p++) 165*57273Sbostic *p->tcaddr = tgetflag(p->tcname); 166*57273Sbostic for (p = tcnums; p->tcaddr; p++) 167*57273Sbostic *p->tcaddr = tgetnum(p->tcname); 168*57273Sbostic } 169*57273Sbostic if (bsflag) 170*57273Sbostic BC = "\b"; 171*57273Sbostic else if (BC == NULL && bcstr != NULL) 172*57273Sbostic BC = bcstr; 173*57273Sbostic if (CLstr == NULL) 174*57273Sbostic stop("cannot clear screen"); 175*57273Sbostic if (CMstr == NULL || UP == NULL || BC == NULL) 176*57273Sbostic stop("cannot do random cursor positioning via tgoto()"); 177*57273Sbostic PC = pcstr ? *pcstr : 0; 178*57273Sbostic if (sgnum >= 0 || xsflag) 179*57273Sbostic SOstr = SEstr = NULL; 180*57273Sbostic #ifdef unneeded 181*57273Sbostic if (ncflag) 182*57273Sbostic CRstr = NULL; 183*57273Sbostic else if (CRstr == NULL) 184*57273Sbostic CRstr = "\r"; 185*57273Sbostic #endif 186*57273Sbostic } 187*57273Sbostic 188*57273Sbostic /* this foolery is needed to modify tty state `atomically' */ 189*57273Sbostic static jmp_buf scr_onstop; 190*57273Sbostic 191*57273Sbostic #define sigunblock(mask) sigsetmask(sigblock(0) & ~(mask)) 192*57273Sbostic 193*57273Sbostic static void 194*57273Sbostic stopset(sig) 195*57273Sbostic int sig; 196*57273Sbostic { 197*57273Sbostic (void) signal(sig, SIG_DFL); 198*57273Sbostic (void) kill(getpid(), sig); 199*57273Sbostic (void) sigunblock(sigmask(sig)); 200*57273Sbostic longjmp(scr_onstop, 1); 201*57273Sbostic } 202*57273Sbostic 203*57273Sbostic static void 204*57273Sbostic scr_stop() 205*57273Sbostic { 206*57273Sbostic scr_end(); 207*57273Sbostic (void) kill(getpid(), SIGTSTP); 208*57273Sbostic (void) sigunblock(sigmask(SIGTSTP)); 209*57273Sbostic scr_set(); 210*57273Sbostic scr_msg(key_msg, 1); 211*57273Sbostic } 212*57273Sbostic 213*57273Sbostic /* 214*57273Sbostic * Set up screen mode. 215*57273Sbostic */ 216*57273Sbostic void 217*57273Sbostic scr_set() 218*57273Sbostic { 219*57273Sbostic struct winsize ws; 220*57273Sbostic struct sgttyb newtt; 221*57273Sbostic volatile int omask; 222*57273Sbostic void (*ttou)(); 223*57273Sbostic 224*57273Sbostic omask = sigblock(sigmask(SIGTSTP) | sigmask(SIGTTOU)); 225*57273Sbostic if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN) 226*57273Sbostic (void) signal(SIGTSTP, SIG_IGN); 227*57273Sbostic if ((ttou = signal(SIGTSTP, stopset)) == SIG_IGN) 228*57273Sbostic (void) signal(SIGTSTP, SIG_IGN); 229*57273Sbostic /* 230*57273Sbostic * At last, we are ready to modify the tty state. If 231*57273Sbostic * we stop while at it, stopset() above will longjmp back 232*57273Sbostic * to the setjmp here and we will start over. 233*57273Sbostic */ 234*57273Sbostic (void) setjmp(scr_onstop); 235*57273Sbostic (void) sigsetmask(omask); 236*57273Sbostic Rows = 0, Cols = 0; 237*57273Sbostic if (ioctl(0, TIOCGWINSZ, &ws) == 0) { 238*57273Sbostic Rows = ws.ws_row; 239*57273Sbostic Cols = ws.ws_col; 240*57273Sbostic } 241*57273Sbostic if (Rows == 0) 242*57273Sbostic Rows = LInum; 243*57273Sbostic if (Cols == 0) 244*57273Sbostic Cols = COnum; 245*57273Sbostic if (Rows < MINROWS || Cols < MINCOLS) { 246*57273Sbostic (void) fprintf(stderr, 247*57273Sbostic "the screen is too small: must be at least %d x %d", 248*57273Sbostic MINROWS, MINCOLS); 249*57273Sbostic stop(""); /* stop() supplies \n */ 250*57273Sbostic } 251*57273Sbostic if (ioctl(0, TIOCGETP, &oldtt)) 252*57273Sbostic stop("ioctl(TIOCGETP) fails"); 253*57273Sbostic newtt = oldtt; 254*57273Sbostic newtt.sg_flags = (newtt.sg_flags | CBREAK) & ~(CRMOD | ECHO); 255*57273Sbostic if ((newtt.sg_flags & TBDELAY) == XTABS) 256*57273Sbostic newtt.sg_flags &= ~TBDELAY; 257*57273Sbostic if (ioctl(0, TIOCSETN, &newtt)) 258*57273Sbostic stop("ioctl(TIOCSETN) fails"); 259*57273Sbostic ospeed = newtt.sg_ospeed; 260*57273Sbostic omask = sigblock(sigmask(SIGTSTP) | sigmask(SIGTTOU)); 261*57273Sbostic 262*57273Sbostic /* 263*57273Sbostic * We made it. We are now in screen mode, modulo TIstr 264*57273Sbostic * (which we will fix immediately). 265*57273Sbostic */ 266*57273Sbostic if (TIstr) 267*57273Sbostic putstr(TIstr); /* termcap(5) says this is not padded */ 268*57273Sbostic if (tstp != SIG_IGN) 269*57273Sbostic (void) signal(SIGTSTP, scr_stop); 270*57273Sbostic (void) signal(SIGTTOU, ttou); 271*57273Sbostic 272*57273Sbostic isset = 1; 273*57273Sbostic (void) sigsetmask(omask); 274*57273Sbostic scr_clear(); 275*57273Sbostic } 276*57273Sbostic 277*57273Sbostic /* 278*57273Sbostic * End screen mode. 279*57273Sbostic */ 280*57273Sbostic void 281*57273Sbostic scr_end() 282*57273Sbostic { 283*57273Sbostic int omask = sigblock(sigmask(SIGTSTP) | sigmask(SIGTTOU)); 284*57273Sbostic 285*57273Sbostic /* move cursor to last line */ 286*57273Sbostic if (LLstr) 287*57273Sbostic putstr(LLstr); /* termcap(5) says this is not padded */ 288*57273Sbostic else 289*57273Sbostic moveto(Rows - 1, 0); 290*57273Sbostic /* exit screen mode */ 291*57273Sbostic if (TEstr) 292*57273Sbostic putstr(TEstr); /* termcap(5) says this is not padded */ 293*57273Sbostic (void) fflush(stdout); 294*57273Sbostic (void) ioctl(0, TIOCSETN, &oldtt); 295*57273Sbostic isset = 0; 296*57273Sbostic /* restore signals */ 297*57273Sbostic (void) signal(SIGTSTP, tstp); 298*57273Sbostic (void) sigsetmask(omask); 299*57273Sbostic } 300*57273Sbostic 301*57273Sbostic void 302*57273Sbostic stop(why) 303*57273Sbostic char *why; 304*57273Sbostic { 305*57273Sbostic 306*57273Sbostic if (isset) 307*57273Sbostic scr_end(); 308*57273Sbostic (void) fprintf(stderr, "aborting: %s\n", why); 309*57273Sbostic exit(1); 310*57273Sbostic } 311*57273Sbostic 312*57273Sbostic /* 313*57273Sbostic * Clear the screen, forgetting the current contents in the process. 314*57273Sbostic */ 315*57273Sbostic void 316*57273Sbostic scr_clear() 317*57273Sbostic { 318*57273Sbostic 319*57273Sbostic putpad(CLstr); 320*57273Sbostic curscore = -1; 321*57273Sbostic bzero((char *)curscreen, sizeof(curscreen)); 322*57273Sbostic } 323*57273Sbostic 324*57273Sbostic #if vax && !__GNUC__ 325*57273Sbostic typedef int regcell; /* pcc is bad at `register char', etc */ 326*57273Sbostic #else 327*57273Sbostic typedef cell regcell; 328*57273Sbostic #endif 329*57273Sbostic 330*57273Sbostic /* 331*57273Sbostic * Update the screen. 332*57273Sbostic */ 333*57273Sbostic void 334*57273Sbostic scr_update() 335*57273Sbostic { 336*57273Sbostic register cell *bp, *sp; 337*57273Sbostic register regcell so, cur_so = 0; 338*57273Sbostic register int i, ccol, j; 339*57273Sbostic int omask = sigblock(sigmask(SIGTSTP)); 340*57273Sbostic 341*57273Sbostic /* always leave cursor after last displayed point */ 342*57273Sbostic curscreen[D_LAST * B_COLS - 1] = -1; 343*57273Sbostic 344*57273Sbostic if (score != curscore) { 345*57273Sbostic if (HOstr) 346*57273Sbostic putpad(HOstr); 347*57273Sbostic else 348*57273Sbostic moveto(0, 0); 349*57273Sbostic (void) printf("%d", score); 350*57273Sbostic curscore = score; 351*57273Sbostic } 352*57273Sbostic 353*57273Sbostic bp = &board[D_FIRST * B_COLS]; 354*57273Sbostic sp = &curscreen[D_FIRST * B_COLS]; 355*57273Sbostic for (j = D_FIRST; j < D_LAST; j++) { 356*57273Sbostic ccol = -1; 357*57273Sbostic for (i = 0; i < B_COLS; bp++, sp++, i++) { 358*57273Sbostic if (*sp == (so = *bp)) 359*57273Sbostic continue; 360*57273Sbostic *sp = so; 361*57273Sbostic if (i != ccol) { 362*57273Sbostic if (cur_so && MSflag) { 363*57273Sbostic putpad(SEstr); 364*57273Sbostic cur_so = 0; 365*57273Sbostic } 366*57273Sbostic moveto(RTOD(j), CTOD(i)); 367*57273Sbostic } 368*57273Sbostic if (SOstr) { 369*57273Sbostic if (so != cur_so) { 370*57273Sbostic putpad(so ? SOstr : SEstr); 371*57273Sbostic cur_so = so; 372*57273Sbostic } 373*57273Sbostic putstr(" "); 374*57273Sbostic } else 375*57273Sbostic putstr(so ? "XX" : " "); 376*57273Sbostic ccol = i + 1; 377*57273Sbostic /* 378*57273Sbostic * Look ahead a bit, to avoid extra motion if 379*57273Sbostic * we will be redrawing the cell after the next. 380*57273Sbostic * Motion probably takes four or more characters, 381*57273Sbostic * so we save even if we rewrite two cells 382*57273Sbostic * `unnecessarily'. Skip it all, though, if 383*57273Sbostic * the next cell is a different color. 384*57273Sbostic */ 385*57273Sbostic #define STOP (B_COLS - 3) 386*57273Sbostic if (i > STOP || sp[1] != bp[1] || so != bp[1]) 387*57273Sbostic continue; 388*57273Sbostic if (sp[2] != bp[2]) 389*57273Sbostic sp[1] = -1; 390*57273Sbostic else if (i < STOP && so == bp[2] && sp[3] != bp[3]) { 391*57273Sbostic sp[2] = -1; 392*57273Sbostic sp[1] = -1; 393*57273Sbostic } 394*57273Sbostic } 395*57273Sbostic } 396*57273Sbostic if (cur_so) 397*57273Sbostic putpad(SEstr); 398*57273Sbostic (void) fflush(stdout); 399*57273Sbostic (void) sigsetmask(omask); 400*57273Sbostic } 401*57273Sbostic 402*57273Sbostic /* 403*57273Sbostic * Write a message (set!=0), or clear the same message (set==0). 404*57273Sbostic * (We need its length in case we have to overwrite with blanks.) 405*57273Sbostic */ 406*57273Sbostic void 407*57273Sbostic scr_msg(s, set) 408*57273Sbostic register char *s; 409*57273Sbostic int set; 410*57273Sbostic { 411*57273Sbostic 412*57273Sbostic if (set || CEstr == NULL) { 413*57273Sbostic register int l = strlen(s); 414*57273Sbostic 415*57273Sbostic moveto(Rows - 2, ((Cols - l) >> 1) - 1); 416*57273Sbostic if (set) 417*57273Sbostic putstr(s); 418*57273Sbostic else 419*57273Sbostic while (--l >= 0) 420*57273Sbostic (void) putchar(' '); 421*57273Sbostic } else { 422*57273Sbostic moveto(Rows - 2, 0); 423*57273Sbostic putpad(CEstr); 424*57273Sbostic } 425*57273Sbostic } 426