1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2002-2024 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 CBOARD_URL "https://gitlab.com/bjk/cboard/wikis"
73 #define COPYRIGHT "Copyright (C) 2002-2024 " PACKAGE_BUGREPORT
74 #define LINE_GRAPHIC(c) ((!config.linegraphics) ? ' ' : c)
75 #define ROWTOMATRIX(r) ((8 - r) * 2 + 2 - 1)
76 #define COLTOMATRIX(c) ((c == 1) ? 1 : c * 4 - 3)
77 #define STATUS_HEIGHT 12
78 #define MEGA_BOARD (LINES >= 50 && COLS >= 144)
79 #define BOARD_HEIGHT_MB 50
80 #define BOARD_WIDTH_MB 98
81 #define STATUS_WIDTH_MB (COLS - BOARD_WIDTH_MB)
82 #define TAG_HEIGHT_MB 31
83 #define TAG_WIDTH_MB (COLS - BOARD_WIDTH_MB)
84 #define HISTORY_HEIGHT_MB (LINES - (STATUS_HEIGHT + TAG_HEIGHT_MB + 1))
85 #define HISTORY_WIDTH_MB (COLS - BOARD_WIDTH_MB)
86 #define BIG_BOARD (LINES >= 40 && COLS >= 112)
87 #define BOARD_HEIGHT ((MEGA_BOARD) ? BOARD_HEIGHT_MB : (BIG_BOARD) ? 34 : 18)
88 #define BOARD_WIDTH ((MEGA_BOARD) ? BOARD_WIDTH_MB : (BIG_BOARD) ? 66 : 34)
89 #define STATUS_WIDTH ((MEGA_BOARD) ? STATUS_WIDTH_MB : COLS - BOARD_WIDTH)
90 #define TAG_HEIGHT ((MEGA_BOARD) ? TAG_HEIGHT_MB : LINES - STATUS_HEIGHT - 1)
91 #define TAG_WIDTH ((MEGA_BOARD) ? TAG_WIDTH_MB : COLS - BOARD_WIDTH)
92 #define HISTORY_HEIGHT ((MEGA_BOARD) ? HISTORY_HEIGHT_MB : LINES - BOARD_HEIGHT)
93 #define HISTORY_WIDTH ((MEGA_BOARD) ? HISTORY_WIDTH_MB : COLS - STATUS_WIDTH)
94 #define MAX_VALUE_WIDTH (COLS - 8)
101 static WINDOW
*boardw
;
102 static PANEL
*boardp
;
105 static WINDOW
*statusw
;
106 static PANEL
*statusp
;
107 static WINDOW
*historyw
;
108 static PANEL
*historyp
;
109 static WINDOW
*loadingw
;
110 static PANEL
*loadingp
;
111 static WINDOW
*enginew
;
112 static PANEL
*enginep
;
114 static char gameexp
[255];
115 static char moveexp
[255];
116 static struct itimerval clock_timer
;
117 static int delete_count
= 0;
118 static int markstart
= -1, markend
= -1;
120 static char loadfile
[FILENAME_MAX
];
122 static wint_t input_c
;
124 // Loaded filename from the command line or from the file input dialog.
128 FILE_NONE
, FILE_PGN
, FILE_FEN
, FILE_EPD
132 static int nag_total
;
133 static int macro_match
;
135 // Primer movimiento de juego cargado
136 // First move loaded game
137 static char fm_loaded_file
= FALSE
;
139 static int COLS_OLD
, LINES_OLD
;
144 wchar_t *notify
; // The status window notification line buffer.
147 static int curses_initialized
;
149 // When in history mode a full step is to the next move of the same playing
150 // side. Half stepping is alternating sides.
153 static wchar_t *w_pawn_wchar
;
154 static wchar_t *w_rook_wchar
;
155 static wchar_t *w_bishop_wchar
;
156 static wchar_t *w_knight_wchar
;
157 static wchar_t *w_queen_wchar
;
158 static wchar_t *w_king_wchar
;
159 static wchar_t *b_pawn_wchar
;
160 static wchar_t *b_rook_wchar
;
161 static wchar_t *b_bishop_wchar
;
162 static wchar_t *b_knight_wchar
;
163 static wchar_t *b_queen_wchar
;
164 static wchar_t *b_king_wchar
;
165 static wchar_t *empty_wchar
;
166 static wchar_t *enpassant_wchar
;
168 static wchar_t *yes_wchar
;
169 static wchar_t *all_wchar
; // do_save_game_overwrite_confirm()
170 static wchar_t *overwrite_wchar
; // do_save_game_overwrite_confirm()
171 static wchar_t *resume_wchar
; // do_history_mode_confirm()
172 static wchar_t *current_wchar
; // do_game_save_multi_confirm()
173 static wchar_t *append_wchar
; // save_pgn()
175 static const char piece_chars
[] = "PpRrNnBbQqKkxx";
176 static char *translatable_tag_names
[7];
177 static const char *f_pieces
[] = {
201 static const bool cb
[8][8] = {
202 {1, 0, 1, 0, 1, 0, 1, 0},
203 {0, 1, 0, 1, 0, 1, 0, 1},
204 {1, 0, 1, 0, 1, 0, 1, 0},
205 {0, 1, 0, 1, 0, 1, 0, 1},
206 {1, 0, 1, 0, 1, 0, 1, 0},
207 {0, 1, 0, 1, 0, 1, 0, 1},
208 {1, 0, 1, 0, 1, 0, 1, 0},
209 {0, 1, 0, 1, 0, 1, 0, 1}
212 static void free_userdata_once (GAME g
);
213 static void do_more_help (WIN
*);
214 static void do_play_help ();
215 static void do_history_help ();
216 static void do_edit_help ();
221 coordofmove (GAME g
, char *move
, char *prow
, char *pcol
)
223 char l
= strlen (move
);
227 *prow
= (g
->turn
== WHITE
) ? 8 : 1;
228 *pcol
= (l
<= 4) ? 7 : 3;
234 while (!isdigit (*move
))
237 *prow
= RANKTOINT (*move
--);
238 *pcol
= FILETOINT (*move
);
241 #define INV_INT(x) (9 - x)
242 #define INV_INT0(x) (7 - x)
244 // Posición por rotación de tablero.
245 // Rotation board position.
247 rotate_position (char *prow
, char *pcol
)
249 *prow
= INV_INT (*prow
);
250 *pcol
= INV_INT (*pcol
);
254 update_cursor (GAME g
, int idx
)
256 int t
= pgn_history_total (g
->hp
);
257 struct userdata_s
*d
= g
->data
;
260 * If not deincremented then r and c would be the next move.
264 if (idx
> t
|| idx
< 0 || !t
|| !g
->hp
[idx
]->move
)
265 d
->c_row
= 2, d
->c_col
= 5;
267 coordofmove (g
, g
->hp
[idx
]->move
, &d
->c_row
, &d
->c_col
);
269 if (d
->mode
== MODE_HISTORY
&& d
->rotate
)
270 rotate_position (&d
->c_row
, &d
->c_col
);
280 if ((fp
= fopen (config
.nagfile
, "r")) == NULL
)
282 cmessage (ERROR_STR
, ANY_KEY_STR
, "%s: %s", config
.nagfile
,
287 nags
= Realloc (nags
, (i
+ 2) * sizeof (char *));
288 nags
[i
++] = strdup (_("none"));
293 if (fscanf (fp
, " %[^\n] ", line
) == 1)
295 nags
= Realloc (nags
, (i
+ 2) * sizeof (char *));
296 nags
[i
++] = strdup (line
);
307 edit_nag_toggle_item (struct menu_input_s
*m
)
309 struct input_s
*in
= m
->data
;
310 struct input_data_s
*id
= in
->data
;
311 HISTORY
*h
= id
->data
;
314 if (m
->selected
== 0)
316 for (i
= 0; i
< MAX_PGN_NAG
; i
++)
319 for (i
= 0; m
->items
[i
]; i
++)
320 m
->items
[i
]->selected
= 0;
325 for (i
= 0; i
< MAX_PGN_NAG
; i
++)
327 if (h
->nag
[i
] == m
->selected
)
328 h
->nag
[i
] = m
->selected
= 0;
333 h
->nag
[i
] = m
->selected
;
341 edit_nag_save (struct menu_input_s
*m
)
347 edit_nag_help (struct menu_input_s
*m
)
349 message (_("NAG Menu Keys"), ANY_KEY_STR
, "%s",
350 _(" UP/DOWN - previous/next menu item\n"
351 " HOME/END - first/last menu item\n"
352 " PGDN/PGUP - next/previous page\n"
353 " a-zA-Z0-9 - jump to item\n"
354 " SPACE - toggle selected item\n"
355 " CTRL-X - quit with changes"));
358 struct menu_item_s
**
359 get_nag_items (WIN
* win
)
362 struct menu_input_s
*m
= win
->data
;
363 struct input_s
*in
= m
->data
;
364 struct input_data_s
*id
= in
->data
;
365 struct menu_item_s
**items
= m
->items
;
366 HISTORY
*h
= id
->data
;
370 for (i
= 0; items
[i
]; i
++)
374 for (i
= 0; nags
[i
]; i
++)
376 items
= Realloc (items
, (i
+ 2) * sizeof (struct menu_item_s
*));
377 items
[i
] = Malloc (sizeof (struct menu_item_s
));
378 items
[i
]->name
= nags
[i
];
379 items
[i
]->value
= NULL
;
381 for (n
= 0; n
< MAX_PGN_NAG
; n
++)
385 items
[i
]->selected
= 1;
392 items
[i
]->selected
= 0;
404 nag_print (WIN
* win
)
406 struct menu_input_s
*m
= win
->data
;
408 mvwprintw (win
->w
, m
->print_line
, 1, "%-*s", win
->cols
- 2, m
->item
->name
);
414 struct menu_key_s
**keys
= NULL
;
422 add_menu_key (&keys
, ' ', edit_nag_toggle_item
);
423 add_menu_key (&keys
, CTRL_KEY ('x'), edit_nag_save
);
424 add_menu_key (&keys
, keycode_lookup (global_keys
, do_global_help
),
426 construct_menu (0, 0, -1, -1, _("Numeric Annotation Glyphs"), 1,
427 get_nag_items
, keys
, arg
, nag_print
, NULL
, NULL
);
434 HISTORY
*h
= (HISTORY
*) arg
;
436 char line
[LINE_MAX
] = { 0 };
439 snprintf (buf
, sizeof (buf
), "%s \"%s\"", _("Viewing NAG for"), h
->move
);
447 for (i
= 0; i
< MAX_PGN_NAG
; i
++)
454 if (h
->nag
[i
] >= nag_total
)
455 strncat (line
, itoa (h
->nag
[i
], buf2
), sizeof (line
) - 1);
457 strncat (line
, nags
[h
->nag
[i
]], sizeof (line
) - 1);
459 strncat (line
, "\n", sizeof (line
) - 1);
462 line
[strlen (line
) - 1] = 0;
463 message (buf
, ANY_KEY_STR
, "%s", line
);
468 view_annotation (HISTORY
* h
)
470 char buf
[MAX_SAN_MOVE_LEN
+ strlen (_("Viewing Annotation for")) + 4];
471 int nag
= 0, comment
= 0;
476 if (h
->comment
&& h
->comment
[0])
482 if (!nag
&& !comment
)
485 snprintf (buf
, sizeof (buf
), "%s \"%s\"", _("Viewing Annotation for"),
489 construct_message (buf
,
490 (nag
) ? _("Any other key to continue") : ANY_KEY_STR
,
491 0, 1, (nag
) ? _("Press 'n' to view NAG") : NULL
,
492 (nag
) ? view_nag
: NULL
, (nag
) ? h
: NULL
, NULL
,
493 (nag
) ? 'n' : 0, 0, NULL
, "%s", h
->comment
);
495 construct_message (buf
, _("Any other key to continue"), 0, 1,
496 _("Press 'n' to view NAG"), view_nag
, h
, NULL
, 'n', 0,
497 NULL
, "%s", _("No comment text for this move"));
501 do_game_write (char *filename
, const char *mode
, int start
, int end
)
504 struct userdata_s
*d
;
507 i
= pgn_open (filename
, mode
, &pgn
);
511 cmessage (ERROR_STR
, ANY_KEY_STR
, "%s\n%s", filename
, strerror (errno
));
514 else if (i
== E_PGN_INVALID
)
516 cmessage (ERROR_STR
, ANY_KEY_STR
, "%s\n%s", filename
,
517 _("Not a regular file"));
521 for (i
= (start
== -1) ? 0 : start
; i
< end
; i
++)
524 pgn_write (pgn
, game
[i
]);
525 CLEAR_FLAG (d
->flags
, CF_MODIFIED
);
528 if (pgn_close (pgn
) != E_PGN_OK
)
529 message (ERROR_STR
, ANY_KEY_STR
, "%s", strerror (errno
));
533 strncpy (loadfile
, filename
, sizeof (loadfile
));
534 loadfile
[sizeof (loadfile
) - 1] = 0;
549 do_save_game_overwrite_confirm (WIN
* win
)
551 const char *mode
= "w";
552 struct save_game_s
*s
= win
->data
;
553 wchar_t str
[] = { win
->c
, 0 };
555 if (!wcscmp (str
, append_wchar
))
557 else if (!wcscmp (str
, overwrite_wchar
))
562 if (do_game_write (s
->filename
, mode
, s
->start
, s
->end
))
563 update_status_notify (gp
, "%s", _("Save game failed."));
565 update_status_notify (gp
, "%s", _("Game saved."));
572 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
576 save_pgn (char *filename
, int saveindex
)
578 char buf
[FILENAME_MAX
];
580 int end
= (saveindex
== -1) ? gtotal
: saveindex
+ 1;
581 struct save_game_s
*s
;
583 if (filename
[0] != '/' && config
.savedirectory
)
585 if (stat (config
.savedirectory
, &st
) == -1)
589 if (mkdir (config
.savedirectory
, 0755) == -1)
591 cmessage (ERROR_STR
, ANY_KEY_STR
, "%s: %s",
592 config
.savedirectory
, strerror (errno
));
598 cmessage (ERROR_STR
, ANY_KEY_STR
, "%s: %s",
599 config
.savedirectory
, strerror (errno
));
604 if (stat (config
.savedirectory
, &st
) == -1)
606 cmessage (ERROR_STR
, ANY_KEY_STR
, "%s: %s", config
.savedirectory
,
611 if (!S_ISDIR (st
.st_mode
))
613 cmessage (ERROR_STR
, ANY_KEY_STR
, "%s: %s", config
.savedirectory
,
614 _("Not a directory."));
618 snprintf (buf
, sizeof (buf
), "%s/%s", config
.savedirectory
, filename
);
622 if (access (filename
, W_OK
) == 0)
624 s
= Malloc (sizeof (struct save_game_s
));
625 s
->filename
= strdup (filename
);
626 s
->start
= saveindex
;
628 construct_message (NULL
, _("What would you like to do?"), 0, 1, NULL
,
629 NULL
, s
, do_save_game_overwrite_confirm
, 0, 0, NULL
,
630 "%s \"%s\"\nPress \"%ls\" to append to this file, \"%ls\" to overwrite or any other key to cancel.",
631 _("File exists:"), filename
, append_wchar
,
636 if (do_game_write (filename
, "a", saveindex
, end
))
637 update_status_notify (gp
, "%s", _("Save game failed."));
639 update_status_notify (gp
, "%s", _("Game saved."));
643 castling_state (GAME g
, BOARD b
, int row
, int col
, int piece
, int mod
)
645 if (pgn_piece_to_int (piece
) == ROOK
&& col
== 7
647 (TEST_FLAG (g
->flags
, GF_WK_CASTLE
) || mod
) &&
648 pgn_piece_to_int (b
[7][4].icon
) == KING
&& isupper (piece
))
651 TOGGLE_FLAG (g
->flags
, GF_WK_CASTLE
);
654 else if (pgn_piece_to_int (piece
) == ROOK
&& col
== 0
656 (TEST_FLAG (g
->flags
, GF_WQ_CASTLE
) || mod
) &&
657 pgn_piece_to_int (b
[7][4].icon
) == KING
&& isupper (piece
))
660 TOGGLE_FLAG (g
->flags
, GF_WQ_CASTLE
);
663 else if (pgn_piece_to_int (piece
) == ROOK
&& col
== 7
665 (TEST_FLAG (g
->flags
, GF_BK_CASTLE
) || mod
) &&
666 pgn_piece_to_int (b
[0][4].icon
) == KING
&& islower (piece
))
669 TOGGLE_FLAG (g
->flags
, GF_BK_CASTLE
);
672 else if (pgn_piece_to_int (piece
) == ROOK
&& col
== 0
674 (TEST_FLAG (g
->flags
, GF_BQ_CASTLE
) || mod
) &&
675 pgn_piece_to_int (b
[0][4].icon
) == KING
&& islower (piece
))
678 TOGGLE_FLAG (g
->flags
, GF_BQ_CASTLE
);
681 else if (pgn_piece_to_int (piece
) == KING
&& col
== 4
683 (mod
|| (pgn_piece_to_int (b
[7][7].icon
) == ROOK
&&
684 TEST_FLAG (g
->flags
, GF_WK_CASTLE
))
686 (pgn_piece_to_int (b
[7][0].icon
) == ROOK
&&
687 TEST_FLAG (g
->flags
, GF_WQ_CASTLE
))) && isupper (piece
))
691 if (TEST_FLAG (g
->flags
, GF_WK_CASTLE
) ||
692 TEST_FLAG (g
->flags
, GF_WQ_CASTLE
))
693 CLEAR_FLAG (g
->flags
, GF_WK_CASTLE
| GF_WQ_CASTLE
);
695 SET_FLAG (g
->flags
, GF_WK_CASTLE
| GF_WQ_CASTLE
);
699 else if (pgn_piece_to_int (piece
) == KING
&& col
== 4
701 (mod
|| (pgn_piece_to_int (b
[0][7].icon
) == ROOK
&&
702 TEST_FLAG (g
->flags
, GF_BK_CASTLE
))
704 (pgn_piece_to_int (b
[0][0].icon
) == ROOK
&&
705 TEST_FLAG (g
->flags
, GF_BQ_CASTLE
))) && islower (piece
))
709 if (TEST_FLAG (g
->flags
, GF_BK_CASTLE
) ||
710 TEST_FLAG (g
->flags
, GF_BQ_CASTLE
))
711 CLEAR_FLAG (g
->flags
, GF_BK_CASTLE
| GF_BQ_CASTLE
);
713 SET_FLAG (g
->flags
, GF_BK_CASTLE
| GF_BQ_CASTLE
);
721 #define IS_ENPASSANT(c) (c == 'x') ? CP_BOARD_ENPASSANT : isupper(c) ? CP_BOARD_WHITE : CP_BOARD_BLACK
722 #define ATTRS(cp) (cp & (A_BOLD|A_STANDOUT|A_BLINK|A_DIM|A_UNDERLINE|A_INVIS|A_REVERSE))
727 w_pawn_wchar
= str_to_wchar (config
.utf8_pieces
? "♙" : "P");
728 w_rook_wchar
= str_to_wchar (config
.utf8_pieces
? "♖" : "R");
729 w_bishop_wchar
= str_to_wchar (config
.utf8_pieces
? "♗" : "B");
730 w_knight_wchar
= str_to_wchar (config
.utf8_pieces
? "♘" : "N");
731 w_queen_wchar
= str_to_wchar (config
.utf8_pieces
? "♕" : "Q");
732 w_king_wchar
= str_to_wchar (config
.utf8_pieces
? "♔" : "K");
733 b_pawn_wchar
= str_to_wchar (config
.utf8_pieces
? "♟" : "p");
734 b_rook_wchar
= str_to_wchar (config
.utf8_pieces
? "♜" : "r");
735 b_bishop_wchar
= str_to_wchar (config
.utf8_pieces
? "♝" : "b");
736 b_knight_wchar
= str_to_wchar (config
.utf8_pieces
? "♞" : "n");
737 b_queen_wchar
= str_to_wchar (config
.utf8_pieces
? "♛" : "q");
738 b_king_wchar
= str_to_wchar (config
.utf8_pieces
? "♚" : "k");
739 empty_wchar
= str_to_wchar (" ");
740 enpassant_wchar
= str_to_wchar ("x");
744 piece_to_wchar (unsigned char p
)
757 return w_bishop_wchar
;
759 return b_bishop_wchar
;
761 return w_knight_wchar
;
763 return b_knight_wchar
;
765 return w_queen_wchar
;
767 return b_queen_wchar
;
773 return enpassant_wchar
;
780 piece_can_attack (GAME g
, int rank
, int file
)
782 struct userdata_s
*d
= g
->data
;
783 char *m
, *frfr
= NULL
;
785 int row
, col
, p
, v
, pi
, cpi
;
789 rotate_position (&d
->c_row
, &d
->c_col
);
790 rotate_position (&d
->sp
.srow
, &d
->sp
.scol
);
793 v
= d
->b
[RANKTOBOARD (d
->c_row
)][FILETOBOARD (d
->c_col
)].valid
;
794 pi
= pgn_piece_to_int (d
->b
[RANKTOBOARD (rank
)][FILETOBOARD (file
)].icon
);
797 pgn_piece_to_int (d
->b
[RANKTOBOARD (d
->sp
.srow
)]
798 [FILETOBOARD (d
->sp
.scol
)].icon
) :
799 pgn_piece_to_int (d
->b
[RANKTOBOARD (d
->c_row
)]
800 [FILETOBOARD (d
->c_col
)].icon
);
802 if (pi
== OPEN_SQUARE
|| cpi
== OPEN_SQUARE
|| !VALIDFILE (file
)
803 || !VALIDRANK (rank
))
807 rotate_position (&d
->c_row
, &d
->c_col
);
808 rotate_position (&d
->sp
.srow
, &d
->sp
.scol
);
816 col
= v
? d
->c_col
: d
->sp
.scol
;
817 row
= v
? d
->c_row
: d
->sp
.srow
;
825 m
= Malloc (MAX_SAN_MOVE_LEN
+ 1);
826 m
[0] = INTTOFILE (file
);
827 m
[1] = INTTORANK (rank
);
828 m
[2] = INTTOFILE (col
);
829 m
[3] = INTTORANK (row
);
836 memcpy (b
, d
->b
, sizeof (BOARD
));
837 p
= b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
;
838 b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
=
839 pgn_int_to_piece (WHITE
, OPEN_SQUARE
);
840 b
[RANKTOBOARD (row
)][FILETOBOARD (col
)].icon
= p
;
842 e
= pgn_validate_move (g
, b
, &m
, &frfr
);
847 if (e
!= E_PGN_OK
&& pgn_piece_to_int (d
->sp
.icon
) == PAWN
)
849 int n
= (d
->sp
.srow
== 7 && islower (d
->sp
.icon
) && rank
== 5) ? 6 :
850 (d
->sp
.srow
== 2 && isupper (d
->sp
.icon
) && rank
== 4) ? 3 : 0;
852 if (n
&& (file
== d
->c_col
- 1 || file
== d
->c_col
+ 1))
854 memcpy (b
, d
->b
, sizeof (BOARD
));
855 p
= b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
;
856 b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
=
857 pgn_int_to_piece (WHITE
, OPEN_SQUARE
);
858 b
[RANKTOBOARD (row
)][FILETOBOARD (col
)].icon
= p
;
859 b
[RANKTOBOARD (n
)][FILETOBOARD (d
->sp
.scol
)].enpassant
= 1;
860 m
= Malloc (MAX_SAN_MOVE_LEN
+ 1);
861 m
[0] = INTTOFILE (file
);
862 m
[1] = INTTORANK (rank
);
863 m
[2] = INTTOFILE (col
);
864 m
[3] = INTTORANK (n
);
867 SET_FLAG (g
->flags
, GF_ENPASSANT
);
868 e
= pgn_validate_move (g
, b
, &m
, &frfr
);
869 CLEAR_FLAG (g
->flags
, GF_ENPASSANT
);
880 e
= pgn_validate_move (g
, d
->b
, &m
, &frfr
);
883 if (!strcmp (m
, "O-O") || !strcmp (m
, "O-O-O"))
888 int sf
= FILETOINT (frfr
[0]), sr
= RANKTOINT (frfr
[1]);
889 int df
= FILETOINT (frfr
[2]);
891 pi
= d
->b
[RANKTOBOARD (sr
)][FILETOBOARD (sf
)].icon
;
892 pi
= pgn_piece_to_int (pi
);
893 if (pi
== PAWN
&& sf
== df
)
903 rotate_position (&d
->c_row
, &d
->c_col
);
904 rotate_position (&d
->sp
.srow
, &d
->sp
.scol
);
907 return e
== E_PGN_OK
? 1 : 0;
911 print_piece (WINDOW
* w
, int l
, int c
, char p
)
915 for (i
= 0; i
< 13; i
+= 2)
917 if (p
== piece_chars
[i
] || p
== piece_chars
[i
+ 1])
919 for (y
= 0; y
< 3; y
++)
920 mvwprintw (w
, l
+ y
, c
, "%s", f_pieces
[i
+ ff
+ y
]);
927 for (y
= 0; y
< 3; y
++)
928 mvwprintw (w
, l
+ y
, c
, "%s", f_pieces
[0]);
932 board_prev_move_play (GAME g
)
934 struct userdata_s
*d
= g
->data
;
935 char l
= strlen (d
->pm_frfr
);
939 char q
= (l
> 4) ? 2 : 1;
941 d
->pm_row
= RANKTOINT (d
->pm_frfr
[l
- q
++]);
942 d
->pm_col
= FILETOINT (d
->pm_frfr
[l
- q
++]);
943 d
->ospm_row
= RANKTOINT (d
->pm_frfr
[l
- q
++]);
944 d
->ospm_col
= FILETOINT (d
->pm_frfr
[l
- q
]);
947 rotate_position (&d
->pm_row
, &d
->pm_col
);
948 rotate_position (&d
->ospm_row
, &d
->ospm_col
);
961 board_prev_move_history (GAME g
)
963 struct userdata_s
*d
= g
->data
;
967 char *move
= g
->hp
[g
->hindex
- 1]->move
;
971 if (d
->mode
== MODE_PLAY
)
972 coordofmove (g
, move
, &d
->pm_row
, &d
->pm_col
);
981 d
->ospm_row
= g
->turn
== WHITE
? 8 : 1;
993 HISTORY
*h
= pgn_history_by_n (g
->hp
, g
->hindex
- 2);
996 pgn_board_init_fen (g
, ob
, h
->fen
);
1001 for (f
= 0; f
< 8; f
++)
1003 for (r
= 0; r
< 8; r
++)
1005 if (ob
[f
][r
].icon
!= '.' && d
->b
[f
][r
].icon
== '.')
1007 d
->ospm_row
= INV_INT0 (f
) + 1;
1008 d
->ospm_col
= r
+ 1;
1016 if (d
->mode
== MODE_PLAY
)
1017 rotate_position (&d
->pm_row
, &d
->pm_col
);
1019 rotate_position (&d
->ospm_row
, &d
->ospm_col
);
1035 is_the_square (int brow
, int bcol
, int row
, int col
, int prow
, int pcol
)
1037 if ((!BIG_BOARD
&& row
== ROWTOMATRIX (prow
) && col
== COLTOMATRIX (pcol
))
1038 || (BIG_BOARD
&& brow
+ 1 == INV_INT (prow
) && bcol
+ 1 == pcol
))
1045 is_prev_move (struct userdata_s
*d
, int brow
, int bcol
, int row
, int col
)
1047 if (is_the_square (brow
, bcol
, row
, col
, d
->pm_row
, d
->pm_col
))
1054 update_board_window (GAME g
)
1057 int bcol
= 0, brow
= 0;
1058 int l
= config
.coordsyleft
;
1059 int maxy
= BOARD_HEIGHT
, maxx
= BOARD_WIDTH
;
1060 int ncols
= 0, offset
= 1;
1061 int rowr
= (MEGA_BOARD
) ? 6 : (BIG_BOARD
) ? 4 : 2;
1062 int colr
= (MEGA_BOARD
) ? 12 : (BIG_BOARD
) ? 8 : 4;
1063 unsigned coords_y
= 8, cxgc
= 0;
1064 unsigned i
, cpd
= 0;
1065 struct userdata_s
*d
= g
->data
;
1067 if (config
.bprevmove
&& d
->mode
!= MODE_EDIT
)
1069 if (!d
->pm_undo
&& d
->mode
== MODE_PLAY
)
1070 board_prev_move_play (g
);
1072 board_prev_move_history (g
);
1082 if (d
->mode
!= MODE_PLAY
&& d
->mode
!= MODE_EDIT
)
1083 update_cursor (g
, g
->hindex
);
1104 for (row
= 0; row
< maxy
; row
++)
1121 for (col
= 0; col
< maxx
; col
++)
1124 chtype attrs
= 0, old_attrs
= 0;
1129 if (row
== 0 || row
== maxy
- 2)
1132 mvwaddch (boardw
, row
, col
+ l
,
1134 ? ACS_LLCORNER
| CP_BOARD_GRAPHICS
1135 : ACS_ULCORNER
| CP_BOARD_GRAPHICS
));
1136 else if (col
== maxx
- 2)
1137 mvwaddch (boardw
, row
, col
+ l
,
1139 ? ACS_LRCORNER
| CP_BOARD_GRAPHICS
1140 : ACS_URCORNER
| CP_BOARD_GRAPHICS
));
1141 else if (!(col
% colr
))
1142 mvwaddch (boardw
, row
, col
+ l
,
1144 ? ACS_BTEE
| CP_BOARD_GRAPHICS
1145 : ACS_TTEE
| CP_BOARD_GRAPHICS
));
1148 if (col
!= maxx
- 1)
1149 mvwaddch (boardw
, row
, col
+ l
,
1150 LINE_GRAPHIC (ACS_HLINE
| CP_BOARD_GRAPHICS
));
1156 if ((row
% 2) && col
== maxx
- 1 && (coords_y
> 0 && coords_y
< 9))
1158 wattron (boardw
, CP_BOARD_COORDS
);
1160 (BIG_BOARD
) ? row
* ((MEGA_BOARD
) ? 3 : 2)
1161 : row
, (l
) ? 0 : col
, "%d",
1162 (d
->rotate
) ? coords_y
++ : coords_y
--);
1163 wattroff (boardw
, CP_BOARD_COORDS
);
1167 if ((col
== 0 || col
== maxx
- 2) && row
!= maxy
- 1)
1170 mvwaddch (boardw
, row
, col
+ l
,
1171 LINE_GRAPHIC ((col
) ?
1172 ACS_RTEE
| CP_BOARD_GRAPHICS
:
1173 ACS_LTEE
| CP_BOARD_GRAPHICS
));
1175 mvwaddch (boardw
, row
, col
+ l
,
1176 LINE_GRAPHIC (ACS_VLINE
| CP_BOARD_GRAPHICS
));
1181 if ((row
% rowr
) && !(col
% colr
) && row
!= maxy
- 1)
1183 mvwaddch (boardw
, row
, col
+ l
,
1184 LINE_GRAPHIC (ACS_VLINE
| CP_BOARD_GRAPHICS
));
1188 if (!(col
% colr
) && row
!= maxy
- 1)
1190 mvwaddch (boardw
, row
, col
+ l
,
1191 LINE_GRAPHIC (ACS_PLUS
| CP_BOARD_GRAPHICS
));
1200 attrwhich
= (cb
[brow
][bcol
]) ? WHITE
: BLACK
;
1209 if (((ncols
% 2) && !(offset
% 2))
1210 || (!(ncols
% 2) && (offset
% 2)))
1216 if (BIG_BOARD
&& d
->rotate
)
1218 brow
= INV_INT0 (brow
);
1219 bcol
= INV_INT0 (bcol
);
1223 p
= d
->b
[brow
][bcol
].icon
;
1225 p
= d
->b
[RANKTOBOARD (brow
)][FILETOBOARD (bcol
)].icon
;
1227 int pi
= pgn_piece_to_int (p
);
1229 if (config
.details
&&
1232 b
[RANKTOBOARD (brow
)][FILETOBOARD (bcol
)].enpassant
)
1233 || (BIG_BOARD
&& d
->b
[brow
][bcol
].enpassant
)))
1236 attrs
= mix_cp (CP_BOARD_ENPASSANT
,
1238 WHITE
) ? CP_BOARD_WHITE
:
1240 ATTRS (CP_BOARD_ENPASSANT
), A_FG_B_BG
);
1243 if (config
.showattacks
&& config
.details
1244 && piece_can_attack (g
,
1245 BIG_BOARD
? INV_INT0 (brow
) +
1247 BIG_BOARD
? bcol
+ 1 : bcol
))
1249 attrs
= CP_BOARD_ATTACK
;
1254 if (config
.validmoves
&&
1256 && d
->b
[RANKTOBOARD (brow
)][FILETOBOARD (bcol
)].valid
)
1257 || (BIG_BOARD
&& d
->b
[brow
][bcol
].valid
)))
1262 if (attrwhich
== WHITE
)
1263 attrs
= mix_cp (CP_BOARD_MOVES_WHITE
,
1265 ATTRS (CP_BOARD_MOVES_WHITE
),
1268 attrs
= mix_cp (CP_BOARD_MOVES_BLACK
,
1270 ATTRS (CP_BOARD_MOVES_BLACK
),
1273 else if (p
!= 'x' && !can_attack
)
1275 (attrwhich
== WHITE
) ? CP_BOARD_WHITE
: CP_BOARD_BLACK
;
1277 if (BIG_BOARD
&& d
->rotate
)
1279 brow
= INV_INT0 (brow
);
1280 bcol
= INV_INT0 (bcol
);
1283 if (is_the_square (brow
, bcol
, row
, col
, d
->c_row
,
1286 attrs
= mix_cp (CP_BOARD_CURSOR
, IS_ENPASSANT (p
),
1287 ATTRS (CP_BOARD_CURSOR
), B_FG_A_BG
);
1290 else if (is_the_square (brow
, bcol
, row
, col
,
1291 d
->sp
.srow
, d
->sp
.scol
))
1293 attrs
= mix_cp (CP_BOARD_SELECTED
, IS_ENPASSANT (p
),
1294 ATTRS (CP_BOARD_SELECTED
), B_FG_A_BG
);
1297 else if ((is_prev_move (d
, brow
, bcol
, row
, col
) && !valid
)
1298 || (is_the_square (brow
, bcol
, row
, col
,
1299 d
->ospm_row
, d
->ospm_col
)))
1301 attrs
= mix_cp (CP_BOARD_PREVMOVE
, IS_ENPASSANT (p
),
1302 ATTRS (CP_BOARD_PREVMOVE
), B_FG_A_BG
);
1306 if (row
== maxy
- 1)
1311 int n
= is_prev_move (d
, brow
, bcol
, row
, col
);
1312 chtype a
= n
&& !valid
1313 ? CP_BOARD_PREVMOVE
: attrwhich
==
1314 WHITE
? valid
? CP_BOARD_MOVES_WHITE
: CP_BOARD_WHITE
1315 : valid
? CP_BOARD_MOVES_BLACK
: CP_BOARD_BLACK
;
1317 mix_cp (CP_BOARD_ATTACK
, a
, ATTRS (CP_BOARD_ATTACK
),
1323 wmove (boardw
, row
, col
+ ((MEGA_BOARD
) ? 5 : 3) + l
);
1325 mvwaddch (boardw
, row
, col
+ l
, ' ' | attrs
);
1327 if (row
== maxy
- 1 && cxgc
< 8)
1330 "abcdefgh"[(BIG_BOARD
) ? bcol
: bcol
-
1331 1] | CP_BOARD_COORDS
);
1336 if (old_attrs
== -1)
1344 if (pi
!= OPEN_SQUARE
&& p
!= 'x' && !can_attack
)
1346 if (attrwhich
== WHITE
)
1349 attrs
= CP_BOARD_W_W
;
1351 attrs
= CP_BOARD_W_B
;
1356 attrs
= CP_BOARD_B_W
;
1358 attrs
= CP_BOARD_B_B
;
1365 if (config
.details
&& !can_attack
1366 && castling_state (g
, d
->b
,
1367 (d
->rotate
) ? INV_INT0 (brow
+ 1) - 1 : brow
,
1368 (d
->rotate
) ? INV_INT0 (bcol
+ 1) - 1 : bcol
,
1370 attrs
= mix_cp (CP_BOARD_CASTLING
, attrs
,
1371 ATTRS (CP_BOARD_CASTLING
), A_FG_B_BG
);
1375 if (config
.details
&& !can_attack
1376 && castling_state (g
, d
->b
, RANKTOBOARD (brow
),
1377 FILETOBOARD (bcol
), p
, 0))
1379 attrs
= mix_cp (CP_BOARD_CASTLING
, attrs
,
1380 ATTRS (CP_BOARD_CASTLING
),
1387 // FIXME: Reimpresión de piezas(+4).
1390 wattron (boardw
, attrs
);
1393 for (i
= 0; i
< 5; i
++)
1394 mvwprintw (boardw
, i
+ brow
* 6 + 1,
1397 if (pi
!= OPEN_SQUARE
)
1398 print_piece (boardw
, brow
* 6 + 2,
1399 bcol
* 12 + 3 + l
, p
);
1403 print_piece (boardw
, brow
* 4 + 1,
1405 (pi
!= OPEN_SQUARE
) ? p
: 0);
1408 wattroff (boardw
, attrs
);
1414 wattron (boardw
, attrs
);
1416 piece_to_wchar (pi
!=
1417 OPEN_SQUARE
? p
: 0));
1418 wattroff (boardw
, attrs
);
1425 col
+= (MEGA_BOARD
) ? 10 : 6;
1428 waddch (boardw
, ' ' | attrs
);
1448 if (col
!= maxx
- 1)
1449 mvwaddch (boardw
, row
, col
+ l
,
1450 LINE_GRAPHIC (ACS_HLINE
| CP_BOARD_GRAPHICS
));
1473 invalid_move (int n
, int e
, const char *m
)
1475 if (curses_initialized
)
1476 cmessage (ERROR_STR
, ANY_KEY_STR
, "%s \"%s\" (round #%i)",
1478 E_PGN_AMBIGUOUS
) ? _("Ambiguous move") : _("Invalid move"), m
,
1481 warnx ("%s: %s \"%s\" (round #%i)", loadfile
, (e
== E_PGN_AMBIGUOUS
)
1482 ? _("Ambiguous move") : _("Invalid move"), m
, n
);
1488 struct userdata_s
*d
= g
->data
;
1490 SET_FLAG (g
->flags
, GF_GAMEOVER
);
1491 d
->mode
= MODE_HISTORY
;
1496 update_clock (GAME g
, struct itimerval it
)
1498 struct userdata_s
*d
= g
->data
;
1500 if (TEST_FLAG (d
->flags
, CF_CLOCK
) && g
->turn
== WHITE
)
1502 d
->wclock
.elapsed
.tv_sec
+= it
.it_value
.tv_sec
;
1503 d
->wclock
.elapsed
.tv_usec
+= it
.it_value
.tv_usec
;
1505 if (d
->wclock
.elapsed
.tv_usec
> 1000000 - 1)
1507 d
->wclock
.elapsed
.tv_sec
+= d
->wclock
.elapsed
.tv_usec
/ 1000000;
1508 d
->wclock
.elapsed
.tv_usec
= d
->wclock
.elapsed
.tv_usec
% 1000000;
1511 if (d
->wclock
.tc
[d
->wclock
.tcn
][1] &&
1512 d
->wclock
.elapsed
.tv_sec
>= d
->wclock
.tc
[d
->wclock
.tcn
][1])
1514 pgn_tag_add (&g
->tag
, (char *) "Result", (char *) "0-1");
1518 else if (TEST_FLAG (d
->flags
, CF_CLOCK
) && g
->turn
== BLACK
)
1520 d
->bclock
.elapsed
.tv_sec
+= it
.it_value
.tv_sec
;
1521 d
->bclock
.elapsed
.tv_usec
+= it
.it_value
.tv_usec
;
1523 if (d
->bclock
.elapsed
.tv_usec
> 1000000 - 1)
1525 d
->bclock
.elapsed
.tv_sec
+= d
->bclock
.elapsed
.tv_usec
/ 1000000;
1526 d
->bclock
.elapsed
.tv_usec
= d
->bclock
.elapsed
.tv_usec
% 1000000;
1529 if (d
->bclock
.tc
[d
->bclock
.tcn
][1] &&
1530 d
->bclock
.elapsed
.tv_sec
>= d
->bclock
.tc
[d
->bclock
.tcn
][1])
1532 pgn_tag_add (&g
->tag
, (char *) "Result", (char *) "1-0");
1537 d
->elapsed
.tv_sec
+= it
.it_value
.tv_sec
;
1538 d
->elapsed
.tv_usec
+= it
.it_value
.tv_usec
;
1540 if (d
->elapsed
.tv_usec
> 1000000 - 1)
1542 d
->elapsed
.tv_sec
+= d
->elapsed
.tv_usec
/ 1000000;
1543 d
->elapsed
.tv_usec
= d
->elapsed
.tv_usec
% 1000000;
1548 update_time_control (GAME g
)
1550 struct userdata_s
*d
= g
->data
;
1551 struct clock_s
*clk
= (g
->turn
== WHITE
) ? &d
->wclock
: &d
->bclock
;
1554 clk
->tc
[clk
->tcn
][1] += clk
->incr
;
1556 if (!clk
->tc
[clk
->tcn
][1])
1561 if (!clk
->tc
[clk
->tcn
][0] || clk
->move
>= clk
->tc
[clk
->tcn
][0])
1564 clk
->tc
[clk
->tcn
+ 1][1] +=
1565 labs (clk
->elapsed
.tv_sec
- clk
->tc
[clk
->tcn
][1]);
1566 memset (&clk
->elapsed
, 0, sizeof (clk
->elapsed
));
1572 update_history_window (GAME g
)
1574 char buf
[HISTORY_WIDTH
- 1];
1577 int t
= pgn_history_total (g
->hp
);
1579 n
= (g
->hindex
+ 1) / 2;
1582 total
= (t
+ 1) / 2;
1587 snprintf (buf
, sizeof (buf
), "%u %s %u%s", n
, _("of"), total
,
1588 (movestep
== 1) ? _(" (ply)") : "");
1590 strncpy (buf
, _("not available"), sizeof (buf
) - 1);
1592 buf
[sizeof (buf
) - 1] = 0;
1593 mvwprintw (historyw
, 2, 1, "%*s %-*s", 10, _("Move:"),
1594 HISTORY_WIDTH
- 14, buf
);
1596 h
= pgn_history_by_n (g
->hp
, g
->hindex
);
1597 snprintf (buf
, sizeof (buf
), "%s",
1598 (h
&& h
->move
) ? h
->move
1599 : (LINES
< 24) ? _("empty") : _("not available"));
1602 if (h
&& ((h
->comment
) || h
->nag
[0]))
1604 strncat (buf
, _(" (Annotated"), sizeof (buf
) - 1);
1610 strncat (buf
, (n
) ? ",+" : " (+", sizeof (buf
) - 1);
1616 strncat (buf
, (n
) ? ",-" : " (-", sizeof (buf
) - 1);
1621 strncat (buf
, ")", sizeof (buf
) - 1);
1623 mvwprintw (historyw
, 3, ((LINES
< 24) ? 17 : 1), "%s %-*s",
1624 (LINES
< 24) ? _("Next:") : _("Next move:"),
1625 HISTORY_WIDTH
- ((LINES
< 24) ? 26 : 14), buf
);
1627 h
= pgn_history_by_n (g
->hp
, g
->hindex
- 1);
1628 snprintf (buf
, sizeof (buf
), "%s",
1629 (h
&& h
->move
) ? h
->move
1630 : (LINES
< 24) ? _("empty") : _("not available"));
1633 if (h
&& ((h
->comment
) || h
->nag
[0]))
1635 strncat (buf
, _(" (Annotated"), sizeof (buf
) - 1);
1641 strncat (buf
, (n
) ? ",+" : " (+", sizeof (buf
) - 1);
1647 strncat (buf
, (n
) ? ",-" : " (-", sizeof (buf
) - 1);
1652 strncat (buf
, ")", sizeof (buf
) - 1);
1654 mvwprintw (historyw
, ((LINES
< 24) ? 3 : 4), 1, "%s %-*s",
1655 (LINES
< 24) ? _("Prev.:") : _("Prev move:"),
1656 HISTORY_WIDTH
- ((LINES
< 24) ? 26 : 14), buf
);
1660 do_validate_move (char **move
)
1662 struct userdata_s
*d
= gp
->data
;
1666 if (TEST_FLAG (d
->flags
, CF_HUMAN
))
1668 if ((n
= pgn_parse_move (gp
, d
->b
, move
, &frfr
)) != E_PGN_OK
)
1670 invalid_move (d
->n
+ 1, n
, *move
);
1674 strcpy (d
->pm_frfr
, frfr
);
1675 update_time_control (gp
);
1676 pgn_history_add (gp
, d
->b
, *move
);
1677 pgn_switch_turn (gp
);
1681 if ((n
= pgn_validate_move (gp
, d
->b
, move
, &frfr
)) != E_PGN_OK
)
1683 invalid_move (d
->n
+ 1, n
, *move
);
1687 add_engine_command (gp
, ENGINE_THINKING
, "%s\n",
1688 (config
.engine_protocol
== 1) ? frfr
: *move
);
1691 d
->sp
.srow
= d
->sp
.scol
= d
->sp
.icon
= 0;
1693 if (config
.validmoves
)
1694 pgn_reset_valid_moves (d
->b
);
1696 if (TEST_FLAG (gp
->flags
, GF_GAMEOVER
))
1697 d
->mode
= MODE_HISTORY
;
1699 SET_FLAG (d
->flags
, CF_MODIFIED
);
1703 update_history_window (gp
);
1704 update_board_window (gp
);
1709 do_promotion_piece_finalize (WIN
* win
)
1711 char *p
, *str
= win
->data
;
1713 if (pgn_piece_to_int (win
->c
) == -1)
1716 p
= str
+ strlen (str
);
1717 *p
++ = toupper (win
->c
);
1719 do_validate_move (&str
);
1725 move_to_engine (GAME g
)
1727 struct userdata_s
*d
= g
->data
;
1731 if (config
.validmoves
&&
1732 !d
->b
[RANKTOBOARD (d
->sp
.row
)][FILETOBOARD (d
->sp
.col
)].valid
)
1735 str
= Malloc (MAX_SAN_MOVE_LEN
+ 1);
1736 snprintf (str
, MAX_SAN_MOVE_LEN
+ 1, "%c%u%c%u",
1737 _("abcdefgh")[d
->sp
.scol
- 1],
1738 d
->sp
.srow
, _("abcdefgh")[d
->sp
.col
- 1], d
->sp
.row
);
1741 pgn_piece_to_int (d
->b
[RANKTOBOARD (d
->sp
.srow
)]
1742 [FILETOBOARD (d
->sp
.scol
)].icon
);
1744 if (piece
== PAWN
&& (d
->sp
.row
== 8 || d
->sp
.row
== 1))
1746 construct_message (_("Select Pawn Promotion Piece"), _("R/N/B/Q"), 1, 1,
1747 NULL
, NULL
, str
, do_promotion_piece_finalize
, 0, 0,
1749 _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1753 do_validate_move (&str
);
1758 clock_to_char (long n
)
1760 static char buf
[16];
1761 int h
= 0, m
= 0, s
= 0;
1764 m
= (n
% 3600) / 60;
1765 s
= (n
% 3600) % 60;
1766 snprintf (buf
, sizeof (buf
), "%.2i:%.2i:%.2i", h
, m
, s
);
1771 timeval_to_char (struct timeval t
, long limit
)
1774 unsigned h
= 0, m
= 0, s
= 0;
1775 int n
= limit
? labs (limit
- t
.tv_sec
) : 0;
1778 m
= (n
% 3600) / 60;
1779 s
= (n
% 3600) % 60;
1780 snprintf (buf
, sizeof (buf
), "%.2u:%.2u:%.2u", h
, m
, s
);
1785 time_control_status (struct clock_s
*clk
)
1787 static char buf
[80] = { 0 };
1791 if (clk
->tc
[clk
->tcn
][0] && clk
->tc
[clk
->tcn
+ 1][1])
1792 snprintf (buf
, sizeof (buf
), " M%.2i/%s",
1793 abs (clk
->tc
[clk
->tcn
][0] - clk
->move
),
1794 clock_to_char (clk
->tc
[clk
->tcn
+ 1][1]));
1795 else if (!clk
->incr
)
1802 strncat (tbuf
, " I", sizeof (tbuf
) - 1);
1803 strncat (tbuf
, itoa (clk
->incr
, buf
), sizeof (tbuf
) - 1);
1810 update_status_window (GAME g
)
1814 char tmp
[15] = { 0 }, *engine
, *mode
;
1820 struct userdata_s
*d
= g
->data
;
1824 if (!curses_initialized
)
1827 getmaxyx (statusw
, maxy
, maxx
);
1834 wchar_t *loadfilew
= loadfile
[0]
1835 ? str_etc (loadfile
, w
, 1) : str_to_wchar (_("not available"));
1836 mvwprintw (statusw
, y
++, 1, "%*s %-*ls", 7, _("File:"), w
, loadfilew
);
1838 snprintf (buf
, len
, "%i %s %i", gindex
+ 1, _("of"), gtotal
);
1839 mvwprintw (statusw
, y
++, 1, "%*s %-*s", 7, _("Game:"), w
, buf
);
1850 if (TEST_FLAG (d
->flags
, CF_DELETE
))
1859 if (TEST_FLAG (g
->flags
, GF_PERROR
))
1868 if (TEST_FLAG (d
->flags
, CF_MODIFIED
))
1877 pgn_config_get (PGN_STRICT_CASTLING
, &n
);
1888 if (TEST_FLAG (d
->flags
, CF_PERL
))
1899 mvwprintw (statusw
, y
++, 1, "%*s %-*s", 7, _("Flags:"), w
,
1900 (tmp
[0]) ? tmp
: "-");
1905 mode
= _("move history");
1914 mode
= _("(empty value)");
1918 snprintf (buf
, len
- 1, "%*s %s", 7, _("Mode:"), mode
);
1920 if (d
->mode
== MODE_PLAY
)
1922 if (TEST_FLAG (d
->flags
, CF_HUMAN
))
1923 strncat (buf
, _(" (human/human)"), len
- 1);
1924 else if (TEST_FLAG (d
->flags
, CF_ENGINE_LOOP
))
1925 strncat (buf
, _(" (engine/engine)"), len
- 1);
1927 strncat (buf
, (d
->play_mode
== PLAY_EH
) ?
1928 _(" (engine/human)") : _(" (human/engine)"), len
- 1);
1932 mvwprintw (statusw
, y
++, 1, "%-*s", len
, buf
);
1937 switch (d
->engine
->status
)
1939 case ENGINE_THINKING
:
1940 engine
= _("pondering...");
1943 engine
= _("ready");
1945 case ENGINE_INITIALIZING
:
1946 engine
= _("initializing...");
1948 case ENGINE_OFFLINE
:
1949 engine
= _("offline");
1952 engine
= _("(empty value)");
1957 engine
= _("offline");
1959 mvwprintw (statusw
, y
, 1, "%*s %-*s", 7, _("Engine:"), w
, " ");
1960 wattron (statusw
, CP_STATUS_ENGINE
);
1961 mvwaddstr (statusw
, y
++, 9, engine
);
1962 wattroff (statusw
, CP_STATUS_ENGINE
);
1964 mvwprintw (statusw
, y
++, 1, "%*s %-*s", 7, _("Turn:"), w
,
1965 (g
->turn
== WHITE
) ? _("white") : _("black"));
1967 strncpy (tmp
, _("white"), sizeof (tmp
) - 1);
1968 tmp
[0] = toupper (tmp
[0]);
1969 snprintf (t
, sizeof (t
), "%s%s",
1970 timeval_to_char (d
->wclock
.elapsed
,
1971 d
->wclock
.tc
[d
->wclock
.tcn
][1]),
1972 time_control_status (&d
->wclock
));
1973 mvwprintw (statusw
, y
++, 1, "%*s: %-*s", 6, tmp
, w
, t
);
1975 strncpy (tmp
, _("black"), sizeof (tmp
) - 1);
1976 tmp
[0] = toupper (tmp
[0]);
1977 snprintf (t
, sizeof (t
), "%s%s",
1978 timeval_to_char (d
->bclock
.elapsed
,
1979 d
->bclock
.tc
[d
->bclock
.tcn
][1]),
1980 time_control_status (&d
->bclock
));
1981 mvwprintw (statusw
, y
++, 1, "%*s: %-*s", 6, tmp
, w
, t
);
1983 mvwprintw (statusw
, y
++, 1, "%*s %-*s", 7, _("Total:"), w
,
1984 clock_to_char (d
->elapsed
.tv_sec
));
1986 // for (i = 0; i < STATUS_WIDTH; i++)
1987 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1993 snprintf (tbuf
, sizeof (tbuf
), _("Type %ls for help"),
1994 key_lookup (global_keys
, do_global_help
));
1995 status
.notify
= str_to_wchar (tbuf
);
1998 wattron (stdscr
, CP_STATUS_NOTIFY
);
1999 for (i
= (config
.boardleft
) ? BOARD_WIDTH
: 0;
2000 i
< ((config
.boardleft
) ? COLS
: STATUS_WIDTH
); i
++)
2001 mvwprintw (stdscr
, STATUS_HEIGHT
, i
, " ");
2002 mvwprintw (stdscr
, STATUS_HEIGHT
, CENTERX (STATUS_WIDTH
, status
.notify
)
2003 + ((config
.boardleft
) ? BOARD_WIDTH
: 0), "%ls", status
.notify
);
2004 wattroff (stdscr
, CP_STATUS_NOTIFY
);
2008 translate_tag_name (const char *tag
)
2010 if (!strcmp (tag
, "Event"))
2011 return str_to_wchar (translatable_tag_names
[0]);
2012 else if (!strcmp (tag
, "Site"))
2013 return str_to_wchar (translatable_tag_names
[1]);
2014 else if (!strcmp (tag
, "Date"))
2015 return str_to_wchar (translatable_tag_names
[2]);
2016 else if (!strcmp (tag
, "Round"))
2017 return str_to_wchar (translatable_tag_names
[3]);
2018 else if (!strcmp (tag
, "White"))
2019 return str_to_wchar (translatable_tag_names
[4]);
2020 else if (!strcmp (tag
, "Black"))
2021 return str_to_wchar (translatable_tag_names
[5]);
2022 else if (!strcmp (tag
, "Result"))
2023 return str_to_wchar (translatable_tag_names
[6]);
2025 return str_to_wchar (tag
);
2029 update_tag_window (TAG
** t
)
2034 for (i
= 0; t
[i
]; i
++)
2036 wchar_t *namewc
= translate_tag_name (t
[i
]->name
);
2038 l
= wcslen (namewc
);
2044 w
= TAG_WIDTH
- namel
- 4;
2046 for (i
= 0; t
[i
] && i
< TAG_HEIGHT
- 3; i
++)
2048 wchar_t *namewc
= translate_tag_name (t
[i
]->name
);
2049 wchar_t *valuewc
= str_etc (t
[i
]->value
, w
, 0);
2051 mvwprintw (tagw
, (i
+ 2), 1, "%*ls: %-*ls", namel
, namewc
, w
, valuewc
);
2056 for (; i
< TAG_HEIGHT
- 3; i
++)
2057 mvwprintw (tagw
, (i
+ 2), 1, "%*s", namel
+ w
+ 2, " ");
2061 append_enginebuf (GAME g
, char *line
)
2064 struct userdata_s
*d
= g
->data
;
2066 if (d
->engine
->enginebuf
)
2067 for (i
= 0; d
->engine
->enginebuf
[i
]; i
++);
2069 if (d
->engine
->enginebuf
&& i
>= LINES
- 3)
2071 free (d
->engine
->enginebuf
[0]);
2073 for (i
= 0; d
->engine
->enginebuf
[i
+ 1]; i
++)
2074 d
->engine
->enginebuf
[i
] = d
->engine
->enginebuf
[i
+ 1];
2076 d
->engine
->enginebuf
[i
] = strdup (line
);
2080 d
->engine
->enginebuf
=
2081 Realloc (d
->engine
->enginebuf
, (i
+ 2) * sizeof (char *));
2082 d
->engine
->enginebuf
[i
++] = strdup (line
);
2083 d
->engine
->enginebuf
[i
] = NULL
;
2088 update_engine_window (GAME g
)
2091 struct userdata_s
*d
= g
->data
;
2093 wmove (enginew
, 0, 0);
2094 wclrtobot (enginew
);
2096 if (d
->engine
&& d
->engine
->enginebuf
)
2098 for (i
= 0; d
->engine
->enginebuf
[i
]; i
++)
2099 mvwprintw (enginew
, i
+ 2, 1, "%s", d
->engine
->enginebuf
[i
]);
2102 window_draw_title (enginew
, _("Engine IO Window"), COLS
, CP_MESSAGE_TITLE
,
2109 struct userdata_s
*d
= g
->data
;
2112 * In the middle of a macro. Don't update the screen.
2114 if (macro_match
!= -1)
2117 wmove (boardw
, ROWTOMATRIX (d
->c_row
), COLTOMATRIX (d
->c_col
));
2118 update_board_window (g
);
2119 update_status_window (g
);
2120 update_history_window (g
);
2121 update_tag_window (g
->tag
);
2122 update_engine_window (g
);
2128 game_next_prev (GAME g
, int n
, int count
)
2135 if (gindex
+ count
> gtotal
- 1)
2138 gindex
= gtotal
- 1;
2147 if (gindex
- count
< 0)
2152 gindex
= gtotal
- 1;
2162 delete_game (int which
)
2165 struct userdata_s
*d
;
2167 for (i
= 0; i
< gtotal
; i
++)
2171 if (i
== w
|| TEST_FLAG (d
->flags
, CF_DELETE
))
2175 free_userdata_once (game
[i
]);
2178 for (n
= i
; n
+ 1 < gtotal
; n
++)
2179 game
[n
] = game
[n
+ 1];
2189 if (which
+ 1 >= gtotal
)
2190 gindex
= gtotal
- 1;
2195 gindex
= gtotal
- 1;
2198 gp
->hp
= gp
->history
;
2202 * FIXME find across multiple games.
2205 find_move_exp (GAME g
, regex_t r
, int which
, int count
)
2213 incr
= (which
== 0) ? -1 : 1;
2215 for (i
= g
->hindex
+ incr
- 1, found
= 0;; i
+= incr
)
2217 if (i
== g
->hindex
- 1)
2220 if (i
>= pgn_history_total (g
->hp
))
2223 i
= pgn_history_total (g
->hp
) - 1;
2226 ret
= regexec (&r
, g
->hp
[i
]->move
, 0, 0, 0);
2230 if (count
== ++found
)
2237 if (ret
!= REG_NOMATCH
)
2239 regerror (ret
, &r
, errbuf
, sizeof (errbuf
));
2240 cmessage (_("Error Matching Regular Expression"), ANY_KEY_STR
,
2251 toggle_delete_flag (int n
)
2254 struct userdata_s
*d
= game
[n
]->data
;
2256 TOGGLE_FLAG (d
->flags
, CF_DELETE
);
2259 for (i
= x
= 0; i
< gtotal
; i
++)
2263 if (TEST_FLAG (d
->flags
, CF_DELETE
))
2269 cmessage (NULL
, ANY_KEY_STR
, "%s", _("Cannot delete last game."));
2271 CLEAR_FLAG (d
->flags
, CF_DELETE
);
2279 find_game_exp (char *str
, int which
, int count
)
2281 char *nstr
= NULL
, *exp
= NULL
;
2285 char buf
[255] = { 0 }, *tmp
;
2288 int incr
= (which
== 0) ? -(1) : 1;
2290 strncpy (buf
, str
, sizeof (buf
));
2291 buf
[sizeof (buf
)-1] = 0;
2294 if (strstr (tmp
, ":") != NULL
)
2296 nstr
= strsep (&tmp
, ":");
2298 if ((ret
= regcomp (&nexp
, nstr
,
2299 REG_ICASE
| REG_EXTENDED
| REG_NOSUB
)) != 0)
2301 regerror (ret
, &nexp
, errbuf
, sizeof (errbuf
));
2302 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR
,
2311 while (exp
&& *exp
&& isspace (*exp
))
2317 if ((ret
= regcomp (&vexp
, exp
, REG_EXTENDED
| REG_NOSUB
)) != 0)
2319 regerror (ret
, &vexp
, errbuf
, sizeof (errbuf
));
2320 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR
, "%s",
2328 for (g
= gindex
+ incr
, found
= 0;; g
+= incr
)
2340 for (t
= 0; game
[g
]->tag
[t
]; t
++)
2344 if (regexec (&nexp
, game
[g
]->tag
[t
]->name
, 0, 0, 0) == 0)
2346 if (regexec (&vexp
, game
[g
]->tag
[t
]->value
, 0, 0, 0) == 0)
2348 if (count
== ++found
)
2358 if (regexec (&vexp
, game
[g
]->tag
[t
]->value
, 0, 0, 0) == 0)
2360 if (count
== ++found
)
2383 * Updates the notification line in the status window then refreshes the
2387 update_status_notify (GAME g
, const char *fmt
, ...)
2390 #ifdef HAVE_VASPRINTF
2396 free (status
.notify
);
2397 status
.notify
= NULL
;
2403 #ifdef HAVE_VASPRINTF
2404 vasprintf (&line
, fmt
, ap
);
2406 vsnprintf (line
, sizeof (line
), fmt
, ap
);
2410 status
.notify
= str_to_wchar (line
);
2412 #ifdef HAVE_VASPRINTF
2418 rav_next_prev (GAME g
, BOARD b
, int n
)
2423 if ((!g
->ravlevel
&& g
->hindex
&& g
->hp
[g
->hindex
- 1]->rav
== NULL
) ||
2424 (!g
->ravlevel
&& !g
->hindex
&& g
->hp
[g
->hindex
]->rav
== NULL
) ||
2425 (g
->ravlevel
&& g
->hp
[g
->hindex
]->rav
== NULL
))
2428 g
->rav
= Realloc (g
->rav
, (g
->ravlevel
+ 1) * sizeof (RAV
));
2429 g
->rav
[g
->ravlevel
].hp
= g
->hp
;
2430 g
->rav
[g
->ravlevel
].flags
= g
->flags
;
2431 g
->rav
[g
->ravlevel
].fen
= pgn_game_to_fen (g
, b
);
2432 g
->rav
[g
->ravlevel
].hindex
= g
->hindex
;
2434 (!g
->ravlevel
) ? (g
->hindex
) ? g
->hp
[g
->hindex
-
2437 g
->hp
[g
->hindex
]->rav
;
2440 pgn_board_update (g
, b
, g
->hindex
+ 1);
2444 if (g
->ravlevel
- 1 < 0)
2449 pgn_board_init_fen (g
, b
, g
->rav
[g
->ravlevel
].fen
);
2450 free (g
->rav
[g
->ravlevel
].fen
);
2451 g
->hp
= g
->rav
[g
->ravlevel
].hp
;
2452 g
->flags
= g
->rav
[g
->ravlevel
].flags
;
2453 g
->hindex
= g
->rav
[g
->ravlevel
].hindex
;
2458 draw_window_decor ()
2460 move_panel (boardp
, 0, (config
.boardleft
) ? 0 : COLS
- BOARD_WIDTH
);
2461 move_panel (historyp
, LINES
- HISTORY_HEIGHT
,
2462 (config
.boardleft
) ? (MEGA_BOARD
) ? BOARD_WIDTH
: 0 :
2463 (MEGA_BOARD
) ? 0 : COLS
- HISTORY_WIDTH
);
2464 move_panel (statusp
, 0, (config
.boardleft
) ? BOARD_WIDTH
: 0);
2465 move_panel (tagp
, STATUS_HEIGHT
+ 1,
2466 (config
.boardleft
) ? (MEGA_BOARD
) ? BOARD_WIDTH
:
2469 wbkgd (boardw
, CP_BOARD_WINDOW
);
2470 wbkgd (statusw
, CP_STATUS_WINDOW
);
2471 window_draw_title (statusw
, _("Game Status"), STATUS_WIDTH
,
2472 CP_STATUS_TITLE
, CP_STATUS_BORDER
);
2473 wbkgd (tagw
, CP_TAG_WINDOW
);
2474 window_draw_title (tagw
, _("Roster Tags"), TAG_WIDTH
, CP_TAG_TITLE
,
2476 wbkgd (historyw
, CP_HISTORY_WINDOW
);
2477 window_draw_title (historyw
, _("Move History"), HISTORY_WIDTH
,
2478 CP_HISTORY_TITLE
, CP_HISTORY_BORDER
);
2482 history_menu_resize (WIN
*w
)
2484 struct menu_input_s
*m
;
2489 w
->rows
= MEGA_BOARD
? LINES
- HISTORY_HEIGHT_MB
: LINES
;
2490 w
->cols
= TAG_WIDTH
;
2492 w
->posx
= config
.boardleft
? BOARD_WIDTH
: 0;
2494 m
->ystatic
= w
->posy
;
2495 m
->xstatic
= w
->posx
;
2502 if (LINES
< 23 || COLS
< 74)
2505 resizeterm (LINES
, COLS
);
2507 wresize (boardw
, BOARD_HEIGHT
, BOARD_WIDTH
);
2508 wresize (historyw
, HISTORY_HEIGHT
, HISTORY_WIDTH
);
2509 wresize (statusw
, STATUS_HEIGHT
, STATUS_WIDTH
);
2510 wresize (tagw
, TAG_HEIGHT
, TAG_WIDTH
);
2511 //resize_history_menu ();
2520 draw_window_decor ();
2522 keypad (boardw
, TRUE
);
2531 do_window_resize ();
2537 memset (&clock_timer
, 0, sizeof (struct itimerval
));
2538 setitimer (ITIMER_REAL
, &clock_timer
, NULL
);
2542 start_clock (GAME g
)
2544 struct userdata_s
*d
= g
->data
;
2546 if (clock_timer
.it_interval
.tv_usec
)
2549 memset (&d
->elapsed
, 0, sizeof (struct timeval
));
2550 clock_timer
.it_value
.tv_sec
= 0;
2551 clock_timer
.it_value
.tv_usec
= 100000;
2552 clock_timer
.it_interval
.tv_sec
= 0;
2553 clock_timer
.it_interval
.tv_usec
= 100000;
2554 setitimer (ITIMER_REAL
, &clock_timer
, NULL
);
2561 struct userdata_s
*d
;
2562 struct itimerval it
;
2565 getitimer (ITIMER_REAL
, &it
);
2567 for (i
= 0; i
< gtotal
; i
++)
2571 if (d
&& d
->mode
== MODE_PLAY
)
2573 if (d
->paused
== 1 || TEST_FLAG (d
->flags
, CF_NEW
))
2575 else if (d
->paused
== -1)
2577 if (game
[i
]->side
== game
[i
]->turn
)
2584 update_clock (game
[i
], it
);
2593 update_status_window (gp
);
2599 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2602 parse_clock_time (char **str
)
2618 while (isdigit (*p
))
2661 parse_clock_input (struct clock_s
*clk
, char *str
, int *incr
)
2684 memset (clk
, 0, sizeof (struct clock_s
));
2688 if (strncasecmp (p
, "SD", 2) == 0)
2695 n
= parse_clock_time (&p
);
2710 /* Sudden death without a previous time control. */
2716 n
= parse_clock_time (&p
);
2723 message (ERROR_STR
, ANY_KEY_STR
, "%s (%i)",
2724 _("Maximum number of time controls reached"), MAX_TC
);
2729 clk
->tc
[tc
++][1] = n
;
2744 clk
->tc
[clk
->tcn
][1] =
2745 (n
<= clk
->elapsed
.tv_sec
) ? clk
->elapsed
.tv_sec
+ n
: n
;
2755 n
= parse_clock_time (&p
);
2776 parse_which_clock (struct clock_s
*clk
, char *str
)
2781 memcpy (&tmp
, clk
, sizeof (struct clock_s
));
2783 if (parse_clock_input (&tmp
, str
, &incr
))
2785 cmessage (ERROR_STR
, ANY_KEY_STR
, _("Invalid clock specification"));
2789 memcpy (clk
, &tmp
, sizeof (struct clock_s
));
2790 clk
->tc
[clk
->tcn
][1] += incr
;
2795 do_clock_input_finalize (WIN
* win
)
2797 struct userdata_s
*d
= gp
->data
;
2798 struct input_data_s
*in
= win
->data
;
2809 if (tolower (*p
) == 'w')
2813 if (parse_which_clock (&d
->wclock
, p
))
2816 else if (tolower (*p
) == 'b')
2820 if (parse_which_clock (&d
->bclock
, p
))
2825 if (parse_which_clock (&d
->wclock
, p
))
2828 if (parse_which_clock (&d
->bclock
, p
))
2832 if (!d
->wclock
.tc
[0][1] && !d
->bclock
.tc
[0][1])
2833 CLEAR_FLAG (d
->flags
, CF_CLOCK
);
2835 SET_FLAG (d
->flags
, CF_CLOCK
);
2843 do_engine_command_finalize (WIN
* win
)
2845 struct userdata_s
*d
= gp
->data
;
2846 struct input_data_s
*in
= win
->data
;
2858 x
= d
->engine
->status
;
2859 send_to_engine (gp
, -1, "%s\n", in
->str
);
2860 d
->engine
->status
= x
;
2870 config
.details
= (config
.details
) ? 0 : 1;
2874 do_toggle_strict_castling ()
2878 pgn_config_get (PGN_STRICT_CASTLING
, &n
);
2881 pgn_config_set (PGN_STRICT_CASTLING
, 1);
2883 pgn_config_set (PGN_STRICT_CASTLING
, 0);
2887 do_play_set_clock ()
2889 struct input_data_s
*in
;
2891 in
= Calloc (1, sizeof (struct input_data_s
));
2892 in
->efunc
= do_clock_input_finalize
;
2893 construct_input (_("Set Clock"), NULL
, 1, 1,
2895 ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n"
2896 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2897 NULL
, NULL
, 0, in
, INPUT_HIST_CLOCK
, NULL
, -1);
2901 do_play_toggle_human ()
2903 struct userdata_s
*d
= gp
->data
;
2905 TOGGLE_FLAG (d
->flags
, CF_HUMAN
);
2907 if (!TEST_FLAG (d
->flags
, CF_HUMAN
) && pgn_history_total (gp
->hp
))
2909 if (init_chess_engine (gp
))
2913 CLEAR_FLAG (d
->flags
, CF_ENGINE_LOOP
);
2916 d
->engine
->status
= ENGINE_READY
;
2920 do_play_toggle_engine ()
2922 struct userdata_s
*d
= gp
->data
;
2924 TOGGLE_FLAG (d
->flags
, CF_ENGINE_LOOP
);
2925 CLEAR_FLAG (d
->flags
, CF_HUMAN
);
2927 if (d
->engine
&& TEST_FLAG (d
->flags
, CF_ENGINE_LOOP
))
2929 char *fen
= pgn_game_to_fen (gp
, d
->b
);
2931 pgn_board_update (gp
, d
->b
, pgn_history_total (gp
->hp
));
2932 add_engine_command (gp
, ENGINE_READY
, "setboard %s\n", fen
);
2938 * This will send a command to the engine skipping the command queue.
2941 do_play_send_command ()
2943 struct userdata_s
*d
= gp
->data
;
2944 struct input_data_s
*in
;
2946 if (!d
->engine
|| d
->engine
->status
== ENGINE_OFFLINE
)
2948 if (init_chess_engine (gp
))
2952 in
= Calloc (1, sizeof (struct input_data_s
));
2953 in
->efunc
= do_engine_command_finalize
;
2954 construct_input (_("Engine Command"), NULL
, 1, 1, NULL
, NULL
, NULL
, 0, in
,
2955 INPUT_HIST_ENGINE
, NULL
, -1);
2959 void do_play_switch_turn()
2961 struct userdata_s *d = gp->data;
2963 pgn_switch_side(gp);
2964 pgn_switch_turn(gp);
2966 if (!TEST_FLAG(d->flags, CF_HUMAN))
2967 add_engine_command(gp, -1,
2968 (gp->side == WHITE) ? "white\n" : "black\n");
2970 update_status_window(gp);
2974 do_play_toggle_eh_mode ()
2976 struct userdata_s
*d
= gp
->data
;
2978 if (!TEST_FLAG (d
->flags
, CF_HUMAN
))
2982 pgn_switch_side (gp
, TRUE
);
2983 d
->play_mode
= (d
->play_mode
) ? PLAY_HE
: PLAY_EH
;
2984 if (gp
->side
== BLACK
)
2985 update_status_notify (gp
, _("Press 'g' to start the game"));
2987 d
->rotate
= !d
->rotate
;
2990 message (NULL
, ANY_KEY_STR
,
2991 _("You may only switch sides at the start of the \n"
2992 "game. Press ^K or ^N to begin a new game."));
2999 struct userdata_s
*d
= gp
->data
;
3001 if (!pgn_history_total (gp
->hp
))
3006 if (gp
->hindex
- keycount
< 0)
3011 gp
->hindex
-= (keycount
* 2) - 1;
3013 gp
->hindex
-= keycount
* 2;
3018 if (gp
->hindex
- 2 < 0)
3029 pgn_history_free (gp
->hp
, gp
->hindex
);
3030 gp
->hindex
= pgn_history_total (gp
->hp
);
3031 pgn_board_update (gp
, d
->b
, gp
->hindex
);
3033 if (d
->engine
&& d
->engine
->status
== ENGINE_READY
)
3035 char *fen
= pgn_game_to_fen (gp
, d
->b
);
3037 add_engine_command (gp
, ENGINE_READY
, "setboard %s\n", fen
);
3039 d
->engine
->status
= ENGINE_READY
;
3042 update_history_window (gp
);
3046 pgn_switch_side (gp
, FALSE
);
3054 do_play_toggle_pause ()
3056 struct userdata_s
*d
= gp
->data
;
3058 if (!TEST_FLAG (d
->flags
, CF_HUMAN
) && gp
->turn
!= gp
->side
)
3064 d
->paused
= (d
->paused
) ? 0 : 1;
3070 struct userdata_s
*d
= gp
->data
;
3072 if (TEST_FLAG (d
->flags
, CF_HUMAN
))
3075 if (fm_loaded_file
&& gp
->side
!= gp
->turn
)
3077 pgn_switch_side (gp
, FALSE
);
3078 add_engine_command (gp
, ENGINE_THINKING
, "black\n");
3081 add_engine_command (gp
, ENGINE_THINKING
, "go\n");
3083 // Completa la función para que permita seguir jugando al usarla.
3084 // Complete the function to allow continue playing when using.
3085 if (gp
->side
== gp
->turn
)
3086 pgn_switch_side (gp
, FALSE
);
3092 do_play_config_command ()
3098 for (x
= 0; config
.keys
[x
]; x
++)
3100 if (config
.keys
[x
]->c
== input_c
)
3102 switch (config
.keys
[x
]->type
)
3105 add_engine_command (gp
, -1, "%ls\n", config
.keys
[x
]->str
);
3111 add_engine_command (gp
, -1,
3112 "%ls %i\n", config
.keys
[x
]->str
,
3120 for (w
= 0; w
< keycount
; w
++)
3121 add_engine_command (gp
, -1, "%ls\n", config
.keys
[x
]->str
);
3129 update_status_notify (gp
, NULL
);
3133 do_play_cancel_selected ()
3135 struct userdata_s
*d
= gp
->data
;
3137 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
3139 pgn_reset_valid_moves (d
->b
);
3140 update_status_notify (gp
, NULL
);
3146 struct userdata_s
*d
= gp
->data
;
3148 pushkey
= keycount
= 0;
3149 update_status_notify (gp
, NULL
);
3151 if (!TEST_FLAG (d
->flags
, CF_HUMAN
) &&
3152 (!d
->engine
|| d
->engine
->status
== ENGINE_THINKING
))
3158 d
->sp
.row
= d
->c_row
;
3159 d
->sp
.col
= d
->c_col
;
3163 rotate_position (&d
->sp
.row
, &d
->sp
.col
);
3164 rotate_position (&d
->sp
.srow
, &d
->sp
.scol
);
3167 move_to_engine (gp
);
3169 // Completa la función para que permita seguir jugando cuando se carga un
3170 // archivo pgn (con juego no terminado) que inicie con turno del lado
3172 // Complete the function to allow continue playing when loading a file
3173 // pgn (with unfinished game) you start to turn black side.
3174 if (gp
->side
!= gp
->turn
)
3175 pgn_switch_side (gp
, FALSE
);
3177 if (d
->rotate
&& d
->sp
.icon
)
3178 rotate_position (&d
->sp
.srow
, &d
->sp
.scol
);
3181 fm_loaded_file
= FALSE
;
3188 struct userdata_s
*d
= gp
->data
;
3190 if (!TEST_FLAG (d
->flags
, CF_HUMAN
) && (!d
->engine
||
3191 d
->engine
->status
==
3194 if (init_chess_engine (gp
))
3198 if (d
->engine
&& d
->engine
->status
== ENGINE_THINKING
)
3202 do_play_cancel_selected ();
3205 rotate_position (&d
->c_row
, &d
->c_col
);
3207 d
->sp
.icon
= d
->b
[RANKTOBOARD (d
->c_row
)][FILETOBOARD (d
->c_col
)].icon
;
3209 if (pgn_piece_to_int (d
->sp
.icon
) == OPEN_SQUARE
)
3215 if (((islower (d
->sp
.icon
) && gp
->turn
!= BLACK
)
3216 || (isupper (d
->sp
.icon
) && gp
->turn
!= WHITE
)))
3219 char *str
= Malloc (512);
3221 for (k
= play_keys
; *k
; k
++)
3223 if ((*k
)->f
== do_play_toggle_eh_mode
)
3229 ("It is not your turn to move. You may switch playing sides by pressing \"%lc\"."),
3230 *k
? (*k
)->c
: '?');
3231 message (NULL
, ANY_KEY_STR
, "%s", str
);
3236 if (pgn_history_total (gp
->hp
))
3238 message (NULL
, ANY_KEY_STR
, "%s",
3239 _("It is not your turn to move. You can switch sides "));
3245 if (pgn_tag_find (gp
->tag
, "FEN") != E_PGN_ERR
)
3248 add_engine_command (gp
, ENGINE_READY
, "black\n");
3249 pgn_switch_turn (gp
);
3251 if (gp
->side
!= BLACK
)
3252 pgn_switch_side (gp
);
3257 d
->sp
.srow
= d
->c_row
;
3258 d
->sp
.scol
= d
->c_col
;
3260 if (config
.validmoves
)
3261 pgn_find_valid_moves (gp
, d
->b
, d
->sp
.scol
, d
->sp
.srow
);
3265 rotate_position (&d
->c_row
, &d
->c_col
);
3266 rotate_position (&d
->sp
.srow
, &d
->sp
.scol
);
3269 CLEAR_FLAG (d
->flags
, CF_NEW
);
3274 build_help_line_once (wchar_t *buf
, wchar_t **pp
, struct key_s
*k
, int t
,
3277 wchar_t *p
= *pp
, *wc
;
3281 n
= wcslen (k
->key
);
3292 wcsncat (buf
, k
->key
, t
- 1);
3293 p
= buf
+ wcslen (buf
);
3304 wcsncat (buf
, k
->d
, t
- 1);
3308 wc
= str_to_wchar ("*");
3309 wcsncat (buf
, wc
, t
- 1);
3313 wc
= str_to_wchar ("\n");
3316 *pp
= buf
+ wcslen (buf
);
3320 calc_help_len (struct key_s
*k
, int *t
, int *nlen
, int *len
)
3327 if (wcslen (k
->key
) > *nlen
)
3329 *nlen
= wcslen (k
->key
);
3340 if (wcslen (k
->d
) > *len
)
3341 *len
= wcslen (k
->d
);
3349 build_help (struct key_s
**keys
)
3351 int i
, m
= 0, nlen
= 1, len
, t
;
3352 wchar_t *buf
= NULL
;
3354 wchar_t *more_help
= str_to_wchar (_("more help"));
3355 const wchar_t *more_help_key
= key_lookup (global_keys
, do_global_help
);
3357 int mode
= MODE_ANY
;
3365 for (i
= t
= len
= 0; keys
[i
]; i
++)
3366 calc_help_len (keys
[i
], &t
, &nlen
, &len
);
3370 if (keys
== play_keys
)
3372 else if (keys
== history_keys
)
3373 mode
= MODE_HISTORY
;
3374 else if (keys
== edit_keys
)
3379 for (m
= 0; macros
[m
]; m
++)
3381 if (macros
[m
]->mode
== mode
)
3383 k
.d
= macros
[m
]->desc
;
3385 k
.key
= str_to_wchar (fancy_key_name (macros
[m
]->c
));
3386 calc_help_len (&k
, &t
, &nlen
, &len
);
3394 k
.key
= (wchar_t *)more_help_key
;
3395 calc_help_len (&k
, &t
, &nlen
, &len
);
3397 t
+= 4 + i
+ 1 + m
+ 1; // +1 for more_help
3398 buf
= Malloc ((t
+ 1) * sizeof (wchar_t));
3401 for (i
= 0; keys
[i
]; i
++)
3406 build_help_line_once (buf
, &p
, keys
[i
], t
, nlen
);
3411 for (m
= 0; macros
[m
]; m
++)
3413 if (macros
[m
]->mode
== mode
)
3415 k
.d
= macros
[m
]->desc
;
3417 k
.key
= str_to_wchar (fancy_key_name (macros
[m
]->c
));
3418 build_help_line_once (buf
, &p
, &k
, t
, nlen
);
3426 k
.key
= (wchar_t *)more_help_key
;
3427 build_help_line_once (buf
, &p
, &k
, t
, nlen
);
3435 struct userdata_s
*d
= gp
->data
;
3438 if (!d
->global_help
)
3457 buf
= build_help (global_keys
);
3458 construct_message (_("Global Game Keys (* = can take a repeat count)"),
3459 ANY_KEY_SCROLL_STR
, 0, 0, NULL
, NULL
, buf
, do_more_help
,
3460 0, 1, NULL
, "%ls", buf
);
3464 do_main_help (WIN
* win
)
3466 struct userdata_s
*d
= gp
->data
;
3489 do_more_help (WIN
* win
)
3493 for (i
= 0; global_keys
[i
]; i
++)
3495 if (global_keys
[i
]->f
== do_global_help
3496 && global_keys
[i
]->c
== win
->c
)
3497 construct_message (_("Command Key Index"),
3498 _("p/h/e/g or any other key to quit"), 0, 0,
3499 NULL
, NULL
, NULL
, do_main_help
, 0, 0, NULL
, "%s",
3500 _(" p - play mode keys\n"
3501 " h - history mode keys\n"
3502 " e - board edit mode keys\n"
3503 " g - global game keys"));
3510 wchar_t *buf
= build_help (play_keys
);
3512 construct_message (_("Play Mode Keys (* = can take a repeat count)"),
3513 ANY_KEY_SCROLL_STR
, 0, 0, NULL
, NULL
,
3514 buf
, do_more_help
, 0, 1, NULL
, "%ls", buf
);
3518 do_play_history_mode ()
3520 struct userdata_s
*d
= gp
->data
;
3522 if (!pgn_history_total (gp
->hp
) ||
3523 (d
->engine
&& d
->engine
->status
== ENGINE_THINKING
))
3526 d
->mode
= MODE_HISTORY
;
3527 pgn_board_update (gp
, d
->b
, pgn_history_total (gp
->hp
));
3531 do_play_edit_mode ()
3533 struct userdata_s
*d
= gp
->data
;
3535 if (pgn_history_total (gp
->hp
))
3538 pgn_board_init_fen (gp
, d
->b
, NULL
);
3540 d
->mode
= MODE_EDIT
;
3544 do_edit_insert_finalize (WIN
* win
)
3546 struct userdata_s
*d
= win
->data
;
3548 if (pgn_piece_to_int (win
->c
) == -1)
3551 d
->b
[RANKTOBOARD (d
->c_row
)][FILETOBOARD (d
->c_col
)].icon
= win
->c
;
3557 struct userdata_s
*d
= gp
->data
;
3562 d
->sp
.icon
= d
->b
[RANKTOBOARD (d
->c_row
)][FILETOBOARD (d
->c_col
)].icon
;
3564 if (pgn_piece_to_int (d
->sp
.icon
) == OPEN_SQUARE
)
3570 d
->sp
.srow
= d
->c_row
;
3571 d
->sp
.scol
= d
->c_col
;
3578 struct userdata_s
*d
= gp
->data
;
3580 pushkey
= keycount
= 0;
3581 update_status_notify (gp
, NULL
);
3586 d
->sp
.row
= d
->c_row
;
3587 d
->sp
.col
= d
->c_col
;
3588 p
= d
->b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
;
3589 d
->b
[RANKTOBOARD (d
->sp
.row
)][FILETOBOARD (d
->sp
.col
)].icon
= p
;
3590 d
->b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
=
3591 pgn_int_to_piece (gp
->turn
, OPEN_SQUARE
);
3592 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
3598 struct userdata_s
*d
= gp
->data
;
3601 d
->b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
=
3602 pgn_int_to_piece (gp
->turn
, OPEN_SQUARE
);
3604 d
->b
[RANKTOBOARD (d
->c_row
)][FILETOBOARD (d
->c_col
)].icon
=
3605 pgn_int_to_piece (gp
->turn
, OPEN_SQUARE
);
3607 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
3611 do_edit_cancel_selected ()
3613 struct userdata_s
*d
= gp
->data
;
3615 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
3617 update_status_notify (gp
, NULL
);
3621 do_edit_switch_turn ()
3623 pgn_switch_turn (gp
);
3627 do_edit_toggle_castle ()
3629 struct userdata_s
*d
= gp
->data
;
3631 castling_state (gp
, d
->b
, RANKTOBOARD (d
->c_row
),
3632 FILETOBOARD (d
->c_col
),
3633 d
->b
[RANKTOBOARD (d
->c_row
)][FILETOBOARD (d
->c_col
)].icon
,
3640 struct userdata_s
*d
= gp
->data
;
3642 construct_message (_("Insert Piece"),
3643 _("P=pawn, R=rook, N=knight, B=bishop, Q=queen, K=king"),
3644 0, 0, NULL
, NULL
, d
->b
, do_edit_insert_finalize
, 0, 0,
3646 _("Type the piece letter to insert. Lowercase for a black piece, uppercase for a white piece."));
3650 do_edit_enpassant ()
3652 struct userdata_s
*d
= gp
->data
;
3654 if (d
->c_row
== 6 || d
->c_row
== 3)
3656 int n
= d
->b
[RANKTOBOARD (d
->c_row
)][FILETOBOARD (d
->c_col
)].enpassant
;
3658 pgn_reset_enpassant (d
->b
);
3660 d
->b
[RANKTOBOARD (d
->c_row
)][FILETOBOARD (d
->c_col
)].enpassant
= 1;
3667 wchar_t *buf
= build_help (edit_keys
);
3669 construct_message (_("Edit Mode Keys (* = can take a repeat count)"),
3670 ANY_KEY_SCROLL_STR
, 0, 0, NULL
, NULL
, buf
, do_more_help
,
3671 0, 1, NULL
, "%ls", buf
);
3677 struct userdata_s
*d
= gp
->data
;
3678 char *fen
= pgn_game_to_fen (gp
, d
->b
);
3681 pgn_tag_add (&gp
->tag
, (char *) "FEN", fen
);
3683 pgn_tag_add (&gp
->tag
, (char *) "SetUp", (char *) "1");
3684 pgn_tag_sort (gp
->tag
);
3685 pgn_board_update (gp
, d
->b
, gp
->hindex
);
3686 d
->mode
= MODE_PLAY
;
3690 really_do_annotate_finalize (struct input_data_s
*in
, struct userdata_s
*d
)
3692 HISTORY
*h
= in
->data
;
3705 len
= strlen (in
->str
);
3706 h
->comment
= Realloc (h
->comment
, len
+ 1);
3707 strncpy (h
->comment
, in
->str
, len
);
3708 h
->comment
[len
] = 0;
3713 SET_FLAG (d
->flags
, CF_MODIFIED
);
3717 do_annotate_finalize (WIN
* win
)
3719 struct userdata_s
*d
= gp
->data
;
3720 struct input_data_s
*in
= win
->data
;
3722 really_do_annotate_finalize (in
, d
);
3726 do_find_move_exp_finalize (int init
, int which
)
3729 struct userdata_s
*d
= gp
->data
;
3730 static int firstrun
;
3735 if (init
|| !firstrun
)
3740 if ((ret
= regcomp (&r
, moveexp
, REG_EXTENDED
| REG_NOSUB
)) != 0)
3742 regerror (ret
, &r
, errbuf
, sizeof (errbuf
));
3743 cmessage (_("Error Compiling Regular Expression"), ANY_KEY_STR
,
3751 if ((n
= find_move_exp (gp
, r
,
3752 (which
== -1) ? 0 : 1,
3753 (keycount
) ? keycount
: 1)) == -1)
3757 pgn_board_update (gp
, d
->b
, gp
->hindex
);
3761 do_find_move_exp (WIN
* win
)
3763 struct input_data_s
*in
= win
->data
;
3769 strncpy (moveexp
, in
->str
, sizeof (moveexp
) - 1);
3770 moveexp
[sizeof (moveexp
) - 1] = 0;
3771 do_find_move_exp_finalize (1, which
);
3780 do_move_jump_finalize (int n
)
3782 struct userdata_s
*d
= gp
->data
;
3784 if (n
< 0 || n
> (pgn_history_total (gp
->hp
) / 2))
3788 update_status_notify (gp
, NULL
);
3789 gp
->hindex
= (n
) ? n
* 2 - 1 : n
* 2;
3790 pgn_board_update (gp
, d
->b
, gp
->hindex
);
3794 do_move_jump (WIN
* win
)
3796 struct input_data_s
*in
= win
->data
;
3798 if (!in
->str
|| !isinteger (in
->str
))
3807 do_move_jump_finalize (atoi (in
->str
));
3812 struct history_menu_s
3822 free_history_menu_data (struct history_menu_s
**h
)
3829 for (i
= 0; h
[i
]; i
++)
3839 get_history_data (HISTORY
** hp
, struct history_menu_s
***menu
, int m
,
3843 int t
= pgn_history_total (hp
);
3844 char buf
[MAX_SAN_MOVE_LEN
+ 4];
3846 struct history_menu_s
**hmenu
= *menu
;
3849 for (n
= 0; hmenu
[n
]; n
++);
3853 for (i
= 0; i
< t
; i
++)
3855 hmenu
= Realloc (hmenu
, (n
+ 2) * sizeof (struct history_menu_s
*));
3856 hmenu
[n
] = Malloc (sizeof (struct history_menu_s
));
3857 snprintf (buf
, sizeof (buf
), "%c%s%s", (turn
== WHITE
) ? 'W' : 'B',
3858 hp
[i
]->move
, (hp
[i
]->comment
|| hp
[i
]->nag
[0]) ? " !" : "");
3859 hmenu
[n
]->line
= strdup (buf
);
3860 hmenu
[n
]->hindex
= i
;
3861 hmenu
[n
]->indent
= 0;
3862 hmenu
[n
]->ravlevel
= depth
;
3863 hmenu
[n
]->move
= (n
&& depth
> hmenu
[n
- 1]->ravlevel
) ? m
++ : m
;
3871 get_history_data (hp
[i
]->rav
, &hmenu
, m
, turn
);
3872 for (n
= 0; hmenu
[n
]; n
++);
3880 turn
= (turn
== WHITE
) ? BLACK
: WHITE
;
3887 history_draw_update (struct menu_input_s
*m
)
3890 struct userdata_s
*d
= g
->data
;
3892 g
->hindex
= m
->selected
+ 1;
3893 update_cursor (g
, m
->selected
);
3894 pgn_board_update (g
, d
->b
, m
->selected
+ 1);
3897 struct menu_item_s
**
3898 get_history_items (WIN
* win
)
3900 struct menu_input_s
*m
= win
->data
;
3902 struct userdata_s
*d
= g
->data
;
3903 struct history_menu_s
**hm
= d
->data
;
3904 struct menu_item_s
**items
= m
->items
;
3909 get_history_data (g
->history
, &hm
, 0,
3910 TEST_FLAG (g
->flags
, GF_BLACK_OPENING
));
3911 m
->selected
= g
->hindex
- 1;
3913 if (m
->selected
< 0)
3916 m
->draw_exit_func
= history_draw_update
;
3923 for (i
= 0; items
[i
]; i
++)
3930 for (i
= 0; hm
[i
]; i
++)
3932 items
= Realloc (items
, (i
+ 2) * sizeof (struct menu_item_s
*));
3933 items
[i
] = Malloc (sizeof (struct menu_item_s
));
3934 items
[i
]->name
= hm
[i
]->line
;
3935 items
[i
]->value
= NULL
;
3936 items
[i
]->selected
= 0;
3948 history_menu_quit (struct menu_input_s
*m
)
3954 history_menu_exit (WIN
* win
)
3957 struct userdata_s
*d
= g
->data
;
3958 struct history_menu_s
**hm
= d
->data
;
3964 for (i
= 0; hm
[i
]; i
++)
3976 history_menu_next (struct menu_input_s
*m
)
3979 struct userdata_s
*d
= g
->data
;
3980 struct history_menu_s
**hm
= d
->data
;
3983 for (t
= 0; hm
[t
]; t
++);
3985 if (m
->selected
+ 1 == t
)
3988 n
= hm
[m
->selected
+ 1]->hindex
;
3996 history_menu_prev (struct menu_input_s
*m
)
3999 struct userdata_s
*d
= g
->data
;
4000 struct history_menu_s
**hm
= d
->data
;
4003 for (t
= 0; hm
[t
]; t
++);
4005 if (m
->selected
- 1 < 0)
4008 n
= hm
[m
->selected
- 1]->hindex
;
4015 history_menu_help (struct menu_input_s
*m
)
4017 message (_("History Menu Help"), ANY_KEY_STR
, "%s",
4018 _(" UP/DOWN - previous/next menu item\n"
4019 " HOME/END - first/last menu item\n"
4020 " PGDN/PGUP - next/previous page\n"
4021 " a-zA-Z0-9 - jump to item\n"
4022 " CTRL-a - annotate the selected move\n"
4023 " ENTER - view annotation\n"
4024 " CTRL-d - toggle board details\n"
4025 " ESCAPE/M - return to move history"));
4029 do_annotate_move (HISTORY
* hp
)
4032 struct input_data_s
*in
;
4034 snprintf (buf
, sizeof (buf
), "%s \"%s\"", _("Editing Annotation for"),
4036 in
= Calloc (1, sizeof (struct input_data_s
));
4038 in
->efunc
= do_annotate_finalize
;
4039 construct_input (buf
, hp
->comment
, MAX_PGN_LINE_LEN
/ INPUT_WIDTH
, 0,
4040 _("Type CTRL-t to edit NAG"), edit_nag
, NULL
,
4041 CTRL_KEY ('T'), in
, -1, NULL
, -1);
4045 history_menu_view_annotation (struct menu_input_s
*m
)
4050 view_annotation (g
->history
[m
->selected
]);
4054 history_menu_annotate_finalize (WIN
* win
)
4056 struct input_data_s
*in
= win
->data
;
4057 GAME g
= in
->moredata
;
4058 struct userdata_s
*d
= g
->data
;
4059 struct history_menu_s
**hm
= d
->data
;
4061 really_do_annotate_finalize (in
, d
);
4062 free_history_menu_data (hm
);
4064 get_history_data (g
->history
, &hm
, 0,
4065 TEST_FLAG (g
->flags
, GF_BLACK_OPENING
));
4067 pushkey
= REFRESH_MENU
;
4071 history_menu_annotate (struct menu_input_s
*m
)
4075 struct input_data_s
*in
;
4076 HISTORY
*hp
= g
->history
[m
->selected
]; // FIXME RAV
4078 snprintf (buf
, sizeof (buf
), "%s \"%s\"", _("Editing Annotation for"),
4080 in
= Calloc (1, sizeof (struct input_data_s
));
4082 in
->moredata
= m
->data
;
4083 in
->efunc
= history_menu_annotate_finalize
;
4084 construct_input (buf
, hp
->comment
, MAX_PGN_LINE_LEN
/ INPUT_WIDTH
, 0,
4085 _("Type CTRL-t to edit NAG"), edit_nag
, NULL
,
4086 CTRL_KEY ('T'), in
, -1, NULL
, -1);
4090 history_menu_details (struct menu_input_s
*m
)
4092 do_board_details ();
4097 history_menu_print (WIN
* win
)
4099 struct menu_input_s
*m
= win
->data
;
4101 struct userdata_s
*d
= g
->data
;
4102 struct history_menu_s
**hm
= d
->data
;
4103 struct history_menu_s
*h
= hm
[m
->top
];
4105 char *p
= m
->item
->name
;
4106 int line
= m
->print_line
- 2;
4108 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
4115 for (total
= 0; hm
[total
]; total
++);
4116 wattr_get (win
->w
, &attrs
, &pair
, NULL
);
4117 wattroff (win
->w
, COLOR_PAIR (pair
));
4118 mvwaddch (win
->w
, m
->print_line
, 1,
4119 *p
== 'W' ? *p
| mix_cp (CP_BOARD_WHITE
, CP_HISTORY_WINDOW
,
4120 ATTRS (CP_BOARD_WHITE
),
4121 A_FG_B_BG
) : *p
| mix_cp (CP_BOARD_BLACK
,
4128 if (h
->hindex
== 0 && line
== 0)
4129 waddch (win
->w
, ACS_ULCORNER
| CP_HISTORY_MENU_LG
);
4130 else if ((!hm
[h
->hindex
+ (win
->rows
- 5) + 1] && line
== win
->rows
- 5) ||
4131 (m
->top
+ line
== total
- 1))
4132 waddch (win
->w
, ACS_LLCORNER
| CP_HISTORY_MENU_LG
);
4133 else if (hm
[m
->top
+ 1]->ravlevel
!= h
->ravlevel
|| !h
->ravlevel
)
4134 waddch (win
->w
, ACS_LTEE
| CP_HISTORY_MENU_LG
);
4136 waddch (win
->w
, ACS_VLINE
| CP_HISTORY_MENU_LG
);
4138 wattron (win
->w
, COLOR_PAIR (pair
) | attrs
);
4140 for (i
= 2; *p
; p
++, i
++)
4141 waddch (win
->w
, (*p
== '!') ? *p
| A_BOLD
: *p
);
4143 while (i
++ < win
->cols
- 2)
4144 waddch (win
->w
, ' ');
4148 history_menu (GAME g
)
4150 struct menu_key_s
**keys
= NULL
;
4152 add_menu_key (&keys
, KEY_ESCAPE
, history_menu_quit
);
4153 add_menu_key (&keys
, 'M', history_menu_quit
);
4154 add_menu_key (&keys
, KEY_UP
, history_menu_prev
);
4155 add_menu_key (&keys
, KEY_DOWN
, history_menu_next
);
4156 add_menu_key (&keys
, keycode_lookup (global_keys
, do_global_help
),
4158 add_menu_key (&keys
, CTRL_KEY ('a'), history_menu_annotate
);
4159 add_menu_key (&keys
, CTRL_KEY ('d'), history_menu_details
);
4160 add_menu_key (&keys
, '\n', history_menu_view_annotation
);
4161 construct_menu (MEGA_BOARD
? LINES
- HISTORY_HEIGHT_MB
: LINES
, TAG_WIDTH
,
4162 0, config
.boardleft
? BOARD_WIDTH
: 0,
4163 _("Move History Tree"), 1, get_history_items
, keys
, g
,
4164 history_menu_print
, history_menu_exit
, history_menu_resize
);
4174 do_history_half_move_toggle ()
4176 movestep
= (movestep
== 1) ? 2 : 1;
4177 update_history_window (gp
);
4181 do_history_rotate_board ()
4183 struct userdata_s
*d
= gp
->data
;
4184 d
->rotate
= !d
->rotate
;
4188 do_history_jump_next ()
4190 struct userdata_s
*d
= gp
->data
;
4192 pgn_history_next (gp
, d
->b
, (keycount
> 0) ?
4193 config
.jumpcount
* keycount
* movestep
:
4194 config
.jumpcount
* movestep
);
4198 do_history_jump_prev ()
4200 struct userdata_s
*d
= gp
->data
;
4202 pgn_history_prev (gp
, d
->b
, (keycount
) ?
4203 config
.jumpcount
* keycount
* movestep
:
4204 config
.jumpcount
* movestep
);
4210 struct userdata_s
*d
= gp
->data
;
4212 pgn_history_prev (gp
, d
->b
, (keycount
) ? keycount
* movestep
: movestep
);
4218 struct userdata_s
*d
= gp
->data
;
4220 pgn_history_next (gp
, d
->b
, (keycount
) ? keycount
* movestep
: movestep
);
4224 do_history_mode_finalize (struct userdata_s
*d
)
4227 d
->mode
= MODE_PLAY
;
4231 do_history_mode_confirm (WIN
* win
)
4233 struct userdata_s
*d
= gp
->data
;
4234 wchar_t str
[] = { win
->c
, 0 };
4236 if (!wcscmp (str
, resume_wchar
))
4238 pgn_history_free (gp
->hp
, gp
->hindex
);
4239 pgn_board_update (gp
, d
->b
, pgn_history_total (gp
->hp
));
4244 if (pgn_history_rav_new (gp
, d
->b
, gp
->hindex
) != E_PGN_OK
)
4252 if (!TEST_FLAG (d
->flags
, CF_HUMAN
))
4254 char *fen
= pgn_game_to_fen (gp
, d
->b
);
4256 add_engine_command (gp
, ENGINE_READY
, "setboard %s\n", fen
);
4260 do_history_mode_finalize (d
);
4264 do_history_toggle ()
4266 struct userdata_s
*d
= gp
->data
;
4268 // FIXME Resuming from previous history could append to a RAV.
4269 if (gp
->hindex
!= pgn_history_total (gp
->hp
))
4272 construct_message (NULL
, _("What would you like to do?"), 0, 1,
4273 NULL
, NULL
, NULL
, do_history_mode_confirm
, 0, 0,
4275 ("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."),
4281 if (TEST_FLAG (gp
->flags
, GF_GAMEOVER
))
4285 if (gp
->side
!= gp
->turn
)
4287 d
->play_mode
= PLAY_EH
;
4291 d
->play_mode
= PLAY_HE
;
4295 do_history_mode_finalize (d
);
4299 do_history_annotate ()
4303 if (n
&& gp
->hp
[n
- 1]->move
)
4308 do_annotate_move (gp
->hp
[n
]);
4314 wchar_t *buf
= build_help (history_keys
);
4316 construct_message (_("History Mode Keys (* = can take a repeat count)"),
4317 ANY_KEY_SCROLL_STR
, 0, 0, NULL
, NULL
,
4318 buf
, do_more_help
, 0, 1, NULL
, "%ls", buf
);
4322 do_history_find (int which
)
4324 struct input_data_s
*in
;
4327 if (pgn_history_total (gp
->hp
) < 2)
4330 in
= Calloc (1, sizeof (struct input_data_s
));
4331 p
= Malloc (sizeof (int));
4334 in
->efunc
= do_find_move_exp
;
4336 if (!*moveexp
|| which
== 0)
4338 construct_input (_("Find Move Text Expression"), NULL
, 1, 0, NULL
, NULL
,
4339 NULL
, 0, in
, INPUT_HIST_MOVE_EXP
, NULL
, -1);
4345 do_find_move_exp_finalize (0, which
);
4349 do_history_find_new ()
4351 do_history_find (0);
4355 do_history_find_prev ()
4357 do_history_find (-1);
4361 do_history_find_next ()
4363 do_history_find (1);
4367 do_history_rav (int which
)
4369 struct userdata_s
*d
= gp
->data
;
4371 rav_next_prev (gp
, d
->b
, which
);
4375 do_history_rav_next ()
4381 do_history_rav_prev ()
4389 struct input_data_s
*in
;
4391 if (pgn_history_total (gp
->hp
) < 2)
4396 in
= Calloc (1, sizeof (struct input_data_s
));
4397 in
->efunc
= do_move_jump
;
4399 construct_input (_("Jump to Move Number"), NULL
, 1, 1, NULL
,
4400 NULL
, NULL
, 0, in
, -1, NULL
, 0);
4404 do_move_jump_finalize (keycount
);
4408 free_userdata_once (GAME g
)
4410 struct userdata_s
*d
= g
->data
;
4419 if (d
->engine
->enginebuf
)
4423 for (n
= 0; d
->engine
->enginebuf
[n
]; n
++)
4424 free (d
->engine
->enginebuf
[n
]);
4426 free (d
->engine
->enginebuf
);
4429 if (d
->engine
->queue
)
4433 for (q
= d
->engine
->queue
; *q
; q
++)
4436 free (d
->engine
->queue
);
4459 for (i
= 0; i
< gtotal
; i
++)
4461 free_userdata_once (game
[i
]);
4462 game
[i
]->data
= NULL
;
4467 update_loading_window (int n
)
4473 loadingw
= newwin (3, COLS
/ 2, CALCPOSY (3), CALCPOSX (COLS
/ 2));
4474 loadingp
= new_panel (loadingw
);
4475 wbkgd (loadingw
, CP_MESSAGE_WINDOW
);
4478 wmove (loadingw
, 0, 0);
4479 wclrtobot (loadingw
);
4480 wattron (loadingw
, CP_MESSAGE_BORDER
);
4481 box (loadingw
, ACS_VLINE
, ACS_HLINE
);
4482 wattroff (loadingw
, CP_MESSAGE_BORDER
);
4483 mvwprintw (loadingw
, 1, CENTER_INT ((COLS
/ 2), 11 +
4484 strlen (itoa (gtotal
, buf
))),
4485 _("Loading... %i%% (%i games)"), n
, gtotal
);
4491 init_userdata_once (GAME g
, int n
)
4493 struct userdata_s
*d
= NULL
;
4495 d
= Calloc (1, sizeof (struct userdata_s
));
4497 d
->c_row
= 2, d
->c_col
= 5;
4498 SET_FLAG (d
->flags
, CF_NEW
);
4501 if (pgn_board_init_fen (g
, d
->b
, NULL
) != E_PGN_OK
)
4502 pgn_board_init (d
->b
);
4510 for (i
= 0; i
< gtotal
; i
++)
4511 init_userdata_once (game
[i
], i
);
4515 fix_marks (int *start
, int *end
)
4519 *start
= (*start
< 0) ? 0 : *start
;
4520 *end
= (*end
< 0) ? 0 : *end
;
4529 *end
= (*end
> gtotal
) ? gtotal
: *end
;
4533 do_new_game_finalize (GAME g
)
4535 struct userdata_s
*d
= g
->data
;
4537 d
->mode
= MODE_PLAY
;
4538 update_status_notify (g
, NULL
);
4544 do_new_game_from_scratch (WIN
* win
)
4546 wchar_t str
[] = { win
->c
, 0 };
4548 if (wcscmp (str
, yes_wchar
))
4555 add_custom_tags (&gp
->tag
);
4558 do_new_game_finalize (gp
);
4566 add_custom_tags (&gp
->tag
);
4567 init_userdata_once (gp
, gindex
);
4568 do_new_game_finalize (gp
);
4572 do_game_delete_finalize (int n
)
4574 struct userdata_s
*d
;
4576 delete_game ((!n
) ? gindex
: -1);
4578 if (d
->mode
!= MODE_EDIT
)
4579 pgn_board_update (gp
, d
->b
, pgn_history_total (gp
->hp
));
4583 do_game_delete_confirm (WIN
* win
)
4586 wchar_t str
[] = { win
->c
, 0 };
4588 if (wcscmp (str
, yes_wchar
))
4594 n
= (int *) win
->data
;
4595 do_game_delete_finalize (*n
);
4604 struct userdata_s
*d
;
4609 cmessage (NULL
, ANY_KEY_STR
, "%s", _("Cannot delete last game."));
4615 for (i
= n
= 0; i
< gtotal
; i
++)
4619 if (TEST_FLAG (d
->flags
, CF_DELETE
))
4624 tmp
= _("Delete the current game?");
4629 cmessage (NULL
, ANY_KEY_STR
, "%s", _("Cannot delete last game."));
4633 tmp
= _("Delete all games marked for deletion?");
4636 if (config
.deleteprompt
)
4638 p
= Malloc (sizeof (int));
4640 construct_message (NULL
, _("[ Yes or No ]"), 1, 1, NULL
, NULL
, p
,
4641 do_game_delete_confirm
, 0, 0, NULL
, "%s", tmp
);
4645 do_game_delete_finalize (n
);
4649 do_find_game_exp_finalize (int which
)
4651 struct userdata_s
*d
= gp
->data
;
4654 if ((n
= find_game_exp (gameexp
, (which
== -1) ? 0 : 1,
4655 (keycount
) ? keycount
: 1)) == -1)
4657 update_status_notify (gp
, "%s", _("No matches found"));
4664 if (pgn_history_total (gp
->hp
))
4665 d
->mode
= MODE_HISTORY
;
4667 pgn_board_update (gp
, d
->b
, pgn_history_total (gp
->hp
));
4671 do_find_game_exp (WIN
* win
)
4673 struct input_data_s
*in
= win
->data
;
4679 strncpy (gameexp
, in
->str
, sizeof (gameexp
));
4680 gameexp
[sizeof (gameexp
) - 1] = 0;
4685 do_find_game_exp_finalize (c
);
4694 do_game_jump_finalize (int n
)
4696 struct userdata_s
*d
;
4698 if (--n
> gtotal
- 1 || n
< 0)
4704 pgn_board_update (gp
, d
->b
, pgn_history_total (gp
->hp
));
4705 update_status_notify (gp
, NULL
);
4709 do_game_jump (WIN
* win
)
4711 struct input_data_s
*in
= win
->data
;
4713 if (!in
->str
|| !isinteger (in
->str
))
4722 do_game_jump_finalize (atoi (in
->str
));
4728 do_load_file (WIN
* win
)
4730 struct input_data_s
*in
= win
->data
;
4731 char *tmp
= in
->str
;
4732 struct userdata_s
*d
;
4733 PGN_FILE
*pgn
= NULL
;
4742 if ((tmp
= pathfix (tmp
)) == NULL
)
4745 n
= pgn_open (tmp
, "r", &pgn
);
4749 cmessage (ERROR_STR
, ANY_KEY_STR
, "%s\n%s", tmp
, strerror (errno
));
4752 else if (n
== E_PGN_INVALID
)
4754 cmessage (ERROR_STR
, ANY_KEY_STR
, "%s\n%s", tmp
,
4755 _("Not a regular file"));
4761 if (pgn_parse (pgn
) == E_PGN_ERR
)
4763 del_panel (loadingp
);
4771 del_panel (loadingp
);
4776 strncpy (loadfile
, tmp
, sizeof (loadfile
));
4777 loadfile
[sizeof (loadfile
) - 1] = 0;
4781 if (pgn_history_total (gp
->hp
))
4782 d
->mode
= MODE_HISTORY
;
4784 pgn_board_update (gp
, d
->b
, pgn_history_total (gp
->hp
));
4786 fm_loaded_file
= TRUE
;
4799 do_game_save (WIN
* win
)
4801 struct input_data_s
*in
= win
->data
;
4804 char *tmp
= in
->str
;
4805 char tfile
[FILENAME_MAX
];
4808 struct userdata_s
*d
;
4810 if (!tmp
|| (tmp
= pathfix (tmp
)) == NULL
)
4813 if (pgn_is_compressed (tmp
) == E_PGN_ERR
)
4815 p
= tmp
+ strlen (tmp
) - 1;
4817 if (*p
!= 'n' || *(p
- 1) != 'g' || *(p
- 2) != 'p' || *(p
- 3) != '.')
4819 snprintf (tfile
, sizeof (tfile
), "%s.pgn", tmp
);
4825 * When in edit mode, update the FEN tag.
4829 for (i
= 0; i
< gtotal
; i
++)
4833 if (d
->mode
== MODE_EDIT
)
4835 char *fen
= pgn_game_to_fen (game
[i
], d
->b
);
4837 pgn_tag_add (&game
[i
]->tag
, (char *) "FEN", fen
);
4846 if (d
->mode
== MODE_EDIT
)
4848 char *fen
= pgn_game_to_fen (game
[n
], d
->b
);
4850 pgn_tag_add (&game
[n
]->tag
, (char *) "FEN", fen
);
4866 do_get_game_save_input (int n
)
4868 struct input_data_s
*in
= Calloc (1, sizeof (struct input_data_s
));
4869 int *p
= Malloc (sizeof (int));
4871 in
->efunc
= do_game_save
;
4875 construct_input (_("Save Game Filename"), loadfile
, 1, 1,
4876 _("Type TAB for file browser"), file_browser
, NULL
, '\t',
4877 in
, INPUT_HIST_FILE
, NULL
, -1);
4881 do_game_save_multi_confirm (WIN
* win
)
4884 wchar_t str
[] = { win
->c
, 0 };
4886 if (!wcscmp (str
, current_wchar
))
4888 else if (!wcscmp (str
, all_wchar
))
4892 update_status_notify (gp
, "%s", _("Save game aborted."));
4896 do_get_game_save_input (i
);
4902 cmessage (_("ABOUT"), ANY_KEY_STR
,
4903 _("%s\nUsing %s with %i colors and %i color pairs\n%s\n%s"),
4904 PACKAGE_STRING
, curses_version (), COLORS
, COLOR_PAIRS
,
4905 COPYRIGHT
, CBOARD_URL
);
4909 global_game_next_prev (int which
)
4911 struct userdata_s
*d
;
4913 game_next_prev (gp
, (which
== 1) ? 1 : 0, (keycount
) ? keycount
: 1);
4920 markend
= markstart
+ delete_count
;
4925 markend
= markstart
- delete_count
+ 1;
4926 delete_count
= -1; // to fix gindex in the other direction
4929 fix_marks (&markstart
, &markend
);
4930 do_global_toggle_delete ();
4933 if (d
->mode
== MODE_HISTORY
)
4934 pgn_board_update (gp
, d
->b
, gp
->hindex
);
4935 else if (d
->mode
== MODE_PLAY
)
4936 pgn_board_update (gp
, d
->b
, pgn_history_total (gp
->hp
));
4940 do_global_next_game ()
4942 global_game_next_prev (1);
4946 do_global_prev_game ()
4948 global_game_next_prev (0);
4952 global_find (int which
)
4954 struct input_data_s
*in
;
4960 in
= Calloc (1, sizeof (struct input_data_s
));
4961 p
= Malloc (sizeof (int));
4964 in
->efunc
= do_find_game_exp
;
4966 if (!*gameexp
|| which
== 0)
4968 construct_input (_("Find Game by Tag Expression"), NULL
, 1, 0,
4969 _("[name expression:]value expression"), NULL
, NULL
, 0,
4970 in
, INPUT_HIST_GAME_EXP
, NULL
, -1);
4976 do_find_game_exp_finalize (which
);
4980 do_global_find_new ()
4986 do_global_find_next ()
4992 do_global_find_prev ()
4998 do_global_game_jump ()
5005 struct input_data_s
*in
;
5007 in
= Calloc (1, sizeof (struct input_data_s
));
5008 in
->efunc
= do_game_jump
;
5009 construct_input (_("Jump to Game Number"), NULL
, 1, 1, NULL
, NULL
, NULL
,
5010 0, in
, -1, NULL
, 0);
5014 do_game_jump_finalize (keycount
);
5018 do_global_toggle_delete ()
5027 if (keycount
&& delete_count
== 0)
5030 delete_count
= keycount
;
5031 update_status_notify (gp
, "%s (delete)", status
.notify
);
5035 if (markstart
>= 0 && markend
>= 0)
5037 for (i
= markstart
; i
< markend
; i
++)
5039 if (toggle_delete_flag (i
))
5045 gindex
= (delete_count
< 0) ? markstart
: i
- 1;
5049 if (toggle_delete_flag (gindex
))
5053 markstart
= markend
= -1;
5055 update_status_window (gp
);
5059 do_global_delete_game ()
5065 do_global_tag_edit ()
5067 struct userdata_s
*d
= gp
->data
;
5069 edit_tags (gp
, d
->b
, 1);
5073 do_global_tag_view ()
5075 struct userdata_s
*d
= gp
->data
;
5077 edit_tags (gp
, d
->b
, 0);
5081 do_global_resume_game ()
5083 struct input_data_s
*in
;
5085 in
= Calloc (1, sizeof (struct input_data_s
));
5086 in
->efunc
= do_load_file
;
5087 construct_input (_("Load Filename"), NULL
, 1, 1,
5088 _("Type TAB for file browser"), file_browser
, NULL
, '\t',
5089 in
, INPUT_HIST_FILE
, NULL
, -1);
5093 do_global_save_game ()
5097 construct_message (NULL
, _("What would you like to do?"), 0, 1,
5098 NULL
, NULL
, NULL
, do_game_save_multi_confirm
, 0, 0,
5100 ("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."),
5101 current_wchar
, all_wchar
);
5105 do_get_game_save_input (-1);
5109 do_global_new_game ()
5115 copy_game_common (int fen
)
5119 struct userdata_s
*d
= gp
->data
;
5120 char *fentag
= fen
? pgn_game_to_fen (gp
, d
->b
) : NULL
;
5122 do_global_new_game ();
5124 n
= pgn_tag_total (game
[g
]->tag
);
5126 for (i
= 0; i
< n
; i
++)
5127 pgn_tag_add (&gp
->tag
, game
[g
]->tag
[i
]->name
, game
[g
]->tag
[i
]->value
);
5131 pgn_tag_add (&gp
->tag
, (char *) "SetUp", (char *) "1");
5132 pgn_tag_add (&gp
->tag
, (char *) "FEN", fentag
);
5134 pgn_tag_sort (gp
->tag
);
5138 pgn_board_init_fen (gp
, d
->b
, NULL
);
5139 n
= pgn_history_total (game
[g
]->history
);
5142 for (i
= 0; i
< n
; i
++)
5145 char *move
= strdup (game
[g
]->history
[i
]->move
);
5147 if (pgn_parse_move (gp
, d
->b
, &move
, &frfr
) != E_PGN_OK
)
5150 SET_FLAG (gp
->flags
, GF_PERROR
);
5154 pgn_history_add (gp
, d
->b
, move
);
5157 pgn_switch_turn (gp
);
5161 pgn_board_update (gp
, d
->b
, pgn_history_total (gp
->hp
));
5165 do_global_copy_game ()
5167 copy_game_common (0);
5171 do_global_copy_game_fen ()
5173 copy_game_common (1);
5177 do_global_new_all ()
5179 construct_message (NULL
, _("[ Yes or No ]"), 1, 1, NULL
, NULL
, NULL
,
5180 do_new_game_from_scratch
, 0, 0, NULL
, "%s",
5181 _("Really start a new game from scratch?"));
5187 wchar_t str
[] = { win
->c
, 0 };
5188 int n
= wcscmp (str
, yes_wchar
);
5199 if (config
.exitdialogbox
)
5200 construct_message (NULL
, _("[ Yes or No ]"), 1, 1, NULL
, NULL
, NULL
,
5201 do_quit
, 0, 0, NULL
, "%s", _("Want to Quit?"));
5207 do_global_toggle_engine_window ()
5211 enginew
= newwin (LINES
, COLS
, 0, 0);
5212 enginep
= new_panel (enginew
);
5213 window_draw_title (enginew
, _("Engine IO Window"), COLS
,
5214 CP_MESSAGE_TITLE
, CP_MESSAGE_BORDER
);
5215 hide_panel (enginep
);
5218 if (panel_hidden (enginep
))
5220 update_engine_window (gp
);
5221 top_panel (enginep
);
5225 hide_panel (enginep
);
5230 do_global_toggle_board_details ()
5232 do_board_details ();
5236 do_play_toggle_strict_castling ()
5238 do_toggle_strict_castling ();
5241 // Global and other keys.
5245 struct userdata_s
*d
= gp
->data
;
5249 * These cannot be modified and other game mode keys cannot conflict with
5255 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
5256 markend
= markstart
= 0;
5261 update_status_notify (gp
, NULL
);
5264 if (config
.validmoves
)
5265 pgn_reset_valid_moves (d
->b
);
5272 keycount
= keycount
* 10 + i
;
5276 update_status_notify (gp
, _("Repeat %i"), keycount
);
5279 if (d
->mode
== MODE_HISTORY
)
5283 d
->c_row
+= keycount
;
5292 if (d
->mode
== MODE_HISTORY
)
5297 d
->c_row
-= keycount
;
5298 update_status_notify (gp
, NULL
);
5308 if (d
->mode
== MODE_HISTORY
)
5312 d
->c_col
-= keycount
;
5321 if (d
->mode
== MODE_HISTORY
)
5325 d
->c_col
+= keycount
;
5337 for (i
= 0; global_keys
[i
]; i
++)
5339 if (input_c
== global_keys
[i
]->c
&& global_keys
[i
]->f
)
5341 (*global_keys
[i
]->f
) ();
5353 perl_error (const char *fmt
, ...)
5359 vasprintf (&buf
, fmt
, ap
);
5362 message (ERROR_STR
, ANY_KEY_STR
, "%s", buf
);
5367 do_perl_finalize (WIN
* win
)
5369 struct input_data_s
*in
= win
->data
;
5371 struct userdata_s
*d
= g
->data
;
5373 char *result
= NULL
;
5377 asprintf (&filename
, "%s/perl.pl", config
.datadir
);
5382 if (perl_init_file (filename
, perl_error
))
5385 arg
= pgn_game_to_fen (g
, d
->b
);
5387 if (perl_call_sub (trim (in
->str
), arg
, &result
))
5390 d
->perlfen
= pgn_game_to_fen (g
, d
->b
);
5391 d
->perlflags
= g
->flags
;
5393 if (pgn_board_init_fen (g
, d
->b
, result
) != E_PGN_OK
)
5395 message (ERROR_STR
, ANY_KEY_STR
, "%s", _("FEN parse error."));
5396 pgn_board_init_fen (g
, d
->b
, d
->perlfen
);
5397 g
->flags
= d
->perlflags
;
5403 SET_FLAG (d
->flags
, CF_PERL
);
5404 n
= pgn_tag_find (g
->tag
, "FEN");
5407 d
->oldfen
= strdup (g
->tag
[n
]->value
);
5409 pgn_tag_add (&g
->tag
, (char *) "FEN", result
);
5410 update_status_notify (g
, "%s", ANY_KEY_STR
);
5424 struct input_data_s
*in
;
5426 in
= Calloc (1, sizeof (struct input_data_s
));
5428 in
->efunc
= do_perl_finalize
;
5429 construct_input (_("PERL Subroutine Filter"), NULL
, 1, 0, NULL
, NULL
, NULL
,
5430 0, in
, INPUT_HIST_PERL
, NULL
, -1);
5435 * A macro may contain a key that belongs to another macro so macro_match will
5436 * need to be updated to the new index of the matching macro.
5439 find_macro (struct userdata_s
*d
)
5444 * Macros can't contain macros when in a window.
5450 for (i
= 0; macros
[i
]; i
++)
5452 if ((macros
[i
]->mode
== -1 || macros
[i
]->mode
== d
->mode
) &&
5453 input_c
== macros
[i
]->c
)
5455 input_c
= macros
[i
]->keys
[macros
[i
]->n
++];
5457 if (!macro_depth_n
&& macro_match
> -1)
5459 macro_depth
= Realloc (macro_depth
, (macro_depth_n
+ 1) * sizeof (int));
5460 macro_depth
[macro_depth_n
++] = macro_match
;
5463 macro_depth
= Realloc (macro_depth
, (macro_depth_n
+ 1) * sizeof (int));
5464 macro_depth
[macro_depth_n
++] = i
;
5472 * Resets the position in each macro to the first key.
5478 struct userdata_s
*d
= gp
->data
;
5481 if (macro_depth_n
> 0)
5484 macro_match
= macro_depth
[macro_depth_n
];
5486 if (macros
[macro_match
]->n
>= macros
[macro_match
]->total
)
5489 input_c
= macros
[macro_match
]->keys
[macros
[macro_match
]->n
++];
5494 for (i
= 0; macros
[i
]; i
++)
5506 struct userdata_s
*d
;
5509 gindex
= gtotal
- 1;
5513 if (pgn_history_total (gp
->hp
))
5514 d
->mode
= MODE_HISTORY
;
5517 d
->mode
= MODE_PLAY
;
5518 d
->play_mode
= PLAY_HE
;
5524 d
->pm_frfr
[0] = '\0';
5526 if (d
->mode
== MODE_HISTORY
)
5527 pgn_board_update (gp
, d
->b
, pgn_history_total (gp
->hp
));
5529 update_status_notify (gp
, _("Type %ls for help"),
5530 key_lookup (global_keys
, do_global_help
));
5534 wtimeout (boardw
, WINDOW_TIMEOUT
);
5539 char fdbuf
[8192] = { 0 };
5541 struct timeval tv
= { 0, 0 };
5549 for (i
= 0; i
< gtotal
; i
++)
5553 if (d
->engine
&& d
->engine
->pid
!= -1)
5555 if (d
->engine
->fd
[ENGINE_IN_FD
] > 2)
5557 if (d
->engine
->fd
[ENGINE_IN_FD
] > n
)
5558 n
= d
->engine
->fd
[ENGINE_IN_FD
];
5560 FD_SET (d
->engine
->fd
[ENGINE_IN_FD
], &rfds
);
5563 if (d
->engine
->fd
[ENGINE_OUT_FD
] > 2)
5565 if (d
->engine
->fd
[ENGINE_OUT_FD
] > n
)
5566 n
= d
->engine
->fd
[ENGINE_OUT_FD
];
5568 FD_SET (d
->engine
->fd
[ENGINE_OUT_FD
], &wfds
);
5575 if ((n
= select (n
+ 1, &rfds
, &wfds
, NULL
, &tv
)) > 0)
5577 for (i
= 0; i
< gtotal
; i
++)
5581 if (d
->engine
&& d
->engine
->pid
!= -1)
5583 if (FD_ISSET (d
->engine
->fd
[ENGINE_IN_FD
], &rfds
))
5585 len
= read (d
->engine
->fd
[ENGINE_IN_FD
], fdbuf
,
5590 if (d
->engine
->iobuf
)
5592 Realloc (d
->engine
->iobuf
,
5593 d
->engine
->len
+ len
+ 1);
5595 d
->engine
->iobuf
= Calloc (1, len
+ 1);
5597 memcpy (&(d
->engine
->iobuf
[d
->engine
->len
]),
5599 d
->engine
->len
+= len
;
5600 d
->engine
->iobuf
[d
->engine
->len
] = 0;
5603 * The fdbuf is full or no newline
5604 * was found. So we'll append the next
5605 * read() to this games buffer.
5607 if (d
->engine
->iobuf
[d
->engine
->len
- 1] !=
5611 parse_engine_output (game
[i
], d
->engine
->iobuf
);
5612 free (d
->engine
->iobuf
);
5613 d
->engine
->iobuf
= NULL
;
5618 if (errno
!= EAGAIN
)
5620 cmessage (ERROR_STR
, ANY_KEY_STR
,
5621 "Engine read(): %s",
5623 waitpid (d
->engine
->pid
, &n
, 0);
5631 if (FD_ISSET (d
->engine
->fd
[ENGINE_OUT_FD
], &wfds
))
5633 if (d
->engine
->queue
)
5634 send_engine_command (game
[i
]);
5642 cmessage (ERROR_STR
, ANY_KEY_STR
, "select(): %s",
5652 * This is needed to detect terminal resizing.
5655 if (LINES
!= LINES_OLD
|| COLS
!= COLS_OLD
)
5659 do_window_resize ();
5663 * Finds the top level window in the window stack so we know what
5664 * window the wget_wch()'ed key belongs to.
5668 for (i
= 0; wins
[i
]; i
++);
5671 wtimeout (wp
, WINDOW_TIMEOUT
);
5682 if (macros
&& macro_match
>= 0)
5684 if (macros
[macro_match
]->n
>= macros
[macro_match
]->total
)
5691 input_c
= macros
[macro_match
]->keys
[macros
[macro_match
]->n
++];
5697 if (wget_wch (wp
, &input_c
) == ERR
|| input_c
== KEY_RESIZE
)
5699 if (input_c
== KEY_RESIZE
)
5704 window_resize_all ();
5719 * Run the function associated with the window. When the
5720 * function returns 0 win->efunc is ran (if not NULL) with
5721 * win as the one and only parameter. Then the window is
5724 * The exit function may create another window which will
5725 * mess up the window stack when window_destroy() is called.
5726 * So don't destory the window until the top window is
5727 * destroyable. See window_destroy().
5729 if ((*win
->func
) (win
) == 0)
5732 (*win
->efunc
) (win
);
5735 window_destroy (win
);
5743 if (!keycount
&& status
.notify
)
5744 update_status_notify (gp
, NULL
);
5747 if (TEST_FLAG (d
->flags
, CF_PERL
))
5749 CLEAR_FLAG (d
->flags
, CF_PERL
);
5750 pgn_board_init_fen (gp
, d
->b
, d
->perlfen
);
5751 gp
->flags
= d
->perlflags
;
5753 pgn_tag_add (&gp
->tag
, (char *) "FEN", d
->oldfen
);
5755 d
->perlfen
= d
->oldfen
= NULL
;
5761 if (macros
&& macro_match
< 0)
5764 if ((n
= globalkeys ()) == 1)
5766 if (macro_match
== -1)
5777 for (i
= 0; edit_keys
[i
]; i
++)
5779 if (input_c
== edit_keys
[i
]->c
)
5781 (*edit_keys
[i
]->f
) ();
5787 for (i
= 0; play_keys
[i
]; i
++)
5789 if (input_c
== play_keys
[i
]->c
)
5791 (*play_keys
[i
]->f
) ();
5796 do_play_config_command ();
5799 for (i
= 0; history_keys
[i
]; i
++)
5801 if (input_c
== history_keys
[i
]->c
)
5803 (*history_keys
[i
]->f
) ();
5814 update_status_notify (gp
, NULL
);
5824 usage (const char *pn
, int ret
)
5826 fprintf ((ret
) ? stderr
: stdout
, "%s%s",
5828 _("Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
5829 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"),
5831 _("Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"),
5833 _(" -p Load PGN file.\n"
5834 " -V Validate a game file.\n"
5835 " -S Validate and output a PGN formatted game.\n"
5836 " -R Like -S but write a reduced PGN formatted game.\n"
5837 " -t Also write custom PGN tags from config file.\n"
5838 " -E Stop processing on file parsing error (overrides config).\n"
5839 " -C Enable strict castling (overrides config).\n"
5840 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, overrides config).\n"
5841 " -v Version information.\n" " -h This help text.\n"));
5854 free (config
.engine_cmd
);
5855 free (config
.pattern
);
5856 free (config
.ccfile
);
5857 free (config
.nagfile
);
5858 free (config
.configfile
);
5862 for (i
= 0; config
.keys
[i
]; i
++)
5864 free (config
.keys
[i
]->str
);
5865 free (config
.keys
[i
]);
5873 for (i
= 0; config
.einit
[i
]; i
++)
5874 free (config
.einit
[i
]);
5876 free (config
.einit
);
5880 pgn_tag_free (config
.tag
);
5882 free (config
.datadir
);
5884 if (curses_initialized
)
5887 del_panel (historyp
);
5888 del_panel (statusp
);
5897 del_panel (enginep
);
5910 signal_save_pgn (int sig
)
5914 char *p
= config
.savedirectory
? config
.savedirectory
: config
.datadir
;
5917 asprintf (&buf
, "%s/signal-%i-%li.pgn", p
, sig
, now
);
5919 if (do_game_write (buf
, "w", 0, gtotal
))
5921 cmessage (ERROR_STR
, ANY_KEY_STR
, "%s: %s", p
, strerror (errno
));
5922 update_status_notify (gp
, "%s", _("Save game failed."));
5930 catch_signal (int which
, siginfo_t
*info
, void *ctx
)
5941 if (which
== SIGPIPE
&& quit
)
5944 if (which
== SIGPIPE
)
5945 cmessage (NULL
, ANY_KEY_STR
, "%s", _("Broken pipe. Quitting."));
5948 exit (EXIT_FAILURE
);
5955 do_window_resize ();
5956 keypad (boardw
, TRUE
);
5962 signal_save_pgn (which
);
5970 loading_progress (long total
, long offset
)
5972 int n
= (100 * (offset
/ 100) / (total
/ 100));
5974 if (curses_initialized
)
5975 update_loading_window (n
);
5978 fprintf (stderr
, _("Loading... %i%% (%i games)%c"), n
, gtotal
, '\r');
5986 set_config_defaults ();
5987 set_default_keys ();
5988 filetype
= FILE_NONE
;
5989 pgn_config_set (PGN_PROGRESS
, 1024);
5990 pgn_config_set (PGN_PROGRESS_FUNC
, loading_progress
);
5994 main (int argc
, char *argv
[])
5998 char buf
[FILENAME_MAX
];
5999 char datadir
[FILENAME_MAX
];
6000 int ret
= EXIT_SUCCESS
;
6001 int validate_only
= 0, validate_and_write
= 0;
6002 int write_custom_tags
= 0;
6005 int utf8_pieces
= -1;
6006 struct sigaction sigact
;
6008 setlocale (LC_ALL
, "");
6009 bindtextdomain ("cboard", LOCALE_DIR
);
6010 textdomain ("cboard");
6013 #ifndef HAVE_PROGNAME
6014 __progname
= argv
[0];
6017 if ((config
.pwd
= getpwuid (getuid ())) == NULL
)
6018 err (EXIT_FAILURE
, "getpwuid()");
6020 snprintf (datadir
, sizeof (datadir
), "%s/.cboard", config
.pwd
->pw_dir
);
6021 config
.datadir
= strdup (datadir
);
6022 snprintf (buf
, sizeof (buf
), "%s/cc.data", datadir
);
6023 config
.ccfile
= strdup (buf
);
6024 snprintf (buf
, sizeof (buf
), "%s/nag.data", datadir
);
6025 config
.nagfile
= strdup (buf
);
6026 snprintf (buf
, sizeof (buf
), "%s/config", datadir
);
6027 config
.configfile
= strdup (buf
);
6029 if (stat (datadir
, &st
) == -1)
6031 if (errno
== ENOENT
)
6033 if (mkdir (datadir
, 0755) == -1)
6034 err (EXIT_FAILURE
, "%s", datadir
);
6037 err (EXIT_FAILURE
, "%s", datadir
);
6039 stat (datadir
, &st
);
6042 if (!S_ISDIR (st
.st_mode
))
6043 errx (EXIT_FAILURE
, "%s: %s", datadir
, _("Not a directory."));
6048 while ((opt
= getopt (argc
, argv
, "DCEVtSRhp:vu::")) != -1)
6051 while ((opt
= getopt (argc
, argv
, "ECVtSRhp:vu::")) != -1)
6058 unlink ("libchess.debug");
6059 pgn_config_set (PGN_DEBUG
, 1);
6063 pgn_config_set (PGN_STRICT_CASTLING
, 1);
6066 write_custom_tags
= 1;
6072 pgn_config_set (PGN_REDUCED
, 1);
6074 validate_and_write
= 1;
6079 printf ("%s (%s)\n%s\n%s\n", PACKAGE_STRING
, curses_version (),
6080 COPYRIGHT
, CBOARD_URL
);
6081 exit (EXIT_SUCCESS
);
6083 filetype
= FILE_PGN
;
6084 strncpy (loadfile
, optarg
, sizeof (loadfile
));
6085 loadfile
[sizeof (loadfile
) - 1] = 0;
6088 utf8_pieces
= optarg
? atoi (optarg
) : 1;
6092 usage (argv
[0], EXIT_SUCCESS
);
6096 if ((validate_only
|| validate_and_write
) && !*loadfile
)
6097 usage (argv
[0], EXIT_FAILURE
);
6099 if (access (config
.configfile
, R_OK
) == 0)
6100 parse_rcfile (config
.configfile
);
6103 pgn_config_set (PGN_STOP_ON_ERROR
, 1);
6105 memset (&sigact
, 0, sizeof (sigact
));
6106 sigact
.sa_flags
= SA_SIGINFO
;
6107 sigact
.sa_sigaction
= catch_signal
;
6108 sigaction (SIGPIPE
, &sigact
, NULL
);
6109 sigaction (SIGCONT
, &sigact
, NULL
);
6110 sigaction (SIGSTOP
, &sigact
, NULL
);
6111 sigaction (SIGINT
, &sigact
, NULL
);
6112 sigaction (SIGALRM
, &sigact
, NULL
);
6113 sigaction (SIGTERM
, &sigact
, NULL
);
6114 signal (SIGCHLD
, SIG_IGN
);
6116 srandom (getpid ());
6121 if (pgn_open (loadfile
, "r", &pgn
) != E_PGN_OK
)
6122 err (EXIT_FAILURE
, "%s", loadfile
);
6124 ret
= pgn_parse (pgn
);
6128 //ret = parse_fen_file(loadfile);
6130 case FILE_EPD
: // Not implemented.
6133 // No file specified. Empty game.
6134 ret
= pgn_parse (NULL
);
6136 add_custom_tags (&gp
->tag
);
6140 if (validate_only
|| validate_and_write
)
6142 if (validate_and_write
)
6144 if (pgn_open ("-", "r", &pgn
) != E_PGN_OK
)
6145 err (EXIT_FAILURE
, "pgn_open()");
6147 for (i
= 0; i
< gtotal
; i
++)
6149 if (write_custom_tags
)
6150 add_custom_tags (&game
[i
]->tag
);
6152 pgn_write (pgn
, game
[i
]);
6157 fm_loaded_file
= TRUE
;
6163 else if (ret
== E_PGN_ERR
)
6166 if (utf8_pieces
!= -1)
6167 config
.utf8_pieces
= utf8_pieces
;
6169 init_wchar_pieces ();
6170 yes_wchar
= str_to_wchar (_("y"));
6171 all_wchar
= str_to_wchar (_("a"));
6172 overwrite_wchar
= str_to_wchar (_("o"));
6173 resume_wchar
= str_to_wchar (_("r"));
6174 current_wchar
= str_to_wchar (_("c"));
6175 append_wchar
= str_to_wchar (_("a"));
6176 translatable_tag_names
[0] = _("Event");
6177 translatable_tag_names
[1] = _("Site");
6178 translatable_tag_names
[2] = _("Date");
6179 translatable_tag_names
[3] = _("Round");
6180 translatable_tag_names
[4] = _("White");
6181 translatable_tag_names
[5] = _("Black");
6182 translatable_tag_names
[6] = _("Result");
6186 * This fixes window resizing in an xterm.
6188 if (getenv ("DISPLAY") != NULL
)
6190 putenv ((char *) "LINES=");
6191 putenv ((char *) "COLUMNS=");
6194 if (initscr () == NULL
)
6195 errx (EXIT_FAILURE
, "%s", _("Could not initialize curses."));
6197 curses_initialized
= 1;
6199 if (LINES
< 23 || COLS
< 74)
6202 errx (EXIT_FAILURE
, _("Need at least an 74x23 terminal."));
6208 if (has_colors () == TRUE
&& start_color () == OK
)
6209 init_color_pairs ();
6211 boardw
= newwin (BOARD_HEIGHT
, BOARD_WIDTH
, 0, COLS
- BOARD_WIDTH
);
6212 boardp
= new_panel (boardw
);
6213 historyw
= newwin (HISTORY_HEIGHT
, HISTORY_WIDTH
, LINES
- HISTORY_HEIGHT
,
6214 COLS
- HISTORY_WIDTH
);
6215 historyp
= new_panel (historyw
);
6216 statusw
= newwin (STATUS_HEIGHT
, STATUS_WIDTH
, 0, 0);
6217 statusp
= new_panel (statusw
);
6218 tagw
= newwin (TAG_HEIGHT
, TAG_WIDTH
, STATUS_HEIGHT
+ 1, 0);
6219 tagp
= new_panel (tagw
);
6220 keypad (boardw
, TRUE
);
6221 // leaveok(boardw, TRUE);
6222 leaveok (tagw
, TRUE
);
6223 leaveok (statusw
, TRUE
);
6224 leaveok (historyw
, TRUE
);
6228 draw_window_decor ();
6231 free (w_pawn_wchar
);
6232 free (w_rook_wchar
);
6233 free (w_bishop_wchar
);
6234 free (w_knight_wchar
);
6235 free (w_queen_wchar
);
6236 free (w_king_wchar
);
6237 free (b_pawn_wchar
);
6238 free (b_rook_wchar
);
6239 free (b_bishop_wchar
);
6240 free (b_knight_wchar
);
6241 free (b_queen_wchar
);
6242 free (b_king_wchar
);
6244 free (enpassant_wchar
);
6247 free (overwrite_wchar
);
6248 free (resume_wchar
);
6249 free (current_wchar
);
6250 free (append_wchar
);
6251 free (status
.notify
);
6252 exit (EXIT_SUCCESS
);