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..c9a6709bd2 160000
--- a/Externals/SDL/SDL
+++ b/Externals/SDL/SDL
@@ -1 +1 @@
-Subproject commit c98c4fbff6d8f3016a3ce6685bf8f43433c3efcc
+Subproject commit c9a6709bd21750f1ad9597be21abace78c6378c9
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