Add raw joystick fallback for non-standard controllers

Open the joystick via SDL_JoystickOpen() when no GameController mapping
exists, fixing d-pad and face button input on devices like the Anbernic
retro handheld (GUID not in SDL's database). Handles JOYHATMOTION for
d-pad navigation and JOYBUTTONDOWN button 0 for activation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Michael Smith 2026-02-13 11:37:19 +01:00
parent dbc3f11797
commit 2fd764f60f
2 changed files with 45 additions and 0 deletions

View File

@ -43,6 +43,12 @@ This document specifies the functional requirements for an SDL2 based media play
## 6. Changelog ## 6. Changelog
### 2026-02-13 — Raw joystick fallback for non-standard controllers
- **Joystick fallback**: When no SDL GameController mapping exists for a connected device, the joystick is now opened directly via `SDL_JoystickOpen()` as a fallback. This fixes d-pad and face button input on devices like the Anbernic retro handheld whose GUID is not in SDL's GameController database.
- **Hat/button handling**: `SDL_JOYHATMOTION` events drive d-pad navigation (left/right to move focus, up/down for volume), and `SDL_JOYBUTTONDOWN` button 0 (BTN_SOUTH / A) activates the focused button.
- **Hot-unplug**: Raw joystick is properly closed on `SDL_JOYDEVICEREMOVED`.
### 2026-02-13 — Embed controls spritesheet into binary ### 2026-02-13 — Embed controls spritesheet into binary
- **Embedded asset**: `controls.png` is now compiled into the binary as a C byte array (`src/controls_png.h`), eliminating the requirement to run from the `build/` directory. - **Embedded asset**: `controls.png` is now compiled into the binary as a C byte array (`src/controls_png.h`), eliminating the requirement to run from the `build/` directory.

View File

@ -67,6 +67,7 @@ static int current_file_index = 0;
static float volume = 0.5f; static float volume = 0.5f;
static int focus_index = FOCUS_PLAY; static int focus_index = FOCUS_PLAY;
static SDL_GameController* controller = NULL; static SDL_GameController* controller = NULL;
static SDL_Joystick* joystick = NULL;
static int debug_mode = 0; static int debug_mode = 0;
/* --- Utility --- */ /* --- Utility --- */
@ -627,6 +628,16 @@ int main(int argc, char** argv) {
} }
} }
if (!controller) {
for (int i = 0; i < num_joy; i++) {
joystick = SDL_JoystickOpen(i);
if (joystick) {
printf("Joystick: %s\n", SDL_JoystickName(joystick));
break;
}
}
}
SDL_Surface* controls_surface = IMG_Load("controls.png"); SDL_Surface* controls_surface = IMG_Load("controls.png");
if (!controls_surface) { if (!controls_surface) {
SDL_RWops* rw = SDL_RWFromConstMem(controls_png_data, controls_png_size); SDL_RWops* rw = SDL_RWFromConstMem(controls_png_data, controls_png_size);
@ -763,6 +774,26 @@ int main(int argc, char** argv) {
} }
break; break;
case SDL_JOYHATMOTION: {
Uint8 hat = e.jhat.value;
if (hat & SDL_HAT_LEFT) {
focus_index = (focus_index - 1 + FOCUS_COUNT) % FOCUS_COUNT;
} else if (hat & SDL_HAT_RIGHT) {
focus_index = (focus_index + 1) % FOCUS_COUNT;
} else if (hat & SDL_HAT_UP) {
if (focus_index == FOCUS_VOLUME) adjust_volume(0.05f);
} else if (hat & SDL_HAT_DOWN) {
if (focus_index == FOCUS_VOLUME) adjust_volume(-0.05f);
}
break;
}
case SDL_JOYBUTTONDOWN:
if (e.jbutton.button == 0) {
activate_focused_button();
}
break;
case SDL_CONTROLLERDEVICEADDED: case SDL_CONTROLLERDEVICEADDED:
if (!controller && SDL_IsGameController(e.cdevice.which)) { if (!controller && SDL_IsGameController(e.cdevice.which)) {
controller = SDL_GameControllerOpen(e.cdevice.which); controller = SDL_GameControllerOpen(e.cdevice.which);
@ -786,6 +817,13 @@ int main(int argc, char** argv) {
printf("Controller removed\n"); printf("Controller removed\n");
} }
break; break;
case SDL_JOYDEVICEREMOVED:
if (joystick && e.jdevice.which == SDL_JoystickInstanceID(joystick)) {
SDL_JoystickClose(joystick);
joystick = NULL;
}
break;
} }
} }
@ -898,6 +936,7 @@ int main(int argc, char** argv) {
decoder_close(); decoder_close();
SDL_FreeAudioStream(stream); SDL_FreeAudioStream(stream);
SDL_DestroyTexture(controls_texture); SDL_DestroyTexture(controls_texture);
if (joystick) SDL_JoystickClose(joystick);
if (controller) SDL_GameControllerClose(controller); if (controller) SDL_GameControllerClose(controller);
SDL_CloseAudioDevice(audio_device); SDL_CloseAudioDevice(audio_device);
SDL_DestroyRenderer(renderer); SDL_DestroyRenderer(renderer);