Qt: Revamp all input to go through InputController
jump to
@@ -75,7 +75,6 @@ Display.cpp
DisplayGL.cpp DisplayQt.cpp GBAApp.cpp - GBAKeyEditor.cpp GIFView.cpp GameController.cpp GamepadAxisEvent.cpp
@@ -1,409 +0,0 @@
-/* Copyright (c) 2013-2014 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 "GBAKeyEditor.h" - -#include <QComboBox> -#include <QHBoxLayout> -#include <QPaintEvent> -#include <QPainter> -#include <QPushButton> -#include <QVBoxLayout> - -#include "InputController.h" -#include "KeyEditor.h" - -#ifdef BUILD_SDL -#include "platform/sdl/sdl-events.h" -#endif - -using namespace QGBA; - -const qreal GBAKeyEditor::DPAD_CENTER_X = 0.247; -const qreal GBAKeyEditor::DPAD_CENTER_Y = 0.432; -const qreal GBAKeyEditor::DPAD_WIDTH = 0.12; -const qreal GBAKeyEditor::DPAD_HEIGHT = 0.12; - -GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& profile, QWidget* parent) - : QWidget(parent) - , m_profileSelect(nullptr) - , m_clear(nullptr) - , m_type(type) - , m_profile(profile) - , m_controller(controller) -{ - setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint); - setMinimumSize(300, 300); - - const mInputMap* map = controller->map(); - controller->stealFocus(this); - - m_keyDU = new KeyEditor(this); - m_keyDD = new KeyEditor(this); - m_keyDL = new KeyEditor(this); - m_keyDR = new KeyEditor(this); - m_keySelect = new KeyEditor(this); - m_keyStart = new KeyEditor(this); - m_keyA = new KeyEditor(this); - m_keyB = new KeyEditor(this); - m_keyL = new KeyEditor(this); - m_keyR = new KeyEditor(this); - - refresh(); - -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - m_profileSelect = new QComboBox(this); - connect(m_profileSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectGamepad(int))); - - updateJoysticks(); - - m_clear = new QWidget(this); - QHBoxLayout* layout = new QHBoxLayout; - m_clear->setLayout(layout); - layout->setSpacing(6); - - QPushButton* clearButton = new QPushButton(tr("Clear Button")); - layout->addWidget(clearButton); - connect(clearButton, &QAbstractButton::pressed, [this]() { - if (!findFocus()) { - return; - } - bool signalsBlocked = (*m_currentKey)->blockSignals(true); - (*m_currentKey)->clearButton(); - (*m_currentKey)->clearHat(); - (*m_currentKey)->blockSignals(signalsBlocked); - }); - - QPushButton* clearAxis = new QPushButton(tr("Clear Analog")); - layout->addWidget(clearAxis); - connect(clearAxis, &QAbstractButton::pressed, [this]() { - if (!findFocus()) { - return; - } - bool signalsBlocked = (*m_currentKey)->blockSignals(true); - (*m_currentKey)->clearAxis(); - (*m_currentKey)->blockSignals(signalsBlocked); - }); - - QPushButton* updateJoysticksButton = new QPushButton(tr("Refresh")); - layout->addWidget(updateJoysticksButton); - connect(updateJoysticksButton, SIGNAL(pressed()), this, SLOT(updateJoysticks())); - } -#endif - - m_buttons = new QWidget(this); - QVBoxLayout* layout = new QVBoxLayout; - m_buttons->setLayout(layout); - - QPushButton* setAll = new QPushButton(tr("Set all")); - connect(setAll, SIGNAL(pressed()), this, SLOT(setAll())); - layout->addWidget(setAll); - - layout->setSpacing(6); - - m_keyOrder = QList<KeyEditor*>{ - m_keyDU, - m_keyDR, - m_keyDD, - m_keyDL, - m_keyA, - m_keyB, - m_keySelect, - m_keyStart, - m_keyL, - m_keyR - }; - - 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); - } - - m_currentKey = m_keyOrder.end(); - - m_background.load(":/res/keymap.qpic"); - - setAll->setFocus(); -} - -GBAKeyEditor::~GBAKeyEditor() { - m_controller->releaseFocus(this); -} - -void GBAKeyEditor::setAll() { - m_currentKey = m_keyOrder.begin(); - (*m_currentKey)->setFocus(); -} - -void GBAKeyEditor::resizeEvent(QResizeEvent* event) { - setLocation(m_buttons, 0.5, 0.2); - setLocation(m_keyDU, DPAD_CENTER_X, DPAD_CENTER_Y - DPAD_HEIGHT); - setLocation(m_keyDD, DPAD_CENTER_X, DPAD_CENTER_Y + DPAD_HEIGHT); - setLocation(m_keyDL, DPAD_CENTER_X - DPAD_WIDTH, DPAD_CENTER_Y); - setLocation(m_keyDR, DPAD_CENTER_X + DPAD_WIDTH, DPAD_CENTER_Y); - setLocation(m_keySelect, 0.415, 0.93); - setLocation(m_keyStart, 0.585, 0.93); - setLocation(m_keyA, 0.826, 0.475); - setLocation(m_keyB, 0.667, 0.514); - setLocation(m_keyL, 0.1, 0.1); - setLocation(m_keyR, 0.9, 0.1); - - if (m_profileSelect) { - setLocation(m_profileSelect, 0.5, 0.67); - } - - if (m_clear) { - setLocation(m_clear, 0.5, 0.77); - } -} - -void GBAKeyEditor::paintEvent(QPaintEvent* event) { - QPainter painter(this); - painter.scale(width() / 480.0, height() / 480.0); - painter.drawPicture(0, 0, m_background); -} - -void GBAKeyEditor::closeEvent(QCloseEvent*) { - m_controller->releaseFocus(this); -} - -bool GBAKeyEditor::event(QEvent* event) { - QEvent::Type type = event->type(); - if (type == QEvent::WindowActivate || type == QEvent::Show) { - m_controller->stealFocus(this); - } else if (type == QEvent::WindowDeactivate || type == QEvent::Hide) { - m_controller->releaseFocus(this); - } - return QWidget::event(event); -} - -bool GBAKeyEditor::eventFilter(QObject* obj, QEvent* event) { - if (event->type() != QEvent::FocusIn) { - return false; - } - findFocus(static_cast<KeyEditor*>(obj)); - return true; -} - -void GBAKeyEditor::setNext() { - if (m_currentKey == m_keyOrder.end()) { - return; - } - - ++m_currentKey; - if (m_currentKey != m_keyOrder.end()) { - (*m_currentKey)->setFocus(); - } else { - (*(m_currentKey - 1))->clearFocus(); - } -} - -void GBAKeyEditor::save() { -#ifdef BUILD_SDL - m_controller->unbindAllAxes(m_type); -#endif - - 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); - -#ifdef BUILD_SDL - if (m_profileSelect) { - m_controller->setPreferredGamepad(m_type, m_profileSelect->currentText()); - } -#endif - - if (!m_profile.isNull()) { - m_controller->saveProfile(m_type, m_profile); - } -} - -void GBAKeyEditor::refresh() { - const mInputMap* map = m_controller->map(); - 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); - -#ifdef BUILD_SDL - lookupAxes(map); - lookupHats(map); -#endif -} - -void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBAKey key) { -#ifdef BUILD_SDL - if (m_type == SDL_BINDING_BUTTON) { - int value = mInputQueryBinding(map, m_type, key); - keyEditor->setValueButton(value); - return; - } -#endif - keyEditor->setValueKey(mInputQueryBinding(map, m_type, key)); -} - -#ifdef BUILD_SDL -void GBAKeyEditor::lookupAxes(const mInputMap* map) { - mInputEnumerateAxes(map, m_type, [](int axis, const mInputAxis* description, void* user) { - GBAKeyEditor* self = static_cast<GBAKeyEditor*>(user); - if (description->highDirection != GBA_KEY_NONE) { - KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->highDirection)); - if (key) { - key->setValueAxis(axis, description->deadHigh); - } - } - if (description->lowDirection != GBA_KEY_NONE) { - KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->lowDirection)); - if (key) { - key->setValueAxis(axis, description->deadLow); - } - } - }, 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); -} - -bool GBAKeyEditor::findFocus(KeyEditor* needle) { - if (m_currentKey != m_keyOrder.end() && (*m_currentKey)->hasFocus()) { - return true; - } - - for (auto key = m_keyOrder.begin(); key != m_keyOrder.end(); ++key) { - if ((*key)->hasFocus() || needle == *key) { - m_currentKey = key; - return true; - } - } - return m_currentKey != m_keyOrder.end(); -} - -#ifdef BUILD_SDL -void GBAKeyEditor::setAxisValue(int axis, int32_t value) { - if (!findFocus()) { - return; - } - KeyEditor* focused = *m_currentKey; - focused->setValueAxis(axis, value); -} - -void GBAKeyEditor::selectGamepad(int index) { - m_controller->setGamepad(m_type, index); - m_profile = m_profileSelect->currentText(); - m_controller->loadProfile(m_type, m_profile); - refresh(); -} -#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(); - widget->setGeometry(s.width() * x - hint.width() / 2.0, s.height() * y - hint.height() / 2.0, hint.width(), - hint.height()); -} - -#ifdef BUILD_SDL -void GBAKeyEditor::updateJoysticks() { - m_controller->updateJoysticks(); - m_controller->recalibrateAxes(); - - m_profileSelect->clear(); - m_profileSelect->addItems(m_controller->connectedGamepads(m_type)); - int activeGamepad = m_controller->gamepad(m_type); - selectGamepad(activeGamepad); - if (activeGamepad > 0) { - m_profileSelect->setCurrentIndex(activeGamepad); - } - lookupAxes(m_controller->map()); - lookupHats(m_controller->map()); -} -#endif
@@ -1,96 +0,0 @@
-/* Copyright (c) 2013-2015 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_GBA_KEY_EDITOR -#define QGBA_GBA_KEY_EDITOR - -#include <QList> -#include <QPicture> -#include <QSet> -#include <QWidget> - -#include <mgba/internal/gba/input.h> - -class QComboBox; -class QTimer; - -namespace QGBA { - -class InputController; -class KeyEditor; - -class GBAKeyEditor : public QWidget { -Q_OBJECT - -public: - GBAKeyEditor(InputController* controller, int type, const QString& profile = QString(), QWidget* parent = nullptr); - virtual ~GBAKeyEditor(); - -public slots: - void setAll(); - void save(); - -protected: - virtual void resizeEvent(QResizeEvent*) override; - virtual void paintEvent(QPaintEvent*) override; - virtual bool event(QEvent*) override; - virtual void closeEvent(QCloseEvent*) override; - virtual bool eventFilter(QObject* obj, QEvent* event) override; - -private slots: - void setNext(); - void refresh(); -#ifdef BUILD_SDL - void setAxisValue(int axis, int32_t value); - void selectGamepad(int index); - void updateJoysticks(); -#endif - -private: - static const qreal DPAD_CENTER_X; - static const qreal DPAD_CENTER_Y; - static const qreal DPAD_WIDTH; - static const qreal DPAD_HEIGHT; - - void setLocation(QWidget* widget, qreal x, qreal y); - - void lookupBinding(const mInputMap*, KeyEditor*, GBAKey); - void bindKey(const KeyEditor*, GBAKey); - - bool findFocus(KeyEditor* needle = nullptr); - -#ifdef BUILD_SDL - void lookupAxes(const mInputMap*); - void lookupHats(const mInputMap*); -#endif - - KeyEditor* keyById(GBAKey); - - QComboBox* m_profileSelect; - QWidget* m_clear; - QWidget* m_buttons; - KeyEditor* m_keyDU; - KeyEditor* m_keyDD; - KeyEditor* m_keyDL; - KeyEditor* m_keyDR; - KeyEditor* m_keySelect; - KeyEditor* m_keyStart; - KeyEditor* m_keyA; - KeyEditor* m_keyB; - KeyEditor* m_keyL; - KeyEditor* m_keyR; - QList<KeyEditor*> m_keyOrder; - QList<KeyEditor*>::iterator m_currentKey; - - uint32_t m_type; - QString m_profile; - InputController* m_controller; - - QPicture m_background; -}; - -} - -#endif
@@ -27,6 +27,7 @@ }
QEvent::Type GamepadAxisEvent::Type() { if (s_type == None) { + qRegisterMetaType<Direction>("GamepadAxisEvent::Direction"); s_type = static_cast<enum Type>(registerEventType()); } return s_type;
@@ -27,6 +27,7 @@ }
QEvent::Type GamepadHatEvent::Down() { if (s_downType == None) { + qRegisterMetaType<Direction>("GamepadHatEvent::Direction"); s_downType = static_cast<Type>(registerEventType()); } return s_downType;
@@ -6,11 +6,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InputController.h" #include "ConfigController.h" +#include "GameController.h" #include "GamepadAxisEvent.h" #include "GamepadButtonEvent.h" +#include "InputModel.h" #include "InputProfile.h" #include <QApplication> +#include <QKeyEvent> +#include <QMenu> #include <QTimer> #include <QWidget>@@ -24,8 +28,10 @@ int InputController::s_sdlInited = 0;
mSDLEvents InputController::s_sdlEvents; #endif -InputController::InputController(int playerId, QWidget* topLevel, QObject* parent) +InputController::InputController(InputModel* model, int playerId, QWidget* topLevel, QObject* parent) : QObject(parent) + , m_inputModel(model) + , m_platform(PLATFORM_NONE) , m_playerId(playerId) , m_config(nullptr) , m_gamepadTimer(nullptr)@@ -37,15 +43,11 @@ , m_allowOpposing(false)
, m_topLevel(topLevel) , m_focusParent(topLevel) { - mInputMapInit(&m_inputMap, &GBAInputInfo); - #ifdef BUILD_SDL if (s_sdlInited == 0) { mSDLInitEvents(&s_sdlEvents); } ++s_sdlInited; - m_sdlPlayer.bindings = &m_inputMap; - mSDLInitBindingsGBA(&m_inputMap); updateJoysticks(); #endif@@ -60,20 +62,21 @@ #endif
m_gamepadTimer.setInterval(50); m_gamepadTimer.start(); - mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A); - mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B); - mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L); - mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R); - mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START); - mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT); - mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP); - mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN); - mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT); - mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT); + m_autofireMenu = std::unique_ptr<QMenu>(new QMenu(tr("Autofire"))); + m_inputModel->addMenu(m_autofireMenu.get()); + + m_inputMenu = std::unique_ptr<QMenu>(new QMenu(tr("Bindings"))); + m_inputModel->addMenu(m_inputMenu.get()); + + connect(m_inputModel, SIGNAL(keyRebound(const QModelIndex&, int)), this, SLOT(bindKey(const QModelIndex&, int))); + connect(m_inputModel, SIGNAL(buttonRebound(const QModelIndex&, int)), this, SLOT(bindButton(const QModelIndex&, int))); + connect(m_inputModel, SIGNAL(axisRebound(const QModelIndex&, int, GamepadAxisEvent::Direction)), this, SLOT(bindAxis(const QModelIndex&, int, GamepadAxisEvent::Direction))); } InputController::~InputController() { - mInputMapDeinit(&m_inputMap); + for (auto& inputMap : m_inputMaps) { + mInputMapDeinit(&inputMap); + } #ifdef BUILD_SDL if (m_playerAttached) {@@ -87,6 +90,42 @@ }
#endif } +void InputController::addPlatform(mPlatform platform, const QString& visibleName, const mInputPlatformInfo* info) { + mInputMap* inputMap = &m_inputMaps[platform]; + mInputMapInit(inputMap, info); + + QMenu* input = m_inputMenu->addMenu(visibleName); + QMenu* autofire = m_autofireMenu->addMenu(visibleName); + m_inputMenuIndices[platform] = m_inputModel->addMenu(input, m_inputMenu.get()); + m_inputModel->addMenu(autofire, m_autofireMenu.get()); + + for (size_t i = 0; i < info->nKeys; ++i) { + m_inputModel->addKey(input, platform, i, 0, info->keyId[i], info->keyId[i]); + m_inputModel->addKey(autofire, platform, i, 0, info->keyId[i], info->keyId[i]); + } + +#ifdef BUILD_SDL + mSDLInitBindingsGBA(inputMap); +#endif + mInputBindKey(inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A); + mInputBindKey(inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B); + mInputBindKey(inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L); + mInputBindKey(inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R); + mInputBindKey(inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START); + mInputBindKey(inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT); + mInputBindKey(inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP); + mInputBindKey(inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN); + mInputBindKey(inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT); + mInputBindKey(inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT); +} + +void InputController::setPlatform(mPlatform platform) { +#ifdef BUILD_SDL + m_sdlPlayer.bindings = &m_inputMaps[platform]; +#endif + m_platform = platform; +} + void InputController::setConfiguration(ConfigController* config) { m_config = config; setAllowOpposing(config->getOption("allowOpposingDirections").toInt());@@ -99,26 +138,35 @@ }
loadConfiguration(SDL_BINDING_BUTTON); loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON)); #endif + restoreModel(); } void InputController::loadConfiguration(uint32_t type) { - mInputMapLoad(&m_inputMap, type, m_config->input()); + for (auto& inputMap : m_inputMaps) { + mInputMapLoad(&inputMap, type, m_config->input()); #ifdef BUILD_SDL - if (m_playerAttached) { - mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input()); - } + if (m_playerAttached) { + mInputMap* bindings = m_sdlPlayer.bindings; + m_sdlPlayer.bindings = &inputMap; + mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input()); + m_sdlPlayer.bindings = bindings; + } #endif + } } void InputController::loadProfile(uint32_t type, const QString& profile) { - bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData()); - recalibrateAxes(); - if (!loaded) { - const InputProfile* ip = InputProfile::findProfile(profile); - if (ip) { - ip->apply(this); + for (auto iter = m_inputMaps.begin(); iter != m_inputMaps.end(); ++iter) { + bool loaded = mInputProfileLoad(&iter.value(), type, m_config->input(), profile.toUtf8().constData()); + if (!loaded) { + const InputProfile* ip = InputProfile::findProfile(iter.key(), profile); + if (ip) { + ip->apply(iter.key(), this); + } } } + recalibrateAxes(); + m_inputModel->loadProfile(PLATFORM_NONE, profile); // TODO emit profileLoaded(profile); }@@ -135,12 +183,16 @@ #endif
} void InputController::saveConfiguration(uint32_t type) { - mInputMapSave(&m_inputMap, type, m_config->input()); + for (auto& inputMap : m_inputMaps) { + mInputMapSave(&inputMap, type, m_config->input()); + } m_config->write(); } void InputController::saveProfile(uint32_t type, const QString& profile) { - mInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData()); + for (auto& inputMap : m_inputMaps) { + mInputProfileSave(&inputMap, type, m_config->input(), profile.toUtf8().constData()); + } m_config->write(); }@@ -277,14 +329,6 @@ }
#endif } -GBAKey InputController::mapKeyboard(int key) const { - return static_cast<GBAKey>(mInputMapKey(&m_inputMap, KEYBOARD, key)); -} - -void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) { - return mInputBindKey(&m_inputMap, type, key, gbaKey); -} - void InputController::updateJoysticks() { #ifdef BUILD_SDL QString profile = profileForType(SDL_BINDING_BUTTON);@@ -296,6 +340,10 @@ }
#endif } +const mInputMap* InputController::map() { + return &m_inputMaps[m_platform]; +} + int InputController::pollEvents() { int activeButtons = 0; #ifdef BUILD_SDL@@ -305,7 +353,7 @@ SDL_JoystickUpdate();
int numButtons = SDL_JoystickNumButtons(joystick); int i; for (i = 0; i < numButtons; ++i) { - GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i)); + GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i)); if (key == GBA_KEY_NONE) { continue; }@@ -319,14 +367,14 @@ }
int numHats = SDL_JoystickNumHats(joystick); for (i = 0; i < numHats; ++i) { int hat = SDL_JoystickGetHat(joystick, i); - activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat); + activeButtons |= mInputMapHat(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, hat); } int numAxes = SDL_JoystickNumAxes(joystick); for (i = 0; i < numAxes; ++i) { int value = SDL_JoystickGetAxis(joystick, i); - enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value)); + enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, value)); if (key != GBA_KEY_NONE) { activeButtons |= 1 << key; }@@ -396,8 +444,25 @@ #endif
return activeAxes; } -void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) { - const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis); +void InputController::bindKey(mPlatform platform, uint32_t type, int key, int coreKey) { + QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]); + bool signalsBlocked = m_inputModel->blockSignals(true); + if (type != KEYBOARD) { + m_inputModel->updateButton(index, key); + } else { + m_inputModel->updateKey(index, key); + } + m_inputModel->blockSignals(signalsBlocked); + mInputBindKey(&m_inputMaps[platform], type, key, coreKey); +} + +void InputController::bindAxis(mPlatform platform, uint32_t type, int axis, GamepadAxisEvent::Direction direction, int key) { + QModelIndex index = m_inputModel->index(key, 0, m_inputMenuIndices[platform]); + bool signalsBlocked = m_inputModel->blockSignals(true); + m_inputModel->updateAxis(index, axis, direction); + m_inputModel->blockSignals(signalsBlocked); + + const mInputAxis* old = mInputQueryAxis(&m_inputMaps[platform], type, axis); mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD }; if (old) { description = *old;@@ -419,11 +484,7 @@ break;
default: return; } - mInputBindAxis(&m_inputMap, type, axis, &description); -} - -void InputController::unbindAllAxes(uint32_t type) { - mInputUnbindAllAxes(&m_inputMap, type); + mInputBindAxis(&m_inputMaps[platform], type, axis, &description); } QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {@@ -458,26 +519,29 @@ #endif
return activeHats; } -void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) { +void InputController::bindHat(mPlatform platform, uint32_t type, int hat, GamepadHatEvent::Direction direction, int coreKey) { + QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]); + //m_inputModel->updateHat(index, hat, direction); + mInputHatBindings bindings{ -1, -1, -1, -1 }; - mInputQueryHat(&m_inputMap, type, hat, &bindings); + mInputQueryHat(&m_inputMaps[platform], type, hat, &bindings); switch (direction) { case GamepadHatEvent::UP: - bindings.up = gbaKey; + bindings.up = coreKey; break; case GamepadHatEvent::RIGHT: - bindings.right = gbaKey; + bindings.right = coreKey; break; case GamepadHatEvent::DOWN: - bindings.down = gbaKey; + bindings.down = coreKey; break; case GamepadHatEvent::LEFT: - bindings.left = gbaKey; + bindings.left = coreKey; break; default: return; } - mInputBindHat(&m_inputMap, type, hat, &bindings); + mInputBindHat(&m_inputMaps[platform], type, hat, &bindings); } void InputController::testGamepad(int type) {@@ -569,15 +633,15 @@ }
QApplication::sendEvent(focusWidget, event); } -void InputController::postPendingEvent(GBAKey key) { +void InputController::postPendingEvent(int key) { m_pendingEvents.insert(key); } -void InputController::clearPendingEvent(GBAKey key) { +void InputController::clearPendingEvent(int key) { m_pendingEvents.remove(key); } -bool InputController::hasPendingEvent(GBAKey key) const { +bool InputController::hasPendingEvent(int key) const { return m_pendingEvents.contains(key); }@@ -614,3 +678,139 @@ if (focus == m_focusParent) {
m_focusParent = m_topLevel; } } + +void InputController::setupCallback(GameController* controller) { + m_inputModel->setKeyCallback([this, controller](QMenu* menu, int key, bool down) { + if (menu == m_autofireMenu.get()) { + controller->setAutofire(key, down); + } else { + if (down) { + controller->keyPressed(key); + } else { + controller->keyReleased(key); + } + } + }); +} + +void InputController::bindKey(const QModelIndex& index, int key) { + int coreKey = m_inputModel->keyAt(index); + if (coreKey < 0) { + return; + } + mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE); + bindKey(platform, KEYBOARD, key, coreKey); +} + +#ifdef BUILD_SDL +void InputController::bindButton(const QModelIndex& index, int key) { + int coreKey = m_inputModel->keyAt(index); + if (coreKey < 0) { + return; + } + mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE); + bindKey(platform, SDL_BINDING_BUTTON, key, coreKey); +} + +void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction) { + int coreKey = m_inputModel->keyAt(index); + if (coreKey < 0) { + return; + } + mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE); + bindAxis(platform, SDL_BINDING_BUTTON, axis, direction, coreKey); +} + +void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction direction) { + int coreKey = m_inputModel->keyAt(index); + if (coreKey < 0) { + return; + } + mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE); + bindHat(platform, SDL_BINDING_BUTTON, hat, direction, coreKey); +} +#else +void InputController::bindButton(const QModelIndex& index, int key, int) {} +void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction, int) {} +void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction, int) {} +#endif + +bool InputController::eventFilter(QObject*, QEvent* event) { + if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { + QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); + int key = keyEvent->key(); + if (!InputModel::isModifierKey(key)) { + key |= (keyEvent->modifiers() & ~Qt::KeypadModifier); + } else { + key = InputModel::toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier)); + } + + if (keyEvent->isAutoRepeat()) { + event->accept(); + return true; + } + + if (m_inputModel->triggerKey(key, event->type() == QEvent::KeyPress, m_platform)) { + event->accept(); + return true; + } + } + + + if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadButtonEvent::Up()) { + GamepadButtonEvent* gbe = static_cast<GamepadButtonEvent*>(event); + if (m_inputModel->triggerButton(gbe->value(), event->type() == GamepadButtonEvent::Down())) { + event->accept(); + return true; + } + } + if (event->type() == GamepadAxisEvent::Type()) { + GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event); + if (m_inputModel->triggerAxis(gae->axis(), gae->direction(), gae->isNew())) { + event->accept(); + return true; + } + } + return false; +} + +void InputController::restoreModel() { + bool signalsBlocked = m_inputModel->blockSignals(true); + for (auto iter = m_inputMaps.begin(); iter != m_inputMaps.end(); ++iter) { + mPlatform platform = iter.key(); + QModelIndex parent = m_inputMenuIndices[platform]; + int nKeys = iter->info->nKeys; + for (int i = 0; i < nKeys; ++i) { + int key = mInputQueryBinding(&iter.value(), KEYBOARD, i); + if (key >= 0) { + m_inputModel->updateKey(m_inputModel->index(i, 0, parent), key); + } else { + m_inputModel->clearKey(m_inputModel->index(i, 0, parent)); + } +#ifdef BUILD_SDL + key = mInputQueryBinding(&iter.value(), SDL_BINDING_BUTTON, i); + if (key >= 0) { + m_inputModel->updateButton(m_inputModel->index(i, 0, parent), key); + } else { + m_inputModel->clearButton(m_inputModel->index(i, 0, parent)); + } +#endif + } +#ifdef BUILD_SDL + struct Context { + InputModel* model; + QModelIndex parent; + } context{ m_inputModel, parent }; + mInputEnumerateAxes(&iter.value(), SDL_BINDING_BUTTON, [](int axis, const struct mInputAxis* description, void* user) { + Context* context = static_cast<Context*>(user); + if (description->highDirection >= 0) { + context->model->updateAxis(context->model->index(description->highDirection, 0, context->parent), axis, GamepadAxisEvent::POSITIVE); + } + if (description->lowDirection >= 0) { + context->model->updateAxis(context->model->index(description->lowDirection, 0, context->parent), axis, GamepadAxisEvent::NEGATIVE); + } + }, &context); +#endif + } + m_inputModel->blockSignals(signalsBlocked); +}
@@ -9,23 +9,29 @@
#include "GamepadAxisEvent.h" #include "GamepadHatEvent.h" +#include <QMap> #include <QObject> #include <QSet> #include <QTimer> #include <QVector> -#include <mgba/internal/gba/input.h> +#include <mgba/core/core.h> +#include <mgba/core/input.h> #ifdef BUILD_SDL #include "platform/sdl/sdl-events.h" #endif +class QMenu; + struct mRotationSource; struct mRumble; namespace QGBA { class ConfigController; +class GameController; +class InputModel; class InputController : public QObject { Q_OBJECT@@ -33,9 +39,12 @@
public: static const uint32_t KEYBOARD = 0x51545F4B; - InputController(int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr); + InputController(InputModel* model, int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr); ~InputController(); + void addPlatform(mPlatform, const QString& visibleName, const mInputPlatformInfo*); + void setPlatform(mPlatform); + void setConfiguration(ConfigController* config); void saveConfiguration(); void loadConfiguration(uint32_t type);@@ -47,11 +56,7 @@
bool allowOpposing() const { return m_allowOpposing; } void setAllowOpposing(bool allowOpposing) { m_allowOpposing = allowOpposing; } - GBAKey mapKeyboard(int key) const; - - void bindKey(uint32_t type, int key, GBAKey); - - const mInputMap* map() const { return &m_inputMap; } + const mInputMap* map(); int pollEvents();@@ -61,10 +66,9 @@ 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); + void bindKey(mPlatform platform, uint32_t type, int key, int); + void bindAxis(mPlatform platform, uint32_t type, int axis, GamepadAxisEvent::Direction, int); + void bindHat(mPlatform platform, uint32_t type, int hat, GamepadHatEvent::Direction, int); QStringList connectedGamepads(uint32_t type) const; int gamepad(uint32_t type) const;@@ -85,6 +89,8 @@
mRumble* rumble(); mRotationSource* rotationSource(); + void setupCallback(GameController* controller); + signals: void profileLoaded(const QString& profile);@@ -97,13 +103,25 @@ void suspendScreensaver();
void resumeScreensaver(); void setScreensaverSuspendable(bool); +private slots: + void bindKey(const QModelIndex&, int key); + void bindButton(const QModelIndex&, int key); + void bindAxis(const QModelIndex&, int axis, GamepadAxisEvent::Direction); + void bindHat(const QModelIndex&, int hat, GamepadHatEvent::Direction); + +protected: + bool eventFilter(QObject*, QEvent*) override; + private: - void postPendingEvent(GBAKey); - void clearPendingEvent(GBAKey); - bool hasPendingEvent(GBAKey) const; + void postPendingEvent(int key); + void clearPendingEvent(int key); + bool hasPendingEvent(int key) const; void sendGamepadEvent(QEvent*); + void restoreModel(); - mInputMap m_inputMap; + InputModel* m_inputModel; + mPlatform m_platform; + QMap<mPlatform, mInputMap> m_inputMaps; ConfigController* m_config; int m_playerId; bool m_allowOpposing;@@ -119,12 +137,16 @@ #endif
QVector<int> m_deadzones; + std::unique_ptr<QMenu> m_inputMenu; + std::unique_ptr<QMenu> m_autofireMenu; + QMap<mPlatform, QModelIndex> m_inputMenuIndices; + 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; + QSet<int> m_pendingEvents; }; }
@@ -12,11 +12,13 @@
InputItem::InputItem(QAction* action, const QString& name, InputItem* parent) : m_action(action) , m_shortcut(action->shortcut().isEmpty() ? 0 : action->shortcut()[0]) + , m_keys(-1) , m_menu(nullptr) , m_name(name) , m_button(-1) , m_axis(-1) , m_direction(GamepadAxisEvent::NEUTRAL) + , m_platform(PLATFORM_NONE) , m_parent(parent) { m_visibleName = action->text()@@ -28,12 +30,29 @@ InputItem::InputItem(InputItem::Functions functions, int shortcut, const QString& visibleName, const QString& name, InputItem* parent)
: m_action(nullptr) , m_shortcut(shortcut) , m_functions(functions) + , m_keys(-1) , m_menu(nullptr) , m_name(name) , m_visibleName(visibleName) , m_button(-1) , m_axis(-1) , m_direction(GamepadAxisEvent::NEUTRAL) + , m_platform(PLATFORM_NONE) + , m_parent(parent) +{ +} + +InputItem::InputItem(mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name, InputItem* parent) + : m_action(nullptr) + , m_shortcut(shortcut) + , m_keys(key) + , m_menu(nullptr) + , m_name(name) + , m_visibleName(visibleName) + , m_button(-1) + , m_axis(-1) + , m_direction(GamepadAxisEvent::NEUTRAL) + , m_platform(platform) , m_parent(parent) { }@@ -62,6 +81,10 @@ void InputItem::addFunctions(InputItem::Functions functions,
int shortcut, const QString& visibleName, const QString& name) { m_items.append(InputItem(functions, shortcut, visibleName, name, this)); +} + +void InputItem::addKey(mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name) { + m_items.append(InputItem(platform, key, shortcut, visibleName, name, this)); } void InputItem::addSubmenu(QMenu* menu) {
@@ -7,8 +7,11 @@ #ifndef QGBA_INPUT_ITEM
#define QGBA_INPUT_ITEM #include "GamepadAxisEvent.h" +#include "GamepadHatEvent.h" #include <QAction> + +#include <mgba/core/core.h> namespace QGBA {@@ -19,12 +22,15 @@
InputItem(QAction* action, const QString& name, InputItem* parent = nullptr); InputItem(Functions functions, int shortcut, const QString& visibleName, const QString& name, InputItem* parent = nullptr); + InputItem(mPlatform platform, int key, int shortcut, const QString& name, const QString& visibleName, InputItem* parent = nullptr); InputItem(QMenu* action, InputItem* parent = nullptr); QAction* action() { return m_action; } const QAction* action() const { return m_action; } - const int shortcut() const { return m_shortcut; } + int shortcut() const { return m_shortcut; } + mPlatform platform() const { return m_platform; } Functions functions() const { return m_functions; } + int key() const { return m_keys; } QMenu* menu() { return m_menu; } const QMenu* menu() const { return m_menu; } const QString& visibleName() const { return m_visibleName; }@@ -36,6 +42,7 @@ const InputItem* parent() const { return m_parent; }
void addAction(QAction* action, const QString& name); void addFunctions(Functions functions, int shortcut, const QString& visibleName, const QString& name); + void addKey(mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name); void addSubmenu(QMenu* menu); int button() const { return m_button; } void setShortcut(int sequence);@@ -57,7 +64,9 @@ QString m_name;
QString m_visibleName; int m_button; int m_axis; + int m_keys; GamepadAxisEvent::Direction m_direction; + mPlatform m_platform; QList<InputItem> m_items; InputItem* m_parent; };
@@ -87,10 +87,14 @@ if (!index.isValid() || !index.internalPointer()) {
return QModelIndex(); } InputItem* item = static_cast<InputItem*>(index.internalPointer()); - if (!item->parent() || !item->parent()->parent()) { + return this->index(item->parent()); +} + +QModelIndex InputModel::index(InputItem* item) const { + if (!item || !item->parent()) { return QModelIndex(); } - return createIndex(item->parent()->parent()->items().indexOf(*item->parent()), 0, item->parent()); + return createIndex(item->parent()->items().indexOf(*item), 0, item); } int InputModel::columnCount(const QModelIndex& index) const {@@ -105,38 +109,42 @@ const InputItem* item = static_cast<const InputItem*>(index.internalPointer());
return item->items().count(); } -void InputModel::addAction(QMenu* menu, QAction* action, const QString& name) { +InputItem* InputModel::add(QMenu* menu, std::function<void (InputItem*)> callback) { InputItem* smenu = m_menuMap[menu]; if (!smenu) { - return; + return nullptr; } - InputItem* pmenu = smenu->parent(); - int row = pmenu->items().indexOf(*smenu); - QModelIndex parent = createIndex(row, 0, smenu); + QModelIndex parent = index(smenu); beginInsertRows(parent, smenu->items().count(), smenu->items().count()); - smenu->addAction(action, name); + callback(smenu); endInsertRows(); InputItem* item = &smenu->items().last(); + emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), + createIndex(smenu->items().count() - 1, 2, item)); + return item; +} + +void InputModel::addAction(QMenu* menu, QAction* action, const QString& name) { + InputItem* item = add(menu, [&](InputItem* smenu) { + smenu->addAction(action, name); + }); + if (!item) { + return; + } if (m_config) { loadShortcuts(item); } - emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), - createIndex(smenu->items().count() - 1, 2, item)); } void InputModel::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, int shortcut, const QString& visibleName, const QString& name) { - InputItem* smenu = m_menuMap[menu]; - if (!smenu) { + InputItem* item = add(menu, [&](InputItem* smenu) { + smenu->addFunctions(qMakePair(press, release), shortcut, visibleName, name); + }); + if (!item) { return; } - InputItem* pmenu = smenu->parent(); - int row = pmenu->items().indexOf(*smenu); - QModelIndex parent = createIndex(row, 0, smenu); - beginInsertRows(parent, smenu->items().count(), smenu->items().count()); - smenu->addFunctions(qMakePair(press, release), shortcut, visibleName, name); - endInsertRows(); - InputItem* item = &smenu->items().last(); + bool loadedShortcut = false; if (m_config) { loadedShortcut = loadShortcuts(item);@@ -144,8 +152,6 @@ }
if (!loadedShortcut && !m_heldKeys.contains(shortcut)) { m_heldKeys[shortcut] = item; } - emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), - createIndex(smenu->items().count() - 1, 2, item)); } void InputModel::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,@@ -153,17 +159,22 @@ const QKeySequence& shortcut, const QString& visibleName, const QString& name) {
addFunctions(menu, press, release, shortcut[0], visibleName, name); } -void InputModel::addMenu(QMenu* menu, QMenu* parentMenu) { +void InputModel::addKey(QMenu* menu, mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name) { + InputItem* item = add(menu, [&](InputItem* smenu) { + smenu->addKey(platform, key, shortcut, visibleName, name); + }); + if (!item) { + return; + } + m_keys[qMakePair(platform, key)] = item; +} + +QModelIndex InputModel::addMenu(QMenu* menu, QMenu* parentMenu) { InputItem* smenu = m_menuMap[parentMenu]; if (!smenu) { smenu = &m_rootMenu; } - QModelIndex parent; - InputItem* pmenu = smenu->parent(); - if (pmenu) { - int row = pmenu->items().indexOf(*smenu); - parent = createIndex(row, 0, smenu); - } + QModelIndex parent = index(smenu); beginInsertRows(parent, smenu->items().count(), smenu->items().count()); smenu->addSubmenu(menu); endInsertRows();@@ -171,20 +182,35 @@ InputItem* item = &smenu->items().last();
emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), createIndex(smenu->items().count() - 1, 2, item)); m_menuMap[menu] = item; + return index(item); } InputItem* InputModel::itemAt(const QModelIndex& index) { if (!index.isValid()) { return nullptr; } - return static_cast<InputItem*>(index.internalPointer()); + if (index.internalPointer()) { + return static_cast<InputItem*>(index.internalPointer()); + } + if (!index.parent().isValid()) { + return nullptr; + } + InputItem* pmenu = static_cast<InputItem*>(index.parent().internalPointer()); + return &pmenu->items()[index.row()]; } const InputItem* InputModel::itemAt(const QModelIndex& index) const { if (!index.isValid()) { return nullptr; } - return static_cast<const InputItem*>(index.internalPointer()); + if (index.internalPointer()) { + return static_cast<InputItem*>(index.internalPointer()); + } + if (!index.parent().isValid()) { + return nullptr; + } + InputItem* pmenu = static_cast<InputItem*>(index.parent().internalPointer()); + return &pmenu->items()[index.row()]; } int InputModel::shortcutAt(const QModelIndex& index) const {@@ -193,6 +219,14 @@ if (!item) {
return 0; } return item->shortcut(); +} + +int InputModel::keyAt(const QModelIndex& index) const { + const InputItem* item = itemAt(index); + if (!item) { + return -1; + } + return item->key(); } bool InputModel::isMenuAt(const QModelIndex& index) const {@@ -216,22 +250,25 @@ updateKey(item, keySequence);
if (m_config) { m_config->setQtOption(item->name(), QKeySequence(keySequence).toString(), KEY_SECTION); } - emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), - createIndex(index.row(), 2, index.internalPointer())); } void InputModel::updateKey(InputItem* item, int keySequence) { int oldShortcut = item->shortcut(); - if (item->functions().first) { + if (item->functions().first || item->key() >= 0) { if (oldShortcut > 0) { m_heldKeys.take(oldShortcut); } - if (keySequence > 0) { - m_heldKeys[keySequence] = item; + if (keySequence >= 0) { + m_keys[qMakePair(item->platform(), keySequence)] = item; } } item->setShortcut(keySequence); + + emit dataChanged(createIndex(index(item).row(), 0, item), + createIndex(index(item).row(), 2, item)); + + emit keyRebound(index(item), keySequence); } void InputModel::updateButton(const QModelIndex& index, int button) {@@ -260,6 +297,8 @@ }
} emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), createIndex(index.row(), 2, index.internalPointer())); + + emit buttonRebound(index, button); } void InputModel::updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction) {@@ -296,6 +335,8 @@ }
} emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), createIndex(index.row(), 2, index.internalPointer())); + + emit axisRebound(index, axis, direction); } void InputModel::clearKey(const QModelIndex& index) {@@ -306,39 +347,35 @@ void InputModel::clearButton(const QModelIndex& index) {
updateButton(index, -1); } -bool InputModel::eventFilter(QObject*, QEvent* event) { - if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { - QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); - if (keyEvent->isAutoRepeat()) { - return false; - } - int key = keyEvent->key(); - if (!isModifierKey(key)) { - key |= (keyEvent->modifiers() & ~Qt::KeypadModifier); +bool InputModel::triggerKey(int keySequence, bool down, mPlatform platform) { + auto key = m_keys.find(qMakePair(platform, keySequence)); + if (key != m_keys.end()) { + m_keyCallback(key.value()->parent()->menu(), key.value()->key(), down); + return true; + } + auto heldKey = m_heldKeys.find(keySequence); + if (heldKey != m_heldKeys.end()) { + auto pair = heldKey.value()->functions(); + if (down) { + if (pair.first) { + pair.first(); + } } else { - key = toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier)); - } - auto item = m_heldKeys.find(key); - if (item != m_heldKeys.end()) { - auto pair = item.value()->functions(); - if (event->type() == QEvent::KeyPress) { - if (pair.first) { - pair.first(); - } - } else { - if (pair.second) { - pair.second(); - } + if (pair.second) { + pair.second(); } - event->accept(); - return true; } + return true; } - if (event->type() == GamepadButtonEvent::Down()) { - auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value()); - if (item == m_buttons.end()) { - return false; - } + return false; +} + +bool InputModel::triggerButton(int button, bool down) { + auto item = m_buttons.find(button); + if (item == m_buttons.end()) { + return false; + } + if (down) { QAction* action = item.value()->action(); if (action && action->isEnabled()) { action->trigger();@@ -347,47 +384,37 @@ auto pair = item.value()->functions();
if (pair.first) { pair.first(); } - event->accept(); - return true; - } - if (event->type() == GamepadButtonEvent::Up()) { - auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value()); - if (item == m_buttons.end()) { - return false; - } + } else { auto pair = item.value()->functions(); if (pair.second) { pair.second(); } - event->accept(); - return true; } - if (event->type() == GamepadAxisEvent::Type()) { - GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event); - auto item = m_axes.find(qMakePair(gae->axis(), gae->direction())); - if (item == m_axes.end()) { - return false; + return true; +} + +bool InputModel::triggerAxis(int axis, GamepadAxisEvent::Direction direction, bool isNew) { + auto item = m_axes.find(qMakePair(axis, direction)); + if (item == m_axes.end()) { + return false; + } + if (isNew) { + QAction* action = item.value()->action(); + if (action && action->isEnabled()) { + action->trigger(); } - if (gae->isNew()) { - QAction* action = item.value()->action(); - if (action && action->isEnabled()) { - action->trigger(); - } + } + auto pair = item.value()->functions(); + if (isNew) { + if (pair.first) { + pair.first(); } - auto pair = item.value()->functions(); - if (gae->isNew()) { - if (pair.first) { - pair.first(); - } - } else { - if (pair.second) { - pair.second(); - } + } else { + if (pair.second) { + pair.second(); } - event->accept(); - return true; } - return false; + return true; } bool InputModel::loadShortcuts(InputItem* item) {@@ -462,9 +489,9 @@ }
} } -void InputModel::loadProfile(const QString& profile) { +void InputModel::loadProfile(mPlatform platform, const QString& profile) { m_profileName = profile; - m_profile = InputProfile::findProfile(profile); + m_profile = InputProfile::findProfile(platform, profile); onSubitems(&m_rootMenu, [this](InputItem* item) { loadGamepadShortcuts(item); });@@ -534,5 +561,4 @@ default:
break; } return modifiers; - }
@@ -6,6 +6,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef QGBA_INPUT_MODEL #define QGBA_INPUT_MODEL +#include <mgba/core/core.h> + #include "GamepadAxisEvent.h" #include "InputItem.h"@@ -38,6 +40,7 @@ InputModel(QObject* parent = nullptr);
void setConfigController(ConfigController* controller); void setProfile(const QString& profile); + void setKeyCallback(std::function<void (QMenu*, int, bool)> callback) { m_keyCallback = callback; } virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;@@ -50,18 +53,21 @@ virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
void addAction(QMenu* menu, QAction* action, const QString& name); void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, - int shortcut, const QString& visibleName, const QString& name); + int shortcut, const QString& visibleName, const QString& name); void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, - const QKeySequence& shortcut, const QString& visibleName, const QString& name); - void addMenu(QMenu* menu, QMenu* parent = nullptr); + const QKeySequence& shortcut, const QString& visibleName, const QString& name); + void addKey(QMenu* menu, mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name); + QModelIndex addMenu(QMenu* menu, QMenu* parent = nullptr); QAction* getAction(const QString& name); int shortcutAt(const QModelIndex& index) const; + int keyAt(const QModelIndex& index) const; bool isMenuAt(const QModelIndex& index) const; void updateKey(const QModelIndex& index, int keySequence); void updateButton(const QModelIndex& index, int button); - void updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction); + void updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction); + void updateHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction); void clearKey(const QModelIndex& index); void clearButton(const QModelIndex& index);@@ -70,13 +76,21 @@ static int toModifierShortcut(const QString& shortcut);
static bool isModifierKey(int key); static int toModifierKey(int key); -public slots: - void loadProfile(const QString& profile); + void loadProfile(mPlatform platform, const QString& profile); -protected: - bool eventFilter(QObject*, QEvent*) override; + bool triggerKey(int keySequence, bool down, mPlatform platform = PLATFORM_NONE); + bool triggerButton(int button, bool down); + bool triggerAxis(int axis, GamepadAxisEvent::Direction, bool isNew); + bool triggerHat(int hat, GamepadHatEvent::Direction); + +signals: + void keyRebound(const QModelIndex&, int keySequence); + void buttonRebound(const QModelIndex&, int button); + void axisRebound(const QModelIndex& index, int axis, GamepadAxisEvent::Direction); + void hatRebound(const QModelIndex& index, int hat, GamepadHatEvent::Direction); private: + InputItem* add(QMenu* menu, std::function<void (InputItem*)>); InputItem* itemAt(const QModelIndex& index); const InputItem* itemAt(const QModelIndex& index) const; bool loadShortcuts(InputItem*);@@ -84,12 +98,16 @@ void loadGamepadShortcuts(InputItem*);
void onSubitems(InputItem*, std::function<void(InputItem*)> func); void updateKey(InputItem* item, int keySequence); + QModelIndex index(InputItem* item) const; + InputItem m_rootMenu; QMap<QMenu*, InputItem*> m_menuMap; QMap<int, InputItem*> m_buttons; QMap<QPair<int, GamepadAxisEvent::Direction>, InputItem*> m_axes; QMap<int, InputItem*> m_heldKeys; + QMap<QPair<mPlatform, int>, InputItem*> m_keys; ConfigController* m_config; + std::function<void (QMenu*, int key, bool down)> m_keyCallback; QString m_profileName; const InputProfile* m_profile; };
@@ -199,7 +199,8 @@ , m_gyroSensitivity(gyroSensitivity)
{ } -const InputProfile* InputProfile::findProfile(const QString& name) { +const InputProfile* InputProfile::findProfile(mPlatform platform, const QString& name) { + // TODO: Use platform for (size_t i = 0; i < sizeof(s_defaultMaps) / sizeof(*s_defaultMaps); ++i) { QRegExp re(s_defaultMaps[i].m_profileName); if (re.exactMatch(name)) {@@ -209,11 +210,11 @@ }
return nullptr; } -void InputProfile::apply(InputController* controller) const { +void InputProfile::apply(mPlatform platform, InputController* controller) const { for (size_t i = 0; i < GBA_KEY_MAX; ++i) { #ifdef BUILD_SDL - controller->bindKey(SDL_BINDING_BUTTON, m_keys[i], static_cast<GBAKey>(i)); - controller->bindAxis(SDL_BINDING_BUTTON, m_axes[i].axis, m_axes[i].direction, static_cast<GBAKey>(i)); + controller->bindKey(platform, SDL_BINDING_BUTTON, m_keys[i], static_cast<GBAKey>(i)); + controller->bindAxis(platform, SDL_BINDING_BUTTON, m_axes[i].axis, m_axes[i].direction, static_cast<GBAKey>(i)); #endif } controller->registerTiltAxisX(m_tiltAxis.x);
@@ -8,6 +8,7 @@ #define QGBA_INPUT_PROFILE
#include "GamepadAxisEvent.h" +#include <mgba/core/core.h> #include <mgba/gba/interface.h> namespace QGBA {@@ -16,9 +17,9 @@ class InputController;
class InputProfile { public: - static const InputProfile* findProfile(const QString& name); + static const InputProfile* findProfile(mPlatform platform, const QString& name); - void apply(InputController*) const; + void apply(mPlatform platform, InputController*) const; bool lookupShortcutButton(const QString& shortcut, int* button) const; bool lookupShortcutAxis(const QString& shortcut, int* axis, GamepadAxisEvent::Direction* direction) const;
@@ -9,7 +9,6 @@ #include "AudioProcessor.h"
#include "ConfigController.h" #include "Display.h" #include "GBAApp.h" -#include "GBAKeyEditor.h" #include "InputController.h" #include "ShortcutView.h"@@ -137,31 +136,7 @@ connect(m_ui.gbcBiosBrowse, &QPushButton::clicked, [this]() {
selectBios(m_ui.gbcBios); }); - GBAKeyEditor* editor = new GBAKeyEditor(inputController, InputController::KEYBOARD, QString(), this); - m_ui.stackedWidget->addWidget(editor); - m_ui.tabs->addItem(tr("Keyboard")); - connect(m_ui.buttonBox, SIGNAL(accepted()), editor, SLOT(save())); - - GBAKeyEditor* buttonEditor = nullptr; -#ifdef BUILD_SDL - inputController->recalibrateAxes(); - const char* profile = inputController->profileForType(SDL_BINDING_BUTTON); - buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile); - m_ui.stackedWidget->addWidget(buttonEditor); - m_ui.tabs->addItem(tr("Controllers")); - connect(m_ui.buttonBox, SIGNAL(accepted()), buttonEditor, SLOT(save())); -#endif - connect(m_ui.buttonBox, SIGNAL(accepted()), this, SLOT(updateConfig())); - connect(m_ui.buttonBox, &QDialogButtonBox::clicked, [this, editor, buttonEditor](QAbstractButton* button) { - if (m_ui.buttonBox->buttonRole(button) == QDialogButtonBox::ApplyRole) { - updateConfig(); - editor->save(); - if (buttonEditor) { - buttonEditor->save(); - } - } - }); ShortcutView* shortcutView = new ShortcutView(); shortcutView->setModel(inputModel);
@@ -51,6 +51,7 @@ #include <mgba/internal/gb/video.h>
#endif #ifdef M_CORE_GBA #include <mgba/internal/gba/gba.h> +#include <mgba/internal/gba/input.h> #include <mgba/internal/gba/video.h> #endif #include "feature/commandline.h"@@ -67,7 +68,8 @@ , m_stateWindow(nullptr)
, m_screenWidget(new WindowBackground()) , m_logo(":/res/mgba-1024.png") , m_config(config) - , m_inputController(playerId, this) + , m_inputModel(new InputModel(this)) + , m_inputController(m_inputModel, playerId, this) #ifdef USE_FFMPEG , m_videoView(nullptr) #endif@@ -81,7 +83,6 @@ #ifdef USE_DEBUGGERS
, m_console(nullptr) #endif , m_mruMenu(nullptr) - , m_inputModel(new InputModel(this)) , m_fullscreenOnStart(false) , m_autoresume(false) , m_wasOpened(false)@@ -194,7 +195,6 @@ });
connect(m_display, &Display::showCursor, [this]() { m_screenWidget->unsetCursor(); }); - connect(&m_inputController, SIGNAL(profileLoaded(const QString&)), m_inputModel, SLOT(loadProfile(const QString&))); m_log.setLevels(mLOG_WARN | mLOG_ERROR | mLOG_FATAL); m_fpsTimer.setInterval(FPS_TIMER_INTERVAL);@@ -202,6 +202,14 @@ m_focusCheck.setInterval(200);
m_inputModel->setConfigController(m_config); setupMenu(menuBar()); + +#ifdef M_CORE_GBA + m_inputController.addPlatform(PLATFORM_GBA, tr("Game Boy Advance"), &GBAInputInfo); +#endif +#ifdef M_CORE_GB + m_inputController.addPlatform(PLATFORM_GB, tr("Game Boy"), &GBAInputInfo); +#endif + m_inputController.setupCallback(m_controller); } Window::~Window() {@@ -559,34 +567,6 @@ openView(window);
} #endif -void Window::keyPressEvent(QKeyEvent* event) { - if (event->isAutoRepeat()) { - QWidget::keyPressEvent(event); - return; - } - GBAKey key = m_inputController.mapKeyboard(event->key()); - if (key == GBA_KEY_NONE) { - QWidget::keyPressEvent(event); - return; - } - m_controller->keyPressed(key); - event->accept(); -} - -void Window::keyReleaseEvent(QKeyEvent* event) { - if (event->isAutoRepeat()) { - QWidget::keyReleaseEvent(event); - return; - } - GBAKey key = m_inputController.mapKeyboard(event->key()); - if (key == GBA_KEY_NONE) { - QWidget::keyPressEvent(event); - return; - } - m_controller->keyReleased(key); - event->accept(); -} - void Window::resizeEvent(QResizeEvent* event) { if (!isFullScreen()) { m_config->setOption("height", m_screenWidget->height());@@ -755,6 +735,8 @@ menuBar()->hide();
} #endif + m_inputController.setPlatform(m_controller->platform()); + m_hitUnimplementedBiosCall = false; m_fpsTimer.start(); m_focusCheck.start();@@ -923,7 +905,7 @@ void Window::setupMenu(QMenuBar* menubar) {
menubar->clear(); QMenu* fileMenu = menubar->addMenu(tr("&File")); m_inputModel->addMenu(fileMenu); - installEventFilter(m_inputModel); + installEventFilter(&m_inputController); addControlledAction(fileMenu, fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open), "loadROM"); #ifdef USE_SQLITE3@@ -1479,69 +1461,6 @@ QAction* exitFullScreen = new QAction(tr("Exit fullscreen"), frameMenu);
connect(exitFullScreen, SIGNAL(triggered()), this, SLOT(exitFullScreen())); exitFullScreen->setShortcut(QKeySequence("Esc")); addHiddenAction(frameMenu, exitFullScreen, "exitFullScreen"); - - QMenu* autofireMenu = new QMenu(tr("Autofire"), this); - m_inputModel->addMenu(autofireMenu); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_A, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_A, false); - }, QKeySequence(), tr("Autofire A"), "autofireA"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_B, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_B, false); - }, QKeySequence(), tr("Autofire B"), "autofireB"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_L, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_L, false); - }, QKeySequence(), tr("Autofire L"), "autofireL"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_R, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_R, false); - }, QKeySequence(), tr("Autofire R"), "autofireR"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_START, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_START, false); - }, QKeySequence(), tr("Autofire Start"), "autofireStart"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_SELECT, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_SELECT, false); - }, QKeySequence(), tr("Autofire Select"), "autofireSelect"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_UP, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_UP, false); - }, QKeySequence(), tr("Autofire Up"), "autofireUp"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_RIGHT, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_RIGHT, false); - }, QKeySequence(), tr("Autofire Right"), "autofireRight"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_DOWN, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_DOWN, false); - }, QKeySequence(), tr("Autofire Down"), "autofireDown"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_LEFT, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_LEFT, false); - }, QKeySequence(), tr("Autofire Left"), "autofireLeft"); foreach (QAction* action, m_gameActions) { action->setDisabled(true);
@@ -98,8 +98,6 @@ void gdbOpen();
#endif protected: - virtual void keyPressEvent(QKeyEvent* event) override; - virtual void keyReleaseEvent(QKeyEvent* event) override; virtual void resizeEvent(QResizeEvent*) override; virtual void showEvent(QShowEvent*) override; virtual void closeEvent(QCloseEvent*) override;@@ -168,12 +166,12 @@ LoadSaveState* m_stateWindow;
WindowBackground* m_screenWidget; QPixmap m_logo; ConfigController* m_config; + InputModel* m_inputModel; InputController m_inputController; QList<QDateTime> m_frameList; QTimer m_fpsTimer; QList<QString> m_mruFiles; QMenu* m_mruMenu; - InputModel* m_inputModel; ShaderSelector* m_shaderView; bool m_fullscreenOnStart; QTimer m_focusCheck;