2 * Netris -- A free networked version of T*tris
3 * Copyright (C) 1994-1996,1999 Mark H. Weaver <mhw@netris.org>
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.
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.
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.
19 * $Id: curses.c,v 1.33 1999/05/16 06:56:25 mhw Exp $
23 #include <sys/types.h>
29 #ifdef NCURSES_VERSION
38 { BT_white, COLOR_WHITE },
39 { BT_blue, COLOR_BLUE },
40 { BT_magenta, COLOR_MAGENTA },
41 { BT_cyan, COLOR_CYAN },
42 { BT_yellow, COLOR_YELLOW },
43 { BT_green, COLOR_GREEN },
44 { BT_red, COLOR_RED },
49 ExtFunc void PlotBlock1(int scr, int y, int x, BlockType type);
50 static MyEventType KeyGenFunc(EventGenRec *gen, MyEvent *event);
52 static EventGenRec keyGen =
53 { NULL, 0, FT_read, STDIN_FILENO, KeyGenFunc, EM_key };
55 static int boardYPos[MAX_SCREENS], boardXPos[MAX_SCREENS];
56 static int statusYPos, statusXPos;
59 static char *term_vi; /* String to make cursor invisible */
60 static char *term_ve; /* String to make cursor visible */
62 ExtFunc void InitScreens(void)
69 * Block signals while initializing curses. Otherwise a badly timed
70 * Ctrl-C during initialization might leave the terminal in a bad state.
72 BlockSignals(&oldMask, SIGINT, 0);
84 haveColor = Game.color && has_colors();
89 for (i = 0; myColorTable[i].type != BT_none; ++i)
90 init_pair(myColorTable[i].type, COLOR_BLACK,
91 myColorTable[i].color);
97 AtExit(CleanupScreens);
98 RestoreSignals(NULL, &oldMask);
102 OutputTermStr(term_vi, 0);
103 AddEventGen(&keyGen);
107 addstr(version_string);
111 getmaxyx(stdscr, rows, cols);
113 addstr("(C)1994-1996,1999 Mark H. Weaver, (C)2002 Shiar");
114 // addstr(" \"netris -h\" for more info");
120 ExtFunc void CleanupScreens(void)
122 RemoveEventGen(&keyGen);
124 OutputTermStr(term_ve, 1);
127 ExtFunc void GetTermcapInfo(void)
129 char *term, *buf, *data;
132 if (!(term = getenv("TERM")))
134 if (tgetent(scratch, term) == 1) {
136 * Make the buffer HUGE, since tgetstr is unsafe.
137 * Allocate it on the heap too.
139 data = buf = malloc(bufSize);
142 * There is no standard include file for tgetstr, no prototype
143 * definitions. I like casting better than using my own prototypes
144 * because if I guess the prototype, I might be wrong, especially
145 * with regards to "const".
147 term_vi = (char *)tgetstr("vi", &data);
148 term_ve = (char *)tgetstr("ve", &data);
150 /* Okay, so I'm paranoid; I just don't like unsafe routines */
151 if (data > buf + bufSize)
152 fatal("tgetstr overflow, you must have a very sick termcap");
154 /* Trim off the unused portion of buffer */
155 buf = realloc(buf, data - buf);
159 * If that fails, use hardcoded vt220 codes.
160 * They don't seem to do anything bad on vt100's, so
161 * we'll try them just in case they work.
163 if (!term_vi || !term_ve) {
164 static char *vts[] = {
165 "vt100", "vt101", "vt102",
166 "vt200", "vt220", "vt300",
167 "vt320", "vt400", "vt420",
168 "screen", "xterm", NULL };
171 for (i = 0; vts[i]; i++)
172 if (!strcmp(term, vts[i]))
174 term_vi = "\033[?25l";
175 term_ve = "\033[?25h";
179 if (!term_vi || !term_ve)
180 term_vi = term_ve = NULL;
183 ExtFunc void OutputTermStr(char *str, int flush)
187 if (flush) fflush(stdout);
191 ExtFunc void DrawField(int scr)
192 { //drow field for player scr
196 getmaxyx(stdscr, y, x);
197 if (x < boardXPos[scr] + 2 * Players[scr].boardWidth + 1) {
198 Players[scr].spy = 0;
202 for (y = Players[scr].boardVisible - 1; y >= 0; --y) {
203 mvaddch(boardYPos[scr] - y, boardXPos[scr] - 1,
204 Game.ascii ? '|' : ACS_VLINE); //left
205 mvaddch(boardYPos[scr] - y,
206 boardXPos[scr] + 2 * Players[scr].boardWidth,
207 Game.ascii ? '|' : ACS_VLINE); //right
209 move(2, boardXPos[scr] - 1); //top
210 addch(Game.ascii ? '+' : ACS_ULCORNER);
211 for (x = Players[scr].boardWidth * 2 - 1; x >= 0; --x)
212 addch(Game.ascii ? '-' : ACS_HLINE);
213 addch(Game.ascii ? '+' : ACS_URCORNER);
214 move(boardYPos[scr] + 1, boardXPos[scr] - 1); //bottom
215 addch(Game.ascii ? '+' : ACS_LLCORNER);
216 for (x = Players[scr].boardWidth * 2 - 1; x >= 0; --x)
217 addch(Game.ascii ? '-' : ACS_HLINE);
218 addch(Game.ascii ? '+' : ACS_LRCORNER);
224 if (Players[scr].host && Players[scr].host[0])
225 sprintf(userstr, "%s <%s>", Players[scr].name, Players[scr].host);
226 else sprintf(userstr, "%s", Players[scr].name);
227 userstr[20 - 7*((Players[scr].flags & SCF_usingRobot) != 0)
228 - 5*((Players[scr].flags & SCF_fairRobot) != 0)] = 0;
229 mvaddstr(1, boardXPos[scr], userstr);
231 if (Players[scr].flags & SCF_usingRobot) {
232 addstr((Players[scr].flags & SCF_fairRobot)
233 ? "(fair robot)" : "(robot)");
235 } //display playername/host
240 ExtFunc void InitFields()
241 { //place fields for all players
244 statusXPos = 2 * Players[me].boardWidth + 3;
248 for (scr = 1; scr <= totalPlayers + 1; scr++) if (scr != me) {
250 boardXPos[prevscr] + 2 * Players[prevscr].boardWidth + 3;
252 boardXPos[scr] += 24; //scorebar
258 ExtFunc void CleanupScreen(int scr)
262 ExtFunc void PlotBlock1(int scr, int y, int x, BlockType type)
264 int colorIndex = abs(type);
276 attrset(COLOR_PAIR(colorIndex));
282 addstr(type ? "[]" : "$$");
287 ExtFunc void PlotBlock(int scr, int y, int x, BlockType type)
289 if (y >= 0 && y < Players[scr].boardVisible &&
290 x >= 0 && x < Players[scr].boardWidth)
291 PlotBlock1(scr, boardYPos[scr] - y, boardXPos[scr] + 2 * x, type);
294 ExtFunc void PlotUnderline(int scr, int x, int flag)
296 move(boardYPos[scr] + 1, boardXPos[scr] + 2 * x);
298 addstr(flag ? "==" : "--");
300 addch(flag ? ACS_BTEE : ACS_HLINE);
301 addch(flag ? ACS_BTEE : ACS_HLINE);
305 ExtFunc void ShowScore(int scr, struct _Score score)
309 move(6, statusXPos); addstr("Next: ");
310 move(7, statusXPos + 7); addstr(" ");
311 ShapeIterate(Players[scr].nextShape, scr,
312 ShapeToNetNum(Players[scr].nextShape) == 15 ? 13 : 14,
313 statusXPos / 2 + 5, 1, GlanceFunc, NULL);
314 move(statusYPos - 21 + 1, statusXPos);
315 printw("Score:%6d level: %2d", score.score, score.level);
316 move(statusYPos - 20 + 1, statusXPos);
317 timer = CurTimeval() / 1e6;
318 printw("Lines:%6d", score.lines);
320 printw(" ppm:%5.1f", score.drops * 60 / timer);
321 move(statusYPos - 18, statusXPos);
323 printw("yield: %3d%%", 100 * score.adds / score.lines);
325 printw(" apm:%5.1f", score.adds * 60 / timer);
329 ExtFunc void FieldMessage(int playa, char *message)
330 { //put a message over playa's field
331 if (!Players[playa].spy) return;
333 char s[MAX_BOARD_WIDTH+1];
334 memset(s, ' ', MAX_BOARD_WIDTH);
335 memcpy(&s[Players[playa].boardWidth - strlen(message) / 2],
336 message, strlen(message));
337 s[Players[playa].boardWidth * 2] = 0;
338 if (Game.standout) standout();
339 mvprintw(boardYPos[playa] - Players[playa].boardVisible / 2,
340 boardXPos[playa], "%s", s);
345 y = Players[playa].boardVisible / 2;
346 for (x = 0; x <= Players[playa].boardWidth; x++)
347 PlotBlock(playa, y, x, GetBlock(playa, y, x));
351 ExtFunc void ShowPause(int playa)
352 { //put paused over player's field
353 if (Players[playa].flags & SCF_paused)
354 FieldMessage(playa, "P A U S E D");
355 else FieldMessage(playa, NULL);
359 ExtFunc void Message(char *s)
363 // move(statusYPos - 20 + line, statusXPos);
364 move(statusYPos + 2 + line, 1);
365 addstr(s); /* XXX Should truncate long lines */
367 line = (line + 1) % 10;
368 // move(statusYPos - 20 + line, statusXPos);
369 move(statusYPos + 2 + line, 1);
373 ExtFunc void ShowTime(void)
375 move(statusYPos, statusXPos);
376 printw("Timer: %.0f ", CurTimeval() / 1e6);
377 move(boardYPos[0] + 1, boardXPos[0] + 2 * Players[0].boardWidth + 1);
381 ExtFunc void ScheduleFullRedraw(void)
384 } //ScheduleFullRedraw
386 static MyEventType KeyGenFunc(EventGenRec *gen, MyEvent *event)
388 if (MyRead(gen->fd, &event->u.key, 1))