Qt: Add infrastructure for gamepad hats

This commit is contained in:
Vicki Pfau 2017-01-23 01:28:41 -08:00
parent 78526ae71a
commit f4a61f91d4
11 changed files with 280 additions and 2 deletions

View File

@ -66,6 +66,7 @@ void mInputEnumerateAxes(const struct mInputMap*, uint32_t type, void (handler(i
int mInputMapHat(const struct mInputMap*, uint32_t type, int id, int direction);
void mInputBindHat(struct mInputMap*, uint32_t type, int id, const struct mInputHatBindings* bindings);
bool mInputQueryHat(const struct mInputMap*, uint32_t type, int id, struct mInputHatBindings* bindings);
void mInputUnbindHat(struct mInputMap*, uint32_t type, int id);
void mInputUnbindAllHats(struct mInputMap*, uint32_t type);

View File

@ -541,6 +541,19 @@ void mInputBindHat(struct mInputMap* map, uint32_t type, int id, const struct mI
*mInputHatListGetPointer(&impl->hats, id) = *bindings;
}
bool mInputQueryHat(const struct mInputMap* map, uint32_t type, int id, struct mInputHatBindings* bindings) {
const struct mInputMapImpl* impl = _lookupMapConst(map, type);
if (!impl) {
return false;
}
if (id >= (ssize_t) mInputHatListSize(&impl->hats)) {
return false;
}
*bindings = *mInputHatListGetConstPointer(&impl->hats, id);
return true;
}
void mInputUnbindHat(struct mInputMap* map, uint32_t type, int id) {
struct mInputMapImpl* impl = _lookupMap(map, type);
if (!impl) {

View File

@ -83,6 +83,7 @@ set(SOURCE_FILES
GameController.cpp
GamepadAxisEvent.cpp
GamepadButtonEvent.cpp
GamepadHatEvent.cpp
IOViewer.cpp
InputController.cpp
InputProfile.cpp

View File

@ -73,6 +73,7 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString&
}
bool signalsBlocked = (*m_currentKey)->blockSignals(true);
(*m_currentKey)->clearButton();
(*m_currentKey)->clearHat();
(*m_currentKey)->blockSignals(signalsBlocked);
});
@ -119,6 +120,7 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString&
for (auto& key : m_keyOrder) {
connect(key, SIGNAL(valueChanged(int)), this, SLOT(setNext()));
connect(key, SIGNAL(axisChanged(int, int)), this, SLOT(setNext()));
connect(key, SIGNAL(hatChanged(int, int)), this, SLOT(setNext()));
key->installEventFilter(this);
}
@ -241,6 +243,11 @@ void GBAKeyEditor::refresh() {
lookupBinding(map, m_keyB, GBA_KEY_B);
lookupBinding(map, m_keyL, GBA_KEY_L);
lookupBinding(map, m_keyR, GBA_KEY_R);
#ifdef BUILD_SDL
lookupAxes(map);
lookupHats(map);
#endif
}
void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBAKey key) {
@ -272,6 +279,38 @@ void GBAKeyEditor::lookupAxes(const mInputMap* map) {
}
}, this);
}
void GBAKeyEditor::lookupHats(const mInputMap* map) {
struct mInputHatBindings bindings;
int i = 0;
while (mInputQueryHat(map, m_type, i, &bindings)) {
if (bindings.up >= 0) {
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.up));
if (key) {
key->setValueHat(i, GamepadHatEvent::UP);
}
}
if (bindings.right >= 0) {
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.right));
if (key) {
key->setValueHat(i, GamepadHatEvent::RIGHT);
}
}
if (bindings.down >= 0) {
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.down));
if (key) {
key->setValueHat(i, GamepadHatEvent::DOWN);
}
}
if (bindings.left >= 0) {
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.left));
if (key) {
key->setValueHat(i, GamepadHatEvent::LEFT);
}
}
++i;
}
}
#endif
void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) {
@ -279,6 +318,9 @@ void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) {
if (m_type == SDL_BINDING_BUTTON && keyEditor->axis() >= 0) {
m_controller->bindAxis(m_type, keyEditor->axis(), keyEditor->direction(), key);
}
if (m_type == SDL_BINDING_BUTTON && keyEditor->hat() >= 0) {
m_controller->bindHat(m_type, keyEditor->hat(), keyEditor->hatDirection(), key);
}
#endif
m_controller->bindKey(m_type, keyEditor->value(), key);
}
@ -362,5 +404,6 @@ void GBAKeyEditor::updateJoysticks() {
m_profileSelect->setCurrentIndex(activeGamepad);
}
lookupAxes(m_controller->map());
lookupHats(m_controller->map());
}
#endif

View File

@ -63,6 +63,7 @@ private:
#ifdef BUILD_SDL
void lookupAxes(const mInputMap*);
void lookupHats(const mInputMap*);
#endif
KeyEditor* keyById(GBAKey);

