Removed notice about xwin. There's no xwin.c ;-).
[tetrinet.git] / tty.c
blob93d0e7df62bf832f185aed09e6e9daab4e9ef8d3
1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2 * This program is public domain.
4 * Text terminal I/O routines.
5 */
7 #define _GNU_SOURCE /* strsignal() - FIXME!!! --pasky */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <ctype.h>
14 #include <curses.h>
15 #include <errno.h>
16 #include <signal.h>
17 #include <sys/time.h>
18 #include "tetrinet.h"
19 #include "tetris.h"
20 #include "io.h"
22 /*************************************************************************/
24 #define MY_HLINE (fancy ? ACS_HLINE : '-')
25 #define MY_VLINE (fancy ? ACS_VLINE : '|')
26 #define MY_ULCORNER (fancy ? ACS_ULCORNER : '+')
27 #define MY_URCORNER (fancy ? ACS_URCORNER : '+')
28 #define MY_LLCORNER (fancy ? ACS_LLCORNER : '+')
29 #define MY_LRCORNER (fancy ? ACS_LRCORNER : '+')
31 #define MY_HLINE2 (fancy ? (ACS_HLINE | A_BOLD) : '=')
32 #define MY_BOLD (fancy ? A_BOLD : 0)
34 /*************************************************************************/
35 /******************************* Input stuff *****************************/
36 /*************************************************************************/
38 /* Return either an ASCII code 0-255, a K_* value, or -1 if server input is
39 * waiting. Return -2 if we run out of time with no input.
42 static int wait_for_input(int msec)
44 fd_set fds;
45 struct timeval tv;
46 int c;
47 static int escape = 0;
49 FD_ZERO(&fds);
50 FD_SET(0, &fds);
51 FD_SET(server_sock, &fds);
52 tv.tv_sec = msec/1000;
53 tv.tv_usec = (msec*1000) % 1000000;
54 while (select(server_sock+1, &fds, NULL, NULL, msec<0 ? NULL : &tv) < 0) {
55 if (errno != EINTR)
56 perror("Warning: select() failed");
58 if (FD_ISSET(0, &fds)) {
59 c = getch();
60 if (!escape && c == 27) { /* Escape */
61 escape = 1;
62 c = wait_for_input(1000);
63 escape = 0;
64 if (c < 0)
65 return 27;
66 else
67 return c;
69 if (c == KEY_UP)
70 return K_UP;
71 else if (c == KEY_DOWN)
72 return K_DOWN;
73 else if (c == KEY_LEFT)
74 return K_LEFT;
75 else if (c == KEY_RIGHT)
76 return K_RIGHT;
77 else if (c == KEY_F(1) || c == ('1'|0x80) || (escape && c == '1'))
78 return K_F1;
79 else if (c == KEY_F(2) || c == ('2'|0x80) || (escape && c == '2'))
80 return K_F2;
81 else if (c == KEY_F(3) || c == ('3'|0x80) || (escape && c == '3'))
82 return K_F3;
83 else if (c == KEY_F(4) || c == ('4'|0x80) || (escape && c == '4'))
84 return K_F4;
85 else if (c == KEY_F(5) || c == ('5'|0x80) || (escape && c == '5'))
86 return K_F5;
87 else if (c == KEY_F(6) || c == ('6'|0x80) || (escape && c == '6'))
88 return K_F6;
89 else if (c == KEY_F(7) || c == ('7'|0x80) || (escape && c == '7'))
90 return K_F7;
91 else if (c == KEY_F(8) || c == ('8'|0x80) || (escape && c == '8'))
92 return K_F8;
93 else if (c == KEY_F(9) || c == ('9'|0x80) || (escape && c == '9'))
94 return K_F9;
95 else if (c == KEY_F(10) || c == ('0'|0x80) || (escape && c == '0'))
96 return K_F10;
97 else if (c == KEY_F(11))
98 return K_F11;
99 else if (c == KEY_F(12))
100 return K_F12;
101 else if (c == KEY_BACKSPACE)
102 return 8;
103 else if (c >= 0x0100)
104 return K_INVALID;
105 else if (c == 7) /* ^G */
106 return 27; /* Escape */
107 else
108 return c;
109 } /* if (FD_ISSET(0, &fds)) */
110 else if (FD_ISSET(server_sock, &fds))
111 return -1;
112 else
113 return -2; /* out of time */
116 /*************************************************************************/
117 /****************************** Output stuff *****************************/
118 /*************************************************************************/
120 /* Size of the screen */
121 static int scrwidth, scrheight;
123 /* Is color available? */
124 static int has_color;
126 /*************************************************************************/
128 /* Text buffers: */
130 typedef struct {
131 int x, y, width, height;
132 int line;
133 WINDOW *win; /* NULL if not currently displayed */
134 char **text;
135 } TextBuffer;
137 static TextBuffer plinebuf, gmsgbuf, attdefbuf;
139 /*************************************************************************/
141 /* Window for typing in-game text, and its coordinates: */
143 static WINDOW *gmsg_inputwin;
144 static int gmsg_inputpos, gmsg_inputheight;
146 /*************************************************************************/
147 /*************************************************************************/
149 /* Clean up the screen on exit. */
151 static void screen_cleanup()
153 wmove(stdscr, scrheight-1, 0);
154 wrefresh(stdscr);
155 endwin();
156 printf("\n");
159 /*************************************************************************/
161 /* Little signal handler that just does an exit(1) (thereby getting our
162 * cleanup routine called), except for TSTP, which does a clean suspend.
165 static void (*old_tstp)(int sig);
167 static void sighandler(int sig)
169 if (sig != SIGTSTP) {
170 endwin();
171 if (sig != SIGINT)
172 fprintf(stderr, "%s\n", strsignal(sig));
173 exit(1);
175 endwin();
176 signal(SIGTSTP, old_tstp);
177 raise(SIGTSTP);
178 doupdate();
179 signal(SIGTSTP, sighandler);
182 /*************************************************************************/
183 /*************************************************************************/
185 #define MAXCOLORS 256
187 static int colors[MAXCOLORS][2] = { {-1,-1} };
189 /* Return a color attribute value. */
191 static long getcolor(int fg, int bg)
193 int i;
195 if (colors[0][0] < 0) {
196 start_color();
197 memset(colors, -1, sizeof(colors));
198 colors[0][0] = COLOR_WHITE;
199 colors[0][1] = COLOR_BLACK;
201 if (fg == COLOR_WHITE && bg == COLOR_BLACK)
202 return COLOR_PAIR(0);
203 for (i = 1; i < MAXCOLORS; i++) {
204 if (colors[i][0] == fg && colors[i][1] == bg)
205 return COLOR_PAIR(i);
207 for (i = 1; i < MAXCOLORS; i++) {
208 if (colors[i][0] < 0) {
209 if (init_pair(i, fg, bg) == ERR)
210 continue;
211 colors[i][0] = fg;
212 colors[i][1] = bg;
213 return COLOR_PAIR(i);
216 return -1;
219 /*************************************************************************/
220 /*************************************************************************/
222 /* Set up the screen stuff. */
224 static void screen_setup(void)
226 /* Avoid messy keyfield signals while we're setting up */
227 signal(SIGINT, SIG_IGN);
228 signal(SIGTSTP, SIG_IGN);
230 initscr();
231 cbreak();
232 noecho();
233 nodelay(stdscr, TRUE);
234 keypad(stdscr, TRUE);
235 leaveok(stdscr, TRUE);
236 if ((has_color = has_colors()))
237 start_color();
238 getmaxyx(stdscr, scrheight, scrwidth);
239 scrwidth--; /* Don't draw in last column--this can cause scroll */
241 /* Cancel all this when we exit. */
242 atexit(screen_cleanup);
244 /* Catch signals so we can exit cleanly. */
245 signal(SIGINT, sighandler);
246 signal(SIGTERM, sighandler);
247 signal(SIGHUP, sighandler);
248 signal(SIGUSR1, sighandler);
249 signal(SIGUSR2, sighandler);
250 signal(SIGALRM, sighandler);
251 signal(SIGTSTP, sighandler);
252 #ifdef SIGXCPU
253 signal(SIGXCPU, sighandler);
254 #endif
255 #ifdef SIGXFSZ
256 signal(SIGXFSZ, sighandler);
257 #endif
259 /* Broken pipes don't want to bother us at all. */
260 signal(SIGPIPE, SIG_IGN);
263 /*************************************************************************/
265 /* Redraw everything on the screen. */
267 static void screen_refresh(void)
269 if (gmsg_inputwin)
270 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
271 if (plinebuf.win)
272 touchline(stdscr, plinebuf.y, plinebuf.height);
273 if (gmsgbuf.win)
274 touchline(stdscr, gmsgbuf.y, gmsgbuf.height);
275 if (attdefbuf.win)
276 touchline(stdscr, attdefbuf.y, attdefbuf.height);
277 wnoutrefresh(stdscr);
278 doupdate();
281 /*************************************************************************/
283 /* Like screen_refresh(), but clear the screen first. */
285 static void screen_redraw(void)
287 clearok(stdscr, TRUE);
288 screen_refresh();
291 /*************************************************************************/
292 /************************* Text buffer routines **************************/
293 /*************************************************************************/
295 /* Put a line of text in a text buffer. */
297 static void outline(TextBuffer *buf, const char *s)
299 if (buf->line == buf->height) {
300 if (buf->win)
301 scroll(buf->win);
302 memmove(buf->text, buf->text+1, (buf->height-1) * sizeof(char *));
303 buf->line--;
305 if (buf->win)
306 mvwaddstr(buf->win, buf->line, 0, s);
307 if (s != buf->text[buf->line]) /* check for restoring display */
308 buf->text[buf->line] = strdup(s);
309 buf->line++;
312 static void draw_text(int bufnum, const char *s)
314 char str[1024]; /* hopefully scrwidth < 1024 */
315 const char *t;
316 int indent = 0;
317 int x = 0, y = 0;
318 TextBuffer *buf;
320 switch (bufnum) {
321 case BUFFER_PLINE: buf = &plinebuf; break;
322 case BUFFER_GMSG: buf = &gmsgbuf; break;
323 case BUFFER_ATTDEF: buf = &attdefbuf; break;
324 default: return;
326 if (!buf->text)
327 return;
328 if (buf->win) {
329 getyx(stdscr, y, x);
330 attrset(getcolor(COLOR_WHITE, COLOR_BLACK));
332 while (*s && isspace(*s))
333 s++;
334 while (strlen(s) > buf->width - indent) {
335 t = s + buf->width - indent;
336 while (t >= s && !isspace(*t))
337 t--;
338 while (t >= s && isspace(*t))
339 t--;
340 t++;
341 if (t < s)
342 t = s + buf->width - indent;
343 if (indent > 0)
344 sprintf(str, "%*s", indent, "");
345 strncpy(str+indent, s, t-s);
346 str[t-s+indent] = 0;
347 outline(buf, str);
348 indent = 2;
349 while (isspace(*t))
350 t++;
351 s = t;
353 if (indent > 0)
354 sprintf(str, "%*s", indent, "");
355 strcpy(str+indent, s);
356 outline(buf, str);
357 if (buf->win) {
358 move(y, x);
359 screen_refresh();
363 /*************************************************************************/
365 /* Clear the contents of a text buffer. */
367 static void clear_text(int bufnum)
369 TextBuffer *buf;
370 int i;
372 switch (bufnum) {
373 case BUFFER_PLINE: buf = &plinebuf; break;
374 case BUFFER_GMSG: buf = &gmsgbuf; break;
375 case BUFFER_ATTDEF: buf = &attdefbuf; break;
376 default: return;
378 if (buf->text) {
379 for (i = 0; i < buf->height; i++) {
380 if (buf->text[i]) {
381 free(buf->text[i]);
382 buf->text[i] = NULL;
385 buf->line = 0;
387 if (buf->win) {
388 werase(buf->win);
389 screen_refresh();
393 /*************************************************************************/
395 /* Restore the contents of the given text buffer. */
397 static void restore_text(TextBuffer *buf)
399 buf->line = 0;
400 while (buf->line < buf->height && buf->text[buf->line])
401 outline(buf, buf->text[buf->line]);
404 /*************************************************************************/
406 /* Open a window for the given text buffer. */
408 static void open_textwin(TextBuffer *buf)
410 if (buf->height <= 0 || buf->width <= 0) {
411 char str[256];
412 move(scrheight-1, 0);
413 snprintf(str, sizeof(str), "ERROR: bad textwin size (%d,%d)",
414 buf->width, buf->height);
415 addstr(str);
416 exit(1);
418 if (!buf->win) {
419 buf->win = subwin(stdscr, buf->height, buf->width, buf->y, buf->x);
420 scrollok(buf->win, TRUE);
422 if (!buf->text)
423 buf->text = calloc(buf->height, sizeof(char *));
424 else
425 restore_text(buf);
428 /*************************************************************************/
430 /* Close the window for the given text buffer, if it's open. */
432 static void close_textwin(TextBuffer *buf)
434 if (buf->win) {
435 delwin(buf->win);
436 buf->win = NULL;
440 /*************************************************************************/
441 /************************ Field drawing routines *************************/
442 /*************************************************************************/
444 /* Are we on a wide screen (>=92 columns)? */
445 static int wide_screen = 0;
447 /* Field display X/Y coordinates. */
448 static const int own_coord[2] = {1,0};
449 static int other_coord[5][2] = /* Recomputed based on screen width */
450 { {30,0}, {47,0}, {64,0}, {47,24}, {64,24} };
452 /* Position of the status window. */
453 static const int status_coord[2] = {29,25};
454 static const int next_coord[2] = {41,24};
455 static const int alt_status_coord[2] = {29,2};
456 static const int alt_next_coord[2] = {30,8};
458 /* Position of the attacks/defenses window. */
459 static const int attdef_coord[2] = {28,28};
460 static const int alt_attdef_coord[2] = {28,24};
462 /* Position of the text window. X coordinate is ignored. */
463 static const int field_text_coord[2] = {0,47};
465 /* Information for drawing blocks. Color attributes are added to blocks in
466 * the setup_fields() routine. */
467 static int tile_chars[15] =
468 { ' ','#','#','#','#','#','a','c','n','r','s','b','g','q','o' };
470 /* Are we redrawing the entire display? */
471 static int field_redraw = 0;
473 /*************************************************************************/
474 /*************************************************************************/
476 /* Set up the field display. */
478 static void draw_own_field(void);
479 static void draw_other_field(int player);
480 static void draw_status(void);
481 static void draw_specials(void);
482 static void draw_gmsg_input(const char *s, int pos);
484 static void setup_fields(void)
486 int i, j, x, y, base, delta, attdefbot;
487 char buf[32];
489 if (!(tile_chars[0] & A_ATTRIBUTES)) {
490 for (i = 1; i < 15; i++)
491 tile_chars[i] |= A_BOLD;
492 tile_chars[1] |= getcolor(COLOR_BLUE, COLOR_BLACK);
493 tile_chars[2] |= getcolor(COLOR_YELLOW, COLOR_BLACK);
494 tile_chars[3] |= getcolor(COLOR_GREEN, COLOR_BLACK);
495 tile_chars[4] |= getcolor(COLOR_MAGENTA, COLOR_BLACK);
496 tile_chars[5] |= getcolor(COLOR_RED, COLOR_BLACK);
499 field_redraw = 1;
500 leaveok(stdscr, TRUE);
501 close_textwin(&plinebuf);
502 clear();
503 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
505 if (scrwidth >= 92) {
506 wide_screen = 1;
507 base = 41;
508 } else {
509 base = 28;
511 delta = (scrwidth - base) / 3;
512 base += 2 + (delta - (FIELD_WIDTH+5)) / 2;
513 other_coord[0][0] = base;
514 other_coord[1][0] = base + delta;
515 other_coord[2][0] = base + delta*2;
516 other_coord[3][0] = base + delta;
517 other_coord[4][0] = base + delta*2;
519 attdefbot = field_text_coord[1] - 1;
520 if (scrheight - field_text_coord[1] > 3) {
521 move(field_text_coord[1], 0);
522 hline(MY_HLINE2, scrwidth);
523 attdefbot--;
524 if (scrheight - field_text_coord[1] > 5) {
525 move(scrheight-2, 0);
526 hline(MY_HLINE2, scrwidth);
527 attrset(MY_BOLD);
528 move(scrheight-1, 0);
529 addstr("F1=Show Fields F2=Partyline F3=Winlist");
530 move(scrheight-1, scrwidth-8);
531 addstr("F10=Quit");
532 attrset(A_NORMAL);
533 gmsgbuf.y = field_text_coord[1]+1;
534 gmsgbuf.height = scrheight - field_text_coord[1] - 3;
535 } else {
536 gmsgbuf.y = field_text_coord[1]+1;
537 gmsgbuf.height = scrheight - field_text_coord[1] - 1;
539 } else {
540 gmsgbuf.y = field_text_coord[1];
541 gmsgbuf.height = scrheight - field_text_coord[1];
543 gmsgbuf.x = field_text_coord[0];
544 gmsgbuf.width = scrwidth;
545 open_textwin(&gmsgbuf);
547 x = own_coord[0];
548 y = own_coord[1];
549 sprintf(buf, "%d", my_playernum);
550 mvaddstr(y, x-1, buf);
551 for (i = 2; i < FIELD_HEIGHT*2 && players[my_playernum-1][i-2]; i++)
552 mvaddch(y+i, x-1, players[my_playernum-1][i-2]);
553 if (teams[my_playernum-1] != '\0') {
554 mvaddstr(y, x+FIELD_WIDTH*2+2, "T");
555 for (i = 2; i < FIELD_HEIGHT*2 && teams[my_playernum-1][i-2]; i++)
556 mvaddch(y+i, x+FIELD_WIDTH*2+2, teams[my_playernum-1][i-2]);
558 move(y, x);
559 vline(MY_VLINE, FIELD_HEIGHT*2);
560 move(y, x+FIELD_WIDTH*2+1);
561 vline(MY_VLINE, FIELD_HEIGHT*2);
562 move(y+FIELD_HEIGHT*2, x);
563 addch(MY_LLCORNER);
564 hline(MY_HLINE, FIELD_WIDTH*2);
565 move(y+FIELD_HEIGHT*2, x+FIELD_WIDTH*2+1);
566 addch(MY_LRCORNER);
567 mvaddstr(y+FIELD_HEIGHT*2+2, x, "Specials:");
568 draw_own_field();
569 draw_specials();
571 for (j = 0; j < 5; j++) {
572 x = other_coord[j][0];
573 y = other_coord[j][1];
574 move(y, x);
575 vline(MY_VLINE, FIELD_HEIGHT);
576 move(y, x+FIELD_WIDTH+1);
577 vline(MY_VLINE, FIELD_HEIGHT);
578 move(y+FIELD_HEIGHT, x);
579 addch(MY_LLCORNER);
580 hline(MY_HLINE, FIELD_WIDTH);
581 move(y+FIELD_HEIGHT, x+FIELD_WIDTH+1);
582 addch(MY_LRCORNER);
583 if (j+1 >= my_playernum) {
584 sprintf(buf, "%d", j+2);
585 mvaddstr(y, x-1, buf);
586 if (players[j+1]) {
587 for (i = 0; i < FIELD_HEIGHT-2 && players[j+1][i]; i++)
588 mvaddch(y+i+2, x-1, players[j+1][i]);
589 if (teams[j+1] != '\0') {
590 mvaddstr(y, x+FIELD_WIDTH+2, "T");
591 for (i = 0; i < FIELD_HEIGHT-2 && teams[j+1][i]; i++)
592 mvaddch(y+i+2, x+FIELD_WIDTH+2, teams[j+1][i]);
595 draw_other_field(j+2);
596 } else {
597 sprintf(buf, "%d", j+1);
598 mvaddstr(y, x-1, buf);
599 if (players[j]) {
600 for (i = 0; i < FIELD_HEIGHT-2 && players[j][i]; i++)
601 mvaddch(y+i+2, x-1, players[j][i]);
602 if (teams[j] != '\0') {
603 mvaddstr(y, x+FIELD_WIDTH+2, "T");
604 for (i = 0; i < FIELD_HEIGHT-2 && teams[j][i]; i++)
605 mvaddch(y+i+2, x+FIELD_WIDTH+2, teams[j][i]);
608 draw_other_field(j+1);
612 if (wide_screen) {
613 x = alt_status_coord[0];
614 y = alt_status_coord[1];
615 mvaddstr(y, x, "Lines:");
616 mvaddstr(y+1, x, "Level:");
617 x = alt_next_coord[0];
618 y = alt_next_coord[1];
619 mvaddstr(y-2, x-1, "Next piece:");
620 move(y-1, x-1);
621 addch(MY_ULCORNER);
622 hline(MY_HLINE, 8);
623 mvaddch(y-1, x+8, MY_URCORNER);
624 move(y, x-1);
625 vline(MY_VLINE, 8);
626 move(y, x+8);
627 vline(MY_VLINE, 8);
628 move(y+8, x-1);
629 addch(MY_LLCORNER);
630 hline(MY_HLINE, 8);
631 mvaddch(y+8, x+8, MY_LRCORNER);
632 } else {
633 x = status_coord[0];
634 y = status_coord[1];
635 mvaddstr(y-1, x, "Next piece:");
636 mvaddstr(y, x, "Lines:");
637 mvaddstr(y+1, x, "Level:");
639 if (playing_game)
640 draw_status();
642 attdefbuf.x = wide_screen ? alt_attdef_coord[0] : attdef_coord[0];
643 attdefbuf.y = wide_screen ? alt_attdef_coord[1] : attdef_coord[1];
644 attdefbuf.width = (other_coord[3][0]-1) - attdefbuf.x;
645 attdefbuf.height = (attdefbot+1) - attdefbuf.y;
646 open_textwin(&attdefbuf);
648 if (gmsg_inputwin) {
649 delwin(gmsg_inputwin);
650 gmsg_inputwin = NULL;
651 draw_gmsg_input(NULL, -1);
654 screen_refresh();
655 field_redraw = 0;
658 /*************************************************************************/
660 /* Display the player's own field. */
662 static void draw_own_field(void)
664 int x, y, x0, y0;
665 Field *f = &fields[my_playernum-1];
666 int shadow[4] = { -1, -1, -1, -1 };
668 if (dispmode != MODE_FIELDS)
669 return;
671 /* XXX: Code duplication with tetris.c:draw_piece(). --pasky */
672 if (playing_game && cast_shadow) {
673 int y = current_y - piecedata[current_piece][current_rotation].hot_y;
674 char *shape = (char *) piecedata[current_piece][current_rotation].shape;
675 int i, j;
677 for (j = 0; j < 4; j++) {
678 if (y+j < 0) {
679 shape += 4;
680 continue;
682 for (i = 0; i < 4; i++) {
683 if (*shape++)
684 shadow[i] = y + j;
689 x0 = own_coord[0]+1;
690 y0 = own_coord[1];
691 for (y = 0; y < 22; y++) {
692 for (x = 0; x < 12; x++) {
693 int c = tile_chars[(int) (*f)[y][x]];
695 if (playing_game && cast_shadow) {
696 PieceData *piece = &piecedata[current_piece][current_rotation];
697 int piece_x = current_x - piece->hot_x;
699 if (x >= piece_x && x <= piece_x + 3
700 && shadow[(x - piece_x)] >= 0
701 && shadow[(x - piece_x)] < y
702 && ((c & 0x7f) == ' ')) {
703 c = (c & (~0x7f)) | '.'
704 | getcolor(COLOR_BLACK, COLOR_BLACK) | A_BOLD;
708 mvaddch(y0+y*2, x0+x*2, c);
709 addch(c);
710 mvaddch(y0+y*2+1, x0+x*2, c);
711 addch(c);
714 if (gmsg_inputwin) {
715 delwin(gmsg_inputwin);
716 gmsg_inputwin = NULL;
717 draw_gmsg_input(NULL, -1);
719 if (!field_redraw)
720 screen_refresh();
723 /*************************************************************************/
725 /* Display another player's field. */
727 static void draw_other_field(int player)
729 int x, y, x0, y0;
730 Field *f;
732 if (dispmode != MODE_FIELDS)
733 return;
734 f = &fields[player-1];
735 if (player > my_playernum)
736 player--;
737 player--;
738 x0 = other_coord[player][0]+1;
739 y0 = other_coord[player][1];
740 for (y = 0; y < 22; y++) {
741 move(y0+y, x0);
742 for (x = 0; x < 12; x++) {
743 addch(tile_chars[(int) (*f)[y][x]]);
746 if (gmsg_inputwin) {
747 delwin(gmsg_inputwin);
748 gmsg_inputwin = NULL;
749 draw_gmsg_input(NULL, -1);
751 if (!field_redraw)
752 screen_refresh();
755 /*************************************************************************/
757 /* Display the current game status (level, lines, next piece). */
759 static void draw_status(void)
761 int x, y, i, j;
762 char buf[32], shape[4][4];
764 x = wide_screen ? alt_status_coord[0] : status_coord[0];
765 y = wide_screen ? alt_status_coord[1] : status_coord[1];
766 sprintf(buf, "%d", lines>99999 ? 99999 : lines);
767 mvaddstr(y, x+7, buf);
768 sprintf(buf, "%d", levels[my_playernum]);
769 mvaddstr(y+1, x+7, buf);
770 x = wide_screen ? alt_next_coord[0] : next_coord[0];
771 y = wide_screen ? alt_next_coord[1] : next_coord[1];
772 if (get_shape(next_piece, 0, shape) == 0) {
773 for (j = 0; j < 4; j++) {
774 if (!wide_screen)
775 move(y+j, x);
776 for (i = 0; i < 4; i++) {
777 if (wide_screen) {
778 move(y+j*2, x+i*2);
779 addch(tile_chars[(int) shape[j][i]]);
780 addch(tile_chars[(int) shape[j][i]]);
781 move(y+j*2+1, x+i*2);
782 addch(tile_chars[(int) shape[j][i]]);
783 addch(tile_chars[(int) shape[j][i]]);
784 } else
785 addch(tile_chars[(int) shape[j][i]]);
791 /*************************************************************************/
793 /* Display the special inventory and description of the current special. */
795 static const char *descs[] = {
796 " ",
797 "Add Line ",
798 "Clear Line ",
799 "Nuke Field ",
800 "Clear Random Blocks ",
801 "Switch Fields ",
802 "Clear Special Blocks",
803 "Block Gravity ",
804 "Blockquake ",
805 "Block Bomb "
808 static void draw_specials(void)
810 int x, y, i;
812 if (dispmode != MODE_FIELDS)
813 return;
814 x = own_coord[0];
815 y = own_coord[1]+45;
816 mvaddstr(y, x, descs[specials[0]+1]);
817 move(y+1, x+10);
818 i = 0;
819 while (i < special_capacity && specials[i] >= 0 && x < attdef_coord[0]-1) {
820 addch(tile_chars[specials[i]+6]);
821 i++;
822 x++;
824 while (x < attdef_coord[0]-1) {
825 addch(tile_chars[0]);
826 x++;
828 if (!field_redraw)
829 screen_refresh();
832 /*************************************************************************/
834 /* Display an attack/defense message. */
836 static const char *msgs[][2] = {
837 { "cs1", "1 Line Added to All" },
838 { "cs2", "2 Lines Added to All" },
839 { "cs4", "4 Lines Added to All" },
840 { "a", "Add Line" },
841 { "c", "Clear Line" },
842 { "n", "Nuke Field" },
843 { "r", "Clear Random Blocks" },
844 { "s", "Switch Fields" },
845 { "b", "Clear Special Blocks" },
846 { "g", "Block Gravity" },
847 { "q", "Blockquake" },
848 { "o", "Block Bomb" },
849 { NULL }
852 static void draw_attdef(const char *type, int from, int to)
854 int i, width;
855 char buf[512];
857 width = other_coord[4][0] - attdef_coord[0] - 1;
858 for (i = 0; msgs[i][0]; i++) {
859 if (strcmp(type, msgs[i][0]) == 0)
860 break;
862 if (!msgs[i][0])
863 return;
864 strcpy(buf, msgs[i][1]);
865 if (to != 0)
866 sprintf(buf+strlen(buf), " on %s", players[to-1]);
867 if (from == 0)
868 sprintf(buf+strlen(buf), " by Server");
869 else
870 sprintf(buf+strlen(buf), " by %s", players[from-1]);
871 draw_text(BUFFER_ATTDEF, buf);
874 /*************************************************************************/
876 /* Display the in-game text window. */
878 static void draw_gmsg_input(const char *s, int pos)
880 static int start = 0; /* Start of displayed part of input line */
881 static const char *last_s;
882 static int last_pos;
884 if (s)
885 last_s = s;
886 else
887 s = last_s;
888 if (pos >= 0)
889 last_pos = pos;
890 else
891 pos = last_pos;
893 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
895 if (!gmsg_inputwin) {
896 gmsg_inputpos = scrheight/2 - 1;
897 gmsg_inputheight = 3;
898 gmsg_inputwin =
899 subwin(stdscr, gmsg_inputheight, scrwidth, gmsg_inputpos, 0);
900 werase(gmsg_inputwin);
901 leaveok(gmsg_inputwin, FALSE);
902 leaveok(stdscr, FALSE);
903 mvwaddstr(gmsg_inputwin, 1, 0, "Text>");
906 if (strlen(s) < scrwidth-7) {
907 start = 0;
908 mvwaddstr(gmsg_inputwin, 1, 6, s);
909 wmove(gmsg_inputwin, 1, 6+strlen(s));
910 move(gmsg_inputpos+1, 6+strlen(s));
911 wclrtoeol(gmsg_inputwin);
912 wmove(gmsg_inputwin, 1, 6+pos);
913 move(gmsg_inputpos+1, 6+pos);
914 } else {
915 if (pos < start+8) {
916 start = pos-8;
917 if (start < 0)
918 start = 0;
919 } else if (pos > start + scrwidth-15) {
920 start = pos - (scrwidth-15);
921 if (start > strlen(s) - (scrwidth-7))
922 start = strlen(s) - (scrwidth-7);
924 mvwaddnstr(gmsg_inputwin, 1, 6, s+start, scrwidth-6);
925 wmove(gmsg_inputwin, 1, 6 + (pos-start));
926 move(gmsg_inputpos+1, 6 + (pos-start));
928 screen_refresh();
931 /*************************************************************************/
933 /* Clear the in-game text window. */
935 static void clear_gmsg_input(void)
937 if (gmsg_inputwin) {
938 delwin(gmsg_inputwin);
939 gmsg_inputwin = NULL;
940 leaveok(stdscr, TRUE);
941 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
942 setup_fields();
943 screen_refresh();
947 /*************************************************************************/
948 /*************************** Partyline display ***************************/
949 /*************************************************************************/
951 static void setup_partyline(void)
953 close_textwin(&gmsgbuf);
954 close_textwin(&attdefbuf);
955 clear();
957 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
959 plinebuf.x = plinebuf.y = 0;
960 plinebuf.width = scrwidth;
961 plinebuf.height = scrheight-4;
962 open_textwin(&plinebuf);
964 move(scrheight-4, 0);
965 hline(MY_HLINE, scrwidth);
966 move(scrheight-3, 0);
967 addstr("> ");
969 move(scrheight-2, 0);
970 hline(MY_HLINE2, scrwidth);
971 attrset(MY_BOLD);
972 move(scrheight-1, 0);
973 addstr("F1=Show Fields F2=Partyline F3=Winlist");
974 move(scrheight-1, scrwidth-8);
975 addstr("F10=Quit");
976 attrset(A_NORMAL);
978 move(scrheight-3, 2);
979 leaveok(stdscr, FALSE);
980 screen_refresh();
983 /*************************************************************************/
985 static void draw_partyline_input(const char *s, int pos)
987 static int start = 0; /* Start of displayed part of input line */
989 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
990 if (strlen(s) < scrwidth-3) {
991 start = 0;
992 mvaddstr(scrheight-3, 2, s);
993 move(scrheight-3, 2+strlen(s));
994 clrtoeol();
995 move(scrheight-3, 2+pos);
996 } else {
997 if (pos < start+8) {
998 start = pos-8;
999 if (start < 0)
1000 start = 0;
1001 } else if (pos > start + scrwidth-11) {
1002 start = pos - (scrwidth-11);
1003 if (start > strlen(s) - (scrwidth-3))
1004 start = strlen(s) - (scrwidth-3);
1006 mvaddnstr(scrheight-3, 2, s+start, scrwidth-2);
1007 move(scrheight-3, 2 + (pos-start));
1009 screen_refresh();
1012 /*************************************************************************/
1013 /**************************** Winlist display ****************************/
1014 /*************************************************************************/
1016 static void setup_winlist(void)
1018 int i, x;
1019 char buf[32];
1021 leaveok(stdscr, TRUE);
1022 close_textwin(&plinebuf);
1023 clear();
1024 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
1026 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
1027 x = scrwidth/2 - strlen(winlist[i].name);
1028 if (x < 0)
1029 x = 0;
1030 if (winlist[i].team) {
1031 if (x < 4)
1032 x = 4;
1033 mvaddstr(i*2, x-4, "<T>");
1035 mvaddstr(i*2, x, winlist[i].name);
1036 snprintf(buf, sizeof(buf), "%4d", winlist[i].points);
1037 if (winlist[i].games) {
1038 int avg100 = winlist[i].points*100 / winlist[i].games;
1039 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
1040 " %d.%02d",avg100/100, avg100%100);
1042 x += strlen(winlist[i].name) + 2;
1043 if (x > scrwidth - strlen(buf))
1044 x = scrwidth - strlen(buf);
1045 mvaddstr(i*2, x, buf);
1048 move(scrheight-2, 0);
1049 hline(MY_HLINE2, scrwidth);
1050 attrset(MY_BOLD);
1051 move(scrheight-1, 0);
1052 addstr("F1=Show Fields F2=Partyline F3=Winlist");
1053 move(scrheight-1, scrwidth-8);
1054 addstr("F10=Quit");
1055 attrset(A_NORMAL);
1057 screen_refresh();
1060 /*************************************************************************/
1061 /************************** Interface declaration ************************/
1062 /*************************************************************************/
1064 Interface tty_interface = {
1066 wait_for_input,
1068 screen_setup,
1069 screen_refresh,
1070 screen_redraw,
1072 draw_text,
1073 clear_text,
1075 setup_fields,
1076 draw_own_field,
1077 draw_other_field,
1078 draw_status,
1079 draw_specials,
1080 draw_attdef,
1081 draw_gmsg_input,
1082 clear_gmsg_input,
1084 setup_partyline,
1085 draw_partyline_input,
1087 setup_winlist
1090 /*************************************************************************/