edit: fix piece insert prompt.
[cboard.git] / src / cboard.c
blob06875508032e39c9d3ac099fbaf399c002b3c460
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 && rank == 5) ? 6 :
846 (d->sp.srow == 2 && 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 pgn_reset_enpassant (d->b);
3652 d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].enpassant = 1;
3656 static void
3657 do_edit_help ()
3659 wchar_t *buf = build_help (edit_keys);
3661 construct_message (_("Edit Mode Keys (* = can take a repeat count)"),
3662 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
3663 0, 1, NULL, "%ls", buf);
3666 void
3667 do_edit_exit ()
3669 struct userdata_s *d = gp->data;
3670 char *fen = pgn_game_to_fen (gp, d->b);
3672 config.details--;
3673 pgn_tag_add (&gp->tag, (char *) "FEN", fen);
3674 free (fen);
3675 pgn_tag_add (&gp->tag, (char *) "SetUp", (char *) "1");
3676 pgn_tag_sort (gp->tag);
3677 pgn_board_update (gp, d->b, gp->hindex);
3678 d->mode = MODE_PLAY;
3681 void
3682 really_do_annotate_finalize (struct input_data_s *in, struct userdata_s *d)
3684 HISTORY *h = in->data;
3685 int len;
3687 if (!in->str)
3689 if (h->comment)
3691 free (h->comment);
3692 h->comment = NULL;
3695 else
3697 len = strlen (in->str);
3698 h->comment = Realloc (h->comment, len + 1);
3699 strncpy (h->comment, in->str, len);
3700 h->comment[len] = 0;
3703 free (in->str);
3704 free (in);
3705 SET_FLAG (d->flags, CF_MODIFIED);
3708 void
3709 do_annotate_finalize (WIN * win)
3711 struct userdata_s *d = gp->data;
3712 struct input_data_s *in = win->data;
3714 really_do_annotate_finalize (in, d);
3717 void
3718 do_find_move_exp_finalize (int init, int which)
3720 int n;
3721 struct userdata_s *d = gp->data;
3722 static int firstrun;
3723 static regex_t r;
3724 int ret;
3725 char errbuf[255];
3727 if (init || !firstrun)
3729 if (!firstrun)
3730 regfree (&r);
3732 if ((ret = regcomp (&r, moveexp, REG_EXTENDED | REG_NOSUB)) != 0)
3734 regerror (ret, &r, errbuf, sizeof (errbuf));
3735 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR,
3736 "%s", errbuf);
3737 return;
3740 firstrun = 1;
3743 if ((n = find_move_exp (gp, r,
3744 (which == -1) ? 0 : 1,
3745 (keycount) ? keycount : 1)) == -1)
3746 return;
3748 gp->hindex = n;
3749 pgn_board_update (gp, d->b, gp->hindex);
3752 void
3753 do_find_move_exp (WIN * win)
3755 struct input_data_s *in = win->data;
3756 int *n = in->data;
3757 int which = *n;
3759 if (in->str)
3761 strncpy (moveexp, in->str, sizeof (moveexp) - 1);
3762 moveexp[sizeof (moveexp) - 1] = 0;
3763 do_find_move_exp_finalize (1, which);
3764 free (in->str);
3767 free (in->data);
3768 free (in);
3771 void
3772 do_move_jump_finalize (int n)
3774 struct userdata_s *d = gp->data;
3776 if (n < 0 || n > (pgn_history_total (gp->hp) / 2))
3777 return;
3779 keycount = 0;
3780 update_status_notify (gp, NULL);
3781 gp->hindex = (n) ? n * 2 - 1 : n * 2;
3782 pgn_board_update (gp, d->b, gp->hindex);
3785 void
3786 do_move_jump (WIN * win)
3788 struct input_data_s *in = win->data;
3790 if (!in->str || !isinteger (in->str))
3792 if (in->str)
3793 free (in->str);
3795 free (in);
3796 return;
3799 do_move_jump_finalize (atoi (in->str));
3800 free (in->str);
3801 free (in);
3804 struct history_menu_s
3806 char *line;
3807 int hindex;
3808 int ravlevel;
3809 int move;
3810 int indent;
3813 void
3814 free_history_menu_data (struct history_menu_s **h)
3816 int i;
3818 if (!h)
3819 return;
3821 for (i = 0; h[i]; i++)
3823 free (h[i]->line);
3824 free (h[i]);
3827 free (h);
3830 void
3831 get_history_data (HISTORY ** hp, struct history_menu_s ***menu, int m,
3832 int turn)
3834 int i, n = 0;
3835 int t = pgn_history_total (hp);
3836 char buf[MAX_SAN_MOVE_LEN + 4];
3837 static int depth;
3838 struct history_menu_s **hmenu = *menu;
3840 if (hmenu)
3841 for (n = 0; hmenu[n]; n++);
3842 else
3843 depth = 0;
3845 for (i = 0; i < t; i++)
3847 hmenu = Realloc (hmenu, (n + 2) * sizeof (struct history_menu_s *));
3848 hmenu[n] = Malloc (sizeof (struct history_menu_s));
3849 snprintf (buf, sizeof (buf), "%c%s%s", (turn == WHITE) ? 'W' : 'B',
3850 hp[i]->move, (hp[i]->comment || hp[i]->nag[0]) ? " !" : "");
3851 hmenu[n]->line = strdup (buf);
3852 hmenu[n]->hindex = i;
3853 hmenu[n]->indent = 0;
3854 hmenu[n]->ravlevel = depth;
3855 hmenu[n]->move = (n && depth > hmenu[n - 1]->ravlevel) ? m++ : m;
3856 n++;
3857 hmenu[n] = NULL;
3859 #if 0
3860 if (hp[i]->rav)
3862 depth++;
3863 get_history_data (hp[i]->rav, &hmenu, m, turn);
3864 for (n = 0; hmenu[n]; n++);
3865 depth--;
3867 if (depth)
3868 m--;
3870 #endif
3872 turn = (turn == WHITE) ? BLACK : WHITE;
3875 *menu = hmenu;
3878 void
3879 history_draw_update (struct menu_input_s *m)
3881 GAME g = m->data;
3882 struct userdata_s *d = g->data;
3884 g->hindex = m->selected + 1;
3885 update_cursor (g, m->selected);
3886 pgn_board_update (g, d->b, m->selected + 1);
3889 struct menu_item_s **
3890 get_history_items (WIN * win)
3892 struct menu_input_s *m = win->data;
3893 GAME g = m->data;
3894 struct userdata_s *d = g->data;
3895 struct history_menu_s **hm = d->data;
3896 struct menu_item_s **items = m->items;
3897 int i;
3899 if (!hm)
3901 get_history_data (g->history, &hm, 0,
3902 TEST_FLAG (g->flags, GF_BLACK_OPENING));
3903 m->selected = g->hindex - 1;
3905 if (m->selected < 0)
3906 m->selected = 0;
3908 m->draw_exit_func = history_draw_update;
3911 d->data = hm;
3913 if (items)
3915 for (i = 0; items[i]; i++)
3916 free (items[i]);
3918 free (items);
3919 items = NULL;
3922 for (i = 0; hm[i]; i++)
3924 items = Realloc (items, (i + 2) * sizeof (struct menu_item_s *));
3925 items[i] = Malloc (sizeof (struct menu_item_s));
3926 items[i]->name = hm[i]->line;
3927 items[i]->value = NULL;
3928 items[i]->selected = 0;
3931 if (items)
3932 items[i] = NULL;
3934 m->nofree = 1;
3935 m->items = items;
3936 return items;
3939 void
3940 history_menu_quit (struct menu_input_s *m)
3942 pushkey = -1;
3945 void
3946 history_menu_exit (WIN * win)
3948 GAME g = win->data;
3949 struct userdata_s *d = g->data;
3950 struct history_menu_s **hm = d->data;
3951 int i;
3953 if (!hm)
3954 return;
3956 for (i = 0; hm[i]; i++)
3958 free (hm[i]->line);
3959 free (hm[i]);
3962 free (hm);
3963 d->data = NULL;
3966 // FIXME RAV
3967 void
3968 history_menu_next (struct menu_input_s *m)
3970 GAME g = m->data;
3971 struct userdata_s *d = g->data;
3972 struct history_menu_s **hm = d->data;
3973 int n, t;
3975 for (t = 0; hm[t]; t++);
3977 if (m->selected + 1 == t)
3978 n = 0;
3979 else
3980 n = hm[m->selected + 1]->hindex;
3982 n++;
3983 g->hindex = n;
3986 // FIXME RAV
3987 void
3988 history_menu_prev (struct menu_input_s *m)
3990 GAME g = m->data;
3991 struct userdata_s *d = g->data;
3992 struct history_menu_s **hm = d->data;
3993 int n, t;
3995 for (t = 0; hm[t]; t++);
3997 if (m->selected - 1 < 0)
3998 n = t - 1;
3999 else
4000 n = hm[m->selected - 1]->hindex;
4002 n++;
4003 g->hindex = n;
4006 void
4007 history_menu_help (struct menu_input_s *m)
4009 message (_("History Menu Help"), ANY_KEY_STR, "%s",
4010 _(" UP/DOWN - previous/next menu item\n"
4011 " HOME/END - first/last menu item\n"
4012 " PGDN/PGUP - next/previous page\n"
4013 " a-zA-Z0-9 - jump to item\n"
4014 " CTRL-a - annotate the selected move\n"
4015 " ENTER - view annotation\n"
4016 " CTRL-d - toggle board details\n"
4017 " ESCAPE/M - return to move history"));
4020 void
4021 do_annotate_move (HISTORY * hp)
4023 char buf[COLS - 4];
4024 struct input_data_s *in;
4026 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Editing Annotation for"),
4027 hp->move);
4028 in = Calloc (1, sizeof (struct input_data_s));
4029 in->data = hp;
4030 in->efunc = do_annotate_finalize;
4031 construct_input (buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
4032 _("Type CTRL-t to edit NAG"), edit_nag, NULL,
4033 CTRL_KEY ('T'), in, -1, NULL, -1);
4036 void
4037 history_menu_view_annotation (struct menu_input_s *m)
4039 GAME g = m->data;
4041 // FIXME RAV
4042 view_annotation (g->history[m->selected]);
4045 void
4046 history_menu_annotate_finalize (WIN * win)
4048 struct input_data_s *in = win->data;
4049 GAME g = in->moredata;
4050 struct userdata_s *d = g->data;
4051 struct history_menu_s **hm = d->data;
4053 really_do_annotate_finalize (in, d);
4054 free_history_menu_data (hm);
4055 hm = NULL;
4056 get_history_data (g->history, &hm, 0,
4057 TEST_FLAG (g->flags, GF_BLACK_OPENING));
4058 d->data = hm;
4059 pushkey = REFRESH_MENU;
4062 void
4063 history_menu_annotate (struct menu_input_s *m)
4065 GAME g = m->data;
4066 char buf[COLS - 4];
4067 struct input_data_s *in;
4068 HISTORY *hp = g->history[m->selected]; // FIXME RAV
4070 snprintf (buf, sizeof (buf), "%s \"%s\"", _("Editing Annotation for"),
4071 hp->move);
4072 in = Calloc (1, sizeof (struct input_data_s));
4073 in->data = hp;
4074 in->moredata = m->data;
4075 in->efunc = history_menu_annotate_finalize;
4076 construct_input (buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
4077 _("Type CTRL-t to edit NAG"), edit_nag, NULL,
4078 CTRL_KEY ('T'), in, -1, NULL, -1);
4081 void
4082 history_menu_details (struct menu_input_s *m)
4084 do_board_details ();
4087 // FIXME RAV
4088 void
4089 history_menu_print (WIN * win)
4091 struct menu_input_s *m = win->data;
4092 GAME g = m->data;
4093 struct userdata_s *d = g->data;
4094 struct history_menu_s **hm = d->data;
4095 struct history_menu_s *h = hm[m->top];
4096 int i;
4097 char *p = m->item->name;
4098 int line = m->print_line - 2;
4100 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
4101 * attr_t data type.
4103 attr_t attrs;
4104 short pair;
4105 int total;
4107 for (total = 0; hm[total]; total++);
4108 wattr_get (win->w, &attrs, &pair, NULL);
4109 wattroff (win->w, COLOR_PAIR (pair));
4110 mvwaddch (win->w, m->print_line, 1,
4111 *p == 'W' ? *p | mix_cp (CP_BOARD_WHITE, CP_HISTORY_WINDOW,
4112 ATTRS (CP_BOARD_WHITE),
4113 A_FG_B_BG) : *p | mix_cp (CP_BOARD_BLACK,
4114 CP_HISTORY_WINDOW,
4115 ATTRS
4116 (CP_BOARD_BLACK),
4117 A_FG_B_BG));
4118 p++;
4120 if (h->hindex == 0 && line == 0)
4121 waddch (win->w, ACS_ULCORNER | CP_HISTORY_MENU_LG);
4122 else if ((!hm[h->hindex + (win->rows - 5) + 1] && line == win->rows - 5) ||
4123 (m->top + line == total - 1))
4124 waddch (win->w, ACS_LLCORNER | CP_HISTORY_MENU_LG);
4125 else if (hm[m->top + 1]->ravlevel != h->ravlevel || !h->ravlevel)
4126 waddch (win->w, ACS_LTEE | CP_HISTORY_MENU_LG);
4127 else
4128 waddch (win->w, ACS_VLINE | CP_HISTORY_MENU_LG);
4130 wattron (win->w, COLOR_PAIR (pair) | attrs);
4132 for (i = 2; *p; p++, i++)
4133 waddch (win->w, (*p == '!') ? *p | A_BOLD : *p);
4135 while (i++ < win->cols - 2)
4136 waddch (win->w, ' ');
4139 void
4140 history_menu (GAME g)
4142 struct menu_key_s **keys = NULL;
4144 add_menu_key (&keys, KEY_ESCAPE, history_menu_quit);
4145 add_menu_key (&keys, 'M', history_menu_quit);
4146 add_menu_key (&keys, KEY_UP, history_menu_prev);
4147 add_menu_key (&keys, KEY_DOWN, history_menu_next);
4148 add_menu_key (&keys, keycode_lookup (global_keys, do_global_help),
4149 history_menu_help);
4150 add_menu_key (&keys, CTRL_KEY ('a'), history_menu_annotate);
4151 add_menu_key (&keys, CTRL_KEY ('d'), history_menu_details);
4152 add_menu_key (&keys, '\n', history_menu_view_annotation);
4153 construct_menu (MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES, TAG_WIDTH,
4154 0, config.boardleft ? BOARD_WIDTH : 0,
4155 _("Move History Tree"), 1, get_history_items, keys, g,
4156 history_menu_print, history_menu_exit, history_menu_resize);
4159 void
4160 do_history_menu ()
4162 history_menu (gp);
4165 void
4166 do_history_half_move_toggle ()
4168 movestep = (movestep == 1) ? 2 : 1;
4169 update_history_window (gp);
4172 void
4173 do_history_rotate_board ()
4175 struct userdata_s *d = gp->data;
4176 d->rotate = !d->rotate;
4179 void
4180 do_history_jump_next ()
4182 struct userdata_s *d = gp->data;
4184 pgn_history_next (gp, d->b, (keycount > 0) ?
4185 config.jumpcount * keycount * movestep :
4186 config.jumpcount * movestep);
4189 void
4190 do_history_jump_prev ()
4192 struct userdata_s *d = gp->data;
4194 pgn_history_prev (gp, d->b, (keycount) ?
4195 config.jumpcount * keycount * movestep :
4196 config.jumpcount * movestep);
4199 void
4200 do_history_prev ()
4202 struct userdata_s *d = gp->data;
4204 pgn_history_prev (gp, d->b, (keycount) ? keycount * movestep : movestep);
4207 void
4208 do_history_next ()
4210 struct userdata_s *d = gp->data;
4212 pgn_history_next (gp, d->b, (keycount) ? keycount * movestep : movestep);
4215 void
4216 do_history_mode_finalize (struct userdata_s *d)
4218 pushkey = 0;
4219 d->mode = MODE_PLAY;
4222 void
4223 do_history_mode_confirm (WIN * win)
4225 struct userdata_s *d = gp->data;
4226 wchar_t str[] = { win->c, 0 };
4228 if (!wcscmp (str, resume_wchar))
4230 pgn_history_free (gp->hp, gp->hindex);
4231 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4233 #if 0
4234 case 'C':
4235 case 'c':
4236 if (pgn_history_rav_new (gp, d->b, gp->hindex) != E_PGN_OK)
4237 return;
4239 break;
4240 #endif
4241 else
4242 return;
4244 if (!TEST_FLAG (d->flags, CF_HUMAN))
4246 char *fen = pgn_game_to_fen (gp, d->b);
4248 add_engine_command (gp, ENGINE_READY, "setboard %s\n", fen);
4249 free (fen);
4252 do_history_mode_finalize (d);
4255 void
4256 do_history_toggle ()
4258 struct userdata_s *d = gp->data;
4260 // FIXME Resuming from previous history could append to a RAV.
4261 if (gp->hindex != pgn_history_total (gp->hp))
4263 if (!pushkey)
4264 construct_message (NULL, _("What would you like to do?"), 0, 1,
4265 NULL, NULL, NULL, do_history_mode_confirm, 0, 0,
4266 NULL, _
4267 ("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."),
4268 resume_wchar);
4269 return;
4271 else
4273 if (TEST_FLAG (gp->flags, GF_GAMEOVER))
4274 return;
4277 if (gp->side != gp->turn)
4279 d->play_mode = PLAY_EH;
4281 else
4283 d->play_mode = PLAY_HE;
4284 d->rotate = FALSE;
4287 do_history_mode_finalize (d);
4290 void
4291 do_history_annotate ()
4293 int n = gp->hindex;
4295 if (n && gp->hp[n - 1]->move)
4296 n--;
4297 else
4298 return;
4300 do_annotate_move (gp->hp[n]);
4303 static void
4304 do_history_help ()
4306 wchar_t *buf = build_help (history_keys);
4308 construct_message (_("History Mode Keys (* = can take a repeat count)"),
4309 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
4310 buf, do_more_help, 0, 1, NULL, "%ls", buf);
4313 void
4314 do_history_find (int which)
4316 struct input_data_s *in;
4317 int *p;
4319 if (pgn_history_total (gp->hp) < 2)
4320 return;
4322 in = Calloc (1, sizeof (struct input_data_s));
4323 p = Malloc (sizeof (int));
4324 *p = which;
4325 in->data = p;
4326 in->efunc = do_find_move_exp;
4328 if (!*moveexp || which == 0)
4330 construct_input (_("Find Move Text Expression"), NULL, 1, 0, NULL, NULL,
4331 NULL, 0, in, INPUT_HIST_MOVE_EXP, NULL, -1);
4332 return;
4335 free (p);
4336 free (in);
4337 do_find_move_exp_finalize (0, which);
4340 void
4341 do_history_find_new ()
4343 do_history_find (0);
4346 void
4347 do_history_find_prev ()
4349 do_history_find (-1);
4352 void
4353 do_history_find_next ()
4355 do_history_find (1);
4358 void
4359 do_history_rav (int which)
4361 struct userdata_s *d = gp->data;
4363 rav_next_prev (gp, d->b, which);
4366 void
4367 do_history_rav_next ()
4369 do_history_rav (1);
4372 void
4373 do_history_rav_prev ()
4375 do_history_rav (0);
4378 void
4379 do_history_jump ()
4381 struct input_data_s *in;
4383 if (pgn_history_total (gp->hp) < 2)
4384 return;
4386 if (!keycount)
4388 in = Calloc (1, sizeof (struct input_data_s));
4389 in->efunc = do_move_jump;
4391 construct_input (_("Jump to Move Number"), NULL, 1, 1, NULL,
4392 NULL, NULL, 0, in, -1, NULL, 0);
4393 return;
4396 do_move_jump_finalize (keycount);
4399 static void
4400 free_userdata_once (GAME g)
4402 struct userdata_s *d = g->data;
4404 if (!d)
4405 return;
4407 if (d->engine)
4409 stop_engine (g);
4411 if (d->engine->enginebuf)
4413 int n;
4415 for (n = 0; d->engine->enginebuf[n]; n++)
4416 free (d->engine->enginebuf[n]);
4418 free (d->engine->enginebuf);
4421 if (d->engine->queue)
4423 struct queue_s **q;
4425 for (q = d->engine->queue; *q; q++)
4426 free (*q);
4428 free (d->engine->queue);
4431 free (d->engine);
4434 #ifdef WITH_LIBPERL
4435 if (d->perlfen)
4436 free (d->perlfen);
4438 if (d->oldfen)
4439 free (d->oldfen);
4440 #endif
4442 free (d);
4443 g->data = NULL;
4446 static void
4447 free_userdata ()
4449 int i;
4451 for (i = 0; i < gtotal; i++)
4453 free_userdata_once (game[i]);
4454 game[i]->data = NULL;
4458 void
4459 update_loading_window (int n)
4461 char buf[16];
4463 if (!loadingw)
4465 loadingw = newwin (3, COLS / 2, CALCPOSY (3), CALCPOSX (COLS / 2));
4466 loadingp = new_panel (loadingw);
4467 wbkgd (loadingw, CP_MESSAGE_WINDOW);
4470 wmove (loadingw, 0, 0);
4471 wclrtobot (loadingw);
4472 wattron (loadingw, CP_MESSAGE_BORDER);
4473 box (loadingw, ACS_VLINE, ACS_HLINE);
4474 wattroff (loadingw, CP_MESSAGE_BORDER);
4475 mvwprintw (loadingw, 1, CENTER_INT ((COLS / 2), 11 +
4476 strlen (itoa (gtotal, buf))),
4477 _("Loading... %i%% (%i games)"), n, gtotal);
4478 update_panels ();
4479 doupdate ();
4482 static void
4483 init_userdata_once (GAME g, int n)
4485 struct userdata_s *d = NULL;
4487 d = Calloc (1, sizeof (struct userdata_s));
4488 d->n = n;
4489 d->c_row = 2, d->c_col = 5;
4490 SET_FLAG (d->flags, CF_NEW);
4491 g->data = d;
4493 if (pgn_board_init_fen (g, d->b, NULL) != E_PGN_OK)
4494 pgn_board_init (d->b);
4497 void
4498 init_userdata ()
4500 int i;
4502 for (i = 0; i < gtotal; i++)
4503 init_userdata_once (game[i], i);
4506 void
4507 fix_marks (int *start, int *end)
4509 int i;
4511 *start = (*start < 0) ? 0 : *start;
4512 *end = (*end < 0) ? 0 : *end;
4514 if (*start > *end)
4516 i = *start;
4517 *start = *end;
4518 *end = i + 1;
4521 *end = (*end > gtotal) ? gtotal : *end;
4524 void
4525 do_new_game_finalize (GAME g)
4527 struct userdata_s *d = g->data;
4529 d->mode = MODE_PLAY;
4530 update_status_notify (g, NULL);
4531 d->rotate = FALSE;
4532 d->go_move = 0;
4535 void
4536 do_new_game_from_scratch (WIN * win)
4538 wchar_t str[] = { win->c, 0 };
4540 if (wcscmp (str, yes_wchar))
4541 return;
4543 stop_clock ();
4544 free_userdata ();
4545 pgn_parse (NULL);
4546 gp = game[gindex];
4547 add_custom_tags (&gp->tag);
4548 init_userdata ();
4549 loadfile[0] = 0;
4550 do_new_game_finalize (gp);
4553 void
4554 do_new_game ()
4556 pgn_new_game ();
4557 gp = game[gindex];
4558 add_custom_tags (&gp->tag);
4559 init_userdata_once (gp, gindex);
4560 do_new_game_finalize (gp);
4563 void
4564 do_game_delete_finalize (int n)
4566 struct userdata_s *d;
4568 delete_game ((!n) ? gindex : -1);
4569 d = gp->data;
4570 if (d->mode != MODE_EDIT)
4571 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4574 void
4575 do_game_delete_confirm (WIN * win)
4577 int *n;
4578 wchar_t str[] = { win->c, 0 };
4580 if (wcscmp (str, yes_wchar))
4582 free (win->data);
4583 return;
4586 n = (int *) win->data;
4587 do_game_delete_finalize (*n);
4588 free (win->data);
4591 void
4592 do_game_delete ()
4594 char *tmp = NULL;
4595 int i, n;
4596 struct userdata_s *d;
4597 int *p;
4599 if (gtotal < 2)
4601 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4602 return;
4605 tmp = NULL;
4607 for (i = n = 0; i < gtotal; i++)
4609 d = game[i]->data;
4611 if (TEST_FLAG (d->flags, CF_DELETE))
4612 n++;
4615 if (!n)
4616 tmp = _("Delete the current game?");
4617 else
4619 if (n == gtotal)
4621 cmessage (NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4622 return;
4625 tmp = _("Delete all games marked for deletion?");
4628 if (config.deleteprompt)
4630 p = Malloc (sizeof (int));
4631 *p = n;
4632 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, p,
4633 do_game_delete_confirm, 0, 0, NULL, "%s", tmp);
4634 return;
4637 do_game_delete_finalize (n);
4640 void
4641 do_find_game_exp_finalize (int which)
4643 struct userdata_s *d = gp->data;
4644 int n;
4646 if ((n = find_game_exp (gameexp, (which == -1) ? 0 : 1,
4647 (keycount) ? keycount : 1)) == -1)
4649 update_status_notify (gp, "%s", _("No matches found"));
4650 return;
4653 gindex = n;
4654 d = gp->data;
4656 if (pgn_history_total (gp->hp))
4657 d->mode = MODE_HISTORY;
4659 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4662 void
4663 do_find_game_exp (WIN * win)
4665 struct input_data_s *in = win->data;
4666 int *n = in->data;
4667 int c = *n;
4669 if (in->str)
4671 strncpy (gameexp, in->str, sizeof (gameexp));
4672 gameexp[sizeof (gameexp) - 1] = 0;
4674 if (c == '?')
4675 c = '}';
4677 do_find_game_exp_finalize (c);
4678 free (in->str);
4681 free (in->data);
4682 free (in);
4685 void
4686 do_game_jump_finalize (int n)
4688 struct userdata_s *d;
4690 if (--n > gtotal - 1 || n < 0)
4691 return;
4693 gindex = n;
4694 gp = game[gindex];
4695 d = gp->data;
4696 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4697 update_status_notify (gp, NULL);
4700 void
4701 do_game_jump (WIN * win)
4703 struct input_data_s *in = win->data;
4705 if (!in->str || !isinteger (in->str))
4707 if (in->str)
4708 free (in->str);
4710 free (in);
4711 return;
4714 do_game_jump_finalize (atoi (in->str));
4715 free (in->str);
4716 free (in);
4719 void
4720 do_load_file (WIN * win)
4722 struct input_data_s *in = win->data;
4723 char *tmp = in->str;
4724 struct userdata_s *d;
4725 PGN_FILE *pgn = NULL;
4726 int n;
4728 if (!in->str)
4730 free (in);
4731 return;
4734 if ((tmp = pathfix (tmp)) == NULL)
4735 goto done;
4737 n = pgn_open (tmp, "r", &pgn);
4739 if (n == E_PGN_ERR)
4741 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, strerror (errno));
4742 goto done;
4744 else if (n == E_PGN_INVALID)
4746 cmessage (ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp,
4747 _("Not a regular file"));
4748 goto done;
4751 free_userdata ();
4753 if (pgn_parse (pgn) == E_PGN_ERR)
4755 del_panel (loadingp);
4756 delwin (loadingw);
4757 loadingw = NULL;
4758 loadingp = NULL;
4759 init_userdata ();
4760 goto done;
4763 del_panel (loadingp);
4764 delwin (loadingw);
4765 loadingw = NULL;
4766 loadingp = NULL;
4767 init_userdata ();
4768 strncpy (loadfile, tmp, sizeof (loadfile));
4769 loadfile[sizeof (loadfile) - 1] = 0;
4770 gp = game[gindex];
4771 d = gp->data;
4773 if (pgn_history_total (gp->hp))
4774 d->mode = MODE_HISTORY;
4776 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4778 fm_loaded_file = TRUE;
4779 d->rotate = FALSE;
4781 done:
4782 pgn_close (pgn);
4784 if (in->str)
4785 free (in->str);
4787 free (in);
4790 void
4791 do_game_save (WIN * win)
4793 struct input_data_s *in = win->data;
4794 int *x = in->data;
4795 int n = *x;
4796 char *tmp = in->str;
4797 char tfile[FILENAME_MAX];
4798 char *p;
4799 int i;
4800 struct userdata_s *d;
4802 if (!tmp || (tmp = pathfix (tmp)) == NULL)
4803 goto done;
4805 if (pgn_is_compressed (tmp) == E_PGN_ERR)
4807 p = tmp + strlen (tmp) - 1;
4809 if (*p != 'n' || *(p - 1) != 'g' || *(p - 2) != 'p' || *(p - 3) != '.')
4811 snprintf (tfile, sizeof (tfile), "%s.pgn", tmp);
4812 tmp = tfile;
4817 * When in edit mode, update the FEN tag.
4819 if (n == -1)
4821 for (i = 0; i < gtotal; i++)
4823 d = game[i]->data;
4825 if (d->mode == MODE_EDIT)
4827 char *fen = pgn_game_to_fen (game[i], d->b);
4829 pgn_tag_add (&game[i]->tag, (char *) "FEN", fen);
4830 free (fen);
4834 else
4836 d = game[n]->data;
4838 if (d->mode == MODE_EDIT)
4840 char *fen = pgn_game_to_fen (game[n], d->b);
4842 pgn_tag_add (&game[n]->tag, (char *) "FEN", fen);
4843 free (fen);
4847 save_pgn (tmp, n);
4849 done:
4850 if (in->str)
4851 free (in->str);
4853 free (in->data);
4854 free (in);
4857 void
4858 do_get_game_save_input (int n)
4860 struct input_data_s *in = Calloc (1, sizeof (struct input_data_s));
4861 int *p = Malloc (sizeof (int));
4863 in->efunc = do_game_save;
4864 *p = n;
4865 in->data = p;
4867 construct_input (_("Save Game Filename"), loadfile, 1, 1,
4868 _("Type TAB for file browser"), file_browser, NULL, '\t',
4869 in, INPUT_HIST_FILE, NULL, -1);
4872 void
4873 do_game_save_multi_confirm (WIN * win)
4875 int i;
4876 wchar_t str[] = { win->c, 0 };
4878 if (!wcscmp (str, current_wchar))
4879 i = gindex;
4880 else if (!wcscmp (str, all_wchar))
4881 i = -1;
4882 else
4884 update_status_notify (gp, "%s", _("Save game aborted."));
4885 return;
4888 do_get_game_save_input (i);
4891 void
4892 do_global_about ()
4894 cmessage (_("ABOUT"), ANY_KEY_STR,
4895 _("%s\nUsing %s with %i colors and %i color pairs\n%s\n%s"),
4896 PACKAGE_STRING, curses_version (), COLORS, COLOR_PAIRS,
4897 COPYRIGHT, CBOARD_URL);
4900 void
4901 global_game_next_prev (int which)
4903 struct userdata_s *d;
4905 game_next_prev (gp, (which == 1) ? 1 : 0, (keycount) ? keycount : 1);
4906 d = gp->data;
4908 if (delete_count)
4910 if (which == 1)
4912 markend = markstart + delete_count;
4913 delete_count = 0;
4915 else
4917 markend = markstart - delete_count + 1;
4918 delete_count = -1; // to fix gindex in the other direction
4921 fix_marks (&markstart, &markend);
4922 do_global_toggle_delete ();
4925 if (d->mode == MODE_HISTORY)
4926 pgn_board_update (gp, d->b, gp->hindex);
4927 else if (d->mode == MODE_PLAY)
4928 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
4931 void
4932 do_global_next_game ()
4934 global_game_next_prev (1);
4937 void
4938 do_global_prev_game ()
4940 global_game_next_prev (0);
4943 void
4944 global_find (int which)
4946 struct input_data_s *in;
4947 int *p;
4949 if (gtotal < 2)
4950 return;
4952 in = Calloc (1, sizeof (struct input_data_s));
4953 p = Malloc (sizeof (int));
4954 *p = which;
4955 in->data = p;
4956 in->efunc = do_find_game_exp;
4958 if (!*gameexp || which == 0)
4960 construct_input (_("Find Game by Tag Expression"), NULL, 1, 0,
4961 _("[name expression:]value expression"), NULL, NULL, 0,
4962 in, INPUT_HIST_GAME_EXP, NULL, -1);
4963 return;
4966 free (p);
4967 free (in);
4968 do_find_game_exp_finalize (which);
4971 void
4972 do_global_find_new ()
4974 global_find (0);
4977 void
4978 do_global_find_next ()
4980 global_find (1);
4983 void
4984 do_global_find_prev ()
4986 global_find (-1);
4989 void
4990 do_global_game_jump ()
4992 if (gtotal < 2)
4993 return;
4995 if (!keycount)
4997 struct input_data_s *in;
4999 in = Calloc (1, sizeof (struct input_data_s));
5000 in->efunc = do_game_jump;
5001 construct_input (_("Jump to Game Number"), NULL, 1, 1, NULL, NULL, NULL,
5002 0, in, -1, NULL, 0);
5003 return;
5006 do_game_jump_finalize (keycount);
5009 void
5010 do_global_toggle_delete ()
5012 int i;
5014 pushkey = 0;
5016 if (gtotal < 2)
5017 return;
5019 if (keycount && delete_count == 0)
5021 markstart = gindex;
5022 delete_count = keycount;
5023 update_status_notify (gp, "%s (delete)", status.notify);
5024 return;
5027 if (markstart >= 0 && markend >= 0)
5029 for (i = markstart; i < markend; i++)
5031 if (toggle_delete_flag (i))
5033 return;
5037 gindex = (delete_count < 0) ? markstart : i - 1;
5039 else
5041 if (toggle_delete_flag (gindex))
5042 return;
5045 markstart = markend = -1;
5046 delete_count = 0;
5047 update_status_window (gp);
5050 void
5051 do_global_delete_game ()
5053 do_game_delete ();
5056 void
5057 do_global_tag_edit ()
5059 struct userdata_s *d = gp->data;
5061 edit_tags (gp, d->b, 1);
5064 void
5065 do_global_tag_view ()
5067 struct userdata_s *d = gp->data;
5069 edit_tags (gp, d->b, 0);
5072 void
5073 do_global_resume_game ()
5075 struct input_data_s *in;
5077 in = Calloc (1, sizeof (struct input_data_s));
5078 in->efunc = do_load_file;
5079 construct_input (_("Load Filename"), NULL, 1, 1,
5080 _("Type TAB for file browser"), file_browser, NULL, '\t',
5081 in, INPUT_HIST_FILE, NULL, -1);
5084 void
5085 do_global_save_game ()
5087 if (gtotal > 1)
5089 construct_message (NULL, _("What would you like to do?"), 0, 1,
5090 NULL, NULL, NULL, do_game_save_multi_confirm, 0, 0,
5091 NULL, _
5092 ("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."),
5093 current_wchar, all_wchar);
5094 return;
5097 do_get_game_save_input (-1);
5100 void
5101 do_global_new_game ()
5103 do_new_game ();
5106 void
5107 do_global_copy_game ()
5109 int g = gindex;
5110 int i, n;
5111 struct userdata_s *d;
5113 do_global_new_game ();
5114 d = gp->data;
5115 n = pgn_tag_total (game[g]->tag);
5117 for (i = 0; i < n; i++)
5118 pgn_tag_add (&gp->tag, game[g]->tag[i]->name, game[g]->tag[i]->value);
5120 pgn_board_init_fen (gp, d->b, NULL);
5121 n = pgn_history_total (game[g]->history);
5123 // FIXME RAV
5124 for (i = 0; i < n; i++)
5126 char *frfr = NULL;
5127 char *move = strdup (game[g]->history[i]->move);
5129 if (pgn_parse_move (gp, d->b, &move, &frfr) != E_PGN_OK)
5131 free (move);
5132 SET_FLAG (gp->flags, GF_PERROR);
5133 return;
5136 pgn_history_add (gp, d->b, move);
5137 free (move);
5138 free (frfr);
5139 pgn_switch_turn (gp);
5142 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
5145 void
5146 do_global_new_all ()
5148 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
5149 do_new_game_from_scratch, 0, 0, NULL, "%s",
5150 _("Really start a new game from scratch?"));
5153 void
5154 do_quit (WIN * win)
5156 wchar_t str[] = { win->c, 0 };
5157 int n = wcscmp (str, yes_wchar);
5159 if (n)
5160 return;
5162 quit = 1;
5165 void
5166 do_global_quit ()
5168 if (config.exitdialogbox)
5169 construct_message (NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
5170 do_quit, 0, 0, NULL, "%s", _("Want to Quit?"));
5171 else
5172 quit = 1;
5175 void
5176 do_global_toggle_engine_window ()
5178 if (!enginew)
5180 enginew = newwin (LINES, COLS, 0, 0);
5181 enginep = new_panel (enginew);
5182 window_draw_title (enginew, _("Engine IO Window"), COLS,
5183 CP_MESSAGE_TITLE, CP_MESSAGE_BORDER);
5184 hide_panel (enginep);
5187 if (panel_hidden (enginep))
5189 update_engine_window (gp);
5190 top_panel (enginep);
5192 else
5194 hide_panel (enginep);
5198 void
5199 do_global_toggle_board_details ()
5201 do_board_details ();
5204 void
5205 do_play_toggle_strict_castling ()
5207 do_toggle_strict_castling ();
5210 // Global and other keys.
5211 static int
5212 globalkeys ()
5214 struct userdata_s *d = gp->data;
5215 int i;
5218 * These cannot be modified and other game mode keys cannot conflict with
5219 * these.
5221 switch (input_c)
5223 case KEY_ESCAPE:
5224 d->sp.icon = d->sp.srow = d->sp.scol = 0;
5225 markend = markstart = 0;
5227 if (keycount)
5229 keycount = 0;
5230 update_status_notify (gp, NULL);
5233 if (config.validmoves)
5234 pgn_reset_valid_moves (d->b);
5236 return 1;
5237 case '0' ... '9':
5238 i = input_c - '0';
5240 if (keycount)
5241 keycount = keycount * 10 + i;
5242 else
5243 keycount = i;
5245 update_status_notify (gp, _("Repeat %i"), keycount);
5246 return -1;
5247 case KEY_UP:
5248 if (d->mode == MODE_HISTORY)
5249 return 0;
5251 if (keycount)
5252 d->c_row += keycount;
5253 else
5254 d->c_row++;
5256 if (d->c_row > 8)
5257 d->c_row = 1;
5259 return 1;
5260 case KEY_DOWN:
5261 if (d->mode == MODE_HISTORY)
5262 return 0;
5264 if (keycount)
5266 d->c_row -= keycount;
5267 update_status_notify (gp, NULL);
5269 else
5270 d->c_row--;
5272 if (d->c_row < 1)
5273 d->c_row = 8;
5275 return 1;
5276 case KEY_LEFT:
5277 if (d->mode == MODE_HISTORY)
5278 return 0;
5280 if (keycount)
5281 d->c_col -= keycount;
5282 else
5283 d->c_col--;
5285 if (d->c_col < 1)
5286 d->c_col = 8;
5288 return 1;
5289 case KEY_RIGHT:
5290 if (d->mode == MODE_HISTORY)
5291 return 0;
5293 if (keycount)
5294 d->c_col += keycount;
5295 else
5296 d->c_col++;
5298 if (d->c_col > 8)
5299 d->c_col = 1;
5301 return 1;
5302 case KEY_RESIZE:
5303 return 1;
5304 case 0:
5305 default:
5306 for (i = 0; global_keys[i]; i++)
5308 if (input_c == global_keys[i]->c && global_keys[i]->f)
5310 (*global_keys[i]->f) ();
5311 return 1;
5314 break;
5317 return 0;
5320 #ifdef WITH_LIBPERL
5321 static void
5322 perl_error (const char *fmt, ...)
5324 va_list ap;
5325 char *buf;
5327 va_start (ap, fmt);
5328 vasprintf (&buf, fmt, ap);
5329 va_end (ap);
5331 message (ERROR_STR, ANY_KEY_STR, "%s", buf);
5332 free (buf);
5335 static void
5336 do_perl_finalize (WIN * win)
5338 struct input_data_s *in = win->data;
5339 GAME g = in->data;
5340 struct userdata_s *d = g->data;
5341 char *filename;
5342 char *result = NULL;
5343 char *arg = NULL;
5344 int n;
5346 asprintf (&filename, "%s/perl.pl", config.datadir);
5348 if (!in->str)
5349 goto done;
5351 if (perl_init_file (filename, perl_error))
5352 goto done;
5354 arg = pgn_game_to_fen (g, d->b);
5356 if (perl_call_sub (trim (in->str), arg, &result))
5357 goto done;
5359 d->perlfen = pgn_game_to_fen (g, d->b);
5360 d->perlflags = g->flags;
5362 if (pgn_board_init_fen (g, d->b, result) != E_PGN_OK)
5364 message (ERROR_STR, ANY_KEY_STR, "%s", _("FEN parse error."));
5365 pgn_board_init_fen (g, d->b, d->perlfen);
5366 g->flags = d->perlflags;
5367 free (d->perlfen);
5368 d->perlfen = NULL;
5369 goto done;
5372 SET_FLAG (d->flags, CF_PERL);
5373 n = pgn_tag_find (g->tag, "FEN");
5375 if (n != E_PGN_ERR)
5376 d->oldfen = strdup (g->tag[n]->value);
5378 pgn_tag_add (&g->tag, (char *) "FEN", result);
5379 update_status_notify (g, "%s", ANY_KEY_STR);
5380 update_all (g);
5382 done:
5383 free (result);
5384 free (arg);
5385 free (in->str);
5386 free (in);
5387 free (filename);
5390 void
5391 do_global_perl ()
5393 struct input_data_s *in;
5395 in = Calloc (1, sizeof (struct input_data_s));
5396 in->data = gp;
5397 in->efunc = do_perl_finalize;
5398 construct_input (_("PERL Subroutine Filter"), NULL, 1, 0, NULL, NULL, NULL,
5399 0, in, INPUT_HIST_PERL, NULL, -1);
5401 #endif
5404 * A macro may contain a key that belongs to another macro so macro_match will
5405 * need to be updated to the new index of the matching macro.
5407 static void
5408 find_macro (struct userdata_s *d)
5410 int i;
5413 * Macros can't contain macros when in a window.
5415 if (wins)
5416 return;
5418 again:
5419 for (i = 0; macros[i]; i++)
5421 if ((macros[i]->mode == -1 || macros[i]->mode == d->mode) &&
5422 input_c == macros[i]->c)
5424 input_c = macros[i]->keys[macros[i]->n++];
5426 if (!macro_depth_n && macro_match > -1)
5428 macro_depth =
5429 realloc (macro_depth, (macro_depth_n + 1) * sizeof (int));
5430 macro_depth[macro_depth_n++] = macro_match;
5433 macro_depth =
5434 realloc (macro_depth, (macro_depth_n + 1) * sizeof (int));
5435 macro_depth[macro_depth_n++] = i;
5436 macro_match = i;
5437 goto again;
5443 * Resets the position in each macro to the first key.
5445 static void
5446 reset_macros ()
5448 int i;
5449 struct userdata_s *d = gp->data;
5451 again:
5452 if (macro_depth_n > 0)
5454 macro_depth_n--;
5455 macro_match = macro_depth[macro_depth_n];
5457 if (macros[macro_match]->n >= macros[macro_match]->total)
5458 goto again;
5460 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
5461 find_macro (d);
5462 return;
5465 for (i = 0; macros[i]; i++)
5466 macros[i]->n = 0;
5468 free (macro_depth);
5469 macro_depth = NULL;
5470 macro_depth_n = 0;
5471 macro_match = -1;
5474 void
5475 game_loop ()
5477 struct userdata_s *d;
5479 macro_match = -1;
5480 gindex = gtotal - 1;
5481 gp = game[gindex];
5482 d = gp->data;
5484 if (pgn_history_total (gp->hp))
5485 d->mode = MODE_HISTORY;
5486 else
5488 d->mode = MODE_PLAY;
5489 d->play_mode = PLAY_HE;
5492 d->rotate = FALSE;
5493 d->go_move = 0;
5494 d->pm_undo = FALSE;
5495 d->pm_frfr[0] = '\0';
5497 if (d->mode == MODE_HISTORY)
5498 pgn_board_update (gp, d->b, pgn_history_total (gp->hp));
5500 update_status_notify (gp, _("Type %ls for help"),
5501 key_lookup (global_keys, do_global_help));
5502 movestep = 2;
5503 flushinp ();
5504 update_all (gp);
5505 wtimeout (boardw, WINDOW_TIMEOUT);
5507 while (!quit)
5509 int n = 0, i;
5510 char fdbuf[8192] = { 0 };
5511 int len;
5512 struct timeval tv = { 0, 0 };
5513 fd_set rfds, wfds;
5514 WIN *win = NULL;
5515 WINDOW *wp = NULL;
5517 FD_ZERO (&rfds);
5518 FD_ZERO (&wfds);
5520 for (i = 0; i < gtotal; i++)
5522 d = game[i]->data;
5524 if (d->engine && d->engine->pid != -1)
5526 if (d->engine->fd[ENGINE_IN_FD] > 2)
5528 if (d->engine->fd[ENGINE_IN_FD] > n)
5529 n = d->engine->fd[ENGINE_IN_FD];
5531 FD_SET (d->engine->fd[ENGINE_IN_FD], &rfds);
5534 if (d->engine->fd[ENGINE_OUT_FD] > 2)
5536 if (d->engine->fd[ENGINE_OUT_FD] > n)
5537 n = d->engine->fd[ENGINE_OUT_FD];
5539 FD_SET (d->engine->fd[ENGINE_OUT_FD], &wfds);
5544 if (n)
5546 if ((n = select (n + 1, &rfds, &wfds, NULL, &tv)) > 0)
5548 for (i = 0; i < gtotal; i++)
5550 d = game[i]->data;
5552 if (d->engine && d->engine->pid != -1)
5554 if (FD_ISSET (d->engine->fd[ENGINE_IN_FD], &rfds))
5556 len = read (d->engine->fd[ENGINE_IN_FD], fdbuf,
5557 sizeof (fdbuf));
5559 if (len > 0)
5561 if (d->engine->iobuf)
5562 d->engine->iobuf =
5563 Realloc (d->engine->iobuf,
5564 d->engine->len + len + 1);
5565 else
5566 d->engine->iobuf = Calloc (1, len + 1);
5568 memcpy (&(d->engine->iobuf[d->engine->len]),
5569 &fdbuf, len);
5570 d->engine->len += len;
5571 d->engine->iobuf[d->engine->len] = 0;
5574 * The fdbuf is full or no newline
5575 * was found. So we'll append the next
5576 * read() to this games buffer.
5578 if (d->engine->iobuf[d->engine->len - 1] !=
5579 '\n')
5580 continue;
5582 parse_engine_output (game[i], d->engine->iobuf);
5583 free (d->engine->iobuf);
5584 d->engine->iobuf = NULL;
5585 d->engine->len = 0;
5587 else if (len == -1)
5589 if (errno != EAGAIN)
5591 cmessage (ERROR_STR, ANY_KEY_STR,
5592 "Engine read(): %s",
5593 strerror (errno));
5594 waitpid (d->engine->pid, &n, 0);
5595 free (d->engine);
5596 d->engine = NULL;
5597 break;
5602 if (FD_ISSET (d->engine->fd[ENGINE_OUT_FD], &wfds))
5604 if (d->engine->queue)
5605 send_engine_command (game[i]);
5610 else
5612 if (n == -1)
5613 cmessage (ERROR_STR, ANY_KEY_STR, "select(): %s",
5614 strerror (errno));
5615 /* timeout */
5619 gp = game[gindex];
5620 d = gp->data;
5623 * This is needed to detect terminal resizing.
5625 doupdate ();
5626 if (LINES != LINES_OLD || COLS != COLS_OLD)
5628 COLS_OLD = COLS;
5629 LINES_OLD = LINES;
5630 do_window_resize ();
5634 * Finds the top level window in the window stack so we know what
5635 * window the wget_wch()'ed key belongs to.
5637 if (wins)
5639 for (i = 0; wins[i]; i++);
5640 win = wins[i - 1];
5641 wp = win->w;
5642 wtimeout (wp, WINDOW_TIMEOUT);
5644 else
5645 wp = boardw;
5647 if (!i && pushkey)
5648 input_c = pushkey;
5649 else
5651 if (!pushkey)
5653 if (macros && macro_match >= 0)
5655 if (macros[macro_match]->n >= macros[macro_match]->total)
5656 reset_macros ();
5657 else
5659 input_c =
5660 macros[macro_match]->keys[macros[macro_match]->n++];
5661 find_macro (d);
5664 else
5666 if (wget_wch (wp, &input_c) == ERR || input_c == KEY_RESIZE)
5668 if (input_c == KEY_RESIZE)
5670 if (win)
5671 win->c = input_c;
5673 window_resize_all ();
5674 update_all (gp);
5676 continue;
5680 else
5681 input_c = pushkey;
5683 if (win)
5685 win->c = input_c;
5688 * Run the function associated with the window. When the
5689 * function returns 0 win->efunc is ran (if not NULL) with
5690 * win as the one and only parameter. Then the window is
5691 * destroyed.
5693 * The exit function may create another window which will
5694 * mess up the window stack when window_destroy() is called.
5695 * So don't destory the window until the top window is
5696 * destroyable. See window_destroy().
5698 if ((*win->func) (win) == 0)
5700 if (win->efunc)
5701 (*win->efunc) (win);
5703 win->keep = 1;
5704 window_destroy (win);
5705 update_all (gp);
5708 continue;
5712 if (!keycount && status.notify)
5713 update_status_notify (gp, NULL);
5715 #ifdef WITH_LIBPERL
5716 if (TEST_FLAG (d->flags, CF_PERL))
5718 CLEAR_FLAG (d->flags, CF_PERL);
5719 pgn_board_init_fen (gp, d->b, d->perlfen);
5720 gp->flags = d->perlflags;
5721 free (d->perlfen);
5722 pgn_tag_add (&gp->tag, (char *) "FEN", d->oldfen);
5723 free (d->oldfen);
5724 d->perlfen = d->oldfen = NULL;
5725 update_all (gp);
5726 continue;
5728 #endif
5730 if (macros && macro_match < 0)
5731 find_macro (d);
5733 if ((n = globalkeys ()) == 1)
5735 if (macro_match == -1)
5736 keycount = 0;
5738 goto refresh;
5740 else if (n == -1)
5741 goto refresh;
5743 switch (d->mode)
5745 case MODE_EDIT:
5746 for (i = 0; edit_keys[i]; i++)
5748 if (input_c == edit_keys[i]->c)
5750 (*edit_keys[i]->f) ();
5751 break;
5754 break;
5755 case MODE_PLAY:
5756 for (i = 0; play_keys[i]; i++)
5758 if (input_c == play_keys[i]->c)
5760 (*play_keys[i]->f) ();
5761 goto done;
5765 do_play_config_command ();
5766 break;
5767 case MODE_HISTORY:
5768 for (i = 0; history_keys[i]; i++)
5770 if (input_c == history_keys[i]->c)
5772 (*history_keys[i]->f) ();
5773 break;
5776 break;
5777 default:
5778 break;
5781 done:
5782 if (keycount)
5783 update_status_notify (gp, NULL);
5785 keycount = 0;
5787 refresh:
5788 update_all (gp);
5792 void
5793 usage (const char *pn, int ret)
5795 fprintf ((ret) ? stderr : stdout, "%s%s",
5796 #ifdef DEBUG
5797 _("Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
5798 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"),
5799 #else
5800 _("Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"),
5801 #endif
5802 _(" -p Load PGN file.\n"
5803 " -V Validate a game file.\n"
5804 " -S Validate and output a PGN formatted game.\n"
5805 " -R Like -S but write a reduced PGN formatted game.\n"
5806 " -t Also write custom PGN tags from config file.\n"
5807 " -E Stop processing on file parsing error (overrides config).\n"
5808 " -C Enable strict castling (overrides config).\n"
5809 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, overrides config).\n"
5810 " -v Version information.\n" " -h This help text.\n"));
5812 exit (ret);
5815 void
5816 cleanup_all ()
5818 int i;
5820 stop_clock ();
5821 free_userdata ();
5822 pgn_free_all ();
5823 free (config.engine_cmd);
5824 free (config.pattern);
5825 free (config.ccfile);
5826 free (config.nagfile);
5827 free (config.configfile);
5829 if (config.keys)
5831 for (i = 0; config.keys[i]; i++)
5833 free (config.keys[i]->str);
5834 free (config.keys[i]);
5837 free (config.keys);
5840 if (config.einit)
5842 for (i = 0; config.einit[i]; i++)
5843 free (config.einit[i]);
5845 free (config.einit);
5848 if (config.tag)
5849 pgn_tag_free (config.tag);
5851 free (config.datadir);
5853 if (curses_initialized)
5855 del_panel (boardp);
5856 del_panel (historyp);
5857 del_panel (statusp);
5858 del_panel (tagp);
5859 delwin (boardw);
5860 delwin (historyw);
5861 delwin (statusw);
5862 delwin (tagw);
5864 if (enginew)
5866 del_panel (enginep);
5867 delwin (enginew);
5870 endwin ();
5873 #ifdef WITH_LIBPERL
5874 perl_cleanup ();
5875 #endif
5878 static void
5879 signal_save_pgn (int sig)
5881 char *buf;
5882 time_t now;
5883 char *p = config.savedirectory ? config.savedirectory : config.datadir;
5885 time (&now);
5886 asprintf (&buf, "%s/signal-%i-%li.pgn", p, sig, now);
5888 if (do_game_write (buf, "w", 0, gtotal))
5890 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", p, strerror (errno));
5891 update_status_notify (gp, "%s", _("Save game failed."));
5894 free (buf);
5895 quit = 1;
5898 void
5899 catch_signal (int which, siginfo_t *info, void *ctx)
5901 (void)info;
5902 (void)ctx;
5904 switch (which)
5906 case SIGALRM:
5907 update_clocks ();
5908 break;
5909 case SIGPIPE:
5910 if (which == SIGPIPE && quit)
5911 break;
5913 if (which == SIGPIPE)
5914 cmessage (NULL, ANY_KEY_STR, "%s", _("Broken pipe. Quitting."));
5916 cleanup_all ();
5917 exit (EXIT_FAILURE);
5918 break;
5919 case SIGSTOP:
5920 savetty ();
5921 break;
5922 case SIGCONT:
5923 resetty ();
5924 do_window_resize ();
5925 keypad (boardw, TRUE);
5926 break;
5927 case SIGINT:
5928 quit = 1;
5929 break;
5930 case SIGTERM:
5931 signal_save_pgn (which);
5932 break;
5933 default:
5934 break;
5938 void
5939 loading_progress (long total, long offset)
5941 int n = (100 * (offset / 100) / (total / 100));
5943 if (curses_initialized)
5944 update_loading_window (n);
5945 else
5947 fprintf (stderr, _("Loading... %i%% (%i games)%c"), n, gtotal, '\r');
5948 fflush (stderr);
5952 static void
5953 set_defaults ()
5955 set_config_defaults ();
5956 set_default_keys ();
5957 filetype = FILE_NONE;
5958 pgn_config_set (PGN_PROGRESS, 1024);
5959 pgn_config_set (PGN_PROGRESS_FUNC, loading_progress);
5963 main (int argc, char *argv[])
5965 int opt;
5966 struct stat st;
5967 char buf[FILENAME_MAX];
5968 char datadir[FILENAME_MAX];
5969 int ret = EXIT_SUCCESS;
5970 int validate_only = 0, validate_and_write = 0;
5971 int write_custom_tags = 0;
5972 int i = 0;
5973 PGN_FILE *pgn;
5974 int utf8_pieces = -1;
5975 struct sigaction sigact;
5977 setlocale (LC_ALL, "");
5978 bindtextdomain ("cboard", LOCALE_DIR);
5979 textdomain ("cboard");
5981 /* Solaris 5.9 */
5982 #ifndef HAVE_PROGNAME
5983 __progname = argv[0];
5984 #endif
5986 if ((config.pwd = getpwuid (getuid ())) == NULL)
5987 err (EXIT_FAILURE, "getpwuid()");
5989 snprintf (datadir, sizeof (datadir), "%s/.cboard", config.pwd->pw_dir);
5990 config.datadir = strdup (datadir);
5991 snprintf (buf, sizeof (buf), "%s/cc.data", datadir);
5992 config.ccfile = strdup (buf);
5993 snprintf (buf, sizeof (buf), "%s/nag.data", datadir);
5994 config.nagfile = strdup (buf);
5995 snprintf (buf, sizeof (buf), "%s/config", datadir);
5996 config.configfile = strdup (buf);
5998 if (stat (datadir, &st) == -1)
6000 if (errno == ENOENT)
6002 if (mkdir (datadir, 0755) == -1)
6003 err (EXIT_FAILURE, "%s", datadir);
6005 else
6006 err (EXIT_FAILURE, "%s", datadir);
6008 stat (datadir, &st);
6011 if (!S_ISDIR (st.st_mode))
6012 errx (EXIT_FAILURE, "%s: %s", datadir, _("Not a directory."));
6014 set_defaults ();
6016 #ifdef DEBUG
6017 while ((opt = getopt (argc, argv, "DCEVtSRhp:vu::")) != -1)
6019 #else
6020 while ((opt = getopt (argc, argv, "ECVtSRhp:vu::")) != -1)
6022 #endif
6023 switch (opt)
6025 #ifdef DEBUG
6026 case 'D':
6027 unlink ("libchess.debug");
6028 pgn_config_set (PGN_DEBUG, 1);
6029 break;
6030 #endif
6031 case 'C':
6032 pgn_config_set (PGN_STRICT_CASTLING, 1);
6033 break;
6034 case 't':
6035 write_custom_tags = 1;
6036 break;
6037 case 'E':
6038 i = 1;
6039 break;
6040 case 'R':
6041 pgn_config_set (PGN_REDUCED, 1);
6042 case 'S':
6043 validate_and_write = 1;
6044 case 'V':
6045 validate_only = 1;
6046 break;
6047 case 'v':
6048 printf ("%s (%s)\n%s\n%s\n", PACKAGE_STRING, curses_version (),
6049 COPYRIGHT, CBOARD_URL);
6050 exit (EXIT_SUCCESS);
6051 case 'p':
6052 filetype = FILE_PGN;
6053 strncpy (loadfile, optarg, sizeof (loadfile));
6054 loadfile[sizeof (loadfile) - 1] = 0;
6055 break;
6056 case 'u':
6057 utf8_pieces = optarg ? atoi (optarg) : 1;
6058 break;
6059 case 'h':
6060 default:
6061 usage (argv[0], EXIT_SUCCESS);
6065 if ((validate_only || validate_and_write) && !*loadfile)
6066 usage (argv[0], EXIT_FAILURE);
6068 if (access (config.configfile, R_OK) == 0)
6069 parse_rcfile (config.configfile);
6071 if (i)
6072 pgn_config_set (PGN_STOP_ON_ERROR, 1);
6074 memset (&sigact, 0, sizeof (sigact));
6075 sigact.sa_flags = SA_SIGINFO;
6076 sigact.sa_sigaction = catch_signal;
6077 sigaction (SIGPIPE, &sigact, NULL);
6078 sigaction (SIGCONT, &sigact, NULL);
6079 sigaction (SIGSTOP, &sigact, NULL);
6080 sigaction (SIGINT, &sigact, NULL);
6081 sigaction (SIGALRM, &sigact, NULL);
6082 sigaction (SIGTERM, &sigact, NULL);
6083 signal (SIGCHLD, SIG_IGN);
6085 srandom (getpid ());
6087 switch (filetype)
6089 case FILE_PGN:
6090 if (pgn_open (loadfile, "r", &pgn) != E_PGN_OK)
6091 err (EXIT_FAILURE, "%s", loadfile);
6093 ret = pgn_parse (pgn);
6094 pgn_close (pgn);
6095 break;
6096 case FILE_FEN:
6097 //ret = parse_fen_file(loadfile);
6098 break;
6099 case FILE_EPD: // Not implemented.
6100 case FILE_NONE:
6101 default:
6102 // No file specified. Empty game.
6103 ret = pgn_parse (NULL);
6104 gp = game[gindex];
6105 add_custom_tags (&gp->tag);
6106 break;
6109 if (validate_only || validate_and_write)
6111 if (validate_and_write)
6113 if (pgn_open ("-", "r", &pgn) != E_PGN_OK)
6114 err (EXIT_FAILURE, "pgn_open()");
6116 for (i = 0; i < gtotal; i++)
6118 if (write_custom_tags)
6119 add_custom_tags (&game[i]->tag);
6121 pgn_write (pgn, game[i]);
6124 pgn_close (pgn);
6126 fm_loaded_file = TRUE;
6129 cleanup_all ();
6130 exit (ret);
6132 else if (ret == E_PGN_ERR)
6133 exit (ret);
6135 if (utf8_pieces != -1)
6136 config.utf8_pieces = utf8_pieces;
6138 init_wchar_pieces ();
6139 yes_wchar = str_to_wchar (_("y"));
6140 all_wchar = str_to_wchar (_("a"));
6141 overwrite_wchar = str_to_wchar (_("o"));
6142 resume_wchar = str_to_wchar (_("r"));
6143 current_wchar = str_to_wchar (_("c"));
6144 append_wchar = str_to_wchar (_("a"));
6145 translatable_tag_names[0] = _("Event");
6146 translatable_tag_names[1] = _("Site");
6147 translatable_tag_names[2] = _("Date");
6148 translatable_tag_names[3] = _("Round");
6149 translatable_tag_names[4] = _("White");
6150 translatable_tag_names[5] = _("Black");
6151 translatable_tag_names[6] = _("Result");
6152 init_userdata ();
6155 * This fixes window resizing in an xterm.
6157 if (getenv ("DISPLAY") != NULL)
6159 putenv ((char *) "LINES=");
6160 putenv ((char *) "COLUMNS=");
6163 if (initscr () == NULL)
6164 errx (EXIT_FAILURE, "%s", _("Could not initialize curses."));
6165 else
6166 curses_initialized = 1;
6168 if (LINES < 23 || COLS < 74)
6170 endwin ();
6171 errx (EXIT_FAILURE, _("Need at least an 74x23 terminal."));
6174 COLS_OLD = COLS;
6175 LINES_OLD = LINES;
6177 if (has_colors () == TRUE && start_color () == OK)
6178 init_color_pairs ();
6180 boardw = newwin (BOARD_HEIGHT, BOARD_WIDTH, 0, COLS - BOARD_WIDTH);
6181 boardp = new_panel (boardw);
6182 historyw = newwin (HISTORY_HEIGHT, HISTORY_WIDTH, LINES - HISTORY_HEIGHT,
6183 COLS - HISTORY_WIDTH);
6184 historyp = new_panel (historyw);
6185 statusw = newwin (STATUS_HEIGHT, STATUS_WIDTH, 0, 0);
6186 statusp = new_panel (statusw);
6187 tagw = newwin (TAG_HEIGHT, TAG_WIDTH, STATUS_HEIGHT + 1, 0);
6188 tagp = new_panel (tagw);
6189 keypad (boardw, TRUE);
6190 // leaveok(boardw, TRUE);
6191 leaveok (tagw, TRUE);
6192 leaveok (statusw, TRUE);
6193 leaveok (historyw, TRUE);
6194 curs_set (0);
6195 cbreak ();
6196 noecho ();
6197 draw_window_decor ();
6198 game_loop ();
6199 cleanup_all ();
6200 free (w_pawn_wchar);
6201 free (w_rook_wchar);
6202 free (w_bishop_wchar);
6203 free (w_knight_wchar);
6204 free (w_queen_wchar);
6205 free (w_king_wchar);
6206 free (b_pawn_wchar);
6207 free (b_rook_wchar);
6208 free (b_bishop_wchar);
6209 free (b_knight_wchar);
6210 free (b_queen_wchar);
6211 free (b_king_wchar);
6212 free (empty_wchar);
6213 free (enpassant_wchar);
6214 free (yes_wchar);
6215 free (all_wchar);
6216 free (overwrite_wchar);
6217 free (resume_wchar);
6218 free (current_wchar);
6219 free (append_wchar);
6220 free (status.notify);
6221 exit (EXIT_SUCCESS);