shithub: m8c

Download patch

ref: 0ff5d079ae3fb69df0f01a5808b089ce954a10cf
parent: bbedaf64feaa36c5263c4212ae120eb0d8f899e5
author: laamaa <jonne.kokkonen@gmail.com>
date: Mon Sep 22 06:24:33 EDT 2025

update README.md and gamepad handling logic: improve clarity and fix accidental settings toggle issues while device is connected

--- a/README.md
+++ b/README.md
@@ -7,11 +7,12 @@
 or alternative input methods. The application is cross‑platform and can be built on Linux, Windows (MSYS2/MINGW64), and
 macOS.
 
-The Dirtywave M8 Tracker is a portable sequencer and synthesizer featuring 8 tracks of assignable instruments
-(using engines like FM, waveform synthesis, virtual analog, sample playback, and MIDI output). 
+The Dirtywave M8 Tracker is a portable sequencer and synthesizer featuring eight tracks of assignable instruments
+(using engines like FM, waveform synthesis, virtual analog, sample playback, and MIDI output).
 It is inspired by the Game Boy tracker [Little Sound DJ](https://www.littlesounddj.com/lsd/index.php).
 
-m8c works with the M8 hardware over USB. It also supports the [M8 Headless](https://github.com/Dirtywave/M8HeadlessFirmware)
+m8c works with the M8 hardware over USB. It also supports
+the [M8 Headless](https://github.com/Dirtywave/M8HeadlessFirmware)
 firmware running on a [Teensy](https://www.pjrc.com/teensy/) microcontroller. If you enjoy the M8 and its
 tracker workflow, please support [Dirtywave](https://dirtywave.com/) by purchasing the hardware. You can check
 availability [here](https://dirtywave.com/products/m8-tracker-model-02). You can also
@@ -20,9 +21,11 @@
 Many thanks to:
 
 * Trash80: For the great M8 hardware and the original fonts that were converted to a bitmap for use in the program.
-* driedfruit: For a wonderful little routine to blit inline bitmap fonts, [SDL_inprint](https://github.com/driedfruit/SDL_inprint/)
+* driedfruit: For a wonderful little routine to blit inline bitmap
+  fonts, [SDL_inprint](https://github.com/driedfruit/SDL_inprint/)
 * marcinbor85: For the slip handling routine, https://github.com/marcinbor85/slip
-* turbolent: For the great Golang-based g0m8 application, which I used as reference on how the M8 serial protocol works.
+* turbolent: For the great Golang-based g0m8 application, which I used as a reference on how the M8 serial protocol
+  works.
 * *Everyone who's contributed to m8c!*
 
 -------
@@ -47,9 +50,6 @@
 There are prebuilt binaries available in the [releases section](https://github.com/laamaa/m8c/releases/) for recent
 versions of macOS.
 
-When running the program for the first time on macOS, it may not open as it is from an Unidentified Developer. You need
-to open it from the Applications Folder via Control+Click > Open then select Open from the popup menu.
-
 ### Linux
 
 There are packages available for NixOS, an AppImage for easy installation, or you can build the program from source.
@@ -62,7 +62,7 @@
 2. Make it executable: `chmod +x m8c-*.AppImage`
 3. Run it: `./m8c-*.AppImage`
 
-The AppImage is portable and doesn't require installation - just download and run.
+The AppImage is portable and doesn't require installation—it can be run directly from the file.
 
 #### NixOS
 
@@ -137,7 +137,7 @@
 
 #### Choosing a preferred device
 
-When you have multiple M8 devices connected and you want to choose a specific one or launch m8c multiple times, you can
+When you have multiple M8 devices connected, and you want to choose a specific one or launch m8c multiple times, you can
 get the list of devices by running
 
 ```sh
@@ -157,7 +157,9 @@
 ./m8c --dev /dev/cu.usbmodem124709801
 ```
 
-**Note:** The `--dev` option can force detection of any serial device by name. This is useful if libserialport cannot get the correct USB identifiers, like on some Windows 11 setups for example. You may need to look up the correct device name from Device Manager for example if `--list` does not give you any results.
+**Note:** The `--dev` option can force detection of any serial device by name. This is useful if libserialport cannot
+get the correct USB identifiers, like on some Windows 11 setups, for example. You may need to look up the correct device
+name from Device Manager, if `--list` does not give you any results, for example.
 
 -----------
 
@@ -187,7 +189,7 @@
 
 ### Keyjazz
 
-Keyjazz allows to enter notes with keyboard, oldschool tracker-style. The layout is two octaves, starting from keys Z
+Keyjazz allows entering notes with a keyboard, old school tracker-style. The layout is two octaves, starting from keys Z
 and Q.
 When keyjazz is active, regular a/s/z/x keys are disabled. The base octave can be adjusted with numpad star/divide keys
 and the velocity can be set
@@ -202,8 +204,8 @@
 
 The program uses SDL's game controller system, which should make it work automatically with most gamepads. On startup,
 the program tries to load a SDL game controller database named `gamecontrollerdb.txt` from the same directory as the
-config file. If your joypad doesn't work out of the box, you might need to create custom bindings to this file, for
-example with [SDL2 Gamepad Tool](https://generalarcade.com/gamepadtool/).
+config file. If your joypad doesn't work out of the box, you might need to create custom bindings to this file,
+with [SDL2 Gamepad Tool](https://generalarcade.com/gamepadtool/), for example.
 
 ### Gamepad Configuration
 
@@ -238,13 +240,15 @@
 
 - **How to open:**
     - Keyboard: press F1.
-    - Gamepad: hold the Back/Select button for about 2 seconds.
+    - Gamepad: hold the Back/Select button for about 2 seconds (works only when the M8 is disconnected to avoid opening
+      the menu accidentally).
 - **How to navigate:**
     - Move: Up/Down arrows or D‑pad.
     - Activate/enter: Enter/Space or South/A.
     - Adjust values (sliders/integers): Left/Right arrows or D‑pad left/right.
     - Back/close: Esc or F1; on gamepad use East/B or Back.
-    - While remapping inputs, the menu will prompt you; press the desired key/button or move an axis. Use Esc/B/Back to
+    - While remapping inputs, the menu will prompt you; press the desired key/button or move an axis. Use Esc/East/Back
+      to
       cancel a capture.
 
 Changes take effect immediately; use Save if you want them persisted to disk.
@@ -275,7 +279,8 @@
 
 An in-app log overlay is available for platforms where reading console output is inconvenient.
 
-- Default toggle key: F2. You can change it in `config.ini` under `[keyboard]` using `key_toggle_log=<SDL_SCANCODE>`.
+- Default toggle key: F2. You can change it in the config editor, or `config.ini` under `[keyboard]` using
+  `key_toggle_log=<SDL_SCANCODE>`.
 - The overlay shows recent `SDL_Log*` messages.
 - Long lines are wrapped to fit; the view tails the most recent output.
 
@@ -287,7 +292,7 @@
 
 ### Permission Issues
 
-* When starting the program, something like the following appears and the program does not start:
+* When starting the program, something like the following appears, and the program does not start:
 
 ```sh
 $ ./m8c
--- a/src/events.c
+++ b/src/events.c
@@ -8,8 +8,6 @@
 #include <SDL3/SDL.h>
 #include <SDL3/SDL_events.h>
 
-static Uint64 g_back_pressed_at = 0;
-
 SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
   struct app_context *ctx = appstate;
   SDL_AppResult ret_val = SDL_APP_CONTINUE;
@@ -82,29 +80,23 @@
     break;
 
   case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
-    // Start measuring hold time for GUIDE; trigger handled on button up after 1s hold
-    if (event->gbutton.button == SDL_GAMEPAD_BUTTON_BACK) {
-      g_back_pressed_at = SDL_GetTicks();
-    }
-
     if (settings_is_open()) {
       settings_handle_event(ctx, event);
       return ret_val;
     }
-    input_handle_gamepad_button(ctx, event->gbutton.button, true);
-    break;
 
-  case SDL_EVENT_GAMEPAD_BUTTON_UP:
-    // Handle GUIDE release: toggle settings if held for at least 1 second
+    // Allow toggling the settings view using a gamepad only when the device is disconnected to
+    // avoid accidentally opening the screen while using the device
     if (event->gbutton.button == SDL_GAMEPAD_BUTTON_BACK) {
-      const Uint64 now = SDL_GetTicks();
-      if (g_back_pressed_at != 0 && (now - g_back_pressed_at) >= 2000) {
+      if (ctx->app_state == WAIT_FOR_DEVICE && !settings_is_open()) {
         settings_toggle_open();
       }
-      g_back_pressed_at = 0;
-      return ret_val;
     }
 
+    input_handle_gamepad_button(ctx, event->gbutton.button, true);
+    break;
+
+  case SDL_EVENT_GAMEPAD_BUTTON_UP:
     if (settings_is_open()) {
       settings_handle_event(ctx, event);
       return ret_val;
@@ -119,7 +111,6 @@
     }
     input_handle_gamepad_axis(ctx, event->gaxis.axis, event->gaxis.value);
     break;
-
 
   default:
     break;
--- a/src/settings.c
+++ b/src/settings.c
@@ -413,37 +413,46 @@
   if (!g_settings.is_open)
     return;
 
-  if (e->type == SDL_EVENT_KEY_DOWN) {
-    if (e->key.key == SDLK_ESCAPE || e->key.key == SDLK_F1) {
-      settings_handle_back();
-      return;
-    }
-    // Capture key remap
-    if (g_settings.capture_mode == CAPTURE_KEY) {
-      if (g_settings.capture_target != NULL) {
-        unsigned int *dst = g_settings.capture_target;
-        *dst = e->key.scancode;
+  /**
+   * iOS sends both keyboard and gamepad events on gamepad button presses.
+   * Workaround here is to ignore the keyboard events on iOS, if a gamepad is connected.
+   */
+  int gamepad_count = 0;
+  SDL_GetGamepads(&gamepad_count);
+
+  if (TARGET_OS_IOS == 0 || gamepad_count == 0) {
+    if (e->type == SDL_EVENT_KEY_DOWN) {
+      if (e->key.key == SDLK_ESCAPE || e->key.key == SDLK_F1) {
+        settings_handle_back();
+        return;
       }
-      g_settings.capture_mode = CAPTURE_NONE;
-      g_settings.capture_target = NULL;
-      g_settings.needs_redraw = 1;
-      return;
-    }
-    if (e->key.key == SDLK_UP) {
-      settings_move(&ctx->conf, -1);
-      return;
-    }
-    if (e->key.key == SDLK_DOWN) {
-      settings_move(&ctx->conf, 1);
-      return;
-    }
-    if (e->key.key == SDLK_LEFT || e->key.key == SDLK_RIGHT) {
-      if (settings_adjust_selected(&ctx->conf, e->key.key == SDLK_LEFT ? -1 : 1))
+      // Capture key remap
+      if (g_settings.capture_mode == CAPTURE_KEY) {
+        if (g_settings.capture_target != NULL) {
+          unsigned int *dst = g_settings.capture_target;
+          *dst = e->key.scancode;
+        }
+        g_settings.capture_mode = CAPTURE_NONE;
+        g_settings.capture_target = NULL;
+        g_settings.needs_redraw = 1;
         return;
-    }
-    if (e->key.key == SDLK_RETURN || e->key.key == SDLK_SPACE) {
-      settings_handle_enter(ctx);
-      return;
+      }
+      if (e->key.key == SDLK_UP) {
+        settings_move(&ctx->conf, -1);
+        return;
+      }
+      if (e->key.key == SDLK_DOWN) {
+        settings_move(&ctx->conf, 1);
+        return;
+      }
+      if (e->key.key == SDLK_LEFT || e->key.key == SDLK_RIGHT) {
+        if (settings_adjust_selected(&ctx->conf, e->key.key == SDLK_LEFT ? -1 : 1))
+          return;
+      }
+      if (e->key.key == SDLK_RETURN || e->key.key == SDLK_SPACE) {
+        settings_handle_enter(ctx);
+        return;
+      }
     }
   }
 
--