157273Sbostic /*- 257273Sbostic * Copyright (c) 1992 The Regents of the University of California. 357273Sbostic * All rights reserved. 457273Sbostic * 5*57288Sbostic * This code is derived from software contributed to Berkeley by 6*57288Sbostic * Chris Torek and Darren F. Provine. 7*57288Sbostic * 857273Sbostic * %sccs.include.redist.c% 957273Sbostic * 10*57288Sbostic * @(#)screen.c 5.2 (Berkeley) 12/23/92 1157273Sbostic */ 1257273Sbostic 1357273Sbostic /* 1457273Sbostic * Tetris screen control. 1557273Sbostic */ 1657273Sbostic 1757273Sbostic #include <sgtty.h> 1857273Sbostic #include <sys/ioctl.h> 1957273Sbostic 2057273Sbostic #include <setjmp.h> 2157273Sbostic #include <signal.h> 2257273Sbostic #include <stdio.h> 2357273Sbostic #include <stdlib.h> 2457273Sbostic #include <string.h> 2557273Sbostic #include <unistd.h> 2657273Sbostic 2757273Sbostic #ifndef sigmask 2857273Sbostic #define sigmask(s) (1 << ((s) - 1)) 2957273Sbostic #endif 3057273Sbostic 3157273Sbostic #include "screen.h" 3257273Sbostic #include "tetris.h" 3357273Sbostic 3457273Sbostic /* 3557273Sbostic * XXX - need a <termcap.h> 3657273Sbostic */ 3757273Sbostic int tgetent __P((char *, const char *)); 3857273Sbostic int tgetflag __P((const char *)); 3957273Sbostic int tgetnum __P((const char *)); 4057273Sbostic int tputs __P((const char *, int, int (*)(int))); 4157273Sbostic 4257273Sbostic static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */ 4357273Sbostic static int curscore; 4457273Sbostic static int isset; /* true => terminal is in game mode */ 4557273Sbostic static struct sgttyb oldtt; 4657273Sbostic static void (*tstp)(); 4757273Sbostic 4857273Sbostic char *tgetstr(), *tgoto(); 4957273Sbostic 5057273Sbostic 5157273Sbostic /* 5257273Sbostic * Capabilities from TERMCAP. 5357273Sbostic */ 5457273Sbostic char PC, *BC, *UP; /* tgoto requires globals: ugh! */ 5557273Sbostic short ospeed; 5657273Sbostic 5757273Sbostic static char 5857273Sbostic *bcstr, /* backspace char */ 5957273Sbostic *CEstr, /* clear to end of line */ 6057273Sbostic *CLstr, /* clear screen */ 6157273Sbostic *CMstr, /* cursor motion string */ 6257273Sbostic #ifdef unneeded 6357273Sbostic *CRstr, /* "\r" equivalent */ 6457273Sbostic #endif 6557273Sbostic *HOstr, /* cursor home */ 6657273Sbostic *LLstr, /* last line, first column */ 6757273Sbostic *pcstr, /* pad character */ 6857273Sbostic *TEstr, /* end cursor motion mode */ 6957273Sbostic *TIstr; /* begin cursor motion mode */ 7057273Sbostic char 7157273Sbostic *SEstr, /* end standout mode */ 7257273Sbostic *SOstr; /* begin standout mode */ 7357273Sbostic static int 7457273Sbostic COnum, /* co# value */ 7557273Sbostic LInum, /* li# value */ 7657273Sbostic MSflag; /* can move in standout mode */ 7757273Sbostic 7857273Sbostic 7957273Sbostic struct tcsinfo { /* termcap string info; some abbrevs above */ 8057273Sbostic char tcname[3]; 8157273Sbostic char **tcaddr; 8257273Sbostic } tcstrings[] = { 8357273Sbostic "bc", &bcstr, 8457273Sbostic "ce", &CEstr, 8557273Sbostic "cl", &CLstr, 8657273Sbostic "cm", &CMstr, 8757273Sbostic #ifdef unneeded 8857273Sbostic "cr", &CRstr, 8957273Sbostic #endif 9057273Sbostic "le", &BC, /* move cursor left one space */ 9157273Sbostic "pc", &pcstr, 9257273Sbostic "se", &SEstr, 9357273Sbostic "so", &SOstr, 9457273Sbostic "te", &TEstr, 9557273Sbostic "ti", &TIstr, 9657273Sbostic "up", &UP, /* cursor up */ 9757273Sbostic 0 9857273Sbostic }; 9957273Sbostic 10057273Sbostic /* This is where we will actually stuff the information */ 10157273Sbostic 10257273Sbostic static char combuf[1024], tbuf[1024]; 10357273Sbostic 10457273Sbostic 10557273Sbostic /* 10657273Sbostic * Routine used by tputs(). 10757273Sbostic */ 10857273Sbostic int 10957273Sbostic put(c) 11057273Sbostic int c; 11157273Sbostic { 11257273Sbostic 11357273Sbostic return (putchar(c)); 11457273Sbostic } 11557273Sbostic 11657273Sbostic /* 11757273Sbostic * putstr() is for unpadded strings (either as in termcap(5) or 11857273Sbostic * simply literal strings); putpad() is for padded strings with 11957273Sbostic * count=1. (See screen.h for putpad().) 12057273Sbostic */ 12157273Sbostic #define putstr(s) (void)fputs(s, stdout) 12257273Sbostic #define moveto(r, c) putpad(tgoto(CMstr, c, r)) 12357273Sbostic 12457273Sbostic /* 12557273Sbostic * Set up from termcap. 12657273Sbostic */ 12757273Sbostic void 12857273Sbostic scr_init() 12957273Sbostic { 13057273Sbostic static int bsflag, xsflag, sgnum; 13157273Sbostic #ifdef unneeded 13257273Sbostic static int ncflag; 13357273Sbostic #endif 13457273Sbostic char *term, *fill; 13557273Sbostic static struct tcninfo { /* termcap numeric and flag info */ 13657273Sbostic char tcname[3]; 13757273Sbostic int *tcaddr; 13857273Sbostic } tcflags[] = { 13957273Sbostic "bs", &bsflag, 14057273Sbostic "ms", &MSflag, 14157273Sbostic #ifdef unneeded 14257273Sbostic "nc", &ncflag, 14357273Sbostic #endif 14457273Sbostic "xs", &xsflag, 14557273Sbostic 0 14657273Sbostic }, tcnums[] = { 14757273Sbostic "co", &COnum, 14857273Sbostic "li", &LInum, 14957273Sbostic "sg", &sgnum, 15057273Sbostic 0 15157273Sbostic }; 15257273Sbostic 15357273Sbostic if ((term = getenv("TERM")) == NULL) 15457273Sbostic stop("you must set the TERM environment variable"); 15557273Sbostic if (tgetent(tbuf, term) <= 0) 15657273Sbostic stop("cannot find your termcap"); 15757273Sbostic fill = combuf; 15857273Sbostic { 15957273Sbostic register struct tcsinfo *p; 16057273Sbostic 16157273Sbostic for (p = tcstrings; p->tcaddr; p++) 16257273Sbostic *p->tcaddr = tgetstr(p->tcname, &fill); 16357273Sbostic } 16457273Sbostic { 16557273Sbostic register struct tcninfo *p; 16657273Sbostic 16757273Sbostic for (p = tcflags; p->tcaddr; p++) 16857273Sbostic *p->tcaddr = tgetflag(p->tcname); 16957273Sbostic for (p = tcnums; p->tcaddr; p++) 17057273Sbostic *p->tcaddr = tgetnum(p->tcname); 17157273Sbostic } 17257273Sbostic if (bsflag) 17357273Sbostic BC = "\b"; 17457273Sbostic else if (BC == NULL && bcstr != NULL) 17557273Sbostic BC = bcstr; 17657273Sbostic if (CLstr == NULL) 17757273Sbostic stop("cannot clear screen"); 17857273Sbostic if (CMstr == NULL || UP == NULL || BC == NULL) 17957273Sbostic stop("cannot do random cursor positioning via tgoto()"); 18057273Sbostic PC = pcstr ? *pcstr : 0; 18157273Sbostic if (sgnum >= 0 || xsflag) 18257273Sbostic SOstr = SEstr = NULL; 18357273Sbostic #ifdef unneeded 18457273Sbostic if (ncflag) 18557273Sbostic CRstr = NULL; 18657273Sbostic else if (CRstr == NULL) 18757273Sbostic CRstr = "\r"; 18857273Sbostic #endif 18957273Sbostic } 19057273Sbostic 19157273Sbostic /* this foolery is needed to modify tty state `atomically' */ 19257273Sbostic static jmp_buf scr_onstop; 19357273Sbostic 19457273Sbostic #define sigunblock(mask) sigsetmask(sigblock(0) & ~(mask)) 19557273Sbostic 19657273Sbostic static void 19757273Sbostic stopset(sig) 19857273Sbostic int sig; 19957273Sbostic { 20057273Sbostic (void) signal(sig, SIG_DFL); 20157273Sbostic (void) kill(getpid(), sig); 20257273Sbostic (void) sigunblock(sigmask(sig)); 20357273Sbostic longjmp(scr_onstop, 1); 20457273Sbostic } 20557273Sbostic 20657273Sbostic static void 20757273Sbostic scr_stop() 20857273Sbostic { 20957273Sbostic scr_end(); 21057273Sbostic (void) kill(getpid(), SIGTSTP); 21157273Sbostic (void) sigunblock(sigmask(SIGTSTP)); 21257273Sbostic scr_set(); 21357273Sbostic scr_msg(key_msg, 1); 21457273Sbostic } 21557273Sbostic 21657273Sbostic /* 21757273Sbostic * Set up screen mode. 21857273Sbostic */ 21957273Sbostic void 22057273Sbostic scr_set() 22157273Sbostic { 22257273Sbostic struct winsize ws; 22357273Sbostic struct sgttyb newtt; 22457273Sbostic volatile int omask; 22557273Sbostic void (*ttou)(); 22657273Sbostic 22757273Sbostic omask = sigblock(sigmask(SIGTSTP) | sigmask(SIGTTOU)); 22857273Sbostic if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN) 22957273Sbostic (void) signal(SIGTSTP, SIG_IGN); 23057273Sbostic if ((ttou = signal(SIGTSTP, stopset)) == SIG_IGN) 23157273Sbostic (void) signal(SIGTSTP, SIG_IGN); 23257273Sbostic /* 23357273Sbostic * At last, we are ready to modify the tty state. If 23457273Sbostic * we stop while at it, stopset() above will longjmp back 23557273Sbostic * to the setjmp here and we will start over. 23657273Sbostic */ 23757273Sbostic (void) setjmp(scr_onstop); 23857273Sbostic (void) sigsetmask(omask); 23957273Sbostic Rows = 0, Cols = 0; 24057273Sbostic if (ioctl(0, TIOCGWINSZ, &ws) == 0) { 24157273Sbostic Rows = ws.ws_row; 24257273Sbostic Cols = ws.ws_col; 24357273Sbostic } 24457273Sbostic if (Rows == 0) 24557273Sbostic Rows = LInum; 24657273Sbostic if (Cols == 0) 24757273Sbostic Cols = COnum; 24857273Sbostic if (Rows < MINROWS || Cols < MINCOLS) { 24957273Sbostic (void) fprintf(stderr, 25057273Sbostic "the screen is too small: must be at least %d x %d", 25157273Sbostic MINROWS, MINCOLS); 25257273Sbostic stop(""); /* stop() supplies \n */ 25357273Sbostic } 25457273Sbostic if (ioctl(0, TIOCGETP, &oldtt)) 25557273Sbostic stop("ioctl(TIOCGETP) fails"); 25657273Sbostic newtt = oldtt; 25757273Sbostic newtt.sg_flags = (newtt.sg_flags | CBREAK) & ~(CRMOD | ECHO); 25857273Sbostic if ((newtt.sg_flags & TBDELAY) == XTABS) 25957273Sbostic newtt.sg_flags &= ~TBDELAY; 26057273Sbostic if (ioctl(0, TIOCSETN, &newtt)) 26157273Sbostic stop("ioctl(TIOCSETN) fails"); 26257273Sbostic ospeed = newtt.sg_ospeed; 26357273Sbostic omask = sigblock(sigmask(SIGTSTP) | sigmask(SIGTTOU)); 26457273Sbostic 26557273Sbostic /* 26657273Sbostic * We made it. We are now in screen mode, modulo TIstr 26757273Sbostic * (which we will fix immediately). 26857273Sbostic */ 26957273Sbostic if (TIstr) 27057273Sbostic putstr(TIstr); /* termcap(5) says this is not padded */ 27157273Sbostic if (tstp != SIG_IGN) 27257273Sbostic (void) signal(SIGTSTP, scr_stop); 27357273Sbostic (void) signal(SIGTTOU, ttou); 27457273Sbostic 27557273Sbostic isset = 1; 27657273Sbostic (void) sigsetmask(omask); 27757273Sbostic scr_clear(); 27857273Sbostic } 27957273Sbostic 28057273Sbostic /* 28157273Sbostic * End screen mode. 28257273Sbostic */ 28357273Sbostic void 28457273Sbostic scr_end() 28557273Sbostic { 28657273Sbostic int omask = sigblock(sigmask(SIGTSTP) | sigmask(SIGTTOU)); 28757273Sbostic 28857273Sbostic /* move cursor to last line */ 28957273Sbostic if (LLstr) 29057273Sbostic putstr(LLstr); /* termcap(5) says this is not padded */ 29157273Sbostic else 29257273Sbostic moveto(Rows - 1, 0); 29357273Sbostic /* exit screen mode */ 29457273Sbostic if (TEstr) 29557273Sbostic putstr(TEstr); /* termcap(5) says this is not padded */ 29657273Sbostic (void) fflush(stdout); 29757273Sbostic (void) ioctl(0, TIOCSETN, &oldtt); 29857273Sbostic isset = 0; 29957273Sbostic /* restore signals */ 30057273Sbostic (void) signal(SIGTSTP, tstp); 30157273Sbostic (void) sigsetmask(omask); 30257273Sbostic } 30357273Sbostic 30457273Sbostic void 30557273Sbostic stop(why) 30657273Sbostic char *why; 30757273Sbostic { 30857273Sbostic 30957273Sbostic if (isset) 31057273Sbostic scr_end(); 31157273Sbostic (void) fprintf(stderr, "aborting: %s\n", why); 31257273Sbostic exit(1); 31357273Sbostic } 31457273Sbostic 31557273Sbostic /* 31657273Sbostic * Clear the screen, forgetting the current contents in the process. 31757273Sbostic */ 31857273Sbostic void 31957273Sbostic scr_clear() 32057273Sbostic { 32157273Sbostic 32257273Sbostic putpad(CLstr); 32357273Sbostic curscore = -1; 32457273Sbostic bzero((char *)curscreen, sizeof(curscreen)); 32557273Sbostic } 32657273Sbostic 32757273Sbostic #if vax && !__GNUC__ 32857273Sbostic typedef int regcell; /* pcc is bad at `register char', etc */ 32957273Sbostic #else 33057273Sbostic typedef cell regcell; 33157273Sbostic #endif 33257273Sbostic 33357273Sbostic /* 33457273Sbostic * Update the screen. 33557273Sbostic */ 33657273Sbostic void 33757273Sbostic scr_update() 33857273Sbostic { 33957273Sbostic register cell *bp, *sp; 34057273Sbostic register regcell so, cur_so = 0; 34157273Sbostic register int i, ccol, j; 34257273Sbostic int omask = sigblock(sigmask(SIGTSTP)); 34357273Sbostic 34457273Sbostic /* always leave cursor after last displayed point */ 34557273Sbostic curscreen[D_LAST * B_COLS - 1] = -1; 34657273Sbostic 34757273Sbostic if (score != curscore) { 34857273Sbostic if (HOstr) 34957273Sbostic putpad(HOstr); 35057273Sbostic else 35157273Sbostic moveto(0, 0); 35257273Sbostic (void) printf("%d", score); 35357273Sbostic curscore = score; 35457273Sbostic } 35557273Sbostic 35657273Sbostic bp = &board[D_FIRST * B_COLS]; 35757273Sbostic sp = &curscreen[D_FIRST * B_COLS]; 35857273Sbostic for (j = D_FIRST; j < D_LAST; j++) { 35957273Sbostic ccol = -1; 36057273Sbostic for (i = 0; i < B_COLS; bp++, sp++, i++) { 36157273Sbostic if (*sp == (so = *bp)) 36257273Sbostic continue; 36357273Sbostic *sp = so; 36457273Sbostic if (i != ccol) { 36557273Sbostic if (cur_so && MSflag) { 36657273Sbostic putpad(SEstr); 36757273Sbostic cur_so = 0; 36857273Sbostic } 36957273Sbostic moveto(RTOD(j), CTOD(i)); 37057273Sbostic } 37157273Sbostic if (SOstr) { 37257273Sbostic if (so != cur_so) { 37357273Sbostic putpad(so ? SOstr : SEstr); 37457273Sbostic cur_so = so; 37557273Sbostic } 37657273Sbostic putstr(" "); 37757273Sbostic } else 37857273Sbostic putstr(so ? "XX" : " "); 37957273Sbostic ccol = i + 1; 38057273Sbostic /* 38157273Sbostic * Look ahead a bit, to avoid extra motion if 38257273Sbostic * we will be redrawing the cell after the next. 38357273Sbostic * Motion probably takes four or more characters, 38457273Sbostic * so we save even if we rewrite two cells 38557273Sbostic * `unnecessarily'. Skip it all, though, if 38657273Sbostic * the next cell is a different color. 38757273Sbostic */ 38857273Sbostic #define STOP (B_COLS - 3) 38957273Sbostic if (i > STOP || sp[1] != bp[1] || so != bp[1]) 39057273Sbostic continue; 39157273Sbostic if (sp[2] != bp[2]) 39257273Sbostic sp[1] = -1; 39357273Sbostic else if (i < STOP && so == bp[2] && sp[3] != bp[3]) { 39457273Sbostic sp[2] = -1; 39557273Sbostic sp[1] = -1; 39657273Sbostic } 39757273Sbostic } 39857273Sbostic } 39957273Sbostic if (cur_so) 40057273Sbostic putpad(SEstr); 40157273Sbostic (void) fflush(stdout); 40257273Sbostic (void) sigsetmask(omask); 40357273Sbostic } 40457273Sbostic 40557273Sbostic /* 40657273Sbostic * Write a message (set!=0), or clear the same message (set==0). 40757273Sbostic * (We need its length in case we have to overwrite with blanks.) 40857273Sbostic */ 40957273Sbostic void 41057273Sbostic scr_msg(s, set) 41157273Sbostic register char *s; 41257273Sbostic int set; 41357273Sbostic { 41457273Sbostic 41557273Sbostic if (set || CEstr == NULL) { 41657273Sbostic register int l = strlen(s); 41757273Sbostic 41857273Sbostic moveto(Rows - 2, ((Cols - l) >> 1) - 1); 41957273Sbostic if (set) 42057273Sbostic putstr(s); 42157273Sbostic else 42257273Sbostic while (--l >= 0) 42357273Sbostic (void) putchar(' '); 42457273Sbostic } else { 42557273Sbostic moveto(Rows - 2, 0); 42657273Sbostic putpad(CEstr); 42757273Sbostic } 42857273Sbostic } 429