screen.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  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. * Copyright (c) 1992, 1993
  5. * The Regents of the University of California. All rights reserved.
  6. *
  7. * This code is derived from software contributed to Berkeley by
  8. * Chris Torek and Darren F. Provine.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in the
  17. * documentation and/or other materials provided with the distribution.
  18. * 3. Neither the name of the University nor the names of its contributors
  19. * may be used to endorse or promote products derived from this software
  20. * without specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  23. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  26. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  27. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  28. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  31. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32. * SUCH DAMAGE.
  33. *
  34. * @(#)screen.c 8.1 (Berkeley) 5/31/93
  35. */
  36. /*
  37. * Tetris screen control.
  38. */
  39. #include <sys/ioctl.h>
  40. #include <setjmp.h>
  41. #include <signal.h>
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <string.h>
  45. #include <termcap.h>
  46. #include <termios.h>
  47. #include <unistd.h>
  48. #ifndef sigmask
  49. #define sigmask(s) (1 << ((s) - 1))
  50. #endif
  51. #include "screen.h"
  52. #include "tetris.h"
  53. static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */
  54. static int curscore;
  55. static int isset; /* true => terminal is in game mode */
  56. static struct termios oldtt;
  57. static void (*tstp)(int);
  58. static void scr_stop(int);
  59. static void stopset(int) __attribute__((__noreturn__));
  60. /*
  61. * Capabilities from TERMCAP.
  62. */
  63. extern char PC, *BC, *UP; /* tgoto requires globals: ugh! */
  64. static char BCdefault[] = "\b";
  65. #ifndef NCURSES_VERSION
  66. short ospeed;
  67. #endif
  68. static char
  69. *bcstr, /* backspace char */
  70. *CEstr, /* clear to end of line */
  71. *CLstr, /* clear screen */
  72. *CMstr, /* cursor motion string */
  73. #ifdef unneeded
  74. *CRstr, /* "\r" equivalent */
  75. #endif
  76. *HOstr, /* cursor home */
  77. *LLstr, /* last line, first column */
  78. *pcstr, /* pad character */
  79. *TEstr, /* end cursor motion mode */
  80. *TIstr; /* begin cursor motion mode */
  81. char
  82. *SEstr, /* end standout mode */
  83. *SOstr; /* begin standout mode */
  84. static int
  85. COnum, /* co# value */
  86. LInum, /* li# value */
  87. MSflag; /* can move in standout mode */
  88. struct tcsinfo { /* termcap string info; some abbrevs above */
  89. char tcname[3];
  90. char **tcaddr;
  91. } tcstrings[] = {
  92. {"bc", &bcstr},
  93. {"ce", &CEstr},
  94. {"cl", &CLstr},
  95. {"cm", &CMstr},
  96. #ifdef unneeded
  97. {"cr", &CRstr},
  98. #endif
  99. {"le", &BC}, /* move cursor left one space */
  100. {"pc", &pcstr},
  101. {"se", &SEstr},
  102. {"so", &SOstr},
  103. {"te", &TEstr},
  104. {"ti", &TIstr},
  105. {"up", &UP}, /* cursor up */
  106. { {0}, NULL}
  107. };
  108. /* This is where we will actually stuff the information */
  109. static char combuf[1024], tbuf[1024];
  110. /*
  111. * Routine used by tputs().
  112. */
  113. int
  114. put(c)
  115. int c;
  116. {
  117. return (putchar(c));
  118. }
  119. /*
  120. * putstr() is for unpadded strings (either as in termcap(5) or
  121. * simply literal strings); putpad() is for padded strings with
  122. * count=1. (See screen.h for putpad().)
  123. */
  124. #define putstr(s) (void)fputs(s, stdout)
  125. #define moveto(r, c) putpad(tgoto(CMstr, c, r))
  126. /*
  127. * Set up from termcap.
  128. */
  129. void
  130. scr_init()
  131. {
  132. static int bsflag, xsflag, sgnum;
  133. #ifdef unneeded
  134. static int ncflag;
  135. #endif
  136. char *term, *fill;
  137. static struct tcninfo { /* termcap numeric and flag info */
  138. char tcname[3];
  139. int *tcaddr;
  140. } tcflags[] = {
  141. {"bs", &bsflag},
  142. {"ms", &MSflag},
  143. #ifdef unneeded
  144. {"nc", &ncflag},
  145. #endif
  146. {"xs", &xsflag},
  147. { {0}, NULL}
  148. }, tcnums[] = {
  149. {"co", &COnum},
  150. {"li", &LInum},
  151. {"sg", &sgnum},
  152. { {0}, NULL}
  153. };
  154. if ((term = getenv("TERM")) == NULL)
  155. stop("you must set the TERM environment variable");
  156. if (tgetent(tbuf, term) <= 0)
  157. stop("cannot find your termcap");
  158. fill = combuf;
  159. {
  160. struct tcsinfo *p;
  161. for (p = tcstrings; p->tcaddr; p++)
  162. *p->tcaddr = tgetstr(p->tcname, &fill);
  163. }
  164. {
  165. struct tcninfo *p;
  166. for (p = tcflags; p->tcaddr; p++)
  167. *p->tcaddr = tgetflag(p->tcname);
  168. for (p = tcnums; p->tcaddr; p++)
  169. *p->tcaddr = tgetnum(p->tcname);
  170. }
  171. if (bsflag)
  172. BC = BCdefault;
  173. else if (BC == NULL && bcstr != NULL)
  174. BC = bcstr;
  175. if (CLstr == NULL)
  176. stop("cannot clear screen");
  177. if (CMstr == NULL || UP == NULL || BC == NULL)
  178. stop("cannot do random cursor positioning via tgoto()");
  179. PC = pcstr ? *pcstr : 0;
  180. if (sgnum >= 0 || xsflag)
  181. SOstr = SEstr = NULL;
  182. #ifdef unneeded
  183. if (ncflag)
  184. CRstr = NULL;
  185. else if (CRstr == NULL)
  186. CRstr = "\r";
  187. #endif
  188. }
  189. /* this foolery is needed to modify tty state `atomically' */
  190. static jmp_buf scr_onstop;
  191. static void
  192. stopset(sig)
  193. int sig;
  194. {
  195. sigset_t sigset;
  196. (void) signal(sig, SIG_DFL);
  197. (void) kill(getpid(), sig);
  198. sigemptyset(&sigset);
  199. sigaddset(&sigset, sig);
  200. (void) sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
  201. longjmp(scr_onstop, 1);
  202. }
  203. static void
  204. scr_stop(sig)
  205. int sig;
  206. {
  207. sigset_t sigset;
  208. scr_end();
  209. (void) kill(getpid(), sig);
  210. sigemptyset(&sigset);
  211. sigaddset(&sigset, sig);
  212. (void) sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
  213. scr_set();
  214. scr_msg(key_msg, 1);
  215. }
  216. /*
  217. * Set up screen mode.
  218. */
  219. void
  220. scr_set()
  221. {
  222. struct winsize ws;
  223. struct termios newtt;
  224. sigset_t sigset, osigset;
  225. void (*ttou)(int);
  226. sigemptyset(&sigset);
  227. sigaddset(&sigset, SIGTSTP);
  228. sigaddset(&sigset, SIGTTOU);
  229. (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
  230. if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN)
  231. (void) signal(SIGTSTP, SIG_IGN);
  232. if ((ttou = signal(SIGTTOU, stopset)) == SIG_IGN)
  233. (void) signal(SIGTTOU, SIG_IGN);
  234. /*
  235. * At last, we are ready to modify the tty state. If
  236. * we stop while at it, stopset() above will longjmp back
  237. * to the setjmp here and we will start over.
  238. */
  239. (void) setjmp(scr_onstop);
  240. (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
  241. Rows = 0, Cols = 0;
  242. if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
  243. Rows = ws.ws_row;
  244. Cols = ws.ws_col;
  245. }
  246. if (Rows == 0)
  247. Rows = LInum;
  248. if (Cols == 0)
  249. Cols = COnum;
  250. if (Rows < MINROWS || Cols < MINCOLS) {
  251. (void) fprintf(stderr,
  252. "the screen is too small: must be at least %dx%d, ",
  253. MINCOLS, MINROWS);
  254. stop(""); /* stop() supplies \n */
  255. }
  256. if (tcgetattr(0, &oldtt) < 0)
  257. stop("tcgetattr() fails");
  258. newtt = oldtt;
  259. newtt.c_lflag &= ~(ICANON|ECHO);
  260. newtt.c_oflag &= ~OXTABS;
  261. newtt.c_cc[VMIN] = 1;
  262. newtt.c_cc[VTIME] = 0;
  263. if (tcsetattr(0, TCSADRAIN, &newtt) < 0)
  264. stop("tcsetattr() fails");
  265. ospeed = cfgetospeed(&newtt);
  266. (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
  267. /*
  268. * We made it. We are now in screen mode, modulo TIstr
  269. * (which we will fix immediately).
  270. */
  271. if (TIstr)
  272. putstr(TIstr); /* termcap(5) says this is not padded */
  273. if (tstp != SIG_IGN)
  274. (void) signal(SIGTSTP, scr_stop);
  275. if (ttou != SIG_IGN)
  276. (void) signal(SIGTTOU, ttou);
  277. isset = 1;
  278. (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
  279. scr_clear();
  280. }
  281. /*
  282. * End screen mode.
  283. */
  284. void
  285. scr_end()
  286. {
  287. sigset_t sigset, osigset;
  288. sigemptyset(&sigset);
  289. sigaddset(&sigset, SIGTSTP);
  290. sigaddset(&sigset, SIGTTOU);
  291. (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
  292. /* move cursor to last line */
  293. if (LLstr)
  294. putstr(LLstr); /* termcap(5) says this is not padded */
  295. else
  296. moveto(Rows - 1, 0);
  297. /* exit screen mode */
  298. if (TEstr)
  299. putstr(TEstr); /* termcap(5) says this is not padded */
  300. (void) fflush(stdout);
  301. (void) tcsetattr(0, TCSADRAIN, &oldtt);
  302. isset = 0;
  303. /* restore signals */
  304. (void) signal(SIGTSTP, tstp);
  305. (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
  306. }
  307. void
  308. stop(why)
  309. const char *why;
  310. {
  311. if (isset)
  312. scr_end();
  313. (void) fprintf(stderr, "aborting: %s\n", why);
  314. exit(1);
  315. }
  316. /*
  317. * Clear the screen, forgetting the current contents in the process.
  318. */
  319. void
  320. scr_clear()
  321. {
  322. putpad(CLstr);
  323. curscore = -1;
  324. memset((char *)curscreen, 0, sizeof(curscreen));
  325. }
  326. #if vax && !__GNUC__
  327. typedef int regcell; /* pcc is bad at `register char', etc */
  328. #else
  329. typedef cell regcell;
  330. #endif
  331. /*
  332. * Update the screen.
  333. */
  334. void
  335. scr_update()
  336. {
  337. cell *bp, *sp;
  338. regcell so, cur_so = 0;
  339. int i, ccol, j;
  340. sigset_t sigset, osigset;
  341. static const struct shape *lastshape;
  342. sigemptyset(&sigset);
  343. sigaddset(&sigset, SIGTSTP);
  344. (void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
  345. /* always leave cursor after last displayed point */
  346. curscreen[D_LAST * B_COLS - 1] = -1;
  347. if (score != curscore) {
  348. if (HOstr)
  349. putpad(HOstr);
  350. else
  351. moveto(0, 0);
  352. (void) printf("Score: %d", score);
  353. curscore = score;
  354. }
  355. /* draw preview of nextpattern */
  356. if (showpreview && (nextshape != lastshape)) {
  357. int i;
  358. static int r=5, c=2;
  359. int tr, tc, t;
  360. lastshape = nextshape;
  361. /* clean */
  362. putpad(SEstr);
  363. moveto(r-1, c-1); putstr(" ");
  364. moveto(r, c-1); putstr(" ");
  365. moveto(r+1, c-1); putstr(" ");
  366. moveto(r+2, c-1); putstr(" ");
  367. moveto(r-3, c-2);
  368. putstr("Next shape:");
  369. /* draw */
  370. putpad(SOstr);
  371. moveto(r, 2*c);
  372. putstr(" ");
  373. for(i=0; i<3; i++) {
  374. t = c + r*B_COLS;
  375. t += nextshape->off[i];
  376. tr = t / B_COLS;
  377. tc = t % B_COLS;
  378. moveto(tr, 2*tc);
  379. putstr(" ");
  380. }
  381. putpad(SEstr);
  382. }
  383. bp = &board[D_FIRST * B_COLS];
  384. sp = &curscreen[D_FIRST * B_COLS];
  385. for (j = D_FIRST; j < D_LAST; j++) {
  386. ccol = -1;
  387. for (i = 0; i < B_COLS; bp++, sp++, i++) {
  388. if (*sp == (so = *bp))
  389. continue;
  390. *sp = so;
  391. if (i != ccol) {
  392. if (cur_so && MSflag) {
  393. putpad(SEstr);
  394. cur_so = 0;
  395. }
  396. moveto(RTOD(j), CTOD(i));
  397. }
  398. if (SOstr) {
  399. if (so != cur_so) {
  400. putpad(so ? SOstr : SEstr);
  401. cur_so = so;
  402. }
  403. putstr(" ");
  404. } else
  405. putstr(so ? "XX" : " ");
  406. ccol = i + 1;
  407. /*
  408. * Look ahead a bit, to avoid extra motion if
  409. * we will be redrawing the cell after the next.
  410. * Motion probably takes four or more characters,
  411. * so we save even if we rewrite two cells
  412. * `unnecessarily'. Skip it all, though, if
  413. * the next cell is a different color.
  414. */
  415. #define STOP (B_COLS - 3)
  416. if (i > STOP || sp[1] != bp[1] || so != bp[1])
  417. continue;
  418. if (sp[2] != bp[2])
  419. sp[1] = -1;
  420. else if (i < STOP && so == bp[2] && sp[3] != bp[3]) {
  421. sp[2] = -1;
  422. sp[1] = -1;
  423. }
  424. }
  425. }
  426. if (cur_so)
  427. putpad(SEstr);
  428. (void) fflush(stdout);
  429. (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
  430. }
  431. /*
  432. * Write a message (set!=0), or clear the same message (set==0).
  433. * (We need its length in case we have to overwrite with blanks.)
  434. */
  435. void
  436. scr_msg(s, set)
  437. char *s;
  438. int set;
  439. {
  440. if (set || CEstr == NULL) {
  441. int l = strlen(s);
  442. moveto(Rows - 2, ((Cols - l) >> 1) - 1);
  443. if (set)
  444. putstr(s);
  445. else
  446. while (--l >= 0)
  447. (void) putchar(' ');
  448. } else {
  449. moveto(Rows - 2, 0);
  450. putpad(CEstr);
  451. }
  452. }