diff --git a/scripts/appimage/make-appimage.sh b/scripts/appimage/make-appimage.sh index 610416a95..3585536a1 100755 --- a/scripts/appimage/make-appimage.sh +++ b/scripts/appimage/make-appimage.sh @@ -48,6 +48,7 @@ declare -a MANUAL_LIBS=( "libshaderc_shared.so" "libspirv-cross-c-shared.so.0" "libsqlite3.so.3" + "libSDL3.so.0" "libavcodec.so.63" "libavformat.so.63" diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 595205f65..12feb5c7a 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -31,6 +31,7 @@ add_library(util dyn_shaderc.h dyn_spirv_cross.h dyn_libpng.h + dyn_sdl.h dyn_sqlite.h elf_file.cpp elf_file.h @@ -218,8 +219,9 @@ if(NOT ANDROID) ) target_link_libraries(util PUBLIC cubeb - SDL3::SDL3 + SDL3::Headers ) + # FFmpeg loaded dynamically on demand. target_include_directories(util PUBLIC ${FFMPEG_INCLUDE_DIRS}) endif() @@ -280,7 +282,7 @@ function(add_util_resources target) endif() # Copy dynamically-loaded libraries into the bundle. - add_runtime_libraries(${target} spirv-cross-c-shared Shaderc::shaderc_shared SQLite3::sqlite3-shared + add_runtime_libraries(${target} spirv-cross-c-shared Shaderc::shaderc_shared SQLite3::sqlite3-shared SDL3::SDL3 libjpeg-turbo::jpeg PNG::png_shared WebP::webp WebP::sharpyuv WebP::webpdecoder WebP::webpdemux WebP::libwebpmux) if(APPLE) diff --git a/src/util/dyn_sdl.h b/src/util/dyn_sdl.h new file mode 100644 index 000000000..7183f33ac --- /dev/null +++ b/src/util/dyn_sdl.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin +// SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +#pragma once + +#include + +class Error; + +#define DYN_SDL_FUNCTIONS(X) \ + X(SDL_GetError) \ + X(SDL_SetLogOutputFunction) \ + X(SDL_SetLogPriorities) \ + X(SDL_SetHint) \ + X(SDL_InitSubSystem) \ + X(SDL_QuitSubSystem) \ + X(SDL_PollEvent) \ + X(SDL_free) \ + X(SDL_OpenAudioDeviceStream) \ + X(SDL_DestroyAudioStream) \ + X(SDL_ResumeAudioStreamDevice) \ + X(SDL_PauseAudioStreamDevice) \ + X(SDL_PutAudioStreamData) \ + X(SDL_GetBooleanProperty) \ + X(SDL_GUIDToString) \ + X(SDL_OpenGamepad) \ + X(SDL_CloseGamepad) \ + X(SDL_GetGamepadType) \ + X(SDL_GetGamepadButtonLabelForType) \ + X(SDL_GetGamepadVendor) \ + X(SDL_GetGamepadProduct) \ + X(SDL_SetGamepadLED) \ + X(SDL_GetGamepadMappings) \ + X(SDL_GetGamepadJoystick) \ + X(SDL_GetGamepadName) \ + X(SDL_GetGamepadBindings) \ + X(SDL_SetGamepadSensorEnabled) \ + X(SDL_GetGamepadPlayerIndex) \ + X(SDL_GetGamepadProperties) \ + X(SDL_GetGamepadAxis) \ + X(SDL_GetGamepadButton) \ + X(SDL_SendGamepadEffect) \ + X(SDL_GamepadHasSensor) \ + X(SDL_RumbleGamepad) \ + X(SDL_IsGamepad) \ + X(SDL_OpenJoystick) \ + X(SDL_CloseJoystick) \ + X(SDL_GetJoystickName) \ + X(SDL_GetJoystickPlayerIndex) \ + X(SDL_GetJoystickGUID) \ + X(SDL_GetJoystickProperties) \ + X(SDL_GetJoystickID) \ + X(SDL_GetJoystickAxis) \ + X(SDL_GetJoystickButton) \ + X(SDL_GetJoystickHat) \ + X(SDL_GetNumJoystickAxes) \ + X(SDL_GetNumJoystickButtons) \ + X(SDL_GetNumJoystickHats) \ + X(SDL_OpenHapticFromJoystick) \ + X(SDL_CloseHaptic) \ + X(SDL_HapticRumbleSupported) \ + X(SDL_InitHapticRumble) \ + X(SDL_PlayHapticRumble) \ + X(SDL_StopHapticRumble) \ + X(SDL_GetHapticFeatures) \ + X(SDL_CreateHapticEffect) \ + X(SDL_UpdateHapticEffect) \ + X(SDL_DestroyHapticEffect) \ + X(SDL_RunHapticEffect) \ + X(SDL_StopHapticEffect) + +struct DynSDL +{ +#define ADD_FUNC(F) decltype(&::F) F; + DYN_SDL_FUNCTIONS(ADD_FUNC) +#undef ADD_FUNC + + bool Open(Error* error); +}; + +extern DynSDL g_dyn_sdl; diff --git a/src/util/sdl_audio_stream.cpp b/src/util/sdl_audio_stream.cpp index b10c7cfa8..a8f0778e2 100644 --- a/src/util/sdl_audio_stream.cpp +++ b/src/util/sdl_audio_stream.cpp @@ -1,14 +1,13 @@ -// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #include "audio_stream.h" +#include "dyn_sdl.h" #include "common/assert.h" #include "common/error.h" #include "common/log.h" -#include - LOG_CHANNEL(AudioStream); namespace { @@ -40,14 +39,17 @@ static bool InitializeSDLAudio(Error* error) if (initialized) return true; + if (!g_dyn_sdl.Open(error)) + return false; + // May as well keep it alive until the process exits. - if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) + if (!g_dyn_sdl.SDL_InitSubSystem(SDL_INIT_AUDIO)) { - Error::SetStringFmt(error, "SDL_InitSubSystem(SDL_INIT_AUDIO) failed: {}", SDL_GetError()); + Error::SetStringFmt(error, "SDL_InitSubSystem(SDL_INIT_AUDIO) failed: {}", g_dyn_sdl.SDL_GetError()); return false; } - std::atexit([]() { SDL_QuitSubSystem(SDL_INIT_AUDIO); }); + std::atexit([]() { g_dyn_sdl.SDL_QuitSubSystem(SDL_INIT_AUDIO); }); initialized = true; return true; @@ -61,7 +63,7 @@ SDLAudioStream::~SDLAudioStream() { if (m_sdl_stream) { - SDL_DestroyAudioStream(m_sdl_stream); + g_dyn_sdl.SDL_DestroyAudioStream(m_sdl_stream); m_sdl_stream = nullptr; } } @@ -72,24 +74,24 @@ bool SDLAudioStream::Initialize(u32 sample_rate, u32 channels, u32 output_latenc const SDL_AudioSpec spec = { .format = SDL_AUDIO_S16LE, .channels = static_cast(channels), .freq = static_cast(sample_rate)}; - m_sdl_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, AudioCallback, this); + m_sdl_stream = g_dyn_sdl.SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, AudioCallback, this); if (!m_sdl_stream) { - Error::SetStringFmt(error, "SDL_OpenAudioDeviceStream() failed: {}", SDL_GetError()); + Error::SetStringFmt(error, "SDL_OpenAudioDeviceStream() failed: {}", g_dyn_sdl.SDL_GetError()); return false; } if (auto_start) - SDL_ResumeAudioStreamDevice(m_sdl_stream); + g_dyn_sdl.SDL_ResumeAudioStreamDevice(m_sdl_stream); return true; } bool SDLAudioStream::Start(Error* error) { - if (!SDL_ResumeAudioStreamDevice(m_sdl_stream)) + if (!g_dyn_sdl.SDL_ResumeAudioStreamDevice(m_sdl_stream)) { - Error::SetStringFmt(error, "SDL_ResumeAudioStreamDevice() failed: {}", SDL_GetError()); + Error::SetStringFmt(error, "SDL_ResumeAudioStreamDevice() failed: {}", g_dyn_sdl.SDL_GetError()); return false; } @@ -98,9 +100,9 @@ bool SDLAudioStream::Start(Error* error) bool SDLAudioStream::Stop(Error* error) { - if (!SDL_PauseAudioStreamDevice(m_sdl_stream)) + if (!g_dyn_sdl.SDL_PauseAudioStreamDevice(m_sdl_stream)) { - Error::SetStringFmt(error, "SDL_PauseAudioStreamDevice() failed: {}", SDL_GetError()); + Error::SetStringFmt(error, "SDL_PauseAudioStreamDevice() failed: {}", g_dyn_sdl.SDL_GetError()); return false; } @@ -118,7 +120,7 @@ void SDLAudioStream::AudioCallback(void* userdata, SDL_AudioStream* stream, int SDLAudioStream* const this_ptr = static_cast(userdata); const u32 num_frames = static_cast(additional_amount) / (sizeof(SampleType) * this_ptr->m_channels); this_ptr->m_source->ReadFrames(reinterpret_cast(data), num_frames); - SDL_PutAudioStreamData(stream, data, additional_amount); + g_dyn_sdl.SDL_PutAudioStreamData(stream, data, additional_amount); SDL_stack_free(data); } } diff --git a/src/util/sdl_input_source.cpp b/src/util/sdl_input_source.cpp index 9c6094b15..48d28c689 100644 --- a/src/util/sdl_input_source.cpp +++ b/src/util/sdl_input_source.cpp @@ -1,7 +1,8 @@ -// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #include "sdl_input_source.h" +#include "dyn_sdl.h" #include "input_manager.h" #include "core/settings.h" @@ -12,6 +13,7 @@ #include "common/assert.h" #include "common/bitutils.h" +#include "common/dynamic_library.h" #include "common/error.h" #include "common/file_system.h" #include "common/gsvector.h" @@ -23,6 +25,7 @@ #include "fmt/format.h" #include +#include #ifdef __APPLE__ #include @@ -131,6 +134,13 @@ static constexpr const std::array s_ps_but }}; // clang-format on +struct Locals +{ + // Dynamic libraries + DynamicLibrary sdl_library; + std::once_flag sdl_init_flag; +}; + } // namespace static constexpr std::array s_sdl_hat_direction_names = {{ @@ -213,6 +223,9 @@ static constexpr const SettingInfo s_sdl_advanced_settings_info[] = { #endif }; +DynSDL g_dyn_sdl; +static Locals s_sdl_locals; + static bool IsPSControllerType(u8 type) { return (type >= SDL_GAMEPAD_TYPE_PS3 && type <= SDL_GAMEPAD_TYPE_PS5); @@ -244,8 +257,8 @@ static const AxisInfo& GetAxisInfo(u8 type, u32 axis) static std::string_view GetButtonLabel(u8 type, u32 button) { - const SDL_GamepadButtonLabel label = - SDL_GetGamepadButtonLabelForType(static_cast(type), static_cast(button)); + const SDL_GamepadButtonLabel label = g_dyn_sdl.SDL_GetGamepadButtonLabelForType( + static_cast(type), static_cast(button)); static constexpr const std::array lut = { static_cast(nullptr), // SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN @@ -268,8 +281,8 @@ static std::string_view GetButtonLabel(u8 type, u32 button) static const char* GetButtonIcon(u8 type, u32 button) { - const SDL_GamepadButtonLabel label = - SDL_GetGamepadButtonLabelForType(static_cast(type), static_cast(button)); + const SDL_GamepadButtonLabel label = g_dyn_sdl.SDL_GetGamepadButtonLabelForType( + static_cast(type), static_cast(button)); static constexpr const std::array lut = { static_cast(nullptr), // SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN @@ -306,6 +319,52 @@ static void SDLLogCallback(void* userdata, int category, SDL_LogPriority priorit GENERIC_LOG(Log::Channel::SDL, priority_map[priority], Log::Color::Default, message); } +bool DynSDL::Open(Error* error) +{ + // Because of course friggin linux is different... +#ifdef _WIN32 + static constexpr int lib_major_version = -1; +#else + static constexpr int lib_major_version = 0; +#endif + + if (s_sdl_locals.sdl_library.IsOpen()) + return true; + + std::call_once(s_sdl_locals.sdl_init_flag, [&error]() { + Error lerror; + DynamicLibrary lib; + if (!lib.Open(DynamicLibrary::GetVersionedFilename("SDL3", lib_major_version).c_str(), &lerror)) + { + ERROR_LOG("Failed to load sqlite: {}", lerror.GetDescription()); + Error::SetStringFmt(error, "Failed to load sqlite: {}", lerror.GetDescription()); + return; + } + + // clang-format off + static const DynamicLibrary::SymbolTable sdl_symbols[] = { +#define SDL_SYMBOL(F) {#F, (void**)&g_dyn_sdl.F}, + DYN_SDL_FUNCTIONS(SDL_SYMBOL) +#undef SDL_SYMBOL + }; + // clang-format on + + if (!lib.ResolveSymbols(sdl_symbols, std::size(sdl_symbols), error)) + return; + + s_sdl_locals.sdl_library = std::move(lib); + + g_dyn_sdl.SDL_SetLogOutputFunction(SDLLogCallback, nullptr); +#if defined(_DEBUG) || defined(_DEVEL) + g_dyn_sdl.SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG); +#else + g_dyn_sdl.SDL_SetLogPriorities(SDL_LOG_PRIORITY_INFO); +#endif + }); + + return s_sdl_locals.sdl_library.IsOpen(); +} + SDLInputSource::SDLInputSource() = default; SDLInputSource::~SDLInputSource() @@ -315,6 +374,12 @@ SDLInputSource::~SDLInputSource() bool SDLInputSource::Initialize(const SettingsInterface& si, std::unique_lock& settings_lock) { + if (Error error; !g_dyn_sdl.Open(&error)) + { + ERROR_LOG("Failed to load SDL: {}", error.GetDescription()); + return false; + } + LoadSettings(si); settings_lock.unlock(); SetHints(); @@ -429,8 +494,8 @@ static bool IsPS5Controller(SDL_Gamepad* gp) {static_cast(0x054c), static_cast(0x0ce6)}, // Sony DualSense Controller {static_cast(0x054c), static_cast(0x0df2)} // Sony DualSense Edge Controller }; - const u16 gamepad_vendor = SDL_GetGamepadVendor(gp); - const u16 gamepad_product = SDL_GetGamepadProduct(gp); + const u16 gamepad_vendor = g_dyn_sdl.SDL_GetGamepadVendor(gp); + const u16 gamepad_product = g_dyn_sdl.SDL_GetGamepadProduct(gp); bool supported = false; for (auto& supported_controller : supported_controllers) @@ -483,12 +548,12 @@ void SDLInputSource::SetControllerRGBLED(SDL_Gamepad* gp, bool has_rgb_led, cons const GSVector4 c = c0 + (c1 - c0) * GSVector4(intensity); const u32 color = c.rgba32(); - SDL_SetGamepadLED(gp, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + g_dyn_sdl.SDL_SetGamepadLED(gp, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); } else { const u8 color = static_cast(std::clamp(static_cast(std::round(255.0f * intensity)), 0, 255)); - SDL_SetGamepadLED(gp, color, color, color); + g_dyn_sdl.SDL_SetGamepadLED(gp, color, color, color); } } @@ -528,8 +593,8 @@ void SDLInputSource::SetControllerMicMuteLED(SDL_Gamepad* gp, bool enabled) effects.ucMicLightMode = static_cast(enabled); // 0x00: Disable Mute LED, 0x01: Enable Mute LED, 0x02: Enable Mute LED (Pulsing) - if (!SDL_SendGamepadEffect(gp, &effects, sizeof(effects))) - ERROR_LOG("Failed to set mic mute LED to {}: {}", enabled, SDL_GetError()); + if (!g_dyn_sdl.SDL_SendGamepadEffect(gp, &effects, sizeof(effects))) + ERROR_LOG("Failed to set mic mute LED to {}: {}", enabled, g_dyn_sdl.SDL_GetError()); } std::span SDLInputSource::GetAdvancedSettingsInfo() @@ -543,13 +608,13 @@ void SDLInputSource::SetHints() FileSystem::FileExists(upath.c_str())) { INFO_LOG("Using Controller DB from user directory: '{}'", upath); - SDL_SetHint(SDL_HINT_GAMECONTROLLERCONFIG_FILE, upath.c_str()); + g_dyn_sdl.SDL_SetHint(SDL_HINT_GAMECONTROLLERCONFIG_FILE, upath.c_str()); } else if (const std::string rpath = EmuFolders::GetOverridableResourcePath(CONTROLLER_DB_FILENAME); FileSystem::FileExists(rpath.c_str())) { INFO_LOG("Using Controller DB from resources."); - SDL_SetHint(SDL_HINT_GAMECONTROLLERCONFIG_FILE, rpath.c_str()); + g_dyn_sdl.SDL_SetHint(SDL_HINT_GAMECONTROLLERCONFIG_FILE, rpath.c_str()); } else { @@ -557,57 +622,50 @@ void SDLInputSource::SetHints() } INFO_LOG("Enhanced controller reports are {}.", m_controller_enhanced_mode ? "enabled" : "disabled"); - SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, m_controller_enhanced_mode ? "auto" : "0"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED, m_controller_ps5_player_led ? "1" : "0"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_WII, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS3, "1"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, m_controller_enhanced_mode ? "auto" : "0"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED, m_controller_ps5_player_led ? "1" : "0"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_WII, "1"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS3, "1"); INFO_LOG("XBox HIDAPI is {}.", m_joystick_xbox_hidapi ? "enabled" : "disabled"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, m_joystick_xbox_hidapi ? "1" : "0"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, m_joystick_xbox_hidapi ? "1" : "0"); #if defined(_WIN32) INFO_LOG("RawInput is {}, DirectInput is {}, XInput is {}, WGI is {}, GameInput is {}.", m_joystick_rawinput ? "enabled" : "disabled", m_joystick_directinput ? "enabled" : "disabled", m_joystick_xinput ? "enabled" : "disabled", m_joystick_wgi ? "enabled" : "disabled", m_joystick_gameinput ? "enabled" : "disabled"); - SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, m_joystick_rawinput ? "1" : "0"); - SDL_SetHint(SDL_HINT_JOYSTICK_DIRECTINPUT, m_joystick_directinput ? "1" : "0"); - SDL_SetHint(SDL_HINT_XINPUT_ENABLED, m_joystick_xinput ? "1" : "0"); - SDL_SetHint(SDL_HINT_JOYSTICK_WGI, m_joystick_wgi ? "1" : "0"); - SDL_SetHint(SDL_HINT_JOYSTICK_GAMEINPUT, m_joystick_gameinput ? "1" : "0"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, m_joystick_rawinput ? "1" : "0"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_JOYSTICK_DIRECTINPUT, m_joystick_directinput ? "1" : "0"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_XINPUT_ENABLED, m_joystick_xinput ? "1" : "0"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_JOYSTICK_WGI, m_joystick_wgi ? "1" : "0"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_JOYSTICK_GAMEINPUT, m_joystick_gameinput ? "1" : "0"); #elif defined(__APPLE__) INFO_LOG("IOKit is {}, MFI is {}.", m_enable_iokit_driver ? "enabled" : "disabled", m_enable_mfi_driver ? "enabled" : "disabled"); - SDL_SetHint(SDL_HINT_JOYSTICK_IOKIT, m_enable_iokit_driver ? "1" : "0"); - SDL_SetHint(SDL_HINT_JOYSTICK_MFI, m_enable_mfi_driver ? "1" : "0"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_JOYSTICK_IOKIT, m_enable_iokit_driver ? "1" : "0"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_JOYSTICK_MFI, m_enable_mfi_driver ? "1" : "0"); #else - SDL_SetHint(SDL_HINT_JOYSTICK_LINUX_DIGITAL_HATS, m_joystick_force_hat_input ? "1" : "0"); + g_dyn_sdl.SDL_SetHint(SDL_HINT_JOYSTICK_LINUX_DIGITAL_HATS, m_joystick_force_hat_input ? "1" : "0"); #endif for (const std::pair& hint : m_sdl_hints) - SDL_SetHint(hint.first.c_str(), hint.second.c_str()); + g_dyn_sdl.SDL_SetHint(hint.first.c_str(), hint.second.c_str()); } bool SDLInputSource::InitializeSubsystem() { - if (!SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC)) + if (!g_dyn_sdl.SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC)) { ERROR_LOG("SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC) failed"); return false; } - SDL_SetLogOutputFunction(SDLLogCallback, nullptr); -#if defined(_DEBUG) || defined(_DEVEL) - SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG); -#else - SDL_SetLogPriorities(SDL_LOG_PRIORITY_INFO); -#endif - // we should open the controllers as the connected events come in, so no need to do any more here m_sdl_subsystem_initialized = true; int mapping_count = 0; - SDL_free(SDL_GetGamepadMappings(&mapping_count)); + g_dyn_sdl.SDL_free(g_dyn_sdl.SDL_GetGamepadMappings(&mapping_count)); INFO_LOG("{} controller mappings are loaded.", mapping_count); return true; @@ -620,7 +678,7 @@ void SDLInputSource::ShutdownSubsystem() if (m_sdl_subsystem_initialized) { - SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC); + g_dyn_sdl.SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC); m_sdl_subsystem_initialized = false; } } @@ -630,7 +688,7 @@ void SDLInputSource::PollEvents() for (;;) { SDL_Event ev; - if (SDL_PollEvent(&ev)) + if (g_dyn_sdl.SDL_PollEvent(&ev)) ProcessSDLEvent(&ev); else break; @@ -646,7 +704,8 @@ InputManager::DeviceList SDLInputSource::EnumerateDevices() std::string id = fmt::format("SDL-{}", cd.player_id); const InputBindingKey key = MakeGenericControllerDeviceKey(InputSourceType::SDL, cd.player_id); - const char* name = cd.gamepad ? SDL_GetGamepadName(cd.gamepad) : SDL_GetJoystickName(cd.joystick); + const char* name = + cd.gamepad ? g_dyn_sdl.SDL_GetGamepadName(cd.gamepad) : g_dyn_sdl.SDL_GetJoystickName(cd.joystick); if (name) ret.emplace_back(key, std::move(id), name); else @@ -992,14 +1051,14 @@ void SDLInputSource::SetSubclassPollDeviceList(InputSubclass subclass, const std return; DEV_LOG("{} accelerometer on gamepad {}", wants_enable ? "Enabling" : "Disabling", cd.player_id); - if (SDL_SetGamepadSensorEnabled(cd.gamepad, SDL_SENSOR_ACCEL, wants_enable)) + if (g_dyn_sdl.SDL_SetGamepadSensorEnabled(cd.gamepad, SDL_SENSOR_ACCEL, wants_enable)) { cd.accel_enabled = wants_enable; } else { ERROR_LOG("Failed to {} accelerometer on gamepad {}: {}", wants_enable ? "enable" : "disabled", cd.player_id, - SDL_GetError()); + g_dyn_sdl.SDL_GetError()); } } } @@ -1051,7 +1110,7 @@ bool SDLInputSource::ProcessSDLEvent(const SDL_Event* event) case SDL_EVENT_JOYSTICK_ADDED: { // Let gamepad handle.. well.. gamepads. - if (SDL_IsGamepad(event->jdevice.which)) + if (g_dyn_sdl.SDL_IsGamepad(event->jdevice.which)) return false; INFO_LOG("Joystick {} inserted", event->jdevice.which); @@ -1152,13 +1211,13 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller) if (is_gamecontroller) { - gamepad = SDL_OpenGamepad(index); - joystick = gamepad ? SDL_GetGamepadJoystick(gamepad) : nullptr; + gamepad = g_dyn_sdl.SDL_OpenGamepad(index); + joystick = gamepad ? g_dyn_sdl.SDL_GetGamepadJoystick(gamepad) : nullptr; } else { gamepad = nullptr; - joystick = SDL_OpenJoystick(index); + joystick = g_dyn_sdl.SDL_OpenJoystick(index); } if (!gamepad && !joystick) @@ -1167,8 +1226,9 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller) return false; } - const SDL_JoystickID joystick_id = SDL_GetJoystickID(joystick); - int player_id = gamepad ? SDL_GetGamepadPlayerIndex(gamepad) : SDL_GetJoystickPlayerIndex(joystick); + const SDL_JoystickID joystick_id = g_dyn_sdl.SDL_GetJoystickID(joystick); + int player_id = + gamepad ? g_dyn_sdl.SDL_GetGamepadPlayerIndex(gamepad) : g_dyn_sdl.SDL_GetJoystickPlayerIndex(joystick); if (player_id < 0 || GetControllerDataForPlayerId(player_id) != m_controllers.end()) { const int free_player_id = GetFreePlayerId(); @@ -1178,16 +1238,17 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller) } char guid[33]; - SDL_GUIDToString(SDL_GetJoystickGUID(joystick), guid, sizeof(guid)); + g_dyn_sdl.SDL_GUIDToString(g_dyn_sdl.SDL_GetJoystickGUID(joystick), guid, sizeof(guid)); - const char* name = gamepad ? SDL_GetGamepadName(gamepad) : SDL_GetJoystickName(joystick); + const char* name = gamepad ? g_dyn_sdl.SDL_GetGamepadName(gamepad) : g_dyn_sdl.SDL_GetJoystickName(joystick); if (!name) name = "Unknown Device"; VERBOSE_LOG("Opened {} {} (instance id {}, player id {}, guid {}): {}", is_gamecontroller ? "game controller" : "joystick", index, joystick_id, player_id, guid, name); - const SDL_PropertiesID properties = gamepad ? SDL_GetGamepadProperties(gamepad) : SDL_GetJoystickProperties(joystick); + const SDL_PropertiesID properties = + gamepad ? g_dyn_sdl.SDL_GetGamepadProperties(gamepad) : g_dyn_sdl.SDL_GetJoystickProperties(joystick); ControllerData cd = {}; cd.player_id = player_id; @@ -1197,13 +1258,13 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller) cd.joystick = joystick; cd.last_touch_x = 0.0f; cd.last_touch_y = 0.0f; - cd.gamepad_type = gamepad ? - static_cast(std::clamp(SDL_GetGamepadType(gamepad), 0, SDL_GAMEPAD_TYPE_COUNT - 1)) : - SDL_GAMEPAD_TYPE_UNKNOWN; + cd.gamepad_type = + gamepad ? static_cast(std::clamp(g_dyn_sdl.SDL_GetGamepadType(gamepad), 0, SDL_GAMEPAD_TYPE_COUNT - 1)) : + SDL_GAMEPAD_TYPE_UNKNOWN; - const u32 num_axes = static_cast(std::max(SDL_GetNumJoystickAxes(joystick), 0)); - const u32 num_buttons = static_cast(std::max(SDL_GetNumJoystickButtons(joystick), 0)); - const u32 num_hats = static_cast(std::max(SDL_GetNumJoystickHats(joystick), 0)); + const u32 num_axes = static_cast(std::max(g_dyn_sdl.SDL_GetNumJoystickAxes(joystick), 0)); + const u32 num_buttons = static_cast(std::max(g_dyn_sdl.SDL_GetNumJoystickButtons(joystick), 0)); + const u32 num_hats = static_cast(std::max(g_dyn_sdl.SDL_GetNumJoystickHats(joystick), 0)); VERBOSE_LOG("Controller {} is a {} with {} axes, {} buttons and {} hats", player_id, s_sdl_gamepad_type_names[cd.gamepad_type], num_axes, num_buttons, num_hats); @@ -1235,7 +1296,7 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller) cd.joy_hat_used_in_gc.resize(num_hats, false); int binding_count = 0; - SDL_GamepadBinding** const bindings = SDL_GetGamepadBindings(gamepad, &binding_count); + SDL_GamepadBinding** const bindings = g_dyn_sdl.SDL_GetGamepadBindings(gamepad, &binding_count); for (int i = 0; i < binding_count; i++) { const SDL_GamepadBinding* binding = bindings[i]; @@ -1267,24 +1328,25 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller) } } } - SDL_free(bindings); + g_dyn_sdl.SDL_free(bindings); } - cd.use_gamepad_rumble = (gamepad && SDL_GetBooleanProperty(properties, SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN, false)); + cd.use_gamepad_rumble = + (gamepad && g_dyn_sdl.SDL_GetBooleanProperty(properties, SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN, false)); if (cd.use_gamepad_rumble) { VERBOSE_LOG("Rumble is supported on '{}' via gamepad", name); } else { - SDL_Haptic* haptic = SDL_OpenHapticFromJoystick(joystick); + SDL_Haptic* haptic = g_dyn_sdl.SDL_OpenHapticFromJoystick(joystick); if (haptic) { SDL_HapticEffect ef = {}; ef.leftright.type = SDL_HAPTIC_LEFTRIGHT; ef.leftright.length = 1000; - int ef_id = SDL_CreateHapticEffect(haptic, &ef); + int ef_id = g_dyn_sdl.SDL_CreateHapticEffect(haptic, &ef); if (ef_id >= 0) { cd.haptic = haptic; @@ -1292,15 +1354,15 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller) } else { - ERROR_LOG("Failed to create haptic left/right effect: {}", SDL_GetError()); - if (SDL_HapticRumbleSupported(haptic) && SDL_InitHapticRumble(haptic) != 0) + ERROR_LOG("Failed to create haptic left/right effect: {}", g_dyn_sdl.SDL_GetError()); + if (g_dyn_sdl.SDL_HapticRumbleSupported(haptic) && g_dyn_sdl.SDL_InitHapticRumble(haptic) != 0) { cd.haptic = haptic; } else { - ERROR_LOG("No haptic rumble supported: {}", SDL_GetError()); - SDL_CloseHaptic(haptic); + ERROR_LOG("No haptic rumble supported: {}", g_dyn_sdl.SDL_GetError()); + g_dyn_sdl.SDL_CloseHaptic(haptic); } } } @@ -1316,9 +1378,11 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller) if ((cd.has_mode_led = IsPS5Controller(gamepad))) SetControllerMicMuteLED(gamepad, false); - cd.has_rgb_led = (gamepad && SDL_GetBooleanProperty(properties, SDL_PROP_GAMEPAD_CAP_RGB_LED_BOOLEAN, false)); + cd.has_rgb_led = + (gamepad && g_dyn_sdl.SDL_GetBooleanProperty(properties, SDL_PROP_GAMEPAD_CAP_RGB_LED_BOOLEAN, false)); cd.has_led = - (cd.has_rgb_led || (gamepad && SDL_GetBooleanProperty(properties, SDL_PROP_GAMEPAD_CAP_MONO_LED_BOOLEAN, false))); + (cd.has_rgb_led || + (gamepad && g_dyn_sdl.SDL_GetBooleanProperty(properties, SDL_PROP_GAMEPAD_CAP_MONO_LED_BOOLEAN, false))); if (cd.has_led && player_id >= 0 && static_cast(player_id) < MAX_LED_COLORS) SetControllerRGBLED(gamepad, cd.has_rgb_led, m_led_colors[player_id], 0.0f); @@ -1326,7 +1390,7 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller) const InputBindingKey device_key = MakeGenericControllerDeviceKey(InputSourceType::SDL, player_id); // Check for accelerometer support and enable it - cd.has_accel = (gamepad && SDL_GamepadHasSensor(gamepad, SDL_SENSOR_ACCEL)); + cd.has_accel = (gamepad && g_dyn_sdl.SDL_GamepadHasSensor(gamepad, SDL_SENSOR_ACCEL)); cd.accel_enabled = false; if (cd.has_accel) { @@ -1336,9 +1400,9 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller) subclass_key.source_subtype = InputSubclass::ControllerSensor; if (InputManager::HasAnyBindingsForSubclass(subclass_key)) { - if (!SDL_SetGamepadSensorEnabled(gamepad, SDL_SENSOR_ACCEL, true)) + if (!g_dyn_sdl.SDL_SetGamepadSensorEnabled(gamepad, SDL_SENSOR_ACCEL, true)) { - WARNING_LOG("Failed to enable accelerometer on '{}': {}", name, SDL_GetError()); + WARNING_LOG("Failed to enable accelerometer on '{}': {}", name, g_dyn_sdl.SDL_GetError()); cd.has_accel = false; } else @@ -1365,12 +1429,12 @@ bool SDLInputSource::CloseDevice(SDL_JoystickID joystick_index) return false; if (it->haptic) - SDL_CloseHaptic(it->haptic); + g_dyn_sdl.SDL_CloseHaptic(it->haptic); if (it->gamepad) - SDL_CloseGamepad(it->gamepad); + g_dyn_sdl.SDL_CloseGamepad(it->gamepad); else - SDL_CloseJoystick(it->joystick); + g_dyn_sdl.SDL_CloseJoystick(it->joystick); const int player_id = it->player_id; m_controllers.erase(it); @@ -1563,31 +1627,31 @@ std::optional SDLInputSource::GetCurrentValue(InputBindingKey key) { if (cd->gamepad && key.data < s_axis_info.size()) { - ret = NormalizeS16(SDL_GetGamepadAxis(cd->gamepad, static_cast(key.data))); + ret = NormalizeS16(g_dyn_sdl.SDL_GetGamepadAxis(cd->gamepad, static_cast(key.data))); } else if (key.data >= s_axis_info.size()) { ret = NormalizeS16( - SDL_GetJoystickAxis(cd->joystick, static_cast(key.data - static_cast(s_axis_info.size())))); + g_dyn_sdl.SDL_GetJoystickAxis(cd->joystick, static_cast(key.data - static_cast(s_axis_info.size())))); } } else if (key.source_subtype == InputSubclass::ControllerButton) { if (cd->gamepad && key.data < s_button_info.size()) { - ret = BoolToFloat(SDL_GetGamepadButton(cd->gamepad, static_cast(key.data))); + ret = BoolToFloat(g_dyn_sdl.SDL_GetGamepadButton(cd->gamepad, static_cast(key.data))); } else if (key.data >= s_button_info.size()) { - ret = BoolToFloat( - SDL_GetJoystickButton(cd->joystick, static_cast(key.data - static_cast(s_button_info.size())))); + ret = BoolToFloat(g_dyn_sdl.SDL_GetJoystickButton( + cd->joystick, static_cast(key.data - static_cast(s_button_info.size())))); } } else if (key.source_subtype == InputSubclass::ControllerHat) { const u32 hat_index = key.data / static_cast(std::size(s_sdl_hat_direction_names)); const u8 hat_direction = Truncate8(key.data % static_cast(std::size(s_sdl_hat_direction_names))); - const u8 hat_value = SDL_GetJoystickHat(cd->joystick, static_cast(hat_index)); + const u8 hat_value = g_dyn_sdl.SDL_GetJoystickHat(cd->joystick, static_cast(hat_index)); ret = BoolToFloat((hat_value & (1u << hat_direction)) != 0); } else if (key.source_subtype == InputSubclass::ControllerSensor) @@ -1765,7 +1829,7 @@ void SDLInputSource::SendRumbleUpdate(ControllerData* cd) if (cd->use_gamepad_rumble) { - SDL_RumbleGamepad(cd->gamepad, cd->rumble_intensity[0], cd->rumble_intensity[1], DURATION); + g_dyn_sdl.SDL_RumbleGamepad(cd->gamepad, cd->rumble_intensity[0], cd->rumble_intensity[1], DURATION); return; } @@ -1778,12 +1842,12 @@ void SDLInputSource::SendRumbleUpdate(ControllerData* cd) ef.leftright.large_magnitude = cd->rumble_intensity[0]; ef.leftright.small_magnitude = cd->rumble_intensity[1]; ef.leftright.length = DURATION; - SDL_UpdateHapticEffect(cd->haptic, cd->haptic_left_right_effect, &ef); - SDL_RunHapticEffect(cd->haptic, cd->haptic_left_right_effect, SDL_HAPTIC_INFINITY); + g_dyn_sdl.SDL_UpdateHapticEffect(cd->haptic, cd->haptic_left_right_effect, &ef); + g_dyn_sdl.SDL_RunHapticEffect(cd->haptic, cd->haptic_left_right_effect, SDL_HAPTIC_INFINITY); } else { - SDL_StopHapticEffect(cd->haptic, cd->haptic_left_right_effect); + g_dyn_sdl.SDL_StopHapticEffect(cd->haptic, cd->haptic_left_right_effect); } } else @@ -1791,9 +1855,9 @@ void SDLInputSource::SendRumbleUpdate(ControllerData* cd) const float strength = static_cast(std::max(cd->rumble_intensity[0], cd->rumble_intensity[1])) * (1.0f / 65535.0f); if (strength > 0.0f) - SDL_PlayHapticRumble(cd->haptic, strength, DURATION); + g_dyn_sdl.SDL_PlayHapticRumble(cd->haptic, strength, DURATION); else - SDL_StopHapticRumble(cd->haptic); + g_dyn_sdl.SDL_StopHapticRumble(cd->haptic); } } @@ -1811,10 +1875,10 @@ std::unique_ptr SDLInputSource::CreateForceFeedbackDevice(s return nullptr; } - SDL_Haptic* haptic = SDL_OpenHapticFromJoystick(joystick); + SDL_Haptic* haptic = g_dyn_sdl.SDL_OpenHapticFromJoystick(joystick); if (!haptic) { - Error::SetStringFmt(error, "Haptic is not supported on {} ({})", device, SDL_GetJoystickName(joystick)); + Error::SetStringFmt(error, "Haptic is not supported on {} ({})", device, g_dyn_sdl.SDL_GetJoystickName(joystick)); return nullptr; } @@ -1832,7 +1896,7 @@ SDLForceFeedbackDevice::~SDLForceFeedbackDevice() { DestroyEffects(); - SDL_CloseHaptic(m_haptic); + g_dyn_sdl.SDL_CloseHaptic(m_haptic); m_haptic = nullptr; } } @@ -1841,20 +1905,20 @@ void SDLForceFeedbackDevice::CreateEffects(SDL_Joystick* joystick) { constexpr u32 length = 10000; // 10 seconds since NFS games seem to not issue new commands while rotating. - const u32 features = SDL_GetHapticFeatures(m_haptic); + const u32 features = g_dyn_sdl.SDL_GetHapticFeatures(m_haptic); if (features & SDL_HAPTIC_CONSTANT) { m_constant_effect.type = SDL_HAPTIC_CONSTANT; m_constant_effect.constant.direction.type = SDL_HAPTIC_STEERING_AXIS; m_constant_effect.constant.length = length; - m_constant_effect_id = SDL_CreateHapticEffect(m_haptic, &m_constant_effect); + m_constant_effect_id = g_dyn_sdl.SDL_CreateHapticEffect(m_haptic, &m_constant_effect); if (m_constant_effect_id < 0) - ERROR_LOG("SDL_HapticNewEffect() for constant failed: {}", SDL_GetError()); + ERROR_LOG("SDL_HapticNewEffect() for constant failed: {}", g_dyn_sdl.SDL_GetError()); } else { - WARNING_LOG("Constant effect is not supported on '{}'", SDL_GetJoystickName(joystick)); + WARNING_LOG("Constant effect is not supported on '{}'", g_dyn_sdl.SDL_GetJoystickName(joystick)); } } @@ -1864,10 +1928,10 @@ void SDLForceFeedbackDevice::DestroyEffects() { if (m_constant_effect_running) { - SDL_StopHapticEffect(m_haptic, m_constant_effect_id); + g_dyn_sdl.SDL_StopHapticEffect(m_haptic, m_constant_effect_id); m_constant_effect_running = false; } - SDL_DestroyHapticEffect(m_haptic, m_constant_effect_id); + g_dyn_sdl.SDL_DestroyHapticEffect(m_haptic, m_constant_effect_id); m_constant_effect_id = -1; } } @@ -1893,16 +1957,16 @@ void SDLForceFeedbackDevice::SetConstantForce(s32 level) if (m_constant_effect.constant.level != new_level) { m_constant_effect.constant.level = new_level; - if (SDL_UpdateHapticEffect(m_haptic, m_constant_effect_id, &m_constant_effect) != 0) - ERROR_LOG("SDL_HapticUpdateEffect() for constant failed: {}", SDL_GetError()); + if (g_dyn_sdl.SDL_UpdateHapticEffect(m_haptic, m_constant_effect_id, &m_constant_effect) != 0) + ERROR_LOG("SDL_HapticUpdateEffect() for constant failed: {}", g_dyn_sdl.SDL_GetError()); } if (!m_constant_effect_running) { - if (SDL_RunHapticEffect(m_haptic, m_constant_effect_id, SDL_HAPTIC_INFINITY) == 0) + if (g_dyn_sdl.SDL_RunHapticEffect(m_haptic, m_constant_effect_id, SDL_HAPTIC_INFINITY) == 0) m_constant_effect_running = true; else - ERROR_LOG("SDL_HapticRunEffect() for constant failed: {}", SDL_GetError()); + ERROR_LOG("SDL_HapticRunEffect() for constant failed: {}", g_dyn_sdl.SDL_GetError()); } } @@ -1914,7 +1978,7 @@ void SDLForceFeedbackDevice::DisableForce(Effect force) { if (m_constant_effect_running) { - SDL_StopHapticEffect(m_haptic, m_constant_effect_id); + g_dyn_sdl.SDL_StopHapticEffect(m_haptic, m_constant_effect_id); m_constant_effect_running = false; } } diff --git a/src/util/util.props b/src/util/util.props index c6b432186..fe7683605 100644 --- a/src/util/util.props +++ b/src/util/util.props @@ -25,7 +25,7 @@ %(AdditionalIncludeDirectories);$(DepsIncludeDir)spirv_cross - %(AdditionalDependencies);freetype.lib;plutosvg.lib;SDL3.lib;soundtouch.lib;zip.lib;zlib.lib;zstd.lib + %(AdditionalDependencies);freetype.lib;plutosvg.lib;soundtouch.lib;zip.lib;zlib.lib;zstd.lib diff --git a/src/util/util.vcxproj b/src/util/util.vcxproj index f3c3668b9..de143c867 100644 --- a/src/util/util.vcxproj +++ b/src/util/util.vcxproj @@ -3,6 +3,7 @@ + diff --git a/src/util/util.vcxproj.filters b/src/util/util.vcxproj.filters index 1d0d3b407..5faad9dee 100644 --- a/src/util/util.vcxproj.filters +++ b/src/util/util.vcxproj.filters @@ -86,6 +86,7 @@ +