From dc5fda352920a27da020717ee7775fa89bf9fb7e Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Sat, 11 Jun 2022 18:55:39 -0400 Subject: [PATCH] curses: Add timer display. It seems timeouts interact very weirdly with mouse support in ncurses, but enabling all the button events appears to avoid serious problems. --- src/curses.c | 95 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 16 deletions(-) diff --git a/src/curses.c b/src/curses.c index d5b1cc4..6d86101 100644 --- a/src/curses.c +++ b/src/curses.c @@ -49,6 +49,7 @@ static struct app_state { struct board board; WINDOW *gamewin[WINDOW_MAX], *goalwin[WINDOW_MAX]; + WINDOW *timer; int cursor; } state; @@ -288,6 +289,10 @@ static void setup_mainwin(struct app_state *state) w = MIN(scr_w-split, 3+6*goalsz); h = MIN(scr_h-GAME_YPOS, 2+3*goalsz); realloc_area(&state->goalwin[WINDOW_AREA], h, w, GAME_YPOS, split); + + /* Status area */ + w = MAX(0, scr_w-split-1); + realloc_area(&state->timer, 1, w, GAME_YPOS+h, split+1); } static void app_initialize(int argc, char **argv) @@ -319,7 +324,7 @@ static void app_initialize(int argc, char **argv) } game_reset(&state.board); - state.cursor = 5*state.board.y + state.board.x; + state.cursor = -1; initscr(); start_color(); @@ -328,14 +333,36 @@ static void app_initialize(int argc, char **argv) cbreak(); keypad(stdscr, TRUE); if (enable_mouse) { + /* + * While we only care about a subset of these events, for + * some reason with ncurses failing to enable all of them + * causes timeout to stop working when disabled buttons are + * pressed (or released). + * + * It is not known if other curses have this problem; at least + * pdcurses does not, but the extra events should be harmless + * in any case. + */ +#if HAVE_CURSES_MOUSE_SUPPORT + unsigned long mask = BUTTON1_PRESSED | BUTTON1_RELEASED + | BUTTON2_PRESSED | BUTTON2_RELEASED + | BUTTON3_PRESSED | BUTTON3_RELEASED +#ifdef BUTTON4_PRESSED + | BUTTON4_PRESSED | BUTTON4_RELEASED +#endif +#ifdef BUTTON5_PRESSED + | BUTTON5_PRESSED | BUTTON5_RELEASED +#endif + ; #if HAVE_CURSES_MOUSE_SET - mouse_set(BUTTON1_PRESSED); + mouse_set(mask); #elif HAVE_CURSES_MOUSEMASK - mousemask(BUTTON1_PRESSED, NULL); + mousemask(mask, NULL); #endif #if HAVE_CURSES_MOUSEINTERVAL mouseinterval(0); #endif +#endif /* HAVE_CURSES_MOUSE_SUPPORT */ } noecho(); @@ -358,12 +385,43 @@ static uint_fast32_t do_move(struct app_state *state, int x, int y) if ((mask = game_do_move(&state->board, x, y)) != 0) { curs_redraw_game(state, mask); - refresh(); + doupdate(); } return mask; } +static void do_reset_cursor(struct app_state *state) +{ + state->cursor = 5*state->board.y + state->board.x; +} + +static void update_timer(struct app_state *state, uint_fast32_t ms) +{ + unsigned sec, min; + + sec = ms / 1000; ms %= 1000; + min = sec / 60; sec %= 60; + mvwprintw(state->timer, 0, 0, "Time: %u:%.2u.%.3u", + min, sec, (unsigned)ms); + wclrtoeol(state->timer); + wrefresh(state->timer); +} + +static void do_new_game(struct app_state *state) +{ + game_reset(&state->board); + + do_reset_cursor(state); + curs_redraw_game(state, -1); + curs_redraw_goal(state, -1); + update_timer(state, 0); + doupdate(); + + timeout(33); + game_begin(&state->board); +} + #if HAVE_CURSES_MOUSE_SUPPORT static void do_mouse(struct app_state *state) { @@ -384,12 +442,16 @@ static void do_mouse(struct app_state *state) y = MOUSE_Y_POS; bstate = 0; - if (BUTTON_CHANGED(1)) { - switch (BUTTON_STATUS(1)) { - case BUTTON_RELEASED: bstate |= BUTTON1_RELEASED; - case BUTTON_PRESSED: bstate |= BUTTON1_PRESSED; - } - } + +#define set_bstate_helper(button) do { if (BUTTON_CHANGED(button)) \ + switch (BUTTON_STATUS(button)) { \ + case BUTTON_RELEASED: bstate |= BUTTON ## button ## _RELEASED; break; \ + case BUTTON_PRESSED: bstate |= BUTTON ## button ## _PRESSED; break; \ + } \ +} while (0); + + set_bstate_helper(1); + set_bstate_helper(3); #endif if (bstate == BUTTON1_PRESSED) { uint_fast32_t cursor_mask, move_mask; @@ -409,7 +471,7 @@ static void do_mouse(struct app_state *state) move_mask = do_move(state, x/w, y/h); if ((cursor_mask & move_mask) == 0) { curs_redraw_game(state, cursor_mask); - refresh(); + doupdate(); } } } @@ -420,8 +482,8 @@ static void do_move_cursor(struct app_state *state, int c) uint_fast32_t mask = 1ul << state->cursor; if (state->cursor < 0) { - /* Reset keyboard cursor to the empty position */ - state->cursor = 5*state->board.y + state->board.x; + /* Cursor was hidden; reset it */ + do_reset_cursor(state); } switch (c) { @@ -465,9 +527,7 @@ int main(int argc, char **argv) setlocale(LC_ALL, ""); app_initialize(argc, argv); - curs_redraw_game(&state, -1); - curs_redraw_goal(&state, -1); - refresh(); + do_new_game(&state); while (1) { int c = getch(); @@ -490,6 +550,9 @@ int main(int argc, char **argv) #endif default: do_keystroke(&state, c); + case ERR:; } + + update_timer(&state, game_elapsed(&state.board)); } } -- 2.43.2