all repos — mgba @ 412aa147ea8de3d58b6dbd4990c0223c89362cac

mGBA Game Boy Advance Emulator

Qt: Axis mapping
Jeffrey Pfau jeffrey@endrift.com
Sun, 14 Dec 2014 21:11:22 -0800
commit

412aa147ea8de3d58b6dbd4990c0223c89362cac

parent

1c1fbfe163964cb9a6744b134bda71464dd232f4

M src/gba/gba-input.csrc/gba/gba-input.c

@@ -27,6 +27,11 @@ struct Configuration* config;

uint32_t type; }; +struct GBAAxisEnumerate { + void (*handler)(int axis, const struct GBAAxis* description, void* user); + void* user; +}; + const char* GBAKeyNames[] = { "A", "B",

@@ -233,6 +238,12 @@ ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);

} } +void _enumerateAxis(uint32_t axis, void* dp, void* ep) { + struct GBAAxisEnumerate* enumUser = ep; + const struct GBAAxis* description = dp; + enumUser->handler(axis, description, enumUser->user); +} + void GBAInputMapInit(struct GBAInputMap* map) { map->maps = 0; map->numMaps = 0;

@@ -274,7 +285,7 @@

void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) { struct GBAInputMapImpl* impl = _lookupMap(map, type); if (impl) { - impl->map[input] = -1; + impl->map[input] = GBA_NO_MAPPING; } }

@@ -354,6 +365,18 @@ if (!impl) {

return 0; } return TableLookup(&impl->axes, axis); +} + +void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) { + const struct GBAInputMapImpl* impl = _lookupMapConst(map, type); + if (!impl) { + return; + } + struct GBAAxisEnumerate enumUser = { + handler, + user + }; + TableEnumerate(&impl->axes, _enumerateAxis, &enumUser); } void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
M src/gba/gba-input.hsrc/gba/gba-input.h

@@ -22,6 +22,8 @@ int32_t deadHigh;

int32_t deadLow; }; +#define GBA_NO_MAPPING -1 + extern const char* GBAKeyNames[]; void GBAInputMapInit(struct GBAInputMap*);

@@ -38,6 +40,7 @@ void GBAInputBindAxis(struct GBAInputMap*, uint32_t type, int axis, const struct GBAAxis* description);

void GBAInputUnbindAxis(struct GBAInputMap*, uint32_t type, int axis); void GBAInputUnbindAllAxes(struct GBAInputMap*, uint32_t type); const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap*, uint32_t type, int axis); +void GBAInputEnumerateAxes(const struct GBAInputMap*, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user); void GBAInputMapLoad(struct GBAInputMap*, uint32_t type, const struct Configuration*); void GBAInputMapSave(const struct GBAInputMap*, uint32_t type, struct Configuration*);
M src/platform/qt/GBAKeyEditor.cppsrc/platform/qt/GBAKeyEditor.cpp

@@ -14,10 +14,6 @@

#include "InputController.h" #include "KeyEditor.h" -extern "C" { -#include "gba-input.h" -} - using namespace QGBA; const qreal GBAKeyEditor::DPAD_CENTER_X = 0.247;

@@ -46,32 +42,21 @@ 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); + #ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - m_keyDU->setNumeric(true); - m_keyDD->setNumeric(true); - m_keyDL->setNumeric(true); - m_keyDR->setNumeric(true); - m_keySelect->setNumeric(true); - m_keyStart->setNumeric(true); - m_keyA->setNumeric(true); - m_keyB->setNumeric(true); - m_keyL->setNumeric(true); - m_keyR->setNumeric(true); - } + lookupAxes(map); #endif - m_keyDU->setValue(GBAInputQueryBinding(map, type, GBA_KEY_UP)); - m_keyDD->setValue(GBAInputQueryBinding(map, type, GBA_KEY_DOWN)); - m_keyDL->setValue(GBAInputQueryBinding(map, type, GBA_KEY_LEFT)); - m_keyDR->setValue(GBAInputQueryBinding(map, type, GBA_KEY_RIGHT)); - m_keySelect->setValue(GBAInputQueryBinding(map, type, GBA_KEY_SELECT)); - m_keyStart->setValue(GBAInputQueryBinding(map, type, GBA_KEY_START)); - m_keyA->setValue(GBAInputQueryBinding(map, type, GBA_KEY_A)); - m_keyB->setValue(GBAInputQueryBinding(map, type, GBA_KEY_B)); - m_keyL->setValue(GBAInputQueryBinding(map, type, GBA_KEY_L)); - m_keyR->setValue(GBAInputQueryBinding(map, type, GBA_KEY_R)); - connect(m_keyDU, SIGNAL(valueChanged(int)), this, SLOT(setNext())); connect(m_keyDD, SIGNAL(valueChanged(int)), this, SLOT(setNext())); connect(m_keyDL, SIGNAL(valueChanged(int)), this, SLOT(setNext()));