View File

@ -0,0 +1,40 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GamepadHatEvent.h"
#include "InputController.h"
using namespace QGBA;
QEvent::Type GamepadHatEvent::s_downType = QEvent::None;
QEvent::Type GamepadHatEvent::s_upType = QEvent::None;
GamepadHatEvent::GamepadHatEvent(QEvent::Type pressType, int hatId, Direction direction, int type, InputController* controller)
: QEvent(pressType)
, m_hatId(hatId)
, m_direction(direction)
, m_controller(controller)
, m_key(GBA_KEY_NONE)
{
ignore();
if (controller) {
m_key = static_cast<GBAKey>(mInputMapHat(controller->map(), type, hatId, direction));
}
}
QEvent::Type GamepadHatEvent::Down() {
if (s_downType == None) {
s_downType = static_cast<Type>(registerEventType());
}
return s_downType;
}
QEvent::Type GamepadHatEvent::Up() {
if (s_upType == None) {
s_upType = static_cast<Type>(registerEventType());
}
return s_upType;
}

View File

@ -0,0 +1,48 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef QGBA_GAMEPAD_HAT_EVENT
#define QGBA_GAMEPAD_HAT_EVENT
#include <QEvent>
#include <mgba/internal/gba/input.h>
namespace QGBA {
class InputController;
class GamepadHatEvent : public QEvent {
public:
enum Direction {
CENTER = 0,
UP = 1,
RIGHT = 2,
DOWN = 4,
LEFT = 8
};
GamepadHatEvent(Type pressType, int hatId, Direction direction, int type, InputController* controller = nullptr);
int hatId() const { return m_hatId; }
Direction direction() const { return m_direction; }
GBAKey gbaKey() const { return m_key; }
static Type Down();
static Type Up();
private:
static Type s_downType;
static Type s_upType;
int m_hatId;
Direction m_direction;
InputController* m_controller;
GBAKey m_key;
};
}
#endif

View File

