mirror of https://github.com/mgba-emu/mgba.git
Qt: Axis mapping
This commit is contained in:
parent
1c1fbfe163
commit
412aa147ea
|
@ -27,6 +27,11 @@ struct GBAAxisSave {
|
|||
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 @@ static void _saveAxis(uint32_t axis, void* dp, void* up) {
|
|||
}
|
||||
}
|
||||
|
||||
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 GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKe
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,6 +367,18 @@ const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t
|
|||
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) {
|
||||
_loadKey(map, type, config, GBA_KEY_A, "A");
|
||||
_loadKey(map, type, config, GBA_KEY_B, "B");
|
||||
|
|
|
@ -22,6 +22,8 @@ struct GBAAxis {
|
|||
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
|
|||
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*);
|
||||
|
|
|
@ -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,31 +42,20 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, QWidget* paren
|
|||
m_keyL = new KeyEditor(this);
|
||||
m_keyR = new KeyEditor(this);
|
||||
|
||||
#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);
|
||||
}
|
||||
#endif
|
||||
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);
|
||||
|
||||
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));
|
||||
#ifdef BUILD_SDL
|
||||
lookupAxes(map);
|
||||
#endif
|
||||
|
||||
connect(m_keyDU, SIGNAL(valueChanged(int)), this, SLOT(setNext()));
|
||||
connect(m_keyDD, SIGNAL(valueChanged(int)), this, SLOT(setNext()));
|
||||
|
@ -83,6 +68,17 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, QWidget* paren
|
|||
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 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, QWidget* paren
|
|||
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,34 +161,116 @@ void GBAKeyEditor::setNext() {
|
|||
}
|
||||
|
||||
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();
|
||||
QSize hint = widget->sizeHint();
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
#include <QPicture>
|
||||
#include <QWidget>
|
||||
|
||||
extern "C" {
|
||||
#include "gba-input.h"
|
||||
}
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class InputController;
|
||||
|
@ -43,11 +47,15 @@ private:
|
|||
|
||||
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;
|
||||
KeyEditor* m_keyDD;
|
||||
|
|
|
@ -126,4 +126,40 @@ QSet<int> InputController::activeGamepadButtons() {
|
|||
}
|
||||
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
|
||||
|
|
|
@ -38,8 +38,18 @@ public:
|
|||
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:
|
||||
|
|
|
@ -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 @@ void KeyEditor::setValue(int 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 @@ QSize KeyEditor::sizeHint() const {
|
|||
}
|
||||
|
||||
void KeyEditor::keyPressEvent(QKeyEvent* event) {
|
||||
if (!m_numeric) {
|
||||
if (!m_button) {
|
||||
setValue(event->key());
|
||||
}
|
||||
event->accept();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#ifndef QGBA_KEY_EDITOR
|
||||
#define QGBA_KEY_EDITOR
|
||||
|
||||
#include "InputController.h"
|
||||
#include <QLineEdit>
|
||||
|
||||
namespace QGBA {
|
||||
|
@ -19,19 +20,24 @@ public:
|
|||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue