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
--
⑨