@@ -83,6 +68,17 @@ connect(m_keyB, SIGNAL(valueChanged(int)), this, SLOT(setNext()));

connect(m_keyL, SIGNAL(valueChanged(int)), this, SLOT(setNext())); connect(m_keyR, SIGNAL(valueChanged(int)), this, SLOT(setNext())); + connect(m_keyDU, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); + connect(m_keyDD, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); + connect(m_keyDL, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); + connect(m_keyDR, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); + connect(m_keySelect, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); + connect(m_keyStart, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); + connect(m_keyA, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); + connect(m_keyB, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); + connect(m_keyL, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); + connect(m_keyR, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); + m_buttons = new QWidget(this); QVBoxLayout* layout = new QVBoxLayout; m_buttons->setLayout(layout);

@@ -116,11 +112,8 @@

setAll->setFocus(); #ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - m_gamepadTimer = new QTimer(this); - connect(m_gamepadTimer, SIGNAL(timeout()), this, SLOT(testGamepad())); - m_gamepadTimer->setInterval(100); - m_gamepadTimer->start(); + if (type == SDL_BINDING_BUTTON) {\ + QTimer::singleShot(50, this, SLOT(testGamepad())); } #endif }

@@ -168,33 +161,115 @@ }

} void GBAKeyEditor::save() { - m_controller->bindKey(m_type, m_keyDU->value(), GBA_KEY_UP); - m_controller->bindKey(m_type, m_keyDD->value(), GBA_KEY_DOWN); - m_controller->bindKey(m_type, m_keyDL->value(), GBA_KEY_LEFT); - m_controller->bindKey(m_type, m_keyDR->value(), GBA_KEY_RIGHT); - m_controller->bindKey(m_type, m_keySelect->value(), GBA_KEY_SELECT); - m_controller->bindKey(m_type, m_keyStart->value(), GBA_KEY_START); - m_controller->bindKey(m_type, m_keyA->value(), GBA_KEY_A); - m_controller->bindKey(m_type, m_keyB->value(), GBA_KEY_B); - m_controller->bindKey(m_type, m_keyL->value(), GBA_KEY_L); - m_controller->bindKey(m_type, m_keyR->value(), GBA_KEY_R); + bindKey(m_keyDU, GBA_KEY_UP); + bindKey(m_keyDD, GBA_KEY_DOWN); + bindKey(m_keyDL, GBA_KEY_LEFT); + bindKey(m_keyDR, GBA_KEY_RIGHT); + bindKey(m_keySelect, GBA_KEY_SELECT); + bindKey(m_keyStart, GBA_KEY_START); + bindKey(m_keyA, GBA_KEY_A); + bindKey(m_keyB, GBA_KEY_B); + bindKey(m_keyL, GBA_KEY_L); + bindKey(m_keyR, GBA_KEY_R); m_controller->saveConfiguration(m_type); } +void GBAKeyEditor::lookupBinding(const GBAInputMap* map, KeyEditor* keyEditor, GBAKey key) { + #ifdef BUILD_SDL + if (m_type == SDL_BINDING_BUTTON) { + int value = GBAInputQueryBinding(map, m_type, key); + if (value != GBA_NO_MAPPING) { + keyEditor->setValueButton(value); + } + return; + } + #endif + keyEditor->setValueKey(GBAInputQueryBinding(map, m_type, key)); +} + +#ifdef BUILD_SDL +void GBAKeyEditor::lookupAxes(const GBAInputMap* map) { + GBAInputEnumerateAxes(map, m_type, [](int axis, const GBAAxis* description, void* user) { + GBAKeyEditor* self = static_cast<GBAKeyEditor*>(user); + if (description->highDirection != GBA_KEY_NONE) { + KeyEditor* key = self->keyById(description->highDirection); + if (key) { + key->setValueAxis(axis, description->deadHigh); + } + } + if (description->lowDirection != GBA_KEY_NONE) { + KeyEditor* key = self->keyById(description->lowDirection); + if (key) { + key->setValueAxis(axis, description->deadLow); + } + } + }, this); +} +#endif + +void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) { + if (keyEditor->direction() != InputController::NEUTRAL) { + m_controller->bindAxis(m_type, keyEditor->value(), keyEditor->direction(), key); + } else { + m_controller->bindKey(m_type, keyEditor->value(), key); + } +} + #ifdef BUILD_SDL void GBAKeyEditor::testGamepad() { - QSet<int> activeKeys = m_controller->activeGamepadButtons(); - if (activeKeys.empty()) { + KeyEditor* focused = *m_currentKey; + if (!focused) { + QTimer::singleShot(50, this, SLOT(testGamepad())); return; } - for (KeyEditor* key : m_keyOrder) { - if (!key->hasFocus()) { - continue; - } - key->setValue(*activeKeys.begin()); + + QSet<QPair<int, int32_t>> activeAxes = m_controller->activeGamepadAxes(); + if (!activeAxes.empty()) { + focused->setValueAxis(activeAxes.begin()->first, activeAxes.begin()->second); + + QTimer::singleShot(200, this, SLOT(testGamepad())); + return; } + + QSet<int> activeKeys = m_controller->activeGamepadButtons(); + if (!activeKeys.empty()) { + focused->setValueButton(*activeKeys.begin()); + + QTimer::singleShot(200, this, SLOT(testGamepad())); + return; + } + + QTimer::singleShot(50, this, SLOT(testGamepad())); } #endif + +KeyEditor* GBAKeyEditor::keyById(GBAKey key) { + switch (key) { + case GBA_KEY_UP: + return m_keyDU; + case GBA_KEY_DOWN: + return m_keyDD; + case GBA_KEY_LEFT: + return m_keyDL; + case GBA_KEY_RIGHT: + return m_keyDR; + case GBA_KEY_A: + return m_keyA; + case GBA_KEY_B: + return m_keyB; + case GBA_KEY_L: + return m_keyL; + case GBA_KEY_R: + return m_keyR; + case GBA_KEY_SELECT: + return m_keySelect; + case GBA_KEY_START: + return m_keyStart; + default: + break; + } + return nullptr; +} void GBAKeyEditor::setLocation(QWidget* widget, qreal x, qreal y) { QSize s = size();
M src/platform/qt/GBAKeyEditor.hsrc/platform/qt/GBAKeyEditor.h

@@ -10,6 +10,10 @@ #include <QList>

#include <QPicture> #include <QWidget> +extern "C" { +#include "gba-input.h" +} + namespace QGBA { class InputController;

@@ -43,10 +47,14 @@ static const qreal DPAD_HEIGHT;

void setLocation(QWidget* widget, qreal x, qreal y); + void lookupBinding(const GBAInputMap*, KeyEditor*, GBAKey); + void bindKey(const KeyEditor*, GBAKey); #ifdef BUILD_SDL - QTimer* m_gamepadTimer; + void lookupAxes(const GBAInputMap*); #endif + + KeyEditor* keyById(GBAKey); QWidget* m_buttons; KeyEditor* m_keyDU;
M src/platform/qt/InputController.cppsrc/platform/qt/InputController.cpp

@@ -126,4 +126,40 @@ }

} return activeButtons; } + +QSet<QPair<int, int32_t>> InputController::activeGamepadAxes() { + SDL_Joystick* joystick = m_sdlEvents.joystick; + SDL_JoystickUpdate(); + int numButtons = SDL_JoystickNumAxes(joystick); + QSet<QPair<int, int32_t>> activeAxes; + int i; + for (i = 0; i < numButtons; ++i) { + int32_t axis = SDL_JoystickGetAxis(joystick, i); + if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) { + activeAxes.insert(qMakePair(i, axis)); + } + } + return activeAxes; +} + +void InputController::bindAxis(uint32_t type, int axis, Direction direction, GBAKey key) { + const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, SDL_BINDING_BUTTON, axis); + GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD }; + if (old) { + description = *old; + } + switch (direction) { + case NEGATIVE: + description.lowDirection = key; + description.deadLow = -AXIS_THRESHOLD; + break; + case POSITIVE: + description.highDirection = key; + description.deadHigh = AXIS_THRESHOLD; + break; + default: + return; + } + GBAInputBindAxis(&m_inputMap, SDL_BINDING_BUTTON, axis, &description); +} #endif
M src/platform/qt/InputController.hsrc/platform/qt/InputController.h

