unofficial version 0.6: first major updates
[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 = Game.color && has_colors();
87         if (haveColor) {
88                 int i = 0;
89
90                 start_color();
91                 for (i = 0; myColorTable[i].type != BT_none; ++i)
92                         init_pair(myColorTable[i].type, COLOR_BLACK,
93                                         myColorTable[i].color);
94         }
95 #else
96         haveColor = 0;
97 #endif
98
99         AtExit(CleanupScreens);
100         RestoreSignals(NULL, &oldMask);
101
102         cbreak();
103         noecho();
104         OutputTermStr(term_vi, 0);
105         AddEventGen(&keyGen);
106
107         move(0, 1);
108         addstr("NETRIS ");
109         addstr(version_string);
110         {
111                 int rows, cols;
112
113                 getmaxyx(stdscr, rows, cols);
114                 move(0, cols - 48);
115                 addstr("(C)1994-1996,1999 Mark H. Weaver, (C)2002 Shiar");
116 //              addstr("    \"netris -h\" for more info");
117         }
118         statusYPos = 22;
119         statusXPos = 0;
120 }
121
122 ExtFunc void CleanupScreens(void)
123 {
124         RemoveEventGen(&keyGen);
125         endwin();
126         OutputTermStr(term_ve, 1);
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 DrawField(int scr)
195 {
196         {
197                 int y, x;
198
199                 for (y = Players[scr].boardVisible - 1; y >= 0; --y) {
200                         mvaddch(boardYPos[scr] - y, boardXPos[scr] - 1,
201                                 Game.ascii ? '|' : ACS_VLINE); //left
202                         mvaddch(boardYPos[scr] - y,
203                                 boardXPos[scr] + 2 * Players[scr].boardWidth,
204                                 Game.ascii ? '|' : ACS_VLINE); //right
205                 }
206                 move(2, boardXPos[scr] - 1); //top
207                 addch(Game.ascii ? '+' : ACS_ULCORNER);
208                 for (x = Players[scr].boardWidth * 2 - 1; x >= 0; --x)
209                         addch(Game.ascii ? '-' : ACS_HLINE);
210                 addch(Game.ascii ? '+' : ACS_URCORNER);
211                 move(boardYPos[scr] + 1, boardXPos[scr] - 1); //bottom
212                 addch(Game.ascii ? '+' : ACS_LLCORNER);
213                 for (x = Players[scr].boardWidth * 2 - 1; x >= 0; --x)
214                         addch(Game.ascii ? '-' : ACS_HLINE);
215                 addch(Game.ascii ? '+' : ACS_LRCORNER);
216         } //draw field grid
217
218         {
219                 char userstr[300];
220
221                 sprintf(userstr, "%s <%s>", Players[scr].name, Players[scr].host);
222                 userstr[20 - 7*((Players[scr].flags & SCF_usingRobot) != 0)
223                         - 5*((Players[scr].flags & SCF_fairRobot) != 0)] = 0;
224                 mvaddstr(1, boardXPos[scr], userstr);
225
226                 if (Players[scr].flags & SCF_usingRobot) {
227                         addstr((Players[scr].flags & SCF_fairRobot)
228                                 ? "(fair robot)" : "(robot)");
229                 }
230         } //display playername/host
231 } //DrawScreen
232
233 ExtFunc void InitFields()
234 {
235         int scr, prevscr;
236
237         boardXPos[me] = 1;
238         boardYPos[me] = 22;
239         prevscr = me;
240         for (scr = 1; scr <= totalPlayers + 1; scr++) if (scr != me) {
241                 boardXPos[scr] =
242                         boardXPos[prevscr] + 2 * Players[prevscr].boardWidth + 3;
243                 if (prevscr == me)
244                         boardXPos[scr] += 24; //scorebar
245                 boardYPos[scr] = 22;
246                 prevscr = scr;
247         }
248         statusXPos = 2 * Players[me].boardWidth + 3;
249 } //InitScreen
250
251 ExtFunc void CleanupScreen(int scr)
252 {
253 }
254
255 ExtFunc void PlotBlock1(int scr, int y, int x, BlockType type)
256 {
257         int colorIndex = abs(type);
258
259         move(y, x);
260
261         if (type == BT_none)
262                 addstr("  ");
263         else
264         {
265                 if (Game.standout)
266                 {
267 #ifdef HAVE_NCURSES
268                         if (haveColor)
269                                 attrset(COLOR_PAIR(colorIndex));
270                         else
271 #endif
272                                 standout();
273                 }
274
275                 addstr(type ? "[]" : "$$");
276                 standend();
277         }
278 }
279
280 ExtFunc void PlotBlock(int scr, int y, int x, BlockType type)
281 {
282         if (y >= 0 && y < Players[scr].boardVisible &&
283                 x >= 0 && x < Players[scr].boardWidth)
284           PlotBlock1(scr, boardYPos[scr] - y, boardXPos[scr] + 2 * x, type);
285 }
286
287 ExtFunc void PlotUnderline(int scr, int x, int flag)
288 {
289   move(boardYPos[scr] + 1, boardXPos[scr] + 2 * x);
290   if (Game.ascii)
291         addstr(flag ? "==" : "--");
292   else {
293         addch(flag ? ACS_BTEE : ACS_HLINE);
294         addch(flag ? ACS_BTEE : ACS_HLINE);
295   }
296 }
297
298 ExtFunc void ShowDisplayInfo(void)
299 {
300   move(statusYPos - 9, statusXPos);
301   printw("Seed: %010d", Game.seed);
302   //    move(statusYPos - 8, statusXPos);
303   //    printw("Speed: %dms ", speed / 1000);
304   if (robotEnable) {
305         move(statusYPos - 6, statusXPos);
306         if (fairRobot)
307           addstr("Controlled by a fair robot");
308         else
309           addstr("Controlled by a robot");
310         //              clrtoeol();
311   }
312   if (Players[1].flags & SCF_usingRobot) {
313         move(statusYPos - 5, statusXPos);
314         if (Players[1].flags & SCF_fairRobot)
315           addstr("The opponent is a fair robot");
316         else
317           addstr("The opponent is a robot");
318         //              clrtoeol();
319   }
320 }
321
322 ExtFunc void ShowScore(int scr, struct _Score score)
323 {
324         float timer;
325
326         move(6, statusXPos); addstr("Next:          ");
327         move(7, statusXPos + 7);    addstr("        ");
328         ShapeIterate(Players[scr].nextShape, scr,
329           ShapeToNetNum(Players[scr].nextShape) == 15 ? 13 : 14,
330           statusXPos / 2 + 5, 1, GlanceFunc, NULL);
331         move(statusYPos - 21 + 1, statusXPos);
332         printw("Score:%6d  level: %2d", score.score, score.level);
333         move(statusYPos - 20 + 1, statusXPos);
334         timer = CurTimeval() / 1e6;
335         printw("Lines:%6d", score.lines);
336         if (timer > 4) {
337                 printw("  ppm:%5.1f", score.drops * 60 / timer);
338                 move(statusYPos - 18, statusXPos);
339                 if (score.lines > 0)
340                         printw("yield:  %3d%%", 100 * score.adds / score.lines);
341                 else addstr("            ");
342                 printw("  apm:%5.1f", score.adds * 60 / timer);
343         }
344 }
345
346 ExtFunc void ShowPause(int pausedByMe, int pausedByThem)
347 {
348         move(statusYPos - 3, statusXPos);
349         if (pausedByThem)
350                 addstr("Game paused by opponent");
351         else
352                 addstr("                       ");
353         move(statusYPos - 2, statusXPos);
354         if (pausedByMe)
355                 addstr("Game paused by you");
356         else
357                 addstr("                  ");
358 }
359
360 ExtFunc void Message(char *s)
361 {
362         static int line = 0;
363
364 //      move(statusYPos - 20 + line, statusXPos);
365         move(statusYPos + 2 + line, 1);
366         addstr(s);      /* XXX Should truncate long lines */
367         clrtoeol();
368         line = (line + 1) % 10;
369 //      move(statusYPos - 20 + line, statusXPos);
370         move(statusYPos + 2 + line, 1);
371         clrtoeol();
372 }
373
374 ExtFunc void ShowTime(void)
375 {
376         move(statusYPos, statusXPos);
377         printw("Timer: %.0f ", CurTimeval() / 1e6);
378         move(boardYPos[0] + 1, boardXPos[0] + 2 * Players[0].boardWidth + 1);
379 //      refresh();
380 }
381
382 ExtFunc void ScheduleFullRedraw(void)
383 {
384         touchwin(stdscr);
385 }
386
387 static MyEventType KeyGenFunc(EventGenRec *gen, MyEvent *event)
388 {
389         if (MyRead(gen->fd, &event->u.key, 1))
390                 return E_key;
391         else
392                 return E_none;
393 }
394
395 /*
396  * vi: ts=4 ai
397  * vim: noai si
398  */