Don't update the board to move history after delete if in edit mode.
[cboard.git] / src / cboard.c
blob04f3862bdd88c45a0fd6639d6a8e0fd4b36ecf9c
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2013 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <pwd.h>
33 #include <signal.h>
34 #include <time.h>
35 #include <err.h>
36 #include <locale.h>
38 #ifdef HAVE_STDARG_H
39 #include <stdarg.h>
40 #endif
42 #ifdef HAVE_SYS_WAIT_H
43 #include <sys/wait.h>
44 #endif
46 #ifdef HAVE_REGEX_H
47 #include <regex.h>
48 #endif
50 #ifdef WITH_LIBPERL
51 #include "perl-plugin.h"
52 #endif
54 #include "common.h"
55 #include "conf.h"
56 #include "window.h"
57 #include "message.h"
58 #include "colors.h"
59 #include "input.h"
60 #include "misc.h"
61 #include "engine.h"
62 #include "strings.h"
63 #include "menu.h"
64 #include "keys.h"
65 #include "rcfile.h"
66 #include "filebrowser.h"
68 #ifdef DEBUG
69 #include <debug.h>
70 #endif
72 #define COPYRIGHT "Copyright (C) 2002-2013 " PACKAGE_BUGREPORT
73 #define LINE_GRAPHIC(c) ((!config.linegraphics) ? ' ' : c)
74 #define ROWTOMATRIX(r) ((8 - r) * 2 + 2 - 1)
75 #define COLTOMATRIX(c) ((c == 1) ? 1 : c * 4 - 3)
76 #define STATUS_HEIGHT 12
77 #define MEGA_BOARD (LINES >= 50 && COLS >= 144)
78 #define BOARD_HEIGHT_MB 50
79 #define BOARD_WIDTH_MB 98
80 #define STATUS_WIDTH_MB (COLS - BOARD_WIDTH_MB)
81 #define TAG_HEIGHT_MB 31
82 #define TAG_WIDTH_MB (COLS - BOARD_WIDTH_MB)
83 #define HISTORY_HEIGHT_MB (LINES - (STATUS_HEIGHT + TAG_HEIGHT_MB + 1))
84 #define HISTORY_WIDTH_MB (COLS - BOARD_WIDTH_MB)
85 #define BIG_BOARD (LINES >= 40 && COLS >= 112)
86 #define BOARD_HEIGHT ((MEGA_BOARD) ? BOARD_HEIGHT_MB : (BIG_BOARD) ? 34 : 18)
87 #define BOARD_WIDTH ((MEGA_BOARD) ? BOARD_WIDTH_MB : (BIG_BOARD) ? 66 : 34)
88 #define STATUS_WIDTH ((MEGA_BOARD) ? STATUS_WIDTH_MB : COLS - BOARD_WIDTH)
89 #define TAG_HEIGHT ((MEGA_BOARD) ? TAG_HEIGHT_MB : LINES - STATUS_HEIGHT - 1)
90 #define TAG_WIDTH ((MEGA_BOARD) ? TAG_WIDTH_MB : COLS - BOARD_WIDTH)
91 #define HISTORY_HEIGHT ((MEGA_BOARD) ? HISTORY_HEIGHT_MB : LINES - BOARD_HEIGHT)
92 #define HISTORY_WIDTH ((MEGA_BOARD) ? HISTORY_WIDTH_MB : COLS - STATUS_WIDTH)
93 #define MAX_VALUE_WIDTH (COLS - 8)
95 enum {
96 UP, DOWN, LEFT, RIGHT
99 static WINDOW *boardw;
100 static PANEL *boardp;
101 static WINDOW *tagw;
102 static PANEL *tagp;
103 static WINDOW *statusw;
104 static PANEL *statusp;
105 static WINDOW *historyw;
106 static PANEL *historyp;
107 static WINDOW *loadingw;
108 static PANEL *loadingp;
109 static WINDOW *enginew;
110 static PANEL *enginep;
112 static char gameexp[255];
113 static char moveexp[255];
114 static struct itimerval clock_timer;
115 static int delete_count = 0;
116 static int markstart = -1, markend = -1;
117 static int keycount;
118 static char loadfile[FILENAME_MAX];
119 static int quit;
120 static wint_t input_c;
122 // Loaded filename from the command line or from the file input dialog.
123 static int filetype;
124 enum {
125 FILE_NONE, FILE_PGN, FILE_FEN, FILE_EPD
128 static char **nags;
129 static int nag_total;
130 static int macro_match;
132 // Primer movimiento de juego cargado
133 // First move loaded game
134 static char fm_loaded_file = FALSE;
135 // Movimiento resultado de la función 'do_play_go'
136 // Movement function result 'do_play_go'
137 static int go_move = 0;
138 // Controla rotación de tablero
139 // Rotation control board
140 static int rotate = FALSE;
142 static int COLS_OLD, LINES_OLD;
144 // Status window.
145 static struct {
146 wchar_t *notify; // The status window notification line buffer.
147 } status;
149 static int curses_initialized;
151 // When in history mode a full step is to the next move of the same playing
152 // side. Half stepping is alternating sides.
153 static int movestep;
155 static wchar_t *w_pawn_wchar;
156 static wchar_t *w_rook_wchar;
157 static wchar_t *w_bishop_wchar;
158 static wchar_t *w_knight_wchar;
159 static wchar_t *w_queen_wchar;
160 static wchar_t *w_king_wchar;
161 static wchar_t *b_pawn_wchar;
162 static wchar_t *b_rook_wchar;
163 static wchar_t *b_bishop_wchar;
164 static wchar_t *b_knight_wchar;
165 static wchar_t *b_queen_wchar;
166 static wchar_t *b_king_wchar;
167 static wchar_t *empty_wchar;
168 static wchar_t *enpassant_wchar;
170 static wchar_t *yes_wchar;
171 static wchar_t *all_wchar; // do_save_game_overwrite_confirm()
172 static wchar_t *overwrite_wchar; // do_save_game_overwrite_confirm()
173 static wchar_t *resume_wchar; // do_history_mode_confirm()
174 static wchar_t *current_wchar; // do_game_save_multi_confirm()
175 static wchar_t *append_wchar; // save_pgn()
177 static const char piece_chars[] = "PpRrNnBbQqKkxx";
178 static char *translatable_tag_names[7];
179 static const char *f_pieces[] = {
180 " ", // 0
181 " O ",
182 " /_\\ ",
183 " |-|-| ", // 3
184 " ] [ ",
185 " /___\\ ",
186 " /?M ", // 6
187 " (@/)) ",
188 " /__))",
189 " O ", // 9
190 " (+) ",
191 " /_\\ ",
192 "•°°°°°•",// 12
193 " \\\\|// ",
194 " |___| ",
195 " __+__ ", // 15
196 "(__|__)",
197 " |___| ",
198 " \\ / ", // 18
199 " X ",
200 " / \\ "
203 static const bool cb[8][8] = {
204 {1,0,1,0,1,0,1,0},
205 {0,1,0,1,0,1,0,1},
206 {1,0,1,0,1,0,1,0},
207 {0,1,0,1,0,1,0,1},
208 {1,0,1,0,1,0,1,0},
209 {0,1,0,1,0,1,0,1},
210 {1,0,1,0,1,0,1,0},
211 {0,1,0,1,0,1,0,1}
214 static void free_userdata_once(GAME g);
216 // Posición por rotación de tablero.
217 // Rotation board position.
218 static void rotate_position(int p)
220 struct userdata_s *d = gp->data;
221 char fr, fc;
222 char fr2 = 8, fc2 = 8;
224 for (fr = 1; fr < 9; fr++) {
225 if (fr2 < 1)
226 fr2 = 8;
227 for (fc = 1; fc < 9; fc++){
228 if (fc2 < 1)
229 fc2 = 8;
230 if (p == CURSOR_POSITION &&
231 d->c_row == fr && d->c_col == fc) {
232 d->c_row = fr2;
233 d->c_col = fc2;
234 return;
236 else if (p == SP_POSITION &&
237 d->sp.row == fr && d->sp.col == fc) {
238 d->sp.row = fr2;
239 d->sp.col = fc2;
240 return;
242 else if (p == SPS_POSITION &&
243 d->sp.srow == fr && d->sp.scol == fc) {
244 d->sp.srow = fr2;
245 d->sp.scol = fc2;
246 return;
248 fc2--;
250 fr2--;
254 void update_cursor(GAME g, int idx)
256 char *p;
257 int len;
258 int t = pgn_history_total(g->hp);
259 struct userdata_s *d = g->data;
262 * If not deincremented then r and c would be the next move.
264 idx--;
266 if (idx > t || idx < 0 || !t || !g->hp[idx]->move) {
267 d->c_row = 2, d->c_col = 5;
268 goto historyrotate;
269 // return;
272 p = g->hp[idx]->move;
273 len = strlen(p);
275 if (*p == 'O') {
276 if (len <= 4)
277 d->c_col = rotate ? 3 : 7;
278 else
279 d->c_col = rotate ? 6 : 3;
281 if (rotate)
282 d->c_row = (g->turn == WHITE) ? 1 : 8;
283 else
284 d->c_row = (g->turn == WHITE) ? 8 : 1;
286 return;
289 p += len;
291 while (!isdigit(*p))
292 p--;
294 d->c_row = RANKTOINT(*p--);
295 d->c_col = FILETOINT(*p);
297 historyrotate:
298 if (d->mode == MODE_HISTORY && rotate)
299 rotate_position(CURSOR_POSITION);
302 static int init_nag()
304 FILE *fp;
305 char line[LINE_MAX];
306 int i = 0;
308 if ((fp = fopen(config.nagfile, "r")) == NULL) {
309 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.nagfile, strerror(errno));
310 return 1;
313 nags = Realloc(nags, (i+2) * sizeof(char *));
314 nags[i++] = strdup(_("none"));
315 nags[i] = NULL;
317 while (!feof(fp)) {
318 if (fscanf(fp, " %[^\n] ", line) == 1) {
319 nags = Realloc(nags, (i + 2) * sizeof(char *));
320 nags[i++] = strdup(line);
324 nags[i] = NULL;
325 nag_total = i;
326 return 0;
329 void edit_nag_toggle_item(struct menu_input_s *m)
331 struct input_s *in = m->data;
332 struct input_data_s *id = in->data;
333 HISTORY *h = id->data;
334 int i;
336 if (m->selected == 0) {
337 for (i = 0; i < MAX_PGN_NAG; i++)
338 h->nag[i] = 0;
340 for (i = 0; m->items[i]; i++)
341 m->items[i]->selected = 0;
343 return;
346 for (i = 0; i < MAX_PGN_NAG; i++) {
347 if (h->nag[i] == m->selected)
348 h->nag[i] = m->selected = 0;
349 else {
350 if (!h->nag[i]) {
351 h->nag[i] = m->selected;
352 break;
358 void edit_nag_save(struct menu_input_s *m)
360 pushkey = -1;
363 void edit_nag_help(struct menu_input_s *m)
365 message(_("NAG Menu Keys"), ANY_KEY_STR, "%s",
367 " UP/DOWN - previous/next menu item\n"
368 " HOME/END - first/last menu item\n"
369 " PGDN/PGUP - next/previous page\n"
370 " a-zA-Z0-9 - jump to item\n"
371 " SPACE - toggle selected item\n"
372 " CTRL-X - quit with changes"
376 struct menu_item_s **get_nag_items(WIN *win)
378 int i, n;
379 struct menu_input_s *m = win->data;
380 struct input_s *in = m->data;
381 struct input_data_s *id = in->data;
382 struct menu_item_s **items = m->items;
383 HISTORY *h = id->data;
385 if (items) {
386 for (i = 0; items[i]; i++)
387 free(items[i]);
390 for (i = 0; nags[i]; i++) {
391 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
392 items[i] = Malloc(sizeof(struct menu_item_s));
393 items[i]->name = nags[i];
394 items[i]->value = NULL;
396 for (n = 0; n < MAX_PGN_NAG; n++) {
397 if (h->nag[n] == i) {
398 items[i]->selected = 1;
399 n = -1;
400 break;
404 if (n >= 0)
405 items[i]->selected = 0;
408 items[i] = NULL;
409 m->nofree = 1;
410 m->items = items;
411 return items;
414 void nag_print(WIN *win)
416 struct menu_input_s *m = win->data;
418 mvwprintw(win->w, m->print_line, 1, "%-*s", win->cols - 2, m->item->name);
421 void edit_nag(void *arg)
423 struct menu_key_s **keys = NULL;
425 if (!nags) {
426 if (init_nag())
427 return;
430 add_menu_key(&keys, ' ', edit_nag_toggle_item);
431 add_menu_key(&keys, CTRL_KEY('x'), edit_nag_save);
432 add_menu_key(&keys, KEY_F(1), edit_nag_help);
433 construct_menu(0, 0, -1, -1, _("Numeric Annotation Glyphs"), 1, get_nag_items, keys, arg,
434 nag_print, NULL);
435 return;
438 static void *view_nag(void *arg)
440 HISTORY *h = (HISTORY *)arg;
441 char buf[80];
442 char line[LINE_MAX] = {0};
443 int i = 0;
445 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Viewing NAG for"), h->move);
447 if (!nags) {
448 if (init_nag())
449 return NULL;
452 for (i = 0; i < MAX_PGN_NAG; i++) {
453 char buf2[16];
455 if (!h->nag[i])
456 break;
458 if (h->nag[i] >= nag_total)
459 strncat(line, itoa(h->nag[i], buf2), sizeof(line)-1);
460 else
461 strncat(line, nags[h->nag[i]], sizeof(line)-1);
463 strncat(line, "\n", sizeof(line)-1);
466 line[strlen(line) - 1] = 0;
467 message(buf, ANY_KEY_STR, "%s", line);
468 return NULL;
471 void view_annotation(HISTORY *h)
473 char buf[MAX_SAN_MOVE_LEN + strlen(_("Viewing Annotation for")) + 4];
474 int nag = 0, comment = 0;
476 if (!h)
477 return;
479 if (h->comment && h->comment[0])
480 comment++;
482 if (h->nag[0])
483 nag++;
485 if (!nag && !comment)
486 return;
488 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Viewing Annotation for"), h->move);
490 if (comment)
491 construct_message(buf, (nag) ? _("Any other key to continue") : ANY_KEY_STR, 0, 1,
492 (nag) ? _("Press 'n' to view NAG") : NULL,
493 (nag) ? view_nag : NULL, (nag) ? h : NULL, NULL,
494 (nag) ? 'n' : 0, 0, "%s", h->comment);
495 else
496 construct_message(buf, _("Any other key to continue"), 0, 1, _("Press 'n' to view NAG"), view_nag, h, NULL,
497 'n', 0, "%s", _("No comment text for this move"));
500 int do_game_write(char *filename, char *mode, int start, int end)
502 int i;
503 struct userdata_s *d;
504 PGN_FILE *pgn;
506 i = pgn_open(filename, mode, &pgn);
508 if (i == E_PGN_ERR) {
509 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", filename, strerror(errno));
510 return 1;
512 else if (i == E_PGN_INVALID) {
513 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", filename, _("Not a regular file"));
514 return 1;
517 for (i = (start == -1) ? 0 : start; i < end; i++) {
518 d = game[i]->data;
519 pgn_write(pgn, game[i]);
520 CLEAR_FLAG(d->flags, CF_MODIFIED);
523 if (pgn_close(pgn) != E_PGN_OK)
524 message(ERROR_STR, ANY_KEY_STR, "%s", strerror(errno));
526 if (start == -1) {
527 strncpy(loadfile, filename, sizeof(loadfile));
528 loadfile[sizeof(loadfile)-1] = 0;
531 return 0;
534 struct save_game_s {
535 char *filename;
536 char *mode;
537 int start;
538 int end;
541 void do_save_game_overwrite_confirm(WIN *win)
543 char *mode = "w";
544 struct save_game_s *s = win->data;
545 wchar_t str[] = { win->c, 0 };
547 if (!wcscmp (str, append_wchar))
548 mode = "a";
549 else if (!wcscmp (str, overwrite_wchar))
550 mode = "w";
551 else
552 goto done;
554 if (do_game_write(s->filename, mode, s->start, s->end))
555 update_status_notify(gp, "%s", _("Save game failed."));
556 else
557 update_status_notify(gp, "%s", _("Game saved."));
559 done:
560 free(s->filename);
561 free(s);
564 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
565 * game index number.
567 void save_pgn(char *filename, int saveindex)
569 char buf[FILENAME_MAX];
570 struct stat st;
571 int end = (saveindex == -1) ? gtotal : saveindex + 1;
572 struct save_game_s *s;
574 if (filename[0] != '/' && config.savedirectory) {
575 if (stat(config.savedirectory, &st) == -1) {
576 if (errno == ENOENT) {
577 if (mkdir(config.savedirectory, 0755) == -1) {
578 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
579 strerror(errno));
580 return;
583 else {
584 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
585 strerror(errno));
586 return;
590 stat(config.savedirectory, &st);
592 if (!S_ISDIR(st.st_mode)) {
593 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory, _("Not a directory."));
594 return;
597 snprintf(buf, sizeof(buf), "%s/%s", config.savedirectory, filename);
598 filename = buf;
601 if (access(filename, W_OK) == 0) {
602 s = Malloc(sizeof(struct save_game_s));
603 s->filename = strdup(filename);
604 s->start = saveindex;
605 s->end = end;
606 construct_message(NULL, _("What would you like to do?"), 0, 1, NULL,
607 NULL, s, do_save_game_overwrite_confirm, 0, 0,
608 "%s \"%s\"\nPress \"%ls\" to append to this file, \"%ls\" to overwrite or any other key to cancel.",
609 _("File exists:"), filename, append_wchar,
610 overwrite_wchar);
611 return;
614 if (do_game_write(filename, "a", saveindex, end))
615 update_status_notify(gp, "%s", _("Save game failed."));
616 else
617 update_status_notify(gp, "%s", _("Game saved."));
620 static int castling_state(GAME g, BOARD b, int row, int col, int piece, int mod)
622 if (pgn_piece_to_int(piece) == ROOK && col == 7
623 && row == 7 &&
624 (TEST_FLAG(g->flags, GF_WK_CASTLE) || mod) &&
625 pgn_piece_to_int(b[7][4].icon) == KING && isupper(piece)) {
626 if (mod)
627 TOGGLE_FLAG(g->flags, GF_WK_CASTLE);
628 return 1;
630 else if (pgn_piece_to_int(piece) == ROOK && col == 0
631 && row == 7 &&
632 (TEST_FLAG(g->flags, GF_WQ_CASTLE) || mod) &&
633 pgn_piece_to_int(b[7][4].icon) == KING && isupper(piece)) {
634 if (mod)
635 TOGGLE_FLAG(g->flags, GF_WQ_CASTLE);
636 return 1;
638 else if (pgn_piece_to_int(piece) == ROOK && col == 7
639 && row == 0 &&
640 (TEST_FLAG(g->flags, GF_BK_CASTLE) || mod) &&
641 pgn_piece_to_int(b[0][4].icon) == KING && islower(piece)) {
642 if (mod)
643 TOGGLE_FLAG(g->flags, GF_BK_CASTLE);
644 return 1;
646 else if (pgn_piece_to_int(piece) == ROOK && col == 0
647 && row == 0 &&
648 (TEST_FLAG(g->flags, GF_BQ_CASTLE) || mod) &&
649 pgn_piece_to_int(b[0][4].icon) == KING && islower(piece)) {
650 if (mod)
651 TOGGLE_FLAG(g->flags, GF_BQ_CASTLE);
652 return 1;
654 else if (pgn_piece_to_int(piece) == KING && col == 4
655 && row == 7 &&
656 (mod || (pgn_piece_to_int(b[7][7].icon) == ROOK &&
657 TEST_FLAG(g->flags, GF_WK_CASTLE))
659 (pgn_piece_to_int(b[7][0].icon) == ROOK &&
660 TEST_FLAG(g->flags, GF_WQ_CASTLE))) && isupper(piece)) {
661 if (mod) {
662 if (TEST_FLAG(g->flags, GF_WK_CASTLE) ||
663 TEST_FLAG(g->flags, GF_WQ_CASTLE))
664 CLEAR_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE);
665 else
666 SET_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE);
668 return 1;
670 else if (pgn_piece_to_int(piece) == KING && col == 4
671 && row == 0 &&
672 (mod || (pgn_piece_to_int(b[0][7].icon) == ROOK &&
673 TEST_FLAG(g->flags, GF_BK_CASTLE))
675 (pgn_piece_to_int(b[0][0].icon) == ROOK &&
676 TEST_FLAG(g->flags, GF_BQ_CASTLE))) && islower(piece)) {
677 if (mod) {
678 if (TEST_FLAG(g->flags, GF_BK_CASTLE) ||
679 TEST_FLAG(g->flags, GF_BQ_CASTLE))
680 CLEAR_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE);
681 else
682 SET_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE);
684 return 1;
687 return 0;
690 #define IS_ENPASSANT(c) (c == 'x') ? CP_BOARD_ENPASSANT : isupper(c) ? CP_BOARD_WHITE : CP_BOARD_BLACK
691 #define ATTRS(cp) (cp & (A_BOLD|A_STANDOUT|A_BLINK|A_DIM|A_UNDERLINE|A_INVIS|A_REVERSE))
693 static void
694 init_wchar_pieces ()
696 w_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♙" : "P");
697 w_rook_wchar = str_to_wchar (config.utf8_pieces ? "♖" : "R");
698 w_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♗" : "B");
699 w_knight_wchar = str_to_wchar (config.utf8_pieces ? "♘" : "N");
700 w_queen_wchar = str_to_wchar (config.utf8_pieces ? "♕" : "Q");
701 w_king_wchar = str_to_wchar (config.utf8_pieces ? "♔" : "K");
702 b_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♟" : "p");
703 b_rook_wchar = str_to_wchar (config.utf8_pieces ? "♜" : "r");
704 b_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♝" : "b");
705 b_knight_wchar = str_to_wchar (config.utf8_pieces ? "♞" : "n");
706 b_queen_wchar = str_to_wchar (config.utf8_pieces ? "♛" : "q");
707 b_king_wchar = str_to_wchar (config.utf8_pieces ? "♚" : "k");
708 empty_wchar = str_to_wchar (" ");
709 enpassant_wchar = str_to_wchar ("x");
712 static wchar_t *
713 piece_to_wchar (unsigned char p)
715 switch (p)
717 case 'P':
718 return w_pawn_wchar;
719 case 'p':
720 return b_pawn_wchar;
721 case 'R':
722 return w_rook_wchar;
723 case 'r':
724 return b_rook_wchar;
725 case 'B':
726 return w_bishop_wchar;
727 case 'b':
728 return b_bishop_wchar;
729 case 'N':
730 return w_knight_wchar;
731 case 'n':
732 return b_knight_wchar;
733 case 'Q':
734 return w_queen_wchar;
735 case 'q':
736 return b_queen_wchar;
737 case 'K':
738 return w_king_wchar;
739 case 'k':
740 return b_king_wchar;
741 case 'x':
742 return enpassant_wchar;
745 return empty_wchar;
748 static int piece_can_attack (GAME g, int rank, int file)
750 struct userdata_s *d = g->data;
751 char *m, *frfr = NULL;
752 pgn_error_t e;
753 int row, col, p, v, pi, cpi;
755 if (rotate) {
756 rotate_position(CURSOR_POSITION);
757 rotate_position(SPS_POSITION);
760 v = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].valid;
761 pi = pgn_piece_to_int (d->b[RANKTOBOARD (rank)][FILETOBOARD (file)].icon);
762 cpi = d->sp.icon
763 ? pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon)
764 : pgn_piece_to_int (d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon);
766 if (pi == OPEN_SQUARE || cpi == OPEN_SQUARE || !VALIDFILE (file)
767 || !VALIDRANK (rank)) {
768 if (rotate) {
769 rotate_position(CURSOR_POSITION);
770 rotate_position(SPS_POSITION);
773 return 0;
776 if (d->sp.icon) {
777 col = v ? d->c_col : d->sp.scol;
778 row = v ? d->c_row : d->sp.srow;
780 else {
781 col = d->c_col;
782 row = d->c_row;
785 m = malloc(MAX_SAN_MOVE_LEN+1);
786 m[0] = INTTOFILE (file);
787 m[1] = INTTORANK (rank);
788 m[2] = INTTOFILE (col);
789 m[3] = INTTORANK (row);
790 m[4] = 0;
792 if (d->sp.icon && v) {
793 BOARD b;
795 memcpy (b, d->b, sizeof(BOARD));
796 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
797 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
798 pgn_int_to_piece (WHITE, OPEN_SQUARE);
799 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
800 pgn_switch_turn(g);
801 e = pgn_validate_move (g, b, &m, &frfr);
802 pgn_switch_turn (g);
803 free (m);
804 free (frfr);
806 if (e != E_PGN_OK && pgn_piece_to_int (d->sp.icon) == PAWN) {
807 int n = (d->sp.srow == 7 && rank == 5) ? 6 :
808 (d->sp.srow == 2 && rank == 4) ? 3 : 0;
810 if (n && (file == d->c_col-1 || file == d->c_col+1)) {
811 unsigned short flags = g->flags;
813 memcpy (b, d->b, sizeof(BOARD));
814 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
815 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
816 pgn_int_to_piece (WHITE, OPEN_SQUARE);
817 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
818 b[RANKTOBOARD (n)][FILETOBOARD (d->sp.scol)].enpassant = 1;
819 m = malloc(MAX_SAN_MOVE_LEN+1);
820 m[0] = INTTOFILE (file);
821 m[1] = INTTORANK (rank);
822 m[2] = INTTOFILE (col);
823 m[3] = INTTORANK (n);
824 m[4] = 0;
825 pgn_switch_turn(g);
826 SET_FLAG (g->flags, GF_ENPASSANT);
827 e = pgn_validate_move (g, b, &m, &frfr);
828 g->flags = flags;
829 pgn_switch_turn (g);
830 free (m);
831 free (frfr);
835 if (rotate) {
836 rotate_position(CURSOR_POSITION);
837 rotate_position(SPS_POSITION);
840 return e == E_PGN_OK ? 1 : 0;
843 pgn_switch_turn(g);
844 e = pgn_validate_move (g, d->b, &m, &frfr);
845 pgn_switch_turn (g);
847 if (!strcmp (m, "O-O") || !strcmp (m, "O-O-O"))
848 e = E_PGN_INVALID;
850 if (e == E_PGN_OK) {
851 int sf = FILETOINT (frfr[0]), sr = RANKTOINT (frfr[1]);
852 int df = FILETOINT (frfr[2]);
854 pi = d->b[RANKTOBOARD (sr)][FILETOBOARD (sf)].icon;
855 pi = pgn_piece_to_int (pi);
856 if (pi == PAWN && sf == df)
857 e = E_PGN_INVALID;
860 free (m);
861 free (frfr);
863 if (rotate) {
864 rotate_position(CURSOR_POSITION);
865 rotate_position(SPS_POSITION);
868 return e == E_PGN_OK ? 1 : 0;
871 void print_piece(WINDOW *w, int l, int c, char p)
873 int i, y, ff = 0;
875 for (i = 0; i < 13; i += 2) {
876 if (p == piece_chars[i] || p == piece_chars[i + 1]) {
877 for (y = 0; y < 3; y++)
878 mvwprintw(w, l + y, c, "%s", f_pieces[i + ff + y]);
879 return;
882 ff++;
885 for (y = 0; y < 3; y++)
886 mvwprintw(w, l + y, c, f_pieces[0]);
889 int inv_int(int x)
891 int fx, fx2 = 8;
893 for (fx = 1; fx < 9; fx++) {
894 if (x == fx)
895 break;
896 fx2--;
899 return fx2;
902 int coordofmove(GAME g, char *move, int yorx)
904 int l, y, x, i;
906 if (!strcmp (move, "O-O")) {
907 if (yorx)
908 return g->turn == WHITE ? 8 : 1;
910 return 7;
912 else if (!strcmp (move, "O-O-O")) {
913 if (yorx)
914 return g->turn == WHITE ? 8 : 1;
916 return 3;
919 l = strlen(move);
920 if (strchr(move, '+') && strchr(move, '=')) {
921 y = 4;
922 x = 5;
924 else if (!strchr(move, '+') && strchr(move, '=')) {
925 y = 3;
926 x = 4;
928 else if (strchr(move, '+') && !strchr(move, '=')) {
929 y = 2;
930 x = 3;
932 else {
933 y = 1;
934 x = 2;
937 if (yorx)
938 return move[l - y] - 48;
939 else {
940 for (i = 0; i < 8; i++) {
941 if (move[l - x] == "abcdefgh"[i])
942 return i + 1;
946 return 0;
949 static int is_prev_move (struct userdata_s *d, int brow, int bcol,
950 int row, int col)
952 if ((!BIG_BOARD &&
953 row == ROWTOMATRIX(((rotate) ? inv_int(d->pm_row) : d->pm_row)) &&
954 col == COLTOMATRIX(((rotate) ? inv_int(d->pm_col) : d->pm_col)))
955 || (BIG_BOARD &&
956 brow + 1 == ((rotate) ? d->pm_row : inv_int(d->pm_row)) &&
957 bcol + 1 == ((rotate) ? inv_int(d->pm_col) : d->pm_col)))
958 return 1;
960 return 0;
963 void update_board_window(GAME g)
965 int row, col;
966 int bcol = 0, brow = 0;
967 int l = config.coordsyleft;
968 int maxy = BOARD_HEIGHT, maxx = BOARD_WIDTH;
969 int ncols = 0, offset = 1;
970 int rowr = (MEGA_BOARD) ? 6 : (BIG_BOARD) ? 4 : 2;
971 int colr = (MEGA_BOARD) ? 12 : (BIG_BOARD) ? 8 : 4;
972 unsigned coords_y = 8, cxgc = 0;
973 unsigned i, cpd = 0;
974 struct userdata_s *d = g->data;
975 HISTORY *h = NULL;
977 h = pgn_history_by_n(g->hp, g->hindex - 1);
978 d->pm_row = (h && h->move && d->mode == MODE_PLAY)
979 ? coordofmove(g, h->move, 1) : 0;
980 d->pm_col = (h && h->move && d->mode == MODE_PLAY)
981 ? coordofmove(g, h->move, 0) : 0;
983 if (d->mode != MODE_PLAY && d->mode != MODE_EDIT)
984 update_cursor(g, g->hindex);
986 if (BIG_BOARD) {
987 if (rotate) {
988 brow = 7;
989 coords_y = 1;
992 else {
993 if (rotate) {
994 brow = 1;
995 coords_y = 1;
997 else
998 brow = 8;
1001 for (row = 0; row < maxy; row++) {
1002 if (BIG_BOARD) {
1003 if (rotate)
1004 bcol = 7;
1005 else
1006 bcol = 0;
1008 else {
1009 if (rotate)
1010 bcol = 8;
1011 else
1012 bcol = 1;
1015 for (col = 0; col < maxx; col++) {
1016 int attrwhich = -1;
1017 chtype attrs = 0, old_attrs = 0;
1018 unsigned char p;
1019 int can_attack = 0;
1020 int valid = 0;
1022 if (row == 0 || row == maxy - 2) {
1023 if (col == 0)
1024 mvwaddch(boardw, row, col + l,
1025 LINE_GRAPHIC((row)
1026 ? ACS_LLCORNER | CP_BOARD_GRAPHICS
1027 : ACS_ULCORNER | CP_BOARD_GRAPHICS));
1028 else if (col == maxx - 2)
1029 mvwaddch(boardw, row, col + l,
1030 LINE_GRAPHIC((row)
1031 ? ACS_LRCORNER | CP_BOARD_GRAPHICS
1032 : ACS_URCORNER | CP_BOARD_GRAPHICS));
1033 else if (!(col % colr))
1034 mvwaddch(boardw, row, col + l,
1035 LINE_GRAPHIC((row)
1036 ? ACS_BTEE | CP_BOARD_GRAPHICS
1037 : ACS_TTEE | CP_BOARD_GRAPHICS));
1038 else {
1039 if (col != maxx - 1)
1040 mvwaddch(boardw, row, col + l,
1041 LINE_GRAPHIC(ACS_HLINE | CP_BOARD_GRAPHICS));
1044 continue;
1047 if ((row % 2) && col == maxx - 1 &&
1048 (coords_y > 0 && coords_y < 9)) {
1049 wattron(boardw, CP_BOARD_COORDS);
1050 mvwprintw(boardw,
1051 (BIG_BOARD) ? row * ((MEGA_BOARD) ? 3 : 2)
1052 : row, (l) ? 0 : col, "%d",
1053 (rotate) ? coords_y++ : coords_y--);
1054 wattroff(boardw, CP_BOARD_COORDS);
1055 continue;
1058 if ((col == 0 || col == maxx - 2) && row != maxy - 1) {
1059 if (!(row % rowr))
1060 mvwaddch(boardw, row, col + l,
1061 LINE_GRAPHIC((col) ?
1062 ACS_RTEE | CP_BOARD_GRAPHICS :
1063 ACS_LTEE | CP_BOARD_GRAPHICS));
1064 else
1065 mvwaddch(boardw, row, col + l,
1066 LINE_GRAPHIC(ACS_VLINE | CP_BOARD_GRAPHICS));
1068 continue;
1071 if ((row % rowr) && !(col % colr) && row != maxy - 1) {
1072 mvwaddch(boardw, row, col + l,
1073 LINE_GRAPHIC(ACS_VLINE | CP_BOARD_GRAPHICS));
1074 continue;
1077 if (!(col % colr) && row != maxy - 1) {
1078 mvwaddch(boardw, row, col + l,
1079 LINE_GRAPHIC(ACS_PLUS | CP_BOARD_GRAPHICS));
1080 continue;
1083 if ((row % rowr)) {
1084 if ((col % colr)) {
1085 if (BIG_BOARD)
1086 attrwhich = (cb[brow][bcol]) ? WHITE : BLACK;
1087 else {
1088 if (ncols++ == 8) {
1089 offset++;
1090 ncols = 1;
1093 if (((ncols % 2) && !(offset % 2))
1094 || (!(ncols % 2) && (offset % 2)))
1095 attrwhich = BLACK;
1096 else
1097 attrwhich = WHITE;
1100 if (BIG_BOARD && rotate) {
1101 brow = inv_int(brow + 1) - 1;
1102 bcol = inv_int(bcol + 1) - 1;
1105 if (BIG_BOARD)
1106 p = d->b[brow][bcol].icon;
1107 else
1108 p = d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].icon;
1110 int pi = pgn_piece_to_int(p);
1112 if (config.details &&
1113 ((!BIG_BOARD && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].enpassant)
1114 || (BIG_BOARD && d->b[brow][bcol].enpassant))) {
1115 p = pi = 'x';
1116 attrs = mix_cp(CP_BOARD_ENPASSANT,
1117 (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK,
1118 ATTRS(CP_BOARD_ENPASSANT), A_FG_B_BG);
1121 if (config.showattacks && config.details
1122 && piece_can_attack (g,
1123 BIG_BOARD ? inv_int (brow+1) : brow,
1124 BIG_BOARD ? bcol+1 : bcol)) {
1125 attrs = CP_BOARD_ATTACK;
1126 old_attrs = attrs;
1127 can_attack = 1;
1130 if (config.validmoves &&
1131 ((!BIG_BOARD && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].valid)
1132 || (BIG_BOARD && d->b[brow][bcol].valid))) {
1133 old_attrs = -1;
1134 valid = 1;
1136 if (attrwhich == WHITE)
1137 attrs = mix_cp(CP_BOARD_MOVES_WHITE,
1138 IS_ENPASSANT(p),
1139 ATTRS(CP_BOARD_MOVES_WHITE),
1140 B_FG_A_BG);
1141 else
1142 attrs = mix_cp(CP_BOARD_MOVES_BLACK,
1143 IS_ENPASSANT(p),
1144 ATTRS(CP_BOARD_MOVES_BLACK),
1145 B_FG_A_BG);
1147 else if (p != 'x' && !can_attack)
1148 attrs = (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK;
1150 if (BIG_BOARD && rotate) {
1151 brow = inv_int(brow + 1) - 1;
1152 bcol = inv_int(bcol + 1) - 1;
1155 if ((!BIG_BOARD && row == ROWTOMATRIX(d->c_row)
1156 && col == COLTOMATRIX(d->c_col))
1157 || (BIG_BOARD && brow + 1 == inv_int(d->c_row)
1158 && bcol + 1 == d->c_col)) {
1159 attrs = mix_cp(CP_BOARD_CURSOR, IS_ENPASSANT(p),
1160 ATTRS(CP_BOARD_CURSOR), B_FG_A_BG);
1161 old_attrs = -1;
1163 else if ((!BIG_BOARD && row == ROWTOMATRIX(d->sp.srow) &&
1164 col == COLTOMATRIX(d->sp.scol)) ||
1165 (BIG_BOARD && brow + 1 == inv_int(d->sp.srow) &&
1166 bcol + 1 == d->sp.scol)) {
1167 attrs = mix_cp(CP_BOARD_SELECTED, IS_ENPASSANT(p),
1168 ATTRS(CP_BOARD_SELECTED), B_FG_A_BG);
1169 old_attrs = -1;
1171 else if (is_prev_move (d, brow, bcol, row, col) && !valid)
1173 attrs = mix_cp(CP_BOARD_PREVMOVE, IS_ENPASSANT(p),
1174 ATTRS(CP_BOARD_PREVMOVE), B_FG_A_BG);
1175 old_attrs = -1;
1178 if (row == maxy - 1)
1179 attrs = 0;
1181 if (can_attack) {
1182 int n = is_prev_move (d, brow, bcol, row, col);
1183 chtype a = n && !valid
1184 ? CP_BOARD_PREVMOVE : attrwhich == WHITE ? valid ? CP_BOARD_MOVES_WHITE : CP_BOARD_WHITE : valid ? CP_BOARD_MOVES_BLACK : CP_BOARD_BLACK;
1185 attrs = mix_cp(CP_BOARD_ATTACK, a,
1186 ATTRS (CP_BOARD_ATTACK), A_FG_B_BG);
1187 old_attrs = -1;
1190 if (BIG_BOARD)
1191 wmove(boardw, row, col + ((MEGA_BOARD) ? 5 : 3) + l);
1192 else
1193 mvwaddch(boardw, row, col + l, ' ' | attrs);
1195 if (row == maxy - 1 && cxgc < 8) {
1196 waddch(boardw, "abcdefgh"[(BIG_BOARD) ? bcol : bcol - 1] | CP_BOARD_COORDS);
1197 cxgc++;
1199 else {
1200 if (old_attrs == -1) {
1201 old_attrs = attrs;
1202 goto printc;
1205 old_attrs = attrs;
1207 if (pi != OPEN_SQUARE && p != 'x' && !can_attack) {
1208 if (attrwhich == WHITE) {
1209 if (isupper(p))
1210 attrs = CP_BOARD_W_W;
1211 else
1212 attrs = CP_BOARD_W_B;
1214 else {
1215 if (isupper(p))
1216 attrs = CP_BOARD_B_W;
1217 else
1218 attrs = CP_BOARD_B_B;
1222 printc:
1223 if (BIG_BOARD) {
1224 if (config.details && !can_attack
1225 && castling_state(g, d->b,
1226 (rotate) ? inv_int(brow + 1) - 1: brow,
1227 (rotate) ? inv_int(bcol + 1) - 1: bcol, p, 0))
1228 attrs = mix_cp(CP_BOARD_CASTLING, attrs,
1229 ATTRS(CP_BOARD_CASTLING),
1230 A_FG_B_BG);
1232 else {
1233 if (config.details && !can_attack
1234 && castling_state(g, d->b, RANKTOBOARD (brow),
1235 FILETOBOARD (bcol), p, 0)) {
1236 attrs = mix_cp(CP_BOARD_CASTLING, attrs,
1237 ATTRS(CP_BOARD_CASTLING),
1238 A_FG_B_BG);
1242 if (BIG_BOARD) {
1243 // FIXME: Reimpresión de piezas(+4).
1244 if (cpd < 67) {
1245 wattron (boardw, attrs);
1246 if (MEGA_BOARD){
1247 for (i = 0; i < 5; i++)
1248 mvwprintw(boardw, i + brow * 6 + 1,
1249 bcol * 12 + 1 + l,
1250 " ");
1251 if (pi != OPEN_SQUARE)
1252 print_piece(boardw, brow * 6 + 2,
1253 bcol * 12 + 3 + l, p);
1255 else {
1256 print_piece(boardw, brow * 4 + 1,
1257 bcol * 8 + 1 + l,
1258 (pi != OPEN_SQUARE) ? p : 0);
1261 wattroff (boardw, attrs);
1262 cpd++;
1265 else {
1266 wattron (boardw, attrs);
1267 waddwstr (boardw, piece_to_wchar (pi != OPEN_SQUARE ? p : 0));
1268 wattroff (boardw, attrs);
1271 attrs = old_attrs;
1274 if (BIG_BOARD)
1275 col += (MEGA_BOARD) ? 10 : 6;
1276 else {
1277 waddch(boardw, ' ' | attrs);
1278 col += 2;
1281 if (rotate)
1282 bcol--;
1283 else
1284 bcol++;
1286 if (BIG_BOARD) {
1287 if (bcol > 7)
1288 bcol = 0;
1289 if (bcol < 0)
1290 bcol = 7;
1294 else {
1295 if (col != maxx - 1)
1296 mvwaddch(boardw, row, col + l,
1297 LINE_GRAPHIC(ACS_HLINE | CP_BOARD_GRAPHICS));
1301 if (row % rowr) {
1302 if (rotate)
1303 brow++;
1304 else
1305 brow--;
1308 if (BIG_BOARD) {
1309 if (brow > 7)
1310 brow = 0;
1311 if (brow < 0)
1312 brow = 7;
1317 void invalid_move(int n, int e, const char *m)
1319 if (curses_initialized)
1320 cmessage(ERROR_STR, ANY_KEY_STR, "%s \"%s\" (round #%i)", (e == E_PGN_AMBIGUOUS)
1321 ? _("Ambiguous move") : _("Invalid move"), m, n);
1322 else
1323 warnx("%s: %s \"%s\" (round #%i)", loadfile, (e == E_PGN_AMBIGUOUS)
1324 ? _("Ambiguous move") : _("Invalid move"), m, n);
1327 void gameover(GAME g)
1329 struct userdata_s *d = g->data;
1331 SET_FLAG(g->flags, GF_GAMEOVER);
1332 d->mode = MODE_HISTORY;
1333 stop_engine(g);
1336 static void update_clock(GAME g, struct itimerval it)
1338 struct userdata_s *d = g->data;
1340 if (TEST_FLAG(d->flags, CF_CLOCK) && g->turn == WHITE) {
1341 d->wclock.elapsed.tv_sec += it.it_value.tv_sec;
1342 d->wclock.elapsed.tv_usec += it.it_value.tv_usec;
1344 if (d->wclock.elapsed.tv_usec > 1000000 - 1) {
1345 d->wclock.elapsed.tv_sec += d->wclock.elapsed.tv_usec / 1000000;
1346 d->wclock.elapsed.tv_usec = d->wclock.elapsed.tv_usec % 1000000;
1349 if (d->wclock.tc[d->wclock.tcn][1] &&
1350 d->wclock.elapsed.tv_sec >= d->wclock.tc[d->wclock.tcn][1]) {
1351 pgn_tag_add(&g->tag, "Result", "0-1");
1352 gameover(g);
1355 else if (TEST_FLAG(d->flags, CF_CLOCK) && g->turn == BLACK) {
1356 d->bclock.elapsed.tv_sec += it.it_value.tv_sec;
1357 d->bclock.elapsed.tv_usec += it.it_value.tv_usec;
1359 if (d->bclock.elapsed.tv_usec > 1000000 - 1) {
1360 d->bclock.elapsed.tv_sec += d->bclock.elapsed.tv_usec / 1000000;
1361 d->bclock.elapsed.tv_usec = d->bclock.elapsed.tv_usec % 1000000;
1364 if (d->bclock.tc[d->bclock.tcn][1] &&
1365 d->bclock.elapsed.tv_sec >= d->bclock.tc[d->bclock.tcn][1]) {
1366 pgn_tag_add(&g->tag, "Result", "1-0");
1367 gameover(g);
1371 d->elapsed.tv_sec += it.it_value.tv_sec;
1372 d->elapsed.tv_usec += it.it_value.tv_usec;
1374 if (d->elapsed.tv_usec > 1000000 - 1) {
1375 d->elapsed.tv_sec += d->elapsed.tv_usec / 1000000;
1376 d->elapsed.tv_usec = d->elapsed.tv_usec % 1000000;
1380 static void update_time_control(GAME g)
1382 struct userdata_s *d = g->data;
1383 struct clock_s *clk = (g->turn == WHITE) ? &d->wclock : &d->bclock;
1385 if (clk->incr)
1386 clk->tc[clk->tcn][1] += clk->incr;
1388 if (!clk->tc[clk->tcn][1])
1389 return;
1391 clk->move++;
1393 if (!clk->tc[clk->tcn][0] || clk->move >= clk->tc[clk->tcn][0]) {
1394 clk->move = 0;
1395 clk->tc[clk->tcn + 1][1] += abs(clk->elapsed.tv_sec - clk->tc[clk->tcn][1]);
1396 memset(&clk->elapsed, 0, sizeof(clk->elapsed));
1397 clk->tcn++;
1401 void update_history_window(GAME g)
1403 char buf[HISTORY_WIDTH - 1];
1404 HISTORY *h = NULL;
1405 int n, total;
1406 int t = pgn_history_total(g->hp);
1408 n = (g->hindex + 1) / 2;
1410 if (t % 2)
1411 total = (t + 1) / 2;
1412 else
1413 total = t / 2;
1415 if (t)
1416 snprintf(buf, sizeof(buf), "%u %s %u%s", n, _("of"), total,
1417 (movestep == 1) ? _(" (ply)") : "");
1418 else
1419 strncpy(buf, _("not available"), sizeof(buf)-1);
1421 buf[sizeof(buf)-1] = 0;
1422 mvwprintw(historyw, 2, 1, "%*s %-*s", 10, _("Move:"),
1423 HISTORY_WIDTH - 14, buf);
1425 h = pgn_history_by_n(g->hp, g->hindex);
1426 snprintf(buf, sizeof(buf), "%s",
1427 (h && h->move) ? h->move
1428 : (LINES < 24) ? _("empty") : _("not available"));
1429 n = 0;
1431 if (h && ((h->comment) || h->nag[0])) {
1432 strncat(buf, _(" (Annotated"), sizeof(buf)-1);
1433 n++;
1436 if (h && h->rav) {
1437 strncat(buf, (n) ? ",+" : " (+", sizeof(buf)-1);
1438 n++;
1441 if (g->ravlevel) {
1442 strncat(buf, (n) ? ",-" : " (-", sizeof(buf)-1);
1443 n++;
1446 if (n)
1447 strncat(buf, ")", sizeof(buf)-1);
1449 mvwprintw(historyw, 3, ((LINES < 24) ? 17 : 1), "%s %-*s",
1450 (LINES < 24) ? _("Next:") :_("Next move:"),
1451 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1453 h = pgn_history_by_n(g->hp, g->hindex - 1);
1454 snprintf(buf, sizeof(buf), "%s",
1455 (h && h->move) ? h->move
1456 : (LINES < 24) ? _("empty") : _("not available"));
1457 n = 0;
1459 if (h && ((h->comment) || h->nag[0])) {
1460 strncat(buf, _(" (Annotated"), sizeof(buf)-1);
1461 n++;
1464 if (h && h->rav) {
1465 strncat(buf, (n) ? ",+" : " (+", sizeof(buf)-1);
1466 n++;
1469 if (g->ravlevel) {
1470 strncat(buf, (n) ? ",-" : " (-", sizeof(buf)-1);
1471 n++;
1474 if (n)
1475 strncat(buf, ")", sizeof(buf)-1);
1477 mvwprintw(historyw, ((LINES < 24) ? 3 : 4), 1, "%s %-*s",
1478 (LINES < 24) ? _("Prev.:") : _("Prev move:"),
1479 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1482 void do_validate_move(char *move)
1484 struct userdata_s *d = gp->data;
1485 int n;
1486 char *frfr = NULL;
1488 if (TEST_FLAG(d->flags, CF_HUMAN)) {
1489 if ((n = pgn_parse_move(gp, d->b, &move, &frfr)) != E_PGN_OK) {
1490 invalid_move(d->n + 1, n, move);
1491 return;
1494 update_time_control(gp);
1495 pgn_history_add(gp, d->b, move);
1496 pgn_switch_turn(gp);
1498 else {
1499 if ((n = pgn_validate_move(gp, d->b, &move, &frfr)) != E_PGN_OK) {
1500 invalid_move(d->n + 1, n, move);
1501 return;
1504 add_engine_command(gp, ENGINE_THINKING, "%s\n",
1505 (config.engine_protocol == 1) ? frfr : move);
1508 d->sp.srow = d->sp.scol = d->sp.icon = 0;
1510 if (config.validmoves)
1511 pgn_reset_valid_moves(d->b);
1513 if (TEST_FLAG(gp->flags, GF_GAMEOVER))
1514 d->mode = MODE_HISTORY;
1515 else
1516 SET_FLAG(d->flags, CF_MODIFIED);
1518 free (frfr);
1519 d->paused = 0;
1520 update_history_window(gp);
1521 update_board_window(gp);
1522 return;
1525 void do_promotion_piece_finalize(WIN *win)
1527 char *p, *str = win->data;
1529 if (pgn_piece_to_int(win->c) == -1)
1530 return;
1532 p = str + strlen(str);
1533 *p++ = toupper(win->c);
1534 *p = '\0';
1535 do_validate_move(str);
1536 free (win->data);
1537 win->data = NULL;
1540 static void move_to_engine(GAME g)
1542 struct userdata_s *d = g->data;
1543 char *str;
1544 int piece;
1546 if (config.validmoves &&
1547 !d->b[RANKTOBOARD(d->sp.row)][FILETOBOARD(d->sp.col)].valid)
1548 return;
1550 str = Malloc(MAX_SAN_MOVE_LEN + 1);
1551 snprintf(str, MAX_SAN_MOVE_LEN + 1, "%c%i%c%i",
1552 _("abcdefgh")[d->sp.scol - 1],
1553 d->sp.srow, _("abcdefgh")[d->sp.col - 1], d->sp.row);
1555 piece = pgn_piece_to_int(d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon);
1557 if (piece == PAWN && (d->sp.row == 8 || d->sp.row == 1)) {
1558 construct_message(_("Select Pawn Promotion Piece"), _ ("R/N/B/Q"), 1, 1, NULL, NULL,
1559 str, do_promotion_piece_finalize, 0, 0, "%s", _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1560 return;
1563 do_validate_move(str);
1564 free (str);
1567 static char *clock_to_char(long n)
1569 static char buf[16];
1570 int h = 0, m = 0, s = 0;
1572 h = n / 3600;
1573 m = (n % 3600) / 60;
1574 s = (n % 3600) % 60;
1575 snprintf(buf, sizeof(buf), "%.2i:%.2i:%.2i", h, m, s);
1576 return buf;
1579 static char *timeval_to_char(struct timeval t, long limit)
1581 static char buf[9];
1582 int h = 0, m = 0, s = 0;
1583 int n = limit ? abs(limit - t.tv_sec) : 0;
1585 h = n / 3600;
1586 m = (n % 3600) / 60;
1587 s = (n % 3600) % 60;
1588 snprintf(buf, sizeof(buf), "%.2i:%.2i:%.2i", h, m, s);
1589 return buf;
1592 static char *time_control_status(struct clock_s *clk)
1594 static char buf[80] = {0};
1596 buf[0] = 0;
1598 if (clk->tc[clk->tcn][0] && clk->tc[clk->tcn + 1][1])
1599 snprintf(buf, sizeof(buf), " M%.2i/%s", abs(clk->tc[clk->tcn][0] - clk->move),
1600 clock_to_char(clk->tc[clk->tcn + 1][1]));
1601 else if (!clk->incr)
1602 return "";
1604 if (clk->incr) {
1605 char buf[16];
1606 strncat(buf, " I", sizeof(buf)-1);
1607 strncat(buf, itoa(clk->incr, buf), sizeof(buf)-1);
1610 return buf;
1613 void update_status_window(GAME g)
1615 int i = 0;
1616 char *buf;
1617 char tmp[15] = {0}, *engine, *mode;
1618 char t[COLS];
1619 int w;
1620 char *p;
1621 int maxy, maxx;
1622 int len;
1623 struct userdata_s *d = g->data;
1624 int y;
1625 int n;
1627 if (!curses_initialized)
1628 return;
1630 getmaxyx(statusw, maxy, maxx);
1631 (void)maxy;
1632 w = maxx - 2 - 8;
1633 len = maxx - 2;
1634 buf = Malloc(len);
1635 y = 2;
1637 wchar_t *loadfilew = loadfile[0] ? str_etc (loadfile, w, 1) : str_to_wchar (_ ("not available"));
1638 mvwprintw(statusw, y++, 1, "%*s %-*ls", 7, _("File:"), w, loadfilew);
1639 free (loadfilew);
1640 snprintf(buf, len, "%i %s %i", gindex + 1, _("of"), gtotal);
1641 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Game:"), w, buf);
1643 *tmp = '\0';
1644 p = tmp;
1646 if (config.details) {
1647 *p++ = 'D';
1648 i++;
1651 if (TEST_FLAG(d->flags, CF_DELETE)) {
1652 if (i)
1653 *p++ = '/';
1655 *p++ = 'X';
1656 i++;
1659 if (TEST_FLAG(g->flags, GF_PERROR)) {
1660 if (i)
1661 *p++ = '/';
1663 *p++ = '!';
1664 i++;
1667 if (TEST_FLAG(d->flags, CF_MODIFIED)) {
1668 if (i)
1669 *p++ = '/';
1671 *p++ = '*';
1672 i++;
1675 pgn_config_get(PGN_STRICT_CASTLING, &n);
1677 if (n == 1) {
1678 if (i)
1679 *p++ = '/';
1681 *p++ = 'C';
1682 i++;
1684 #ifdef WITH_LIBPERL
1685 if (TEST_FLAG(d->flags, CF_PERL)) {
1686 if (i)
1687 *p++ = '/';
1689 *p++ = 'P';
1690 i++;
1692 #endif
1694 *p = '\0';
1695 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Flags:"), w, (tmp[0]) ? tmp : "-");
1697 switch (d->mode) {
1698 case MODE_HISTORY:
1699 mode = _("move history");
1700 break;
1701 case MODE_EDIT:
1702 mode = _("edit");
1703 break;
1704 case MODE_PLAY:
1705 mode = _("play");
1706 break;
1707 default:
1708 mode = _("(empty value)");
1709 break;
1712 snprintf(buf, len - 1, "%*s %s", 7, _("Mode:"), mode);
1714 if (d->mode == MODE_PLAY) {
1715 if (TEST_FLAG(d->flags, CF_HUMAN))
1716 strncat(buf, _(" (human/human)"), len - 1);
1717 else if (TEST_FLAG(d->flags, CF_ENGINE_LOOP))
1718 strncat(buf, _(" (engine/engine)"), len - 1);
1719 else
1720 strncat(buf, (d->play_mode == PLAY_EH) ?
1721 _(" (engine/human)") : _(" (human/engine)"), len - 1);
1724 buf[len-1] = 0;
1725 mvwprintw(statusw, y++, 1, "%-*s", len, buf);
1726 free(buf);
1728 if (d->engine) {
1729 switch (d->engine->status) {
1730 case ENGINE_THINKING:
1731 engine = _("pondering...");
1732 break;
1733 case ENGINE_READY:
1734 engine = _("ready");
1735 break;
1736 case ENGINE_INITIALIZING:
1737 engine = _("initializing...");
1738 break;
1739 case ENGINE_OFFLINE:
1740 engine = _("offline");
1741 break;
1742 default:
1743 engine = _("(empty value)");
1744 break;
1747 else
1748 engine = _("offline");
1750 mvwprintw(statusw, y, 1, "%*s %-*s", 7, _("Engine:"), w, " ");
1751 wattron(statusw, CP_STATUS_ENGINE);
1752 mvwaddstr(statusw, y++, 9, engine);
1753 wattroff(statusw, CP_STATUS_ENGINE);
1755 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Turn:"), w,
1756 (g->turn == WHITE) ? _("white") : _("black"));
1758 strncpy(tmp, _("white"), sizeof(tmp)-1);
1759 tmp[0] = toupper(tmp[0]);
1760 snprintf(t, sizeof(t), "%s%s",
1761 timeval_to_char(d->wclock.elapsed, d->wclock.tc[d->wclock.tcn][1]),
1762 time_control_status(&d->wclock));
1763 mvwprintw(statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1765 strncpy(tmp, _("black"), sizeof(tmp)-1);
1766 tmp[0] = toupper(tmp[0]);
1767 snprintf(t, sizeof(t), "%s%s",
1768 timeval_to_char(d->bclock.elapsed, d->bclock.tc[d->bclock.tcn][1]),
1769 time_control_status(&d->bclock));
1770 mvwprintw(statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1772 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Total:"), w,
1773 clock_to_char(d->elapsed.tv_sec));
1775 // for (i = 0; i < STATUS_WIDTH; i++)
1776 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1778 if (!status.notify)
1779 status.notify = str_to_wchar(_("Type F1 for help"));
1781 wattron(stdscr, CP_STATUS_NOTIFY);
1782 for (i = (config.boardleft) ? BOARD_WIDTH : 0;
1783 i < ((config.boardleft) ? COLS : STATUS_WIDTH); i++)
1784 mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1785 mvwprintw(stdscr, STATUS_HEIGHT, CENTERX(STATUS_WIDTH, status.notify)
1786 + ((config.boardleft) ? BOARD_WIDTH : 0),
1787 "%ls", status.notify);
1788 wattroff(stdscr, CP_STATUS_NOTIFY);
1791 wchar_t *translate_tag_name(const char *tag)
1793 if (!strcmp (tag, "Event"))
1794 return str_to_wchar (translatable_tag_names[0]);
1795 else if (!strcmp (tag, "Site"))
1796 return str_to_wchar (translatable_tag_names[1]);
1797 else if (!strcmp (tag, "Date"))
1798 return str_to_wchar (translatable_tag_names[2]);
1799 else if (!strcmp (tag, "Round"))
1800 return str_to_wchar (translatable_tag_names[3]);
1801 else if (!strcmp (tag, "White"))
1802 return str_to_wchar (translatable_tag_names[4]);
1803 else if (!strcmp (tag, "Black"))
1804 return str_to_wchar (translatable_tag_names[5]);
1805 else if (!strcmp (tag, "Result"))
1806 return str_to_wchar (translatable_tag_names[6]);
1808 return str_to_wchar (tag);
1811 void update_tag_window(TAG **t)
1813 int i, l, w;
1814 int namel = 0;
1816 for (i = 0; t[i]; i++) {
1817 wchar_t *namewc = translate_tag_name(t[i]->name);
1819 l = wcslen(namewc);
1820 free (namewc);
1821 if (l > namel)
1822 namel = l;
1825 w = TAG_WIDTH - namel - 4;
1827 for (i = 0; t[i] && i < TAG_HEIGHT - 3; i++) {
1828 wchar_t *namewc = translate_tag_name(t[i]->name);
1829 wchar_t *valuewc = str_etc(t[i]->value, w, 0);
1831 mvwprintw(tagw, (i + 2), 1, "%*ls: %-*ls", namel, namewc, w, valuewc);
1832 free (namewc);
1833 free (valuewc);
1836 for (; i < TAG_HEIGHT - 3; i++)
1837 mvwprintw(tagw, (i + 2), 1, "%*s", namel + w + 2, " ");
1840 void append_enginebuf(GAME g, char *line)
1842 int i = 0;
1843 struct userdata_s *d = g->data;
1845 if (d->engine->enginebuf)
1846 for (i = 0; d->engine->enginebuf[i]; i++);
1848 if (i >= LINES - 3) {
1849 free(d->engine->enginebuf[0]);
1851 for (i = 0; d->engine->enginebuf[i+1]; i++)
1852 d->engine->enginebuf[i] = d->engine->enginebuf[i+1];
1854 d->engine->enginebuf[i] = strdup(line);
1856 else {
1857 d->engine->enginebuf = Realloc(d->engine->enginebuf, (i + 2) * sizeof(char *));
1858 d->engine->enginebuf[i++] = strdup(line);
1859 d->engine->enginebuf[i] = NULL;
1863 void update_engine_window(GAME g)
1865 int i;
1866 struct userdata_s *d = g->data;
1868 wmove(enginew, 0, 0);
1869 wclrtobot(enginew);
1871 if (d->engine && d->engine->enginebuf) {
1872 for (i = 0; d->engine->enginebuf[i]; i++)
1873 mvwprintw(enginew, i + 2, 1, "%s", d->engine->enginebuf[i]);
1876 window_draw_title(enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
1877 CP_MESSAGE_BORDER);
1880 void update_all(GAME g)
1882 struct userdata_s *d = g->data;
1885 * In the middle of a macro. Don't update the screen.
1887 if (macro_match != -1)
1888 return;
1890 wmove(boardw, ROWTOMATRIX(d->c_row), COLTOMATRIX(d->c_col));
1891 update_board_window(g);
1892 update_status_window(g);
1893 update_history_window(g);
1894 update_tag_window(g->tag);
1895 update_engine_window(g);
1896 update_panels();
1897 doupdate();
1900 static void game_next_prev(GAME g, int n, int count)
1902 if (gtotal < 2)
1903 return;
1905 if (n == 1) {
1906 if (gindex + count > gtotal - 1) {
1907 if (count != 1)
1908 gindex = gtotal - 1;
1909 else
1910 gindex = 0;
1912 else
1913 gindex += count;
1915 else {
1916 if (gindex - count < 0) {
1917 if (count != 1)
1918 gindex = 0;
1919 else
1920 gindex = gtotal - 1;
1922 else
1923 gindex -= count;
1926 gp = game[gindex];
1929 static void delete_game(int which)
1931 int i;
1932 struct userdata_s *d;
1934 for (i = 0; i < gtotal; i++) {
1935 d = game[i]->data;
1937 if (i == which || TEST_FLAG(d->flags, CF_DELETE)) {
1938 int n;
1940 free_userdata_once(game[i]);
1941 pgn_free(game[i]);
1943 for (n = i; n+1 < gtotal; n++)
1944 game[n] = game[n+1];
1946 gtotal--;
1947 i--;
1948 which = -1;
1952 if (which != -1) {
1953 if (which + 1 >= gtotal)
1954 gindex = gtotal - 1;
1955 else
1956 gindex = which;
1958 else
1959 gindex = gtotal - 1;
1961 gp = game[gindex];
1962 gp->hp = gp->history;
1966 * FIXME find across multiple games.
1968 static int find_move_exp(GAME g, regex_t r, int which, int count)
1970 int i;
1971 int ret;
1972 char errbuf[255];
1973 int incr;
1974 int found;
1976 incr = (which == 0) ? -1 : 1;
1978 for (i = g->hindex + incr - 1, found = 0; ; i += incr) {
1979 if (i == g->hindex - 1)
1980 break;
1982 if (i >= pgn_history_total(g->hp))
1983 i = 0;
1984 else if (i < 0)
1985 i = pgn_history_total(g->hp) - 1;
1987 // FIXME RAV
1988 ret = regexec(&r, g->hp[i]->move, 0, 0, 0);
1990 if (ret == 0) {
1991 if (count == ++found) {
1992 return i + 1;
1995 else {
1996 if (ret != REG_NOMATCH) {
1997 regerror(ret, &r, errbuf, sizeof(errbuf));
1998 cmessage(_("Error Matching Regular Expression"), ANY_KEY_STR, "%s", errbuf);
1999 return -1;
2004 return -1;
2007 static int toggle_delete_flag(int n)
2009 int i, x;
2010 struct userdata_s *d = game[n]->data;
2012 TOGGLE_FLAG(d->flags, CF_DELETE);
2013 gindex = n;
2015 for (i = x = 0; i < gtotal; i++) {
2016 d = game[i]->data;
2018 if (TEST_FLAG(d->flags, CF_DELETE))
2019 x++;
2022 if (x == gtotal) {
2023 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
2024 d = game[n]->data;
2025 CLEAR_FLAG(d->flags, CF_DELETE);
2026 return 1;
2029 return 0;
2032 static int find_game_exp(char *str, int which, int count)
2034 char *nstr = NULL, *exp = NULL;
2035 regex_t nexp, vexp;
2036 int ret = -1;
2037 int g = 0;
2038 char buf[255] = {0}, *tmp;
2039 char errbuf[255];
2040 int found = 0;
2041 int incr = (which == 0) ? -(1) : 1;
2043 strncpy(buf, str, sizeof(buf)-1);
2044 tmp = buf;
2046 if (strstr(tmp, ":") != NULL) {
2047 nstr = strsep(&tmp, ":");
2049 if ((ret = regcomp(&nexp, nstr,
2050 REG_ICASE|REG_EXTENDED|REG_NOSUB)) != 0) {
2051 regerror(ret, &nexp, errbuf, sizeof(errbuf));
2052 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
2053 ret = g = -1;
2054 goto cleanup;
2058 exp = tmp;
2060 while (*exp && isspace(*exp))
2061 exp++;
2063 if (exp == NULL)
2064 goto cleanup;
2066 if ((ret = regcomp(&vexp, exp, REG_EXTENDED|REG_NOSUB)) != 0) {
2067 regerror(ret, &vexp, errbuf, sizeof(errbuf));
2068 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
2069 ret = -1;
2070 goto cleanup;
2073 ret = -1;
2075 for (g = gindex + incr, found = 0; ; g += incr) {
2076 int t;
2078 if (g == gtotal)
2079 g = 0;
2080 else if (g < 0)
2081 g = gtotal - 1;
2083 if (g == gindex)
2084 break;
2086 for (t = 0; game[g]->tag[t]; t++) {
2087 if (nstr) {
2088 if (regexec(&nexp, game[g]->tag[t]->name, 0, 0, 0) == 0) {
2089 if (regexec(&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0) {
2090 if (count == ++found) {
2091 ret = g;
2092 goto cleanup;
2097 else {
2098 if (regexec(&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0) {
2099 if (count == ++found) {
2100 ret = g;
2101 goto cleanup;
2107 ret = -1;
2110 cleanup:
2111 if (nstr)
2112 regfree(&nexp);
2114 if (g != -1)
2115 regfree(&vexp);
2117 return ret;
2121 * Updates the notification line in the status window then refreshes the
2122 * status window.
2124 void update_status_notify(GAME g, char *fmt, ...)
2126 va_list ap;
2127 #ifdef HAVE_VASPRINTF
2128 char *line;
2129 #else
2130 char line[COLS];
2131 #endif
2133 if (!fmt) {
2134 if (status.notify) {
2135 free(status.notify);
2136 status.notify = NULL;
2139 return;
2142 va_start(ap, fmt);
2143 #ifdef HAVE_VASPRINTF
2144 vasprintf(&line, fmt, ap);
2145 #else
2146 vsnprintf(line, sizeof(line), fmt, ap);
2147 #endif
2148 va_end(ap);
2150 if (status.notify)
2151 free(status.notify);
2153 status.notify = str_to_wchar(line);
2155 #ifdef HAVE_VASPRINTF
2156 free(line);
2157 #endif
2160 int rav_next_prev(GAME g, BOARD b, int n)
2162 // Next RAV.
2163 if (n) {
2164 if ((!g->ravlevel && g->hindex && g->hp[g->hindex - 1]->rav == NULL) ||
2165 (!g->ravlevel && !g->hindex && g->hp[g->hindex]->rav == NULL) ||
2166 (g->ravlevel && g->hp[g->hindex]->rav == NULL))
2167 return 1;
2169 g->rav = Realloc(g->rav, (g->ravlevel + 1) * sizeof(RAV));
2170 g->rav[g->ravlevel].hp = g->hp;
2171 g->rav[g->ravlevel].flags = g->flags;
2172 g->rav[g->ravlevel].fen = pgn_game_to_fen(g, b);
2173 g->rav[g->ravlevel].hindex = g->hindex;
2174 g->hp = (!g->ravlevel) ? (g->hindex) ? g->hp[g->hindex - 1]->rav : g->hp[g->hindex]->rav : g->hp[g->hindex]->rav;
2175 g->hindex = 0;
2176 g->ravlevel++;
2177 pgn_board_update(g, b, g->hindex + 1);
2178 return 0;
2181 if (g->ravlevel - 1 < 0)
2182 return 1;
2184 // Previous RAV.
2185 g->ravlevel--;
2186 pgn_board_init_fen(g, b, g->rav[g->ravlevel].fen);
2187 free(g->rav[g->ravlevel].fen);
2188 g->hp = g->rav[g->ravlevel].hp;
2189 g->flags = g->rav[g->ravlevel].flags;
2190 g->hindex = g->rav[g->ravlevel].hindex;
2191 return 0;
2194 static void draw_window_decor()
2196 move_panel(boardp, 0,
2197 (config.boardleft) ? 0 : COLS - BOARD_WIDTH);
2198 move_panel(historyp, LINES - HISTORY_HEIGHT,
2199 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH : 0 :
2200 (MEGA_BOARD) ? 0 : COLS - HISTORY_WIDTH);
2201 move_panel(statusp, 0,
2202 (config.boardleft) ? BOARD_WIDTH : 0);
2203 move_panel(tagp, STATUS_HEIGHT + 1,
2204 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH :
2205 HISTORY_WIDTH : 0);
2207 wbkgd(boardw, CP_BOARD_WINDOW);
2208 wbkgd(statusw, CP_STATUS_WINDOW);
2209 window_draw_title(statusw, _("Game Status"), STATUS_WIDTH,
2210 CP_STATUS_TITLE, CP_STATUS_BORDER);
2211 wbkgd(tagw, CP_TAG_WINDOW);
2212 window_draw_title(tagw, _("Roster Tags"), TAG_WIDTH, CP_TAG_TITLE,
2213 CP_TAG_BORDER);
2214 wbkgd(historyw, CP_HISTORY_WINDOW);
2215 window_draw_title(historyw, _("Move History"), HISTORY_WIDTH,
2216 CP_HISTORY_TITLE, CP_HISTORY_BORDER);
2219 void do_window_resize()
2221 if (LINES < 23 || COLS < 74)
2222 return;
2224 resizeterm(LINES, COLS);
2225 endwin();
2226 wresize(boardw, BOARD_HEIGHT, BOARD_WIDTH);
2227 wresize(historyw, HISTORY_HEIGHT, HISTORY_WIDTH);
2228 wresize(statusw, STATUS_HEIGHT, STATUS_WIDTH);
2229 wresize(tagw, TAG_HEIGHT, TAG_WIDTH);
2230 clear ();
2231 wclear (boardw);
2232 wclear (historyw);
2233 wclear (tagw);
2234 wclear (statusw);
2235 wclear (loadingw);
2236 wclear (enginew);
2237 draw_window_decor();
2238 update_all(gp);
2239 keypad(boardw, TRUE);
2240 curs_set(0);
2241 cbreak();
2242 noecho();
2245 void do_global_redraw ()
2247 do_window_resize ();
2250 void stop_clock()
2252 memset(&clock_timer, 0, sizeof(struct itimerval));
2253 setitimer(ITIMER_REAL, &clock_timer, NULL);
2256 void start_clock(GAME g)
2258 struct userdata_s *d = g->data;
2260 if (clock_timer.it_interval.tv_usec)
2261 return;
2263 memset(&d->elapsed, 0, sizeof(struct timeval));
2264 clock_timer.it_value.tv_sec = 0;
2265 clock_timer.it_value.tv_usec = 100000;
2266 clock_timer.it_interval.tv_sec = 0;
2267 clock_timer.it_interval.tv_usec = 100000;
2268 setitimer(ITIMER_REAL, &clock_timer, NULL);
2271 static void update_clocks()
2273 int i;
2274 struct userdata_s *d;
2275 struct itimerval it;
2276 int update = 0;
2278 getitimer(ITIMER_REAL, &it);
2280 for (i = 0; i < gtotal; i++) {
2281 d = game[i]->data;
2283 if (d && d->mode == MODE_PLAY) {
2284 if (d->paused == 1 || TEST_FLAG(d->flags, CF_NEW))
2285 continue;
2286 else if (d->paused == -1) {
2287 if (game[i]->side == game[i]->turn) {
2288 d->paused = 1;
2289 continue;
2293 update_clock(game[i], it);
2295 if (game[i] == gp)
2296 update = 1;
2300 if (update) {
2301 update_status_window(gp);
2302 update_panels();
2303 doupdate();
2307 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2309 static int parse_clock_time(char **str)
2311 char *p = *str;
2312 int n = 0, t = 0;
2314 SKIP_SPACE(p);
2316 if (!isdigit(*p))
2317 return -1;
2319 while (*p) {
2320 if (isdigit(*p)) {
2321 t = atoi(p);
2323 while (isdigit(*p))
2324 p++;
2326 continue;
2329 switch (*p) {
2330 case 'H':
2331 case 'h':
2332 n += t * (60 * 60);
2333 t = 0;
2334 break;
2335 case 'M':
2336 case 'm':
2337 n += t * 60;
2338 t = 0;
2339 break;
2340 case 'S':
2341 case 's':
2342 n += t;
2343 t = 0;
2344 break;
2345 case ' ':
2346 p++;
2347 case '/':
2348 case '+':
2349 goto done;
2350 default:
2351 *str = p;
2352 return -1;
2355 p++;
2358 done:
2359 n += t;
2360 *str = p;
2361 return n;
2364 static int parse_clock_input(struct clock_s *clk, char *str, int *incr)
2366 char *p = str;
2367 long n = 0;
2368 int plus = 0;
2369 int m = 0;
2370 int tc = 0;
2372 SKIP_SPACE(p);
2374 if (!*p)
2375 return 0;
2377 if (*p == '+') {
2378 plus = 1;
2379 p++;
2380 SKIP_SPACE(p);
2382 if (*p == '+')
2383 goto move_incr;
2385 else
2386 memset(clk, 0, sizeof(struct clock_s));
2388 again:
2389 /* Sudden death. */
2390 if (strncasecmp(p, "SD", 2) == 0) {
2391 n = 0;
2392 p += 2;
2393 goto tc;
2396 n = parse_clock_time(&p);
2398 if (n == -1)
2399 return 1;
2401 if (!n)
2402 goto done;
2404 /* Time control. */
2406 if (*p == '/') {
2407 if (plus)
2408 return 1;
2410 /* Sudden death without a previous time control. */
2411 if (!n && !tc)
2412 return 1;
2414 m = n;
2415 p++;
2416 n = parse_clock_time(&p);
2418 if (n == -1)
2419 return 1;
2421 if (tc >= MAX_TC) {
2422 message(ERROR_STR, ANY_KEY_STR, "%s (%i)", _("Maximum number of time controls reached"), MAX_TC);
2423 return 1;
2426 clk->tc[tc][0] = m;
2427 clk->tc[tc++][1] = n;
2428 SKIP_SPACE(p);
2430 if (*p == '+')
2431 goto move_incr;
2433 if (*p)
2434 goto again;
2436 goto done;
2439 if (plus)
2440 *incr = n;
2441 else
2442 clk->tc[clk->tcn][1] = (n <= clk->elapsed.tv_sec) ? clk->elapsed.tv_sec + n : n;
2444 move_incr:
2445 if (*p) {
2446 if (*p++ == '+') {
2447 if (!isdigit(*p))
2448 return 1;
2450 n = parse_clock_time(&p);
2452 if (n == -1 || *p)
2453 return 1;
2455 clk->incr = n;
2457 SKIP_SPACE(p);
2459 if (*p)
2460 return 1;
2462 else
2463 return 1;
2466 done:
2467 return 0;
2470 static int parse_which_clock(struct clock_s *clk, char *str)
2472 struct clock_s tmp;
2473 int incr = 0;
2475 memcpy(&tmp, clk, sizeof(struct clock_s));
2477 if (parse_clock_input(&tmp, str, &incr)) {
2478 cmessage(ERROR_STR, ANY_KEY_STR, _("Invalid clock specification"));
2479 return 1;
2482 memcpy(clk, &tmp, sizeof(struct clock_s));
2483 clk->tc[clk->tcn][1] += incr;
2484 return 0;
2487 void do_clock_input_finalize(WIN *win)
2489 struct userdata_s *d = gp->data;
2490 struct input_data_s *in = win->data;
2491 char *p = in->str;
2493 if (!in->str) {
2494 free(in);
2495 return;
2498 SKIP_SPACE(p);
2500 if (tolower(*p) == 'w') {
2501 p++;
2503 if (parse_which_clock(&d->wclock, p))
2504 goto done;
2506 else if (tolower(*p) == 'b') {
2507 p++;
2509 if (parse_which_clock(&d->bclock, p))
2510 goto done;
2512 else {
2513 if (parse_which_clock(&d->wclock, p))
2514 goto done;
2516 if (parse_which_clock(&d->bclock, p))
2517 goto done;
2520 if (!d->wclock.tc[0][1] && !d->bclock.tc[0][1])
2521 CLEAR_FLAG(d->flags, CF_CLOCK);
2522 else
2523 SET_FLAG(d->flags, CF_CLOCK);
2525 done:
2526 free(in->str);
2527 free(in);
2530 void do_engine_command_finalize(WIN *win)
2532 struct userdata_s *d = gp->data;
2533 struct input_data_s *in = win->data;
2534 int x;
2536 if (!in->str) {
2537 free(in);
2538 return;
2541 if (!d->engine)
2542 goto done;
2544 x = d->engine->status;
2545 send_to_engine(gp, -1, "%s\n", in->str);
2546 d->engine->status = x;
2548 done:
2549 free(in->str);
2550 free(in);
2553 void do_board_details()
2555 config.details = (config.details) ? 0 : 1;
2558 void do_toggle_strict_castling()
2560 int n;
2562 pgn_config_get(PGN_STRICT_CASTLING, &n);
2564 if (n == 0)
2565 pgn_config_set(PGN_STRICT_CASTLING, 1);
2566 else
2567 pgn_config_set(PGN_STRICT_CASTLING, 0);
2570 void do_play_set_clock()
2572 struct input_data_s *in;
2574 in = Calloc(1, sizeof(struct input_data_s));
2575 in->efunc = do_clock_input_finalize;
2576 construct_input(_("Set Clock"), NULL, 1, 1,
2577 _ ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n" \
2578 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2579 NULL, NULL, 0, in, INPUT_HIST_CLOCK, -1);
2582 void do_play_toggle_human()
2584 struct userdata_s *d = gp->data;
2586 TOGGLE_FLAG(d->flags, CF_HUMAN);
2588 if (!TEST_FLAG(d->flags, CF_HUMAN) && pgn_history_total(gp->hp)) {
2589 if (init_chess_engine(gp))
2590 return;
2593 CLEAR_FLAG(d->flags, CF_ENGINE_LOOP);
2595 if (d->engine)
2596 d->engine->status = ENGINE_READY;
2599 void do_play_toggle_engine()
2601 struct userdata_s *d = gp->data;
2603 TOGGLE_FLAG(d->flags, CF_ENGINE_LOOP);
2604 CLEAR_FLAG(d->flags, CF_HUMAN);
2606 if (d->engine && TEST_FLAG(d->flags, CF_ENGINE_LOOP)) {
2607 char *fen = pgn_game_to_fen (gp, d->b);
2609 pgn_board_update(gp, d->b,
2610 pgn_history_total(gp->hp));
2611 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
2612 free (fen);
2617 * This will send a command to the engine skipping the command queue.
2619 void do_play_send_command()
2621 struct userdata_s *d = gp->data;
2622 struct input_data_s *in;
2624 if (!d->engine || d->engine->status == ENGINE_OFFLINE) {
2625 if (init_chess_engine(gp))
2626 return;
2629 in = Calloc(1, sizeof(struct input_data_s));
2630 in->efunc = do_engine_command_finalize;
2631 construct_input(_("Engine Command"), NULL, 1, 1, NULL, NULL, NULL, 0, in, INPUT_HIST_ENGINE, -1);
2634 void do_play_switch_turn()
2636 struct userdata_s *d = gp->data;
2638 pgn_switch_side(gp);
2639 pgn_switch_turn(gp);
2641 if (!TEST_FLAG(d->flags, CF_HUMAN))
2642 add_engine_command(gp, -1,
2643 (gp->side == WHITE) ? "white\n" : "black\n");
2645 update_status_window(gp);
2648 void do_play_toggle_eh_mode()
2650 struct userdata_s *d = gp->data;
2652 if (!TEST_FLAG(d->flags, CF_HUMAN)) {
2653 if (!gp->hindex){
2654 pgn_switch_side(gp, TRUE);
2655 d->play_mode = (d->play_mode) ? PLAY_HE : PLAY_EH;
2656 if (gp->side == BLACK)
2657 update_status_notify(gp, _("Press 'g' to start the game"));
2659 rotate = (rotate) ? FALSE : TRUE;
2661 else
2662 message(NULL, ANY_KEY_STR,
2663 _("You may only switch sides at the start of the \n"
2664 "game. Press ^K or ^N to begin a new game."));
2668 void do_play_undo()
2670 struct userdata_s *d = gp->data;
2672 if (!pgn_history_total(gp->hp))
2673 return;
2675 if (keycount) {
2676 if (gp->hindex - keycount < 0)
2677 gp->hindex = 0;
2678 else {
2679 if (go_move)
2680 gp->hindex -= (keycount * 2) -1;
2681 else
2682 gp->hindex -= keycount * 2;
2685 else {
2686 if (gp->hindex - 2 < 0)
2687 gp->hindex = 0;
2688 else {
2689 if (go_move)
2690 gp->hindex -= 1;
2691 else
2692 gp->hindex -= 2;
2696 pgn_history_free(gp->hp, gp->hindex);
2697 gp->hindex = pgn_history_total(gp->hp);
2698 pgn_board_update(gp, d->b, gp->hindex);
2700 if (d->engine && d->engine->status == ENGINE_READY) {
2701 char *fen = pgn_game_to_fen(gp, d->b);
2703 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
2704 free (fen);
2705 d->engine->status = ENGINE_READY;
2708 update_history_window(gp);
2710 if (go_move) {
2711 pgn_switch_side(gp, FALSE);
2712 go_move--;
2716 void do_play_toggle_pause()
2718 struct userdata_s *d = gp->data;
2720 if (!TEST_FLAG(d->flags, CF_HUMAN) && gp->turn !=
2721 gp->side) {
2722 d->paused = -1;
2723 return;
2726 d->paused = (d->paused) ? 0 : 1;
2729 void do_play_go()
2731 struct userdata_s *d = gp->data;
2733 if (TEST_FLAG(d->flags, CF_HUMAN))
2734 return;
2736 if (fm_loaded_file && gp->side != gp->turn) {
2737 pgn_switch_side(gp, FALSE);
2738 add_engine_command(gp, ENGINE_THINKING, "black\n");
2741 add_engine_command(gp, ENGINE_THINKING, "go\n");
2743 // Completa la función para que permita seguir jugando al usarla.
2744 // Complete the function to allow continue playing when using.
2745 if (gp->side == gp->turn)
2746 pgn_switch_side(gp, FALSE);
2748 go_move++;
2751 void do_play_config_command()
2753 int x, w;
2755 if (config.keys) {
2756 for (x = 0; config.keys[x]; x++) {
2757 if (config.keys[x]->c == input_c) {
2758 switch (config.keys[x]->type) {
2759 case KEY_DEFAULT:
2760 add_engine_command(gp, -1, "%ls\n",
2761 config.keys[x]->str);
2762 break;
2763 case KEY_SET:
2764 if (!keycount)
2765 break;
2767 add_engine_command(gp, -1,
2768 "%ls %i\n", config.keys[x]->str, keycount);
2769 keycount = 0;
2770 break;
2771 case KEY_REPEAT:
2772 if (!keycount)
2773 break;
2775 for (w = 0; w < keycount; w++)
2776 add_engine_command(gp, -1,
2777 "%ls\n", config.keys[x]->str);
2778 keycount = 0;
2779 break;
2785 update_status_notify(gp, NULL);
2788 void do_play_cancel_selected()
2790 struct userdata_s *d = gp->data;
2792 d->sp.icon = d->sp.srow = d->sp.scol = 0;
2793 keycount = 0;
2794 pgn_reset_valid_moves(d->b);
2795 update_status_notify(gp, NULL);
2798 void do_play_commit()
2800 struct userdata_s *d = gp->data;
2802 pushkey = keycount = 0;
2803 update_status_notify(gp, NULL);
2805 if (!TEST_FLAG(d->flags, CF_HUMAN) &&
2806 (!d->engine || d->engine->status == ENGINE_THINKING))
2807 return;
2809 if (!d->sp.icon)
2810 return;
2812 d->sp.row = d->c_row;
2813 d->sp.col = d->c_col;
2815 if (rotate) {
2816 rotate_position(SP_POSITION);
2817 rotate_position(SPS_POSITION);
2820 move_to_engine(gp);
2822 // Completa la función para que permita seguir jugando cuando se carga un
2823 // archivo pgn (con juego no terminado) que inicie con turno del lado
2824 // negro.
2825 // Complete the function to allow continue playing when loading a file
2826 // pgn (with unfinished game) you start to turn black side.
2827 if (gp->side != gp->turn)
2828 pgn_switch_side(gp, FALSE);
2830 if (rotate && d->sp.icon)
2831 rotate_position(SPS_POSITION);
2833 // Envia comando 'go' a Polyglot en el primer movimiento debido a que
2834 // polyglot no envia el movimiento y cboard se queda esperando.
2835 // Send command 'go' to the first movement Polyglot because cboard
2836 // waits to send polyglot movement and this does not make.
2837 if (config.fmpolyglot &&
2838 ((gp->side == WHITE && !gp->hindex) || fm_loaded_file))
2839 add_engine_command(gp, ENGINE_THINKING, "go\n");
2841 go_move = 0;
2842 fm_loaded_file = FALSE;
2845 void do_play_select()
2847 struct userdata_s *d = gp->data;
2849 if (!TEST_FLAG(d->flags, CF_HUMAN) && (!d->engine ||
2850 d->engine->status == ENGINE_OFFLINE)) {
2851 if (init_chess_engine(gp))
2852 return;
2855 if (d->engine && d->engine->status == ENGINE_THINKING)
2856 return;
2858 if (d->sp.icon)
2859 do_play_cancel_selected ();
2861 if (rotate)
2862 rotate_position(CURSOR_POSITION);
2864 d->sp.icon = d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon;
2866 if (pgn_piece_to_int(d->sp.icon) == OPEN_SQUARE) {
2867 d->sp.icon = 0;
2868 return;
2871 if (((islower(d->sp.icon) && gp->turn != BLACK)
2872 || (isupper(d->sp.icon) && gp->turn != WHITE))) {
2873 struct key_s **k;
2874 char *str = Malloc (512);
2876 for (k = play_keys; *k; k++) {
2877 if ((*k)->f == do_play_toggle_eh_mode)
2878 break;
2881 snprintf (str, 512, _("It is not your turn to move. You may switch playing sides by pressing \"%lc\"."),
2882 *k ? (*k)->c : '?');
2883 message(NULL, ANY_KEY_STR, "%s", str);
2884 free (str);
2885 d->sp.icon = 0;
2886 return;
2887 #if 0
2888 if (pgn_history_total(gp->hp)) {
2889 message(NULL, ANY_KEY_STR, "%s", _("It is not your turn to move. You can switch sides "));
2890 d->sp.icon = 0;
2891 return;
2893 else {
2894 if (pgn_tag_find(gp->tag, "FEN") != E_PGN_ERR)
2895 return;
2897 add_engine_command(gp, ENGINE_READY, "black\n");
2898 pgn_switch_turn(gp);
2900 if (gp->side != BLACK)
2901 pgn_switch_side(gp);
2903 #endif
2906 d->sp.srow = d->c_row;
2907 d->sp.scol = d->c_col;
2909 if (config.validmoves)
2910 pgn_find_valid_moves(gp, d->b, d->sp.scol, d->sp.srow);
2912 if (rotate) {
2913 rotate_position(CURSOR_POSITION);
2914 rotate_position(SPS_POSITION);
2917 CLEAR_FLAG(d->flags, CF_NEW);
2918 start_clock(gp);
2921 /* FIXME: keys with the same function should comma deliminated. */
2922 static wchar_t *build_help(struct key_s **keys)
2924 int i, nlen = 1, len, t, n;
2925 wchar_t *buf = NULL, *wc = NULL;
2926 wchar_t *p;
2928 if (!keys)
2929 return NULL;
2931 for (i = len = t = 0; keys[i]; i++) {
2932 if (!keys[i]->d)
2933 continue;
2935 if (keys[i]->key) {
2936 if (wcslen(keys[i]->key) > nlen) {
2937 nlen = wcslen(keys[i]->key);
2938 t += nlen;
2940 else
2941 t++;
2944 if (keys[i]->d) {
2945 if (wcslen(keys[i]->d) > len)
2946 len = wcslen(keys[i]->d);
2949 t += len;
2950 t += keys[i]->r;
2953 t += 4 + i;
2954 buf = Malloc(t*sizeof(wchar_t));
2955 p = buf;
2957 for (i = 0; keys[i]; i++) {
2958 if (!keys[i]->d)
2959 continue;
2961 if (keys[i]->key)
2962 n = wcslen(keys[i]->key);
2963 else
2964 n = 1;
2966 while (n++ <= nlen)
2967 *p++ = ' ';
2969 *p = 0;
2971 if (keys[i]->key) {
2972 wcscat(buf, keys[i]->key);
2973 p = buf + wcslen(buf);
2975 else
2976 *p++ = keys[i]->c;
2978 *p++ = ' ';
2979 *p++ = '-';
2980 *p++ = ' ';
2981 *p = 0;
2983 if (keys[i]->d)
2984 wcscat(buf, keys[i]->d);
2986 if (keys[i]->r) {
2987 wc = str_to_wchar ("*");
2988 wcscat(buf, wc);
2989 free (wc);
2992 wc = str_to_wchar ("\n");
2993 wcscat(buf, wc);
2994 free (wc);
2995 p = buf + wcslen(buf);
2998 return buf;
3001 void do_more_help(WIN *);
3002 void do_main_help(WIN *win)
3004 wchar_t *buf;
3006 switch (win->c) {
3007 case 'p':
3008 buf = build_help(play_keys);
3009 construct_message(_("Play Mode Keys (* = can take a repeat count)"), ANY_KEY_SCROLL_STR, 0, 0,
3010 NULL, NULL, buf, do_more_help, 0, 1, "%ls", buf);
3011 break;
3012 case 'h':
3013 buf = build_help(history_keys);
3014 construct_message(_("History Mode Keys (* = can take a repeat count)"), ANY_KEY_SCROLL_STR, 0, 0,
3015 NULL, NULL, buf, do_more_help, 0, 1, "%ls", buf);
3016 break;
3017 case 'e':
3018 buf = build_help(edit_keys);
3019 construct_message(_("Edit Mode Keys (* = can take a repeat count)"), ANY_KEY_SCROLL_STR, 0, 0,
3020 NULL, NULL, buf, do_more_help, 0, 1, "%ls", buf);
3021 break;
3022 case 'g':
3023 buf = build_help(global_keys);
3024 construct_message(_("Global Game Keys (* = can take a repeat count)"), ANY_KEY_SCROLL_STR, 0, 0,
3025 NULL, NULL, buf, do_more_help, 0, 1, "%ls", buf);
3026 break;
3027 default:
3028 break;
3032 void do_more_help(WIN *win)
3034 if (win->c == KEY_F(1) || win->c == CTRL_KEY('g'))
3035 construct_message(_("Command Key Index"),
3036 _ ("p/h/e/g or any other key to quit"), 0, 0,
3037 NULL, NULL, NULL, do_main_help, 0, 0, "%s",
3039 "p - play mode keys\n"
3040 "h - history mode keys\n"
3041 "e - board edit mode keys\n"
3042 "g - global game keys"
3046 void do_play_help()
3048 wchar_t *buf = build_help(play_keys);
3050 construct_message(_("Play Mode Keys (* = can take a repeat count)"),
3051 ANY_KEY_STR, 0, 0, NULL, NULL,
3052 buf, do_more_help, 0, 1, "%ls", buf);
3055 void do_play_history_mode()
3057 struct userdata_s *d = gp->data;
3059 if (!pgn_history_total(gp->hp) ||
3060 (d->engine && d->engine->status == ENGINE_THINKING))
3061 return;
3063 d->mode = MODE_HISTORY;
3064 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3067 void do_play_edit_mode()
3069 struct userdata_s *d = gp->data;
3071 if (pgn_history_total(gp->hp))
3072 return;
3074 pgn_board_init_fen(gp, d->b, NULL);
3075 config.details++;
3076 d->mode = MODE_EDIT;
3079 void do_edit_insert_finalize(WIN *win)
3081 struct userdata_s *d = win->data;
3083 if (pgn_piece_to_int(win->c) == -1)
3084 return;
3086 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon = win->c;
3089 void do_edit_select()
3091 struct userdata_s *d = gp->data;
3093 if (d->sp.icon)
3094 return;
3096 d->sp.icon = d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon;
3098 if (pgn_piece_to_int(d->sp.icon) == OPEN_SQUARE) {
3099 d->sp.icon = 0;
3100 return;
3103 d->sp.srow = d->c_row;
3104 d->sp.scol = d->c_col;
3107 void do_edit_commit()
3109 int p;
3110 struct userdata_s *d = gp->data;
3112 pushkey = keycount = 0;
3113 update_status_notify(gp, NULL);
3115 if (!d->sp.icon)
3116 return;
3118 d->sp.row = d->c_row;
3119 d->sp.col = d->c_col;
3120 p = d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon;
3121 d->b[RANKTOBOARD(d->sp.row)][FILETOBOARD(d->sp.col)].icon = p;
3122 d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon =
3123 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3124 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3127 void do_edit_delete()
3129 struct userdata_s *d = gp->data;
3131 if (d->sp.icon)
3132 d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon =
3133 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3134 else
3135 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon =
3136 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3138 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3141 void do_edit_cancel_selected()
3143 struct userdata_s *d = gp->data;
3145 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3146 keycount = 0;
3147 update_status_notify(gp, NULL);
3150 void do_edit_switch_turn()
3152 pgn_switch_turn(gp);
3155 void do_edit_toggle_castle()
3157 struct userdata_s *d = gp->data;
3159 castling_state(gp, d->b, RANKTOBOARD(d->c_row),
3160 FILETOBOARD(d->c_col),
3161 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon, 1);
3164 void do_edit_insert()
3166 struct userdata_s *d = gp->data;
3168 construct_message(_("Insert Piece"), _("P=pawn, R=rook, N=knight, B=bishop, "), 0, 0, NULL, NULL,
3169 d->b, do_edit_insert_finalize, 0, 0, "%s", _("Type the piece letter to insert. Lowercase "));
3172 void do_edit_enpassant()
3174 struct userdata_s *d = gp->data;
3176 if (d->c_row == 6 || d->c_row == 3) {
3177 pgn_reset_enpassant(d->b);
3178 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].enpassant = 1;
3182 void do_edit_help()
3184 wchar_t *buf = build_help(edit_keys);
3186 construct_message(_("Edit Mode Keys (* = can take a repeat count)"),
3187 ANY_KEY_STR, 0, 0, NULL, NULL,
3188 buf, do_more_help, 0, 1, "%ls", buf);
3191 void do_edit_exit()
3193 struct userdata_s *d = gp->data;
3194 char *fen = pgn_game_to_fen(gp, d->b);
3196 config.details--;
3197 pgn_tag_add(&gp->tag, "FEN", fen);
3198 free (fen);
3199 pgn_tag_add(&gp->tag, "SetUp", "1");
3200 pgn_tag_sort(gp->tag);
3201 pgn_board_update(gp, d->b, gp->hindex);
3202 d->mode = MODE_PLAY;
3205 void really_do_annotate_finalize(struct input_data_s *in,
3206 struct userdata_s *d)
3208 HISTORY *h = in->data;
3209 int len;
3211 if (!in->str) {
3212 if (h->comment) {
3213 free(h->comment);
3214 h->comment = NULL;
3217 else {
3218 len = strlen(in->str);
3219 h->comment = Realloc(h->comment, len+1);
3220 strncpy(h->comment, in->str, len);
3221 h->comment[len] = 0;
3224 free(in->str);
3225 free(in);
3226 SET_FLAG(d->flags, CF_MODIFIED);
3229 void do_annotate_finalize(WIN *win)
3231 struct userdata_s *d = gp->data;
3232 struct input_data_s *in = win->data;
3234 really_do_annotate_finalize(in, d);
3237 void do_find_move_exp_finalize(int init, int which)
3239 int n;
3240 struct userdata_s *d = gp->data;
3241 static int firstrun;
3242 static regex_t r;
3243 int ret;
3244 char errbuf[255];
3246 if (init || !firstrun) {
3247 if (!firstrun)
3248 regfree(&r);
3250 if ((ret = regcomp(&r, moveexp, REG_EXTENDED|REG_NOSUB)) != 0) {
3251 regerror(ret, &r, errbuf, sizeof(errbuf));
3252 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
3253 return;
3256 firstrun = 1;
3259 if ((n = find_move_exp(gp, r,
3260 (which == -1) ? 0 : 1, (keycount) ? keycount : 1)) == -1)
3261 return;
3263 gp->hindex = n;
3264 pgn_board_update(gp, d->b, gp->hindex);
3267 void do_find_move_exp(WIN *win)
3269 struct input_data_s *in = win->data;
3270 int *n = in->data;
3271 int which = *n;
3273 if (in->str) {
3274 strncpy(moveexp, in->str, sizeof(moveexp)-1);
3275 moveexp[sizeof(moveexp)-1] = 0;
3276 do_find_move_exp_finalize(1, which);
3277 free(in->str);
3280 free(in->data);
3281 free(in);
3284 void do_move_jump_finalize(int n)
3286 struct userdata_s *d = gp->data;
3288 if (n < 0 || n > (pgn_history_total(gp->hp) / 2))
3289 return;
3291 keycount = 0;
3292 update_status_notify(gp, NULL);
3293 gp->hindex = (n) ? n * 2 - 1 : n * 2;
3294 pgn_board_update(gp, d->b, gp->hindex);
3297 void do_move_jump(WIN *win)
3299 struct input_data_s *in = win->data;
3301 if (!in->str || !isinteger(in->str)) {
3302 if (in->str)
3303 free(in->str);
3305 free(in);
3306 return;
3309 do_move_jump_finalize(atoi(in->str));
3310 free(in->str);
3311 free(in);
3314 struct history_menu_s {
3315 char *line;
3316 int hindex;
3317 int ravlevel;
3318 int move;
3319 int indent;
3322 void free_history_menu_data(struct history_menu_s **h)
3324 int i;
3326 if (!h)
3327 return;
3329 for (i = 0; h[i]; i++) {
3330 free(h[i]->line);
3331 free(h[i]);
3334 free(h);
3337 void get_history_data(HISTORY **hp, struct history_menu_s ***menu, int m,
3338 int turn)
3340 int i, n = 0;
3341 int t = pgn_history_total(hp);
3342 char buf[MAX_SAN_MOVE_LEN + 4];
3343 static int depth;
3344 struct history_menu_s **hmenu = *menu;
3346 if (hmenu)
3347 for (n = 0; hmenu[n]; n++);
3348 else
3349 depth = 0;
3351 for (i = 0; i < t; i++) {
3352 hmenu = Realloc(hmenu, (n + 2) * sizeof(struct history_menu_s *));
3353 hmenu[n] = Malloc(sizeof(struct history_menu_s));
3354 snprintf(buf, sizeof(buf), "%c%s%s", (turn == WHITE) ? 'W' : 'B',
3355 hp[i]->move, (hp[i]->comment || hp[i]->nag[0]) ? " !" : "");
3356 hmenu[n]->line = strdup(buf);
3357 hmenu[n]->hindex = i;
3358 hmenu[n]->indent = 0;
3359 hmenu[n]->ravlevel = depth;
3360 hmenu[n]->move = (n && depth > hmenu[n-1]->ravlevel) ? m++ : m;
3361 n++;
3362 hmenu[n] = NULL;
3364 #if 0
3365 if (hp[i]->rav) {
3366 depth++;
3367 get_history_data(hp[i]->rav, &hmenu, m, turn);
3368 for (n = 0; hmenu[n]; n++);
3369 depth--;
3371 if (depth)
3372 m--;
3374 #endif
3376 turn = (turn == WHITE) ? BLACK : WHITE;
3379 *menu = hmenu;
3382 void history_draw_update(struct menu_input_s *m)
3384 GAME g = m->data;
3385 struct userdata_s *d = g->data;
3387 g->hindex = m->selected + 1;
3388 update_cursor(g, m->selected);
3389 pgn_board_update(g, d->b, m->selected + 1);
3392 struct menu_item_s **get_history_items(WIN *win)
3394 struct menu_input_s *m = win->data;
3395 GAME g = m->data;
3396 struct userdata_s *d = g->data;
3397 struct history_menu_s **hm = d->data;
3398 struct menu_item_s **items = m->items;
3399 int i;
3401 if (!hm) {
3402 get_history_data(g->history, &hm, 0,
3403 TEST_FLAG(g->flags, GF_BLACK_OPENING));
3404 m->selected = g->hindex - 1;
3406 if (m->selected < 0)
3407 m->selected = 0;
3409 m->draw_exit_func = history_draw_update;
3412 d->data = hm;
3414 if (items) {
3415 for (i = 0; items[i]; i++)
3416 free(items[i]);
3418 free(items);
3419 items = NULL;
3422 for (i = 0; hm[i]; i++) {
3423 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
3424 items[i] = Malloc(sizeof(struct menu_item_s));
3425 items[i]->name = hm[i]->line;
3426 items[i]->value = NULL;
3427 items[i]->selected = 0;
3430 if (items)
3431 items[i] = NULL;
3433 m->nofree = 1;
3434 m->items = items;
3435 return items;
3438 void history_menu_quit(struct menu_input_s *m)
3440 pushkey = -1;
3443 void history_menu_exit(WIN *win)
3445 GAME g = win->data;
3446 struct userdata_s *d = g->data;
3447 struct history_menu_s **hm = d->data;
3448 int i;
3450 if (!hm)
3451 return;
3453 for (i = 0; hm[i]; i++) {
3454 free(hm[i]->line);
3455 free(hm[i]);
3458 free(hm);
3459 d->data = NULL;
3462 // FIXME RAV
3463 void history_menu_next(struct menu_input_s *m)
3465 GAME g = m->data;
3466 struct userdata_s *d = g->data;
3467 struct history_menu_s **hm = d->data;
3468 int n, t;
3470 for (t = 0; hm[t]; t++);
3472 if (m->selected + 1 == t)
3473 n = 0;
3474 else
3475 n = hm[m->selected + 1]->hindex;
3477 n++;
3478 g->hindex = n;
3481 // FIXME RAV
3482 void history_menu_prev(struct menu_input_s *m)
3484 GAME g = m->data;
3485 struct userdata_s *d = g->data;
3486 struct history_menu_s **hm = d->data;
3487 int n, t;
3489 for (t = 0; hm[t]; t++);
3491 if (m->selected - 1 < 0)
3492 n = t - 1;
3493 else
3494 n = hm[m->selected - 1]->hindex;
3496 n++;
3497 g->hindex = n;
3500 void history_menu_help(struct menu_input_s *m)
3502 message(_("History Menu Help"), ANY_KEY_STR, "%s",
3504 " UP/DOWN - previous/next menu item\n"
3505 " HOME/END - first/last menu item\n"
3506 " PGDN/PGUP - next/previous page\n"
3507 " a-zA-Z0-9 - jump to item\n"
3508 " CTRL-a - annotate the selected move\n"
3509 " ENTER - view annotation\n"
3510 " CTRL-d - toggle board details\n"
3511 " ESCAPE/M - return to move history"
3515 void do_annotate_move(HISTORY *hp)
3517 char buf[COLS - 4];
3518 struct input_data_s *in;
3520 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Editing Annotation for"), hp->move);
3521 in = Calloc(1, sizeof(struct input_data_s));
3522 in->data = hp;
3523 in->efunc = do_annotate_finalize;
3524 construct_input(buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3525 _("Type CTRL-t to edit NAG"), edit_nag, NULL, CTRL_KEY('T'), in, -1, -1);
3528 void history_menu_view_annotation(struct menu_input_s *m)
3530 GAME g = m->data;
3532 // FIXME RAV
3533 view_annotation(g->history[m->selected]);
3536 void history_menu_annotate_finalize(WIN *win)
3538 struct input_data_s *in = win->data;
3539 GAME g = in->moredata;
3540 struct userdata_s *d = g->data;
3541 struct history_menu_s **hm = d->data;
3543 really_do_annotate_finalize(in, d);
3544 free_history_menu_data(hm);
3545 hm = NULL;
3546 get_history_data(g->history, &hm, 0, TEST_FLAG(g->flags, GF_BLACK_OPENING));
3547 d->data = hm;
3548 pushkey = REFRESH_MENU;
3551 void history_menu_annotate(struct menu_input_s *m)
3553 GAME g = m->data;
3554 char buf[COLS - 4];
3555 struct input_data_s *in;
3556 HISTORY *hp = g->history[m->selected]; // FIXME RAV
3558 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Editing Annotation for"), hp->move);
3559 in = Calloc(1, sizeof(struct input_data_s));
3560 in->data = hp;
3561 in->moredata = m->data;
3562 in->efunc = history_menu_annotate_finalize;
3563 construct_input(buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3564 _("Type CTRL-t to edit NAG"), edit_nag, NULL, CTRL_KEY('T'), in, -1, -1);
3567 void history_menu_details(struct menu_input_s *m)
3569 do_board_details();
3572 // FIXME RAV
3573 void history_menu_print(WIN *win)
3575 struct menu_input_s *m = win->data;
3576 GAME g = m->data;
3577 struct userdata_s *d = g->data;
3578 struct history_menu_s **hm = d->data;
3579 struct history_menu_s *h = hm[m->top];
3580 int i;
3581 char *p = m->item->name;
3582 int line = m->print_line - 2;
3584 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
3585 * attr_t data type.
3587 attr_t attrs;
3588 short pair;
3589 int total;
3591 for (total = 0; hm[total]; total++);
3592 wattr_get(win->w, &attrs, &pair, NULL);
3593 wattroff(win->w, COLOR_PAIR(pair));
3594 mvwaddch(win->w, m->print_line, 1,
3595 *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));
3596 p++;
3598 if (h->hindex == 0 && line == 0)
3599 waddch(win->w, ACS_ULCORNER | CP_HISTORY_MENU_LG);
3600 else if ((!hm[h->hindex + (win->rows - 5) + 1] && line == win->rows - 5) ||
3601 (m->top + line == total - 1))
3602 waddch(win->w, ACS_LLCORNER | CP_HISTORY_MENU_LG);
3603 else if (hm[m->top + 1]->ravlevel != h->ravlevel || !h->ravlevel)
3604 waddch(win->w, ACS_LTEE | CP_HISTORY_MENU_LG);
3605 else
3606 waddch(win->w, ACS_VLINE | CP_HISTORY_MENU_LG);
3608 wattron(win->w, COLOR_PAIR(pair) | attrs);
3610 for (i = 2; *p; p++, i++)
3611 waddch(win->w, (*p == '!') ? *p | A_BOLD : *p);
3613 while (i++ < win->cols - 2)
3614 waddch(win->w, ' ');
3617 void history_menu(GAME g)
3619 struct menu_key_s **keys = NULL;
3621 add_menu_key(&keys, KEY_ESCAPE, history_menu_quit);
3622 add_menu_key(&keys, 'M', history_menu_quit);
3623 add_menu_key(&keys, KEY_UP, history_menu_prev);
3624 add_menu_key(&keys, KEY_DOWN, history_menu_next);
3625 add_menu_key(&keys, KEY_F(1), history_menu_help);
3626 add_menu_key(&keys, CTRL_KEY('a'), history_menu_annotate);
3627 add_menu_key(&keys, CTRL_KEY('d'), history_menu_details);
3628 add_menu_key(&keys, '\n', history_menu_view_annotation);
3629 construct_menu(MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES,
3630 TAG_WIDTH, 0, config.boardleft ? BOARD_WIDTH : 0,
3631 _("Move History Tree"), 1, get_history_items, keys, g,
3632 history_menu_print, history_menu_exit);
3635 void do_history_menu()
3637 history_menu(gp);
3640 void do_history_half_move_toggle()
3642 movestep = (movestep == 1) ? 2 : 1;
3643 update_history_window(gp);
3646 void do_history_rotate_board()
3648 rotate = (rotate) ? FALSE : TRUE;
3651 void do_history_jump_next()
3653 struct userdata_s *d = gp->data;
3655 pgn_history_next(gp, d->b, (keycount > 0) ?
3656 config.jumpcount * keycount * movestep :
3657 config.jumpcount * movestep);
3660 void do_history_jump_prev()
3662 struct userdata_s *d = gp->data;
3664 pgn_history_prev(gp, d->b, (keycount) ?
3665 config.jumpcount * keycount * movestep :
3666 config.jumpcount * movestep);
3669 void do_history_prev()
3671 struct userdata_s *d = gp->data;
3673 pgn_history_prev(gp, d->b,
3674 (keycount) ? keycount * movestep : movestep);
3677 void do_history_next()
3679 struct userdata_s *d = gp->data;
3681 pgn_history_next(gp, d->b, (keycount) ?
3682 keycount * movestep : movestep);
3685 void do_history_mode_finalize(struct userdata_s *d)
3687 pushkey = 0;
3688 d->mode = MODE_PLAY;
3691 void do_history_mode_confirm(WIN *win)
3693 struct userdata_s *d = gp->data;
3694 wchar_t str[] = { win->c, 0 };
3696 if (!wcscmp (str, resume_wchar)) {
3697 pgn_history_free(gp->hp, gp->hindex);
3698 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3700 #if 0
3701 case 'C':
3702 case 'c':
3703 if (pgn_history_rav_new(gp, d->b,
3704 gp->hindex) != E_PGN_OK)
3705 return;
3707 break;
3708 #endif
3709 else
3710 return;
3712 if (!TEST_FLAG(d->flags, CF_HUMAN)) {
3713 char *fen = pgn_game_to_fen(gp, d->b);
3715 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
3716 free (fen);
3719 do_history_mode_finalize(d);
3722 void do_history_toggle()
3724 struct userdata_s *d = gp->data;
3726 // FIXME Resuming from previous history could append to a RAV.
3727 if (gp->hindex != pgn_history_total(gp->hp)) {
3728 if (!pushkey)
3729 construct_message(NULL, _ ("What would you like to do?"), 0, 1,
3730 NULL, NULL, NULL, do_history_mode_confirm, 0, 0,
3731 _("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."),
3732 resume_wchar);
3733 return;
3735 else {
3736 if (TEST_FLAG(gp->flags, GF_GAMEOVER))
3737 return;
3740 if (gp->side != gp->turn) {
3741 d->play_mode = PLAY_EH;
3743 else {
3744 d->play_mode = PLAY_HE;
3745 rotate = FALSE;
3748 do_history_mode_finalize(d);
3751 void do_history_annotate()
3753 int n = gp->hindex;
3755 if (n && gp->hp[n - 1]->move)
3756 n--;
3757 else
3758 return;
3760 do_annotate_move(gp->hp[n]);
3763 void do_history_help()
3765 wchar_t *buf = build_help(history_keys);
3767 construct_message(_("History Mode Keys (* = can take a repeat count)"),
3768 ANY_KEY_STR, 0, 0, NULL, NULL,
3769 buf, do_more_help, 0, 1, "%ls", buf);
3772 void do_history_find(int which)
3774 struct input_data_s *in;
3775 int *p;
3777 if (pgn_history_total(gp->hp) < 2)
3778 return;
3780 in = Calloc(1, sizeof(struct input_data_s));
3781 p = Malloc(sizeof(int));
3782 *p = which;
3783 in->data = p;
3784 in->efunc = do_find_move_exp;
3786 if (!*moveexp || which == 0) {
3787 construct_input(_("Find Move Text Expression"), NULL, 1, 0, NULL, NULL, NULL,
3788 0, in, INPUT_HIST_MOVE_EXP, -1);
3789 return;
3792 do_find_move_exp_finalize(0, which);
3795 void do_history_find_new()
3797 do_history_find(0);
3800 void do_history_find_prev()
3802 do_history_find(-1);
3805 void do_history_find_next()
3807 do_history_find(1);
3810 void do_history_rav(int which)
3812 struct userdata_s *d = gp->data;
3814 rav_next_prev(gp, d->b, which);
3817 void do_history_rav_next()
3819 do_history_rav(1);
3822 void do_history_rav_prev()
3824 do_history_rav(0);
3827 void do_history_jump()
3829 struct input_data_s *in;
3831 if (pgn_history_total(gp->hp) < 2)
3832 return;
3834 if (!keycount) {
3835 in = Calloc(1, sizeof(struct input_data_s));
3836 in->efunc = do_move_jump;
3838 construct_input(_("Jump to Move Number"), NULL, 1, 1, NULL,
3839 NULL, NULL, 0, in, -1, 0);
3840 return;
3843 do_move_jump_finalize(keycount);
3846 static void free_userdata_once(GAME g)
3848 struct userdata_s *d = g->data;
3850 if (!d)
3851 return;
3853 if (d->engine) {
3854 stop_engine(g);
3856 if (d->engine->enginebuf) {
3857 int n;
3859 for (n = 0; d->engine->enginebuf[n]; n++)
3860 free(d->engine->enginebuf[n]);
3862 free(d->engine->enginebuf);
3865 if (d->engine->queue) {
3866 struct queue_s **q;
3868 for (q = d->engine->queue; *q; q++)
3869 free(*q);
3871 free(d->engine->queue);
3874 free(d->engine);
3877 #ifdef WITH_LIBPERL
3878 if (d->perlfen)
3879 free(d->perlfen);
3881 if (d->oldfen)
3882 free(d->oldfen);
3883 #endif
3885 free(d);
3886 g->data = NULL;
3889 static void free_userdata()
3891 int i;
3893 for (i = 0; i < gtotal; i++) {
3894 free_userdata_once(game[i]);
3895 game[i]->data = NULL;
3899 void update_loading_window(int n)
3901 char buf[16];
3903 if (!loadingw) {
3904 loadingw = newwin(3, COLS / 2, CALCPOSY(3), CALCPOSX(COLS / 2));
3905 loadingp = new_panel(loadingw);
3906 wbkgd(loadingw, CP_MESSAGE_WINDOW);
3909 wmove(loadingw, 0, 0);
3910 wclrtobot(loadingw);
3911 wattron(loadingw, CP_MESSAGE_BORDER);
3912 box(loadingw, ACS_VLINE, ACS_HLINE);
3913 wattroff(loadingw, CP_MESSAGE_BORDER);
3914 mvwprintw(loadingw, 1, CENTER_INT((COLS / 2), 11 +
3915 strlen(itoa(gtotal, buf))),
3916 _("Loading... %i%% (%i games)"), n, gtotal);
3917 update_panels();
3918 doupdate();
3921 static void init_userdata_once(GAME g, int n)
3923 struct userdata_s *d = NULL;
3925 d = Calloc(1, sizeof(struct userdata_s));
3926 d->n = n;
3927 d->c_row = 2, d->c_col = 5;
3928 SET_FLAG(d->flags, CF_NEW);
3929 g->data = d;
3931 if (pgn_board_init_fen(g, d->b, NULL) != E_PGN_OK)
3932 pgn_board_init(d->b);
3935 void init_userdata()
3937 int i;
3939 for (i = 0; i < gtotal; i++)
3940 init_userdata_once(game[i], i);
3943 void fix_marks(int *start, int *end)
3945 int i;
3947 *start = (*start < 0) ? 0 : *start;
3948 *end = (*end < 0) ? 0 : *end;
3950 if (*start > *end) {
3951 i = *start;
3952 *start = *end;
3953 *end = i + 1;
3956 *end = (*end > gtotal) ? gtotal : *end;
3959 void do_new_game_finalize(GAME g)
3961 struct userdata_s *d = g->data;
3963 d->mode = MODE_PLAY;
3964 update_status_notify(g, NULL);
3967 void do_new_game_from_scratch(WIN *win)
3969 wchar_t str[] = { win->c, 0 };
3971 if (wcscmp (str, yes_wchar))
3972 return;
3974 stop_clock();
3975 free_userdata();
3976 pgn_parse(NULL);
3977 gp = game[gindex];
3978 add_custom_tags(&gp->tag);
3979 init_userdata();
3980 loadfile[0] = 0;
3981 do_new_game_finalize(gp);
3982 rotate = FALSE;
3985 void do_new_game()
3987 pgn_new_game();
3988 gp = game[gindex];
3989 add_custom_tags(&gp->tag);
3990 init_userdata_once(gp, gindex);
3991 do_new_game_finalize(gp);
3992 rotate = FALSE;
3995 void do_game_delete_finalize(int n)
3997 struct userdata_s *d;
3999 delete_game((!n) ? gindex : -1);
4000 d = gp->data;
4001 if (d->mode != MODE_EDIT)
4002 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4005 void do_game_delete_confirm(WIN *win)
4007 int *n;
4008 wchar_t str[] = { win->c, 0 };
4010 if (wcscmp (str, yes_wchar)) {
4011 free(win->data);
4012 return;
4015 n = (int *)win->data;
4016 do_game_delete_finalize(*n);
4017 free(win->data);
4020 void do_game_delete()
4022 char *tmp = NULL;
4023 int i, n;
4024 struct userdata_s *d;
4025 int *p;
4027 if (gtotal < 2) {
4028 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4029 return;
4032 tmp = NULL;
4034 for (i = n = 0; i < gtotal; i++) {
4035 d = game[i]->data;
4037 if (TEST_FLAG(d->flags, CF_DELETE))
4038 n++;
4041 if (!n)
4042 tmp = _("Delete the current game?");
4043 else {
4044 if (n == gtotal) {
4045 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
4046 return;
4049 tmp = _("Delete all games marked for deletion?");
4052 if (config.deleteprompt) {
4053 p = Malloc(sizeof(int));
4054 *p = n;
4055 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, p,
4056 do_game_delete_confirm, 0, 0, tmp);
4057 return;
4060 do_game_delete_finalize(n);
4063 void do_find_game_exp_finalize(int which)
4065 struct userdata_s *d = gp->data;
4066 int n;
4068 if ((n = find_game_exp(gameexp, (which == -1) ? 0 : 1,
4069 (keycount) ? keycount : 1)) == -1) {
4070 update_status_notify(gp, "%s", _("No matches found"));
4071 return;
4074 gindex = n;
4075 d = gp->data;
4077 if (pgn_history_total(gp->hp))
4078 d->mode = MODE_HISTORY;
4080 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4083 void do_find_game_exp(WIN *win)
4085 struct input_data_s *in = win->data;
4086 int *n = in->data;
4087 int c = *n;
4089 if (in->str) {
4090 strncpy(gameexp, in->str, sizeof(gameexp));
4091 gameexp[sizeof(gameexp)-1] = 0;
4093 if (c == '?')
4094 c = '}';
4096 do_find_game_exp_finalize(c);
4097 free(in->str);
4100 free(in->data);
4101 free(in);
4104 void do_game_jump_finalize(int n)
4106 struct userdata_s *d;
4108 if (--n > gtotal - 1 || n < 0)
4109 return;
4111 gindex = n;
4112 gp = game[gindex];
4113 d = gp->data;
4114 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4115 update_status_notify(gp, NULL);
4118 void do_game_jump(WIN *win)
4120 struct input_data_s *in = win->data;
4122 if (!in->str || !isinteger(in->str)) {
4123 if (in->str)
4124 free(in->str);
4126 free(in);
4127 return;
4130 do_game_jump_finalize(atoi(in->str));
4131 free(in->str);
4132 free(in);
4135 void do_load_file(WIN *win)
4137 struct input_data_s *in = win->data;
4138 char *tmp = in->str;
4139 struct userdata_s *d;
4140 PGN_FILE *pgn = NULL;
4141 int n;
4143 if (!in->str) {
4144 free(in);
4145 return;
4148 if ((tmp = pathfix(tmp)) == NULL)
4149 goto done;
4151 n = pgn_open(tmp, "r", &pgn);
4153 if (n == E_PGN_ERR) {
4154 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, strerror(errno));
4155 goto done;
4157 else if (n == E_PGN_INVALID) {
4158 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, _("Not a regular file"));
4159 goto done;
4162 free_userdata();
4164 if (pgn_parse(pgn) == E_PGN_ERR) {
4165 del_panel(loadingp);
4166 delwin(loadingw);
4167 loadingw = NULL;
4168 loadingp = NULL;
4169 init_userdata();
4170 goto done;
4173 del_panel(loadingp);
4174 delwin(loadingw);
4175 loadingw = NULL;
4176 loadingp = NULL;
4177 init_userdata();
4178 strncpy(loadfile, tmp, sizeof(loadfile));
4179 loadfile[sizeof(loadfile)-1] = 0;
4180 gp = game[gindex];
4181 d = gp->data;
4183 if (pgn_history_total(gp->hp))
4184 d->mode = MODE_HISTORY;
4186 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4188 fm_loaded_file = TRUE;
4190 done:
4191 pgn_close(pgn);
4193 if (in->str)
4194 free(in->str);
4196 free(in);
4199 void do_game_save(WIN *win)
4201 struct input_data_s *in = win->data;
4202 int *x = in->data;
4203 int n = *x;
4204 char *tmp = in->str;
4205 char tfile[FILENAME_MAX];
4206 char *p;
4207 int i;
4208 struct userdata_s *d;
4210 if (!tmp || (tmp = pathfix(tmp)) == NULL)
4211 goto done;
4213 if (pgn_is_compressed(tmp) == E_PGN_ERR) {
4214 p = tmp + strlen(tmp) - 1;
4216 if (*p != 'n' || *(p-1) != 'g' || *(p-2) != 'p' ||
4217 *(p-3) != '.') {
4218 snprintf(tfile, sizeof(tfile), "%s.pgn", tmp);
4219 tmp = tfile;
4224 * When in edit mode, update the FEN tag.
4226 if (n == -1) {
4227 for (i = 0; i < gtotal; i++) {
4228 d = game[i]->data;
4230 if (d->mode == MODE_EDIT) {
4231 char *fen = pgn_game_to_fen(game[i], d->b);
4233 pgn_tag_add(&game[i]->tag, "FEN", fen);
4234 free (fen);
4238 else {
4239 d = game[n]->data;
4241 if (d->mode == MODE_EDIT) {
4242 char *fen = pgn_game_to_fen(game[n], d->b);
4244 pgn_tag_add(&game[n]->tag, "FEN", fen);
4245 free (fen);
4249 save_pgn(tmp, n);
4251 done:
4252 if (in->str)
4253 free(in->str);
4255 free(in->data);
4256 free(in);
4259 void do_get_game_save_input(int n)
4261 struct input_data_s *in = Calloc(1, sizeof(struct input_data_s));
4262 int *p = Malloc(sizeof(int));
4264 in->efunc = do_game_save;
4265 *p = n;
4266 in->data = p;
4268 construct_input(_("Save Game Filename"), loadfile, 1, 1, _("Type TAB for file browser"),
4269 file_browser, NULL, '\t', in, INPUT_HIST_FILE, -1);
4272 void do_game_save_multi_confirm(WIN *win)
4274 int i;
4275 wchar_t str[] = { win->c, 0 };
4277 if (!wcscmp (str, current_wchar))
4278 i = gindex;
4279 else if (!wcscmp (str, all_wchar))
4280 i = -1;
4281 else {
4282 update_status_notify(gp, "%s", _("Save game aborted."));
4283 return;
4286 do_get_game_save_input(i);
4289 void do_global_about()
4291 cmessage(_("ABOUT"), ANY_KEY_STR,
4292 _("%s\nUsing %s with %i colors and %i color pairs\n%s"),
4293 PACKAGE_STRING, curses_version(), COLORS, COLOR_PAIRS,
4294 COPYRIGHT);
4297 void global_game_next_prev(int which)
4299 struct userdata_s *d;
4301 game_next_prev(gp, (which == 1) ? 1 : 0,
4302 (keycount) ? keycount : 1);
4303 d = gp->data;
4305 if (delete_count) {
4306 if (which == 1) {
4307 markend = markstart + delete_count;
4308 delete_count = 0;
4310 else {
4311 markend = markstart - delete_count + 1;
4312 delete_count = -1; // to fix gindex in the other direction
4315 fix_marks(&markstart, &markend);
4316 do_global_toggle_delete();
4319 if (d->mode == MODE_HISTORY)
4320 pgn_board_update(gp, d->b, gp->hindex);
4321 else if (d->mode == MODE_PLAY)
4322 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4325 void do_global_next_game()
4327 global_game_next_prev(1);
4330 void do_global_prev_game()
4332 global_game_next_prev(0);
4335 void global_find(int which)
4337 struct input_data_s *in;
4338 int *p;
4340 if (gtotal < 2)
4341 return;
4343 in = Calloc(1, sizeof(struct input_data_s));
4344 p = Malloc(sizeof(int));
4345 *p = which;
4346 in->data = p;
4347 in->efunc = do_find_game_exp;
4349 if (!*gameexp || which == 0) {
4350 construct_input(_("Find Game by Tag Expression"), NULL, 1, 0,
4351 _("[name expression:]value expression"), NULL, NULL, 0, in,
4352 INPUT_HIST_GAME_EXP, -1);
4353 return;
4356 do_find_game_exp_finalize(which);
4359 void do_global_find_new()
4361 global_find(0);
4364 void do_global_find_next()
4366 global_find(1);
4369 void do_global_find_prev()
4371 global_find(-1);
4374 void do_global_game_jump()
4376 if (gtotal < 2)
4377 return;
4379 if (!keycount) {
4380 struct input_data_s *in;
4382 in = Calloc(1, sizeof(struct input_data_s));
4383 in->efunc = do_game_jump;
4384 construct_input(_("Jump to Game Number"), NULL, 1, 1, NULL, NULL, NULL, 0, in,
4385 -1, 0);
4386 return;
4389 do_game_jump_finalize(keycount);
4392 void do_global_toggle_delete()
4394 int i;
4396 pushkey = 0;
4398 if (gtotal < 2)
4399 return;
4401 if (keycount && delete_count == 0) {
4402 markstart = gindex;
4403 delete_count = keycount;
4404 update_status_notify(gp, "%s (delete)", status.notify);
4405 return;
4408 if (markstart >= 0 && markend >= 0) {
4409 for (i = markstart; i < markend; i++) {
4410 if (toggle_delete_flag(i)) {
4411 return;
4415 gindex = (delete_count < 0) ? markstart : i - 1;
4417 else {
4418 if (toggle_delete_flag(gindex))
4419 return;
4422 markstart = markend = -1;
4423 delete_count = 0;
4424 update_status_window(gp);
4427 void do_global_delete_game()
4429 do_game_delete();
4432 void do_global_tag_edit()
4434 struct userdata_s *d = gp->data;
4436 edit_tags(gp, d->b, 1);
4439 void do_global_tag_view()
4441 struct userdata_s *d = gp->data;
4443 edit_tags(gp, d->b, 0);
4446 void do_global_resume_game()
4448 struct input_data_s *in;
4450 in = Calloc(1, sizeof(struct input_data_s));
4451 in->efunc = do_load_file;
4452 construct_input(_("Load Filename"), NULL, 1, 1, _("Type TAB for file browser"), file_browser,
4453 NULL, '\t', in, INPUT_HIST_FILE, -1);
4456 void do_global_save_game()
4458 if (gtotal > 1) {
4459 construct_message(NULL, _("What would you like to do?"), 0, 1,
4460 NULL, NULL, NULL, do_game_save_multi_confirm, 0, 0,
4461 _("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."),
4462 current_wchar, all_wchar);
4463 return;
4466 do_get_game_save_input(-1);
4469 void do_global_new_game()
4471 do_new_game();
4474 void do_global_copy_game()
4476 int g = gindex;
4477 int i, n;
4478 struct userdata_s *d;
4480 do_global_new_game();
4481 d = gp->data;
4482 n = pgn_tag_total(game[g]->tag);
4484 for (i = 0; i < n; i++)
4485 pgn_tag_add(&gp->tag, game[g]->tag[i]->name,
4486 game[g]->tag[i]->value);
4488 pgn_board_init_fen (gp, d->b, NULL);
4489 n = pgn_history_total(game[g]->history);
4491 // FIXME RAV
4492 for (i = 0; i < n; i++) {
4493 char *frfr = NULL;
4494 char move[MAX_SAN_MOVE_LEN+1] = {0}, *m = move;
4496 strcpy (move, game[g]->history[i]->move);
4497 if (pgn_parse_move(gp, d->b, &m, &frfr) != E_PGN_OK) {
4498 SET_FLAG(gp->flags, GF_PERROR);
4499 return;
4502 pgn_history_add(gp, d->b, move);
4503 free(frfr);
4504 pgn_switch_turn(gp);
4507 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4510 void do_global_new_all()
4512 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
4513 do_new_game_from_scratch, 0, 0, "%s", _("Really start a new game from scratch?"));
4516 void do_quit(WIN *win)
4518 wchar_t str[] = { win->c, 0 };
4520 if (wcscmp (str, yes_wchar))
4521 return;
4523 quit = 1;
4526 void do_global_quit()
4528 if (config.exitdialogbox)
4529 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
4530 do_quit, 0, 0, _("Want to Quit?"));
4531 else
4532 quit = 1;
4535 void do_global_toggle_engine_window()
4537 if (!enginew) {
4538 enginew = newwin(LINES, COLS, 0, 0);
4539 enginep = new_panel(enginew);
4540 window_draw_title(enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
4541 CP_MESSAGE_BORDER);
4542 hide_panel(enginep);
4545 if (panel_hidden(enginep)) {
4546 update_engine_window(gp);
4547 top_panel(enginep);
4549 else {
4550 hide_panel(enginep);
4554 void do_global_toggle_board_details()
4556 do_board_details();
4559 void do_global_toggle_strict_castling()
4561 do_toggle_strict_castling();
4564 // Global and other keys.
4565 static int globalkeys()
4567 struct userdata_s *d = gp->data;
4568 int i;
4571 * These cannot be modified and other game mode keys cannot conflict with
4572 * these.
4574 switch (input_c) {
4575 case KEY_ESCAPE:
4576 d->sp.icon = d->sp.srow = d->sp.scol = 0;
4577 markend = markstart = 0;
4579 if (keycount) {
4580 keycount = 0;
4581 update_status_notify(gp, NULL);
4584 if (config.validmoves)
4585 pgn_reset_valid_moves(d->b);
4587 return 1;
4588 case '0' ... '9':
4589 i = input_c - '0';
4591 if (keycount)
4592 keycount = keycount * 10 + i;
4593 else
4594 keycount = i;
4596 update_status_notify(gp, _("Repeat %i"), keycount);
4597 return -1;
4598 case KEY_UP:
4599 if (d->mode == MODE_HISTORY)
4600 return 0;
4602 if (keycount)
4603 d->c_row += keycount;
4604 else
4605 d->c_row++;
4607 if (d->c_row > 8)
4608 d->c_row = 1;
4610 return 1;
4611 case KEY_DOWN:
4612 if (d->mode == MODE_HISTORY)
4613 return 0;
4615 if (keycount) {
4616 d->c_row -= keycount;
4617 update_status_notify(gp, NULL);
4619 else
4620 d->c_row--;
4622 if (d->c_row < 1)
4623 d->c_row = 8;
4625 return 1;
4626 case KEY_LEFT:
4627 if (d->mode == MODE_HISTORY)
4628 return 0;
4630 if (keycount)
4631 d->c_col -= keycount;
4632 else
4633 d->c_col--;
4635 if (d->c_col < 1)
4636 d->c_col = 8;
4638 return 1;
4639 case KEY_RIGHT:
4640 if (d->mode == MODE_HISTORY)
4641 return 0;
4643 if (keycount)
4644 d->c_col += keycount;
4645 else
4646 d->c_col++;
4648 if (d->c_col > 8)
4649 d->c_col = 1;
4651 return 1;
4652 case KEY_RESIZE:
4653 return 1;
4654 case 0:
4655 default:
4656 for (i = 0; global_keys[i]; i++) {
4657 if (input_c == global_keys[i]->c) {
4658 (*global_keys[i]->f)();
4659 return 1;
4662 break;
4665 return 0;
4668 #ifdef WITH_LIBPERL
4669 static void perl_error(const char *fmt, ...)
4671 va_list ap;
4672 char *buf;
4674 va_start(ap, fmt);
4675 vasprintf(&buf, fmt, ap);
4676 va_end(ap);
4678 message(ERROR_STR, ANY_KEY_STR, "%s", buf);
4679 free(buf);
4682 static void do_perl_finalize(WIN *win)
4684 struct input_data_s *in = win->data;
4685 GAME g = in->data;
4686 struct userdata_s *d = g->data;
4687 char *filename;
4688 char *result = NULL;
4689 char *arg = NULL;
4690 int n;
4692 asprintf(&filename, "%s/perl.pl", config.datadir);
4694 if (!in->str)
4695 goto done;
4697 if (perl_init_file(filename, perl_error))
4698 goto done;
4700 arg = pgn_game_to_fen(g, d->b);
4702 if (perl_call_sub(trim(in->str), arg, &result))
4703 goto done;
4705 d->perlfen = pgn_game_to_fen(g, d->b);
4706 d->perlflags = g->flags;
4708 if (pgn_board_init_fen(g, d->b, result) != E_PGN_OK) {
4709 message(ERROR_STR, ANY_KEY_STR, "%s", _("FEN parse error."));
4710 pgn_board_init_fen(g, d->b, d->perlfen);
4711 g->flags = d->perlflags;
4712 free(d->perlfen);
4713 d->perlfen = NULL;
4714 goto done;
4717 SET_FLAG(d->flags, CF_PERL);
4718 n = pgn_tag_find(g->tag, "FEN");
4720 if (n != E_PGN_ERR)
4721 d->oldfen = strdup(g->tag[n]->value);
4723 pgn_tag_add(&g->tag, "FEN", result);
4724 update_status_notify(g, "%s", ANY_KEY_STR);
4725 update_all(g);
4727 done:
4728 free(result);
4729 free(arg);
4730 free(in->str);
4731 free(in);
4732 free(filename);
4735 void do_global_perl()
4737 struct input_data_s *in;
4739 in = Calloc(1, sizeof(struct input_data_s));
4740 in->data = gp;
4741 in->efunc = do_perl_finalize;
4742 construct_input(_("PERL Subroutine Filter"), NULL, 1, 0, NULL, NULL, NULL, 0, in, INPUT_HIST_PERL, -1);
4744 #endif
4747 * A macro may contain a key that belongs to another macro so macro_match will
4748 * need to be updated to the new index of the matching macro.
4750 static void find_macro(struct userdata_s *d)
4752 int i;
4755 * Macros can't contain macros when in a window.
4757 if (wins)
4758 return;
4760 again:
4761 for (i = 0; macros[i]; i++) {
4762 if ((macros[i]->mode == -1 || macros[i]->mode == d->mode) &&
4763 input_c == macros[i]->c) {
4764 input_c = macros[i]->keys[macros[i]->n++];
4766 if (!macro_depth_n && macro_match > -1) {
4767 macro_depth = realloc(macro_depth, (macro_depth_n + 1) * sizeof(int));
4768 macro_depth[macro_depth_n++] = macro_match;
4771 macro_depth = realloc(macro_depth, (macro_depth_n + 1) * sizeof(int));
4772 macro_depth[macro_depth_n++] = i;
4773 macro_match = i;
4774 goto again;
4780 * Resets the position in each macro to the first key.
4782 static void reset_macros()
4784 int i;
4785 struct userdata_s *d = gp->data;
4787 again:
4788 if (macro_depth_n > 0) {
4789 macro_depth_n--;
4790 macro_match = macro_depth[macro_depth_n];
4792 if (macros[macro_match]->n >= macros[macro_match]->total)
4793 goto again;
4795 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
4796 find_macro(d);
4797 return;
4800 for (i = 0; macros[i]; i++)
4801 macros[i]->n = 0;
4803 free(macro_depth);
4804 macro_depth = NULL;
4805 macro_depth_n = 0;
4806 macro_match = -1;
4809 void game_loop()
4811 struct userdata_s *d;
4813 macro_match = -1;
4814 gindex = gtotal - 1;
4815 gp = game[gindex];
4816 d = gp->data;
4818 if (pgn_history_total(gp->hp))
4819 d->mode = MODE_HISTORY;
4820 else {
4821 d->mode = MODE_PLAY;
4822 d->play_mode = PLAY_HE;
4825 if (d->mode == MODE_HISTORY)
4826 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4828 update_status_notify(gp, "%s", _("Type F1 for help"));
4829 movestep = 2;
4830 flushinp();
4831 update_all(gp);
4832 wtimeout(boardw, WINDOW_TIMEOUT);
4834 while (!quit) {
4835 int n = 0, i;
4836 char fdbuf[8192] = {0};
4837 int len;
4838 struct timeval tv = {0, 0};
4839 fd_set rfds, wfds;
4840 WIN *win = NULL;
4841 WINDOW *wp = NULL;
4843 FD_ZERO(&rfds);
4844 FD_ZERO(&wfds);
4846 for (i = 0; i < gtotal; i++) {
4847 d = game[i]->data;
4849 if (d->engine && d->engine->pid != -1) {
4850 if (d->engine->fd[ENGINE_IN_FD] > 2) {
4851 if (d->engine->fd[ENGINE_IN_FD] > n)
4852 n = d->engine->fd[ENGINE_IN_FD];
4854 FD_SET(d->engine->fd[ENGINE_IN_FD], &rfds);
4857 if (d->engine->fd[ENGINE_OUT_FD] > 2) {
4858 if (d->engine->fd[ENGINE_OUT_FD] > n)
4859 n = d->engine->fd[ENGINE_OUT_FD];
4861 FD_SET(d->engine->fd[ENGINE_OUT_FD], &wfds);
4866 if (n) {
4867 if ((n = select(n + 1, &rfds, &wfds, NULL, &tv)) > 0) {
4868 for (i = 0; i < gtotal; i++) {
4869 d = game[i]->data;
4871 if (d->engine && d->engine->pid != -1) {
4872 if (FD_ISSET(d->engine->fd[ENGINE_IN_FD], &rfds)) {
4873 len = read(d->engine->fd[ENGINE_IN_FD], fdbuf,
4874 sizeof(fdbuf));
4876 if (len > 0) {
4877 if (d->engine->iobuf)
4878 d->engine->iobuf = Realloc(d->engine->iobuf, d->engine->len + len + 1);
4879 else
4880 d->engine->iobuf = Calloc(1, len + 1);
4882 memcpy(&(d->engine->iobuf[d->engine->len]), &fdbuf, len);
4883 d->engine->len += len;
4884 d->engine->iobuf[d->engine->len] = 0;
4887 * The fdbuf is full or no newline
4888 * was found. So we'll append the next
4889 * read() to this games buffer.
4891 if (d->engine->iobuf[d->engine->len - 1] != '\n')
4892 continue;
4894 parse_engine_output(game[i], d->engine->iobuf);
4895 free(d->engine->iobuf);
4896 d->engine->iobuf = NULL;
4897 d->engine->len = 0;
4899 else if (len == -1) {
4900 if (errno != EAGAIN) {
4901 cmessage(ERROR_STR, ANY_KEY_STR, "Engine read(): %s",
4902 strerror(errno));
4903 waitpid(d->engine->pid, &n, 0);
4904 free(d->engine);
4905 d->engine = NULL;
4906 break;
4911 if (FD_ISSET(d->engine->fd[ENGINE_OUT_FD], &wfds)) {
4912 if (d->engine->queue)
4913 send_engine_command(game[i]);
4918 else {
4919 if (n == -1)
4920 cmessage(ERROR_STR, ANY_KEY_STR, "select(): %s", strerror(errno));
4921 /* timeout */
4925 gp = game[gindex];
4926 d = gp->data;
4929 * This is needed to detect terminal resizing.
4931 doupdate();
4932 if (LINES != LINES_OLD || COLS != COLS_OLD) {
4933 COLS_OLD = COLS;
4934 LINES_OLD = LINES;
4935 do_window_resize();
4939 * Finds the top level window in the window stack so we know what
4940 * window the wget_wch()'ed key belongs to.
4942 if (wins) {
4943 for (i = 0; wins[i]; i++);
4944 win = wins[i-1];
4945 wp = win->w;
4946 wtimeout(wp, WINDOW_TIMEOUT);
4948 else
4949 wp = boardw;
4951 if (!i && pushkey)
4952 input_c = pushkey;
4953 else {
4954 if (!pushkey) {
4955 if (macros && macro_match >= 0) {
4956 if (macros[macro_match]->n >= macros[macro_match]->total)
4957 reset_macros();
4958 else {
4959 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
4960 find_macro(d);
4963 else {
4964 if (wget_wch(wp, &input_c) == ERR || input_c == KEY_RESIZE)
4965 continue;
4968 else
4969 input_c = pushkey;
4971 if (win) {
4972 switch (input_c) {
4973 case CTRL_KEY('L'):
4974 do_window_resize ();
4975 goto refresh;
4978 win->c = input_c;
4981 * Run the function associated with the window. When the
4982 * function returns 0 win->efunc is ran (if not NULL) with
4983 * win as the one and only parameter. Then the window is
4984 * destroyed.
4986 * The exit function may create another window which will
4987 * mess up the window stack when window_destroy() is called.
4988 * So don't destory the window until the top window is
4989 * destroyable. See window_destroy().
4991 if ((*win->func)(win) == 0) {
4992 if (win->efunc)
4993 (*win->efunc)(win);
4995 win->keep = 1;
4996 window_destroy(win);
4997 update_all(gp);
5000 continue;
5004 if (!keycount && status.notify)
5005 update_status_notify(gp, NULL);
5007 #ifdef WITH_LIBPERL
5008 if (TEST_FLAG(d->flags, CF_PERL)) {
5009 CLEAR_FLAG(d->flags, CF_PERL);
5010 pgn_board_init_fen(gp, d->b, d->perlfen);
5011 gp->flags = d->perlflags;
5012 free(d->perlfen);
5013 pgn_tag_add(&gp->tag, "FEN", d->oldfen);
5014 free(d->oldfen);
5015 d->perlfen = d->oldfen = NULL;
5016 update_all(gp);
5017 continue;
5019 #endif
5021 if (macros && macro_match < 0)
5022 find_macro(d);
5024 if ((n = globalkeys()) == 1) {
5025 if (macro_match == -1)
5026 keycount = 0;
5028 goto refresh;
5030 else if (n == -1)
5031 goto refresh;
5033 switch (d->mode) {
5034 case MODE_EDIT:
5035 for (i = 0; edit_keys[i]; i++) {
5036 if (input_c == edit_keys[i]->c) {
5037 (*edit_keys[i]->f)();
5038 break;
5041 break;
5042 case MODE_PLAY:
5043 for (i = 0; play_keys[i]; i++) {
5044 if (input_c == play_keys[i]->c) {
5045 (*play_keys[i]->f)();
5046 goto done;
5050 do_play_config_command();
5051 break;
5052 case MODE_HISTORY:
5053 for (i = 0; history_keys[i]; i++) {
5054 if (input_c == history_keys[i]->c) {
5055 (*history_keys[i]->f)();
5056 break;
5059 break;
5060 default:
5061 break;
5064 done:
5065 if (keycount)
5066 update_status_notify(gp, NULL);
5068 keycount = 0;
5070 refresh:
5071 update_all(gp);
5075 void usage(const char *pn, int ret)
5077 fprintf((ret) ? stderr : stdout, "%s%s",
5078 #ifdef DEBUG
5080 "Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
5081 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"),
5082 #else
5084 "Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"),
5085 #endif
5087 " -p Load PGN file.\n"
5088 " -V Validate a game file.\n"
5089 " -S Validate and output a PGN formatted game.\n"
5090 " -R Like -S but write a reduced PGN formatted game.\n"
5091 " -t Also write custom PGN tags from config file.\n"
5092 " -E Stop processing on file parsing error (overrides config).\n"
5093 " -C Enable strict castling (overrides config).\n"
5094 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, overrides config).\n"
5095 " -v Version information.\n"
5096 " -h This help text.\n"));
5098 exit(ret);
5101 void cleanup_all()
5103 int i;
5105 stop_clock();
5106 free_userdata();
5107 pgn_free_all();
5108 free(config.engine_cmd);
5109 free(config.pattern);
5110 free(config.ccfile);
5111 free(config.nagfile);
5112 free(config.configfile);
5114 if (config.keys) {
5115 for (i = 0; config.keys[i]; i++) {
5116 free(config.keys[i]->str);
5117 free(config.keys[i]);
5120 free(config.keys);
5123 if (config.einit) {
5124 for (i = 0; config.einit[i]; i++)
5125 free(config.einit[i]);
5127 free(config.einit);
5130 if (config.tag)
5131 pgn_tag_free(config.tag);
5133 free(config.datadir);
5135 if (curses_initialized) {
5136 del_panel(boardp);
5137 del_panel(historyp);
5138 del_panel(statusp);
5139 del_panel(tagp);
5140 delwin(boardw);
5141 delwin(historyw);
5142 delwin(statusw);
5143 delwin(tagw);
5145 if (enginew) {
5146 del_panel(enginep);
5147 delwin(enginew);
5150 endwin();
5153 #ifdef WITH_LIBPERL
5154 perl_cleanup();
5155 #endif
5158 static void signal_save_pgn(int sig)
5160 char *buf;
5161 time_t now;
5162 char *p = config.savedirectory ? config.savedirectory : config.datadir;
5164 time(&now);
5165 asprintf(&buf, "%s/signal-%i-%li.pgn", p, sig, now);
5167 if (do_game_write(buf, "w", 0, gtotal)) {
5168 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", p, strerror(errno));
5169 update_status_notify(gp, "%s", _("Save game failed."));
5172 free(buf);
5173 quit = 1;
5176 void catch_signal(int which)
5178 switch (which) {
5179 case SIGALRM:
5180 update_clocks();
5181 break;
5182 case SIGPIPE:
5183 if (which == SIGPIPE && quit)
5184 break;
5186 if (which == SIGPIPE)
5187 cmessage(NULL, ANY_KEY_STR, "%s", _("Broken pipe. Quitting."));
5189 cleanup_all();
5190 exit(EXIT_FAILURE);
5191 break;
5192 case SIGSTOP:
5193 savetty();
5194 break;
5195 case SIGCONT:
5196 resetty();
5197 do_window_resize ();
5198 keypad(boardw, TRUE);
5199 break;
5200 case SIGINT:
5201 quit = 1;
5202 break;
5203 case SIGTERM:
5204 signal_save_pgn(which);
5205 break;
5206 default:
5207 break;
5211 void loading_progress(long total, long offset)
5213 int n = (100 * (offset / 100) / (total / 100));
5215 if (curses_initialized)
5216 update_loading_window(n);
5217 else {
5218 fprintf(stderr, _("Loading... %i%% (%i games)%c"), n, gtotal, '\r');
5219 fflush(stderr);
5223 static void set_defaults()
5225 set_config_defaults();
5226 set_default_keys();
5227 filetype = FILE_NONE;
5228 pgn_config_set(PGN_PROGRESS, 1024);
5229 pgn_config_set(PGN_PROGRESS_FUNC, loading_progress);
5232 int main(int argc, char *argv[])
5234 int opt;
5235 struct stat st;
5236 char buf[FILENAME_MAX];
5237 char datadir[FILENAME_MAX];
5238 int ret = EXIT_SUCCESS;
5239 int validate_only = 0, validate_and_write = 0;
5240 int write_custom_tags = 0;
5241 int i = 0;
5242 PGN_FILE *pgn;
5243 int utf8_pieces = -1;
5245 setlocale (LC_ALL, "");
5246 bindtextdomain ("cboard", LOCALE_DIR);
5247 textdomain ("cboard");
5249 /* Solaris 5.9 */
5250 #ifndef HAVE_PROGNAME
5251 __progname = argv[0];
5252 #endif
5254 if ((config.pwd = getpwuid(getuid())) == NULL)
5255 err(EXIT_FAILURE, "getpwuid()");
5257 snprintf(datadir, sizeof(datadir), "%s/.cboard", config.pwd->pw_dir);
5258 config.datadir = strdup(datadir);
5259 snprintf(buf, sizeof(buf), "%s/cc.data", datadir);
5260 config.ccfile = strdup(buf);
5261 snprintf(buf, sizeof(buf), "%s/nag.data", datadir);
5262 config.nagfile = strdup(buf);
5263 snprintf(buf, sizeof(buf), "%s/config", datadir);
5264 config.configfile = strdup(buf);
5266 if (stat(datadir, &st) == -1) {
5267 if (errno == ENOENT) {
5268 if (mkdir(datadir, 0755) == -1)
5269 err(EXIT_FAILURE, "%s", datadir);
5271 else
5272 err(EXIT_FAILURE, "%s", datadir);
5274 stat(datadir, &st);
5277 if (!S_ISDIR(st.st_mode))
5278 errx(EXIT_FAILURE, "%s: %s", datadir, _("Not a directory."));
5280 set_defaults();
5282 #ifdef DEBUG
5283 while ((opt = getopt(argc, argv, "DCEVtSRhp:vu::")) != -1) {
5284 #else
5285 while ((opt = getopt(argc, argv, "ECVtSRhp:vu::")) != -1) {
5286 #endif
5287 switch (opt) {
5288 #ifdef DEBUG
5289 case 'D':
5290 unlink("libchess.debug");
5291 pgn_config_set(PGN_DEBUG, 1);
5292 break;
5293 #endif
5294 case 'C':
5295 pgn_config_set(PGN_STRICT_CASTLING, 1);
5296 break;
5297 case 't':
5298 write_custom_tags = 1;
5299 break;
5300 case 'E':
5301 i = 1;
5302 break;
5303 case 'R':
5304 pgn_config_set(PGN_REDUCED, 1);
5305 case 'S':
5306 validate_and_write = 1;
5307 case 'V':
5308 validate_only = 1;
5309 break;
5310 case 'v':
5311 printf("%s (%s)\n%s\n", PACKAGE_STRING, curses_version(),
5312 COPYRIGHT);
5313 exit(EXIT_SUCCESS);
5314 case 'p':
5315 filetype = FILE_PGN;
5316 strncpy(loadfile, optarg, sizeof(loadfile));
5317 loadfile[sizeof(loadfile)-1] = 0;
5318 break;
5319 case 'u':
5320 utf8_pieces = optarg ? atoi (optarg): 1;
5321 break;
5322 case 'h':
5323 default:
5324 usage(argv[0], EXIT_SUCCESS);
5328 if ((validate_only || validate_and_write) && !*loadfile)
5329 usage(argv[0], EXIT_FAILURE);
5331 if (access(config.configfile, R_OK) == 0)
5332 parse_rcfile(config.configfile);
5334 if (i)
5335 pgn_config_set(PGN_STOP_ON_ERROR, 1);
5337 signal(SIGPIPE, catch_signal);
5338 signal(SIGCONT, catch_signal);
5339 signal(SIGSTOP, catch_signal);
5340 signal(SIGINT, catch_signal);
5341 signal(SIGALRM, catch_signal);
5342 signal(SIGTERM, catch_signal);
5344 srandom(getpid());
5346 switch (filetype) {
5347 case FILE_PGN:
5348 if (pgn_open(loadfile, "r", &pgn) != E_PGN_OK)
5349 err(EXIT_FAILURE, "%s", loadfile);
5351 ret = pgn_parse(pgn);
5352 pgn_close(pgn);
5353 break;
5354 case FILE_FEN:
5355 //ret = parse_fen_file(loadfile);
5356 break;
5357 case FILE_EPD: // Not implemented.
5358 case FILE_NONE:
5359 default:
5360 // No file specified. Empty game.
5361 ret = pgn_parse(NULL);
5362 gp = game[gindex];
5363 add_custom_tags(&gp->tag);
5364 break;
5367 if (validate_only || validate_and_write) {
5368 if (validate_and_write) {
5369 if (pgn_open("-", "r", &pgn) != E_PGN_OK)
5370 err(EXIT_FAILURE, "pgn_open()");
5372 for (i = 0; i < gtotal; i++) {
5373 if (write_custom_tags)
5374 add_custom_tags(&game[i]->tag);
5376 pgn_write(pgn, game[i]);
5379 pgn_close(pgn);
5381 fm_loaded_file = TRUE;
5384 cleanup_all();
5385 exit(ret);
5387 else if (ret == E_PGN_ERR)
5388 exit(ret);
5390 if (utf8_pieces != -1)
5391 config.utf8_pieces = utf8_pieces;
5393 init_wchar_pieces ();
5394 yes_wchar = str_to_wchar (_("y"));
5395 all_wchar = str_to_wchar (_("a"));
5396 overwrite_wchar = str_to_wchar (_("o"));
5397 resume_wchar = str_to_wchar (_("r"));
5398 current_wchar = str_to_wchar (_("c"));
5399 append_wchar = str_to_wchar (_("a"));
5400 translatable_tag_names[0] = _("Event");
5401 translatable_tag_names[1] = _("Site");
5402 translatable_tag_names[2] = _("Date");
5403 translatable_tag_names[3] = _("Round");
5404 translatable_tag_names[4] = _("White");
5405 translatable_tag_names[5] = _("Black");
5406 translatable_tag_names[6] = _("Result");
5407 init_userdata();
5410 * This fixes window resizing in an xterm.
5412 if (getenv("DISPLAY") != NULL) {
5413 putenv("LINES=");
5414 putenv("COLUMNS=");
5417 if (initscr() == NULL)
5418 errx(EXIT_FAILURE, "%s", _("Could not initialize curses."));
5419 else
5420 curses_initialized = 1;
5422 if (LINES < 23 || COLS < 74) {
5423 endwin();
5424 errx(EXIT_FAILURE, _("Need at least an 74x23 terminal."));
5427 COLS_OLD = COLS;
5428 LINES_OLD = LINES;
5430 if (has_colors() == TRUE && start_color() == OK)
5431 init_color_pairs();
5433 boardw = newwin(BOARD_HEIGHT, BOARD_WIDTH, 0, COLS - BOARD_WIDTH);
5434 boardp = new_panel(boardw);
5435 historyw = newwin(HISTORY_HEIGHT, HISTORY_WIDTH, LINES - HISTORY_HEIGHT,
5436 COLS - HISTORY_WIDTH);
5437 historyp = new_panel(historyw);
5438 statusw = newwin(STATUS_HEIGHT, STATUS_WIDTH, 0, 0);
5439 statusp = new_panel(statusw);
5440 tagw = newwin(TAG_HEIGHT, TAG_WIDTH, STATUS_HEIGHT + 1, 0);
5441 tagp = new_panel(tagw);
5442 keypad(boardw, TRUE);
5443 // leaveok(boardw, TRUE);
5444 leaveok(tagw, TRUE);
5445 leaveok(statusw, TRUE);
5446 leaveok(historyw, TRUE);
5447 curs_set(0);
5448 cbreak();
5449 noecho();
5450 draw_window_decor();
5451 game_loop();
5452 cleanup_all();
5453 free (w_pawn_wchar);
5454 free (w_rook_wchar);
5455 free (w_bishop_wchar);
5456 free (w_knight_wchar);
5457 free (w_queen_wchar);
5458 free (w_king_wchar);
5459 free (b_pawn_wchar);
5460 free (b_rook_wchar);
5461 free (b_bishop_wchar);
5462 free (b_knight_wchar);
5463 free (b_queen_wchar);
5464 free (b_king_wchar);
5465 free (empty_wchar);
5466 free (enpassant_wchar);
5467 free (yes_wchar);
5468 free (all_wchar);
5469 free (overwrite_wchar);
5470 free (resume_wchar);
5471 free (current_wchar);
5472 free (append_wchar);
5473 exit(EXIT_SUCCESS);