@@ -38,8 +38,18 @@

const GBAInputMap* map() const { return &m_inputMap; } #ifdef BUILD_SDL + static const int32_t AXIS_THRESHOLD = 0x3000; + enum Direction { + NEUTRAL = 0, + POSITIVE = 1, + NEGATIVE = -1 + }; + int testSDLEvents(); QSet<int> activeGamepadButtons(); + QSet<QPair<int, int32_t>> activeGamepadAxes(); + + void bindAxis(uint32_t type, int axis, Direction, GBAKey); #endif private:
M src/platform/qt/KeyEditor.cppsrc/platform/qt/KeyEditor.cpp

@@ -11,13 +11,13 @@ using namespace QGBA;

KeyEditor::KeyEditor(QWidget* parent) : QLineEdit(parent) - , m_numeric(false) + , m_direction(InputController::NEUTRAL) { setAlignment(Qt::AlignCenter); } void KeyEditor::setValue(int key) { - if (m_numeric) { + if (m_button) { setText(QString::number(key)); } else { setText(QKeySequence(key).toString(QKeySequence::NativeText));

@@ -26,6 +26,24 @@ m_key = key;

emit valueChanged(key); } +void KeyEditor::setValueKey(int key) { + m_button = false; + setValue(key); +} + +void KeyEditor::setValueButton(int button) { + m_button = true; + setValue(button); +} + +void KeyEditor::setValueAxis(int axis, int32_t value) { + m_button = true; + m_key = axis; + m_direction = value < 0 ? InputController::NEGATIVE : InputController::POSITIVE; + setText((value < 0 ? "-" : "+") + QString::number(axis)); + emit axisChanged(axis, m_direction); +} + QSize KeyEditor::sizeHint() const { QSize hint = QLineEdit::sizeHint(); hint.setWidth(40);

@@ -33,7 +51,7 @@ return hint;

} void KeyEditor::keyPressEvent(QKeyEvent* event) { - if (!m_numeric) { + if (!m_button) { setValue(event->key()); } event->accept();
M src/platform/qt/KeyEditor.hsrc/platform/qt/KeyEditor.h

@@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef QGBA_KEY_EDITOR #define QGBA_KEY_EDITOR +#include "InputController.h" #include <QLineEdit> namespace QGBA {

@@ -19,19 +20,24 @@

void setValue(int key); int value() const { return m_key; } - void setNumeric(bool numeric) { m_numeric = numeric; } + void setValueKey(int key); + void setValueButton(int button); + void setValueAxis(int axis, int32_t value); + InputController::Direction direction() const { return m_direction; } virtual QSize sizeHint() const override; signals: void valueChanged(int key); + void axisChanged(int key, int direction); protected: virtual void keyPressEvent(QKeyEvent* event) override; private: int m_key; - bool m_numeric; + bool m_button; + InputController::Direction m_direction; }; }