@ -418,6 +418,60 @@ void InputController::unbindAllAxes(uint32_t type) {
mInputUnbindAllAxes(&m_inputMap, type);
}
QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
#ifdef BUILD_SDL
if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
SDL_JoystickUpdate();
int numHats = SDL_JoystickNumHats(joystick);
if (numHats < 1) {
return activeHats;
}
int i;
for (i = 0; i < numHats; ++i) {
int hat = SDL_JoystickGetHat(joystick, i);
if (hat & GamepadHatEvent::UP) {
activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
}
if (hat & GamepadHatEvent::RIGHT) {
activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
}
if (hat & GamepadHatEvent::DOWN) {
activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
}
if (hat & GamepadHatEvent::LEFT) {
activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
}
}
}
#endif
return activeHats;
}
void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) {
mInputHatBindings bindings{ -1, -1, -1, -1 };
mInputQueryHat(&m_inputMap, type, hat, &bindings);
switch (direction) {
case GamepadHatEvent::UP:
bindings.up = gbaKey;
break;
case GamepadHatEvent::RIGHT:
bindings.right = gbaKey;
break;
case GamepadHatEvent::DOWN:
bindings.down = gbaKey;
break;
case GamepadHatEvent::LEFT:
bindings.left = gbaKey;
break;
default:
return;
}
mInputBindHat(&m_inputMap, type, hat, &bindings);
}
void InputController::testGamepad(int type) {
auto activeAxes = activeGamepadAxes(type);
auto oldAxes = m_activeAxes;
@ -427,6 +481,10 @@ void InputController::testGamepad(int type) {
auto oldButtons = m_activeButtons;
m_activeButtons = activeButtons;
auto activeHats = activeGamepadHats(type);
auto oldHats = m_activeHats;
m_activeHats = activeHats;
if (!QApplication::focusWidget()) {
return;
}
@ -471,6 +529,23 @@ void InputController::testGamepad(int type) {
clearPendingEvent(event->gbaKey());
sendGamepadEvent(event);
}
activeHats.subtract(oldHats);
oldHats.subtract(m_activeHats);
for (auto& hat : activeHats) {
GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
postPendingEvent(event->gbaKey());
sendGamepadEvent(event);
if (!event->isAccepted()) {
clearPendingEvent(event->gbaKey());
}
}
for (auto& hat : oldHats) {
GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
clearPendingEvent(event->gbaKey());
sendGamepadEvent(event);
}
}
void InputController::sendGamepadEvent(QEvent* event) {

View File

@ -7,6 +7,7 @@
#define QGBA_INPUT_CONTROLLER_H
#include "GamepadAxisEvent.h"
#include "GamepadHatEvent.h"
#include <QObject>
#include <QSet>
@ -59,11 +60,14 @@ public:
static const int32_t AXIS_THRESHOLD = 0x3000;
QSet<int> activeGamepadButtons(int type);
QSet<QPair<int, GamepadAxisEvent::Direction>> activeGamepadAxes(int type);
QSet<QPair<int, GamepadHatEvent::Direction>> activeGamepadHats(int type);
void recalibrateAxes();
void bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction, GBAKey);
void unbindAllAxes(uint32_t type);
void bindHat(uint32_t type, int hat, GamepadHatEvent::Direction, GBAKey);
QStringList connectedGamepads(uint32_t type) const;
int gamepad(uint32_t type) const;
void setGamepad(uint32_t type, int index);
@ -118,6 +122,7 @@ private:
QSet<int> m_activeButtons;
QSet<QPair<int, GamepadAxisEvent::Direction>> m_activeAxes;
QSet<QPair<int, GamepadHatEvent::Direction>> m_activeHats;
QTimer* m_gamepadTimer;
QSet<GBAKey> m_pendingEvents;

View File

@ -17,8 +17,10 @@ using namespace QGBA;
KeyEditor::KeyEditor(QWidget* parent)
: QLineEdit(parent)
, m_direction(GamepadAxisEvent::NEUTRAL)
, m_hatDirection(GamepadHatEvent::CENTER)
, m_key(-1)
, m_axis(-1)
, m_hat(-1)
, m_button(false)
{
setAlignment(Qt::AlignCenter);
@ -58,6 +60,14 @@ void KeyEditor::setValueAxis(int axis, int32_t value) {
emit axisChanged(axis, m_direction);
}
void KeyEditor::setValueHat(int hat, GamepadHatEvent::Direction direction) {
m_button = true;
m_hat = hat;
m_hatDirection = direction;
updateButtonText();
emit hatChanged(hat, m_hatDirection);
}
void KeyEditor::clearButton() {
m_button = true;
setValue(-1);
@ -71,10 +81,18 @@ void KeyEditor::clearAxis() {
emit axisChanged(m_axis, m_direction);
}
void KeyEditor::clearHat() {
m_button = true;
m_hat = -1;
m_hatDirection = GamepadHatEvent::CENTER;
updateButtonText();
emit hatChanged(m_hat, m_hatDirection);
}
QSize KeyEditor::sizeHint() const {
QSize hint = QLineEdit::sizeHint();
QFontMetrics fm(font());
hint.setWidth(fm.height() * 3);
hint.setWidth(fm.height() * 4);
return hint;
}
@ -145,6 +163,12 @@ bool KeyEditor::event(QEvent* event) {
event->accept();
return true;
}
if (event->type() == GamepadHatEvent::Down()) {
GamepadHatEvent* ghe = static_cast<GamepadHatEvent*>(event);
setValueHat(ghe->hatId(), ghe->direction());
event->accept();
return true;
}
if (event->type() == GamepadAxisEvent::Type()) {
GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
if (gae->isNew()) {
@ -159,6 +183,24 @@ bool KeyEditor::event(QEvent* event) {
void KeyEditor::updateButtonText() {
QStringList text;
if (m_hat >= 0) {
switch (m_hatDirection) {
case GamepadHatEvent::UP:
text.append(QString("↑%0").arg(m_hat));
break;
case GamepadHatEvent::RIGHT:
text.append(QString("→%0").arg(m_hat));
break;
case GamepadHatEvent::DOWN:
text.append(QString("↓%0").arg(m_hat));
break;
case GamepadHatEvent::LEFT:
text.append(QString("←%0").arg(m_hat));
break;
default:
break;
}
}
if (m_key >= 0) {
text.append(QString::number(m_key));
}

View File

@ -7,6 +7,7 @@
#define QGBA_KEY_EDITOR
#include "GamepadAxisEvent.h"
#include "GamepadHatEvent.h"
#include <QLineEdit>
#include <QTimer>
@ -24,6 +25,9 @@ public:
GamepadAxisEvent::Direction direction() const { return m_direction; }
int axis() const { return m_axis; }
GamepadHatEvent::Direction hatDirection() const { return m_hatDirection; }
int hat() const { return m_hat; }
virtual QSize sizeHint() const override;
public slots:
@ -31,12 +35,15 @@ public slots:
void setValueKey(int key);
void setValueButton(int button);
void setValueAxis(int axis, int32_t value);
void setValueHat(int hat, GamepadHatEvent::Direction value);
void clearButton();
void clearAxis();
void clearHat();
signals:
void valueChanged(int key);
void axisChanged(int key, int direction);
void axisChanged(int axis, int direction);
void hatChanged(int hat, int direction);
protected:
virtual void keyPressEvent(QKeyEvent* event) override;
@ -49,8 +56,10 @@ private:
int m_key;
int m_axis;
int m_hat;
bool m_button;
GamepadAxisEvent::Direction m_direction;
GamepadHatEvent::Direction m_hatDirection;
QTimer m_lastKey;
};