Simplify coordinates code with rotation and show_attacks.
[cboard.git] / src / cboard.c
blob2fa3312e3a2836e249f4a8835c3ba6e8fab8cabb
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2013 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 COPYRIGHT "Copyright (C) 2002-2013 " PACKAGE_BUGREPORT
73 #define LINE_GRAPHIC(c) ((!config.linegraphics) ? ' ' : c)
74 #define ROWTOMATRIX(r) ((8 - r) * 2 + 2 - 1)
75 #define COLTOMATRIX(c) ((c == 1) ? 1 : c * 4 - 3)
76 #define STATUS_HEIGHT 12
77 #define MEGA_BOARD (LINES >= 50 && COLS >= 144)
78 #define BOARD_HEIGHT_MB 50
79 #define BOARD_WIDTH_MB 98
80 #define STATUS_WIDTH_MB (COLS - BOARD_WIDTH_MB)
81 #define TAG_HEIGHT_MB 31
82 #define TAG_WIDTH_MB (COLS - BOARD_WIDTH_MB)
83 #define HISTORY_HEIGHT_MB (LINES - (STATUS_HEIGHT + TAG_HEIGHT_MB + 1))
84 #define HISTORY_WIDTH_MB (COLS - BOARD_WIDTH_MB)
85 #define BIG_BOARD (LINES >= 40 && COLS >= 112)
86 #define BOARD_HEIGHT ((MEGA_BOARD) ? BOARD_HEIGHT_MB : (BIG_BOARD) ? 34 : 18)
87 #define BOARD_WIDTH ((MEGA_BOARD) ? BOARD_WIDTH_MB : (BIG_BOARD) ? 66 : 34)
88 #define STATUS_WIDTH ((MEGA_BOARD) ? STATUS_WIDTH_MB : COLS - BOARD_WIDTH)
89 #define TAG_HEIGHT ((MEGA_BOARD) ? TAG_HEIGHT_MB : LINES - STATUS_HEIGHT - 1)
90 #define TAG_WIDTH ((MEGA_BOARD) ? TAG_WIDTH_MB : COLS - BOARD_WIDTH)
91 #define HISTORY_HEIGHT ((MEGA_BOARD) ? HISTORY_HEIGHT_MB : LINES - BOARD_HEIGHT)
92 #define HISTORY_WIDTH ((MEGA_BOARD) ? HISTORY_WIDTH_MB : COLS - STATUS_WIDTH)
93 #define MAX_VALUE_WIDTH (COLS - 8)
95 enum {
96 UP, DOWN, LEFT, RIGHT
99 static WINDOW *boardw;
100 static PANEL *boardp;
101 static WINDOW *tagw;
102 static PANEL *tagp;
103 static WINDOW *statusw;
104 static PANEL *statusp;
105 static WINDOW *historyw;
106 static PANEL *historyp;
107 static WINDOW *loadingw;
108 static PANEL *loadingp;
109 static WINDOW *enginew;
110 static PANEL *enginep;
112 static char gameexp[255];
113 static char moveexp[255];
114 static struct itimerval clock_timer;
115 static int delete_count = 0;
116 static int markstart = -1, markend = -1;
117 static int keycount;
118 static char loadfile[FILENAME_MAX];
119 static int quit;
120 static wint_t input_c;
122 // Loaded filename from the command line or from the file input dialog.
123 static int filetype;
124 enum {
125 FILE_NONE, FILE_PGN, FILE_FEN, FILE_EPD
128 static char **nags;
129 static int nag_total;
130 static int macro_match;
132 // Primer movimiento de juego cargado
133 // First move loaded game
134 static char fm_loaded_file = FALSE;
136 static int COLS_OLD, LINES_OLD;
138 // Status window.
139 static struct {
140 wchar_t *notify; // The status window notification line buffer.
141 } status;
143 static int curses_initialized;
145 // When in history mode a full step is to the next move of the same playing
146 // side. Half stepping is alternating sides.
147 static int movestep;
149 static wchar_t *w_pawn_wchar;
150 static wchar_t *w_rook_wchar;
151 static wchar_t *w_bishop_wchar;
152 static wchar_t *w_knight_wchar;
153 static wchar_t *w_queen_wchar;
154 static wchar_t *w_king_wchar;
155 static wchar_t *b_pawn_wchar;
156 static wchar_t *b_rook_wchar;
157 static wchar_t *b_bishop_wchar;
158 static wchar_t *b_knight_wchar;
159 static wchar_t *b_queen_wchar;
160 static wchar_t *b_king_wchar;
161 static wchar_t *empty_wchar;
162 static wchar_t *enpassant_wchar;
164 static wchar_t *yes_wchar;
165 static wchar_t *all_wchar; // do_save_game_overwrite_confirm()
166 static wchar_t *overwrite_wchar; // do_save_game_overwrite_confirm()
167 static wchar_t *resume_wchar; // do_history_mode_confirm()
168 static wchar_t *current_wchar; // do_game_save_multi_confirm()
169 static wchar_t *append_wchar; // save_pgn()
171 static const char piece_chars[] = "PpRrNnBbQqKkxx";
172 static char *translatable_tag_names[7];
173 static const char *f_pieces[] = {
174 " ", // 0
175 " O ",
176 " /_\\ ",
177 " |-|-| ", // 3
178 " ] [ ",
179 " /___\\ ",
180 " /?M ", // 6
181 " (@/)) ",
182 " /__))",
183 " O ", // 9
184 " (+) ",
185 " /_\\ ",
186 "•°°°°°•",// 12
187 " \\\\|// ",
188 " |___| ",
189 " __+__ ", // 15
190 "(__|__)",
191 " |___| ",
192 " \\ / ", // 18
193 " X ",
194 " / \\ "
197 static const bool cb[8][8] = {
198 {1,0,1,0,1,0,1,0},
199 {0,1,0,1,0,1,0,1},
200 {1,0,1,0,1,0,1,0},
201 {0,1,0,1,0,1,0,1},
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}
208 static void free_userdata_once(GAME g);
209 static void do_more_help(WIN *);
211 void coordofmove(GAME g, char *move, char *prow, char *pcol)
213 char l = strlen(move);
215 if (*move == 'O') {
216 *prow = (g->turn == WHITE) ? 8 : 1;
217 *pcol = (l <= 4) ? 7 : 3;
218 return;
221 move += l;
223 while (!isdigit(*move))
224 move--;
226 *prow = RANKTOINT(*move--);
227 *pcol = FILETOINT(*move);
230 #define INV_INT(x) (9 - x)
231 #define INV_INT0(x) (7 - x)
233 // Posición por rotación de tablero.
234 // Rotation board position.
235 void rotate_position(char *prow, char *pcol)
237 *prow = INV_INT(*prow);
238 *pcol = INV_INT(*pcol);
241 void update_cursor(GAME g, int idx)
243 int t = pgn_history_total(g->hp);
244 struct userdata_s *d = g->data;
247 * If not deincremented then r and c would be the next move.
249 idx--;
251 if (idx > t || idx < 0 || !t || !g->hp[idx]->move)
252 d->c_row = 2, d->c_col = 5;
253 else
254 coordofmove(g, g->hp[idx]->move, &d->c_row, &d->c_col);
256 if (d->mode == MODE_HISTORY && d->rotate)
257 rotate_position(&d->c_row, &d->c_col);
260 static int init_nag()
262 FILE *fp;
263 char line[LINE_MAX];
264 int i = 0;
266 if ((fp = fopen(config.nagfile, "r")) == NULL) {
267 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.nagfile, strerror(errno));
268 return 1;
271 nags = Realloc(nags, (i+2) * sizeof(char *));
272 nags[i++] = strdup(_("none"));
273 nags[i] = NULL;
275 while (!feof(fp)) {
276 if (fscanf(fp, " %[^\n] ", line) == 1) {
277 nags = Realloc(nags, (i + 2) * sizeof(char *));
278 nags[i++] = strdup(line);
282 nags[i] = NULL;
283 nag_total = i;
284 return 0;
287 void edit_nag_toggle_item(struct menu_input_s *m)
289 struct input_s *in = m->data;
290 struct input_data_s *id = in->data;
291 HISTORY *h = id->data;
292 int i;
294 if (m->selected == 0) {
295 for (i = 0; i < MAX_PGN_NAG; i++)
296 h->nag[i] = 0;
298 for (i = 0; m->items[i]; i++)
299 m->items[i]->selected = 0;
301 return;
304 for (i = 0; i < MAX_PGN_NAG; i++) {
305 if (h->nag[i] == m->selected)
306 h->nag[i] = m->selected = 0;
307 else {
308 if (!h->nag[i]) {
309 h->nag[i] = m->selected;
310 break;
316 void edit_nag_save(struct menu_input_s *m)
318 pushkey = -1;
321 void edit_nag_help(struct menu_input_s *m)
323 message(_("NAG Menu Keys"), ANY_KEY_STR, "%s",
325 " UP/DOWN - previous/next menu item\n"
326 " HOME/END - first/last menu item\n"
327 " PGDN/PGUP - next/previous page\n"
328 " a-zA-Z0-9 - jump to item\n"
329 " SPACE - toggle selected item\n"
330 " CTRL-X - quit with changes"
334 struct menu_item_s **get_nag_items(WIN *win)
336 int i, n;
337 struct menu_input_s *m = win->data;
338 struct input_s *in = m->data;
339 struct input_data_s *id = in->data;
340 struct menu_item_s **items = m->items;
341 HISTORY *h = id->data;
343 if (items) {
344 for (i = 0; items[i]; i++)
345 free(items[i]);
348 for (i = 0; nags[i]; i++) {
349 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
350 items[i] = Malloc(sizeof(struct menu_item_s));
351 items[i]->name = nags[i];
352 items[i]->value = NULL;
354 for (n = 0; n < MAX_PGN_NAG; n++) {
355 if (h->nag[n] == i) {
356 items[i]->selected = 1;
357 n = -1;
358 break;
362 if (n >= 0)
363 items[i]->selected = 0;
366 items[i] = NULL;
367 m->nofree = 1;
368 m->items = items;
369 return items;
372 void nag_print(WIN *win)
374 struct menu_input_s *m = win->data;
376 mvwprintw(win->w, m->print_line, 1, "%-*s", win->cols - 2, m->item->name);
379 void edit_nag(void *arg)
381 struct menu_key_s **keys = NULL;
383 if (!nags) {
384 if (init_nag())
385 return;
388 add_menu_key(&keys, ' ', edit_nag_toggle_item);
389 add_menu_key(&keys, CTRL_KEY('x'), edit_nag_save);
390 add_menu_key(&keys, KEY_F(1), edit_nag_help);
391 construct_menu(0, 0, -1, -1, _("Numeric Annotation Glyphs"), 1, get_nag_items, keys, arg,
392 nag_print, NULL);
393 return;
396 static void *view_nag(void *arg)
398 HISTORY *h = (HISTORY *)arg;
399 char buf[80];
400 char line[LINE_MAX] = {0};
401 int i = 0;
403 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Viewing NAG for"), h->move);
405 if (!nags) {
406 if (init_nag())
407 return NULL;
410 for (i = 0; i < MAX_PGN_NAG; i++) {
411 char buf2[16];
413 if (!h->nag[i])
414 break;
416 if (h->nag[i] >= nag_total)
417 strncat(line, itoa(h->nag[i], buf2), sizeof(line)-1);
418 else
419 strncat(line, nags[h->nag[i]], sizeof(line)-1);
421 strncat(line, "\n", sizeof(line)-1);
424 line[strlen(line) - 1] = 0;
425 message(buf, ANY_KEY_STR, "%s", line);
426 return NULL;
429 void view_annotation(HISTORY *h)
431 char buf[MAX_SAN_MOVE_LEN + strlen(_("Viewing Annotation for")) + 4];
432 int nag = 0, comment = 0;
434 if (!h)
435 return;
437 if (h->comment && h->comment[0])
438 comment++;
440 if (h->nag[0])
441 nag++;
443 if (!nag && !comment)
444 return;
446 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Viewing Annotation for"), h->move);
448 if (comment)
449 construct_message(buf, (nag) ? _("Any other key to continue") : ANY_KEY_STR, 0, 1,
450 (nag) ? _("Press 'n' to view NAG") : NULL,
451 (nag) ? view_nag : NULL, (nag) ? h : NULL, NULL,
452 (nag) ? 'n' : 0, 0, "%s", h->comment);
453 else
454 construct_message(buf, _("Any other key to continue"), 0, 1, _("Press 'n' to view NAG"), view_nag, h, NULL,
455 'n', 0, "%s", _("No comment text for this move"));
458 int do_game_write(char *filename, char *mode, int start, int end)
460 int i;
461 struct userdata_s *d;
462 PGN_FILE *pgn;
464 i = pgn_open(filename, mode, &pgn);
466 if (i == E_PGN_ERR) {
467 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", filename, strerror(errno));
468 return 1;
470 else if (i == E_PGN_INVALID) {
471 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", filename, _("Not a regular file"));
472 return 1;
475 for (i = (start == -1) ? 0 : start; i < end; i++) {
476 d = game[i]->data;
477 pgn_write(pgn, game[i]);
478 CLEAR_FLAG(d->flags, CF_MODIFIED);
481 if (pgn_close(pgn) != E_PGN_OK)
482 message(ERROR_STR, ANY_KEY_STR, "%s", strerror(errno));
484 if (start == -1) {
485 strncpy(loadfile, filename, sizeof(loadfile));
486 loadfile[sizeof(loadfile)-1] = 0;
489 return 0;
492 struct save_game_s {
493 char *filename;
494 char *mode;
495 int start;
496 int end;
499 void do_save_game_overwrite_confirm(WIN *win)
501 char *mode = "w";
502 struct save_game_s *s = win->data;
503 wchar_t str[] = { win->c, 0 };
505 if (!wcscmp (str, append_wchar))
506 mode = "a";
507 else if (!wcscmp (str, overwrite_wchar))
508 mode = "w";
509 else
510 goto done;
512 if (do_game_write(s->filename, mode, s->start, s->end))
513 update_status_notify(gp, "%s", _("Save game failed."));
514 else
515 update_status_notify(gp, "%s", _("Game saved."));
517 done:
518 free(s->filename);
519 free(s);
522 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
523 * game index number.
525 void save_pgn(char *filename, int saveindex)
527 char buf[FILENAME_MAX];
528 struct stat st;
529 int end = (saveindex == -1) ? gtotal : saveindex + 1;
530 struct save_game_s *s;
532 if (filename[0] != '/' && config.savedirectory) {
533 if (stat(config.savedirectory, &st) == -1) {
534 if (errno == ENOENT) {
535 if (mkdir(config.savedirectory, 0755) == -1) {
536 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
537 strerror(errno));
538 return;
541 else {
542 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
543 strerror(errno));
544 return;
548 stat(config.savedirectory, &st);
550 if (!S_ISDIR(st.st_mode)) {
551 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory, _("Not a directory."));
552 return;
555 snprintf(buf, sizeof(buf), "%s/%s", config.savedirectory, filename);
556 filename = buf;
559 if (access(filename, W_OK) == 0) {
560 s = Malloc(sizeof(struct save_game_s));
561 s->filename = strdup(filename);
562 s->start = saveindex;
563 s->end = end;
564 construct_message(NULL, _("What would you like to do?"), 0, 1, NULL,
565 NULL, s, do_save_game_overwrite_confirm, 0, 0,
566 "%s \"%s\"\nPress \"%ls\" to append to this file, \"%ls\" to overwrite or any other key to cancel.",
567 _("File exists:"), filename, append_wchar,
568 overwrite_wchar);
569 return;
572 if (do_game_write(filename, "a", saveindex, end))
573 update_status_notify(gp, "%s", _("Save game failed."));
574 else
575 update_status_notify(gp, "%s", _("Game saved."));
578 static int castling_state(GAME g, BOARD b, int row, int col, int piece, int mod)
580 if (pgn_piece_to_int(piece) == ROOK && col == 7
581 && row == 7 &&
582 (TEST_FLAG(g->flags, GF_WK_CASTLE) || mod) &&
583 pgn_piece_to_int(b[7][4].icon) == KING && isupper(piece)) {
584 if (mod)
585 TOGGLE_FLAG(g->flags, GF_WK_CASTLE);
586 return 1;
588 else if (pgn_piece_to_int(piece) == ROOK && col == 0
589 && row == 7 &&
590 (TEST_FLAG(g->flags, GF_WQ_CASTLE) || mod) &&
591 pgn_piece_to_int(b[7][4].icon) == KING && isupper(piece)) {
592 if (mod)
593 TOGGLE_FLAG(g->flags, GF_WQ_CASTLE);
594 return 1;
596 else if (pgn_piece_to_int(piece) == ROOK && col == 7
597 && row == 0 &&
598 (TEST_FLAG(g->flags, GF_BK_CASTLE) || mod) &&
599 pgn_piece_to_int(b[0][4].icon) == KING && islower(piece)) {
600 if (mod)
601 TOGGLE_FLAG(g->flags, GF_BK_CASTLE);
602 return 1;
604 else if (pgn_piece_to_int(piece) == ROOK && col == 0
605 && row == 0 &&
606 (TEST_FLAG(g->flags, GF_BQ_CASTLE) || mod) &&
607 pgn_piece_to_int(b[0][4].icon) == KING && islower(piece)) {
608 if (mod)
609 TOGGLE_FLAG(g->flags, GF_BQ_CASTLE);
610 return 1;
612 else if (pgn_piece_to_int(piece) == KING && col == 4
613 && row == 7 &&
614 (mod || (pgn_piece_to_int(b[7][7].icon) == ROOK &&
615 TEST_FLAG(g->flags, GF_WK_CASTLE))
617 (pgn_piece_to_int(b[7][0].icon) == ROOK &&
618 TEST_FLAG(g->flags, GF_WQ_CASTLE))) && isupper(piece)) {
619 if (mod) {
620 if (TEST_FLAG(g->flags, GF_WK_CASTLE) ||
621 TEST_FLAG(g->flags, GF_WQ_CASTLE))
622 CLEAR_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE);
623 else
624 SET_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE);
626 return 1;
628 else if (pgn_piece_to_int(piece) == KING && col == 4
629 && row == 0 &&
630 (mod || (pgn_piece_to_int(b[0][7].icon) == ROOK &&
631 TEST_FLAG(g->flags, GF_BK_CASTLE))
633 (pgn_piece_to_int(b[0][0].icon) == ROOK &&
634 TEST_FLAG(g->flags, GF_BQ_CASTLE))) && islower(piece)) {
635 if (mod) {
636 if (TEST_FLAG(g->flags, GF_BK_CASTLE) ||
637 TEST_FLAG(g->flags, GF_BQ_CASTLE))
638 CLEAR_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE);
639 else
640 SET_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE);
642 return 1;
645 return 0;
648 #define IS_ENPASSANT(c) (c == 'x') ? CP_BOARD_ENPASSANT : isupper(c) ? CP_BOARD_WHITE : CP_BOARD_BLACK
649 #define ATTRS(cp) (cp & (A_BOLD|A_STANDOUT|A_BLINK|A_DIM|A_UNDERLINE|A_INVIS|A_REVERSE))
651 static void
652 init_wchar_pieces ()
654 w_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♙" : "P");
655 w_rook_wchar = str_to_wchar (config.utf8_pieces ? "♖" : "R");
656 w_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♗" : "B");
657 w_knight_wchar = str_to_wchar (config.utf8_pieces ? "♘" : "N");
658 w_queen_wchar = str_to_wchar (config.utf8_pieces ? "♕" : "Q");
659 w_king_wchar = str_to_wchar (config.utf8_pieces ? "♔" : "K");
660 b_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♟" : "p");
661 b_rook_wchar = str_to_wchar (config.utf8_pieces ? "♜" : "r");
662 b_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♝" : "b");
663 b_knight_wchar = str_to_wchar (config.utf8_pieces ? "♞" : "n");
664 b_queen_wchar = str_to_wchar (config.utf8_pieces ? "♛" : "q");
665 b_king_wchar = str_to_wchar (config.utf8_pieces ? "♚" : "k");
666 empty_wchar = str_to_wchar (" ");
667 enpassant_wchar = str_to_wchar ("x");
670 static wchar_t *
671 piece_to_wchar (unsigned char p)
673 switch (p)
675 case 'P':
676 return w_pawn_wchar;
677 case 'p':
678 return b_pawn_wchar;
679 case 'R':
680 return w_rook_wchar;
681 case 'r':
682 return b_rook_wchar;
683 case 'B':
684 return w_bishop_wchar;
685 case 'b':
686 return b_bishop_wchar;
687 case 'N':
688 return w_knight_wchar;
689 case 'n':
690 return b_knight_wchar;
691 case 'Q':
692 return w_queen_wchar;
693 case 'q':
694 return b_queen_wchar;
695 case 'K':
696 return w_king_wchar;
697 case 'k':
698 return b_king_wchar;
699 case 'x':
700 return enpassant_wchar;
703 return empty_wchar;
706 static int piece_can_attack (GAME g, int rank, int file)
708 struct userdata_s *d = g->data;
709 char *m, *frfr = NULL;
710 pgn_error_t e;
711 int row, col, p, v, pi, cpi;
713 if (d->rotate) {
714 rotate_position(&d->c_row, &d->c_col);
715 rotate_position(&d->sp.srow, &d->sp.scol);
718 v = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].valid;
719 pi = pgn_piece_to_int (d->b[RANKTOBOARD (rank)][FILETOBOARD (file)].icon);
720 cpi = d->sp.icon
721 ? pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon)
722 : pgn_piece_to_int (d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon);
724 if (pi == OPEN_SQUARE || cpi == OPEN_SQUARE || !VALIDFILE (file)
725 || !VALIDRANK (rank)) {
726 if (d->rotate) {
727 rotate_position(&d->c_row, &d->c_col);
728 rotate_position(&d->sp.srow, &d->sp.scol);
731 return 0;
734 if (d->sp.icon) {
735 col = v ? d->c_col : d->sp.scol;
736 row = v ? d->c_row : d->sp.srow;
738 else {
739 col = d->c_col;
740 row = d->c_row;
743 m = malloc(MAX_SAN_MOVE_LEN+1);
744 m[0] = INTTOFILE (file);
745 m[1] = INTTORANK (rank);
746 m[2] = INTTOFILE (col);
747 m[3] = INTTORANK (row);
748 m[4] = 0;
750 if (d->sp.icon && v) {
751 BOARD b;
753 memcpy (b, d->b, sizeof(BOARD));
754 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
755 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
756 pgn_int_to_piece (WHITE, OPEN_SQUARE);
757 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
758 pgn_switch_turn(g);
759 e = pgn_validate_move (g, b, &m, &frfr);
760 pgn_switch_turn (g);
761 free (m);
762 free (frfr);
764 if (e != E_PGN_OK && pgn_piece_to_int (d->sp.icon) == PAWN) {
765 int n = (d->sp.srow == 7 && rank == 5) ? 6 :
766 (d->sp.srow == 2 && rank == 4) ? 3 : 0;
768 if (n && (file == d->c_col-1 || file == d->c_col+1)) {
769 memcpy (b, d->b, sizeof(BOARD));
770 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
771 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
772 pgn_int_to_piece (WHITE, OPEN_SQUARE);
773 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
774 b[RANKTOBOARD (n)][FILETOBOARD (d->sp.scol)].enpassant = 1;
775 m = malloc(MAX_SAN_MOVE_LEN+1);
776 m[0] = INTTOFILE (file);
777 m[1] = INTTORANK (rank);
778 m[2] = INTTOFILE (col);
779 m[3] = INTTORANK (n);
780 m[4] = 0;
781 pgn_switch_turn(g);
782 SET_FLAG (g->flags, GF_ENPASSANT);
783 e = pgn_validate_move (g, b, &m, &frfr);
784 CLEAR_FLAG (g->flags, GF_ENPASSANT);
785 pgn_switch_turn (g);
786 free (m);
787 free (frfr);
791 goto pca_quit;
794 pgn_switch_turn(g);
795 e = pgn_validate_move (g, d->b, &m, &frfr);
796 pgn_switch_turn (g);
798 if (!strcmp (m, "O-O") || !strcmp (m, "O-O-O"))
799 e = E_PGN_INVALID;
801 if (e == E_PGN_OK) {
802 int sf = FILETOINT (frfr[0]), sr = RANKTOINT (frfr[1]);
803 int df = FILETOINT (frfr[2]);
805 pi = d->b[RANKTOBOARD (sr)][FILETOBOARD (sf)].icon;
806 pi = pgn_piece_to_int (pi);
807 if (pi == PAWN && sf == df)
808 e = E_PGN_INVALID;
811 free (m);
812 free (frfr);
814 pca_quit:
815 if (d->rotate) {
816 rotate_position(&d->c_row, &d->c_col);
817 rotate_position(&d->sp.srow, &d->sp.scol);
820 return e == E_PGN_OK ? 1 : 0;
823 void print_piece(WINDOW *w, int l, int c, char p)
825 int i, y, ff = 0;
827 for (i = 0; i < 13; i += 2) {
828 if (p == piece_chars[i] || p == piece_chars[i + 1]) {
829 for (y = 0; y < 3; y++)
830 mvwprintw(w, l + y, c, "%s", f_pieces[i + ff + y]);
831 return;
834 ff++;
837 for (y = 0; y < 3; y++)
838 mvwprintw(w, l + y, c, f_pieces[0]);
841 void board_prev_move_play (GAME g)
843 struct userdata_s *d = g->data;
845 char l = strlen(d->pm_frfr);
846 if (l) {
847 char q = (l > 4) ? 2 : 1;
849 d->pm_row = RANKTOINT(d->pm_frfr[l - q++]);
850 d->pm_col = FILETOINT(d->pm_frfr[l - q++]);
851 d->ospm_row = RANKTOINT(d->pm_frfr[l - q++]);
852 d->ospm_col = FILETOINT(d->pm_frfr[l - q]);
853 if (d->rotate) {
854 rotate_position(&d->pm_row, &d->pm_col);
855 rotate_position(&d->ospm_row, &d->ospm_col);
858 else {
859 d->pm_row = 0;
860 d->pm_col = 0;
861 d->ospm_row = 0;
862 d->ospm_col = 0;
866 void board_prev_move_history (GAME g)
868 struct userdata_s *d = g->data;
870 if (g->hindex) {
871 char *move = g->hp[g->hindex - 1]->move;
873 if (move) {
874 if (d->mode == MODE_PLAY)
875 coordofmove(g, move, &d->pm_row, &d->pm_col);
876 else {
877 d->pm_row = 0;
878 d->pm_col = 0;
881 if (*move == 'O') {
882 d->ospm_row = g->turn == WHITE ? 8 : 1;
883 d->ospm_col = 5;
884 return;
887 BOARD ob;
888 char f, r;
889 pgn_board_init(ob);
890 if (g->hindex > 1) {
891 HISTORY *h = pgn_history_by_n(g->hp, g->hindex - 2);
892 if (h) {
893 pgn_board_init_fen(g, ob, h->fen);
894 pgn_switch_turn(g);
897 for (f = 0; f < 8; f++) {
898 for (r = 0; r < 8; r++) {
899 if (ob[f][r].icon != '.' && d->b[f][r].icon == '.') {
900 d->ospm_row = INV_INT0(f) + 1;
901 d->ospm_col = r + 1;
902 break;
906 if (d->rotate) {
907 if (d->mode == MODE_PLAY)
908 rotate_position(&d->pm_row, &d->pm_col);
909 rotate_position(&d->ospm_row, &d->ospm_col);
911 return;
914 else {
915 d->ospm_row = 0;
916 d->ospm_col = 0;
917 d->pm_row = 0;
918 d->pm_col = 0;
922 static int is_the_square (int brow, int bcol, int row, int col,
923 int prow, int pcol)
925 if ((!BIG_BOARD &&
926 row == ROWTOMATRIX(prow) && col == COLTOMATRIX(pcol))
927 || (BIG_BOARD &&
928 brow + 1 == INV_INT(prow) && bcol + 1 == pcol))
929 return 1;
931 return 0;
934 static int is_prev_move (struct userdata_s *d, int brow, int bcol,
935 int row, int col)
937 if (is_the_square (brow, bcol, row, col, d->pm_row, d->pm_col))
938 return 1;
940 return 0;
943 void update_board_window(GAME g)
945 int row, col;
946 int bcol = 0, brow = 0;
947 int l = config.coordsyleft;
948 int maxy = BOARD_HEIGHT, maxx = BOARD_WIDTH;
949 int ncols = 0, offset = 1;
950 int rowr = (MEGA_BOARD) ? 6 : (BIG_BOARD) ? 4 : 2;
951 int colr = (MEGA_BOARD) ? 12 : (BIG_BOARD) ? 8 : 4;
952 unsigned coords_y = 8, cxgc = 0;
953 unsigned i, cpd = 0;
954 struct userdata_s *d = g->data;
956 if (config.bprevmove && d->mode != MODE_EDIT)
957 if (!d->pm_undo && d->mode == MODE_PLAY)
958 board_prev_move_play(g);
959 else
960 board_prev_move_history(g);
961 else {
962 d->pm_row = 0;
963 d->pm_col = 0;
964 d->ospm_row = 0;
965 d->ospm_col = 0;
968 if (d->mode != MODE_PLAY && d->mode != MODE_EDIT)
969 update_cursor(g, g->hindex);
971 if (BIG_BOARD) {
972 if (d->rotate) {
973 brow = 7;
974 coords_y = 1;
977 else {
978 if (d->rotate) {
979 brow = 1;
980 coords_y = 1;
982 else
983 brow = 8;
986 for (row = 0; row < maxy; row++) {
987 if (BIG_BOARD) {
988 if (d->rotate)
989 bcol = 7;
990 else
991 bcol = 0;
993 else {
994 if (d->rotate)
995 bcol = 8;
996 else
997 bcol = 1;
1000 for (col = 0; col < maxx; col++) {
1001 int attrwhich = -1;
1002 chtype attrs = 0, old_attrs = 0;
1003 unsigned char p;
1004 int can_attack = 0;
1005 int valid = 0;
1007 if (row == 0 || row == maxy - 2) {
1008 if (col == 0)
1009 mvwaddch(boardw, row, col + l,
1010 LINE_GRAPHIC((row)
1011 ? ACS_LLCORNER | CP_BOARD_GRAPHICS
1012 : ACS_ULCORNER | CP_BOARD_GRAPHICS));
1013 else if (col == maxx - 2)
1014 mvwaddch(boardw, row, col + l,
1015 LINE_GRAPHIC((row)
1016 ? ACS_LRCORNER | CP_BOARD_GRAPHICS
1017 : ACS_URCORNER | CP_BOARD_GRAPHICS));
1018 else if (!(col % colr))
1019 mvwaddch(boardw, row, col + l,
1020 LINE_GRAPHIC((row)
1021 ? ACS_BTEE | CP_BOARD_GRAPHICS
1022 : ACS_TTEE | CP_BOARD_GRAPHICS));
1023 else {
1024 if (col != maxx - 1)
1025 mvwaddch(boardw, row, col + l,
1026 LINE_GRAPHIC(ACS_HLINE | CP_BOARD_GRAPHICS));
1029 continue;
1032 if ((row % 2) && col == maxx - 1 &&
1033 (coords_y > 0 && coords_y < 9)) {
1034 wattron(boardw, CP_BOARD_COORDS);
1035 mvwprintw(boardw,
1036 (BIG_BOARD) ? row * ((MEGA_BOARD) ? 3 : 2)
1037 : row, (l) ? 0 : col, "%d",
1038 (d->rotate) ? coords_y++ : coords_y--);
1039 wattroff(boardw, CP_BOARD_COORDS);
1040 continue;
1043 if ((col == 0 || col == maxx - 2) && row != maxy - 1) {
1044 if (!(row % rowr))
1045 mvwaddch(boardw, row, col + l,
1046 LINE_GRAPHIC((col) ?
1047 ACS_RTEE | CP_BOARD_GRAPHICS :
1048 ACS_LTEE | CP_BOARD_GRAPHICS));
1049 else
1050 mvwaddch(boardw, row, col + l,
1051 LINE_GRAPHIC(ACS_VLINE | CP_BOARD_GRAPHICS));
1053 continue;
1056 if ((row % rowr) && !(col % colr) && row != maxy - 1) {
1057 mvwaddch(boardw, row, col + l,
1058 LINE_GRAPHIC(ACS_VLINE | CP_BOARD_GRAPHICS));
1059 continue;
1062 if (!(col % colr) && row != maxy - 1) {
1063 mvwaddch(boardw, row, col + l,
1064 LINE_GRAPHIC(ACS_PLUS | CP_BOARD_GRAPHICS));
1065 continue;
1068 if ((row % rowr)) {
1069 if ((col % colr)) {
1070 if (BIG_BOARD)
1071 attrwhich = (cb[brow][bcol]) ? WHITE : BLACK;
1072 else {
1073 if (ncols++ == 8) {
1074 offset++;
1075 ncols = 1;
1078 if (((ncols % 2) && !(offset % 2))
1079 || (!(ncols % 2) && (offset % 2)))
1080 attrwhich = BLACK;
1081 else
1082 attrwhich = WHITE;
1085 if (BIG_BOARD && d->rotate) {
1086 brow = INV_INT0(brow);
1087 bcol = INV_INT0(bcol);
1090 if (BIG_BOARD)
1091 p = d->b[brow][bcol].icon;
1092 else
1093 p = d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].icon;
1095 int pi = pgn_piece_to_int(p);
1097 if (config.details &&
1098 ((!BIG_BOARD && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].enpassant)
1099 || (BIG_BOARD && d->b[brow][bcol].enpassant))) {
1100 p = pi = 'x';
1101 attrs = mix_cp(CP_BOARD_ENPASSANT,
1102 (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK,
1103 ATTRS(CP_BOARD_ENPASSANT), A_FG_B_BG);
1106 if (config.showattacks && config.details
1107 && piece_can_attack (g,
1108 BIG_BOARD ? INV_INT0(brow)+1 : brow,
1109 BIG_BOARD ? bcol+1 : bcol)) {
1110 attrs = CP_BOARD_ATTACK;
1111 old_attrs = attrs;
1112 can_attack = 1;
1115 if (config.validmoves &&
1116 ((!BIG_BOARD && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].valid)
1117 || (BIG_BOARD && d->b[brow][bcol].valid))) {
1118 old_attrs = -1;
1119 valid = 1;
1121 if (attrwhich == WHITE)
1122 attrs = mix_cp(CP_BOARD_MOVES_WHITE,
1123 IS_ENPASSANT(p),
1124 ATTRS(CP_BOARD_MOVES_WHITE),
1125 B_FG_A_BG);
1126 else
1127 attrs = mix_cp(CP_BOARD_MOVES_BLACK,
1128 IS_ENPASSANT(p),
1129 ATTRS(CP_BOARD_MOVES_BLACK),
1130 B_FG_A_BG);
1132 else if (p != 'x' && !can_attack)
1133 attrs = (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK;
1135 if (BIG_BOARD && d->rotate) {
1136 brow = INV_INT0(brow);
1137 bcol = INV_INT0(bcol);
1140 if (is_the_square (brow, bcol, row, col, d->c_row,
1141 d->c_col)) {
1142 attrs = mix_cp(CP_BOARD_CURSOR, IS_ENPASSANT(p),
1143 ATTRS(CP_BOARD_CURSOR), B_FG_A_BG);
1144 old_attrs = -1;
1146 else if (is_the_square (brow, bcol, row, col, d->sp.srow,
1147 d->sp.scol)) {
1148 attrs = mix_cp(CP_BOARD_SELECTED, IS_ENPASSANT(p),
1149 ATTRS(CP_BOARD_SELECTED), B_FG_A_BG);
1150 old_attrs = -1;
1152 else if ((is_prev_move (d, brow, bcol, row, col) && !valid)
1153 || (is_the_square (brow, bcol, row, col, d->ospm_row,
1154 d->ospm_col))) {
1155 attrs = mix_cp(CP_BOARD_PREVMOVE, IS_ENPASSANT(p),
1156 ATTRS(CP_BOARD_PREVMOVE), B_FG_A_BG);
1157 old_attrs = -1;
1160 if (row == maxy - 1)
1161 attrs = 0;
1163 if (can_attack) {
1164 int n = is_prev_move (d, brow, bcol, row, col);
1165 chtype a = n && !valid
1166 ? CP_BOARD_PREVMOVE : attrwhich == WHITE ? valid ? CP_BOARD_MOVES_WHITE : CP_BOARD_WHITE : valid ? CP_BOARD_MOVES_BLACK : CP_BOARD_BLACK;
1167 attrs = mix_cp(CP_BOARD_ATTACK, a,
1168 ATTRS (CP_BOARD_ATTACK), A_FG_B_BG);
1169 old_attrs = -1;
1172 if (BIG_BOARD)
1173 wmove(boardw, row, col + ((MEGA_BOARD) ? 5 : 3) + l);
1174 else
1175 mvwaddch(boardw, row, col + l, ' ' | attrs);
1177 if (row == maxy - 1 && cxgc < 8) {
1178 waddch(boardw, "abcdefgh"[(BIG_BOARD) ? bcol : bcol - 1] | CP_BOARD_COORDS);
1179 cxgc++;
1181 else {
1182 if (old_attrs == -1) {
1183 old_attrs = attrs;
1184 goto printc;
1187 old_attrs = attrs;
1189 if (pi != OPEN_SQUARE && p != 'x' && !can_attack) {
1190 if (attrwhich == WHITE) {
1191 if (isupper(p))
1192 attrs = CP_BOARD_W_W;
1193 else
1194 attrs = CP_BOARD_W_B;
1196 else {
1197 if (isupper(p))
1198 attrs = CP_BOARD_B_W;
1199 else
1200 attrs = CP_BOARD_B_B;
1204 printc:
1205 if (BIG_BOARD) {
1206 if (config.details && !can_attack
1207 && castling_state(g, d->b,
1208 (d->rotate) ? INV_INT(brow + 1) - 1: brow,
1209 (d->rotate) ? INV_INT(bcol + 1) - 1: bcol, p, 0))
1210 attrs = mix_cp(CP_BOARD_CASTLING, attrs,
1211 ATTRS(CP_BOARD_CASTLING),
1212 A_FG_B_BG);
1214 else {
1215 if (config.details && !can_attack
1216 && castling_state(g, d->b, RANKTOBOARD (brow),
1217 FILETOBOARD (bcol), p, 0)) {
1218 attrs = mix_cp(CP_BOARD_CASTLING, attrs,
1219 ATTRS(CP_BOARD_CASTLING),
1220 A_FG_B_BG);
1224 if (BIG_BOARD) {
1225 // FIXME: Reimpresión de piezas(+4).
1226 if (cpd < 67) {
1227 wattron (boardw, attrs);
1228 if (MEGA_BOARD){
1229 for (i = 0; i < 5; i++)
1230 mvwprintw(boardw, i + brow * 6 + 1,
1231 bcol * 12 + 1 + l,
1232 " ");
1233 if (pi != OPEN_SQUARE)
1234 print_piece(boardw, brow * 6 + 2,
1235 bcol * 12 + 3 + l, p);
1237 else {
1238 print_piece(boardw, brow * 4 + 1,
1239 bcol * 8 + 1 + l,
1240 (pi != OPEN_SQUARE) ? p : 0);
1243 wattroff (boardw, attrs);
1244 cpd++;
1247 else {
1248 wattron (boardw, attrs);
1249 waddwstr (boardw, piece_to_wchar (pi != OPEN_SQUARE ? p : 0));
1250 wattroff (boardw, attrs);
1253 attrs = old_attrs;
1256 if (BIG_BOARD)
1257 col += (MEGA_BOARD) ? 10 : 6;
1258 else {
1259 waddch(boardw, ' ' | attrs);
1260 col += 2;
1263 if (d->rotate)
1264 bcol--;
1265 else
1266 bcol++;
1268 if (BIG_BOARD) {
1269 if (bcol > 7)
1270 bcol = 0;
1271 if (bcol < 0)
1272 bcol = 7;
1276 else {
1277 if (col != maxx - 1)
1278 mvwaddch(boardw, row, col + l,
1279 LINE_GRAPHIC(ACS_HLINE | CP_BOARD_GRAPHICS));
1283 if (row % rowr) {
1284 if (d->rotate)
1285 brow++;
1286 else
1287 brow--;
1290 if (BIG_BOARD) {
1291 if (brow > 7)
1292 brow = 0;
1293 if (brow < 0)
1294 brow = 7;
1299 void invalid_move(int n, int e, const char *m)
1301 if (curses_initialized)
1302 cmessage(ERROR_STR, ANY_KEY_STR, "%s \"%s\" (round #%i)", (e == E_PGN_AMBIGUOUS)
1303 ? _("Ambiguous move") : _("Invalid move"), m, n);
1304 else
1305 warnx("%s: %s \"%s\" (round #%i)", loadfile, (e == E_PGN_AMBIGUOUS)
1306 ? _("Ambiguous move") : _("Invalid move"), m, n);
1309 void gameover(GAME g)
1311 struct userdata_s *d = g->data;
1313 SET_FLAG(g->flags, GF_GAMEOVER);
1314 d->mode = MODE_HISTORY;
1315 stop_engine(g);
1318 static void update_clock(GAME g, struct itimerval it)
1320 struct userdata_s *d = g->data;
1322 if (TEST_FLAG(d->flags, CF_CLOCK) && g->turn == WHITE) {
1323 d->wclock.elapsed.tv_sec += it.it_value.tv_sec;
1324 d->wclock.elapsed.tv_usec += it.it_value.tv_usec;
1326 if (d->wclock.elapsed.tv_usec > 1000000 - 1) {
1327 d->wclock.elapsed.tv_sec += d->wclock.elapsed.tv_usec / 1000000;
1328 d->wclock.elapsed.tv_usec = d->wclock.elapsed.tv_usec % 1000000;
1331 if (d->wclock.tc[d->wclock.tcn][1] &&
1332 d->wclock.elapsed.tv_sec >= d->wclock.tc[d->wclock.tcn][1]) {
1333 pgn_tag_add(&g->tag, "Result", "0-1");
1334 gameover(g);
1337 else if (TEST_FLAG(d->flags, CF_CLOCK) && g->turn == BLACK) {
1338 d->bclock.elapsed.tv_sec += it.it_value.tv_sec;
1339 d->bclock.elapsed.tv_usec += it.it_value.tv_usec;
1341 if (d->bclock.elapsed.tv_usec > 1000000 - 1) {
1342 d->bclock.elapsed.tv_sec += d->bclock.elapsed.tv_usec / 1000000;
1343 d->bclock.elapsed.tv_usec = d->bclock.elapsed.tv_usec % 1000000;
1346 if (d->bclock.tc[d->bclock.tcn][1] &&
1347 d->bclock.elapsed.tv_sec >= d->bclock.tc[d->bclock.tcn][1]) {
1348 pgn_tag_add(&g->tag, "Result", "1-0");
1349 gameover(g);
1353 d->elapsed.tv_sec += it.it_value.tv_sec;
1354 d->elapsed.tv_usec += it.it_value.tv_usec;
1356 if (d->elapsed.tv_usec > 1000000 - 1) {
1357 d->elapsed.tv_sec += d->elapsed.tv_usec / 1000000;
1358 d->elapsed.tv_usec = d->elapsed.tv_usec % 1000000;
1362 static void update_time_control(GAME g)
1364 struct userdata_s *d = g->data;
1365 struct clock_s *clk = (g->turn == WHITE) ? &d->wclock : &d->bclock;
1367 if (clk->incr)
1368 clk->tc[clk->tcn][1] += clk->incr;
1370 if (!clk->tc[clk->tcn][1])
1371 return;
1373 clk->move++;
1375 if (!clk->tc[clk->tcn][0] || clk->move >= clk->tc[clk->tcn][0]) {
1376 clk->move = 0;
1377 clk->tc[clk->tcn + 1][1] += abs(clk->elapsed.tv_sec - clk->tc[clk->tcn][1]);
1378 memset(&clk->elapsed, 0, sizeof(clk->elapsed));
1379 clk->tcn++;
1383 void update_history_window(GAME g)
1385 char buf[HISTORY_WIDTH - 1];
1386 HISTORY *h = NULL;
1387 int n, total;
1388 int t = pgn_history_total(g->hp);
1390 n = (g->hindex + 1) / 2;
1392 if (t % 2)
1393 total = (t + 1) / 2;
1394 else
1395 total = t / 2;
1397 if (t)
1398 snprintf(buf, sizeof(buf), "%u %s %u%s", n, _("of"), total,
1399 (movestep == 1) ? _(" (ply)") : "");
1400 else
1401 strncpy(buf, _("not available"), sizeof(buf)-1);
1403 buf[sizeof(buf)-1] = 0;
1404 mvwprintw(historyw, 2, 1, "%*s %-*s", 10, _("Move:"),
1405 HISTORY_WIDTH - 14, buf);
1407 h = pgn_history_by_n(g->hp, g->hindex);
1408 snprintf(buf, sizeof(buf), "%s",
1409 (h && h->move) ? h->move
1410 : (LINES < 24) ? _("empty") : _("not available"));
1411 n = 0;
1413 if (h && ((h->comment) || h->nag[0])) {
1414 strncat(buf, _(" (Annotated"), sizeof(buf)-1);
1415 n++;
1418 if (h && h->rav) {
1419 strncat(buf, (n) ? ",+" : " (+", sizeof(buf)-1);
1420 n++;
1423 if (g->ravlevel) {
1424 strncat(buf, (n) ? ",-" : " (-", sizeof(buf)-1);
1425 n++;
1428 if (n)
1429 strncat(buf, ")", sizeof(buf)-1);
1431 mvwprintw(historyw, 3, ((LINES < 24) ? 17 : 1), "%s %-*s",
1432 (LINES < 24) ? _("Next:") :_("Next move:"),
1433 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1435 h = pgn_history_by_n(g->hp, g->hindex - 1);
1436 snprintf(buf, sizeof(buf), "%s",
1437 (h && h->move) ? h->move
1438 : (LINES < 24) ? _("empty") : _("not available"));
1439 n = 0;
1441 if (h && ((h->comment) || h->nag[0])) {
1442 strncat(buf, _(" (Annotated"), sizeof(buf)-1);
1443 n++;
1446 if (h && h->rav) {
1447 strncat(buf, (n) ? ",+" : " (+", sizeof(buf)-1);
1448 n++;
1451 if (g->ravlevel) {
1452 strncat(buf, (n) ? ",-" : " (-", sizeof(buf)-1);
1453 n++;
1456 if (n)
1457 strncat(buf, ")", sizeof(buf)-1);
1459 mvwprintw(historyw, ((LINES < 24) ? 3 : 4), 1, "%s %-*s",
1460 (LINES < 24) ? _("Prev.:") : _("Prev move:"),
1461 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1464 void do_validate_move(char **move)
1466 struct userdata_s *d = gp->data;
1467 int n;
1468 char *frfr = NULL;
1470 if (TEST_FLAG(d->flags, CF_HUMAN)) {
1471 if ((n = pgn_parse_move(gp, d->b, move, &frfr)) != E_PGN_OK) {
1472 invalid_move(d->n + 1, n, *move);
1473 return;
1476 strcpy(d->pm_frfr, frfr);
1477 update_time_control(gp);
1478 pgn_history_add(gp, d->b, *move);
1479 pgn_switch_turn(gp);
1481 else {
1482 if ((n = pgn_validate_move(gp, d->b, move, &frfr)) != E_PGN_OK) {
1483 invalid_move(d->n + 1, n, *move);
1484 return;
1487 add_engine_command(gp, ENGINE_THINKING, "%s\n",
1488 (config.engine_protocol == 1) ? frfr : *move);
1491 d->sp.srow = d->sp.scol = d->sp.icon = 0;
1493 if (config.validmoves)
1494 pgn_reset_valid_moves(d->b);
1496 if (TEST_FLAG(gp->flags, GF_GAMEOVER))
1497 d->mode = MODE_HISTORY;
1498 else
1499 SET_FLAG(d->flags, CF_MODIFIED);
1501 free (frfr);
1502 d->paused = 0;
1503 update_history_window(gp);
1504 update_board_window(gp);
1505 return;
1508 void do_promotion_piece_finalize(WIN *win)
1510 char *p, *str = win->data;
1512 if (pgn_piece_to_int(win->c) == -1)
1513 return;
1515 p = str + strlen(str);
1516 *p++ = toupper(win->c);
1517 *p = '\0';
1518 do_validate_move(&str);
1519 free (str);
1520 win->data = NULL;
1523 static void move_to_engine(GAME g)
1525 struct userdata_s *d = g->data;
1526 char *str;
1527 int piece;
1529 if (config.validmoves &&
1530 !d->b[RANKTOBOARD(d->sp.row)][FILETOBOARD(d->sp.col)].valid)
1531 return;
1533 str = Malloc(MAX_SAN_MOVE_LEN + 1);
1534 snprintf(str, MAX_SAN_MOVE_LEN + 1, "%c%i%c%i",
1535 _("abcdefgh")[d->sp.scol - 1],
1536 d->sp.srow, _("abcdefgh")[d->sp.col - 1], d->sp.row);
1538 piece = pgn_piece_to_int(d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon);
1540 if (piece == PAWN && (d->sp.row == 8 || d->sp.row == 1)) {
1541 construct_message(_("Select Pawn Promotion Piece"), _ ("R/N/B/Q"), 1, 1, NULL, NULL,
1542 str, do_promotion_piece_finalize, 0, 0, "%s", _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1543 return;
1546 do_validate_move(&str);
1547 free (str);
1550 static char *clock_to_char(long n)
1552 static char buf[16];
1553 int h = 0, m = 0, s = 0;
1555 h = n / 3600;
1556 m = (n % 3600) / 60;
1557 s = (n % 3600) % 60;
1558 snprintf(buf, sizeof(buf), "%.2i:%.2i:%.2i", h, m, s);
1559 return buf;
1562 static char *timeval_to_char(struct timeval t, long limit)
1564 static char buf[9];
1565 int h = 0, m = 0, s = 0;
1566 int n = limit ? abs(limit - t.tv_sec) : 0;
1568 h = n / 3600;
1569 m = (n % 3600) / 60;
1570 s = (n % 3600) % 60;
1571 snprintf(buf, sizeof(buf), "%.2i:%.2i:%.2i", h, m, s);
1572 return buf;
1575 static char *time_control_status(struct clock_s *clk)
1577 static char buf[80] = {0};
1579 buf[0] = 0;
1581 if (clk->tc[clk->tcn][0] && clk->tc[clk->tcn + 1][1])
1582 snprintf(buf, sizeof(buf), " M%.2i/%s", abs(clk->tc[clk->tcn][0] - clk->move),
1583 clock_to_char(clk->tc[clk->tcn + 1][1]));
1584 else if (!clk->incr)
1585 return "";
1587 if (clk->incr) {
1588 char buf[16];
1589 strncat(buf, " I", sizeof(buf)-1);
1590 strncat(buf, itoa(clk->incr, buf), sizeof(buf)-1);
1593 return buf;
1596 void update_status_window(GAME g)
1598 int i = 0;
1599 char *buf;
1600 char tmp[15] = {0}, *engine, *mode;
1601 char t[COLS];
1602 int w;
1603 char *p;
1604 int maxy, maxx;
1605 int len;
1606 struct userdata_s *d = g->data;
1607 int y;
1608 int n;
1610 if (!curses_initialized)
1611 return;
1613 getmaxyx(statusw, maxy, maxx);
1614 (void)maxy;
1615 w = maxx - 2 - 8;
1616 len = maxx - 2;
1617 buf = Malloc(len);
1618 y = 2;
1620 wchar_t *loadfilew = loadfile[0] ? str_etc (loadfile, w, 1) : str_to_wchar (_ ("not available"));
1621 mvwprintw(statusw, y++, 1, "%*s %-*ls", 7, _("File:"), w, loadfilew);
1622 free (loadfilew);
1623 snprintf(buf, len, "%i %s %i", gindex + 1, _("of"), gtotal);
1624 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Game:"), w, buf);
1626 *tmp = '\0';
1627 p = tmp;
1629 if (config.details) {
1630 *p++ = 'D';
1631 i++;
1634 if (TEST_FLAG(d->flags, CF_DELETE)) {
1635 if (i)
1636 *p++ = '/';
1638 *p++ = 'X';
1639 i++;
1642 if (TEST_FLAG(g->flags, GF_PERROR)) {
1643 if (i)
1644 *p++ = '/';
1646 *p++ = '!';
1647 i++;
1650 if (TEST_FLAG(d->flags, CF_MODIFIED)) {
1651 if (i)
1652 *p++ = '/';
1654 *p++ = '*';
1655 i++;
1658 pgn_config_get(PGN_STRICT_CASTLING, &n);
1660 if (n == 1) {
1661 if (i)
1662 *p++ = '/';
1664 *p++ = 'C';
1665 i++;
1667 #ifdef WITH_LIBPERL
1668 if (TEST_FLAG(d->flags, CF_PERL)) {
1669 if (i)
1670 *p++ = '/';
1672 *p++ = 'P';
1673 i++;
1675 #endif
1677 *p = '\0';
1678 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Flags:"), w, (tmp[0]) ? tmp : "-");
1680 switch (d->mode) {
1681 case MODE_HISTORY:
1682 mode = _("move history");
1683 break;
1684 case MODE_EDIT:
1685 mode = _("edit");
1686 break;
1687 case MODE_PLAY:
1688 mode = _("play");
1689 break;
1690 default:
1691 mode = _("(empty value)");
1692 break;
1695 snprintf(buf, len - 1, "%*s %s", 7, _("Mode:"), mode);
1697 if (d->mode == MODE_PLAY) {
1698 if (TEST_FLAG(d->flags, CF_HUMAN))
1699 strncat(buf, _(" (human/human)"), len - 1);
1700 else if (TEST_FLAG(d->flags, CF_ENGINE_LOOP))
1701 strncat(buf, _(" (engine/engine)"), len - 1);
1702 else
1703 strncat(buf, (d->play_mode == PLAY_EH) ?
1704 _(" (engine/human)") : _(" (human/engine)"), len - 1);
1707 buf[len-1] = 0;
1708 mvwprintw(statusw, y++, 1, "%-*s", len, buf);
1709 free(buf);
1711 if (d->engine) {
1712 switch (d->engine->status) {
1713 case ENGINE_THINKING:
1714 engine = _("pondering...");
1715 break;
1716 case ENGINE_READY:
1717 engine = _("ready");
1718 break;
1719 case ENGINE_INITIALIZING:
1720 engine = _("initializing...");
1721 break;
1722 case ENGINE_OFFLINE:
1723 engine = _("offline");
1724 break;
1725 default:
1726 engine = _("(empty value)");
1727 break;
1730 else
1731 engine = _("offline");
1733 mvwprintw(statusw, y, 1, "%*s %-*s", 7, _("Engine:"), w, " ");
1734 wattron(statusw, CP_STATUS_ENGINE);
1735 mvwaddstr(statusw, y++, 9, engine);
1736 wattroff(statusw, CP_STATUS_ENGINE);
1738 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Turn:"), w,
1739 (g->turn == WHITE) ? _("white") : _("black"));
1741 strncpy(tmp, _("white"), sizeof(tmp)-1);
1742 tmp[0] = toupper(tmp[0]);
1743 snprintf(t, sizeof(t), "%s%s",
1744 timeval_to_char(d->wclock.elapsed, d->wclock.tc[d->wclock.tcn][1]),
1745 time_control_status(&d->wclock));
1746 mvwprintw(statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1748 strncpy(tmp, _("black"), sizeof(tmp)-1);
1749 tmp[0] = toupper(tmp[0]);
1750 snprintf(t, sizeof(t), "%s%s",
1751 timeval_to_char(d->bclock.elapsed, d->bclock.tc[d->bclock.tcn][1]),
1752 time_control_status(&d->bclock));
1753 mvwprintw(statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1755 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Total:"), w,
1756 clock_to_char(d->elapsed.tv_sec));
1758 // for (i = 0; i < STATUS_WIDTH; i++)
1759 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1761 if (!status.notify)
1762 status.notify = str_to_wchar(_("Type F1 for help"));
1764 wattron(stdscr, CP_STATUS_NOTIFY);
1765 for (i = (config.boardleft) ? BOARD_WIDTH : 0;
1766 i < ((config.boardleft) ? COLS : STATUS_WIDTH); i++)
1767 mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1768 mvwprintw(stdscr, STATUS_HEIGHT, CENTERX(STATUS_WIDTH, status.notify)
1769 + ((config.boardleft) ? BOARD_WIDTH : 0),
1770 "%ls", status.notify);
1771 wattroff(stdscr, CP_STATUS_NOTIFY);
1774 wchar_t *translate_tag_name(const char *tag)
1776 if (!strcmp (tag, "Event"))
1777 return str_to_wchar (translatable_tag_names[0]);
1778 else if (!strcmp (tag, "Site"))
1779 return str_to_wchar (translatable_tag_names[1]);
1780 else if (!strcmp (tag, "Date"))
1781 return str_to_wchar (translatable_tag_names[2]);
1782 else if (!strcmp (tag, "Round"))
1783 return str_to_wchar (translatable_tag_names[3]);
1784 else if (!strcmp (tag, "White"))
1785 return str_to_wchar (translatable_tag_names[4]);
1786 else if (!strcmp (tag, "Black"))
1787 return str_to_wchar (translatable_tag_names[5]);
1788 else if (!strcmp (tag, "Result"))
1789 return str_to_wchar (translatable_tag_names[6]);
1791 return str_to_wchar (tag);
1794 void update_tag_window(TAG **t)
1796 int i, l, w;
1797 int namel = 0;
1799 for (i = 0; t[i]; i++) {
1800 wchar_t *namewc = translate_tag_name(t[i]->name);
1802 l = wcslen(namewc);
1803 free (namewc);
1804 if (l > namel)
1805 namel = l;
1808 w = TAG_WIDTH - namel - 4;
1810 for (i = 0; t[i] && i < TAG_HEIGHT - 3; i++) {
1811 wchar_t *namewc = translate_tag_name(t[i]->name);
1812 wchar_t *valuewc = str_etc(t[i]->value, w, 0);
1814 mvwprintw(tagw, (i + 2), 1, "%*ls: %-*ls", namel, namewc, w, valuewc);
1815 free (namewc);
1816 free (valuewc);
1819 for (; i < TAG_HEIGHT - 3; i++)
1820 mvwprintw(tagw, (i + 2), 1, "%*s", namel + w + 2, " ");
1823 void append_enginebuf(GAME g, char *line)
1825 int i = 0;
1826 struct userdata_s *d = g->data;
1828 if (d->engine->enginebuf)
1829 for (i = 0; d->engine->enginebuf[i]; i++);
1831 if (i >= LINES - 3) {
1832 free(d->engine->enginebuf[0]);
1834 for (i = 0; d->engine->enginebuf[i+1]; i++)
1835 d->engine->enginebuf[i] = d->engine->enginebuf[i+1];
1837 d->engine->enginebuf[i] = strdup(line);
1839 else {
1840 d->engine->enginebuf = Realloc(d->engine->enginebuf, (i + 2) * sizeof(char *));
1841 d->engine->enginebuf[i++] = strdup(line);
1842 d->engine->enginebuf[i] = NULL;
1846 void update_engine_window(GAME g)
1848 int i;
1849 struct userdata_s *d = g->data;
1851 wmove(enginew, 0, 0);
1852 wclrtobot(enginew);
1854 if (d->engine && d->engine->enginebuf) {
1855 for (i = 0; d->engine->enginebuf[i]; i++)
1856 mvwprintw(enginew, i + 2, 1, "%s", d->engine->enginebuf[i]);
1859 window_draw_title(enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
1860 CP_MESSAGE_BORDER);
1863 void update_all(GAME g)
1865 struct userdata_s *d = g->data;
1868 * In the middle of a macro. Don't update the screen.
1870 if (macro_match != -1)
1871 return;
1873 wmove(boardw, ROWTOMATRIX(d->c_row), COLTOMATRIX(d->c_col));
1874 update_board_window(g);
1875 update_status_window(g);
1876 update_history_window(g);
1877 update_tag_window(g->tag);
1878 update_engine_window(g);
1879 update_panels();
1880 doupdate();
1883 static void game_next_prev(GAME g, int n, int count)
1885 if (gtotal < 2)
1886 return;
1888 if (n == 1) {
1889 if (gindex + count > gtotal - 1) {
1890 if (count != 1)
1891 gindex = gtotal - 1;
1892 else
1893 gindex = 0;
1895 else
1896 gindex += count;
1898 else {
1899 if (gindex - count < 0) {
1900 if (count != 1)
1901 gindex = 0;
1902 else
1903 gindex = gtotal - 1;
1905 else
1906 gindex -= count;
1909 gp = game[gindex];
1912 static void delete_game(int which)
1914 int i, w = which;
1915 struct userdata_s *d;
1917 for (i = 0; i < gtotal; i++) {
1918 d = game[i]->data;
1920 if (i == w || TEST_FLAG(d->flags, CF_DELETE)) {
1921 int n;
1923 free_userdata_once(game[i]);
1924 pgn_free(game[i]);
1926 for (n = i; n+1 < gtotal; n++)
1927 game[n] = game[n+1];
1929 gtotal--;
1930 i--;
1931 w = -1;
1935 if (which != -1) {
1936 if (which + 1 >= gtotal)
1937 gindex = gtotal - 1;
1938 else
1939 gindex = which;
1941 else
1942 gindex = gtotal - 1;
1944 gp = game[gindex];
1945 gp->hp = gp->history;
1949 * FIXME find across multiple games.
1951 static int find_move_exp(GAME g, regex_t r, int which, int count)
1953 int i;
1954 int ret;
1955 char errbuf[255];
1956 int incr;
1957 int found;
1959 incr = (which == 0) ? -1 : 1;
1961 for (i = g->hindex + incr - 1, found = 0; ; i += incr) {
1962 if (i == g->hindex - 1)
1963 break;
1965 if (i >= pgn_history_total(g->hp))
1966 i = 0;
1967 else if (i < 0)
1968 i = pgn_history_total(g->hp) - 1;
1970 // FIXME RAV
1971 ret = regexec(&r, g->hp[i]->move, 0, 0, 0);
1973 if (ret == 0) {
1974 if (count == ++found) {
1975 return i + 1;
1978 else {
1979 if (ret != REG_NOMATCH) {
1980 regerror(ret, &r, errbuf, sizeof(errbuf));
1981 cmessage(_("Error Matching Regular Expression"), ANY_KEY_STR, "%s", errbuf);
1982 return -1;
1987 return -1;
1990 static int toggle_delete_flag(int n)
1992 int i, x;
1993 struct userdata_s *d = game[n]->data;
1995 TOGGLE_FLAG(d->flags, CF_DELETE);
1996 gindex = n;
1998 for (i = x = 0; i < gtotal; i++) {
1999 d = game[i]->data;
2001 if (TEST_FLAG(d->flags, CF_DELETE))
2002 x++;
2005 if (x == gtotal) {
2006 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
2007 d = game[n]->data;
2008 CLEAR_FLAG(d->flags, CF_DELETE);
2009 return 1;
2012 return 0;
2015 static int find_game_exp(char *str, int which, int count)
2017 char *nstr = NULL, *exp = NULL;
2018 regex_t nexp, vexp;
2019 int ret = -1;
2020 int g = 0;
2021 char buf[255] = {0}, *tmp;
2022 char errbuf[255];
2023 int found = 0;
2024 int incr = (which == 0) ? -(1) : 1;
2026 strncpy(buf, str, sizeof(buf)-1);
2027 tmp = buf;
2029 if (strstr(tmp, ":") != NULL) {
2030 nstr = strsep(&tmp, ":");
2032 if ((ret = regcomp(&nexp, nstr,
2033 REG_ICASE|REG_EXTENDED|REG_NOSUB)) != 0) {
2034 regerror(ret, &nexp, errbuf, sizeof(errbuf));
2035 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
2036 ret = g = -1;
2037 goto cleanup;
2041 exp = tmp;
2043 while (*exp && isspace(*exp))
2044 exp++;
2046 if (exp == NULL)
2047 goto cleanup;
2049 if ((ret = regcomp(&vexp, exp, REG_EXTENDED|REG_NOSUB)) != 0) {
2050 regerror(ret, &vexp, errbuf, sizeof(errbuf));
2051 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
2052 ret = -1;
2053 goto cleanup;
2056 ret = -1;
2058 for (g = gindex + incr, found = 0; ; g += incr) {
2059 int t;
2061 if (g == gtotal)
2062 g = 0;
2063 else if (g < 0)
2064 g = gtotal - 1;
2066 if (g == gindex)
2067 break;
2069 for (t = 0; game[g]->tag[t]; t++) {
2070 if (nstr) {
2071 if (regexec(&nexp, game[g]->tag[t]->name, 0, 0, 0) == 0) {
2072 if (regexec(&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0) {
2073 if (count == ++found) {
2074 ret = g;
2075 goto cleanup;
2080 else {
2081 if (regexec(&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0) {
2082 if (count == ++found) {
2083 ret = g;
2084 goto cleanup;
2090 ret = -1;
2093 cleanup:
2094 if (nstr)
2095 regfree(&nexp);
2097 if (g != -1)
2098 regfree(&vexp);
2100 return ret;
2104 * Updates the notification line in the status window then refreshes the
2105 * status window.
2107 void update_status_notify(GAME g, char *fmt, ...)
2109 va_list ap;
2110 #ifdef HAVE_VASPRINTF
2111 char *line;
2112 #else
2113 char line[COLS];
2114 #endif
2116 free(status.notify);
2117 status.notify = NULL;
2119 if (!fmt)
2120 return;
2122 va_start(ap, fmt);
2123 #ifdef HAVE_VASPRINTF
2124 vasprintf(&line, fmt, ap);
2125 #else
2126 vsnprintf(line, sizeof(line), fmt, ap);
2127 #endif
2128 va_end(ap);
2130 status.notify = str_to_wchar(line);
2132 #ifdef HAVE_VASPRINTF
2133 free(line);
2134 #endif
2137 int rav_next_prev(GAME g, BOARD b, int n)
2139 // Next RAV.
2140 if (n) {
2141 if ((!g->ravlevel && g->hindex && g->hp[g->hindex - 1]->rav == NULL) ||
2142 (!g->ravlevel && !g->hindex && g->hp[g->hindex]->rav == NULL) ||
2143 (g->ravlevel && g->hp[g->hindex]->rav == NULL))
2144 return 1;
2146 g->rav = Realloc(g->rav, (g->ravlevel + 1) * sizeof(RAV));
2147 g->rav[g->ravlevel].hp = g->hp;
2148 g->rav[g->ravlevel].flags = g->flags;
2149 g->rav[g->ravlevel].fen = pgn_game_to_fen(g, b);
2150 g->rav[g->ravlevel].hindex = g->hindex;
2151 g->hp = (!g->ravlevel) ? (g->hindex) ? g->hp[g->hindex - 1]->rav : g->hp[g->hindex]->rav : g->hp[g->hindex]->rav;
2152 g->hindex = 0;
2153 g->ravlevel++;
2154 pgn_board_update(g, b, g->hindex + 1);
2155 return 0;
2158 if (g->ravlevel - 1 < 0)
2159 return 1;
2161 // Previous RAV.
2162 g->ravlevel--;
2163 pgn_board_init_fen(g, b, g->rav[g->ravlevel].fen);
2164 free(g->rav[g->ravlevel].fen);
2165 g->hp = g->rav[g->ravlevel].hp;
2166 g->flags = g->rav[g->ravlevel].flags;
2167 g->hindex = g->rav[g->ravlevel].hindex;
2168 return 0;
2171 static void draw_window_decor()
2173 move_panel(boardp, 0,
2174 (config.boardleft) ? 0 : COLS - BOARD_WIDTH);
2175 move_panel(historyp, LINES - HISTORY_HEIGHT,
2176 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH : 0 :
2177 (MEGA_BOARD) ? 0 : COLS - HISTORY_WIDTH);
2178 move_panel(statusp, 0,
2179 (config.boardleft) ? BOARD_WIDTH : 0);
2180 move_panel(tagp, STATUS_HEIGHT + 1,
2181 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH :
2182 HISTORY_WIDTH : 0);
2184 wbkgd(boardw, CP_BOARD_WINDOW);
2185 wbkgd(statusw, CP_STATUS_WINDOW);
2186 window_draw_title(statusw, _("Game Status"), STATUS_WIDTH,
2187 CP_STATUS_TITLE, CP_STATUS_BORDER);
2188 wbkgd(tagw, CP_TAG_WINDOW);
2189 window_draw_title(tagw, _("Roster Tags"), TAG_WIDTH, CP_TAG_TITLE,
2190 CP_TAG_BORDER);
2191 wbkgd(historyw, CP_HISTORY_WINDOW);
2192 window_draw_title(historyw, _("Move History"), HISTORY_WIDTH,
2193 CP_HISTORY_TITLE, CP_HISTORY_BORDER);
2196 void do_window_resize()
2198 if (LINES < 23 || COLS < 74)
2199 return;
2201 resizeterm(LINES, COLS);
2202 endwin();
2203 wresize(boardw, BOARD_HEIGHT, BOARD_WIDTH);
2204 wresize(historyw, HISTORY_HEIGHT, HISTORY_WIDTH);
2205 wresize(statusw, STATUS_HEIGHT, STATUS_WIDTH);
2206 wresize(tagw, TAG_HEIGHT, TAG_WIDTH);
2207 clear ();
2208 wclear (boardw);
2209 wclear (historyw);
2210 wclear (tagw);
2211 wclear (statusw);
2212 wclear (loadingw);
2213 wclear (enginew);
2214 draw_window_decor();
2215 update_all(gp);
2216 keypad(boardw, TRUE);
2217 curs_set(0);
2218 cbreak();
2219 noecho();
2222 void do_global_redraw ()
2224 do_window_resize ();
2227 void stop_clock()
2229 memset(&clock_timer, 0, sizeof(struct itimerval));
2230 setitimer(ITIMER_REAL, &clock_timer, NULL);
2233 void start_clock(GAME g)
2235 struct userdata_s *d = g->data;
2237 if (clock_timer.it_interval.tv_usec)
2238 return;
2240 memset(&d->elapsed, 0, sizeof(struct timeval));
2241 clock_timer.it_value.tv_sec = 0;
2242 clock_timer.it_value.tv_usec = 100000;
2243 clock_timer.it_interval.tv_sec = 0;
2244 clock_timer.it_interval.tv_usec = 100000;
2245 setitimer(ITIMER_REAL, &clock_timer, NULL);
2248 static void update_clocks()
2250 int i;
2251 struct userdata_s *d;
2252 struct itimerval it;
2253 int update = 0;
2255 getitimer(ITIMER_REAL, &it);
2257 for (i = 0; i < gtotal; i++) {
2258 d = game[i]->data;
2260 if (d && d->mode == MODE_PLAY) {
2261 if (d->paused == 1 || TEST_FLAG(d->flags, CF_NEW))
2262 continue;
2263 else if (d->paused == -1) {
2264 if (game[i]->side == game[i]->turn) {
2265 d->paused = 1;
2266 continue;
2270 update_clock(game[i], it);
2272 if (game[i] == gp)
2273 update = 1;
2277 if (update) {
2278 update_status_window(gp);
2279 update_panels();
2280 doupdate();
2284 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2286 static int parse_clock_time(char **str)
2288 char *p = *str;
2289 int n = 0, t = 0;
2291 SKIP_SPACE(p);
2293 if (!isdigit(*p))
2294 return -1;
2296 while (*p) {
2297 if (isdigit(*p)) {
2298 t = atoi(p);
2300 while (isdigit(*p))
2301 p++;
2303 continue;
2306 switch (*p) {
2307 case 'H':
2308 case 'h':
2309 n += t * (60 * 60);
2310 t = 0;
2311 break;
2312 case 'M':
2313 case 'm':
2314 n += t * 60;
2315 t = 0;
2316 break;
2317 case 'S':
2318 case 's':
2319 n += t;
2320 t = 0;
2321 break;
2322 case ' ':
2323 p++;
2324 case '/':
2325 case '+':
2326 goto done;
2327 default:
2328 *str = p;
2329 return -1;
2332 p++;
2335 done:
2336 n += t;
2337 *str = p;
2338 return n;
2341 static int parse_clock_input(struct clock_s *clk, char *str, int *incr)
2343 char *p = str;
2344 long n = 0;
2345 int plus = 0;
2346 int m = 0;
2347 int tc = 0;
2349 SKIP_SPACE(p);
2351 if (!*p)
2352 return 0;
2354 if (*p == '+') {
2355 plus = 1;
2356 p++;
2357 SKIP_SPACE(p);
2359 if (*p == '+')
2360 goto move_incr;
2362 else
2363 memset(clk, 0, sizeof(struct clock_s));
2365 again:
2366 /* Sudden death. */
2367 if (strncasecmp(p, "SD", 2) == 0) {
2368 n = 0;
2369 p += 2;
2370 goto tc;
2373 n = parse_clock_time(&p);
2375 if (n == -1)
2376 return 1;
2378 if (!n)
2379 goto done;
2381 /* Time control. */
2383 if (*p == '/') {
2384 if (plus)
2385 return 1;
2387 /* Sudden death without a previous time control. */
2388 if (!n && !tc)
2389 return 1;
2391 m = n;
2392 p++;
2393 n = parse_clock_time(&p);
2395 if (n == -1)
2396 return 1;
2398 if (tc >= MAX_TC) {
2399 message(ERROR_STR, ANY_KEY_STR, "%s (%i)", _("Maximum number of time controls reached"), MAX_TC);
2400 return 1;
2403 clk->tc[tc][0] = m;
2404 clk->tc[tc++][1] = n;
2405 SKIP_SPACE(p);
2407 if (*p == '+')
2408 goto move_incr;
2410 if (*p)
2411 goto again;
2413 goto done;
2416 if (plus)
2417 *incr = n;
2418 else
2419 clk->tc[clk->tcn][1] = (n <= clk->elapsed.tv_sec) ? clk->elapsed.tv_sec + n : n;
2421 move_incr:
2422 if (*p) {
2423 if (*p++ == '+') {
2424 if (!isdigit(*p))
2425 return 1;
2427 n = parse_clock_time(&p);
2429 if (n == -1 || *p)
2430 return 1;
2432 clk->incr = n;
2434 SKIP_SPACE(p);
2436 if (*p)
2437 return 1;
2439 else
2440 return 1;
2443 done:
2444 return 0;
2447 static int parse_which_clock(struct clock_s *clk, char *str)
2449 struct clock_s tmp;
2450 int incr = 0;
2452 memcpy(&tmp, clk, sizeof(struct clock_s));
2454 if (parse_clock_input(&tmp, str, &incr)) {
2455 cmessage(ERROR_STR, ANY_KEY_STR, _("Invalid clock specification"));
2456 return 1;
2459 memcpy(clk, &tmp, sizeof(struct clock_s));
2460 clk->tc[clk->tcn][1] += incr;
2461 return 0;
2464 void do_clock_input_finalize(WIN *win)
2466 struct userdata_s *d = gp->data;
2467 struct input_data_s *in = win->data;
2468 char *p = in->str;
2470 if (!in->str) {
2471 free(in);
2472 return;
2475 SKIP_SPACE(p);
2477 if (tolower(*p) == 'w') {
2478 p++;
2480 if (parse_which_clock(&d->wclock, p))
2481 goto done;
2483 else if (tolower(*p) == 'b') {
2484 p++;
2486 if (parse_which_clock(&d->bclock, p))
2487 goto done;
2489 else {
2490 if (parse_which_clock(&d->wclock, p))
2491 goto done;
2493 if (parse_which_clock(&d->bclock, p))
2494 goto done;
2497 if (!d->wclock.tc[0][1] && !d->bclock.tc[0][1])
2498 CLEAR_FLAG(d->flags, CF_CLOCK);
2499 else
2500 SET_FLAG(d->flags, CF_CLOCK);
2502 done:
2503 free(in->str);
2504 free(in);
2507 void do_engine_command_finalize(WIN *win)
2509 struct userdata_s *d = gp->data;
2510 struct input_data_s *in = win->data;
2511 int x;
2513 if (!in->str) {
2514 free(in);
2515 return;
2518 if (!d->engine)
2519 goto done;
2521 x = d->engine->status;
2522 send_to_engine(gp, -1, "%s\n", in->str);
2523 d->engine->status = x;
2525 done:
2526 free(in->str);
2527 free(in);
2530 void do_board_details()
2532 config.details = (config.details) ? 0 : 1;
2535 void do_toggle_strict_castling()
2537 int n;
2539 pgn_config_get(PGN_STRICT_CASTLING, &n);
2541 if (n == 0)
2542 pgn_config_set(PGN_STRICT_CASTLING, 1);
2543 else
2544 pgn_config_set(PGN_STRICT_CASTLING, 0);
2547 void do_play_set_clock()
2549 struct input_data_s *in;
2551 in = Calloc(1, sizeof(struct input_data_s));
2552 in->efunc = do_clock_input_finalize;
2553 construct_input(_("Set Clock"), NULL, 1, 1,
2554 _ ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n" \
2555 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2556 NULL, NULL, 0, in, INPUT_HIST_CLOCK, -1);
2559 void do_play_toggle_human()
2561 struct userdata_s *d = gp->data;
2563 TOGGLE_FLAG(d->flags, CF_HUMAN);
2565 if (!TEST_FLAG(d->flags, CF_HUMAN) && pgn_history_total(gp->hp)) {
2566 if (init_chess_engine(gp))
2567 return;
2570 CLEAR_FLAG(d->flags, CF_ENGINE_LOOP);
2572 if (d->engine)
2573 d->engine->status = ENGINE_READY;
2576 void do_play_toggle_engine()
2578 struct userdata_s *d = gp->data;
2580 TOGGLE_FLAG(d->flags, CF_ENGINE_LOOP);
2581 CLEAR_FLAG(d->flags, CF_HUMAN);
2583 if (d->engine && TEST_FLAG(d->flags, CF_ENGINE_LOOP)) {
2584 char *fen = pgn_game_to_fen (gp, d->b);
2586 pgn_board_update(gp, d->b,
2587 pgn_history_total(gp->hp));
2588 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
2589 free (fen);
2594 * This will send a command to the engine skipping the command queue.
2596 void do_play_send_command()
2598 struct userdata_s *d = gp->data;
2599 struct input_data_s *in;
2601 if (!d->engine || d->engine->status == ENGINE_OFFLINE) {
2602 if (init_chess_engine(gp))
2603 return;
2606 in = Calloc(1, sizeof(struct input_data_s));
2607 in->efunc = do_engine_command_finalize;
2608 construct_input(_("Engine Command"), NULL, 1, 1, NULL, NULL, NULL, 0, in, INPUT_HIST_ENGINE, -1);
2611 void do_play_switch_turn()
2613 struct userdata_s *d = gp->data;
2615 pgn_switch_side(gp);
2616 pgn_switch_turn(gp);
2618 if (!TEST_FLAG(d->flags, CF_HUMAN))
2619 add_engine_command(gp, -1,
2620 (gp->side == WHITE) ? "white\n" : "black\n");
2622 update_status_window(gp);
2625 void do_play_toggle_eh_mode()
2627 struct userdata_s *d = gp->data;
2629 if (!TEST_FLAG(d->flags, CF_HUMAN)) {
2630 if (!gp->hindex){
2631 pgn_switch_side(gp, TRUE);
2632 d->play_mode = (d->play_mode) ? PLAY_HE : PLAY_EH;
2633 if (gp->side == BLACK)
2634 update_status_notify(gp, _("Press 'g' to start the game"));
2636 d->rotate = !d->rotate;
2638 else
2639 message(NULL, ANY_KEY_STR,
2640 _("You may only switch sides at the start of the \n"
2641 "game. Press ^K or ^N to begin a new game."));
2645 void do_play_undo()
2647 struct userdata_s *d = gp->data;
2649 if (!pgn_history_total(gp->hp))
2650 return;
2652 if (keycount) {
2653 if (gp->hindex - keycount < 0)
2654 gp->hindex = 0;
2655 else {
2656 if (d->go_move)
2657 gp->hindex -= (keycount * 2) -1;
2658 else
2659 gp->hindex -= keycount * 2;
2662 else {
2663 if (gp->hindex - 2 < 0)
2664 gp->hindex = 0;
2665 else {
2666 if (d->go_move)
2667 gp->hindex -= 1;
2668 else
2669 gp->hindex -= 2;
2673 pgn_history_free(gp->hp, gp->hindex);
2674 gp->hindex = pgn_history_total(gp->hp);
2675 pgn_board_update(gp, d->b, gp->hindex);
2677 if (d->engine && d->engine->status == ENGINE_READY) {
2678 char *fen = pgn_game_to_fen(gp, d->b);
2680 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
2681 free (fen);
2682 d->engine->status = ENGINE_READY;
2685 update_history_window(gp);
2687 if (d->go_move) {
2688 pgn_switch_side(gp, FALSE);
2689 d->go_move--;
2691 d->pm_undo = TRUE;
2694 void do_play_toggle_pause()
2696 struct userdata_s *d = gp->data;
2698 if (!TEST_FLAG(d->flags, CF_HUMAN) && gp->turn !=
2699 gp->side) {
2700 d->paused = -1;
2701 return;
2704 d->paused = (d->paused) ? 0 : 1;
2707 void do_play_go()
2709 struct userdata_s *d = gp->data;
2711 if (TEST_FLAG(d->flags, CF_HUMAN))
2712 return;
2714 if (fm_loaded_file && gp->side != gp->turn) {
2715 pgn_switch_side(gp, FALSE);
2716 add_engine_command(gp, ENGINE_THINKING, "black\n");
2719 add_engine_command(gp, ENGINE_THINKING, "go\n");
2721 // Completa la función para que permita seguir jugando al usarla.
2722 // Complete the function to allow continue playing when using.
2723 if (gp->side == gp->turn)
2724 pgn_switch_side(gp, FALSE);
2726 d->go_move++;
2729 void do_play_config_command()
2731 int x, w;
2733 if (config.keys) {
2734 for (x = 0; config.keys[x]; x++) {
2735 if (config.keys[x]->c == input_c) {
2736 switch (config.keys[x]->type) {
2737 case KEY_DEFAULT:
2738 add_engine_command(gp, -1, "%ls\n",
2739 config.keys[x]->str);
2740 break;
2741 case KEY_SET:
2742 if (!keycount)
2743 break;
2745 add_engine_command(gp, -1,
2746 "%ls %i\n", config.keys[x]->str, keycount);
2747 keycount = 0;
2748 break;
2749 case KEY_REPEAT:
2750 if (!keycount)
2751 break;
2753 for (w = 0; w < keycount; w++)
2754 add_engine_command(gp, -1,
2755 "%ls\n", config.keys[x]->str);
2756 keycount = 0;
2757 break;
2763 update_status_notify(gp, NULL);
2766 void do_play_cancel_selected()
2768 struct userdata_s *d = gp->data;
2770 d->sp.icon = d->sp.srow = d->sp.scol = 0;
2771 keycount = 0;
2772 pgn_reset_valid_moves(d->b);
2773 update_status_notify(gp, NULL);
2776 void do_play_commit()
2778 struct userdata_s *d = gp->data;
2780 pushkey = keycount = 0;
2781 update_status_notify(gp, NULL);
2783 if (!TEST_FLAG(d->flags, CF_HUMAN) &&
2784 (!d->engine || d->engine->status == ENGINE_THINKING))
2785 return;
2787 if (!d->sp.icon)
2788 return;
2790 d->sp.row = d->c_row;
2791 d->sp.col = d->c_col;
2793 if (d->rotate) {
2794 rotate_position(&d->sp.row, &d->sp.col);
2795 rotate_position(&d->sp.srow, &d->sp.scol);
2798 move_to_engine(gp);
2800 // Completa la función para que permita seguir jugando cuando se carga un
2801 // archivo pgn (con juego no terminado) que inicie con turno del lado
2802 // negro.
2803 // Complete the function to allow continue playing when loading a file
2804 // pgn (with unfinished game) you start to turn black side.
2805 if (gp->side != gp->turn)
2806 pgn_switch_side(gp, FALSE);
2808 if (d->rotate && d->sp.icon)
2809 rotate_position(&d->sp.srow, &d->sp.scol);
2811 // Envia comando 'go' a Polyglot en el primer movimiento debido a que
2812 // polyglot no envia el movimiento y cboard se queda esperando.
2813 // Send command 'go' to the first movement Polyglot because cboard
2814 // waits to send polyglot movement and this does not make.
2815 if (config.fmpolyglot &&
2816 ((gp->side == WHITE && !gp->hindex) || fm_loaded_file))
2817 add_engine_command(gp, ENGINE_THINKING, "go\n");
2819 d->go_move = 0;
2820 fm_loaded_file = FALSE;
2821 d->pm_undo = FALSE;
2824 void do_play_select()
2826 struct userdata_s *d = gp->data;
2828 if (!TEST_FLAG(d->flags, CF_HUMAN) && (!d->engine ||
2829 d->engine->status == ENGINE_OFFLINE)) {
2830 if (init_chess_engine(gp))
2831 return;
2834 if (d->engine && d->engine->status == ENGINE_THINKING)
2835 return;
2837 if (d->sp.icon)
2838 do_play_cancel_selected ();
2840 if (d->rotate)
2841 rotate_position(&d->c_row, &d->c_col);
2843 d->sp.icon = d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon;
2845 if (pgn_piece_to_int(d->sp.icon) == OPEN_SQUARE) {
2846 d->sp.icon = 0;
2847 return;
2850 if (((islower(d->sp.icon) && gp->turn != BLACK)
2851 || (isupper(d->sp.icon) && gp->turn != WHITE))) {
2852 struct key_s **k;
2853 char *str = Malloc (512);
2855 for (k = play_keys; *k; k++) {
2856 if ((*k)->f == do_play_toggle_eh_mode)
2857 break;
2860 snprintf (str, 512, _("It is not your turn to move. You may switch playing sides by pressing \"%lc\"."),
2861 *k ? (*k)->c : '?');
2862 message(NULL, ANY_KEY_STR, "%s", str);
2863 free (str);
2864 d->sp.icon = 0;
2865 return;
2866 #if 0
2867 if (pgn_history_total(gp->hp)) {
2868 message(NULL, ANY_KEY_STR, "%s", _("It is not your turn to move. You can switch sides "));
2869 d->sp.icon = 0;
2870 return;
2872 else {
2873 if (pgn_tag_find(gp->tag, "FEN") != E_PGN_ERR)
2874 return;
2876 add_engine_command(gp, ENGINE_READY, "black\n");
2877 pgn_switch_turn(gp);
2879 if (gp->side != BLACK)
2880 pgn_switch_side(gp);
2882 #endif
2885 d->sp.srow = d->c_row;
2886 d->sp.scol = d->c_col;
2888 if (config.validmoves)
2889 pgn_find_valid_moves(gp, d->b, d->sp.scol, d->sp.srow);
2891 if (d->rotate) {
2892 rotate_position(&d->c_row, &d->c_col);
2893 rotate_position(&d->sp.srow, &d->sp.scol);
2896 CLEAR_FLAG(d->flags, CF_NEW);
2897 start_clock(gp);
2900 static wchar_t *build_help(struct key_s **keys)
2902 int i, nlen = 1, len, t, n;
2903 wchar_t *buf = NULL, *wc = NULL;
2904 wchar_t *p;
2906 if (!keys)
2907 return NULL;
2909 for (i = len = t = 0; keys[i]; i++) {
2910 if (!keys[i]->d)
2911 continue;
2913 if (keys[i]->key) {
2914 if (wcslen(keys[i]->key) > nlen) {
2915 nlen = wcslen(keys[i]->key);
2916 t += nlen;
2918 else
2919 t++;
2921 else
2922 t++;
2924 if (keys[i]->d) {
2925 if (wcslen(keys[i]->d) > len)
2926 len = wcslen(keys[i]->d);
2929 t += len;
2930 t += keys[i]->r;
2933 t += 4 + i + 1;
2934 buf = Malloc((t+1)*sizeof(wchar_t));
2935 p = buf;
2937 for (i = 0; keys[i]; i++) {
2938 if (!keys[i]->d)
2939 continue;
2941 if (keys[i]->key)
2942 n = wcslen(keys[i]->key);
2943 else
2944 n = 1;
2946 while (n++ <= nlen)
2947 *p++ = ' ';
2949 *p = 0;
2951 if (keys[i]->key) {
2952 wcsncat(buf, keys[i]->key, t-1);
2953 p = buf + wcslen(buf);
2955 else
2956 *p++ = keys[i]->c;
2958 *p++ = ' ';
2959 *p++ = '-';
2960 *p++ = ' ';
2961 *p = 0;
2963 if (keys[i]->d)
2964 wcsncat(buf, keys[i]->d, t-1);
2966 if (keys[i]->r) {
2967 wc = str_to_wchar ("*");
2968 wcsncat(buf, wc, t-1);
2969 free (wc);
2972 wc = str_to_wchar ("\n");
2973 wcscat(buf, wc);
2974 free (wc);
2975 p = buf + wcslen(buf);
2978 return buf;
2981 void do_global_help ()
2983 wchar_t *buf = build_help(global_keys);
2985 construct_message(_("Global Game Keys (* = can take a repeat count)"),
2986 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
2987 0, 1, "%ls", buf);
2990 void do_main_help(WIN *win)
2992 switch (win->c) {
2993 case 'p':
2994 do_play_help ();
2995 break;
2996 case 'h':
2997 do_history_help ();
2998 break;
2999 case 'e':
3000 do_edit_help ();
3001 break;
3002 case 'g':
3003 do_global_help ();
3004 break;
3005 default:
3006 break;
3010 static void do_more_help(WIN *win)
3012 if (win->c == KEY_F(1) || win->c == CTRL_KEY('g'))
3013 construct_message(_("Command Key Index"),
3014 _ ("p/h/e/g or any other key to quit"), 0, 0,
3015 NULL, NULL, NULL, do_main_help, 0, 0, "%s",
3017 "p - play mode keys\n"
3018 "h - history mode keys\n"
3019 "e - board edit mode keys\n"
3020 "g - global game keys"
3024 void do_play_help()
3026 wchar_t *buf = build_help(play_keys);
3028 construct_message(_("Play Mode Keys (* = can take a repeat count)"),
3029 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
3030 buf, do_more_help, 0, 1, "%ls", buf);
3033 void do_play_history_mode()
3035 struct userdata_s *d = gp->data;
3037 if (!pgn_history_total(gp->hp) ||
3038 (d->engine && d->engine->status == ENGINE_THINKING))
3039 return;
3041 d->mode = MODE_HISTORY;
3042 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3045 void do_play_edit_mode()
3047 struct userdata_s *d = gp->data;
3049 if (pgn_history_total(gp->hp))
3050 return;
3052 pgn_board_init_fen(gp, d->b, NULL);
3053 config.details++;
3054 d->mode = MODE_EDIT;
3057 void do_edit_insert_finalize(WIN *win)
3059 struct userdata_s *d = win->data;
3061 if (pgn_piece_to_int(win->c) == -1)
3062 return;
3064 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon = win->c;
3067 void do_edit_select()
3069 struct userdata_s *d = gp->data;
3071 if (d->sp.icon)
3072 return;
3074 d->sp.icon = d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon;
3076 if (pgn_piece_to_int(d->sp.icon) == OPEN_SQUARE) {
3077 d->sp.icon = 0;
3078 return;
3081 d->sp.srow = d->c_row;
3082 d->sp.scol = d->c_col;
3085 void do_edit_commit()
3087 int p;
3088 struct userdata_s *d = gp->data;
3090 pushkey = keycount = 0;
3091 update_status_notify(gp, NULL);
3093 if (!d->sp.icon)
3094 return;
3096 d->sp.row = d->c_row;
3097 d->sp.col = d->c_col;
3098 p = d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon;
3099 d->b[RANKTOBOARD(d->sp.row)][FILETOBOARD(d->sp.col)].icon = p;
3100 d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon =
3101 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3102 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3105 void do_edit_delete()
3107 struct userdata_s *d = gp->data;
3109 if (d->sp.icon)
3110 d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon =
3111 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3112 else
3113 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon =
3114 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3116 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3119 void do_edit_cancel_selected()
3121 struct userdata_s *d = gp->data;
3123 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3124 keycount = 0;
3125 update_status_notify(gp, NULL);
3128 void do_edit_switch_turn()
3130 pgn_switch_turn(gp);
3133 void do_edit_toggle_castle()
3135 struct userdata_s *d = gp->data;
3137 castling_state(gp, d->b, RANKTOBOARD(d->c_row),
3138 FILETOBOARD(d->c_col),
3139 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon, 1);
3142 void do_edit_insert()
3144 struct userdata_s *d = gp->data;
3146 construct_message(_("Insert Piece"), _("P=pawn, R=rook, N=knight, B=bishop, "), 0, 0, NULL, NULL,
3147 d->b, do_edit_insert_finalize, 0, 0, "%s", _("Type the piece letter to insert. Lowercase "));
3150 void do_edit_enpassant()
3152 struct userdata_s *d = gp->data;
3154 if (d->c_row == 6 || d->c_row == 3) {
3155 pgn_reset_enpassant(d->b);
3156 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].enpassant = 1;
3160 void do_edit_help()
3162 wchar_t *buf = build_help(edit_keys);
3164 construct_message(_("Edit Mode Keys (* = can take a repeat count)"),
3165 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
3166 0, 1, "%ls", buf);
3169 void do_edit_exit()
3171 struct userdata_s *d = gp->data;
3172 char *fen = pgn_game_to_fen(gp, d->b);
3174 config.details--;
3175 pgn_tag_add(&gp->tag, "FEN", fen);
3176 free (fen);
3177 pgn_tag_add(&gp->tag, "SetUp", "1");
3178 pgn_tag_sort(gp->tag);
3179 pgn_board_update(gp, d->b, gp->hindex);
3180 d->mode = MODE_PLAY;
3183 void really_do_annotate_finalize(struct input_data_s *in,
3184 struct userdata_s *d)
3186 HISTORY *h = in->data;
3187 int len;
3189 if (!in->str) {
3190 if (h->comment) {
3191 free(h->comment);
3192 h->comment = NULL;
3195 else {
3196 len = strlen(in->str);
3197 h->comment = Realloc(h->comment, len+1);
3198 strncpy(h->comment, in->str, len);
3199 h->comment[len] = 0;
3202 free(in->str);
3203 free(in);
3204 SET_FLAG(d->flags, CF_MODIFIED);
3207 void do_annotate_finalize(WIN *win)
3209 struct userdata_s *d = gp->data;
3210 struct input_data_s *in = win->data;
3212 really_do_annotate_finalize(in, d);
3215 void do_find_move_exp_finalize(int init, int which)
3217 int n;
3218 struct userdata_s *d = gp->data;
3219 static int firstrun;
3220 static regex_t r;
3221 int ret;
3222 char errbuf[255];
3224 if (init || !firstrun) {
3225 if (!firstrun)
3226 regfree(&r);
3228 if ((ret = regcomp(&r, moveexp, REG_EXTENDED|REG_NOSUB)) != 0) {
3229 regerror(ret, &r, errbuf, sizeof(errbuf));
3230 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
3231 return;
3234 firstrun = 1;
3237 if ((n = find_move_exp(gp, r,
3238 (which == -1) ? 0 : 1, (keycount) ? keycount : 1)) == -1)
3239 return;
3241 gp->hindex = n;
3242 pgn_board_update(gp, d->b, gp->hindex);
3245 void do_find_move_exp(WIN *win)
3247 struct input_data_s *in = win->data;
3248 int *n = in->data;
3249 int which = *n;
3251 if (in->str) {
3252 strncpy(moveexp, in->str, sizeof(moveexp)-1);
3253 moveexp[sizeof(moveexp)-1] = 0;
3254 do_find_move_exp_finalize(1, which);
3255 free(in->str);
3258 free(in->data);
3259 free(in);
3262 void do_move_jump_finalize(int n)
3264 struct userdata_s *d = gp->data;
3266 if (n < 0 || n > (pgn_history_total(gp->hp) / 2))
3267 return;
3269 keycount = 0;
3270 update_status_notify(gp, NULL);
3271 gp->hindex = (n) ? n * 2 - 1 : n * 2;
3272 pgn_board_update(gp, d->b, gp->hindex);
3275 void do_move_jump(WIN *win)
3277 struct input_data_s *in = win->data;
3279 if (!in->str || !isinteger(in->str)) {
3280 if (in->str)
3281 free(in->str);
3283 free(in);
3284 return;
3287 do_move_jump_finalize(atoi(in->str));
3288 free(in->str);
3289 free(in);
3292 struct history_menu_s {
3293 char *line;
3294 int hindex;
3295 int ravlevel;
3296 int move;
3297 int indent;
3300 void free_history_menu_data(struct history_menu_s **h)
3302 int i;
3304 if (!h)
3305 return;
3307 for (i = 0; h[i]; i++) {
3308 free(h[i]->line);
3309 free(h[i]);
3312 free(h);
3315 void get_history_data(HISTORY **hp, struct history_menu_s ***menu, int m,
3316 int turn)
3318 int i, n = 0;
3319 int t = pgn_history_total(hp);
3320 char buf[MAX_SAN_MOVE_LEN + 4];
3321 static int depth;
3322 struct history_menu_s **hmenu = *menu;
3324 if (hmenu)
3325 for (n = 0; hmenu[n]; n++);
3326 else
3327 depth = 0;
3329 for (i = 0; i < t; i++) {
3330 hmenu = Realloc(hmenu, (n + 2) * sizeof(struct history_menu_s *));
3331 hmenu[n] = Malloc(sizeof(struct history_menu_s));
3332 snprintf(buf, sizeof(buf), "%c%s%s", (turn == WHITE) ? 'W' : 'B',
3333 hp[i]->move, (hp[i]->comment || hp[i]->nag[0]) ? " !" : "");
3334 hmenu[n]->line = strdup(buf);
3335 hmenu[n]->hindex = i;
3336 hmenu[n]->indent = 0;
3337 hmenu[n]->ravlevel = depth;
3338 hmenu[n]->move = (n && depth > hmenu[n-1]->ravlevel) ? m++ : m;
3339 n++;
3340 hmenu[n] = NULL;
3342 #if 0
3343 if (hp[i]->rav) {
3344 depth++;
3345 get_history_data(hp[i]->rav, &hmenu, m, turn);
3346 for (n = 0; hmenu[n]; n++);
3347 depth--;
3349 if (depth)
3350 m--;
3352 #endif
3354 turn = (turn == WHITE) ? BLACK : WHITE;
3357 *menu = hmenu;
3360 void history_draw_update(struct menu_input_s *m)
3362 GAME g = m->data;
3363 struct userdata_s *d = g->data;
3365 g->hindex = m->selected + 1;
3366 update_cursor(g, m->selected);
3367 pgn_board_update(g, d->b, m->selected + 1);
3370 struct menu_item_s **get_history_items(WIN *win)
3372 struct menu_input_s *m = win->data;
3373 GAME g = m->data;
3374 struct userdata_s *d = g->data;
3375 struct history_menu_s **hm = d->data;
3376 struct menu_item_s **items = m->items;
3377 int i;
3379 if (!hm) {
3380 get_history_data(g->history, &hm, 0,
3381 TEST_FLAG(g->flags, GF_BLACK_OPENING));
3382 m->selected = g->hindex - 1;
3384 if (m->selected < 0)
3385 m->selected = 0;
3387 m->draw_exit_func = history_draw_update;
3390 d->data = hm;
3392 if (items) {
3393 for (i = 0; items[i]; i++)
3394 free(items[i]);
3396 free(items);
3397 items = NULL;
3400 for (i = 0; hm[i]; i++) {
3401 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
3402 items[i] = Malloc(sizeof(struct menu_item_s));
3403 items[i]->name = hm[i]->line;
3404 items[i]->value = NULL;
3405 items[i]->selected = 0;
3408 if (items)
3409 items[i] = NULL;
3411 m->nofree = 1;
3412 m->items = items;
3413 return items;
3416 void history_menu_quit(struct menu_input_s *m)
3418 pushkey = -1;
3421 void history_menu_exit(WIN *win)
3423 GAME g = win->data;
3424 struct userdata_s *d = g->data;
3425 struct history_menu_s **hm = d->data;
3426 int i;
3428 if (!hm)
3429 return;
3431 for (i = 0; hm[i]; i++) {
3432 free(hm[i]->line);
3433 free(hm[i]);
3436 free(hm);
3437 d->data = NULL;
3440 // FIXME RAV
3441 void history_menu_next(struct menu_input_s *m)
3443 GAME g = m->data;
3444 struct userdata_s *d = g->data;
3445 struct history_menu_s **hm = d->data;
3446 int n, t;
3448 for (t = 0; hm[t]; t++);
3450 if (m->selected + 1 == t)
3451 n = 0;
3452 else
3453 n = hm[m->selected + 1]->hindex;
3455 n++;
3456 g->hindex = n;
3459 // FIXME RAV
3460 void history_menu_prev(struct menu_input_s *m)
3462 GAME g = m->data;
3463 struct userdata_s *d = g->data;
3464 struct history_menu_s **hm = d->data;
3465 int n, t;
3467 for (t = 0; hm[t]; t++);
3469 if (m->selected - 1 < 0)
3470 n = t - 1;
3471 else
3472 n = hm[m->selected - 1]->hindex;
3474 n++;
3475 g->hindex = n;
3478 void history_menu_help(struct menu_input_s *m)
3480 message(_("History Menu Help"), ANY_KEY_STR, "%s",
3482 " UP/DOWN - previous/next menu item\n"
3483 " HOME/END - first/last menu item\n"
3484 " PGDN/PGUP - next/previous page\n"
3485 " a-zA-Z0-9 - jump to item\n"
3486 " CTRL-a - annotate the selected move\n"
3487 " ENTER - view annotation\n"
3488 " CTRL-d - toggle board details\n"
3489 " ESCAPE/M - return to move history"
3493 void do_annotate_move(HISTORY *hp)
3495 char buf[COLS - 4];
3496 struct input_data_s *in;
3498 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Editing Annotation for"), hp->move);
3499 in = Calloc(1, sizeof(struct input_data_s));
3500 in->data = hp;
3501 in->efunc = do_annotate_finalize;
3502 construct_input(buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3503 _("Type CTRL-t to edit NAG"), edit_nag, NULL, CTRL_KEY('T'), in, -1, -1);
3506 void history_menu_view_annotation(struct menu_input_s *m)
3508 GAME g = m->data;
3510 // FIXME RAV
3511 view_annotation(g->history[m->selected]);
3514 void history_menu_annotate_finalize(WIN *win)
3516 struct input_data_s *in = win->data;
3517 GAME g = in->moredata;
3518 struct userdata_s *d = g->data;
3519 struct history_menu_s **hm = d->data;
3521 really_do_annotate_finalize(in, d);
3522 free_history_menu_data(hm);
3523 hm = NULL;
3524 get_history_data(g->history, &hm, 0, TEST_FLAG(g->flags, GF_BLACK_OPENING));
3525 d->data = hm;
3526 pushkey = REFRESH_MENU;
3529 void history_menu_annotate(struct menu_input_s *m)
3531 GAME g = m->data;
3532 char buf[COLS - 4];
3533 struct input_data_s *in;
3534 HISTORY *hp = g->history[m->selected]; // FIXME RAV
3536 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Editing Annotation for"), hp->move);
3537 in = Calloc(1, sizeof(struct input_data_s));
3538 in->data = hp;
3539 in->moredata = m->data;
3540 in->efunc = history_menu_annotate_finalize;
3541 construct_input(buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3542 _("Type CTRL-t to edit NAG"), edit_nag, NULL, CTRL_KEY('T'), in, -1, -1);
3545 void history_menu_details(struct menu_input_s *m)
3547 do_board_details();
3550 // FIXME RAV
3551 void history_menu_print(WIN *win)
3553 struct menu_input_s *m = win->data;
3554 GAME g = m->data;
3555 struct userdata_s *d = g->data;
3556 struct history_menu_s **hm = d->data;
3557 struct history_menu_s *h = hm[m->top];
3558 int i;
3559 char *p = m->item->name;
3560 int line = m->print_line - 2;
3562 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
3563 * attr_t data type.
3565 attr_t attrs;
3566 short pair;
3567 int total;
3569 for (total = 0; hm[total]; total++);
3570 wattr_get(win->w, &attrs, &pair, NULL);
3571 wattroff(win->w, COLOR_PAIR(pair));
3572 mvwaddch(win->w, m->print_line, 1,
3573 *p == 'W' ? *p | mix_cp(CP_BOARD_WHITE, CP_HISTORY_WINDOW, ATTRS(CP_BOARD_WHITE), A_FG_B_BG) : *p | mix_cp(CP_BOARD_BLACK, CP_HISTORY_WINDOW, ATTRS(CP_BOARD_BLACK), A_FG_B_BG));
3574 p++;
3576 if (h->hindex == 0 && line == 0)
3577 waddch(win->w, ACS_ULCORNER | CP_HISTORY_MENU_LG);
3578 else if ((!hm[h->hindex + (win->rows - 5) + 1] && line == win->rows - 5) ||
3579 (m->top + line == total - 1))
3580 waddch(win->w, ACS_LLCORNER | CP_HISTORY_MENU_LG);
3581 else if (hm[m->top + 1]->ravlevel != h->ravlevel || !h->ravlevel)
3582 waddch(win->w, ACS_LTEE | CP_HISTORY_MENU_LG);
3583 else
3584 waddch(win->w, ACS_VLINE | CP_HISTORY_MENU_LG);
3586 wattron(win->w, COLOR_PAIR(pair) | attrs);
3588 for (i = 2; *p; p++, i++)
3589 waddch(win->w, (*p == '!') ? *p | A_BOLD : *p);
3591 while (i++ < win->cols - 2)
3592 waddch(win->w, ' ');
3595 void history_menu(GAME g)
3597 struct menu_key_s **keys = NULL;
3599 add_menu_key(&keys, KEY_ESCAPE, history_menu_quit);
3600 add_menu_key(&keys, 'M', history_menu_quit);
3601 add_menu_key(&keys, KEY_UP, history_menu_prev);
3602 add_menu_key(&keys, KEY_DOWN, history_menu_next);
3603 add_menu_key(&keys, KEY_F(1), history_menu_help);
3604 add_menu_key(&keys, CTRL_KEY('a'), history_menu_annotate);
3605 add_menu_key(&keys, CTRL_KEY('d'), history_menu_details);
3606 add_menu_key(&keys, '\n', history_menu_view_annotation);
3607 construct_menu(MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES,
3608 TAG_WIDTH, 0, config.boardleft ? BOARD_WIDTH : 0,
3609 _("Move History Tree"), 1, get_history_items, keys, g,
3610 history_menu_print, history_menu_exit);
3613 void do_history_menu()
3615 history_menu(gp);
3618 void do_history_half_move_toggle()
3620 movestep = (movestep == 1) ? 2 : 1;
3621 update_history_window(gp);
3624 void do_history_rotate_board()
3626 struct userdata_s *d = gp->data;
3627 d->rotate = !d->rotate;
3630 void do_history_jump_next()
3632 struct userdata_s *d = gp->data;
3634 pgn_history_next(gp, d->b, (keycount > 0) ?
3635 config.jumpcount * keycount * movestep :
3636 config.jumpcount * movestep);
3639 void do_history_jump_prev()
3641 struct userdata_s *d = gp->data;
3643 pgn_history_prev(gp, d->b, (keycount) ?
3644 config.jumpcount * keycount * movestep :
3645 config.jumpcount * movestep);
3648 void do_history_prev()
3650 struct userdata_s *d = gp->data;
3652 pgn_history_prev(gp, d->b,
3653 (keycount) ? keycount * movestep : movestep);
3656 void do_history_next()
3658 struct userdata_s *d = gp->data;
3660 pgn_history_next(gp, d->b, (keycount) ?
3661 keycount * movestep : movestep);
3664 void do_history_mode_finalize(struct userdata_s *d)
3666 pushkey = 0;
3667 d->mode = MODE_PLAY;
3670 void do_history_mode_confirm(WIN *win)
3672 struct userdata_s *d = gp->data;
3673 wchar_t str[] = { win->c, 0 };
3675 if (!wcscmp (str, resume_wchar)) {
3676 pgn_history_free(gp->hp, gp->hindex);
3677 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3679 #if 0
3680 case 'C':
3681 case 'c':
3682 if (pgn_history_rav_new(gp, d->b,
3683 gp->hindex) != E_PGN_OK)
3684 return;
3686 break;
3687 #endif
3688 else
3689 return;
3691 if (!TEST_FLAG(d->flags, CF_HUMAN)) {
3692 char *fen = pgn_game_to_fen(gp, d->b);
3694 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
3695 free (fen);
3698 do_history_mode_finalize(d);
3701 void do_history_toggle()
3703 struct userdata_s *d = gp->data;
3705 // FIXME Resuming from previous history could append to a RAV.
3706 if (gp->hindex != pgn_history_total(gp->hp)) {
3707 if (!pushkey)
3708 construct_message(NULL, _ ("What would you like to do?"), 0, 1,
3709 NULL, NULL, NULL, do_history_mode_confirm, 0, 0,
3710 _("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."),
3711 resume_wchar);
3712 return;
3714 else {
3715 if (TEST_FLAG(gp->flags, GF_GAMEOVER))
3716 return;
3719 if (gp->side != gp->turn) {
3720 d->play_mode = PLAY_EH;
3722 else {
3723 d->play_mode = PLAY_HE;
3724 d->rotate = FALSE;
3727 do_history_mode_finalize(d);
3730 void do_history_annotate()
3732 int n = gp->hindex;
3734 if (n && gp->hp[n - 1]->move)
3735 n--;
3736 else
3737 return;
3739 do_annotate_move(gp->hp[n]);
3742 void do_history_help()
3744 wchar_t *buf = build_help(history_keys);
3746 construct_message(_("History Mode Keys (* = can take a repeat count)"),
3747 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
3748 buf, do_more_help, 0, 1, "%ls", buf);
3751 void do_history_find(int which)
3753 struct input_data_s *in;
3754 int *p;
3756 if (pgn_history_total(gp->hp) < 2)
3757 return;
3759 in = Calloc(1, sizeof(struct input_data_s));
3760 p = Malloc(sizeof(int));
3761 *p = which;
3762 in->data = p;
3763 in->efunc = do_find_move_exp;
3765 if (!*moveexp || which == 0) {
3766 construct_input(_("Find Move Text Expression"), NULL, 1, 0, NULL, NULL, NULL,
3767 0, in, INPUT_HIST_MOVE_EXP, -1);
3768 return;
3771 do_find_move_exp_finalize(0, which);
3774 void do_history_find_new()
3776 do_history_find(0);
3779 void do_history_find_prev()
3781 do_history_find(-1);
3784 void do_history_find_next()
3786 do_history_find(1);
3789 void do_history_rav(int which)
3791 struct userdata_s *d = gp->data;
3793 rav_next_prev(gp, d->b, which);
3796 void do_history_rav_next()
3798 do_history_rav(1);
3801 void do_history_rav_prev()
3803 do_history_rav(0);
3806 void do_history_jump()
3808 struct input_data_s *in;
3810 if (pgn_history_total(gp->hp) < 2)
3811 return;
3813 if (!keycount) {
3814 in = Calloc(1, sizeof(struct input_data_s));
3815 in->efunc = do_move_jump;
3817 construct_input(_("Jump to Move Number"), NULL, 1, 1, NULL,
3818 NULL, NULL, 0, in, -1, 0);
3819 return;
3822 do_move_jump_finalize(keycount);
3825 static void free_userdata_once(GAME g)
3827 struct userdata_s *d = g->data;
3829 if (!d)
3830 return;
3832 if (d->engine) {
3833 stop_engine(g);
3835 if (d->engine->enginebuf) {
3836 int n;
3838 for (n = 0; d->engine->enginebuf[n]; n++)
3839 free(d->engine->enginebuf[n]);
3841 free(d->engine->enginebuf);
3844 if (d->engine->queue) {
3845 struct queue_s **q;
3847 for (q = d->engine->queue; *q; q++)
3848 free(*q);
3850 free(d->engine->queue);
3853 free(d->engine);
3856 #ifdef WITH_LIBPERL
3857 if (d->perlfen)
3858 free(d->perlfen);
3860 if (d->oldfen)
3861 free(d->oldfen);
3862 #endif
3864 free(d);
3865 g->data = NULL;
3868 static void free_userdata()
3870 int i;
3872 for (i = 0; i < gtotal; i++) {
3873 free_userdata_once(game[i]);
3874 game[i]->data = NULL;
3878 void update_loading_window(int n)
3880 char buf[16];
3882 if (!loadingw) {
3883 loadingw = newwin(3, COLS / 2, CALCPOSY(3), CALCPOSX(COLS / 2));
3884 loadingp = new_panel(loadingw);
3885 wbkgd(loadingw, CP_MESSAGE_WINDOW);
3888 wmove(loadingw, 0, 0);
3889 wclrtobot(loadingw);
3890 wattron(loadingw, CP_MESSAGE_BORDER);
3891 box(loadingw, ACS_VLINE, ACS_HLINE);
3892 wattroff(loadingw, CP_MESSAGE_BORDER);
3893 mvwprintw(loadingw, 1, CENTER_INT((COLS / 2), 11 +
3894 strlen(itoa(gtotal, buf))),
3895 _("Loading... %i%% (%i games)"), n, gtotal);
3896 update_panels();
3897 doupdate();
3900 static void init_userdata_once(GAME g, int n)
3902 struct userdata_s *d = NULL;
3904 d = Calloc(1, sizeof(struct userdata_s));
3905 d->n = n;
3906 d->c_row = 2, d->c_col = 5;
3907 SET_FLAG(d->flags, CF_NEW);
3908 g->data = d;
3910 if (pgn_board_init_fen(g, d->b, NULL) != E_PGN_OK)
3911 pgn_board_init(d->b);
3914 void init_userdata()
3916 int i;
3918 for (i = 0; i < gtotal; i++)
3919 init_userdata_once(game[i], i);
3922 void fix_marks(int *start, int *end)
3924 int i;
3926 *start = (*start < 0) ? 0 : *start;
3927 *end = (*end < 0) ? 0 : *end;
3929 if (*start > *end) {
3930 i = *start;
3931 *start = *end;
3932 *end = i + 1;
3935 *end = (*end > gtotal) ? gtotal : *end;
3938 void do_new_game_finalize(GAME g)
3940 struct userdata_s *d = g->data;
3942 d->mode = MODE_PLAY;
3943 update_status_notify(g, NULL);
3944 d->rotate = FALSE;
3945 d->go_move = 0;
3948 void do_new_game_from_scratch(WIN *win)
3950 wchar_t str[] = { win->c, 0 };
3952 if (wcscmp (str, yes_wchar))
3953 return;
3955 stop_clock();
3956 free_userdata();
3957 pgn_parse(NULL);
3958 gp = game[gindex];
3959 add_custom_tags(&gp->tag);
3960 init_userdata();
3961 loadfile[0] = 0;
3962 do_new_game_finalize(gp);
3965 void do_new_game()
3967 pgn_new_game();
3968 gp = game[gindex];
3969 add_custom_tags(&gp->tag);
3970 init_userdata_once(gp, gindex);
3971 do_new_game_finalize(gp);
3974 void do_game_delete_finalize(int n)
3976 struct userdata_s *d;
3978 delete_game((!n) ? gindex : -1);
3979 d = gp->data;
3980 if (d->mode != MODE_EDIT)
3981 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3984 void do_game_delete_confirm(WIN *win)
3986 int *n;
3987 wchar_t str[] = { win->c, 0 };
3989 if (wcscmp (str, yes_wchar)) {
3990 free(win->data);
3991 return;
3994 n = (int *)win->data;
3995 do_game_delete_finalize(*n);
3996 free(win->data);
3999 void do_game_delete()
4001 char *tmp = NULL;
4002 int i, n;
4003 struct userdata_s *d;
4004 int *p;
4006 if (gtotal < 2) {
4007 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4008 return;
4011 tmp = NULL;
4013 for (i = n = 0; i < gtotal; i++) {
4014 d = game[i]->data;
4016 if (TEST_FLAG(d->flags, CF_DELETE))
4017 n++;
4020 if (!n)
4021 tmp = _("Delete the current game?");
4022 else {
4023 if (n == gtotal) {
4024 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4025 return;
4028 tmp = _("Delete all games marked for deletion?");
4031 if (config.deleteprompt) {
4032 p = Malloc(sizeof(int));
4033 *p = n;
4034 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, p,
4035 do_game_delete_confirm, 0, 0, tmp);
4036 return;
4039 do_game_delete_finalize(n);
4042 void do_find_game_exp_finalize(int which)
4044 struct userdata_s *d = gp->data;
4045 int n;
4047 if ((n = find_game_exp(gameexp, (which == -1) ? 0 : 1,
4048 (keycount) ? keycount : 1)) == -1) {
4049 update_status_notify(gp, "%s", _("No matches found"));
4050 return;
4053 gindex = n;
4054 d = gp->data;
4056 if (pgn_history_total(gp->hp))
4057 d->mode = MODE_HISTORY;
4059 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4062 void do_find_game_exp(WIN *win)
4064 struct input_data_s *in = win->data;
4065 int *n = in->data;
4066 int c = *n;
4068 if (in->str) {
4069 strncpy(gameexp, in->str, sizeof(gameexp));
4070 gameexp[sizeof(gameexp)-1] = 0;
4072 if (c == '?')
4073 c = '}';
4075 do_find_game_exp_finalize(c);
4076 free(in->str);
4079 free(in->data);
4080 free(in);
4083 void do_game_jump_finalize(int n)
4085 struct userdata_s *d;
4087 if (--n > gtotal - 1 || n < 0)
4088 return;
4090 gindex = n;
4091 gp = game[gindex];
4092 d = gp->data;
4093 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4094 update_status_notify(gp, NULL);
4097 void do_game_jump(WIN *win)
4099 struct input_data_s *in = win->data;
4101 if (!in->str || !isinteger(in->str)) {
4102 if (in->str)
4103 free(in->str);
4105 free(in);
4106 return;
4109 do_game_jump_finalize(atoi(in->str));
4110 free(in->str);
4111 free(in);
4114 void do_load_file(WIN *win)
4116 struct input_data_s *in = win->data;
4117 char *tmp = in->str;
4118 struct userdata_s *d;
4119 PGN_FILE *pgn = NULL;
4120 int n;
4122 if (!in->str) {
4123 free(in);
4124 return;
4127 if ((tmp = pathfix(tmp)) == NULL)
4128 goto done;
4130 n = pgn_open(tmp, "r", &pgn);
4132 if (n == E_PGN_ERR) {
4133 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, strerror(errno));
4134 goto done;
4136 else if (n == E_PGN_INVALID) {
4137 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, _("Not a regular file"));
4138 goto done;
4141 free_userdata();
4143 if (pgn_parse(pgn) == E_PGN_ERR) {
4144 del_panel(loadingp);
4145 delwin(loadingw);
4146 loadingw = NULL;
4147 loadingp = NULL;
4148 init_userdata();
4149 goto done;
4152 del_panel(loadingp);
4153 delwin(loadingw);
4154 loadingw = NULL;
4155 loadingp = NULL;
4156 init_userdata();
4157 strncpy(loadfile, tmp, sizeof(loadfile));
4158 loadfile[sizeof(loadfile)-1] = 0;
4159 gp = game[gindex];
4160 d = gp->data;
4162 if (pgn_history_total(gp->hp))
4163 d->mode = MODE_HISTORY;
4165 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4167 fm_loaded_file = TRUE;
4168 d->rotate = FALSE;
4170 done:
4171 pgn_close(pgn);
4173 if (in->str)
4174 free(in->str);
4176 free(in);
4179 void do_game_save(WIN *win)
4181 struct input_data_s *in = win->data;
4182 int *x = in->data;
4183 int n = *x;
4184 char *tmp = in->str;
4185 char tfile[FILENAME_MAX];
4186 char *p;
4187 int i;
4188 struct userdata_s *d;
4190 if (!tmp || (tmp = pathfix(tmp)) == NULL)
4191 goto done;
4193 if (pgn_is_compressed(tmp) == E_PGN_ERR) {
4194 p = tmp + strlen(tmp) - 1;
4196 if (*p != 'n' || *(p-1) != 'g' || *(p-2) != 'p' ||
4197 *(p-3) != '.') {
4198 snprintf(tfile, sizeof(tfile), "%s.pgn", tmp);
4199 tmp = tfile;
4204 * When in edit mode, update the FEN tag.
4206 if (n == -1) {
4207 for (i = 0; i < gtotal; i++) {
4208 d = game[i]->data;
4210 if (d->mode == MODE_EDIT) {
4211 char *fen = pgn_game_to_fen(game[i], d->b);
4213 pgn_tag_add(&game[i]->tag, "FEN", fen);
4214 free (fen);
4218 else {
4219 d = game[n]->data;
4221 if (d->mode == MODE_EDIT) {
4222 char *fen = pgn_game_to_fen(game[n], d->b);
4224 pgn_tag_add(&game[n]->tag, "FEN", fen);
4225 free (fen);
4229 save_pgn(tmp, n);
4231 done:
4232 if (in->str)
4233 free(in->str);
4235 free(in->data);
4236 free(in);
4239 void do_get_game_save_input(int n)
4241 struct input_data_s *in = Calloc(1, sizeof(struct input_data_s));
4242 int *p = Malloc(sizeof(int));
4244 in->efunc = do_game_save;
4245 *p = n;
4246 in->data = p;
4248 construct_input(_("Save Game Filename"), loadfile, 1, 1, _("Type TAB for file browser"),
4249 file_browser, NULL, '\t', in, INPUT_HIST_FILE, -1);
4252 void do_game_save_multi_confirm(WIN *win)
4254 int i;
4255 wchar_t str[] = { win->c, 0 };
4257 if (!wcscmp (str, current_wchar))
4258 i = gindex;
4259 else if (!wcscmp (str, all_wchar))
4260 i = -1;
4261 else {
4262 update_status_notify(gp, "%s", _("Save game aborted."));
4263 return;
4266 do_get_game_save_input(i);
4269 void do_global_about()
4271 cmessage(_("ABOUT"), ANY_KEY_STR,
4272 _("%s\nUsing %s with %i colors and %i color pairs\n%s"),
4273 PACKAGE_STRING, curses_version(), COLORS, COLOR_PAIRS,
4274 COPYRIGHT);
4277 void global_game_next_prev(int which)
4279 struct userdata_s *d;
4281 game_next_prev(gp, (which == 1) ? 1 : 0,
4282 (keycount) ? keycount : 1);
4283 d = gp->data;
4285 if (delete_count) {
4286 if (which == 1) {
4287 markend = markstart + delete_count;
4288 delete_count = 0;
4290 else {
4291 markend = markstart - delete_count + 1;
4292 delete_count = -1; // to fix gindex in the other direction
4295 fix_marks(&markstart, &markend);
4296 do_global_toggle_delete();
4299 if (d->mode == MODE_HISTORY)
4300 pgn_board_update(gp, d->b, gp->hindex);
4301 else if (d->mode == MODE_PLAY)
4302 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4305 void do_global_next_game()
4307 global_game_next_prev(1);
4310 void do_global_prev_game()
4312 global_game_next_prev(0);
4315 void global_find(int which)
4317 struct input_data_s *in;
4318 int *p;
4320 if (gtotal < 2)
4321 return;
4323 in = Calloc(1, sizeof(struct input_data_s));
4324 p = Malloc(sizeof(int));
4325 *p = which;
4326 in->data = p;
4327 in->efunc = do_find_game_exp;
4329 if (!*gameexp || which == 0) {
4330 construct_input(_("Find Game by Tag Expression"), NULL, 1, 0,
4331 _("[name expression:]value expression"), NULL, NULL, 0, in,
4332 INPUT_HIST_GAME_EXP, -1);
4333 return;
4336 do_find_game_exp_finalize(which);
4339 void do_global_find_new()
4341 global_find(0);
4344 void do_global_find_next()
4346 global_find(1);
4349 void do_global_find_prev()
4351 global_find(-1);
4354 void do_global_game_jump()
4356 if (gtotal < 2)
4357 return;
4359 if (!keycount) {
4360 struct input_data_s *in;
4362 in = Calloc(1, sizeof(struct input_data_s));
4363 in->efunc = do_game_jump;
4364 construct_input(_("Jump to Game Number"), NULL, 1, 1, NULL, NULL, NULL, 0, in,
4365 -1, 0);
4366 return;
4369 do_game_jump_finalize(keycount);
4372 void do_global_toggle_delete()
4374 int i;
4376 pushkey = 0;
4378 if (gtotal < 2)
4379 return;
4381 if (keycount && delete_count == 0) {
4382 markstart = gindex;
4383 delete_count = keycount;
4384 update_status_notify(gp, "%s (delete)", status.notify);
4385 return;
4388 if (markstart >= 0 && markend >= 0) {
4389 for (i = markstart; i < markend; i++) {
4390 if (toggle_delete_flag(i)) {
4391 return;
4395 gindex = (delete_count < 0) ? markstart : i - 1;
4397 else {
4398 if (toggle_delete_flag(gindex))
4399 return;
4402 markstart = markend = -1;
4403 delete_count = 0;
4404 update_status_window(gp);
4407 void do_global_delete_game()
4409 do_game_delete();
4412 void do_global_tag_edit()
4414 struct userdata_s *d = gp->data;
4416 edit_tags(gp, d->b, 1);
4419 void do_global_tag_view()
4421 struct userdata_s *d = gp->data;
4423 edit_tags(gp, d->b, 0);
4426 void do_global_resume_game()
4428 struct input_data_s *in;
4430 in = Calloc(1, sizeof(struct input_data_s));
4431 in->efunc = do_load_file;
4432 construct_input(_("Load Filename"), NULL, 1, 1, _("Type TAB for file browser"), file_browser,
4433 NULL, '\t', in, INPUT_HIST_FILE, -1);
4436 void do_global_save_game()
4438 if (gtotal > 1) {
4439 construct_message(NULL, _("What would you like to do?"), 0, 1,
4440 NULL, NULL, NULL, do_game_save_multi_confirm, 0, 0,
4441 _("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."),
4442 current_wchar, all_wchar);
4443 return;
4446 do_get_game_save_input(-1);
4449 void do_global_new_game()
4451 do_new_game();
4454 void do_global_copy_game()
4456 int g = gindex;
4457 int i, n;
4458 struct userdata_s *d;
4460 do_global_new_game();
4461 d = gp->data;
4462 n = pgn_tag_total(game[g]->tag);
4464 for (i = 0; i < n; i++)
4465 pgn_tag_add(&gp->tag, game[g]->tag[i]->name,
4466 game[g]->tag[i]->value);
4468 pgn_board_init_fen (gp, d->b, NULL);
4469 n = pgn_history_total(game[g]->history);
4471 // FIXME RAV
4472 for (i = 0; i < n; i++) {
4473 char *frfr = NULL;
4474 char *move = strdup (game[g]->history[i]->move);
4476 if (pgn_parse_move(gp, d->b, &move, &frfr) != E_PGN_OK) {
4477 free (move);
4478 SET_FLAG(gp->flags, GF_PERROR);
4479 return;
4482 pgn_history_add(gp, d->b, move);
4483 free (move);
4484 free(frfr);
4485 pgn_switch_turn(gp);
4488 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4491 void do_global_new_all()
4493 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
4494 do_new_game_from_scratch, 0, 0, "%s", _("Really start a new game from scratch?"));
4497 void do_quit(WIN *win)
4499 wchar_t str[] = { win->c, 0 };
4501 if (wcscmp (str, yes_wchar))
4502 return;
4504 quit = 1;
4507 void do_global_quit()
4509 if (config.exitdialogbox)
4510 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
4511 do_quit, 0, 0, _("Want to Quit?"));
4512 else
4513 quit = 1;
4516 void do_global_toggle_engine_window()
4518 if (!enginew) {
4519 enginew = newwin(LINES, COLS, 0, 0);
4520 enginep = new_panel(enginew);
4521 window_draw_title(enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
4522 CP_MESSAGE_BORDER);
4523 hide_panel(enginep);
4526 if (panel_hidden(enginep)) {
4527 update_engine_window(gp);
4528 top_panel(enginep);
4530 else {
4531 hide_panel(enginep);
4535 void do_global_toggle_board_details()
4537 do_board_details();
4540 void do_global_toggle_strict_castling()
4542 do_toggle_strict_castling();
4545 // Global and other keys.
4546 static int globalkeys()
4548 struct userdata_s *d = gp->data;
4549 int i;
4552 * These cannot be modified and other game mode keys cannot conflict with
4553 * these.
4555 switch (input_c) {
4556 case KEY_ESCAPE:
4557 d->sp.icon = d->sp.srow = d->sp.scol = 0;
4558 markend = markstart = 0;
4560 if (keycount) {
4561 keycount = 0;
4562 update_status_notify(gp, NULL);
4565 if (config.validmoves)
4566 pgn_reset_valid_moves(d->b);
4568 return 1;
4569 case '0' ... '9':
4570 i = input_c - '0';
4572 if (keycount)
4573 keycount = keycount * 10 + i;
4574 else
4575 keycount = i;
4577 update_status_notify(gp, _("Repeat %i"), keycount);
4578 return -1;
4579 case KEY_UP:
4580 if (d->mode == MODE_HISTORY)
4581 return 0;
4583 if (keycount)
4584 d->c_row += keycount;
4585 else
4586 d->c_row++;
4588 if (d->c_row > 8)
4589 d->c_row = 1;
4591 return 1;
4592 case KEY_DOWN:
4593 if (d->mode == MODE_HISTORY)
4594 return 0;
4596 if (keycount) {
4597 d->c_row -= keycount;
4598 update_status_notify(gp, NULL);
4600 else
4601 d->c_row--;
4603 if (d->c_row < 1)
4604 d->c_row = 8;
4606 return 1;
4607 case KEY_LEFT:
4608 if (d->mode == MODE_HISTORY)
4609 return 0;
4611 if (keycount)
4612 d->c_col -= keycount;
4613 else
4614 d->c_col--;
4616 if (d->c_col < 1)
4617 d->c_col = 8;
4619 return 1;
4620 case KEY_RIGHT:
4621 if (d->mode == MODE_HISTORY)
4622 return 0;
4624 if (keycount)
4625 d->c_col += keycount;
4626 else
4627 d->c_col++;
4629 if (d->c_col > 8)
4630 d->c_col = 1;
4632 return 1;
4633 case KEY_RESIZE:
4634 return 1;
4635 case 0:
4636 default:
4637 for (i = 0; global_keys[i]; i++) {
4638 if (input_c == global_keys[i]->c && global_keys[i]->f) {
4639 (*global_keys[i]->f)();
4640 return 1;
4643 break;
4646 return 0;
4649 #ifdef WITH_LIBPERL
4650 static void perl_error(const char *fmt, ...)
4652 va_list ap;
4653 char *buf;
4655 va_start(ap, fmt);
4656 vasprintf(&buf, fmt, ap);
4657 va_end(ap);
4659 message(ERROR_STR, ANY_KEY_STR, "%s", buf);
4660 free(buf);
4663 static void do_perl_finalize(WIN *win)
4665 struct input_data_s *in = win->data;
4666 GAME g = in->data;
4667 struct userdata_s *d = g->data;
4668 char *filename;
4669 char *result = NULL;
4670 char *arg = NULL;
4671 int n;
4673 asprintf(&filename, "%s/perl.pl", config.datadir);
4675 if (!in->str)
4676 goto done;
4678 if (perl_init_file(filename, perl_error))
4679 goto done;
4681 arg = pgn_game_to_fen(g, d->b);
4683 if (perl_call_sub(trim(in->str), arg, &result))
4684 goto done;
4686 d->perlfen = pgn_game_to_fen(g, d->b);
4687 d->perlflags = g->flags;
4689 if (pgn_board_init_fen(g, d->b, result) != E_PGN_OK) {
4690 message(ERROR_STR, ANY_KEY_STR, "%s", _("FEN parse error."));
4691 pgn_board_init_fen(g, d->b, d->perlfen);
4692 g->flags = d->perlflags;
4693 free(d->perlfen);
4694 d->perlfen = NULL;
4695 goto done;
4698 SET_FLAG(d->flags, CF_PERL);
4699 n = pgn_tag_find(g->tag, "FEN");
4701 if (n != E_PGN_ERR)
4702 d->oldfen = strdup(g->tag[n]->value);
4704 pgn_tag_add(&g->tag, "FEN", result);
4705 update_status_notify(g, "%s", ANY_KEY_STR);
4706 update_all(g);
4708 done:
4709 free(result);
4710 free(arg);
4711 free(in->str);
4712 free(in);
4713 free(filename);
4716 void do_global_perl()
4718 struct input_data_s *in;
4720 in = Calloc(1, sizeof(struct input_data_s));
4721 in->data = gp;
4722 in->efunc = do_perl_finalize;
4723 construct_input(_("PERL Subroutine Filter"), NULL, 1, 0, NULL, NULL, NULL, 0, in, INPUT_HIST_PERL, -1);
4725 #endif
4728 * A macro may contain a key that belongs to another macro so macro_match will
4729 * need to be updated to the new index of the matching macro.
4731 static void find_macro(struct userdata_s *d)
4733 int i;
4736 * Macros can't contain macros when in a window.
4738 if (wins)
4739 return;
4741 again:
4742 for (i = 0; macros[i]; i++) {
4743 if ((macros[i]->mode == -1 || macros[i]->mode == d->mode) &&
4744 input_c == macros[i]->c) {
4745 input_c = macros[i]->keys[macros[i]->n++];
4747 if (!macro_depth_n && macro_match > -1) {
4748 macro_depth = realloc(macro_depth, (macro_depth_n + 1) * sizeof(int));
4749 macro_depth[macro_depth_n++] = macro_match;
4752 macro_depth = realloc(macro_depth, (macro_depth_n + 1) * sizeof(int));
4753 macro_depth[macro_depth_n++] = i;
4754 macro_match = i;
4755 goto again;
4761 * Resets the position in each macro to the first key.
4763 static void reset_macros()
4765 int i;
4766 struct userdata_s *d = gp->data;
4768 again:
4769 if (macro_depth_n > 0) {
4770 macro_depth_n--;
4771 macro_match = macro_depth[macro_depth_n];
4773 if (macros[macro_match]->n >= macros[macro_match]->total)
4774 goto again;
4776 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
4777 find_macro(d);
4778 return;
4781 for (i = 0; macros[i]; i++)
4782 macros[i]->n = 0;
4784 free(macro_depth);
4785 macro_depth = NULL;
4786 macro_depth_n = 0;
4787 macro_match = -1;
4790 void game_loop()
4792 struct userdata_s *d;
4794 macro_match = -1;
4795 gindex = gtotal - 1;
4796 gp = game[gindex];
4797 d = gp->data;
4799 if (pgn_history_total(gp->hp))
4800 d->mode = MODE_HISTORY;
4801 else {
4802 d->mode = MODE_PLAY;
4803 d->play_mode = PLAY_HE;
4806 d->rotate = FALSE;
4807 d->go_move = 0;
4808 d->pm_undo = FALSE;
4809 d->pm_frfr[0] = '\0';
4811 if (d->mode == MODE_HISTORY)
4812 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4814 update_status_notify(gp, "%s", _("Type F1 for help"));
4815 movestep = 2;
4816 flushinp();
4817 update_all(gp);
4818 wtimeout(boardw, WINDOW_TIMEOUT);
4820 while (!quit) {
4821 int n = 0, i;
4822 char fdbuf[8192] = {0};
4823 int len;
4824 struct timeval tv = {0, 0};
4825 fd_set rfds, wfds;
4826 WIN *win = NULL;
4827 WINDOW *wp = NULL;
4829 FD_ZERO(&rfds);
4830 FD_ZERO(&wfds);
4832 for (i = 0; i < gtotal; i++) {
4833 d = game[i]->data;
4835 if (d->engine && d->engine->pid != -1) {
4836 if (d->engine->fd[ENGINE_IN_FD] > 2) {
4837 if (d->engine->fd[ENGINE_IN_FD] > n)
4838 n = d->engine->fd[ENGINE_IN_FD];
4840 FD_SET(d->engine->fd[ENGINE_IN_FD], &rfds);
4843 if (d->engine->fd[ENGINE_OUT_FD] > 2) {
4844 if (d->engine->fd[ENGINE_OUT_FD] > n)
4845 n = d->engine->fd[ENGINE_OUT_FD];
4847 FD_SET(d->engine->fd[ENGINE_OUT_FD], &wfds);
4852 if (n) {
4853 if ((n = select(n + 1, &rfds, &wfds, NULL, &tv)) > 0) {
4854 for (i = 0; i < gtotal; i++) {
4855 d = game[i]->data;
4857 if (d->engine && d->engine->pid != -1) {
4858 if (FD_ISSET(d->engine->fd[ENGINE_IN_FD], &rfds)) {
4859 len = read(d->engine->fd[ENGINE_IN_FD], fdbuf,
4860 sizeof(fdbuf));
4862 if (len > 0) {
4863 if (d->engine->iobuf)
4864 d->engine->iobuf = Realloc(d->engine->iobuf, d->engine->len + len + 1);
4865 else
4866 d->engine->iobuf = Calloc(1, len + 1);
4868 memcpy(&(d->engine->iobuf[d->engine->len]), &fdbuf, len);
4869 d->engine->len += len;
4870 d->engine->iobuf[d->engine->len] = 0;
4873 * The fdbuf is full or no newline
4874 * was found. So we'll append the next
4875 * read() to this games buffer.
4877 if (d->engine->iobuf[d->engine->len - 1] != '\n')
4878 continue;
4880 parse_engine_output(game[i], d->engine->iobuf);
4881 free(d->engine->iobuf);
4882 d->engine->iobuf = NULL;
4883 d->engine->len = 0;
4885 else if (len == -1) {
4886 if (errno != EAGAIN) {
4887 cmessage(ERROR_STR, ANY_KEY_STR, "Engine read(): %s",
4888 strerror(errno));
4889 waitpid(d->engine->pid, &n, 0);
4890 free(d->engine);
4891 d->engine = NULL;
4892 break;
4897 if (FD_ISSET(d->engine->fd[ENGINE_OUT_FD], &wfds)) {
4898 if (d->engine->queue)
4899 send_engine_command(game[i]);
4904 else {
4905 if (n == -1)
4906 cmessage(ERROR_STR, ANY_KEY_STR, "select(): %s", strerror(errno));
4907 /* timeout */
4911 gp = game[gindex];
4912 d = gp->data;
4915 * This is needed to detect terminal resizing.
4917 doupdate();
4918 if (LINES != LINES_OLD || COLS != COLS_OLD) {
4919 COLS_OLD = COLS;
4920 LINES_OLD = LINES;
4921 do_window_resize();
4925 * Finds the top level window in the window stack so we know what
4926 * window the wget_wch()'ed key belongs to.
4928 if (wins) {
4929 for (i = 0; wins[i]; i++);
4930 win = wins[i-1];
4931 wp = win->w;
4932 wtimeout(wp, WINDOW_TIMEOUT);
4934 else
4935 wp = boardw;
4937 if (!i && pushkey)
4938 input_c = pushkey;
4939 else {
4940 if (!pushkey) {
4941 if (macros && macro_match >= 0) {
4942 if (macros[macro_match]->n >= macros[macro_match]->total)
4943 reset_macros();
4944 else {
4945 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
4946 find_macro(d);
4949 else {
4950 if (wget_wch(wp, &input_c) == ERR || input_c == KEY_RESIZE)
4951 continue;
4954 else
4955 input_c = pushkey;
4957 if (win) {
4958 win->c = input_c;
4961 * Run the function associated with the window. When the
4962 * function returns 0 win->efunc is ran (if not NULL) with
4963 * win as the one and only parameter. Then the window is
4964 * destroyed.
4966 * The exit function may create another window which will
4967 * mess up the window stack when window_destroy() is called.
4968 * So don't destory the window until the top window is
4969 * destroyable. See window_destroy().
4971 if ((*win->func)(win) == 0) {
4972 if (win->efunc)
4973 (*win->efunc)(win);
4975 win->keep = 1;
4976 window_destroy(win);
4977 update_all(gp);
4980 continue;
4984 if (!keycount && status.notify)
4985 update_status_notify(gp, NULL);
4987 #ifdef WITH_LIBPERL
4988 if (TEST_FLAG(d->flags, CF_PERL)) {
4989 CLEAR_FLAG(d->flags, CF_PERL);
4990 pgn_board_init_fen(gp, d->b, d->perlfen);
4991 gp->flags = d->perlflags;
4992 free(d->perlfen);
4993 pgn_tag_add(&gp->tag, "FEN", d->oldfen);
4994 free(d->oldfen);
4995 d->perlfen = d->oldfen = NULL;
4996 update_all(gp);
4997 continue;
4999 #endif
5001 if (macros && macro_match < 0)
5002 find_macro(d);
5004 if ((n = globalkeys()) == 1) {
5005 if (macro_match == -1)
5006 keycount = 0;
5008 goto refresh;
5010 else if (n == -1)
5011 goto refresh;
5013 switch (d->mode) {
5014 case MODE_EDIT:
5015 for (i = 0; edit_keys[i]; i++) {
5016 if (input_c == edit_keys[i]->c) {
5017 (*edit_keys[i]->f)();
5018 break;
5021 break;
5022 case MODE_PLAY:
5023 for (i = 0; play_keys[i]; i++) {
5024 if (input_c == play_keys[i]->c) {
5025 (*play_keys[i]->f)();
5026 goto done;
5030 do_play_config_command();
5031 break;
5032 case MODE_HISTORY:
5033 for (i = 0; history_keys[i]; i++) {
5034 if (input_c == history_keys[i]->c) {
5035 (*history_keys[i]->f)();
5036 break;
5039 break;
5040 default:
5041 break;
5044 done:
5045 if (keycount)
5046 update_status_notify(gp, NULL);
5048 keycount = 0;
5050 refresh:
5051 update_all(gp);
5055 void usage(const char *pn, int ret)
5057 fprintf((ret) ? stderr : stdout, "%s%s",
5058 #ifdef DEBUG
5060 "Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
5061 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"),
5062 #else
5064 "Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"),
5065 #endif
5067 " -p Load PGN file.\n"
5068 " -V Validate a game file.\n"
5069 " -S Validate and output a PGN formatted game.\n"
5070 " -R Like -S but write a reduced PGN formatted game.\n"
5071 " -t Also write custom PGN tags from config file.\n"
5072 " -E Stop processing on file parsing error (overrides config).\n"
5073 " -C Enable strict castling (overrides config).\n"
5074 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, overrides config).\n"
5075 " -v Version information.\n"
5076 " -h This help text.\n"));
5078 exit(ret);
5081 void cleanup_all()
5083 int i;
5085 stop_clock();
5086 free_userdata();
5087 pgn_free_all();
5088 free(config.engine_cmd);
5089 free(config.pattern);
5090 free(config.ccfile);
5091 free(config.nagfile);
5092 free(config.configfile);
5094 if (config.keys) {
5095 for (i = 0; config.keys[i]; i++) {
5096 free(config.keys[i]->str);
5097 free(config.keys[i]);
5100 free(config.keys);
5103 if (config.einit) {
5104 for (i = 0; config.einit[i]; i++)
5105 free(config.einit[i]);
5107 free(config.einit);
5110 if (config.tag)
5111 pgn_tag_free(config.tag);
5113 free(config.datadir);
5115 if (curses_initialized) {
5116 del_panel(boardp);
5117 del_panel(historyp);
5118 del_panel(statusp);
5119 del_panel(tagp);
5120 delwin(boardw);
5121 delwin(historyw);
5122 delwin(statusw);
5123 delwin(tagw);
5125 if (enginew) {
5126 del_panel(enginep);
5127 delwin(enginew);
5130 endwin();
5133 #ifdef WITH_LIBPERL
5134 perl_cleanup();
5135 #endif
5138 static void signal_save_pgn(int sig)
5140 char *buf;
5141 time_t now;
5142 char *p = config.savedirectory ? config.savedirectory : config.datadir;
5144 time(&now);
5145 asprintf(&buf, "%s/signal-%i-%li.pgn", p, sig, now);
5147 if (do_game_write(buf, "w", 0, gtotal)) {
5148 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", p, strerror(errno));
5149 update_status_notify(gp, "%s", _("Save game failed."));
5152 free(buf);
5153 quit = 1;
5156 void catch_signal(int which)
5158 switch (which) {
5159 case SIGALRM:
5160 update_clocks();
5161 break;
5162 case SIGPIPE:
5163 if (which == SIGPIPE && quit)
5164 break;
5166 if (which == SIGPIPE)
5167 cmessage(NULL, ANY_KEY_STR, "%s", _("Broken pipe. Quitting."));
5169 cleanup_all();
5170 exit(EXIT_FAILURE);
5171 break;
5172 case SIGSTOP:
5173 savetty();
5174 break;
5175 case SIGCONT:
5176 resetty();
5177 do_window_resize ();
5178 keypad(boardw, TRUE);
5179 break;
5180 case SIGINT:
5181 quit = 1;
5182 break;
5183 case SIGTERM:
5184 signal_save_pgn(which);
5185 break;
5186 default:
5187 break;
5191 void loading_progress(long total, long offset)
5193 int n = (100 * (offset / 100) / (total / 100));
5195 if (curses_initialized)
5196 update_loading_window(n);
5197 else {
5198 fprintf(stderr, _("Loading... %i%% (%i games)%c"), n, gtotal, '\r');
5199 fflush(stderr);
5203 static void set_defaults()
5205 set_config_defaults();
5206 set_default_keys();
5207 filetype = FILE_NONE;
5208 pgn_config_set(PGN_PROGRESS, 1024);
5209 pgn_config_set(PGN_PROGRESS_FUNC, loading_progress);
5212 int main(int argc, char *argv[])
5214 int opt;
5215 struct stat st;
5216 char buf[FILENAME_MAX];
5217 char datadir[FILENAME_MAX];
5218 int ret = EXIT_SUCCESS;
5219 int validate_only = 0, validate_and_write = 0;
5220 int write_custom_tags = 0;
5221 int i = 0;
5222 PGN_FILE *pgn;
5223 int utf8_pieces = -1;
5225 setlocale (LC_ALL, "");
5226 bindtextdomain ("cboard", LOCALE_DIR);
5227 textdomain ("cboard");
5229 /* Solaris 5.9 */
5230 #ifndef HAVE_PROGNAME
5231 __progname = argv[0];
5232 #endif
5234 if ((config.pwd = getpwuid(getuid())) == NULL)
5235 err(EXIT_FAILURE, "getpwuid()");
5237 snprintf(datadir, sizeof(datadir), "%s/.cboard", config.pwd->pw_dir);
5238 config.datadir = strdup(datadir);
5239 snprintf(buf, sizeof(buf), "%s/cc.data", datadir);
5240 config.ccfile = strdup(buf);
5241 snprintf(buf, sizeof(buf), "%s/nag.data", datadir);
5242 config.nagfile = strdup(buf);
5243 snprintf(buf, sizeof(buf), "%s/config", datadir);
5244 config.configfile = strdup(buf);
5246 if (stat(datadir, &st) == -1) {
5247 if (errno == ENOENT) {
5248 if (mkdir(datadir, 0755) == -1)
5249 err(EXIT_FAILURE, "%s", datadir);
5251 else
5252 err(EXIT_FAILURE, "%s", datadir);
5254 stat(datadir, &st);
5257 if (!S_ISDIR(st.st_mode))
5258 errx(EXIT_FAILURE, "%s: %s", datadir, _("Not a directory."));
5260 set_defaults();
5262 #ifdef DEBUG
5263 while ((opt = getopt(argc, argv, "DCEVtSRhp:vu::")) != -1) {
5264 #else
5265 while ((opt = getopt(argc, argv, "ECVtSRhp:vu::")) != -1) {
5266 #endif
5267 switch (opt) {
5268 #ifdef DEBUG
5269 case 'D':
5270 unlink("libchess.debug");
5271 pgn_config_set(PGN_DEBUG, 1);
5272 break;
5273 #endif
5274 case 'C':
5275 pgn_config_set(PGN_STRICT_CASTLING, 1);
5276 break;
5277 case 't':
5278 write_custom_tags = 1;
5279 break;
5280 case 'E':
5281 i = 1;
5282 break;
5283 case 'R':
5284 pgn_config_set(PGN_REDUCED, 1);
5285 case 'S':
5286 validate_and_write = 1;
5287 case 'V':
5288 validate_only = 1;
5289 break;
5290 case 'v':
5291 printf("%s (%s)\n%s\n", PACKAGE_STRING, curses_version(),
5292 COPYRIGHT);
5293 exit(EXIT_SUCCESS);
5294 case 'p':
5295 filetype = FILE_PGN;
5296 strncpy(loadfile, optarg, sizeof(loadfile));
5297 loadfile[sizeof(loadfile)-1] = 0;
5298 break;
5299 case 'u':
5300 utf8_pieces = optarg ? atoi (optarg): 1;
5301 break;
5302 case 'h':
5303 default:
5304 usage(argv[0], EXIT_SUCCESS);
5308 if ((validate_only || validate_and_write) && !*loadfile)
5309 usage(argv[0], EXIT_FAILURE);
5311 if (access(config.configfile, R_OK) == 0)
5312 parse_rcfile(config.configfile);
5314 if (i)
5315 pgn_config_set(PGN_STOP_ON_ERROR, 1);
5317 signal(SIGPIPE, catch_signal);
5318 signal(SIGCONT, catch_signal);
5319 signal(SIGSTOP, catch_signal);
5320 signal(SIGINT, catch_signal);
5321 signal(SIGALRM, catch_signal);
5322 signal(SIGTERM, catch_signal);
5324 srandom(getpid());
5326 switch (filetype) {
5327 case FILE_PGN:
5328 if (pgn_open(loadfile, "r", &pgn) != E_PGN_OK)
5329 err(EXIT_FAILURE, "%s", loadfile);
5331 ret = pgn_parse(pgn);
5332 pgn_close(pgn);
5333 break;
5334 case FILE_FEN:
5335 //ret = parse_fen_file(loadfile);
5336 break;
5337 case FILE_EPD: // Not implemented.
5338 case FILE_NONE:
5339 default:
5340 // No file specified. Empty game.
5341 ret = pgn_parse(NULL);
5342 gp = game[gindex];
5343 add_custom_tags(&gp->tag);
5344 break;
5347 if (validate_only || validate_and_write) {
5348 if (validate_and_write) {
5349 if (pgn_open("-", "r", &pgn) != E_PGN_OK)
5350 err(EXIT_FAILURE, "pgn_open()");
5352 for (i = 0; i < gtotal; i++) {
5353 if (write_custom_tags)
5354 add_custom_tags(&game[i]->tag);
5356 pgn_write(pgn, game[i]);
5359 pgn_close(pgn);
5361 fm_loaded_file = TRUE;
5364 cleanup_all();
5365 exit(ret);
5367 else if (ret == E_PGN_ERR)
5368 exit(ret);
5370 if (utf8_pieces != -1)
5371 config.utf8_pieces = utf8_pieces;
5373 init_wchar_pieces ();
5374 yes_wchar = str_to_wchar (_("y"));
5375 all_wchar = str_to_wchar (_("a"));
5376 overwrite_wchar = str_to_wchar (_("o"));
5377 resume_wchar = str_to_wchar (_("r"));
5378 current_wchar = str_to_wchar (_("c"));
5379 append_wchar = str_to_wchar (_("a"));
5380 translatable_tag_names[0] = _("Event");
5381 translatable_tag_names[1] = _("Site");
5382 translatable_tag_names[2] = _("Date");
5383 translatable_tag_names[3] = _("Round");
5384 translatable_tag_names[4] = _("White");
5385 translatable_tag_names[5] = _("Black");
5386 translatable_tag_names[6] = _("Result");
5387 init_userdata();
5390 * This fixes window resizing in an xterm.
5392 if (getenv("DISPLAY") != NULL) {
5393 putenv("LINES=");
5394 putenv("COLUMNS=");
5397 if (initscr() == NULL)
5398 errx(EXIT_FAILURE, "%s", _("Could not initialize curses."));
5399 else
5400 curses_initialized = 1;
5402 if (LINES < 23 || COLS < 74) {
5403 endwin();
5404 errx(EXIT_FAILURE, _("Need at least an 74x23 terminal."));
5407 COLS_OLD = COLS;
5408 LINES_OLD = LINES;
5410 if (has_colors() == TRUE && start_color() == OK)
5411 init_color_pairs();
5413 boardw = newwin(BOARD_HEIGHT, BOARD_WIDTH, 0, COLS - BOARD_WIDTH);
5414 boardp = new_panel(boardw);
5415 historyw = newwin(HISTORY_HEIGHT, HISTORY_WIDTH, LINES - HISTORY_HEIGHT,
5416 COLS - HISTORY_WIDTH);
5417 historyp = new_panel(historyw);
5418 statusw = newwin(STATUS_HEIGHT, STATUS_WIDTH, 0, 0);
5419 statusp = new_panel(statusw);
5420 tagw = newwin(TAG_HEIGHT, TAG_WIDTH, STATUS_HEIGHT + 1, 0);
5421 tagp = new_panel(tagw);
5422 keypad(boardw, TRUE);
5423 // leaveok(boardw, TRUE);
5424 leaveok(tagw, TRUE);
5425 leaveok(statusw, TRUE);
5426 leaveok(historyw, TRUE);
5427 curs_set(0);
5428 cbreak();
5429 noecho();
5430 draw_window_decor();
5431 game_loop();
5432 cleanup_all();
5433 free (w_pawn_wchar);
5434 free (w_rook_wchar);
5435 free (w_bishop_wchar);
5436 free (w_knight_wchar);
5437 free (w_queen_wchar);
5438 free (w_king_wchar);
5439 free (b_pawn_wchar);
5440 free (b_rook_wchar);
5441 free (b_bishop_wchar);
5442 free (b_knight_wchar);
5443 free (b_queen_wchar);
5444 free (b_king_wchar);
5445 free (empty_wchar);
5446 free (enpassant_wchar);
5447 free (yes_wchar);
5448 free (all_wchar);
5449 free (overwrite_wchar);
5450 free (resume_wchar);
5451 free (current_wchar);
5452 free (append_wchar);
5453 free (status.notify);
5454 exit(EXIT_SUCCESS);