diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 3d17014a1..d20adb05e 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -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() diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index ef33a2ade..746806577 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -65,6 +65,7 @@ public: GBAKey mapKeyboard(int key) const; + mInputMap* map() { return &m_inputMap; } const mInputMap* map() const { return &m_inputMap; } int pollEvents(); diff --git a/src/platform/qt/input/Gamepad.cpp b/src/platform/qt/input/Gamepad.cpp new file mode 100644 index 000000000..183c9c0ab --- /dev/null +++ b/src/platform/qt/input/Gamepad.cpp @@ -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) +{ +} diff --git a/src/platform/qt/input/Gamepad.h b/src/platform/qt/input/Gamepad.h new file mode 100644 index 000000000..1781885cd --- /dev/null +++ b/src/platform/qt/input/Gamepad.h @@ -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 currentButtons() = 0; + virtual QList currentAxes() = 0; + virtual QList currentHats() = 0; + + virtual int buttonCount() const = 0; + virtual int axisCount() const = 0; + virtual int hatCount() const = 0; +}; + +} diff --git a/src/platform/qt/input/InputDriver.cpp b/src/platform/qt/input/InputDriver.cpp new file mode 100644 index 000000000..549fa13bb --- /dev/null +++ b/src/platform/qt/input/InputDriver.cpp @@ -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 InputDriver::connectedKeySources() const { + return {}; +} + +QList InputDriver::connectedGamepads() const { + return {}; +} + +mRumble* InputDriver::rumble() { + return nullptr; +} + +mRotationSource* InputDriver::rotationSource() { + return nullptr; +} diff --git a/src/platform/qt/input/InputDriver.h b/src/platform/qt/input/InputDriver.h new file mode 100644 index 000000000..139f1a500 --- /dev/null +++ b/src/platform/qt/input/InputDriver.h @@ -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 +#include +#include + +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 connectedKeySources() const; + virtual QList connectedGamepads() const; + + virtual mRumble* rumble(); + virtual mRotationSource* rotationSource(); +}; + +} diff --git a/src/platform/qt/input/InputSource.cpp b/src/platform/qt/input/InputSource.cpp new file mode 100644 index 000000000..ec1dc8bc5 --- /dev/null +++ b/src/platform/qt/input/InputSource.cpp @@ -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) +{ +} diff --git a/src/platform/qt/input/InputSource.h b/src/platform/qt/input/InputSource.h new file mode 100644 index 000000000..9ba18acf5 --- /dev/null +++ b/src/platform/qt/input/InputSource.h @@ -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 +#include + +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; +}; + +} diff --git a/src/platform/qt/input/KeySource.cpp b/src/platform/qt/input/KeySource.cpp new file mode 100644 index 000000000..32f0ca740 --- /dev/null +++ b/src/platform/qt/input/KeySource.cpp @@ -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) +{ +} diff --git a/src/platform/qt/input/KeySource.h b/src/platform/qt/input/KeySource.h new file mode 100644 index 000000000..e3df912a6 --- /dev/null +++ b/src/platform/qt/input/KeySource.h @@ -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 currentKeys() = 0; +}; + +} diff --git a/src/platform/qt/input/SDLInputDriver.cpp b/src/platform/qt/input/SDLInputDriver.cpp new file mode 100644 index 000000000..efd8e3f36 --- /dev/null +++ b/src/platform/qt/input/SDLInputDriver.cpp @@ -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 + +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(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 SDLInputDriver::connectedGamepads() const { + QList 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 gamepad = m_gamepads.at(j); + if (gamepad->m_index == i) { + ++j; + continue; + } + m_gamepads.append(std::make_shared(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 SDLGamepad::currentButtons() { + if (!verify()) { + return {}; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + QList buttons; + + int numButtons = SDL_JoystickNumButtons(joystick); + for (int i = 0; i < numButtons; ++i) { + buttons.append(SDL_JoystickGetButton(joystick, i)); + } + + return buttons; +} + +QList SDLGamepad::currentAxes() { + if (!verify()) { + return {}; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + QList axes; + + int numAxes = SDL_JoystickNumAxes(joystick); + for (int i = 0; i < numAxes; ++i) { + axes.append(SDL_JoystickGetAxis(joystick, i)); + } + + return axes; +} + +QList SDLGamepad::currentHats() { + if (!verify()) { + return {}; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + QList hats; + + int numHats = SDL_JoystickNumHats(joystick); + for (int i = 0; i < numHats; ++i) { + hats.append(static_cast(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); +} diff --git a/src/platform/qt/input/SDLInputDriver.h b/src/platform/qt/input/SDLInputDriver.h new file mode 100644 index 000000000..752ab7a91 --- /dev/null +++ b/src/platform/qt/input/SDLInputDriver.h @@ -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 + +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 connectedGamepads() const override; + + mRumble* rumble() override; + mRotationSource* rotationSource() override; + +private: + InputController* m_controller; + mSDLPlayer m_sdlPlayer{}; + bool m_playerAttached = false; + QList> 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 currentButtons() override; + QList currentAxes() override; + QList 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; +}; + +}