Imported Debian patch 0.52-6
[pkg/netris.git] / curses.c
1 /*
2  * Netris -- A free networked version of T*tris
3  * Copyright (C) 1994-1996,1999  Mark H. Weaver <mhw@netris.org>
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * $Id: curses.c,v 1.33 1999/05/16 06:56:25 mhw Exp $
20  */
21
22 #include "netris.h"
23 #include <time.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <term.h>
27 #include <curses.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #ifdef NCURSES_VERSION
32 # define HAVE_NCURSES
33 #endif
34
35 #ifdef HAVE_NCURSES
36 static struct
37 {
38         BlockType type;
39         short color;
40 } myColorTable[] =
41 {
42         { BT_white,             COLOR_WHITE },
43         { BT_blue,              COLOR_BLUE },
44         { BT_magenta,   COLOR_MAGENTA },
45         { BT_cyan,              COLOR_CYAN },
46         { BT_yellow,    COLOR_YELLOW },
47         { BT_green,             COLOR_GREEN },
48         { BT_red,               COLOR_RED },
49         { BT_none, 0 }
50 };
51 #endif
52
53 static void PlotBlock1(int scr, int y, int x, BlockType type);
54 static MyEventType KeyGenFunc(EventGenRec *gen, MyEvent *event);
55
56 static EventGenRec keyGen =
57                 { NULL, 0, FT_read, STDIN_FILENO, KeyGenFunc, EM_key };
58
59 static int boardYPos[MAX_SCREENS], boardXPos[MAX_SCREENS];
60 static int statusYPos, statusXPos;
61 static int haveColor;
62 static int screens_dirty = 0;
63
64 static char *term_vi;   /* String to make cursor invisible */
65 static char *term_ve;   /* String to make cursor visible */
66
67 ExtFunc void InitScreens(void)
68 {
69         MySigSet oldMask;
70
71         GetTermcapInfo();
72
73         /*
74          * Block signals while initializing curses.  Otherwise a badly timed
75          * Ctrl-C during initialization might leave the terminal in a bad state.
76          */
77         BlockSignals(&oldMask, SIGINT, 0);
78         initscr();
79
80 #ifdef CURSES_HACK
81         {
82                 extern char *CS;
83
84                 CS = 0;
85         }
86 #endif
87
88 #ifdef HAVE_NCURSES
89         haveColor = colorEnable && has_colors();
90         if (haveColor)
91         {
92                 int i = 0;
93
94                 start_color();
95                 for (i = 0; myColorTable[i].type != BT_none; ++i)
96                         init_pair(myColorTable[i].type, COLOR_BLACK,
97                                         myColorTable[i].color);
98         }
99 #else
100         haveColor = 0;
101 #endif
102
103         AtExit(CleanupScreens);
104         screens_dirty = 1;
105         RestoreSignals(NULL, &oldMask);
106
107         cbreak();
108         noecho();
109         OutputTermStr(term_vi, 0);
110         AddEventGen(&keyGen);
111
112         move(0, 0);
113         addstr("Netris ");
114         addstr(version_string);
115         addstr(" (C) 1994-1996,1999  Mark H. Weaver     "
116                         "\"netris -h\" for more info");
117         statusYPos = 22;
118         statusXPos = 0;
119 }
120
121 ExtFunc void CleanupScreens(void)
122 {
123         if (screens_dirty) {
124                 RemoveEventGen(&keyGen);
125                 endwin();
126                 OutputTermStr(term_ve, 1);
127                 screens_dirty = 0;
128         }
129 }
130
131 ExtFunc void GetTermcapInfo(void)
132 {
133         char *term, *buf, *data;
134         int bufSize = 10240;
135
136         if (!(term = getenv("TERM")))
137                 return;
138         if (tgetent(scratch, term) == 1) {
139                 /*
140                  * Make the buffer HUGE, since tgetstr is unsafe.
141                  * Allocate it on the heap too.
142                  */
143                 data = buf = malloc(bufSize);
144
145                 /*
146                  * There is no standard include file for tgetstr, no prototype
147                  * definitions.  I like casting better than using my own prototypes
148                  * because if I guess the prototype, I might be wrong, especially
149                  * with regards to "const".
150                  */
151                 term_vi = (char *)tgetstr("vi", &data);
152                 term_ve = (char *)tgetstr("ve", &data);
153
154                 /* Okay, so I'm paranoid; I just don't like unsafe routines */
155                 if (data > buf + bufSize)
156                         fatal("tgetstr overflow, you must have a very sick termcap");
157
158                 /* Trim off the unused portion of buffer */
159                 buf = realloc(buf, data - buf);
160         }
161
162         /*
163          * If that fails, use hardcoded vt220 codes.
164          * They don't seem to do anything bad on vt100's, so
165          * we'll try them just in case they work.
166          */
167         if (!term_vi || !term_ve) {
168                 static char *vts[] = {
169                                 "vt100", "vt101", "vt102",
170                                 "vt200", "vt220", "vt300",
171                                 "vt320", "vt400", "vt420",
172                                 "screen", "xterm", NULL };
173                 int i;
174
175                 for (i = 0; vts[i]; i++)
176                         if (!strcmp(term, vts[i]))
177                         {
178                                 term_vi = "\033[?25l";
179                                 term_ve = "\033[?25h";
180                                 break;
181                         }
182         }
183         if (!term_vi || !term_ve)
184                 term_vi = term_ve = NULL;
185 }
186
187 ExtFunc void OutputTermStr(char *str, int flush)
188 {
189         if (str) {
190                 fputs(str, stdout);
191                 if (flush)
192                         fflush(stdout);
193         }
194 }
195
196 ExtFunc void InitScreen(int scr)
197 {
198         int y, x;
199
200         if (scr == 0)
201                 boardXPos[scr] = 1;
202         else
203                 boardXPos[scr] = boardXPos[scr - 1] +
204                                         2 * boardWidth[scr - 1] + 3;
205         boardYPos[scr] = 22;
206         if (statusXPos < boardXPos[scr] + 2 * boardWidth[scr] + 3)
207                 statusXPos = boardXPos[scr] + 2 * boardWidth[scr] + 3;
208         for (y = boardVisible[scr] - 1; y >= 0; --y) {
209                 move(boardYPos[scr] - y, boardXPos[scr] - 1);
210                 addch('|');
211                 for (x = boardWidth[scr] - 1; x >= 0; --x)
212                         addstr("  ");
213                 move(boardYPos[scr] - y, boardXPos[scr] + 2 * boardWidth[scr]);
214                 addch('|');
215         }
216         for (y = boardVisible[scr]; y >= -1; y -= boardVisible[scr] + 1) {
217                 move(boardYPos[scr] - y, boardXPos[scr] - 1);
218                 addch('+');
219                 for (x = boardWidth[scr] - 1; x >= 0; --x)
220                         addstr("--");
221                 addch('+');
222         }
223 }
224
225 ExtFunc void CleanupScreen(int scr)
226 {
227 }
228
229 static void PlotBlock1(int scr, int y, int x, BlockType type)
230 {
231         int colorIndex = abs(type);
232
233         move(boardYPos[scr] - y, boardXPos[scr] + 2 * x);
234
235         if (type == BT_none)
236                 addstr("  ");
237         else
238         {
239                 if (standoutEnable)
240                 {
241 #ifdef HAVE_NCURSES
242                         if (haveColor)
243                                 attrset(COLOR_PAIR(colorIndex));
244                         else
245 #endif
246                                 standout();
247                 }
248
249                 addstr(type > 0 ? "[]" : "$$");
250                 standend();
251         }
252 }
253
254 ExtFunc void PlotBlock(int scr, int y, int x, BlockType type)
255 {
256         if (y >= 0 && y < boardVisible[scr] && x >= 0 && x < boardWidth[scr])
257                 PlotBlock1(scr, y, x, type);
258 }
259
260 ExtFunc void PlotUnderline(int scr, int x, int flag)
261 {
262         move(boardYPos[scr] + 1, boardXPos[scr] + 2 * x);
263         addstr(flag ? "==" : "--");
264 }
265
266 ExtFunc void ShowDisplayInfo(void)
267 {
268         if (game == GT_classicTwo) {
269                 move(statusYPos - 5, statusXPos);
270                 printw("Enemy lines: %3d/%4d", enemyLinesCleared, enemyTotalLinesCleared);
271         }
272         move(statusYPos - 4, statusXPos);
273         printw("My lines:    %3d/%4d", myLinesCleared, myTotalLinesCleared);
274         move(statusYPos - 3, statusXPos);
275         printw("Won:  %3d", won);
276         move(statusYPos - 2, statusXPos);
277         printw("Lost: %3d", lost);
278
279         move(statusYPos - 1, statusXPos);
280         switch(gameState) {
281         case STATE_WAIT_CONNECTION:
282                 addstr("Waiting for opponent...      ");
283                 break;
284         case STATE_WAIT_KEYPRESS:
285                 addstr("Press the key for a new game.");
286                 break;
287         default:
288                 addstr("                             ");
289         }
290
291         move(statusYPos - 9, statusXPos);
292         printw("Seed: %d", initSeed);
293         clrtoeol();
294         move(statusYPos - 8, statusXPos);
295         printw("Speed: %dms", speed / 1000);
296         clrtoeol();
297         if (robotEnable) {
298                 move(statusYPos - 7, statusXPos);
299                 if (fairRobot)
300                         addstr("Controlled by a fair robot");
301                 else
302                         addstr("Controlled by a robot");
303                 clrtoeol();
304         }
305         if (opponentFlags & SCF_usingRobot) {
306                 move(statusYPos - 6, statusXPos);
307                 if (opponentFlags & SCF_fairRobot)
308                         addstr("The opponent is a fair robot");
309                 else
310                         addstr("The opponent is a robot");
311                 clrtoeol();
312         }
313 }
314
315 ExtFunc void UpdateOpponentDisplay(void)
316 {
317         move(1, 0);
318         printw("Playing %s@%s", opponentName, opponentHost);
319         clrtoeol();
320 }
321
322 ExtFunc void ShowPause(int pausedByMe, int pausedByThem)
323 {
324         move(statusYPos - 3, statusXPos);
325         if (pausedByThem)
326                 addstr("Game paused by opponent");
327         else
328                 clrtoeol();
329         move(statusYPos - 2, statusXPos);
330         if (pausedByMe)
331                 addstr("Game paused by you");
332         else
333                 clrtoeol();
334 }
335
336 ExtFunc void Message(char *s)
337 {
338         static int line = 0;
339
340         move(statusYPos - 20 + line, statusXPos);
341         addstr(s);      /* XXX Should truncate long lines */
342         clrtoeol();
343         line = (line + 1) % 10;
344         move(statusYPos - 20 + line, statusXPos);
345         clrtoeol();
346 }
347
348 ExtFunc void RefreshScreen(void)
349 {
350         static char timeStr[2][32];
351         time_t theTime;
352
353         time(&theTime);
354         strftime(timeStr[0], 30, "%I:%M %p", localtime(&theTime));
355         /* Just in case the local curses library sucks */
356         if (strcmp(timeStr[0], timeStr[1]))
357         {
358                 move(statusYPos, statusXPos);
359                 addstr(timeStr[0]);
360                 strcpy(timeStr[1], timeStr[0]);
361         }
362         move(boardYPos[0] + 1, boardXPos[0] + 2 * boardWidth[0] + 1);
363         refresh();
364 }
365
366 ExtFunc void ScheduleFullRedraw(void)
367 {
368         touchwin(stdscr);
369 }
370
371 static MyEventType KeyGenFunc(EventGenRec *gen, MyEvent *event)
372 {
373         if (MyRead(gen->fd, &event->u.key, 1))
374                 return E_key;
375         else
376                 return E_none;
377 }
378
379 /*
380  * vi: ts=4 ai
381  * vim: noai si
382  */