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