#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.
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)
{
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);
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);
static int
redraw_tile(WINDOW **win, unsigned x, unsigned y, unsigned start_column,
- uint_fast32_t bit0, uint_fast32_t bit1, uint_fast32_t bit2,
- unsigned selected)
+ uint_least32_t *gp, unsigned selected)
{
- uint_fast32_t pos = board_position(x, y);
- unsigned char tile = 0;
+ unsigned tile = board_tile(gp, 5*y+x);
- if (bit0 & pos) tile |= 1;
- if (bit1 & pos) tile |= 2;
- if (bit2 & pos) tile |= 4;
assert(tile < TILE_MAX);
-
draw_tile(win, tile, selected, x, y, start_column);
return tile;
}
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);
for (i = 0; i < 25; i++) {
if (mask & 1) {
redraw_tile(state->gamewin, i%5, i/5,
- 4, gp[0], gp[1], gp[2],
- i == state->cursor);
+ 4, gp, i == state->cursor);
}
mask >>= 1;
}
static void curs_redraw_goal(struct app_state *state, uint_fast32_t mask)
{
- uint_least16_t *gp = state->board.goal;
+ uint_least32_t gp[3] = {
+ state->board.goal[0],
+ state->board.goal[1],
+ state->board.goal[2]
+ };
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);
for (i = 0; i < 9; i++) {
if (mask & 1) {
- redraw_tile(state->goalwin, i%3, i/3,
- x+2, gp[0], gp[1], gp[2], 0);
+ redraw_tile(state->goalwin, i%3, i/3, x+2, gp, 0);
}
mask >>= 1;
}
}
+/* Thin wrapper around wresize which returns ERR if support is unavailable. */
+static int do_wresize(WINDOW *window, int h, int w)
+{
+#if HAVE_CURSES_WRESIZE
+ return wresize(window, h, w);
+#endif
+ return ERR;
+}
+
static WINDOW *realloc_area(WINDOW **orig, int h, int w, int y, int x)
{
WINDOW *win = *orig;
if (win) {
-#if HAVE_CURSES_WRESIZE
- if (wresize(win, h, w) != ERR) {
+ if (do_wresize(win, h, w) != ERR) {
mvwin(win, y, x);
return win;
}
-#endif
+
delwin(win);
}
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) {
int old_w, old_h;
-#if HAVE_CURSES_WRESIZE
- if (wresize(fill, h-2, w-2) != ERR
- && wresize(border, h, w) != ERR)
- {
+ if (do_wresize(fill, h-2, w-2) != ERR
+ && do_wresize(border, h, w) != ERR)
return;
- }
-#endif
getmaxyx(border, old_h, old_w);
if (old_h == 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)
/* 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);
/* 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)
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);
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)
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);
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.
*/
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;
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 */
/* 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);
}
}
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
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)
setlocale(LC_ALL, "");
app_initialize(argc, argv);
- do_new_game(&state);
+ curs_new_game(&state);
while (1)
do_mainloop(&state);
abort();