]> git.draconx.ca Git - rrace.git/blobdiff - src/curses.c
Use gen-tree.awk to produce curses menu labels.
[rrace.git] / src / curses.c
index 7497395cbaa4f4865f18faf3ea979113a1935bad..cdaf39630b200dcbe88aa2e9b635c0cb57c00c04 100644 (file)
 
 #include "help.h"
 #include "version.h"
-#include "cursesopt.h"
-#include "game.h"
 
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#include "cursesui.h"
+#include "cursesopt.h"
 
 enum {
        GAME_YPOS = 1 // top row of game and goal areas.
@@ -38,40 +36,7 @@ enum {
 static const char *progname = "rrace";
 static const struct option lopts[] = { LOPTS_INITIALIZER, {0} };
 
-enum {
-       WINDOW_TILEBORDER,
-       WINDOW_TILEFILL,
-       WINDOW_AREA,
-       WINDOW_MAX,
-};
-
-/* Colour pair enumeration */
-enum {
-       /* Pairs 1-6 correspond to tile colours */
-       RR_COLOUR_CURSOR = TILE_MAX, // black on black, for the cursor
-       RR_COLOUR_TOOLBAR,           // cyan on black (use reverse video)
-       RR_COLOUR_MAX
-};
-
-static struct app_state {
-       struct board board;
-
-       WINDOW *gamewin[WINDOW_MAX], *goalwin[WINDOW_MAX];
-       WINDOW *toolbar, *timer;
-       int last_input;
-
-       /* Most recently displayed timer value, for screen redraw. */
-       uint_least32_t timer_ms;
-
-       /* Location of the keyboard cursor */
-       int_least8_t cursor;
-
-       /* If true, the goal will be displayed over the main play area. */
-       uint_least8_t view_goal_on_game;
-
-       /* Clicked toolbar item */
-       uint_least8_t toolbar_click;
-} state;
+static struct app_state state;
 
 static void print_version(void)
 {
@@ -110,7 +75,7 @@ static void print_help(void)
 static void draw_tile(WINDOW **win, unsigned colour, unsigned selected,
                       unsigned x, unsigned y, unsigned start_column)
 {
-       WINDOW *border = win[WINDOW_TILEBORDER], *fill = win[WINDOW_TILEFILL];
+       WINDOW *border = win[PLAYWIN_TILEBORDER], *fill = win[PLAYWIN_TILEFILL];
        int w, h, attr, ch, bc = selected ? '#' : 0;
 
        assert(colour < TILE_MAX);
@@ -123,7 +88,7 @@ static void draw_tile(WINDOW **win, unsigned colour, unsigned selected,
        case TILE_BLUE:   ch = 'o'; attr |= A_BOLD; break;
        case TILE_WHITE:  ch = '.'; attr |= A_BOLD; break;
 
-       case TILE_EMPTY: attr = A_BOLD|COLOR_PAIR(RR_COLOUR_CURSOR);
+       case TILE_EMPTY: attr = A_BOLD|COLOR_PAIR(RR_COLOUR_SHADOW);
        }
 
        getmaxyx(border, h, w);
@@ -168,7 +133,7 @@ redraw_tile(WINDOW **win, unsigned x, unsigned y, unsigned start_column,
 static void redraw_area_border(WINDOW **win, unsigned x, unsigned sz)
 {
        int w, h, tr = 0, rs = 0, br = 0, bs = 0, bl = 0;
-       WINDOW *area = win[WINDOW_AREA];
+       WINDOW *area = win[PLAYWIN_AREA];
 
        getmaxyx(stdscr, h, w);
 
@@ -220,10 +185,10 @@ static void curs_redraw_goal(struct app_state *state, uint_fast32_t mask)
        uint_least16_t *gp = state->board.goal;
        int i, x, y;
 
-       if (!state->goalwin[WINDOW_AREA])
+       if (!state->goalwin[PLAYWIN_AREA])
                return;
 
-       getbegyx(state->goalwin[WINDOW_AREA], y, x);
+       getbegyx(state->goalwin[PLAYWIN_AREA], y, x);
        if (mask == -1)
                redraw_area_border(state->goalwin, x, 3);
 
@@ -257,7 +222,7 @@ static WINDOW *realloc_area(WINDOW **orig, int h, int w, int y, int x)
 
 static void realloc_tiles(WINDOW **win, int h)
 {
-       WINDOW *border = win[WINDOW_TILEBORDER], *fill = win[WINDOW_TILEFILL];
+       WINDOW *border = win[PLAYWIN_TILEBORDER], *fill = win[PLAYWIN_TILEFILL];
        int w = 2*h - 1;
 
        if (fill && border) {
@@ -281,60 +246,8 @@ static void realloc_tiles(WINDOW **win, int h)
        if (border)
                delwin(border);
 
-       win[WINDOW_TILEBORDER] = border = newwin(h, w, 0, 0);
-       win[WINDOW_TILEFILL] = derwin(border, h-2, w-2, 1, 1);
-}
-
-/*
- * Given the toolbar function number (between 1 and 10, inclusive), and the
- * total width of the screen, return the character position for the start of
- * its label display.
- *
- * The intention is to divide the width of the screen into 10 roughly
- * equally-sized areas, spreading out the remainder so that the width of
- * each label is a monotone increasing function of the total width.
- *
- * The minimum size of a label is 6 characters (2 of which are used for the
- * number indicator)
- */
-static int toolbar_xpos(int i, int total_width)
-{
-       int button_width = MAX(6, total_width/10);
-       int rem = total_width - 10*button_width;
-       int pos = (i-1)*button_width;
-
-       switch (rem) {
-       case 9: pos += i > 6;
-       case 8: pos += i > 2;
-       case 7: pos += i > 7;
-       case 6: pos += i > 3;
-       case 5: pos += i > 8;
-       case 4: pos += i > 4;
-       case 3: pos += i > 9;
-       case 2: pos += i > 5;
-       }
-
-       return pos;
-}
-
-static void draw_toolbar(struct app_state *state)
-{
-       WINDOW *toolbar = state->toolbar;
-       int i, w, lw;
-
-       getmaxyx(toolbar, i, w);
-       werase(toolbar);
-
-       lw = MAX(6, w/10);
-       mvwprintw(toolbar, 0, toolbar_xpos( 1, w)+2, "%.*s", lw, "Help");
-       mvwprintw(toolbar, 0, toolbar_xpos( 2, w)+2, "%.*s", lw, "NewGame");
-       mvwprintw(toolbar, 0, toolbar_xpos(10, w)+2, "%.*s", lw, "Exit");
-
-       mvwchgat(toolbar, 0, 0, -1, A_REVERSE, RR_COLOUR_TOOLBAR, 0);
-       for (i = 1; i <= 10; i++)
-               mvwprintw(toolbar, 0, toolbar_xpos(i, w), "%2d", i);
-
-       wnoutrefresh(state->toolbar);
+       win[PLAYWIN_TILEBORDER] = border = newwin(h, w, 0, 0);
+       win[PLAYWIN_TILEFILL] = derwin(border, h-2, w-2, 1, 1);
 }
 
 static void setup_mainwin(struct app_state *state)
@@ -363,12 +276,12 @@ static void setup_mainwin(struct app_state *state)
        /* Frame for game area */
        w = MIN(scr_w-2, 3+10*gamesz);
        h = MIN(scr_h-GAME_YPOS, 2+5*gamesz);
-       realloc_area(&state->gamewin[WINDOW_AREA], h, w, GAME_YPOS, 2);
+       realloc_area(&state->gamewin[PLAYWIN_AREA], h, w, GAME_YPOS, 2);
 
        /* Frame for goal area */
        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);
+       realloc_area(&state->goalwin[PLAYWIN_AREA], h, w, GAME_YPOS, split);
 
        /* Status area */
        w = MAX(0, scr_w-split-1);
@@ -376,7 +289,7 @@ static void setup_mainwin(struct app_state *state)
 
        /* Toolbar */
        realloc_area(&state->toolbar, 1, scr_w, scr_h-1, 0);
-       draw_toolbar(state);
+       curs_draw_toolbar(state);
 }
 
 static void app_initialize(int argc, char **argv)
@@ -457,7 +370,7 @@ static void app_initialize(int argc, char **argv)
        init_pair(TILE_GREEN, COLOR_GREEN, COLOR_BLACK);
        init_pair(TILE_BLUE, COLOR_BLUE, COLOR_BLACK);
        init_pair(TILE_WHITE, COLOR_WHITE, COLOR_BLACK);
-       init_pair(RR_COLOUR_CURSOR, COLOR_BLACK, COLOR_BLACK);
+       init_pair(RR_COLOUR_SHADOW, COLOR_BLACK, COLOR_BLACK);
        init_pair(RR_COLOUR_TOOLBAR, COLOR_CYAN, COLOR_BLACK);
 
        setup_mainwin(&state);
@@ -474,7 +387,7 @@ static void update_timer(struct app_state *state, uint_fast32_t ms)
        mvwprintw(state->timer, 0, 0, "Time: %u:%.2u.%.3u",
                  min, sec, (unsigned)ms);
        wclrtoeol(state->timer);
-       wrefresh(state->timer);
+       wnoutrefresh(state->timer);
 }
 
 static uint_fast32_t do_move(struct app_state *state, int x, int y)
@@ -509,7 +422,7 @@ static void do_reset_cursor(struct app_state *state)
        state->cursor = 5*state->board.y + state->board.x;
 }
 
-static void do_new_game(struct app_state *state)
+void curs_new_game(struct app_state *state)
 {
        game_reset(&state->board);
 
@@ -523,42 +436,7 @@ static void do_new_game(struct app_state *state)
        game_begin(&state->board);
 }
 
-static void do_function(struct app_state *state, unsigned func)
-{
-       switch (func) {
-       case 2:
-               do_new_game(state);
-               break;
-       case 10:
-               endwin();
-               exit(0);
-       }
-}
-
 #if HAVE_CURSES_MOUSE_SUPPORT
-/*
- * Returns the toolbar function (1 through 10) under the given x, y screen
- * coordinates, or 0 if the coordinates are outside of the toolbar.
- */
-static int mouse_toolbar_function(struct app_state *state, int x, int y)
-{
-       int toolbar_x, toolbar_y, toolbar_w, toolbar_h, i;
-
-       getbegyx(state->toolbar, toolbar_y, toolbar_x);
-       getmaxyx(state->toolbar, toolbar_h, toolbar_w);
-
-       if ((void)toolbar_x, y != toolbar_y)
-               return 0;
-
-       /* OK, selected a button, determine which one */
-       for (i = 10; i > 1; i--) {
-               if ((void)toolbar_h, x >= toolbar_xpos(i, toolbar_w))
-                       break;
-       }
-
-       return i;
-}
-
 /*
  * Given x, y as screen coordinates, record which (if any) toolbar function
  * label is at that position, to be performed later.
@@ -567,19 +445,21 @@ static int mouse_toolbar_function(struct app_state *state, int x, int y)
  */
 static int press_toolbar(struct app_state *state, int x, int y)
 {
-       return state->toolbar_click = mouse_toolbar_function(state, x, y);
+       return state->toolbar_click = curs_toolbar_mouse_func(state, x, y);
 }
 
 /*
+ * Given x, y as screen coordinates, perform the toolbar action.
+ *
  * Perform the action previously recorded by press_toolbar, if and only if
  * the x, y screen coordinates correspond to the same function.
  */
 static void release_toolbar(struct app_state *state, int x, int y)
 {
-       int func = mouse_toolbar_function(state, x, y);
+       int func = curs_toolbar_mouse_func(state, x, y);
 
        if (func && state->toolbar_click == func) {
-               do_function(state, func);
+               curs_execute_function(state, func);
        }
 
        state->toolbar_click = 0;
@@ -598,8 +478,8 @@ static int press_tile(struct app_state *state, int x, int y)
        int game_x, game_y, tile_w, tile_h;
        uint_fast32_t cursor_mask, move_mask;
 
-       getbegyx(state->gamewin[WINDOW_AREA], game_y, game_x);
-       getmaxyx(state->gamewin[WINDOW_TILEBORDER], tile_h, tile_w);
+       getbegyx(state->gamewin[PLAYWIN_AREA], game_y, game_x);
+       getmaxyx(state->gamewin[PLAYWIN_TILEBORDER], tile_h, tile_w);
        tile_w += tile_w & 1;
 
        /* special case the left spacer column */
@@ -746,12 +626,12 @@ static void do_keystroke(struct app_state *state, int c)
 
        /* ESC+# keys */
        if (last_input == '\33' && c >= '0' && c <= '9') {
-               do_function(state, (c -= '0') == 0 ? 10 : c);
+               curs_execute_function(state, (c -= '0') == 0 ? 10 : c);
        }
 
        /* F# keys */
        if (c >= KEY_F(1) && c <= KEY_F(10)) {
-               do_function(state, c - KEY_F0);
+               curs_execute_function(state, c - KEY_F0);
        }
 }
 
@@ -768,8 +648,9 @@ void do_mainloop(struct app_state *state)
                refresh();
                curs_redraw_game(state, -1);
                curs_redraw_goal(state, -1);
-               draw_toolbar(state);
+               curs_draw_toolbar(state);
                update_timer(state, state->timer_ms);
+               doupdate();
                break;
 #endif
 #if HAVE_CURSES_MOUSE_SUPPORT
@@ -782,8 +663,10 @@ void do_mainloop(struct app_state *state)
        case ERR:;
        }
 
-       if (state->board.x <= 4)
+       if (state->board.x <= 4) {
                update_timer(state, game_elapsed(&state->board));
+               doupdate();
+       }
 }
 
 int main(int argc, char **argv)
@@ -791,7 +674,7 @@ int main(int argc, char **argv)
        setlocale(LC_ALL, "");
        app_initialize(argc, argv);
 
-       do_new_game(&state);
+       curs_new_game(&state);
        while (1)
                do_mainloop(&state);
        abort();