SDL: examples: Smush game/01-snake into one source file, clean up some things.

From f6fc5e2881eb73b76487860e51a7ccc5eaef865c Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Fri, 30 Aug 2024 13:41:51 -0400
Subject: [PATCH] examples: Smush game/01-snake into one source file, clean up
 some things.

---
 examples/CMakeLists.txt        |   2 +-
 examples/game/01-snake/main.c  | 155 -----------------------
 examples/game/01-snake/snake.c | 222 +++++++++++++++++++++++++++++----
 examples/game/01-snake/snake.h |  54 --------
 4 files changed, 202 insertions(+), 231 deletions(-)
 delete mode 100644 examples/game/01-snake/main.c
 delete mode 100644 examples/game/01-snake/snake.h

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 21770603444f3..acbd1d1122314 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -188,7 +188,7 @@ add_sdl_example_executable(audio-simple-playback-callback SOURCES audio/02-simpl
 add_sdl_example_executable(audio-load-wav SOURCES audio/03-load-wav/load-wav.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.wav)
 add_sdl_example_executable(camera-read-and-draw SOURCES camera/01-read-and-draw/read-and-draw.c)
 add_sdl_example_executable(pen-drawing-lines SOURCES pen/01-drawing-lines/drawing-lines.c)
-add_sdl_example_executable(game-snake SOURCES game/01-snake/main.c game/01-snake/snake.c)
+add_sdl_example_executable(game-snake SOURCES game/01-snake/snake.c)
 
 
 if(PSP)
diff --git a/examples/game/01-snake/main.c b/examples/game/01-snake/main.c
deleted file mode 100644
index d958b71c54b88..0000000000000
--- a/examples/game/01-snake/main.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * This example code implements a Snake game that showcases some of the
- * functionalities of SDL, such as timer callbacks and event handling.
- *
- * This code is public domain. Feel free to use it for any purpose!
- */
-#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
-#include <SDL3/SDL.h>
-#include <SDL3/SDL_main.h>
-#include <stdlib.h> /* malloc(), free() */
-
-#include "snake.h"
-
-#define STEP_RATE_IN_MILLISECONDS  125
-#define SNAKE_BLOCK_SIZE_IN_PIXELS 24
-#define SDL_WINDOW_WIDTH           (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_WIDTH)
-#define SDL_WINDOW_HEIGHT          (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_HEIGHT)
-
-typedef struct
-{
-    SDL_Window *window;
-    SDL_Renderer *renderer;
-    SDL_TimerID step_timer;
-    SnakeContext snake_ctx;
-} AppState;
-
-static Uint32 sdl_timer_callback_(void *payload, SDL_TimerID timer_id, Uint32 interval)
-{
-    /* NOTE: snake_step is not called here directly for multithreaded concerns. */
-    SDL_Event event;
-    SDL_zero(event);
-    event.type = SDL_EVENT_USER;
-    SDL_PushEvent(&event);
-    return interval;
-}
-
-static int handle_key_event_(SnakeContext *ctx, SDL_Scancode key_code)
-{
-    switch (key_code) {
-    /* Quit. */
-    case SDL_SCANCODE_ESCAPE:
-    case SDL_SCANCODE_Q:
-        return SDL_APP_SUCCESS;
-    /* Restart the game as if the program was launched. */
-    case SDL_SCANCODE_R:
-        snake_initialize(ctx, SDL_rand);
-        break;
-    /* Decide new direction of the snake. */
-    case SDL_SCANCODE_RIGHT:
-        snake_redir(ctx, SNAKE_DIR_RIGHT);
-        break;
-    case SDL_SCANCODE_UP:
-        snake_redir(ctx, SNAKE_DIR_UP);
-        break;
-    case SDL_SCANCODE_LEFT:
-        snake_redir(ctx, SNAKE_DIR_LEFT);
-        break;
-    case SDL_SCANCODE_DOWN:
-        snake_redir(ctx, SNAKE_DIR_DOWN);
-        break;
-    default:
-        break;
-    }
-    return SDL_APP_CONTINUE;
-}
-
-static void set_rect_xy_(SDL_FRect *r, short x, short y)
-{
-    r->x = (float)(x * SNAKE_BLOCK_SIZE_IN_PIXELS);
-    r->y = (float)(y * SNAKE_BLOCK_SIZE_IN_PIXELS);
-}
-
-SDL_AppResult SDL_AppIterate(void *appstate)
-{
-    AppState *as;
-    SnakeContext *ctx;
-    SDL_FRect r;
-    unsigned i;
-    unsigned j;
-    int ct;
-    as = (AppState *)appstate;
-    ctx = &as->snake_ctx;
-    r.w = r.h = SNAKE_BLOCK_SIZE_IN_PIXELS;
-    SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, 255);
-    SDL_RenderClear(as->renderer);
-    for (i = 0; i < SNAKE_GAME_WIDTH; i++) {
-        for (j = 0; j < SNAKE_GAME_HEIGHT; j++) {
-            ct = snake_cell_at(ctx, i, j);
-            if (ct == SNAKE_CELL_NOTHING)
-                continue;
-            set_rect_xy_(&r, i, j);
-            if (ct == SNAKE_CELL_FOOD)
-                SDL_SetRenderDrawColor(as->renderer, 0, 0, 128, 255);
-            else /* body */
-                SDL_SetRenderDrawColor(as->renderer, 0, 128, 0, 255);
-            SDL_RenderFillRect(as->renderer, &r);
-        }
-    }
-    SDL_SetRenderDrawColor(as->renderer, 255, 255, 0, 255); /*head*/
-    set_rect_xy_(&r, ctx->head_xpos, ctx->head_ypos);
-    SDL_RenderFillRect(as->renderer, &r);
-    SDL_RenderPresent(as->renderer);
-    return SDL_APP_CONTINUE;
-}
-
-SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
-{
-    (void)argc;
-    (void)argv;
-    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {
-        return SDL_APP_FAILURE;
-    }
-    SDL_SetHint("SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR", "0");
-    AppState *as = malloc(sizeof(AppState));
-    *appstate = as;
-    as->step_timer = 0;
-    if (!SDL_CreateWindowAndRenderer("examples/game/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer)) {
-        return SDL_APP_FAILURE;
-    }
-    snake_initialize(&as->snake_ctx, SDL_rand);
-    as->step_timer = SDL_AddTimer(
-        STEP_RATE_IN_MILLISECONDS,
-        sdl_timer_callback_,
-        NULL);
-    if (as->step_timer == 0) {
-        return SDL_APP_FAILURE;
-    }
-    return SDL_APP_CONTINUE;
-}
-
-SDL_AppResult SDL_AppEvent(void *appstate, const SDL_Event *event)
-{
-    SnakeContext *ctx = &((AppState *)appstate)->snake_ctx;
-    switch (event->type) {
-    case SDL_EVENT_QUIT:
-        return SDL_APP_SUCCESS;
-    case SDL_EVENT_USER:
-        snake_step(ctx, SDL_rand);
-        break;
-    case SDL_EVENT_KEY_DOWN:
-        return handle_key_event_(ctx, event->key.scancode);
-    }
-    return SDL_APP_CONTINUE;
-}
-
-void SDL_AppQuit(void *appstate)
-{
-    if (appstate != NULL) {
-        AppState *as = (AppState *)appstate;
-        SDL_RemoveTimer(as->step_timer);
-        SDL_DestroyRenderer(as->renderer);
-        SDL_DestroyWindow(as->window);
-        free(as);
-    }
-}
diff --git a/examples/game/01-snake/snake.c b/examples/game/01-snake/snake.c
index 294edff1699c7..b27b57866b4e6 100644
--- a/examples/game/01-snake/snake.c
+++ b/examples/game/01-snake/snake.c
@@ -4,24 +4,89 @@
  *
  * This code is public domain. Feel free to use it for any purpose!
  */
-#include "snake.h"
 
-#include <limits.h> /* CHAR_BIT, CHAR_MAX */
-#include <string.h> /* memcpy() */
+#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
+#include <SDL3/SDL.h>
+#include <SDL3/SDL_main.h>
+
+#define STEP_RATE_IN_MILLISECONDS  125
+#define SNAKE_BLOCK_SIZE_IN_PIXELS 24
+#define SDL_WINDOW_WIDTH           (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_WIDTH)
+#define SDL_WINDOW_HEIGHT          (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_HEIGHT)
+
+#define SNAKE_GAME_WIDTH  24U
+#define SNAKE_GAME_HEIGHT 18U
+#define SNAKE_MATRIX_SIZE (SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT)
 
 #define THREE_BITS  0x7U /* ~CHAR_MAX >> (CHAR_BIT - SNAKE_CELL_MAX_BITS) */
 #define SHIFT(x, y) (((x) + ((y) * SNAKE_GAME_WIDTH)) * SNAKE_CELL_MAX_BITS)
 
+typedef enum
+{
+    SNAKE_CELL_NOTHING = 0U,
+    SNAKE_CELL_SRIGHT = 1U,
+    SNAKE_CELL_SUP = 2U,
+    SNAKE_CELL_SLEFT = 3U,
+    SNAKE_CELL_SDOWN = 4U,
+    SNAKE_CELL_FOOD = 5U
+} SnakeCell;
+
+#define SNAKE_CELL_MAX_BITS 3U /* floor(log2(SNAKE_CELL_FOOD)) + 1 */
+
+typedef enum
+{
+    SNAKE_DIR_RIGHT,
+    SNAKE_DIR_UP,
+    SNAKE_DIR_LEFT,
+    SNAKE_DIR_DOWN
+} SnakeDirection;
+
+typedef struct
+{
+    unsigned char cells[(SNAKE_MATRIX_SIZE * SNAKE_CELL_MAX_BITS) / 8U];
+    char head_xpos;
+    char head_ypos;
+    char tail_xpos;
+    char tail_ypos;
+    char next_dir;
+    char inhibit_tail_step;
+    unsigned occupied_cells;
+} SnakeContext;
+
+typedef Sint32 (SDLCALL *RandFunc)(Sint32 n);
+
+typedef struct
+{
+    SDL_Window *window;
+    SDL_Renderer *renderer;
+    SDL_TimerID step_timer;
+    SnakeContext snake_ctx;
+} AppState;
+
+SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y)
+{
+    const int shift = SHIFT(x, y);
+    unsigned short range;
+    SDL_memcpy(&range, ctx->cells + (shift / 8), sizeof(range));
+    return (SnakeCell)((range >> (shift % 8)) & THREE_BITS);
+}
+
+static void set_rect_xy_(SDL_FRect *r, short x, short y)
+{
+    r->x = (float)(x * SNAKE_BLOCK_SIZE_IN_PIXELS);
+    r->y = (float)(y * SNAKE_BLOCK_SIZE_IN_PIXELS);
+}
+
 static void put_cell_at_(SnakeContext *ctx, char x, char y, SnakeCell ct)
 {
     const int shift = SHIFT(x, y);
-    const int adjust = shift % CHAR_BIT;
-    unsigned char *const pos = ctx->cells + (shift / CHAR_BIT);
+    const int adjust = shift % 8;
+    unsigned char *const pos = ctx->cells + (shift / 8);
     unsigned short range;
-    memcpy(&range, pos, sizeof(range));
+    SDL_memcpy(&range, pos, sizeof(range));
     range &= ~(THREE_BITS << adjust); /* clear bits */
     range |= (ct & THREE_BITS) << adjust;
-    memcpy(pos, &range, sizeof(range));
+    SDL_memcpy(pos, &range, sizeof(range));
 }
 
 static int are_cells_full_(SnakeContext *ctx)
