ref: d47bf0e01b84f6c16165dbcedeef9286ee0d3b3a
parent: f70b0dfb8b5d4d2d4e1b2f65f13d64b4323d0ded
author: Jonne Kokkonen <jonne.kokkonen@gmail.com>
date: Mon Apr 14 18:44:49 EDT 2025
experiment with sdl callback based structure
--- a/src/main.c
+++ b/src/main.c
@@ -6,6 +6,7 @@
// #define DEBUG_MSG
#include <SDL3/SDL.h>
+#define SDL_MAIN_USE_CALLBACKS
#include <SDL3/SDL_main.h>
#include <signal.h>
#include <stdlib.h>
@@ -19,87 +20,62 @@
#include "input.h"
#include "render.h"
-enum app_state app_state = WAIT_FOR_DEVICE;
-unsigned char device_connected = 0;
-unsigned char app_suspended = 0;
+struct app_context {+ config_params_s conf;
+ enum app_state app_state;
+ char *preferred_device;
+ unsigned char device_connected;
+ unsigned char app_suspended;
+};
-// Handle CTRL+C / SIGINT, SIGKILL etc.
-static void signal_handler(int unused) {- (void)unused;
- app_state = QUIT;
-}
-
-static void initialize_signals(void) {- signal(SIGINT, signal_handler);
- signal(SIGTERM, signal_handler);
-#ifdef SIGQUIT // Not available on Windows.
- signal(SIGQUIT, signal_handler);
-#endif
-}
-
static void do_wait_for_device(const char *preferred_device, unsigned char *m8_connected,
- config_params_s *conf) {+ config_params_s *conf, enum app_state *app_state) {static Uint64 ticks_poll_device = 0;
- static Uint64 ticks_update_screen = 0;
if (*m8_connected == 0) {screensaver_init();
}
- while (app_state == WAIT_FOR_DEVICE) {-
- // get current input
- const input_msg_s input = input_get_msg(&*conf);
- if (input.type == special && input.value == msg_quit) {- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Input message QUIT.");
- app_state = QUIT;
- }
-
#if TARGET_OS_IOS
- // Handle app suspension
- if (app_suspended) {- SDL_Delay(conf->idle_ms);
- continue;
- }
+ // Handle app suspension
+ if (app_suspended) {+ SDL_Delay(conf->idle_ms);
+ continue;
+ }
#endif // TARGET_OS_IOS
- if (SDL_GetTicks() - ticks_update_screen > 16) {- ticks_update_screen = SDL_GetTicks();
- screensaver_draw();
- render_screen();
- }
+ screensaver_draw();
+ render_screen();
- // Poll for M8 device every second
- if (*m8_connected == 0 && SDL_GetTicks() - ticks_poll_device > 1000) {- ticks_poll_device = SDL_GetTicks();
- if (app_state == WAIT_FOR_DEVICE && m8_initialize(0, preferred_device) == 1) {+ // Poll for M8 device every second
+ if (*m8_connected == 0 && SDL_GetTicks() - ticks_poll_device > 1000) {+ ticks_poll_device = SDL_GetTicks();
+ if (m8_initialize(0, preferred_device) == 1) {- if (conf->audio_enabled == 1) {- if (audio_initialize(conf->audio_device_name, conf->audio_buffer_size) == 0) {- SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Cannot initialize audio");
- conf->audio_enabled = 0;
- }
+ if (conf->audio_enabled == 1) {+ if (audio_initialize(conf->audio_device_name, conf->audio_buffer_size) == 0) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Cannot initialize audio");
+ conf->audio_enabled = 0;
}
+ }
- 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;
- screensaver_destroy();
- } else {- SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected.");
- app_state = QUIT;
- screensaver_destroy();
+ 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;
+ screensaver_destroy();
+ } else {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected.");
+ *app_state = QUIT;
+ screensaver_destroy();
#ifdef USE_RTMIDI
- show_error_message(
- "Cannot initialize M8 remote display. Make sure you're running "
- "firmware 6.0.0 or newer. Please close and restart the application to try again.");
+ show_error_message(
+ "Cannot initialize M8 remote display. Make sure you're running "
+ "firmware 6.0.0 or newer. Please close and restart the application to try again.");
#endif
- }
}
}
- SDL_Delay(conf->idle_ms);
}
}
@@ -111,11 +87,11 @@
}
if (SDL_strcmp(argv[i], "--dev") == 0 && i + 1 < argc) {*preferred_device = argv[i + 1];
- SDL_Log("Using preferred device: %s.\n", *preferred_device);+ SDL_Log("Using preferred device: %s", *preferred_device);i++;
} else if (SDL_strcmp(argv[i], "--config") == 0 && i + 1 < argc) {*config_filename = argv[i + 1];
- SDL_Log("Using config file: %s.\n", *config_filename);+ SDL_Log("Using config file: %s", *config_filename);i++;
}
}
@@ -128,20 +104,6 @@
return conf;
}
-static void cleanup_resources(const unsigned char device_connected, const config_params_s *conf) {- if (conf->audio_enabled) {- audio_close();
- }
- gamecontrollers_close();
- renderer_close();
- inline_font_close();
- if (device_connected) {- m8_close();
- }
- SDL_Quit();
- SDL_Log("Shutting down.");-}
-
static unsigned char handle_device_initialization(const unsigned char wait_for_device,
const char *preferred_device) {const unsigned char device_connected = m8_initialize(1, preferred_device);
@@ -207,62 +169,23 @@
}
#endif // TARGET_OS_IOS
-static void main_loop(config_params_s *conf, const char *preferred_device) {-
- do {- if (!device_connected) {- device_connected = handle_device_initialization(conf->wait_for_device, preferred_device);
- }
- if (device_connected && m8_enable_and_reset_display()) {- if (conf->audio_enabled) {- audio_initialize(conf->audio_device_name, conf->audio_buffer_size);
- m8_reset_display(); // Avoid display glitches.
- }
- app_state = RUN;
- } else {- SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected.");
- device_connected = 0;
- app_state = conf->wait_for_device ? WAIT_FOR_DEVICE : QUIT;
- }
-
- if (conf->wait_for_device && app_state == WAIT_FOR_DEVICE) {- do_wait_for_device(preferred_device, &device_connected, conf);
- } else if (!device_connected && app_state != WAIT_FOR_DEVICE) {- cleanup_resources(0, conf);
- exit(EXIT_FAILURE);
- }
-
- // Handle input, process data, and render screen while running.
- while (app_state == RUN) {- input_process(conf, &app_state);
- const int result = m8_process_data(conf);
- if (result == DEVICE_DISCONNECTED) {- device_connected = 0;
- app_state = WAIT_FOR_DEVICE;
- audio_close();
- } else if (result == DEVICE_FATAL_ERROR) {- app_state = QUIT;
- }
- render_screen();
- SDL_Delay(conf->idle_ms);
- }
- } while (app_state > QUIT);
-
- cleanup_resources(device_connected, conf);
-}
-
-int main(const int argc, char *argv[]) {- char *preferred_device = NULL;
+SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {char *config_filename = NULL;
- config_params_s conf = initialize_config(argc, argv, &preferred_device, &config_filename);
- initialize_signals();
+ struct app_context *ctx = SDL_calloc(1, sizeof(struct app_context));
+ if (ctx == NULL) {+ SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "SDL_calloc failed: %s", SDL_GetError());
+ return SDL_APP_FAILURE;
+ }
- device_connected = handle_device_initialization(conf.wait_for_device, preferred_device);
- if (!renderer_initialize(&conf)) {+ ctx->app_state = WAIT_FOR_DEVICE;
+
+ ctx->conf = initialize_config(argc, argv, &ctx->preferred_device, &config_filename);
+ ctx->device_connected =
+ handle_device_initialization(ctx->conf.wait_for_device, ctx->preferred_device);
+ if (!renderer_initialize(&ctx->conf)) {SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Failed to initialize renderer.");
- cleanup_resources(device_connected, &conf);
- return EXIT_FAILURE;
+ return SDL_APP_FAILURE;
}
#if TARGET_OS_IOS
@@ -270,13 +193,82 @@
SDL_SetEventFilter(handle_app_events, &conf);
#endif
- gamecontrollers_initialize();
-
#ifndef NDEBUG
SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
SDL_LogDebug(SDL_LOG_CATEGORY_TEST, "Running a Debug build");
#endif
- main_loop(&conf, preferred_device);
- return EXIT_SUCCESS;
+ 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 =
+ handle_device_initialization(ctx->conf.wait_for_device, ctx->preferred_device);
+ }
+ if (ctx->device_connected && m8_enable_and_reset_display()) {+ if (ctx->conf.audio_enabled) {+ audio_initialize(ctx->conf.audio_device_name, ctx->conf.audio_buffer_size);
+ m8_reset_display(); // Avoid display glitches.
+ }
+ ctx->app_state = RUN;
+ } else {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected.");
+ ctx->device_connected = 0;
+ ctx->app_state = ctx->conf.wait_for_device ? WAIT_FOR_DEVICE : QUIT;
+ }
+ }
+
+ 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);
+ } else if (!ctx->device_connected && ctx->app_state != WAIT_FOR_DEVICE) {+ return SDL_APP_FAILURE;
+ }
+
+ // Handle input, process data, and render screen while running.
+ if (ctx->app_state == RUN) {+ const int result = m8_process_data(&ctx->conf);
+ if (result == DEVICE_DISCONNECTED) {+ ctx->device_connected = 0;
+ ctx->app_state = WAIT_FOR_DEVICE;
+ audio_close();
+ } else if (result == DEVICE_FATAL_ERROR) {+ return SDL_APP_FAILURE;
+ }
+ render_screen();
+ }
+ return SDL_APP_CONTINUE;
+}
+
+void SDL_AppQuit(void *appstate, SDL_AppResult result) {+ struct app_context *app = appstate;
+ if (appstate) {+ if (app->conf.audio_enabled) {+ audio_close();
+ }
+ gamecontrollers_close();
+ renderer_close();
+ inline_font_close();
+ if (app->device_connected) {+ m8_close();
+ }
+ SDL_Quit();
+ SDL_Log("Shutting down.");+
+ SDL_free(app);
+ }
+}
+
+SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {+ struct app_context *app = appstate;
+ SDL_AppResult ret_val = SDL_APP_CONTINUE;
+ if (event->type == SDL_EVENT_QUIT) {+ ret_val = SDL_APP_SUCCESS;
+ }
+ return ret_val;
+}
\ No newline at end of file
--
⑨