rotate_history_cursor_fix Fix cursor in rotate board mode history "castling"
[cboard.git] / src / cboard.c
blob5e98046cc53a59d860420b31c620200c3ad0fc1a
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 (20)
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 int 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;
135 // Movimiento resultado de la función 'do_play_go'
136 // Movement function result 'do_play_go'
137 static int go_move = 0;
138 // Controla rotación de tablero
139 // Rotation control board
140 static int rotate = FALSE;
142 static int COLS_OLD, LINES_OLD;
144 // Status window.
145 static struct {
146 wchar_t *notify; // The status window notification line buffer.
147 } status;
149 static int curses_initialized;
151 // When in history mode a full step is to the next move of the same playing
152 // side. Half stepping is alternating sides.
153 static int movestep;
155 static wchar_t *w_pawn_wchar;
156 static wchar_t *w_rook_wchar;
157 static wchar_t *w_bishop_wchar;
158 static wchar_t *w_knight_wchar;
159 static wchar_t *w_queen_wchar;
160 static wchar_t *w_king_wchar;
161 static wchar_t *b_pawn_wchar;
162 static wchar_t *b_rook_wchar;
163 static wchar_t *b_bishop_wchar;
164 static wchar_t *b_knight_wchar;
165 static wchar_t *b_queen_wchar;
166 static wchar_t *b_king_wchar;
167 static wchar_t *empty_wchar;
168 static wchar_t *enpassant_wchar;
170 static const char piece_chars[] = "PpRrNnBbQqKkxx";
171 static const char *f_pieces[] = {
172 " ", // 0
173 " O ",
174 " /_\\ ",
175 " |-|-| ", // 3
176 " ] [ ",
177 " /___\\ ",
178 " /?M ", // 6
179 " (@/)) ",
180 " /__))",
181 " O ", // 9
182 " (+) ",
183 " /_\\ ",
184 "•°°°°°•",// 12
185 " \\\\|// ",
186 " |___| ",
187 " __+__ ", // 15
188 "(__|__)",
189 " |___| ",
190 " \\ / ", // 18
191 " X ",
192 " / \\ "
195 static const bool cb[8][8] = {
196 {1,0,1,0,1,0,1,0},
197 {0,1,0,1,0,1,0,1},
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}
206 static void free_userdata_once(GAME g);
208 // Posición por rotación de tablero.
209 // Rotation board position.
210 static void rotate_position(int p)
212 struct userdata_s *d = gp->data;
213 char fr, fc;
214 char fr2 = 8, fc2 = 8;
216 for (fr = 1; fr < 9; fr++) {
217 if (fr2 < 1)
218 fr2 = 8;
219 for (fc = 1; fc < 9; fc++){
220 if (fc2 < 1)
221 fc2 = 8;
222 if (p == CURSOR_POSITION &&
223 d->c_row == fr && d->c_col == fc) {
224 d->c_row = fr2;
225 d->c_col = fc2;
226 return;
228 else if (p == SP_POSITION &&
229 d->sp.row == fr && d->sp.col == fc) {
230 d->sp.row = fr2;
231 d->sp.col = fc2;
232 return;
234 else if (p == SPS_POSITION &&
235 d->sp.srow == fr && d->sp.scol == fc) {
236 d->sp.srow = fr2;
237 d->sp.scol = fc2;
238 return;
240 fc2--;
242 fr2--;
246 void update_cursor(GAME g, int idx)
248 char *p;
249 int len;
250 int t = pgn_history_total(g->hp);
251 struct userdata_s *d = g->data;
254 * If not deincremented then r and c would be the next move.
256 idx--;
258 if (idx > t || idx < 0 || !t || !g->hp[idx]->move) {
259 d->c_row = 2, d->c_col = 5;
260 goto historyrotate;
261 // return;
264 p = g->hp[idx]->move;
265 len = strlen(p);
267 if (*p == 'O') {
268 if (len <= 4)
269 d->c_col = 7;
270 else
271 d->c_col = 3;
273 d->c_row = (g->turn == WHITE) ? 8 : 1;
274 return;
277 p += len;
279 while (!isdigit(*p))
280 p--;
282 d->c_row = RANKTOINT(*p--);
283 d->c_col = FILETOINT(*p);
285 historyrotate:
286 if (d->mode == MODE_HISTORY && rotate)
287 rotate_position(CURSOR_POSITION);
290 static int init_nag()
292 FILE *fp;
293 char line[LINE_MAX];
294 int i = 0;
296 if ((fp = fopen(config.nagfile, "r")) == NULL) {
297 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s: %s", config.nagfile, strerror(errno));
298 return 1;
301 nags = Realloc(nags, (i+2) * sizeof(char *));
302 nags[i++] = strdup(_("none"));
303 nags[i] = NULL;
305 while (!feof(fp)) {
306 if (fscanf(fp, " %[^\n] ", line) == 1) {
307 nags = Realloc(nags, (i + 2) * sizeof(char *));
308 nags[i++] = strdup(line);
312 nags[i] = NULL;
313 nag_total = i;
314 return 0;
317 void edit_nag_toggle_item(struct menu_input_s *m)
319 struct input_s *in = m->data;
320 struct input_data_s *id = in->data;
321 HISTORY *h = id->data;
322 int i;
324 if (m->selected == 0) {
325 for (i = 0; i < MAX_PGN_NAG; i++)
326 h->nag[i] = 0;
328 for (i = 0; m->items[i]; i++)
329 m->items[i]->selected = 0;
331 return;
334 for (i = 0; i < MAX_PGN_NAG; i++) {
335 if (h->nag[i] == m->selected)
336 h->nag[i] = m->selected = 0;
337 else {
338 if (!h->nag[i]) {
339 h->nag[i] = m->selected;
340 break;
346 void edit_nag_save(struct menu_input_s *m)
348 pushkey = -1;
351 void edit_nag_help(struct menu_input_s *m)
353 message(_("NAG Menu Keys"), _("[ press any key to continue ]"), "%s",
355 " UP/DOWN - previous/next menu item\n"
356 " HOME/END - first/last menu item\n"
357 " PGDN/PGUP - next/previous page\n"
358 " a-zA-Z0-9 - jump to item\n"
359 " SPACE - toggle selected item\n"
360 " CTRL-X - quit with changes"
364 struct menu_item_s **get_nag_items(WIN *win)
366 int i, n;
367 struct menu_input_s *m = win->data;
368 struct input_s *in = m->data;
369 struct input_data_s *id = in->data;
370 struct menu_item_s **items = m->items;
371 HISTORY *h = id->data;
373 if (items) {
374 for (i = 0; items[i]; i++)
375 free(items[i]);
378 for (i = 0; nags[i]; i++) {
379 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
380 items[i] = Malloc(sizeof(struct menu_item_s));
381 items[i]->name = nags[i];
382 items[i]->value = NULL;
384 for (n = 0; n < MAX_PGN_NAG; n++) {
385 if (h->nag[n] == i) {
386 items[i]->selected = 1;
387 n = -1;
388 break;
392 if (n >= 0)
393 items[i]->selected = 0;
396 items[i] = NULL;
397 m->nofree = 1;
398 m->items = items;
399 return items;
402 void nag_print(WIN *win)
404 struct menu_input_s *m = win->data;
406 mvwprintw(win->w, m->print_line, 1, "%-*s", win->cols - 2, m->item->name);
409 void edit_nag(void *arg)
411 struct menu_key_s **keys = NULL;
413 if (!nags) {
414 if (init_nag())
415 return;
418 add_menu_key(&keys, ' ', edit_nag_toggle_item);
419 add_menu_key(&keys, CTRL_KEY('x'), edit_nag_save);
420 add_menu_key(&keys, KEY_F(1), edit_nag_help);
421 construct_menu(0, 0, -1, -1, _("Numeric Annotation Glyphs"), 1, get_nag_items, keys, arg,
422 nag_print, NULL);
423 return;
426 static void *view_nag(void *arg)
428 HISTORY *h = (HISTORY *)arg;
429 char buf[80];
430 char line[LINE_MAX] = {0};
431 int i = 0;
433 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Viewing NAG for"), h->move);
435 if (!nags) {
436 if (init_nag())
437 return NULL;
440 for (i = 0; i < MAX_PGN_NAG; i++) {
441 char buf2[16];
443 if (!h->nag[i])
444 break;
446 if (h->nag[i] >= nag_total)
447 strncat(line, itoa(h->nag[i], buf2), sizeof(line)-1);
448 else
449 strncat(line, nags[h->nag[i]], sizeof(line)-1);
451 strncat(line, "\n", sizeof(line)-1);
454 line[strlen(line) - 1] = 0;
455 message(buf, _("[ press any key to continue ]"), "%s", line);
456 return NULL;
459 void view_annotation(HISTORY *h)
461 char buf[MAX_SAN_MOVE_LEN + strlen(_("Viewing Annotation for")) + 4];
462 int nag = 0, comment = 0;
464 if (!h)
465 return;
467 if (h->comment && h->comment[0])
468 comment++;
470 if (h->nag[0])
471 nag++;
473 if (!nag && !comment)
474 return;
476 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Viewing Annotation for"), h->move);
478 if (comment)
479 construct_message(buf, (nag) ? _("Any other key to continue") : _("[ press any key to continue ]"), 0, 1,
480 (nag) ? _("Press 'n' to view NAG") : NULL,
481 (nag) ? view_nag : NULL, (nag) ? h : NULL, NULL,
482 (nag) ? 'n' : 0, 0, "%s", h->comment);
483 else
484 construct_message(buf, _("Any other key to continue"), 0, 1, _("Press 'n' to view NAG"), view_nag, h, NULL,
485 'n', 0, "%s", _("No comment text for this move"));
488 int do_game_write(char *filename, char *mode, int start, int end)
490 int i;
491 struct userdata_s *d;
492 PGN_FILE *pgn;
494 i = pgn_open(filename, mode, &pgn);
496 if (i == E_PGN_ERR) {
497 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s\n%s", filename, strerror(errno));
498 return 1;
500 else if (i == E_PGN_INVALID) {
501 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s\n%s", filename, _("Not a regular file"));
502 return 1;
505 for (i = (start == -1) ? 0 : start; i < end; i++) {
506 d = game[i]->data;
507 pgn_write(pgn, game[i]);
508 CLEAR_FLAG(d->flags, CF_MODIFIED);
511 if (pgn_close(pgn) != E_PGN_OK)
512 message(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s", strerror(errno));
514 if (start == -1) {
515 strncpy(loadfile, filename, sizeof(loadfile));
516 loadfile[sizeof(loadfile)-1] = 0;
519 return 0;
522 struct save_game_s {
523 char *filename;
524 char *mode;
525 int start;
526 int end;
529 void do_save_game_overwrite_confirm(WIN *win)
531 char *mode = "w";
532 struct save_game_s *s = win->data;
534 switch (win->c) {
535 case 'a':
536 mode = "a";
537 break;
538 case 'o':
539 mode = "w";
540 break;
541 default:
542 goto done;
545 if (do_game_write(s->filename, mode, s->start, s->end))
546 update_status_notify(gp, "%s", _("Save game failed."));
547 else
548 update_status_notify(gp, "%s", _("Game saved."));
550 done:
551 free(s->filename);
552 free(s);
555 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
556 * game index number.
558 void save_pgn(char *filename, int saveindex)
560 char buf[FILENAME_MAX];
561 struct stat st;
562 int end = (saveindex == -1) ? gtotal : saveindex + 1;
563 struct save_game_s *s;
565 if (filename[0] != '/' && config.savedirectory) {
566 if (stat(config.savedirectory, &st) == -1) {
567 if (errno == ENOENT) {
568 if (mkdir(config.savedirectory, 0755) == -1) {
569 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s: %s", config.savedirectory,
570 strerror(errno));
571 return;
574 else {
575 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s: %s", config.savedirectory,
576 strerror(errno));
577 return;
581 stat(config.savedirectory, &st);
583 if (!S_ISDIR(st.st_mode)) {
584 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s: %s", config.savedirectory, _("Not a directory."));
585 return;
588 snprintf(buf, sizeof(buf), "%s/%s", config.savedirectory, filename);
589 filename = buf;
592 if (access(filename, W_OK) == 0) {
593 s = Malloc(sizeof(struct save_game_s));
594 s->filename = strdup(filename);
595 s->start = saveindex;
596 s->end = end;
597 construct_message(NULL, _("'a' to append, 'o' to overwrite"), 1, 1, NULL, NULL,
598 s, do_save_game_overwrite_confirm, 0, 0, "%s \"%s\"",
599 _("File exists:"), filename);
600 return;
603 if (do_game_write(filename, "a", saveindex, end))
604 update_status_notify(gp, "%s", _("Save game failed."));
605 else
606 update_status_notify(gp, "%s", _("Game saved."));
609 static int castling_state(GAME g, BOARD b, int row, int col, int piece, int mod)
611 if (pgn_piece_to_int(piece) == ROOK && col == 7
612 && row == 7 &&
613 (TEST_FLAG(g->flags, GF_WK_CASTLE) || mod) &&
614 pgn_piece_to_int(b[7][4].icon) == KING && isupper(piece)) {
615 if (mod)
616 TOGGLE_FLAG(g->flags, GF_WK_CASTLE);
617 return 1;
619 else if (pgn_piece_to_int(piece) == ROOK && col == 0
620 && row == 7 &&
621 (TEST_FLAG(g->flags, GF_WQ_CASTLE) || mod) &&
622 pgn_piece_to_int(b[7][4].icon) == KING && isupper(piece)) {
623 if (mod)
624 TOGGLE_FLAG(g->flags, GF_WQ_CASTLE);
625 return 1;
627 else if (pgn_piece_to_int(piece) == ROOK && col == 7
628 && row == 0 &&
629 (TEST_FLAG(g->flags, GF_BK_CASTLE) || mod) &&
630 pgn_piece_to_int(b[0][4].icon) == KING && islower(piece)) {
631 if (mod)
632 TOGGLE_FLAG(g->flags, GF_BK_CASTLE);
633 return 1;
635 else if (pgn_piece_to_int(piece) == ROOK && col == 0
636 && row == 0 &&
637 (TEST_FLAG(g->flags, GF_BQ_CASTLE) || mod) &&
638 pgn_piece_to_int(b[0][4].icon) == KING && islower(piece)) {
639 if (mod)
640 TOGGLE_FLAG(g->flags, GF_BQ_CASTLE);
641 return 1;
643 else if (pgn_piece_to_int(piece) == KING && col == 4
644 && row == 7 &&
645 (mod || (pgn_piece_to_int(b[7][7].icon) == ROOK &&
646 TEST_FLAG(g->flags, GF_WK_CASTLE))
648 (pgn_piece_to_int(b[7][0].icon) == ROOK &&
649 TEST_FLAG(g->flags, GF_WQ_CASTLE))) && isupper(piece)) {
650 if (mod) {
651 if (TEST_FLAG(g->flags, GF_WK_CASTLE) ||
652 TEST_FLAG(g->flags, GF_WQ_CASTLE))
653 CLEAR_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE);
654 else
655 SET_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE);
657 return 1;
659 else if (pgn_piece_to_int(piece) == KING && col == 4
660 && row == 0 &&
661 (mod || (pgn_piece_to_int(b[0][7].icon) == ROOK &&
662 TEST_FLAG(g->flags, GF_BK_CASTLE))
664 (pgn_piece_to_int(b[0][0].icon) == ROOK &&
665 TEST_FLAG(g->flags, GF_BQ_CASTLE))) && islower(piece)) {
666 if (mod) {
667 if (TEST_FLAG(g->flags, GF_BK_CASTLE) ||
668 TEST_FLAG(g->flags, GF_BQ_CASTLE))
669 CLEAR_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE);
670 else
671 SET_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE);
673 return 1;
676 return 0;
679 #define IS_ENPASSANT(c) (c == 'x') ? CP_BOARD_ENPASSANT : isupper(c) ? CP_BOARD_WHITE : CP_BOARD_BLACK
680 #define ATTRS(cp) (cp & (A_BOLD|A_STANDOUT|A_BLINK|A_DIM|A_UNDERLINE|A_INVIS|A_REVERSE))
682 static void
683 init_wchar_pieces ()
685 w_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♙" : "P");
686 w_rook_wchar = str_to_wchar (config.utf8_pieces ? "♖" : "R");
687 w_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♗" : "B");
688 w_knight_wchar = str_to_wchar (config.utf8_pieces ? "♘" : "N");
689 w_queen_wchar = str_to_wchar (config.utf8_pieces ? "♕" : "Q");
690 w_king_wchar = str_to_wchar (config.utf8_pieces ? "♔" : "K");
691 b_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♟" : "p");
692 b_rook_wchar = str_to_wchar (config.utf8_pieces ? "♜" : "r");
693 b_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♝" : "b");
694 b_knight_wchar = str_to_wchar (config.utf8_pieces ? "♞" : "n");
695 b_queen_wchar = str_to_wchar (config.utf8_pieces ? "♛" : "q");
696 b_king_wchar = str_to_wchar (config.utf8_pieces ? "♚" : "k");
697 empty_wchar = str_to_wchar (" ");
698 enpassant_wchar = str_to_wchar ("x");
701 static wchar_t *
702 piece_to_wchar (unsigned char p)
704 switch (p)
706 case 'P':
707 return w_pawn_wchar;
708 case 'p':
709 return b_pawn_wchar;
710 case 'R':
711 return w_rook_wchar;
712 case 'r':
713 return b_rook_wchar;
714 case 'B':
715 return w_bishop_wchar;
716 case 'b':
717 return b_bishop_wchar;
718 case 'N':
719 return w_knight_wchar;
720 case 'n':
721 return b_knight_wchar;
722 case 'Q':
723 return w_queen_wchar;
724 case 'q':
725 return b_queen_wchar;
726 case 'K':
727 return w_king_wchar;
728 case 'k':
729 return b_king_wchar;
730 case 'x':
731 return enpassant_wchar;
734 return empty_wchar;
737 static int piece_can_attack (GAME g, int rank, int file)
739 struct userdata_s *d = g->data;
740 char *m, *frfr = NULL;
741 pgn_error_t e;
742 int row, col, p;
743 int v = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].valid;
744 int pi = pgn_piece_to_int (d->b[RANKTOBOARD (rank)][FILETOBOARD (file)].icon);
745 int cpi = d->sp.icon
746 ? pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon)
747 : pgn_piece_to_int (d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon);
749 if (pi == OPEN_SQUARE || cpi == OPEN_SQUARE || !VALIDFILE (file)
750 || !VALIDRANK (rank))
751 return 0;
753 if (d->sp.icon) {
754 col = v ? d->c_col : d->sp.scol;
755 row = v ? d->c_row : d->sp.srow;
757 else {
758 col = d->c_col;
759 row = d->c_row;
762 m = malloc(MAX_SAN_MOVE_LEN+1);
763 m[0] = INTTOFILE (file);
764 m[1] = INTTORANK (rank);
765 m[2] = INTTOFILE (col);
766 m[3] = INTTORANK (row);
767 m[4] = 0;
769 if (d->sp.icon && v) {
770 BOARD b;
772 memcpy (b, d->b, sizeof(BOARD));
773 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
774 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
775 pgn_int_to_piece (WHITE, OPEN_SQUARE);
776 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
777 pgn_switch_turn(g);
778 e = pgn_validate_move (g, b, &m, &frfr);
779 pgn_switch_turn (g);
780 free (m);
781 free (frfr);
782 return e == E_PGN_OK ? 1 : 0;
785 pgn_switch_turn(g);
786 e = pgn_validate_move (g, d->b, &m, &frfr);
787 pgn_switch_turn (g);
789 if (!strcmp (m, "O-O") || !strcmp (m, "O-O-O"))
790 e = E_PGN_INVALID;
792 if (e == E_PGN_OK) {
793 int sf = FILETOINT (frfr[0]), sr = RANKTOINT (frfr[1]);
794 int df = FILETOINT (frfr[2]);
796 pi = d->b[RANKTOBOARD (sr)][FILETOBOARD (sf)].icon;
797 pi = pgn_piece_to_int (pi);
798 if (pi == PAWN && sf == df)
799 e = E_PGN_INVALID;
802 free (m);
803 free (frfr);
804 return e == E_PGN_OK ? 1 : 0;
807 void print_piece(WINDOW *w, int l, int c, char p)
809 int i, y, ff = 0;
811 for (i = 0; i < 13; i += 2) {
812 if (p == piece_chars[i] || p == piece_chars[i + 1]) {
813 for (y = 0; y < 3; y++)
814 mvwprintw(w, l + y, c, "%s", f_pieces[i + ff + y]);
815 return;
818 ff++;
821 for (y = 0; y < 3; y++)
822 mvwprintw(w, l + y, c, f_pieces[0]);
825 int inv_int(int x)
827 int fx, fx2 = 8;
829 for (fx = 1; fx < 9; fx++) {
830 if (x == fx)
831 break;
832 fx2--;
835 return fx2;
838 // FIXME: BIG_BOARD - start in dwm monocle layout.
839 // Example: lxterminal -e cboard (dmenu, geany, etc.)
840 void update_board_window(GAME g)
842 int row, col;
843 int bcol = 0, brow = 0;
844 int l = config.coordsyleft;
845 int maxy = BOARD_HEIGHT, maxx = BOARD_WIDTH;
846 int ncols = 0, offset = 1;
847 int rowr = (MEGA_BOARD) ? 6 : (BIG_BOARD) ? 4 : 2;
848 int colr = (MEGA_BOARD) ? 12 : (BIG_BOARD) ? 8 : 4;
849 unsigned coords_y = 8, cxgc = 0;
850 unsigned i, cpd = 0;
851 struct userdata_s *d = g->data;
853 if (d->mode != MODE_PLAY && d->mode != MODE_EDIT)
854 update_cursor(g, g->hindex);
856 if (BIG_BOARD) {
857 if (rotate) {
858 brow = 7;
859 coords_y = 1;
862 else {
863 if (rotate) {
864 brow = 1;
865 coords_y = 1;
867 else
868 brow = 8;
871 for (row = 0; row < maxy; row++) {
872 if (BIG_BOARD) {
873 if (rotate)
874 bcol = 7;
875 else
876 bcol = 0;
878 else {
879 if (rotate)
880 bcol = 8;
881 else
882 bcol = 1;
885 for (col = 0; col < maxx; col++) {
886 int attrwhich = -1;
887 chtype attrs = 0, old_attrs = 0;
888 unsigned char p;
889 int can_attack = 0;
890 int valid = 0;
892 if (row == 0 || row == maxy - 2) {
893 if (col == 0)
894 mvwaddch(boardw, row, col + l,
895 LINE_GRAPHIC((row)
896 ? ACS_LLCORNER | CP_BOARD_GRAPHICS
897 : ACS_ULCORNER | CP_BOARD_GRAPHICS));
898 else if (col == maxx - 2)
899 mvwaddch(boardw, row, col + l,
900 LINE_GRAPHIC((row)
901 ? ACS_LRCORNER | CP_BOARD_GRAPHICS
902 : ACS_URCORNER | CP_BOARD_GRAPHICS));
903 else if (!(col % colr))
904 mvwaddch(boardw, row, col + l,
905 LINE_GRAPHIC((row)
906 ? ACS_BTEE | CP_BOARD_GRAPHICS
907 : ACS_TTEE | CP_BOARD_GRAPHICS));
908 else {
909 if (col != maxx - 1)
910 mvwaddch(boardw, row, col + l,
911 LINE_GRAPHIC(ACS_HLINE | CP_BOARD_GRAPHICS));
914 continue;
917 if ((row % 2) && col == maxx - 1 &&
918 (coords_y > 0 && coords_y < 9)) {
919 wattron(boardw, CP_BOARD_COORDS);
920 mvwprintw(boardw,
921 (BIG_BOARD) ? row * ((MEGA_BOARD) ? 3 : 2)
922 : row, (l) ? 0 : col, "%d",
923 (rotate) ? coords_y++ : coords_y--);
924 wattroff(boardw, CP_BOARD_COORDS);
925 continue;
928 if ((col == 0 || col == maxx - 2) && row != maxy - 1) {
929 if (!(row % rowr))
930 mvwaddch(boardw, row, col + l,
931 LINE_GRAPHIC((col) ?
932 ACS_RTEE | CP_BOARD_GRAPHICS :
933 ACS_LTEE | CP_BOARD_GRAPHICS));
934 else
935 mvwaddch(boardw, row, col + l,
936 LINE_GRAPHIC(ACS_VLINE | CP_BOARD_GRAPHICS));
938 continue;
941 if ((row % rowr) && !(col % colr) && row != maxy - 1) {
942 mvwaddch(boardw, row, col + l,
943 LINE_GRAPHIC(ACS_VLINE | CP_BOARD_GRAPHICS));
944 continue;
947 if (!(col % colr) && row != maxy - 1) {
948 mvwaddch(boardw, row, col + l,
949 LINE_GRAPHIC(ACS_PLUS | CP_BOARD_GRAPHICS));
950 continue;
953 if ((row % rowr)) {
954 if ((col % colr)) {
955 if (BIG_BOARD)
956 attrwhich = (cb[brow][bcol]) ? WHITE : BLACK;
957 else {
958 if (ncols++ == 8) {
959 offset++;
960 ncols = 1;
963 if (((ncols % 2) && !(offset % 2))
964 || (!(ncols % 2) && (offset % 2)))
965 attrwhich = BLACK;
966 else
967 attrwhich = WHITE;
970 if (BIG_BOARD && rotate) {
971 brow = inv_int(brow + 1) - 1;
972 bcol = inv_int(bcol + 1) - 1;
975 if (BIG_BOARD)
976 p = d->b[brow][bcol].icon;
977 else
978 p = d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].icon;
980 int pi = pgn_piece_to_int(p);
982 if (config.details &&
983 ((!BIG_BOARD && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].enpassant)
984 || (BIG_BOARD && d->b[brow][bcol].enpassant))) {
985 p = pi = 'x';
986 attrs = mix_cp(CP_BOARD_ENPASSANT,
987 (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK,
988 ATTRS(CP_BOARD_ENPASSANT), A_FG_B_BG);
991 // FIXME: showattacks - BIG_BOARD and rotated board.
992 if (config.showattacks && config.details
993 && piece_can_attack (g, brow, bcol)) {
994 attrs = CP_BOARD_ATTACK;
995 old_attrs = attrs;
996 can_attack = 1;
999 if (config.validmoves &&
1000 ((!BIG_BOARD && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].valid)
1001 || (BIG_BOARD && d->b[brow][bcol].valid))) {
1002 old_attrs = -1;
1003 valid = 1;
1005 if (attrwhich == WHITE)
1006 attrs = mix_cp(CP_BOARD_MOVES_WHITE,
1007 IS_ENPASSANT(p),
1008 ATTRS(CP_BOARD_MOVES_WHITE),
1009 B_FG_A_BG);
1010 else
1011 attrs = mix_cp(CP_BOARD_MOVES_BLACK,
1012 IS_ENPASSANT(p),
1013 ATTRS(CP_BOARD_MOVES_BLACK),
1014 B_FG_A_BG);
1016 else if (p != 'x' && !can_attack)
1017 attrs = (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK;
1019 if (BIG_BOARD && rotate) {
1020 brow = inv_int(brow + 1) - 1;
1021 bcol = inv_int(bcol + 1) - 1;
1024 if ((!BIG_BOARD && row == ROWTOMATRIX(d->c_row)
1025 && col == COLTOMATRIX(d->c_col))
1026 || (BIG_BOARD && brow + 1 == inv_int(d->c_row)
1027 && bcol + 1 == d->c_col)) {
1028 attrs = mix_cp(CP_BOARD_CURSOR, IS_ENPASSANT(p),
1029 ATTRS(CP_BOARD_CURSOR), B_FG_A_BG);
1030 old_attrs = -1;
1032 else if ((!BIG_BOARD && row == ROWTOMATRIX(d->sp.srow) &&
1033 col == COLTOMATRIX(d->sp.scol)) ||
1034 (BIG_BOARD && brow + 1 == inv_int(d->sp.srow) &&
1035 bcol + 1 == d->sp.scol)) {
1036 attrs = mix_cp(CP_BOARD_SELECTED, IS_ENPASSANT(p),
1037 ATTRS(CP_BOARD_SELECTED), B_FG_A_BG);
1038 old_attrs = -1;
1041 if (row == maxy - 1)
1042 attrs = 0;
1044 if (can_attack) {
1045 attrs = mix_cp(CP_BOARD_ATTACK,
1046 valid ?
1047 (attrwhich == WHITE) ? CP_BOARD_MOVES_WHITE : CP_BOARD_MOVES_BLACK :
1048 (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK,
1049 ATTRS (CP_BOARD_ATTACK), A_FG_B_BG);
1052 if (BIG_BOARD)
1053 wmove(boardw, row, col + ((MEGA_BOARD) ? 5 : 3) + l);
1054 else
1055 mvwaddch(boardw, row, col + l, ' ' | attrs);
1057 if (row == maxy - 1 && cxgc < 8) {
1058 waddch(boardw, "abcdefgh"[(BIG_BOARD) ? bcol : bcol - 1] | CP_BOARD_COORDS);
1059 cxgc++;
1061 else {
1062 if (old_attrs == -1) {
1063 old_attrs = attrs;
1064 goto printc;
1067 old_attrs = attrs;
1069 if (pi != OPEN_SQUARE && p != 'x' && !can_attack) {
1070 if (attrwhich == WHITE) {
1071 if (isupper(p))
1072 attrs = CP_BOARD_W_W;
1073 else
1074 attrs = CP_BOARD_W_B;
1076 else {
1077 if (isupper(p))
1078 attrs = CP_BOARD_B_W;
1079 else
1080 attrs = CP_BOARD_B_B;
1084 printc:
1085 if (BIG_BOARD) {
1086 if (config.details && !can_attack
1087 && castling_state(g, d->b,
1088 (rotate) ? inv_int(brow + 1) - 1: brow,
1089 (rotate) ? inv_int(bcol + 1) - 1: bcol, p, 0))
1090 attrs = mix_cp(CP_BOARD_CASTLING, attrs,
1091 ATTRS(CP_BOARD_CASTLING),
1092 A_FG_B_BG);
1094 else {
1095 if (config.details && !can_attack
1096 && castling_state(g, d->b, RANKTOBOARD (brow),
1097 FILETOBOARD (bcol), p, 0)) {
1098 attrs = mix_cp(CP_BOARD_CASTLING, attrs,
1099 ATTRS(CP_BOARD_CASTLING),
1100 A_FG_B_BG);
1104 if (BIG_BOARD) {
1105 // FIXME: Reimpresión de piezas(+3).
1106 if (cpd < 67) {
1107 wattron (boardw, attrs);
1108 if (MEGA_BOARD){
1109 for (i = 0; i < 5; i++)
1110 mvwprintw(boardw, i + brow * 6 + 1,
1111 bcol * 12 + 1 + l,
1112 " ");
1113 if (pi != OPEN_SQUARE)
1114 print_piece(boardw, brow * 6 + 2,
1115 bcol * 12 + 3 + l, p);
1117 else {
1118 print_piece(boardw, brow * 4 + 1,
1119 bcol * 8 + 1 + l,
1120 (pi != OPEN_SQUARE) ? p : 0);
1123 wattroff (boardw, attrs);
1124 cpd++;
1127 else {
1128 wattron (boardw, attrs);
1129 waddwstr (boardw, piece_to_wchar (pi != OPEN_SQUARE ? p : 0));
1130 wattroff (boardw, attrs);
1133 attrs = old_attrs;
1136 if (BIG_BOARD)
1137 col += (MEGA_BOARD) ? 10 : 6;
1138 else {
1139 waddch(boardw, ' ' | attrs);
1140 col += 2;
1143 if (rotate)
1144 bcol--;
1145 else
1146 bcol++;
1148 if (BIG_BOARD) {
1149 if (bcol > 7)
1150 bcol = 0;
1151 if (bcol < 0)
1152 bcol = 7;
1156 else {
1157 if (col != maxx - 1)
1158 mvwaddch(boardw, row, col + l,
1159 LINE_GRAPHIC(ACS_HLINE | CP_BOARD_GRAPHICS));
1163 if (row % rowr) {
1164 if (rotate)
1165 brow++;
1166 else
1167 brow--;
1170 if (BIG_BOARD) {
1171 if (brow > 7)
1172 brow = 0;
1173 if (brow < 0)
1174 brow = 7;
1179 void invalid_move(int n, int e, const char *m)
1181 if (curses_initialized)
1182 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s \"%s\" (round #%i)", (e == E_PGN_AMBIGUOUS)
1183 ? _("Ambiguous move") : _("Invalid move"), m, n);
1184 else
1185 warnx("%s: %s \"%s\" (round #%i)", loadfile, (e == E_PGN_AMBIGUOUS)
1186 ? _("Ambiguous move") : _("Invalid move"), m, n);
1189 void gameover(GAME g)
1191 struct userdata_s *d = g->data;
1193 SET_FLAG(g->flags, GF_GAMEOVER);
1194 d->mode = MODE_HISTORY;
1195 stop_engine(g);
1198 static void update_clock(GAME g, struct itimerval it)
1200 struct userdata_s *d = g->data;
1202 if (TEST_FLAG(d->flags, CF_CLOCK) && g->turn == WHITE) {
1203 d->wclock.elapsed.tv_sec += it.it_value.tv_sec;
1204 d->wclock.elapsed.tv_usec += it.it_value.tv_usec;
1206 if (d->wclock.elapsed.tv_usec > 1000000 - 1) {
1207 d->wclock.elapsed.tv_sec += d->wclock.elapsed.tv_usec / 1000000;
1208 d->wclock.elapsed.tv_usec = d->wclock.elapsed.tv_usec % 1000000;
1211 if (d->wclock.tc[d->wclock.tcn][1] &&
1212 d->wclock.elapsed.tv_sec >= d->wclock.tc[d->wclock.tcn][1]) {
1213 pgn_tag_add(&g->tag, "Result", "0-1");
1214 gameover(g);
1217 else if (TEST_FLAG(d->flags, CF_CLOCK) && g->turn == BLACK) {
1218 d->bclock.elapsed.tv_sec += it.it_value.tv_sec;
1219 d->bclock.elapsed.tv_usec += it.it_value.tv_usec;
1221 if (d->bclock.elapsed.tv_usec > 1000000 - 1) {
1222 d->bclock.elapsed.tv_sec += d->bclock.elapsed.tv_usec / 1000000;
1223 d->bclock.elapsed.tv_usec = d->bclock.elapsed.tv_usec % 1000000;
1226 if (d->bclock.tc[d->bclock.tcn][1] &&
1227 d->bclock.elapsed.tv_sec >= d->bclock.tc[d->bclock.tcn][1]) {
1228 pgn_tag_add(&g->tag, "Result", "1-0");
1229 gameover(g);
1233 d->elapsed.tv_sec += it.it_value.tv_sec;
1234 d->elapsed.tv_usec += it.it_value.tv_usec;
1236 if (d->elapsed.tv_usec > 1000000 - 1) {
1237 d->elapsed.tv_sec += d->elapsed.tv_usec / 1000000;
1238 d->elapsed.tv_usec = d->elapsed.tv_usec % 1000000;
1242 static void update_time_control(GAME g)
1244 struct userdata_s *d = g->data;
1245 struct clock_s *clk = (g->turn == WHITE) ? &d->wclock : &d->bclock;
1247 if (clk->incr)
1248 clk->tc[clk->tcn][1] += clk->incr;
1250 if (!clk->tc[clk->tcn][1])
1251 return;
1253 clk->move++;
1255 if (!clk->tc[clk->tcn][0] || clk->move >= clk->tc[clk->tcn][0]) {
1256 clk->move = 0;
1257 clk->tc[clk->tcn + 1][1] += abs(clk->elapsed.tv_sec - clk->tc[clk->tcn][1]);
1258 memset(&clk->elapsed, 0, sizeof(clk->elapsed));
1259 clk->tcn++;
1263 void update_history_window(GAME g)
1265 char buf[HISTORY_WIDTH - 1];
1266 HISTORY *h = NULL;
1267 int n, total;
1268 int t = pgn_history_total(g->hp);
1270 n = (g->hindex + 1) / 2;
1272 if (t % 2)
1273 total = (t + 1) / 2;
1274 else
1275 total = t / 2;
1277 if (t)
1278 snprintf(buf, sizeof(buf), "%u %s %u%s", n, _("of"), total,
1279 (movestep == 1) ? _(" (ply)") : "");
1280 else
1281 strncpy(buf, _("not available"), sizeof(buf)-1);
1283 buf[sizeof(buf)-1] = 0;
1284 mvwprintw(historyw, 2, 1, "%*s %-*s", 10, _("Move:"),
1285 HISTORY_WIDTH - 13, buf);
1287 h = pgn_history_by_n(g->hp, g->hindex);
1288 snprintf(buf, sizeof(buf), "%s", (h && h->move) ? h->move : _("not available"));
1289 n = 0;
1291 if (h && ((h->comment) || h->nag[0])) {
1292 strncat(buf, _(" (Annotated"), sizeof(buf)-1);
1293 n++;
1296 if (h && h->rav) {
1297 strncat(buf, (n) ? ",+" : " (+", sizeof(buf)-1);
1298 n++;
1301 if (g->ravlevel) {
1302 strncat(buf, (n) ? ",-" : " (-", sizeof(buf)-1);
1303 n++;
1306 if (n)
1307 strncat(buf, ")", sizeof(buf)-1);
1309 mvwprintw(historyw, 3, 1, "%s %-*s", _("Next move:"),
1310 HISTORY_WIDTH - 13, buf);
1312 h = pgn_history_by_n(g->hp, g->hindex - 1);
1313 snprintf(buf, sizeof(buf), "%s", (h && h->move) ? h->move : _("not available"));
1314 n = 0;
1316 if (h && ((h->comment) || h->nag[0])) {
1317 strncat(buf, _(" (Annotated"), sizeof(buf)-1);
1318 n++;
1321 if (h && h->rav) {
1322 strncat(buf, (n) ? ",+" : " (+", sizeof(buf)-1);
1323 n++;
1326 if (g->ravlevel) {
1327 strncat(buf, (n) ? ",-" : " (-", sizeof(buf)-1);
1328 n++;
1331 if (n)
1332 strncat(buf, ")", sizeof(buf)-1);
1334 mvwprintw(historyw, 4, 1, "%s %-*s", _("Prev move:"),
1335 HISTORY_WIDTH - 13, buf);
1338 void do_validate_move(char *move)
1340 struct userdata_s *d = gp->data;
1341 int n;
1342 char *frfr = NULL;
1344 if (TEST_FLAG(d->flags, CF_HUMAN)) {
1345 if ((n = pgn_parse_move(gp, d->b, &move, &frfr)) != E_PGN_OK) {
1346 invalid_move(d->n + 1, n, move);
1347 return;
1350 update_time_control(gp);
1351 pgn_history_add(gp, d->b, move);
1352 pgn_switch_turn(gp);
1354 else {
1355 if ((n = pgn_validate_move(gp, d->b, &move, &frfr)) != E_PGN_OK) {
1356 invalid_move(d->n + 1, n, move);
1357 return;
1360 add_engine_command(gp, ENGINE_THINKING, "%s\n",
1361 (config.engine_protocol == 1) ? frfr : move);
1364 d->sp.srow = d->sp.scol = d->sp.icon = 0;
1366 if (config.validmoves)
1367 pgn_reset_valid_moves(d->b);
1369 if (TEST_FLAG(gp->flags, GF_GAMEOVER))
1370 d->mode = MODE_HISTORY;
1371 else
1372 SET_FLAG(d->flags, CF_MODIFIED);
1374 free (frfr);
1375 d->paused = 0;
1376 update_history_window(gp);
1377 update_board_window(gp);
1378 return;
1381 void do_promotion_piece_finalize(WIN *win)
1383 char *p, *str = win->data;
1385 if (pgn_piece_to_int(win->c) == -1)
1386 return;
1388 p = str + strlen(str);
1389 *p++ = toupper(win->c);
1390 *p = '\0';
1391 do_validate_move(str);
1392 free (win->data);
1393 win->data = NULL;
1396 static void move_to_engine(GAME g)
1398 struct userdata_s *d = g->data;
1399 char *str;
1400 int piece;
1402 if (config.validmoves &&
1403 !d->b[RANKTOBOARD(d->sp.row)][FILETOBOARD(d->sp.col)].valid)
1404 return;
1406 str = Malloc(MAX_SAN_MOVE_LEN + 1);
1407 snprintf(str, MAX_SAN_MOVE_LEN + 1, "%c%i%c%i",
1408 _("abcdefgh")[d->sp.scol - 1],
1409 d->sp.srow, _("abcdefgh")[d->sp.col - 1], d->sp.row);
1411 piece = pgn_piece_to_int(d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon);
1413 if (piece == PAWN && (d->sp.row == 8 || d->sp.row == 1)) {
1414 construct_message(_("Select Pawn Promotion Piece"), _ ("R/N/B/Q"), 1, 1, NULL, NULL,
1415 str, do_promotion_piece_finalize, 0, 0, "%s", _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1416 return;
1419 do_validate_move(str);
1420 free (str);
1423 static char *clock_to_char(long n)
1425 static char buf[16];
1426 int h = 0, m = 0, s = 0;
1428 h = n / 3600;
1429 m = (n % 3600) / 60;
1430 s = (n % 3600) % 60;
1431 snprintf(buf, sizeof(buf), "%.2i:%.2i:%.2i", h, m, s);
1432 return buf;
1435 static char *timeval_to_char(struct timeval t, long limit)
1437 static char buf[9];
1438 int h = 0, m = 0, s = 0;
1439 int n = limit ? abs(limit - t.tv_sec) : 0;
1441 h = n / 3600;
1442 m = (n % 3600) / 60;
1443 s = (n % 3600) % 60;
1444 snprintf(buf, sizeof(buf), "%.2i:%.2i:%.2i", h, m, s);
1445 return buf;
1448 static char *time_control_status(struct clock_s *clk)
1450 static char buf[80] = {0};
1452 buf[0] = 0;
1454 if (clk->tc[clk->tcn][0] && clk->tc[clk->tcn + 1][1])
1455 snprintf(buf, sizeof(buf), " M%.2i/%s", abs(clk->tc[clk->tcn][0] - clk->move),
1456 clock_to_char(clk->tc[clk->tcn + 1][1]));
1457 else if (!clk->incr)
1458 return "";
1460 if (clk->incr) {
1461 char buf[16];
1462 strncat(buf, " I", sizeof(buf)-1);
1463 strncat(buf, itoa(clk->incr, buf), sizeof(buf)-1);
1466 return buf;
1469 void update_status_window(GAME g)
1471 int i = 0;
1472 char *buf;
1473 char tmp[15] = {0}, *engine, *mode;
1474 char t[COLS];
1475 int w;
1476 char *p;
1477 int maxy, maxx;
1478 int len;
1479 struct userdata_s *d = g->data;
1480 int y;
1481 int n;
1483 if (!curses_initialized)
1484 return;
1486 getmaxyx(statusw, maxy, maxx);
1487 (void)maxy;
1488 w = maxx - 2 - 8;
1489 len = maxx - 2;
1490 buf = Malloc(len);
1491 y = 2;
1493 wchar_t *loadfilew = loadfile[0] ? str_etc (loadfile, w, 1) : str_to_wchar (_ ("not available"));
1494 mvwprintw(statusw, y++, 1, "%*s %-*ls", 7, _("File:"), w, loadfilew);
1495 free (loadfilew);
1496 snprintf(buf, len, "%i %s %i", gindex + 1, _("of"), gtotal);
1497 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Game:"), w, buf);
1499 *tmp = '\0';
1500 p = tmp;
1502 if (config.details) {
1503 *p++ = 'D';
1504 i++;
1507 if (TEST_FLAG(d->flags, CF_DELETE)) {
1508 if (i)
1509 *p++ = '/';
1511 *p++ = 'X';
1512 i++;
1515 if (TEST_FLAG(g->flags, GF_PERROR)) {
1516 if (i)
1517 *p++ = '/';
1519 *p++ = '!';
1520 i++;
1523 if (TEST_FLAG(d->flags, CF_MODIFIED)) {
1524 if (i)
1525 *p++ = '/';
1527 *p++ = '*';
1528 i++;
1531 pgn_config_get(PGN_STRICT_CASTLING, &n);
1533 if (n == 1) {
1534 if (i)
1535 *p++ = '/';
1537 *p++ = 'C';
1538 i++;
1540 #ifdef WITH_LIBPERL
1541 if (TEST_FLAG(d->flags, CF_PERL)) {
1542 if (i)
1543 *p++ = '/';
1545 *p++ = 'P';
1546 i++;
1548 #endif
1550 *p = '\0';
1551 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Flags:"), w, (tmp[0]) ? tmp : "-");
1553 switch (d->mode) {
1554 case MODE_HISTORY:
1555 mode = _("move history");
1556 break;
1557 case MODE_EDIT:
1558 mode = _("edit");
1559 break;
1560 case MODE_PLAY:
1561 mode = _("play");
1562 break;
1563 default:
1564 mode = _("(empty value)");
1565 break;
1568 snprintf(buf, len - 1, "%*s %s", 7, _("Mode:"), mode);
1570 if (d->mode == MODE_PLAY) {
1571 if (TEST_FLAG(d->flags, CF_HUMAN))
1572 strncat(buf, _(" (human/human)"), len - 1);
1573 else if (TEST_FLAG(d->flags, CF_ENGINE_LOOP))
1574 strncat(buf, _(" (engine/engine)"), len - 1);
1575 else
1576 strncat(buf, (d->play_mode == PLAY_EH) ?
1577 _(" (engine/human)") : _(" (human/engine)"), len - 1);
1580 buf[len-1] = 0;
1581 mvwprintw(statusw, y++, 1, "%-*s", len, buf);
1582 free(buf);
1584 if (d->engine) {
1585 switch (d->engine->status) {
1586 case ENGINE_THINKING:
1587 engine = _("pondering...");
1588 break;
1589 case ENGINE_READY:
1590 engine = _("ready");
1591 break;
1592 case ENGINE_INITIALIZING:
1593 engine = _("initializing...");
1594 break;
1595 case ENGINE_OFFLINE:
1596 engine = _("offline");
1597 break;
1598 default:
1599 engine = _("(empty value)");
1600 break;
1603 else
1604 engine = _("offline");
1606 mvwprintw(statusw, y, 1, "%*s %-*s", 7, _("Engine:"), w, " ");
1607 wattron(statusw, CP_STATUS_ENGINE);
1608 mvwaddstr(statusw, y++, 9, engine);
1609 wattroff(statusw, CP_STATUS_ENGINE);
1611 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Turn:"), w,
1612 (g->turn == WHITE) ? _("white") : _("black"));
1614 strncpy(tmp, _("white"), sizeof(tmp)-1);
1615 tmp[0] = toupper(tmp[0]);
1616 snprintf(t, sizeof(t), "%s%s",
1617 timeval_to_char(d->wclock.elapsed, d->wclock.tc[d->wclock.tcn][1]),
1618 time_control_status(&d->wclock));
1619 mvwprintw(statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1621 strncpy(tmp, _("black"), sizeof(tmp)-1);
1622 tmp[0] = toupper(tmp[0]);
1623 snprintf(t, sizeof(t), "%s%s",
1624 timeval_to_char(d->bclock.elapsed, d->bclock.tc[d->bclock.tcn][1]),
1625 time_control_status(&d->bclock));
1626 mvwprintw(statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1628 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Total:"), w,
1629 clock_to_char(d->elapsed.tv_sec));
1631 // for (i = 0; i < STATUS_WIDTH; i++)
1632 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1634 if (!status.notify)
1635 status.notify = str_to_wchar(_("Type F1 for help"));
1637 wattron(stdscr, CP_STATUS_NOTIFY);
1638 for (i = (config.boardleft) ? BOARD_WIDTH : 0;
1639 i < ((config.boardleft) ? COLS : STATUS_WIDTH); i++)
1640 mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1641 mvwprintw(stdscr, STATUS_HEIGHT, CENTERX(STATUS_WIDTH, status.notify)
1642 + ((config.boardleft) ? BOARD_WIDTH : 0),
1643 "%ls", status.notify);
1644 wattroff(stdscr, CP_STATUS_NOTIFY);
1647 void update_tag_window(TAG **t)
1649 int i, l, w;
1650 int namel = 0;
1652 for (i = 0; t[i]; i++) {
1653 wchar_t *namewc = str_to_wchar (t[i]->name);
1655 l = wcslen(namewc);
1656 free (namewc);
1657 if (l > namel)
1658 namel = l;
1661 w = TAG_WIDTH - namel - 4;
1663 for (i = 0; t[i] && i < TAG_HEIGHT - 3; i++) {
1664 wchar_t *namewc = str_to_wchar(t[i]->name);
1665 wchar_t *valuewc = str_etc(t[i]->value, w, 0);
1667 mvwprintw(tagw, (i + 2), 1, "%*ls: %-*ls", namel, namewc, w, valuewc);
1668 free (namewc);
1669 free (valuewc);
1672 for (; i < TAG_HEIGHT - 3; i++)
1673 mvwprintw(tagw, (i + 2), 1, "%*s", namel + w + 2, " ");
1676 void append_enginebuf(GAME g, char *line)
1678 int i = 0;
1679 struct userdata_s *d = g->data;
1681 if (d->engine->enginebuf)
1682 for (i = 0; d->engine->enginebuf[i]; i++);
1684 if (i >= LINES - 3) {
1685 free(d->engine->enginebuf[0]);
1687 for (i = 0; d->engine->enginebuf[i+1]; i++)
1688 d->engine->enginebuf[i] = d->engine->enginebuf[i+1];
1690 d->engine->enginebuf[i] = strdup(line);
1692 else {
1693 d->engine->enginebuf = Realloc(d->engine->enginebuf, (i + 2) * sizeof(char *));
1694 d->engine->enginebuf[i++] = strdup(line);
1695 d->engine->enginebuf[i] = NULL;
1699 void update_engine_window(GAME g)
1701 int i;
1702 struct userdata_s *d = g->data;
1704 if (!d->engine || !d->engine->enginebuf)
1705 return;
1707 wmove(enginew, 0, 0);
1708 wclrtobot(enginew);
1710 if (d->engine->enginebuf) {
1711 for (i = 0; d->engine->enginebuf[i]; i++)
1712 mvwprintw(enginew, i + 2, 1, "%s", d->engine->enginebuf[i]);
1715 window_draw_title(enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
1716 CP_MESSAGE_BORDER);
1719 void update_all(GAME g)
1721 struct userdata_s *d = g->data;
1724 * In the middle of a macro. Don't update the screen.
1726 if (macro_match != -1)
1727 return;
1730 * No need to update when the engine window is being shown.
1732 if (enginep && panel_hidden(enginep) == ERR) {
1733 update_panels();
1734 doupdate();
1735 return;
1738 wmove(boardw, ROWTOMATRIX(d->c_row), COLTOMATRIX(d->c_col));
1739 update_board_window(g);
1740 update_status_window(g);
1741 update_history_window(g);
1742 update_tag_window(g->tag);
1743 update_engine_window(g);
1744 update_panels();
1745 doupdate();
1748 static void game_next_prev(GAME g, int n, int count)
1750 if (gtotal < 2)
1751 return;
1753 if (n == 1) {
1754 if (gindex + count > gtotal - 1) {
1755 if (count != 1)
1756 gindex = gtotal - 1;
1757 else
1758 gindex = 0;
1760 else
1761 gindex += count;
1763 else {
1764 if (gindex - count < 0) {
1765 if (count != 1)
1766 gindex = 0;
1767 else
1768 gindex = gtotal - 1;
1770 else
1771 gindex -= count;
1774 gp = game[gindex];
1777 static void delete_game(int which)
1779 GAME *g = NULL;
1780 int gi = 0;
1781 int i;
1782 struct userdata_s *d;
1784 for (i = 0; i < gtotal; i++) {
1785 d = game[i]->data;
1787 if (i == which || TEST_FLAG(d->flags, CF_DELETE)) {
1788 free_userdata_once(game[i]);
1789 pgn_free(game[i]);
1790 continue;
1793 g = Realloc(g, (gi + 1) * sizeof(GAME *));
1794 g[gi] = Calloc(1, sizeof(struct game_s));
1795 memcpy(g[gi], game[i], sizeof(struct game_s));
1796 g[gi]->tag = game[i]->tag;
1797 g[gi]->history = game[i]->history;
1798 g[gi]->hp = game[i]->hp;
1799 gi++;
1802 game = g;
1803 gtotal = gi;
1805 if (which != -1) {
1806 if (which + 1 >= gtotal)
1807 gindex = gtotal - 1;
1808 else
1809 gindex = which;
1811 else
1812 gindex = gtotal - 1;
1814 gp = game[gindex];
1815 gp->hp = gp->history;
1819 * FIXME find across multiple games.
1821 static int find_move_exp(GAME g, regex_t r, int which, int count)
1823 int i;
1824 int ret;
1825 char errbuf[255];
1826 int incr;
1827 int found;
1829 incr = (which == 0) ? -1 : 1;
1831 for (i = g->hindex + incr - 1, found = 0; ; i += incr) {
1832 if (i == g->hindex - 1)
1833 break;
1835 if (i >= pgn_history_total(g->hp))
1836 i = 0;
1837 else if (i < 0)
1838 i = pgn_history_total(g->hp) - 1;
1840 // FIXME RAV
1841 ret = regexec(&r, g->hp[i]->move, 0, 0, 0);
1843 if (ret == 0) {
1844 if (count == ++found) {
1845 return i + 1;
1848 else {
1849 if (ret != REG_NOMATCH) {
1850 regerror(ret, &r, errbuf, sizeof(errbuf));
1851 cmessage(_("Error Matching Regular Expression"), _("[ press any key to continue ]"), "%s", errbuf);
1852 return -1;
1857 return -1;
1860 static int toggle_delete_flag(int n)
1862 int i, x;
1863 struct userdata_s *d = game[n]->data;
1865 TOGGLE_FLAG(d->flags, CF_DELETE);
1866 gindex = n;
1868 for (i = x = 0; i < gtotal; i++) {
1869 d = game[i]->data;
1871 if (TEST_FLAG(d->flags, CF_DELETE))
1872 x++;
1875 if (x == gtotal) {
1876 cmessage(NULL, _("[ press any key to continue ]"), "%s", _("Cannot delete last game."));
1877 d = game[n]->data;
1878 CLEAR_FLAG(d->flags, CF_DELETE);
1879 return 1;
1882 return 0;
1885 static int find_game_exp(char *str, int which, int count)
1887 char *nstr = NULL, *exp = NULL;
1888 regex_t nexp, vexp;
1889 int ret = -1;
1890 int g = 0;
1891 char buf[255] = {0}, *tmp;
1892 char errbuf[255];
1893 int found = 0;
1894 int incr = (which == 0) ? -(1) : 1;
1896 strncpy(buf, str, sizeof(buf)-1);
1897 tmp = buf;
1899 if (strstr(tmp, ":") != NULL) {
1900 nstr = strsep(&tmp, ":");
1902 if ((ret = regcomp(&nexp, nstr,
1903 REG_ICASE|REG_EXTENDED|REG_NOSUB)) != 0) {
1904 regerror(ret, &nexp, errbuf, sizeof(errbuf));
1905 cmessage(_("Error Compiling Regular Expression"), _("[ press any key to continue ]"), "%s", errbuf);
1906 ret = g = -1;
1907 goto cleanup;
1911 exp = tmp;
1913 while (*exp && isspace(*exp))
1914 exp++;
1916 if (exp == NULL)
1917 goto cleanup;
1919 if ((ret = regcomp(&vexp, exp, REG_EXTENDED|REG_NOSUB)) != 0) {
1920 regerror(ret, &vexp, errbuf, sizeof(errbuf));
1921 cmessage(_("Error Compiling Regular Expression"), _("[ press any key to continue ]"), "%s", errbuf);
1922 ret = -1;
1923 goto cleanup;
1926 ret = -1;
1928 for (g = gindex + incr, found = 0; ; g += incr) {
1929 int t;
1931 if (g == gtotal)
1932 g = 0;
1933 else if (g < 0)
1934 g = gtotal - 1;
1936 if (g == gindex)
1937 break;
1939 for (t = 0; game[g]->tag[t]; t++) {
1940 if (nstr) {
1941 if (regexec(&nexp, game[g]->tag[t]->name, 0, 0, 0) == 0) {
1942 if (regexec(&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0) {
1943 if (count == ++found) {
1944 ret = g;
1945 goto cleanup;
1950 else {
1951 if (regexec(&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0) {
1952 if (count == ++found) {
1953 ret = g;
1954 goto cleanup;
1960 ret = -1;
1963 cleanup:
1964 if (nstr)
1965 regfree(&nexp);
1967 if (g != -1)
1968 regfree(&vexp);
1970 return ret;
1974 * Updates the notification line in the status window then refreshes the
1975 * status window.
1977 void update_status_notify(GAME g, char *fmt, ...)
1979 va_list ap;
1980 #ifdef HAVE_VASPRINTF
1981 char *line;
1982 #else
1983 char line[COLS];
1984 #endif
1986 if (!fmt) {
1987 if (status.notify) {
1988 free(status.notify);
1989 status.notify = NULL;
1992 return;
1995 va_start(ap, fmt);
1996 #ifdef HAVE_VASPRINTF
1997 vasprintf(&line, fmt, ap);
1998 #else
1999 vsnprintf(line, sizeof(line), fmt, ap);
2000 #endif
2001 va_end(ap);
2003 if (status.notify)
2004 free(status.notify);
2006 status.notify = str_to_wchar(line);
2008 #ifdef HAVE_VASPRINTF
2009 free(line);
2010 #endif
2013 int rav_next_prev(GAME g, BOARD b, int n)
2015 // Next RAV.
2016 if (n) {
2017 if ((!g->ravlevel && g->hindex && g->hp[g->hindex - 1]->rav == NULL) ||
2018 (!g->ravlevel && !g->hindex && g->hp[g->hindex]->rav == NULL) ||
2019 (g->ravlevel && g->hp[g->hindex]->rav == NULL))
2020 return 1;
2022 g->rav = Realloc(g->rav, (g->ravlevel + 1) * sizeof(RAV));
2023 g->rav[g->ravlevel].hp = g->hp;
2024 g->rav[g->ravlevel].flags = g->flags;
2025 g->rav[g->ravlevel].fen = pgn_game_to_fen(g, b);
2026 g->rav[g->ravlevel].hindex = g->hindex;
2027 g->hp = (!g->ravlevel) ? (g->hindex) ? g->hp[g->hindex - 1]->rav : g->hp[g->hindex]->rav : g->hp[g->hindex]->rav;
2028 g->hindex = 0;
2029 g->ravlevel++;
2030 pgn_board_update(g, b, g->hindex + 1);
2031 return 0;
2034 if (g->ravlevel - 1 < 0)
2035 return 1;
2037 // Previous RAV.
2038 g->ravlevel--;
2039 pgn_board_init_fen(g, b, g->rav[g->ravlevel].fen);
2040 free(g->rav[g->ravlevel].fen);
2041 g->hp = g->rav[g->ravlevel].hp;
2042 g->flags = g->rav[g->ravlevel].flags;
2043 g->hindex = g->rav[g->ravlevel].hindex;
2044 return 0;
2047 static void draw_window_decor()
2049 move_panel(boardp, 0,
2050 (config.boardleft) ? 0 : COLS - BOARD_WIDTH);
2051 move_panel(historyp, LINES - HISTORY_HEIGHT,
2052 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH : 0 :
2053 (MEGA_BOARD) ? 0 : COLS - HISTORY_WIDTH);
2054 move_panel(statusp, 0,
2055 (config.boardleft) ? BOARD_WIDTH : 0);
2056 move_panel(tagp, STATUS_HEIGHT + 1,
2057 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH :
2058 HISTORY_WIDTH : 0);
2060 wbkgd(boardw, CP_BOARD_WINDOW);
2061 wbkgd(statusw, CP_STATUS_WINDOW);
2062 window_draw_title(statusw, _("Game Status"), STATUS_WIDTH,
2063 CP_STATUS_TITLE, CP_STATUS_BORDER);
2064 wbkgd(tagw, CP_TAG_WINDOW);
2065 window_draw_title(tagw, _("Roster Tags"), TAG_WIDTH, CP_TAG_TITLE,
2066 CP_TAG_BORDER);
2067 wbkgd(historyw, CP_HISTORY_WINDOW);
2068 window_draw_title(historyw, _("Move History"), HISTORY_WIDTH,
2069 CP_HISTORY_TITLE, CP_HISTORY_BORDER);
2072 #ifdef HAVE_WRESIZE
2073 static void do_window_resize()
2075 if (LINES < 24 || COLS < 80)
2076 return;
2078 resizeterm(LINES, COLS);
2079 wresize(boardw, BOARD_HEIGHT, BOARD_WIDTH);
2080 wresize(historyw, HISTORY_HEIGHT, HISTORY_WIDTH);
2081 wresize(statusw, STATUS_HEIGHT, STATUS_WIDTH);
2082 wresize(tagw, TAG_HEIGHT, TAG_WIDTH);
2083 wmove(boardw, 0, 0);
2084 wclrtobot(boardw);
2085 wmove(historyw, 0, 0);
2086 wclrtobot(historyw);
2087 wmove(tagw, 0, 0);
2088 wclrtobot(tagw);
2089 wmove(statusw, 0, 0);
2090 wclrtobot(statusw);
2091 draw_window_decor();
2092 update_all(gp);
2094 #endif
2096 void stop_clock()
2098 memset(&clock_timer, 0, sizeof(struct itimerval));
2099 setitimer(ITIMER_REAL, &clock_timer, NULL);
2102 void start_clock(GAME g)
2104 struct userdata_s *d = g->data;
2106 if (clock_timer.it_interval.tv_usec)
2107 return;
2109 memset(&d->elapsed, 0, sizeof(struct timeval));
2110 clock_timer.it_value.tv_sec = 0;
2111 clock_timer.it_value.tv_usec = 100000;
2112 clock_timer.it_interval.tv_sec = 0;
2113 clock_timer.it_interval.tv_usec = 100000;
2114 setitimer(ITIMER_REAL, &clock_timer, NULL);
2117 static void update_clocks()
2119 int i;
2120 struct userdata_s *d;
2121 struct itimerval it;
2122 int update = 0;
2124 getitimer(ITIMER_REAL, &it);
2126 for (i = 0; i < gtotal; i++) {
2127 d = game[i]->data;
2129 if (d && d->mode == MODE_PLAY) {
2130 if (d->paused == 1 || TEST_FLAG(d->flags, CF_NEW))
2131 continue;
2132 else if (d->paused == -1) {
2133 if (game[i]->side == game[i]->turn) {
2134 d->paused = 1;
2135 continue;
2139 update_clock(game[i], it);
2141 if (game[i] == gp)
2142 update = 1;
2146 if (update) {
2147 update_status_window(gp);
2148 update_panels();
2149 doupdate();
2153 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2155 static int parse_clock_time(char **str)
2157 char *p = *str;
2158 int n = 0, t = 0;
2160 SKIP_SPACE(p);
2162 if (!isdigit(*p))
2163 return -1;
2165 while (*p) {
2166 if (isdigit(*p)) {
2167 t = atoi(p);
2169 while (isdigit(*p))
2170 p++;
2172 continue;
2175 switch (*p) {
2176 case 'H':
2177 case 'h':
2178 n += t * (60 * 60);
2179 t = 0;
2180 break;
2181 case 'M':
2182 case 'm':
2183 n += t * 60;
2184 t = 0;
2185 break;
2186 case 'S':
2187 case 's':
2188 n += t;
2189 t = 0;
2190 break;
2191 case ' ':
2192 p++;
2193 case '/':
2194 case '+':
2195 goto done;
2196 default:
2197 *str = p;
2198 return -1;
2201 p++;
2204 done:
2205 n += t;
2206 *str = p;
2207 return n;
2210 static int parse_clock_input(struct clock_s *clk, char *str, int *incr)
2212 char *p = str;
2213 long n = 0;
2214 int plus = 0;
2215 int m = 0;
2216 int tc = 0;
2218 SKIP_SPACE(p);
2220 if (!*p)
2221 return 0;
2223 if (*p == '+') {
2224 plus = 1;
2225 p++;
2226 SKIP_SPACE(p);
2228 if (*p == '+')
2229 goto move_incr;
2231 else
2232 memset(clk, 0, sizeof(struct clock_s));
2234 again:
2235 /* Sudden death. */
2236 if (strncasecmp(p, "SD", 2) == 0) {
2237 n = 0;
2238 p += 2;
2239 goto tc;
2242 n = parse_clock_time(&p);
2244 if (n == -1)
2245 return 1;
2247 if (!n)
2248 goto done;
2250 /* Time control. */
2252 if (*p == '/') {
2253 if (plus)
2254 return 1;
2256 /* Sudden death without a previous time control. */
2257 if (!n && !tc)
2258 return 1;
2260 m = n;
2261 p++;
2262 n = parse_clock_time(&p);
2264 if (n == -1)
2265 return 1;
2267 if (tc >= MAX_TC) {
2268 message(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s (%i)", _("Maximum number of time controls reached"), MAX_TC);
2269 return 1;
2272 clk->tc[tc][0] = m;
2273 clk->tc[tc++][1] = n;
2274 SKIP_SPACE(p);
2276 if (*p == '+')
2277 goto move_incr;
2279 if (*p)
2280 goto again;
2282 goto done;
2285 if (plus)
2286 *incr = n;
2287 else
2288 clk->tc[clk->tcn][1] = (n <= clk->elapsed.tv_sec) ? clk->elapsed.tv_sec + n : n;
2290 move_incr:
2291 if (*p) {
2292 if (*p++ == '+') {
2293 if (!isdigit(*p))
2294 return 1;
2296 n = parse_clock_time(&p);
2298 if (n == -1 || *p)
2299 return 1;
2301 clk->incr = n;
2303 SKIP_SPACE(p);
2305 if (*p)
2306 return 1;
2308 else
2309 return 1;
2312 done:
2313 return 0;
2316 static int parse_which_clock(struct clock_s *clk, char *str)
2318 struct clock_s tmp;
2319 int incr = 0;
2321 memcpy(&tmp, clk, sizeof(struct clock_s));
2323 if (parse_clock_input(&tmp, str, &incr)) {
2324 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), _("Invalid clock specification"));
2325 return 1;
2328 memcpy(clk, &tmp, sizeof(struct clock_s));
2329 clk->tc[clk->tcn][1] += incr;
2330 return 0;
2333 void do_clock_input_finalize(WIN *win)
2335 struct userdata_s *d = gp->data;
2336 struct input_data_s *in = win->data;
2337 char *p = in->str;
2339 if (!in->str) {
2340 free(in);
2341 return;
2344 SKIP_SPACE(p);
2346 if (tolower(*p) == 'w') {
2347 p++;
2349 if (parse_which_clock(&d->wclock, p))
2350 goto done;
2352 else if (tolower(*p) == 'b') {
2353 p++;
2355 if (parse_which_clock(&d->bclock, p))
2356 goto done;
2358 else {
2359 if (parse_which_clock(&d->wclock, p))
2360 goto done;
2362 if (parse_which_clock(&d->bclock, p))
2363 goto done;
2366 if (!d->wclock.tc[0][1] && !d->bclock.tc[0][1])
2367 CLEAR_FLAG(d->flags, CF_CLOCK);
2368 else
2369 SET_FLAG(d->flags, CF_CLOCK);
2371 done:
2372 free(in->str);
2373 free(in);
2376 void do_engine_command_finalize(WIN *win)
2378 struct userdata_s *d = gp->data;
2379 struct input_data_s *in = win->data;
2380 int x;
2382 if (!in->str) {
2383 free(in);
2384 return;
2387 if (!d->engine)
2388 goto done;
2390 x = d->engine->status;
2391 send_to_engine(gp, -1, "%s\n", in->str);
2392 d->engine->status = x;
2394 done:
2395 free(in->str);
2396 free(in);
2399 void do_board_details()
2401 config.details = (config.details) ? 0 : 1;
2404 void do_toggle_strict_castling()
2406 int n;
2408 pgn_config_get(PGN_STRICT_CASTLING, &n);
2410 if (n == 0)
2411 pgn_config_set(PGN_STRICT_CASTLING, 1);
2412 else
2413 pgn_config_set(PGN_STRICT_CASTLING, 0);
2416 void do_play_set_clock()
2418 struct input_data_s *in;
2420 in = Calloc(1, sizeof(struct input_data_s));
2421 in->efunc = do_clock_input_finalize;
2422 construct_input(_("Set Clock"), NULL, 1, 1,
2423 _ ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n" \
2424 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2425 NULL, NULL, 0, in, INPUT_HIST_CLOCK, -1);
2428 void do_play_toggle_human()
2430 struct userdata_s *d = gp->data;
2432 TOGGLE_FLAG(d->flags, CF_HUMAN);
2434 if (!TEST_FLAG(d->flags, CF_HUMAN) && pgn_history_total(gp->hp)) {
2435 if (init_chess_engine(gp))
2436 return;
2439 CLEAR_FLAG(d->flags, CF_ENGINE_LOOP);
2441 if (d->engine)
2442 d->engine->status = ENGINE_READY;
2445 void do_play_toggle_engine()
2447 struct userdata_s *d = gp->data;
2449 TOGGLE_FLAG(d->flags, CF_ENGINE_LOOP);
2450 CLEAR_FLAG(d->flags, CF_HUMAN);
2452 if (d->engine && TEST_FLAG(d->flags, CF_ENGINE_LOOP)) {
2453 char *fen = pgn_game_to_fen (gp, d->b);
2455 pgn_board_update(gp, d->b,
2456 pgn_history_total(gp->hp));
2457 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
2458 free (fen);
2463 * This will send a command to the engine skipping the command queue.
2465 void do_play_send_command()
2467 struct userdata_s *d = gp->data;
2468 struct input_data_s *in;
2470 if (!d->engine || d->engine->status == ENGINE_OFFLINE) {
2471 if (init_chess_engine(gp))
2472 return;
2475 in = Calloc(1, sizeof(struct input_data_s));
2476 in->efunc = do_engine_command_finalize;
2477 construct_input(_("Engine Command"), NULL, 1, 1, NULL, NULL, NULL, 0, in, INPUT_HIST_ENGINE, -1);
2480 void do_play_switch_turn()
2482 struct userdata_s *d = gp->data;
2484 pgn_switch_side(gp);
2485 pgn_switch_turn(gp);
2487 if (!TEST_FLAG(d->flags, CF_HUMAN))
2488 add_engine_command(gp, -1,
2489 (gp->side == WHITE) ? "white\n" : "black\n");
2491 update_status_window(gp);
2494 void do_play_toggle_eh_mode()
2496 struct userdata_s *d = gp->data;
2498 if (!TEST_FLAG(d->flags, CF_HUMAN)) {
2499 if (!gp->hindex){
2500 pgn_switch_side(gp, TRUE);
2501 d->play_mode = (d->play_mode) ? PLAY_HE : PLAY_EH;
2502 if (gp->side == BLACK)
2503 update_status_notify(gp, _("Press 'g' to start the game"));
2505 rotate = (rotate) ? FALSE : TRUE;
2507 else
2508 message(NULL, _("[ press any key to continue ]"),
2509 _("You may only switch sides at the start of the \n"
2510 "game. Press ^K or ^N to begin a new game."));
2514 void do_play_undo()
2516 struct userdata_s *d = gp->data;
2518 if (!pgn_history_total(gp->hp))
2519 return;
2521 if (keycount) {
2522 if (gp->hindex - keycount < 0)
2523 gp->hindex = 0;
2524 else {
2525 if (go_move)
2526 gp->hindex -= (keycount * 2) -1;
2527 else
2528 gp->hindex -= keycount * 2;
2531 else {
2532 if (gp->hindex - 2 < 0)
2533 gp->hindex = 0;
2534 else {
2535 if (go_move)
2536 gp->hindex -= 1;
2537 else
2538 gp->hindex -= 2;
2542 pgn_history_free(gp->hp, gp->hindex);
2543 gp->hindex = pgn_history_total(gp->hp);
2544 pgn_board_update(gp, d->b, gp->hindex);
2546 if (d->engine && d->engine->status == ENGINE_READY) {
2547 char *fen = pgn_game_to_fen(gp, d->b);
2549 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
2550 free (fen);
2551 d->engine->status = ENGINE_READY;
2554 update_history_window(gp);
2556 if (go_move) {
2557 pgn_switch_side(gp, FALSE);
2558 go_move--;
2562 void do_play_toggle_pause()
2564 struct userdata_s *d = gp->data;
2566 if (!TEST_FLAG(d->flags, CF_HUMAN) && gp->turn !=
2567 gp->side) {
2568 d->paused = -1;
2569 return;
2572 d->paused = (d->paused) ? 0 : 1;
2575 void do_play_go()
2577 struct userdata_s *d = gp->data;
2579 if (TEST_FLAG(d->flags, CF_HUMAN))
2580 return;
2582 if (fm_loaded_file && gp->side != gp->turn) {
2583 pgn_switch_side(gp, FALSE);
2584 add_engine_command(gp, ENGINE_THINKING, "black\n");
2587 add_engine_command(gp, ENGINE_THINKING, "go\n");
2589 // Completa la función para que permita seguir jugando al usarla.
2590 // Complete the function to allow continue playing when using.
2591 if (gp->side == gp->turn)
2592 pgn_switch_side(gp, FALSE);
2594 go_move++;
2597 void do_play_config_command()
2599 int x, w;
2601 if (config.keys) {
2602 for (x = 0; config.keys[x]; x++) {
2603 if (config.keys[x]->c == input_c) {
2604 switch (config.keys[x]->type) {
2605 case KEY_DEFAULT:
2606 add_engine_command(gp, -1, "%s\n",
2607 config.keys[x]->str);
2608 break;
2609 case KEY_SET:
2610 if (!keycount)
2611 break;
2613 add_engine_command(gp, -1,
2614 "%s %i\n", config.keys[x]->str, keycount);
2615 keycount = 0;
2616 break;
2617 case KEY_REPEAT:
2618 if (!keycount)
2619 break;
2621 for (w = 0; w < keycount; w++)
2622 add_engine_command(gp, -1,
2623 "%s\n", config.keys[x]->str);
2624 keycount = 0;
2625 break;
2631 update_status_notify(gp, NULL);
2634 void do_play_cancel_selected()
2636 struct userdata_s *d = gp->data;
2638 d->sp.icon = d->sp.srow = d->sp.scol = 0;
2639 keycount = 0;
2640 pgn_reset_valid_moves(d->b);
2641 update_status_notify(gp, NULL);
2644 void do_play_commit()
2646 struct userdata_s *d = gp->data;
2648 pushkey = keycount = 0;
2649 update_status_notify(gp, NULL);
2651 if (!TEST_FLAG(d->flags, CF_HUMAN) &&
2652 (!d->engine || d->engine->status == ENGINE_THINKING))
2653 return;
2655 if (!d->sp.icon)
2656 return;
2658 d->sp.row = d->c_row;
2659 d->sp.col = d->c_col;
2661 if (rotate) {
2662 rotate_position(SP_POSITION);
2663 rotate_position(SPS_POSITION);
2666 move_to_engine(gp);
2668 // Completa la función para que permita seguir jugando cuando se carga un
2669 // archivo pgn (con juego no terminado) que inicie con turno del lado
2670 // negro.
2671 // Complete the function to allow continue playing when loading a file
2672 // pgn (with unfinished game) you start to turn black side.
2673 if (gp->side != gp->turn)
2674 pgn_switch_side(gp, FALSE);
2676 if (rotate && d->sp.icon)
2677 rotate_position(SPS_POSITION);
2679 // Envia comando 'go' a Polyglot en el primer movimiento debido a que
2680 // polyglot no envia el movimiento y cboard se queda esperando.
2681 // Send command 'go' to the first movement Polyglot because cboard
2682 // waits to send polyglot movement and this does not make.
2683 if (config.fmpolyglot &&
2684 ((gp->side == WHITE && !gp->hindex) || fm_loaded_file))
2685 add_engine_command(gp, ENGINE_THINKING, "go\n");
2687 go_move = 0;
2688 fm_loaded_file = FALSE;
2691 void do_play_select()
2693 struct userdata_s *d = gp->data;
2695 if (!TEST_FLAG(d->flags, CF_HUMAN) && (!d->engine ||
2696 d->engine->status == ENGINE_OFFLINE)) {
2697 if (init_chess_engine(gp))
2698 return;
2701 if (d->engine && d->engine->status == ENGINE_THINKING)
2702 return;
2704 if (d->sp.icon)
2705 do_play_cancel_selected ();
2707 if (rotate)
2708 rotate_position(CURSOR_POSITION);
2710 d->sp.icon = d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon;
2712 if (pgn_piece_to_int(d->sp.icon) == OPEN_SQUARE) {
2713 d->sp.icon = 0;
2714 return;
2717 if (((islower(d->sp.icon) && gp->turn != BLACK)
2718 || (isupper(d->sp.icon) && gp->turn != WHITE))) {
2719 message(NULL, _("[ press any key to continue ]"), "%s", _("It is not your turn to move. You can switch sides "));
2720 d->sp.icon = 0;
2721 return;
2722 #if 0
2723 if (pgn_history_total(gp->hp)) {
2724 message(NULL, _("[ press any key to continue ]"), "%s", _("It is not your turn to move. You can switch sides "));
2725 d->sp.icon = 0;
2726 return;
2728 else {
2729 if (pgn_tag_find(gp->tag, "FEN") != E_PGN_ERR)
2730 return;
2732 add_engine_command(gp, ENGINE_READY, "black\n");
2733 pgn_switch_turn(gp);
2735 if (gp->side != BLACK)
2736 pgn_switch_side(gp);
2738 #endif
2741 d->sp.srow = d->c_row;
2742 d->sp.scol = d->c_col;
2744 if (config.validmoves)
2745 pgn_find_valid_moves(gp, d->b, d->sp.scol, d->sp.srow);
2747 if (rotate) {
2748 rotate_position(CURSOR_POSITION);
2749 rotate_position(SPS_POSITION);
2752 CLEAR_FLAG(d->flags, CF_NEW);
2753 start_clock(gp);
2756 /* FIXME: keys with the same function should comma deliminated. */
2757 static char *build_help(struct key_s **keys)
2759 int i, nlen = 1, len, t, n;
2760 char *buf = NULL;
2761 char *p;
2763 if (!keys)
2764 return NULL;
2766 for (i = len = t = 0; keys[i]; i++) {
2767 if (!keys[i]->d)
2768 continue;
2770 if (keys[i]->key) {
2771 if (strlen(keys[i]->key) > nlen) {
2772 nlen = strlen(keys[i]->key);
2773 t += nlen;
2775 else
2776 t++;
2779 if (keys[i]->d) {
2780 if (strlen(keys[i]->d) > len)
2781 len = strlen(keys[i]->d);
2784 t += len;
2785 t += keys[i]->r;
2788 t += 4 + i;
2789 buf = Malloc(t);
2790 p = buf;
2792 for (i = 0; keys[i]; i++) {
2793 if (!keys[i]->d)
2794 continue;
2796 if (keys[i]->key)
2797 n = strlen(keys[i]->key);
2798 else
2799 n = 1;
2801 while (n++ <= nlen)
2802 *p++ = ' ';
2804 *p = 0;
2806 if (keys[i]->key) {
2807 strcat(buf, keys[i]->key);
2808 p = buf + strlen(buf);
2810 else
2811 *p++ = keys[i]->c;
2813 *p++ = ' ';
2814 *p++ = '-';
2815 *p++ = ' ';
2816 *p = 0;
2818 if (keys[i]->d)
2819 strcat(buf, keys[i]->d);
2821 if (keys[i]->r)
2822 strcat(buf, "*");
2824 strcat(buf, "\n");
2825 p = buf + strlen(buf);
2828 return buf;
2831 void do_more_help(WIN *);
2832 void do_main_help(WIN *win)
2834 char *buf;
2836 switch (win->c) {
2837 case 'p':
2838 buf = build_help(play_keys);
2839 construct_message(_("Play Mode Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0,
2840 NULL, NULL, buf, do_more_help, 0, 1, "%s", buf);
2841 break;
2842 case 'h':
2843 buf = build_help(history_keys);
2844 construct_message(_("History Mode Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0,
2845 NULL, NULL, buf, do_more_help, 0, 1, "%s", buf);
2846 break;
2847 case 'e':
2848 buf = build_help(edit_keys);
2849 construct_message(_("Edit Mode Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0,
2850 NULL, NULL, buf, do_more_help, 0, 1, "%s", buf);
2851 break;
2852 case 'g':
2853 buf = build_help(global_keys);
2854 construct_message(_("Global Game Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0,
2855 NULL, NULL, buf, do_more_help, 0, 1, "%s", buf);
2856 break;
2857 default:
2858 break;
2862 void do_more_help(WIN *win)
2864 if (win->c == KEY_F(1) || win->c == CTRL_KEY('g'))
2865 construct_message(_("Command Key Index"),
2866 _ ("p/h/e/g or any other key to quit"), 0, 0,
2867 NULL, NULL, NULL, do_main_help, 0, 0, "%s",
2869 "p - play mode keys\n"
2870 "h - history mode keys\n"
2871 "e - board edit mode keys\n"
2872 "g - global game keys"
2876 void do_play_help()
2878 char *buf = build_help(play_keys);
2880 construct_message(_("Play Mode Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0, NULL, NULL, buf,
2881 do_more_help, 0, 1, "%s", buf);
2884 void do_play_history_mode()
2886 struct userdata_s *d = gp->data;
2888 if (!pgn_history_total(gp->hp) ||
2889 (d->engine && d->engine->status == ENGINE_THINKING))
2890 return;
2892 d->mode = MODE_HISTORY;
2893 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
2896 void do_play_edit_mode()
2898 struct userdata_s *d = gp->data;
2900 if (pgn_history_total(gp->hp))
2901 return;
2903 pgn_board_init_fen(gp, d->b, NULL);
2904 config.details++;
2905 d->mode = MODE_EDIT;
2908 void do_edit_insert_finalize(WIN *win)
2910 struct userdata_s *d = win->data;
2912 if (pgn_piece_to_int(win->c) == -1)
2913 return;
2915 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon = win->c;
2918 void do_edit_select()
2920 struct userdata_s *d = gp->data;
2922 if (d->sp.icon)
2923 return;
2925 d->sp.icon = d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon;
2927 if (pgn_piece_to_int(d->sp.icon) == OPEN_SQUARE) {
2928 d->sp.icon = 0;
2929 return;
2932 d->sp.srow = d->c_row;
2933 d->sp.scol = d->c_col;
2936 void do_edit_commit()
2938 int p;
2939 struct userdata_s *d = gp->data;
2941 pushkey = keycount = 0;
2942 update_status_notify(gp, NULL);
2944 if (!d->sp.icon)
2945 return;
2947 d->sp.row = d->c_row;
2948 d->sp.col = d->c_col;
2949 p = d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon;
2950 d->b[RANKTOBOARD(d->sp.row)][FILETOBOARD(d->sp.col)].icon = p;
2951 d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon =
2952 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
2953 d->sp.icon = d->sp.srow = d->sp.scol = 0;
2956 void do_edit_delete()
2958 struct userdata_s *d = gp->data;
2960 if (d->sp.icon)
2961 d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon =
2962 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
2963 else
2964 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon =
2965 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
2967 d->sp.icon = d->sp.srow = d->sp.scol = 0;
2970 void do_edit_cancel_selected()
2972 struct userdata_s *d = gp->data;
2974 d->sp.icon = d->sp.srow = d->sp.scol = 0;
2975 keycount = 0;
2976 update_status_notify(gp, NULL);
2979 void do_edit_switch_turn()
2981 pgn_switch_turn(gp);
2984 void do_edit_toggle_castle()
2986 struct userdata_s *d = gp->data;
2988 castling_state(gp, d->b, RANKTOBOARD(d->c_row),
2989 FILETOBOARD(d->c_col),
2990 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon, 1);
2993 void do_edit_insert()
2995 struct userdata_s *d = gp->data;
2997 construct_message(_("Insert Piece"), _("P=pawn, R=rook, N=knight, B=bishop, "), 0, 0, NULL, NULL,
2998 d->b, do_edit_insert_finalize, 0, 0, "%s", _("Type the piece letter to insert. Lowercase "));
3001 void do_edit_enpassant()
3003 struct userdata_s *d = gp->data;
3005 if (d->c_row == 6 || d->c_row == 3) {
3006 pgn_reset_enpassant(d->b);
3007 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].enpassant = 1;
3011 void do_edit_help()
3013 char *buf = build_help(edit_keys);
3015 construct_message(_("Edit Mode Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0, NULL, NULL, buf,
3016 do_more_help, 0, 1, "%s", buf);
3019 void do_edit_exit()
3021 struct userdata_s *d = gp->data;
3022 char *fen = pgn_game_to_fen(gp, d->b);
3024 config.details--;
3025 pgn_tag_add(&gp->tag, "FEN", fen);
3026 free (fen);
3027 pgn_tag_add(&gp->tag, "SetUp", "1");
3028 pgn_tag_sort(gp->tag);
3029 pgn_board_update(gp, d->b, gp->hindex);
3030 d->mode = MODE_PLAY;
3033 void really_do_annotate_finalize(struct input_data_s *in,
3034 struct userdata_s *d)
3036 HISTORY *h = in->data;
3037 int len;
3039 if (!in->str) {
3040 if (h->comment) {
3041 free(h->comment);
3042 h->comment = NULL;
3045 else {
3046 len = strlen(in->str);
3047 h->comment = Realloc(h->comment, len+1);
3048 strncpy(h->comment, in->str, len);
3049 h->comment[len] = 0;
3052 free(in->str);
3053 free(in);
3054 SET_FLAG(d->flags, CF_MODIFIED);
3057 void do_annotate_finalize(WIN *win)
3059 struct userdata_s *d = gp->data;
3060 struct input_data_s *in = win->data;
3062 really_do_annotate_finalize(in, d);
3065 void do_find_move_exp_finalize(int init, int which)
3067 int n;
3068 struct userdata_s *d = gp->data;
3069 static int firstrun;
3070 static regex_t r;
3071 int ret;
3072 char errbuf[255];
3074 if (init || !firstrun) {
3075 if (!firstrun)
3076 regfree(&r);
3078 if ((ret = regcomp(&r, moveexp, REG_EXTENDED|REG_NOSUB)) != 0) {
3079 regerror(ret, &r, errbuf, sizeof(errbuf));
3080 cmessage(_("Error Compiling Regular Expression"), _("[ press any key to continue ]"), "%s", errbuf);
3081 return;
3084 firstrun = 1;
3087 if ((n = find_move_exp(gp, r,
3088 (which == -1) ? 0 : 1, (keycount) ? keycount : 1)) == -1)
3089 return;
3091 gp->hindex = n;
3092 pgn_board_update(gp, d->b, gp->hindex);
3095 void do_find_move_exp(WIN *win)
3097 struct input_data_s *in = win->data;
3098 int *n = in->data;
3099 int which = *n;
3101 if (in->str) {
3102 strncpy(moveexp, in->str, sizeof(moveexp)-1);
3103 moveexp[sizeof(moveexp)-1] = 0;
3104 do_find_move_exp_finalize(1, which);
3105 free(in->str);
3108 free(in->data);
3109 free(in);
3112 void do_move_jump_finalize(int n)
3114 struct userdata_s *d = gp->data;
3116 if (n < 0 || n > (pgn_history_total(gp->hp) / 2))
3117 return;
3119 keycount = 0;
3120 update_status_notify(gp, NULL);
3121 gp->hindex = (n) ? n * 2 - 1 : n * 2;
3122 pgn_board_update(gp, d->b, gp->hindex);
3125 void do_move_jump(WIN *win)
3127 struct input_data_s *in = win->data;
3129 if (!in->str || !isinteger(in->str)) {
3130 if (in->str)
3131 free(in->str);
3133 free(in);
3134 return;
3137 do_move_jump_finalize(atoi(in->str));
3138 free(in->str);
3139 free(in);
3142 struct history_menu_s {
3143 char *line;
3144 int hindex;
3145 int ravlevel;
3146 int move;
3147 int indent;
3150 void free_history_menu_data(struct history_menu_s **h)
3152 int i;
3154 if (!h)
3155 return;
3157 for (i = 0; h[i]; i++) {
3158 free(h[i]->line);
3159 free(h[i]);
3162 free(h);
3165 void get_history_data(HISTORY **hp, struct history_menu_s ***menu, int m,
3166 int turn)
3168 int i, n = 0;
3169 int t = pgn_history_total(hp);
3170 char buf[MAX_SAN_MOVE_LEN + 4];
3171 static int depth;
3172 struct history_menu_s **hmenu = *menu;
3174 if (hmenu)
3175 for (n = 0; hmenu[n]; n++);
3176 else
3177 depth = 0;
3179 for (i = 0; i < t; i++) {
3180 hmenu = Realloc(hmenu, (n + 2) * sizeof(struct history_menu_s *));
3181 hmenu[n] = Malloc(sizeof(struct history_menu_s));
3182 snprintf(buf, sizeof(buf), "%c%s%s", (turn == WHITE) ? 'W' : 'B',
3183 hp[i]->move, (hp[i]->comment || hp[i]->nag[0]) ? " !" : "");
3184 hmenu[n]->line = strdup(buf);
3185 hmenu[n]->hindex = i;
3186 hmenu[n]->indent = 0;
3187 hmenu[n]->ravlevel = depth;
3188 hmenu[n]->move = (n && depth > hmenu[n-1]->ravlevel) ? m++ : m;
3189 n++;
3190 hmenu[n] = NULL;
3192 #if 0
3193 if (hp[i]->rav) {
3194 depth++;
3195 get_history_data(hp[i]->rav, &hmenu, m, turn);
3196 for (n = 0; hmenu[n]; n++);
3197 depth--;
3199 if (depth)
3200 m--;
3202 #endif
3204 turn = (turn == WHITE) ? BLACK : WHITE;
3207 *menu = hmenu;
3210 void history_draw_update(struct menu_input_s *m)
3212 GAME g = m->data;
3213 struct userdata_s *d = g->data;
3215 g->hindex = m->selected + 1;
3216 update_cursor(g, m->selected);
3217 pgn_board_update(g, d->b, m->selected + 1);
3220 struct menu_item_s **get_history_items(WIN *win)
3222 struct menu_input_s *m = win->data;
3223 GAME g = m->data;
3224 struct userdata_s *d = g->data;
3225 struct history_menu_s **hm = d->data;
3226 struct menu_item_s **items = m->items;
3227 int i;
3229 if (!hm) {
3230 get_history_data(g->history, &hm, 0,
3231 TEST_FLAG(g->flags, GF_BLACK_OPENING));
3232 m->selected = g->hindex - 1;
3234 if (m->selected < 0)
3235 m->selected = 0;
3237 m->draw_exit_func = history_draw_update;
3240 d->data = hm;
3242 if (items) {
3243 for (i = 0; items[i]; i++)
3244 free(items[i]);
3246 free(items);
3247 items = NULL;
3250 for (i = 0; hm[i]; i++) {
3251 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
3252 items[i] = Malloc(sizeof(struct menu_item_s));
3253 items[i]->name = hm[i]->line;
3254 items[i]->value = NULL;
3255 items[i]->selected = 0;
3258 if (items)
3259 items[i] = NULL;
3261 m->nofree = 1;
3262 m->items = items;
3263 return items;
3266 void history_menu_quit(struct menu_input_s *m)
3268 pushkey = -1;
3271 void history_menu_exit(WIN *win)
3273 GAME g = win->data;
3274 struct userdata_s *d = g->data;
3275 struct history_menu_s **hm = d->data;
3276 int i;
3278 if (!hm)
3279 return;
3281 for (i = 0; hm[i]; i++) {
3282 free(hm[i]->line);
3283 free(hm[i]);
3286 free(hm);
3287 d->data = NULL;
3290 // FIXME RAV
3291 void history_menu_next(struct menu_input_s *m)
3293 GAME g = m->data;
3294 struct userdata_s *d = g->data;
3295 struct history_menu_s **hm = d->data;
3296 int n, t;
3298 for (t = 0; hm[t]; t++);
3300 if (m->selected + 1 == t)
3301 n = 0;
3302 else
3303 n = hm[m->selected + 1]->hindex;
3305 n++;
3306 g->hindex = n;
3309 // FIXME RAV
3310 void history_menu_prev(struct menu_input_s *m)
3312 GAME g = m->data;
3313 struct userdata_s *d = g->data;
3314 struct history_menu_s **hm = d->data;
3315 int n, t;
3317 for (t = 0; hm[t]; t++);
3319 if (m->selected - 1 < 0)
3320 n = t - 1;
3321 else
3322 n = hm[m->selected - 1]->hindex;
3324 n++;
3325 g->hindex = n;
3328 void history_menu_help(struct menu_input_s *m)
3330 message("History Menu Help", _("[ press any key to continue ]"), "%s",
3332 " UP/DOWN - previous/next menu item\n"
3333 " HOME/END - first/last menu item\n"
3334 " PGDN/PGUP - next/previous page\n"
3335 " a-zA-Z0-9 - jump to item\n"
3336 " CTRL-a - annotate the selected move\n"
3337 " ENTER - view annotation\n"
3338 " CTRL-d - toggle board details\n"
3339 " ESCAPE/M - return to move history"
3343 void do_annotate_move(HISTORY *hp)
3345 char buf[COLS - 4];
3346 struct input_data_s *in;
3348 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Editing Annotation for"), hp->move);
3349 in = Calloc(1, sizeof(struct input_data_s));
3350 in->data = hp;
3351 in->efunc = do_annotate_finalize;
3352 construct_input(buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3353 _("Type CTRL-t to edit NAG"), edit_nag, NULL, CTRL_KEY('T'), in, -1, -1);
3356 void history_menu_view_annotation(struct menu_input_s *m)
3358 GAME g = m->data;
3360 // FIXME RAV
3361 view_annotation(g->history[m->selected]);
3364 void history_menu_annotate_finalize(WIN *win)
3366 struct input_data_s *in = win->data;
3367 GAME g = in->moredata;
3368 struct userdata_s *d = g->data;
3369 struct history_menu_s **hm = d->data;
3371 really_do_annotate_finalize(in, d);
3372 free_history_menu_data(hm);
3373 hm = NULL;
3374 get_history_data(g->history, &hm, 0, TEST_FLAG(g->flags, GF_BLACK_OPENING));
3375 d->data = hm;
3376 pushkey = REFRESH_MENU;
3379 void history_menu_annotate(struct menu_input_s *m)
3381 GAME g = m->data;
3382 char buf[COLS - 4];
3383 struct input_data_s *in;
3384 HISTORY *hp = g->history[m->selected]; // FIXME RAV
3386 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Editing Annotation for"), hp->move);
3387 in = Calloc(1, sizeof(struct input_data_s));
3388 in->data = hp;
3389 in->moredata = m->data;
3390 in->efunc = history_menu_annotate_finalize;
3391 construct_input(buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3392 _("Type CTRL-t to edit NAG"), edit_nag, NULL, CTRL_KEY('T'), in, -1, -1);
3395 void history_menu_details(struct menu_input_s *m)
3397 do_board_details();
3400 // FIXME RAV
3401 void history_menu_print(WIN *win)
3403 struct menu_input_s *m = win->data;
3404 GAME g = m->data;
3405 struct userdata_s *d = g->data;
3406 struct history_menu_s **hm = d->data;
3407 struct history_menu_s *h = hm[m->top];
3408 int i;
3409 char *p = m->item->name;
3410 int line = m->print_line - 2;
3412 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
3413 * attr_t data type.
3415 #ifdef HAVE_ATTR_T
3416 attr_t attrs;
3417 short pair;
3418 #endif
3419 int total;
3421 for (total = 0; hm[total]; total++);
3422 #ifdef HAVE_ATTR_T
3423 wattr_get(win->w, &attrs, &pair, NULL);
3424 wattroff(win->w, COLOR_PAIR(pair));
3425 #endif
3426 mvwaddch(win->w, m->print_line, 1,
3427 *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));
3428 p++;
3430 if (h->hindex == 0 && line == 0)
3431 waddch(win->w, ACS_ULCORNER | CP_HISTORY_MENU_LG);
3432 else if ((!hm[h->hindex + (win->rows - 5) + 1] && line == win->rows - 5) ||
3433 (m->top + line == total - 1))
3434 waddch(win->w, ACS_LLCORNER | CP_HISTORY_MENU_LG);
3435 else if (hm[m->top + 1]->ravlevel != h->ravlevel || !h->ravlevel)
3436 waddch(win->w, ACS_LTEE | CP_HISTORY_MENU_LG);
3437 else
3438 waddch(win->w, ACS_VLINE | CP_HISTORY_MENU_LG);
3440 #ifdef HAVE_ATTR_T
3441 wattron(win->w, COLOR_PAIR(pair) | attrs);
3442 #endif
3444 for (i = 2; *p; p++, i++)
3445 waddch(win->w, (*p == '!') ? *p | A_BOLD : *p);
3447 while (i++ < win->cols - 2)
3448 waddch(win->w, ' ');
3451 void history_menu(GAME g)
3453 struct menu_key_s **keys = NULL;
3455 add_menu_key(&keys, KEY_ESCAPE, history_menu_quit);
3456 add_menu_key(&keys, 'M', history_menu_quit);
3457 add_menu_key(&keys, KEY_UP, history_menu_prev);
3458 add_menu_key(&keys, KEY_DOWN, history_menu_next);
3459 add_menu_key(&keys, KEY_F(1), history_menu_help);
3460 add_menu_key(&keys, CTRL_KEY('a'), history_menu_annotate);
3461 add_menu_key(&keys, CTRL_KEY('d'), history_menu_details);
3462 add_menu_key(&keys, '\n', history_menu_view_annotation);
3463 construct_menu(LINES, TAG_WIDTH, 0, config.boardleft ? BOARD_WIDTH : 0,
3464 _("Move History Tree"), 1, get_history_items, keys, g,
3465 history_menu_print, history_menu_exit);
3468 void do_history_menu()
3470 history_menu(gp);
3473 void do_history_half_move_toggle()
3475 movestep = (movestep == 1) ? 2 : 1;
3476 update_history_window(gp);
3479 void do_history_rotate_board()
3481 rotate = (rotate) ? FALSE : TRUE;
3484 void do_history_jump_next()
3486 struct userdata_s *d = gp->data;
3488 pgn_history_next(gp, d->b, (keycount > 0) ?
3489 config.jumpcount * keycount * movestep :
3490 config.jumpcount * movestep);
3493 void do_history_jump_prev()
3495 struct userdata_s *d = gp->data;
3497 pgn_history_prev(gp, d->b, (keycount) ?
3498 config.jumpcount * keycount * movestep :
3499 config.jumpcount * movestep);
3502 void do_history_prev()
3504 struct userdata_s *d = gp->data;
3506 pgn_history_prev(gp, d->b,
3507 (keycount) ? keycount * movestep : movestep);
3510 void do_history_next()
3512 struct userdata_s *d = gp->data;
3514 pgn_history_next(gp, d->b, (keycount) ?
3515 keycount * movestep : movestep);
3518 void do_history_mode_finalize(struct userdata_s *d)
3520 pushkey = 0;
3521 d->mode = MODE_PLAY;
3524 void do_history_mode_confirm(WIN *win)
3526 struct userdata_s *d = gp->data;
3528 switch (win->c) {
3529 case 'R':
3530 case 'r':
3531 pgn_history_free(gp->hp,
3532 gp->hindex);
3533 pgn_board_update(gp, d->b,
3534 pgn_history_total(gp->hp));
3535 break;
3536 #if 0
3537 case 'C':
3538 case 'c':
3539 if (pgn_history_rav_new(gp, d->b,
3540 gp->hindex) != E_PGN_OK)
3541 return;
3543 break;
3544 #endif
3545 default:
3546 return;
3549 if (!TEST_FLAG(d->flags, CF_HUMAN)) {
3550 char *fen = pgn_game_to_fen(gp, d->b);
3552 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
3553 free (fen);
3556 do_history_mode_finalize(d);
3559 void do_history_toggle()
3561 struct userdata_s *d = gp->data;
3563 // FIXME Resuming from previous history could append to a RAV.
3564 if (gp->hindex != pgn_history_total(gp->hp)) {
3565 if (!pushkey)
3566 construct_message(NULL, _ ("(r)esume or abort"), 0, 1, NULL, NULL,
3567 NULL, do_history_mode_confirm, 0, 0, "%s",
3568 _("Resuming a game from previous "));
3569 return;
3571 else {
3572 if (TEST_FLAG(gp->flags, GF_GAMEOVER))
3573 return;
3576 if (gp->side != gp->turn) {
3577 d->play_mode = PLAY_EH;
3579 else {
3580 d->play_mode = PLAY_HE;
3581 rotate = FALSE;
3584 do_history_mode_finalize(d);
3587 void do_history_annotate()
3589 int n = gp->hindex;
3591 if (n && gp->hp[n - 1]->move)
3592 n--;
3593 else
3594 return;
3596 do_annotate_move(gp->hp[n]);
3599 void do_history_help()
3601 char *buf = build_help(history_keys);
3603 construct_message(_("History Mode Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0, NULL, NULL, buf,
3604 do_more_help, 0, 1, "%s", buf);
3607 void do_history_find(int which)
3609 struct input_data_s *in;
3610 int *p;
3612 if (pgn_history_total(gp->hp) < 2)
3613 return;
3615 in = Calloc(1, sizeof(struct input_data_s));
3616 p = Malloc(sizeof(int));
3617 *p = which;
3618 in->data = p;
3619 in->efunc = do_find_move_exp;
3621 if (!*moveexp || which == 0) {
3622 construct_input(_("Find Move Text Expression"), NULL, 1, 0, NULL, NULL, NULL,
3623 0, in, INPUT_HIST_MOVE_EXP, -1);
3624 return;
3627 do_find_move_exp_finalize(0, which);
3630 void do_history_find_new()
3632 do_history_find(0);
3635 void do_history_find_prev()
3637 do_history_find(-1);
3640 void do_history_find_next()
3642 do_history_find(1);
3645 void do_history_rav(int which)
3647 struct userdata_s *d = gp->data;
3649 rav_next_prev(gp, d->b, which);
3652 void do_history_rav_next()
3654 do_history_rav(1);
3657 void do_history_rav_prev()
3659 do_history_rav(0);
3662 void do_history_jump()
3664 struct input_data_s *in;
3666 if (pgn_history_total(gp->hp) < 2)
3667 return;
3669 if (!keycount) {
3670 in = Calloc(1, sizeof(struct input_data_s));
3671 in->efunc = do_move_jump;
3673 construct_input(_("Jump to Move Number"), NULL, 1, 1, NULL,
3674 NULL, NULL, 0, in, -1, 0);
3675 return;
3678 do_move_jump_finalize(keycount);
3681 static void free_userdata_once(GAME g)
3683 struct userdata_s *d = g->data;
3685 if (!d)
3686 return;
3688 if (d->engine) {
3689 stop_engine(g);
3691 if (d->engine->enginebuf) {
3692 int n;
3694 for (n = 0; d->engine->enginebuf[n]; n++)
3695 free(d->engine->enginebuf[n]);
3697 free(d->engine->enginebuf);
3700 if (d->engine->queue) {
3701 struct queue_s **q;
3703 for (q = d->engine->queue; *q; q++)
3704 free(*q);
3706 free(d->engine->queue);
3709 free(d->engine);
3712 #ifdef WITH_LIBPERL
3713 if (d->perlfen)
3714 free(d->perlfen);
3716 if (d->oldfen)
3717 free(d->oldfen);
3718 #endif
3720 free(d);
3721 g->data = NULL;
3724 static void free_userdata()
3726 int i;
3728 for (i = 0; i < gtotal; i++) {
3729 free_userdata_once(game[i]);
3730 game[i]->data = NULL;
3734 void update_loading_window(int n)
3736 char buf[16];
3738 if (!loadingw) {
3739 loadingw = newwin(3, COLS / 2, CALCPOSY(3), CALCPOSX(COLS / 2));
3740 loadingp = new_panel(loadingw);
3741 wbkgd(loadingw, CP_MESSAGE_WINDOW);
3744 wmove(loadingw, 0, 0);
3745 wclrtobot(loadingw);
3746 wattron(loadingw, CP_MESSAGE_BORDER);
3747 box(loadingw, ACS_VLINE, ACS_HLINE);
3748 wattroff(loadingw, CP_MESSAGE_BORDER);
3749 mvwprintw(loadingw, 1, CENTER_INT((COLS / 2), 11 +
3750 strlen(itoa(gtotal, buf))),
3751 _("Loading... %i%% (%i games)"), n, gtotal);
3752 update_panels();
3753 doupdate();
3756 static void init_userdata_once(GAME g, int n)
3758 struct userdata_s *d = NULL;
3760 d = Calloc(1, sizeof(struct userdata_s));
3761 d->n = n;
3762 d->c_row = 2, d->c_col = 5;
3763 SET_FLAG(d->flags, CF_NEW);
3764 g->data = d;
3766 if (pgn_board_init_fen(g, d->b, NULL) != E_PGN_OK)
3767 pgn_board_init(d->b);
3770 void init_userdata()
3772 int i;
3774 for (i = 0; i < gtotal; i++)
3775 init_userdata_once(game[i], i);
3778 void fix_marks(int *start, int *end)
3780 int i;
3782 *start = (*start < 0) ? 0 : *start;
3783 *end = (*end < 0) ? 0 : *end;
3785 if (*start > *end) {
3786 i = *start;
3787 *start = *end;
3788 *end = i + 1;
3791 *end = (*end > gtotal) ? gtotal : *end;
3794 void do_new_game_finalize(GAME g)
3796 struct userdata_s *d = g->data;
3798 d->mode = MODE_PLAY;
3799 update_status_notify(g, NULL);
3802 void do_new_game_from_scratch(WIN *win)
3804 if (tolower(win->c) != 'y')
3805 return;
3807 stop_clock();
3808 free_userdata();
3809 pgn_parse(NULL);
3810 gp = game[gindex];
3811 add_custom_tags(&gp->tag);
3812 init_userdata();
3813 loadfile[0] = 0;
3814 do_new_game_finalize(gp);
3815 rotate = FALSE;
3818 void do_new_game()
3820 pgn_new_game();
3821 gp = game[gindex];
3822 add_custom_tags(&gp->tag);
3823 init_userdata_once(gp, gindex);
3824 do_new_game_finalize(gp);
3825 rotate = FALSE;
3828 void do_game_delete_finalize(int n)
3830 struct userdata_s *d;
3832 delete_game((!n) ? gindex : -1);
3833 d = gp->data;
3834 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3837 void do_game_delete_confirm(WIN *win)
3839 int *n;
3841 if (tolower(win->c) != 'y') {
3842 free(win->data);
3843 return;
3847 n = (int *)win->data;
3848 do_game_delete_finalize(*n);
3849 free(win->data);
3852 void do_game_delete()
3854 char *tmp = NULL;
3855 int i, n;
3856 struct userdata_s *d;
3857 int *p;
3859 if (gtotal < 2) {
3860 cmessage(NULL, _("[ press any key to continue ]"), "%s", _("Cannot delete last game."));
3861 return;
3864 tmp = NULL;
3866 for (i = n = 0; i < gtotal; i++) {
3867 d = game[i]->data;
3869 if (TEST_FLAG(d->flags, CF_DELETE))
3870 n++;
3873 if (!n)
3874 tmp = _("Delete the current game?");
3875 else {
3876 if (n == gtotal) {
3877 cmessage(NULL, _("[ press any key to continue ]"), "%s", _("Cannot delete last game."));
3878 return;
3881 tmp = _("Delete all games marked for deletion?");
3884 if (config.deleteprompt) {
3885 p = Malloc(sizeof(int));
3886 *p = n;
3887 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, p,
3888 do_game_delete_confirm, 0, 0, tmp);
3889 return;
3892 do_game_delete_finalize(n);
3895 void do_find_game_exp_finalize(int which)
3897 struct userdata_s *d = gp->data;
3898 int n;
3900 if ((n = find_game_exp(gameexp, (which == -1) ? 0 : 1,
3901 (keycount) ? keycount : 1)) == -1) {
3902 update_status_notify(gp, "%s", _("No matches found"));
3903 return;
3906 gindex = n;
3907 d = gp->data;
3909 if (pgn_history_total(gp->hp))
3910 d->mode = MODE_HISTORY;
3912 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3915 void do_find_game_exp(WIN *win)
3917 struct input_data_s *in = win->data;
3918 int *n = in->data;
3919 int c = *n;
3921 if (in->str) {
3922 strncpy(gameexp, in->str, sizeof(gameexp));
3923 gameexp[sizeof(gameexp)-1] = 0;
3925 if (c == '?')
3926 c = '}';
3928 do_find_game_exp_finalize(c);
3929 free(in->str);
3932 free(in->data);
3933 free(in);
3936 void do_game_jump_finalize(int n)
3938 struct userdata_s *d;
3940 if (--n > gtotal - 1 || n < 0)
3941 return;
3943 gindex = n;
3944 gp = game[gindex];
3945 d = gp->data;
3946 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3947 update_status_notify(gp, NULL);
3950 void do_game_jump(WIN *win)
3952 struct input_data_s *in = win->data;
3954 if (!in->str || !isinteger(in->str)) {
3955 if (in->str)
3956 free(in->str);
3958 free(in);
3959 return;
3962 do_game_jump_finalize(atoi(in->str));
3963 free(in->str);
3964 free(in);
3967 void do_load_file(WIN *win)
3969 struct input_data_s *in = win->data;
3970 char *tmp = in->str;
3971 struct userdata_s *d;
3972 PGN_FILE *pgn = NULL;
3973 int n;
3975 if (!in->str) {
3976 free(in);
3977 return;
3980 if ((tmp = pathfix(tmp)) == NULL)
3981 goto done;
3983 n = pgn_open(tmp, "r", &pgn);
3985 if (n == E_PGN_ERR) {
3986 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s\n%s", tmp, strerror(errno));
3987 goto done;
3989 else if (n == E_PGN_INVALID) {
3990 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s\n%s", tmp, _("Not a regular file"));
3991 goto done;
3994 free_userdata();
3997 * FIXME what is the game state after a parse error?
3999 if (pgn_parse(pgn) == E_PGN_ERR) {
4000 del_panel(loadingp);
4001 delwin(loadingw);
4002 loadingw = NULL;
4003 loadingp = NULL;
4004 init_userdata();
4005 goto done;
4008 del_panel(loadingp);
4009 delwin(loadingw);
4010 loadingw = NULL;
4011 loadingp = NULL;
4012 init_userdata();
4013 strncpy(loadfile, tmp, sizeof(loadfile));
4014 loadfile[sizeof(loadfile)-1] = 0;
4015 gp = game[gindex];
4016 d = gp->data;
4018 if (pgn_history_total(gp->hp))
4019 d->mode = MODE_HISTORY;
4021 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4023 fm_loaded_file = TRUE;
4025 done:
4026 pgn_close(pgn);
4028 if (in->str)
4029 free(in->str);
4031 free(in);
4034 void do_game_save(WIN *win)
4036 struct input_data_s *in = win->data;
4037 int *x = in->data;
4038 int n = *x;
4039 char *tmp = in->str;
4040 char tfile[FILENAME_MAX];
4041 char *p;
4042 int i;
4043 struct userdata_s *d;
4045 if (!tmp || (tmp = pathfix(tmp)) == NULL)
4046 goto done;
4048 if (pgn_is_compressed(tmp) == E_PGN_ERR) {
4049 p = tmp + strlen(tmp) - 1;
4051 if (*p != 'n' || *(p-1) != 'g' || *(p-2) != 'p' ||
4052 *(p-3) != '.') {
4053 snprintf(tfile, sizeof(tfile), "%s.pgn", tmp);
4054 tmp = tfile;
4059 * When in edit mode, update the FEN tag.
4061 if (n == -1) {
4062 for (i = 0; i < gtotal; i++) {
4063 d = game[i]->data;
4065 if (d->mode == MODE_EDIT) {
4066 char *fen = pgn_game_to_fen(game[i], d->b);
4068 pgn_tag_add(&game[i]->tag, "FEN", fen);
4069 free (fen);
4073 else {
4074 d = game[n]->data;
4076 if (d->mode == MODE_EDIT) {
4077 char *fen = pgn_game_to_fen(game[n], d->b);
4079 pgn_tag_add(&game[n]->tag, "FEN", fen);
4080 free (fen);
4084 save_pgn(tmp, n);
4086 done:
4087 if (in->str)
4088 free(in->str);
4090 free(in->data);
4091 free(in);
4094 void do_get_game_save_input(int n)
4096 struct input_data_s *in = Calloc(1, sizeof(struct input_data_s));
4097 int *p = Malloc(sizeof(int));
4099 in->efunc = do_game_save;
4100 *p = n;
4101 in->data = p;
4103 construct_input(_("Save Game Filename"), loadfile, 1, 1, _("Type TAB for file browser"),
4104 file_browser, NULL, '\t', in, INPUT_HIST_FILE, -1);
4107 void do_game_save_multi_confirm(WIN *win)
4109 int i;
4111 if (win->c == 'c')
4112 i = gindex;
4113 else if (win->c == 'a')
4114 i = -1;
4115 else {
4116 update_status_notify(gp, "%s", _("Save game aborted."));
4117 return;
4120 do_get_game_save_input(i);
4123 void do_global_about()
4125 cmessage("ABOUT", _("[ press any key to continue ]"), "%s\nUsing %s with %i colors "
4126 "and %i color pairs\n%s",
4127 PACKAGE_STRING, curses_version(), COLORS, COLOR_PAIRS,
4128 COPYRIGHT);
4131 void global_game_next_prev(int which)
4133 struct userdata_s *d;
4135 game_next_prev(gp, (which == 1) ? 1 : 0,
4136 (keycount) ? keycount : 1);
4137 d = gp->data;
4139 if (delete_count) {
4140 if (which == 1) {
4141 markend = markstart + delete_count;
4142 delete_count = 0;
4144 else {
4145 markend = markstart - delete_count + 1;
4146 delete_count = -1; // to fix gindex in the other direction
4149 fix_marks(&markstart, &markend);
4150 do_global_toggle_delete();
4153 if (d->mode == MODE_HISTORY)
4154 pgn_board_update(gp, d->b, gp->hindex);
4155 else if (d->mode == MODE_PLAY)
4156 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4159 void do_global_next_game()
4161 global_game_next_prev(1);
4164 void do_global_prev_game()
4166 global_game_next_prev(0);
4169 void global_find(int which)
4171 struct input_data_s *in;
4172 int *p;
4174 if (gtotal < 2)
4175 return;
4177 in = Calloc(1, sizeof(struct input_data_s));
4178 p = Malloc(sizeof(int));
4179 *p = which;
4180 in->data = p;
4181 in->efunc = do_find_game_exp;
4183 if (!*gameexp || which == 0) {
4184 construct_input(_("Find Game by Tag Expression"), NULL, 1, 0,
4185 _("[name expression:]value expression"), NULL, NULL, 0, in,
4186 INPUT_HIST_GAME_EXP, -1);
4187 return;
4190 do_find_game_exp_finalize(which);
4193 void do_global_find_new()
4195 global_find(0);
4198 void do_global_find_next()
4200 global_find(1);
4203 void do_global_find_prev()
4205 global_find(-1);
4208 void do_global_game_jump()
4210 if (gtotal < 2)
4211 return;
4213 if (!keycount) {
4214 struct input_data_s *in;
4216 in = Calloc(1, sizeof(struct input_data_s));
4217 in->efunc = do_game_jump;
4218 construct_input(_("Jump to Game Number"), NULL, 1, 1, NULL, NULL, NULL, 0, in,
4219 -1, 0);
4220 return;
4223 do_game_jump_finalize(keycount);
4226 void do_global_toggle_delete()
4228 int i;
4230 pushkey = 0;
4232 if (gtotal < 2)
4233 return;
4235 if (keycount && delete_count == 0) {
4236 markstart = gindex;
4237 delete_count = keycount;
4238 update_status_notify(gp, "%s (delete)", status.notify);
4239 return;
4242 if (markstart >= 0 && markend >= 0) {
4243 for (i = markstart; i < markend; i++) {
4244 if (toggle_delete_flag(i)) {
4245 return;
4249 gindex = (delete_count < 0) ? markstart : i - 1;
4251 else {
4252 if (toggle_delete_flag(gindex))
4253 return;
4256 markstart = markend = -1;
4257 delete_count = 0;
4258 update_status_window(gp);
4261 void do_global_delete_game()
4263 do_game_delete();
4266 void do_global_tag_edit()
4268 struct userdata_s *d = gp->data;
4270 edit_tags(gp, d->b, 1);
4273 void do_global_tag_view()
4275 struct userdata_s *d = gp->data;
4277 edit_tags(gp, d->b, 0);
4280 void do_global_resume_game()
4282 struct input_data_s *in;
4284 in = Calloc(1, sizeof(struct input_data_s));
4285 in->efunc = do_load_file;
4286 construct_input(_("Load Filename"), NULL, 1, 1, _("Type TAB for file browser"), file_browser,
4287 NULL, '\t', in, INPUT_HIST_FILE, -1);
4290 void do_global_save_game()
4292 if (gtotal > 1) {
4293 construct_message(NULL, _("Type 'c' or 'a' or any other key "), 1, 1, NULL, NULL, NULL,
4294 do_game_save_multi_confirm, 0, 0, "%s", _("There is more than one game "));
4295 return;
4298 do_get_game_save_input(-1);
4301 void do_global_new_game()
4303 do_new_game();
4306 void do_global_copy_game()
4308 int g = gindex;
4309 int i, n;
4310 struct userdata_s *d;
4312 do_global_new_game();
4313 d = gp->data;
4314 n = pgn_tag_total(game[g]->tag);
4316 for (i = 0; i < n; i++)
4317 pgn_tag_add(&gp->tag, game[g]->tag[i]->name,
4318 game[g]->tag[i]->value);
4320 pgn_board_init_fen (gp, d->b, NULL);
4321 n = pgn_history_total(game[g]->history);
4323 // FIXME RAV
4324 for (i = 0; i < n; i++) {
4325 char *frfr = NULL;
4326 char move[MAX_SAN_MOVE_LEN+1] = {0}, *m = move;
4328 strcpy (move, game[g]->history[i]->move);
4329 if (pgn_parse_move(gp, d->b, &m, &frfr) != E_PGN_OK) {
4330 SET_FLAG(gp->flags, GF_PERROR);
4331 return;
4334 pgn_history_add(gp, d->b, move);
4335 free(frfr);
4336 pgn_switch_turn(gp);
4339 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4342 void do_global_new_all()
4344 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
4345 do_new_game_from_scratch, 0, 0, "%s", _("Really start a new game from scratch?"));
4348 void do_quit(WIN *win)
4350 if (tolower(win->c) != 'y')
4351 return;
4353 quit = 1;
4356 void do_global_quit()
4358 if (config.exitdialogbox)
4359 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
4360 do_quit, 0, 0, _("Want to Quit?"));
4361 else
4362 quit = 1;
4365 void do_global_toggle_engine_window()
4367 if (!enginew) {
4368 enginew = newwin(LINES, COLS, 0, 0);
4369 enginep = new_panel(enginew);
4370 window_draw_title(enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
4371 CP_MESSAGE_BORDER);
4372 hide_panel(enginep);
4375 if (panel_hidden(enginep)) {
4376 update_engine_window(gp);
4377 top_panel(enginep);
4379 else {
4380 hide_panel(enginep);
4384 void do_global_toggle_board_details()
4386 do_board_details();
4389 void do_global_toggle_strict_castling()
4391 do_toggle_strict_castling();
4394 // Global and other keys.
4395 static int globalkeys()
4397 struct userdata_s *d = gp->data;
4398 int i;
4401 * These cannot be modified and other game mode keys cannot conflict with
4402 * these.
4404 switch (input_c) {
4405 case CTRL_KEY('L'):
4406 endwin();
4407 keypad(boardw, TRUE);
4408 wmove(stdscr, 0, 0);
4409 wclrtobot(stdscr);
4410 return 1;
4411 case KEY_ESCAPE:
4412 d->sp.icon = d->sp.srow = d->sp.scol = 0;
4413 markend = markstart = 0;
4415 if (keycount) {
4416 keycount = 0;
4417 update_status_notify(gp, NULL);
4420 if (config.validmoves)
4421 pgn_reset_valid_moves(d->b);
4423 return 1;
4424 case '0' ... '9':
4425 i = input_c - '0';
4427 if (keycount)
4428 keycount = keycount * 10 + i;
4429 else
4430 keycount = i;
4432 update_status_notify(gp, _("Repeat %i"), keycount);
4433 return -1;
4434 case KEY_UP:
4435 if (d->mode == MODE_HISTORY)
4436 return 0;
4438 if (keycount)
4439 d->c_row += keycount;
4440 else
4441 d->c_row++;
4443 if (d->c_row > 8)
4444 d->c_row = 1;
4446 return 1;
4447 case KEY_DOWN:
4448 if (d->mode == MODE_HISTORY)
4449 return 0;
4451 if (keycount) {
4452 d->c_row -= keycount;
4453 update_status_notify(gp, NULL);
4455 else
4456 d->c_row--;
4458 if (d->c_row < 1)
4459 d->c_row = 8;
4461 return 1;
4462 case KEY_LEFT:
4463 if (d->mode == MODE_HISTORY)
4464 return 0;
4466 if (keycount)
4467 d->c_col -= keycount;
4468 else
4469 d->c_col--;
4471 if (d->c_col < 1)
4472 d->c_col = 8;
4474 return 1;
4475 case KEY_RIGHT:
4476 if (d->mode == MODE_HISTORY)
4477 return 0;
4479 if (keycount)
4480 d->c_col += keycount;
4481 else
4482 d->c_col++;
4484 if (d->c_col > 8)
4485 d->c_col = 1;
4487 return 1;
4488 #ifdef HAVE_WRESIZE
4489 case KEY_RESIZE:
4490 // do_window_resize();
4491 return 1;
4492 #endif
4493 case 0:
4494 default:
4495 for (i = 0; global_keys[i]; i++) {
4496 if (input_c == global_keys[i]->c) {
4497 (*global_keys[i]->f)();
4498 return 1;
4501 break;
4504 return 0;
4507 #ifdef WITH_LIBPERL
4508 static void perl_error(const char *fmt, ...)
4510 va_list ap;
4511 char *buf;
4513 va_start(ap, fmt);
4514 vasprintf(&buf, fmt, ap);
4515 va_end(ap);
4517 message(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s", buf);
4518 free(buf);
4521 static void do_perl_finalize(WIN *win)
4523 struct input_data_s *in = win->data;
4524 GAME g = in->data;
4525 struct userdata_s *d = g->data;
4526 char *filename;
4527 char *result = NULL;
4528 char *arg = NULL;
4529 int n;
4531 asprintf(&filename, "%s/perl.pl", config.datadir);
4533 if (!in->str)
4534 goto done;
4536 if (perl_init_file(filename, perl_error))
4537 goto done;
4539 arg = pgn_game_to_fen(g, d->b);
4541 if (perl_call_sub(trim(in->str), arg, &result))
4542 goto done;
4544 d->perlfen = pgn_game_to_fen(g, d->b);
4545 d->perlflags = g->flags;
4547 if (pgn_board_init_fen(g, d->b, result) != E_PGN_OK) {
4548 message(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s", _("FEN parse error."));
4549 pgn_board_init_fen(g, d->b, d->perlfen);
4550 g->flags = d->perlflags;
4551 free(d->perlfen);
4552 d->perlfen = NULL;
4553 goto done;
4556 SET_FLAG(d->flags, CF_PERL);
4557 n = pgn_tag_find(g->tag, "FEN");
4559 if (n != E_PGN_ERR)
4560 d->oldfen = strdup(g->tag[n]->value);
4562 pgn_tag_add(&g->tag, "FEN", result);
4563 update_status_notify(g, "%s", _("[ press any key to continue ]"));
4564 update_all(g);
4566 done:
4567 free(result);
4568 free(arg);
4569 free(in->str);
4570 free(in);
4571 free(filename);
4574 void do_global_perl()
4576 struct input_data_s *in;
4578 in = Calloc(1, sizeof(struct input_data_s));
4579 in->data = gp;
4580 in->efunc = do_perl_finalize;
4581 construct_input(_("PERL Subroutine Filter"), NULL, 1, 0, NULL, NULL, NULL, 0, in, INPUT_HIST_PERL, -1);
4583 #endif
4586 * A macro may contain a key that belongs to another macro so macro_match will
4587 * need to be updated to the new index of the matching macro.
4589 static void find_macro(struct userdata_s *d)
4591 int i;
4594 * Macros can't contain macros when in a window.
4596 if (wins)
4597 return;
4599 again:
4600 for (i = 0; macros[i]; i++) {
4601 if ((macros[i]->mode == -1 || macros[i]->mode == d->mode) &&
4602 input_c == macros[i]->c) {
4603 input_c = macros[i]->keys[macros[i]->n++];
4605 if (!macro_depth_n && macro_match > -1) {
4606 macro_depth = realloc(macro_depth, (macro_depth_n + 1) * sizeof(int));
4607 macro_depth[macro_depth_n++] = macro_match;
4610 macro_depth = realloc(macro_depth, (macro_depth_n + 1) * sizeof(int));
4611 macro_depth[macro_depth_n++] = i;
4612 macro_match = i;
4613 goto again;
4619 * Resets the position in each macro to the first key.
4621 static void reset_macros()
4623 int i;
4624 struct userdata_s *d = gp->data;
4626 again:
4627 if (macro_depth_n > 0) {
4628 macro_depth_n--;
4629 macro_match = macro_depth[macro_depth_n];
4631 if (macros[macro_match]->n >= macros[macro_match]->total)
4632 goto again;
4634 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
4635 find_macro(d);
4636 return;
4639 for (i = 0; macros[i]; i++)
4640 macros[i]->n = 0;
4642 free(macro_depth);
4643 macro_depth = NULL;
4644 macro_depth_n = 0;
4645 macro_match = -1;
4648 void game_loop()
4650 struct userdata_s *d;
4652 macro_match = -1;
4653 gindex = gtotal - 1;
4654 gp = game[gindex];
4655 d = gp->data;
4657 if (pgn_history_total(gp->hp))
4658 d->mode = MODE_HISTORY;
4659 else {
4660 d->mode = MODE_PLAY;
4661 d->play_mode = PLAY_HE;
4664 if (d->mode == MODE_HISTORY)
4665 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4667 update_status_notify(gp, "%s", _("Type F1 for help"));
4668 movestep = 2;
4669 flushinp();
4670 update_all(gp);
4671 wtimeout(boardw, WINDOW_TIMEOUT);
4673 while (!quit) {
4674 int n = 0, i;
4675 char fdbuf[8192] = {0};
4676 int len;
4677 struct timeval tv = {0, 0};
4678 fd_set rfds, wfds;
4679 WIN *win = NULL;
4680 WINDOW *wp = NULL;
4682 FD_ZERO(&rfds);
4683 FD_ZERO(&wfds);
4685 for (i = 0; i < gtotal; i++) {
4686 d = game[i]->data;
4688 if (d->engine && d->engine->pid != -1) {
4689 if (d->engine->fd[ENGINE_IN_FD] > 2) {
4690 if (d->engine->fd[ENGINE_IN_FD] > n)
4691 n = d->engine->fd[ENGINE_IN_FD];
4693 FD_SET(d->engine->fd[ENGINE_IN_FD], &rfds);
4696 if (d->engine->fd[ENGINE_OUT_FD] > 2) {
4697 if (d->engine->fd[ENGINE_OUT_FD] > n)
4698 n = d->engine->fd[ENGINE_OUT_FD];
4700 FD_SET(d->engine->fd[ENGINE_OUT_FD], &wfds);
4705 if (n) {
4706 if ((n = select(n + 1, &rfds, &wfds, NULL, &tv)) > 0) {
4707 for (i = 0; i < gtotal; i++) {
4708 d = game[i]->data;
4710 if (d->engine && d->engine->pid != -1) {
4711 if (FD_ISSET(d->engine->fd[ENGINE_IN_FD], &rfds)) {
4712 len = read(d->engine->fd[ENGINE_IN_FD], fdbuf,
4713 sizeof(fdbuf));
4715 if (len > 0) {
4716 if (d->engine->iobuf)
4717 d->engine->iobuf = Realloc(d->engine->iobuf, d->engine->len + len + 1);
4718 else
4719 d->engine->iobuf = Calloc(1, len + 1);
4721 memcpy(&(d->engine->iobuf[d->engine->len]), &fdbuf, len);
4722 d->engine->len += len;
4723 d->engine->iobuf[d->engine->len] = 0;
4726 * The fdbuf is full or no newline
4727 * was found. So we'll append the next
4728 * read() to this games buffer.
4730 if (d->engine->iobuf[d->engine->len - 1] != '\n')
4731 continue;
4733 parse_engine_output(game[i], d->engine->iobuf);
4734 free(d->engine->iobuf);
4735 d->engine->iobuf = NULL;
4736 d->engine->len = 0;
4738 else if (len == -1) {
4739 if (errno != EAGAIN) {
4740 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "Engine read(): %s",
4741 strerror(errno));
4742 waitpid(d->engine->pid, &n, 0);
4743 free(d->engine);
4744 d->engine = NULL;
4745 break;
4750 if (FD_ISSET(d->engine->fd[ENGINE_OUT_FD], &wfds)) {
4751 if (d->engine->queue)
4752 send_engine_command(game[i]);
4757 else {
4758 if (n == -1)
4759 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "select(): %s", strerror(errno));
4760 /* timeout */
4764 gp = game[gindex];
4765 d = gp->data;
4768 * This is needed to detect terminal resizing.
4770 doupdate();
4771 if (LINES != LINES_OLD || COLS != COLS_OLD){
4772 COLS_OLD = COLS;
4773 LINES_OLD = LINES;
4774 do_window_resize();
4778 * Finds the top level window in the window stack so we know what
4779 * window the wgetch()ed key belongs to.
4781 if (wins) {
4782 for (i = 0; wins[i]; i++);
4783 win = wins[i-1];
4784 wp = win->w;
4785 wtimeout(wp, WINDOW_TIMEOUT);
4787 else
4788 wp = boardw;
4790 if (!i && pushkey)
4791 input_c = pushkey;
4792 else {
4793 if (!pushkey) {
4794 if (macros && macro_match >= 0) {
4795 if (macros[macro_match]->n >= macros[macro_match]->total)
4796 reset_macros();
4797 else {
4798 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
4799 find_macro(d);
4802 else {
4803 if ((input_c = wgetch(wp)) == ERR)
4804 continue;
4807 else
4808 input_c = pushkey;
4810 if (win) {
4811 switch (input_c) {
4812 case CTRL_KEY('L'):
4813 endwin();
4814 keypad(boardw, TRUE);
4815 wmove(stdscr, 0, 0);
4816 wclrtobot(stdscr);
4817 goto refresh;
4820 win->c = input_c;
4823 * Run the function associated with the window. When the
4824 * function returns 0 win->efunc is ran (if not NULL) with
4825 * win as the one and only parameter. Then the window is
4826 * destroyed.
4828 * The exit function may create another window which will
4829 * mess up the window stack when window_destroy() is called.
4830 * So don't destory the window until the top window is
4831 * destroyable. See window_destroy().
4833 if ((*win->func)(win) == 0) {
4834 if (win->efunc)
4835 (*win->efunc)(win);
4837 win->keep = 1;
4838 window_destroy(win);
4839 update_all(gp);
4842 continue;
4846 if (!keycount && status.notify)
4847 update_status_notify(gp, NULL);
4849 #ifdef WITH_LIBPERL
4850 if (TEST_FLAG(d->flags, CF_PERL)) {
4851 CLEAR_FLAG(d->flags, CF_PERL);
4852 pgn_board_init_fen(gp, d->b, d->perlfen);
4853 gp->flags = d->perlflags;
4854 free(d->perlfen);
4855 pgn_tag_add(&gp->tag, "FEN", d->oldfen);
4856 free(d->oldfen);
4857 d->perlfen = d->oldfen = NULL;
4858 update_all(gp);
4859 continue;
4861 #endif
4863 if (macros && macro_match < 0)
4864 find_macro(d);
4866 if ((n = globalkeys()) == 1) {
4867 if (macro_match == -1)
4868 keycount = 0;
4870 goto refresh;
4872 else if (n == -1)
4873 goto refresh;
4875 switch (d->mode) {
4876 case MODE_EDIT:
4877 for (i = 0; edit_keys[i]; i++) {
4878 if (input_c == edit_keys[i]->c) {
4879 (*edit_keys[i]->f)();
4880 break;
4883 break;
4884 case MODE_PLAY:
4885 for (i = 0; play_keys[i]; i++) {
4886 if (input_c == play_keys[i]->c) {
4887 (*play_keys[i]->f)();
4888 goto done;
4892 do_play_config_command();
4893 break;
4894 case MODE_HISTORY:
4895 for (i = 0; history_keys[i]; i++) {
4896 if (input_c == history_keys[i]->c) {
4897 (*history_keys[i]->f)();
4898 break;
4901 break;
4902 default:
4903 break;
4906 done:
4907 if (keycount)
4908 update_status_notify(gp, NULL);
4910 keycount = 0;
4912 refresh:
4913 update_all(gp);
4917 void usage(const char *pn, int ret)
4919 fprintf((ret) ? stderr : stdout, "%s", _(
4920 #ifdef DEBUG
4921 "Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
4922 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"
4923 #else
4924 "Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"
4925 #endif
4926 " -p Load PGN file.\n"
4927 " -V Validate a game file.\n"
4928 " -S Validate and output a PGN formatted game.\n"
4929 " -R Like -S but write a reduced PGN formatted game.\n"
4930 " -t Also write custom PGN tags from config file.\n"
4931 " -E Stop processing on file parsing error (overrides config).\n"
4932 " -C Enable strict castling (overrides config).\n"
4933 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, moverrides config).\n"
4934 " -v Version information.\n"
4935 " -h This help text.\n"));
4937 exit(ret);
4940 void cleanup_all()
4942 int i;
4944 stop_clock();
4945 free_userdata();
4946 pgn_free_all();
4947 free(config.engine_cmd);
4948 free(config.pattern);
4949 free(config.ccfile);
4950 free(config.nagfile);
4951 free(config.configfile);
4953 if (config.keys) {
4954 for (i = 0; config.keys[i]; i++) {
4955 free(config.keys[i]->str);
4956 free(config.keys[i]);
4959 free(config.keys);
4962 if (config.einit) {
4963 for (i = 0; config.einit[i]; i++)
4964 free(config.einit[i]);
4966 free(config.einit);
4969 if (config.tag)
4970 pgn_tag_free(config.tag);
4972 free(config.datadir);
4974 if (curses_initialized) {
4975 del_panel(boardp);
4976 del_panel(historyp);
4977 del_panel(statusp);
4978 del_panel(tagp);
4979 delwin(boardw);
4980 delwin(historyw);
4981 delwin(statusw);
4982 delwin(tagw);
4984 if (enginew) {
4985 del_panel(enginep);
4986 delwin(enginew);
4989 endwin();
4992 #ifdef WITH_LIBPERL
4993 perl_cleanup();
4994 #endif
4997 static void signal_save_pgn(int sig)
4999 char *buf;
5000 time_t now;
5001 char *p = config.savedirectory ? config.savedirectory : config.datadir;
5003 time(&now);
5004 asprintf(&buf, "%s/signal-%i-%li.pgn", p, sig, now);
5006 if (do_game_write(buf, "w", 0, gtotal)) {
5007 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s: %s", p, strerror(errno));
5008 update_status_notify(gp, "%s", _("Save game failed."));
5011 free(buf);
5012 quit = 1;
5015 void catch_signal(int which)
5017 switch (which) {
5018 case SIGALRM:
5019 update_clocks();
5020 break;
5021 case SIGPIPE:
5022 if (which == SIGPIPE && quit)
5023 break;
5025 if (which == SIGPIPE)
5026 cmessage(NULL, _("[ press any key to continue ]"), "%s", _("Broken pipe. Quitting."));
5028 cleanup_all();
5029 exit(EXIT_FAILURE);
5030 break;
5031 case SIGSTOP:
5032 savetty();
5033 break;
5034 case SIGCONT:
5035 resetty();
5036 keypad(boardw, TRUE);
5037 curs_set(0);
5038 cbreak();
5039 noecho();
5040 break;
5041 case SIGINT:
5042 quit = 1;
5043 break;
5044 case SIGTERM:
5045 signal_save_pgn(which);
5046 break;
5047 default:
5048 break;
5052 void loading_progress(long total, long offset)
5054 int n = (100 * (offset / 100) / (total / 100));
5056 if (curses_initialized)
5057 update_loading_window(n);
5058 else {
5059 fprintf(stderr, _("Loading... %i%% (%i games)\r"), n, gtotal);
5060 fflush(stderr);
5064 static void set_defaults()
5066 set_config_defaults();
5067 set_default_keys();
5068 filetype = FILE_NONE;
5069 pgn_config_set(PGN_PROGRESS, 1024);
5070 pgn_config_set(PGN_PROGRESS_FUNC, loading_progress);
5073 int main(int argc, char *argv[])
5075 int opt;
5076 struct stat st;
5077 char buf[FILENAME_MAX];
5078 char datadir[FILENAME_MAX];
5079 int ret = EXIT_SUCCESS;
5080 int validate_only = 0, validate_and_write = 0;
5081 int write_custom_tags = 0;
5082 int i = 0;
5083 PGN_FILE *pgn;
5084 int utf8_pieces = -1;
5086 setlocale (LC_ALL, "");
5087 bindtextdomain ("cboard", LOCALE_DIR);
5088 textdomain ("cboard");
5090 /* Solaris 5.9 */
5091 #ifndef HAVE_PROGNAME
5092 __progname = argv[0];
5093 #endif
5095 if ((config.pwd = getpwuid(getuid())) == NULL)
5096 err(EXIT_FAILURE, "getpwuid()");
5098 snprintf(datadir, sizeof(datadir), "%s/.cboard", config.pwd->pw_dir);
5099 config.datadir = strdup(datadir);
5100 snprintf(buf, sizeof(buf), "%s/cc.data", datadir);
5101 config.ccfile = strdup(buf);
5102 snprintf(buf, sizeof(buf), "%s/nag.data", datadir);
5103 config.nagfile = strdup(buf);
5104 snprintf(buf, sizeof(buf), "%s/config", datadir);
5105 config.configfile = strdup(buf);
5107 if (stat(datadir, &st) == -1) {
5108 if (errno == ENOENT) {
5109 if (mkdir(datadir, 0755) == -1)
5110 err(EXIT_FAILURE, "%s", datadir);
5112 else
5113 err(EXIT_FAILURE, "%s", datadir);
5115 stat(datadir, &st);
5118 if (!S_ISDIR(st.st_mode))
5119 errx(EXIT_FAILURE, "%s: %s", datadir, _("Not a directory."));
5121 set_defaults();
5123 #ifdef DEBUG
5124 while ((opt = getopt(argc, argv, "DCEVtSRhp:vu::")) != -1) {
5125 #else
5126 while ((opt = getopt(argc, argv, "ECVtSRhp:vu::")) != -1) {
5127 #endif
5128 switch (opt) {
5129 #ifdef DEBUG
5130 case 'D':
5131 unlink("libchess.debug");
5132 pgn_config_set(PGN_DEBUG, 1);
5133 break;
5134 #endif
5135 case 'C':
5136 pgn_config_set(PGN_STRICT_CASTLING, 1);
5137 break;
5138 case 't':
5139 write_custom_tags = 1;
5140 break;
5141 case 'E':
5142 i = 1;
5143 break;
5144 case 'R':
5145 pgn_config_set(PGN_REDUCED, 1);
5146 case 'S':
5147 validate_and_write = 1;
5148 case 'V':
5149 validate_only = 1;
5150 break;
5151 case 'v':
5152 printf("%s (%s)\n%s\n", PACKAGE_STRING, curses_version(),
5153 COPYRIGHT);
5154 exit(EXIT_SUCCESS);
5155 case 'p':
5156 filetype = FILE_PGN;
5157 strncpy(loadfile, optarg, sizeof(loadfile));
5158 loadfile[sizeof(loadfile)-1] = 0;
5159 break;
5160 case 'u':
5161 utf8_pieces = optarg ? atoi (optarg): 1;
5162 break;
5163 case 'h':
5164 default:
5165 usage(argv[0], EXIT_SUCCESS);
5169 if ((validate_only || validate_and_write) && !*loadfile)
5170 usage(argv[0], EXIT_FAILURE);
5172 if (access(config.configfile, R_OK) == 0)
5173 parse_rcfile(config.configfile);
5175 if (i)
5176 pgn_config_set(PGN_STOP_ON_ERROR, 1);
5178 signal(SIGPIPE, catch_signal);
5179 signal(SIGCONT, catch_signal);
5180 signal(SIGSTOP, catch_signal);
5181 signal(SIGINT, catch_signal);
5182 signal(SIGALRM, catch_signal);
5183 signal(SIGTERM, catch_signal);
5185 srandom(getpid());
5187 switch (filetype) {
5188 case FILE_PGN:
5189 if (pgn_open(loadfile, "r", &pgn) != E_PGN_OK)
5190 err(EXIT_FAILURE, "%s", loadfile);
5192 ret = pgn_parse(pgn);
5193 pgn_close(pgn);
5194 break;
5195 case FILE_FEN:
5196 //ret = parse_fen_file(loadfile);
5197 break;
5198 case FILE_EPD: // Not implemented.
5199 case FILE_NONE:
5200 default:
5201 // No file specified. Empty game.
5202 ret = pgn_parse(NULL);
5203 gp = game[gindex];
5204 add_custom_tags(&gp->tag);
5205 break;
5208 if (validate_only || validate_and_write) {
5209 if (validate_and_write) {
5210 if (pgn_open("-", "r", &pgn) != E_PGN_OK)
5211 err(EXIT_FAILURE, "pgn_open()");
5213 for (i = 0; i < gtotal; i++) {
5214 if (write_custom_tags)
5215 add_custom_tags(&game[i]->tag);
5217 pgn_write(pgn, game[i]);
5220 pgn_close(pgn);
5222 fm_loaded_file = TRUE;
5225 cleanup_all();
5226 exit(ret);
5228 else if (ret == E_PGN_ERR)
5229 exit(ret);
5231 if (utf8_pieces != -1)
5232 config.utf8_pieces = utf8_pieces;
5234 init_wchar_pieces ();
5235 init_userdata();
5238 * This fixes window resizing in an xterm.
5240 if (getenv("DISPLAY") != NULL) {
5241 putenv("LINES=");
5242 putenv("COLUMNS=");
5245 if (initscr() == NULL)
5246 errx(EXIT_FAILURE, "%s", _("Could not initialize curses."));
5247 else
5248 curses_initialized = 1;
5250 if (LINES < 24 || COLS < 80) {
5251 endwin();
5252 errx(EXIT_FAILURE, _("Need at least an 80x24 terminal."));
5255 COLS_OLD = COLS;
5256 LINES_OLD = LINES;
5258 if (has_colors() == TRUE && start_color() == OK)
5259 init_color_pairs();
5261 boardw = newwin(BOARD_HEIGHT, BOARD_WIDTH, 0, COLS - BOARD_WIDTH);
5262 boardp = new_panel(boardw);
5263 historyw = newwin(HISTORY_HEIGHT, HISTORY_WIDTH, LINES - HISTORY_HEIGHT,
5264 COLS - HISTORY_WIDTH);
5265 historyp = new_panel(historyw);
5266 statusw = newwin(STATUS_HEIGHT, STATUS_WIDTH, 0, 0);
5267 statusp = new_panel(statusw);
5268 tagw = newwin(TAG_HEIGHT, TAG_WIDTH, STATUS_HEIGHT + 1, 0);
5269 tagp = new_panel(tagw);
5270 keypad(boardw, TRUE);
5271 // leaveok(boardw, TRUE);
5272 leaveok(tagw, TRUE);
5273 leaveok(statusw, TRUE);
5274 leaveok(historyw, TRUE);
5275 curs_set(0);
5276 cbreak();
5277 noecho();
5278 draw_window_decor();
5279 game_loop();
5280 cleanup_all();
5281 exit(EXIT_SUCCESS);