diff --git a/include/mgba/core/input.h b/include/mgba/core/input.h index f7ef212a5..f5d8002ed 100644 --- a/include/mgba/core/input.h +++ b/include/mgba/core/input.h @@ -55,6 +55,7 @@ int mInputMapKeyBits(const struct mInputMap* map, uint32_t type, uint32_t bits, void mInputBindKey(struct mInputMap*, uint32_t type, int key, int input); int mInputQueryBinding(const struct mInputMap*, uint32_t type, int input); void mInputUnbindKey(struct mInputMap*, uint32_t type, int input); +void mInputUnbindAllKeys(struct mInputMap*, uint32_t type); int mInputMapAxis(const struct mInputMap*, uint32_t type, int axis, int value); int mInputClearAxis(const struct mInputMap*, uint32_t type, int axis, int keys); @@ -69,6 +70,7 @@ void mInputBindHat(struct mInputMap*, uint32_t type, int id, const struct mInput bool mInputQueryHat(const struct mInputMap*, uint32_t type, int id, struct mInputHatBindings* bindings); void mInputUnbindHat(struct mInputMap*, uint32_t type, int id); void mInputUnbindAllHats(struct mInputMap*, uint32_t type); +void mInputEnumerateHats(const struct mInputMap*, uint32_t type, void (handler(int hat, const struct mInputHatBindings* bindings, void* user)), void* user); bool mInputMapLoad(struct mInputMap*, uint32_t type, const struct Configuration*); void mInputMapSave(const struct mInputMap*, uint32_t type, struct Configuration*); diff --git a/src/core/input.c b/src/core/input.c index 76296a373..787e31f86 100644 --- a/src/core/input.c +++ b/src/core/input.c @@ -38,6 +38,11 @@ struct mInputAxisEnumerate { void* user; }; +struct mInputHatEnumerate { + void (*handler)(int axis, const struct mInputHatBindings* bindings, void* user); + void* user; +}; + static void _makeSectionName(const char* platform, char* sectionName, size_t len, uint32_t type) { snprintf(sectionName, len, "%s.input.%c%c%c%c", platform, type >> 24, type >> 16, type >> 8, type); sectionName[len - 1] = '\0'; @@ -304,6 +309,12 @@ void _unbindAxis(uint32_t axis, void* dp, void* user) { } } +void _enumerateHat(uint32_t axis, void* dp, void* ep) { + struct mInputHatEnumerate* enumUser = ep; + const struct mInputHatBindings* description = dp; + enumUser->handler(axis, description, enumUser->user); +} + static bool _loadAll(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) { if (!ConfigurationHasSection(config, sectionName)) { return false; @@ -415,6 +426,16 @@ void mInputUnbindKey(struct mInputMap* map, uint32_t type, int input) { } } +void mInputUnbindAllKeys(struct mInputMap* map, uint32_t type) { + struct mInputMapImpl* impl = _lookupMap(map, type); + if (impl) { + size_t i; + for (i = 0; i < map->info->nKeys; ++i) { + impl->map[i] = -1; + } + } +} + int mInputQueryBinding(const struct mInputMap* map, uint32_t type, int input) { if (input < 0 || (size_t) input >= map->info->nKeys) { return -1; @@ -578,6 +599,18 @@ void mInputUnbindAllHats(struct mInputMap* map, uint32_t type) { } } +void mInputEnumerateHats(const struct mInputMap* map, uint32_t type, void (handler(int hat, const struct mInputHatBindings* bindings, void* user)), void* user) { + const struct mInputMapImpl* impl = _lookupMapConst(map, type); + if (!impl) { + return; + } + struct mInputHatEnumerate enumUser = { + handler, + user + }; + TableEnumerate(&impl->axes, _enumerateHat, &enumUser); +} + bool mInputMapLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config) { char sectionName[SECTION_NAME_MAX]; _makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type); diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 1bb8758c7..327309acf 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -103,10 +103,15 @@ set(SOURCE_FILES GBAApp.cpp GBAKeyEditor.cpp GIFView.cpp - GamepadAxisEvent.cpp - GamepadButtonEvent.cpp - GamepadHatEvent.cpp IOViewer.cpp + input/Gamepad.cpp + input/GamepadAxisEvent.cpp + input/GamepadButtonEvent.cpp + input/GamepadHatEvent.cpp + input/InputDriver.cpp + input/InputSource.cpp + input/InputMapper.cpp + input/KeySource.cpp InputController.cpp InputProfile.cpp KeyEditor.cpp @@ -198,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/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index 30287262a..c7c380a7c 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -32,6 +32,10 @@ #include "DiscordCoordinator.h" #endif +#ifdef BUILD_SDL +#include "input/SDLInputDriver.h" +#endif + using namespace QGBA; static GBAApp* g_app = nullptr; @@ -309,6 +313,25 @@ bool GBAApp::waitOnJob(qint64 jobId, QObject* context, std::function ca return true; } +void GBAApp::suspendScreensaver() { +#ifdef BUILD_SDL + SDL::suspendScreensaver(); +#endif +} + +void GBAApp::resumeScreensaver() { +#ifdef BUILD_SDL + SDL::resumeScreensaver(); +#endif +} + +void GBAApp::setScreensaverSuspendable(bool suspendable) { + UNUSED(suspendable); +#ifdef BUILD_SDL + SDL::setScreensaverSuspendable(suspendable); +#endif +} + void GBAApp::cleanupAfterUpdate() { // Remove leftover updater if there's one present QDir configDir(ConfigController::configDir()); diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index 6245dba3f..3110b0733 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -86,6 +86,10 @@ public slots: void restartForUpdate(); Window* newWindow(); + void suspendScreensaver(); + void resumeScreensaver(); + void setScreensaverSuspendable(bool); + signals: void jobFinished(qint64 jobId); diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 7890812e7..88963afa2 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -13,6 +13,7 @@ #include #include +#include "input/InputMapper.h" #include "InputController.h" #include "KeyEditor.h" @@ -214,8 +215,9 @@ void GBAKeyEditor::setNext() { void GBAKeyEditor::save() { #ifdef BUILD_SDL - m_controller->unbindAllAxes(m_type); - m_controller->unbindAllHats(m_type); + InputMapper mapper = m_controller->mapper(m_type); + mapper.unbindAllAxes(); + mapper.unbindAllHats(); #endif bindKey(m_keyDU, GBA_KEY_UP); @@ -260,7 +262,7 @@ void GBAKeyEditor::refresh() { #endif } -void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBAKey key) { +void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, int key) { #ifdef BUILD_SDL if (m_type == SDL_BINDING_BUTTON) { int value = mInputQueryBinding(map, m_type, key); @@ -275,14 +277,14 @@ void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBA 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 (description->highDirection != -1) { + KeyEditor* key = self->keyById(description->highDirection); if (key) { key->setValueAxis(axis, GamepadAxisEvent::POSITIVE); } } - if (description->lowDirection != GBA_KEY_NONE) { - KeyEditor* key = self->keyById(static_cast(description->lowDirection)); + if (description->lowDirection != -1) { + KeyEditor* key = self->keyById(description->lowDirection); if (key) { key->setValueAxis(axis, GamepadAxisEvent::NEGATIVE); } @@ -295,25 +297,25 @@ void GBAKeyEditor::lookupHats(const mInputMap* map) { int i = 0; while (mInputQueryHat(map, m_type, i, &bindings)) { if (bindings.up >= 0) { - KeyEditor* key = keyById(static_cast(bindings.up)); + KeyEditor* key = keyById(bindings.up); if (key) { key->setValueHat(i, GamepadHatEvent::UP); } } if (bindings.right >= 0) { - KeyEditor* key = keyById(static_cast(bindings.right)); + KeyEditor* key = keyById(bindings.right); if (key) { key->setValueHat(i, GamepadHatEvent::RIGHT); } } if (bindings.down >= 0) { - KeyEditor* key = keyById(static_cast(bindings.down)); + KeyEditor* key = keyById(bindings.down); if (key) { key->setValueHat(i, GamepadHatEvent::DOWN); } } if (bindings.left >= 0) { - KeyEditor* key = keyById(static_cast(bindings.left)); + KeyEditor* key = keyById(bindings.left); if (key) { key->setValueHat(i, GamepadHatEvent::LEFT); } @@ -323,16 +325,17 @@ void GBAKeyEditor::lookupHats(const mInputMap* map) { } #endif -void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) { +void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, int key) { + InputMapper mapper = m_controller->mapper(m_type); #ifdef BUILD_SDL if (m_type == SDL_BINDING_BUTTON && keyEditor->axis() >= 0) { - m_controller->bindAxis(m_type, keyEditor->axis(), keyEditor->direction(), key); + mapper.bindAxis(keyEditor->axis(), keyEditor->direction(), key); } if (m_type == SDL_BINDING_BUTTON && keyEditor->hat() >= 0) { - m_controller->bindHat(m_type, keyEditor->hat(), keyEditor->hatDirection(), key); + mapper.bindHat(keyEditor->hat(), keyEditor->hatDirection(), key); } #endif - m_controller->bindKey(m_type, keyEditor->value(), key); + mapper.bindKey(keyEditor->value(), key); } bool GBAKeyEditor::findFocus(KeyEditor* needle) { @@ -358,7 +361,7 @@ void GBAKeyEditor::selectGamepad(int index) { } #endif -KeyEditor* GBAKeyEditor::keyById(GBAKey key) { +KeyEditor* GBAKeyEditor::keyById(int key) { switch (key) { case GBA_KEY_UP: return m_keyDU; @@ -395,14 +398,13 @@ void GBAKeyEditor::setLocation(QWidget* widget, qreal x, qreal y) { #ifdef BUILD_SDL void GBAKeyEditor::updateJoysticks() { - m_controller->updateJoysticks(); - m_controller->recalibrateAxes(); + m_controller->update(); // Block the currentIndexChanged signal while rearranging the combo box auto wasBlocked = m_profileSelect->blockSignals(true); m_profileSelect->clear(); m_profileSelect->addItems(m_controller->connectedGamepads(m_type)); - int activeGamepad = m_controller->gamepad(m_type); + int activeGamepad = m_controller->gamepadIndex(m_type); m_profileSelect->setCurrentIndex(activeGamepad); m_profileSelect->blockSignals(wasBlocked); diff --git a/src/platform/qt/GBAKeyEditor.h b/src/platform/qt/GBAKeyEditor.h index 24b1e5c4a..9578abda9 100644 --- a/src/platform/qt/GBAKeyEditor.h +++ b/src/platform/qt/GBAKeyEditor.h @@ -54,8 +54,8 @@ private: void setLocation(QWidget* widget, qreal x, qreal y); - void lookupBinding(const mInputMap*, KeyEditor*, GBAKey); - void bindKey(const KeyEditor*, GBAKey); + void lookupBinding(const mInputMap*, KeyEditor*, int key); + void bindKey(const KeyEditor*, int key); bool findFocus(KeyEditor* needle = nullptr); @@ -64,7 +64,7 @@ private: void lookupHats(const mInputMap*); #endif - KeyEditor* keyById(GBAKey); + KeyEditor* keyById(int); QComboBox* m_profileSelect = nullptr; QWidget* m_clear = nullptr; diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index a0fb91397..0c94e7511 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014 Jeffrey Pfau +/* 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 @@ -6,8 +6,8 @@ #include "InputController.h" #include "ConfigController.h" -#include "GamepadAxisEvent.h" -#include "GamepadButtonEvent.h" +#include "input/Gamepad.h" +#include "input/GamepadButtonEvent.h" #include "InputProfile.h" #include "LogController.h" #include "utils.h" @@ -25,11 +25,6 @@ using namespace QGBA; -#ifdef BUILD_SDL -int InputController::s_sdlInited = 0; -mSDLEvents InputController::s_sdlEvents; -#endif - InputController::InputController(int playerId, QWidget* topLevel, QObject* parent) : QObject(parent) , m_playerId(playerId) @@ -38,23 +33,17 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren { mInputMapInit(&m_inputMap, &GBAInputInfo); -#ifdef BUILD_SDL - if (s_sdlInited == 0) { - mSDLInitEvents(&s_sdlEvents); - } - ++s_sdlInited; - m_sdlPlayer.bindings = &m_inputMap; - updateJoysticks(); -#endif - -#ifdef BUILD_SDL connect(&m_gamepadTimer, &QTimer::timeout, [this]() { - testGamepad(SDL_BINDING_BUTTON); + for (auto& driver : m_inputDrivers) { + if (driver->supportsPolling() && driver->supportsGamepads()) { + testGamepad(driver->type()); + } + } if (m_playerId == 0) { - updateJoysticks(); + update(); } }); -#endif + m_gamepadTimer.setInterval(50); m_gamepadTimer.start(); @@ -141,43 +130,32 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren InputController::~InputController() { mInputMapDeinit(&m_inputMap); +} -#ifdef BUILD_SDL - if (m_playerAttached) { - mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer); +void InputController::addInputDriver(std::shared_ptr driver) { + m_inputDrivers[driver->type()] = driver; + if (!m_sensorDriver && driver->supportsSensors()) { + m_sensorDriver = driver->type(); } - - --s_sdlInited; - if (s_sdlInited == 0) { - mSDLDeinitEvents(&s_sdlEvents); - } -#endif } void InputController::setConfiguration(ConfigController* config) { m_config = config; loadConfiguration(KEYBOARD); -#ifdef BUILD_SDL - mSDLEventsLoadConfig(&s_sdlEvents, config->input()); - if (!m_playerAttached) { - m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer); + for (auto& driver : m_inputDrivers) { + driver->loadConfiguration(config); } - if (!loadConfiguration(SDL_BINDING_BUTTON)) { - mSDLInitBindingsGBA(&m_inputMap); - } - loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON)); -#endif } bool InputController::loadConfiguration(uint32_t type) { if (!mInputMapLoad(&m_inputMap, type, m_config->input())) { return false; } -#ifdef BUILD_SDL - if (m_playerAttached) { - mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input()); + auto driver = m_inputDrivers.value(type); + if (!driver) { + return false; } -#endif + driver->loadConfiguration(m_config); return true; } @@ -186,7 +164,6 @@ bool InputController::loadProfile(uint32_t type, const QString& profile) { return false; } bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData()); - recalibrateAxes(); if (!loaded) { const InputProfile* ip = InputProfile::findProfile(profile); if (ip) { @@ -200,18 +177,18 @@ bool InputController::loadProfile(uint32_t type, const QString& profile) { void InputController::saveConfiguration() { saveConfiguration(KEYBOARD); -#ifdef BUILD_SDL - saveConfiguration(SDL_BINDING_BUTTON); - saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON)); - if (m_playerAttached) { - mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input()); + for (auto& driver : m_inputDrivers) { + driver->saveConfiguration(m_config); } -#endif m_config->write(); } void InputController::saveConfiguration(uint32_t type) { mInputMapSave(&m_inputMap, type, m_config->input()); + auto driver = m_inputDrivers.value(type); + if (driver) { + driver->saveConfiguration(m_config); + } m_config->write(); } @@ -223,372 +200,242 @@ void InputController::saveProfile(uint32_t type, const QString& profile) { m_config->write(); } -const char* InputController::profileForType(uint32_t type) { - UNUSED(type); -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { -#if SDL_VERSION_ATLEAST(2, 0, 0) - return SDL_JoystickName(m_sdlPlayer.joystick->joystick); -#else - return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick)); -#endif +QString InputController::profileForType(uint32_t type) { + auto driver = m_inputDrivers.value(type); + if (!driver) { + return {}; } -#endif - return 0; + return driver->currentProfile(); +} + +void InputController::setGamepadDriver(uint32_t type) { + auto driver = m_inputDrivers.value(type); + if (!driver || !driver->supportsGamepads()) { + return; + } + m_gamepadDriver = type; } QStringList InputController::connectedGamepads(uint32_t type) const { - UNUSED(type); - -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - QStringList pads; - for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { - const char* name; -#if SDL_VERSION_ATLEAST(2, 0, 0) - name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick); -#else - name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick)); -#endif - if (name) { - pads.append(QString(name)); - } else { - pads.append(QString()); - } - } - return pads; + if (!type) { + type = m_gamepadDriver; + } + auto driver = m_inputDrivers.value(type); + if (!driver) { + return {}; } -#endif - return QStringList(); + QStringList pads; + for (auto pad : driver->connectedGamepads()) { + pads.append(pad->visibleName()); + } + return pads; } -int InputController::gamepad(uint32_t type) const { -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0; +int InputController::gamepadIndex(uint32_t type) const { + if (!type) { + type = m_gamepadDriver; } -#endif - return 0; + auto driver = m_inputDrivers.value(type); + if (!driver) { + return -1; + } + return driver->activeGamepad(); } void InputController::setGamepad(uint32_t type, int index) { -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index); + if (!type) { + type = m_gamepadDriver; } -#endif + auto driver = m_inputDrivers.value(type); + if (!driver) { + return; + } + driver->setActiveGamepad(index); +} + +void InputController::setGamepad(int index) { + setGamepad(0, index); } void InputController::setPreferredGamepad(uint32_t type, int index) { if (!m_config) { return; } -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - char name[34] = {0}; - SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick), name, sizeof(name)); -#else - const char* name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick)); - if (!name) { + if (!type) { + type = m_gamepadDriver; + } + auto driver = m_inputDrivers.value(type); + if (!driver) { return; } -#endif - mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name); -#else - UNUSED(type); - UNUSED(index); -#endif + + auto pads = driver->connectedGamepads(); + if (index >= pads.count()) { + return; + } + + QString name = pads[index]->name(); + if (name.isEmpty()) { + return; + } + mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name.toUtf8().constData()); } -mRumble* InputController::rumble() { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - if (m_playerAttached) { - return &m_sdlPlayer.rumble.d; +void InputController::setPreferredGamepad(int index) { + setPreferredGamepad(0, index); +} + +InputMapper InputController::mapper(uint32_t type) { + return InputMapper(&m_inputMap, type); +} + +InputMapper InputController::mapper(InputDriver* driver) { + return InputMapper(&m_inputMap, driver->type()); +} + +InputMapper InputController::mapper(InputSource* source) { + return InputMapper(&m_inputMap, source->type()); +} + +void InputController::setSensorDriver(uint32_t type) { + auto driver = m_inputDrivers.value(type); + if (!driver || !driver->supportsSensors()) { + return; + } + m_sensorDriver = type; +} + + +mRumble* InputController::rumble() { + auto driver = m_inputDrivers.value(m_sensorDriver); + if (driver) { + return driver->rumble(); } -#endif -#endif return nullptr; } mRotationSource* InputController::rotationSource() { -#ifdef BUILD_SDL - if (m_playerAttached) { - return &m_sdlPlayer.rotation.d; + auto driver = m_inputDrivers.value(m_sensorDriver); + if (driver) { + return driver->rotationSource(); } -#endif return nullptr; } -void InputController::registerTiltAxisX(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.axisX = axis; - } -#endif +int InputController::mapKeyboard(int key) const { + return mInputMapKey(&m_inputMap, KEYBOARD, key); } -void InputController::registerTiltAxisY(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.axisY = axis; - } -#endif -} - -void InputController::registerGyroAxisX(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.gyroX = axis; - if (m_sdlPlayer.rotation.gyroY == axis) { - m_sdlPlayer.rotation.gyroZ = axis; - } else { - m_sdlPlayer.rotation.gyroZ = -1; +void InputController::update() { + for (auto& driver : m_inputDrivers) { + QString profile = profileForType(driver->type()); + driver->update(); + QString newProfile = profileForType(driver->type()); + if (profile != newProfile) { + loadProfile(driver->type(), newProfile); } } -#endif -} - -void InputController::registerGyroAxisY(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.gyroY = axis; - if (m_sdlPlayer.rotation.gyroX == axis) { - m_sdlPlayer.rotation.gyroZ = axis; - } else { - m_sdlPlayer.rotation.gyroZ = -1; - } - } -#endif -} - -float InputController::gyroSensitivity() const { -#ifdef BUILD_SDL - if (m_playerAttached) { - return m_sdlPlayer.rotation.gyroSensitivity; - } -#endif - return 0; -} - -void InputController::setGyroSensitivity(float sensitivity) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.gyroSensitivity = 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); - mSDLUpdateJoysticks(&s_sdlEvents, m_config->input()); - QString newProfile = profileForType(SDL_BINDING_BUTTON); - if (profile != newProfile) { - loadProfile(SDL_BINDING_BUTTON, newProfile); - } -#endif } int InputController::pollEvents() { int activeButtons = 0; -#ifdef BUILD_SDL - if (m_playerAttached && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numButtons = SDL_JoystickNumButtons(joystick); - int i; - QReadLocker l(&m_eventsLock); - for (i = 0; i < numButtons; ++i) { - GBAKey key = static_cast(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i)); - if (key == GBA_KEY_NONE) { - continue; - } - if (hasPendingEvent(key)) { - continue; - } - if (SDL_JoystickGetButton(joystick, i)) { - activeButtons |= 1 << key; - } - } - l.unlock(); - 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); - } - - 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)); - if (key != GBA_KEY_NONE) { - activeButtons |= 1 << key; - } + for (auto pad : gamepads()) { + InputMapper im(mapper(pad)); + activeButtons |= im.mapKeys(pad->currentButtons()); + activeButtons |= im.mapAxes(pad->currentAxes()); + activeButtons |= im.mapHats(pad->currentHats()); + } + for (int i = 0; i < GBA_KEY_MAX; ++i) { + if ((activeButtons & (1 << i)) && hasPendingEvent(i)) { + activeButtons ^= 1 << i; } } -#endif return activeButtons; } +Gamepad* InputController::gamepad(uint32_t type) { + auto driver = m_inputDrivers.value(type); + if (!driver) { + return nullptr; + } + if (!driver->supportsGamepads()) { + return nullptr; +} + QList driverPads(driver->connectedGamepads()); + int activeGamepad = driver->activeGamepad(); + if (activeGamepad < 0 || activeGamepad >= driverPads.count()) { + return nullptr; + } + return driverPads[activeGamepad]; +} + +QList InputController::gamepads() { + QList pads; + for (auto& driver : m_inputDrivers) { + if (!driver->supportsGamepads()) { + continue; + } + QList driverPads(driver->connectedGamepads()); + int activeGamepad = driver->activeGamepad(); + if (activeGamepad >= 0 && activeGamepad < driverPads.count()) { + pads.append(driverPads[activeGamepad]); + } + } + return pads; +} + QSet InputController::activeGamepadButtons(int type) { QSet activeButtons; -#ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numButtons = SDL_JoystickNumButtons(joystick); - int i; - for (i = 0; i < numButtons; ++i) { - if (SDL_JoystickGetButton(joystick, i)) { - activeButtons.insert(i); - } + Gamepad* pad = gamepad(type); + if (!pad) { + return {}; + } + auto allButtons = pad->currentButtons(); + for (int i = 0; i < allButtons.size(); ++i) { + if (allButtons[i]) { + activeButtons.insert(i); } } -#endif return activeButtons; } -void InputController::recalibrateAxes() { -#ifdef BUILD_SDL - if (m_playerAttached && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numAxes = SDL_JoystickNumAxes(joystick); - if (numAxes < 1) { - return; - } - m_deadzones.resize(numAxes); - int i; - for (i = 0; i < numAxes; ++i) { - m_deadzones[i] = SDL_JoystickGetAxis(joystick, i); - } - } -#endif -} - QSet> InputController::activeGamepadAxes(int type) { QSet> activeAxes; -#ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numAxes = SDL_JoystickNumAxes(joystick); - if (numAxes < 1) { - return activeAxes; + Gamepad* pad = gamepad(type); + if (!pad) { + return {}; + } + InputMapper im(mapper(type)); + auto allAxes = pad->currentAxes(); + for (int i = 0; i < allAxes.size(); ++i) { + if (allAxes[i] - im.axisCenter(i) >= im.axisThreshold(i)) { + activeAxes.insert(qMakePair(i, GamepadAxisEvent::POSITIVE)); + continue; } - m_deadzones.resize(numAxes); - int i; - for (i = 0; i < numAxes; ++i) { - int32_t axis = SDL_JoystickGetAxis(joystick, i); - axis -= m_deadzones[i]; - if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) { - activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE)); - } + if (allAxes[i] - im.axisCenter(i) <= -im.axisThreshold(i)) { + activeAxes.insert(qMakePair(i, GamepadAxisEvent::NEGATIVE)); + continue; } } -#endif return activeAxes; } -void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) { - const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis); - mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD }; - if (old) { - description = *old; - } - int deadzone = 0; - if (axis > 0 && m_deadzones.size() > axis) { - deadzone = m_deadzones[axis]; - } - switch (direction) { - case GamepadAxisEvent::NEGATIVE: - description.lowDirection = key; - - description.deadLow = deadzone - AXIS_THRESHOLD; - break; - case GamepadAxisEvent::POSITIVE: - description.highDirection = key; - description.deadHigh = deadzone + AXIS_THRESHOLD; - break; - default: - return; - } - mInputBindAxis(&m_inputMap, type, axis, &description); -} - -void InputController::unbindAllAxes(uint32_t type) { - mInputUnbindAllAxes(&m_inputMap, type); -} - QSet> InputController::activeGamepadHats(int type) { QSet> activeHats; -#ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numHats = SDL_JoystickNumHats(joystick); - if (numHats < 1) { - return activeHats; - } - - int i; - for (i = 0; i < numHats; ++i) { - int hat = SDL_JoystickGetHat(joystick, i); - if (hat & GamepadHatEvent::UP) { - activeHats.insert(qMakePair(i, GamepadHatEvent::UP)); - } - if (hat & GamepadHatEvent::RIGHT) { - activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT)); - } - if (hat & GamepadHatEvent::DOWN) { - activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN)); - } - if (hat & GamepadHatEvent::LEFT) { - activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT)); - } + Gamepad* pad = gamepad(type); + if (!pad) { + return {}; + } + auto allHats = pad->currentHats(); + for (int i = 0; i < allHats.size(); ++i) { + if (allHats[i] != GamepadHatEvent::CENTER) { + activeHats.insert(qMakePair(i, allHats[i])); } } -#endif return activeHats; } -void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) { - mInputHatBindings bindings{ -1, -1, -1, -1 }; - mInputQueryHat(&m_inputMap, type, hat, &bindings); - switch (direction) { - case GamepadHatEvent::UP: - bindings.up = gbaKey; - break; - case GamepadHatEvent::RIGHT: - bindings.right = gbaKey; - break; - case GamepadHatEvent::DOWN: - bindings.down = gbaKey; - break; - case GamepadHatEvent::LEFT: - bindings.left = gbaKey; - break; - default: - return; - } - mInputBindHat(&m_inputMap, type, hat, &bindings); -} - -void InputController::unbindAllHats(uint32_t type) { - mInputUnbindAllHats(&m_inputMap, type); -} - void InputController::testGamepad(int type) { QWriteLocker l(&m_eventsLock); auto activeAxes = activeGamepadAxes(type); @@ -614,16 +461,16 @@ void InputController::testGamepad(int type) { bool newlyAboveThreshold = activeAxes.contains(axis); if (newlyAboveThreshold) { GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this); - postPendingEvent(event->gbaKey()); + postPendingEvent(event->platformKey()); sendGamepadEvent(event); if (!event->isAccepted()) { - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); } } } for (auto axis : oldAxes) { GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this); - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); sendGamepadEvent(event); } @@ -636,15 +483,15 @@ void InputController::testGamepad(int type) { for (int button : activeButtons) { GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this); - postPendingEvent(event->gbaKey()); + postPendingEvent(event->platformKey()); sendGamepadEvent(event); if (!event->isAccepted()) { - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); } } for (int button : oldButtons) { GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this); - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); sendGamepadEvent(event); } @@ -653,15 +500,15 @@ void InputController::testGamepad(int type) { for (auto& hat : activeHats) { GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this); - postPendingEvent(event->gbaKey()); + postPendingEvent(event->platformKey()); sendGamepadEvent(event); if (!event->isAccepted()) { - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); } } for (auto& hat : oldHats) { GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this); - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); sendGamepadEvent(event); } } @@ -679,42 +526,18 @@ void InputController::sendGamepadEvent(QEvent* event) { QApplication::postEvent(focusWidget, event, Qt::HighEventPriority); } -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); } -void InputController::suspendScreensaver() { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - mSDLSuspendScreensaver(&s_sdlEvents); -#endif -#endif -} - -void InputController::resumeScreensaver() { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - mSDLResumeScreensaver(&s_sdlEvents); -#endif -#endif -} - -void InputController::setScreensaverSuspendable(bool suspendable) { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable); -#endif -#endif -} - void InputController::stealFocus(QWidget* focus) { m_focusParent = focus; } diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index a370ba80e..ab6590e7c 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -1,13 +1,16 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* 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 "GamepadAxisEvent.h" -#include "GamepadHatEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/GamepadHatEvent.h" +#include "input/InputDriver.h" +#include "input/InputMapper.h" +#include #include #include #include @@ -18,13 +21,8 @@ #include +#include #include -#include - -#ifdef BUILD_SDL -#include "platform/sdl/sdl-events.h" -#endif - #ifdef BUILD_QT_MULTIMEDIA #include "VideoDumper.h" @@ -37,6 +35,8 @@ struct mRumble; namespace QGBA { class ConfigController; +class Gamepad; +class InputSource; class InputController : public QObject { Q_OBJECT @@ -54,46 +54,43 @@ public: InputController(int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr); ~InputController(); + void addInputDriver(std::shared_ptr); + void setConfiguration(ConfigController* config); void saveConfiguration(); bool loadConfiguration(uint32_t type); bool loadProfile(uint32_t type, const QString& profile); void saveConfiguration(uint32_t type); void saveProfile(uint32_t type, const QString& profile); - const char* profileForType(uint32_t type); + QString profileForType(uint32_t type); - GBAKey mapKeyboard(int key) const; - - void bindKey(uint32_t type, int key, GBAKey); + int mapKeyboard(int key) const; + mInputMap* map() { return &m_inputMap; } const mInputMap* map() const { return &m_inputMap; } int pollEvents(); static const int32_t AXIS_THRESHOLD = 0x3000; - QSet activeGamepadButtons(int type); - QSet> activeGamepadAxes(int type); - QSet> activeGamepadHats(int type); - void recalibrateAxes(); - void bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction, GBAKey); - void unbindAllAxes(uint32_t type); + void setGamepadDriver(uint32_t type); + const InputDriver* gamepadDriver() const { return m_inputDrivers.value(m_sensorDriver).get(); } + InputDriver* gamepadDriver() { return m_inputDrivers.value(m_sensorDriver).get(); } - void bindHat(uint32_t type, int hat, GamepadHatEvent::Direction, GBAKey); - void unbindAllHats(uint32_t type); - - QStringList connectedGamepads(uint32_t type) const; - int gamepad(uint32_t type) const; + QStringList connectedGamepads(uint32_t type = 0) const; + int gamepadIndex(uint32_t type = 0) const; void setGamepad(uint32_t type, int index); + void setGamepad(int index); void setPreferredGamepad(uint32_t type, int index); + void setPreferredGamepad(int index); - void registerTiltAxisX(int axis); - void registerTiltAxisY(int axis); - void registerGyroAxisX(int axis); - void registerGyroAxisY(int axis); + InputMapper mapper(uint32_t type); + InputMapper mapper(InputDriver*); + InputMapper mapper(InputSource*); - float gyroSensitivity() const; - void setGyroSensitivity(float sensitivity); + void setSensorDriver(uint32_t type); + const InputDriver* sensorDriver() const { return m_inputDrivers.value(m_sensorDriver).get(); } + InputDriver* sensorDriver() { return m_inputDrivers.value(m_sensorDriver).get(); } void stealFocus(QWidget* focus); void releaseFocus(QWidget* focus); @@ -111,12 +108,7 @@ signals: public slots: void testGamepad(int type); - void updateJoysticks(); - - // TODO: Move these to somewhere that makes sense - void suspendScreensaver(); - void resumeScreensaver(); - void setScreensaverSuspendable(bool); + void update(); void increaseLuminanceLevel(); void decreaseLuminanceLevel(); @@ -136,11 +128,18 @@ private slots: void teardownCam(); private: - void postPendingEvent(GBAKey); - void clearPendingEvent(GBAKey); - bool hasPendingEvent(GBAKey) const; + void postPendingEvent(int); + void clearPendingEvent(int); + bool hasPendingEvent(int) const; void sendGamepadEvent(QEvent*); + Gamepad* gamepad(uint32_t type); + QList gamepads(); + + QSet activeGamepadButtons(int type); + QSet> activeGamepadAxes(int type); + QSet> activeGamepadHats(int type); + struct InputControllerLux : GBALuminanceSource { InputController* p; uint8_t value; @@ -170,21 +169,16 @@ private: QWidget* m_topLevel; QWidget* m_focusParent; -#ifdef BUILD_SDL - static int s_sdlInited; - static mSDLEvents s_sdlEvents; - mSDLPlayer m_sdlPlayer{}; - bool m_playerAttached = false; -#endif - - QVector m_deadzones; + QHash> m_inputDrivers; + uint32_t m_gamepadDriver; + uint32_t m_sensorDriver; QSet m_activeButtons; QSet> m_activeAxes; QSet> m_activeHats; QTimer m_gamepadTimer{nullptr}; - QSet m_pendingEvents; + QSet m_pendingEvents; QReadWriteLock m_eventsLock; }; diff --git a/src/platform/qt/InputProfile.cpp b/src/platform/qt/InputProfile.cpp index aedc47f6e..e8f7f1711 100644 --- a/src/platform/qt/InputProfile.cpp +++ b/src/platform/qt/InputProfile.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "InputProfile.h" +#include "input/InputMapper.h" #include "InputController.h" #include @@ -210,17 +211,23 @@ const InputProfile* InputProfile::findProfile(const QString& name) { } void InputProfile::apply(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)); -#endif + auto gamepadDriver = controller->gamepadDriver(); + if (gamepadDriver) { + InputMapper mapper = controller->mapper(gamepadDriver); + for (size_t i = 0; i < GBA_KEY_MAX; ++i) { + mapper.bindKey(m_keys[i], i); + mapper.bindAxis(m_axes[i].axis, m_axes[i].direction, i); + } + } + + InputDriver* sensorDriver = controller->sensorDriver(); + if (sensorDriver) { + sensorDriver->registerTiltAxisX(m_tiltAxis.x); + sensorDriver->registerTiltAxisY(m_tiltAxis.y); + sensorDriver->registerGyroAxisX(m_gyroAxis.x); + sensorDriver->registerGyroAxisY(m_gyroAxis.y); + sensorDriver->setGyroSensitivity(m_gyroSensitivity); } - controller->registerTiltAxisX(m_tiltAxis.x); - controller->registerTiltAxisY(m_tiltAxis.y); - controller->registerGyroAxisX(m_gyroAxis.x); - controller->registerGyroAxisY(m_gyroAxis.y); - controller->setGyroSensitivity(m_gyroSensitivity); } bool InputProfile::lookupShortcutButton(const QString& shortcutName, int* button) const { diff --git a/src/platform/qt/InputProfile.h b/src/platform/qt/InputProfile.h index 4e43d16a7..a7670a40c 100644 --- a/src/platform/qt/InputProfile.h +++ b/src/platform/qt/InputProfile.h @@ -5,9 +5,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#include "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include +#include namespace QGBA { diff --git a/src/platform/qt/KeyEditor.cpp b/src/platform/qt/KeyEditor.cpp index 95b83a9e8..e8da70970 100644 --- a/src/platform/qt/KeyEditor.cpp +++ b/src/platform/qt/KeyEditor.cpp @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "KeyEditor.h" -#include "GamepadAxisEvent.h" -#include "GamepadButtonEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/GamepadButtonEvent.h" #include "ShortcutController.h" #include diff --git a/src/platform/qt/KeyEditor.h b/src/platform/qt/KeyEditor.h index e74df7f27..c1bb56fae 100644 --- a/src/platform/qt/KeyEditor.h +++ b/src/platform/qt/KeyEditor.h @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#include "GamepadAxisEvent.h" -#include "GamepadHatEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/GamepadHatEvent.h" #include #include diff --git a/src/platform/qt/LoadSaveState.cpp b/src/platform/qt/LoadSaveState.cpp index 3198a4c90..89cc7f65a 100644 --- a/src/platform/qt/LoadSaveState.cpp +++ b/src/platform/qt/LoadSaveState.cpp @@ -6,8 +6,8 @@ #include "LoadSaveState.h" #include "CoreController.h" -#include "GamepadAxisEvent.h" -#include "GamepadButtonEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/GamepadButtonEvent.h" #include "VFileDevice.h" #include "utils.h" @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -131,13 +132,13 @@ bool LoadSaveState::eventFilter(QObject* object, QEvent* event) { if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadAxisEvent::Type()) { int column = m_currentFocus % 3; int row = m_currentFocus - column; - GBAKey key = GBA_KEY_NONE; + int key = -1; if (event->type() == GamepadButtonEvent::Down()) { - key = static_cast(event)->gbaKey(); + key = static_cast(event)->platformKey(); } else if (event->type() == GamepadAxisEvent::Type()) { GamepadAxisEvent* gae = static_cast(event); if (gae->isNew()) { - key = gae->gbaKey(); + key = gae->platformKey(); } else { return false; } diff --git a/src/platform/qt/ReportView.cpp b/src/platform/qt/ReportView.cpp index 494f45a64..c38c84a03 100644 --- a/src/platform/qt/ReportView.cpp +++ b/src/platform/qt/ReportView.cpp @@ -314,10 +314,8 @@ void ReportView::generateReport() { } else { windowReport << QString("ROM open: No"); } -#ifdef BUILD_SDL InputController* input = window->inputController(); - windowReport << QString("Active gamepad: %1").arg(input->gamepad(SDL_BINDING_BUTTON)); -#endif + windowReport << QString("Active gamepad: %1").arg(input->gamepadIndex()); windowReport << QString("Configuration: %1").arg(configs.indexOf(config) + 1); addReport(QString("Window %1").arg(winId), windowReport.join('\n')); } @@ -477,9 +475,8 @@ void ReportView::addGLInfo(QStringList& report) { } void ReportView::addGamepadInfo(QStringList& report) { -#ifdef BUILD_SDL InputController* input = GBAApp::app()->windows()[0]->inputController(); - QStringList gamepads = input->connectedGamepads(SDL_BINDING_BUTTON); + QStringList gamepads = input->connectedGamepads(); report << QString("Connected gamepads: %1").arg(gamepads.size()); int i = 0; for (const auto& gamepad : gamepads) { @@ -490,10 +487,9 @@ void ReportView::addGamepadInfo(QStringList& report) { i = 0; for (Window* window : GBAApp::app()->windows()) { ++i; - report << QString("Window %1 gamepad: %2").arg(i).arg(window->inputController()->gamepad(SDL_BINDING_BUTTON)); + report << QString("Window %1 gamepad: %2").arg(i).arg(window->inputController()->gamepadIndex()); } } -#endif } void ReportView::addROMInfo(QStringList& report, CoreController* controller) { diff --git a/src/platform/qt/SensorView.cpp b/src/platform/qt/SensorView.cpp index 22c2f5f3f..ba1cf0ce9 100644 --- a/src/platform/qt/SensorView.cpp +++ b/src/platform/qt/SensorView.cpp @@ -6,7 +6,8 @@ #include "SensorView.h" #include "CoreController.h" -#include "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/InputDriver.h" #include "InputController.h" #include "utils.h" @@ -47,14 +48,20 @@ SensorView::SensorView(InputController* input, QWidget* parent) m_timer.start(); } - jiggerer(m_ui.tiltSetX, &InputController::registerTiltAxisX); - jiggerer(m_ui.tiltSetY, &InputController::registerTiltAxisY); - jiggerer(m_ui.gyroSetX, &InputController::registerGyroAxisX); - jiggerer(m_ui.gyroSetY, &InputController::registerGyroAxisY); + jiggerer(m_ui.tiltSetX, &InputDriver::registerTiltAxisX); + jiggerer(m_ui.tiltSetY, &InputDriver::registerTiltAxisY); + jiggerer(m_ui.gyroSetX, &InputDriver::registerGyroAxisX); + jiggerer(m_ui.gyroSetY, &InputDriver::registerGyroAxisY); - m_ui.gyroSensitivity->setValue(m_input->gyroSensitivity() / 1e8f); + InputDriver* sensorDriver = m_input->sensorDriver(); + if (sensorDriver) { + m_ui.gyroSensitivity->setValue(sensorDriver->gyroSensitivity() / 1e8f); + } connect(m_ui.gyroSensitivity, static_cast(&QDoubleSpinBox::valueChanged), [this](double value) { - m_input->setGyroSensitivity(value * 1e8f); + InputDriver* sensorDriver = m_input->sensorDriver(); + if (sensorDriver) { + sensorDriver->setGyroSensitivity(value * 1e8f); + } }); m_input->stealFocus(this); connect(m_input, &InputController::luminanceValueChanged, this, &SensorView::luminanceValueChanged); @@ -84,7 +91,7 @@ void SensorView::setController(std::shared_ptr controller) { }); } -void SensorView::jiggerer(QAbstractButton* button, void (InputController::*setter)(int)) { +void SensorView::jiggerer(QAbstractButton* button, void (InputDriver::*setter)(int)) { connect(button, &QAbstractButton::toggled, [this, button, setter](bool checked) { if (!checked) { m_button = nullptr; @@ -115,7 +122,10 @@ bool SensorView::eventFilter(QObject*, QEvent* event) { m_button->removeEventFilter(this); m_button->clearFocus(); m_button->setChecked(false); - (m_input->*m_setter)(gae->axis()); + InputDriver* sensorDriver = m_input->sensorDriver(); + if (sensorDriver) { + (sensorDriver->*m_setter)(gae->axis()); + } m_button = nullptr; } return true; diff --git a/src/platform/qt/SensorView.h b/src/platform/qt/SensorView.h index 5ccc40ec8..2bb5047cd 100644 --- a/src/platform/qt/SensorView.h +++ b/src/platform/qt/SensorView.h @@ -21,6 +21,7 @@ class ConfigController; class CoreController; class GamepadAxisEvent; class InputController; +class InputDriver; class SensorView : public QDialog { Q_OBJECT @@ -43,14 +44,14 @@ private: Ui::SensorView m_ui; QAbstractButton* m_button = nullptr; - void (InputController::*m_setter)(int); + void (InputDriver::*m_setter)(int); std::shared_ptr m_controller; InputController* m_input; mRotationSource* m_rotation; QTimer m_timer; - void jiggerer(QAbstractButton*, void (InputController::*)(int)); + void jiggerer(QAbstractButton*, void (InputDriver::*)(int)); }; } diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index cba481dc8..4eca3cb46 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -25,6 +25,10 @@ #include #include +#ifdef BUILD_SDL +#include "platform/sdl/sdl-events.h" +#endif + using namespace QGBA; SettingsView::SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, LogController* logController, QWidget* parent) @@ -326,8 +330,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC GBAKeyEditor* buttonEditor = nullptr; #ifdef BUILD_SDL - inputController->recalibrateAxes(); - const char* profile = inputController->profileForType(SDL_BINDING_BUTTON); + QString profile = inputController->profileForType(SDL_BINDING_BUTTON); buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile); addPage(tr("Controllers"), buttonEditor, Page::CONTROLLERS); connect(m_ui.buttonBox, &QDialogButtonBox::accepted, buttonEditor, &GBAKeyEditor::save); diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index d401fd1ba..e81c01b26 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -6,7 +6,7 @@ #include "ShortcutController.h" #include "ConfigController.h" -#include "GamepadButtonEvent.h" +#include "input/GamepadButtonEvent.h" #include "InputProfile.h" #include diff --git a/src/platform/qt/ShortcutController.h b/src/platform/qt/ShortcutController.h index eb24cdc96..c4db4241e 100644 --- a/src/platform/qt/ShortcutController.h +++ b/src/platform/qt/ShortcutController.h @@ -6,7 +6,7 @@ #pragma once #include "ActionMapper.h" -#include "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include #include diff --git a/src/platform/qt/ShortcutView.cpp b/src/platform/qt/ShortcutView.cpp index e82c2dd9b..5efbc4d62 100644 --- a/src/platform/qt/ShortcutView.cpp +++ b/src/platform/qt/ShortcutView.cpp @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ShortcutView.h" -#include "GamepadButtonEvent.h" #include "InputController.h" +#include "input/GamepadButtonEvent.h" #include "ShortcutController.h" #include "ShortcutModel.h" diff --git a/src/platform/qt/ShortcutView.h b/src/platform/qt/ShortcutView.h index 903d365c8..d2e307419 100644 --- a/src/platform/qt/ShortcutView.h +++ b/src/platform/qt/ShortcutView.h @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#include "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index cdfb5b9c1..c07d82704 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -36,6 +36,9 @@ #include "GDBController.h" #include "GDBWindow.h" #include "GIFView.h" +#ifdef BUILD_SDL +#include "input/SDLInputDriver.h" +#endif #include "IOViewer.h" #include "LoadSaveState.h" #include "LogView.h" @@ -76,6 +79,7 @@ #include #endif #include +#include #include #include @@ -170,6 +174,10 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi m_mustReset.setInterval(MUST_RESTART_TIMEOUT); m_mustReset.setSingleShot(true); +#ifdef BUILD_SDL + m_inputController.addInputDriver(std::make_shared(&m_inputController)); +#endif + m_shortcutController->setConfigController(m_config); m_shortcutController->setActionMapper(&m_actions); setupMenu(menuBar()); @@ -297,7 +305,7 @@ void Window::reloadConfig() { m_display->resizeContext(); } - m_inputController.setScreensaverSuspendable(opts->suspendScreensaver); + GBAApp::app()->setScreensaverSuspendable(opts->suspendScreensaver); } void Window::saveConfig() { @@ -631,8 +639,8 @@ void Window::keyPressEvent(QKeyEvent* event) { QWidget::keyPressEvent(event); return; } - GBAKey key = m_inputController.mapKeyboard(event->key()); - if (key == GBA_KEY_NONE) { + int key = m_inputController.mapKeyboard(event->key()); + if (key == -1) { QWidget::keyPressEvent(event); return; } @@ -647,8 +655,8 @@ void Window::keyReleaseEvent(QKeyEvent* event) { QWidget::keyReleaseEvent(event); return; } - GBAKey key = m_inputController.mapKeyboard(event->key()); - if (key == GBA_KEY_NONE) { + int key = m_inputController.mapKeyboard(event->key()); + if (key == -1) { QWidget::keyPressEvent(event); return; } @@ -1999,7 +2007,6 @@ void Window::setController(CoreController* controller, const QString& fname) { } m_controller = std::shared_ptr(controller); - m_inputController.recalibrateAxes(); m_controller->setInputController(&m_inputController); m_controller->setLogger(&m_log); @@ -2012,14 +2019,14 @@ void Window::setController(CoreController* controller, const QString& fname) { }); connect(m_controller.get(), &CoreController::started, this, &Window::gameStarted); - connect(m_controller.get(), &CoreController::started, &m_inputController, &InputController::suspendScreensaver); + connect(m_controller.get(), &CoreController::started, GBAApp::app(), &GBAApp::suspendScreensaver); connect(m_controller.get(), &CoreController::stopping, this, &Window::gameStopped); { connect(m_controller.get(), &CoreController::stopping, [this]() { m_controller.reset(); }); } - connect(m_controller.get(), &CoreController::stopping, &m_inputController, &InputController::resumeScreensaver); + connect(m_controller.get(), &CoreController::stopping, GBAApp::app(), &GBAApp::resumeScreensaver); connect(m_controller.get(), &CoreController::paused, this, &Window::updateFrame); #ifndef Q_OS_MAC @@ -2031,14 +2038,14 @@ void Window::setController(CoreController* controller, const QString& fname) { }); #endif - connect(m_controller.get(), &CoreController::paused, &m_inputController, &InputController::resumeScreensaver); + connect(m_controller.get(), &CoreController::paused, GBAApp::app(), &GBAApp::resumeScreensaver); connect(m_controller.get(), &CoreController::paused, [this]() { emit paused(true); }); connect(m_controller.get(), &CoreController::unpaused, [this]() { emit paused(false); }); - connect(m_controller.get(), &CoreController::unpaused, &m_inputController, &InputController::suspendScreensaver); + connect(m_controller.get(), &CoreController::unpaused, GBAApp::app(), &GBAApp::suspendScreensaver); connect(m_controller.get(), &CoreController::frameAvailable, this, &Window::recordFrame); connect(m_controller.get(), &CoreController::crashed, this, &Window::gameCrashed); connect(m_controller.get(), &CoreController::failed, this, &Window::gameFailed); 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/GamepadAxisEvent.cpp b/src/platform/qt/input/GamepadAxisEvent.cpp similarity index 78% rename from src/platform/qt/GamepadAxisEvent.cpp rename to src/platform/qt/input/GamepadAxisEvent.cpp index 937e055ed..396574720 100644 --- a/src/platform/qt/GamepadAxisEvent.cpp +++ b/src/platform/qt/input/GamepadAxisEvent.cpp @@ -1,9 +1,9 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* 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 "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include "InputController.h" @@ -16,11 +16,11 @@ GamepadAxisEvent::GamepadAxisEvent(int axis, Direction direction, bool isNew, in , m_axis(axis) , m_direction(direction) , m_isNew(isNew) - , m_key(GBA_KEY_NONE) + , m_key(-1) { ignore(); if (controller) { - m_key = static_cast(mInputMapAxis(controller->map(), type, axis, direction * INT_MAX)); + m_key = mInputMapAxis(controller->map(), type, axis, direction * INT_MAX); } } diff --git a/src/platform/qt/GamepadAxisEvent.h b/src/platform/qt/input/GamepadAxisEvent.h similarity index 89% rename from src/platform/qt/GamepadAxisEvent.h rename to src/platform/qt/input/GamepadAxisEvent.h index 75d887425..3f88f4be0 100644 --- a/src/platform/qt/GamepadAxisEvent.h +++ b/src/platform/qt/input/GamepadAxisEvent.h @@ -7,8 +7,6 @@ #include -#include - namespace QGBA { class InputController; @@ -26,7 +24,7 @@ public: int axis() const { return m_axis; } Direction direction() const { return m_direction; } bool isNew() const { return m_isNew; } - GBAKey gbaKey() const { return m_key; } + int platformKey() const { return m_key; } static enum Type Type(); @@ -36,7 +34,7 @@ private: int m_axis; Direction m_direction; bool m_isNew; - GBAKey m_key; + int m_key; }; } diff --git a/src/platform/qt/GamepadButtonEvent.cpp b/src/platform/qt/input/GamepadButtonEvent.cpp similarity index 83% rename from src/platform/qt/GamepadButtonEvent.cpp rename to src/platform/qt/input/GamepadButtonEvent.cpp index 6657a2c69..5ad9ba8f1 100644 --- a/src/platform/qt/GamepadButtonEvent.cpp +++ b/src/platform/qt/input/GamepadButtonEvent.cpp @@ -1,9 +1,9 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* 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 "GamepadButtonEvent.h" +#include "input/GamepadButtonEvent.h" #include "InputController.h" @@ -15,11 +15,11 @@ QEvent::Type GamepadButtonEvent::s_upType = QEvent::None; GamepadButtonEvent::GamepadButtonEvent(QEvent::Type pressType, int button, int type, InputController* controller) : QEvent(pressType) , m_button(button) - , m_key(GBA_KEY_NONE) + , m_key(-1) { ignore(); if (controller) { - m_key = static_cast(mInputMapKey(controller->map(), type, button)); + m_key = mInputMapKey(controller->map(), type, button); } } diff --git a/src/platform/qt/GamepadButtonEvent.h b/src/platform/qt/input/GamepadButtonEvent.h similarity index 81% rename from src/platform/qt/GamepadButtonEvent.h rename to src/platform/qt/input/GamepadButtonEvent.h index 5ba444490..ed2200d91 100644 --- a/src/platform/qt/GamepadButtonEvent.h +++ b/src/platform/qt/input/GamepadButtonEvent.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* 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 @@ -7,8 +7,6 @@ #include -#include - namespace QGBA { class InputController; @@ -18,7 +16,7 @@ public: GamepadButtonEvent(Type pressType, int button, int type, InputController* controller = nullptr); int value() const { return m_button; } - GBAKey gbaKey() const { return m_key; } + int platformKey() const { return m_key; } static Type Down(); static Type Up(); @@ -28,7 +26,7 @@ private: static Type s_upType; int m_button; - GBAKey m_key; + int m_key; }; } diff --git a/src/platform/qt/GamepadHatEvent.cpp b/src/platform/qt/input/GamepadHatEvent.cpp similarity index 83% rename from src/platform/qt/GamepadHatEvent.cpp rename to src/platform/qt/input/GamepadHatEvent.cpp index 052783e8e..1ca0408b0 100644 --- a/src/platform/qt/GamepadHatEvent.cpp +++ b/src/platform/qt/input/GamepadHatEvent.cpp @@ -1,9 +1,9 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau +/* 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 "GamepadHatEvent.h" +#include "input/GamepadHatEvent.h" #include "InputController.h" @@ -16,11 +16,11 @@ GamepadHatEvent::GamepadHatEvent(QEvent::Type pressType, int hatId, Direction di : QEvent(pressType) , m_hatId(hatId) , m_direction(direction) - , m_key(GBA_KEY_NONE) + , m_key(-1) { ignore(); if (controller) { - m_key = static_cast(mInputMapHat(controller->map(), type, hatId, direction)); + m_key = mInputMapHat(controller->map(), type, hatId, direction); } } diff --git a/src/platform/qt/GamepadHatEvent.h b/src/platform/qt/input/GamepadHatEvent.h similarity index 89% rename from src/platform/qt/GamepadHatEvent.h rename to src/platform/qt/input/GamepadHatEvent.h index ecaf1cd2b..2ba73088d 100644 --- a/src/platform/qt/GamepadHatEvent.h +++ b/src/platform/qt/input/GamepadHatEvent.h @@ -7,8 +7,6 @@ #include -#include - namespace QGBA { class InputController; @@ -27,7 +25,7 @@ public: int hatId() const { return m_hatId; } Direction direction() const { return m_direction; } - GBAKey gbaKey() const { return m_key; } + int platformKey() const { return m_key; } static Type Down(); static Type Up(); @@ -38,7 +36,7 @@ private: int m_hatId; Direction m_direction; - GBAKey m_key; + int m_key; }; } diff --git a/src/platform/qt/input/InputDriver.cpp b/src/platform/qt/input/InputDriver.cpp new file mode 100644 index 000000000..912395e0d --- /dev/null +++ b/src/platform/qt/input/InputDriver.cpp @@ -0,0 +1,85 @@ +/* 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*) { +} + +bool InputDriver::supportsPolling() const { + return false; +} + +bool InputDriver::supportsGamepads() const { + return false; +} + +bool InputDriver::supportsSensors() const { + return false; +} + +void InputDriver::bindDefaults(InputController*) { +} + +QList InputDriver::connectedKeySources() const { + return {}; +} + +QList InputDriver::connectedGamepads() const { + return {}; +} + +int InputDriver::activeKeySource() const { + return -1; +} + +int InputDriver::activeGamepad() const { + return -1; +} + +void InputDriver::setActiveKeySource(int) { +} + +void InputDriver::setActiveGamepad(int) { +} + +void InputDriver::registerTiltAxisX(int) { +} + +void InputDriver::registerTiltAxisY(int) { +} + +void InputDriver::registerGyroAxisX(int) { +} + +void InputDriver::registerGyroAxisY(int) { +} + +void InputDriver::registerGyroAxisZ(int) { +} + +float InputDriver::gyroSensitivity() const { + return 0; +} + +void InputDriver::setGyroSensitivity(float) { +} + +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..f344b271b --- /dev/null +++ b/src/platform/qt/input/InputDriver.h @@ -0,0 +1,66 @@ +/* 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 QString currentProfile() const = 0; + + virtual bool supportsPolling() const; + virtual bool supportsGamepads() const; + virtual bool supportsSensors() const; + + 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 int activeKeySource() const; + virtual int activeGamepad() const; + + virtual void setActiveKeySource(int); + virtual void setActiveGamepad(int); + + virtual void registerTiltAxisX(int axis); + virtual void registerTiltAxisY(int axis); + virtual void registerGyroAxisX(int axis); + virtual void registerGyroAxisY(int axis); + virtual void registerGyroAxisZ(int axis); + + virtual float gyroSensitivity() const; + virtual void setGyroSensitivity(float sensitivity); + + virtual mRumble* rumble(); + virtual mRotationSource* rotationSource(); +}; + +} diff --git a/src/platform/qt/input/InputMapper.cpp b/src/platform/qt/input/InputMapper.cpp new file mode 100644 index 000000000..41a3bc8c3 --- /dev/null +++ b/src/platform/qt/input/InputMapper.cpp @@ -0,0 +1,184 @@ +/* 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/InputMapper.h" + +#include + +using namespace QGBA; + +InputMapper::InputMapper(mInputMap* map, uint32_t type) + : m_map(map) + , m_type(type) +{ +} + +int InputMapper::mapKey(int key) const { + return mInputMapKey(m_map, m_type, key); +} + +int InputMapper::mapAxis(int axis, int16_t value) const { + return mInputMapAxis(m_map, m_type, axis, value); +} + +int InputMapper::mapHat(int hat, GamepadHatEvent::Direction direction) const { + return mInputMapHat(m_map, m_type, hat, direction); +} + +int InputMapper::mapKeys(QList keys) const { + int platformKeys = 0; + for (int i = 0; i < keys.count(); ++i) { + if (!keys[i]) { + continue; + } + int platformKey = mInputMapKey(m_map, m_type, i); + if (platformKey >= 0) { + platformKeys |= 1 << platformKey; + } + } + return platformKeys; +} + +int InputMapper::mapKeys(QSet keys) const { + int platformKeys = 0; + for (int key : keys) { + int platformKey = mInputMapKey(m_map, m_type, key); + if (platformKey >= 0) { + platformKeys |= 1 << platformKey; + } + } + return platformKeys; +} + +int InputMapper::mapAxes(QList axes) const { + int platformKeys = 0; + for (int i = 0; i < axes.count(); ++i) { + int platformKey = mInputMapAxis(m_map, m_type, i, axes[i]); + if (platformKey >= 0) { + platformKeys |= 1 << platformKey; + } + } + return platformKeys; +} + +int InputMapper::mapHats(QList hats) const { + int platformKeys = 0; + for (int i = 0; i < hats.count(); ++i) { + int platformKey = mInputMapHat(m_map, m_type, i, hats[i]); + if (platformKey >= 0) { + platformKeys |= 1 << platformKey; + } + } + return platformKeys; +} + +void InputMapper::bindKey(int key, int platformKey) { + mInputBindKey(m_map, m_type, key, platformKey); +} + +void InputMapper::bindAxis(int axis, GamepadAxisEvent::Direction direction, int platformKey) { + const mInputAxis* old = mInputQueryAxis(m_map, m_type, axis); + mInputAxis description = { -1, -1, -axisThreshold(axis), axisThreshold(axis) }; + if (old) { + description = *old; + } + switch (direction) { + case GamepadAxisEvent::NEGATIVE: + description.lowDirection = platformKey; + description.deadLow = axisCenter(axis) - axisThreshold(axis); + break; + case GamepadAxisEvent::POSITIVE: + description.highDirection = platformKey; + description.deadHigh = axisCenter(axis) + axisThreshold(axis); + break; + default: + return; + } + mInputBindAxis(m_map, m_type, axis, &description); +} + +void InputMapper::bindHat(int hat, GamepadHatEvent::Direction direction, int platformKey) { + mInputHatBindings bindings{ -1, -1, -1, -1 }; + mInputQueryHat(m_map, m_type, hat, &bindings); + switch (direction) { + case GamepadHatEvent::UP: + bindings.up = platformKey; + break; + case GamepadHatEvent::RIGHT: + bindings.right = platformKey; + break; + case GamepadHatEvent::DOWN: + bindings.down = platformKey; + break; + case GamepadHatEvent::LEFT: + bindings.left = platformKey; + break; + default: + return; + } + mInputBindHat(m_map, m_type, hat, &bindings); +} + +QSet InputMapper::queryKeyBindings(int platformKey) const { + return {mInputQueryBinding(m_map, m_type, platformKey)}; +} + +QSet> InputMapper::queryAxisBindings(int platformKey) const { + QPair>> userdata; + userdata.first = platformKey; + + mInputEnumerateAxes(m_map, m_type, [](int axis, const struct mInputAxis* description, void* user) { + auto userdata = static_cast>>*>(user); + int platformKey = userdata->first; + auto& bindings = userdata->second; + + if (description->lowDirection == platformKey) { + bindings.insert({axis, GamepadAxisEvent::NEGATIVE}); + } + if (description->highDirection == platformKey) { + bindings.insert({axis, GamepadAxisEvent::POSITIVE}); + } + }, &userdata); + + return userdata.second; +} + +QSet> InputMapper::queryHatBindings(int platformKey) const { + QPair>> userdata; + userdata.first = platformKey; + + mInputEnumerateHats(m_map, m_type, [](int hat, const struct mInputHatBindings* description, void* user) { + auto userdata = static_cast>>*>(user); + int platformKey = userdata->first; + auto& bindings = userdata->second; + + if (description->up == platformKey) { + bindings.insert({hat, GamepadHatEvent::UP}); + } + if (description->right == platformKey) { + bindings.insert({hat, GamepadHatEvent::RIGHT}); + } + if (description->down == platformKey) { + bindings.insert({hat, GamepadHatEvent::DOWN}); + } + if (description->left == platformKey) { + bindings.insert({hat, GamepadHatEvent::LEFT}); + } + }, &userdata); + + return userdata.second; +} + +void InputMapper::unbindAllKeys() { + mInputUnbindAllKeys(m_map, m_type); +} + +void InputMapper::unbindAllAxes() { + mInputUnbindAllAxes(m_map, m_type); +} + +void InputMapper::unbindAllHats() { + mInputUnbindAllHats(m_map, m_type); +} diff --git a/src/platform/qt/input/InputMapper.h b/src/platform/qt/input/InputMapper.h new file mode 100644 index 000000000..0e8268356 --- /dev/null +++ b/src/platform/qt/input/InputMapper.h @@ -0,0 +1,56 @@ +/* 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 "GamepadAxisEvent.h" +#include "GamepadHatEvent.h" + +struct mInputMap; + +namespace QGBA { + +class InputMapper final { +public: + static const int32_t DEFAULT_AXIS_THRESHOLD = 0x3000; + + InputMapper(mInputMap* map, uint32_t type); + + mInputMap* inputMap() const { return m_map; } + uint32_t type() const { return m_type; } + + int mapKey(int key) const; + int mapAxis(int axis, int16_t value) const; + int mapHat(int hat, GamepadHatEvent::Direction) const; + + int mapKeys(QList keys) const; + int mapKeys(QSet keys) const; + int mapAxes(QList axes) const; + int mapHats(QList hats) const; + + void bindKey(int key, int platformKey); + void bindAxis(int axis, GamepadAxisEvent::Direction, int platformKey); + void bindHat(int hat, GamepadHatEvent::Direction, int platformKey); + + QSet queryKeyBindings(int platformKey) const; + QSet> queryAxisBindings(int platformKey) const; + QSet> queryHatBindings(int platformKey) const; + + int16_t axisThreshold(int) const { return DEFAULT_AXIS_THRESHOLD; } + int16_t axisCenter(int) const { return 0; } + + void unbindAllKeys(); + void unbindAllAxes(); + void unbindAllHats(); + +private: + mInputMap* m_map; + uint32_t m_type; +}; + +} 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..3bf7350d7 --- /dev/null +++ b/src/platform/qt/input/InputSource.h @@ -0,0 +1,32 @@ +/* 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 "input/InputDriver.h" + +namespace QGBA { + +class InputSource : public QObject { +Q_OBJECT + +public: + InputSource(InputDriver* driver, QObject* parent = nullptr); + virtual ~InputSource() = default; + + InputDriver* driver() { return m_driver; } + uint32_t type() { return m_driver->type(); } + + 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..2eda6a4f0 --- /dev/null +++ b/src/platform/qt/input/SDLInputDriver.cpp @@ -0,0 +1,357 @@ +/* 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; + +void SDL::suspendScreensaver() { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (s_sdlInited) { + mSDLSuspendScreensaver(&s_sdlEvents); + } +#endif +} + +void SDL::resumeScreensaver() { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (s_sdlInited) { + mSDLResumeScreensaver(&s_sdlEvents); + } +#endif +} + +void SDL::setScreensaverSuspendable(bool suspendable) { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (s_sdlInited) { + mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable); + } +#endif +} + +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); + } +} + +bool SDLInputDriver::supportsPolling() const { + return true; +} + +bool SDLInputDriver::supportsGamepads() const { + return true; +} + +bool SDLInputDriver::supportsSensors() const { + return true; +} + +QString SDLInputDriver::currentProfile() const { + if (m_sdlPlayer.joystick) { +#if SDL_VERSION_ATLEAST(2, 0, 0) + return SDL_JoystickName(m_sdlPlayer.joystick->joystick); +#else + return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick)); +#endif + } + return {}; +} + +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 + +int SDLInputDriver::activeGamepad() const { + return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0; +} + +void SDLInputDriver::setActiveGamepad(int index) { + mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index); +} + +void SDLInputDriver::registerTiltAxisX(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.axisX = axis; + } +} + +void SDLInputDriver::registerTiltAxisY(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.axisY = axis; + } +} + +void SDLInputDriver::registerGyroAxisX(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroX = axis; + if (m_sdlPlayer.rotation.gyroY == axis) { + m_sdlPlayer.rotation.gyroZ = axis; + } else { + m_sdlPlayer.rotation.gyroZ = -1; + } + } +} + +void SDLInputDriver::registerGyroAxisY(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroY = axis; + if (m_sdlPlayer.rotation.gyroX == axis) { + m_sdlPlayer.rotation.gyroZ = axis; + } else { + m_sdlPlayer.rotation.gyroZ = -1; + } + } +} + +void SDLInputDriver::registerGyroAxisZ(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroZ = axis; + m_sdlPlayer.rotation.gyroX = -1; + m_sdlPlayer.rotation.gyroY = -1; + } +} + +float SDLInputDriver::gyroSensitivity() const { + if (m_playerAttached) { + return m_sdlPlayer.rotation.gyroSensitivity; + } + return 0; +} + +void SDLInputDriver::setGyroSensitivity(float sensitivity) { + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroSensitivity = sensitivity; + } +} + +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..79deac22c --- /dev/null +++ b/src/platform/qt/input/SDLInputDriver.h @@ -0,0 +1,107 @@ +/* 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; + +namespace SDL { + void suspendScreensaver(); + void resumeScreensaver(); + void setScreensaverSuspendable(bool); +} + +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"); } + QString currentProfile() const override; + + bool supportsPolling() const override; + bool supportsGamepads() const override; + bool supportsSensors() const override; + + void loadConfiguration(ConfigController* config) override; + void saveConfiguration(ConfigController* config) override; + + void bindDefaults(InputController*) override; + + bool update() override; + + QList connectedGamepads() const override; + + int activeGamepad() const override; + void setActiveGamepad(int) override; + + void registerTiltAxisX(int axis) override; + void registerTiltAxisY(int axis) override; + void registerGyroAxisX(int axis) override; + void registerGyroAxisY(int axis) override; + void registerGyroAxisZ(int axis) override; + + float gyroSensitivity() const override; + void setGyroSensitivity(float sensitivity) 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; +}; + +} diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp index 67668b011..b3d4bafc9 100644 --- a/src/platform/qt/main.cpp +++ b/src/platform/qt/main.cpp @@ -14,6 +14,10 @@ #include #include +#ifdef BUILD_SDL +#include "platform/sdl/sdl-events.h" +#endif + #include #include