SDL: Gamepads can now be preferentially loaded
jump to
@@ -79,6 +79,8 @@
Configuration* overrides() { return GBAConfigGetOverrides(&m_config); } void saveOverride(const GBACartridgeOverride&); + Configuration* input() { return GBAConfigGetInput(&m_config); } + public slots: void setOption(const char* key, bool value); void setOption(const char* key, int value);@@ -90,10 +92,7 @@
void write(); private: - Configuration* configuration() { return &m_config.configTable; } Configuration* defaults() { return &m_config.defaultsTable; } - - friend class InputController; // TODO: Do this without friends GBAConfig m_config; GBAOptions m_opts;
@@ -19,6 +19,9 @@ }
using namespace QGBA; +int InputController::s_sdlInited = 0; +GBASDLEvents InputController::s_sdlEvents; + InputController::InputController(int playerId, QObject* parent) : QObject(parent) , m_playerId(playerId)@@ -28,8 +31,11 @@ {
GBAInputMapInit(&m_inputMap); #ifdef BUILD_SDL - m_sdlEvents.bindings = &m_inputMap; - GBASDLInitEvents(&m_sdlEvents, playerId); + if (s_sdlInited == 0) { + GBASDLInitEvents(&s_sdlEvents); + } + ++s_sdlInited; + m_sdlPlayer.bindings = &m_inputMap; GBASDLInitBindings(&m_inputMap); m_gamepadTimer = new QTimer(this);@@ -54,7 +60,10 @@ InputController::~InputController() {
GBAInputMapDeinit(&m_inputMap); #ifdef BUILD_SDL - GBASDLDeinitEvents(&m_sdlEvents); + --s_sdlInited; + if (s_sdlInited == 0) { + GBASDLDeinitEvents(&s_sdlEvents); + } #endif }@@ -62,26 +71,31 @@ void InputController::setConfiguration(ConfigController* config) {
m_config = config; loadConfiguration(KEYBOARD); #ifdef BUILD_SDL + GBASDLEventsLoadConfig(&s_sdlEvents, config->input()); + if (!m_playerAttached) { + GBASDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer); + m_playerAttached = true; + } loadConfiguration(SDL_BINDING_BUTTON); loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON)); #endif } void InputController::loadConfiguration(uint32_t type) { - GBAInputMapLoad(&m_inputMap, type, m_config->configuration()); + GBAInputMapLoad(&m_inputMap, type, m_config->input()); } void InputController::loadProfile(uint32_t type, const char* profile) { - GBAInputProfileLoad(&m_inputMap, type, m_config->configuration(), profile); + GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile); } void InputController::saveConfiguration(uint32_t type) { - GBAInputMapSave(&m_inputMap, type, m_config->configuration()); + GBAInputMapSave(&m_inputMap, type, m_config->input()); m_config->write(); } void InputController::saveProfile(uint32_t type, const char* profile) { - GBAInputProfileSave(&m_inputMap, type, m_config->configuration(), profile); + GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile); m_config->write(); }@@ -90,9 +104,9 @@ UNUSED(type);
#ifdef BUILD_SDL if (type == SDL_BINDING_BUTTON) { #if SDL_VERSION_ATLEAST(2, 0, 0) - return SDL_JoystickName(m_sdlEvents.joystick); + return SDL_JoystickName(m_sdlPlayer.joystick); #else - return SDL_JoystickName(SDL_JoystickIndex(m_sdlEvents.joystick)); + return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick)); #endif } #endif@@ -109,7 +123,7 @@ }
#ifdef BUILD_SDL int InputController::testSDLEvents() { - SDL_Joystick* joystick = m_sdlEvents.joystick; + SDL_Joystick* joystick = m_sdlPlayer.joystick; SDL_JoystickUpdate(); int numButtons = SDL_JoystickNumButtons(joystick); int activeButtons = 0;@@ -156,7 +170,7 @@ return activeButtons;
} QSet<int> InputController::activeGamepadButtons() { - SDL_Joystick* joystick = m_sdlEvents.joystick; + SDL_Joystick* joystick = m_sdlPlayer.joystick; SDL_JoystickUpdate(); int numButtons = SDL_JoystickNumButtons(joystick); QSet<int> activeButtons;@@ -170,7 +184,7 @@ return activeButtons;
} QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes() { - SDL_Joystick* joystick = m_sdlEvents.joystick; + SDL_Joystick* joystick = m_sdlPlayer.joystick; SDL_JoystickUpdate(); int numButtons = SDL_JoystickNumAxes(joystick); QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
@@ -70,7 +70,10 @@ ConfigController* m_config;
int m_playerId; #ifdef BUILD_SDL - GBASDLEvents m_sdlEvents; + static int s_sdlInited; + static GBASDLEvents s_sdlEvents; + GBASDLPlayer m_sdlPlayer; + bool m_playerAttached; #endif QSet<int> m_activeButtons;
@@ -136,7 +136,7 @@ SDL_Event event;
while (context->state < THREAD_EXITING) { while (SDL_PollEvent(&event)) { - GBASDLHandleEvent(context, &renderer->events, &event); + GBASDLHandleEvent(context, &renderer->player, &event); } if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) {
@@ -65,11 +65,11 @@ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
#endif #if SDL_VERSION_ATLEAST(2, 0, 0) - renderer->window = SDL_CreateWindow(PROJECT_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->events.fullscreen)); + renderer->window = SDL_CreateWindow(PROJECT_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->player.fullscreen)); SDL_GL_CreateContext(renderer->window); SDL_GL_SetSwapInterval(1); SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight); - renderer->events.window = renderer->window; + renderer->player.window = renderer->window; #else SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); #ifdef COLOR_16_BIT@@ -123,13 +123,13 @@ glLoadIdentity();
glOrtho(0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, 0, 1); while (context->state < THREAD_EXITING) { while (SDL_PollEvent(&event)) { - GBASDLHandleEvent(context, &renderer->events, &event); + GBASDLHandleEvent(context, &renderer->player, &event); #if SDL_VERSION_ATLEAST(2, 0, 0) // Event handling can change the size of the screen - if (renderer->events.windowUpdated) { + if (renderer->player.windowUpdated) { SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight); _doViewport(renderer->viewportWidth, renderer->viewportHeight, renderer); - renderer->events.windowUpdated = 0; + renderer->player.windowUpdated = 0; } #endif }
@@ -72,8 +72,8 @@
renderer.viewportWidth = opts.width; renderer.viewportHeight = opts.height; #if SDL_VERSION_ATLEAST(2, 0, 0) - renderer.events.fullscreen = opts.fullscreen; - renderer.events.windowUpdated = 0; + renderer.player.fullscreen = opts.fullscreen; + renderer.player.windowUpdated = 0; #endif renderer.ratio = graphicsOpts.multiplier; if (renderer.ratio == 0) {@@ -103,10 +103,12 @@
renderer.audio.samples = context.audioBuffers; GBASDLInitAudio(&renderer.audio, &context); - renderer.events.bindings = &inputMap; + renderer.player.bindings = &inputMap; GBASDLInitBindings(&inputMap); - GBASDLInitEvents(&renderer.events, 0); + GBASDLInitEvents(&renderer.events); GBASDLEventsLoadConfig(&renderer.events, GBAConfigGetInput(&config)); + GBASDLAttachPlayer(&renderer.events, &renderer.player); + GBASDLPlayerLoadConfig(&renderer.player, GBAConfigGetInput(&config)); context.overrides = GBAConfigGetOverrides(&config); int didFail = 0;
@@ -39,6 +39,7 @@ struct SDLSoftwareRenderer {
struct GBAVideoSoftwareRenderer d; struct GBASDLAudio audio; struct GBASDLEvents events; + struct GBASDLPlayer player; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Window* window;
@@ -68,7 +68,7 @@ SDL_Event event;
while (context->state < THREAD_EXITING) { while (SDL_PollEvent(&event)) { - GBASDLHandleEvent(context, &renderer->events, &event); + GBASDLHandleEvent(context, &renderer->player, &event); } if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) {
@@ -11,6 +11,7 @@ #include "gba/supervisor/rr.h"
#include "gba/serialize.h" #include "gba/video.h" #include "gba/renderers/video-software.h" +#include "util/configuration.h" #include "util/vfs.h" #if SDL_VERSION_ATLEAST(2, 0, 0) && defined(__APPLE__)@@ -19,21 +20,57 @@ #else
#define GUI_MOD KMOD_CTRL #endif -static int _openContexts = 0; - -bool GBASDLInitEvents(struct GBASDLEvents* context, int playerId) { - if (!_openContexts && SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { +bool GBASDLInitEvents(struct GBASDLEvents* context) { + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { return false; } - ++_openContexts; + SDL_JoystickEventState(SDL_ENABLE); - context->joystick = SDL_JoystickOpen(playerId); + int nJoysticks = SDL_NumJoysticks(); + if (nJoysticks > 0) { + context->nJoysticks = nJoysticks; + context->joysticks = calloc(context->nJoysticks, sizeof(SDL_Joystick*)); + size_t i; + for (i = 0; i < context->nJoysticks; ++i) { + context->joysticks[i] = SDL_JoystickOpen(i); + } + } else { + context->nJoysticks = 0; + context->joysticks = 0; + } + + context->playersAttached = 0; + + size_t i; + for (i = 0; i < MAX_PLAYERS; ++i) { + context->preferredJoysticks[i] = 0; + context->joysticksClaimed[i] = SIZE_MAX; + } + #if !SDL_VERSION_ATLEAST(2, 0, 0) SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); #endif return true; } +void GBASDLDeinitEvents(struct GBASDLEvents* context) { + size_t i; + for (i = 0; i < context->nJoysticks; ++i) { + SDL_JoystickClose(context->joysticks[i]); + } + + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); +} + +void GBASDLEventsLoadConfig(struct GBASDLEvents* context, const struct Configuration* config) { + char sectionName[16]; + snprintf(sectionName, sizeof(sectionName), "input.%c%c%c%c", SDL_BINDING_BUTTON >> 24, SDL_BINDING_BUTTON >> 16, SDL_BINDING_BUTTON >> 8, SDL_BINDING_BUTTON); + context->preferredJoysticks[0] = ConfigurationGetValue(config, sectionName, "device0"); + context->preferredJoysticks[1] = ConfigurationGetValue(config, sectionName, "device1"); + context->preferredJoysticks[2] = ConfigurationGetValue(config, sectionName, "device2"); + context->preferredJoysticks[3] = ConfigurationGetValue(config, sectionName, "device3"); +} + void GBASDLInitBindings(struct GBAInputMap* inputMap) { #ifdef BUILD_PANDORA GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_PAGEDOWN, GBA_KEY_A);@@ -87,7 +124,62 @@ description = (struct GBAAxis) { GBA_KEY_DOWN, GBA_KEY_UP, 0x4000, -0x4000 };
GBAInputBindAxis(inputMap, SDL_BINDING_BUTTON, 1, &description); } -void GBASDLEventsLoadConfig(struct GBASDLEvents* context, const struct Configuration* config) { +bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player) { + player->joystick = 0; + player->joystickIndex = SIZE_MAX; + + if (events->playersAttached >= MAX_PLAYERS) { + return false; + } + + int playerId = events->playersAttached; + size_t firstUnclaimed = SIZE_MAX; + + size_t i; + for (i = 0; i < events->nJoysticks; ++i) { + bool claimed = false; + + int p; + for (p = 0; p < events->playersAttached; ++p) { + if (events->joysticksClaimed[p] == i) { + claimed = true; + break; + } + } + if (claimed) { + continue; + } + + if (firstUnclaimed == SIZE_MAX) { + firstUnclaimed = i; + } + + const char* joystickName; +#if SDL_VERSION_ATLEAST(2, 0, 0) + joystickName = SDL_JoystickName(events->joysticks[i]); +#else + joystickName = SDL_JoystickName(SDL_JoystickIndex(events->joysticks[i])); +#endif + if (events->preferredJoysticks[playerId] && strcmp(events->preferredJoysticks[playerId], joystickName) == 0) { + player->joystickIndex = i; + break; + } + } + + if (player->joystickIndex == SIZE_MAX && firstUnclaimed != SIZE_MAX) { + player->joystickIndex = firstUnclaimed; + } + + if (player->joystickIndex != SIZE_MAX) { + player->joystick = events->joysticks[player->joystickIndex]; + events->joysticksClaimed[playerId] = player->joystickIndex; + } + + ++events->playersAttached; + return true; +} + +void GBASDLPlayerLoadConfig(struct GBASDLPlayer* context, const struct Configuration* config) { GBAInputMapLoad(context->bindings, SDL_BINDING_KEY, config); if (context->joystick) { GBAInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config);@@ -99,21 +191,12 @@ #endif
} } -void GBASDLDeinitEvents(struct GBASDLEvents* context) { - SDL_JoystickClose(context->joystick); - - --_openContexts; - if (!_openContexts) { - SDL_QuitSubSystem(SDL_INIT_JOYSTICK); - } -} - static void _pauseAfterFrame(struct GBAThread* context) { context->frameCallback = 0; GBAThreadPauseFromThread(context); } -static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_KeyboardEvent* event) { +static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const struct SDL_KeyboardEvent* event) { enum GBAKey key = GBA_KEY_NONE; if (!event->keysym.mod) { #if !defined(BUILD_PANDORA) && SDL_VERSION_ATLEAST(2, 0, 0)@@ -230,7 +313,7 @@ }
} } -static void _GBASDLHandleJoyButton(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_JoyButtonEvent* event) { +static void _GBASDLHandleJoyButton(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const struct SDL_JoyButtonEvent* event) { enum GBAKey key = 0; key = GBAInputMapKey(sdlContext->bindings, SDL_BINDING_BUTTON, event->button); if (key == GBA_KEY_NONE) {@@ -264,7 +347,7 @@ context->activeKeys &= ~((1 << GBA_KEY_UP) | (1 << GBA_KEY_LEFT) | (1 << GBA_KEY_DOWN) | (1 << GBA_KEY_RIGHT));
context->activeKeys |= key; } -static void _GBASDLHandleJoyAxis(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_JoyAxisEvent* event) { +static void _GBASDLHandleJoyAxis(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const struct SDL_JoyAxisEvent* event) { int keys = context->activeKeys; keys = GBAInputClearAxis(sdlContext->bindings, SDL_BINDING_BUTTON, event->axis, keys);@@ -277,7 +360,7 @@ context->activeKeys = keys;
} #if SDL_VERSION_ATLEAST(2, 0, 0) -static void _GBASDLHandleWindowEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_WindowEvent* event) { +static void _GBASDLHandleWindowEvent(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const struct SDL_WindowEvent* event) { UNUSED(context); switch (event->event) { case SDL_WINDOWEVENT_SIZE_CHANGED:@@ -287,7 +370,7 @@ }
} #endif -void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const union SDL_Event* event) { +void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const union SDL_Event* event) { switch (event->type) { case SDL_QUIT: GBAThreadEnd(context);
@@ -12,15 +12,26 @@ #include "gba/supervisor/thread.h"
#include <SDL.h> -#define SDL_BINDING_KEY 0x53444C4B -#define SDL_BINDING_BUTTON 0x53444C42 +#define SDL_BINDING_KEY 0x53444C4BU +#define SDL_BINDING_BUTTON 0x53444C42U + +#define MAX_PLAYERS 4 struct GBAVideoSoftwareRenderer; struct Configuration; struct GBASDLEvents { + SDL_Joystick** joysticks; + size_t nJoysticks; + const char* preferredJoysticks[MAX_PLAYERS]; + int playersAttached; + size_t joysticksClaimed[MAX_PLAYERS]; +}; + +struct GBASDLPlayer { struct GBAInputMap* bindings; SDL_Joystick* joystick; + size_t joystickIndex; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Window* window; int fullscreen;@@ -28,12 +39,16 @@ int windowUpdated;
#endif }; -bool GBASDLInitEvents(struct GBASDLEvents*, int playerId); +bool GBASDLInitEvents(struct GBASDLEvents*); void GBASDLDeinitEvents(struct GBASDLEvents*); -void GBASDLInitBindings(struct GBAInputMap* inputMap); +bool GBASDLAttachPlayer(struct GBASDLEvents*, struct GBASDLPlayer*); void GBASDLEventsLoadConfig(struct GBASDLEvents*, const struct Configuration*); +void GBASDLPlayerChangeJoystick(struct GBASDLEvents*, struct GBASDLPlayer*, size_t index); -void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const union SDL_Event* event); +void GBASDLInitBindings(struct GBAInputMap* inputMap); +void GBASDLPlayerLoadConfig(struct GBASDLPlayer*, const struct Configuration*); + +void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const union SDL_Event* event); #endif
@@ -18,9 +18,9 @@ #endif
#endif #if SDL_VERSION_ATLEAST(2, 0, 0) - renderer->window = SDL_CreateWindow(PROJECT_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->events.fullscreen)); + renderer->window = SDL_CreateWindow(PROJECT_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->player.fullscreen)); SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight); - renderer->events.window = renderer->window; + renderer->player.window = renderer->window; renderer->sdlRenderer = SDL_CreateRenderer(renderer->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); #ifdef COLOR_16_BIT #ifdef COLOR_5_6_5@@ -80,7 +80,7 @@ #endif
while (context->state < THREAD_EXITING) { while (SDL_PollEvent(&event)) { - GBASDLHandleEvent(context, &renderer->events, &event); + GBASDLHandleEvent(context, &renderer->player, &event); } if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) {