Rewrite updating of cursor code.
[cboard.git] / src / cboard.c
blobe3fd2f3b4364656aaf1b168ea63527b0fde3db53
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2013 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <pwd.h>
33 #include <signal.h>
34 #include <time.h>
35 #include <err.h>
36 #include <locale.h>
38 #ifdef HAVE_STDARG_H
39 #include <stdarg.h>
40 #endif
42 #ifdef HAVE_SYS_WAIT_H
43 #include <sys/wait.h>
44 #endif
46 #ifdef HAVE_REGEX_H
47 #include <regex.h>
48 #endif
50 #ifdef WITH_LIBPERL
51 #include "perl-plugin.h"
52 #endif
54 #include "common.h"
55 #include "conf.h"
56 #include "window.h"
57 #include "message.h"
58 #include "colors.h"
59 #include "input.h"
60 #include "misc.h"
61 #include "engine.h"
62 #include "strings.h"
63 #include "menu.h"
64 #include "keys.h"
65 #include "rcfile.h"
66 #include "filebrowser.h"
68 #ifdef DEBUG
69 #include <debug.h>
70 #endif
72 #define COPYRIGHT "Copyright (C) 2002-2013 " PACKAGE_BUGREPORT
73 #define LINE_GRAPHIC(c) ((!config.linegraphics) ? ' ' : c)
74 #define ROWTOMATRIX(r) ((8 - r) * 2 + 2 - 1)
75 #define COLTOMATRIX(c) ((c == 1) ? 1 : c * 4 - 3)
76 #define STATUS_HEIGHT 12
77 #define MEGA_BOARD (LINES >= 50 && COLS >= 144)
78 #define BOARD_HEIGHT_MB 50
79 #define BOARD_WIDTH_MB 98
80 #define STATUS_WIDTH_MB (COLS - BOARD_WIDTH_MB)
81 #define TAG_HEIGHT_MB 31
82 #define TAG_WIDTH_MB (COLS - BOARD_WIDTH_MB)
83 #define HISTORY_HEIGHT_MB (LINES - (STATUS_HEIGHT + TAG_HEIGHT_MB + 1))
84 #define HISTORY_WIDTH_MB (COLS - BOARD_WIDTH_MB)
85 #define BIG_BOARD (LINES >= 40 && COLS >= 112)
86 #define BOARD_HEIGHT ((MEGA_BOARD) ? BOARD_HEIGHT_MB : (BIG_BOARD) ? 34 : 18)
87 #define BOARD_WIDTH ((MEGA_BOARD) ? BOARD_WIDTH_MB : (BIG_BOARD) ? 66 : 34)
88 #define STATUS_WIDTH ((MEGA_BOARD) ? STATUS_WIDTH_MB : COLS - BOARD_WIDTH)
89 #define TAG_HEIGHT ((MEGA_BOARD) ? TAG_HEIGHT_MB : LINES - STATUS_HEIGHT - 1)
90 #define TAG_WIDTH ((MEGA_BOARD) ? TAG_WIDTH_MB : COLS - BOARD_WIDTH)
91 #define HISTORY_HEIGHT ((MEGA_BOARD) ? HISTORY_HEIGHT_MB : LINES - BOARD_HEIGHT)
92 #define HISTORY_WIDTH ((MEGA_BOARD) ? HISTORY_WIDTH_MB : COLS - STATUS_WIDTH)
93 #define MAX_VALUE_WIDTH (COLS - 8)
95 enum {
96 UP, DOWN, LEFT, RIGHT
99 static WINDOW *boardw;
100 static PANEL *boardp;
101 static WINDOW *tagw;
102 static PANEL *tagp;
103 static WINDOW *statusw;
104 static PANEL *statusp;
105 static WINDOW *historyw;
106 static PANEL *historyp;
107 static WINDOW *loadingw;
108 static PANEL *loadingp;
109 static WINDOW *enginew;
110 static PANEL *enginep;
112 static char gameexp[255];
113 static char moveexp[255];
114 static struct itimerval clock_timer;
115 static int delete_count = 0;
116 static int markstart = -1, markend = -1;
117 static int keycount;
118 static char loadfile[FILENAME_MAX];
119 static int quit;
120 static wint_t input_c;
122 // Loaded filename from the command line or from the file input dialog.
123 static int filetype;
124 enum {
125 FILE_NONE, FILE_PGN, FILE_FEN, FILE_EPD
128 static char **nags;
129 static int nag_total;
130 static int macro_match;
132 // Primer movimiento de juego cargado
133 // First move loaded game
134 static char fm_loaded_file = FALSE;
135 // Movimiento resultado de la función 'do_play_go'
136 // Movement function result 'do_play_go'
137 static int go_move = 0;
138 // Controla rotación de tablero
139 // Rotation control board
140 static int rotate = FALSE;
142 static int COLS_OLD, LINES_OLD;
144 // Status window.
145 static struct {
146 wchar_t *notify; // The status window notification line buffer.
147 } status;
149 static int curses_initialized;
151 // When in history mode a full step is to the next move of the same playing
152 // side. Half stepping is alternating sides.
153 static int movestep;
155 static wchar_t *w_pawn_wchar;
156 static wchar_t *w_rook_wchar;
157 static wchar_t *w_bishop_wchar;
158 static wchar_t *w_knight_wchar;
159 static wchar_t *w_queen_wchar;
160 static wchar_t *w_king_wchar;
161 static wchar_t *b_pawn_wchar;
162 static wchar_t *b_rook_wchar;
163 static wchar_t *b_bishop_wchar;
164 static wchar_t *b_knight_wchar;
165 static wchar_t *b_queen_wchar;
166 static wchar_t *b_king_wchar;
167 static wchar_t *empty_wchar;
168 static wchar_t *enpassant_wchar;
170 static wchar_t *yes_wchar;
171 static wchar_t *all_wchar; // do_save_game_overwrite_confirm()
172 static wchar_t *overwrite_wchar; // do_save_game_overwrite_confirm()
173 static wchar_t *resume_wchar; // do_history_mode_confirm()
174 static wchar_t *current_wchar; // do_game_save_multi_confirm()
175 static wchar_t *append_wchar; // save_pgn()
177 static const char piece_chars[] = "PpRrNnBbQqKkxx";
178 static char *translatable_tag_names[7];
179 static const char *f_pieces[] = {
180 " ", // 0
181 " O ",
182 " /_\\ ",
183 " |-|-| ", // 3
184 " ] [ ",
185 " /___\\ ",
186 " /?M ", // 6
187 " (@/)) ",
188 " /__))",
189 " O ", // 9
190 " (+) ",
191 " /_\\ ",
192 "•°°°°°•",// 12
193 " \\\\|// ",
194 " |___| ",
195 " __+__ ", // 15
196 "(__|__)",
197 " |___| ",
198 " \\ / ", // 18
199 " X ",
200 " / \\ "
203 static const bool cb[8][8] = {
204 {1,0,1,0,1,0,1,0},
205 {0,1,0,1,0,1,0,1},
206 {1,0,1,0,1,0,1,0},
207 {0,1,0,1,0,1,0,1},
208 {1,0,1,0,1,0,1,0},
209 {0,1,0,1,0,1,0,1},
210 {1,0,1,0,1,0,1,0},
211 {0,1,0,1,0,1,0,1}
214 static void free_userdata_once(GAME g);
215 static void do_more_help(WIN *);
217 void coordofmove(GAME g, char *move, char *prow, char *pcol)
219 char l = strlen(move);
221 if (*move == 'O') {
222 *prow = (g->turn == WHITE) ? 8 : 1;
223 if (l <= 4)
224 *pcol = 7;
225 else
226 *pcol = 3;
227 return;
230 move += l;
232 while (!isdigit(*move))
233 move--;
235 *prow = RANKTOINT(*move--);
236 *pcol = FILETOINT(*move);
239 // Posición por rotación de tablero.
240 // Rotation board position.
241 static void rotate_position(int p)
243 struct userdata_s *d = gp->data;
244 char fr, fc;
245 char fr2 = 8, fc2 = 8;
247 for (fr = 1; fr < 9; fr++) {
248 if (fr2 < 1)
249 fr2 = 8;
250 for (fc = 1; fc < 9; fc++){
251 if (fc2 < 1)
252 fc2 = 8;
253 if (p == CURSOR_POSITION &&
254 d->c_row == fr && d->c_col == fc) {
255 d->c_row = fr2;
256 d->c_col = fc2;
257 return;
259 else if (p == SP_POSITION &&
260 d->sp.row == fr && d->sp.col == fc) {
261 d->sp.row = fr2;
262 d->sp.col = fc2;
263 return;
265 else if (p == SPS_POSITION &&
266 d->sp.srow == fr && d->sp.scol == fc) {
267 d->sp.srow = fr2;
268 d->sp.scol = fc2;
269 return;
271 fc2--;
273 fr2--;
277 void update_cursor(GAME g, int idx)
279 int t = pgn_history_total(g->hp);
280 struct userdata_s *d = g->data;
283 * If not deincremented then r and c would be the next move.
285 idx--;
287 if (idx > t || idx < 0 || !t || !g->hp[idx]->move)
288 d->c_row = 2, d->c_col = 5;
289 else
290 coordofmove(g, g->hp[idx]->move, &d->c_row, &d->c_col);
292 if (d->mode == MODE_HISTORY && rotate)
293 rotate_position(CURSOR_POSITION);
296 static int init_nag()
298 FILE *fp;
299 char line[LINE_MAX];
300 int i = 0;
302 if ((fp = fopen(config.nagfile, "r")) == NULL) {
303 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.nagfile, strerror(errno));
304 return 1;
307 nags = Realloc(nags, (i+2) * sizeof(char *));
308 nags[i++] = strdup(_("none"));
309 nags[i] = NULL;
311 while (!feof(fp)) {
312 if (fscanf(fp, " %[^\n] ", line) == 1) {
313 nags = Realloc(nags, (i + 2) * sizeof(char *));
314 nags[i++] = strdup(line);
318 nags[i] = NULL;
319 nag_total = i;
320 return 0;
323 void edit_nag_toggle_item(struct menu_input_s *m)
325 struct input_s *in = m->data;
326 struct input_data_s *id = in->data;
327 HISTORY *h = id->data;
328 int i;
330 if (m->selected == 0) {
331 for (i = 0; i < MAX_PGN_NAG; i++)
332 h->nag[i] = 0;
334 for (i = 0; m->items[i]; i++)
335 m->items[i]->selected = 0;
337 return;
340 for (i = 0; i < MAX_PGN_NAG; i++) {
341 if (h->nag[i] == m->selected)
342 h->nag[i] = m->selected = 0;
343 else {
344 if (!h->nag[i]) {
345 h->nag[i] = m->selected;
346 break;
352 void edit_nag_save(struct menu_input_s *m)
354 pushkey = -1;
357 void edit_nag_help(struct menu_input_s *m)
359 message(_("NAG Menu Keys"), ANY_KEY_STR, "%s",
361 " UP/DOWN - previous/next menu item\n"
362 " HOME/END - first/last menu item\n"
363 " PGDN/PGUP - next/previous page\n"
364 " a-zA-Z0-9 - jump to item\n"
365 " SPACE - toggle selected item\n"
366 " CTRL-X - quit with changes"
370 struct menu_item_s **get_nag_items(WIN *win)
372 int i, n;
373 struct menu_input_s *m = win->data;
374 struct input_s *in = m->data;
375 struct input_data_s *id = in->data;
376 struct menu_item_s **items = m->items;
377 HISTORY *h = id->data;
379 if (items) {
380 for (i = 0; items[i]; i++)
381 free(items[i]);
384 for (i = 0; nags[i]; i++) {
385 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
386 items[i] = Malloc(sizeof(struct menu_item_s));
387 items[i]->name = nags[i];
388 items[i]->value = NULL;
390 for (n = 0; n < MAX_PGN_NAG; n++) {
391 if (h->nag[n] == i) {
392 items[i]->selected = 1;
393 n = -1;
394 break;
398 if (n >= 0)
399 items[i]->selected = 0;
402 items[i] = NULL;
403 m->nofree = 1;
404 m->items = items;
405 return items;
408 void nag_print(WIN *win)
410 struct menu_input_s *m = win->data;
412 mvwprintw(win->w, m->print_line, 1, "%-*s", win->cols - 2, m->item->name);
415 void edit_nag(void *arg)
417 struct menu_key_s **keys = NULL;
419 if (!nags) {
420 if (init_nag())
421 return;
424 add_menu_key(&keys, ' ', edit_nag_toggle_item);
425 add_menu_key(&keys, CTRL_KEY('x'), edit_nag_save);
426 add_menu_key(&keys, KEY_F(1), edit_nag_help);
427 construct_menu(0, 0, -1, -1, _("Numeric Annotation Glyphs"), 1, get_nag_items, keys, arg,
428 nag_print, NULL);
429 return;
432 static void *view_nag(void *arg)
434 HISTORY *h = (HISTORY *)arg;
435 char buf[80];
436 char line[LINE_MAX] = {0};
437 int i = 0;
439 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Viewing NAG for"), h->move);
441 if (!nags) {
442 if (init_nag())
443 return NULL;
446 for (i = 0; i < MAX_PGN_NAG; i++) {
447 char buf2[16];
449 if (!h->nag[i])
450 break;
452 if (h->nag[i] >= nag_total)
453 strncat(line, itoa(h->nag[i], buf2), sizeof(line)-1);
454 else
455 strncat(line, nags[h->nag[i]], sizeof(line)-1);
457 strncat(line, "\n", sizeof(line)-1);
460 line[strlen(line) - 1] = 0;
461 message(buf, ANY_KEY_STR, "%s", line);
462 return NULL;
465 void view_annotation(HISTORY *h)
467 char buf[MAX_SAN_MOVE_LEN + strlen(_("Viewing Annotation for")) + 4];
468 int nag = 0, comment = 0;
470 if (!h)
471 return;
473 if (h->comment && h->comment[0])
474 comment++;
476 if (h->nag[0])
477 nag++;
479 if (!nag && !comment)
480 return;
482 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Viewing Annotation for"), h->move);
484 if (comment)
485 construct_message(buf, (nag) ? _("Any other key to continue") : ANY_KEY_STR, 0, 1,
486 (nag) ? _("Press 'n' to view NAG") : NULL,
487 (nag) ? view_nag : NULL, (nag) ? h : NULL, NULL,
488 (nag) ? 'n' : 0, 0, "%s", h->comment);
489 else
490 construct_message(buf, _("Any other key to continue"), 0, 1, _("Press 'n' to view NAG"), view_nag, h, NULL,
491 'n', 0, "%s", _("No comment text for this move"));
494 int do_game_write(char *filename, char *mode, int start, int end)
496 int i;
497 struct userdata_s *d;
498 PGN_FILE *pgn;
500 i = pgn_open(filename, mode, &pgn);
502 if (i == E_PGN_ERR) {
503 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", filename, strerror(errno));
504 return 1;
506 else if (i == E_PGN_INVALID) {
507 cmessage(ERROR_STR, ANY_KEY_STR, "%s\n%s", filename, _("Not a regular file"));
508 return 1;
511 for (i = (start == -1) ? 0 : start; i < end; i++) {
512 d = game[i]->data;
513 pgn_write(pgn, game[i]);
514 CLEAR_FLAG(d->flags, CF_MODIFIED);
517 if (pgn_close(pgn) != E_PGN_OK)
518 message(ERROR_STR, ANY_KEY_STR, "%s", strerror(errno));
520 if (start == -1) {
521 strncpy(loadfile, filename, sizeof(loadfile));
522 loadfile[sizeof(loadfile)-1] = 0;
525 return 0;
528 struct save_game_s {
529 char *filename;
530 char *mode;
531 int start;
532 int end;
535 void do_save_game_overwrite_confirm(WIN *win)
537 char *mode = "w";
538 struct save_game_s *s = win->data;
539 wchar_t str[] = { win->c, 0 };
541 if (!wcscmp (str, append_wchar))
542 mode = "a";
543 else if (!wcscmp (str, overwrite_wchar))
544 mode = "w";
545 else
546 goto done;
548 if (do_game_write(s->filename, mode, s->start, s->end))
549 update_status_notify(gp, "%s", _("Save game failed."));
550 else
551 update_status_notify(gp, "%s", _("Game saved."));
553 done:
554 free(s->filename);
555 free(s);
558 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
559 * game index number.
561 void save_pgn(char *filename, int saveindex)
563 char buf[FILENAME_MAX];
564 struct stat st;
565 int end = (saveindex == -1) ? gtotal : saveindex + 1;
566 struct save_game_s *s;
568 if (filename[0] != '/' && config.savedirectory) {
569 if (stat(config.savedirectory, &st) == -1) {
570 if (errno == ENOENT) {
571 if (mkdir(config.savedirectory, 0755) == -1) {
572 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
573 strerror(errno));
574 return;
577 else {
578 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory,
579 strerror(errno));
580 return;
584 stat(config.savedirectory, &st);
586 if (!S_ISDIR(st.st_mode)) {
587 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.savedirectory, _("Not a directory."));
588 return;
591 snprintf(buf, sizeof(buf), "%s/%s", config.savedirectory, filename);
592 filename = buf;
595 if (access(filename, W_OK) == 0) {
596 s = Malloc(sizeof(struct save_game_s));
597 s->filename = strdup(filename);
598 s->start = saveindex;
599 s->end = end;
600 construct_message(NULL, _("What would you like to do?"), 0, 1, NULL,
601 NULL, s, do_save_game_overwrite_confirm, 0, 0,
602 "%s \"%s\"\nPress \"%ls\" to append to this file, \"%ls\" to overwrite or any other key to cancel.",
603 _("File exists:"), filename, append_wchar,
604 overwrite_wchar);
605 return;
608 if (do_game_write(filename, "a", saveindex, end))
609 update_status_notify(gp, "%s", _("Save game failed."));
610 else
611 update_status_notify(gp, "%s", _("Game saved."));
614 static int castling_state(GAME g, BOARD b, int row, int col, int piece, int mod)
616 if (pgn_piece_to_int(piece) == ROOK && col == 7
617 && row == 7 &&
618 (TEST_FLAG(g->flags, GF_WK_CASTLE) || mod) &&
619 pgn_piece_to_int(b[7][4].icon) == KING && isupper(piece)) {
620 if (mod)
621 TOGGLE_FLAG(g->flags, GF_WK_CASTLE);
622 return 1;
624 else if (pgn_piece_to_int(piece) == ROOK && col == 0
625 && row == 7 &&
626 (TEST_FLAG(g->flags, GF_WQ_CASTLE) || mod) &&
627 pgn_piece_to_int(b[7][4].icon) == KING && isupper(piece)) {
628 if (mod)
629 TOGGLE_FLAG(g->flags, GF_WQ_CASTLE);
630 return 1;
632 else if (pgn_piece_to_int(piece) == ROOK && col == 7
633 && row == 0 &&
634 (TEST_FLAG(g->flags, GF_BK_CASTLE) || mod) &&
635 pgn_piece_to_int(b[0][4].icon) == KING && islower(piece)) {
636 if (mod)
637 TOGGLE_FLAG(g->flags, GF_BK_CASTLE);
638 return 1;
640 else if (pgn_piece_to_int(piece) == ROOK && col == 0
641 && row == 0 &&
642 (TEST_FLAG(g->flags, GF_BQ_CASTLE) || mod) &&
643 pgn_piece_to_int(b[0][4].icon) == KING && islower(piece)) {
644 if (mod)
645 TOGGLE_FLAG(g->flags, GF_BQ_CASTLE);
646 return 1;
648 else if (pgn_piece_to_int(piece) == KING && col == 4
649 && row == 7 &&
650 (mod || (pgn_piece_to_int(b[7][7].icon) == ROOK &&
651 TEST_FLAG(g->flags, GF_WK_CASTLE))
653 (pgn_piece_to_int(b[7][0].icon) == ROOK &&
654 TEST_FLAG(g->flags, GF_WQ_CASTLE))) && isupper(piece)) {
655 if (mod) {
656 if (TEST_FLAG(g->flags, GF_WK_CASTLE) ||
657 TEST_FLAG(g->flags, GF_WQ_CASTLE))
658 CLEAR_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE);
659 else
660 SET_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE);
662 return 1;
664 else if (pgn_piece_to_int(piece) == KING && col == 4
665 && row == 0 &&
666 (mod || (pgn_piece_to_int(b[0][7].icon) == ROOK &&
667 TEST_FLAG(g->flags, GF_BK_CASTLE))
669 (pgn_piece_to_int(b[0][0].icon) == ROOK &&
670 TEST_FLAG(g->flags, GF_BQ_CASTLE))) && islower(piece)) {
671 if (mod) {
672 if (TEST_FLAG(g->flags, GF_BK_CASTLE) ||
673 TEST_FLAG(g->flags, GF_BQ_CASTLE))
674 CLEAR_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE);
675 else
676 SET_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE);
678 return 1;
681 return 0;
684 #define IS_ENPASSANT(c) (c == 'x') ? CP_BOARD_ENPASSANT : isupper(c) ? CP_BOARD_WHITE : CP_BOARD_BLACK
685 #define ATTRS(cp) (cp & (A_BOLD|A_STANDOUT|A_BLINK|A_DIM|A_UNDERLINE|A_INVIS|A_REVERSE))
687 static void
688 init_wchar_pieces ()
690 w_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♙" : "P");
691 w_rook_wchar = str_to_wchar (config.utf8_pieces ? "♖" : "R");
692 w_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♗" : "B");
693 w_knight_wchar = str_to_wchar (config.utf8_pieces ? "♘" : "N");
694 w_queen_wchar = str_to_wchar (config.utf8_pieces ? "♕" : "Q");
695 w_king_wchar = str_to_wchar (config.utf8_pieces ? "♔" : "K");
696 b_pawn_wchar = str_to_wchar (config.utf8_pieces ? "♟" : "p");
697 b_rook_wchar = str_to_wchar (config.utf8_pieces ? "♜" : "r");
698 b_bishop_wchar = str_to_wchar (config.utf8_pieces ? "♝" : "b");
699 b_knight_wchar = str_to_wchar (config.utf8_pieces ? "♞" : "n");
700 b_queen_wchar = str_to_wchar (config.utf8_pieces ? "♛" : "q");
701 b_king_wchar = str_to_wchar (config.utf8_pieces ? "♚" : "k");
702 empty_wchar = str_to_wchar (" ");
703 enpassant_wchar = str_to_wchar ("x");
706 static wchar_t *
707 piece_to_wchar (unsigned char p)
709 switch (p)
711 case 'P':
712 return w_pawn_wchar;
713 case 'p':
714 return b_pawn_wchar;
715 case 'R':
716 return w_rook_wchar;
717 case 'r':
718 return b_rook_wchar;
719 case 'B':
720 return w_bishop_wchar;
721 case 'b':
722 return b_bishop_wchar;
723 case 'N':
724 return w_knight_wchar;
725 case 'n':
726 return b_knight_wchar;
727 case 'Q':
728 return w_queen_wchar;
729 case 'q':
730 return b_queen_wchar;
731 case 'K':
732 return w_king_wchar;
733 case 'k':
734 return b_king_wchar;
735 case 'x':
736 return enpassant_wchar;
739 return empty_wchar;
742 static int piece_can_attack (GAME g, int rank, int file)
744 struct userdata_s *d = g->data;
745 char *m, *frfr = NULL;
746 pgn_error_t e;
747 int row, col, p, v, pi, cpi;
749 if (rotate) {
750 rotate_position(CURSOR_POSITION);
751 rotate_position(SPS_POSITION);
754 v = d->b[RANKTOBOARD (d->c_row)][FILETOBOARD (d->c_col)].valid;
755 pi = pgn_piece_to_int (d->b[RANKTOBOARD (rank)][FILETOBOARD (file)].icon);
756 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 if (rotate) {
763 rotate_position(CURSOR_POSITION);
764 rotate_position(SPS_POSITION);
767 return 0;
770 if (d->sp.icon) {
771 col = v ? d->c_col : d->sp.scol;
772 row = v ? d->c_row : d->sp.srow;
774 else {
775 col = d->c_col;
776 row = d->c_row;
779 m = malloc(MAX_SAN_MOVE_LEN+1);
780 m[0] = INTTOFILE (file);
781 m[1] = INTTORANK (rank);
782 m[2] = INTTOFILE (col);
783 m[3] = INTTORANK (row);
784 m[4] = 0;
786 if (d->sp.icon && v) {
787 BOARD b;
789 memcpy (b, d->b, sizeof(BOARD));
790 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
791 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
792 pgn_int_to_piece (WHITE, OPEN_SQUARE);
793 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
794 pgn_switch_turn(g);
795 e = pgn_validate_move (g, b, &m, &frfr);
796 pgn_switch_turn (g);
797 free (m);
798 free (frfr);
800 if (e != E_PGN_OK && pgn_piece_to_int (d->sp.icon) == PAWN) {
801 int n = (d->sp.srow == 7 && rank == 5) ? 6 :
802 (d->sp.srow == 2 && rank == 4) ? 3 : 0;
804 if (n && (file == d->c_col-1 || file == d->c_col+1)) {
805 memcpy (b, d->b, sizeof(BOARD));
806 p = b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon;
807 b[RANKTOBOARD (d->sp.srow)][FILETOBOARD (d->sp.scol)].icon =
808 pgn_int_to_piece (WHITE, OPEN_SQUARE);
809 b[RANKTOBOARD (row)][FILETOBOARD (col)].icon = p;
810 b[RANKTOBOARD (n)][FILETOBOARD (d->sp.scol)].enpassant = 1;
811 m = malloc(MAX_SAN_MOVE_LEN+1);
812 m[0] = INTTOFILE (file);
813 m[1] = INTTORANK (rank);
814 m[2] = INTTOFILE (col);
815 m[3] = INTTORANK (n);
816 m[4] = 0;
817 pgn_switch_turn(g);
818 SET_FLAG (g->flags, GF_ENPASSANT);
819 e = pgn_validate_move (g, b, &m, &frfr);
820 CLEAR_FLAG (g->flags, GF_ENPASSANT);
821 pgn_switch_turn (g);
822 free (m);
823 free (frfr);
827 if (rotate) {
828 rotate_position(CURSOR_POSITION);
829 rotate_position(SPS_POSITION);
832 return e == E_PGN_OK ? 1 : 0;
835 pgn_switch_turn(g);
836 e = pgn_validate_move (g, d->b, &m, &frfr);
837 pgn_switch_turn (g);
839 if (!strcmp (m, "O-O") || !strcmp (m, "O-O-O"))
840 e = E_PGN_INVALID;
842 if (e == E_PGN_OK) {
843 int sf = FILETOINT (frfr[0]), sr = RANKTOINT (frfr[1]);
844 int df = FILETOINT (frfr[2]);
846 pi = d->b[RANKTOBOARD (sr)][FILETOBOARD (sf)].icon;
847 pi = pgn_piece_to_int (pi);
848 if (pi == PAWN && sf == df)
849 e = E_PGN_INVALID;
852 free (m);
853 free (frfr);
855 if (rotate) {
856 rotate_position(CURSOR_POSITION);
857 rotate_position(SPS_POSITION);
860 return e == E_PGN_OK ? 1 : 0;
863 void print_piece(WINDOW *w, int l, int c, char p)
865 int i, y, ff = 0;
867 for (i = 0; i < 13; i += 2) {
868 if (p == piece_chars[i] || p == piece_chars[i + 1]) {
869 for (y = 0; y < 3; y++)
870 mvwprintw(w, l + y, c, "%s", f_pieces[i + ff + y]);
871 return;
874 ff++;
877 for (y = 0; y < 3; y++)
878 mvwprintw(w, l + y, c, f_pieces[0]);
881 int inv_int(int x)
883 int fx, fx2 = 8;
885 for (fx = 1; fx < 9; fx++) {
886 if (x == fx)
887 break;
888 fx2--;
891 return fx2;
894 static int is_prev_move (struct userdata_s *d, int brow, int bcol,
895 int row, int col)
897 if ((!BIG_BOARD &&
898 row == ROWTOMATRIX(((rotate) ? inv_int(d->pm_row) : d->pm_row)) &&
899 col == COLTOMATRIX(((rotate) ? inv_int(d->pm_col) : d->pm_col)))
900 || (BIG_BOARD &&
901 brow + 1 == ((rotate) ? d->pm_row : inv_int(d->pm_row)) &&
902 bcol + 1 == ((rotate) ? inv_int(d->pm_col) : d->pm_col)))
903 return 1;
905 return 0;
908 void update_board_window(GAME g)
910 int row, col;
911 int bcol = 0, brow = 0;
912 int l = config.coordsyleft;
913 int maxy = BOARD_HEIGHT, maxx = BOARD_WIDTH;
914 int ncols = 0, offset = 1;
915 int rowr = (MEGA_BOARD) ? 6 : (BIG_BOARD) ? 4 : 2;
916 int colr = (MEGA_BOARD) ? 12 : (BIG_BOARD) ? 8 : 4;
917 unsigned coords_y = 8, cxgc = 0;
918 unsigned i, cpd = 0;
919 struct userdata_s *d = g->data;
920 HISTORY *h = NULL;
922 h = pgn_history_by_n(g->hp, g->hindex - 1);
923 if (h && h->move && d->mode == MODE_PLAY)
924 coordofmove(g, h->move, &d->pm_row, &d->pm_col);
925 else {
926 d->pm_row = 0;
927 d->pm_col = 0;
930 if (d->mode != MODE_PLAY && d->mode != MODE_EDIT)
931 update_cursor(g, g->hindex);
933 if (BIG_BOARD) {
934 if (rotate) {
935 brow = 7;
936 coords_y = 1;
939 else {
940 if (rotate) {
941 brow = 1;
942 coords_y = 1;
944 else
945 brow = 8;
948 for (row = 0; row < maxy; row++) {
949 if (BIG_BOARD) {
950 if (rotate)
951 bcol = 7;
952 else
953 bcol = 0;
955 else {
956 if (rotate)
957 bcol = 8;
958 else
959 bcol = 1;
962 for (col = 0; col < maxx; col++) {
963 int attrwhich = -1;
964 chtype attrs = 0, old_attrs = 0;
965 unsigned char p;
966 int can_attack = 0;
967 int valid = 0;
969 if (row == 0 || row == maxy - 2) {
970 if (col == 0)
971 mvwaddch(boardw, row, col + l,
972 LINE_GRAPHIC((row)
973 ? ACS_LLCORNER | CP_BOARD_GRAPHICS
974 : ACS_ULCORNER | CP_BOARD_GRAPHICS));
975 else if (col == maxx - 2)
976 mvwaddch(boardw, row, col + l,
977 LINE_GRAPHIC((row)
978 ? ACS_LRCORNER | CP_BOARD_GRAPHICS
979 : ACS_URCORNER | CP_BOARD_GRAPHICS));
980 else if (!(col % colr))
981 mvwaddch(boardw, row, col + l,
982 LINE_GRAPHIC((row)
983 ? ACS_BTEE | CP_BOARD_GRAPHICS
984 : ACS_TTEE | CP_BOARD_GRAPHICS));
985 else {
986 if (col != maxx - 1)
987 mvwaddch(boardw, row, col + l,
988 LINE_GRAPHIC(ACS_HLINE | CP_BOARD_GRAPHICS));
991 continue;
994 if ((row % 2) && col == maxx - 1 &&
995 (coords_y > 0 && coords_y < 9)) {
996 wattron(boardw, CP_BOARD_COORDS);
997 mvwprintw(boardw,
998 (BIG_BOARD) ? row * ((MEGA_BOARD) ? 3 : 2)
999 : row, (l) ? 0 : col, "%d",
1000 (rotate) ? coords_y++ : coords_y--);
1001 wattroff(boardw, CP_BOARD_COORDS);
1002 continue;
1005 if ((col == 0 || col == maxx - 2) && row != maxy - 1) {
1006 if (!(row % rowr))
1007 mvwaddch(boardw, row, col + l,
1008 LINE_GRAPHIC((col) ?
1009 ACS_RTEE | CP_BOARD_GRAPHICS :
1010 ACS_LTEE | CP_BOARD_GRAPHICS));
1011 else
1012 mvwaddch(boardw, row, col + l,
1013 LINE_GRAPHIC(ACS_VLINE | CP_BOARD_GRAPHICS));
1015 continue;
1018 if ((row % rowr) && !(col % colr) && row != maxy - 1) {
1019 mvwaddch(boardw, row, col + l,
1020 LINE_GRAPHIC(ACS_VLINE | CP_BOARD_GRAPHICS));
1021 continue;
1024 if (!(col % colr) && row != maxy - 1) {
1025 mvwaddch(boardw, row, col + l,
1026 LINE_GRAPHIC(ACS_PLUS | CP_BOARD_GRAPHICS));
1027 continue;
1030 if ((row % rowr)) {
1031 if ((col % colr)) {
1032 if (BIG_BOARD)
1033 attrwhich = (cb[brow][bcol]) ? WHITE : BLACK;
1034 else {
1035 if (ncols++ == 8) {
1036 offset++;
1037 ncols = 1;
1040 if (((ncols % 2) && !(offset % 2))
1041 || (!(ncols % 2) && (offset % 2)))
1042 attrwhich = BLACK;
1043 else
1044 attrwhich = WHITE;
1047 if (BIG_BOARD && rotate) {
1048 brow = inv_int(brow + 1) - 1;
1049 bcol = inv_int(bcol + 1) - 1;
1052 if (BIG_BOARD)
1053 p = d->b[brow][bcol].icon;
1054 else
1055 p = d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].icon;
1057 int pi = pgn_piece_to_int(p);
1059 if (config.details &&
1060 ((!BIG_BOARD && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].enpassant)
1061 || (BIG_BOARD && d->b[brow][bcol].enpassant))) {
1062 p = pi = 'x';
1063 attrs = mix_cp(CP_BOARD_ENPASSANT,
1064 (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK,
1065 ATTRS(CP_BOARD_ENPASSANT), A_FG_B_BG);
1068 if (config.showattacks && config.details
1069 && piece_can_attack (g,
1070 BIG_BOARD ? inv_int (brow+1) : brow,
1071 BIG_BOARD ? bcol+1 : bcol)) {
1072 attrs = CP_BOARD_ATTACK;
1073 old_attrs = attrs;
1074 can_attack = 1;
1077 if (config.validmoves &&
1078 ((!BIG_BOARD && d->b[RANKTOBOARD (brow)][FILETOBOARD (bcol)].valid)
1079 || (BIG_BOARD && d->b[brow][bcol].valid))) {
1080 old_attrs = -1;
1081 valid = 1;
1083 if (attrwhich == WHITE)
1084 attrs = mix_cp(CP_BOARD_MOVES_WHITE,
1085 IS_ENPASSANT(p),
1086 ATTRS(CP_BOARD_MOVES_WHITE),
1087 B_FG_A_BG);
1088 else
1089 attrs = mix_cp(CP_BOARD_MOVES_BLACK,
1090 IS_ENPASSANT(p),
1091 ATTRS(CP_BOARD_MOVES_BLACK),
1092 B_FG_A_BG);
1094 else if (p != 'x' && !can_attack)
1095 attrs = (attrwhich == WHITE) ? CP_BOARD_WHITE : CP_BOARD_BLACK;
1097 if (BIG_BOARD && rotate) {
1098 brow = inv_int(brow + 1) - 1;
1099 bcol = inv_int(bcol + 1) - 1;
1102 if ((!BIG_BOARD && row == ROWTOMATRIX(d->c_row)
1103 && col == COLTOMATRIX(d->c_col))
1104 || (BIG_BOARD && brow + 1 == inv_int(d->c_row)
1105 && bcol + 1 == d->c_col)) {
1106 attrs = mix_cp(CP_BOARD_CURSOR, IS_ENPASSANT(p),
1107 ATTRS(CP_BOARD_CURSOR), B_FG_A_BG);
1108 old_attrs = -1;
1110 else if ((!BIG_BOARD && row == ROWTOMATRIX(d->sp.srow) &&
1111 col == COLTOMATRIX(d->sp.scol)) ||
1112 (BIG_BOARD && brow + 1 == inv_int(d->sp.srow) &&
1113 bcol + 1 == d->sp.scol)) {
1114 attrs = mix_cp(CP_BOARD_SELECTED, IS_ENPASSANT(p),
1115 ATTRS(CP_BOARD_SELECTED), B_FG_A_BG);
1116 old_attrs = -1;
1118 else if (is_prev_move (d, brow, bcol, row, col) && !valid)
1120 attrs = mix_cp(CP_BOARD_PREVMOVE, IS_ENPASSANT(p),
1121 ATTRS(CP_BOARD_PREVMOVE), B_FG_A_BG);
1122 old_attrs = -1;
1125 if (row == maxy - 1)
1126 attrs = 0;
1128 if (can_attack) {
1129 int n = is_prev_move (d, brow, bcol, row, col);
1130 chtype a = n && !valid
1131 ? CP_BOARD_PREVMOVE : attrwhich == WHITE ? valid ? CP_BOARD_MOVES_WHITE : CP_BOARD_WHITE : valid ? CP_BOARD_MOVES_BLACK : CP_BOARD_BLACK;
1132 attrs = mix_cp(CP_BOARD_ATTACK, a,
1133 ATTRS (CP_BOARD_ATTACK), A_FG_B_BG);
1134 old_attrs = -1;
1137 if (BIG_BOARD)
1138 wmove(boardw, row, col + ((MEGA_BOARD) ? 5 : 3) + l);
1139 else
1140 mvwaddch(boardw, row, col + l, ' ' | attrs);
1142 if (row == maxy - 1 && cxgc < 8) {
1143 waddch(boardw, "abcdefgh"[(BIG_BOARD) ? bcol : bcol - 1] | CP_BOARD_COORDS);
1144 cxgc++;
1146 else {
1147 if (old_attrs == -1) {
1148 old_attrs = attrs;
1149 goto printc;
1152 old_attrs = attrs;
1154 if (pi != OPEN_SQUARE && p != 'x' && !can_attack) {
1155 if (attrwhich == WHITE) {
1156 if (isupper(p))
1157 attrs = CP_BOARD_W_W;
1158 else
1159 attrs = CP_BOARD_W_B;
1161 else {
1162 if (isupper(p))
1163 attrs = CP_BOARD_B_W;
1164 else
1165 attrs = CP_BOARD_B_B;
1169 printc:
1170 if (BIG_BOARD) {
1171 if (config.details && !can_attack
1172 && castling_state(g, d->b,
1173 (rotate) ? inv_int(brow + 1) - 1: brow,
1174 (rotate) ? inv_int(bcol + 1) - 1: bcol, p, 0))
1175 attrs = mix_cp(CP_BOARD_CASTLING, attrs,
1176 ATTRS(CP_BOARD_CASTLING),
1177 A_FG_B_BG);
1179 else {
1180 if (config.details && !can_attack
1181 && castling_state(g, d->b, RANKTOBOARD (brow),
1182 FILETOBOARD (bcol), p, 0)) {
1183 attrs = mix_cp(CP_BOARD_CASTLING, attrs,
1184 ATTRS(CP_BOARD_CASTLING),
1185 A_FG_B_BG);
1189 if (BIG_BOARD) {
1190 // FIXME: Reimpresión de piezas(+4).
1191 if (cpd < 67) {
1192 wattron (boardw, attrs);
1193 if (MEGA_BOARD){
1194 for (i = 0; i < 5; i++)
1195 mvwprintw(boardw, i + brow * 6 + 1,
1196 bcol * 12 + 1 + l,
1197 " ");
1198 if (pi != OPEN_SQUARE)
1199 print_piece(boardw, brow * 6 + 2,
1200 bcol * 12 + 3 + l, p);
1202 else {
1203 print_piece(boardw, brow * 4 + 1,
1204 bcol * 8 + 1 + l,
1205 (pi != OPEN_SQUARE) ? p : 0);
1208 wattroff (boardw, attrs);
1209 cpd++;
1212 else {
1213 wattron (boardw, attrs);
1214 waddwstr (boardw, piece_to_wchar (pi != OPEN_SQUARE ? p : 0));
1215 wattroff (boardw, attrs);
1218 attrs = old_attrs;
1221 if (BIG_BOARD)
1222 col += (MEGA_BOARD) ? 10 : 6;
1223 else {
1224 waddch(boardw, ' ' | attrs);
1225 col += 2;
1228 if (rotate)
1229 bcol--;
1230 else
1231 bcol++;
1233 if (BIG_BOARD) {
1234 if (bcol > 7)
1235 bcol = 0;
1236 if (bcol < 0)
1237 bcol = 7;
1241 else {
1242 if (col != maxx - 1)
1243 mvwaddch(boardw, row, col + l,
1244 LINE_GRAPHIC(ACS_HLINE | CP_BOARD_GRAPHICS));
1248 if (row % rowr) {
1249 if (rotate)
1250 brow++;
1251 else
1252 brow--;
1255 if (BIG_BOARD) {
1256 if (brow > 7)
1257 brow = 0;
1258 if (brow < 0)
1259 brow = 7;
1264 void invalid_move(int n, int e, const char *m)
1266 if (curses_initialized)
1267 cmessage(ERROR_STR, ANY_KEY_STR, "%s \"%s\" (round #%i)", (e == E_PGN_AMBIGUOUS)
1268 ? _("Ambiguous move") : _("Invalid move"), m, n);
1269 else
1270 warnx("%s: %s \"%s\" (round #%i)", loadfile, (e == E_PGN_AMBIGUOUS)
1271 ? _("Ambiguous move") : _("Invalid move"), m, n);
1274 void gameover(GAME g)
1276 struct userdata_s *d = g->data;
1278 SET_FLAG(g->flags, GF_GAMEOVER);
1279 d->mode = MODE_HISTORY;
1280 stop_engine(g);
1283 static void update_clock(GAME g, struct itimerval it)
1285 struct userdata_s *d = g->data;
1287 if (TEST_FLAG(d->flags, CF_CLOCK) && g->turn == WHITE) {
1288 d->wclock.elapsed.tv_sec += it.it_value.tv_sec;
1289 d->wclock.elapsed.tv_usec += it.it_value.tv_usec;
1291 if (d->wclock.elapsed.tv_usec > 1000000 - 1) {
1292 d->wclock.elapsed.tv_sec += d->wclock.elapsed.tv_usec / 1000000;
1293 d->wclock.elapsed.tv_usec = d->wclock.elapsed.tv_usec % 1000000;
1296 if (d->wclock.tc[d->wclock.tcn][1] &&
1297 d->wclock.elapsed.tv_sec >= d->wclock.tc[d->wclock.tcn][1]) {
1298 pgn_tag_add(&g->tag, "Result", "0-1");
1299 gameover(g);
1302 else if (TEST_FLAG(d->flags, CF_CLOCK) && g->turn == BLACK) {
1303 d->bclock.elapsed.tv_sec += it.it_value.tv_sec;
1304 d->bclock.elapsed.tv_usec += it.it_value.tv_usec;
1306 if (d->bclock.elapsed.tv_usec > 1000000 - 1) {
1307 d->bclock.elapsed.tv_sec += d->bclock.elapsed.tv_usec / 1000000;
1308 d->bclock.elapsed.tv_usec = d->bclock.elapsed.tv_usec % 1000000;
1311 if (d->bclock.tc[d->bclock.tcn][1] &&
1312 d->bclock.elapsed.tv_sec >= d->bclock.tc[d->bclock.tcn][1]) {
1313 pgn_tag_add(&g->tag, "Result", "1-0");
1314 gameover(g);
1318 d->elapsed.tv_sec += it.it_value.tv_sec;
1319 d->elapsed.tv_usec += it.it_value.tv_usec;
1321 if (d->elapsed.tv_usec > 1000000 - 1) {
1322 d->elapsed.tv_sec += d->elapsed.tv_usec / 1000000;
1323 d->elapsed.tv_usec = d->elapsed.tv_usec % 1000000;
1327 static void update_time_control(GAME g)
1329 struct userdata_s *d = g->data;
1330 struct clock_s *clk = (g->turn == WHITE) ? &d->wclock : &d->bclock;
1332 if (clk->incr)
1333 clk->tc[clk->tcn][1] += clk->incr;
1335 if (!clk->tc[clk->tcn][1])
1336 return;
1338 clk->move++;
1340 if (!clk->tc[clk->tcn][0] || clk->move >= clk->tc[clk->tcn][0]) {
1341 clk->move = 0;
1342 clk->tc[clk->tcn + 1][1] += abs(clk->elapsed.tv_sec - clk->tc[clk->tcn][1]);
1343 memset(&clk->elapsed, 0, sizeof(clk->elapsed));
1344 clk->tcn++;
1348 void update_history_window(GAME g)
1350 char buf[HISTORY_WIDTH - 1];
1351 HISTORY *h = NULL;
1352 int n, total;
1353 int t = pgn_history_total(g->hp);
1355 n = (g->hindex + 1) / 2;
1357 if (t % 2)
1358 total = (t + 1) / 2;
1359 else
1360 total = t / 2;
1362 if (t)
1363 snprintf(buf, sizeof(buf), "%u %s %u%s", n, _("of"), total,
1364 (movestep == 1) ? _(" (ply)") : "");
1365 else
1366 strncpy(buf, _("not available"), sizeof(buf)-1);
1368 buf[sizeof(buf)-1] = 0;
1369 mvwprintw(historyw, 2, 1, "%*s %-*s", 10, _("Move:"),
1370 HISTORY_WIDTH - 14, buf);
1372 h = pgn_history_by_n(g->hp, g->hindex);
1373 snprintf(buf, sizeof(buf), "%s",
1374 (h && h->move) ? h->move
1375 : (LINES < 24) ? _("empty") : _("not available"));
1376 n = 0;
1378 if (h && ((h->comment) || h->nag[0])) {
1379 strncat(buf, _(" (Annotated"), sizeof(buf)-1);
1380 n++;
1383 if (h && h->rav) {
1384 strncat(buf, (n) ? ",+" : " (+", sizeof(buf)-1);
1385 n++;
1388 if (g->ravlevel) {
1389 strncat(buf, (n) ? ",-" : " (-", sizeof(buf)-1);
1390 n++;
1393 if (n)
1394 strncat(buf, ")", sizeof(buf)-1);
1396 mvwprintw(historyw, 3, ((LINES < 24) ? 17 : 1), "%s %-*s",
1397 (LINES < 24) ? _("Next:") :_("Next move:"),
1398 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1400 h = pgn_history_by_n(g->hp, g->hindex - 1);
1401 snprintf(buf, sizeof(buf), "%s",
1402 (h && h->move) ? h->move
1403 : (LINES < 24) ? _("empty") : _("not available"));
1404 n = 0;
1406 if (h && ((h->comment) || h->nag[0])) {
1407 strncat(buf, _(" (Annotated"), sizeof(buf)-1);
1408 n++;
1411 if (h && h->rav) {
1412 strncat(buf, (n) ? ",+" : " (+", sizeof(buf)-1);
1413 n++;
1416 if (g->ravlevel) {
1417 strncat(buf, (n) ? ",-" : " (-", sizeof(buf)-1);
1418 n++;
1421 if (n)
1422 strncat(buf, ")", sizeof(buf)-1);
1424 mvwprintw(historyw, ((LINES < 24) ? 3 : 4), 1, "%s %-*s",
1425 (LINES < 24) ? _("Prev.:") : _("Prev move:"),
1426 HISTORY_WIDTH - ((LINES < 24) ? 26 : 14), buf);
1429 void do_validate_move(char **move)
1431 struct userdata_s *d = gp->data;
1432 int n;
1433 char *frfr = NULL;
1435 if (TEST_FLAG(d->flags, CF_HUMAN)) {
1436 if ((n = pgn_parse_move(gp, d->b, move, &frfr)) != E_PGN_OK) {
1437 invalid_move(d->n + 1, n, *move);
1438 return;
1441 update_time_control(gp);
1442 pgn_history_add(gp, d->b, *move);
1443 pgn_switch_turn(gp);
1445 else {
1446 if ((n = pgn_validate_move(gp, d->b, move, &frfr)) != E_PGN_OK) {
1447 invalid_move(d->n + 1, n, *move);
1448 return;
1451 add_engine_command(gp, ENGINE_THINKING, "%s\n",
1452 (config.engine_protocol == 1) ? frfr : *move);
1455 d->sp.srow = d->sp.scol = d->sp.icon = 0;
1457 if (config.validmoves)
1458 pgn_reset_valid_moves(d->b);
1460 if (TEST_FLAG(gp->flags, GF_GAMEOVER))
1461 d->mode = MODE_HISTORY;
1462 else
1463 SET_FLAG(d->flags, CF_MODIFIED);
1465 free (frfr);
1466 d->paused = 0;
1467 update_history_window(gp);
1468 update_board_window(gp);
1469 return;
1472 void do_promotion_piece_finalize(WIN *win)
1474 char *p, *str = win->data;
1476 if (pgn_piece_to_int(win->c) == -1)
1477 return;
1479 p = str + strlen(str);
1480 *p++ = toupper(win->c);
1481 *p = '\0';
1482 do_validate_move(&str);
1483 free (str);
1484 win->data = NULL;
1487 static void move_to_engine(GAME g)
1489 struct userdata_s *d = g->data;
1490 char *str;
1491 int piece;
1493 if (config.validmoves &&
1494 !d->b[RANKTOBOARD(d->sp.row)][FILETOBOARD(d->sp.col)].valid)
1495 return;
1497 str = Malloc(MAX_SAN_MOVE_LEN + 1);
1498 snprintf(str, MAX_SAN_MOVE_LEN + 1, "%c%i%c%i",
1499 _("abcdefgh")[d->sp.scol - 1],
1500 d->sp.srow, _("abcdefgh")[d->sp.col - 1], d->sp.row);
1502 piece = pgn_piece_to_int(d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon);
1504 if (piece == PAWN && (d->sp.row == 8 || d->sp.row == 1)) {
1505 construct_message(_("Select Pawn Promotion Piece"), _ ("R/N/B/Q"), 1, 1, NULL, NULL,
1506 str, do_promotion_piece_finalize, 0, 0, "%s", _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1507 return;
1510 do_validate_move(&str);
1511 free (str);
1514 static char *clock_to_char(long n)
1516 static char buf[16];
1517 int h = 0, m = 0, s = 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 *timeval_to_char(struct timeval t, long limit)
1528 static char buf[9];
1529 int h = 0, m = 0, s = 0;
1530 int n = limit ? abs(limit - t.tv_sec) : 0;
1532 h = n / 3600;
1533 m = (n % 3600) / 60;
1534 s = (n % 3600) % 60;
1535 snprintf(buf, sizeof(buf), "%.2i:%.2i:%.2i", h, m, s);
1536 return buf;
1539 static char *time_control_status(struct clock_s *clk)
1541 static char buf[80] = {0};
1543 buf[0] = 0;
1545 if (clk->tc[clk->tcn][0] && clk->tc[clk->tcn + 1][1])
1546 snprintf(buf, sizeof(buf), " M%.2i/%s", abs(clk->tc[clk->tcn][0] - clk->move),
1547 clock_to_char(clk->tc[clk->tcn + 1][1]));
1548 else if (!clk->incr)
1549 return "";
1551 if (clk->incr) {
1552 char buf[16];
1553 strncat(buf, " I", sizeof(buf)-1);
1554 strncat(buf, itoa(clk->incr, buf), sizeof(buf)-1);
1557 return buf;
1560 void update_status_window(GAME g)
1562 int i = 0;
1563 char *buf;
1564 char tmp[15] = {0}, *engine, *mode;
1565 char t[COLS];
1566 int w;
1567 char *p;
1568 int maxy, maxx;
1569 int len;
1570 struct userdata_s *d = g->data;
1571 int y;
1572 int n;
1574 if (!curses_initialized)
1575 return;
1577 getmaxyx(statusw, maxy, maxx);
1578 (void)maxy;
1579 w = maxx - 2 - 8;
1580 len = maxx - 2;
1581 buf = Malloc(len);
1582 y = 2;
1584 wchar_t *loadfilew = loadfile[0] ? str_etc (loadfile, w, 1) : str_to_wchar (_ ("not available"));
1585 mvwprintw(statusw, y++, 1, "%*s %-*ls", 7, _("File:"), w, loadfilew);
1586 free (loadfilew);
1587 snprintf(buf, len, "%i %s %i", gindex + 1, _("of"), gtotal);
1588 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Game:"), w, buf);
1590 *tmp = '\0';
1591 p = tmp;
1593 if (config.details) {
1594 *p++ = 'D';
1595 i++;
1598 if (TEST_FLAG(d->flags, CF_DELETE)) {
1599 if (i)
1600 *p++ = '/';
1602 *p++ = 'X';
1603 i++;
1606 if (TEST_FLAG(g->flags, GF_PERROR)) {
1607 if (i)
1608 *p++ = '/';
1610 *p++ = '!';
1611 i++;
1614 if (TEST_FLAG(d->flags, CF_MODIFIED)) {
1615 if (i)
1616 *p++ = '/';
1618 *p++ = '*';
1619 i++;
1622 pgn_config_get(PGN_STRICT_CASTLING, &n);
1624 if (n == 1) {
1625 if (i)
1626 *p++ = '/';
1628 *p++ = 'C';
1629 i++;
1631 #ifdef WITH_LIBPERL
1632 if (TEST_FLAG(d->flags, CF_PERL)) {
1633 if (i)
1634 *p++ = '/';
1636 *p++ = 'P';
1637 i++;
1639 #endif
1641 *p = '\0';
1642 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Flags:"), w, (tmp[0]) ? tmp : "-");
1644 switch (d->mode) {
1645 case MODE_HISTORY:
1646 mode = _("move history");
1647 break;
1648 case MODE_EDIT:
1649 mode = _("edit");
1650 break;
1651 case MODE_PLAY:
1652 mode = _("play");
1653 break;
1654 default:
1655 mode = _("(empty value)");
1656 break;
1659 snprintf(buf, len - 1, "%*s %s", 7, _("Mode:"), mode);
1661 if (d->mode == MODE_PLAY) {
1662 if (TEST_FLAG(d->flags, CF_HUMAN))
1663 strncat(buf, _(" (human/human)"), len - 1);
1664 else if (TEST_FLAG(d->flags, CF_ENGINE_LOOP))
1665 strncat(buf, _(" (engine/engine)"), len - 1);
1666 else
1667 strncat(buf, (d->play_mode == PLAY_EH) ?
1668 _(" (engine/human)") : _(" (human/engine)"), len - 1);
1671 buf[len-1] = 0;
1672 mvwprintw(statusw, y++, 1, "%-*s", len, buf);
1673 free(buf);
1675 if (d->engine) {
1676 switch (d->engine->status) {
1677 case ENGINE_THINKING:
1678 engine = _("pondering...");
1679 break;
1680 case ENGINE_READY:
1681 engine = _("ready");
1682 break;
1683 case ENGINE_INITIALIZING:
1684 engine = _("initializing...");
1685 break;
1686 case ENGINE_OFFLINE:
1687 engine = _("offline");
1688 break;
1689 default:
1690 engine = _("(empty value)");
1691 break;
1694 else
1695 engine = _("offline");
1697 mvwprintw(statusw, y, 1, "%*s %-*s", 7, _("Engine:"), w, " ");
1698 wattron(statusw, CP_STATUS_ENGINE);
1699 mvwaddstr(statusw, y++, 9, engine);
1700 wattroff(statusw, CP_STATUS_ENGINE);
1702 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Turn:"), w,
1703 (g->turn == WHITE) ? _("white") : _("black"));
1705 strncpy(tmp, _("white"), sizeof(tmp)-1);
1706 tmp[0] = toupper(tmp[0]);
1707 snprintf(t, sizeof(t), "%s%s",
1708 timeval_to_char(d->wclock.elapsed, d->wclock.tc[d->wclock.tcn][1]),
1709 time_control_status(&d->wclock));
1710 mvwprintw(statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1712 strncpy(tmp, _("black"), sizeof(tmp)-1);
1713 tmp[0] = toupper(tmp[0]);
1714 snprintf(t, sizeof(t), "%s%s",
1715 timeval_to_char(d->bclock.elapsed, d->bclock.tc[d->bclock.tcn][1]),
1716 time_control_status(&d->bclock));
1717 mvwprintw(statusw, y++, 1, "%*s: %-*s", 6, tmp, w, t);
1719 mvwprintw(statusw, y++, 1, "%*s %-*s", 7, _("Total:"), w,
1720 clock_to_char(d->elapsed.tv_sec));
1722 // for (i = 0; i < STATUS_WIDTH; i++)
1723 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1725 if (!status.notify)
1726 status.notify = str_to_wchar(_("Type F1 for help"));
1728 wattron(stdscr, CP_STATUS_NOTIFY);
1729 for (i = (config.boardleft) ? BOARD_WIDTH : 0;
1730 i < ((config.boardleft) ? COLS : STATUS_WIDTH); i++)
1731 mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1732 mvwprintw(stdscr, STATUS_HEIGHT, CENTERX(STATUS_WIDTH, status.notify)
1733 + ((config.boardleft) ? BOARD_WIDTH : 0),
1734 "%ls", status.notify);
1735 wattroff(stdscr, CP_STATUS_NOTIFY);
1738 wchar_t *translate_tag_name(const char *tag)
1740 if (!strcmp (tag, "Event"))
1741 return str_to_wchar (translatable_tag_names[0]);
1742 else if (!strcmp (tag, "Site"))
1743 return str_to_wchar (translatable_tag_names[1]);
1744 else if (!strcmp (tag, "Date"))
1745 return str_to_wchar (translatable_tag_names[2]);
1746 else if (!strcmp (tag, "Round"))
1747 return str_to_wchar (translatable_tag_names[3]);
1748 else if (!strcmp (tag, "White"))
1749 return str_to_wchar (translatable_tag_names[4]);
1750 else if (!strcmp (tag, "Black"))
1751 return str_to_wchar (translatable_tag_names[5]);
1752 else if (!strcmp (tag, "Result"))
1753 return str_to_wchar (translatable_tag_names[6]);
1755 return str_to_wchar (tag);
1758 void update_tag_window(TAG **t)
1760 int i, l, w;
1761 int namel = 0;
1763 for (i = 0; t[i]; i++) {
1764 wchar_t *namewc = translate_tag_name(t[i]->name);
1766 l = wcslen(namewc);
1767 free (namewc);
1768 if (l > namel)
1769 namel = l;
1772 w = TAG_WIDTH - namel - 4;
1774 for (i = 0; t[i] && i < TAG_HEIGHT - 3; i++) {
1775 wchar_t *namewc = translate_tag_name(t[i]->name);
1776 wchar_t *valuewc = str_etc(t[i]->value, w, 0);
1778 mvwprintw(tagw, (i + 2), 1, "%*ls: %-*ls", namel, namewc, w, valuewc);
1779 free (namewc);
1780 free (valuewc);
1783 for (; i < TAG_HEIGHT - 3; i++)
1784 mvwprintw(tagw, (i + 2), 1, "%*s", namel + w + 2, " ");
1787 void append_enginebuf(GAME g, char *line)
1789 int i = 0;
1790 struct userdata_s *d = g->data;
1792 if (d->engine->enginebuf)
1793 for (i = 0; d->engine->enginebuf[i]; i++);
1795 if (i >= LINES - 3) {
1796 free(d->engine->enginebuf[0]);
1798 for (i = 0; d->engine->enginebuf[i+1]; i++)
1799 d->engine->enginebuf[i] = d->engine->enginebuf[i+1];
1801 d->engine->enginebuf[i] = strdup(line);
1803 else {
1804 d->engine->enginebuf = Realloc(d->engine->enginebuf, (i + 2) * sizeof(char *));
1805 d->engine->enginebuf[i++] = strdup(line);
1806 d->engine->enginebuf[i] = NULL;
1810 void update_engine_window(GAME g)
1812 int i;
1813 struct userdata_s *d = g->data;
1815 wmove(enginew, 0, 0);
1816 wclrtobot(enginew);
1818 if (d->engine && d->engine->enginebuf) {
1819 for (i = 0; d->engine->enginebuf[i]; i++)
1820 mvwprintw(enginew, i + 2, 1, "%s", d->engine->enginebuf[i]);
1823 window_draw_title(enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
1824 CP_MESSAGE_BORDER);
1827 void update_all(GAME g)
1829 struct userdata_s *d = g->data;
1832 * In the middle of a macro. Don't update the screen.
1834 if (macro_match != -1)
1835 return;
1837 wmove(boardw, ROWTOMATRIX(d->c_row), COLTOMATRIX(d->c_col));
1838 update_board_window(g);
1839 update_status_window(g);
1840 update_history_window(g);
1841 update_tag_window(g->tag);
1842 update_engine_window(g);
1843 update_panels();
1844 doupdate();
1847 static void game_next_prev(GAME g, int n, int count)
1849 if (gtotal < 2)
1850 return;
1852 if (n == 1) {
1853 if (gindex + count > gtotal - 1) {
1854 if (count != 1)
1855 gindex = gtotal - 1;
1856 else
1857 gindex = 0;
1859 else
1860 gindex += count;
1862 else {
1863 if (gindex - count < 0) {
1864 if (count != 1)
1865 gindex = 0;
1866 else
1867 gindex = gtotal - 1;
1869 else
1870 gindex -= count;
1873 gp = game[gindex];
1876 static void delete_game(int which)
1878 int i, w = which;
1879 struct userdata_s *d;
1881 for (i = 0; i < gtotal; i++) {
1882 d = game[i]->data;
1884 if (i == w || TEST_FLAG(d->flags, CF_DELETE)) {
1885 int n;
1887 free_userdata_once(game[i]);
1888 pgn_free(game[i]);
1890 for (n = i; n+1 < gtotal; n++)
1891 game[n] = game[n+1];
1893 gtotal--;
1894 i--;
1895 w = -1;
1899 if (which != -1) {
1900 if (which + 1 >= gtotal)
1901 gindex = gtotal - 1;
1902 else
1903 gindex = which;
1905 else
1906 gindex = gtotal - 1;
1908 gp = game[gindex];
1909 gp->hp = gp->history;
1913 * FIXME find across multiple games.
1915 static int find_move_exp(GAME g, regex_t r, int which, int count)
1917 int i;
1918 int ret;
1919 char errbuf[255];
1920 int incr;
1921 int found;
1923 incr = (which == 0) ? -1 : 1;
1925 for (i = g->hindex + incr - 1, found = 0; ; i += incr) {
1926 if (i == g->hindex - 1)
1927 break;
1929 if (i >= pgn_history_total(g->hp))
1930 i = 0;
1931 else if (i < 0)
1932 i = pgn_history_total(g->hp) - 1;
1934 // FIXME RAV
1935 ret = regexec(&r, g->hp[i]->move, 0, 0, 0);
1937 if (ret == 0) {
1938 if (count == ++found) {
1939 return i + 1;
1942 else {
1943 if (ret != REG_NOMATCH) {
1944 regerror(ret, &r, errbuf, sizeof(errbuf));
1945 cmessage(_("Error Matching Regular Expression"), ANY_KEY_STR, "%s", errbuf);
1946 return -1;
1951 return -1;
1954 static int toggle_delete_flag(int n)
1956 int i, x;
1957 struct userdata_s *d = game[n]->data;
1959 TOGGLE_FLAG(d->flags, CF_DELETE);
1960 gindex = n;
1962 for (i = x = 0; i < gtotal; i++) {
1963 d = game[i]->data;
1965 if (TEST_FLAG(d->flags, CF_DELETE))
1966 x++;
1969 if (x == gtotal) {
1970 cmessage(NULL, ANY_KEY_STR, "%s", _("Cannot delete last game."));
1971 d = game[n]->data;
1972 CLEAR_FLAG(d->flags, CF_DELETE);
1973 return 1;
1976 return 0;
1979 static int find_game_exp(char *str, int which, int count)
1981 char *nstr = NULL, *exp = NULL;
1982 regex_t nexp, vexp;
1983 int ret = -1;
1984 int g = 0;
1985 char buf[255] = {0}, *tmp;
1986 char errbuf[255];
1987 int found = 0;
1988 int incr = (which == 0) ? -(1) : 1;
1990 strncpy(buf, str, sizeof(buf)-1);
1991 tmp = buf;
1993 if (strstr(tmp, ":") != NULL) {
1994 nstr = strsep(&tmp, ":");
1996 if ((ret = regcomp(&nexp, nstr,
1997 REG_ICASE|REG_EXTENDED|REG_NOSUB)) != 0) {
1998 regerror(ret, &nexp, errbuf, sizeof(errbuf));
1999 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
2000 ret = g = -1;
2001 goto cleanup;
2005 exp = tmp;
2007 while (*exp && isspace(*exp))
2008 exp++;
2010 if (exp == NULL)
2011 goto cleanup;
2013 if ((ret = regcomp(&vexp, exp, REG_EXTENDED|REG_NOSUB)) != 0) {
2014 regerror(ret, &vexp, errbuf, sizeof(errbuf));
2015 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
2016 ret = -1;
2017 goto cleanup;
2020 ret = -1;
2022 for (g = gindex + incr, found = 0; ; g += incr) {
2023 int t;
2025 if (g == gtotal)
2026 g = 0;
2027 else if (g < 0)
2028 g = gtotal - 1;
2030 if (g == gindex)
2031 break;
2033 for (t = 0; game[g]->tag[t]; t++) {
2034 if (nstr) {
2035 if (regexec(&nexp, game[g]->tag[t]->name, 0, 0, 0) == 0) {
2036 if (regexec(&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0) {
2037 if (count == ++found) {
2038 ret = g;
2039 goto cleanup;
2044 else {
2045 if (regexec(&vexp, game[g]->tag[t]->value, 0, 0, 0) == 0) {
2046 if (count == ++found) {
2047 ret = g;
2048 goto cleanup;
2054 ret = -1;
2057 cleanup:
2058 if (nstr)
2059 regfree(&nexp);
2061 if (g != -1)
2062 regfree(&vexp);
2064 return ret;
2068 * Updates the notification line in the status window then refreshes the
2069 * status window.
2071 void update_status_notify(GAME g, char *fmt, ...)
2073 va_list ap;
2074 #ifdef HAVE_VASPRINTF
2075 char *line;
2076 #else
2077 char line[COLS];
2078 #endif
2080 free(status.notify);
2081 status.notify = NULL;
2083 if (!fmt)
2084 return;
2086 va_start(ap, fmt);
2087 #ifdef HAVE_VASPRINTF
2088 vasprintf(&line, fmt, ap);
2089 #else
2090 vsnprintf(line, sizeof(line), fmt, ap);
2091 #endif
2092 va_end(ap);
2094 status.notify = str_to_wchar(line);
2096 #ifdef HAVE_VASPRINTF
2097 free(line);
2098 #endif
2101 int rav_next_prev(GAME g, BOARD b, int n)
2103 // Next RAV.
2104 if (n) {
2105 if ((!g->ravlevel && g->hindex && g->hp[g->hindex - 1]->rav == NULL) ||
2106 (!g->ravlevel && !g->hindex && g->hp[g->hindex]->rav == NULL) ||
2107 (g->ravlevel && g->hp[g->hindex]->rav == NULL))
2108 return 1;
2110 g->rav = Realloc(g->rav, (g->ravlevel + 1) * sizeof(RAV));
2111 g->rav[g->ravlevel].hp = g->hp;
2112 g->rav[g->ravlevel].flags = g->flags;
2113 g->rav[g->ravlevel].fen = pgn_game_to_fen(g, b);
2114 g->rav[g->ravlevel].hindex = g->hindex;
2115 g->hp = (!g->ravlevel) ? (g->hindex) ? g->hp[g->hindex - 1]->rav : g->hp[g->hindex]->rav : g->hp[g->hindex]->rav;
2116 g->hindex = 0;
2117 g->ravlevel++;
2118 pgn_board_update(g, b, g->hindex + 1);
2119 return 0;
2122 if (g->ravlevel - 1 < 0)
2123 return 1;
2125 // Previous RAV.
2126 g->ravlevel--;
2127 pgn_board_init_fen(g, b, g->rav[g->ravlevel].fen);
2128 free(g->rav[g->ravlevel].fen);
2129 g->hp = g->rav[g->ravlevel].hp;
2130 g->flags = g->rav[g->ravlevel].flags;
2131 g->hindex = g->rav[g->ravlevel].hindex;
2132 return 0;
2135 static void draw_window_decor()
2137 move_panel(boardp, 0,
2138 (config.boardleft) ? 0 : COLS - BOARD_WIDTH);
2139 move_panel(historyp, LINES - HISTORY_HEIGHT,
2140 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH : 0 :
2141 (MEGA_BOARD) ? 0 : COLS - HISTORY_WIDTH);
2142 move_panel(statusp, 0,
2143 (config.boardleft) ? BOARD_WIDTH : 0);
2144 move_panel(tagp, STATUS_HEIGHT + 1,
2145 (config.boardleft) ? (MEGA_BOARD) ? BOARD_WIDTH :
2146 HISTORY_WIDTH : 0);
2148 wbkgd(boardw, CP_BOARD_WINDOW);
2149 wbkgd(statusw, CP_STATUS_WINDOW);
2150 window_draw_title(statusw, _("Game Status"), STATUS_WIDTH,
2151 CP_STATUS_TITLE, CP_STATUS_BORDER);
2152 wbkgd(tagw, CP_TAG_WINDOW);
2153 window_draw_title(tagw, _("Roster Tags"), TAG_WIDTH, CP_TAG_TITLE,
2154 CP_TAG_BORDER);
2155 wbkgd(historyw, CP_HISTORY_WINDOW);
2156 window_draw_title(historyw, _("Move History"), HISTORY_WIDTH,
2157 CP_HISTORY_TITLE, CP_HISTORY_BORDER);
2160 void do_window_resize()
2162 if (LINES < 23 || COLS < 74)
2163 return;
2165 resizeterm(LINES, COLS);
2166 endwin();
2167 wresize(boardw, BOARD_HEIGHT, BOARD_WIDTH);
2168 wresize(historyw, HISTORY_HEIGHT, HISTORY_WIDTH);
2169 wresize(statusw, STATUS_HEIGHT, STATUS_WIDTH);
2170 wresize(tagw, TAG_HEIGHT, TAG_WIDTH);
2171 clear ();
2172 wclear (boardw);
2173 wclear (historyw);
2174 wclear (tagw);
2175 wclear (statusw);
2176 wclear (loadingw);
2177 wclear (enginew);
2178 draw_window_decor();
2179 update_all(gp);
2180 keypad(boardw, TRUE);
2181 curs_set(0);
2182 cbreak();
2183 noecho();
2186 void do_global_redraw ()
2188 do_window_resize ();
2191 void stop_clock()
2193 memset(&clock_timer, 0, sizeof(struct itimerval));
2194 setitimer(ITIMER_REAL, &clock_timer, NULL);
2197 void start_clock(GAME g)
2199 struct userdata_s *d = g->data;
2201 if (clock_timer.it_interval.tv_usec)
2202 return;
2204 memset(&d->elapsed, 0, sizeof(struct timeval));
2205 clock_timer.it_value.tv_sec = 0;
2206 clock_timer.it_value.tv_usec = 100000;
2207 clock_timer.it_interval.tv_sec = 0;
2208 clock_timer.it_interval.tv_usec = 100000;
2209 setitimer(ITIMER_REAL, &clock_timer, NULL);
2212 static void update_clocks()
2214 int i;
2215 struct userdata_s *d;
2216 struct itimerval it;
2217 int update = 0;
2219 getitimer(ITIMER_REAL, &it);
2221 for (i = 0; i < gtotal; i++) {
2222 d = game[i]->data;
2224 if (d && d->mode == MODE_PLAY) {
2225 if (d->paused == 1 || TEST_FLAG(d->flags, CF_NEW))
2226 continue;
2227 else if (d->paused == -1) {
2228 if (game[i]->side == game[i]->turn) {
2229 d->paused = 1;
2230 continue;
2234 update_clock(game[i], it);
2236 if (game[i] == gp)
2237 update = 1;
2241 if (update) {
2242 update_status_window(gp);
2243 update_panels();
2244 doupdate();
2248 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2250 static int parse_clock_time(char **str)
2252 char *p = *str;
2253 int n = 0, t = 0;
2255 SKIP_SPACE(p);
2257 if (!isdigit(*p))
2258 return -1;
2260 while (*p) {
2261 if (isdigit(*p)) {
2262 t = atoi(p);
2264 while (isdigit(*p))
2265 p++;
2267 continue;
2270 switch (*p) {
2271 case 'H':
2272 case 'h':
2273 n += t * (60 * 60);
2274 t = 0;
2275 break;
2276 case 'M':
2277 case 'm':
2278 n += t * 60;
2279 t = 0;
2280 break;
2281 case 'S':
2282 case 's':
2283 n += t;
2284 t = 0;
2285 break;
2286 case ' ':
2287 p++;
2288 case '/':
2289 case '+':
2290 goto done;
2291 default:
2292 *str = p;
2293 return -1;
2296 p++;
2299 done:
2300 n += t;
2301 *str = p;
2302 return n;
2305 static int parse_clock_input(struct clock_s *clk, char *str, int *incr)
2307 char *p = str;
2308 long n = 0;
2309 int plus = 0;
2310 int m = 0;
2311 int tc = 0;
2313 SKIP_SPACE(p);
2315 if (!*p)
2316 return 0;
2318 if (*p == '+') {
2319 plus = 1;
2320 p++;
2321 SKIP_SPACE(p);
2323 if (*p == '+')
2324 goto move_incr;
2326 else
2327 memset(clk, 0, sizeof(struct clock_s));
2329 again:
2330 /* Sudden death. */
2331 if (strncasecmp(p, "SD", 2) == 0) {
2332 n = 0;
2333 p += 2;
2334 goto tc;
2337 n = parse_clock_time(&p);
2339 if (n == -1)
2340 return 1;
2342 if (!n)
2343 goto done;
2345 /* Time control. */
2347 if (*p == '/') {
2348 if (plus)
2349 return 1;
2351 /* Sudden death without a previous time control. */
2352 if (!n && !tc)
2353 return 1;
2355 m = n;
2356 p++;
2357 n = parse_clock_time(&p);
2359 if (n == -1)
2360 return 1;
2362 if (tc >= MAX_TC) {
2363 message(ERROR_STR, ANY_KEY_STR, "%s (%i)", _("Maximum number of time controls reached"), MAX_TC);
2364 return 1;
2367 clk->tc[tc][0] = m;
2368 clk->tc[tc++][1] = n;
2369 SKIP_SPACE(p);
2371 if (*p == '+')
2372 goto move_incr;
2374 if (*p)
2375 goto again;
2377 goto done;
2380 if (plus)
2381 *incr = n;
2382 else
2383 clk->tc[clk->tcn][1] = (n <= clk->elapsed.tv_sec) ? clk->elapsed.tv_sec + n : n;
2385 move_incr:
2386 if (*p) {
2387 if (*p++ == '+') {
2388 if (!isdigit(*p))
2389 return 1;
2391 n = parse_clock_time(&p);
2393 if (n == -1 || *p)
2394 return 1;
2396 clk->incr = n;
2398 SKIP_SPACE(p);
2400 if (*p)
2401 return 1;
2403 else
2404 return 1;
2407 done:
2408 return 0;
2411 static int parse_which_clock(struct clock_s *clk, char *str)
2413 struct clock_s tmp;
2414 int incr = 0;
2416 memcpy(&tmp, clk, sizeof(struct clock_s));
2418 if (parse_clock_input(&tmp, str, &incr)) {
2419 cmessage(ERROR_STR, ANY_KEY_STR, _("Invalid clock specification"));
2420 return 1;
2423 memcpy(clk, &tmp, sizeof(struct clock_s));
2424 clk->tc[clk->tcn][1] += incr;
2425 return 0;
2428 void do_clock_input_finalize(WIN *win)
2430 struct userdata_s *d = gp->data;
2431 struct input_data_s *in = win->data;
2432 char *p = in->str;
2434 if (!in->str) {
2435 free(in);
2436 return;
2439 SKIP_SPACE(p);
2441 if (tolower(*p) == 'w') {
2442 p++;
2444 if (parse_which_clock(&d->wclock, p))
2445 goto done;
2447 else if (tolower(*p) == 'b') {
2448 p++;
2450 if (parse_which_clock(&d->bclock, p))
2451 goto done;
2453 else {
2454 if (parse_which_clock(&d->wclock, p))
2455 goto done;
2457 if (parse_which_clock(&d->bclock, p))
2458 goto done;
2461 if (!d->wclock.tc[0][1] && !d->bclock.tc[0][1])
2462 CLEAR_FLAG(d->flags, CF_CLOCK);
2463 else
2464 SET_FLAG(d->flags, CF_CLOCK);
2466 done:
2467 free(in->str);
2468 free(in);
2471 void do_engine_command_finalize(WIN *win)
2473 struct userdata_s *d = gp->data;
2474 struct input_data_s *in = win->data;
2475 int x;
2477 if (!in->str) {
2478 free(in);
2479 return;
2482 if (!d->engine)
2483 goto done;
2485 x = d->engine->status;
2486 send_to_engine(gp, -1, "%s\n", in->str);
2487 d->engine->status = x;
2489 done:
2490 free(in->str);
2491 free(in);
2494 void do_board_details()
2496 config.details = (config.details) ? 0 : 1;
2499 void do_toggle_strict_castling()
2501 int n;
2503 pgn_config_get(PGN_STRICT_CASTLING, &n);
2505 if (n == 0)
2506 pgn_config_set(PGN_STRICT_CASTLING, 1);
2507 else
2508 pgn_config_set(PGN_STRICT_CASTLING, 0);
2511 void do_play_set_clock()
2513 struct input_data_s *in;
2515 in = Calloc(1, sizeof(struct input_data_s));
2516 in->efunc = do_clock_input_finalize;
2517 construct_input(_("Set Clock"), NULL, 1, 1,
2518 _ ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n" \
2519 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2520 NULL, NULL, 0, in, INPUT_HIST_CLOCK, -1);
2523 void do_play_toggle_human()
2525 struct userdata_s *d = gp->data;
2527 TOGGLE_FLAG(d->flags, CF_HUMAN);
2529 if (!TEST_FLAG(d->flags, CF_HUMAN) && pgn_history_total(gp->hp)) {
2530 if (init_chess_engine(gp))
2531 return;
2534 CLEAR_FLAG(d->flags, CF_ENGINE_LOOP);
2536 if (d->engine)
2537 d->engine->status = ENGINE_READY;
2540 void do_play_toggle_engine()
2542 struct userdata_s *d = gp->data;
2544 TOGGLE_FLAG(d->flags, CF_ENGINE_LOOP);
2545 CLEAR_FLAG(d->flags, CF_HUMAN);
2547 if (d->engine && TEST_FLAG(d->flags, CF_ENGINE_LOOP)) {
2548 char *fen = pgn_game_to_fen (gp, d->b);
2550 pgn_board_update(gp, d->b,
2551 pgn_history_total(gp->hp));
2552 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
2553 free (fen);
2558 * This will send a command to the engine skipping the command queue.
2560 void do_play_send_command()
2562 struct userdata_s *d = gp->data;
2563 struct input_data_s *in;
2565 if (!d->engine || d->engine->status == ENGINE_OFFLINE) {
2566 if (init_chess_engine(gp))
2567 return;
2570 in = Calloc(1, sizeof(struct input_data_s));
2571 in->efunc = do_engine_command_finalize;
2572 construct_input(_("Engine Command"), NULL, 1, 1, NULL, NULL, NULL, 0, in, INPUT_HIST_ENGINE, -1);
2575 void do_play_switch_turn()
2577 struct userdata_s *d = gp->data;
2579 pgn_switch_side(gp);
2580 pgn_switch_turn(gp);
2582 if (!TEST_FLAG(d->flags, CF_HUMAN))
2583 add_engine_command(gp, -1,
2584 (gp->side == WHITE) ? "white\n" : "black\n");
2586 update_status_window(gp);
2589 void do_play_toggle_eh_mode()
2591 struct userdata_s *d = gp->data;
2593 if (!TEST_FLAG(d->flags, CF_HUMAN)) {
2594 if (!gp->hindex){
2595 pgn_switch_side(gp, TRUE);
2596 d->play_mode = (d->play_mode) ? PLAY_HE : PLAY_EH;
2597 if (gp->side == BLACK)
2598 update_status_notify(gp, _("Press 'g' to start the game"));
2600 rotate = (rotate) ? FALSE : TRUE;
2602 else
2603 message(NULL, ANY_KEY_STR,
2604 _("You may only switch sides at the start of the \n"
2605 "game. Press ^K or ^N to begin a new game."));
2609 void do_play_undo()
2611 struct userdata_s *d = gp->data;
2613 if (!pgn_history_total(gp->hp))
2614 return;
2616 if (keycount) {
2617 if (gp->hindex - keycount < 0)
2618 gp->hindex = 0;
2619 else {
2620 if (go_move)
2621 gp->hindex -= (keycount * 2) -1;
2622 else
2623 gp->hindex -= keycount * 2;
2626 else {
2627 if (gp->hindex - 2 < 0)
2628 gp->hindex = 0;
2629 else {
2630 if (go_move)
2631 gp->hindex -= 1;
2632 else
2633 gp->hindex -= 2;
2637 pgn_history_free(gp->hp, gp->hindex);
2638 gp->hindex = pgn_history_total(gp->hp);
2639 pgn_board_update(gp, d->b, gp->hindex);
2641 if (d->engine && d->engine->status == ENGINE_READY) {
2642 char *fen = pgn_game_to_fen(gp, d->b);
2644 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
2645 free (fen);
2646 d->engine->status = ENGINE_READY;
2649 update_history_window(gp);
2651 if (go_move) {
2652 pgn_switch_side(gp, FALSE);
2653 go_move--;
2657 void do_play_toggle_pause()
2659 struct userdata_s *d = gp->data;
2661 if (!TEST_FLAG(d->flags, CF_HUMAN) && gp->turn !=
2662 gp->side) {
2663 d->paused = -1;
2664 return;
2667 d->paused = (d->paused) ? 0 : 1;
2670 void do_play_go()
2672 struct userdata_s *d = gp->data;
2674 if (TEST_FLAG(d->flags, CF_HUMAN))
2675 return;
2677 if (fm_loaded_file && gp->side != gp->turn) {
2678 pgn_switch_side(gp, FALSE);
2679 add_engine_command(gp, ENGINE_THINKING, "black\n");
2682 add_engine_command(gp, ENGINE_THINKING, "go\n");
2684 // Completa la función para que permita seguir jugando al usarla.
2685 // Complete the function to allow continue playing when using.
2686 if (gp->side == gp->turn)
2687 pgn_switch_side(gp, FALSE);
2689 go_move++;
2692 void do_play_config_command()
2694 int x, w;
2696 if (config.keys) {
2697 for (x = 0; config.keys[x]; x++) {
2698 if (config.keys[x]->c == input_c) {
2699 switch (config.keys[x]->type) {
2700 case KEY_DEFAULT:
2701 add_engine_command(gp, -1, "%ls\n",
2702 config.keys[x]->str);
2703 break;
2704 case KEY_SET:
2705 if (!keycount)
2706 break;
2708 add_engine_command(gp, -1,
2709 "%ls %i\n", config.keys[x]->str, keycount);
2710 keycount = 0;
2711 break;
2712 case KEY_REPEAT:
2713 if (!keycount)
2714 break;
2716 for (w = 0; w < keycount; w++)
2717 add_engine_command(gp, -1,
2718 "%ls\n", config.keys[x]->str);
2719 keycount = 0;
2720 break;
2726 update_status_notify(gp, NULL);
2729 void do_play_cancel_selected()
2731 struct userdata_s *d = gp->data;
2733 d->sp.icon = d->sp.srow = d->sp.scol = 0;
2734 keycount = 0;
2735 pgn_reset_valid_moves(d->b);
2736 update_status_notify(gp, NULL);
2739 void do_play_commit()
2741 struct userdata_s *d = gp->data;
2743 pushkey = keycount = 0;
2744 update_status_notify(gp, NULL);
2746 if (!TEST_FLAG(d->flags, CF_HUMAN) &&
2747 (!d->engine || d->engine->status == ENGINE_THINKING))
2748 return;
2750 if (!d->sp.icon)
2751 return;
2753 d->sp.row = d->c_row;
2754 d->sp.col = d->c_col;
2756 if (rotate) {
2757 rotate_position(SP_POSITION);
2758 rotate_position(SPS_POSITION);
2761 move_to_engine(gp);
2763 // Completa la función para que permita seguir jugando cuando se carga un
2764 // archivo pgn (con juego no terminado) que inicie con turno del lado
2765 // negro.
2766 // Complete the function to allow continue playing when loading a file
2767 // pgn (with unfinished game) you start to turn black side.
2768 if (gp->side != gp->turn)
2769 pgn_switch_side(gp, FALSE);
2771 if (rotate && d->sp.icon)
2772 rotate_position(SPS_POSITION);
2774 // Envia comando 'go' a Polyglot en el primer movimiento debido a que
2775 // polyglot no envia el movimiento y cboard se queda esperando.
2776 // Send command 'go' to the first movement Polyglot because cboard
2777 // waits to send polyglot movement and this does not make.
2778 if (config.fmpolyglot &&
2779 ((gp->side == WHITE && !gp->hindex) || fm_loaded_file))
2780 add_engine_command(gp, ENGINE_THINKING, "go\n");
2782 go_move = 0;
2783 fm_loaded_file = FALSE;
2786 void do_play_select()
2788 struct userdata_s *d = gp->data;
2790 if (!TEST_FLAG(d->flags, CF_HUMAN) && (!d->engine ||
2791 d->engine->status == ENGINE_OFFLINE)) {
2792 if (init_chess_engine(gp))
2793 return;
2796 if (d->engine && d->engine->status == ENGINE_THINKING)
2797 return;
2799 if (d->sp.icon)
2800 do_play_cancel_selected ();
2802 if (rotate)
2803 rotate_position(CURSOR_POSITION);
2805 d->sp.icon = d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon;
2807 if (pgn_piece_to_int(d->sp.icon) == OPEN_SQUARE) {
2808 d->sp.icon = 0;
2809 return;
2812 if (((islower(d->sp.icon) && gp->turn != BLACK)
2813 || (isupper(d->sp.icon) && gp->turn != WHITE))) {
2814 struct key_s **k;
2815 char *str = Malloc (512);
2817 for (k = play_keys; *k; k++) {
2818 if ((*k)->f == do_play_toggle_eh_mode)
2819 break;
2822 snprintf (str, 512, _("It is not your turn to move. You may switch playing sides by pressing \"%lc\"."),
2823 *k ? (*k)->c : '?');
2824 message(NULL, ANY_KEY_STR, "%s", str);
2825 free (str);
2826 d->sp.icon = 0;
2827 return;
2828 #if 0
2829 if (pgn_history_total(gp->hp)) {
2830 message(NULL, ANY_KEY_STR, "%s", _("It is not your turn to move. You can switch sides "));
2831 d->sp.icon = 0;
2832 return;
2834 else {
2835 if (pgn_tag_find(gp->tag, "FEN") != E_PGN_ERR)
2836 return;
2838 add_engine_command(gp, ENGINE_READY, "black\n");
2839 pgn_switch_turn(gp);
2841 if (gp->side != BLACK)
2842 pgn_switch_side(gp);
2844 #endif
2847 d->sp.srow = d->c_row;
2848 d->sp.scol = d->c_col;
2850 if (config.validmoves)
2851 pgn_find_valid_moves(gp, d->b, d->sp.scol, d->sp.srow);
2853 if (rotate) {
2854 rotate_position(CURSOR_POSITION);
2855 rotate_position(SPS_POSITION);
2858 CLEAR_FLAG(d->flags, CF_NEW);
2859 start_clock(gp);
2862 static wchar_t *build_help(struct key_s **keys)
2864 int i, nlen = 1, len, t, n;
2865 wchar_t *buf = NULL, *wc = NULL;
2866 wchar_t *p;
2868 if (!keys)
2869 return NULL;
2871 for (i = len = t = 0; keys[i]; i++) {
2872 if (!keys[i]->d)
2873 continue;
2875 if (keys[i]->key) {
2876 if (wcslen(keys[i]->key) > nlen) {
2877 nlen = wcslen(keys[i]->key);
2878 t += nlen;
2880 else
2881 t++;
2883 else
2884 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 + 1;
2896 buf = Malloc((t+1)*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 wcsncat(buf, keys[i]->key, t-1);
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 wcsncat(buf, keys[i]->d, t-1);
2928 if (keys[i]->r) {
2929 wc = str_to_wchar ("*");
2930 wcsncat(buf, wc, t-1);
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_global_help ()
2945 wchar_t *buf = build_help(global_keys);
2947 construct_message(_("Global Game Keys (* = can take a repeat count)"),
2948 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
2949 0, 1, "%ls", buf);
2952 void do_main_help(WIN *win)
2954 switch (win->c) {
2955 case 'p':
2956 do_play_help ();
2957 break;
2958 case 'h':
2959 do_history_help ();
2960 break;
2961 case 'e':
2962 do_edit_help ();
2963 break;
2964 case 'g':
2965 do_global_help ();
2966 break;
2967 default:
2968 break;
2972 static void do_more_help(WIN *win)
2974 if (win->c == KEY_F(1) || win->c == CTRL_KEY('g'))
2975 construct_message(_("Command Key Index"),
2976 _ ("p/h/e/g or any other key to quit"), 0, 0,
2977 NULL, NULL, NULL, do_main_help, 0, 0, "%s",
2979 "p - play mode keys\n"
2980 "h - history mode keys\n"
2981 "e - board edit mode keys\n"
2982 "g - global game keys"
2986 void do_play_help()
2988 wchar_t *buf = build_help(play_keys);
2990 construct_message(_("Play Mode Keys (* = can take a repeat count)"),
2991 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
2992 buf, do_more_help, 0, 1, "%ls", buf);
2995 void do_play_history_mode()
2997 struct userdata_s *d = gp->data;
2999 if (!pgn_history_total(gp->hp) ||
3000 (d->engine && d->engine->status == ENGINE_THINKING))
3001 return;
3003 d->mode = MODE_HISTORY;
3004 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3007 void do_play_edit_mode()
3009 struct userdata_s *d = gp->data;
3011 if (pgn_history_total(gp->hp))
3012 return;
3014 pgn_board_init_fen(gp, d->b, NULL);
3015 config.details++;
3016 d->mode = MODE_EDIT;
3019 void do_edit_insert_finalize(WIN *win)
3021 struct userdata_s *d = win->data;
3023 if (pgn_piece_to_int(win->c) == -1)
3024 return;
3026 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon = win->c;
3029 void do_edit_select()
3031 struct userdata_s *d = gp->data;
3033 if (d->sp.icon)
3034 return;
3036 d->sp.icon = d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon;
3038 if (pgn_piece_to_int(d->sp.icon) == OPEN_SQUARE) {
3039 d->sp.icon = 0;
3040 return;
3043 d->sp.srow = d->c_row;
3044 d->sp.scol = d->c_col;
3047 void do_edit_commit()
3049 int p;
3050 struct userdata_s *d = gp->data;
3052 pushkey = keycount = 0;
3053 update_status_notify(gp, NULL);
3055 if (!d->sp.icon)
3056 return;
3058 d->sp.row = d->c_row;
3059 d->sp.col = d->c_col;
3060 p = d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon;
3061 d->b[RANKTOBOARD(d->sp.row)][FILETOBOARD(d->sp.col)].icon = p;
3062 d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon =
3063 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3064 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3067 void do_edit_delete()
3069 struct userdata_s *d = gp->data;
3071 if (d->sp.icon)
3072 d->b[RANKTOBOARD(d->sp.srow)][FILETOBOARD(d->sp.scol)].icon =
3073 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3074 else
3075 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon =
3076 pgn_int_to_piece(gp->turn, OPEN_SQUARE);
3078 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3081 void do_edit_cancel_selected()
3083 struct userdata_s *d = gp->data;
3085 d->sp.icon = d->sp.srow = d->sp.scol = 0;
3086 keycount = 0;
3087 update_status_notify(gp, NULL);
3090 void do_edit_switch_turn()
3092 pgn_switch_turn(gp);
3095 void do_edit_toggle_castle()
3097 struct userdata_s *d = gp->data;
3099 castling_state(gp, d->b, RANKTOBOARD(d->c_row),
3100 FILETOBOARD(d->c_col),
3101 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].icon, 1);
3104 void do_edit_insert()
3106 struct userdata_s *d = gp->data;
3108 construct_message(_("Insert Piece"), _("P=pawn, R=rook, N=knight, B=bishop, "), 0, 0, NULL, NULL,
3109 d->b, do_edit_insert_finalize, 0, 0, "%s", _("Type the piece letter to insert. Lowercase "));
3112 void do_edit_enpassant()
3114 struct userdata_s *d = gp->data;
3116 if (d->c_row == 6 || d->c_row == 3) {
3117 pgn_reset_enpassant(d->b);
3118 d->b[RANKTOBOARD(d->c_row)][FILETOBOARD(d->c_col)].enpassant = 1;
3122 void do_edit_help()
3124 wchar_t *buf = build_help(edit_keys);
3126 construct_message(_("Edit Mode Keys (* = can take a repeat count)"),
3127 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL, buf, do_more_help,
3128 0, 1, "%ls", buf);
3131 void do_edit_exit()
3133 struct userdata_s *d = gp->data;
3134 char *fen = pgn_game_to_fen(gp, d->b);
3136 config.details--;
3137 pgn_tag_add(&gp->tag, "FEN", fen);
3138 free (fen);
3139 pgn_tag_add(&gp->tag, "SetUp", "1");
3140 pgn_tag_sort(gp->tag);
3141 pgn_board_update(gp, d->b, gp->hindex);
3142 d->mode = MODE_PLAY;
3145 void really_do_annotate_finalize(struct input_data_s *in,
3146 struct userdata_s *d)
3148 HISTORY *h = in->data;
3149 int len;
3151 if (!in->str) {
3152 if (h->comment) {
3153 free(h->comment);
3154 h->comment = NULL;
3157 else {
3158 len = strlen(in->str);
3159 h->comment = Realloc(h->comment, len+1);
3160 strncpy(h->comment, in->str, len);
3161 h->comment[len] = 0;
3164 free(in->str);
3165 free(in);
3166 SET_FLAG(d->flags, CF_MODIFIED);
3169 void do_annotate_finalize(WIN *win)
3171 struct userdata_s *d = gp->data;
3172 struct input_data_s *in = win->data;
3174 really_do_annotate_finalize(in, d);
3177 void do_find_move_exp_finalize(int init, int which)
3179 int n;
3180 struct userdata_s *d = gp->data;
3181 static int firstrun;
3182 static regex_t r;
3183 int ret;
3184 char errbuf[255];
3186 if (init || !firstrun) {
3187 if (!firstrun)
3188 regfree(&r);
3190 if ((ret = regcomp(&r, moveexp, REG_EXTENDED|REG_NOSUB)) != 0) {
3191 regerror(ret, &r, errbuf, sizeof(errbuf));
3192 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR, "%s", errbuf);
3193 return;
3196 firstrun = 1;
3199 if ((n = find_move_exp(gp, r,
3200 (which == -1) ? 0 : 1, (keycount) ? keycount : 1)) == -1)
3201 return;
3203 gp->hindex = n;
3204 pgn_board_update(gp, d->b, gp->hindex);
3207 void do_find_move_exp(WIN *win)
3209 struct input_data_s *in = win->data;
3210 int *n = in->data;
3211 int which = *n;
3213 if (in->str) {
3214 strncpy(moveexp, in->str, sizeof(moveexp)-1);
3215 moveexp[sizeof(moveexp)-1] = 0;
3216 do_find_move_exp_finalize(1, which);
3217 free(in->str);
3220 free(in->data);
3221 free(in);
3224 void do_move_jump_finalize(int n)
3226 struct userdata_s *d = gp->data;
3228 if (n < 0 || n > (pgn_history_total(gp->hp) / 2))
3229 return;
3231 keycount = 0;
3232 update_status_notify(gp, NULL);
3233 gp->hindex = (n) ? n * 2 - 1 : n * 2;
3234 pgn_board_update(gp, d->b, gp->hindex);
3237 void do_move_jump(WIN *win)
3239 struct input_data_s *in = win->data;
3241 if (!in->str || !isinteger(in->str)) {
3242 if (in->str)
3243 free(in->str);
3245 free(in);
3246 return;
3249 do_move_jump_finalize(atoi(in->str));
3250 free(in->str);
3251 free(in);
3254 struct history_menu_s {
3255 char *line;
3256 int hindex;
3257 int ravlevel;
3258 int move;
3259 int indent;
3262 void free_history_menu_data(struct history_menu_s **h)
3264 int i;
3266 if (!h)
3267 return;
3269 for (i = 0; h[i]; i++) {
3270 free(h[i]->line);
3271 free(h[i]);
3274 free(h);
3277 void get_history_data(HISTORY **hp, struct history_menu_s ***menu, int m,
3278 int turn)
3280 int i, n = 0;
3281 int t = pgn_history_total(hp);
3282 char buf[MAX_SAN_MOVE_LEN + 4];
3283 static int depth;
3284 struct history_menu_s **hmenu = *menu;
3286 if (hmenu)
3287 for (n = 0; hmenu[n]; n++);
3288 else
3289 depth = 0;
3291 for (i = 0; i < t; i++) {
3292 hmenu = Realloc(hmenu, (n + 2) * sizeof(struct history_menu_s *));
3293 hmenu[n] = Malloc(sizeof(struct history_menu_s));
3294 snprintf(buf, sizeof(buf), "%c%s%s", (turn == WHITE) ? 'W' : 'B',
3295 hp[i]->move, (hp[i]->comment || hp[i]->nag[0]) ? " !" : "");
3296 hmenu[n]->line = strdup(buf);
3297 hmenu[n]->hindex = i;
3298 hmenu[n]->indent = 0;
3299 hmenu[n]->ravlevel = depth;
3300 hmenu[n]->move = (n && depth > hmenu[n-1]->ravlevel) ? m++ : m;
3301 n++;
3302 hmenu[n] = NULL;
3304 #if 0
3305 if (hp[i]->rav) {
3306 depth++;
3307 get_history_data(hp[i]->rav, &hmenu, m, turn);
3308 for (n = 0; hmenu[n]; n++);
3309 depth--;
3311 if (depth)
3312 m--;
3314 #endif
3316 turn = (turn == WHITE) ? BLACK : WHITE;
3319 *menu = hmenu;
3322 void history_draw_update(struct menu_input_s *m)
3324 GAME g = m->data;
3325 struct userdata_s *d = g->data;
3327 g->hindex = m->selected + 1;
3328 update_cursor(g, m->selected);
3329 pgn_board_update(g, d->b, m->selected + 1);
3332 struct menu_item_s **get_history_items(WIN *win)
3334 struct menu_input_s *m = win->data;
3335 GAME g = m->data;
3336 struct userdata_s *d = g->data;
3337 struct history_menu_s **hm = d->data;
3338 struct menu_item_s **items = m->items;
3339 int i;
3341 if (!hm) {
3342 get_history_data(g->history, &hm, 0,
3343 TEST_FLAG(g->flags, GF_BLACK_OPENING));
3344 m->selected = g->hindex - 1;
3346 if (m->selected < 0)
3347 m->selected = 0;
3349 m->draw_exit_func = history_draw_update;
3352 d->data = hm;
3354 if (items) {
3355 for (i = 0; items[i]; i++)
3356 free(items[i]);
3358 free(items);
3359 items = NULL;
3362 for (i = 0; hm[i]; i++) {
3363 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
3364 items[i] = Malloc(sizeof(struct menu_item_s));
3365 items[i]->name = hm[i]->line;
3366 items[i]->value = NULL;
3367 items[i]->selected = 0;
3370 if (items)
3371 items[i] = NULL;
3373 m->nofree = 1;
3374 m->items = items;
3375 return items;
3378 void history_menu_quit(struct menu_input_s *m)
3380 pushkey = -1;
3383 void history_menu_exit(WIN *win)
3385 GAME g = win->data;
3386 struct userdata_s *d = g->data;
3387 struct history_menu_s **hm = d->data;
3388 int i;
3390 if (!hm)
3391 return;
3393 for (i = 0; hm[i]; i++) {
3394 free(hm[i]->line);
3395 free(hm[i]);
3398 free(hm);
3399 d->data = NULL;
3402 // FIXME RAV
3403 void history_menu_next(struct menu_input_s *m)
3405 GAME g = m->data;
3406 struct userdata_s *d = g->data;
3407 struct history_menu_s **hm = d->data;
3408 int n, t;
3410 for (t = 0; hm[t]; t++);
3412 if (m->selected + 1 == t)
3413 n = 0;
3414 else
3415 n = hm[m->selected + 1]->hindex;
3417 n++;
3418 g->hindex = n;
3421 // FIXME RAV
3422 void history_menu_prev(struct menu_input_s *m)
3424 GAME g = m->data;
3425 struct userdata_s *d = g->data;
3426 struct history_menu_s **hm = d->data;
3427 int n, t;
3429 for (t = 0; hm[t]; t++);
3431 if (m->selected - 1 < 0)
3432 n = t - 1;
3433 else
3434 n = hm[m->selected - 1]->hindex;
3436 n++;
3437 g->hindex = n;
3440 void history_menu_help(struct menu_input_s *m)
3442 message(_("History Menu Help"), ANY_KEY_STR, "%s",
3444 " UP/DOWN - previous/next menu item\n"
3445 " HOME/END - first/last menu item\n"
3446 " PGDN/PGUP - next/previous page\n"
3447 " a-zA-Z0-9 - jump to item\n"
3448 " CTRL-a - annotate the selected move\n"
3449 " ENTER - view annotation\n"
3450 " CTRL-d - toggle board details\n"
3451 " ESCAPE/M - return to move history"
3455 void do_annotate_move(HISTORY *hp)
3457 char buf[COLS - 4];
3458 struct input_data_s *in;
3460 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Editing Annotation for"), hp->move);
3461 in = Calloc(1, sizeof(struct input_data_s));
3462 in->data = hp;
3463 in->efunc = do_annotate_finalize;
3464 construct_input(buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3465 _("Type CTRL-t to edit NAG"), edit_nag, NULL, CTRL_KEY('T'), in, -1, -1);
3468 void history_menu_view_annotation(struct menu_input_s *m)
3470 GAME g = m->data;
3472 // FIXME RAV
3473 view_annotation(g->history[m->selected]);
3476 void history_menu_annotate_finalize(WIN *win)
3478 struct input_data_s *in = win->data;
3479 GAME g = in->moredata;
3480 struct userdata_s *d = g->data;
3481 struct history_menu_s **hm = d->data;
3483 really_do_annotate_finalize(in, d);
3484 free_history_menu_data(hm);
3485 hm = NULL;
3486 get_history_data(g->history, &hm, 0, TEST_FLAG(g->flags, GF_BLACK_OPENING));
3487 d->data = hm;
3488 pushkey = REFRESH_MENU;
3491 void history_menu_annotate(struct menu_input_s *m)
3493 GAME g = m->data;
3494 char buf[COLS - 4];
3495 struct input_data_s *in;
3496 HISTORY *hp = g->history[m->selected]; // FIXME RAV
3498 snprintf(buf, sizeof(buf), "%s \"%s\"", _("Editing Annotation for"), hp->move);
3499 in = Calloc(1, sizeof(struct input_data_s));
3500 in->data = hp;
3501 in->moredata = m->data;
3502 in->efunc = history_menu_annotate_finalize;
3503 construct_input(buf, hp->comment, MAX_PGN_LINE_LEN / INPUT_WIDTH, 0,
3504 _("Type CTRL-t to edit NAG"), edit_nag, NULL, CTRL_KEY('T'), in, -1, -1);
3507 void history_menu_details(struct menu_input_s *m)
3509 do_board_details();
3512 // FIXME RAV
3513 void history_menu_print(WIN *win)
3515 struct menu_input_s *m = win->data;
3516 GAME g = m->data;
3517 struct userdata_s *d = g->data;
3518 struct history_menu_s **hm = d->data;
3519 struct history_menu_s *h = hm[m->top];
3520 int i;
3521 char *p = m->item->name;
3522 int line = m->print_line - 2;
3524 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
3525 * attr_t data type.
3527 attr_t attrs;
3528 short pair;
3529 int total;
3531 for (total = 0; hm[total]; total++);
3532 wattr_get(win->w, &attrs, &pair, NULL);
3533 wattroff(win->w, COLOR_PAIR(pair));
3534 mvwaddch(win->w, m->print_line, 1,
3535 *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));
3536 p++;
3538 if (h->hindex == 0 && line == 0)
3539 waddch(win->w, ACS_ULCORNER | CP_HISTORY_MENU_LG);
3540 else if ((!hm[h->hindex + (win->rows - 5) + 1] && line == win->rows - 5) ||
3541 (m->top + line == total - 1))
3542 waddch(win->w, ACS_LLCORNER | CP_HISTORY_MENU_LG);
3543 else if (hm[m->top + 1]->ravlevel != h->ravlevel || !h->ravlevel)
3544 waddch(win->w, ACS_LTEE | CP_HISTORY_MENU_LG);
3545 else
3546 waddch(win->w, ACS_VLINE | CP_HISTORY_MENU_LG);
3548 wattron(win->w, COLOR_PAIR(pair) | attrs);
3550 for (i = 2; *p; p++, i++)
3551 waddch(win->w, (*p == '!') ? *p | A_BOLD : *p);
3553 while (i++ < win->cols - 2)
3554 waddch(win->w, ' ');
3557 void history_menu(GAME g)
3559 struct menu_key_s **keys = NULL;
3561 add_menu_key(&keys, KEY_ESCAPE, history_menu_quit);
3562 add_menu_key(&keys, 'M', history_menu_quit);
3563 add_menu_key(&keys, KEY_UP, history_menu_prev);
3564 add_menu_key(&keys, KEY_DOWN, history_menu_next);
3565 add_menu_key(&keys, KEY_F(1), history_menu_help);
3566 add_menu_key(&keys, CTRL_KEY('a'), history_menu_annotate);
3567 add_menu_key(&keys, CTRL_KEY('d'), history_menu_details);
3568 add_menu_key(&keys, '\n', history_menu_view_annotation);
3569 construct_menu(MEGA_BOARD ? LINES - HISTORY_HEIGHT_MB : LINES,
3570 TAG_WIDTH, 0, config.boardleft ? BOARD_WIDTH : 0,
3571 _("Move History Tree"), 1, get_history_items, keys, g,
3572 history_menu_print, history_menu_exit);
3575 void do_history_menu()
3577 history_menu(gp);
3580 void do_history_half_move_toggle()
3582 movestep = (movestep == 1) ? 2 : 1;
3583 update_history_window(gp);
3586 void do_history_rotate_board()
3588 rotate = (rotate) ? FALSE : TRUE;
3591 void do_history_jump_next()
3593 struct userdata_s *d = gp->data;
3595 pgn_history_next(gp, d->b, (keycount > 0) ?
3596 config.jumpcount * keycount * movestep :
3597 config.jumpcount * movestep);
3600 void do_history_jump_prev()
3602 struct userdata_s *d = gp->data;
3604 pgn_history_prev(gp, d->b, (keycount) ?
3605 config.jumpcount * keycount * movestep :
3606 config.jumpcount * movestep);
3609 void do_history_prev()
3611 struct userdata_s *d = gp->data;
3613 pgn_history_prev(gp, d->b,
3614 (keycount) ? keycount * movestep : movestep);
3617 void do_history_next()
3619 struct userdata_s *d = gp->data;
3621 pgn_history_next(gp, d->b, (keycount) ?
3622 keycount * movestep : movestep);
3625 void do_history_mode_finalize(struct userdata_s *d)
3627 pushkey = 0;
3628 d->mode = MODE_PLAY;
3631 void do_history_mode_confirm(WIN *win)
3633 struct userdata_s *d = gp->data;
3634 wchar_t str[] = { win->c, 0 };
3636 if (!wcscmp (str, resume_wchar)) {
3637 pgn_history_free(gp->hp, gp->hindex);
3638 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
3640 #if 0
3641 case 'C':
3642 case 'c':
3643 if (pgn_history_rav_new(gp, d->b,
3644 gp->hindex) != E_PGN_OK)
3645 return;
3647 break;
3648 #endif
3649 else
3650 return;
3652 if (!TEST_FLAG(d->flags, CF_HUMAN)) {
3653 char *fen = pgn_game_to_fen(gp, d->b);
3655 add_engine_command(gp, ENGINE_READY, "setboard %s\n", fen);
3656 free (fen);
3659 do_history_mode_finalize(d);
3662 void do_history_toggle()
3664 struct userdata_s *d = gp->data;
3666 // FIXME Resuming from previous history could append to a RAV.
3667 if (gp->hindex != pgn_history_total(gp->hp)) {
3668 if (!pushkey)
3669 construct_message(NULL, _ ("What would you like to do?"), 0, 1,
3670 NULL, NULL, NULL, do_history_mode_confirm, 0, 0,
3671 _("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."),
3672 resume_wchar);
3673 return;
3675 else {
3676 if (TEST_FLAG(gp->flags, GF_GAMEOVER))
3677 return;
3680 if (gp->side != gp->turn) {
3681 d->play_mode = PLAY_EH;
3683 else {
3684 d->play_mode = PLAY_HE;
3685 rotate = FALSE;
3688 do_history_mode_finalize(d);
3691 void do_history_annotate()
3693 int n = gp->hindex;
3695 if (n && gp->hp[n - 1]->move)
3696 n--;
3697 else
3698 return;
3700 do_annotate_move(gp->hp[n]);
3703 void do_history_help()
3705 wchar_t *buf = build_help(history_keys);
3707 construct_message(_("History Mode Keys (* = can take a repeat count)"),
3708 ANY_KEY_SCROLL_STR, 0, 0, NULL, NULL,
3709 buf, do_more_help, 0, 1, "%ls", buf);
3712 void do_history_find(int which)
3714 struct input_data_s *in;
3715 int *p;
3717 if (pgn_history_total(gp->hp) < 2)
3718 return;
3720 in = Calloc(1, sizeof(struct input_data_s));
3721 p = Malloc(sizeof(int));
3722 *p = which;
3723 in->data = p;
3724 in->efunc = do_find_move_exp;
3726 if (!*moveexp || which == 0) {
3727 construct_input(_("Find Move Text Expression"), NULL, 1, 0, NULL, NULL, NULL,
3728 0, in, INPUT_HIST_MOVE_EXP, -1);
3729 return;
3732 do_find_move_exp_finalize(0, which);
3735 void do_history_find_new()
3737 do_history_find(0);
3740 void do_history_find_prev()
3742 do_history_find(-1);
3745 void do_history_find_next()
3747 do_history_find(1);
3750 void do_history_rav(int which)
3752 struct userdata_s *d = gp->data;
3754 rav_next_prev(gp, d->b, which);
3757 void do_history_rav_next()
3759 do_history_rav(1);
3762 void do_history_rav_prev()
3764 do_history_rav(0);
3767 void do_history_jump()
3769 struct input_data_s *in;
3771 if (pgn_history_total(gp->hp) < 2)
3772 return;
3774 if (!keycount) {
3775 in = Calloc(1, sizeof(struct input_data_s));
3776 in->efunc = do_move_jump;
3778 construct_input(_("Jump to Move Number"), NULL, 1, 1, NULL,
3779 NULL, NULL, 0, in, -1, 0);
3780 return;
3783 do_move_jump_finalize(keycount);
3786 static void free_userdata_once(GAME g)
3788 struct userdata_s *d = g->data;
3790 if (!d)
3791 return;
3793 if (d->engine) {
3794 stop_engine(g);
3796 if (d->engine->enginebuf) {
3797 int n;
3799 for (n = 0; d->engine->enginebuf[n]; n++)
3800 free(d->engine->enginebuf[n]);
3802 free(d->engine->enginebuf);
3805 if (d->engine->queue) {
3806 struct queue_s **q;
3808 for (q = d->engine->queue; *q; q++)
3809 free(*q);
3811 free(d->engine->queue);
3814 free(d->engine);
3817 #ifdef WITH_LIBPERL
3818 if (d->perlfen)
3819 free(d->perlfen);
3821 if (d->oldfen)
3822 free(d->oldfen);
3823 #endif
3825 free(d);
3826 g->data = NULL;
3829 static void free_userdata()
3831 int i;
3833 for (i = 0; i < gtotal; i++) {
3834 free_userdata_once(game[i]);
3835 game[i]->data = NULL;
3839 void update_loading_window(int n)
3841 char buf[16];
3843 if (!loadingw) {
3844 loadingw = newwin(3, COLS / 2, CALCPOSY(3), CALCPOSX(COLS / 2));
3845 loadingp = new_panel(loadingw);
3846 wbkgd(loadingw, CP_MESSAGE_WINDOW);
3849 wmove(loadingw, 0, 0);
3850 wclrtobot(loadingw);
3851 wattron(loadingw, CP_MESSAGE_BORDER);
3852 box(loadingw, ACS_VLINE, ACS_HLINE);
3853 wattroff(loadingw, CP_MESSAGE_BORDER);
3854 mvwprintw(loadingw, 1, CENTER_INT((COLS / 2), 11 +
3855 strlen(itoa(gtotal, buf))),
3856 _("Loading... %i%% (%i games)"), n, gtotal);
3857 update_panels();
3858 doupdate();
3861 static void init_userdata_once(GAME g, int n)
3863 struct userdata_s *d = NULL;
3865 d = Calloc(1, sizeof(struct userdata_s));
3866 d->n = n;
3867 d->c_row = 2, d->c_col = 5;
3868 SET_FLAG(d->flags, CF_NEW);
3869 g->data = d;
3871 if (pgn_board_init_fen(g, d->b, NULL) != E_PGN_OK)
3872 pgn_board_init(d->b);
3875 void init_userdata()
3877 int i;
3879 for (i = 0; i < gtotal; i++)
3880 init_userdata_once(game[i], i);
3883 void fix_marks(int *start, int *end)
3885 int i;
3887 *start = (*start < 0) ? 0 : *start;
3888 *end = (*end < 0) ? 0 : *end;
3890 if (*start > *end) {
3891 i = *start;
3892 *start = *end;
3893 *end = i + 1;
3896 *end = (*end > gtotal) ? gtotal : *end;
3899 void do_new_game_finalize(GAME g)
3901 struct userdata_s *d = g->data;
3903 d->mode = MODE_PLAY;
3904 update_status_notify(g, NULL);
3907 void do_new_game_from_scratch(WIN *win)
3909 wchar_t str[] = { win->c, 0 };
3911 if (wcscmp (str, yes_wchar))
3912 return;
3914 stop_clock();
3915 free_userdata();
3916 pgn_parse(NULL);
3917 gp = game[gindex];
3918 add_custom_tags(&gp->tag);
3919 init_userdata();
3920 loadfile[0] = 0;
3921 do_new_game_finalize(gp);
3922 rotate = FALSE;
3925 void do_new_game()
3927 pgn_new_game();
3928 gp = game[gindex];
3929 add_custom_tags(&gp->tag);
3930 init_userdata_once(gp, gindex);
3931 do_new_game_finalize(gp);
3932 rotate = FALSE;
3935 void do_game_delete_finalize(int n)
3937 struct userdata_s *d;
3939 delete_game((!n) ? gindex : -1);
3940 d = gp->data;
3941 if (d->mode != MODE_EDIT)
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,
4232 _("%s\nUsing %s with %i colors 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 = strdup (game[g]->history[i]->move);
4436 if (pgn_parse_move(gp, d->b, &move, &frfr) != E_PGN_OK) {
4437 free (move);
4438 SET_FLAG(gp->flags, GF_PERROR);
4439 return;
4442 pgn_history_add(gp, d->b, move);
4443 free (move);
4444 free(frfr);
4445 pgn_switch_turn(gp);
4448 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4451 void do_global_new_all()
4453 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
4454 do_new_game_from_scratch, 0, 0, "%s", _("Really start a new game from scratch?"));
4457 void do_quit(WIN *win)
4459 wchar_t str[] = { win->c, 0 };
4461 if (wcscmp (str, yes_wchar))
4462 return;
4464 quit = 1;
4467 void do_global_quit()
4469 if (config.exitdialogbox)
4470 construct_message(NULL, _("[ Yes or No ]"), 1, 1, NULL, NULL, NULL,
4471 do_quit, 0, 0, _("Want to Quit?"));
4472 else
4473 quit = 1;
4476 void do_global_toggle_engine_window()
4478 if (!enginew) {
4479 enginew = newwin(LINES, COLS, 0, 0);
4480 enginep = new_panel(enginew);
4481 window_draw_title(enginew, _("Engine IO Window"), COLS, CP_MESSAGE_TITLE,
4482 CP_MESSAGE_BORDER);
4483 hide_panel(enginep);
4486 if (panel_hidden(enginep)) {
4487 update_engine_window(gp);
4488 top_panel(enginep);
4490 else {
4491 hide_panel(enginep);
4495 void do_global_toggle_board_details()
4497 do_board_details();
4500 void do_global_toggle_strict_castling()
4502 do_toggle_strict_castling();
4505 // Global and other keys.
4506 static int globalkeys()
4508 struct userdata_s *d = gp->data;
4509 int i;
4512 * These cannot be modified and other game mode keys cannot conflict with
4513 * these.
4515 switch (input_c) {
4516 case KEY_ESCAPE:
4517 d->sp.icon = d->sp.srow = d->sp.scol = 0;
4518 markend = markstart = 0;
4520 if (keycount) {
4521 keycount = 0;
4522 update_status_notify(gp, NULL);
4525 if (config.validmoves)
4526 pgn_reset_valid_moves(d->b);
4528 return 1;
4529 case '0' ... '9':
4530 i = input_c - '0';
4532 if (keycount)
4533 keycount = keycount * 10 + i;
4534 else
4535 keycount = i;
4537 update_status_notify(gp, _("Repeat %i"), keycount);
4538 return -1;
4539 case KEY_UP:
4540 if (d->mode == MODE_HISTORY)
4541 return 0;
4543 if (keycount)
4544 d->c_row += keycount;
4545 else
4546 d->c_row++;
4548 if (d->c_row > 8)
4549 d->c_row = 1;
4551 return 1;
4552 case KEY_DOWN:
4553 if (d->mode == MODE_HISTORY)
4554 return 0;
4556 if (keycount) {
4557 d->c_row -= keycount;
4558 update_status_notify(gp, NULL);
4560 else
4561 d->c_row--;
4563 if (d->c_row < 1)
4564 d->c_row = 8;
4566 return 1;
4567 case KEY_LEFT:
4568 if (d->mode == MODE_HISTORY)
4569 return 0;
4571 if (keycount)
4572 d->c_col -= keycount;
4573 else
4574 d->c_col--;
4576 if (d->c_col < 1)
4577 d->c_col = 8;
4579 return 1;
4580 case KEY_RIGHT:
4581 if (d->mode == MODE_HISTORY)
4582 return 0;
4584 if (keycount)
4585 d->c_col += keycount;
4586 else
4587 d->c_col++;
4589 if (d->c_col > 8)
4590 d->c_col = 1;
4592 return 1;
4593 case KEY_RESIZE:
4594 return 1;
4595 case 0:
4596 default:
4597 for (i = 0; global_keys[i]; i++) {
4598 if (input_c == global_keys[i]->c && global_keys[i]->f) {
4599 (*global_keys[i]->f)();
4600 return 1;
4603 break;
4606 return 0;
4609 #ifdef WITH_LIBPERL
4610 static void perl_error(const char *fmt, ...)
4612 va_list ap;
4613 char *buf;
4615 va_start(ap, fmt);
4616 vasprintf(&buf, fmt, ap);
4617 va_end(ap);
4619 message(ERROR_STR, ANY_KEY_STR, "%s", buf);
4620 free(buf);
4623 static void do_perl_finalize(WIN *win)
4625 struct input_data_s *in = win->data;
4626 GAME g = in->data;
4627 struct userdata_s *d = g->data;
4628 char *filename;
4629 char *result = NULL;
4630 char *arg = NULL;
4631 int n;
4633 asprintf(&filename, "%s/perl.pl", config.datadir);
4635 if (!in->str)
4636 goto done;
4638 if (perl_init_file(filename, perl_error))
4639 goto done;
4641 arg = pgn_game_to_fen(g, d->b);
4643 if (perl_call_sub(trim(in->str), arg, &result))
4644 goto done;
4646 d->perlfen = pgn_game_to_fen(g, d->b);
4647 d->perlflags = g->flags;
4649 if (pgn_board_init_fen(g, d->b, result) != E_PGN_OK) {
4650 message(ERROR_STR, ANY_KEY_STR, "%s", _("FEN parse error."));
4651 pgn_board_init_fen(g, d->b, d->perlfen);
4652 g->flags = d->perlflags;
4653 free(d->perlfen);
4654 d->perlfen = NULL;
4655 goto done;
4658 SET_FLAG(d->flags, CF_PERL);
4659 n = pgn_tag_find(g->tag, "FEN");
4661 if (n != E_PGN_ERR)
4662 d->oldfen = strdup(g->tag[n]->value);
4664 pgn_tag_add(&g->tag, "FEN", result);
4665 update_status_notify(g, "%s", ANY_KEY_STR);
4666 update_all(g);
4668 done:
4669 free(result);
4670 free(arg);
4671 free(in->str);
4672 free(in);
4673 free(filename);
4676 void do_global_perl()
4678 struct input_data_s *in;
4680 in = Calloc(1, sizeof(struct input_data_s));
4681 in->data = gp;
4682 in->efunc = do_perl_finalize;
4683 construct_input(_("PERL Subroutine Filter"), NULL, 1, 0, NULL, NULL, NULL, 0, in, INPUT_HIST_PERL, -1);
4685 #endif
4688 * A macro may contain a key that belongs to another macro so macro_match will
4689 * need to be updated to the new index of the matching macro.
4691 static void find_macro(struct userdata_s *d)
4693 int i;
4696 * Macros can't contain macros when in a window.
4698 if (wins)
4699 return;
4701 again:
4702 for (i = 0; macros[i]; i++) {
4703 if ((macros[i]->mode == -1 || macros[i]->mode == d->mode) &&
4704 input_c == macros[i]->c) {
4705 input_c = macros[i]->keys[macros[i]->n++];
4707 if (!macro_depth_n && macro_match > -1) {
4708 macro_depth = realloc(macro_depth, (macro_depth_n + 1) * sizeof(int));
4709 macro_depth[macro_depth_n++] = macro_match;
4712 macro_depth = realloc(macro_depth, (macro_depth_n + 1) * sizeof(int));
4713 macro_depth[macro_depth_n++] = i;
4714 macro_match = i;
4715 goto again;
4721 * Resets the position in each macro to the first key.
4723 static void reset_macros()
4725 int i;
4726 struct userdata_s *d = gp->data;
4728 again:
4729 if (macro_depth_n > 0) {
4730 macro_depth_n--;
4731 macro_match = macro_depth[macro_depth_n];
4733 if (macros[macro_match]->n >= macros[macro_match]->total)
4734 goto again;
4736 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
4737 find_macro(d);
4738 return;
4741 for (i = 0; macros[i]; i++)
4742 macros[i]->n = 0;
4744 free(macro_depth);
4745 macro_depth = NULL;
4746 macro_depth_n = 0;
4747 macro_match = -1;
4750 void game_loop()
4752 struct userdata_s *d;
4754 macro_match = -1;
4755 gindex = gtotal - 1;
4756 gp = game[gindex];
4757 d = gp->data;
4759 if (pgn_history_total(gp->hp))
4760 d->mode = MODE_HISTORY;
4761 else {
4762 d->mode = MODE_PLAY;
4763 d->play_mode = PLAY_HE;
4766 if (d->mode == MODE_HISTORY)
4767 pgn_board_update(gp, d->b, pgn_history_total(gp->hp));
4769 update_status_notify(gp, "%s", _("Type F1 for help"));
4770 movestep = 2;
4771 flushinp();
4772 update_all(gp);
4773 wtimeout(boardw, WINDOW_TIMEOUT);
4775 while (!quit) {
4776 int n = 0, i;
4777 char fdbuf[8192] = {0};
4778 int len;
4779 struct timeval tv = {0, 0};
4780 fd_set rfds, wfds;
4781 WIN *win = NULL;
4782 WINDOW *wp = NULL;
4784 FD_ZERO(&rfds);
4785 FD_ZERO(&wfds);
4787 for (i = 0; i < gtotal; i++) {
4788 d = game[i]->data;
4790 if (d->engine && d->engine->pid != -1) {
4791 if (d->engine->fd[ENGINE_IN_FD] > 2) {
4792 if (d->engine->fd[ENGINE_IN_FD] > n)
4793 n = d->engine->fd[ENGINE_IN_FD];
4795 FD_SET(d->engine->fd[ENGINE_IN_FD], &rfds);
4798 if (d->engine->fd[ENGINE_OUT_FD] > 2) {
4799 if (d->engine->fd[ENGINE_OUT_FD] > n)
4800 n = d->engine->fd[ENGINE_OUT_FD];
4802 FD_SET(d->engine->fd[ENGINE_OUT_FD], &wfds);
4807 if (n) {
4808 if ((n = select(n + 1, &rfds, &wfds, NULL, &tv)) > 0) {
4809 for (i = 0; i < gtotal; i++) {
4810 d = game[i]->data;
4812 if (d->engine && d->engine->pid != -1) {
4813 if (FD_ISSET(d->engine->fd[ENGINE_IN_FD], &rfds)) {
4814 len = read(d->engine->fd[ENGINE_IN_FD], fdbuf,
4815 sizeof(fdbuf));
4817 if (len > 0) {
4818 if (d->engine->iobuf)
4819 d->engine->iobuf = Realloc(d->engine->iobuf, d->engine->len + len + 1);
4820 else
4821 d->engine->iobuf = Calloc(1, len + 1);
4823 memcpy(&(d->engine->iobuf[d->engine->len]), &fdbuf, len);
4824 d->engine->len += len;
4825 d->engine->iobuf[d->engine->len] = 0;
4828 * The fdbuf is full or no newline
4829 * was found. So we'll append the next
4830 * read() to this games buffer.
4832 if (d->engine->iobuf[d->engine->len - 1] != '\n')
4833 continue;
4835 parse_engine_output(game[i], d->engine->iobuf);
4836 free(d->engine->iobuf);
4837 d->engine->iobuf = NULL;
4838 d->engine->len = 0;
4840 else if (len == -1) {
4841 if (errno != EAGAIN) {
4842 cmessage(ERROR_STR, ANY_KEY_STR, "Engine read(): %s",
4843 strerror(errno));
4844 waitpid(d->engine->pid, &n, 0);
4845 free(d->engine);
4846 d->engine = NULL;
4847 break;
4852 if (FD_ISSET(d->engine->fd[ENGINE_OUT_FD], &wfds)) {
4853 if (d->engine->queue)
4854 send_engine_command(game[i]);
4859 else {
4860 if (n == -1)
4861 cmessage(ERROR_STR, ANY_KEY_STR, "select(): %s", strerror(errno));
4862 /* timeout */
4866 gp = game[gindex];
4867 d = gp->data;
4870 * This is needed to detect terminal resizing.
4872 doupdate();
4873 if (LINES != LINES_OLD || COLS != COLS_OLD) {
4874 COLS_OLD = COLS;
4875 LINES_OLD = LINES;
4876 do_window_resize();
4880 * Finds the top level window in the window stack so we know what
4881 * window the wget_wch()'ed key belongs to.
4883 if (wins) {
4884 for (i = 0; wins[i]; i++);
4885 win = wins[i-1];
4886 wp = win->w;
4887 wtimeout(wp, WINDOW_TIMEOUT);
4889 else
4890 wp = boardw;
4892 if (!i && pushkey)
4893 input_c = pushkey;
4894 else {
4895 if (!pushkey) {
4896 if (macros && macro_match >= 0) {
4897 if (macros[macro_match]->n >= macros[macro_match]->total)
4898 reset_macros();
4899 else {
4900 input_c = macros[macro_match]->keys[macros[macro_match]->n++];
4901 find_macro(d);
4904 else {
4905 if (wget_wch(wp, &input_c) == ERR || input_c == KEY_RESIZE)
4906 continue;
4909 else
4910 input_c = pushkey;
4912 if (win) {
4913 win->c = input_c;
4916 * Run the function associated with the window. When the
4917 * function returns 0 win->efunc is ran (if not NULL) with
4918 * win as the one and only parameter. Then the window is
4919 * destroyed.
4921 * The exit function may create another window which will
4922 * mess up the window stack when window_destroy() is called.
4923 * So don't destory the window until the top window is
4924 * destroyable. See window_destroy().
4926 if ((*win->func)(win) == 0) {
4927 if (win->efunc)
4928 (*win->efunc)(win);
4930 win->keep = 1;
4931 window_destroy(win);
4932 update_all(gp);
4935 continue;
4939 if (!keycount && status.notify)
4940 update_status_notify(gp, NULL);
4942 #ifdef WITH_LIBPERL
4943 if (TEST_FLAG(d->flags, CF_PERL)) {
4944 CLEAR_FLAG(d->flags, CF_PERL);
4945 pgn_board_init_fen(gp, d->b, d->perlfen);
4946 gp->flags = d->perlflags;
4947 free(d->perlfen);
4948 pgn_tag_add(&gp->tag, "FEN", d->oldfen);
4949 free(d->oldfen);
4950 d->perlfen = d->oldfen = NULL;
4951 update_all(gp);
4952 continue;
4954 #endif
4956 if (macros && macro_match < 0)
4957 find_macro(d);
4959 if ((n = globalkeys()) == 1) {
4960 if (macro_match == -1)
4961 keycount = 0;
4963 goto refresh;
4965 else if (n == -1)
4966 goto refresh;
4968 switch (d->mode) {
4969 case MODE_EDIT:
4970 for (i = 0; edit_keys[i]; i++) {
4971 if (input_c == edit_keys[i]->c) {
4972 (*edit_keys[i]->f)();
4973 break;
4976 break;
4977 case MODE_PLAY:
4978 for (i = 0; play_keys[i]; i++) {
4979 if (input_c == play_keys[i]->c) {
4980 (*play_keys[i]->f)();
4981 goto done;
4985 do_play_config_command();
4986 break;
4987 case MODE_HISTORY:
4988 for (i = 0; history_keys[i]; i++) {
4989 if (input_c == history_keys[i]->c) {
4990 (*history_keys[i]->f)();
4991 break;
4994 break;
4995 default:
4996 break;
4999 done:
5000 if (keycount)
5001 update_status_notify(gp, NULL);
5003 keycount = 0;
5005 refresh:
5006 update_all(gp);
5010 void usage(const char *pn, int ret)
5012 fprintf((ret) ? stderr : stdout, "%s%s",
5013 #ifdef DEBUG
5015 "Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
5016 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"),
5017 #else
5019 "Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"),
5020 #endif
5022 " -p Load PGN file.\n"
5023 " -V Validate a game file.\n"
5024 " -S Validate and output a PGN formatted game.\n"
5025 " -R Like -S but write a reduced PGN formatted game.\n"
5026 " -t Also write custom PGN tags from config file.\n"
5027 " -E Stop processing on file parsing error (overrides config).\n"
5028 " -C Enable strict castling (overrides config).\n"
5029 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, overrides config).\n"
5030 " -v Version information.\n"
5031 " -h This help text.\n"));
5033 exit(ret);
5036 void cleanup_all()
5038 int i;
5040 stop_clock();
5041 free_userdata();
5042 pgn_free_all();
5043 free(config.engine_cmd);
5044 free(config.pattern);
5045 free(config.ccfile);
5046 free(config.nagfile);
5047 free(config.configfile);
5049 if (config.keys) {
5050 for (i = 0; config.keys[i]; i++) {
5051 free(config.keys[i]->str);
5052 free(config.keys[i]);
5055 free(config.keys);
5058 if (config.einit) {
5059 for (i = 0; config.einit[i]; i++)
5060 free(config.einit[i]);
5062 free(config.einit);
5065 if (config.tag)
5066 pgn_tag_free(config.tag);
5068 free(config.datadir);
5070 if (curses_initialized) {
5071 del_panel(boardp);
5072 del_panel(historyp);
5073 del_panel(statusp);
5074 del_panel(tagp);
5075 delwin(boardw);
5076 delwin(historyw);
5077 delwin(statusw);
5078 delwin(tagw);
5080 if (enginew) {
5081 del_panel(enginep);
5082 delwin(enginew);
5085 endwin();
5088 #ifdef WITH_LIBPERL
5089 perl_cleanup();
5090 #endif
5093 static void signal_save_pgn(int sig)
5095 char *buf;
5096 time_t now;
5097 char *p = config.savedirectory ? config.savedirectory : config.datadir;
5099 time(&now);
5100 asprintf(&buf, "%s/signal-%i-%li.pgn", p, sig, now);
5102 if (do_game_write(buf, "w", 0, gtotal)) {
5103 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", p, strerror(errno));
5104 update_status_notify(gp, "%s", _("Save game failed."));
5107 free(buf);
5108 quit = 1;
5111 void catch_signal(int which)
5113 switch (which) {
5114 case SIGALRM:
5115 update_clocks();
5116 break;
5117 case SIGPIPE:
5118 if (which == SIGPIPE && quit)
5119 break;
5121 if (which == SIGPIPE)
5122 cmessage(NULL, ANY_KEY_STR, "%s", _("Broken pipe. Quitting."));
5124 cleanup_all();
5125 exit(EXIT_FAILURE);
5126 break;
5127 case SIGSTOP:
5128 savetty();
5129 break;
5130 case SIGCONT:
5131 resetty();
5132 do_window_resize ();
5133 keypad(boardw, TRUE);
5134 break;
5135 case SIGINT:
5136 quit = 1;
5137 break;
5138 case SIGTERM:
5139 signal_save_pgn(which);
5140 break;
5141 default:
5142 break;
5146 void loading_progress(long total, long offset)
5148 int n = (100 * (offset / 100) / (total / 100));
5150 if (curses_initialized)
5151 update_loading_window(n);
5152 else {
5153 fprintf(stderr, _("Loading... %i%% (%i games)%c"), n, gtotal, '\r');
5154 fflush(stderr);
5158 static void set_defaults()
5160 set_config_defaults();
5161 set_default_keys();
5162 filetype = FILE_NONE;
5163 pgn_config_set(PGN_PROGRESS, 1024);
5164 pgn_config_set(PGN_PROGRESS_FUNC, loading_progress);
5167 int main(int argc, char *argv[])
5169 int opt;
5170 struct stat st;
5171 char buf[FILENAME_MAX];
5172 char datadir[FILENAME_MAX];
5173 int ret = EXIT_SUCCESS;
5174 int validate_only = 0, validate_and_write = 0;
5175 int write_custom_tags = 0;
5176 int i = 0;
5177 PGN_FILE *pgn;
5178 int utf8_pieces = -1;
5180 setlocale (LC_ALL, "");
5181 bindtextdomain ("cboard", LOCALE_DIR);
5182 textdomain ("cboard");
5184 /* Solaris 5.9 */
5185 #ifndef HAVE_PROGNAME
5186 __progname = argv[0];
5187 #endif
5189 if ((config.pwd = getpwuid(getuid())) == NULL)
5190 err(EXIT_FAILURE, "getpwuid()");
5192 snprintf(datadir, sizeof(datadir), "%s/.cboard", config.pwd->pw_dir);
5193 config.datadir = strdup(datadir);
5194 snprintf(buf, sizeof(buf), "%s/cc.data", datadir);
5195 config.ccfile = strdup(buf);
5196 snprintf(buf, sizeof(buf), "%s/nag.data", datadir);
5197 config.nagfile = strdup(buf);
5198 snprintf(buf, sizeof(buf), "%s/config", datadir);
5199 config.configfile = strdup(buf);
5201 if (stat(datadir, &st) == -1) {
5202 if (errno == ENOENT) {
5203 if (mkdir(datadir, 0755) == -1)
5204 err(EXIT_FAILURE, "%s", datadir);
5206 else
5207 err(EXIT_FAILURE, "%s", datadir);
5209 stat(datadir, &st);
5212 if (!S_ISDIR(st.st_mode))
5213 errx(EXIT_FAILURE, "%s: %s", datadir, _("Not a directory."));
5215 set_defaults();
5217 #ifdef DEBUG
5218 while ((opt = getopt(argc, argv, "DCEVtSRhp:vu::")) != -1) {
5219 #else
5220 while ((opt = getopt(argc, argv, "ECVtSRhp:vu::")) != -1) {
5221 #endif
5222 switch (opt) {
5223 #ifdef DEBUG
5224 case 'D':
5225 unlink("libchess.debug");
5226 pgn_config_set(PGN_DEBUG, 1);
5227 break;
5228 #endif
5229 case 'C':
5230 pgn_config_set(PGN_STRICT_CASTLING, 1);
5231 break;
5232 case 't':
5233 write_custom_tags = 1;
5234 break;
5235 case 'E':
5236 i = 1;
5237 break;
5238 case 'R':
5239 pgn_config_set(PGN_REDUCED, 1);
5240 case 'S':
5241 validate_and_write = 1;
5242 case 'V':
5243 validate_only = 1;
5244 break;
5245 case 'v':
5246 printf("%s (%s)\n%s\n", PACKAGE_STRING, curses_version(),
5247 COPYRIGHT);
5248 exit(EXIT_SUCCESS);
5249 case 'p':
5250 filetype = FILE_PGN;
5251 strncpy(loadfile, optarg, sizeof(loadfile));
5252 loadfile[sizeof(loadfile)-1] = 0;
5253 break;
5254 case 'u':
5255 utf8_pieces = optarg ? atoi (optarg): 1;
5256 break;
5257 case 'h':
5258 default:
5259 usage(argv[0], EXIT_SUCCESS);
5263 if ((validate_only || validate_and_write) && !*loadfile)
5264 usage(argv[0], EXIT_FAILURE);
5266 if (access(config.configfile, R_OK) == 0)
5267 parse_rcfile(config.configfile);
5269 if (i)
5270 pgn_config_set(PGN_STOP_ON_ERROR, 1);
5272 signal(SIGPIPE, catch_signal);
5273 signal(SIGCONT, catch_signal);
5274 signal(SIGSTOP, catch_signal);
5275 signal(SIGINT, catch_signal);
5276 signal(SIGALRM, catch_signal);
5277 signal(SIGTERM, catch_signal);
5279 srandom(getpid());
5281 switch (filetype) {
5282 case FILE_PGN:
5283 if (pgn_open(loadfile, "r", &pgn) != E_PGN_OK)
5284 err(EXIT_FAILURE, "%s", loadfile);
5286 ret = pgn_parse(pgn);
5287 pgn_close(pgn);
5288 break;
5289 case FILE_FEN:
5290 //ret = parse_fen_file(loadfile);
5291 break;
5292 case FILE_EPD: // Not implemented.
5293 case FILE_NONE:
5294 default:
5295 // No file specified. Empty game.
5296 ret = pgn_parse(NULL);
5297 gp = game[gindex];
5298 add_custom_tags(&gp->tag);
5299 break;
5302 if (validate_only || validate_and_write) {
5303 if (validate_and_write) {
5304 if (pgn_open("-", "r", &pgn) != E_PGN_OK)
5305 err(EXIT_FAILURE, "pgn_open()");
5307 for (i = 0; i < gtotal; i++) {
5308 if (write_custom_tags)
5309 add_custom_tags(&game[i]->tag);
5311 pgn_write(pgn, game[i]);
5314 pgn_close(pgn);
5316 fm_loaded_file = TRUE;
5319 cleanup_all();
5320 exit(ret);
5322 else if (ret == E_PGN_ERR)
5323 exit(ret);
5325 if (utf8_pieces != -1)
5326 config.utf8_pieces = utf8_pieces;
5328 init_wchar_pieces ();
5329 yes_wchar = str_to_wchar (_("y"));
5330 all_wchar = str_to_wchar (_("a"));
5331 overwrite_wchar = str_to_wchar (_("o"));
5332 resume_wchar = str_to_wchar (_("r"));
5333 current_wchar = str_to_wchar (_("c"));
5334 append_wchar = str_to_wchar (_("a"));
5335 translatable_tag_names[0] = _("Event");
5336 translatable_tag_names[1] = _("Site");
5337 translatable_tag_names[2] = _("Date");
5338 translatable_tag_names[3] = _("Round");
5339 translatable_tag_names[4] = _("White");
5340 translatable_tag_names[5] = _("Black");
5341 translatable_tag_names[6] = _("Result");
5342 init_userdata();
5345 * This fixes window resizing in an xterm.
5347 if (getenv("DISPLAY") != NULL) {
5348 putenv("LINES=");
5349 putenv("COLUMNS=");
5352 if (initscr() == NULL)
5353 errx(EXIT_FAILURE, "%s", _("Could not initialize curses."));
5354 else
5355 curses_initialized = 1;
5357 if (LINES < 23 || COLS < 74) {
5358 endwin();
5359 errx(EXIT_FAILURE, _("Need at least an 74x23 terminal."));
5362 COLS_OLD = COLS;
5363 LINES_OLD = LINES;
5365 if (has_colors() == TRUE && start_color() == OK)
5366 init_color_pairs();
5368 boardw = newwin(BOARD_HEIGHT, BOARD_WIDTH, 0, COLS - BOARD_WIDTH);
5369 boardp = new_panel(boardw);
5370 historyw = newwin(HISTORY_HEIGHT, HISTORY_WIDTH, LINES - HISTORY_HEIGHT,
5371 COLS - HISTORY_WIDTH);
5372 historyp = new_panel(historyw);
5373 statusw = newwin(STATUS_HEIGHT, STATUS_WIDTH, 0, 0);
5374 statusp = new_panel(statusw);
5375 tagw = newwin(TAG_HEIGHT, TAG_WIDTH, STATUS_HEIGHT + 1, 0);
5376 tagp = new_panel(tagw);
5377 keypad(boardw, TRUE);
5378 // leaveok(boardw, TRUE);
5379 leaveok(tagw, TRUE);
5380 leaveok(statusw, TRUE);
5381 leaveok(historyw, TRUE);
5382 curs_set(0);
5383 cbreak();
5384 noecho();
5385 draw_window_decor();
5386 game_loop();
5387 cleanup_all();
5388 free (w_pawn_wchar);
5389 free (w_rook_wchar);
5390 free (w_bishop_wchar);
5391 free (w_knight_wchar);
5392 free (w_queen_wchar);
5393 free (w_king_wchar);
5394 free (b_pawn_wchar);
5395 free (b_rook_wchar);
5396 free (b_bishop_wchar);
5397 free (b_knight_wchar);
5398 free (b_queen_wchar);
5399 free (b_king_wchar);
5400 free (empty_wchar);
5401 free (enpassant_wchar);
5402 free (yes_wchar);
5403 free (all_wchar);
5404 free (overwrite_wchar);
5405 free (resume_wchar);
5406 free (current_wchar);
5407 free (append_wchar);
5408 free (status.notify);
5409 exit(EXIT_SUCCESS);