ChangeLog update
[tetrinet.git] / xwin.c
blob1f1c1199b39aca298e64d32fe1f9f7185007b2e1
1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2 * This program is public domain.
4 * Text terminal I/O routines.
5 */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <ctype.h>
12 #include <curses.h>
13 #include <errno.h>
14 #include <signal.h>
15 #include <sys/time.h>
16 #include "tetrinet.h"
17 #include "tetris.h"
18 #include "io.h"
20 /*************************************************************************/
22 #define MY_HLINE (fancy ? ACS_HLINE : '-')
23 #define MY_VLINE (fancy ? ACS_VLINE : '|')
24 #define MY_ULCORNER (fancy ? ACS_ULCORNER : '+')
25 #define MY_URCORNER (fancy ? ACS_URCORNER : '+')
26 #define MY_LLCORNER (fancy ? ACS_LLCORNER : '+')
27 #define MY_LRCORNER (fancy ? ACS_LRCORNER : '+')
29 #define MY_HLINE2 (fancy ? (ACS_HLINE | A_BOLD) : '=')
30 #define MY_BOLD (fancy ? A_BOLD : 0)
32 /*************************************************************************/
33 /******************************* Input stuff *****************************/
34 /*************************************************************************/
36 /* Return either an ASCII code 0-255, a K_* value, or -1 if server input is
37 * waiting. Return -2 if we run out of time with no input.
40 static int wait_for_input(int msec)
42 fd_set fds;
43 struct timeval tv;
44 int c;
46 FD_ZERO(&fds);
47 FD_SET(0, &fds);
48 FD_SET(server_sock, &fds);
49 tv.tv_sec = msec/1000;
50 tv.tv_usec = (msec*1000) % 1000000;
51 while (select(server_sock+1, &fds, NULL, NULL, msec<0 ? NULL : &tv) < 0) {
52 if (errno != EINTR)
53 perror("Warning: select() failed");
55 if (FD_ISSET(0, &fds)) {
56 c = getch();
57 if (c == KEY_UP)
58 return K_UP;
59 else if (c == KEY_DOWN)
60 return K_DOWN;
61 else if (c == KEY_LEFT)
62 return K_LEFT;
63 else if (c == KEY_RIGHT)
64 return K_RIGHT;
65 else if (c == KEY_F(1))
66 return K_F1;
67 else if (c == KEY_F(2))
68 return K_F2;
69 else if (c == KEY_F(3))
70 return K_F3;
71 else if (c == KEY_F(4))
72 return K_F4;
73 else if (c == KEY_F(5))
74 return K_F5;
75 else if (c == KEY_F(6))
76 return K_F6;
77 else if (c == KEY_F(7))
78 return K_F7;
79 else if (c == KEY_F(8))
80 return K_F8;
81 else if (c == KEY_F(9))
82 return K_F9;
83 else if (c == KEY_F(10))
84 return K_F10;
85 else if (c == KEY_F(11))
86 return K_F11;
87 else if (c == KEY_F(12))
88 return K_F12;
89 else if (c == KEY_BACKSPACE)
90 return 8;
91 else if (c >= 0x0100)
92 return K_INVALID;
93 else if (c == 7) /* ^G */
94 return 27; /* Escape */
95 else
96 return c;
97 } /* if (FD_ISSET(0, &fds)) */
98 else if (FD_ISSET(server_sock, &fds))
99 return -1;
100 else
101 return -2; /* out of time */
104 /*************************************************************************/
105 /****************************** Output stuff *****************************/
106 /*************************************************************************/
108 /* Size of the screen */
109 static int scrwidth, scrheight;
111 /* Is color available? */
112 static int has_color;
114 /*************************************************************************/
116 /* Text buffers: */
118 typedef struct {
119 int x, y, width, height;
120 int line;
121 WINDOW *win; /* NULL if not currently displayed */
122 char **text;
123 } TextBuffer;
125 static TextBuffer plinebuf, gmsgbuf, attdefbuf;
127 /*************************************************************************/
129 /* Window for typing in-game text, and its coordinates: */
131 static WINDOW *gmsg_inputwin;
132 static int gmsg_inputpos, gmsg_inputheight;
134 /*************************************************************************/
135 /*************************************************************************/
137 /* Clean up the screen on exit. */
139 static void screen_cleanup()
141 wmove(stdscr, scrheight-1, 0);
142 wrefresh(stdscr);
143 endwin();
144 printf("\n");
147 /*************************************************************************/
149 /* Little signal handler that just does an exit(1) (thereby getting our
150 * cleanup routine called), except for TSTP, which does a clean suspend.
153 static void (*old_tstp)(int sig);
155 static void sighandler(int sig)
157 if (sig != SIGTSTP) {
158 endwin();
159 if (sig != SIGINT)
160 fprintf(stderr, "%s\n", strsignal(sig));
161 exit(1);
163 endwin();
164 signal(SIGTSTP, old_tstp);
165 raise(SIGTSTP);
166 doupdate();
167 signal(SIGTSTP, sighandler);
170 /*************************************************************************/
171 /*************************************************************************/
173 #define MAXCOLORS 256
175 static int colors[MAXCOLORS][2] = { {-1,-1} };
177 /* Return a color attribute value. */
179 static long getcolor(int fg, int bg)
181 int i;
183 if (colors[0][0] < 0) {
184 start_color();
185 memset(colors, -1, sizeof(colors));
186 colors[0][0] = COLOR_WHITE;
187 colors[0][1] = COLOR_BLACK;
189 if (fg == COLOR_WHITE && bg == COLOR_BLACK)
190 return COLOR_PAIR(0);
191 for (i = 1; i < MAXCOLORS; i++) {
192 if (colors[i][0] == fg && colors[i][1] == bg)
193 return COLOR_PAIR(i);
195 for (i = 1; i < MAXCOLORS; i++) {
196 if (colors[i][0] < 0) {
197 if (init_pair(i, fg, bg) == ERR)
198 continue;
199 colors[i][0] = fg;
200 colors[i][1] = bg;
201 return COLOR_PAIR(i);
204 return -1;
207 /*************************************************************************/
208 /*************************************************************************/
210 /* Set up the screen stuff. */
212 static void screen_setup(void)
214 /* Avoid messy keyfield signals while we're setting up */
215 signal(SIGINT, SIG_IGN);
216 signal(SIGQUIT, SIG_IGN);
217 signal(SIGTSTP, SIG_IGN);
219 initscr();
220 cbreak();
221 noecho();
222 nodelay(stdscr, TRUE);
223 keypad(stdscr, TRUE);
224 leaveok(stdscr, TRUE);
225 if ((has_color = has_colors()))
226 start_color();
227 getmaxyx(stdscr, scrheight, scrwidth);
228 scrwidth--; /* Don't draw in last column--this can cause scroll */
230 /* Cancel all this when we exit. */
231 atexit(screen_cleanup);
233 /* Catch signals so we can exit cleanly. */
234 signal(SIGINT, sighandler);
235 signal(SIGQUIT, sighandler);
236 signal(SIGTERM, sighandler);
237 signal(SIGHUP, sighandler);
238 /* signal(SIGSEGV, sighandler); */
239 signal(SIGABRT, sighandler);
240 signal(SIGIOT, sighandler);
241 signal(SIGTRAP, sighandler);
242 signal(SIGBUS, sighandler);
243 signal(SIGFPE, sighandler);
244 signal(SIGUSR1, sighandler);
245 signal(SIGUSR2, sighandler);
246 signal(SIGALRM, sighandler);
247 #ifdef linux
248 signal(SIGSTKFLT, sighandler);
249 #endif
250 signal(SIGTSTP, sighandler);
251 signal(SIGXCPU, sighandler);
252 signal(SIGXFSZ, sighandler);
253 signal(SIGVTALRM, sighandler);
255 /* Broken pipes don't want to bother us at all. */
256 signal(SIGPIPE, SIG_IGN);
259 /*************************************************************************/
261 /* Redraw everything on the screen. */
263 static void screen_refresh(void)
265 if (gmsg_inputwin)
266 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
267 if (plinebuf.win)
268 touchline(stdscr, plinebuf.y, plinebuf.height);
269 if (gmsgbuf.win)
270 touchline(stdscr, gmsgbuf.y, gmsgbuf.height);
271 if (attdefbuf.win)
272 touchline(stdscr, attdefbuf.y, attdefbuf.height);
273 wnoutrefresh(stdscr);
274 doupdate();
277 /*************************************************************************/
279 /* Like screen_refresh(), but clear the screen first. */
281 static void screen_redraw(void)
283 clearok(stdscr, TRUE);
284 screen_refresh();
287 /*************************************************************************/
288 /************************* Text buffer routines **************************/
289 /*************************************************************************/
291 /* Put a line of text in a text buffer. */
293 static void outline(TextBuffer *buf, const char *s)
295 if (buf->line == buf->height) {
296 if (buf->win)
297 scroll(buf->win);
298 memmove(buf->text, buf->text+1, (buf->height-1) * sizeof(char *));
299 buf->line--;
301 if (buf->win)
302 mvwaddstr(buf->win, buf->line, 0, s);
303 if (s != buf->text[buf->line]) /* check for restoring display */
304 buf->text[buf->line] = strdup(s);
305 buf->line++;
308 static void draw_text(int bufnum, const char *s)
310 char str[1024]; /* hopefully scrwidth < 1024 */
311 const char *t;
312 int indent = 0;
313 int x, y;
314 TextBuffer *buf;
316 switch (bufnum) {
317 case BUFFER_PLINE: buf = &plinebuf; break;
318 case BUFFER_GMSG: buf = &gmsgbuf; break;
319 case BUFFER_ATTDEF: buf = &attdefbuf; break;
320 default: return;
322 if (!buf->text)
323 return;
324 if (buf->win) {
325 getyx(stdscr, y, x);
326 attrset(getcolor(COLOR_WHITE, COLOR_BLACK));
328 while (*s && isspace(*s))
329 s++;
330 while (strlen(s) > buf->width - indent) {
331 t = s + buf->width - indent;
332 while (t >= s && !isspace(*t))
333 t--;
334 while (t >= s && isspace(*t))
335 t--;
336 t++;
337 if (t < s)
338 t = s + buf->width - indent;
339 if (indent > 0)
340 sprintf(str, "%*s", indent, "");
341 strncpy(str+indent, s, t-s);
342 str[t-s+indent] = 0;
343 outline(buf, str);
344 indent = 2;
345 while (isspace(*t))
346 t++;
347 s = t;
349 if (indent > 0)
350 sprintf(str, "%*s", indent, "");
351 strcpy(str+indent, s);
352 outline(buf, str);
353 if (buf->win) {
354 move(y, x);
355 screen_refresh();
359 /*************************************************************************/
361 /* Clear the contents of a text buffer. */
363 static void clear_text(int bufnum)
365 TextBuffer *buf;
366 int i;
368 switch (bufnum) {
369 case BUFFER_PLINE: buf = &plinebuf; break;
370 case BUFFER_GMSG: buf = &gmsgbuf; break;
371 case BUFFER_ATTDEF: buf = &attdefbuf; break;
372 default: return;
374 if (buf->text) {
375 for (i = 0; i < buf->height; i++) {
376 if (buf->text[i]) {
377 free(buf->text[i]);
378 buf->text[i] = NULL;
381 buf->line = 0;
383 if (buf->win) {
384 werase(buf->win);
385 screen_refresh();
389 /*************************************************************************/
391 /* Restore the contents of the given text buffer. */
393 static void restore_text(TextBuffer *buf)
395 buf->line = 0;
396 while (buf->line < buf->height && buf->text[buf->line])
397 outline(buf, buf->text[buf->line]);
400 /*************************************************************************/
402 /* Open a window for the given text buffer. */
404 static void open_textwin(TextBuffer *buf)
406 if (buf->height <= 0 || buf->width <= 0) {
407 char str[256];
408 move(scrheight-1, 0);
409 snprintf(str, sizeof(str), "ERROR: bad textwin size (%d,%d)",
410 buf->width, buf->height);
411 addstr(str);
412 exit(1);
414 if (!buf->win) {
415 buf->win = subwin(stdscr, buf->height, buf->width, buf->y, buf->x);
416 scrollok(buf->win, TRUE);
418 if (!buf->text)
419 buf->text = calloc(buf->height, sizeof(char *));
420 else
421 restore_text(buf);
424 /*************************************************************************/
426 /* Close the window for the given text buffer, if it's open. */
428 static void close_textwin(TextBuffer *buf)
430 if (buf->win) {
431 delwin(buf->win);
432 buf->win = NULL;
436 /*************************************************************************/
437 /************************ Field drawing routines *************************/
438 /*************************************************************************/
440 /* Are we on a wide screen (>=92 columns)? */
441 static int wide_screen = 0;
443 /* Field display X/Y coordinates. */
444 static const int own_coord[2] = {0,0};
445 static int other_coord[5][2] = /* Recomputed based on screen width */
446 { {30,0}, {47,0}, {64,0}, {47,24}, {64,24} };
448 /* Position of the status window. */
449 static const int status_coord[2] = {28,25};
450 static const int next_coord[2] = {40,24};
451 static const int alt_status_coord[2] = {28,2};
452 static const int alt_next_coord[2] = {29,8};
454 /* Position of the attacks/defenses window. */
455 static const int attdef_coord[2] = {28,28};
456 static const int alt_attdef_coord[2] = {28,24};
458 /* Position of the text window. X coordinate is ignored. */
459 static const int field_text_coord[2] = {0,47};
461 /* Information for drawing blocks. Color attributes are added to blocks in
462 * the setup_fields() routine. */
463 static int tile_chars[15] =
464 { ' ','#','#','#','#','#','a','c','n','r','s','b','g','q','o' };
466 static int attdef_size;
467 static int attdef_line;
468 static char **attdef_text;
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+FIELD_WIDTH*2+2, buf);
551 for (i = 2; i < FIELD_HEIGHT*2 && players[my_playernum-1][i-2]; i++)
552 mvaddch(y+i, x+FIELD_WIDTH*2+2, players[my_playernum-1][i-2]);
553 move(y, x);
554 vline(MY_VLINE, FIELD_HEIGHT*2);
555 move(y, x+FIELD_WIDTH*2+1);
556 vline(MY_VLINE, FIELD_HEIGHT*2);
557 move(y+FIELD_HEIGHT*2, x);
558 addch(MY_LLCORNER);
559 hline(MY_HLINE, FIELD_WIDTH*2);
560 move(y+FIELD_HEIGHT*2, x+FIELD_WIDTH*2+1);
561 addch(MY_LRCORNER);
562 mvaddstr(y+FIELD_HEIGHT*2+2, x, "Specials:");
563 draw_own_field();
564 draw_specials();
566 for (j = 0; j < 5; j++) {
567 x = other_coord[j][0];
568 y = other_coord[j][1];
569 move(y, x);
570 vline(MY_VLINE, FIELD_HEIGHT);
571 move(y, x+FIELD_WIDTH+1);
572 vline(MY_VLINE, FIELD_HEIGHT);
573 move(y+FIELD_HEIGHT, x);
574 addch(MY_LLCORNER);
575 hline(MY_HLINE, FIELD_WIDTH);
576 move(y+FIELD_HEIGHT, x+FIELD_WIDTH+1);
577 addch(MY_LRCORNER);
578 if (j+1 >= my_playernum) {
579 sprintf(buf, "%d", j+2);
580 mvaddstr(y, x+FIELD_WIDTH+2, buf);
581 if (players[j+1]) {
582 for (i = 0; i < FIELD_HEIGHT-2 && players[j+1][i]; i++)
583 mvaddch(y+i+2, x+FIELD_WIDTH+2, players[j+1][i]);
585 draw_other_field(j+2);
586 } else {
587 sprintf(buf, "%d", j+1);
588 mvaddstr(y, x+FIELD_WIDTH+2, buf);
589 if (players[j]) {
590 for (i = 0; i < FIELD_HEIGHT-2 && players[j][i]; i++)
591 mvaddch(y+i+2, x+FIELD_WIDTH+2, players[j][i]);
593 draw_other_field(j+1);
597 if (wide_screen) {
598 x = alt_status_coord[0];
599 y = alt_status_coord[1];
600 mvaddstr(y, x, "Lines:");
601 mvaddstr(y+1, x, "Level:");
602 x = alt_next_coord[0];
603 y = alt_next_coord[1];
604 mvaddstr(y-2, x-1, "Next piece:");
605 move(y-1, x-1);
606 addch(MY_ULCORNER);
607 hline(MY_HLINE, 8);
608 mvaddch(y-1, x+8, MY_URCORNER);
609 move(y, x-1);
610 vline(MY_VLINE, 8);
611 move(y, x+8);
612 vline(MY_VLINE, 8);
613 move(y+8, x-1);
614 addch(MY_LLCORNER);
615 hline(MY_HLINE, 8);
616 mvaddch(y+8, x+8, MY_LRCORNER);
617 } else {
618 x = status_coord[0];
619 y = status_coord[1];
620 mvaddstr(y-1, x, "Next piece:");
621 mvaddstr(y, x, "Lines:");
622 mvaddstr(y+1, x, "Level:");
624 if (playing_game)
625 draw_status();
627 attdefbuf.x = wide_screen ? alt_attdef_coord[0] : attdef_coord[0];
628 attdefbuf.y = wide_screen ? alt_attdef_coord[1] : attdef_coord[1];
629 attdefbuf.width = (other_coord[3][0]-1) - attdefbuf.x;
630 attdefbuf.height = (attdefbot+1) - attdefbuf.y;
631 open_textwin(&attdefbuf);
633 if (gmsg_inputwin) {
634 delwin(gmsg_inputwin);
635 gmsg_inputwin = NULL;
636 draw_gmsg_input(NULL, -1);
639 screen_refresh();
640 field_redraw = 0;
643 /*************************************************************************/
645 /* Display the player's own field. */
647 static void draw_own_field(void)
649 int x, y, x0, y0;
650 Field *f = &fields[my_playernum-1];
652 if (dispmode != MODE_FIELDS)
653 return;
654 x0 = own_coord[0]+1;
655 y0 = own_coord[1];
656 for (y = 0; y < 22; y++) {
657 for (x = 0; x < 12; x++) {
658 int c = tile_chars[(*f)[y][x]];
659 mvaddch(y0+y*2, x0+x*2, c);
660 addch(c);
661 mvaddch(y0+y*2+1, x0+x*2, c);
662 addch(c);
665 if (gmsg_inputwin) {
666 delwin(gmsg_inputwin);
667 gmsg_inputwin = NULL;
668 draw_gmsg_input(NULL, -1);
670 if (!field_redraw)
671 screen_refresh();
674 /*************************************************************************/
676 /* Display another player's field. */
678 static void draw_other_field(int player)
680 int x, y, x0, y0;
681 Field *f;
683 if (dispmode != MODE_FIELDS)
684 return;
685 f = &fields[player-1];
686 if (player > my_playernum)
687 player--;
688 player--;
689 x0 = other_coord[player][0]+1;
690 y0 = other_coord[player][1];
691 for (y = 0; y < 22; y++) {
692 move(y0+y, x0);
693 for (x = 0; x < 12; x++)
694 addch(tile_chars[(*f)[y][x]]);
696 if (gmsg_inputwin) {
697 delwin(gmsg_inputwin);
698 gmsg_inputwin = NULL;
699 draw_gmsg_input(NULL, -1);
701 if (!field_redraw)
702 screen_refresh();
705 /*************************************************************************/
707 /* Display the current game status (level, lines, next piece). */
709 static void draw_status(void)
711 int x, y, i, j;
712 char buf[32], shape[4][4];
714 x = wide_screen ? alt_status_coord[0] : status_coord[0];
715 y = wide_screen ? alt_status_coord[1] : status_coord[1];
716 sprintf(buf, "%d", lines>99999 ? 99999 : lines);
717 mvaddstr(y+1, x+7, buf);
718 sprintf(buf, "%d", levels[my_playernum]);
719 mvaddstr(y+2, x+7, buf);
720 x = wide_screen ? alt_next_coord[0] : next_coord[0];
721 y = wide_screen ? alt_next_coord[1] : next_coord[1];
722 if (get_shape(next_piece, 0, shape) == 0) {
723 for (j = 0; j < 4; j++) {
724 if (!wide_screen)
725 move(y+j, x);
726 for (i = 0; i < 4; i++) {
727 if (wide_screen) {
728 move(y+j*2, x+i*2);
729 addch(tile_chars[shape[j][i]]);
730 addch(tile_chars[shape[j][i]]);
731 move(y+j*2+1, x+i*2);
732 addch(tile_chars[shape[j][i]]);
733 addch(tile_chars[shape[j][i]]);
734 } else
735 addch(tile_chars[shape[j][i]]);
741 /*************************************************************************/
743 /* Display the special inventory and description of the current special. */
745 static const char *descs[] = {
746 " ",
747 "Add Line ",
748 "Clear Line ",
749 "Nuke Field ",
750 "Clear Random Blocks ",
751 "Switch Fields ",
752 "Clear Special Blocks",
753 "Block Gravity ",
754 "Blockquake ",
755 "Block Bomb "
758 static void draw_specials(void)
760 int x, y, i;
762 if (dispmode != MODE_FIELDS)
763 return;
764 x = own_coord[0];
765 y = own_coord[1]+45;
766 mvaddstr(y, x, descs[specials[0]+1]);
767 move(y+1, x+10);
768 i = 0;
769 while (i < special_capacity && specials[i] >= 0 && x < attdef_coord[0]-1) {
770 addch(tile_chars[specials[i]+6]);
771 i++;
772 x++;
774 while (x < attdef_coord[0]-1) {
775 addch(tile_chars[0]);
776 x++;
778 if (!field_redraw)
779 screen_refresh();
782 /*************************************************************************/
784 /* Display an attack/defense message. */
786 static const char *msgs[][2] = {
787 { "cs1", "1 Line Added to All" },
788 { "cs2", "2 Lines Added to All" },
789 { "cs4", "4 Lines Added to All" },
790 { "a", "Add Line" },
791 { "c", "Clear Line" },
792 { "n", "Nuke Field" },
793 { "r", "Clear Random Blocks" },
794 { "s", "Switch Fields" },
795 { "b", "Clear Special Blocks" },
796 { "g", "Block Gravity" },
797 { "q", "Blockquake" },
798 { "o", "Block Bomb" },
799 { NULL }
802 static void draw_attdef(const char *type, int from, int to)
804 int i, width;
805 char *s, buf[512];
807 width = other_coord[4][0] - attdef_coord[0] - 1;
808 for (i = 0; msgs[i][0]; i++) {
809 if (strcmp(type, msgs[i][0]) == 0)
810 break;
812 if (!msgs[i][0])
813 return;
814 strcpy(buf, msgs[i][1]);
815 if (to != 0)
816 sprintf(buf+strlen(buf), " on %s", players[to-1]);
817 if (from == 0)
818 sprintf(buf+strlen(buf), " by Server");
819 else
820 sprintf(buf+strlen(buf), " by %s", players[from-1]);
821 draw_text(BUFFER_ATTDEF, buf);
824 /*************************************************************************/
826 /* Display the in-game text window. */
828 static void draw_gmsg_input(const char *s, int pos)
830 static int start = 0; /* Start of displayed part of input line */
831 static const char *last_s;
832 static int last_pos;
834 if (s)
835 last_s = s;
836 else
837 s = last_s;
838 if (pos >= 0)
839 last_pos = pos;
840 else
841 pos = last_pos;
843 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
845 if (!gmsg_inputwin) {
846 gmsg_inputpos = scrheight/2 - 1;
847 gmsg_inputheight = 3;
848 gmsg_inputwin =
849 subwin(stdscr, gmsg_inputheight, scrwidth, gmsg_inputpos, 0);
850 werase(gmsg_inputwin);
851 leaveok(gmsg_inputwin, FALSE);
852 leaveok(stdscr, FALSE);
853 mvwaddstr(gmsg_inputwin, 1, 0, "Text>");
856 if (strlen(s) < scrwidth-7) {
857 start = 0;
858 mvwaddstr(gmsg_inputwin, 1, 6, s);
859 wmove(gmsg_inputwin, 1, 6+strlen(s));
860 move(gmsg_inputpos+1, 6+strlen(s));
861 wclrtoeol(gmsg_inputwin);
862 wmove(gmsg_inputwin, 1, 6+pos);
863 move(gmsg_inputpos+1, 6+pos);
864 } else {
865 if (pos < start+8) {
866 start = pos-8;
867 if (start < 0)
868 start = 0;
869 } else if (pos > start + scrwidth-15) {
870 start = pos - (scrwidth-15);
871 if (start > strlen(s) - (scrwidth-7))
872 start = strlen(s) - (scrwidth-7);
874 mvwaddnstr(gmsg_inputwin, 1, 6, s+start, scrwidth-6);
875 wmove(gmsg_inputwin, 1, 6 + (pos-start));
876 move(gmsg_inputpos+1, 6 + (pos-start));
878 screen_refresh();
881 /*************************************************************************/
883 /* Clear the in-game text window. */
885 static void clear_gmsg_input(void)
887 if (gmsg_inputwin) {
888 delwin(gmsg_inputwin);
889 gmsg_inputwin = NULL;
890 leaveok(stdscr, TRUE);
891 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
892 setup_fields();
893 screen_refresh();
897 /*************************************************************************/
898 /*************************** Partyline display ***************************/
899 /*************************************************************************/
901 static void setup_partyline(void)
903 int i;
905 close_textwin(&gmsgbuf);
906 close_textwin(&attdefbuf);
907 clear();
909 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
911 plinebuf.x = plinebuf.y = 0;
912 plinebuf.width = scrwidth;
913 plinebuf.height = scrheight-4;
914 open_textwin(&plinebuf);
916 move(scrheight-4, 0);
917 hline(MY_HLINE, scrwidth);
918 move(scrheight-3, 0);
919 addstr("> ");
921 move(scrheight-2, 0);
922 hline(MY_HLINE2, scrwidth);
923 attrset(MY_BOLD);
924 move(scrheight-1, 0);
925 addstr("F1=Show Fields F2=Partyline F3=Winlist");
926 move(scrheight-1, scrwidth-8);
927 addstr("F10=Quit");
928 attrset(A_NORMAL);
930 move(scrheight-3, 2);
931 leaveok(stdscr, FALSE);
932 screen_refresh();
935 /*************************************************************************/
937 static void draw_partyline_input(const char *s, int pos)
939 static int start = 0; /* Start of displayed part of input line */
941 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
942 if (strlen(s) < scrwidth-3) {
943 start = 0;
944 mvaddstr(scrheight-3, 2, s);
945 move(scrheight-3, 2+strlen(s));
946 clrtoeol();
947 move(scrheight-3, 2+pos);
948 } else {
949 if (pos < start+8) {
950 start = pos-8;
951 if (start < 0)
952 start = 0;
953 } else if (pos > start + scrwidth-11) {
954 start = pos - (scrwidth-11);
955 if (start > strlen(s) - (scrwidth-3))
956 start = strlen(s) - (scrwidth-3);
958 mvaddnstr(scrheight-3, 2, s+start, scrwidth-2);
959 move(scrheight-3, 2 + (pos-start));
961 screen_refresh();
964 /*************************************************************************/
965 /**************************** Winlist display ****************************/
966 /*************************************************************************/
968 static void setup_winlist(void)
970 int i, x;
971 char buf[32];
973 leaveok(stdscr, TRUE);
974 close_textwin(&plinebuf);
975 clear();
976 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
978 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
979 x = scrwidth/2 - strlen(winlist[i].name);
980 if (x < 0)
981 x = 0;
982 if (winlist[i].team) {
983 if (x < 4)
984 x = 4;
985 mvaddstr(i*2, x-4, "<T>");
987 mvaddstr(i*2, x, winlist[i].name);
988 snprintf(buf, sizeof(buf), "%4d", winlist[i].points);
989 if (winlist[i].games) {
990 int avg100 = winlist[i].points*100 / winlist[i].games;
991 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
992 " %d.%02d",avg100/100, avg100%100);
994 x += strlen(winlist[i].name) + 2;
995 if (x > scrwidth - strlen(buf))
996 x = scrwidth - strlen(buf);
997 mvaddstr(i*2, x, buf);
1000 move(scrheight-2, 0);
1001 hline(MY_HLINE2, scrwidth);
1002 attrset(MY_BOLD);
1003 move(scrheight-1, 0);
1004 addstr("F1=Show Fields F2=Partyline F3=Winlist");
1005 move(scrheight-1, scrwidth-8);
1006 addstr("F10=Quit");
1007 attrset(A_NORMAL);
1009 screen_refresh();
1012 /*************************************************************************/
1013 /************************** Interface declaration ************************/
1014 /*************************************************************************/
1016 Interface xwin_interface = {
1018 wait_for_input,
1020 screen_setup,
1021 screen_refresh,
1022 screen_redraw,
1024 draw_text,
1025 clear_text,
1027 setup_fields,
1028 draw_own_field,
1029 draw_other_field,
1030 draw_status,
1031 draw_specials,
1032 draw_attdef,
1033 draw_gmsg_input,
1034 clear_gmsg_input,
1036 setup_partyline,
1037 draw_partyline_input,
1039 setup_winlist
1042 /*************************************************************************/