shithub: m8c

Download patch

ref: eac70ef06f57c7e535247c32fd43880c1ac88b29
parent: 7ee19d3d5baa4da0fb7a06f82b5fb2a3f233fd4f
parent: 1c32be8a815f4fd4c096ff06706cbdf7db7ff664
author: Jonne Kokkonen <jonne.kokkonen@gmail.com>
date: Wed Apr 2 05:06:11 EDT 2025

Merge pull request #189 from laamaa/feature/ios-event-handers

Feature/ios event handers

--- a/src/backends/m8.h
+++ b/src/backends/m8.h
@@ -18,6 +18,8 @@
 int m8_send_msg_controller(unsigned char input);
 int m8_send_msg_keyjazz(unsigned char note, unsigned char velocity);
 int m8_process_data(const config_params_s *conf);
+int m8_pause_processing(void);
+int m8_resume_processing(void);
 int m8_close(void);
 
 #endif
\ No newline at end of file
--- a/src/backends/m8_libserialport.c
+++ b/src/backends/m8_libserialport.c
@@ -362,4 +362,8 @@
 }
 
 int m8_close() { return disconnect(); }
+
+// These shouldn't be needed with serial
+int m8_pause_processing(void) { return 1; }
+int m8_resume_processing(void) { return 1; }
 #endif
--- a/src/backends/m8_libusb.c
+++ b/src/backends/m8_libusb.c
@@ -364,4 +364,8 @@
   return 1;
 }
 
+// These shouldn't be needed with serial
+int m8_pause_processing(void) { return 1; }
+int m8_resume_processing(void) { return 1; }
+
 #endif
--- a/src/backends/m8_rtmidi.c
+++ b/src/backends/m8_rtmidi.c
@@ -23,6 +23,8 @@
 const unsigned int m8_sysex_header_size = sizeof(m8_sysex_header);
 const unsigned char sysex_message_end = 0xF7;
 
+bool midi_processing_suspended = false;
+
 bool message_is_m8_sysex(const unsigned char *message) {
   if (memcmp(m8_sysex_header, message, m8_sysex_header_size) == 0) {
     return true;
@@ -90,7 +92,7 @@
   (void)delta_time;
   (void)user_data;
 
-  if (message_size < 5 || !message_is_m8_sysex(message))
+  if (midi_processing_suspended || message_size < 5 || !message_is_m8_sysex(message))
     return;
 
   unsigned char *decoded_data;
@@ -205,6 +207,7 @@
 }
 
 int disconnect(void) {
+  SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Sending disconnect message to M8");
   const unsigned char disconnect_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'D', 0xF7};
   const int result =
       rtmidi_out_send_message(midi_out, &disconnect_sysex[0], sizeof(disconnect_sysex));
@@ -287,12 +290,9 @@
 }
 
 int m8_close() {
-  if (queue.mutex != NULL) {
-    SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Destroying command queue");
-    SDL_DestroyMutex(queue.mutex);
-    SDL_DestroyCondition(queue.cond);
-  }
-  return disconnect();
+  const int result = disconnect();
+  destroy_queue(&queue);
+  return result;
 }
 
 int m8_list_devices() {
@@ -309,6 +309,18 @@
     SDL_Log("MIDI IN port %d, name: %s", port_number, port_name);
   }
   close_and_free_midi_ports();
+  return 1;
+}
+
+int m8_pause_processing(void) {
+  midi_processing_suspended = true;
+  SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Pausing MIDI processing");
+  return 1;
+}
+int m8_resume_processing(void) {
+  midi_processing_suspended = false;
+  SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Resuming MIDI processing");
+  m8_reset_display();
   return 1;
 }
 
