Update copyright year.
[cboard.git] / src / cboard.c
bloba29b3ec6eaccdfad0b7828666f874d5d04236ebe
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2024 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-2024 " 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 if (items)
396 items[i] = NULL;
398 m->nofree = 1;
399 m->items = items;
400 return items;
403 void
404 nag_print (WIN * win)
406 struct menu_input_s *m = win->data;
408 mvwprintw (win->w, m->print_line, 1, "%-*s", win->cols - 2, m->item->name);
411 void
412 edit_nag (void *arg)
414 struct menu_key_s **keys = NULL;
416 if (!nags)
418 if (init_nag ())
419 return;
422 add_menu_key (&keys, ' ', edit_nag_toggle_item);
423 add_menu_key (&keys, CTRL_KEY ('x'), edit_nag_save);
424 add_menu_key (&keys, keycode_lookup (global_keys, do_global_help),
425 edit_nag_help);
426 construct_menu (0, 0, -1, -1, _("Numeric Annotation Glyphs"), 1,
427 get_nag_items, keys, arg, nag_print, NULL, NULL);
428 return;
431 static void *
432 view_nag (void *arg)
434 HISTORY *h = (HISTORY *) arg;
435 char buf[80];
436 char line[LINE_MAX] = { 0 };
437 int i = 0;
439 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Viewing NAG for"), h->move);
441 if (!nags)
443 if (init_nag ())
444 return NULL;
447 for (i = 0; i < MAX_PGN_NAG; i++)
449 char buf2[16];
451 if (!h->nag[i])
452 break;
454 if (h->nag[i] >= nag_total)
455 strncat (line, itoa (h->nag[i], buf2), sizeof (line) - 1);
456 else if (nags)
457 strncat (line, nags[h->nag[i]], sizeof (line) - 1);
459 strncat (line, "\n", sizeof (line) - 1);
462 line[strlen (line) - 1] = 0;
463 message (buf, ANY_KEY_STR, "%s", line);
464 return NULL;
467 void
468 view_annotation (HISTORY * h)
470 char buf[MAX_SAN_MOVE_LEN + strlen (_("Viewing Annotation for")) + 4];
471 int nag = 0, comment = 0;
473 if (!h)
474 return;
476 if (h->comment && h->comment[0])
477 comment++;
479 if (h->nag[0])
480 nag++;
482 if (!nag && !comment)
483 return;
485 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Viewing Annotation for"),
486 h->move);
488 if (comment)
489 construct_message (buf,
490 (nag) ? _("Any other key to continue") : ANY_KEY_STR,
491 0, 1, (nag) ? _("Press 'n' to view NAG") : NULL,
492 (nag) ? view_nag : NULL, (nag) ? h : NULL, NULL,
493 (nag) ? 'n' : 0, 0, NULL, "%s", h->comment);
494 else
495 construct_message (buf, _("Any other key to continue"), 0, 1,
496 _("Press 'n' to view NAG"), view_nag, h, NULL, 'n', 0,
497 NULL, "%s", _("No comment text for this move"));
501 do_game_write (char *filename, const char *mode, int start, int end)
503 int i;
504 struct userdata_s *d;
505 PGN_FILE *pgn;
507 i = pgn_open (filename, mode, &pgn);
509 if (i == E_PGN_ERR)
511 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", filename, strerror (errno));
512 return 1;
514 else if (i == E_PGN_INVALID)
516 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", filename,
517 _("Not a regular file"));
518 return 1;
521 for (i = (start == -1) ? 0 : start; i < end; i++)
523 d = game[i]->data;
524 pgn_write (pgn, game[i]);
525 CLEAR_FLAG (d->flags, CF_MODIFIED);
528 if (pgn_close (pgn) != E_PGN_OK)
529 message (ERROR_STR, ANY_KEY_STR, "%s", strerror (errno));
531 if (start == -1)
533 strncpy (loadfile, filename, sizeof (loadfile));
534 loadfile[sizeof (loadfile) - 1] = 0;
537 return 0;
540 struct save_game_s
542 char *filename;
543 char *mode;
544 int start;
545 int end;
548 void
549 do_save_game_overwrite_confirm (WIN * win)
551 const char *mode = "w";
552 struct save_game_s *s = win->data;
553 wchar_t str[] = { win->c, 0 };
555 if (!wcscmp (str, append_wchar))
556 mode = "a";
557 else if (!wcscmp (str, overwrite_wchar))
558 mode = "w";
559 else
560 goto done;
562 if (do_game_write (s->filename, mode, s->start, s->end))
563 update_status_notify (gp, "%s", _("Save game failed."));
564 else
565 update_status_notify (gp, "%s", _("Game saved."));
567 done:
568 free (s->filename);
569 free (s);
572 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
573 * game index number.
575 void
576 save_pgn (char *filename, int saveindex)
578 char buf[FILENAME_MAX];
579 struct stat st;
580 int end = (saveindex == -1) ? gtotal : saveindex + 1;
581 struct save_game_s *s;
583 if (filename[0] != '/' && config.savedirectory)
585 if (stat (config.savedirectory, &st) == -1)
587 if (errno == ENOENT)
589 if (mkdir (config.savedirectory, 0755) == -1)
591 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s",
592 config.savedirectory, strerror (errno));
593 return;
596 else
598 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s",
599 config.savedirectory, strerror (errno));
600 return;
604 if (stat (config.savedirectory, &st) == -1)
606 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
607 strerror (errno));
608 return;
611 if (!S_ISDIR (st.st_mode))
613 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
614 _("Not a directory."));
615 return;
618 snprintf (buf, sizeof (buf), "%s/%s", config.savedirectory, filename);
619 filename = buf;
622 if (access (filename, W_OK) == 0)
624 s = Malloc (sizeof (struct save_game_s));
625 s->filename = strdup (filename);
626 s->start = saveindex;
627 s->end = end;
628 construct_message (NULL, _("What would you like to do?"), 0, 1, NULL,
629 NULL, s, do_save_game_overwrite_confirm, 0, 0, NULL,
630 "%s \"%s\"\nPress \"%ls\" to append to this file, \"%ls\" to overwrite or any other key to cancel.",
631 _("File exists:"), filename, append_wchar,
632 overwrite_wchar);
633 return;
636 if (do_game_write (filename, "a", saveindex, end))
637 update_status_notify (gp, "%s", _("Save game failed."));
638 else
639 update_status_notify (gp, "%s", _("Game saved."));
642 static int
643 castling_state (GAME g, BOARD b, int row, int col, int piece, int mod)
645 if (pgn_piece_to_int (piece) == ROOK && col == 7
646 && row == 7 &&
647 (TEST_FLAG (g->flags, GF_WK_CASTLE) || mod) &&
648 pgn_piece_to_int (b[7][4].icon) == KING && isupper (piece))
650 if (mod)
651 TOGGLE_FLAG (g->flags, GF_WK_CASTLE);
652 return 1;
654 else if (pgn_piece_to_int (piece) == ROOK && col == 0
655 && row == 7 &&
656 (TEST_FLAG (g->flags, GF_WQ_CASTLE) || mod) &&
657 pgn_piece_to_int (b[7][4].icon) == KING && isupper (piece))
659 if (mod)
660 TOGGLE_FLAG (g->flags, GF_WQ_CASTLE);
661 return 1;
663 else if (pgn_piece_to_int (piece) == ROOK && col == 7
664 && row == 0 &&
665 (TEST_FLAG (g->flags, GF_BK_CASTLE) || mod) &&
666 pgn_piece_to_int (b[0][4].icon) == KING && islower (piece))
668 if (mod)
669 TOGGLE_FLAG (g->flags, GF_BK_CASTLE);
670 return 1;
672 else if (pgn_piece_to_int (piece) == ROOK && col == 0
673 && row == 0 &&
674 (TEST_FLAG (g->flags, GF_BQ_CASTLE) || mod) &&
675 pgn_piece_to_int (b[0][4].icon) == KING && islower (piece))
677 if (mod)
678 TOGGLE_FLAG (g->flags, GF_BQ_CASTLE);
679 return 1;
681 else if (pgn_piece_to_int (piece) == KING && col == 4
682 && row == 7 &&
683 (mod || (pgn_piece_to_int (b[7][7].icon) == ROOK &&
684 TEST_FLAG (g->flags, GF_WK_CASTLE))
686 (pgn_piece_to_int (b[7][0].icon) == ROOK &&
687 TEST_FLAG (g->flags, GF_WQ_CASTLE))) && isupper (piece))
689 if (mod)
691 if (TEST_FLAG (g->flags, GF_WK_CASTLE) ||
692 TEST_FLAG (g->flags, GF_WQ_CASTLE))
693 CLEAR_FLAG (g->flags, GF_WK_CASTLE | GF_WQ_CASTLE);
694 else
695 SET_FLAG (g->flags, GF_WK_CASTLE | GF_WQ_CASTLE);
697 return 1;
699 else if (pgn_piece_to_int (piece) == KING && col == 4
700 && row == 0 &&
701 (mod || (pgn_piece_to_int (b[0][7].icon) == ROOK &&
702 TEST_FLAG (g->flags, GF_BK_CASTLE))
704 (pgn_piece_to_int (b[0][0].icon) == ROOK &&
705 TEST_FLAG (g->flags, GF_BQ_CASTLE))) && islower (piece))
707 if (mod)
709 if (TEST_FLAG (g->flags, GF_BK_CASTLE) ||
710 TEST_FLAG (g->flags, GF_BQ_CASTLE))
711 CLEAR_FLAG (g->flags, GF_BK_CASTLE | GF_BQ_CASTLE);
712 else
713 SET_FLAG (g->flags, GF_BK_CASTLE | GF_BQ_CASTLE);
715 return 1;
718 return 0;
721 #define IS_ENPASSANT(c) (c == 'x') ? CP_BOARD_ENPASSANT : isupper(c) ? CP_BOARD_WHITE : CP_BOARD_BLACK
722 #define ATTRS(cp) (cp & (A_BOLD|A_STANDOUT|A_BLINK|A_DIM|A_UNDERLINE|A_INVIS|A_REVERSE))
724 static void
725 init_wchar_pieces ()
727 w_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♙" : "P");
728 w_rook_wchar = str_to_wchar (config.utf8_pieces ? "♖" : "R");
729 w_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♗" : "B");
730 w_knight_wchar = str_to_wchar (config.utf8_pieces ? "♘" : "N");
731 w_queen_wchar = str_to_wchar (config.utf8_pieces ? "♕" : "Q");
732 w_king_wchar = str_to_wchar (config.utf8_pieces ? "♔" : "K");
733 b_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♟" : "p");
734 b_rook_wchar = str_to_wchar (config.utf8_pieces ? "♜" : "r");
735 b_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♝" : "b");
736 b_knight_wchar = str_to_wchar (config.utf8_pieces ? "♞" : "n");
737 b_queen_wchar = str_to_wchar (config.utf8_pieces ? "♛" : "q");
738 b_king_wchar = str_to_wchar (config.utf8_pieces ? "♚" : "k");
739 empty_wchar = str_to_wchar (" ");
740 enpassant_wchar = str_to_wchar ("x");
743 static wchar_t *
744 piece_to_wchar (unsigned char p)
746 switch (p)
748 case 'P':
749 return w_pawn_wchar;
750 case 'p':
751 return b_pawn_wchar;
752 case 'R':
753 return w_rook_wchar;
754 case 'r':
755 return b_rook_wchar;
756 case 'B':
757 return w_bishop_wchar;
758 case 'b':
759 return b_bishop_wchar;
760 case 'N':
761 return w_knight_wchar;
762 case 'n':
763 return b_knight_wchar;
764 case 'Q':
765 return w_queen_wchar;
766 case 'q':
767 return b_queen_wchar;
768 case 'K':
769 return w_king_wchar;
770 case 'k':
771 return b_king_wchar;
772 case 'x':
773 return enpassant_wchar;
776 return empty_wchar;
779 static int
780 piece_can_attack (GAME g, int rank, int file)
782 struct userdata_s *d = g->data;
783 char *m, *frfr = NULL;
784 pgn_error_t e;
785 int row, col, p, v, pi, cpi;
787 if (d->rotate)
789 rotate_position (&d->c_row, &d->c_col);
790 rotate_position (&d->sp.srow, &d->sp.scol);
793 v = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].valid;
794 pi = pgn_piece_to_int (d->b[RANKTOBOARD (rank)][FILETOBOARD (file)].icon);
795 cpi = d->sp.icon
797 pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)]
798 [FILETOBOARD (d->sp.scol)].icon) :
799 pgn_piece_to_int (d->b[RANKTOBOARD (d->c_row)]
800 [FILETOBOARD (d->c_col)].icon);
802 if (pi == OPEN_SQUARE || cpi == OPEN_SQUARE || !VALIDFILE (file)
803 || !VALIDRANK (rank))
805 if (d->rotate)
807 rotate_position (&d->c_row, &d->c_col);
808 rotate_position (&d->sp.srow, &d->sp.scol);
811 return 0;
814 if (d->sp.icon)
816 col = v ? d->c_col : d->sp.scol;
817 row = v ? d->c_row : d->sp.srow;
819 else
821 col = d->c_col;
822 row = d->c_row;
825 m = Malloc (MAX_SAN_MOVE_LEN + 1);
826 m[0] = INTTOFILE (file);
827 m[1] = INTTORANK (rank);
828 m[2] = INTTOFILE (col);
829 m[3] = INTTORANK (row);
830 m[4] = 0;
832 if (d->sp.icon && v)
834 BOARD b;
836 memcpy (b, d->b, sizeof (BOARD));
837 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
838 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
839 pgn_int_to_piece (WHITE, OPEN_SQUARE);
840 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
841 pgn_switch_turn (g);
842 e = pgn_validate_move (g, b, &m, &frfr);
843 pgn_switch_turn (g);
844 free (m);
845 free (frfr);
847 if (e != E_PGN_OK && pgn_piece_to_int (d->sp.icon) == PAWN)
849 int n = (d->sp.srow == 7 && islower (d->sp.icon) && rank == 5) ? 6 :
850 (d->sp.srow == 2 && isupper (d->sp.icon) && rank == 4) ? 3 : 0;
852 if (n && (file == d->c_col - 1 || file == d->c_col + 1))
854 memcpy (b, d->b, sizeof (BOARD));
855 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
856 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
857 pgn_int_to_piece (WHITE, OPEN_SQUARE);
858 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
859 b[RANKTOBOARD (n)][FILETOBOARD (d->sp.scol)].enpassant = 1;
860 m = Malloc (MAX_SAN_MOVE_LEN + 1);
861 m[0] = INTTOFILE (file);
862 m[1] = INTTORANK (rank);
863 m[2] = INTTOFILE (col);
864 m[3] = INTTORANK (n);
865 m[4] = 0;
866 pgn_switch_turn (g);
867 SET_FLAG (g->flags, GF_ENPASSANT);
868 e = pgn_validate_move (g, b, &m, &frfr);
869 CLEAR_FLAG (g->flags, GF_ENPASSANT);
870 pgn_switch_turn (g);
871 free (m);
872 free (frfr);
876 goto pca_quit;
879 pgn_switch_turn (g);
880 e = pgn_validate_move (g, d->b, &m, &frfr);
881 pgn_switch_turn (g);
883 if (!strcmp (m, "O-O") || !strcmp (m, "O-O-O"))
884 e = E_PGN_INVALID;
886 if (e == E_PGN_OK)
888 int sf = FILETOINT (frfr[0]), sr = RANKTOINT (frfr[1]);
889 int df = FILETOINT (frfr[2]);
891 pi = d->b[RANKTOBOARD (sr)][FILETOBOARD (sf)].icon;
892 pi = pgn_piece_to_int (pi);
893 if (pi == PAWN && sf == df)
894 e = E_PGN_INVALID;
897 free (m);
898 free (frfr);
900 pca_quit:
901 if (d->rotate)
903 rotate_position (&d->c_row, &d->c_col);
904 rotate_position (&d->sp.srow, &d->sp.scol);
907 return e == E_PGN_OK ? 1 : 0;
910 void
911 print_piece (WINDOW * w, int l, int c, char p)
913 int i, y, ff = 0;
915 for (i = 0; i < 13; i += 2)
917 if (p == piece_chars[i] || p == piece_chars[i + 1])
919 for (y = 0; y < 3; y++)
920 mvwprintw (w, l + y, c, "%s", f_pieces[i + ff + y]);
921 return;
924 ff++;
927 for (y = 0; y < 3; y++)
928 mvwprintw (w, l + y, c, "%s", f_pieces[0]);
931 void
932 board_prev_move_play (GAME g)
934 struct userdata_s *d = g->data;
935 char l = strlen (d->pm_frfr);
937 if (l)
939 char q = (l > 4) ? 2 : 1;
941 d->pm_row = RANKTOINT (d->pm_frfr[l - q++]);
942 d->pm_col = FILETOINT (d->pm_frfr[l - q++]);
943 d->ospm_row = RANKTOINT (d->pm_frfr[l - q++]);
944 d->ospm_col = FILETOINT (d->pm_frfr[l - q]);
945 if (d->rotate)
947 rotate_position (&d->pm_row, &d->pm_col);
948 rotate_position (&d->ospm_row, &d->ospm_col);
951 else
953 d->pm_row = 0;
954 d->pm_col = 0;
955 d->ospm_row = 0;
956 d->ospm_col = 0;
960 void
961 board_prev_move_history (GAME g)
963 struct userdata_s *d = g->data;
965 if (g->hindex)
967 char *move = g->hp[g->hindex - 1]->move;
969 if (move)
971 if (d->mode == MODE_PLAY)
972 coordofmove (g, move, &d->pm_row, &d->pm_col);
973 else
975 d->pm_row = 0;
976 d->pm_col = 0;
979 if (*move == 'O')
981 d->ospm_row = g->turn == WHITE ? 8 : 1;
982 d->ospm_col = 5;
983 return;
986 BOARD ob;
987 unsigned char f, r;
989 pgn_board_init (ob);
991 if (g->hindex > 1)
993 HISTORY *h = pgn_history_by_n (g->hp, g->hindex - 2);
994 if (h)
996 pgn_board_init_fen (g, ob, h->fen);
997 pgn_switch_turn (g);
1001 for (f = 0; f < 8; f++)
1003 for (r = 0; r < 8; r++)
1005 if (ob[f][r].icon != '.' && d->b[f][r].icon == '.')
1007 d->ospm_row = INV_INT0 (f) + 1;
1008 d->ospm_col = r + 1;
1009 break;
1014 if (d->rotate)
1016 if (d->mode == MODE_PLAY)
1017 rotate_position (&d->pm_row, &d->pm_col);
1019 rotate_position (&d->ospm_row, &d->ospm_col);
1022 return;
1025 else
1027 d->ospm_row = 0;
1028 d->ospm_col = 0;
1029 d->pm_row = 0;
1030 d->pm_col = 0;
1034 static int
1035 is_the_square (int brow, int bcol, int row, int col, int prow, int pcol)
1037 if ((!BIG_BOARD && row == ROWTOMATRIX (prow) && col == COLTOMATRIX (pcol))
1038 || (BIG_BOARD && brow + 1 == INV_INT (prow) && bcol + 1 == pcol))
1039 return 1;
1041 return 0;
1044 static int
1045 is_prev_move (struct userdata_s *d, int brow, int bcol, int row, int col)
1047 if (is_the_square (brow, bcol, row, col, d->pm_row, d->pm_col))
1048 return 1;
1050 return 0;
1053 void
1054 update_board_window (GAME g)
1056 int row, col;
1057 int bcol = 0, brow = 0;
1058 int l = config.coordsyleft;
1059 int maxy = BOARD_HEIGHT, maxx = BOARD_WIDTH;
1060 int ncols = 0, offset = 1;
1061 int rowr = (MEGA_BOARD) ? 6 : (BIG_BOARD) ? 4 : 2;
1062 int colr = (MEGA_BOARD) ? 12 : (BIG_BOARD) ? 8 : 4;
1063 unsigned coords_y = 8, cxgc = 0;
1064 unsigned i, cpd = 0;
1065 struct userdata_s *d = g->data;
1067 if (config.bprevmove && d->mode != MODE_EDIT)
1069 if (!d->pm_undo && d->mode == MODE_PLAY)
1070 board_prev_move_play (g);
1071 else
1072 board_prev_move_history (g);
1074 else
1076 d->pm_row = 0;
1077 d->pm_col = 0;
1078 d->ospm_row = 0;
1079 d->ospm_col = 0;
1082 if (d->mode != MODE_PLAY && d->mode != MODE_EDIT)
1083 update_cursor (g, g->hindex);
1085 if (BIG_BOARD)
1087 if (d->rotate)
1089 brow = 7;
1090 coords_y = 1;
1093 else
1095 if (d->rotate)
1097 brow = 1;
1098 coords_y = 1;
1100 else
1101 brow = 8;
1104 for (row = 0; row < maxy; row++)
1106 if (BIG_BOARD)
1108 if (d->rotate)
1109 bcol = 7;
1110 else
1111 bcol = 0;
1113 else
1115 if (d->rotate)
1116 bcol = 8;
1117 else
1118 bcol = 1;
1121 for (col = 0; col < maxx; col++)
1123 int attrwhich = -1;
1124 chtype attrs = 0, old_attrs = 0;
1125 unsigned char p;
1126 int can_attack = 0;
1127 int valid = 0;
1129 if (row == 0 || row == maxy - 2)
1131 if (col == 0)
1132 mvwaddch (boardw, row, col + l,
1133 LINE_GRAPHIC ((row)
1134 ? ACS_LLCORNER | CP_BOARD_GRAPHICS
1135 : ACS_ULCORNER | CP_BOARD_GRAPHICS));
1136 else if (col == maxx - 2)
1137 mvwaddch (boardw, row, col + l,
1138 LINE_GRAPHIC ((row)
1139 ? ACS_LRCORNER | CP_BOARD_GRAPHICS
1140 : ACS_URCORNER | CP_BOARD_GRAPHICS));
1141 else if (!(col % colr))
1142 mvwaddch (boardw, row, col + l,
1143 LINE_GRAPHIC ((row)
1144 ? ACS_BTEE | CP_BOARD_GRAPHICS
1145 : ACS_TTEE | CP_BOARD_GRAPHICS));
1146 else
1148 if (col != maxx - 1)
1149 mvwaddch (boardw, row, col + l,
1150 LINE_GRAPHIC (ACS_HLINE | CP_BOARD_GRAPHICS));
1153 continue;
1156 if ((row % 2) && col == maxx - 1 && (coords_y > 0 && coords_y < 9))
1158 wattron (boardw, CP_BOARD_COORDS);
1159 mvwprintw (boardw,
1160 (BIG_BOARD) ? row * ((MEGA_BOARD) ? 3 : 2)
1161 : row, (l) ? 0 : col, "%d",
1162 (d->rotate) ? coords_y++ : coords_y--);
1163 wattroff (boardw, CP_BOARD_COORDS);
1164 continue;
1167 if ((col == 0 || col == maxx - 2) && row != maxy - 1)
1169 if (!(row % rowr))
1170 mvwaddch (boardw, row, col + l,
1171 LINE_GRAPHIC ((col) ?
1172 ACS_RTEE | CP_BOARD_GRAPHICS :
1173 ACS_LTEE | CP_BOARD_GRAPHICS));
1174 else
1175 mvwaddch (boardw, row, col + l,
1176 LINE_GRAPHIC (ACS_VLINE | CP_BOARD_GRAPHICS));
1178 continue;
1181 if ((row % rowr) && !(col % colr) && row != maxy - 1)
1183 mvwaddch (boardw, row, col + l,
1184 LINE_GRAPHIC (ACS_VLINE | CP_BOARD_GRAPHICS));
1185 continue;
1188 if (!(col % colr) && row != maxy - 1)
1190 mvwaddch (boardw, row, col + l,
1191 LINE_GRAPHIC (ACS_PLUS | CP_BOARD_GRAPHICS));
1192 continue;
1195 if ((row % rowr))
1197 if ((col % colr))
1199 if (BIG_BOARD)
1200 attrwhich = (cb[brow][bcol]) ? WHITE : BLACK;
1201 else
1203 if (ncols++ == 8)
1205 offset++;
1206 ncols = 1;
1209 if (((ncols % 2) && !(offset % 2))
1210 || (!(ncols % 2) && (offset % 2)))
1211 attrwhich = BLACK;
1212 else
1213 attrwhich = WHITE;
1216 if (BIG_BOARD && d->rotate)
1218 brow = INV_INT0 (brow);
1219 bcol = INV_INT0 (bcol);
1222 if (BIG_BOARD)
1223 p = d->b[brow][bcol].icon;
1224 else
1225 p = d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].icon;
1227 int pi = pgn_piece_to_int (p);
1229 if (config.details &&
1230 ((!BIG_BOARD
1231 && d->
1232 b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].enpassant)
1233 || (BIG_BOARD && d->b[brow][bcol].enpassant)))
1235 p = pi = 'x';
1236 attrs = mix_cp (CP_BOARD_ENPASSANT,
1237 (attrwhich ==
1238 WHITE) ? CP_BOARD_WHITE :
1239 CP_BOARD_BLACK,
1240 ATTRS (CP_BOARD_ENPASSANT), A_FG_B_BG);
1243 if (config.showattacks && config.details
1244 && piece_can_attack (g,
1245 BIG_BOARD ? INV_INT0 (brow) +
1246 1 : brow,
1247 BIG_BOARD ? bcol + 1 : bcol))
1249 attrs = CP_BOARD_ATTACK;
1250 old_attrs = attrs;
1251 can_attack = 1;
1254 if (config.validmoves &&
1255 ((!BIG_BOARD
1256 && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].valid)
1257 || (BIG_BOARD && d->b[brow][bcol].valid)))
1259 old_attrs = -1;
1260 valid = 1;
1262 if (attrwhich == WHITE)
1263 attrs = mix_cp (CP_BOARD_MOVES_WHITE,
1264 IS_ENPASSANT (p),
1265 ATTRS (CP_BOARD_MOVES_WHITE),
1266 B_FG_A_BG);
1267 else
1268 attrs = mix_cp (CP_BOARD_MOVES_BLACK,
1269 IS_ENPASSANT (p),
1270 ATTRS (CP_BOARD_MOVES_BLACK),
1271 B_FG_A_BG);
1273 else if (p != 'x' && !can_attack)
1274 attrs =
1275 (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK;
1277 if (BIG_BOARD && d->rotate)
1279 brow = INV_INT0 (brow);
1280 bcol = INV_INT0 (bcol);
1283 if (is_the_square (brow, bcol, row, col, d->c_row,
1284 d->c_col))
1286 attrs = mix_cp (CP_BOARD_CURSOR, IS_ENPASSANT (p),
1287 ATTRS (CP_BOARD_CURSOR), B_FG_A_BG);
1288 old_attrs = -1;
1290 else if (is_the_square (brow, bcol, row, col,
1291 d->sp.srow, d->sp.scol))
1293 attrs = mix_cp (CP_BOARD_SELECTED, IS_ENPASSANT (p),
1294 ATTRS (CP_BOARD_SELECTED), B_FG_A_BG);
1295 old_attrs = -1;
1297 else if ((is_prev_move (d, brow, bcol, row, col) && !valid)
1298 || (is_the_square (brow, bcol, row, col,
1299 d->ospm_row, d->ospm_col)))
1301 attrs = mix_cp (CP_BOARD_PREVMOVE, IS_ENPASSANT (p),
1302 ATTRS (CP_BOARD_PREVMOVE), B_FG_A_BG);
1303 old_attrs = -1;
1306 if (row == maxy - 1)
1307 attrs = 0;
1309 if (can_attack)
1311 int n = is_prev_move (d, brow, bcol, row, col);
1312 chtype a = n && !valid
1313 ? CP_BOARD_PREVMOVE : attrwhich ==
1314 WHITE ? valid ? CP_BOARD_MOVES_WHITE : CP_BOARD_WHITE
1315 : valid ? CP_BOARD_MOVES_BLACK : CP_BOARD_BLACK;
1316 attrs =
1317 mix_cp (CP_BOARD_ATTACK, a, ATTRS (CP_BOARD_ATTACK),
1318 A_FG_B_BG);
1319 old_attrs = -1;
1322 if (BIG_BOARD)
1323 wmove (boardw, row, col + ((MEGA_BOARD) ? 5 : 3) + l);
1324 else
1325 mvwaddch (boardw, row, col + l, ' ' | attrs);
1327 if (row == maxy - 1 && cxgc < 8)
1329 waddch (boardw,
1330 "abcdefgh"[(BIG_BOARD) ? bcol : bcol -
1331 1] | CP_BOARD_COORDS);
1332 cxgc++;
1334 else
1336 if (old_attrs == -1)
1338 old_attrs = attrs;
1339 goto printc;
1342 old_attrs = attrs;
1344 if (pi != OPEN_SQUARE && p != 'x' && !can_attack)
1346 if (attrwhich == WHITE)
1348 if (isupper (p))
1349 attrs = CP_BOARD_W_W;
1350 else
1351 attrs = CP_BOARD_W_B;
1353 else
1355 if (isupper (p))
1356 attrs = CP_BOARD_B_W;
1357 else
1358 attrs = CP_BOARD_B_B;
1362 printc:
1363 if (BIG_BOARD)
1365 if (config.details && !can_attack
1366 && castling_state (g, d->b,
1367 (d->rotate) ? INV_INT0 (brow + 1) - 1 : brow,
1368 (d->rotate) ? INV_INT0 (bcol + 1) - 1 : bcol,
1369 p, 0))
1370 attrs = mix_cp (CP_BOARD_CASTLING, attrs,
1371 ATTRS (CP_BOARD_CASTLING), A_FG_B_BG);
1373 else
1375 if (config.details && !can_attack
1376 && castling_state (g, d->b, RANKTOBOARD (brow),
1377 FILETOBOARD (bcol), p, 0))
1379 attrs = mix_cp (CP_BOARD_CASTLING, attrs,
1380 ATTRS (CP_BOARD_CASTLING),
1381 A_FG_B_BG);
1385 if (BIG_BOARD)
1387 // FIXME: Reimpresión de piezas(+4).
1388 if (cpd < 67)
1390 wattron (boardw, attrs);
1391 if (MEGA_BOARD)
1393 for (i = 0; i < 5; i++)
1394 mvwprintw (boardw, i + brow * 6 + 1,
1395 bcol * 12 + 1 + l,
1396 " ");
1397 if (pi != OPEN_SQUARE)
1398 print_piece (boardw, brow * 6 + 2,
1399 bcol * 12 + 3 + l, p);
1401 else
1403 print_piece (boardw, brow * 4 + 1,
1404 bcol * 8 + 1 + l,
1405 (pi != OPEN_SQUARE) ? p : 0);
1408 wattroff (boardw, attrs);
1409 cpd++;
1412 else
1414 wattron (boardw, attrs);
1415 waddwstr (boardw,
1416 piece_to_wchar (pi !=
1417 OPEN_SQUARE ? p : 0));
1418 wattroff (boardw, attrs);
1421 attrs = old_attrs;
1424 if (BIG_BOARD)
1425 col += (MEGA_BOARD) ? 10 : 6;
1426 else
1428 waddch (boardw, ' ' | attrs);
1429 col += 2;
1432 if (d->rotate)
1433 bcol--;
1434 else
1435 bcol++;
1437 if (BIG_BOARD)
1439 if (bcol > 7)
1440 bcol = 0;
1441 if (bcol < 0)
1442 bcol = 7;
1446 else
1448 if (col != maxx - 1)
1449 mvwaddch (boardw, row, col + l,
1450 LINE_GRAPHIC (ACS_HLINE | CP_BOARD_GRAPHICS));
1454 if (row % rowr)
1456 if (d->rotate)
1457 brow++;
1458 else
1459 brow--;
1462 if (BIG_BOARD)
1464 if (brow > 7)
1465 brow = 0;
1466 if (brow < 0)
1467 brow = 7;
1472 void
1473 invalid_move (int n, int e, const char *m)
1475 if (curses_initialized)
1476 cmessage (ERROR_STR, ANY_KEY_STR, "%s \"%s\" (round #%i)",
1477 (e ==
1478 E_PGN_AMBIGUOUS) ? _("Ambiguous move") : _("Invalid move"), m,
1480 else
1481 warnx ("%s: %s \"%s\" (round #%i)", loadfile, (e == E_PGN_AMBIGUOUS)
1482 ? _("Ambiguous move") : _("Invalid move"), m, n);
1485 void
1486 gameover (GAME g)
1488 struct userdata_s *d = g->data;
1490 SET_FLAG (g->flags, GF_GAMEOVER);
1491 d->mode = MODE_HISTORY;
1492 stop_engine (g);
1495 static void
1496 update_clock (GAME g, struct itimerval it)
1498 struct userdata_s *d = g->data;
1500 if (TEST_FLAG (d->flags, CF_CLOCK) && g->turn == WHITE)
1502 d->wclock.elapsed.tv_sec += it.it_value.tv_sec;
1503 d->wclock.elapsed.tv_usec += it.it_value.tv_usec;
1505 if (d->wclock.elapsed.tv_usec > 1000000 - 1)
1507 d->wclock.elapsed.tv_sec += d->wclock.elapsed.tv_usec / 1000000;
1508 d->wclock.elapsed.tv_usec = d->wclock.elapsed.tv_usec % 1000000;
1511 if (d->wclock.tc[d->wclock.tcn][1] &&
1512 d->wclock.elapsed.tv_sec >= d->wclock.tc[d->wclock.tcn][1])
1514 pgn_tag_add (&g->tag, (char *) "Result", (char *) "0-1");
1515 gameover (g);
1518 else if (TEST_FLAG (d->flags, CF_CLOCK) && g->turn == BLACK)
1520 d->bclock.elapsed.tv_sec += it.it_value.tv_sec;
1521 d->bclock.elapsed.tv_usec += it.it_value.tv_usec;
1523 if (d->bclock.elapsed.tv_usec > 1000000 - 1)
1525 d->bclock.elapsed.tv_sec += d->bclock.elapsed.tv_usec / 1000000;
1526 d->bclock.elapsed.tv_usec = d->bclock.elapsed.tv_usec % 1000000;
1529 if (d->bclock.tc[d->bclock.tcn][1] &&
1530 d->bclock.elapsed.tv_sec >= d->bclock.tc[d->bclock.tcn][1])
1532 pgn_tag_add (&g->tag, (char *) "Result", (char *) "1-0");
1533 gameover (g);
1537 d->elapsed.tv_sec += it.it_value.tv_sec;
1538 d->elapsed.tv_usec += it.it_value.tv_usec;
1540 if (d->elapsed.tv_usec > 1000000 - 1)
1542 d->elapsed.tv_sec += d->elapsed.tv_usec / 1000000;
1543 d->elapsed.tv_usec = d->elapsed.tv_usec % 1000000;
1547 static void
1548 update_time_control (GAME g)
1550 struct userdata_s *d = g->data;
1551 struct clock_s *clk = (g->turn == WHITE) ? &d->wclock : &d->bclock;
1553 if (clk->incr)
1554 clk->tc[clk->tcn][1] += clk->incr;
1556 if (!clk->tc[clk->tcn][1])
1557 return;
1559 clk->move++;
1561 if (!clk->tc[clk->tcn][0] || clk->move >= clk->tc[clk->tcn][0])
1563 clk->move = 0;
1564 clk->tc[clk->tcn + 1][1] +=
1565 labs (clk->elapsed.tv_sec - clk->tc[clk->tcn][1]);
1566 memset (&clk->elapsed, 0, sizeof (clk->elapsed));
1567 clk->tcn++;
1571 void
1572 update_history_window (GAME g)
1574 char buf[HISTORY_WIDTH - 1];
1575 HISTORY *h = NULL;
1576 int n, total;
1577 int t = pgn_history_total (g->hp);
1579 n = (g->hindex + 1) / 2;
1581 if (t % 2)
1582 total = (t + 1) / 2;
1583 else
1584 total = t / 2;
1586 if (t)
1587 snprintf (buf, sizeof (buf), "%u %s %u%s", n, _("of"), total,
1588 (movestep == 1) ? _(" (ply)") : "");
1589 else
1590 strncpy (buf, _("not available"), sizeof (buf) - 1);
1592 buf[sizeof (buf) - 1] = 0;
1593 mvwprintw (historyw, 2, 1, "%*s %-*s", 10, _("Move:"),
1594 HISTORY_WIDTH - 14, buf);
1596 h = pgn_history_by_n (g->hp, g->hindex);
1597 snprintf (buf, sizeof (buf), "%s",
1598 (h && h->move) ? h->move
1599 : (LINES < 24) ? _("empty") : _("not available"));
1600 n = 0;
1602 if (h && ((h->comment) || h->nag[0]))
1604 strncat (buf, _(" (Annotated"), sizeof (buf) - 1);
1605 n++;
1608 if (h && h->rav)
1610 strncat (buf, (n) ? ",+" : " (+", sizeof (buf) - 1);
1611 n++;
1614 if (g->ravlevel)
1616 strncat (buf, (n) ? ",-" : " (-", sizeof (buf) - 1);
1617 n++;
1620 if (n)
1621 strncat (buf, ")", sizeof (buf) - 1);
1623 mvwprintw (historyw, 3, ((LINES < 24) ? 17 : 1), "%s %-*s",
1624 (LINES < 24) ? _("Next:") : _("Next move:"),
1625 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1627 h = pgn_history_by_n (g->hp, g->hindex - 1);
1628 snprintf (buf, sizeof (buf), "%s",
1629 (h && h->move) ? h->move
1630 : (LINES < 24) ? _("empty") : _("not available"));
1631 n = 0;
1633 if (h && ((h->comment) || h->nag[0]))
1635 strncat (buf, _(" (Annotated"), sizeof (buf) - 1);
1636 n++;
1639 if (h && h->rav)
1641 strncat (buf, (n) ? ",+" : " (+", sizeof (buf) - 1);
1642 n++;
1645 if (g->ravlevel)
1647 strncat (buf, (n) ? ",-" : " (-", sizeof (buf) - 1);
1648 n++;
1651 if (n)
1652 strncat (buf, ")", sizeof (buf) - 1);
1654 mvwprintw (historyw, ((LINES < 24) ? 3 : 4), 1, "%s %-*s",
1655 (LINES < 24) ? _("Prev.:") : _("Prev move:"),
1656 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1659 void
1660 do_validate_move (char **move)
1662 struct userdata_s *d = gp->data;
1663 int n;
1664 char *frfr = NULL;
1666 if (TEST_FLAG (d->flags, CF_HUMAN))
1668 if ((n = pgn_parse_move (gp, d->b, move, &frfr)) != E_PGN_OK)
1670 invalid_move (d->n + 1, n, *move);
1671 return;
1674 strcpy (d->pm_frfr, frfr);
1675 update_time_control (gp);
1676 pgn_history_add (gp, d->b, *move);
1677 pgn_switch_turn (gp);
1679 else
1681 if ((n = pgn_validate_move (gp, d->b, move, &frfr)) != E_PGN_OK)
1683 invalid_move (d->n + 1, n, *move);
1684 return;
1687 add_engine_command (gp, ENGINE_THINKING, "%s\n",
1688 (config.engine_protocol == 1) ? frfr : *move);
1691 d->sp.srow = d->sp.scol = d->sp.icon = 0;
1693 if (config.validmoves)
1694 pgn_reset_valid_moves (d->b);
1696 if (TEST_FLAG (gp->flags, GF_GAMEOVER))
1697 d->mode = MODE_HISTORY;
1698 else
1699 SET_FLAG (d->flags, CF_MODIFIED);
1701 free (frfr);
1702 d->paused = 0;
1703 update_history_window (gp);
1704 update_board_window (gp);
1705 return;
1708 void
1709 do_promotion_piece_finalize (WIN * win)
1711 char *p, *str = win->data;
1713 if (pgn_piece_to_int (win->c) == -1)
1714 return;
1716 p = str + strlen (str);
1717 *p++ = toupper (win->c);
1718 *p = '\0';
1719 do_validate_move (&str);
1720 free (str);
1721 win->data = NULL;
1724 static void
1725 move_to_engine (GAME g)
1727 struct userdata_s *d = g->data;
1728 char *str;
1729 int piece;
1731 if (config.validmoves &&
1732 !d->b[RANKTOBOARD (d->sp.row)][FILETOBOARD (d->sp.col)].valid)
1733 return;
1735 str = Malloc (MAX_SAN_MOVE_LEN + 1);
1736 snprintf (str, MAX_SAN_MOVE_LEN + 1, "%c%u%c%u",
1737 _("abcdefgh")[d->sp.scol - 1],
1738 d->sp.srow, _("abcdefgh")[d->sp.col - 1], d->sp.row);
1740 piece =
1741 pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)]
1742 [FILETOBOARD (d->sp.scol)].icon);
1744 if (piece == PAWN && (d->sp.row == 8 || d->sp.row == 1))
1746 construct_message (_("Select Pawn Promotion Piece"), _("R/N/B/Q"), 1, 1,
1747 NULL, NULL, str, do_promotion_piece_finalize, 0, 0,
1748 NULL, "%s",
1749 _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1750 return;
1753 do_validate_move (&str);
1754 free (str);
1757 static char *
1758 clock_to_char (long n)
1760 static char buf[16];
1761 int h = 0, m = 0, s = 0;
1763 h = n / 3600;
1764 m = (n % 3600) / 60;
1765 s = (n % 3600) % 60;
1766 snprintf (buf, sizeof (buf), "%.2i:%.2i:%.2i", h, m, s);
1767 return buf;
1770 static char *
1771 timeval_to_char (struct timeval t, long limit)
1773 static char buf[9];
1774 unsigned h = 0, m = 0, s = 0;
1775 int n = limit ? labs (limit - t.tv_sec) : 0;
1777 h = n / 3600;
1778 m = (n % 3600) / 60;
1779 s = (n % 3600) % 60;
1780 snprintf (buf, sizeof (buf), "%.2u:%.2u:%.2u", h, m, s);
1781 return buf;
1784 static char *
1785 time_control_status (struct clock_s *clk)
1787 static char buf[80] = { 0 };
1789 buf[0] = 0;
1791 if (clk->tc[clk->tcn][0] && clk->tc[clk->tcn + 1][1])
1792 snprintf (buf, sizeof (buf), " M%.2i/%s",
1793 abs (clk->tc[clk->tcn][0] - clk->move),
1794 clock_to_char (clk->tc[clk->tcn + 1][1]));
1795 else if (!clk->incr)
1796 return (char *) "";
1798 if (clk->incr)
1800 char tbuf[16];
1802 strncat (tbuf, " I", sizeof (tbuf) - 1);
1803 strncat (tbuf, itoa (clk->incr, buf), sizeof (tbuf) - 1);
1806 return buf;
1809 void
1810 update_status_window (GAME g)
1812 int i = 0;
1813 char *buf;
1814 char tmp[15] = { 0 }, *engine, *mode;
1815 char t[COLS];
1816 int w;
1817 char *p;
1818 int maxy, maxx;
1819 int len;
1820 struct userdata_s *d = g->data;
1821 int y;
1822 int n;
1824 if (!curses_initialized)
1825 return;
1827 getmaxyx (statusw, maxy, maxx);
1828 (void) maxy;
1829 w = maxx - 2 - 8;
1830 len = maxx - 2;
1831 buf = Malloc (len);
1832 y = 2;
1834 wchar_t *loadfilew = loadfile[0]
1835 ? str_etc (loadfile, w, 1) : str_to_wchar (_("not available"));
1836 mvwprintw (statusw, y++, 1, "%*s %-*ls", 7, _("File:"), w, loadfilew);
1837 free (loadfilew);
1838 snprintf (buf, len, "%i %s %i", gindex + 1, _("of"), gtotal);
1839 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Game:"), w, buf);
1841 *tmp = '\0';
1842 p = tmp;
1844 if (config.details)
1846 *p++ = 'D';
1847 i++;
1850 if (TEST_FLAG (d->flags, CF_DELETE))
1852 if (i)
1853 *p++ = '/';
1855 *p++ = 'X';
1856 i++;
1859 if (TEST_FLAG (g->flags, GF_PERROR))
1861 if (i)
1862 *p++ = '/';
1864 *p++ = '!';
1865 i++;
1868 if (TEST_FLAG (d->flags, CF_MODIFIED))
1870 if (i)
1871 *p++ = '/';
1873 *p++ = '*';
1874 i++;
1877 pgn_config_get (PGN_STRICT_CASTLING, &n);
1879 if (n == 1)
1881 if (i)
1882 *p++ = '/';
1884 *p++ = 'C';
1885 i++;
1887 #ifdef WITH_LIBPERL
1888 if (TEST_FLAG (d->flags, CF_PERL))
1890 if (i)
1891 *p++ = '/';
1893 *p++ = 'P';
1894 i++;
1896 #endif
1898 *p = '\0';
1899 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Flags:"), w,
1900 (tmp[0]) ? tmp : "-");
1902 switch (d->mode)
1904 case MODE_HISTORY:
1905 mode = _("move history");
1906 break;
1907 case MODE_EDIT:
1908 mode = _("edit");
1909 break;
1910 case MODE_PLAY:
1911 mode = _("play");
1912 break;
1913 default:
1914 mode = _("(empty value)");
1915 break;
1918 snprintf (buf, len - 1, "%*s %s", 7, _("Mode:"), mode);
1920 if (d->mode == MODE_PLAY)
1922 if (TEST_FLAG (d->flags, CF_HUMAN))
1923 strncat (buf, _(" (human/human)"), len - 1);
1924 else if (TEST_FLAG (d->flags, CF_ENGINE_LOOP))
1925 strncat (buf, _(" (engine/engine)"), len - 1);
1926 else
1927 strncat (buf, (d->play_mode == PLAY_EH) ?
1928 _(" (engine/human)") : _(" (human/engine)"), len - 1);
1931 buf[len - 1] = 0;
1932 mvwprintw (statusw, y++, 1, "%-*s", len, buf);
1933 free (buf);
1935 if (d->engine)
1937 switch (d->engine->status)
1939 case ENGINE_THINKING:
1940 engine = _("pondering...");
1941 break;
1942 case ENGINE_READY:
1943 engine = _("ready");
1944 break;
1945 case ENGINE_INITIALIZING:
1946 engine = _("initializing...");
1947 break;
1948 case ENGINE_OFFLINE:
1949 engine = _("offline");
1950 break;
1951 default:
1952 engine = _("(empty value)");
1953 break;
1956 else
1957 engine = _("offline");
1959 mvwprintw (statusw, y, 1, "%*s %-*s", 7, _("Engine:"), w, " ");
1960 wattron (statusw, CP_STATUS_ENGINE);
1961 mvwaddstr (statusw, y++, 9, engine);
1962 wattroff (statusw, CP_STATUS_ENGINE);
1964 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Turn:"), w,
1965 (g->turn == WHITE) ? _("white") : _("black"));
1967 strncpy (tmp, _("white"), sizeof (tmp) - 1);
1968 tmp[0] = toupper (tmp[0]);
1969 snprintf (t, sizeof (t), "%s%s",
1970 timeval_to_char (d->wclock.elapsed,
1971 d->wclock.tc[d->wclock.tcn][1]),
1972 time_control_status (&d->wclock));
1973 mvwprintw (statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1975 strncpy (tmp, _("black"), sizeof (tmp) - 1);
1976 tmp[0] = toupper (tmp[0]);
1977 snprintf (t, sizeof (t), "%s%s",
1978 timeval_to_char (d->bclock.elapsed,
1979 d->bclock.tc[d->bclock.tcn][1]),
1980 time_control_status (&d->bclock));
1981 mvwprintw (statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1983 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Total:"), w,
1984 clock_to_char (d->elapsed.tv_sec));
1986 // for (i = 0; i < STATUS_WIDTH; i++)
1987 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1989 if (!status.notify)
1991 char tbuf[255];
1993 snprintf (tbuf, sizeof (tbuf), _("Type %ls for help"),
1994 key_lookup (global_keys, do_global_help));
1995 status.notify = str_to_wchar (tbuf);
1998 wattron (stdscr, CP_STATUS_NOTIFY);
1999 for (i = (config.boardleft) ? BOARD_WIDTH : 0;
2000 i < ((config.boardleft) ? COLS : STATUS_WIDTH); i++)
2001 mvwprintw (stdscr, STATUS_HEIGHT, i, " ");
2002 mvwprintw (stdscr, STATUS_HEIGHT, CENTERX (STATUS_WIDTH, status.notify)
2003 + ((config.boardleft) ? BOARD_WIDTH : 0), "%ls", status.notify);
2004 wattroff (stdscr, CP_STATUS_NOTIFY);
2007 wchar_t *
2008 translate_tag_name (const char *tag)
2010 if (!strcmp (tag, "Event"))
2011 return str_to_wchar (translatable_tag_names[0]);
2012 else if (!strcmp (tag, "Site"))
2013 return str_to_wchar (translatable_tag_names[1]);
2014 else if (!strcmp (tag, "Date"))
2015 return str_to_wchar (translatable_tag_names[2]);
2016 else if (!strcmp (tag, "Round"))
2017 return str_to_wchar (translatable_tag_names[3]);
2018 else if (!strcmp (tag, "White"))
2019 return str_to_wchar (translatable_tag_names[4]);
2020 else if (!strcmp (tag, "Black"))
2021 return str_to_wchar (translatable_tag_names[5]);
2022 else if (!strcmp (tag, "Result"))
2023 return str_to_wchar (translatable_tag_names[6]);
2025 return str_to_wchar (tag);
2028 void
2029 update_tag_window (TAG ** t)
2031 int i, l, w;
2032 int namel = 0;
2034 for (i = 0; t[i]; i++)
2036 wchar_t *namewc = translate_tag_name (t[i]->name);
2038 l = wcslen (namewc);
2039 free (namewc);
2040 if (l > namel)
2041 namel = l;
2044 w = TAG_WIDTH - namel - 4;
2046 for (i = 0; t[i] && i < TAG_HEIGHT - 3; i++)
2048 wchar_t *namewc = translate_tag_name (t[i]->name);
2049 wchar_t *valuewc = str_etc (t[i]->value, w, 0);
2051 mvwprintw (tagw, (i + 2), 1, "%*ls: %-*ls", namel, namewc, w, valuewc);
2052 free (namewc);
2053 free (valuewc);
2056 for (; i < TAG_HEIGHT - 3; i++)
2057 mvwprintw (tagw, (i + 2), 1, "%*s", namel + w + 2, " ");
2060 void
2061 append_enginebuf (GAME g, char *line)
2063 int i = 0;
2064 struct userdata_s *d = g->data;
2066 if (d->engine->enginebuf)
2067 for (i = 0; d->engine->enginebuf[i]; i++);
2069 if (d->engine->enginebuf && i >= LINES - 3)
2071 free (d->engine->enginebuf[0]);
2073 for (i = 0; d->engine->enginebuf[i + 1]; i++)
2074 d->engine->enginebuf[i] = d->engine->enginebuf[i + 1];
2076 d->engine->enginebuf[i] = strdup (line);
2078 else
2080 d->engine->enginebuf =
2081 Realloc (d->engine->enginebuf, (i + 2) * sizeof (char *));
2082 d->engine->enginebuf[i++] = strdup (line);
2083 d->engine->enginebuf[i] = NULL;
2087 void
2088 update_engine_window (GAME g)
2090 int i;
2091 struct userdata_s *d = g->data;
2093 wmove (enginew, 0, 0);
2094 wclrtobot (enginew);
2096 if (d->engine && d->engine->enginebuf)
2098 for (i = 0; d->engine->enginebuf[i]; i++)
2099 mvwprintw (enginew, i + 2, 1, "%s", d->engine->enginebuf[i]);
2102 window_draw_title (enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
2103 CP_MESSAGE_BORDER);
2106 void
2107 update_all (GAME g)
2109 struct userdata_s *d = g->data;
2112 * In the middle of a macro. Don't update the screen.
2114 if (macro_match != -1)
2115 return;
2117 wmove (boardw, ROWTOMATRIX (d->c_row), COLTOMATRIX (d->c_col));
2118 update_board_window (g);
2119 update_status_window (g);
2120 update_history_window (g);
2121 update_tag_window (g->tag);
2122 update_engine_window (g);
2123 update_panels ();
2124 doupdate ();
2127 static void
2128 game_next_prev (GAME g, int n, int count)
2130 if (gtotal < 2)
2131 return;
2133 if (n == 1)
2135 if (gindex + count > gtotal - 1)
2137 if (count != 1)
2138 gindex = gtotal - 1;
2139 else
2140 gindex = 0;
2142 else
2143 gindex += count;
2145 else
2147 if (gindex - count < 0)
2149 if (count != 1)
2150 gindex = 0;
2151 else
2152 gindex = gtotal - 1;
2154 else
2155 gindex -= count;
2158 gp = game[gindex];
2161 static void
2162 delete_game (int which)
2164 int i, w = which;
2165 struct userdata_s *d;
2167 for (i = 0; i < gtotal; i++)
2169 d = game[i]->data;
2171 if (i == w || TEST_FLAG (d->flags, CF_DELETE))
2173 int n;
2175 free_userdata_once (game[i]);
2176 pgn_free (game[i]);
2178 for (n = i; n + 1 < gtotal; n++)
2179 game[n] = game[n + 1];
2181 gtotal--;
2182 i--;
2183 w = -1;
2187 if (which != -1)
2189 if (which + 1 >= gtotal)
2190 gindex = gtotal - 1;
2191 else
2192 gindex = which;
2194 else
2195 gindex = gtotal - 1;
2197 gp = game[gindex];
2198 gp->hp = gp->history;
2202 * FIXME find across multiple games.
2204 static int
2205 find_move_exp (GAME g, regex_t r, int which, int count)
2207 int i;
2208 int ret;
2209 char errbuf[255];
2210 int incr;
2211 int found;
2213 incr = (which == 0) ? -1 : 1;
2215 for (i = g->hindex + incr - 1, found = 0;; i += incr)
2217 if (i == g->hindex - 1)
2218 break;
2220 if (i >= pgn_history_total (g->hp))
2221 i = 0;
2222 else if (i < 0)
2223 i = pgn_history_total (g->hp) - 1;
2225 // FIXME RAV
2226 ret = regexec (&r, g->hp[i]->move, 0, 0, 0);
2228 if (ret == 0)
2230 if (count == ++found)
2232 return i + 1;
2235 else
2237 if (ret != REG_NOMATCH)
2239 regerror (ret, &r, errbuf, sizeof (errbuf));
2240 cmessage (_("Error Matching Regular Expression"), ANY_KEY_STR,
2241 "%s", errbuf);
2242 return -1;
2247 return -1;
2250 static int
2251 toggle_delete_flag (int n)
2253 int i, x;
2254 struct userdata_s *d = game[n]->data;
2256 TOGGLE_FLAG (d->flags, CF_DELETE);
2257 gindex = n;
2259 for (i = x = 0; i < gtotal; i++)
2261 d = game[i]->data;
2263 if (TEST_FLAG (d->flags, CF_DELETE))
2264 x++;
2267 if (x == gtotal)
2269 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
2270 d = game[n]->data;
2271 CLEAR_FLAG (d->flags, CF_DELETE);
2272 return 1;
2275 return 0;
2278 static int
2279 find_game_exp (char *str, int which, int count)
2281 char *nstr = NULL, *exp = NULL;
2282 regex_t nexp, vexp;
2283 int ret = -1;
2284 int g = 0;
2285 char buf[255] = { 0 }, *tmp;
2286 char errbuf[255];
2287 int found = 0;
2288 int incr = (which == 0) ? -(1) : 1;
2290 strncpy (buf, str, sizeof (buf));
2291 buf[sizeof (buf)-1] = 0;
2292 tmp = buf;
2294 if (strstr (tmp, ":") != NULL)
2296 nstr = strsep (&tmp, ":");
2298 if ((ret = regcomp (&nexp, nstr,
2299 REG_ICASE | REG_EXTENDED | REG_NOSUB)) != 0)
2301 regerror (ret, &nexp, errbuf, sizeof (errbuf));
2302 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR,
2303 "%s", errbuf);
2304 ret = g = -1;
2305 goto cleanup;
2309 exp = tmp;
2311 while (exp && *exp && isspace (*exp))
2312 exp++;
2314 if (exp == NULL)
2315 goto cleanup;
2317 if ((ret = regcomp (&vexp, exp, REG_EXTENDED | REG_NOSUB)) != 0)
2319 regerror (ret, &vexp, errbuf, sizeof (errbuf));
2320 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s",
2321 errbuf);
2322 ret = -1;
2323 goto cleanup;
2326 ret = -1;
2328 for (g = gindex + incr, found = 0;; g += incr)
2330 int t;
2332 if (g == gtotal)
2333 g = 0;
2334 else if (g < 0)
2335 g = gtotal - 1;
2337 if (g == gindex)
2338 break;
2340 for (t = 0; game[g]->tag[t]; t++)
2342 if (nstr)
2344 if (regexec (&nexp, game[g]->tag[t]->name, 0, 0, 0) == 0)
2346 if (regexec (&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0)
2348 if (count == ++found)
2350 ret = g;
2351 goto cleanup;
2356 else
2358 if (regexec (&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0)
2360 if (count == ++found)
2362 ret = g;
2363 goto cleanup;
2369 ret = -1;
2372 cleanup:
2373 if (nstr)
2374 regfree (&nexp);
2376 if (g != -1)
2377 regfree (&vexp);
2379 return ret;
2383 * Updates the notification line in the status window then refreshes the
2384 * status window.
2386 void
2387 update_status_notify (GAME g, const char *fmt, ...)
2389 va_list ap;
2390 #ifdef HAVE_VASPRINTF
2391 char *line;
2392 #else
2393 char line[COLS];
2394 #endif
2396 free (status.notify);
2397 status.notify = NULL;
2399 if (!fmt)
2400 return;
2402 va_start (ap, fmt);
2403 #ifdef HAVE_VASPRINTF
2404 vasprintf (&line, fmt, ap);
2405 #else
2406 vsnprintf (line, sizeof (line), fmt, ap);
2407 #endif
2408 va_end (ap);
2410 status.notify = str_to_wchar (line);
2412 #ifdef HAVE_VASPRINTF
2413 free (line);
2414 #endif
2418 rav_next_prev (GAME g, BOARD b, int n)
2420 // Next RAV.
2421 if (n)
2423 if ((!g->ravlevel && g->hindex && g->hp[g->hindex - 1]->rav == NULL) ||
2424 (!g->ravlevel && !g->hindex && g->hp[g->hindex]->rav == NULL) ||
2425 (g->ravlevel && g->hp[g->hindex]->rav == NULL))
2426 return 1;
2428 g->rav = Realloc (g->rav, (g->ravlevel + 1) * sizeof (RAV));
2429 g->rav[g->ravlevel].hp = g->hp;
2430 g->rav[g->ravlevel].flags = g->flags;
2431 g->rav[g->ravlevel].fen = pgn_game_to_fen (g, b);
2432 g->rav[g->ravlevel].hindex = g->hindex;
2433 g->hp =
2434 (!g->ravlevel) ? (g->hindex) ? g->hp[g->hindex -
2435 1]->rav : g->hp[g->
2436 hindex]->rav :
2437 g->hp[g->hindex]->rav;
2438 g->hindex = 0;
2439 g->ravlevel++;
2440 pgn_board_update (g, b, g->hindex + 1);
2441 return 0;
2444 if (g->ravlevel - 1 < 0)
2445 return 1;
2447 // Previous RAV.
2448 g->ravlevel--;
2449 pgn_board_init_fen (g, b, g->rav[g->ravlevel].fen);
2450 free (g->rav[g->ravlevel].fen);
2451 g->hp = g->rav[g->ravlevel].hp;
2452 g->flags = g->rav[g->ravlevel].flags;
2453 g->hindex = g->rav[g->ravlevel].hindex;
2454 return 0;
2457 static void
2458 draw_window_decor ()
2460 move_panel (boardp, 0, (config.boardleft) ? 0 : COLS - BOARD_WIDTH);
2461 move_panel (historyp, LINES - HISTORY_HEIGHT,
2462 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH : 0 :
2463 (MEGA_BOARD) ? 0 : COLS - HISTORY_WIDTH);
2464 move_panel (statusp, 0, (config.boardleft) ? BOARD_WIDTH : 0);
2465 move_panel (tagp, STATUS_HEIGHT + 1,
2466 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH :
2467 HISTORY_WIDTH : 0);
2469 wbkgd (boardw, CP_BOARD_WINDOW);
2470 wbkgd (statusw, CP_STATUS_WINDOW);
2471 window_draw_title (statusw, _("Game Status"), STATUS_WIDTH,
2472 CP_STATUS_TITLE, CP_STATUS_BORDER);
2473 wbkgd (tagw, CP_TAG_WINDOW);
2474 window_draw_title (tagw, _("Roster Tags"), TAG_WIDTH, CP_TAG_TITLE,
2475 CP_TAG_BORDER);
2476 wbkgd (historyw, CP_HISTORY_WINDOW);
2477 window_draw_title (historyw, _("Move History"), HISTORY_WIDTH,
2478 CP_HISTORY_TITLE, CP_HISTORY_BORDER);
2481 static void
2482 history_menu_resize (WIN *w)
2484 struct menu_input_s *m;
2486 if (!w)
2487 return;
2489 w->rows = MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES;
2490 w->cols = TAG_WIDTH;
2491 w->posy = 0;
2492 w->posx = config.boardleft ? BOARD_WIDTH : 0;
2493 m = w->data;
2494 m->ystatic = w->posy;
2495 m->xstatic = w->posx;
2496 redraw_menu (w);
2499 void
2500 do_window_resize ()
2502 if (LINES < 23 || COLS < 74)
2503 return;
2505 resizeterm (LINES, COLS);
2506 endwin ();
2507 wresize (boardw, BOARD_HEIGHT, BOARD_WIDTH);
2508 wresize (historyw, HISTORY_HEIGHT, HISTORY_WIDTH);
2509 wresize (statusw, STATUS_HEIGHT, STATUS_WIDTH);
2510 wresize (tagw, TAG_HEIGHT, TAG_WIDTH);
2511 //resize_history_menu ();
2513 clear ();
2514 wclear (boardw);
2515 wclear (historyw);
2516 wclear (tagw);
2517 wclear (statusw);
2518 wclear (loadingw);
2519 wclear (enginew);
2520 draw_window_decor ();
2521 update_all (gp);
2522 keypad (boardw, TRUE);
2523 curs_set (0);
2524 cbreak ();
2525 noecho ();
2528 void
2529 do_global_redraw ()
2531 do_window_resize ();
2534 void
2535 stop_clock ()
2537 memset (&clock_timer, 0, sizeof (struct itimerval));
2538 setitimer (ITIMER_REAL, &clock_timer, NULL);
2541 void
2542 start_clock (GAME g)
2544 struct userdata_s *d = g->data;
2546 if (clock_timer.it_interval.tv_usec)
2547 return;
2549 memset (&d->elapsed, 0, sizeof (struct timeval));
2550 clock_timer.it_value.tv_sec = 0;
2551 clock_timer.it_value.tv_usec = 100000;
2552 clock_timer.it_interval.tv_sec = 0;
2553 clock_timer.it_interval.tv_usec = 100000;
2554 setitimer (ITIMER_REAL, &clock_timer, NULL);
2557 static void
2558 update_clocks ()
2560 int i;
2561 struct userdata_s *d;
2562 struct itimerval it;
2563 int update = 0;
2565 getitimer (ITIMER_REAL, &it);
2567 for (i = 0; i < gtotal; i++)
2569 d = game[i]->data;
2571 if (d && d->mode == MODE_PLAY)
2573 if (d->paused == 1 || TEST_FLAG (d->flags, CF_NEW))
2574 continue;
2575 else if (d->paused == -1)
2577 if (game[i]->side == game[i]->turn)
2579 d->paused = 1;
2580 continue;
2584 update_clock (game[i], it);
2586 if (game[i] == gp)
2587 update = 1;
2591 if (update)
2593 update_status_window (gp);
2594 update_panels ();
2595 doupdate ();
2599 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2601 static int
2602 parse_clock_time (char **str)
2604 char *p = *str;
2605 int n = 0, t = 0;
2607 SKIP_SPACE (p);
2609 if (!isdigit (*p))
2610 return -1;
2612 while (*p)
2614 if (isdigit (*p))
2616 t = atoi (p);
2618 while (isdigit (*p))
2619 p++;
2621 continue;
2624 switch (*p)
2626 case 'H':
2627 case 'h':
2628 n += t * (60 * 60);
2629 t = 0;
2630 break;
2631 case 'M':
2632 case 'm':
2633 n += t * 60;
2634 t = 0;
2635 break;
2636 case 'S':
2637 case 's':
2638 n += t;
2639 t = 0;
2640 break;
2641 case ' ':
2642 p++;
2643 case '/':
2644 case '+':
2645 goto done;
2646 default:
2647 *str = p;
2648 return -1;
2651 p++;
2654 done:
2655 n += t;
2656 *str = p;
2657 return n;
2660 static int
2661 parse_clock_input (struct clock_s *clk, char *str, int *incr)
2663 char *p = str;
2664 long n = 0;
2665 int plus = 0;
2666 int m = 0;
2667 int tc = 0;
2669 SKIP_SPACE (p);
2671 if (!*p)
2672 return 0;
2674 if (*p == '+')
2676 plus = 1;
2677 p++;
2678 SKIP_SPACE (p);
2680 if (*p == '+')
2681 goto move_incr;
2683 else
2684 memset (clk, 0, sizeof (struct clock_s));
2686 again:
2687 /* Sudden death. */
2688 if (strncasecmp (p, "SD", 2) == 0)
2690 n = 0;
2691 p += 2;
2692 goto tc;
2695 n = parse_clock_time (&p);
2697 if (n == -1)
2698 return 1;
2700 if (!n)
2701 goto done;
2703 /* Time control. */
2705 if (*p == '/')
2707 if (plus)
2708 return 1;
2710 /* Sudden death without a previous time control. */
2711 if (!n && !tc)
2712 return 1;
2714 m = n;
2715 p++;
2716 n = parse_clock_time (&p);
2718 if (n == -1)
2719 return 1;
2721 if (tc >= MAX_TC)
2723 message (ERROR_STR, ANY_KEY_STR, "%s (%i)",
2724 _("Maximum number of time controls reached"), MAX_TC);
2725 return 1;
2728 clk->tc[tc][0] = m;
2729 clk->tc[tc++][1] = n;
2730 SKIP_SPACE (p);
2732 if (*p == '+')
2733 goto move_incr;
2735 if (*p)
2736 goto again;
2738 goto done;
2741 if (plus)
2742 *incr = n;
2743 else
2744 clk->tc[clk->tcn][1] =
2745 (n <= clk->elapsed.tv_sec) ? clk->elapsed.tv_sec + n : n;
2747 move_incr:
2748 if (*p)
2750 if (*p++ == '+')
2752 if (!isdigit (*p))
2753 return 1;
2755 n = parse_clock_time (&p);
2757 if (n == -1 || *p)
2758 return 1;
2760 clk->incr = n;
2762 SKIP_SPACE (p);
2764 if (*p)
2765 return 1;
2767 else
2768 return 1;
2771 done:
2772 return 0;
2775 static int
2776 parse_which_clock (struct clock_s *clk, char *str)
2778 struct clock_s tmp;
2779 int incr = 0;
2781 memcpy (&tmp, clk, sizeof (struct clock_s));
2783 if (parse_clock_input (&tmp, str, &incr))
2785 cmessage (ERROR_STR, ANY_KEY_STR, _("Invalid clock specification"));
2786 return 1;
2789 memcpy (clk, &tmp, sizeof (struct clock_s));
2790 clk->tc[clk->tcn][1] += incr;
2791 return 0;
2794 void
2795 do_clock_input_finalize (WIN * win)
2797 struct userdata_s *d = gp->data;
2798 struct input_data_s *in = win->data;
2799 char *p = in->str;
2801 if (!in->str)
2803 free (in);
2804 return;
2807 SKIP_SPACE (p);
2809 if (tolower (*p) == 'w')
2811 p++;
2813 if (parse_which_clock (&d->wclock, p))
2814 goto done;
2816 else if (tolower (*p) == 'b')
2818 p++;
2820 if (parse_which_clock (&d->bclock, p))
2821 goto done;
2823 else
2825 if (parse_which_clock (&d->wclock, p))
2826 goto done;
2828 if (parse_which_clock (&d->bclock, p))
2829 goto done;
2832 if (!d->wclock.tc[0][1] && !d->bclock.tc[0][1])
2833 CLEAR_FLAG (d->flags, CF_CLOCK);
2834 else
2835 SET_FLAG (d->flags, CF_CLOCK);
2837 done:
2838 free (in->str);
2839 free (in);
2842 void
2843 do_engine_command_finalize (WIN * win)
2845 struct userdata_s *d = gp->data;
2846 struct input_data_s *in = win->data;
2847 int x;
2849 if (!in->str)
2851 free (in);
2852 return;
2855 if (!d->engine)
2856 goto done;
2858 x = d->engine->status;
2859 send_to_engine (gp, -1, "%s\n", in->str);
2860 d->engine->status = x;
2862 done:
2863 free (in->str);
2864 free (in);
2867 void
2868 do_board_details ()
2870 config.details = (config.details) ? 0 : 1;
2873 void
2874 do_toggle_strict_castling ()
2876 int n;
2878 pgn_config_get (PGN_STRICT_CASTLING, &n);
2880 if (n == 0)
2881 pgn_config_set (PGN_STRICT_CASTLING, 1);
2882 else
2883 pgn_config_set (PGN_STRICT_CASTLING, 0);
2886 void
2887 do_play_set_clock ()
2889 struct input_data_s *in;
2891 in = Calloc (1, sizeof (struct input_data_s));
2892 in->efunc = do_clock_input_finalize;
2893 construct_input (_("Set Clock"), NULL, 1, 1,
2895 ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n"
2896 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2897 NULL, NULL, 0, in, INPUT_HIST_CLOCK, NULL, -1);
2900 void
2901 do_play_toggle_human ()
2903 struct userdata_s *d = gp->data;
2905 TOGGLE_FLAG (d->flags, CF_HUMAN);
2907 if (!TEST_FLAG (d->flags, CF_HUMAN) && pgn_history_total (gp->hp))
2909 if (init_chess_engine (gp))
2910 return;
2913 CLEAR_FLAG (d->flags, CF_ENGINE_LOOP);
2915 if (d->engine)
2916 d->engine->status = ENGINE_READY;
2919 void
2920 do_play_toggle_engine ()
2922 struct userdata_s *d = gp->data;
2924 TOGGLE_FLAG (d->flags, CF_ENGINE_LOOP);
2925 CLEAR_FLAG (d->flags, CF_HUMAN);
2927 if (d->engine && TEST_FLAG (d->flags, CF_ENGINE_LOOP))
2929 char *fen = pgn_game_to_fen (gp, d->b);
2931 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
2932 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
2933 free (fen);
2938 * This will send a command to the engine skipping the command queue.
2940 void
2941 do_play_send_command ()
2943 struct userdata_s *d = gp->data;
2944 struct input_data_s *in;
2946 if (!d->engine || d->engine->status == ENGINE_OFFLINE)
2948 if (init_chess_engine (gp))
2949 return;
2952 in = Calloc (1, sizeof (struct input_data_s));
2953 in->efunc = do_engine_command_finalize;
2954 construct_input (_("Engine Command"), NULL, 1, 1, NULL, NULL, NULL, 0, in,
2955 INPUT_HIST_ENGINE, NULL, -1);
2959 void do_play_switch_turn()
2961 struct userdata_s *d = gp->data;
2963 pgn_switch_side(gp);
2964 pgn_switch_turn(gp);
2966 if (!TEST_FLAG(d->flags, CF_HUMAN))
2967 add_engine_command(gp, -1,
2968 (gp->side == WHITE) ? "white\n" : "black\n");
2970 update_status_window(gp);
2973 void
2974 do_play_toggle_eh_mode ()
2976 struct userdata_s *d = gp->data;
2978 if (!TEST_FLAG (d->flags, CF_HUMAN))
2980 if (!gp->hindex)
2982 pgn_switch_side (gp, TRUE);
2983 d->play_mode = (d->play_mode) ? PLAY_HE : PLAY_EH;
2984 if (gp->side == BLACK)
2985 update_status_notify (gp, _("Press 'g' to start the game"));
2987 d->rotate = !d->rotate;
2989 else
2990 message (NULL, ANY_KEY_STR,
2991 _("You may only switch sides at the start of the \n"
2992 "game. Press ^K or ^N to begin a new game."));
2996 void
2997 do_play_undo ()
2999 struct userdata_s *d = gp->data;
3001 if (!pgn_history_total (gp->hp))
3002 return;
3004 if (keycount)
3006 if (gp->hindex - keycount < 0)
3007 gp->hindex = 0;
3008 else
3010 if (d->go_move)
3011 gp->hindex -= (keycount * 2) - 1;
3012 else
3013 gp->hindex -= keycount * 2;
3016 else
3018 if (gp->hindex - 2 < 0)
3019 gp->hindex = 0;
3020 else
3022 if (d->go_move)
3023 gp->hindex -= 1;
3024 else
3025 gp->hindex -= 2;
3029 pgn_history_free (gp->hp, gp->hindex);
3030 gp->hindex = pgn_history_total (gp->hp);
3031 pgn_board_update (gp, d->b, gp->hindex);
3033 if (d->engine && d->engine->status == ENGINE_READY)
3035 char *fen = pgn_game_to_fen (gp, d->b);
3037 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
3038 free (fen);
3039 d->engine->status = ENGINE_READY;
3042 update_history_window (gp);
3044 if (d->go_move)
3046 pgn_switch_side (gp, FALSE);
3047 d->go_move--;
3050 d->pm_undo = TRUE;
3053 void
3054 do_play_toggle_pause ()
3056 struct userdata_s *d = gp->data;
3058 if (!TEST_FLAG (d->flags, CF_HUMAN) && gp->turn != gp->side)
3060 d->paused = -1;
3061 return;
3064 d->paused = (d->paused) ? 0 : 1;
3067 void
3068 do_play_go ()
3070 struct userdata_s *d = gp->data;
3072 if (TEST_FLAG (d->flags, CF_HUMAN))
3073 return;
3075 if (fm_loaded_file && gp->side != gp->turn)
3077 pgn_switch_side (gp, FALSE);
3078 add_engine_command (gp, ENGINE_THINKING, "black\n");
3081 add_engine_command (gp, ENGINE_THINKING, "go\n");
3083 // Completa la función para que permita seguir jugando al usarla.
3084 // Complete the function to allow continue playing when using.
3085 if (gp->side == gp->turn)
3086 pgn_switch_side (gp, FALSE);
3088 d->go_move++;
3091 void
3092 do_play_config_command ()
3094 int x, w;
3096 if (config.keys)
3098 for (x = 0; config.keys[x]; x++)
3100 if (config.keys[x]->c == input_c)
3102 switch (config.keys[x]->type)
3104 case KEY_DEFAULT:
3105 add_engine_command (gp, -1, "%ls\n", config.keys[x]->str);
3106 break;
3107 case KEY_SET:
3108 if (!keycount)
3109 break;
3111 add_engine_command (gp, -1,
3112 "%ls %i\n", config.keys[x]->str,
3113 keycount);
3114 keycount = 0;
3115 break;
3116 case KEY_REPEAT:
3117 if (!keycount)
3118 break;
3120 for (w = 0; w < keycount; w++)
3121 add_engine_command (gp, -1, "%ls\n", config.keys[x]->str);
3122 keycount = 0;
3123 break;
3129 update_status_notify (gp, NULL);
3132 void
3133 do_play_cancel_selected ()
3135 struct userdata_s *d = gp->data;
3137 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3138 keycount = 0;
3139 pgn_reset_valid_moves (d->b);
3140 update_status_notify (gp, NULL);
3143 void
3144 do_play_commit ()
3146 struct userdata_s *d = gp->data;
3148 pushkey = keycount = 0;
3149 update_status_notify (gp, NULL);
3151 if (!TEST_FLAG (d->flags, CF_HUMAN) &&
3152 (!d->engine || d->engine->status == ENGINE_THINKING))
3153 return;
3155 if (!d->sp.icon)
3156 return;
3158 d->sp.row = d->c_row;
3159 d->sp.col = d->c_col;
3161 if (d->rotate)
3163 rotate_position (&d->sp.row, &d->sp.col);
3164 rotate_position (&d->sp.srow, &d->sp.scol);
3167 move_to_engine (gp);
3169 // Completa la función para que permita seguir jugando cuando se carga un
3170 // archivo pgn (con juego no terminado) que inicie con turno del lado
3171 // negro.
3172 // Complete the function to allow continue playing when loading a file
3173 // pgn (with unfinished game) you start to turn black side.
3174 if (gp->side != gp->turn)
3175 pgn_switch_side (gp, FALSE);
3177 if (d->rotate && d->sp.icon)
3178 rotate_position (&d->sp.srow, &d->sp.scol);
3180 d->go_move = 0;
3181 fm_loaded_file = FALSE;
3182 d->pm_undo = FALSE;
3185 void
3186 do_play_select ()
3188 struct userdata_s *d = gp->data;
3190 if (!TEST_FLAG (d->flags, CF_HUMAN) && (!d->engine ||
3191 d->engine->status ==
3192 ENGINE_OFFLINE))
3194 if (init_chess_engine (gp))
3195 return;
3198 if (d->engine && d->engine->status == ENGINE_THINKING)
3199 return;
3201 if (d->sp.icon)
3202 do_play_cancel_selected ();
3204 if (d->rotate)
3205 rotate_position (&d->c_row, &d->c_col);
3207 d->sp.icon = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon;
3209 if (pgn_piece_to_int (d->sp.icon) == OPEN_SQUARE)
3211 d->sp.icon = 0;
3212 return;
3215 if (((islower (d->sp.icon) && gp->turn != BLACK)
3216 || (isupper (d->sp.icon) && gp->turn != WHITE)))
3218 struct key_s **k;
3219 char *str = Malloc (512);
3221 for (k = play_keys; *k; k++)
3223 if ((*k)->f == do_play_toggle_eh_mode)
3224 break;
3227 snprintf (str, 512,
3229 ("It is not your turn to move. You may switch playing sides by pressing \"%lc\"."),
3230 *k ? (*k)->c : '?');
3231 message (NULL, ANY_KEY_STR, "%s", str);
3232 free (str);
3233 d->sp.icon = 0;
3234 return;
3235 #if 0
3236 if (pgn_history_total (gp->hp))
3238 message (NULL, ANY_KEY_STR, "%s",
3239 _("It is not your turn to move. You can switch sides "));
3240 d->sp.icon = 0;
3241 return;
3243 else
3245 if (pgn_tag_find (gp->tag, "FEN") != E_PGN_ERR)
3246 return;
3248 add_engine_command (gp, ENGINE_READY, "black\n");
3249 pgn_switch_turn (gp);
3251 if (gp->side != BLACK)
3252 pgn_switch_side (gp);
3254 #endif
3257 d->sp.srow = d->c_row;
3258 d->sp.scol = d->c_col;
3260 if (config.validmoves)
3261 pgn_find_valid_moves (gp, d->b, d->sp.scol, d->sp.srow);
3263 if (d->rotate)
3265 rotate_position (&d->c_row, &d->c_col);
3266 rotate_position (&d->sp.srow, &d->sp.scol);
3269 CLEAR_FLAG (d->flags, CF_NEW);
3270 start_clock (gp);
3273 static void
3274 build_help_line_once (wchar_t *buf, wchar_t **pp, struct key_s *k, int t,
3275 int nlen)
3277 wchar_t *p = *pp, *wc;
3278 int n;
3280 if (k->key)
3281 n = wcslen (k->key);
3282 else
3283 n = 1;
3285 while (n++ <= nlen)
3286 *p++ = ' ';
3288 *p = 0;
3290 if (k->key)
3292 wcsncat (buf, k->key, t - 1);
3293 p = buf + wcslen (buf);
3295 else
3296 *p++ = k->c;
3298 *p++ = ' ';
3299 *p++ = '-';
3300 *p++ = ' ';
3301 *p = 0;
3303 if (k->d)
3304 wcsncat (buf, k->d, t - 1);
3306 if (k->r)
3308 wc = str_to_wchar ("*");
3309 wcsncat (buf, wc, t - 1);
3310 free (wc);
3313 wc = str_to_wchar ("\n");
3314 wcscat (buf, wc);
3315 free (wc);
3316 *pp = buf + wcslen (buf);
3319 static void
3320 calc_help_len (struct key_s *k, int *t, int *nlen, int *len)
3322 if (!k->d)
3323 return;
3325 if (k->key)
3327 if (wcslen (k->key) > *nlen)
3329 *nlen = wcslen (k->key);
3330 *t += *nlen;
3332 else
3333 (*t)++;
3335 else
3336 (*t)++;
3338 if (k->d)
3340 if (wcslen (k->d) > *len)
3341 *len = wcslen (k->d);
3344 *t += *len;
3345 *t += k->r;
3348 static wchar_t *
3349 build_help (struct key_s **keys)
3351 int i, m = 0, nlen = 1, len, t;
3352 wchar_t *buf = NULL;
3353 wchar_t *p;
3354 wchar_t *more_help = str_to_wchar (_("more help"));
3355 const wchar_t *more_help_key = key_lookup (global_keys, do_global_help);
3356 struct key_s k;
3357 int mode = MODE_ANY;
3359 if (!keys)
3361 free (more_help);
3362 return NULL;
3365 for (i = t = len = 0; keys[i]; i++)
3366 calc_help_len (keys[i], &t, &nlen, &len);
3368 if (macros)
3370 if (keys == play_keys)
3371 mode = MODE_PLAY;
3372 else if (keys == history_keys)
3373 mode = MODE_HISTORY;
3374 else if (keys == edit_keys)
3375 mode = MODE_EDIT;
3376 else
3377 mode = MODE_ANY;
3379 for (m = 0; macros[m]; m++)
3381 if (macros[m]->mode == mode)
3383 k.d = macros[m]->desc;
3384 k.r = k.c = 0;
3385 k.key = str_to_wchar (fancy_key_name (macros[m]->c));
3386 calc_help_len (&k, &t, &nlen, &len);
3387 free (k.key);
3392 k.d = more_help;
3393 k.r = k.c = 0;
3394 k.key = (wchar_t *)more_help_key;
3395 calc_help_len (&k, &t, &nlen, &len);
3397 t += 4 + i + 1 + m + 1; // +1 for more_help
3398 buf = Malloc ((t + 1) * sizeof (wchar_t));
3399 p = buf;
3401 for (i = 0; keys[i]; i++)
3403 if (!keys[i]->d)
3404 continue;
3406 build_help_line_once (buf, &p, keys[i], t, nlen);
3409 if (macros)
3411 for (m = 0; macros[m]; m++)
3413 if (macros[m]->mode == mode)
3415 k.d = macros[m]->desc;
3416 k.r = k.c = 0;
3417 k.key = str_to_wchar (fancy_key_name (macros[m]->c));
3418 build_help_line_once (buf, &p, &k, t, nlen);
3419 free (k.key);
3424 k.d = more_help;
3425 k.r = k.c = 0;
3426 k.key = (wchar_t *)more_help_key;
3427 build_help_line_once (buf, &p, &k, t, nlen);
3428 free (more_help);
3429 return buf;
3432 void
3433 do_global_help ()
3435 struct userdata_s *d = gp->data;
3436 wchar_t *buf;
3438 if (!d->global_help)
3440 switch (d->mode)
3442 case MODE_PLAY:
3443 do_play_help ();
3444 return;
3445 case MODE_EDIT:
3446 do_edit_help ();
3447 return;
3448 case MODE_HISTORY:
3449 do_history_help ();
3450 return;
3451 default:
3452 break;
3456 d->global_help = 0;
3457 buf = build_help (global_keys);
3458 construct_message (_("Global Game Keys (* = can take a repeat count)"),
3459 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
3460 0, 1, NULL, "%ls", buf);
3463 void
3464 do_main_help (WIN * win)
3466 struct userdata_s *d = gp->data;
3468 switch (win->c)
3470 case 'p':
3471 do_play_help ();
3472 break;
3473 case 'h':
3474 do_history_help ();
3475 break;
3476 case 'e':
3477 do_edit_help ();
3478 break;
3479 case 'g':
3480 d->global_help = 1;
3481 do_global_help ();
3482 break;
3483 default:
3484 break;
3488 static void
3489 do_more_help (WIN * win)
3491 int i;
3493 for (i = 0; global_keys[i]; i++)
3495 if (global_keys[i]->f == do_global_help
3496 && global_keys[i]->c == win->c)
3497 construct_message (_("Command Key Index"),
3498 _("p/h/e/g or any other key to quit"), 0, 0,
3499 NULL, NULL, NULL, do_main_help, 0, 0, NULL, "%s",
3500 _(" p - play mode keys\n"
3501 " h - history mode keys\n"
3502 " e - board edit mode keys\n"
3503 " g - global game keys"));
3507 static void
3508 do_play_help ()
3510 wchar_t *buf = build_help (play_keys);
3512 construct_message (_("Play Mode Keys (* = can take a repeat count)"),
3513 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
3514 buf, do_more_help, 0, 1, NULL, "%ls", buf);
3517 void
3518 do_play_history_mode ()
3520 struct userdata_s *d = gp->data;
3522 if (!pgn_history_total (gp->hp) ||
3523 (d->engine && d->engine->status == ENGINE_THINKING))
3524 return;
3526 d->mode = MODE_HISTORY;
3527 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
3530 void
3531 do_play_edit_mode ()
3533 struct userdata_s *d = gp->data;
3535 if (pgn_history_total (gp->hp))
3536 return;
3538 pgn_board_init_fen (gp, d->b, NULL);
3539 config.details++;
3540 d->mode = MODE_EDIT;
3543 void
3544 do_edit_insert_finalize (WIN * win)
3546 struct userdata_s *d = win->data;
3548 if (pgn_piece_to_int (win->c) == -1)
3549 return;
3551 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon = win->c;
3554 void
3555 do_edit_select ()
3557 struct userdata_s *d = gp->data;
3559 if (d->sp.icon)
3560 return;
3562 d->sp.icon = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon;
3564 if (pgn_piece_to_int (d->sp.icon) == OPEN_SQUARE)
3566 d->sp.icon = 0;
3567 return;
3570 d->sp.srow = d->c_row;
3571 d->sp.scol = d->c_col;
3574 void
3575 do_edit_commit ()
3577 int p;
3578 struct userdata_s *d = gp->data;
3580 pushkey = keycount = 0;
3581 update_status_notify (gp, NULL);
3583 if (!d->sp.icon)
3584 return;
3586 d->sp.row = d->c_row;
3587 d->sp.col = d->c_col;
3588 p = d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
3589 d->b[RANKTOBOARD (d->sp.row)][FILETOBOARD (d->sp.col)].icon = p;
3590 d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
3591 pgn_int_to_piece (gp->turn, OPEN_SQUARE);
3592 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3595 void
3596 do_edit_delete ()
3598 struct userdata_s *d = gp->data;
3600 if (d->sp.icon)
3601 d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
3602 pgn_int_to_piece (gp->turn, OPEN_SQUARE);
3603 else
3604 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon =
3605 pgn_int_to_piece (gp->turn, OPEN_SQUARE);
3607 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3610 void
3611 do_edit_cancel_selected ()
3613 struct userdata_s *d = gp->data;
3615 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3616 keycount = 0;
3617 update_status_notify (gp, NULL);
3620 void
3621 do_edit_switch_turn ()
3623 pgn_switch_turn (gp);
3626 void
3627 do_edit_toggle_castle ()
3629 struct userdata_s *d = gp->data;
3631 castling_state (gp, d->b, RANKTOBOARD (d->c_row),
3632 FILETOBOARD (d->c_col),
3633 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon,
3637 void
3638 do_edit_insert ()
3640 struct userdata_s *d = gp->data;
3642 construct_message (_("Insert Piece"),
3643 _("P=pawn, R=rook, N=knight, B=bishop, Q=queen, K=king"),
3644 0, 0, NULL, NULL, d->b, do_edit_insert_finalize, 0, 0,
3645 NULL, "%s",
3646 _("Type the piece letter to insert. Lowercase for a black piece, uppercase for a white piece."));
3649 void
3650 do_edit_enpassant ()
3652 struct userdata_s *d = gp->data;
3654 if (d->c_row == 6 || d->c_row == 3)
3656 int n = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].enpassant;
3658 pgn_reset_enpassant (d->b);
3659 if (!n)
3660 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].enpassant = 1;
3664 static void
3665 do_edit_help ()
3667 wchar_t *buf = build_help (edit_keys);
3669 construct_message (_("Edit Mode Keys (* = can take a repeat count)"),
3670 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
3671 0, 1, NULL, "%ls", buf);
3674 void
3675 do_edit_exit ()
3677 struct userdata_s *d = gp->data;
3678 char *fen = pgn_game_to_fen (gp, d->b);
3680 config.details--;
3681 pgn_tag_add (&gp->tag, (char *) "FEN", fen);
3682 free (fen);
3683 pgn_tag_add (&gp->tag, (char *) "SetUp", (char *) "1");
3684 pgn_tag_sort (gp->tag);
3685 pgn_board_update (gp, d->b, gp->hindex);
3686 d->mode = MODE_PLAY;
3689 void
3690 really_do_annotate_finalize (struct input_data_s *in, struct userdata_s *d)
3692 HISTORY *h = in->data;
3693 int len;
3695 if (!in->str)
3697 if (h->comment)
3699 free (h->comment);
3700 h->comment = NULL;
3703 else
3705 len = strlen (in->str);
3706 h->comment = Realloc (h->comment, len + 1);
3707 strncpy (h->comment, in->str, len);
3708 h->comment[len] = 0;
3711 free (in->str);
3712 free (in);
3713 SET_FLAG (d->flags, CF_MODIFIED);
3716 void
3717 do_annotate_finalize (WIN * win)
3719 struct userdata_s *d = gp->data;
3720 struct input_data_s *in = win->data;
3722 really_do_annotate_finalize (in, d);
3725 void
3726 do_find_move_exp_finalize (int init, int which)
3728 int n;
3729 struct userdata_s *d = gp->data;
3730 static int firstrun;
3731 static regex_t r;
3732 int ret;
3733 char errbuf[255];
3735 if (init || !firstrun)
3737 if (!firstrun)
3738 regfree (&r);
3740 if ((ret = regcomp (&r, moveexp, REG_EXTENDED | REG_NOSUB)) != 0)
3742 regerror (ret, &r, errbuf, sizeof (errbuf));
3743 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR,
3744 "%s", errbuf);
3745 return;
3748 firstrun = 1;
3751 if ((n = find_move_exp (gp, r,
3752 (which == -1) ? 0 : 1,
3753 (keycount) ? keycount : 1)) == -1)
3754 return;
3756 gp->hindex = n;
3757 pgn_board_update (gp, d->b, gp->hindex);
3760 void
3761 do_find_move_exp (WIN * win)
3763 struct input_data_s *in = win->data;
3764 int *n = in->data;
3765 int which = *n;
3767 if (in->str)
3769 strncpy (moveexp, in->str, sizeof (moveexp) - 1);
3770 moveexp[sizeof (moveexp) - 1] = 0;
3771 do_find_move_exp_finalize (1, which);
3772 free (in->str);
3775 free (in->data);
3776 free (in);
3779 void
3780 do_move_jump_finalize (int n)
3782 struct userdata_s *d = gp->data;
3784 if (n < 0 || n > (pgn_history_total (gp->hp) / 2))
3785 return;
3787 keycount = 0;
3788 update_status_notify (gp, NULL);
3789 gp->hindex = (n) ? n * 2 - 1 : n * 2;
3790 pgn_board_update (gp, d->b, gp->hindex);
3793 void
3794 do_move_jump (WIN * win)
3796 struct input_data_s *in = win->data;
3798 if (!in->str || !isinteger (in->str))
3800 if (in->str)
3801 free (in->str);
3803 free (in);
3804 return;
3807 do_move_jump_finalize (atoi (in->str));
3808 free (in->str);
3809 free (in);
3812 struct history_menu_s
3814 char *line;
3815 int hindex;
3816 int ravlevel;
3817 int move;
3818 int indent;
3821 void
3822 free_history_menu_data (struct history_menu_s **h)
3824 int i;
3826 if (!h)
3827 return;
3829 for (i = 0; h[i]; i++)
3831 free (h[i]->line);
3832 free (h[i]);
3835 free (h);
3838 void
3839 get_history_data (HISTORY ** hp, struct history_menu_s ***menu, int m,
3840 int turn)
3842 int i, n = 0;
3843 int t = pgn_history_total (hp);
3844 char buf[MAX_SAN_MOVE_LEN + 4];
3845 static int depth;
3846 struct history_menu_s **hmenu = *menu;
3848 if (hmenu)
3849 for (n = 0; hmenu[n]; n++);
3850 else
3851 depth = 0;
3853 for (i = 0; i < t; i++)
3855 hmenu = Realloc (hmenu, (n + 2) * sizeof (struct history_menu_s *));
3856 hmenu[n] = Malloc (sizeof (struct history_menu_s));
3857 snprintf (buf, sizeof (buf), "%c%s%s", (turn == WHITE) ? 'W' : 'B',
3858 hp[i]->move, (hp[i]->comment || hp[i]->nag[0]) ? " !" : "");
3859 hmenu[n]->line = strdup (buf);
3860 hmenu[n]->hindex = i;
3861 hmenu[n]->indent = 0;
3862 hmenu[n]->ravlevel = depth;
3863 hmenu[n]->move = (n && depth > hmenu[n - 1]->ravlevel) ? m++ : m;
3864 n++;
3865 hmenu[n] = NULL;
3867 #if 0
3868 if (hp[i]->rav)
3870 depth++;
3871 get_history_data (hp[i]->rav, &hmenu, m, turn);
3872 for (n = 0; hmenu[n]; n++);
3873 depth--;
3875 if (depth)
3876 m--;
3878 #endif
3880 turn = (turn == WHITE) ? BLACK : WHITE;
3883 *menu = hmenu;
3886 void
3887 history_draw_update (struct menu_input_s *m)
3889 GAME g = m->data;
3890 struct userdata_s *d = g->data;
3892 g->hindex = m->selected + 1;
3893 update_cursor (g, m->selected);
3894 pgn_board_update (g, d->b, m->selected + 1);
3897 struct menu_item_s **
3898 get_history_items (WIN * win)
3900 struct menu_input_s *m = win->data;
3901 GAME g = m->data;
3902 struct userdata_s *d = g->data;
3903 struct history_menu_s **hm = d->data;
3904 struct menu_item_s **items = m->items;
3905 int i;
3907 if (!hm)
3909 get_history_data (g->history, &hm, 0,
3910 TEST_FLAG (g->flags, GF_BLACK_OPENING));
3911 m->selected = g->hindex - 1;
3913 if (m->selected < 0)
3914 m->selected = 0;
3916 m->draw_exit_func = history_draw_update;
3919 d->data = hm;
3921 if (items)
3923 for (i = 0; items[i]; i++)
3924 free (items[i]);
3926 free (items);
3927 items = NULL;
3930 for (i = 0; hm[i]; i++)
3932 items = Realloc (items, (i + 2) * sizeof (struct menu_item_s *));
3933 items[i] = Malloc (sizeof (struct menu_item_s));
3934 items[i]->name = hm[i]->line;
3935 items[i]->value = NULL;
3936 items[i]->selected = 0;
3939 if (items)
3940 items[i] = NULL;
3942 m->nofree = 1;
3943 m->items = items;
3944 return items;
3947 void
3948 history_menu_quit (struct menu_input_s *m)
3950 pushkey = -1;
3953 void
3954 history_menu_exit (WIN * win)
3956 GAME g = win->data;
3957 struct userdata_s *d = g->data;
3958 struct history_menu_s **hm = d->data;
3959 int i;
3961 if (!hm)
3962 return;
3964 for (i = 0; hm[i]; i++)
3966 free (hm[i]->line);
3967 free (hm[i]);
3970 free (hm);
3971 d->data = NULL;
3974 // FIXME RAV
3975 void
3976 history_menu_next (struct menu_input_s *m)
3978 GAME g = m->data;
3979 struct userdata_s *d = g->data;
3980 struct history_menu_s **hm = d->data;
3981 int n, t;
3983 for (t = 0; hm[t]; t++);
3985 if (m->selected + 1 == t)
3986 n = 0;
3987 else
3988 n = hm[m->selected + 1]->hindex;
3990 n++;
3991 g->hindex = n;
3994 // FIXME RAV
3995 void
3996 history_menu_prev (struct menu_input_s *m)
3998 GAME g = m->data;
3999 struct userdata_s *d = g->data;
4000 struct history_menu_s **hm = d->data;
4001 int n, t;
4003 for (t = 0; hm[t]; t++);
4005 if (m->selected - 1 < 0)
4006 n = t - 1;
4007 else
4008 n = hm[m->selected - 1]->hindex;
4010 n++;
4011 g->hindex = n;
4014 void
4015 history_menu_help (struct menu_input_s *m)
4017 message (_("History Menu Help"), ANY_KEY_STR, "%s",
4018 _(" UP/DOWN - previous/next menu item\n"
4019 " HOME/END - first/last menu item\n"
4020 " PGDN/PGUP - next/previous page\n"
4021 " a-zA-Z0-9 - jump to item\n"
4022 " CTRL-a - annotate the selected move\n"
4023 " ENTER - view annotation\n"
4024 " CTRL-d - toggle board details\n"
4025 " ESCAPE/M - return to move history"));
4028 void
4029 do_annotate_move (HISTORY * hp)
4031 char buf[COLS - 4];
4032 struct input_data_s *in;
4034 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Editing Annotation for"),
4035 hp->move);
4036 in = Calloc (1, sizeof (struct input_data_s));
4037 in->data = hp;
4038 in->efunc = do_annotate_finalize;
4039 construct_input (buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
4040 _("Type CTRL-t to edit NAG"), edit_nag, NULL,
4041 CTRL_KEY ('T'), in, -1, NULL, -1);
4044 void
4045 history_menu_view_annotation (struct menu_input_s *m)
4047 GAME g = m->data;
4049 // FIXME RAV
4050 view_annotation (g->history[m->selected]);
4053 void
4054 history_menu_annotate_finalize (WIN * win)
4056 struct input_data_s *in = win->data;
4057 GAME g = in->moredata;
4058 struct userdata_s *d = g->data;
4059 struct history_menu_s **hm = d->data;
4061 really_do_annotate_finalize (in, d);
4062 free_history_menu_data (hm);
4063 hm = NULL;
4064 get_history_data (g->history, &hm, 0,
4065 TEST_FLAG (g->flags, GF_BLACK_OPENING));
4066 d->data = hm;
4067 pushkey = REFRESH_MENU;
4070 void
4071 history_menu_annotate (struct menu_input_s *m)
4073 GAME g = m->data;
4074 char buf[COLS - 4];
4075 struct input_data_s *in;
4076 HISTORY *hp = g->history[m->selected]; // FIXME RAV
4078 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Editing Annotation for"),
4079 hp->move);
4080 in = Calloc (1, sizeof (struct input_data_s));
4081 in->data = hp;
4082 in->moredata = m->data;
4083 in->efunc = history_menu_annotate_finalize;
4084 construct_input (buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
4085 _("Type CTRL-t to edit NAG"), edit_nag, NULL,
4086 CTRL_KEY ('T'), in, -1, NULL, -1);
4089 void
4090 history_menu_details (struct menu_input_s *m)
4092 do_board_details ();
4095 // FIXME RAV
4096 void
4097 history_menu_print (WIN * win)
4099 struct menu_input_s *m = win->data;
4100 GAME g = m->data;
4101 struct userdata_s *d = g->data;
4102 struct history_menu_s **hm = d->data;
4103 struct history_menu_s *h = hm[m->top];
4104 int i;
4105 char *p = m->item->name;
4106 int line = m->print_line - 2;
4108 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
4109 * attr_t data type.
4111 attr_t attrs;
4112 short pair;
4113 int total;
4115 for (total = 0; hm[total]; total++);
4116 wattr_get (win->w, &attrs, &pair, NULL);
4117 wattroff (win->w, COLOR_PAIR (pair));
4118 mvwaddch (win->w, m->print_line, 1,
4119 *p == 'W' ? *p | mix_cp (CP_BOARD_WHITE, CP_HISTORY_WINDOW,
4120 ATTRS (CP_BOARD_WHITE),
4121 A_FG_B_BG) : *p | mix_cp (CP_BOARD_BLACK,
4122 CP_HISTORY_WINDOW,
4123 ATTRS
4124 (CP_BOARD_BLACK),
4125 A_FG_B_BG));
4126 p++;
4128 if (h->hindex == 0 && line == 0)
4129 waddch (win->w, ACS_ULCORNER | CP_HISTORY_MENU_LG);
4130 else if ((!hm[h->hindex + (win->rows - 5) + 1] && line == win->rows - 5) ||
4131 (m->top + line == total - 1))
4132 waddch (win->w, ACS_LLCORNER | CP_HISTORY_MENU_LG);
4133 else if (hm[m->top + 1]->ravlevel != h->ravlevel || !h->ravlevel)
4134 waddch (win->w, ACS_LTEE | CP_HISTORY_MENU_LG);
4135 else
4136 waddch (win->w, ACS_VLINE | CP_HISTORY_MENU_LG);
4138 wattron (win->w, COLOR_PAIR (pair) | attrs);
4140 for (i = 2; *p; p++, i++)
4141 waddch (win->w, (*p == '!') ? *p | A_BOLD : *p);
4143 while (i++ < win->cols - 2)
4144 waddch (win->w, ' ');
4147 void
4148 history_menu (GAME g)
4150 struct menu_key_s **keys = NULL;
4152 add_menu_key (&keys, KEY_ESCAPE, history_menu_quit);
4153 add_menu_key (&keys, 'M', history_menu_quit);
4154 add_menu_key (&keys, KEY_UP, history_menu_prev);
4155 add_menu_key (&keys, KEY_DOWN, history_menu_next);
4156 add_menu_key (&keys, keycode_lookup (global_keys, do_global_help),
4157 history_menu_help);
4158 add_menu_key (&keys, CTRL_KEY ('a'), history_menu_annotate);
4159 add_menu_key (&keys, CTRL_KEY ('d'), history_menu_details);
4160 add_menu_key (&keys, '\n', history_menu_view_annotation);
4161 construct_menu (MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES, TAG_WIDTH,
4162 0, config.boardleft ? BOARD_WIDTH : 0,
4163 _("Move History Tree"), 1, get_history_items, keys, g,
4164 history_menu_print, history_menu_exit, history_menu_resize);
4167 void
4168 do_history_menu ()
4170 history_menu (gp);
4173 void
4174 do_history_half_move_toggle ()
4176 movestep = (movestep == 1) ? 2 : 1;
4177 update_history_window (gp);
4180 void
4181 do_history_rotate_board ()
4183 struct userdata_s *d = gp->data;
4184 d->rotate = !d->rotate;
4187 void
4188 do_history_jump_next ()
4190 struct userdata_s *d = gp->data;
4192 pgn_history_next (gp, d->b, (keycount > 0) ?
4193 config.jumpcount * keycount * movestep :
4194 config.jumpcount * movestep);
4197 void
4198 do_history_jump_prev ()
4200 struct userdata_s *d = gp->data;
4202 pgn_history_prev (gp, d->b, (keycount) ?
4203 config.jumpcount * keycount * movestep :
4204 config.jumpcount * movestep);
4207 void
4208 do_history_prev ()
4210 struct userdata_s *d = gp->data;
4212 pgn_history_prev (gp, d->b, (keycount) ? keycount * movestep : movestep);
4215 void
4216 do_history_next ()
4218 struct userdata_s *d = gp->data;
4220 pgn_history_next (gp, d->b, (keycount) ? keycount * movestep : movestep);
4223 void
4224 do_history_mode_finalize (struct userdata_s *d)
4226 pushkey = 0;
4227 d->mode = MODE_PLAY;
4230 void
4231 do_history_mode_confirm (WIN * win)
4233 struct userdata_s *d = gp->data;
4234 wchar_t str[] = { win->c, 0 };
4236 if (!wcscmp (str, resume_wchar))
4238 pgn_history_free (gp->hp, gp->hindex);
4239 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4241 #if 0
4242 case 'C':
4243 case 'c':
4244 if (pgn_history_rav_new (gp, d->b, gp->hindex) != E_PGN_OK)
4245 return;
4247 break;
4248 #endif
4249 else
4250 return;
4252 if (!TEST_FLAG (d->flags, CF_HUMAN))
4254 char *fen = pgn_game_to_fen (gp, d->b);
4256 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
4257 free (fen);
4260 do_history_mode_finalize (d);
4263 void
4264 do_history_toggle ()
4266 struct userdata_s *d = gp->data;
4268 // FIXME Resuming from previous history could append to a RAV.
4269 if (gp->hindex != pgn_history_total (gp->hp))
4271 if (!pushkey)
4272 construct_message (NULL, _("What would you like to do?"), 0, 1,
4273 NULL, NULL, NULL, do_history_mode_confirm, 0, 0,
4274 NULL, _
4275 ("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."),
4276 resume_wchar);
4277 return;
4279 else
4281 if (TEST_FLAG (gp->flags, GF_GAMEOVER))
4282 return;
4285 if (gp->side != gp->turn)
4287 d->play_mode = PLAY_EH;
4289 else
4291 d->play_mode = PLAY_HE;
4292 d->rotate = FALSE;
4295 do_history_mode_finalize (d);
4298 void
4299 do_history_annotate ()
4301 int n = gp->hindex;
4303 if (n && gp->hp[n - 1]->move)
4304 n--;
4305 else
4306 return;
4308 do_annotate_move (gp->hp[n]);
4311 static void
4312 do_history_help ()
4314 wchar_t *buf = build_help (history_keys);
4316 construct_message (_("History Mode Keys (* = can take a repeat count)"),
4317 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
4318 buf, do_more_help, 0, 1, NULL, "%ls", buf);
4321 void
4322 do_history_find (int which)
4324 struct input_data_s *in;
4325 int *p;
4327 if (pgn_history_total (gp->hp) < 2)
4328 return;
4330 in = Calloc (1, sizeof (struct input_data_s));
4331 p = Malloc (sizeof (int));
4332 *p = which;
4333 in->data = p;
4334 in->efunc = do_find_move_exp;
4336 if (!*moveexp || which == 0)
4338 construct_input (_("Find Move Text Expression"), NULL, 1, 0, NULL, NULL,
4339 NULL, 0, in, INPUT_HIST_MOVE_EXP, NULL, -1);
4340 return;
4343 free (p);
4344 free (in);
4345 do_find_move_exp_finalize (0, which);
4348 void
4349 do_history_find_new ()
4351 do_history_find (0);
4354 void
4355 do_history_find_prev ()
4357 do_history_find (-1);
4360 void
4361 do_history_find_next ()
4363 do_history_find (1);
4366 void
4367 do_history_rav (int which)
4369 struct userdata_s *d = gp->data;
4371 rav_next_prev (gp, d->b, which);
4374 void
4375 do_history_rav_next ()
4377 do_history_rav (1);
4380 void
4381 do_history_rav_prev ()
4383 do_history_rav (0);
4386 void
4387 do_history_jump ()
4389 struct input_data_s *in;
4391 if (pgn_history_total (gp->hp) < 2)
4392 return;
4394 if (!keycount)
4396 in = Calloc (1, sizeof (struct input_data_s));
4397 in->efunc = do_move_jump;
4399 construct_input (_("Jump to Move Number"), NULL, 1, 1, NULL,
4400 NULL, NULL, 0, in, -1, NULL, 0);
4401 return;
4404 do_move_jump_finalize (keycount);
4407 static void
4408 free_userdata_once (GAME g)
4410 struct userdata_s *d = g->data;
4412 if (!d)
4413 return;
4415 if (d->engine)
4417 stop_engine (g);
4419 if (d->engine->enginebuf)
4421 int n;
4423 for (n = 0; d->engine->enginebuf[n]; n++)
4424 free (d->engine->enginebuf[n]);
4426 free (d->engine->enginebuf);
4429 if (d->engine->queue)
4431 struct queue_s **q;
4433 for (q = d->engine->queue; *q; q++)
4434 free (*q);
4436 free (d->engine->queue);
4439 free (d->engine);
4442 #ifdef WITH_LIBPERL
4443 if (d->perlfen)
4444 free (d->perlfen);
4446 if (d->oldfen)
4447 free (d->oldfen);
4448 #endif
4450 free (d);
4451 g->data = NULL;
4454 static void
4455 free_userdata ()
4457 int i;
4459 for (i = 0; i < gtotal; i++)
4461 free_userdata_once (game[i]);
4462 game[i]->data = NULL;
4466 void
4467 update_loading_window (int n)
4469 char buf[16];
4471 if (!loadingw)
4473 loadingw = newwin (3, COLS / 2, CALCPOSY (3), CALCPOSX (COLS / 2));
4474 loadingp = new_panel (loadingw);
4475 wbkgd (loadingw, CP_MESSAGE_WINDOW);
4478 wmove (loadingw, 0, 0);
4479 wclrtobot (loadingw);
4480 wattron (loadingw, CP_MESSAGE_BORDER);
4481 box (loadingw, ACS_VLINE, ACS_HLINE);
4482 wattroff (loadingw, CP_MESSAGE_BORDER);
4483 mvwprintw (loadingw, 1, CENTER_INT ((COLS / 2), 11 +
4484 strlen (itoa (gtotal, buf))),
4485 _("Loading... %i%% (%i games)"), n, gtotal);
4486 update_panels ();
4487 doupdate ();
4490 static void
4491 init_userdata_once (GAME g, int n)
4493 struct userdata_s *d = NULL;
4495 d = Calloc (1, sizeof (struct userdata_s));
4496 d->n = n;
4497 d->c_row = 2, d->c_col = 5;
4498 SET_FLAG (d->flags, CF_NEW);
4499 g->data = d;
4501 if (pgn_board_init_fen (g, d->b, NULL) != E_PGN_OK)
4502 pgn_board_init (d->b);
4505 void
4506 init_userdata ()
4508 int i;
4510 for (i = 0; i < gtotal; i++)
4511 init_userdata_once (game[i], i);
4514 void
4515 fix_marks (int *start, int *end)
4517 int i;
4519 *start = (*start < 0) ? 0 : *start;
4520 *end = (*end < 0) ? 0 : *end;
4522 if (*start > *end)
4524 i = *start;
4525 *start = *end;
4526 *end = i + 1;
4529 *end = (*end > gtotal) ? gtotal : *end;
4532 void
4533 do_new_game_finalize (GAME g)
4535 struct userdata_s *d = g->data;
4537 d->mode = MODE_PLAY;
4538 update_status_notify (g, NULL);
4539 d->rotate = FALSE;
4540 d->go_move = 0;
4543 void
4544 do_new_game_from_scratch (WIN * win)
4546 wchar_t str[] = { win->c, 0 };
4548 if (wcscmp (str, yes_wchar))
4549 return;
4551 stop_clock ();
4552 free_userdata ();
4553 pgn_parse (NULL);
4554 gp = game[gindex];
4555 add_custom_tags (&gp->tag);
4556 init_userdata ();
4557 loadfile[0] = 0;
4558 do_new_game_finalize (gp);
4561 void
4562 do_new_game ()
4564 pgn_new_game ();
4565 gp = game[gindex];
4566 add_custom_tags (&gp->tag);
4567 init_userdata_once (gp, gindex);
4568 do_new_game_finalize (gp);
4571 void
4572 do_game_delete_finalize (int n)
4574 struct userdata_s *d;
4576 delete_game ((!n) ? gindex : -1);
4577 d = gp->data;
4578 if (d->mode != MODE_EDIT)
4579 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4582 void
4583 do_game_delete_confirm (WIN * win)
4585 int *n;
4586 wchar_t str[] = { win->c, 0 };
4588 if (wcscmp (str, yes_wchar))
4590 free (win->data);
4591 return;
4594 n = (int *) win->data;
4595 do_game_delete_finalize (*n);
4596 free (win->data);
4599 void
4600 do_game_delete ()
4602 char *tmp = NULL;
4603 int i, n;
4604 struct userdata_s *d;
4605 int *p;
4607 if (gtotal < 2)
4609 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4610 return;
4613 tmp = NULL;
4615 for (i = n = 0; i < gtotal; i++)
4617 d = game[i]->data;
4619 if (TEST_FLAG (d->flags, CF_DELETE))
4620 n++;
4623 if (!n)
4624 tmp = _("Delete the current game?");
4625 else
4627 if (n == gtotal)
4629 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4630 return;
4633 tmp = _("Delete all games marked for deletion?");
4636 if (config.deleteprompt)
4638 p = Malloc (sizeof (int));
4639 *p = n;
4640 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, p,
4641 do_game_delete_confirm, 0, 0, NULL, "%s", tmp);
4642 return;
4645 do_game_delete_finalize (n);
4648 void
4649 do_find_game_exp_finalize (int which)
4651 struct userdata_s *d = gp->data;
4652 int n;
4654 if ((n = find_game_exp (gameexp, (which == -1) ? 0 : 1,
4655 (keycount) ? keycount : 1)) == -1)
4657 update_status_notify (gp, "%s", _("No matches found"));
4658 return;
4661 gindex = n;
4662 d = gp->data;
4664 if (pgn_history_total (gp->hp))
4665 d->mode = MODE_HISTORY;
4667 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4670 void
4671 do_find_game_exp (WIN * win)
4673 struct input_data_s *in = win->data;
4674 int *n = in->data;
4675 int c = *n;
4677 if (in->str)
4679 strncpy (gameexp, in->str, sizeof (gameexp));
4680 gameexp[sizeof (gameexp) - 1] = 0;
4682 if (c == '?')
4683 c = '}';
4685 do_find_game_exp_finalize (c);
4686 free (in->str);
4689 free (in->data);
4690 free (in);
4693 void
4694 do_game_jump_finalize (int n)
4696 struct userdata_s *d;
4698 if (--n > gtotal - 1 || n < 0)
4699 return;
4701 gindex = n;
4702 gp = game[gindex];
4703 d = gp->data;
4704 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4705 update_status_notify (gp, NULL);
4708 void
4709 do_game_jump (WIN * win)
4711 struct input_data_s *in = win->data;
4713 if (!in->str || !isinteger (in->str))
4715 if (in->str)
4716 free (in->str);
4718 free (in);
4719 return;
4722 do_game_jump_finalize (atoi (in->str));
4723 free (in->str);
4724 free (in);
4727 void
4728 do_load_file (WIN * win)
4730 struct input_data_s *in = win->data;
4731 char *tmp = in->str;
4732 struct userdata_s *d;
4733 PGN_FILE *pgn = NULL;
4734 int n;
4736 if (!in->str)
4738 free (in);
4739 return;
4742 if ((tmp = pathfix (tmp)) == NULL)
4743 goto done;
4745 n = pgn_open (tmp, "r", &pgn);
4747 if (n == E_PGN_ERR)
4749 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, strerror (errno));
4750 goto done;
4752 else if (n == E_PGN_INVALID)
4754 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp,
4755 _("Not a regular file"));
4756 goto done;
4759 free_userdata ();
4761 if (pgn_parse (pgn) == E_PGN_ERR)
4763 del_panel (loadingp);
4764 delwin (loadingw);
4765 loadingw = NULL;
4766 loadingp = NULL;
4767 init_userdata ();
4768 goto done;
4771 del_panel (loadingp);
4772 delwin (loadingw);
4773 loadingw = NULL;
4774 loadingp = NULL;
4775 init_userdata ();
4776 strncpy (loadfile, tmp, sizeof (loadfile));
4777 loadfile[sizeof (loadfile) - 1] = 0;
4778 gp = game[gindex];
4779 d = gp->data;
4781 if (pgn_history_total (gp->hp))
4782 d->mode = MODE_HISTORY;
4784 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4786 fm_loaded_file = TRUE;
4787 d->rotate = FALSE;
4789 done:
4790 pgn_close (pgn);
4792 if (in->str)
4793 free (in->str);
4795 free (in);
4798 void
4799 do_game_save (WIN * win)
4801 struct input_data_s *in = win->data;
4802 int *x = in->data;
4803 int n = *x;
4804 char *tmp = in->str;
4805 char tfile[FILENAME_MAX];
4806 char *p;
4807 int i;
4808 struct userdata_s *d;
4810 if (!tmp || (tmp = pathfix (tmp)) == NULL)
4811 goto done;
4813 if (pgn_is_compressed (tmp) == E_PGN_ERR)
4815 p = tmp + strlen (tmp) - 1;
4817 if (*p != 'n' || *(p - 1) != 'g' || *(p - 2) != 'p' || *(p - 3) != '.')
4819 snprintf (tfile, sizeof (tfile), "%s.pgn", tmp);
4820 tmp = tfile;
4825 * When in edit mode, update the FEN tag.
4827 if (n == -1)
4829 for (i = 0; i < gtotal; i++)
4831 d = game[i]->data;
4833 if (d->mode == MODE_EDIT)
4835 char *fen = pgn_game_to_fen (game[i], d->b);
4837 pgn_tag_add (&game[i]->tag, (char *) "FEN", fen);
4838 free (fen);
4842 else
4844 d = game[n]->data;
4846 if (d->mode == MODE_EDIT)
4848 char *fen = pgn_game_to_fen (game[n], d->b);
4850 pgn_tag_add (&game[n]->tag, (char *) "FEN", fen);
4851 free (fen);
4855 save_pgn (tmp, n);
4857 done:
4858 if (in->str)
4859 free (in->str);
4861 free (in->data);
4862 free (in);
4865 void
4866 do_get_game_save_input (int n)
4868 struct input_data_s *in = Calloc (1, sizeof (struct input_data_s));
4869 int *p = Malloc (sizeof (int));
4871 in->efunc = do_game_save;
4872 *p = n;
4873 in->data = p;
4875 construct_input (_("Save Game Filename"), loadfile, 1, 1,
4876 _("Type TAB for file browser"), file_browser, NULL, '\t',
4877 in, INPUT_HIST_FILE, NULL, -1);
4880 void
4881 do_game_save_multi_confirm (WIN * win)
4883 int i;
4884 wchar_t str[] = { win->c, 0 };
4886 if (!wcscmp (str, current_wchar))
4887 i = gindex;
4888 else if (!wcscmp (str, all_wchar))
4889 i = -1;
4890 else
4892 update_status_notify (gp, "%s", _("Save game aborted."));
4893 return;
4896 do_get_game_save_input (i);
4899 void
4900 do_global_about ()
4902 cmessage (_("ABOUT"), ANY_KEY_STR,
4903 _("%s\nUsing %s with %i colors and %i color pairs\n%s\n%s"),
4904 PACKAGE_STRING, curses_version (), COLORS, COLOR_PAIRS,
4905 COPYRIGHT, CBOARD_URL);
4908 void
4909 global_game_next_prev (int which)
4911 struct userdata_s *d;
4913 game_next_prev (gp, (which == 1) ? 1 : 0, (keycount) ? keycount : 1);
4914 d = gp->data;
4916 if (delete_count)
4918 if (which == 1)
4920 markend = markstart + delete_count;
4921 delete_count = 0;
4923 else
4925 markend = markstart - delete_count + 1;
4926 delete_count = -1; // to fix gindex in the other direction
4929 fix_marks (&markstart, &markend);
4930 do_global_toggle_delete ();
4933 if (d->mode == MODE_HISTORY)
4934 pgn_board_update (gp, d->b, gp->hindex);
4935 else if (d->mode == MODE_PLAY)
4936 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4939 void
4940 do_global_next_game ()
4942 global_game_next_prev (1);
4945 void
4946 do_global_prev_game ()
4948 global_game_next_prev (0);
4951 void
4952 global_find (int which)
4954 struct input_data_s *in;
4955 int *p;
4957 if (gtotal < 2)
4958 return;
4960 in = Calloc (1, sizeof (struct input_data_s));
4961 p = Malloc (sizeof (int));
4962 *p = which;
4963 in->data = p;
4964 in->efunc = do_find_game_exp;
4966 if (!*gameexp || which == 0)
4968 construct_input (_("Find Game by Tag Expression"), NULL, 1, 0,
4969 _("[name expression:]value expression"), NULL, NULL, 0,
4970 in, INPUT_HIST_GAME_EXP, NULL, -1);
4971 return;
4974 free (p);
4975 free (in);
4976 do_find_game_exp_finalize (which);
4979 void
4980 do_global_find_new ()
4982 global_find (0);
4985 void
4986 do_global_find_next ()
4988 global_find (1);
4991 void
4992 do_global_find_prev ()
4994 global_find (-1);
4997 void
4998 do_global_game_jump ()
5000 if (gtotal < 2)
5001 return;
5003 if (!keycount)
5005 struct input_data_s *in;
5007 in = Calloc (1, sizeof (struct input_data_s));
5008 in->efunc = do_game_jump;
5009 construct_input (_("Jump to Game Number"), NULL, 1, 1, NULL, NULL, NULL,
5010 0, in, -1, NULL, 0);
5011 return;
5014 do_game_jump_finalize (keycount);
5017 void
5018 do_global_toggle_delete ()
5020 int i;
5022 pushkey = 0;
5024 if (gtotal < 2)
5025 return;
5027 if (keycount && delete_count == 0)
5029 markstart = gindex;
5030 delete_count = keycount;
5031 update_status_notify (gp, "%s (delete)", status.notify);
5032 return;
5035 if (markstart >= 0 && markend >= 0)
5037 for (i = markstart; i < markend; i++)
5039 if (toggle_delete_flag (i))
5041 return;
5045 gindex = (delete_count < 0) ? markstart : i - 1;
5047 else
5049 if (toggle_delete_flag (gindex))
5050 return;
5053 markstart = markend = -1;
5054 delete_count = 0;
5055 update_status_window (gp);
5058 void
5059 do_global_delete_game ()
5061 do_game_delete ();
5064 void
5065 do_global_tag_edit ()
5067 struct userdata_s *d = gp->data;
5069 edit_tags (gp, d->b, 1);
5072 void
5073 do_global_tag_view ()
5075 struct userdata_s *d = gp->data;
5077 edit_tags (gp, d->b, 0);
5080 void
5081 do_global_resume_game ()
5083 struct input_data_s *in;
5085 in = Calloc (1, sizeof (struct input_data_s));
5086 in->efunc = do_load_file;
5087 construct_input (_("Load Filename"), NULL, 1, 1,
5088 _("Type TAB for file browser"), file_browser, NULL, '\t',
5089 in, INPUT_HIST_FILE, NULL, -1);
5092 void
5093 do_global_save_game ()
5095 if (gtotal > 1)
5097 construct_message (NULL, _("What would you like to do?"), 0, 1,
5098 NULL, NULL, NULL, do_game_save_multi_confirm, 0, 0,
5099 NULL, _
5100 ("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."),
5101 current_wchar, all_wchar);
5102 return;
5105 do_get_game_save_input (-1);
5108 void
5109 do_global_new_game ()
5111 do_new_game ();
5114 void
5115 copy_game_common (int fen)
5117 int g = gindex;
5118 int i, n;
5119 struct userdata_s *d = gp->data;
5120 char *fentag = fen ? pgn_game_to_fen (gp, d->b) : NULL;
5122 do_global_new_game ();
5123 d = gp->data;
5124 n = pgn_tag_total (game[g]->tag);
5126 for (i = 0; i < n; i++)
5127 pgn_tag_add (&gp->tag, game[g]->tag[i]->name, game[g]->tag[i]->value);
5129 if (fentag)
5131 pgn_tag_add (&gp->tag, (char *) "SetUp", (char *) "1");
5132 pgn_tag_add (&gp->tag, (char *) "FEN", fentag);
5133 free (fentag);
5134 pgn_tag_sort (gp->tag);
5136 else
5138 pgn_board_init_fen (gp, d->b, NULL);
5139 n = pgn_history_total (game[g]->history);
5141 // FIXME RAV
5142 for (i = 0; i < n; i++)
5144 char *frfr = NULL;
5145 char *move = strdup (game[g]->history[i]->move);
5147 if (pgn_parse_move (gp, d->b, &move, &frfr) != E_PGN_OK)
5149 free (move);
5150 SET_FLAG (gp->flags, GF_PERROR);
5151 return;
5154 pgn_history_add (gp, d->b, move);
5155 free (move);
5156 free (frfr);
5157 pgn_switch_turn (gp);
5161 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
5164 void
5165 do_global_copy_game ()
5167 copy_game_common (0);
5170 void
5171 do_global_copy_game_fen ()
5173 copy_game_common (1);
5176 void
5177 do_global_new_all ()
5179 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
5180 do_new_game_from_scratch, 0, 0, NULL, "%s",
5181 _("Really start a new game from scratch?"));
5184 void
5185 do_quit (WIN * win)
5187 wchar_t str[] = { win->c, 0 };
5188 int n = wcscmp (str, yes_wchar);
5190 if (n)
5191 return;
5193 quit = 1;
5196 void
5197 do_global_quit ()
5199 if (config.exitdialogbox)
5200 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
5201 do_quit, 0, 0, NULL, "%s", _("Want to Quit?"));
5202 else
5203 quit = 1;
5206 void
5207 do_global_toggle_engine_window ()
5209 if (!enginew)
5211 enginew = newwin (LINES, COLS, 0, 0);
5212 enginep = new_panel (enginew);
5213 window_draw_title (enginew, _("Engine IO Window"), COLS,
5214 CP_MESSAGE_TITLE, CP_MESSAGE_BORDER);
5215 hide_panel (enginep);
5218 if (panel_hidden (enginep))
5220 update_engine_window (gp);
5221 top_panel (enginep);
5223 else
5225 hide_panel (enginep);
5229 void
5230 do_global_toggle_board_details ()
5232 do_board_details ();
5235 void
5236 do_play_toggle_strict_castling ()
5238 do_toggle_strict_castling ();
5241 // Global and other keys.
5242 static int
5243 globalkeys ()
5245 struct userdata_s *d = gp->data;
5246 int i;
5249 * These cannot be modified and other game mode keys cannot conflict with
5250 * these.
5252 switch (input_c)
5254 case KEY_ESCAPE:
5255 d->sp.icon = d->sp.srow = d->sp.scol = 0;
5256 markend = markstart = 0;
5258 if (keycount)
5260 keycount = 0;
5261 update_status_notify (gp, NULL);
5264 if (config.validmoves)
5265 pgn_reset_valid_moves (d->b);
5267 return 1;
5268 case '0' ... '9':
5269 i = input_c - '0';
5271 if (keycount)
5272 keycount = keycount * 10 + i;
5273 else
5274 keycount = i;
5276 update_status_notify (gp, _("Repeat %i"), keycount);
5277 return -1;
5278 case KEY_UP:
5279 if (d->mode == MODE_HISTORY)
5280 return 0;
5282 if (keycount)
5283 d->c_row += keycount;
5284 else
5285 d->c_row++;
5287 if (d->c_row > 8)
5288 d->c_row = 1;
5290 return 1;
5291 case KEY_DOWN:
5292 if (d->mode == MODE_HISTORY)
5293 return 0;
5295 if (keycount)
5297 d->c_row -= keycount;
5298 update_status_notify (gp, NULL);
5300 else
5301 d->c_row--;
5303 if (d->c_row < 1)
5304 d->c_row = 8;
5306 return 1;
5307 case KEY_LEFT:
5308 if (d->mode == MODE_HISTORY)
5309 return 0;
5311 if (keycount)
5312 d->c_col -= keycount;
5313 else
5314 d->c_col--;
5316 if (d->c_col < 1)
5317 d->c_col = 8;
5319 return 1;
5320 case KEY_RIGHT:
5321 if (d->mode == MODE_HISTORY)
5322 return 0;
5324 if (keycount)
5325 d->c_col += keycount;
5326 else
5327 d->c_col++;
5329 if (d->c_col > 8)
5330 d->c_col = 1;
5332 return 1;
5333 case KEY_RESIZE:
5334 return 1;
5335 case 0:
5336 default:
5337 for (i = 0; global_keys[i]; i++)
5339 if (input_c == global_keys[i]->c && global_keys[i]->f)
5341 (*global_keys[i]->f) ();
5342 return 1;
5345 break;
5348 return 0;
5351 #ifdef WITH_LIBPERL
5352 static void
5353 perl_error (const char *fmt, ...)
5355 va_list ap;
5356 char *buf;
5358 va_start (ap, fmt);
5359 vasprintf (&buf, fmt, ap);
5360 va_end (ap);
5362 message (ERROR_STR, ANY_KEY_STR, "%s", buf);
5363 free (buf);
5366 static void
5367 do_perl_finalize (WIN * win)
5369 struct input_data_s *in = win->data;
5370 GAME g = in->data;
5371 struct userdata_s *d = g->data;
5372 char *filename;
5373 char *result = NULL;
5374 char *arg = NULL;
5375 int n;
5377 asprintf (&filename, "%s/perl.pl", config.datadir);
5379 if (!in->str)
5380 goto done;
5382 if (perl_init_file (filename, perl_error))
5383 goto done;
5385 arg = pgn_game_to_fen (g, d->b);
5387 if (perl_call_sub (trim (in->str), arg, &result))
5388 goto done;
5390 d->perlfen = pgn_game_to_fen (g, d->b);
5391 d->perlflags = g->flags;
5393 if (pgn_board_init_fen (g, d->b, result) != E_PGN_OK)
5395 message (ERROR_STR, ANY_KEY_STR, "%s", _("FEN parse error."));
5396 pgn_board_init_fen (g, d->b, d->perlfen);
5397 g->flags = d->perlflags;
5398 free (d->perlfen);
5399 d->perlfen = NULL;
5400 goto done;
5403 SET_FLAG (d->flags, CF_PERL);
5404 n = pgn_tag_find (g->tag, "FEN");
5406 if (n != E_PGN_ERR)
5407 d->oldfen = strdup (g->tag[n]->value);
5409 pgn_tag_add (&g->tag, (char *) "FEN", result);
5410 update_status_notify (g, "%s", ANY_KEY_STR);
5411 update_all (g);
5413 done:
5414 free (result);
5415 free (arg);
5416 free (in->str);
5417 free (in);
5418 free (filename);
5421 void
5422 do_global_perl ()
5424 struct input_data_s *in;
5426 in = Calloc (1, sizeof (struct input_data_s));
5427 in->data = gp;
5428 in->efunc = do_perl_finalize;
5429 construct_input (_("PERL Subroutine Filter"), NULL, 1, 0, NULL, NULL, NULL,
5430 0, in, INPUT_HIST_PERL, NULL, -1);
5432 #endif
5435 * A macro may contain a key that belongs to another macro so macro_match will
5436 * need to be updated to the new index of the matching macro.
5438 static void
5439 find_macro (struct userdata_s *d)
5441 int i;
5444 * Macros can't contain macros when in a window.
5446 if (wins)
5447 return;
5449 again:
5450 for (i = 0; macros[i]; i++)
5452 if ((macros[i]->mode == -1 || macros[i]->mode == d->mode) &&
5453 input_c == macros[i]->c)
5455 input_c = macros[i]->keys[macros[i]->n++];
5457 if (!macro_depth_n && macro_match > -1)
5459 macro_depth = Realloc (macro_depth, (macro_depth_n + 1) * sizeof (int));
5460 macro_depth[macro_depth_n++] = macro_match;
5463 macro_depth = Realloc (macro_depth, (macro_depth_n + 1) * sizeof (int));
5464 macro_depth[macro_depth_n++] = i;
5465 macro_match = i;
5466 goto again;
5472 * Resets the position in each macro to the first key.
5474 static void
5475 reset_macros ()
5477 int i;
5478 struct userdata_s *d = gp->data;
5480 again:
5481 if (macro_depth_n > 0)
5483 macro_depth_n--;
5484 macro_match = macro_depth[macro_depth_n];
5486 if (macros[macro_match]->n >= macros[macro_match]->total)
5487 goto again;
5489 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
5490 find_macro (d);
5491 return;
5494 for (i = 0; macros[i]; i++)
5495 macros[i]->n = 0;
5497 free (macro_depth);
5498 macro_depth = NULL;
5499 macro_depth_n = 0;
5500 macro_match = -1;
5503 void
5504 game_loop ()
5506 struct userdata_s *d;
5508 macro_match = -1;
5509 gindex = gtotal - 1;
5510 gp = game[gindex];
5511 d = gp->data;
5513 if (pgn_history_total (gp->hp))
5514 d->mode = MODE_HISTORY;
5515 else
5517 d->mode = MODE_PLAY;
5518 d->play_mode = PLAY_HE;
5521 d->rotate = FALSE;
5522 d->go_move = 0;
5523 d->pm_undo = FALSE;
5524 d->pm_frfr[0] = '\0';
5526 if (d->mode == MODE_HISTORY)
5527 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
5529 update_status_notify (gp, _("Type %ls for help"),
5530 key_lookup (global_keys, do_global_help));
5531 movestep = 2;
5532 flushinp ();
5533 update_all (gp);
5534 wtimeout (boardw, WINDOW_TIMEOUT);
5536 while (!quit)
5538 int n = 0, i;
5539 char fdbuf[8192] = { 0 };
5540 int len;
5541 struct timeval tv = { 0, 0 };
5542 fd_set rfds, wfds;
5543 WIN *win = NULL;
5544 WINDOW *wp = NULL;
5546 FD_ZERO (&rfds);
5547 FD_ZERO (&wfds);
5549 for (i = 0; i < gtotal; i++)
5551 d = game[i]->data;
5553 if (d->engine && d->engine->pid != -1)
5555 if (d->engine->fd[ENGINE_IN_FD] > 2)
5557 if (d->engine->fd[ENGINE_IN_FD] > n)
5558 n = d->engine->fd[ENGINE_IN_FD];
5560 FD_SET (d->engine->fd[ENGINE_IN_FD], &rfds);
5563 if (d->engine->fd[ENGINE_OUT_FD] > 2)
5565 if (d->engine->fd[ENGINE_OUT_FD] > n)
5566 n = d->engine->fd[ENGINE_OUT_FD];
5568 FD_SET (d->engine->fd[ENGINE_OUT_FD], &wfds);
5573 if (n)
5575 if ((n = select (n + 1, &rfds, &wfds, NULL, &tv)) > 0)
5577 for (i = 0; i < gtotal; i++)
5579 d = game[i]->data;
5581 if (d->engine && d->engine->pid != -1)
5583 if (FD_ISSET (d->engine->fd[ENGINE_IN_FD], &rfds))
5585 len = read (d->engine->fd[ENGINE_IN_FD], fdbuf,
5586 sizeof (fdbuf));
5588 if (len > 0)
5590 if (d->engine->iobuf)
5591 d->engine->iobuf =
5592 Realloc (d->engine->iobuf,
5593 d->engine->len + len + 1);
5594 else
5595 d->engine->iobuf = Calloc (1, len + 1);
5597 memcpy (&(d->engine->iobuf[d->engine->len]),
5598 &fdbuf, len);
5599 d->engine->len += len;
5600 d->engine->iobuf[d->engine->len] = 0;
5603 * The fdbuf is full or no newline
5604 * was found. So we'll append the next
5605 * read() to this games buffer.
5607 if (d->engine->iobuf[d->engine->len - 1] !=
5608 '\n')
5609 continue;
5611 parse_engine_output (game[i], d->engine->iobuf);
5612 free (d->engine->iobuf);
5613 d->engine->iobuf = NULL;
5614 d->engine->len = 0;
5616 else if (len == -1)
5618 if (errno != EAGAIN)
5620 cmessage (ERROR_STR, ANY_KEY_STR,
5621 "Engine read(): %s",
5622 strerror (errno));
5623 waitpid (d->engine->pid, &n, 0);
5624 free (d->engine);
5625 d->engine = NULL;
5626 break;
5631 if (FD_ISSET (d->engine->fd[ENGINE_OUT_FD], &wfds))
5633 if (d->engine->queue)
5634 send_engine_command (game[i]);
5639 else
5641 if (n == -1)
5642 cmessage (ERROR_STR, ANY_KEY_STR, "select(): %s",
5643 strerror (errno));
5644 /* timeout */
5648 gp = game[gindex];
5649 d = gp->data;
5652 * This is needed to detect terminal resizing.
5654 doupdate ();
5655 if (LINES != LINES_OLD || COLS != COLS_OLD)
5657 COLS_OLD = COLS;
5658 LINES_OLD = LINES;
5659 do_window_resize ();
5663 * Finds the top level window in the window stack so we know what
5664 * window the wget_wch()'ed key belongs to.
5666 if (wins)
5668 for (i = 0; wins[i]; i++);
5669 win = wins[i - 1];
5670 wp = win->w;
5671 wtimeout (wp, WINDOW_TIMEOUT);
5673 else
5674 wp = boardw;
5676 if (!i && pushkey)
5677 input_c = pushkey;
5678 else
5680 if (!pushkey)
5682 if (macros && macro_match >= 0)
5684 if (macros[macro_match]->n >= macros[macro_match]->total)
5686 reset_macros ();
5687 goto refresh;
5689 else
5691 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
5692 find_macro (d);
5695 else
5697 if (wget_wch (wp, &input_c) == ERR || input_c == KEY_RESIZE)
5699 if (input_c == KEY_RESIZE)
5701 if (win)
5702 win->c = input_c;
5704 window_resize_all ();
5705 update_all (gp);
5707 continue;
5711 else
5712 input_c = pushkey;
5714 if (win)
5716 win->c = input_c;
5719 * Run the function associated with the window. When the
5720 * function returns 0 win->efunc is ran (if not NULL) with
5721 * win as the one and only parameter. Then the window is
5722 * destroyed.
5724 * The exit function may create another window which will
5725 * mess up the window stack when window_destroy() is called.
5726 * So don't destory the window until the top window is
5727 * destroyable. See window_destroy().
5729 if ((*win->func) (win) == 0)
5731 if (win->efunc)
5732 (*win->efunc) (win);
5734 win->keep = 1;
5735 window_destroy (win);
5736 update_all (gp);
5739 continue;
5743 if (!keycount && status.notify)
5744 update_status_notify (gp, NULL);
5746 #ifdef WITH_LIBPERL
5747 if (TEST_FLAG (d->flags, CF_PERL))
5749 CLEAR_FLAG (d->flags, CF_PERL);
5750 pgn_board_init_fen (gp, d->b, d->perlfen);
5751 gp->flags = d->perlflags;
5752 free (d->perlfen);
5753 pgn_tag_add (&gp->tag, (char *) "FEN", d->oldfen);
5754 free (d->oldfen);
5755 d->perlfen = d->oldfen = NULL;
5756 update_all (gp);
5757 continue;
5759 #endif
5761 if (macros && macro_match < 0)
5762 find_macro (d);
5764 if ((n = globalkeys ()) == 1)
5766 if (macro_match == -1)
5767 keycount = 0;
5769 goto refresh;
5771 else if (n == -1)
5772 goto refresh;
5774 switch (d->mode)
5776 case MODE_EDIT:
5777 for (i = 0; edit_keys[i]; i++)
5779 if (input_c == edit_keys[i]->c)
5781 (*edit_keys[i]->f) ();
5782 break;
5785 break;
5786 case MODE_PLAY:
5787 for (i = 0; play_keys[i]; i++)
5789 if (input_c == play_keys[i]->c)
5791 (*play_keys[i]->f) ();
5792 goto done;
5796 do_play_config_command ();
5797 break;
5798 case MODE_HISTORY:
5799 for (i = 0; history_keys[i]; i++)
5801 if (input_c == history_keys[i]->c)
5803 (*history_keys[i]->f) ();
5804 break;
5807 break;
5808 default:
5809 break;
5812 done:
5813 if (keycount)
5814 update_status_notify (gp, NULL);
5816 keycount = 0;
5818 refresh:
5819 update_all (gp);
5823 void
5824 usage (const char *pn, int ret)
5826 fprintf ((ret) ? stderr : stdout, "%s%s",
5827 #ifdef DEBUG
5828 _("Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
5829 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"),
5830 #else
5831 _("Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"),
5832 #endif
5833 _(" -p Load PGN file.\n"
5834 " -V Validate a game file.\n"
5835 " -S Validate and output a PGN formatted game.\n"
5836 " -R Like -S but write a reduced PGN formatted game.\n"
5837 " -t Also write custom PGN tags from config file.\n"
5838 " -E Stop processing on file parsing error (overrides config).\n"
5839 " -C Enable strict castling (overrides config).\n"
5840 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, overrides config).\n"
5841 " -v Version information.\n" " -h This help text.\n"));
5843 exit (ret);
5846 void
5847 cleanup_all ()
5849 int i;
5851 stop_clock ();
5852 free_userdata ();
5853 pgn_free_all ();
5854 free (config.engine_cmd);
5855 free (config.pattern);
5856 free (config.ccfile);
5857 free (config.nagfile);
5858 free (config.configfile);
5860 if (config.keys)
5862 for (i = 0; config.keys[i]; i++)
5864 free (config.keys[i]->str);
5865 free (config.keys[i]);
5868 free (config.keys);
5871 if (config.einit)
5873 for (i = 0; config.einit[i]; i++)
5874 free (config.einit[i]);
5876 free (config.einit);
5879 if (config.tag)
5880 pgn_tag_free (config.tag);
5882 free (config.datadir);
5884 if (curses_initialized)
5886 del_panel (boardp);
5887 del_panel (historyp);
5888 del_panel (statusp);
5889 del_panel (tagp);
5890 delwin (boardw);
5891 delwin (historyw);
5892 delwin (statusw);
5893 delwin (tagw);
5895 if (enginew)
5897 del_panel (enginep);
5898 delwin (enginew);
5901 endwin ();
5904 #ifdef WITH_LIBPERL
5905 perl_cleanup ();
5906 #endif
5909 static void
5910 signal_save_pgn (int sig)
5912 char *buf;
5913 time_t now;
5914 char *p = config.savedirectory ? config.savedirectory : config.datadir;
5916 time (&now);
5917 asprintf (&buf, "%s/signal-%i-%li.pgn", p, sig, now);
5919 if (do_game_write (buf, "w", 0, gtotal))
5921 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", p, strerror (errno));
5922 update_status_notify (gp, "%s", _("Save game failed."));
5925 free (buf);
5926 quit = 1;
5929 void
5930 catch_signal (int which, siginfo_t *info, void *ctx)
5932 (void)info;
5933 (void)ctx;
5935 switch (which)
5937 case SIGALRM:
5938 update_clocks ();
5939 break;
5940 case SIGPIPE:
5941 if (which == SIGPIPE && quit)
5942 break;
5944 if (which == SIGPIPE)
5945 cmessage (NULL, ANY_KEY_STR, "%s", _("Broken pipe. Quitting."));
5947 cleanup_all ();
5948 exit (EXIT_FAILURE);
5949 break;
5950 case SIGSTOP:
5951 savetty ();
5952 break;
5953 case SIGCONT:
5954 resetty ();
5955 do_window_resize ();
5956 keypad (boardw, TRUE);
5957 break;
5958 case SIGINT:
5959 quit = 1;
5960 break;
5961 case SIGTERM:
5962 signal_save_pgn (which);
5963 break;
5964 default:
5965 break;
5969 void
5970 loading_progress (long total, long offset)
5972 int n = (100 * (offset / 100) / (total / 100));
5974 if (curses_initialized)
5975 update_loading_window (n);
5976 else
5978 fprintf (stderr, _("Loading... %i%% (%i games)%c"), n, gtotal, '\r');
5979 fflush (stderr);
5983 static void
5984 set_defaults ()
5986 set_config_defaults ();
5987 set_default_keys ();
5988 filetype = FILE_NONE;
5989 pgn_config_set (PGN_PROGRESS, 1024);
5990 pgn_config_set (PGN_PROGRESS_FUNC, loading_progress);
5994 main (int argc, char *argv[])
5996 int opt;
5997 struct stat st;
5998 char buf[FILENAME_MAX];
5999 char datadir[FILENAME_MAX];
6000 int ret = EXIT_SUCCESS;
6001 int validate_only = 0, validate_and_write = 0;
6002 int write_custom_tags = 0;
6003 int i = 0;
6004 PGN_FILE *pgn;
6005 int utf8_pieces = -1;
6006 struct sigaction sigact;
6008 setlocale (LC_ALL, "");
6009 bindtextdomain ("cboard", LOCALE_DIR);
6010 textdomain ("cboard");
6012 /* Solaris 5.9 */
6013 #ifndef HAVE_PROGNAME
6014 __progname = argv[0];
6015 #endif
6017 if ((config.pwd = getpwuid (getuid ())) == NULL)
6018 err (EXIT_FAILURE, "getpwuid()");
6020 snprintf (datadir, sizeof (datadir), "%s/.cboard", config.pwd->pw_dir);
6021 config.datadir = strdup (datadir);
6022 snprintf (buf, sizeof (buf), "%s/cc.data", datadir);
6023 config.ccfile = strdup (buf);
6024 snprintf (buf, sizeof (buf), "%s/nag.data", datadir);
6025 config.nagfile = strdup (buf);
6026 snprintf (buf, sizeof (buf), "%s/config", datadir);
6027 config.configfile = strdup (buf);
6029 if (stat (datadir, &st) == -1)
6031 if (errno == ENOENT)
6033 if (mkdir (datadir, 0755) == -1)
6034 err (EXIT_FAILURE, "%s", datadir);
6036 else
6037 err (EXIT_FAILURE, "%s", datadir);
6039 stat (datadir, &st);
6042 if (!S_ISDIR (st.st_mode))
6043 errx (EXIT_FAILURE, "%s: %s", datadir, _("Not a directory."));
6045 set_defaults ();
6047 #ifdef DEBUG
6048 while ((opt = getopt (argc, argv, "DCEVtSRhp:vu::")) != -1)
6050 #else
6051 while ((opt = getopt (argc, argv, "ECVtSRhp:vu::")) != -1)
6053 #endif
6054 switch (opt)
6056 #ifdef DEBUG
6057 case 'D':
6058 unlink ("libchess.debug");
6059 pgn_config_set (PGN_DEBUG, 1);
6060 break;
6061 #endif
6062 case 'C':
6063 pgn_config_set (PGN_STRICT_CASTLING, 1);
6064 break;
6065 case 't':
6066 write_custom_tags = 1;
6067 break;
6068 case 'E':
6069 i = 1;
6070 break;
6071 case 'R':
6072 pgn_config_set (PGN_REDUCED, 1);
6073 case 'S':
6074 validate_and_write = 1;
6075 case 'V':
6076 validate_only = 1;
6077 break;
6078 case 'v':
6079 printf ("%s (%s)\n%s\n%s\n", PACKAGE_STRING, curses_version (),
6080 COPYRIGHT, CBOARD_URL);
6081 exit (EXIT_SUCCESS);
6082 case 'p':
6083 filetype = FILE_PGN;
6084 strncpy (loadfile, optarg, sizeof (loadfile));
6085 loadfile[sizeof (loadfile) - 1] = 0;
6086 break;
6087 case 'u':
6088 utf8_pieces = optarg ? atoi (optarg) : 1;
6089 break;
6090 case 'h':
6091 default:
6092 usage (argv[0], EXIT_SUCCESS);
6096 if ((validate_only || validate_and_write) && !*loadfile)
6097 usage (argv[0], EXIT_FAILURE);
6099 if (access (config.configfile, R_OK) == 0)
6100 parse_rcfile (config.configfile);
6102 if (i)
6103 pgn_config_set (PGN_STOP_ON_ERROR, 1);
6105 memset (&sigact, 0, sizeof (sigact));
6106 sigact.sa_flags = SA_SIGINFO;
6107 sigact.sa_sigaction = catch_signal;
6108 sigaction (SIGPIPE, &sigact, NULL);
6109 sigaction (SIGCONT, &sigact, NULL);
6110 sigaction (SIGSTOP, &sigact, NULL);
6111 sigaction (SIGINT, &sigact, NULL);
6112 sigaction (SIGALRM, &sigact, NULL);
6113 sigaction (SIGTERM, &sigact, NULL);
6114 signal (SIGCHLD, SIG_IGN);
6116 srandom (getpid ());
6118 switch (filetype)
6120 case FILE_PGN:
6121 if (pgn_open (loadfile, "r", &pgn) != E_PGN_OK)
6122 err (EXIT_FAILURE, "%s", loadfile);
6124 ret = pgn_parse (pgn);
6125 pgn_close (pgn);
6126 break;
6127 case FILE_FEN:
6128 //ret = parse_fen_file(loadfile);
6129 break;
6130 case FILE_EPD: // Not implemented.
6131 case FILE_NONE:
6132 default:
6133 // No file specified. Empty game.
6134 ret = pgn_parse (NULL);
6135 gp = game[gindex];
6136 add_custom_tags (&gp->tag);
6137 break;
6140 if (validate_only || validate_and_write)
6142 if (validate_and_write)
6144 if (pgn_open ("-", "r", &pgn) != E_PGN_OK)
6145 err (EXIT_FAILURE, "pgn_open()");
6147 for (i = 0; i < gtotal; i++)
6149 if (write_custom_tags)
6150 add_custom_tags (&game[i]->tag);
6152 pgn_write (pgn, game[i]);
6155 pgn_close (pgn);
6157 fm_loaded_file = TRUE;
6160 cleanup_all ();
6161 exit (ret);
6163 else if (ret == E_PGN_ERR)
6164 exit (ret);
6166 if (utf8_pieces != -1)
6167 config.utf8_pieces = utf8_pieces;
6169 init_wchar_pieces ();
6170 yes_wchar = str_to_wchar (_("y"));
6171 all_wchar = str_to_wchar (_("a"));
6172 overwrite_wchar = str_to_wchar (_("o"));
6173 resume_wchar = str_to_wchar (_("r"));
6174 current_wchar = str_to_wchar (_("c"));
6175 append_wchar = str_to_wchar (_("a"));
6176 translatable_tag_names[0] = _("Event");
6177 translatable_tag_names[1] = _("Site");
6178 translatable_tag_names[2] = _("Date");
6179 translatable_tag_names[3] = _("Round");
6180 translatable_tag_names[4] = _("White");
6181 translatable_tag_names[5] = _("Black");
6182 translatable_tag_names[6] = _("Result");
6183 init_userdata ();
6186 * This fixes window resizing in an xterm.
6188 if (getenv ("DISPLAY") != NULL)
6190 putenv ((char *) "LINES=");
6191 putenv ((char *) "COLUMNS=");
6194 if (initscr () == NULL)
6195 errx (EXIT_FAILURE, "%s", _("Could not initialize curses."));
6196 else
6197 curses_initialized = 1;
6199 if (LINES < 23 || COLS < 74)
6201 endwin ();
6202 errx (EXIT_FAILURE, _("Need at least an 74x23 terminal."));
6205 COLS_OLD = COLS;
6206 LINES_OLD = LINES;
6208 if (has_colors () == TRUE && start_color () == OK)
6209 init_color_pairs ();
6211 boardw = newwin (BOARD_HEIGHT, BOARD_WIDTH, 0, COLS - BOARD_WIDTH);
6212 boardp = new_panel (boardw);
6213 historyw = newwin (HISTORY_HEIGHT, HISTORY_WIDTH, LINES - HISTORY_HEIGHT,
6214 COLS - HISTORY_WIDTH);
6215 historyp = new_panel (historyw);
6216 statusw = newwin (STATUS_HEIGHT, STATUS_WIDTH, 0, 0);
6217 statusp = new_panel (statusw);
6218 tagw = newwin (TAG_HEIGHT, TAG_WIDTH, STATUS_HEIGHT + 1, 0);
6219 tagp = new_panel (tagw);
6220 keypad (boardw, TRUE);
6221 // leaveok(boardw, TRUE);
6222 leaveok (tagw, TRUE);
6223 leaveok (statusw, TRUE);
6224 leaveok (historyw, TRUE);
6225 curs_set (0);
6226 cbreak ();
6227 noecho ();
6228 draw_window_decor ();
6229 game_loop ();
6230 cleanup_all ();
6231 free (w_pawn_wchar);
6232 free (w_rook_wchar);
6233 free (w_bishop_wchar);
6234 free (w_knight_wchar);
6235 free (w_queen_wchar);
6236 free (w_king_wchar);
6237 free (b_pawn_wchar);
6238 free (b_rook_wchar);
6239 free (b_bishop_wchar);
6240 free (b_knight_wchar);
6241 free (b_queen_wchar);
6242 free (b_king_wchar);
6243 free (empty_wchar);
6244 free (enpassant_wchar);
6245 free (yes_wchar);
6246 free (all_wchar);
6247 free (overwrite_wchar);
6248 free (resume_wchar);
6249 free (current_wchar);
6250 free (append_wchar);
6251 free (status.notify);
6252 exit (EXIT_SUCCESS);