mirror of https://github.com/mgba-emu/mgba.git
Qt: Allow configuring arbitrary attached gamepads (fixes #204)
This commit is contained in:
parent
d4ef56cd16
commit
afae3c8b80
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -29,6 +29,7 @@ struct GBASDLEvents {
|
|||
};
|
||||
|
||||
struct GBASDLPlayer {
|
||||
size_t playerId;
|
||||
struct GBAInputMap* bindings;
|
||||
SDL_Joystick* joystick;
|
||||
size_t joystickIndex;
|
||||
|
|
Loading…
Reference in New Issue