diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 89d821f82..01d006a2b 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -75,7 +75,6 @@ set(SOURCE_FILES DisplayGL.cpp DisplayQt.cpp GBAApp.cpp - GBAKeyEditor.cpp GIFView.cpp GameController.cpp GamepadAxisEvent.cpp diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp deleted file mode 100644 index 1f5aafeed..000000000 --- a/src/platform/qt/GBAKeyEditor.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include - -#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{ - 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(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(user); - if (description->highDirection != GBA_KEY_NONE) { - KeyEditor* key = self->keyById(static_cast(description->highDirection)); - if (key) { - key->setValueAxis(axis, description->deadHigh); - } - } - if (description->lowDirection != GBA_KEY_NONE) { - KeyEditor* key = self->keyById(static_cast(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(bindings.up)); - if (key) { - key->setValueHat(i, GamepadHatEvent::UP); - } - } - if (bindings.right >= 0) { - KeyEditor* key = keyById(static_cast(bindings.right)); - if (key) { - key->setValueHat(i, GamepadHatEvent::RIGHT); - } - } - if (bindings.down >= 0) { - KeyEditor* key = keyById(static_cast(bindings.down)); - if (key) { - key->setValueHat(i, GamepadHatEvent::DOWN); - } - } - if (bindings.left >= 0) { - KeyEditor* key = keyById(static_cast(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 diff --git a/src/platform/qt/GBAKeyEditor.h b/src/platform/qt/GBAKeyEditor.h deleted file mode 100644 index 1df22ff70..000000000 --- a/src/platform/qt/GBAKeyEditor.h +++ /dev/null @@ -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 -#include -#include -#include - -#include - -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 m_keyOrder; - QList::iterator m_currentKey; - - uint32_t m_type; - QString m_profile; - InputController* m_controller; - - QPicture m_background; -}; - -} - -#endif diff --git a/src/platform/qt/GamepadAxisEvent.cpp b/src/platform/qt/GamepadAxisEvent.cpp index 59e9ea0f7..833e82f3f 100644 --- a/src/platform/qt/GamepadAxisEvent.cpp +++ b/src/platform/qt/GamepadAxisEvent.cpp @@ -27,6 +27,7 @@ GamepadAxisEvent::GamepadAxisEvent(int axis, Direction direction, bool isNew, in QEvent::Type GamepadAxisEvent::Type() { if (s_type == None) { + qRegisterMetaType("GamepadAxisEvent::Direction"); s_type = static_cast(registerEventType()); } return s_type; diff --git a/src/platform/qt/GamepadHatEvent.cpp b/src/platform/qt/GamepadHatEvent.cpp index 9ca1dce32..65c9a633c 100644 --- a/src/platform/qt/GamepadHatEvent.cpp +++ b/src/platform/qt/GamepadHatEvent.cpp @@ -27,6 +27,7 @@ GamepadHatEvent::GamepadHatEvent(QEvent::Type pressType, int hatId, Direction di QEvent::Type GamepadHatEvent::Down() { if (s_downType == None) { + qRegisterMetaType("GamepadHatEvent::Direction"); s_downType = static_cast(registerEventType()); } return s_downType; diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 2d8d8025d..99a86e093 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -6,11 +6,15 @@ #include "InputController.h" #include "ConfigController.h" +#include "GameController.h" #include "GamepadAxisEvent.h" #include "GamepadButtonEvent.h" +#include "InputModel.h" #include "InputProfile.h" #include +#include +#include #include #include @@ -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 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren , 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 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren 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(new QMenu(tr("Autofire"))); + m_inputModel->addMenu(m_autofireMenu.get()); + + m_inputMenu = std::unique_ptr(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 @@ InputController::~InputController() { #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 @@ void InputController::setConfiguration(ConfigController* config) { 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 @@ void InputController::saveConfiguration() { } 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 @@ void InputController::setGyroSensitivity(float sensitivity) { #endif } -GBAKey InputController::mapKeyboard(int key) const { - return static_cast(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 @@ void InputController::updateJoysticks() { #endif } +const mInputMap* InputController::map() { + return &m_inputMaps[m_platform]; +} + int InputController::pollEvents() { int activeButtons = 0; #ifdef BUILD_SDL @@ -305,7 +353,7 @@ int InputController::pollEvents() { int numButtons = SDL_JoystickNumButtons(joystick); int i; for (i = 0; i < numButtons; ++i) { - GBAKey key = static_cast(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i)); + GBAKey key = static_cast(mInputMapKey(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i)); if (key == GBA_KEY_NONE) { continue; } @@ -319,14 +367,14 @@ int InputController::pollEvents() { 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(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value)); + enum GBAKey key = static_cast(mInputMapAxis(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, value)); if (key != GBA_KEY_NONE) { activeButtons |= 1 << key; } @@ -396,8 +444,25 @@ QSet> InputController::activeGamepadAxes 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 @@ void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direct 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> InputController::activeGamepadHats(int type) { @@ -458,26 +519,29 @@ QSet> InputController::activeGamepadHats( 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 @@ void InputController::sendGamepadEvent(QEvent* event) { 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 @@ void InputController::releaseFocus(QWidget* focus) { 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(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(event); + if (m_inputModel->triggerButton(gbe->value(), event->type() == GamepadButtonEvent::Down())) { + event->accept(); + return true; + } + } + if (event->type() == GamepadAxisEvent::Type()) { + GamepadAxisEvent* gae = static_cast(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(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); +} diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index 95382ce80..bdf7ba2e9 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -9,23 +9,29 @@ #include "GamepadAxisEvent.h" #include "GamepadHatEvent.h" +#include #include #include #include #include -#include +#include +#include #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 @@ Q_OBJECT 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 @@ public: 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 @@ public: QSet> 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 @@ public: mRumble* rumble(); mRotationSource* rotationSource(); + void setupCallback(GameController* controller); + signals: void profileLoaded(const QString& profile); @@ -97,13 +103,25 @@ public slots: void resumeScreensaver(); void setScreensaverSuspendable(bool); -private: - void postPendingEvent(GBAKey); - void clearPendingEvent(GBAKey); - bool hasPendingEvent(GBAKey) const; - void sendGamepadEvent(QEvent*); +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); - mInputMap m_inputMap; +protected: + bool eventFilter(QObject*, QEvent*) override; + +private: + void postPendingEvent(int key); + void clearPendingEvent(int key); + bool hasPendingEvent(int key) const; + void sendGamepadEvent(QEvent*); + void restoreModel(); + + InputModel* m_inputModel; + mPlatform m_platform; + QMap m_inputMaps; ConfigController* m_config; int m_playerId; bool m_allowOpposing; @@ -119,12 +137,16 @@ private: QVector m_deadzones; + std::unique_ptr m_inputMenu; + std::unique_ptr m_autofireMenu; + QMap m_inputMenuIndices; + QSet m_activeButtons; QSet> m_activeAxes; QSet> m_activeHats; QTimer m_gamepadTimer; - QSet m_pendingEvents; + QSet m_pendingEvents; }; } diff --git a/src/platform/qt/InputItem.cpp b/src/platform/qt/InputItem.cpp index 060b0f0af..f68a66763 100644 --- a/src/platform/qt/InputItem.cpp +++ b/src/platform/qt/InputItem.cpp @@ -12,11 +12,13 @@ using namespace QGBA; 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 : 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) { } @@ -64,6 +83,10 @@ void InputItem::addFunctions(InputItem::Functions functions, 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) { m_items.append(InputItem(menu, this)); } diff --git a/src/platform/qt/InputItem.h b/src/platform/qt/InputItem.h index 7a40d669b..4b7871d15 100644 --- a/src/platform/qt/InputItem.h +++ b/src/platform/qt/InputItem.h @@ -7,9 +7,12 @@ #define QGBA_INPUT_ITEM #include "GamepadAxisEvent.h" +#include "GamepadHatEvent.h" #include +#include + namespace QGBA { class InputItem { @@ -19,12 +22,15 @@ public: 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 @@ public: 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 @@ private: QString m_visibleName; int m_button; int m_axis; + int m_keys; GamepadAxisEvent::Direction m_direction; + mPlatform m_platform; QList m_items; InputItem* m_parent; }; diff --git a/src/platform/qt/InputModel.cpp b/src/platform/qt/InputModel.cpp index 92df9022d..ebf94b820 100644 --- a/src/platform/qt/InputModel.cpp +++ b/src/platform/qt/InputModel.cpp @@ -87,10 +87,14 @@ QModelIndex InputModel::parent(const QModelIndex& index) const { return QModelIndex(); } InputItem* item = static_cast(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 @@ int InputModel::rowCount(const QModelIndex& index) const { return item->items().count(); } -void InputModel::addAction(QMenu* menu, QAction* action, const QString& name) { +InputItem* InputModel::add(QMenu* menu, std::function 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 press, std::function 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 @@ void InputModel::addFunctions(QMenu* menu, std::function press, std::fun 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 press, std::function release, @@ -153,17 +159,22 @@ void InputModel::addFunctions(QMenu* menu, std::function press, std::fun 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 @@ void InputModel::addMenu(QMenu* menu, QMenu* parentMenu) { 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(index.internalPointer()); + if (index.internalPointer()) { + return static_cast(index.internalPointer()); + } + if (!index.parent().isValid()) { + return nullptr; + } + InputItem* pmenu = static_cast(index.parent().internalPointer()); + return &pmenu->items()[index.row()]; } const InputItem* InputModel::itemAt(const QModelIndex& index) const { if (!index.isValid()) { return nullptr; } - return static_cast(index.internalPointer()); + if (index.internalPointer()) { + return static_cast(index.internalPointer()); + } + if (!index.parent().isValid()) { + return nullptr; + } + InputItem* pmenu = static_cast(index.parent().internalPointer()); + return &pmenu->items()[index.row()]; } int InputModel::shortcutAt(const QModelIndex& index) const { @@ -195,6 +221,14 @@ int InputModel::shortcutAt(const QModelIndex& index) const { 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 { const InputItem* item = itemAt(index); if (!item) { @@ -216,22 +250,25 @@ void InputModel::updateKey(const QModelIndex& index, int 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 @@ void InputModel::updateButton(const QModelIndex& index, int button) { } 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 @@ void InputModel::updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent } 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,76 +347,16 @@ 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(event); - if (keyEvent->isAutoRepeat()) { - return false; - } - int key = keyEvent->key(); - if (!isModifierKey(key)) { - key |= (keyEvent->modifiers() & ~Qt::KeypadModifier); - } 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(); - } - } - event->accept(); - return true; - } - } - if (event->type() == GamepadButtonEvent::Down()) { - auto item = m_buttons.find(static_cast(event)->value()); - if (item == m_buttons.end()) { - return false; - } - QAction* action = item.value()->action(); - if (action && action->isEnabled()) { - action->trigger(); - } - auto pair = item.value()->functions(); - if (pair.first) { - pair.first(); - } - event->accept(); +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; } - if (event->type() == GamepadButtonEvent::Up()) { - auto item = m_buttons.find(static_cast(event)->value()); - if (item == m_buttons.end()) { - return false; - } - auto pair = item.value()->functions(); - if (pair.second) { - pair.second(); - } - event->accept(); - return true; - } - if (event->type() == GamepadAxisEvent::Type()) { - GamepadAxisEvent* gae = static_cast(event); - auto item = m_axes.find(qMakePair(gae->axis(), gae->direction())); - if (item == m_axes.end()) { - return false; - } - if (gae->isNew()) { - QAction* action = item.value()->action(); - if (action && action->isEnabled()) { - action->trigger(); - } - } - auto pair = item.value()->functions(); - if (gae->isNew()) { + auto heldKey = m_heldKeys.find(keySequence); + if (heldKey != m_heldKeys.end()) { + auto pair = heldKey.value()->functions(); + if (down) { if (pair.first) { pair.first(); } @@ -384,12 +365,58 @@ bool InputModel::eventFilter(QObject*, QEvent* event) { pair.second(); } } - event->accept(); return true; } 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(); + } + auto pair = item.value()->functions(); + if (pair.first) { + pair.first(); + } + } else { + auto pair = item.value()->functions(); + if (pair.second) { + pair.second(); + } + } + 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(); + } + } + auto pair = item.value()->functions(); + if (isNew) { + if (pair.first) { + pair.first(); + } + } else { + if (pair.second) { + pair.second(); + } + } + return true; +} + bool InputModel::loadShortcuts(InputItem* item) { if (item->name().isNull()) { return false; @@ -462,9 +489,9 @@ void InputModel::loadGamepadShortcuts(InputItem* item) { } } -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 @@ int InputModel::toModifierKey(int key) { break; } return modifiers; - } diff --git a/src/platform/qt/InputModel.h b/src/platform/qt/InputModel.h index a0d5ba4d8..2d1044d2c 100644 --- a/src/platform/qt/InputModel.h +++ b/src/platform/qt/InputModel.h @@ -6,6 +6,8 @@ #ifndef QGBA_INPUT_MODEL #define QGBA_INPUT_MODEL +#include + #include "GamepadAxisEvent.h" #include "InputItem.h" @@ -38,6 +40,7 @@ public: void setConfigController(ConfigController* controller); void setProfile(const QString& profile); + void setKeyCallback(std::function 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 @@ public: void addAction(QMenu* menu, QAction* action, const QString& name); void addFunctions(QMenu* menu, std::function press, std::function release, - int shortcut, const QString& visibleName, const QString& name); + int shortcut, const QString& visibleName, const QString& name); void addFunctions(QMenu* menu, std::function press, std::function 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 @@ public: 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); InputItem* itemAt(const QModelIndex& index); const InputItem* itemAt(const QModelIndex& index) const; bool loadShortcuts(InputItem*); @@ -84,12 +98,16 @@ private: void onSubitems(InputItem*, std::function func); void updateKey(InputItem* item, int keySequence); + QModelIndex index(InputItem* item) const; + InputItem m_rootMenu; QMap m_menuMap; QMap m_buttons; QMap, InputItem*> m_axes; QMap m_heldKeys; + QMap, InputItem*> m_keys; ConfigController* m_config; + std::function m_keyCallback; QString m_profileName; const InputProfile* m_profile; }; diff --git a/src/platform/qt/InputProfile.cpp b/src/platform/qt/InputProfile.cpp index 80aece552..5499f874e 100644 --- a/src/platform/qt/InputProfile.cpp +++ b/src/platform/qt/InputProfile.cpp @@ -199,7 +199,8 @@ constexpr InputProfile::InputProfile(const char* name, { } -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 @@ const InputProfile* InputProfile::findProfile(const QString& name) { 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(i)); - controller->bindAxis(SDL_BINDING_BUTTON, m_axes[i].axis, m_axes[i].direction, static_cast(i)); + controller->bindKey(platform, SDL_BINDING_BUTTON, m_keys[i], static_cast(i)); + controller->bindAxis(platform, SDL_BINDING_BUTTON, m_axes[i].axis, m_axes[i].direction, static_cast(i)); #endif } controller->registerTiltAxisX(m_tiltAxis.x); diff --git a/src/platform/qt/InputProfile.h b/src/platform/qt/InputProfile.h index 431bd4b4f..37e608cd6 100644 --- a/src/platform/qt/InputProfile.h +++ b/src/platform/qt/InputProfile.h @@ -8,6 +8,7 @@ #include "GamepadAxisEvent.h" +#include #include 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; diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 9fb55267a..162720131 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -9,7 +9,6 @@ #include "ConfigController.h" #include "Display.h" #include "GBAApp.h" -#include "GBAKeyEditor.h" #include "InputController.h" #include "ShortcutView.h" @@ -137,31 +136,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC 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); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 960783429..11003001e 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -51,6 +51,7 @@ #endif #ifdef M_CORE_GBA #include +#include #include #endif #include "feature/commandline.h" @@ -67,7 +68,8 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) , 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 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) , 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 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) 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 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) 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 @@ void Window::consoleOpen() { } #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 @@ void Window::gameStarted(mCoreThread* context, const QString& fname) { } #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 @@ -1480,69 +1462,6 @@ void Window::setupMenu(QMenuBar* menubar) { 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); } diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 2d4605d48..058848185 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -98,8 +98,6 @@ public slots: #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 @@ private: WindowBackground* m_screenWidget; QPixmap m_logo; ConfigController* m_config; + InputModel* m_inputModel; InputController m_inputController; QList m_frameList; QTimer m_fpsTimer; QList m_mruFiles; QMenu* m_mruMenu; - InputModel* m_inputModel; ShaderSelector* m_shaderView; bool m_fullscreenOnStart; QTimer m_focusCheck;