diff options
Diffstat (limited to 'games/tetris/screen.c')
-rw-r--r-- | games/tetris/screen.c | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/games/tetris/screen.c b/games/tetris/screen.c new file mode 100644 index 000000000000..9662ed015570 --- /dev/null +++ b/games/tetris/screen.c @@ -0,0 +1,454 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)screen.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Tetris screen control. + */ + +#include <sgtty.h> +#include <sys/ioctl.h> + +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifndef sigmask +#define sigmask(s) (1 << ((s) - 1)) +#endif + +#include "screen.h" +#include "tetris.h" + +/* + * XXX - need a <termcap.h> + */ +int tgetent __P((char *, const char *)); +int tgetflag __P((const char *)); +int tgetnum __P((const char *)); +int tputs __P((const char *, int, int (*)(int))); + +static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */ +static int curscore; +static int isset; /* true => terminal is in game mode */ +static struct sgttyb oldtt; +static void (*tstp)(); + +char *tgetstr(), *tgoto(); + + +/* + * Capabilities from TERMCAP. + */ +char PC, *BC, *UP; /* tgoto requires globals: ugh! */ +short ospeed; + +static char + *bcstr, /* backspace char */ + *CEstr, /* clear to end of line */ + *CLstr, /* clear screen */ + *CMstr, /* cursor motion string */ +#ifdef unneeded + *CRstr, /* "\r" equivalent */ +#endif + *HOstr, /* cursor home */ + *LLstr, /* last line, first column */ + *pcstr, /* pad character */ + *TEstr, /* end cursor motion mode */ + *TIstr; /* begin cursor motion mode */ +char + *SEstr, /* end standout mode */ + *SOstr; /* begin standout mode */ +static int + COnum, /* co# value */ + LInum, /* li# value */ + MSflag; /* can move in standout mode */ + + +struct tcsinfo { /* termcap string info; some abbrevs above */ + char tcname[3]; + char **tcaddr; +} tcstrings[] = { + "bc", &bcstr, + "ce", &CEstr, + "cl", &CLstr, + "cm", &CMstr, +#ifdef unneeded + "cr", &CRstr, +#endif + "le", &BC, /* move cursor left one space */ + "pc", &pcstr, + "se", &SEstr, + "so", &SOstr, + "te", &TEstr, + "ti", &TIstr, + "up", &UP, /* cursor up */ + 0 +}; + +/* This is where we will actually stuff the information */ + +static char combuf[1024], tbuf[1024]; + + +/* + * Routine used by tputs(). + */ +int +put(c) + int c; +{ + + return (putchar(c)); +} + +/* + * putstr() is for unpadded strings (either as in termcap(5) or + * simply literal strings); putpad() is for padded strings with + * count=1. (See screen.h for putpad().) + */ +#define putstr(s) (void)fputs(s, stdout) +#define moveto(r, c) putpad(tgoto(CMstr, c, r)) + +/* + * Set up from termcap. + */ +void +scr_init() +{ + static int bsflag, xsflag, sgnum; +#ifdef unneeded + static int ncflag; +#endif + char *term, *fill; + static struct tcninfo { /* termcap numeric and flag info */ + char tcname[3]; + int *tcaddr; + } tcflags[] = { + "bs", &bsflag, + "ms", &MSflag, +#ifdef unneeded + "nc", &ncflag, +#endif + "xs", &xsflag, + 0 + }, tcnums[] = { + "co", &COnum, + "li", &LInum, + "sg", &sgnum, + 0 + }; + + if ((term = getenv("TERM")) == NULL) + stop("you must set the TERM environment variable"); + if (tgetent(tbuf, term) <= 0) + stop("cannot find your termcap"); + fill = combuf; + { + register struct tcsinfo *p; + + for (p = tcstrings; p->tcaddr; p++) + *p->tcaddr = tgetstr(p->tcname, &fill); + } + { + register struct tcninfo *p; + + for (p = tcflags; p->tcaddr; p++) + *p->tcaddr = tgetflag(p->tcname); + for (p = tcnums; p->tcaddr; p++) + *p->tcaddr = tgetnum(p->tcname); + } + if (bsflag) + BC = "\b"; + else if (BC == NULL && bcstr != NULL) + BC = bcstr; + if (CLstr == NULL) + stop("cannot clear screen"); + if (CMstr == NULL || UP == NULL || BC == NULL) + stop("cannot do random cursor positioning via tgoto()"); + PC = pcstr ? *pcstr : 0; + if (sgnum >= 0 || xsflag) + SOstr = SEstr = NULL; +#ifdef unneeded + if (ncflag) + CRstr = NULL; + else if (CRstr == NULL) + CRstr = "\r"; +#endif +} + +/* this foolery is needed to modify tty state `atomically' */ +static jmp_buf scr_onstop; + +#define sigunblock(mask) sigsetmask(sigblock(0) & ~(mask)) + +static void +stopset(sig) + int sig; +{ + (void) signal(sig, SIG_DFL); + (void) kill(getpid(), sig); + (void) sigunblock(sigmask(sig)); + longjmp(scr_onstop, 1); +} + +static void +scr_stop() +{ + scr_end(); + (void) kill(getpid(), SIGTSTP); + (void) sigunblock(sigmask(SIGTSTP)); + scr_set(); + scr_msg(key_msg, 1); +} + +/* + * Set up screen mode. + */ +void +scr_set() +{ + struct winsize ws; + struct sgttyb newtt; + volatile int omask; + void (*ttou)(); + + omask = sigblock(sigmask(SIGTSTP) | sigmask(SIGTTOU)); + if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN) + (void) signal(SIGTSTP, SIG_IGN); + if ((ttou = signal(SIGTSTP, stopset)) == SIG_IGN) + (void) signal(SIGTSTP, SIG_IGN); + /* + * At last, we are ready to modify the tty state. If + * we stop while at it, stopset() above will longjmp back + * to the setjmp here and we will start over. + */ + (void) setjmp(scr_onstop); + (void) sigsetmask(omask); + Rows = 0, Cols = 0; + if (ioctl(0, TIOCGWINSZ, &ws) == 0) { + Rows = ws.ws_row; + Cols = ws.ws_col; + } + if (Rows == 0) + Rows = LInum; + if (Cols == 0) + Cols = COnum; + if (Rows < MINROWS || Cols < MINCOLS) { + (void) fprintf(stderr, + "the screen is too small: must be at least %d x %d", + MINROWS, MINCOLS); + stop(""); /* stop() supplies \n */ + } + if (ioctl(0, TIOCGETP, &oldtt)) + stop("ioctl(TIOCGETP) fails"); + newtt = oldtt; + newtt.sg_flags = (newtt.sg_flags | CBREAK) & ~(CRMOD | ECHO); + if ((newtt.sg_flags & TBDELAY) == XTABS) + newtt.sg_flags &= ~TBDELAY; + if (ioctl(0, TIOCSETN, &newtt)) + stop("ioctl(TIOCSETN) fails"); + ospeed = newtt.sg_ospeed; + omask = sigblock(sigmask(SIGTSTP) | sigmask(SIGTTOU)); + + /* + * We made it. We are now in screen mode, modulo TIstr + * (which we will fix immediately). + */ + if (TIstr) + putstr(TIstr); /* termcap(5) says this is not padded */ + if (tstp != SIG_IGN) + (void) signal(SIGTSTP, scr_stop); + (void) signal(SIGTTOU, ttou); + + isset = 1; + (void) sigsetmask(omask); + scr_clear(); +} + +/* + * End screen mode. + */ +void +scr_end() +{ + int omask = sigblock(sigmask(SIGTSTP) | sigmask(SIGTTOU)); + + /* move cursor to last line */ + if (LLstr) + putstr(LLstr); /* termcap(5) says this is not padded */ + else + moveto(Rows - 1, 0); + /* exit screen mode */ + if (TEstr) + putstr(TEstr); /* termcap(5) says this is not padded */ + (void) fflush(stdout); + (void) ioctl(0, TIOCSETN, &oldtt); + isset = 0; + /* restore signals */ + (void) signal(SIGTSTP, tstp); + (void) sigsetmask(omask); +} + +void +stop(why) + char *why; +{ + + if (isset) + scr_end(); + (void) fprintf(stderr, "aborting: %s\n", why); + exit(1); +} + +/* + * Clear the screen, forgetting the current contents in the process. + */ +void +scr_clear() +{ + + putpad(CLstr); + curscore = -1; + bzero((char *)curscreen, sizeof(curscreen)); +} + +#if vax && !__GNUC__ +typedef int regcell; /* pcc is bad at `register char', etc */ +#else +typedef cell regcell; +#endif + +/* + * Update the screen. + */ +void +scr_update() +{ + register cell *bp, *sp; + register regcell so, cur_so = 0; + register int i, ccol, j; + int omask = sigblock(sigmask(SIGTSTP)); + + /* always leave cursor after last displayed point */ + curscreen[D_LAST * B_COLS - 1] = -1; + + if (score != curscore) { + if (HOstr) + putpad(HOstr); + else + moveto(0, 0); + (void) printf("%d", score); + curscore = score; + } + + bp = &board[D_FIRST * B_COLS]; + sp = &curscreen[D_FIRST * B_COLS]; + for (j = D_FIRST; j < D_LAST; j++) { + ccol = -1; + for (i = 0; i < B_COLS; bp++, sp++, i++) { + if (*sp == (so = *bp)) + continue; + *sp = so; + if (i != ccol) { + if (cur_so && MSflag) { + putpad(SEstr); + cur_so = 0; + } + moveto(RTOD(j), CTOD(i)); + } + if (SOstr) { + if (so != cur_so) { + putpad(so ? SOstr : SEstr); + cur_so = so; + } + putstr(" "); + } else + putstr(so ? "XX" : " "); + ccol = i + 1; + /* + * Look ahead a bit, to avoid extra motion if + * we will be redrawing the cell after the next. + * Motion probably takes four or more characters, + * so we save even if we rewrite two cells + * `unnecessarily'. Skip it all, though, if + * the next cell is a different color. + */ +#define STOP (B_COLS - 3) + if (i > STOP || sp[1] != bp[1] || so != bp[1]) + continue; + if (sp[2] != bp[2]) + sp[1] = -1; + else if (i < STOP && so == bp[2] && sp[3] != bp[3]) { + sp[2] = -1; + sp[1] = -1; + } + } + } + if (cur_so) + putpad(SEstr); + (void) fflush(stdout); + (void) sigsetmask(omask); +} + +/* + * Write a message (set!=0), or clear the same message (set==0). + * (We need its length in case we have to overwrite with blanks.) + */ +void +scr_msg(s, set) + register char *s; + int set; +{ + + if (set || CEstr == NULL) { + register int l = strlen(s); + + moveto(Rows - 2, ((Cols - l) >> 1) - 1); + if (set) + putstr(s); + else + while (--l >= 0) + (void) putchar(' '); + } else { + moveto(Rows - 2, 0); + putpad(CEstr); + } +} |