SDLInputSource: Load SDL dynamically

pull/3794/head
Stenzek 2 weeks ago
parent fa935c2968
commit 38751bdf7d
No known key found for this signature in database

@ -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"

@ -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)

@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
#include <SDL3/SDL.h>
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;

@ -1,14 +1,13 @@
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// 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 <SDL3/SDL.h>
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<int>(channels), .freq = static_cast<int>(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<SDLAudioStream*>(userdata);
const u32 num_frames = static_cast<u32>(additional_amount) / (sizeof(SampleType) * this_ptr->m_channels);
this_ptr->m_source->ReadFrames(reinterpret_cast<SampleType*>(data), num_frames);
SDL_PutAudioStreamData(stream, data, additional_amount);
g_dyn_sdl.SDL_PutAudioStreamData(stream, data, additional_amount);
SDL_stack_free(data);
}
}

@ -1,7 +1,8 @@
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// 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 <cmath>
#include <mutex>
#ifdef __APPLE__
#include <dispatch/dispatch.h>
@ -131,6 +134,13 @@ static constexpr const std::array<ButtonInfo, SDL_GAMEPAD_BUTTON_COUNT> s_ps_but
}};
// clang-format on
struct Locals
{
// Dynamic libraries
DynamicLibrary sdl_library;
std::once_flag sdl_init_flag;
};
} // namespace
static constexpr std::array<const char*, 4> 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<SDL_GamepadType>(type), static_cast<SDL_GamepadButton>(button));
const SDL_GamepadButtonLabel label = g_dyn_sdl.SDL_GetGamepadButtonLabelForType(
static_cast<SDL_GamepadType>(type), static_cast<SDL_GamepadButton>(button));
static constexpr const std::array lut = {
static_cast<const char*>(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<SDL_GamepadType>(type), static_cast<SDL_GamepadButton>(button));
const SDL_GamepadButtonLabel label = g_dyn_sdl.SDL_GetGamepadButtonLabelForType(
static_cast<SDL_GamepadType>(type), static_cast<SDL_GamepadButton>(button));
static constexpr const std::array lut = {
static_cast<const char*>(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<std::mutex>& 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<u16>(0x054c), static_cast<u16>(0x0ce6)}, // Sony DualSense Controller
{static_cast<u16>(0x054c), static_cast<u16>(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<u8>(std::clamp(static_cast<int>(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<Uint8>(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<const SettingInfo> 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<std::string, std::string>& 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<u8>(std::clamp<int>(SDL_GetGamepadType(gamepad), 0, SDL_GAMEPAD_TYPE_COUNT - 1)) :
SDL_GAMEPAD_TYPE_UNKNOWN;
cd.gamepad_type =
gamepad ? static_cast<u8>(std::clamp<int>(g_dyn_sdl.SDL_GetGamepadType(gamepad), 0, SDL_GAMEPAD_TYPE_COUNT - 1)) :
SDL_GAMEPAD_TYPE_UNKNOWN;
const u32 num_axes = static_cast<u32>(std::max(SDL_GetNumJoystickAxes(joystick), 0));
const u32 num_buttons = static_cast<u32>(std::max(SDL_GetNumJoystickButtons(joystick), 0));
const u32 num_hats = static_cast<u32>(std::max(SDL_GetNumJoystickHats(joystick), 0));
const u32 num_axes = static_cast<u32>(std::max(g_dyn_sdl.SDL_GetNumJoystickAxes(joystick), 0));
const u32 num_buttons = static_cast<u32>(std::max(g_dyn_sdl.SDL_GetNumJoystickButtons(joystick), 0));
const u32 num_hats = static_cast<u32>(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<u32>(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<float> SDLInputSource::GetCurrentValue(InputBindingKey key)
{
if (cd->gamepad && key.data < s_axis_info.size())
{
ret = NormalizeS16(SDL_GetGamepadAxis(cd->gamepad, static_cast<SDL_GamepadAxis>(key.data)));
ret = NormalizeS16(g_dyn_sdl.SDL_GetGamepadAxis(cd->gamepad, static_cast<SDL_GamepadAxis>(key.data)));
}
else if (key.data >= s_axis_info.size())
{
ret = NormalizeS16(
SDL_GetJoystickAxis(cd->joystick, static_cast<int>(key.data - static_cast<u32>(s_axis_info.size()))));
g_dyn_sdl.SDL_GetJoystickAxis(cd->joystick, static_cast<int>(key.data - static_cast<u32>(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<SDL_GamepadButton>(key.data)));
ret = BoolToFloat(g_dyn_sdl.SDL_GetGamepadButton(cd->gamepad, static_cast<SDL_GamepadButton>(key.data)));
}
else if (key.data >= s_button_info.size())
{
ret = BoolToFloat(
SDL_GetJoystickButton(cd->joystick, static_cast<int>(key.data - static_cast<u32>(s_button_info.size()))));
ret = BoolToFloat(g_dyn_sdl.SDL_GetJoystickButton(
cd->joystick, static_cast<int>(key.data - static_cast<u32>(s_button_info.size()))));
}
}
else if (key.source_subtype == InputSubclass::ControllerHat)
{
const u32 hat_index = key.data / static_cast<u32>(std::size(s_sdl_hat_direction_names));
const u8 hat_direction = Truncate8(key.data % static_cast<u32>(std::size(s_sdl_hat_direction_names)));
const u8 hat_value = SDL_GetJoystickHat(cd->joystick, static_cast<int>(hat_index));
const u8 hat_value = g_dyn_sdl.SDL_GetJoystickHat(cd->joystick, static_cast<int>(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<float>(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<ForceFeedbackDevice> 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;
}
}

@ -25,7 +25,7 @@
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DepsIncludeDir)spirv_cross</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>%(AdditionalDependencies);freetype.lib;plutosvg.lib;SDL3.lib;soundtouch.lib;zip.lib;zlib.lib;zstd.lib</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies);freetype.lib;plutosvg.lib;soundtouch.lib;zip.lib;zlib.lib;zstd.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

@ -3,6 +3,7 @@
<Import Project="..\..\dep\vsprops\Configurations.props" />
<ItemGroup>
<ClInclude Include="animated_image.h" />
<ClInclude Include="dyn_sdl.h" />
<ClInclude Include="dyn_sqlite.h" />
<ClInclude Include="http_cache.h" />
<ClInclude Include="object_archive.h" />

@ -86,6 +86,7 @@
<ClInclude Include="dyn_libpng.h" />
<ClInclude Include="sqlite_helpers.h" />
<ClInclude Include="zip_helpers.h" />
<ClInclude Include="dyn_sdl.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="state_wrapper.cpp" />

Loading…
Cancel
Save