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