ChangeLog update
[tetrinet/clach04.git] / xwin.c
blob0cd4e84a890cb853ee2b634a851582156de02c71
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;
48 FD_ZERO(&fds);
49 FD_SET(0, &fds);
50 FD_SET(server_sock, &fds);
51 tv.tv_sec = msec/1000;
52 tv.tv_usec = (msec*1000) % 1000000;
53 while (select(server_sock+1, &fds, NULL, NULL, msec<0 ? NULL : &tv) < 0) {
54 if (errno != EINTR)
55 perror("Warning: select() failed");
57 if (FD_ISSET(0, &fds)) {
58 c = getch();
59 if (c == KEY_UP)
60 return K_UP;
61 else if (c == KEY_DOWN)
62 return K_DOWN;
63 else if (c == KEY_LEFT)
64 return K_LEFT;
65 else if (c == KEY_RIGHT)
66 return K_RIGHT;
67 else if (c == KEY_F(1))
68 return K_F1;
69 else if (c == KEY_F(2))
70 return K_F2;
71 else if (c == KEY_F(3))
72 return K_F3;
73 else if (c == KEY_F(4))
74 return K_F4;
75 else if (c == KEY_F(5))
76 return K_F5;
77 else if (c == KEY_F(6))
78 return K_F6;
79 else if (c == KEY_F(7))
80 return K_F7;
81 else if (c == KEY_F(8))
82 return K_F8;
83 else if (c == KEY_F(9))
84 return K_F9;
85 else if (c == KEY_F(10))
86 return K_F10;
87 else if (c == KEY_F(11))
88 return K_F11;
89 else if (c == KEY_F(12))
90 return K_F12;
91 else if (c == KEY_BACKSPACE)
92 return 8;
93 else if (c >= 0x0100)
94 return K_INVALID;
95 else if (c == 7) /* ^G */
96 return 27; /* Escape */
97 else
98 return c;
99 } /* if (FD_ISSET(0, &fds)) */
100 else if (FD_ISSET(server_sock, &fds))
101 return -1;
102 else
103 return -2; /* out of time */
106 /*************************************************************************/
107 /****************************** Output stuff *****************************/
108 /*************************************************************************/
110 /* Size of the screen */
111 static int scrwidth, scrheight;
113 /* Is color available? */
114 static int has_color;
116 /*************************************************************************/
118 /* Text buffers: */
120 typedef struct {
121 int x, y, width, height;
122 int line;
123 WINDOW *win; /* NULL if not currently displayed */
124 char **text;
125 } TextBuffer;
127 static TextBuffer plinebuf, gmsgbuf, attdefbuf;
129 /*************************************************************************/
131 /* Window for typing in-game text, and its coordinates: */
133 static WINDOW *gmsg_inputwin;
134 static int gmsg_inputpos, gmsg_inputheight;
136 /*************************************************************************/
137 /*************************************************************************/
139 /* Clean up the screen on exit. */
141 static void screen_cleanup()
143 wmove(stdscr, scrheight-1, 0);
144 wrefresh(stdscr);
145 endwin();
146 printf("\n");
149 /*************************************************************************/
151 /* Little signal handler that just does an exit(1) (thereby getting our
152 * cleanup routine called), except for TSTP, which does a clean suspend.
155 static void (*old_tstp)(int sig);
157 static void sighandler(int sig)
159 if (sig != SIGTSTP) {
160 endwin();
161 if (sig != SIGINT)
162 fprintf(stderr, "%s\n", strsignal(sig));
163 exit(1);
165 endwin();
166 signal(SIGTSTP, old_tstp);
167 raise(SIGTSTP);
168 doupdate();
169 signal(SIGTSTP, sighandler);
172 /*************************************************************************/
173 /*************************************************************************/
175 #define MAXCOLORS 256
177 static int colors[MAXCOLORS][2] = { {-1,-1} };
179 /* Return a color attribute value. */
181 static long getcolor(int fg, int bg)
183 int i;
185 if (colors[0][0] < 0) {
186 start_color();
187 memset(colors, -1, sizeof(colors));
188 colors[0][0] = COLOR_WHITE;
189 colors[0][1] = COLOR_BLACK;
191 if (fg == COLOR_WHITE && bg == COLOR_BLACK)
192 return COLOR_PAIR(0);
193 for (i = 1; i < MAXCOLORS; i++) {
194 if (colors[i][0] == fg && colors[i][1] == bg)
195 return COLOR_PAIR(i);
197 for (i = 1; i < MAXCOLORS; i++) {
198 if (colors[i][0] < 0) {
199 if (init_pair(i, fg, bg) == ERR)
200 continue;
201 colors[i][0] = fg;
202 colors[i][1] = bg;
203 return COLOR_PAIR(i);
206 return -1;
209 /*************************************************************************/
210 /*************************************************************************/
212 /* Set up the screen stuff. */
214 static void screen_setup(void)
216 /* Avoid messy keyfield signals while we're setting up */
217 signal(SIGINT, SIG_IGN);
218 signal(SIGQUIT, SIG_IGN);
219 signal(SIGTSTP, SIG_IGN);
221 initscr();
222 cbreak();
223 noecho();
224 nodelay(stdscr, TRUE);
225 keypad(stdscr, TRUE);
226 leaveok(stdscr, TRUE);
227 if ((has_color = has_colors()))
228 start_color();
229 getmaxyx(stdscr, scrheight, scrwidth);
230 scrwidth--; /* Don't draw in last column--this can cause scroll */
232 /* Cancel all this when we exit. */
233 atexit(screen_cleanup);
235 /* Catch signals so we can exit cleanly. */
236 signal(SIGINT, sighandler);
237 signal(SIGQUIT, sighandler);
238 signal(SIGTERM, sighandler);
239 signal(SIGHUP, sighandler);
240 /* signal(SIGSEGV, sighandler); */
241 signal(SIGABRT, sighandler);
242 signal(SIGIOT, sighandler);
243 signal(SIGTRAP, sighandler);
244 signal(SIGBUS, sighandler);
245 signal(SIGFPE, sighandler);
246 signal(SIGUSR1, sighandler);
247 signal(SIGUSR2, sighandler);
248 signal(SIGALRM, sighandler);
249 #ifdef linux
250 signal(SIGSTKFLT, sighandler);
251 #endif
252 signal(SIGTSTP, sighandler);
253 signal(SIGXCPU, sighandler);
254 signal(SIGXFSZ, sighandler);
255 signal(SIGVTALRM, sighandler);
257 /* Broken pipes don't want to bother us at all. */
258 signal(SIGPIPE, SIG_IGN);
261 /*************************************************************************/
263 /* Redraw everything on the screen. */
265 static void screen_refresh(void)
267 if (gmsg_inputwin)
268 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
269 if (plinebuf.win)
270 touchline(stdscr, plinebuf.y, plinebuf.height);
271 if (gmsgbuf.win)
272 touchline(stdscr, gmsgbuf.y, gmsgbuf.height);
273 if (attdefbuf.win)
274 touchline(stdscr, attdefbuf.y, attdefbuf.height);
275 wnoutrefresh(stdscr);
276 doupdate();
279 /*************************************************************************/
281 /* Like screen_refresh(), but clear the screen first. */
283 static void screen_redraw(void)
285 clearok(stdscr, TRUE);
286 screen_refresh();
289 /*************************************************************************/
290 /************************* Text buffer routines **************************/
291 /*************************************************************************/
293 /* Put a line of text in a text buffer. */
295 static void outline(TextBuffer *buf, const char *s)
297 if (buf->line == buf->height) {
298 if (buf->win)
299 scroll(buf->win);
300 memmove(buf->text, buf->text+1, (buf->height-1) * sizeof(char *));
301 buf->line--;
303 if (buf->win)
304 mvwaddstr(buf->win, buf->line, 0, s);
305 if (s != buf->text[buf->line]) /* check for restoring display */
306 buf->text[buf->line] = strdup(s);
307 buf->line++;
310 static void draw_text(int bufnum, const char *s)
312 char str[1024]; /* hopefully scrwidth < 1024 */
313 const char *t;
314 int indent = 0;
315 int x = 0, y = 0;
316 TextBuffer *buf;
318 switch (bufnum) {
319 case BUFFER_PLINE: buf = &plinebuf; break;
320 case BUFFER_GMSG: buf = &gmsgbuf; break;
321 case BUFFER_ATTDEF: buf = &attdefbuf; break;
322 default: return;
324 if (!buf->text)
325 return;
326 if (buf->win) {
327 getyx(stdscr, y, x);
328 attrset(getcolor(COLOR_WHITE, COLOR_BLACK));
330 while (*s && isspace(*s))
331 s++;
332 while (strlen(s) > buf->width - indent) {
333 t = s + buf->width - indent;
334 while (t >= s && !isspace(*t))
335 t--;
336 while (t >= s && isspace(*t))
337 t--;
338 t++;
339 if (t < s)
340 t = s + buf->width - indent;
341 if (indent > 0)
342 sprintf(str, "%*s", indent, "");
343 strncpy(str+indent, s, t-s);
344 str[t-s+indent] = 0;
345 outline(buf, str);
346 indent = 2;
347 while (isspace(*t))
348 t++;
349 s = t;
351 if (indent > 0)
352 sprintf(str, "%*s", indent, "");
353 strcpy(str+indent, s);
354 outline(buf, str);
355 if (buf->win) {
356 move(y, x);
357 screen_refresh();
361 /*************************************************************************/
363 /* Clear the contents of a text buffer. */
365 static void clear_text(int bufnum)
367 TextBuffer *buf;
368 int i;
370 switch (bufnum) {
371 case BUFFER_PLINE: buf = &plinebuf; break;
372 case BUFFER_GMSG: buf = &gmsgbuf; break;
373 case BUFFER_ATTDEF: buf = &attdefbuf; break;
374 default: return;
376 if (buf->text) {
377 for (i = 0; i < buf->height; i++) {
378 if (buf->text[i]) {
379 free(buf->text[i]);
380 buf->text[i] = NULL;
383 buf->line = 0;
385 if (buf->win) {
386 werase(buf->win);
387 screen_refresh();
391 /*************************************************************************/
393 /* Restore the contents of the given text buffer. */
395 static void restore_text(TextBuffer *buf)
397 buf->line = 0;
398 while (buf->line < buf->height && buf->text[buf->line])
399 outline(buf, buf->text[buf->line]);
402 /*************************************************************************/
404 /* Open a window for the given text buffer. */
406 static void open_textwin(TextBuffer *buf)
408 if (buf->height <= 0 || buf->width <= 0) {
409 char str[256];
410 move(scrheight-1, 0);
411 snprintf(str, sizeof(str), "ERROR: bad textwin size (%d,%d)",
412 buf->width, buf->height);
413 addstr(str);
414 exit(1);
416 if (!buf->win) {
417 buf->win = subwin(stdscr, buf->height, buf->width, buf->y, buf->x);
418 scrollok(buf->win, TRUE);
420 if (!buf->text)
421 buf->text = calloc(buf->height, sizeof(char *));
422 else
423 restore_text(buf);
426 /*************************************************************************/
428 /* Close the window for the given text buffer, if it's open. */
430 static void close_textwin(TextBuffer *buf)
432 if (buf->win) {
433 delwin(buf->win);
434 buf->win = NULL;
438 /*************************************************************************/
439 /************************ Field drawing routines *************************/
440 /*************************************************************************/
442 /* Are we on a wide screen (>=92 columns)? */
443 static int wide_screen = 0;
445 /* Field display X/Y coordinates. */
446 static const int own_coord[2] = {0,0};
447 static int other_coord[5][2] = /* Recomputed based on screen width */
448 { {30,0}, {47,0}, {64,0}, {47,24}, {64,24} };
450 /* Position of the status window. */
451 static const int status_coord[2] = {28,25};
452 static const int next_coord[2] = {40,24};
453 static const int alt_status_coord[2] = {28,2};
454 static const int alt_next_coord[2] = {29,8};
456 /* Position of the attacks/defenses window. */
457 static const int attdef_coord[2] = {28,28};
458 static const int alt_attdef_coord[2] = {28,24};
460 /* Position of the text window. X coordinate is ignored. */
461 static const int field_text_coord[2] = {0,47};
463 /* Information for drawing blocks. Color attributes are added to blocks in
464 * the setup_fields() routine. */
465 static int tile_chars[15] =
466 { ' ','#','#','#','#','#','a','c','n','r','s','b','g','q','o' };
468 /* Are we redrawing the entire display? */
469 static int field_redraw = 0;
471 /*************************************************************************/
472 /*************************************************************************/
474 /* Set up the field display. */
476 static void draw_own_field(void);
477 static void draw_other_field(int player);
478 static void draw_status(void);
479 static void draw_specials(void);
480 static void draw_gmsg_input(const char *s, int pos);
482 static void setup_fields(void)
484 int i, j, x, y, base, delta, attdefbot;
485 char buf[32];
487 if (!(tile_chars[0] & A_ATTRIBUTES)) {
488 for (i = 1; i < 15; i++)
489 tile_chars[i] |= A_BOLD;
490 tile_chars[1] |= getcolor(COLOR_BLUE, COLOR_BLACK);
491 tile_chars[2] |= getcolor(COLOR_YELLOW, COLOR_BLACK);
492 tile_chars[3] |= getcolor(COLOR_GREEN, COLOR_BLACK);
493 tile_chars[4] |= getcolor(COLOR_MAGENTA, COLOR_BLACK);
494 tile_chars[5] |= getcolor(COLOR_RED, COLOR_BLACK);
497 field_redraw = 1;
498 leaveok(stdscr, TRUE);
499 close_textwin(&plinebuf);
500 clear();
501 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
503 if (scrwidth >= 92) {
504 wide_screen = 1;
505 base = 41;
506 } else {
507 base = 28;
509 delta = (scrwidth - base) / 3;
510 base += 2 + (delta - (FIELD_WIDTH+5)) / 2;
511 other_coord[0][0] = base;
512 other_coord[1][0] = base + delta;
513 other_coord[2][0] = base + delta*2;
514 other_coord[3][0] = base + delta;
515 other_coord[4][0] = base + delta*2;
517 attdefbot = field_text_coord[1] - 1;
518 if (scrheight - field_text_coord[1] > 3) {
519 move(field_text_coord[1], 0);
520 hline(MY_HLINE2, scrwidth);
521 attdefbot--;
522 if (scrheight - field_text_coord[1] > 5) {
523 move(scrheight-2, 0);
524 hline(MY_HLINE2, scrwidth);
525 attrset(MY_BOLD);
526 move(scrheight-1, 0);
527 addstr("F1=Show Fields F2=Partyline F3=Winlist");
528 move(scrheight-1, scrwidth-8);
529 addstr("F10=Quit");
530 attrset(A_NORMAL);
531 gmsgbuf.y = field_text_coord[1]+1;
532 gmsgbuf.height = scrheight - field_text_coord[1] - 3;
533 } else {
534 gmsgbuf.y = field_text_coord[1]+1;
535 gmsgbuf.height = scrheight - field_text_coord[1] - 1;
537 } else {
538 gmsgbuf.y = field_text_coord[1];
539 gmsgbuf.height = scrheight - field_text_coord[1];
541 gmsgbuf.x = field_text_coord[0];
542 gmsgbuf.width = scrwidth;
543 open_textwin(&gmsgbuf);
545 x = own_coord[0];
546 y = own_coord[1];
547 sprintf(buf, "%d", my_playernum);
548 mvaddstr(y, x+FIELD_WIDTH*2+2, buf);
549 for (i = 2; i < FIELD_HEIGHT*2 && players[my_playernum-1][i-2]; i++)
550 mvaddch(y+i, x+FIELD_WIDTH*2+2, players[my_playernum-1][i-2]);
551 move(y, x);
552 vline(MY_VLINE, FIELD_HEIGHT*2);
553 move(y, x+FIELD_WIDTH*2+1);
554 vline(MY_VLINE, FIELD_HEIGHT*2);
555 move(y+FIELD_HEIGHT*2, x);
556 addch(MY_LLCORNER);
557 hline(MY_HLINE, FIELD_WIDTH*2);
558 move(y+FIELD_HEIGHT*2, x+FIELD_WIDTH*2+1);
559 addch(MY_LRCORNER);
560 mvaddstr(y+FIELD_HEIGHT*2+2, x, "Specials:");
561 draw_own_field();
562 draw_specials();
564 for (j = 0; j < 5; j++) {
565 x = other_coord[j][0];
566 y = other_coord[j][1];
567 move(y, x);
568 vline(MY_VLINE, FIELD_HEIGHT);
569 move(y, x+FIELD_WIDTH+1);
570 vline(MY_VLINE, FIELD_HEIGHT);
571 move(y+FIELD_HEIGHT, x);
572 addch(MY_LLCORNER);
573 hline(MY_HLINE, FIELD_WIDTH);
574 move(y+FIELD_HEIGHT, x+FIELD_WIDTH+1);
575 addch(MY_LRCORNER);
576 if (j+1 >= my_playernum) {
577 sprintf(buf, "%d", j+2);
578 mvaddstr(y, x+FIELD_WIDTH+2, buf);
579 if (players[j+1]) {
580 for (i = 0; i < FIELD_HEIGHT-2 && players[j+1][i]; i++)
581 mvaddch(y+i+2, x+FIELD_WIDTH+2, players[j+1][i]);
583 draw_other_field(j+2);
584 } else {
585 sprintf(buf, "%d", j+1);
586 mvaddstr(y, x+FIELD_WIDTH+2, buf);
587 if (players[j]) {
588 for (i = 0; i < FIELD_HEIGHT-2 && players[j][i]; i++)
589 mvaddch(y+i+2, x+FIELD_WIDTH+2, players[j][i]);
591 draw_other_field(j+1);
595 if (wide_screen) {
596 x = alt_status_coord[0];
597 y = alt_status_coord[1];
598 mvaddstr(y, x, "Lines:");
599 mvaddstr(y+1, x, "Level:");
600 x = alt_next_coord[0];
601 y = alt_next_coord[1];
602 mvaddstr(y-2, x-1, "Next piece:");
603 move(y-1, x-1);
604 addch(MY_ULCORNER);
605 hline(MY_HLINE, 8);
606 mvaddch(y-1, x+8, MY_URCORNER);
607 move(y, x-1);
608 vline(MY_VLINE, 8);
609 move(y, x+8);
610 vline(MY_VLINE, 8);
611 move(y+8, x-1);
612 addch(MY_LLCORNER);
613 hline(MY_HLINE, 8);
614 mvaddch(y+8, x+8, MY_LRCORNER);
615 } else {
616 x = status_coord[0];
617 y = status_coord[1];
618 mvaddstr(y-1, x, "Next piece:");
619 mvaddstr(y, x, "Lines:");
620 mvaddstr(y+1, x, "Level:");
622 if (playing_game)
623 draw_status();
625 attdefbuf.x = wide_screen ? alt_attdef_coord[0] : attdef_coord[0];
626 attdefbuf.y = wide_screen ? alt_attdef_coord[1] : attdef_coord[1];
627 attdefbuf.width = (other_coord[3][0]-1) - attdefbuf.x;
628 attdefbuf.height = (attdefbot+1) - attdefbuf.y;
629 open_textwin(&attdefbuf);
631 if (gmsg_inputwin) {
632 delwin(gmsg_inputwin);
633 gmsg_inputwin = NULL;
634 draw_gmsg_input(NULL, -1);
637 screen_refresh();
638 field_redraw = 0;
641 /*************************************************************************/
643 /* Display the player's own field. */
645 static void draw_own_field(void)
647 int x, y, x0, y0;
648 Field *f = &fields[my_playernum-1];
650 if (dispmode != MODE_FIELDS)
651 return;
652 x0 = own_coord[0]+1;
653 y0 = own_coord[1];
654 for (y = 0; y < 22; y++) {
655 for (x = 0; x < 12; x++) {
656 int c = tile_chars[(int) (*f)[y][x]];
657 mvaddch(y0+y*2, x0+x*2, c);
658 addch(c);
659 mvaddch(y0+y*2+1, x0+x*2, c);
660 addch(c);
663 if (gmsg_inputwin) {
664 delwin(gmsg_inputwin);
665 gmsg_inputwin = NULL;
666 draw_gmsg_input(NULL, -1);
668 if (!field_redraw)
669 screen_refresh();
672 /*************************************************************************/
674 /* Display another player's field. */
676 static void draw_other_field(int player)
678 int x, y, x0, y0;
679 Field *f;
681 if (dispmode != MODE_FIELDS)
682 return;
683 f = &fields[player-1];
684 if (player > my_playernum)
685 player--;
686 player--;
687 x0 = other_coord[player][0]+1;
688 y0 = other_coord[player][1];
689 for (y = 0; y < 22; y++) {
690 move(y0+y, x0);
691 for (x = 0; x < 12; x++)
692 addch(tile_chars[(int) (*f)[y][x]]);
694 if (gmsg_inputwin) {
695 delwin(gmsg_inputwin);
696 gmsg_inputwin = NULL;
697 draw_gmsg_input(NULL, -1);
699 if (!field_redraw)
700 screen_refresh();
703 /*************************************************************************/
705 /* Display the current game status (level, lines, next piece). */
707 static void draw_status(void)
709 int x, y, i, j;
710 char buf[32], shape[4][4];
712 x = wide_screen ? alt_status_coord[0] : status_coord[0];
713 y = wide_screen ? alt_status_coord[1] : status_coord[1];
714 sprintf(buf, "%d", lines>99999 ? 99999 : lines);
715 mvaddstr(y+1, x+7, buf);
716 sprintf(buf, "%d", levels[my_playernum]);
717 mvaddstr(y+2, x+7, buf);
718 x = wide_screen ? alt_next_coord[0] : next_coord[0];
719 y = wide_screen ? alt_next_coord[1] : next_coord[1];
720 if (get_shape(next_piece, 0, shape) == 0) {
721 for (j = 0; j < 4; j++) {
722 if (!wide_screen)
723 move(y+j, x);
724 for (i = 0; i < 4; i++) {
725 if (wide_screen) {
726 move(y+j*2, x+i*2);
727 addch(tile_chars[(int) shape[j][i]]);
728 addch(tile_chars[(int) shape[j][i]]);
729 move(y+j*2+1, x+i*2);
730 addch(tile_chars[(int) shape[j][i]]);
731 addch(tile_chars[(int) shape[j][i]]);
732 } else
733 addch(tile_chars[(int) shape[j][i]]);
739 /*************************************************************************/
741 /* Display the special inventory and description of the current special. */
743 static const char *descs[] = {
744 " ",
745 "Add Line ",
746 "Clear Line ",
747 "Nuke Field ",
748 "Clear Random Blocks ",
749 "Switch Fields ",
750 "Clear Special Blocks",
751 "Block Gravity ",
752 "Blockquake ",
753 "Block Bomb "
756 static void draw_specials(void)
758 int x, y, i;
760 if (dispmode != MODE_FIELDS)
761 return;
762 x = own_coord[0];
763 y = own_coord[1]+45;
764 mvaddstr(y, x, descs[specials[0]+1]);
765 move(y+1, x+10);
766 i = 0;
767 while (i < special_capacity && specials[i] >= 0 && x < attdef_coord[0]-1) {
768 addch(tile_chars[specials[i]+6]);
769 i++;
770 x++;
772 while (x < attdef_coord[0]-1) {
773 addch(tile_chars[0]);
774 x++;
776 if (!field_redraw)
777 screen_refresh();
780 /*************************************************************************/
782 /* Display an attack/defense message. */
784 static const char *msgs[][2] = {
785 { "cs1", "1 Line Added to All" },
786 { "cs2", "2 Lines Added to All" },
787 { "cs4", "4 Lines Added to All" },
788 { "a", "Add Line" },
789 { "c", "Clear Line" },
790 { "n", "Nuke Field" },
791 { "r", "Clear Random Blocks" },
792 { "s", "Switch Fields" },
793 { "b", "Clear Special Blocks" },
794 { "g", "Block Gravity" },
795 { "q", "Blockquake" },
796 { "o", "Block Bomb" },
797 { NULL }
800 static void draw_attdef(const char *type, int from, int to)
802 int i, width;
803 char buf[512];
805 width = other_coord[4][0] - attdef_coord[0] - 1;
806 for (i = 0; msgs[i][0]; i++) {
807 if (strcmp(type, msgs[i][0]) == 0)
808 break;
810 if (!msgs[i][0])
811 return;
812 strcpy(buf, msgs[i][1]);
813 if (to != 0)
814 sprintf(buf+strlen(buf), " on %s", players[to-1]);
815 if (from == 0)
816 sprintf(buf+strlen(buf), " by Server");
817 else
818 sprintf(buf+strlen(buf), " by %s", players[from-1]);
819 draw_text(BUFFER_ATTDEF, buf);
822 /*************************************************************************/
824 /* Display the in-game text window. */
826 static void draw_gmsg_input(const char *s, int pos)
828 static int start = 0; /* Start of displayed part of input line */
829 static const char *last_s;
830 static int last_pos;
832 if (s)
833 last_s = s;
834 else
835 s = last_s;
836 if (pos >= 0)
837 last_pos = pos;
838 else
839 pos = last_pos;
841 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
843 if (!gmsg_inputwin) {
844 gmsg_inputpos = scrheight/2 - 1;
845 gmsg_inputheight = 3;
846 gmsg_inputwin =
847 subwin(stdscr, gmsg_inputheight, scrwidth, gmsg_inputpos, 0);
848 werase(gmsg_inputwin);
849 leaveok(gmsg_inputwin, FALSE);
850 leaveok(stdscr, FALSE);
851 mvwaddstr(gmsg_inputwin, 1, 0, "Text>");
854 if (strlen(s) < scrwidth-7) {
855 start = 0;
856 mvwaddstr(gmsg_inputwin, 1, 6, s);
857 wmove(gmsg_inputwin, 1, 6+strlen(s));
858 move(gmsg_inputpos+1, 6+strlen(s));
859 wclrtoeol(gmsg_inputwin);
860 wmove(gmsg_inputwin, 1, 6+pos);
861 move(gmsg_inputpos+1, 6+pos);
862 } else {
863 if (pos < start+8) {
864 start = pos-8;
865 if (start < 0)
866 start = 0;
867 } else if (pos > start + scrwidth-15) {
868 start = pos - (scrwidth-15);
869 if (start > strlen(s) - (scrwidth-7))
870 start = strlen(s) - (scrwidth-7);
872 mvwaddnstr(gmsg_inputwin, 1, 6, s+start, scrwidth-6);
873 wmove(gmsg_inputwin, 1, 6 + (pos-start));
874 move(gmsg_inputpos+1, 6 + (pos-start));
876 screen_refresh();
879 /*************************************************************************/
881 /* Clear the in-game text window. */
883 static void clear_gmsg_input(void)
885 if (gmsg_inputwin) {
886 delwin(gmsg_inputwin);
887 gmsg_inputwin = NULL;
888 leaveok(stdscr, TRUE);
889 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
890 setup_fields();
891 screen_refresh();
895 /*************************************************************************/
896 /*************************** Partyline display ***************************/
897 /*************************************************************************/
899 static void setup_partyline(void)
901 close_textwin(&gmsgbuf);
902 close_textwin(&attdefbuf);
903 clear();
905 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
907 plinebuf.x = plinebuf.y = 0;
908 plinebuf.width = scrwidth;
909 plinebuf.height = scrheight-4;
910 open_textwin(&plinebuf);
912 move(scrheight-4, 0);
913 hline(MY_HLINE, scrwidth);
914 move(scrheight-3, 0);
915 addstr("> ");
917 move(scrheight-2, 0);
918 hline(MY_HLINE2, scrwidth);
919 attrset(MY_BOLD);
920 move(scrheight-1, 0);
921 addstr("F1=Show Fields F2=Partyline F3=Winlist");
922 move(scrheight-1, scrwidth-8);
923 addstr("F10=Quit");
924 attrset(A_NORMAL);
926 move(scrheight-3, 2);
927 leaveok(stdscr, FALSE);
928 screen_refresh();
931 /*************************************************************************/
933 static void draw_partyline_input(const char *s, int pos)
935 static int start = 0; /* Start of displayed part of input line */
937 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
938 if (strlen(s) < scrwidth-3) {
939 start = 0;
940 mvaddstr(scrheight-3, 2, s);
941 move(scrheight-3, 2+strlen(s));
942 clrtoeol();
943 move(scrheight-3, 2+pos);
944 } else {
945 if (pos < start+8) {
946 start = pos-8;
947 if (start < 0)
948 start = 0;
949 } else if (pos > start + scrwidth-11) {
950 start = pos - (scrwidth-11);
951 if (start > strlen(s) - (scrwidth-3))
952 start = strlen(s) - (scrwidth-3);
954 mvaddnstr(scrheight-3, 2, s+start, scrwidth-2);
955 move(scrheight-3, 2 + (pos-start));
957 screen_refresh();
960 /*************************************************************************/
961 /**************************** Winlist display ****************************/
962 /*************************************************************************/
964 static void setup_winlist(void)
966 int i, x;
967 char buf[32];
969 leaveok(stdscr, TRUE);
970 close_textwin(&plinebuf);
971 clear();
972 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
974 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
975 x = scrwidth/2 - strlen(winlist[i].name);
976 if (x < 0)
977 x = 0;
978 if (winlist[i].team) {
979 if (x < 4)
980 x = 4;
981 mvaddstr(i*2, x-4, "<T>");
983 mvaddstr(i*2, x, winlist[i].name);
984 snprintf(buf, sizeof(buf), "%4d", winlist[i].points);
985 if (winlist[i].games) {
986 int avg100 = winlist[i].points*100 / winlist[i].games;
987 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
988 " %d.%02d",avg100/100, avg100%100);
990 x += strlen(winlist[i].name) + 2;
991 if (x > scrwidth - strlen(buf))
992 x = scrwidth - strlen(buf);
993 mvaddstr(i*2, x, buf);
996 move(scrheight-2, 0);
997 hline(MY_HLINE2, scrwidth);
998 attrset(MY_BOLD);
999 move(scrheight-1, 0);
1000 addstr("F1=Show Fields F2=Partyline F3=Winlist");
1001 move(scrheight-1, scrwidth-8);
1002 addstr("F10=Quit");
1003 attrset(A_NORMAL);
1005 screen_refresh();
1008 /*************************************************************************/
1009 /************************** Interface declaration ************************/
1010 /*************************************************************************/
1012 Interface xwin_interface = {
1014 wait_for_input,
1016 screen_setup,
1017 screen_refresh,
1018 screen_redraw,
1020 draw_text,
1021 clear_text,
1023 setup_fields,
1024 draw_own_field,
1025 draw_other_field,
1026 draw_status,
1027 draw_specials,
1028 draw_attdef,
1029 draw_gmsg_input,
1030 clear_gmsg_input,
1032 setup_partyline,
1033 draw_partyline_input,
1035 setup_winlist
1038 /*************************************************************************/