]> git.cameronkatri.com Git - bsdgames-darwin.git/blob - tetris/screen.c
install games files with the correct permissions.
[bsdgames-darwin.git] / tetris / screen.c
1 /* $NetBSD: screen.c,v 1.6 1997/10/14 01:14:28 lukem 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 <sys/ioctl.h>
46
47 #include <setjmp.h>
48 #include <signal.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <termcap.h>
53 #include <termios.h>
54 #include <unistd.h>
55
56 #ifndef sigmask
57 #define sigmask(s) (1 << ((s) - 1))
58 #endif
59
60 #include "screen.h"
61 #include "tetris.h"
62
63 static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */
64 static int curscore;
65 static int isset; /* true => terminal is in game mode */
66 static struct termios oldtt;
67 static void (*tstp) __P((int));
68
69 static void scr_stop __P((int));
70 static void stopset __P((int));
71
72
73 /*
74 * Capabilities from TERMCAP.
75 */
76 char PC, *BC, *UP; /* tgoto requires globals: ugh! */
77 speed_t ospeed;
78
79 static char
80 *bcstr, /* backspace char */
81 *CEstr, /* clear to end of line */
82 *CLstr, /* clear screen */
83 *CMstr, /* cursor motion string */
84 #ifdef unneeded
85 *CRstr, /* "\r" equivalent */
86 #endif
87 *HOstr, /* cursor home */
88 *LLstr, /* last line, first column */
89 *pcstr, /* pad character */
90 *TEstr, /* end cursor motion mode */
91 *TIstr; /* begin cursor motion mode */
92 char
93 *SEstr, /* end standout mode */
94 *SOstr; /* begin standout mode */
95 static int
96 COnum, /* co# value */
97 LInum, /* li# value */
98 MSflag; /* can move in standout mode */
99
100
101 struct tcsinfo { /* termcap string info; some abbrevs above */
102 char tcname[3];
103 char **tcaddr;
104 } tcstrings[] = {
105 {"bc", &bcstr},
106 {"ce", &CEstr},
107 {"cl", &CLstr},
108 {"cm", &CMstr},
109 #ifdef unneeded
110 {"cr", &CRstr},
111 #endif
112 {"le", &BC}, /* move cursor left one space */
113 {"pc", &pcstr},
114 {"se", &SEstr},
115 {"so", &SOstr},
116 {"te", &TEstr},
117 {"ti", &TIstr},
118 {"up", &UP}, /* cursor up */
119 { {0}, NULL}
120 };
121
122 /* This is where we will actually stuff the information */
123
124 static char combuf[1024], tbuf[1024];
125
126
127 /*
128 * Routine used by tputs().
129 */
130 void
131 put(c)
132 int c;
133 {
134
135 (void) putchar(c);
136 }
137
138 /*
139 * putstr() is for unpadded strings (either as in termcap(5) or
140 * simply literal strings); putpad() is for padded strings with
141 * count=1. (See screen.h for putpad().)
142 */
143 #define putstr(s) (void)fputs(s, stdout)
144 #define moveto(r, c) putpad(tgoto(CMstr, c, r))
145
146 /*
147 * Set up from termcap.
148 */
149 void
150 scr_init()
151 {
152 static int bsflag, xsflag, sgnum;
153 #ifdef unneeded
154 static int ncflag;
155 #endif
156 char *term, *fill;
157 static struct tcninfo { /* termcap numeric and flag info */
158 char tcname[3];
159 int *tcaddr;
160 } tcflags[] = {
161 {"bs", &bsflag},
162 {"ms", &MSflag},
163 #ifdef unneeded
164 {"nc", &ncflag},
165 #endif
166 {"xs", &xsflag},
167 { {0}, NULL}
168 }, tcnums[] = {
169 {"co", &COnum},
170 {"li", &LInum},
171 {"sg", &sgnum},
172 { {0}, NULL}
173 };
174
175 if ((term = getenv("TERM")) == NULL)
176 stop("you must set the TERM environment variable");
177 if (tgetent(tbuf, term) <= 0)
178 stop("cannot find your termcap");
179 fill = combuf;
180 {
181 register struct tcsinfo *p;
182
183 for (p = tcstrings; p->tcaddr; p++)
184 *p->tcaddr = tgetstr(p->tcname, &fill);
185 }
186 {
187 register struct tcninfo *p;
188
189 for (p = tcflags; p->tcaddr; p++)
190 *p->tcaddr = tgetflag(p->tcname);
191 for (p = tcnums; p->tcaddr; p++)
192 *p->tcaddr = tgetnum(p->tcname);
193 }
194 if (bsflag)
195 BC = "\b";
196 else if (BC == NULL && bcstr != NULL)
197 BC = bcstr;
198 if (CLstr == NULL)
199 stop("cannot clear screen");
200 if (CMstr == NULL || UP == NULL || BC == NULL)
201 stop("cannot do random cursor positioning via tgoto()");
202 PC = pcstr ? *pcstr : 0;
203 if (sgnum >= 0 || xsflag)
204 SOstr = SEstr = NULL;
205 #ifdef unneeded
206 if (ncflag)
207 CRstr = NULL;
208 else if (CRstr == NULL)
209 CRstr = "\r";
210 #endif
211 }
212
213 /* this foolery is needed to modify tty state `atomically' */
214 static jmp_buf scr_onstop;
215
216 static void
217 stopset(sig)
218 int sig;
219 {
220 sigset_t sigset;
221
222 (void) signal(sig, SIG_DFL);
223 (void) kill(getpid(), sig);
224 sigemptyset(&sigset);
225 sigaddset(&sigset, sig);
226 (void) sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
227 longjmp(scr_onstop, 1);
228 }
229
230 static void
231 scr_stop(sig)
232 int sig;
233 {
234 sigset_t sigset;
235
236 scr_end();
237 (void) kill(getpid(), sig);
238 sigemptyset(&sigset);
239 sigaddset(&sigset, sig);
240 (void) sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
241 scr_set();
242 scr_msg(key_msg, 1);
243 }
244
245 /*
246 * Set up screen mode.
247 */
248 void
249 scr_set()
250 {
251 struct winsize ws;
252 struct termios newtt;
253 sigset_t sigset, osigset;
254 void (*ttou) __P((int));
255
256 sigemptyset(&sigset);
257 sigaddset(&sigset, SIGTSTP);
258 sigaddset(&sigset, SIGTTOU);
259 (void) sigprocmask(SIG_BLOCK, &sigset, &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 %d x %d",
283 MINROWS, MINCOLS);
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, &sigset, &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 */
315 void
316 scr_end()
317 {
318 sigset_t sigset, osigset;
319
320 sigemptyset(&sigset);
321 sigaddset(&sigset, SIGTSTP);
322 sigaddset(&sigset, SIGTTOU);
323 (void) sigprocmask(SIG_BLOCK, &sigset, &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
340 void
341 stop(why)
342 char *why;
343 {
344
345 if (isset)
346 scr_end();
347 (void) fprintf(stderr, "aborting: %s\n", why);
348 exit(1);
349 }
350
351 /*
352 * Clear the screen, forgetting the current contents in the process.
353 */
354 void
355 scr_clear()
356 {
357
358 putpad(CLstr);
359 curscore = -1;
360 bzero((char *)curscreen, sizeof(curscreen));
361 }
362
363 #if vax && !__GNUC__
364 typedef int regcell; /* pcc is bad at `register char', etc */
365 #else
366 typedef cell regcell;
367 #endif
368
369 /*
370 * Update the screen.
371 */
372 void
373 scr_update()
374 {
375 register cell *bp, *sp;
376 register regcell so, cur_so = 0;
377 register int i, ccol, j;
378 sigset_t sigset, osigset;
379
380 sigemptyset(&sigset);
381 sigaddset(&sigset, SIGTSTP);
382 (void) sigprocmask(SIG_BLOCK, &sigset, &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("%d", score);
393 curscore = score;
394 }
395
396 bp = &board[D_FIRST * B_COLS];
397 sp = &curscreen[D_FIRST * B_COLS];
398 for (j = D_FIRST; j < D_LAST; j++) {
399 ccol = -1;
400 for (i = 0; i < B_COLS; bp++, sp++, i++) {
401 if (*sp == (so = *bp))
402 continue;
403 *sp = so;
404 if (i != ccol) {
405 if (cur_so && MSflag) {
406 putpad(SEstr);
407 cur_so = 0;
408 }
409 moveto(RTOD(j), CTOD(i));
410 }
411 if (SOstr) {
412 if (so != cur_so) {
413 putpad(so ? SOstr : SEstr);
414 cur_so = so;
415 }
416 putstr(" ");
417 } else
418 putstr(so ? "XX" : " ");
419 ccol = i + 1;
420 /*
421 * Look ahead a bit, to avoid extra motion if
422 * we will be redrawing the cell after the next.
423 * Motion probably takes four or more characters,
424 * so we save even if we rewrite two cells
425 * `unnecessarily'. Skip it all, though, if
426 * the next cell is a different color.
427 */
428 #define STOP (B_COLS - 3)
429 if (i > STOP || sp[1] != bp[1] || so != bp[1])
430 continue;
431 if (sp[2] != bp[2])
432 sp[1] = -1;
433 else if (i < STOP && so == bp[2] && sp[3] != bp[3]) {
434 sp[2] = -1;
435 sp[1] = -1;
436 }
437 }
438 }
439 if (cur_so)
440 putpad(SEstr);
441 (void) fflush(stdout);
442 (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
443 }
444
445 /*
446 * Write a message (set!=0), or clear the same message (set==0).
447 * (We need its length in case we have to overwrite with blanks.)
448 */
449 void
450 scr_msg(s, set)
451 register char *s;
452 int set;
453 {
454
455 if (set || CEstr == NULL) {
456 register int l = strlen(s);
457
458 moveto(Rows - 2, ((Cols - l) >> 1) - 1);
459 if (set)
460 putstr(s);
461 else
462 while (--l >= 0)
463 (void) putchar(' ');
464 } else {
465 moveto(Rows - 2, 0);
466 putpad(CEstr);
467 }
468 }