Qt: Axis mapping

This commit is contained in:
Jeffrey Pfau 2014-12-14 21:11:22 -08:00
parent 1c1fbfe163
commit 412aa147ea
8 changed files with 236 additions and 57 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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