Fix build failure.
[cboard.git] / src / cboard.c
blob3291377a021d4ecb3c3b4105904fd0165af892c9
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2019 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-2019 " 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 *);
214 static void do_play_help ();
215 static void do_history_help ();
216 static void do_edit_help ();
218 GAME gp;
220 void
221 coordofmove (GAME g, char *move, char *prow, char *pcol)
223 char l = strlen (move);
225 if (*move == 'O')
227 *prow = (g->turn == WHITE) ? 8 : 1;
228 *pcol = (l <= 4) ? 7 : 3;
229 return;
232 move += l;
234 while (!isdigit (*move))
235 move--;
237 *prow = RANKTOINT (*move--);
238 *pcol = FILETOINT (*move);
241 #define INV_INT(x) (9 - x)
242 #define INV_INT0(x) (7 - x)
244 // Posición por rotación de tablero.
245 // Rotation board position.
246 void
247 rotate_position (char *prow, char *pcol)
249 *prow = INV_INT (*prow);
250 *pcol = INV_INT (*pcol);
253 void
254 update_cursor (GAME g, int idx)
256 int t = pgn_history_total (g->hp);
257 struct userdata_s *d = g->data;
260 * If not deincremented then r and c would be the next move.
262 idx--;
264 if (idx > t || idx < 0 || !t || !g->hp[idx]->move)
265 d->c_row = 2, d->c_col = 5;
266 else
267 coordofmove (g, g->hp[idx]->move, &d->c_row, &d->c_col);
269 if (d->mode == MODE_HISTORY && d->rotate)
270 rotate_position (&d->c_row, &d->c_col);
273 static int
274 init_nag ()
276 FILE *fp;
277 char line[LINE_MAX];
278 int i = 0;
280 if ((fp = fopen (config.nagfile, "r")) == NULL)
282 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", config.nagfile,
283 strerror (errno));
284 return 1;
287 nags = Realloc (nags, (i + 2) * sizeof (char *));
288 nags[i++] = strdup (_("none"));
289 nags[i] = NULL;
291 while (!feof (fp))
293 if (fscanf (fp, " %[^\n] ", line) == 1)
295 nags = Realloc (nags, (i + 2) * sizeof (char *));
296 nags[i++] = strdup (line);
300 nags[i] = NULL;
301 nag_total = i;
302 fclose (fp);
303 return 0;
306 void
307 edit_nag_toggle_item (struct menu_input_s *m)
309 struct input_s *in = m->data;
310 struct input_data_s *id = in->data;
311 HISTORY *h = id->data;
312 int i;
314 if (m->selected == 0)
316 for (i = 0; i < MAX_PGN_NAG; i++)
317 h->nag[i] = 0;
319 for (i = 0; m->items[i]; i++)
320 m->items[i]->selected = 0;
322 return;
325 for (i = 0; i < MAX_PGN_NAG; i++)
327 if (h->nag[i] == m->selected)
328 h->nag[i] = m->selected = 0;
329 else
331 if (!h->nag[i])
333 h->nag[i] = m->selected;
334 break;
340 void
341 edit_nag_save (struct menu_input_s *m)
343 pushkey = -1;
346 void
347 edit_nag_help (struct menu_input_s *m)
349 message (_("NAG Menu Keys"), ANY_KEY_STR, "%s",
350 _(" UP/DOWN - previous/next menu item\n"
351 " HOME/END - first/last menu item\n"
352 " PGDN/PGUP - next/previous page\n"
353 " a-zA-Z0-9 - jump to item\n"
354 " SPACE - toggle selected item\n"
355 " CTRL-X - quit with changes"));
358 struct menu_item_s **
359 get_nag_items (WIN * win)
361 int i, n;
362 struct menu_input_s *m = win->data;
363 struct input_s *in = m->data;
364 struct input_data_s *id = in->data;
365 struct menu_item_s **items = m->items;
366 HISTORY *h = id->data;
368 if (items)
370 for (i = 0; items[i]; i++)
371 free (items[i]);
374 for (i = 0; nags[i]; i++)
376 items = Realloc (items, (i + 2) * sizeof (struct menu_item_s *));
377 items[i] = Malloc (sizeof (struct menu_item_s));
378 items[i]->name = nags[i];
379 items[i]->value = NULL;
381 for (n = 0; n < MAX_PGN_NAG; n++)
383 if (h->nag[n] == i)
385 items[i]->selected = 1;
386 n = -1;
387 break;
391 if (n >= 0)
392 items[i]->selected = 0;
395 items[i] = NULL;
396 m->nofree = 1;
397 m->items = items;
398 return items;
401 void
402 nag_print (WIN * win)
404 struct menu_input_s *m = win->data;
406 mvwprintw (win->w, m->print_line, 1, "%-*s", win->cols - 2, m->item->name);
409 void
410 edit_nag (void *arg)
412 struct menu_key_s **keys = NULL;
414 if (!nags)
416 if (init_nag ())
417 return;
420 add_menu_key (&keys, ' ', edit_nag_toggle_item);
421 add_menu_key (&keys, CTRL_KEY ('x'), edit_nag_save);
422 add_menu_key (&keys, keycode_lookup (global_keys, do_global_help),
423 edit_nag_help);
424 construct_menu (0, 0, -1, -1, _("Numeric Annotation Glyphs"), 1,
425 get_nag_items, keys, arg, nag_print, NULL, NULL);
426 return;
429 static void *
430 view_nag (void *arg)
432 HISTORY *h = (HISTORY *) arg;
433 char buf[80];
434 char line[LINE_MAX] = { 0 };
435 int i = 0;
437 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Viewing NAG for"), h->move);
439 if (!nags)
441 if (init_nag ())
442 return NULL;
445 for (i = 0; i < MAX_PGN_NAG; i++)
447 char buf2[16];
449 if (!h->nag[i])
450 break;
452 if (h->nag[i] >= nag_total)
453 strncat (line, itoa (h->nag[i], buf2), sizeof (line) - 1);
454 else
455 strncat (line, nags[h->nag[i]], sizeof (line) - 1);
457 strncat (line, "\n", sizeof (line) - 1);
460 line[strlen (line) - 1] = 0;
461 message (buf, ANY_KEY_STR, "%s", line);
462 return NULL;
465 void
466 view_annotation (HISTORY * h)
468 char buf[MAX_SAN_MOVE_LEN + strlen (_("Viewing Annotation for")) + 4];
469 int nag = 0, comment = 0;
471 if (!h)
472 return;
474 if (h->comment && h->comment[0])
475 comment++;
477 if (h->nag[0])
478 nag++;
480 if (!nag && !comment)
481 return;
483 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Viewing Annotation for"),
484 h->move);
486 if (comment)
487 construct_message (buf,
488 (nag) ? _("Any other key to continue") : ANY_KEY_STR,
489 0, 1, (nag) ? _("Press 'n' to view NAG") : NULL,
490 (nag) ? view_nag : NULL, (nag) ? h : NULL, NULL,
491 (nag) ? 'n' : 0, 0, NULL, "%s", h->comment);
492 else
493 construct_message (buf, _("Any other key to continue"), 0, 1,
494 _("Press 'n' to view NAG"), view_nag, h, NULL, 'n', 0,
495 NULL, "%s", _("No comment text for this move"));
499 do_game_write (char *filename, const char *mode, int start, int end)
501 int i;
502 struct userdata_s *d;
503 PGN_FILE *pgn;
505 i = pgn_open (filename, mode, &pgn);
507 if (i == E_PGN_ERR)
509 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", filename, strerror (errno));
510 return 1;
512 else if (i == E_PGN_INVALID)
514 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", filename,
515 _("Not a regular file"));
516 return 1;
519 for (i = (start == -1) ? 0 : start; i < end; i++)
521 d = game[i]->data;
522 pgn_write (pgn, game[i]);
523 CLEAR_FLAG (d->flags, CF_MODIFIED);
526 if (pgn_close (pgn) != E_PGN_OK)
527 message (ERROR_STR, ANY_KEY_STR, "%s", strerror (errno));
529 if (start == -1)
531 strncpy (loadfile, filename, sizeof (loadfile));
532 loadfile[sizeof (loadfile) - 1] = 0;
535 return 0;
538 struct save_game_s
540 char *filename;
541 char *mode;
542 int start;
543 int end;
546 void
547 do_save_game_overwrite_confirm (WIN * win)
549 const char *mode = "w";
550 struct save_game_s *s = win->data;
551 wchar_t str[] = { win->c, 0 };
553 if (!wcscmp (str, append_wchar))
554 mode = "a";
555 else if (!wcscmp (str, overwrite_wchar))
556 mode = "w";
557 else
558 goto done;
560 if (do_game_write (s->filename, mode, s->start, s->end))
561 update_status_notify (gp, "%s", _("Save game failed."));
562 else
563 update_status_notify (gp, "%s", _("Game saved."));
565 done:
566 free (s->filename);
567 free (s);
570 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
571 * game index number.
573 void
574 save_pgn (char *filename, int saveindex)
576 char buf[FILENAME_MAX];
577 struct stat st;
578 int end = (saveindex == -1) ? gtotal : saveindex + 1;
579 struct save_game_s *s;
581 if (filename[0] != '/' && config.savedirectory)
583 if (stat (config.savedirectory, &st) == -1)
585 if (errno == ENOENT)
587 if (mkdir (config.savedirectory, 0755) == -1)
589 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s",
590 config.savedirectory, strerror (errno));
591 return;
594 else
596 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s",
597 config.savedirectory, strerror (errno));
598 return;
602 if (stat (config.savedirectory, &st) == -1)
604 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
605 strerror (errno));
606 return;
609 if (!S_ISDIR (st.st_mode))
611 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
612 _("Not a directory."));
613 return;
616 snprintf (buf, sizeof (buf), "%s/%s", config.savedirectory, filename);
617 filename = buf;
620 if (access (filename, W_OK) == 0)
622 s = Malloc (sizeof (struct save_game_s));
623 s->filename = strdup (filename);
624 s->start = saveindex;
625 s->end = end;
626 construct_message (NULL, _("What would you like to do?"), 0, 1, NULL,
627 NULL, s, do_save_game_overwrite_confirm, 0, 0, NULL,
628 "%s \"%s\"\nPress \"%ls\" to append to this file, \"%ls\" to overwrite or any other key to cancel.",
629 _("File exists:"), filename, append_wchar,
630 overwrite_wchar);
631 return;
634 if (do_game_write (filename, "a", saveindex, end))
635 update_status_notify (gp, "%s", _("Save game failed."));
636 else
637 update_status_notify (gp, "%s", _("Game saved."));
640 static int
641 castling_state (GAME g, BOARD b, int row, int col, int piece, int mod)
643 if (pgn_piece_to_int (piece) == ROOK && col == 7
644 && row == 7 &&
645 (TEST_FLAG (g->flags, GF_WK_CASTLE) || mod) &&
646 pgn_piece_to_int (b[7][4].icon) == KING && isupper (piece))
648 if (mod)
649 TOGGLE_FLAG (g->flags, GF_WK_CASTLE);
650 return 1;
652 else if (pgn_piece_to_int (piece) == ROOK && col == 0
653 && row == 7 &&
654 (TEST_FLAG (g->flags, GF_WQ_CASTLE) || mod) &&
655 pgn_piece_to_int (b[7][4].icon) == KING && isupper (piece))
657 if (mod)
658 TOGGLE_FLAG (g->flags, GF_WQ_CASTLE);
659 return 1;
661 else if (pgn_piece_to_int (piece) == ROOK && col == 7
662 && row == 0 &&
663 (TEST_FLAG (g->flags, GF_BK_CASTLE) || mod) &&
664 pgn_piece_to_int (b[0][4].icon) == KING && islower (piece))
666 if (mod)
667 TOGGLE_FLAG (g->flags, GF_BK_CASTLE);
668 return 1;
670 else if (pgn_piece_to_int (piece) == ROOK && col == 0
671 && row == 0 &&
672 (TEST_FLAG (g->flags, GF_BQ_CASTLE) || mod) &&
673 pgn_piece_to_int (b[0][4].icon) == KING && islower (piece))
675 if (mod)
676 TOGGLE_FLAG (g->flags, GF_BQ_CASTLE);
677 return 1;
679 else if (pgn_piece_to_int (piece) == KING && col == 4
680 && row == 7 &&
681 (mod || (pgn_piece_to_int (b[7][7].icon) == ROOK &&
682 TEST_FLAG (g->flags, GF_WK_CASTLE))
684 (pgn_piece_to_int (b[7][0].icon) == ROOK &&
685 TEST_FLAG (g->flags, GF_WQ_CASTLE))) && isupper (piece))
687 if (mod)
689 if (TEST_FLAG (g->flags, GF_WK_CASTLE) ||
690 TEST_FLAG (g->flags, GF_WQ_CASTLE))
691 CLEAR_FLAG (g->flags, GF_WK_CASTLE | GF_WQ_CASTLE);
692 else
693 SET_FLAG (g->flags, GF_WK_CASTLE | GF_WQ_CASTLE);
695 return 1;
697 else if (pgn_piece_to_int (piece) == KING && col == 4
698 && row == 0 &&
699 (mod || (pgn_piece_to_int (b[0][7].icon) == ROOK &&
700 TEST_FLAG (g->flags, GF_BK_CASTLE))
702 (pgn_piece_to_int (b[0][0].icon) == ROOK &&
703 TEST_FLAG (g->flags, GF_BQ_CASTLE))) && islower (piece))
705 if (mod)
707 if (TEST_FLAG (g->flags, GF_BK_CASTLE) ||
708 TEST_FLAG (g->flags, GF_BQ_CASTLE))
709 CLEAR_FLAG (g->flags, GF_BK_CASTLE | GF_BQ_CASTLE);
710 else
711 SET_FLAG (g->flags, GF_BK_CASTLE | GF_BQ_CASTLE);
713 return 1;
716 return 0;
719 #define IS_ENPASSANT(c) (c == 'x') ? CP_BOARD_ENPASSANT : isupper(c) ? CP_BOARD_WHITE : CP_BOARD_BLACK
720 #define ATTRS(cp) (cp & (A_BOLD|A_STANDOUT|A_BLINK|A_DIM|A_UNDERLINE|A_INVIS|A_REVERSE))
722 static void
723 init_wchar_pieces ()
725 w_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♙" : "P");
726 w_rook_wchar = str_to_wchar (config.utf8_pieces ? "♖" : "R");
727 w_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♗" : "B");
728 w_knight_wchar = str_to_wchar (config.utf8_pieces ? "♘" : "N");
729 w_queen_wchar = str_to_wchar (config.utf8_pieces ? "♕" : "Q");
730 w_king_wchar = str_to_wchar (config.utf8_pieces ? "♔" : "K");
731 b_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♟" : "p");
732 b_rook_wchar = str_to_wchar (config.utf8_pieces ? "♜" : "r");
733 b_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♝" : "b");
734 b_knight_wchar = str_to_wchar (config.utf8_pieces ? "♞" : "n");
735 b_queen_wchar = str_to_wchar (config.utf8_pieces ? "♛" : "q");
736 b_king_wchar = str_to_wchar (config.utf8_pieces ? "♚" : "k");
737 empty_wchar = str_to_wchar (" ");
738 enpassant_wchar = str_to_wchar ("x");
741 static wchar_t *
742 piece_to_wchar (unsigned char p)
744 switch (p)
746 case 'P':
747 return w_pawn_wchar;
748 case 'p':
749 return b_pawn_wchar;
750 case 'R':
751 return w_rook_wchar;
752 case 'r':
753 return b_rook_wchar;
754 case 'B':
755 return w_bishop_wchar;
756 case 'b':
757 return b_bishop_wchar;
758 case 'N':
759 return w_knight_wchar;
760 case 'n':
761 return b_knight_wchar;
762 case 'Q':
763 return w_queen_wchar;
764 case 'q':
765 return b_queen_wchar;
766 case 'K':
767 return w_king_wchar;
768 case 'k':
769 return b_king_wchar;
770 case 'x':
771 return enpassant_wchar;
774 return empty_wchar;
777 static int
778 piece_can_attack (GAME g, int rank, int file)
780 struct userdata_s *d = g->data;
781 char *m, *frfr = NULL;
782 pgn_error_t e;
783 int row, col, p, v, pi, cpi;
785 if (d->rotate)
787 rotate_position (&d->c_row, &d->c_col);
788 rotate_position (&d->sp.srow, &d->sp.scol);
791 v = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].valid;
792 pi = pgn_piece_to_int (d->b[RANKTOBOARD (rank)][FILETOBOARD (file)].icon);
793 cpi = d->sp.icon
795 pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)]
796 [FILETOBOARD (d->sp.scol)].icon) :
797 pgn_piece_to_int (d->b[RANKTOBOARD (d->c_row)]
798 [FILETOBOARD (d->c_col)].icon);
800 if (pi == OPEN_SQUARE || cpi == OPEN_SQUARE || !VALIDFILE (file)
801 || !VALIDRANK (rank))
803 if (d->rotate)
805 rotate_position (&d->c_row, &d->c_col);
806 rotate_position (&d->sp.srow, &d->sp.scol);
809 return 0;
812 if (d->sp.icon)
814 col = v ? d->c_col : d->sp.scol;
815 row = v ? d->c_row : d->sp.srow;
817 else
819 col = d->c_col;
820 row = d->c_row;
823 m = Malloc (MAX_SAN_MOVE_LEN + 1);
824 m[0] = INTTOFILE (file);
825 m[1] = INTTORANK (rank);
826 m[2] = INTTOFILE (col);
827 m[3] = INTTORANK (row);
828 m[4] = 0;
830 if (d->sp.icon && v)
832 BOARD b;
834 memcpy (b, d->b, sizeof (BOARD));
835 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
836 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
837 pgn_int_to_piece (WHITE, OPEN_SQUARE);
838 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
839 pgn_switch_turn (g);
840 e = pgn_validate_move (g, b, &m, &frfr);
841 pgn_switch_turn (g);
842 free (m);
843 free (frfr);
845 if (e != E_PGN_OK && pgn_piece_to_int (d->sp.icon) == PAWN)
847 int n = (d->sp.srow == 7 && islower (d->sp.icon) && rank == 5) ? 6 :
848 (d->sp.srow == 2 && isupper (d->sp.icon) && rank == 4) ? 3 : 0;
850 if (n && (file == d->c_col - 1 || file == d->c_col + 1))
852 memcpy (b, d->b, sizeof (BOARD));
853 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
854 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
855 pgn_int_to_piece (WHITE, OPEN_SQUARE);
856 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
857 b[RANKTOBOARD (n)][FILETOBOARD (d->sp.scol)].enpassant = 1;
858 m = Malloc (MAX_SAN_MOVE_LEN + 1);
859 m[0] = INTTOFILE (file);
860 m[1] = INTTORANK (rank);
861 m[2] = INTTOFILE (col);
862 m[3] = INTTORANK (n);
863 m[4] = 0;
864 pgn_switch_turn (g);
865 SET_FLAG (g->flags, GF_ENPASSANT);
866 e = pgn_validate_move (g, b, &m, &frfr);
867 CLEAR_FLAG (g->flags, GF_ENPASSANT);
868 pgn_switch_turn (g);
869 free (m);
870 free (frfr);
874 goto pca_quit;
877 pgn_switch_turn (g);
878 e = pgn_validate_move (g, d->b, &m, &frfr);
879 pgn_switch_turn (g);
881 if (!strcmp (m, "O-O") || !strcmp (m, "O-O-O"))
882 e = E_PGN_INVALID;
884 if (e == E_PGN_OK)
886 int sf = FILETOINT (frfr[0]), sr = RANKTOINT (frfr[1]);
887 int df = FILETOINT (frfr[2]);
889 pi = d->b[RANKTOBOARD (sr)][FILETOBOARD (sf)].icon;
890 pi = pgn_piece_to_int (pi);
891 if (pi == PAWN && sf == df)
892 e = E_PGN_INVALID;
895 free (m);
896 free (frfr);
898 pca_quit:
899 if (d->rotate)
901 rotate_position (&d->c_row, &d->c_col);
902 rotate_position (&d->sp.srow, &d->sp.scol);
905 return e == E_PGN_OK ? 1 : 0;
908 void
909 print_piece (WINDOW * w, int l, int c, char p)
911 int i, y, ff = 0;
913 for (i = 0; i < 13; i += 2)
915 if (p == piece_chars[i] || p == piece_chars[i + 1])
917 for (y = 0; y < 3; y++)
918 mvwprintw (w, l + y, c, "%s", f_pieces[i + ff + y]);
919 return;
922 ff++;
925 for (y = 0; y < 3; y++)
926 mvwprintw (w, l + y, c, "%s", f_pieces[0]);
929 void
930 board_prev_move_play (GAME g)
932 struct userdata_s *d = g->data;
933 char l = strlen (d->pm_frfr);
935 if (l)
937 char q = (l > 4) ? 2 : 1;
939 d->pm_row = RANKTOINT (d->pm_frfr[l - q++]);
940 d->pm_col = FILETOINT (d->pm_frfr[l - q++]);
941 d->ospm_row = RANKTOINT (d->pm_frfr[l - q++]);
942 d->ospm_col = FILETOINT (d->pm_frfr[l - q]);
943 if (d->rotate)
945 rotate_position (&d->pm_row, &d->pm_col);
946 rotate_position (&d->ospm_row, &d->ospm_col);
949 else
951 d->pm_row = 0;
952 d->pm_col = 0;
953 d->ospm_row = 0;
954 d->ospm_col = 0;
958 void
959 board_prev_move_history (GAME g)
961 struct userdata_s *d = g->data;
963 if (g->hindex)
965 char *move = g->hp[g->hindex - 1]->move;
967 if (move)
969 if (d->mode == MODE_PLAY)
970 coordofmove (g, move, &d->pm_row, &d->pm_col);
971 else
973 d->pm_row = 0;
974 d->pm_col = 0;
977 if (*move == 'O')
979 d->ospm_row = g->turn == WHITE ? 8 : 1;
980 d->ospm_col = 5;
981 return;
984 BOARD ob;
985 unsigned char f, r;
987 pgn_board_init (ob);
989 if (g->hindex > 1)
991 HISTORY *h = pgn_history_by_n (g->hp, g->hindex - 2);
992 if (h)
994 pgn_board_init_fen (g, ob, h->fen);
995 pgn_switch_turn (g);
999 for (f = 0; f < 8; f++)
1001 for (r = 0; r < 8; r++)
1003 if (ob[f][r].icon != '.' && d->b[f][r].icon == '.')
1005 d->ospm_row = INV_INT0 (f) + 1;
1006 d->ospm_col = r + 1;
1007 break;
1012 if (d->rotate)
1014 if (d->mode == MODE_PLAY)
1015 rotate_position (&d->pm_row, &d->pm_col);
1017 rotate_position (&d->ospm_row, &d->ospm_col);
1020 return;
1023 else
1025 d->ospm_row = 0;
1026 d->ospm_col = 0;
1027 d->pm_row = 0;
1028 d->pm_col = 0;
1032 static int
1033 is_the_square (int brow, int bcol, int row, int col, int prow, int pcol)
1035 if ((!BIG_BOARD && row == ROWTOMATRIX (prow) && col == COLTOMATRIX (pcol))
1036 || (BIG_BOARD && brow + 1 == INV_INT (prow) && bcol + 1 == pcol))
1037 return 1;
1039 return 0;
1042 static int
1043 is_prev_move (struct userdata_s *d, int brow, int bcol, int row, int col)
1045 if (is_the_square (brow, bcol, row, col, d->pm_row, d->pm_col))
1046 return 1;
1048 return 0;
1051 void
1052 update_board_window (GAME g)
1054 int row, col;
1055 int bcol = 0, brow = 0;
1056 int l = config.coordsyleft;
1057 int maxy = BOARD_HEIGHT, maxx = BOARD_WIDTH;
1058 int ncols = 0, offset = 1;
1059 int rowr = (MEGA_BOARD) ? 6 : (BIG_BOARD) ? 4 : 2;
1060 int colr = (MEGA_BOARD) ? 12 : (BIG_BOARD) ? 8 : 4;
1061 unsigned coords_y = 8, cxgc = 0;
1062 unsigned i, cpd = 0;
1063 struct userdata_s *d = g->data;
1065 if (config.bprevmove && d->mode != MODE_EDIT)
1067 if (!d->pm_undo && d->mode == MODE_PLAY)
1068 board_prev_move_play (g);
1069 else
1070 board_prev_move_history (g);
1072 else
1074 d->pm_row = 0;
1075 d->pm_col = 0;
1076 d->ospm_row = 0;
1077 d->ospm_col = 0;
1080 if (d->mode != MODE_PLAY && d->mode != MODE_EDIT)
1081 update_cursor (g, g->hindex);
1083 if (BIG_BOARD)
1085 if (d->rotate)
1087 brow = 7;
1088 coords_y = 1;
1091 else
1093 if (d->rotate)
1095 brow = 1;
1096 coords_y = 1;
1098 else
1099 brow = 8;
1102 for (row = 0; row < maxy; row++)
1104 if (BIG_BOARD)
1106 if (d->rotate)
1107 bcol = 7;
1108 else
1109 bcol = 0;
1111 else
1113 if (d->rotate)
1114 bcol = 8;
1115 else
1116 bcol = 1;
1119 for (col = 0; col < maxx; col++)
1121 int attrwhich = -1;
1122 chtype attrs = 0, old_attrs = 0;
1123 unsigned char p;
1124 int can_attack = 0;
1125 int valid = 0;
1127 if (row == 0 || row == maxy - 2)
1129 if (col == 0)
1130 mvwaddch (boardw, row, col + l,
1131 LINE_GRAPHIC ((row)
1132 ? ACS_LLCORNER | CP_BOARD_GRAPHICS
1133 : ACS_ULCORNER | CP_BOARD_GRAPHICS));
1134 else if (col == maxx - 2)
1135 mvwaddch (boardw, row, col + l,
1136 LINE_GRAPHIC ((row)
1137 ? ACS_LRCORNER | CP_BOARD_GRAPHICS
1138 : ACS_URCORNER | CP_BOARD_GRAPHICS));
1139 else if (!(col % colr))
1140 mvwaddch (boardw, row, col + l,
1141 LINE_GRAPHIC ((row)
1142 ? ACS_BTEE | CP_BOARD_GRAPHICS
1143 : ACS_TTEE | CP_BOARD_GRAPHICS));
1144 else
1146 if (col != maxx - 1)
1147 mvwaddch (boardw, row, col + l,
1148 LINE_GRAPHIC (ACS_HLINE | CP_BOARD_GRAPHICS));
1151 continue;
1154 if ((row % 2) && col == maxx - 1 && (coords_y > 0 && coords_y < 9))
1156 wattron (boardw, CP_BOARD_COORDS);
1157 mvwprintw (boardw,
1158 (BIG_BOARD) ? row * ((MEGA_BOARD) ? 3 : 2)
1159 : row, (l) ? 0 : col, "%d",
1160 (d->rotate) ? coords_y++ : coords_y--);
1161 wattroff (boardw, CP_BOARD_COORDS);
1162 continue;
1165 if ((col == 0 || col == maxx - 2) && row != maxy - 1)
1167 if (!(row % rowr))
1168 mvwaddch (boardw, row, col + l,
1169 LINE_GRAPHIC ((col) ?
1170 ACS_RTEE | CP_BOARD_GRAPHICS :
1171 ACS_LTEE | CP_BOARD_GRAPHICS));
1172 else
1173 mvwaddch (boardw, row, col + l,
1174 LINE_GRAPHIC (ACS_VLINE | CP_BOARD_GRAPHICS));
1176 continue;
1179 if ((row % rowr) && !(col % colr) && row != maxy - 1)
1181 mvwaddch (boardw, row, col + l,
1182 LINE_GRAPHIC (ACS_VLINE | CP_BOARD_GRAPHICS));
1183 continue;
1186 if (!(col % colr) && row != maxy - 1)
1188 mvwaddch (boardw, row, col + l,
1189 LINE_GRAPHIC (ACS_PLUS | CP_BOARD_GRAPHICS));
1190 continue;
1193 if ((row % rowr))
1195 if ((col % colr))
1197 if (BIG_BOARD)
1198 attrwhich = (cb[brow][bcol]) ? WHITE : BLACK;
1199 else
1201 if (ncols++ == 8)
1203 offset++;
1204 ncols = 1;
1207 if (((ncols % 2) && !(offset % 2))
1208 || (!(ncols % 2) && (offset % 2)))
1209 attrwhich = BLACK;
1210 else
1211 attrwhich = WHITE;
1214 if (BIG_BOARD && d->rotate)
1216 brow = INV_INT0 (brow);
1217 bcol = INV_INT0 (bcol);
1220 if (BIG_BOARD)
1221 p = d->b[brow][bcol].icon;
1222 else
1223 p = d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].icon;
1225 int pi = pgn_piece_to_int (p);
1227 if (config.details &&
1228 ((!BIG_BOARD
1229 && d->
1230 b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].enpassant)
1231 || (BIG_BOARD && d->b[brow][bcol].enpassant)))
1233 p = pi = 'x';
1234 attrs = mix_cp (CP_BOARD_ENPASSANT,
1235 (attrwhich ==
1236 WHITE) ? CP_BOARD_WHITE :
1237 CP_BOARD_BLACK,
1238 ATTRS (CP_BOARD_ENPASSANT), A_FG_B_BG);
1241 if (config.showattacks && config.details
1242 && piece_can_attack (g,
1243 BIG_BOARD ? INV_INT0 (brow) +
1244 1 : brow,
1245 BIG_BOARD ? bcol + 1 : bcol))
1247 attrs = CP_BOARD_ATTACK;
1248 old_attrs = attrs;
1249 can_attack = 1;
1252 if (config.validmoves &&
1253 ((!BIG_BOARD
1254 && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].valid)
1255 || (BIG_BOARD && d->b[brow][bcol].valid)))
1257 old_attrs = -1;
1258 valid = 1;
1260 if (attrwhich == WHITE)
1261 attrs = mix_cp (CP_BOARD_MOVES_WHITE,
1262 IS_ENPASSANT (p),
1263 ATTRS (CP_BOARD_MOVES_WHITE),
1264 B_FG_A_BG);
1265 else
1266 attrs = mix_cp (CP_BOARD_MOVES_BLACK,
1267 IS_ENPASSANT (p),
1268 ATTRS (CP_BOARD_MOVES_BLACK),
1269 B_FG_A_BG);
1271 else if (p != 'x' && !can_attack)
1272 attrs =
1273 (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK;
1275 if (BIG_BOARD && d->rotate)
1277 brow = INV_INT0 (brow);
1278 bcol = INV_INT0 (bcol);
1281 if (is_the_square (brow, bcol, row, col, d->c_row,
1282 d->c_col))
1284 attrs = mix_cp (CP_BOARD_CURSOR, IS_ENPASSANT (p),
1285 ATTRS (CP_BOARD_CURSOR), B_FG_A_BG);
1286 old_attrs = -1;
1288 else if (is_the_square (brow, bcol, row, col,
1289 d->sp.srow, d->sp.scol))
1291 attrs = mix_cp (CP_BOARD_SELECTED, IS_ENPASSANT (p),
1292 ATTRS (CP_BOARD_SELECTED), B_FG_A_BG);
1293 old_attrs = -1;
1295 else if ((is_prev_move (d, brow, bcol, row, col) && !valid)
1296 || (is_the_square (brow, bcol, row, col,
1297 d->ospm_row, d->ospm_col)))
1299 attrs = mix_cp (CP_BOARD_PREVMOVE, IS_ENPASSANT (p),
1300 ATTRS (CP_BOARD_PREVMOVE), B_FG_A_BG);
1301 old_attrs = -1;
1304 if (row == maxy - 1)
1305 attrs = 0;
1307 if (can_attack)
1309 int n = is_prev_move (d, brow, bcol, row, col);
1310 chtype a = n && !valid
1311 ? CP_BOARD_PREVMOVE : attrwhich ==
1312 WHITE ? valid ? CP_BOARD_MOVES_WHITE : CP_BOARD_WHITE
1313 : valid ? CP_BOARD_MOVES_BLACK : CP_BOARD_BLACK;
1314 attrs =
1315 mix_cp (CP_BOARD_ATTACK, a, ATTRS (CP_BOARD_ATTACK),
1316 A_FG_B_BG);
1317 old_attrs = -1;
1320 if (BIG_BOARD)
1321 wmove (boardw, row, col + ((MEGA_BOARD) ? 5 : 3) + l);
1322 else
1323 mvwaddch (boardw, row, col + l, ' ' | attrs);
1325 if (row == maxy - 1 && cxgc < 8)
1327 waddch (boardw,
1328 "abcdefgh"[(BIG_BOARD) ? bcol : bcol -
1329 1] | CP_BOARD_COORDS);
1330 cxgc++;
1332 else
1334 if (old_attrs == -1)
1336 old_attrs = attrs;
1337 goto printc;
1340 old_attrs = attrs;
1342 if (pi != OPEN_SQUARE && p != 'x' && !can_attack)
1344 if (attrwhich == WHITE)
1346 if (isupper (p))
1347 attrs = CP_BOARD_W_W;
1348 else
1349 attrs = CP_BOARD_W_B;
1351 else
1353 if (isupper (p))
1354 attrs = CP_BOARD_B_W;
1355 else
1356 attrs = CP_BOARD_B_B;
1360 printc:
1361 if (BIG_BOARD)
1363 if (config.details && !can_attack
1364 && castling_state (g, d->b,
1365 (d->rotate) ? INV_INT0 (brow + 1) - 1 : brow,
1366 (d->rotate) ? INV_INT0 (bcol + 1) - 1 : bcol,
1367 p, 0))
1368 attrs = mix_cp (CP_BOARD_CASTLING, attrs,
1369 ATTRS (CP_BOARD_CASTLING), A_FG_B_BG);
1371 else
1373 if (config.details && !can_attack
1374 && castling_state (g, d->b, RANKTOBOARD (brow),
1375 FILETOBOARD (bcol), p, 0))
1377 attrs = mix_cp (CP_BOARD_CASTLING, attrs,
1378 ATTRS (CP_BOARD_CASTLING),
1379 A_FG_B_BG);
1383 if (BIG_BOARD)
1385 // FIXME: Reimpresión de piezas(+4).
1386 if (cpd < 67)
1388 wattron (boardw, attrs);
1389 if (MEGA_BOARD)
1391 for (i = 0; i < 5; i++)
1392 mvwprintw (boardw, i + brow * 6 + 1,
1393 bcol * 12 + 1 + l,
1394 " ");
1395 if (pi != OPEN_SQUARE)
1396 print_piece (boardw, brow * 6 + 2,
1397 bcol * 12 + 3 + l, p);
1399 else
1401 print_piece (boardw, brow * 4 + 1,
1402 bcol * 8 + 1 + l,
1403 (pi != OPEN_SQUARE) ? p : 0);
1406 wattroff (boardw, attrs);
1407 cpd++;
1410 else
1412 wattron (boardw, attrs);
1413 waddwstr (boardw,
1414 piece_to_wchar (pi !=
1415 OPEN_SQUARE ? p : 0));
1416 wattroff (boardw, attrs);
1419 attrs = old_attrs;
1422 if (BIG_BOARD)
1423 col += (MEGA_BOARD) ? 10 : 6;
1424 else
1426 waddch (boardw, ' ' | attrs);
1427 col += 2;
1430 if (d->rotate)
1431 bcol--;
1432 else
1433 bcol++;
1435 if (BIG_BOARD)
1437 if (bcol > 7)
1438 bcol = 0;
1439 if (bcol < 0)
1440 bcol = 7;
1444 else
1446 if (col != maxx - 1)
1447 mvwaddch (boardw, row, col + l,
1448 LINE_GRAPHIC (ACS_HLINE | CP_BOARD_GRAPHICS));
1452 if (row % rowr)
1454 if (d->rotate)
1455 brow++;
1456 else
1457 brow--;
1460 if (BIG_BOARD)
1462 if (brow > 7)
1463 brow = 0;
1464 if (brow < 0)
1465 brow = 7;
1470 void
1471 invalid_move (int n, int e, const char *m)
1473 if (curses_initialized)
1474 cmessage (ERROR_STR, ANY_KEY_STR, "%s \"%s\" (round #%i)",
1475 (e ==
1476 E_PGN_AMBIGUOUS) ? _("Ambiguous move") : _("Invalid move"), m,
1478 else
1479 warnx ("%s: %s \"%s\" (round #%i)", loadfile, (e == E_PGN_AMBIGUOUS)
1480 ? _("Ambiguous move") : _("Invalid move"), m, n);
1483 void
1484 gameover (GAME g)
1486 struct userdata_s *d = g->data;
1488 SET_FLAG (g->flags, GF_GAMEOVER);
1489 d->mode = MODE_HISTORY;
1490 stop_engine (g);
1493 static void
1494 update_clock (GAME g, struct itimerval it)
1496 struct userdata_s *d = g->data;
1498 if (TEST_FLAG (d->flags, CF_CLOCK) && g->turn == WHITE)
1500 d->wclock.elapsed.tv_sec += it.it_value.tv_sec;
1501 d->wclock.elapsed.tv_usec += it.it_value.tv_usec;
1503 if (d->wclock.elapsed.tv_usec > 1000000 - 1)
1505 d->wclock.elapsed.tv_sec += d->wclock.elapsed.tv_usec / 1000000;
1506 d->wclock.elapsed.tv_usec = d->wclock.elapsed.tv_usec % 1000000;
1509 if (d->wclock.tc[d->wclock.tcn][1] &&
1510 d->wclock.elapsed.tv_sec >= d->wclock.tc[d->wclock.tcn][1])
1512 pgn_tag_add (&g->tag, (char *) "Result", (char *) "0-1");
1513 gameover (g);
1516 else if (TEST_FLAG (d->flags, CF_CLOCK) && g->turn == BLACK)
1518 d->bclock.elapsed.tv_sec += it.it_value.tv_sec;
1519 d->bclock.elapsed.tv_usec += it.it_value.tv_usec;
1521 if (d->bclock.elapsed.tv_usec > 1000000 - 1)
1523 d->bclock.elapsed.tv_sec += d->bclock.elapsed.tv_usec / 1000000;
1524 d->bclock.elapsed.tv_usec = d->bclock.elapsed.tv_usec % 1000000;
1527 if (d->bclock.tc[d->bclock.tcn][1] &&
1528 d->bclock.elapsed.tv_sec >= d->bclock.tc[d->bclock.tcn][1])
1530 pgn_tag_add (&g->tag, (char *) "Result", (char *) "1-0");
1531 gameover (g);
1535 d->elapsed.tv_sec += it.it_value.tv_sec;
1536 d->elapsed.tv_usec += it.it_value.tv_usec;
1538 if (d->elapsed.tv_usec > 1000000 - 1)
1540 d->elapsed.tv_sec += d->elapsed.tv_usec / 1000000;
1541 d->elapsed.tv_usec = d->elapsed.tv_usec % 1000000;
1545 static void
1546 update_time_control (GAME g)
1548 struct userdata_s *d = g->data;
1549 struct clock_s *clk = (g->turn == WHITE) ? &d->wclock : &d->bclock;
1551 if (clk->incr)
1552 clk->tc[clk->tcn][1] += clk->incr;
1554 if (!clk->tc[clk->tcn][1])
1555 return;
1557 clk->move++;
1559 if (!clk->tc[clk->tcn][0] || clk->move >= clk->tc[clk->tcn][0])
1561 clk->move = 0;
1562 clk->tc[clk->tcn + 1][1] +=
1563 labs (clk->elapsed.tv_sec - clk->tc[clk->tcn][1]);
1564 memset (&clk->elapsed, 0, sizeof (clk->elapsed));
1565 clk->tcn++;
1569 void
1570 update_history_window (GAME g)
1572 char buf[HISTORY_WIDTH - 1];
1573 HISTORY *h = NULL;
1574 int n, total;
1575 int t = pgn_history_total (g->hp);
1577 n = (g->hindex + 1) / 2;
1579 if (t % 2)
1580 total = (t + 1) / 2;
1581 else
1582 total = t / 2;
1584 if (t)
1585 snprintf (buf, sizeof (buf), "%u %s %u%s", n, _("of"), total,
1586 (movestep == 1) ? _(" (ply)") : "");
1587 else
1588 strncpy (buf, _("not available"), sizeof (buf) - 1);
1590 buf[sizeof (buf) - 1] = 0;
1591 mvwprintw (historyw, 2, 1, "%*s %-*s", 10, _("Move:"),
1592 HISTORY_WIDTH - 14, buf);
1594 h = pgn_history_by_n (g->hp, g->hindex);
1595 snprintf (buf, sizeof (buf), "%s",
1596 (h && h->move) ? h->move
1597 : (LINES < 24) ? _("empty") : _("not available"));
1598 n = 0;
1600 if (h && ((h->comment) || h->nag[0]))
1602 strncat (buf, _(" (Annotated"), sizeof (buf) - 1);
1603 n++;
1606 if (h && h->rav)
1608 strncat (buf, (n) ? ",+" : " (+", sizeof (buf) - 1);
1609 n++;
1612 if (g->ravlevel)
1614 strncat (buf, (n) ? ",-" : " (-", sizeof (buf) - 1);
1615 n++;
1618 if (n)
1619 strncat (buf, ")", sizeof (buf) - 1);
1621 mvwprintw (historyw, 3, ((LINES < 24) ? 17 : 1), "%s %-*s",
1622 (LINES < 24) ? _("Next:") : _("Next move:"),
1623 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1625 h = pgn_history_by_n (g->hp, g->hindex - 1);
1626 snprintf (buf, sizeof (buf), "%s",
1627 (h && h->move) ? h->move
1628 : (LINES < 24) ? _("empty") : _("not available"));
1629 n = 0;
1631 if (h && ((h->comment) || h->nag[0]))
1633 strncat (buf, _(" (Annotated"), sizeof (buf) - 1);
1634 n++;
1637 if (h && h->rav)
1639 strncat (buf, (n) ? ",+" : " (+", sizeof (buf) - 1);
1640 n++;
1643 if (g->ravlevel)
1645 strncat (buf, (n) ? ",-" : " (-", sizeof (buf) - 1);
1646 n++;
1649 if (n)
1650 strncat (buf, ")", sizeof (buf) - 1);
1652 mvwprintw (historyw, ((LINES < 24) ? 3 : 4), 1, "%s %-*s",
1653 (LINES < 24) ? _("Prev.:") : _("Prev move:"),
1654 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1657 void
1658 do_validate_move (char **move)
1660 struct userdata_s *d = gp->data;
1661 int n;
1662 char *frfr = NULL;
1664 if (TEST_FLAG (d->flags, CF_HUMAN))
1666 if ((n = pgn_parse_move (gp, d->b, move, &frfr)) != E_PGN_OK)
1668 invalid_move (d->n + 1, n, *move);
1669 return;
1672 strcpy (d->pm_frfr, frfr);
1673 update_time_control (gp);
1674 pgn_history_add (gp, d->b, *move);
1675 pgn_switch_turn (gp);
1677 else
1679 if ((n = pgn_validate_move (gp, d->b, move, &frfr)) != E_PGN_OK)
1681 invalid_move (d->n + 1, n, *move);
1682 return;
1685 add_engine_command (gp, ENGINE_THINKING, "%s\n",
1686 (config.engine_protocol == 1) ? frfr : *move);
1689 d->sp.srow = d->sp.scol = d->sp.icon = 0;
1691 if (config.validmoves)
1692 pgn_reset_valid_moves (d->b);
1694 if (TEST_FLAG (gp->flags, GF_GAMEOVER))
1695 d->mode = MODE_HISTORY;
1696 else
1697 SET_FLAG (d->flags, CF_MODIFIED);
1699 free (frfr);
1700 d->paused = 0;
1701 update_history_window (gp);
1702 update_board_window (gp);
1703 return;
1706 void
1707 do_promotion_piece_finalize (WIN * win)
1709 char *p, *str = win->data;
1711 if (pgn_piece_to_int (win->c) == -1)
1712 return;
1714 p = str + strlen (str);
1715 *p++ = toupper (win->c);
1716 *p = '\0';
1717 do_validate_move (&str);
1718 free (str);
1719 win->data = NULL;
1722 static void
1723 move_to_engine (GAME g)
1725 struct userdata_s *d = g->data;
1726 char *str;
1727 int piece;
1729 if (config.validmoves &&
1730 !d->b[RANKTOBOARD (d->sp.row)][FILETOBOARD (d->sp.col)].valid)
1731 return;
1733 str = Malloc (MAX_SAN_MOVE_LEN + 1);
1734 snprintf (str, MAX_SAN_MOVE_LEN + 1, "%c%u%c%u",
1735 _("abcdefgh")[d->sp.scol - 1],
1736 d->sp.srow, _("abcdefgh")[d->sp.col - 1], d->sp.row);
1738 piece =
1739 pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)]
1740 [FILETOBOARD (d->sp.scol)].icon);
1742 if (piece == PAWN && (d->sp.row == 8 || d->sp.row == 1))
1744 construct_message (_("Select Pawn Promotion Piece"), _("R/N/B/Q"), 1, 1,
1745 NULL, NULL, str, do_promotion_piece_finalize, 0, 0,
1746 NULL, "%s",
1747 _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1748 return;
1751 do_validate_move (&str);
1752 free (str);
1755 static char *
1756 clock_to_char (long n)
1758 static char buf[16];
1759 int h = 0, m = 0, s = 0;
1761 h = n / 3600;
1762 m = (n % 3600) / 60;
1763 s = (n % 3600) % 60;
1764 snprintf (buf, sizeof (buf), "%.2i:%.2i:%.2i", h, m, s);
1765 return buf;
1768 static char *
1769 timeval_to_char (struct timeval t, long limit)
1771 static char buf[9];
1772 unsigned h = 0, m = 0, s = 0;
1773 int n = limit ? labs (limit - t.tv_sec) : 0;
1775 h = n / 3600;
1776 m = (n % 3600) / 60;
1777 s = (n % 3600) % 60;
1778 snprintf (buf, sizeof (buf), "%.2u:%.2u:%.2u", h, m, s);
1779 return buf;
1782 static char *
1783 time_control_status (struct clock_s *clk)
1785 static char buf[80] = { 0 };
1787 buf[0] = 0;
1789 if (clk->tc[clk->tcn][0] && clk->tc[clk->tcn + 1][1])
1790 snprintf (buf, sizeof (buf), " M%.2i/%s",
1791 abs (clk->tc[clk->tcn][0] - clk->move),
1792 clock_to_char (clk->tc[clk->tcn + 1][1]));
1793 else if (!clk->incr)
1794 return (char *) "";
1796 if (clk->incr)
1798 char tbuf[16];
1800 strncat (tbuf, " I", sizeof (tbuf) - 1);
1801 strncat (tbuf, itoa (clk->incr, buf), sizeof (tbuf) - 1);
1804 return buf;
1807 void
1808 update_status_window (GAME g)
1810 int i = 0;
1811 char *buf;
1812 char tmp[15] = { 0 }, *engine, *mode;
1813 char t[COLS];
1814 int w;
1815 char *p;
1816 int maxy, maxx;
1817 int len;
1818 struct userdata_s *d = g->data;
1819 int y;
1820 int n;
1822 if (!curses_initialized)
1823 return;
1825 getmaxyx (statusw, maxy, maxx);
1826 (void) maxy;
1827 w = maxx - 2 - 8;
1828 len = maxx - 2;
1829 buf = Malloc (len);
1830 y = 2;
1832 wchar_t *loadfilew = loadfile[0]
1833 ? str_etc (loadfile, w, 1) : str_to_wchar (_("not available"));
1834 mvwprintw (statusw, y++, 1, "%*s %-*ls", 7, _("File:"), w, loadfilew);
1835 free (loadfilew);
1836 snprintf (buf, len, "%i %s %i", gindex + 1, _("of"), gtotal);
1837 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Game:"), w, buf);
1839 *tmp = '\0';
1840 p = tmp;
1842 if (config.details)
1844 *p++ = 'D';
1845 i++;
1848 if (TEST_FLAG (d->flags, CF_DELETE))
1850 if (i)
1851 *p++ = '/';
1853 *p++ = 'X';
1854 i++;
1857 if (TEST_FLAG (g->flags, GF_PERROR))
1859 if (i)
1860 *p++ = '/';
1862 *p++ = '!';
1863 i++;
1866 if (TEST_FLAG (d->flags, CF_MODIFIED))
1868 if (i)
1869 *p++ = '/';
1871 *p++ = '*';
1872 i++;
1875 pgn_config_get (PGN_STRICT_CASTLING, &n);
1877 if (n == 1)
1879 if (i)
1880 *p++ = '/';
1882 *p++ = 'C';
1883 i++;
1885 #ifdef WITH_LIBPERL
1886 if (TEST_FLAG (d->flags, CF_PERL))
1888 if (i)
1889 *p++ = '/';
1891 *p++ = 'P';
1892 i++;
1894 #endif
1896 *p = '\0';
1897 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Flags:"), w,
1898 (tmp[0]) ? tmp : "-");
1900 switch (d->mode)
1902 case MODE_HISTORY:
1903 mode = _("move history");
1904 break;
1905 case MODE_EDIT:
1906 mode = _("edit");
1907 break;
1908 case MODE_PLAY:
1909 mode = _("play");
1910 break;
1911 default:
1912 mode = _("(empty value)");
1913 break;
1916 snprintf (buf, len - 1, "%*s %s", 7, _("Mode:"), mode);
1918 if (d->mode == MODE_PLAY)
1920 if (TEST_FLAG (d->flags, CF_HUMAN))
1921 strncat (buf, _(" (human/human)"), len - 1);
1922 else if (TEST_FLAG (d->flags, CF_ENGINE_LOOP))
1923 strncat (buf, _(" (engine/engine)"), len - 1);
1924 else
1925 strncat (buf, (d->play_mode == PLAY_EH) ?
1926 _(" (engine/human)") : _(" (human/engine)"), len - 1);
1929 buf[len - 1] = 0;
1930 mvwprintw (statusw, y++, 1, "%-*s", len, buf);
1931 free (buf);
1933 if (d->engine)
1935 switch (d->engine->status)
1937 case ENGINE_THINKING:
1938 engine = _("pondering...");
1939 break;
1940 case ENGINE_READY:
1941 engine = _("ready");
1942 break;
1943 case ENGINE_INITIALIZING:
1944 engine = _("initializing...");
1945 break;
1946 case ENGINE_OFFLINE:
1947 engine = _("offline");
1948 break;
1949 default:
1950 engine = _("(empty value)");
1951 break;
1954 else
1955 engine = _("offline");
1957 mvwprintw (statusw, y, 1, "%*s %-*s", 7, _("Engine:"), w, " ");
1958 wattron (statusw, CP_STATUS_ENGINE);
1959 mvwaddstr (statusw, y++, 9, engine);
1960 wattroff (statusw, CP_STATUS_ENGINE);
1962 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Turn:"), w,
1963 (g->turn == WHITE) ? _("white") : _("black"));
1965 strncpy (tmp, _("white"), sizeof (tmp) - 1);
1966 tmp[0] = toupper (tmp[0]);
1967 snprintf (t, sizeof (t), "%s%s",
1968 timeval_to_char (d->wclock.elapsed,
1969 d->wclock.tc[d->wclock.tcn][1]),
1970 time_control_status (&d->wclock));
1971 mvwprintw (statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1973 strncpy (tmp, _("black"), sizeof (tmp) - 1);
1974 tmp[0] = toupper (tmp[0]);
1975 snprintf (t, sizeof (t), "%s%s",
1976 timeval_to_char (d->bclock.elapsed,
1977 d->bclock.tc[d->bclock.tcn][1]),
1978 time_control_status (&d->bclock));
1979 mvwprintw (statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1981 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Total:"), w,
1982 clock_to_char (d->elapsed.tv_sec));
1984 // for (i = 0; i < STATUS_WIDTH; i++)
1985 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1987 if (!status.notify)
1989 char tbuf[255];
1991 snprintf (tbuf, sizeof (tbuf), _("Type %ls for help"),
1992 key_lookup (global_keys, do_global_help));
1993 status.notify = str_to_wchar (tbuf);
1996 wattron (stdscr, CP_STATUS_NOTIFY);
1997 for (i = (config.boardleft) ? BOARD_WIDTH : 0;
1998 i < ((config.boardleft) ? COLS : STATUS_WIDTH); i++)
1999 mvwprintw (stdscr, STATUS_HEIGHT, i, " ");
2000 mvwprintw (stdscr, STATUS_HEIGHT, CENTERX (STATUS_WIDTH, status.notify)
2001 + ((config.boardleft) ? BOARD_WIDTH : 0), "%ls", status.notify);
2002 wattroff (stdscr, CP_STATUS_NOTIFY);
2005 wchar_t *
2006 translate_tag_name (const char *tag)
2008 if (!strcmp (tag, "Event"))
2009 return str_to_wchar (translatable_tag_names[0]);
2010 else if (!strcmp (tag, "Site"))
2011 return str_to_wchar (translatable_tag_names[1]);
2012 else if (!strcmp (tag, "Date"))
2013 return str_to_wchar (translatable_tag_names[2]);
2014 else if (!strcmp (tag, "Round"))
2015 return str_to_wchar (translatable_tag_names[3]);
2016 else if (!strcmp (tag, "White"))
2017 return str_to_wchar (translatable_tag_names[4]);
2018 else if (!strcmp (tag, "Black"))
2019 return str_to_wchar (translatable_tag_names[5]);
2020 else if (!strcmp (tag, "Result"))
2021 return str_to_wchar (translatable_tag_names[6]);
2023 return str_to_wchar (tag);
2026 void
2027 update_tag_window (TAG ** t)
2029 int i, l, w;
2030 int namel = 0;
2032 for (i = 0; t[i]; i++)
2034 wchar_t *namewc = translate_tag_name (t[i]->name);
2036 l = wcslen (namewc);
2037 free (namewc);
2038 if (l > namel)
2039 namel = l;
2042 w = TAG_WIDTH - namel - 4;
2044 for (i = 0; t[i] && i < TAG_HEIGHT - 3; i++)
2046 wchar_t *namewc = translate_tag_name (t[i]->name);
2047 wchar_t *valuewc = str_etc (t[i]->value, w, 0);
2049 mvwprintw (tagw, (i + 2), 1, "%*ls: %-*ls", namel, namewc, w, valuewc);
2050 free (namewc);
2051 free (valuewc);
2054 for (; i < TAG_HEIGHT - 3; i++)
2055 mvwprintw (tagw, (i + 2), 1, "%*s", namel + w + 2, " ");
2058 void
2059 append_enginebuf (GAME g, char *line)
2061 int i = 0;
2062 struct userdata_s *d = g->data;
2064 if (d->engine->enginebuf)
2065 for (i = 0; d->engine->enginebuf[i]; i++);
2067 if (i >= LINES - 3)
2069 free (d->engine->enginebuf[0]);
2071 for (i = 0; d->engine->enginebuf[i + 1]; i++)
2072 d->engine->enginebuf[i] = d->engine->enginebuf[i + 1];
2074 d->engine->enginebuf[i] = strdup (line);
2076 else
2078 d->engine->enginebuf =
2079 Realloc (d->engine->enginebuf, (i + 2) * sizeof (char *));
2080 d->engine->enginebuf[i++] = strdup (line);
2081 d->engine->enginebuf[i] = NULL;
2085 void
2086 update_engine_window (GAME g)
2088 int i;
2089 struct userdata_s *d = g->data;
2091 wmove (enginew, 0, 0);
2092 wclrtobot (enginew);
2094 if (d->engine && d->engine->enginebuf)
2096 for (i = 0; d->engine->enginebuf[i]; i++)
2097 mvwprintw (enginew, i + 2, 1, "%s", d->engine->enginebuf[i]);
2100 window_draw_title (enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
2101 CP_MESSAGE_BORDER);
2104 void
2105 update_all (GAME g)
2107 struct userdata_s *d = g->data;
2110 * In the middle of a macro. Don't update the screen.
2112 if (macro_match != -1)
2113 return;
2115 wmove (boardw, ROWTOMATRIX (d->c_row), COLTOMATRIX (d->c_col));
2116 update_board_window (g);
2117 update_status_window (g);
2118 update_history_window (g);
2119 update_tag_window (g->tag);
2120 update_engine_window (g);
2121 update_panels ();
2122 doupdate ();
2125 static void
2126 game_next_prev (GAME g, int n, int count)
2128 if (gtotal < 2)
2129 return;
2131 if (n == 1)
2133 if (gindex + count > gtotal - 1)
2135 if (count != 1)
2136 gindex = gtotal - 1;
2137 else
2138 gindex = 0;
2140 else
2141 gindex += count;
2143 else
2145 if (gindex - count < 0)
2147 if (count != 1)
2148 gindex = 0;
2149 else
2150 gindex = gtotal - 1;
2152 else
2153 gindex -= count;
2156 gp = game[gindex];
2159 static void
2160 delete_game (int which)
2162 int i, w = which;
2163 struct userdata_s *d;
2165 for (i = 0; i < gtotal; i++)
2167 d = game[i]->data;
2169 if (i == w || TEST_FLAG (d->flags, CF_DELETE))
2171 int n;
2173 free_userdata_once (game[i]);
2174 pgn_free (game[i]);
2176 for (n = i; n + 1 < gtotal; n++)
2177 game[n] = game[n + 1];
2179 gtotal--;
2180 i--;
2181 w = -1;
2185 if (which != -1)
2187 if (which + 1 >= gtotal)
2188 gindex = gtotal - 1;
2189 else
2190 gindex = which;
2192 else
2193 gindex = gtotal - 1;
2195 gp = game[gindex];
2196 gp->hp = gp->history;
2200 * FIXME find across multiple games.
2202 static int
2203 find_move_exp (GAME g, regex_t r, int which, int count)
2205 int i;
2206 int ret;
2207 char errbuf[255];
2208 int incr;
2209 int found;
2211 incr = (which == 0) ? -1 : 1;
2213 for (i = g->hindex + incr - 1, found = 0;; i += incr)
2215 if (i == g->hindex - 1)
2216 break;
2218 if (i >= pgn_history_total (g->hp))
2219 i = 0;
2220 else if (i < 0)
2221 i = pgn_history_total (g->hp) - 1;
2223 // FIXME RAV
2224 ret = regexec (&r, g->hp[i]->move, 0, 0, 0);
2226 if (ret == 0)
2228 if (count == ++found)
2230 return i + 1;
2233 else
2235 if (ret != REG_NOMATCH)
2237 regerror (ret, &r, errbuf, sizeof (errbuf));
2238 cmessage (_("Error Matching Regular Expression"), ANY_KEY_STR,
2239 "%s", errbuf);
2240 return -1;
2245 return -1;
2248 static int
2249 toggle_delete_flag (int n)
2251 int i, x;
2252 struct userdata_s *d = game[n]->data;
2254 TOGGLE_FLAG (d->flags, CF_DELETE);
2255 gindex = n;
2257 for (i = x = 0; i < gtotal; i++)
2259 d = game[i]->data;
2261 if (TEST_FLAG (d->flags, CF_DELETE))
2262 x++;
2265 if (x == gtotal)
2267 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
2268 d = game[n]->data;
2269 CLEAR_FLAG (d->flags, CF_DELETE);
2270 return 1;
2273 return 0;
2276 static int
2277 find_game_exp (char *str, int which, int count)
2279 char *nstr = NULL, *exp = NULL;
2280 regex_t nexp, vexp;
2281 int ret = -1;
2282 int g = 0;
2283 char buf[255] = { 0 }, *tmp;
2284 char errbuf[255];
2285 int found = 0;
2286 int incr = (which == 0) ? -(1) : 1;
2288 strncpy (buf, str, sizeof (buf));
2289 buf[sizeof (buf)-1] = 0;
2290 tmp = buf;
2292 if (strstr (tmp, ":") != NULL)
2294 nstr = strsep (&tmp, ":");
2296 if ((ret = regcomp (&nexp, nstr,
2297 REG_ICASE | REG_EXTENDED | REG_NOSUB)) != 0)
2299 regerror (ret, &nexp, errbuf, sizeof (errbuf));
2300 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR,
2301 "%s", errbuf);
2302 ret = g = -1;
2303 goto cleanup;
2307 exp = tmp;
2309 while (exp && *exp && isspace (*exp))
2310 exp++;
2312 if (exp == NULL)
2313 goto cleanup;
2315 if ((ret = regcomp (&vexp, exp, REG_EXTENDED | REG_NOSUB)) != 0)
2317 regerror (ret, &vexp, errbuf, sizeof (errbuf));
2318 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s",
2319 errbuf);
2320 ret = -1;
2321 goto cleanup;
2324 ret = -1;
2326 for (g = gindex + incr, found = 0;; g += incr)
2328 int t;
2330 if (g == gtotal)
2331 g = 0;
2332 else if (g < 0)
2333 g = gtotal - 1;
2335 if (g == gindex)
2336 break;
2338 for (t = 0; game[g]->tag[t]; t++)
2340 if (nstr)
2342 if (regexec (&nexp, game[g]->tag[t]->name, 0, 0, 0) == 0)
2344 if (regexec (&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0)
2346 if (count == ++found)
2348 ret = g;
2349 goto cleanup;
2354 else
2356 if (regexec (&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0)
2358 if (count == ++found)
2360 ret = g;
2361 goto cleanup;
2367 ret = -1;
2370 cleanup:
2371 if (nstr)
2372 regfree (&nexp);
2374 if (g != -1)
2375 regfree (&vexp);
2377 return ret;
2381 * Updates the notification line in the status window then refreshes the
2382 * status window.
2384 void
2385 update_status_notify (GAME g, const char *fmt, ...)
2387 va_list ap;
2388 #ifdef HAVE_VASPRINTF
2389 char *line;
2390 #else
2391 char line[COLS];
2392 #endif
2394 free (status.notify);
2395 status.notify = NULL;
2397 if (!fmt)
2398 return;
2400 va_start (ap, fmt);
2401 #ifdef HAVE_VASPRINTF
2402 vasprintf (&line, fmt, ap);
2403 #else
2404 vsnprintf (line, sizeof (line), fmt, ap);
2405 #endif
2406 va_end (ap);
2408 status.notify = str_to_wchar (line);
2410 #ifdef HAVE_VASPRINTF
2411 free (line);
2412 #endif
2416 rav_next_prev (GAME g, BOARD b, int n)
2418 // Next RAV.
2419 if (n)
2421 if ((!g->ravlevel && g->hindex && g->hp[g->hindex - 1]->rav == NULL) ||
2422 (!g->ravlevel && !g->hindex && g->hp[g->hindex]->rav == NULL) ||
2423 (g->ravlevel && g->hp[g->hindex]->rav == NULL))
2424 return 1;
2426 g->rav = Realloc (g->rav, (g->ravlevel + 1) * sizeof (RAV));
2427 g->rav[g->ravlevel].hp = g->hp;
2428 g->rav[g->ravlevel].flags = g->flags;
2429 g->rav[g->ravlevel].fen = pgn_game_to_fen (g, b);
2430 g->rav[g->ravlevel].hindex = g->hindex;
2431 g->hp =
2432 (!g->ravlevel) ? (g->hindex) ? g->hp[g->hindex -
2433 1]->rav : g->hp[g->
2434 hindex]->rav :
2435 g->hp[g->hindex]->rav;
2436 g->hindex = 0;
2437 g->ravlevel++;
2438 pgn_board_update (g, b, g->hindex + 1);
2439 return 0;
2442 if (g->ravlevel - 1 < 0)
2443 return 1;
2445 // Previous RAV.
2446 g->ravlevel--;
2447 pgn_board_init_fen (g, b, g->rav[g->ravlevel].fen);
2448 free (g->rav[g->ravlevel].fen);
2449 g->hp = g->rav[g->ravlevel].hp;
2450 g->flags = g->rav[g->ravlevel].flags;
2451 g->hindex = g->rav[g->ravlevel].hindex;
2452 return 0;
2455 static void
2456 draw_window_decor ()
2458 move_panel (boardp, 0, (config.boardleft) ? 0 : COLS - BOARD_WIDTH);
2459 move_panel (historyp, LINES - HISTORY_HEIGHT,
2460 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH : 0 :
2461 (MEGA_BOARD) ? 0 : COLS - HISTORY_WIDTH);
2462 move_panel (statusp, 0, (config.boardleft) ? BOARD_WIDTH : 0);
2463 move_panel (tagp, STATUS_HEIGHT + 1,
2464 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH :
2465 HISTORY_WIDTH : 0);
2467 wbkgd (boardw, CP_BOARD_WINDOW);
2468 wbkgd (statusw, CP_STATUS_WINDOW);
2469 window_draw_title (statusw, _("Game Status"), STATUS_WIDTH,
2470 CP_STATUS_TITLE, CP_STATUS_BORDER);
2471 wbkgd (tagw, CP_TAG_WINDOW);
2472 window_draw_title (tagw, _("Roster Tags"), TAG_WIDTH, CP_TAG_TITLE,
2473 CP_TAG_BORDER);
2474 wbkgd (historyw, CP_HISTORY_WINDOW);
2475 window_draw_title (historyw, _("Move History"), HISTORY_WIDTH,
2476 CP_HISTORY_TITLE, CP_HISTORY_BORDER);
2479 static void
2480 history_menu_resize (WIN *w)
2482 struct menu_input_s *m;
2484 if (!w)
2485 return;
2487 w->rows = MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES;
2488 w->cols = TAG_WIDTH;
2489 w->posy = 0;
2490 w->posx = config.boardleft ? BOARD_WIDTH : 0;
2491 m = w->data;
2492 m->ystatic = w->posy;
2493 m->xstatic = w->posx;
2494 redraw_menu (w);
2497 void
2498 do_window_resize ()
2500 if (LINES < 23 || COLS < 74)
2501 return;
2503 resizeterm (LINES, COLS);
2504 endwin ();
2505 wresize (boardw, BOARD_HEIGHT, BOARD_WIDTH);
2506 wresize (historyw, HISTORY_HEIGHT, HISTORY_WIDTH);
2507 wresize (statusw, STATUS_HEIGHT, STATUS_WIDTH);
2508 wresize (tagw, TAG_HEIGHT, TAG_WIDTH);
2509 //resize_history_menu ();
2511 clear ();
2512 wclear (boardw);
2513 wclear (historyw);
2514 wclear (tagw);
2515 wclear (statusw);
2516 wclear (loadingw);
2517 wclear (enginew);
2518 draw_window_decor ();
2519 update_all (gp);
2520 keypad (boardw, TRUE);
2521 curs_set (0);
2522 cbreak ();
2523 noecho ();
2526 void
2527 do_global_redraw ()
2529 do_window_resize ();
2532 void
2533 stop_clock ()
2535 memset (&clock_timer, 0, sizeof (struct itimerval));
2536 setitimer (ITIMER_REAL, &clock_timer, NULL);
2539 void
2540 start_clock (GAME g)
2542 struct userdata_s *d = g->data;
2544 if (clock_timer.it_interval.tv_usec)
2545 return;
2547 memset (&d->elapsed, 0, sizeof (struct timeval));
2548 clock_timer.it_value.tv_sec = 0;
2549 clock_timer.it_value.tv_usec = 100000;
2550 clock_timer.it_interval.tv_sec = 0;
2551 clock_timer.it_interval.tv_usec = 100000;
2552 setitimer (ITIMER_REAL, &clock_timer, NULL);
2555 static void
2556 update_clocks ()
2558 int i;
2559 struct userdata_s *d;
2560 struct itimerval it;
2561 int update = 0;
2563 getitimer (ITIMER_REAL, &it);
2565 for (i = 0; i < gtotal; i++)
2567 d = game[i]->data;
2569 if (d && d->mode == MODE_PLAY)
2571 if (d->paused == 1 || TEST_FLAG (d->flags, CF_NEW))
2572 continue;
2573 else if (d->paused == -1)
2575 if (game[i]->side == game[i]->turn)
2577 d->paused = 1;
2578 continue;
2582 update_clock (game[i], it);
2584 if (game[i] == gp)
2585 update = 1;
2589 if (update)
2591 update_status_window (gp);
2592 update_panels ();
2593 doupdate ();
2597 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2599 static int
2600 parse_clock_time (char **str)
2602 char *p = *str;
2603 int n = 0, t = 0;
2605 SKIP_SPACE (p);
2607 if (!isdigit (*p))
2608 return -1;
2610 while (*p)
2612 if (isdigit (*p))
2614 t = atoi (p);
2616 while (isdigit (*p))
2617 p++;
2619 continue;
2622 switch (*p)
2624 case 'H':
2625 case 'h':
2626 n += t * (60 * 60);
2627 t = 0;
2628 break;
2629 case 'M':
2630 case 'm':
2631 n += t * 60;
2632 t = 0;
2633 break;
2634 case 'S':
2635 case 's':
2636 n += t;
2637 t = 0;
2638 break;
2639 case ' ':
2640 p++;
2641 case '/':
2642 case '+':
2643 goto done;
2644 default:
2645 *str = p;
2646 return -1;
2649 p++;
2652 done:
2653 n += t;
2654 *str = p;
2655 return n;
2658 static int
2659 parse_clock_input (struct clock_s *clk, char *str, int *incr)
2661 char *p = str;
2662 long n = 0;
2663 int plus = 0;
2664 int m = 0;
2665 int tc = 0;
2667 SKIP_SPACE (p);
2669 if (!*p)
2670 return 0;
2672 if (*p == '+')
2674 plus = 1;
2675 p++;
2676 SKIP_SPACE (p);
2678 if (*p == '+')
2679 goto move_incr;
2681 else
2682 memset (clk, 0, sizeof (struct clock_s));
2684 again:
2685 /* Sudden death. */
2686 if (strncasecmp (p, "SD", 2) == 0)
2688 n = 0;
2689 p += 2;
2690 goto tc;
2693 n = parse_clock_time (&p);
2695 if (n == -1)
2696 return 1;
2698 if (!n)
2699 goto done;
2701 /* Time control. */
2703 if (*p == '/')
2705 if (plus)
2706 return 1;
2708 /* Sudden death without a previous time control. */
2709 if (!n && !tc)
2710 return 1;
2712 m = n;
2713 p++;
2714 n = parse_clock_time (&p);
2716 if (n == -1)
2717 return 1;
2719 if (tc >= MAX_TC)
2721 message (ERROR_STR, ANY_KEY_STR, "%s (%i)",
2722 _("Maximum number of time controls reached"), MAX_TC);
2723 return 1;
2726 clk->tc[tc][0] = m;
2727 clk->tc[tc++][1] = n;
2728 SKIP_SPACE (p);
2730 if (*p == '+')
2731 goto move_incr;
2733 if (*p)
2734 goto again;
2736 goto done;
2739 if (plus)
2740 *incr = n;
2741 else
2742 clk->tc[clk->tcn][1] =
2743 (n <= clk->elapsed.tv_sec) ? clk->elapsed.tv_sec + n : n;
2745 move_incr:
2746 if (*p)
2748 if (*p++ == '+')
2750 if (!isdigit (*p))
2751 return 1;
2753 n = parse_clock_time (&p);
2755 if (n == -1 || *p)
2756 return 1;
2758 clk->incr = n;
2760 SKIP_SPACE (p);
2762 if (*p)
2763 return 1;
2765 else
2766 return 1;
2769 done:
2770 return 0;
2773 static int
2774 parse_which_clock (struct clock_s *clk, char *str)
2776 struct clock_s tmp;
2777 int incr = 0;
2779 memcpy (&tmp, clk, sizeof (struct clock_s));
2781 if (parse_clock_input (&tmp, str, &incr))
2783 cmessage (ERROR_STR, ANY_KEY_STR, _("Invalid clock specification"));
2784 return 1;
2787 memcpy (clk, &tmp, sizeof (struct clock_s));
2788 clk->tc[clk->tcn][1] += incr;
2789 return 0;
2792 void
2793 do_clock_input_finalize (WIN * win)
2795 struct userdata_s *d = gp->data;
2796 struct input_data_s *in = win->data;
2797 char *p = in->str;
2799 if (!in->str)
2801 free (in);
2802 return;
2805 SKIP_SPACE (p);
2807 if (tolower (*p) == 'w')
2809 p++;
2811 if (parse_which_clock (&d->wclock, p))
2812 goto done;
2814 else if (tolower (*p) == 'b')
2816 p++;
2818 if (parse_which_clock (&d->bclock, p))
2819 goto done;
2821 else
2823 if (parse_which_clock (&d->wclock, p))
2824 goto done;
2826 if (parse_which_clock (&d->bclock, p))
2827 goto done;
2830 if (!d->wclock.tc[0][1] && !d->bclock.tc[0][1])
2831 CLEAR_FLAG (d->flags, CF_CLOCK);
2832 else
2833 SET_FLAG (d->flags, CF_CLOCK);
2835 done:
2836 free (in->str);
2837 free (in);
2840 void
2841 do_engine_command_finalize (WIN * win)
2843 struct userdata_s *d = gp->data;
2844 struct input_data_s *in = win->data;
2845 int x;
2847 if (!in->str)
2849 free (in);
2850 return;
2853 if (!d->engine)
2854 goto done;
2856 x = d->engine->status;
2857 send_to_engine (gp, -1, "%s\n", in->str);
2858 d->engine->status = x;
2860 done:
2861 free (in->str);
2862 free (in);
2865 void
2866 do_board_details ()
2868 config.details = (config.details) ? 0 : 1;
2871 void
2872 do_toggle_strict_castling ()
2874 int n;
2876 pgn_config_get (PGN_STRICT_CASTLING, &n);
2878 if (n == 0)
2879 pgn_config_set (PGN_STRICT_CASTLING, 1);
2880 else
2881 pgn_config_set (PGN_STRICT_CASTLING, 0);
2884 void
2885 do_play_set_clock ()
2887 struct input_data_s *in;
2889 in = Calloc (1, sizeof (struct input_data_s));
2890 in->efunc = do_clock_input_finalize;
2891 construct_input (_("Set Clock"), NULL, 1, 1,
2893 ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n"
2894 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2895 NULL, NULL, 0, in, INPUT_HIST_CLOCK, NULL, -1);
2898 void
2899 do_play_toggle_human ()
2901 struct userdata_s *d = gp->data;
2903 TOGGLE_FLAG (d->flags, CF_HUMAN);
2905 if (!TEST_FLAG (d->flags, CF_HUMAN) && pgn_history_total (gp->hp))
2907 if (init_chess_engine (gp))
2908 return;
2911 CLEAR_FLAG (d->flags, CF_ENGINE_LOOP);
2913 if (d->engine)
2914 d->engine->status = ENGINE_READY;
2917 void
2918 do_play_toggle_engine ()
2920 struct userdata_s *d = gp->data;
2922 TOGGLE_FLAG (d->flags, CF_ENGINE_LOOP);
2923 CLEAR_FLAG (d->flags, CF_HUMAN);
2925 if (d->engine && TEST_FLAG (d->flags, CF_ENGINE_LOOP))
2927 char *fen = pgn_game_to_fen (gp, d->b);
2929 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
2930 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
2931 free (fen);
2936 * This will send a command to the engine skipping the command queue.
2938 void
2939 do_play_send_command ()
2941 struct userdata_s *d = gp->data;
2942 struct input_data_s *in;
2944 if (!d->engine || d->engine->status == ENGINE_OFFLINE)
2946 if (init_chess_engine (gp))
2947 return;
2950 in = Calloc (1, sizeof (struct input_data_s));
2951 in->efunc = do_engine_command_finalize;
2952 construct_input (_("Engine Command"), NULL, 1, 1, NULL, NULL, NULL, 0, in,
2953 INPUT_HIST_ENGINE, NULL, -1);
2957 void do_play_switch_turn()
2959 struct userdata_s *d = gp->data;
2961 pgn_switch_side(gp);
2962 pgn_switch_turn(gp);
2964 if (!TEST_FLAG(d->flags, CF_HUMAN))
2965 add_engine_command(gp, -1,
2966 (gp->side == WHITE) ? "white\n" : "black\n");
2968 update_status_window(gp);
2971 void
2972 do_play_toggle_eh_mode ()
2974 struct userdata_s *d = gp->data;
2976 if (!TEST_FLAG (d->flags, CF_HUMAN))
2978 if (!gp->hindex)
2980 pgn_switch_side (gp, TRUE);
2981 d->play_mode = (d->play_mode) ? PLAY_HE : PLAY_EH;
2982 if (gp->side == BLACK)
2983 update_status_notify (gp, _("Press 'g' to start the game"));
2985 d->rotate = !d->rotate;
2987 else
2988 message (NULL, ANY_KEY_STR,
2989 _("You may only switch sides at the start of the \n"
2990 "game. Press ^K or ^N to begin a new game."));
2994 void
2995 do_play_undo ()
2997 struct userdata_s *d = gp->data;
2999 if (!pgn_history_total (gp->hp))
3000 return;
3002 if (keycount)
3004 if (gp->hindex - keycount < 0)
3005 gp->hindex = 0;
3006 else
3008 if (d->go_move)
3009 gp->hindex -= (keycount * 2) - 1;
3010 else
3011 gp->hindex -= keycount * 2;
3014 else
3016 if (gp->hindex - 2 < 0)
3017 gp->hindex = 0;
3018 else
3020 if (d->go_move)
3021 gp->hindex -= 1;
3022 else
3023 gp->hindex -= 2;
3027 pgn_history_free (gp->hp, gp->hindex);
3028 gp->hindex = pgn_history_total (gp->hp);
3029 pgn_board_update (gp, d->b, gp->hindex);
3031 if (d->engine && d->engine->status == ENGINE_READY)
3033 char *fen = pgn_game_to_fen (gp, d->b);
3035 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
3036 free (fen);
3037 d->engine->status = ENGINE_READY;
3040 update_history_window (gp);
3042 if (d->go_move)
3044 pgn_switch_side (gp, FALSE);
3045 d->go_move--;
3048 d->pm_undo = TRUE;
3051 void
3052 do_play_toggle_pause ()
3054 struct userdata_s *d = gp->data;
3056 if (!TEST_FLAG (d->flags, CF_HUMAN) && gp->turn != gp->side)
3058 d->paused = -1;
3059 return;
3062 d->paused = (d->paused) ? 0 : 1;
3065 void
3066 do_play_go ()
3068 struct userdata_s *d = gp->data;
3070 if (TEST_FLAG (d->flags, CF_HUMAN))
3071 return;
3073 if (fm_loaded_file && gp->side != gp->turn)
3075 pgn_switch_side (gp, FALSE);
3076 add_engine_command (gp, ENGINE_THINKING, "black\n");
3079 add_engine_command (gp, ENGINE_THINKING, "go\n");
3081 // Completa la función para que permita seguir jugando al usarla.
3082 // Complete the function to allow continue playing when using.
3083 if (gp->side == gp->turn)
3084 pgn_switch_side (gp, FALSE);
3086 d->go_move++;
3089 void
3090 do_play_config_command ()
3092 int x, w;
3094 if (config.keys)
3096 for (x = 0; config.keys[x]; x++)
3098 if (config.keys[x]->c == input_c)
3100 switch (config.keys[x]->type)
3102 case KEY_DEFAULT:
3103 add_engine_command (gp, -1, "%ls\n", config.keys[x]->str);
3104 break;
3105 case KEY_SET:
3106 if (!keycount)
3107 break;
3109 add_engine_command (gp, -1,
3110 "%ls %i\n", config.keys[x]->str,
3111 keycount);
3112 keycount = 0;
3113 break;
3114 case KEY_REPEAT:
3115 if (!keycount)
3116 break;
3118 for (w = 0; w < keycount; w++)
3119 add_engine_command (gp, -1, "%ls\n", config.keys[x]->str);
3120 keycount = 0;
3121 break;
3127 update_status_notify (gp, NULL);
3130 void
3131 do_play_cancel_selected ()
3133 struct userdata_s *d = gp->data;
3135 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3136 keycount = 0;
3137 pgn_reset_valid_moves (d->b);
3138 update_status_notify (gp, NULL);
3141 void
3142 do_play_commit ()
3144 struct userdata_s *d = gp->data;
3146 pushkey = keycount = 0;
3147 update_status_notify (gp, NULL);
3149 if (!TEST_FLAG (d->flags, CF_HUMAN) &&
3150 (!d->engine || d->engine->status == ENGINE_THINKING))
3151 return;
3153 if (!d->sp.icon)
3154 return;
3156 d->sp.row = d->c_row;
3157 d->sp.col = d->c_col;
3159 if (d->rotate)
3161 rotate_position (&d->sp.row, &d->sp.col);
3162 rotate_position (&d->sp.srow, &d->sp.scol);
3165 move_to_engine (gp);
3167 // Completa la función para que permita seguir jugando cuando se carga un
3168 // archivo pgn (con juego no terminado) que inicie con turno del lado
3169 // negro.
3170 // Complete the function to allow continue playing when loading a file
3171 // pgn (with unfinished game) you start to turn black side.
3172 if (gp->side != gp->turn)
3173 pgn_switch_side (gp, FALSE);
3175 if (d->rotate && d->sp.icon)
3176 rotate_position (&d->sp.srow, &d->sp.scol);
3178 d->go_move = 0;
3179 fm_loaded_file = FALSE;
3180 d->pm_undo = FALSE;
3183 void
3184 do_play_select ()
3186 struct userdata_s *d = gp->data;
3188 if (!TEST_FLAG (d->flags, CF_HUMAN) && (!d->engine ||
3189 d->engine->status ==
3190 ENGINE_OFFLINE))
3192 if (init_chess_engine (gp))
3193 return;
3196 if (d->engine && d->engine->status == ENGINE_THINKING)
3197 return;
3199 if (d->sp.icon)
3200 do_play_cancel_selected ();
3202 if (d->rotate)
3203 rotate_position (&d->c_row, &d->c_col);
3205 d->sp.icon = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon;
3207 if (pgn_piece_to_int (d->sp.icon) == OPEN_SQUARE)
3209 d->sp.icon = 0;
3210 return;
3213 if (((islower (d->sp.icon) && gp->turn != BLACK)
3214 || (isupper (d->sp.icon) && gp->turn != WHITE)))
3216 struct key_s **k;
3217 char *str = Malloc (512);
3219 for (k = play_keys; *k; k++)
3221 if ((*k)->f == do_play_toggle_eh_mode)
3222 break;
3225 snprintf (str, 512,
3227 ("It is not your turn to move. You may switch playing sides by pressing \"%lc\"."),
3228 *k ? (*k)->c : '?');
3229 message (NULL, ANY_KEY_STR, "%s", str);
3230 free (str);
3231 d->sp.icon = 0;
3232 return;
3233 #if 0
3234 if (pgn_history_total (gp->hp))
3236 message (NULL, ANY_KEY_STR, "%s",
3237 _("It is not your turn to move. You can switch sides "));
3238 d->sp.icon = 0;
3239 return;
3241 else
3243 if (pgn_tag_find (gp->tag, "FEN") != E_PGN_ERR)
3244 return;
3246 add_engine_command (gp, ENGINE_READY, "black\n");
3247 pgn_switch_turn (gp);
3249 if (gp->side != BLACK)
3250 pgn_switch_side (gp);
3252 #endif
3255 d->sp.srow = d->c_row;
3256 d->sp.scol = d->c_col;
3258 if (config.validmoves)
3259 pgn_find_valid_moves (gp, d->b, d->sp.scol, d->sp.srow);
3261 if (d->rotate)
3263 rotate_position (&d->c_row, &d->c_col);
3264 rotate_position (&d->sp.srow, &d->sp.scol);
3267 CLEAR_FLAG (d->flags, CF_NEW);
3268 start_clock (gp);
3271 static void
3272 build_help_line_once (wchar_t *buf, wchar_t **pp, struct key_s *k, int t,
3273 int nlen)
3275 wchar_t *p = *pp, *wc;
3276 int n;
3278 if (k->key)
3279 n = wcslen (k->key);
3280 else
3281 n = 1;
3283 while (n++ <= nlen)
3284 *p++ = ' ';
3286 *p = 0;
3288 if (k->key)
3290 wcsncat (buf, k->key, t - 1);
3291 p = buf + wcslen (buf);
3293 else
3294 *p++ = k->c;
3296 *p++ = ' ';
3297 *p++ = '-';
3298 *p++ = ' ';
3299 *p = 0;
3301 if (k->d)
3302 wcsncat (buf, k->d, t - 1);
3304 if (k->r)
3306 wc = str_to_wchar ("*");
3307 wcsncat (buf, wc, t - 1);
3308 free (wc);
3311 wc = str_to_wchar ("\n");
3312 wcscat (buf, wc);
3313 free (wc);
3314 *pp = buf + wcslen (buf);
3317 static void
3318 calc_help_len (struct key_s *k, int *t, int *nlen, int *len)
3320 if (!k->d)
3321 return;
3323 if (k->key)
3325 if (wcslen (k->key) > *nlen)
3327 *nlen = wcslen (k->key);
3328 *t += *nlen;
3330 else
3331 (*t)++;
3333 else
3334 (*t)++;
3336 if (k->d)
3338 if (wcslen (k->d) > *len)
3339 *len = wcslen (k->d);
3342 *t += *len;
3343 *t += k->r;
3346 static wchar_t *
3347 build_help (struct key_s **keys)
3349 int i, m = 0, nlen = 1, len, t;
3350 wchar_t *buf = NULL;
3351 wchar_t *p;
3352 wchar_t *more_help = str_to_wchar (_("more help"));
3353 const wchar_t *more_help_key = key_lookup (global_keys, do_global_help);
3354 struct key_s k;
3355 int mode = MODE_ANY;
3357 if (!keys)
3359 free (more_help);
3360 return NULL;
3363 for (i = t = len = 0; keys[i]; i++)
3364 calc_help_len (keys[i], &t, &nlen, &len);
3366 if (macros)
3368 if (keys == play_keys)
3369 mode = MODE_PLAY;
3370 else if (keys == history_keys)
3371 mode = MODE_HISTORY;
3372 else if (keys == edit_keys)
3373 mode = MODE_EDIT;
3374 else
3375 mode = MODE_ANY;
3377 for (m = 0; macros[m]; m++)
3379 if (macros[m]->mode == mode)
3381 k.d = macros[m]->desc;
3382 k.r = k.c = 0;
3383 k.key = str_to_wchar (fancy_key_name (macros[m]->c));
3384 calc_help_len (&k, &t, &nlen, &len);
3385 free (k.key);
3390 k.d = more_help;
3391 k.r = k.c = 0;
3392 k.key = (wchar_t *)more_help_key;
3393 calc_help_len (&k, &t, &nlen, &len);
3395 t += 4 + i + 1 + m + 1; // +1 for more_help
3396 buf = Malloc ((t + 1) * sizeof (wchar_t));
3397 p = buf;
3399 for (i = 0; keys[i]; i++)
3401 if (!keys[i]->d)
3402 continue;
3404 build_help_line_once (buf, &p, keys[i], t, nlen);
3407 if (macros)
3409 for (m = 0; macros[m]; m++)
3411 if (macros[m]->mode == mode)
3413 k.d = macros[m]->desc;
3414 k.r = k.c = 0;
3415 k.key = str_to_wchar (fancy_key_name (macros[m]->c));
3416 build_help_line_once (buf, &p, &k, t, nlen);
3417 free (k.key);
3422 k.d = more_help;
3423 k.r = k.c = 0;
3424 k.key = (wchar_t *)more_help_key;
3425 build_help_line_once (buf, &p, &k, t, nlen);
3426 free (more_help);
3427 return buf;
3430 void
3431 do_global_help ()
3433 struct userdata_s *d = gp->data;
3434 wchar_t *buf;
3436 if (!d->global_help)
3438 switch (d->mode)
3440 case MODE_PLAY:
3441 do_play_help ();
3442 return;
3443 case MODE_EDIT:
3444 do_edit_help ();
3445 return;
3446 case MODE_HISTORY:
3447 do_history_help ();
3448 return;
3449 default:
3450 break;
3454 d->global_help = 0;
3455 buf = build_help (global_keys);
3456 construct_message (_("Global Game Keys (* = can take a repeat count)"),
3457 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
3458 0, 1, NULL, "%ls", buf);
3461 void
3462 do_main_help (WIN * win)
3464 struct userdata_s *d = gp->data;
3466 switch (win->c)
3468 case 'p':
3469 do_play_help ();
3470 break;
3471 case 'h':
3472 do_history_help ();
3473 break;
3474 case 'e':
3475 do_edit_help ();
3476 break;
3477 case 'g':
3478 d->global_help = 1;
3479 do_global_help ();
3480 break;
3481 default:
3482 break;
3486 static void
3487 do_more_help (WIN * win)
3489 int i;
3491 for (i = 0; global_keys[i]; i++)
3493 if (global_keys[i]->f == do_global_help
3494 && global_keys[i]->c == win->c)
3495 construct_message (_("Command Key Index"),
3496 _("p/h/e/g or any other key to quit"), 0, 0,
3497 NULL, NULL, NULL, do_main_help, 0, 0, NULL, "%s",
3498 _(" p - play mode keys\n"
3499 " h - history mode keys\n"
3500 " e - board edit mode keys\n"
3501 " g - global game keys"));
3505 static void
3506 do_play_help ()
3508 wchar_t *buf = build_help (play_keys);
3510 construct_message (_("Play Mode Keys (* = can take a repeat count)"),
3511 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
3512 buf, do_more_help, 0, 1, NULL, "%ls", buf);
3515 void
3516 do_play_history_mode ()
3518 struct userdata_s *d = gp->data;
3520 if (!pgn_history_total (gp->hp) ||
3521 (d->engine && d->engine->status == ENGINE_THINKING))
3522 return;
3524 d->mode = MODE_HISTORY;
3525 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
3528 void
3529 do_play_edit_mode ()
3531 struct userdata_s *d = gp->data;
3533 if (pgn_history_total (gp->hp))
3534 return;
3536 pgn_board_init_fen (gp, d->b, NULL);
3537 config.details++;
3538 d->mode = MODE_EDIT;
3541 void
3542 do_edit_insert_finalize (WIN * win)
3544 struct userdata_s *d = win->data;
3546 if (pgn_piece_to_int (win->c) == -1)
3547 return;
3549 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon = win->c;
3552 void
3553 do_edit_select ()
3555 struct userdata_s *d = gp->data;
3557 if (d->sp.icon)
3558 return;
3560 d->sp.icon = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon;
3562 if (pgn_piece_to_int (d->sp.icon) == OPEN_SQUARE)
3564 d->sp.icon = 0;
3565 return;
3568 d->sp.srow = d->c_row;
3569 d->sp.scol = d->c_col;
3572 void
3573 do_edit_commit ()
3575 int p;
3576 struct userdata_s *d = gp->data;
3578 pushkey = keycount = 0;
3579 update_status_notify (gp, NULL);
3581 if (!d->sp.icon)
3582 return;
3584 d->sp.row = d->c_row;
3585 d->sp.col = d->c_col;
3586 p = d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
3587 d->b[RANKTOBOARD (d->sp.row)][FILETOBOARD (d->sp.col)].icon = p;
3588 d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
3589 pgn_int_to_piece (gp->turn, OPEN_SQUARE);
3590 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3593 void
3594 do_edit_delete ()
3596 struct userdata_s *d = gp->data;
3598 if (d->sp.icon)
3599 d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
3600 pgn_int_to_piece (gp->turn, OPEN_SQUARE);
3601 else
3602 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon =
3603 pgn_int_to_piece (gp->turn, OPEN_SQUARE);
3605 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3608 void
3609 do_edit_cancel_selected ()
3611 struct userdata_s *d = gp->data;
3613 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3614 keycount = 0;
3615 update_status_notify (gp, NULL);
3618 void
3619 do_edit_switch_turn ()
3621 pgn_switch_turn (gp);
3624 void
3625 do_edit_toggle_castle ()
3627 struct userdata_s *d = gp->data;
3629 castling_state (gp, d->b, RANKTOBOARD (d->c_row),
3630 FILETOBOARD (d->c_col),
3631 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon,
3635 void
3636 do_edit_insert ()
3638 struct userdata_s *d = gp->data;
3640 construct_message (_("Insert Piece"),
3641 _("P=pawn, R=rook, N=knight, B=bishop, Q=queen, K=king"),
3642 0, 0, NULL, NULL, d->b, do_edit_insert_finalize, 0, 0,
3643 NULL, "%s",
3644 _("Type the piece letter to insert. Lowercase for a black piece, uppercase for a white piece."));
3647 void
3648 do_edit_enpassant ()
3650 struct userdata_s *d = gp->data;
3652 if (d->c_row == 6 || d->c_row == 3)
3654 int n = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].enpassant;
3656 pgn_reset_enpassant (d->b);
3657 if (!n)
3658 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].enpassant = 1;
3662 static void
3663 do_edit_help ()
3665 wchar_t *buf = build_help (edit_keys);
3667 construct_message (_("Edit Mode Keys (* = can take a repeat count)"),
3668 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
3669 0, 1, NULL, "%ls", buf);
3672 void
3673 do_edit_exit ()
3675 struct userdata_s *d = gp->data;
3676 char *fen = pgn_game_to_fen (gp, d->b);
3678 config.details--;
3679 pgn_tag_add (&gp->tag, (char *) "FEN", fen);
3680 free (fen);
3681 pgn_tag_add (&gp->tag, (char *) "SetUp", (char *) "1");
3682 pgn_tag_sort (gp->tag);
3683 pgn_board_update (gp, d->b, gp->hindex);
3684 d->mode = MODE_PLAY;
3687 void
3688 really_do_annotate_finalize (struct input_data_s *in, struct userdata_s *d)
3690 HISTORY *h = in->data;
3691 int len;
3693 if (!in->str)
3695 if (h->comment)
3697 free (h->comment);
3698 h->comment = NULL;
3701 else
3703 len = strlen (in->str);
3704 h->comment = Realloc (h->comment, len + 1);
3705 strncpy (h->comment, in->str, len);
3706 h->comment[len] = 0;
3709 free (in->str);
3710 free (in);
3711 SET_FLAG (d->flags, CF_MODIFIED);
3714 void
3715 do_annotate_finalize (WIN * win)
3717 struct userdata_s *d = gp->data;
3718 struct input_data_s *in = win->data;
3720 really_do_annotate_finalize (in, d);
3723 void
3724 do_find_move_exp_finalize (int init, int which)
3726 int n;
3727 struct userdata_s *d = gp->data;
3728 static int firstrun;
3729 static regex_t r;
3730 int ret;
3731 char errbuf[255];
3733 if (init || !firstrun)
3735 if (!firstrun)
3736 regfree (&r);
3738 if ((ret = regcomp (&r, moveexp, REG_EXTENDED | REG_NOSUB)) != 0)
3740 regerror (ret, &r, errbuf, sizeof (errbuf));
3741 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR,
3742 "%s", errbuf);
3743 return;
3746 firstrun = 1;
3749 if ((n = find_move_exp (gp, r,
3750 (which == -1) ? 0 : 1,
3751 (keycount) ? keycount : 1)) == -1)
3752 return;
3754 gp->hindex = n;
3755 pgn_board_update (gp, d->b, gp->hindex);
3758 void
3759 do_find_move_exp (WIN * win)
3761 struct input_data_s *in = win->data;
3762 int *n = in->data;
3763 int which = *n;
3765 if (in->str)
3767 strncpy (moveexp, in->str, sizeof (moveexp) - 1);
3768 moveexp[sizeof (moveexp) - 1] = 0;
3769 do_find_move_exp_finalize (1, which);
3770 free (in->str);
3773 free (in->data);
3774 free (in);
3777 void
3778 do_move_jump_finalize (int n)
3780 struct userdata_s *d = gp->data;
3782 if (n < 0 || n > (pgn_history_total (gp->hp) / 2))
3783 return;
3785 keycount = 0;
3786 update_status_notify (gp, NULL);
3787 gp->hindex = (n) ? n * 2 - 1 : n * 2;
3788 pgn_board_update (gp, d->b, gp->hindex);
3791 void
3792 do_move_jump (WIN * win)
3794 struct input_data_s *in = win->data;
3796 if (!in->str || !isinteger (in->str))
3798 if (in->str)
3799 free (in->str);
3801 free (in);
3802 return;
3805 do_move_jump_finalize (atoi (in->str));
3806 free (in->str);
3807 free (in);
3810 struct history_menu_s
3812 char *line;
3813 int hindex;
3814 int ravlevel;
3815 int move;
3816 int indent;
3819 void
3820 free_history_menu_data (struct history_menu_s **h)
3822 int i;
3824 if (!h)
3825 return;
3827 for (i = 0; h[i]; i++)
3829 free (h[i]->line);
3830 free (h[i]);
3833 free (h);
3836 void
3837 get_history_data (HISTORY ** hp, struct history_menu_s ***menu, int m,
3838 int turn)
3840 int i, n = 0;
3841 int t = pgn_history_total (hp);
3842 char buf[MAX_SAN_MOVE_LEN + 4];
3843 static int depth;
3844 struct history_menu_s **hmenu = *menu;
3846 if (hmenu)
3847 for (n = 0; hmenu[n]; n++);
3848 else
3849 depth = 0;
3851 for (i = 0; i < t; i++)
3853 hmenu = Realloc (hmenu, (n + 2) * sizeof (struct history_menu_s *));
3854 hmenu[n] = Malloc (sizeof (struct history_menu_s));
3855 snprintf (buf, sizeof (buf), "%c%s%s", (turn == WHITE) ? 'W' : 'B',
3856 hp[i]->move, (hp[i]->comment || hp[i]->nag[0]) ? " !" : "");
3857 hmenu[n]->line = strdup (buf);
3858 hmenu[n]->hindex = i;
3859 hmenu[n]->indent = 0;
3860 hmenu[n]->ravlevel = depth;
3861 hmenu[n]->move = (n && depth > hmenu[n - 1]->ravlevel) ? m++ : m;
3862 n++;
3863 hmenu[n] = NULL;
3865 #if 0
3866 if (hp[i]->rav)
3868 depth++;
3869 get_history_data (hp[i]->rav, &hmenu, m, turn);
3870 for (n = 0; hmenu[n]; n++);
3871 depth--;
3873 if (depth)
3874 m--;
3876 #endif
3878 turn = (turn == WHITE) ? BLACK : WHITE;
3881 *menu = hmenu;
3884 void
3885 history_draw_update (struct menu_input_s *m)
3887 GAME g = m->data;
3888 struct userdata_s *d = g->data;
3890 g->hindex = m->selected + 1;
3891 update_cursor (g, m->selected);
3892 pgn_board_update (g, d->b, m->selected + 1);
3895 struct menu_item_s **
3896 get_history_items (WIN * win)
3898 struct menu_input_s *m = win->data;
3899 GAME g = m->data;
3900 struct userdata_s *d = g->data;
3901 struct history_menu_s **hm = d->data;
3902 struct menu_item_s **items = m->items;
3903 int i;
3905 if (!hm)
3907 get_history_data (g->history, &hm, 0,
3908 TEST_FLAG (g->flags, GF_BLACK_OPENING));
3909 m->selected = g->hindex - 1;
3911 if (m->selected < 0)
3912 m->selected = 0;
3914 m->draw_exit_func = history_draw_update;
3917 d->data = hm;
3919 if (items)
3921 for (i = 0; items[i]; i++)
3922 free (items[i]);
3924 free (items);
3925 items = NULL;
3928 for (i = 0; hm[i]; i++)
3930 items = Realloc (items, (i + 2) * sizeof (struct menu_item_s *));
3931 items[i] = Malloc (sizeof (struct menu_item_s));
3932 items[i]->name = hm[i]->line;
3933 items[i]->value = NULL;
3934 items[i]->selected = 0;
3937 if (items)
3938 items[i] = NULL;
3940 m->nofree = 1;
3941 m->items = items;
3942 return items;
3945 void
3946 history_menu_quit (struct menu_input_s *m)
3948 pushkey = -1;
3951 void
3952 history_menu_exit (WIN * win)
3954 GAME g = win->data;
3955 struct userdata_s *d = g->data;
3956 struct history_menu_s **hm = d->data;
3957 int i;
3959 if (!hm)
3960 return;
3962 for (i = 0; hm[i]; i++)
3964 free (hm[i]->line);
3965 free (hm[i]);
3968 free (hm);
3969 d->data = NULL;
3972 // FIXME RAV
3973 void
3974 history_menu_next (struct menu_input_s *m)
3976 GAME g = m->data;
3977 struct userdata_s *d = g->data;
3978 struct history_menu_s **hm = d->data;
3979 int n, t;
3981 for (t = 0; hm[t]; t++);
3983 if (m->selected + 1 == t)
3984 n = 0;
3985 else
3986 n = hm[m->selected + 1]->hindex;
3988 n++;
3989 g->hindex = n;
3992 // FIXME RAV
3993 void
3994 history_menu_prev (struct menu_input_s *m)
3996 GAME g = m->data;
3997 struct userdata_s *d = g->data;
3998 struct history_menu_s **hm = d->data;
3999 int n, t;
4001 for (t = 0; hm[t]; t++);
4003 if (m->selected - 1 < 0)
4004 n = t - 1;
4005 else
4006 n = hm[m->selected - 1]->hindex;
4008 n++;
4009 g->hindex = n;
4012 void
4013 history_menu_help (struct menu_input_s *m)
4015 message (_("History Menu Help"), ANY_KEY_STR, "%s",
4016 _(" UP/DOWN - previous/next menu item\n"
4017 " HOME/END - first/last menu item\n"
4018 " PGDN/PGUP - next/previous page\n"
4019 " a-zA-Z0-9 - jump to item\n"
4020 " CTRL-a - annotate the selected move\n"
4021 " ENTER - view annotation\n"
4022 " CTRL-d - toggle board details\n"
4023 " ESCAPE/M - return to move history"));
4026 void
4027 do_annotate_move (HISTORY * hp)
4029 char buf[COLS - 4];
4030 struct input_data_s *in;
4032 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Editing Annotation for"),
4033 hp->move);
4034 in = Calloc (1, sizeof (struct input_data_s));
4035 in->data = hp;
4036 in->efunc = do_annotate_finalize;
4037 construct_input (buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
4038 _("Type CTRL-t to edit NAG"), edit_nag, NULL,
4039 CTRL_KEY ('T'), in, -1, NULL, -1);
4042 void
4043 history_menu_view_annotation (struct menu_input_s *m)
4045 GAME g = m->data;
4047 // FIXME RAV
4048 view_annotation (g->history[m->selected]);
4051 void
4052 history_menu_annotate_finalize (WIN * win)
4054 struct input_data_s *in = win->data;
4055 GAME g = in->moredata;
4056 struct userdata_s *d = g->data;
4057 struct history_menu_s **hm = d->data;
4059 really_do_annotate_finalize (in, d);
4060 free_history_menu_data (hm);
4061 hm = NULL;
4062 get_history_data (g->history, &hm, 0,
4063 TEST_FLAG (g->flags, GF_BLACK_OPENING));
4064 d->data = hm;
4065 pushkey = REFRESH_MENU;
4068 void
4069 history_menu_annotate (struct menu_input_s *m)
4071 GAME g = m->data;
4072 char buf[COLS - 4];
4073 struct input_data_s *in;
4074 HISTORY *hp = g->history[m->selected]; // FIXME RAV
4076 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Editing Annotation for"),
4077 hp->move);
4078 in = Calloc (1, sizeof (struct input_data_s));
4079 in->data = hp;
4080 in->moredata = m->data;
4081 in->efunc = history_menu_annotate_finalize;
4082 construct_input (buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
4083 _("Type CTRL-t to edit NAG"), edit_nag, NULL,
4084 CTRL_KEY ('T'), in, -1, NULL, -1);
4087 void
4088 history_menu_details (struct menu_input_s *m)
4090 do_board_details ();
4093 // FIXME RAV
4094 void
4095 history_menu_print (WIN * win)
4097 struct menu_input_s *m = win->data;
4098 GAME g = m->data;
4099 struct userdata_s *d = g->data;
4100 struct history_menu_s **hm = d->data;
4101 struct history_menu_s *h = hm[m->top];
4102 int i;
4103 char *p = m->item->name;
4104 int line = m->print_line - 2;
4106 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
4107 * attr_t data type.
4109 attr_t attrs;
4110 short pair;
4111 int total;
4113 for (total = 0; hm[total]; total++);
4114 wattr_get (win->w, &attrs, &pair, NULL);
4115 wattroff (win->w, COLOR_PAIR (pair));
4116 mvwaddch (win->w, m->print_line, 1,
4117 *p == 'W' ? *p | mix_cp (CP_BOARD_WHITE, CP_HISTORY_WINDOW,
4118 ATTRS (CP_BOARD_WHITE),
4119 A_FG_B_BG) : *p | mix_cp (CP_BOARD_BLACK,
4120 CP_HISTORY_WINDOW,
4121 ATTRS
4122 (CP_BOARD_BLACK),
4123 A_FG_B_BG));
4124 p++;
4126 if (h->hindex == 0 && line == 0)
4127 waddch (win->w, ACS_ULCORNER | CP_HISTORY_MENU_LG);
4128 else if ((!hm[h->hindex + (win->rows - 5) + 1] && line == win->rows - 5) ||
4129 (m->top + line == total - 1))
4130 waddch (win->w, ACS_LLCORNER | CP_HISTORY_MENU_LG);
4131 else if (hm[m->top + 1]->ravlevel != h->ravlevel || !h->ravlevel)
4132 waddch (win->w, ACS_LTEE | CP_HISTORY_MENU_LG);
4133 else
4134 waddch (win->w, ACS_VLINE | CP_HISTORY_MENU_LG);
4136 wattron (win->w, COLOR_PAIR (pair) | attrs);
4138 for (i = 2; *p; p++, i++)
4139 waddch (win->w, (*p == '!') ? *p | A_BOLD : *p);
4141 while (i++ < win->cols - 2)
4142 waddch (win->w, ' ');
4145 void
4146 history_menu (GAME g)
4148 struct menu_key_s **keys = NULL;
4150 add_menu_key (&keys, KEY_ESCAPE, history_menu_quit);
4151 add_menu_key (&keys, 'M', history_menu_quit);
4152 add_menu_key (&keys, KEY_UP, history_menu_prev);
4153 add_menu_key (&keys, KEY_DOWN, history_menu_next);
4154 add_menu_key (&keys, keycode_lookup (global_keys, do_global_help),
4155 history_menu_help);
4156 add_menu_key (&keys, CTRL_KEY ('a'), history_menu_annotate);
4157 add_menu_key (&keys, CTRL_KEY ('d'), history_menu_details);
4158 add_menu_key (&keys, '\n', history_menu_view_annotation);
4159 construct_menu (MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES, TAG_WIDTH,
4160 0, config.boardleft ? BOARD_WIDTH : 0,
4161 _("Move History Tree"), 1, get_history_items, keys, g,
4162 history_menu_print, history_menu_exit, history_menu_resize);
4165 void
4166 do_history_menu ()
4168 history_menu (gp);
4171 void
4172 do_history_half_move_toggle ()
4174 movestep = (movestep == 1) ? 2 : 1;
4175 update_history_window (gp);
4178 void
4179 do_history_rotate_board ()
4181 struct userdata_s *d = gp->data;
4182 d->rotate = !d->rotate;
4185 void
4186 do_history_jump_next ()
4188 struct userdata_s *d = gp->data;
4190 pgn_history_next (gp, d->b, (keycount > 0) ?
4191 config.jumpcount * keycount * movestep :
4192 config.jumpcount * movestep);
4195 void
4196 do_history_jump_prev ()
4198 struct userdata_s *d = gp->data;
4200 pgn_history_prev (gp, d->b, (keycount) ?
4201 config.jumpcount * keycount * movestep :
4202 config.jumpcount * movestep);
4205 void
4206 do_history_prev ()
4208 struct userdata_s *d = gp->data;
4210 pgn_history_prev (gp, d->b, (keycount) ? keycount * movestep : movestep);
4213 void
4214 do_history_next ()
4216 struct userdata_s *d = gp->data;
4218 pgn_history_next (gp, d->b, (keycount) ? keycount * movestep : movestep);
4221 void
4222 do_history_mode_finalize (struct userdata_s *d)
4224 pushkey = 0;
4225 d->mode = MODE_PLAY;
4228 void
4229 do_history_mode_confirm (WIN * win)
4231 struct userdata_s *d = gp->data;
4232 wchar_t str[] = { win->c, 0 };
4234 if (!wcscmp (str, resume_wchar))
4236 pgn_history_free (gp->hp, gp->hindex);
4237 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4239 #if 0
4240 case 'C':
4241 case 'c':
4242 if (pgn_history_rav_new (gp, d->b, gp->hindex) != E_PGN_OK)
4243 return;
4245 break;
4246 #endif
4247 else
4248 return;
4250 if (!TEST_FLAG (d->flags, CF_HUMAN))
4252 char *fen = pgn_game_to_fen (gp, d->b);
4254 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
4255 free (fen);
4258 do_history_mode_finalize (d);
4261 void
4262 do_history_toggle ()
4264 struct userdata_s *d = gp->data;
4266 // FIXME Resuming from previous history could append to a RAV.
4267 if (gp->hindex != pgn_history_total (gp->hp))
4269 if (!pushkey)
4270 construct_message (NULL, _("What would you like to do?"), 0, 1,
4271 NULL, NULL, NULL, do_history_mode_confirm, 0, 0,
4272 NULL, _
4273 ("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."),
4274 resume_wchar);
4275 return;
4277 else
4279 if (TEST_FLAG (gp->flags, GF_GAMEOVER))
4280 return;
4283 if (gp->side != gp->turn)
4285 d->play_mode = PLAY_EH;
4287 else
4289 d->play_mode = PLAY_HE;
4290 d->rotate = FALSE;
4293 do_history_mode_finalize (d);
4296 void
4297 do_history_annotate ()
4299 int n = gp->hindex;
4301 if (n && gp->hp[n - 1]->move)
4302 n--;
4303 else
4304 return;
4306 do_annotate_move (gp->hp[n]);
4309 static void
4310 do_history_help ()
4312 wchar_t *buf = build_help (history_keys);
4314 construct_message (_("History Mode Keys (* = can take a repeat count)"),
4315 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
4316 buf, do_more_help, 0, 1, NULL, "%ls", buf);
4319 void
4320 do_history_find (int which)
4322 struct input_data_s *in;
4323 int *p;
4325 if (pgn_history_total (gp->hp) < 2)
4326 return;
4328 in = Calloc (1, sizeof (struct input_data_s));
4329 p = Malloc (sizeof (int));
4330 *p = which;
4331 in->data = p;
4332 in->efunc = do_find_move_exp;
4334 if (!*moveexp || which == 0)
4336 construct_input (_("Find Move Text Expression"), NULL, 1, 0, NULL, NULL,
4337 NULL, 0, in, INPUT_HIST_MOVE_EXP, NULL, -1);
4338 return;
4341 free (p);
4342 free (in);
4343 do_find_move_exp_finalize (0, which);
4346 void
4347 do_history_find_new ()
4349 do_history_find (0);
4352 void
4353 do_history_find_prev ()
4355 do_history_find (-1);
4358 void
4359 do_history_find_next ()
4361 do_history_find (1);
4364 void
4365 do_history_rav (int which)
4367 struct userdata_s *d = gp->data;
4369 rav_next_prev (gp, d->b, which);
4372 void
4373 do_history_rav_next ()
4375 do_history_rav (1);
4378 void
4379 do_history_rav_prev ()
4381 do_history_rav (0);
4384 void
4385 do_history_jump ()
4387 struct input_data_s *in;
4389 if (pgn_history_total (gp->hp) < 2)
4390 return;
4392 if (!keycount)
4394 in = Calloc (1, sizeof (struct input_data_s));
4395 in->efunc = do_move_jump;
4397 construct_input (_("Jump to Move Number"), NULL, 1, 1, NULL,
4398 NULL, NULL, 0, in, -1, NULL, 0);
4399 return;
4402 do_move_jump_finalize (keycount);
4405 static void
4406 free_userdata_once (GAME g)
4408 struct userdata_s *d = g->data;
4410 if (!d)
4411 return;
4413 if (d->engine)
4415 stop_engine (g);
4417 if (d->engine->enginebuf)
4419 int n;
4421 for (n = 0; d->engine->enginebuf[n]; n++)
4422 free (d->engine->enginebuf[n]);
4424 free (d->engine->enginebuf);
4427 if (d->engine->queue)
4429 struct queue_s **q;
4431 for (q = d->engine->queue; *q; q++)
4432 free (*q);
4434 free (d->engine->queue);
4437 free (d->engine);
4440 #ifdef WITH_LIBPERL
4441 if (d->perlfen)
4442 free (d->perlfen);
4444 if (d->oldfen)
4445 free (d->oldfen);
4446 #endif
4448 free (d);
4449 g->data = NULL;
4452 static void
4453 free_userdata ()
4455 int i;
4457 for (i = 0; i < gtotal; i++)
4459 free_userdata_once (game[i]);
4460 game[i]->data = NULL;
4464 void
4465 update_loading_window (int n)
4467 char buf[16];
4469 if (!loadingw)
4471 loadingw = newwin (3, COLS / 2, CALCPOSY (3), CALCPOSX (COLS / 2));
4472 loadingp = new_panel (loadingw);
4473 wbkgd (loadingw, CP_MESSAGE_WINDOW);
4476 wmove (loadingw, 0, 0);
4477 wclrtobot (loadingw);
4478 wattron (loadingw, CP_MESSAGE_BORDER);
4479 box (loadingw, ACS_VLINE, ACS_HLINE);
4480 wattroff (loadingw, CP_MESSAGE_BORDER);
4481 mvwprintw (loadingw, 1, CENTER_INT ((COLS / 2), 11 +
4482 strlen (itoa (gtotal, buf))),
4483 _("Loading... %i%% (%i games)"), n, gtotal);
4484 update_panels ();
4485 doupdate ();
4488 static void
4489 init_userdata_once (GAME g, int n)
4491 struct userdata_s *d = NULL;
4493 d = Calloc (1, sizeof (struct userdata_s));
4494 d->n = n;
4495 d->c_row = 2, d->c_col = 5;
4496 SET_FLAG (d->flags, CF_NEW);
4497 g->data = d;
4499 if (pgn_board_init_fen (g, d->b, NULL) != E_PGN_OK)
4500 pgn_board_init (d->b);
4503 void
4504 init_userdata ()
4506 int i;
4508 for (i = 0; i < gtotal; i++)
4509 init_userdata_once (game[i], i);
4512 void
4513 fix_marks (int *start, int *end)
4515 int i;
4517 *start = (*start < 0) ? 0 : *start;
4518 *end = (*end < 0) ? 0 : *end;
4520 if (*start > *end)
4522 i = *start;
4523 *start = *end;
4524 *end = i + 1;
4527 *end = (*end > gtotal) ? gtotal : *end;
4530 void
4531 do_new_game_finalize (GAME g)
4533 struct userdata_s *d = g->data;
4535 d->mode = MODE_PLAY;
4536 update_status_notify (g, NULL);
4537 d->rotate = FALSE;
4538 d->go_move = 0;
4541 void
4542 do_new_game_from_scratch (WIN * win)
4544 wchar_t str[] = { win->c, 0 };
4546 if (wcscmp (str, yes_wchar))
4547 return;
4549 stop_clock ();
4550 free_userdata ();
4551 pgn_parse (NULL);
4552 gp = game[gindex];
4553 add_custom_tags (&gp->tag);
4554 init_userdata ();
4555 loadfile[0] = 0;
4556 do_new_game_finalize (gp);
4559 void
4560 do_new_game ()
4562 pgn_new_game ();
4563 gp = game[gindex];
4564 add_custom_tags (&gp->tag);
4565 init_userdata_once (gp, gindex);
4566 do_new_game_finalize (gp);
4569 void
4570 do_game_delete_finalize (int n)
4572 struct userdata_s *d;
4574 delete_game ((!n) ? gindex : -1);
4575 d = gp->data;
4576 if (d->mode != MODE_EDIT)
4577 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4580 void
4581 do_game_delete_confirm (WIN * win)
4583 int *n;
4584 wchar_t str[] = { win->c, 0 };
4586 if (wcscmp (str, yes_wchar))
4588 free (win->data);
4589 return;
4592 n = (int *) win->data;
4593 do_game_delete_finalize (*n);
4594 free (win->data);
4597 void
4598 do_game_delete ()
4600 char *tmp = NULL;
4601 int i, n;
4602 struct userdata_s *d;
4603 int *p;
4605 if (gtotal < 2)
4607 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4608 return;
4611 tmp = NULL;
4613 for (i = n = 0; i < gtotal; i++)
4615 d = game[i]->data;
4617 if (TEST_FLAG (d->flags, CF_DELETE))
4618 n++;
4621 if (!n)
4622 tmp = _("Delete the current game?");
4623 else
4625 if (n == gtotal)
4627 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4628 return;
4631 tmp = _("Delete all games marked for deletion?");
4634 if (config.deleteprompt)
4636 p = Malloc (sizeof (int));
4637 *p = n;
4638 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, p,
4639 do_game_delete_confirm, 0, 0, NULL, "%s", tmp);
4640 return;
4643 do_game_delete_finalize (n);
4646 void
4647 do_find_game_exp_finalize (int which)
4649 struct userdata_s *d = gp->data;
4650 int n;
4652 if ((n = find_game_exp (gameexp, (which == -1) ? 0 : 1,
4653 (keycount) ? keycount : 1)) == -1)
4655 update_status_notify (gp, "%s", _("No matches found"));
4656 return;
4659 gindex = n;
4660 d = gp->data;
4662 if (pgn_history_total (gp->hp))
4663 d->mode = MODE_HISTORY;
4665 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4668 void
4669 do_find_game_exp (WIN * win)
4671 struct input_data_s *in = win->data;
4672 int *n = in->data;
4673 int c = *n;
4675 if (in->str)
4677 strncpy (gameexp, in->str, sizeof (gameexp));
4678 gameexp[sizeof (gameexp) - 1] = 0;
4680 if (c == '?')
4681 c = '}';
4683 do_find_game_exp_finalize (c);
4684 free (in->str);
4687 free (in->data);
4688 free (in);
4691 void
4692 do_game_jump_finalize (int n)
4694 struct userdata_s *d;
4696 if (--n > gtotal - 1 || n < 0)
4697 return;
4699 gindex = n;
4700 gp = game[gindex];
4701 d = gp->data;
4702 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4703 update_status_notify (gp, NULL);
4706 void
4707 do_game_jump (WIN * win)
4709 struct input_data_s *in = win->data;
4711 if (!in->str || !isinteger (in->str))
4713 if (in->str)
4714 free (in->str);
4716 free (in);
4717 return;
4720 do_game_jump_finalize (atoi (in->str));
4721 free (in->str);
4722 free (in);
4725 void
4726 do_load_file (WIN * win)
4728 struct input_data_s *in = win->data;
4729 char *tmp = in->str;
4730 struct userdata_s *d;
4731 PGN_FILE *pgn = NULL;
4732 int n;
4734 if (!in->str)
4736 free (in);
4737 return;
4740 if ((tmp = pathfix (tmp)) == NULL)
4741 goto done;
4743 n = pgn_open (tmp, "r", &pgn);
4745 if (n == E_PGN_ERR)
4747 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, strerror (errno));
4748 goto done;
4750 else if (n == E_PGN_INVALID)
4752 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp,
4753 _("Not a regular file"));
4754 goto done;
4757 free_userdata ();
4759 if (pgn_parse (pgn) == E_PGN_ERR)
4761 del_panel (loadingp);
4762 delwin (loadingw);
4763 loadingw = NULL;
4764 loadingp = NULL;
4765 init_userdata ();
4766 goto done;
4769 del_panel (loadingp);
4770 delwin (loadingw);
4771 loadingw = NULL;
4772 loadingp = NULL;
4773 init_userdata ();
4774 strncpy (loadfile, tmp, sizeof (loadfile));
4775 loadfile[sizeof (loadfile) - 1] = 0;
4776 gp = game[gindex];
4777 d = gp->data;
4779 if (pgn_history_total (gp->hp))
4780 d->mode = MODE_HISTORY;
4782 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4784 fm_loaded_file = TRUE;
4785 d->rotate = FALSE;
4787 done:
4788 pgn_close (pgn);
4790 if (in->str)
4791 free (in->str);
4793 free (in);
4796 void
4797 do_game_save (WIN * win)
4799 struct input_data_s *in = win->data;
4800 int *x = in->data;
4801 int n = *x;
4802 char *tmp = in->str;
4803 char tfile[FILENAME_MAX];
4804 char *p;
4805 int i;
4806 struct userdata_s *d;
4808 if (!tmp || (tmp = pathfix (tmp)) == NULL)
4809 goto done;
4811 if (pgn_is_compressed (tmp) == E_PGN_ERR)
4813 p = tmp + strlen (tmp) - 1;
4815 if (*p != 'n' || *(p - 1) != 'g' || *(p - 2) != 'p' || *(p - 3) != '.')
4817 snprintf (tfile, sizeof (tfile), "%s.pgn", tmp);
4818 tmp = tfile;
4823 * When in edit mode, update the FEN tag.
4825 if (n == -1)
4827 for (i = 0; i < gtotal; i++)
4829 d = game[i]->data;
4831 if (d->mode == MODE_EDIT)
4833 char *fen = pgn_game_to_fen (game[i], d->b);
4835 pgn_tag_add (&game[i]->tag, (char *) "FEN", fen);
4836 free (fen);
4840 else
4842 d = game[n]->data;
4844 if (d->mode == MODE_EDIT)
4846 char *fen = pgn_game_to_fen (game[n], d->b);
4848 pgn_tag_add (&game[n]->tag, (char *) "FEN", fen);
4849 free (fen);
4853 save_pgn (tmp, n);
4855 done:
4856 if (in->str)
4857 free (in->str);
4859 free (in->data);
4860 free (in);
4863 void
4864 do_get_game_save_input (int n)
4866 struct input_data_s *in = Calloc (1, sizeof (struct input_data_s));
4867 int *p = Malloc (sizeof (int));
4869 in->efunc = do_game_save;
4870 *p = n;
4871 in->data = p;
4873 construct_input (_("Save Game Filename"), loadfile, 1, 1,
4874 _("Type TAB for file browser"), file_browser, NULL, '\t',
4875 in, INPUT_HIST_FILE, NULL, -1);
4878 void
4879 do_game_save_multi_confirm (WIN * win)
4881 int i;
4882 wchar_t str[] = { win->c, 0 };
4884 if (!wcscmp (str, current_wchar))
4885 i = gindex;
4886 else if (!wcscmp (str, all_wchar))
4887 i = -1;
4888 else
4890 update_status_notify (gp, "%s", _("Save game aborted."));
4891 return;
4894 do_get_game_save_input (i);
4897 void
4898 do_global_about ()
4900 cmessage (_("ABOUT"), ANY_KEY_STR,
4901 _("%s\nUsing %s with %i colors and %i color pairs\n%s\n%s"),
4902 PACKAGE_STRING, curses_version (), COLORS, COLOR_PAIRS,
4903 COPYRIGHT, CBOARD_URL);
4906 void
4907 global_game_next_prev (int which)
4909 struct userdata_s *d;
4911 game_next_prev (gp, (which == 1) ? 1 : 0, (keycount) ? keycount : 1);
4912 d = gp->data;
4914 if (delete_count)
4916 if (which == 1)
4918 markend = markstart + delete_count;
4919 delete_count = 0;
4921 else
4923 markend = markstart - delete_count + 1;
4924 delete_count = -1; // to fix gindex in the other direction
4927 fix_marks (&markstart, &markend);
4928 do_global_toggle_delete ();
4931 if (d->mode == MODE_HISTORY)
4932 pgn_board_update (gp, d->b, gp->hindex);
4933 else if (d->mode == MODE_PLAY)
4934 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4937 void
4938 do_global_next_game ()
4940 global_game_next_prev (1);
4943 void
4944 do_global_prev_game ()
4946 global_game_next_prev (0);
4949 void
4950 global_find (int which)
4952 struct input_data_s *in;
4953 int *p;
4955 if (gtotal < 2)
4956 return;
4958 in = Calloc (1, sizeof (struct input_data_s));
4959 p = Malloc (sizeof (int));
4960 *p = which;
4961 in->data = p;
4962 in->efunc = do_find_game_exp;
4964 if (!*gameexp || which == 0)
4966 construct_input (_("Find Game by Tag Expression"), NULL, 1, 0,
4967 _("[name expression:]value expression"), NULL, NULL, 0,
4968 in, INPUT_HIST_GAME_EXP, NULL, -1);
4969 return;
4972 free (p);
4973 free (in);
4974 do_find_game_exp_finalize (which);
4977 void
4978 do_global_find_new ()
4980 global_find (0);
4983 void
4984 do_global_find_next ()
4986 global_find (1);
4989 void
4990 do_global_find_prev ()
4992 global_find (-1);
4995 void
4996 do_global_game_jump ()
4998 if (gtotal < 2)
4999 return;
5001 if (!keycount)
5003 struct input_data_s *in;
5005 in = Calloc (1, sizeof (struct input_data_s));
5006 in->efunc = do_game_jump;
5007 construct_input (_("Jump to Game Number"), NULL, 1, 1, NULL, NULL, NULL,
5008 0, in, -1, NULL, 0);
5009 return;
5012 do_game_jump_finalize (keycount);
5015 void
5016 do_global_toggle_delete ()
5018 int i;
5020 pushkey = 0;
5022 if (gtotal < 2)
5023 return;
5025 if (keycount && delete_count == 0)
5027 markstart = gindex;
5028 delete_count = keycount;
5029 update_status_notify (gp, "%s (delete)", status.notify);
5030 return;
5033 if (markstart >= 0 && markend >= 0)
5035 for (i = markstart; i < markend; i++)
5037 if (toggle_delete_flag (i))
5039 return;
5043 gindex = (delete_count < 0) ? markstart : i - 1;
5045 else
5047 if (toggle_delete_flag (gindex))
5048 return;
5051 markstart = markend = -1;
5052 delete_count = 0;
5053 update_status_window (gp);
5056 void
5057 do_global_delete_game ()
5059 do_game_delete ();
5062 void
5063 do_global_tag_edit ()
5065 struct userdata_s *d = gp->data;
5067 edit_tags (gp, d->b, 1);
5070 void
5071 do_global_tag_view ()
5073 struct userdata_s *d = gp->data;
5075 edit_tags (gp, d->b, 0);
5078 void
5079 do_global_resume_game ()
5081 struct input_data_s *in;
5083 in = Calloc (1, sizeof (struct input_data_s));
5084 in->efunc = do_load_file;
5085 construct_input (_("Load Filename"), NULL, 1, 1,
5086 _("Type TAB for file browser"), file_browser, NULL, '\t',
5087 in, INPUT_HIST_FILE, NULL, -1);
5090 void
5091 do_global_save_game ()
5093 if (gtotal > 1)
5095 construct_message (NULL, _("What would you like to do?"), 0, 1,
5096 NULL, NULL, NULL, do_game_save_multi_confirm, 0, 0,
5097 NULL, _
5098 ("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."),
5099 current_wchar, all_wchar);
5100 return;
5103 do_get_game_save_input (-1);
5106 void
5107 do_global_new_game ()
5109 do_new_game ();
5112 void
5113 copy_game_common (int fen)
5115 int g = gindex;
5116 int i, n;
5117 struct userdata_s *d = gp->data;
5118 char *fentag = fen ? pgn_game_to_fen (gp, d->b) : NULL;
5120 do_global_new_game ();
5121 d = gp->data;
5122 n = pgn_tag_total (game[g]->tag);
5124 for (i = 0; i < n; i++)
5125 pgn_tag_add (&gp->tag, game[g]->tag[i]->name, game[g]->tag[i]->value);
5127 if (fentag)
5129 pgn_tag_add (&gp->tag, (char *) "SetUp", (char *) "1");
5130 pgn_tag_add (&gp->tag, (char *) "FEN", fentag);
5131 free (fentag);
5132 pgn_tag_sort (gp->tag);
5134 else
5136 pgn_board_init_fen (gp, d->b, NULL);
5137 n = pgn_history_total (game[g]->history);
5139 // FIXME RAV
5140 for (i = 0; i < n; i++)
5142 char *frfr = NULL;
5143 char *move = strdup (game[g]->history[i]->move);
5145 if (pgn_parse_move (gp, d->b, &move, &frfr) != E_PGN_OK)
5147 free (move);
5148 SET_FLAG (gp->flags, GF_PERROR);
5149 return;
5152 pgn_history_add (gp, d->b, move);
5153 free (move);
5154 free (frfr);
5155 pgn_switch_turn (gp);
5159 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
5162 void
5163 do_global_copy_game ()
5165 copy_game_common (0);
5168 void
5169 do_global_copy_game_fen ()
5171 copy_game_common (1);
5174 void
5175 do_global_new_all ()
5177 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
5178 do_new_game_from_scratch, 0, 0, NULL, "%s",
5179 _("Really start a new game from scratch?"));
5182 void
5183 do_quit (WIN * win)
5185 wchar_t str[] = { win->c, 0 };
5186 int n = wcscmp (str, yes_wchar);
5188 if (n)
5189 return;
5191 quit = 1;
5194 void
5195 do_global_quit ()
5197 if (config.exitdialogbox)
5198 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
5199 do_quit, 0, 0, NULL, "%s", _("Want to Quit?"));
5200 else
5201 quit = 1;
5204 void
5205 do_global_toggle_engine_window ()
5207 if (!enginew)
5209 enginew = newwin (LINES, COLS, 0, 0);
5210 enginep = new_panel (enginew);
5211 window_draw_title (enginew, _("Engine IO Window"), COLS,
5212 CP_MESSAGE_TITLE, CP_MESSAGE_BORDER);
5213 hide_panel (enginep);
5216 if (panel_hidden (enginep))
5218 update_engine_window (gp);
5219 top_panel (enginep);
5221 else
5223 hide_panel (enginep);
5227 void
5228 do_global_toggle_board_details ()
5230 do_board_details ();
5233 void
5234 do_play_toggle_strict_castling ()
5236 do_toggle_strict_castling ();
5239 // Global and other keys.
5240 static int
5241 globalkeys ()
5243 struct userdata_s *d = gp->data;
5244 int i;
5247 * These cannot be modified and other game mode keys cannot conflict with
5248 * these.
5250 switch (input_c)
5252 case KEY_ESCAPE:
5253 d->sp.icon = d->sp.srow = d->sp.scol = 0;
5254 markend = markstart = 0;
5256 if (keycount)
5258 keycount = 0;
5259 update_status_notify (gp, NULL);
5262 if (config.validmoves)
5263 pgn_reset_valid_moves (d->b);
5265 return 1;
5266 case '0' ... '9':
5267 i = input_c - '0';
5269 if (keycount)
5270 keycount = keycount * 10 + i;
5271 else
5272 keycount = i;
5274 update_status_notify (gp, _("Repeat %i"), keycount);
5275 return -1;
5276 case KEY_UP:
5277 if (d->mode == MODE_HISTORY)
5278 return 0;
5280 if (keycount)
5281 d->c_row += keycount;
5282 else
5283 d->c_row++;
5285 if (d->c_row > 8)
5286 d->c_row = 1;
5288 return 1;
5289 case KEY_DOWN:
5290 if (d->mode == MODE_HISTORY)
5291 return 0;
5293 if (keycount)
5295 d->c_row -= keycount;
5296 update_status_notify (gp, NULL);
5298 else
5299 d->c_row--;
5301 if (d->c_row < 1)
5302 d->c_row = 8;
5304 return 1;
5305 case KEY_LEFT:
5306 if (d->mode == MODE_HISTORY)
5307 return 0;
5309 if (keycount)
5310 d->c_col -= keycount;
5311 else
5312 d->c_col--;
5314 if (d->c_col < 1)
5315 d->c_col = 8;
5317 return 1;
5318 case KEY_RIGHT:
5319 if (d->mode == MODE_HISTORY)
5320 return 0;
5322 if (keycount)
5323 d->c_col += keycount;
5324 else
5325 d->c_col++;
5327 if (d->c_col > 8)
5328 d->c_col = 1;
5330 return 1;
5331 case KEY_RESIZE:
5332 return 1;
5333 case 0:
5334 default:
5335 for (i = 0; global_keys[i]; i++)
5337 if (input_c == global_keys[i]->c && global_keys[i]->f)
5339 (*global_keys[i]->f) ();
5340 return 1;
5343 break;
5346 return 0;
5349 #ifdef WITH_LIBPERL
5350 static void
5351 perl_error (const char *fmt, ...)
5353 va_list ap;
5354 char *buf;
5356 va_start (ap, fmt);
5357 vasprintf (&buf, fmt, ap);
5358 va_end (ap);
5360 message (ERROR_STR, ANY_KEY_STR, "%s", buf);
5361 free (buf);
5364 static void
5365 do_perl_finalize (WIN * win)
5367 struct input_data_s *in = win->data;
5368 GAME g = in->data;
5369 struct userdata_s *d = g->data;
5370 char *filename;
5371 char *result = NULL;
5372 char *arg = NULL;
5373 int n;
5375 asprintf (&filename, "%s/perl.pl", config.datadir);
5377 if (!in->str)
5378 goto done;
5380 if (perl_init_file (filename, perl_error))
5381 goto done;
5383 arg = pgn_game_to_fen (g, d->b);
5385 if (perl_call_sub (trim (in->str), arg, &result))
5386 goto done;
5388 d->perlfen = pgn_game_to_fen (g, d->b);
5389 d->perlflags = g->flags;
5391 if (pgn_board_init_fen (g, d->b, result) != E_PGN_OK)
5393 message (ERROR_STR, ANY_KEY_STR, "%s", _("FEN parse error."));
5394 pgn_board_init_fen (g, d->b, d->perlfen);
5395 g->flags = d->perlflags;
5396 free (d->perlfen);
5397 d->perlfen = NULL;
5398 goto done;
5401 SET_FLAG (d->flags, CF_PERL);
5402 n = pgn_tag_find (g->tag, "FEN");
5404 if (n != E_PGN_ERR)
5405 d->oldfen = strdup (g->tag[n]->value);
5407 pgn_tag_add (&g->tag, (char *) "FEN", result);
5408 update_status_notify (g, "%s", ANY_KEY_STR);
5409 update_all (g);
5411 done:
5412 free (result);
5413 free (arg);
5414 free (in->str);
5415 free (in);
5416 free (filename);
5419 void
5420 do_global_perl ()
5422 struct input_data_s *in;
5424 in = Calloc (1, sizeof (struct input_data_s));
5425 in->data = gp;
5426 in->efunc = do_perl_finalize;
5427 construct_input (_("PERL Subroutine Filter"), NULL, 1, 0, NULL, NULL, NULL,
5428 0, in, INPUT_HIST_PERL, NULL, -1);
5430 #endif
5433 * A macro may contain a key that belongs to another macro so macro_match will
5434 * need to be updated to the new index of the matching macro.
5436 static void
5437 find_macro (struct userdata_s *d)
5439 int i;
5442 * Macros can't contain macros when in a window.
5444 if (wins)
5445 return;
5447 again:
5448 for (i = 0; macros[i]; i++)
5450 if ((macros[i]->mode == -1 || macros[i]->mode == d->mode) &&
5451 input_c == macros[i]->c)
5453 input_c = macros[i]->keys[macros[i]->n++];
5455 if (!macro_depth_n && macro_match > -1)
5457 macro_depth = Realloc (macro_depth, (macro_depth_n + 1) * sizeof (int));
5458 macro_depth[macro_depth_n++] = macro_match;
5461 macro_depth = Realloc (macro_depth, (macro_depth_n + 1) * sizeof (int));
5462 macro_depth[macro_depth_n++] = i;
5463 macro_match = i;
5464 goto again;
5470 * Resets the position in each macro to the first key.
5472 static void
5473 reset_macros ()
5475 int i;
5476 struct userdata_s *d = gp->data;
5478 again:
5479 if (macro_depth_n > 0)
5481 macro_depth_n--;
5482 macro_match = macro_depth[macro_depth_n];
5484 if (macros[macro_match]->n >= macros[macro_match]->total)
5485 goto again;
5487 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
5488 find_macro (d);
5489 return;
5492 for (i = 0; macros[i]; i++)
5493 macros[i]->n = 0;
5495 free (macro_depth);
5496 macro_depth = NULL;
5497 macro_depth_n = 0;
5498 macro_match = -1;
5501 void
5502 game_loop ()
5504 struct userdata_s *d;
5506 macro_match = -1;
5507 gindex = gtotal - 1;
5508 gp = game[gindex];
5509 d = gp->data;
5511 if (pgn_history_total (gp->hp))
5512 d->mode = MODE_HISTORY;
5513 else
5515 d->mode = MODE_PLAY;
5516 d->play_mode = PLAY_HE;
5519 d->rotate = FALSE;
5520 d->go_move = 0;
5521 d->pm_undo = FALSE;
5522 d->pm_frfr[0] = '\0';
5524 if (d->mode == MODE_HISTORY)
5525 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
5527 update_status_notify (gp, _("Type %ls for help"),
5528 key_lookup (global_keys, do_global_help));
5529 movestep = 2;
5530 flushinp ();
5531 update_all (gp);
5532 wtimeout (boardw, WINDOW_TIMEOUT);
5534 while (!quit)
5536 int n = 0, i;
5537 char fdbuf[8192] = { 0 };
5538 int len;
5539 struct timeval tv = { 0, 0 };
5540 fd_set rfds, wfds;
5541 WIN *win = NULL;
5542 WINDOW *wp = NULL;
5544 FD_ZERO (&rfds);
5545 FD_ZERO (&wfds);
5547 for (i = 0; i < gtotal; i++)
5549 d = game[i]->data;
5551 if (d->engine && d->engine->pid != -1)
5553 if (d->engine->fd[ENGINE_IN_FD] > 2)
5555 if (d->engine->fd[ENGINE_IN_FD] > n)
5556 n = d->engine->fd[ENGINE_IN_FD];
5558 FD_SET (d->engine->fd[ENGINE_IN_FD], &rfds);
5561 if (d->engine->fd[ENGINE_OUT_FD] > 2)
5563 if (d->engine->fd[ENGINE_OUT_FD] > n)
5564 n = d->engine->fd[ENGINE_OUT_FD];
5566 FD_SET (d->engine->fd[ENGINE_OUT_FD], &wfds);
5571 if (n)
5573 if ((n = select (n + 1, &rfds, &wfds, NULL, &tv)) > 0)
5575 for (i = 0; i < gtotal; i++)
5577 d = game[i]->data;
5579 if (d->engine && d->engine->pid != -1)
5581 if (FD_ISSET (d->engine->fd[ENGINE_IN_FD], &rfds))
5583 len = read (d->engine->fd[ENGINE_IN_FD], fdbuf,
5584 sizeof (fdbuf));
5586 if (len > 0)
5588 if (d->engine->iobuf)
5589 d->engine->iobuf =
5590 Realloc (d->engine->iobuf,
5591 d->engine->len + len + 1);
5592 else
5593 d->engine->iobuf = Calloc (1, len + 1);
5595 memcpy (&(d->engine->iobuf[d->engine->len]),
5596 &fdbuf, len);
5597 d->engine->len += len;
5598 d->engine->iobuf[d->engine->len] = 0;
5601 * The fdbuf is full or no newline
5602 * was found. So we'll append the next
5603 * read() to this games buffer.
5605 if (d->engine->iobuf[d->engine->len - 1] !=
5606 '\n')
5607 continue;
5609 parse_engine_output (game[i], d->engine->iobuf);
5610 free (d->engine->iobuf);
5611 d->engine->iobuf = NULL;
5612 d->engine->len = 0;
5614 else if (len == -1)
5616 if (errno != EAGAIN)
5618 cmessage (ERROR_STR, ANY_KEY_STR,
5619 "Engine read(): %s",
5620 strerror (errno));
5621 waitpid (d->engine->pid, &n, 0);
5622 free (d->engine);
5623 d->engine = NULL;
5624 break;
5629 if (FD_ISSET (d->engine->fd[ENGINE_OUT_FD], &wfds))
5631 if (d->engine->queue)
5632 send_engine_command (game[i]);
5637 else
5639 if (n == -1)
5640 cmessage (ERROR_STR, ANY_KEY_STR, "select(): %s",
5641 strerror (errno));
5642 /* timeout */
5646 gp = game[gindex];
5647 d = gp->data;
5650 * This is needed to detect terminal resizing.
5652 doupdate ();
5653 if (LINES != LINES_OLD || COLS != COLS_OLD)
5655 COLS_OLD = COLS;
5656 LINES_OLD = LINES;
5657 do_window_resize ();
5661 * Finds the top level window in the window stack so we know what
5662 * window the wget_wch()'ed key belongs to.
5664 if (wins)
5666 for (i = 0; wins[i]; i++);
5667 win = wins[i - 1];
5668 wp = win->w;
5669 wtimeout (wp, WINDOW_TIMEOUT);
5671 else
5672 wp = boardw;
5674 if (!i && pushkey)
5675 input_c = pushkey;
5676 else
5678 if (!pushkey)
5680 if (macros && macro_match >= 0)
5682 if (macros[macro_match]->n >= macros[macro_match]->total)
5684 reset_macros ();
5685 goto refresh;
5687 else
5689 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
5690 find_macro (d);
5693 else
5695 if (wget_wch (wp, &input_c) == ERR || input_c == KEY_RESIZE)
5697 if (input_c == KEY_RESIZE)
5699 if (win)
5700 win->c = input_c;
5702 window_resize_all ();
5703 update_all (gp);
5705 continue;
5709 else
5710 input_c = pushkey;
5712 if (win)
5714 win->c = input_c;
5717 * Run the function associated with the window. When the
5718 * function returns 0 win->efunc is ran (if not NULL) with
5719 * win as the one and only parameter. Then the window is
5720 * destroyed.
5722 * The exit function may create another window which will
5723 * mess up the window stack when window_destroy() is called.
5724 * So don't destory the window until the top window is
5725 * destroyable. See window_destroy().
5727 if ((*win->func) (win) == 0)
5729 if (win->efunc)
5730 (*win->efunc) (win);
5732 win->keep = 1;
5733 window_destroy (win);
5734 update_all (gp);
5737 continue;
5741 if (!keycount && status.notify)
5742 update_status_notify (gp, NULL);
5744 #ifdef WITH_LIBPERL
5745 if (TEST_FLAG (d->flags, CF_PERL))
5747 CLEAR_FLAG (d->flags, CF_PERL);
5748 pgn_board_init_fen (gp, d->b, d->perlfen);
5749 gp->flags = d->perlflags;
5750 free (d->perlfen);
5751 pgn_tag_add (&gp->tag, (char *) "FEN", d->oldfen);
5752 free (d->oldfen);
5753 d->perlfen = d->oldfen = NULL;
5754 update_all (gp);
5755 continue;
5757 #endif
5759 if (macros && macro_match < 0)
5760 find_macro (d);
5762 if ((n = globalkeys ()) == 1)
5764 if (macro_match == -1)
5765 keycount = 0;
5767 goto refresh;
5769 else if (n == -1)
5770 goto refresh;
5772 switch (d->mode)
5774 case MODE_EDIT:
5775 for (i = 0; edit_keys[i]; i++)
5777 if (input_c == edit_keys[i]->c)
5779 (*edit_keys[i]->f) ();
5780 break;
5783 break;
5784 case MODE_PLAY:
5785 for (i = 0; play_keys[i]; i++)
5787 if (input_c == play_keys[i]->c)
5789 (*play_keys[i]->f) ();
5790 goto done;
5794 do_play_config_command ();
5795 break;
5796 case MODE_HISTORY:
5797 for (i = 0; history_keys[i]; i++)
5799 if (input_c == history_keys[i]->c)
5801 (*history_keys[i]->f) ();
5802 break;
5805 break;
5806 default:
5807 break;
5810 done:
5811 if (keycount)
5812 update_status_notify (gp, NULL);
5814 keycount = 0;
5816 refresh:
5817 update_all (gp);
5821 void
5822 usage (const char *pn, int ret)
5824 fprintf ((ret) ? stderr : stdout, "%s%s",
5825 #ifdef DEBUG
5826 _("Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
5827 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"),
5828 #else
5829 _("Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"),
5830 #endif
5831 _(" -p Load PGN file.\n"
5832 " -V Validate a game file.\n"
5833 " -S Validate and output a PGN formatted game.\n"
5834 " -R Like -S but write a reduced PGN formatted game.\n"
5835 " -t Also write custom PGN tags from config file.\n"
5836 " -E Stop processing on file parsing error (overrides config).\n"
5837 " -C Enable strict castling (overrides config).\n"
5838 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, overrides config).\n"
5839 " -v Version information.\n" " -h This help text.\n"));
5841 exit (ret);
5844 void
5845 cleanup_all ()
5847 int i;
5849 stop_clock ();
5850 free_userdata ();
5851 pgn_free_all ();
5852 free (config.engine_cmd);
5853 free (config.pattern);
5854 free (config.ccfile);
5855 free (config.nagfile);
5856 free (config.configfile);
5858 if (config.keys)
5860 for (i = 0; config.keys[i]; i++)
5862 free (config.keys[i]->str);
5863 free (config.keys[i]);
5866 free (config.keys);
5869 if (config.einit)
5871 for (i = 0; config.einit[i]; i++)
5872 free (config.einit[i]);
5874 free (config.einit);
5877 if (config.tag)
5878 pgn_tag_free (config.tag);
5880 free (config.datadir);
5882 if (curses_initialized)
5884 del_panel (boardp);
5885 del_panel (historyp);
5886 del_panel (statusp);
5887 del_panel (tagp);
5888 delwin (boardw);
5889 delwin (historyw);
5890 delwin (statusw);
5891 delwin (tagw);
5893 if (enginew)
5895 del_panel (enginep);
5896 delwin (enginew);
5899 endwin ();
5902 #ifdef WITH_LIBPERL
5903 perl_cleanup ();
5904 #endif
5907 static void
5908 signal_save_pgn (int sig)
5910 char *buf;
5911 time_t now;
5912 char *p = config.savedirectory ? config.savedirectory : config.datadir;
5914 time (&now);
5915 asprintf (&buf, "%s/signal-%i-%li.pgn", p, sig, now);
5917 if (do_game_write (buf, "w", 0, gtotal))
5919 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", p, strerror (errno));
5920 update_status_notify (gp, "%s", _("Save game failed."));
5923 free (buf);
5924 quit = 1;
5927 void
5928 catch_signal (int which, siginfo_t *info, void *ctx)
5930 (void)info;
5931 (void)ctx;
5933 switch (which)
5935 case SIGALRM:
5936 update_clocks ();
5937 break;
5938 case SIGPIPE:
5939 if (which == SIGPIPE && quit)
5940 break;
5942 if (which == SIGPIPE)
5943 cmessage (NULL, ANY_KEY_STR, "%s", _("Broken pipe. Quitting."));
5945 cleanup_all ();
5946 exit (EXIT_FAILURE);
5947 break;
5948 case SIGSTOP:
5949 savetty ();
5950 break;
5951 case SIGCONT:
5952 resetty ();
5953 do_window_resize ();
5954 keypad (boardw, TRUE);
5955 break;
5956 case SIGINT:
5957 quit = 1;
5958 break;
5959 case SIGTERM:
5960 signal_save_pgn (which);
5961 break;
5962 default:
5963 break;
5967 void
5968 loading_progress (long total, long offset)
5970 int n = (100 * (offset / 100) / (total / 100));
5972 if (curses_initialized)
5973 update_loading_window (n);
5974 else
5976 fprintf (stderr, _("Loading... %i%% (%i games)%c"), n, gtotal, '\r');
5977 fflush (stderr);
5981 static void
5982 set_defaults ()
5984 set_config_defaults ();
5985 set_default_keys ();
5986 filetype = FILE_NONE;
5987 pgn_config_set (PGN_PROGRESS, 1024);
5988 pgn_config_set (PGN_PROGRESS_FUNC, loading_progress);
5992 main (int argc, char *argv[])
5994 int opt;
5995 struct stat st;
5996 char buf[FILENAME_MAX];
5997 char datadir[FILENAME_MAX];
5998 int ret = EXIT_SUCCESS;
5999 int validate_only = 0, validate_and_write = 0;
6000 int write_custom_tags = 0;
6001 int i = 0;
6002 PGN_FILE *pgn;
6003 int utf8_pieces = -1;
6004 struct sigaction sigact;
6006 setlocale (LC_ALL, "");
6007 bindtextdomain ("cboard", LOCALE_DIR);
6008 textdomain ("cboard");
6010 /* Solaris 5.9 */
6011 #ifndef HAVE_PROGNAME
6012 __progname = argv[0];
6013 #endif
6015 if ((config.pwd = getpwuid (getuid ())) == NULL)
6016 err (EXIT_FAILURE, "getpwuid()");
6018 snprintf (datadir, sizeof (datadir), "%s/.cboard", config.pwd->pw_dir);
6019 config.datadir = strdup (datadir);
6020 snprintf (buf, sizeof (buf), "%s/cc.data", datadir);
6021 config.ccfile = strdup (buf);
6022 snprintf (buf, sizeof (buf), "%s/nag.data", datadir);
6023 config.nagfile = strdup (buf);
6024 snprintf (buf, sizeof (buf), "%s/config", datadir);
6025 config.configfile = strdup (buf);
6027 if (stat (datadir, &st) == -1)
6029 if (errno == ENOENT)
6031 if (mkdir (datadir, 0755) == -1)
6032 err (EXIT_FAILURE, "%s", datadir);
6034 else
6035 err (EXIT_FAILURE, "%s", datadir);
6037 stat (datadir, &st);
6040 if (!S_ISDIR (st.st_mode))
6041 errx (EXIT_FAILURE, "%s: %s", datadir, _("Not a directory."));
6043 set_defaults ();
6045 #ifdef DEBUG
6046 while ((opt = getopt (argc, argv, "DCEVtSRhp:vu::")) != -1)
6048 #else
6049 while ((opt = getopt (argc, argv, "ECVtSRhp:vu::")) != -1)
6051 #endif
6052 switch (opt)
6054 #ifdef DEBUG
6055 case 'D':
6056 unlink ("libchess.debug");
6057 pgn_config_set (PGN_DEBUG, 1);
6058 break;
6059 #endif
6060 case 'C':
6061 pgn_config_set (PGN_STRICT_CASTLING, 1);
6062 break;
6063 case 't':
6064 write_custom_tags = 1;
6065 break;
6066 case 'E':
6067 i = 1;
6068 break;
6069 case 'R':
6070 pgn_config_set (PGN_REDUCED, 1);
6071 case 'S':
6072 validate_and_write = 1;
6073 case 'V':
6074 validate_only = 1;
6075 break;
6076 case 'v':
6077 printf ("%s (%s)\n%s\n%s\n", PACKAGE_STRING, curses_version (),
6078 COPYRIGHT, CBOARD_URL);
6079 exit (EXIT_SUCCESS);
6080 case 'p':
6081 filetype = FILE_PGN;
6082 strncpy (loadfile, optarg, sizeof (loadfile));
6083 loadfile[sizeof (loadfile) - 1] = 0;
6084 break;
6085 case 'u':
6086 utf8_pieces = optarg ? atoi (optarg) : 1;
6087 break;
6088 case 'h':
6089 default:
6090 usage (argv[0], EXIT_SUCCESS);
6094 if ((validate_only || validate_and_write) && !*loadfile)
6095 usage (argv[0], EXIT_FAILURE);
6097 if (access (config.configfile, R_OK) == 0)
6098 parse_rcfile (config.configfile);
6100 if (i)
6101 pgn_config_set (PGN_STOP_ON_ERROR, 1);
6103 memset (&sigact, 0, sizeof (sigact));
6104 sigact.sa_flags = SA_SIGINFO;
6105 sigact.sa_sigaction = catch_signal;
6106 sigaction (SIGPIPE, &sigact, NULL);
6107 sigaction (SIGCONT, &sigact, NULL);
6108 sigaction (SIGSTOP, &sigact, NULL);
6109 sigaction (SIGINT, &sigact, NULL);
6110 sigaction (SIGALRM, &sigact, NULL);
6111 sigaction (SIGTERM, &sigact, NULL);
6112 signal (SIGCHLD, SIG_IGN);
6114 srandom (getpid ());
6116 switch (filetype)
6118 case FILE_PGN:
6119 if (pgn_open (loadfile, "r", &pgn) != E_PGN_OK)
6120 err (EXIT_FAILURE, "%s", loadfile);
6122 ret = pgn_parse (pgn);
6123 pgn_close (pgn);
6124 break;
6125 case FILE_FEN:
6126 //ret = parse_fen_file(loadfile);
6127 break;
6128 case FILE_EPD: // Not implemented.
6129 case FILE_NONE:
6130 default:
6131 // No file specified. Empty game.
6132 ret = pgn_parse (NULL);
6133 gp = game[gindex];
6134 add_custom_tags (&gp->tag);
6135 break;
6138 if (validate_only || validate_and_write)
6140 if (validate_and_write)
6142 if (pgn_open ("-", "r", &pgn) != E_PGN_OK)
6143 err (EXIT_FAILURE, "pgn_open()");
6145 for (i = 0; i < gtotal; i++)
6147 if (write_custom_tags)
6148 add_custom_tags (&game[i]->tag);
6150 pgn_write (pgn, game[i]);
6153 pgn_close (pgn);
6155 fm_loaded_file = TRUE;
6158 cleanup_all ();
6159 exit (ret);
6161 else if (ret == E_PGN_ERR)
6162 exit (ret);
6164 if (utf8_pieces != -1)
6165 config.utf8_pieces = utf8_pieces;
6167 init_wchar_pieces ();
6168 yes_wchar = str_to_wchar (_("y"));
6169 all_wchar = str_to_wchar (_("a"));
6170 overwrite_wchar = str_to_wchar (_("o"));
6171 resume_wchar = str_to_wchar (_("r"));
6172 current_wchar = str_to_wchar (_("c"));
6173 append_wchar = str_to_wchar (_("a"));
6174 translatable_tag_names[0] = _("Event");
6175 translatable_tag_names[1] = _("Site");
6176 translatable_tag_names[2] = _("Date");
6177 translatable_tag_names[3] = _("Round");
6178 translatable_tag_names[4] = _("White");
6179 translatable_tag_names[5] = _("Black");
6180 translatable_tag_names[6] = _("Result");
6181 init_userdata ();
6184 * This fixes window resizing in an xterm.
6186 if (getenv ("DISPLAY") != NULL)
6188 putenv ((char *) "LINES=");
6189 putenv ((char *) "COLUMNS=");
6192 if (initscr () == NULL)
6193 errx (EXIT_FAILURE, "%s", _("Could not initialize curses."));
6194 else
6195 curses_initialized = 1;
6197 if (LINES < 23 || COLS < 74)
6199 endwin ();
6200 errx (EXIT_FAILURE, _("Need at least an 74x23 terminal."));
6203 COLS_OLD = COLS;
6204 LINES_OLD = LINES;
6206 if (has_colors () == TRUE && start_color () == OK)
6207 init_color_pairs ();
6209 boardw = newwin (BOARD_HEIGHT, BOARD_WIDTH, 0, COLS - BOARD_WIDTH);
6210 boardp = new_panel (boardw);
6211 historyw = newwin (HISTORY_HEIGHT, HISTORY_WIDTH, LINES - HISTORY_HEIGHT,
6212 COLS - HISTORY_WIDTH);
6213 historyp = new_panel (historyw);
6214 statusw = newwin (STATUS_HEIGHT, STATUS_WIDTH, 0, 0);
6215 statusp = new_panel (statusw);
6216 tagw = newwin (TAG_HEIGHT, TAG_WIDTH, STATUS_HEIGHT + 1, 0);
6217 tagp = new_panel (tagw);
6218 keypad (boardw, TRUE);
6219 // leaveok(boardw, TRUE);
6220 leaveok (tagw, TRUE);
6221 leaveok (statusw, TRUE);
6222 leaveok (historyw, TRUE);
6223 curs_set (0);
6224 cbreak ();
6225 noecho ();
6226 draw_window_decor ();
6227 game_loop ();
6228 cleanup_all ();
6229 free (w_pawn_wchar);
6230 free (w_rook_wchar);
6231 free (w_bishop_wchar);
6232 free (w_knight_wchar);
6233 free (w_queen_wchar);
6234 free (w_king_wchar);
6235 free (b_pawn_wchar);
6236 free (b_rook_wchar);
6237 free (b_bishop_wchar);
6238 free (b_knight_wchar);
6239 free (b_queen_wchar);
6240 free (b_king_wchar);
6241 free (empty_wchar);
6242 free (enpassant_wchar);
6243 free (yes_wchar);
6244 free (all_wchar);
6245 free (overwrite_wchar);
6246 free (resume_wchar);
6247 free (current_wchar);
6248 free (append_wchar);
6249 free (status.notify);
6250 exit (EXIT_SUCCESS);