Update copyright year to 2015.
[cboard.git] / src / cboard.c
blob3f0fc68b7bda7c8d21e1da0a505fc2162f564ee2
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2015 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-2015 " PACKAGE_BUGREPORT
73 #define LINE_GRAPHIC(c) ((!config.linegraphics) ? ' ' : c)
74 #define ROWTOMATRIX(r) ((8 - r) * 2 + 2 - 1)
75 #define COLTOMATRIX(c) ((c == 1) ? 1 : c * 4 - 3)
76 #define STATUS_HEIGHT 12
77 #define MEGA_BOARD (LINES >= 50 && COLS >= 144)
78 #define BOARD_HEIGHT_MB 50
79 #define BOARD_WIDTH_MB 98
80 #define STATUS_WIDTH_MB (COLS - BOARD_WIDTH_MB)
81 #define TAG_HEIGHT_MB 31
82 #define TAG_WIDTH_MB (COLS - BOARD_WIDTH_MB)
83 #define HISTORY_HEIGHT_MB (LINES - (STATUS_HEIGHT + TAG_HEIGHT_MB + 1))
84 #define HISTORY_WIDTH_MB (COLS - BOARD_WIDTH_MB)
85 #define BIG_BOARD (LINES >= 40 && COLS >= 112)
86 #define BOARD_HEIGHT ((MEGA_BOARD) ? BOARD_HEIGHT_MB : (BIG_BOARD) ? 34 : 18)
87 #define BOARD_WIDTH ((MEGA_BOARD) ? BOARD_WIDTH_MB : (BIG_BOARD) ? 66 : 34)
88 #define STATUS_WIDTH ((MEGA_BOARD) ? STATUS_WIDTH_MB : COLS - BOARD_WIDTH)
89 #define TAG_HEIGHT ((MEGA_BOARD) ? TAG_HEIGHT_MB : LINES - STATUS_HEIGHT - 1)
90 #define TAG_WIDTH ((MEGA_BOARD) ? TAG_WIDTH_MB : COLS - BOARD_WIDTH)
91 #define HISTORY_HEIGHT ((MEGA_BOARD) ? HISTORY_HEIGHT_MB : LINES - BOARD_HEIGHT)
92 #define HISTORY_WIDTH ((MEGA_BOARD) ? HISTORY_WIDTH_MB : COLS - STATUS_WIDTH)
93 #define MAX_VALUE_WIDTH (COLS - 8)
95 enum {
96 UP, DOWN, LEFT, RIGHT
99 static WINDOW *boardw;
100 static PANEL *boardp;
101 static WINDOW *tagw;
102 static PANEL *tagp;
103 static WINDOW *statusw;
104 static PANEL *statusp;
105 static WINDOW *historyw;
106 static PANEL *historyp;
107 static WINDOW *loadingw;
108 static PANEL *loadingp;
109 static WINDOW *enginew;
110 static PANEL *enginep;
112 static char gameexp[255];
113 static char moveexp[255];
114 static struct itimerval clock_timer;
115 static int delete_count = 0;
116 static int markstart = -1, markend = -1;
117 static int keycount;
118 static char loadfile[FILENAME_MAX];
119 static int quit;
120 static wint_t input_c;
122 // Loaded filename from the command line or from the file input dialog.
123 static int filetype;
124 enum {
125 FILE_NONE, FILE_PGN, FILE_FEN, FILE_EPD
128 static char **nags;
129 static int nag_total;
130 static int macro_match;
132 // Primer movimiento de juego cargado
133 // First move loaded game
134 static char fm_loaded_file = FALSE;
136 static int COLS_OLD, LINES_OLD;
138 // Status window.
139 static struct {
140 wchar_t *notify; // The status window notification line buffer.
141 } status;
143 static int curses_initialized;
145 // When in history mode a full step is to the next move of the same playing
146 // side. Half stepping is alternating sides.
147 static int movestep;
149 static wchar_t *w_pawn_wchar;
150 static wchar_t *w_rook_wchar;
151 static wchar_t *w_bishop_wchar;
152 static wchar_t *w_knight_wchar;
153 static wchar_t *w_queen_wchar;
154 static wchar_t *w_king_wchar;
155 static wchar_t *b_pawn_wchar;
156 static wchar_t *b_rook_wchar;
157 static wchar_t *b_bishop_wchar;
158 static wchar_t *b_knight_wchar;
159 static wchar_t *b_queen_wchar;
160 static wchar_t *b_king_wchar;
161 static wchar_t *empty_wchar;
162 static wchar_t *enpassant_wchar;
164 static wchar_t *yes_wchar;
165 static wchar_t *all_wchar; // do_save_game_overwrite_confirm()
166 static wchar_t *overwrite_wchar; // do_save_game_overwrite_confirm()
167 static wchar_t *resume_wchar; // do_history_mode_confirm()
168 static wchar_t *current_wchar; // do_game_save_multi_confirm()
169 static wchar_t *append_wchar; // save_pgn()
171 static const char piece_chars[] = "PpRrNnBbQqKkxx";
172 static char *translatable_tag_names[7];
173 static const char *f_pieces[] = {
174 " ", // 0
175 " O ",
176 " /_\\ ",
177 " |-|-| ", // 3
178 " ] [ ",
179 " /___\\ ",
180 " /?M ", // 6
181 " (@/)) ",
182 " /__))",
183 " O ", // 9
184 " (+) ",
185 " /_\\ ",
186 "•°°°°°•",// 12
187 " \\\\|// ",
188 " |___| ",
189 " __+__ ", // 15
190 "(__|__)",
191 " |___| ",
192 " \\ / ", // 18
193 " X ",
194 " / \\ "
197 static const bool cb[8][8] = {
198 {1,0,1,0,1,0,1,0},
199 {0,1,0,1,0,1,0,1},
200 {1,0,1,0,1,0,1,0},
201 {0,1,0,1,0,1,0,1},
202 {1,0,1,0,1,0,1,0},
203 {0,1,0,1,0,1,0,1},
204 {1,0,1,0,1,0,1,0},
205 {0,1,0,1,0,1,0,1}
208 static void free_userdata_once(GAME g);
209 static void do_more_help(WIN *);
211 void coordofmove(GAME g, char *move, char *prow, char *pcol)
213 char l = strlen(move);
215 if (*move == 'O') {
216 *prow = (g->turn == WHITE) ? 8 : 1;
217 *pcol = (l <= 4) ? 7 : 3;
218 return;
221 move += l;
223 while (!isdigit(*move))
224 move--;
226 *prow = RANKTOINT(*move--);
227 *pcol = FILETOINT(*move);
230 #define INV_INT(x) (9 - x)
231 #define INV_INT0(x) (7 - x)
233 // Posición por rotación de tablero.
234 // Rotation board position.
235 void rotate_position(char *prow, char *pcol)
237 *prow = INV_INT(*prow);
238 *pcol = INV_INT(*pcol);
241 void update_cursor(GAME g, int idx)
243 int t = pgn_history_total(g->hp);
244 struct userdata_s *d = g->data;
247 * If not deincremented then r and c would be the next move.
249 idx--;
251 if (idx > t || idx < 0 || !t || !g->hp[idx]->move)
252 d->c_row = 2, d->c_col = 5;
253 else
254 coordofmove(g, g->hp[idx]->move, &d->c_row, &d->c_col);
256 if (d->mode == MODE_HISTORY && d->rotate)
257 rotate_position(&d->c_row, &d->c_col);
260 static int init_nag()
262 FILE *fp;
263 char line[LINE_MAX];
264 int i = 0;
266 if ((fp = fopen(config.nagfile, "r")) == NULL) {
267 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.nagfile, strerror(errno));
268 return 1;
271 nags = Realloc(nags, (i+2) * sizeof(char *));
272 nags[i++] = strdup(_("none"));
273 nags[i] = NULL;
275 while (!feof(fp)) {
276 if (fscanf(fp, " %[^\n] ", line) == 1) {
277 nags = Realloc(nags, (i + 2) * sizeof(char *));
278 nags[i++] = strdup(line);
282 nags[i] = NULL;
283 nag_total = i;
284 fclose(fp);
285 return 0;
288 void edit_nag_toggle_item(struct menu_input_s *m)
290 struct input_s *in = m->data;
291 struct input_data_s *id = in->data;
292 HISTORY *h = id->data;
293 int i;
295 if (m->selected == 0) {
296 for (i = 0; i < MAX_PGN_NAG; i++)
297 h->nag[i] = 0;
299 for (i = 0; m->items[i]; i++)
300 m->items[i]->selected = 0;
302 return;
305 for (i = 0; i < MAX_PGN_NAG; i++) {
306 if (h->nag[i] == m->selected)
307 h->nag[i] = m->selected = 0;
308 else {
309 if (!h->nag[i]) {
310 h->nag[i] = m->selected;
311 break;
317 void edit_nag_save(struct menu_input_s *m)
319 pushkey = -1;
322 void edit_nag_help(struct menu_input_s *m)
324 message(_("NAG Menu Keys"), ANY_KEY_STR, "%s",
326 " UP/DOWN - previous/next menu item\n"
327 " HOME/END - first/last menu item\n"
328 " PGDN/PGUP - next/previous page\n"
329 " a-zA-Z0-9 - jump to item\n"
330 " SPACE - toggle selected item\n"
331 " CTRL-X - quit with changes"
335 struct menu_item_s **get_nag_items(WIN *win)
337 int i, n;
338 struct menu_input_s *m = win->data;
339 struct input_s *in = m->data;
340 struct input_data_s *id = in->data;
341 struct menu_item_s **items = m->items;
342 HISTORY *h = id->data;
344 if (items) {
345 for (i = 0; items[i]; i++)
346 free(items[i]);
349 for (i = 0; nags[i]; i++) {
350 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
351 items[i] = Malloc(sizeof(struct menu_item_s));
352 items[i]->name = nags[i];
353 items[i]->value = NULL;
355 for (n = 0; n < MAX_PGN_NAG; n++) {
356 if (h->nag[n] == i) {
357 items[i]->selected = 1;
358 n = -1;
359 break;
363 if (n >= 0)
364 items[i]->selected = 0;
367 items[i] = NULL;
368 m->nofree = 1;
369 m->items = items;
370 return items;
373 void nag_print(WIN *win)
375 struct menu_input_s *m = win->data;
377 mvwprintw(win->w, m->print_line, 1, "%-*s", win->cols - 2, m->item->name);
380 void edit_nag(void *arg)
382 struct menu_key_s **keys = NULL;
384 if (!nags) {
385 if (init_nag())
386 return;
389 add_menu_key(&keys, ' ', edit_nag_toggle_item);
390 add_menu_key(&keys, CTRL_KEY('x'), edit_nag_save);
391 add_menu_key(&keys, KEY_F(1), edit_nag_help);
392 construct_menu(0, 0, -1, -1, _("Numeric Annotation Glyphs"), 1, get_nag_items, keys, arg,
393 nag_print, NULL);
394 return;
397 static void *view_nag(void *arg)
399 HISTORY *h = (HISTORY *)arg;
400 char buf[80];
401 char line[LINE_MAX] = {0};
402 int i = 0;
404 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Viewing NAG for"), h->move);
406 if (!nags) {
407 if (init_nag())
408 return NULL;
411 for (i = 0; i < MAX_PGN_NAG; i++) {
412 char buf2[16];
414 if (!h->nag[i])
415 break;
417 if (h->nag[i] >= nag_total)
418 strncat(line, itoa(h->nag[i], buf2), sizeof(line)-1);
419 else
420 strncat(line, nags[h->nag[i]], sizeof(line)-1);
422 strncat(line, "\n", sizeof(line)-1);
425 line[strlen(line) - 1] = 0;
426 message(buf, ANY_KEY_STR, "%s", line);
427 return NULL;
430 void view_annotation(HISTORY *h)
432 char buf[MAX_SAN_MOVE_LEN + strlen(_("Viewing Annotation for")) + 4];
433 int nag = 0, comment = 0;
435 if (!h)
436 return;
438 if (h->comment && h->comment[0])
439 comment++;
441 if (h->nag[0])
442 nag++;
444 if (!nag && !comment)
445 return;
447 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Viewing Annotation for"), h->move);
449 if (comment)
450 construct_message(buf, (nag) ? _("Any other key to continue") : ANY_KEY_STR, 0, 1,
451 (nag) ? _("Press 'n' to view NAG") : NULL,
452 (nag) ? view_nag : NULL, (nag) ? h : NULL, NULL,
453 (nag) ? 'n' : 0, 0, "%s", h->comment);
454 else
455 construct_message(buf, _("Any other key to continue"), 0, 1, _("Press 'n' to view NAG"), view_nag, h, NULL,
456 'n', 0, "%s", _("No comment text for this move"));
459 int do_game_write(char *filename, char *mode, int start, int end)
461 int i;
462 struct userdata_s *d;
463 PGN_FILE *pgn;
465 i = pgn_open(filename, mode, &pgn);
467 if (i == E_PGN_ERR) {
468 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", filename, strerror(errno));
469 return 1;
471 else if (i == E_PGN_INVALID) {
472 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", filename, _("Not a regular file"));
473 return 1;
476 for (i = (start == -1) ? 0 : start; i < end; i++) {
477 d = game[i]->data;
478 pgn_write(pgn, game[i]);
479 CLEAR_FLAG(d->flags, CF_MODIFIED);
482 if (pgn_close(pgn) != E_PGN_OK)
483 message(ERROR_STR, ANY_KEY_STR, "%s", strerror(errno));
485 if (start == -1) {
486 strncpy(loadfile, filename, sizeof(loadfile));
487 loadfile[sizeof(loadfile)-1] = 0;
490 return 0;
493 struct save_game_s {
494 char *filename;
495 char *mode;
496 int start;
497 int end;
500 void do_save_game_overwrite_confirm(WIN *win)
502 char *mode = "w";
503 struct save_game_s *s = win->data;
504 wchar_t str[] = { win->c, 0 };
506 if (!wcscmp (str, append_wchar))
507 mode = "a";
508 else if (!wcscmp (str, overwrite_wchar))
509 mode = "w";
510 else
511 goto done;
513 if (do_game_write(s->filename, mode, s->start, s->end))
514 update_status_notify(gp, "%s", _("Save game failed."));
515 else
516 update_status_notify(gp, "%s", _("Game saved."));
518 done:
519 free(s->filename);
520 free(s);
523 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
524 * game index number.
526 void save_pgn(char *filename, int saveindex)
528 char buf[FILENAME_MAX];
529 struct stat st;
530 int end = (saveindex == -1) ? gtotal : saveindex + 1;
531 struct save_game_s *s;
533 if (filename[0] != '/' && config.savedirectory) {
534 if (stat(config.savedirectory, &st) == -1) {
535 if (errno == ENOENT) {
536 if (mkdir(config.savedirectory, 0755) == -1) {
537 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
538 strerror(errno));
539 return;
542 else {
543 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
544 strerror(errno));
545 return;
549 if (stat(config.savedirectory, &st) == -1) {
550 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
551 strerror(errno));
552 return;
555 if (!S_ISDIR(st.st_mode)) {
556 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory, _("Not a directory."));
557 return;
560 snprintf(buf, sizeof(buf), "%s/%s", config.savedirectory, filename);
561 filename = buf;
564 if (access(filename, W_OK) == 0) {
565 s = Malloc(sizeof(struct save_game_s));
566 s->filename = strdup(filename);
567 s->start = saveindex;
568 s->end = end;
569 construct_message(NULL, _("What would you like to do?"), 0, 1, NULL,
570 NULL, s, do_save_game_overwrite_confirm, 0, 0,
571 "%s \"%s\"\nPress \"%ls\" to append to this file, \"%ls\" to overwrite or any other key to cancel.",
572 _("File exists:"), filename, append_wchar,
573 overwrite_wchar);
574 return;
577 if (do_game_write(filename, "a", saveindex, end))
578 update_status_notify(gp, "%s", _("Save game failed."));
579 else
580 update_status_notify(gp, "%s", _("Game saved."));
583 static int castling_state(GAME g, BOARD b, int row, int col, int piece, int mod)
585 if (pgn_piece_to_int(piece) == ROOK && col == 7
586 && row == 7 &&
587 (TEST_FLAG(g->flags, GF_WK_CASTLE) || mod) &&
588 pgn_piece_to_int(b[7][4].icon) == KING && isupper(piece)) {
589 if (mod)
590 TOGGLE_FLAG(g->flags, GF_WK_CASTLE);
591 return 1;
593 else if (pgn_piece_to_int(piece) == ROOK && col == 0
594 && row == 7 &&
595 (TEST_FLAG(g->flags, GF_WQ_CASTLE) || mod) &&
596 pgn_piece_to_int(b[7][4].icon) == KING && isupper(piece)) {
597 if (mod)
598 TOGGLE_FLAG(g->flags, GF_WQ_CASTLE);
599 return 1;
601 else if (pgn_piece_to_int(piece) == ROOK && col == 7
602 && row == 0 &&
603 (TEST_FLAG(g->flags, GF_BK_CASTLE) || mod) &&
604 pgn_piece_to_int(b[0][4].icon) == KING && islower(piece)) {
605 if (mod)
606 TOGGLE_FLAG(g->flags, GF_BK_CASTLE);
607 return 1;
609 else if (pgn_piece_to_int(piece) == ROOK && col == 0
610 && row == 0 &&
611 (TEST_FLAG(g->flags, GF_BQ_CASTLE) || mod) &&
612 pgn_piece_to_int(b[0][4].icon) == KING && islower(piece)) {
613 if (mod)
614 TOGGLE_FLAG(g->flags, GF_BQ_CASTLE);
615 return 1;
617 else if (pgn_piece_to_int(piece) == KING && col == 4
618 && row == 7 &&
619 (mod || (pgn_piece_to_int(b[7][7].icon) == ROOK &&
620 TEST_FLAG(g->flags, GF_WK_CASTLE))
622 (pgn_piece_to_int(b[7][0].icon) == ROOK &&
623 TEST_FLAG(g->flags, GF_WQ_CASTLE))) && isupper(piece)) {
624 if (mod) {
625 if (TEST_FLAG(g->flags, GF_WK_CASTLE) ||
626 TEST_FLAG(g->flags, GF_WQ_CASTLE))
627 CLEAR_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE);
628 else
629 SET_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE);
631 return 1;
633 else if (pgn_piece_to_int(piece) == KING && col == 4
634 && row == 0 &&
635 (mod || (pgn_piece_to_int(b[0][7].icon) == ROOK &&
636 TEST_FLAG(g->flags, GF_BK_CASTLE))
638 (pgn_piece_to_int(b[0][0].icon) == ROOK &&
639 TEST_FLAG(g->flags, GF_BQ_CASTLE))) && islower(piece)) {
640 if (mod) {
641 if (TEST_FLAG(g->flags, GF_BK_CASTLE) ||
642 TEST_FLAG(g->flags, GF_BQ_CASTLE))
643 CLEAR_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE);
644 else
645 SET_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE);
647 return 1;
650 return 0;
653 #define IS_ENPASSANT(c) (c == 'x') ? CP_BOARD_ENPASSANT : isupper(c) ? CP_BOARD_WHITE : CP_BOARD_BLACK
654 #define ATTRS(cp) (cp & (A_BOLD|A_STANDOUT|A_BLINK|A_DIM|A_UNDERLINE|A_INVIS|A_REVERSE))
656 static void
657 init_wchar_pieces ()
659 w_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♙" : "P");
660 w_rook_wchar = str_to_wchar (config.utf8_pieces ? "♖" : "R");
661 w_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♗" : "B");
662 w_knight_wchar = str_to_wchar (config.utf8_pieces ? "♘" : "N");
663 w_queen_wchar = str_to_wchar (config.utf8_pieces ? "♕" : "Q");
664 w_king_wchar = str_to_wchar (config.utf8_pieces ? "♔" : "K");
665 b_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♟" : "p");
666 b_rook_wchar = str_to_wchar (config.utf8_pieces ? "♜" : "r");
667 b_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♝" : "b");
668 b_knight_wchar = str_to_wchar (config.utf8_pieces ? "♞" : "n");
669 b_queen_wchar = str_to_wchar (config.utf8_pieces ? "♛" : "q");
670 b_king_wchar = str_to_wchar (config.utf8_pieces ? "♚" : "k");
671 empty_wchar = str_to_wchar (" ");
672 enpassant_wchar = str_to_wchar ("x");
675 static wchar_t *
676 piece_to_wchar (unsigned char p)
678 switch (p)
680 case 'P':
681 return w_pawn_wchar;
682 case 'p':
683 return b_pawn_wchar;
684 case 'R':
685 return w_rook_wchar;
686 case 'r':
687 return b_rook_wchar;
688 case 'B':
689 return w_bishop_wchar;
690 case 'b':
691 return b_bishop_wchar;
692 case 'N':
693 return w_knight_wchar;
694 case 'n':
695 return b_knight_wchar;
696 case 'Q':
697 return w_queen_wchar;
698 case 'q':
699 return b_queen_wchar;
700 case 'K':
701 return w_king_wchar;
702 case 'k':
703 return b_king_wchar;
704 case 'x':
705 return enpassant_wchar;
708 return empty_wchar;
711 static int piece_can_attack (GAME g, int rank, int file)
713 struct userdata_s *d = g->data;
714 char *m, *frfr = NULL;
715 pgn_error_t e;
716 int row, col, p, v, pi, cpi;
718 if (d->rotate) {
719 rotate_position(&d->c_row, &d->c_col);
720 rotate_position(&d->sp.srow, &d->sp.scol);
723 v = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].valid;
724 pi = pgn_piece_to_int (d->b[RANKTOBOARD (rank)][FILETOBOARD (file)].icon);
725 cpi = d->sp.icon
726 ? pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon)
727 : pgn_piece_to_int (d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon);
729 if (pi == OPEN_SQUARE || cpi == OPEN_SQUARE || !VALIDFILE (file)
730 || !VALIDRANK (rank)) {
731 if (d->rotate) {
732 rotate_position(&d->c_row, &d->c_col);
733 rotate_position(&d->sp.srow, &d->sp.scol);
736 return 0;
739 if (d->sp.icon) {
740 col = v ? d->c_col : d->sp.scol;
741 row = v ? d->c_row : d->sp.srow;
743 else {
744 col = d->c_col;
745 row = d->c_row;
748 m = malloc(MAX_SAN_MOVE_LEN+1);
749 m[0] = INTTOFILE (file);
750 m[1] = INTTORANK (rank);
751 m[2] = INTTOFILE (col);
752 m[3] = INTTORANK (row);
753 m[4] = 0;
755 if (d->sp.icon && v) {
756 BOARD b;
758 memcpy (b, d->b, sizeof(BOARD));
759 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
760 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
761 pgn_int_to_piece (WHITE, OPEN_SQUARE);
762 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
763 pgn_switch_turn(g);
764 e = pgn_validate_move (g, b, &m, &frfr);
765 pgn_switch_turn (g);
766 free (m);
767 free (frfr);
769 if (e != E_PGN_OK && pgn_piece_to_int (d->sp.icon) == PAWN) {
770 int n = (d->sp.srow == 7 && rank == 5) ? 6 :
771 (d->sp.srow == 2 && rank == 4) ? 3 : 0;
773 if (n && (file == d->c_col-1 || file == d->c_col+1)) {
774 memcpy (b, d->b, sizeof(BOARD));
775 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
776 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
777 pgn_int_to_piece (WHITE, OPEN_SQUARE);
778 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
779 b[RANKTOBOARD (n)][FILETOBOARD (d->sp.scol)].enpassant = 1;
780 m = malloc(MAX_SAN_MOVE_LEN+1);
781 m[0] = INTTOFILE (file);
782 m[1] = INTTORANK (rank);
783 m[2] = INTTOFILE (col);
784 m[3] = INTTORANK (n);
785 m[4] = 0;
786 pgn_switch_turn(g);
787 SET_FLAG (g->flags, GF_ENPASSANT);
788 e = pgn_validate_move (g, b, &m, &frfr);
789 CLEAR_FLAG (g->flags, GF_ENPASSANT);
790 pgn_switch_turn (g);
791 free (m);
792 free (frfr);
796 goto pca_quit;
799 pgn_switch_turn(g);
800 e = pgn_validate_move (g, d->b, &m, &frfr);
801 pgn_switch_turn (g);
803 if (!strcmp (m, "O-O") || !strcmp (m, "O-O-O"))
804 e = E_PGN_INVALID;
806 if (e == E_PGN_OK) {
807 int sf = FILETOINT (frfr[0]), sr = RANKTOINT (frfr[1]);
808 int df = FILETOINT (frfr[2]);
810 pi = d->b[RANKTOBOARD (sr)][FILETOBOARD (sf)].icon;
811 pi = pgn_piece_to_int (pi);
812 if (pi == PAWN && sf == df)
813 e = E_PGN_INVALID;
816 free (m);
817 free (frfr);
819 pca_quit:
820 if (d->rotate) {
821 rotate_position(&d->c_row, &d->c_col);
822 rotate_position(&d->sp.srow, &d->sp.scol);
825 return e == E_PGN_OK ? 1 : 0;
828 void print_piece(WINDOW *w, int l, int c, char p)
830 int i, y, ff = 0;
832 for (i = 0; i < 13; i += 2) {
833 if (p == piece_chars[i] || p == piece_chars[i + 1]) {
834 for (y = 0; y < 3; y++)
835 mvwprintw(w, l + y, c, "%s", f_pieces[i + ff + y]);
836 return;
839 ff++;
842 for (y = 0; y < 3; y++)
843 mvwprintw(w, l + y, c, f_pieces[0]);
846 void board_prev_move_play (GAME g)
848 struct userdata_s *d = g->data;
849 char l = strlen(d->pm_frfr);
851 if (l) {
852 char q = (l > 4) ? 2 : 1;
854 d->pm_row = RANKTOINT(d->pm_frfr[l - q++]);
855 d->pm_col = FILETOINT(d->pm_frfr[l - q++]);
856 d->ospm_row = RANKTOINT(d->pm_frfr[l - q++]);
857 d->ospm_col = FILETOINT(d->pm_frfr[l - q]);
858 if (d->rotate) {
859 rotate_position(&d->pm_row, &d->pm_col);
860 rotate_position(&d->ospm_row, &d->ospm_col);
863 else {
864 d->pm_row = 0;
865 d->pm_col = 0;
866 d->ospm_row = 0;
867 d->ospm_col = 0;
871 void board_prev_move_history (GAME g)
873 struct userdata_s *d = g->data;
875 if (g->hindex) {
876 char *move = g->hp[g->hindex - 1]->move;
878 if (move) {
879 if (d->mode == MODE_PLAY)
880 coordofmove(g, move, &d->pm_row, &d->pm_col);
881 else {
882 d->pm_row = 0;
883 d->pm_col = 0;
886 if (*move == 'O') {
887 d->ospm_row = g->turn == WHITE ? 8 : 1;
888 d->ospm_col = 5;
889 return;
892 BOARD ob;
893 unsigned char f, r;
895 pgn_board_init(ob);
897 if (g->hindex > 1) {
898 HISTORY *h = pgn_history_by_n(g->hp, g->hindex - 2);
899 if (h) {
900 pgn_board_init_fen(g, ob, h->fen);
901 pgn_switch_turn(g);
905 for (f = 0; f < 8; f++) {
906 for (r = 0; r < 8; r++) {
907 if (ob[f][r].icon != '.' && d->b[f][r].icon == '.') {
908 d->ospm_row = INV_INT0(f) + 1;
909 d->ospm_col = r + 1;
910 break;
915 if (d->rotate) {
916 if (d->mode == MODE_PLAY)
917 rotate_position(&d->pm_row, &d->pm_col);
919 rotate_position(&d->ospm_row, &d->ospm_col);
922 return;
925 else {
926 d->ospm_row = 0;
927 d->ospm_col = 0;
928 d->pm_row = 0;
929 d->pm_col = 0;
933 static int is_the_square (int brow, int bcol, int row, int col,
934 int prow, int pcol)
936 if ((!BIG_BOARD && row == ROWTOMATRIX(prow) && col == COLTOMATRIX(pcol))
937 || (BIG_BOARD && brow + 1 == INV_INT(prow) && bcol + 1 == pcol))
938 return 1;
940 return 0;
943 static int is_prev_move (struct userdata_s *d, int brow, int bcol,
944 int row, int col)
946 if (is_the_square (brow, bcol, row, col, d->pm_row, d->pm_col))
947 return 1;
949 return 0;
952 void update_board_window(GAME g)
954 int row, col;
955 int bcol = 0, brow = 0;
956 int l = config.coordsyleft;
957 int maxy = BOARD_HEIGHT, maxx = BOARD_WIDTH;
958 int ncols = 0, offset = 1;
959 int rowr = (MEGA_BOARD) ? 6 : (BIG_BOARD) ? 4 : 2;
960 int colr = (MEGA_BOARD) ? 12 : (BIG_BOARD) ? 8 : 4;
961 unsigned coords_y = 8, cxgc = 0;
962 unsigned i, cpd = 0;
963 struct userdata_s *d = g->data;
965 if (config.bprevmove && d->mode != MODE_EDIT) {
966 if (!d->pm_undo && d->mode == MODE_PLAY)
967 board_prev_move_play(g);
968 else
969 board_prev_move_history(g);
971 else {
972 d->pm_row = 0;
973 d->pm_col = 0;
974 d->ospm_row = 0;
975 d->ospm_col = 0;
978 if (d->mode != MODE_PLAY && d->mode != MODE_EDIT)
979 update_cursor(g, g->hindex);
981 if (BIG_BOARD) {
982 if (d->rotate) {
983 brow = 7;
984 coords_y = 1;
987 else {
988 if (d->rotate) {
989 brow = 1;
990 coords_y = 1;
992 else
993 brow = 8;
996 for (row = 0; row < maxy; row++) {
997 if (BIG_BOARD) {
998 if (d->rotate)
999 bcol = 7;
1000 else
1001 bcol = 0;
1003 else {
1004 if (d->rotate)
1005 bcol = 8;
1006 else
1007 bcol = 1;
1010 for (col = 0; col < maxx; col++) {
1011 int attrwhich = -1;
1012 chtype attrs = 0, old_attrs = 0;
1013 unsigned char p;
1014 int can_attack = 0;
1015 int valid = 0;
1017 if (row == 0 || row == maxy - 2) {
1018 if (col == 0)
1019 mvwaddch(boardw, row, col + l,
1020 LINE_GRAPHIC((row)
1021 ? ACS_LLCORNER | CP_BOARD_GRAPHICS
1022 : ACS_ULCORNER | CP_BOARD_GRAPHICS));
1023 else if (col == maxx - 2)
1024 mvwaddch(boardw, row, col + l,
1025 LINE_GRAPHIC((row)
1026 ? ACS_LRCORNER | CP_BOARD_GRAPHICS
1027 : ACS_URCORNER | CP_BOARD_GRAPHICS));
1028 else if (!(col % colr))
1029 mvwaddch(boardw, row, col + l,
1030 LINE_GRAPHIC((row)
1031 ? ACS_BTEE | CP_BOARD_GRAPHICS
1032 : ACS_TTEE | CP_BOARD_GRAPHICS));
1033 else {
1034 if (col != maxx - 1)
1035 mvwaddch(boardw, row, col + l,
1036 LINE_GRAPHIC(ACS_HLINE | CP_BOARD_GRAPHICS));
1039 continue;
1042 if ((row % 2) && col == maxx - 1 &&
1043 (coords_y > 0 && coords_y < 9)) {
1044 wattron(boardw, CP_BOARD_COORDS);
1045 mvwprintw(boardw,
1046 (BIG_BOARD) ? row * ((MEGA_BOARD) ? 3 : 2)
1047 : row, (l) ? 0 : col, "%d",
1048 (d->rotate) ? coords_y++ : coords_y--);
1049 wattroff(boardw, CP_BOARD_COORDS);
1050 continue;
1053 if ((col == 0 || col == maxx - 2) && row != maxy - 1) {
1054 if (!(row % rowr))
1055 mvwaddch(boardw, row, col + l,
1056 LINE_GRAPHIC((col) ?
1057 ACS_RTEE | CP_BOARD_GRAPHICS :
1058 ACS_LTEE | CP_BOARD_GRAPHICS));
1059 else
1060 mvwaddch(boardw, row, col + l,
1061 LINE_GRAPHIC(ACS_VLINE | CP_BOARD_GRAPHICS));
1063 continue;
1066 if ((row % rowr) && !(col % colr) && row != maxy - 1) {
1067 mvwaddch(boardw, row, col + l,
1068 LINE_GRAPHIC(ACS_VLINE | CP_BOARD_GRAPHICS));
1069 continue;
1072 if (!(col % colr) && row != maxy - 1) {
1073 mvwaddch(boardw, row, col + l,
1074 LINE_GRAPHIC(ACS_PLUS | CP_BOARD_GRAPHICS));
1075 continue;
1078 if ((row % rowr)) {
1079 if ((col % colr)) {
1080 if (BIG_BOARD)
1081 attrwhich = (cb[brow][bcol]) ? WHITE : BLACK;
1082 else {
1083 if (ncols++ == 8) {
1084 offset++;
1085 ncols = 1;
1088 if (((ncols % 2) && !(offset % 2))
1089 || (!(ncols % 2) && (offset % 2)))
1090 attrwhich = BLACK;
1091 else
1092 attrwhich = WHITE;
1095 if (BIG_BOARD && d->rotate) {
1096 brow = INV_INT0(brow);
1097 bcol = INV_INT0(bcol);
1100 if (BIG_BOARD)
1101 p = d->b[brow][bcol].icon;
1102 else
1103 p = d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].icon;
1105 int pi = pgn_piece_to_int(p);
1107 if (config.details &&
1108 ((!BIG_BOARD && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].enpassant)
1109 || (BIG_BOARD && d->b[brow][bcol].enpassant))) {
1110 p = pi = 'x';
1111 attrs = mix_cp(CP_BOARD_ENPASSANT,
1112 (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK,
1113 ATTRS(CP_BOARD_ENPASSANT), A_FG_B_BG);
1116 if (config.showattacks && config.details
1117 && piece_can_attack (g,
1118 BIG_BOARD ? INV_INT0(brow)+1 : brow,
1119 BIG_BOARD ? bcol+1 : bcol)) {
1120 attrs = CP_BOARD_ATTACK;
1121 old_attrs = attrs;
1122 can_attack = 1;
1125 if (config.validmoves &&
1126 ((!BIG_BOARD && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].valid)
1127 || (BIG_BOARD && d->b[brow][bcol].valid))) {
1128 old_attrs = -1;
1129 valid = 1;
1131 if (attrwhich == WHITE)
1132 attrs = mix_cp(CP_BOARD_MOVES_WHITE,
1133 IS_ENPASSANT(p),
1134 ATTRS(CP_BOARD_MOVES_WHITE),
1135 B_FG_A_BG);
1136 else
1137 attrs = mix_cp(CP_BOARD_MOVES_BLACK,
1138 IS_ENPASSANT(p),
1139 ATTRS(CP_BOARD_MOVES_BLACK),
1140 B_FG_A_BG);
1142 else if (p != 'x' && !can_attack)
1143 attrs = (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK;
1145 if (BIG_BOARD && d->rotate) {
1146 brow = INV_INT0(brow);
1147 bcol = INV_INT0(bcol);
1150 if (is_the_square (brow, bcol, row, col, d->c_row,
1151 d->c_col)) {
1152 attrs = mix_cp(CP_BOARD_CURSOR, IS_ENPASSANT(p),
1153 ATTRS(CP_BOARD_CURSOR), B_FG_A_BG);
1154 old_attrs = -1;
1156 else if (is_the_square (brow, bcol, row, col,
1157 d->sp.srow, d->sp.scol)) {
1158 attrs = mix_cp(CP_BOARD_SELECTED, IS_ENPASSANT(p),
1159 ATTRS(CP_BOARD_SELECTED), B_FG_A_BG);
1160 old_attrs = -1;
1162 else if ((is_prev_move (d, brow, bcol, row, col) && !valid)
1163 || (is_the_square (brow, bcol, row, col,
1164 d->ospm_row, d->ospm_col))) {
1165 attrs = mix_cp(CP_BOARD_PREVMOVE, IS_ENPASSANT(p),
1166 ATTRS(CP_BOARD_PREVMOVE), B_FG_A_BG);
1167 old_attrs = -1;
1170 if (row == maxy - 1)
1171 attrs = 0;
1173 if (can_attack) {
1174 int n = is_prev_move (d, brow, bcol, row, col);
1175 chtype a = n && !valid
1176 ? CP_BOARD_PREVMOVE : attrwhich == WHITE ? valid ? CP_BOARD_MOVES_WHITE : CP_BOARD_WHITE : valid ? CP_BOARD_MOVES_BLACK : CP_BOARD_BLACK;
1177 attrs = mix_cp(CP_BOARD_ATTACK, a,
1178 ATTRS (CP_BOARD_ATTACK), A_FG_B_BG);
1179 old_attrs = -1;
1182 if (BIG_BOARD)
1183 wmove(boardw, row, col + ((MEGA_BOARD) ? 5 : 3) + l);
1184 else
1185 mvwaddch(boardw, row, col + l, ' ' | attrs);
1187 if (row == maxy - 1 && cxgc < 8) {
1188 waddch(boardw, "abcdefgh"[(BIG_BOARD) ? bcol : bcol - 1] | CP_BOARD_COORDS);
1189 cxgc++;
1191 else {
1192 if (old_attrs == -1) {
1193 old_attrs = attrs;
1194 goto printc;
1197 old_attrs = attrs;
1199 if (pi != OPEN_SQUARE && p != 'x' && !can_attack) {
1200 if (attrwhich == WHITE) {
1201 if (isupper(p))
1202 attrs = CP_BOARD_W_W;
1203 else
1204 attrs = CP_BOARD_W_B;
1206 else {
1207 if (isupper(p))
1208 attrs = CP_BOARD_B_W;
1209 else
1210 attrs = CP_BOARD_B_B;
1214 printc:
1215 if (BIG_BOARD) {
1216 if (config.details && !can_attack
1217 && castling_state(g, d->b,
1218 (d->rotate) ? INV_INT(brow + 1) - 1: brow,
1219 (d->rotate) ? INV_INT(bcol + 1) - 1: bcol, p, 0))
1220 attrs = mix_cp(CP_BOARD_CASTLING, attrs,
1221 ATTRS(CP_BOARD_CASTLING),
1222 A_FG_B_BG);
1224 else {
1225 if (config.details && !can_attack
1226 && castling_state(g, d->b, RANKTOBOARD (brow),
1227 FILETOBOARD (bcol), p, 0)) {
1228 attrs = mix_cp(CP_BOARD_CASTLING, attrs,
1229 ATTRS(CP_BOARD_CASTLING),
1230 A_FG_B_BG);
1234 if (BIG_BOARD) {
1235 // FIXME: Reimpresión de piezas(+4).
1236 if (cpd < 67) {
1237 wattron (boardw, attrs);
1238 if (MEGA_BOARD){
1239 for (i = 0; i < 5; i++)
1240 mvwprintw(boardw, i + brow * 6 + 1,
1241 bcol * 12 + 1 + l,
1242 " ");
1243 if (pi != OPEN_SQUARE)
1244 print_piece(boardw, brow * 6 + 2,
1245 bcol * 12 + 3 + l, p);
1247 else {
1248 print_piece(boardw, brow * 4 + 1,
1249 bcol * 8 + 1 + l,
1250 (pi != OPEN_SQUARE) ? p : 0);
1253 wattroff (boardw, attrs);
1254 cpd++;
1257 else {
1258 wattron (boardw, attrs);
1259 waddwstr (boardw, piece_to_wchar (pi != OPEN_SQUARE ? p : 0));
1260 wattroff (boardw, attrs);
1263 attrs = old_attrs;
1266 if (BIG_BOARD)
1267 col += (MEGA_BOARD) ? 10 : 6;
1268 else {
1269 waddch(boardw, ' ' | attrs);
1270 col += 2;
1273 if (d->rotate)
1274 bcol--;
1275 else
1276 bcol++;
1278 if (BIG_BOARD) {
1279 if (bcol > 7)
1280 bcol = 0;
1281 if (bcol < 0)
1282 bcol = 7;
1286 else {
1287 if (col != maxx - 1)
1288 mvwaddch(boardw, row, col + l,
1289 LINE_GRAPHIC(ACS_HLINE | CP_BOARD_GRAPHICS));
1293 if (row % rowr) {
1294 if (d->rotate)
1295 brow++;
1296 else
1297 brow--;
1300 if (BIG_BOARD) {
1301 if (brow > 7)
1302 brow = 0;
1303 if (brow < 0)
1304 brow = 7;
1309 void invalid_move(int n, int e, const char *m)
1311 if (curses_initialized)
1312 cmessage(ERROR_STR, ANY_KEY_STR, "%s \"%s\" (round #%i)", (e == E_PGN_AMBIGUOUS)
1313 ? _("Ambiguous move") : _("Invalid move"), m, n);
1314 else
1315 warnx("%s: %s \"%s\" (round #%i)", loadfile, (e == E_PGN_AMBIGUOUS)
1316 ? _("Ambiguous move") : _("Invalid move"), m, n);
1319 void gameover(GAME g)
1321 struct userdata_s *d = g->data;
1323 SET_FLAG(g->flags, GF_GAMEOVER);
1324 d->mode = MODE_HISTORY;
1325 stop_engine(g);
1328 static void update_clock(GAME g, struct itimerval it)
1330 struct userdata_s *d = g->data;
1332 if (TEST_FLAG(d->flags, CF_CLOCK) && g->turn == WHITE) {
1333 d->wclock.elapsed.tv_sec += it.it_value.tv_sec;
1334 d->wclock.elapsed.tv_usec += it.it_value.tv_usec;
1336 if (d->wclock.elapsed.tv_usec > 1000000 - 1) {
1337 d->wclock.elapsed.tv_sec += d->wclock.elapsed.tv_usec / 1000000;
1338 d->wclock.elapsed.tv_usec = d->wclock.elapsed.tv_usec % 1000000;
1341 if (d->wclock.tc[d->wclock.tcn][1] &&
1342 d->wclock.elapsed.tv_sec >= d->wclock.tc[d->wclock.tcn][1]) {
1343 pgn_tag_add(&g->tag, "Result", "0-1");
1344 gameover(g);
1347 else if (TEST_FLAG(d->flags, CF_CLOCK) && g->turn == BLACK) {
1348 d->bclock.elapsed.tv_sec += it.it_value.tv_sec;
1349 d->bclock.elapsed.tv_usec += it.it_value.tv_usec;
1351 if (d->bclock.elapsed.tv_usec > 1000000 - 1) {
1352 d->bclock.elapsed.tv_sec += d->bclock.elapsed.tv_usec / 1000000;
1353 d->bclock.elapsed.tv_usec = d->bclock.elapsed.tv_usec % 1000000;
1356 if (d->bclock.tc[d->bclock.tcn][1] &&
1357 d->bclock.elapsed.tv_sec >= d->bclock.tc[d->bclock.tcn][1]) {
1358 pgn_tag_add(&g->tag, "Result", "1-0");
1359 gameover(g);
1363 d->elapsed.tv_sec += it.it_value.tv_sec;
1364 d->elapsed.tv_usec += it.it_value.tv_usec;
1366 if (d->elapsed.tv_usec > 1000000 - 1) {
1367 d->elapsed.tv_sec += d->elapsed.tv_usec / 1000000;
1368 d->elapsed.tv_usec = d->elapsed.tv_usec % 1000000;
1372 static void update_time_control(GAME g)
1374 struct userdata_s *d = g->data;
1375 struct clock_s *clk = (g->turn == WHITE) ? &d->wclock : &d->bclock;
1377 if (clk->incr)
1378 clk->tc[clk->tcn][1] += clk->incr;
1380 if (!clk->tc[clk->tcn][1])
1381 return;
1383 clk->move++;
1385 if (!clk->tc[clk->tcn][0] || clk->move >= clk->tc[clk->tcn][0]) {
1386 clk->move = 0;
1387 clk->tc[clk->tcn + 1][1] += abs(clk->elapsed.tv_sec - clk->tc[clk->tcn][1]);
1388 memset(&clk->elapsed, 0, sizeof(clk->elapsed));
1389 clk->tcn++;
1393 void update_history_window(GAME g)
1395 char buf[HISTORY_WIDTH - 1];
1396 HISTORY *h = NULL;
1397 int n, total;
1398 int t = pgn_history_total(g->hp);
1400 n = (g->hindex + 1) / 2;
1402 if (t % 2)
1403 total = (t + 1) / 2;
1404 else
1405 total = t / 2;
1407 if (t)
1408 snprintf(buf, sizeof(buf), "%u %s %u%s", n, _("of"), total,
1409 (movestep == 1) ? _(" (ply)") : "");
1410 else
1411 strncpy(buf, _("not available"), sizeof(buf)-1);
1413 buf[sizeof(buf)-1] = 0;
1414 mvwprintw(historyw, 2, 1, "%*s %-*s", 10, _("Move:"),
1415 HISTORY_WIDTH - 14, buf);
1417 h = pgn_history_by_n(g->hp, g->hindex);
1418 snprintf(buf, sizeof(buf), "%s",
1419 (h && h->move) ? h->move
1420 : (LINES < 24) ? _("empty") : _("not available"));
1421 n = 0;
1423 if (h && ((h->comment) || h->nag[0])) {
1424 strncat(buf, _(" (Annotated"), sizeof(buf)-1);
1425 n++;
1428 if (h && h->rav) {
1429 strncat(buf, (n) ? ",+" : " (+", sizeof(buf)-1);
1430 n++;
1433 if (g->ravlevel) {
1434 strncat(buf, (n) ? ",-" : " (-", sizeof(buf)-1);
1435 n++;
1438 if (n)
1439 strncat(buf, ")", sizeof(buf)-1);
1441 mvwprintw(historyw, 3, ((LINES < 24) ? 17 : 1), "%s %-*s",
1442 (LINES < 24) ? _("Next:") :_("Next move:"),
1443 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1445 h = pgn_history_by_n(g->hp, g->hindex - 1);
1446 snprintf(buf, sizeof(buf), "%s",
1447 (h && h->move) ? h->move
1448 : (LINES < 24) ? _("empty") : _("not available"));
1449 n = 0;
1451 if (h && ((h->comment) || h->nag[0])) {
1452 strncat(buf, _(" (Annotated"), sizeof(buf)-1);
1453 n++;
1456 if (h && h->rav) {
1457 strncat(buf, (n) ? ",+" : " (+", sizeof(buf)-1);
1458 n++;
1461 if (g->ravlevel) {
1462 strncat(buf, (n) ? ",-" : " (-", sizeof(buf)-1);
1463 n++;
1466 if (n)
1467 strncat(buf, ")", sizeof(buf)-1);
1469 mvwprintw(historyw, ((LINES < 24) ? 3 : 4), 1, "%s %-*s",
1470 (LINES < 24) ? _("Prev.:") : _("Prev move:"),
1471 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1474 void do_validate_move(char **move)
1476 struct userdata_s *d = gp->data;
1477 int n;
1478 char *frfr = NULL;
1480 if (TEST_FLAG(d->flags, CF_HUMAN)) {
1481 if ((n = pgn_parse_move(gp, d->b, move, &frfr)) != E_PGN_OK) {
1482 invalid_move(d->n + 1, n, *move);
1483 return;
1486 strcpy(d->pm_frfr, frfr);
1487 update_time_control(gp);
1488 pgn_history_add(gp, d->b, *move);
1489 pgn_switch_turn(gp);
1491 else {
1492 if ((n = pgn_validate_move(gp, d->b, move, &frfr)) != E_PGN_OK) {
1493 invalid_move(d->n + 1, n, *move);
1494 return;
1497 add_engine_command(gp, ENGINE_THINKING, "%s\n",
1498 (config.engine_protocol == 1) ? frfr : *move);
1501 d->sp.srow = d->sp.scol = d->sp.icon = 0;
1503 if (config.validmoves)
1504 pgn_reset_valid_moves(d->b);
1506 if (TEST_FLAG(gp->flags, GF_GAMEOVER))
1507 d->mode = MODE_HISTORY;
1508 else
1509 SET_FLAG(d->flags, CF_MODIFIED);
1511 free (frfr);
1512 d->paused = 0;
1513 update_history_window(gp);
1514 update_board_window(gp);
1515 return;
1518 void do_promotion_piece_finalize(WIN *win)
1520 char *p, *str = win->data;
1522 if (pgn_piece_to_int(win->c) == -1)
1523 return;
1525 p = str + strlen(str);
1526 *p++ = toupper(win->c);
1527 *p = '\0';
1528 do_validate_move(&str);
1529 free (str);
1530 win->data = NULL;
1533 static void move_to_engine(GAME g)
1535 struct userdata_s *d = g->data;
1536 char *str;
1537 int piece;
1539 if (config.validmoves &&
1540 !d->b[RANKTOBOARD(d->sp.row)][FILETOBOARD(d->sp.col)].valid)
1541 return;
1543 str = Malloc(MAX_SAN_MOVE_LEN + 1);
1544 snprintf(str, MAX_SAN_MOVE_LEN + 1, "%c%i%c%i",
1545 _("abcdefgh")[d->sp.scol - 1],
1546 d->sp.srow, _("abcdefgh")[d->sp.col - 1], d->sp.row);
1548 piece = pgn_piece_to_int(d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon);
1550 if (piece == PAWN && (d->sp.row == 8 || d->sp.row == 1)) {
1551 construct_message(_("Select Pawn Promotion Piece"), _ ("R/N/B/Q"), 1, 1, NULL, NULL,
1552 str, do_promotion_piece_finalize, 0, 0, "%s", _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1553 return;
1556 do_validate_move(&str);
1557 free (str);
1560 static char *clock_to_char(long n)
1562 static char buf[16];
1563 int h = 0, m = 0, s = 0;
1565 h = n / 3600;
1566 m = (n % 3600) / 60;
1567 s = (n % 3600) % 60;
1568 snprintf(buf, sizeof(buf), "%.2i:%.2i:%.2i", h, m, s);
1569 return buf;
1572 static char *timeval_to_char(struct timeval t, long limit)
1574 static char buf[9];
1575 int h = 0, m = 0, s = 0;
1576 int n = limit ? abs(limit - t.tv_sec) : 0;
1578 h = n / 3600;
1579 m = (n % 3600) / 60;
1580 s = (n % 3600) % 60;
1581 snprintf(buf, sizeof(buf), "%.2i:%.2i:%.2i", h, m, s);
1582 return buf;
1585 static char *time_control_status(struct clock_s *clk)
1587 static char buf[80] = {0};
1589 buf[0] = 0;
1591 if (clk->tc[clk->tcn][0] && clk->tc[clk->tcn + 1][1])
1592 snprintf(buf, sizeof(buf), " M%.2i/%s", abs(clk->tc[clk->tcn][0] - clk->move),
1593 clock_to_char(clk->tc[clk->tcn + 1][1]));
1594 else if (!clk->incr)
1595 return "";
1597 if (clk->incr) {
1598 char buf[16];
1599 strncat(buf, " I", sizeof(buf)-1);
1600 strncat(buf, itoa(clk->incr, buf), sizeof(buf)-1);
1603 return buf;
1606 void update_status_window(GAME g)
1608 int i = 0;
1609 char *buf;
1610 char tmp[15] = {0}, *engine, *mode;
1611 char t[COLS];
1612 int w;
1613 char *p;
1614 int maxy, maxx;
1615 int len;
1616 struct userdata_s *d = g->data;
1617 int y;
1618 int n;
1620 if (!curses_initialized)
1621 return;
1623 getmaxyx(statusw, maxy, maxx);
1624 (void)maxy;
1625 w = maxx - 2 - 8;
1626 len = maxx - 2;
1627 buf = Malloc(len);
1628 y = 2;
1630 wchar_t *loadfilew = loadfile[0] ? str_etc (loadfile, w, 1) : str_to_wchar (_ ("not available"));
1631 mvwprintw(statusw, y++, 1, "%*s %-*ls", 7, _("File:"), w, loadfilew);
1632 free (loadfilew);
1633 snprintf(buf, len, "%i %s %i", gindex + 1, _("of"), gtotal);
1634 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Game:"), w, buf);
1636 *tmp = '\0';
1637 p = tmp;
1639 if (config.details) {
1640 *p++ = 'D';
1641 i++;
1644 if (TEST_FLAG(d->flags, CF_DELETE)) {
1645 if (i)
1646 *p++ = '/';
1648 *p++ = 'X';
1649 i++;
1652 if (TEST_FLAG(g->flags, GF_PERROR)) {
1653 if (i)
1654 *p++ = '/';
1656 *p++ = '!';
1657 i++;
1660 if (TEST_FLAG(d->flags, CF_MODIFIED)) {
1661 if (i)
1662 *p++ = '/';
1664 *p++ = '*';
1665 i++;
1668 pgn_config_get(PGN_STRICT_CASTLING, &n);
1670 if (n == 1) {
1671 if (i)
1672 *p++ = '/';
1674 *p++ = 'C';
1675 i++;
1677 #ifdef WITH_LIBPERL
1678 if (TEST_FLAG(d->flags, CF_PERL)) {
1679 if (i)
1680 *p++ = '/';
1682 *p++ = 'P';
1683 i++;
1685 #endif
1687 *p = '\0';
1688 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Flags:"), w, (tmp[0]) ? tmp : "-");
1690 switch (d->mode) {
1691 case MODE_HISTORY:
1692 mode = _("move history");
1693 break;
1694 case MODE_EDIT:
1695 mode = _("edit");
1696 break;
1697 case MODE_PLAY:
1698 mode = _("play");
1699 break;
1700 default:
1701 mode = _("(empty value)");
1702 break;
1705 snprintf(buf, len - 1, "%*s %s", 7, _("Mode:"), mode);
1707 if (d->mode == MODE_PLAY) {
1708 if (TEST_FLAG(d->flags, CF_HUMAN))
1709 strncat(buf, _(" (human/human)"), len - 1);
1710 else if (TEST_FLAG(d->flags, CF_ENGINE_LOOP))
1711 strncat(buf, _(" (engine/engine)"), len - 1);
1712 else
1713 strncat(buf, (d->play_mode == PLAY_EH) ?
1714 _(" (engine/human)") : _(" (human/engine)"), len - 1);
1717 buf[len-1] = 0;
1718 mvwprintw(statusw, y++, 1, "%-*s", len, buf);
1719 free(buf);
1721 if (d->engine) {
1722 switch (d->engine->status) {
1723 case ENGINE_THINKING:
1724 engine = _("pondering...");
1725 break;
1726 case ENGINE_READY:
1727 engine = _("ready");
1728 break;
1729 case ENGINE_INITIALIZING:
1730 engine = _("initializing...");
1731 break;
1732 case ENGINE_OFFLINE:
1733 engine = _("offline");
1734 break;
1735 default:
1736 engine = _("(empty value)");
1737 break;
1740 else
1741 engine = _("offline");
1743 mvwprintw(statusw, y, 1, "%*s %-*s", 7, _("Engine:"), w, " ");
1744 wattron(statusw, CP_STATUS_ENGINE);
1745 mvwaddstr(statusw, y++, 9, engine);
1746 wattroff(statusw, CP_STATUS_ENGINE);
1748 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Turn:"), w,
1749 (g->turn == WHITE) ? _("white") : _("black"));
1751 strncpy(tmp, _("white"), sizeof(tmp)-1);
1752 tmp[0] = toupper(tmp[0]);
1753 snprintf(t, sizeof(t), "%s%s",
1754 timeval_to_char(d->wclock.elapsed, d->wclock.tc[d->wclock.tcn][1]),
1755 time_control_status(&d->wclock));
1756 mvwprintw(statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1758 strncpy(tmp, _("black"), sizeof(tmp)-1);
1759 tmp[0] = toupper(tmp[0]);
1760 snprintf(t, sizeof(t), "%s%s",
1761 timeval_to_char(d->bclock.elapsed, d->bclock.tc[d->bclock.tcn][1]),
1762 time_control_status(&d->bclock));
1763 mvwprintw(statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1765 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Total:"), w,
1766 clock_to_char(d->elapsed.tv_sec));
1768 // for (i = 0; i < STATUS_WIDTH; i++)
1769 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1771 if (!status.notify)
1772 status.notify = str_to_wchar(_("Type F1 for help"));
1774 wattron(stdscr, CP_STATUS_NOTIFY);
1775 for (i = (config.boardleft) ? BOARD_WIDTH : 0;
1776 i < ((config.boardleft) ? COLS : STATUS_WIDTH); i++)
1777 mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1778 mvwprintw(stdscr, STATUS_HEIGHT, CENTERX(STATUS_WIDTH, status.notify)
1779 + ((config.boardleft) ? BOARD_WIDTH : 0),
1780 "%ls", status.notify);
1781 wattroff(stdscr, CP_STATUS_NOTIFY);
1784 wchar_t *translate_tag_name(const char *tag)
1786 if (!strcmp (tag, "Event"))
1787 return str_to_wchar (translatable_tag_names[0]);
1788 else if (!strcmp (tag, "Site"))
1789 return str_to_wchar (translatable_tag_names[1]);
1790 else if (!strcmp (tag, "Date"))
1791 return str_to_wchar (translatable_tag_names[2]);
1792 else if (!strcmp (tag, "Round"))
1793 return str_to_wchar (translatable_tag_names[3]);
1794 else if (!strcmp (tag, "White"))
1795 return str_to_wchar (translatable_tag_names[4]);
1796 else if (!strcmp (tag, "Black"))
1797 return str_to_wchar (translatable_tag_names[5]);
1798 else if (!strcmp (tag, "Result"))
1799 return str_to_wchar (translatable_tag_names[6]);
1801 return str_to_wchar (tag);
1804 void update_tag_window(TAG **t)
1806 int i, l, w;
1807 int namel = 0;
1809 for (i = 0; t[i]; i++) {
1810 wchar_t *namewc = translate_tag_name(t[i]->name);
1812 l = wcslen(namewc);
1813 free (namewc);
1814 if (l > namel)
1815 namel = l;
1818 w = TAG_WIDTH - namel - 4;
1820 for (i = 0; t[i] && i < TAG_HEIGHT - 3; i++) {
1821 wchar_t *namewc = translate_tag_name(t[i]->name);
1822 wchar_t *valuewc = str_etc(t[i]->value, w, 0);
1824 mvwprintw(tagw, (i + 2), 1, "%*ls: %-*ls", namel, namewc, w, valuewc);
1825 free (namewc);
1826 free (valuewc);
1829 for (; i < TAG_HEIGHT - 3; i++)
1830 mvwprintw(tagw, (i + 2), 1, "%*s", namel + w + 2, " ");
1833 void append_enginebuf(GAME g, char *line)
1835 int i = 0;
1836 struct userdata_s *d = g->data;
1838 if (d->engine->enginebuf)
1839 for (i = 0; d->engine->enginebuf[i]; i++);
1841 if (i >= LINES - 3) {
1842 free(d->engine->enginebuf[0]);
1844 for (i = 0; d->engine->enginebuf[i+1]; i++)
1845 d->engine->enginebuf[i] = d->engine->enginebuf[i+1];
1847 d->engine->enginebuf[i] = strdup(line);
1849 else {
1850 d->engine->enginebuf = Realloc(d->engine->enginebuf, (i + 2) * sizeof(char *));
1851 d->engine->enginebuf[i++] = strdup(line);
1852 d->engine->enginebuf[i] = NULL;
1856 void update_engine_window(GAME g)
1858 int i;
1859 struct userdata_s *d = g->data;
1861 wmove(enginew, 0, 0);
1862 wclrtobot(enginew);
1864 if (d->engine && d->engine->enginebuf) {
1865 for (i = 0; d->engine->enginebuf[i]; i++)
1866 mvwprintw(enginew, i + 2, 1, "%s", d->engine->enginebuf[i]);
1869 window_draw_title(enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
1870 CP_MESSAGE_BORDER);
1873 void update_all(GAME g)
1875 struct userdata_s *d = g->data;
1878 * In the middle of a macro. Don't update the screen.
1880 if (macro_match != -1)
1881 return;
1883 wmove(boardw, ROWTOMATRIX(d->c_row), COLTOMATRIX(d->c_col));
1884 update_board_window(g);
1885 update_status_window(g);
1886 update_history_window(g);
1887 update_tag_window(g->tag);
1888 update_engine_window(g);
1889 update_panels();
1890 doupdate();
1893 static void game_next_prev(GAME g, int n, int count)
1895 if (gtotal < 2)
1896 return;
1898 if (n == 1) {
1899 if (gindex + count > gtotal - 1) {
1900 if (count != 1)
1901 gindex = gtotal - 1;
1902 else
1903 gindex = 0;
1905 else
1906 gindex += count;
1908 else {
1909 if (gindex - count < 0) {
1910 if (count != 1)
1911 gindex = 0;
1912 else
1913 gindex = gtotal - 1;
1915 else
1916 gindex -= count;
1919 gp = game[gindex];
1922 static void delete_game(int which)
1924 int i, w = which;
1925 struct userdata_s *d;
1927 for (i = 0; i < gtotal; i++) {
1928 d = game[i]->data;
1930 if (i == w || TEST_FLAG(d->flags, CF_DELETE)) {
1931 int n;
1933 free_userdata_once(game[i]);
1934 pgn_free(game[i]);
1936 for (n = i; n+1 < gtotal; n++)
1937 game[n] = game[n+1];
1939 gtotal--;
1940 i--;
1941 w = -1;
1945 if (which != -1) {
1946 if (which + 1 >= gtotal)
1947 gindex = gtotal - 1;
1948 else
1949 gindex = which;
1951 else
1952 gindex = gtotal - 1;
1954 gp = game[gindex];
1955 gp->hp = gp->history;
1959 * FIXME find across multiple games.
1961 static int find_move_exp(GAME g, regex_t r, int which, int count)
1963 int i;
1964 int ret;
1965 char errbuf[255];
1966 int incr;
1967 int found;
1969 incr = (which == 0) ? -1 : 1;
1971 for (i = g->hindex + incr - 1, found = 0; ; i += incr) {
1972 if (i == g->hindex - 1)
1973 break;
1975 if (i >= pgn_history_total(g->hp))
1976 i = 0;
1977 else if (i < 0)
1978 i = pgn_history_total(g->hp) - 1;
1980 // FIXME RAV
1981 ret = regexec(&r, g->hp[i]->move, 0, 0, 0);
1983 if (ret == 0) {
1984 if (count == ++found) {
1985 return i + 1;
1988 else {
1989 if (ret != REG_NOMATCH) {
1990 regerror(ret, &r, errbuf, sizeof(errbuf));
1991 cmessage(_("Error Matching Regular Expression"), ANY_KEY_STR, "%s", errbuf);
1992 return -1;
1997 return -1;
2000 static int toggle_delete_flag(int n)
2002 int i, x;
2003 struct userdata_s *d = game[n]->data;
2005 TOGGLE_FLAG(d->flags, CF_DELETE);
2006 gindex = n;
2008 for (i = x = 0; i < gtotal; i++) {
2009 d = game[i]->data;
2011 if (TEST_FLAG(d->flags, CF_DELETE))
2012 x++;
2015 if (x == gtotal) {
2016 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
2017 d = game[n]->data;
2018 CLEAR_FLAG(d->flags, CF_DELETE);
2019 return 1;
2022 return 0;
2025 static int find_game_exp(char *str, int which, int count)
2027 char *nstr = NULL, *exp = NULL;
2028 regex_t nexp, vexp;
2029 int ret = -1;
2030 int g = 0;
2031 char buf[255] = {0}, *tmp;
2032 char errbuf[255];
2033 int found = 0;
2034 int incr = (which == 0) ? -(1) : 1;
2036 strncpy(buf, str, sizeof(buf)-1);
2037 tmp = buf;
2039 if (strstr(tmp, ":") != NULL) {
2040 nstr = strsep(&tmp, ":");
2042 if ((ret = regcomp(&nexp, nstr,
2043 REG_ICASE|REG_EXTENDED|REG_NOSUB)) != 0) {
2044 regerror(ret, &nexp, errbuf, sizeof(errbuf));
2045 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
2046 ret = g = -1;
2047 goto cleanup;
2051 exp = tmp;
2053 while (exp && *exp && isspace(*exp))
2054 exp++;
2056 if (exp == NULL)
2057 goto cleanup;
2059 if ((ret = regcomp(&vexp, exp, REG_EXTENDED|REG_NOSUB)) != 0) {
2060 regerror(ret, &vexp, errbuf, sizeof(errbuf));
2061 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
2062 ret = -1;
2063 goto cleanup;
2066 ret = -1;
2068 for (g = gindex + incr, found = 0; ; g += incr) {
2069 int t;
2071 if (g == gtotal)
2072 g = 0;
2073 else if (g < 0)
2074 g = gtotal - 1;
2076 if (g == gindex)
2077 break;
2079 for (t = 0; game[g]->tag[t]; t++) {
2080 if (nstr) {
2081 if (regexec(&nexp, game[g]->tag[t]->name, 0, 0, 0) == 0) {
2082 if (regexec(&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0) {
2083 if (count == ++found) {
2084 ret = g;
2085 goto cleanup;
2090 else {
2091 if (regexec(&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0) {
2092 if (count == ++found) {
2093 ret = g;
2094 goto cleanup;
2100 ret = -1;
2103 cleanup:
2104 if (nstr)
2105 regfree(&nexp);
2107 if (g != -1)
2108 regfree(&vexp);
2110 return ret;
2114 * Updates the notification line in the status window then refreshes the
2115 * status window.
2117 void update_status_notify(GAME g, char *fmt, ...)
2119 va_list ap;
2120 #ifdef HAVE_VASPRINTF
2121 char *line;
2122 #else
2123 char line[COLS];
2124 #endif
2126 free(status.notify);
2127 status.notify = NULL;
2129 if (!fmt)
2130 return;
2132 va_start(ap, fmt);
2133 #ifdef HAVE_VASPRINTF
2134 vasprintf(&line, fmt, ap);
2135 #else
2136 vsnprintf(line, sizeof(line), fmt, ap);
2137 #endif
2138 va_end(ap);
2140 status.notify = str_to_wchar(line);
2142 #ifdef HAVE_VASPRINTF
2143 free(line);
2144 #endif
2147 int rav_next_prev(GAME g, BOARD b, int n)
2149 // Next RAV.
2150 if (n) {
2151 if ((!g->ravlevel && g->hindex && g->hp[g->hindex - 1]->rav == NULL) ||
2152 (!g->ravlevel && !g->hindex && g->hp[g->hindex]->rav == NULL) ||
2153 (g->ravlevel && g->hp[g->hindex]->rav == NULL))
2154 return 1;
2156 g->rav = Realloc(g->rav, (g->ravlevel + 1) * sizeof(RAV));
2157 g->rav[g->ravlevel].hp = g->hp;
2158 g->rav[g->ravlevel].flags = g->flags;
2159 g->rav[g->ravlevel].fen = pgn_game_to_fen(g, b);
2160 g->rav[g->ravlevel].hindex = g->hindex;
2161 g->hp = (!g->ravlevel) ? (g->hindex) ? g->hp[g->hindex - 1]->rav : g->hp[g->hindex]->rav : g->hp[g->hindex]->rav;
2162 g->hindex = 0;
2163 g->ravlevel++;
2164 pgn_board_update(g, b, g->hindex + 1);
2165 return 0;
2168 if (g->ravlevel - 1 < 0)
2169 return 1;
2171 // Previous RAV.
2172 g->ravlevel--;
2173 pgn_board_init_fen(g, b, g->rav[g->ravlevel].fen);
2174 free(g->rav[g->ravlevel].fen);
2175 g->hp = g->rav[g->ravlevel].hp;
2176 g->flags = g->rav[g->ravlevel].flags;
2177 g->hindex = g->rav[g->ravlevel].hindex;
2178 return 0;
2181 static void draw_window_decor()
2183 move_panel(boardp, 0,
2184 (config.boardleft) ? 0 : COLS - BOARD_WIDTH);
2185 move_panel(historyp, LINES - HISTORY_HEIGHT,
2186 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH : 0 :
2187 (MEGA_BOARD) ? 0 : COLS - HISTORY_WIDTH);
2188 move_panel(statusp, 0,
2189 (config.boardleft) ? BOARD_WIDTH : 0);
2190 move_panel(tagp, STATUS_HEIGHT + 1,
2191 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH :
2192 HISTORY_WIDTH : 0);
2194 wbkgd(boardw, CP_BOARD_WINDOW);
2195 wbkgd(statusw, CP_STATUS_WINDOW);
2196 window_draw_title(statusw, _("Game Status"), STATUS_WIDTH,
2197 CP_STATUS_TITLE, CP_STATUS_BORDER);
2198 wbkgd(tagw, CP_TAG_WINDOW);
2199 window_draw_title(tagw, _("Roster Tags"), TAG_WIDTH, CP_TAG_TITLE,
2200 CP_TAG_BORDER);
2201 wbkgd(historyw, CP_HISTORY_WINDOW);
2202 window_draw_title(historyw, _("Move History"), HISTORY_WIDTH,
2203 CP_HISTORY_TITLE, CP_HISTORY_BORDER);
2206 void do_window_resize()
2208 if (LINES < 23 || COLS < 74)
2209 return;
2211 resizeterm(LINES, COLS);
2212 endwin();
2213 wresize(boardw, BOARD_HEIGHT, BOARD_WIDTH);
2214 wresize(historyw, HISTORY_HEIGHT, HISTORY_WIDTH);
2215 wresize(statusw, STATUS_HEIGHT, STATUS_WIDTH);
2216 wresize(tagw, TAG_HEIGHT, TAG_WIDTH);
2217 clear ();
2218 wclear (boardw);
2219 wclear (historyw);
2220 wclear (tagw);
2221 wclear (statusw);
2222 wclear (loadingw);
2223 wclear (enginew);
2224 draw_window_decor();
2225 update_all(gp);
2226 keypad(boardw, TRUE);
2227 curs_set(0);
2228 cbreak();
2229 noecho();
2232 void do_global_redraw ()
2234 do_window_resize ();
2237 void stop_clock()
2239 memset(&clock_timer, 0, sizeof(struct itimerval));
2240 setitimer(ITIMER_REAL, &clock_timer, NULL);
2243 void start_clock(GAME g)
2245 struct userdata_s *d = g->data;
2247 if (clock_timer.it_interval.tv_usec)
2248 return;
2250 memset(&d->elapsed, 0, sizeof(struct timeval));
2251 clock_timer.it_value.tv_sec = 0;
2252 clock_timer.it_value.tv_usec = 100000;
2253 clock_timer.it_interval.tv_sec = 0;
2254 clock_timer.it_interval.tv_usec = 100000;
2255 setitimer(ITIMER_REAL, &clock_timer, NULL);
2258 static void update_clocks()
2260 int i;
2261 struct userdata_s *d;
2262 struct itimerval it;
2263 int update = 0;
2265 getitimer(ITIMER_REAL, &it);
2267 for (i = 0; i < gtotal; i++) {
2268 d = game[i]->data;
2270 if (d && d->mode == MODE_PLAY) {
2271 if (d->paused == 1 || TEST_FLAG(d->flags, CF_NEW))
2272 continue;
2273 else if (d->paused == -1) {
2274 if (game[i]->side == game[i]->turn) {
2275 d->paused = 1;
2276 continue;
2280 update_clock(game[i], it);
2282 if (game[i] == gp)
2283 update = 1;
2287 if (update) {
2288 update_status_window(gp);
2289 update_panels();
2290 doupdate();
2294 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2296 static int parse_clock_time(char **str)
2298 char *p = *str;
2299 int n = 0, t = 0;
2301 SKIP_SPACE(p);
2303 if (!isdigit(*p))
2304 return -1;
2306 while (*p) {
2307 if (isdigit(*p)) {
2308 t = atoi(p);
2310 while (isdigit(*p))
2311 p++;
2313 continue;
2316 switch (*p) {
2317 case 'H':
2318 case 'h':
2319 n += t * (60 * 60);
2320 t = 0;
2321 break;
2322 case 'M':
2323 case 'm':
2324 n += t * 60;
2325 t = 0;
2326 break;
2327 case 'S':
2328 case 's':
2329 n += t;
2330 t = 0;
2331 break;
2332 case ' ':
2333 p++;
2334 case '/':
2335 case '+':
2336 goto done;
2337 default:
2338 *str = p;
2339 return -1;
2342 p++;
2345 done:
2346 n += t;
2347 *str = p;
2348 return n;
2351 static int parse_clock_input(struct clock_s *clk, char *str, int *incr)
2353 char *p = str;
2354 long n = 0;
2355 int plus = 0;
2356 int m = 0;
2357 int tc = 0;
2359 SKIP_SPACE(p);
2361 if (!*p)
2362 return 0;
2364 if (*p == '+') {
2365 plus = 1;
2366 p++;
2367 SKIP_SPACE(p);
2369 if (*p == '+')
2370 goto move_incr;
2372 else
2373 memset(clk, 0, sizeof(struct clock_s));
2375 again:
2376 /* Sudden death. */
2377 if (strncasecmp(p, "SD", 2) == 0) {
2378 n = 0;
2379 p += 2;
2380 goto tc;
2383 n = parse_clock_time(&p);
2385 if (n == -1)
2386 return 1;
2388 if (!n)
2389 goto done;
2391 /* Time control. */
2393 if (*p == '/') {
2394 if (plus)
2395 return 1;
2397 /* Sudden death without a previous time control. */
2398 if (!n && !tc)
2399 return 1;
2401 m = n;
2402 p++;
2403 n = parse_clock_time(&p);
2405 if (n == -1)
2406 return 1;
2408 if (tc >= MAX_TC) {
2409 message(ERROR_STR, ANY_KEY_STR, "%s (%i)", _("Maximum number of time controls reached"), MAX_TC);
2410 return 1;
2413 clk->tc[tc][0] = m;
2414 clk->tc[tc++][1] = n;
2415 SKIP_SPACE(p);
2417 if (*p == '+')
2418 goto move_incr;
2420 if (*p)
2421 goto again;
2423 goto done;
2426 if (plus)
2427 *incr = n;
2428 else
2429 clk->tc[clk->tcn][1] = (n <= clk->elapsed.tv_sec) ? clk->elapsed.tv_sec + n : n;
2431 move_incr:
2432 if (*p) {
2433 if (*p++ == '+') {
2434 if (!isdigit(*p))
2435 return 1;
2437 n = parse_clock_time(&p);
2439 if (n == -1 || *p)
2440 return 1;
2442 clk->incr = n;
2444 SKIP_SPACE(p);
2446 if (*p)
2447 return 1;
2449 else
2450 return 1;
2453 done:
2454 return 0;
2457 static int parse_which_clock(struct clock_s *clk, char *str)
2459 struct clock_s tmp;
2460 int incr = 0;
2462 memcpy(&tmp, clk, sizeof(struct clock_s));
2464 if (parse_clock_input(&tmp, str, &incr)) {
2465 cmessage(ERROR_STR, ANY_KEY_STR, _("Invalid clock specification"));
2466 return 1;
2469 memcpy(clk, &tmp, sizeof(struct clock_s));
2470 clk->tc[clk->tcn][1] += incr;
2471 return 0;
2474 void do_clock_input_finalize(WIN *win)
2476 struct userdata_s *d = gp->data;
2477 struct input_data_s *in = win->data;
2478 char *p = in->str;
2480 if (!in->str) {
2481 free(in);
2482 return;
2485 SKIP_SPACE(p);
2487 if (tolower(*p) == 'w') {
2488 p++;
2490 if (parse_which_clock(&d->wclock, p))
2491 goto done;
2493 else if (tolower(*p) == 'b') {
2494 p++;
2496 if (parse_which_clock(&d->bclock, p))
2497 goto done;
2499 else {
2500 if (parse_which_clock(&d->wclock, p))
2501 goto done;
2503 if (parse_which_clock(&d->bclock, p))
2504 goto done;
2507 if (!d->wclock.tc[0][1] && !d->bclock.tc[0][1])
2508 CLEAR_FLAG(d->flags, CF_CLOCK);
2509 else
2510 SET_FLAG(d->flags, CF_CLOCK);
2512 done:
2513 free(in->str);
2514 free(in);
2517 void do_engine_command_finalize(WIN *win)
2519 struct userdata_s *d = gp->data;
2520 struct input_data_s *in = win->data;
2521 int x;
2523 if (!in->str) {
2524 free(in);
2525 return;
2528 if (!d->engine)
2529 goto done;
2531 x = d->engine->status;
2532 send_to_engine(gp, -1, "%s\n", in->str);
2533 d->engine->status = x;
2535 done:
2536 free(in->str);
2537 free(in);
2540 void do_board_details()
2542 config.details = (config.details) ? 0 : 1;
2545 void do_toggle_strict_castling()
2547 int n;
2549 pgn_config_get(PGN_STRICT_CASTLING, &n);
2551 if (n == 0)
2552 pgn_config_set(PGN_STRICT_CASTLING, 1);
2553 else
2554 pgn_config_set(PGN_STRICT_CASTLING, 0);
2557 void do_play_set_clock()
2559 struct input_data_s *in;
2561 in = Calloc(1, sizeof(struct input_data_s));
2562 in->efunc = do_clock_input_finalize;
2563 construct_input(_("Set Clock"), NULL, 1, 1,
2564 _ ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n" \
2565 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2566 NULL, NULL, 0, in, INPUT_HIST_CLOCK, -1);
2569 void do_play_toggle_human()
2571 struct userdata_s *d = gp->data;
2573 TOGGLE_FLAG(d->flags, CF_HUMAN);
2575 if (!TEST_FLAG(d->flags, CF_HUMAN) && pgn_history_total(gp->hp)) {
2576 if (init_chess_engine(gp))
2577 return;
2580 CLEAR_FLAG(d->flags, CF_ENGINE_LOOP);
2582 if (d->engine)
2583 d->engine->status = ENGINE_READY;
2586 void do_play_toggle_engine()
2588 struct userdata_s *d = gp->data;
2590 TOGGLE_FLAG(d->flags, CF_ENGINE_LOOP);
2591 CLEAR_FLAG(d->flags, CF_HUMAN);
2593 if (d->engine && TEST_FLAG(d->flags, CF_ENGINE_LOOP)) {
2594 char *fen = pgn_game_to_fen (gp, d->b);
2596 pgn_board_update(gp, d->b,
2597 pgn_history_total(gp->hp));
2598 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
2599 free (fen);
2604 * This will send a command to the engine skipping the command queue.
2606 void do_play_send_command()
2608 struct userdata_s *d = gp->data;
2609 struct input_data_s *in;
2611 if (!d->engine || d->engine->status == ENGINE_OFFLINE) {
2612 if (init_chess_engine(gp))
2613 return;
2616 in = Calloc(1, sizeof(struct input_data_s));
2617 in->efunc = do_engine_command_finalize;
2618 construct_input(_("Engine Command"), NULL, 1, 1, NULL, NULL, NULL, 0, in, INPUT_HIST_ENGINE, -1);
2621 void do_play_switch_turn()
2623 struct userdata_s *d = gp->data;
2625 pgn_switch_side(gp);
2626 pgn_switch_turn(gp);
2628 if (!TEST_FLAG(d->flags, CF_HUMAN))
2629 add_engine_command(gp, -1,
2630 (gp->side == WHITE) ? "white\n" : "black\n");
2632 update_status_window(gp);
2635 void do_play_toggle_eh_mode()
2637 struct userdata_s *d = gp->data;
2639 if (!TEST_FLAG(d->flags, CF_HUMAN)) {
2640 if (!gp->hindex){
2641 pgn_switch_side(gp, TRUE);
2642 d->play_mode = (d->play_mode) ? PLAY_HE : PLAY_EH;
2643 if (gp->side == BLACK)
2644 update_status_notify(gp, _("Press 'g' to start the game"));
2646 d->rotate = !d->rotate;
2648 else
2649 message(NULL, ANY_KEY_STR,
2650 _("You may only switch sides at the start of the \n"
2651 "game. Press ^K or ^N to begin a new game."));
2655 void do_play_undo()
2657 struct userdata_s *d = gp->data;
2659 if (!pgn_history_total(gp->hp))
2660 return;
2662 if (keycount) {
2663 if (gp->hindex - keycount < 0)
2664 gp->hindex = 0;
2665 else {
2666 if (d->go_move)
2667 gp->hindex -= (keycount * 2) -1;
2668 else
2669 gp->hindex -= keycount * 2;
2672 else {
2673 if (gp->hindex - 2 < 0)
2674 gp->hindex = 0;
2675 else {
2676 if (d->go_move)
2677 gp->hindex -= 1;
2678 else
2679 gp->hindex -= 2;
2683 pgn_history_free(gp->hp, gp->hindex);
2684 gp->hindex = pgn_history_total(gp->hp);
2685 pgn_board_update(gp, d->b, gp->hindex);
2687 if (d->engine && d->engine->status == ENGINE_READY) {
2688 char *fen = pgn_game_to_fen(gp, d->b);
2690 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
2691 free (fen);
2692 d->engine->status = ENGINE_READY;
2695 update_history_window(gp);
2697 if (d->go_move) {
2698 pgn_switch_side(gp, FALSE);
2699 d->go_move--;
2702 d->pm_undo = TRUE;
2705 void do_play_toggle_pause()
2707 struct userdata_s *d = gp->data;
2709 if (!TEST_FLAG(d->flags, CF_HUMAN) && gp->turn !=
2710 gp->side) {
2711 d->paused = -1;
2712 return;
2715 d->paused = (d->paused) ? 0 : 1;
2718 void do_play_go()
2720 struct userdata_s *d = gp->data;
2722 if (TEST_FLAG(d->flags, CF_HUMAN))
2723 return;
2725 if (fm_loaded_file && gp->side != gp->turn) {
2726 pgn_switch_side(gp, FALSE);
2727 add_engine_command(gp, ENGINE_THINKING, "black\n");
2730 add_engine_command(gp, ENGINE_THINKING, "go\n");
2732 // Completa la función para que permita seguir jugando al usarla.
2733 // Complete the function to allow continue playing when using.
2734 if (gp->side == gp->turn)
2735 pgn_switch_side(gp, FALSE);
2737 d->go_move++;
2740 void do_play_config_command()
2742 int x, w;
2744 if (config.keys) {
2745 for (x = 0; config.keys[x]; x++) {
2746 if (config.keys[x]->c == input_c) {
2747 switch (config.keys[x]->type) {
2748 case KEY_DEFAULT:
2749 add_engine_command(gp, -1, "%ls\n",
2750 config.keys[x]->str);
2751 break;
2752 case KEY_SET:
2753 if (!keycount)
2754 break;
2756 add_engine_command(gp, -1,
2757 "%ls %i\n", config.keys[x]->str, keycount);
2758 keycount = 0;
2759 break;
2760 case KEY_REPEAT:
2761 if (!keycount)
2762 break;
2764 for (w = 0; w < keycount; w++)
2765 add_engine_command(gp, -1,
2766 "%ls\n", config.keys[x]->str);
2767 keycount = 0;
2768 break;
2774 update_status_notify(gp, NULL);
2777 void do_play_cancel_selected()
2779 struct userdata_s *d = gp->data;
2781 d->sp.icon = d->sp.srow = d->sp.scol = 0;
2782 keycount = 0;
2783 pgn_reset_valid_moves(d->b);
2784 update_status_notify(gp, NULL);
2787 void do_play_commit()
2789 struct userdata_s *d = gp->data;
2791 pushkey = keycount = 0;
2792 update_status_notify(gp, NULL);
2794 if (!TEST_FLAG(d->flags, CF_HUMAN) &&
2795 (!d->engine || d->engine->status == ENGINE_THINKING))
2796 return;
2798 if (!d->sp.icon)
2799 return;
2801 d->sp.row = d->c_row;
2802 d->sp.col = d->c_col;
2804 if (d->rotate) {
2805 rotate_position(&d->sp.row, &d->sp.col);
2806 rotate_position(&d->sp.srow, &d->sp.scol);
2809 move_to_engine(gp);
2811 // Completa la función para que permita seguir jugando cuando se carga un
2812 // archivo pgn (con juego no terminado) que inicie con turno del lado
2813 // negro.
2814 // Complete the function to allow continue playing when loading a file
2815 // pgn (with unfinished game) you start to turn black side.
2816 if (gp->side != gp->turn)
2817 pgn_switch_side(gp, FALSE);
2819 if (d->rotate && d->sp.icon)
2820 rotate_position(&d->sp.srow, &d->sp.scol);
2822 // Envia comando 'go' a Polyglot en el primer movimiento debido a que
2823 // polyglot no envia el movimiento y cboard se queda esperando.
2824 // Send command 'go' to the first movement Polyglot because cboard
2825 // waits to send polyglot movement and this does not make.
2826 if (config.fmpolyglot &&
2827 ((gp->side == WHITE && !gp->hindex) || fm_loaded_file))
2828 add_engine_command(gp, ENGINE_THINKING, "go\n");
2830 d->go_move = 0;
2831 fm_loaded_file = FALSE;
2832 d->pm_undo = FALSE;
2835 void do_play_select()
2837 struct userdata_s *d = gp->data;
2839 if (!TEST_FLAG(d->flags, CF_HUMAN) && (!d->engine ||
2840 d->engine->status == ENGINE_OFFLINE)) {
2841 if (init_chess_engine(gp))
2842 return;
2845 if (d->engine && d->engine->status == ENGINE_THINKING)
2846 return;
2848 if (d->sp.icon)
2849 do_play_cancel_selected ();
2851 if (d->rotate)
2852 rotate_position(&d->c_row, &d->c_col);
2854 d->sp.icon = d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon;
2856 if (pgn_piece_to_int(d->sp.icon) == OPEN_SQUARE) {
2857 d->sp.icon = 0;
2858 return;
2861 if (((islower(d->sp.icon) && gp->turn != BLACK)
2862 || (isupper(d->sp.icon) && gp->turn != WHITE))) {
2863 struct key_s **k;
2864 char *str = Malloc (512);
2866 for (k = play_keys; *k; k++) {
2867 if ((*k)->f == do_play_toggle_eh_mode)
2868 break;
2871 snprintf (str, 512, _("It is not your turn to move. You may switch playing sides by pressing \"%lc\"."),
2872 *k ? (*k)->c : '?');
2873 message(NULL, ANY_KEY_STR, "%s", str);
2874 free (str);
2875 d->sp.icon = 0;
2876 return;
2877 #if 0
2878 if (pgn_history_total(gp->hp)) {
2879 message(NULL, ANY_KEY_STR, "%s", _("It is not your turn to move. You can switch sides "));
2880 d->sp.icon = 0;
2881 return;
2883 else {
2884 if (pgn_tag_find(gp->tag, "FEN") != E_PGN_ERR)
2885 return;
2887 add_engine_command(gp, ENGINE_READY, "black\n");
2888 pgn_switch_turn(gp);
2890 if (gp->side != BLACK)
2891 pgn_switch_side(gp);
2893 #endif
2896 d->sp.srow = d->c_row;
2897 d->sp.scol = d->c_col;
2899 if (config.validmoves)
2900 pgn_find_valid_moves(gp, d->b, d->sp.scol, d->sp.srow);
2902 if (d->rotate) {
2903 rotate_position(&d->c_row, &d->c_col);
2904 rotate_position(&d->sp.srow, &d->sp.scol);
2907 CLEAR_FLAG(d->flags, CF_NEW);
2908 start_clock(gp);
2911 static wchar_t *build_help(struct key_s **keys)
2913 int i, nlen = 1, len, t, n;
2914 wchar_t *buf = NULL, *wc = NULL;
2915 wchar_t *p;
2917 if (!keys)
2918 return NULL;
2920 for (i = len = t = 0; keys[i]; i++) {
2921 if (!keys[i]->d)
2922 continue;
2924 if (keys[i]->key) {
2925 if (wcslen(keys[i]->key) > nlen) {
2926 nlen = wcslen(keys[i]->key);
2927 t += nlen;
2929 else
2930 t++;
2932 else
2933 t++;
2935 if (keys[i]->d) {
2936 if (wcslen(keys[i]->d) > len)
2937 len = wcslen(keys[i]->d);
2940 t += len;
2941 t += keys[i]->r;
2944 t += 4 + i + 1;
2945 buf = Malloc((t+1)*sizeof(wchar_t));
2946 p = buf;
2948 for (i = 0; keys[i]; i++) {
2949 if (!keys[i]->d)
2950 continue;
2952 if (keys[i]->key)
2953 n = wcslen(keys[i]->key);
2954 else
2955 n = 1;
2957 while (n++ <= nlen)
2958 *p++ = ' ';
2960 *p = 0;
2962 if (keys[i]->key) {
2963 wcsncat(buf, keys[i]->key, t-1);
2964 p = buf + wcslen(buf);
2966 else
2967 *p++ = keys[i]->c;
2969 *p++ = ' ';
2970 *p++ = '-';
2971 *p++ = ' ';
2972 *p = 0;
2974 if (keys[i]->d)
2975 wcsncat(buf, keys[i]->d, t-1);
2977 if (keys[i]->r) {
2978 wc = str_to_wchar ("*");
2979 wcsncat(buf, wc, t-1);
2980 free (wc);
2983 wc = str_to_wchar ("\n");
2984 wcscat(buf, wc);
2985 free (wc);
2986 p = buf + wcslen(buf);
2989 return buf;
2992 void do_global_help ()
2994 wchar_t *buf = build_help(global_keys);
2996 construct_message(_("Global Game Keys (* = can take a repeat count)"),
2997 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
2998 0, 1, "%ls", buf);
3001 void do_main_help(WIN *win)
3003 switch (win->c) {
3004 case 'p':
3005 do_play_help ();
3006 break;
3007 case 'h':
3008 do_history_help ();
3009 break;
3010 case 'e':
3011 do_edit_help ();
3012 break;
3013 case 'g':
3014 do_global_help ();
3015 break;
3016 default:
3017 break;
3021 static void do_more_help(WIN *win)
3023 if (win->c == KEY_F(1) || win->c == CTRL_KEY('g'))
3024 construct_message(_("Command Key Index"),
3025 _ ("p/h/e/g or any other key to quit"), 0, 0,
3026 NULL, NULL, NULL, do_main_help, 0, 0, "%s",
3028 "p - play mode keys\n"
3029 "h - history mode keys\n"
3030 "e - board edit mode keys\n"
3031 "g - global game keys"
3035 void do_play_help()
3037 wchar_t *buf = build_help(play_keys);
3039 construct_message(_("Play Mode Keys (* = can take a repeat count)"),
3040 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
3041 buf, do_more_help, 0, 1, "%ls", buf);
3044 void do_play_history_mode()
3046 struct userdata_s *d = gp->data;
3048 if (!pgn_history_total(gp->hp) ||
3049 (d->engine && d->engine->status == ENGINE_THINKING))
3050 return;
3052 d->mode = MODE_HISTORY;
3053 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3056 void do_play_edit_mode()
3058 struct userdata_s *d = gp->data;
3060 if (pgn_history_total(gp->hp))
3061 return;
3063 pgn_board_init_fen(gp, d->b, NULL);
3064 config.details++;
3065 d->mode = MODE_EDIT;
3068 void do_edit_insert_finalize(WIN *win)
3070 struct userdata_s *d = win->data;
3072 if (pgn_piece_to_int(win->c) == -1)
3073 return;
3075 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon = win->c;
3078 void do_edit_select()
3080 struct userdata_s *d = gp->data;
3082 if (d->sp.icon)
3083 return;
3085 d->sp.icon = d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon;
3087 if (pgn_piece_to_int(d->sp.icon) == OPEN_SQUARE) {
3088 d->sp.icon = 0;
3089 return;
3092 d->sp.srow = d->c_row;
3093 d->sp.scol = d->c_col;
3096 void do_edit_commit()
3098 int p;
3099 struct userdata_s *d = gp->data;
3101 pushkey = keycount = 0;
3102 update_status_notify(gp, NULL);
3104 if (!d->sp.icon)
3105 return;
3107 d->sp.row = d->c_row;
3108 d->sp.col = d->c_col;
3109 p = d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon;
3110 d->b[RANKTOBOARD(d->sp.row)][FILETOBOARD(d->sp.col)].icon = p;
3111 d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon =
3112 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3113 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3116 void do_edit_delete()
3118 struct userdata_s *d = gp->data;
3120 if (d->sp.icon)
3121 d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon =
3122 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3123 else
3124 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon =
3125 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3127 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3130 void do_edit_cancel_selected()
3132 struct userdata_s *d = gp->data;
3134 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3135 keycount = 0;
3136 update_status_notify(gp, NULL);
3139 void do_edit_switch_turn()
3141 pgn_switch_turn(gp);
3144 void do_edit_toggle_castle()
3146 struct userdata_s *d = gp->data;
3148 castling_state(gp, d->b, RANKTOBOARD(d->c_row),
3149 FILETOBOARD(d->c_col),
3150 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon, 1);
3153 void do_edit_insert()
3155 struct userdata_s *d = gp->data;
3157 construct_message(_("Insert Piece"), _("P=pawn, R=rook, N=knight, B=bishop, "), 0, 0, NULL, NULL,
3158 d->b, do_edit_insert_finalize, 0, 0, "%s", _("Type the piece letter to insert. Lowercase "));
3161 void do_edit_enpassant()
3163 struct userdata_s *d = gp->data;
3165 if (d->c_row == 6 || d->c_row == 3) {
3166 pgn_reset_enpassant(d->b);
3167 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].enpassant = 1;
3171 void do_edit_help()
3173 wchar_t *buf = build_help(edit_keys);
3175 construct_message(_("Edit Mode Keys (* = can take a repeat count)"),
3176 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
3177 0, 1, "%ls", buf);
3180 void do_edit_exit()
3182 struct userdata_s *d = gp->data;
3183 char *fen = pgn_game_to_fen(gp, d->b);
3185 config.details--;
3186 pgn_tag_add(&gp->tag, "FEN", fen);
3187 free (fen);
3188 pgn_tag_add(&gp->tag, "SetUp", "1");
3189 pgn_tag_sort(gp->tag);
3190 pgn_board_update(gp, d->b, gp->hindex);
3191 d->mode = MODE_PLAY;
3194 void really_do_annotate_finalize(struct input_data_s *in,
3195 struct userdata_s *d)
3197 HISTORY *h = in->data;
3198 int len;
3200 if (!in->str) {
3201 if (h->comment) {
3202 free(h->comment);
3203 h->comment = NULL;
3206 else {
3207 len = strlen(in->str);
3208 h->comment = Realloc(h->comment, len+1);
3209 strncpy(h->comment, in->str, len);
3210 h->comment[len] = 0;
3213 free(in->str);
3214 free(in);
3215 SET_FLAG(d->flags, CF_MODIFIED);
3218 void do_annotate_finalize(WIN *win)
3220 struct userdata_s *d = gp->data;
3221 struct input_data_s *in = win->data;
3223 really_do_annotate_finalize(in, d);
3226 void do_find_move_exp_finalize(int init, int which)
3228 int n;
3229 struct userdata_s *d = gp->data;
3230 static int firstrun;
3231 static regex_t r;
3232 int ret;
3233 char errbuf[255];
3235 if (init || !firstrun) {
3236 if (!firstrun)
3237 regfree(&r);
3239 if ((ret = regcomp(&r, moveexp, REG_EXTENDED|REG_NOSUB)) != 0) {
3240 regerror(ret, &r, errbuf, sizeof(errbuf));
3241 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
3242 return;
3245 firstrun = 1;
3248 if ((n = find_move_exp(gp, r,
3249 (which == -1) ? 0 : 1, (keycount) ? keycount : 1)) == -1)
3250 return;
3252 gp->hindex = n;
3253 pgn_board_update(gp, d->b, gp->hindex);
3256 void do_find_move_exp(WIN *win)
3258 struct input_data_s *in = win->data;
3259 int *n = in->data;
3260 int which = *n;
3262 if (in->str) {
3263 strncpy(moveexp, in->str, sizeof(moveexp)-1);
3264 moveexp[sizeof(moveexp)-1] = 0;
3265 do_find_move_exp_finalize(1, which);
3266 free(in->str);
3269 free(in->data);
3270 free(in);
3273 void do_move_jump_finalize(int n)
3275 struct userdata_s *d = gp->data;
3277 if (n < 0 || n > (pgn_history_total(gp->hp) / 2))
3278 return;
3280 keycount = 0;
3281 update_status_notify(gp, NULL);
3282 gp->hindex = (n) ? n * 2 - 1 : n * 2;
3283 pgn_board_update(gp, d->b, gp->hindex);
3286 void do_move_jump(WIN *win)
3288 struct input_data_s *in = win->data;
3290 if (!in->str || !isinteger(in->str)) {
3291 if (in->str)
3292 free(in->str);
3294 free(in);
3295 return;
3298 do_move_jump_finalize(atoi(in->str));
3299 free(in->str);
3300 free(in);
3303 struct history_menu_s {
3304 char *line;
3305 int hindex;
3306 int ravlevel;
3307 int move;
3308 int indent;
3311 void free_history_menu_data(struct history_menu_s **h)
3313 int i;
3315 if (!h)
3316 return;
3318 for (i = 0; h[i]; i++) {
3319 free(h[i]->line);
3320 free(h[i]);
3323 free(h);
3326 void get_history_data(HISTORY **hp, struct history_menu_s ***menu, int m,
3327 int turn)
3329 int i, n = 0;
3330 int t = pgn_history_total(hp);
3331 char buf[MAX_SAN_MOVE_LEN + 4];
3332 static int depth;
3333 struct history_menu_s **hmenu = *menu;
3335 if (hmenu)
3336 for (n = 0; hmenu[n]; n++);
3337 else
3338 depth = 0;
3340 for (i = 0; i < t; i++) {
3341 hmenu = Realloc(hmenu, (n + 2) * sizeof(struct history_menu_s *));
3342 hmenu[n] = Malloc(sizeof(struct history_menu_s));
3343 snprintf(buf, sizeof(buf), "%c%s%s", (turn == WHITE) ? 'W' : 'B',
3344 hp[i]->move, (hp[i]->comment || hp[i]->nag[0]) ? " !" : "");
3345 hmenu[n]->line = strdup(buf);
3346 hmenu[n]->hindex = i;
3347 hmenu[n]->indent = 0;
3348 hmenu[n]->ravlevel = depth;
3349 hmenu[n]->move = (n && depth > hmenu[n-1]->ravlevel) ? m++ : m;
3350 n++;
3351 hmenu[n] = NULL;
3353 #if 0
3354 if (hp[i]->rav) {
3355 depth++;
3356 get_history_data(hp[i]->rav, &hmenu, m, turn);
3357 for (n = 0; hmenu[n]; n++);
3358 depth--;
3360 if (depth)
3361 m--;
3363 #endif
3365 turn = (turn == WHITE) ? BLACK : WHITE;
3368 *menu = hmenu;
3371 void history_draw_update(struct menu_input_s *m)
3373 GAME g = m->data;
3374 struct userdata_s *d = g->data;
3376 g->hindex = m->selected + 1;
3377 update_cursor(g, m->selected);
3378 pgn_board_update(g, d->b, m->selected + 1);
3381 struct menu_item_s **get_history_items(WIN *win)
3383 struct menu_input_s *m = win->data;
3384 GAME g = m->data;
3385 struct userdata_s *d = g->data;
3386 struct history_menu_s **hm = d->data;
3387 struct menu_item_s **items = m->items;
3388 int i;
3390 if (!hm) {
3391 get_history_data(g->history, &hm, 0,
3392 TEST_FLAG(g->flags, GF_BLACK_OPENING));
3393 m->selected = g->hindex - 1;
3395 if (m->selected < 0)
3396 m->selected = 0;
3398 m->draw_exit_func = history_draw_update;
3401 d->data = hm;
3403 if (items) {
3404 for (i = 0; items[i]; i++)
3405 free(items[i]);
3407 free(items);
3408 items = NULL;
3411 for (i = 0; hm[i]; i++) {
3412 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
3413 items[i] = Malloc(sizeof(struct menu_item_s));
3414 items[i]->name = hm[i]->line;
3415 items[i]->value = NULL;
3416 items[i]->selected = 0;
3419 if (items)
3420 items[i] = NULL;
3422 m->nofree = 1;
3423 m->items = items;
3424 return items;
3427 void history_menu_quit(struct menu_input_s *m)
3429 pushkey = -1;
3432 void history_menu_exit(WIN *win)
3434 GAME g = win->data;
3435 struct userdata_s *d = g->data;
3436 struct history_menu_s **hm = d->data;
3437 int i;
3439 if (!hm)
3440 return;
3442 for (i = 0; hm[i]; i++) {
3443 free(hm[i]->line);
3444 free(hm[i]);
3447 free(hm);
3448 d->data = NULL;
3451 // FIXME RAV
3452 void history_menu_next(struct menu_input_s *m)
3454 GAME g = m->data;
3455 struct userdata_s *d = g->data;
3456 struct history_menu_s **hm = d->data;
3457 int n, t;
3459 for (t = 0; hm[t]; t++);
3461 if (m->selected + 1 == t)
3462 n = 0;
3463 else
3464 n = hm[m->selected + 1]->hindex;
3466 n++;
3467 g->hindex = n;
3470 // FIXME RAV
3471 void history_menu_prev(struct menu_input_s *m)
3473 GAME g = m->data;
3474 struct userdata_s *d = g->data;
3475 struct history_menu_s **hm = d->data;
3476 int n, t;
3478 for (t = 0; hm[t]; t++);
3480 if (m->selected - 1 < 0)
3481 n = t - 1;
3482 else
3483 n = hm[m->selected - 1]->hindex;
3485 n++;
3486 g->hindex = n;
3489 void history_menu_help(struct menu_input_s *m)
3491 message(_("History Menu Help"), ANY_KEY_STR, "%s",
3493 " UP/DOWN - previous/next menu item\n"
3494 " HOME/END - first/last menu item\n"
3495 " PGDN/PGUP - next/previous page\n"
3496 " a-zA-Z0-9 - jump to item\n"
3497 " CTRL-a - annotate the selected move\n"
3498 " ENTER - view annotation\n"
3499 " CTRL-d - toggle board details\n"
3500 " ESCAPE/M - return to move history"
3504 void do_annotate_move(HISTORY *hp)
3506 char buf[COLS - 4];
3507 struct input_data_s *in;
3509 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Editing Annotation for"), hp->move);
3510 in = Calloc(1, sizeof(struct input_data_s));
3511 in->data = hp;
3512 in->efunc = do_annotate_finalize;
3513 construct_input(buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3514 _("Type CTRL-t to edit NAG"), edit_nag, NULL, CTRL_KEY('T'), in, -1, -1);
3517 void history_menu_view_annotation(struct menu_input_s *m)
3519 GAME g = m->data;
3521 // FIXME RAV
3522 view_annotation(g->history[m->selected]);
3525 void history_menu_annotate_finalize(WIN *win)
3527 struct input_data_s *in = win->data;
3528 GAME g = in->moredata;
3529 struct userdata_s *d = g->data;
3530 struct history_menu_s **hm = d->data;
3532 really_do_annotate_finalize(in, d);
3533 free_history_menu_data(hm);
3534 hm = NULL;
3535 get_history_data(g->history, &hm, 0, TEST_FLAG(g->flags, GF_BLACK_OPENING));
3536 d->data = hm;
3537 pushkey = REFRESH_MENU;
3540 void history_menu_annotate(struct menu_input_s *m)
3542 GAME g = m->data;
3543 char buf[COLS - 4];
3544 struct input_data_s *in;
3545 HISTORY *hp = g->history[m->selected]; // FIXME RAV
3547 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Editing Annotation for"), hp->move);
3548 in = Calloc(1, sizeof(struct input_data_s));
3549 in->data = hp;
3550 in->moredata = m->data;
3551 in->efunc = history_menu_annotate_finalize;
3552 construct_input(buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3553 _("Type CTRL-t to edit NAG"), edit_nag, NULL, CTRL_KEY('T'), in, -1, -1);
3556 void history_menu_details(struct menu_input_s *m)
3558 do_board_details();
3561 // FIXME RAV
3562 void history_menu_print(WIN *win)
3564 struct menu_input_s *m = win->data;
3565 GAME g = m->data;
3566 struct userdata_s *d = g->data;
3567 struct history_menu_s **hm = d->data;
3568 struct history_menu_s *h = hm[m->top];
3569 int i;
3570 char *p = m->item->name;
3571 int line = m->print_line - 2;
3573 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
3574 * attr_t data type.
3576 attr_t attrs;
3577 short pair;
3578 int total;
3580 for (total = 0; hm[total]; total++);
3581 wattr_get(win->w, &attrs, &pair, NULL);
3582 wattroff(win->w, COLOR_PAIR(pair));
3583 mvwaddch(win->w, m->print_line, 1,
3584 *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));
3585 p++;
3587 if (h->hindex == 0 && line == 0)
3588 waddch(win->w, ACS_ULCORNER | CP_HISTORY_MENU_LG);
3589 else if ((!hm[h->hindex + (win->rows - 5) + 1] && line == win->rows - 5) ||
3590 (m->top + line == total - 1))
3591 waddch(win->w, ACS_LLCORNER | CP_HISTORY_MENU_LG);
3592 else if (hm[m->top + 1]->ravlevel != h->ravlevel || !h->ravlevel)
3593 waddch(win->w, ACS_LTEE | CP_HISTORY_MENU_LG);
3594 else
3595 waddch(win->w, ACS_VLINE | CP_HISTORY_MENU_LG);
3597 wattron(win->w, COLOR_PAIR(pair) | attrs);
3599 for (i = 2; *p; p++, i++)
3600 waddch(win->w, (*p == '!') ? *p | A_BOLD : *p);
3602 while (i++ < win->cols - 2)
3603 waddch(win->w, ' ');
3606 void history_menu(GAME g)
3608 struct menu_key_s **keys = NULL;
3610 add_menu_key(&keys, KEY_ESCAPE, history_menu_quit);
3611 add_menu_key(&keys, 'M', history_menu_quit);
3612 add_menu_key(&keys, KEY_UP, history_menu_prev);
3613 add_menu_key(&keys, KEY_DOWN, history_menu_next);
3614 add_menu_key(&keys, KEY_F(1), history_menu_help);
3615 add_menu_key(&keys, CTRL_KEY('a'), history_menu_annotate);
3616 add_menu_key(&keys, CTRL_KEY('d'), history_menu_details);
3617 add_menu_key(&keys, '\n', history_menu_view_annotation);
3618 construct_menu(MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES,
3619 TAG_WIDTH, 0, config.boardleft ? BOARD_WIDTH : 0,
3620 _("Move History Tree"), 1, get_history_items, keys, g,
3621 history_menu_print, history_menu_exit);
3624 void do_history_menu()
3626 history_menu(gp);
3629 void do_history_half_move_toggle()
3631 movestep = (movestep == 1) ? 2 : 1;
3632 update_history_window(gp);
3635 void do_history_rotate_board()
3637 struct userdata_s *d = gp->data;
3638 d->rotate = !d->rotate;
3641 void do_history_jump_next()
3643 struct userdata_s *d = gp->data;
3645 pgn_history_next(gp, d->b, (keycount > 0) ?
3646 config.jumpcount * keycount * movestep :
3647 config.jumpcount * movestep);
3650 void do_history_jump_prev()
3652 struct userdata_s *d = gp->data;
3654 pgn_history_prev(gp, d->b, (keycount) ?
3655 config.jumpcount * keycount * movestep :
3656 config.jumpcount * movestep);
3659 void do_history_prev()
3661 struct userdata_s *d = gp->data;
3663 pgn_history_prev(gp, d->b,
3664 (keycount) ? keycount * movestep : movestep);
3667 void do_history_next()
3669 struct userdata_s *d = gp->data;
3671 pgn_history_next(gp, d->b, (keycount) ?
3672 keycount * movestep : movestep);
3675 void do_history_mode_finalize(struct userdata_s *d)
3677 pushkey = 0;
3678 d->mode = MODE_PLAY;
3681 void do_history_mode_confirm(WIN *win)
3683 struct userdata_s *d = gp->data;
3684 wchar_t str[] = { win->c, 0 };
3686 if (!wcscmp (str, resume_wchar)) {
3687 pgn_history_free(gp->hp, gp->hindex);
3688 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3690 #if 0
3691 case 'C':
3692 case 'c':
3693 if (pgn_history_rav_new(gp, d->b,
3694 gp->hindex) != E_PGN_OK)
3695 return;
3697 break;
3698 #endif
3699 else
3700 return;
3702 if (!TEST_FLAG(d->flags, CF_HUMAN)) {
3703 char *fen = pgn_game_to_fen(gp, d->b);
3705 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
3706 free (fen);
3709 do_history_mode_finalize(d);
3712 void do_history_toggle()
3714 struct userdata_s *d = gp->data;
3716 // FIXME Resuming from previous history could append to a RAV.
3717 if (gp->hindex != pgn_history_total(gp->hp)) {
3718 if (!pushkey)
3719 construct_message(NULL, _ ("What would you like to do?"), 0, 1,
3720 NULL, NULL, NULL, do_history_mode_confirm, 0, 0,
3721 _("The current move is not the final move of this round. Press \"%ls\" to resume a game from the current move and discard future moves or any other key to cancel."),
3722 resume_wchar);
3723 return;
3725 else {
3726 if (TEST_FLAG(gp->flags, GF_GAMEOVER))
3727 return;
3730 if (gp->side != gp->turn) {
3731 d->play_mode = PLAY_EH;
3733 else {
3734 d->play_mode = PLAY_HE;
3735 d->rotate = FALSE;
3738 do_history_mode_finalize(d);
3741 void do_history_annotate()
3743 int n = gp->hindex;
3745 if (n && gp->hp[n - 1]->move)
3746 n--;
3747 else
3748 return;
3750 do_annotate_move(gp->hp[n]);
3753 void do_history_help()
3755 wchar_t *buf = build_help(history_keys);
3757 construct_message(_("History Mode Keys (* = can take a repeat count)"),
3758 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
3759 buf, do_more_help, 0, 1, "%ls", buf);
3762 void do_history_find(int which)
3764 struct input_data_s *in;
3765 int *p;
3767 if (pgn_history_total(gp->hp) < 2)
3768 return;
3770 in = Calloc(1, sizeof(struct input_data_s));
3771 p = Malloc(sizeof(int));
3772 *p = which;
3773 in->data = p;
3774 in->efunc = do_find_move_exp;
3776 if (!*moveexp || which == 0) {
3777 construct_input(_("Find Move Text Expression"), NULL, 1, 0, NULL, NULL, NULL,
3778 0, in, INPUT_HIST_MOVE_EXP, -1);
3779 return;
3782 free(p);
3783 free(in);
3784 do_find_move_exp_finalize(0, which);
3787 void do_history_find_new()
3789 do_history_find(0);
3792 void do_history_find_prev()
3794 do_history_find(-1);
3797 void do_history_find_next()
3799 do_history_find(1);
3802 void do_history_rav(int which)
3804 struct userdata_s *d = gp->data;
3806 rav_next_prev(gp, d->b, which);
3809 void do_history_rav_next()
3811 do_history_rav(1);
3814 void do_history_rav_prev()
3816 do_history_rav(0);
3819 void do_history_jump()
3821 struct input_data_s *in;
3823 if (pgn_history_total(gp->hp) < 2)
3824 return;
3826 if (!keycount) {
3827 in = Calloc(1, sizeof(struct input_data_s));
3828 in->efunc = do_move_jump;
3830 construct_input(_("Jump to Move Number"), NULL, 1, 1, NULL,
3831 NULL, NULL, 0, in, -1, 0);
3832 return;
3835 do_move_jump_finalize(keycount);
3838 static void free_userdata_once(GAME g)
3840 struct userdata_s *d = g->data;
3842 if (!d)
3843 return;
3845 if (d->engine) {
3846 stop_engine(g);
3848 if (d->engine->enginebuf) {
3849 int n;
3851 for (n = 0; d->engine->enginebuf[n]; n++)
3852 free(d->engine->enginebuf[n]);
3854 free(d->engine->enginebuf);
3857 if (d->engine->queue) {
3858 struct queue_s **q;
3860 for (q = d->engine->queue; *q; q++)
3861 free(*q);
3863 free(d->engine->queue);
3866 free(d->engine);
3869 #ifdef WITH_LIBPERL
3870 if (d->perlfen)
3871 free(d->perlfen);
3873 if (d->oldfen)
3874 free(d->oldfen);
3875 #endif
3877 free(d);
3878 g->data = NULL;
3881 static void free_userdata()
3883 int i;
3885 for (i = 0; i < gtotal; i++) {
3886 free_userdata_once(game[i]);
3887 game[i]->data = NULL;
3891 void update_loading_window(int n)
3893 char buf[16];
3895 if (!loadingw) {
3896 loadingw = newwin(3, COLS / 2, CALCPOSY(3), CALCPOSX(COLS / 2));
3897 loadingp = new_panel(loadingw);
3898 wbkgd(loadingw, CP_MESSAGE_WINDOW);
3901 wmove(loadingw, 0, 0);
3902 wclrtobot(loadingw);
3903 wattron(loadingw, CP_MESSAGE_BORDER);
3904 box(loadingw, ACS_VLINE, ACS_HLINE);
3905 wattroff(loadingw, CP_MESSAGE_BORDER);
3906 mvwprintw(loadingw, 1, CENTER_INT((COLS / 2), 11 +
3907 strlen(itoa(gtotal, buf))),
3908 _("Loading... %i%% (%i games)"), n, gtotal);
3909 update_panels();
3910 doupdate();
3913 static void init_userdata_once(GAME g, int n)
3915 struct userdata_s *d = NULL;
3917 d = Calloc(1, sizeof(struct userdata_s));
3918 d->n = n;
3919 d->c_row = 2, d->c_col = 5;
3920 SET_FLAG(d->flags, CF_NEW);
3921 g->data = d;
3923 if (pgn_board_init_fen(g, d->b, NULL) != E_PGN_OK)
3924 pgn_board_init(d->b);
3927 void init_userdata()
3929 int i;
3931 for (i = 0; i < gtotal; i++)
3932 init_userdata_once(game[i], i);
3935 void fix_marks(int *start, int *end)
3937 int i;
3939 *start = (*start < 0) ? 0 : *start;
3940 *end = (*end < 0) ? 0 : *end;
3942 if (*start > *end) {
3943 i = *start;
3944 *start = *end;
3945 *end = i + 1;
3948 *end = (*end > gtotal) ? gtotal : *end;
3951 void do_new_game_finalize(GAME g)
3953 struct userdata_s *d = g->data;
3955 d->mode = MODE_PLAY;
3956 update_status_notify(g, NULL);
3957 d->rotate = FALSE;
3958 d->go_move = 0;
3961 void do_new_game_from_scratch(WIN *win)
3963 wchar_t str[] = { win->c, 0 };
3965 if (wcscmp (str, yes_wchar))
3966 return;
3968 stop_clock();
3969 free_userdata();
3970 pgn_parse(NULL);
3971 gp = game[gindex];
3972 add_custom_tags(&gp->tag);
3973 init_userdata();
3974 loadfile[0] = 0;
3975 do_new_game_finalize(gp);
3978 void do_new_game()
3980 pgn_new_game();
3981 gp = game[gindex];
3982 add_custom_tags(&gp->tag);
3983 init_userdata_once(gp, gindex);
3984 do_new_game_finalize(gp);
3987 void do_game_delete_finalize(int n)
3989 struct userdata_s *d;
3991 delete_game((!n) ? gindex : -1);
3992 d = gp->data;
3993 if (d->mode != MODE_EDIT)
3994 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3997 void do_game_delete_confirm(WIN *win)
3999 int *n;
4000 wchar_t str[] = { win->c, 0 };
4002 if (wcscmp (str, yes_wchar)) {
4003 free(win->data);
4004 return;
4007 n = (int *)win->data;
4008 do_game_delete_finalize(*n);
4009 free(win->data);
4012 void do_game_delete()
4014 char *tmp = NULL;
4015 int i, n;
4016 struct userdata_s *d;
4017 int *p;
4019 if (gtotal < 2) {
4020 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4021 return;
4024 tmp = NULL;
4026 for (i = n = 0; i < gtotal; i++) {
4027 d = game[i]->data;
4029 if (TEST_FLAG(d->flags, CF_DELETE))
4030 n++;
4033 if (!n)
4034 tmp = _("Delete the current game?");
4035 else {
4036 if (n == gtotal) {
4037 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4038 return;
4041 tmp = _("Delete all games marked for deletion?");
4044 if (config.deleteprompt) {
4045 p = Malloc(sizeof(int));
4046 *p = n;
4047 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, p,
4048 do_game_delete_confirm, 0, 0, tmp);
4049 return;
4052 do_game_delete_finalize(n);
4055 void do_find_game_exp_finalize(int which)
4057 struct userdata_s *d = gp->data;
4058 int n;
4060 if ((n = find_game_exp(gameexp, (which == -1) ? 0 : 1,
4061 (keycount) ? keycount : 1)) == -1) {
4062 update_status_notify(gp, "%s", _("No matches found"));
4063 return;
4066 gindex = n;
4067 d = gp->data;
4069 if (pgn_history_total(gp->hp))
4070 d->mode = MODE_HISTORY;
4072 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4075 void do_find_game_exp(WIN *win)
4077 struct input_data_s *in = win->data;
4078 int *n = in->data;
4079 int c = *n;
4081 if (in->str) {
4082 strncpy(gameexp, in->str, sizeof(gameexp));
4083 gameexp[sizeof(gameexp)-1] = 0;
4085 if (c == '?')
4086 c = '}';
4088 do_find_game_exp_finalize(c);
4089 free(in->str);
4092 free(in->data);
4093 free(in);
4096 void do_game_jump_finalize(int n)
4098 struct userdata_s *d;
4100 if (--n > gtotal - 1 || n < 0)
4101 return;
4103 gindex = n;
4104 gp = game[gindex];
4105 d = gp->data;
4106 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4107 update_status_notify(gp, NULL);
4110 void do_game_jump(WIN *win)
4112 struct input_data_s *in = win->data;
4114 if (!in->str || !isinteger(in->str)) {
4115 if (in->str)
4116 free(in->str);
4118 free(in);
4119 return;
4122 do_game_jump_finalize(atoi(in->str));
4123 free(in->str);
4124 free(in);
4127 void do_load_file(WIN *win)
4129 struct input_data_s *in = win->data;
4130 char *tmp = in->str;
4131 struct userdata_s *d;
4132 PGN_FILE *pgn = NULL;
4133 int n;
4135 if (!in->str) {
4136 free(in);
4137 return;
4140 if ((tmp = pathfix(tmp)) == NULL)
4141 goto done;
4143 n = pgn_open(tmp, "r", &pgn);
4145 if (n == E_PGN_ERR) {
4146 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, strerror(errno));
4147 goto done;
4149 else if (n == E_PGN_INVALID) {
4150 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, _("Not a regular file"));
4151 goto done;
4154 free_userdata();
4156 if (pgn_parse(pgn) == E_PGN_ERR) {
4157 del_panel(loadingp);
4158 delwin(loadingw);
4159 loadingw = NULL;
4160 loadingp = NULL;
4161 init_userdata();
4162 goto done;
4165 del_panel(loadingp);
4166 delwin(loadingw);
4167 loadingw = NULL;
4168 loadingp = NULL;
4169 init_userdata();
4170 strncpy(loadfile, tmp, sizeof(loadfile));
4171 loadfile[sizeof(loadfile)-1] = 0;
4172 gp = game[gindex];
4173 d = gp->data;
4175 if (pgn_history_total(gp->hp))
4176 d->mode = MODE_HISTORY;
4178 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4180 fm_loaded_file = TRUE;
4181 d->rotate = FALSE;
4183 done:
4184 pgn_close(pgn);
4186 if (in->str)
4187 free(in->str);
4189 free(in);
4192 void do_game_save(WIN *win)
4194 struct input_data_s *in = win->data;
4195 int *x = in->data;
4196 int n = *x;
4197 char *tmp = in->str;
4198 char tfile[FILENAME_MAX];
4199 char *p;
4200 int i;
4201 struct userdata_s *d;
4203 if (!tmp || (tmp = pathfix(tmp)) == NULL)
4204 goto done;
4206 if (pgn_is_compressed(tmp) == E_PGN_ERR) {
4207 p = tmp + strlen(tmp) - 1;
4209 if (*p != 'n' || *(p-1) != 'g' || *(p-2) != 'p' ||
4210 *(p-3) != '.') {
4211 snprintf(tfile, sizeof(tfile), "%s.pgn", tmp);
4212 tmp = tfile;
4217 * When in edit mode, update the FEN tag.
4219 if (n == -1) {
4220 for (i = 0; i < gtotal; i++) {
4221 d = game[i]->data;
4223 if (d->mode == MODE_EDIT) {
4224 char *fen = pgn_game_to_fen(game[i], d->b);
4226 pgn_tag_add(&game[i]->tag, "FEN", fen);
4227 free (fen);
4231 else {
4232 d = game[n]->data;
4234 if (d->mode == MODE_EDIT) {
4235 char *fen = pgn_game_to_fen(game[n], d->b);
4237 pgn_tag_add(&game[n]->tag, "FEN", fen);
4238 free (fen);
4242 save_pgn(tmp, n);
4244 done:
4245 if (in->str)
4246 free(in->str);
4248 free(in->data);
4249 free(in);
4252 void do_get_game_save_input(int n)
4254 struct input_data_s *in = Calloc(1, sizeof(struct input_data_s));
4255 int *p = Malloc(sizeof(int));
4257 in->efunc = do_game_save;
4258 *p = n;
4259 in->data = p;
4261 construct_input(_("Save Game Filename"), loadfile, 1, 1, _("Type TAB for file browser"),
4262 file_browser, NULL, '\t', in, INPUT_HIST_FILE, -1);
4265 void do_game_save_multi_confirm(WIN *win)
4267 int i;
4268 wchar_t str[] = { win->c, 0 };
4270 if (!wcscmp (str, current_wchar))
4271 i = gindex;
4272 else if (!wcscmp (str, all_wchar))
4273 i = -1;
4274 else {
4275 update_status_notify(gp, "%s", _("Save game aborted."));
4276 return;
4279 do_get_game_save_input(i);
4282 void do_global_about()
4284 cmessage(_("ABOUT"), ANY_KEY_STR,
4285 _("%s\nUsing %s with %i colors and %i color pairs\n%s"),
4286 PACKAGE_STRING, curses_version(), COLORS, COLOR_PAIRS,
4287 COPYRIGHT);
4290 void global_game_next_prev(int which)
4292 struct userdata_s *d;
4294 game_next_prev(gp, (which == 1) ? 1 : 0,
4295 (keycount) ? keycount : 1);
4296 d = gp->data;
4298 if (delete_count) {
4299 if (which == 1) {
4300 markend = markstart + delete_count;
4301 delete_count = 0;
4303 else {
4304 markend = markstart - delete_count + 1;
4305 delete_count = -1; // to fix gindex in the other direction
4308 fix_marks(&markstart, &markend);
4309 do_global_toggle_delete();
4312 if (d->mode == MODE_HISTORY)
4313 pgn_board_update(gp, d->b, gp->hindex);
4314 else if (d->mode == MODE_PLAY)
4315 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4318 void do_global_next_game()
4320 global_game_next_prev(1);
4323 void do_global_prev_game()
4325 global_game_next_prev(0);
4328 void global_find(int which)
4330 struct input_data_s *in;
4331 int *p;
4333 if (gtotal < 2)
4334 return;
4336 in = Calloc(1, sizeof(struct input_data_s));
4337 p = Malloc(sizeof(int));
4338 *p = which;
4339 in->data = p;
4340 in->efunc = do_find_game_exp;
4342 if (!*gameexp || which == 0) {
4343 construct_input(_("Find Game by Tag Expression"), NULL, 1, 0,
4344 _("[name expression:]value expression"), NULL, NULL, 0, in,
4345 INPUT_HIST_GAME_EXP, -1);
4346 return;
4349 free(p);
4350 free(in);
4351 do_find_game_exp_finalize(which);
4354 void do_global_find_new()
4356 global_find(0);
4359 void do_global_find_next()
4361 global_find(1);
4364 void do_global_find_prev()
4366 global_find(-1);
4369 void do_global_game_jump()
4371 if (gtotal < 2)
4372 return;
4374 if (!keycount) {
4375 struct input_data_s *in;
4377 in = Calloc(1, sizeof(struct input_data_s));
4378 in->efunc = do_game_jump;
4379 construct_input(_("Jump to Game Number"), NULL, 1, 1, NULL, NULL, NULL, 0, in,
4380 -1, 0);
4381 return;
4384 do_game_jump_finalize(keycount);
4387 void do_global_toggle_delete()
4389 int i;
4391 pushkey = 0;
4393 if (gtotal < 2)
4394 return;
4396 if (keycount && delete_count == 0) {
4397 markstart = gindex;
4398 delete_count = keycount;
4399 update_status_notify(gp, "%s (delete)", status.notify);
4400 return;
4403 if (markstart >= 0 && markend >= 0) {
4404 for (i = markstart; i < markend; i++) {
4405 if (toggle_delete_flag(i)) {
4406 return;
4410 gindex = (delete_count < 0) ? markstart : i - 1;
4412 else {
4413 if (toggle_delete_flag(gindex))
4414 return;
4417 markstart = markend = -1;
4418 delete_count = 0;
4419 update_status_window(gp);
4422 void do_global_delete_game()
4424 do_game_delete();
4427 void do_global_tag_edit()
4429 struct userdata_s *d = gp->data;
4431 edit_tags(gp, d->b, 1);
4434 void do_global_tag_view()
4436 struct userdata_s *d = gp->data;
4438 edit_tags(gp, d->b, 0);
4441 void do_global_resume_game()
4443 struct input_data_s *in;
4445 in = Calloc(1, sizeof(struct input_data_s));
4446 in->efunc = do_load_file;
4447 construct_input(_("Load Filename"), NULL, 1, 1, _("Type TAB for file browser"), file_browser,
4448 NULL, '\t', in, INPUT_HIST_FILE, -1);
4451 void do_global_save_game()
4453 if (gtotal > 1) {
4454 construct_message(NULL, _("What would you like to do?"), 0, 1,
4455 NULL, NULL, NULL, do_game_save_multi_confirm, 0, 0,
4456 _("There is more than one game loaded. Press \"%ls\" to save the current game, \"%ls\" to save all games or any other key to cancel."),
4457 current_wchar, all_wchar);
4458 return;
4461 do_get_game_save_input(-1);
4464 void do_global_new_game()
4466 do_new_game();
4469 void do_global_copy_game()
4471 int g = gindex;
4472 int i, n;
4473 struct userdata_s *d;
4475 do_global_new_game();
4476 d = gp->data;
4477 n = pgn_tag_total(game[g]->tag);
4479 for (i = 0; i < n; i++)
4480 pgn_tag_add(&gp->tag, game[g]->tag[i]->name,
4481 game[g]->tag[i]->value);
4483 pgn_board_init_fen (gp, d->b, NULL);
4484 n = pgn_history_total(game[g]->history);
4486 // FIXME RAV
4487 for (i = 0; i < n; i++) {
4488 char *frfr = NULL;
4489 char *move = strdup (game[g]->history[i]->move);
4491 if (pgn_parse_move(gp, d->b, &move, &frfr) != E_PGN_OK) {
4492 free (move);
4493 SET_FLAG(gp->flags, GF_PERROR);
4494 return;
4497 pgn_history_add(gp, d->b, move);
4498 free (move);
4499 free(frfr);
4500 pgn_switch_turn(gp);
4503 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4506 void do_global_new_all()
4508 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
4509 do_new_game_from_scratch, 0, 0, "%s", _("Really start a new game from scratch?"));
4512 void do_quit(WIN *win)
4514 wchar_t str[] = { win->c, 0 };
4516 if (wcscmp (str, yes_wchar))
4517 return;
4519 quit = 1;
4522 void do_global_quit()
4524 if (config.exitdialogbox)
4525 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
4526 do_quit, 0, 0, _("Want to Quit?"));
4527 else
4528 quit = 1;
4531 void do_global_toggle_engine_window()
4533 if (!enginew) {
4534 enginew = newwin(LINES, COLS, 0, 0);
4535 enginep = new_panel(enginew);
4536 window_draw_title(enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
4537 CP_MESSAGE_BORDER);
4538 hide_panel(enginep);
4541 if (panel_hidden(enginep)) {
4542 update_engine_window(gp);
4543 top_panel(enginep);
4545 else {
4546 hide_panel(enginep);
4550 void do_global_toggle_board_details()
4552 do_board_details();
4555 void do_global_toggle_strict_castling()
4557 do_toggle_strict_castling();
4560 // Global and other keys.
4561 static int globalkeys()
4563 struct userdata_s *d = gp->data;
4564 int i;
4567 * These cannot be modified and other game mode keys cannot conflict with
4568 * these.
4570 switch (input_c) {
4571 case KEY_ESCAPE:
4572 d->sp.icon = d->sp.srow = d->sp.scol = 0;
4573 markend = markstart = 0;
4575 if (keycount) {
4576 keycount = 0;
4577 update_status_notify(gp, NULL);
4580 if (config.validmoves)
4581 pgn_reset_valid_moves(d->b);
4583 return 1;
4584 case '0' ... '9':
4585 i = input_c - '0';
4587 if (keycount)
4588 keycount = keycount * 10 + i;
4589 else
4590 keycount = i;
4592 update_status_notify(gp, _("Repeat %i"), keycount);
4593 return -1;
4594 case KEY_UP:
4595 if (d->mode == MODE_HISTORY)
4596 return 0;
4598 if (keycount)
4599 d->c_row += keycount;
4600 else
4601 d->c_row++;
4603 if (d->c_row > 8)
4604 d->c_row = 1;
4606 return 1;
4607 case KEY_DOWN:
4608 if (d->mode == MODE_HISTORY)
4609 return 0;
4611 if (keycount) {
4612 d->c_row -= keycount;
4613 update_status_notify(gp, NULL);
4615 else
4616 d->c_row--;
4618 if (d->c_row < 1)
4619 d->c_row = 8;
4621 return 1;
4622 case KEY_LEFT:
4623 if (d->mode == MODE_HISTORY)
4624 return 0;
4626 if (keycount)
4627 d->c_col -= keycount;
4628 else
4629 d->c_col--;
4631 if (d->c_col < 1)
4632 d->c_col = 8;
4634 return 1;
4635 case KEY_RIGHT:
4636 if (d->mode == MODE_HISTORY)
4637 return 0;
4639 if (keycount)
4640 d->c_col += keycount;
4641 else
4642 d->c_col++;
4644 if (d->c_col > 8)
4645 d->c_col = 1;
4647 return 1;
4648 case KEY_RESIZE:
4649 return 1;
4650 case 0:
4651 default:
4652 for (i = 0; global_keys[i]; i++) {
4653 if (input_c == global_keys[i]->c && global_keys[i]->f) {
4654 (*global_keys[i]->f)();
4655 return 1;
4658 break;
4661 return 0;
4664 #ifdef WITH_LIBPERL
4665 static void perl_error(const char *fmt, ...)
4667 va_list ap;
4668 char *buf;
4670 va_start(ap, fmt);
4671 vasprintf(&buf, fmt, ap);
4672 va_end(ap);
4674 message(ERROR_STR, ANY_KEY_STR, "%s", buf);
4675 free(buf);
4678 static void do_perl_finalize(WIN *win)
4680 struct input_data_s *in = win->data;
4681 GAME g = in->data;
4682 struct userdata_s *d = g->data;
4683 char *filename;
4684 char *result = NULL;
4685 char *arg = NULL;
4686 int n;
4688 asprintf(&filename, "%s/perl.pl", config.datadir);
4690 if (!in->str)
4691 goto done;
4693 if (perl_init_file(filename, perl_error))
4694 goto done;
4696 arg = pgn_game_to_fen(g, d->b);
4698 if (perl_call_sub(trim(in->str), arg, &result))
4699 goto done;
4701 d->perlfen = pgn_game_to_fen(g, d->b);
4702 d->perlflags = g->flags;
4704 if (pgn_board_init_fen(g, d->b, result) != E_PGN_OK) {
4705 message(ERROR_STR, ANY_KEY_STR, "%s", _("FEN parse error."));
4706 pgn_board_init_fen(g, d->b, d->perlfen);
4707 g->flags = d->perlflags;
4708 free(d->perlfen);
4709 d->perlfen = NULL;
4710 goto done;
4713 SET_FLAG(d->flags, CF_PERL);
4714 n = pgn_tag_find(g->tag, "FEN");
4716 if (n != E_PGN_ERR)
4717 d->oldfen = strdup(g->tag[n]->value);
4719 pgn_tag_add(&g->tag, "FEN", result);
4720 update_status_notify(g, "%s", ANY_KEY_STR);
4721 update_all(g);
4723 done:
4724 free(result);
4725 free(arg);
4726 free(in->str);
4727 free(in);
4728 free(filename);
4731 void do_global_perl()
4733 struct input_data_s *in;
4735 in = Calloc(1, sizeof(struct input_data_s));
4736 in->data = gp;
4737 in->efunc = do_perl_finalize;
4738 construct_input(_("PERL Subroutine Filter"), NULL, 1, 0, NULL, NULL, NULL, 0, in, INPUT_HIST_PERL, -1);
4740 #endif
4743 * A macro may contain a key that belongs to another macro so macro_match will
4744 * need to be updated to the new index of the matching macro.
4746 static void find_macro(struct userdata_s *d)
4748 int i;
4751 * Macros can't contain macros when in a window.
4753 if (wins)
4754 return;
4756 again:
4757 for (i = 0; macros[i]; i++) {
4758 if ((macros[i]->mode == -1 || macros[i]->mode == d->mode) &&
4759 input_c == macros[i]->c) {
4760 input_c = macros[i]->keys[macros[i]->n++];
4762 if (!macro_depth_n && macro_match > -1) {
4763 macro_depth = realloc(macro_depth, (macro_depth_n + 1) * sizeof(int));
4764 macro_depth[macro_depth_n++] = macro_match;
4767 macro_depth = realloc(macro_depth, (macro_depth_n + 1) * sizeof(int));
4768 macro_depth[macro_depth_n++] = i;
4769 macro_match = i;
4770 goto again;
4776 * Resets the position in each macro to the first key.
4778 static void reset_macros()
4780 int i;
4781 struct userdata_s *d = gp->data;
4783 again:
4784 if (macro_depth_n > 0) {
4785 macro_depth_n--;
4786 macro_match = macro_depth[macro_depth_n];
4788 if (macros[macro_match]->n >= macros[macro_match]->total)
4789 goto again;
4791 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
4792 find_macro(d);
4793 return;
4796 for (i = 0; macros[i]; i++)
4797 macros[i]->n = 0;
4799 free(macro_depth);
4800 macro_depth = NULL;
4801 macro_depth_n = 0;
4802 macro_match = -1;
4805 void game_loop()
4807 struct userdata_s *d;
4809 macro_match = -1;
4810 gindex = gtotal - 1;
4811 gp = game[gindex];
4812 d = gp->data;
4814 if (pgn_history_total(gp->hp))
4815 d->mode = MODE_HISTORY;
4816 else {
4817 d->mode = MODE_PLAY;
4818 d->play_mode = PLAY_HE;
4821 d->rotate = FALSE;
4822 d->go_move = 0;
4823 d->pm_undo = FALSE;
4824 d->pm_frfr[0] = '\0';
4826 if (d->mode == MODE_HISTORY)
4827 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4829 update_status_notify(gp, "%s", _("Type F1 for help"));
4830 movestep = 2;
4831 flushinp();
4832 update_all(gp);
4833 wtimeout(boardw, WINDOW_TIMEOUT);
4835 while (!quit) {
4836 int n = 0, i;
4837 char fdbuf[8192] = {0};
4838 int len;
4839 struct timeval tv = {0, 0};
4840 fd_set rfds, wfds;
4841 WIN *win = NULL;
4842 WINDOW *wp = NULL;
4844 FD_ZERO(&rfds);
4845 FD_ZERO(&wfds);
4847 for (i = 0; i < gtotal; i++) {
4848 d = game[i]->data;
4850 if (d->engine && d->engine->pid != -1) {
4851 if (d->engine->fd[ENGINE_IN_FD] > 2) {
4852 if (d->engine->fd[ENGINE_IN_FD] > n)
4853 n = d->engine->fd[ENGINE_IN_FD];
4855 FD_SET(d->engine->fd[ENGINE_IN_FD], &rfds);
4858 if (d->engine->fd[ENGINE_OUT_FD] > 2) {
4859 if (d->engine->fd[ENGINE_OUT_FD] > n)
4860 n = d->engine->fd[ENGINE_OUT_FD];
4862 FD_SET(d->engine->fd[ENGINE_OUT_FD], &wfds);
4867 if (n) {
4868 if ((n = select(n + 1, &rfds, &wfds, NULL, &tv)) > 0) {
4869 for (i = 0; i < gtotal; i++) {
4870 d = game[i]->data;
4872 if (d->engine && d->engine->pid != -1) {
4873 if (FD_ISSET(d->engine->fd[ENGINE_IN_FD], &rfds)) {
4874 len = read(d->engine->fd[ENGINE_IN_FD], fdbuf,
4875 sizeof(fdbuf));
4877 if (len > 0) {
4878 if (d->engine->iobuf)
4879 d->engine->iobuf = Realloc(d->engine->iobuf, d->engine->len + len + 1);
4880 else
4881 d->engine->iobuf = Calloc(1, len + 1);
4883 memcpy(&(d->engine->iobuf[d->engine->len]), &fdbuf, len);
4884 d->engine->len += len;
4885 d->engine->iobuf[d->engine->len] = 0;
4888 * The fdbuf is full or no newline
4889 * was found. So we'll append the next
4890 * read() to this games buffer.
4892 if (d->engine->iobuf[d->engine->len - 1] != '\n')
4893 continue;
4895 parse_engine_output(game[i], d->engine->iobuf);
4896 free(d->engine->iobuf);
4897 d->engine->iobuf = NULL;
4898 d->engine->len = 0;
4900 else if (len == -1) {
4901 if (errno != EAGAIN) {
4902 cmessage(ERROR_STR, ANY_KEY_STR, "Engine read(): %s",
4903 strerror(errno));
4904 waitpid(d->engine->pid, &n, 0);
4905 free(d->engine);
4906 d->engine = NULL;
4907 break;
4912 if (FD_ISSET(d->engine->fd[ENGINE_OUT_FD], &wfds)) {
4913 if (d->engine->queue)
4914 send_engine_command(game[i]);
4919 else {
4920 if (n == -1)
4921 cmessage(ERROR_STR, ANY_KEY_STR, "select(): %s", strerror(errno));
4922 /* timeout */
4926 gp = game[gindex];
4927 d = gp->data;
4930 * This is needed to detect terminal resizing.
4932 doupdate();
4933 if (LINES != LINES_OLD || COLS != COLS_OLD) {
4934 COLS_OLD = COLS;
4935 LINES_OLD = LINES;
4936 do_window_resize();
4940 * Finds the top level window in the window stack so we know what
4941 * window the wget_wch()'ed key belongs to.
4943 if (wins) {
4944 for (i = 0; wins[i]; i++);
4945 win = wins[i-1];
4946 wp = win->w;
4947 wtimeout(wp, WINDOW_TIMEOUT);
4949 else
4950 wp = boardw;
4952 if (!i && pushkey)
4953 input_c = pushkey;
4954 else {
4955 if (!pushkey) {
4956 if (macros && macro_match >= 0) {
4957 if (macros[macro_match]->n >= macros[macro_match]->total)
4958 reset_macros();
4959 else {
4960 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
4961 find_macro(d);
4964 else {
4965 if (wget_wch(wp, &input_c) == ERR || input_c == KEY_RESIZE)
4966 continue;
4969 else
4970 input_c = pushkey;
4972 if (win) {
4973 win->c = input_c;
4976 * Run the function associated with the window. When the
4977 * function returns 0 win->efunc is ran (if not NULL) with
4978 * win as the one and only parameter. Then the window is
4979 * destroyed.
4981 * The exit function may create another window which will
4982 * mess up the window stack when window_destroy() is called.
4983 * So don't destory the window until the top window is
4984 * destroyable. See window_destroy().
4986 if ((*win->func)(win) == 0) {
4987 if (win->efunc)
4988 (*win->efunc)(win);
4990 win->keep = 1;
4991 window_destroy(win);
4992 update_all(gp);
4995 continue;
4999 if (!keycount && status.notify)
5000 update_status_notify(gp, NULL);
5002 #ifdef WITH_LIBPERL
5003 if (TEST_FLAG(d->flags, CF_PERL)) {
5004 CLEAR_FLAG(d->flags, CF_PERL);
5005 pgn_board_init_fen(gp, d->b, d->perlfen);
5006 gp->flags = d->perlflags;
5007 free(d->perlfen);
5008 pgn_tag_add(&gp->tag, "FEN", d->oldfen);
5009 free(d->oldfen);
5010 d->perlfen = d->oldfen = NULL;
5011 update_all(gp);
5012 continue;
5014 #endif
5016 if (macros && macro_match < 0)
5017 find_macro(d);
5019 if ((n = globalkeys()) == 1) {
5020 if (macro_match == -1)
5021 keycount = 0;
5023 goto refresh;
5025 else if (n == -1)
5026 goto refresh;
5028 switch (d->mode) {
5029 case MODE_EDIT:
5030 for (i = 0; edit_keys[i]; i++) {
5031 if (input_c == edit_keys[i]->c) {
5032 (*edit_keys[i]->f)();
5033 break;
5036 break;
5037 case MODE_PLAY:
5038 for (i = 0; play_keys[i]; i++) {
5039 if (input_c == play_keys[i]->c) {
5040 (*play_keys[i]->f)();
5041 goto done;
5045 do_play_config_command();
5046 break;
5047 case MODE_HISTORY:
5048 for (i = 0; history_keys[i]; i++) {
5049 if (input_c == history_keys[i]->c) {
5050 (*history_keys[i]->f)();
5051 break;
5054 break;
5055 default:
5056 break;
5059 done:
5060 if (keycount)
5061 update_status_notify(gp, NULL);
5063 keycount = 0;
5065 refresh:
5066 update_all(gp);
5070 void usage(const char *pn, int ret)
5072 fprintf((ret) ? stderr : stdout, "%s%s",
5073 #ifdef DEBUG
5075 "Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
5076 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"),
5077 #else
5079 "Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"),
5080 #endif
5082 " -p Load PGN file.\n"
5083 " -V Validate a game file.\n"
5084 " -S Validate and output a PGN formatted game.\n"
5085 " -R Like -S but write a reduced PGN formatted game.\n"
5086 " -t Also write custom PGN tags from config file.\n"
5087 " -E Stop processing on file parsing error (overrides config).\n"
5088 " -C Enable strict castling (overrides config).\n"
5089 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, overrides config).\n"
5090 " -v Version information.\n"
5091 " -h This help text.\n"));
5093 exit(ret);
5096 void cleanup_all()
5098 int i;
5100 stop_clock();
5101 free_userdata();
5102 pgn_free_all();
5103 free(config.engine_cmd);
5104 free(config.pattern);
5105 free(config.ccfile);
5106 free(config.nagfile);
5107 free(config.configfile);
5109 if (config.keys) {
5110 for (i = 0; config.keys[i]; i++) {
5111 free(config.keys[i]->str);
5112 free(config.keys[i]);
5115 free(config.keys);
5118 if (config.einit) {
5119 for (i = 0; config.einit[i]; i++)
5120 free(config.einit[i]);
5122 free(config.einit);
5125 if (config.tag)
5126 pgn_tag_free(config.tag);
5128 free(config.datadir);
5130 if (curses_initialized) {
5131 del_panel(boardp);
5132 del_panel(historyp);
5133 del_panel(statusp);
5134 del_panel(tagp);
5135 delwin(boardw);
5136 delwin(historyw);
5137 delwin(statusw);
5138 delwin(tagw);
5140 if (enginew) {
5141 del_panel(enginep);
5142 delwin(enginew);
5145 endwin();
5148 #ifdef WITH_LIBPERL
5149 perl_cleanup();
5150 #endif
5153 static void signal_save_pgn(int sig)
5155 char *buf;
5156 time_t now;
5157 char *p = config.savedirectory ? config.savedirectory : config.datadir;
5159 time(&now);
5160 asprintf(&buf, "%s/signal-%i-%li.pgn", p, sig, now);
5162 if (do_game_write(buf, "w", 0, gtotal)) {
5163 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", p, strerror(errno));
5164 update_status_notify(gp, "%s", _("Save game failed."));
5167 free(buf);
5168 quit = 1;
5171 void catch_signal(int which)
5173 switch (which) {
5174 case SIGALRM:
5175 update_clocks();
5176 break;
5177 case SIGPIPE:
5178 if (which == SIGPIPE && quit)
5179 break;
5181 if (which == SIGPIPE)
5182 cmessage(NULL, ANY_KEY_STR, "%s", _("Broken pipe. Quitting."));
5184 cleanup_all();
5185 exit(EXIT_FAILURE);
5186 break;
5187 case SIGSTOP:
5188 savetty();
5189 break;
5190 case SIGCONT:
5191 resetty();
5192 do_window_resize ();
5193 keypad(boardw, TRUE);
5194 break;
5195 case SIGINT:
5196 quit = 1;
5197 break;
5198 case SIGTERM:
5199 signal_save_pgn(which);
5200 break;
5201 default:
5202 break;
5206 void loading_progress(long total, long offset)
5208 int n = (100 * (offset / 100) / (total / 100));
5210 if (curses_initialized)
5211 update_loading_window(n);
5212 else {
5213 fprintf(stderr, _("Loading... %i%% (%i games)%c"), n, gtotal, '\r');
5214 fflush(stderr);
5218 static void set_defaults()
5220 set_config_defaults();
5221 set_default_keys();
5222 filetype = FILE_NONE;
5223 pgn_config_set(PGN_PROGRESS, 1024);
5224 pgn_config_set(PGN_PROGRESS_FUNC, loading_progress);
5227 int main(int argc, char *argv[])
5229 int opt;
5230 struct stat st;
5231 char buf[FILENAME_MAX];
5232 char datadir[FILENAME_MAX];
5233 int ret = EXIT_SUCCESS;
5234 int validate_only = 0, validate_and_write = 0;
5235 int write_custom_tags = 0;
5236 int i = 0;
5237 PGN_FILE *pgn;
5238 int utf8_pieces = -1;
5240 setlocale (LC_ALL, "");
5241 bindtextdomain ("cboard", LOCALE_DIR);
5242 textdomain ("cboard");
5244 /* Solaris 5.9 */
5245 #ifndef HAVE_PROGNAME
5246 __progname = argv[0];
5247 #endif
5249 if ((config.pwd = getpwuid(getuid())) == NULL)
5250 err(EXIT_FAILURE, "getpwuid()");
5252 snprintf(datadir, sizeof(datadir), "%s/.cboard", config.pwd->pw_dir);
5253 config.datadir = strdup(datadir);
5254 snprintf(buf, sizeof(buf), "%s/cc.data", datadir);
5255 config.ccfile = strdup(buf);
5256 snprintf(buf, sizeof(buf), "%s/nag.data", datadir);
5257 config.nagfile = strdup(buf);
5258 snprintf(buf, sizeof(buf), "%s/config", datadir);
5259 config.configfile = strdup(buf);
5261 if (stat(datadir, &st) == -1) {
5262 if (errno == ENOENT) {
5263 if (mkdir(datadir, 0755) == -1)
5264 err(EXIT_FAILURE, "%s", datadir);
5266 else
5267 err(EXIT_FAILURE, "%s", datadir);
5269 stat(datadir, &st);
5272 if (!S_ISDIR(st.st_mode))
5273 errx(EXIT_FAILURE, "%s: %s", datadir, _("Not a directory."));
5275 set_defaults();
5277 #ifdef DEBUG
5278 while ((opt = getopt(argc, argv, "DCEVtSRhp:vu::")) != -1) {
5279 #else
5280 while ((opt = getopt(argc, argv, "ECVtSRhp:vu::")) != -1) {
5281 #endif
5282 switch (opt) {
5283 #ifdef DEBUG
5284 case 'D':
5285 unlink("libchess.debug");
5286 pgn_config_set(PGN_DEBUG, 1);
5287 break;
5288 #endif
5289 case 'C':
5290 pgn_config_set(PGN_STRICT_CASTLING, 1);
5291 break;
5292 case 't':
5293 write_custom_tags = 1;
5294 break;
5295 case 'E':
5296 i = 1;
5297 break;
5298 case 'R':
5299 pgn_config_set(PGN_REDUCED, 1);
5300 case 'S':
5301 validate_and_write = 1;
5302 case 'V':
5303 validate_only = 1;
5304 break;
5305 case 'v':
5306 printf("%s (%s)\n%s\n", PACKAGE_STRING, curses_version(),
5307 COPYRIGHT);
5308 exit(EXIT_SUCCESS);
5309 case 'p':
5310 filetype = FILE_PGN;
5311 strncpy(loadfile, optarg, sizeof(loadfile));
5312 loadfile[sizeof(loadfile)-1] = 0;
5313 break;
5314 case 'u':
5315 utf8_pieces = optarg ? atoi (optarg): 1;
5316 break;
5317 case 'h':
5318 default:
5319 usage(argv[0], EXIT_SUCCESS);
5323 if ((validate_only || validate_and_write) && !*loadfile)
5324 usage(argv[0], EXIT_FAILURE);
5326 if (access(config.configfile, R_OK) == 0)
5327 parse_rcfile(config.configfile);
5329 if (i)
5330 pgn_config_set(PGN_STOP_ON_ERROR, 1);
5332 signal(SIGPIPE, catch_signal);
5333 signal(SIGCONT, catch_signal);
5334 signal(SIGSTOP, catch_signal);
5335 signal(SIGINT, catch_signal);
5336 signal(SIGALRM, catch_signal);
5337 signal(SIGTERM, catch_signal);
5339 srandom(getpid());
5341 switch (filetype) {
5342 case FILE_PGN:
5343 if (pgn_open(loadfile, "r", &pgn) != E_PGN_OK)
5344 err(EXIT_FAILURE, "%s", loadfile);
5346 ret = pgn_parse(pgn);
5347 pgn_close(pgn);
5348 break;
5349 case FILE_FEN:
5350 //ret = parse_fen_file(loadfile);
5351 break;
5352 case FILE_EPD: // Not implemented.
5353 case FILE_NONE:
5354 default:
5355 // No file specified. Empty game.
5356 ret = pgn_parse(NULL);
5357 gp = game[gindex];
5358 add_custom_tags(&gp->tag);
5359 break;
5362 if (validate_only || validate_and_write) {
5363 if (validate_and_write) {
5364 if (pgn_open("-", "r", &pgn) != E_PGN_OK)
5365 err(EXIT_FAILURE, "pgn_open()");
5367 for (i = 0; i < gtotal; i++) {
5368 if (write_custom_tags)
5369 add_custom_tags(&game[i]->tag);
5371 pgn_write(pgn, game[i]);
5374 pgn_close(pgn);
5376 fm_loaded_file = TRUE;
5379 cleanup_all();
5380 exit(ret);
5382 else if (ret == E_PGN_ERR)
5383 exit(ret);
5385 if (utf8_pieces != -1)
5386 config.utf8_pieces = utf8_pieces;
5388 init_wchar_pieces ();
5389 yes_wchar = str_to_wchar (_("y"));
5390 all_wchar = str_to_wchar (_("a"));
5391 overwrite_wchar = str_to_wchar (_("o"));
5392 resume_wchar = str_to_wchar (_("r"));
5393 current_wchar = str_to_wchar (_("c"));
5394 append_wchar = str_to_wchar (_("a"));
5395 translatable_tag_names[0] = _("Event");
5396 translatable_tag_names[1] = _("Site");
5397 translatable_tag_names[2] = _("Date");
5398 translatable_tag_names[3] = _("Round");
5399 translatable_tag_names[4] = _("White");
5400 translatable_tag_names[5] = _("Black");
5401 translatable_tag_names[6] = _("Result");
5402 init_userdata();
5405 * This fixes window resizing in an xterm.
5407 if (getenv("DISPLAY") != NULL) {
5408 putenv("LINES=");
5409 putenv("COLUMNS=");
5412 if (initscr() == NULL)
5413 errx(EXIT_FAILURE, "%s", _("Could not initialize curses."));
5414 else
5415 curses_initialized = 1;
5417 if (LINES < 23 || COLS < 74) {
5418 endwin();
5419 errx(EXIT_FAILURE, _("Need at least an 74x23 terminal."));
5422 COLS_OLD = COLS;
5423 LINES_OLD = LINES;
5425 if (has_colors() == TRUE && start_color() == OK)
5426 init_color_pairs();
5428 boardw = newwin(BOARD_HEIGHT, BOARD_WIDTH, 0, COLS - BOARD_WIDTH);
5429 boardp = new_panel(boardw);
5430 historyw = newwin(HISTORY_HEIGHT, HISTORY_WIDTH, LINES - HISTORY_HEIGHT,
5431 COLS - HISTORY_WIDTH);
5432 historyp = new_panel(historyw);
5433 statusw = newwin(STATUS_HEIGHT, STATUS_WIDTH, 0, 0);
5434 statusp = new_panel(statusw);
5435 tagw = newwin(TAG_HEIGHT, TAG_WIDTH, STATUS_HEIGHT + 1, 0);
5436 tagp = new_panel(tagw);
5437 keypad(boardw, TRUE);
5438 // leaveok(boardw, TRUE);
5439 leaveok(tagw, TRUE);
5440 leaveok(statusw, TRUE);
5441 leaveok(historyw, TRUE);
5442 curs_set(0);
5443 cbreak();
5444 noecho();
5445 draw_window_decor();
5446 game_loop();
5447 cleanup_all();
5448 free (w_pawn_wchar);
5449 free (w_rook_wchar);
5450 free (w_bishop_wchar);
5451 free (w_knight_wchar);
5452 free (w_queen_wchar);
5453 free (w_king_wchar);
5454 free (b_pawn_wchar);
5455 free (b_rook_wchar);
5456 free (b_bishop_wchar);
5457 free (b_knight_wchar);
5458 free (b_queen_wchar);
5459 free (b_king_wchar);
5460 free (empty_wchar);
5461 free (enpassant_wchar);
5462 free (yes_wchar);
5463 free (all_wchar);
5464 free (overwrite_wchar);
5465 free (resume_wchar);
5466 free (current_wchar);
5467 free (append_wchar);
5468 free (status.notify);
5469 exit(EXIT_SUCCESS);