1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2002-2007 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
22 #include <sys/types.h>
24 #include <sys/socket.h>
43 #ifdef HAVE_SYS_WAIT_H
64 #include "filebrowser.h"
75 void update_cursor(GAME g
, int idx
)
79 int t
= pgn_history_total(g
->hp
);
80 struct userdata_s
*d
= g
->data
;
83 * If not deincremented then r and c would be the next move.
87 if (idx
> t
|| idx
< 0 || !t
|| !g
->hp
[idx
]->move
) {
88 d
->c_row
= 2, d
->c_col
= 5;
101 d
->c_row
= (g
->turn
== WHITE
) ? 8 : 1;
110 d
->c_row
= RANKTOINT(*p
--);
111 d
->c_col
= FILETOINT(*p
);
114 static int init_nag()
120 if ((fp
= fopen(config
.nagfile
, "r")) == NULL
) {
121 cmessage(ERROR
, ANYKEY
, "%s: %s", config
.nagfile
, strerror(errno
));
125 nags
= Realloc(nags
, (i
+2) * sizeof(char *));
126 nags
[i
++] = strdup(NONE
);
130 if (fscanf(fp
, " %[^\n] ", line
) == 1) {
131 nags
= Realloc(nags
, (i
+ 2) * sizeof(char *));
132 nags
[i
++] = strdup(line
);
141 void edit_nag_toggle_item(struct menu_input_s
*m
)
143 struct input_s
*in
= m
->data
;
144 struct input_data_s
*id
= in
->data
;
145 HISTORY
*h
= id
->data
;
148 if (m
->selected
== 0) {
149 for (i
= 0; i
< MAX_PGN_NAG
; i
++)
152 for (i
= 0; m
->items
[i
]; i
++)
153 m
->items
[i
]->selected
= 0;
158 for (i
= 0; i
< MAX_PGN_NAG
; i
++) {
159 if (h
->nag
[i
] == m
->selected
)
160 h
->nag
[i
] = m
->selected
= 0;
163 h
->nag
[i
] = m
->selected
;
170 void edit_nag_save(struct menu_input_s
*m
)
175 void edit_nag_help(struct menu_input_s
*m
)
177 message(NAG_EDIT_HELP
, ANYKEY
, "%s", naghelp
);
180 struct menu_item_s
**get_nag_items(WIN
*win
)
183 struct menu_input_s
*m
= win
->data
;
184 struct input_s
*in
= m
->data
;
185 struct input_data_s
*id
= in
->data
;
186 struct menu_item_s
**items
= m
->items
;
187 HISTORY
*h
= id
->data
;
190 for (i
= 0; items
[i
]; i
++)
194 for (i
= 0; nags
[i
]; i
++) {
195 items
= Realloc(items
, (i
+2) * sizeof(struct menu_item_s
*));
196 items
[i
] = Malloc(sizeof(struct menu_item_s
));
197 items
[i
]->name
= nags
[i
];
198 items
[i
]->value
= NULL
;
200 for (n
= 0; n
< MAX_PGN_NAG
; n
++) {
201 if (h
->nag
[n
] == i
) {
202 items
[i
]->selected
= 1;
209 items
[i
]->selected
= 0;
218 void nag_print(WIN
*win
)
220 struct menu_input_s
*m
= win
->data
;
222 mvwprintw(win
->w
, m
->print_line
, 1, "%-*s", win
->cols
- 2, m
->item
->name
);
225 void edit_nag(void *arg
)
227 struct menu_key_s
**keys
= NULL
;
234 add_menu_key(&keys
, ' ', edit_nag_toggle_item
);
235 add_menu_key(&keys
, CTRL('x'), edit_nag_save
);
236 add_menu_key(&keys
, KEY_F(1), edit_nag_help
);
237 construct_menu(0, 0, -1, -1, NAG_EDIT_TITLE
, 1, get_nag_items
, keys
, arg
,
242 static void *view_nag(void *arg
)
244 HISTORY
*h
= (HISTORY
*)arg
;
246 char line
[LINE_MAX
] = {0};
249 snprintf(buf
, sizeof(buf
), "%s \"%s\"", VIEW_MOVE_NAG
, h
->move
);
256 for (i
= 0; i
< MAX_PGN_NAG
; i
++) {
260 if (h
->nag
[i
] >= nag_total
)
261 strncat(line
, itoa(h
->nag
[i
]), sizeof(line
));
263 strncat(line
, nags
[h
->nag
[i
]], sizeof(line
));
265 strncat(line
, "\n", sizeof(line
));
268 line
[strlen(line
) - 1] = 0;
269 message(buf
, ANYKEY
, "%s", line
);
273 void view_annotation(HISTORY
*h
)
275 char buf
[MAX_SAN_MOVE_LEN
+ strlen(ANNOTATION_VIEW_TITLE
) + 4];
276 int nag
= 0, comment
= 0;
281 if (h
->comment
&& h
->comment
[0])
287 if (!nag
&& !comment
)
290 snprintf(buf
, sizeof(buf
), "%s \"%s\"", ANNOTATION_VIEW_TITLE
, h
->move
);
293 construct_message(buf
, (nag
) ? ANY_OTHER_KEY
: ANYKEY
, 0, 1,
294 (nag
) ? VIEW_NAG
: NULL
,
295 (nag
) ? view_nag
: NULL
, (nag
) ? h
: NULL
, NULL
,
296 (nag
) ? 'n' : 0, 0, "%s", h
->comment
);
298 construct_message(buf
, ANY_OTHER_KEY
, 0, 1, VIEW_NAG
, view_nag
, h
, NULL
,
299 'n', 0, "%s", NO_ANNOTATIONS
);
302 void do_game_write(char *filename
, char *mode
, int start
, int end
)
304 char *command
= NULL
;
307 struct userdata_s
*d
;
310 if ((fp
= popen(command
, "w")) == NULL
) {
311 cmessage(ERROR
, ANYKEY
, "%s: %s", filename
, strerror(errno
));
316 if ((fp
= fopen(filename
, mode
)) == NULL
) {
317 cmessage(ERROR
, ANYKEY
, "%s: %s", filename
, strerror(errno
));
322 for (i
= (start
== -1) ? 0 : start
; i
< end
; i
++) {
324 pgn_write(fp
, game
[i
]);
325 CLEAR_FLAG(d
->flags
, CF_MODIFIED
);
334 strncpy(loadfile
, filename
, sizeof(loadfile
));
336 update_status_notify(game
[gindex
], "%s", NOTIFY_SAVED
);
337 update_all(game
[gindex
]);
341 update_status_notify(game
[gindex
], "%s", NOTIFY_SAVE_FAILED
);
342 update_all(game
[gindex
]);
352 void do_save_game_overwrite_confirm(WIN
*win
)
355 struct save_game_s
*s
= win
->data
;
359 if (pgn_is_compressed(s
->filename
) == E_PGN_OK
) {
360 cmessage(NULL
, ANYKEY
, "%s", E_SAVE_COMPRESS
);
373 do_game_write(s
->filename
, mode
, s
->start
, s
->end
);
380 /* If the saveindex argument is -1, all games will be saved. Otherwise it's a
383 // FIXME command (compression)
384 void save_pgn(char *filename
, int saveindex
)
386 char buf
[FILENAME_MAX
];
388 int end
= (saveindex
== -1) ? gtotal
: saveindex
+ 1;
389 struct save_game_s
*s
;
391 if (filename
[0] != '/' && config
.savedirectory
) {
392 if (stat(config
.savedirectory
, &st
) == -1) {
393 if (errno
== ENOENT
) {
394 if (mkdir(config
.savedirectory
, 0755) == -1) {
395 cmessage(ERROR
, ANYKEY
, "%s: %s", config
.savedirectory
,
401 cmessage(ERROR
, ANYKEY
, "%s: %s", config
.savedirectory
,
407 stat(config
.savedirectory
, &st
);
409 if (!S_ISDIR(st
.st_mode
)) {
410 cmessage(ERROR
, ANYKEY
, "%s: %s", config
.savedirectory
, E_NOTADIR
);
414 snprintf(buf
, sizeof(buf
), "%s/%s", config
.savedirectory
, filename
);
418 if (access(filename
, W_OK
) == 0) {
419 s
= Malloc(sizeof(struct save_game_s
));
420 s
->filename
= strdup(filename
);
421 s
->start
= saveindex
;
423 construct_message(NULL
, GAME_SAVE_OVERWRITE_PROMPT
, 1, 1, NULL
, NULL
,
424 s
, do_save_game_overwrite_confirm
, 0, 0, "%s \"%s\"",
425 E_FILEEXISTS
, filename
);
429 do_game_write(filename
, "a", saveindex
, end
);
432 static int castling_state(GAME g
, BOARD b
, int row
, int col
, int piece
, int mod
)
434 if (pgn_piece_to_int(piece
) == ROOK
&& col
== 7
436 (TEST_FLAG(g
->flags
, GF_WK_CASTLE
) || mod
) &&
437 pgn_piece_to_int(b
[7][4].icon
) == KING
&& isupper(piece
)) {
439 TOGGLE_FLAG(g
->flags
, GF_WK_CASTLE
);
442 else if (pgn_piece_to_int(piece
) == ROOK
&& col
== 0
444 (TEST_FLAG(g
->flags
, GF_WQ_CASTLE
) || mod
) &&
445 pgn_piece_to_int(b
[7][4].icon
) == KING
&& isupper(piece
)) {
447 TOGGLE_FLAG(g
->flags
, GF_WQ_CASTLE
);
450 else if (pgn_piece_to_int(piece
) == ROOK
&& col
== 7
452 (TEST_FLAG(g
->flags
, GF_BK_CASTLE
) || mod
) &&
453 pgn_piece_to_int(b
[0][4].icon
) == KING
&& islower(piece
)) {
455 TOGGLE_FLAG(g
->flags
, GF_BK_CASTLE
);
458 else if (pgn_piece_to_int(piece
) == ROOK
&& col
== 0
460 (TEST_FLAG(g
->flags
, GF_BQ_CASTLE
) || mod
) &&
461 pgn_piece_to_int(b
[0][4].icon
) == KING
&& islower(piece
)) {
463 TOGGLE_FLAG(g
->flags
, GF_BQ_CASTLE
);
466 else if (pgn_piece_to_int(piece
) == KING
&& col
== 4
468 (mod
|| (pgn_piece_to_int(b
[7][7].icon
) == ROOK
&&
469 TEST_FLAG(g
->flags
, GF_WK_CASTLE
))
471 (pgn_piece_to_int(b
[7][0].icon
) == ROOK
&&
472 TEST_FLAG(g
->flags
, GF_WQ_CASTLE
))) && isupper(piece
)) {
474 if (TEST_FLAG(g
->flags
, GF_WK_CASTLE
) ||
475 TEST_FLAG(g
->flags
, GF_WQ_CASTLE
))
476 CLEAR_FLAG(g
->flags
, GF_WK_CASTLE
|GF_WQ_CASTLE
);
478 SET_FLAG(g
->flags
, GF_WK_CASTLE
|GF_WQ_CASTLE
);
482 else if (pgn_piece_to_int(piece
) == KING
&& col
== 4
484 (mod
|| (pgn_piece_to_int(b
[0][7].icon
) == ROOK
&&
485 TEST_FLAG(g
->flags
, GF_BK_CASTLE
))
487 (pgn_piece_to_int(b
[0][0].icon
) == ROOK
&&
488 TEST_FLAG(g
->flags
, GF_BQ_CASTLE
))) && islower(piece
)) {
490 if (TEST_FLAG(g
->flags
, GF_BK_CASTLE
) ||
491 TEST_FLAG(g
->flags
, GF_BQ_CASTLE
))
492 CLEAR_FLAG(g
->flags
, GF_BK_CASTLE
|GF_BQ_CASTLE
);
494 SET_FLAG(g
->flags
, GF_BK_CASTLE
|GF_BQ_CASTLE
);
502 static void draw_board(GAME g
)
505 int bcol
= 0, brow
= 0;
506 int maxy
= BOARD_HEIGHT
, maxx
= BOARD_WIDTH
;
507 int ncols
= 0, offset
= 1;
508 unsigned coords_y
= 8;
509 struct userdata_s
*d
= g
->data
;
511 if (d
->mode
!= MODE_PLAY
&& d
->mode
!= MODE_EDIT
)
512 update_cursor(g
, g
->hindex
);
514 for (row
= 0; row
< maxy
; row
++) {
517 for (col
= 0; col
< maxx
; col
++) {
522 if (row
== 0 || row
== maxy
- 2) {
524 mvwaddch(boardw
, row
, col
,
526 ACS_LLCORNER
| CP_BOARD_GRAPHICS
:
527 ACS_ULCORNER
| CP_BOARD_GRAPHICS
));
528 else if (col
== maxx
- 2)
529 mvwaddch(boardw
, row
, col
,
531 ACS_LRCORNER
| CP_BOARD_GRAPHICS
:
532 ACS_URCORNER
| CP_BOARD_GRAPHICS
));
534 mvwaddch(boardw
, row
, col
,
536 ACS_BTEE
| CP_BOARD_GRAPHICS
:
537 ACS_TTEE
| CP_BOARD_GRAPHICS
));
540 mvwaddch(boardw
, row
, col
,
541 LINE_GRAPHIC(ACS_HLINE
| CP_BOARD_GRAPHICS
));
547 if ((row
% 2) && col
== maxx
- 1 && coords_y
) {
548 wattron(boardw
, CP_BOARD_COORDS
);
549 mvwprintw(boardw
, row
, col
, "%d", coords_y
--);
550 wattroff(boardw
, CP_BOARD_COORDS
);
554 if ((col
== 0 || col
== maxx
- 2) && row
!= maxy
- 1) {
556 mvwaddch(boardw
, row
, col
,
558 ACS_RTEE
| CP_BOARD_GRAPHICS
:
559 ACS_LTEE
| CP_BOARD_GRAPHICS
));
561 mvwaddch(boardw
, row
, col
,
562 LINE_GRAPHIC(ACS_VLINE
| CP_BOARD_GRAPHICS
));
567 if ((row
% 2) && !(col
% 4) && row
!= maxy
- 1) {
568 mvwaddch(boardw
, row
, col
,
569 LINE_GRAPHIC(ACS_VLINE
| CP_BOARD_GRAPHICS
));
573 if (!(col
% 4) && row
!= maxy
- 1) {
574 mvwaddch(boardw
, row
, col
,
575 LINE_GRAPHIC(ACS_PLUS
| CP_BOARD_GRAPHICS
));
586 if (((ncols
% 2) && !(offset
% 2)) || (!(ncols
% 2)
592 if (config
.validmoves
&& d
->b
[brow
][bcol
].valid
) {
593 attrs
= (attrwhich
== WHITE
) ? CP_BOARD_MOVES_WHITE
:
594 CP_BOARD_MOVES_BLACK
;
597 attrs
= (attrwhich
== WHITE
) ? CP_BOARD_WHITE
:
600 if (row
== ROWTOMATRIX(d
->c_row
) && col
==
601 COLTOMATRIX(d
->c_col
)) {
602 attrs
= CP_BOARD_CURSOR
;
605 if (row
== ROWTOMATRIX(d
->sp
.srow
) &&
606 col
== COLTOMATRIX(d
->sp
.scol
)) {
607 attrs
= CP_BOARD_SELECTED
;
613 mvwaddch(boardw
, row
, col
, ' ' | attrs
);
616 waddch(boardw
, x_grid_chars
[bcol
] | CP_BOARD_COORDS
);
618 if (config
.details
&& d
->b
[row
/ 2][bcol
].enpassant
)
621 piece
= d
->b
[row
/ 2][bcol
].icon
;
623 if (config
.details
&& castling_state(g
, d
->b
, brow
,
627 if (g
->side
== WHITE
&& isupper(piece
))
629 else if (g
->side
== BLACK
&& islower(piece
))
632 waddch(boardw
, (pgn_piece_to_int(piece
) != OPEN_SQUARE
) ? piece
| attrs
: ' ' | attrs
);
634 CLEAR_FLAG(attrs
, A_BOLD
);
635 CLEAR_FLAG(attrs
, A_REVERSE
);
638 waddch(boardw
, ' ' | attrs
);
645 mvwaddch(boardw
, row
, col
,
646 LINE_GRAPHIC(ACS_HLINE
| CP_BOARD_GRAPHICS
));
654 void invalid_move(int n
, int e
, const char *m
)
656 if (curses_initialized
)
657 cmessage(ERROR
, ANYKEY
, "%s \"%s\" (round #%i)", (e
== E_PGN_AMBIGUOUS
)
658 ? E_AMBIGUOUS
: E_INVALID_MOVE
, m
, n
);
660 warnx("%s: %s \"%s\" (round #%i)", loadfile
, (e
== E_PGN_AMBIGUOUS
)
661 ? E_AMBIGUOUS
: E_INVALID_MOVE
, m
, n
);
664 static void update_clock(GAME g
, struct itimerval it
)
666 struct userdata_s
*d
= g
->data
;
669 if (g
->turn
== WHITE
) {
670 d
->wc
.tv_sec
+= it
.it_value
.tv_sec
;
671 d
->wc
.tv_usec
+= it
.it_value
.tv_usec
;
673 if (d
->wc
.tv_usec
> 1000000 - 1) {
674 d
->wc
.tv_sec
+= d
->wc
.tv_usec
/ 1000000;
675 d
->wc
.tv_usec
= d
->wc
.tv_usec
% 1000000;
679 d
->bc
.tv_sec
+= it
.it_value
.tv_sec
;
680 d
->bc
.tv_usec
+= it
.it_value
.tv_usec
;
682 if (d
->bc
.tv_usec
> 1000000 - 1) {
683 d
->bc
.tv_sec
+= d
->bc
.tv_usec
/ 1000000;
684 d
->bc
.tv_usec
= d
->bc
.tv_usec
% 1000000;
688 d
->elapsed
= d
->wc
.tv_sec
+ d
->bc
.tv_sec
;
689 n
= d
->wc
.tv_usec
+ d
->bc
.tv_usec
;
690 d
->elapsed
+= (n
> 1000000 - 1) ? n
/ 1000000 : 0;
692 if (TEST_FLAG(d
->flags
, CF_CLOCK
)) {
693 if (d
->elapsed
>= d
->limit
) {
694 SET_FLAG(g
->flags
, GF_GAMEOVER
);
695 pgn_tag_add(&g
->tag
, "Result", "1/2-1/2");
700 void do_validate_move(char *m
)
702 struct userdata_s
*d
= game
[gindex
]->data
;
706 if (TEST_FLAG(d
->flags
, CF_HUMAN
)) {
707 if ((n
= pgn_parse_move(game
[gindex
], d
->b
, &m
, &frfr
)) != E_PGN_OK
) {
708 invalid_move(d
->n
+ 1, n
, m
);
713 pgn_history_add(game
[gindex
], m
);
714 pgn_switch_turn(game
[gindex
]);
717 if ((n
= pgn_validate_move(game
[gindex
], d
->b
, &m
, &frfr
)) != E_PGN_OK
) {
718 invalid_move(d
->n
+ 1, n
, m
);
723 add_engine_command(game
[gindex
], ENGINE_THINKING
, "%s\n",
724 (config
.engine_protocol
== 1) ? frfr
: m
);
727 d
->sp
.srow
= d
->sp
.scol
= d
->sp
.icon
= 0;
729 if (config
.validmoves
)
730 pgn_reset_valid_moves(d
->b
);
732 if (TEST_FLAG(game
[gindex
]->flags
, GF_GAMEOVER
))
733 d
->mode
= MODE_HISTORY
;
735 SET_FLAG(d
->flags
, CF_MODIFIED
);
742 void do_promotion_piece_finalize(WIN
*win
)
744 char *p
, *str
= win
->data
;
746 if (pgn_piece_to_int(win
->c
) == -1)
749 p
= str
+ strlen(str
);
750 *p
++ = toupper(win
->c
);
752 do_validate_move(str
);
755 static void move_to_engine(GAME g
)
757 struct userdata_s
*d
= g
->data
;
761 if (config
.validmoves
&&
762 !d
->b
[RANKTOBOARD(d
->sp
.row
)][FILETOBOARD(d
->sp
.col
)].valid
)
765 str
= Malloc(MAX_SAN_MOVE_LEN
+ 1);
766 snprintf(str
, MAX_SAN_MOVE_LEN
+ 1, "%c%i%c%i",
767 x_grid_chars
[d
->sp
.scol
- 1],
768 d
->sp
.srow
, x_grid_chars
[d
->sp
.col
- 1], d
->sp
.row
);
770 piece
= pgn_piece_to_int(d
->b
[RANKTOBOARD(d
->sp
.srow
)][FILETOBOARD(d
->sp
.scol
)].icon
);
772 if (piece
== PAWN
&& (d
->sp
.row
== 8 || d
->sp
.row
== 1)) {
773 construct_message(PROMOTION_TITLE
, PROMOTION_PROMPT
, 1, 1, NULL
, NULL
,
774 str
, do_promotion_piece_finalize
, 0, 0, "%s", PROMOTION_TEXT
);
778 do_validate_move(str
);
781 static char *clock_to_char(long n
)
784 int h
= 0, m
= 0, s
= 0;
789 snprintf(buf
, sizeof(buf
), "%.2i:%.2i:%.2i", h
, m
, s
);
793 static char *timeval_to_char(struct timeval t
)
796 int h
= 0, m
= 0, s
= 0;
802 snprintf(buf
, sizeof(buf
), "%.2i:%.2i:%.2i.%.2i", h
, m
, s
,
803 (int)t
.tv_usec
/ 10000);
807 void update_status_window(GAME g
)
811 char tmp
[15], *engine
, *mode
;
816 struct userdata_s
*d
= g
->data
;
820 getmaxyx(statusw
, maxy
, maxx
);
826 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, STATUS_FILE_STR
, w
,
827 (loadfile
[0]) ? str_etc(loadfile
, w
, 1) : UNAVAILABLE
);
828 snprintf(buf
, len
, "%i %s %i", gindex
+ 1, N_OF_N_STR
, gtotal
);
829 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, STATUS_GAME_STR
, w
, buf
);
834 if (config
.details
) {
839 if (TEST_FLAG(d
->flags
, CF_DELETE
)) {
847 if (TEST_FLAG(g
->flags
, GF_PERROR
)) {
855 if (TEST_FLAG(d
->flags
, CF_MODIFIED
)) {
863 pgn_config_get(PGN_STRICT_CASTLING
, &n
);
874 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, STATUS_FLAGS_STR
, w
, tmp
);
878 mode
= MODE_HISTORY_STR
;
881 mode
= MODE_EDIT_STR
;
884 mode
= MODE_PLAY_STR
;
891 snprintf(buf
, len
- 1, "%*s %s", 7, STATUS_MODE_STR
, mode
);
893 if (d
->mode
== MODE_PLAY
) {
894 if (TEST_FLAG(d
->flags
, CF_HUMAN
))
895 strncat(buf
, " (human/human)", len
- 1);
896 else if (TEST_FLAG(d
->flags
, CF_ENGINE_LOOP
))
897 strncat(buf
, " (engine/engine)", len
- 1);
899 strncat(buf
, " (human/engine)", len
- 1);
902 mvwprintw(statusw
, y
++, 1, "%-*s", len
, buf
);
905 switch (d
->engine
->status
) {
906 case ENGINE_THINKING
:
907 engine
= ENGINE_PONDER_STR
;
910 engine
= ENGINE_READY_STR
;
912 case ENGINE_INITIALIZING
:
913 engine
= ENGINE_INITIALIZING_STR
;
916 engine
= ENGINE_OFFLINE_STR
;
924 engine
= ENGINE_OFFLINE_STR
;
926 mvwprintw(statusw
, y
, 1, "%*s %-*s", 7, STATUS_ENGINE_STR
, w
, " ");
927 wattron(statusw
, CP_STATUS_ENGINE
);
928 mvwaddstr(statusw
, y
++, 9, engine
);
929 wattroff(statusw
, CP_STATUS_ENGINE
);
931 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, STATUS_TURN_STR
, w
,
932 (g
->turn
== WHITE
) ? WHITE_STR
: BLACK_STR
);
934 mvwprintw(statusw
, y
++, 1, "%*s %-*s", 7, STATUS_CLOCK_STR
, w
,
935 clock_to_char((TEST_FLAG(d
->flags
, CF_CLOCK
)) ?
936 d
->limit
- d
->elapsed
: 0));
938 strncpy(tmp
, WHITE_STR
, sizeof(tmp
));
939 tmp
[0] = toupper(tmp
[0]);
940 mvwprintw(statusw
, y
++, 1, "%*s: %-*s", 6, tmp
, w
, timeval_to_char(d
->wc
));
942 strncpy(tmp
, BLACK_STR
, sizeof(tmp
));
943 tmp
[0] = toupper(tmp
[0]);
944 mvwprintw(statusw
, y
++, 1, "%*s: %-*s", 6, tmp
, w
, timeval_to_char(d
->bc
));
947 for (i
= 1; i
< maxx
- 4; i
++)
948 mvwprintw(statusw
, maxy
- 2, i
, " ");
951 status
.notify
= strdup(GAME_HELP_PROMPT
);
953 wattron(statusw
, CP_STATUS_NOTIFY
);
954 mvwprintw(statusw
, maxy
- 2, CENTERX(maxx
, status
.notify
), "%s",
956 wattroff(statusw
, CP_STATUS_NOTIFY
);
959 void update_history_window(GAME g
)
961 char buf
[HISTORY_WIDTH
- 1];
964 int t
= pgn_history_total(g
->hp
);
966 n
= (g
->hindex
+ 1) / 2;
974 snprintf(buf
, sizeof(buf
), "%u %s %u%s", n
, N_OF_N_STR
, total
,
975 (movestep
== 1) ? HISTORY_PLY_STEP
: "");
977 strncpy(buf
, UNAVAILABLE
, sizeof(buf
));
979 mvwprintw(historyw
, 2, 1, "%*s %-*s", 10, HISTORY_MOVE_STR
,
980 HISTORY_WIDTH
- 13, buf
);
982 h
= pgn_history_by_n(g
->hp
, g
->hindex
);
983 snprintf(buf
, sizeof(buf
), "%s", (h
&& h
->move
) ? h
->move
: UNAVAILABLE
);
986 if (h
&& ((h
->comment
) || h
->nag
[0])) {
987 strncat(buf
, " (Annotated", sizeof(buf
));
992 strncat(buf
, (n
) ? ",+" : " (+", sizeof(buf
));
997 strncat(buf
, (n
) ? ",-" : " (-", sizeof(buf
));
1002 strncat(buf
, ")", sizeof(buf
));
1004 mvwprintw(historyw
, 3, 1, "%s %-*s", HISTORY_MOVE_NEXT_STR
,
1005 HISTORY_WIDTH
- 13, buf
);
1007 h
= pgn_history_by_n(g
->hp
, g
->hindex
- 1);
1008 snprintf(buf
, sizeof(buf
), "%s", (h
&& h
->move
) ? h
->move
: UNAVAILABLE
);
1011 if (h
&& ((h
->comment
) || h
->nag
[0])) {
1012 strncat(buf
, " (Annotated", sizeof(buf
));
1017 strncat(buf
, (n
) ? ",+" : " (+", sizeof(buf
));
1022 strncat(buf
, (n
) ? ",-" : " (-", sizeof(buf
));
1027 strncat(buf
, ")", sizeof(buf
));
1029 mvwprintw(historyw
, 4, 1, "%s %-*s", HISTORY_MOVE_PREV_STR
,
1030 HISTORY_WIDTH
- 13, buf
);
1033 void update_tag_window(TAG
**t
)
1036 int w
= TAG_WIDTH
- 10;
1038 for (i
= 0; i
< 7; i
++)
1039 mvwprintw(tagw
, (i
+ 2), 1, "%*s: %-*s", 6, t
[i
]->name
, w
,
1040 str_etc(t
[i
]->value
, w
, 0));
1043 void append_enginebuf(GAME g
, char *line
)
1046 struct userdata_s
*d
= g
->data
;
1048 if (d
->engine
->enginebuf
)
1049 for (i
= 0; d
->engine
->enginebuf
[i
]; i
++);
1051 if (i
>= LINES
- 3) {
1052 free(d
->engine
->enginebuf
[0]);
1054 for (i
= 0; d
->engine
->enginebuf
[i
+1]; i
++)
1055 d
->engine
->enginebuf
[i
] = d
->engine
->enginebuf
[i
+1];
1057 d
->engine
->enginebuf
[i
] = strdup(line
);
1060 d
->engine
->enginebuf
= Realloc(d
->engine
->enginebuf
, (i
+ 2) * sizeof(char *));
1061 d
->engine
->enginebuf
[i
++] = strdup(line
);
1062 d
->engine
->enginebuf
[i
] = NULL
;
1066 void update_engine_window(GAME g
)
1069 struct userdata_s
*d
= g
->data
;
1071 if (!d
->engine
|| !d
->engine
->enginebuf
)
1074 wmove(enginew
, 0, 0);
1077 if (d
->engine
->enginebuf
) {
1078 for (i
= 0; d
->engine
->enginebuf
[i
]; i
++)
1079 mvwprintw(enginew
, i
+ 2, 1, "%s", d
->engine
->enginebuf
[i
]);
1082 window_draw_title(enginew
, ENGINE_IO_TITLE
, COLS
, CP_MESSAGE_TITLE
,
1092 void update_all(GAME g
)
1094 update_status_window(g
);
1095 update_history_window(g
);
1096 update_tag_window(g
->tag
);
1097 update_engine_window(g
);
1100 static void game_next_prev(GAME g
, int n
, int count
)
1106 if (gindex
+ count
> gtotal
- 1) {
1108 gindex
= gtotal
- 1;
1116 if (gindex
- count
< 0) {
1120 gindex
= gtotal
- 1;
1127 static void delete_game(int which
)
1132 struct userdata_s
*d
;
1134 for (i
= 0; i
< gtotal
; i
++) {
1137 if (i
== which
|| TEST_FLAG(d
->flags
, CF_DELETE
)) {
1138 free_userdata_once(game
[i
]);
1143 g
= Realloc(g
, (gi
+ 1) * sizeof(GAME
*));
1144 g
[gi
] = Calloc(1, sizeof(struct game_s
));
1145 memcpy(g
[gi
], game
[i
], sizeof(struct game_s
));
1146 g
[gi
]->tag
= game
[i
]->tag
;
1147 g
[gi
]->history
= game
[i
]->history
;
1148 g
[gi
]->hp
= game
[i
]->hp
;
1156 if (which
+ 1 >= gtotal
)
1157 gindex
= gtotal
- 1;
1162 gindex
= gtotal
- 1;
1164 game
[gindex
]->hp
= game
[gindex
]->history
;
1168 * FIXME find across multiple games.
1170 static int find_move_exp(GAME g
, regex_t r
, int which
, int count
)
1178 incr
= (which
== 0) ? -1 : 1;
1180 for (i
= g
->hindex
+ incr
- 1, found
= 0; ; i
+= incr
) {
1181 if (i
== g
->hindex
- 1)
1184 if (i
>= pgn_history_total(g
->hp
))
1187 i
= pgn_history_total(g
->hp
) - 1;
1190 ret
= regexec(&r
, g
->hp
[i
]->move
, 0, 0, 0);
1193 if (count
== ++found
) {
1198 if (ret
!= REG_NOMATCH
) {
1199 regerror(ret
, &r
, errbuf
, sizeof(errbuf
));
1200 cmessage(E_REGEXEC_TITLE
, ANYKEY
, "%s", errbuf
);
1209 static int toggle_delete_flag(int n
)
1212 struct userdata_s
*d
= game
[n
]->data
;
1214 TOGGLE_FLAG(d
->flags
, CF_DELETE
);
1216 update_all(game
[gindex
]);
1218 for (i
= x
= 0; i
< gtotal
; i
++) {
1221 if (TEST_FLAG(d
->flags
, CF_DELETE
))
1226 cmessage(NULL
, ANYKEY
, "%s", E_DELETE_GAME
);
1228 CLEAR_FLAG(d
->flags
, CF_DELETE
);
1235 static int find_game_exp(char *str
, int which
, int count
)
1237 char *nstr
= NULL
, *exp
= NULL
;
1241 char buf
[255], *tmp
;
1244 int incr
= (which
== 0) ? -(1) : 1;
1246 strncpy(buf
, str
, sizeof(buf
));
1249 if (strstr(tmp
, ":") != NULL
) {
1250 nstr
= strsep(&tmp
, ":");
1252 if ((ret
= regcomp(&nexp
, nstr
,
1253 REG_ICASE
|REG_EXTENDED
|REG_NOSUB
)) != 0) {
1254 regerror(ret
, &nexp
, errbuf
, sizeof(errbuf
));
1255 cmessage(E_REGCOMP_TITLE
, ANYKEY
, "%s", errbuf
);
1266 if ((ret
= regcomp(&vexp
, exp
, REG_EXTENDED
|REG_NOSUB
)) != 0) {
1267 regerror(ret
, &vexp
, errbuf
, sizeof(errbuf
));
1268 cmessage(E_REGCOMP_TITLE
, ANYKEY
, "%s", errbuf
);
1275 for (g
= gindex
+ incr
, found
= 0; ; g
+= incr
) {
1286 for (t
= 0; game
[g
]->tag
[t
]; t
++) {
1288 if (regexec(&nexp
, game
[g
]->tag
[t
]->name
, 0, 0, 0) == 0) {
1289 if (regexec(&vexp
, game
[g
]->tag
[t
]->value
, 0, 0, 0) == 0) {
1290 if (count
== ++found
) {
1298 if (regexec(&vexp
, game
[g
]->tag
[t
]->value
, 0, 0, 0) == 0) {
1299 if (count
== ++found
) {
1321 * Updates the notification line in the status window then refreshes the
1324 void update_status_notify(GAME g
, char *fmt
, ...)
1327 #ifdef HAVE_VASPRINTF
1334 if (status
.notify
) {
1335 free(status
.notify
);
1336 status
.notify
= NULL
;
1338 if (curses_initialized
)
1339 update_status_window(g
);
1346 #ifdef HAVE_VASPRINTF
1347 vasprintf(&line
, fmt
, ap
);
1349 vsnprintf(line
, sizeof(line
), fmt
, ap
);
1354 free(status
.notify
);
1356 status
.notify
= strdup(line
);
1358 #ifdef HAVE_VASPRINTF
1361 if (curses_initialized
)
1362 update_status_window(g
);
1365 int rav_next_prev(GAME g
, BOARD b
, int n
)
1369 if ((!g
->ravlevel
&& g
->hindex
&& g
->hp
[g
->hindex
- 1]->rav
== NULL
) ||
1370 (!g
->ravlevel
&& !g
->hindex
&& g
->hp
[g
->hindex
]->rav
== NULL
) ||
1371 (g
->ravlevel
&& g
->hp
[g
->hindex
]->rav
== NULL
))
1374 g
->rav
= Realloc(g
->rav
, (g
->ravlevel
+ 1) * sizeof(RAV
));
1375 g
->rav
[g
->ravlevel
].hp
= g
->hp
;
1376 g
->rav
[g
->ravlevel
].flags
= g
->flags
;
1377 g
->rav
[g
->ravlevel
].fen
= strdup(pgn_game_to_fen(g
, b
));
1378 g
->rav
[g
->ravlevel
].hindex
= g
->hindex
;
1379 g
->hp
= (!g
->ravlevel
) ? (g
->hindex
) ? g
->hp
[g
->hindex
- 1]->rav
: g
->hp
[g
->hindex
]->rav
: g
->hp
[g
->hindex
]->rav
;
1382 pgn_board_update(g
, b
, g
->hindex
+ 1);
1386 if (g
->ravlevel
- 1 < 0)
1391 pgn_board_init_fen(g
, b
, g
->rav
[g
->ravlevel
].fen
);
1392 free(g
->rav
[g
->ravlevel
].fen
);
1393 g
->hp
= g
->rav
[g
->ravlevel
].hp
;
1394 g
->flags
= g
->rav
[g
->ravlevel
].flags
;
1395 g
->hindex
= g
->rav
[g
->ravlevel
].hindex
;
1399 static void draw_window_decor()
1401 move_panel(historyp
, LINES
- HISTORY_HEIGHT
, COLS
- HISTORY_WIDTH
);
1402 move_panel(boardp
, 0, COLS
- BOARD_WIDTH
);
1403 wbkgd(boardw
, CP_BOARD_WINDOW
);
1404 wbkgd(statusw
, CP_STATUS_WINDOW
);
1405 window_draw_title(statusw
, STATUS_WINDOW_TITLE
, STATUS_WIDTH
,
1406 CP_STATUS_TITLE
, CP_STATUS_BORDER
);
1407 wbkgd(tagw
, CP_TAG_WINDOW
);
1408 window_draw_title(tagw
, TAG_WINDOW_TITLE
, TAG_WIDTH
, CP_TAG_TITLE
,
1410 wbkgd(historyw
, CP_HISTORY_WINDOW
);
1411 window_draw_title(historyw
, HISTORY_WINDOW_TITLE
, HISTORY_WIDTH
,
1412 CP_HISTORY_TITLE
, CP_HISTORY_BORDER
);
1416 static void do_window_resize()
1418 if (LINES
< 24 || COLS
< 80)
1421 resizeterm(LINES
, COLS
);
1422 wresize(historyw
, HISTORY_HEIGHT
, HISTORY_WIDTH
);
1423 wresize(statusw
, STATUS_HEIGHT
, STATUS_WIDTH
);
1424 wresize(tagw
, TAG_HEIGHT
, TAG_WIDTH
);
1425 wmove(historyw
, 0, 0);
1426 wclrtobot(historyw
);
1429 wmove(statusw
, 0, 0);
1431 draw_window_decor();
1432 update_all(game
[gindex
]);
1438 memset(&clock_timer
, 0, sizeof(struct itimerval
));
1439 setitimer(ITIMER_REAL
, &clock_timer
, NULL
);
1444 if (clock_timer
.it_interval
.tv_usec
)
1447 clock_timer
.it_value
.tv_sec
= 0;
1448 clock_timer
.it_value
.tv_usec
= 100000;
1449 clock_timer
.it_interval
.tv_sec
= 0;
1450 clock_timer
.it_interval
.tv_usec
= 100000;
1451 setitimer(ITIMER_REAL
, &clock_timer
, NULL
);
1454 static void update_clocks()
1457 struct userdata_s
*d
;
1458 struct itimerval it
;
1460 getitimer(ITIMER_REAL
, &it
);
1462 for (i
= 0; i
< gtotal
; i
++) {
1465 if (d
&& d
->mode
== MODE_PLAY
) {
1466 if (d
->paused
== 1 || TEST_FLAG(d
->flags
, CF_NEW
))
1468 else if (d
->paused
== -1) {
1469 if (game
[i
]->side
== game
[i
]->turn
) {
1475 update_clock(game
[i
], it
);
1480 static int init_chess_engine(GAME g
)
1482 struct userdata_s
*d
= g
->data
;
1485 if (start_chess_engine(g
) > 0) {
1490 x
= pgn_tag_find(g
->tag
, "FEN");
1491 w
= pgn_tag_find(g
->tag
, "SetUp");
1493 if ((w
>= 0 && x
>= 0 && atoi(g
->tag
[w
]->value
) == 1) ||
1494 (x
>= 0 && w
== -1))
1495 add_engine_command(g
, ENGINE_READY
, "setboard %s\n", g
->tag
[x
]->value
);
1497 add_engine_command(g
, ENGINE_READY
, "setboard %s\n",
1498 pgn_game_to_fen(g
, d
->b
));
1503 static int parse_clock_input(struct userdata_s
*d
, char *str
)
1525 if (!t
&& *p
!= ' ')
1559 CLEAR_FLAG(d
->flags
, CF_CLOCK
);
1562 SET_FLAG(d
->flags
, CF_CLOCK
);
1567 d
->limit
= (n
<= d
->elapsed
) ? d
->elapsed
+ n
: n
;
1573 void do_clock_input_finalize(WIN
*win
)
1575 struct userdata_s
*d
= game
[gindex
]->data
;
1576 struct input_data_s
*in
= win
->data
;
1581 if (parse_clock_input(d
, in
->str
))
1582 cmessage(ERROR
, ANYKEY
, "Invalid time specification");
1588 void do_engine_command_finalize(WIN
*win
)
1590 struct userdata_s
*d
= game
[gindex
]->data
;
1591 struct input_data_s
*in
= win
->data
;
1599 x
= d
->engine
->status
;
1600 send_to_engine(game
[gindex
], -1, "%s\n", in
->str
);
1601 d
->engine
->status
= x
;
1606 void do_board_details()
1608 config
.details
= (config
.details
) ? 0 : 1;
1611 void do_toggle_strict_castling()
1615 pgn_config_get(PGN_STRICT_CASTLING
, &n
);
1618 pgn_config_set(PGN_STRICT_CASTLING
, 1);
1620 pgn_config_set(PGN_STRICT_CASTLING
, 0);
1623 void do_play_set_clock()
1625 struct input_data_s
*in
;
1627 in
= Calloc(1, sizeof(struct input_data_s
));
1628 in
->efunc
= do_clock_input_finalize
;
1629 construct_input(CLOCK_TITLE
, NULL
, 1, 1, CLOCK_HELP
, NULL
, NULL
, 0, in
, -1);
1632 void do_play_toggle_human()
1634 struct userdata_s
*d
= game
[gindex
]->data
;
1636 TOGGLE_FLAG(d
->flags
, CF_HUMAN
);
1638 if (!TEST_FLAG(d
->flags
, CF_HUMAN
) && pgn_history_total(game
[gindex
]->hp
)) {
1639 if (init_chess_engine(game
[gindex
]))
1643 CLEAR_FLAG(d
->flags
, CF_ENGINE_LOOP
);
1646 d
->engine
->status
= ENGINE_READY
;
1648 update_all(game
[gindex
]);
1651 void do_play_toggle_engine()
1653 struct userdata_s
*d
= game
[gindex
]->data
;
1655 TOGGLE_FLAG(d
->flags
, CF_ENGINE_LOOP
);
1656 CLEAR_FLAG(d
->flags
, CF_HUMAN
);
1658 if (d
->engine
&& TEST_FLAG(d
->flags
, CF_ENGINE_LOOP
)) {
1659 pgn_board_update(game
[gindex
], d
->b
,
1660 pgn_history_total(game
[gindex
]->hp
));
1661 add_engine_command(game
[gindex
], ENGINE_READY
,
1662 "setboard %s\n", pgn_game_to_fen(game
[gindex
], d
->b
));
1665 update_all(game
[gindex
]);
1668 void do_play_send_command()
1670 struct userdata_s
*d
= game
[gindex
]->data
;
1671 struct input_data_s
*in
;
1673 if (!d
->engine
|| d
->engine
->status
== ENGINE_OFFLINE
)
1676 in
= Calloc(1, sizeof(struct input_data_s
));
1677 in
->efunc
= do_engine_command_finalize
;
1678 construct_input(ENGINE_CMD_TITLE
, NULL
, 1, 1, NULL
, NULL
, NULL
, 0, in
, -1);
1681 void do_play_switch_turn()
1683 pgn_switch_side(game
[gindex
]);
1684 pgn_switch_turn(game
[gindex
]);
1685 add_engine_command(game
[gindex
], -1,
1686 (game
[gindex
]->side
== WHITE
) ? "white\n" : "black\n");
1687 update_status_window(game
[gindex
]);
1692 struct userdata_s
*d
= game
[gindex
]->data
;
1694 if (!pgn_history_total(game
[gindex
]->hp
))
1697 if (d
->engine
&& d
->engine
->status
== ENGINE_READY
) {
1698 add_engine_command(game
[gindex
], ENGINE_READY
, "remove\n");
1699 d
->engine
->status
= ENGINE_READY
;
1702 game
[gindex
]->hindex
-= 2;
1703 pgn_history_free(game
[gindex
]->hp
, game
[gindex
]->hindex
);
1704 game
[gindex
]->hindex
= pgn_history_total(game
[gindex
]->hp
);
1705 pgn_board_update(game
[gindex
], d
->b
, game
[gindex
]->hindex
);
1706 update_history_window(game
[gindex
]);
1709 void do_play_toggle_pause()
1711 struct userdata_s
*d
= game
[gindex
]->data
;
1713 if (!TEST_FLAG(d
->flags
, CF_HUMAN
) && game
[gindex
]->turn
!=
1714 game
[gindex
]->side
) {
1719 d
->paused
= (d
->paused
) ? 0 : 1;
1724 struct userdata_s
*d
= game
[gindex
]->data
;
1726 if (TEST_FLAG(d
->flags
, CF_HUMAN
))
1729 if (!d
->engine
|| d
->engine
->status
== ENGINE_OFFLINE
) {
1730 if (init_chess_engine(game
[gindex
]))
1734 add_engine_command(game
[gindex
], ENGINE_THINKING
, "go\n");
1737 void do_play_config_command()
1739 struct userdata_s
*d
= game
[gindex
]->data
;
1746 for (x
= 0; config
.keys
[x
]; x
++) {
1747 if (config
.keys
[x
]->c
== input_c
) {
1748 switch (config
.keys
[x
]->type
) {
1750 add_engine_command(game
[gindex
], -1, "%s\n",
1751 config
.keys
[x
]->str
);
1757 add_engine_command(game
[gindex
], -1,
1758 "%s %i\n", config
.keys
[x
]->str
, keycount
);
1765 for (w
= 0; w
< keycount
; w
++)
1766 add_engine_command(game
[gindex
], -1,
1767 "%s\n", config
.keys
[x
]->str
);
1775 update_status_notify(game
[gindex
], NULL
);
1778 void do_play_cancel_selected()
1780 struct userdata_s
*d
= game
[gindex
]->data
;
1782 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
1784 update_status_notify(game
[gindex
], NULL
);
1787 void do_play_commit()
1789 struct userdata_s
*d
= game
[gindex
]->data
;
1791 pushkey
= keycount
= 0;
1792 update_status_notify(game
[gindex
], NULL
);
1794 if (!TEST_FLAG(d
->flags
, CF_HUMAN
) &&
1795 (!d
->engine
|| d
->engine
->status
== ENGINE_THINKING
))
1801 d
->sp
.row
= d
->c_row
;
1802 d
->sp
.col
= d
->c_col
;
1803 move_to_engine(game
[gindex
]);
1806 void do_play_select()
1808 struct userdata_s
*d
= game
[gindex
]->data
;
1810 if (!TEST_FLAG(d
->flags
, CF_HUMAN
) && (!d
->engine
||
1811 d
->engine
->status
== ENGINE_OFFLINE
)) {
1812 if (init_chess_engine(game
[gindex
]))
1816 if (d
->sp
.icon
|| (d
->engine
&& d
->engine
->status
== ENGINE_THINKING
))
1819 d
->sp
.icon
= d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
;
1821 if (pgn_piece_to_int(d
->sp
.icon
) == OPEN_SQUARE
) {
1826 if (((islower(d
->sp
.icon
) && game
[gindex
]->turn
!= BLACK
)
1827 || (isupper(d
->sp
.icon
) && game
[gindex
]->turn
!= WHITE
))) {
1828 if (pgn_history_total(game
[gindex
]->hp
)) {
1829 message(NULL
, ANYKEY
, "%s", E_SELECT_TURN
);
1834 if (pgn_tag_find(game
[gindex
]->tag
, "FEN") != E_PGN_ERR
)
1837 add_engine_command(game
[gindex
], ENGINE_READY
, "black\n");
1838 pgn_switch_turn(game
[gindex
]);
1840 if (game
[gindex
]->side
!= BLACK
)
1841 pgn_switch_side(game
[gindex
]);
1845 d
->sp
.srow
= d
->c_row
;
1846 d
->sp
.scol
= d
->c_col
;
1848 if (config
.validmoves
)
1849 pgn_find_valid_moves(game
[gindex
], d
->b
, d
->sp
.scol
, d
->sp
.srow
);
1851 CLEAR_FLAG(d
->flags
, CF_NEW
);
1855 /* FIXME: keys with the same function should comma deliminated. */
1856 static char *build_help(struct key_s
**keys
)
1858 int i
, nlen
= 1, len
, t
, n
;
1865 for (i
= len
= t
= 0; keys
[i
]; i
++) {
1870 if (strlen(keys
[i
]->key
) > nlen
) {
1871 nlen
= strlen(keys
[i
]->key
);
1879 if (strlen(keys
[i
]->d
) > len
)
1880 len
= strlen(keys
[i
]->d
);
1891 for (i
= 0; keys
[i
]; i
++) {
1896 n
= strlen(keys
[i
]->key
);
1906 strcat(buf
, keys
[i
]->key
);
1907 p
= buf
+ strlen(buf
);
1918 strcat(buf
, keys
[i
]->d
);
1924 p
= buf
+ strlen(buf
);
1930 void do_more_help(WIN
*);
1931 void do_main_help(WIN
*win
)
1937 buf
= build_help(play_keys
);
1938 construct_message(GAME_HELP_PLAY_TITLE
, ANYKEY
, 0, 0,
1939 NULL
, NULL
, buf
, do_more_help
, 0, 1, "%s", buf
);
1942 buf
= build_help(history_keys
);
1943 construct_message(GAME_HELP_HISTORY_TITLE
, ANYKEY
, 0, 0,
1944 NULL
, NULL
, buf
, do_more_help
, 0, 1, "%s", buf
);
1947 buf
= build_help(edit_keys
);
1948 construct_message(GAME_HELP_EDIT_TITLE
, ANYKEY
, 0, 0,
1949 NULL
, NULL
, buf
, do_more_help
, 0, 1, "%s", buf
);
1952 buf
= build_help(global_keys
);
1953 construct_message(GAME_HELP_GAME_TITLE
, ANYKEY
, 0, 0,
1954 NULL
, NULL
, buf
, do_more_help
, 0, 1, "%s", buf
);
1961 void do_more_help(WIN
*win
)
1963 if (win
->c
== KEY_F(1) || win
->c
== CTRL('g'))
1964 construct_message(GAME_HELP_INDEX_TITLE
, GAME_HELP_INDEX_PROMPT
, 0, 0,
1965 NULL
, NULL
, NULL
, do_main_help
, 0, 0, "%s", mainhelp
);
1970 char *buf
= build_help(play_keys
);
1972 construct_message(GAME_HELP_PLAY_TITLE
, ANYKEY
, 0, 0, NULL
, NULL
, buf
,
1973 do_more_help
, 0, 1, "%s", buf
);
1976 void do_play_history_mode()
1978 struct userdata_s
*d
= game
[gindex
]->data
;
1980 if (!pgn_history_total(game
[gindex
]->hp
) ||
1981 (d
->engine
&& d
->engine
->status
== ENGINE_THINKING
))
1984 d
->mode
= MODE_HISTORY
;
1985 pgn_board_update(game
[gindex
], d
->b
, pgn_history_total(game
[gindex
]->hp
));
1986 update_all(game
[gindex
]);
1989 void do_play_edit_mode()
1991 struct userdata_s
*d
= game
[gindex
]->data
;
1993 if (pgn_history_total(game
[gindex
]->hp
))
1996 pgn_board_init_fen(game
[gindex
], d
->b
, NULL
);
1998 d
->mode
= MODE_EDIT
;
1999 update_all(game
[gindex
]);
2002 void do_edit_insert_finalize(WIN
*win
)
2004 struct userdata_s
*d
= win
->data
;
2006 if (pgn_piece_to_int(win
->c
) == -1)
2009 d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
= win
->c
;
2012 void do_edit_select()
2014 struct userdata_s
*d
= game
[gindex
]->data
;
2019 d
->sp
.icon
= d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
;
2021 if (pgn_piece_to_int(d
->sp
.icon
) == OPEN_SQUARE
) {
2026 d
->sp
.srow
= d
->c_row
;
2027 d
->sp
.scol
= d
->c_col
;
2030 void do_edit_commit()
2033 struct userdata_s
*d
= game
[gindex
]->data
;
2035 pushkey
= keycount
= 0;
2036 update_status_notify(game
[gindex
], NULL
);
2041 d
->sp
.row
= d
->c_row
;
2042 d
->sp
.col
= d
->c_col
;
2043 p
= d
->b
[RANKTOBOARD(d
->sp
.srow
)][FILETOBOARD(d
->sp
.scol
)].icon
;
2044 d
->b
[RANKTOBOARD(d
->sp
.row
)][FILETOBOARD(d
->sp
.col
)].icon
= p
;
2045 d
->b
[RANKTOBOARD(d
->sp
.srow
)][FILETOBOARD(d
->sp
.scol
)].icon
=
2046 pgn_int_to_piece(game
[gindex
]->turn
, OPEN_SQUARE
);
2047 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
2050 void do_edit_delete()
2052 struct userdata_s
*d
= game
[gindex
]->data
;
2055 d
->b
[RANKTOBOARD(d
->sp
.srow
)][FILETOBOARD(d
->sp
.scol
)].icon
=
2056 pgn_int_to_piece(game
[gindex
]->turn
, OPEN_SQUARE
);
2058 d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
=
2059 pgn_int_to_piece(game
[gindex
]->turn
, OPEN_SQUARE
);
2061 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
2064 void do_edit_cancel_selected()
2066 struct userdata_s
*d
= game
[gindex
]->data
;
2068 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
2070 update_status_notify(game
[gindex
], NULL
);
2073 void do_edit_switch_turn()
2075 pgn_switch_turn(game
[gindex
]);
2076 update_all(game
[gindex
]);
2079 void do_edit_toggle_castle()
2081 struct userdata_s
*d
= game
[gindex
]->data
;
2083 castling_state(game
[gindex
], d
->b
, RANKTOBOARD(d
->c_row
),
2084 FILETOBOARD(d
->c_col
),
2085 d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].icon
, 1);
2088 void do_edit_insert()
2090 struct userdata_s
*d
= game
[gindex
]->data
;
2092 construct_message(GAME_EDIT_TITLE
, GAME_EDIT_PROMPT
, 0, 0, NULL
, NULL
,
2093 d
->b
, do_edit_insert_finalize
, 0, 0, "%s", GAME_EDIT_TEXT
);
2096 void do_edit_enpassant()
2098 struct userdata_s
*d
= game
[gindex
]->data
;
2100 if (d
->c_row
== 6 || d
->c_row
== 3) {
2101 pgn_reset_enpassant(d
->b
);
2102 d
->b
[RANKTOBOARD(d
->c_row
)][FILETOBOARD(d
->c_col
)].enpassant
= 1;
2108 char *buf
= build_help(edit_keys
);
2110 construct_message(GAME_HELP_EDIT_TITLE
, ANYKEY
, 0, 0, NULL
, NULL
, buf
,
2111 do_more_help
, 0, 1, "%s", buf
);
2116 struct userdata_s
*d
= game
[gindex
]->data
;
2119 pgn_tag_add(&game
[gindex
]->tag
, "FEN", pgn_game_to_fen(game
[gindex
], d
->b
));
2120 pgn_tag_add(&game
[gindex
]->tag
, "SetUp", "1");
2121 pgn_tag_sort(game
[gindex
]->tag
);
2122 d
->mode
= MODE_PLAY
;
2123 update_all(game
[gindex
]);
2126 void really_do_annotate_finalize(struct input_data_s
*in
,
2127 struct userdata_s
*d
)
2129 HISTORY
*h
= in
->data
;
2139 len
= strlen(in
->str
) + 1;
2140 h
->comment
= Realloc(h
->comment
, len
);
2141 strncpy(h
->comment
, in
->str
, len
);
2146 SET_FLAG(d
->flags
, CF_MODIFIED
);
2147 update_all(game
[gindex
]);
2150 void do_annotate_finalize(WIN
*win
)
2152 struct userdata_s
*d
= game
[gindex
]->data
;
2153 struct input_data_s
*in
= win
->data
;
2155 really_do_annotate_finalize(in
, d
);
2158 void do_find_move_exp_finalize(int init
, int which
)
2161 struct userdata_s
*d
= game
[gindex
]->data
;
2162 static int firstrun
;
2167 if (init
|| !firstrun
) {
2171 if ((ret
= regcomp(&r
, moveexp
, REG_EXTENDED
|REG_NOSUB
)) != 0) {
2172 regerror(ret
, &r
, errbuf
, sizeof(errbuf
));
2173 cmessage(E_REGCOMP_TITLE
, ANYKEY
, "%s", errbuf
);
2180 if ((n
= find_move_exp(game
[gindex
], r
,
2181 (which
== -1) ? 0 : 1, (keycount
) ? keycount
: 1)) == -1)
2184 game
[gindex
]->hindex
= n
;
2185 pgn_board_update(game
[gindex
], d
->b
, game
[gindex
]->hindex
);
2186 update_all(game
[gindex
]);
2189 void do_find_move_exp(WIN
*win
)
2191 struct input_data_s
*in
= win
->data
;
2196 strncpy(moveexp
, in
->str
, sizeof(moveexp
));
2197 do_find_move_exp_finalize(1, which
);
2205 void do_move_jump_finalize(int n
)
2207 struct userdata_s
*d
= game
[gindex
]->data
;
2209 if (n
< 0 || n
> (pgn_history_total(game
[gindex
]->hp
) / 2))
2213 update_status_notify(game
[gindex
], NULL
);
2214 game
[gindex
]->hindex
= (n
) ? n
* 2 - 1 : n
* 2;
2215 pgn_board_update(game
[gindex
], d
->b
, game
[gindex
]->hindex
);
2216 update_all(game
[gindex
]);
2219 void do_move_jump(WIN
*win
)
2221 struct input_data_s
*in
= win
->data
;
2223 if (!in
->str
|| !isinteger(in
->str
)) {
2231 do_move_jump_finalize(atoi(in
->str
));
2236 struct history_menu_s
{
2244 void free_history_menu_data(struct history_menu_s
**h
)
2251 for (i
= 0; h
[i
]; i
++) {
2259 void get_history_data(HISTORY
**hp
, struct history_menu_s
***menu
, int m
,
2263 int t
= pgn_history_total(hp
);
2264 char buf
[MAX_SAN_MOVE_LEN
+ 4];
2266 struct history_menu_s
**hmenu
= *menu
;
2269 for (n
= 0; hmenu
[n
]; n
++);
2273 for (i
= 0; i
< t
; i
++) {
2274 hmenu
= Realloc(hmenu
, (n
+ 2) * sizeof(struct history_menu_s
*));
2275 hmenu
[n
] = Malloc(sizeof(struct history_menu_s
));
2276 snprintf(buf
, sizeof(buf
), "%c%s%s", (turn
== WHITE
) ? 'W' : 'B',
2277 hp
[i
]->move
, (hp
[i
]->comment
|| hp
[i
]->nag
[0]) ? " !" : "");
2278 hmenu
[n
]->line
= strdup(buf
);
2279 hmenu
[n
]->hindex
= i
;
2280 hmenu
[n
]->indent
= 0;
2281 hmenu
[n
]->ravlevel
= depth
;
2282 hmenu
[n
]->move
= (n
&& depth
> hmenu
[n
-1]->ravlevel
) ? m
++ : m
;
2289 get_history_data(hp
[i
]->rav
, &hmenu
, m
, turn
);
2290 for (n
= 0; hmenu
[n
]; n
++);
2298 turn
= (turn
== WHITE
) ? BLACK
: WHITE
;
2304 void history_draw_update(struct menu_input_s
*m
)
2307 struct userdata_s
*d
= g
->data
;
2309 g
->hindex
= m
->selected
+ 1;
2310 update_cursor(g
, m
->selected
);
2311 pgn_board_update(g
, d
->b
, m
->selected
+ 1);
2314 struct menu_item_s
**get_history_items(WIN
*win
)
2316 struct menu_input_s
*m
= win
->data
;
2318 struct userdata_s
*d
= g
->data
;
2319 struct history_menu_s
**hm
= d
->data
;
2320 struct menu_item_s
**items
= m
->items
;
2324 get_history_data(g
->history
, &hm
, 0,
2325 TEST_FLAG(g
->flags
, GF_BLACK_OPENING
));
2326 m
->selected
= g
->hindex
- 1;
2328 if (m
->selected
< 0)
2331 m
->draw_exit_func
= history_draw_update
;
2337 for (i
= 0; items
[i
]; i
++)
2344 for (i
= 0; hm
[i
]; i
++) {
2345 items
= Realloc(items
, (i
+2) * sizeof(struct menu_item_s
*));
2346 items
[i
] = Malloc(sizeof(struct menu_item_s
));
2347 items
[i
]->name
= hm
[i
]->line
;
2348 items
[i
]->value
= NULL
;
2349 items
[i
]->selected
= 0;
2360 void history_menu_quit(struct menu_input_s
*m
)
2365 void history_menu_exit(WIN
*win
)
2368 struct userdata_s
*d
= g
->data
;
2369 struct history_menu_s
**hm
= d
->data
;
2375 for (i
= 0; hm
[i
]; i
++) {
2385 void history_menu_next(struct menu_input_s
*m
)
2388 struct userdata_s
*d
= g
->data
;
2389 struct history_menu_s
**hm
= d
->data
;
2392 for (t
= 0; hm
[t
]; t
++);
2394 if (m
->selected
+ 1 == t
)
2397 n
= hm
[m
->selected
+ 1]->hindex
;
2404 void history_menu_prev(struct menu_input_s
*m
)
2407 struct userdata_s
*d
= g
->data
;
2408 struct history_menu_s
**hm
= d
->data
;
2411 for (t
= 0; hm
[t
]; t
++);
2413 if (m
->selected
- 1 < 0)
2416 n
= hm
[m
->selected
- 1]->hindex
;
2422 void history_menu_help(struct menu_input_s
*m
)
2424 message("History Menu Help", ANYKEY
, "%s", history_menu_help_str
);
2427 void do_annotate_move(HISTORY
*hp
)
2430 struct input_data_s
*in
;
2432 snprintf(buf
, sizeof(buf
), "%s \"%s\"", ANNOTATION_EDIT_TITLE
, hp
->move
);
2433 in
= Calloc(1, sizeof(struct input_data_s
));
2435 in
->efunc
= do_annotate_finalize
;
2436 construct_input(buf
, hp
->comment
, MAX_PGN_LINE_LEN
/ INPUT_WIDTH
, 0,
2437 NAG_PROMPT
, edit_nag
, NULL
, CTRL('T'), in
, -1);
2440 void history_menu_view_annotation(struct menu_input_s
*m
)
2445 view_annotation(g
->history
[m
->selected
]);
2448 void history_menu_annotate_finalize(WIN
*win
)
2450 struct input_data_s
*in
= win
->data
;
2451 GAME g
= in
->moredata
;
2452 struct userdata_s
*d
= g
->data
;
2453 struct history_menu_s
**hm
= d
->data
;
2455 really_do_annotate_finalize(in
, d
);
2456 free_history_menu_data(hm
);
2458 get_history_data(g
->history
, &hm
, 0, TEST_FLAG(g
->flags
, GF_BLACK_OPENING
));
2460 pushkey
= REFRESH_MENU
;
2463 void history_menu_annotate(struct menu_input_s
*m
)
2467 struct input_data_s
*in
;
2468 HISTORY
*hp
= g
->history
[m
->selected
]; // FIXME RAV
2470 snprintf(buf
, sizeof(buf
), "%s \"%s\"", ANNOTATION_EDIT_TITLE
, hp
->move
);
2471 in
= Calloc(1, sizeof(struct input_data_s
));
2473 in
->moredata
= m
->data
;
2474 in
->efunc
= history_menu_annotate_finalize
;
2475 construct_input(buf
, hp
->comment
, MAX_PGN_LINE_LEN
/ INPUT_WIDTH
, 0,
2476 NAG_PROMPT
, edit_nag
, NULL
, CTRL('T'), in
, -1);
2479 void history_menu_details(struct menu_input_s
*m
)
2485 void history_menu_print(WIN
*win
)
2487 struct menu_input_s
*m
= win
->data
;
2489 struct userdata_s
*d
= g
->data
;
2490 struct history_menu_s
**hm
= d
->data
;
2491 struct history_menu_s
*h
= hm
[m
->top
];
2493 char *p
= m
->item
->name
;
2494 int line
= m
->print_line
- 2;
2496 * Solaris 5.9 doesn't have wattr_get() or any function that requires an
2505 for (total
= 0; hm
[total
]; total
++);
2507 wattr_get(win
->w
, &attrs
, &pair
, NULL
);
2508 wattroff(win
->w
, COLOR_PAIR(pair
));
2510 mvwaddch(win
->w
, m
->print_line
, 1, *p
++);
2512 if (h
->hindex
== 0 && line
== 0)
2513 waddch(win
->w
, ACS_ULCORNER
| CP_HISTORY_MENU_LG
);
2514 else if ((!hm
[h
->hindex
+ (win
->rows
- 5) + 1] && line
== win
->rows
- 5) ||
2515 (m
->top
+ line
== total
- 1))
2516 waddch(win
->w
, ACS_LLCORNER
| CP_HISTORY_MENU_LG
);
2517 else if (hm
[m
->top
+ 1]->ravlevel
!= h
->ravlevel
|| !h
->ravlevel
)
2518 waddch(win
->w
, ACS_LTEE
| CP_HISTORY_MENU_LG
);
2520 waddch(win
->w
, ACS_VLINE
| CP_HISTORY_MENU_LG
);
2523 wattron(win
->w
, COLOR_PAIR(pair
) | attrs
);
2526 for (i
= 2; *p
; p
++, i
++)
2527 waddch(win
->w
, (*p
== '!') ? *p
| A_BOLD
: *p
);
2529 while (i
++ < win
->cols
- 2)
2530 waddch(win
->w
, ' ');
2533 void history_menu(GAME g
)
2535 struct menu_key_s
**keys
= NULL
;
2537 add_menu_key(&keys
, KEY_ESCAPE
, history_menu_quit
);
2538 add_menu_key(&keys
, KEY_UP
, history_menu_prev
);
2539 add_menu_key(&keys
, KEY_DOWN
, history_menu_next
);
2540 add_menu_key(&keys
, KEY_F(1), history_menu_help
);
2541 add_menu_key(&keys
, CTRL('a'), history_menu_annotate
);
2542 add_menu_key(&keys
, CTRL('d'), history_menu_details
);
2543 add_menu_key(&keys
, '\n', history_menu_view_annotation
);
2544 construct_menu(LINES
, TAG_WIDTH
, 0, 0, HISTORY_MENU_TITLE
, 1,
2545 get_history_items
, keys
, g
, history_menu_print
, history_menu_exit
);
2548 void do_history_menu()
2550 history_menu(game
[gindex
]);
2553 void do_history_half_move_toggle()
2555 movestep
= (movestep
== 1) ? 2 : 1;
2556 update_history_window(game
[gindex
]);
2559 void do_history_jump_next()
2561 struct userdata_s
*d
= game
[gindex
]->data
;
2563 pgn_history_next(game
[gindex
], d
->b
, (keycount
> 0) ?
2564 config
.jumpcount
* keycount
* movestep
:
2565 config
.jumpcount
* movestep
);
2566 update_all(game
[gindex
]);
2569 void do_history_jump_prev()
2571 struct userdata_s
*d
= game
[gindex
]->data
;
2573 pgn_history_prev(game
[gindex
], d
->b
, (keycount
) ?
2574 config
.jumpcount
* keycount
* movestep
:
2575 config
.jumpcount
* movestep
);
2576 update_all(game
[gindex
]);
2579 void do_history_prev()
2581 struct userdata_s
*d
= game
[gindex
]->data
;
2583 pgn_history_prev(game
[gindex
], d
->b
,
2584 (keycount
) ? keycount
* movestep
: movestep
);
2585 update_all(game
[gindex
]);
2588 void do_history_next()
2590 struct userdata_s
*d
= game
[gindex
]->data
;
2592 pgn_history_next(game
[gindex
], d
->b
, (keycount
) ?
2593 keycount
* movestep
: movestep
);
2594 update_all(game
[gindex
]);
2597 void do_history_mode_finalize(struct userdata_s
*d
)
2600 d
->mode
= MODE_PLAY
;
2601 update_all(game
[gindex
]);
2604 void do_history_mode_confirm(WIN
*win
)
2606 struct userdata_s
*d
= game
[gindex
]->data
;
2611 pgn_history_free(game
[gindex
]->hp
,
2612 game
[gindex
]->hindex
);
2613 pgn_board_update(game
[gindex
], d
->b
,
2614 pgn_history_total(game
[gindex
]->hp
));
2619 if (pgn_history_rav_new(game
[gindex
], d
->b
,
2620 game
[gindex
]->hindex
) != E_PGN_OK
)
2629 if (!TEST_FLAG(d
->flags
, CF_HUMAN
))
2630 add_engine_command(game
[gindex
], ENGINE_READY
,
2631 "setboard %s\n", pgn_game_to_fen(game
[gindex
], d
->b
));
2633 do_history_mode_finalize(d
);
2636 void do_history_toggle()
2638 struct userdata_s
*d
= game
[gindex
]->data
;
2640 // FIXME Resuming from previous history could append to a RAV.
2641 if (game
[gindex
]->hindex
!= pgn_history_total(game
[gindex
]->hp
)) {
2643 construct_message(NULL
, "(r)esume or abort", 0, 1, NULL
, NULL
, NULL
,
2644 do_history_mode_confirm
, 0, 0, "%s",
2645 GAME_RESUME_HISTORY_TEXT
);
2650 if (TEST_FLAG(game
[gindex
]->flags
, GF_GAMEOVER
))
2654 do_history_mode_finalize(d
);
2657 void do_history_annotate()
2659 int n
= game
[gindex
]->hindex
;
2661 if (n
&& game
[gindex
]->hp
[n
- 1]->move
)
2666 do_annotate_move(game
[gindex
]->hp
[n
]);
2669 void do_history_help()
2671 char *buf
= build_help(history_keys
);
2673 construct_message(GAME_HELP_HISTORY_TITLE
, ANYKEY
, 0, 0, NULL
, NULL
, buf
,
2674 do_more_help
, 0, 1, "%s", buf
);
2677 void do_history_find(int which
)
2679 struct input_data_s
*in
;
2682 if (pgn_history_total(game
[gindex
]->hp
) < 2)
2685 in
= Calloc(1, sizeof(struct input_data_s
));
2686 p
= Malloc(sizeof(int));
2689 in
->efunc
= do_find_move_exp
;
2691 if (!*moveexp
|| which
== 0) {
2692 construct_input(FIND_REGEXP
, moveexp
, 1, 0, NULL
, NULL
, NULL
,
2697 do_find_move_exp_finalize(0, which
);
2700 void do_history_find_new()
2705 void do_history_find_prev()
2707 do_history_find(-1);
2710 void do_history_find_next()
2715 void do_history_rav(int which
)
2717 struct userdata_s
*d
= game
[gindex
]->data
;
2719 rav_next_prev(game
[gindex
], d
->b
, which
);
2720 update_all(game
[gindex
]);
2723 void do_history_rav_next()
2728 void do_history_rav_prev()
2733 void do_history_jump()
2735 struct input_data_s
*in
;
2737 if (pgn_history_total(game
[gindex
]->hp
) < 2)
2741 in
= Calloc(1, sizeof(struct input_data_s
));
2742 in
->efunc
= do_move_jump
;
2744 construct_input(GAME_HISTORY_JUMP_TITLE
, NULL
, 1, 1, NULL
,
2745 NULL
, NULL
, 0, in
, 0);
2749 do_move_jump_finalize(keycount
);
2752 static void free_userdata_once(GAME g
)
2754 struct userdata_s
*d
= g
->data
;
2762 if (d
->engine
->enginebuf
) {
2765 for (n
= 0; d
->engine
->enginebuf
[n
]; n
++)
2766 free(d
->engine
->enginebuf
[n
]);
2768 free(d
->engine
->enginebuf
);
2778 static void free_userdata()
2782 for (i
= 0; i
< gtotal
; i
++) {
2783 free_userdata_once(game
[i
]);
2784 game
[i
]->data
= NULL
;
2788 void update_loading_window(int n
)
2791 loadingw
= newwin(3, COLS
/ 2, CALCPOSY(3), CALCPOSX(COLS
/ 2));
2792 loadingp
= new_panel(loadingw
);
2793 wbkgd(loadingw
, CP_MESSAGE_WINDOW
);
2796 wmove(loadingw
, 0, 0);
2797 wclrtobot(loadingw
);
2798 wattron(loadingw
, CP_MESSAGE_BORDER
);
2799 box(loadingw
, ACS_VLINE
, ACS_HLINE
);
2800 wattroff(loadingw
, CP_MESSAGE_BORDER
);
2801 mvwprintw(loadingw
, 1, CENTER_INT((COLS
/ 2),
2802 11 + strlen(itoa(gtotal
))), "Loading... %i%% (%i games)", n
,
2807 static void init_userdata_once(GAME g
, int n
)
2809 struct userdata_s
*d
= NULL
;
2811 d
= Calloc(1, sizeof(struct userdata_s
));
2813 d
->c_row
= 2, d
->c_col
= 5;
2814 SET_FLAG(d
->flags
, CF_NEW
);
2817 if (pgn_board_init_fen(g
, d
->b
, NULL
) != E_PGN_OK
)
2818 pgn_board_init(d
->b
);
2821 void init_userdata()
2825 for (i
= 0; i
< gtotal
; i
++)
2826 init_userdata_once(game
[i
], i
);
2829 void fix_marks(int *start
, int *end
)
2833 *start
= (*start
< 0) ? 0 : *start
;
2834 *end
= (*end
< 0) ? 0 : *end
;
2836 if (*start
> *end
) {
2842 *end
= (*end
> gtotal
) ? gtotal
: *end
;
2845 void do_new_game_finalize(GAME g
)
2847 struct userdata_s
*d
= g
->data
;
2849 d
->mode
= MODE_PLAY
;
2850 update_status_notify(g
, NULL
);
2854 void do_new_game_from_scratch(WIN
*win
)
2856 if (tolower(win
->c
) != 'y')
2862 add_custom_tags(&game
[gindex
]->tag
);
2865 do_new_game_finalize(game
[gindex
]);
2871 add_custom_tags(&game
[gindex
]->tag
);
2872 init_userdata_once(game
[gindex
], gindex
);
2873 do_new_game_finalize(game
[gindex
]);
2876 void do_game_delete_finalize(int n
)
2878 struct userdata_s
*d
;
2880 delete_game((!n
) ? gindex
: -1);
2881 d
= game
[gindex
]->data
;
2882 pgn_board_update(game
[gindex
], d
->b
, pgn_history_total(game
[gindex
]->hp
));
2883 update_all(game
[gindex
]);
2886 void do_game_delete_confirm(WIN
*win
)
2890 if (tolower(win
->c
) != 'y') {
2896 n
= (int *)win
->data
;
2897 do_game_delete_finalize(*n
);
2901 void do_game_delete()
2905 struct userdata_s
*d
;
2909 cmessage(NULL
, ANYKEY
, "%s", E_DELETE_GAME
);
2915 for (i
= n
= 0; i
< gtotal
; i
++) {
2918 if (TEST_FLAG(d
->flags
, CF_DELETE
))
2923 tmp
= GAME_DELETE_GAME_TEXT
;
2926 cmessage(NULL
, ANYKEY
, "%s", E_DELETE_GAME
);
2930 tmp
= GAME_DELETE_ALL_TEXT
;
2933 if (config
.deleteprompt
) {
2934 p
= Malloc(sizeof(int));
2936 construct_message(NULL
, YESNO
, 1, 1, NULL
, NULL
, p
,
2937 do_game_delete_confirm
, 0, 0, tmp
);
2941 do_game_delete_finalize(n
);
2944 void do_find_game_exp_finalize(int which
)
2946 struct userdata_s
*d
= game
[gindex
]->data
;
2949 if ((n
= find_game_exp(gameexp
, (which
== -1) ? 0 : 1,
2950 (keycount
) ? keycount
: 1)) == -1)
2954 d
= game
[gindex
]->data
;
2956 if (pgn_history_total(game
[gindex
]->hp
))
2957 d
->mode
= MODE_HISTORY
;
2959 pgn_board_update(game
[gindex
], d
->b
, pgn_history_total(game
[gindex
]->hp
));
2960 update_all(game
[gindex
]);
2963 void do_find_game_exp(WIN
*win
)
2965 struct input_data_s
*in
= win
->data
;
2970 strncpy(gameexp
, in
->str
, sizeof(gameexp
));
2975 do_find_game_exp_finalize(c
);
2983 void do_game_jump_finalize(int n
)
2985 struct userdata_s
*d
;
2987 if (--n
> gtotal
- 1 || n
< 0)
2991 d
= game
[gindex
]->data
;
2992 pgn_board_update(game
[gindex
], d
->b
, pgn_history_total(game
[gindex
]->hp
));
2993 update_status_notify(game
[gindex
], NULL
);
2994 update_all(game
[gindex
]);
2997 void do_game_jump(WIN
*win
)
2999 struct input_data_s
*in
= win
->data
;
3001 if (!in
->str
|| !isinteger(in
->str
)) {
3009 do_game_jump_finalize(atoi(in
->str
));
3014 void do_load_file(WIN
*win
)
3017 struct input_data_s
*in
= win
->data
;
3018 char *tmp
= in
->str
;
3019 struct userdata_s
*d
;
3026 if ((tmp
= pathfix(tmp
)) == NULL
)
3029 if ((fp
= pgn_open(tmp
)) == NULL
) {
3030 cmessage(ERROR
, ANYKEY
, "%s\n%s", tmp
, strerror(errno
));
3037 * FIXME what is the game state after a parse error?
3039 if (pgn_parse(fp
) == E_PGN_ERR
) {
3040 del_panel(loadingp
);
3045 update_all(game
[gindex
]);
3049 del_panel(loadingp
);
3054 strncpy(loadfile
, tmp
, sizeof(loadfile
));
3055 d
= game
[gindex
]->data
;
3057 if (pgn_history_total(game
[gindex
]->hp
))
3058 d
->mode
= MODE_HISTORY
;
3060 pgn_board_update(game
[gindex
], d
->b
, pgn_history_total(game
[gindex
]->hp
));
3061 update_all(game
[gindex
]);
3070 void do_game_save(WIN
*win
)
3072 struct input_data_s
*in
= win
->data
;
3075 char *tmp
= in
->str
;
3076 char tfile
[FILENAME_MAX
];
3079 if (!tmp
|| (tmp
= pathfix(tmp
)) == NULL
)
3082 if (pgn_is_compressed(tmp
)) {
3083 p
= tmp
+ strlen(tmp
) - 1;
3085 if (*p
!= 'n' || *(p
-1) != 'g' || *(p
-2) != 'p' ||
3087 snprintf(tfile
, sizeof(tfile
), "%s.pgn", tmp
);
3092 if ((p
= strchr(tmp
, '.')) != NULL
) {
3093 if (strcmp(p
, ".pgn") != 0) {
3094 snprintf(tfile
, sizeof(tfile
), "%s.pgn", tmp
);
3099 snprintf(tfile
, sizeof(tfile
), "%s.pgn", tmp
);
3114 void do_get_game_save_input(int n
)
3116 struct input_data_s
*in
= Calloc(1, sizeof(struct input_data_s
));
3117 int *p
= Malloc(sizeof(int));
3119 in
->efunc
= do_game_save
;
3123 construct_input(GAME_SAVE_TITLE
, loadfile
, 1, 1, BROWSER_PROMPT
,
3124 file_browser
, NULL
, '\t', in
, -1);
3127 void do_game_save_multi_confirm(WIN
*win
)
3133 else if (win
->c
== 'a')
3136 update_status_notify(game
[gindex
], "%s", NOTIFY_SAVE_ABORTED
);
3140 do_get_game_save_input(i
);
3143 void do_global_about()
3145 cmessage("ABOUT", ANYKEY
, "%s (%s)\nUsing %s with %i colors "
3146 "and %i color pairs\nCopyright 2002-2007 %s",
3147 PACKAGE_STRING
, pgn_version(), curses_version(), COLORS
,
3148 COLOR_PAIRS
, PACKAGE_BUGREPORT
);
3151 void global_game_next_prev(int which
)
3153 struct userdata_s
*d
;
3155 game_next_prev(game
[gindex
], (which
== 1) ? 1 : 0,
3156 (keycount
) ? keycount
: 1);
3157 d
= game
[gindex
]->data
;
3161 markend
= markstart
+ delete_count
;
3165 markend
= markstart
- delete_count
;
3166 delete_count
= -1; // to fix gindex in the other direction
3170 fix_marks(&markstart
, &markend
);
3173 if (d
->mode
== MODE_HISTORY
)
3174 pgn_board_update(game
[gindex
], d
->b
, game
[gindex
]->hindex
);
3175 else if (d
->mode
== MODE_PLAY
)
3176 pgn_board_update(game
[gindex
], d
->b
, pgn_history_total(game
[gindex
]->hp
));
3178 update_status_notify(game
[gindex
], NULL
);
3179 update_all(game
[gindex
]);
3182 void do_global_next_game()
3184 global_game_next_prev(1);
3187 void do_global_prev_game()
3189 global_game_next_prev(0);
3192 void global_find(int which
)
3194 struct input_data_s
*in
;
3200 in
= Calloc(1, sizeof(struct input_data_s
));
3201 p
= Malloc(sizeof(int));
3204 in
->efunc
= do_find_game_exp
;
3206 if (!*gameexp
|| which
== 0) {
3207 construct_input(GAME_FIND_EXPRESSION_TITLE
, gameexp
, 1, 0,
3208 GAME_FIND_EXPRESSION_PROMPT
, NULL
, NULL
, 0, in
, -1);
3212 do_find_game_exp_finalize(which
);
3215 void do_global_find_new()
3220 void do_global_find_next()
3225 void do_global_find_prev()
3230 void do_global_game_jump()
3232 struct input_data_s
*in
;
3237 in
= Calloc(1, sizeof(struct input_data_s
));
3238 in
->efunc
= do_game_jump
;
3241 construct_input(GAME_JUMP_TITLE
, NULL
, 1, 1, NULL
, NULL
, NULL
, 0, in
,
3246 do_game_jump_finalize(keycount
);
3249 void do_global_toggle_delete()
3258 if (keycount
&& delete_count
== 0) {
3260 delete_count
= keycount
;
3261 update_status_notify(game
[gindex
], "%s (delete)", status
.notify
);
3265 if (markstart
>= 0 && markend
>= 0) {
3266 for (i
= markstart
; i
< markend
; i
++) {
3267 if (toggle_delete_flag(i
)) {
3268 update_all(game
[gindex
]);
3273 gindex
= (delete_count
< 0) ? markstart
: i
- 1;
3274 update_all(game
[gindex
]);
3277 if (toggle_delete_flag(gindex
))
3281 markstart
= markend
= -1;
3283 update_status_window(game
[gindex
]);
3286 void do_global_delete_game()
3291 void do_global_tag_edit()
3293 struct userdata_s
*d
= game
[gindex
]->data
;
3295 edit_tags(game
[gindex
], d
->b
, 1);
3298 void do_global_tag_view()
3300 struct userdata_s
*d
= game
[gindex
]->data
;
3302 edit_tags(game
[gindex
], d
->b
, 0);
3305 void do_global_resume_game()
3307 struct input_data_s
*in
;
3309 in
= Calloc(1, sizeof(struct input_data_s
));
3310 in
->efunc
= do_load_file
;
3311 construct_input(GAME_LOAD_TITLE
, NULL
, 1, 1, BROWSER_PROMPT
, file_browser
,
3312 NULL
, '\t', in
, -1);
3315 void do_global_save_game()
3318 construct_message(NULL
, GAME_SAVE_MULTI_PROMPT
, 1, 1, NULL
, NULL
, NULL
,
3319 do_game_save_multi_confirm
, 0, 0, "%s", GAME_SAVE_MULTI_TEXT
);
3323 do_get_game_save_input(-1);
3326 void do_global_new_game()
3331 void do_global_copy_game()
3335 struct userdata_s
*d
;
3337 do_global_new_game();
3338 n
= pgn_history_total(game
[g
]->history
);
3341 for (i
= 0; i
< n
; i
++)
3342 pgn_history_add(game
[gindex
], game
[g
]->history
[i
]->move
);
3344 n
= pgn_tag_total(game
[g
]->tag
);
3346 for (i
= 0; i
< n
; i
++)
3347 pgn_tag_add(&game
[gindex
]->tag
, game
[g
]->tag
[i
]->name
,
3348 game
[g
]->tag
[i
]->value
);
3350 d
= game
[gindex
]->data
;
3351 pgn_board_update(game
[gindex
], d
->b
,
3352 pgn_history_total(game
[gindex
]->hp
));
3355 void do_global_new_all()
3357 construct_message(NULL
, YESNO
, 1, 1, NULL
, NULL
, NULL
,
3358 do_new_game_from_scratch
, 0, 0, "%s", GAME_NEW_PROMPT
);
3361 void do_global_quit()
3366 void do_global_toggle_engine_window()
3369 enginew
= newwin(LINES
, COLS
, 0, 0);
3370 enginep
= new_panel(enginew
);
3371 window_draw_title(enginew
, ENGINE_IO_TITLE
, COLS
, CP_MESSAGE_TITLE
,
3373 hide_panel(enginep
);
3376 if (panel_hidden(enginep
)) {
3377 update_engine_window(game
[gindex
]);
3382 hide_panel(enginep
);
3387 void do_global_toggle_board_details()
3392 void do_global_toggle_strict_castling()
3394 do_toggle_strict_castling();
3397 // Global and other keys.
3398 static int globalkeys()
3400 struct userdata_s
*d
= game
[gindex
]->data
;
3404 * These cannot be modified and other game mode keys cannot conflict with
3410 keypad(boardw
, TRUE
);
3414 d
->sp
.icon
= d
->sp
.srow
= d
->sp
.scol
= 0;
3415 markend
= markstart
= 0;
3419 update_status_notify(game
[gindex
], NULL
);
3422 if (config
.validmoves
)
3423 pgn_reset_valid_moves(d
->b
);
3430 keycount
= keycount
* 10 + i
;
3434 update_status_notify(game
[gindex
], "Repeat %i", keycount
);
3437 if (d
->mode
== MODE_HISTORY
)
3441 d
->c_row
+= keycount
;
3452 if (d
->mode
== MODE_HISTORY
)
3456 d
->c_row
-= keycount
;
3458 update_status_notify(game
[gindex
], NULL
);
3468 if (d
->mode
== MODE_HISTORY
)
3472 d
->c_col
-= keycount
;
3483 if (d
->mode
== MODE_HISTORY
)
3487 d
->c_col
+= keycount
;
3504 for (i
= 0; global_keys
[i
]; i
++) {
3505 if (input_c
== global_keys
[i
]->c
) {
3506 (*global_keys
[i
]->f
)();
3518 int error_recover
= 0;
3519 struct userdata_s
*d
= game
[gindex
]->data
;
3520 int macro_match
= -1;
3522 gindex
= gtotal
- 1;
3524 if (pgn_history_total(game
[gindex
]->hp
))
3525 d
->mode
= MODE_HISTORY
;
3527 d
->mode
= MODE_PLAY
;
3529 if (d
->mode
== MODE_HISTORY
) {
3530 pgn_board_update(game
[gindex
], d
->b
,
3531 pgn_history_total(game
[gindex
]->hp
));
3534 update_status_notify(game
[gindex
], "%s", GAME_HELP_PROMPT
);
3537 update_all(game
[gindex
]);
3538 update_tag_window(game
[gindex
]->tag
);
3539 wtimeout(boardw
, WINDOW_TIMEOUT
);
3543 char fdbuf
[8192] = {0};
3545 struct timeval tv
= {0, 0};
3553 for (i
= 0; i
< gtotal
; i
++) {
3556 if (d
->engine
&& d
->engine
->pid
!= -1) {
3557 if (d
->engine
->fd
[ENGINE_IN_FD
] > 2) {
3558 if (d
->engine
->fd
[ENGINE_IN_FD
] > n
)
3559 n
= d
->engine
->fd
[ENGINE_IN_FD
];
3561 FD_SET(d
->engine
->fd
[ENGINE_IN_FD
], &rfds
);
3564 if (d
->engine
->fd
[ENGINE_OUT_FD
] > 2) {
3565 if (d
->engine
->fd
[ENGINE_OUT_FD
] > n
)
3566 n
= d
->engine
->fd
[ENGINE_OUT_FD
];
3568 FD_SET(d
->engine
->fd
[ENGINE_OUT_FD
], &wfds
);
3574 if ((n
= select(n
+ 1, &rfds
, &wfds
, NULL
, &tv
)) > 0) {
3575 for (i
= 0; i
< gtotal
; i
++) {
3578 if (d
->engine
&& d
->engine
->pid
!= -1) {
3579 if (FD_ISSET(d
->engine
->fd
[ENGINE_IN_FD
], &rfds
)) {
3580 len
= read(d
->engine
->fd
[ENGINE_IN_FD
], fdbuf
,
3584 if (errno
!= EAGAIN
) {
3585 cmessage(ERROR
, ANYKEY
, "Engine read(): %s",
3587 waitpid(d
->engine
->pid
, &n
, 0);
3595 if (d
->engine
->iobuf
)
3596 d
->engine
->iobuf
= Realloc(d
->engine
->iobuf
, len
+
3597 strlen(d
->engine
->iobuf
) + 1);
3599 d
->engine
->iobuf
= Calloc(1, len
+ 1);
3601 memcpy(d
->engine
->iobuf
, &fdbuf
, len
);
3602 d
->engine
->iobuf
[len
] = 0;
3605 * The fdbuf is full and no newline
3606 * was found. So we'll append the next
3607 * read() to this games buffer.
3609 if (d
->engine
->iobuf
[strlen(d
->engine
->iobuf
) - 1] != '\n')
3612 parse_engine_output(game
[i
], d
->engine
->iobuf
);
3613 free(d
->engine
->iobuf
);
3614 d
->engine
->iobuf
= NULL
;
3619 if (FD_ISSET(d
->engine
->fd
[ENGINE_OUT_FD
], &wfds
)) {
3620 if (d
->engine
->queue
)
3621 send_engine_command(game
[i
]);
3628 cmessage(ERROR
, ANYKEY
, "select(): %s", strerror(errno
));
3633 d
= game
[gindex
]->data
;
3635 if (TEST_FLAG(game
[gindex
]->flags
, GF_GAMEOVER
))
3636 d
->mode
= MODE_HISTORY
;
3639 draw_board(game
[gindex
]);
3640 update_all(game
[gindex
]);
3641 wmove(boardw
, ROWTOMATRIX(d
->c_row
), COLTOMATRIX(d
->c_col
));
3645 * Finds the top level window in the window stack so we know what
3646 * window the wgetch()ed key belongs to.
3649 for (i
= 0; wins
[i
]; i
++);
3652 wtimeout(wp
, WINDOW_TIMEOUT
);
3661 if (macros
&& macro_match
>= 0) {
3662 if (macros
[macro_match
]->n
>= macros
[macro_match
]->total
) {
3663 macros
[macro_match
]->n
= 0;
3668 input_c
= macros
[macro_match
]->keys
[macros
[macro_match
]->n
++];
3671 if ((input_c
= wgetch(wp
)) == ERR
)
3682 keypad(boardw
, TRUE
);
3690 * Run the function associated with the window. When the
3691 * function returns 0 win->efunc is ran (if not NULL) with
3692 * win as the one and only parameter. Then the window is
3695 * The exit function may create another window which will
3696 * mess up the window stack when window_destroy() is called.
3697 * So don't destory the window until the top window is
3698 * destroyable. See window_destroy().
3700 if ((*win
->func
)(win
) == 0) {
3705 window_destroy(win
);
3712 if (!keycount
&& status
.notify
)
3713 update_status_notify(game
[gindex
], NULL
);
3715 if (macros
&& macro_match
< 0) {
3716 for (i
= 0; macros
[i
]; i
++) {
3717 if ((macros
[i
]->mode
== -1 || macros
[i
]->mode
== d
->mode
) &&
3718 input_c
== macros
[i
]->c
) {
3719 input_c
= macros
[i
]->keys
[macros
[i
]->n
++];
3726 if ((n
= globalkeys()) == 1) {
3727 if (macro_match
== -1)
3737 for (i
= 0; edit_keys
[i
]; i
++) {
3738 if (input_c
== edit_keys
[i
]->c
) {
3739 (*edit_keys
[i
]->f
)();
3745 for (i
= 0; play_keys
[i
]; i
++) {
3746 if (input_c
== play_keys
[i
]->c
) {
3747 (*play_keys
[i
]->f
)();
3752 do_play_config_command();
3755 for (i
= 0; history_keys
[i
]; i
++) {
3756 if (input_c
== history_keys
[i
]->c
) {
3757 (*history_keys
[i
]->f
)();
3768 update_status_notify(game
[gindex
], NULL
);
3774 void usage(const char *pn
, int ret
)
3776 fprintf((ret
) ? stderr
: stdout
, "%s",
3778 "Usage: cboard [-hvD] [-p [-VtRSE] <file>]\n"
3779 " -D Dump libchess debugging info to \"libchess.debug\" (stderr)\n"
3781 "Usage: cboard [-hv] [-p [-VtRSE] <file>]\n"
3783 " -p Load PGN file.\n"
3784 " -V Validate a game file.\n"
3785 " -S Validate and output a PGN formatted game.\n"
3786 " -R Like -S but write a reduced PGN formatted game.\n"
3787 " -t Also write custom PGN tags from config file.\n"
3788 " -E Stop processing on file parsing error (overrides config).\n"
3789 " -v Version information.\n"
3790 " -h This help text.\n");
3802 free(config
.engine_cmd
);
3803 free(config
.pattern
);
3804 free(config
.ccfile
);
3805 free(config
.nagfile
);
3806 free(config
.configfile
);
3809 for (i
= 0; config
.keys
[i
]; i
++) {
3810 free(config
.keys
[i
]->str
);
3811 free(config
.keys
[i
]);
3818 for (i
= 0; config
.einit
[i
]; i
++)
3819 free(config
.einit
[i
]);
3825 pgn_tag_free(config
.tag
);
3827 if (curses_initialized
) {
3829 del_panel(historyp
);
3846 void catch_signal(int which
)
3853 if (which
== SIGPIPE
&& quit
)
3856 if (which
== SIGPIPE
)
3857 cmessage(NULL
, ANYKEY
, "%s", E_BROKEN_PIPE
);
3867 keypad(boardw
, TRUE
);
3881 void loading_progress(long total
, long offset
)
3883 int n
= (100 * (offset
/ 100) / (total
/ 100));
3885 if (curses_initialized
)
3886 update_loading_window(n
);
3888 fprintf(stderr
, "Loading... %i%% (%i games)\r", n
, gtotal
);
3893 static void set_defaults()
3895 set_config_defaults();
3898 pgn_config_set(PGN_PROGRESS
, 1024);
3899 pgn_config_set(PGN_PROGRESS_FUNC
, loading_progress
);
3902 int main(int argc
, char *argv
[])
3906 char buf
[FILENAME_MAX
];
3907 char datadir
[FILENAME_MAX
];
3908 int ret
= EXIT_SUCCESS
;
3909 int validate_only
= 0, validate_and_write
= 0;
3910 int write_custom_tags
= 0;
3915 #ifndef HAVE_PROGNAME
3916 __progname
= argv
[0];
3919 if ((config
.pwd
= getpwuid(getuid())) == NULL
)
3920 err(EXIT_FAILURE
, "getpwuid()");
3922 snprintf(datadir
, sizeof(datadir
), "%s/.cboard", config
.pwd
->pw_dir
);
3923 snprintf(buf
, sizeof(buf
), "%s/cc.data", datadir
);
3924 config
.ccfile
= strdup(buf
);
3925 snprintf(buf
, sizeof(buf
), "%s/nag.data", datadir
);
3926 config
.nagfile
= strdup(buf
);
3927 snprintf(buf
, sizeof(buf
), "%s/config", datadir
);
3928 config
.configfile
= strdup(buf
);
3930 if (stat(datadir
, &st
) == -1) {
3931 if (errno
== ENOENT
) {
3932 if (mkdir(datadir
, 0755) == -1)
3933 err(EXIT_FAILURE
, "%s", datadir
);
3936 err(EXIT_FAILURE
, "%s", datadir
);
3941 if (!S_ISDIR(st
.st_mode
))
3942 errx(EXIT_FAILURE
, "%s: %s", datadir
, E_NOTADIR
);
3947 while ((opt
= getopt(argc
, argv
, "DEVtSRhp:v")) != -1) {
3949 while ((opt
= getopt(argc
, argv
, "EVtSRhp:v")) != -1) {
3954 unlink("libchess.debug");
3955 pgn_config_set(PGN_DEBUG
, 1);
3959 write_custom_tags
= 1;
3965 pgn_config_set(PGN_REDUCED
, 1);
3967 validate_and_write
= 1;
3972 printf("%s (%s, %s)\n%s\n", PACKAGE_STRING
, pgn_version(),
3973 curses_version(), COPYRIGHT
);
3976 filetype
= PGN_FILE
;
3977 strncpy(loadfile
, optarg
, sizeof(loadfile
));
3981 usage(argv
[0], EXIT_SUCCESS
);
3985 if ((validate_only
|| validate_and_write
) && !*loadfile
)
3986 usage(argv
[0], EXIT_FAILURE
);
3988 if (access(config
.configfile
, R_OK
) == 0)
3989 parse_rcfile(config
.configfile
);
3992 pgn_config_set(PGN_STOP_ON_ERROR
, 1);
3994 signal(SIGPIPE
, catch_signal
);
3995 signal(SIGCONT
, catch_signal
);
3996 signal(SIGSTOP
, catch_signal
);
3997 signal(SIGINT
, catch_signal
);
3998 signal(SIGALRM
, catch_signal
);
3999 signal(SIGTERM
, catch_signal
);
4005 if ((fp
= pgn_open(loadfile
)) == NULL
)
4006 err(EXIT_FAILURE
, "%s", loadfile
);
4008 ret
= pgn_parse(fp
);
4011 //ret = parse_fen_file(loadfile);
4013 case EPD_FILE
: // Not implemented.
4016 // No file specified. Empty game.
4017 ret
= pgn_parse(NULL
);
4018 add_custom_tags(&game
[gindex
]->tag
);
4022 if (validate_only
|| validate_and_write
) {
4023 if (validate_and_write
) {
4024 for (i
= 0; i
< gtotal
; i
++) {
4025 if (write_custom_tags
)
4026 add_custom_tags(&game
[i
]->tag
);
4028 pgn_write(stdout
, game
[i
]);
4035 else if (ret
== E_PGN_ERR
)
4040 if (initscr() == NULL
)
4041 errx(EXIT_FAILURE
, "%s", E_INITCURSES
);
4043 curses_initialized
= 1;
4045 if (LINES
< 24 || COLS
< 80) {
4047 errx(EXIT_FAILURE
, "Need at least an 80x24 terminal.");
4050 if (has_colors() == TRUE
&& start_color() == OK
)
4053 boardw
= newwin(BOARD_HEIGHT
, BOARD_WIDTH
, 0, COLS
- BOARD_WIDTH
);
4054 boardp
= new_panel(boardw
);
4055 historyw
= newwin(HISTORY_HEIGHT
, HISTORY_WIDTH
, LINES
- HISTORY_HEIGHT
,
4056 COLS
- HISTORY_WIDTH
);
4057 historyp
= new_panel(historyw
);
4058 statusw
= newwin(STATUS_HEIGHT
, STATUS_WIDTH
, LINES
- STATUS_HEIGHT
, 0);
4059 statusp
= new_panel(statusw
);
4060 tagw
= newwin(TAG_HEIGHT
, TAG_WIDTH
, 0, 0);
4061 tagp
= new_panel(tagw
);
4062 keypad(boardw
, TRUE
);
4063 // leaveok(boardw, TRUE);
4064 leaveok(tagw
, TRUE
);
4065 leaveok(statusw
, TRUE
);
4066 leaveok(historyw
, TRUE
);
4070 draw_window_decor();