ChangeLog update
[tetrinet.git] / tty.c
blobba8001e2283dde96d2db987685aa0847440efaec
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 (void)curs_set(0);
655 screen_refresh();
656 field_redraw = 0;
659 /*************************************************************************/
661 /* Display the player's own field. */
663 static void draw_own_field(void)
665 int x, y, x0, y0;
666 Field *f = &fields[my_playernum-1];
667 int shadow[4] = { -1, -1, -1, -1 };
669 if (dispmode != MODE_FIELDS)
670 return;
672 /* XXX: Code duplication with tetris.c:draw_piece(). --pasky */
673 if (playing_game && cast_shadow) {
674 int y = current_y - piecedata[current_piece][current_rotation].hot_y;
675 char *shape = (char *) piecedata[current_piece][current_rotation].shape;
676 int i, j;
678 for (j = 0; j < 4; j++) {
679 if (y+j < 0) {
680 shape += 4;
681 continue;
683 for (i = 0; i < 4; i++) {
684 if (*shape++)
685 shadow[i] = y + j;
690 x0 = own_coord[0]+1;
691 y0 = own_coord[1];
692 for (y = 0; y < 22; y++) {
693 for (x = 0; x < 12; x++) {
694 int c = tile_chars[(int) (*f)[y][x]];
696 if (playing_game && cast_shadow) {
697 PieceData *piece = &piecedata[current_piece][current_rotation];
698 int piece_x = current_x - piece->hot_x;
700 if (x >= piece_x && x <= piece_x + 3
701 && shadow[(x - piece_x)] >= 0
702 && shadow[(x - piece_x)] < y
703 && ((c & 0x7f) == ' ')) {
704 c = (c & (~0x7f)) | '.'
705 | getcolor(COLOR_BLACK, COLOR_BLACK) | A_BOLD;
709 mvaddch(y0+y*2, x0+x*2, c);
710 addch(c);
711 mvaddch(y0+y*2+1, x0+x*2, c);
712 addch(c);
715 if (gmsg_inputwin) {
716 delwin(gmsg_inputwin);
717 gmsg_inputwin = NULL;
718 draw_gmsg_input(NULL, -1);
720 if (!field_redraw)
721 screen_refresh();
724 /*************************************************************************/
726 /* Display another player's field. */
728 static void draw_other_field(int player)
730 int x, y, x0, y0;
731 Field *f;
733 if (dispmode != MODE_FIELDS)
734 return;
735 f = &fields[player-1];
736 if (player > my_playernum)
737 player--;
738 player--;
739 x0 = other_coord[player][0]+1;
740 y0 = other_coord[player][1];
741 for (y = 0; y < 22; y++) {
742 move(y0+y, x0);
743 for (x = 0; x < 12; x++) {
744 addch(tile_chars[(int) (*f)[y][x]]);
747 if (gmsg_inputwin) {
748 delwin(gmsg_inputwin);
749 gmsg_inputwin = NULL;
750 draw_gmsg_input(NULL, -1);
752 if (!field_redraw)
753 screen_refresh();
756 /*************************************************************************/
758 /* Display the current game status (level, lines, next piece). */
760 static void draw_status(void)
762 int x, y, i, j;
763 char buf[32], shape[4][4];
765 x = wide_screen ? alt_status_coord[0] : status_coord[0];
766 y = wide_screen ? alt_status_coord[1] : status_coord[1];
767 sprintf(buf, "%d", lines>99999 ? 99999 : lines);
768 mvaddstr(y, x+7, buf);
769 sprintf(buf, "%d", levels[my_playernum]);
770 mvaddstr(y+1, x+7, buf);
771 x = wide_screen ? alt_next_coord[0] : next_coord[0];
772 y = wide_screen ? alt_next_coord[1] : next_coord[1];
773 if (get_shape(next_piece, 0, shape) == 0) {
774 for (j = 0; j < 4; j++) {
775 if (!wide_screen)
776 move(y+j, x);
777 for (i = 0; i < 4; i++) {
778 if (wide_screen) {
779 move(y+j*2, x+i*2);
780 addch(tile_chars[(int) shape[j][i]]);
781 addch(tile_chars[(int) shape[j][i]]);
782 move(y+j*2+1, x+i*2);
783 addch(tile_chars[(int) shape[j][i]]);
784 addch(tile_chars[(int) shape[j][i]]);
785 } else
786 addch(tile_chars[(int) shape[j][i]]);
792 /*************************************************************************/
794 /* Display the special inventory and description of the current special. */
796 static const char *descs[] = {
797 " ",
798 "Add Line ",
799 "Clear Line ",
800 "Nuke Field ",
801 "Clear Random Blocks ",
802 "Switch Fields ",
803 "Clear Special Blocks",
804 "Block Gravity ",
805 "Blockquake ",
806 "Block Bomb "
809 static void draw_specials(void)
811 int x, y, i;
813 if (dispmode != MODE_FIELDS)
814 return;
815 x = own_coord[0];
816 y = own_coord[1]+45;
817 mvaddstr(y, x, descs[specials[0]+1]);
818 move(y+1, x+10);
819 i = 0;
820 while (i < special_capacity && specials[i] >= 0 && x < attdef_coord[0]-1) {
821 addch(tile_chars[specials[i]+6]);
822 i++;
823 x++;
825 while (x < attdef_coord[0]-1) {
826 addch(tile_chars[0]);
827 x++;
829 if (!field_redraw)
830 screen_refresh();
833 /*************************************************************************/
835 /* Display an attack/defense message. */
837 static const char *msgs[][2] = {
838 { "cs1", "1 Line Added to All" },
839 { "cs2", "2 Lines Added to All" },
840 { "cs4", "4 Lines Added to All" },
841 { "a", "Add Line" },
842 { "c", "Clear Line" },
843 { "n", "Nuke Field" },
844 { "r", "Clear Random Blocks" },
845 { "s", "Switch Fields" },
846 { "b", "Clear Special Blocks" },
847 { "g", "Block Gravity" },
848 { "q", "Blockquake" },
849 { "o", "Block Bomb" },
850 { NULL }
853 static void draw_attdef(const char *type, int from, int to)
855 int i, width;
856 char buf[512];
858 width = other_coord[4][0] - attdef_coord[0] - 1;
859 for (i = 0; msgs[i][0]; i++) {
860 if (strcmp(type, msgs[i][0]) == 0)
861 break;
863 if (!msgs[i][0])
864 return;
865 strcpy(buf, msgs[i][1]);
866 if (to != 0)
867 sprintf(buf+strlen(buf), " on %s", players[to-1]);
868 if (from == 0)
869 sprintf(buf+strlen(buf), " by Server");
870 else
871 sprintf(buf+strlen(buf), " by %s", players[from-1]);
872 draw_text(BUFFER_ATTDEF, buf);
875 /*************************************************************************/
877 /* Display the in-game text window. */
879 static void draw_gmsg_input(const char *s, int pos)
881 static int start = 0; /* Start of displayed part of input line */
882 static const char *last_s;
883 static int last_pos;
885 if (s)
886 last_s = s;
887 else
888 s = last_s;
889 if (pos >= 0)
890 last_pos = pos;
891 else
892 pos = last_pos;
894 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
896 if (!gmsg_inputwin) {
897 gmsg_inputpos = scrheight/2 - 1;
898 gmsg_inputheight = 3;
899 gmsg_inputwin =
900 subwin(stdscr, gmsg_inputheight, scrwidth, gmsg_inputpos, 0);
901 werase(gmsg_inputwin);
902 leaveok(gmsg_inputwin, FALSE);
903 leaveok(stdscr, FALSE);
904 mvwaddstr(gmsg_inputwin, 1, 0, "Text>");
907 if (strlen(s) < scrwidth-7) {
908 start = 0;
909 mvwaddstr(gmsg_inputwin, 1, 6, s);
910 wmove(gmsg_inputwin, 1, 6+strlen(s));
911 move(gmsg_inputpos+1, 6+strlen(s));
912 wclrtoeol(gmsg_inputwin);
913 wmove(gmsg_inputwin, 1, 6+pos);
914 move(gmsg_inputpos+1, 6+pos);
915 } else {
916 if (pos < start+8) {
917 start = pos-8;
918 if (start < 0)
919 start = 0;
920 } else if (pos > start + scrwidth-15) {
921 start = pos - (scrwidth-15);
922 if (start > strlen(s) - (scrwidth-7))
923 start = strlen(s) - (scrwidth-7);
925 mvwaddnstr(gmsg_inputwin, 1, 6, s+start, scrwidth-6);
926 wmove(gmsg_inputwin, 1, 6 + (pos-start));
927 move(gmsg_inputpos+1, 6 + (pos-start));
929 (void)curs_set(1);
930 screen_refresh();
933 /*************************************************************************/
935 /* Clear the in-game text window. */
937 static void clear_gmsg_input(void)
939 if (gmsg_inputwin) {
940 delwin(gmsg_inputwin);
941 gmsg_inputwin = NULL;
942 leaveok(stdscr, TRUE);
943 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
944 setup_fields();
945 (void)curs_set(0);
946 screen_refresh();
950 /*************************************************************************/
951 /*************************** Partyline display ***************************/
952 /*************************************************************************/
954 static void setup_partyline(void)
956 close_textwin(&gmsgbuf);
957 close_textwin(&attdefbuf);
958 clear();
960 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
962 plinebuf.x = plinebuf.y = 0;
963 plinebuf.width = scrwidth;
964 plinebuf.height = scrheight-4;
965 open_textwin(&plinebuf);
967 move(scrheight-4, 0);
968 hline(MY_HLINE, scrwidth);
969 move(scrheight-3, 0);
970 addstr("> ");
972 move(scrheight-2, 0);
973 hline(MY_HLINE2, scrwidth);
974 attrset(MY_BOLD);
975 move(scrheight-1, 0);
976 addstr("F1=Show Fields F2=Partyline F3=Winlist");
977 move(scrheight-1, scrwidth-8);
978 addstr("F10=Quit");
979 attrset(A_NORMAL);
981 move(scrheight-3, 2);
982 leaveok(stdscr, FALSE);
983 (void)curs_set(1);
984 screen_refresh();
987 /*************************************************************************/
989 static void draw_partyline_input(const char *s, int pos)
991 static int start = 0; /* Start of displayed part of input line */
993 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
994 if (strlen(s) < scrwidth-3) {
995 start = 0;
996 mvaddstr(scrheight-3, 2, s);
997 move(scrheight-3, 2+strlen(s));
998 clrtoeol();
999 move(scrheight-3, 2+pos);
1000 } else {
1001 if (pos < start+8) {
1002 start = pos-8;
1003 if (start < 0)
1004 start = 0;
1005 } else if (pos > start + scrwidth-11) {
1006 start = pos - (scrwidth-11);
1007 if (start > strlen(s) - (scrwidth-3))
1008 start = strlen(s) - (scrwidth-3);
1010 mvaddnstr(scrheight-3, 2, s+start, scrwidth-2);
1011 move(scrheight-3, 2 + (pos-start));
1013 screen_refresh();
1016 /*************************************************************************/
1017 /**************************** Winlist display ****************************/
1018 /*************************************************************************/
1020 static void setup_winlist(void)
1022 int i, x;
1023 char buf[32];
1025 leaveok(stdscr, TRUE);
1026 close_textwin(&plinebuf);
1027 clear();
1028 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
1030 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
1031 x = scrwidth/2 - strlen(winlist[i].name);
1032 if (x < 0)
1033 x = 0;
1034 if (winlist[i].team) {
1035 if (x < 4)
1036 x = 4;
1037 mvaddstr(i*2, x-4, "<T>");
1039 mvaddstr(i*2, x, winlist[i].name);
1040 snprintf(buf, sizeof(buf), "%4d", winlist[i].points);
1041 if (winlist[i].games) {
1042 int avg100 = winlist[i].points*100 / winlist[i].games;
1043 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
1044 " %d.%02d",avg100/100, avg100%100);
1046 x += strlen(winlist[i].name) + 2;
1047 if (x > scrwidth - strlen(buf))
1048 x = scrwidth - strlen(buf);
1049 mvaddstr(i*2, x, buf);
1052 move(scrheight-2, 0);
1053 hline(MY_HLINE2, scrwidth);
1054 attrset(MY_BOLD);
1055 move(scrheight-1, 0);
1056 addstr("F1=Show Fields F2=Partyline F3=Winlist");
1057 move(scrheight-1, scrwidth-8);
1058 addstr("F10=Quit");
1059 attrset(A_NORMAL);
1061 (void)curs_set(0);
1062 screen_refresh();
1065 /*************************************************************************/
1066 /************************** Interface declaration ************************/
1067 /*************************************************************************/
1069 Interface tty_interface = {
1071 wait_for_input,
1073 screen_setup,
1074 screen_refresh,
1075 screen_redraw,
1077 draw_text,
1078 clear_text,
1080 setup_fields,
1081 draw_own_field,
1082 draw_other_field,
1083 draw_status,
1084 draw_specials,
1085 draw_attdef,
1086 draw_gmsg_input,
1087 clear_gmsg_input,
1089 setup_partyline,
1090 draw_partyline_input,
1092 setup_winlist
1095 /*************************************************************************/