Fixup color pairs for the previous move.
[cboard.git] / src / cboard.c
blob6ff53fad8129ab536b88c025940af3ccb22fea32
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2013 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <pwd.h>
33 #include <signal.h>
34 #include <time.h>
35 #include <err.h>
36 #include <locale.h>
38 #ifdef HAVE_STDARG_H
39 #include <stdarg.h>
40 #endif
42 #ifdef HAVE_SYS_WAIT_H
43 #include <sys/wait.h>
44 #endif
46 #ifdef HAVE_REGEX_H
47 #include <regex.h>
48 #endif
50 #ifdef WITH_LIBPERL
51 #include "perl-plugin.h"
52 #endif
54 #include "common.h"
55 #include "conf.h"
56 #include "window.h"
57 #include "message.h"
58 #include "colors.h"
59 #include "input.h"
60 #include "misc.h"
61 #include "engine.h"
62 #include "strings.h"
63 #include "menu.h"
64 #include "keys.h"
65 #include "rcfile.h"
66 #include "filebrowser.h"
68 #ifdef DEBUG
69 #include <debug.h>
70 #endif
72 #define COPYRIGHT "Copyright (C) 2002-2013 " PACKAGE_BUGREPORT
73 #define LINE_GRAPHIC(c) ((!config.linegraphics) ? ' ' : c)
74 #define ROWTOMATRIX(r) ((8 - r) * 2 + 2 - 1)
75 #define COLTOMATRIX(c) ((c == 1) ? 1 : c * 4 - 3)
76 #define STATUS_HEIGHT 12
77 #define MEGA_BOARD (LINES >= 50 && COLS >= 144)
78 #define BOARD_HEIGHT_MB (50)
79 #define BOARD_WIDTH_MB (98)
80 #define STATUS_WIDTH_MB (COLS - BOARD_WIDTH_MB)
81 #define TAG_HEIGHT_MB (20)
82 #define TAG_WIDTH_MB (COLS - BOARD_WIDTH_MB)
83 #define HISTORY_HEIGHT_MB (LINES - (STATUS_HEIGHT + TAG_HEIGHT_MB + 1))
84 #define HISTORY_WIDTH_MB (COLS - BOARD_WIDTH_MB)
85 #define BIG_BOARD (LINES >= 40 && COLS >= 112)
86 #define BOARD_HEIGHT ((MEGA_BOARD) ? BOARD_HEIGHT_MB : (BIG_BOARD) ? 34 : 18)
87 #define BOARD_WIDTH ((MEGA_BOARD) ? BOARD_WIDTH_MB : (BIG_BOARD) ? 66 : 34)
88 #define STATUS_WIDTH ((MEGA_BOARD) ? STATUS_WIDTH_MB : COLS - BOARD_WIDTH)
89 #define TAG_HEIGHT ((MEGA_BOARD) ? TAG_HEIGHT_MB : LINES - STATUS_HEIGHT - 1)
90 #define TAG_WIDTH ((MEGA_BOARD) ? TAG_WIDTH_MB : COLS - BOARD_WIDTH)
91 #define HISTORY_HEIGHT ((MEGA_BOARD) ? HISTORY_HEIGHT_MB : LINES - BOARD_HEIGHT)
92 #define HISTORY_WIDTH ((MEGA_BOARD) ? HISTORY_WIDTH_MB : COLS - STATUS_WIDTH)
93 #define MAX_VALUE_WIDTH (COLS - 8)
95 enum {
96 UP, DOWN, LEFT, RIGHT
99 static WINDOW *boardw;
100 static PANEL *boardp;
101 static WINDOW *tagw;
102 static PANEL *tagp;
103 static WINDOW *statusw;
104 static PANEL *statusp;
105 static WINDOW *historyw;
106 static PANEL *historyp;
107 static WINDOW *loadingw;
108 static PANEL *loadingp;
109 static WINDOW *enginew;
110 static PANEL *enginep;
112 static char gameexp[255];
113 static char moveexp[255];
114 static struct itimerval clock_timer;
115 static int delete_count = 0;
116 static int markstart = -1, markend = -1;
117 static int keycount;
118 static char loadfile[FILENAME_MAX];
119 static int quit;
120 static 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;
754 int v = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].valid;
755 int pi = pgn_piece_to_int (d->b[RANKTOBOARD (rank)][FILETOBOARD (file)].icon);
756 int cpi = d->sp.icon
757 ? pgn_piece_to_int (d->b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon)
758 : pgn_piece_to_int (d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].icon);
760 if (pi == OPEN_SQUARE || cpi == OPEN_SQUARE || !VALIDFILE (file)
761 || !VALIDRANK (rank))
762 return 0;
764 if (d->sp.icon) {
765 col = v ? d->c_col : d->sp.scol;
766 row = v ? d->c_row : d->sp.srow;
768 else {
769 col = d->c_col;
770 row = d->c_row;
773 m = malloc(MAX_SAN_MOVE_LEN+1);
774 m[0] = INTTOFILE (file);
775 m[1] = INTTORANK (rank);
776 m[2] = INTTOFILE (col);
777 m[3] = INTTORANK (row);
778 m[4] = 0;
780 if (d->sp.icon && v) {
781 BOARD b;
783 memcpy (b, d->b, sizeof(BOARD));
784 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
785 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
786 pgn_int_to_piece (WHITE, OPEN_SQUARE);
787 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
788 pgn_switch_turn(g);
789 e = pgn_validate_move (g, b, &m, &frfr);
790 pgn_switch_turn (g);
791 free (m);
792 free (frfr);
793 return e == E_PGN_OK ? 1 : 0;
796 pgn_switch_turn(g);
797 e = pgn_validate_move (g, d->b, &m, &frfr);
798 pgn_switch_turn (g);
800 if (!strcmp (m, "O-O") || !strcmp (m, "O-O-O"))
801 e = E_PGN_INVALID;
803 if (e == E_PGN_OK) {
804 int sf = FILETOINT (frfr[0]), sr = RANKTOINT (frfr[1]);
805 int df = FILETOINT (frfr[2]);
807 pi = d->b[RANKTOBOARD (sr)][FILETOBOARD (sf)].icon;
808 pi = pgn_piece_to_int (pi);
809 if (pi == PAWN && sf == df)
810 e = E_PGN_INVALID;
813 free (m);
814 free (frfr);
815 return e == E_PGN_OK ? 1 : 0;
818 void print_piece(WINDOW *w, int l, int c, char p)
820 int i, y, ff = 0;
822 for (i = 0; i < 13; i += 2) {
823 if (p == piece_chars[i] || p == piece_chars[i + 1]) {
824 for (y = 0; y < 3; y++)
825 mvwprintw(w, l + y, c, "%s", f_pieces[i + ff + y]);
826 return;
829 ff++;
832 for (y = 0; y < 3; y++)
833 mvwprintw(w, l + y, c, f_pieces[0]);
836 int inv_int(int x)
838 int fx, fx2 = 8;
840 for (fx = 1; fx < 9; fx++) {
841 if (x == fx)
842 break;
843 fx2--;
846 return fx2;
849 int coordofmove(char *move, int yorx)
851 int l, i;
853 if (!rotate) {
854 if (!strcmp(move, "O-O"))
855 return (yorx) ? 8 : 7;
856 else if (!strcmp(move, "O-O-O"))
857 return (yorx) ? 8 : 3;
859 else {
860 if (!strcmp(move, "O-O"))
861 return (yorx) ? 1 : 7;
862 else if (!strcmp(move, "O-O-O"))
863 return (yorx) ? 1 : 3;
866 l = strlen(move);
868 if (yorx)
869 return move[l - 1] - 48;
870 else {
871 for (i = 0; i < 8; i++) {
872 if (move[l - 2] == "abcdefgh"[i])
873 return i + 1;
877 return 0;
880 static int is_prev_move (struct userdata_s *d, int brow, int bcol,
881 int row, int col)
883 if ((!BIG_BOARD &&
884 row == ROWTOMATRIX(((rotate) ? inv_int(d->pm_row) : d->pm_row)) &&
885 col == COLTOMATRIX(((rotate) ? inv_int(d->pm_col) : d->pm_col)))
886 || (BIG_BOARD &&
887 brow + 1 == ((rotate) ? d->pm_row : inv_int(d->pm_row)) &&
888 bcol + 1 == ((rotate) ? inv_int(d->pm_col) : d->pm_col)))
889 return 1;
891 return 0;
894 // FIXME: BIG_BOARD - start in dwm monocle layout.
895 // Example: lxterminal -e cboard (dmenu, geany, etc.)
896 void update_board_window(GAME g)
898 int row, col;
899 int bcol = 0, brow = 0;
900 int l = config.coordsyleft;
901 int maxy = BOARD_HEIGHT, maxx = BOARD_WIDTH;
902 int ncols = 0, offset = 1;
903 int rowr = (MEGA_BOARD) ? 6 : (BIG_BOARD) ? 4 : 2;
904 int colr = (MEGA_BOARD) ? 12 : (BIG_BOARD) ? 8 : 4;
905 unsigned coords_y = 8, cxgc = 0;
906 unsigned i, cpd = 0;
907 struct userdata_s *d = g->data;
908 HISTORY *h = NULL;
910 h = pgn_history_by_n(g->hp, g->hindex - 1);
911 d->pm_row = (h && h->move && d->mode == MODE_PLAY)
912 ? coordofmove(h->move, 1) : 0;
913 d->pm_col = (h && h->move && d->mode == MODE_PLAY)
914 ? coordofmove(h->move, 0) : 0;
916 if (d->mode != MODE_PLAY && d->mode != MODE_EDIT)
917 update_cursor(g, g->hindex);
919 if (BIG_BOARD) {
920 if (rotate) {
921 brow = 7;
922 coords_y = 1;
925 else {
926 if (rotate) {
927 brow = 1;
928 coords_y = 1;
930 else
931 brow = 8;
934 for (row = 0; row < maxy; row++) {
935 if (BIG_BOARD) {
936 if (rotate)
937 bcol = 7;
938 else
939 bcol = 0;
941 else {
942 if (rotate)
943 bcol = 8;
944 else
945 bcol = 1;
948 for (col = 0; col < maxx; col++) {
949 int attrwhich = -1;
950 chtype attrs = 0, old_attrs = 0;
951 unsigned char p;
952 int can_attack = 0;
953 int valid = 0;
955 if (row == 0 || row == maxy - 2) {
956 if (col == 0)
957 mvwaddch(boardw, row, col + l,
958 LINE_GRAPHIC((row)
959 ? ACS_LLCORNER | CP_BOARD_GRAPHICS
960 : ACS_ULCORNER | CP_BOARD_GRAPHICS));
961 else if (col == maxx - 2)
962 mvwaddch(boardw, row, col + l,
963 LINE_GRAPHIC((row)
964 ? ACS_LRCORNER | CP_BOARD_GRAPHICS
965 : ACS_URCORNER | CP_BOARD_GRAPHICS));
966 else if (!(col % colr))
967 mvwaddch(boardw, row, col + l,
968 LINE_GRAPHIC((row)
969 ? ACS_BTEE | CP_BOARD_GRAPHICS
970 : ACS_TTEE | CP_BOARD_GRAPHICS));
971 else {
972 if (col != maxx - 1)
973 mvwaddch(boardw, row, col + l,
974 LINE_GRAPHIC(ACS_HLINE | CP_BOARD_GRAPHICS));
977 continue;
980 if ((row % 2) && col == maxx - 1 &&
981 (coords_y > 0 && coords_y < 9)) {
982 wattron(boardw, CP_BOARD_COORDS);
983 mvwprintw(boardw,
984 (BIG_BOARD) ? row * ((MEGA_BOARD) ? 3 : 2)
985 : row, (l) ? 0 : col, "%d",
986 (rotate) ? coords_y++ : coords_y--);
987 wattroff(boardw, CP_BOARD_COORDS);
988 continue;
991 if ((col == 0 || col == maxx - 2) && row != maxy - 1) {
992 if (!(row % rowr))
993 mvwaddch(boardw, row, col + l,
994 LINE_GRAPHIC((col) ?
995 ACS_RTEE | CP_BOARD_GRAPHICS :
996 ACS_LTEE | CP_BOARD_GRAPHICS));
997 else
998 mvwaddch(boardw, row, col + l,
999 LINE_GRAPHIC(ACS_VLINE | CP_BOARD_GRAPHICS));
1001 continue;
1004 if ((row % rowr) && !(col % colr) && row != maxy - 1) {
1005 mvwaddch(boardw, row, col + l,
1006 LINE_GRAPHIC(ACS_VLINE | CP_BOARD_GRAPHICS));
1007 continue;
1010 if (!(col % colr) && row != maxy - 1) {
1011 mvwaddch(boardw, row, col + l,
1012 LINE_GRAPHIC(ACS_PLUS | CP_BOARD_GRAPHICS));
1013 continue;
1016 if ((row % rowr)) {
1017 if ((col % colr)) {
1018 if (BIG_BOARD)
1019 attrwhich = (cb[brow][bcol]) ? WHITE : BLACK;
1020 else {
1021 if (ncols++ == 8) {
1022 offset++;
1023 ncols = 1;
1026 if (((ncols % 2) && !(offset % 2))
1027 || (!(ncols % 2) && (offset % 2)))
1028 attrwhich = BLACK;
1029 else
1030 attrwhich = WHITE;
1033 if (BIG_BOARD && rotate) {
1034 brow = inv_int(brow + 1) - 1;
1035 bcol = inv_int(bcol + 1) - 1;
1038 if (BIG_BOARD)
1039 p = d->b[brow][bcol].icon;
1040 else
1041 p = d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].icon;
1043 int pi = pgn_piece_to_int(p);
1045 if (config.details &&
1046 ((!BIG_BOARD && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].enpassant)
1047 || (BIG_BOARD && d->b[brow][bcol].enpassant))) {
1048 p = pi = 'x';
1049 attrs = mix_cp(CP_BOARD_ENPASSANT,
1050 (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK,
1051 ATTRS(CP_BOARD_ENPASSANT), A_FG_B_BG);
1054 // FIXME: showattacks - rotated board.
1055 if (config.showattacks && config.details
1056 && piece_can_attack (g,
1057 BIG_BOARD ? inv_int (brow+1) : brow,
1058 BIG_BOARD ? bcol+1 : bcol)) {
1059 attrs = CP_BOARD_ATTACK;
1060 old_attrs = attrs;
1061 can_attack = 1;
1064 if (config.validmoves &&
1065 ((!BIG_BOARD && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].valid)
1066 || (BIG_BOARD && d->b[brow][bcol].valid))) {
1067 old_attrs = -1;
1068 valid = 1;
1070 if (attrwhich == WHITE)
1071 attrs = mix_cp(CP_BOARD_MOVES_WHITE,
1072 IS_ENPASSANT(p),
1073 ATTRS(CP_BOARD_MOVES_WHITE),
1074 B_FG_A_BG);
1075 else
1076 attrs = mix_cp(CP_BOARD_MOVES_BLACK,
1077 IS_ENPASSANT(p),
1078 ATTRS(CP_BOARD_MOVES_BLACK),
1079 B_FG_A_BG);
1081 else if (p != 'x' && !can_attack)
1082 attrs = (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK;
1084 if (BIG_BOARD && rotate) {
1085 brow = inv_int(brow + 1) - 1;
1086 bcol = inv_int(bcol + 1) - 1;
1089 if ((!BIG_BOARD && row == ROWTOMATRIX(d->c_row)
1090 && col == COLTOMATRIX(d->c_col))
1091 || (BIG_BOARD && brow + 1 == inv_int(d->c_row)
1092 && bcol + 1 == d->c_col)) {
1093 attrs = mix_cp(CP_BOARD_CURSOR, IS_ENPASSANT(p),
1094 ATTRS(CP_BOARD_CURSOR), B_FG_A_BG);
1095 old_attrs = -1;
1097 else if ((!BIG_BOARD && row == ROWTOMATRIX(d->sp.srow) &&
1098 col == COLTOMATRIX(d->sp.scol)) ||
1099 (BIG_BOARD && brow + 1 == inv_int(d->sp.srow) &&
1100 bcol + 1 == d->sp.scol)) {
1101 attrs = mix_cp(CP_BOARD_SELECTED, IS_ENPASSANT(p),
1102 ATTRS(CP_BOARD_SELECTED), B_FG_A_BG);
1103 old_attrs = -1;
1105 else if (is_prev_move (d, brow, bcol, row, col) && !valid)
1107 attrs = mix_cp(CP_BOARD_PREVMOVE, IS_ENPASSANT(p),
1108 ATTRS(CP_BOARD_PREVMOVE), B_FG_A_BG);
1109 old_attrs = -1;
1112 if (row == maxy - 1)
1113 attrs = 0;
1115 if (can_attack) {
1116 int n = is_prev_move (d, brow, bcol, row, col);
1117 chtype a = n && !valid
1118 ? CP_BOARD_PREVMOVE : attrwhich == WHITE ? valid ? CP_BOARD_MOVES_WHITE : CP_BOARD_WHITE : valid ? CP_BOARD_MOVES_BLACK : CP_BOARD_BLACK;
1119 attrs = mix_cp(CP_BOARD_ATTACK, a,
1120 ATTRS (CP_BOARD_ATTACK), A_FG_B_BG);
1121 old_attrs = -1;
1124 if (BIG_BOARD)
1125 wmove(boardw, row, col + ((MEGA_BOARD) ? 5 : 3) + l);
1126 else
1127 mvwaddch(boardw, row, col + l, ' ' | attrs);
1129 if (row == maxy - 1 && cxgc < 8) {
1130 waddch(boardw, "abcdefgh"[(BIG_BOARD) ? bcol : bcol - 1] | CP_BOARD_COORDS);
1131 cxgc++;
1133 else {
1134 if (old_attrs == -1) {
1135 old_attrs = attrs;
1136 goto printc;
1139 old_attrs = attrs;
1141 if (pi != OPEN_SQUARE && p != 'x' && !can_attack) {
1142 if (attrwhich == WHITE) {
1143 if (isupper(p))
1144 attrs = CP_BOARD_W_W;
1145 else
1146 attrs = CP_BOARD_W_B;
1148 else {
1149 if (isupper(p))
1150 attrs = CP_BOARD_B_W;
1151 else
1152 attrs = CP_BOARD_B_B;
1156 printc:
1157 if (BIG_BOARD) {
1158 if (config.details && !can_attack
1159 && castling_state(g, d->b,
1160 (rotate) ? inv_int(brow + 1) - 1: brow,
1161 (rotate) ? inv_int(bcol + 1) - 1: bcol, p, 0))
1162 attrs = mix_cp(CP_BOARD_CASTLING, attrs,
1163 ATTRS(CP_BOARD_CASTLING),
1164 A_FG_B_BG);
1166 else {
1167 if (config.details && !can_attack
1168 && castling_state(g, d->b, RANKTOBOARD (brow),
1169 FILETOBOARD (bcol), p, 0)) {
1170 attrs = mix_cp(CP_BOARD_CASTLING, attrs,
1171 ATTRS(CP_BOARD_CASTLING),
1172 A_FG_B_BG);
1176 if (BIG_BOARD) {
1177 // FIXME: Reimpresión de piezas(+3).
1178 if (cpd < 67) {
1179 wattron (boardw, attrs);
1180 if (MEGA_BOARD){
1181 for (i = 0; i < 5; i++)
1182 mvwprintw(boardw, i + brow * 6 + 1,
1183 bcol * 12 + 1 + l,
1184 " ");
1185 if (pi != OPEN_SQUARE)
1186 print_piece(boardw, brow * 6 + 2,
1187 bcol * 12 + 3 + l, p);
1189 else {
1190 print_piece(boardw, brow * 4 + 1,
1191 bcol * 8 + 1 + l,
1192 (pi != OPEN_SQUARE) ? p : 0);
1195 wattroff (boardw, attrs);
1196 cpd++;
1199 else {
1200 wattron (boardw, attrs);
1201 waddwstr (boardw, piece_to_wchar (pi != OPEN_SQUARE ? p : 0));
1202 wattroff (boardw, attrs);
1205 attrs = old_attrs;
1208 if (BIG_BOARD)
1209 col += (MEGA_BOARD) ? 10 : 6;
1210 else {
1211 waddch(boardw, ' ' | attrs);
1212 col += 2;
1215 if (rotate)
1216 bcol--;
1217 else
1218 bcol++;
1220 if (BIG_BOARD) {
1221 if (bcol > 7)
1222 bcol = 0;
1223 if (bcol < 0)
1224 bcol = 7;
1228 else {
1229 if (col != maxx - 1)
1230 mvwaddch(boardw, row, col + l,
1231 LINE_GRAPHIC(ACS_HLINE | CP_BOARD_GRAPHICS));
1235 if (row % rowr) {
1236 if (rotate)
1237 brow++;
1238 else
1239 brow--;
1242 if (BIG_BOARD) {
1243 if (brow > 7)
1244 brow = 0;
1245 if (brow < 0)
1246 brow = 7;
1251 void invalid_move(int n, int e, const char *m)
1253 if (curses_initialized)
1254 cmessage(ERROR_STR, ANY_KEY_STR, "%s \"%s\" (round #%i)", (e == E_PGN_AMBIGUOUS)
1255 ? _("Ambiguous move") : _("Invalid move"), m, n);
1256 else
1257 warnx("%s: %s \"%s\" (round #%i)", loadfile, (e == E_PGN_AMBIGUOUS)
1258 ? _("Ambiguous move") : _("Invalid move"), m, n);
1261 void gameover(GAME g)
1263 struct userdata_s *d = g->data;
1265 SET_FLAG(g->flags, GF_GAMEOVER);
1266 d->mode = MODE_HISTORY;
1267 stop_engine(g);
1270 static void update_clock(GAME g, struct itimerval it)
1272 struct userdata_s *d = g->data;
1274 if (TEST_FLAG(d->flags, CF_CLOCK) && g->turn == WHITE) {
1275 d->wclock.elapsed.tv_sec += it.it_value.tv_sec;
1276 d->wclock.elapsed.tv_usec += it.it_value.tv_usec;
1278 if (d->wclock.elapsed.tv_usec > 1000000 - 1) {
1279 d->wclock.elapsed.tv_sec += d->wclock.elapsed.tv_usec / 1000000;
1280 d->wclock.elapsed.tv_usec = d->wclock.elapsed.tv_usec % 1000000;
1283 if (d->wclock.tc[d->wclock.tcn][1] &&
1284 d->wclock.elapsed.tv_sec >= d->wclock.tc[d->wclock.tcn][1]) {
1285 pgn_tag_add(&g->tag, "Result", "0-1");
1286 gameover(g);
1289 else if (TEST_FLAG(d->flags, CF_CLOCK) && g->turn == BLACK) {
1290 d->bclock.elapsed.tv_sec += it.it_value.tv_sec;
1291 d->bclock.elapsed.tv_usec += it.it_value.tv_usec;
1293 if (d->bclock.elapsed.tv_usec > 1000000 - 1) {
1294 d->bclock.elapsed.tv_sec += d->bclock.elapsed.tv_usec / 1000000;
1295 d->bclock.elapsed.tv_usec = d->bclock.elapsed.tv_usec % 1000000;
1298 if (d->bclock.tc[d->bclock.tcn][1] &&
1299 d->bclock.elapsed.tv_sec >= d->bclock.tc[d->bclock.tcn][1]) {
1300 pgn_tag_add(&g->tag, "Result", "1-0");
1301 gameover(g);
1305 d->elapsed.tv_sec += it.it_value.tv_sec;
1306 d->elapsed.tv_usec += it.it_value.tv_usec;
1308 if (d->elapsed.tv_usec > 1000000 - 1) {
1309 d->elapsed.tv_sec += d->elapsed.tv_usec / 1000000;
1310 d->elapsed.tv_usec = d->elapsed.tv_usec % 1000000;
1314 static void update_time_control(GAME g)
1316 struct userdata_s *d = g->data;
1317 struct clock_s *clk = (g->turn == WHITE) ? &d->wclock : &d->bclock;
1319 if (clk->incr)
1320 clk->tc[clk->tcn][1] += clk->incr;
1322 if (!clk->tc[clk->tcn][1])
1323 return;
1325 clk->move++;
1327 if (!clk->tc[clk->tcn][0] || clk->move >= clk->tc[clk->tcn][0]) {
1328 clk->move = 0;
1329 clk->tc[clk->tcn + 1][1] += abs(clk->elapsed.tv_sec - clk->tc[clk->tcn][1]);
1330 memset(&clk->elapsed, 0, sizeof(clk->elapsed));
1331 clk->tcn++;
1335 void update_history_window(GAME g)
1337 char buf[HISTORY_WIDTH - 1];
1338 HISTORY *h = NULL;
1339 int n, total;
1340 int t = pgn_history_total(g->hp);
1342 n = (g->hindex + 1) / 2;
1344 if (t % 2)
1345 total = (t + 1) / 2;
1346 else
1347 total = t / 2;
1349 if (t)
1350 snprintf(buf, sizeof(buf), "%u %s %u%s", n, _("of"), total,
1351 (movestep == 1) ? _(" (ply)") : "");
1352 else
1353 strncpy(buf, _("not available"), sizeof(buf)-1);
1355 buf[sizeof(buf)-1] = 0;
1356 mvwprintw(historyw, 2, 1, "%*s %-*s", 10, _("Move:"),
1357 HISTORY_WIDTH - 14, buf);
1359 h = pgn_history_by_n(g->hp, g->hindex);
1360 snprintf(buf, sizeof(buf), "%s",
1361 (h && h->move) ? h->move
1362 : (LINES < 24) ? _("empty") : _("not available"));
1363 n = 0;
1365 if (h && ((h->comment) || h->nag[0])) {
1366 strncat(buf, _(" (Annotated"), sizeof(buf)-1);
1367 n++;
1370 if (h && h->rav) {
1371 strncat(buf, (n) ? ",+" : " (+", sizeof(buf)-1);
1372 n++;
1375 if (g->ravlevel) {
1376 strncat(buf, (n) ? ",-" : " (-", sizeof(buf)-1);
1377 n++;
1380 if (n)
1381 strncat(buf, ")", sizeof(buf)-1);
1383 mvwprintw(historyw, 3, ((LINES < 24) ? 17 : 1), "%s %-*s",
1384 (LINES < 24) ? _("Next:") :_("Next move:"),
1385 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1387 h = pgn_history_by_n(g->hp, g->hindex - 1);
1388 snprintf(buf, sizeof(buf), "%s",
1389 (h && h->move) ? h->move
1390 : (LINES < 24) ? _("empty") : _("not available"));
1391 n = 0;
1393 if (h && ((h->comment) || h->nag[0])) {
1394 strncat(buf, _(" (Annotated"), sizeof(buf)-1);
1395 n++;
1398 if (h && h->rav) {
1399 strncat(buf, (n) ? ",+" : " (+", sizeof(buf)-1);
1400 n++;
1403 if (g->ravlevel) {
1404 strncat(buf, (n) ? ",-" : " (-", sizeof(buf)-1);
1405 n++;
1408 if (n)
1409 strncat(buf, ")", sizeof(buf)-1);
1411 mvwprintw(historyw, ((LINES < 24) ? 3 : 4), 1, "%s %-*s",
1412 (LINES < 24) ? _("Prev.:") : _("Prev move:"),
1413 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1416 void do_validate_move(char *move)
1418 struct userdata_s *d = gp->data;
1419 int n;
1420 char *frfr = NULL;
1422 if (TEST_FLAG(d->flags, CF_HUMAN)) {
1423 if ((n = pgn_parse_move(gp, d->b, &move, &frfr)) != E_PGN_OK) {
1424 invalid_move(d->n + 1, n, move);
1425 return;
1428 update_time_control(gp);
1429 pgn_history_add(gp, d->b, move);
1430 pgn_switch_turn(gp);
1432 else {
1433 if ((n = pgn_validate_move(gp, d->b, &move, &frfr)) != E_PGN_OK) {
1434 invalid_move(d->n + 1, n, move);
1435 return;
1438 add_engine_command(gp, ENGINE_THINKING, "%s\n",
1439 (config.engine_protocol == 1) ? frfr : move);
1442 d->sp.srow = d->sp.scol = d->sp.icon = 0;
1444 if (config.validmoves)
1445 pgn_reset_valid_moves(d->b);
1447 if (TEST_FLAG(gp->flags, GF_GAMEOVER))
1448 d->mode = MODE_HISTORY;
1449 else
1450 SET_FLAG(d->flags, CF_MODIFIED);
1452 free (frfr);
1453 d->paused = 0;
1454 update_history_window(gp);
1455 update_board_window(gp);
1456 return;
1459 void do_promotion_piece_finalize(WIN *win)
1461 char *p, *str = win->data;
1463 if (pgn_piece_to_int(win->c) == -1)
1464 return;
1466 p = str + strlen(str);
1467 *p++ = toupper(win->c);
1468 *p = '\0';
1469 do_validate_move(str);
1470 free (win->data);
1471 win->data = NULL;
1474 static void move_to_engine(GAME g)
1476 struct userdata_s *d = g->data;
1477 char *str;
1478 int piece;
1480 if (config.validmoves &&
1481 !d->b[RANKTOBOARD(d->sp.row)][FILETOBOARD(d->sp.col)].valid)
1482 return;
1484 str = Malloc(MAX_SAN_MOVE_LEN + 1);
1485 snprintf(str, MAX_SAN_MOVE_LEN + 1, "%c%i%c%i",
1486 _("abcdefgh")[d->sp.scol - 1],
1487 d->sp.srow, _("abcdefgh")[d->sp.col - 1], d->sp.row);
1489 piece = pgn_piece_to_int(d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon);
1491 if (piece == PAWN && (d->sp.row == 8 || d->sp.row == 1)) {
1492 construct_message(_("Select Pawn Promotion Piece"), _ ("R/N/B/Q"), 1, 1, NULL, NULL,
1493 str, do_promotion_piece_finalize, 0, 0, "%s", _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1494 return;
1497 do_validate_move(str);
1498 free (str);
1501 static char *clock_to_char(long n)
1503 static char buf[16];
1504 int h = 0, m = 0, s = 0;
1506 h = n / 3600;
1507 m = (n % 3600) / 60;
1508 s = (n % 3600) % 60;
1509 snprintf(buf, sizeof(buf), "%.2i:%.2i:%.2i", h, m, s);
1510 return buf;
1513 static char *timeval_to_char(struct timeval t, long limit)
1515 static char buf[9];
1516 int h = 0, m = 0, s = 0;
1517 int n = limit ? abs(limit - t.tv_sec) : 0;
1519 h = n / 3600;
1520 m = (n % 3600) / 60;
1521 s = (n % 3600) % 60;
1522 snprintf(buf, sizeof(buf), "%.2i:%.2i:%.2i", h, m, s);
1523 return buf;
1526 static char *time_control_status(struct clock_s *clk)
1528 static char buf[80] = {0};
1530 buf[0] = 0;
1532 if (clk->tc[clk->tcn][0] && clk->tc[clk->tcn + 1][1])
1533 snprintf(buf, sizeof(buf), " M%.2i/%s", abs(clk->tc[clk->tcn][0] - clk->move),
1534 clock_to_char(clk->tc[clk->tcn + 1][1]));
1535 else if (!clk->incr)
1536 return "";
1538 if (clk->incr) {
1539 char buf[16];
1540 strncat(buf, " I", sizeof(buf)-1);
1541 strncat(buf, itoa(clk->incr, buf), sizeof(buf)-1);
1544 return buf;
1547 void update_status_window(GAME g)
1549 int i = 0;
1550 char *buf;
1551 char tmp[15] = {0}, *engine, *mode;
1552 char t[COLS];
1553 int w;
1554 char *p;
1555 int maxy, maxx;
1556 int len;
1557 struct userdata_s *d = g->data;
1558 int y;
1559 int n;
1561 if (!curses_initialized)
1562 return;
1564 getmaxyx(statusw, maxy, maxx);
1565 (void)maxy;
1566 w = maxx - 2 - 8;
1567 len = maxx - 2;
1568 buf = Malloc(len);
1569 y = 2;
1571 wchar_t *loadfilew = loadfile[0] ? str_etc (loadfile, w, 1) : str_to_wchar (_ ("not available"));
1572 mvwprintw(statusw, y++, 1, "%*s %-*ls", 7, _("File:"), w, loadfilew);
1573 free (loadfilew);
1574 snprintf(buf, len, "%i %s %i", gindex + 1, _("of"), gtotal);
1575 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Game:"), w, buf);
1577 *tmp = '\0';
1578 p = tmp;
1580 if (config.details) {
1581 *p++ = 'D';
1582 i++;
1585 if (TEST_FLAG(d->flags, CF_DELETE)) {
1586 if (i)
1587 *p++ = '/';
1589 *p++ = 'X';
1590 i++;
1593 if (TEST_FLAG(g->flags, GF_PERROR)) {
1594 if (i)
1595 *p++ = '/';
1597 *p++ = '!';
1598 i++;
1601 if (TEST_FLAG(d->flags, CF_MODIFIED)) {
1602 if (i)
1603 *p++ = '/';
1605 *p++ = '*';
1606 i++;
1609 pgn_config_get(PGN_STRICT_CASTLING, &n);
1611 if (n == 1) {
1612 if (i)
1613 *p++ = '/';
1615 *p++ = 'C';
1616 i++;
1618 #ifdef WITH_LIBPERL
1619 if (TEST_FLAG(d->flags, CF_PERL)) {
1620 if (i)
1621 *p++ = '/';
1623 *p++ = 'P';
1624 i++;
1626 #endif
1628 *p = '\0';
1629 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Flags:"), w, (tmp[0]) ? tmp : "-");
1631 switch (d->mode) {
1632 case MODE_HISTORY:
1633 mode = _("move history");
1634 break;
1635 case MODE_EDIT:
1636 mode = _("edit");
1637 break;
1638 case MODE_PLAY:
1639 mode = _("play");
1640 break;
1641 default:
1642 mode = _("(empty value)");
1643 break;
1646 snprintf(buf, len - 1, "%*s %s", 7, _("Mode:"), mode);
1648 if (d->mode == MODE_PLAY) {
1649 if (TEST_FLAG(d->flags, CF_HUMAN))
1650 strncat(buf, _(" (human/human)"), len - 1);
1651 else if (TEST_FLAG(d->flags, CF_ENGINE_LOOP))
1652 strncat(buf, _(" (engine/engine)"), len - 1);
1653 else
1654 strncat(buf, (d->play_mode == PLAY_EH) ?
1655 _(" (engine/human)") : _(" (human/engine)"), len - 1);
1658 buf[len-1] = 0;
1659 mvwprintw(statusw, y++, 1, "%-*s", len, buf);
1660 free(buf);
1662 if (d->engine) {
1663 switch (d->engine->status) {
1664 case ENGINE_THINKING:
1665 engine = _("pondering...");
1666 break;
1667 case ENGINE_READY:
1668 engine = _("ready");
1669 break;
1670 case ENGINE_INITIALIZING:
1671 engine = _("initializing...");
1672 break;
1673 case ENGINE_OFFLINE:
1674 engine = _("offline");
1675 break;
1676 default:
1677 engine = _("(empty value)");
1678 break;
1681 else
1682 engine = _("offline");
1684 mvwprintw(statusw, y, 1, "%*s %-*s", 7, _("Engine:"), w, " ");
1685 wattron(statusw, CP_STATUS_ENGINE);
1686 mvwaddstr(statusw, y++, 9, engine);
1687 wattroff(statusw, CP_STATUS_ENGINE);
1689 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Turn:"), w,
1690 (g->turn == WHITE) ? _("white") : _("black"));
1692 strncpy(tmp, _("white"), sizeof(tmp)-1);
1693 tmp[0] = toupper(tmp[0]);
1694 snprintf(t, sizeof(t), "%s%s",
1695 timeval_to_char(d->wclock.elapsed, d->wclock.tc[d->wclock.tcn][1]),
1696 time_control_status(&d->wclock));
1697 mvwprintw(statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1699 strncpy(tmp, _("black"), sizeof(tmp)-1);
1700 tmp[0] = toupper(tmp[0]);
1701 snprintf(t, sizeof(t), "%s%s",
1702 timeval_to_char(d->bclock.elapsed, d->bclock.tc[d->bclock.tcn][1]),
1703 time_control_status(&d->bclock));
1704 mvwprintw(statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1706 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Total:"), w,
1707 clock_to_char(d->elapsed.tv_sec));
1709 // for (i = 0; i < STATUS_WIDTH; i++)
1710 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1712 if (!status.notify)
1713 status.notify = str_to_wchar(_("Type F1 for help"));
1715 wattron(stdscr, CP_STATUS_NOTIFY);
1716 for (i = (config.boardleft) ? BOARD_WIDTH : 0;
1717 i < ((config.boardleft) ? COLS : STATUS_WIDTH); i++)
1718 mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1719 mvwprintw(stdscr, STATUS_HEIGHT, CENTERX(STATUS_WIDTH, status.notify)
1720 + ((config.boardleft) ? BOARD_WIDTH : 0),
1721 "%ls", status.notify);
1722 wattroff(stdscr, CP_STATUS_NOTIFY);
1725 wchar_t *translate_tag_name(const char *tag)
1727 if (!strcmp (tag, "Event"))
1728 return str_to_wchar (translatable_tag_names[0]);
1729 else if (!strcmp (tag, "Site"))
1730 return str_to_wchar (translatable_tag_names[1]);
1731 else if (!strcmp (tag, "Date"))
1732 return str_to_wchar (translatable_tag_names[2]);
1733 else if (!strcmp (tag, "Round"))
1734 return str_to_wchar (translatable_tag_names[3]);
1735 else if (!strcmp (tag, "White"))
1736 return str_to_wchar (translatable_tag_names[4]);
1737 else if (!strcmp (tag, "Black"))
1738 return str_to_wchar (translatable_tag_names[5]);
1739 else if (!strcmp (tag, "Result"))
1740 return str_to_wchar (translatable_tag_names[6]);
1742 return str_to_wchar (tag);
1745 void update_tag_window(TAG **t)
1747 int i, l, w;
1748 int namel = 0;
1750 for (i = 0; t[i]; i++) {
1751 wchar_t *namewc = translate_tag_name(t[i]->name);
1753 l = wcslen(namewc);
1754 free (namewc);
1755 if (l > namel)
1756 namel = l;
1759 w = TAG_WIDTH - namel - 4;
1761 for (i = 0; t[i] && i < TAG_HEIGHT - 3; i++) {
1762 wchar_t *namewc = translate_tag_name(t[i]->name);
1763 wchar_t *valuewc = str_etc(t[i]->value, w, 0);
1765 mvwprintw(tagw, (i + 2), 1, "%*ls: %-*ls", namel, namewc, w, valuewc);
1766 free (namewc);
1767 free (valuewc);
1770 for (; i < TAG_HEIGHT - 3; i++)
1771 mvwprintw(tagw, (i + 2), 1, "%*s", namel + w + 2, " ");
1774 void append_enginebuf(GAME g, char *line)
1776 int i = 0;
1777 struct userdata_s *d = g->data;
1779 if (d->engine->enginebuf)
1780 for (i = 0; d->engine->enginebuf[i]; i++);
1782 if (i >= LINES - 3) {
1783 free(d->engine->enginebuf[0]);
1785 for (i = 0; d->engine->enginebuf[i+1]; i++)
1786 d->engine->enginebuf[i] = d->engine->enginebuf[i+1];
1788 d->engine->enginebuf[i] = strdup(line);
1790 else {
1791 d->engine->enginebuf = Realloc(d->engine->enginebuf, (i + 2) * sizeof(char *));
1792 d->engine->enginebuf[i++] = strdup(line);
1793 d->engine->enginebuf[i] = NULL;
1797 void update_engine_window(GAME g)
1799 int i;
1800 struct userdata_s *d = g->data;
1802 if (!d->engine || !d->engine->enginebuf)
1803 return;
1805 wmove(enginew, 0, 0);
1806 wclrtobot(enginew);
1808 if (d->engine->enginebuf) {
1809 for (i = 0; d->engine->enginebuf[i]; i++)
1810 mvwprintw(enginew, i + 2, 1, "%s", d->engine->enginebuf[i]);
1813 window_draw_title(enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
1814 CP_MESSAGE_BORDER);
1817 void update_all(GAME g)
1819 struct userdata_s *d = g->data;
1822 * In the middle of a macro. Don't update the screen.
1824 if (macro_match != -1)
1825 return;
1828 * No need to update when the engine window is being shown.
1830 if (enginep && panel_hidden(enginep) == ERR) {
1831 update_panels();
1832 doupdate();
1833 return;
1836 wmove(boardw, ROWTOMATRIX(d->c_row), COLTOMATRIX(d->c_col));
1837 update_board_window(g);
1838 update_status_window(g);
1839 update_history_window(g);
1840 update_tag_window(g->tag);
1841 update_engine_window(g);
1842 update_panels();
1843 doupdate();
1846 static void game_next_prev(GAME g, int n, int count)
1848 if (gtotal < 2)
1849 return;
1851 if (n == 1) {
1852 if (gindex + count > gtotal - 1) {
1853 if (count != 1)
1854 gindex = gtotal - 1;
1855 else
1856 gindex = 0;
1858 else
1859 gindex += count;
1861 else {
1862 if (gindex - count < 0) {
1863 if (count != 1)
1864 gindex = 0;
1865 else
1866 gindex = gtotal - 1;
1868 else
1869 gindex -= count;
1872 gp = game[gindex];
1875 static void delete_game(int which)
1877 GAME *g = NULL;
1878 int gi = 0;
1879 int i;
1880 struct userdata_s *d;
1882 for (i = 0; i < gtotal; i++) {
1883 d = game[i]->data;
1885 if (i == which || TEST_FLAG(d->flags, CF_DELETE)) {
1886 free_userdata_once(game[i]);
1887 pgn_free(game[i]);
1888 continue;
1891 g = Realloc(g, (gi + 1) * sizeof(GAME *));
1892 g[gi] = Calloc(1, sizeof(struct game_s));
1893 memcpy(g[gi], game[i], sizeof(struct game_s));
1894 g[gi]->tag = game[i]->tag;
1895 g[gi]->history = game[i]->history;
1896 g[gi]->hp = game[i]->hp;
1897 gi++;
1900 game = g;
1901 gtotal = gi;
1903 if (which != -1) {
1904 if (which + 1 >= gtotal)
1905 gindex = gtotal - 1;
1906 else
1907 gindex = which;
1909 else
1910 gindex = gtotal - 1;
1912 gp = game[gindex];
1913 gp->hp = gp->history;
1917 * FIXME find across multiple games.
1919 static int find_move_exp(GAME g, regex_t r, int which, int count)
1921 int i;
1922 int ret;
1923 char errbuf[255];
1924 int incr;
1925 int found;
1927 incr = (which == 0) ? -1 : 1;
1929 for (i = g->hindex + incr - 1, found = 0; ; i += incr) {
1930 if (i == g->hindex - 1)
1931 break;
1933 if (i >= pgn_history_total(g->hp))
1934 i = 0;
1935 else if (i < 0)
1936 i = pgn_history_total(g->hp) - 1;
1938 // FIXME RAV
1939 ret = regexec(&r, g->hp[i]->move, 0, 0, 0);
1941 if (ret == 0) {
1942 if (count == ++found) {
1943 return i + 1;
1946 else {
1947 if (ret != REG_NOMATCH) {
1948 regerror(ret, &r, errbuf, sizeof(errbuf));
1949 cmessage(_("Error Matching Regular Expression"), ANY_KEY_STR, "%s", errbuf);
1950 return -1;
1955 return -1;
1958 static int toggle_delete_flag(int n)
1960 int i, x;
1961 struct userdata_s *d = game[n]->data;
1963 TOGGLE_FLAG(d->flags, CF_DELETE);
1964 gindex = n;
1966 for (i = x = 0; i < gtotal; i++) {
1967 d = game[i]->data;
1969 if (TEST_FLAG(d->flags, CF_DELETE))
1970 x++;
1973 if (x == gtotal) {
1974 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
1975 d = game[n]->data;
1976 CLEAR_FLAG(d->flags, CF_DELETE);
1977 return 1;
1980 return 0;
1983 static int find_game_exp(char *str, int which, int count)
1985 char *nstr = NULL, *exp = NULL;
1986 regex_t nexp, vexp;
1987 int ret = -1;
1988 int g = 0;
1989 char buf[255] = {0}, *tmp;
1990 char errbuf[255];
1991 int found = 0;
1992 int incr = (which == 0) ? -(1) : 1;
1994 strncpy(buf, str, sizeof(buf)-1);
1995 tmp = buf;
1997 if (strstr(tmp, ":") != NULL) {
1998 nstr = strsep(&tmp, ":");
2000 if ((ret = regcomp(&nexp, nstr,
2001 REG_ICASE|REG_EXTENDED|REG_NOSUB)) != 0) {
2002 regerror(ret, &nexp, errbuf, sizeof(errbuf));
2003 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
2004 ret = g = -1;
2005 goto cleanup;
2009 exp = tmp;
2011 while (*exp && isspace(*exp))
2012 exp++;
2014 if (exp == NULL)
2015 goto cleanup;
2017 if ((ret = regcomp(&vexp, exp, REG_EXTENDED|REG_NOSUB)) != 0) {
2018 regerror(ret, &vexp, errbuf, sizeof(errbuf));
2019 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
2020 ret = -1;
2021 goto cleanup;
2024 ret = -1;
2026 for (g = gindex + incr, found = 0; ; g += incr) {
2027 int t;
2029 if (g == gtotal)
2030 g = 0;
2031 else if (g < 0)
2032 g = gtotal - 1;
2034 if (g == gindex)
2035 break;
2037 for (t = 0; game[g]->tag[t]; t++) {
2038 if (nstr) {
2039 if (regexec(&nexp, game[g]->tag[t]->name, 0, 0, 0) == 0) {
2040 if (regexec(&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0) {
2041 if (count == ++found) {
2042 ret = g;
2043 goto cleanup;
2048 else {
2049 if (regexec(&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0) {
2050 if (count == ++found) {
2051 ret = g;
2052 goto cleanup;
2058 ret = -1;
2061 cleanup:
2062 if (nstr)
2063 regfree(&nexp);
2065 if (g != -1)
2066 regfree(&vexp);
2068 return ret;
2072 * Updates the notification line in the status window then refreshes the
2073 * status window.
2075 void update_status_notify(GAME g, char *fmt, ...)
2077 va_list ap;
2078 #ifdef HAVE_VASPRINTF
2079 char *line;
2080 #else
2081 char line[COLS];
2082 #endif
2084 if (!fmt) {
2085 if (status.notify) {
2086 free(status.notify);
2087 status.notify = NULL;
2090 return;
2093 va_start(ap, fmt);
2094 #ifdef HAVE_VASPRINTF
2095 vasprintf(&line, fmt, ap);
2096 #else
2097 vsnprintf(line, sizeof(line), fmt, ap);
2098 #endif
2099 va_end(ap);
2101 if (status.notify)
2102 free(status.notify);
2104 status.notify = str_to_wchar(line);
2106 #ifdef HAVE_VASPRINTF
2107 free(line);
2108 #endif
2111 int rav_next_prev(GAME g, BOARD b, int n)
2113 // Next RAV.
2114 if (n) {
2115 if ((!g->ravlevel && g->hindex && g->hp[g->hindex - 1]->rav == NULL) ||
2116 (!g->ravlevel && !g->hindex && g->hp[g->hindex]->rav == NULL) ||
2117 (g->ravlevel && g->hp[g->hindex]->rav == NULL))
2118 return 1;
2120 g->rav = Realloc(g->rav, (g->ravlevel + 1) * sizeof(RAV));
2121 g->rav[g->ravlevel].hp = g->hp;
2122 g->rav[g->ravlevel].flags = g->flags;
2123 g->rav[g->ravlevel].fen = pgn_game_to_fen(g, b);
2124 g->rav[g->ravlevel].hindex = g->hindex;
2125 g->hp = (!g->ravlevel) ? (g->hindex) ? g->hp[g->hindex - 1]->rav : g->hp[g->hindex]->rav : g->hp[g->hindex]->rav;
2126 g->hindex = 0;
2127 g->ravlevel++;
2128 pgn_board_update(g, b, g->hindex + 1);
2129 return 0;
2132 if (g->ravlevel - 1 < 0)
2133 return 1;
2135 // Previous RAV.
2136 g->ravlevel--;
2137 pgn_board_init_fen(g, b, g->rav[g->ravlevel].fen);
2138 free(g->rav[g->ravlevel].fen);
2139 g->hp = g->rav[g->ravlevel].hp;
2140 g->flags = g->rav[g->ravlevel].flags;
2141 g->hindex = g->rav[g->ravlevel].hindex;
2142 return 0;
2145 static void draw_window_decor()
2147 move_panel(boardp, 0,
2148 (config.boardleft) ? 0 : COLS - BOARD_WIDTH);
2149 move_panel(historyp, LINES - HISTORY_HEIGHT,
2150 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH : 0 :
2151 (MEGA_BOARD) ? 0 : COLS - HISTORY_WIDTH);
2152 move_panel(statusp, 0,
2153 (config.boardleft) ? BOARD_WIDTH : 0);
2154 move_panel(tagp, STATUS_HEIGHT + 1,
2155 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH :
2156 HISTORY_WIDTH : 0);
2158 wbkgd(boardw, CP_BOARD_WINDOW);
2159 wbkgd(statusw, CP_STATUS_WINDOW);
2160 window_draw_title(statusw, _("Game Status"), STATUS_WIDTH,
2161 CP_STATUS_TITLE, CP_STATUS_BORDER);
2162 wbkgd(tagw, CP_TAG_WINDOW);
2163 window_draw_title(tagw, _("Roster Tags"), TAG_WIDTH, CP_TAG_TITLE,
2164 CP_TAG_BORDER);
2165 wbkgd(historyw, CP_HISTORY_WINDOW);
2166 window_draw_title(historyw, _("Move History"), HISTORY_WIDTH,
2167 CP_HISTORY_TITLE, CP_HISTORY_BORDER);
2170 static void do_window_resize()
2172 if (LINES < 23 || COLS < 74)
2173 return;
2175 resizeterm(LINES, COLS);
2176 wresize(boardw, BOARD_HEIGHT, BOARD_WIDTH);
2177 wresize(historyw, HISTORY_HEIGHT, HISTORY_WIDTH);
2178 wresize(statusw, STATUS_HEIGHT, STATUS_WIDTH);
2179 wresize(tagw, TAG_HEIGHT, TAG_WIDTH);
2180 wmove(boardw, 0, 0);
2181 wclrtobot(boardw);
2182 wmove(historyw, 0, 0);
2183 wclrtobot(historyw);
2184 wmove(tagw, 0, 0);
2185 wclrtobot(tagw);
2186 wmove(statusw, 0, 0);
2187 wclrtobot(statusw);
2188 draw_window_decor();
2189 update_all(gp);
2192 void stop_clock()
2194 memset(&clock_timer, 0, sizeof(struct itimerval));
2195 setitimer(ITIMER_REAL, &clock_timer, NULL);
2198 void start_clock(GAME g)
2200 struct userdata_s *d = g->data;
2202 if (clock_timer.it_interval.tv_usec)
2203 return;
2205 memset(&d->elapsed, 0, sizeof(struct timeval));
2206 clock_timer.it_value.tv_sec = 0;
2207 clock_timer.it_value.tv_usec = 100000;
2208 clock_timer.it_interval.tv_sec = 0;
2209 clock_timer.it_interval.tv_usec = 100000;
2210 setitimer(ITIMER_REAL, &clock_timer, NULL);
2213 static void update_clocks()
2215 int i;
2216 struct userdata_s *d;
2217 struct itimerval it;
2218 int update = 0;
2220 getitimer(ITIMER_REAL, &it);
2222 for (i = 0; i < gtotal; i++) {
2223 d = game[i]->data;
2225 if (d && d->mode == MODE_PLAY) {
2226 if (d->paused == 1 || TEST_FLAG(d->flags, CF_NEW))
2227 continue;
2228 else if (d->paused == -1) {
2229 if (game[i]->side == game[i]->turn) {
2230 d->paused = 1;
2231 continue;
2235 update_clock(game[i], it);
2237 if (game[i] == gp)
2238 update = 1;
2242 if (update) {
2243 update_status_window(gp);
2244 update_panels();
2245 doupdate();
2249 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2251 static int parse_clock_time(char **str)
2253 char *p = *str;
2254 int n = 0, t = 0;
2256 SKIP_SPACE(p);
2258 if (!isdigit(*p))
2259 return -1;
2261 while (*p) {
2262 if (isdigit(*p)) {
2263 t = atoi(p);
2265 while (isdigit(*p))
2266 p++;
2268 continue;
2271 switch (*p) {
2272 case 'H':
2273 case 'h':
2274 n += t * (60 * 60);
2275 t = 0;
2276 break;
2277 case 'M':
2278 case 'm':
2279 n += t * 60;
2280 t = 0;
2281 break;
2282 case 'S':
2283 case 's':
2284 n += t;
2285 t = 0;
2286 break;
2287 case ' ':
2288 p++;
2289 case '/':
2290 case '+':
2291 goto done;
2292 default:
2293 *str = p;
2294 return -1;
2297 p++;
2300 done:
2301 n += t;
2302 *str = p;
2303 return n;
2306 static int parse_clock_input(struct clock_s *clk, char *str, int *incr)
2308 char *p = str;
2309 long n = 0;
2310 int plus = 0;
2311 int m = 0;
2312 int tc = 0;
2314 SKIP_SPACE(p);
2316 if (!*p)
2317 return 0;
2319 if (*p == '+') {
2320 plus = 1;
2321 p++;
2322 SKIP_SPACE(p);
2324 if (*p == '+')
2325 goto move_incr;
2327 else
2328 memset(clk, 0, sizeof(struct clock_s));
2330 again:
2331 /* Sudden death. */
2332 if (strncasecmp(p, "SD", 2) == 0) {
2333 n = 0;
2334 p += 2;
2335 goto tc;
2338 n = parse_clock_time(&p);
2340 if (n == -1)
2341 return 1;
2343 if (!n)
2344 goto done;
2346 /* Time control. */
2348 if (*p == '/') {
2349 if (plus)
2350 return 1;
2352 /* Sudden death without a previous time control. */
2353 if (!n && !tc)
2354 return 1;
2356 m = n;
2357 p++;
2358 n = parse_clock_time(&p);
2360 if (n == -1)
2361 return 1;
2363 if (tc >= MAX_TC) {
2364 message(ERROR_STR, ANY_KEY_STR, "%s (%i)", _("Maximum number of time controls reached"), MAX_TC);
2365 return 1;
2368 clk->tc[tc][0] = m;
2369 clk->tc[tc++][1] = n;
2370 SKIP_SPACE(p);
2372 if (*p == '+')
2373 goto move_incr;
2375 if (*p)
2376 goto again;
2378 goto done;
2381 if (plus)
2382 *incr = n;
2383 else
2384 clk->tc[clk->tcn][1] = (n <= clk->elapsed.tv_sec) ? clk->elapsed.tv_sec + n : n;
2386 move_incr:
2387 if (*p) {
2388 if (*p++ == '+') {
2389 if (!isdigit(*p))
2390 return 1;
2392 n = parse_clock_time(&p);
2394 if (n == -1 || *p)
2395 return 1;
2397 clk->incr = n;
2399 SKIP_SPACE(p);
2401 if (*p)
2402 return 1;
2404 else
2405 return 1;
2408 done:
2409 return 0;
2412 static int parse_which_clock(struct clock_s *clk, char *str)
2414 struct clock_s tmp;
2415 int incr = 0;
2417 memcpy(&tmp, clk, sizeof(struct clock_s));
2419 if (parse_clock_input(&tmp, str, &incr)) {
2420 cmessage(ERROR_STR, ANY_KEY_STR, _("Invalid clock specification"));
2421 return 1;
2424 memcpy(clk, &tmp, sizeof(struct clock_s));
2425 clk->tc[clk->tcn][1] += incr;
2426 return 0;
2429 void do_clock_input_finalize(WIN *win)
2431 struct userdata_s *d = gp->data;
2432 struct input_data_s *in = win->data;
2433 char *p = in->str;
2435 if (!in->str) {
2436 free(in);
2437 return;
2440 SKIP_SPACE(p);
2442 if (tolower(*p) == 'w') {
2443 p++;
2445 if (parse_which_clock(&d->wclock, p))
2446 goto done;
2448 else if (tolower(*p) == 'b') {
2449 p++;
2451 if (parse_which_clock(&d->bclock, p))
2452 goto done;
2454 else {
2455 if (parse_which_clock(&d->wclock, p))
2456 goto done;
2458 if (parse_which_clock(&d->bclock, p))
2459 goto done;
2462 if (!d->wclock.tc[0][1] && !d->bclock.tc[0][1])
2463 CLEAR_FLAG(d->flags, CF_CLOCK);
2464 else
2465 SET_FLAG(d->flags, CF_CLOCK);
2467 done:
2468 free(in->str);
2469 free(in);
2472 void do_engine_command_finalize(WIN *win)
2474 struct userdata_s *d = gp->data;
2475 struct input_data_s *in = win->data;
2476 int x;
2478 if (!in->str) {
2479 free(in);
2480 return;
2483 if (!d->engine)
2484 goto done;
2486 x = d->engine->status;
2487 send_to_engine(gp, -1, "%s\n", in->str);
2488 d->engine->status = x;
2490 done:
2491 free(in->str);
2492 free(in);
2495 void do_board_details()
2497 config.details = (config.details) ? 0 : 1;
2500 void do_toggle_strict_castling()
2502 int n;
2504 pgn_config_get(PGN_STRICT_CASTLING, &n);
2506 if (n == 0)
2507 pgn_config_set(PGN_STRICT_CASTLING, 1);
2508 else
2509 pgn_config_set(PGN_STRICT_CASTLING, 0);
2512 void do_play_set_clock()
2514 struct input_data_s *in;
2516 in = Calloc(1, sizeof(struct input_data_s));
2517 in->efunc = do_clock_input_finalize;
2518 construct_input(_("Set Clock"), NULL, 1, 1,
2519 _ ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n" \
2520 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2521 NULL, NULL, 0, in, INPUT_HIST_CLOCK, -1);
2524 void do_play_toggle_human()
2526 struct userdata_s *d = gp->data;
2528 TOGGLE_FLAG(d->flags, CF_HUMAN);
2530 if (!TEST_FLAG(d->flags, CF_HUMAN) && pgn_history_total(gp->hp)) {
2531 if (init_chess_engine(gp))
2532 return;
2535 CLEAR_FLAG(d->flags, CF_ENGINE_LOOP);
2537 if (d->engine)
2538 d->engine->status = ENGINE_READY;
2541 void do_play_toggle_engine()
2543 struct userdata_s *d = gp->data;
2545 TOGGLE_FLAG(d->flags, CF_ENGINE_LOOP);
2546 CLEAR_FLAG(d->flags, CF_HUMAN);
2548 if (d->engine && TEST_FLAG(d->flags, CF_ENGINE_LOOP)) {
2549 char *fen = pgn_game_to_fen (gp, d->b);
2551 pgn_board_update(gp, d->b,
2552 pgn_history_total(gp->hp));
2553 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
2554 free (fen);
2559 * This will send a command to the engine skipping the command queue.
2561 void do_play_send_command()
2563 struct userdata_s *d = gp->data;
2564 struct input_data_s *in;
2566 if (!d->engine || d->engine->status == ENGINE_OFFLINE) {
2567 if (init_chess_engine(gp))
2568 return;
2571 in = Calloc(1, sizeof(struct input_data_s));
2572 in->efunc = do_engine_command_finalize;
2573 construct_input(_("Engine Command"), NULL, 1, 1, NULL, NULL, NULL, 0, in, INPUT_HIST_ENGINE, -1);
2576 void do_play_switch_turn()
2578 struct userdata_s *d = gp->data;
2580 pgn_switch_side(gp);
2581 pgn_switch_turn(gp);
2583 if (!TEST_FLAG(d->flags, CF_HUMAN))
2584 add_engine_command(gp, -1,
2585 (gp->side == WHITE) ? "white\n" : "black\n");
2587 update_status_window(gp);
2590 void do_play_toggle_eh_mode()
2592 struct userdata_s *d = gp->data;
2594 if (!TEST_FLAG(d->flags, CF_HUMAN)) {
2595 if (!gp->hindex){
2596 pgn_switch_side(gp, TRUE);
2597 d->play_mode = (d->play_mode) ? PLAY_HE : PLAY_EH;
2598 if (gp->side == BLACK)
2599 update_status_notify(gp, _("Press 'g' to start the game"));
2601 rotate = (rotate) ? FALSE : TRUE;
2603 else
2604 message(NULL, ANY_KEY_STR,
2605 _("You may only switch sides at the start of the \n"
2606 "game. Press ^K or ^N to begin a new game."));
2610 void do_play_undo()
2612 struct userdata_s *d = gp->data;
2614 if (!pgn_history_total(gp->hp))
2615 return;
2617 if (keycount) {
2618 if (gp->hindex - keycount < 0)
2619 gp->hindex = 0;
2620 else {
2621 if (go_move)
2622 gp->hindex -= (keycount * 2) -1;
2623 else
2624 gp->hindex -= keycount * 2;
2627 else {
2628 if (gp->hindex - 2 < 0)
2629 gp->hindex = 0;
2630 else {
2631 if (go_move)
2632 gp->hindex -= 1;
2633 else
2634 gp->hindex -= 2;
2638 pgn_history_free(gp->hp, gp->hindex);
2639 gp->hindex = pgn_history_total(gp->hp);
2640 pgn_board_update(gp, d->b, gp->hindex);
2642 if (d->engine && d->engine->status == ENGINE_READY) {
2643 char *fen = pgn_game_to_fen(gp, d->b);
2645 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
2646 free (fen);
2647 d->engine->status = ENGINE_READY;
2650 update_history_window(gp);
2652 if (go_move) {
2653 pgn_switch_side(gp, FALSE);
2654 go_move--;
2658 void do_play_toggle_pause()
2660 struct userdata_s *d = gp->data;
2662 if (!TEST_FLAG(d->flags, CF_HUMAN) && gp->turn !=
2663 gp->side) {
2664 d->paused = -1;
2665 return;
2668 d->paused = (d->paused) ? 0 : 1;
2671 void do_play_go()
2673 struct userdata_s *d = gp->data;
2675 if (TEST_FLAG(d->flags, CF_HUMAN))
2676 return;
2678 if (fm_loaded_file && gp->side != gp->turn) {
2679 pgn_switch_side(gp, FALSE);
2680 add_engine_command(gp, ENGINE_THINKING, "black\n");
2683 add_engine_command(gp, ENGINE_THINKING, "go\n");
2685 // Completa la función para que permita seguir jugando al usarla.
2686 // Complete the function to allow continue playing when using.
2687 if (gp->side == gp->turn)
2688 pgn_switch_side(gp, FALSE);
2690 go_move++;
2693 void do_play_config_command()
2695 int x, w;
2697 if (config.keys) {
2698 for (x = 0; config.keys[x]; x++) {
2699 if (config.keys[x]->c == input_c) {
2700 switch (config.keys[x]->type) {
2701 case KEY_DEFAULT:
2702 add_engine_command(gp, -1, "%s\n",
2703 config.keys[x]->str);
2704 break;
2705 case KEY_SET:
2706 if (!keycount)
2707 break;
2709 add_engine_command(gp, -1,
2710 "%s %i\n", config.keys[x]->str, keycount);
2711 keycount = 0;
2712 break;
2713 case KEY_REPEAT:
2714 if (!keycount)
2715 break;
2717 for (w = 0; w < keycount; w++)
2718 add_engine_command(gp, -1,
2719 "%s\n", config.keys[x]->str);
2720 keycount = 0;
2721 break;
2727 update_status_notify(gp, NULL);
2730 void do_play_cancel_selected()
2732 struct userdata_s *d = gp->data;
2734 d->sp.icon = d->sp.srow = d->sp.scol = 0;
2735 keycount = 0;
2736 pgn_reset_valid_moves(d->b);
2737 update_status_notify(gp, NULL);
2740 void do_play_commit()
2742 struct userdata_s *d = gp->data;
2744 pushkey = keycount = 0;
2745 update_status_notify(gp, NULL);
2747 if (!TEST_FLAG(d->flags, CF_HUMAN) &&
2748 (!d->engine || d->engine->status == ENGINE_THINKING))
2749 return;
2751 if (!d->sp.icon)
2752 return;
2754 d->sp.row = d->c_row;
2755 d->sp.col = d->c_col;
2757 if (rotate) {
2758 rotate_position(SP_POSITION);
2759 rotate_position(SPS_POSITION);
2762 move_to_engine(gp);
2764 // Completa la función para que permita seguir jugando cuando se carga un
2765 // archivo pgn (con juego no terminado) que inicie con turno del lado
2766 // negro.
2767 // Complete the function to allow continue playing when loading a file
2768 // pgn (with unfinished game) you start to turn black side.
2769 if (gp->side != gp->turn)
2770 pgn_switch_side(gp, FALSE);
2772 if (rotate && d->sp.icon)
2773 rotate_position(SPS_POSITION);
2775 // Envia comando 'go' a Polyglot en el primer movimiento debido a que
2776 // polyglot no envia el movimiento y cboard se queda esperando.
2777 // Send command 'go' to the first movement Polyglot because cboard
2778 // waits to send polyglot movement and this does not make.
2779 if (config.fmpolyglot &&
2780 ((gp->side == WHITE && !gp->hindex) || fm_loaded_file))
2781 add_engine_command(gp, ENGINE_THINKING, "go\n");
2783 go_move = 0;
2784 fm_loaded_file = FALSE;
2787 void do_play_select()
2789 struct userdata_s *d = gp->data;
2791 if (!TEST_FLAG(d->flags, CF_HUMAN) && (!d->engine ||
2792 d->engine->status == ENGINE_OFFLINE)) {
2793 if (init_chess_engine(gp))
2794 return;
2797 if (d->engine && d->engine->status == ENGINE_THINKING)
2798 return;
2800 if (d->sp.icon)
2801 do_play_cancel_selected ();
2803 if (rotate)
2804 rotate_position(CURSOR_POSITION);
2806 d->sp.icon = d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon;
2808 if (pgn_piece_to_int(d->sp.icon) == OPEN_SQUARE) {
2809 d->sp.icon = 0;
2810 return;
2813 if (((islower(d->sp.icon) && gp->turn != BLACK)
2814 || (isupper(d->sp.icon) && gp->turn != WHITE))) {
2815 struct key_s **k;
2816 char *str = Malloc (512);
2818 for (k = play_keys; *k; k++) {
2819 if ((*k)->f == do_play_toggle_eh_mode)
2820 break;
2823 snprintf (str, 512, _("It is not your turn to move. You may switch playing sides by pressing \"%lc\"."),
2824 *k ? (*k)->c : '?');
2825 message(NULL, ANY_KEY_STR, "%s", str);
2826 free (str);
2827 d->sp.icon = 0;
2828 return;
2829 #if 0
2830 if (pgn_history_total(gp->hp)) {
2831 message(NULL, ANY_KEY_STR, "%s", _("It is not your turn to move. You can switch sides "));
2832 d->sp.icon = 0;
2833 return;
2835 else {
2836 if (pgn_tag_find(gp->tag, "FEN") != E_PGN_ERR)
2837 return;
2839 add_engine_command(gp, ENGINE_READY, "black\n");
2840 pgn_switch_turn(gp);
2842 if (gp->side != BLACK)
2843 pgn_switch_side(gp);
2845 #endif
2848 d->sp.srow = d->c_row;
2849 d->sp.scol = d->c_col;
2851 if (config.validmoves)
2852 pgn_find_valid_moves(gp, d->b, d->sp.scol, d->sp.srow);
2854 if (rotate) {
2855 rotate_position(CURSOR_POSITION);
2856 rotate_position(SPS_POSITION);
2859 CLEAR_FLAG(d->flags, CF_NEW);
2860 start_clock(gp);
2863 /* FIXME: keys with the same function should comma deliminated. */
2864 static wchar_t *build_help(struct key_s **keys)
2866 int i, nlen = 1, len, t, n;
2867 wchar_t *buf = NULL, *wc = NULL;
2868 wchar_t *p;
2870 if (!keys)
2871 return NULL;
2873 for (i = len = t = 0; keys[i]; i++) {
2874 if (!keys[i]->d)
2875 continue;
2877 if (keys[i]->key) {
2878 if (wcslen(keys[i]->key) > nlen) {
2879 nlen = wcslen(keys[i]->key);
2880 t += nlen;
2882 else
2883 t++;
2886 if (keys[i]->d) {
2887 if (wcslen(keys[i]->d) > len)
2888 len = wcslen(keys[i]->d);
2891 t += len;
2892 t += keys[i]->r;
2895 t += 4 + i;
2896 buf = Malloc(t*sizeof(wchar_t));
2897 p = buf;
2899 for (i = 0; keys[i]; i++) {
2900 if (!keys[i]->d)
2901 continue;
2903 if (keys[i]->key)
2904 n = wcslen(keys[i]->key);
2905 else
2906 n = 1;
2908 while (n++ <= nlen)
2909 *p++ = ' ';
2911 *p = 0;
2913 if (keys[i]->key) {
2914 wcscat(buf, keys[i]->key);
2915 p = buf + wcslen(buf);
2917 else
2918 *p++ = keys[i]->c;
2920 *p++ = ' ';
2921 *p++ = '-';
2922 *p++ = ' ';
2923 *p = 0;
2925 if (keys[i]->d)
2926 wcscat(buf, keys[i]->d);
2928 if (keys[i]->r) {
2929 wc = str_to_wchar ("*");
2930 wcscat(buf, wc);
2931 free (wc);
2934 wc = str_to_wchar ("\n");
2935 wcscat(buf, wc);
2936 free (wc);
2937 p = buf + wcslen(buf);
2940 return buf;
2943 void do_more_help(WIN *);
2944 void do_main_help(WIN *win)
2946 wchar_t *buf;
2948 switch (win->c) {
2949 case 'p':
2950 buf = build_help(play_keys);
2951 construct_message(_("Play Mode Keys (* = can take a repeat count)"), ANY_KEY_STR, 0, 0,
2952 NULL, NULL, buf, do_more_help, 0, 1, "%ls", buf);
2953 break;
2954 case 'h':
2955 buf = build_help(history_keys);
2956 construct_message(_("History Mode Keys (* = can take a repeat count)"), ANY_KEY_STR, 0, 0,
2957 NULL, NULL, buf, do_more_help, 0, 1, "%ls", buf);
2958 break;
2959 case 'e':
2960 buf = build_help(edit_keys);
2961 construct_message(_("Edit Mode Keys (* = can take a repeat count)"), ANY_KEY_STR, 0, 0,
2962 NULL, NULL, buf, do_more_help, 0, 1, "%ls", buf);
2963 break;
2964 case 'g':
2965 buf = build_help(global_keys);
2966 construct_message(_("Global Game Keys (* = can take a repeat count)"), ANY_KEY_STR, 0, 0,
2967 NULL, NULL, buf, do_more_help, 0, 1, "%ls", buf);
2968 break;
2969 default:
2970 break;
2974 void do_more_help(WIN *win)
2976 if (win->c == KEY_F(1) || win->c == CTRL_KEY('g'))
2977 construct_message(_("Command Key Index"),
2978 _ ("p/h/e/g or any other key to quit"), 0, 0,
2979 NULL, NULL, NULL, do_main_help, 0, 0, "%s",
2981 "p - play mode keys\n"
2982 "h - history mode keys\n"
2983 "e - board edit mode keys\n"
2984 "g - global game keys"
2988 void do_play_help()
2990 wchar_t *buf = build_help(play_keys);
2992 construct_message(_("Play Mode Keys (* = can take a repeat count)"),
2993 ANY_KEY_STR, 0, 0, NULL, NULL,
2994 buf, do_more_help, 0, 1, "%ls", buf);
2997 void do_play_history_mode()
2999 struct userdata_s *d = gp->data;
3001 if (!pgn_history_total(gp->hp) ||
3002 (d->engine && d->engine->status == ENGINE_THINKING))
3003 return;
3005 d->mode = MODE_HISTORY;
3006 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3009 void do_play_edit_mode()
3011 struct userdata_s *d = gp->data;
3013 if (pgn_history_total(gp->hp))
3014 return;
3016 pgn_board_init_fen(gp, d->b, NULL);
3017 config.details++;
3018 d->mode = MODE_EDIT;
3021 void do_edit_insert_finalize(WIN *win)
3023 struct userdata_s *d = win->data;
3025 if (pgn_piece_to_int(win->c) == -1)
3026 return;
3028 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon = win->c;
3031 void do_edit_select()
3033 struct userdata_s *d = gp->data;
3035 if (d->sp.icon)
3036 return;
3038 d->sp.icon = d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon;
3040 if (pgn_piece_to_int(d->sp.icon) == OPEN_SQUARE) {
3041 d->sp.icon = 0;
3042 return;
3045 d->sp.srow = d->c_row;
3046 d->sp.scol = d->c_col;
3049 void do_edit_commit()
3051 int p;
3052 struct userdata_s *d = gp->data;
3054 pushkey = keycount = 0;
3055 update_status_notify(gp, NULL);
3057 if (!d->sp.icon)
3058 return;
3060 d->sp.row = d->c_row;
3061 d->sp.col = d->c_col;
3062 p = d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon;
3063 d->b[RANKTOBOARD(d->sp.row)][FILETOBOARD(d->sp.col)].icon = p;
3064 d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon =
3065 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3066 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3069 void do_edit_delete()
3071 struct userdata_s *d = gp->data;
3073 if (d->sp.icon)
3074 d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon =
3075 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3076 else
3077 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon =
3078 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3080 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3083 void do_edit_cancel_selected()
3085 struct userdata_s *d = gp->data;
3087 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3088 keycount = 0;
3089 update_status_notify(gp, NULL);
3092 void do_edit_switch_turn()
3094 pgn_switch_turn(gp);
3097 void do_edit_toggle_castle()
3099 struct userdata_s *d = gp->data;
3101 castling_state(gp, d->b, RANKTOBOARD(d->c_row),
3102 FILETOBOARD(d->c_col),
3103 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon, 1);
3106 void do_edit_insert()
3108 struct userdata_s *d = gp->data;
3110 construct_message(_("Insert Piece"), _("P=pawn, R=rook, N=knight, B=bishop, "), 0, 0, NULL, NULL,
3111 d->b, do_edit_insert_finalize, 0, 0, "%s", _("Type the piece letter to insert. Lowercase "));
3114 void do_edit_enpassant()
3116 struct userdata_s *d = gp->data;
3118 if (d->c_row == 6 || d->c_row == 3) {
3119 pgn_reset_enpassant(d->b);
3120 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].enpassant = 1;
3124 void do_edit_help()
3126 wchar_t *buf = build_help(edit_keys);
3128 construct_message(_("Edit Mode Keys (* = can take a repeat count)"),
3129 ANY_KEY_STR, 0, 0, NULL, NULL,
3130 buf, do_more_help, 0, 1, "%ls", buf);
3133 void do_edit_exit()
3135 struct userdata_s *d = gp->data;
3136 char *fen = pgn_game_to_fen(gp, d->b);
3138 config.details--;
3139 pgn_tag_add(&gp->tag, "FEN", fen);
3140 free (fen);
3141 pgn_tag_add(&gp->tag, "SetUp", "1");
3142 pgn_tag_sort(gp->tag);
3143 pgn_board_update(gp, d->b, gp->hindex);
3144 d->mode = MODE_PLAY;
3147 void really_do_annotate_finalize(struct input_data_s *in,
3148 struct userdata_s *d)
3150 HISTORY *h = in->data;
3151 int len;
3153 if (!in->str) {
3154 if (h->comment) {
3155 free(h->comment);
3156 h->comment = NULL;
3159 else {
3160 len = strlen(in->str);
3161 h->comment = Realloc(h->comment, len+1);
3162 strncpy(h->comment, in->str, len);
3163 h->comment[len] = 0;
3166 free(in->str);
3167 free(in);
3168 SET_FLAG(d->flags, CF_MODIFIED);
3171 void do_annotate_finalize(WIN *win)
3173 struct userdata_s *d = gp->data;
3174 struct input_data_s *in = win->data;
3176 really_do_annotate_finalize(in, d);
3179 void do_find_move_exp_finalize(int init, int which)
3181 int n;
3182 struct userdata_s *d = gp->data;
3183 static int firstrun;
3184 static regex_t r;
3185 int ret;
3186 char errbuf[255];
3188 if (init || !firstrun) {
3189 if (!firstrun)
3190 regfree(&r);
3192 if ((ret = regcomp(&r, moveexp, REG_EXTENDED|REG_NOSUB)) != 0) {
3193 regerror(ret, &r, errbuf, sizeof(errbuf));
3194 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
3195 return;
3198 firstrun = 1;
3201 if ((n = find_move_exp(gp, r,
3202 (which == -1) ? 0 : 1, (keycount) ? keycount : 1)) == -1)
3203 return;
3205 gp->hindex = n;
3206 pgn_board_update(gp, d->b, gp->hindex);
3209 void do_find_move_exp(WIN *win)
3211 struct input_data_s *in = win->data;
3212 int *n = in->data;
3213 int which = *n;
3215 if (in->str) {
3216 strncpy(moveexp, in->str, sizeof(moveexp)-1);
3217 moveexp[sizeof(moveexp)-1] = 0;
3218 do_find_move_exp_finalize(1, which);
3219 free(in->str);
3222 free(in->data);
3223 free(in);
3226 void do_move_jump_finalize(int n)
3228 struct userdata_s *d = gp->data;
3230 if (n < 0 || n > (pgn_history_total(gp->hp) / 2))
3231 return;
3233 keycount = 0;
3234 update_status_notify(gp, NULL);
3235 gp->hindex = (n) ? n * 2 - 1 : n * 2;
3236 pgn_board_update(gp, d->b, gp->hindex);
3239 void do_move_jump(WIN *win)
3241 struct input_data_s *in = win->data;
3243 if (!in->str || !isinteger(in->str)) {
3244 if (in->str)
3245 free(in->str);
3247 free(in);
3248 return;
3251 do_move_jump_finalize(atoi(in->str));
3252 free(in->str);
3253 free(in);
3256 struct history_menu_s {
3257 char *line;
3258 int hindex;
3259 int ravlevel;
3260 int move;
3261 int indent;
3264 void free_history_menu_data(struct history_menu_s **h)
3266 int i;
3268 if (!h)
3269 return;
3271 for (i = 0; h[i]; i++) {
3272 free(h[i]->line);
3273 free(h[i]);
3276 free(h);
3279 void get_history_data(HISTORY **hp, struct history_menu_s ***menu, int m,
3280 int turn)
3282 int i, n = 0;
3283 int t = pgn_history_total(hp);
3284 char buf[MAX_SAN_MOVE_LEN + 4];
3285 static int depth;
3286 struct history_menu_s **hmenu = *menu;
3288 if (hmenu)
3289 for (n = 0; hmenu[n]; n++);
3290 else
3291 depth = 0;
3293 for (i = 0; i < t; i++) {
3294 hmenu = Realloc(hmenu, (n + 2) * sizeof(struct history_menu_s *));
3295 hmenu[n] = Malloc(sizeof(struct history_menu_s));
3296 snprintf(buf, sizeof(buf), "%c%s%s", (turn == WHITE) ? 'W' : 'B',
3297 hp[i]->move, (hp[i]->comment || hp[i]->nag[0]) ? " !" : "");
3298 hmenu[n]->line = strdup(buf);
3299 hmenu[n]->hindex = i;
3300 hmenu[n]->indent = 0;
3301 hmenu[n]->ravlevel = depth;
3302 hmenu[n]->move = (n && depth > hmenu[n-1]->ravlevel) ? m++ : m;
3303 n++;
3304 hmenu[n] = NULL;
3306 #if 0
3307 if (hp[i]->rav) {
3308 depth++;
3309 get_history_data(hp[i]->rav, &hmenu, m, turn);
3310 for (n = 0; hmenu[n]; n++);
3311 depth--;
3313 if (depth)
3314 m--;
3316 #endif
3318 turn = (turn == WHITE) ? BLACK : WHITE;
3321 *menu = hmenu;
3324 void history_draw_update(struct menu_input_s *m)
3326 GAME g = m->data;
3327 struct userdata_s *d = g->data;
3329 g->hindex = m->selected + 1;
3330 update_cursor(g, m->selected);
3331 pgn_board_update(g, d->b, m->selected + 1);
3334 struct menu_item_s **get_history_items(WIN *win)
3336 struct menu_input_s *m = win->data;
3337 GAME g = m->data;
3338 struct userdata_s *d = g->data;
3339 struct history_menu_s **hm = d->data;
3340 struct menu_item_s **items = m->items;
3341 int i;
3343 if (!hm) {
3344 get_history_data(g->history, &hm, 0,
3345 TEST_FLAG(g->flags, GF_BLACK_OPENING));
3346 m->selected = g->hindex - 1;
3348 if (m->selected < 0)
3349 m->selected = 0;
3351 m->draw_exit_func = history_draw_update;
3354 d->data = hm;
3356 if (items) {
3357 for (i = 0; items[i]; i++)
3358 free(items[i]);
3360 free(items);
3361 items = NULL;
3364 for (i = 0; hm[i]; i++) {
3365 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
3366 items[i] = Malloc(sizeof(struct menu_item_s));
3367 items[i]->name = hm[i]->line;
3368 items[i]->value = NULL;
3369 items[i]->selected = 0;
3372 if (items)
3373 items[i] = NULL;
3375 m->nofree = 1;
3376 m->items = items;
3377 return items;
3380 void history_menu_quit(struct menu_input_s *m)
3382 pushkey = -1;
3385 void history_menu_exit(WIN *win)
3387 GAME g = win->data;
3388 struct userdata_s *d = g->data;
3389 struct history_menu_s **hm = d->data;
3390 int i;
3392 if (!hm)
3393 return;
3395 for (i = 0; hm[i]; i++) {
3396 free(hm[i]->line);
3397 free(hm[i]);
3400 free(hm);
3401 d->data = NULL;
3404 // FIXME RAV
3405 void history_menu_next(struct menu_input_s *m)
3407 GAME g = m->data;
3408 struct userdata_s *d = g->data;
3409 struct history_menu_s **hm = d->data;
3410 int n, t;
3412 for (t = 0; hm[t]; t++);
3414 if (m->selected + 1 == t)
3415 n = 0;
3416 else
3417 n = hm[m->selected + 1]->hindex;
3419 n++;
3420 g->hindex = n;
3423 // FIXME RAV
3424 void history_menu_prev(struct menu_input_s *m)
3426 GAME g = m->data;
3427 struct userdata_s *d = g->data;
3428 struct history_menu_s **hm = d->data;
3429 int n, t;
3431 for (t = 0; hm[t]; t++);
3433 if (m->selected - 1 < 0)
3434 n = t - 1;
3435 else
3436 n = hm[m->selected - 1]->hindex;
3438 n++;
3439 g->hindex = n;
3442 void history_menu_help(struct menu_input_s *m)
3444 message("History Menu Help", ANY_KEY_STR, "%s",
3446 " UP/DOWN - previous/next menu item\n"
3447 " HOME/END - first/last menu item\n"
3448 " PGDN/PGUP - next/previous page\n"
3449 " a-zA-Z0-9 - jump to item\n"
3450 " CTRL-a - annotate the selected move\n"
3451 " ENTER - view annotation\n"
3452 " CTRL-d - toggle board details\n"
3453 " ESCAPE/M - return to move history"
3457 void do_annotate_move(HISTORY *hp)
3459 char buf[COLS - 4];
3460 struct input_data_s *in;
3462 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Editing Annotation for"), hp->move);
3463 in = Calloc(1, sizeof(struct input_data_s));
3464 in->data = hp;
3465 in->efunc = do_annotate_finalize;
3466 construct_input(buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3467 _("Type CTRL-t to edit NAG"), edit_nag, NULL, CTRL_KEY('T'), in, -1, -1);
3470 void history_menu_view_annotation(struct menu_input_s *m)
3472 GAME g = m->data;
3474 // FIXME RAV
3475 view_annotation(g->history[m->selected]);
3478 void history_menu_annotate_finalize(WIN *win)
3480 struct input_data_s *in = win->data;
3481 GAME g = in->moredata;
3482 struct userdata_s *d = g->data;
3483 struct history_menu_s **hm = d->data;
3485 really_do_annotate_finalize(in, d);
3486 free_history_menu_data(hm);
3487 hm = NULL;
3488 get_history_data(g->history, &hm, 0, TEST_FLAG(g->flags, GF_BLACK_OPENING));
3489 d->data = hm;
3490 pushkey = REFRESH_MENU;
3493 void history_menu_annotate(struct menu_input_s *m)
3495 GAME g = m->data;
3496 char buf[COLS - 4];
3497 struct input_data_s *in;
3498 HISTORY *hp = g->history[m->selected]; // FIXME RAV
3500 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Editing Annotation for"), hp->move);
3501 in = Calloc(1, sizeof(struct input_data_s));
3502 in->data = hp;
3503 in->moredata = m->data;
3504 in->efunc = history_menu_annotate_finalize;
3505 construct_input(buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3506 _("Type CTRL-t to edit NAG"), edit_nag, NULL, CTRL_KEY('T'), in, -1, -1);
3509 void history_menu_details(struct menu_input_s *m)
3511 do_board_details();
3514 // FIXME RAV
3515 void history_menu_print(WIN *win)
3517 struct menu_input_s *m = win->data;
3518 GAME g = m->data;
3519 struct userdata_s *d = g->data;
3520 struct history_menu_s **hm = d->data;
3521 struct history_menu_s *h = hm[m->top];
3522 int i;
3523 char *p = m->item->name;
3524 int line = m->print_line - 2;
3526 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
3527 * attr_t data type.
3529 attr_t attrs;
3530 short pair;
3531 int total;
3533 for (total = 0; hm[total]; total++);
3534 wattr_get(win->w, &attrs, &pair, NULL);
3535 wattroff(win->w, COLOR_PAIR(pair));
3536 mvwaddch(win->w, m->print_line, 1,
3537 *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));
3538 p++;
3540 if (h->hindex == 0 && line == 0)
3541 waddch(win->w, ACS_ULCORNER | CP_HISTORY_MENU_LG);
3542 else if ((!hm[h->hindex + (win->rows - 5) + 1] && line == win->rows - 5) ||
3543 (m->top + line == total - 1))
3544 waddch(win->w, ACS_LLCORNER | CP_HISTORY_MENU_LG);
3545 else if (hm[m->top + 1]->ravlevel != h->ravlevel || !h->ravlevel)
3546 waddch(win->w, ACS_LTEE | CP_HISTORY_MENU_LG);
3547 else
3548 waddch(win->w, ACS_VLINE | CP_HISTORY_MENU_LG);
3550 wattron(win->w, COLOR_PAIR(pair) | attrs);
3552 for (i = 2; *p; p++, i++)
3553 waddch(win->w, (*p == '!') ? *p | A_BOLD : *p);
3555 while (i++ < win->cols - 2)
3556 waddch(win->w, ' ');
3559 void history_menu(GAME g)
3561 struct menu_key_s **keys = NULL;
3563 add_menu_key(&keys, KEY_ESCAPE, history_menu_quit);
3564 add_menu_key(&keys, 'M', history_menu_quit);
3565 add_menu_key(&keys, KEY_UP, history_menu_prev);
3566 add_menu_key(&keys, KEY_DOWN, history_menu_next);
3567 add_menu_key(&keys, KEY_F(1), history_menu_help);
3568 add_menu_key(&keys, CTRL_KEY('a'), history_menu_annotate);
3569 add_menu_key(&keys, CTRL_KEY('d'), history_menu_details);
3570 add_menu_key(&keys, '\n', history_menu_view_annotation);
3571 construct_menu(LINES, TAG_WIDTH, 0, config.boardleft ? BOARD_WIDTH : 0,
3572 _("Move History Tree"), 1, get_history_items, keys, g,
3573 history_menu_print, history_menu_exit);
3576 void do_history_menu()
3578 history_menu(gp);
3581 void do_history_half_move_toggle()
3583 movestep = (movestep == 1) ? 2 : 1;
3584 update_history_window(gp);
3587 void do_history_rotate_board()
3589 rotate = (rotate) ? FALSE : TRUE;
3592 void do_history_jump_next()
3594 struct userdata_s *d = gp->data;
3596 pgn_history_next(gp, d->b, (keycount > 0) ?
3597 config.jumpcount * keycount * movestep :
3598 config.jumpcount * movestep);
3601 void do_history_jump_prev()
3603 struct userdata_s *d = gp->data;
3605 pgn_history_prev(gp, d->b, (keycount) ?
3606 config.jumpcount * keycount * movestep :
3607 config.jumpcount * movestep);
3610 void do_history_prev()
3612 struct userdata_s *d = gp->data;
3614 pgn_history_prev(gp, d->b,
3615 (keycount) ? keycount * movestep : movestep);
3618 void do_history_next()
3620 struct userdata_s *d = gp->data;
3622 pgn_history_next(gp, d->b, (keycount) ?
3623 keycount * movestep : movestep);
3626 void do_history_mode_finalize(struct userdata_s *d)
3628 pushkey = 0;
3629 d->mode = MODE_PLAY;
3632 void do_history_mode_confirm(WIN *win)
3634 struct userdata_s *d = gp->data;
3635 wchar_t str[] = { win->c, 0 };
3637 if (!wcscmp (str, resume_wchar)) {
3638 pgn_history_free(gp->hp, gp->hindex);
3639 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3641 #if 0
3642 case 'C':
3643 case 'c':
3644 if (pgn_history_rav_new(gp, d->b,
3645 gp->hindex) != E_PGN_OK)
3646 return;
3648 break;
3649 #endif
3650 else
3651 return;
3653 if (!TEST_FLAG(d->flags, CF_HUMAN)) {
3654 char *fen = pgn_game_to_fen(gp, d->b);
3656 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
3657 free (fen);
3660 do_history_mode_finalize(d);
3663 void do_history_toggle()
3665 struct userdata_s *d = gp->data;
3667 // FIXME Resuming from previous history could append to a RAV.
3668 if (gp->hindex != pgn_history_total(gp->hp)) {
3669 if (!pushkey)
3670 construct_message(NULL, _ ("What would you like to do?"), 0, 1,
3671 NULL, NULL, NULL, do_history_mode_confirm, 0, 0,
3672 _("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."),
3673 resume_wchar);
3674 return;
3676 else {
3677 if (TEST_FLAG(gp->flags, GF_GAMEOVER))
3678 return;
3681 if (gp->side != gp->turn) {
3682 d->play_mode = PLAY_EH;
3684 else {
3685 d->play_mode = PLAY_HE;
3686 rotate = FALSE;
3689 do_history_mode_finalize(d);
3692 void do_history_annotate()
3694 int n = gp->hindex;
3696 if (n && gp->hp[n - 1]->move)
3697 n--;
3698 else
3699 return;
3701 do_annotate_move(gp->hp[n]);
3704 void do_history_help()
3706 wchar_t *buf = build_help(history_keys);
3708 construct_message(_("History Mode Keys (* = can take a repeat count)"),
3709 ANY_KEY_STR, 0, 0, NULL, NULL,
3710 buf, do_more_help, 0, 1, "%ls", buf);
3713 void do_history_find(int which)
3715 struct input_data_s *in;
3716 int *p;
3718 if (pgn_history_total(gp->hp) < 2)
3719 return;
3721 in = Calloc(1, sizeof(struct input_data_s));
3722 p = Malloc(sizeof(int));
3723 *p = which;
3724 in->data = p;
3725 in->efunc = do_find_move_exp;
3727 if (!*moveexp || which == 0) {
3728 construct_input(_("Find Move Text Expression"), NULL, 1, 0, NULL, NULL, NULL,
3729 0, in, INPUT_HIST_MOVE_EXP, -1);
3730 return;
3733 do_find_move_exp_finalize(0, which);
3736 void do_history_find_new()
3738 do_history_find(0);
3741 void do_history_find_prev()
3743 do_history_find(-1);
3746 void do_history_find_next()
3748 do_history_find(1);
3751 void do_history_rav(int which)
3753 struct userdata_s *d = gp->data;
3755 rav_next_prev(gp, d->b, which);
3758 void do_history_rav_next()
3760 do_history_rav(1);
3763 void do_history_rav_prev()
3765 do_history_rav(0);
3768 void do_history_jump()
3770 struct input_data_s *in;
3772 if (pgn_history_total(gp->hp) < 2)
3773 return;
3775 if (!keycount) {
3776 in = Calloc(1, sizeof(struct input_data_s));
3777 in->efunc = do_move_jump;
3779 construct_input(_("Jump to Move Number"), NULL, 1, 1, NULL,
3780 NULL, NULL, 0, in, -1, 0);
3781 return;
3784 do_move_jump_finalize(keycount);
3787 static void free_userdata_once(GAME g)
3789 struct userdata_s *d = g->data;
3791 if (!d)
3792 return;
3794 if (d->engine) {
3795 stop_engine(g);
3797 if (d->engine->enginebuf) {
3798 int n;
3800 for (n = 0; d->engine->enginebuf[n]; n++)
3801 free(d->engine->enginebuf[n]);
3803 free(d->engine->enginebuf);
3806 if (d->engine->queue) {
3807 struct queue_s **q;
3809 for (q = d->engine->queue; *q; q++)
3810 free(*q);
3812 free(d->engine->queue);
3815 free(d->engine);
3818 #ifdef WITH_LIBPERL
3819 if (d->perlfen)
3820 free(d->perlfen);
3822 if (d->oldfen)
3823 free(d->oldfen);
3824 #endif
3826 free(d);
3827 g->data = NULL;
3830 static void free_userdata()
3832 int i;
3834 for (i = 0; i < gtotal; i++) {
3835 free_userdata_once(game[i]);
3836 game[i]->data = NULL;
3840 void update_loading_window(int n)
3842 char buf[16];
3844 if (!loadingw) {
3845 loadingw = newwin(3, COLS / 2, CALCPOSY(3), CALCPOSX(COLS / 2));
3846 loadingp = new_panel(loadingw);
3847 wbkgd(loadingw, CP_MESSAGE_WINDOW);
3850 wmove(loadingw, 0, 0);
3851 wclrtobot(loadingw);
3852 wattron(loadingw, CP_MESSAGE_BORDER);
3853 box(loadingw, ACS_VLINE, ACS_HLINE);
3854 wattroff(loadingw, CP_MESSAGE_BORDER);
3855 mvwprintw(loadingw, 1, CENTER_INT((COLS / 2), 11 +
3856 strlen(itoa(gtotal, buf))),
3857 _("Loading... %i%% (%i games)"), n, gtotal);
3858 update_panels();
3859 doupdate();
3862 static void init_userdata_once(GAME g, int n)
3864 struct userdata_s *d = NULL;
3866 d = Calloc(1, sizeof(struct userdata_s));
3867 d->n = n;
3868 d->c_row = 2, d->c_col = 5;
3869 SET_FLAG(d->flags, CF_NEW);
3870 g->data = d;
3872 if (pgn_board_init_fen(g, d->b, NULL) != E_PGN_OK)
3873 pgn_board_init(d->b);
3876 void init_userdata()
3878 int i;
3880 for (i = 0; i < gtotal; i++)
3881 init_userdata_once(game[i], i);
3884 void fix_marks(int *start, int *end)
3886 int i;
3888 *start = (*start < 0) ? 0 : *start;
3889 *end = (*end < 0) ? 0 : *end;
3891 if (*start > *end) {
3892 i = *start;
3893 *start = *end;
3894 *end = i + 1;
3897 *end = (*end > gtotal) ? gtotal : *end;
3900 void do_new_game_finalize(GAME g)
3902 struct userdata_s *d = g->data;
3904 d->mode = MODE_PLAY;
3905 update_status_notify(g, NULL);
3908 void do_new_game_from_scratch(WIN *win)
3910 wchar_t str[] = { win->c, 0 };
3912 if (wcscmp (str, yes_wchar))
3913 return;
3915 stop_clock();
3916 free_userdata();
3917 pgn_parse(NULL);
3918 gp = game[gindex];
3919 add_custom_tags(&gp->tag);
3920 init_userdata();
3921 loadfile[0] = 0;
3922 do_new_game_finalize(gp);
3923 rotate = FALSE;
3926 void do_new_game()
3928 pgn_new_game();
3929 gp = game[gindex];
3930 add_custom_tags(&gp->tag);
3931 init_userdata_once(gp, gindex);
3932 do_new_game_finalize(gp);
3933 rotate = FALSE;
3936 void do_game_delete_finalize(int n)
3938 struct userdata_s *d;
3940 delete_game((!n) ? gindex : -1);
3941 d = gp->data;
3942 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3945 void do_game_delete_confirm(WIN *win)
3947 int *n;
3948 wchar_t str[] = { win->c, 0 };
3950 if (wcscmp (str, yes_wchar)) {
3951 free(win->data);
3952 return;
3955 n = (int *)win->data;
3956 do_game_delete_finalize(*n);
3957 free(win->data);
3960 void do_game_delete()
3962 char *tmp = NULL;
3963 int i, n;
3964 struct userdata_s *d;
3965 int *p;
3967 if (gtotal < 2) {
3968 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
3969 return;
3972 tmp = NULL;
3974 for (i = n = 0; i < gtotal; i++) {
3975 d = game[i]->data;
3977 if (TEST_FLAG(d->flags, CF_DELETE))
3978 n++;
3981 if (!n)
3982 tmp = _("Delete the current game?");
3983 else {
3984 if (n == gtotal) {
3985 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
3986 return;
3989 tmp = _("Delete all games marked for deletion?");
3992 if (config.deleteprompt) {
3993 p = Malloc(sizeof(int));
3994 *p = n;
3995 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, p,
3996 do_game_delete_confirm, 0, 0, tmp);
3997 return;
4000 do_game_delete_finalize(n);
4003 void do_find_game_exp_finalize(int which)
4005 struct userdata_s *d = gp->data;
4006 int n;
4008 if ((n = find_game_exp(gameexp, (which == -1) ? 0 : 1,
4009 (keycount) ? keycount : 1)) == -1) {
4010 update_status_notify(gp, "%s", _("No matches found"));
4011 return;
4014 gindex = n;
4015 d = gp->data;
4017 if (pgn_history_total(gp->hp))
4018 d->mode = MODE_HISTORY;
4020 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4023 void do_find_game_exp(WIN *win)
4025 struct input_data_s *in = win->data;
4026 int *n = in->data;
4027 int c = *n;
4029 if (in->str) {
4030 strncpy(gameexp, in->str, sizeof(gameexp));
4031 gameexp[sizeof(gameexp)-1] = 0;
4033 if (c == '?')
4034 c = '}';
4036 do_find_game_exp_finalize(c);
4037 free(in->str);
4040 free(in->data);
4041 free(in);
4044 void do_game_jump_finalize(int n)
4046 struct userdata_s *d;
4048 if (--n > gtotal - 1 || n < 0)
4049 return;
4051 gindex = n;
4052 gp = game[gindex];
4053 d = gp->data;
4054 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4055 update_status_notify(gp, NULL);
4058 void do_game_jump(WIN *win)
4060 struct input_data_s *in = win->data;
4062 if (!in->str || !isinteger(in->str)) {
4063 if (in->str)
4064 free(in->str);
4066 free(in);
4067 return;
4070 do_game_jump_finalize(atoi(in->str));
4071 free(in->str);
4072 free(in);
4075 void do_load_file(WIN *win)
4077 struct input_data_s *in = win->data;
4078 char *tmp = in->str;
4079 struct userdata_s *d;
4080 PGN_FILE *pgn = NULL;
4081 int n;
4083 if (!in->str) {
4084 free(in);
4085 return;
4088 if ((tmp = pathfix(tmp)) == NULL)
4089 goto done;
4091 n = pgn_open(tmp, "r", &pgn);
4093 if (n == E_PGN_ERR) {
4094 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, strerror(errno));
4095 goto done;
4097 else if (n == E_PGN_INVALID) {
4098 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", tmp, _("Not a regular file"));
4099 goto done;
4102 free_userdata();
4104 if (pgn_parse(pgn) == E_PGN_ERR) {
4105 del_panel(loadingp);
4106 delwin(loadingw);
4107 loadingw = NULL;
4108 loadingp = NULL;
4109 init_userdata();
4110 goto done;
4113 del_panel(loadingp);
4114 delwin(loadingw);
4115 loadingw = NULL;
4116 loadingp = NULL;
4117 init_userdata();
4118 strncpy(loadfile, tmp, sizeof(loadfile));
4119 loadfile[sizeof(loadfile)-1] = 0;
4120 gp = game[gindex];
4121 d = gp->data;
4123 if (pgn_history_total(gp->hp))
4124 d->mode = MODE_HISTORY;
4126 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4128 fm_loaded_file = TRUE;
4130 done:
4131 pgn_close(pgn);
4133 if (in->str)
4134 free(in->str);
4136 free(in);
4139 void do_game_save(WIN *win)
4141 struct input_data_s *in = win->data;
4142 int *x = in->data;
4143 int n = *x;
4144 char *tmp = in->str;
4145 char tfile[FILENAME_MAX];
4146 char *p;
4147 int i;
4148 struct userdata_s *d;
4150 if (!tmp || (tmp = pathfix(tmp)) == NULL)
4151 goto done;
4153 if (pgn_is_compressed(tmp) == E_PGN_ERR) {
4154 p = tmp + strlen(tmp) - 1;
4156 if (*p != 'n' || *(p-1) != 'g' || *(p-2) != 'p' ||
4157 *(p-3) != '.') {
4158 snprintf(tfile, sizeof(tfile), "%s.pgn", tmp);
4159 tmp = tfile;
4164 * When in edit mode, update the FEN tag.
4166 if (n == -1) {
4167 for (i = 0; i < gtotal; i++) {
4168 d = game[i]->data;
4170 if (d->mode == MODE_EDIT) {
4171 char *fen = pgn_game_to_fen(game[i], d->b);
4173 pgn_tag_add(&game[i]->tag, "FEN", fen);
4174 free (fen);
4178 else {
4179 d = game[n]->data;
4181 if (d->mode == MODE_EDIT) {
4182 char *fen = pgn_game_to_fen(game[n], d->b);
4184 pgn_tag_add(&game[n]->tag, "FEN", fen);
4185 free (fen);
4189 save_pgn(tmp, n);
4191 done:
4192 if (in->str)
4193 free(in->str);
4195 free(in->data);
4196 free(in);
4199 void do_get_game_save_input(int n)
4201 struct input_data_s *in = Calloc(1, sizeof(struct input_data_s));
4202 int *p = Malloc(sizeof(int));
4204 in->efunc = do_game_save;
4205 *p = n;
4206 in->data = p;
4208 construct_input(_("Save Game Filename"), loadfile, 1, 1, _("Type TAB for file browser"),
4209 file_browser, NULL, '\t', in, INPUT_HIST_FILE, -1);
4212 void do_game_save_multi_confirm(WIN *win)
4214 int i;
4215 wchar_t str[] = { win->c, 0 };
4217 if (!wcscmp (str, current_wchar))
4218 i = gindex;
4219 else if (!wcscmp (str, all_wchar))
4220 i = -1;
4221 else {
4222 update_status_notify(gp, "%s", _("Save game aborted."));
4223 return;
4226 do_get_game_save_input(i);
4229 void do_global_about()
4231 cmessage("ABOUT", ANY_KEY_STR, "%s\nUsing %s with %i colors "
4232 "and %i color pairs\n%s",
4233 PACKAGE_STRING, curses_version(), COLORS, COLOR_PAIRS,
4234 COPYRIGHT);
4237 void global_game_next_prev(int which)
4239 struct userdata_s *d;
4241 game_next_prev(gp, (which == 1) ? 1 : 0,
4242 (keycount) ? keycount : 1);
4243 d = gp->data;
4245 if (delete_count) {
4246 if (which == 1) {
4247 markend = markstart + delete_count;
4248 delete_count = 0;
4250 else {
4251 markend = markstart - delete_count + 1;
4252 delete_count = -1; // to fix gindex in the other direction
4255 fix_marks(&markstart, &markend);
4256 do_global_toggle_delete();
4259 if (d->mode == MODE_HISTORY)
4260 pgn_board_update(gp, d->b, gp->hindex);
4261 else if (d->mode == MODE_PLAY)
4262 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4265 void do_global_next_game()
4267 global_game_next_prev(1);
4270 void do_global_prev_game()
4272 global_game_next_prev(0);
4275 void global_find(int which)
4277 struct input_data_s *in;
4278 int *p;
4280 if (gtotal < 2)
4281 return;
4283 in = Calloc(1, sizeof(struct input_data_s));
4284 p = Malloc(sizeof(int));
4285 *p = which;
4286 in->data = p;
4287 in->efunc = do_find_game_exp;
4289 if (!*gameexp || which == 0) {
4290 construct_input(_("Find Game by Tag Expression"), NULL, 1, 0,
4291 _("[name expression:]value expression"), NULL, NULL, 0, in,
4292 INPUT_HIST_GAME_EXP, -1);
4293 return;
4296 do_find_game_exp_finalize(which);
4299 void do_global_find_new()
4301 global_find(0);
4304 void do_global_find_next()
4306 global_find(1);
4309 void do_global_find_prev()
4311 global_find(-1);
4314 void do_global_game_jump()
4316 if (gtotal < 2)
4317 return;
4319 if (!keycount) {
4320 struct input_data_s *in;
4322 in = Calloc(1, sizeof(struct input_data_s));
4323 in->efunc = do_game_jump;
4324 construct_input(_("Jump to Game Number"), NULL, 1, 1, NULL, NULL, NULL, 0, in,
4325 -1, 0);
4326 return;
4329 do_game_jump_finalize(keycount);
4332 void do_global_toggle_delete()
4334 int i;
4336 pushkey = 0;
4338 if (gtotal < 2)
4339 return;
4341 if (keycount && delete_count == 0) {
4342 markstart = gindex;
4343 delete_count = keycount;
4344 update_status_notify(gp, "%s (delete)", status.notify);
4345 return;
4348 if (markstart >= 0 && markend >= 0) {
4349 for (i = markstart; i < markend; i++) {
4350 if (toggle_delete_flag(i)) {
4351 return;
4355 gindex = (delete_count < 0) ? markstart : i - 1;
4357 else {
4358 if (toggle_delete_flag(gindex))
4359 return;
4362 markstart = markend = -1;
4363 delete_count = 0;
4364 update_status_window(gp);
4367 void do_global_delete_game()
4369 do_game_delete();
4372 void do_global_tag_edit()
4374 struct userdata_s *d = gp->data;
4376 edit_tags(gp, d->b, 1);
4379 void do_global_tag_view()
4381 struct userdata_s *d = gp->data;
4383 edit_tags(gp, d->b, 0);
4386 void do_global_resume_game()
4388 struct input_data_s *in;
4390 in = Calloc(1, sizeof(struct input_data_s));
4391 in->efunc = do_load_file;
4392 construct_input(_("Load Filename"), NULL, 1, 1, _("Type TAB for file browser"), file_browser,
4393 NULL, '\t', in, INPUT_HIST_FILE, -1);
4396 void do_global_save_game()
4398 if (gtotal > 1) {
4399 construct_message(NULL, _("What would you like to do?"), 0, 1,
4400 NULL, NULL, NULL, do_game_save_multi_confirm, 0, 0,
4401 _("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."),
4402 current_wchar, all_wchar);
4403 return;
4406 do_get_game_save_input(-1);
4409 void do_global_new_game()
4411 do_new_game();
4414 void do_global_copy_game()
4416 int g = gindex;
4417 int i, n;
4418 struct userdata_s *d;
4420 do_global_new_game();
4421 d = gp->data;
4422 n = pgn_tag_total(game[g]->tag);
4424 for (i = 0; i < n; i++)
4425 pgn_tag_add(&gp->tag, game[g]->tag[i]->name,
4426 game[g]->tag[i]->value);
4428 pgn_board_init_fen (gp, d->b, NULL);
4429 n = pgn_history_total(game[g]->history);
4431 // FIXME RAV
4432 for (i = 0; i < n; i++) {
4433 char *frfr = NULL;
4434 char move[MAX_SAN_MOVE_LEN+1] = {0}, *m = move;
4436 strcpy (move, game[g]->history[i]->move);
4437 if (pgn_parse_move(gp, d->b, &m, &frfr) != E_PGN_OK) {
4438 SET_FLAG(gp->flags, GF_PERROR);
4439 return;
4442 pgn_history_add(gp, d->b, move);
4443 free(frfr);
4444 pgn_switch_turn(gp);
4447 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4450 void do_global_new_all()
4452 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
4453 do_new_game_from_scratch, 0, 0, "%s", _("Really start a new game from scratch?"));
4456 void do_quit(WIN *win)
4458 wchar_t str[] = { win->c, 0 };
4460 if (wcscmp (str, yes_wchar))
4461 return;
4463 quit = 1;
4466 void do_global_quit()
4468 if (config.exitdialogbox)
4469 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
4470 do_quit, 0, 0, _("Want to Quit?"));
4471 else
4472 quit = 1;
4475 void do_global_toggle_engine_window()
4477 if (!enginew) {
4478 enginew = newwin(LINES, COLS, 0, 0);
4479 enginep = new_panel(enginew);
4480 window_draw_title(enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
4481 CP_MESSAGE_BORDER);
4482 hide_panel(enginep);
4485 if (panel_hidden(enginep)) {
4486 update_engine_window(gp);
4487 top_panel(enginep);
4489 else {
4490 hide_panel(enginep);
4494 void do_global_toggle_board_details()
4496 do_board_details();
4499 void do_global_toggle_strict_castling()
4501 do_toggle_strict_castling();
4504 // Global and other keys.
4505 static int globalkeys()
4507 struct userdata_s *d = gp->data;
4508 int i;
4511 * These cannot be modified and other game mode keys cannot conflict with
4512 * these.
4514 switch (input_c) {
4515 case CTRL_KEY('L'):
4516 endwin();
4517 keypad(boardw, TRUE);
4518 wmove(stdscr, 0, 0);
4519 wclrtobot(stdscr);
4520 return 1;
4521 case KEY_ESCAPE:
4522 d->sp.icon = d->sp.srow = d->sp.scol = 0;
4523 markend = markstart = 0;
4525 if (keycount) {
4526 keycount = 0;
4527 update_status_notify(gp, NULL);
4530 if (config.validmoves)
4531 pgn_reset_valid_moves(d->b);
4533 return 1;
4534 case '0' ... '9':
4535 i = input_c - '0';
4537 if (keycount)
4538 keycount = keycount * 10 + i;
4539 else
4540 keycount = i;
4542 update_status_notify(gp, _("Repeat %i"), keycount);
4543 return -1;
4544 case KEY_UP:
4545 if (d->mode == MODE_HISTORY)
4546 return 0;
4548 if (keycount)
4549 d->c_row += keycount;
4550 else
4551 d->c_row++;
4553 if (d->c_row > 8)
4554 d->c_row = 1;
4556 return 1;
4557 case KEY_DOWN:
4558 if (d->mode == MODE_HISTORY)
4559 return 0;
4561 if (keycount) {
4562 d->c_row -= keycount;
4563 update_status_notify(gp, NULL);
4565 else
4566 d->c_row--;
4568 if (d->c_row < 1)
4569 d->c_row = 8;
4571 return 1;
4572 case KEY_LEFT:
4573 if (d->mode == MODE_HISTORY)
4574 return 0;
4576 if (keycount)
4577 d->c_col -= keycount;
4578 else
4579 d->c_col--;
4581 if (d->c_col < 1)
4582 d->c_col = 8;
4584 return 1;
4585 case KEY_RIGHT:
4586 if (d->mode == MODE_HISTORY)
4587 return 0;
4589 if (keycount)
4590 d->c_col += keycount;
4591 else
4592 d->c_col++;
4594 if (d->c_col > 8)
4595 d->c_col = 1;
4597 return 1;
4598 case KEY_RESIZE:
4599 return 1;
4600 case 0:
4601 default:
4602 for (i = 0; global_keys[i]; i++) {
4603 if (input_c == global_keys[i]->c) {
4604 (*global_keys[i]->f)();
4605 return 1;
4608 break;
4611 return 0;
4614 #ifdef WITH_LIBPERL
4615 static void perl_error(const char *fmt, ...)
4617 va_list ap;
4618 char *buf;
4620 va_start(ap, fmt);
4621 vasprintf(&buf, fmt, ap);
4622 va_end(ap);
4624 message(ERROR_STR, ANY_KEY_STR, "%s", buf);
4625 free(buf);
4628 static void do_perl_finalize(WIN *win)
4630 struct input_data_s *in = win->data;
4631 GAME g = in->data;
4632 struct userdata_s *d = g->data;
4633 char *filename;
4634 char *result = NULL;
4635 char *arg = NULL;
4636 int n;
4638 asprintf(&filename, "%s/perl.pl", config.datadir);
4640 if (!in->str)
4641 goto done;
4643 if (perl_init_file(filename, perl_error))
4644 goto done;
4646 arg = pgn_game_to_fen(g, d->b);
4648 if (perl_call_sub(trim(in->str), arg, &result))
4649 goto done;
4651 d->perlfen = pgn_game_to_fen(g, d->b);
4652 d->perlflags = g->flags;
4654 if (pgn_board_init_fen(g, d->b, result) != E_PGN_OK) {
4655 message(ERROR_STR, ANY_KEY_STR, "%s", _("FEN parse error."));
4656 pgn_board_init_fen(g, d->b, d->perlfen);
4657 g->flags = d->perlflags;
4658 free(d->perlfen);
4659 d->perlfen = NULL;
4660 goto done;
4663 SET_FLAG(d->flags, CF_PERL);
4664 n = pgn_tag_find(g->tag, "FEN");
4666 if (n != E_PGN_ERR)
4667 d->oldfen = strdup(g->tag[n]->value);
4669 pgn_tag_add(&g->tag, "FEN", result);
4670 update_status_notify(g, "%s", ANY_KEY_STR);
4671 update_all(g);
4673 done:
4674 free(result);
4675 free(arg);
4676 free(in->str);
4677 free(in);
4678 free(filename);
4681 void do_global_perl()
4683 struct input_data_s *in;
4685 in = Calloc(1, sizeof(struct input_data_s));
4686 in->data = gp;
4687 in->efunc = do_perl_finalize;
4688 construct_input(_("PERL Subroutine Filter"), NULL, 1, 0, NULL, NULL, NULL, 0, in, INPUT_HIST_PERL, -1);
4690 #endif
4693 * A macro may contain a key that belongs to another macro so macro_match will
4694 * need to be updated to the new index of the matching macro.
4696 static void find_macro(struct userdata_s *d)
4698 int i;
4701 * Macros can't contain macros when in a window.
4703 if (wins)
4704 return;
4706 again:
4707 for (i = 0; macros[i]; i++) {
4708 if ((macros[i]->mode == -1 || macros[i]->mode == d->mode) &&
4709 input_c == macros[i]->c) {
4710 input_c = macros[i]->keys[macros[i]->n++];
4712 if (!macro_depth_n && macro_match > -1) {
4713 macro_depth = realloc(macro_depth, (macro_depth_n + 1) * sizeof(int));
4714 macro_depth[macro_depth_n++] = macro_match;
4717 macro_depth = realloc(macro_depth, (macro_depth_n + 1) * sizeof(int));
4718 macro_depth[macro_depth_n++] = i;
4719 macro_match = i;
4720 goto again;
4726 * Resets the position in each macro to the first key.
4728 static void reset_macros()
4730 int i;
4731 struct userdata_s *d = gp->data;
4733 again:
4734 if (macro_depth_n > 0) {
4735 macro_depth_n--;
4736 macro_match = macro_depth[macro_depth_n];
4738 if (macros[macro_match]->n >= macros[macro_match]->total)
4739 goto again;
4741 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
4742 find_macro(d);
4743 return;
4746 for (i = 0; macros[i]; i++)
4747 macros[i]->n = 0;
4749 free(macro_depth);
4750 macro_depth = NULL;
4751 macro_depth_n = 0;
4752 macro_match = -1;
4755 void game_loop()
4757 struct userdata_s *d;
4759 macro_match = -1;
4760 gindex = gtotal - 1;
4761 gp = game[gindex];
4762 d = gp->data;
4764 if (pgn_history_total(gp->hp))
4765 d->mode = MODE_HISTORY;
4766 else {
4767 d->mode = MODE_PLAY;
4768 d->play_mode = PLAY_HE;
4771 if (d->mode == MODE_HISTORY)
4772 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4774 update_status_notify(gp, "%s", _("Type F1 for help"));
4775 movestep = 2;
4776 flushinp();
4777 update_all(gp);
4778 wtimeout(boardw, WINDOW_TIMEOUT);
4780 while (!quit) {
4781 int n = 0, i;
4782 char fdbuf[8192] = {0};
4783 int len;
4784 struct timeval tv = {0, 0};
4785 fd_set rfds, wfds;
4786 WIN *win = NULL;
4787 WINDOW *wp = NULL;
4789 FD_ZERO(&rfds);
4790 FD_ZERO(&wfds);
4792 for (i = 0; i < gtotal; i++) {
4793 d = game[i]->data;
4795 if (d->engine && d->engine->pid != -1) {
4796 if (d->engine->fd[ENGINE_IN_FD] > 2) {
4797 if (d->engine->fd[ENGINE_IN_FD] > n)
4798 n = d->engine->fd[ENGINE_IN_FD];
4800 FD_SET(d->engine->fd[ENGINE_IN_FD], &rfds);
4803 if (d->engine->fd[ENGINE_OUT_FD] > 2) {
4804 if (d->engine->fd[ENGINE_OUT_FD] > n)
4805 n = d->engine->fd[ENGINE_OUT_FD];
4807 FD_SET(d->engine->fd[ENGINE_OUT_FD], &wfds);
4812 if (n) {
4813 if ((n = select(n + 1, &rfds, &wfds, NULL, &tv)) > 0) {
4814 for (i = 0; i < gtotal; i++) {
4815 d = game[i]->data;
4817 if (d->engine && d->engine->pid != -1) {
4818 if (FD_ISSET(d->engine->fd[ENGINE_IN_FD], &rfds)) {
4819 len = read(d->engine->fd[ENGINE_IN_FD], fdbuf,
4820 sizeof(fdbuf));
4822 if (len > 0) {
4823 if (d->engine->iobuf)
4824 d->engine->iobuf = Realloc(d->engine->iobuf, d->engine->len + len + 1);
4825 else
4826 d->engine->iobuf = Calloc(1, len + 1);
4828 memcpy(&(d->engine->iobuf[d->engine->len]), &fdbuf, len);
4829 d->engine->len += len;
4830 d->engine->iobuf[d->engine->len] = 0;
4833 * The fdbuf is full or no newline
4834 * was found. So we'll append the next
4835 * read() to this games buffer.
4837 if (d->engine->iobuf[d->engine->len - 1] != '\n')
4838 continue;
4840 parse_engine_output(game[i], d->engine->iobuf);
4841 free(d->engine->iobuf);
4842 d->engine->iobuf = NULL;
4843 d->engine->len = 0;
4845 else if (len == -1) {
4846 if (errno != EAGAIN) {
4847 cmessage(ERROR_STR, ANY_KEY_STR, "Engine read(): %s",
4848 strerror(errno));
4849 waitpid(d->engine->pid, &n, 0);
4850 free(d->engine);
4851 d->engine = NULL;
4852 break;
4857 if (FD_ISSET(d->engine->fd[ENGINE_OUT_FD], &wfds)) {
4858 if (d->engine->queue)
4859 send_engine_command(game[i]);
4864 else {
4865 if (n == -1)
4866 cmessage(ERROR_STR, ANY_KEY_STR, "select(): %s", strerror(errno));
4867 /* timeout */
4871 gp = game[gindex];
4872 d = gp->data;
4875 * This is needed to detect terminal resizing.
4877 doupdate();
4878 if (LINES != LINES_OLD || COLS != COLS_OLD) {
4879 COLS_OLD = COLS;
4880 LINES_OLD = LINES;
4881 do_window_resize();
4885 * Finds the top level window in the window stack so we know what
4886 * window the wget_wch()'ed key belongs to.
4888 if (wins) {
4889 for (i = 0; wins[i]; i++);
4890 win = wins[i-1];
4891 wp = win->w;
4892 wtimeout(wp, WINDOW_TIMEOUT);
4894 else
4895 wp = boardw;
4897 if (!i && pushkey)
4898 input_c = pushkey;
4899 else {
4900 if (!pushkey) {
4901 if (macros && macro_match >= 0) {
4902 if (macros[macro_match]->n >= macros[macro_match]->total)
4903 reset_macros();
4904 else {
4905 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
4906 find_macro(d);
4909 else {
4910 if (wget_wch(wp, &input_c) == ERR || input_c == KEY_RESIZE)
4911 continue;
4914 else
4915 input_c = pushkey;
4917 if (win) {
4918 switch (input_c) {
4919 case CTRL_KEY('L'):
4920 endwin();
4921 keypad(boardw, TRUE);
4922 wmove(stdscr, 0, 0);
4923 wclrtobot(stdscr);
4924 goto refresh;
4927 win->c = input_c;
4930 * Run the function associated with the window. When the
4931 * function returns 0 win->efunc is ran (if not NULL) with
4932 * win as the one and only parameter. Then the window is
4933 * destroyed.
4935 * The exit function may create another window which will
4936 * mess up the window stack when window_destroy() is called.
4937 * So don't destory the window until the top window is
4938 * destroyable. See window_destroy().
4940 if ((*win->func)(win) == 0) {
4941 if (win->efunc)
4942 (*win->efunc)(win);
4944 win->keep = 1;
4945 window_destroy(win);
4946 update_all(gp);
4949 continue;
4953 if (!keycount && status.notify)
4954 update_status_notify(gp, NULL);
4956 #ifdef WITH_LIBPERL
4957 if (TEST_FLAG(d->flags, CF_PERL)) {
4958 CLEAR_FLAG(d->flags, CF_PERL);
4959 pgn_board_init_fen(gp, d->b, d->perlfen);
4960 gp->flags = d->perlflags;
4961 free(d->perlfen);
4962 pgn_tag_add(&gp->tag, "FEN", d->oldfen);
4963 free(d->oldfen);
4964 d->perlfen = d->oldfen = NULL;
4965 update_all(gp);
4966 continue;
4968 #endif
4970 if (macros && macro_match < 0)
4971 find_macro(d);
4973 if ((n = globalkeys()) == 1) {
4974 if (macro_match == -1)
4975 keycount = 0;
4977 goto refresh;
4979 else if (n == -1)
4980 goto refresh;
4982 switch (d->mode) {
4983 case MODE_EDIT:
4984 for (i = 0; edit_keys[i]; i++) {
4985 if (input_c == edit_keys[i]->c) {
4986 (*edit_keys[i]->f)();
4987 break;
4990 break;
4991 case MODE_PLAY:
4992 for (i = 0; play_keys[i]; i++) {
4993 if (input_c == play_keys[i]->c) {
4994 (*play_keys[i]->f)();
4995 goto done;
4999 do_play_config_command();
5000 break;
5001 case MODE_HISTORY:
5002 for (i = 0; history_keys[i]; i++) {
5003 if (input_c == history_keys[i]->c) {
5004 (*history_keys[i]->f)();
5005 break;
5008 break;
5009 default:
5010 break;
5013 done:
5014 if (keycount)
5015 update_status_notify(gp, NULL);
5017 keycount = 0;
5019 refresh:
5020 update_all(gp);
5024 void usage(const char *pn, int ret)
5026 fprintf((ret) ? stderr : stdout, "%s", _(
5027 #ifdef DEBUG
5028 "Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
5029 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"
5030 #else
5031 "Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"
5032 #endif
5033 " -p Load PGN file.\n"
5034 " -V Validate a game file.\n"
5035 " -S Validate and output a PGN formatted game.\n"
5036 " -R Like -S but write a reduced PGN formatted game.\n"
5037 " -t Also write custom PGN tags from config file.\n"
5038 " -E Stop processing on file parsing error (overrides config).\n"
5039 " -C Enable strict castling (overrides config).\n"
5040 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, moverrides config).\n"
5041 " -v Version information.\n"
5042 " -h This help text.\n"));
5044 exit(ret);
5047 void cleanup_all()
5049 int i;
5051 stop_clock();
5052 free_userdata();
5053 pgn_free_all();
5054 free(config.engine_cmd);
5055 free(config.pattern);
5056 free(config.ccfile);
5057 free(config.nagfile);
5058 free(config.configfile);
5060 if (config.keys) {
5061 for (i = 0; config.keys[i]; i++) {
5062 free(config.keys[i]->str);
5063 free(config.keys[i]);
5066 free(config.keys);
5069 if (config.einit) {
5070 for (i = 0; config.einit[i]; i++)
5071 free(config.einit[i]);
5073 free(config.einit);
5076 if (config.tag)
5077 pgn_tag_free(config.tag);
5079 free(config.datadir);
5081 if (curses_initialized) {
5082 del_panel(boardp);
5083 del_panel(historyp);
5084 del_panel(statusp);
5085 del_panel(tagp);
5086 delwin(boardw);
5087 delwin(historyw);
5088 delwin(statusw);
5089 delwin(tagw);
5091 if (enginew) {
5092 del_panel(enginep);
5093 delwin(enginew);
5096 endwin();
5099 #ifdef WITH_LIBPERL
5100 perl_cleanup();
5101 #endif
5104 static void signal_save_pgn(int sig)
5106 char *buf;
5107 time_t now;
5108 char *p = config.savedirectory ? config.savedirectory : config.datadir;
5110 time(&now);
5111 asprintf(&buf, "%s/signal-%i-%li.pgn", p, sig, now);
5113 if (do_game_write(buf, "w", 0, gtotal)) {
5114 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", p, strerror(errno));
5115 update_status_notify(gp, "%s", _("Save game failed."));
5118 free(buf);
5119 quit = 1;
5122 void catch_signal(int which)
5124 switch (which) {
5125 case SIGALRM:
5126 update_clocks();
5127 break;
5128 case SIGPIPE:
5129 if (which == SIGPIPE && quit)
5130 break;
5132 if (which == SIGPIPE)
5133 cmessage(NULL, ANY_KEY_STR, "%s", _("Broken pipe. Quitting."));
5135 cleanup_all();
5136 exit(EXIT_FAILURE);
5137 break;
5138 case SIGSTOP:
5139 savetty();
5140 break;
5141 case SIGCONT:
5142 resetty();
5143 keypad(boardw, TRUE);
5144 curs_set(0);
5145 cbreak();
5146 noecho();
5147 break;
5148 case SIGINT:
5149 quit = 1;
5150 break;
5151 case SIGTERM:
5152 signal_save_pgn(which);
5153 break;
5154 default:
5155 break;
5159 void loading_progress(long total, long offset)
5161 int n = (100 * (offset / 100) / (total / 100));
5163 if (curses_initialized)
5164 update_loading_window(n);
5165 else {
5166 fprintf(stderr, _("Loading... %i%% (%i games)%c"), n, gtotal, '\r');
5167 fflush(stderr);
5171 static void set_defaults()
5173 set_config_defaults();
5174 set_default_keys();
5175 filetype = FILE_NONE;
5176 pgn_config_set(PGN_PROGRESS, 1024);
5177 pgn_config_set(PGN_PROGRESS_FUNC, loading_progress);
5180 int main(int argc, char *argv[])
5182 int opt;
5183 struct stat st;
5184 char buf[FILENAME_MAX];
5185 char datadir[FILENAME_MAX];
5186 int ret = EXIT_SUCCESS;
5187 int validate_only = 0, validate_and_write = 0;
5188 int write_custom_tags = 0;
5189 int i = 0;
5190 PGN_FILE *pgn;
5191 int utf8_pieces = -1;
5193 setlocale (LC_ALL, "");
5194 bindtextdomain ("cboard", LOCALE_DIR);
5195 textdomain ("cboard");
5197 /* Solaris 5.9 */
5198 #ifndef HAVE_PROGNAME
5199 __progname = argv[0];
5200 #endif
5202 if ((config.pwd = getpwuid(getuid())) == NULL)
5203 err(EXIT_FAILURE, "getpwuid()");
5205 snprintf(datadir, sizeof(datadir), "%s/.cboard", config.pwd->pw_dir);
5206 config.datadir = strdup(datadir);
5207 snprintf(buf, sizeof(buf), "%s/cc.data", datadir);
5208 config.ccfile = strdup(buf);
5209 snprintf(buf, sizeof(buf), "%s/nag.data", datadir);
5210 config.nagfile = strdup(buf);
5211 snprintf(buf, sizeof(buf), "%s/config", datadir);
5212 config.configfile = strdup(buf);
5214 if (stat(datadir, &st) == -1) {
5215 if (errno == ENOENT) {
5216 if (mkdir(datadir, 0755) == -1)
5217 err(EXIT_FAILURE, "%s", datadir);
5219 else
5220 err(EXIT_FAILURE, "%s", datadir);
5222 stat(datadir, &st);
5225 if (!S_ISDIR(st.st_mode))
5226 errx(EXIT_FAILURE, "%s: %s", datadir, _("Not a directory."));
5228 set_defaults();
5230 #ifdef DEBUG
5231 while ((opt = getopt(argc, argv, "DCEVtSRhp:vu::")) != -1) {
5232 #else
5233 while ((opt = getopt(argc, argv, "ECVtSRhp:vu::")) != -1) {
5234 #endif
5235 switch (opt) {
5236 #ifdef DEBUG
5237 case 'D':
5238 unlink("libchess.debug");
5239 pgn_config_set(PGN_DEBUG, 1);
5240 break;
5241 #endif
5242 case 'C':
5243 pgn_config_set(PGN_STRICT_CASTLING, 1);
5244 break;
5245 case 't':
5246 write_custom_tags = 1;
5247 break;
5248 case 'E':
5249 i = 1;
5250 break;
5251 case 'R':
5252 pgn_config_set(PGN_REDUCED, 1);
5253 case 'S':
5254 validate_and_write = 1;
5255 case 'V':
5256 validate_only = 1;
5257 break;
5258 case 'v':
5259 printf("%s (%s)\n%s\n", PACKAGE_STRING, curses_version(),
5260 COPYRIGHT);
5261 exit(EXIT_SUCCESS);
5262 case 'p':
5263 filetype = FILE_PGN;
5264 strncpy(loadfile, optarg, sizeof(loadfile));
5265 loadfile[sizeof(loadfile)-1] = 0;
5266 break;
5267 case 'u':
5268 utf8_pieces = optarg ? atoi (optarg): 1;
5269 break;
5270 case 'h':
5271 default:
5272 usage(argv[0], EXIT_SUCCESS);
5276 if ((validate_only || validate_and_write) && !*loadfile)
5277 usage(argv[0], EXIT_FAILURE);
5279 if (access(config.configfile, R_OK) == 0)
5280 parse_rcfile(config.configfile);
5282 if (i)
5283 pgn_config_set(PGN_STOP_ON_ERROR, 1);
5285 signal(SIGPIPE, catch_signal);
5286 signal(SIGCONT, catch_signal);
5287 signal(SIGSTOP, catch_signal);
5288 signal(SIGINT, catch_signal);
5289 signal(SIGALRM, catch_signal);
5290 signal(SIGTERM, catch_signal);
5292 srandom(getpid());
5294 switch (filetype) {
5295 case FILE_PGN:
5296 if (pgn_open(loadfile, "r", &pgn) != E_PGN_OK)
5297 err(EXIT_FAILURE, "%s", loadfile);
5299 ret = pgn_parse(pgn);
5300 pgn_close(pgn);
5301 break;
5302 case FILE_FEN:
5303 //ret = parse_fen_file(loadfile);
5304 break;
5305 case FILE_EPD: // Not implemented.
5306 case FILE_NONE:
5307 default:
5308 // No file specified. Empty game.
5309 ret = pgn_parse(NULL);
5310 gp = game[gindex];
5311 add_custom_tags(&gp->tag);
5312 break;
5315 if (validate_only || validate_and_write) {
5316 if (validate_and_write) {
5317 if (pgn_open("-", "r", &pgn) != E_PGN_OK)
5318 err(EXIT_FAILURE, "pgn_open()");
5320 for (i = 0; i < gtotal; i++) {
5321 if (write_custom_tags)
5322 add_custom_tags(&game[i]->tag);
5324 pgn_write(pgn, game[i]);
5327 pgn_close(pgn);
5329 fm_loaded_file = TRUE;
5332 cleanup_all();
5333 exit(ret);
5335 else if (ret == E_PGN_ERR)
5336 exit(ret);
5338 if (utf8_pieces != -1)
5339 config.utf8_pieces = utf8_pieces;
5341 init_wchar_pieces ();
5342 yes_wchar = str_to_wchar (_("y"));
5343 all_wchar = str_to_wchar (_("a"));
5344 overwrite_wchar = str_to_wchar (_("o"));
5345 resume_wchar = str_to_wchar (_("r"));
5346 current_wchar = str_to_wchar (_("c"));
5347 append_wchar = str_to_wchar (_("a"));
5348 translatable_tag_names[0] = _("Event");
5349 translatable_tag_names[1] = _("Site");
5350 translatable_tag_names[2] = _("Date");
5351 translatable_tag_names[3] = _("Round");
5352 translatable_tag_names[4] = _("White");
5353 translatable_tag_names[5] = _("Black");
5354 translatable_tag_names[6] = _("Result");
5355 init_userdata();
5358 * This fixes window resizing in an xterm.
5360 if (getenv("DISPLAY") != NULL) {
5361 putenv("LINES=");
5362 putenv("COLUMNS=");
5365 if (initscr() == NULL)
5366 errx(EXIT_FAILURE, "%s", _("Could not initialize curses."));
5367 else
5368 curses_initialized = 1;
5370 if (LINES < 23 || COLS < 74) {
5371 endwin();
5372 errx(EXIT_FAILURE, _("Need at least an 74x23 terminal."));
5375 COLS_OLD = COLS;
5376 LINES_OLD = LINES;
5378 if (has_colors() == TRUE && start_color() == OK)
5379 init_color_pairs();
5381 boardw = newwin(BOARD_HEIGHT, BOARD_WIDTH, 0, COLS - BOARD_WIDTH);
5382 boardp = new_panel(boardw);
5383 historyw = newwin(HISTORY_HEIGHT, HISTORY_WIDTH, LINES - HISTORY_HEIGHT,
5384 COLS - HISTORY_WIDTH);
5385 historyp = new_panel(historyw);
5386 statusw = newwin(STATUS_HEIGHT, STATUS_WIDTH, 0, 0);
5387 statusp = new_panel(statusw);
5388 tagw = newwin(TAG_HEIGHT, TAG_WIDTH, STATUS_HEIGHT + 1, 0);
5389 tagp = new_panel(tagw);
5390 keypad(boardw, TRUE);
5391 // leaveok(boardw, TRUE);
5392 leaveok(tagw, TRUE);
5393 leaveok(statusw, TRUE);
5394 leaveok(historyw, TRUE);
5395 curs_set(0);
5396 cbreak();
5397 noecho();
5398 draw_window_decor();
5399 game_loop();
5400 cleanup_all();
5401 free (w_pawn_wchar);
5402 free (w_rook_wchar);
5403 free (w_bishop_wchar);
5404 free (w_knight_wchar);
5405 free (w_queen_wchar);
5406 free (w_king_wchar);
5407 free (b_pawn_wchar);
5408 free (b_rook_wchar);
5409 free (b_bishop_wchar);
5410 free (b_knight_wchar);
5411 free (b_queen_wchar);
5412 free (b_king_wchar);
5413 free (empty_wchar);
5414 free (enpassant_wchar);
5415 free (yes_wchar);
5416 free (all_wchar);
5417 free (overwrite_wchar);
5418 free (resume_wchar);
5419 free (current_wchar);
5420 free (append_wchar);
5421 exit(EXIT_SUCCESS);