Qt: Start adding better input abstractions

This commit is contained in:
Vicki Pfau 2023-01-18 00:35:26 -08:00
parent be3022156f
commit 4580e8d2e9
12 changed files with 544 additions and 0 deletions

View File

@ -107,7 +107,11 @@ set(SOURCE_FILES
GamepadButtonEvent.cpp
GamepadHatEvent.cpp
IOViewer.cpp
input/Gamepad.cpp
input/InputDriver.cpp
input/InputSource.cpp
input/InputMapper.cpp
input/KeySource.cpp
InputController.cpp
InputProfile.cpp
KeyEditor.cpp
@ -199,6 +203,7 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5
set(AUDIO_SRC)
if(BUILD_SDL)
list(APPEND SOURCE_FILES input/SDLInputDriver.cpp)
list(APPEND AUDIO_SRC AudioProcessorSDL.cpp)
endif()

View File

@ -65,6 +65,7 @@ public:
GBAKey mapKeyboard(int key) const;
mInputMap* map() { return &m_inputMap; }
const mInputMap* map() const { return &m_inputMap; }
int pollEvents();

View File

@ -0,0 +1,13 @@
/* Copyright (c) 2013-2023 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 "input/Gamepad.h"
using namespace QGBA;
Gamepad::Gamepad(InputDriver* driver, QObject* parent)
: InputSource(driver, parent)
{
}

View File

@ -0,0 +1,30 @@
/* Copyright (c) 2013-2023 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/. */
#pragma once
#include "GamepadHatEvent.h"
#include "input/InputSource.h"
namespace QGBA {
class InputDriver;
class Gamepad : public InputSource {
Q_OBJECT
public:
Gamepad(InputDriver* driver, QObject* parent = nullptr);
virtual QList<bool> currentButtons() = 0;
virtual QList<int16_t> currentAxes() = 0;
virtual QList<GamepadHatEvent::Direction> currentHats() = 0;
virtual int buttonCount() const = 0;
virtual int axisCount() const = 0;
virtual int hatCount() const = 0;
};
}

View File

@ -0,0 +1,37 @@
/* Copyright (c) 2013-2023 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 "input/InputDriver.h"
using namespace QGBA;
InputDriver::InputDriver(QObject* parent)
: QObject(parent)
{}
void InputDriver::loadConfiguration(ConfigController*) {
}
void InputDriver::saveConfiguration(ConfigController*) {
}
void InputDriver::bindDefaults(InputController*) {
}
QList<KeySource*> InputDriver::connectedKeySources() const {
return {};
}
QList<Gamepad*> InputDriver::connectedGamepads() const {
return {};
}
mRumble* InputDriver::rumble() {
return nullptr;
}
mRotationSource* InputDriver::rotationSource() {
return nullptr;
}

View File

@ -0,0 +1,46 @@
/* Copyright (c) 2013-2023 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/. */
#pragma once
#include <QList>
#include <QString>
#include <QObject>
struct mRotationSource;
struct mRumble;
namespace QGBA {
class ConfigController;
class Gamepad;
class InputController;
class KeySource;
class InputDriver : public QObject {
Q_OBJECT
public:
InputDriver(QObject* parent = nullptr);
virtual ~InputDriver() = default;
virtual uint32_t type() const = 0;
virtual QString visibleName() const = 0;
virtual void loadConfiguration(ConfigController*);
virtual void saveConfiguration(ConfigController*);
virtual void bindDefaults(InputController*);
virtual bool update() = 0;
virtual QList<KeySource*> connectedKeySources() const;
virtual QList<Gamepad*> connectedGamepads() const;
virtual mRumble* rumble();
virtual mRotationSource* rotationSource();
};
}

View File

@ -0,0 +1,14 @@
/* Copyright (c) 2013-2023 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 "input/InputSource.h"
using namespace QGBA;
InputSource::InputSource(InputDriver* driver, QObject* parent)
: QObject(parent)
, m_driver(driver)
{
}

View File

@ -0,0 +1,31 @@
/* Copyright (c) 2013-2023 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/. */
#pragma once
#include <QObject>
#include <QString>
namespace QGBA {
class InputDriver;
class InputSource : public QObject {
Q_OBJECT
public:
InputSource(InputDriver* driver, QObject* parent = nullptr);
virtual ~InputSource() = default;
InputDriver* driver() { return m_driver; }
virtual QString name() const = 0;
virtual QString visibleName() const = 0;
protected:
InputDriver* const m_driver;
};
}

View File

@ -0,0 +1,13 @@
/* Copyright (c) 2013-2023 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 "input/KeySource.h"
using namespace QGBA;
KeySource::KeySource(InputDriver* driver, QObject* parent)
: InputSource(driver, parent)
{
}

View File

@ -0,0 +1,23 @@
/* Copyright (c) 2013-2023 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/. */
#pragma once
#include "input/InputSource.h"
namespace QGBA {
class InputDriver;
class KeySource : public InputSource {
Q_OBJECT
public:
KeySource(InputDriver* driver, QObject* parent = nullptr);
virtual QSet<int> currentKeys() = 0;
};
}

View File

@ -0,0 +1,247 @@
/* Copyright (c) 2013-2023 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 "input/SDLInputDriver.h"
#include "ConfigController.h"
#include "InputController.h"
#include <algorithm>
using namespace QGBA;
int s_sdlInited = 0;
mSDLEvents s_sdlEvents;
SDLInputDriver::SDLInputDriver(InputController* controller, QObject* parent)
: InputDriver(parent)
, m_controller(controller)
{
if (s_sdlInited == 0) {
mSDLInitEvents(&s_sdlEvents);
}
++s_sdlInited;
m_sdlPlayer.bindings = m_controller->map();
for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
m_gamepads.append(std::make_shared<SDLGamepad>(this, i));
}
}
SDLInputDriver::~SDLInputDriver() {
if (m_playerAttached) {
mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
}
--s_sdlInited;
if (s_sdlInited == 0) {
mSDLDeinitEvents(&s_sdlEvents);
}
}
void SDLInputDriver::loadConfiguration(ConfigController* config) {
mSDLEventsLoadConfig(&s_sdlEvents, config->input());
if (!m_playerAttached) {
m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
}
if (m_playerAttached) {
mSDLPlayerLoadConfig(&m_sdlPlayer, config->input());
}
}
void SDLInputDriver::saveConfiguration(ConfigController* config) {
if (m_playerAttached) {
mSDLPlayerSaveConfig(&m_sdlPlayer, config->input());
}
}
void SDLInputDriver::bindDefaults(InputController* controller) {
mSDLInitBindingsGBA(controller->map());
}
mRumble* SDLInputDriver::rumble() {
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (m_playerAttached) {
return &m_sdlPlayer.rumble.d;
}
#endif
return nullptr;
}
mRotationSource* SDLInputDriver::rotationSource() {
if (m_playerAttached) {
return &m_sdlPlayer.rotation.d;
}
return nullptr;
}
bool SDLInputDriver::update() {
if (!m_playerAttached || !m_sdlPlayer.joystick) {
return false;
}
SDL_JoystickUpdate();
#if SDL_VERSION_ATLEAST(2, 0, 0)
updateGamepads();
#endif
return true;
}
QList<Gamepad*> SDLInputDriver::connectedGamepads() const {
QList<Gamepad*> pads;
for (auto& pad : m_gamepads) {
pads.append(pad.get());
}
return pads;
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
void SDLInputDriver::updateGamepads() {
for (int i = 0; i < m_gamepads.size(); ++i) {
if (m_gamepads.at(i)->updateIndex()) {
continue;
}
m_gamepads.removeAt(i);
--i;
}
std::sort(m_gamepads.begin(), m_gamepads.end(), [](const auto& a, const auto b) {
return a->m_index < b->m_index;
});
for (size_t i = 0, j = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
std::shared_ptr<SDLGamepad> gamepad = m_gamepads.at(j);
if (gamepad->m_index == i) {
++j;
continue;
}
m_gamepads.append(std::make_shared<SDLGamepad>(this, i));
}
std::sort(m_gamepads.begin(), m_gamepads.end(), [](const auto& a, const auto b) {
return a->m_index < b->m_index;
});
}
#endif
SDLGamepad::SDLGamepad(SDLInputDriver* driver, int index, QObject* parent)
: Gamepad(driver, parent)
, m_index(index)
{
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), m_guid, sizeof(m_guid));
#endif
}
QList<bool> SDLGamepad::currentButtons() {
if (!verify()) {
return {};
}
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
QList<bool> buttons;
int numButtons = SDL_JoystickNumButtons(joystick);
for (int i = 0; i < numButtons; ++i) {
buttons.append(SDL_JoystickGetButton(joystick, i));
}
return buttons;
}
QList<int16_t> SDLGamepad::currentAxes() {
if (!verify()) {
return {};
}
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
QList<int16_t> axes;
int numAxes = SDL_JoystickNumAxes(joystick);
for (int i = 0; i < numAxes; ++i) {
axes.append(SDL_JoystickGetAxis(joystick, i));
}
return axes;
}
QList<GamepadHatEvent::Direction> SDLGamepad::currentHats() {
if (!verify()) {
return {};
}
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
QList<GamepadHatEvent::Direction> hats;
int numHats = SDL_JoystickNumHats(joystick);
for (int i = 0; i < numHats; ++i) {
hats.append(static_cast<GamepadHatEvent::Direction>(SDL_JoystickGetHat(joystick, i)));
}
return hats;
}
int SDLGamepad::buttonCount() const {
if (!verify()) {
return -1;
}
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
return SDL_JoystickNumButtons(joystick);
}
int SDLGamepad::axisCount() const {
if (!verify()) {
return -1;
}
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
return SDL_JoystickNumAxes(joystick);
}
int SDLGamepad::hatCount() const {
if (!verify()) {
return -1;
}
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
return SDL_JoystickNumHats(joystick);
}
QString SDLGamepad::name() const {
#if SDL_VERSION_ATLEAST(2, 0, 0)
return m_guid;
#else
return visibleName();
#endif
}
QString SDLGamepad::visibleName() const {
#if SDL_VERSION_ATLEAST(2, 0, 0)
return SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick);
#else
return SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick));
#endif
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
bool SDLGamepad::updateIndex() {
char guid[34];
for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick;
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), guid, sizeof(guid));
if (memcmp(guid, m_guid, 33) == 0) {
m_index = i;
return true;
}
}
return false;
}
#endif
bool SDLGamepad::verify() const {
return m_index < SDL_JoystickListSize(&s_sdlEvents.joysticks);
}

View File

@ -0,0 +1,84 @@
/* Copyright (c) 2013-2023 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/. */
#pragma once
#include "input/Gamepad.h"
#include "input/InputDriver.h"
#include "platform/sdl/sdl-events.h"
#include <memory>
namespace QGBA {
class SDLGamepad;
class SDLInputDriver : public InputDriver {
Q_OBJECT
public:
SDLInputDriver(InputController*, QObject* parent = nullptr);
~SDLInputDriver();
uint32_t type() const override { return SDL_BINDING_BUTTON; }
QString visibleName() const override { return QLatin1String("SDL"); }
void loadConfiguration(ConfigController* config) override;
void saveConfiguration(ConfigController* config) override;
void bindDefaults(InputController*) override;
bool update() override;
QList<Gamepad*> connectedGamepads() const override;
mRumble* rumble() override;
mRotationSource* rotationSource() override;
private:
InputController* m_controller;
mSDLPlayer m_sdlPlayer{};
bool m_playerAttached = false;
QList<std::shared_ptr<SDLGamepad>> m_gamepads;
#if SDL_VERSION_ATLEAST(2, 0, 0)
void updateGamepads();
#endif
};
class SDLGamepad : public Gamepad {
Q_OBJECT
public:
SDLGamepad(SDLInputDriver*, int index, QObject* parent = nullptr);
QList<bool> currentButtons() override;
QList<int16_t> currentAxes() override;
QList<GamepadHatEvent::Direction> currentHats() override;
int buttonCount() const override;
int axisCount() const override;
int hatCount() const override;
QString name() const override;
QString visibleName() const override;
#if SDL_VERSION_ATLEAST(2, 0, 0)
bool updateIndex();
#endif
private:
friend class SDLInputDriver;
size_t m_index;
#if SDL_VERSION_ATLEAST(2, 0, 0)
char m_guid[34]{};
#endif
bool verify() const;
};
}