1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2002-2013 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include <sys/types.h>
27 #include <sys/socket.h>
42 #ifdef HAVE_SYS_WAIT_H
51 #include "perl-plugin.h"
66 #include "filebrowser.h"
72 #define COPYRIGHT "Copyright (C) 2002-2013 " PACKAGE_BUGREPORT
73 #define LINE_GRAPHIC(c) ((!config.linegraphics) ? ' ' : c)
74 #define ROWTOMATRIX(r) ((8 - r) * 2 + 2 - 1)
75 #define COLTOMATRIX(c) ((c == 1) ? 1 : c * 4 - 3)
76 #define STATUS_HEIGHT 12
77 #define MEGA_BOARD (LINES >= 50 && COLS >= 144)
78 #define BOARD_HEIGHT_MB (50)
79 #define BOARD_WIDTH_MB (98)
80 #define STATUS_WIDTH_MB (COLS - BOARD_WIDTH_MB)
81 #define TAG_HEIGHT_MB (20)
82 #define TAG_WIDTH_MB (COLS - BOARD_WIDTH_MB)
83 #define HISTORY_HEIGHT_MB (LINES - (STATUS_HEIGHT + TAG_HEIGHT_MB + 1))
84 #define HISTORY_WIDTH_MB (COLS - BOARD_WIDTH_MB)
85 #define BIG_BOARD (LINES >= 40 && COLS >= 112)
86 #define BOARD_HEIGHT ((MEGA_BOARD) ? BOARD_HEIGHT_MB : (BIG_BOARD) ? 34 : 18)
87 #define BOARD_WIDTH ((MEGA_BOARD) ? BOARD_WIDTH_MB : (BIG_BOARD) ? 66 : 34)
88 #define STATUS_WIDTH ((MEGA_BOARD) ? STATUS_WIDTH_MB : COLS - BOARD_WIDTH)
89 #define TAG_HEIGHT ((MEGA_BOARD) ? TAG_HEIGHT_MB : LINES - STATUS_HEIGHT - 1)
90 #define TAG_WIDTH ((MEGA_BOARD) ? TAG_WIDTH_MB : COLS - BOARD_WIDTH)
91 #define HISTORY_HEIGHT ((MEGA_BOARD) ? HISTORY_HEIGHT_MB : LINES - BOARD_HEIGHT)
92 #define HISTORY_WIDTH ((MEGA_BOARD) ? HISTORY_WIDTH_MB : COLS - STATUS_WIDTH)
93 #define MAX_VALUE_WIDTH (COLS - 8)
99 static WINDOW
*boardw
;
100 static PANEL
*boardp
;
103 static WINDOW
*statusw
;
104 static PANEL
*statusp
;
105 static WINDOW
*historyw
;
106 static PANEL
*historyp
;
107 static WINDOW
*loadingw
;
108 static PANEL
*loadingp
;
109 static WINDOW
*enginew
;
110 static PANEL
*enginep
;
112 static char gameexp
[255];
113 static char moveexp
[255];
114 static struct itimerval clock_timer
;
115 static int delete_count
= 0;
116 static int markstart
= -1, markend
= -1;
118 static char loadfile
[FILENAME_MAX
];
122 // Loaded filename from the command line or from the file input dialog.
125 FILE_NONE
, FILE_PGN
, FILE_FEN
, FILE_EPD
129 static int nag_total
;
130 static int macro_match
;
132 // Primer movimiento de juego cargado
133 // First move loaded game
134 static char fm_loaded_file
= FALSE
;
135 // Movimiento resultado de la función 'do_play_go'
136 // Movement function result 'do_play_go'
137 static int go_move
= 0;
138 // Controla rotación de tablero
139 // Rotation control board
140 static int rotate
= FALSE
;
142 static int COLS_OLD
, LINES_OLD
;
146 wchar_t *notify
; // The status window notification line buffer.
149 static int curses_initialized
;
151 // When in history mode a full step is to the next move of the same playing
152 // side. Half stepping is alternating sides.
155 static wchar_t *w_pawn_wchar
;
156 static wchar_t *w_rook_wchar
;
157 static wchar_t *w_bishop_wchar
;
158 static wchar_t *w_knight_wchar
;
159 static wchar_t *w_queen_wchar
;
160 static wchar_t *w_king_wchar
;
161 static wchar_t *b_pawn_wchar
;
162 static wchar_t *b_rook_wchar
;
163 static wchar_t *b_bishop_wchar
;
164 static wchar_t *b_knight_wchar
;
165 static wchar_t *b_queen_wchar
;
166 static wchar_t *b_king_wchar
;
167 static wchar_t *empty_wchar
;
168 static wchar_t *enpassant_wchar
;
170 static const char piece_chars
[] = "PpRrNnBbQqKkxx";
171 static const char *f_pieces
[] = {
195 static const bool cb
[8][8] = {
206 static void free_userdata_once(GAME g
);
208 // Posición por rotación de tablero.
209 // Rotation board position.
210 static void rotate_position(int p
)
212 struct userdata_s
*d
= gp
->data
;
214 char fr2
= 8, fc2
= 8;
216 for (fr
= 1; fr
< 9; fr
++) {
219 for (fc
= 1; fc
< 9; fc
++){
222 if (p
== CURSOR_POSITION
&&
223 d
->c_row
== fr
&& d
->c_col
== fc
) {
228 else if (p
== SP_POSITION
&&
229 d
->sp
.row
== fr
&& d
->sp
.col
== fc
) {
234 else if (p
== SPS_POSITION
&&
235 d
->sp
.srow
== fr
&& d
->sp
.scol
== fc
) {
246 void update_cursor(GAME g
, int idx
)
250 int t
= pgn_history_total(g
->hp
);
251 struct userdata_s
*d
= g
->data
;
254 * If not deincremented then r and c would be the next move.
258 if (idx
> t
|| idx
< 0 || !t
|| !g
->hp
[idx
]->move
) {
259 d
->c_row
= 2, d
->c_col
= 5;
264 p
= g
->hp
[idx
]->move
;
273 d
->c_row
= (g
->turn
== WHITE
) ? 8 : 1;
282 d
->c_row
= RANKTOINT(*p
--);
283 d
->c_col
= FILETOINT(*p
);
286 if (d
->mode
== MODE_HISTORY
&& rotate
)
287 rotate_position(CURSOR_POSITION
);
290 static int init_nag()
296 if ((fp
= fopen(config
.nagfile
, "r")) == NULL
) {
297 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s: %s", config
.nagfile
, strerror(errno
));
301 nags
= Realloc(nags
, (i
+2) * sizeof(char *));
302 nags
[i
++] = strdup(_("none"));
306 if (fscanf(fp
, " %[^\n] ", line
) == 1) {
307 nags
= Realloc(nags
, (i
+ 2) * sizeof(char *));
308 nags
[i
++] = strdup(line
);
317 void edit_nag_toggle_item(struct menu_input_s
*m
)
319 struct input_s
*in
= m
->data
;
320 struct input_data_s
*id
= in
->data
;
321 HISTORY
*h
= id
->data
;
324 if (m
->selected
== 0) {
325 for (i
= 0; i
< MAX_PGN_NAG
; i
++)
328 for (i
= 0; m
->items
[i
]; i
++)
329 m
->items
[i
]->selected
= 0;
334 for (i
= 0; i
< MAX_PGN_NAG
; i
++) {
335 if (h
->nag
[i
] == m
->selected
)
336 h
->nag
[i
] = m
->selected
= 0;
339 h
->nag
[i
] = m
->selected
;
346 void edit_nag_save(struct menu_input_s
*m
)
351 void edit_nag_help(struct menu_input_s
*m
)
353 message(_("NAG Menu Keys"), _("[ press any key to continue ]"), "%s",
355 " UP/DOWN - previous/next menu item\n"
356 " HOME/END - first/last menu item\n"
357 " PGDN/PGUP - next/previous page\n"
358 " a-zA-Z0-9 - jump to item\n"
359 " SPACE - toggle selected item\n"
360 " CTRL-X - quit with changes"
364 struct menu_item_s
**get_nag_items(WIN
*win
)
367 struct menu_input_s
*m
= win
->data
;
368 struct input_s
*in
= m
->data
;
369 struct input_data_s
*id
= in
->data
;
370 struct menu_item_s
**items
= m
->items
;
371 HISTORY
*h
= id
->data
;
374 for (i
= 0; items
[i
]; i
++)
378 for (i
= 0; nags
[i
]; i
++) {
379 items
= Realloc(items
, (i
+2) * sizeof(struct menu_item_s
*));
380 items
[i
] = Malloc(sizeof(struct menu_item_s
));
381 items
[i
]->name
= nags
[i
];
382 items
[i
]->value
= NULL
;
384 for (n
= 0; n
< MAX_PGN_NAG
; n
++) {
385 if (h
->nag
[n
] == i
) {
386 items
[i
]->selected
= 1;
393 items
[i
]->selected
= 0;
402 void nag_print(WIN
*win
)
404 struct menu_input_s
*m
= win
->data
;
406 mvwprintw(win
->w
, m
->print_line
, 1, "%-*s", win
->cols
- 2, m
->item
->name
);
409 void edit_nag(void *arg
)
411 struct menu_key_s
**keys
= NULL
;
418 add_menu_key(&keys
, ' ', edit_nag_toggle_item
);
419 add_menu_key(&keys
, CTRL_KEY('x'), edit_nag_save
);
420 add_menu_key(&keys
, KEY_F(1), edit_nag_help
);
421 construct_menu(0, 0, -1, -1, _("Numeric Annotation Glyphs"), 1, get_nag_items
, keys
, arg
,
426 static void *view_nag(void *arg
)
428 HISTORY
*h
= (HISTORY
*)arg
;
430 char line
[LINE_MAX
] = {0};
433 snprintf(buf
, sizeof(buf
), "%s \"%s\"", _("Viewing NAG for"), h
->move
);
440 for (i
= 0; i
< MAX_PGN_NAG
; i
++) {
446 if (h
->nag
[i
] >= nag_total
)
447 strncat(line
, itoa(h
->nag
[i
], buf2
), sizeof(line
)-1);
449 strncat(line
, nags
[h
->nag
[i
]], sizeof(line
)-1);
451 strncat(line
, "\n", sizeof(line
)-1);
454 line
[strlen(line
) - 1] = 0;
455 message(buf
, _("[ press any key to continue ]"), "%s", line
);
459 void view_annotation(HISTORY
*h
)
461 char buf
[MAX_SAN_MOVE_LEN
+ strlen(_("Viewing Annotation for")) + 4];
462 int nag
= 0, comment
= 0;
467 if (h
->comment
&& h
->comment
[0])
473 if (!nag
&& !comment
)
476 snprintf(buf
, sizeof(buf
), "%s \"%s\"", _("Viewing Annotation for"), h
->move
);
479 construct_message(buf
, (nag
) ? _("Any other key to continue") : _("[ press any key to continue ]"), 0, 1,
480 (nag
) ? _("Press 'n' to view NAG") : NULL
,
481 (nag
) ? view_nag
: NULL
, (nag
) ? h
: NULL
, NULL
,
482 (nag
) ? 'n' : 0, 0, "%s", h
->comment
);
484 construct_message(buf
, _("Any other key to continue"), 0, 1, _("Press 'n' to view NAG"), view_nag
, h
, NULL
,
485 'n', 0, "%s", _("No comment text for this move"));
488 int do_game_write(char *filename
, char *mode
, int start
, int end
)
491 struct userdata_s
*d
;
494 i
= pgn_open(filename
, mode
, &pgn
);
496 if (i
== E_PGN_ERR
) {
497 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s\n%s", filename
, strerror(errno
));
500 else if (i
== E_PGN_INVALID
) {
501 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s\n%s", filename
, _("Not a regular file"));
505 for (i
= (start
== -1) ? 0 : start
; i
< end
; i
++) {
507 pgn_write(pgn
, game
[i
]);
508 CLEAR_FLAG(d
->flags
, CF_MODIFIED
);
511 if (pgn_close(pgn
) != E_PGN_OK
)
512 message(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s", strerror(errno
));
515 strncpy(loadfile
, filename
, sizeof(loadfile
));
516 loadfile
[sizeof(loadfile
)-1] = 0;
529 void do_save_game_overwrite_confirm(WIN
*win
)
532 struct save_game_s
*s
= win
->data
;
545 if (do_game_write(s
->filename
, mode
, s
->start
, s
->end
))
546 update_status_notify(gp
, "%s", _("Save game failed."));
548 update_status_notify(gp
, "%s", _("Game saved."));
555 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
558 void save_pgn(char *filename
, int saveindex
)
560 char buf
[FILENAME_MAX
];
562 int end
= (saveindex
== -1) ? gtotal
: saveindex
+ 1;
563 struct save_game_s
*s
;
565 if (filename
[0] != '/' && config
.savedirectory
) {
566 if (stat(config
.savedirectory
, &st
) == -1) {
567 if (errno
== ENOENT
) {
568 if (mkdir(config
.savedirectory
, 0755) == -1) {
569 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s: %s", config
.savedirectory
,
575 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s: %s", config
.savedirectory
,
581 stat(config
.savedirectory
, &st
);
583 if (!S_ISDIR(st
.st_mode
)) {
584 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s: %s", config
.savedirectory
, _("Not a directory."));
588 snprintf(buf
, sizeof(buf
), "%s/%s", config
.savedirectory
, filename
);
592 if (access(filename
, W_OK
) == 0) {
593 s
= Malloc(sizeof(struct save_game_s
));
594 s
->filename
= strdup(filename
);
595 s
->start
= saveindex
;
597 construct_message(NULL
, _("'a' to append, 'o' to overwrite"), 1, 1, NULL
, NULL
,
598 s
, do_save_game_overwrite_confirm
, 0, 0, "%s \"%s\"",
599 _("File exists:"), filename
);
603 if (do_game_write(filename
, "a", saveindex
, end
))
604 update_status_notify(gp
, "%s", _("Save game failed."));
606 update_status_notify(gp
, "%s", _("Game saved."));
609 static int castling_state(GAME g
, BOARD b
, int row
, int col
, int piece
, int mod
)
611 if (pgn_piece_to_int(piece
) == ROOK
&& col
== 7
613 (TEST_FLAG(g
->flags
, GF_WK_CASTLE
) || mod
) &&
614 pgn_piece_to_int(b
[7][4].icon
) == KING
&& isupper(piece
)) {
616 TOGGLE_FLAG(g
->flags
, GF_WK_CASTLE
);
619 else if (pgn_piece_to_int(piece
) == ROOK
&& col
== 0
621 (TEST_FLAG(g
->flags
, GF_WQ_CASTLE
) || mod
) &&
622 pgn_piece_to_int(b
[7][4].icon
) == KING
&& isupper(piece
)) {
624 TOGGLE_FLAG(g
->flags
, GF_WQ_CASTLE
);
627 else if (pgn_piece_to_int(piece
) == ROOK
&& col
== 7
629 (TEST_FLAG(g
->flags
, GF_BK_CASTLE
) || mod
) &&
630 pgn_piece_to_int(b
[0][4].icon
) == KING
&& islower(piece
)) {
632 TOGGLE_FLAG(g
->flags
, GF_BK_CASTLE
);
635 else if (pgn_piece_to_int(piece
) == ROOK
&& col
== 0
637 (TEST_FLAG(g
->flags
, GF_BQ_CASTLE
) || mod
) &&
638 pgn_piece_to_int(b
[0][4].icon
) == KING
&& islower(piece
)) {
640 TOGGLE_FLAG(g
->flags
, GF_BQ_CASTLE
);
643 else if (pgn_piece_to_int(piece
) == KING
&& col
== 4
645 (mod
|| (pgn_piece_to_int(b
[7][7].icon
) == ROOK
&&
646 TEST_FLAG(g
->flags
, GF_WK_CASTLE
))
648 (pgn_piece_to_int(b
[7][0].icon
) == ROOK
&&
649 TEST_FLAG(g
->flags
, GF_WQ_CASTLE
))) && isupper(piece
)) {
651 if (TEST_FLAG(g
->flags
, GF_WK_CASTLE
) ||
652 TEST_FLAG(g
->flags
, GF_WQ_CASTLE
))
653 CLEAR_FLAG(g
->flags
, GF_WK_CASTLE
|GF_WQ_CASTLE
);
655 SET_FLAG(g
->flags
, GF_WK_CASTLE
|GF_WQ_CASTLE
);
659 else if (pgn_piece_to_int(piece
) == KING
&& col
== 4
661 (mod
|| (pgn_piece_to_int(b
[0][7].icon
) == ROOK
&&
662 TEST_FLAG(g
->flags
, GF_BK_CASTLE
))
664 (pgn_piece_to_int(b
[0][0].icon
) == ROOK
&&
665 TEST_FLAG(g
->flags
, GF_BQ_CASTLE
))) && islower(piece
)) {
667 if (TEST_FLAG(g
->flags
, GF_BK_CASTLE
) ||
668 TEST_FLAG(g
->flags
, GF_BQ_CASTLE
))
669 CLEAR_FLAG(g
->flags
, GF_BK_CASTLE
|GF_BQ_CASTLE
);
671 SET_FLAG(g
->flags
, GF_BK_CASTLE
|GF_BQ_CASTLE
);
679 #define IS_ENPASSANT(c) (c == 'x') ? CP_BOARD_ENPASSANT : isupper(c) ? CP_BOARD_WHITE : CP_BOARD_BLACK
680 #define ATTRS(cp) (cp & (A_BOLD|A_STANDOUT|A_BLINK|A_DIM|A_UNDERLINE|A_INVIS|A_REVERSE))
685 w_pawn_wchar
= str_to_wchar (config
.utf8_pieces
? "♙" : "P");
686 w_rook_wchar
= str_to_wchar (config
.utf8_pieces
? "♖" : "R");
687 w_bishop_wchar
= str_to_wchar (config
.utf8_pieces
? "♗" : "B");
688 w_knight_wchar
= str_to_wchar (config
.utf8_pieces
? "♘" : "N");
689 w_queen_wchar
= str_to_wchar (config
.utf8_pieces
? "♕" : "Q");
690 w_king_wchar
= str_to_wchar (config
.utf8_pieces
? "♔" : "K");
691 b_pawn_wchar
= str_to_wchar (config
.utf8_pieces
? "♟" : "p");
692 b_rook_wchar
= str_to_wchar (config
.utf8_pieces
? "♜" : "r");
693 b_bishop_wchar
= str_to_wchar (config
.utf8_pieces
? "♝" : "b");
694 b_knight_wchar
= str_to_wchar (config
.utf8_pieces
? "♞" : "n");
695 b_queen_wchar
= str_to_wchar (config
.utf8_pieces
? "♛" : "q");
696 b_king_wchar
= str_to_wchar (config
.utf8_pieces
? "♚" : "k");
697 empty_wchar
= str_to_wchar (" ");
698 enpassant_wchar
= str_to_wchar ("x");
702 piece_to_wchar (unsigned char p
)
715 return w_bishop_wchar
;
717 return b_bishop_wchar
;
719 return w_knight_wchar
;
721 return b_knight_wchar
;
723 return w_queen_wchar
;
725 return b_queen_wchar
;
731 return enpassant_wchar
;
737 static int piece_can_attack (GAME g
, int rank
, int file
)
739 struct userdata_s
*d
= g
->data
;
740 char *m
, *frfr
= NULL
;
743 int v
= d
->b
[RANKTOBOARD (d
->c_row
)][FILETOBOARD (d
->c_col
)].valid
;
744 int pi
= pgn_piece_to_int (d
->b
[RANKTOBOARD (rank
)][FILETOBOARD (file
)].icon
);
746 ? pgn_piece_to_int (d
->b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
)
747 : pgn_piece_to_int (d
->b
[RANKTOBOARD (d
->c_row
)][FILETOBOARD (d
->c_col
)].icon
);
749 if (pi
== OPEN_SQUARE
|| cpi
== OPEN_SQUARE
|| !VALIDFILE (file
)
750 || !VALIDRANK (rank
))
754 col
= v
? d
->c_col
: d
->sp
.scol
;
755 row
= v
? d
->c_row
: d
->sp
.srow
;
762 m
= malloc(MAX_SAN_MOVE_LEN
+1);
763 m
[0] = INTTOFILE (file
);
764 m
[1] = INTTORANK (rank
);
765 m
[2] = INTTOFILE (col
);
766 m
[3] = INTTORANK (row
);
769 if (d
->sp
.icon
&& v
) {
772 memcpy (b
, d
->b
, sizeof(BOARD
));
773 p
= b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
;
774 b
[RANKTOBOARD (d
->sp
.srow
)][FILETOBOARD (d
->sp
.scol
)].icon
=
775 pgn_int_to_piece (WHITE
, OPEN_SQUARE
);
776 b
[RANKTOBOARD (row
)][FILETOBOARD (col
)].icon
= p
;
778 e
= pgn_validate_move (g
, b
, &m
, &frfr
);
782 return e
== E_PGN_OK
? 1 : 0;
786 e
= pgn_validate_move (g
, d
->b
, &m
, &frfr
);
789 if (!strcmp (m
, "O-O") || !strcmp (m
, "O-O-O"))
793 int sf
= FILETOINT (frfr
[0]), sr
= RANKTOINT (frfr
[1]);
794 int df
= FILETOINT (frfr
[2]);
796 pi
= d
->b
[RANKTOBOARD (sr
)][FILETOBOARD (sf
)].icon
;
797 pi
= pgn_piece_to_int (pi
);
798 if (pi
== PAWN
&& sf
== df
)
804 return e
== E_PGN_OK
? 1 : 0;
807 void print_piece(WINDOW
*w
, int l
, int c
, char p
)
811 for (i
= 0; i
< 13; i
+= 2) {
812 if (p
== piece_chars
[i
] || p
== piece_chars
[i
+ 1]) {
813 for (y
= 0; y
< 3; y
++)
814 mvwprintw(w
, l
+ y
, c
, "%s", f_pieces
[i
+ ff
+ y
]);
821 for (y
= 0; y
< 3; y
++)
822 mvwprintw(w
, l
+ y
, c
, f_pieces
[0]);
829 for (fx
= 1; fx
< 9; fx
++) {
838 // FIXME: BIG_BOARD - start in dwm monocle layout.
839 // Example: lxterminal -e cboard (dmenu, geany, etc.)
840 void update_board_window(GAME g
)
843 int bcol
= 0, brow
= 0;
844 int l
= config
.coordsyleft
;
845 int maxy
= BOARD_HEIGHT
, maxx
= BOARD_WIDTH
;
846 int ncols
= 0, offset
= 1;
847 int rowr
= (MEGA_BOARD
) ? 6 : (BIG_BOARD
) ? 4 : 2;
848 int colr
= (MEGA_BOARD
) ? 12 : (BIG_BOARD
) ? 8 : 4;
849 unsigned coords_y
= 8, cxgc
= 0;
851 struct userdata_s
*d
= g
->data
;
853 if (d
->mode
!= MODE_PLAY
&& d
->mode
!= MODE_EDIT
)
854 update_cursor(g
, g
->hindex
);
871 for (row
= 0; row
< maxy
; row
++) {
885 for (col
= 0; col
< maxx
; col
++) {
887 chtype attrs
= 0, old_attrs
= 0;
892 if (row
== 0 || row
== maxy
- 2) {
894 mvwaddch(boardw
, row
, col
+ l
,
896 ? ACS_LLCORNER
| CP_BOARD_GRAPHICS
897 : ACS_ULCORNER
| CP_BOARD_GRAPHICS
));
898 else if (col
== maxx
- 2)
899 mvwaddch(boardw
, row
, col
+ l
,
901 ? ACS_LRCORNER
| CP_BOARD_GRAPHICS
902 : ACS_URCORNER
| CP_BOARD_GRAPHICS
));
903 else if (!(col
% colr
))
904 mvwaddch(boardw
, row
, col
+ l
,
906 ? ACS_BTEE
| CP_BOARD_GRAPHICS
907 : ACS_TTEE
| CP_BOARD_GRAPHICS
));
910 mvwaddch(boardw
, row
, col
+ l
,
911 LINE_GRAPHIC(ACS_HLINE
| CP_BOARD_GRAPHICS
));
917 if ((row
% 2) && col
== maxx
- 1 &&
918 (coords_y
> 0 && coords_y
< 9)) {
919 wattron(boardw
, CP_BOARD_COORDS
);
921 (BIG_BOARD
) ? row
* ((MEGA_BOARD
) ? 3 : 2)
922 : row
, (l
) ? 0 : col
, "%d",
923 (rotate
) ? coords_y
++ : coords_y
--);
924 wattroff(boardw
, CP_BOARD_COORDS
);
928 if ((col
== 0 || col
== maxx
- 2) && row
!= maxy
- 1) {
930 mvwaddch(boardw
, row
, col
+ l
,
932 ACS_RTEE
| CP_BOARD_GRAPHICS
:
933 ACS_LTEE
| CP_BOARD_GRAPHICS
));
935 mvwaddch(boardw
, row
, col
+ l
,
936 LINE_GRAPHIC(ACS_VLINE
| CP_BOARD_GRAPHICS
));
941 if ((row
% rowr
) && !(col
% colr
) && row
!= maxy
- 1) {
942 mvwaddch(boardw
, row
, col
+ l
,
943 LINE_GRAPHIC(ACS_VLINE
| CP_BOARD_GRAPHICS
));
947 if (!(col
% colr
) && row
!= maxy
- 1) {
948 mvwaddch(boardw
, row
, col
+ l
,
949 LINE_GRAPHIC(ACS_PLUS
| CP_BOARD_GRAPHICS
));
956 attrwhich
= (cb
[brow
][bcol
]) ? WHITE
: BLACK
;
963 if (((ncols
% 2) && !(offset
% 2))
964 || (!(ncols
% 2) && (offset
% 2)))
970 if (BIG_BOARD
&& rotate
) {
971 brow
= inv_int(brow
+ 1) - 1;
972 bcol
= inv_int(bcol
+ 1) - 1;
976 p
= d
->b
[brow
][bcol
].icon
;
978 p
= d
->b
[RANKTOBOARD (brow
)][FILETOBOARD (bcol
)].icon
;
980 int pi
= pgn_piece_to_int(p
);
982 if (config
.details
&&
983 ((!BIG_BOARD
&& d
->b
[RANKTOBOARD (brow
)][FILETOBOARD (bcol
)].enpassant
)
984 || (BIG_BOARD
&& d
->b
[brow
][bcol
].enpassant
))) {
986 attrs
= mix_cp(CP_BOARD_ENPASSANT
,
987 (attrwhich
== WHITE
) ? CP_BOARD_WHITE
: CP_BOARD_BLACK
,
988 ATTRS(CP_BOARD_ENPASSANT
), A_FG_B_BG
);
991 // FIXME: showattacks - BIG_BOARD and rotated board.
992 if (config
.showattacks
&& config
.details
993 && piece_can_attack (g
, brow
, bcol
)) {
994 attrs
= CP_BOARD_ATTACK
;
999 if (config
.validmoves
&&
1000 ((!BIG_BOARD
&& d
->b
[RANKTOBOARD (brow
)][FILETOBOARD (bcol
)].valid
)
1001 || (BIG_BOARD
&& d
->b
[brow
][bcol
].valid
))) {
1005 if (attrwhich
== WHITE
)
1006 attrs
= mix_cp(CP_BOARD_MOVES_WHITE
,
1008 ATTRS(CP_BOARD_MOVES_WHITE
),
1011 attrs
= mix_cp(CP_BOARD_MOVES_BLACK
,
1013 ATTRS(CP_BOARD_MOVES_BLACK
),
1016 else if (p
!= 'x' && !can_attack
)
1017 attrs
= (attrwhich
== WHITE
) ? CP_BOARD_WHITE
: CP_BOARD_BLACK
;
1019 if (BIG_BOARD
&& rotate
) {
1020 brow
= inv_int(brow
+ 1) - 1;
1021 bcol
= inv_int(bcol
+ 1) - 1;
1024 if ((!BIG_BOARD
&& row
== ROWTOMATRIX(d
->c_row
)
1025 && col
== COLTOMATRIX(d
->c_col
))
1026 || (BIG_BOARD
&& brow
+ 1 == inv_int(d
->c_row
)
1027 && bcol
+ 1 == d
->c_col
)) {
1028 attrs
= mix_cp(CP_BOARD_CURSOR
, IS_ENPASSANT(p
),
1029 ATTRS(CP_BOARD_CURSOR
), B_FG_A_BG
);
1032 else if ((!BIG_BOARD
&& row
== ROWTOMATRIX(d
->sp
.srow
) &&
1033 col
== COLTOMATRIX(d
->sp
.scol
)) ||
1034 (BIG_BOARD
&& brow
+ 1 == inv_int(d
->sp
.srow
) &&
1035 bcol
+ 1 == d
->sp
.scol
)) {
1036 attrs
= mix_cp(CP_BOARD_SELECTED
, IS_ENPASSANT(p
),
1037 ATTRS(CP_BOARD_SELECTED
), B_FG_A_BG
);
1041 if (row
== maxy
- 1)
1045 attrs
= mix_cp(CP_BOARD_ATTACK
,
1047 (attrwhich
== WHITE
) ? CP_BOARD_MOVES_WHITE
: CP_BOARD_MOVES_BLACK
:
1048 (attrwhich
== WHITE
) ? CP_BOARD_WHITE
: CP_BOARD_BLACK
,
1049 ATTRS (CP_BOARD_ATTACK
), A_FG_B_BG
);
1053 wmove(boardw
, row
, col
+ ((MEGA_BOARD
) ? 5 : 3) + l
);
1055 mvwaddch(boardw
, row
, col
+ l
, ' ' | attrs
);
1057 if (row
== maxy
- 1 && cxgc
< 8) {
1058 waddch(boardw
, "abcdefgh"[(BIG_BOARD
) ? bcol
: bcol
- 1] | CP_BOARD_COORDS
);
1062 if (old_attrs
== -1) {
1069 if (pi
!= OPEN_SQUARE
&& p
!= 'x' && !can_attack
) {
1070 if (attrwhich
== WHITE
) {
1072 attrs
= CP_BOARD_W_W
;
1074 attrs
= CP_BOARD_W_B
;
1078 attrs
= CP_BOARD_B_W
;
1080 attrs
= CP_BOARD_B_B
;
1086 if (config
.details
&& !can_attack
1087 && castling_state(g
, d
->b
,
1088 (rotate
) ? inv_int(brow
+ 1) - 1: brow
,
1089 (rotate
) ? inv_int(bcol
+ 1) - 1: bcol
, p
, 0))
1090 attrs
= mix_cp(CP_BOARD_CASTLING
, attrs
,
1091 ATTRS(CP_BOARD_CASTLING
),
1095 if (config
.details
&& !can_attack
1096 && castling_state(g
, d
->b
, RANKTOBOARD (brow
),
1097 FILETOBOARD (bcol
), p
, 0)) {
1098 attrs
= mix_cp(CP_BOARD_CASTLING
, attrs
,
1099 ATTRS(CP_BOARD_CASTLING
),
1105 // FIXME: Reimpresión de piezas(+3).
1107 wattron (boardw
, attrs
);
1109 for (i
= 0; i
< 5; i
++)
1110 mvwprintw(boardw
, i
+ brow
* 6 + 1,
1113 if (pi
!= OPEN_SQUARE
)
1114 print_piece(boardw
, brow
* 6 + 2,
1115 bcol
* 12 + 3 + l
, p
);
1118 print_piece(boardw
, brow
* 4 + 1,
1120 (pi
!= OPEN_SQUARE
) ? p
: 0);
1123 wattroff (boardw
, attrs
);
1128 wattron (boardw
, attrs
);
1129 waddwstr (boardw
, piece_to_wchar (pi
!= OPEN_SQUARE
? p
: 0));
1130 wattroff (boardw
, attrs
);
1137 col
+= (MEGA_BOARD
) ? 10 : 6;
1139 waddch(boardw
, ' ' | attrs
);
1157 if (col
!= maxx
- 1)
1158 mvwaddch(boardw
, row
, col
+ l
,
1159 LINE_GRAPHIC(ACS_HLINE
| CP_BOARD_GRAPHICS
));
1179 void invalid_move(int n
, int e
, const char *m
)
1181 if (curses_initialized
)
1182 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s \"%s\" (round #%i)", (e
== E_PGN_AMBIGUOUS
)
1183 ? _("Ambiguous move") : _("Invalid move"), m
, n
);
1185 warnx("%s: %s \"%s\" (round #%i)", loadfile
, (e
== E_PGN_AMBIGUOUS
)
1186 ? _("Ambiguous move") : _("Invalid move"), m
, n
);
1189 void gameover(GAME g
)
1191 struct userdata_s
*d
= g
->data
;
1193 SET_FLAG(g
->flags
, GF_GAMEOVER
);
1194 d
->mode
= MODE_HISTORY
;
1198 static void update_clock(GAME g
, struct itimerval it
)
1200 struct userdata_s
*d
= g
->data
;
1202 if (TEST_FLAG(d
->flags
, CF_CLOCK
) && g
->turn
== WHITE
) {
1203 d
->wclock
.elapsed
.tv_sec
+= it
.it_value
.tv_sec
;
1204 d
->wclock
.elapsed
.tv_usec
+= it
.it_value
.tv_usec
;
1206 if (d
->wclock
.elapsed
.tv_usec
> 1000000 - 1) {
1207 d
->wclock
.elapsed
.tv_sec
+= d
->wclock
.elapsed
.tv_usec
/ 1000000;
1208 d
->wclock
.elapsed
.tv_usec
= d
->wclock
.elapsed
.tv_usec
% 1000000;
1211 if (d
->wclock
.tc
[d
->wclock
.tcn
][1] &&
1212 d
->wclock
.elapsed
.tv_sec
>= d
->wclock
.tc
[d
->wclock
.tcn
][1]) {
1213 pgn_tag_add(&g
->tag
, "Result", "0-1");
1217 else if (TEST_FLAG(d
->flags
, CF_CLOCK
) && g
->turn
== BLACK
) {
1218 d
->bclock
.elapsed
.tv_sec
+= it
.it_value
.tv_sec
;
1219 d
->bclock
.elapsed
.tv_usec
+= it
.it_value
.tv_usec
;
1221 if (d
->bclock
.elapsed
.tv_usec
> 1000000 - 1) {
1222 d
->bclock
.elapsed
.tv_sec
+= d
->bclock
.elapsed
.tv_usec
/ 1000000;
1223 d
->bclock
.elapsed
.tv_usec
= d
->bclock
.elapsed
.tv_usec
% 1000000;
1226 if (d
->bclock
.tc
[d
->bclock
.tcn
][1] &&
1227 d
->bclock
.elapsed
.tv_sec
>= d
->bclock
.tc
[d
->bclock
.tcn
][1]) {
1228 pgn_tag_add(&g
->tag
, "Result", "1-0");
1233 d
->elapsed
.tv_sec
+= it
.it_value
.tv_sec
;
1234 d
->elapsed
.tv_usec
+= it
.it_value
.tv_usec
;
1236 if (d
->elapsed
.tv_usec
> 1000000 - 1) {
1237 d
->elapsed
.tv_sec
+= d
->elapsed
.tv_usec
/ 1000000;
1238 d
->elapsed
.tv_usec
= d
->elapsed
.tv_usec
% 1000000;
1242 static void update_time_control(GAME g
)
1244 struct userdata_s
*d
= g
->data
;
1245 struct clock_s
*clk
= (g
->turn
== WHITE
) ? &d
->wclock
: &d
->bclock
;
1248 clk
->tc
[clk
->tcn
][1] += clk
->incr
;
1250 if (!clk
->tc
[clk
->tcn
][1])
1255 if (!clk
->tc
[clk
->tcn
][0] || clk
->move
>= clk
->tc
[clk
->tcn
][0]) {
1257 clk
->tc
[clk
->tcn
+ 1][1] += abs(clk
->elapsed
.tv_sec
- clk
->tc
[clk
->tcn
][1]);
1258 memset(&clk
->elapsed
, 0, sizeof(clk
->elapsed
));
1263 void update_history_window(GAME g
)
1265 char buf
[HISTORY_WIDTH
- 1];
1268 int t
= pgn_history_total(g
->hp
);
1270 n
= (g
->hindex
+ 1) / 2;
1273 total
= (t
+ 1) / 2;
1278 snprintf(buf
, sizeof(buf
), "%u %s %u%s", n
, _("of"), total
,
1279 (movestep
== 1) ? _(" (ply)") : "");
1281 strncpy(buf
, _("not available"), sizeof(buf
)-1);
1283 buf
[sizeof(buf
)-1] = 0;
1284 mvwprintw(historyw
, 2, 1, "%*s %-*s", 10, _("Move:"),
1285 HISTORY_WIDTH
- 13, buf
);
1287 h
= pgn_history_by_n(g
->hp
, g
->hindex
);
1288 snprintf(buf
, sizeof(buf
), "%s", (h
&& h
->move
) ? h
->move
: _("not available"));
1291 if (h
&& ((h
->comment
) || h
->nag
[0])) {
1292 strncat(buf
, _(" (Annotated"), sizeof(buf
)-1);
1297 strncat(buf
, (n
) ? ",+" : " (+", sizeof(buf
)-1);
1302 strncat(buf
, (n
) ? ",-" : " (-", sizeof(buf
)-1);
1307 strncat(buf
, ")", sizeof(buf
)-1);
1309 mvwprintw(historyw
, 3, 1, "%s %-*s", _("Next move:"),
1310 HISTORY_WIDTH
- 13, buf
);
1312 h
= pgn_history_by_n(g
->hp
, g
->hindex
- 1);
1313 snprintf(buf
, sizeof(buf
), "%s", (h
&& h
->move
) ? h
->move
: _("not available"));
1316 if (h
&& ((h
->comment
) || h
->nag
[0])) {
1317 strncat(buf
, _(" (Annotated"), sizeof(buf
)-1);
1322 strncat(buf
, (n
) ? ",+" : " (+", sizeof(buf
)-1);
1327 strncat(buf
, (n
) ? ",-" : " (-", sizeof(buf
)-1);
1332 strncat(buf
, ")", sizeof(buf
)-1);
1334 mvwprintw(historyw
, 4, 1, "%s %-*s", _("Prev move:"),
1335 HISTORY_WIDTH
- 13, buf
);
1338 void do_validate_move(char *move
)
1340 struct userdata_s
*d
= gp
->data
;
1344 if (TEST_FLAG(d
->flags
, CF_HUMAN
)) {
1345 if ((n
= pgn_parse_move(gp
, d
->b
, &move
, &frfr
)) != E_PGN_OK
) {
1346 invalid_move(d
->n
+ 1, n
, move
);
1350 update_time_control(gp
);
1351 pgn_history_add(gp
, d
->b
, move
);
1352 pgn_switch_turn(gp
);
1355 if ((n
= pgn_validate_move(gp
, d
->b
, &move
, &frfr
)) != E_PGN_OK
) {
1356 invalid_move(d
->n
+ 1, n
, move
);
1360 add_engine_command(gp
, ENGINE_THINKING
, "%s\n",
1361 (config
.engine_protocol
== 1) ? frfr
: move
);
1364 d
->sp
.srow
= d
->sp
.scol
= d
->sp
.icon
= 0;
1366 if (config
.validmoves
)
1367 pgn_reset_valid_moves(d
->b
);
1369 if (TEST_FLAG(gp
->flags
, GF_GAMEOVER
))
1370 d
->mode
= MODE_HISTORY
;
1372 SET_FLAG(d
->flags
, CF_MODIFIED
);
1376 update_history_window(gp
);
1377 update_board_window(gp
);
1381 void do_promotion_piece_finalize(WIN
*win
)
1383 char *p
, *str
= win
->data
;
1385 if (pgn_piece_to_int(win
->c
) == -1)
1388 p
= str
+ strlen(str
);
1389 *p
++ = toupper(win
->c
);
1391 do_validate_move(str
);
1396 static void move_to_engine(GAME g
)
1398 struct userdata_s
*d
= g
->data
;
1402 if (config
.validmoves
&&
1403 !d
->b
[RANKTOBOARD(d
->sp
.row
)][FILETOBOARD(d
->sp
.col
)].valid
)
1406 str
= Malloc(MAX_SAN_MOVE_LEN
+ 1);
1407 snprintf(str
, MAX_SAN_MOVE_LEN
+ 1, "%c%i%c%i",
1408 _("abcdefgh")[d
->sp
.scol
- 1],
1409 d
->sp
.srow
, _("abcdefgh")[d
->sp
.col
- 1], d
->sp
.row
);
1411 piece
= pgn_piece_to_int(d
->b
[RANKTOBOARD(d
->sp
.srow
)][FILETOBOARD(d
->sp
.scol
)].icon
);
1413 if (piece
== PAWN
&& (d
->sp
.row
== 8 || d
->sp
.row
== 1)) {
1414 construct_message(_("Select Pawn Promotion Piece"), _ ("R/N/B/Q"), 1, 1, NULL
, NULL
,
1415 str
, do_promotion_piece_finalize
, 0, 0, "%s", _("R = Rook, N = Knight, B = Bishop, Q = Queen"));
1419 do_validate_move(str
);
1423 static char *clock_to_char(long n
)
1425 static char buf
[16];
1426 int h
= 0, m
= 0, s
= 0;
1429 m
= (n
% 3600) / 60;
1430 s
= (n
% 3600) % 60;
1431 snprintf(buf
, sizeof(buf
), "%.2i:%.2i:%.2i", h
, m
, s
);
1435 static char *timeval_to_char(struct timeval t
, long limit
)
1438 int h
= 0, m
= 0, s
= 0;
1439 int n
= limit
? abs(limit
- t
.tv_sec
) : 0;
1442 m
= (n
% 3600) / 60;
1443 s
= (n
% 3600) % 60;
1444 snprintf(buf
, sizeof(buf
), "%.2i:%.2i:%.2i", h
, m
, s
);
1448 static char *time_control_status(struct clock_s
*clk
)
1450 static char buf
[80] = {0};
1454 if (clk
->tc
[clk
->tcn
][0] && clk
->tc
[clk
->tcn
+ 1][1])
1455 snprintf(buf
, sizeof(buf
), " M%.2i/%s", abs(clk
->tc
[clk
->tcn
][0] - clk
->move
),
1456 clock_to_char(clk
->tc
[clk
->tcn
+ 1][1]));
1457 else if (!clk
->incr
)
1462 strncat(buf
, " I", sizeof(buf
)-1);
1463 strncat(buf
, itoa(clk
->incr
, buf
), sizeof(buf
)-1);
1469 void update_status_window(GAME g
)
1473 char tmp
[15] = {0}, *engine
, *mode
;
1479 struct userdata_s
*d
= g
->data
;
1483 if (!curses_initialized
)
1486 getmaxyx(statusw
, maxy
, maxx
);
1493 wchar_t *loadfilew
= loadfile
[0] ? str_etc (loadfile
, w
, 1) : str_to_wchar (_ ("not available"));
1494 mvwprintw(statusw
, y
++, 1, "%*s %-*ls", 7, _("File:"), w
, loadfilew
);
1496 snprintf(buf
, len
, "%i %s %i", gindex
+ 1, _("of"), gtotal
);
1497 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, _("Game:"), w
, buf
);
1502 if (config
.details
) {
1507 if (TEST_FLAG(d
->flags
, CF_DELETE
)) {
1515 if (TEST_FLAG(g
->flags
, GF_PERROR
)) {
1523 if (TEST_FLAG(d
->flags
, CF_MODIFIED
)) {
1531 pgn_config_get(PGN_STRICT_CASTLING
, &n
);
1541 if (TEST_FLAG(d
->flags
, CF_PERL
)) {
1551 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, _("Flags:"), w
, (tmp
[0]) ? tmp
: "-");
1555 mode
= _("move history");
1564 mode
= _("(empty value)");
1568 snprintf(buf
, len
- 1, "%*s %s", 7, _("Mode:"), mode
);
1570 if (d
->mode
== MODE_PLAY
) {
1571 if (TEST_FLAG(d
->flags
, CF_HUMAN
))
1572 strncat(buf
, _(" (human/human)"), len
- 1);
1573 else if (TEST_FLAG(d
->flags
, CF_ENGINE_LOOP
))
1574 strncat(buf
, _(" (engine/engine)"), len
- 1);
1576 strncat(buf
, (d
->play_mode
== PLAY_EH
) ?
1577 _(" (engine/human)") : _(" (human/engine)"), len
- 1);
1581 mvwprintw(statusw
, y
++, 1, "%-*s", len
, buf
);
1585 switch (d
->engine
->status
) {
1586 case ENGINE_THINKING
:
1587 engine
= _("pondering...");
1590 engine
= _("ready");
1592 case ENGINE_INITIALIZING
:
1593 engine
= _("initializing...");
1595 case ENGINE_OFFLINE
:
1596 engine
= _("offline");
1599 engine
= _("(empty value)");
1604 engine
= _("offline");
1606 mvwprintw(statusw
, y
, 1, "%*s %-*s", 7, _("Engine:"), w
, " ");
1607 wattron(statusw
, CP_STATUS_ENGINE
);
1608 mvwaddstr(statusw
, y
++, 9, engine
);
1609 wattroff(statusw
, CP_STATUS_ENGINE
);
1611 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, _("Turn:"), w
,
1612 (g
->turn
== WHITE
) ? _("white") : _("black"));
1614 strncpy(tmp
, _("white"), sizeof(tmp
)-1);
1615 tmp
[0] = toupper(tmp
[0]);
1616 snprintf(t
, sizeof(t
), "%s%s",
1617 timeval_to_char(d
->wclock
.elapsed
, d
->wclock
.tc
[d
->wclock
.tcn
][1]),
1618 time_control_status(&d
->wclock
));
1619 mvwprintw(statusw
, y
++, 1, "%*s: %-*s", 6, tmp
, w
, t
);
1621 strncpy(tmp
, _("black"), sizeof(tmp
)-1);
1622 tmp
[0] = toupper(tmp
[0]);
1623 snprintf(t
, sizeof(t
), "%s%s",
1624 timeval_to_char(d
->bclock
.elapsed
, d
->bclock
.tc
[d
->bclock
.tcn
][1]),
1625 time_control_status(&d
->bclock
));
1626 mvwprintw(statusw
, y
++, 1, "%*s: %-*s", 6, tmp
, w
, t
);
1628 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, _("Total:"), w
,
1629 clock_to_char(d
->elapsed
.tv_sec
));
1631 // for (i = 0; i < STATUS_WIDTH; i++)
1632 // mvwprintw(stdscr, STATUS_HEIGHT, i, " ");
1635 status
.notify
= str_to_wchar(_("Type F1 for help"));
1637 wattron(stdscr
, CP_STATUS_NOTIFY
);
1638 for (i
= (config
.boardleft
) ? BOARD_WIDTH
: 0;
1639 i
< ((config
.boardleft
) ? COLS
: STATUS_WIDTH
); i
++)
1640 mvwprintw(stdscr
, STATUS_HEIGHT
, i
, " ");
1641 mvwprintw(stdscr
, STATUS_HEIGHT
, CENTERX(STATUS_WIDTH
, status
.notify
)
1642 + ((config
.boardleft
) ? BOARD_WIDTH
: 0),
1643 "%ls", status
.notify
);
1644 wattroff(stdscr
, CP_STATUS_NOTIFY
);
1647 void update_tag_window(TAG
**t
)
1652 for (i
= 0; t
[i
]; i
++) {
1653 wchar_t *namewc
= str_to_wchar (t
[i
]->name
);
1661 w
= TAG_WIDTH
- namel
- 4;
1663 for (i
= 0; t
[i
] && i
< TAG_HEIGHT
- 3; i
++) {
1664 wchar_t *namewc
= str_to_wchar(t
[i
]->name
);
1665 wchar_t *valuewc
= str_etc(t
[i
]->value
, w
, 0);
1667 mvwprintw(tagw
, (i
+ 2), 1, "%*ls: %-*ls", namel
, namewc
, w
, valuewc
);
1672 for (; i
< TAG_HEIGHT
- 3; i
++)
1673 mvwprintw(tagw
, (i
+ 2), 1, "%*s", namel
+ w
+ 2, " ");
1676 void append_enginebuf(GAME g
, char *line
)
1679 struct userdata_s
*d
= g
->data
;
1681 if (d
->engine
->enginebuf
)
1682 for (i
= 0; d
->engine
->enginebuf
[i
]; i
++);
1684 if (i
>= LINES
- 3) {
1685 free(d
->engine
->enginebuf
[0]);
1687 for (i
= 0; d
->engine
->enginebuf
[i
+1]; i
++)
1688 d
->engine
->enginebuf
[i
] = d
->engine
->enginebuf
[i
+1];
1690 d
->engine
->enginebuf
[i
] = strdup(line
);
1693 d
->engine
->enginebuf
= Realloc(d
->engine
->enginebuf
, (i
+ 2) * sizeof(char *));
1694 d
->engine
->enginebuf
[i
++] = strdup(line
);
1695 d
->engine
->enginebuf
[i
] = NULL
;
1699 void update_engine_window(GAME g
)
1702 struct userdata_s
*d
= g
->data
;
1704 if (!d
->engine
|| !d
->engine
->enginebuf
)
1707 wmove(enginew
, 0, 0);
1710 if (d
->engine
->enginebuf
) {
1711 for (i
= 0; d
->engine
->enginebuf
[i
]; i
++)
1712 mvwprintw(enginew
, i
+ 2, 1, "%s", d
->engine
->enginebuf
[i
]);
1715 window_draw_title(enginew
, _("Engine IO Window"), COLS
, CP_MESSAGE_TITLE
,
1719 void update_all(GAME g
)
1721 struct userdata_s
*d
= g
->data
;
1724 * In the middle of a macro. Don't update the screen.
1726 if (macro_match
!= -1)
1730 * No need to update when the engine window is being shown.
1732 if (enginep
&& panel_hidden(enginep
) == ERR
) {
1738 wmove(boardw
, ROWTOMATRIX(d
->c_row
), COLTOMATRIX(d
->c_col
));
1739 update_board_window(g
);
1740 update_status_window(g
);
1741 update_history_window(g
);
1742 update_tag_window(g
->tag
);
1743 update_engine_window(g
);
1748 static void game_next_prev(GAME g
, int n
, int count
)
1754 if (gindex
+ count
> gtotal
- 1) {
1756 gindex
= gtotal
- 1;
1764 if (gindex
- count
< 0) {
1768 gindex
= gtotal
- 1;
1777 static void delete_game(int which
)
1782 struct userdata_s
*d
;
1784 for (i
= 0; i
< gtotal
; i
++) {
1787 if (i
== which
|| TEST_FLAG(d
->flags
, CF_DELETE
)) {
1788 free_userdata_once(game
[i
]);
1793 g
= Realloc(g
, (gi
+ 1) * sizeof(GAME
*));
1794 g
[gi
] = Calloc(1, sizeof(struct game_s
));
1795 memcpy(g
[gi
], game
[i
], sizeof(struct game_s
));
1796 g
[gi
]->tag
= game
[i
]->tag
;
1797 g
[gi
]->history
= game
[i
]->history
;
1798 g
[gi
]->hp
= game
[i
]->hp
;
1806 if (which
+ 1 >= gtotal
)
1807 gindex
= gtotal
- 1;
1812 gindex
= gtotal
- 1;
1815 gp
->hp
= gp
->history
;
1819 * FIXME find across multiple games.
1821 static int find_move_exp(GAME g
, regex_t r
, int which
, int count
)
1829 incr
= (which
== 0) ? -1 : 1;
1831 for (i
= g
->hindex
+ incr
- 1, found
= 0; ; i
+= incr
) {
1832 if (i
== g
->hindex
- 1)
1835 if (i
>= pgn_history_total(g
->hp
))
1838 i
= pgn_history_total(g
->hp
) - 1;
1841 ret
= regexec(&r
, g
->hp
[i
]->move
, 0, 0, 0);
1844 if (count
== ++found
) {
1849 if (ret
!= REG_NOMATCH
) {
1850 regerror(ret
, &r
, errbuf
, sizeof(errbuf
));
1851 cmessage(_("Error Matching Regular Expression"), _("[ press any key to continue ]"), "%s", errbuf
);
1860 static int toggle_delete_flag(int n
)
1863 struct userdata_s
*d
= game
[n
]->data
;
1865 TOGGLE_FLAG(d
->flags
, CF_DELETE
);
1868 for (i
= x
= 0; i
< gtotal
; i
++) {
1871 if (TEST_FLAG(d
->flags
, CF_DELETE
))
1876 cmessage(NULL
, _("[ press any key to continue ]"), "%s", _("Cannot delete last game."));
1878 CLEAR_FLAG(d
->flags
, CF_DELETE
);
1885 static int find_game_exp(char *str
, int which
, int count
)
1887 char *nstr
= NULL
, *exp
= NULL
;
1891 char buf
[255] = {0}, *tmp
;
1894 int incr
= (which
== 0) ? -(1) : 1;
1896 strncpy(buf
, str
, sizeof(buf
)-1);
1899 if (strstr(tmp
, ":") != NULL
) {
1900 nstr
= strsep(&tmp
, ":");
1902 if ((ret
= regcomp(&nexp
, nstr
,
1903 REG_ICASE
|REG_EXTENDED
|REG_NOSUB
)) != 0) {
1904 regerror(ret
, &nexp
, errbuf
, sizeof(errbuf
));
1905 cmessage(_("Error Compiling Regular Expression"), _("[ press any key to continue ]"), "%s", errbuf
);
1913 while (*exp
&& isspace(*exp
))
1919 if ((ret
= regcomp(&vexp
, exp
, REG_EXTENDED
|REG_NOSUB
)) != 0) {
1920 regerror(ret
, &vexp
, errbuf
, sizeof(errbuf
));
1921 cmessage(_("Error Compiling Regular Expression"), _("[ press any key to continue ]"), "%s", errbuf
);
1928 for (g
= gindex
+ incr
, found
= 0; ; g
+= incr
) {
1939 for (t
= 0; game
[g
]->tag
[t
]; t
++) {
1941 if (regexec(&nexp
, game
[g
]->tag
[t
]->name
, 0, 0, 0) == 0) {
1942 if (regexec(&vexp
, game
[g
]->tag
[t
]->value
, 0, 0, 0) == 0) {
1943 if (count
== ++found
) {
1951 if (regexec(&vexp
, game
[g
]->tag
[t
]->value
, 0, 0, 0) == 0) {
1952 if (count
== ++found
) {
1974 * Updates the notification line in the status window then refreshes the
1977 void update_status_notify(GAME g
, char *fmt
, ...)
1980 #ifdef HAVE_VASPRINTF
1987 if (status
.notify
) {
1988 free(status
.notify
);
1989 status
.notify
= NULL
;
1996 #ifdef HAVE_VASPRINTF
1997 vasprintf(&line
, fmt
, ap
);
1999 vsnprintf(line
, sizeof(line
), fmt
, ap
);
2004 free(status
.notify
);
2006 status
.notify
= str_to_wchar(line
);
2008 #ifdef HAVE_VASPRINTF
2013 int rav_next_prev(GAME g
, BOARD b
, int n
)
2017 if ((!g
->ravlevel
&& g
->hindex
&& g
->hp
[g
->hindex
- 1]->rav
== NULL
) ||
2018 (!g
->ravlevel
&& !g
->hindex
&& g
->hp
[g
->hindex
]->rav
== NULL
) ||
2019 (g
->ravlevel
&& g
->hp
[g
->hindex
]->rav
== NULL
))
2022 g
->rav
= Realloc(g
->rav
, (g
->ravlevel
+ 1) * sizeof(RAV
));
2023 g
->rav
[g
->ravlevel
].hp
= g
->hp
;
2024 g
->rav
[g
->ravlevel
].flags
= g
->flags
;
2025 g
->rav
[g
->ravlevel
].fen
= pgn_game_to_fen(g
, b
);
2026 g
->rav
[g
->ravlevel
].hindex
= g
->hindex
;
2027 g
->hp
= (!g
->ravlevel
) ? (g
->hindex
) ? g
->hp
[g
->hindex
- 1]->rav
: g
->hp
[g
->hindex
]->rav
: g
->hp
[g
->hindex
]->rav
;
2030 pgn_board_update(g
, b
, g
->hindex
+ 1);
2034 if (g
->ravlevel
- 1 < 0)
2039 pgn_board_init_fen(g
, b
, g
->rav
[g
->ravlevel
].fen
);
2040 free(g
->rav
[g
->ravlevel
].fen
);
2041 g
->hp
= g
->rav
[g
->ravlevel
].hp
;
2042 g
->flags
= g
->rav
[g
->ravlevel
].flags
;
2043 g
->hindex
= g
->rav
[g
->ravlevel
].hindex
;
2047 static void draw_window_decor()
2049 move_panel(boardp
, 0,
2050 (config
.boardleft
) ? 0 : COLS
- BOARD_WIDTH
);
2051 move_panel(historyp
, LINES
- HISTORY_HEIGHT
,
2052 (config
.boardleft
) ? (MEGA_BOARD
) ? BOARD_WIDTH
: 0 :
2053 (MEGA_BOARD
) ? 0 : COLS
- HISTORY_WIDTH
);
2054 move_panel(statusp
, 0,
2055 (config
.boardleft
) ? BOARD_WIDTH
: 0);
2056 move_panel(tagp
, STATUS_HEIGHT
+ 1,
2057 (config
.boardleft
) ? (MEGA_BOARD
) ? BOARD_WIDTH
:
2060 wbkgd(boardw
, CP_BOARD_WINDOW
);
2061 wbkgd(statusw
, CP_STATUS_WINDOW
);
2062 window_draw_title(statusw
, _("Game Status"), STATUS_WIDTH
,
2063 CP_STATUS_TITLE
, CP_STATUS_BORDER
);
2064 wbkgd(tagw
, CP_TAG_WINDOW
);
2065 window_draw_title(tagw
, _("Roster Tags"), TAG_WIDTH
, CP_TAG_TITLE
,
2067 wbkgd(historyw
, CP_HISTORY_WINDOW
);
2068 window_draw_title(historyw
, _("Move History"), HISTORY_WIDTH
,
2069 CP_HISTORY_TITLE
, CP_HISTORY_BORDER
);
2073 static void do_window_resize()
2075 if (LINES
< 24 || COLS
< 80)
2078 resizeterm(LINES
, COLS
);
2079 wresize(boardw
, BOARD_HEIGHT
, BOARD_WIDTH
);
2080 wresize(historyw
, HISTORY_HEIGHT
, HISTORY_WIDTH
);
2081 wresize(statusw
, STATUS_HEIGHT
, STATUS_WIDTH
);
2082 wresize(tagw
, TAG_HEIGHT
, TAG_WIDTH
);
2083 wmove(boardw
, 0, 0);
2085 wmove(historyw
, 0, 0);
2086 wclrtobot(historyw
);
2089 wmove(statusw
, 0, 0);
2091 draw_window_decor();
2098 memset(&clock_timer
, 0, sizeof(struct itimerval
));
2099 setitimer(ITIMER_REAL
, &clock_timer
, NULL
);
2102 void start_clock(GAME g
)
2104 struct userdata_s
*d
= g
->data
;
2106 if (clock_timer
.it_interval
.tv_usec
)
2109 memset(&d
->elapsed
, 0, sizeof(struct timeval
));
2110 clock_timer
.it_value
.tv_sec
= 0;
2111 clock_timer
.it_value
.tv_usec
= 100000;
2112 clock_timer
.it_interval
.tv_sec
= 0;
2113 clock_timer
.it_interval
.tv_usec
= 100000;
2114 setitimer(ITIMER_REAL
, &clock_timer
, NULL
);
2117 static void update_clocks()
2120 struct userdata_s
*d
;
2121 struct itimerval it
;
2124 getitimer(ITIMER_REAL
, &it
);
2126 for (i
= 0; i
< gtotal
; i
++) {
2129 if (d
&& d
->mode
== MODE_PLAY
) {
2130 if (d
->paused
== 1 || TEST_FLAG(d
->flags
, CF_NEW
))
2132 else if (d
->paused
== -1) {
2133 if (game
[i
]->side
== game
[i
]->turn
) {
2139 update_clock(game
[i
], it
);
2147 update_status_window(gp
);
2153 #define SKIP_SPACE(str) { while (isspace(*str)) str++; }
2155 static int parse_clock_time(char **str
)
2210 static int parse_clock_input(struct clock_s
*clk
, char *str
, int *incr
)
2232 memset(clk
, 0, sizeof(struct clock_s
));
2236 if (strncasecmp(p
, "SD", 2) == 0) {
2242 n
= parse_clock_time(&p
);
2256 /* Sudden death without a previous time control. */
2262 n
= parse_clock_time(&p
);
2268 message(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s (%i)", _("Maximum number of time controls reached"), MAX_TC
);
2273 clk
->tc
[tc
++][1] = n
;
2288 clk
->tc
[clk
->tcn
][1] = (n
<= clk
->elapsed
.tv_sec
) ? clk
->elapsed
.tv_sec
+ n
: n
;
2296 n
= parse_clock_time(&p
);
2316 static int parse_which_clock(struct clock_s
*clk
, char *str
)
2321 memcpy(&tmp
, clk
, sizeof(struct clock_s
));
2323 if (parse_clock_input(&tmp
, str
, &incr
)) {
2324 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), _("Invalid clock specification"));
2328 memcpy(clk
, &tmp
, sizeof(struct clock_s
));
2329 clk
->tc
[clk
->tcn
][1] += incr
;
2333 void do_clock_input_finalize(WIN
*win
)
2335 struct userdata_s
*d
= gp
->data
;
2336 struct input_data_s
*in
= win
->data
;
2346 if (tolower(*p
) == 'w') {
2349 if (parse_which_clock(&d
->wclock
, p
))
2352 else if (tolower(*p
) == 'b') {
2355 if (parse_which_clock(&d
->bclock
, p
))
2359 if (parse_which_clock(&d
->wclock
, p
))
2362 if (parse_which_clock(&d
->bclock
, p
))
2366 if (!d
->wclock
.tc
[0][1] && !d
->bclock
.tc
[0][1])
2367 CLEAR_FLAG(d
->flags
, CF_CLOCK
);
2369 SET_FLAG(d
->flags
, CF_CLOCK
);
2376 void do_engine_command_finalize(WIN
*win
)
2378 struct userdata_s
*d
= gp
->data
;
2379 struct input_data_s
*in
= win
->data
;
2390 x
= d
->engine
->status
;
2391 send_to_engine(gp
, -1, "%s\n", in
->str
);
2392 d
->engine
->status
= x
;
2399 void do_board_details()
2401 config
.details
= (config
.details
) ? 0 : 1;
2404 void do_toggle_strict_castling()
2408 pgn_config_get(PGN_STRICT_CASTLING
, &n
);
2411 pgn_config_set(PGN_STRICT_CASTLING
, 1);
2413 pgn_config_set(PGN_STRICT_CASTLING
, 0);
2416 void do_play_set_clock()
2418 struct input_data_s
*in
;
2420 in
= Calloc(1, sizeof(struct input_data_s
));
2421 in
->efunc
= do_clock_input_finalize
;
2422 construct_input(_("Set Clock"), NULL
, 1, 1,
2423 _ ("Format: [W | B] [+]T[+I] | ++I | M/T [M/T [...] [SD/T]] [+I]\n" \
2424 "T = time (hms), I = increment, M = moves per, SD = sudden death\ne.g., 30m or 4m+12s or 35/90m SD/30m"),
2425 NULL
, NULL
, 0, in
, INPUT_HIST_CLOCK
, -1);
2428 void do_play_toggle_human()
2430 struct userdata_s
*d
= gp
->data
;
2432 TOGGLE_FLAG(d
->flags
, CF_HUMAN
);
2434 if (!TEST_FLAG(d
->flags
, CF_HUMAN
) && pgn_history_total(gp
->hp
)) {
2435 if (init_chess_engine(gp
))
2439 CLEAR_FLAG(d
->flags
, CF_ENGINE_LOOP
);
2442 d
->engine
->status
= ENGINE_READY
;
2445 void do_play_toggle_engine()
2447 struct userdata_s
*d
= gp
->data
;
2449 TOGGLE_FLAG(d
->flags
, CF_ENGINE_LOOP
);
2450 CLEAR_FLAG(d
->flags
, CF_HUMAN
);
2452 if (d
->engine
&& TEST_FLAG(d
->flags
, CF_ENGINE_LOOP
)) {
2453 char *fen
= pgn_game_to_fen (gp
, d
->b
);
2455 pgn_board_update(gp
, d
->b
,
2456 pgn_history_total(gp
->hp
));
2457 add_engine_command(gp
, ENGINE_READY
, "setboard %s\n", fen
);
2463 * This will send a command to the engine skipping the command queue.
2465 void do_play_send_command()
2467 struct userdata_s
*d
= gp
->data
;
2468 struct input_data_s
*in
;
2470 if (!d
->engine
|| d
->engine
->status
== ENGINE_OFFLINE
) {
2471 if (init_chess_engine(gp
))
2475 in
= Calloc(1, sizeof(struct input_data_s
));
2476 in
->efunc
= do_engine_command_finalize
;
2477 construct_input(_("Engine Command"), NULL
, 1, 1, NULL
, NULL
, NULL
, 0, in
, INPUT_HIST_ENGINE
, -1);
2480 void do_play_switch_turn()
2482 struct userdata_s *d = gp->data;
2484 pgn_switch_side(gp);
2485 pgn_switch_turn(gp);
2487 if (!TEST_FLAG(d->flags, CF_HUMAN))
2488 add_engine_command(gp, -1,
2489 (gp->side == WHITE) ? "white\n" : "black\n");
2491 update_status_window(gp);
2494 void do_play_toggle_eh_mode()
2496 struct userdata_s
*d
= gp
->data
;
2498 if (!TEST_FLAG(d
->flags
, CF_HUMAN
)) {
2500 pgn_switch_side(gp
, TRUE
);
2501 d
->play_mode
= (d
->play_mode
) ? PLAY_HE
: PLAY_EH
;
2502 if (gp
->side
== BLACK
)
2503 update_status_notify(gp
, _("Press 'g' to start the game"));
2505 rotate
= (rotate
) ? FALSE
: TRUE
;
2508 message(NULL
, _("[ press any key to continue ]"),
2509 _("You may only switch sides at the start of the \n"
2510 "game. Press ^K or ^N to begin a new game."));
2516 struct userdata_s
*d
= gp
->data
;
2518 if (!pgn_history_total(gp
->hp
))
2522 if (gp
->hindex
- keycount
< 0)
2526 gp
->hindex
-= (keycount
* 2) -1;
2528 gp
->hindex
-= keycount
* 2;
2532 if (gp
->hindex
- 2 < 0)
2542 pgn_history_free(gp
->hp
, gp
->hindex
);
2543 gp
->hindex
= pgn_history_total(gp
->hp
);
2544 pgn_board_update(gp
, d
->b
, gp
->hindex
);
2546 if (d
->engine
&& d
->engine
->status
== ENGINE_READY
) {
2547 char *fen
= pgn_game_to_fen(gp
, d
->b
);
2549 add_engine_command(gp
, ENGINE_READY
, "setboard %s\n", fen
);
2551 d
->engine
->status
= ENGINE_READY
;
2554 update_history_window(gp
);
2557 pgn_switch_side(gp
, FALSE
);
2562 void do_play_toggle_pause()
2564 struct userdata_s
*d
= gp
->data
;
2566 if (!TEST_FLAG(d
->flags
, CF_HUMAN
) && gp
->turn
!=
2572 d
->paused
= (d
->paused
) ? 0 : 1;
2577 struct userdata_s
*d
= gp
->data
;
2579 if (TEST_FLAG(d
->flags
, CF_HUMAN
))
2582 if (fm_loaded_file
&& gp
->side
!= gp
->turn
) {
2583 pgn_switch_side(gp
, FALSE
);
2584 add_engine_command(gp
, ENGINE_THINKING
, "black\n");
2587 add_engine_command(gp
, ENGINE_THINKING
, "go\n");
2589 // Completa la función para que permita seguir jugando al usarla.
2590 // Complete the function to allow continue playing when using.
2591 if (gp
->side
== gp
->turn
)
2592 pgn_switch_side(gp
, FALSE
);
2597 void do_play_config_command()
2602 for (x
= 0; config
.keys
[x
]; x
++) {
2603 if (config
.keys
[x
]->c
== input_c
) {
2604 switch (config
.keys
[x
]->type
) {
2606 add_engine_command(gp
, -1, "%s\n",
2607 config
.keys
[x
]->str
);
2613 add_engine_command(gp
, -1,
2614 "%s %i\n", config
.keys
[x
]->str
, keycount
);
2621 for (w
= 0; w
< keycount
; w
++)
2622 add_engine_command(gp
, -1,
2623 "%s\n", config
.keys
[x
]->str
);
2631 update_status_notify(gp
, NULL
);
2634 void do_play_cancel_selected()
2636 struct userdata_s
*d
= gp
->data
;
2638 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
2640 pgn_reset_valid_moves(d
->b
);
2641 update_status_notify(gp
, NULL
);
2644 void do_play_commit()
2646 struct userdata_s
*d
= gp
->data
;
2648 pushkey
= keycount
= 0;
2649 update_status_notify(gp
, NULL
);
2651 if (!TEST_FLAG(d
->flags
, CF_HUMAN
) &&
2652 (!d
->engine
|| d
->engine
->status
== ENGINE_THINKING
))
2658 d
->sp
.row
= d
->c_row
;
2659 d
->sp
.col
= d
->c_col
;
2662 rotate_position(SP_POSITION
);
2663 rotate_position(SPS_POSITION
);
2668 // Completa la función para que permita seguir jugando cuando se carga un
2669 // archivo pgn (con juego no terminado) que inicie con turno del lado
2671 // Complete the function to allow continue playing when loading a file
2672 // pgn (with unfinished game) you start to turn black side.
2673 if (gp
->side
!= gp
->turn
)
2674 pgn_switch_side(gp
, FALSE
);
2676 if (rotate
&& d
->sp
.icon
)
2677 rotate_position(SPS_POSITION
);
2679 // Envia comando 'go' a Polyglot en el primer movimiento debido a que
2680 // polyglot no envia el movimiento y cboard se queda esperando.
2681 // Send command 'go' to the first movement Polyglot because cboard
2682 // waits to send polyglot movement and this does not make.
2683 if (config
.fmpolyglot
&&
2684 ((gp
->side
== WHITE
&& !gp
->hindex
) || fm_loaded_file
))
2685 add_engine_command(gp
, ENGINE_THINKING
, "go\n");
2688 fm_loaded_file
= FALSE
;
2691 void do_play_select()
2693 struct userdata_s
*d
= gp
->data
;
2695 if (!TEST_FLAG(d
->flags
, CF_HUMAN
) && (!d
->engine
||
2696 d
->engine
->status
== ENGINE_OFFLINE
)) {
2697 if (init_chess_engine(gp
))
2701 if (d
->engine
&& d
->engine
->status
== ENGINE_THINKING
)
2705 do_play_cancel_selected ();
2708 rotate_position(CURSOR_POSITION
);
2710 d
->sp
.icon
= d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
;
2712 if (pgn_piece_to_int(d
->sp
.icon
) == OPEN_SQUARE
) {
2717 if (((islower(d
->sp
.icon
) && gp
->turn
!= BLACK
)
2718 || (isupper(d
->sp
.icon
) && gp
->turn
!= WHITE
))) {
2719 message(NULL
, _("[ press any key to continue ]"), "%s", _("It is not your turn to move. You can switch sides "));
2723 if (pgn_history_total(gp
->hp
)) {
2724 message(NULL
, _("[ press any key to continue ]"), "%s", _("It is not your turn to move. You can switch sides "));
2729 if (pgn_tag_find(gp
->tag
, "FEN") != E_PGN_ERR
)
2732 add_engine_command(gp
, ENGINE_READY
, "black\n");
2733 pgn_switch_turn(gp
);
2735 if (gp
->side
!= BLACK
)
2736 pgn_switch_side(gp
);
2741 d
->sp
.srow
= d
->c_row
;
2742 d
->sp
.scol
= d
->c_col
;
2744 if (config
.validmoves
)
2745 pgn_find_valid_moves(gp
, d
->b
, d
->sp
.scol
, d
->sp
.srow
);
2748 rotate_position(CURSOR_POSITION
);
2749 rotate_position(SPS_POSITION
);
2752 CLEAR_FLAG(d
->flags
, CF_NEW
);
2756 /* FIXME: keys with the same function should comma deliminated. */
2757 static char *build_help(struct key_s
**keys
)
2759 int i
, nlen
= 1, len
, t
, n
;
2766 for (i
= len
= t
= 0; keys
[i
]; i
++) {
2771 if (strlen(keys
[i
]->key
) > nlen
) {
2772 nlen
= strlen(keys
[i
]->key
);
2780 if (strlen(keys
[i
]->d
) > len
)
2781 len
= strlen(keys
[i
]->d
);
2792 for (i
= 0; keys
[i
]; i
++) {
2797 n
= strlen(keys
[i
]->key
);
2807 strcat(buf
, keys
[i
]->key
);
2808 p
= buf
+ strlen(buf
);
2819 strcat(buf
, keys
[i
]->d
);
2825 p
= buf
+ strlen(buf
);
2831 void do_more_help(WIN
*);
2832 void do_main_help(WIN
*win
)
2838 buf
= build_help(play_keys
);
2839 construct_message(_("Play Mode Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0,
2840 NULL
, NULL
, buf
, do_more_help
, 0, 1, "%s", buf
);
2843 buf
= build_help(history_keys
);
2844 construct_message(_("History Mode Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0,
2845 NULL
, NULL
, buf
, do_more_help
, 0, 1, "%s", buf
);
2848 buf
= build_help(edit_keys
);
2849 construct_message(_("Edit Mode Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0,
2850 NULL
, NULL
, buf
, do_more_help
, 0, 1, "%s", buf
);
2853 buf
= build_help(global_keys
);
2854 construct_message(_("Global Game Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0,
2855 NULL
, NULL
, buf
, do_more_help
, 0, 1, "%s", buf
);
2862 void do_more_help(WIN
*win
)
2864 if (win
->c
== KEY_F(1) || win
->c
== CTRL_KEY('g'))
2865 construct_message(_("Command Key Index"),
2866 _ ("p/h/e/g or any other key to quit"), 0, 0,
2867 NULL
, NULL
, NULL
, do_main_help
, 0, 0, "%s",
2869 "p - play mode keys\n"
2870 "h - history mode keys\n"
2871 "e - board edit mode keys\n"
2872 "g - global game keys"
2878 char *buf
= build_help(play_keys
);
2880 construct_message(_("Play Mode Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0, NULL
, NULL
, buf
,
2881 do_more_help
, 0, 1, "%s", buf
);
2884 void do_play_history_mode()
2886 struct userdata_s
*d
= gp
->data
;
2888 if (!pgn_history_total(gp
->hp
) ||
2889 (d
->engine
&& d
->engine
->status
== ENGINE_THINKING
))
2892 d
->mode
= MODE_HISTORY
;
2893 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
2896 void do_play_edit_mode()
2898 struct userdata_s
*d
= gp
->data
;
2900 if (pgn_history_total(gp
->hp
))
2903 pgn_board_init_fen(gp
, d
->b
, NULL
);
2905 d
->mode
= MODE_EDIT
;
2908 void do_edit_insert_finalize(WIN
*win
)
2910 struct userdata_s
*d
= win
->data
;
2912 if (pgn_piece_to_int(win
->c
) == -1)
2915 d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
= win
->c
;
2918 void do_edit_select()
2920 struct userdata_s
*d
= gp
->data
;
2925 d
->sp
.icon
= d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
;
2927 if (pgn_piece_to_int(d
->sp
.icon
) == OPEN_SQUARE
) {
2932 d
->sp
.srow
= d
->c_row
;
2933 d
->sp
.scol
= d
->c_col
;
2936 void do_edit_commit()
2939 struct userdata_s
*d
= gp
->data
;
2941 pushkey
= keycount
= 0;
2942 update_status_notify(gp
, NULL
);
2947 d
->sp
.row
= d
->c_row
;
2948 d
->sp
.col
= d
->c_col
;
2949 p
= d
->b
[RANKTOBOARD(d
->sp
.srow
)][FILETOBOARD(d
->sp
.scol
)].icon
;
2950 d
->b
[RANKTOBOARD(d
->sp
.row
)][FILETOBOARD(d
->sp
.col
)].icon
= p
;
2951 d
->b
[RANKTOBOARD(d
->sp
.srow
)][FILETOBOARD(d
->sp
.scol
)].icon
=
2952 pgn_int_to_piece(gp
->turn
, OPEN_SQUARE
);
2953 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
2956 void do_edit_delete()
2958 struct userdata_s
*d
= gp
->data
;
2961 d
->b
[RANKTOBOARD(d
->sp
.srow
)][FILETOBOARD(d
->sp
.scol
)].icon
=
2962 pgn_int_to_piece(gp
->turn
, OPEN_SQUARE
);
2964 d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
=
2965 pgn_int_to_piece(gp
->turn
, OPEN_SQUARE
);
2967 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
2970 void do_edit_cancel_selected()
2972 struct userdata_s
*d
= gp
->data
;
2974 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
2976 update_status_notify(gp
, NULL
);
2979 void do_edit_switch_turn()
2981 pgn_switch_turn(gp
);
2984 void do_edit_toggle_castle()
2986 struct userdata_s
*d
= gp
->data
;
2988 castling_state(gp
, d
->b
, RANKTOBOARD(d
->c_row
),
2989 FILETOBOARD(d
->c_col
),
2990 d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
, 1);
2993 void do_edit_insert()
2995 struct userdata_s
*d
= gp
->data
;
2997 construct_message(_("Insert Piece"), _("P=pawn, R=rook, N=knight, B=bishop, "), 0, 0, NULL
, NULL
,
2998 d
->b
, do_edit_insert_finalize
, 0, 0, "%s", _("Type the piece letter to insert. Lowercase "));
3001 void do_edit_enpassant()
3003 struct userdata_s
*d
= gp
->data
;
3005 if (d
->c_row
== 6 || d
->c_row
== 3) {
3006 pgn_reset_enpassant(d
->b
);
3007 d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].enpassant
= 1;
3013 char *buf
= build_help(edit_keys
);
3015 construct_message(_("Edit Mode Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0, NULL
, NULL
, buf
,
3016 do_more_help
, 0, 1, "%s", buf
);
3021 struct userdata_s
*d
= gp
->data
;
3022 char *fen
= pgn_game_to_fen(gp
, d
->b
);
3025 pgn_tag_add(&gp
->tag
, "FEN", fen
);
3027 pgn_tag_add(&gp
->tag
, "SetUp", "1");
3028 pgn_tag_sort(gp
->tag
);
3029 pgn_board_update(gp
, d
->b
, gp
->hindex
);
3030 d
->mode
= MODE_PLAY
;
3033 void really_do_annotate_finalize(struct input_data_s
*in
,
3034 struct userdata_s
*d
)
3036 HISTORY
*h
= in
->data
;
3046 len
= strlen(in
->str
);
3047 h
->comment
= Realloc(h
->comment
, len
+1);
3048 strncpy(h
->comment
, in
->str
, len
);
3049 h
->comment
[len
] = 0;
3054 SET_FLAG(d
->flags
, CF_MODIFIED
);
3057 void do_annotate_finalize(WIN
*win
)
3059 struct userdata_s
*d
= gp
->data
;
3060 struct input_data_s
*in
= win
->data
;
3062 really_do_annotate_finalize(in
, d
);
3065 void do_find_move_exp_finalize(int init
, int which
)
3068 struct userdata_s
*d
= gp
->data
;
3069 static int firstrun
;
3074 if (init
|| !firstrun
) {
3078 if ((ret
= regcomp(&r
, moveexp
, REG_EXTENDED
|REG_NOSUB
)) != 0) {
3079 regerror(ret
, &r
, errbuf
, sizeof(errbuf
));
3080 cmessage(_("Error Compiling Regular Expression"), _("[ press any key to continue ]"), "%s", errbuf
);
3087 if ((n
= find_move_exp(gp
, r
,
3088 (which
== -1) ? 0 : 1, (keycount
) ? keycount
: 1)) == -1)
3092 pgn_board_update(gp
, d
->b
, gp
->hindex
);
3095 void do_find_move_exp(WIN
*win
)
3097 struct input_data_s
*in
= win
->data
;
3102 strncpy(moveexp
, in
->str
, sizeof(moveexp
)-1);
3103 moveexp
[sizeof(moveexp
)-1] = 0;
3104 do_find_move_exp_finalize(1, which
);
3112 void do_move_jump_finalize(int n
)
3114 struct userdata_s
*d
= gp
->data
;
3116 if (n
< 0 || n
> (pgn_history_total(gp
->hp
) / 2))
3120 update_status_notify(gp
, NULL
);
3121 gp
->hindex
= (n
) ? n
* 2 - 1 : n
* 2;
3122 pgn_board_update(gp
, d
->b
, gp
->hindex
);
3125 void do_move_jump(WIN
*win
)
3127 struct input_data_s
*in
= win
->data
;
3129 if (!in
->str
|| !isinteger(in
->str
)) {
3137 do_move_jump_finalize(atoi(in
->str
));
3142 struct history_menu_s
{
3150 void free_history_menu_data(struct history_menu_s
**h
)
3157 for (i
= 0; h
[i
]; i
++) {
3165 void get_history_data(HISTORY
**hp
, struct history_menu_s
***menu
, int m
,
3169 int t
= pgn_history_total(hp
);
3170 char buf
[MAX_SAN_MOVE_LEN
+ 4];
3172 struct history_menu_s
**hmenu
= *menu
;
3175 for (n
= 0; hmenu
[n
]; n
++);
3179 for (i
= 0; i
< t
; i
++) {
3180 hmenu
= Realloc(hmenu
, (n
+ 2) * sizeof(struct history_menu_s
*));
3181 hmenu
[n
] = Malloc(sizeof(struct history_menu_s
));
3182 snprintf(buf
, sizeof(buf
), "%c%s%s", (turn
== WHITE
) ? 'W' : 'B',
3183 hp
[i
]->move
, (hp
[i
]->comment
|| hp
[i
]->nag
[0]) ? " !" : "");
3184 hmenu
[n
]->line
= strdup(buf
);
3185 hmenu
[n
]->hindex
= i
;
3186 hmenu
[n
]->indent
= 0;
3187 hmenu
[n
]->ravlevel
= depth
;
3188 hmenu
[n
]->move
= (n
&& depth
> hmenu
[n
-1]->ravlevel
) ? m
++ : m
;
3195 get_history_data(hp
[i
]->rav
, &hmenu
, m
, turn
);
3196 for (n
= 0; hmenu
[n
]; n
++);
3204 turn
= (turn
== WHITE
) ? BLACK
: WHITE
;
3210 void history_draw_update(struct menu_input_s
*m
)
3213 struct userdata_s
*d
= g
->data
;
3215 g
->hindex
= m
->selected
+ 1;
3216 update_cursor(g
, m
->selected
);
3217 pgn_board_update(g
, d
->b
, m
->selected
+ 1);
3220 struct menu_item_s
**get_history_items(WIN
*win
)
3222 struct menu_input_s
*m
= win
->data
;
3224 struct userdata_s
*d
= g
->data
;
3225 struct history_menu_s
**hm
= d
->data
;
3226 struct menu_item_s
**items
= m
->items
;
3230 get_history_data(g
->history
, &hm
, 0,
3231 TEST_FLAG(g
->flags
, GF_BLACK_OPENING
));
3232 m
->selected
= g
->hindex
- 1;
3234 if (m
->selected
< 0)
3237 m
->draw_exit_func
= history_draw_update
;
3243 for (i
= 0; items
[i
]; i
++)
3250 for (i
= 0; hm
[i
]; i
++) {
3251 items
= Realloc(items
, (i
+2) * sizeof(struct menu_item_s
*));
3252 items
[i
] = Malloc(sizeof(struct menu_item_s
));
3253 items
[i
]->name
= hm
[i
]->line
;
3254 items
[i
]->value
= NULL
;
3255 items
[i
]->selected
= 0;
3266 void history_menu_quit(struct menu_input_s
*m
)
3271 void history_menu_exit(WIN
*win
)
3274 struct userdata_s
*d
= g
->data
;
3275 struct history_menu_s
**hm
= d
->data
;
3281 for (i
= 0; hm
[i
]; i
++) {
3291 void history_menu_next(struct menu_input_s
*m
)
3294 struct userdata_s
*d
= g
->data
;
3295 struct history_menu_s
**hm
= d
->data
;
3298 for (t
= 0; hm
[t
]; t
++);
3300 if (m
->selected
+ 1 == t
)
3303 n
= hm
[m
->selected
+ 1]->hindex
;
3310 void history_menu_prev(struct menu_input_s
*m
)
3313 struct userdata_s
*d
= g
->data
;
3314 struct history_menu_s
**hm
= d
->data
;
3317 for (t
= 0; hm
[t
]; t
++);
3319 if (m
->selected
- 1 < 0)
3322 n
= hm
[m
->selected
- 1]->hindex
;
3328 void history_menu_help(struct menu_input_s
*m
)
3330 message("History Menu Help", _("[ press any key to continue ]"), "%s",
3332 " UP/DOWN - previous/next menu item\n"
3333 " HOME/END - first/last menu item\n"
3334 " PGDN/PGUP - next/previous page\n"
3335 " a-zA-Z0-9 - jump to item\n"
3336 " CTRL-a - annotate the selected move\n"
3337 " ENTER - view annotation\n"
3338 " CTRL-d - toggle board details\n"
3339 " ESCAPE/M - return to move history"
3343 void do_annotate_move(HISTORY
*hp
)
3346 struct input_data_s
*in
;
3348 snprintf(buf
, sizeof(buf
), "%s \"%s\"", _("Editing Annotation for"), hp
->move
);
3349 in
= Calloc(1, sizeof(struct input_data_s
));
3351 in
->efunc
= do_annotate_finalize
;
3352 construct_input(buf
, hp
->comment
, MAX_PGN_LINE_LEN
/ INPUT_WIDTH
, 0,
3353 _("Type CTRL-t to edit NAG"), edit_nag
, NULL
, CTRL_KEY('T'), in
, -1, -1);
3356 void history_menu_view_annotation(struct menu_input_s
*m
)
3361 view_annotation(g
->history
[m
->selected
]);
3364 void history_menu_annotate_finalize(WIN
*win
)
3366 struct input_data_s
*in
= win
->data
;
3367 GAME g
= in
->moredata
;
3368 struct userdata_s
*d
= g
->data
;
3369 struct history_menu_s
**hm
= d
->data
;
3371 really_do_annotate_finalize(in
, d
);
3372 free_history_menu_data(hm
);
3374 get_history_data(g
->history
, &hm
, 0, TEST_FLAG(g
->flags
, GF_BLACK_OPENING
));
3376 pushkey
= REFRESH_MENU
;
3379 void history_menu_annotate(struct menu_input_s
*m
)
3383 struct input_data_s
*in
;
3384 HISTORY
*hp
= g
->history
[m
->selected
]; // FIXME RAV
3386 snprintf(buf
, sizeof(buf
), "%s \"%s\"", _("Editing Annotation for"), hp
->move
);
3387 in
= Calloc(1, sizeof(struct input_data_s
));
3389 in
->moredata
= m
->data
;
3390 in
->efunc
= history_menu_annotate_finalize
;
3391 construct_input(buf
, hp
->comment
, MAX_PGN_LINE_LEN
/ INPUT_WIDTH
, 0,
3392 _("Type CTRL-t to edit NAG"), edit_nag
, NULL
, CTRL_KEY('T'), in
, -1, -1);
3395 void history_menu_details(struct menu_input_s
*m
)
3401 void history_menu_print(WIN
*win
)
3403 struct menu_input_s
*m
= win
->data
;
3405 struct userdata_s
*d
= g
->data
;
3406 struct history_menu_s
**hm
= d
->data
;
3407 struct history_menu_s
*h
= hm
[m
->top
];
3409 char *p
= m
->item
->name
;
3410 int line
= m
->print_line
- 2;
3412 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
3421 for (total
= 0; hm
[total
]; total
++);
3423 wattr_get(win
->w
, &attrs
, &pair
, NULL
);
3424 wattroff(win
->w
, COLOR_PAIR(pair
));
3426 mvwaddch(win
->w
, m
->print_line
, 1,
3427 *p
== 'W' ? *p
| mix_cp(CP_BOARD_WHITE
, CP_HISTORY_WINDOW
, ATTRS(CP_BOARD_WHITE
), A_FG_B_BG
) : *p
| mix_cp(CP_BOARD_BLACK
, CP_HISTORY_WINDOW
, ATTRS(CP_BOARD_BLACK
), A_FG_B_BG
));
3430 if (h
->hindex
== 0 && line
== 0)
3431 waddch(win
->w
, ACS_ULCORNER
| CP_HISTORY_MENU_LG
);
3432 else if ((!hm
[h
->hindex
+ (win
->rows
- 5) + 1] && line
== win
->rows
- 5) ||
3433 (m
->top
+ line
== total
- 1))
3434 waddch(win
->w
, ACS_LLCORNER
| CP_HISTORY_MENU_LG
);
3435 else if (hm
[m
->top
+ 1]->ravlevel
!= h
->ravlevel
|| !h
->ravlevel
)
3436 waddch(win
->w
, ACS_LTEE
| CP_HISTORY_MENU_LG
);
3438 waddch(win
->w
, ACS_VLINE
| CP_HISTORY_MENU_LG
);
3441 wattron(win
->w
, COLOR_PAIR(pair
) | attrs
);
3444 for (i
= 2; *p
; p
++, i
++)
3445 waddch(win
->w
, (*p
== '!') ? *p
| A_BOLD
: *p
);
3447 while (i
++ < win
->cols
- 2)
3448 waddch(win
->w
, ' ');
3451 void history_menu(GAME g
)
3453 struct menu_key_s
**keys
= NULL
;
3455 add_menu_key(&keys
, KEY_ESCAPE
, history_menu_quit
);
3456 add_menu_key(&keys
, 'M', history_menu_quit
);
3457 add_menu_key(&keys
, KEY_UP
, history_menu_prev
);
3458 add_menu_key(&keys
, KEY_DOWN
, history_menu_next
);
3459 add_menu_key(&keys
, KEY_F(1), history_menu_help
);
3460 add_menu_key(&keys
, CTRL_KEY('a'), history_menu_annotate
);
3461 add_menu_key(&keys
, CTRL_KEY('d'), history_menu_details
);
3462 add_menu_key(&keys
, '\n', history_menu_view_annotation
);
3463 construct_menu(LINES
, TAG_WIDTH
, 0, config
.boardleft
? BOARD_WIDTH
: 0,
3464 _("Move History Tree"), 1, get_history_items
, keys
, g
,
3465 history_menu_print
, history_menu_exit
);
3468 void do_history_menu()
3473 void do_history_half_move_toggle()
3475 movestep
= (movestep
== 1) ? 2 : 1;
3476 update_history_window(gp
);
3479 void do_history_rotate_board()
3481 rotate
= (rotate
) ? FALSE
: TRUE
;
3484 void do_history_jump_next()
3486 struct userdata_s
*d
= gp
->data
;
3488 pgn_history_next(gp
, d
->b
, (keycount
> 0) ?
3489 config
.jumpcount
* keycount
* movestep
:
3490 config
.jumpcount
* movestep
);
3493 void do_history_jump_prev()
3495 struct userdata_s
*d
= gp
->data
;
3497 pgn_history_prev(gp
, d
->b
, (keycount
) ?
3498 config
.jumpcount
* keycount
* movestep
:
3499 config
.jumpcount
* movestep
);
3502 void do_history_prev()
3504 struct userdata_s
*d
= gp
->data
;
3506 pgn_history_prev(gp
, d
->b
,
3507 (keycount
) ? keycount
* movestep
: movestep
);
3510 void do_history_next()
3512 struct userdata_s
*d
= gp
->data
;
3514 pgn_history_next(gp
, d
->b
, (keycount
) ?
3515 keycount
* movestep
: movestep
);
3518 void do_history_mode_finalize(struct userdata_s
*d
)
3521 d
->mode
= MODE_PLAY
;
3524 void do_history_mode_confirm(WIN
*win
)
3526 struct userdata_s
*d
= gp
->data
;
3531 pgn_history_free(gp
->hp
,
3533 pgn_board_update(gp
, d
->b
,
3534 pgn_history_total(gp
->hp
));
3539 if (pgn_history_rav_new(gp
, d
->b
,
3540 gp
->hindex
) != E_PGN_OK
)
3549 if (!TEST_FLAG(d
->flags
, CF_HUMAN
)) {
3550 char *fen
= pgn_game_to_fen(gp
, d
->b
);
3552 add_engine_command(gp
, ENGINE_READY
, "setboard %s\n", fen
);
3556 do_history_mode_finalize(d
);
3559 void do_history_toggle()
3561 struct userdata_s
*d
= gp
->data
;
3563 // FIXME Resuming from previous history could append to a RAV.
3564 if (gp
->hindex
!= pgn_history_total(gp
->hp
)) {
3566 construct_message(NULL
, _ ("(r)esume or abort"), 0, 1, NULL
, NULL
,
3567 NULL
, do_history_mode_confirm
, 0, 0, "%s",
3568 _("Resuming a game from previous "));
3572 if (TEST_FLAG(gp
->flags
, GF_GAMEOVER
))
3576 if (gp
->side
!= gp
->turn
) {
3577 d
->play_mode
= PLAY_EH
;
3580 d
->play_mode
= PLAY_HE
;
3584 do_history_mode_finalize(d
);
3587 void do_history_annotate()
3591 if (n
&& gp
->hp
[n
- 1]->move
)
3596 do_annotate_move(gp
->hp
[n
]);
3599 void do_history_help()
3601 char *buf
= build_help(history_keys
);
3603 construct_message(_("History Mode Keys (* = can take a repeat count)"), _("[ press any key to continue ]"), 0, 0, NULL
, NULL
, buf
,
3604 do_more_help
, 0, 1, "%s", buf
);
3607 void do_history_find(int which
)
3609 struct input_data_s
*in
;
3612 if (pgn_history_total(gp
->hp
) < 2)
3615 in
= Calloc(1, sizeof(struct input_data_s
));
3616 p
= Malloc(sizeof(int));
3619 in
->efunc
= do_find_move_exp
;
3621 if (!*moveexp
|| which
== 0) {
3622 construct_input(_("Find Move Text Expression"), NULL
, 1, 0, NULL
, NULL
, NULL
,
3623 0, in
, INPUT_HIST_MOVE_EXP
, -1);
3627 do_find_move_exp_finalize(0, which
);
3630 void do_history_find_new()
3635 void do_history_find_prev()
3637 do_history_find(-1);
3640 void do_history_find_next()
3645 void do_history_rav(int which
)
3647 struct userdata_s
*d
= gp
->data
;
3649 rav_next_prev(gp
, d
->b
, which
);
3652 void do_history_rav_next()
3657 void do_history_rav_prev()
3662 void do_history_jump()
3664 struct input_data_s
*in
;
3666 if (pgn_history_total(gp
->hp
) < 2)
3670 in
= Calloc(1, sizeof(struct input_data_s
));
3671 in
->efunc
= do_move_jump
;
3673 construct_input(_("Jump to Move Number"), NULL
, 1, 1, NULL
,
3674 NULL
, NULL
, 0, in
, -1, 0);
3678 do_move_jump_finalize(keycount
);
3681 static void free_userdata_once(GAME g
)
3683 struct userdata_s
*d
= g
->data
;
3691 if (d
->engine
->enginebuf
) {
3694 for (n
= 0; d
->engine
->enginebuf
[n
]; n
++)
3695 free(d
->engine
->enginebuf
[n
]);
3697 free(d
->engine
->enginebuf
);
3700 if (d
->engine
->queue
) {
3703 for (q
= d
->engine
->queue
; *q
; q
++)
3706 free(d
->engine
->queue
);
3724 static void free_userdata()
3728 for (i
= 0; i
< gtotal
; i
++) {
3729 free_userdata_once(game
[i
]);
3730 game
[i
]->data
= NULL
;
3734 void update_loading_window(int n
)
3739 loadingw
= newwin(3, COLS
/ 2, CALCPOSY(3), CALCPOSX(COLS
/ 2));
3740 loadingp
= new_panel(loadingw
);
3741 wbkgd(loadingw
, CP_MESSAGE_WINDOW
);
3744 wmove(loadingw
, 0, 0);
3745 wclrtobot(loadingw
);
3746 wattron(loadingw
, CP_MESSAGE_BORDER
);
3747 box(loadingw
, ACS_VLINE
, ACS_HLINE
);
3748 wattroff(loadingw
, CP_MESSAGE_BORDER
);
3749 mvwprintw(loadingw
, 1, CENTER_INT((COLS
/ 2), 11 +
3750 strlen(itoa(gtotal
, buf
))),
3751 _("Loading... %i%% (%i games)"), n
, gtotal
);
3756 static void init_userdata_once(GAME g
, int n
)
3758 struct userdata_s
*d
= NULL
;
3760 d
= Calloc(1, sizeof(struct userdata_s
));
3762 d
->c_row
= 2, d
->c_col
= 5;
3763 SET_FLAG(d
->flags
, CF_NEW
);
3766 if (pgn_board_init_fen(g
, d
->b
, NULL
) != E_PGN_OK
)
3767 pgn_board_init(d
->b
);
3770 void init_userdata()
3774 for (i
= 0; i
< gtotal
; i
++)
3775 init_userdata_once(game
[i
], i
);
3778 void fix_marks(int *start
, int *end
)
3782 *start
= (*start
< 0) ? 0 : *start
;
3783 *end
= (*end
< 0) ? 0 : *end
;
3785 if (*start
> *end
) {
3791 *end
= (*end
> gtotal
) ? gtotal
: *end
;
3794 void do_new_game_finalize(GAME g
)
3796 struct userdata_s
*d
= g
->data
;
3798 d
->mode
= MODE_PLAY
;
3799 update_status_notify(g
, NULL
);
3802 void do_new_game_from_scratch(WIN
*win
)
3804 if (tolower(win
->c
) != 'y')
3811 add_custom_tags(&gp
->tag
);
3814 do_new_game_finalize(gp
);
3822 add_custom_tags(&gp
->tag
);
3823 init_userdata_once(gp
, gindex
);
3824 do_new_game_finalize(gp
);
3828 void do_game_delete_finalize(int n
)
3830 struct userdata_s
*d
;
3832 delete_game((!n
) ? gindex
: -1);
3834 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
3837 void do_game_delete_confirm(WIN
*win
)
3841 if (tolower(win
->c
) != 'y') {
3847 n
= (int *)win
->data
;
3848 do_game_delete_finalize(*n
);
3852 void do_game_delete()
3856 struct userdata_s
*d
;
3860 cmessage(NULL
, _("[ press any key to continue ]"), "%s", _("Cannot delete last game."));
3866 for (i
= n
= 0; i
< gtotal
; i
++) {
3869 if (TEST_FLAG(d
->flags
, CF_DELETE
))
3874 tmp
= _("Delete the current game?");
3877 cmessage(NULL
, _("[ press any key to continue ]"), "%s", _("Cannot delete last game."));
3881 tmp
= _("Delete all games marked for deletion?");
3884 if (config
.deleteprompt
) {
3885 p
= Malloc(sizeof(int));
3887 construct_message(NULL
, _("[ Yes or No ]"), 1, 1, NULL
, NULL
, p
,
3888 do_game_delete_confirm
, 0, 0, tmp
);
3892 do_game_delete_finalize(n
);
3895 void do_find_game_exp_finalize(int which
)
3897 struct userdata_s
*d
= gp
->data
;
3900 if ((n
= find_game_exp(gameexp
, (which
== -1) ? 0 : 1,
3901 (keycount
) ? keycount
: 1)) == -1) {
3902 update_status_notify(gp
, "%s", _("No matches found"));
3909 if (pgn_history_total(gp
->hp
))
3910 d
->mode
= MODE_HISTORY
;
3912 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
3915 void do_find_game_exp(WIN
*win
)
3917 struct input_data_s
*in
= win
->data
;
3922 strncpy(gameexp
, in
->str
, sizeof(gameexp
));
3923 gameexp
[sizeof(gameexp
)-1] = 0;
3928 do_find_game_exp_finalize(c
);
3936 void do_game_jump_finalize(int n
)
3938 struct userdata_s
*d
;
3940 if (--n
> gtotal
- 1 || n
< 0)
3946 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
3947 update_status_notify(gp
, NULL
);
3950 void do_game_jump(WIN
*win
)
3952 struct input_data_s
*in
= win
->data
;
3954 if (!in
->str
|| !isinteger(in
->str
)) {
3962 do_game_jump_finalize(atoi(in
->str
));
3967 void do_load_file(WIN
*win
)
3969 struct input_data_s
*in
= win
->data
;
3970 char *tmp
= in
->str
;
3971 struct userdata_s
*d
;
3972 PGN_FILE
*pgn
= NULL
;
3980 if ((tmp
= pathfix(tmp
)) == NULL
)
3983 n
= pgn_open(tmp
, "r", &pgn
);
3985 if (n
== E_PGN_ERR
) {
3986 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s\n%s", tmp
, strerror(errno
));
3989 else if (n
== E_PGN_INVALID
) {
3990 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s\n%s", tmp
, _("Not a regular file"));
3997 * FIXME what is the game state after a parse error?
3999 if (pgn_parse(pgn
) == E_PGN_ERR
) {
4000 del_panel(loadingp
);
4008 del_panel(loadingp
);
4013 strncpy(loadfile
, tmp
, sizeof(loadfile
));
4014 loadfile
[sizeof(loadfile
)-1] = 0;
4018 if (pgn_history_total(gp
->hp
))
4019 d
->mode
= MODE_HISTORY
;
4021 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
4023 fm_loaded_file
= TRUE
;
4034 void do_game_save(WIN
*win
)
4036 struct input_data_s
*in
= win
->data
;
4039 char *tmp
= in
->str
;
4040 char tfile
[FILENAME_MAX
];
4043 struct userdata_s
*d
;
4045 if (!tmp
|| (tmp
= pathfix(tmp
)) == NULL
)
4048 if (pgn_is_compressed(tmp
) == E_PGN_ERR
) {
4049 p
= tmp
+ strlen(tmp
) - 1;
4051 if (*p
!= 'n' || *(p
-1) != 'g' || *(p
-2) != 'p' ||
4053 snprintf(tfile
, sizeof(tfile
), "%s.pgn", tmp
);
4059 * When in edit mode, update the FEN tag.
4062 for (i
= 0; i
< gtotal
; i
++) {
4065 if (d
->mode
== MODE_EDIT
) {
4066 char *fen
= pgn_game_to_fen(game
[i
], d
->b
);
4068 pgn_tag_add(&game
[i
]->tag
, "FEN", fen
);
4076 if (d
->mode
== MODE_EDIT
) {
4077 char *fen
= pgn_game_to_fen(game
[n
], d
->b
);
4079 pgn_tag_add(&game
[n
]->tag
, "FEN", fen
);
4094 void do_get_game_save_input(int n
)
4096 struct input_data_s
*in
= Calloc(1, sizeof(struct input_data_s
));
4097 int *p
= Malloc(sizeof(int));
4099 in
->efunc
= do_game_save
;
4103 construct_input(_("Save Game Filename"), loadfile
, 1, 1, _("Type TAB for file browser"),
4104 file_browser
, NULL
, '\t', in
, INPUT_HIST_FILE
, -1);
4107 void do_game_save_multi_confirm(WIN
*win
)
4113 else if (win
->c
== 'a')
4116 update_status_notify(gp
, "%s", _("Save game aborted."));
4120 do_get_game_save_input(i
);
4123 void do_global_about()
4125 cmessage("ABOUT", _("[ press any key to continue ]"), "%s\nUsing %s with %i colors "
4126 "and %i color pairs\n%s",
4127 PACKAGE_STRING
, curses_version(), COLORS
, COLOR_PAIRS
,
4131 void global_game_next_prev(int which
)
4133 struct userdata_s
*d
;
4135 game_next_prev(gp
, (which
== 1) ? 1 : 0,
4136 (keycount
) ? keycount
: 1);
4141 markend
= markstart
+ delete_count
;
4145 markend
= markstart
- delete_count
+ 1;
4146 delete_count
= -1; // to fix gindex in the other direction
4149 fix_marks(&markstart
, &markend
);
4150 do_global_toggle_delete();
4153 if (d
->mode
== MODE_HISTORY
)
4154 pgn_board_update(gp
, d
->b
, gp
->hindex
);
4155 else if (d
->mode
== MODE_PLAY
)
4156 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
4159 void do_global_next_game()
4161 global_game_next_prev(1);
4164 void do_global_prev_game()
4166 global_game_next_prev(0);
4169 void global_find(int which
)
4171 struct input_data_s
*in
;
4177 in
= Calloc(1, sizeof(struct input_data_s
));
4178 p
= Malloc(sizeof(int));
4181 in
->efunc
= do_find_game_exp
;
4183 if (!*gameexp
|| which
== 0) {
4184 construct_input(_("Find Game by Tag Expression"), NULL
, 1, 0,
4185 _("[name expression:]value expression"), NULL
, NULL
, 0, in
,
4186 INPUT_HIST_GAME_EXP
, -1);
4190 do_find_game_exp_finalize(which
);
4193 void do_global_find_new()
4198 void do_global_find_next()
4203 void do_global_find_prev()
4208 void do_global_game_jump()
4214 struct input_data_s
*in
;
4216 in
= Calloc(1, sizeof(struct input_data_s
));
4217 in
->efunc
= do_game_jump
;
4218 construct_input(_("Jump to Game Number"), NULL
, 1, 1, NULL
, NULL
, NULL
, 0, in
,
4223 do_game_jump_finalize(keycount
);
4226 void do_global_toggle_delete()
4235 if (keycount
&& delete_count
== 0) {
4237 delete_count
= keycount
;
4238 update_status_notify(gp
, "%s (delete)", status
.notify
);
4242 if (markstart
>= 0 && markend
>= 0) {
4243 for (i
= markstart
; i
< markend
; i
++) {
4244 if (toggle_delete_flag(i
)) {
4249 gindex
= (delete_count
< 0) ? markstart
: i
- 1;
4252 if (toggle_delete_flag(gindex
))
4256 markstart
= markend
= -1;
4258 update_status_window(gp
);
4261 void do_global_delete_game()
4266 void do_global_tag_edit()
4268 struct userdata_s
*d
= gp
->data
;
4270 edit_tags(gp
, d
->b
, 1);
4273 void do_global_tag_view()
4275 struct userdata_s
*d
= gp
->data
;
4277 edit_tags(gp
, d
->b
, 0);
4280 void do_global_resume_game()
4282 struct input_data_s
*in
;
4284 in
= Calloc(1, sizeof(struct input_data_s
));
4285 in
->efunc
= do_load_file
;
4286 construct_input(_("Load Filename"), NULL
, 1, 1, _("Type TAB for file browser"), file_browser
,
4287 NULL
, '\t', in
, INPUT_HIST_FILE
, -1);
4290 void do_global_save_game()
4293 construct_message(NULL
, _("Type 'c' or 'a' or any other key "), 1, 1, NULL
, NULL
, NULL
,
4294 do_game_save_multi_confirm
, 0, 0, "%s", _("There is more than one game "));
4298 do_get_game_save_input(-1);
4301 void do_global_new_game()
4306 void do_global_copy_game()
4310 struct userdata_s
*d
;
4312 do_global_new_game();
4314 n
= pgn_tag_total(game
[g
]->tag
);
4316 for (i
= 0; i
< n
; i
++)
4317 pgn_tag_add(&gp
->tag
, game
[g
]->tag
[i
]->name
,
4318 game
[g
]->tag
[i
]->value
);
4320 pgn_board_init_fen (gp
, d
->b
, NULL
);
4321 n
= pgn_history_total(game
[g
]->history
);
4324 for (i
= 0; i
< n
; i
++) {
4326 char move
[MAX_SAN_MOVE_LEN
+1] = {0}, *m
= move
;
4328 strcpy (move
, game
[g
]->history
[i
]->move
);
4329 if (pgn_parse_move(gp
, d
->b
, &m
, &frfr
) != E_PGN_OK
) {
4330 SET_FLAG(gp
->flags
, GF_PERROR
);
4334 pgn_history_add(gp
, d
->b
, move
);
4336 pgn_switch_turn(gp
);
4339 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
4342 void do_global_new_all()
4344 construct_message(NULL
, _("[ Yes or No ]"), 1, 1, NULL
, NULL
, NULL
,
4345 do_new_game_from_scratch
, 0, 0, "%s", _("Really start a new game from scratch?"));
4348 void do_quit(WIN
*win
)
4350 if (tolower(win
->c
) != 'y')
4356 void do_global_quit()
4358 if (config
.exitdialogbox
)
4359 construct_message(NULL
, _("[ Yes or No ]"), 1, 1, NULL
, NULL
, NULL
,
4360 do_quit
, 0, 0, _("Want to Quit?"));
4365 void do_global_toggle_engine_window()
4368 enginew
= newwin(LINES
, COLS
, 0, 0);
4369 enginep
= new_panel(enginew
);
4370 window_draw_title(enginew
, _("Engine IO Window"), COLS
, CP_MESSAGE_TITLE
,
4372 hide_panel(enginep
);
4375 if (panel_hidden(enginep
)) {
4376 update_engine_window(gp
);
4380 hide_panel(enginep
);
4384 void do_global_toggle_board_details()
4389 void do_global_toggle_strict_castling()
4391 do_toggle_strict_castling();
4394 // Global and other keys.
4395 static int globalkeys()
4397 struct userdata_s
*d
= gp
->data
;
4401 * These cannot be modified and other game mode keys cannot conflict with
4407 keypad(boardw
, TRUE
);
4408 wmove(stdscr
, 0, 0);
4412 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
4413 markend
= markstart
= 0;
4417 update_status_notify(gp
, NULL
);
4420 if (config
.validmoves
)
4421 pgn_reset_valid_moves(d
->b
);
4428 keycount
= keycount
* 10 + i
;
4432 update_status_notify(gp
, _("Repeat %i"), keycount
);
4435 if (d
->mode
== MODE_HISTORY
)
4439 d
->c_row
+= keycount
;
4448 if (d
->mode
== MODE_HISTORY
)
4452 d
->c_row
-= keycount
;
4453 update_status_notify(gp
, NULL
);
4463 if (d
->mode
== MODE_HISTORY
)
4467 d
->c_col
-= keycount
;
4476 if (d
->mode
== MODE_HISTORY
)
4480 d
->c_col
+= keycount
;
4490 // do_window_resize();
4495 for (i
= 0; global_keys
[i
]; i
++) {
4496 if (input_c
== global_keys
[i
]->c
) {
4497 (*global_keys
[i
]->f
)();
4508 static void perl_error(const char *fmt
, ...)
4514 vasprintf(&buf
, fmt
, ap
);
4517 message(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s", buf
);
4521 static void do_perl_finalize(WIN
*win
)
4523 struct input_data_s
*in
= win
->data
;
4525 struct userdata_s
*d
= g
->data
;
4527 char *result
= NULL
;
4531 asprintf(&filename
, "%s/perl.pl", config
.datadir
);
4536 if (perl_init_file(filename
, perl_error
))
4539 arg
= pgn_game_to_fen(g
, d
->b
);
4541 if (perl_call_sub(trim(in
->str
), arg
, &result
))
4544 d
->perlfen
= pgn_game_to_fen(g
, d
->b
);
4545 d
->perlflags
= g
->flags
;
4547 if (pgn_board_init_fen(g
, d
->b
, result
) != E_PGN_OK
) {
4548 message(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s", _("FEN parse error."));
4549 pgn_board_init_fen(g
, d
->b
, d
->perlfen
);
4550 g
->flags
= d
->perlflags
;
4556 SET_FLAG(d
->flags
, CF_PERL
);
4557 n
= pgn_tag_find(g
->tag
, "FEN");
4560 d
->oldfen
= strdup(g
->tag
[n
]->value
);
4562 pgn_tag_add(&g
->tag
, "FEN", result
);
4563 update_status_notify(g
, "%s", _("[ press any key to continue ]"));
4574 void do_global_perl()
4576 struct input_data_s
*in
;
4578 in
= Calloc(1, sizeof(struct input_data_s
));
4580 in
->efunc
= do_perl_finalize
;
4581 construct_input(_("PERL Subroutine Filter"), NULL
, 1, 0, NULL
, NULL
, NULL
, 0, in
, INPUT_HIST_PERL
, -1);
4586 * A macro may contain a key that belongs to another macro so macro_match will
4587 * need to be updated to the new index of the matching macro.
4589 static void find_macro(struct userdata_s
*d
)
4594 * Macros can't contain macros when in a window.
4600 for (i
= 0; macros
[i
]; i
++) {
4601 if ((macros
[i
]->mode
== -1 || macros
[i
]->mode
== d
->mode
) &&
4602 input_c
== macros
[i
]->c
) {
4603 input_c
= macros
[i
]->keys
[macros
[i
]->n
++];
4605 if (!macro_depth_n
&& macro_match
> -1) {
4606 macro_depth
= realloc(macro_depth
, (macro_depth_n
+ 1) * sizeof(int));
4607 macro_depth
[macro_depth_n
++] = macro_match
;
4610 macro_depth
= realloc(macro_depth
, (macro_depth_n
+ 1) * sizeof(int));
4611 macro_depth
[macro_depth_n
++] = i
;
4619 * Resets the position in each macro to the first key.
4621 static void reset_macros()
4624 struct userdata_s
*d
= gp
->data
;
4627 if (macro_depth_n
> 0) {
4629 macro_match
= macro_depth
[macro_depth_n
];
4631 if (macros
[macro_match
]->n
>= macros
[macro_match
]->total
)
4634 input_c
= macros
[macro_match
]->keys
[macros
[macro_match
]->n
++];
4639 for (i
= 0; macros
[i
]; i
++)
4650 struct userdata_s
*d
;
4653 gindex
= gtotal
- 1;
4657 if (pgn_history_total(gp
->hp
))
4658 d
->mode
= MODE_HISTORY
;
4660 d
->mode
= MODE_PLAY
;
4661 d
->play_mode
= PLAY_HE
;
4664 if (d
->mode
== MODE_HISTORY
)
4665 pgn_board_update(gp
, d
->b
, pgn_history_total(gp
->hp
));
4667 update_status_notify(gp
, "%s", _("Type F1 for help"));
4671 wtimeout(boardw
, WINDOW_TIMEOUT
);
4675 char fdbuf
[8192] = {0};
4677 struct timeval tv
= {0, 0};
4685 for (i
= 0; i
< gtotal
; i
++) {
4688 if (d
->engine
&& d
->engine
->pid
!= -1) {
4689 if (d
->engine
->fd
[ENGINE_IN_FD
] > 2) {
4690 if (d
->engine
->fd
[ENGINE_IN_FD
] > n
)
4691 n
= d
->engine
->fd
[ENGINE_IN_FD
];
4693 FD_SET(d
->engine
->fd
[ENGINE_IN_FD
], &rfds
);
4696 if (d
->engine
->fd
[ENGINE_OUT_FD
] > 2) {
4697 if (d
->engine
->fd
[ENGINE_OUT_FD
] > n
)
4698 n
= d
->engine
->fd
[ENGINE_OUT_FD
];
4700 FD_SET(d
->engine
->fd
[ENGINE_OUT_FD
], &wfds
);
4706 if ((n
= select(n
+ 1, &rfds
, &wfds
, NULL
, &tv
)) > 0) {
4707 for (i
= 0; i
< gtotal
; i
++) {
4710 if (d
->engine
&& d
->engine
->pid
!= -1) {
4711 if (FD_ISSET(d
->engine
->fd
[ENGINE_IN_FD
], &rfds
)) {
4712 len
= read(d
->engine
->fd
[ENGINE_IN_FD
], fdbuf
,
4716 if (d
->engine
->iobuf
)
4717 d
->engine
->iobuf
= Realloc(d
->engine
->iobuf
, d
->engine
->len
+ len
+ 1);
4719 d
->engine
->iobuf
= Calloc(1, len
+ 1);
4721 memcpy(&(d
->engine
->iobuf
[d
->engine
->len
]), &fdbuf
, len
);
4722 d
->engine
->len
+= len
;
4723 d
->engine
->iobuf
[d
->engine
->len
] = 0;
4726 * The fdbuf is full or no newline
4727 * was found. So we'll append the next
4728 * read() to this games buffer.
4730 if (d
->engine
->iobuf
[d
->engine
->len
- 1] != '\n')
4733 parse_engine_output(game
[i
], d
->engine
->iobuf
);
4734 free(d
->engine
->iobuf
);
4735 d
->engine
->iobuf
= NULL
;
4738 else if (len
== -1) {
4739 if (errno
!= EAGAIN
) {
4740 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "Engine read(): %s",
4742 waitpid(d
->engine
->pid
, &n
, 0);
4750 if (FD_ISSET(d
->engine
->fd
[ENGINE_OUT_FD
], &wfds
)) {
4751 if (d
->engine
->queue
)
4752 send_engine_command(game
[i
]);
4759 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "select(): %s", strerror(errno
));
4768 * This is needed to detect terminal resizing.
4771 if (LINES
!= LINES_OLD
|| COLS
!= COLS_OLD
){
4778 * Finds the top level window in the window stack so we know what
4779 * window the wgetch()ed key belongs to.
4782 for (i
= 0; wins
[i
]; i
++);
4785 wtimeout(wp
, WINDOW_TIMEOUT
);
4794 if (macros
&& macro_match
>= 0) {
4795 if (macros
[macro_match
]->n
>= macros
[macro_match
]->total
)
4798 input_c
= macros
[macro_match
]->keys
[macros
[macro_match
]->n
++];
4803 if ((input_c
= wgetch(wp
)) == ERR
)
4814 keypad(boardw
, TRUE
);
4815 wmove(stdscr
, 0, 0);
4823 * Run the function associated with the window. When the
4824 * function returns 0 win->efunc is ran (if not NULL) with
4825 * win as the one and only parameter. Then the window is
4828 * The exit function may create another window which will
4829 * mess up the window stack when window_destroy() is called.
4830 * So don't destory the window until the top window is
4831 * destroyable. See window_destroy().
4833 if ((*win
->func
)(win
) == 0) {
4838 window_destroy(win
);
4846 if (!keycount
&& status
.notify
)
4847 update_status_notify(gp
, NULL
);
4850 if (TEST_FLAG(d
->flags
, CF_PERL
)) {
4851 CLEAR_FLAG(d
->flags
, CF_PERL
);
4852 pgn_board_init_fen(gp
, d
->b
, d
->perlfen
);
4853 gp
->flags
= d
->perlflags
;
4855 pgn_tag_add(&gp
->tag
, "FEN", d
->oldfen
);
4857 d
->perlfen
= d
->oldfen
= NULL
;
4863 if (macros
&& macro_match
< 0)
4866 if ((n
= globalkeys()) == 1) {
4867 if (macro_match
== -1)
4877 for (i
= 0; edit_keys
[i
]; i
++) {
4878 if (input_c
== edit_keys
[i
]->c
) {
4879 (*edit_keys
[i
]->f
)();
4885 for (i
= 0; play_keys
[i
]; i
++) {
4886 if (input_c
== play_keys
[i
]->c
) {
4887 (*play_keys
[i
]->f
)();
4892 do_play_config_command();
4895 for (i
= 0; history_keys
[i
]; i
++) {
4896 if (input_c
== history_keys
[i
]->c
) {
4897 (*history_keys
[i
]->f
)();
4908 update_status_notify(gp
, NULL
);
4917 void usage(const char *pn
, int ret
)
4919 fprintf((ret
) ? stderr
: stdout
, "%s", _(
4921 "Usage: cboard [-hvCD] [-u [N]] [-p [-VtRSE] <file>]\n"
4922 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"
4924 "Usage: cboard [-hvC] [-u [N]] [-p [-VtRSE] <file>]\n"
4926 " -p Load PGN file.\n"
4927 " -V Validate a game file.\n"
4928 " -S Validate and output a PGN formatted game.\n"
4929 " -R Like -S but write a reduced PGN formatted game.\n"
4930 " -t Also write custom PGN tags from config file.\n"
4931 " -E Stop processing on file parsing error (overrides config).\n"
4932 " -C Enable strict castling (overrides config).\n"
4933 " -u Enable/disable UTF-8 pieces (1=enable, 0=disable, moverrides config).\n"
4934 " -v Version information.\n"
4935 " -h This help text.\n"));
4947 free(config
.engine_cmd
);
4948 free(config
.pattern
);
4949 free(config
.ccfile
);
4950 free(config
.nagfile
);
4951 free(config
.configfile
);
4954 for (i
= 0; config
.keys
[i
]; i
++) {
4955 free(config
.keys
[i
]->str
);
4956 free(config
.keys
[i
]);
4963 for (i
= 0; config
.einit
[i
]; i
++)
4964 free(config
.einit
[i
]);
4970 pgn_tag_free(config
.tag
);
4972 free(config
.datadir
);
4974 if (curses_initialized
) {
4976 del_panel(historyp
);
4997 static void signal_save_pgn(int sig
)
5001 char *p
= config
.savedirectory
? config
.savedirectory
: config
.datadir
;
5004 asprintf(&buf
, "%s/signal-%i-%li.pgn", p
, sig
, now
);
5006 if (do_game_write(buf
, "w", 0, gtotal
)) {
5007 cmessage(_("[ ERROR ]"), _("[ press any key to continue ]"), "%s: %s", p
, strerror(errno
));
5008 update_status_notify(gp
, "%s", _("Save game failed."));
5015 void catch_signal(int which
)
5022 if (which
== SIGPIPE
&& quit
)
5025 if (which
== SIGPIPE
)
5026 cmessage(NULL
, _("[ press any key to continue ]"), "%s", _("Broken pipe. Quitting."));
5036 keypad(boardw
, TRUE
);
5045 signal_save_pgn(which
);
5052 void loading_progress(long total
, long offset
)
5054 int n
= (100 * (offset
/ 100) / (total
/ 100));
5056 if (curses_initialized
)
5057 update_loading_window(n
);
5059 fprintf(stderr
, _("Loading... %i%% (%i games)\r"), n
, gtotal
);
5064 static void set_defaults()
5066 set_config_defaults();
5068 filetype
= FILE_NONE
;
5069 pgn_config_set(PGN_PROGRESS
, 1024);
5070 pgn_config_set(PGN_PROGRESS_FUNC
, loading_progress
);
5073 int main(int argc
, char *argv
[])
5077 char buf
[FILENAME_MAX
];
5078 char datadir
[FILENAME_MAX
];
5079 int ret
= EXIT_SUCCESS
;
5080 int validate_only
= 0, validate_and_write
= 0;
5081 int write_custom_tags
= 0;
5084 int utf8_pieces
= -1;
5086 setlocale (LC_ALL
, "");
5087 bindtextdomain ("cboard", LOCALE_DIR
);
5088 textdomain ("cboard");
5091 #ifndef HAVE_PROGNAME
5092 __progname
= argv
[0];
5095 if ((config
.pwd
= getpwuid(getuid())) == NULL
)
5096 err(EXIT_FAILURE
, "getpwuid()");
5098 snprintf(datadir
, sizeof(datadir
), "%s/.cboard", config
.pwd
->pw_dir
);
5099 config
.datadir
= strdup(datadir
);
5100 snprintf(buf
, sizeof(buf
), "%s/cc.data", datadir
);
5101 config
.ccfile
= strdup(buf
);
5102 snprintf(buf
, sizeof(buf
), "%s/nag.data", datadir
);
5103 config
.nagfile
= strdup(buf
);
5104 snprintf(buf
, sizeof(buf
), "%s/config", datadir
);
5105 config
.configfile
= strdup(buf
);
5107 if (stat(datadir
, &st
) == -1) {
5108 if (errno
== ENOENT
) {
5109 if (mkdir(datadir
, 0755) == -1)
5110 err(EXIT_FAILURE
, "%s", datadir
);
5113 err(EXIT_FAILURE
, "%s", datadir
);
5118 if (!S_ISDIR(st
.st_mode
))
5119 errx(EXIT_FAILURE
, "%s: %s", datadir
, _("Not a directory."));
5124 while ((opt
= getopt(argc
, argv
, "DCEVtSRhp:vu::")) != -1) {
5126 while ((opt
= getopt(argc
, argv
, "ECVtSRhp:vu::")) != -1) {
5131 unlink("libchess.debug");
5132 pgn_config_set(PGN_DEBUG
, 1);
5136 pgn_config_set(PGN_STRICT_CASTLING
, 1);
5139 write_custom_tags
= 1;
5145 pgn_config_set(PGN_REDUCED
, 1);
5147 validate_and_write
= 1;
5152 printf("%s (%s)\n%s\n", PACKAGE_STRING
, curses_version(),
5156 filetype
= FILE_PGN
;
5157 strncpy(loadfile
, optarg
, sizeof(loadfile
));
5158 loadfile
[sizeof(loadfile
)-1] = 0;
5161 utf8_pieces
= optarg
? atoi (optarg
): 1;
5165 usage(argv
[0], EXIT_SUCCESS
);
5169 if ((validate_only
|| validate_and_write
) && !*loadfile
)
5170 usage(argv
[0], EXIT_FAILURE
);
5172 if (access(config
.configfile
, R_OK
) == 0)
5173 parse_rcfile(config
.configfile
);
5176 pgn_config_set(PGN_STOP_ON_ERROR
, 1);
5178 signal(SIGPIPE
, catch_signal
);
5179 signal(SIGCONT
, catch_signal
);
5180 signal(SIGSTOP
, catch_signal
);
5181 signal(SIGINT
, catch_signal
);
5182 signal(SIGALRM
, catch_signal
);
5183 signal(SIGTERM
, catch_signal
);
5189 if (pgn_open(loadfile
, "r", &pgn
) != E_PGN_OK
)
5190 err(EXIT_FAILURE
, "%s", loadfile
);
5192 ret
= pgn_parse(pgn
);
5196 //ret = parse_fen_file(loadfile);
5198 case FILE_EPD
: // Not implemented.
5201 // No file specified. Empty game.
5202 ret
= pgn_parse(NULL
);
5204 add_custom_tags(&gp
->tag
);
5208 if (validate_only
|| validate_and_write
) {
5209 if (validate_and_write
) {
5210 if (pgn_open("-", "r", &pgn
) != E_PGN_OK
)
5211 err(EXIT_FAILURE
, "pgn_open()");
5213 for (i
= 0; i
< gtotal
; i
++) {
5214 if (write_custom_tags
)
5215 add_custom_tags(&game
[i
]->tag
);
5217 pgn_write(pgn
, game
[i
]);
5222 fm_loaded_file
= TRUE
;
5228 else if (ret
== E_PGN_ERR
)
5231 if (utf8_pieces
!= -1)
5232 config
.utf8_pieces
= utf8_pieces
;
5234 init_wchar_pieces ();
5238 * This fixes window resizing in an xterm.
5240 if (getenv("DISPLAY") != NULL
) {
5245 if (initscr() == NULL
)
5246 errx(EXIT_FAILURE
, "%s", _("Could not initialize curses."));
5248 curses_initialized
= 1;
5250 if (LINES
< 24 || COLS
< 80) {
5252 errx(EXIT_FAILURE
, _("Need at least an 80x24 terminal."));
5258 if (has_colors() == TRUE
&& start_color() == OK
)
5261 boardw
= newwin(BOARD_HEIGHT
, BOARD_WIDTH
, 0, COLS
- BOARD_WIDTH
);
5262 boardp
= new_panel(boardw
);
5263 historyw
= newwin(HISTORY_HEIGHT
, HISTORY_WIDTH
, LINES
- HISTORY_HEIGHT
,
5264 COLS
- HISTORY_WIDTH
);
5265 historyp
= new_panel(historyw
);
5266 statusw
= newwin(STATUS_HEIGHT
, STATUS_WIDTH
, 0, 0);
5267 statusp
= new_panel(statusw
);
5268 tagw
= newwin(TAG_HEIGHT
, TAG_WIDTH
, STATUS_HEIGHT
+ 1, 0);
5269 tagp
= new_panel(tagw
);
5270 keypad(boardw
, TRUE
);
5271 // leaveok(boardw, TRUE);
5272 leaveok(tagw
, TRUE
);
5273 leaveok(statusw
, TRUE
);
5274 leaveok(historyw
, TRUE
);
5278 draw_window_decor();