@@ -31,11 +96,9 @@ static int are_cells_full_(SnakeContext *ctx)
 
 static void new_food_pos_(SnakeContext *ctx, RandFunc rand)
 {
-    char x;
-    char y;
-    for (;;) {
-        x = (char) rand(SNAKE_GAME_WIDTH);
-        y = (char) rand(SNAKE_GAME_HEIGHT);
+    while (SDL_TRUE) {
+        const char x = (char) rand(SNAKE_GAME_WIDTH);
+        const char y = (char) rand(SNAKE_GAME_HEIGHT);
         if (snake_cell_at(ctx, x, y) == SNAKE_CELL_NOTHING) {
             put_cell_at_(ctx, x, y, SNAKE_CELL_FOOD);
             break;
@@ -46,7 +109,7 @@ static void new_food_pos_(SnakeContext *ctx, RandFunc rand)
 void snake_initialize(SnakeContext *ctx, RandFunc rand)
 {
     int i;
-    memset(ctx, 0, sizeof ctx->cells);
+    SDL_zeroa(ctx->cells);
     ctx->head_xpos = ctx->tail_xpos = SNAKE_GAME_WIDTH / 2;
     ctx->head_ypos = ctx->tail_ypos = SNAKE_GAME_HEIGHT / 2;
     ctx->next_dir = SNAKE_DIR_RIGHT;
@@ -65,16 +128,18 @@ void snake_redir(SnakeContext *ctx, SnakeDirection dir)
     if ((dir == SNAKE_DIR_RIGHT && ct != SNAKE_CELL_SLEFT) ||
         (dir == SNAKE_DIR_UP && ct != SNAKE_CELL_SDOWN) ||
         (dir == SNAKE_DIR_LEFT && ct != SNAKE_CELL_SRIGHT) ||
-        (dir == SNAKE_DIR_DOWN && ct != SNAKE_CELL_SUP))
+        (dir == SNAKE_DIR_DOWN && ct != SNAKE_CELL_SUP)) {
         ctx->next_dir = dir;
+    }
 }
 
 static void wrap_around_(char *val, char max)
 {
-    if (*val < 0)
+    if (*val < 0) {
         *val = max - 1;
-    if (*val > max - 1)
+    } else if (*val > max - 1) {
         *val = 0;
+    }
 }
 
 void snake_step(SnakeContext *ctx, RandFunc rand)
@@ -145,10 +210,125 @@ void snake_step(SnakeContext *ctx, RandFunc rand)
     }
 }
 
-SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y)
+static Uint32 sdl_timer_callback_(void *payload, SDL_TimerID timer_id, Uint32 interval)
 {
-    const int shift = SHIFT(x, y);
-    unsigned short range;
-    memcpy(&range, ctx->cells + (shift / CHAR_BIT), sizeof(range));
-    return (SnakeCell)((range >> (shift % CHAR_BIT)) & THREE_BITS);
+    /* NOTE: snake_step is not called here directly for multithreaded concerns. */
+    SDL_Event event;
+    SDL_zero(event);
+    event.type = SDL_EVENT_USER;
+    SDL_PushEvent(&event);
+    return interval;
+}
+
+static int handle_key_event_(SnakeContext *ctx, SDL_Scancode key_code)
+{
+    switch (key_code) {
+    /* Quit. */
+    case SDL_SCANCODE_ESCAPE:
+    case SDL_SCANCODE_Q:
+        return SDL_APP_SUCCESS;
+    /* Restart the game as if the program was launched. */
+    case SDL_SCANCODE_R:
+        snake_initialize(ctx, SDL_rand);
+        break;
+    /* Decide new direction of the snake. */
+    case SDL_SCANCODE_RIGHT:
+        snake_redir(ctx, SNAKE_DIR_RIGHT);
+        break;
+    case SDL_SCANCODE_UP:
+        snake_redir(ctx, SNAKE_DIR_UP);
+        break;
+    case SDL_SCANCODE_LEFT:
+        snake_redir(ctx, SNAKE_DIR_LEFT);
+        break;
+    case SDL_SCANCODE_DOWN:
+        snake_redir(ctx, SNAKE_DIR_DOWN);
+        break;
+    default:
+        break;
+    }
+    return SDL_APP_CONTINUE;
+}
+
+SDL_AppResult SDL_AppIterate(void *appstate)
+{
+    AppState *as;
+    SnakeContext *ctx;
+    SDL_FRect r;
+    unsigned i;
+    unsigned j;
+    int ct;
+    as = (AppState *)appstate;
+    ctx = &as->snake_ctx;
+    r.w = r.h = SNAKE_BLOCK_SIZE_IN_PIXELS;
+    SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, 255);
+    SDL_RenderClear(as->renderer);
+    for (i = 0; i < SNAKE_GAME_WIDTH; i++) {
+        for (j = 0; j < SNAKE_GAME_HEIGHT; j++) {
+            ct = snake_cell_at(ctx, i, j);
+            if (ct == SNAKE_CELL_NOTHING)
+                continue;
+            set_rect_xy_(&r, i, j);
+            if (ct == SNAKE_CELL_FOOD)
+                SDL_SetRenderDrawColor(as->renderer, 0, 0, 128, 255);
+            else /* body */
+                SDL_SetRenderDrawColor(as->renderer, 0, 128, 0, 255);
+            SDL_RenderFillRect(as->renderer, &r);
+        }
+    }
+    SDL_SetRenderDrawColor(as->renderer, 255, 255, 0, 255); /*head*/
+    set_rect_xy_(&r, ctx->head_xpos, ctx->head_ypos);
+    SDL_RenderFillRect(as->renderer, &r);
+    SDL_RenderPresent(as->renderer);
+    return SDL_APP_CONTINUE;
+}
+
+SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
+{
+    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {
+        return SDL_APP_FAILURE;
+    }
+
+    AppState *as = SDL_calloc(1, sizeof(AppState));
+
+    *appstate = as;
+
+    if (!SDL_CreateWindowAndRenderer("examples/game/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer)) {
+        return SDL_APP_FAILURE;
+    }
+
+    snake_initialize(&as->snake_ctx, SDL_rand);
+
+    as->step_timer = SDL_AddTimer(STEP_RATE_IN_MILLISECONDS, sdl_timer_callback_, NULL);
+    if (as->step_timer == 0) {
+        return SDL_APP_FAILURE;
+    }
+
+    return SDL_APP_CONTINUE;
+}
+
+SDL_AppResult SDL_AppEvent(void *appstate, const SDL_Event *event)
+{
+    SnakeContext *ctx = &((AppState *)appstate)->snake_ctx;
+    switch (event->type) {
+    case SDL_EVENT_QUIT:
+        return SDL_APP_SUCCESS;
+    case SDL_EVENT_USER:
+        snake_step(ctx, SDL_rand);
+        break;
+    case SDL_EVENT_KEY_DOWN:
+        return handle_key_event_(ctx, event->key.scancode);
+    }
+    return SDL_APP_CONTINUE;
+}
+
+void SDL_AppQuit(void *appstate)
+{
+    if (appstate != NULL) {
+        AppState *as = (AppState *)appstate;
+        SDL_RemoveTimer(as->step_timer);
+        SDL_DestroyRenderer(as->renderer);
+        SDL_DestroyWindow(as->window);
+        SDL_free(as);
+    }
 }
diff --git a/examples/game/01-snake/snake.h b/examples/game/01-snake/snake.h
deleted file mode 100644
index dd2d507c850d5..0000000000000
--- a/examples/game/01-snake/snake.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Interface definition of the Snake game.
- *
- * This code is public domain. Feel free to use it for any purpose!
- */
-#ifndef SNAKE_H
-#define SNAKE_H
-
-#include <SDL3/SDL.h>
-
-#define SNAKE_GAME_WIDTH  24U
-#define SNAKE_GAME_HEIGHT 18U
-#define SNAKE_MATRIX_SIZE (SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT)
-
-typedef enum
-{
-    SNAKE_CELL_NOTHING = 0U,
-    SNAKE_CELL_SRIGHT = 1U,
-    SNAKE_CELL_SUP = 2U,
-    SNAKE_CELL_SLEFT = 3U,
-    SNAKE_CELL_SDOWN = 4U,
-    SNAKE_CELL_FOOD = 5U
-} SnakeCell;
-
-#define SNAKE_CELL_MAX_BITS 3U /* floor(log2(SNAKE_CELL_FOOD)) + 1 */
-
-typedef enum
-{
-    SNAKE_DIR_RIGHT,
-    SNAKE_DIR_UP,
-    SNAKE_DIR_LEFT,
-    SNAKE_DIR_DOWN
-} SnakeDirection;
-
-typedef struct
-{
-    unsigned char cells[(SNAKE_MATRIX_SIZE * SNAKE_CELL_MAX_BITS) / 8U];
-    char head_xpos;
-    char head_ypos;
-    char tail_xpos;
-    char tail_ypos;
-    char next_dir;
-    char inhibit_tail_step;
-    unsigned occupied_cells;
-} SnakeContext;
-
-typedef Sint32 (SDLCALL *RandFunc)(Sint32 n);
-
-void snake_initialize(SnakeContext *ctx, RandFunc rand);
-void snake_redir(SnakeContext *ctx, SnakeDirection dir);
-void snake_step(SnakeContext *ctx, RandFunc rand);
-SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y);
-
-#endif /* SNAKE_H */