1 1.34 rillig /* $NetBSD: screen.c,v 1.34 2021/05/02 12:50:46 rillig Exp $ */ 2 1.2 cgd 3 1.1 cgd /*- 4 1.1 cgd * Copyright (c) 1992, 1993 5 1.1 cgd * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * This code is derived from software contributed to Berkeley by 8 1.1 cgd * Chris Torek and Darren F. Provine. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.18 agc * 3. Neither the name of the University nor the names of its contributors 19 1.1 cgd * may be used to endorse or promote products derived from this software 20 1.1 cgd * without specific prior written permission. 21 1.1 cgd * 22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 cgd * SUCH DAMAGE. 33 1.1 cgd * 34 1.1 cgd * @(#)screen.c 8.1 (Berkeley) 5/31/93 35 1.1 cgd */ 36 1.1 cgd 37 1.1 cgd /* 38 1.1 cgd * Tetris screen control. 39 1.1 cgd */ 40 1.1 cgd 41 1.20 perry #include <sys/cdefs.h> 42 1.1 cgd #include <sys/ioctl.h> 43 1.1 cgd 44 1.1 cgd #include <setjmp.h> 45 1.1 cgd #include <signal.h> 46 1.1 cgd #include <stdio.h> 47 1.1 cgd #include <stdlib.h> 48 1.1 cgd #include <string.h> 49 1.26 roy #include <term.h> 50 1.3 mycroft #include <termios.h> 51 1.1 cgd #include <unistd.h> 52 1.1 cgd 53 1.1 cgd #ifndef sigmask 54 1.1 cgd #define sigmask(s) (1 << ((s) - 1)) 55 1.1 cgd #endif 56 1.1 cgd 57 1.1 cgd #include "screen.h" 58 1.1 cgd #include "tetris.h" 59 1.1 cgd 60 1.1 cgd static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */ 61 1.1 cgd static int curscore; 62 1.1 cgd static int isset; /* true => terminal is in game mode */ 63 1.3 mycroft static struct termios oldtt; 64 1.19 jsm static void (*tstp)(int); 65 1.1 cgd 66 1.19 jsm static void scr_stop(int); 67 1.21 perry static void stopset(int) __dead; 68 1.1 cgd 69 1.1 cgd 70 1.1 cgd /* 71 1.1 cgd * Routine used by tputs(). 72 1.1 cgd */ 73 1.13 lukem int 74 1.23 dholland put(int c) 75 1.1 cgd { 76 1.1 cgd 77 1.13 lukem return (putchar(c)); 78 1.1 cgd } 79 1.1 cgd 80 1.1 cgd /* 81 1.1 cgd * putstr() is for unpadded strings (either as in termcap(5) or 82 1.1 cgd * simply literal strings); putpad() is for padded strings with 83 1.1 cgd * count=1. (See screen.h for putpad().) 84 1.1 cgd */ 85 1.1 cgd #define putstr(s) (void)fputs(s, stdout) 86 1.14 blymn 87 1.24 dholland static void 88 1.14 blymn moveto(int r, int c) 89 1.14 blymn { 90 1.26 roy char *buf; 91 1.14 blymn 92 1.27 roy buf = tiparm(cursor_address, r, c); 93 1.26 roy if (buf != NULL) 94 1.14 blymn putpad(buf); 95 1.14 blymn } 96 1.1 cgd 97 1.28 christos static void 98 1.28 christos setcolor(int c) 99 1.28 christos { 100 1.28 christos char *buf; 101 1.30 nat char monochrome[] = "\033[0m"; 102 1.29 pgoyette if (nocolor == 1) 103 1.29 pgoyette return; 104 1.28 christos if (set_a_foreground == NULL) 105 1.28 christos return; 106 1.28 christos 107 1.30 nat if (c == 0 || c == 7) 108 1.30 nat buf = monochrome; 109 1.30 nat else 110 1.30 nat buf = tiparm(set_a_foreground, c); 111 1.28 christos if (buf != NULL) 112 1.28 christos putpad(buf); 113 1.28 christos } 114 1.28 christos 115 1.1 cgd /* 116 1.1 cgd * Set up from termcap. 117 1.1 cgd */ 118 1.1 cgd void 119 1.23 dholland scr_init(void) 120 1.1 cgd { 121 1.1 cgd 122 1.26 roy setupterm(NULL, 0, NULL); 123 1.26 roy if (clear_screen == NULL) 124 1.1 cgd stop("cannot clear screen"); 125 1.26 roy if (cursor_address == NULL || cursor_up == NULL) 126 1.26 roy stop("cannot do random cursor positioning"); 127 1.1 cgd } 128 1.1 cgd 129 1.1 cgd /* this foolery is needed to modify tty state `atomically' */ 130 1.1 cgd static jmp_buf scr_onstop; 131 1.1 cgd 132 1.1 cgd static void 133 1.23 dholland stopset(int sig) 134 1.1 cgd { 135 1.22 dholland sigset_t set; 136 1.3 mycroft 137 1.1 cgd (void) signal(sig, SIG_DFL); 138 1.1 cgd (void) kill(getpid(), sig); 139 1.22 dholland sigemptyset(&set); 140 1.22 dholland sigaddset(&set, sig); 141 1.22 dholland (void) sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)0); 142 1.1 cgd longjmp(scr_onstop, 1); 143 1.1 cgd } 144 1.1 cgd 145 1.1 cgd static void 146 1.23 dholland scr_stop(int sig) 147 1.1 cgd { 148 1.22 dholland sigset_t set; 149 1.3 mycroft 150 1.1 cgd scr_end(); 151 1.3 mycroft (void) kill(getpid(), sig); 152 1.22 dholland sigemptyset(&set); 153 1.22 dholland sigaddset(&set, sig); 154 1.22 dholland (void) sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)0); 155 1.1 cgd scr_set(); 156 1.1 cgd scr_msg(key_msg, 1); 157 1.1 cgd } 158 1.1 cgd 159 1.1 cgd /* 160 1.1 cgd * Set up screen mode. 161 1.1 cgd */ 162 1.1 cgd void 163 1.23 dholland scr_set(void) 164 1.1 cgd { 165 1.1 cgd struct winsize ws; 166 1.3 mycroft struct termios newtt; 167 1.22 dholland sigset_t nsigset, osigset; 168 1.19 jsm void (*ttou)(int); 169 1.1 cgd 170 1.22 dholland sigemptyset(&nsigset); 171 1.22 dholland sigaddset(&nsigset, SIGTSTP); 172 1.22 dholland sigaddset(&nsigset, SIGTTOU); 173 1.22 dholland (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset); 174 1.1 cgd if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN) 175 1.1 cgd (void) signal(SIGTSTP, SIG_IGN); 176 1.3 mycroft if ((ttou = signal(SIGTTOU, stopset)) == SIG_IGN) 177 1.3 mycroft (void) signal(SIGTTOU, SIG_IGN); 178 1.1 cgd /* 179 1.1 cgd * At last, we are ready to modify the tty state. If 180 1.1 cgd * we stop while at it, stopset() above will longjmp back 181 1.1 cgd * to the setjmp here and we will start over. 182 1.1 cgd */ 183 1.1 cgd (void) setjmp(scr_onstop); 184 1.3 mycroft (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); 185 1.1 cgd Rows = 0, Cols = 0; 186 1.1 cgd if (ioctl(0, TIOCGWINSZ, &ws) == 0) { 187 1.1 cgd Rows = ws.ws_row; 188 1.1 cgd Cols = ws.ws_col; 189 1.1 cgd } 190 1.1 cgd if (Rows == 0) 191 1.26 roy Rows = lines; 192 1.1 cgd if (Cols == 0) 193 1.26 roy Cols = columns; 194 1.1 cgd if (Rows < MINROWS || Cols < MINCOLS) { 195 1.1 cgd (void) fprintf(stderr, 196 1.8 hubertf "the screen is too small: must be at least %dx%d, ", 197 1.8 hubertf MINCOLS, MINROWS); 198 1.1 cgd stop(""); /* stop() supplies \n */ 199 1.1 cgd } 200 1.32 nat Offset = (Rows - D_LAST + D_FIRST - 2) / 2; 201 1.3 mycroft if (tcgetattr(0, &oldtt) < 0) 202 1.3 mycroft stop("tcgetattr() fails"); 203 1.1 cgd newtt = oldtt; 204 1.3 mycroft newtt.c_lflag &= ~(ICANON|ECHO); 205 1.3 mycroft newtt.c_oflag &= ~OXTABS; 206 1.3 mycroft if (tcsetattr(0, TCSADRAIN, &newtt) < 0) 207 1.3 mycroft stop("tcsetattr() fails"); 208 1.3 mycroft ospeed = cfgetospeed(&newtt); 209 1.22 dholland (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset); 210 1.1 cgd 211 1.1 cgd /* 212 1.1 cgd * We made it. We are now in screen mode, modulo TIstr 213 1.1 cgd * (which we will fix immediately). 214 1.1 cgd */ 215 1.33 christos const char *tstr; 216 1.33 christos if ((tstr = enter_ca_mode) != NULL) 217 1.33 christos putstr(tstr); 218 1.33 christos if ((tstr = cursor_invisible) != NULL) 219 1.33 christos putstr(tstr); 220 1.1 cgd if (tstp != SIG_IGN) 221 1.1 cgd (void) signal(SIGTSTP, scr_stop); 222 1.3 mycroft if (ttou != SIG_IGN) 223 1.3 mycroft (void) signal(SIGTTOU, ttou); 224 1.1 cgd 225 1.1 cgd isset = 1; 226 1.3 mycroft (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); 227 1.1 cgd scr_clear(); 228 1.1 cgd } 229 1.1 cgd 230 1.1 cgd /* 231 1.1 cgd * End screen mode. 232 1.1 cgd */ 233 1.1 cgd void 234 1.23 dholland scr_end(void) 235 1.1 cgd { 236 1.22 dholland sigset_t nsigset, osigset; 237 1.1 cgd 238 1.22 dholland sigemptyset(&nsigset); 239 1.22 dholland sigaddset(&nsigset, SIGTSTP); 240 1.22 dholland sigaddset(&nsigset, SIGTTOU); 241 1.22 dholland (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset); 242 1.1 cgd /* move cursor to last line */ 243 1.33 christos const char *tstr; 244 1.33 christos if ((tstr = cursor_to_ll) != NULL) 245 1.33 christos putstr(tstr); 246 1.1 cgd else 247 1.1 cgd moveto(Rows - 1, 0); 248 1.1 cgd /* exit screen mode */ 249 1.33 christos if ((tstr = exit_ca_mode) != NULL) 250 1.33 christos putstr(tstr); 251 1.33 christos if ((tstr = cursor_normal) != NULL) 252 1.33 christos putstr(tstr); 253 1.1 cgd (void) fflush(stdout); 254 1.3 mycroft (void) tcsetattr(0, TCSADRAIN, &oldtt); 255 1.1 cgd isset = 0; 256 1.1 cgd /* restore signals */ 257 1.1 cgd (void) signal(SIGTSTP, tstp); 258 1.3 mycroft (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); 259 1.1 cgd } 260 1.1 cgd 261 1.1 cgd void 262 1.23 dholland stop(const char *why) 263 1.1 cgd { 264 1.1 cgd 265 1.1 cgd if (isset) 266 1.1 cgd scr_end(); 267 1.1 cgd (void) fprintf(stderr, "aborting: %s\n", why); 268 1.1 cgd exit(1); 269 1.1 cgd } 270 1.1 cgd 271 1.1 cgd /* 272 1.1 cgd * Clear the screen, forgetting the current contents in the process. 273 1.1 cgd */ 274 1.1 cgd void 275 1.23 dholland scr_clear(void) 276 1.1 cgd { 277 1.1 cgd 278 1.26 roy putpad(clear_screen); 279 1.1 cgd curscore = -1; 280 1.7 perry memset((char *)curscreen, 0, sizeof(curscreen)); 281 1.1 cgd } 282 1.1 cgd 283 1.1 cgd #if vax && !__GNUC__ 284 1.1 cgd typedef int regcell; /* pcc is bad at `register char', etc */ 285 1.1 cgd #else 286 1.1 cgd typedef cell regcell; 287 1.1 cgd #endif 288 1.1 cgd 289 1.1 cgd /* 290 1.1 cgd * Update the screen. 291 1.1 cgd */ 292 1.1 cgd void 293 1.23 dholland scr_update(void) 294 1.1 cgd { 295 1.17 wiz cell *bp, *sp; 296 1.17 wiz regcell so, cur_so = 0; 297 1.17 wiz int i, ccol, j; 298 1.22 dholland sigset_t nsigset, osigset; 299 1.11 jsm static const struct shape *lastshape; 300 1.3 mycroft 301 1.22 dholland sigemptyset(&nsigset); 302 1.22 dholland sigaddset(&nsigset, SIGTSTP); 303 1.22 dholland (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset); 304 1.1 cgd 305 1.1 cgd /* always leave cursor after last displayed point */ 306 1.1 cgd curscreen[D_LAST * B_COLS - 1] = -1; 307 1.1 cgd 308 1.1 cgd if (score != curscore) { 309 1.26 roy if (cursor_home) 310 1.26 roy putpad(cursor_home); 311 1.1 cgd else 312 1.1 cgd moveto(0, 0); 313 1.30 nat setcolor(0); 314 1.8 hubertf (void) printf("Score: %d", score); 315 1.1 cgd curscore = score; 316 1.1 cgd } 317 1.1 cgd 318 1.8 hubertf /* draw preview of nextpattern */ 319 1.9 hubertf if (showpreview && (nextshape != lastshape)) { 320 1.8 hubertf static int r=5, c=2; 321 1.34 rillig int tr, tc, t; 322 1.8 hubertf 323 1.8 hubertf lastshape = nextshape; 324 1.34 rillig 325 1.8 hubertf /* clean */ 326 1.26 roy putpad(exit_standout_mode); 327 1.8 hubertf moveto(r-1, c-1); putstr(" "); 328 1.8 hubertf moveto(r, c-1); putstr(" "); 329 1.8 hubertf moveto(r+1, c-1); putstr(" "); 330 1.8 hubertf moveto(r+2, c-1); putstr(" "); 331 1.8 hubertf 332 1.8 hubertf moveto(r-3, c-2); 333 1.8 hubertf putstr("Next shape:"); 334 1.34 rillig 335 1.8 hubertf /* draw */ 336 1.31 christos setcolor(nextshape->color); 337 1.26 roy putpad(enter_standout_mode); 338 1.8 hubertf moveto(r, 2*c); 339 1.8 hubertf putstr(" "); 340 1.8 hubertf for(i=0; i<3; i++) { 341 1.8 hubertf t = c + r*B_COLS; 342 1.8 hubertf t += nextshape->off[i]; 343 1.8 hubertf 344 1.8 hubertf tr = t / B_COLS; 345 1.8 hubertf tc = t % B_COLS; 346 1.8 hubertf 347 1.8 hubertf moveto(tr, 2*tc); 348 1.8 hubertf putstr(" "); 349 1.8 hubertf } 350 1.26 roy putpad(exit_standout_mode); 351 1.8 hubertf } 352 1.34 rillig 353 1.1 cgd bp = &board[D_FIRST * B_COLS]; 354 1.1 cgd sp = &curscreen[D_FIRST * B_COLS]; 355 1.1 cgd for (j = D_FIRST; j < D_LAST; j++) { 356 1.1 cgd ccol = -1; 357 1.1 cgd for (i = 0; i < B_COLS; bp++, sp++, i++) { 358 1.1 cgd if (*sp == (so = *bp)) 359 1.1 cgd continue; 360 1.1 cgd *sp = so; 361 1.1 cgd if (i != ccol) { 362 1.26 roy if (cur_so && move_standout_mode) { 363 1.26 roy putpad(exit_standout_mode); 364 1.1 cgd cur_so = 0; 365 1.1 cgd } 366 1.32 nat moveto(RTOD(j + Offset), CTOD(i)); 367 1.1 cgd } 368 1.26 roy if (enter_standout_mode) { 369 1.1 cgd if (so != cur_so) { 370 1.30 nat setcolor(so); 371 1.26 roy putpad(so ? 372 1.26 roy enter_standout_mode : 373 1.26 roy exit_standout_mode); 374 1.1 cgd cur_so = so; 375 1.1 cgd } 376 1.28 christos #ifdef DEBUG 377 1.28 christos char buf[3]; 378 1.28 christos snprintf(buf, sizeof(buf), "%d%d", so, so); 379 1.28 christos putstr(buf); 380 1.28 christos #else 381 1.1 cgd putstr(" "); 382 1.28 christos #endif 383 1.1 cgd } else 384 1.1 cgd putstr(so ? "XX" : " "); 385 1.1 cgd ccol = i + 1; 386 1.1 cgd /* 387 1.1 cgd * Look ahead a bit, to avoid extra motion if 388 1.1 cgd * we will be redrawing the cell after the next. 389 1.1 cgd * Motion probably takes four or more characters, 390 1.1 cgd * so we save even if we rewrite two cells 391 1.1 cgd * `unnecessarily'. Skip it all, though, if 392 1.1 cgd * the next cell is a different color. 393 1.1 cgd */ 394 1.1 cgd #define STOP (B_COLS - 3) 395 1.1 cgd if (i > STOP || sp[1] != bp[1] || so != bp[1]) 396 1.1 cgd continue; 397 1.1 cgd if (sp[2] != bp[2]) 398 1.1 cgd sp[1] = -1; 399 1.1 cgd else if (i < STOP && so == bp[2] && sp[3] != bp[3]) { 400 1.1 cgd sp[2] = -1; 401 1.1 cgd sp[1] = -1; 402 1.1 cgd } 403 1.1 cgd } 404 1.1 cgd } 405 1.1 cgd if (cur_so) 406 1.26 roy putpad(exit_standout_mode); 407 1.1 cgd (void) fflush(stdout); 408 1.3 mycroft (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); 409 1.1 cgd } 410 1.1 cgd 411 1.1 cgd /* 412 1.1 cgd * Write a message (set!=0), or clear the same message (set==0). 413 1.1 cgd * (We need its length in case we have to overwrite with blanks.) 414 1.1 cgd */ 415 1.1 cgd void 416 1.23 dholland scr_msg(char *s, int set) 417 1.1 cgd { 418 1.34 rillig 419 1.26 roy if (set || clr_eol == NULL) { 420 1.17 wiz int l = strlen(s); 421 1.1 cgd 422 1.1 cgd moveto(Rows - 2, ((Cols - l) >> 1) - 1); 423 1.1 cgd if (set) 424 1.1 cgd putstr(s); 425 1.1 cgd else 426 1.1 cgd while (--l >= 0) 427 1.1 cgd (void) putchar(' '); 428 1.1 cgd } else { 429 1.1 cgd moveto(Rows - 2, 0); 430 1.26 roy putpad(clr_eol); 431 1.1 cgd } 432 1.1 cgd } 433