Qt: Allow configuring arbitrary attached gamepads (fixes #204)
@@ -482,3 +482,21 @@ snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
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); +}
@@ -48,4 +48,7 @@
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
@@ -5,6 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GBAKeyEditor.h" +#include <QComboBox> #include <QPaintEvent> #include <QPainter> #include <QPushButton>@@ -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 @@ m_keyB = new KeyEditor(this);
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<void (QComboBox::*)(int)>(&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 @@ setLocation(m_keyA, 0.826, 0.451);
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,9 +178,29 @@ bindKey(m_keyL, GBA_KEY_L);
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) {
@@ -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 @@
private slots: void setNext(); void save(); + void refresh(); #ifdef BUILD_SDL void setAxisValue(int axis, int32_t value); #endif@@ -61,6 +63,7 @@ #endif
KeyEditor* keyById(GBAKey); + QComboBox* m_profileSelect; QWidget* m_buttons; KeyEditor* m_keyDU; KeyEditor* m_keyDD;@@ -76,7 +79,7 @@ QList<KeyEditor*> m_keyOrder;
QList<KeyEditor*>::iterator m_currentKey; uint32_t m_type; - const char* m_profile; + QString m_profile; InputController* m_controller; QPicture m_background;
@@ -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 @@ 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->input(), profile); +void InputController::saveProfile(uint32_t type, const QString& profile) { + GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData()); m_config->write(); }@@ -112,6 +112,38 @@ }
#endif 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);
@@ -36,9 +36,9 @@ ~InputController();
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 @@ QSet<int> activeGamepadButtons();
QSet<QPair<int, GamepadAxisEvent::Direction>> 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:
@@ -63,12 +63,10 @@ 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"); + 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 @@ if (events->playersAttached >= MAX_PLAYERS) {
return false; } - int playerId = events->playersAttached; + player->playerId = events->playersAttached; size_t firstUnclaimed = SIZE_MAX; size_t i;@@ -160,7 +158,7 @@ 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) { + if (events->preferredJoysticks[player->playerId] && strcmp(events->preferredJoysticks[player->playerId], joystickName) == 0) { player->joystickIndex = i; break; }@@ -172,7 +170,7 @@ }
if (player->joystickIndex != SIZE_MAX) { player->joystick = events->joysticks[player->joystickIndex]; - events->joysticksClaimed[playerId] = player->joystickIndex; + events->joysticksClaimed[player->playerId] = player->joystickIndex; } ++events->playersAttached;@@ -189,6 +187,15 @@ #else
GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, SDL_JoystickName(SDL_JoystickIndex(context->joystick))); #endif } +} + +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) {
@@ -29,6 +29,7 @@ size_t joysticksClaimed[MAX_PLAYERS];
}; struct GBASDLPlayer { + size_t playerId; struct GBAInputMap* bindings; SDL_Joystick* joystick; size_t joystickIndex;