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