--- a/src/input.c
+++ b/src/input.c
@@ -117,19 +117,19 @@
     SDL_Scancode scancode;
     uint8_t value;
   } normal_key_map[] = {
-    {conf->key_up, key_up},
-    {conf->key_left, key_left},
-    {conf->key_down, key_down},
-    {conf->key_right, key_right},
-    {conf->key_select, key_select},
-    {conf->key_select_alt, key_select},
-    {conf->key_start, key_start},
-    {conf->key_start_alt, key_start},
-    {conf->key_opt, key_opt},
-    {conf->key_opt_alt, key_opt},
-    {conf->key_edit, key_edit},
-    {conf->key_edit_alt, key_edit},
-    {conf->key_delete, key_opt | key_edit},
+      {conf->key_up, key_up},
+      {conf->key_left, key_left},
+      {conf->key_down, key_down},
+      {conf->key_right, key_right},
+      {conf->key_select, key_select},
+      {conf->key_select_alt, key_select},
+      {conf->key_start, key_start},
+      {conf->key_start_alt, key_start},
+      {conf->key_opt, key_opt},
+      {conf->key_opt_alt, key_opt},
+      {conf->key_edit, key_edit},
+      {conf->key_edit_alt, key_edit},
+      {conf->key_delete, key_opt | key_edit},
   };
 
   // Handle special messages (different message type)
@@ -137,8 +137,8 @@
     SDL_Scancode scancode;
     special_messages_t message;
   } special_key_map[] = {
-    {conf->key_reset, msg_reset_display},
-    {conf->key_toggle_audio, msg_toggle_audio},
+      {conf->key_reset, msg_reset_display},
+      {conf->key_toggle_audio, msg_toggle_audio},
   };
 
   // Check normal key mappings
@@ -198,11 +198,7 @@
       break;
 
     case SDL_EVENT_WINDOW_RESIZED:
-      if (SDL_GetTicks() - ticks_window_resized > 500) {
-        SDL_Log("Resizing window...");
-        key = (input_msg_s){special, msg_reset_display, 0, 0};
-        ticks_window_resized = SDL_GetTicks();
-      }
+      fix_texture_scaling_after_window_resize();
       break;
 
     case SDL_EVENT_KEY_DOWN:
--- a/src/main.c
+++ b/src/main.c
@@ -7,6 +7,7 @@
 
 #include <SDL3/SDL.h>
 #include <signal.h>
+#include <stdlib.h>
 
 #include "SDL2_inprint.h"
 #include "backends/audio.h"
@@ -17,9 +18,13 @@
 #include "input.h"
 #include "render.h"
 
-#include <stdlib.h>
+#if TARGET_OS_IOS
+#include <SDL3/SDL_main.h>
+unsigned char app_suspended = 0;
+#endif // TARGET_OS_IOS
 
 enum app_state app_state = WAIT_FOR_DEVICE;
+unsigned char device_connected = 0;
 
 // Handle CTRL+C / SIGINT, SIGKILL etc.
 static void signal_handler(int unused) {
@@ -27,17 +32,16 @@
   app_state = QUIT;
 }
 
-static void initialize_signals() {
+static void initialize_signals(void) {
   signal(SIGINT, signal_handler);
   signal(SIGTERM, signal_handler);
-#ifdef SIGQUIT
+#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) {
   static Uint64 ticks_poll_device = 0;
   static Uint64 ticks_update_screen = 0;
 
@@ -46,6 +50,7 @@
   }
 
   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) {
@@ -53,6 +58,14 @@
       app_state = QUIT;
     }
 
+#if TARGET_OS_IOS
+    // 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();
@@ -88,7 +101,8 @@
   }
 }
 
