mirror of https://github.com/mgba-emu/mgba.git
Qt: Add infrastructure for gamepad hats
This commit is contained in:
parent
78526ae71a
commit
f4a61f91d4
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -83,6 +83,7 @@ set(SOURCE_FILES
|
|||
GameController.cpp
|
||||
GamepadAxisEvent.cpp
|
||||
GamepadButtonEvent.cpp
|
||||
GamepadHatEvent.cpp
|
||||
IOViewer.cpp
|
||||
InputController.cpp
|
||||
InputProfile.cpp
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -63,6 +63,7 @@ private:
|
|||
|
||||
#ifdef BUILD_SDL
|
||||
void lookupAxes(const mInputMap*);
|
||||
void lookupHats(const mInputMap*);
|
||||
#endif
|
||||
|
||||
KeyEditor* keyById(GBAKey);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue