all repos — mgba @ f4a61f91d46b8a90a61ca0f9ef04f431f0a4fd9f

mGBA Game Boy Advance Emulator

Qt: Add infrastructure for gamepad hats
Vicki Pfau vi@endrift.com
Mon, 23 Jan 2017 01:28:41 -0800
commit

f4a61f91d46b8a90a61ca0f9ef04f431f0a4fd9f

parent

78526ae71a7776c57b3a63a74c46031bf7a9a4f8

M include/mgba/core/input.hinclude/mgba/core/input.h

@@ -66,6 +66,7 @@ void mInputEnumerateAxes(const struct mInputMap*, uint32_t type, void (handler(int axis, const struct mInputAxis* description, void* user)), void* user);

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);
M src/core/input.csrc/core/input.c

@@ -541,6 +541,19 @@ }

*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) {
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -83,6 +83,7 @@ GIFView.cpp

GameController.cpp GamepadAxisEvent.cpp GamepadButtonEvent.cpp + GamepadHatEvent.cpp IOViewer.cpp InputController.cpp InputProfile.cpp
M src/platform/qt/GBAKeyEditor.cppsrc/platform/qt/GBAKeyEditor.cpp

@@ -73,6 +73,7 @@ return;

} bool signalsBlocked = (*m_currentKey)->blockSignals(true); (*m_currentKey)->clearButton(); + (*m_currentKey)->clearHat(); (*m_currentKey)->blockSignals(signalsBlocked); });

@@ -119,6 +120,7 @@

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 @@ 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 + lookupAxes(map); + lookupHats(map); +#endif } void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBAKey key) {

@@ -272,12 +279,47 @@ }

} }, 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) { #ifdef BUILD_SDL 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 @@ if (activeGamepad > 0) {

m_profileSelect->setCurrentIndex(activeGamepad); } lookupAxes(m_controller->map()); + lookupHats(m_controller->map()); } #endif
M src/platform/qt/GBAKeyEditor.hsrc/platform/qt/GBAKeyEditor.h

@@ -63,6 +63,7 @@ bool findFocus(KeyEditor* needle = nullptr);

#ifdef BUILD_SDL void lookupAxes(const mInputMap*); + void lookupHats(const mInputMap*); #endif KeyEditor* keyById(GBAKey);
A src/platform/qt/GamepadHatEvent.cpp

@@ -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; +}
A src/platform/qt/GamepadHatEvent.h

@@ -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
M src/platform/qt/InputController.cppsrc/platform/qt/InputController.cpp

@@ -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 @@ auto activeButtons = activeGamepadButtons(type);

auto oldButtons = m_activeButtons; m_activeButtons = activeButtons; + auto activeHats = activeGamepadHats(type); + auto oldHats = m_activeHats; + m_activeHats = activeHats; + if (!QApplication::focusWidget()) { return; }

@@ -468,6 +526,23 @@ }

} for (int button : oldButtons) { GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this); + 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); }
M src/platform/qt/InputController.hsrc/platform/qt/InputController.h

@@ -7,6 +7,7 @@ #ifndef QGBA_INPUT_CONTROLLER_H

#define QGBA_INPUT_CONTROLLER_H #include "GamepadAxisEvent.h" +#include "GamepadHatEvent.h" #include <QObject> #include <QSet>

@@ -59,11 +60,14 @@

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 @@ QVector<int> m_deadzones;

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;
M src/platform/qt/KeyEditor.cppsrc/platform/qt/KeyEditor.cpp

@@ -17,8 +17,10 @@

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

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

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 @@ setValueButton(static_cast<GamepadButtonEvent*>(event)->value());

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

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)); }
M src/platform/qt/KeyEditor.hsrc/platform/qt/KeyEditor.h

@@ -7,6 +7,7 @@ #ifndef QGBA_KEY_EDITOR

#define QGBA_KEY_EDITOR #include "GamepadAxisEvent.h" +#include "GamepadHatEvent.h" #include <QLineEdit> #include <QTimer>

@@ -24,6 +25,9 @@

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 @@ void setValue(int key);

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

int m_key; int m_axis; + int m_hat; bool m_button; GamepadAxisEvent::Direction m_direction; + GamepadHatEvent::Direction m_hatDirection; QTimer m_lastKey; };