(linenum→info "unix/slp.c:2238")

bsd-games/2.17/tetris/screen.c

    1: /*      $NetBSD: screen.c,v screen.c,v 1.19 2004/01/27 20:30:30 jsm Exp $    */
    2: /* For Linux: still using old termcap interface from version 1.13.  */
    3: 
    4: /*-
    5:  * Copyright (c) 1992, 1993
    6:  *      The Regents of the University of California.  All rights reserved.
    7:  *
    8:  * This code is derived from software contributed to Berkeley by
    9:  * Chris Torek and Darren F. Provine.
   10:  *
   11:  * Redistribution and use in source and binary forms, with or without
   12:  * modification, are permitted provided that the following conditions
   13:  * are met:
   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.
   22:  *
   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
   33:  * SUCH DAMAGE.
   34:  *
   35:  *      @(#)screen.c 8.1 (Berkeley) 5/31/93
   36:  */
   37: 
   38: /*
   39:  * Tetris screen control.
   40:  */
   41: 
   42: #include <sys/ioctl.h>
   43: 
   44: #include <setjmp.h>
   45: #include <signal.h>
   46: #include <stdio.h>
   47: #include <stdlib.h>
   48: #include <string.h>
   49: #include <termcap.h>
   50: #include <termios.h>
   51: #include <unistd.h>
   52: 
   53: #ifndef sigmask
   54: #define sigmask(s) (1 << ((s) - 1))
   55: #endif
   56: 
   57: #include "screen.h"
   58: #include "tetris.h"
   59: 
   60: static cell curscreen[B_SIZE];  /* 1 => standout (or otherwise marked) */
   61: static int curscore;
   62: static int isset;               /* true => terminal is in game mode */
   63: static struct termios oldtt;
   64: static void (*tstp)(int);
   65: 
   66: static  void     scr_stop(int);
   67: static  void     stopset(int) __attribute__((__noreturn__));
   68: 
   69: 
   70: /*
   71:  * Capabilities from TERMCAP.
   72:  */
   73: extern char     PC, *BC, *UP;       /* tgoto requires globals: ugh! */
   74: static char BCdefault[] = "\b";
   75: #ifndef NCURSES_VERSION
   76: short   ospeed;
   77: #endif
   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: int
  131: put(c)
  132:         int c;
  133: {
  134: 
  135:         return (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:                 struct tcsinfo *p;
  182: 
  183:                 for (p = tcstrings; p->tcaddr; p++)
  184:                         *p->tcaddr = tgetstr(p->tcname, &fill);
  185:         }
  186:         {
  187:                 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 = BCdefault;
  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)(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 %dx%d, ",
  283:                     MINCOLS, MINROWS);
  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:         newtt.c_cc[VMIN] = 1;
  292:         newtt.c_cc[VTIME] = 0;
  293:         if (tcsetattr(0, TCSADRAIN, &newtt) < 0)
  294:                 stop("tcsetattr() fails");
  295:         ospeed = cfgetospeed(&newtt);
  296:         (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
  297: 
  298:         /*
  299:          * We made it.  We are now in screen mode, modulo TIstr
  300:          * (which we will fix immediately).
  301:          */
  302:         if (TIstr)
  303:                 putstr(TIstr);        /* termcap(5) says this is not padded */
  304:         if (tstp != SIG_IGN)
  305:                 (void) signal(SIGTSTP, scr_stop);
  306:         if (ttou != SIG_IGN)
  307:                 (void) signal(SIGTTOU, ttou);
  308: 
  309:         isset = 1;
  310:         (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
  311:         scr_clear();
  312: }
  313: 
  314: /*
  315:  * End screen mode.
  316:  */
  317: void
  318: scr_end()
  319: {
  320:         sigset_t sigset, osigset;
  321: 
  322:         sigemptyset(&sigset);
  323:         sigaddset(&sigset, SIGTSTP);
  324:         sigaddset(&sigset, SIGTTOU);
  325:         (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
  326:         /* move cursor to last line */
  327:         if (LLstr)
  328:                 putstr(LLstr);        /* termcap(5) says this is not padded */
  329:         else
  330:                 moveto(Rows - 1, 0);
  331:         /* exit screen mode */
  332:         if (TEstr)
  333:                 putstr(TEstr);        /* termcap(5) says this is not padded */
  334:         (void) fflush(stdout);
  335:         (void) tcsetattr(0, TCSADRAIN, &oldtt);
  336:         isset = 0;
  337:         /* restore signals */
  338:         (void) signal(SIGTSTP, tstp);
  339:         (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
  340: }
  341: 
  342: void
  343: stop(why)
  344:         const char *why;
  345: {
  346: 
  347:         if (isset)
  348:                 scr_end();
  349:         (void) fprintf(stderr, "aborting: %s\n", why);
  350:         exit(1);
  351: }
  352: 
  353: /*
  354:  * Clear the screen, forgetting the current contents in the process.
  355:  */
  356: void
  357: scr_clear()
  358: {
  359: 
  360:         putpad(CLstr);
  361:         curscore = -1;
  362:         memset((char *)curscreen, 0, sizeof(curscreen));
  363: }
  364: 
  365: #if vax && !__GNUC__
  366: typedef int regcell;    /* pcc is bad at `register char', etc */
  367: #else
  368: typedef cell regcell;
  369: #endif
  370: 
  371: /*
  372:  * Update the screen.
  373:  */
  374: void
  375: scr_update()
  376: {
  377:         cell *bp, *sp;
  378:         regcell so, cur_so = 0;
  379:         int i, ccol, j;
  380:         sigset_t sigset, osigset;
  381:         static const struct shape *lastshape;
  382: 
  383:         sigemptyset(&sigset);
  384:         sigaddset(&sigset, SIGTSTP);
  385:         (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
  386: 
  387:         /* always leave cursor after last displayed point */
  388:         curscreen[D_LAST * B_COLS - 1] = -1;
  389: 
  390:         if (score != curscore) {
  391:                 if (HOstr)
  392:                         putpad(HOstr);
  393:                 else
  394:                         moveto(0, 0);
  395:                 (void) printf("Score: %d", score);
  396:                 curscore = score;
  397:         }
  398: 
  399:         /* draw preview of nextpattern */
  400:         if (showpreview && (nextshape != lastshape)) {
  401:                 int i;
  402:                 static int r=5, c=2;
  403:                 int tr, tc, t; 
  404: 
  405:                 lastshape = nextshape;
  406:                 
  407:                 /* clean */
  408:                 putpad(SEstr);
  409:                 moveto(r-1, c-1); putstr("          ");
  410:                 moveto(r,   c-1); putstr("          ");
  411:                 moveto(r+1, c-1); putstr("          ");
  412:                 moveto(r+2, c-1); putstr("          ");
  413: 
  414:                 moveto(r-3, c-2);
  415:                 putstr("Next shape:");
  416:                                                 
  417:                 /* draw */
  418:                 putpad(SOstr);
  419:                 moveto(r, 2*c);
  420:                 putstr("  ");
  421:                 for(i=0; i<3; i++) {
  422:                         t = c + r*B_COLS;
  423:                         t += nextshape->off[i];
  424: 
  425:                         tr = t / B_COLS;
  426:                         tc = t % B_COLS;
  427: 
  428:                         moveto(tr, 2*tc);
  429:                         putstr("  ");
  430:                 }
  431:                 putpad(SEstr);
  432:         }
  433:         
  434:         bp = &board[D_FIRST * B_COLS];
  435:         sp = &curscreen[D_FIRST * B_COLS];
  436:         for (j = D_FIRST; j < D_LAST; j++) {
  437:                 ccol = -1;
  438:                 for (i = 0; i < B_COLS; bp++, sp++, i++) {
  439:                         if (*sp == (so = *bp))
  440:                                 continue;
  441:                         *sp = so;
  442:                         if (i != ccol) {
  443:                                 if (cur_so && MSflag) {
  444:                                         putpad(SEstr);
  445:                                         cur_so = 0;
  446:                                 }
  447:                                 moveto(RTOD(j), CTOD(i));
  448:                         }
  449:                         if (SOstr) {
  450:                                 if (so != cur_so) {
  451:                                         putpad(so ? SOstr : SEstr);
  452:                                         cur_so = so;
  453:                                 }
  454:                                 putstr("  ");
  455:                         } else
  456:                                 putstr(so ? "XX" : "  ");
  457:                         ccol = i + 1;
  458:                         /*
  459:                          * Look ahead a bit, to avoid extra motion if
  460:                          * we will be redrawing the cell after the next.
  461:                          * Motion probably takes four or more characters,
  462:                          * so we save even if we rewrite two cells
  463:                          * `unnecessarily'.  Skip it all, though, if
  464:                          * the next cell is a different color.
  465:                          */
  466: #define STOP (B_COLS - 3)
  467:                         if (i > STOP || sp[1] != bp[1] || so != bp[1])
  468:                                 continue;
  469:                         if (sp[2] != bp[2])
  470:                                 sp[1] = -1;
  471:                         else if (i < STOP && so == bp[2] && sp[3] != bp[3]) {
  472:                                 sp[2] = -1;
  473:                                 sp[1] = -1;
  474:                         }
  475:                 }
  476:         }
  477:         if (cur_so)
  478:                 putpad(SEstr);
  479:         (void) fflush(stdout);
  480:         (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
  481: }
  482: 
  483: /*
  484:  * Write a message (set!=0), or clear the same message (set==0).
  485:  * (We need its length in case we have to overwrite with blanks.)
  486:  */
  487: void
  488: scr_msg(s, set)
  489:         char *s;
  490:         int set;
  491: {
  492:         
  493:         if (set || CEstr == NULL) {
  494:                 int l = strlen(s);
  495: 
  496:                 moveto(Rows - 2, ((Cols - l) >> 1) - 1);
  497:                 if (set)
  498:                         putstr(s);
  499:                 else
  500:                         while (--l >= 0)
  501:                                 (void) putchar(' ');
  502:         } else {
  503:                 moveto(Rows - 2, 0);
  504:                 putpad(CEstr);
  505:         }
  506: }
1
Syntax (Markdown)