From 58abae105fd5ec339c3a663b2f5fdc40a1f17b86 Mon Sep 17 00:00:00 2001 From: Nathaniel Walizer Date: Fri, 3 Jan 2025 10:54:39 -0800 Subject: [PATCH] Add gamepad support --- src/sdl_input.c | 80 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/src/sdl_input.c b/src/sdl_input.c index ac04a9a..8a818a2 100644 --- a/src/sdl_input.c +++ b/src/sdl_input.c @@ -3,27 +3,50 @@ #include "input.h" -static int filter(void*, SDL_Event* event) { +static SDL_GameController* sdl_find_gamepad() { + int i = SDL_NumJoysticks() - 1; + for ( ; i >= 0 && !SDL_IsGameController(i); --i); + return (i < 0 ? NULL : SDL_GameControllerOpen(i)); +} + +static void sdl_lose_gamepad(void* data) { + SDL_GameController* gamepad = (SDL_GameController*)data; + if (NULL != gamepad) SDL_GameControllerClose(gamepad); +} + +static int sdl_match_gamepad(SDL_JoystickID id, void* data) { + SDL_GameController* gamepad = (SDL_GameController*)data; + return ( SDL_JoystickInstanceID( + SDL_GameControllerGetJoystick(gamepad)) + == id); +} + +static int sdl_event_filter(void*, SDL_Event* event) { return ( SDL_QUIT == event->type || SDL_KEYDOWN == event->type || - SDL_KEYUP == event->type + SDL_KEYUP == event->type || + SDL_CONTROLLERDEVICEADDED == event->type || + SDL_CONTROLLERDEVICEREMOVED == event->type ); } - -static int sdl_input_init(nes_Input_Reader*) { - int status = SDL_Init(SDL_INIT_EVENTS); +static int sdl_input_init(nes_Input_Reader* reader) { + int status = SDL_Init(SDL_INIT_EVENTS | + SDL_INIT_GAMECONTROLLER); if (status == 0) { - SDL_SetEventFilter(filter, NULL); + reader->data = sdl_find_gamepad(); + SDL_SetEventFilter(sdl_event_filter, NULL); } return status; } -static void sdl_input_done(nes_Input_Reader*) {} +static void sdl_input_done(nes_Input_Reader* input) { + sdl_lose_gamepad(input->data); +} -static const int keycodes[nes_controller_num_buttons] = { +static const int sdl_keycodes[nes_controller_num_buttons] = { SDLK_a, SDLK_s, SDLK_q, @@ -36,11 +59,23 @@ static const int keycodes[nes_controller_num_buttons] = { static int button_index(int keycode) { int index = nes_controller_num_buttons - 1; - for ( ; index > 0 && keycode != keycodes[index]; --index); + for ( ; index > 0 && keycode != sdl_keycodes[index]; --index); return index; } -static int sdl_input_update(nes_Input_Reader*, nes_input* input) { +static const int sdl_gamepad_buttons[nes_controller_num_buttons] = { + SDL_CONTROLLER_BUTTON_A, + SDL_CONTROLLER_BUTTON_B, + SDL_CONTROLLER_BUTTON_BACK, + SDL_CONTROLLER_BUTTON_START, + SDL_CONTROLLER_BUTTON_DPAD_UP, + SDL_CONTROLLER_BUTTON_DPAD_DOWN, + SDL_CONTROLLER_BUTTON_DPAD_LEFT, + SDL_CONTROLLER_BUTTON_DPAD_RIGHT, +}; + +static int sdl_input_update(nes_Input_Reader* reader, + nes_input* input) { int status = 0; SDL_Event event = {0}; @@ -58,6 +93,31 @@ static int sdl_input_update(nes_Input_Reader*, nes_input* input) { } else { input->controllers[0].buttons &= ~mask; } + + } else if (SDL_CONTROLLERDEVICEADDED == event.type) { + if (NULL == reader->data) { + reader->data = sdl_find_gamepad(); + } + + } else if (SDL_CONTROLLERDEVICEREMOVED == event.type) { + if (sdl_match_gamepad(event.cdevice.which, + reader->data)) { + sdl_lose_gamepad(reader->data); + reader->data = sdl_find_gamepad(); + } + } + } + + if (NULL != reader->data) { + SDL_GameController* gamepad = (SDL_GameController*)reader->data; + + for (int b = 0; b < nes_controller_num_buttons; ++b) { + if (SDL_GameControllerGetButton( + gamepad, sdl_gamepad_buttons[b])) { + input->controllers[0].buttons |= (1 << b); + } else { + input->controllers[0].buttons &= ~(1 << b); + } } }