From cd0f70f86d1cedffcd1bacf57d9250fdff1f7af3 Mon Sep 17 00:00:00 2001 From: Paul Duncan Date: Sun, 13 Jan 2019 08:29:12 -0500 Subject: refactor sok-sdl, add zoom and warp_buf support --- meson.build | 3 + src/sdl/action.c | 37 +++++- src/sdl/action.h | 5 +- src/sdl/color.c | 25 ++++ src/sdl/color.h | 20 ++++ src/sdl/draw.c | 169 +++++++++++++++++++++++++++ src/sdl/draw.h | 21 ++++ src/sdl/main.c | 335 ++++++++++++++++++----------------------------------- src/sdl/util.h | 13 +++ src/sdl/warp-buf.c | 52 +++++++++ src/sdl/warp-buf.h | 17 +++ 11 files changed, 473 insertions(+), 224 deletions(-) create mode 100644 src/sdl/color.c create mode 100644 src/sdl/color.h create mode 100644 src/sdl/draw.c create mode 100644 src/sdl/draw.h create mode 100644 src/sdl/util.h create mode 100644 src/sdl/warp-buf.c create mode 100644 src/sdl/warp-buf.h diff --git a/meson.build b/meson.build index d8c3b35..b3c7cbd 100644 --- a/meson.build +++ b/meson.build @@ -19,7 +19,10 @@ executable('sok-text', sources + [ # sdl exe executable('sok-sdl', sources + [ 'src/text/levels.c', + 'src/sdl/warp-buf.c', + 'src/sdl/color.c', 'src/sdl/action.c', + 'src/sdl/draw.c', 'src/sdl/main.c', ], dependencies: [ dependency('SDL2'), diff --git a/src/sdl/action.c b/src/sdl/action.c index a4dcb79..8307f19 100644 --- a/src/sdl/action.c +++ b/src/sdl/action.c @@ -24,8 +24,8 @@ keycode_to_dir(const SDL_Keycode code) { } } -action_t -get_action( +static action_t +get_key_action( const SDL_Keycode code ) { switch (code) { @@ -61,3 +61,36 @@ get_action( return (action_t) { .type = ACTION_NONE }; } } + +static action_t +get_wheel_action( + const Sint32 y +) { + if (y > 0) { + return (action_t) { .type = ACTION_ZOOM_IN, .data = y }; + } else if (y < 0) { + return (action_t) { .type = ACTION_ZOOM_OUT, .data = -y }; + } else { + return (action_t) { .type = ACTION_NONE }; + } +} + +action_t +get_action( + const SDL_Event * const ev +) { + switch (ev->type) { + case SDL_QUIT: + return (action_t) { .type = ACTION_QUIT }; + break; + case SDL_KEYUP: + return get_key_action(ev->key.keysym.sym); + break; + case SDL_MOUSEWHEEL: + return get_wheel_action(ev->wheel.y); + break; + default: + // ignore event + return (action_t) { .type = ACTION_NONE }; + } +} diff --git a/src/sdl/action.h b/src/sdl/action.h index d6d9870..4713f62 100644 --- a/src/sdl/action.h +++ b/src/sdl/action.h @@ -10,6 +10,9 @@ typedef enum { ACTION_WARP, ACTION_WARP_BUF_PUSH, ACTION_WARP_BUF_POP, + ACTION_ZOOM_IN, + ACTION_ZOOM_OUT, + ACTION_ZOOM_RESET, ACTION_UNDO, ACTION_NEXT, ACTION_SOLVE, @@ -22,6 +25,6 @@ typedef struct { uint64_t data; } action_t; -action_t get_action(const SDL_Keycode); +action_t get_action(const SDL_Event * const); #endif /* ACTION_H */ diff --git a/src/sdl/color.c b/src/sdl/color.c new file mode 100644 index 0000000..1e75cb6 --- /dev/null +++ b/src/sdl/color.c @@ -0,0 +1,25 @@ +#include +#include "color.h" + +static const SDL_Color +PALETTE[] = { + { .r = 0x00, .g = 0x00, .b = 0x00, .a = 0xFF }, // COLOR_BG + { .r = 0x00, .g = 0xFF, .b = 0x00, .a = 0xFF }, // COLOR_BG_WON + { .r = 0x66, .g = 0x66, .b = 0x66, .a = 0xFF }, // COLOR_WALL + { .r = 0x00, .g = 0xFF, .b = 0x00, .a = 0xFF }, // COLOR_GOAL + { .r = 0xFF, .g = 0x00, .b = 0x00, .a = 0xFF }, // COLOR_HOME + { .r = 0xFF, .g = 0xFF, .b = 0x00, .a = 0xFF }, // COLOR_HOME_GOAL + { .r = 0x00, .g = 0x00, .b = 0xFF, .a = 0xFF }, // COLOR_BOX + { .r = 0x00, .g = 0xFF, .b = 0xFF, .a = 0xFF }, // COLOR_BOX_GOAL + { 0, 0, 0, 0 }, // COLOR_LAST +}; + +bool +set_color( + SDL_Renderer * const renderer, + const color_t ofs +) { + // FIXME: check for error? + const SDL_Color c = PALETTE[ofs]; + return SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a) < 0; +} diff --git a/src/sdl/color.h b/src/sdl/color.h new file mode 100644 index 0000000..5cff762 --- /dev/null +++ b/src/sdl/color.h @@ -0,0 +1,20 @@ +#ifndef COLOR_H +#define COLOR_H + +#include + +typedef enum { + COLOR_BG, + COLOR_BG_WON, + COLOR_WALL, + COLOR_GOAL, + COLOR_HOME, + COLOR_HOME_GOAL, + COLOR_BOX, + COLOR_BOX_GOAL, + COLOR_LAST, +} color_t; + +_Bool set_color(SDL_Renderer * const, const color_t); + +#endif /* COLOR_H */ diff --git a/src/sdl/draw.c b/src/sdl/draw.c new file mode 100644 index 0000000..f6e350b --- /dev/null +++ b/src/sdl/draw.c @@ -0,0 +1,169 @@ +#include // bool +#include "util.h" // warn()/die() +#include "color.h" // set_color() +#include "draw.h" + +static size_t +get_cell_size( + const draw_ctx_t * const draw_ctx +) { + return 32 + 8 * *draw_ctx->zoom; +} + +static SDL_Rect +get_cell_rect( + const draw_ctx_t * const draw_ctx, + const sok_pos_t pos +) { + const size_t cell_size = get_cell_size(draw_ctx); + + return (SDL_Rect) { + draw_ctx->render_ofs.x + pos.x * cell_size, + draw_ctx->render_ofs.y + pos.y * cell_size, + cell_size, + cell_size + }; +} + +static bool +draw_on_size( + const sok_ctx_t * const ctx, + const sok_pos_t level_size, + void * const data +) { + draw_ctx_t * const draw_ctx = data; + const size_t cell_size = get_cell_size(draw_ctx); + + // get renderer size + int renderer_x, renderer_y; + if (SDL_GetRendererOutputSize(draw_ctx->renderer, &renderer_x, &renderer_y)) { + die("SDL_GetRendererOutputSize(): %s", SDL_GetError()); + } + + // calculate renderer offset + draw_ctx->render_ofs.x = (renderer_x - level_size.x * cell_size) / 2; + draw_ctx->render_ofs.y = (renderer_y - level_size.y * cell_size) / 2; + + return true; +} + +static bool +draw_on_walls_start( + const sok_ctx_t * const ctx, + void * const data +) { + draw_ctx_t * const draw_ctx = data; + set_color(draw_ctx->renderer, COLOR_WALL); + + return true; +} + +static bool +draw_on_wall( + const sok_ctx_t * const ctx, + const sok_pos_t pos, + void * const data +) { + draw_ctx_t * const draw_ctx = data; + const SDL_Rect rect = get_cell_rect(draw_ctx, pos); + + if (SDL_RenderFillRect(draw_ctx->renderer, &rect)) { + die("SDL_RenderFillRect(): %s", SDL_GetError()); + } + + return true; +} + +static bool +draw_on_goals_start( + const sok_ctx_t * const ctx, + void * const data +) { + draw_ctx_t * const draw_ctx = data; + set_color(draw_ctx->renderer, COLOR_GOAL); + + return true; +} + +static bool +draw_on_goal( + const sok_ctx_t * const ctx, + const sok_pos_t pos, + const bool has_player, + const bool has_box, + void * const data +) { + draw_ctx_t * const draw_ctx = data; + const SDL_Rect rect = get_cell_rect(draw_ctx, pos); + + if (SDL_RenderFillRect(draw_ctx->renderer, &rect)) { + die("SDL_RenderFillRect(): %s", SDL_GetError()); + } + + return true; +} + +static bool +draw_on_home( + const sok_ctx_t * const ctx, + const sok_pos_t pos, + const bool has_goal, + void * const data +) { + draw_ctx_t * const draw_ctx = data; + const SDL_Rect rect = get_cell_rect(draw_ctx, pos); + + set_color(draw_ctx->renderer, has_goal ? COLOR_HOME_GOAL : COLOR_HOME); + + if (SDL_RenderFillRect(draw_ctx->renderer, &rect)) { + die("SDL_RenderFillRect(): %s", SDL_GetError()); + } + + return true; +} + +static bool +draw_on_box( + const sok_ctx_t * const ctx, + const sok_pos_t pos, + const bool has_goal, + void * const data +) { + draw_ctx_t * const draw_ctx = data; + const SDL_Rect rect = get_cell_rect(draw_ctx, pos); + + set_color(draw_ctx->renderer, has_goal ? COLOR_BOX_GOAL : COLOR_BOX); + + if (SDL_RenderFillRect(draw_ctx->renderer, &rect)) { + die("SDL_RenderFillRect(): %s", SDL_GetError()); + } + + return true; +} + +static const sok_ctx_walk_cbs_t +DRAW_CBS = { + .on_size = draw_on_size, + .on_walls_start = draw_on_walls_start, + .on_wall = draw_on_wall, + .on_goals_start = draw_on_goals_start, + .on_goal = draw_on_goal, + .on_home = draw_on_home, + .on_box = draw_on_box, +}; + +void +draw( + draw_ctx_t * const draw_ctx +) { + // clear background + set_color(draw_ctx->renderer, sok_ctx_is_done(draw_ctx->ctx) ? COLOR_BG_WON : COLOR_BG); + SDL_RenderClear(draw_ctx->renderer); + + // render + sok_ctx_walk(draw_ctx->ctx, &DRAW_CBS, draw_ctx); + + // flip + SDL_RenderPresent(draw_ctx->renderer); +} + diff --git a/src/sdl/draw.h b/src/sdl/draw.h new file mode 100644 index 0000000..304d972 --- /dev/null +++ b/src/sdl/draw.h @@ -0,0 +1,21 @@ +#ifndef DRAW_H +#define DRAW_H + +#include // size_t +#include +#include "../libsok/sok.h" +#include "../text/levels.h" + +typedef struct { + SDL_Renderer * const renderer; + const sok_ctx_t * const ctx; + const size_t * const level_num; + const level_t *level; + const size_t * const zoom; + + sok_pos_t render_ofs; +} draw_ctx_t; + +void draw(draw_ctx_t * const); + +#endif /* DRAW_H */ diff --git a/src/sdl/main.c b/src/sdl/main.c index 831d3f7..882eefb 100644 --- a/src/sdl/main.c +++ b/src/sdl/main.c @@ -6,171 +6,47 @@ #include #include "../text/levels.h" #include "../libsok/sok.h" +#include "util.h" #include "action.h" - -#define warn(...) do { \ - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__); \ -} while (0) - -#define die(...) do { \ - SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__); \ - exit(EXIT_FAILURE); \ -} while (0) - -static void -solve_on_error( - const char * const err -) { - die("Error solving level: %s", err); -} +#include "warp-buf.h" +#include "draw.h" static void draw_moves( const sok_ctx_t * const ctx, const size_t skip_moves ) { - printf("Solution (%zu moves): ", ctx->num_moves - skip_moves); + char buf[1024] = { 0 }; + size_t ofs = 0; + for (size_t i = skip_moves; i < ctx->num_moves; i++) { if (ctx->moves[i].dir >= SOK_DIR_LAST) { die("invalid move: %u", ctx->moves[i].dir); } - fputs(SOK_DIR_TO_STR(ctx->moves[i].dir), stdout); - } - printf("\n"); -} - -static const SDL_Color -RED = { .r = 0x00, .g = 0xFF, .b = 0x00, .a = 0xFF }, -BLACK = { .r = 0x00, .g = 0x00, .b = 0x00, .a = 0xFF }; - -static SDL_Color -get_bg( - const sok_ctx_t * const ctx -) { - return (sok_ctx_is_done(ctx)) ? RED : BLACK; -} - -bool draw_on_wall( - const sok_ctx_t * const ctx, - const sok_pos_t pos, - void * const data -) { - SDL_Renderer *renderer = data; - SDL_Rect rect = { 32 * pos.x, 32 * pos.y, 32, 32 }; - - SDL_SetRenderDrawColor(renderer, 0x66, 0x66, 0x66, 0xFF); - - if (SDL_RenderFillRect(renderer, &rect)) { - die("SDL_RenderFillRect(): %s", SDL_GetError()); - } - - return true; -} - -bool draw_on_goal( - const sok_ctx_t * const ctx, - const sok_pos_t pos, - const bool has_player, - const bool has_box, - void * const data -) { - SDL_Renderer *renderer = data; - SDL_Rect rect = { 32 * pos.x, 32 * pos.y, 32, 32 }; - - SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF); - - if (SDL_RenderFillRect(renderer, &rect)) { - die("SDL_RenderFillRect(): %s", SDL_GetError()); - } - - return true; -} - -bool draw_on_home( - const sok_ctx_t * const ctx, - const sok_pos_t pos, - const bool has_goal, - void * const data -) { - SDL_Renderer *renderer = data; - SDL_Rect rect = { 32 * pos.x, 32 * pos.y, 32, 32 }; - - SDL_SetRenderDrawColor(renderer, 0xFF, has_goal ? 0xFF : 0x00, 0x00, 0xFF); - - if (SDL_RenderFillRect(renderer, &rect)) { - die("SDL_RenderFillRect(): %s", SDL_GetError()); - } - - return true; -} - -bool draw_on_box( - const sok_ctx_t * const ctx, - const sok_pos_t pos, - const bool has_goal, - void * const data -) { - SDL_Renderer *renderer = data; - SDL_Rect rect = { 32 * pos.x, 32 * pos.y, 32, 32 }; - - SDL_SetRenderDrawColor(renderer, 0x00, has_goal ? 0xFF : 0x00, 0xFF, 0xFF); - - if (SDL_RenderFillRect(renderer, &rect)) { - die("SDL_RenderFillRect(): %s", SDL_GetError()); + buf[ofs++] = SOK_DIR_TO_CHAR(ctx->moves[i].dir); } - return true; + SDL_Log("Solution (%zu moves): %s", ctx->num_moves - skip_moves, buf); } -static sok_ctx_walk_cbs_t -DRAW_CBS = { - .on_wall = draw_on_wall, - .on_goal = draw_on_goal, - .on_home = draw_on_home, - .on_box = draw_on_box, -}; -/* - * typedef struct { - * sok_ctx_walk_pos_cb_t on_size, - * on_wall; - * sok_ctx_walk_tile_cb_t on_home, - * on_box; - * sok_ctx_walk_goal_cb_t on_goal; - * sok_ctx_walk_move_cb_t on_move; - * } sok_ctx_walk_cbs_t; - * - * _Bool sok_ctx_walk( - * const sok_ctx_t * const, - * const sok_ctx_walk_cbs_t * const, - * void * const - * ); - */ - static void -draw( - SDL_Renderer * const renderer, - const sok_ctx_t * const ctx, - const size_t level_num, - const level_t * const level +solve_on_error( + const char * const err ) { - // clear - const SDL_Color c = get_bg(ctx); - SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a); - SDL_RenderClear(renderer); - - sok_ctx_walk(ctx, &DRAW_CBS, renderer); - - // flip - SDL_RenderPresent(renderer); + die("Error solving level: %s", err); } int main(int argc, char *argv[]) { - size_t warp_buf = 0; - size_t level_num = (argc > 1) ? atoi(argv[1]) : 0; + size_t level_num = (argc > 1) ? atoi(argv[1]) : 0, + zoom = 0; const level_t *level = levels_get_level(level_num); - // init context + // init warp buffer + warp_buf_t warp_buf; + warp_buf_clear(&warp_buf); + + // init sok context sok_ctx_t ctx; sok_ctx_init(&ctx, NULL); @@ -195,94 +71,111 @@ int main(int argc, char *argv[]) { die("SDL_CreateWindowAndRenderer(): %s", SDL_GetError()); } + // init draw context + draw_ctx_t draw_ctx = { + .level_num = &level_num, + .level = level, + .ctx = &ctx, + .renderer = renderer, + .zoom = &zoom, + }; + bool done = false; SDL_Event ev; while (!done) { while (SDL_PollEvent(&ev)) { - switch (ev.type) { - case SDL_QUIT: + const action_t action = get_action(&ev); + + switch (action.type) { + case ACTION_NONE: + // do nothing + break; + case ACTION_QUIT: done = true; break; - case SDL_KEYUP: + case ACTION_MOVE: + if (!sok_ctx_move(&ctx, (sok_dir_t) action.data)) { + warn("move %s failed", SOK_DIR_TO_STR((sok_dir_t) action.data)); + } + + break; + case ACTION_UNDO: + if (!sok_ctx_undo(&ctx)) { + warn("undo failed"); + } + + break; + case ACTION_NEXT: + if (sok_ctx_is_done(&ctx)) { + // advance level + level_num++; + level = levels_get_level(level_num); + draw_ctx.level = level; + + // load next level + if (!sok_ctx_set_level(&ctx, level->data)) { + die("Couldn't load level %zu", level_num); + } + } else { + warn("cannot advance to next level"); + } + + break; + case ACTION_RESET: + // reset level + if (!sok_ctx_set_level(&ctx, level->data)) { + die("Couldn't load level %zu", level_num); + } + + break; + case ACTION_WARP: + if (warp_buf_get(&warp_buf, &level_num)) { + level = levels_get_level(level_num); + draw_ctx.level = level; + + // load level + if (!sok_ctx_set_level(&ctx, level->data)) { + die("Couldn't load level %zu", level_num); + } + + // clear warp buf + warp_buf_clear(&warp_buf); + } + + break; + case ACTION_WARP_BUF_PUSH: + warp_buf_push_num(&warp_buf, action.data); + + break; + case ACTION_WARP_BUF_POP: + warp_buf_pop_num(&warp_buf); + + break; + case ACTION_SOLVE: { - const action_t action = get_action(ev.key.keysym.sym); - - switch (action.type) { - case ACTION_QUIT: - done = true; - break; - case ACTION_MOVE: - if (!sok_ctx_move(&ctx, (sok_dir_t) action.data)) { - warn("move %s failed", SOK_DIR_TO_STR((sok_dir_t) action.data)); - } - - break; - case ACTION_UNDO: - if (!sok_ctx_undo(&ctx)) { - warn("undo failed"); - } - - break; - case ACTION_NEXT: - if (sok_ctx_is_done(&ctx)) { - // advance level - level_num++; - level = levels_get_level(level_num); - - // load next level - if (!sok_ctx_set_level(&ctx, level->data)) { - die("Couldn't load level %zu", level_num); - } - } else { - warn("cannot advance to next level"); - } - - break; - case ACTION_RESET: - // reset level - if (!sok_ctx_set_level(&ctx, level->data)) { - die("Couldn't load level %zu", level_num); - } - - break; - case ACTION_WARP: - level_num = warp_buf; - level = levels_get_level(level_num); - - // load level - if (!sok_ctx_set_level(&ctx, level->data)) { - die("Couldn't load level %zu", level_num); - } - - warp_buf = 0; - - break; - case ACTION_WARP_BUF_PUSH: - warp_buf = 10 * warp_buf + action.data; - - break; - case ACTION_WARP_BUF_POP: - warp_buf /= 10; - - break; - case ACTION_SOLVE: - { - // get current number of moves - const size_t old_num_moves = ctx.num_moves; - - if (sok_solve(&ctx, solve_on_error)) { - // found solution, print it - draw_moves(&ctx, old_num_moves); - } else { - warn("Couldn't solve level"); - } - } - default: - // ignore - break; + // get current number of moves + const size_t old_num_moves = ctx.num_moves; + + if (sok_solve(&ctx, solve_on_error)) { + // found solution, print it + draw_moves(&ctx, old_num_moves); + } else { + warn("Couldn't solve level"); } } + break; + case ACTION_ZOOM_IN: + if (zoom < 10) { + zoom++; + } + + break; + case ACTION_ZOOM_OUT: + if (zoom > 0) { + zoom--; + } + break; default: // ignore @@ -290,7 +183,7 @@ int main(int argc, char *argv[]) { } } - draw(renderer, &ctx, level_num, level); + draw(&draw_ctx); } // fini renderer, window diff --git a/src/sdl/util.h b/src/sdl/util.h new file mode 100644 index 0000000..8d7e130 --- /dev/null +++ b/src/sdl/util.h @@ -0,0 +1,13 @@ +#ifndef UTIL_H +#define UTIL_H + +#define warn(...) do { \ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__); \ +} while (0) + +#define die(...) do { \ + SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__); \ + exit(EXIT_FAILURE); \ +} while (0) + +#endif /* UTIL_H */ diff --git a/src/sdl/warp-buf.c b/src/sdl/warp-buf.c new file mode 100644 index 0000000..5168c7f --- /dev/null +++ b/src/sdl/warp-buf.c @@ -0,0 +1,52 @@ +#include +#include "warp-buf.h" + +void +warp_buf_clear( + warp_buf_t * const buf +) { + buf->len = 0; + buf->dst = 0; +} + +bool +warp_buf_has_num( + const warp_buf_t * const buf +) { + return buf->len > 0; +} + +void +warp_buf_push_num( + warp_buf_t * const buf, + const size_t num +) { + buf->dst = buf->dst * 10 + num; + buf->len++; +} + +void +warp_buf_pop_num( + warp_buf_t * const buf +) { + if (buf->len > 0) { + buf->len--; + buf->dst /= 10; + } +} + +bool +warp_buf_get( + const warp_buf_t * const buf, + size_t * const r +) { + if (!buf->len) { + return false; + } + + if (r) { + *r = buf->dst; + } + + return true; +} diff --git a/src/sdl/warp-buf.h b/src/sdl/warp-buf.h new file mode 100644 index 0000000..5e00e5d --- /dev/null +++ b/src/sdl/warp-buf.h @@ -0,0 +1,17 @@ +#ifndef WARP_BUF_H +#define WARP_BUF_H + +#include // size_t + +typedef struct { + size_t len, + dst; +} warp_buf_t; + +void warp_buf_clear(warp_buf_t * const); +_Bool warp_buf_has_num(const warp_buf_t * const); +void warp_buf_push_num(warp_buf_t * const, const size_t); +void warp_buf_pop_num(warp_buf_t * const); +_Bool warp_buf_get(const warp_buf_t * const, size_t * const); + +#endif /* WARP_BUF_H */ -- cgit v1.2.3