1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
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
26 #include <sys/types.h>
27 #include <sys/socket.h>
42 #ifdef HAVE_SYS_WAIT_H
51 #include "perl-plugin.h"
66 #include "filebrowser.h"
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)
99 static WINDOW
*boardw
;
100 static PANEL
*boardp
;
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;
118 static char loadfile
[FILENAME_MAX
];
120 static wint_t input_c
;
122 // Loaded filename from the command line or from the file input dialog.
125 FILE_NONE
, FILE_PGN
, FILE_FEN
, FILE_EPD
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
;
146 wchar_t *notify
; // The status window notification line buffer.
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.
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
[] = {
203 static const bool cb
[8][8] = {
214 static void free_userdata_once(GAME g
);
216 // Posición por rotación de tablero.
217 // Rotation board position.
218 static void rotate_position(int p
)
220 struct userdata_s
*d
= gp
->data
;
222 char fr2
= 8, fc2
= 8;
224 for (fr
= 1; fr
< 9; fr
++) {
227 for (fc
= 1; fc
< 9; fc
++){
230 if (p
== CURSOR_POSITION
&&
231 d
->c_row
== fr
&& d
->c_col
== fc
) {
236 else if (p
== SP_POSITION
&&
237 d
->sp
.row
== fr
&& d
->sp
.col
== fc
) {
242 else if (p
== SPS_POSITION
&&
243 d
->sp
.srow
== fr
&& d
->sp
.scol
== fc
) {
254 void update_cursor(GAME g
, int idx
)
258 int t
= pgn_history_total(g
->hp
);
259 struct userdata_s
*d
= g
->data
;
262 * If not deincremented then r and c would be the next move.
266 if (idx
> t
|| idx
< 0 || !t
|| !g
->hp
[idx
]->move
) {
267 d
->c_row
= 2, d
->c_col
= 5;
272 p
= g
->hp
[idx
]->move
;
277 d
->c_col
= rotate
? 3 : 7;
279 d
->c_col
= rotate
? 6 : 3;
282 d
->c_row
= (g
->turn
== WHITE
) ? 1 : 8;
284 d
->c_row
= (g
->turn
== WHITE
) ? 8 : 1;
294 d
->c_row
= RANKTOINT(*p
--);
295 d
->c_col
= FILETOINT(*p
);
298 if (d
->mode
== MODE_HISTORY
&& rotate
)
299 rotate_position(CURSOR_POSITION
);
302 static int init_nag()
308 if ((fp
= fopen(config
.nagfile
, "r")) == NULL
) {
309 cmessage(ERROR_STR
, ANY_KEY_STR
, "%s: %s", config
.nagfile
, strerror(errno
));
313 nags
= Realloc(nags
, (i
+2) * sizeof(char *));
314 nags
[i
++] = strdup(_("none"));
318 if (fscanf(fp
, " %[^\n] ", line
) == 1) {
319 nags
= Realloc(nags
, (i
+ 2) * sizeof(char *));
320 nags
[i
++] = strdup(line
);
329 void edit_nag_toggle_item(struct menu_input_s
*m
)
331 struct input_s
*in
= m
->data
;
332 struct input_data_s
*id
= in
->data
;
333 HISTORY
*h
= id
->data
;
336 if (m
->selected
== 0) {
337 for (i
= 0; i
< MAX_PGN_NAG
; i
++)
340 for (i
= 0; m
->items
[i
]; i
++)
341 m
->items
[i
]->selected
= 0;
346 for (i
= 0; i
< MAX_PGN_NAG
; i
++) {
347 if (h
->nag
[i
] == m
->selected
)
348 h
->nag
[i
] = m
->selected
= 0;
351 h
->nag
[i
] = m
->selected
;
358 void edit_nag_save(struct menu_input_s
*m
)
363 void edit_nag_help(struct menu_input_s
*m
)
365 message(_("NAG Menu Keys"), ANY_KEY_STR
, "%s",
367 " UP/DOWN - previous/next menu item\n"
368 " HOME/END - first/last menu item\n"
369 " PGDN/PGUP - next/previous page\n"
370 " a-zA-Z0-9 - jump to item\n"
371 " SPACE - toggle selected item\n"
372 " CTRL-X - quit with changes"
376 struct menu_item_s
**get_nag_items(WIN
*win
)
379 struct menu_input_s
*m
= win
->data
;
380 struct input_s
*in
= m
->data
;
381 struct input_data_s
*id
= in
->data
;
382 struct menu_item_s
**items
= m
->items
;
383 HISTORY
*h
= id
->data
;
386 for (i
= 0; items
[i
]; i
++)
390 for (i
= 0; nags
[i
]; i
++) {
391 items
= Realloc(items
, (i
+2) * sizeof(struct menu_item_s
*));
392 items
[i
] = Malloc(sizeof(struct menu_item_s
));
393 items
[i
]->name
= nags
[i
];
394 items
[i
]->value
= NULL
;
396 for (n
= 0; n
< MAX_PGN_NAG
; n
++) {
397 if (h
->nag
[n
] == i
) {
398 items
[i
]->selected
= 1;
405 items
[i
]->selected
= 0;
414 void nag_print(WIN
*win
)
416 struct menu_input_s
*m
= win
->data
;
418 mvwprintw(win
->w
, m
->print_line
, 1, "%-*s", win
->cols
- 2, m
->item
->name
);
421 void edit_nag(void *arg
)
423 struct menu_key_s
**keys
= NULL
;
430 add_menu_key(&keys
, ' ', edit_nag_toggle_item
);
431 add_menu_key(&keys
, CTRL_KEY('x'), edit_nag_save
);
432 add_menu_key(&keys
, KEY_F(1), edit_nag_help
);
433 construct_menu(0, 0, -1, -1, _("Numeric Annotation Glyphs"), 1, get_nag_items
, keys
, arg
,
438 static void *view_nag(void *arg
)
440 HISTORY
*h
= (HISTORY
*)arg
;
442 char line
[LINE_MAX
] = {0};
445 snprintf(buf
, sizeof(buf
), "%s \"%s\"", _("Viewing NAG for"), h
->move
);
452 for (i
= 0; i
< MAX_PGN_NAG
; i
++) {
458 if (h
->nag
[i
] >= nag_total
)
459 strncat(line
, itoa(h
->nag
[i
], buf2
), sizeof(line
)-1);
461 strncat(line
, nags
[h
->nag
[i
]], sizeof(line
)-1);
463 strncat(line
, "\n", sizeof(line
)-1);
466 line
[strlen(line
) - 1] = 0;
467 message(buf
, ANY_KEY_STR
, "%s", line
);
471 void view_annotation(HISTORY
*h
)
473 char buf
[MAX_SAN_MOVE_LEN
+ strlen(_("Viewing Annotation for")) + 4];
474 int nag
= 0, comment
= 0;
479 if (h
->comment
&& h
->comment
[0])
485 if (!nag
&& !comment
)
488 snprintf(buf
, sizeof(buf
), "%s \"%s\"", _("Viewing Annotation for"), h
->move
);
491 construct_message(buf
, (nag
) ? _("Any other key to continue") : ANY_KEY_STR
, 0, 1,
492 (nag
) ? _("Press 'n' to view NAG") : NULL
,
493 (nag
) ? view_nag
: NULL
, (nag
) ? h
: NULL
, NULL
,
494 (nag
) ? 'n' : 0, 0, "%s", h
->comment
);
496 construct_message(buf
, _("Any other key to continue"), 0, 1, _("Press 'n' to view NAG"), view_nag
, h
, NULL
,
497 'n', 0, "%s", _("No comment text for this move"));
500 int do_game_write(char *filename
, char *mode
, int start
, int end
)
503 struct userdata_s
*d
;
506 i
= pgn_open(filename
, mode
, &pgn
);
508 if (i
== E_PGN_ERR
) {
509 cmessage(ERROR_STR
, ANY_KEY_STR
, "%s\n%s", filename
, strerror(errno
));
512 else if (i
== E_PGN_INVALID
) {
513 cmessage(ERROR_STR
, ANY_KEY_STR
, "%s\n%s", filename
, _("Not a regular file"));
517 for (i
= (start
== -1) ? 0 : start
; i
< end
; i
++) {
519 pgn_write(pgn
, game
[i
]);
520 CLEAR_FLAG(d
->flags
, CF_MODIFIED
);
523 if (pgn_close(pgn
) != E_PGN_OK
)
524 message(ERROR_STR
, ANY_KEY_STR
, "%s", strerror(errno
));
527 strncpy(loadfile
, filename
, sizeof(loadfile
));
528 loadfile
[sizeof(loadfile
)-1] = 0;
541 void do_save_game_overwrite_confirm(WIN
*win
)
544 struct save_game_s
*s
= win
->data
;
545 wchar_t str
[] = { win
->c
, 0 };
547 if (!wcscmp (str
, append_wchar
))
549 else if (!wcscmp (str
, overwrite_wchar
))
554 if (do_game_write(s
->filename
, mode
, s
->start
, s
->end
))
555 update_status_notify(gp
, "%s", _("Save game failed."));
557 update_status_notify(gp
, "%s", _("Game saved."));
564 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
567 void save_pgn(char *filename
, int saveindex
)
569 char buf
[FILENAME_MAX
];
571 int end
= (saveindex
== -1) ? gtotal
: saveindex
+ 1;
572 struct save_game_s
*s
;
574 if (filename
[0] != '/' && config
.savedirectory
) {
575 if (stat(config
.savedirectory
, &st
) == -1) {
576 if (errno
== ENOENT
) {
577 if (mkdir(config
.savedirectory
, 0755) == -1) {
578 cmessage(ERROR_STR
, ANY_KEY_STR
, "%s: %s", config
.savedirectory
,
584 cmessage(ERROR_STR
, ANY_KEY_STR
, "%s: %s", config
.savedirectory
,
590 stat(config
.savedirectory
, &st
);
592 if (!S_ISDIR(st
.st_mode
)) {
593 cmessage(ERROR_STR
, ANY_KEY_STR
, "%s: %s", config
.savedirectory
, _("Not a directory."));
597 snprintf(buf
, sizeof(buf
), "%s/%s", config
.savedirectory
, filename
);
601 if (access(filename
, W_OK
) == 0) {
602 s
= Malloc(sizeof(struct save_game_s
));
603 s
->filename
= strdup(filename
);
604 s
->start
= saveindex
;
606 construct_message(NULL
, _("What would you like to do?"), 0, 1, NULL
,
607 NULL
, s
, do_save_game_overwrite_confirm
, 0, 0,
608 "%s \"%s\"\nPress \"%ls\" to append to this file, \"%ls\" to overwrite or any other key to cancel.",
609 _("File exists:"), filename
, append_wchar
,
614 if (do_game_write(filename
, "a", saveindex
, end
))
615 update_status_notify(gp
, "%s", _("Save game failed."));
617 update_status_notify(gp
, "%s", _("Game saved."));
620 static int castling_state(GAME g
, BOARD b
, int row
, int col
, int piece
, int mod
)
622 if (pgn_piece_to_int(piece
) == ROOK
&& col
== 7
624 (TEST_FLAG(g
->flags
, GF_WK_CASTLE
) || mod
) &&
625 pgn_piece_to_int(b
[7][4].icon
) == KING
&& isupper(piece
)) {
627 TOGGLE_FLAG(g
->flags
, GF_WK_CASTLE
);
630 else if (pgn_piece_to_int(piece
) == ROOK
&& col
== 0
632 (TEST_FLAG(g
->flags
, GF_WQ_CASTLE
) || mod
) &&
633 pgn_piece_to_int(b
[7][4].icon
) == KING
&& isupper(piece
)) {
635 TOGGLE_FLAG(g
->flags
, GF_WQ_CASTLE
);
638 else if (pgn_piece_to_int(piece
) == ROOK
&& col
== 7
640 (TEST_FLAG(g
->flags
, GF_BK_CASTLE
) || mod
) &&
641 pgn_piece_to_int(b
[0][4].icon
) == KING
&& islower(piece
)) {
643 TOGGLE_FLAG(g
->flags
, GF_BK_CASTLE
);
646 else if (pgn_piece_to_int(piece
) == ROOK
&& col
== 0
648 (TEST_FLAG(g
->flags
, GF_BQ_CASTLE
) || mod
) &&
649 pgn_piece_to_int(b
[0][4].icon
) == KING
&& islower(piece
)) {
651 TOGGLE_FLAG(g
->flags
, GF_BQ_CASTLE
);
654 else if (pgn_piece_to_int(piece
) == KING
&& col
== 4
656 (mod
|| (pgn_piece_to_int(b
[7][7].icon
) == ROOK
&&
657 TEST_FLAG(g
->flags
, GF_WK_CASTLE
))
659 (pgn_piece_to_int(b
[7][0].icon
) == ROOK
&&
660 TEST_FLAG(g
->flags
, GF_WQ_CASTLE
))) && isupper(piece
)) {
662 if (TEST_FLAG(g
->flags
, GF_WK_CASTLE
) ||
663 TEST_FLAG(g
->flags
, GF_WQ_CASTLE
))
664 CLEAR_FLAG(g
->flags
, GF_WK_CASTLE
|GF_WQ_CASTLE
);
666 SET_FLAG(g
->flags
, GF_WK_CASTLE
|GF_WQ_CASTLE
);
670 else if (pgn_piece_to_int(piece
) == KING
&& col
== 4
672 (mod
|| (pgn_piece_to_int(b
[0][7].icon
) == ROOK
&&
673 TEST_FLAG(g
->flags
, GF_BK_CASTLE
))
675 (pgn_piece_to_int(b
[0][0].icon
) == ROOK
&&
676 TEST_FLAG(g
->flags
, GF_BQ_CASTLE
))) && islower(piece
)) {
678 if (TEST_FLAG(g
->flags
, GF_BK_CASTLE
) ||
679 TEST_FLAG(g
->flags
, GF_BQ_CASTLE
))
680 CLEAR_FLAG(g
->flags
, GF_BK_CASTLE
|GF_BQ_CASTLE
);
682 SET_FLAG(g
->flags
, GF_BK_CASTLE
|GF_BQ_CASTLE
);
690 #define IS_ENPASSANT(c) (c == 'x') ? CP_BOARD_ENPASSANT : isupper(c) ? CP_BOARD_WHITE : CP_BOARD_BLACK
691 #define ATTRS(cp) (cp & (A_BOLD|A_STANDOUT|A_BLINK|A_DIM|A_UNDERLINE|A_INVIS|A_REVERSE))
696 w_pawn_wchar
= str_to_wchar (config
.utf8_pieces
? "♙" : "P");
697 w_rook_wchar
= str_to_wchar (config
.utf8_pieces
? "♖" : "R");
698 w_bishop_wchar
= str_to_wchar (config
.utf8_pieces
? "♗" : "B");
699 w_knight_wchar
= str_to_wchar (config
.utf8_pieces
? "♘" : "N");
700 w_queen_wchar
= str_to_wchar (config
.utf8_pieces
? "♕" : "Q");
701 w_king_wchar
= str_to_wchar (config
.utf8_pieces
? "♔" : "K");
702 b_pawn_wchar
= str_to_wchar (config
.utf8_pieces
? "♟" : "p");
703 b_rook_wchar
= str_to_wchar (config
.utf8_pieces
? "♜" : "r");
704 b_bishop_wchar
= str_to_wchar (config
.utf8_pieces
? "♝" : "b");
705 b_knight_wchar
= str_to_wchar (config
.utf8_pieces
? "♞" : "n");
706 b_queen_wchar
= str_to_wchar (config
.utf8_pieces
? "♛" : "q");
707 b_king_wchar
= str_to_wchar (config
.utf8_pieces
? "♚" : "k");
708 empty_wchar
= str_to_wchar (" ");
709 enpassant_wchar
= str_to_wchar ("x");
713 piece_to_wchar (unsigned char p
)
726 return w_bishop_wchar
;
728 return b_bishop_wchar
;
730 return w_knight_wchar
;
732 return b_knight_wchar
;
734 return w_queen_wchar
;
736 return b_queen_wchar
;
742 return enpassant_wchar
;
748 static int piece_can_attack (GAME g
, int rank
, int file
)
750 struct userdata_s
*d
= g
->data
;
751 char *m
, *frfr
= NULL
;
753 int row
, col
, p
, v
, pi
, cpi
;
756 rotate_position(CURSOR_POSITION
);
757 rotate_position(SPS_POSITION
);
760 v
= d
->b
[RANKTOBOARD (d
->c_row
)][FILETOBOARD (d
->c_col
)].valid
;
761 pi
= pgn_piece_to_int (d
->b
[RANKTOBOARD (rank
)][FILETOBOARD (file
)].icon
);
763 ? pgn_piece_to_int (d
->b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
)
764 : pgn_piece_to_int (d
->b
[RANKTOBOARD (d
->c_row
)][FILETOBOARD (d
->c_col
)].icon
);
766 if (pi
== OPEN_SQUARE
|| cpi
== OPEN_SQUARE
|| !VALIDFILE (file
)
767 || !VALIDRANK (rank
)) {
769 rotate_position(CURSOR_POSITION
);
770 rotate_position(SPS_POSITION
);
777 col
= v
? d
->c_col
: d
->sp
.scol
;
778 row
= v
? d
->c_row
: d
->sp
.srow
;
785 m
= malloc(MAX_SAN_MOVE_LEN
+1);
786 m
[0] = INTTOFILE (file
);
787 m
[1] = INTTORANK (rank
);
788 m
[2] = INTTOFILE (col
);
789 m
[3] = INTTORANK (row
);
792 if (d
->sp
.icon
&& v
) {
795 memcpy (b
, d
->b
, sizeof(BOARD
));
796 p
= b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
;
797 b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
=
798 pgn_int_to_piece (WHITE
, OPEN_SQUARE
);
799 b
[RANKTOBOARD (row
)][FILETOBOARD (col
)].icon
= p
;
801 e
= pgn_validate_move (g
, b
, &m
, &frfr
);
806 if (e
!= E_PGN_OK
&& pgn_piece_to_int (d
->sp
.icon
) == PAWN
) {
807 int n
= (d
->sp
.srow
== 7 && rank
== 5) ? 6 :
808 (d
->sp
.srow
== 2 && rank
== 4) ? 3 : 0;
810 if (n
&& (file
== d
->c_col
-1 || file
== d
->c_col
+1)) {
811 memcpy (b
, d
->b
, sizeof(BOARD
));
812 p
= b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
;
813 b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
=
814 pgn_int_to_piece (WHITE
, OPEN_SQUARE
);
815 b
[RANKTOBOARD (row
)][FILETOBOARD (col
)].icon
= p
;
816 b
[RANKTOBOARD (n
)][FILETOBOARD (d
->sp
.scol
)].enpassant
= 1;
817 m
= malloc(MAX_SAN_MOVE_LEN
+1);
818 m
[0] = INTTOFILE (file
);
819 m
[1] = INTTORANK (rank
);
820 m
[2] = INTTOFILE (col
);
821 m
[3] = INTTORANK (n
);
824 SET_FLAG (g
->flags
, GF_ENPASSANT
);
825 e
= pgn_validate_move (g
, b
, &m
, &frfr
);
826 CLEAR_FLAG (g
->flags
, GF_ENPASSANT
);
834 rotate_position(CURSOR_POSITION
);
835 rotate_position(SPS_POSITION
);
838 return e
== E_PGN_OK
? 1 : 0;
842 e
= pgn_validate_move (g
, d
->b
, &m
, &frfr
);
845 if (!strcmp (m
, "O-O") || !strcmp (m
, "O-O-O"))
849 int sf
= FILETOINT (frfr
[0]), sr
= RANKTOINT (frfr
[1]);
850 int df
= FILETOINT (frfr
[2]);
852 pi
= d
->b
[RANKTOBOARD (sr
)][FILETOBOARD (sf
)].icon
;
853 pi
= pgn_piece_to_int (pi
);
854 if (pi
== PAWN
&& sf
== df
)
862 rotate_position(CURSOR_POSITION
);
863 rotate_position(SPS_POSITION
);
866 return e
== E_PGN_OK
? 1 : 0;
869 void print_piece(WINDOW
*w
, int l
, int c
, char p
)
873 for (i
= 0; i
< 13; i
+= 2) {
874 if (p
== piece_chars
[i
] || p
== piece_chars
[i
+ 1]) {
875 for (y
= 0; y
< 3; y
++)
876 mvwprintw(w
, l
+ y
, c
, "%s", f_pieces
[i
+ ff
+ y
]);
883 for (y
= 0; y
< 3; y
++)
884 mvwprintw(w
, l
+ y
, c
, f_pieces
[0]);
891 for (fx
= 1; fx
< 9; fx
++) {
900 int coordofmove(GAME g
, char *move
, int yorx
)
904 if (!strcmp (move
, "O-O")) {
906 return g
->turn
== WHITE
? 8 : 1;
910 else if (!strcmp (move
, "O-O-O")) {
912 return g
->turn
== WHITE
? 8 : 1;
918 if (strchr(move
, '+') && strchr(move
, '=')) {
922 else if (!strchr(move
, '+') && strchr(move
, '=')) {
926 else if (strchr(move
, '+') && !strchr(move
, '=')) {
936 return move
[l
- y
] - 48;
938 for (i
= 0; i
< 8; i
++) {
939 if (move
[l
- x
] == "abcdefgh"[i
])
947 static int is_prev_move (struct userdata_s
*d
, int brow
, int bcol
,
951 row
== ROWTOMATRIX(((rotate
) ? inv_int(d
->pm_row
) : d
->pm_row
)) &&
952 col
== COLTOMATRIX(((rotate
) ? inv_int(d
->pm_col
) : d
->pm_col
)))
954 brow
+ 1 == ((rotate
) ? d
->pm_row
: inv_int(d
->pm_row
)) &&
955 bcol
+ 1 == ((rotate
) ? inv_int(d
->pm_col
) : d
->pm_col
)))
961 void update_board_window(GAME g
)
964 int bcol
= 0, brow
= 0;
965 int l
= config
.coordsyleft
;
966 int maxy
= BOARD_HEIGHT
, maxx
= BOARD_WIDTH
;
967 int ncols
= 0, offset
= 1;
968 int rowr
= (MEGA_BOARD
) ? 6 : (BIG_BOARD
) ? 4 : 2;
969 int colr
= (MEGA_BOARD
) ? 12 : (BIG_BOARD
) ? 8 : 4;
970 unsigned coords_y
= 8, cxgc
= 0;
972 struct userdata_s
*d
= g
->data
;
975 h
= pgn_history_by_n(g
->hp
, g
->hindex
- 1);
976 d
->pm_row
= (h
&& h
->move
&& d
->mode
== MODE_PLAY
)
977 ? coordofmove(g
, h
->move
, 1) : 0;
978 d
->pm_col
= (h
&& h
->move
&& d
->mode
== MODE_PLAY
)
979 ? coordofmove(g
, h
->move
, 0) : 0;
981 if (d
->mode
!= MODE_PLAY
&& d
->mode
!= MODE_EDIT
)
982 update_cursor(g
, g
->hindex
);
999 for (row
= 0; row
< maxy
; row
++) {
1013 for (col
= 0; col
< maxx
; col
++) {
1015 chtype attrs
= 0, old_attrs
= 0;
1020 if (row
== 0 || row
== maxy
- 2) {
1022 mvwaddch(boardw
, row
, col
+ l
,
1024 ? ACS_LLCORNER
| CP_BOARD_GRAPHICS
1025 : ACS_ULCORNER
| CP_BOARD_GRAPHICS
));
1026 else if (col
== maxx
- 2)
1027 mvwaddch(boardw
, row
, col
+ l
,
1029 ? ACS_LRCORNER
| CP_BOARD_GRAPHICS
1030 : ACS_URCORNER
| CP_BOARD_GRAPHICS
));
1031 else if (!(col
% colr
))
1032 mvwaddch(boardw
, row
, col
+ l
,
1034 ? ACS_BTEE
| CP_BOARD_GRAPHICS
1035 : ACS_TTEE
| CP_BOARD_GRAPHICS
));
1037 if (col
!= maxx
- 1)
1038 mvwaddch(boardw
, row
, col
+ l
,
1039 LINE_GRAPHIC(ACS_HLINE
| CP_BOARD_GRAPHICS
));
1045 if ((row
% 2) && col
== maxx
- 1 &&
1046 (coords_y
> 0 && coords_y
< 9)) {
1047 wattron(boardw
, CP_BOARD_COORDS
);
1049 (BIG_BOARD
) ? row
* ((MEGA_BOARD
) ? 3 : 2)
1050 : row
, (l
) ? 0 : col
, "%d",
1051 (rotate
) ? coords_y
++ : coords_y
--);
1052 wattroff(boardw
, CP_BOARD_COORDS
);
1056 if ((col
== 0 || col
== maxx
- 2) && row
!= maxy
- 1) {
1058 mvwaddch(boardw
, row
, col
+ l
,
1059 LINE_GRAPHIC((col
) ?
1060 ACS_RTEE
| CP_BOARD_GRAPHICS
:
1061 ACS_LTEE
| CP_BOARD_GRAPHICS
));
1063 mvwaddch(boardw
, row
, col
+ l
,
1064 LINE_GRAPHIC(ACS_VLINE
| CP_BOARD_GRAPHICS
));
1069 if ((row
% rowr
) && !(col
% colr
) && row
!= maxy
- 1) {
1070 mvwaddch(boardw
, row
, col
+ l
,
1071 LINE_GRAPHIC(ACS_VLINE
| CP_BOARD_GRAPHICS
));
1075 if (!(col
% colr
) && row
!= maxy
- 1) {
1076 mvwaddch(boardw
, row
, col
+ l
,
1077 LINE_GRAPHIC(ACS_PLUS
| CP_BOARD_GRAPHICS
));
1084 attrwhich
= (cb
[brow
][bcol
]) ? WHITE
: BLACK
;
1091 if (((ncols
% 2) && !(offset
% 2))
1092 || (!(ncols
% 2) && (offset
% 2)))
1098 if (BIG_BOARD
&& rotate
) {
1099 brow
= inv_int(brow
+ 1) - 1;
1100 bcol
= inv_int(bcol
+ 1) - 1;
1104 p
= d
->b
[brow
][bcol
].icon
;
1106 p
= d
->b
[RANKTOBOARD (brow
)][FILETOBOARD (bcol
)].icon
;
1108 int pi
= pgn_piece_to_int(p
);
1110 if (config
.details
&&
1111 ((!BIG_BOARD
&& d
->b
[RANKTOBOARD (brow
)][FILETOBOARD (bcol
)].enpassant
)
1112 || (BIG_BOARD
&& d
->b
[brow
][bcol
].enpassant
))) {
1114 attrs
= mix_cp(CP_BOARD_ENPASSANT
,
1115 (attrwhich
== WHITE
) ? CP_BOARD_WHITE
: CP_BOARD_BLACK
,
1116 ATTRS(CP_BOARD_ENPASSANT
), A_FG_B_BG
);
1119 if (config
.showattacks
&& config
.details
1120 && piece_can_attack (g
,
1121 BIG_BOARD
? inv_int (brow
+1) : brow
,
1122 BIG_BOARD
? bcol
+1 : bcol
)) {
1123 attrs
= CP_BOARD_ATTACK
;
1128 if (config
.validmoves
&&
1129 ((!BIG_BOARD
&& d
->b
[RANKTOBOARD (brow
)][FILETOBOARD (bcol
)].valid
)
1130 || (BIG_BOARD
&& d
->b
[brow
][bcol
].valid
))) {
1134 if (attrwhich
== WHITE
)
1135 attrs
= mix_cp(CP_BOARD_MOVES_WHITE
,
1137 ATTRS(CP_BOARD_MOVES_WHITE
),
1140 attrs
= mix_cp(CP_BOARD_MOVES_BLACK
,
1142 ATTRS(CP_BOARD_MOVES_BLACK
),
1145 else if (p
!= 'x' && !can_attack
)
1146 attrs
= (attrwhich
== WHITE
) ? CP_BOARD_WHITE
: CP_BOARD_BLACK
;
1148 if (BIG_BOARD
&& rotate
) {
1149 brow
= inv_int(brow
+ 1) - 1;
1150 bcol
= inv_int(bcol
+ 1) - 1;
1153 if ((!BIG_BOARD
&& row
== ROWTOMATRIX(d
->c_row
)
1154 && col
== COLTOMATRIX(d
->c_col
))
1155 || (BIG_BOARD
&& brow
+ 1 == inv_int(d
->c_row
)
1156 && bcol
+ 1 == d
->c_col
)) {
1157 attrs
= mix_cp(CP_BOARD_CURSOR
, IS_ENPASSANT(p
),
1158 ATTRS(CP_BOARD_CURSOR
), B_FG_A_BG
);
1161 else if ((!BIG_BOARD
&& row
== ROWTOMATRIX(d
->sp
.srow
) &&
1162 col
== COLTOMATRIX(d
->sp
.scol
)) ||
1163 (BIG_BOARD
&& brow
+ 1 == inv_int(d
->sp
.srow
) &&
1164 bcol
+ 1 == d
->sp
.scol
)) {
1165 attrs
= mix_cp(CP_BOARD_SELECTED
, IS_ENPASSANT(p
),
1166 ATTRS(CP_BOARD_SELECTED
), B_FG_A_BG
);
1169 else if (is_prev_move (d
, brow
, bcol
, row
, col
) && !valid
)
1171 attrs
= mix_cp(CP_BOARD_PREVMOVE
, IS_ENPASSANT(p
),
1172 ATTRS(CP_BOARD_PREVMOVE
), B_FG_A_BG
);
1176 if (row
== maxy
- 1)
1180 int n
= is_prev_move (d
, brow
, bcol
, row
, col
);
1181 chtype a
= n
&& !valid
1182 ? CP_BOARD_PREVMOVE
: attrwhich
== WHITE
? valid
? CP_BOARD_MOVES_WHITE
: CP_BOARD_WHITE
: valid
? CP_BOARD_MOVES_BLACK
: CP_BOARD_BLACK
;
1183 attrs
= mix_cp(CP_BOARD_ATTACK
, a
,
1184 ATTRS (CP_BOARD_ATTACK
), A_FG_B_BG
);
1189 wmove(boardw
, row
, col
+ ((MEGA_BOARD
) ? 5 : 3) + l
);
1191 mvwaddch(boardw
, row
, col
+ l
, ' ' | attrs
);
1193 if (row
== maxy
- 1 && cxgc
< 8) {
1194 waddch(boardw
, "abcdefgh"[(BIG_BOARD
) ? bcol
: bcol
- 1] | CP_BOARD_COORDS
);
1198 if (old_attrs
== -1) {
1205 if (pi
!= OPEN_SQUARE
&& p
!= 'x' && !can_attack
) {
1206 if (attrwhich
== WHITE
) {
1208 attrs
= CP_BOARD_W_W
;
1210 attrs
= CP_BOARD_W_B
;
1214 attrs
= CP_BOARD_B_W
;
1216 attrs
= CP_BOARD_B_B
;
1222 if (config
.details
&& !can_attack
1223 && castling_state(g
, d
->b
,
1224 (rotate
) ? inv_int(brow
+ 1) - 1: brow
,
1225 (rotate
) ? inv_int(bcol
+ 1) - 1: bcol
, p
, 0))
1226 attrs
= mix_cp(CP_BOARD_CASTLING
, attrs
,
1227 ATTRS(CP_BOARD_CASTLING
),
1231 if (config
.details
&& !can_attack
1232 && castling_state(g
, d
->b
, RANKTOBOARD (brow
),
1233 FILETOBOARD (bcol
), p
, 0)) {
1234 attrs
= mix_cp(CP_BOARD_CASTLING
, attrs
,
1235 ATTRS(CP_BOARD_CASTLING
),
1241 // FIXME: Reimpresión de piezas(+4).
1243 wattron (boardw
, attrs
);
1245 for (i
= 0; i
< 5; i
++)
1246 mvwprintw(boardw
, i
+ brow
* 6 + 1,
1249 if (pi
!= OPEN_SQUARE
)
1250 print_piece(boardw
, brow
* 6 + 2,
1251 bcol
* 12 + 3 + l
, p
);
1254 print_piece(boardw
, brow
* 4 + 1,
1256 (pi
!= OPEN_SQUARE
) ? p
: 0);
1259 wattroff (boardw
, attrs
);
1264 wattron (boardw
, attrs
);
1265 waddwstr (boardw
, piece_to_wchar (pi
!= OPEN_SQUARE
? p
: 0));
1266 wattroff (boardw
, attrs
);
1273 col
+= (MEGA_BOARD
) ? 10 : 6;
1275 waddch(boardw
, ' ' | attrs
);
1293 if (col
!= maxx
- 1)
1294 mvwaddch(boardw
, row
, col
+ l
,
1295 LINE_GRAPHIC(ACS_HLINE
| CP_BOARD_GRAPHICS
));
1315 void invalid_move(int n
, int e
, const char *m
)
1317 if (curses_initialized
)
1318 cmessage(ERROR_STR
, ANY_KEY_STR
, "%s \"%s\" (round #%i)", (e
== E_PGN_AMBIGUOUS
)
1319 ? _("Ambiguous move") : _("Invalid move"), m
, n
);
1321 warnx("%s: %s \"%s\" (round #%i)", loadfile
, (e
== E_PGN_AMBIGUOUS
)
1322 ? _("Ambiguous move") : _("Invalid move"), m
, n
);
1325 void gameover(GAME g
)
1327 struct userdata_s
*d
= g
->data
;
1329 SET_FLAG(g
->flags
, GF_GAMEOVER
);
1330 d
->mode
= MODE_HISTORY
;
1334 static void update_clock(GAME g
, struct itimerval it
)
1336 struct userdata_s
*d
= g
->data
;
1338 if (TEST_FLAG(d
->flags
, CF_CLOCK
) && g
->turn
== WHITE
) {
1339 d
->wclock
.elapsed
.tv_sec
+= it
.it_value
.tv_sec
;
1340 d
->wclock
.elapsed
.tv_usec
+= it
.it_value
.tv_usec
;
1342 if (d
->wclock
.elapsed
.tv_usec
> 1000000 - 1) {
1343 d
->wclock
.elapsed
.tv_sec
+= d
->wclock
.elapsed
.tv_usec
/ 1000000;
1344 d
->wclock
.elapsed
.tv_usec
= d
->wclock
.elapsed
.tv_usec
% 1000000;
1347 if (d
->wclock
.tc
[d
->wclock
.tcn
][1] &&
1348 d
->wclock
.elapsed
.tv_sec
>= d
->wclock
.tc
[d
->wclock
.tcn
][1]) {
1349 pgn_tag_add(&g
->tag
, "Result", "0-1");
1353 else if (TEST_FLAG(d
->flags
, CF_CLOCK
) && g
->turn
== BLACK
) {
1354 d
->bclock
.elapsed
.tv_sec
+= it
.it_value
.tv_sec
;
1355 d
->bclock
.elapsed
.tv_usec
+= it
.it_value
.tv_usec
;
1357 if (d
->bclock
.elapsed
.tv_usec
> 1000000 - 1) {
1358 d
->bclock
.elapsed
.tv_sec
+= d
->bclock
.elapsed
.tv_usec
/ 1000000;
1359 d
->bclock
.elapsed
.tv_usec
= d
->bclock
.elapsed
.tv_usec
% 1000000;
1362 if (d
->bclock
.tc
[d
->bclock
.tcn
][1] &&
1363 d
->bclock
.elapsed
.tv_sec
>= d
->bclock
.tc
[d
->bclock
.tcn
][1]) {
1364 pgn_tag_add(&g
->tag
, "Result", "1-0");
1369 d
->elapsed
.tv_sec
+= it
.it_value
.tv_sec
;
1370 d
->elapsed
.tv_usec
+= it
.it_value
.tv_usec
;
1372 if (d
->elapsed
.tv_usec
> 1000000 - 1) {
1373 d
->elapsed
.tv_sec
+= d
->elapsed
.tv_usec
/ 1000000;
1374 d
->elapsed
.tv_usec
= d
->elapsed
.tv_usec
% 1000000;
1378 static void update_time_control(GAME g
)
1380 struct userdata_s
*d
= g
->data
;
1381 struct clock_s
*clk
= (g
->turn
== WHITE
) ? &d
->wclock
: &d
->bclock
;
1384 clk
->tc
[clk
->tcn
][1] += clk
->incr
;
1386 if (!clk
->tc
[clk
->tcn
][1])
1391 if (!clk
->tc
[clk
->tcn
][0] || clk
->move
>= clk
->tc
[clk
->tcn
][0]) {
1393 clk
->tc
[clk
->tcn
+ 1][1] += abs(clk
->elapsed
.tv_sec
- clk
->tc
[clk
->tcn
][1]);
1394 memset(&clk
->elapsed
, 0, sizeof(clk
->elapsed
));
1399 void update_history_window(GAME g
)
1401 char buf
[HISTORY_WIDTH
- 1];
1404 int t
= pgn_history_total(g
->hp
);
1406 n
= (g
->hindex
+ 1) / 2;
1409 total
= (t
+ 1) / 2;
1414 snprintf(buf
, sizeof(buf
), "%u %s %u%s", n
, _("of"), total
,
1415 (movestep
== 1) ? _(" (ply)") : "");
1417 strncpy(buf
, _("not available"), sizeof(buf
)-1);
1419 buf
[sizeof(buf
)-1] = 0;
1420 mvwprintw(historyw
, 2, 1, "%*s %-*s", 10, _("Move:"),
1421 HISTORY_WIDTH
- 14, buf
);
1423 h
= pgn_history_by_n(g
->hp
, g
->hindex
);
1424 snprintf(buf
, sizeof(buf
), "%s",
1425 (h
&& h
->move
) ? h
->move
1426 : (LINES
< 24) ? _("empty") : _("not available"));
1429 if (h
&& ((h
->comment
) || h
->nag
[0])) {
1430 strncat(buf
, _(" (Annotated"), sizeof(buf
)-1);
1435 strncat(buf
, (n
) ? ",+" : " (+", sizeof(buf
)-1);
1440 strncat(buf
, (n
) ? ",-" : " (-", sizeof(buf
)-1);
1445 strncat(buf
, ")", sizeof(buf
)-1);
1447 mvwprintw(historyw
, 3, ((LINES
< 24) ? 17 : 1), "%s %-*s",
1448 (LINES
< 24) ? _("Next:") :_("Next move:"),
1449 HISTORY_WIDTH
- ((LINES
< 24) ? 26 : 14), buf
);
1451 h
= pgn_history_by_n(g
->hp
, g
->hindex
- 1);
1452 snprintf(buf
, sizeof(buf
), "%s",
1453 (h
&& h
->move
) ? h
->move
1454 : (LINES
< 24) ? _("empty") : _("not available"));
1457 if (h
&& ((h
->comment
) || h
->nag
[0])) {
1458 strncat(buf
, _(" (Annotated"), sizeof(buf
)-1);
1463 strncat(buf
, (n
) ? ",+" : " (+", sizeof(buf
)-1);
1468 strncat(buf
, (n
) ? ",-" : " (-", sizeof(buf
)-1);
1473 strncat(buf
, ")", sizeof(buf
)-1);
1475 mvwprintw(historyw
, ((LINES
< 24) ? 3 : 4), 1, "%s %-*s",
1476 (LINES
< 24) ? _("Prev.:") : _("Prev move:"),
1477 HISTORY_WIDTH
- ((LINES
< 24) ? 26 : 14), buf
);
1480 void do_validate_move(char **move
)
1482 struct userdata_s
*d
= gp
->data
;
1486 if (TEST_FLAG(d
->flags
, CF_HUMAN
)) {
1487 if ((n
= pgn_parse_move(gp
, d
->b
, move
, &frfr
)) != E_PGN_OK
) {
1488 invalid_move(d
->n
+ 1, n
, *move
);
1492 update_time_control(gp
);
1493 pgn_history_add(gp
, d
->b
, *move
);
1494 pgn_switch_turn(gp
);
1497 if ((n
= pgn_validate_move(gp
, d
->b
, move
, &frfr
)) != E_PGN_OK
) {
1498 invalid_move(d
->n
+ 1, n
, *move
);
1502 add_engine_command(gp
, ENGINE_THINKING
, "%s\n",
1503 (config
.engine_protocol
== 1) ? frfr
: *move
);
1506 d
->sp
.srow
= d
->sp
.scol
= d
->sp
.icon
= 0;
1508 if (config
.validmoves
)
1509 pgn_reset_valid_moves(d
->b
);
1511 if (TEST_FLAG(gp
->flags
, GF_GAMEOVER
))
1512 d
->mode
= MODE_HISTORY
;
1514 SET_FLAG(d
->flags
, CF_MODIFIED
);
1518 update_history_window(gp
);
1519 update_board_window(gp
);
1523 void do_promotion_piece_finalize(WIN
*win
)
1525 char *p
, *str
= win
->data
;
1527 if (pgn_piece_to_int(win
->c
) == -1)
1530 p
= str
+ strlen(str
);
1531 *p
++ = toupper(win
->c
);
1533 do_validate_move(&str
);
1538 static void move_to_engine(GAME g
)
1540 struct userdata_s
*d
= g
->data
;
1544 if (config
.validmoves
&&
1545 !d
->b
[RANKTOBOARD(d
->sp
.row
)][FILETOBOARD(d
->sp
.col
)].valid
)
1548 str
= Malloc(MAX_SAN_MOVE_LEN
+ 1);
1549 snprintf(str
, MAX_SAN_MOVE_LEN
+ 1, "%c%i%c%i",
1550 _("abcdefgh")[d
->sp
.scol
- 1],
1551 d
->sp
.srow
, _("abcdefgh")[d
->sp
.col
- 1], d
->sp
.row
);
1553 piece
= pgn_piece_to_int(d
->b
[RANKTOBOARD(d
->sp
.srow
)][FILETOBOARD(d
->sp
.scol
)].icon
);
1555 if (piece
== PAWN
&& (d
->sp
.row
== 8 || d
->sp
.row
== 1)) {
1556 construct_message(_("Select Pawn Promotion Piece"), _ ("R/N/B/Q"), 1, 1, NULL
, NULL
,
1557 str
, do_promotion_piece_finalize
, 0, 0, "%s", _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1561 do_validate_move(&str
);
1565 static char *clock_to_char(long n
)
1567 static char buf
[16];
1568 int h
= 0, m
= 0, s
= 0;
1571 m
= (n
% 3600) / 60;
1572 s
= (n
% 3600) % 60;
1573 snprintf(buf
, sizeof(buf
), "%.2i:%.2i:%.2i", h
, m
, s
);
1577 static char *timeval_to_char(struct timeval t
, long limit
)
1580 int h
= 0, m
= 0, s
= 0;
1581 int n
= limit
? abs(limit
- t
.tv_sec
) : 0;
1584 m
= (n
% 3600) / 60;
1585 s
= (n
% 3600) % 60;
1586 snprintf(buf
, sizeof(buf
), "%.2i:%.2i:%.2i", h
, m
, s
);
1590 static char *time_control_status(struct clock_s
*clk
)
1592 static char buf
[80] = {0};
1596 if (clk
->tc
[clk
->tcn
][0] && clk
->tc
[clk
->tcn
+ 1][1])
1597 snprintf(buf
, sizeof(buf
), " M%.2i/%s", abs(clk
->tc
[clk
->tcn
][0] - clk
->move
),
1598 clock_to_char(clk
->tc
[clk
->tcn
+ 1][1]));
1599 else if (!clk
->incr
)
1604 strncat(buf
, " I", sizeof(buf
)-1);
1605 strncat(buf
, itoa(clk
->incr
, buf
), sizeof(buf
)-1);
1611 void update_status_window(GAME g
)
1615 char tmp
[15] = {0}, *engine
, *mode
;
1621 struct userdata_s
*d
= g
->data
;
1625 if (!curses_initialized
)
1628 getmaxyx(statusw
, maxy
, maxx
);
1635 wchar_t *loadfilew
= loadfile
[0] ? str_etc (loadfile
, w
, 1) : str_to_wchar (_ ("not available"));
1636 mvwprintw(statusw
, y
++, 1, "%*s %-*ls", 7, _("File:"), w
, loadfilew
);
1638 snprintf(buf
, len
, "%i %s %i", gindex
+ 1, _("of"), gtotal
);
1639 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, _("Game:"), w
, buf
);
1644 if (config
.details
) {
1649 if (TEST_FLAG(d
->flags
, CF_DELETE
)) {
1657 if (TEST_FLAG(g
->flags
, GF_PERROR
)) {
1665 if (TEST_FLAG(d
->flags
, CF_MODIFIED
)) {
1673 pgn_config_get(PGN_STRICT_CASTLING
, &n
);
1683 if (TEST_FLAG(d
->flags
, CF_PERL
)) {
1693 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, _("Flags:"), w
, (tmp
[0]) ? tmp
: "-");
1697 mode
= _("move history");
1706 mode
= _("(empty value)");
1710 snprintf(buf
, len
- 1, "%*s %s", 7, _("Mode:"), mode
);
1712 if (d
->mode
== MODE_PLAY
) {
1713 if (TEST_FLAG(d
->flags
, CF_HUMAN
))
1714 strncat(buf
, _(" (human/human)"), len
- 1);
1715 else if (TEST_FLAG(d
->flags
, CF_ENGINE_LOOP
))
1716 strncat(buf
, _(" (engine/engine)"), len
- 1);
1718 strncat(buf
, (d
->play_mode
== PLAY_EH
) ?
1719 _(" (engine/human)") : _(" (human/engine)"), len
- 1);
1723 mvwprintw(statusw
, y
++, 1, "%-*s", len
, buf
);
1727 switch (d
->engine
->status
) {
1728 case ENGINE_THINKING
:
1729 engine
= _("pondering...");
1732 engine
= _("ready");
1734 case ENGINE_INITIALIZING
:
1735 engine
= _("initializing...");
1737 case ENGINE_OFFLINE
:
1738 engine
= _("offline");
1741 engine
= _("(empty value)");
1746 engine
= _("offline");
1748 mvwprintw(statusw
, y
, 1, "%*s %-*s", 7, _("Engine:"), w
, " ");
1749 wattron(statusw
, CP_STATUS_ENGINE
);
1750 mvwaddstr(statusw
, y
++, 9, engine
);
1751 wattroff(statusw
, CP_STATUS_ENGINE
);
1753 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, _("Turn:"), w
,
1754 (g
->turn
== WHITE
) ? _("white") : _("black"));
1756 strncpy(tmp
, _("white"), sizeof(tmp
)-1);
1757 tmp
[0] = toupper(tmp
[0]);
1758 snprintf(t
, sizeof(t
), "%s%s",
1759 timeval_to_char(d
->wclock
.elapsed
, d
->wclock
.tc
[d
->wclock
.tcn
][1]),
1760 time_control_status(&d
->wclock
));
1761 mvwprintw(statusw
, y
++, 1, "%*s: %-*s", 6, tmp
, w
, t
);
1763 strncpy(tmp
, _("black"), sizeof(tmp
)-1);
1764 tmp
[0] = toupper(tmp
[0]);
1765 snprintf(t
, sizeof(t
), "%s%s",
1766 timeval_to_char(d
->bclock
.elapsed
, d
->bclock
.tc
[d
->bclock
.tcn
][1]),
1767 time_control_status(&d
->bclock
));
1768 mvwprintw(statusw
, y
++, 1, "%*s: %-*s", 6, tmp
, w
, t
);
1770 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, _("Total:"), w
,
1771 clock_to_char(d
->elapsed
.tv_sec
));
1773 // for (i = 0; i < STATUS_WIDTH; i++)
1774 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1777 status
.notify
= str_to_wchar(_("Type F1 for help"));
1779 wattron(stdscr
, CP_STATUS_NOTIFY
);
1780 for (i
= (config
.boardleft
) ? BOARD_WIDTH
: 0;
1781 i
< ((config
.boardleft
) ? COLS
: STATUS_WIDTH
); i
++)
1782 mvwprintw(stdscr
, STATUS_HEIGHT
, i
, " ");
1783 mvwprintw(stdscr
, STATUS_HEIGHT
, CENTERX(STATUS_WIDTH
, status
.notify
)
1784 + ((config
.boardleft
) ? BOARD_WIDTH
: 0),
1785 "%ls", status
.notify
);
1786 wattroff(stdscr
, CP_STATUS_NOTIFY
);
1789 wchar_t *translate_tag_name(const char *tag
)
1791 if (!strcmp (tag
, "Event"))
1792 return str_to_wchar (translatable_tag_names
[0]);
1793 else if (!strcmp (tag
, "Site"))
1794 return str_to_wchar (translatable_tag_names
[1]);
1795 else if (!strcmp (tag
, "Date"))
1796 return str_to_wchar (translatable_tag_names
[2]);
1797 else if (!strcmp (tag
, "Round"))
1798 return str_to_wchar (translatable_tag_names
[3]);
1799 else if (!strcmp (tag
, "White"))
1800 return str_to_wchar (translatable_tag_names
[4]);
1801 else if (!strcmp (tag
, "Black"))
1802 return str_to_wchar (translatable_tag_names
[5]);
1803 else if (!strcmp (tag
, "Result"))
1804 return str_to_wchar (translatable_tag_names
[6]);
1806 return str_to_wchar (tag
);
1809 void update_tag_window(TAG
**t
)
1814 for (i
= 0; t
[i
]; i
++) {
1815 wchar_t *namewc
= translate_tag_name(t
[i
]->name
);
1823 w
= TAG_WIDTH
- namel
- 4;
1825 for (i
= 0; t
[i
] && i
< TAG_HEIGHT
- 3; i
++) {
1826 wchar_t *namewc
= translate_tag_name(t
[i
]->name
);
1827 wchar_t *valuewc
= str_etc(t
[i
]->value
, w
, 0);
1829 mvwprintw(tagw
, (i
+ 2), 1, "%*ls: %-*ls", namel
, namewc
, w
, valuewc
);
1834 for (; i
< TAG_HEIGHT
- 3; i
++)
1835 mvwprintw(tagw
, (i
+ 2), 1, "%*s", namel
+ w
+ 2, " ");
1838 void append_enginebuf(GAME g
, char *line
)
1841 struct userdata_s
*d
= g
->data
;
1843 if (d
->engine
->enginebuf
)
1844 for (i
= 0; d
->engine
->enginebuf
[i
]; i
++);
1846 if (i
>= LINES
- 3) {
1847 free(d
->engine
->enginebuf
[0]);
1849 for (i
= 0; d
->engine
->enginebuf
[i
+1]; i
++)
1850 d
->engine
->enginebuf
[i
] = d
->engine
->enginebuf
[i
+1];
1852 d
->engine
->enginebuf
[i
] = strdup(line
);
1855 d
->engine
->enginebuf
= Realloc(d
->engine
->enginebuf
, (i
+ 2) * sizeof(char *));
1856 d
->engine
->enginebuf
[i
++] = strdup(line
);
1857 d
->engine
->enginebuf
[i
] = NULL
;
1861 void update_engine_window(GAME g
)
1864 struct userdata_s
*d
= g
->data
;
1866 wmove(enginew
, 0, 0);
1869 if (d
->engine
&& d
->engine
->enginebuf
) {
1870 for (i
= 0; d
->engine
->enginebuf
[i
]; i
++)
1871 mvwprintw(enginew
, i
+ 2, 1, "%s", d
->engine
->enginebuf
[i
]);
1874 window_draw_title(enginew
, _("Engine IO Window"), COLS
, CP_MESSAGE_TITLE
,
1878 void update_all(GAME g
)
1880 struct userdata_s
*d
= g
->data
;
1883 * In the middle of a macro. Don't update the screen.
1885 if (macro_match
!= -1)
1888 wmove(boardw
, ROWTOMATRIX(d
->c_row
), COLTOMATRIX(d
->c_col
));
1889 update_board_window(g
);
1890 update_status_window(g
);
1891 update_history_window(g
);
1892 update_tag_window(g
->tag
);
1893 update_engine_window(g
);
1898 static void game_next_prev(GAME g
, int n
, int count
)
1904 if (gindex
+ count
> gtotal
- 1) {
1906 gindex
= gtotal
- 1;
1914 if (gindex
- count
< 0) {
1918 gindex
= gtotal
- 1;
1927 static void delete_game(int which
)
1930 struct userdata_s
*d
;
1932 for (i
= 0; i
< gtotal
; i
++) {
1935 if (i
== w
|| TEST_FLAG(d
->flags
, CF_DELETE
)) {
1938 free_userdata_once(game
[i
]);
1941 for (n
= i
; n
+1 < gtotal
; n
++)
1942 game
[n
] = game
[n
+1];
1951 if (which
+ 1 >= gtotal
)
1952 gindex
= gtotal
- 1;
1957 gindex
= gtotal
- 1;
1960 gp
->hp
= gp
->history
;
1964 * FIXME find across multiple games.
1966 static int find_move_exp(GAME g
, regex_t r
, int which
, int count
)
1974 incr
= (which
== 0) ? -1 : 1;
1976 for (i
= g
->hindex
+ incr
- 1, found
= 0; ; i
+= incr
) {
1977 if (i
== g
->hindex
- 1)
1980 if (i
>= pgn_history_total(g
->hp
))
1983 i
= pgn_history_total(g
->hp
) - 1;
1986 ret
= regexec(&r
, g
->hp
[i
]->move
, 0, 0, 0);
1989 if (count
== ++found
) {
1994 if (ret
!= REG_NOMATCH
) {
1995 regerror(ret
, &r
, errbuf
, sizeof(errbuf
));
1996 cmessage(_("Error Matching Regular Expression"), ANY_KEY_STR
, "%s", errbuf
);
2005 static int toggle_delete_flag(int n
)
2008 struct userdata_s
*d
= game
[n
]->data
;
2010 TOGGLE_FLAG(d
->flags
, CF_DELETE
);
2013 for (i
= x
= 0; i
< gtotal
; i
++) {
2016 if (TEST_FLAG(d
->flags
, CF_DELETE
))
2021 cmessage(NULL
, ANY_KEY_STR
, "%s", _("Cannot delete last game."));
2023 CLEAR_FLAG(d
->flags
, CF_DELETE
);
2030 static int find_game_exp(char *str
, int which
, int count
)
2032 char *nstr
= NULL
, *exp
= NULL
;
2036 char buf
[255] = {0}, *tmp
;
2039 int incr
= (which
== 0) ? -(1) : 1;
2041 strncpy(buf
, str
, sizeof(buf
)-1);
2044 if (strstr(tmp
, ":") != NULL
) {
2045 nstr
= strsep(&tmp
, ":");
2047 if ((ret
= regcomp(&nexp
, nstr
,
2048 REG_ICASE
|REG_EXTENDED
|REG_NOSUB
)) != 0) {
2049 regerror(ret
, &nexp
, errbuf
, sizeof(errbuf
));
2050 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR
, "%s", errbuf
);
2058 while (*exp
&& isspace(*exp
))
2064 if ((ret
= regcomp(&vexp
, exp
, REG_EXTENDED
|REG_NOSUB
)) != 0) {
2065 regerror(ret
, &vexp
, errbuf
, sizeof(errbuf
));
2066 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR
, "%s", errbuf
);
2073 for (g
= gindex
+ incr
, found
= 0; ; g
+= incr
) {
2084 for (t
= 0; game
[g
]->tag
[t
]; t
++) {
2086 if (regexec(&nexp
, game
[g
]->tag
[t
]->name
, 0, 0, 0) == 0) {
2087 if (regexec(&vexp
, game
[g
]->tag
[t
]->value
, 0, 0, 0) == 0) {
2088 if (count
== ++found
) {
2096 if (regexec(&vexp
, game
[g
]->tag
[t
]->value
, 0, 0, 0) == 0) {
2097 if (count
== ++found
) {
2119 * Updates the notification line in the status window then refreshes the
2122 void update_status_notify(GAME g
, char *fmt
, ...)
2125 #ifdef HAVE_VASPRINTF
2132 if (status
.notify
) {
2133 free(status
.notify
);
2134 status
.notify
= NULL
;
2141 #ifdef HAVE_VASPRINTF
2142 vasprintf(&line
, fmt
, ap
);
2144 vsnprintf(line
, sizeof(line
), fmt
, ap
);
2149 free(status
.notify
);
2151 status
.notify
= str_to_wchar(line
);
2153 #ifdef HAVE_VASPRINTF
2158 int rav_next_prev(GAME g
, BOARD b
, int n
)
2162 if ((!g
->ravlevel
&& g
->hindex
&& g
->hp
[g
->hindex
- 1]->rav
== NULL
) ||
2163 (!g
->ravlevel
&& !g
->hindex
&& g
->hp
[g
->hindex
]->rav
== NULL
) ||
2164 (g
->ravlevel
&& g
->hp
[g
->hindex
]->rav
== NULL
))
2167 g
->rav
= Realloc(g
->rav
, (g
->ravlevel
+ 1) * sizeof(RAV
));
2168 g
->rav
[g
->ravlevel
].hp
= g
->hp
;
2169 g
->rav
[g
->ravlevel
].flags
= g
->flags
;
2170 g
->rav
[g
->ravlevel
].fen
= pgn_game_to_fen(g
, b
);
2171 g
->rav
[g
->ravlevel
].hindex
= g
->hindex
;
2172 g
->hp
= (!g
->ravlevel
) ? (g
->hindex
) ? g
->hp
[g
->hindex
- 1]->rav
: g
->hp
[g
->hindex
]->rav
: g
->hp
[g
->hindex
]->rav
;
2175 pgn_board_update(g
, b
, g
->hindex
+ 1);
2179 if (g
->ravlevel
- 1 < 0)
2184 pgn_board_init_fen(g
, b
, g
->rav
[g
->ravlevel
].fen
);
2185 free(g
->rav
[g
->ravlevel
].fen
);
2186 g
->hp
= g
->rav
[g
->ravlevel
].hp
;
2187 g
->flags
= g
->rav
[g
->ravlevel
].flags
;
2188 g
->hindex
= g
->rav
[g
->ravlevel
].hindex
;
2192 static void draw_window_decor()
2194 move_panel(boardp
, 0,
2195 (config
.boardleft
) ? 0 : COLS
- BOARD_WIDTH
);
2196 move_panel(historyp
, LINES
- HISTORY_HEIGHT
,
2197 (config
.boardleft
) ? (MEGA_BOARD
) ? BOARD_WIDTH
: 0 :
2198 (MEGA_BOARD
) ? 0 : COLS
- HISTORY_WIDTH
);
2199 move_panel(statusp
, 0,
2200 (config
.boardleft
) ? BOARD_WIDTH
: 0);
2201 move_panel(tagp
, STATUS_HEIGHT
+ 1,
2202 (config
.boardleft
) ? (MEGA_BOARD
) ? BOARD_WIDTH
:
2205 wbkgd(boardw
, CP_BOARD_WINDOW
);
2206 wbkgd(statusw
, CP_STATUS_WINDOW
);
2207 window_draw_title(statusw
, _("Game Status"), STATUS_WIDTH
,
2208 CP_STATUS_TITLE
, CP_STATUS_BORDER
);
2209 wbkgd(tagw
, CP_TAG_WINDOW
);
2210 window_draw_title(tagw
, _("Roster Tags"), TAG_WIDTH
, CP_TAG_TITLE
,
2212 wbkgd(historyw
, CP_HISTORY_WINDOW
);
2213 window_draw_title(historyw
, _("Move History"), HISTORY_WIDTH
,
2214 CP_HISTORY_TITLE
, CP_HISTORY_BORDER
);
2217 void do_window_resize()
2219 if (LINES
< 23 || COLS
< 74)
2222 resizeterm(LINES
, COLS
);
2224 wresize(boardw
, BOARD_HEIGHT
, BOARD_WIDTH
);
2225 wresize(historyw
, HISTORY_HEIGHT
, HISTORY_WIDTH
);
2226 wresize(statusw
, STATUS_HEIGHT
, STATUS_WIDTH
);
2227 wresize(tagw
, TAG_HEIGHT
, TAG_WIDTH
);
2235 draw_window_decor();
2237 keypad(boardw
, TRUE
);
2243 void do_global_redraw ()
2245 do_window_resize ();
2250 memset(&clock_timer
, 0, sizeof(struct itimerval
));
2251 setitimer(ITIMER_REAL
, &clock_timer
, NULL
);
2254 void start_clock(GAME g
)
2256 struct userdata_s
*d
= g
->data
;
2258 if (clock_timer
.it_interval
.tv_usec
)
2261 memset(&d
->elapsed
, 0, sizeof(struct timeval
));
2262 clock_timer
.it_value
.tv_sec
= 0;
2263 clock_timer
.it_value
.tv_usec
= 100000;
2264 clock_timer
.it_interval
.tv_sec
= 0;
2265 clock_timer
.it_interval
.tv_usec
= 100000;
2266 setitimer(ITIMER_REAL
, &clock_timer
, NULL
);
2269 static void update_clocks()
2272 struct userdata_s
*d
;
2273 struct itimerval it
;
2276 getitimer(ITIMER_REAL
, &it
);
2278 for (i
= 0; i
< gtotal
; i
++) {
2281 if (d
&& d
->mode
== MODE_PLAY
) {
2282 if (d
->paused
== 1 || TEST_FLAG(d
->flags
, CF_NEW
))
2284 else if (d
->paused
== -1) {
2285 if (game
[i
]->side
== game
[i
]->turn
) {
2291 update_clock(game
[i
], it
);
2299 update_status_window(gp
);
2305 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2307 static int parse_clock_time(char **str
)
2362 static int parse_clock_input(struct clock_s
*clk
, char *str
, int *incr
)
2384 memset(clk
, 0, sizeof(struct clock_s
));
2388 if (strncasecmp(p
, "SD", 2) == 0) {
2394 n
= parse_clock_time(&p
);
2408 /* Sudden death without a previous time control. */
2414 n
= parse_clock_time(&p
);
2420 message(ERROR_STR
, ANY_KEY_STR
, "%s (%i)", _("Maximum number of time controls reached"), MAX_TC
);
2425 clk
->tc
[tc
++][1] = n
;
2440 clk
->tc
[clk
->tcn
][1] = (n
<= clk
->elapsed
.tv_sec
) ? clk
->elapsed
.tv_sec
+ n
: n
;
2448 n
= parse_clock_time(&p
);
2468 static int parse_which_clock(struct clock_s
*clk
, char *str
)
2473 memcpy(&tmp
, clk
, sizeof(struct clock_s
));
2475 if (parse_clock_input(&tmp
, str
, &incr
)) {
2476 cmessage(ERROR_STR
, ANY_KEY_STR
, _("Invalid clock specification"));
2480 memcpy(clk
, &tmp
, sizeof(struct clock_s
));
2481 clk
->tc
[clk
->tcn
][1] += incr
;
2485 void do_clock_input_finalize(WIN
*win
)
2487 struct userdata_s
*d
= gp
->data
;
2488 struct input_data_s
*in
= win
->data
;
2498 if (tolower(*p
) == 'w') {
2501 if (parse_which_clock(&d
->wclock
, p
))
2504 else if (tolower(*p
) == 'b') {
2507 if (parse_which_clock(&d
->bclock
, p
))
2511 if (parse_which_clock(&d
->wclock
, p
))
2514 if (parse_which_clock(&d
->bclock
, p
))
2518 if (!d
->wclock
.tc
[0][1] && !d
->bclock
.tc
[0][1])
2519 CLEAR_FLAG(d
->flags
, CF_CLOCK
);
2521 SET_FLAG(d
->flags
, CF_CLOCK
);
2528 void do_engine_command_finalize(WIN
*win
)
2530 struct userdata_s
*d
= gp
->data
;
2531 struct input_data_s
*in
= win
->data
;
2542 x
= d
->engine
->status
;
2543 send_to_engine(gp
, -1, "%s\n", in
->str
);
2544 d
->engine
->status
= x
;
2551 void do_board_details()
2553 config
.details
= (config
.details
) ? 0 : 1;
2556 void do_toggle_strict_castling()
2560 pgn_config_get(PGN_STRICT_CASTLING
, &n
);
2563 pgn_config_set(PGN_STRICT_CASTLING
, 1);
2565 pgn_config_set(PGN_STRICT_CASTLING
, 0);
2568 void do_play_set_clock()
2570 struct input_data_s
*in
;
2572 in
= Calloc(1, sizeof(struct input_data_s
));
2573 in
->efunc
= do_clock_input_finalize
;
2574 construct_input(_("Set Clock"), NULL
, 1, 1,
2575 _ ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n" \
2576 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2577 NULL
, NULL
, 0, in
, INPUT_HIST_CLOCK
, -1);
2580 void do_play_toggle_human()
2582 struct userdata_s
*d
= gp
->data
;
2584 TOGGLE_FLAG(d
->flags
, CF_HUMAN
);
2586 if (!TEST_FLAG(d
->flags
, CF_HUMAN
) && pgn_history_total(gp
->hp
)) {
2587 if (init_chess_engine(gp
))
2591 CLEAR_FLAG(d
->flags
, CF_ENGINE_LOOP
);
2594 d
->engine
->status
= ENGINE_READY
;
2597 void do_play_toggle_engine()
2599 struct userdata_s
*d
= gp
->data
;
2601 TOGGLE_FLAG(d
->flags
, CF_ENGINE_LOOP
);
2602 CLEAR_FLAG(d
->flags
, CF_HUMAN
);
2604 if (d
->engine
&& TEST_FLAG(d
->flags
, CF_ENGINE_LOOP
)) {
2605 char *fen
= pgn_game_to_fen (gp
, d
->b
);
2607 pgn_board_update(gp
, d
->b
,
2608 pgn_history_total(gp
->hp
));
2609 add_engine_command(gp
, ENGINE_READY
, "setboard %s\n", fen
);
2615 * This will send a command to the engine skipping the command queue.
2617 void do_play_send_command()
2619 struct userdata_s
*d
= gp
->data
;
2620 struct input_data_s
*in
;
2622 if (!d
->engine
|| d
->engine
->status
== ENGINE_OFFLINE
) {
2623 if (init_chess_engine(gp
))
2627 in
= Calloc(1, sizeof(struct input_data_s
));
2628 in
->efunc
= do_engine_command_finalize
;
2629 construct_input(_("Engine Command"), NULL
, 1, 1, NULL
, NULL
, NULL
, 0, in
, INPUT_HIST_ENGINE
, -1);
2632 void do_play_switch_turn()
2634 struct userdata_s *d = gp->data;
2636 pgn_switch_side(gp);
2637 pgn_switch_turn(gp);
2639 if (!TEST_FLAG(d->flags, CF_HUMAN))
2640 add_engine_command(gp, -1,
2641 (gp->side == WHITE) ? "white\n" : "black\n");
2643 update_status_window(gp);
2646 void do_play_toggle_eh_mode()
2648 struct userdata_s
*d
= gp
->data
;
2650 if (!TEST_FLAG(d
->flags
, CF_HUMAN
)) {
2652 pgn_switch_side(gp
, TRUE
);
2653 d
->play_mode
= (d
->play_mode
) ? PLAY_HE
: PLAY_EH
;
2654 if (gp
->side
== BLACK
)
2655 update_status_notify(gp
, _("Press 'g' to start the game"));
2657 rotate
= (rotate
) ? FALSE
: TRUE
;
2660 message(NULL
, ANY_KEY_STR
,
2661 _("You may only switch sides at the start of the \n"
2662 "game. Press ^K or ^N to begin a new game."));
2668 struct userdata_s
*d
= gp
->data
;
2670 if (!pgn_history_total(gp
->hp
))
2674 if (gp
->hindex
- keycount
< 0)
2678 gp
->hindex
-= (keycount
* 2) -1;
2680 gp
->hindex
-= keycount
* 2;
2684 if (gp
->hindex
- 2 < 0)
2694 pgn_history_free(gp
->hp
, gp
->hindex
);
2695 gp
->hindex
= pgn_history_total(gp
->hp
);
2696 pgn_board_update(gp
, d
->b
, gp
->hindex
);
2698 if (d
->engine
&& d
->engine
->status
== ENGINE_READY
) {
2699 char *fen
= pgn_game_to_fen(gp
, d
->b
);
2701 add_engine_command(gp
, ENGINE_READY
, "setboard %s\n", fen
);
2703 d
->engine
->status
= ENGINE_READY
;
2706 update_history_window(gp
);
2709 pgn_switch_side(gp
, FALSE
);
2714 void do_play_toggle_pause()
2716 struct userdata_s
*d
= gp
->data
;
2718 if (!TEST_FLAG(d
->flags
, CF_HUMAN
) && gp
->turn
!=
2724 d
->paused
= (d
->paused
) ? 0 : 1;
2729 struct userdata_s
*d
= gp
->data
;
2731 if (TEST_FLAG(d
->flags
, CF_HUMAN
))
2734 if (fm_loaded_file
&& gp
->side
!= gp
->turn
) {
2735 pgn_switch_side(gp
, FALSE
);
2736 add_engine_command(gp
, ENGINE_THINKING
, "black\n");
2739 add_engine_command(gp
, ENGINE_THINKING
, "go\n");
2741 // Completa la función para que permita seguir jugando al usarla.
2742 // Complete the function to allow continue playing when using.
2743 if (gp
->side
== gp
->turn
)
2744 pgn_switch_side(gp
, FALSE
);
2749 void do_play_config_command()
2754 for (x
= 0; config
.keys
[x
]; x
++) {
2755 if (config
.keys
[x
]->c
== input_c
) {
2756 switch (config
.keys
[x
]->type
) {
2758 add_engine_command(gp
, -1, "%ls\n",
2759 config
.keys
[x
]->str
);
2765 add_engine_command(gp
, -1,
2766 "%ls %i\n", config
.keys
[x
]->str
, keycount
);
2773 for (w
= 0; w
< keycount
; w
++)
2774 add_engine_command(gp
, -1,
2775 "%ls\n", config
.keys
[x
]->str
);
2783 update_status_notify(gp
, NULL
);
2786 void do_play_cancel_selected()
2788 struct userdata_s
*d
= gp
->data
;
2790 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
2792 pgn_reset_valid_moves(d
->b
);
2793 update_status_notify(gp
, NULL
);
2796 void do_play_commit()
2798 struct userdata_s
*d
= gp
->data
;
2800 pushkey
= keycount
= 0;
2801 update_status_notify(gp
, NULL
);
2803 if (!TEST_FLAG(d
->flags
, CF_HUMAN
) &&
2804 (!d
->engine
|| d
->engine
->status
== ENGINE_THINKING
))
2810 d
->sp
.row
= d
->c_row
;
2811 d
->sp
.col
= d
->c_col
;
2814 rotate_position(SP_POSITION
);
2815 rotate_position(SPS_POSITION
);
2820 // Completa la función para que permita seguir jugando cuando se carga un
2821 // archivo pgn (con juego no terminado) que inicie con turno del lado
2823 // Complete the function to allow continue playing when loading a file
2824 // pgn (with unfinished game) you start to turn black side.
2825 if (gp
->side
!= gp
->turn
)
2826 pgn_switch_side(gp
, FALSE
);
2828 if (rotate
&& d
->sp
.icon
)
2829 rotate_position(SPS_POSITION
);
2831 // Envia comando 'go' a Polyglot en el primer movimiento debido a que
2832 // polyglot no envia el movimiento y cboard se queda esperando.
2833 // Send command 'go' to the first movement Polyglot because cboard
2834 // waits to send polyglot movement and this does not make.
2835 if (config
.fmpolyglot
&&
2836 ((gp
->side
== WHITE
&& !gp
->hindex
) || fm_loaded_file
))
2837 add_engine_command(gp
, ENGINE_THINKING
, "go\n");
2840 fm_loaded_file
= FALSE
;
2843 void do_play_select()
2845 struct userdata_s
*d
= gp
->data
;
2847 if (!TEST_FLAG(d
->flags
, CF_HUMAN
) && (!d
->engine
||
2848 d
->engine
->status
== ENGINE_OFFLINE
)) {
2849 if (init_chess_engine(gp
))
2853 if (d
->engine
&& d
->engine
->status
== ENGINE_THINKING
)
2857 do_play_cancel_selected ();
2860 rotate_position(CURSOR_POSITION
);
2862 d
->sp
.icon
= d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
;
2864 if (pgn_piece_to_int(d
->sp
.icon
) == OPEN_SQUARE
) {
2869 if (((islower(d
->sp
.icon
) && gp
->turn
!= BLACK
)
2870 || (isupper(d
->sp
.icon
) && gp
->turn
!= WHITE
))) {
2872 char *str
= Malloc (512);
2874 for (k
= play_keys
; *k
; k
++) {
2875 if ((*k
)->f
== do_play_toggle_eh_mode
)
2879 snprintf (str
, 512, _("It is not your turn to move. You may switch playing sides by pressing \"%lc\"."),
2880 *k
? (*k
)->c
: '?');
2881 message(NULL
, ANY_KEY_STR
, "%s", str
);
2886 if (pgn_history_total(gp
->hp
)) {
2887 message(NULL
, ANY_KEY_STR
, "%s", _("It is not your turn to move. You can switch sides "));
2892 if (pgn_tag_find(gp
->tag
, "FEN") != E_PGN_ERR
)
2895 add_engine_command(gp
, ENGINE_READY
, "black\n");
2896 pgn_switch_turn(gp
);
2898 if (gp
->side
!= BLACK
)
2899 pgn_switch_side(gp
);
2904 d
->sp
.srow
= d
->c_row
;
2905 d
->sp
.scol
= d
->c_col
;
2907 if (config
.validmoves
)
2908 pgn_find_valid_moves(gp
, d
->b
, d
->sp
.scol
, d
->sp
.srow
);
2911 rotate_position(CURSOR_POSITION
);
2912 rotate_position(SPS_POSITION
);
2915 CLEAR_FLAG(d
->flags
, CF_NEW
);
2919 /* FIXME: keys with the same function should comma deliminated. */
2920 static wchar_t *build_help(struct key_s
**keys
)
2922 int i
, nlen
= 1, len
, t
, n
;
2923 wchar_t *buf
= NULL
, *wc
= NULL
;
2929 for (i
= len
= t
= 0; keys
[i
]; i
++) {
2934 if (wcslen(keys
[i
]->key
) > nlen
) {
2935 nlen
= wcslen(keys
[i
]->key
);
2943 if (wcslen(keys
[i
]->d
) > len
)
2944 len
= wcslen(keys
[i
]->d
);
2952 buf
= Malloc(t
*sizeof(wchar_t));
2955 for (i
= 0; keys
[i
]; i
++) {
2960 n
= wcslen(keys
[i
]->key
);
2970 wcscat(buf
, keys
[i
]->key
);
2971 p
= buf
+ wcslen(buf
);
2982 wcscat(buf
, keys
[i
]->d
);
2985 wc
= str_to_wchar ("*");
2990 wc
= str_to_wchar ("\n");
2993 p
= buf
+ wcslen(buf
);
2999 void do_more_help(WIN
*);
3000 void do_main_help(WIN
*win
)
3006 buf
= build_help(play_keys
);
3007 construct_message(_("Play Mode Keys (* = can take a repeat count)"), ANY_KEY_SCROLL_STR
, 0, 0,
3008 NULL
, NULL
, buf
, do_more_help
, 0, 1, "%ls", buf
);
3011 buf
= build_help(history_keys
);
3012 construct_message(_("History Mode Keys (* = can take a repeat count)"), ANY_KEY_SCROLL_STR
, 0, 0,
3013 NULL
, NULL
, buf
, do_more_help
, 0, 1, "%ls", buf
);
3016 buf
= build_help(edit_keys
);
3017 construct_message(_("Edit Mode Keys (* = can take a repeat count)"), ANY_KEY_SCROLL_STR
, 0, 0,
3018 NULL
, NULL
, buf
, do_more_help
, 0, 1, "%ls", buf
);
3021 buf
= build_help(global_keys
);
3022 construct_message(_("Global Game Keys (* = can take a repeat count)"), ANY_KEY_SCROLL_STR
, 0, 0,
3023 NULL
, NULL
, buf
, do_more_help
, 0, 1, "%ls", buf
);
3030 void do_more_help(WIN
*win
)
3032 if (win
->c
== KEY_F(1) || win
->c
== CTRL_KEY('g'))
3033 construct_message(_("Command Key Index"),
3034 _ ("p/h/e/g or any other key to quit"), 0, 0,
3035 NULL
, NULL
, NULL
, do_main_help
, 0, 0, "%s",
3037 "p - play mode keys\n"
3038 "h - history mode keys\n"
3039 "e - board edit mode keys\n"
3040 "g - global game keys"
3046 wchar_t *buf
= build_help(play_keys
);
3048 construct_message(_("Play Mode Keys (* = can take a repeat count)"),
3049 ANY_KEY_STR
, 0, 0, NULL
, NULL
,
3050 buf
, do_more_help
, 0, 1, "%ls", buf
);
3053 void do_play_history_mode()
3055 struct userdata_s
*d
= gp
->data
;
3057 if (!pgn_history_total(gp
->hp
) ||
3058 (d
->engine
&& d
->engine
->status
== ENGINE_THINKING
))
3061 d
->mode
= MODE_HISTORY
;
3062 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
3065 void do_play_edit_mode()
3067 struct userdata_s
*d
= gp
->data
;
3069 if (pgn_history_total(gp
->hp
))
3072 pgn_board_init_fen(gp
, d
->b
, NULL
);
3074 d
->mode
= MODE_EDIT
;
3077 void do_edit_insert_finalize(WIN
*win
)
3079 struct userdata_s
*d
= win
->data
;
3081 if (pgn_piece_to_int(win
->c
) == -1)
3084 d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
= win
->c
;
3087 void do_edit_select()
3089 struct userdata_s
*d
= gp
->data
;
3094 d
->sp
.icon
= d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
;
3096 if (pgn_piece_to_int(d
->sp
.icon
) == OPEN_SQUARE
) {
3101 d
->sp
.srow
= d
->c_row
;
3102 d
->sp
.scol
= d
->c_col
;
3105 void do_edit_commit()
3108 struct userdata_s
*d
= gp
->data
;
3110 pushkey
= keycount
= 0;
3111 update_status_notify(gp
, NULL
);
3116 d
->sp
.row
= d
->c_row
;
3117 d
->sp
.col
= d
->c_col
;
3118 p
= d
->b
[RANKTOBOARD(d
->sp
.srow
)][FILETOBOARD(d
->sp
.scol
)].icon
;
3119 d
->b
[RANKTOBOARD(d
->sp
.row
)][FILETOBOARD(d
->sp
.col
)].icon
= p
;
3120 d
->b
[RANKTOBOARD(d
->sp
.srow
)][FILETOBOARD(d
->sp
.scol
)].icon
=
3121 pgn_int_to_piece(gp
->turn
, OPEN_SQUARE
);
3122 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
3125 void do_edit_delete()
3127 struct userdata_s
*d
= gp
->data
;
3130 d
->b
[RANKTOBOARD(d
->sp
.srow
)][FILETOBOARD(d
->sp
.scol
)].icon
=
3131 pgn_int_to_piece(gp
->turn
, OPEN_SQUARE
);
3133 d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
=
3134 pgn_int_to_piece(gp
->turn
, OPEN_SQUARE
);
3136 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
3139 void do_edit_cancel_selected()
3141 struct userdata_s
*d
= gp
->data
;
3143 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
3145 update_status_notify(gp
, NULL
);
3148 void do_edit_switch_turn()
3150 pgn_switch_turn(gp
);
3153 void do_edit_toggle_castle()
3155 struct userdata_s
*d
= gp
->data
;
3157 castling_state(gp
, d
->b
, RANKTOBOARD(d
->c_row
),
3158 FILETOBOARD(d
->c_col
),
3159 d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
, 1);
3162 void do_edit_insert()
3164 struct userdata_s
*d
= gp
->data
;
3166 construct_message(_("Insert Piece"), _("P=pawn, R=rook, N=knight, B=bishop, "), 0, 0, NULL
, NULL
,
3167 d
->b
, do_edit_insert_finalize
, 0, 0, "%s", _("Type the piece letter to insert. Lowercase "));
3170 void do_edit_enpassant()
3172 struct userdata_s
*d
= gp
->data
;
3174 if (d
->c_row
== 6 || d
->c_row
== 3) {
3175 pgn_reset_enpassant(d
->b
);
3176 d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].enpassant
= 1;
3182 wchar_t *buf
= build_help(edit_keys
);
3184 construct_message(_("Edit Mode Keys (* = can take a repeat count)"),
3185 ANY_KEY_STR
, 0, 0, NULL
, NULL
,
3186 buf
, do_more_help
, 0, 1, "%ls", buf
);
3191 struct userdata_s
*d
= gp
->data
;
3192 char *fen
= pgn_game_to_fen(gp
, d
->b
);
3195 pgn_tag_add(&gp
->tag
, "FEN", fen
);
3197 pgn_tag_add(&gp
->tag
, "SetUp", "1");
3198 pgn_tag_sort(gp
->tag
);
3199 pgn_board_update(gp
, d
->b
, gp
->hindex
);
3200 d
->mode
= MODE_PLAY
;
3203 void really_do_annotate_finalize(struct input_data_s
*in
,
3204 struct userdata_s
*d
)
3206 HISTORY
*h
= in
->data
;
3216 len
= strlen(in
->str
);
3217 h
->comment
= Realloc(h
->comment
, len
+1);
3218 strncpy(h
->comment
, in
->str
, len
);
3219 h
->comment
[len
] = 0;
3224 SET_FLAG(d
->flags
, CF_MODIFIED
);
3227 void do_annotate_finalize(WIN
*win
)
3229 struct userdata_s
*d
= gp
->data
;
3230 struct input_data_s
*in
= win
->data
;
3232 really_do_annotate_finalize(in
, d
);
3235 void do_find_move_exp_finalize(int init
, int which
)
3238 struct userdata_s
*d
= gp
->data
;
3239 static int firstrun
;
3244 if (init
|| !firstrun
) {
3248 if ((ret
= regcomp(&r
, moveexp
, REG_EXTENDED
|REG_NOSUB
)) != 0) {
3249 regerror(ret
, &r
, errbuf
, sizeof(errbuf
));
3250 cmessage(_("Error Compiling Regular Expression"), ANY_KEY_STR
, "%s", errbuf
);
3257 if ((n
= find_move_exp(gp
, r
,
3258 (which
== -1) ? 0 : 1, (keycount
) ? keycount
: 1)) == -1)
3262 pgn_board_update(gp
, d
->b
, gp
->hindex
);
3265 void do_find_move_exp(WIN
*win
)
3267 struct input_data_s
*in
= win
->data
;
3272 strncpy(moveexp
, in
->str
, sizeof(moveexp
)-1);
3273 moveexp
[sizeof(moveexp
)-1] = 0;
3274 do_find_move_exp_finalize(1, which
);
3282 void do_move_jump_finalize(int n
)
3284 struct userdata_s
*d
= gp
->data
;
3286 if (n
< 0 || n
> (pgn_history_total(gp
->hp
) / 2))
3290 update_status_notify(gp
, NULL
);
3291 gp
->hindex
= (n
) ? n
* 2 - 1 : n
* 2;
3292 pgn_board_update(gp
, d
->b
, gp
->hindex
);
3295 void do_move_jump(WIN
*win
)
3297 struct input_data_s
*in
= win
->data
;
3299 if (!in
->str
|| !isinteger(in
->str
)) {
3307 do_move_jump_finalize(atoi(in
->str
));
3312 struct history_menu_s
{
3320 void free_history_menu_data(struct history_menu_s
**h
)
3327 for (i
= 0; h
[i
]; i
++) {
3335 void get_history_data(HISTORY
**hp
, struct history_menu_s
***menu
, int m
,
3339 int t
= pgn_history_total(hp
);
3340 char buf
[MAX_SAN_MOVE_LEN
+ 4];
3342 struct history_menu_s
**hmenu
= *menu
;
3345 for (n
= 0; hmenu
[n
]; n
++);
3349 for (i
= 0; i
< t
; i
++) {
3350 hmenu
= Realloc(hmenu
, (n
+ 2) * sizeof(struct history_menu_s
*));
3351 hmenu
[n
] = Malloc(sizeof(struct history_menu_s
));
3352 snprintf(buf
, sizeof(buf
), "%c%s%s", (turn
== WHITE
) ? 'W' : 'B',
3353 hp
[i
]->move
, (hp
[i
]->comment
|| hp
[i
]->nag
[0]) ? " !" : "");
3354 hmenu
[n
]->line
= strdup(buf
);
3355 hmenu
[n
]->hindex
= i
;
3356 hmenu
[n
]->indent
= 0;
3357 hmenu
[n
]->ravlevel
= depth
;
3358 hmenu
[n
]->move
= (n
&& depth
> hmenu
[n
-1]->ravlevel
) ? m
++ : m
;
3365 get_history_data(hp
[i
]->rav
, &hmenu
, m
, turn
);
3366 for (n
= 0; hmenu
[n
]; n
++);
3374 turn
= (turn
== WHITE
) ? BLACK
: WHITE
;
3380 void history_draw_update(struct menu_input_s
*m
)
3383 struct userdata_s
*d
= g
->data
;
3385 g
->hindex
= m
->selected
+ 1;
3386 update_cursor(g
, m
->selected
);
3387 pgn_board_update(g
, d
->b
, m
->selected
+ 1);
3390 struct menu_item_s
**get_history_items(WIN
*win
)
3392 struct menu_input_s
*m
= win
->data
;
3394 struct userdata_s
*d
= g
->data
;
3395 struct history_menu_s
**hm
= d
->data
;
3396 struct menu_item_s
**items
= m
->items
;
3400 get_history_data(g
->history
, &hm
, 0,
3401 TEST_FLAG(g
->flags
, GF_BLACK_OPENING
));
3402 m
->selected
= g
->hindex
- 1;
3404 if (m
->selected
< 0)
3407 m
->draw_exit_func
= history_draw_update
;
3413 for (i
= 0; items
[i
]; i
++)
3420 for (i
= 0; hm
[i
]; i
++) {
3421 items
= Realloc(items
, (i
+2) * sizeof(struct menu_item_s
*));
3422 items
[i
] = Malloc(sizeof(struct menu_item_s
));
3423 items
[i
]->name
= hm
[i
]->line
;
3424 items
[i
]->value
= NULL
;
3425 items
[i
]->selected
= 0;
3436 void history_menu_quit(struct menu_input_s
*m
)
3441 void history_menu_exit(WIN
*win
)
3444 struct userdata_s
*d
= g
->data
;
3445 struct history_menu_s
**hm
= d
->data
;
3451 for (i
= 0; hm
[i
]; i
++) {
3461 void history_menu_next(struct menu_input_s
*m
)
3464 struct userdata_s
*d
= g
->data
;
3465 struct history_menu_s
**hm
= d
->data
;
3468 for (t
= 0; hm
[t
]; t
++);
3470 if (m
->selected
+ 1 == t
)
3473 n
= hm
[m
->selected
+ 1]->hindex
;
3480 void history_menu_prev(struct menu_input_s
*m
)
3483 struct userdata_s
*d
= g
->data
;
3484 struct history_menu_s
**hm
= d
->data
;
3487 for (t
= 0; hm
[t
]; t
++);
3489 if (m
->selected
- 1 < 0)
3492 n
= hm
[m
->selected
- 1]->hindex
;
3498 void history_menu_help(struct menu_input_s
*m
)
3500 message(_("History Menu Help"), ANY_KEY_STR
, "%s",
3502 " UP/DOWN - previous/next menu item\n"
3503 " HOME/END - first/last menu item\n"
3504 " PGDN/PGUP - next/previous page\n"
3505 " a-zA-Z0-9 - jump to item\n"
3506 " CTRL-a - annotate the selected move\n"
3507 " ENTER - view annotation\n"
3508 " CTRL-d - toggle board details\n"
3509 " ESCAPE/M - return to move history"
3513 void do_annotate_move(HISTORY
*hp
)
3516 struct input_data_s
*in
;
3518 snprintf(buf
, sizeof(buf
), "%s \"%s\"", _("Editing Annotation for"), hp
->move
);
3519 in
= Calloc(1, sizeof(struct input_data_s
));
3521 in
->efunc
= do_annotate_finalize
;
3522 construct_input(buf
, hp
->comment
, MAX_PGN_LINE_LEN
/ INPUT_WIDTH
, 0,
3523 _("Type CTRL-t to edit NAG"), edit_nag
, NULL
, CTRL_KEY('T'), in
, -1, -1);
3526 void history_menu_view_annotation(struct menu_input_s
*m
)
3531 view_annotation(g
->history
[m
->selected
]);
3534 void history_menu_annotate_finalize(WIN
*win
)
3536 struct input_data_s
*in
= win
->data
;
3537 GAME g
= in
->moredata
;
3538 struct userdata_s
*d
= g
->data
;
3539 struct history_menu_s
**hm
= d
->data
;
3541 really_do_annotate_finalize(in
, d
);
3542 free_history_menu_data(hm
);
3544 get_history_data(g
->history
, &hm
, 0, TEST_FLAG(g
->flags
, GF_BLACK_OPENING
));
3546 pushkey
= REFRESH_MENU
;
3549 void history_menu_annotate(struct menu_input_s
*m
)
3553 struct input_data_s
*in
;
3554 HISTORY
*hp
= g
->history
[m
->selected
]; // FIXME RAV
3556 snprintf(buf
, sizeof(buf
), "%s \"%s\"", _("Editing Annotation for"), hp
->move
);
3557 in
= Calloc(1, sizeof(struct input_data_s
));
3559 in
->moredata
= m
->data
;
3560 in
->efunc
= history_menu_annotate_finalize
;
3561 construct_input(buf
, hp
->comment
, MAX_PGN_LINE_LEN
/ INPUT_WIDTH
, 0,
3562 _("Type CTRL-t to edit NAG"), edit_nag
, NULL
, CTRL_KEY('T'), in
, -1, -1);
3565 void history_menu_details(struct menu_input_s
*m
)
3571 void history_menu_print(WIN
*win
)
3573 struct menu_input_s
*m
= win
->data
;
3575 struct userdata_s
*d
= g
->data
;
3576 struct history_menu_s
**hm
= d
->data
;
3577 struct history_menu_s
*h
= hm
[m
->top
];
3579 char *p
= m
->item
->name
;
3580 int line
= m
->print_line
- 2;
3582 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
3589 for (total
= 0; hm
[total
]; total
++);
3590 wattr_get(win
->w
, &attrs
, &pair
, NULL
);
3591 wattroff(win
->w
, COLOR_PAIR(pair
));
3592 mvwaddch(win
->w
, m
->print_line
, 1,
3593 *p
== 'W' ? *p
| mix_cp(CP_BOARD_WHITE
, CP_HISTORY_WINDOW
, ATTRS(CP_BOARD_WHITE
), A_FG_B_BG
) : *p
| mix_cp(CP_BOARD_BLACK
, CP_HISTORY_WINDOW
, ATTRS(CP_BOARD_BLACK
), A_FG_B_BG
));
3596 if (h
->hindex
== 0 && line
== 0)
3597 waddch(win
->w
, ACS_ULCORNER
| CP_HISTORY_MENU_LG
);
3598 else if ((!hm
[h
->hindex
+ (win
->rows
- 5) + 1] && line
== win
->rows
- 5) ||
3599 (m
->top
+ line
== total
- 1))
3600 waddch(win
->w
, ACS_LLCORNER
| CP_HISTORY_MENU_LG
);
3601 else if (hm
[m
->top
+ 1]->ravlevel
!= h
->ravlevel
|| !h
->ravlevel
)
3602 waddch(win
->w
, ACS_LTEE
| CP_HISTORY_MENU_LG
);
3604 waddch(win
->w
, ACS_VLINE
| CP_HISTORY_MENU_LG
);
3606 wattron(win
->w
, COLOR_PAIR(pair
) | attrs
);
3608 for (i
= 2; *p
; p
++, i
++)
3609 waddch(win
->w
, (*p
== '!') ? *p
| A_BOLD
: *p
);
3611 while (i
++ < win
->cols
- 2)
3612 waddch(win
->w
, ' ');
3615 void history_menu(GAME g
)
3617 struct menu_key_s
**keys
= NULL
;
3619 add_menu_key(&keys
, KEY_ESCAPE
, history_menu_quit
);
3620 add_menu_key(&keys
, 'M', history_menu_quit
);
3621 add_menu_key(&keys
, KEY_UP
, history_menu_prev
);
3622 add_menu_key(&keys
, KEY_DOWN
, history_menu_next
);
3623 add_menu_key(&keys
, KEY_F(1), history_menu_help
);
3624 add_menu_key(&keys
, CTRL_KEY('a'), history_menu_annotate
);
3625 add_menu_key(&keys
, CTRL_KEY('d'), history_menu_details
);
3626 add_menu_key(&keys
, '\n', history_menu_view_annotation
);
3627 construct_menu(MEGA_BOARD
? LINES
- HISTORY_HEIGHT_MB
: LINES
,
3628 TAG_WIDTH
, 0, config
.boardleft
? BOARD_WIDTH
: 0,
3629 _("Move History Tree"), 1, get_history_items
, keys
, g
,
3630 history_menu_print
, history_menu_exit
);
3633 void do_history_menu()
3638 void do_history_half_move_toggle()
3640 movestep
= (movestep
== 1) ? 2 : 1;
3641 update_history_window(gp
);
3644 void do_history_rotate_board()
3646 rotate
= (rotate
) ? FALSE
: TRUE
;
3649 void do_history_jump_next()
3651 struct userdata_s
*d
= gp
->data
;
3653 pgn_history_next(gp
, d
->b
, (keycount
> 0) ?
3654 config
.jumpcount
* keycount
* movestep
:
3655 config
.jumpcount
* movestep
);
3658 void do_history_jump_prev()
3660 struct userdata_s
*d
= gp
->data
;
3662 pgn_history_prev(gp
, d
->b
, (keycount
) ?
3663 config
.jumpcount
* keycount
* movestep
:
3664 config
.jumpcount
* movestep
);
3667 void do_history_prev()
3669 struct userdata_s
*d
= gp
->data
;
3671 pgn_history_prev(gp
, d
->b
,
3672 (keycount
) ? keycount
* movestep
: movestep
);
3675 void do_history_next()
3677 struct userdata_s
*d
= gp
->data
;
3679 pgn_history_next(gp
, d
->b
, (keycount
) ?
3680 keycount
* movestep
: movestep
);
3683 void do_history_mode_finalize(struct userdata_s
*d
)
3686 d
->mode
= MODE_PLAY
;
3689 void do_history_mode_confirm(WIN
*win
)
3691 struct userdata_s
*d
= gp
->data
;
3692 wchar_t str
[] = { win
->c
, 0 };
3694 if (!wcscmp (str
, resume_wchar
)) {
3695 pgn_history_free(gp
->hp
, gp
->hindex
);
3696 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
3701 if (pgn_history_rav_new(gp
, d
->b
,
3702 gp
->hindex
) != E_PGN_OK
)
3710 if (!TEST_FLAG(d
->flags
, CF_HUMAN
)) {
3711 char *fen
= pgn_game_to_fen(gp
, d
->b
);
3713 add_engine_command(gp
, ENGINE_READY
, "setboard %s\n", fen
);
3717 do_history_mode_finalize(d
);
3720 void do_history_toggle()
3722 struct userdata_s
*d
= gp
->data
;
3724 // FIXME Resuming from previous history could append to a RAV.
3725 if (gp
->hindex
!= pgn_history_total(gp
->hp
)) {
3727 construct_message(NULL
, _ ("What would you like to do?"), 0, 1,
3728 NULL
, NULL
, NULL
, do_history_mode_confirm
, 0, 0,
3729 _("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."),
3734 if (TEST_FLAG(gp
->flags
, GF_GAMEOVER
))
3738 if (gp
->side
!= gp
->turn
) {
3739 d
->play_mode
= PLAY_EH
;
3742 d
->play_mode
= PLAY_HE
;
3746 do_history_mode_finalize(d
);
3749 void do_history_annotate()
3753 if (n
&& gp
->hp
[n
- 1]->move
)
3758 do_annotate_move(gp
->hp
[n
]);
3761 void do_history_help()
3763 wchar_t *buf
= build_help(history_keys
);
3765 construct_message(_("History Mode Keys (* = can take a repeat count)"),
3766 ANY_KEY_STR
, 0, 0, NULL
, NULL
,
3767 buf
, do_more_help
, 0, 1, "%ls", buf
);
3770 void do_history_find(int which
)
3772 struct input_data_s
*in
;
3775 if (pgn_history_total(gp
->hp
) < 2)
3778 in
= Calloc(1, sizeof(struct input_data_s
));
3779 p
= Malloc(sizeof(int));
3782 in
->efunc
= do_find_move_exp
;
3784 if (!*moveexp
|| which
== 0) {
3785 construct_input(_("Find Move Text Expression"), NULL
, 1, 0, NULL
, NULL
, NULL
,
3786 0, in
, INPUT_HIST_MOVE_EXP
, -1);
3790 do_find_move_exp_finalize(0, which
);
3793 void do_history_find_new()
3798 void do_history_find_prev()
3800 do_history_find(-1);
3803 void do_history_find_next()
3808 void do_history_rav(int which
)
3810 struct userdata_s
*d
= gp
->data
;
3812 rav_next_prev(gp
, d
->b
, which
);
3815 void do_history_rav_next()
3820 void do_history_rav_prev()
3825 void do_history_jump()
3827 struct input_data_s
*in
;
3829 if (pgn_history_total(gp
->hp
) < 2)
3833 in
= Calloc(1, sizeof(struct input_data_s
));
3834 in
->efunc
= do_move_jump
;
3836 construct_input(_("Jump to Move Number"), NULL
, 1, 1, NULL
,
3837 NULL
, NULL
, 0, in
, -1, 0);
3841 do_move_jump_finalize(keycount
);
3844 static void free_userdata_once(GAME g
)
3846 struct userdata_s
*d
= g
->data
;
3854 if (d
->engine
->enginebuf
) {
3857 for (n
= 0; d
->engine
->enginebuf
[n
]; n
++)
3858 free(d
->engine
->enginebuf
[n
]);
3860 free(d
->engine
->enginebuf
);
3863 if (d
->engine
->queue
) {
3866 for (q
= d
->engine
->queue
; *q
; q
++)
3869 free(d
->engine
->queue
);
3887 static void free_userdata()
3891 for (i
= 0; i
< gtotal
; i
++) {
3892 free_userdata_once(game
[i
]);
3893 game
[i
]->data
= NULL
;
3897 void update_loading_window(int n
)
3902 loadingw
= newwin(3, COLS
/ 2, CALCPOSY(3), CALCPOSX(COLS
/ 2));
3903 loadingp
= new_panel(loadingw
);
3904 wbkgd(loadingw
, CP_MESSAGE_WINDOW
);
3907 wmove(loadingw
, 0, 0);
3908 wclrtobot(loadingw
);
3909 wattron(loadingw
, CP_MESSAGE_BORDER
);
3910 box(loadingw
, ACS_VLINE
, ACS_HLINE
);
3911 wattroff(loadingw
, CP_MESSAGE_BORDER
);
3912 mvwprintw(loadingw
, 1, CENTER_INT((COLS
/ 2), 11 +
3913 strlen(itoa(gtotal
, buf
))),
3914 _("Loading... %i%% (%i games)"), n
, gtotal
);
3919 static void init_userdata_once(GAME g
, int n
)
3921 struct userdata_s
*d
= NULL
;
3923 d
= Calloc(1, sizeof(struct userdata_s
));
3925 d
->c_row
= 2, d
->c_col
= 5;
3926 SET_FLAG(d
->flags
, CF_NEW
);
3929 if (pgn_board_init_fen(g
, d
->b
, NULL
) != E_PGN_OK
)
3930 pgn_board_init(d
->b
);
3933 void init_userdata()
3937 for (i
= 0; i
< gtotal
; i
++)
3938 init_userdata_once(game
[i
], i
);
3941 void fix_marks(int *start
, int *end
)
3945 *start
= (*start
< 0) ? 0 : *start
;
3946 *end
= (*end
< 0) ? 0 : *end
;
3948 if (*start
> *end
) {
3954 *end
= (*end
> gtotal
) ? gtotal
: *end
;
3957 void do_new_game_finalize(GAME g
)
3959 struct userdata_s
*d
= g
->data
;
3961 d
->mode
= MODE_PLAY
;
3962 update_status_notify(g
, NULL
);
3965 void do_new_game_from_scratch(WIN
*win
)
3967 wchar_t str
[] = { win
->c
, 0 };
3969 if (wcscmp (str
, yes_wchar
))
3976 add_custom_tags(&gp
->tag
);
3979 do_new_game_finalize(gp
);
3987 add_custom_tags(&gp
->tag
);
3988 init_userdata_once(gp
, gindex
);
3989 do_new_game_finalize(gp
);
3993 void do_game_delete_finalize(int n
)
3995 struct userdata_s
*d
;
3997 delete_game((!n
) ? gindex
: -1);
3999 if (d
->mode
!= MODE_EDIT
)
4000 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
4003 void do_game_delete_confirm(WIN
*win
)
4006 wchar_t str
[] = { win
->c
, 0 };
4008 if (wcscmp (str
, yes_wchar
)) {
4013 n
= (int *)win
->data
;
4014 do_game_delete_finalize(*n
);
4018 void do_game_delete()
4022 struct userdata_s
*d
;
4026 cmessage(NULL
, ANY_KEY_STR
, "%s", _("Cannot delete last game."));
4032 for (i
= n
= 0; i
< gtotal
; i
++) {
4035 if (TEST_FLAG(d
->flags
, CF_DELETE
))
4040 tmp
= _("Delete the current game?");
4043 cmessage(NULL
, ANY_KEY_STR
, "%s", _("Cannot delete last game."));
4047 tmp
= _("Delete all games marked for deletion?");
4050 if (config
.deleteprompt
) {
4051 p
= Malloc(sizeof(int));
4053 construct_message(NULL
, _("[ Yes or No ]"), 1, 1, NULL
, NULL
, p
,
4054 do_game_delete_confirm
, 0, 0, tmp
);
4058 do_game_delete_finalize(n
);
4061 void do_find_game_exp_finalize(int which
)
4063 struct userdata_s
*d
= gp
->data
;
4066 if ((n
= find_game_exp(gameexp
, (which
== -1) ? 0 : 1,
4067 (keycount
) ? keycount
: 1)) == -1) {
4068 update_status_notify(gp
, "%s", _("No matches found"));
4075 if (pgn_history_total(gp
->hp
))
4076 d
->mode
= MODE_HISTORY
;
4078 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
4081 void do_find_game_exp(WIN
*win
)
4083 struct input_data_s
*in
= win
->data
;
4088 strncpy(gameexp
, in
->str
, sizeof(gameexp
));
4089 gameexp
[sizeof(gameexp
)-1] = 0;
4094 do_find_game_exp_finalize(c
);
4102 void do_game_jump_finalize(int n
)
4104 struct userdata_s
*d
;
4106 if (--n
> gtotal
- 1 || n
< 0)
4112 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
4113 update_status_notify(gp
, NULL
);
4116 void do_game_jump(WIN
*win
)
4118 struct input_data_s
*in
= win
->data
;
4120 if (!in
->str
|| !isinteger(in
->str
)) {
4128 do_game_jump_finalize(atoi(in
->str
));
4133 void do_load_file(WIN
*win
)
4135 struct input_data_s
*in
= win
->data
;
4136 char *tmp
= in
->str
;
4137 struct userdata_s
*d
;
4138 PGN_FILE
*pgn
= NULL
;
4146 if ((tmp
= pathfix(tmp
)) == NULL
)
4149 n
= pgn_open(tmp
, "r", &pgn
);
4151 if (n
== E_PGN_ERR
) {
4152 cmessage(ERROR_STR
, ANY_KEY_STR
, "%s\n%s", tmp
, strerror(errno
));
4155 else if (n
== E_PGN_INVALID
) {
4156 cmessage(ERROR_STR
, ANY_KEY_STR
, "%s\n%s", tmp
, _("Not a regular file"));
4162 if (pgn_parse(pgn
) == E_PGN_ERR
) {
4163 del_panel(loadingp
);
4171 del_panel(loadingp
);
4176 strncpy(loadfile
, tmp
, sizeof(loadfile
));
4177 loadfile
[sizeof(loadfile
)-1] = 0;
4181 if (pgn_history_total(gp
->hp
))
4182 d
->mode
= MODE_HISTORY
;
4184 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
4186 fm_loaded_file
= TRUE
;
4197 void do_game_save(WIN
*win
)
4199 struct input_data_s
*in
= win
->data
;
4202 char *tmp
= in
->str
;
4203 char tfile
[FILENAME_MAX
];
4206 struct userdata_s
*d
;
4208 if (!tmp
|| (tmp
= pathfix(tmp
)) == NULL
)
4211 if (pgn_is_compressed(tmp
) == E_PGN_ERR
) {
4212 p
= tmp
+ strlen(tmp
) - 1;
4214 if (*p
!= 'n' || *(p
-1) != 'g' || *(p
-2) != 'p' ||
4216 snprintf(tfile
, sizeof(tfile
), "%s.pgn", tmp
);
4222 * When in edit mode, update the FEN tag.
4225 for (i
= 0; i
< gtotal
; i
++) {
4228 if (d
->mode
== MODE_EDIT
) {
4229 char *fen
= pgn_game_to_fen(game
[i
], d
->b
);
4231 pgn_tag_add(&game
[i
]->tag
, "FEN", fen
);
4239 if (d
->mode
== MODE_EDIT
) {
4240 char *fen
= pgn_game_to_fen(game
[n
], d
->b
);
4242 pgn_tag_add(&game
[n
]->tag
, "FEN", fen
);
4257 void do_get_game_save_input(int n
)
4259 struct input_data_s
*in
= Calloc(1, sizeof(struct input_data_s
));
4260 int *p
= Malloc(sizeof(int));
4262 in
->efunc
= do_game_save
;
4266 construct_input(_("Save Game Filename"), loadfile
, 1, 1, _("Type TAB for file browser"),
4267 file_browser
, NULL
, '\t', in
, INPUT_HIST_FILE
, -1);
4270 void do_game_save_multi_confirm(WIN
*win
)
4273 wchar_t str
[] = { win
->c
, 0 };
4275 if (!wcscmp (str
, current_wchar
))
4277 else if (!wcscmp (str
, all_wchar
))
4280 update_status_notify(gp
, "%s", _("Save game aborted."));
4284 do_get_game_save_input(i
);
4287 void do_global_about()
4289 cmessage(_("ABOUT"), ANY_KEY_STR
,
4290 _("%s\nUsing %s with %i colors and %i color pairs\n%s"),
4291 PACKAGE_STRING
, curses_version(), COLORS
, COLOR_PAIRS
,
4295 void global_game_next_prev(int which
)
4297 struct userdata_s
*d
;
4299 game_next_prev(gp
, (which
== 1) ? 1 : 0,
4300 (keycount
) ? keycount
: 1);
4305 markend
= markstart
+ delete_count
;
4309 markend
= markstart
- delete_count
+ 1;
4310 delete_count
= -1; // to fix gindex in the other direction
4313 fix_marks(&markstart
, &markend
);
4314 do_global_toggle_delete();
4317 if (d
->mode
== MODE_HISTORY
)
4318 pgn_board_update(gp
, d
->b
, gp
->hindex
);
4319 else if (d
->mode
== MODE_PLAY
)
4320 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
4323 void do_global_next_game()
4325 global_game_next_prev(1);
4328 void do_global_prev_game()
4330 global_game_next_prev(0);
4333 void global_find(int which
)
4335 struct input_data_s
*in
;
4341 in
= Calloc(1, sizeof(struct input_data_s
));
4342 p
= Malloc(sizeof(int));
4345 in
->efunc
= do_find_game_exp
;
4347 if (!*gameexp
|| which
== 0) {
4348 construct_input(_("Find Game by Tag Expression"), NULL
, 1, 0,
4349 _("[name expression:]value expression"), NULL
, NULL
, 0, in
,
4350 INPUT_HIST_GAME_EXP
, -1);
4354 do_find_game_exp_finalize(which
);
4357 void do_global_find_new()
4362 void do_global_find_next()
4367 void do_global_find_prev()
4372 void do_global_game_jump()
4378 struct input_data_s
*in
;
4380 in
= Calloc(1, sizeof(struct input_data_s
));
4381 in
->efunc
= do_game_jump
;
4382 construct_input(_("Jump to Game Number"), NULL
, 1, 1, NULL
, NULL
, NULL
, 0, in
,
4387 do_game_jump_finalize(keycount
);
4390 void do_global_toggle_delete()
4399 if (keycount
&& delete_count
== 0) {
4401 delete_count
= keycount
;
4402 update_status_notify(gp
, "%s (delete)", status
.notify
);
4406 if (markstart
>= 0 && markend
>= 0) {
4407 for (i
= markstart
; i
< markend
; i
++) {
4408 if (toggle_delete_flag(i
)) {
4413 gindex
= (delete_count
< 0) ? markstart
: i
- 1;
4416 if (toggle_delete_flag(gindex
))
4420 markstart
= markend
= -1;
4422 update_status_window(gp
);
4425 void do_global_delete_game()
4430 void do_global_tag_edit()
4432 struct userdata_s
*d
= gp
->data
;
4434 edit_tags(gp
, d
->b
, 1);
4437 void do_global_tag_view()
4439 struct userdata_s
*d
= gp
->data
;
4441 edit_tags(gp
, d
->b
, 0);
4444 void do_global_resume_game()
4446 struct input_data_s
*in
;
4448 in
= Calloc(1, sizeof(struct input_data_s
));
4449 in
->efunc
= do_load_file
;
4450 construct_input(_("Load Filename"), NULL
, 1, 1, _("Type TAB for file browser"), file_browser
,
4451 NULL
, '\t', in
, INPUT_HIST_FILE
, -1);
4454 void do_global_save_game()
4457 construct_message(NULL
, _("What would you like to do?"), 0, 1,
4458 NULL
, NULL
, NULL
, do_game_save_multi_confirm
, 0, 0,
4459 _("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."),
4460 current_wchar
, all_wchar
);
4464 do_get_game_save_input(-1);
4467 void do_global_new_game()
4472 void do_global_copy_game()
4476 struct userdata_s
*d
;
4478 do_global_new_game();
4480 n
= pgn_tag_total(game
[g
]->tag
);
4482 for (i
= 0; i
< n
; i
++)
4483 pgn_tag_add(&gp
->tag
, game
[g
]->tag
[i
]->name
,
4484 game
[g
]->tag
[i
]->value
);
4486 pgn_board_init_fen (gp
, d
->b
, NULL
);
4487 n
= pgn_history_total(game
[g
]->history
);
4490 for (i
= 0; i
< n
; i
++) {
4492 char *move
= strdup (game
[g
]->history
[i
]->move
);
4494 if (pgn_parse_move(gp
, d
->b
, &move
, &frfr
) != E_PGN_OK
) {
4496 SET_FLAG(gp
->flags
, GF_PERROR
);
4500 pgn_history_add(gp
, d
->b
, move
);
4503 pgn_switch_turn(gp
);
4506 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
4509 void do_global_new_all()
4511 construct_message(NULL
, _("[ Yes or No ]"), 1, 1, NULL
, NULL
, NULL
,
4512 do_new_game_from_scratch
, 0, 0, "%s", _("Really start a new game from scratch?"));
4515 void do_quit(WIN
*win
)
4517 wchar_t str
[] = { win
->c
, 0 };
4519 if (wcscmp (str
, yes_wchar
))
4525 void do_global_quit()
4527 if (config
.exitdialogbox
)
4528 construct_message(NULL
, _("[ Yes or No ]"), 1, 1, NULL
, NULL
, NULL
,
4529 do_quit
, 0, 0, _("Want to Quit?"));
4534 void do_global_toggle_engine_window()
4537 enginew
= newwin(LINES
, COLS
, 0, 0);
4538 enginep
= new_panel(enginew
);
4539 window_draw_title(enginew
, _("Engine IO Window"), COLS
, CP_MESSAGE_TITLE
,
4541 hide_panel(enginep
);
4544 if (panel_hidden(enginep
)) {
4545 update_engine_window(gp
);
4549 hide_panel(enginep
);
4553 void do_global_toggle_board_details()
4558 void do_global_toggle_strict_castling()
4560 do_toggle_strict_castling();
4563 // Global and other keys.
4564 static int globalkeys()
4566 struct userdata_s
*d
= gp
->data
;
4570 * These cannot be modified and other game mode keys cannot conflict with
4575 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
4576 markend
= markstart
= 0;
4580 update_status_notify(gp
, NULL
);
4583 if (config
.validmoves
)
4584 pgn_reset_valid_moves(d
->b
);
4591 keycount
= keycount
* 10 + i
;
4595 update_status_notify(gp
, _("Repeat %i"), keycount
);
4598 if (d
->mode
== MODE_HISTORY
)
4602 d
->c_row
+= keycount
;
4611 if (d
->mode
== MODE_HISTORY
)
4615 d
->c_row
-= keycount
;
4616 update_status_notify(gp
, NULL
);
4626 if (d
->mode
== MODE_HISTORY
)
4630 d
->c_col
-= keycount
;
4639 if (d
->mode
== MODE_HISTORY
)
4643 d
->c_col
+= keycount
;
4655 for (i
= 0; global_keys
[i
]; i
++) {
4656 if (input_c
== global_keys
[i
]->c
) {
4657 (*global_keys
[i
]->f
)();
4668 static void perl_error(const char *fmt
, ...)
4674 vasprintf(&buf
, fmt
, ap
);
4677 message(ERROR_STR
, ANY_KEY_STR
, "%s", buf
);
4681 static void do_perl_finalize(WIN
*win
)
4683 struct input_data_s
*in
= win
->data
;
4685 struct userdata_s
*d
= g
->data
;
4687 char *result
= NULL
;
4691 asprintf(&filename
, "%s/perl.pl", config
.datadir
);
4696 if (perl_init_file(filename
, perl_error
))
4699 arg
= pgn_game_to_fen(g
, d
->b
);
4701 if (perl_call_sub(trim(in
->str
), arg
, &result
))
4704 d
->perlfen
= pgn_game_to_fen(g
, d
->b
);
4705 d
->perlflags
= g
->flags
;
4707 if (pgn_board_init_fen(g
, d
->b
, result
) != E_PGN_OK
) {
4708 message(ERROR_STR
, ANY_KEY_STR
, "%s", _("FEN parse error."));
4709 pgn_board_init_fen(g
, d
->b
, d
->perlfen
);
4710 g
->flags
= d
->perlflags
;
4716 SET_FLAG(d
->flags
, CF_PERL
);
4717 n
= pgn_tag_find(g
->tag
, "FEN");
4720 d
->oldfen
= strdup(g
->tag
[n
]->value
);
4722 pgn_tag_add(&g
->tag
, "FEN", result
);
4723 update_status_notify(g
, "%s", ANY_KEY_STR
);
4734 void do_global_perl()
4736 struct input_data_s
*in
;
4738 in
= Calloc(1, sizeof(struct input_data_s
));
4740 in
->efunc
= do_perl_finalize
;
4741 construct_input(_("PERL Subroutine Filter"), NULL
, 1, 0, NULL
, NULL
, NULL
, 0, in
, INPUT_HIST_PERL
, -1);
4746 * A macro may contain a key that belongs to another macro so macro_match will
4747 * need to be updated to the new index of the matching macro.
4749 static void find_macro(struct userdata_s
*d
)
4754 * Macros can't contain macros when in a window.
4760 for (i
= 0; macros
[i
]; i
++) {
4761 if ((macros
[i
]->mode
== -1 || macros
[i
]->mode
== d
->mode
) &&
4762 input_c
== macros
[i
]->c
) {
4763 input_c
= macros
[i
]->keys
[macros
[i
]->n
++];
4765 if (!macro_depth_n
&& macro_match
> -1) {
4766 macro_depth
= realloc(macro_depth
, (macro_depth_n
+ 1) * sizeof(int));
4767 macro_depth
[macro_depth_n
++] = macro_match
;
4770 macro_depth
= realloc(macro_depth
, (macro_depth_n
+ 1) * sizeof(int));
4771 macro_depth
[macro_depth_n
++] = i
;
4779 * Resets the position in each macro to the first key.
4781 static void reset_macros()
4784 struct userdata_s
*d
= gp
->data
;
4787 if (macro_depth_n
> 0) {
4789 macro_match
= macro_depth
[macro_depth_n
];
4791 if (macros
[macro_match
]->n
>= macros
[macro_match
]->total
)
4794 input_c
= macros
[macro_match
]->keys
[macros
[macro_match
]->n
++];
4799 for (i
= 0; macros
[i
]; i
++)
4810 struct userdata_s
*d
;
4813 gindex
= gtotal
- 1;
4817 if (pgn_history_total(gp
->hp
))
4818 d
->mode
= MODE_HISTORY
;
4820 d
->mode
= MODE_PLAY
;
4821 d
->play_mode
= PLAY_HE
;
4824 if (d
->mode
== MODE_HISTORY
)
4825 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
4827 update_status_notify(gp
, "%s", _("Type F1 for help"));
4831 wtimeout(boardw
, WINDOW_TIMEOUT
);
4835 char fdbuf
[8192] = {0};
4837 struct timeval tv
= {0, 0};
4845 for (i
= 0; i
< gtotal
; i
++) {
4848 if (d
->engine
&& d
->engine
->pid
!= -1) {
4849 if (d
->engine
->fd
[ENGINE_IN_FD
] > 2) {
4850 if (d
->engine
->fd
[ENGINE_IN_FD
] > n
)
4851 n
= d
->engine
->fd
[ENGINE_IN_FD
];
4853 FD_SET(d
->engine
->fd
[ENGINE_IN_FD
], &rfds
);
4856 if (d
->engine
->fd
[ENGINE_OUT_FD
] > 2) {
4857 if (d
->engine
->fd
[ENGINE_OUT_FD
] > n
)
4858 n
= d
->engine
->fd
[ENGINE_OUT_FD
];
4860 FD_SET(d
->engine
->fd
[ENGINE_OUT_FD
], &wfds
);
4866 if ((n
= select(n
+ 1, &rfds
, &wfds
, NULL
, &tv
)) > 0) {
4867 for (i
= 0; i
< gtotal
; i
++) {
4870 if (d
->engine
&& d
->engine
->pid
!= -1) {
4871 if (FD_ISSET(d
->engine
->fd
[ENGINE_IN_FD
], &rfds
)) {
4872 len
= read(d
->engine
->fd
[ENGINE_IN_FD
], fdbuf
,
4876 if (d
->engine
->iobuf
)
4877 d
->engine
->iobuf
= Realloc(d
->engine
->iobuf
, d
->engine
->len
+ len
+ 1);
4879 d
->engine
->iobuf
= Calloc(1, len
+ 1);
4881 memcpy(&(d
->engine
->iobuf
[d
->engine
->len
]), &fdbuf
, len
);
4882 d
->engine
->len
+= len
;
4883 d
->engine
->iobuf
[d
->engine
->len
] = 0;
4886 * The fdbuf is full or no newline
4887 * was found. So we'll append the next
4888 * read() to this games buffer.
4890 if (d
->engine
->iobuf
[d
->engine
->len
- 1] != '\n')
4893 parse_engine_output(game
[i
], d
->engine
->iobuf
);
4894 free(d
->engine
->iobuf
);
4895 d
->engine
->iobuf
= NULL
;
4898 else if (len
== -1) {
4899 if (errno
!= EAGAIN
) {
4900 cmessage(ERROR_STR
, ANY_KEY_STR
, "Engine read(): %s",
4902 waitpid(d
->engine
->pid
, &n
, 0);
4910 if (FD_ISSET(d
->engine
->fd
[ENGINE_OUT_FD
], &wfds
)) {
4911 if (d
->engine
->queue
)
4912 send_engine_command(game
[i
]);
4919 cmessage(ERROR_STR
, ANY_KEY_STR
, "select(): %s", strerror(errno
));
4928 * This is needed to detect terminal resizing.
4931 if (LINES
!= LINES_OLD
|| COLS
!= COLS_OLD
) {
4938 * Finds the top level window in the window stack so we know what
4939 * window the wget_wch()'ed key belongs to.
4942 for (i
= 0; wins
[i
]; i
++);
4945 wtimeout(wp
, WINDOW_TIMEOUT
);
4954 if (macros
&& macro_match
>= 0) {
4955 if (macros
[macro_match
]->n
>= macros
[macro_match
]->total
)
4958 input_c
= macros
[macro_match
]->keys
[macros
[macro_match
]->n
++];
4963 if (wget_wch(wp
, &input_c
) == ERR
|| input_c
== KEY_RESIZE
)
4973 do_window_resize ();
4980 * Run the function associated with the window. When the
4981 * function returns 0 win->efunc is ran (if not NULL) with
4982 * win as the one and only parameter. Then the window is
4985 * The exit function may create another window which will
4986 * mess up the window stack when window_destroy() is called.
4987 * So don't destory the window until the top window is
4988 * destroyable. See window_destroy().
4990 if ((*win
->func
)(win
) == 0) {
4995 window_destroy(win
);
5003 if (!keycount
&& status
.notify
)
5004 update_status_notify(gp
, NULL
);
5007 if (TEST_FLAG(d
->flags
, CF_PERL
)) {
5008 CLEAR_FLAG(d
->flags
, CF_PERL
);
5009 pgn_board_init_fen(gp
, d
->b
, d
->perlfen
);
5010 gp
->flags
= d
->perlflags
;
5012 pgn_tag_add(&gp
->tag
, "FEN", d
->oldfen
);
5014 d
->perlfen
= d
->oldfen
= NULL
;
5020 if (macros
&& macro_match
< 0)
5023 if ((n
= globalkeys()) == 1) {
5024 if (macro_match
== -1)
5034 for (i
= 0; edit_keys
[i
]; i
++) {
5035 if (input_c
== edit_keys
[i
]->c
) {
5036 (*edit_keys
[i
]->f
)();
5042 for (i
= 0; play_keys
[i
]; i
++) {
5043 if (input_c
== play_keys
[i
]->c
) {
5044 (*play_keys
[i
]->f
)();
5049 do_play_config_command();
5052 for (i
= 0; history_keys
[i
]; i
++) {
5053 if (input_c
== history_keys
[i
]->c
) {
5054 (*history_keys
[i
]->f
)();
5065 update_status_notify(gp
, NULL
);
5074 void usage(const char *pn
, int ret
)
5076 fprintf((ret
) ? stderr
: stdout
, "%s%s",
5079 "Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
5080 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"),
5083 "Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"),
5086 " -p Load PGN file.\n"
5087 " -V Validate a game file.\n"
5088 " -S Validate and output a PGN formatted game.\n"
5089 " -R Like -S but write a reduced PGN formatted game.\n"
5090 " -t Also write custom PGN tags from config file.\n"
5091 " -E Stop processing on file parsing error (overrides config).\n"
5092 " -C Enable strict castling (overrides config).\n"
5093 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, overrides config).\n"
5094 " -v Version information.\n"
5095 " -h This help text.\n"));
5107 free(config
.engine_cmd
);
5108 free(config
.pattern
);
5109 free(config
.ccfile
);
5110 free(config
.nagfile
);
5111 free(config
.configfile
);
5114 for (i
= 0; config
.keys
[i
]; i
++) {
5115 free(config
.keys
[i
]->str
);
5116 free(config
.keys
[i
]);
5123 for (i
= 0; config
.einit
[i
]; i
++)
5124 free(config
.einit
[i
]);
5130 pgn_tag_free(config
.tag
);
5132 free(config
.datadir
);
5134 if (curses_initialized
) {
5136 del_panel(historyp
);
5157 static void signal_save_pgn(int sig
)
5161 char *p
= config
.savedirectory
? config
.savedirectory
: config
.datadir
;
5164 asprintf(&buf
, "%s/signal-%i-%li.pgn", p
, sig
, now
);
5166 if (do_game_write(buf
, "w", 0, gtotal
)) {
5167 cmessage(ERROR_STR
, ANY_KEY_STR
, "%s: %s", p
, strerror(errno
));
5168 update_status_notify(gp
, "%s", _("Save game failed."));
5175 void catch_signal(int which
)
5182 if (which
== SIGPIPE
&& quit
)
5185 if (which
== SIGPIPE
)
5186 cmessage(NULL
, ANY_KEY_STR
, "%s", _("Broken pipe. Quitting."));
5196 do_window_resize ();
5197 keypad(boardw
, TRUE
);
5203 signal_save_pgn(which
);
5210 void loading_progress(long total
, long offset
)
5212 int n
= (100 * (offset
/ 100) / (total
/ 100));
5214 if (curses_initialized
)
5215 update_loading_window(n
);
5217 fprintf(stderr
, _("Loading... %i%% (%i games)%c"), n
, gtotal
, '\r');
5222 static void set_defaults()
5224 set_config_defaults();
5226 filetype
= FILE_NONE
;
5227 pgn_config_set(PGN_PROGRESS
, 1024);
5228 pgn_config_set(PGN_PROGRESS_FUNC
, loading_progress
);
5231 int main(int argc
, char *argv
[])
5235 char buf
[FILENAME_MAX
];
5236 char datadir
[FILENAME_MAX
];
5237 int ret
= EXIT_SUCCESS
;
5238 int validate_only
= 0, validate_and_write
= 0;
5239 int write_custom_tags
= 0;
5242 int utf8_pieces
= -1;
5244 setlocale (LC_ALL
, "");
5245 bindtextdomain ("cboard", LOCALE_DIR
);
5246 textdomain ("cboard");
5249 #ifndef HAVE_PROGNAME
5250 __progname
= argv
[0];
5253 if ((config
.pwd
= getpwuid(getuid())) == NULL
)
5254 err(EXIT_FAILURE
, "getpwuid()");
5256 snprintf(datadir
, sizeof(datadir
), "%s/.cboard", config
.pwd
->pw_dir
);
5257 config
.datadir
= strdup(datadir
);
5258 snprintf(buf
, sizeof(buf
), "%s/cc.data", datadir
);
5259 config
.ccfile
= strdup(buf
);
5260 snprintf(buf
, sizeof(buf
), "%s/nag.data", datadir
);
5261 config
.nagfile
= strdup(buf
);
5262 snprintf(buf
, sizeof(buf
), "%s/config", datadir
);
5263 config
.configfile
= strdup(buf
);
5265 if (stat(datadir
, &st
) == -1) {
5266 if (errno
== ENOENT
) {
5267 if (mkdir(datadir
, 0755) == -1)
5268 err(EXIT_FAILURE
, "%s", datadir
);
5271 err(EXIT_FAILURE
, "%s", datadir
);
5276 if (!S_ISDIR(st
.st_mode
))
5277 errx(EXIT_FAILURE
, "%s: %s", datadir
, _("Not a directory."));
5282 while ((opt
= getopt(argc
, argv
, "DCEVtSRhp:vu::")) != -1) {
5284 while ((opt
= getopt(argc
, argv
, "ECVtSRhp:vu::")) != -1) {
5289 unlink("libchess.debug");
5290 pgn_config_set(PGN_DEBUG
, 1);
5294 pgn_config_set(PGN_STRICT_CASTLING
, 1);
5297 write_custom_tags
= 1;
5303 pgn_config_set(PGN_REDUCED
, 1);
5305 validate_and_write
= 1;
5310 printf("%s (%s)\n%s\n", PACKAGE_STRING
, curses_version(),
5314 filetype
= FILE_PGN
;
5315 strncpy(loadfile
, optarg
, sizeof(loadfile
));
5316 loadfile
[sizeof(loadfile
)-1] = 0;
5319 utf8_pieces
= optarg
? atoi (optarg
): 1;
5323 usage(argv
[0], EXIT_SUCCESS
);
5327 if ((validate_only
|| validate_and_write
) && !*loadfile
)
5328 usage(argv
[0], EXIT_FAILURE
);
5330 if (access(config
.configfile
, R_OK
) == 0)
5331 parse_rcfile(config
.configfile
);
5334 pgn_config_set(PGN_STOP_ON_ERROR
, 1);
5336 signal(SIGPIPE
, catch_signal
);
5337 signal(SIGCONT
, catch_signal
);
5338 signal(SIGSTOP
, catch_signal
);
5339 signal(SIGINT
, catch_signal
);
5340 signal(SIGALRM
, catch_signal
);
5341 signal(SIGTERM
, catch_signal
);
5347 if (pgn_open(loadfile
, "r", &pgn
) != E_PGN_OK
)
5348 err(EXIT_FAILURE
, "%s", loadfile
);
5350 ret
= pgn_parse(pgn
);
5354 //ret = parse_fen_file(loadfile);
5356 case FILE_EPD
: // Not implemented.
5359 // No file specified. Empty game.
5360 ret
= pgn_parse(NULL
);
5362 add_custom_tags(&gp
->tag
);
5366 if (validate_only
|| validate_and_write
) {
5367 if (validate_and_write
) {
5368 if (pgn_open("-", "r", &pgn
) != E_PGN_OK
)
5369 err(EXIT_FAILURE
, "pgn_open()");
5371 for (i
= 0; i
< gtotal
; i
++) {
5372 if (write_custom_tags
)
5373 add_custom_tags(&game
[i
]->tag
);
5375 pgn_write(pgn
, game
[i
]);
5380 fm_loaded_file
= TRUE
;
5386 else if (ret
== E_PGN_ERR
)
5389 if (utf8_pieces
!= -1)
5390 config
.utf8_pieces
= utf8_pieces
;
5392 init_wchar_pieces ();
5393 yes_wchar
= str_to_wchar (_("y"));
5394 all_wchar
= str_to_wchar (_("a"));
5395 overwrite_wchar
= str_to_wchar (_("o"));
5396 resume_wchar
= str_to_wchar (_("r"));
5397 current_wchar
= str_to_wchar (_("c"));
5398 append_wchar
= str_to_wchar (_("a"));
5399 translatable_tag_names
[0] = _("Event");
5400 translatable_tag_names
[1] = _("Site");
5401 translatable_tag_names
[2] = _("Date");
5402 translatable_tag_names
[3] = _("Round");
5403 translatable_tag_names
[4] = _("White");
5404 translatable_tag_names
[5] = _("Black");
5405 translatable_tag_names
[6] = _("Result");
5409 * This fixes window resizing in an xterm.
5411 if (getenv("DISPLAY") != NULL
) {
5416 if (initscr() == NULL
)
5417 errx(EXIT_FAILURE
, "%s", _("Could not initialize curses."));
5419 curses_initialized
= 1;
5421 if (LINES
< 23 || COLS
< 74) {
5423 errx(EXIT_FAILURE
, _("Need at least an 74x23 terminal."));
5429 if (has_colors() == TRUE
&& start_color() == OK
)
5432 boardw
= newwin(BOARD_HEIGHT
, BOARD_WIDTH
, 0, COLS
- BOARD_WIDTH
);
5433 boardp
= new_panel(boardw
);
5434 historyw
= newwin(HISTORY_HEIGHT
, HISTORY_WIDTH
, LINES
- HISTORY_HEIGHT
,
5435 COLS
- HISTORY_WIDTH
);
5436 historyp
= new_panel(historyw
);
5437 statusw
= newwin(STATUS_HEIGHT
, STATUS_WIDTH
, 0, 0);
5438 statusp
= new_panel(statusw
);
5439 tagw
= newwin(TAG_HEIGHT
, TAG_WIDTH
, STATUS_HEIGHT
+ 1, 0);
5440 tagp
= new_panel(tagw
);
5441 keypad(boardw
, TRUE
);
5442 // leaveok(boardw, TRUE);
5443 leaveok(tagw
, TRUE
);
5444 leaveok(statusw
, TRUE
);
5445 leaveok(historyw
, TRUE
);
5449 draw_window_decor();
5452 free (w_pawn_wchar
);
5453 free (w_rook_wchar
);
5454 free (w_bishop_wchar
);
5455 free (w_knight_wchar
);
5456 free (w_queen_wchar
);
5457 free (w_king_wchar
);
5458 free (b_pawn_wchar
);
5459 free (b_rook_wchar
);
5460 free (b_bishop_wchar
);
5461 free (b_knight_wchar
);
5462 free (b_queen_wchar
);
5463 free (b_king_wchar
);
5465 free (enpassant_wchar
);
5468 free (overwrite_wchar
);
5469 free (resume_wchar
);
5470 free (current_wchar
);
5471 free (append_wchar
);