Add description field to macros.
[cboard.git] / src / cboard.c
blobb8b74ceb4366a363d7f396f71db4faca8fdc6019
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2018 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <pwd.h>
33 #include <signal.h>
34 #include <time.h>
35 #include <err.h>
36 #include <locale.h>
38 #ifdef HAVE_STDARG_H
39 #include <stdarg.h>
40 #endif
42 #ifdef HAVE_SYS_WAIT_H
43 #include <sys/wait.h>
44 #endif
46 #ifdef HAVE_REGEX_H
47 #include <regex.h>
48 #endif
50 #ifdef WITH_LIBPERL
51 #include "perl-plugin.h"
52 #endif
54 #include "common.h"
55 #include "conf.h"
56 #include "window.h"
57 #include "message.h"
58 #include "colors.h"
59 #include "input.h"
60 #include "misc.h"
61 #include "engine.h"
62 #include "strings.h"
63 #include "menu.h"
64 #include "keys.h"
65 #include "rcfile.h"
66 #include "filebrowser.h"
68 #ifdef DEBUG
69 #include <debug.h>
70 #endif
72 #define CBOARD_URL "https://gitlab.com/bjk/cboard/wikis"
73 #define COPYRIGHT "Copyright (C) 2002-2018 " PACKAGE_BUGREPORT
74 #define LINE_GRAPHIC(c) ((!config.linegraphics) ? ' ' : c)
75 #define ROWTOMATRIX(r) ((8 - r) * 2 + 2 - 1)
76 #define COLTOMATRIX(c) ((c == 1) ? 1 : c * 4 - 3)
77 #define STATUS_HEIGHT 12
78 #define MEGA_BOARD (LINES >= 50 && COLS >= 144)
79 #define BOARD_HEIGHT_MB 50
80 #define BOARD_WIDTH_MB 98
81 #define STATUS_WIDTH_MB (COLS - BOARD_WIDTH_MB)
82 #define TAG_HEIGHT_MB 31
83 #define TAG_WIDTH_MB (COLS - BOARD_WIDTH_MB)
84 #define HISTORY_HEIGHT_MB (LINES - (STATUS_HEIGHT + TAG_HEIGHT_MB + 1))
85 #define HISTORY_WIDTH_MB (COLS - BOARD_WIDTH_MB)
86 #define BIG_BOARD (LINES >= 40 && COLS >= 112)
87 #define BOARD_HEIGHT ((MEGA_BOARD) ? BOARD_HEIGHT_MB : (BIG_BOARD) ? 34 : 18)
88 #define BOARD_WIDTH ((MEGA_BOARD) ? BOARD_WIDTH_MB : (BIG_BOARD) ? 66 : 34)
89 #define STATUS_WIDTH ((MEGA_BOARD) ? STATUS_WIDTH_MB : COLS - BOARD_WIDTH)
90 #define TAG_HEIGHT ((MEGA_BOARD) ? TAG_HEIGHT_MB : LINES - STATUS_HEIGHT - 1)
91 #define TAG_WIDTH ((MEGA_BOARD) ? TAG_WIDTH_MB : COLS - BOARD_WIDTH)
92 #define HISTORY_HEIGHT ((MEGA_BOARD) ? HISTORY_HEIGHT_MB : LINES - BOARD_HEIGHT)
93 #define HISTORY_WIDTH ((MEGA_BOARD) ? HISTORY_WIDTH_MB : COLS - STATUS_WIDTH)
94 #define MAX_VALUE_WIDTH (COLS - 8)
96 enum
98 UP, DOWN, LEFT, RIGHT
101 static WINDOW *boardw;
102 static PANEL *boardp;
103 static WINDOW *tagw;
104 static PANEL *tagp;
105 static WINDOW *statusw;
106 static PANEL *statusp;
107 static WINDOW *historyw;
108 static PANEL *historyp;
109 static WINDOW *loadingw;
110 static PANEL *loadingp;
111 static WINDOW *enginew;
112 static PANEL *enginep;
114 static char gameexp[255];
115 static char moveexp[255];
116 static struct itimerval clock_timer;
117 static int delete_count = 0;
118 static int markstart = -1, markend = -1;
119 static int keycount;
120 static char loadfile[FILENAME_MAX];
121 static int quit;
122 static wint_t input_c;
124 // Loaded filename from the command line or from the file input dialog.
125 static int filetype;
126 enum
128 FILE_NONE, FILE_PGN, FILE_FEN, FILE_EPD
131 static char **nags;
132 static int nag_total;
133 static int macro_match;
135 // Primer movimiento de juego cargado
136 // First move loaded game
137 static char fm_loaded_file = FALSE;
139 static int COLS_OLD, LINES_OLD;
141 // Status window.
142 static struct
144 wchar_t *notify; // The status window notification line buffer.
145 } status;
147 static int curses_initialized;
149 // When in history mode a full step is to the next move of the same playing
150 // side. Half stepping is alternating sides.
151 static int movestep;
153 static wchar_t *w_pawn_wchar;
154 static wchar_t *w_rook_wchar;
155 static wchar_t *w_bishop_wchar;
156 static wchar_t *w_knight_wchar;
157 static wchar_t *w_queen_wchar;
158 static wchar_t *w_king_wchar;
159 static wchar_t *b_pawn_wchar;
160 static wchar_t *b_rook_wchar;
161 static wchar_t *b_bishop_wchar;
162 static wchar_t *b_knight_wchar;
163 static wchar_t *b_queen_wchar;
164 static wchar_t *b_king_wchar;
165 static wchar_t *empty_wchar;
166 static wchar_t *enpassant_wchar;
168 static wchar_t *yes_wchar;
169 static wchar_t *all_wchar; // do_save_game_overwrite_confirm()
170 static wchar_t *overwrite_wchar; // do_save_game_overwrite_confirm()
171 static wchar_t *resume_wchar; // do_history_mode_confirm()
172 static wchar_t *current_wchar; // do_game_save_multi_confirm()
173 static wchar_t *append_wchar; // save_pgn()
175 static const char piece_chars[] = "PpRrNnBbQqKkxx";
176 static char *translatable_tag_names[7];
177 static const char *f_pieces[] = {
178 " ", // 0
179 " O ",
180 " /_\\ ",
181 " |-|-| ", // 3
182 " ] [ ",
183 " /___\\ ",
184 " /?M ", // 6
185 " (@/)) ",
186 " /__))",
187 " O ", // 9
188 " (+) ",
189 " /_\\ ",
190 "•°°°°°•", // 12
191 " \\\\|// ",
192 " |___| ",
193 " __+__ ", // 15
194 "(__|__)",
195 " |___| ",
196 " \\ / ", // 18
197 " X ",
198 " / \\ "
201 static const bool cb[8][8] = {
202 {1, 0, 1, 0, 1, 0, 1, 0},
203 {0, 1, 0, 1, 0, 1, 0, 1},
204 {1, 0, 1, 0, 1, 0, 1, 0},
205 {0, 1, 0, 1, 0, 1, 0, 1},
206 {1, 0, 1, 0, 1, 0, 1, 0},
207 {0, 1, 0, 1, 0, 1, 0, 1},
208 {1, 0, 1, 0, 1, 0, 1, 0},
209 {0, 1, 0, 1, 0, 1, 0, 1}
212 static void free_userdata_once (GAME g);
213 static void do_more_help (WIN *);
215 void
216 coordofmove (GAME g, char *move, char *prow, char *pcol)
218 char l = strlen (move);
220 if (*move == 'O')
222 *prow = (g->turn == WHITE) ? 8 : 1;
223 *pcol = (l <= 4) ? 7 : 3;
224 return;
227 move += l;
229 while (!isdigit (*move))
230 move--;
232 *prow = RANKTOINT (*move--);
233 *pcol = FILETOINT (*move);
236 #define INV_INT(x) (9 - x)
237 #define INV_INT0(x) (7 - x)
239 // Posición por rotación de tablero.
240 // Rotation board position.
241 void
242 rotate_position (char *prow, char *pcol)
244 *prow = INV_INT (*prow);
245 *pcol = INV_INT (*pcol);
248 void
249 update_cursor (GAME g, int idx)
251 int t = pgn_history_total (g->hp);
252 struct userdata_s *d = g->data;
255 * If not deincremented then r and c would be the next move.
257 idx--;
259 if (idx > t || idx < 0 || !t || !g->hp[idx]->move)
260 d->c_row = 2, d->c_col = 5;
261 else
262 coordofmove (g, g->hp[idx]->move, &d->c_row, &d->c_col);
264 if (d->mode == MODE_HISTORY && d->rotate)
265 rotate_position (&d->c_row, &d->c_col);
268 static int
269 init_nag ()
271 FILE *fp;
272 char line[LINE_MAX];
273 int i = 0;
275 if ((fp = fopen (config.nagfile, "r")) == NULL)
277 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", config.nagfile,
278 strerror (errno));
279 return 1;
282 nags = Realloc (nags, (i + 2) * sizeof (char *));
283 nags[i++] = strdup (_("none"));
284 nags[i] = NULL;
286 while (!feof (fp))
288 if (fscanf (fp, " %[^\n] ", line) == 1)
290 nags = Realloc (nags, (i + 2) * sizeof (char *));
291 nags[i++] = strdup (line);
295 nags[i] = NULL;
296 nag_total = i;
297 fclose (fp);
298 return 0;
301 void
302 edit_nag_toggle_item (struct menu_input_s *m)
304 struct input_s *in = m->data;
305 struct input_data_s *id = in->data;
306 HISTORY *h = id->data;
307 int i;
309 if (m->selected == 0)
311 for (i = 0; i < MAX_PGN_NAG; i++)
312 h->nag[i] = 0;
314 for (i = 0; m->items[i]; i++)
315 m->items[i]->selected = 0;
317 return;
320 for (i = 0; i < MAX_PGN_NAG; i++)
322 if (h->nag[i] == m->selected)
323 h->nag[i] = m->selected = 0;
324 else
326 if (!h->nag[i])
328 h->nag[i] = m->selected;
329 break;
335 void
336 edit_nag_save (struct menu_input_s *m)
338 pushkey = -1;
341 void
342 edit_nag_help (struct menu_input_s *m)
344 message (_("NAG Menu Keys"), ANY_KEY_STR, "%s",
345 _(" UP/DOWN - previous/next menu item\n"
346 " HOME/END - first/last menu item\n"
347 " PGDN/PGUP - next/previous page\n"
348 " a-zA-Z0-9 - jump to item\n"
349 " SPACE - toggle selected item\n"
350 " CTRL-X - quit with changes"));
353 struct menu_item_s **
354 get_nag_items (WIN * win)
356 int i, n;
357 struct menu_input_s *m = win->data;
358 struct input_s *in = m->data;
359 struct input_data_s *id = in->data;
360 struct menu_item_s **items = m->items;
361 HISTORY *h = id->data;
363 if (items)
365 for (i = 0; items[i]; i++)
366 free (items[i]);
369 for (i = 0; nags[i]; i++)
371 items = Realloc (items, (i + 2) * sizeof (struct menu_item_s *));
372 items[i] = Malloc (sizeof (struct menu_item_s));
373 items[i]->name = nags[i];
374 items[i]->value = NULL;
376 for (n = 0; n < MAX_PGN_NAG; n++)
378 if (h->nag[n] == i)
380 items[i]->selected = 1;
381 n = -1;
382 break;
386 if (n >= 0)
387 items[i]->selected = 0;
390 items[i] = NULL;
391 m->nofree = 1;
392 m->items = items;
393 return items;
396 void
397 nag_print (WIN * win)
399 struct menu_input_s *m = win->data;
401 mvwprintw (win->w, m->print_line, 1, "%-*s", win->cols - 2, m->item->name);
404 void
405 edit_nag (void *arg)
407 struct menu_key_s **keys = NULL;
409 if (!nags)
411 if (init_nag ())
412 return;
415 add_menu_key (&keys, ' ', edit_nag_toggle_item);
416 add_menu_key (&keys, CTRL_KEY ('x'), edit_nag_save);
417 add_menu_key (&keys, KEY_F (1), edit_nag_help);
418 construct_menu (0, 0, -1, -1, _("Numeric Annotation Glyphs"), 1,
419 get_nag_items, keys, arg, nag_print, NULL, NULL);
420 return;
423 static void *
424 view_nag (void *arg)
426 HISTORY *h = (HISTORY *) arg;
427 char buf[80];
428 char line[LINE_MAX] = { 0 };
429 int i = 0;
431 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Viewing NAG for"), h->move);
433 if (!nags)
435 if (init_nag ())
436 return NULL;
439 for (i = 0; i < MAX_PGN_NAG; i++)
441 char buf2[16];
443 if (!h->nag[i])
444 break;
446 if (h->nag[i] >= nag_total)
447 strncat (line, itoa (h->nag[i], buf2), sizeof (line) - 1);
448 else
449 strncat (line, nags[h->nag[i]], sizeof (line) - 1);
451 strncat (line, "\n", sizeof (line) - 1);
454 line[strlen (line) - 1] = 0;
455 message (buf, ANY_KEY_STR, "%s", line);
456 return NULL;
459 void
460 view_annotation (HISTORY * h)
462 char buf[MAX_SAN_MOVE_LEN + strlen (_("Viewing Annotation for")) + 4];
463 int nag = 0, comment = 0;
465 if (!h)
466 return;
468 if (h->comment && h->comment[0])
469 comment++;
471 if (h->nag[0])
472 nag++;
474 if (!nag && !comment)
475 return;
477 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Viewing Annotation for"),
478 h->move);
480 if (comment)
481 construct_message (buf,
482 (nag) ? _("Any other key to continue") : ANY_KEY_STR,
483 0, 1, (nag) ? _("Press 'n' to view NAG") : NULL,
484 (nag) ? view_nag : NULL, (nag) ? h : NULL, NULL,
485 (nag) ? 'n' : 0, 0, NULL, "%s", h->comment);
486 else
487 construct_message (buf, _("Any other key to continue"), 0, 1,
488 _("Press 'n' to view NAG"), view_nag, h, NULL, 'n', 0,
489 NULL, "%s", _("No comment text for this move"));
493 do_game_write (char *filename, const char *mode, int start, int end)
495 int i;
496 struct userdata_s *d;
497 PGN_FILE *pgn;
499 i = pgn_open (filename, mode, &pgn);
501 if (i == E_PGN_ERR)
503 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", filename, strerror (errno));
504 return 1;
506 else if (i == E_PGN_INVALID)
508 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", filename,
509 _("Not a regular file"));
510 return 1;
513 for (i = (start == -1) ? 0 : start; i < end; i++)
515 d = game[i]->data;
516 pgn_write (pgn, game[i]);
517 CLEAR_FLAG (d->flags, CF_MODIFIED);
520 if (pgn_close (pgn) != E_PGN_OK)
521 message (ERROR_STR, ANY_KEY_STR, "%s", strerror (errno));
523 if (start == -1)
525 strncpy (loadfile, filename, sizeof (loadfile));
526 loadfile[sizeof (loadfile) - 1] = 0;
529 return 0;
532 struct save_game_s
534 char *filename;
535 char *mode;
536 int start;
537 int end;
540 void
541 do_save_game_overwrite_confirm (WIN * win)
543 const char *mode = "w";
544 struct save_game_s *s = win->data;
545 wchar_t str[] = { win->c, 0 };
547 if (!wcscmp (str, append_wchar))
548 mode = "a";
549 else if (!wcscmp (str, overwrite_wchar))
550 mode = "w";
551 else
552 goto done;
554 if (do_game_write (s->filename, mode, s->start, s->end))
555 update_status_notify (gp, "%s", _("Save game failed."));
556 else
557 update_status_notify (gp, "%s", _("Game saved."));
559 done:
560 free (s->filename);
561 free (s);
564 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
565 * game index number.
567 void
568 save_pgn (char *filename, int saveindex)
570 char buf[FILENAME_MAX];
571 struct stat st;
572 int end = (saveindex == -1) ? gtotal : saveindex + 1;
573 struct save_game_s *s;
575 if (filename[0] != '/' && config.savedirectory)
577 if (stat (config.savedirectory, &st) == -1)
579 if (errno == ENOENT)
581 if (mkdir (config.savedirectory, 0755) == -1)
583 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s",
584 config.savedirectory, strerror (errno));
585 return;
588 else
590 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s",
591 config.savedirectory, strerror (errno));
592 return;
596 if (stat (config.savedirectory, &st) == -1)
598 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
599 strerror (errno));
600 return;
603 if (!S_ISDIR (st.st_mode))
605 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
606 _("Not a directory."));
607 return;
610 snprintf (buf, sizeof (buf), "%s/%s", config.savedirectory, filename);
611 filename = buf;
614 if (access (filename, W_OK) == 0)
616 s = Malloc (sizeof (struct save_game_s));
617 s->filename = strdup (filename);
618 s->start = saveindex;
619 s->end = end;
620 construct_message (NULL, _("What would you like to do?"), 0, 1, NULL,
621 NULL, s, do_save_game_overwrite_confirm, 0, 0, NULL,
622 "%s \"%s\"\nPress \"%ls\" to append to this file, \"%ls\" to overwrite or any other key to cancel.",
623 _("File exists:"), filename, append_wchar,
624 overwrite_wchar);
625 return;
628 if (do_game_write (filename, "a", saveindex, end))
629 update_status_notify (gp, "%s", _("Save game failed."));
630 else
631 update_status_notify (gp, "%s", _("Game saved."));
634 static int
635 castling_state (GAME g, BOARD b, int row, int col, int piece, int mod)
637 if (pgn_piece_to_int (piece) == ROOK && col == 7
638 && row == 7 &&
639 (TEST_FLAG (g->flags, GF_WK_CASTLE) || mod) &&
640 pgn_piece_to_int (b[7][4].icon) == KING && isupper (piece))
642 if (mod)
643 TOGGLE_FLAG (g->flags, GF_WK_CASTLE);
644 return 1;
646 else if (pgn_piece_to_int (piece) == ROOK && col == 0
647 && row == 7 &&
648 (TEST_FLAG (g->flags, GF_WQ_CASTLE) || mod) &&
649 pgn_piece_to_int (b[7][4].icon) == KING && isupper (piece))
651 if (mod)
652 TOGGLE_FLAG (g->flags, GF_WQ_CASTLE);
653 return 1;
655 else if (pgn_piece_to_int (piece) == ROOK && col == 7
656 && row == 0 &&
657 (TEST_FLAG (g->flags, GF_BK_CASTLE) || mod) &&
658 pgn_piece_to_int (b[0][4].icon) == KING && islower (piece))
660 if (mod)
661 TOGGLE_FLAG (g->flags, GF_BK_CASTLE);
662 return 1;
664 else if (pgn_piece_to_int (piece) == ROOK && col == 0
665 && row == 0 &&
666 (TEST_FLAG (g->flags, GF_BQ_CASTLE) || mod) &&
667 pgn_piece_to_int (b[0][4].icon) == KING && islower (piece))
669 if (mod)
670 TOGGLE_FLAG (g->flags, GF_BQ_CASTLE);
671 return 1;
673 else if (pgn_piece_to_int (piece) == KING && col == 4
674 && row == 7 &&
675 (mod || (pgn_piece_to_int (b[7][7].icon) == ROOK &&
676 TEST_FLAG (g->flags, GF_WK_CASTLE))
678 (pgn_piece_to_int (b[7][0].icon) == ROOK &&
679 TEST_FLAG (g->flags, GF_WQ_CASTLE))) && isupper (piece))
681 if (mod)
683 if (TEST_FLAG (g->flags, GF_WK_CASTLE) ||
684 TEST_FLAG (g->flags, GF_WQ_CASTLE))
685 CLEAR_FLAG (g->flags, GF_WK_CASTLE | GF_WQ_CASTLE);
686 else
687 SET_FLAG (g->flags, GF_WK_CASTLE | GF_WQ_CASTLE);
689 return 1;
691 else if (pgn_piece_to_int (piece) == KING && col == 4
692 && row == 0 &&
693 (mod || (pgn_piece_to_int (b[0][7].icon) == ROOK &&
694 TEST_FLAG (g->flags, GF_BK_CASTLE))
696 (pgn_piece_to_int (b[0][0].icon) == ROOK &&
697 TEST_FLAG (g->flags, GF_BQ_CASTLE))) && islower (piece))
699 if (mod)
701 if (TEST_FLAG (g->flags, GF_BK_CASTLE) ||
702 TEST_FLAG (g->flags, GF_BQ_CASTLE))
703 CLEAR_FLAG (g->flags, GF_BK_CASTLE | GF_BQ_CASTLE);
704 else
705 SET_FLAG (g->flags, GF_BK_CASTLE | GF_BQ_CASTLE);
707 return 1;
710 return 0;
713 #define IS_ENPASSANT(c) (c == 'x') ? CP_BOARD_ENPASSANT : isupper(c) ? CP_BOARD_WHITE : CP_BOARD_BLACK
714 #define ATTRS(cp) (cp & (A_BOLD|A_STANDOUT|A_BLINK|A_DIM|A_UNDERLINE|A_INVIS|A_REVERSE))
716 static void
717 init_wchar_pieces ()
719 w_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♙" : "P");
720 w_rook_wchar = str_to_wchar (config.utf8_pieces ? "♖" : "R");
721 w_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♗" : "B");
722 w_knight_wchar = str_to_wchar (config.utf8_pieces ? "♘" : "N");
723 w_queen_wchar = str_to_wchar (config.utf8_pieces ? "♕" : "Q");
724 w_king_wchar = str_to_wchar (config.utf8_pieces ? "♔" : "K");
725 b_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♟" : "p");
726 b_rook_wchar = str_to_wchar (config.utf8_pieces ? "♜" : "r");
727 b_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♝" : "b");
728 b_knight_wchar = str_to_wchar (config.utf8_pieces ? "♞" : "n");
729 b_queen_wchar = str_to_wchar (config.utf8_pieces ? "♛" : "q");
730 b_king_wchar = str_to_wchar (config.utf8_pieces ? "♚" : "k");
731 empty_wchar = str_to_wchar (" ");
732 enpassant_wchar = str_to_wchar ("x");
735 static wchar_t *
736 piece_to_wchar (unsigned char p)
738 switch (p)
740 case 'P':
741 return w_pawn_wchar;
742 case 'p':
743 return b_pawn_wchar;
744 case 'R':
745 return w_rook_wchar;
746 case 'r':
747 return b_rook_wchar;
748 case 'B':
749 return w_bishop_wchar;
750 case 'b':
751 return b_bishop_wchar;
752 case 'N':
753 return w_knight_wchar;
754 case 'n':
755 return b_knight_wchar;
756 case 'Q':
757 return w_queen_wchar;
758 case 'q':
759 return b_queen_wchar;
760 case 'K':
761 return w_king_wchar;
762 case 'k':
763 return b_king_wchar;
764 case 'x':
765 return enpassant_wchar;
768 return empty_wchar;
771 static int
772 piece_can_attack (GAME g, int rank, int file)
774 struct userdata_s *d = g->data;
775 char *m, *frfr = NULL;
776 pgn_error_t e;
777 int row, col, p, v, pi, cpi;
779 if (d->rotate)
781 rotate_position (&d->c_row, &d->c_col);
782 rotate_position (&d->sp.srow, &d->sp.scol);
785 v = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].valid;
786 pi = pgn_piece_to_int (d->b[RANKTOBOARD (rank)][FILETOBOARD (file)].icon);
787 cpi = d->sp.icon
789 pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)]
790 [FILETOBOARD (d->sp.scol)].icon) :
791 pgn_piece_to_int (d->b[RANKTOBOARD (d->c_row)]
792 [FILETOBOARD (d->c_col)].icon);
794 if (pi == OPEN_SQUARE || cpi == OPEN_SQUARE || !VALIDFILE (file)
795 || !VALIDRANK (rank))
797 if (d->rotate)
799 rotate_position (&d->c_row, &d->c_col);
800 rotate_position (&d->sp.srow, &d->sp.scol);
803 return 0;
806 if (d->sp.icon)
808 col = v ? d->c_col : d->sp.scol;
809 row = v ? d->c_row : d->sp.srow;
811 else
813 col = d->c_col;
814 row = d->c_row;
817 m = malloc (MAX_SAN_MOVE_LEN + 1);
818 m[0] = INTTOFILE (file);
819 m[1] = INTTORANK (rank);
820 m[2] = INTTOFILE (col);
821 m[3] = INTTORANK (row);
822 m[4] = 0;
824 if (d->sp.icon && v)
826 BOARD b;
828 memcpy (b, d->b, sizeof (BOARD));
829 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
830 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
831 pgn_int_to_piece (WHITE, OPEN_SQUARE);
832 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
833 pgn_switch_turn (g);
834 e = pgn_validate_move (g, b, &m, &frfr);
835 pgn_switch_turn (g);
836 free (m);
837 free (frfr);
839 if (e != E_PGN_OK && pgn_piece_to_int (d->sp.icon) == PAWN)
841 int n = (d->sp.srow == 7 && rank == 5) ? 6 :
842 (d->sp.srow == 2 && rank == 4) ? 3 : 0;
844 if (n && (file == d->c_col - 1 || file == d->c_col + 1))
846 memcpy (b, d->b, sizeof (BOARD));
847 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
848 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
849 pgn_int_to_piece (WHITE, OPEN_SQUARE);
850 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
851 b[RANKTOBOARD (n)][FILETOBOARD (d->sp.scol)].enpassant = 1;
852 m = malloc (MAX_SAN_MOVE_LEN + 1);
853 m[0] = INTTOFILE (file);
854 m[1] = INTTORANK (rank);
855 m[2] = INTTOFILE (col);
856 m[3] = INTTORANK (n);
857 m[4] = 0;
858 pgn_switch_turn (g);
859 SET_FLAG (g->flags, GF_ENPASSANT);
860 e = pgn_validate_move (g, b, &m, &frfr);
861 CLEAR_FLAG (g->flags, GF_ENPASSANT);
862 pgn_switch_turn (g);
863 free (m);
864 free (frfr);
868 goto pca_quit;
871 pgn_switch_turn (g);
872 e = pgn_validate_move (g, d->b, &m, &frfr);
873 pgn_switch_turn (g);
875 if (!strcmp (m, "O-O") || !strcmp (m, "O-O-O"))
876 e = E_PGN_INVALID;
878 if (e == E_PGN_OK)
880 int sf = FILETOINT (frfr[0]), sr = RANKTOINT (frfr[1]);
881 int df = FILETOINT (frfr[2]);
883 pi = d->b[RANKTOBOARD (sr)][FILETOBOARD (sf)].icon;
884 pi = pgn_piece_to_int (pi);
885 if (pi == PAWN && sf == df)
886 e = E_PGN_INVALID;
889 free (m);
890 free (frfr);
892 pca_quit:
893 if (d->rotate)
895 rotate_position (&d->c_row, &d->c_col);
896 rotate_position (&d->sp.srow, &d->sp.scol);
899 return e == E_PGN_OK ? 1 : 0;
902 void
903 print_piece (WINDOW * w, int l, int c, char p)
905 int i, y, ff = 0;
907 for (i = 0; i < 13; i += 2)
909 if (p == piece_chars[i] || p == piece_chars[i + 1])
911 for (y = 0; y < 3; y++)
912 mvwprintw (w, l + y, c, "%s", f_pieces[i + ff + y]);
913 return;
916 ff++;
919 for (y = 0; y < 3; y++)
920 mvwprintw (w, l + y, c, f_pieces[0]);
923 void
924 board_prev_move_play (GAME g)
926 struct userdata_s *d = g->data;
927 char l = strlen (d->pm_frfr);
929 if (l)
931 char q = (l > 4) ? 2 : 1;
933 d->pm_row = RANKTOINT (d->pm_frfr[l - q++]);
934 d->pm_col = FILETOINT (d->pm_frfr[l - q++]);
935 d->ospm_row = RANKTOINT (d->pm_frfr[l - q++]);
936 d->ospm_col = FILETOINT (d->pm_frfr[l - q]);
937 if (d->rotate)
939 rotate_position (&d->pm_row, &d->pm_col);
940 rotate_position (&d->ospm_row, &d->ospm_col);
943 else
945 d->pm_row = 0;
946 d->pm_col = 0;
947 d->ospm_row = 0;
948 d->ospm_col = 0;
952 void
953 board_prev_move_history (GAME g)
955 struct userdata_s *d = g->data;
957 if (g->hindex)
959 char *move = g->hp[g->hindex - 1]->move;
961 if (move)
963 if (d->mode == MODE_PLAY)
964 coordofmove (g, move, &d->pm_row, &d->pm_col);
965 else
967 d->pm_row = 0;
968 d->pm_col = 0;
971 if (*move == 'O')
973 d->ospm_row = g->turn == WHITE ? 8 : 1;
974 d->ospm_col = 5;
975 return;
978 BOARD ob;
979 unsigned char f, r;
981 pgn_board_init (ob);
983 if (g->hindex > 1)
985 HISTORY *h = pgn_history_by_n (g->hp, g->hindex - 2);
986 if (h)
988 pgn_board_init_fen (g, ob, h->fen);
989 pgn_switch_turn (g);
993 for (f = 0; f < 8; f++)
995 for (r = 0; r < 8; r++)
997 if (ob[f][r].icon != '.' && d->b[f][r].icon == '.')
999 d->ospm_row = INV_INT0 (f) + 1;
1000 d->ospm_col = r + 1;
1001 break;
1006 if (d->rotate)
1008 if (d->mode == MODE_PLAY)
1009 rotate_position (&d->pm_row, &d->pm_col);
1011 rotate_position (&d->ospm_row, &d->ospm_col);
1014 return;
1017 else
1019 d->ospm_row = 0;
1020 d->ospm_col = 0;
1021 d->pm_row = 0;
1022 d->pm_col = 0;
1026 static int
1027 is_the_square (int brow, int bcol, int row, int col, int prow, int pcol)
1029 if ((!BIG_BOARD && row == ROWTOMATRIX (prow) && col == COLTOMATRIX (pcol))
1030 || (BIG_BOARD && brow + 1 == INV_INT (prow) && bcol + 1 == pcol))
1031 return 1;
1033 return 0;
1036 static int
1037 is_prev_move (struct userdata_s *d, int brow, int bcol, int row, int col)
1039 if (is_the_square (brow, bcol, row, col, d->pm_row, d->pm_col))
1040 return 1;
1042 return 0;
1045 void
1046 update_board_window (GAME g)
1048 int row, col;
1049 int bcol = 0, brow = 0;
1050 int l = config.coordsyleft;
1051 int maxy = BOARD_HEIGHT, maxx = BOARD_WIDTH;
1052 int ncols = 0, offset = 1;
1053 int rowr = (MEGA_BOARD) ? 6 : (BIG_BOARD) ? 4 : 2;
1054 int colr = (MEGA_BOARD) ? 12 : (BIG_BOARD) ? 8 : 4;
1055 unsigned coords_y = 8, cxgc = 0;
1056 unsigned i, cpd = 0;
1057 struct userdata_s *d = g->data;
1059 if (config.bprevmove && d->mode != MODE_EDIT)
1061 if (!d->pm_undo && d->mode == MODE_PLAY)
1062 board_prev_move_play (g);
1063 else
1064 board_prev_move_history (g);
1066 else
1068 d->pm_row = 0;
1069 d->pm_col = 0;
1070 d->ospm_row = 0;
1071 d->ospm_col = 0;
1074 if (d->mode != MODE_PLAY && d->mode != MODE_EDIT)
1075 update_cursor (g, g->hindex);
1077 if (BIG_BOARD)
1079 if (d->rotate)
1081 brow = 7;
1082 coords_y = 1;
1085 else
1087 if (d->rotate)
1089 brow = 1;
1090 coords_y = 1;
1092 else
1093 brow = 8;
1096 for (row = 0; row < maxy; row++)
1098 if (BIG_BOARD)
1100 if (d->rotate)
1101 bcol = 7;
1102 else
1103 bcol = 0;
1105 else
1107 if (d->rotate)
1108 bcol = 8;
1109 else
1110 bcol = 1;
1113 for (col = 0; col < maxx; col++)
1115 int attrwhich = -1;
1116 chtype attrs = 0, old_attrs = 0;
1117 unsigned char p;
1118 int can_attack = 0;
1119 int valid = 0;
1121 if (row == 0 || row == maxy - 2)
1123 if (col == 0)
1124 mvwaddch (boardw, row, col + l,
1125 LINE_GRAPHIC ((row)
1126 ? ACS_LLCORNER | CP_BOARD_GRAPHICS
1127 : ACS_ULCORNER | CP_BOARD_GRAPHICS));
1128 else if (col == maxx - 2)
1129 mvwaddch (boardw, row, col + l,
1130 LINE_GRAPHIC ((row)
1131 ? ACS_LRCORNER | CP_BOARD_GRAPHICS
1132 : ACS_URCORNER | CP_BOARD_GRAPHICS));
1133 else if (!(col % colr))
1134 mvwaddch (boardw, row, col + l,
1135 LINE_GRAPHIC ((row)
1136 ? ACS_BTEE | CP_BOARD_GRAPHICS
1137 : ACS_TTEE | CP_BOARD_GRAPHICS));
1138 else
1140 if (col != maxx - 1)
1141 mvwaddch (boardw, row, col + l,
1142 LINE_GRAPHIC (ACS_HLINE | CP_BOARD_GRAPHICS));
1145 continue;
1148 if ((row % 2) && col == maxx - 1 && (coords_y > 0 && coords_y < 9))
1150 wattron (boardw, CP_BOARD_COORDS);
1151 mvwprintw (boardw,
1152 (BIG_BOARD) ? row * ((MEGA_BOARD) ? 3 : 2)
1153 : row, (l) ? 0 : col, "%d",
1154 (d->rotate) ? coords_y++ : coords_y--);
1155 wattroff (boardw, CP_BOARD_COORDS);
1156 continue;
1159 if ((col == 0 || col == maxx - 2) && row != maxy - 1)
1161 if (!(row % rowr))
1162 mvwaddch (boardw, row, col + l,
1163 LINE_GRAPHIC ((col) ?
1164 ACS_RTEE | CP_BOARD_GRAPHICS :
1165 ACS_LTEE | CP_BOARD_GRAPHICS));
1166 else
1167 mvwaddch (boardw, row, col + l,
1168 LINE_GRAPHIC (ACS_VLINE | CP_BOARD_GRAPHICS));
1170 continue;
1173 if ((row % rowr) && !(col % colr) && row != maxy - 1)
1175 mvwaddch (boardw, row, col + l,
1176 LINE_GRAPHIC (ACS_VLINE | CP_BOARD_GRAPHICS));
1177 continue;
1180 if (!(col % colr) && row != maxy - 1)
1182 mvwaddch (boardw, row, col + l,
1183 LINE_GRAPHIC (ACS_PLUS | CP_BOARD_GRAPHICS));
1184 continue;
1187 if ((row % rowr))
1189 if ((col % colr))
1191 if (BIG_BOARD)
1192 attrwhich = (cb[brow][bcol]) ? WHITE : BLACK;
1193 else
1195 if (ncols++ == 8)
1197 offset++;
1198 ncols = 1;
1201 if (((ncols % 2) && !(offset % 2))
1202 || (!(ncols % 2) && (offset % 2)))
1203 attrwhich = BLACK;
1204 else
1205 attrwhich = WHITE;
1208 if (BIG_BOARD && d->rotate)
1210 brow = INV_INT0 (brow);
1211 bcol = INV_INT0 (bcol);
1214 if (BIG_BOARD)
1215 p = d->b[brow][bcol].icon;
1216 else
1217 p = d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].icon;
1219 int pi = pgn_piece_to_int (p);
1221 if (config.details &&
1222 ((!BIG_BOARD
1223 && d->
1224 b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].enpassant)
1225 || (BIG_BOARD && d->b[brow][bcol].enpassant)))
1227 p = pi = 'x';
1228 attrs = mix_cp (CP_BOARD_ENPASSANT,
1229 (attrwhich ==
1230 WHITE) ? CP_BOARD_WHITE :
1231 CP_BOARD_BLACK,
1232 ATTRS (CP_BOARD_ENPASSANT), A_FG_B_BG);
1235 if (config.showattacks && config.details
1236 && piece_can_attack (g,
1237 BIG_BOARD ? INV_INT0 (brow) +
1238 1 : brow,
1239 BIG_BOARD ? bcol + 1 : bcol))
1241 attrs = CP_BOARD_ATTACK;
1242 old_attrs = attrs;
1243 can_attack = 1;
1246 if (config.validmoves &&
1247 ((!BIG_BOARD
1248 && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].valid)
1249 || (BIG_BOARD && d->b[brow][bcol].valid)))
1251 old_attrs = -1;
1252 valid = 1;
1254 if (attrwhich == WHITE)
1255 attrs = mix_cp (CP_BOARD_MOVES_WHITE,
1256 IS_ENPASSANT (p),
1257 ATTRS (CP_BOARD_MOVES_WHITE),
1258 B_FG_A_BG);
1259 else
1260 attrs = mix_cp (CP_BOARD_MOVES_BLACK,
1261 IS_ENPASSANT (p),
1262 ATTRS (CP_BOARD_MOVES_BLACK),
1263 B_FG_A_BG);
1265 else if (p != 'x' && !can_attack)
1266 attrs =
1267 (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK;
1269 if (BIG_BOARD && d->rotate)
1271 brow = INV_INT0 (brow);
1272 bcol = INV_INT0 (bcol);
1275 if (is_the_square (brow, bcol, row, col, d->c_row,
1276 d->c_col))
1278 attrs = mix_cp (CP_BOARD_CURSOR, IS_ENPASSANT (p),
1279 ATTRS (CP_BOARD_CURSOR), B_FG_A_BG);
1280 old_attrs = -1;
1282 else if (is_the_square (brow, bcol, row, col,
1283 d->sp.srow, d->sp.scol))
1285 attrs = mix_cp (CP_BOARD_SELECTED, IS_ENPASSANT (p),
1286 ATTRS (CP_BOARD_SELECTED), B_FG_A_BG);
1287 old_attrs = -1;
1289 else if ((is_prev_move (d, brow, bcol, row, col) && !valid)
1290 || (is_the_square (brow, bcol, row, col,
1291 d->ospm_row, d->ospm_col)))
1293 attrs = mix_cp (CP_BOARD_PREVMOVE, IS_ENPASSANT (p),
1294 ATTRS (CP_BOARD_PREVMOVE), B_FG_A_BG);
1295 old_attrs = -1;
1298 if (row == maxy - 1)
1299 attrs = 0;
1301 if (can_attack)
1303 int n = is_prev_move (d, brow, bcol, row, col);
1304 chtype a = n && !valid
1305 ? CP_BOARD_PREVMOVE : attrwhich ==
1306 WHITE ? valid ? CP_BOARD_MOVES_WHITE : CP_BOARD_WHITE
1307 : valid ? CP_BOARD_MOVES_BLACK : CP_BOARD_BLACK;
1308 attrs =
1309 mix_cp (CP_BOARD_ATTACK, a, ATTRS (CP_BOARD_ATTACK),
1310 A_FG_B_BG);
1311 old_attrs = -1;
1314 if (BIG_BOARD)
1315 wmove (boardw, row, col + ((MEGA_BOARD) ? 5 : 3) + l);
1316 else
1317 mvwaddch (boardw, row, col + l, ' ' | attrs);
1319 if (row == maxy - 1 && cxgc < 8)
1321 waddch (boardw,
1322 "abcdefgh"[(BIG_BOARD) ? bcol : bcol -
1323 1] | CP_BOARD_COORDS);
1324 cxgc++;
1326 else
1328 if (old_attrs == -1)
1330 old_attrs = attrs;
1331 goto printc;
1334 old_attrs = attrs;
1336 if (pi != OPEN_SQUARE && p != 'x' && !can_attack)
1338 if (attrwhich == WHITE)
1340 if (isupper (p))
1341 attrs = CP_BOARD_W_W;
1342 else
1343 attrs = CP_BOARD_W_B;
1345 else
1347 if (isupper (p))
1348 attrs = CP_BOARD_B_W;
1349 else
1350 attrs = CP_BOARD_B_B;
1354 printc:
1355 if (BIG_BOARD)
1357 if (config.details && !can_attack
1358 && castling_state (g, d->b,
1359 (d->rotate) ? INV_INT0 (brow + 1) - 1 : brow,
1360 (d->rotate) ? INV_INT0 (bcol + 1) - 1 : bcol,
1361 p, 0))
1362 attrs = mix_cp (CP_BOARD_CASTLING, attrs,
1363 ATTRS (CP_BOARD_CASTLING), A_FG_B_BG);
1365 else
1367 if (config.details && !can_attack
1368 && castling_state (g, d->b, RANKTOBOARD (brow),
1369 FILETOBOARD (bcol), p, 0))
1371 attrs = mix_cp (CP_BOARD_CASTLING, attrs,
1372 ATTRS (CP_BOARD_CASTLING),
1373 A_FG_B_BG);
1377 if (BIG_BOARD)
1379 // FIXME: Reimpresión de piezas(+4).
1380 if (cpd < 67)
1382 wattron (boardw, attrs);
1383 if (MEGA_BOARD)
1385 for (i = 0; i < 5; i++)
1386 mvwprintw (boardw, i + brow * 6 + 1,
1387 bcol * 12 + 1 + l,
1388 " ");
1389 if (pi != OPEN_SQUARE)
1390 print_piece (boardw, brow * 6 + 2,
1391 bcol * 12 + 3 + l, p);
1393 else
1395 print_piece (boardw, brow * 4 + 1,
1396 bcol * 8 + 1 + l,
1397 (pi != OPEN_SQUARE) ? p : 0);
1400 wattroff (boardw, attrs);
1401 cpd++;
1404 else
1406 wattron (boardw, attrs);
1407 waddwstr (boardw,
1408 piece_to_wchar (pi !=
1409 OPEN_SQUARE ? p : 0));
1410 wattroff (boardw, attrs);
1413 attrs = old_attrs;
1416 if (BIG_BOARD)
1417 col += (MEGA_BOARD) ? 10 : 6;
1418 else
1420 waddch (boardw, ' ' | attrs);
1421 col += 2;
1424 if (d->rotate)
1425 bcol--;
1426 else
1427 bcol++;
1429 if (BIG_BOARD)
1431 if (bcol > 7)
1432 bcol = 0;
1433 if (bcol < 0)
1434 bcol = 7;
1438 else
1440 if (col != maxx - 1)
1441 mvwaddch (boardw, row, col + l,
1442 LINE_GRAPHIC (ACS_HLINE | CP_BOARD_GRAPHICS));
1446 if (row % rowr)
1448 if (d->rotate)
1449 brow++;
1450 else
1451 brow--;
1454 if (BIG_BOARD)
1456 if (brow > 7)
1457 brow = 0;
1458 if (brow < 0)
1459 brow = 7;
1464 void
1465 invalid_move (int n, int e, const char *m)
1467 if (curses_initialized)
1468 cmessage (ERROR_STR, ANY_KEY_STR, "%s \"%s\" (round #%i)",
1469 (e ==
1470 E_PGN_AMBIGUOUS) ? _("Ambiguous move") : _("Invalid move"), m,
1472 else
1473 warnx ("%s: %s \"%s\" (round #%i)", loadfile, (e == E_PGN_AMBIGUOUS)
1474 ? _("Ambiguous move") : _("Invalid move"), m, n);
1477 void
1478 gameover (GAME g)
1480 struct userdata_s *d = g->data;
1482 SET_FLAG (g->flags, GF_GAMEOVER);
1483 d->mode = MODE_HISTORY;
1484 stop_engine (g);
1487 static void
1488 update_clock (GAME g, struct itimerval it)
1490 struct userdata_s *d = g->data;
1492 if (TEST_FLAG (d->flags, CF_CLOCK) && g->turn == WHITE)
1494 d->wclock.elapsed.tv_sec += it.it_value.tv_sec;
1495 d->wclock.elapsed.tv_usec += it.it_value.tv_usec;
1497 if (d->wclock.elapsed.tv_usec > 1000000 - 1)
1499 d->wclock.elapsed.tv_sec += d->wclock.elapsed.tv_usec / 1000000;
1500 d->wclock.elapsed.tv_usec = d->wclock.elapsed.tv_usec % 1000000;
1503 if (d->wclock.tc[d->wclock.tcn][1] &&
1504 d->wclock.elapsed.tv_sec >= d->wclock.tc[d->wclock.tcn][1])
1506 pgn_tag_add (&g->tag, (char *) "Result", (char *) "0-1");
1507 gameover (g);
1510 else if (TEST_FLAG (d->flags, CF_CLOCK) && g->turn == BLACK)
1512 d->bclock.elapsed.tv_sec += it.it_value.tv_sec;
1513 d->bclock.elapsed.tv_usec += it.it_value.tv_usec;
1515 if (d->bclock.elapsed.tv_usec > 1000000 - 1)
1517 d->bclock.elapsed.tv_sec += d->bclock.elapsed.tv_usec / 1000000;
1518 d->bclock.elapsed.tv_usec = d->bclock.elapsed.tv_usec % 1000000;
1521 if (d->bclock.tc[d->bclock.tcn][1] &&
1522 d->bclock.elapsed.tv_sec >= d->bclock.tc[d->bclock.tcn][1])
1524 pgn_tag_add (&g->tag, (char *) "Result", (char *) "1-0");
1525 gameover (g);
1529 d->elapsed.tv_sec += it.it_value.tv_sec;
1530 d->elapsed.tv_usec += it.it_value.tv_usec;
1532 if (d->elapsed.tv_usec > 1000000 - 1)
1534 d->elapsed.tv_sec += d->elapsed.tv_usec / 1000000;
1535 d->elapsed.tv_usec = d->elapsed.tv_usec % 1000000;
1539 static void
1540 update_time_control (GAME g)
1542 struct userdata_s *d = g->data;
1543 struct clock_s *clk = (g->turn == WHITE) ? &d->wclock : &d->bclock;
1545 if (clk->incr)
1546 clk->tc[clk->tcn][1] += clk->incr;
1548 if (!clk->tc[clk->tcn][1])
1549 return;
1551 clk->move++;
1553 if (!clk->tc[clk->tcn][0] || clk->move >= clk->tc[clk->tcn][0])
1555 clk->move = 0;
1556 clk->tc[clk->tcn + 1][1] +=
1557 labs (clk->elapsed.tv_sec - clk->tc[clk->tcn][1]);
1558 memset (&clk->elapsed, 0, sizeof (clk->elapsed));
1559 clk->tcn++;
1563 void
1564 update_history_window (GAME g)
1566 char buf[HISTORY_WIDTH - 1];
1567 HISTORY *h = NULL;
1568 int n, total;
1569 int t = pgn_history_total (g->hp);
1571 n = (g->hindex + 1) / 2;
1573 if (t % 2)
1574 total = (t + 1) / 2;
1575 else
1576 total = t / 2;
1578 if (t)
1579 snprintf (buf, sizeof (buf), "%u %s %u%s", n, _("of"), total,
1580 (movestep == 1) ? _(" (ply)") : "");
1581 else
1582 strncpy (buf, _("not available"), sizeof (buf) - 1);
1584 buf[sizeof (buf) - 1] = 0;
1585 mvwprintw (historyw, 2, 1, "%*s %-*s", 10, _("Move:"),
1586 HISTORY_WIDTH - 14, buf);
1588 h = pgn_history_by_n (g->hp, g->hindex);
1589 snprintf (buf, sizeof (buf), "%s",
1590 (h && h->move) ? h->move
1591 : (LINES < 24) ? _("empty") : _("not available"));
1592 n = 0;
1594 if (h && ((h->comment) || h->nag[0]))
1596 strncat (buf, _(" (Annotated"), sizeof (buf) - 1);
1597 n++;
1600 if (h && h->rav)
1602 strncat (buf, (n) ? ",+" : " (+", sizeof (buf) - 1);
1603 n++;
1606 if (g->ravlevel)
1608 strncat (buf, (n) ? ",-" : " (-", sizeof (buf) - 1);
1609 n++;
1612 if (n)
1613 strncat (buf, ")", sizeof (buf) - 1);
1615 mvwprintw (historyw, 3, ((LINES < 24) ? 17 : 1), "%s %-*s",
1616 (LINES < 24) ? _("Next:") : _("Next move:"),
1617 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1619 h = pgn_history_by_n (g->hp, g->hindex - 1);
1620 snprintf (buf, sizeof (buf), "%s",
1621 (h && h->move) ? h->move
1622 : (LINES < 24) ? _("empty") : _("not available"));
1623 n = 0;
1625 if (h && ((h->comment) || h->nag[0]))
1627 strncat (buf, _(" (Annotated"), sizeof (buf) - 1);
1628 n++;
1631 if (h && h->rav)
1633 strncat (buf, (n) ? ",+" : " (+", sizeof (buf) - 1);
1634 n++;
1637 if (g->ravlevel)
1639 strncat (buf, (n) ? ",-" : " (-", sizeof (buf) - 1);
1640 n++;
1643 if (n)
1644 strncat (buf, ")", sizeof (buf) - 1);
1646 mvwprintw (historyw, ((LINES < 24) ? 3 : 4), 1, "%s %-*s",
1647 (LINES < 24) ? _("Prev.:") : _("Prev move:"),
1648 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1651 void
1652 do_validate_move (char **move)
1654 struct userdata_s *d = gp->data;
1655 int n;
1656 char *frfr = NULL;
1658 if (TEST_FLAG (d->flags, CF_HUMAN))
1660 if ((n = pgn_parse_move (gp, d->b, move, &frfr)) != E_PGN_OK)
1662 invalid_move (d->n + 1, n, *move);
1663 return;
1666 strcpy (d->pm_frfr, frfr);
1667 update_time_control (gp);
1668 pgn_history_add (gp, d->b, *move);
1669 pgn_switch_turn (gp);
1671 else
1673 if ((n = pgn_validate_move (gp, d->b, move, &frfr)) != E_PGN_OK)
1675 invalid_move (d->n + 1, n, *move);
1676 return;
1679 add_engine_command (gp, ENGINE_THINKING, "%s\n",
1680 (config.engine_protocol == 1) ? frfr : *move);
1683 d->sp.srow = d->sp.scol = d->sp.icon = 0;
1685 if (config.validmoves)
1686 pgn_reset_valid_moves (d->b);
1688 if (TEST_FLAG (gp->flags, GF_GAMEOVER))
1689 d->mode = MODE_HISTORY;
1690 else
1691 SET_FLAG (d->flags, CF_MODIFIED);
1693 free (frfr);
1694 d->paused = 0;
1695 update_history_window (gp);
1696 update_board_window (gp);
1697 return;
1700 void
1701 do_promotion_piece_finalize (WIN * win)
1703 char *p, *str = win->data;
1705 if (pgn_piece_to_int (win->c) == -1)
1706 return;
1708 p = str + strlen (str);
1709 *p++ = toupper (win->c);
1710 *p = '\0';
1711 do_validate_move (&str);
1712 free (str);
1713 win->data = NULL;
1716 static void
1717 move_to_engine (GAME g)
1719 struct userdata_s *d = g->data;
1720 char *str;
1721 int piece;
1723 if (config.validmoves &&
1724 !d->b[RANKTOBOARD (d->sp.row)][FILETOBOARD (d->sp.col)].valid)
1725 return;
1727 str = Malloc (MAX_SAN_MOVE_LEN + 1);
1728 snprintf (str, MAX_SAN_MOVE_LEN + 1, "%c%u%c%u",
1729 _("abcdefgh")[d->sp.scol - 1],
1730 d->sp.srow, _("abcdefgh")[d->sp.col - 1], d->sp.row);
1732 piece =
1733 pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)]
1734 [FILETOBOARD (d->sp.scol)].icon);
1736 if (piece == PAWN && (d->sp.row == 8 || d->sp.row == 1))
1738 construct_message (_("Select Pawn Promotion Piece"), _("R/N/B/Q"), 1, 1,
1739 NULL, NULL, str, do_promotion_piece_finalize, 0, 0,
1740 NULL, "%s",
1741 _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1742 return;
1745 do_validate_move (&str);
1746 free (str);
1749 static char *
1750 clock_to_char (long n)
1752 static char buf[16];
1753 int h = 0, m = 0, s = 0;
1755 h = n / 3600;
1756 m = (n % 3600) / 60;
1757 s = (n % 3600) % 60;
1758 snprintf (buf, sizeof (buf), "%.2i:%.2i:%.2i", h, m, s);
1759 return buf;
1762 static char *
1763 timeval_to_char (struct timeval t, long limit)
1765 static char buf[9];
1766 unsigned h = 0, m = 0, s = 0;
1767 int n = limit ? labs (limit - t.tv_sec) : 0;
1769 h = n / 3600;
1770 m = (n % 3600) / 60;
1771 s = (n % 3600) % 60;
1772 snprintf (buf, sizeof (buf), "%.2u:%.2u:%.2u", h, m, s);
1773 return buf;
1776 static char *
1777 time_control_status (struct clock_s *clk)
1779 static char buf[80] = { 0 };
1781 buf[0] = 0;
1783 if (clk->tc[clk->tcn][0] && clk->tc[clk->tcn + 1][1])
1784 snprintf (buf, sizeof (buf), " M%.2i/%s",
1785 abs (clk->tc[clk->tcn][0] - clk->move),
1786 clock_to_char (clk->tc[clk->tcn + 1][1]));
1787 else if (!clk->incr)
1788 return (char *) "";
1790 if (clk->incr)
1792 char tbuf[16];
1794 strncat (tbuf, " I", sizeof (tbuf) - 1);
1795 strncat (tbuf, itoa (clk->incr, buf), sizeof (tbuf) - 1);
1798 return buf;
1801 void
1802 update_status_window (GAME g)
1804 int i = 0;
1805 char *buf;
1806 char tmp[15] = { 0 }, *engine, *mode;
1807 char t[COLS];
1808 int w;
1809 char *p;
1810 int maxy, maxx;
1811 int len;
1812 struct userdata_s *d = g->data;
1813 int y;
1814 int n;
1816 if (!curses_initialized)
1817 return;
1819 getmaxyx (statusw, maxy, maxx);
1820 (void) maxy;
1821 w = maxx - 2 - 8;
1822 len = maxx - 2;
1823 buf = Malloc (len);
1824 y = 2;
1826 wchar_t *loadfilew = loadfile[0]
1827 ? str_etc (loadfile, w, 1) : str_to_wchar (_("not available"));
1828 mvwprintw (statusw, y++, 1, "%*s %-*ls", 7, _("File:"), w, loadfilew);
1829 free (loadfilew);
1830 snprintf (buf, len, "%i %s %i", gindex + 1, _("of"), gtotal);
1831 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Game:"), w, buf);
1833 *tmp = '\0';
1834 p = tmp;
1836 if (config.details)
1838 *p++ = 'D';
1839 i++;
1842 if (TEST_FLAG (d->flags, CF_DELETE))
1844 if (i)
1845 *p++ = '/';
1847 *p++ = 'X';
1848 i++;
1851 if (TEST_FLAG (g->flags, GF_PERROR))
1853 if (i)
1854 *p++ = '/';
1856 *p++ = '!';
1857 i++;
1860 if (TEST_FLAG (d->flags, CF_MODIFIED))
1862 if (i)
1863 *p++ = '/';
1865 *p++ = '*';
1866 i++;
1869 pgn_config_get (PGN_STRICT_CASTLING, &n);
1871 if (n == 1)
1873 if (i)
1874 *p++ = '/';
1876 *p++ = 'C';
1877 i++;
1879 #ifdef WITH_LIBPERL
1880 if (TEST_FLAG (d->flags, CF_PERL))
1882 if (i)
1883 *p++ = '/';
1885 *p++ = 'P';
1886 i++;
1888 #endif
1890 *p = '\0';
1891 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Flags:"), w,
1892 (tmp[0]) ? tmp : "-");
1894 switch (d->mode)
1896 case MODE_HISTORY:
1897 mode = _("move history");
1898 break;
1899 case MODE_EDIT:
1900 mode = _("edit");
1901 break;
1902 case MODE_PLAY:
1903 mode = _("play");
1904 break;
1905 default:
1906 mode = _("(empty value)");
1907 break;
1910 snprintf (buf, len - 1, "%*s %s", 7, _("Mode:"), mode);
1912 if (d->mode == MODE_PLAY)
1914 if (TEST_FLAG (d->flags, CF_HUMAN))
1915 strncat (buf, _(" (human/human)"), len - 1);
1916 else if (TEST_FLAG (d->flags, CF_ENGINE_LOOP))
1917 strncat (buf, _(" (engine/engine)"), len - 1);
1918 else
1919 strncat (buf, (d->play_mode == PLAY_EH) ?
1920 _(" (engine/human)") : _(" (human/engine)"), len - 1);
1923 buf[len - 1] = 0;
1924 mvwprintw (statusw, y++, 1, "%-*s", len, buf);
1925 free (buf);
1927 if (d->engine)
1929 switch (d->engine->status)
1931 case ENGINE_THINKING:
1932 engine = _("pondering...");
1933 break;
1934 case ENGINE_READY:
1935 engine = _("ready");
1936 break;
1937 case ENGINE_INITIALIZING:
1938 engine = _("initializing...");
1939 break;
1940 case ENGINE_OFFLINE:
1941 engine = _("offline");
1942 break;
1943 default:
1944 engine = _("(empty value)");
1945 break;
1948 else
1949 engine = _("offline");
1951 mvwprintw (statusw, y, 1, "%*s %-*s", 7, _("Engine:"), w, " ");
1952 wattron (statusw, CP_STATUS_ENGINE);
1953 mvwaddstr (statusw, y++, 9, engine);
1954 wattroff (statusw, CP_STATUS_ENGINE);
1956 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Turn:"), w,
1957 (g->turn == WHITE) ? _("white") : _("black"));
1959 strncpy (tmp, _("white"), sizeof (tmp) - 1);
1960 tmp[0] = toupper (tmp[0]);
1961 snprintf (t, sizeof (t), "%s%s",
1962 timeval_to_char (d->wclock.elapsed,
1963 d->wclock.tc[d->wclock.tcn][1]),
1964 time_control_status (&d->wclock));
1965 mvwprintw (statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1967 strncpy (tmp, _("black"), sizeof (tmp) - 1);
1968 tmp[0] = toupper (tmp[0]);
1969 snprintf (t, sizeof (t), "%s%s",
1970 timeval_to_char (d->bclock.elapsed,
1971 d->bclock.tc[d->bclock.tcn][1]),
1972 time_control_status (&d->bclock));
1973 mvwprintw (statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1975 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Total:"), w,
1976 clock_to_char (d->elapsed.tv_sec));
1978 // for (i = 0; i < STATUS_WIDTH; i++)
1979 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1981 if (!status.notify)
1982 status.notify = str_to_wchar (_("Type F1 for help"));
1984 wattron (stdscr, CP_STATUS_NOTIFY);
1985 for (i = (config.boardleft) ? BOARD_WIDTH : 0;
1986 i < ((config.boardleft) ? COLS : STATUS_WIDTH); i++)
1987 mvwprintw (stdscr, STATUS_HEIGHT, i, " ");
1988 mvwprintw (stdscr, STATUS_HEIGHT, CENTERX (STATUS_WIDTH, status.notify)
1989 + ((config.boardleft) ? BOARD_WIDTH : 0), "%ls", status.notify);
1990 wattroff (stdscr, CP_STATUS_NOTIFY);
1993 wchar_t *
1994 translate_tag_name (const char *tag)
1996 if (!strcmp (tag, "Event"))
1997 return str_to_wchar (translatable_tag_names[0]);
1998 else if (!strcmp (tag, "Site"))
1999 return str_to_wchar (translatable_tag_names[1]);
2000 else if (!strcmp (tag, "Date"))
2001 return str_to_wchar (translatable_tag_names[2]);
2002 else if (!strcmp (tag, "Round"))
2003 return str_to_wchar (translatable_tag_names[3]);
2004 else if (!strcmp (tag, "White"))
2005 return str_to_wchar (translatable_tag_names[4]);
2006 else if (!strcmp (tag, "Black"))
2007 return str_to_wchar (translatable_tag_names[5]);
2008 else if (!strcmp (tag, "Result"))
2009 return str_to_wchar (translatable_tag_names[6]);
2011 return str_to_wchar (tag);
2014 void
2015 update_tag_window (TAG ** t)
2017 int i, l, w;
2018 int namel = 0;
2020 for (i = 0; t[i]; i++)
2022 wchar_t *namewc = translate_tag_name (t[i]->name);
2024 l = wcslen (namewc);
2025 free (namewc);
2026 if (l > namel)
2027 namel = l;
2030 w = TAG_WIDTH - namel - 4;
2032 for (i = 0; t[i] && i < TAG_HEIGHT - 3; i++)
2034 wchar_t *namewc = translate_tag_name (t[i]->name);
2035 wchar_t *valuewc = str_etc (t[i]->value, w, 0);
2037 mvwprintw (tagw, (i + 2), 1, "%*ls: %-*ls", namel, namewc, w, valuewc);
2038 free (namewc);
2039 free (valuewc);
2042 for (; i < TAG_HEIGHT - 3; i++)
2043 mvwprintw (tagw, (i + 2), 1, "%*s", namel + w + 2, " ");
2046 void
2047 append_enginebuf (GAME g, char *line)
2049 int i = 0;
2050 struct userdata_s *d = g->data;
2052 if (d->engine->enginebuf)
2053 for (i = 0; d->engine->enginebuf[i]; i++);
2055 if (i >= LINES - 3)
2057 free (d->engine->enginebuf[0]);
2059 for (i = 0; d->engine->enginebuf[i + 1]; i++)
2060 d->engine->enginebuf[i] = d->engine->enginebuf[i + 1];
2062 d->engine->enginebuf[i] = strdup (line);
2064 else
2066 d->engine->enginebuf =
2067 Realloc (d->engine->enginebuf, (i + 2) * sizeof (char *));
2068 d->engine->enginebuf[i++] = strdup (line);
2069 d->engine->enginebuf[i] = NULL;
2073 void
2074 update_engine_window (GAME g)
2076 int i;
2077 struct userdata_s *d = g->data;
2079 wmove (enginew, 0, 0);
2080 wclrtobot (enginew);
2082 if (d->engine && d->engine->enginebuf)
2084 for (i = 0; d->engine->enginebuf[i]; i++)
2085 mvwprintw (enginew, i + 2, 1, "%s", d->engine->enginebuf[i]);
2088 window_draw_title (enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
2089 CP_MESSAGE_BORDER);
2092 void
2093 update_all (GAME g)
2095 struct userdata_s *d = g->data;
2098 * In the middle of a macro. Don't update the screen.
2100 if (macro_match != -1)
2101 return;
2103 wmove (boardw, ROWTOMATRIX (d->c_row), COLTOMATRIX (d->c_col));
2104 update_board_window (g);
2105 update_status_window (g);
2106 update_history_window (g);
2107 update_tag_window (g->tag);
2108 update_engine_window (g);
2109 update_panels ();
2110 doupdate ();
2113 static void
2114 game_next_prev (GAME g, int n, int count)
2116 if (gtotal < 2)
2117 return;
2119 if (n == 1)
2121 if (gindex + count > gtotal - 1)
2123 if (count != 1)
2124 gindex = gtotal - 1;
2125 else
2126 gindex = 0;
2128 else
2129 gindex += count;
2131 else
2133 if (gindex - count < 0)
2135 if (count != 1)
2136 gindex = 0;
2137 else
2138 gindex = gtotal - 1;
2140 else
2141 gindex -= count;
2144 gp = game[gindex];
2147 static void
2148 delete_game (int which)
2150 int i, w = which;
2151 struct userdata_s *d;
2153 for (i = 0; i < gtotal; i++)
2155 d = game[i]->data;
2157 if (i == w || TEST_FLAG (d->flags, CF_DELETE))
2159 int n;
2161 free_userdata_once (game[i]);
2162 pgn_free (game[i]);
2164 for (n = i; n + 1 < gtotal; n++)
2165 game[n] = game[n + 1];
2167 gtotal--;
2168 i--;
2169 w = -1;
2173 if (which != -1)
2175 if (which + 1 >= gtotal)
2176 gindex = gtotal - 1;
2177 else
2178 gindex = which;
2180 else
2181 gindex = gtotal - 1;
2183 gp = game[gindex];
2184 gp->hp = gp->history;
2188 * FIXME find across multiple games.
2190 static int
2191 find_move_exp (GAME g, regex_t r, int which, int count)
2193 int i;
2194 int ret;
2195 char errbuf[255];
2196 int incr;
2197 int found;
2199 incr = (which == 0) ? -1 : 1;
2201 for (i = g->hindex + incr - 1, found = 0;; i += incr)
2203 if (i == g->hindex - 1)
2204 break;
2206 if (i >= pgn_history_total (g->hp))
2207 i = 0;
2208 else if (i < 0)
2209 i = pgn_history_total (g->hp) - 1;
2211 // FIXME RAV
2212 ret = regexec (&r, g->hp[i]->move, 0, 0, 0);
2214 if (ret == 0)
2216 if (count == ++found)
2218 return i + 1;
2221 else
2223 if (ret != REG_NOMATCH)
2225 regerror (ret, &r, errbuf, sizeof (errbuf));
2226 cmessage (_("Error Matching Regular Expression"), ANY_KEY_STR,
2227 "%s", errbuf);
2228 return -1;
2233 return -1;
2236 static int
2237 toggle_delete_flag (int n)
2239 int i, x;
2240 struct userdata_s *d = game[n]->data;
2242 TOGGLE_FLAG (d->flags, CF_DELETE);
2243 gindex = n;
2245 for (i = x = 0; i < gtotal; i++)
2247 d = game[i]->data;
2249 if (TEST_FLAG (d->flags, CF_DELETE))
2250 x++;
2253 if (x == gtotal)
2255 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
2256 d = game[n]->data;
2257 CLEAR_FLAG (d->flags, CF_DELETE);
2258 return 1;
2261 return 0;
2264 static int
2265 find_game_exp (char *str, int which, int count)
2267 char *nstr = NULL, *exp = NULL;
2268 regex_t nexp, vexp;
2269 int ret = -1;
2270 int g = 0;
2271 char buf[255] = { 0 }, *tmp;
2272 char errbuf[255];
2273 int found = 0;
2274 int incr = (which == 0) ? -(1) : 1;
2276 strncpy (buf, str, sizeof (buf) - 1);
2277 tmp = buf;
2279 if (strstr (tmp, ":") != NULL)
2281 nstr = strsep (&tmp, ":");
2283 if ((ret = regcomp (&nexp, nstr,
2284 REG_ICASE | REG_EXTENDED | REG_NOSUB)) != 0)
2286 regerror (ret, &nexp, errbuf, sizeof (errbuf));
2287 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR,
2288 "%s", errbuf);
2289 ret = g = -1;
2290 goto cleanup;
2294 exp = tmp;
2296 while (exp && *exp && isspace (*exp))
2297 exp++;
2299 if (exp == NULL)
2300 goto cleanup;
2302 if ((ret = regcomp (&vexp, exp, REG_EXTENDED | REG_NOSUB)) != 0)
2304 regerror (ret, &vexp, errbuf, sizeof (errbuf));
2305 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s",
2306 errbuf);
2307 ret = -1;
2308 goto cleanup;
2311 ret = -1;
2313 for (g = gindex + incr, found = 0;; g += incr)
2315 int t;
2317 if (g == gtotal)
2318 g = 0;
2319 else if (g < 0)
2320 g = gtotal - 1;
2322 if (g == gindex)
2323 break;
2325 for (t = 0; game[g]->tag[t]; t++)
2327 if (nstr)
2329 if (regexec (&nexp, game[g]->tag[t]->name, 0, 0, 0) == 0)
2331 if (regexec (&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0)
2333 if (count == ++found)
2335 ret = g;
2336 goto cleanup;
2341 else
2343 if (regexec (&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0)
2345 if (count == ++found)
2347 ret = g;
2348 goto cleanup;
2354 ret = -1;
2357 cleanup:
2358 if (nstr)
2359 regfree (&nexp);
2361 if (g != -1)
2362 regfree (&vexp);
2364 return ret;
2368 * Updates the notification line in the status window then refreshes the
2369 * status window.
2371 void
2372 update_status_notify (GAME g, const char *fmt, ...)
2374 va_list ap;
2375 #ifdef HAVE_VASPRINTF
2376 char *line;
2377 #else
2378 char line[COLS];
2379 #endif
2381 free (status.notify);
2382 status.notify = NULL;
2384 if (!fmt)
2385 return;
2387 va_start (ap, fmt);
2388 #ifdef HAVE_VASPRINTF
2389 vasprintf (&line, fmt, ap);
2390 #else
2391 vsnprintf (line, sizeof (line), fmt, ap);
2392 #endif
2393 va_end (ap);
2395 status.notify = str_to_wchar (line);
2397 #ifdef HAVE_VASPRINTF
2398 free (line);
2399 #endif
2403 rav_next_prev (GAME g, BOARD b, int n)
2405 // Next RAV.
2406 if (n)
2408 if ((!g->ravlevel && g->hindex && g->hp[g->hindex - 1]->rav == NULL) ||
2409 (!g->ravlevel && !g->hindex && g->hp[g->hindex]->rav == NULL) ||
2410 (g->ravlevel && g->hp[g->hindex]->rav == NULL))
2411 return 1;
2413 g->rav = Realloc (g->rav, (g->ravlevel + 1) * sizeof (RAV));
2414 g->rav[g->ravlevel].hp = g->hp;
2415 g->rav[g->ravlevel].flags = g->flags;
2416 g->rav[g->ravlevel].fen = pgn_game_to_fen (g, b);
2417 g->rav[g->ravlevel].hindex = g->hindex;
2418 g->hp =
2419 (!g->ravlevel) ? (g->hindex) ? g->hp[g->hindex -
2420 1]->rav : g->hp[g->
2421 hindex]->rav :
2422 g->hp[g->hindex]->rav;
2423 g->hindex = 0;
2424 g->ravlevel++;
2425 pgn_board_update (g, b, g->hindex + 1);
2426 return 0;
2429 if (g->ravlevel - 1 < 0)
2430 return 1;
2432 // Previous RAV.
2433 g->ravlevel--;
2434 pgn_board_init_fen (g, b, g->rav[g->ravlevel].fen);
2435 free (g->rav[g->ravlevel].fen);
2436 g->hp = g->rav[g->ravlevel].hp;
2437 g->flags = g->rav[g->ravlevel].flags;
2438 g->hindex = g->rav[g->ravlevel].hindex;
2439 return 0;
2442 static void
2443 draw_window_decor ()
2445 move_panel (boardp, 0, (config.boardleft) ? 0 : COLS - BOARD_WIDTH);
2446 move_panel (historyp, LINES - HISTORY_HEIGHT,
2447 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH : 0 :
2448 (MEGA_BOARD) ? 0 : COLS - HISTORY_WIDTH);
2449 move_panel (statusp, 0, (config.boardleft) ? BOARD_WIDTH : 0);
2450 move_panel (tagp, STATUS_HEIGHT + 1,
2451 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH :
2452 HISTORY_WIDTH : 0);
2454 wbkgd (boardw, CP_BOARD_WINDOW);
2455 wbkgd (statusw, CP_STATUS_WINDOW);
2456 window_draw_title (statusw, _("Game Status"), STATUS_WIDTH,
2457 CP_STATUS_TITLE, CP_STATUS_BORDER);
2458 wbkgd (tagw, CP_TAG_WINDOW);
2459 window_draw_title (tagw, _("Roster Tags"), TAG_WIDTH, CP_TAG_TITLE,
2460 CP_TAG_BORDER);
2461 wbkgd (historyw, CP_HISTORY_WINDOW);
2462 window_draw_title (historyw, _("Move History"), HISTORY_WIDTH,
2463 CP_HISTORY_TITLE, CP_HISTORY_BORDER);
2466 static void
2467 history_menu_resize (WIN *w)
2469 struct menu_input_s *m;
2471 if (!w)
2472 return;
2474 w->rows = MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES;
2475 w->cols = TAG_WIDTH;
2476 w->posy = 0;
2477 w->posx = config.boardleft ? BOARD_WIDTH : 0;
2478 m = w->data;
2479 m->ystatic = w->posy;
2480 m->xstatic = w->posx;
2481 redraw_menu (w);
2484 void
2485 do_window_resize ()
2487 if (LINES < 23 || COLS < 74)
2488 return;
2490 resizeterm (LINES, COLS);
2491 endwin ();
2492 wresize (boardw, BOARD_HEIGHT, BOARD_WIDTH);
2493 wresize (historyw, HISTORY_HEIGHT, HISTORY_WIDTH);
2494 wresize (statusw, STATUS_HEIGHT, STATUS_WIDTH);
2495 wresize (tagw, TAG_HEIGHT, TAG_WIDTH);
2496 //resize_history_menu ();
2498 clear ();
2499 wclear (boardw);
2500 wclear (historyw);
2501 wclear (tagw);
2502 wclear (statusw);
2503 wclear (loadingw);
2504 wclear (enginew);
2505 draw_window_decor ();
2506 update_all (gp);
2507 keypad (boardw, TRUE);
2508 curs_set (0);
2509 cbreak ();
2510 noecho ();
2513 void
2514 do_global_redraw ()
2516 do_window_resize ();
2519 void
2520 stop_clock ()
2522 memset (&clock_timer, 0, sizeof (struct itimerval));
2523 setitimer (ITIMER_REAL, &clock_timer, NULL);
2526 void
2527 start_clock (GAME g)
2529 struct userdata_s *d = g->data;
2531 if (clock_timer.it_interval.tv_usec)
2532 return;
2534 memset (&d->elapsed, 0, sizeof (struct timeval));
2535 clock_timer.it_value.tv_sec = 0;
2536 clock_timer.it_value.tv_usec = 100000;
2537 clock_timer.it_interval.tv_sec = 0;
2538 clock_timer.it_interval.tv_usec = 100000;
2539 setitimer (ITIMER_REAL, &clock_timer, NULL);
2542 static void
2543 update_clocks ()
2545 int i;
2546 struct userdata_s *d;
2547 struct itimerval it;
2548 int update = 0;
2550 getitimer (ITIMER_REAL, &it);
2552 for (i = 0; i < gtotal; i++)
2554 d = game[i]->data;
2556 if (d && d->mode == MODE_PLAY)
2558 if (d->paused == 1 || TEST_FLAG (d->flags, CF_NEW))
2559 continue;
2560 else if (d->paused == -1)
2562 if (game[i]->side == game[i]->turn)
2564 d->paused = 1;
2565 continue;
2569 update_clock (game[i], it);
2571 if (game[i] == gp)
2572 update = 1;
2576 if (update)
2578 update_status_window (gp);
2579 update_panels ();
2580 doupdate ();
2584 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2586 static int
2587 parse_clock_time (char **str)
2589 char *p = *str;
2590 int n = 0, t = 0;
2592 SKIP_SPACE (p);
2594 if (!isdigit (*p))
2595 return -1;
2597 while (*p)
2599 if (isdigit (*p))
2601 t = atoi (p);
2603 while (isdigit (*p))
2604 p++;
2606 continue;
2609 switch (*p)
2611 case 'H':
2612 case 'h':
2613 n += t * (60 * 60);
2614 t = 0;
2615 break;
2616 case 'M':
2617 case 'm':
2618 n += t * 60;
2619 t = 0;
2620 break;
2621 case 'S':
2622 case 's':
2623 n += t;
2624 t = 0;
2625 break;
2626 case ' ':
2627 p++;
2628 case '/':
2629 case '+':
2630 goto done;
2631 default:
2632 *str = p;
2633 return -1;
2636 p++;
2639 done:
2640 n += t;
2641 *str = p;
2642 return n;
2645 static int
2646 parse_clock_input (struct clock_s *clk, char *str, int *incr)
2648 char *p = str;
2649 long n = 0;
2650 int plus = 0;
2651 int m = 0;
2652 int tc = 0;
2654 SKIP_SPACE (p);
2656 if (!*p)
2657 return 0;
2659 if (*p == '+')
2661 plus = 1;
2662 p++;
2663 SKIP_SPACE (p);
2665 if (*p == '+')
2666 goto move_incr;
2668 else
2669 memset (clk, 0, sizeof (struct clock_s));
2671 again:
2672 /* Sudden death. */
2673 if (strncasecmp (p, "SD", 2) == 0)
2675 n = 0;
2676 p += 2;
2677 goto tc;
2680 n = parse_clock_time (&p);
2682 if (n == -1)
2683 return 1;
2685 if (!n)
2686 goto done;
2688 /* Time control. */
2690 if (*p == '/')
2692 if (plus)
2693 return 1;
2695 /* Sudden death without a previous time control. */
2696 if (!n && !tc)
2697 return 1;
2699 m = n;
2700 p++;
2701 n = parse_clock_time (&p);
2703 if (n == -1)
2704 return 1;
2706 if (tc >= MAX_TC)
2708 message (ERROR_STR, ANY_KEY_STR, "%s (%i)",
2709 _("Maximum number of time controls reached"), MAX_TC);
2710 return 1;
2713 clk->tc[tc][0] = m;
2714 clk->tc[tc++][1] = n;
2715 SKIP_SPACE (p);
2717 if (*p == '+')
2718 goto move_incr;
2720 if (*p)
2721 goto again;
2723 goto done;
2726 if (plus)
2727 *incr = n;
2728 else
2729 clk->tc[clk->tcn][1] =
2730 (n <= clk->elapsed.tv_sec) ? clk->elapsed.tv_sec + n : n;
2732 move_incr:
2733 if (*p)
2735 if (*p++ == '+')
2737 if (!isdigit (*p))
2738 return 1;
2740 n = parse_clock_time (&p);
2742 if (n == -1 || *p)
2743 return 1;
2745 clk->incr = n;
2747 SKIP_SPACE (p);
2749 if (*p)
2750 return 1;
2752 else
2753 return 1;
2756 done:
2757 return 0;
2760 static int
2761 parse_which_clock (struct clock_s *clk, char *str)
2763 struct clock_s tmp;
2764 int incr = 0;
2766 memcpy (&tmp, clk, sizeof (struct clock_s));
2768 if (parse_clock_input (&tmp, str, &incr))
2770 cmessage (ERROR_STR, ANY_KEY_STR, _("Invalid clock specification"));
2771 return 1;
2774 memcpy (clk, &tmp, sizeof (struct clock_s));
2775 clk->tc[clk->tcn][1] += incr;
2776 return 0;
2779 void
2780 do_clock_input_finalize (WIN * win)
2782 struct userdata_s *d = gp->data;
2783 struct input_data_s *in = win->data;
2784 char *p = in->str;
2786 if (!in->str)
2788 free (in);
2789 return;
2792 SKIP_SPACE (p);
2794 if (tolower (*p) == 'w')
2796 p++;
2798 if (parse_which_clock (&d->wclock, p))
2799 goto done;
2801 else if (tolower (*p) == 'b')
2803 p++;
2805 if (parse_which_clock (&d->bclock, p))
2806 goto done;
2808 else
2810 if (parse_which_clock (&d->wclock, p))
2811 goto done;
2813 if (parse_which_clock (&d->bclock, p))
2814 goto done;
2817 if (!d->wclock.tc[0][1] && !d->bclock.tc[0][1])
2818 CLEAR_FLAG (d->flags, CF_CLOCK);
2819 else
2820 SET_FLAG (d->flags, CF_CLOCK);
2822 done:
2823 free (in->str);
2824 free (in);
2827 void
2828 do_engine_command_finalize (WIN * win)
2830 struct userdata_s *d = gp->data;
2831 struct input_data_s *in = win->data;
2832 int x;
2834 if (!in->str)
2836 free (in);
2837 return;
2840 if (!d->engine)
2841 goto done;
2843 x = d->engine->status;
2844 send_to_engine (gp, -1, "%s\n", in->str);
2845 d->engine->status = x;
2847 done:
2848 free (in->str);
2849 free (in);
2852 void
2853 do_board_details ()
2855 config.details = (config.details) ? 0 : 1;
2858 void
2859 do_toggle_strict_castling ()
2861 int n;
2863 pgn_config_get (PGN_STRICT_CASTLING, &n);
2865 if (n == 0)
2866 pgn_config_set (PGN_STRICT_CASTLING, 1);
2867 else
2868 pgn_config_set (PGN_STRICT_CASTLING, 0);
2871 void
2872 do_play_set_clock ()
2874 struct input_data_s *in;
2876 in = Calloc (1, sizeof (struct input_data_s));
2877 in->efunc = do_clock_input_finalize;
2878 construct_input (_("Set Clock"), NULL, 1, 1,
2880 ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n"
2881 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2882 NULL, NULL, 0, in, INPUT_HIST_CLOCK, NULL, -1);
2885 void
2886 do_play_toggle_human ()
2888 struct userdata_s *d = gp->data;
2890 TOGGLE_FLAG (d->flags, CF_HUMAN);
2892 if (!TEST_FLAG (d->flags, CF_HUMAN) && pgn_history_total (gp->hp))
2894 if (init_chess_engine (gp))
2895 return;
2898 CLEAR_FLAG (d->flags, CF_ENGINE_LOOP);
2900 if (d->engine)
2901 d->engine->status = ENGINE_READY;
2904 void
2905 do_play_toggle_engine ()
2907 struct userdata_s *d = gp->data;
2909 TOGGLE_FLAG (d->flags, CF_ENGINE_LOOP);
2910 CLEAR_FLAG (d->flags, CF_HUMAN);
2912 if (d->engine && TEST_FLAG (d->flags, CF_ENGINE_LOOP))
2914 char *fen = pgn_game_to_fen (gp, d->b);
2916 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
2917 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
2918 free (fen);
2923 * This will send a command to the engine skipping the command queue.
2925 void
2926 do_play_send_command ()
2928 struct userdata_s *d = gp->data;
2929 struct input_data_s *in;
2931 if (!d->engine || d->engine->status == ENGINE_OFFLINE)
2933 if (init_chess_engine (gp))
2934 return;
2937 in = Calloc (1, sizeof (struct input_data_s));
2938 in->efunc = do_engine_command_finalize;
2939 construct_input (_("Engine Command"), NULL, 1, 1, NULL, NULL, NULL, 0, in,
2940 INPUT_HIST_ENGINE, NULL, -1);
2944 void do_play_switch_turn()
2946 struct userdata_s *d = gp->data;
2948 pgn_switch_side(gp);
2949 pgn_switch_turn(gp);
2951 if (!TEST_FLAG(d->flags, CF_HUMAN))
2952 add_engine_command(gp, -1,
2953 (gp->side == WHITE) ? "white\n" : "black\n");
2955 update_status_window(gp);
2958 void
2959 do_play_toggle_eh_mode ()
2961 struct userdata_s *d = gp->data;
2963 if (!TEST_FLAG (d->flags, CF_HUMAN))
2965 if (!gp->hindex)
2967 pgn_switch_side (gp, TRUE);
2968 d->play_mode = (d->play_mode) ? PLAY_HE : PLAY_EH;
2969 if (gp->side == BLACK)
2970 update_status_notify (gp, _("Press 'g' to start the game"));
2972 d->rotate = !d->rotate;
2974 else
2975 message (NULL, ANY_KEY_STR,
2976 _("You may only switch sides at the start of the \n"
2977 "game. Press ^K or ^N to begin a new game."));
2981 void
2982 do_play_undo ()
2984 struct userdata_s *d = gp->data;
2986 if (!pgn_history_total (gp->hp))
2987 return;
2989 if (keycount)
2991 if (gp->hindex - keycount < 0)
2992 gp->hindex = 0;
2993 else
2995 if (d->go_move)
2996 gp->hindex -= (keycount * 2) - 1;
2997 else
2998 gp->hindex -= keycount * 2;
3001 else
3003 if (gp->hindex - 2 < 0)
3004 gp->hindex = 0;
3005 else
3007 if (d->go_move)
3008 gp->hindex -= 1;
3009 else
3010 gp->hindex -= 2;
3014 pgn_history_free (gp->hp, gp->hindex);
3015 gp->hindex = pgn_history_total (gp->hp);
3016 pgn_board_update (gp, d->b, gp->hindex);
3018 if (d->engine && d->engine->status == ENGINE_READY)
3020 char *fen = pgn_game_to_fen (gp, d->b);
3022 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
3023 free (fen);
3024 d->engine->status = ENGINE_READY;
3027 update_history_window (gp);
3029 if (d->go_move)
3031 pgn_switch_side (gp, FALSE);
3032 d->go_move--;
3035 d->pm_undo = TRUE;
3038 void
3039 do_play_toggle_pause ()
3041 struct userdata_s *d = gp->data;
3043 if (!TEST_FLAG (d->flags, CF_HUMAN) && gp->turn != gp->side)
3045 d->paused = -1;
3046 return;
3049 d->paused = (d->paused) ? 0 : 1;
3052 void
3053 do_play_go ()
3055 struct userdata_s *d = gp->data;
3057 if (TEST_FLAG (d->flags, CF_HUMAN))
3058 return;
3060 if (fm_loaded_file && gp->side != gp->turn)
3062 pgn_switch_side (gp, FALSE);
3063 add_engine_command (gp, ENGINE_THINKING, "black\n");
3066 add_engine_command (gp, ENGINE_THINKING, "go\n");
3068 // Completa la función para que permita seguir jugando al usarla.
3069 // Complete the function to allow continue playing when using.
3070 if (gp->side == gp->turn)
3071 pgn_switch_side (gp, FALSE);
3073 d->go_move++;
3076 void
3077 do_play_config_command ()
3079 int x, w;
3081 if (config.keys)
3083 for (x = 0; config.keys[x]; x++)
3085 if (config.keys[x]->c == input_c)
3087 switch (config.keys[x]->type)
3089 case KEY_DEFAULT:
3090 add_engine_command (gp, -1, "%ls\n", config.keys[x]->str);
3091 break;
3092 case KEY_SET:
3093 if (!keycount)
3094 break;
3096 add_engine_command (gp, -1,
3097 "%ls %i\n", config.keys[x]->str,
3098 keycount);
3099 keycount = 0;
3100 break;
3101 case KEY_REPEAT:
3102 if (!keycount)
3103 break;
3105 for (w = 0; w < keycount; w++)
3106 add_engine_command (gp, -1, "%ls\n", config.keys[x]->str);
3107 keycount = 0;
3108 break;
3114 update_status_notify (gp, NULL);
3117 void
3118 do_play_cancel_selected ()
3120 struct userdata_s *d = gp->data;
3122 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3123 keycount = 0;
3124 pgn_reset_valid_moves (d->b);
3125 update_status_notify (gp, NULL);
3128 void
3129 do_play_commit ()
3131 struct userdata_s *d = gp->data;
3133 pushkey = keycount = 0;
3134 update_status_notify (gp, NULL);
3136 if (!TEST_FLAG (d->flags, CF_HUMAN) &&
3137 (!d->engine || d->engine->status == ENGINE_THINKING))
3138 return;
3140 if (!d->sp.icon)
3141 return;
3143 d->sp.row = d->c_row;
3144 d->sp.col = d->c_col;
3146 if (d->rotate)
3148 rotate_position (&d->sp.row, &d->sp.col);
3149 rotate_position (&d->sp.srow, &d->sp.scol);
3152 move_to_engine (gp);
3154 // Completa la función para que permita seguir jugando cuando se carga un
3155 // archivo pgn (con juego no terminado) que inicie con turno del lado
3156 // negro.
3157 // Complete the function to allow continue playing when loading a file
3158 // pgn (with unfinished game) you start to turn black side.
3159 if (gp->side != gp->turn)
3160 pgn_switch_side (gp, FALSE);
3162 if (d->rotate && d->sp.icon)
3163 rotate_position (&d->sp.srow, &d->sp.scol);
3165 d->go_move = 0;
3166 fm_loaded_file = FALSE;
3167 d->pm_undo = FALSE;
3170 void
3171 do_play_select ()
3173 struct userdata_s *d = gp->data;
3175 if (!TEST_FLAG (d->flags, CF_HUMAN) && (!d->engine ||
3176 d->engine->status ==
3177 ENGINE_OFFLINE))
3179 if (init_chess_engine (gp))
3180 return;
3183 if (d->engine && d->engine->status == ENGINE_THINKING)
3184 return;
3186 if (d->sp.icon)
3187 do_play_cancel_selected ();
3189 if (d->rotate)
3190 rotate_position (&d->c_row, &d->c_col);
3192 d->sp.icon = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon;
3194 if (pgn_piece_to_int (d->sp.icon) == OPEN_SQUARE)
3196 d->sp.icon = 0;
3197 return;
3200 if (((islower (d->sp.icon) && gp->turn != BLACK)
3201 || (isupper (d->sp.icon) && gp->turn != WHITE)))
3203 struct key_s **k;
3204 char *str = Malloc (512);
3206 for (k = play_keys; *k; k++)
3208 if ((*k)->f == do_play_toggle_eh_mode)
3209 break;
3212 snprintf (str, 512,
3214 ("It is not your turn to move. You may switch playing sides by pressing \"%lc\"."),
3215 *k ? (*k)->c : '?');
3216 message (NULL, ANY_KEY_STR, "%s", str);
3217 free (str);
3218 d->sp.icon = 0;
3219 return;
3220 #if 0
3221 if (pgn_history_total (gp->hp))
3223 message (NULL, ANY_KEY_STR, "%s",
3224 _("It is not your turn to move. You can switch sides "));
3225 d->sp.icon = 0;
3226 return;
3228 else
3230 if (pgn_tag_find (gp->tag, "FEN") != E_PGN_ERR)
3231 return;
3233 add_engine_command (gp, ENGINE_READY, "black\n");
3234 pgn_switch_turn (gp);
3236 if (gp->side != BLACK)
3237 pgn_switch_side (gp);
3239 #endif
3242 d->sp.srow = d->c_row;
3243 d->sp.scol = d->c_col;
3245 if (config.validmoves)
3246 pgn_find_valid_moves (gp, d->b, d->sp.scol, d->sp.srow);
3248 if (d->rotate)
3250 rotate_position (&d->c_row, &d->c_col);
3251 rotate_position (&d->sp.srow, &d->sp.scol);
3254 CLEAR_FLAG (d->flags, CF_NEW);
3255 start_clock (gp);
3258 static wchar_t *
3259 build_help (struct key_s **keys)
3261 int i, nlen = 1, len, t, n;
3262 wchar_t *buf = NULL, *wc = NULL;
3263 wchar_t *p;
3265 if (!keys)
3266 return NULL;
3268 for (i = len = t = 0; keys[i]; i++)
3270 if (!keys[i]->d)
3271 continue;
3273 if (keys[i]->key)
3275 if (wcslen (keys[i]->key) > nlen)
3277 nlen = wcslen (keys[i]->key);
3278 t += nlen;
3280 else
3281 t++;
3283 else
3284 t++;
3286 if (keys[i]->d)
3288 if (wcslen (keys[i]->d) > len)
3289 len = wcslen (keys[i]->d);
3292 t += len;
3293 t += keys[i]->r;
3296 t += 4 + i + 1;
3297 buf = Malloc ((t + 1) * sizeof (wchar_t));
3298 p = buf;
3300 for (i = 0; keys[i]; i++)
3302 if (!keys[i]->d)
3303 continue;
3305 if (keys[i]->key)
3306 n = wcslen (keys[i]->key);
3307 else
3308 n = 1;
3310 while (n++ <= nlen)
3311 *p++ = ' ';
3313 *p = 0;
3315 if (keys[i]->key)
3317 wcsncat (buf, keys[i]->key, t - 1);
3318 p = buf + wcslen (buf);
3320 else
3321 *p++ = keys[i]->c;
3323 *p++ = ' ';
3324 *p++ = '-';
3325 *p++ = ' ';
3326 *p = 0;
3328 if (keys[i]->d)
3329 wcsncat (buf, keys[i]->d, t - 1);
3331 if (keys[i]->r)
3333 wc = str_to_wchar ("*");
3334 wcsncat (buf, wc, t - 1);
3335 free (wc);
3338 wc = str_to_wchar ("\n");
3339 wcscat (buf, wc);
3340 free (wc);
3341 p = buf + wcslen (buf);
3344 return buf;
3347 void
3348 do_global_help ()
3350 wchar_t *buf = build_help (global_keys);
3352 construct_message (_("Global Game Keys (* = can take a repeat count)"),
3353 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
3354 0, 1, NULL, "%ls", buf);
3357 void
3358 do_main_help (WIN * win)
3360 switch (win->c)
3362 case 'p':
3363 do_play_help ();
3364 break;
3365 case 'h':
3366 do_history_help ();
3367 break;
3368 case 'e':
3369 do_edit_help ();
3370 break;
3371 case 'g':
3372 do_global_help ();
3373 break;
3374 default:
3375 break;
3379 static void
3380 do_more_help (WIN * win)
3382 if (win->c == KEY_F (1) || win->c == CTRL_KEY ('g'))
3383 construct_message (_("Command Key Index"),
3384 _("p/h/e/g or any other key to quit"), 0, 0,
3385 NULL, NULL, NULL, do_main_help, 0, 0, NULL, "%s",
3386 _("p - play mode keys\n"
3387 "h - history mode keys\n"
3388 "e - board edit mode keys\n"
3389 "g - global game keys"));
3392 void
3393 do_play_help ()
3395 wchar_t *buf = build_help (play_keys);
3397 construct_message (_("Play Mode Keys (* = can take a repeat count)"),
3398 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
3399 buf, do_more_help, 0, 1, NULL, "%ls", buf);
3402 void
3403 do_play_history_mode ()
3405 struct userdata_s *d = gp->data;
3407 if (!pgn_history_total (gp->hp) ||
3408 (d->engine && d->engine->status == ENGINE_THINKING))
3409 return;
3411 d->mode = MODE_HISTORY;
3412 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
3415 void
3416 do_play_edit_mode ()
3418 struct userdata_s *d = gp->data;
3420 if (pgn_history_total (gp->hp))
3421 return;
3423 pgn_board_init_fen (gp, d->b, NULL);
3424 config.details++;
3425 d->mode = MODE_EDIT;
3428 void
3429 do_edit_insert_finalize (WIN * win)
3431 struct userdata_s *d = win->data;
3433 if (pgn_piece_to_int (win->c) == -1)
3434 return;
3436 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon = win->c;
3439 void
3440 do_edit_select ()
3442 struct userdata_s *d = gp->data;
3444 if (d->sp.icon)
3445 return;
3447 d->sp.icon = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon;
3449 if (pgn_piece_to_int (d->sp.icon) == OPEN_SQUARE)
3451 d->sp.icon = 0;
3452 return;
3455 d->sp.srow = d->c_row;
3456 d->sp.scol = d->c_col;
3459 void
3460 do_edit_commit ()
3462 int p;
3463 struct userdata_s *d = gp->data;
3465 pushkey = keycount = 0;
3466 update_status_notify (gp, NULL);
3468 if (!d->sp.icon)
3469 return;
3471 d->sp.row = d->c_row;
3472 d->sp.col = d->c_col;
3473 p = d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
3474 d->b[RANKTOBOARD (d->sp.row)][FILETOBOARD (d->sp.col)].icon = p;
3475 d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
3476 pgn_int_to_piece (gp->turn, OPEN_SQUARE);
3477 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3480 void
3481 do_edit_delete ()
3483 struct userdata_s *d = gp->data;
3485 if (d->sp.icon)
3486 d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
3487 pgn_int_to_piece (gp->turn, OPEN_SQUARE);
3488 else
3489 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon =
3490 pgn_int_to_piece (gp->turn, OPEN_SQUARE);
3492 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3495 void
3496 do_edit_cancel_selected ()
3498 struct userdata_s *d = gp->data;
3500 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3501 keycount = 0;
3502 update_status_notify (gp, NULL);
3505 void
3506 do_edit_switch_turn ()
3508 pgn_switch_turn (gp);
3511 void
3512 do_edit_toggle_castle ()
3514 struct userdata_s *d = gp->data;
3516 castling_state (gp, d->b, RANKTOBOARD (d->c_row),
3517 FILETOBOARD (d->c_col),
3518 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon,
3522 void
3523 do_edit_insert ()
3525 struct userdata_s *d = gp->data;
3527 construct_message (_("Insert Piece"),
3528 _("P=pawn, R=rook, N=knight, B=bishop, "), 0, 0, NULL,
3529 NULL, d->b, do_edit_insert_finalize, 0, 0, NULL, "%s",
3530 _("Type the piece letter to insert. Lowercase "));
3533 void
3534 do_edit_enpassant ()
3536 struct userdata_s *d = gp->data;
3538 if (d->c_row == 6 || d->c_row == 3)
3540 pgn_reset_enpassant (d->b);
3541 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].enpassant = 1;
3545 void
3546 do_edit_help ()
3548 wchar_t *buf = build_help (edit_keys);
3550 construct_message (_("Edit Mode Keys (* = can take a repeat count)"),
3551 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
3552 0, 1, NULL, "%ls", buf);
3555 void
3556 do_edit_exit ()
3558 struct userdata_s *d = gp->data;
3559 char *fen = pgn_game_to_fen (gp, d->b);
3561 config.details--;
3562 pgn_tag_add (&gp->tag, (char *) "FEN", fen);
3563 free (fen);
3564 pgn_tag_add (&gp->tag, (char *) "SetUp", (char *) "1");
3565 pgn_tag_sort (gp->tag);
3566 pgn_board_update (gp, d->b, gp->hindex);
3567 d->mode = MODE_PLAY;
3570 void
3571 really_do_annotate_finalize (struct input_data_s *in, struct userdata_s *d)
3573 HISTORY *h = in->data;
3574 int len;
3576 if (!in->str)
3578 if (h->comment)
3580 free (h->comment);
3581 h->comment = NULL;
3584 else
3586 len = strlen (in->str);
3587 h->comment = Realloc (h->comment, len + 1);
3588 strncpy (h->comment, in->str, len);
3589 h->comment[len] = 0;
3592 free (in->str);
3593 free (in);
3594 SET_FLAG (d->flags, CF_MODIFIED);
3597 void
3598 do_annotate_finalize (WIN * win)
3600 struct userdata_s *d = gp->data;
3601 struct input_data_s *in = win->data;
3603 really_do_annotate_finalize (in, d);
3606 void
3607 do_find_move_exp_finalize (int init, int which)
3609 int n;
3610 struct userdata_s *d = gp->data;
3611 static int firstrun;
3612 static regex_t r;
3613 int ret;
3614 char errbuf[255];
3616 if (init || !firstrun)
3618 if (!firstrun)
3619 regfree (&r);
3621 if ((ret = regcomp (&r, moveexp, REG_EXTENDED | REG_NOSUB)) != 0)
3623 regerror (ret, &r, errbuf, sizeof (errbuf));
3624 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR,
3625 "%s", errbuf);
3626 return;
3629 firstrun = 1;
3632 if ((n = find_move_exp (gp, r,
3633 (which == -1) ? 0 : 1,
3634 (keycount) ? keycount : 1)) == -1)
3635 return;
3637 gp->hindex = n;
3638 pgn_board_update (gp, d->b, gp->hindex);
3641 void
3642 do_find_move_exp (WIN * win)
3644 struct input_data_s *in = win->data;
3645 int *n = in->data;
3646 int which = *n;
3648 if (in->str)
3650 strncpy (moveexp, in->str, sizeof (moveexp) - 1);
3651 moveexp[sizeof (moveexp) - 1] = 0;
3652 do_find_move_exp_finalize (1, which);
3653 free (in->str);
3656 free (in->data);
3657 free (in);
3660 void
3661 do_move_jump_finalize (int n)
3663 struct userdata_s *d = gp->data;
3665 if (n < 0 || n > (pgn_history_total (gp->hp) / 2))
3666 return;
3668 keycount = 0;
3669 update_status_notify (gp, NULL);
3670 gp->hindex = (n) ? n * 2 - 1 : n * 2;
3671 pgn_board_update (gp, d->b, gp->hindex);
3674 void
3675 do_move_jump (WIN * win)
3677 struct input_data_s *in = win->data;
3679 if (!in->str || !isinteger (in->str))
3681 if (in->str)
3682 free (in->str);
3684 free (in);
3685 return;
3688 do_move_jump_finalize (atoi (in->str));
3689 free (in->str);
3690 free (in);
3693 struct history_menu_s
3695 char *line;
3696 int hindex;
3697 int ravlevel;
3698 int move;
3699 int indent;
3702 void
3703 free_history_menu_data (struct history_menu_s **h)
3705 int i;
3707 if (!h)
3708 return;
3710 for (i = 0; h[i]; i++)
3712 free (h[i]->line);
3713 free (h[i]);
3716 free (h);
3719 void
3720 get_history_data (HISTORY ** hp, struct history_menu_s ***menu, int m,
3721 int turn)
3723 int i, n = 0;
3724 int t = pgn_history_total (hp);
3725 char buf[MAX_SAN_MOVE_LEN + 4];
3726 static int depth;
3727 struct history_menu_s **hmenu = *menu;
3729 if (hmenu)
3730 for (n = 0; hmenu[n]; n++);
3731 else
3732 depth = 0;
3734 for (i = 0; i < t; i++)
3736 hmenu = Realloc (hmenu, (n + 2) * sizeof (struct history_menu_s *));
3737 hmenu[n] = Malloc (sizeof (struct history_menu_s));
3738 snprintf (buf, sizeof (buf), "%c%s%s", (turn == WHITE) ? 'W' : 'B',
3739 hp[i]->move, (hp[i]->comment || hp[i]->nag[0]) ? " !" : "");
3740 hmenu[n]->line = strdup (buf);
3741 hmenu[n]->hindex = i;
3742 hmenu[n]->indent = 0;
3743 hmenu[n]->ravlevel = depth;
3744 hmenu[n]->move = (n && depth > hmenu[n - 1]->ravlevel) ? m++ : m;
3745 n++;
3746 hmenu[n] = NULL;
3748 #if 0
3749 if (hp[i]->rav)
3751 depth++;
3752 get_history_data (hp[i]->rav, &hmenu, m, turn);
3753 for (n = 0; hmenu[n]; n++);
3754 depth--;
3756 if (depth)
3757 m--;
3759 #endif
3761 turn = (turn == WHITE) ? BLACK : WHITE;
3764 *menu = hmenu;
3767 void
3768 history_draw_update (struct menu_input_s *m)
3770 GAME g = m->data;
3771 struct userdata_s *d = g->data;
3773 g->hindex = m->selected + 1;
3774 update_cursor (g, m->selected);
3775 pgn_board_update (g, d->b, m->selected + 1);
3778 struct menu_item_s **
3779 get_history_items (WIN * win)
3781 struct menu_input_s *m = win->data;
3782 GAME g = m->data;
3783 struct userdata_s *d = g->data;
3784 struct history_menu_s **hm = d->data;
3785 struct menu_item_s **items = m->items;
3786 int i;
3788 if (!hm)
3790 get_history_data (g->history, &hm, 0,
3791 TEST_FLAG (g->flags, GF_BLACK_OPENING));
3792 m->selected = g->hindex - 1;
3794 if (m->selected < 0)
3795 m->selected = 0;
3797 m->draw_exit_func = history_draw_update;
3800 d->data = hm;
3802 if (items)
3804 for (i = 0; items[i]; i++)
3805 free (items[i]);
3807 free (items);
3808 items = NULL;
3811 for (i = 0; hm[i]; i++)
3813 items = Realloc (items, (i + 2) * sizeof (struct menu_item_s *));
3814 items[i] = Malloc (sizeof (struct menu_item_s));
3815 items[i]->name = hm[i]->line;
3816 items[i]->value = NULL;
3817 items[i]->selected = 0;
3820 if (items)
3821 items[i] = NULL;
3823 m->nofree = 1;
3824 m->items = items;
3825 return items;
3828 void
3829 history_menu_quit (struct menu_input_s *m)
3831 pushkey = -1;
3834 void
3835 history_menu_exit (WIN * win)
3837 GAME g = win->data;
3838 struct userdata_s *d = g->data;
3839 struct history_menu_s **hm = d->data;
3840 int i;
3842 if (!hm)
3843 return;
3845 for (i = 0; hm[i]; i++)
3847 free (hm[i]->line);
3848 free (hm[i]);
3851 free (hm);
3852 d->data = NULL;
3855 // FIXME RAV
3856 void
3857 history_menu_next (struct menu_input_s *m)
3859 GAME g = m->data;
3860 struct userdata_s *d = g->data;
3861 struct history_menu_s **hm = d->data;
3862 int n, t;
3864 for (t = 0; hm[t]; t++);
3866 if (m->selected + 1 == t)
3867 n = 0;
3868 else
3869 n = hm[m->selected + 1]->hindex;
3871 n++;
3872 g->hindex = n;
3875 // FIXME RAV
3876 void
3877 history_menu_prev (struct menu_input_s *m)
3879 GAME g = m->data;
3880 struct userdata_s *d = g->data;
3881 struct history_menu_s **hm = d->data;
3882 int n, t;
3884 for (t = 0; hm[t]; t++);
3886 if (m->selected - 1 < 0)
3887 n = t - 1;
3888 else
3889 n = hm[m->selected - 1]->hindex;
3891 n++;
3892 g->hindex = n;
3895 void
3896 history_menu_help (struct menu_input_s *m)
3898 message (_("History Menu Help"), ANY_KEY_STR, "%s",
3899 _(" UP/DOWN - previous/next menu item\n"
3900 " HOME/END - first/last menu item\n"
3901 " PGDN/PGUP - next/previous page\n"
3902 " a-zA-Z0-9 - jump to item\n"
3903 " CTRL-a - annotate the selected move\n"
3904 " ENTER - view annotation\n"
3905 " CTRL-d - toggle board details\n"
3906 " ESCAPE/M - return to move history"));
3909 void
3910 do_annotate_move (HISTORY * hp)
3912 char buf[COLS - 4];
3913 struct input_data_s *in;
3915 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Editing Annotation for"),
3916 hp->move);
3917 in = Calloc (1, sizeof (struct input_data_s));
3918 in->data = hp;
3919 in->efunc = do_annotate_finalize;
3920 construct_input (buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3921 _("Type CTRL-t to edit NAG"), edit_nag, NULL,
3922 CTRL_KEY ('T'), in, -1, NULL, -1);
3925 void
3926 history_menu_view_annotation (struct menu_input_s *m)
3928 GAME g = m->data;
3930 // FIXME RAV
3931 view_annotation (g->history[m->selected]);
3934 void
3935 history_menu_annotate_finalize (WIN * win)
3937 struct input_data_s *in = win->data;
3938 GAME g = in->moredata;
3939 struct userdata_s *d = g->data;
3940 struct history_menu_s **hm = d->data;
3942 really_do_annotate_finalize (in, d);
3943 free_history_menu_data (hm);
3944 hm = NULL;
3945 get_history_data (g->history, &hm, 0,
3946 TEST_FLAG (g->flags, GF_BLACK_OPENING));
3947 d->data = hm;
3948 pushkey = REFRESH_MENU;
3951 void
3952 history_menu_annotate (struct menu_input_s *m)
3954 GAME g = m->data;
3955 char buf[COLS - 4];
3956 struct input_data_s *in;
3957 HISTORY *hp = g->history[m->selected]; // FIXME RAV
3959 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Editing Annotation for"),
3960 hp->move);
3961 in = Calloc (1, sizeof (struct input_data_s));
3962 in->data = hp;
3963 in->moredata = m->data;
3964 in->efunc = history_menu_annotate_finalize;
3965 construct_input (buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3966 _("Type CTRL-t to edit NAG"), edit_nag, NULL,
3967 CTRL_KEY ('T'), in, -1, NULL, -1);
3970 void
3971 history_menu_details (struct menu_input_s *m)
3973 do_board_details ();
3976 // FIXME RAV
3977 void
3978 history_menu_print (WIN * win)
3980 struct menu_input_s *m = win->data;
3981 GAME g = m->data;
3982 struct userdata_s *d = g->data;
3983 struct history_menu_s **hm = d->data;
3984 struct history_menu_s *h = hm[m->top];
3985 int i;
3986 char *p = m->item->name;
3987 int line = m->print_line - 2;
3989 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
3990 * attr_t data type.
3992 attr_t attrs;
3993 short pair;
3994 int total;
3996 for (total = 0; hm[total]; total++);
3997 wattr_get (win->w, &attrs, &pair, NULL);
3998 wattroff (win->w, COLOR_PAIR (pair));
3999 mvwaddch (win->w, m->print_line, 1,
4000 *p == 'W' ? *p | mix_cp (CP_BOARD_WHITE, CP_HISTORY_WINDOW,
4001 ATTRS (CP_BOARD_WHITE),
4002 A_FG_B_BG) : *p | mix_cp (CP_BOARD_BLACK,
4003 CP_HISTORY_WINDOW,
4004 ATTRS
4005 (CP_BOARD_BLACK),
4006 A_FG_B_BG));
4007 p++;
4009 if (h->hindex == 0 && line == 0)
4010 waddch (win->w, ACS_ULCORNER | CP_HISTORY_MENU_LG);
4011 else if ((!hm[h->hindex + (win->rows - 5) + 1] && line == win->rows - 5) ||
4012 (m->top + line == total - 1))
4013 waddch (win->w, ACS_LLCORNER | CP_HISTORY_MENU_LG);
4014 else if (hm[m->top + 1]->ravlevel != h->ravlevel || !h->ravlevel)
4015 waddch (win->w, ACS_LTEE | CP_HISTORY_MENU_LG);
4016 else
4017 waddch (win->w, ACS_VLINE | CP_HISTORY_MENU_LG);
4019 wattron (win->w, COLOR_PAIR (pair) | attrs);
4021 for (i = 2; *p; p++, i++)
4022 waddch (win->w, (*p == '!') ? *p | A_BOLD : *p);
4024 while (i++ < win->cols - 2)
4025 waddch (win->w, ' ');
4028 void
4029 history_menu (GAME g)
4031 struct menu_key_s **keys = NULL;
4033 add_menu_key (&keys, KEY_ESCAPE, history_menu_quit);
4034 add_menu_key (&keys, 'M', history_menu_quit);
4035 add_menu_key (&keys, KEY_UP, history_menu_prev);
4036 add_menu_key (&keys, KEY_DOWN, history_menu_next);
4037 add_menu_key (&keys, KEY_F (1), history_menu_help);
4038 add_menu_key (&keys, CTRL_KEY ('a'), history_menu_annotate);
4039 add_menu_key (&keys, CTRL_KEY ('d'), history_menu_details);
4040 add_menu_key (&keys, '\n', history_menu_view_annotation);
4041 construct_menu (MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES, TAG_WIDTH,
4042 0, config.boardleft ? BOARD_WIDTH : 0,
4043 _("Move History Tree"), 1, get_history_items, keys, g,
4044 history_menu_print, history_menu_exit, history_menu_resize);
4047 void
4048 do_history_menu ()
4050 history_menu (gp);
4053 void
4054 do_history_half_move_toggle ()
4056 movestep = (movestep == 1) ? 2 : 1;
4057 update_history_window (gp);
4060 void
4061 do_history_rotate_board ()
4063 struct userdata_s *d = gp->data;
4064 d->rotate = !d->rotate;
4067 void
4068 do_history_jump_next ()
4070 struct userdata_s *d = gp->data;
4072 pgn_history_next (gp, d->b, (keycount > 0) ?
4073 config.jumpcount * keycount * movestep :
4074 config.jumpcount * movestep);
4077 void
4078 do_history_jump_prev ()
4080 struct userdata_s *d = gp->data;
4082 pgn_history_prev (gp, d->b, (keycount) ?
4083 config.jumpcount * keycount * movestep :
4084 config.jumpcount * movestep);
4087 void
4088 do_history_prev ()
4090 struct userdata_s *d = gp->data;
4092 pgn_history_prev (gp, d->b, (keycount) ? keycount * movestep : movestep);
4095 void
4096 do_history_next ()
4098 struct userdata_s *d = gp->data;
4100 pgn_history_next (gp, d->b, (keycount) ? keycount * movestep : movestep);
4103 void
4104 do_history_mode_finalize (struct userdata_s *d)
4106 pushkey = 0;
4107 d->mode = MODE_PLAY;
4110 void
4111 do_history_mode_confirm (WIN * win)
4113 struct userdata_s *d = gp->data;
4114 wchar_t str[] = { win->c, 0 };
4116 if (!wcscmp (str, resume_wchar))
4118 pgn_history_free (gp->hp, gp->hindex);
4119 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4121 #if 0
4122 case 'C':
4123 case 'c':
4124 if (pgn_history_rav_new (gp, d->b, gp->hindex) != E_PGN_OK)
4125 return;
4127 break;
4128 #endif
4129 else
4130 return;
4132 if (!TEST_FLAG (d->flags, CF_HUMAN))
4134 char *fen = pgn_game_to_fen (gp, d->b);
4136 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
4137 free (fen);
4140 do_history_mode_finalize (d);
4143 void
4144 do_history_toggle ()
4146 struct userdata_s *d = gp->data;
4148 // FIXME Resuming from previous history could append to a RAV.
4149 if (gp->hindex != pgn_history_total (gp->hp))
4151 if (!pushkey)
4152 construct_message (NULL, _("What would you like to do?"), 0, 1,
4153 NULL, NULL, NULL, do_history_mode_confirm, 0, 0,
4154 NULL, _
4155 ("The current move is not the final move of this round. Press \"%ls\" to resume a game from the current move and discard future moves or any other key to cancel."),
4156 resume_wchar);
4157 return;
4159 else
4161 if (TEST_FLAG (gp->flags, GF_GAMEOVER))
4162 return;
4165 if (gp->side != gp->turn)
4167 d->play_mode = PLAY_EH;
4169 else
4171 d->play_mode = PLAY_HE;
4172 d->rotate = FALSE;
4175 do_history_mode_finalize (d);
4178 void
4179 do_history_annotate ()
4181 int n = gp->hindex;
4183 if (n && gp->hp[n - 1]->move)
4184 n--;
4185 else
4186 return;
4188 do_annotate_move (gp->hp[n]);
4191 void
4192 do_history_help ()
4194 wchar_t *buf = build_help (history_keys);
4196 construct_message (_("History Mode Keys (* = can take a repeat count)"),
4197 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
4198 buf, do_more_help, 0, 1, NULL, "%ls", buf);
4201 void
4202 do_history_find (int which)
4204 struct input_data_s *in;
4205 int *p;
4207 if (pgn_history_total (gp->hp) < 2)
4208 return;
4210 in = Calloc (1, sizeof (struct input_data_s));
4211 p = Malloc (sizeof (int));
4212 *p = which;
4213 in->data = p;
4214 in->efunc = do_find_move_exp;
4216 if (!*moveexp || which == 0)
4218 construct_input (_("Find Move Text Expression"), NULL, 1, 0, NULL, NULL,
4219 NULL, 0, in, INPUT_HIST_MOVE_EXP, NULL, -1);
4220 return;
4223 free (p);
4224 free (in);
4225 do_find_move_exp_finalize (0, which);
4228 void
4229 do_history_find_new ()
4231 do_history_find (0);
4234 void
4235 do_history_find_prev ()
4237 do_history_find (-1);
4240 void
4241 do_history_find_next ()
4243 do_history_find (1);
4246 void
4247 do_history_rav (int which)
4249 struct userdata_s *d = gp->data;
4251 rav_next_prev (gp, d->b, which);
4254 void
4255 do_history_rav_next ()
4257 do_history_rav (1);
4260 void
4261 do_history_rav_prev ()
4263 do_history_rav (0);
4266 void
4267 do_history_jump ()
4269 struct input_data_s *in;
4271 if (pgn_history_total (gp->hp) < 2)
4272 return;
4274 if (!keycount)
4276 in = Calloc (1, sizeof (struct input_data_s));
4277 in->efunc = do_move_jump;
4279 construct_input (_("Jump to Move Number"), NULL, 1, 1, NULL,
4280 NULL, NULL, 0, in, -1, NULL, 0);
4281 return;
4284 do_move_jump_finalize (keycount);
4287 static void
4288 free_userdata_once (GAME g)
4290 struct userdata_s *d = g->data;
4292 if (!d)
4293 return;
4295 if (d->engine)
4297 stop_engine (g);
4299 if (d->engine->enginebuf)
4301 int n;
4303 for (n = 0; d->engine->enginebuf[n]; n++)
4304 free (d->engine->enginebuf[n]);
4306 free (d->engine->enginebuf);
4309 if (d->engine->queue)
4311 struct queue_s **q;
4313 for (q = d->engine->queue; *q; q++)
4314 free (*q);
4316 free (d->engine->queue);
4319 free (d->engine);
4322 #ifdef WITH_LIBPERL
4323 if (d->perlfen)
4324 free (d->perlfen);
4326 if (d->oldfen)
4327 free (d->oldfen);
4328 #endif
4330 free (d);
4331 g->data = NULL;
4334 static void
4335 free_userdata ()
4337 int i;
4339 for (i = 0; i < gtotal; i++)
4341 free_userdata_once (game[i]);
4342 game[i]->data = NULL;
4346 void
4347 update_loading_window (int n)
4349 char buf[16];
4351 if (!loadingw)
4353 loadingw = newwin (3, COLS / 2, CALCPOSY (3), CALCPOSX (COLS / 2));
4354 loadingp = new_panel (loadingw);
4355 wbkgd (loadingw, CP_MESSAGE_WINDOW);
4358 wmove (loadingw, 0, 0);
4359 wclrtobot (loadingw);
4360 wattron (loadingw, CP_MESSAGE_BORDER);
4361 box (loadingw, ACS_VLINE, ACS_HLINE);
4362 wattroff (loadingw, CP_MESSAGE_BORDER);
4363 mvwprintw (loadingw, 1, CENTER_INT ((COLS / 2), 11 +
4364 strlen (itoa (gtotal, buf))),
4365 _("Loading... %i%% (%i games)"), n, gtotal);
4366 update_panels ();
4367 doupdate ();
4370 static void
4371 init_userdata_once (GAME g, int n)
4373 struct userdata_s *d = NULL;
4375 d = Calloc (1, sizeof (struct userdata_s));
4376 d->n = n;
4377 d->c_row = 2, d->c_col = 5;
4378 SET_FLAG (d->flags, CF_NEW);
4379 g->data = d;
4381 if (pgn_board_init_fen (g, d->b, NULL) != E_PGN_OK)
4382 pgn_board_init (d->b);
4385 void
4386 init_userdata ()
4388 int i;
4390 for (i = 0; i < gtotal; i++)
4391 init_userdata_once (game[i], i);
4394 void
4395 fix_marks (int *start, int *end)
4397 int i;
4399 *start = (*start < 0) ? 0 : *start;
4400 *end = (*end < 0) ? 0 : *end;
4402 if (*start > *end)
4404 i = *start;
4405 *start = *end;
4406 *end = i + 1;
4409 *end = (*end > gtotal) ? gtotal : *end;
4412 void
4413 do_new_game_finalize (GAME g)
4415 struct userdata_s *d = g->data;
4417 d->mode = MODE_PLAY;
4418 update_status_notify (g, NULL);
4419 d->rotate = FALSE;
4420 d->go_move = 0;
4423 void
4424 do_new_game_from_scratch (WIN * win)
4426 wchar_t str[] = { win->c, 0 };
4428 if (wcscmp (str, yes_wchar))
4429 return;
4431 stop_clock ();
4432 free_userdata ();
4433 pgn_parse (NULL);
4434 gp = game[gindex];
4435 add_custom_tags (&gp->tag);
4436 init_userdata ();
4437 loadfile[0] = 0;
4438 do_new_game_finalize (gp);
4441 void
4442 do_new_game ()
4444 pgn_new_game ();
4445 gp = game[gindex];
4446 add_custom_tags (&gp->tag);
4447 init_userdata_once (gp, gindex);
4448 do_new_game_finalize (gp);
4451 void
4452 do_game_delete_finalize (int n)
4454 struct userdata_s *d;
4456 delete_game ((!n) ? gindex : -1);
4457 d = gp->data;
4458 if (d->mode != MODE_EDIT)
4459 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4462 void
4463 do_game_delete_confirm (WIN * win)
4465 int *n;
4466 wchar_t str[] = { win->c, 0 };
4468 if (wcscmp (str, yes_wchar))
4470 free (win->data);
4471 return;
4474 n = (int *) win->data;
4475 do_game_delete_finalize (*n);
4476 free (win->data);
4479 void
4480 do_game_delete ()
4482 char *tmp = NULL;
4483 int i, n;
4484 struct userdata_s *d;
4485 int *p;
4487 if (gtotal < 2)
4489 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4490 return;
4493 tmp = NULL;
4495 for (i = n = 0; i < gtotal; i++)
4497 d = game[i]->data;
4499 if (TEST_FLAG (d->flags, CF_DELETE))
4500 n++;
4503 if (!n)
4504 tmp = _("Delete the current game?");
4505 else
4507 if (n == gtotal)
4509 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4510 return;
4513 tmp = _("Delete all games marked for deletion?");
4516 if (config.deleteprompt)
4518 p = Malloc (sizeof (int));
4519 *p = n;
4520 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, p,
4521 do_game_delete_confirm, 0, 0, NULL, "%s", tmp);
4522 return;
4525 do_game_delete_finalize (n);
4528 void
4529 do_find_game_exp_finalize (int which)
4531 struct userdata_s *d = gp->data;
4532 int n;
4534 if ((n = find_game_exp (gameexp, (which == -1) ? 0 : 1,
4535 (keycount) ? keycount : 1)) == -1)
4537 update_status_notify (gp, "%s", _("No matches found"));
4538 return;
4541 gindex = n;
4542 d = gp->data;
4544 if (pgn_history_total (gp->hp))
4545 d->mode = MODE_HISTORY;
4547 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4550 void
4551 do_find_game_exp (WIN * win)
4553 struct input_data_s *in = win->data;
4554 int *n = in->data;
4555 int c = *n;
4557 if (in->str)
4559 strncpy (gameexp, in->str, sizeof (gameexp));
4560 gameexp[sizeof (gameexp) - 1] = 0;
4562 if (c == '?')
4563 c = '}';
4565 do_find_game_exp_finalize (c);
4566 free (in->str);
4569 free (in->data);
4570 free (in);
4573 void
4574 do_game_jump_finalize (int n)
4576 struct userdata_s *d;
4578 if (--n > gtotal - 1 || n < 0)
4579 return;
4581 gindex = n;
4582 gp = game[gindex];
4583 d = gp->data;
4584 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4585 update_status_notify (gp, NULL);
4588 void
4589 do_game_jump (WIN * win)
4591 struct input_data_s *in = win->data;
4593 if (!in->str || !isinteger (in->str))
4595 if (in->str)
4596 free (in->str);
4598 free (in);
4599 return;
4602 do_game_jump_finalize (atoi (in->str));
4603 free (in->str);
4604 free (in);
4607 void
4608 do_load_file (WIN * win)
4610 struct input_data_s *in = win->data;
4611 char *tmp = in->str;
4612 struct userdata_s *d;
4613 PGN_FILE *pgn = NULL;
4614 int n;
4616 if (!in->str)
4618 free (in);
4619 return;
4622 if ((tmp = pathfix (tmp)) == NULL)
4623 goto done;
4625 n = pgn_open (tmp, "r", &pgn);
4627 if (n == E_PGN_ERR)
4629 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, strerror (errno));
4630 goto done;
4632 else if (n == E_PGN_INVALID)
4634 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp,
4635 _("Not a regular file"));
4636 goto done;
4639 free_userdata ();
4641 if (pgn_parse (pgn) == E_PGN_ERR)
4643 del_panel (loadingp);
4644 delwin (loadingw);
4645 loadingw = NULL;
4646 loadingp = NULL;
4647 init_userdata ();
4648 goto done;
4651 del_panel (loadingp);
4652 delwin (loadingw);
4653 loadingw = NULL;
4654 loadingp = NULL;
4655 init_userdata ();
4656 strncpy (loadfile, tmp, sizeof (loadfile));
4657 loadfile[sizeof (loadfile) - 1] = 0;
4658 gp = game[gindex];
4659 d = gp->data;
4661 if (pgn_history_total (gp->hp))
4662 d->mode = MODE_HISTORY;
4664 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4666 fm_loaded_file = TRUE;
4667 d->rotate = FALSE;
4669 done:
4670 pgn_close (pgn);
4672 if (in->str)
4673 free (in->str);
4675 free (in);
4678 void
4679 do_game_save (WIN * win)
4681 struct input_data_s *in = win->data;
4682 int *x = in->data;
4683 int n = *x;
4684 char *tmp = in->str;
4685 char tfile[FILENAME_MAX];
4686 char *p;
4687 int i;
4688 struct userdata_s *d;
4690 if (!tmp || (tmp = pathfix (tmp)) == NULL)
4691 goto done;
4693 if (pgn_is_compressed (tmp) == E_PGN_ERR)
4695 p = tmp + strlen (tmp) - 1;
4697 if (*p != 'n' || *(p - 1) != 'g' || *(p - 2) != 'p' || *(p - 3) != '.')
4699 snprintf (tfile, sizeof (tfile), "%s.pgn", tmp);
4700 tmp = tfile;
4705 * When in edit mode, update the FEN tag.
4707 if (n == -1)
4709 for (i = 0; i < gtotal; i++)
4711 d = game[i]->data;
4713 if (d->mode == MODE_EDIT)
4715 char *fen = pgn_game_to_fen (game[i], d->b);
4717 pgn_tag_add (&game[i]->tag, (char *) "FEN", fen);
4718 free (fen);
4722 else
4724 d = game[n]->data;
4726 if (d->mode == MODE_EDIT)
4728 char *fen = pgn_game_to_fen (game[n], d->b);
4730 pgn_tag_add (&game[n]->tag, (char *) "FEN", fen);
4731 free (fen);
4735 save_pgn (tmp, n);
4737 done:
4738 if (in->str)
4739 free (in->str);
4741 free (in->data);
4742 free (in);
4745 void
4746 do_get_game_save_input (int n)
4748 struct input_data_s *in = Calloc (1, sizeof (struct input_data_s));
4749 int *p = Malloc (sizeof (int));
4751 in->efunc = do_game_save;
4752 *p = n;
4753 in->data = p;
4755 construct_input (_("Save Game Filename"), loadfile, 1, 1,
4756 _("Type TAB for file browser"), file_browser, NULL, '\t',
4757 in, INPUT_HIST_FILE, NULL, -1);
4760 void
4761 do_game_save_multi_confirm (WIN * win)
4763 int i;
4764 wchar_t str[] = { win->c, 0 };
4766 if (!wcscmp (str, current_wchar))
4767 i = gindex;
4768 else if (!wcscmp (str, all_wchar))
4769 i = -1;
4770 else
4772 update_status_notify (gp, "%s", _("Save game aborted."));
4773 return;
4776 do_get_game_save_input (i);
4779 void
4780 do_global_about ()
4782 cmessage (_("ABOUT"), ANY_KEY_STR,
4783 _("%s\nUsing %s with %i colors and %i color pairs\n%s\n%s"),
4784 PACKAGE_STRING, curses_version (), COLORS, COLOR_PAIRS,
4785 COPYRIGHT, CBOARD_URL);
4788 void
4789 global_game_next_prev (int which)
4791 struct userdata_s *d;
4793 game_next_prev (gp, (which == 1) ? 1 : 0, (keycount) ? keycount : 1);
4794 d = gp->data;
4796 if (delete_count)
4798 if (which == 1)
4800 markend = markstart + delete_count;
4801 delete_count = 0;
4803 else
4805 markend = markstart - delete_count + 1;
4806 delete_count = -1; // to fix gindex in the other direction
4809 fix_marks (&markstart, &markend);
4810 do_global_toggle_delete ();
4813 if (d->mode == MODE_HISTORY)
4814 pgn_board_update (gp, d->b, gp->hindex);
4815 else if (d->mode == MODE_PLAY)
4816 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4819 void
4820 do_global_next_game ()
4822 global_game_next_prev (1);
4825 void
4826 do_global_prev_game ()
4828 global_game_next_prev (0);
4831 void
4832 global_find (int which)
4834 struct input_data_s *in;
4835 int *p;
4837 if (gtotal < 2)
4838 return;
4840 in = Calloc (1, sizeof (struct input_data_s));
4841 p = Malloc (sizeof (int));
4842 *p = which;
4843 in->data = p;
4844 in->efunc = do_find_game_exp;
4846 if (!*gameexp || which == 0)
4848 construct_input (_("Find Game by Tag Expression"), NULL, 1, 0,
4849 _("[name expression:]value expression"), NULL, NULL, 0,
4850 in, INPUT_HIST_GAME_EXP, NULL, -1);
4851 return;
4854 free (p);
4855 free (in);
4856 do_find_game_exp_finalize (which);
4859 void
4860 do_global_find_new ()
4862 global_find (0);
4865 void
4866 do_global_find_next ()
4868 global_find (1);
4871 void
4872 do_global_find_prev ()
4874 global_find (-1);
4877 void
4878 do_global_game_jump ()
4880 if (gtotal < 2)
4881 return;
4883 if (!keycount)
4885 struct input_data_s *in;
4887 in = Calloc (1, sizeof (struct input_data_s));
4888 in->efunc = do_game_jump;
4889 construct_input (_("Jump to Game Number"), NULL, 1, 1, NULL, NULL, NULL,
4890 0, in, -1, NULL, 0);
4891 return;
4894 do_game_jump_finalize (keycount);
4897 void
4898 do_global_toggle_delete ()
4900 int i;
4902 pushkey = 0;
4904 if (gtotal < 2)
4905 return;
4907 if (keycount && delete_count == 0)
4909 markstart = gindex;
4910 delete_count = keycount;
4911 update_status_notify (gp, "%s (delete)", status.notify);
4912 return;
4915 if (markstart >= 0 && markend >= 0)
4917 for (i = markstart; i < markend; i++)
4919 if (toggle_delete_flag (i))
4921 return;
4925 gindex = (delete_count < 0) ? markstart : i - 1;
4927 else
4929 if (toggle_delete_flag (gindex))
4930 return;
4933 markstart = markend = -1;
4934 delete_count = 0;
4935 update_status_window (gp);
4938 void
4939 do_global_delete_game ()
4941 do_game_delete ();
4944 void
4945 do_global_tag_edit ()
4947 struct userdata_s *d = gp->data;
4949 edit_tags (gp, d->b, 1);
4952 void
4953 do_global_tag_view ()
4955 struct userdata_s *d = gp->data;
4957 edit_tags (gp, d->b, 0);
4960 void
4961 do_global_resume_game ()
4963 struct input_data_s *in;
4965 in = Calloc (1, sizeof (struct input_data_s));
4966 in->efunc = do_load_file;
4967 construct_input (_("Load Filename"), NULL, 1, 1,
4968 _("Type TAB for file browser"), file_browser, NULL, '\t',
4969 in, INPUT_HIST_FILE, NULL, -1);
4972 void
4973 do_global_save_game ()
4975 if (gtotal > 1)
4977 construct_message (NULL, _("What would you like to do?"), 0, 1,
4978 NULL, NULL, NULL, do_game_save_multi_confirm, 0, 0,
4979 NULL, _
4980 ("There is more than one game loaded. Press \"%ls\" to save the current game, \"%ls\" to save all games or any other key to cancel."),
4981 current_wchar, all_wchar);
4982 return;
4985 do_get_game_save_input (-1);
4988 void
4989 do_global_new_game ()
4991 do_new_game ();
4994 void
4995 do_global_copy_game ()
4997 int g = gindex;
4998 int i, n;
4999 struct userdata_s *d;
5001 do_global_new_game ();
5002 d = gp->data;
5003 n = pgn_tag_total (game[g]->tag);
5005 for (i = 0; i < n; i++)
5006 pgn_tag_add (&gp->tag, game[g]->tag[i]->name, game[g]->tag[i]->value);
5008 pgn_board_init_fen (gp, d->b, NULL);
5009 n = pgn_history_total (game[g]->history);
5011 // FIXME RAV
5012 for (i = 0; i < n; i++)
5014 char *frfr = NULL;
5015 char *move = strdup (game[g]->history[i]->move);
5017 if (pgn_parse_move (gp, d->b, &move, &frfr) != E_PGN_OK)
5019 free (move);
5020 SET_FLAG (gp->flags, GF_PERROR);
5021 return;
5024 pgn_history_add (gp, d->b, move);
5025 free (move);
5026 free (frfr);
5027 pgn_switch_turn (gp);
5030 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
5033 void
5034 do_global_new_all ()
5036 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
5037 do_new_game_from_scratch, 0, 0, NULL, "%s",
5038 _("Really start a new game from scratch?"));
5041 void
5042 do_quit (WIN * win)
5044 wchar_t str[] = { win->c, 0 };
5045 int n = wcscmp (str, yes_wchar);
5047 if (n)
5048 return;
5050 quit = 1;
5053 void
5054 do_global_quit ()
5056 if (config.exitdialogbox)
5057 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
5058 do_quit, 0, 0, NULL, "%s", _("Want to Quit?"));
5059 else
5060 quit = 1;
5063 void
5064 do_global_toggle_engine_window ()
5066 if (!enginew)
5068 enginew = newwin (LINES, COLS, 0, 0);
5069 enginep = new_panel (enginew);
5070 window_draw_title (enginew, _("Engine IO Window"), COLS,
5071 CP_MESSAGE_TITLE, CP_MESSAGE_BORDER);
5072 hide_panel (enginep);
5075 if (panel_hidden (enginep))
5077 update_engine_window (gp);
5078 top_panel (enginep);
5080 else
5082 hide_panel (enginep);
5086 void
5087 do_global_toggle_board_details ()
5089 do_board_details ();
5092 void
5093 do_global_toggle_strict_castling ()
5095 do_toggle_strict_castling ();
5098 // Global and other keys.
5099 static int
5100 globalkeys ()
5102 struct userdata_s *d = gp->data;
5103 int i;
5106 * These cannot be modified and other game mode keys cannot conflict with
5107 * these.
5109 switch (input_c)
5111 case KEY_ESCAPE:
5112 d->sp.icon = d->sp.srow = d->sp.scol = 0;
5113 markend = markstart = 0;
5115 if (keycount)
5117 keycount = 0;
5118 update_status_notify (gp, NULL);
5121 if (config.validmoves)
5122 pgn_reset_valid_moves (d->b);
5124 return 1;
5125 case '0' ... '9':
5126 i = input_c - '0';
5128 if (keycount)
5129 keycount = keycount * 10 + i;
5130 else
5131 keycount = i;
5133 update_status_notify (gp, _("Repeat %i"), keycount);
5134 return -1;
5135 case KEY_UP:
5136 if (d->mode == MODE_HISTORY)
5137 return 0;
5139 if (keycount)
5140 d->c_row += keycount;
5141 else
5142 d->c_row++;
5144 if (d->c_row > 8)
5145 d->c_row = 1;
5147 return 1;
5148 case KEY_DOWN:
5149 if (d->mode == MODE_HISTORY)
5150 return 0;
5152 if (keycount)
5154 d->c_row -= keycount;
5155 update_status_notify (gp, NULL);
5157 else
5158 d->c_row--;
5160 if (d->c_row < 1)
5161 d->c_row = 8;
5163 return 1;
5164 case KEY_LEFT:
5165 if (d->mode == MODE_HISTORY)
5166 return 0;
5168 if (keycount)
5169 d->c_col -= keycount;
5170 else
5171 d->c_col--;
5173 if (d->c_col < 1)
5174 d->c_col = 8;
5176 return 1;
5177 case KEY_RIGHT:
5178 if (d->mode == MODE_HISTORY)
5179 return 0;
5181 if (keycount)
5182 d->c_col += keycount;
5183 else
5184 d->c_col++;
5186 if (d->c_col > 8)
5187 d->c_col = 1;
5189 return 1;
5190 case KEY_RESIZE:
5191 return 1;
5192 case 0:
5193 default:
5194 for (i = 0; global_keys[i]; i++)
5196 if (input_c == global_keys[i]->c && global_keys[i]->f)
5198 (*global_keys[i]->f) ();
5199 return 1;
5202 break;
5205 return 0;
5208 #ifdef WITH_LIBPERL
5209 static void
5210 perl_error (const char *fmt, ...)
5212 va_list ap;
5213 char *buf;
5215 va_start (ap, fmt);
5216 vasprintf (&buf, fmt, ap);
5217 va_end (ap);
5219 message (ERROR_STR, ANY_KEY_STR, "%s", buf);
5220 free (buf);
5223 static void
5224 do_perl_finalize (WIN * win)
5226 struct input_data_s *in = win->data;
5227 GAME g = in->data;
5228 struct userdata_s *d = g->data;
5229 char *filename;
5230 char *result = NULL;
5231 char *arg = NULL;
5232 int n;
5234 asprintf (&filename, "%s/perl.pl", config.datadir);
5236 if (!in->str)
5237 goto done;
5239 if (perl_init_file (filename, perl_error))
5240 goto done;
5242 arg = pgn_game_to_fen (g, d->b);
5244 if (perl_call_sub (trim (in->str), arg, &result))
5245 goto done;
5247 d->perlfen = pgn_game_to_fen (g, d->b);
5248 d->perlflags = g->flags;
5250 if (pgn_board_init_fen (g, d->b, result) != E_PGN_OK)
5252 message (ERROR_STR, ANY_KEY_STR, "%s", _("FEN parse error."));
5253 pgn_board_init_fen (g, d->b, d->perlfen);
5254 g->flags = d->perlflags;
5255 free (d->perlfen);
5256 d->perlfen = NULL;
5257 goto done;
5260 SET_FLAG (d->flags, CF_PERL);
5261 n = pgn_tag_find (g->tag, "FEN");
5263 if (n != E_PGN_ERR)
5264 d->oldfen = strdup (g->tag[n]->value);
5266 pgn_tag_add (&g->tag, (char *) "FEN", result);
5267 update_status_notify (g, "%s", ANY_KEY_STR);
5268 update_all (g);
5270 done:
5271 free (result);
5272 free (arg);
5273 free (in->str);
5274 free (in);
5275 free (filename);
5278 void
5279 do_global_perl ()
5281 struct input_data_s *in;
5283 in = Calloc (1, sizeof (struct input_data_s));
5284 in->data = gp;
5285 in->efunc = do_perl_finalize;
5286 construct_input (_("PERL Subroutine Filter"), NULL, 1, 0, NULL, NULL, NULL,
5287 0, in, INPUT_HIST_PERL, NULL, -1);
5289 #endif
5292 * A macro may contain a key that belongs to another macro so macro_match will
5293 * need to be updated to the new index of the matching macro.
5295 static void
5296 find_macro (struct userdata_s *d)
5298 int i;
5301 * Macros can't contain macros when in a window.
5303 if (wins)
5304 return;
5306 again:
5307 for (i = 0; macros[i]; i++)
5309 if ((macros[i]->mode == -1 || macros[i]->mode == d->mode) &&
5310 input_c == macros[i]->c)
5312 input_c = macros[i]->keys[macros[i]->n++];
5314 if (!macro_depth_n && macro_match > -1)
5316 macro_depth =
5317 realloc (macro_depth, (macro_depth_n + 1) * sizeof (int));
5318 macro_depth[macro_depth_n++] = macro_match;
5321 macro_depth =
5322 realloc (macro_depth, (macro_depth_n + 1) * sizeof (int));
5323 macro_depth[macro_depth_n++] = i;
5324 macro_match = i;
5325 goto again;
5331 * Resets the position in each macro to the first key.
5333 static void
5334 reset_macros ()
5336 int i;
5337 struct userdata_s *d = gp->data;
5339 again:
5340 if (macro_depth_n > 0)
5342 macro_depth_n--;
5343 macro_match = macro_depth[macro_depth_n];
5345 if (macros[macro_match]->n >= macros[macro_match]->total)
5346 goto again;
5348 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
5349 find_macro (d);
5350 return;
5353 for (i = 0; macros[i]; i++)
5354 macros[i]->n = 0;
5356 free (macro_depth);
5357 macro_depth = NULL;
5358 macro_depth_n = 0;
5359 macro_match = -1;
5362 void
5363 game_loop ()
5365 struct userdata_s *d;
5367 macro_match = -1;
5368 gindex = gtotal - 1;
5369 gp = game[gindex];
5370 d = gp->data;
5372 if (pgn_history_total (gp->hp))
5373 d->mode = MODE_HISTORY;
5374 else
5376 d->mode = MODE_PLAY;
5377 d->play_mode = PLAY_HE;
5380 d->rotate = FALSE;
5381 d->go_move = 0;
5382 d->pm_undo = FALSE;
5383 d->pm_frfr[0] = '\0';
5385 if (d->mode == MODE_HISTORY)
5386 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
5388 update_status_notify (gp, "%s", _("Type F1 for help"));
5389 movestep = 2;
5390 flushinp ();
5391 update_all (gp);
5392 wtimeout (boardw, WINDOW_TIMEOUT);
5394 while (!quit)
5396 int n = 0, i;
5397 char fdbuf[8192] = { 0 };
5398 int len;
5399 struct timeval tv = { 0, 0 };
5400 fd_set rfds, wfds;
5401 WIN *win = NULL;
5402 WINDOW *wp = NULL;
5404 FD_ZERO (&rfds);
5405 FD_ZERO (&wfds);
5407 for (i = 0; i < gtotal; i++)
5409 d = game[i]->data;
5411 if (d->engine && d->engine->pid != -1)
5413 if (d->engine->fd[ENGINE_IN_FD] > 2)
5415 if (d->engine->fd[ENGINE_IN_FD] > n)
5416 n = d->engine->fd[ENGINE_IN_FD];
5418 FD_SET (d->engine->fd[ENGINE_IN_FD], &rfds);
5421 if (d->engine->fd[ENGINE_OUT_FD] > 2)
5423 if (d->engine->fd[ENGINE_OUT_FD] > n)
5424 n = d->engine->fd[ENGINE_OUT_FD];
5426 FD_SET (d->engine->fd[ENGINE_OUT_FD], &wfds);
5431 if (n)
5433 if ((n = select (n + 1, &rfds, &wfds, NULL, &tv)) > 0)
5435 for (i = 0; i < gtotal; i++)
5437 d = game[i]->data;
5439 if (d->engine && d->engine->pid != -1)
5441 if (FD_ISSET (d->engine->fd[ENGINE_IN_FD], &rfds))
5443 len = read (d->engine->fd[ENGINE_IN_FD], fdbuf,
5444 sizeof (fdbuf));
5446 if (len > 0)
5448 if (d->engine->iobuf)
5449 d->engine->iobuf =
5450 Realloc (d->engine->iobuf,
5451 d->engine->len + len + 1);
5452 else
5453 d->engine->iobuf = Calloc (1, len + 1);
5455 memcpy (&(d->engine->iobuf[d->engine->len]),
5456 &fdbuf, len);
5457 d->engine->len += len;
5458 d->engine->iobuf[d->engine->len] = 0;
5461 * The fdbuf is full or no newline
5462 * was found. So we'll append the next
5463 * read() to this games buffer.
5465 if (d->engine->iobuf[d->engine->len - 1] !=
5466 '\n')
5467 continue;
5469 parse_engine_output (game[i], d->engine->iobuf);
5470 free (d->engine->iobuf);
5471 d->engine->iobuf = NULL;
5472 d->engine->len = 0;
5474 else if (len == -1)
5476 if (errno != EAGAIN)
5478 cmessage (ERROR_STR, ANY_KEY_STR,
5479 "Engine read(): %s",
5480 strerror (errno));
5481 waitpid (d->engine->pid, &n, 0);
5482 free (d->engine);
5483 d->engine = NULL;
5484 break;
5489 if (FD_ISSET (d->engine->fd[ENGINE_OUT_FD], &wfds))
5491 if (d->engine->queue)
5492 send_engine_command (game[i]);
5497 else
5499 if (n == -1)
5500 cmessage (ERROR_STR, ANY_KEY_STR, "select(): %s",
5501 strerror (errno));
5502 /* timeout */
5506 gp = game[gindex];
5507 d = gp->data;
5510 * This is needed to detect terminal resizing.
5512 doupdate ();
5513 if (LINES != LINES_OLD || COLS != COLS_OLD)
5515 COLS_OLD = COLS;
5516 LINES_OLD = LINES;
5517 do_window_resize ();
5521 * Finds the top level window in the window stack so we know what
5522 * window the wget_wch()'ed key belongs to.
5524 if (wins)
5526 for (i = 0; wins[i]; i++);
5527 win = wins[i - 1];
5528 wp = win->w;
5529 wtimeout (wp, WINDOW_TIMEOUT);
5531 else
5532 wp = boardw;
5534 if (!i && pushkey)
5535 input_c = pushkey;
5536 else
5538 if (!pushkey)
5540 if (macros && macro_match >= 0)
5542 if (macros[macro_match]->n >= macros[macro_match]->total)
5543 reset_macros ();
5544 else
5546 input_c =
5547 macros[macro_match]->keys[macros[macro_match]->n++];
5548 find_macro (d);
5551 else
5553 if (wget_wch (wp, &input_c) == ERR || input_c == KEY_RESIZE)
5555 if (input_c == KEY_RESIZE)
5557 if (win)
5558 win->c = input_c;
5560 window_resize_all ();
5561 update_all (gp);
5563 continue;
5567 else
5568 input_c = pushkey;
5570 if (win)
5572 win->c = input_c;
5575 * Run the function associated with the window. When the
5576 * function returns 0 win->efunc is ran (if not NULL) with
5577 * win as the one and only parameter. Then the window is
5578 * destroyed.
5580 * The exit function may create another window which will
5581 * mess up the window stack when window_destroy() is called.
5582 * So don't destory the window until the top window is
5583 * destroyable. See window_destroy().
5585 if ((*win->func) (win) == 0)
5587 if (win->efunc)
5588 (*win->efunc) (win);
5590 win->keep = 1;
5591 window_destroy (win);
5592 update_all (gp);
5595 continue;
5599 if (!keycount && status.notify)
5600 update_status_notify (gp, NULL);
5602 #ifdef WITH_LIBPERL
5603 if (TEST_FLAG (d->flags, CF_PERL))
5605 CLEAR_FLAG (d->flags, CF_PERL);
5606 pgn_board_init_fen (gp, d->b, d->perlfen);
5607 gp->flags = d->perlflags;
5608 free (d->perlfen);
5609 pgn_tag_add (&gp->tag, (char *) "FEN", d->oldfen);
5610 free (d->oldfen);
5611 d->perlfen = d->oldfen = NULL;
5612 update_all (gp);
5613 continue;
5615 #endif
5617 if (macros && macro_match < 0)
5618 find_macro (d);
5620 if ((n = globalkeys ()) == 1)
5622 if (macro_match == -1)
5623 keycount = 0;
5625 goto refresh;
5627 else if (n == -1)
5628 goto refresh;
5630 switch (d->mode)
5632 case MODE_EDIT:
5633 for (i = 0; edit_keys[i]; i++)
5635 if (input_c == edit_keys[i]->c)
5637 (*edit_keys[i]->f) ();
5638 break;
5641 break;
5642 case MODE_PLAY:
5643 for (i = 0; play_keys[i]; i++)
5645 if (input_c == play_keys[i]->c)
5647 (*play_keys[i]->f) ();
5648 goto done;
5652 do_play_config_command ();
5653 break;
5654 case MODE_HISTORY:
5655 for (i = 0; history_keys[i]; i++)
5657 if (input_c == history_keys[i]->c)
5659 (*history_keys[i]->f) ();
5660 break;
5663 break;
5664 default:
5665 break;
5668 done:
5669 if (keycount)
5670 update_status_notify (gp, NULL);
5672 keycount = 0;
5674 refresh:
5675 update_all (gp);
5679 void
5680 usage (const char *pn, int ret)
5682 fprintf ((ret) ? stderr : stdout, "%s%s",
5683 #ifdef DEBUG
5684 _("Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
5685 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"),
5686 #else
5687 _("Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"),
5688 #endif
5689 _(" -p Load PGN file.\n"
5690 " -V Validate a game file.\n"
5691 " -S Validate and output a PGN formatted game.\n"
5692 " -R Like -S but write a reduced PGN formatted game.\n"
5693 " -t Also write custom PGN tags from config file.\n"
5694 " -E Stop processing on file parsing error (overrides config).\n"
5695 " -C Enable strict castling (overrides config).\n"
5696 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, overrides config).\n"
5697 " -v Version information.\n" " -h This help text.\n"));
5699 exit (ret);
5702 void
5703 cleanup_all ()
5705 int i;
5707 stop_clock ();
5708 free_userdata ();
5709 pgn_free_all ();
5710 free (config.engine_cmd);
5711 free (config.pattern);
5712 free (config.ccfile);
5713 free (config.nagfile);
5714 free (config.configfile);
5716 if (config.keys)
5718 for (i = 0; config.keys[i]; i++)
5720 free (config.keys[i]->str);
5721 free (config.keys[i]);
5724 free (config.keys);
5727 if (config.einit)
5729 for (i = 0; config.einit[i]; i++)
5730 free (config.einit[i]);
5732 free (config.einit);
5735 if (config.tag)
5736 pgn_tag_free (config.tag);
5738 free (config.datadir);
5740 if (curses_initialized)
5742 del_panel (boardp);
5743 del_panel (historyp);
5744 del_panel (statusp);
5745 del_panel (tagp);
5746 delwin (boardw);
5747 delwin (historyw);
5748 delwin (statusw);
5749 delwin (tagw);
5751 if (enginew)
5753 del_panel (enginep);
5754 delwin (enginew);
5757 endwin ();
5760 #ifdef WITH_LIBPERL
5761 perl_cleanup ();
5762 #endif
5765 static void
5766 signal_save_pgn (int sig)
5768 char *buf;
5769 time_t now;
5770 char *p = config.savedirectory ? config.savedirectory : config.datadir;
5772 time (&now);
5773 asprintf (&buf, "%s/signal-%i-%li.pgn", p, sig, now);
5775 if (do_game_write (buf, "w", 0, gtotal))
5777 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", p, strerror (errno));
5778 update_status_notify (gp, "%s", _("Save game failed."));
5781 free (buf);
5782 quit = 1;
5785 void
5786 catch_signal (int which)
5788 switch (which)
5790 case SIGALRM:
5791 update_clocks ();
5792 break;
5793 case SIGPIPE:
5794 if (which == SIGPIPE && quit)
5795 break;
5797 if (which == SIGPIPE)
5798 cmessage (NULL, ANY_KEY_STR, "%s", _("Broken pipe. Quitting."));
5800 cleanup_all ();
5801 exit (EXIT_FAILURE);
5802 break;
5803 case SIGSTOP:
5804 savetty ();
5805 break;
5806 case SIGCONT:
5807 resetty ();
5808 do_window_resize ();
5809 keypad (boardw, TRUE);
5810 break;
5811 case SIGINT:
5812 quit = 1;
5813 break;
5814 case SIGTERM:
5815 signal_save_pgn (which);
5816 break;
5817 default:
5818 break;
5822 void
5823 loading_progress (long total, long offset)
5825 int n = (100 * (offset / 100) / (total / 100));
5827 if (curses_initialized)
5828 update_loading_window (n);
5829 else
5831 fprintf (stderr, _("Loading... %i%% (%i games)%c"), n, gtotal, '\r');
5832 fflush (stderr);
5836 static void
5837 set_defaults ()
5839 set_config_defaults ();
5840 set_default_keys ();
5841 filetype = FILE_NONE;
5842 pgn_config_set (PGN_PROGRESS, 1024);
5843 pgn_config_set (PGN_PROGRESS_FUNC, loading_progress);
5847 main (int argc, char *argv[])
5849 int opt;
5850 struct stat st;
5851 char buf[FILENAME_MAX];
5852 char datadir[FILENAME_MAX];
5853 int ret = EXIT_SUCCESS;
5854 int validate_only = 0, validate_and_write = 0;
5855 int write_custom_tags = 0;
5856 int i = 0;
5857 PGN_FILE *pgn;
5858 int utf8_pieces = -1;
5860 setlocale (LC_ALL, "");
5861 bindtextdomain ("cboard", LOCALE_DIR);
5862 textdomain ("cboard");
5864 /* Solaris 5.9 */
5865 #ifndef HAVE_PROGNAME
5866 __progname = argv[0];
5867 #endif
5869 if ((config.pwd = getpwuid (getuid ())) == NULL)
5870 err (EXIT_FAILURE, "getpwuid()");
5872 snprintf (datadir, sizeof (datadir), "%s/.cboard", config.pwd->pw_dir);
5873 config.datadir = strdup (datadir);
5874 snprintf (buf, sizeof (buf), "%s/cc.data", datadir);
5875 config.ccfile = strdup (buf);
5876 snprintf (buf, sizeof (buf), "%s/nag.data", datadir);
5877 config.nagfile = strdup (buf);
5878 snprintf (buf, sizeof (buf), "%s/config", datadir);
5879 config.configfile = strdup (buf);
5881 if (stat (datadir, &st) == -1)
5883 if (errno == ENOENT)
5885 if (mkdir (datadir, 0755) == -1)
5886 err (EXIT_FAILURE, "%s", datadir);
5888 else
5889 err (EXIT_FAILURE, "%s", datadir);
5891 stat (datadir, &st);
5894 if (!S_ISDIR (st.st_mode))
5895 errx (EXIT_FAILURE, "%s: %s", datadir, _("Not a directory."));
5897 set_defaults ();
5899 #ifdef DEBUG
5900 while ((opt = getopt (argc, argv, "DCEVtSRhp:vu::")) != -1)
5902 #else
5903 while ((opt = getopt (argc, argv, "ECVtSRhp:vu::")) != -1)
5905 #endif
5906 switch (opt)
5908 #ifdef DEBUG
5909 case 'D':
5910 unlink ("libchess.debug");
5911 pgn_config_set (PGN_DEBUG, 1);
5912 break;
5913 #endif
5914 case 'C':
5915 pgn_config_set (PGN_STRICT_CASTLING, 1);
5916 break;
5917 case 't':
5918 write_custom_tags = 1;
5919 break;
5920 case 'E':
5921 i = 1;
5922 break;
5923 case 'R':
5924 pgn_config_set (PGN_REDUCED, 1);
5925 case 'S':
5926 validate_and_write = 1;
5927 case 'V':
5928 validate_only = 1;
5929 break;
5930 case 'v':
5931 printf ("%s (%s)\n%s\n%s\n", PACKAGE_STRING, curses_version (),
5932 COPYRIGHT, CBOARD_URL);
5933 exit (EXIT_SUCCESS);
5934 case 'p':
5935 filetype = FILE_PGN;
5936 strncpy (loadfile, optarg, sizeof (loadfile));
5937 loadfile[sizeof (loadfile) - 1] = 0;
5938 break;
5939 case 'u':
5940 utf8_pieces = optarg ? atoi (optarg) : 1;
5941 break;
5942 case 'h':
5943 default:
5944 usage (argv[0], EXIT_SUCCESS);
5948 if ((validate_only || validate_and_write) && !*loadfile)
5949 usage (argv[0], EXIT_FAILURE);
5951 if (access (config.configfile, R_OK) == 0)
5952 parse_rcfile (config.configfile);
5954 if (i)
5955 pgn_config_set (PGN_STOP_ON_ERROR, 1);
5957 signal (SIGPIPE, catch_signal);
5958 signal (SIGCONT, catch_signal);
5959 signal (SIGSTOP, catch_signal);
5960 signal (SIGINT, catch_signal);
5961 signal (SIGALRM, catch_signal);
5962 signal (SIGTERM, catch_signal);
5963 signal (SIGCHLD, SIG_IGN);
5965 srandom (getpid ());
5967 switch (filetype)
5969 case FILE_PGN:
5970 if (pgn_open (loadfile, "r", &pgn) != E_PGN_OK)
5971 err (EXIT_FAILURE, "%s", loadfile);
5973 ret = pgn_parse (pgn);
5974 pgn_close (pgn);
5975 break;
5976 case FILE_FEN:
5977 //ret = parse_fen_file(loadfile);
5978 break;
5979 case FILE_EPD: // Not implemented.
5980 case FILE_NONE:
5981 default:
5982 // No file specified. Empty game.
5983 ret = pgn_parse (NULL);
5984 gp = game[gindex];
5985 add_custom_tags (&gp->tag);
5986 break;
5989 if (validate_only || validate_and_write)
5991 if (validate_and_write)
5993 if (pgn_open ("-", "r", &pgn) != E_PGN_OK)
5994 err (EXIT_FAILURE, "pgn_open()");
5996 for (i = 0; i < gtotal; i++)
5998 if (write_custom_tags)
5999 add_custom_tags (&game[i]->tag);
6001 pgn_write (pgn, game[i]);
6004 pgn_close (pgn);
6006 fm_loaded_file = TRUE;
6009 cleanup_all ();
6010 exit (ret);
6012 else if (ret == E_PGN_ERR)
6013 exit (ret);
6015 if (utf8_pieces != -1)
6016 config.utf8_pieces = utf8_pieces;
6018 init_wchar_pieces ();
6019 yes_wchar = str_to_wchar (_("y"));
6020 all_wchar = str_to_wchar (_("a"));
6021 overwrite_wchar = str_to_wchar (_("o"));
6022 resume_wchar = str_to_wchar (_("r"));
6023 current_wchar = str_to_wchar (_("c"));
6024 append_wchar = str_to_wchar (_("a"));
6025 translatable_tag_names[0] = _("Event");
6026 translatable_tag_names[1] = _("Site");
6027 translatable_tag_names[2] = _("Date");
6028 translatable_tag_names[3] = _("Round");
6029 translatable_tag_names[4] = _("White");
6030 translatable_tag_names[5] = _("Black");
6031 translatable_tag_names[6] = _("Result");
6032 init_userdata ();
6035 * This fixes window resizing in an xterm.
6037 if (getenv ("DISPLAY") != NULL)
6039 putenv ((char *) "LINES=");
6040 putenv ((char *) "COLUMNS=");
6043 if (initscr () == NULL)
6044 errx (EXIT_FAILURE, "%s", _("Could not initialize curses."));
6045 else
6046 curses_initialized = 1;
6048 if (LINES < 23 || COLS < 74)
6050 endwin ();
6051 errx (EXIT_FAILURE, _("Need at least an 74x23 terminal."));
6054 COLS_OLD = COLS;
6055 LINES_OLD = LINES;
6057 if (has_colors () == TRUE && start_color () == OK)
6058 init_color_pairs ();
6060 boardw = newwin (BOARD_HEIGHT, BOARD_WIDTH, 0, COLS - BOARD_WIDTH);
6061 boardp = new_panel (boardw);
6062 historyw = newwin (HISTORY_HEIGHT, HISTORY_WIDTH, LINES - HISTORY_HEIGHT,
6063 COLS - HISTORY_WIDTH);
6064 historyp = new_panel (historyw);
6065 statusw = newwin (STATUS_HEIGHT, STATUS_WIDTH, 0, 0);
6066 statusp = new_panel (statusw);
6067 tagw = newwin (TAG_HEIGHT, TAG_WIDTH, STATUS_HEIGHT + 1, 0);
6068 tagp = new_panel (tagw);
6069 keypad (boardw, TRUE);
6070 // leaveok(boardw, TRUE);
6071 leaveok (tagw, TRUE);
6072 leaveok (statusw, TRUE);
6073 leaveok (historyw, TRUE);
6074 curs_set (0);
6075 cbreak ();
6076 noecho ();
6077 draw_window_decor ();
6078 game_loop ();
6079 cleanup_all ();
6080 free (w_pawn_wchar);
6081 free (w_rook_wchar);
6082 free (w_bishop_wchar);
6083 free (w_knight_wchar);
6084 free (w_queen_wchar);
6085 free (w_king_wchar);
6086 free (b_pawn_wchar);
6087 free (b_rook_wchar);
6088 free (b_bishop_wchar);
6089 free (b_knight_wchar);
6090 free (b_queen_wchar);
6091 free (b_king_wchar);
6092 free (empty_wchar);
6093 free (enpassant_wchar);
6094 free (yes_wchar);
6095 free (all_wchar);
6096 free (overwrite_wchar);
6097 free (resume_wchar);
6098 free (current_wchar);
6099 free (append_wchar);
6100 free (status.notify);
6101 exit (EXIT_SUCCESS);