123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- /* $NetBSD: screen.c,v screen.c,v 1.19 2004/01/27 20:30:30 jsm Exp $ */
- /* For Linux: still using old termcap interface from version 1.13. */
- /*-
- * 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. 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 <sys/ioctl.h>
- #include <setjmp.h>
- #include <signal.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <termcap.h>
- #include <termios.h>
- #include <unistd.h>
- #ifndef sigmask
- #define sigmask(s) (1 << ((s) - 1))
- #endif
- #include "screen.h"
- #include "tetris.h"
- static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */
- static int curscore;
- static int isset; /* true => terminal is in game mode */
- static struct termios oldtt;
- static void (*tstp)(int);
- static void scr_stop(int);
- static void stopset(int) __attribute__((__noreturn__));
- /*
- * Capabilities from TERMCAP.
- */
- extern char PC, *BC, *UP; /* tgoto requires globals: ugh! */
- static char BCdefault[] = "\b";
- #ifndef NCURSES_VERSION
- short ospeed;
- #endif
- 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}, NULL}
- };
- /* 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}, NULL}
- }, tcnums[] = {
- {"co", &COnum},
- {"li", &LInum},
- {"sg", &sgnum},
- { {0}, NULL}
- };
-
- 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;
- {
- struct tcsinfo *p;
- for (p = tcstrings; p->tcaddr; p++)
- *p->tcaddr = tgetstr(p->tcname, &fill);
- }
- {
- 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 = BCdefault;
- 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;
- static void
- stopset(sig)
- int sig;
- {
- sigset_t sigset;
- (void) signal(sig, SIG_DFL);
- (void) kill(getpid(), sig);
- sigemptyset(&sigset);
- sigaddset(&sigset, sig);
- (void) sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
- longjmp(scr_onstop, 1);
- }
- static void
- scr_stop(sig)
- int sig;
- {
- sigset_t sigset;
- scr_end();
- (void) kill(getpid(), sig);
- sigemptyset(&sigset);
- sigaddset(&sigset, sig);
- (void) sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
- scr_set();
- scr_msg(key_msg, 1);
- }
- /*
- * Set up screen mode.
- */
- void
- scr_set()
- {
- struct winsize ws;
- struct termios newtt;
- sigset_t sigset, osigset;
- void (*ttou)(int);
- sigemptyset(&sigset);
- sigaddset(&sigset, SIGTSTP);
- sigaddset(&sigset, SIGTTOU);
- (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
- if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN)
- (void) signal(SIGTSTP, SIG_IGN);
- if ((ttou = signal(SIGTTOU, stopset)) == SIG_IGN)
- (void) signal(SIGTTOU, 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) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
- 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 %dx%d, ",
- MINCOLS, MINROWS);
- stop(""); /* stop() supplies \n */
- }
- if (tcgetattr(0, &oldtt) < 0)
- stop("tcgetattr() fails");
- newtt = oldtt;
- newtt.c_lflag &= ~(ICANON|ECHO);
- newtt.c_oflag &= ~OXTABS;
- newtt.c_cc[VMIN] = 1;
- newtt.c_cc[VTIME] = 0;
- if (tcsetattr(0, TCSADRAIN, &newtt) < 0)
- stop("tcsetattr() fails");
- ospeed = cfgetospeed(&newtt);
- (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
- /*
- * 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);
- if (ttou != SIG_IGN)
- (void) signal(SIGTTOU, ttou);
- isset = 1;
- (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
- scr_clear();
- }
- /*
- * End screen mode.
- */
- void
- scr_end()
- {
- sigset_t sigset, osigset;
- sigemptyset(&sigset);
- sigaddset(&sigset, SIGTSTP);
- sigaddset(&sigset, SIGTTOU);
- (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
- /* 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) tcsetattr(0, TCSADRAIN, &oldtt);
- isset = 0;
- /* restore signals */
- (void) signal(SIGTSTP, tstp);
- (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
- }
- void
- stop(why)
- const 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;
- memset((char *)curscreen, 0, 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()
- {
- cell *bp, *sp;
- regcell so, cur_so = 0;
- int i, ccol, j;
- sigset_t sigset, osigset;
- static const struct shape *lastshape;
- sigemptyset(&sigset);
- sigaddset(&sigset, SIGTSTP);
- (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
- /* 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("Score: %d", score);
- curscore = score;
- }
- /* draw preview of nextpattern */
- if (showpreview && (nextshape != lastshape)) {
- int i;
- static int r=5, c=2;
- int tr, tc, t;
- lastshape = nextshape;
-
- /* clean */
- putpad(SEstr);
- moveto(r-1, c-1); putstr(" ");
- moveto(r, c-1); putstr(" ");
- moveto(r+1, c-1); putstr(" ");
- moveto(r+2, c-1); putstr(" ");
- moveto(r-3, c-2);
- putstr("Next shape:");
-
- /* draw */
- putpad(SOstr);
- moveto(r, 2*c);
- putstr(" ");
- for(i=0; i<3; i++) {
- t = c + r*B_COLS;
- t += nextshape->off[i];
- tr = t / B_COLS;
- tc = t % B_COLS;
- moveto(tr, 2*tc);
- putstr(" ");
- }
- putpad(SEstr);
- }
-
- 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) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
- }
- /*
- * 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)
- char *s;
- int set;
- {
-
- if (set || CEstr == NULL) {
- 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);
- }
- }
|