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