Qt: Add GamepadButtonEvent and remove signal associated; use this for button controls in emulator

This commit is contained in:
Jeffrey Pfau 2015-01-04 04:23:20 -08:00
parent 53c586044d
commit 33c4b17941
16 changed files with 220 additions and 33 deletions

View File

@ -44,6 +44,7 @@ set(SOURCE_FILES
GIFView.cpp GIFView.cpp
GameController.cpp GameController.cpp
GamePakView.cpp GamePakView.cpp
GamepadButtonEvent.cpp
InputController.cpp InputController.cpp
KeyEditor.cpp KeyEditor.cpp
LoadSaveState.cpp LoadSaveState.cpp

View File

@ -112,7 +112,6 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, QWidget* paren
#ifdef BUILD_SDL #ifdef BUILD_SDL
if (type == SDL_BINDING_BUTTON) { if (type == SDL_BINDING_BUTTON) {
connect(m_controller, SIGNAL(buttonPressed(int)), this, SLOT(setButton(int)));
connect(m_controller, SIGNAL(axisChanged(int, int32_t)), this, SLOT(setAxisValue(int, int32_t))); connect(m_controller, SIGNAL(axisChanged(int, int32_t)), this, SLOT(setAxisValue(int, int32_t)));
} }
#endif #endif
@ -237,14 +236,6 @@ void GBAKeyEditor::setAxisValue(int axis, int32_t value) {
KeyEditor* focused = *m_currentKey; KeyEditor* focused = *m_currentKey;
focused->setValueAxis(axis, value); focused->setValueAxis(axis, value);
} }
void GBAKeyEditor::setButton(int button) {
if (!findFocus()) {
return;
}
KeyEditor* focused = *m_currentKey;
focused->setValueButton(button);
}
#endif #endif
KeyEditor* GBAKeyEditor::keyById(GBAKey key) { KeyEditor* GBAKeyEditor::keyById(GBAKey key) {

View File

@ -40,7 +40,6 @@ private slots:
void save(); void save();
#ifdef BUILD_SDL #ifdef BUILD_SDL
void setAxisValue(int axis, int32_t value); void setAxisValue(int axis, int32_t value);
void setButton(int button);
#endif #endif
private: private:

View File

@ -0,0 +1,41 @@
/* 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 "GamepadButtonEvent.h"
#include "InputController.h"
using namespace QGBA;
QEvent::Type GamepadButtonEvent::s_downType = QEvent::None;
QEvent::Type GamepadButtonEvent::s_upType = QEvent::None;
GamepadButtonEvent::GamepadButtonEvent(QEvent::Type type, int button, InputController* controller)
: QEvent(type)
, m_button(button)
, m_controller(controller)
, m_key(GBA_KEY_NONE)
{
ignore();
#ifdef BUILD_SDL
if (controller) {
m_key = GBAInputMapKey(controller->map(), SDL_BINDING_BUTTON, button);
}
#endif
}
QEvent::Type GamepadButtonEvent::Down() {
if (s_downType == None) {
s_downType = static_cast<Type>(registerEventType());
}
return s_downType;
}
QEvent::Type GamepadButtonEvent::Up() {
if (s_upType == None) {
s_upType = static_cast<Type>(registerEventType());
}
return s_upType;
}

View File

@ -0,0 +1,40 @@
/* 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_BUTTON_EVENT
#define QGBA_GAMEPAD_BUTTON_EVENT
#include <QEvent>
extern "C" {
#include "gba-input.h"
}
namespace QGBA {
class InputController;
class GamepadButtonEvent : public QEvent {
public:
GamepadButtonEvent(Type type, int button, InputController* controller = nullptr);
int value() const { return m_button; }
GBAKey gbaKey() const { return m_key; }
static Type Down();
static Type Up();
private:
static Type s_downType;
static Type s_upType;
int m_button;
InputController* m_controller;
GBAKey m_key;
};
}
#endif

View File

@ -6,8 +6,11 @@
#include "InputController.h" #include "InputController.h"
#include "ConfigController.h" #include "ConfigController.h"
#include "GamepadButtonEvent.h"
#include <QApplication>
#include <QTimer> #include <QTimer>
#include <QWidget>
extern "C" { extern "C" {
#include "util/configuration.h" #include "util/configuration.h"
@ -91,6 +94,9 @@ int InputController::testSDLEvents() {
if (key == GBA_KEY_NONE) { if (key == GBA_KEY_NONE) {
continue; continue;
} }
if (hasPendingEvent(key)) {
continue;
}
if (SDL_JoystickGetButton(joystick, i)) { if (SDL_JoystickGetButton(joystick, i)) {
activeButtons |= 1 << key; activeButtons |= 1 << key;
} }
@ -191,10 +197,29 @@ void InputController::testGamepad() {
activeButtons.subtract(oldButtons); activeButtons.subtract(oldButtons);
oldButtons.subtract(m_activeButtons); oldButtons.subtract(m_activeButtons);
for (int button : activeButtons) { for (int button : activeButtons) {
emit buttonPressed(button); GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, this);
postPendingEvent(event->gbaKey());
QApplication::sendEvent(QApplication::focusWidget(), event);
if (!event->isAccepted()) {
clearPendingEvent(event->gbaKey());
}
} }
for (int button : oldButtons) { for (int button : oldButtons) {
emit buttonReleased(button); GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, this);
clearPendingEvent(event->gbaKey());
QApplication::sendEvent(QApplication::focusWidget(), event);
} }
#endif #endif
} }
void InputController::postPendingEvent(GBAKey key) {
m_pendingEvents.insert(key);
}
void InputController::clearPendingEvent(GBAKey key) {
m_pendingEvents.remove(key);
}
bool InputController::hasPendingEvent(GBAKey key) const {
return m_pendingEvents.contains(key);
}

View File

@ -59,13 +59,15 @@ public:
signals: signals:
void axisChanged(int axis, int32_t value); void axisChanged(int axis, int32_t value);
void buttonPressed(int button);
void buttonReleased(int button);
public slots: public slots:
void testGamepad(); void testGamepad();
private: private:
void postPendingEvent(GBAKey);
void clearPendingEvent(GBAKey);
bool hasPendingEvent(GBAKey) const;
GBAInputMap m_inputMap; GBAInputMap m_inputMap;
ConfigController* m_config; ConfigController* m_config;
@ -76,6 +78,8 @@ private:
QSet<int> m_activeButtons; QSet<int> m_activeButtons;
QSet<QPair<int, int32_t>> m_activeAxes; QSet<QPair<int, int32_t>> m_activeAxes;
QTimer* m_gamepadTimer; QTimer* m_gamepadTimer;
QSet<GBAKey> m_pendingEvents;
}; };
} }

View File

@ -5,6 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "KeyEditor.h" #include "KeyEditor.h"
#include "GamepadButtonEvent.h"
#include <QKeyEvent> #include <QKeyEvent>
using namespace QGBA; using namespace QGBA;
@ -56,3 +58,15 @@ void KeyEditor::keyPressEvent(QKeyEvent* event) {
} }
event->accept(); event->accept();
} }
bool KeyEditor::event(QEvent* event) {
if (!m_button) {
return QWidget::event(event);
}
if (event->type() == GamepadButtonEvent::Down()) {
setValueButton(static_cast<GamepadButtonEvent*>(event)->value());
event->accept();
return true;
}
return QWidget::event(event);
}

View File

@ -35,6 +35,7 @@ signals:
protected: protected:
virtual void keyPressEvent(QKeyEvent* event) override; virtual void keyPressEvent(QKeyEvent* event) override;
virtual bool event(QEvent* event) override;
private: private:
int m_key; int m_key;

View File

@ -6,6 +6,7 @@
#include "LoadSaveState.h" #include "LoadSaveState.h"
#include "GameController.h" #include "GameController.h"
#include "GamepadButtonEvent.h"
#include "VFileDevice.h" #include "VFileDevice.h"
#include <QKeyEvent> #include <QKeyEvent>
@ -104,6 +105,40 @@ bool LoadSaveState::eventFilter(QObject* object, QEvent* event) {
} }
} }
} }
if (event->type() == GamepadButtonEvent::Down()) {
int column = m_currentFocus % 3;
int row = m_currentFocus - column;
switch (static_cast<GamepadButtonEvent*>(event)->gbaKey()) {
case GBA_KEY_UP:
row += 6;
break;
case GBA_KEY_DOWN:
row += 3;
break;
case GBA_KEY_LEFT:
column += 2;
break;
case GBA_KEY_RIGHT:
column += 1;
break;
case GBA_KEY_B:
close();
break;
case GBA_KEY_A:
case GBA_KEY_START:
event->accept();
triggerState(m_currentFocus + 1);
return true;
default:
return false;
}
column %= 3;
row %= 9;
m_currentFocus = column + row;
m_slots[m_currentFocus]->setFocus();
event->accept();
return true;
}
return false; return false;
} }

View File

@ -13,6 +13,7 @@
namespace QGBA { namespace QGBA {
class GameController; class GameController;
class InputController;
class SavestateButton; class SavestateButton;
enum class LoadSave { enum class LoadSave {
@ -28,6 +29,7 @@ public:
LoadSaveState(GameController* controller, QWidget* parent = nullptr); LoadSaveState(GameController* controller, QWidget* parent = nullptr);
void setInputController(InputController* controller);
void setMode(LoadSave mode); void setMode(LoadSave mode);
signals: signals:
@ -45,6 +47,7 @@ private:
Ui::LoadSaveState m_ui; Ui::LoadSaveState m_ui;
GameController* m_controller; GameController* m_controller;
InputController* m_inputController;
SavestateButton* m_slots[NUM_SLOTS]; SavestateButton* m_slots[NUM_SLOTS];
LoadSave m_mode; LoadSave m_mode;

View File

@ -5,6 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ShortcutController.h" #include "ShortcutController.h"
#include "GamepadButtonEvent.h"
#include <QAction> #include <QAction>
#include <QMenu> #include <QMenu>
@ -166,16 +168,20 @@ void ShortcutController::updateButton(const QModelIndex& index, int button) {
emit dataChanged(createIndex(index.row(), 0, index.internalId()), createIndex(index.row(), 2, index.internalId())); emit dataChanged(createIndex(index.row(), 0, index.internalId()), createIndex(index.row(), 2, index.internalId()));
} }
void ShortcutController::pressButton(int button) { bool ShortcutController::eventFilter(QObject*, QEvent* event) {
auto item = m_buttons.find(button); if (event->type() == GamepadButtonEvent::Down()) {
auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value());
if (item == m_buttons.end()) { if (item == m_buttons.end()) {
return; return false;
} }
QAction* action = item.value()->action(); QAction* action = item.value()->action();
if (!action->isEnabled()) { if (action->isEnabled()) {
return;
}
action->trigger(); action->trigger();
}
event->accept();
return true;
}
return false;
} }
ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& name) ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& name)

View File

@ -36,8 +36,8 @@ public:
void updateKey(const QModelIndex& index, const QKeySequence& keySequence); void updateKey(const QModelIndex& index, const QKeySequence& keySequence);
void updateButton(const QModelIndex& index, int button); void updateButton(const QModelIndex& index, int button);
private slots: protected:
void pressButton(int button); bool eventFilter(QObject*, QEvent*) override;
private: private:
class ShortcutItem { class ShortcutItem {

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ShortcutView.h" #include "ShortcutView.h"
#include "GamepadButtonEvent.h"
#include "ShortcutController.h" #include "ShortcutController.h"
using namespace QGBA; using namespace QGBA;
@ -15,10 +16,11 @@ ShortcutView::ShortcutView(QWidget* parent)
, m_inputController(nullptr) , m_inputController(nullptr)
{ {
m_ui.setupUi(this); m_ui.setupUi(this);
m_ui.keyEdit->setValueButton(-1);
connect(m_ui.keySequenceEdit, SIGNAL(editingFinished()), this, SLOT(updateKey())); connect(m_ui.keySequenceEdit, SIGNAL(editingFinished()), this, SLOT(updateKey()));
connect(m_ui.keyEdit, SIGNAL(valueChanged(int)), this, SLOT(updateButton(int))); connect(m_ui.keyEdit, SIGNAL(valueChanged(int)), this, SLOT(updateButton(int)));
connect(m_ui.shortcutTable, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(loadKey(const QModelIndex&))); connect(m_ui.shortcutTable, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(load(const QModelIndex&)));
} }
void ShortcutView::setController(ShortcutController* controller) { void ShortcutView::setController(ShortcutController* controller) {
@ -28,11 +30,19 @@ void ShortcutView::setController(ShortcutController* controller) {
void ShortcutView::setInputController(InputController* controller) { void ShortcutView::setInputController(InputController* controller) {
m_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))); connect(controller, SIGNAL(axisChanged(int, int32_t)), m_ui.keyEdit, SLOT(setValueAxis(int, int32_t)));
} }
void ShortcutView::loadKey(const QModelIndex& index) { bool ShortcutView::event(QEvent* event) {
if (event->type() == GamepadButtonEvent::Down()) {
updateButton(static_cast<GamepadButtonEvent*>(event)->value());
event->accept();
return true;
}
return QWidget::event(event);
}
void ShortcutView::load(const QModelIndex& index) {
if (!m_controller) { if (!m_controller) {
return; return;
} }
@ -40,8 +50,11 @@ void ShortcutView::loadKey(const QModelIndex& index) {
if (!action) { if (!action) {
return; return;
} }
m_ui.keySequenceEdit->setFocus(); if (m_ui.gamepadButton->isChecked()) {
m_ui.keySequenceEdit->setKeySequence(action->shortcut()); loadButton();
} else {
loadKey(action);
}
} }
void ShortcutView::updateKey() { void ShortcutView::updateKey() {
@ -59,3 +72,12 @@ void ShortcutView::updateButton(int button) {
m_controller->updateButton(m_ui.shortcutTable->selectionModel()->currentIndex(), button); m_controller->updateButton(m_ui.shortcutTable->selectionModel()->currentIndex(), button);
} }
void ShortcutView::loadKey(const QAction* action) {
m_ui.keySequenceEdit->setFocus();
m_ui.keySequenceEdit->setKeySequence(action->shortcut());
}
void ShortcutView::loadButton() {
m_ui.keyEdit->setFocus();
m_ui.keyEdit->setValueButton(-1); // TODO: Real value
}

View File

@ -25,12 +25,18 @@ public:
void setController(ShortcutController* controller); void setController(ShortcutController* controller);
void setInputController(InputController* controller); void setInputController(InputController* controller);
protected:
virtual bool event(QEvent* event) override;
private slots: private slots:
void loadKey(const QModelIndex&); void load(const QModelIndex&);
void updateKey(); void updateKey();
void updateButton(int button); void updateButton(int button);
private: private:
void loadKey(const QAction*);
void loadButton();
Ui::ShortcutView m_ui; Ui::ShortcutView m_ui;
ShortcutController* m_controller; ShortcutController* m_controller;

View File

@ -93,8 +93,6 @@ Window::Window(ConfigController* config, QWidget* parent)
connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float))); connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float)));
connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS())); 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_logView->setLevels(GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL);
m_fpsTimer.setInterval(FPS_TIMER_INTERVAL); m_fpsTimer.setInterval(FPS_TIMER_INTERVAL);
@ -431,6 +429,7 @@ void Window::setupMenu(QMenuBar* menubar) {
menubar->clear(); menubar->clear();
QMenu* fileMenu = menubar->addMenu(tr("&File")); QMenu* fileMenu = menubar->addMenu(tr("&File"));
m_shortcutController->addMenu(fileMenu); m_shortcutController->addMenu(fileMenu);
installEventFilter(m_shortcutController);
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open), "loadROM"); addControlledAction(fileMenu, fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open), "loadROM");
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &BIOS..."), this, SLOT(selectBIOS())), "loadBIOS"); addControlledAction(fileMenu, fileMenu->addAction(tr("Load &BIOS..."), this, SLOT(selectBIOS())), "loadBIOS");
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &patch..."), this, SLOT(selectPatch())), "loadPatch"); addControlledAction(fileMenu, fileMenu->addAction(tr("Load &patch..."), this, SLOT(selectPatch())), "loadPatch");