From 13dfb144e88290b0e747ac51391f0882e57ce4b2 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 17 Jan 2016 22:45:25 -0800 Subject: [PATCH] SDL: Hotplugging for joysticks. Still needs work. --- CHANGES | 1 + src/platform/qt/GBAKeyEditor.cpp | 22 +++-- src/platform/qt/GBAKeyEditor.h | 1 + src/platform/qt/InputController.cpp | 35 ++++--- src/platform/qt/InputController.h | 1 + src/platform/sdl/sdl-events.c | 142 ++++++++++++++++++---------- src/platform/sdl/sdl-events.h | 25 +++-- 7 files changed, 149 insertions(+), 78 deletions(-) diff --git a/CHANGES b/CHANGES index 1f0f5aaf2..a429fcd02 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,7 @@ Features: - Cleaner, unified settings window - Added a setting for pausing when the emulator is not in focus - Customizable paths for save games, save states, screenshots and patches + - Controller hotplugging Bugfixes: - Util: Fix PowerPC PNG read/write pixel order - VFS: Fix VFileReadline and remove _vfdReadline diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 9519e89d5..4594fbabd 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -15,6 +15,12 @@ #include "InputController.h" #include "KeyEditor.h" +#ifdef BUILD_SDL +extern "C" { +#include "platform/sdl/sdl-events.h" +} +#endif + using namespace QGBA; const qreal GBAKeyEditor::DPAD_CENTER_X = 0.247; @@ -51,22 +57,19 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& #ifdef BUILD_SDL if (type == SDL_BINDING_BUTTON) { + controller->updateJoysticks(); controller->recalibrateAxes(); lookupAxes(map); m_profileSelect = new QComboBox(this); m_profileSelect->addItems(controller->connectedGamepads(type)); int activeGamepad = controller->gamepad(type); + selectGamepad(activeGamepad); if (activeGamepad > 0) { m_profileSelect->setCurrentIndex(activeGamepad); } - connect(m_profileSelect, static_cast(&QComboBox::currentIndexChanged), [this] (int i) { - m_controller->setGamepad(m_type, i); - m_profile = m_profileSelect->currentText(); - m_controller->loadProfile(m_type, m_profile); - refresh(); - }); + connect(m_profileSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectGamepad(int))); m_clear = new QWidget(this); QHBoxLayout* layout = new QHBoxLayout; @@ -311,6 +314,13 @@ void GBAKeyEditor::setAxisValue(int axis, int32_t value) { KeyEditor* focused = *m_currentKey; focused->setValueAxis(axis, value); } + +void GBAKeyEditor::selectGamepad(int index) { + m_controller->setGamepad(m_type, index); + m_profile = m_profileSelect->currentText(); + m_controller->loadProfile(m_type, m_profile); + refresh(); +} #endif KeyEditor* GBAKeyEditor::keyById(GBAKey key) { diff --git a/src/platform/qt/GBAKeyEditor.h b/src/platform/qt/GBAKeyEditor.h index c555e811f..6637571d2 100644 --- a/src/platform/qt/GBAKeyEditor.h +++ b/src/platform/qt/GBAKeyEditor.h @@ -46,6 +46,7 @@ private slots: void refresh(); #ifdef BUILD_SDL void setAxisValue(int axis, int32_t value); + void selectGamepad(int index); #endif private: diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index a7b99ee8a..ebadbab4a 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -46,6 +46,7 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren ++s_sdlInited; m_sdlPlayer.bindings = &m_inputMap; GBASDLInitBindings(&m_inputMap); + updateJoysticks(); #endif m_gamepadTimer = new QTimer(this); @@ -146,9 +147,9 @@ const char* InputController::profileForType(uint32_t type) { #ifdef BUILD_SDL if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { #if SDL_VERSION_ATLEAST(2, 0, 0) - return SDL_JoystickName(m_sdlPlayer.joystick); + return SDL_JoystickName(m_sdlPlayer.joystick->joystick); #else - return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick)); + return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick)); #endif } #endif @@ -161,12 +162,12 @@ QStringList InputController::connectedGamepads(uint32_t type) const { #ifdef BUILD_SDL if (type == SDL_BINDING_BUTTON) { QStringList pads; - for (size_t i = 0; i < s_sdlEvents.nJoysticks; ++i) { + for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { const char* name; #if SDL_VERSION_ATLEAST(2, 0, 0) - name = SDL_JoystickName(s_sdlEvents.joysticks[i]); + name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick); #else - name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i])); + name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick)); #endif if (name) { pads.append(QString(name)); @@ -184,7 +185,7 @@ QStringList InputController::connectedGamepads(uint32_t type) const { int InputController::gamepad(uint32_t type) const { #ifdef BUILD_SDL if (type == SDL_BINDING_BUTTON) { - return m_sdlPlayer.joystickIndex; + return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0; } #endif return 0; @@ -282,11 +283,17 @@ void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) { return GBAInputBindKey(&m_inputMap, type, key, gbaKey); } +void InputController::updateJoysticks() { +#ifdef BUILD_SDL + GBASDLUpdateJoysticks(&s_sdlEvents); +#endif +} + int InputController::pollEvents() { int activeButtons = 0; #ifdef BUILD_SDL - if (m_playerAttached) { - SDL_Joystick* joystick = m_sdlPlayer.joystick; + if (m_playerAttached && m_sdlPlayer.joystick) { + SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; SDL_JoystickUpdate(); int numButtons = SDL_JoystickNumButtons(joystick); int i; @@ -336,8 +343,8 @@ int InputController::pollEvents() { QSet InputController::activeGamepadButtons(int type) { QSet activeButtons; #ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON) { - SDL_Joystick* joystick = m_sdlPlayer.joystick; + if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { + SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; SDL_JoystickUpdate(); int numButtons = SDL_JoystickNumButtons(joystick); int i; @@ -353,8 +360,8 @@ QSet InputController::activeGamepadButtons(int type) { void InputController::recalibrateAxes() { #ifdef BUILD_SDL - if (m_playerAttached) { - SDL_Joystick* joystick = m_sdlPlayer.joystick; + if (m_playerAttached && m_sdlPlayer.joystick) { + SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; SDL_JoystickUpdate(); int numAxes = SDL_JoystickNumAxes(joystick); if (numAxes < 1) { @@ -372,8 +379,8 @@ void InputController::recalibrateAxes() { QSet> InputController::activeGamepadAxes(int type) { QSet> activeAxes; #ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON) { - SDL_Joystick* joystick = m_sdlPlayer.joystick; + if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { + SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; SDL_JoystickUpdate(); int numAxes = SDL_JoystickNumAxes(joystick); if (numAxes < 1) { diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index f73524bad..d44078068 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -52,6 +52,7 @@ public: const GBAInputMap* map() const { return &m_inputMap; } + void updateJoysticks(); int pollEvents(); static const int32_t AXIS_THRESHOLD = 0x3000; diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 42b14b209..8ca7a617b 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -24,6 +24,8 @@ #define GYRO_STEPS 100 #define RUMBLE_PWM 20 +DEFINE_VECTOR(SDL_JoystickList, struct SDL_JoystickCombo); + #if SDL_VERSION_ATLEAST(2, 0, 0) static void _GBASDLSetRumble(struct GBARumble* rumble, int enable); #endif @@ -52,22 +54,9 @@ bool GBASDLInitEvents(struct GBASDLEvents* context) { SDL_JoystickEventState(SDL_ENABLE); int nJoysticks = SDL_NumJoysticks(); + SDL_JoystickListInit(&context->joysticks, nJoysticks); if (nJoysticks > 0) { - context->nJoysticks = nJoysticks; - context->joysticks = calloc(context->nJoysticks, sizeof(SDL_Joystick*)); -#if SDL_VERSION_ATLEAST(2, 0, 0) - context->haptic = calloc(context->nJoysticks, sizeof(SDL_Haptic*)); -#endif - size_t i; - for (i = 0; i < context->nJoysticks; ++i) { - context->joysticks[i] = SDL_JoystickOpen(i); -#if SDL_VERSION_ATLEAST(2, 0, 0) - context->haptic[i] = SDL_HapticOpenFromJoystick(context->joysticks[i]); -#endif - } - } else { - context->nJoysticks = 0; - context->joysticks = 0; + GBASDLUpdateJoysticks(context); } context->playersAttached = 0; @@ -75,7 +64,6 @@ bool GBASDLInitEvents(struct GBASDLEvents* context) { 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) @@ -88,13 +76,14 @@ bool GBASDLInitEvents(struct GBASDLEvents* context) { void GBASDLDeinitEvents(struct GBASDLEvents* context) { size_t i; - for (i = 0; i < context->nJoysticks; ++i) { + for (i = 0; i < SDL_JoystickListSize(&context->joysticks); ++i) { + struct SDL_JoystickCombo* joystick = SDL_JoystickListGetPointer(&context->joysticks, i); #if SDL_VERSION_ATLEAST(2, 0, 0) - SDL_HapticClose(context->haptic[i]); + SDL_HapticClose(joystick->haptic); #endif - SDL_JoystickClose(context->joysticks[i]); + SDL_JoystickClose(joystick->joystick); } - + SDL_JoystickListDeinit(&context->joysticks); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); } @@ -160,7 +149,6 @@ void GBASDLInitBindings(struct GBAInputMap* inputMap) { bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player) { player->joystick = 0; - player->joystickIndex = SIZE_MAX; if (events->playersAttached >= MAX_PLAYERS) { return false; @@ -187,15 +175,17 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player player->rotation.p = player; player->playerId = events->playersAttached; + events->players[player->playerId] = player; size_t firstUnclaimed = SIZE_MAX; + size_t index = SIZE_MAX; size_t i; - for (i = 0; i < events->nJoysticks; ++i) { + for (i = 0; i < SDL_JoystickListSize(&events->joysticks); ++i) { bool claimed = false; int p; for (p = 0; p < events->playersAttached; ++p) { - if (events->joysticksClaimed[p] == i) { + if (events->players[p]->joystick == SDL_JoystickListGetPointer(&events->joysticks, i)) { claimed = true; break; } @@ -210,28 +200,26 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player const char* joystickName; #if SDL_VERSION_ATLEAST(2, 0, 0) - joystickName = SDL_JoystickName(events->joysticks[i]); + joystickName = SDL_JoystickName(SDL_JoystickListGetPointer(&events->joysticks, i)->joystick); #else - joystickName = SDL_JoystickName(SDL_JoystickIndex(events->joysticks[i])); + joystickName = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&events->joysticks, i)->joystick)); #endif if (events->preferredJoysticks[player->playerId] && strcmp(events->preferredJoysticks[player->playerId], joystickName) == 0) { - player->joystickIndex = i; + index = i; break; } } - if (player->joystickIndex == SIZE_MAX && firstUnclaimed != SIZE_MAX) { - player->joystickIndex = firstUnclaimed; + if (index == SIZE_MAX && firstUnclaimed != SIZE_MAX) { + index = firstUnclaimed; } - if (player->joystickIndex != SIZE_MAX) { - player->joystick = events->joysticks[player->joystickIndex]; - events->joysticksClaimed[player->playerId] = player->joystickIndex; + if (index != SIZE_MAX) { + player->joystick = SDL_JoystickListGetPointer(&events->joysticks, index); #if SDL_VERSION_ATLEAST(2, 0, 0) - player->haptic = events->haptic[player->joystickIndex]; - if (player->haptic) { - SDL_HapticRumbleInit(player->haptic); + if (player->joystick->haptic) { + SDL_HapticRumbleInit(player->joystick->haptic); } #endif } @@ -241,7 +229,19 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player } void GBASDLDetachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player) { - events->joysticksClaimed[player->playerId] = SIZE_MAX; + if (player != events->players[player->playerId]) { + return; + } + int i; + for (i = player->playerId; i < events->playersAttached; ++i) { + if (i + 1 < MAX_PLAYERS) { + events->players[i] = events->players[i + 1]; + } + if (i < events->playersAttached - 1) { + events->players[i]->playerId = i; + } + } + --events->playersAttached; CircleBufferDeinit(&player->rotation.zHistory); } @@ -250,15 +250,15 @@ void GBASDLPlayerLoadConfig(struct GBASDLPlayer* context, const struct Configura if (context->joystick) { GBAInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config); #if SDL_VERSION_ATLEAST(2, 0, 0) - const char* name = SDL_JoystickName(context->joystick); + const char* name = SDL_JoystickName(context->joystick->joystick); #else - const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick)); + const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick->joystick)); #endif GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, name); const char* value; char* end; - int numAxes = SDL_JoystickNumAxes(context->joystick); + int numAxes = SDL_JoystickNumAxes(context->joystick->joystick); int axis; value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "tiltAxisX", name); if (value) { @@ -301,9 +301,9 @@ void GBASDLPlayerLoadConfig(struct GBASDLPlayer* context, const struct Configura void GBASDLPlayerSaveConfig(const struct GBASDLPlayer* context, struct Configuration* config) { if (context->joystick) { #if SDL_VERSION_ATLEAST(2, 0, 0) - const char* name = SDL_JoystickName(context->joystick); + const char* name = SDL_JoystickName(context->joystick->joystick); #else - const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick)); + const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick->joystick)); #endif char value[12]; snprintf(value, sizeof(value), "%i", context->rotation.axisX); @@ -320,14 +320,54 @@ void GBASDLPlayerSaveConfig(const struct GBASDLPlayer* context, struct Configura } void GBASDLPlayerChangeJoystick(struct GBASDLEvents* events, struct GBASDLPlayer* player, size_t index) { - if (player->playerId >= MAX_PLAYERS || index >= events->nJoysticks) { + if (player->playerId >= MAX_PLAYERS || index >= SDL_JoystickListSize(&events->joysticks)) { return; } - events->joysticksClaimed[player->playerId] = index; - player->joystickIndex = index; - player->joystick = events->joysticks[index]; + player->joystick = SDL_JoystickListGetPointer(&events->joysticks, index); +} + +void GBASDLUpdateJoysticks(struct GBASDLEvents* events) { + // Pump SDL joystick events without eating the rest of the events + SDL_JoystickUpdate(); #if SDL_VERSION_ATLEAST(2, 0, 0) - player->haptic = events->haptic[index]; + SDL_Event event; + while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED) > 0) { + if (event.type == SDL_JOYDEVICEADDED) { + struct SDL_JoystickCombo* joystick = SDL_JoystickListAppend(&events->joysticks); + joystick->joystick = SDL_JoystickOpen(event.jdevice.which); + joystick->id = SDL_JoystickInstanceID(joystick->joystick); + joystick->index = SDL_JoystickListSize(&events->joysticks) - 1; +#if SDL_VERSION_ATLEAST(2, 0, 0) + joystick->haptic = SDL_HapticOpenFromJoystick(joystick->joystick); +#endif + } else if (event.type == SDL_JOYDEVICEREMOVED) { + SDL_JoystickID ids[MAX_PLAYERS]; + size_t i; + for (i = 0; (int) i < events->playersAttached; ++i) { + if (events->players[i]->joystick) { + ids[i] = events->players[i]->joystick->id; + events->players[i]->joystick = 0; + } else { + ids[i] = -1; + } + } + for (i = 0; i < SDL_JoystickListSize(&events->joysticks);) { + struct SDL_JoystickCombo* joystick = SDL_JoystickListGetPointer(&events->joysticks, i); + if (joystick->id == event.jdevice.which) { + SDL_JoystickListShift(&events->joysticks, i, 1); + continue; + } + SDL_JoystickListGetPointer(&events->joysticks, i)->index = i; + int p; + for (p = 0; p < events->playersAttached; ++p) { + if (joystick->id == ids[p]) { + events->players[p]->joystick = SDL_JoystickListGetPointer(&events->joysticks, i); + } + } + ++i; + } + } + } #endif } @@ -540,7 +580,7 @@ void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLPlayer* sdlContex #if SDL_VERSION_ATLEAST(2, 0, 0) static void _GBASDLSetRumble(struct GBARumble* rumble, int enable) { struct GBASDLRumble* sdlRumble = (struct GBASDLRumble*) rumble; - if (!sdlRumble->p->haptic || !SDL_HapticRumbleSupported(sdlRumble->p->haptic)) { + if (!sdlRumble->p->joystick->haptic || !SDL_HapticRumbleSupported(sdlRumble->p->joystick->haptic)) { return; } sdlRumble->level += enable; @@ -551,15 +591,15 @@ static void _GBASDLSetRumble(struct GBARumble* rumble, int enable) { } CircleBufferWrite8(&sdlRumble->history, enable); if (sdlRumble->level) { - SDL_HapticRumblePlay(sdlRumble->p->haptic, sdlRumble->level / (float) RUMBLE_PWM, 20); + SDL_HapticRumblePlay(sdlRumble->p->joystick->haptic, sdlRumble->level / (float) RUMBLE_PWM, 20); } else { - SDL_HapticRumbleStop(sdlRumble->p->haptic); + SDL_HapticRumbleStop(sdlRumble->p->joystick->haptic); } } #endif static int32_t _readTilt(struct GBASDLPlayer* player, int axis) { - return SDL_JoystickGetAxis(player->joystick, axis) * 0x3800; + return SDL_JoystickGetAxis(player->joystick->joystick, axis) * 0x3800; } static int32_t _GBASDLReadTiltX(struct GBARotationSource* source) { @@ -582,8 +622,8 @@ static void _GBASDLRotationSample(struct GBARotationSource* source) { struct GBASDLRotation* rotation = (struct GBASDLRotation*) source; SDL_JoystickUpdate(); - int x = SDL_JoystickGetAxis(rotation->p->joystick, rotation->gyroX); - int y = SDL_JoystickGetAxis(rotation->p->joystick, rotation->gyroY); + int x = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroX); + int y = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroY); union { float f; int32_t i; diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index eceb9c532..015868f2e 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -8,6 +8,7 @@ #include "util/common.h" #include "util/circle-buffer.h" +#include "util/vector.h" #include "gba/supervisor/thread.h" @@ -21,14 +22,25 @@ struct GBAVideoSoftwareRenderer; struct Configuration; +struct SDL_JoystickCombo { + SDL_JoystickID id; + size_t index; + SDL_Joystick* joystick; +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_Haptic* haptic; +#endif +}; + +DECLARE_VECTOR(SDL_JoystickList, struct SDL_JoystickCombo); + +struct GBASDLPlayer; + struct GBASDLEvents { - SDL_Joystick** joysticks; - size_t nJoysticks; + struct SDL_JoystickList joysticks; const char* preferredJoysticks[MAX_PLAYERS]; int playersAttached; - size_t joysticksClaimed[MAX_PLAYERS]; + struct GBASDLPlayer* players[MAX_PLAYERS]; #if SDL_VERSION_ATLEAST(2, 0, 0) - SDL_Haptic** haptic; int screensaverSuspendDepth; bool screensaverSuspendable; #endif @@ -37,13 +49,11 @@ struct GBASDLEvents { struct GBASDLPlayer { size_t playerId; struct GBAInputMap* bindings; - SDL_Joystick* joystick; - size_t joystickIndex; + struct SDL_JoystickCombo* joystick; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Window* window; int fullscreen; int windowUpdated; - SDL_Haptic* haptic; struct GBASDLRumble { struct GBARumble d; @@ -80,6 +90,7 @@ bool GBASDLAttachPlayer(struct GBASDLEvents*, struct GBASDLPlayer*); void GBASDLDetachPlayer(struct GBASDLEvents*, struct GBASDLPlayer*); void GBASDLEventsLoadConfig(struct GBASDLEvents*, const struct Configuration*); void GBASDLPlayerChangeJoystick(struct GBASDLEvents*, struct GBASDLPlayer*, size_t index); +void GBASDLUpdateJoysticks(struct GBASDLEvents* events); void GBASDLInitBindings(struct GBAInputMap* inputMap); void GBASDLPlayerLoadConfig(struct GBASDLPlayer*, const struct Configuration*);