From 53c586044d76775bb0226a6c92ca61f0c1844467 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 4 Jan 2015 02:16:43 -0800 Subject: [PATCH] Qt: Allow shortcuts to be controlled with a gamepad --- src/platform/qt/CMakeLists.txt | 1 - src/platform/qt/GBAKeyEditor.cpp | 7 +- src/platform/qt/GBAKeyEditor.h | 3 - src/platform/qt/GamepadMonitor.cpp | 46 ------- src/platform/qt/GamepadMonitor.h | 40 ------ src/platform/qt/InputController.cpp | 37 +++++- src/platform/qt/InputController.h | 25 +++- src/platform/qt/KeyEditor.h | 10 +- src/platform/qt/ShortcutController.cpp | 47 ++++++- src/platform/qt/ShortcutController.h | 10 ++ src/platform/qt/ShortcutView.cpp | 17 +++ src/platform/qt/ShortcutView.h | 5 + src/platform/qt/ShortcutView.ui | 165 +++++++++++++++---------- src/platform/qt/Window.cpp | 3 + 14 files changed, 241 insertions(+), 175 deletions(-) delete mode 100644 src/platform/qt/GamepadMonitor.cpp delete mode 100644 src/platform/qt/GamepadMonitor.h diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index d4bf5a9f4..56a746921 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -44,7 +44,6 @@ set(SOURCE_FILES GIFView.cpp GameController.cpp GamePakView.cpp - GamepadMonitor.cpp InputController.cpp KeyEditor.cpp LoadSaveState.cpp diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 6a42a9f11..91065f250 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -11,7 +11,6 @@ #include #include "InputController.h" -#include "GamepadMonitor.h" #include "KeyEditor.h" using namespace QGBA; @@ -25,7 +24,6 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, QWidget* paren : QWidget(parent) , m_type(type) , m_controller(controller) - , m_gamepadMonitor(nullptr) { setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint); setMinimumSize(300, 300); @@ -114,9 +112,8 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, QWidget* paren #ifdef BUILD_SDL if (type == SDL_BINDING_BUTTON) { - m_gamepadMonitor = new GamepadMonitor(m_controller, this); - connect(m_gamepadMonitor, SIGNAL(buttonPressed(int)), this, SLOT(setButton(int))); - connect(m_gamepadMonitor, SIGNAL(axisChanged(int, int32_t)), this, SLOT(setAxisValue(int, int32_t))); + connect(m_controller, SIGNAL(buttonPressed(int)), this, SLOT(setButton(int))); + connect(m_controller, SIGNAL(axisChanged(int, int32_t)), this, SLOT(setAxisValue(int, int32_t))); } #endif } diff --git a/src/platform/qt/GBAKeyEditor.h b/src/platform/qt/GBAKeyEditor.h index 90e67edf6..e4275d613 100644 --- a/src/platform/qt/GBAKeyEditor.h +++ b/src/platform/qt/GBAKeyEditor.h @@ -20,7 +20,6 @@ class QTimer; namespace QGBA { class InputController; -class GamepadMonitor; class KeyEditor; class GBAKeyEditor : public QWidget { @@ -77,8 +76,6 @@ private: QList m_keyOrder; QList::iterator m_currentKey; - GamepadMonitor* m_gamepadMonitor; - uint32_t m_type; InputController* m_controller; diff --git a/src/platform/qt/GamepadMonitor.cpp b/src/platform/qt/GamepadMonitor.cpp deleted file mode 100644 index af73f3574..000000000 --- a/src/platform/qt/GamepadMonitor.cpp +++ /dev/null @@ -1,46 +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/. */ -#include "GamepadMonitor.h" - -#include "InputController.h" - -#include - -using namespace QGBA; - -GamepadMonitor::GamepadMonitor(InputController* controller, QObject* parent) - : QObject(parent) - , m_controller(controller) -{ -#ifdef BUILD_SDL - m_gamepadTimer = new QTimer(this); - connect(m_gamepadTimer, SIGNAL(timeout()), this, SLOT(testGamepad())); - m_gamepadTimer->setInterval(50); - m_gamepadTimer->start(); -#endif -} - -void GamepadMonitor::testGamepad() { -#ifdef BUILD_SDL - m_gamepadTimer->setInterval(50); - - auto activeAxes = m_controller->activeGamepadAxes(); - auto oldAxes = m_activeAxes; - m_activeAxes = activeAxes; - activeAxes.subtract(oldAxes); - if (!activeAxes.empty()) { - emit axisChanged(activeAxes.begin()->first, activeAxes.begin()->second); - } - - auto activeButtons = m_controller->activeGamepadButtons(); - auto oldButtons = m_activeButtons; - m_activeButtons = activeButtons; - activeButtons.subtract(oldButtons); - if (!activeButtons.empty()) { - emit buttonPressed(*activeButtons.begin()); - } -#endif -} diff --git a/src/platform/qt/GamepadMonitor.h b/src/platform/qt/GamepadMonitor.h deleted file mode 100644 index b72a55948..000000000 --- a/src/platform/qt/GamepadMonitor.h +++ /dev/null @@ -1,40 +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_GAMEPAD_MONITOR -#define QGBA_GAMEPAD_MONITOR - -#include -#include - -class QTimer; - -namespace QGBA { - -class InputController; - -class GamepadMonitor : public QObject { -Q_OBJECT - -public: - GamepadMonitor(InputController* controller, QObject* parent = nullptr); - -signals: - void axisChanged(int axis, int32_t value); - void buttonPressed(int button); - -public slots: - void testGamepad(); - -private: - InputController* m_controller; - QSet m_activeButtons; - QSet> m_activeAxes; - QTimer* m_gamepadTimer; -}; - -} - -#endif diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 9df6429fc..04acc0f70 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -7,13 +7,19 @@ #include "ConfigController.h" +#include + extern "C" { #include "util/configuration.h" } using namespace QGBA; -InputController::InputController() { +InputController::InputController(QObject* parent) + : QObject(parent) + , m_config(nullptr) + , m_gamepadTimer(nullptr) +{ GBAInputMapInit(&m_inputMap); #ifdef BUILD_SDL @@ -21,6 +27,11 @@ InputController::InputController() { GBASDLInitEvents(&m_sdlEvents); GBASDLInitBindings(&m_inputMap); SDL_JoystickEventState(SDL_QUERY); + + m_gamepadTimer = new QTimer(this); + connect(m_gamepadTimer, SIGNAL(timeout()), this, SLOT(testGamepad())); + m_gamepadTimer->setInterval(50); + m_gamepadTimer->start(); #endif GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A); @@ -163,3 +174,27 @@ void InputController::bindAxis(uint32_t type, int axis, Direction direction, GBA GBAInputBindAxis(&m_inputMap, SDL_BINDING_BUTTON, axis, &description); } #endif + +void InputController::testGamepad() { +#ifdef BUILD_SDL + auto activeAxes = activeGamepadAxes(); + auto oldAxes = m_activeAxes; + m_activeAxes = activeAxes; + activeAxes.subtract(oldAxes); + if (!activeAxes.empty()) { + emit axisChanged(activeAxes.begin()->first, activeAxes.begin()->second); + } + + auto activeButtons = activeGamepadButtons(); + auto oldButtons = m_activeButtons; + m_activeButtons = activeButtons; + activeButtons.subtract(oldButtons); + oldButtons.subtract(m_activeButtons); + for (int button : activeButtons) { + emit buttonPressed(button); + } + for (int button : oldButtons) { + emit buttonReleased(button); + } +#endif +} diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index 5a9c3325d..ca8602c36 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -6,6 +6,11 @@ #ifndef QGBA_INPUT_CONTROLLER_H #define QGBA_INPUT_CONTROLLER_H +#include +#include + +class QTimer; + extern "C" { #include "gba-input.h" @@ -14,17 +19,17 @@ extern "C" { #endif } -#include - namespace QGBA { class ConfigController; -class InputController { +class InputController : public QObject { +Q_OBJECT + public: static const uint32_t KEYBOARD = 0x51545F4B; - InputController(); + InputController(QObject* parent = nullptr); ~InputController(); void setConfiguration(ConfigController* config); @@ -52,6 +57,14 @@ public: void bindAxis(uint32_t type, int axis, Direction, GBAKey); #endif +signals: + void axisChanged(int axis, int32_t value); + void buttonPressed(int button); + void buttonReleased(int button); + +public slots: + void testGamepad(); + private: GBAInputMap m_inputMap; ConfigController* m_config; @@ -59,6 +72,10 @@ private: #ifdef BUILD_SDL GBASDLEvents m_sdlEvents; #endif + + QSet m_activeButtons; + QSet> m_activeAxes; + QTimer* m_gamepadTimer; }; } diff --git a/src/platform/qt/KeyEditor.h b/src/platform/qt/KeyEditor.h index 49d9a4fff..244cd915a 100644 --- a/src/platform/qt/KeyEditor.h +++ b/src/platform/qt/KeyEditor.h @@ -17,16 +17,18 @@ Q_OBJECT public: KeyEditor(QWidget* parent = nullptr); - void setValue(int key); int value() const { return m_key; } - void setValueKey(int key); - void setValueButton(int button); - void setValueAxis(int axis, int32_t value); InputController::Direction direction() const { return m_direction; } virtual QSize sizeHint() const override; +public slots: + void setValue(int key); + void setValueKey(int key); + void setValueButton(int button); + void setValueAxis(int axis, int32_t value); + signals: void valueChanged(int key); void axisChanged(int key, int direction); diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index ddc748e29..125810e81 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -28,6 +28,11 @@ QVariant ShortcutController::data(const QModelIndex& index, int role) const { return item.visibleName(); case 1: return item.action()->shortcut().toString(QKeySequence::NativeText); + case 2: + if (item.button() >= 0) { + return item.button(); + } + return QVariant(); } } else if (index.column() == 0) { return m_menus[index.row()].visibleName(); @@ -44,7 +49,9 @@ QVariant ShortcutController::headerData(int section, Qt::Orientation orientation case 0: return tr("Action"); case 1: - return tr("Shortcut"); + return tr("Keyboard"); + case 2: + return tr("Gamepad"); } } return section; @@ -68,7 +75,7 @@ QModelIndex ShortcutController::parent(const QModelIndex& index) const { } int ShortcutController::columnCount(const QModelIndex& index) const { - return 2; + return 3; } int ShortcutController::rowCount(const QModelIndex& index) const { @@ -97,7 +104,7 @@ void ShortcutController::addAction(QMenu* menu, QAction* action, const QString& beginInsertRows(parent, smenu->shortcuts().count(), smenu->shortcuts().count()); smenu->addAction(action, name); endInsertRows(); - emit dataChanged(createIndex(smenu->shortcuts().count() - 1, 0, row), createIndex(smenu->shortcuts().count() - 1, 1, row)); + emit dataChanged(createIndex(smenu->shortcuts().count() - 1, 0, row), createIndex(smenu->shortcuts().count() - 1, 2, row)); } void ShortcutController::addMenu(QMenu* menu) { @@ -137,12 +144,44 @@ void ShortcutController::updateKey(const QModelIndex& index, const QKeySequence& ShortcutMenu& menu = m_menus[parent.row()]; ShortcutItem& item = menu.shortcuts()[index.row()]; item.action()->setShortcut(keySequence); - emit dataChanged(createIndex(index.row(), 0, index.internalId()), createIndex(index.row(), 1, index.internalId())); + emit dataChanged(createIndex(index.row(), 0, index.internalId()), createIndex(index.row(), 2, index.internalId())); +} + +void ShortcutController::updateButton(const QModelIndex& index, int button) { + if (!index.isValid()) { + return; + } + const QModelIndex& parent = index.parent(); + if (!parent.isValid()) { + return; + } + ShortcutMenu& menu = m_menus[parent.row()]; + ShortcutItem& item = menu.shortcuts()[index.row()]; + int oldButton = item.button(); + item.setButton(button); + if (oldButton >= 0) { + m_buttons.take(oldButton); + } + m_buttons[button] = &item; + emit dataChanged(createIndex(index.row(), 0, index.internalId()), createIndex(index.row(), 2, index.internalId())); +} + +void ShortcutController::pressButton(int button) { + auto item = m_buttons.find(button); + if (item == m_buttons.end()) { + return; + } + QAction* action = item.value()->action(); + if (!action->isEnabled()) { + return; + } + action->trigger(); } ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& name) : m_action(action) , m_name(name) + , m_button(-1) { m_visibleName = action->text() .remove(QRegExp("&(?!&)")) diff --git a/src/platform/qt/ShortcutController.h b/src/platform/qt/ShortcutController.h index 2bd143721..56ab7e0d4 100644 --- a/src/platform/qt/ShortcutController.h +++ b/src/platform/qt/ShortcutController.h @@ -15,6 +15,8 @@ class QString; namespace QGBA { class ShortcutController : public QAbstractItemModel { +Q_OBJECT + public: ShortcutController(QObject* parent = nullptr); @@ -32,6 +34,10 @@ public: const QAction* actionAt(const QModelIndex& index) const; void updateKey(const QModelIndex& index, const QKeySequence& keySequence); + void updateButton(const QModelIndex& index, int button); + +private slots: + void pressButton(int button); private: class ShortcutItem { @@ -42,11 +48,14 @@ private: const QAction* action() const { return m_action; } const QString& visibleName() const { return m_visibleName; } const QString& name() const { return m_name; } + int button() const { return m_button; } + void setButton(int button) { m_button = button; } private: QAction* m_action; QString m_name; QString m_visibleName; + int m_button; }; class ShortcutMenu { @@ -67,6 +76,7 @@ private: }; QList m_menus; + QMap m_buttons; }; } diff --git a/src/platform/qt/ShortcutView.cpp b/src/platform/qt/ShortcutView.cpp index 064c54125..a9906553a 100644 --- a/src/platform/qt/ShortcutView.cpp +++ b/src/platform/qt/ShortcutView.cpp @@ -11,10 +11,13 @@ using namespace QGBA; ShortcutView::ShortcutView(QWidget* parent) : QWidget(parent) + , m_controller(nullptr) + , m_inputController(nullptr) { m_ui.setupUi(this); connect(m_ui.keySequenceEdit, SIGNAL(editingFinished()), this, SLOT(updateKey())); + connect(m_ui.keyEdit, SIGNAL(valueChanged(int)), this, SLOT(updateButton(int))); connect(m_ui.shortcutTable, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(loadKey(const QModelIndex&))); } @@ -23,6 +26,12 @@ void ShortcutView::setController(ShortcutController* controller) { m_ui.shortcutTable->setModel(controller); } +void ShortcutView::setInputController(InputController* controller) { + m_inputController = controller; + connect(controller, SIGNAL(buttonPressed(int)), m_ui.keyEdit, SLOT(setValueButton(int))); + connect(controller, SIGNAL(axisChanged(int, int32_t)), m_ui.keyEdit, SLOT(setValueAxis(int, int32_t))); +} + void ShortcutView::loadKey(const QModelIndex& index) { if (!m_controller) { return; @@ -42,3 +51,11 @@ void ShortcutView::updateKey() { m_ui.keySequenceEdit->clearFocus(); m_controller->updateKey(m_ui.shortcutTable->selectionModel()->currentIndex(), m_ui.keySequenceEdit->keySequence()); } + +void ShortcutView::updateButton(int button) { + if (!m_controller) { + return; + } + m_controller->updateButton(m_ui.shortcutTable->selectionModel()->currentIndex(), button); + +} diff --git a/src/platform/qt/ShortcutView.h b/src/platform/qt/ShortcutView.h index e57a7b9c6..e7971d6a5 100644 --- a/src/platform/qt/ShortcutView.h +++ b/src/platform/qt/ShortcutView.h @@ -12,8 +12,10 @@ namespace QGBA { +class InputController; class ShortcutController; +// TODO: suspend shortcuts (keyboard and gamepad) while window is open class ShortcutView : public QWidget { Q_OBJECT @@ -21,15 +23,18 @@ public: ShortcutView(QWidget* parent = nullptr); void setController(ShortcutController* controller); + void setInputController(InputController* controller); private slots: void loadKey(const QModelIndex&); void updateKey(); + void updateButton(int button); private: Ui::ShortcutView m_ui; ShortcutController* m_controller; + InputController* m_inputController; }; } diff --git a/src/platform/qt/ShortcutView.ui b/src/platform/qt/ShortcutView.ui index 48b66ba13..0a0cc64c3 100644 --- a/src/platform/qt/ShortcutView.ui +++ b/src/platform/qt/ShortcutView.ui @@ -6,8 +6,8 @@ 0 0 - 300 - 400 + 425 + 443 @@ -21,56 +21,10 @@ - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + Keyboard @@ -80,23 +34,7 @@ - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - false - + Gamepad @@ -115,10 +53,103 @@ + + + + + 0 + 0 + + + + + 40 + 0 + + + + + 100 + 16777215 + + + + + + + + + 0 + 0 + + + + + 40 + 0 + + + + + 100 + 16777215 + + + + false + + + Qt::AlignCenter + + + Press button + + + + + + QGBA::KeyEditor + QLineEdit +
KeyEditor.h
+
+
- + + + keyboardButton + toggled(bool) + keySequenceEdit + setVisible(bool) + + + 86 + 374 + + + 66 + 20 + + + + + gamepadButton + toggled(bool) + keyEdit + setVisible(bool) + + + 213 + 374 + + + 206 + 340 + + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 9183912ee..de640da40 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -93,6 +93,8 @@ Window::Window(ConfigController* config, QWidget* parent) connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float))); connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS())); + connect(&m_inputController, SIGNAL(buttonPressed(int)), m_shortcutController, SLOT(pressButton(int))); + m_logView->setLevels(GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL); m_fpsTimer.setInterval(FPS_TIMER_INTERVAL); @@ -213,6 +215,7 @@ void Window::openSettingsWindow() { void Window::openShortcutWindow() { ShortcutView* shortcutView = new ShortcutView(); shortcutView->setController(m_shortcutController); + shortcutView->setInputController(&m_inputController); connect(this, SIGNAL(shutdown()), shortcutView, SLOT(close())); shortcutView->setAttribute(Qt::WA_DeleteOnClose); shortcutView->show();