1 /* $OpenBSD: screen.c,v 1.18 2017/04/16 18:04:02 tb Exp $ */
2 /* $NetBSD: screen.c,v 1.4 1995/04/29 01:11:36 mycroft Exp $ */
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Chris Torek and Darren F. Provine.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * @(#)screen.c 8.1 (Berkeley) 5/31/93
39 * Tetris screen control.
42 #include <sys/ioctl.h>
56 static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */
58 static int isset; /* true => terminal is in game mode */
59 static struct termios oldtt;
60 static void (*tstp)(int);
62 static void scr_stop(int);
63 static void stopset(int);
66 * Capabilities from TERMCAP.
68 extern char PC, *BC, *UP; /* tgoto requires globals: ugh! */
70 static char *bcstr; /* backspace char */
71 static char *CEstr; /* clear to end of line */
72 static char *CLstr; /* clear screen */
73 static char *CMstr; /* cursor motion string */
75 static char *CRstr; /* "\r" equivalent */
77 static char *HOstr; /* cursor home */
78 static char *LLstr; /* last line, first column */
79 static char *pcstr; /* pad character */
80 static char *TEstr; /* end cursor motion mode */
81 static char *TIstr; /* begin cursor motion mode */
82 static char *VIstr; /* make cursor invisible */
83 static char *VEstr; /* make cursor appear normal */
84 char *SEstr; /* end standout mode */
85 char *SOstr; /* begin standout mode */
86 static int COnum; /* co# value */
87 static int LInum; /* li# value */
88 static int MSflag; /* can move in standout mode */
91 struct tcsinfo { /* termcap string info; some abbrevs above */
102 {"le", &BC}, /* move cursor left one space */
110 {"up", &UP}, /* cursor up */
114 /* This is where we will actually stuff the information */
116 static char combuf[1024], tbuf[1024];
120 * Routine used by tputs().
129 * putstr() is for unpadded strings (either as in termcap(5) or
130 * simply literal strings); putpad() is for padded strings with
131 * count=1. (See screen.h for putpad().)
133 #define putstr(s) fputs(s, stdout)
134 #define moveto(r, c) putpad(tgoto(CMstr, c, r))
137 * Set up from termcap.
142 static int bsflag, xsflag, sgnum;
147 static struct tcninfo { /* termcap numeric and flag info */
165 if ((term = getenv("TERM")) == NULL)
166 stop("you must set the TERM environment variable");
167 if (tgetent(tbuf, term) <= 0)
168 stop("cannot find your termcap");
173 for (p = tcstrings; p->tcaddr; p++)
174 *p->tcaddr = tgetstr(p->tcname, &fill);
177 SOstr = SEstr = NULL;
181 for (p = tcflags; p->tcaddr; p++)
182 *p->tcaddr = tgetflag(p->tcname);
183 for (p = tcnums; p->tcaddr; p++)
184 *p->tcaddr = tgetnum(p->tcname);
187 BC = __DECONST(char *, "\b");
188 else if (BC == NULL && bcstr != NULL)
191 stop("cannot clear screen");
192 if (CMstr == NULL || UP == NULL || BC == NULL)
193 stop("cannot do random cursor positioning via tgoto()");
194 PC = pcstr ? *pcstr : 0;
195 if (sgnum > 0 || xsflag)
196 SOstr = SEstr = NULL;
200 else if (CRstr == NULL)
205 /* this foolery is needed to modify tty state `atomically' */
206 static jmp_buf scr_onstop;
213 signal(sig, SIG_DFL);
215 sigemptyset(&sigset);
216 sigaddset(&sigset, sig);
217 sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
218 longjmp(scr_onstop, 1);
228 sigemptyset(&sigset);
229 sigaddset(&sigset, sig);
230 sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
236 * Set up screen mode.
242 struct termios newtt;
243 sigset_t sigset, osigset;
246 sigemptyset(&sigset);
247 sigaddset(&sigset, SIGTSTP);
248 sigaddset(&sigset, SIGTTOU);
249 sigprocmask(SIG_BLOCK, &sigset, &osigset);
250 if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN)
251 signal(SIGTSTP, SIG_IGN);
252 if ((ttou = signal(SIGTTOU, stopset)) == SIG_IGN)
253 signal(SIGTTOU, SIG_IGN);
255 * At last, we are ready to modify the tty state. If
256 * we stop while at it, stopset() above will longjmp back
257 * to the setjmp here and we will start over.
260 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
262 if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
270 if (Rows < MINROWS || Cols < MINCOLS) {
273 snprintf(smallscr, sizeof(smallscr),
274 "the screen is too small (must be at least %dx%d)",
278 if (tcgetattr(0, &oldtt) < 0)
279 stop("tcgetattr() fails");
281 newtt.c_lflag &= ~(ICANON|ECHO);
282 newtt.c_oflag &= ~OXTABS;
283 if (tcsetattr(0, TCSADRAIN, &newtt) < 0)
284 stop("tcsetattr() fails");
285 sigprocmask(SIG_BLOCK, &sigset, &osigset);
288 * We made it. We are now in screen mode, modulo TIstr
289 * (which we will fix immediately).
292 putstr(TIstr); /* termcap(5) says this is not padded */
294 putstr(VIstr); /* termcap(5) says this is not padded */
296 signal(SIGTSTP, scr_stop);
298 signal(SIGTTOU, ttou);
301 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
311 sigset_t sigset, osigset;
313 sigemptyset(&sigset);
314 sigaddset(&sigset, SIGTSTP);
315 sigaddset(&sigset, SIGTTOU);
316 sigprocmask(SIG_BLOCK, &sigset, &osigset);
317 /* move cursor to last line */
319 putstr(LLstr); /* termcap(5) says this is not padded */
322 /* exit screen mode */
324 putstr(TEstr); /* termcap(5) says this is not padded */
326 putstr(VEstr); /* termcap(5) says this is not padded */
328 tcsetattr(0, TCSADRAIN, &oldtt);
330 /* restore signals */
331 signal(SIGTSTP, tstp);
332 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
336 stop(const char *why)
340 errx(1, "aborting: %s", why);
344 * Clear the screen, forgetting the current contents in the process.
351 memset((char *)curscreen, 0, sizeof(curscreen));
354 typedef cell regcell;
363 regcell so, cur_so = 0;
365 sigset_t sigset, osigset;
366 static const struct shape *lastshape;
368 sigemptyset(&sigset);
369 sigaddset(&sigset, SIGTSTP);
370 sigprocmask(SIG_BLOCK, &sigset, &osigset);
372 /* always leave cursor after last displayed point */
373 curscreen[D_LAST * B_COLS - 1] = -1;
375 if (score != curscore) {
380 printf("Score: %d", score);
384 /* draw preview of next pattern */
385 if (showpreview && (nextshape != lastshape)) {
389 lastshape = nextshape;
393 moveto(r-1, c-1); putstr(" ");
394 moveto(r, c-1); putstr(" ");
395 moveto(r+1, c-1); putstr(" ");
396 moveto(r+2, c-1); putstr(" ");
399 putstr("Next shape:");
405 putstr(SOstr ? " " : "[]");
406 for (i = 0; i < 3; i++) {
408 t += nextshape->off[i];
414 putstr(SOstr ? " " : "[]");
419 bp = &board[D_FIRST * B_COLS];
420 sp = &curscreen[D_FIRST * B_COLS];
421 for (j = D_FIRST; j < D_LAST; j++) {
423 for (i = 0; i < B_COLS; bp++, sp++, i++) {
424 if (*sp == (so = *bp))
428 if (cur_so && MSflag) {
432 moveto(RTOD(j), CTOD(i));
436 putpad(so ? SOstr : SEstr);
441 putstr(so ? "[]" : " ");
444 * Look ahead a bit, to avoid extra motion if
445 * we will be redrawing the cell after the next.
446 * Motion probably takes four or more characters,
447 * so we save even if we rewrite two cells
448 * `unnecessarily'. Skip it all, though, if
449 * the next cell is a different color.
451 #define STOP (B_COLS - 3)
452 if (i > STOP || sp[1] != bp[1] || so != bp[1])
456 else if (i < STOP && so == bp[2] && sp[3] != bp[3]) {
465 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
469 * Write a message (set!=0), or clear the same message (set==0).
470 * (We need its length in case we have to overwrite with blanks.)
473 scr_msg(char *s, int set)
475 if (set || CEstr == NULL) {
478 moveto(Rows - 2, ((Cols - l) >> 1) - 1);