From 2fb1f17c89a667b3748694946585d7186ae931f8 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Wed, 21 May 2025 17:43:09 -0500 Subject: [PATCH] InputCommon: Update to use SDL3 and bump the SDL submodule in Externals to release-3.2.14. --- CMakeLists.txt | 4 +- Externals/SDL/CMakeLists.txt | 42 +-- Externals/SDL/SDL | 2 +- Externals/SDL/{SDL2.vcxproj => SDL3.vcxproj} | 336 +++++++++++------- Externals/SDL/exports.props | 2 +- Flatpak/SDL2/SDL2.json | 21 -- Flatpak/org.DolphinEmu.dolphin-emu.yml | 4 - Source/Core/InputCommon/CMakeLists.txt | 4 +- .../ControllerInterface/ControllerInterface.h | 2 +- .../ControllerInterface/SDL/SDL.cpp | 71 ++-- .../ControllerInterface/SDL/SDLGamepad.cpp | 191 +++++----- .../ControllerInterface/SDL/SDLGamepad.h | 140 +++----- Source/Core/MacUpdater/CMakeLists.txt | 2 - Source/VSProps/Base.Dolphin.props | 2 +- Source/dolphin-emu.sln | 2 +- 15 files changed, 426 insertions(+), 399 deletions(-) rename Externals/SDL/{SDL2.vcxproj => SDL3.vcxproj} (62%) delete mode 100644 Flatpak/SDL2/SDL2.json diff --git a/CMakeLists.txt b/CMakeLists.txt index b67e31838d..6dbb1fefa7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,8 @@ else() endif() if(APPLE) + enable_language(OBJC) + enable_language(OBJCXX) option(MACOS_USE_DEFAULT_SEARCH_PATH "Don't prioritize system library paths" OFF) option(SKIP_POSTPROCESS_BUNDLE "Skip postprocessing bundle for redistributability" OFF) # Enable adhoc code signing by default (otherwise makefile builds on ARM will not work) @@ -602,7 +604,7 @@ if(UNIX) endif() if(ENABLE_SDL) - dolphin_find_optional_system_library(SDL2 Externals/SDL 2.30.9) + dolphin_find_optional_system_library(SDL3 Externals/SDL 3.2.0) endif() if(ENABLE_ANALYTICS) diff --git a/Externals/SDL/CMakeLists.txt b/Externals/SDL/CMakeLists.txt index 185f1ed930..627b96939a 100644 --- a/Externals/SDL/CMakeLists.txt +++ b/Externals/SDL/CMakeLists.txt @@ -1,34 +1,16 @@ -option(SDL2_DISABLE_SDL2MAIN "" ON) -option(SDL2_DISABLE_INSTALL "" ON) -option(SDL2_DISABLE_UNINSTALL "" ON) -option(SDL_SHARED "Build a shared version of the library" OFF) -option(SDL_SHARED_ENABLED_BY_DEFAULT "" OFF) -option(SDL_STATIC "Build a static version of the library" ON) -option(SDL_STATIC_ENABLED_BY_DEFAULT "" ON) -option(SDL_TEST "Build the SDL2_test library" OFF) -option(SDL_TEST_ENABLED_BY_DEFAULT "" OFF) - -# SDL fails to clean up old headers after version upgrades, so do that manually -set(EXPECTED_SDL_REVISION "SDL-release-2.30.9-0") -if (EXISTS "${CMAKE_CURRENT_BINARY_DIR}/SDL/include/SDL2/SDL_revision.h") - file(READ "${CMAKE_CURRENT_BINARY_DIR}/SDL/include/SDL2/SDL_revision.h" ACTUAL_SDL_REVISION) - if (NOT "${ACTUAL_SDL_REVISION}" MATCHES "${EXPECTED_SDL_REVISION}") - message(STATUS "Found unexpected SDL2/SDL_revision.h, removing generated includes.") - file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/SDL/include/") - endif() -endif() -if (EXISTS "${CMAKE_CURRENT_BINARY_DIR}/SDL/include/SDL_revision.h") - file(READ "${CMAKE_CURRENT_BINARY_DIR}/SDL/include/SDL_revision.h" ACTUAL_SDL_REVISION) - if (NOT "${ACTUAL_SDL_REVISION}" MATCHES "${EXPECTED_SDL_REVISION}") - message(STATUS "Found unexpected SDL_revision.h, removing generated includes.") - file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/SDL/include/") - endif() -endif() +set(SDL_SHARED OFF) +set(SDL_STATIC ON) +set(SDL_TEST_LIBRARY OFF) +set(SDL_TESTS OFF) +set(SDL_DISABLE_INSTALL ON) +set(SDL_DISABLE_INSTALL_DOCS ON) +set(SDL_INSTALL_TESTS OFF) add_subdirectory(SDL) -if (TARGET SDL2) - dolphin_disable_warnings(SDL2) + +if (TARGET SDL3) + dolphin_disable_warnings(SDL3) endif() -if (TARGET SDL2-static) - dolphin_disable_warnings(SDL2-static) +if (TARGET SDL3-static) + dolphin_disable_warnings(SDL3-static) endif() diff --git a/Externals/SDL/SDL b/Externals/SDL/SDL index c98c4fbff6..8d604353a5 160000 --- a/Externals/SDL/SDL +++ b/Externals/SDL/SDL @@ -1 +1 @@ -Subproject commit c98c4fbff6d8f3016a3ce6685bf8f43433c3efcc +Subproject commit 8d604353a53853fa56d1bdce0363535605ca868f diff --git a/Externals/SDL/SDL2.vcxproj b/Externals/SDL/SDL3.vcxproj similarity index 62% rename from Externals/SDL/SDL2.vcxproj rename to Externals/SDL/SDL3.vcxproj index 66aace96fc..c4cafbefc0 100644 --- a/Externals/SDL/SDL2.vcxproj +++ b/Externals/SDL/SDL3.vcxproj @@ -17,147 +17,165 @@ - SDL\include;%(AdditionalIncludeDirectories) + SDL\src;SDL\include;SDL\include\build_config;%(AdditionalIncludeDirectories) HAVE_LIBC=1;%(PreprocessorDefinitions) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + + + + + - + - + - + - + + + + + + - - - - - - - + + + - + + + - - - - + + + @@ -170,38 +188,54 @@ + - + + + + + + + + + + + + + + + + + + + + + - + + - - + + - - - - - - - + @@ -211,38 +245,45 @@ - - - - + + + + + + + + - + - + + - + + + @@ -257,8 +298,6 @@ - - @@ -268,37 +307,49 @@ + + - - + + - + + + NotUsing + NotUsing + NotUsing + NotUsing + + - + + + - + - + + @@ -309,23 +360,23 @@ + - - - + - + + @@ -333,18 +384,24 @@ - - - + + + + + + + + + @@ -354,15 +411,15 @@ + - - + + - @@ -371,23 +428,44 @@ + + + + + + + + + + + + + + + + + + + + + @@ -395,7 +473,6 @@ - @@ -403,21 +480,25 @@ - + + + + + @@ -426,7 +507,10 @@ + + + - \ No newline at end of file + diff --git a/Externals/SDL/exports.props b/Externals/SDL/exports.props index 573a8afba9..3f4da20b8c 100644 --- a/Externals/SDL/exports.props +++ b/Externals/SDL/exports.props @@ -6,7 +6,7 @@ - + {8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA} diff --git a/Flatpak/SDL2/SDL2.json b/Flatpak/SDL2/SDL2.json deleted file mode 100644 index 4c1821b742..0000000000 --- a/Flatpak/SDL2/SDL2.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "SDL2", - "buildsystem": "autotools", - "config-opts": ["--disable-static"], - "sources": [ - { - "type": "dir", - "path": "../../Externals/SDL/SDL" - } - ], - "cleanup": [ "/bin/sdl2-config", - "/include", - "/lib/libSDL2.la", - "/lib/libSDL2main.a", - "/lib/libSDL2main.la", - "/lib/libSDL2_test.a", - "/lib/libSDL2_test.la", - "/lib/cmake", - "/share/aclocal", - "/lib/pkgconfig"] -} diff --git a/Flatpak/org.DolphinEmu.dolphin-emu.yml b/Flatpak/org.DolphinEmu.dolphin-emu.yml index 4e2cbbe81d..1bbee5dd53 100644 --- a/Flatpak/org.DolphinEmu.dolphin-emu.yml +++ b/Flatpak/org.DolphinEmu.dolphin-emu.yml @@ -37,10 +37,6 @@ modules: project-id: 20540 stable-only: true url-template: https://www.freedesktop.org/software/libevdev/libevdev-$version.tar.xz - - # build the vendored SDL2 from Externals until the runtime gets 2.30.9 - - SDL2/SDL2.json - - name: dolphin-emu buildsystem: cmake-ninja builddir: true diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index d19a1ef3a2..0e5612f353 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -180,8 +180,8 @@ if(ENABLE_SDL) ControllerInterface/SDL/SDLGamepad.cpp ControllerInterface/SDL/SDLGamepad.h ) - target_link_libraries(inputcommon PRIVATE SDL2::SDL2) - target_compile_definitions(inputcommon PUBLIC HAVE_SDL2=1) + target_link_libraries(inputcommon PRIVATE SDL3::SDL3) + target_compile_definitions(inputcommon PUBLIC HAVE_SDL3=1) endif() if(MSVC) diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index d3998a4839..e5712f0a4f 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -31,7 +31,7 @@ #define CIFACE_USE_PIPES #endif #define CIFACE_USE_DUALSHOCKUDPCLIENT -#if defined(HAVE_SDL2) +#if defined(HAVE_SDL3) #define CIFACE_USE_SDL #endif #if defined(HAVE_HIDAPI) diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp index 07a38b0289..59b3b21bea 100644 --- a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp +++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp @@ -3,6 +3,7 @@ #include "InputCommon/ControllerInterface/SDL/SDL.h" +#include #include #include @@ -10,7 +11,7 @@ #include #endif -#include +#include #include "Common/Event.h" #include "Common/Logging/Log.h" @@ -31,7 +32,7 @@ public: void UpdateInput(std::vector>& devices_to_remove) override; private: - void OpenAndAddDevice(int index); + void OpenAndAddDevice(SDL_JoystickID instance_id); bool HandleEventAndContinue(const SDL_Event& e); @@ -48,10 +49,10 @@ std::unique_ptr CreateInputBackend(ControllerInterface* co static void EnableSDLLogging() { - SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); - SDL_LogSetOutputFunction( + SDL_SetLogPriorities(SDL_LOG_PRIORITY_VERBOSE); + SDL_SetLogOutputFunction( [](void*, int category, SDL_LogPriority priority, const char* message) { - std::string category_name; + std::string_view category_name{}; switch (category) { case SDL_LOG_CATEGORY_APPLICATION: @@ -81,8 +82,10 @@ static void EnableSDLLogging() case SDL_LOG_CATEGORY_TEST: category_name = "test"; break; + case SDL_LOG_CATEGORY_GPU: + category_name = "gpu"; + break; default: - category_name = fmt::format("unknown({})", category); break; } @@ -108,8 +111,16 @@ static void EnableSDLLogging() break; } - GENERIC_LOG_FMT(Common::Log::LogType::CONTROLLERINTERFACE, log_level, "{}: {}", - category_name, message); + if (category_name.empty()) + { + GENERIC_LOG_FMT(Common::Log::LogType::CONTROLLERINTERFACE, log_level, "unknown({}): {}", + category, message); + } + else + { + GENERIC_LOG_FMT(Common::Log::LogType::CONTROLLERINTERFACE, log_level, "{}: {}", + category_name, message); + } }, nullptr); } @@ -122,9 +133,8 @@ InputBackend::InputBackend(ControllerInterface* controller_interface) // This is required on windows so that SDL's joystick code properly pumps window messages SDL_SetHint(SDL_HINT_JOYSTICK_THREAD, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); - // We want buttons to come in as positions, not labels - SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0"); + SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1"); + // We have our own WGI backend. Enabling SDL's WGI handling creates even more redundant devices. SDL_SetHint(SDL_HINT_JOYSTICK_WGI, "0"); @@ -139,7 +149,7 @@ InputBackend::InputBackend(ControllerInterface* controller_interface) { Common::ScopeGuard init_guard([this] { m_init_event.Set(); }); - if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER) != 0) + if (!SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMEPAD)) { ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL failed to initialize"); return; @@ -160,7 +170,7 @@ InputBackend::InputBackend(ControllerInterface* controller_interface) // duplicate devices. Adding devices will actually "fail" here, as the ControllerInterface // hasn't finished initializing yet. SDL_Event e; - while (SDL_PollEvent(&e) != 0) + while (SDL_PollEvent(&e)) { if (!HandleEventAndContinue(e)) return; @@ -179,7 +189,7 @@ InputBackend::InputBackend(ControllerInterface* controller_interface) #endif SDL_Event e; - while (SDL_WaitEvent(&e) != 0) + while (SDL_WaitEvent(&e)) { if (!HandleEventAndContinue(e)) return; @@ -221,20 +231,20 @@ void InputBackend::PopulateDevices() SDL_PushEvent(&populate_event); } -void InputBackend::UpdateInput(std::vector>& devices_to_remove) +void InputBackend::UpdateInput(std::vector>&) { - SDL_GameControllerUpdate(); + SDL_UpdateGamepads(); } -void InputBackend::OpenAndAddDevice(int index) +void InputBackend::OpenAndAddDevice(SDL_JoystickID instance_id) { - SDL_GameController* gc = SDL_GameControllerOpen(index); - SDL_Joystick* js = SDL_JoystickOpen(index); + SDL_Gamepad* gc = SDL_OpenGamepad(instance_id); + SDL_Joystick* js = SDL_OpenJoystick(instance_id); - if (js) + if (js != nullptr) { - if (SDL_JoystickNumButtons(js) > 255 || SDL_JoystickNumAxes(js) > 255 || - SDL_JoystickNumHats(js) > 255 || SDL_JoystickNumBalls(js) > 255) + if (SDL_GetNumJoystickButtons(js) > 255 || SDL_GetNumJoystickAxes(js) > 255 || + SDL_GetNumJoystickHats(js) > 255 || SDL_GetNumJoystickBalls(js) > 255) { // This device is invalid, don't use it // Some crazy devices (HP webcam 2100) end up as HID devices @@ -249,17 +259,12 @@ void InputBackend::OpenAndAddDevice(int index) bool InputBackend::HandleEventAndContinue(const SDL_Event& e) { - if (e.type == SDL_JOYDEVICEADDED) + if (e.type == SDL_EVENT_JOYSTICK_ADDED) { - // NOTE: SDL_JOYDEVICEADDED's `jdevice.which` is a device index in SDL2. - // It will change to an "instance ID" in SDL3. - // OpenAndAddDevice impl and calls will need refactoring when changing to SDL3. - static_assert(!SDL_VERSION_ATLEAST(3, 0, 0), "Refactoring is needed for SDL3."); OpenAndAddDevice(e.jdevice.which); } - else if (e.type == SDL_JOYDEVICEREMOVED) + else if (e.type == SDL_EVENT_JOYSTICK_REMOVED) { - // NOTE: SDL_JOYDEVICEREMOVED's `jdevice.which` is an "instance ID". GetControllerInterface().RemoveDevice([&e](const auto* device) { return device->GetSource() == "SDL" && static_cast(device)->GetSDLInstanceID() == e.jdevice.which; @@ -268,8 +273,12 @@ bool InputBackend::HandleEventAndContinue(const SDL_Event& e) else if (e.type == m_populate_event_type) { GetControllerInterface().PlatformPopulateDevices([this] { - for (int i = 0; i < SDL_NumJoysticks(); ++i) - OpenAndAddDevice(i); + int joystick_count = 0; + auto* const joystick_ids = SDL_GetJoysticks(&joystick_count); + for (auto instance_id : std::span(joystick_ids, joystick_count)) + OpenAndAddDevice(instance_id); + + SDL_free(joystick_ids); }); } else if (e.type == m_stop_event_type) diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDLGamepad.cpp b/Source/Core/InputCommon/ControllerInterface/SDL/SDLGamepad.cpp index 78df8f7ed7..345bef00a3 100644 --- a/Source/Core/InputCommon/ControllerInterface/SDL/SDLGamepad.cpp +++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDLGamepad.cpp @@ -4,9 +4,11 @@ #include "InputCommon/ControllerInterface/SDL/SDLGamepad.h" #include +#include #include #include "Common/Logging/Log.h" +#include "Common/ScopeGuard.h" namespace ciface::SDL { @@ -17,16 +19,12 @@ bool IsTriggerAxis(int index) return index >= 4; } -GameController::GameController(SDL_GameController* const gamecontroller, - SDL_Joystick* const joystick) +GameController::GameController(SDL_Gamepad* const gamecontroller, SDL_Joystick* const joystick) : m_gamecontroller(gamecontroller), m_joystick(joystick) { - const char* name; - if (gamecontroller) - name = SDL_GameControllerName(gamecontroller); - else - name = SDL_JoystickName(joystick); - m_name = name != nullptr ? name : "Unknown"; + const char* const sdl_name = (gamecontroller != nullptr) ? SDL_GetGamepadName(gamecontroller) : + SDL_GetJoystickName(joystick); + m_name = (sdl_name != nullptr) ? sdl_name : "Unknown"; // If a Joystick input has a GameController equivalent button/hat we don't add it. // "Equivalent" axes are still added as hidden/undetectable inputs to handle @@ -35,17 +33,17 @@ GameController::GameController(SDL_GameController* const gamecontroller, std::unordered_set registered_buttons; std::unordered_set registered_hats; std::unordered_set registered_axes; - const auto register_mapping = [&](const SDL_GameControllerButtonBind& bind) { - switch (bind.bindType) + const auto register_mapping = [&](const SDL_GamepadBinding& bind) { + switch (bind.input_type) { - case SDL_CONTROLLER_BINDTYPE_BUTTON: - registered_buttons.insert(bind.value.button); + case SDL_GAMEPAD_BINDTYPE_BUTTON: + registered_buttons.insert(bind.input.button); break; - case SDL_CONTROLLER_BINDTYPE_HAT: - registered_hats.insert(bind.value.hat.hat); + case SDL_GAMEPAD_BINDTYPE_HAT: + registered_hats.insert(bind.input.hat.hat); break; - case SDL_CONTROLLER_BINDTYPE_AXIS: - registered_axes.insert(bind.value.axis); + case SDL_GAMEPAD_BINDTYPE_AXIS: + registered_axes.insert(bind.input.axis.axis); break; default: break; @@ -55,25 +53,22 @@ GameController::GameController(SDL_GameController* const gamecontroller, if (gamecontroller != nullptr) { // Inputs + int binding_count = 0; + auto** bindings = SDL_GetGamepadBindings(gamecontroller, &binding_count); + Common::ScopeGuard free_bindings([&] { SDL_free(bindings); }); - // Buttons - for (u8 i = 0; i != size(s_sdl_button_names); ++i) + for (auto* const binding : std::span(bindings, binding_count)) { - SDL_GameControllerButton button = static_cast(i); - if (SDL_GameControllerHasButton(m_gamecontroller, button)) - { - AddInput(new Button(gamecontroller, button)); + register_mapping(*binding); - register_mapping(SDL_GameControllerGetBindForButton(gamecontroller, button)); - } - } - - // Axes - for (u8 i = 0; i != size(s_sdl_axis_names); ++i) - { - SDL_GameControllerAxis axis = static_cast(i); - if (SDL_GameControllerHasAxis(m_gamecontroller, axis)) + switch (binding->output_type) { + case SDL_GAMEPAD_BINDTYPE_BUTTON: + AddInput(new Button(gamecontroller, *binding)); + break; + case SDL_GAMEPAD_BINDTYPE_AXIS: + { + const auto axis = binding->output.axis.axis; if (IsTriggerAxis(axis)) { AddInput(new Axis(m_gamecontroller, 32767, axis)); @@ -84,19 +79,23 @@ GameController::GameController(SDL_GameController* const gamecontroller, AddInput(new Axis(m_gamecontroller, -32768, axis)); AddInput(new Axis(m_gamecontroller, 32767, axis)); } - - register_mapping(SDL_GameControllerGetBindForAxis(gamecontroller, axis)); + break; + } + default: + break; } } + const auto properties = SDL_GetGamepadProperties(m_gamecontroller); + // Rumble - if (SDL_GameControllerHasRumble(m_gamecontroller)) + if (SDL_GetBooleanProperty(properties, SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN, false)) { AddOutput(new CombinedMotor(*this, &m_low_freq_rumble, &m_high_freq_rumble)); AddOutput(new Rumble("Motor L", *this, &m_low_freq_rumble, &GameController::UpdateRumble)); AddOutput(new Rumble("Motor R", *this, &m_high_freq_rumble, &GameController::UpdateRumble)); } - if (SDL_GameControllerHasRumbleTriggers(m_gamecontroller)) + if (SDL_GetBooleanProperty(properties, SDL_PROP_GAMEPAD_CAP_TRIGGER_RUMBLE_BOOLEAN, false)) { AddOutput(new Rumble("Trigger L", *this, &m_trigger_l_rumble, &GameController::UpdateRumbleTriggers)); @@ -105,7 +104,7 @@ GameController::GameController(SDL_GameController* const gamecontroller, } // Touchpad - if (SDL_GameControllerGetNumTouchpads(m_gamecontroller) > 0) + if (SDL_GetNumGamepadTouchpads(m_gamecontroller) > 0) { const char* const name_x = "Touchpad X"; AddInput(new NonDetectableDirectionalInput<-1>(name_x, &m_touchpad_x)); @@ -119,7 +118,7 @@ GameController::GameController(SDL_GameController* const gamecontroller, // Motion const auto add_sensor = [this](SDL_SensorType type, std::string_view sensor_name, const SDLMotionAxisList& axes) { - if (SDL_GameControllerSetSensorEnabled(m_gamecontroller, type, SDL_TRUE) == 0) + if (SDL_SetGamepadSensorEnabled(m_gamecontroller, type, true)) { for (const SDLMotionAxis& axis : axes) { @@ -140,7 +139,7 @@ GameController::GameController(SDL_GameController* const gamecontroller, // Legacy inputs // Buttons - int n_legacy_buttons = SDL_JoystickNumButtons(joystick); + int n_legacy_buttons = SDL_GetNumJoystickButtons(joystick); if (n_legacy_buttons < 0) { ERROR_LOG_FMT(CONTROLLERINTERFACE, "Error in SDL_JoystickNumButtons(): {}", SDL_GetError()); @@ -155,7 +154,7 @@ GameController::GameController(SDL_GameController* const gamecontroller, } // Axes - int n_legacy_axes = SDL_JoystickNumAxes(joystick); + int n_legacy_axes = SDL_GetNumJoystickAxes(joystick); if (n_legacy_axes < 0) { ERROR_LOG_FMT(CONTROLLERINTERFACE, "Error in SDL_JoystickNumAxes(): {}", SDL_GetError()); @@ -171,7 +170,7 @@ GameController::GameController(SDL_GameController* const gamecontroller, } // Hats - int n_legacy_hats = SDL_JoystickNumHats(joystick); + int n_legacy_hats = SDL_GetNumJoystickHats(joystick); if (n_legacy_hats < 0) { ERROR_LOG_FMT(CONTROLLERINTERFACE, "Error in SDL_JoystickNumHats(): {}", SDL_GetError()); @@ -188,16 +187,16 @@ GameController::GameController(SDL_GameController* const gamecontroller, } // Haptics - if (SDL_JoystickIsHaptic(m_joystick)) + if (SDL_IsJoystickHaptic(m_joystick)) { - m_haptic = SDL_HapticOpenFromJoystick(m_joystick); + m_haptic = SDL_OpenHapticFromJoystick(m_joystick); if (m_haptic) { - const unsigned int supported_effects = SDL_HapticQuery(m_haptic); + const unsigned int supported_effects = SDL_GetMaxHapticEffects(m_haptic); // Disable autocenter: if (supported_effects & SDL_HAPTIC_AUTOCENTER) - SDL_HapticSetAutocenter(m_haptic, 0); + SDL_SetHapticAutocenter(m_haptic, 0); // Constant if (supported_effects & SDL_HAPTIC_CONSTANT) @@ -224,16 +223,19 @@ GameController::GameController(SDL_GameController* const gamecontroller, } } - // Needed to make the below power level not "UNKNOWN". - SDL_JoystickUpdate(); - // Battery - if (SDL_JoystickPowerLevel const power_level = SDL_JoystickCurrentPowerLevel(m_joystick); - power_level != SDL_JOYSTICK_POWER_UNKNOWN) - { - m_battery_value = GetBatteryValueFromSDLPowerLevel(power_level); + if (UpdateBatteryLevel()) AddInput(new BatteryInput{&m_battery_value}); - } +} + +bool GameController::UpdateBatteryLevel() +{ + int battery_percent = 0; + if (SDL_GetJoystickPowerInfo(m_joystick, &battery_percent) == SDL_POWERSTATE_ERROR) + return false; + + m_battery_value = std::max(0, battery_percent); + return true; } GameController::~GameController() @@ -241,20 +243,18 @@ GameController::~GameController() if (m_haptic) { // stop/destroy all effects - SDL_HapticStopAll(m_haptic); + SDL_StopHapticEffects(m_haptic); // close haptic before joystick - SDL_HapticClose(m_haptic); + SDL_CloseHaptic(m_haptic); m_haptic = nullptr; } if (m_gamecontroller) { // stop all rumble - SDL_GameControllerRumble(m_gamecontroller, 0, 0, 0); - // close game controller - SDL_GameControllerClose(m_gamecontroller); + SDL_RumbleGamepad(m_gamecontroller, 0, 0, 0); + SDL_CloseGamepad(m_gamecontroller); } - // close joystick - SDL_JoystickClose(m_joystick); + SDL_CloseJoystick(m_joystick); } std::string GameController::GetName() const @@ -267,20 +267,28 @@ std::string GameController::GetSource() const return "SDL"; } -int GameController::GetSDLInstanceID() const +SDL_JoystickID GameController::GetSDLInstanceID() const { - return SDL_JoystickInstanceID(m_joystick); + return SDL_GetJoystickID(m_joystick); } std::string GameController::Button::GetName() const { - return s_sdl_button_names[m_button]; + const auto button = m_binding.output.button; + + if (std::size_t(button) >= std::size(s_sdl_button_names)) + return GetLegacyButtonName(button); + + return s_sdl_button_names[button]; } std::string GameController::Axis::GetName() const { + if (std::size_t(m_axis) >= std::size(s_sdl_axis_names)) + return GetLegacyAxisName(m_axis, m_range); + if (IsTriggerAxis(m_axis)) - return std::string(s_sdl_axis_names[m_axis]); + return s_sdl_axis_names[m_axis]; bool negative = m_range < 0; @@ -293,12 +301,12 @@ std::string GameController::Axis::GetName() const ControlState GameController::Button::GetState() const { - return SDL_GameControllerGetButton(m_gc, m_button); + return SDL_GetGamepadButton(m_gc, m_binding.output.button); } ControlState GameController::Axis::GetState() const { - return ControlState(SDL_GameControllerGetAxis(m_gc, m_axis)) / m_range; + return ControlState(SDL_GetGamepadAxis(m_gc, m_axis)) / m_range; } bool GameController::Button::IsMatchingName(std::string_view name) const @@ -306,25 +314,26 @@ bool GameController::Button::IsMatchingName(std::string_view name) const if (GetName() == name) return true; - // So that SDL can be a superset of XInput - if (name == "Button A") - return GetName() == "Button S"; - if (name == "Button B") - return GetName() == "Button E"; - if (name == "Button X") - return GetName() == "Button W"; - if (name == "Button Y") - return GetName() == "Button N"; + // Positionally match XInput button names. + // e.g. Switch Pro controller A-button matches "Button B". + // e.g. PlayStation controller Circle-button matches "Button B". + if (m_binding.output.button == SDL_GAMEPAD_BUTTON_SOUTH && name == "Button A") + return true; + if (m_binding.output.button == SDL_GAMEPAD_BUTTON_EAST && name == "Button B") + return true; + if (m_binding.output.button == SDL_GAMEPAD_BUTTON_WEST && name == "Button X") + return true; + if (m_binding.output.button == SDL_GAMEPAD_BUTTON_NORTH && name == "Button Y") + return true; - // Match legacy names. - const auto bind = SDL_GameControllerGetBindForButton(m_gc, m_button); - switch (bind.bindType) + // Match the old "Button 0"-like names. + switch (m_binding.input_type) { - case SDL_CONTROLLER_BINDTYPE_BUTTON: - return name == GetLegacyButtonName(bind.value.button); - case SDL_CONTROLLER_BINDTYPE_HAT: - return name == GetLegacyHatName(bind.value.hat.hat, - GetDirectionFromHatMask(u8(bind.value.hat.hat_mask))); + case SDL_GAMEPAD_BINDTYPE_BUTTON: + return name == GetLegacyButtonName(m_binding.input.button); + case SDL_GAMEPAD_BINDTYPE_HAT: + return name == GetLegacyHatName(m_binding.input.hat.hat, + GetDirectionFromHatMask(m_binding.input.hat.hat_mask)); default: return false; } @@ -333,24 +342,24 @@ bool GameController::Button::IsMatchingName(std::string_view name) const ControlState GameController::MotionInput::GetState() const { std::array data{}; - SDL_GameControllerGetSensorData(m_gc, m_type, data.data(), (int)data.size()); + SDL_GetGamepadSensorData(m_gc, m_type, data.data(), (int)data.size()); return m_scale * data[m_index]; } // Legacy input ControlState GameController::LegacyButton::GetState() const { - return SDL_JoystickGetButton(m_js, m_index); + return SDL_GetJoystickButton(m_js, m_index); } ControlState GameController::LegacyAxis::GetState() const { - return ControlState(SDL_JoystickGetAxis(m_js, m_index)) / m_range; + return ControlState(SDL_GetJoystickAxis(m_js, m_index)) / m_range; } ControlState GameController::LegacyHat::GetState() const { - return (SDL_JoystickGetHat(m_js, m_index) & (1 << m_direction)) > 0; + return (SDL_GetJoystickHat(m_js, m_index) & (1 << m_direction)) > 0; } void GameController::HapticEffect::UpdateEffect() @@ -360,22 +369,22 @@ void GameController::HapticEffect::UpdateEffect() if (m_id < 0) { // Upload and try to play the effect. - m_id = SDL_HapticNewEffect(m_haptic, &m_effect); + m_id = SDL_CreateHapticEffect(m_haptic, &m_effect); if (m_id >= 0) - SDL_HapticRunEffect(m_haptic, m_id, 1); + SDL_RunHapticEffect(m_haptic, m_id, 1); } else { // Effect is already playing. Update parameters. - SDL_HapticUpdateEffect(m_haptic, m_id, &m_effect); + SDL_UpdateHapticEffect(m_haptic, m_id, &m_effect); } } else if (m_id >= 0) { // Stop and remove the effect. - SDL_HapticStopEffect(m_haptic, m_id); - SDL_HapticDestroyEffect(m_haptic, m_id); + SDL_StopHapticEffect(m_haptic, m_id); + SDL_DestroyHapticEffect(m_haptic, m_id); m_id = -1; } } diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDLGamepad.h b/Source/Core/InputCommon/ControllerInterface/SDL/SDLGamepad.h index 4f03ea7d4a..e7e463d01d 100644 --- a/Source/Core/InputCommon/ControllerInterface/SDL/SDLGamepad.h +++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDLGamepad.h @@ -5,8 +5,8 @@ #include -#include -#include +#include +#include #include "Common/MathUtil.h" @@ -29,7 +29,7 @@ std::string GetLegacyHatName(int index, int direction) return "Hat " + std::to_string(index) + ' ' + "NESW"[direction]; } -constexpr int GetDirectionFromHatMask(u8 mask) +constexpr int GetDirectionFromHatMask(int mask) { return MathUtil::IntLog2(mask); } @@ -37,38 +37,6 @@ constexpr int GetDirectionFromHatMask(u8 mask) static_assert(GetDirectionFromHatMask(SDL_HAT_UP) == 0); static_assert(GetDirectionFromHatMask(SDL_HAT_LEFT) == 3); -ControlState GetBatteryValueFromSDLPowerLevel(SDL_JoystickPowerLevel sdl_power_level) -{ - // Values come from comments in SDL_joystick.h - // A proper percentage will be exposed in SDL3. - ControlState result; - switch (sdl_power_level) - { - case SDL_JOYSTICK_POWER_EMPTY: - result = 0.025; - break; - case SDL_JOYSTICK_POWER_LOW: - result = 0.125; - break; - case SDL_JOYSTICK_POWER_MEDIUM: - result = 0.45; - break; - case SDL_JOYSTICK_POWER_FULL: - result = 0.85; - break; - case SDL_JOYSTICK_POWER_WIRED: - case SDL_JOYSTICK_POWER_MAX: - result = 1.0; - break; - case SDL_JOYSTICK_POWER_UNKNOWN: - default: - result = 0.0; - break; - } - - return result * ciface::BATTERY_INPUT_MAX_VALUE; -} - } // namespace namespace ciface::SDL @@ -82,29 +50,29 @@ private: { public: std::string GetName() const override; - Button(SDL_GameController* gc, SDL_GameControllerButton button) : m_gc(gc), m_button(button) {} + Button(SDL_Gamepad* gc, const SDL_GamepadBinding& binding) : m_gc(gc), m_binding(binding) {} ControlState GetState() const override; bool IsMatchingName(std::string_view name) const override; private: - SDL_GameController* const m_gc; - const SDL_GameControllerButton m_button; + SDL_Gamepad* const m_gc; + const SDL_GamepadBinding m_binding; }; class Axis : public Core::Device::Input { public: std::string GetName() const override; - Axis(SDL_GameController* gc, Sint16 range, SDL_GameControllerAxis axis) + Axis(SDL_Gamepad* gc, Sint16 range, SDL_GamepadAxis axis) : m_gc(gc), m_range(range), m_axis(axis) { } ControlState GetState() const override; private: - SDL_GameController* const m_gc; + SDL_Gamepad* const m_gc; const Sint16 m_range; - const SDL_GameControllerAxis m_axis; + const SDL_GamepadAxis m_axis; }; // Legacy inputs @@ -180,7 +148,7 @@ private: std::string GetName() const override { return m_name; } void SetState(ControlState state) override { - const auto new_state = state * std::numeric_limits::max(); + const auto new_state = std::lround(state * std::numeric_limits::max()); if (m_state == new_state) return; @@ -205,7 +173,7 @@ private: std::string GetName() const override { return "Motor"; } void SetState(ControlState state) override { - const auto new_state = state * std::numeric_limits::max(); + const auto new_state = std::lround(state * std::numeric_limits::max()); if (m_low_state == new_state && m_high_state == new_state) return; @@ -327,7 +295,7 @@ private: class MotionInput : public Input { public: - MotionInput(std::string name, SDL_GameController* gc, SDL_SensorType type, int index, + MotionInput(std::string name, SDL_Gamepad* gc, SDL_SensorType type, int index, ControlState scale) : m_name(std::move(name)), m_gc(gc), m_type(type), m_index(index), m_scale(scale) { @@ -340,7 +308,7 @@ private: private: std::string m_name; - SDL_GameController* const m_gc; + SDL_Gamepad* const m_gc; SDL_SensorType const m_type; int const m_index; @@ -348,26 +316,25 @@ private: }; public: - GameController(SDL_GameController* const gamecontroller, SDL_Joystick* const joystick); - ~GameController(); + GameController(SDL_Gamepad* gamecontroller, SDL_Joystick* joystick); + ~GameController() override; std::string GetName() const override; std::string GetSource() const override; - int GetSDLInstanceID() const; + SDL_JoystickID GetSDLInstanceID() const; Core::DeviceRemoval UpdateInput() override { - m_battery_value = GetBatteryValueFromSDLPowerLevel(SDL_JoystickCurrentPowerLevel(m_joystick)); + UpdateBatteryLevel(); // We only support one touchpad and one finger. const int touchpad_index = 0; const int finger_index = 0; - if (SDL_GameControllerGetNumTouchpads(m_gamecontroller) > touchpad_index && - SDL_GameControllerGetNumTouchpadFingers(m_gamecontroller, touchpad_index) > finger_index) + if (SDL_GetNumGamepadTouchpads(m_gamecontroller) > touchpad_index && + SDL_GetNumGamepadTouchpadFingers(m_gamecontroller, touchpad_index) > finger_index) { - Uint8 state = 0; - SDL_GameControllerGetTouchpadFinger(m_gamecontroller, touchpad_index, finger_index, &state, - &m_touchpad_x, &m_touchpad_y, &m_touchpad_pressure); + SDL_GetGamepadTouchpadFinger(m_gamecontroller, touchpad_index, finger_index, nullptr, + &m_touchpad_x, &m_touchpad_y, &m_touchpad_pressure); m_touchpad_x = m_touchpad_x * 2 - 1; m_touchpad_y = m_touchpad_y * 2 - 1; } @@ -378,23 +345,24 @@ public: private: void UpdateRumble() { - SDL_GameControllerRumble(m_gamecontroller, m_low_freq_rumble, m_high_freq_rumble, - RUMBLE_LENGTH_MS); + SDL_RumbleGamepad(m_gamecontroller, m_low_freq_rumble, m_high_freq_rumble, RUMBLE_LENGTH_MS); } void UpdateRumbleTriggers() { - SDL_GameControllerRumbleTriggers(m_gamecontroller, m_trigger_l_rumble, m_trigger_r_rumble, - RUMBLE_LENGTH_MS); + SDL_RumbleGamepadTriggers(m_gamecontroller, m_trigger_l_rumble, m_trigger_r_rumble, + RUMBLE_LENGTH_MS); } + bool UpdateBatteryLevel(); + Uint16 m_low_freq_rumble = 0; Uint16 m_high_freq_rumble = 0; Uint16 m_trigger_l_rumble = 0; Uint16 m_trigger_r_rumble = 0; - SDL_GameController* const m_gamecontroller; + SDL_Gamepad* const m_gamecontroller; std::string m_name; SDL_Joystick* const m_joystick; SDL_Haptic* m_haptic = nullptr; @@ -413,35 +381,35 @@ struct SDLMotionAxis using SDLMotionAxisList = std::array; static constexpr std::array s_sdl_button_names = { - "Button S", // SDL_CONTROLLER_BUTTON_A - "Button E", // SDL_CONTROLLER_BUTTON_B - "Button W", // SDL_CONTROLLER_BUTTON_X - "Button N", // SDL_CONTROLLER_BUTTON_Y - "Back", // SDL_CONTROLLER_BUTTON_BACK - "Guide", // SDL_CONTROLLER_BUTTON_GUIDE - "Start", // SDL_CONTROLLER_BUTTON_START - "Thumb L", // SDL_CONTROLLER_BUTTON_LEFTSTICK - "Thumb R", // SDL_CONTROLLER_BUTTON_RIGHTSTICK - "Shoulder L", // SDL_CONTROLLER_BUTTON_LEFTSHOULDER - "Shoulder R", // SDL_CONTROLLER_BUTTON_RIGHTSHOULDER - "Pad N", // SDL_CONTROLLER_BUTTON_DPAD_UP - "Pad S", // SDL_CONTROLLER_BUTTON_DPAD_DOWN - "Pad W", // SDL_CONTROLLER_BUTTON_DPAD_LEFT - "Pad E", // SDL_CONTROLLER_BUTTON_DPAD_RIGHT - "Misc 1", // SDL_CONTROLLER_BUTTON_MISC1 - "Paddle 1", // SDL_CONTROLLER_BUTTON_PADDLE1 - "Paddle 2", // SDL_CONTROLLER_BUTTON_PADDLE2 - "Paddle 3", // SDL_CONTROLLER_BUTTON_PADDLE3 - "Paddle 4", // SDL_CONTROLLER_BUTTON_PADDLE4 - "Touchpad", // SDL_CONTROLLER_BUTTON_TOUCHPAD + "Button S", // SDL_GAMEPAD_BUTTON_SOUTH + "Button E", // SDL_GAMEPAD_BUTTON_EAST + "Button W", // SDL_GAMEPAD_BUTTON_WEST + "Button N", // SDL_GAMEPAD_BUTTON_NORTH + "Back", // SDL_GAMEPAD_BUTTON_BACK + "Guide", // SDL_GAMEPAD_BUTTON_GUIDE + "Start", // SDL_GAMEPAD_BUTTON_START + "Thumb L", // SDL_GAMEPAD_BUTTON_LEFT_STICK + "Thumb R", // SDL_GAMEPAD_BUTTON_RIGHT_STICK + "Shoulder L", // SDL_GAMEPAD_BUTTON_LEFT_SHOULDER + "Shoulder R", // SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER + "Pad N", // SDL_GAMEPAD_BUTTON_DPAD_UP + "Pad S", // SDL_GAMEPAD_BUTTON_DPAD_DOWN + "Pad W", // SDL_GAMEPAD_BUTTON_DPAD_LEFT + "Pad E", // SDL_GAMEPAD_BUTTON_DPAD_RIGHT + "Misc 1", // SDL_GAMEPAD_BUTTON_MISC1 + "Paddle 1", // SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1 + "Paddle 2", // SDL_GAMEPAD_BUTTON_LEFT_PADDLE1 + "Paddle 3", // SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2 + "Paddle 4", // SDL_GAMEPAD_BUTTON_LEFT_PADDLE2 + "Touchpad", // SDL_GAMEPAD_BUTTON_TOUCHPAD }; static constexpr std::array s_sdl_axis_names = { - "Left X", // SDL_CONTROLLER_AXIS_LEFTX - "Left Y", // SDL_CONTROLLER_AXIS_LEFTY - "Right X", // SDL_CONTROLLER_AXIS_RIGHTX - "Right Y", // SDL_CONTROLLER_AXIS_RIGHTY - "Trigger L", // SDL_CONTROLLER_AXIS_TRIGGERLEFT - "Trigger R", // SDL_CONTROLLER_AXIS_TRIGGERRIGHT + "Left X", // SDL_GAMEPAD_AXIS_LEFTX + "Left Y", // SDL_GAMEPAD_AXIS_LEFTY + "Right X", // SDL_GAMEPAD_AXIS_RIGHTX + "Right Y", // SDL_GAMEPAD_AXIS_RIGHTY + "Trigger L", // SDL_GAMEPAD_AXIS_LEFT_TRIGGER + "Trigger R", // SDL_GAMEPAD_AXIS_RIGHT_TRIGGER }; static constexpr SDLMotionAxisList SDL_AXES_ACCELEROMETER = {{ {"Up", 1, 1}, diff --git a/Source/Core/MacUpdater/CMakeLists.txt b/Source/Core/MacUpdater/CMakeLists.txt index 79a9bfe52f..da6d052fa5 100644 --- a/Source/Core/MacUpdater/CMakeLists.txt +++ b/Source/Core/MacUpdater/CMakeLists.txt @@ -19,8 +19,6 @@ set_target_properties(MacUpdater PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in" OUTPUT_NAME "Dolphin Updater") -target_compile_options(MacUpdater PRIVATE -x objective-c++) - # Copy icon into the bundle target_sources(MacUpdater PRIVATE "${CMAKE_SOURCE_DIR}/Data/Dolphin.icns") set_source_files_properties("${CMAKE_SOURCE_DIR}/Data/Dolphin.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources) diff --git a/Source/VSProps/Base.Dolphin.props b/Source/VSProps/Base.Dolphin.props index f2f213b009..d27f95b084 100644 --- a/Source/VSProps/Base.Dolphin.props +++ b/Source/VSProps/Base.Dolphin.props @@ -44,7 +44,7 @@ HAS_VULKAN;%(PreprocessorDefinitions) HAS_LIBMGBA;%(PreprocessorDefinitions) AUTOUPDATE;%(PreprocessorDefinitions) - HAVE_SDL2;%(PreprocessorDefinitions) + HAVE_SDL3;%(PreprocessorDefinitions) USE_RETRO_ACHIEVEMENTS;%(PreprocessorDefinitions) RC_CLIENT_SUPPORTS_HASH;%(PreprocessorDefinitions) RC_CLIENT_SUPPORTS_RAINTEGRATION;%(PreprocessorDefinitions) diff --git a/Source/dolphin-emu.sln b/Source/dolphin-emu.sln index e11ef45992..f1183c287f 100644 --- a/Source/dolphin-emu.sln +++ b/Source/dolphin-emu.sln @@ -81,7 +81,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmt", "..\Externals\fmt\fmt EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spirv_cross", "..\Externals\spirv_cross\spirv_cross.vcxproj", "{3D780617-EC8C-4721-B9FD-DFC9BB658C7C}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDL2", "..\Externals\SDL\SDL2.vcxproj", "{8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDL3", "..\Externals\SDL\SDL3.vcxproj", "{8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FatFs", "..\Externals\FatFs\FatFs.vcxproj", "{3F17D282-A77D-4931-B844-903AD0809A5E}" EndProject