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