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