shithub: m8c

Download patch

ref: 328b1c845f21f5b47ba8156399245f2c3b1a4159
parent: ea7727d3b68920cfa96cae9ada2961a5fe1c0eda
author: laamaa <jonne.kokkonen@gmail.com>
date: Tue Apr 15 06:58:10 EDT 2025

callback based functionality: destroy screensaver resources on exit, avoid unnecessary draw cycles with screensaver

--- a/src/main.c
+++ b/src/main.c
@@ -6,15 +6,14 @@
 // #define DEBUG_MSG
 
 #include <SDL3/SDL.h>
+#include <SDL3/SDL_init.h>
 #define SDL_MAIN_USE_CALLBACKS
 #include <SDL3/SDL_main.h>
-#include <signal.h>
 #include <stdlib.h>
 
 #include "SDL2_inprint.h"
 #include "backends/audio.h"
 #include "backends/m8.h"
-#include "command.h"
 #include "config.h"
 #include "gamecontrollers.h"
 #include "input.h"
@@ -28,34 +27,31 @@
   unsigned char app_suspended;
 };
 
-static void do_wait_for_device(const char *preferred_device, unsigned char *m8_connected,
-                               config_params_s *conf, enum app_state *app_state) {
+static void do_wait_for_device(struct app_context *ctx) {
   static Uint64 ticks_poll_device = 0;
+  static int screensaver_initialized = 0;
 
-  if (*m8_connected == 0) {
-    screensaver_init();
-  }
-
-#if TARGET_OS_IOS
   // Handle app suspension
-  if (app_suspended) {
-    SDL_Delay(conf->idle_ms);
-    continue;
+  if (ctx->app_suspended) {
+    SDL_Delay(ctx->conf.idle_ms);
+    return;
   }
-#endif // TARGET_OS_IOS
 
+  if (!screensaver_initialized) {
+    screensaver_initialized = screensaver_init();
+  }
   screensaver_draw();
   render_screen();
 
   // Poll for M8 device every second
-  if (*m8_connected == 0 && SDL_GetTicks() - ticks_poll_device > 1000) {
+  if (ctx->device_connected == 0 && SDL_GetTicks() - ticks_poll_device > 1000) {
     ticks_poll_device = SDL_GetTicks();
-    if (m8_initialize(0, preferred_device) == 1) {
+    if (m8_initialize(0, ctx->preferred_device)) {
 
-      if (conf->audio_enabled == 1) {
-        if (audio_initialize(conf->audio_device_name, conf->audio_buffer_size) == 0) {
+      if (ctx->conf.audio_enabled) {
+        if (!audio_initialize(ctx->conf.audio_device_name, ctx->conf.audio_buffer_size)) {
           SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Cannot initialize audio");
-          conf->audio_enabled = 0;
+          ctx->conf.audio_enabled = 0;
         }
       }
 
@@ -62,13 +58,15 @@
       const int m8_enabled = m8_enable_and_reset_display();
       // Device was found; enable display and proceed to the main loop
       if (m8_enabled == 1) {
-        *app_state = RUN;
-        *m8_connected = 1;
+        ctx->app_state = RUN;
+        ctx->device_connected = 1;
         screensaver_destroy();
+        screensaver_initialized = 0;
       } else {
         SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected.");
-        *app_state = QUIT;
+        ctx->app_state = QUIT;
         screensaver_destroy();
+        screensaver_initialized = 0;
 #ifdef USE_RTMIDI
         show_error_message(
             "Cannot initialize M8 remote display. Make sure you're running "
@@ -114,61 +112,6 @@
   return device_connected;
 }
 
-#if TARGET_OS_IOS
-// IOS events handler
-static bool SDLCALL handle_app_events(void *userdata, SDL_Event *event) {
-  const config_params_s *conf = (config_params_s *)userdata;
-  switch (event->type) {
-  case SDL_EVENT_TERMINATING:
-    /* Terminate the app.
-       Shut everything down before returning from this function.
-    */
-    cleanup_resources(device_connected, conf);
-    return 0;
-  case SDL_EVENT_DID_ENTER_BACKGROUND:
-    /* This will get called if the user accepted whatever sent your app to the background.
-       If the user got a phone call and canceled it, you'll instead get an
-       SDL_EVENT_DID_ENTER_FOREGROUND event and restart your loops. When you get this, you have 5
-       seconds to save all your state or the app will be terminated. Your app is NOT active at this
-       point.
-    */
-    SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Received SDL_EVENT_DID_ENTER_BACKGROUND");
-    app_suspended = 1;
-    if (device_connected)
-      m8_pause_processing();
-    return 0;
-  case SDL_EVENT_LOW_MEMORY:
-    /* You will get this when your app is paused and iOS wants more memory.
-       Release as much memory as possible.
-    */
-    return 0;
-  case SDL_EVENT_WILL_ENTER_BACKGROUND:
-    /* Prepare your app to go into the background.  Stop loops, etc.
-       This gets called when the user hits the home button, or gets a call.
-    */
-    return 0;
-  case SDL_EVENT_WILL_ENTER_FOREGROUND:
-    /* This call happens when your app is coming back to the foreground.
-       Restore all your state here.
-    */
-    SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Received SDL_EVENT_WILL_ENTER_FOREGROUND");
-    app_suspended = 0;
-    if (device_connected)
-      m8_resume_processing();
-    return 0;
-  case SDL_EVENT_DID_ENTER_FOREGROUND:
-    /* Restart your loops here.
-       Your app is interactive and getting CPU again.
-    */
-    SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Received SDL_EVENT_DID_ENTER_FOREGROUND");
-    return 0;
-  default:
-    /* No special processing, add it to the event queue */
-    return 1;
-  }
-}
-#endif // TARGET_OS_IOS
-
 SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
   char *config_filename = NULL;
 
@@ -188,11 +131,6 @@
     return SDL_APP_FAILURE;
   }
 
-#if TARGET_OS_IOS
-  // IOS events handler
-  SDL_SetEventFilter(handle_app_events, &conf);
-#endif
-
 #ifndef NDEBUG
   SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
   SDL_LogDebug(SDL_LOG_CATEGORY_TEST, "Running a Debug build");
@@ -201,11 +139,6 @@
   gamecontrollers_initialize();
   *appstate = ctx;
 
-  return SDL_APP_CONTINUE;
-}
-
-SDL_AppResult SDL_AppIterate(void *appstate) {
-  struct app_context *ctx = appstate;
   if (ctx->app_state == WAIT_FOR_DEVICE) {
     if (!ctx->device_connected) {
       ctx->device_connected =
@@ -222,10 +155,33 @@
       ctx->device_connected = 0;
       ctx->app_state = ctx->conf.wait_for_device ? WAIT_FOR_DEVICE : QUIT;
     }
+  }  
+
+  return SDL_APP_CONTINUE;
+}
+
+SDL_AppResult SDL_AppIterate(void *appstate) {
+  struct app_context *ctx = appstate;
+  SDL_AppResult app_result = SDL_APP_CONTINUE;
+
+  switch (ctx->app_state) {
+    case WAIT_FOR_DEVICE: {
+      if (ctx->conf.wait_for_device) {
+        do_wait_for_device(ctx);
+      }
+      break;
+    }
+    
+    case RUN:
+    break;
+    case QUIT:
+    break;
   }
 
+  return app_result;
+
   if (ctx->conf.wait_for_device && ctx->app_state == WAIT_FOR_DEVICE) {
-    do_wait_for_device(ctx->preferred_device, &ctx->device_connected, &ctx->conf, &ctx->app_state);
+    do_wait_for_device(ctx);
   } else if (!ctx->device_connected && ctx->app_state != WAIT_FOR_DEVICE) {
     return SDL_APP_FAILURE;
   }
@@ -247,7 +203,10 @@
 
 void SDL_AppQuit(void *appstate, SDL_AppResult result) {
   struct app_context *app = appstate;
-  if (appstate) {
+  if (app) {
+    if (app->app_state == WAIT_FOR_DEVICE) {
+      screensaver_destroy();
+    }
     if (app->conf.audio_enabled) {
       audio_close();
     }
@@ -265,10 +224,53 @@
 }
 
 SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
-  struct app_context *app = appstate;
+  struct app_context *ctx = appstate;
   SDL_AppResult ret_val = SDL_APP_CONTINUE;
-  if (event->type == SDL_EVENT_QUIT) {
-    ret_val = SDL_APP_SUCCESS;
-  }
+
+  switch (event->type) {
+    case SDL_EVENT_QUIT:
+    case SDL_EVENT_TERMINATING:
+      ret_val = SDL_APP_SUCCESS;
+      break;
+    case SDL_EVENT_DID_ENTER_BACKGROUND:
+      /* This will get called if the user accepted whatever sent your app to the background.
+         If the user got a phone call and canceled it, you'll instead get an
+         SDL_EVENT_DID_ENTER_FOREGROUND event and restart your loops. When you get this, you have 5
+         seconds to save all your state or the app will be terminated. Your app is NOT active at this
+         point.
+      */
+      SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Received SDL_EVENT_DID_ENTER_BACKGROUND");
+      ctx->app_suspended = 1;
+      if (ctx->device_connected)
+        m8_pause_processing();
+      break;
+    case SDL_EVENT_LOW_MEMORY:
+      /* You will get this when your app is paused and iOS wants more memory.
+         Release as much memory as possible.
+      */
+      break;
+    case SDL_EVENT_WILL_ENTER_BACKGROUND:
+      /* Prepare your app to go into the background.  Stop loops, etc.
+         This gets called when the user hits the home button, or gets a call.
+      */
+      break;
+    case SDL_EVENT_WILL_ENTER_FOREGROUND:
+      /* This call happens when your app is coming back to the foreground.
+         Restore all your state here.
+      */
+      SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Received SDL_EVENT_WILL_ENTER_FOREGROUND");
+      break;
+    case SDL_EVENT_DID_ENTER_FOREGROUND:
+      /* Restart your loops here.
+         Your app is interactive and getting CPU again.
+      */
+      SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Received SDL_EVENT_DID_ENTER_FOREGROUND");
+      ctx->app_suspended = 0;
+      if (ctx->device_connected) {
+        m8_resume_processing();
+      }
+    default:
+      break;
+    }
   return ret_val;
 }
\ No newline at end of file
--- a/src/render.c
+++ b/src/render.c
@@ -37,6 +37,8 @@
 static int texture_width = 320;
 static int texture_height = 240;
 
+static int screensaver_initialized = 0;
+
 struct inline_font *fonts[5] = {&font_v1_small, &font_v1_large, &font_v2_small, &font_v2_large,
                                 &font_v2_huge};
 
@@ -354,17 +356,21 @@
   }
 }
 
-void screensaver_init() {
+int screensaver_init() {
+  if (screensaver_initialized) {
+    return 1;
+  }
   renderer_set_font_mode(1);
   global_background_color.r = 0, global_background_color.g = 0, global_background_color.b = 0;
   fx_cube_init(rend, (SDL_Color){255, 255, 255, 255}, texture_width, texture_height,
                fonts[font_mode]->glyph_x);
   SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Screensaver initialized");
+  screensaver_initialized = 1;
+  return 1;
 }
 
 void screensaver_draw() {
-  fx_cube_update();
-  dirty = 1;
+  dirty = fx_cube_update();
 }
 
 void screensaver_destroy() {
@@ -371,6 +377,7 @@
   fx_cube_destroy();
   renderer_set_font_mode(0);
   SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Screensaver destroyed");
+  screensaver_initialized = 0;
 }
 
 void renderer_fix_texture_scaling_after_window_resize(void) {
--- a/src/render.h
+++ b/src/render.h
@@ -26,7 +26,7 @@
 
 void show_error_message(const char *message);
 
-void screensaver_init();
+int screensaver_init();
 void screensaver_draw();
 void screensaver_destroy();
 
--