-static config_params_s initialize_config(int argc, char *argv[], char **preferred_device, char **config_filename) {
+static config_params_s initialize_config(int argc, char *argv[], char **preferred_device,
+                                         char **config_filename) {
   for (int i = 1; i < argc; i++) {
     if (SDL_strcmp(argv[i], "--list") == 0) {
       exit(m8_list_devices());
@@ -123,7 +137,8 @@
   SDL_Log("Shutting down.");
 }
 
-static unsigned char handle_device_initialization(unsigned char wait_for_device, const char *preferred_device) {
+static unsigned char handle_device_initialization(unsigned char wait_for_device,
+                                                  const char *preferred_device) {
   const unsigned char device_connected = m8_initialize(1, preferred_device);
   if (!wait_for_device && device_connected == 0) {
     SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected!");
@@ -132,8 +147,62 @@
   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
+
 static void main_loop(config_params_s *conf, const char *preferred_device) {
-  unsigned char device_connected = 0;
 
   do {
     device_connected = m8_initialize(1, preferred_device);
@@ -140,7 +209,7 @@
     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.
+        m8_reset_display(); // Avoid display glitches.
       }
       app_state = RUN;
     } else {
@@ -174,7 +243,6 @@
   cleanup_resources(device_connected, conf);
 }
 
-
 int main(const int argc, char *argv[]) {
   char *preferred_device = NULL;
   char *config_filename = NULL;
@@ -182,12 +250,18 @@
   config_params_s conf = initialize_config(argc, argv, &preferred_device, &config_filename);
   initialize_signals();
 
-  const unsigned char initial_device_connected = handle_device_initialization(conf.wait_for_device, preferred_device);
+  const unsigned char initial_device_connected =
+      handle_device_initialization(conf.wait_for_device, preferred_device);
   if (!renderer_initialize(conf.init_fullscreen)) {
     SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Failed to initialize renderer.");
     cleanup_resources(initial_device_connected, &conf);
     return EXIT_FAILURE;
   }
+
+#if TARGET_OS_IOS
+  // IOS events handler
+  SDL_SetEventFilter(handle_app_events, &conf);
+#endif
 
   gamecontrollers_initialize();
 
--- a/src/render.c
+++ b/src/render.c
@@ -45,25 +45,37 @@
 // Initializes SDL and creates a renderer and required surfaces
 int renderer_initialize(const unsigned int init_fullscreen) {
 
+  // SDL documentation recommends this
+  atexit(SDL_Quit);
+
   if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_GAMEPAD) == false) {
-    SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "SDL_Init: %s\n", SDL_GetError());
+    SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "SDL_Init: %s", SDL_GetError());
     return false;
   }
 
-  // SDL documentation recommends this
-  atexit(SDL_Quit);
+  if (!SDL_CreateWindowAndRenderer(
+          "m8c", texture_width * 2, texture_height * 2,
+          SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | init_fullscreen, &win, &rend)) {
+    SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window and renderer: %s",
+                 SDL_GetError());
+    return false;
+  }
 
-  win = SDL_CreateWindow("m8c", texture_width * 2, texture_height * 2,
-                         SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | init_fullscreen);
+  if (!SDL_SetRenderLogicalPresentation(rend, texture_width, texture_height, scaling_mode)) {
+    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set renderer logical presentation: %s",
+                 SDL_GetError());
+    return false;
+  }
 
-  rend = SDL_CreateRenderer(win, NULL);
-
-  SDL_SetRenderLogicalPresentation(rend, texture_width, texture_height, scaling_mode);
-
   main_texture = NULL;
   main_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
                                    texture_width, texture_height);
 
+  if (main_texture == NULL) {
+    SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s", SDL_GetError());
+    return false;
+  }
+
   SDL_SetTextureScaleMode(main_texture, SDL_SCALEMODE_NEAREST);
 
   SDL_SetRenderTarget(rend, main_texture);
@@ -334,3 +346,8 @@
   renderer_set_font_mode(0);
   SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Screensaver destroyed");
 }
+
+void fix_texture_scaling_after_window_resize(void) {
+  SDL_SetRenderTarget(rend, NULL);
+  SDL_SetRenderLogicalPresentation(rend, texture_width, texture_height, scaling_mode);
+}
\ No newline at end of file
--- a/src/render.h
+++ b/src/render.h
@@ -24,4 +24,5 @@
 void screensaver_draw();
 void screensaver_destroy();
 
+void fix_texture_scaling_after_window_resize(void);
 #endif
--