Subversion Repositories HelenOS-historic

Rev

Rev 1472 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*  $OpenBSD: screen.c,v 1.13 2006/04/20 03:25:36 ray Exp $ */
  2. /*  $NetBSD: screen.c,v 1.4 1995/04/29 01:11:36 mycroft Exp $   */
  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 <err.h>
  45. #include <setjmp.h>
  46. #include <signal.h>
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50. #include <term.h>
  51. #include <termios.h>
  52. #include <unistd.h>
  53.  
  54. #include "screen.h"
  55. #include "tetris.h"
  56.  
  57. static cell curscreen[B_SIZE];  /* 1 => standout (or otherwise marked) */
  58. static int curscore;
  59. static int isset;       /* true => terminal is in game mode */
  60. static struct termios oldtt;
  61. static void (*tstp)(int);
  62.  
  63. static void scr_stop(int);
  64. static void stopset(int);
  65.  
  66. /*
  67.  * Capabilities from TERMCAP.
  68.  */
  69. char    PC, *BC, *UP;       /* tgoto requires globals: ugh! */
  70.  
  71. static char
  72.     *bcstr,         /* backspace char */
  73.     *CEstr,         /* clear to end of line */
  74.     *CLstr,         /* clear screen */
  75.     *CMstr,         /* cursor motion string */
  76. #ifdef unneeded
  77.     *CRstr,         /* "\r" equivalent */
  78. #endif
  79.     *HOstr,         /* cursor home */
  80.     *LLstr,         /* last line, first column */
  81.     *pcstr,         /* pad character */
  82.     *TEstr,         /* end cursor motion mode */
  83.     *TIstr;         /* begin cursor motion mode */
  84. char
  85.     *SEstr,         /* end standout mode */
  86.     *SOstr;         /* begin standout mode */
  87. static int
  88.     COnum,          /* co# value */
  89.     LInum,          /* li# value */
  90.     MSflag;         /* can move in standout mode */
  91.  
  92.  
  93. struct tcsinfo {        /* termcap string info; some abbrevs above */
  94.     char tcname[3];
  95.     char **tcaddr;
  96. } tcstrings[] = {
  97.     {"bc", &bcstr},
  98.     {"ce", &CEstr},
  99.     {"cl", &CLstr},
  100.     {"cm", &CMstr},
  101. #ifdef unneeded
  102.     {"cr", &CRstr},
  103. #endif
  104.     {"le", &BC},        /* move cursor left one space */
  105.     {"pc", &pcstr},
  106.     {"se", &SEstr},
  107.     {"so", &SOstr},
  108.     {"te", &TEstr},
  109.     {"ti", &TIstr},
  110.     {"up", &UP},        /* cursor up */
  111.     { {0}, NULL}
  112. };
  113.  
  114. /* This is where we will actually stuff the information */
  115.  
  116. static char combuf[1024], tbuf[1024];
  117.  
  118.  
  119. /*
  120.  * Routine used by tputs().
  121.  */
  122. int
  123. put(int c)
  124. {
  125.  
  126.     return (putchar(c));
  127. }
  128.  
  129. /*
  130.  * putstr() is for unpadded strings (either as in termcap(5) or
  131.  * simply literal strings); putpad() is for padded strings with
  132.  * count=1.  (See screen.h for putpad().)
  133.  */
  134. #define putstr(s)   (void)fputs(s, stdout)
  135. #define moveto(r, c)    putpad(tgoto(CMstr, c, r))
  136.  
  137. /*
  138.  * Set up from termcap.
  139.  */
  140. void
  141. scr_init(void)
  142. {
  143.     static int bsflag, xsflag, sgnum;
  144. #ifdef unneeded
  145.     static int ncflag;
  146. #endif
  147.     char *term, *fill;
  148.     static struct tcninfo { /* termcap numeric and flag info */
  149.         char tcname[3];
  150.         int *tcaddr;
  151.     } tcflags[] = {
  152.         {"bs", &bsflag},
  153.         {"ms", &MSflag},
  154. #ifdef unneeded
  155.         {"nc", &ncflag},
  156. #endif
  157.         {"xs", &xsflag},
  158.         { {0}, NULL}
  159.     }, tcnums[] = {
  160.         {"co", &COnum},
  161.         {"li", &LInum},
  162.         {"sg", &sgnum},
  163.         { {0}, NULL}
  164.     };
  165.    
  166.     if ((term = getenv("TERM")) == NULL)
  167.         stop("you must set the TERM environment variable");
  168.     if (tgetent(tbuf, term) <= 0)
  169.         stop("cannot find your termcap");
  170.     fill = combuf;
  171.     {
  172.         struct tcsinfo *p;
  173.  
  174.         for (p = tcstrings; p->tcaddr; p++)
  175.             *p->tcaddr = tgetstr(p->tcname, &fill);
  176.     }
  177.     if (classic)
  178.         SOstr = SEstr = NULL;
  179.     {
  180.         struct tcninfo *p;
  181.  
  182.         for (p = tcflags; p->tcaddr; p++)
  183.             *p->tcaddr = tgetflag(p->tcname);
  184.         for (p = tcnums; p->tcaddr; p++)
  185.             *p->tcaddr = tgetnum(p->tcname);
  186.     }
  187.     if (bsflag)
  188.         BC = "\b";
  189.     else if (BC == NULL && bcstr != NULL)
  190.         BC = bcstr;
  191.     if (CLstr == NULL)
  192.         stop("cannot clear screen");
  193.     if (CMstr == NULL || UP == NULL || BC == NULL)
  194.         stop("cannot do random cursor positioning via tgoto()");
  195.     PC = pcstr ? *pcstr : 0;
  196.     if (sgnum > 0 || xsflag)
  197.         SOstr = SEstr = NULL;
  198. #ifdef unneeded
  199.     if (ncflag)
  200.         CRstr = NULL;
  201.     else if (CRstr == NULL)
  202.         CRstr = "\r";
  203. #endif
  204. }
  205.  
  206. /* this foolery is needed to modify tty state `atomically' */
  207. static jmp_buf scr_onstop;
  208.  
  209. static void
  210. stopset(int sig)
  211. {
  212.     sigset_t sigset;
  213.  
  214.     (void) signal(sig, SIG_DFL);
  215.     (void) kill(getpid(), sig);
  216.     sigemptyset(&sigset);
  217.     sigaddset(&sigset, sig);
  218.     (void) sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
  219.     longjmp(scr_onstop, 1);
  220. }
  221.  
  222. static void
  223. scr_stop(int sig)
  224. {
  225.     sigset_t sigset;
  226.  
  227.     scr_end();
  228.     (void) kill(getpid(), sig);
  229.     sigemptyset(&sigset);
  230.     sigaddset(&sigset, sig);
  231.     (void) sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
  232.     scr_set();
  233.     scr_msg(key_msg, 1);
  234. }
  235.  
  236. /*
  237.  * Set up screen mode.
  238.  */
  239. void
  240. scr_set(void)
  241. {
  242.     struct winsize ws;
  243.     struct termios newtt;
  244.     sigset_t sigset, osigset;
  245.     void (*ttou)(int);
  246.  
  247.     sigemptyset(&sigset);
  248.     sigaddset(&sigset, SIGTSTP);
  249.     sigaddset(&sigset, SIGTTOU);
  250.     (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
  251.     if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN)
  252.         (void) signal(SIGTSTP, SIG_IGN);
  253.     if ((ttou = signal(SIGTTOU, stopset)) == SIG_IGN)
  254.         (void) signal(SIGTTOU, SIG_IGN);
  255.     /*
  256.      * At last, we are ready to modify the tty state.  If
  257.      * we stop while at it, stopset() above will longjmp back
  258.      * to the setjmp here and we will start over.
  259.      */
  260.     (void) setjmp(scr_onstop);
  261.     (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
  262.     Rows = 0, Cols = 0;
  263.     if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
  264.         Rows = ws.ws_row;
  265.         Cols = ws.ws_col;
  266.     }
  267.     if (Rows == 0)
  268.         Rows = LInum;
  269.     if (Cols == 0)
  270.     Cols = COnum;
  271.     if (Rows < MINROWS || Cols < MINCOLS) {
  272.         char smallscr[55];
  273.  
  274.         (void)snprintf(smallscr, sizeof(smallscr),
  275.             "the screen is too small (must be at least %dx%d)",
  276.             MINROWS, MINCOLS);
  277.         stop(smallscr);
  278.     }
  279.     if (tcgetattr(0, &oldtt) < 0)
  280.         stop("tcgetattr() fails");
  281.     newtt = oldtt;
  282.     newtt.c_lflag &= ~(ICANON|ECHO);
  283.     newtt.c_oflag &= ~OXTABS;
  284.     if (tcsetattr(0, TCSADRAIN, &newtt) < 0)
  285.         stop("tcsetattr() fails");
  286.     (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
  287.  
  288.     /*
  289.      * We made it.  We are now in screen mode, modulo TIstr
  290.      * (which we will fix immediately).
  291.      */
  292.     if (TIstr)
  293.         putstr(TIstr);  /* termcap(5) says this is not padded */
  294.     if (tstp != SIG_IGN)
  295.         (void) signal(SIGTSTP, scr_stop);
  296.     if (ttou != SIG_IGN)
  297.         (void) signal(SIGTTOU, ttou);
  298.  
  299.     isset = 1;
  300.     (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
  301.     scr_clear();
  302. }
  303.  
  304. /*
  305.  * End screen mode.
  306.  */
  307. void
  308. scr_end(void)
  309. {
  310.     sigset_t sigset, osigset;
  311.  
  312.     sigemptyset(&sigset);
  313.     sigaddset(&sigset, SIGTSTP);
  314.     sigaddset(&sigset, SIGTTOU);
  315.     (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
  316.     /* move cursor to last line */
  317.     if (LLstr)
  318.         putstr(LLstr);  /* termcap(5) says this is not padded */
  319.     else
  320.         moveto(Rows - 1, 0);
  321.     /* exit screen mode */
  322.     if (TEstr)
  323.         putstr(TEstr);  /* termcap(5) says this is not padded */
  324.     (void) fflush(stdout);
  325.     (void) tcsetattr(0, TCSADRAIN, &oldtt);
  326.     isset = 0;
  327.     /* restore signals */
  328.     (void) signal(SIGTSTP, tstp);
  329.     (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
  330. }
  331.  
  332. void
  333. stop(char *why)
  334. {
  335.  
  336.     if (isset)
  337.         scr_end();
  338.     errx(1, "aborting: %s", why);
  339. }
  340.  
  341. /*
  342.  * Clear the screen, forgetting the current contents in the process.
  343.  */
  344. void
  345. scr_clear(void)
  346. {
  347.  
  348.     putpad(CLstr);
  349.     curscore = -1;
  350.     memset((char *)curscreen, 0, sizeof(curscreen));
  351. }
  352.  
  353. #if vax && !__GNUC__
  354. typedef int regcell;    /* pcc is bad at `register char', etc */
  355. #else
  356. typedef cell regcell;
  357. #endif
  358.  
  359. /*
  360.  * Update the screen.
  361.  */
  362. void
  363. scr_update(void)
  364. {
  365.     cell *bp, *sp;
  366.     regcell so, cur_so = 0;
  367.     int i, ccol, j;
  368.     sigset_t sigset, osigset;
  369.     static const struct shape *lastshape;
  370.  
  371.     sigemptyset(&sigset);
  372.     sigaddset(&sigset, SIGTSTP);
  373.     (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
  374.  
  375.     /* always leave cursor after last displayed point */
  376.     curscreen[D_LAST * B_COLS - 1] = -1;
  377.  
  378.     if (score != curscore) {
  379.         if (HOstr)
  380.             putpad(HOstr);
  381.         else
  382.             moveto(0, 0);
  383.         (void) printf("Score: %d", score);
  384.         curscore = score;
  385.     }
  386.  
  387.     /* draw preview of next pattern */
  388.     if (showpreview && (nextshape != lastshape)) {
  389.         int i;
  390.         static int r=5, c=2;
  391.         int tr, tc, t;
  392.  
  393.         lastshape = nextshape;
  394.  
  395.         /* clean */
  396.         putpad(SEstr);
  397.         moveto(r-1, c-1); putstr("          ");
  398.         moveto(r,   c-1); putstr("          ");
  399.         moveto(r+1, c-1); putstr("          ");
  400.         moveto(r+2, c-1); putstr("          ");
  401.  
  402.         moveto(r-3, c-2);
  403.         putstr("Next shape:");
  404.  
  405.         /* draw */
  406.         if (SOstr)
  407.             putpad(SOstr);
  408.         moveto(r, 2 * c);
  409.         putstr(SOstr ? "  " : "[]");
  410.         for (i = 0; i < 3; i++) {
  411.             t = c + r * B_COLS;
  412.             t += nextshape->off[i];
  413.  
  414.             tr = t / B_COLS;
  415.             tc = t % B_COLS;
  416.  
  417.             moveto(tr, 2*tc);
  418.             putstr(SOstr ? "  " : "[]");
  419.         }
  420.         putpad(SEstr);
  421.     }
  422.  
  423.     bp = &board[D_FIRST * B_COLS];
  424.     sp = &curscreen[D_FIRST * B_COLS];
  425.     for (j = D_FIRST; j < D_LAST; j++) {
  426.         ccol = -1;
  427.         for (i = 0; i < B_COLS; bp++, sp++, i++) {
  428.             if (*sp == (so = *bp))
  429.                 continue;
  430.             *sp = so;
  431.             if (i != ccol) {
  432.                 if (cur_so && MSflag) {
  433.                     putpad(SEstr);
  434.                     cur_so = 0;
  435.                 }
  436.                 moveto(RTOD(j), CTOD(i));
  437.             }
  438.             if (SOstr) {
  439.                 if (so != cur_so) {
  440.                     putpad(so ? SOstr : SEstr);
  441.                     cur_so = so;
  442.                 }
  443.                 putstr("  ");
  444.             } else
  445.                 putstr(so ? "[]" : "  ");
  446.             ccol = i + 1;
  447.             /*
  448.              * Look ahead a bit, to avoid extra motion if
  449.              * we will be redrawing the cell after the next.
  450.              * Motion probably takes four or more characters,
  451.              * so we save even if we rewrite two cells
  452.              * `unnecessarily'.  Skip it all, though, if
  453.              * the next cell is a different color.
  454.              */
  455. #define STOP (B_COLS - 3)
  456.             if (i > STOP || sp[1] != bp[1] || so != bp[1])
  457.                 continue;
  458.             if (sp[2] != bp[2])
  459.                 sp[1] = -1;
  460.             else if (i < STOP && so == bp[2] && sp[3] != bp[3]) {
  461.                 sp[2] = -1;
  462.                 sp[1] = -1;
  463.             }
  464.         }
  465.     }
  466.     if (cur_so)
  467.         putpad(SEstr);
  468.     (void) fflush(stdout);
  469.     (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
  470. }
  471.  
  472. /*
  473.  * Write a message (set!=0), or clear the same message (set==0).
  474.  * (We need its length in case we have to overwrite with blanks.)
  475.  */
  476. void
  477. scr_msg(char *s, int set)
  478. {
  479.    
  480.     if (set || CEstr == NULL) {
  481.         int l = strlen(s);
  482.  
  483.         moveto(Rows - 2, ((Cols - l) >> 1) - 1);
  484.         if (set)
  485.             putstr(s);
  486.         else
  487.             while (--l >= 0)
  488.                 (void) putchar(' ');
  489.     } else {
  490.         moveto(Rows - 2, 0);
  491.         putpad(CEstr);
  492.     }
  493. }
  494.