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';
|
sectionName[SECTION_NAME_MAX - 1] = '\0';
|
||||||
_saveAll(map, type, sectionName, config);
|
_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 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);
|
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
|
#endif
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "GBAKeyEditor.h"
|
#include "GBAKeyEditor.h"
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
#include <QPaintEvent>
|
#include <QPaintEvent>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QPushButton>
|
#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_WIDTH = 0.1;
|
||||||
const qreal GBAKeyEditor::DPAD_HEIGHT = 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)
|
: QWidget(parent)
|
||||||
|
, m_profileSelect(nullptr)
|
||||||
, m_type(type)
|
, m_type(type)
|
||||||
, m_profile(profile)
|
, m_profile(profile)
|
||||||
, m_controller(controller)
|
, m_controller(controller)
|
||||||
|
@ -42,19 +44,26 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const char* pr
|
||||||
m_keyL = new KeyEditor(this);
|
m_keyL = new KeyEditor(this);
|
||||||
m_keyR = new KeyEditor(this);
|
m_keyR = new KeyEditor(this);
|
||||||
|
|
||||||
lookupBinding(map, m_keyDU, GBA_KEY_UP);
|
refresh();
|
||||||
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);
|
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
lookupAxes(map);
|
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
|
#endif
|
||||||
|
|
||||||
connect(m_keyDU, SIGNAL(valueChanged(int)), this, SLOT(setNext()));
|
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_keyB, 0.667, 0.490);
|
||||||
setLocation(m_keyL, 0.1, 0.1);
|
setLocation(m_keyL, 0.1, 0.1);
|
||||||
setLocation(m_keyR, 0.9, 0.1);
|
setLocation(m_keyR, 0.9, 0.1);
|
||||||
|
|
||||||
|
if (m_profileSelect) {
|
||||||
|
setLocation(m_profileSelect, 0.5, 0.7);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAKeyEditor::paintEvent(QPaintEvent* event) {
|
void GBAKeyEditor::paintEvent(QPaintEvent* event) {
|
||||||
|
@ -165,11 +178,31 @@ void GBAKeyEditor::save() {
|
||||||
bindKey(m_keyR, GBA_KEY_R);
|
bindKey(m_keyR, GBA_KEY_R);
|
||||||
m_controller->saveConfiguration(m_type);
|
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);
|
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) {
|
void GBAKeyEditor::lookupBinding(const GBAInputMap* map, KeyEditor* keyEditor, GBAKey key) {
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
if (m_type == SDL_BINDING_BUTTON) {
|
if (m_type == SDL_BINDING_BUTTON) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ extern "C" {
|
||||||
#include "gba/input.h"
|
#include "gba/input.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class QComboBox;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
@ -26,7 +27,7 @@ class GBAKeyEditor : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
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:
|
public slots:
|
||||||
void setAll();
|
void setAll();
|
||||||
|
@ -38,6 +39,7 @@ protected:
|
||||||
private slots:
|
private slots:
|
||||||
void setNext();
|
void setNext();
|
||||||
void save();
|
void save();
|
||||||
|
void refresh();
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
void setAxisValue(int axis, int32_t value);
|
void setAxisValue(int axis, int32_t value);
|
||||||
#endif
|
#endif
|
||||||
|
@ -61,6 +63,7 @@ private:
|
||||||
|
|
||||||
KeyEditor* keyById(GBAKey);
|
KeyEditor* keyById(GBAKey);
|
||||||
|
|
||||||
|
QComboBox* m_profileSelect;
|
||||||
QWidget* m_buttons;
|
QWidget* m_buttons;
|
||||||
KeyEditor* m_keyDU;
|
KeyEditor* m_keyDU;
|
||||||
KeyEditor* m_keyDD;
|
KeyEditor* m_keyDD;
|
||||||
|
@ -76,7 +79,7 @@ private:
|
||||||
QList<KeyEditor*>::iterator m_currentKey;
|
QList<KeyEditor*>::iterator m_currentKey;
|
||||||
|
|
||||||
uint32_t m_type;
|
uint32_t m_type;
|
||||||
const char* m_profile;
|
QString m_profile;
|
||||||
InputController* m_controller;
|
InputController* m_controller;
|
||||||
|
|
||||||
QPicture m_background;
|
QPicture m_background;
|
||||||
|
|
|
@ -85,8 +85,8 @@ void InputController::loadConfiguration(uint32_t type) {
|
||||||
GBAInputMapLoad(&m_inputMap, type, m_config->input());
|
GBAInputMapLoad(&m_inputMap, type, m_config->input());
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputController::loadProfile(uint32_t type, const char* profile) {
|
void InputController::loadProfile(uint32_t type, const QString& profile) {
|
||||||
GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile);
|
GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputController::saveConfiguration(uint32_t type) {
|
void InputController::saveConfiguration(uint32_t type) {
|
||||||
|
@ -94,8 +94,8 @@ void InputController::saveConfiguration(uint32_t type) {
|
||||||
m_config->write();
|
m_config->write();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputController::saveProfile(uint32_t type, const char* profile) {
|
void InputController::saveProfile(uint32_t type, const QString& profile) {
|
||||||
GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile);
|
GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
|
||||||
m_config->write();
|
m_config->write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +113,38 @@ const char* InputController::profileForType(uint32_t type) {
|
||||||
return 0;
|
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 {
|
GBAKey InputController::mapKeyboard(int key) const {
|
||||||
return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
|
return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,9 @@ public:
|
||||||
|
|
||||||
void setConfiguration(ConfigController* config);
|
void setConfiguration(ConfigController* config);
|
||||||
void loadConfiguration(uint32_t type);
|
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 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);
|
const char* profileForType(uint32_t type);
|
||||||
|
|
||||||
GBAKey mapKeyboard(int key) const;
|
GBAKey mapKeyboard(int key) const;
|
||||||
|
@ -55,6 +55,11 @@ public:
|
||||||
QSet<QPair<int, GamepadAxisEvent::Direction>> activeGamepadAxes();
|
QSet<QPair<int, GamepadAxisEvent::Direction>> activeGamepadAxes();
|
||||||
|
|
||||||
void bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction, GBAKey);
|
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
|
#endif
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
|
@ -63,12 +63,10 @@ void GBASDLDeinitEvents(struct GBASDLEvents* context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBASDLEventsLoadConfig(struct GBASDLEvents* context, const struct Configuration* config) {
|
void GBASDLEventsLoadConfig(struct GBASDLEvents* context, const struct Configuration* config) {
|
||||||
char sectionName[16];
|
context->preferredJoysticks[0] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 0);
|
||||||
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[1] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 1);
|
||||||
context->preferredJoysticks[0] = ConfigurationGetValue(config, sectionName, "device0");
|
context->preferredJoysticks[2] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 2);
|
||||||
context->preferredJoysticks[1] = ConfigurationGetValue(config, sectionName, "device1");
|
context->preferredJoysticks[3] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 3);
|
||||||
context->preferredJoysticks[2] = ConfigurationGetValue(config, sectionName, "device2");
|
|
||||||
context->preferredJoysticks[3] = ConfigurationGetValue(config, sectionName, "device3");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBASDLInitBindings(struct GBAInputMap* inputMap) {
|
void GBASDLInitBindings(struct GBAInputMap* inputMap) {
|
||||||
|
@ -132,7 +130,7 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int playerId = events->playersAttached;
|
player->playerId = events->playersAttached;
|
||||||
size_t firstUnclaimed = SIZE_MAX;
|
size_t firstUnclaimed = SIZE_MAX;
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
@ -160,7 +158,7 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player
|
||||||
#else
|
#else
|
||||||
joystickName = SDL_JoystickName(SDL_JoystickIndex(events->joysticks[i]));
|
joystickName = SDL_JoystickName(SDL_JoystickIndex(events->joysticks[i]));
|
||||||
#endif
|
#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;
|
player->joystickIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -172,7 +170,7 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player
|
||||||
|
|
||||||
if (player->joystickIndex != SIZE_MAX) {
|
if (player->joystickIndex != SIZE_MAX) {
|
||||||
player->joystick = events->joysticks[player->joystickIndex];
|
player->joystick = events->joysticks[player->joystickIndex];
|
||||||
events->joysticksClaimed[playerId] = player->joystickIndex;
|
events->joysticksClaimed[player->playerId] = player->joystickIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
++events->playersAttached;
|
++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) {
|
static void _pauseAfterFrame(struct GBAThread* context) {
|
||||||
context->frameCallback = 0;
|
context->frameCallback = 0;
|
||||||
GBAThreadPauseFromThread(context);
|
GBAThreadPauseFromThread(context);
|
||||||
|
|
|
@ -29,6 +29,7 @@ struct GBASDLEvents {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBASDLPlayer {
|
struct GBASDLPlayer {
|
||||||
|
size_t playerId;
|
||||||
struct GBAInputMap* bindings;
|
struct GBAInputMap* bindings;
|
||||||
SDL_Joystick* joystick;
|
SDL_Joystick* joystick;
|
||||||
size_t joystickIndex;
|
size_t joystickIndex;
|
||||||
|
|
Loading…
Reference in New Issue