#include "cursesopt.h"
#include "game.h"
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
static const char *progname = "rrace";
static const struct option lopts[] = { LOPTS_INITIALIZER, {0} };
+enum {
+ WINDOW_TILEBORDER,
+ WINDOW_TILEFILL,
+ WINDOW_AREA,
+ WINDOW_MAX,
+};
+
static struct app_state {
struct board board;
- WINDOW *tile_border, *tile_fill;
+ WINDOW *gamewin[WINDOW_MAX], *goalwin[WINDOW_MAX];
} state;
static void print_version(void)
printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
}
-static void
-draw_tile(struct app_state *state, unsigned colour, unsigned x, unsigned y)
+static void draw_tile(WINDOW **win, unsigned colour,
+ unsigned x, unsigned y, unsigned start_column)
{
+ WINDOW *border = win[WINDOW_TILEBORDER], *fill = win[WINDOW_TILEFILL];
int attr, ch;
int w, h;
case TILE_WHITE: ch = '.'; attr |= A_BOLD; break;
}
- getmaxyx(state->tile_border, h, w);
+ getmaxyx(border, h, w);
w = 2*(w+1)/2;
- if (mvwin(state->tile_border, 2+h*y, 4+w*x) == ERR)
+ if (mvwin(border, 2+h*y, start_column+w*x) == ERR)
return;
if (colour != TILE_EMPTY) {
- wattrset(state->tile_border, attr);
- box(state->tile_border, 0, 0);
+ wattrset(border, attr);
+ box(border, 0, 0);
- mvderwin(state->tile_fill, 1, 1);
- wbkgdset(state->tile_fill, A_REVERSE|attr|ch);
- werase(state->tile_fill);
+ mvderwin(fill, 1, 1);
+ wbkgdset(fill, A_REVERSE|attr|ch);
+ werase(fill);
} else {
- werase(state->tile_border);
+ werase(border);
}
- wnoutrefresh(state->tile_border);
+ wnoutrefresh(border);
}
-static int curs_redraw_tile(struct app_state *state, unsigned x, unsigned y)
+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)
{
uint_fast32_t pos = board_position(x, y);
unsigned char tile = 0;
- if (state->board.game[0] & pos) tile |= 1;
- if (state->board.game[1] & pos) tile |= 2;
- if (state->board.game[2] & pos) tile |= 4;
+ if (bit0 & pos) tile |= 1;
+ if (bit1 & pos) tile |= 2;
+ if (bit2 & pos) tile |= 4;
assert(tile < TILE_MAX);
- draw_tile(state, tile, x, y);
+ draw_tile(win, tile, 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];
+
+ getmaxyx(stdscr, h, w);
+
+ if (h <= 2) {
+ bl = ACS_ULCORNER, br = ACS_URCORNER;
+ } else if (h <= 3*sz+2) {
+ bl = br = ACS_VLINE, bs = ' ';
+ }
+
+ if (w <= x+1) {
+ tr = ACS_ULCORNER, br = ACS_LLCORNER;
+ } else if (w <= 6*sz+x+2) {
+ tr = ACS_HLINE, rs = ' ';
+ br = br ? ' ' : ACS_HLINE;
+ }
+
+ wborder(area, 0, rs, 0, bs, 0, tr, bl, br);
+ wnoutrefresh(area);
+}
+
static void curs_redraw_game(struct app_state *state, uint_fast32_t mask)
{
+ uint_least32_t *gp = state->board.game;
int i;
+ if (mask == -1)
+ redraw_area_border(state->gamewin, 2, 5);
+
for (i = 0; i < 25; i++) {
if (mask & 1) {
- curs_redraw_tile(state, i%5, i/5);
+ redraw_tile(state->gamewin, i%5, i/5,
+ 4, gp[0], gp[1], gp[2]);
}
mask >>= 1;
}
}
-static void curs_alloc_tiles(struct app_state *state)
+static void curs_redraw_goal(struct app_state *state, uint_fast32_t mask)
{
- int w, h, tilesz;
- WINDOW *tile;
+ uint_least16_t *gp = state->board.goal;
+ int i, x, y;
- getmaxyx(stdscr, h, w);
- tilesz = (h - 4) / 5;
- if (tilesz < 3)
- tilesz = 3;
+ if (!state->goalwin[WINDOW_AREA])
+ return;
+
+ getbegyx(state->goalwin[WINDOW_AREA], y, x);
+ if (mask == -1)
+ redraw_area_border(state->goalwin, x, 3);
- h = -1;
- if (state->tile_border) {
- getmaxyx(state->tile_border, h, w);
+ 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]);
+ }
+ mask >>= 1;
}
+}
- if (h == tilesz) {
- /* Nothing to do. */
- return;
+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) {
+ mvwin(win, y, x);
+ return win;
+ }
+#endif
+ delwin(win);
+ }
+
+ if (w > 0 && h > 0)
+ return *orig = subwin(stdscr, h, w, y, x);
+ return *orig = NULL;
+}
+
+static void realloc_tiles(WINDOW **win, int h)
+{
+ WINDOW *border = win[WINDOW_TILEBORDER], *fill = win[WINDOW_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)
+ {
+ return;
+ }
+#endif
+
+ getmaxyx(border, old_h, old_w);
+ if (old_h == h)
+ return;
}
- if (state->tile_border) {
- delwin(state->tile_fill);
- delwin(state->tile_border);
+ if (fill)
+ delwin(fill);
+ if (border)
+ delwin(border);
+
+ win[WINDOW_TILEBORDER] = border = newwin(h, w, 0, 0);
+ win[WINDOW_TILEFILL] = derwin(border, h-2, w-2, 1, 1);
+}
+
+static void setup_mainwin(struct app_state *state)
+{
+ int w, h, gamesz, goalsz, scr_w, scr_h, split;
+
+ getmaxyx(stdscr, scr_h, scr_w);
+
+ /* First try to fit the game tiles based on window height. */
+ gamesz = MAX(3, (scr_h - 4) / 5);
+
+ /* Adjust downward until we can fit smallest possible goal area. */
+ for (; split = 5+10*gamesz, gamesz > 3; gamesz--) {
+ if (split + 20 < scr_w)
+ break;
}
- state->tile_border = tile = newwin(tilesz, 2*tilesz-1, 0, 0);
- state->tile_fill = derwin(tile, tilesz-2, 2*tilesz-3, 1, 1);
+ /* Pick a goal size that will fit in the remaining area */
+ goalsz = MAX(3, (scr_w - split - 4) / 6);
+ if (goalsz >= gamesz)
+ goalsz = MAX(3, gamesz - 1);
+
+ realloc_tiles(state->gamewin, gamesz);
+ realloc_tiles(state->goalwin, goalsz);
+
+ /* Frame for game area */
+ w = MIN(scr_w-2, 3+10*gamesz);
+ h = MIN(scr_h-1, 2+5*gamesz);
+ realloc_area(&state->gamewin[WINDOW_AREA], h, w, 1, 2);
+
+ /* Frame for goal area */
+ w = MIN(scr_w-split, 3+6*goalsz);
+ h = MIN(scr_h-1, 2+3*goalsz);
+ realloc_area(&state->goalwin[WINDOW_AREA], h, w, 1, split);
}
static void app_initialize(int argc, char **argv)
{
+ int enable_mouse = 1;
int opt;
if (argc > 0)
while ((opt = getopt_long(argc, argv, SOPT_STRING, lopts, 0)) != -1) {
switch (opt) {
+ case LOPT_MOUSE:
+ enable_mouse = 2;
+ break;
+ case LOPT_NO_MOUSE:
+ enable_mouse = 0;
+ break;
case LOPT_VERSION:
print_version();
exit(EXIT_SUCCESS);
cbreak();
keypad(stdscr, TRUE);
- mousemask(BUTTON1_PRESSED, NULL);
- mouseinterval(0);
+ if (enable_mouse) {
+#if HAVE_CURSES_MOUSE_SET
+ mouse_set(BUTTON1_PRESSED);
+#elif HAVE_CURSES_MOUSEMASK
+ mousemask(BUTTON1_PRESSED, NULL);
+#endif
+#if HAVE_CURSES_MOUSEINTERVAL
+ mouseinterval(0);
+#endif
+ }
+
noecho();
init_pair(TILE_RED, COLOR_RED, COLOR_BLACK);
init_pair(TILE_BLUE, COLOR_BLUE, COLOR_BLACK);
init_pair(TILE_WHITE, COLOR_WHITE, COLOR_BLACK);
- curs_alloc_tiles(&state);
+ setup_mainwin(&state);
refresh();
}
}
}
-static void do_mouse(struct app_state *state, MEVENT *mev)
+#if HAVE_CURSES_MOUSE_SUPPORT
+static void do_mouse(struct app_state *state)
{
- if (mev->bstate == BUTTON1_PRESSED) {
- int w, h, x, y;
+ unsigned long bstate;
+ int x, y;
+
+#if HAVE_CURSES_GETMOUSE_NCURSES
+ MEVENT mev;
+
+ if (getmouse(&mev) == ERR)
+ return;
+
+ x = mev.x, y = mev.y;
+ bstate = mev.bstate;
+#elif HAVE_CURSES_REQUEST_MOUSE_POS
+ request_mouse_pos();
+ x = MOUSE_X_POS;
+ 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;
+ }
+ }
+#endif
+ if (bstate == BUTTON1_PRESSED) {
+ int w, h;
/* Determine size of the game area */
- getmaxyx(state->tile_border, h, w);
+ getmaxyx(state->gamewin[WINDOW_TILEBORDER], h, w);
w = 2*(w+1)/2;
- if (mev->x < 4 || (x = mev->x - 4)/5 >= w) return;
- if (mev->y < 2 || (y = mev->y - 2)/5 >= h) return;
+ if (x < 4 || (x -= 4)/5 >= w) return;
+ if (y < 2 || (y -= 2)/5 >= h) return;
do_move(state, x/w, y/h);
}
}
+#endif
int main(int argc, char **argv)
{
app_initialize(argc, argv);
curs_redraw_game(&state, -1);
+ curs_redraw_goal(&state, -1);
refresh();
while (1) {
int c = getch();
- MEVENT mev;
switch (c) {
+#ifdef KEY_RESIZE
case KEY_RESIZE:
- curs_alloc_tiles(&state);
+ setup_mainwin(&state);
clear();
refresh();
curs_redraw_game(&state, -1);
+ curs_redraw_goal(&state, -1);
refresh();
break;
+#endif
+#if HAVE_CURSES_MOUSE_SUPPORT
case KEY_MOUSE:
- if (getmouse(&mev) != ERR)
- do_mouse(&state, &mev);
+ do_mouse(&state);
break;
+#endif
}
}
}