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