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);