Qt: Allow configuring arbitrary attached gamepads (fixes #204)

This commit is contained in:
Jeffrey Pfau 2015-03-21 17:09:15 -07:00
parent d4ef56cd16
commit afae3c8b80
8 changed files with 131 additions and 29 deletions

View File

@ -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);
}

View File

@ -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

View File

@ -5,6 +5,7 @@
* 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 @@ 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<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 @@ 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) {

View File

@ -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<KeyEditor*>::iterator m_currentKey;
uint32_t m_type;
const char* m_profile;
QString m_profile;
InputController* m_controller;
QPicture m_background;

View File

@ -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);
}

View File

@ -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<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:

View File

@ -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);

View File

@ -29,6 +29,7 @@ struct GBASDLEvents {
};
struct GBASDLPlayer {
size_t playerId;
struct GBAInputMap* bindings;
SDL_Joystick* joystick;
size_t joystickIndex;