SDL: examples/game/01-snake: Update game logic in AppIterate, don't use a timer.

From e254c99b38713303970526514f6ade21a2956faf Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Mon, 14 Oct 2024 23:16:02 -0400
Subject: [PATCH] examples/game/01-snake: Update game logic in AppIterate,
 don't use a timer.

Reference Issue #11210.
---
 examples/game/01-snake/snake.c | 37 +++++++++++++---------------------
 1 file changed, 14 insertions(+), 23 deletions(-)

diff --git a/examples/game/01-snake/snake.c b/examples/game/01-snake/snake.c
index 8257e416d2f0e..f1be83c4a34eb 100644
--- a/examples/game/01-snake/snake.c
+++ b/examples/game/01-snake/snake.c
@@ -57,8 +57,8 @@ typedef struct
 {
     SDL_Window *window;
     SDL_Renderer *renderer;
-    SDL_TimerID step_timer;
     SnakeContext snake_ctx;
+    Uint64 last_step;
 } AppState;
 
 SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y)
@@ -208,16 +208,6 @@ void snake_step(SnakeContext *ctx)
     }
 }
 
-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) {
@@ -250,14 +240,22 @@ static int handle_key_event_(SnakeContext *ctx, SDL_Scancode key_code)
 
 SDL_AppResult SDL_AppIterate(void *appstate)
 {
-    AppState *as;
-    SnakeContext *ctx;
+    AppState *as = (AppState *)appstate;
+    SnakeContext *ctx = &as->snake_ctx;
+    const Uint64 now = SDL_GetTicks();
     SDL_FRect r;
     unsigned i;
     unsigned j;
     int ct;
-    as = (AppState *)appstate;
-    ctx = &as->snake_ctx;
+
+    // run game logic if we're at or past the time to run it.
+    // if we're _really_ behind the time to run it, run it
+    // several times.
+    while ((now - as->last_step) >= STEP_RATE_IN_MILLISECONDS) {
+        snake_step(ctx);
+        as->last_step += STEP_RATE_IN_MILLISECONDS;
+    }
+
     r.w = r.h = SNAKE_BLOCK_SIZE_IN_PIXELS;
     SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, 255);
     SDL_RenderClear(as->renderer);
@@ -321,10 +319,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
 
     snake_initialize(&as->snake_ctx);
 
-    as->step_timer = SDL_AddTimer(STEP_RATE_IN_MILLISECONDS, sdl_timer_callback_, NULL);
-    if (as->step_timer == 0) {
-        return SDL_APP_FAILURE;
-    }
+    as->last_step = SDL_GetTicks();
 
     return SDL_APP_CONTINUE;
 }
@@ -335,9 +330,6 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
     switch (event->type) {
     case SDL_EVENT_QUIT:
         return SDL_APP_SUCCESS;
-    case SDL_EVENT_USER:
-        snake_step(ctx);
-        break;
     case SDL_EVENT_KEY_DOWN:
         return handle_key_event_(ctx, event->key.scancode);
     }
@@ -348,7 +340,6 @@ void SDL_AppQuit(void *appstate, SDL_AppResult result)
 {
     if (appstate != NULL) {
         AppState *as = (AppState *)appstate;
-        SDL_RemoveTimer(as->step_timer);
         SDL_DestroyRenderer(as->renderer);
         SDL_DestroyWindow(as->window);
         SDL_free(as);