Add copy_game_fen.
[cboard.git] / src / cboard.c
blobf4a4ca797f990560de4852c54bba2fdeda3b5611
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2018 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <pwd.h>
33 #include <signal.h>
34 #include <time.h>
35 #include <err.h>
36 #include <locale.h>
38 #ifdef HAVE_STDARG_H
39 #include <stdarg.h>
40 #endif
42 #ifdef HAVE_SYS_WAIT_H
43 #include <sys/wait.h>
44 #endif
46 #ifdef HAVE_REGEX_H
47 #include <regex.h>
48 #endif
50 #ifdef WITH_LIBPERL
51 #include "perl-plugin.h"
52 #endif
54 #include "common.h"
55 #include "conf.h"
56 #include "window.h"
57 #include "message.h"
58 #include "colors.h"
59 #include "input.h"
60 #include "misc.h"
61 #include "engine.h"
62 #include "strings.h"
63 #include "menu.h"
64 #include "keys.h"
65 #include "rcfile.h"
66 #include "filebrowser.h"
68 #ifdef DEBUG
69 #include <debug.h>
70 #endif
72 #define CBOARD_URL "https://gitlab.com/bjk/cboard/wikis"
73 #define COPYRIGHT "Copyright (C) 2002-2018 " PACKAGE_BUGREPORT
74 #define LINE_GRAPHIC(c) ((!config.linegraphics) ? ' ' : c)
75 #define ROWTOMATRIX(r) ((8 - r) * 2 + 2 - 1)
76 #define COLTOMATRIX(c) ((c == 1) ? 1 : c * 4 - 3)
77 #define STATUS_HEIGHT 12
78 #define MEGA_BOARD (LINES >= 50 && COLS >= 144)
79 #define BOARD_HEIGHT_MB 50
80 #define BOARD_WIDTH_MB 98
81 #define STATUS_WIDTH_MB (COLS - BOARD_WIDTH_MB)
82 #define TAG_HEIGHT_MB 31
83 #define TAG_WIDTH_MB (COLS - BOARD_WIDTH_MB)
84 #define HISTORY_HEIGHT_MB (LINES - (STATUS_HEIGHT + TAG_HEIGHT_MB + 1))
85 #define HISTORY_WIDTH_MB (COLS - BOARD_WIDTH_MB)
86 #define BIG_BOARD (LINES >= 40 && COLS >= 112)
87 #define BOARD_HEIGHT ((MEGA_BOARD) ? BOARD_HEIGHT_MB : (BIG_BOARD) ? 34 : 18)
88 #define BOARD_WIDTH ((MEGA_BOARD) ? BOARD_WIDTH_MB : (BIG_BOARD) ? 66 : 34)
89 #define STATUS_WIDTH ((MEGA_BOARD) ? STATUS_WIDTH_MB : COLS - BOARD_WIDTH)
90 #define TAG_HEIGHT ((MEGA_BOARD) ? TAG_HEIGHT_MB : LINES - STATUS_HEIGHT - 1)
91 #define TAG_WIDTH ((MEGA_BOARD) ? TAG_WIDTH_MB : COLS - BOARD_WIDTH)
92 #define HISTORY_HEIGHT ((MEGA_BOARD) ? HISTORY_HEIGHT_MB : LINES - BOARD_HEIGHT)
93 #define HISTORY_WIDTH ((MEGA_BOARD) ? HISTORY_WIDTH_MB : COLS - STATUS_WIDTH)
94 #define MAX_VALUE_WIDTH (COLS - 8)
96 enum
98 UP, DOWN, LEFT, RIGHT
101 static WINDOW *boardw;
102 static PANEL *boardp;
103 static WINDOW *tagw;
104 static PANEL *tagp;
105 static WINDOW *statusw;
106 static PANEL *statusp;
107 static WINDOW *historyw;
108 static PANEL *historyp;
109 static WINDOW *loadingw;
110 static PANEL *loadingp;
111 static WINDOW *enginew;
112 static PANEL *enginep;
114 static char gameexp[255];
115 static char moveexp[255];
116 static struct itimerval clock_timer;
117 static int delete_count = 0;
118 static int markstart = -1, markend = -1;
119 static int keycount;
120 static char loadfile[FILENAME_MAX];
121 static int quit;
122 static wint_t input_c;
124 // Loaded filename from the command line or from the file input dialog.
125 static int filetype;
126 enum
128 FILE_NONE, FILE_PGN, FILE_FEN, FILE_EPD
131 static char **nags;
132 static int nag_total;
133 static int macro_match;
135 // Primer movimiento de juego cargado
136 // First move loaded game
137 static char fm_loaded_file = FALSE;
139 static int COLS_OLD, LINES_OLD;
141 // Status window.
142 static struct
144 wchar_t *notify; // The status window notification line buffer.
145 } status;
147 static int curses_initialized;
149 // When in history mode a full step is to the next move of the same playing
150 // side. Half stepping is alternating sides.
151 static int movestep;
153 static wchar_t *w_pawn_wchar;
154 static wchar_t *w_rook_wchar;
155 static wchar_t *w_bishop_wchar;
156 static wchar_t *w_knight_wchar;
157 static wchar_t *w_queen_wchar;
158 static wchar_t *w_king_wchar;
159 static wchar_t *b_pawn_wchar;
160 static wchar_t *b_rook_wchar;
161 static wchar_t *b_bishop_wchar;
162 static wchar_t *b_knight_wchar;
163 static wchar_t *b_queen_wchar;
164 static wchar_t *b_king_wchar;
165 static wchar_t *empty_wchar;
166 static wchar_t *enpassant_wchar;
168 static wchar_t *yes_wchar;
169 static wchar_t *all_wchar; // do_save_game_overwrite_confirm()
170 static wchar_t *overwrite_wchar; // do_save_game_overwrite_confirm()
171 static wchar_t *resume_wchar; // do_history_mode_confirm()
172 static wchar_t *current_wchar; // do_game_save_multi_confirm()
173 static wchar_t *append_wchar; // save_pgn()
175 static const char piece_chars[] = "PpRrNnBbQqKkxx";
176 static char *translatable_tag_names[7];
177 static const char *f_pieces[] = {
178 " ", // 0
179 " O ",
180 " /_\\ ",
181 " |-|-| ", // 3
182 " ] [ ",
183 " /___\\ ",
184 " /?M ", // 6
185 " (@/)) ",
186 " /__))",
187 " O ", // 9
188 " (+) ",
189 " /_\\ ",
190 "•°°°°°•", // 12
191 " \\\\|// ",
192 " |___| ",
193 " __+__ ", // 15
194 "(__|__)",
195 " |___| ",
196 " \\ / ", // 18
197 " X ",
198 " / \\ "
201 static const bool cb[8][8] = {
202 {1, 0, 1, 0, 1, 0, 1, 0},
203 {0, 1, 0, 1, 0, 1, 0, 1},
204 {1, 0, 1, 0, 1, 0, 1, 0},
205 {0, 1, 0, 1, 0, 1, 0, 1},
206 {1, 0, 1, 0, 1, 0, 1, 0},
207 {0, 1, 0, 1, 0, 1, 0, 1},
208 {1, 0, 1, 0, 1, 0, 1, 0},
209 {0, 1, 0, 1, 0, 1, 0, 1}
212 static void free_userdata_once (GAME g);
213 static void do_more_help (WIN *);
214 static void do_play_help ();
215 static void do_history_help ();
216 static void do_edit_help ();
218 void
219 coordofmove (GAME g, char *move, char *prow, char *pcol)
221 char l = strlen (move);
223 if (*move == 'O')
225 *prow = (g->turn == WHITE) ? 8 : 1;
226 *pcol = (l <= 4) ? 7 : 3;
227 return;
230 move += l;
232 while (!isdigit (*move))
233 move--;
235 *prow = RANKTOINT (*move--);
236 *pcol = FILETOINT (*move);
239 #define INV_INT(x) (9 - x)
240 #define INV_INT0(x) (7 - x)
242 // Posición por rotación de tablero.
243 // Rotation board position.
244 void
245 rotate_position (char *prow, char *pcol)
247 *prow = INV_INT (*prow);
248 *pcol = INV_INT (*pcol);
251 void
252 update_cursor (GAME g, int idx)
254 int t = pgn_history_total (g->hp);
255 struct userdata_s *d = g->data;
258 * If not deincremented then r and c would be the next move.
260 idx--;
262 if (idx > t || idx < 0 || !t || !g->hp[idx]->move)
263 d->c_row = 2, d->c_col = 5;
264 else
265 coordofmove (g, g->hp[idx]->move, &d->c_row, &d->c_col);
267 if (d->mode == MODE_HISTORY && d->rotate)
268 rotate_position (&d->c_row, &d->c_col);
271 static int
272 init_nag ()
274 FILE *fp;
275 char line[LINE_MAX];
276 int i = 0;
278 if ((fp = fopen (config.nagfile, "r")) == NULL)
280 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", config.nagfile,
281 strerror (errno));
282 return 1;
285 nags = Realloc (nags, (i + 2) * sizeof (char *));
286 nags[i++] = strdup (_("none"));
287 nags[i] = NULL;
289 while (!feof (fp))
291 if (fscanf (fp, " %[^\n] ", line) == 1)
293 nags = Realloc (nags, (i + 2) * sizeof (char *));
294 nags[i++] = strdup (line);
298 nags[i] = NULL;
299 nag_total = i;
300 fclose (fp);
301 return 0;
304 void
305 edit_nag_toggle_item (struct menu_input_s *m)
307 struct input_s *in = m->data;
308 struct input_data_s *id = in->data;
309 HISTORY *h = id->data;
310 int i;
312 if (m->selected == 0)
314 for (i = 0; i < MAX_PGN_NAG; i++)
315 h->nag[i] = 0;
317 for (i = 0; m->items[i]; i++)
318 m->items[i]->selected = 0;
320 return;
323 for (i = 0; i < MAX_PGN_NAG; i++)
325 if (h->nag[i] == m->selected)
326 h->nag[i] = m->selected = 0;
327 else
329 if (!h->nag[i])
331 h->nag[i] = m->selected;
332 break;
338 void
339 edit_nag_save (struct menu_input_s *m)
341 pushkey = -1;
344 void
345 edit_nag_help (struct menu_input_s *m)
347 message (_("NAG Menu Keys"), ANY_KEY_STR, "%s",
348 _(" UP/DOWN - previous/next menu item\n"
349 " HOME/END - first/last menu item\n"
350 " PGDN/PGUP - next/previous page\n"
351 " a-zA-Z0-9 - jump to item\n"
352 " SPACE - toggle selected item\n"
353 " CTRL-X - quit with changes"));
356 struct menu_item_s **
357 get_nag_items (WIN * win)
359 int i, n;
360 struct menu_input_s *m = win->data;
361 struct input_s *in = m->data;
362 struct input_data_s *id = in->data;
363 struct menu_item_s **items = m->items;
364 HISTORY *h = id->data;
366 if (items)
368 for (i = 0; items[i]; i++)
369 free (items[i]);
372 for (i = 0; nags[i]; i++)
374 items = Realloc (items, (i + 2) * sizeof (struct menu_item_s *));
375 items[i] = Malloc (sizeof (struct menu_item_s));
376 items[i]->name = nags[i];
377 items[i]->value = NULL;
379 for (n = 0; n < MAX_PGN_NAG; n++)
381 if (h->nag[n] == i)
383 items[i]->selected = 1;
384 n = -1;
385 break;
389 if (n >= 0)
390 items[i]->selected = 0;
393 items[i] = NULL;
394 m->nofree = 1;
395 m->items = items;
396 return items;
399 void
400 nag_print (WIN * win)
402 struct menu_input_s *m = win->data;
404 mvwprintw (win->w, m->print_line, 1, "%-*s", win->cols - 2, m->item->name);
407 void
408 edit_nag (void *arg)
410 struct menu_key_s **keys = NULL;
412 if (!nags)
414 if (init_nag ())
415 return;
418 add_menu_key (&keys, ' ', edit_nag_toggle_item);
419 add_menu_key (&keys, CTRL_KEY ('x'), edit_nag_save);
420 add_menu_key (&keys, keycode_lookup (global_keys, do_global_help),
421 edit_nag_help);
422 construct_menu (0, 0, -1, -1, _("Numeric Annotation Glyphs"), 1,
423 get_nag_items, keys, arg, nag_print, NULL, NULL);
424 return;
427 static void *
428 view_nag (void *arg)
430 HISTORY *h = (HISTORY *) arg;
431 char buf[80];
432 char line[LINE_MAX] = { 0 };
433 int i = 0;
435 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Viewing NAG for"), h->move);
437 if (!nags)
439 if (init_nag ())
440 return NULL;
443 for (i = 0; i < MAX_PGN_NAG; i++)
445 char buf2[16];
447 if (!h->nag[i])
448 break;
450 if (h->nag[i] >= nag_total)
451 strncat (line, itoa (h->nag[i], buf2), sizeof (line) - 1);
452 else
453 strncat (line, nags[h->nag[i]], sizeof (line) - 1);
455 strncat (line, "\n", sizeof (line) - 1);
458 line[strlen (line) - 1] = 0;
459 message (buf, ANY_KEY_STR, "%s", line);
460 return NULL;
463 void
464 view_annotation (HISTORY * h)
466 char buf[MAX_SAN_MOVE_LEN + strlen (_("Viewing Annotation for")) + 4];
467 int nag = 0, comment = 0;
469 if (!h)
470 return;
472 if (h->comment && h->comment[0])
473 comment++;
475 if (h->nag[0])
476 nag++;
478 if (!nag && !comment)
479 return;
481 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Viewing Annotation for"),
482 h->move);
484 if (comment)
485 construct_message (buf,
486 (nag) ? _("Any other key to continue") : ANY_KEY_STR,
487 0, 1, (nag) ? _("Press 'n' to view NAG") : NULL,
488 (nag) ? view_nag : NULL, (nag) ? h : NULL, NULL,
489 (nag) ? 'n' : 0, 0, NULL, "%s", h->comment);
490 else
491 construct_message (buf, _("Any other key to continue"), 0, 1,
492 _("Press 'n' to view NAG"), view_nag, h, NULL, 'n', 0,
493 NULL, "%s", _("No comment text for this move"));
497 do_game_write (char *filename, const char *mode, int start, int end)
499 int i;
500 struct userdata_s *d;
501 PGN_FILE *pgn;
503 i = pgn_open (filename, mode, &pgn);
505 if (i == E_PGN_ERR)
507 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", filename, strerror (errno));
508 return 1;
510 else if (i == E_PGN_INVALID)
512 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", filename,
513 _("Not a regular file"));
514 return 1;
517 for (i = (start == -1) ? 0 : start; i < end; i++)
519 d = game[i]->data;
520 pgn_write (pgn, game[i]);
521 CLEAR_FLAG (d->flags, CF_MODIFIED);
524 if (pgn_close (pgn) != E_PGN_OK)
525 message (ERROR_STR, ANY_KEY_STR, "%s", strerror (errno));
527 if (start == -1)
529 strncpy (loadfile, filename, sizeof (loadfile));
530 loadfile[sizeof (loadfile) - 1] = 0;
533 return 0;
536 struct save_game_s
538 char *filename;
539 char *mode;
540 int start;
541 int end;
544 void
545 do_save_game_overwrite_confirm (WIN * win)
547 const char *mode = "w";
548 struct save_game_s *s = win->data;
549 wchar_t str[] = { win->c, 0 };
551 if (!wcscmp (str, append_wchar))
552 mode = "a";
553 else if (!wcscmp (str, overwrite_wchar))
554 mode = "w";
555 else
556 goto done;
558 if (do_game_write (s->filename, mode, s->start, s->end))
559 update_status_notify (gp, "%s", _("Save game failed."));
560 else
561 update_status_notify (gp, "%s", _("Game saved."));
563 done:
564 free (s->filename);
565 free (s);
568 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
569 * game index number.
571 void
572 save_pgn (char *filename, int saveindex)
574 char buf[FILENAME_MAX];
575 struct stat st;
576 int end = (saveindex == -1) ? gtotal : saveindex + 1;
577 struct save_game_s *s;
579 if (filename[0] != '/' && config.savedirectory)
581 if (stat (config.savedirectory, &st) == -1)
583 if (errno == ENOENT)
585 if (mkdir (config.savedirectory, 0755) == -1)
587 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s",
588 config.savedirectory, strerror (errno));
589 return;
592 else
594 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s",
595 config.savedirectory, strerror (errno));
596 return;
600 if (stat (config.savedirectory, &st) == -1)
602 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
603 strerror (errno));
604 return;
607 if (!S_ISDIR (st.st_mode))
609 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
610 _("Not a directory."));
611 return;
614 snprintf (buf, sizeof (buf), "%s/%s", config.savedirectory, filename);
615 filename = buf;
618 if (access (filename, W_OK) == 0)
620 s = Malloc (sizeof (struct save_game_s));
621 s->filename = strdup (filename);
622 s->start = saveindex;
623 s->end = end;
624 construct_message (NULL, _("What would you like to do?"), 0, 1, NULL,
625 NULL, s, do_save_game_overwrite_confirm, 0, 0, NULL,
626 "%s \"%s\"\nPress \"%ls\" to append to this file, \"%ls\" to overwrite or any other key to cancel.",
627 _("File exists:"), filename, append_wchar,
628 overwrite_wchar);
629 return;
632 if (do_game_write (filename, "a", saveindex, end))
633 update_status_notify (gp, "%s", _("Save game failed."));
634 else
635 update_status_notify (gp, "%s", _("Game saved."));
638 static int
639 castling_state (GAME g, BOARD b, int row, int col, int piece, int mod)
641 if (pgn_piece_to_int (piece) == ROOK && col == 7
642 && row == 7 &&
643 (TEST_FLAG (g->flags, GF_WK_CASTLE) || mod) &&
644 pgn_piece_to_int (b[7][4].icon) == KING && isupper (piece))
646 if (mod)
647 TOGGLE_FLAG (g->flags, GF_WK_CASTLE);
648 return 1;
650 else if (pgn_piece_to_int (piece) == ROOK && col == 0
651 && row == 7 &&
652 (TEST_FLAG (g->flags, GF_WQ_CASTLE) || mod) &&
653 pgn_piece_to_int (b[7][4].icon) == KING && isupper (piece))
655 if (mod)
656 TOGGLE_FLAG (g->flags, GF_WQ_CASTLE);
657 return 1;
659 else if (pgn_piece_to_int (piece) == ROOK && col == 7
660 && row == 0 &&
661 (TEST_FLAG (g->flags, GF_BK_CASTLE) || mod) &&
662 pgn_piece_to_int (b[0][4].icon) == KING && islower (piece))
664 if (mod)
665 TOGGLE_FLAG (g->flags, GF_BK_CASTLE);
666 return 1;
668 else if (pgn_piece_to_int (piece) == ROOK && col == 0
669 && row == 0 &&
670 (TEST_FLAG (g->flags, GF_BQ_CASTLE) || mod) &&
671 pgn_piece_to_int (b[0][4].icon) == KING && islower (piece))
673 if (mod)
674 TOGGLE_FLAG (g->flags, GF_BQ_CASTLE);
675 return 1;
677 else if (pgn_piece_to_int (piece) == KING && col == 4
678 && row == 7 &&
679 (mod || (pgn_piece_to_int (b[7][7].icon) == ROOK &&
680 TEST_FLAG (g->flags, GF_WK_CASTLE))
682 (pgn_piece_to_int (b[7][0].icon) == ROOK &&
683 TEST_FLAG (g->flags, GF_WQ_CASTLE))) && isupper (piece))
685 if (mod)
687 if (TEST_FLAG (g->flags, GF_WK_CASTLE) ||
688 TEST_FLAG (g->flags, GF_WQ_CASTLE))
689 CLEAR_FLAG (g->flags, GF_WK_CASTLE | GF_WQ_CASTLE);
690 else
691 SET_FLAG (g->flags, GF_WK_CASTLE | GF_WQ_CASTLE);
693 return 1;
695 else if (pgn_piece_to_int (piece) == KING && col == 4
696 && row == 0 &&
697 (mod || (pgn_piece_to_int (b[0][7].icon) == ROOK &&
698 TEST_FLAG (g->flags, GF_BK_CASTLE))
700 (pgn_piece_to_int (b[0][0].icon) == ROOK &&
701 TEST_FLAG (g->flags, GF_BQ_CASTLE))) && islower (piece))
703 if (mod)
705 if (TEST_FLAG (g->flags, GF_BK_CASTLE) ||
706 TEST_FLAG (g->flags, GF_BQ_CASTLE))
707 CLEAR_FLAG (g->flags, GF_BK_CASTLE | GF_BQ_CASTLE);
708 else
709 SET_FLAG (g->flags, GF_BK_CASTLE | GF_BQ_CASTLE);
711 return 1;
714 return 0;
717 #define IS_ENPASSANT(c) (c == 'x') ? CP_BOARD_ENPASSANT : isupper(c) ? CP_BOARD_WHITE : CP_BOARD_BLACK
718 #define ATTRS(cp) (cp & (A_BOLD|A_STANDOUT|A_BLINK|A_DIM|A_UNDERLINE|A_INVIS|A_REVERSE))
720 static void
721 init_wchar_pieces ()
723 w_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♙" : "P");
724 w_rook_wchar = str_to_wchar (config.utf8_pieces ? "♖" : "R");
725 w_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♗" : "B");
726 w_knight_wchar = str_to_wchar (config.utf8_pieces ? "♘" : "N");
727 w_queen_wchar = str_to_wchar (config.utf8_pieces ? "♕" : "Q");
728 w_king_wchar = str_to_wchar (config.utf8_pieces ? "♔" : "K");
729 b_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♟" : "p");
730 b_rook_wchar = str_to_wchar (config.utf8_pieces ? "♜" : "r");
731 b_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♝" : "b");
732 b_knight_wchar = str_to_wchar (config.utf8_pieces ? "♞" : "n");
733 b_queen_wchar = str_to_wchar (config.utf8_pieces ? "♛" : "q");
734 b_king_wchar = str_to_wchar (config.utf8_pieces ? "♚" : "k");
735 empty_wchar = str_to_wchar (" ");
736 enpassant_wchar = str_to_wchar ("x");
739 static wchar_t *
740 piece_to_wchar (unsigned char p)
742 switch (p)
744 case 'P':
745 return w_pawn_wchar;
746 case 'p':
747 return b_pawn_wchar;
748 case 'R':
749 return w_rook_wchar;
750 case 'r':
751 return b_rook_wchar;
752 case 'B':
753 return w_bishop_wchar;
754 case 'b':
755 return b_bishop_wchar;
756 case 'N':
757 return w_knight_wchar;
758 case 'n':
759 return b_knight_wchar;
760 case 'Q':
761 return w_queen_wchar;
762 case 'q':
763 return b_queen_wchar;
764 case 'K':
765 return w_king_wchar;
766 case 'k':
767 return b_king_wchar;
768 case 'x':
769 return enpassant_wchar;
772 return empty_wchar;
775 static int
776 piece_can_attack (GAME g, int rank, int file)
778 struct userdata_s *d = g->data;
779 char *m, *frfr = NULL;
780 pgn_error_t e;
781 int row, col, p, v, pi, cpi;
783 if (d->rotate)
785 rotate_position (&d->c_row, &d->c_col);
786 rotate_position (&d->sp.srow, &d->sp.scol);
789 v = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].valid;
790 pi = pgn_piece_to_int (d->b[RANKTOBOARD (rank)][FILETOBOARD (file)].icon);
791 cpi = d->sp.icon
793 pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)]
794 [FILETOBOARD (d->sp.scol)].icon) :
795 pgn_piece_to_int (d->b[RANKTOBOARD (d->c_row)]
796 [FILETOBOARD (d->c_col)].icon);
798 if (pi == OPEN_SQUARE || cpi == OPEN_SQUARE || !VALIDFILE (file)
799 || !VALIDRANK (rank))
801 if (d->rotate)
803 rotate_position (&d->c_row, &d->c_col);
804 rotate_position (&d->sp.srow, &d->sp.scol);
807 return 0;
810 if (d->sp.icon)
812 col = v ? d->c_col : d->sp.scol;
813 row = v ? d->c_row : d->sp.srow;
815 else
817 col = d->c_col;
818 row = d->c_row;
821 m = malloc (MAX_SAN_MOVE_LEN + 1);
822 m[0] = INTTOFILE (file);
823 m[1] = INTTORANK (rank);
824 m[2] = INTTOFILE (col);
825 m[3] = INTTORANK (row);
826 m[4] = 0;
828 if (d->sp.icon && v)
830 BOARD b;
832 memcpy (b, d->b, sizeof (BOARD));
833 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
834 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
835 pgn_int_to_piece (WHITE, OPEN_SQUARE);
836 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
837 pgn_switch_turn (g);
838 e = pgn_validate_move (g, b, &m, &frfr);
839 pgn_switch_turn (g);
840 free (m);
841 free (frfr);
843 if (e != E_PGN_OK && pgn_piece_to_int (d->sp.icon) == PAWN)
845 int n = (d->sp.srow == 7 && islower (d->sp.icon) && rank == 5) ? 6 :
846 (d->sp.srow == 2 && isupper (d->sp.icon) && rank == 4) ? 3 : 0;
848 if (n && (file == d->c_col - 1 || file == d->c_col + 1))
850 memcpy (b, d->b, sizeof (BOARD));
851 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
852 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
853 pgn_int_to_piece (WHITE, OPEN_SQUARE);
854 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
855 b[RANKTOBOARD (n)][FILETOBOARD (d->sp.scol)].enpassant = 1;
856 m = malloc (MAX_SAN_MOVE_LEN + 1);
857 m[0] = INTTOFILE (file);
858 m[1] = INTTORANK (rank);
859 m[2] = INTTOFILE (col);
860 m[3] = INTTORANK (n);
861 m[4] = 0;
862 pgn_switch_turn (g);
863 SET_FLAG (g->flags, GF_ENPASSANT);
864 e = pgn_validate_move (g, b, &m, &frfr);
865 CLEAR_FLAG (g->flags, GF_ENPASSANT);
866 pgn_switch_turn (g);
867 free (m);
868 free (frfr);
872 goto pca_quit;
875 pgn_switch_turn (g);
876 e = pgn_validate_move (g, d->b, &m, &frfr);
877 pgn_switch_turn (g);
879 if (!strcmp (m, "O-O") || !strcmp (m, "O-O-O"))
880 e = E_PGN_INVALID;
882 if (e == E_PGN_OK)
884 int sf = FILETOINT (frfr[0]), sr = RANKTOINT (frfr[1]);
885 int df = FILETOINT (frfr[2]);
887 pi = d->b[RANKTOBOARD (sr)][FILETOBOARD (sf)].icon;
888 pi = pgn_piece_to_int (pi);
889 if (pi == PAWN && sf == df)
890 e = E_PGN_INVALID;
893 free (m);
894 free (frfr);
896 pca_quit:
897 if (d->rotate)
899 rotate_position (&d->c_row, &d->c_col);
900 rotate_position (&d->sp.srow, &d->sp.scol);
903 return e == E_PGN_OK ? 1 : 0;
906 void
907 print_piece (WINDOW * w, int l, int c, char p)
909 int i, y, ff = 0;
911 for (i = 0; i < 13; i += 2)
913 if (p == piece_chars[i] || p == piece_chars[i + 1])
915 for (y = 0; y < 3; y++)
916 mvwprintw (w, l + y, c, "%s", f_pieces[i + ff + y]);
917 return;
920 ff++;
923 for (y = 0; y < 3; y++)
924 mvwprintw (w, l + y, c, f_pieces[0]);
927 void
928 board_prev_move_play (GAME g)
930 struct userdata_s *d = g->data;
931 char l = strlen (d->pm_frfr);
933 if (l)
935 char q = (l > 4) ? 2 : 1;
937 d->pm_row = RANKTOINT (d->pm_frfr[l - q++]);
938 d->pm_col = FILETOINT (d->pm_frfr[l - q++]);
939 d->ospm_row = RANKTOINT (d->pm_frfr[l - q++]);
940 d->ospm_col = FILETOINT (d->pm_frfr[l - q]);
941 if (d->rotate)
943 rotate_position (&d->pm_row, &d->pm_col);
944 rotate_position (&d->ospm_row, &d->ospm_col);
947 else
949 d->pm_row = 0;
950 d->pm_col = 0;
951 d->ospm_row = 0;
952 d->ospm_col = 0;
956 void
957 board_prev_move_history (GAME g)
959 struct userdata_s *d = g->data;
961 if (g->hindex)
963 char *move = g->hp[g->hindex - 1]->move;
965 if (move)
967 if (d->mode == MODE_PLAY)
968 coordofmove (g, move, &d->pm_row, &d->pm_col);
969 else
971 d->pm_row = 0;
972 d->pm_col = 0;
975 if (*move == 'O')
977 d->ospm_row = g->turn == WHITE ? 8 : 1;
978 d->ospm_col = 5;
979 return;
982 BOARD ob;
983 unsigned char f, r;
985 pgn_board_init (ob);
987 if (g->hindex > 1)
989 HISTORY *h = pgn_history_by_n (g->hp, g->hindex - 2);
990 if (h)
992 pgn_board_init_fen (g, ob, h->fen);
993 pgn_switch_turn (g);
997 for (f = 0; f < 8; f++)
999 for (r = 0; r < 8; r++)
1001 if (ob[f][r].icon != '.' && d->b[f][r].icon == '.')
1003 d->ospm_row = INV_INT0 (f) + 1;
1004 d->ospm_col = r + 1;
1005 break;
1010 if (d->rotate)
1012 if (d->mode == MODE_PLAY)
1013 rotate_position (&d->pm_row, &d->pm_col);
1015 rotate_position (&d->ospm_row, &d->ospm_col);
1018 return;
1021 else
1023 d->ospm_row = 0;
1024 d->ospm_col = 0;
1025 d->pm_row = 0;
1026 d->pm_col = 0;
1030 static int
1031 is_the_square (int brow, int bcol, int row, int col, int prow, int pcol)
1033 if ((!BIG_BOARD && row == ROWTOMATRIX (prow) && col == COLTOMATRIX (pcol))
1034 || (BIG_BOARD && brow + 1 == INV_INT (prow) && bcol + 1 == pcol))
1035 return 1;
1037 return 0;
1040 static int
1041 is_prev_move (struct userdata_s *d, int brow, int bcol, int row, int col)
1043 if (is_the_square (brow, bcol, row, col, d->pm_row, d->pm_col))
1044 return 1;
1046 return 0;
1049 void
1050 update_board_window (GAME g)
1052 int row, col;
1053 int bcol = 0, brow = 0;
1054 int l = config.coordsyleft;
1055 int maxy = BOARD_HEIGHT, maxx = BOARD_WIDTH;
1056 int ncols = 0, offset = 1;
1057 int rowr = (MEGA_BOARD) ? 6 : (BIG_BOARD) ? 4 : 2;
1058 int colr = (MEGA_BOARD) ? 12 : (BIG_BOARD) ? 8 : 4;
1059 unsigned coords_y = 8, cxgc = 0;
1060 unsigned i, cpd = 0;
1061 struct userdata_s *d = g->data;
1063 if (config.bprevmove && d->mode != MODE_EDIT)
1065 if (!d->pm_undo && d->mode == MODE_PLAY)
1066 board_prev_move_play (g);
1067 else
1068 board_prev_move_history (g);
1070 else
1072 d->pm_row = 0;
1073 d->pm_col = 0;
1074 d->ospm_row = 0;
1075 d->ospm_col = 0;
1078 if (d->mode != MODE_PLAY && d->mode != MODE_EDIT)
1079 update_cursor (g, g->hindex);
1081 if (BIG_BOARD)
1083 if (d->rotate)
1085 brow = 7;
1086 coords_y = 1;
1089 else
1091 if (d->rotate)
1093 brow = 1;
1094 coords_y = 1;
1096 else
1097 brow = 8;
1100 for (row = 0; row < maxy; row++)
1102 if (BIG_BOARD)
1104 if (d->rotate)
1105 bcol = 7;
1106 else
1107 bcol = 0;
1109 else
1111 if (d->rotate)
1112 bcol = 8;
1113 else
1114 bcol = 1;
1117 for (col = 0; col < maxx; col++)
1119 int attrwhich = -1;
1120 chtype attrs = 0, old_attrs = 0;
1121 unsigned char p;
1122 int can_attack = 0;
1123 int valid = 0;
1125 if (row == 0 || row == maxy - 2)
1127 if (col == 0)
1128 mvwaddch (boardw, row, col + l,
1129 LINE_GRAPHIC ((row)
1130 ? ACS_LLCORNER | CP_BOARD_GRAPHICS
1131 : ACS_ULCORNER | CP_BOARD_GRAPHICS));
1132 else if (col == maxx - 2)
1133 mvwaddch (boardw, row, col + l,
1134 LINE_GRAPHIC ((row)
1135 ? ACS_LRCORNER | CP_BOARD_GRAPHICS
1136 : ACS_URCORNER | CP_BOARD_GRAPHICS));
1137 else if (!(col % colr))
1138 mvwaddch (boardw, row, col + l,
1139 LINE_GRAPHIC ((row)
1140 ? ACS_BTEE | CP_BOARD_GRAPHICS
1141 : ACS_TTEE | CP_BOARD_GRAPHICS));
1142 else
1144 if (col != maxx - 1)
1145 mvwaddch (boardw, row, col + l,
1146 LINE_GRAPHIC (ACS_HLINE | CP_BOARD_GRAPHICS));
1149 continue;
1152 if ((row % 2) && col == maxx - 1 && (coords_y > 0 && coords_y < 9))
1154 wattron (boardw, CP_BOARD_COORDS);
1155 mvwprintw (boardw,
1156 (BIG_BOARD) ? row * ((MEGA_BOARD) ? 3 : 2)
1157 : row, (l) ? 0 : col, "%d",
1158 (d->rotate) ? coords_y++ : coords_y--);
1159 wattroff (boardw, CP_BOARD_COORDS);
1160 continue;
1163 if ((col == 0 || col == maxx - 2) && row != maxy - 1)
1165 if (!(row % rowr))
1166 mvwaddch (boardw, row, col + l,
1167 LINE_GRAPHIC ((col) ?
1168 ACS_RTEE | CP_BOARD_GRAPHICS :
1169 ACS_LTEE | CP_BOARD_GRAPHICS));
1170 else
1171 mvwaddch (boardw, row, col + l,
1172 LINE_GRAPHIC (ACS_VLINE | CP_BOARD_GRAPHICS));
1174 continue;
1177 if ((row % rowr) && !(col % colr) && row != maxy - 1)
1179 mvwaddch (boardw, row, col + l,
1180 LINE_GRAPHIC (ACS_VLINE | CP_BOARD_GRAPHICS));
1181 continue;
1184 if (!(col % colr) && row != maxy - 1)
1186 mvwaddch (boardw, row, col + l,
1187 LINE_GRAPHIC (ACS_PLUS | CP_BOARD_GRAPHICS));
1188 continue;
1191 if ((row % rowr))
1193 if ((col % colr))
1195 if (BIG_BOARD)
1196 attrwhich = (cb[brow][bcol]) ? WHITE : BLACK;
1197 else
1199 if (ncols++ == 8)
1201 offset++;
1202 ncols = 1;
1205 if (((ncols % 2) && !(offset % 2))
1206 || (!(ncols % 2) && (offset % 2)))
1207 attrwhich = BLACK;
1208 else
1209 attrwhich = WHITE;
1212 if (BIG_BOARD && d->rotate)
1214 brow = INV_INT0 (brow);
1215 bcol = INV_INT0 (bcol);
1218 if (BIG_BOARD)
1219 p = d->b[brow][bcol].icon;
1220 else
1221 p = d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].icon;
1223 int pi = pgn_piece_to_int (p);
1225 if (config.details &&
1226 ((!BIG_BOARD
1227 && d->
1228 b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].enpassant)
1229 || (BIG_BOARD && d->b[brow][bcol].enpassant)))
1231 p = pi = 'x';
1232 attrs = mix_cp (CP_BOARD_ENPASSANT,
1233 (attrwhich ==
1234 WHITE) ? CP_BOARD_WHITE :
1235 CP_BOARD_BLACK,
1236 ATTRS (CP_BOARD_ENPASSANT), A_FG_B_BG);
1239 if (config.showattacks && config.details
1240 && piece_can_attack (g,
1241 BIG_BOARD ? INV_INT0 (brow) +
1242 1 : brow,
1243 BIG_BOARD ? bcol + 1 : bcol))
1245 attrs = CP_BOARD_ATTACK;
1246 old_attrs = attrs;
1247 can_attack = 1;
1250 if (config.validmoves &&
1251 ((!BIG_BOARD
1252 && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].valid)
1253 || (BIG_BOARD && d->b[brow][bcol].valid)))
1255 old_attrs = -1;
1256 valid = 1;
1258 if (attrwhich == WHITE)
1259 attrs = mix_cp (CP_BOARD_MOVES_WHITE,
1260 IS_ENPASSANT (p),
1261 ATTRS (CP_BOARD_MOVES_WHITE),
1262 B_FG_A_BG);
1263 else
1264 attrs = mix_cp (CP_BOARD_MOVES_BLACK,
1265 IS_ENPASSANT (p),
1266 ATTRS (CP_BOARD_MOVES_BLACK),
1267 B_FG_A_BG);
1269 else if (p != 'x' && !can_attack)
1270 attrs =
1271 (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK;
1273 if (BIG_BOARD && d->rotate)
1275 brow = INV_INT0 (brow);
1276 bcol = INV_INT0 (bcol);
1279 if (is_the_square (brow, bcol, row, col, d->c_row,
1280 d->c_col))
1282 attrs = mix_cp (CP_BOARD_CURSOR, IS_ENPASSANT (p),
1283 ATTRS (CP_BOARD_CURSOR), B_FG_A_BG);
1284 old_attrs = -1;
1286 else if (is_the_square (brow, bcol, row, col,
1287 d->sp.srow, d->sp.scol))
1289 attrs = mix_cp (CP_BOARD_SELECTED, IS_ENPASSANT (p),
1290 ATTRS (CP_BOARD_SELECTED), B_FG_A_BG);
1291 old_attrs = -1;
1293 else if ((is_prev_move (d, brow, bcol, row, col) && !valid)
1294 || (is_the_square (brow, bcol, row, col,
1295 d->ospm_row, d->ospm_col)))
1297 attrs = mix_cp (CP_BOARD_PREVMOVE, IS_ENPASSANT (p),
1298 ATTRS (CP_BOARD_PREVMOVE), B_FG_A_BG);
1299 old_attrs = -1;
1302 if (row == maxy - 1)
1303 attrs = 0;
1305 if (can_attack)
1307 int n = is_prev_move (d, brow, bcol, row, col);
1308 chtype a = n && !valid
1309 ? CP_BOARD_PREVMOVE : attrwhich ==
1310 WHITE ? valid ? CP_BOARD_MOVES_WHITE : CP_BOARD_WHITE
1311 : valid ? CP_BOARD_MOVES_BLACK : CP_BOARD_BLACK;
1312 attrs =
1313 mix_cp (CP_BOARD_ATTACK, a, ATTRS (CP_BOARD_ATTACK),
1314 A_FG_B_BG);
1315 old_attrs = -1;
1318 if (BIG_BOARD)
1319 wmove (boardw, row, col + ((MEGA_BOARD) ? 5 : 3) + l);
1320 else
1321 mvwaddch (boardw, row, col + l, ' ' | attrs);
1323 if (row == maxy - 1 && cxgc < 8)
1325 waddch (boardw,
1326 "abcdefgh"[(BIG_BOARD) ? bcol : bcol -
1327 1] | CP_BOARD_COORDS);
1328 cxgc++;
1330 else
1332 if (old_attrs == -1)
1334 old_attrs = attrs;
1335 goto printc;
1338 old_attrs = attrs;
1340 if (pi != OPEN_SQUARE && p != 'x' && !can_attack)
1342 if (attrwhich == WHITE)
1344 if (isupper (p))
1345 attrs = CP_BOARD_W_W;
1346 else
1347 attrs = CP_BOARD_W_B;
1349 else
1351 if (isupper (p))
1352 attrs = CP_BOARD_B_W;
1353 else
1354 attrs = CP_BOARD_B_B;
1358 printc:
1359 if (BIG_BOARD)
1361 if (config.details && !can_attack
1362 && castling_state (g, d->b,
1363 (d->rotate) ? INV_INT0 (brow + 1) - 1 : brow,
1364 (d->rotate) ? INV_INT0 (bcol + 1) - 1 : bcol,
1365 p, 0))
1366 attrs = mix_cp (CP_BOARD_CASTLING, attrs,
1367 ATTRS (CP_BOARD_CASTLING), A_FG_B_BG);
1369 else
1371 if (config.details && !can_attack
1372 && castling_state (g, d->b, RANKTOBOARD (brow),
1373 FILETOBOARD (bcol), p, 0))
1375 attrs = mix_cp (CP_BOARD_CASTLING, attrs,
1376 ATTRS (CP_BOARD_CASTLING),
1377 A_FG_B_BG);
1381 if (BIG_BOARD)
1383 // FIXME: Reimpresión de piezas(+4).
1384 if (cpd < 67)
1386 wattron (boardw, attrs);
1387 if (MEGA_BOARD)
1389 for (i = 0; i < 5; i++)
1390 mvwprintw (boardw, i + brow * 6 + 1,
1391 bcol * 12 + 1 + l,
1392 " ");
1393 if (pi != OPEN_SQUARE)
1394 print_piece (boardw, brow * 6 + 2,
1395 bcol * 12 + 3 + l, p);
1397 else
1399 print_piece (boardw, brow * 4 + 1,
1400 bcol * 8 + 1 + l,
1401 (pi != OPEN_SQUARE) ? p : 0);
1404 wattroff (boardw, attrs);
1405 cpd++;
1408 else
1410 wattron (boardw, attrs);
1411 waddwstr (boardw,
1412 piece_to_wchar (pi !=
1413 OPEN_SQUARE ? p : 0));
1414 wattroff (boardw, attrs);
1417 attrs = old_attrs;
1420 if (BIG_BOARD)
1421 col += (MEGA_BOARD) ? 10 : 6;
1422 else
1424 waddch (boardw, ' ' | attrs);
1425 col += 2;
1428 if (d->rotate)
1429 bcol--;
1430 else
1431 bcol++;
1433 if (BIG_BOARD)
1435 if (bcol > 7)
1436 bcol = 0;
1437 if (bcol < 0)
1438 bcol = 7;
1442 else
1444 if (col != maxx - 1)
1445 mvwaddch (boardw, row, col + l,
1446 LINE_GRAPHIC (ACS_HLINE | CP_BOARD_GRAPHICS));
1450 if (row % rowr)
1452 if (d->rotate)
1453 brow++;
1454 else
1455 brow--;
1458 if (BIG_BOARD)
1460 if (brow > 7)
1461 brow = 0;
1462 if (brow < 0)
1463 brow = 7;
1468 void
1469 invalid_move (int n, int e, const char *m)
1471 if (curses_initialized)
1472 cmessage (ERROR_STR, ANY_KEY_STR, "%s \"%s\" (round #%i)",
1473 (e ==
1474 E_PGN_AMBIGUOUS) ? _("Ambiguous move") : _("Invalid move"), m,
1476 else
1477 warnx ("%s: %s \"%s\" (round #%i)", loadfile, (e == E_PGN_AMBIGUOUS)
1478 ? _("Ambiguous move") : _("Invalid move"), m, n);
1481 void
1482 gameover (GAME g)
1484 struct userdata_s *d = g->data;
1486 SET_FLAG (g->flags, GF_GAMEOVER);
1487 d->mode = MODE_HISTORY;
1488 stop_engine (g);
1491 static void
1492 update_clock (GAME g, struct itimerval it)
1494 struct userdata_s *d = g->data;
1496 if (TEST_FLAG (d->flags, CF_CLOCK) && g->turn == WHITE)
1498 d->wclock.elapsed.tv_sec += it.it_value.tv_sec;
1499 d->wclock.elapsed.tv_usec += it.it_value.tv_usec;
1501 if (d->wclock.elapsed.tv_usec > 1000000 - 1)
1503 d->wclock.elapsed.tv_sec += d->wclock.elapsed.tv_usec / 1000000;
1504 d->wclock.elapsed.tv_usec = d->wclock.elapsed.tv_usec % 1000000;
1507 if (d->wclock.tc[d->wclock.tcn][1] &&
1508 d->wclock.elapsed.tv_sec >= d->wclock.tc[d->wclock.tcn][1])
1510 pgn_tag_add (&g->tag, (char *) "Result", (char *) "0-1");
1511 gameover (g);
1514 else if (TEST_FLAG (d->flags, CF_CLOCK) && g->turn == BLACK)
1516 d->bclock.elapsed.tv_sec += it.it_value.tv_sec;
1517 d->bclock.elapsed.tv_usec += it.it_value.tv_usec;
1519 if (d->bclock.elapsed.tv_usec > 1000000 - 1)
1521 d->bclock.elapsed.tv_sec += d->bclock.elapsed.tv_usec / 1000000;
1522 d->bclock.elapsed.tv_usec = d->bclock.elapsed.tv_usec % 1000000;
1525 if (d->bclock.tc[d->bclock.tcn][1] &&
1526 d->bclock.elapsed.tv_sec >= d->bclock.tc[d->bclock.tcn][1])
1528 pgn_tag_add (&g->tag, (char *) "Result", (char *) "1-0");
1529 gameover (g);
1533 d->elapsed.tv_sec += it.it_value.tv_sec;
1534 d->elapsed.tv_usec += it.it_value.tv_usec;
1536 if (d->elapsed.tv_usec > 1000000 - 1)
1538 d->elapsed.tv_sec += d->elapsed.tv_usec / 1000000;
1539 d->elapsed.tv_usec = d->elapsed.tv_usec % 1000000;
1543 static void
1544 update_time_control (GAME g)
1546 struct userdata_s *d = g->data;
1547 struct clock_s *clk = (g->turn == WHITE) ? &d->wclock : &d->bclock;
1549 if (clk->incr)
1550 clk->tc[clk->tcn][1] += clk->incr;
1552 if (!clk->tc[clk->tcn][1])
1553 return;
1555 clk->move++;
1557 if (!clk->tc[clk->tcn][0] || clk->move >= clk->tc[clk->tcn][0])
1559 clk->move = 0;
1560 clk->tc[clk->tcn + 1][1] +=
1561 labs (clk->elapsed.tv_sec - clk->tc[clk->tcn][1]);
1562 memset (&clk->elapsed, 0, sizeof (clk->elapsed));
1563 clk->tcn++;
1567 void
1568 update_history_window (GAME g)
1570 char buf[HISTORY_WIDTH - 1];
1571 HISTORY *h = NULL;
1572 int n, total;
1573 int t = pgn_history_total (g->hp);
1575 n = (g->hindex + 1) / 2;
1577 if (t % 2)
1578 total = (t + 1) / 2;
1579 else
1580 total = t / 2;
1582 if (t)
1583 snprintf (buf, sizeof (buf), "%u %s %u%s", n, _("of"), total,
1584 (movestep == 1) ? _(" (ply)") : "");
1585 else
1586 strncpy (buf, _("not available"), sizeof (buf) - 1);
1588 buf[sizeof (buf) - 1] = 0;
1589 mvwprintw (historyw, 2, 1, "%*s %-*s", 10, _("Move:"),
1590 HISTORY_WIDTH - 14, buf);
1592 h = pgn_history_by_n (g->hp, g->hindex);
1593 snprintf (buf, sizeof (buf), "%s",
1594 (h && h->move) ? h->move
1595 : (LINES < 24) ? _("empty") : _("not available"));
1596 n = 0;
1598 if (h && ((h->comment) || h->nag[0]))
1600 strncat (buf, _(" (Annotated"), sizeof (buf) - 1);
1601 n++;
1604 if (h && h->rav)
1606 strncat (buf, (n) ? ",+" : " (+", sizeof (buf) - 1);
1607 n++;
1610 if (g->ravlevel)
1612 strncat (buf, (n) ? ",-" : " (-", sizeof (buf) - 1);
1613 n++;
1616 if (n)
1617 strncat (buf, ")", sizeof (buf) - 1);
1619 mvwprintw (historyw, 3, ((LINES < 24) ? 17 : 1), "%s %-*s",
1620 (LINES < 24) ? _("Next:") : _("Next move:"),
1621 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1623 h = pgn_history_by_n (g->hp, g->hindex - 1);
1624 snprintf (buf, sizeof (buf), "%s",
1625 (h && h->move) ? h->move
1626 : (LINES < 24) ? _("empty") : _("not available"));
1627 n = 0;
1629 if (h && ((h->comment) || h->nag[0]))
1631 strncat (buf, _(" (Annotated"), sizeof (buf) - 1);
1632 n++;
1635 if (h && h->rav)
1637 strncat (buf, (n) ? ",+" : " (+", sizeof (buf) - 1);
1638 n++;
1641 if (g->ravlevel)
1643 strncat (buf, (n) ? ",-" : " (-", sizeof (buf) - 1);
1644 n++;
1647 if (n)
1648 strncat (buf, ")", sizeof (buf) - 1);
1650 mvwprintw (historyw, ((LINES < 24) ? 3 : 4), 1, "%s %-*s",
1651 (LINES < 24) ? _("Prev.:") : _("Prev move:"),
1652 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1655 void
1656 do_validate_move (char **move)
1658 struct userdata_s *d = gp->data;
1659 int n;
1660 char *frfr = NULL;
1662 if (TEST_FLAG (d->flags, CF_HUMAN))
1664 if ((n = pgn_parse_move (gp, d->b, move, &frfr)) != E_PGN_OK)
1666 invalid_move (d->n + 1, n, *move);
1667 return;
1670 strcpy (d->pm_frfr, frfr);
1671 update_time_control (gp);
1672 pgn_history_add (gp, d->b, *move);
1673 pgn_switch_turn (gp);
1675 else
1677 if ((n = pgn_validate_move (gp, d->b, move, &frfr)) != E_PGN_OK)
1679 invalid_move (d->n + 1, n, *move);
1680 return;
1683 add_engine_command (gp, ENGINE_THINKING, "%s\n",
1684 (config.engine_protocol == 1) ? frfr : *move);
1687 d->sp.srow = d->sp.scol = d->sp.icon = 0;
1689 if (config.validmoves)
1690 pgn_reset_valid_moves (d->b);
1692 if (TEST_FLAG (gp->flags, GF_GAMEOVER))
1693 d->mode = MODE_HISTORY;
1694 else
1695 SET_FLAG (d->flags, CF_MODIFIED);
1697 free (frfr);
1698 d->paused = 0;
1699 update_history_window (gp);
1700 update_board_window (gp);
1701 return;
1704 void
1705 do_promotion_piece_finalize (WIN * win)
1707 char *p, *str = win->data;
1709 if (pgn_piece_to_int (win->c) == -1)
1710 return;
1712 p = str + strlen (str);
1713 *p++ = toupper (win->c);
1714 *p = '\0';
1715 do_validate_move (&str);
1716 free (str);
1717 win->data = NULL;
1720 static void
1721 move_to_engine (GAME g)
1723 struct userdata_s *d = g->data;
1724 char *str;
1725 int piece;
1727 if (config.validmoves &&
1728 !d->b[RANKTOBOARD (d->sp.row)][FILETOBOARD (d->sp.col)].valid)
1729 return;
1731 str = Malloc (MAX_SAN_MOVE_LEN + 1);
1732 snprintf (str, MAX_SAN_MOVE_LEN + 1, "%c%u%c%u",
1733 _("abcdefgh")[d->sp.scol - 1],
1734 d->sp.srow, _("abcdefgh")[d->sp.col - 1], d->sp.row);
1736 piece =
1737 pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)]
1738 [FILETOBOARD (d->sp.scol)].icon);
1740 if (piece == PAWN && (d->sp.row == 8 || d->sp.row == 1))
1742 construct_message (_("Select Pawn Promotion Piece"), _("R/N/B/Q"), 1, 1,
1743 NULL, NULL, str, do_promotion_piece_finalize, 0, 0,
1744 NULL, "%s",
1745 _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1746 return;
1749 do_validate_move (&str);
1750 free (str);
1753 static char *
1754 clock_to_char (long n)
1756 static char buf[16];
1757 int h = 0, m = 0, s = 0;
1759 h = n / 3600;
1760 m = (n % 3600) / 60;
1761 s = (n % 3600) % 60;
1762 snprintf (buf, sizeof (buf), "%.2i:%.2i:%.2i", h, m, s);
1763 return buf;
1766 static char *
1767 timeval_to_char (struct timeval t, long limit)
1769 static char buf[9];
1770 unsigned h = 0, m = 0, s = 0;
1771 int n = limit ? labs (limit - t.tv_sec) : 0;
1773 h = n / 3600;
1774 m = (n % 3600) / 60;
1775 s = (n % 3600) % 60;
1776 snprintf (buf, sizeof (buf), "%.2u:%.2u:%.2u", h, m, s);
1777 return buf;
1780 static char *
1781 time_control_status (struct clock_s *clk)
1783 static char buf[80] = { 0 };
1785 buf[0] = 0;
1787 if (clk->tc[clk->tcn][0] && clk->tc[clk->tcn + 1][1])
1788 snprintf (buf, sizeof (buf), " M%.2i/%s",
1789 abs (clk->tc[clk->tcn][0] - clk->move),
1790 clock_to_char (clk->tc[clk->tcn + 1][1]));
1791 else if (!clk->incr)
1792 return (char *) "";
1794 if (clk->incr)
1796 char tbuf[16];
1798 strncat (tbuf, " I", sizeof (tbuf) - 1);
1799 strncat (tbuf, itoa (clk->incr, buf), sizeof (tbuf) - 1);
1802 return buf;
1805 void
1806 update_status_window (GAME g)
1808 int i = 0;
1809 char *buf;
1810 char tmp[15] = { 0 }, *engine, *mode;
1811 char t[COLS];
1812 int w;
1813 char *p;
1814 int maxy, maxx;
1815 int len;
1816 struct userdata_s *d = g->data;
1817 int y;
1818 int n;
1820 if (!curses_initialized)
1821 return;
1823 getmaxyx (statusw, maxy, maxx);
1824 (void) maxy;
1825 w = maxx - 2 - 8;
1826 len = maxx - 2;
1827 buf = Malloc (len);
1828 y = 2;
1830 wchar_t *loadfilew = loadfile[0]
1831 ? str_etc (loadfile, w, 1) : str_to_wchar (_("not available"));
1832 mvwprintw (statusw, y++, 1, "%*s %-*ls", 7, _("File:"), w, loadfilew);
1833 free (loadfilew);
1834 snprintf (buf, len, "%i %s %i", gindex + 1, _("of"), gtotal);
1835 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Game:"), w, buf);
1837 *tmp = '\0';
1838 p = tmp;
1840 if (config.details)
1842 *p++ = 'D';
1843 i++;
1846 if (TEST_FLAG (d->flags, CF_DELETE))
1848 if (i)
1849 *p++ = '/';
1851 *p++ = 'X';
1852 i++;
1855 if (TEST_FLAG (g->flags, GF_PERROR))
1857 if (i)
1858 *p++ = '/';
1860 *p++ = '!';
1861 i++;
1864 if (TEST_FLAG (d->flags, CF_MODIFIED))
1866 if (i)
1867 *p++ = '/';
1869 *p++ = '*';
1870 i++;
1873 pgn_config_get (PGN_STRICT_CASTLING, &n);
1875 if (n == 1)
1877 if (i)
1878 *p++ = '/';
1880 *p++ = 'C';
1881 i++;
1883 #ifdef WITH_LIBPERL
1884 if (TEST_FLAG (d->flags, CF_PERL))
1886 if (i)
1887 *p++ = '/';
1889 *p++ = 'P';
1890 i++;
1892 #endif
1894 *p = '\0';
1895 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Flags:"), w,
1896 (tmp[0]) ? tmp : "-");
1898 switch (d->mode)
1900 case MODE_HISTORY:
1901 mode = _("move history");
1902 break;
1903 case MODE_EDIT:
1904 mode = _("edit");
1905 break;
1906 case MODE_PLAY:
1907 mode = _("play");
1908 break;
1909 default:
1910 mode = _("(empty value)");
1911 break;
1914 snprintf (buf, len - 1, "%*s %s", 7, _("Mode:"), mode);
1916 if (d->mode == MODE_PLAY)
1918 if (TEST_FLAG (d->flags, CF_HUMAN))
1919 strncat (buf, _(" (human/human)"), len - 1);
1920 else if (TEST_FLAG (d->flags, CF_ENGINE_LOOP))
1921 strncat (buf, _(" (engine/engine)"), len - 1);
1922 else
1923 strncat (buf, (d->play_mode == PLAY_EH) ?
1924 _(" (engine/human)") : _(" (human/engine)"), len - 1);
1927 buf[len - 1] = 0;
1928 mvwprintw (statusw, y++, 1, "%-*s", len, buf);
1929 free (buf);
1931 if (d->engine)
1933 switch (d->engine->status)
1935 case ENGINE_THINKING:
1936 engine = _("pondering...");
1937 break;
1938 case ENGINE_READY:
1939 engine = _("ready");
1940 break;
1941 case ENGINE_INITIALIZING:
1942 engine = _("initializing...");
1943 break;
1944 case ENGINE_OFFLINE:
1945 engine = _("offline");
1946 break;
1947 default:
1948 engine = _("(empty value)");
1949 break;
1952 else
1953 engine = _("offline");
1955 mvwprintw (statusw, y, 1, "%*s %-*s", 7, _("Engine:"), w, " ");
1956 wattron (statusw, CP_STATUS_ENGINE);
1957 mvwaddstr (statusw, y++, 9, engine);
1958 wattroff (statusw, CP_STATUS_ENGINE);
1960 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Turn:"), w,
1961 (g->turn == WHITE) ? _("white") : _("black"));
1963 strncpy (tmp, _("white"), sizeof (tmp) - 1);
1964 tmp[0] = toupper (tmp[0]);
1965 snprintf (t, sizeof (t), "%s%s",
1966 timeval_to_char (d->wclock.elapsed,
1967 d->wclock.tc[d->wclock.tcn][1]),
1968 time_control_status (&d->wclock));
1969 mvwprintw (statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1971 strncpy (tmp, _("black"), sizeof (tmp) - 1);
1972 tmp[0] = toupper (tmp[0]);
1973 snprintf (t, sizeof (t), "%s%s",
1974 timeval_to_char (d->bclock.elapsed,
1975 d->bclock.tc[d->bclock.tcn][1]),
1976 time_control_status (&d->bclock));
1977 mvwprintw (statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1979 mvwprintw (statusw, y++, 1, "%*s %-*s", 7, _("Total:"), w,
1980 clock_to_char (d->elapsed.tv_sec));
1982 // for (i = 0; i < STATUS_WIDTH; i++)
1983 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1985 if (!status.notify)
1987 char tbuf[255];
1989 snprintf (tbuf, sizeof (tbuf), _("Type %ls for help"),
1990 key_lookup (global_keys, do_global_help));
1991 status.notify = str_to_wchar (tbuf);
1994 wattron (stdscr, CP_STATUS_NOTIFY);
1995 for (i = (config.boardleft) ? BOARD_WIDTH : 0;
1996 i < ((config.boardleft) ? COLS : STATUS_WIDTH); i++)
1997 mvwprintw (stdscr, STATUS_HEIGHT, i, " ");
1998 mvwprintw (stdscr, STATUS_HEIGHT, CENTERX (STATUS_WIDTH, status.notify)
1999 + ((config.boardleft) ? BOARD_WIDTH : 0), "%ls", status.notify);
2000 wattroff (stdscr, CP_STATUS_NOTIFY);
2003 wchar_t *
2004 translate_tag_name (const char *tag)
2006 if (!strcmp (tag, "Event"))
2007 return str_to_wchar (translatable_tag_names[0]);
2008 else if (!strcmp (tag, "Site"))
2009 return str_to_wchar (translatable_tag_names[1]);
2010 else if (!strcmp (tag, "Date"))
2011 return str_to_wchar (translatable_tag_names[2]);
2012 else if (!strcmp (tag, "Round"))
2013 return str_to_wchar (translatable_tag_names[3]);
2014 else if (!strcmp (tag, "White"))
2015 return str_to_wchar (translatable_tag_names[4]);
2016 else if (!strcmp (tag, "Black"))
2017 return str_to_wchar (translatable_tag_names[5]);
2018 else if (!strcmp (tag, "Result"))
2019 return str_to_wchar (translatable_tag_names[6]);
2021 return str_to_wchar (tag);
2024 void
2025 update_tag_window (TAG ** t)
2027 int i, l, w;
2028 int namel = 0;
2030 for (i = 0; t[i]; i++)
2032 wchar_t *namewc = translate_tag_name (t[i]->name);
2034 l = wcslen (namewc);
2035 free (namewc);
2036 if (l > namel)
2037 namel = l;
2040 w = TAG_WIDTH - namel - 4;
2042 for (i = 0; t[i] && i < TAG_HEIGHT - 3; i++)
2044 wchar_t *namewc = translate_tag_name (t[i]->name);
2045 wchar_t *valuewc = str_etc (t[i]->value, w, 0);
2047 mvwprintw (tagw, (i + 2), 1, "%*ls: %-*ls", namel, namewc, w, valuewc);
2048 free (namewc);
2049 free (valuewc);
2052 for (; i < TAG_HEIGHT - 3; i++)
2053 mvwprintw (tagw, (i + 2), 1, "%*s", namel + w + 2, " ");
2056 void
2057 append_enginebuf (GAME g, char *line)
2059 int i = 0;
2060 struct userdata_s *d = g->data;
2062 if (d->engine->enginebuf)
2063 for (i = 0; d->engine->enginebuf[i]; i++);
2065 if (i >= LINES - 3)
2067 free (d->engine->enginebuf[0]);
2069 for (i = 0; d->engine->enginebuf[i + 1]; i++)
2070 d->engine->enginebuf[i] = d->engine->enginebuf[i + 1];
2072 d->engine->enginebuf[i] = strdup (line);
2074 else
2076 d->engine->enginebuf =
2077 Realloc (d->engine->enginebuf, (i + 2) * sizeof (char *));
2078 d->engine->enginebuf[i++] = strdup (line);
2079 d->engine->enginebuf[i] = NULL;
2083 void
2084 update_engine_window (GAME g)
2086 int i;
2087 struct userdata_s *d = g->data;
2089 wmove (enginew, 0, 0);
2090 wclrtobot (enginew);
2092 if (d->engine && d->engine->enginebuf)
2094 for (i = 0; d->engine->enginebuf[i]; i++)
2095 mvwprintw (enginew, i + 2, 1, "%s", d->engine->enginebuf[i]);
2098 window_draw_title (enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
2099 CP_MESSAGE_BORDER);
2102 void
2103 update_all (GAME g)
2105 struct userdata_s *d = g->data;
2108 * In the middle of a macro. Don't update the screen.
2110 if (macro_match != -1)
2111 return;
2113 wmove (boardw, ROWTOMATRIX (d->c_row), COLTOMATRIX (d->c_col));
2114 update_board_window (g);
2115 update_status_window (g);
2116 update_history_window (g);
2117 update_tag_window (g->tag);
2118 update_engine_window (g);
2119 update_panels ();
2120 doupdate ();
2123 static void
2124 game_next_prev (GAME g, int n, int count)
2126 if (gtotal < 2)
2127 return;
2129 if (n == 1)
2131 if (gindex + count > gtotal - 1)
2133 if (count != 1)
2134 gindex = gtotal - 1;
2135 else
2136 gindex = 0;
2138 else
2139 gindex += count;
2141 else
2143 if (gindex - count < 0)
2145 if (count != 1)
2146 gindex = 0;
2147 else
2148 gindex = gtotal - 1;
2150 else
2151 gindex -= count;
2154 gp = game[gindex];
2157 static void
2158 delete_game (int which)
2160 int i, w = which;
2161 struct userdata_s *d;
2163 for (i = 0; i < gtotal; i++)
2165 d = game[i]->data;
2167 if (i == w || TEST_FLAG (d->flags, CF_DELETE))
2169 int n;
2171 free_userdata_once (game[i]);
2172 pgn_free (game[i]);
2174 for (n = i; n + 1 < gtotal; n++)
2175 game[n] = game[n + 1];
2177 gtotal--;
2178 i--;
2179 w = -1;
2183 if (which != -1)
2185 if (which + 1 >= gtotal)
2186 gindex = gtotal - 1;
2187 else
2188 gindex = which;
2190 else
2191 gindex = gtotal - 1;
2193 gp = game[gindex];
2194 gp->hp = gp->history;
2198 * FIXME find across multiple games.
2200 static int
2201 find_move_exp (GAME g, regex_t r, int which, int count)
2203 int i;
2204 int ret;
2205 char errbuf[255];
2206 int incr;
2207 int found;
2209 incr = (which == 0) ? -1 : 1;
2211 for (i = g->hindex + incr - 1, found = 0;; i += incr)
2213 if (i == g->hindex - 1)
2214 break;
2216 if (i >= pgn_history_total (g->hp))
2217 i = 0;
2218 else if (i < 0)
2219 i = pgn_history_total (g->hp) - 1;
2221 // FIXME RAV
2222 ret = regexec (&r, g->hp[i]->move, 0, 0, 0);
2224 if (ret == 0)
2226 if (count == ++found)
2228 return i + 1;
2231 else
2233 if (ret != REG_NOMATCH)
2235 regerror (ret, &r, errbuf, sizeof (errbuf));
2236 cmessage (_("Error Matching Regular Expression"), ANY_KEY_STR,
2237 "%s", errbuf);
2238 return -1;
2243 return -1;
2246 static int
2247 toggle_delete_flag (int n)
2249 int i, x;
2250 struct userdata_s *d = game[n]->data;
2252 TOGGLE_FLAG (d->flags, CF_DELETE);
2253 gindex = n;
2255 for (i = x = 0; i < gtotal; i++)
2257 d = game[i]->data;
2259 if (TEST_FLAG (d->flags, CF_DELETE))
2260 x++;
2263 if (x == gtotal)
2265 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
2266 d = game[n]->data;
2267 CLEAR_FLAG (d->flags, CF_DELETE);
2268 return 1;
2271 return 0;
2274 static int
2275 find_game_exp (char *str, int which, int count)
2277 char *nstr = NULL, *exp = NULL;
2278 regex_t nexp, vexp;
2279 int ret = -1;
2280 int g = 0;
2281 char buf[255] = { 0 }, *tmp;
2282 char errbuf[255];
2283 int found = 0;
2284 int incr = (which == 0) ? -(1) : 1;
2286 strncpy (buf, str, sizeof (buf) - 1);
2287 tmp = buf;
2289 if (strstr (tmp, ":") != NULL)
2291 nstr = strsep (&tmp, ":");
2293 if ((ret = regcomp (&nexp, nstr,
2294 REG_ICASE | REG_EXTENDED | REG_NOSUB)) != 0)
2296 regerror (ret, &nexp, errbuf, sizeof (errbuf));
2297 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR,
2298 "%s", errbuf);
2299 ret = g = -1;
2300 goto cleanup;
2304 exp = tmp;
2306 while (exp && *exp && isspace (*exp))
2307 exp++;
2309 if (exp == NULL)
2310 goto cleanup;
2312 if ((ret = regcomp (&vexp, exp, REG_EXTENDED | REG_NOSUB)) != 0)
2314 regerror (ret, &vexp, errbuf, sizeof (errbuf));
2315 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s",
2316 errbuf);
2317 ret = -1;
2318 goto cleanup;
2321 ret = -1;
2323 for (g = gindex + incr, found = 0;; g += incr)
2325 int t;
2327 if (g == gtotal)
2328 g = 0;
2329 else if (g < 0)
2330 g = gtotal - 1;
2332 if (g == gindex)
2333 break;
2335 for (t = 0; game[g]->tag[t]; t++)
2337 if (nstr)
2339 if (regexec (&nexp, game[g]->tag[t]->name, 0, 0, 0) == 0)
2341 if (regexec (&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0)
2343 if (count == ++found)
2345 ret = g;
2346 goto cleanup;
2351 else
2353 if (regexec (&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0)
2355 if (count == ++found)
2357 ret = g;
2358 goto cleanup;
2364 ret = -1;
2367 cleanup:
2368 if (nstr)
2369 regfree (&nexp);
2371 if (g != -1)
2372 regfree (&vexp);
2374 return ret;
2378 * Updates the notification line in the status window then refreshes the
2379 * status window.
2381 void
2382 update_status_notify (GAME g, const char *fmt, ...)
2384 va_list ap;
2385 #ifdef HAVE_VASPRINTF
2386 char *line;
2387 #else
2388 char line[COLS];
2389 #endif
2391 free (status.notify);
2392 status.notify = NULL;
2394 if (!fmt)
2395 return;
2397 va_start (ap, fmt);
2398 #ifdef HAVE_VASPRINTF
2399 vasprintf (&line, fmt, ap);
2400 #else
2401 vsnprintf (line, sizeof (line), fmt, ap);
2402 #endif
2403 va_end (ap);
2405 status.notify = str_to_wchar (line);
2407 #ifdef HAVE_VASPRINTF
2408 free (line);
2409 #endif
2413 rav_next_prev (GAME g, BOARD b, int n)
2415 // Next RAV.
2416 if (n)
2418 if ((!g->ravlevel && g->hindex && g->hp[g->hindex - 1]->rav == NULL) ||
2419 (!g->ravlevel && !g->hindex && g->hp[g->hindex]->rav == NULL) ||
2420 (g->ravlevel && g->hp[g->hindex]->rav == NULL))
2421 return 1;
2423 g->rav = Realloc (g->rav, (g->ravlevel + 1) * sizeof (RAV));
2424 g->rav[g->ravlevel].hp = g->hp;
2425 g->rav[g->ravlevel].flags = g->flags;
2426 g->rav[g->ravlevel].fen = pgn_game_to_fen (g, b);
2427 g->rav[g->ravlevel].hindex = g->hindex;
2428 g->hp =
2429 (!g->ravlevel) ? (g->hindex) ? g->hp[g->hindex -
2430 1]->rav : g->hp[g->
2431 hindex]->rav :
2432 g->hp[g->hindex]->rav;
2433 g->hindex = 0;
2434 g->ravlevel++;
2435 pgn_board_update (g, b, g->hindex + 1);
2436 return 0;
2439 if (g->ravlevel - 1 < 0)
2440 return 1;
2442 // Previous RAV.
2443 g->ravlevel--;
2444 pgn_board_init_fen (g, b, g->rav[g->ravlevel].fen);
2445 free (g->rav[g->ravlevel].fen);
2446 g->hp = g->rav[g->ravlevel].hp;
2447 g->flags = g->rav[g->ravlevel].flags;
2448 g->hindex = g->rav[g->ravlevel].hindex;
2449 return 0;
2452 static void
2453 draw_window_decor ()
2455 move_panel (boardp, 0, (config.boardleft) ? 0 : COLS - BOARD_WIDTH);
2456 move_panel (historyp, LINES - HISTORY_HEIGHT,
2457 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH : 0 :
2458 (MEGA_BOARD) ? 0 : COLS - HISTORY_WIDTH);
2459 move_panel (statusp, 0, (config.boardleft) ? BOARD_WIDTH : 0);
2460 move_panel (tagp, STATUS_HEIGHT + 1,
2461 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH :
2462 HISTORY_WIDTH : 0);
2464 wbkgd (boardw, CP_BOARD_WINDOW);
2465 wbkgd (statusw, CP_STATUS_WINDOW);
2466 window_draw_title (statusw, _("Game Status"), STATUS_WIDTH,
2467 CP_STATUS_TITLE, CP_STATUS_BORDER);
2468 wbkgd (tagw, CP_TAG_WINDOW);
2469 window_draw_title (tagw, _("Roster Tags"), TAG_WIDTH, CP_TAG_TITLE,
2470 CP_TAG_BORDER);
2471 wbkgd (historyw, CP_HISTORY_WINDOW);
2472 window_draw_title (historyw, _("Move History"), HISTORY_WIDTH,
2473 CP_HISTORY_TITLE, CP_HISTORY_BORDER);
2476 static void
2477 history_menu_resize (WIN *w)
2479 struct menu_input_s *m;
2481 if (!w)
2482 return;
2484 w->rows = MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES;
2485 w->cols = TAG_WIDTH;
2486 w->posy = 0;
2487 w->posx = config.boardleft ? BOARD_WIDTH : 0;
2488 m = w->data;
2489 m->ystatic = w->posy;
2490 m->xstatic = w->posx;
2491 redraw_menu (w);
2494 void
2495 do_window_resize ()
2497 if (LINES < 23 || COLS < 74)
2498 return;
2500 resizeterm (LINES, COLS);
2501 endwin ();
2502 wresize (boardw, BOARD_HEIGHT, BOARD_WIDTH);
2503 wresize (historyw, HISTORY_HEIGHT, HISTORY_WIDTH);
2504 wresize (statusw, STATUS_HEIGHT, STATUS_WIDTH);
2505 wresize (tagw, TAG_HEIGHT, TAG_WIDTH);
2506 //resize_history_menu ();
2508 clear ();
2509 wclear (boardw);
2510 wclear (historyw);
2511 wclear (tagw);
2512 wclear (statusw);
2513 wclear (loadingw);
2514 wclear (enginew);
2515 draw_window_decor ();
2516 update_all (gp);
2517 keypad (boardw, TRUE);
2518 curs_set (0);
2519 cbreak ();
2520 noecho ();
2523 void
2524 do_global_redraw ()
2526 do_window_resize ();
2529 void
2530 stop_clock ()
2532 memset (&clock_timer, 0, sizeof (struct itimerval));
2533 setitimer (ITIMER_REAL, &clock_timer, NULL);
2536 void
2537 start_clock (GAME g)
2539 struct userdata_s *d = g->data;
2541 if (clock_timer.it_interval.tv_usec)
2542 return;
2544 memset (&d->elapsed, 0, sizeof (struct timeval));
2545 clock_timer.it_value.tv_sec = 0;
2546 clock_timer.it_value.tv_usec = 100000;
2547 clock_timer.it_interval.tv_sec = 0;
2548 clock_timer.it_interval.tv_usec = 100000;
2549 setitimer (ITIMER_REAL, &clock_timer, NULL);
2552 static void
2553 update_clocks ()
2555 int i;
2556 struct userdata_s *d;
2557 struct itimerval it;
2558 int update = 0;
2560 getitimer (ITIMER_REAL, &it);
2562 for (i = 0; i < gtotal; i++)
2564 d = game[i]->data;
2566 if (d && d->mode == MODE_PLAY)
2568 if (d->paused == 1 || TEST_FLAG (d->flags, CF_NEW))
2569 continue;
2570 else if (d->paused == -1)
2572 if (game[i]->side == game[i]->turn)
2574 d->paused = 1;
2575 continue;
2579 update_clock (game[i], it);
2581 if (game[i] == gp)
2582 update = 1;
2586 if (update)
2588 update_status_window (gp);
2589 update_panels ();
2590 doupdate ();
2594 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2596 static int
2597 parse_clock_time (char **str)
2599 char *p = *str;
2600 int n = 0, t = 0;
2602 SKIP_SPACE (p);
2604 if (!isdigit (*p))
2605 return -1;
2607 while (*p)
2609 if (isdigit (*p))
2611 t = atoi (p);
2613 while (isdigit (*p))
2614 p++;
2616 continue;
2619 switch (*p)
2621 case 'H':
2622 case 'h':
2623 n += t * (60 * 60);
2624 t = 0;
2625 break;
2626 case 'M':
2627 case 'm':
2628 n += t * 60;
2629 t = 0;
2630 break;
2631 case 'S':
2632 case 's':
2633 n += t;
2634 t = 0;
2635 break;
2636 case ' ':
2637 p++;
2638 case '/':
2639 case '+':
2640 goto done;
2641 default:
2642 *str = p;
2643 return -1;
2646 p++;
2649 done:
2650 n += t;
2651 *str = p;
2652 return n;
2655 static int
2656 parse_clock_input (struct clock_s *clk, char *str, int *incr)
2658 char *p = str;
2659 long n = 0;
2660 int plus = 0;
2661 int m = 0;
2662 int tc = 0;
2664 SKIP_SPACE (p);
2666 if (!*p)
2667 return 0;
2669 if (*p == '+')
2671 plus = 1;
2672 p++;
2673 SKIP_SPACE (p);
2675 if (*p == '+')
2676 goto move_incr;
2678 else
2679 memset (clk, 0, sizeof (struct clock_s));
2681 again:
2682 /* Sudden death. */
2683 if (strncasecmp (p, "SD", 2) == 0)
2685 n = 0;
2686 p += 2;
2687 goto tc;
2690 n = parse_clock_time (&p);
2692 if (n == -1)
2693 return 1;
2695 if (!n)
2696 goto done;
2698 /* Time control. */
2700 if (*p == '/')
2702 if (plus)
2703 return 1;
2705 /* Sudden death without a previous time control. */
2706 if (!n && !tc)
2707 return 1;
2709 m = n;
2710 p++;
2711 n = parse_clock_time (&p);
2713 if (n == -1)
2714 return 1;
2716 if (tc >= MAX_TC)
2718 message (ERROR_STR, ANY_KEY_STR, "%s (%i)",
2719 _("Maximum number of time controls reached"), MAX_TC);
2720 return 1;
2723 clk->tc[tc][0] = m;
2724 clk->tc[tc++][1] = n;
2725 SKIP_SPACE (p);
2727 if (*p == '+')
2728 goto move_incr;
2730 if (*p)
2731 goto again;
2733 goto done;
2736 if (plus)
2737 *incr = n;
2738 else
2739 clk->tc[clk->tcn][1] =
2740 (n <= clk->elapsed.tv_sec) ? clk->elapsed.tv_sec + n : n;
2742 move_incr:
2743 if (*p)
2745 if (*p++ == '+')
2747 if (!isdigit (*p))
2748 return 1;
2750 n = parse_clock_time (&p);
2752 if (n == -1 || *p)
2753 return 1;
2755 clk->incr = n;
2757 SKIP_SPACE (p);
2759 if (*p)
2760 return 1;
2762 else
2763 return 1;
2766 done:
2767 return 0;
2770 static int
2771 parse_which_clock (struct clock_s *clk, char *str)
2773 struct clock_s tmp;
2774 int incr = 0;
2776 memcpy (&tmp, clk, sizeof (struct clock_s));
2778 if (parse_clock_input (&tmp, str, &incr))
2780 cmessage (ERROR_STR, ANY_KEY_STR, _("Invalid clock specification"));
2781 return 1;
2784 memcpy (clk, &tmp, sizeof (struct clock_s));
2785 clk->tc[clk->tcn][1] += incr;
2786 return 0;
2789 void
2790 do_clock_input_finalize (WIN * win)
2792 struct userdata_s *d = gp->data;
2793 struct input_data_s *in = win->data;
2794 char *p = in->str;
2796 if (!in->str)
2798 free (in);
2799 return;
2802 SKIP_SPACE (p);
2804 if (tolower (*p) == 'w')
2806 p++;
2808 if (parse_which_clock (&d->wclock, p))
2809 goto done;
2811 else if (tolower (*p) == 'b')
2813 p++;
2815 if (parse_which_clock (&d->bclock, p))
2816 goto done;
2818 else
2820 if (parse_which_clock (&d->wclock, p))
2821 goto done;
2823 if (parse_which_clock (&d->bclock, p))
2824 goto done;
2827 if (!d->wclock.tc[0][1] && !d->bclock.tc[0][1])
2828 CLEAR_FLAG (d->flags, CF_CLOCK);
2829 else
2830 SET_FLAG (d->flags, CF_CLOCK);
2832 done:
2833 free (in->str);
2834 free (in);
2837 void
2838 do_engine_command_finalize (WIN * win)
2840 struct userdata_s *d = gp->data;
2841 struct input_data_s *in = win->data;
2842 int x;
2844 if (!in->str)
2846 free (in);
2847 return;
2850 if (!d->engine)
2851 goto done;
2853 x = d->engine->status;
2854 send_to_engine (gp, -1, "%s\n", in->str);
2855 d->engine->status = x;
2857 done:
2858 free (in->str);
2859 free (in);
2862 void
2863 do_board_details ()
2865 config.details = (config.details) ? 0 : 1;
2868 void
2869 do_toggle_strict_castling ()
2871 int n;
2873 pgn_config_get (PGN_STRICT_CASTLING, &n);
2875 if (n == 0)
2876 pgn_config_set (PGN_STRICT_CASTLING, 1);
2877 else
2878 pgn_config_set (PGN_STRICT_CASTLING, 0);
2881 void
2882 do_play_set_clock ()
2884 struct input_data_s *in;
2886 in = Calloc (1, sizeof (struct input_data_s));
2887 in->efunc = do_clock_input_finalize;
2888 construct_input (_("Set Clock"), NULL, 1, 1,
2890 ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n"
2891 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2892 NULL, NULL, 0, in, INPUT_HIST_CLOCK, NULL, -1);
2895 void
2896 do_play_toggle_human ()
2898 struct userdata_s *d = gp->data;
2900 TOGGLE_FLAG (d->flags, CF_HUMAN);
2902 if (!TEST_FLAG (d->flags, CF_HUMAN) && pgn_history_total (gp->hp))
2904 if (init_chess_engine (gp))
2905 return;
2908 CLEAR_FLAG (d->flags, CF_ENGINE_LOOP);
2910 if (d->engine)
2911 d->engine->status = ENGINE_READY;
2914 void
2915 do_play_toggle_engine ()
2917 struct userdata_s *d = gp->data;
2919 TOGGLE_FLAG (d->flags, CF_ENGINE_LOOP);
2920 CLEAR_FLAG (d->flags, CF_HUMAN);
2922 if (d->engine && TEST_FLAG (d->flags, CF_ENGINE_LOOP))
2924 char *fen = pgn_game_to_fen (gp, d->b);
2926 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
2927 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
2928 free (fen);
2933 * This will send a command to the engine skipping the command queue.
2935 void
2936 do_play_send_command ()
2938 struct userdata_s *d = gp->data;
2939 struct input_data_s *in;
2941 if (!d->engine || d->engine->status == ENGINE_OFFLINE)
2943 if (init_chess_engine (gp))
2944 return;
2947 in = Calloc (1, sizeof (struct input_data_s));
2948 in->efunc = do_engine_command_finalize;
2949 construct_input (_("Engine Command"), NULL, 1, 1, NULL, NULL, NULL, 0, in,
2950 INPUT_HIST_ENGINE, NULL, -1);
2954 void do_play_switch_turn()
2956 struct userdata_s *d = gp->data;
2958 pgn_switch_side(gp);
2959 pgn_switch_turn(gp);
2961 if (!TEST_FLAG(d->flags, CF_HUMAN))
2962 add_engine_command(gp, -1,
2963 (gp->side == WHITE) ? "white\n" : "black\n");
2965 update_status_window(gp);
2968 void
2969 do_play_toggle_eh_mode ()
2971 struct userdata_s *d = gp->data;
2973 if (!TEST_FLAG (d->flags, CF_HUMAN))
2975 if (!gp->hindex)
2977 pgn_switch_side (gp, TRUE);
2978 d->play_mode = (d->play_mode) ? PLAY_HE : PLAY_EH;
2979 if (gp->side == BLACK)
2980 update_status_notify (gp, _("Press 'g' to start the game"));
2982 d->rotate = !d->rotate;
2984 else
2985 message (NULL, ANY_KEY_STR,
2986 _("You may only switch sides at the start of the \n"
2987 "game. Press ^K or ^N to begin a new game."));
2991 void
2992 do_play_undo ()
2994 struct userdata_s *d = gp->data;
2996 if (!pgn_history_total (gp->hp))
2997 return;
2999 if (keycount)
3001 if (gp->hindex - keycount < 0)
3002 gp->hindex = 0;
3003 else
3005 if (d->go_move)
3006 gp->hindex -= (keycount * 2) - 1;
3007 else
3008 gp->hindex -= keycount * 2;
3011 else
3013 if (gp->hindex - 2 < 0)
3014 gp->hindex = 0;
3015 else
3017 if (d->go_move)
3018 gp->hindex -= 1;
3019 else
3020 gp->hindex -= 2;
3024 pgn_history_free (gp->hp, gp->hindex);
3025 gp->hindex = pgn_history_total (gp->hp);
3026 pgn_board_update (gp, d->b, gp->hindex);
3028 if (d->engine && d->engine->status == ENGINE_READY)
3030 char *fen = pgn_game_to_fen (gp, d->b);
3032 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
3033 free (fen);
3034 d->engine->status = ENGINE_READY;
3037 update_history_window (gp);
3039 if (d->go_move)
3041 pgn_switch_side (gp, FALSE);
3042 d->go_move--;
3045 d->pm_undo = TRUE;
3048 void
3049 do_play_toggle_pause ()
3051 struct userdata_s *d = gp->data;
3053 if (!TEST_FLAG (d->flags, CF_HUMAN) && gp->turn != gp->side)
3055 d->paused = -1;
3056 return;
3059 d->paused = (d->paused) ? 0 : 1;
3062 void
3063 do_play_go ()
3065 struct userdata_s *d = gp->data;
3067 if (TEST_FLAG (d->flags, CF_HUMAN))
3068 return;
3070 if (fm_loaded_file && gp->side != gp->turn)
3072 pgn_switch_side (gp, FALSE);
3073 add_engine_command (gp, ENGINE_THINKING, "black\n");
3076 add_engine_command (gp, ENGINE_THINKING, "go\n");
3078 // Completa la función para que permita seguir jugando al usarla.
3079 // Complete the function to allow continue playing when using.
3080 if (gp->side == gp->turn)
3081 pgn_switch_side (gp, FALSE);
3083 d->go_move++;
3086 void
3087 do_play_config_command ()
3089 int x, w;
3091 if (config.keys)
3093 for (x = 0; config.keys[x]; x++)
3095 if (config.keys[x]->c == input_c)
3097 switch (config.keys[x]->type)
3099 case KEY_DEFAULT:
3100 add_engine_command (gp, -1, "%ls\n", config.keys[x]->str);
3101 break;
3102 case KEY_SET:
3103 if (!keycount)
3104 break;
3106 add_engine_command (gp, -1,
3107 "%ls %i\n", config.keys[x]->str,
3108 keycount);
3109 keycount = 0;
3110 break;
3111 case KEY_REPEAT:
3112 if (!keycount)
3113 break;
3115 for (w = 0; w < keycount; w++)
3116 add_engine_command (gp, -1, "%ls\n", config.keys[x]->str);
3117 keycount = 0;
3118 break;
3124 update_status_notify (gp, NULL);
3127 void
3128 do_play_cancel_selected ()
3130 struct userdata_s *d = gp->data;
3132 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3133 keycount = 0;
3134 pgn_reset_valid_moves (d->b);
3135 update_status_notify (gp, NULL);
3138 void
3139 do_play_commit ()
3141 struct userdata_s *d = gp->data;
3143 pushkey = keycount = 0;
3144 update_status_notify (gp, NULL);
3146 if (!TEST_FLAG (d->flags, CF_HUMAN) &&
3147 (!d->engine || d->engine->status == ENGINE_THINKING))
3148 return;
3150 if (!d->sp.icon)
3151 return;
3153 d->sp.row = d->c_row;
3154 d->sp.col = d->c_col;
3156 if (d->rotate)
3158 rotate_position (&d->sp.row, &d->sp.col);
3159 rotate_position (&d->sp.srow, &d->sp.scol);
3162 move_to_engine (gp);
3164 // Completa la función para que permita seguir jugando cuando se carga un
3165 // archivo pgn (con juego no terminado) que inicie con turno del lado
3166 // negro.
3167 // Complete the function to allow continue playing when loading a file
3168 // pgn (with unfinished game) you start to turn black side.
3169 if (gp->side != gp->turn)
3170 pgn_switch_side (gp, FALSE);
3172 if (d->rotate && d->sp.icon)
3173 rotate_position (&d->sp.srow, &d->sp.scol);
3175 d->go_move = 0;
3176 fm_loaded_file = FALSE;
3177 d->pm_undo = FALSE;
3180 void
3181 do_play_select ()
3183 struct userdata_s *d = gp->data;
3185 if (!TEST_FLAG (d->flags, CF_HUMAN) && (!d->engine ||
3186 d->engine->status ==
3187 ENGINE_OFFLINE))
3189 if (init_chess_engine (gp))
3190 return;
3193 if (d->engine && d->engine->status == ENGINE_THINKING)
3194 return;
3196 if (d->sp.icon)
3197 do_play_cancel_selected ();
3199 if (d->rotate)
3200 rotate_position (&d->c_row, &d->c_col);
3202 d->sp.icon = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon;
3204 if (pgn_piece_to_int (d->sp.icon) == OPEN_SQUARE)
3206 d->sp.icon = 0;
3207 return;
3210 if (((islower (d->sp.icon) && gp->turn != BLACK)
3211 || (isupper (d->sp.icon) && gp->turn != WHITE)))
3213 struct key_s **k;
3214 char *str = Malloc (512);
3216 for (k = play_keys; *k; k++)
3218 if ((*k)->f == do_play_toggle_eh_mode)
3219 break;
3222 snprintf (str, 512,
3224 ("It is not your turn to move. You may switch playing sides by pressing \"%lc\"."),
3225 *k ? (*k)->c : '?');
3226 message (NULL, ANY_KEY_STR, "%s", str);
3227 free (str);
3228 d->sp.icon = 0;
3229 return;
3230 #if 0
3231 if (pgn_history_total (gp->hp))
3233 message (NULL, ANY_KEY_STR, "%s",
3234 _("It is not your turn to move. You can switch sides "));
3235 d->sp.icon = 0;
3236 return;
3238 else
3240 if (pgn_tag_find (gp->tag, "FEN") != E_PGN_ERR)
3241 return;
3243 add_engine_command (gp, ENGINE_READY, "black\n");
3244 pgn_switch_turn (gp);
3246 if (gp->side != BLACK)
3247 pgn_switch_side (gp);
3249 #endif
3252 d->sp.srow = d->c_row;
3253 d->sp.scol = d->c_col;
3255 if (config.validmoves)
3256 pgn_find_valid_moves (gp, d->b, d->sp.scol, d->sp.srow);
3258 if (d->rotate)
3260 rotate_position (&d->c_row, &d->c_col);
3261 rotate_position (&d->sp.srow, &d->sp.scol);
3264 CLEAR_FLAG (d->flags, CF_NEW);
3265 start_clock (gp);
3268 static void
3269 build_help_line_once (wchar_t *buf, wchar_t **pp, struct key_s *k, int t,
3270 int nlen)
3272 wchar_t *p = *pp, *wc;
3273 int n;
3275 if (k->key)
3276 n = wcslen (k->key);
3277 else
3278 n = 1;
3280 while (n++ <= nlen)
3281 *p++ = ' ';
3283 *p = 0;
3285 if (k->key)
3287 wcsncat (buf, k->key, t - 1);
3288 p = buf + wcslen (buf);
3290 else
3291 *p++ = k->c;
3293 *p++ = ' ';
3294 *p++ = '-';
3295 *p++ = ' ';
3296 *p = 0;
3298 if (k->d)
3299 wcsncat (buf, k->d, t - 1);
3301 if (k->r)
3303 wc = str_to_wchar ("*");
3304 wcsncat (buf, wc, t - 1);
3305 free (wc);
3308 wc = str_to_wchar ("\n");
3309 wcscat (buf, wc);
3310 free (wc);
3311 *pp = buf + wcslen (buf);
3314 static void
3315 calc_help_len (struct key_s *k, int *t, int *nlen, int *len)
3317 if (!k->d)
3318 return;
3320 if (k->key)
3322 if (wcslen (k->key) > *nlen)
3324 *nlen = wcslen (k->key);
3325 *t += *nlen;
3327 else
3328 (*t)++;
3330 else
3331 (*t)++;
3333 if (k->d)
3335 if (wcslen (k->d) > *len)
3336 *len = wcslen (k->d);
3339 *t += *len;
3340 *t += k->r;
3343 static wchar_t *
3344 build_help (struct key_s **keys)
3346 int i, m = 0, nlen = 1, len, t;
3347 wchar_t *buf = NULL;
3348 wchar_t *p;
3349 wchar_t *more_help = str_to_wchar (_("more help"));
3350 const wchar_t *more_help_key = key_lookup (global_keys, do_global_help);
3351 struct key_s k;
3352 int mode = MODE_ANY;
3354 if (!keys)
3356 free (more_help);
3357 return NULL;
3360 for (i = t = len = 0; keys[i]; i++)
3361 calc_help_len (keys[i], &t, &nlen, &len);
3363 if (macros)
3365 if (keys == play_keys)
3366 mode = MODE_PLAY;
3367 else if (keys == history_keys)
3368 mode = MODE_HISTORY;
3369 else if (keys == edit_keys)
3370 mode = MODE_EDIT;
3371 else
3372 mode = MODE_ANY;
3374 for (m = 0; macros[m]; m++)
3376 if (macros[m]->mode == mode)
3378 k.d = macros[m]->desc;
3379 k.r = k.c = 0;
3380 k.key = str_to_wchar (fancy_key_name (macros[m]->c));
3381 calc_help_len (&k, &t, &nlen, &len);
3382 free (k.key);
3387 k.d = more_help;
3388 k.r = k.c = 0;
3389 k.key = (wchar_t *)more_help_key;
3390 calc_help_len (&k, &t, &nlen, &len);
3392 t += 4 + i + 1 + m + 1; // +1 for more_help
3393 buf = Malloc ((t + 1) * sizeof (wchar_t));
3394 p = buf;
3396 for (i = 0; keys[i]; i++)
3398 if (!keys[i]->d)
3399 continue;
3401 build_help_line_once (buf, &p, keys[i], t, nlen);
3404 if (macros)
3406 for (m = 0; macros[m]; m++)
3408 if (macros[m]->mode == mode)
3410 k.d = macros[m]->desc;
3411 k.r = k.c = 0;
3412 k.key = str_to_wchar (fancy_key_name (macros[m]->c));
3413 build_help_line_once (buf, &p, &k, t, nlen);
3414 free (k.key);
3419 k.d = more_help;
3420 k.r = k.c = 0;
3421 k.key = (wchar_t *)more_help_key;
3422 build_help_line_once (buf, &p, &k, t, nlen);
3423 free (more_help);
3424 return buf;
3427 void
3428 do_global_help ()
3430 struct userdata_s *d = gp->data;
3431 wchar_t *buf;
3433 if (!d->global_help)
3435 switch (d->mode)
3437 case MODE_PLAY:
3438 do_play_help ();
3439 return;
3440 case MODE_EDIT:
3441 do_edit_help ();
3442 return;
3443 case MODE_HISTORY:
3444 do_history_help ();
3445 return;
3446 default:
3447 break;
3451 d->global_help = 0;
3452 buf = build_help (global_keys);
3453 construct_message (_("Global Game Keys (* = can take a repeat count)"),
3454 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
3455 0, 1, NULL, "%ls", buf);
3458 void
3459 do_main_help (WIN * win)
3461 struct userdata_s *d = gp->data;
3463 switch (win->c)
3465 case 'p':
3466 do_play_help ();
3467 break;
3468 case 'h':
3469 do_history_help ();
3470 break;
3471 case 'e':
3472 do_edit_help ();
3473 break;
3474 case 'g':
3475 d->global_help = 1;
3476 do_global_help ();
3477 break;
3478 default:
3479 break;
3483 static void
3484 do_more_help (WIN * win)
3486 int i;
3488 for (i = 0; global_keys[i]; i++)
3490 if (global_keys[i]->f == do_global_help
3491 && global_keys[i]->c == win->c)
3492 construct_message (_("Command Key Index"),
3493 _("p/h/e/g or any other key to quit"), 0, 0,
3494 NULL, NULL, NULL, do_main_help, 0, 0, NULL, "%s",
3495 _(" p - play mode keys\n"
3496 " h - history mode keys\n"
3497 " e - board edit mode keys\n"
3498 " g - global game keys"));
3502 static void
3503 do_play_help ()
3505 wchar_t *buf = build_help (play_keys);
3507 construct_message (_("Play Mode Keys (* = can take a repeat count)"),
3508 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
3509 buf, do_more_help, 0, 1, NULL, "%ls", buf);
3512 void
3513 do_play_history_mode ()
3515 struct userdata_s *d = gp->data;
3517 if (!pgn_history_total (gp->hp) ||
3518 (d->engine && d->engine->status == ENGINE_THINKING))
3519 return;
3521 d->mode = MODE_HISTORY;
3522 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
3525 void
3526 do_play_edit_mode ()
3528 struct userdata_s *d = gp->data;
3530 if (pgn_history_total (gp->hp))
3531 return;
3533 pgn_board_init_fen (gp, d->b, NULL);
3534 config.details++;
3535 d->mode = MODE_EDIT;
3538 void
3539 do_edit_insert_finalize (WIN * win)
3541 struct userdata_s *d = win->data;
3543 if (pgn_piece_to_int (win->c) == -1)
3544 return;
3546 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon = win->c;
3549 void
3550 do_edit_select ()
3552 struct userdata_s *d = gp->data;
3554 if (d->sp.icon)
3555 return;
3557 d->sp.icon = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon;
3559 if (pgn_piece_to_int (d->sp.icon) == OPEN_SQUARE)
3561 d->sp.icon = 0;
3562 return;
3565 d->sp.srow = d->c_row;
3566 d->sp.scol = d->c_col;
3569 void
3570 do_edit_commit ()
3572 int p;
3573 struct userdata_s *d = gp->data;
3575 pushkey = keycount = 0;
3576 update_status_notify (gp, NULL);
3578 if (!d->sp.icon)
3579 return;
3581 d->sp.row = d->c_row;
3582 d->sp.col = d->c_col;
3583 p = d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
3584 d->b[RANKTOBOARD (d->sp.row)][FILETOBOARD (d->sp.col)].icon = p;
3585 d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
3586 pgn_int_to_piece (gp->turn, OPEN_SQUARE);
3587 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3590 void
3591 do_edit_delete ()
3593 struct userdata_s *d = gp->data;
3595 if (d->sp.icon)
3596 d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
3597 pgn_int_to_piece (gp->turn, OPEN_SQUARE);
3598 else
3599 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon =
3600 pgn_int_to_piece (gp->turn, OPEN_SQUARE);
3602 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3605 void
3606 do_edit_cancel_selected ()
3608 struct userdata_s *d = gp->data;
3610 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3611 keycount = 0;
3612 update_status_notify (gp, NULL);
3615 void
3616 do_edit_switch_turn ()
3618 pgn_switch_turn (gp);
3621 void
3622 do_edit_toggle_castle ()
3624 struct userdata_s *d = gp->data;
3626 castling_state (gp, d->b, RANKTOBOARD (d->c_row),
3627 FILETOBOARD (d->c_col),
3628 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon,
3632 void
3633 do_edit_insert ()
3635 struct userdata_s *d = gp->data;
3637 construct_message (_("Insert Piece"),
3638 _("P=pawn, R=rook, N=knight, B=bishop, Q=queen, K=king"),
3639 0, 0, NULL, NULL, d->b, do_edit_insert_finalize, 0, 0,
3640 NULL, "%s",
3641 _("Type the piece letter to insert. Lowercase for a black piece, uppercase for a white piece."));
3644 void
3645 do_edit_enpassant ()
3647 struct userdata_s *d = gp->data;
3649 if (d->c_row == 6 || d->c_row == 3)
3651 int n = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].enpassant;
3653 pgn_reset_enpassant (d->b);
3654 if (!n)
3655 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].enpassant = 1;
3659 static void
3660 do_edit_help ()
3662 wchar_t *buf = build_help (edit_keys);
3664 construct_message (_("Edit Mode Keys (* = can take a repeat count)"),
3665 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
3666 0, 1, NULL, "%ls", buf);
3669 void
3670 do_edit_exit ()
3672 struct userdata_s *d = gp->data;
3673 char *fen = pgn_game_to_fen (gp, d->b);
3675 config.details--;
3676 pgn_tag_add (&gp->tag, (char *) "FEN", fen);
3677 free (fen);
3678 pgn_tag_add (&gp->tag, (char *) "SetUp", (char *) "1");
3679 pgn_tag_sort (gp->tag);
3680 pgn_board_update (gp, d->b, gp->hindex);
3681 d->mode = MODE_PLAY;
3684 void
3685 really_do_annotate_finalize (struct input_data_s *in, struct userdata_s *d)
3687 HISTORY *h = in->data;
3688 int len;
3690 if (!in->str)
3692 if (h->comment)
3694 free (h->comment);
3695 h->comment = NULL;
3698 else
3700 len = strlen (in->str);
3701 h->comment = Realloc (h->comment, len + 1);
3702 strncpy (h->comment, in->str, len);
3703 h->comment[len] = 0;
3706 free (in->str);
3707 free (in);
3708 SET_FLAG (d->flags, CF_MODIFIED);
3711 void
3712 do_annotate_finalize (WIN * win)
3714 struct userdata_s *d = gp->data;
3715 struct input_data_s *in = win->data;
3717 really_do_annotate_finalize (in, d);
3720 void
3721 do_find_move_exp_finalize (int init, int which)
3723 int n;
3724 struct userdata_s *d = gp->data;
3725 static int firstrun;
3726 static regex_t r;
3727 int ret;
3728 char errbuf[255];
3730 if (init || !firstrun)
3732 if (!firstrun)
3733 regfree (&r);
3735 if ((ret = regcomp (&r, moveexp, REG_EXTENDED | REG_NOSUB)) != 0)
3737 regerror (ret, &r, errbuf, sizeof (errbuf));
3738 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR,
3739 "%s", errbuf);
3740 return;
3743 firstrun = 1;
3746 if ((n = find_move_exp (gp, r,
3747 (which == -1) ? 0 : 1,
3748 (keycount) ? keycount : 1)) == -1)
3749 return;
3751 gp->hindex = n;
3752 pgn_board_update (gp, d->b, gp->hindex);
3755 void
3756 do_find_move_exp (WIN * win)
3758 struct input_data_s *in = win->data;
3759 int *n = in->data;
3760 int which = *n;
3762 if (in->str)
3764 strncpy (moveexp, in->str, sizeof (moveexp) - 1);
3765 moveexp[sizeof (moveexp) - 1] = 0;
3766 do_find_move_exp_finalize (1, which);
3767 free (in->str);
3770 free (in->data);
3771 free (in);
3774 void
3775 do_move_jump_finalize (int n)
3777 struct userdata_s *d = gp->data;
3779 if (n < 0 || n > (pgn_history_total (gp->hp) / 2))
3780 return;
3782 keycount = 0;
3783 update_status_notify (gp, NULL);
3784 gp->hindex = (n) ? n * 2 - 1 : n * 2;
3785 pgn_board_update (gp, d->b, gp->hindex);
3788 void
3789 do_move_jump (WIN * win)
3791 struct input_data_s *in = win->data;
3793 if (!in->str || !isinteger (in->str))
3795 if (in->str)
3796 free (in->str);
3798 free (in);
3799 return;
3802 do_move_jump_finalize (atoi (in->str));
3803 free (in->str);
3804 free (in);
3807 struct history_menu_s
3809 char *line;
3810 int hindex;
3811 int ravlevel;
3812 int move;
3813 int indent;
3816 void
3817 free_history_menu_data (struct history_menu_s **h)
3819 int i;
3821 if (!h)
3822 return;
3824 for (i = 0; h[i]; i++)
3826 free (h[i]->line);
3827 free (h[i]);
3830 free (h);
3833 void
3834 get_history_data (HISTORY ** hp, struct history_menu_s ***menu, int m,
3835 int turn)
3837 int i, n = 0;
3838 int t = pgn_history_total (hp);
3839 char buf[MAX_SAN_MOVE_LEN + 4];
3840 static int depth;
3841 struct history_menu_s **hmenu = *menu;
3843 if (hmenu)
3844 for (n = 0; hmenu[n]; n++);
3845 else
3846 depth = 0;
3848 for (i = 0; i < t; i++)
3850 hmenu = Realloc (hmenu, (n + 2) * sizeof (struct history_menu_s *));
3851 hmenu[n] = Malloc (sizeof (struct history_menu_s));
3852 snprintf (buf, sizeof (buf), "%c%s%s", (turn == WHITE) ? 'W' : 'B',
3853 hp[i]->move, (hp[i]->comment || hp[i]->nag[0]) ? " !" : "");
3854 hmenu[n]->line = strdup (buf);
3855 hmenu[n]->hindex = i;
3856 hmenu[n]->indent = 0;
3857 hmenu[n]->ravlevel = depth;
3858 hmenu[n]->move = (n && depth > hmenu[n - 1]->ravlevel) ? m++ : m;
3859 n++;
3860 hmenu[n] = NULL;
3862 #if 0
3863 if (hp[i]->rav)
3865 depth++;
3866 get_history_data (hp[i]->rav, &hmenu, m, turn);
3867 for (n = 0; hmenu[n]; n++);
3868 depth--;
3870 if (depth)
3871 m--;
3873 #endif
3875 turn = (turn == WHITE) ? BLACK : WHITE;
3878 *menu = hmenu;
3881 void
3882 history_draw_update (struct menu_input_s *m)
3884 GAME g = m->data;
3885 struct userdata_s *d = g->data;
3887 g->hindex = m->selected + 1;
3888 update_cursor (g, m->selected);
3889 pgn_board_update (g, d->b, m->selected + 1);
3892 struct menu_item_s **
3893 get_history_items (WIN * win)
3895 struct menu_input_s *m = win->data;
3896 GAME g = m->data;
3897 struct userdata_s *d = g->data;
3898 struct history_menu_s **hm = d->data;
3899 struct menu_item_s **items = m->items;
3900 int i;
3902 if (!hm)
3904 get_history_data (g->history, &hm, 0,
3905 TEST_FLAG (g->flags, GF_BLACK_OPENING));
3906 m->selected = g->hindex - 1;
3908 if (m->selected < 0)
3909 m->selected = 0;
3911 m->draw_exit_func = history_draw_update;
3914 d->data = hm;
3916 if (items)
3918 for (i = 0; items[i]; i++)
3919 free (items[i]);
3921 free (items);
3922 items = NULL;
3925 for (i = 0; hm[i]; i++)
3927 items = Realloc (items, (i + 2) * sizeof (struct menu_item_s *));
3928 items[i] = Malloc (sizeof (struct menu_item_s));
3929 items[i]->name = hm[i]->line;
3930 items[i]->value = NULL;
3931 items[i]->selected = 0;
3934 if (items)
3935 items[i] = NULL;
3937 m->nofree = 1;
3938 m->items = items;
3939 return items;
3942 void
3943 history_menu_quit (struct menu_input_s *m)
3945 pushkey = -1;
3948 void
3949 history_menu_exit (WIN * win)
3951 GAME g = win->data;
3952 struct userdata_s *d = g->data;
3953 struct history_menu_s **hm = d->data;
3954 int i;
3956 if (!hm)
3957 return;
3959 for (i = 0; hm[i]; i++)
3961 free (hm[i]->line);
3962 free (hm[i]);
3965 free (hm);
3966 d->data = NULL;
3969 // FIXME RAV
3970 void
3971 history_menu_next (struct menu_input_s *m)
3973 GAME g = m->data;
3974 struct userdata_s *d = g->data;
3975 struct history_menu_s **hm = d->data;
3976 int n, t;
3978 for (t = 0; hm[t]; t++);
3980 if (m->selected + 1 == t)
3981 n = 0;
3982 else
3983 n = hm[m->selected + 1]->hindex;
3985 n++;
3986 g->hindex = n;
3989 // FIXME RAV
3990 void
3991 history_menu_prev (struct menu_input_s *m)
3993 GAME g = m->data;
3994 struct userdata_s *d = g->data;
3995 struct history_menu_s **hm = d->data;
3996 int n, t;
3998 for (t = 0; hm[t]; t++);
4000 if (m->selected - 1 < 0)
4001 n = t - 1;
4002 else
4003 n = hm[m->selected - 1]->hindex;
4005 n++;
4006 g->hindex = n;
4009 void
4010 history_menu_help (struct menu_input_s *m)
4012 message (_("History Menu Help"), ANY_KEY_STR, "%s",
4013 _(" UP/DOWN - previous/next menu item\n"
4014 " HOME/END - first/last menu item\n"
4015 " PGDN/PGUP - next/previous page\n"
4016 " a-zA-Z0-9 - jump to item\n"
4017 " CTRL-a - annotate the selected move\n"
4018 " ENTER - view annotation\n"
4019 " CTRL-d - toggle board details\n"
4020 " ESCAPE/M - return to move history"));
4023 void
4024 do_annotate_move (HISTORY * hp)
4026 char buf[COLS - 4];
4027 struct input_data_s *in;
4029 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Editing Annotation for"),
4030 hp->move);
4031 in = Calloc (1, sizeof (struct input_data_s));
4032 in->data = hp;
4033 in->efunc = do_annotate_finalize;
4034 construct_input (buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
4035 _("Type CTRL-t to edit NAG"), edit_nag, NULL,
4036 CTRL_KEY ('T'), in, -1, NULL, -1);
4039 void
4040 history_menu_view_annotation (struct menu_input_s *m)
4042 GAME g = m->data;
4044 // FIXME RAV
4045 view_annotation (g->history[m->selected]);
4048 void
4049 history_menu_annotate_finalize (WIN * win)
4051 struct input_data_s *in = win->data;
4052 GAME g = in->moredata;
4053 struct userdata_s *d = g->data;
4054 struct history_menu_s **hm = d->data;
4056 really_do_annotate_finalize (in, d);
4057 free_history_menu_data (hm);
4058 hm = NULL;
4059 get_history_data (g->history, &hm, 0,
4060 TEST_FLAG (g->flags, GF_BLACK_OPENING));
4061 d->data = hm;
4062 pushkey = REFRESH_MENU;
4065 void
4066 history_menu_annotate (struct menu_input_s *m)
4068 GAME g = m->data;
4069 char buf[COLS - 4];
4070 struct input_data_s *in;
4071 HISTORY *hp = g->history[m->selected]; // FIXME RAV
4073 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Editing Annotation for"),
4074 hp->move);
4075 in = Calloc (1, sizeof (struct input_data_s));
4076 in->data = hp;
4077 in->moredata = m->data;
4078 in->efunc = history_menu_annotate_finalize;
4079 construct_input (buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
4080 _("Type CTRL-t to edit NAG"), edit_nag, NULL,
4081 CTRL_KEY ('T'), in, -1, NULL, -1);
4084 void
4085 history_menu_details (struct menu_input_s *m)
4087 do_board_details ();
4090 // FIXME RAV
4091 void
4092 history_menu_print (WIN * win)
4094 struct menu_input_s *m = win->data;
4095 GAME g = m->data;
4096 struct userdata_s *d = g->data;
4097 struct history_menu_s **hm = d->data;
4098 struct history_menu_s *h = hm[m->top];
4099 int i;
4100 char *p = m->item->name;
4101 int line = m->print_line - 2;
4103 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
4104 * attr_t data type.
4106 attr_t attrs;
4107 short pair;
4108 int total;
4110 for (total = 0; hm[total]; total++);
4111 wattr_get (win->w, &attrs, &pair, NULL);
4112 wattroff (win->w, COLOR_PAIR (pair));
4113 mvwaddch (win->w, m->print_line, 1,
4114 *p == 'W' ? *p | mix_cp (CP_BOARD_WHITE, CP_HISTORY_WINDOW,
4115 ATTRS (CP_BOARD_WHITE),
4116 A_FG_B_BG) : *p | mix_cp (CP_BOARD_BLACK,
4117 CP_HISTORY_WINDOW,
4118 ATTRS
4119 (CP_BOARD_BLACK),
4120 A_FG_B_BG));
4121 p++;
4123 if (h->hindex == 0 && line == 0)
4124 waddch (win->w, ACS_ULCORNER | CP_HISTORY_MENU_LG);
4125 else if ((!hm[h->hindex + (win->rows - 5) + 1] && line == win->rows - 5) ||
4126 (m->top + line == total - 1))
4127 waddch (win->w, ACS_LLCORNER | CP_HISTORY_MENU_LG);
4128 else if (hm[m->top + 1]->ravlevel != h->ravlevel || !h->ravlevel)
4129 waddch (win->w, ACS_LTEE | CP_HISTORY_MENU_LG);
4130 else
4131 waddch (win->w, ACS_VLINE | CP_HISTORY_MENU_LG);
4133 wattron (win->w, COLOR_PAIR (pair) | attrs);
4135 for (i = 2; *p; p++, i++)
4136 waddch (win->w, (*p == '!') ? *p | A_BOLD : *p);
4138 while (i++ < win->cols - 2)
4139 waddch (win->w, ' ');
4142 void
4143 history_menu (GAME g)
4145 struct menu_key_s **keys = NULL;
4147 add_menu_key (&keys, KEY_ESCAPE, history_menu_quit);
4148 add_menu_key (&keys, 'M', history_menu_quit);
4149 add_menu_key (&keys, KEY_UP, history_menu_prev);
4150 add_menu_key (&keys, KEY_DOWN, history_menu_next);
4151 add_menu_key (&keys, keycode_lookup (global_keys, do_global_help),
4152 history_menu_help);
4153 add_menu_key (&keys, CTRL_KEY ('a'), history_menu_annotate);
4154 add_menu_key (&keys, CTRL_KEY ('d'), history_menu_details);
4155 add_menu_key (&keys, '\n', history_menu_view_annotation);
4156 construct_menu (MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES, TAG_WIDTH,
4157 0, config.boardleft ? BOARD_WIDTH : 0,
4158 _("Move History Tree"), 1, get_history_items, keys, g,
4159 history_menu_print, history_menu_exit, history_menu_resize);
4162 void
4163 do_history_menu ()
4165 history_menu (gp);
4168 void
4169 do_history_half_move_toggle ()
4171 movestep = (movestep == 1) ? 2 : 1;
4172 update_history_window (gp);
4175 void
4176 do_history_rotate_board ()
4178 struct userdata_s *d = gp->data;
4179 d->rotate = !d->rotate;
4182 void
4183 do_history_jump_next ()
4185 struct userdata_s *d = gp->data;
4187 pgn_history_next (gp, d->b, (keycount > 0) ?
4188 config.jumpcount * keycount * movestep :
4189 config.jumpcount * movestep);
4192 void
4193 do_history_jump_prev ()
4195 struct userdata_s *d = gp->data;
4197 pgn_history_prev (gp, d->b, (keycount) ?
4198 config.jumpcount * keycount * movestep :
4199 config.jumpcount * movestep);
4202 void
4203 do_history_prev ()
4205 struct userdata_s *d = gp->data;
4207 pgn_history_prev (gp, d->b, (keycount) ? keycount * movestep : movestep);
4210 void
4211 do_history_next ()
4213 struct userdata_s *d = gp->data;
4215 pgn_history_next (gp, d->b, (keycount) ? keycount * movestep : movestep);
4218 void
4219 do_history_mode_finalize (struct userdata_s *d)
4221 pushkey = 0;
4222 d->mode = MODE_PLAY;
4225 void
4226 do_history_mode_confirm (WIN * win)
4228 struct userdata_s *d = gp->data;
4229 wchar_t str[] = { win->c, 0 };
4231 if (!wcscmp (str, resume_wchar))
4233 pgn_history_free (gp->hp, gp->hindex);
4234 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4236 #if 0
4237 case 'C':
4238 case 'c':
4239 if (pgn_history_rav_new (gp, d->b, gp->hindex) != E_PGN_OK)
4240 return;
4242 break;
4243 #endif
4244 else
4245 return;
4247 if (!TEST_FLAG (d->flags, CF_HUMAN))
4249 char *fen = pgn_game_to_fen (gp, d->b);
4251 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
4252 free (fen);
4255 do_history_mode_finalize (d);
4258 void
4259 do_history_toggle ()
4261 struct userdata_s *d = gp->data;
4263 // FIXME Resuming from previous history could append to a RAV.
4264 if (gp->hindex != pgn_history_total (gp->hp))
4266 if (!pushkey)
4267 construct_message (NULL, _("What would you like to do?"), 0, 1,
4268 NULL, NULL, NULL, do_history_mode_confirm, 0, 0,
4269 NULL, _
4270 ("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."),
4271 resume_wchar);
4272 return;
4274 else
4276 if (TEST_FLAG (gp->flags, GF_GAMEOVER))
4277 return;
4280 if (gp->side != gp->turn)
4282 d->play_mode = PLAY_EH;
4284 else
4286 d->play_mode = PLAY_HE;
4287 d->rotate = FALSE;
4290 do_history_mode_finalize (d);
4293 void
4294 do_history_annotate ()
4296 int n = gp->hindex;
4298 if (n && gp->hp[n - 1]->move)
4299 n--;
4300 else
4301 return;
4303 do_annotate_move (gp->hp[n]);
4306 static void
4307 do_history_help ()
4309 wchar_t *buf = build_help (history_keys);
4311 construct_message (_("History Mode Keys (* = can take a repeat count)"),
4312 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
4313 buf, do_more_help, 0, 1, NULL, "%ls", buf);
4316 void
4317 do_history_find (int which)
4319 struct input_data_s *in;
4320 int *p;
4322 if (pgn_history_total (gp->hp) < 2)
4323 return;
4325 in = Calloc (1, sizeof (struct input_data_s));
4326 p = Malloc (sizeof (int));
4327 *p = which;
4328 in->data = p;
4329 in->efunc = do_find_move_exp;
4331 if (!*moveexp || which == 0)
4333 construct_input (_("Find Move Text Expression"), NULL, 1, 0, NULL, NULL,
4334 NULL, 0, in, INPUT_HIST_MOVE_EXP, NULL, -1);
4335 return;
4338 free (p);
4339 free (in);
4340 do_find_move_exp_finalize (0, which);
4343 void
4344 do_history_find_new ()
4346 do_history_find (0);
4349 void
4350 do_history_find_prev ()
4352 do_history_find (-1);
4355 void
4356 do_history_find_next ()
4358 do_history_find (1);
4361 void
4362 do_history_rav (int which)
4364 struct userdata_s *d = gp->data;
4366 rav_next_prev (gp, d->b, which);
4369 void
4370 do_history_rav_next ()
4372 do_history_rav (1);
4375 void
4376 do_history_rav_prev ()
4378 do_history_rav (0);
4381 void
4382 do_history_jump ()
4384 struct input_data_s *in;
4386 if (pgn_history_total (gp->hp) < 2)
4387 return;
4389 if (!keycount)
4391 in = Calloc (1, sizeof (struct input_data_s));
4392 in->efunc = do_move_jump;
4394 construct_input (_("Jump to Move Number"), NULL, 1, 1, NULL,
4395 NULL, NULL, 0, in, -1, NULL, 0);
4396 return;
4399 do_move_jump_finalize (keycount);
4402 static void
4403 free_userdata_once (GAME g)
4405 struct userdata_s *d = g->data;
4407 if (!d)
4408 return;
4410 if (d->engine)
4412 stop_engine (g);
4414 if (d->engine->enginebuf)
4416 int n;
4418 for (n = 0; d->engine->enginebuf[n]; n++)
4419 free (d->engine->enginebuf[n]);
4421 free (d->engine->enginebuf);
4424 if (d->engine->queue)
4426 struct queue_s **q;
4428 for (q = d->engine->queue; *q; q++)
4429 free (*q);
4431 free (d->engine->queue);
4434 free (d->engine);
4437 #ifdef WITH_LIBPERL
4438 if (d->perlfen)
4439 free (d->perlfen);
4441 if (d->oldfen)
4442 free (d->oldfen);
4443 #endif
4445 free (d);
4446 g->data = NULL;
4449 static void
4450 free_userdata ()
4452 int i;
4454 for (i = 0; i < gtotal; i++)
4456 free_userdata_once (game[i]);
4457 game[i]->data = NULL;
4461 void
4462 update_loading_window (int n)
4464 char buf[16];
4466 if (!loadingw)
4468 loadingw = newwin (3, COLS / 2, CALCPOSY (3), CALCPOSX (COLS / 2));
4469 loadingp = new_panel (loadingw);
4470 wbkgd (loadingw, CP_MESSAGE_WINDOW);
4473 wmove (loadingw, 0, 0);
4474 wclrtobot (loadingw);
4475 wattron (loadingw, CP_MESSAGE_BORDER);
4476 box (loadingw, ACS_VLINE, ACS_HLINE);
4477 wattroff (loadingw, CP_MESSAGE_BORDER);
4478 mvwprintw (loadingw, 1, CENTER_INT ((COLS / 2), 11 +
4479 strlen (itoa (gtotal, buf))),
4480 _("Loading... %i%% (%i games)"), n, gtotal);
4481 update_panels ();
4482 doupdate ();
4485 static void
4486 init_userdata_once (GAME g, int n)
4488 struct userdata_s *d = NULL;
4490 d = Calloc (1, sizeof (struct userdata_s));
4491 d->n = n;
4492 d->c_row = 2, d->c_col = 5;
4493 SET_FLAG (d->flags, CF_NEW);
4494 g->data = d;
4496 if (pgn_board_init_fen (g, d->b, NULL) != E_PGN_OK)
4497 pgn_board_init (d->b);
4500 void
4501 init_userdata ()
4503 int i;
4505 for (i = 0; i < gtotal; i++)
4506 init_userdata_once (game[i], i);
4509 void
4510 fix_marks (int *start, int *end)
4512 int i;
4514 *start = (*start < 0) ? 0 : *start;
4515 *end = (*end < 0) ? 0 : *end;
4517 if (*start > *end)
4519 i = *start;
4520 *start = *end;
4521 *end = i + 1;
4524 *end = (*end > gtotal) ? gtotal : *end;
4527 void
4528 do_new_game_finalize (GAME g)
4530 struct userdata_s *d = g->data;
4532 d->mode = MODE_PLAY;
4533 update_status_notify (g, NULL);
4534 d->rotate = FALSE;
4535 d->go_move = 0;
4538 void
4539 do_new_game_from_scratch (WIN * win)
4541 wchar_t str[] = { win->c, 0 };
4543 if (wcscmp (str, yes_wchar))
4544 return;
4546 stop_clock ();
4547 free_userdata ();
4548 pgn_parse (NULL);
4549 gp = game[gindex];
4550 add_custom_tags (&gp->tag);
4551 init_userdata ();
4552 loadfile[0] = 0;
4553 do_new_game_finalize (gp);
4556 void
4557 do_new_game ()
4559 pgn_new_game ();
4560 gp = game[gindex];
4561 add_custom_tags (&gp->tag);
4562 init_userdata_once (gp, gindex);
4563 do_new_game_finalize (gp);
4566 void
4567 do_game_delete_finalize (int n)
4569 struct userdata_s *d;
4571 delete_game ((!n) ? gindex : -1);
4572 d = gp->data;
4573 if (d->mode != MODE_EDIT)
4574 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4577 void
4578 do_game_delete_confirm (WIN * win)
4580 int *n;
4581 wchar_t str[] = { win->c, 0 };
4583 if (wcscmp (str, yes_wchar))
4585 free (win->data);
4586 return;
4589 n = (int *) win->data;
4590 do_game_delete_finalize (*n);
4591 free (win->data);
4594 void
4595 do_game_delete ()
4597 char *tmp = NULL;
4598 int i, n;
4599 struct userdata_s *d;
4600 int *p;
4602 if (gtotal < 2)
4604 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4605 return;
4608 tmp = NULL;
4610 for (i = n = 0; i < gtotal; i++)
4612 d = game[i]->data;
4614 if (TEST_FLAG (d->flags, CF_DELETE))
4615 n++;
4618 if (!n)
4619 tmp = _("Delete the current game?");
4620 else
4622 if (n == gtotal)
4624 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4625 return;
4628 tmp = _("Delete all games marked for deletion?");
4631 if (config.deleteprompt)
4633 p = Malloc (sizeof (int));
4634 *p = n;
4635 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, p,
4636 do_game_delete_confirm, 0, 0, NULL, "%s", tmp);
4637 return;
4640 do_game_delete_finalize (n);
4643 void
4644 do_find_game_exp_finalize (int which)
4646 struct userdata_s *d = gp->data;
4647 int n;
4649 if ((n = find_game_exp (gameexp, (which == -1) ? 0 : 1,
4650 (keycount) ? keycount : 1)) == -1)
4652 update_status_notify (gp, "%s", _("No matches found"));
4653 return;
4656 gindex = n;
4657 d = gp->data;
4659 if (pgn_history_total (gp->hp))
4660 d->mode = MODE_HISTORY;
4662 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4665 void
4666 do_find_game_exp (WIN * win)
4668 struct input_data_s *in = win->data;
4669 int *n = in->data;
4670 int c = *n;
4672 if (in->str)
4674 strncpy (gameexp, in->str, sizeof (gameexp));
4675 gameexp[sizeof (gameexp) - 1] = 0;
4677 if (c == '?')
4678 c = '}';
4680 do_find_game_exp_finalize (c);
4681 free (in->str);
4684 free (in->data);
4685 free (in);
4688 void
4689 do_game_jump_finalize (int n)
4691 struct userdata_s *d;
4693 if (--n > gtotal - 1 || n < 0)
4694 return;
4696 gindex = n;
4697 gp = game[gindex];
4698 d = gp->data;
4699 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4700 update_status_notify (gp, NULL);
4703 void
4704 do_game_jump (WIN * win)
4706 struct input_data_s *in = win->data;
4708 if (!in->str || !isinteger (in->str))
4710 if (in->str)
4711 free (in->str);
4713 free (in);
4714 return;
4717 do_game_jump_finalize (atoi (in->str));
4718 free (in->str);
4719 free (in);
4722 void
4723 do_load_file (WIN * win)
4725 struct input_data_s *in = win->data;
4726 char *tmp = in->str;
4727 struct userdata_s *d;
4728 PGN_FILE *pgn = NULL;
4729 int n;
4731 if (!in->str)
4733 free (in);
4734 return;
4737 if ((tmp = pathfix (tmp)) == NULL)
4738 goto done;
4740 n = pgn_open (tmp, "r", &pgn);
4742 if (n == E_PGN_ERR)
4744 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, strerror (errno));
4745 goto done;
4747 else if (n == E_PGN_INVALID)
4749 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp,
4750 _("Not a regular file"));
4751 goto done;
4754 free_userdata ();
4756 if (pgn_parse (pgn) == E_PGN_ERR)
4758 del_panel (loadingp);
4759 delwin (loadingw);
4760 loadingw = NULL;
4761 loadingp = NULL;
4762 init_userdata ();
4763 goto done;
4766 del_panel (loadingp);
4767 delwin (loadingw);
4768 loadingw = NULL;
4769 loadingp = NULL;
4770 init_userdata ();
4771 strncpy (loadfile, tmp, sizeof (loadfile));
4772 loadfile[sizeof (loadfile) - 1] = 0;
4773 gp = game[gindex];
4774 d = gp->data;
4776 if (pgn_history_total (gp->hp))
4777 d->mode = MODE_HISTORY;
4779 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4781 fm_loaded_file = TRUE;
4782 d->rotate = FALSE;
4784 done:
4785 pgn_close (pgn);
4787 if (in->str)
4788 free (in->str);
4790 free (in);
4793 void
4794 do_game_save (WIN * win)
4796 struct input_data_s *in = win->data;
4797 int *x = in->data;
4798 int n = *x;
4799 char *tmp = in->str;
4800 char tfile[FILENAME_MAX];
4801 char *p;
4802 int i;
4803 struct userdata_s *d;
4805 if (!tmp || (tmp = pathfix (tmp)) == NULL)
4806 goto done;
4808 if (pgn_is_compressed (tmp) == E_PGN_ERR)
4810 p = tmp + strlen (tmp) - 1;
4812 if (*p != 'n' || *(p - 1) != 'g' || *(p - 2) != 'p' || *(p - 3) != '.')
4814 snprintf (tfile, sizeof (tfile), "%s.pgn", tmp);
4815 tmp = tfile;
4820 * When in edit mode, update the FEN tag.
4822 if (n == -1)
4824 for (i = 0; i < gtotal; i++)
4826 d = game[i]->data;
4828 if (d->mode == MODE_EDIT)
4830 char *fen = pgn_game_to_fen (game[i], d->b);
4832 pgn_tag_add (&game[i]->tag, (char *) "FEN", fen);
4833 free (fen);
4837 else
4839 d = game[n]->data;
4841 if (d->mode == MODE_EDIT)
4843 char *fen = pgn_game_to_fen (game[n], d->b);
4845 pgn_tag_add (&game[n]->tag, (char *) "FEN", fen);
4846 free (fen);
4850 save_pgn (tmp, n);
4852 done:
4853 if (in->str)
4854 free (in->str);
4856 free (in->data);
4857 free (in);
4860 void
4861 do_get_game_save_input (int n)
4863 struct input_data_s *in = Calloc (1, sizeof (struct input_data_s));
4864 int *p = Malloc (sizeof (int));
4866 in->efunc = do_game_save;
4867 *p = n;
4868 in->data = p;
4870 construct_input (_("Save Game Filename"), loadfile, 1, 1,
4871 _("Type TAB for file browser"), file_browser, NULL, '\t',
4872 in, INPUT_HIST_FILE, NULL, -1);
4875 void
4876 do_game_save_multi_confirm (WIN * win)
4878 int i;
4879 wchar_t str[] = { win->c, 0 };
4881 if (!wcscmp (str, current_wchar))
4882 i = gindex;
4883 else if (!wcscmp (str, all_wchar))
4884 i = -1;
4885 else
4887 update_status_notify (gp, "%s", _("Save game aborted."));
4888 return;
4891 do_get_game_save_input (i);
4894 void
4895 do_global_about ()
4897 cmessage (_("ABOUT"), ANY_KEY_STR,
4898 _("%s\nUsing %s with %i colors and %i color pairs\n%s\n%s"),
4899 PACKAGE_STRING, curses_version (), COLORS, COLOR_PAIRS,
4900 COPYRIGHT, CBOARD_URL);
4903 void
4904 global_game_next_prev (int which)
4906 struct userdata_s *d;
4908 game_next_prev (gp, (which == 1) ? 1 : 0, (keycount) ? keycount : 1);
4909 d = gp->data;
4911 if (delete_count)
4913 if (which == 1)
4915 markend = markstart + delete_count;
4916 delete_count = 0;
4918 else
4920 markend = markstart - delete_count + 1;
4921 delete_count = -1; // to fix gindex in the other direction
4924 fix_marks (&markstart, &markend);
4925 do_global_toggle_delete ();
4928 if (d->mode == MODE_HISTORY)
4929 pgn_board_update (gp, d->b, gp->hindex);
4930 else if (d->mode == MODE_PLAY)
4931 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4934 void
4935 do_global_next_game ()
4937 global_game_next_prev (1);
4940 void
4941 do_global_prev_game ()
4943 global_game_next_prev (0);
4946 void
4947 global_find (int which)
4949 struct input_data_s *in;
4950 int *p;
4952 if (gtotal < 2)
4953 return;
4955 in = Calloc (1, sizeof (struct input_data_s));
4956 p = Malloc (sizeof (int));
4957 *p = which;
4958 in->data = p;
4959 in->efunc = do_find_game_exp;
4961 if (!*gameexp || which == 0)
4963 construct_input (_("Find Game by Tag Expression"), NULL, 1, 0,
4964 _("[name expression:]value expression"), NULL, NULL, 0,
4965 in, INPUT_HIST_GAME_EXP, NULL, -1);
4966 return;
4969 free (p);
4970 free (in);
4971 do_find_game_exp_finalize (which);
4974 void
4975 do_global_find_new ()
4977 global_find (0);
4980 void
4981 do_global_find_next ()
4983 global_find (1);
4986 void
4987 do_global_find_prev ()
4989 global_find (-1);
4992 void
4993 do_global_game_jump ()
4995 if (gtotal < 2)
4996 return;
4998 if (!keycount)
5000 struct input_data_s *in;
5002 in = Calloc (1, sizeof (struct input_data_s));
5003 in->efunc = do_game_jump;
5004 construct_input (_("Jump to Game Number"), NULL, 1, 1, NULL, NULL, NULL,
5005 0, in, -1, NULL, 0);
5006 return;
5009 do_game_jump_finalize (keycount);
5012 void
5013 do_global_toggle_delete ()
5015 int i;
5017 pushkey = 0;
5019 if (gtotal < 2)
5020 return;
5022 if (keycount && delete_count == 0)
5024 markstart = gindex;
5025 delete_count = keycount;
5026 update_status_notify (gp, "%s (delete)", status.notify);
5027 return;
5030 if (markstart >= 0 && markend >= 0)
5032 for (i = markstart; i < markend; i++)
5034 if (toggle_delete_flag (i))
5036 return;
5040 gindex = (delete_count < 0) ? markstart : i - 1;
5042 else
5044 if (toggle_delete_flag (gindex))
5045 return;
5048 markstart = markend = -1;
5049 delete_count = 0;
5050 update_status_window (gp);
5053 void
5054 do_global_delete_game ()
5056 do_game_delete ();
5059 void
5060 do_global_tag_edit ()
5062 struct userdata_s *d = gp->data;
5064 edit_tags (gp, d->b, 1);
5067 void
5068 do_global_tag_view ()
5070 struct userdata_s *d = gp->data;
5072 edit_tags (gp, d->b, 0);
5075 void
5076 do_global_resume_game ()
5078 struct input_data_s *in;
5080 in = Calloc (1, sizeof (struct input_data_s));
5081 in->efunc = do_load_file;
5082 construct_input (_("Load Filename"), NULL, 1, 1,
5083 _("Type TAB for file browser"), file_browser, NULL, '\t',
5084 in, INPUT_HIST_FILE, NULL, -1);
5087 void
5088 do_global_save_game ()
5090 if (gtotal > 1)
5092 construct_message (NULL, _("What would you like to do?"), 0, 1,
5093 NULL, NULL, NULL, do_game_save_multi_confirm, 0, 0,
5094 NULL, _
5095 ("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."),
5096 current_wchar, all_wchar);
5097 return;
5100 do_get_game_save_input (-1);
5103 void
5104 do_global_new_game ()
5106 do_new_game ();
5109 void
5110 copy_game_common (int fen)
5112 int g = gindex;
5113 int i, n;
5114 struct userdata_s *d = gp->data;
5115 char *fentag = fen ? pgn_game_to_fen (gp, d->b) : NULL;
5117 do_global_new_game ();
5118 d = gp->data;
5119 n = pgn_tag_total (game[g]->tag);
5121 for (i = 0; i < n; i++)
5122 pgn_tag_add (&gp->tag, game[g]->tag[i]->name, game[g]->tag[i]->value);
5124 if (fentag)
5126 pgn_tag_add (&gp->tag, (char *) "SetUp", (char *) "1");
5127 pgn_tag_add (&gp->tag, (char *) "FEN", fentag);
5128 free (fentag);
5129 pgn_tag_sort (gp->tag);
5131 else
5133 pgn_board_init_fen (gp, d->b, NULL);
5134 n = pgn_history_total (game[g]->history);
5136 // FIXME RAV
5137 for (i = 0; i < n; i++)
5139 char *frfr = NULL;
5140 char *move = strdup (game[g]->history[i]->move);
5142 if (pgn_parse_move (gp, d->b, &move, &frfr) != E_PGN_OK)
5144 free (move);
5145 SET_FLAG (gp->flags, GF_PERROR);
5146 return;
5149 pgn_history_add (gp, d->b, move);
5150 free (move);
5151 free (frfr);
5152 pgn_switch_turn (gp);
5156 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
5159 void
5160 do_global_copy_game ()
5162 copy_game_common (0);
5165 void
5166 do_global_copy_game_fen ()
5168 copy_game_common (1);
5171 void
5172 do_global_new_all ()
5174 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
5175 do_new_game_from_scratch, 0, 0, NULL, "%s",
5176 _("Really start a new game from scratch?"));
5179 void
5180 do_quit (WIN * win)
5182 wchar_t str[] = { win->c, 0 };
5183 int n = wcscmp (str, yes_wchar);
5185 if (n)
5186 return;
5188 quit = 1;
5191 void
5192 do_global_quit ()
5194 if (config.exitdialogbox)
5195 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
5196 do_quit, 0, 0, NULL, "%s", _("Want to Quit?"));
5197 else
5198 quit = 1;
5201 void
5202 do_global_toggle_engine_window ()
5204 if (!enginew)
5206 enginew = newwin (LINES, COLS, 0, 0);
5207 enginep = new_panel (enginew);
5208 window_draw_title (enginew, _("Engine IO Window"), COLS,
5209 CP_MESSAGE_TITLE, CP_MESSAGE_BORDER);
5210 hide_panel (enginep);
5213 if (panel_hidden (enginep))
5215 update_engine_window (gp);
5216 top_panel (enginep);
5218 else
5220 hide_panel (enginep);
5224 void
5225 do_global_toggle_board_details ()
5227 do_board_details ();
5230 void
5231 do_play_toggle_strict_castling ()
5233 do_toggle_strict_castling ();
5236 // Global and other keys.
5237 static int
5238 globalkeys ()
5240 struct userdata_s *d = gp->data;
5241 int i;
5244 * These cannot be modified and other game mode keys cannot conflict with
5245 * these.
5247 switch (input_c)
5249 case KEY_ESCAPE:
5250 d->sp.icon = d->sp.srow = d->sp.scol = 0;
5251 markend = markstart = 0;
5253 if (keycount)
5255 keycount = 0;
5256 update_status_notify (gp, NULL);
5259 if (config.validmoves)
5260 pgn_reset_valid_moves (d->b);
5262 return 1;
5263 case '0' ... '9':
5264 i = input_c - '0';
5266 if (keycount)
5267 keycount = keycount * 10 + i;
5268 else
5269 keycount = i;
5271 update_status_notify (gp, _("Repeat %i"), keycount);
5272 return -1;
5273 case KEY_UP:
5274 if (d->mode == MODE_HISTORY)
5275 return 0;
5277 if (keycount)
5278 d->c_row += keycount;
5279 else
5280 d->c_row++;
5282 if (d->c_row > 8)
5283 d->c_row = 1;
5285 return 1;
5286 case KEY_DOWN:
5287 if (d->mode == MODE_HISTORY)
5288 return 0;
5290 if (keycount)
5292 d->c_row -= keycount;
5293 update_status_notify (gp, NULL);
5295 else
5296 d->c_row--;
5298 if (d->c_row < 1)
5299 d->c_row = 8;
5301 return 1;
5302 case KEY_LEFT:
5303 if (d->mode == MODE_HISTORY)
5304 return 0;
5306 if (keycount)
5307 d->c_col -= keycount;
5308 else
5309 d->c_col--;
5311 if (d->c_col < 1)
5312 d->c_col = 8;
5314 return 1;
5315 case KEY_RIGHT:
5316 if (d->mode == MODE_HISTORY)
5317 return 0;
5319 if (keycount)
5320 d->c_col += keycount;
5321 else
5322 d->c_col++;
5324 if (d->c_col > 8)
5325 d->c_col = 1;
5327 return 1;
5328 case KEY_RESIZE:
5329 return 1;
5330 case 0:
5331 default:
5332 for (i = 0; global_keys[i]; i++)
5334 if (input_c == global_keys[i]->c && global_keys[i]->f)
5336 (*global_keys[i]->f) ();
5337 return 1;
5340 break;
5343 return 0;
5346 #ifdef WITH_LIBPERL
5347 static void
5348 perl_error (const char *fmt, ...)
5350 va_list ap;
5351 char *buf;
5353 va_start (ap, fmt);
5354 vasprintf (&buf, fmt, ap);
5355 va_end (ap);
5357 message (ERROR_STR, ANY_KEY_STR, "%s", buf);
5358 free (buf);
5361 static void
5362 do_perl_finalize (WIN * win)
5364 struct input_data_s *in = win->data;
5365 GAME g = in->data;
5366 struct userdata_s *d = g->data;
5367 char *filename;
5368 char *result = NULL;
5369 char *arg = NULL;
5370 int n;
5372 asprintf (&filename, "%s/perl.pl", config.datadir);
5374 if (!in->str)
5375 goto done;
5377 if (perl_init_file (filename, perl_error))
5378 goto done;
5380 arg = pgn_game_to_fen (g, d->b);
5382 if (perl_call_sub (trim (in->str), arg, &result))
5383 goto done;
5385 d->perlfen = pgn_game_to_fen (g, d->b);
5386 d->perlflags = g->flags;
5388 if (pgn_board_init_fen (g, d->b, result) != E_PGN_OK)
5390 message (ERROR_STR, ANY_KEY_STR, "%s", _("FEN parse error."));
5391 pgn_board_init_fen (g, d->b, d->perlfen);
5392 g->flags = d->perlflags;
5393 free (d->perlfen);
5394 d->perlfen = NULL;
5395 goto done;
5398 SET_FLAG (d->flags, CF_PERL);
5399 n = pgn_tag_find (g->tag, "FEN");
5401 if (n != E_PGN_ERR)
5402 d->oldfen = strdup (g->tag[n]->value);
5404 pgn_tag_add (&g->tag, (char *) "FEN", result);
5405 update_status_notify (g, "%s", ANY_KEY_STR);
5406 update_all (g);
5408 done:
5409 free (result);
5410 free (arg);
5411 free (in->str);
5412 free (in);
5413 free (filename);
5416 void
5417 do_global_perl ()
5419 struct input_data_s *in;
5421 in = Calloc (1, sizeof (struct input_data_s));
5422 in->data = gp;
5423 in->efunc = do_perl_finalize;
5424 construct_input (_("PERL Subroutine Filter"), NULL, 1, 0, NULL, NULL, NULL,
5425 0, in, INPUT_HIST_PERL, NULL, -1);
5427 #endif
5430 * A macro may contain a key that belongs to another macro so macro_match will
5431 * need to be updated to the new index of the matching macro.
5433 static void
5434 find_macro (struct userdata_s *d)
5436 int i;
5439 * Macros can't contain macros when in a window.
5441 if (wins)
5442 return;
5444 again:
5445 for (i = 0; macros[i]; i++)
5447 if ((macros[i]->mode == -1 || macros[i]->mode == d->mode) &&
5448 input_c == macros[i]->c)
5450 input_c = macros[i]->keys[macros[i]->n++];
5452 if (!macro_depth_n && macro_match > -1)
5454 macro_depth =
5455 realloc (macro_depth, (macro_depth_n + 1) * sizeof (int));
5456 macro_depth[macro_depth_n++] = macro_match;
5459 macro_depth =
5460 realloc (macro_depth, (macro_depth_n + 1) * sizeof (int));
5461 macro_depth[macro_depth_n++] = i;
5462 macro_match = i;
5463 goto again;
5469 * Resets the position in each macro to the first key.
5471 static void
5472 reset_macros ()
5474 int i;
5475 struct userdata_s *d = gp->data;
5477 again:
5478 if (macro_depth_n > 0)
5480 macro_depth_n--;
5481 macro_match = macro_depth[macro_depth_n];
5483 if (macros[macro_match]->n >= macros[macro_match]->total)
5484 goto again;
5486 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
5487 find_macro (d);
5488 return;
5491 for (i = 0; macros[i]; i++)
5492 macros[i]->n = 0;
5494 free (macro_depth);
5495 macro_depth = NULL;
5496 macro_depth_n = 0;
5497 macro_match = -1;
5500 void
5501 game_loop ()
5503 struct userdata_s *d;
5505 macro_match = -1;
5506 gindex = gtotal - 1;
5507 gp = game[gindex];
5508 d = gp->data;
5510 if (pgn_history_total (gp->hp))
5511 d->mode = MODE_HISTORY;
5512 else
5514 d->mode = MODE_PLAY;
5515 d->play_mode = PLAY_HE;
5518 d->rotate = FALSE;
5519 d->go_move = 0;
5520 d->pm_undo = FALSE;
5521 d->pm_frfr[0] = '\0';
5523 if (d->mode == MODE_HISTORY)
5524 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
5526 update_status_notify (gp, _("Type %ls for help"),
5527 key_lookup (global_keys, do_global_help));
5528 movestep = 2;
5529 flushinp ();
5530 update_all (gp);
5531 wtimeout (boardw, WINDOW_TIMEOUT);
5533 while (!quit)
5535 int n = 0, i;
5536 char fdbuf[8192] = { 0 };
5537 int len;
5538 struct timeval tv = { 0, 0 };
5539 fd_set rfds, wfds;
5540 WIN *win = NULL;
5541 WINDOW *wp = NULL;
5543 FD_ZERO (&rfds);
5544 FD_ZERO (&wfds);
5546 for (i = 0; i < gtotal; i++)
5548 d = game[i]->data;
5550 if (d->engine && d->engine->pid != -1)
5552 if (d->engine->fd[ENGINE_IN_FD] > 2)
5554 if (d->engine->fd[ENGINE_IN_FD] > n)
5555 n = d->engine->fd[ENGINE_IN_FD];
5557 FD_SET (d->engine->fd[ENGINE_IN_FD], &rfds);
5560 if (d->engine->fd[ENGINE_OUT_FD] > 2)
5562 if (d->engine->fd[ENGINE_OUT_FD] > n)
5563 n = d->engine->fd[ENGINE_OUT_FD];
5565 FD_SET (d->engine->fd[ENGINE_OUT_FD], &wfds);
5570 if (n)
5572 if ((n = select (n + 1, &rfds, &wfds, NULL, &tv)) > 0)
5574 for (i = 0; i < gtotal; i++)
5576 d = game[i]->data;
5578 if (d->engine && d->engine->pid != -1)
5580 if (FD_ISSET (d->engine->fd[ENGINE_IN_FD], &rfds))
5582 len = read (d->engine->fd[ENGINE_IN_FD], fdbuf,
5583 sizeof (fdbuf));
5585 if (len > 0)
5587 if (d->engine->iobuf)
5588 d->engine->iobuf =
5589 Realloc (d->engine->iobuf,
5590 d->engine->len + len + 1);
5591 else
5592 d->engine->iobuf = Calloc (1, len + 1);
5594 memcpy (&(d->engine->iobuf[d->engine->len]),
5595 &fdbuf, len);
5596 d->engine->len += len;
5597 d->engine->iobuf[d->engine->len] = 0;
5600 * The fdbuf is full or no newline
5601 * was found. So we'll append the next
5602 * read() to this games buffer.
5604 if (d->engine->iobuf[d->engine->len - 1] !=
5605 '\n')
5606 continue;
5608 parse_engine_output (game[i], d->engine->iobuf);
5609 free (d->engine->iobuf);
5610 d->engine->iobuf = NULL;
5611 d->engine->len = 0;
5613 else if (len == -1)
5615 if (errno != EAGAIN)
5617 cmessage (ERROR_STR, ANY_KEY_STR,
5618 "Engine read(): %s",
5619 strerror (errno));
5620 waitpid (d->engine->pid, &n, 0);
5621 free (d->engine);
5622 d->engine = NULL;
5623 break;
5628 if (FD_ISSET (d->engine->fd[ENGINE_OUT_FD], &wfds))
5630 if (d->engine->queue)
5631 send_engine_command (game[i]);
5636 else
5638 if (n == -1)
5639 cmessage (ERROR_STR, ANY_KEY_STR, "select(): %s",
5640 strerror (errno));
5641 /* timeout */
5645 gp = game[gindex];
5646 d = gp->data;
5649 * This is needed to detect terminal resizing.
5651 doupdate ();
5652 if (LINES != LINES_OLD || COLS != COLS_OLD)
5654 COLS_OLD = COLS;
5655 LINES_OLD = LINES;
5656 do_window_resize ();
5660 * Finds the top level window in the window stack so we know what
5661 * window the wget_wch()'ed key belongs to.
5663 if (wins)
5665 for (i = 0; wins[i]; i++);
5666 win = wins[i - 1];
5667 wp = win->w;
5668 wtimeout (wp, WINDOW_TIMEOUT);
5670 else
5671 wp = boardw;
5673 if (!i && pushkey)
5674 input_c = pushkey;
5675 else
5677 if (!pushkey)
5679 if (macros && macro_match >= 0)
5681 if (macros[macro_match]->n >= macros[macro_match]->total)
5682 reset_macros ();
5683 else
5685 input_c =
5686 macros[macro_match]->keys[macros[macro_match]->n++];
5687 find_macro (d);
5690 else
5692 if (wget_wch (wp, &input_c) == ERR || input_c == KEY_RESIZE)
5694 if (input_c == KEY_RESIZE)
5696 if (win)
5697 win->c = input_c;
5699 window_resize_all ();
5700 update_all (gp);
5702 continue;
5706 else
5707 input_c = pushkey;
5709 if (win)
5711 win->c = input_c;
5714 * Run the function associated with the window. When the
5715 * function returns 0 win->efunc is ran (if not NULL) with
5716 * win as the one and only parameter. Then the window is
5717 * destroyed.
5719 * The exit function may create another window which will
5720 * mess up the window stack when window_destroy() is called.
5721 * So don't destory the window until the top window is
5722 * destroyable. See window_destroy().
5724 if ((*win->func) (win) == 0)
5726 if (win->efunc)
5727 (*win->efunc) (win);
5729 win->keep = 1;
5730 window_destroy (win);
5731 update_all (gp);
5734 continue;
5738 if (!keycount && status.notify)
5739 update_status_notify (gp, NULL);
5741 #ifdef WITH_LIBPERL
5742 if (TEST_FLAG (d->flags, CF_PERL))
5744 CLEAR_FLAG (d->flags, CF_PERL);
5745 pgn_board_init_fen (gp, d->b, d->perlfen);
5746 gp->flags = d->perlflags;
5747 free (d->perlfen);
5748 pgn_tag_add (&gp->tag, (char *) "FEN", d->oldfen);
5749 free (d->oldfen);
5750 d->perlfen = d->oldfen = NULL;
5751 update_all (gp);
5752 continue;
5754 #endif
5756 if (macros && macro_match < 0)
5757 find_macro (d);
5759 if ((n = globalkeys ()) == 1)
5761 if (macro_match == -1)
5762 keycount = 0;
5764 goto refresh;
5766 else if (n == -1)
5767 goto refresh;
5769 switch (d->mode)
5771 case MODE_EDIT:
5772 for (i = 0; edit_keys[i]; i++)
5774 if (input_c == edit_keys[i]->c)
5776 (*edit_keys[i]->f) ();
5777 break;
5780 break;
5781 case MODE_PLAY:
5782 for (i = 0; play_keys[i]; i++)
5784 if (input_c == play_keys[i]->c)
5786 (*play_keys[i]->f) ();
5787 goto done;
5791 do_play_config_command ();
5792 break;
5793 case MODE_HISTORY:
5794 for (i = 0; history_keys[i]; i++)
5796 if (input_c == history_keys[i]->c)
5798 (*history_keys[i]->f) ();
5799 break;
5802 break;
5803 default:
5804 break;
5807 done:
5808 if (keycount)
5809 update_status_notify (gp, NULL);
5811 keycount = 0;
5813 refresh:
5814 update_all (gp);
5818 void
5819 usage (const char *pn, int ret)
5821 fprintf ((ret) ? stderr : stdout, "%s%s",
5822 #ifdef DEBUG
5823 _("Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
5824 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"),
5825 #else
5826 _("Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"),
5827 #endif
5828 _(" -p Load PGN file.\n"
5829 " -V Validate a game file.\n"
5830 " -S Validate and output a PGN formatted game.\n"
5831 " -R Like -S but write a reduced PGN formatted game.\n"
5832 " -t Also write custom PGN tags from config file.\n"
5833 " -E Stop processing on file parsing error (overrides config).\n"
5834 " -C Enable strict castling (overrides config).\n"
5835 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, overrides config).\n"
5836 " -v Version information.\n" " -h This help text.\n"));
5838 exit (ret);
5841 void
5842 cleanup_all ()
5844 int i;
5846 stop_clock ();
5847 free_userdata ();
5848 pgn_free_all ();
5849 free (config.engine_cmd);
5850 free (config.pattern);
5851 free (config.ccfile);
5852 free (config.nagfile);
5853 free (config.configfile);
5855 if (config.keys)
5857 for (i = 0; config.keys[i]; i++)
5859 free (config.keys[i]->str);
5860 free (config.keys[i]);
5863 free (config.keys);
5866 if (config.einit)
5868 for (i = 0; config.einit[i]; i++)
5869 free (config.einit[i]);
5871 free (config.einit);
5874 if (config.tag)
5875 pgn_tag_free (config.tag);
5877 free (config.datadir);
5879 if (curses_initialized)
5881 del_panel (boardp);
5882 del_panel (historyp);
5883 del_panel (statusp);
5884 del_panel (tagp);
5885 delwin (boardw);
5886 delwin (historyw);
5887 delwin (statusw);
5888 delwin (tagw);
5890 if (enginew)
5892 del_panel (enginep);
5893 delwin (enginew);
5896 endwin ();
5899 #ifdef WITH_LIBPERL
5900 perl_cleanup ();
5901 #endif
5904 static void
5905 signal_save_pgn (int sig)
5907 char *buf;
5908 time_t now;
5909 char *p = config.savedirectory ? config.savedirectory : config.datadir;
5911 time (&now);
5912 asprintf (&buf, "%s/signal-%i-%li.pgn", p, sig, now);
5914 if (do_game_write (buf, "w", 0, gtotal))
5916 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", p, strerror (errno));
5917 update_status_notify (gp, "%s", _("Save game failed."));
5920 free (buf);
5921 quit = 1;
5924 void
5925 catch_signal (int which, siginfo_t *info, void *ctx)
5927 (void)info;
5928 (void)ctx;
5930 switch (which)
5932 case SIGALRM:
5933 update_clocks ();
5934 break;
5935 case SIGPIPE:
5936 if (which == SIGPIPE && quit)
5937 break;
5939 if (which == SIGPIPE)
5940 cmessage (NULL, ANY_KEY_STR, "%s", _("Broken pipe. Quitting."));
5942 cleanup_all ();
5943 exit (EXIT_FAILURE);
5944 break;
5945 case SIGSTOP:
5946 savetty ();
5947 break;
5948 case SIGCONT:
5949 resetty ();
5950 do_window_resize ();
5951 keypad (boardw, TRUE);
5952 break;
5953 case SIGINT:
5954 quit = 1;
5955 break;
5956 case SIGTERM:
5957 signal_save_pgn (which);
5958 break;
5959 default:
5960 break;
5964 void
5965 loading_progress (long total, long offset)
5967 int n = (100 * (offset / 100) / (total / 100));
5969 if (curses_initialized)
5970 update_loading_window (n);
5971 else
5973 fprintf (stderr, _("Loading... %i%% (%i games)%c"), n, gtotal, '\r');
5974 fflush (stderr);
5978 static void
5979 set_defaults ()
5981 set_config_defaults ();
5982 set_default_keys ();
5983 filetype = FILE_NONE;
5984 pgn_config_set (PGN_PROGRESS, 1024);
5985 pgn_config_set (PGN_PROGRESS_FUNC, loading_progress);
5989 main (int argc, char *argv[])
5991 int opt;
5992 struct stat st;
5993 char buf[FILENAME_MAX];
5994 char datadir[FILENAME_MAX];
5995 int ret = EXIT_SUCCESS;
5996 int validate_only = 0, validate_and_write = 0;
5997 int write_custom_tags = 0;
5998 int i = 0;
5999 PGN_FILE *pgn;
6000 int utf8_pieces = -1;
6001 struct sigaction sigact;
6003 setlocale (LC_ALL, "");
6004 bindtextdomain ("cboard", LOCALE_DIR);
6005 textdomain ("cboard");
6007 /* Solaris 5.9 */
6008 #ifndef HAVE_PROGNAME
6009 __progname = argv[0];
6010 #endif
6012 if ((config.pwd = getpwuid (getuid ())) == NULL)
6013 err (EXIT_FAILURE, "getpwuid()");
6015 snprintf (datadir, sizeof (datadir), "%s/.cboard", config.pwd->pw_dir);
6016 config.datadir = strdup (datadir);
6017 snprintf (buf, sizeof (buf), "%s/cc.data", datadir);
6018 config.ccfile = strdup (buf);
6019 snprintf (buf, sizeof (buf), "%s/nag.data", datadir);
6020 config.nagfile = strdup (buf);
6021 snprintf (buf, sizeof (buf), "%s/config", datadir);
6022 config.configfile = strdup (buf);
6024 if (stat (datadir, &st) == -1)
6026 if (errno == ENOENT)
6028 if (mkdir (datadir, 0755) == -1)
6029 err (EXIT_FAILURE, "%s", datadir);
6031 else
6032 err (EXIT_FAILURE, "%s", datadir);
6034 stat (datadir, &st);
6037 if (!S_ISDIR (st.st_mode))
6038 errx (EXIT_FAILURE, "%s: %s", datadir, _("Not a directory."));
6040 set_defaults ();
6042 #ifdef DEBUG
6043 while ((opt = getopt (argc, argv, "DCEVtSRhp:vu::")) != -1)
6045 #else
6046 while ((opt = getopt (argc, argv, "ECVtSRhp:vu::")) != -1)
6048 #endif
6049 switch (opt)
6051 #ifdef DEBUG
6052 case 'D':
6053 unlink ("libchess.debug");
6054 pgn_config_set (PGN_DEBUG, 1);
6055 break;
6056 #endif
6057 case 'C':
6058 pgn_config_set (PGN_STRICT_CASTLING, 1);
6059 break;
6060 case 't':
6061 write_custom_tags = 1;
6062 break;
6063 case 'E':
6064 i = 1;
6065 break;
6066 case 'R':
6067 pgn_config_set (PGN_REDUCED, 1);
6068 case 'S':
6069 validate_and_write = 1;
6070 case 'V':
6071 validate_only = 1;
6072 break;
6073 case 'v':
6074 printf ("%s (%s)\n%s\n%s\n", PACKAGE_STRING, curses_version (),
6075 COPYRIGHT, CBOARD_URL);
6076 exit (EXIT_SUCCESS);
6077 case 'p':
6078 filetype = FILE_PGN;
6079 strncpy (loadfile, optarg, sizeof (loadfile));
6080 loadfile[sizeof (loadfile) - 1] = 0;
6081 break;
6082 case 'u':
6083 utf8_pieces = optarg ? atoi (optarg) : 1;
6084 break;
6085 case 'h':
6086 default:
6087 usage (argv[0], EXIT_SUCCESS);
6091 if ((validate_only || validate_and_write) && !*loadfile)
6092 usage (argv[0], EXIT_FAILURE);
6094 if (access (config.configfile, R_OK) == 0)
6095 parse_rcfile (config.configfile);
6097 if (i)
6098 pgn_config_set (PGN_STOP_ON_ERROR, 1);
6100 memset (&sigact, 0, sizeof (sigact));
6101 sigact.sa_flags = SA_SIGINFO;
6102 sigact.sa_sigaction = catch_signal;
6103 sigaction (SIGPIPE, &sigact, NULL);
6104 sigaction (SIGCONT, &sigact, NULL);
6105 sigaction (SIGSTOP, &sigact, NULL);
6106 sigaction (SIGINT, &sigact, NULL);
6107 sigaction (SIGALRM, &sigact, NULL);
6108 sigaction (SIGTERM, &sigact, NULL);
6109 signal (SIGCHLD, SIG_IGN);
6111 srandom (getpid ());
6113 switch (filetype)
6115 case FILE_PGN:
6116 if (pgn_open (loadfile, "r", &pgn) != E_PGN_OK)
6117 err (EXIT_FAILURE, "%s", loadfile);
6119 ret = pgn_parse (pgn);
6120 pgn_close (pgn);
6121 break;
6122 case FILE_FEN:
6123 //ret = parse_fen_file(loadfile);
6124 break;
6125 case FILE_EPD: // Not implemented.
6126 case FILE_NONE:
6127 default:
6128 // No file specified. Empty game.
6129 ret = pgn_parse (NULL);
6130 gp = game[gindex];
6131 add_custom_tags (&gp->tag);
6132 break;
6135 if (validate_only || validate_and_write)
6137 if (validate_and_write)
6139 if (pgn_open ("-", "r", &pgn) != E_PGN_OK)
6140 err (EXIT_FAILURE, "pgn_open()");
6142 for (i = 0; i < gtotal; i++)
6144 if (write_custom_tags)
6145 add_custom_tags (&game[i]->tag);
6147 pgn_write (pgn, game[i]);
6150 pgn_close (pgn);
6152 fm_loaded_file = TRUE;
6155 cleanup_all ();
6156 exit (ret);
6158 else if (ret == E_PGN_ERR)
6159 exit (ret);
6161 if (utf8_pieces != -1)
6162 config.utf8_pieces = utf8_pieces;
6164 init_wchar_pieces ();
6165 yes_wchar = str_to_wchar (_("y"));
6166 all_wchar = str_to_wchar (_("a"));
6167 overwrite_wchar = str_to_wchar (_("o"));
6168 resume_wchar = str_to_wchar (_("r"));
6169 current_wchar = str_to_wchar (_("c"));
6170 append_wchar = str_to_wchar (_("a"));
6171 translatable_tag_names[0] = _("Event");
6172 translatable_tag_names[1] = _("Site");
6173 translatable_tag_names[2] = _("Date");
6174 translatable_tag_names[3] = _("Round");
6175 translatable_tag_names[4] = _("White");
6176 translatable_tag_names[5] = _("Black");
6177 translatable_tag_names[6] = _("Result");
6178 init_userdata ();
6181 * This fixes window resizing in an xterm.
6183 if (getenv ("DISPLAY") != NULL)
6185 putenv ((char *) "LINES=");
6186 putenv ((char *) "COLUMNS=");
6189 if (initscr () == NULL)
6190 errx (EXIT_FAILURE, "%s", _("Could not initialize curses."));
6191 else
6192 curses_initialized = 1;
6194 if (LINES < 23 || COLS < 74)
6196 endwin ();
6197 errx (EXIT_FAILURE, _("Need at least an 74x23 terminal."));
6200 COLS_OLD = COLS;
6201 LINES_OLD = LINES;
6203 if (has_colors () == TRUE && start_color () == OK)
6204 init_color_pairs ();
6206 boardw = newwin (BOARD_HEIGHT, BOARD_WIDTH, 0, COLS - BOARD_WIDTH);
6207 boardp = new_panel (boardw);
6208 historyw = newwin (HISTORY_HEIGHT, HISTORY_WIDTH, LINES - HISTORY_HEIGHT,
6209 COLS - HISTORY_WIDTH);
6210 historyp = new_panel (historyw);
6211 statusw = newwin (STATUS_HEIGHT, STATUS_WIDTH, 0, 0);
6212 statusp = new_panel (statusw);
6213 tagw = newwin (TAG_HEIGHT, TAG_WIDTH, STATUS_HEIGHT + 1, 0);
6214 tagp = new_panel (tagw);
6215 keypad (boardw, TRUE);
6216 // leaveok(boardw, TRUE);
6217 leaveok (tagw, TRUE);
6218 leaveok (statusw, TRUE);
6219 leaveok (historyw, TRUE);
6220 curs_set (0);
6221 cbreak ();
6222 noecho ();
6223 draw_window_decor ();
6224 game_loop ();
6225 cleanup_all ();
6226 free (w_pawn_wchar);
6227 free (w_rook_wchar);
6228 free (w_bishop_wchar);
6229 free (w_knight_wchar);
6230 free (w_queen_wchar);
6231 free (w_king_wchar);
6232 free (b_pawn_wchar);
6233 free (b_rook_wchar);
6234 free (b_bishop_wchar);
6235 free (b_knight_wchar);
6236 free (b_queen_wchar);
6237 free (b_king_wchar);
6238 free (empty_wchar);
6239 free (enpassant_wchar);
6240 free (yes_wchar);
6241 free (all_wchar);
6242 free (overwrite_wchar);
6243 free (resume_wchar);
6244 free (current_wchar);
6245 free (append_wchar);
6246 free (status.notify);
6247 exit (EXIT_SUCCESS);