From afae3c8b807577123c93f32f1145422fc55500af Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 21 Mar 2015 17:09:15 -0700 Subject: [PATCH] Qt: Allow configuring arbitrary attached gamepads (fixes #204) --- src/gba/input.c | 18 +++++++++ src/gba/input.h | 3 ++ src/platform/qt/GBAKeyEditor.cpp | 57 +++++++++++++++++++++++------ src/platform/qt/GBAKeyEditor.h | 7 +++- src/platform/qt/InputController.cpp | 40 ++++++++++++++++++-- src/platform/qt/InputController.h | 9 ++++- src/platform/sdl/sdl-events.c | 25 ++++++++----- src/platform/sdl/sdl-events.h | 1 + 8 files changed, 131 insertions(+), 29 deletions(-) diff --git a/src/gba/input.c b/src/gba/input.c index 75451d16b..fbaf8c3e0 100644 --- a/src/gba/input.c +++ b/src/gba/input.c @@ -482,3 +482,21 @@ void GBAInputProfileSave(const struct GBAInputMap* map, uint32_t type, struct Co sectionName[SECTION_NAME_MAX - 1] = '\0'; _saveAll(map, type, sectionName, config); } + +const char* GBAInputGetPreferredDevice(const struct Configuration* config, uint32_t type, int playerId) { + char sectionName[SECTION_NAME_MAX]; + _makeSectionName(sectionName, SECTION_NAME_MAX, type); + + char deviceId[KEY_NAME_MAX]; + snprintf(deviceId, sizeof(deviceId), "device%i", playerId); + return ConfigurationGetValue(config, sectionName, deviceId); +} + +void GBAInputSetPreferredDevice(struct Configuration* config, uint32_t type, int playerId, const char* deviceName) { + char sectionName[SECTION_NAME_MAX]; + _makeSectionName(sectionName, SECTION_NAME_MAX, type); + + char deviceId[KEY_NAME_MAX]; + snprintf(deviceId, sizeof(deviceId), "device%i", playerId); + return ConfigurationSetValue(config, sectionName, deviceId, deviceName); +} diff --git a/src/gba/input.h b/src/gba/input.h index 0cd2bb1bc..c14c0652a 100644 --- a/src/gba/input.h +++ b/src/gba/input.h @@ -48,4 +48,7 @@ void GBAInputMapSave(const struct GBAInputMap*, uint32_t type, struct Configurat void GBAInputProfileLoad(struct GBAInputMap*, uint32_t type, const struct Configuration*, const char* profile); void GBAInputProfileSave(const struct GBAInputMap*, uint32_t type, struct Configuration*, const char* profile); +const char* GBAInputGetPreferredDevice(const struct Configuration*, uint32_t type, int playerId); +void GBAInputSetPreferredDevice(struct Configuration*, uint32_t type, int playerId, const char* deviceName); + #endif diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index beb499f44..3a0c31257 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GBAKeyEditor.h" +#include #include #include #include @@ -20,8 +21,9 @@ const qreal GBAKeyEditor::DPAD_CENTER_Y = 0.431; const qreal GBAKeyEditor::DPAD_WIDTH = 0.1; const qreal GBAKeyEditor::DPAD_HEIGHT = 0.1; -GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const char* profile, QWidget* parent) +GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& profile, QWidget* parent) : QWidget(parent) + , m_profileSelect(nullptr) , m_type(type) , m_profile(profile) , m_controller(controller) @@ -42,19 +44,26 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const char* pr m_keyL = new KeyEditor(this); m_keyR = new KeyEditor(this); - lookupBinding(map, m_keyDU, GBA_KEY_UP); - lookupBinding(map, m_keyDD, GBA_KEY_DOWN); - lookupBinding(map, m_keyDL, GBA_KEY_LEFT); - lookupBinding(map, m_keyDR, GBA_KEY_RIGHT); - lookupBinding(map, m_keySelect, GBA_KEY_SELECT); - lookupBinding(map, m_keyStart, GBA_KEY_START); - lookupBinding(map, m_keyA, GBA_KEY_A); - lookupBinding(map, m_keyB, GBA_KEY_B); - lookupBinding(map, m_keyL, GBA_KEY_L); - lookupBinding(map, m_keyR, GBA_KEY_R); + refresh(); #ifdef BUILD_SDL lookupAxes(map); + + if (type == SDL_BINDING_BUTTON) { + m_profileSelect = new QComboBox(this); + m_profileSelect->addItems(controller->connectedGamepads(type)); + int activeGamepad = controller->gamepad(type); + 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(); + }); + } #endif connect(m_keyDU, SIGNAL(valueChanged(int)), this, SLOT(setNext())); @@ -129,6 +138,10 @@ void GBAKeyEditor::resizeEvent(QResizeEvent* event) { setLocation(m_keyB, 0.667, 0.490); setLocation(m_keyL, 0.1, 0.1); setLocation(m_keyR, 0.9, 0.1); + + if (m_profileSelect) { + setLocation(m_profileSelect, 0.5, 0.7); + } } void GBAKeyEditor::paintEvent(QPaintEvent* event) { @@ -165,11 +178,31 @@ void GBAKeyEditor::save() { bindKey(m_keyR, GBA_KEY_R); m_controller->saveConfiguration(m_type); - if (m_profile) { +#ifdef BUILD_SDL + if (m_profileSelect) { + m_controller->setPreferredGamepad(m_type, m_profileSelect->currentText()); + } +#endif + + if (!m_profile.isNull()) { m_controller->saveProfile(m_type, m_profile); } } +void GBAKeyEditor::refresh() { + const GBAInputMap* map = m_controller->map(); + lookupBinding(map, m_keyDU, GBA_KEY_UP); + lookupBinding(map, m_keyDD, GBA_KEY_DOWN); + lookupBinding(map, m_keyDL, GBA_KEY_LEFT); + lookupBinding(map, m_keyDR, GBA_KEY_RIGHT); + lookupBinding(map, m_keySelect, GBA_KEY_SELECT); + lookupBinding(map, m_keyStart, GBA_KEY_START); + lookupBinding(map, m_keyA, GBA_KEY_A); + lookupBinding(map, m_keyB, GBA_KEY_B); + lookupBinding(map, m_keyL, GBA_KEY_L); + lookupBinding(map, m_keyR, GBA_KEY_R); +} + void GBAKeyEditor::lookupBinding(const GBAInputMap* map, KeyEditor* keyEditor, GBAKey key) { #ifdef BUILD_SDL if (m_type == SDL_BINDING_BUTTON) { diff --git a/src/platform/qt/GBAKeyEditor.h b/src/platform/qt/GBAKeyEditor.h index cb7cc4c01..9496ffffe 100644 --- a/src/platform/qt/GBAKeyEditor.h +++ b/src/platform/qt/GBAKeyEditor.h @@ -15,6 +15,7 @@ extern "C" { #include "gba/input.h" } +class QComboBox; class QTimer; namespace QGBA { @@ -26,7 +27,7 @@ class GBAKeyEditor : public QWidget { Q_OBJECT public: - GBAKeyEditor(InputController* controller, int type, const char* profile = nullptr, QWidget* parent = nullptr); + GBAKeyEditor(InputController* controller, int type, const QString& profile = QString(), QWidget* parent = nullptr); public slots: void setAll(); @@ -38,6 +39,7 @@ protected: private slots: void setNext(); void save(); + void refresh(); #ifdef BUILD_SDL void setAxisValue(int axis, int32_t value); #endif @@ -61,6 +63,7 @@ private: KeyEditor* keyById(GBAKey); + QComboBox* m_profileSelect; QWidget* m_buttons; KeyEditor* m_keyDU; KeyEditor* m_keyDD; @@ -76,7 +79,7 @@ private: QList::iterator m_currentKey; uint32_t m_type; - const char* m_profile; + QString m_profile; InputController* m_controller; QPicture m_background; diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 7228f5952..9c9920929 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -85,8 +85,8 @@ void InputController::loadConfiguration(uint32_t type) { GBAInputMapLoad(&m_inputMap, type, m_config->input()); } -void InputController::loadProfile(uint32_t type, const char* profile) { - GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile); +void InputController::loadProfile(uint32_t type, const QString& profile) { + GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData()); } void InputController::saveConfiguration(uint32_t type) { @@ -94,8 +94,8 @@ void InputController::saveConfiguration(uint32_t type) { m_config->write(); } -void InputController::saveProfile(uint32_t type, const char* profile) { - GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile); +void InputController::saveProfile(uint32_t type, const QString& profile) { + GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData()); m_config->write(); } @@ -113,6 +113,38 @@ const char* InputController::profileForType(uint32_t type) { return 0; } +#ifdef BUILD_SDL +QStringList InputController::connectedGamepads(uint32_t type) const { + UNUSED(type); + if (type != SDL_BINDING_BUTTON) { + return QStringList(); + } + + QStringList pads; + for (size_t i = 0; i < s_sdlEvents.nJoysticks; ++i) { + const char* name; +#if SDL_VERSION_ATLEAST(2, 0, 0) + name = SDL_JoystickName(s_sdlEvents.joysticks[i]); +#else + name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i])); +#endif + if (name) { + pads.append(QString(name)); + } else { + pads.append(QString()); + } + } + return pads; +} + +void InputController::setPreferredGamepad(uint32_t type, const QString& device) { + if (!m_config) { + return; + } + GBAInputSetPreferredDevice(m_config->input(), type, m_sdlPlayer.playerId, device.toLocal8Bit().constData()); +} +#endif + GBAKey InputController::mapKeyboard(int key) const { return GBAInputMapKey(&m_inputMap, KEYBOARD, key); } diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index be86d305b..497db331c 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -36,9 +36,9 @@ public: void setConfiguration(ConfigController* config); void loadConfiguration(uint32_t type); - void loadProfile(uint32_t type, const char* profile); + void loadProfile(uint32_t type, const QString& profile); void saveConfiguration(uint32_t type = KEYBOARD); - void saveProfile(uint32_t type, const char* profile); + void saveProfile(uint32_t type, const QString& profile); const char* profileForType(uint32_t type); GBAKey mapKeyboard(int key) const; @@ -55,6 +55,11 @@ public: QSet> activeGamepadAxes(); void bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction, GBAKey); + + QStringList connectedGamepads(uint32_t type) const; + int gamepad(uint32_t type) const { return m_sdlPlayer.joystickIndex; } + void setGamepad(uint32_t type, int index) { GBASDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index); } + void setPreferredGamepad(uint32_t type, const QString& device); #endif public slots: diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 21408a1c5..6e9ac8d49 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -63,12 +63,10 @@ void GBASDLDeinitEvents(struct GBASDLEvents* context) { } 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"); + context->preferredJoysticks[0] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 0); + context->preferredJoysticks[1] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 1); + context->preferredJoysticks[2] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 2); + context->preferredJoysticks[3] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 3); } void GBASDLInitBindings(struct GBAInputMap* inputMap) { @@ -132,7 +130,7 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player return false; } - int playerId = events->playersAttached; + player->playerId = events->playersAttached; size_t firstUnclaimed = SIZE_MAX; size_t i; @@ -160,7 +158,7 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player #else joystickName = SDL_JoystickName(SDL_JoystickIndex(events->joysticks[i])); #endif - if (events->preferredJoysticks[playerId] && strcmp(events->preferredJoysticks[playerId], joystickName) == 0) { + if (events->preferredJoysticks[player->playerId] && strcmp(events->preferredJoysticks[player->playerId], joystickName) == 0) { player->joystickIndex = i; break; } @@ -172,7 +170,7 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player if (player->joystickIndex != SIZE_MAX) { player->joystick = events->joysticks[player->joystickIndex]; - events->joysticksClaimed[playerId] = player->joystickIndex; + events->joysticksClaimed[player->playerId] = player->joystickIndex; } ++events->playersAttached; @@ -191,6 +189,15 @@ void GBASDLPlayerLoadConfig(struct GBASDLPlayer* context, const struct Configura } } +void GBASDLPlayerChangeJoystick(struct GBASDLEvents* events, struct GBASDLPlayer* player, size_t index) { + if (player->playerId > MAX_PLAYERS || index >= events->nJoysticks) { + return; + } + events->joysticksClaimed[player->playerId] = index; + player->joystickIndex = index; + player->joystick = events->joysticks[index]; +} + static void _pauseAfterFrame(struct GBAThread* context) { context->frameCallback = 0; GBAThreadPauseFromThread(context); diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index b72964399..69e095e9d 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -29,6 +29,7 @@ struct GBASDLEvents { }; struct GBASDLPlayer { + size_t playerId; struct GBAInputMap* bindings; SDL_Joystick* joystick; size_t joystickIndex;