Started transition to SDL3 (audio working)

This commit is contained in:
Jarrod Norwell 2024-05-27 01:06:51 +08:00
parent 1723651511
commit 0471d11909
35 changed files with 1223 additions and 527 deletions

View File

@ -12,12 +12,12 @@ include(DownloadExternals)
include(CMakeDependentOption) include(CMakeDependentOption)
include(CTest) include(CTest)
# Set bundled sdl2/qt as dependent options. # Set bundled sdl3/qt as dependent options.
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON # OFF by default, but if ENABLE_SDL3 and MSVC are true then ON
option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) option(ENABLE_SDL3 "Enable the SDL3 frontend" ON)
CMAKE_DEPENDENT_OPTION(SUDACHI_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON "ENABLE_SDL2;MSVC" OFF) CMAKE_DEPENDENT_OPTION(SUDACHI_USE_BUNDLED_SDL3 "Download bundled SDL3 binaries" ON "ENABLE_SDL3;MSVC" OFF)
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion # On Linux system SDL3 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
CMAKE_DEPENDENT_OPTION(SUDACHI_USE_EXTERNAL_SDL2 "Compile external SDL2" ON "ENABLE_SDL2;NOT MSVC" OFF) CMAKE_DEPENDENT_OPTION(SUDACHI_USE_EXTERNAL_SDL3 "Compile external SDL3" ON "ENABLE_SDL3;NOT MSVC" OFF)
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "NOT ANDROID" OFF) cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "NOT ANDROID" OFF)
@ -527,32 +527,32 @@ if(ENABLE_QT)
endif() endif()
# find SDL2 exports a bunch of variables that are needed, so its easier to do this outside of the sudachi_find_package # find SDL3 exports a bunch of variables that are needed, so its easier to do this outside of the sudachi_find_package
if (ENABLE_SDL2) if (ENABLE_SDL3)
if (SUDACHI_USE_BUNDLED_SDL2) if (SUDACHI_USE_BUNDLED_SDL3)
# Detect toolchain and platform # Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64) if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
set(SDL2_VER "SDL2-2.28.2") set(SDL3_VER "SDL3-3.1.2")
else() else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable SUDACHI_USE_BUNDLED_SDL2 and provide your own.") message(FATAL_ERROR "No bundled SDL3 binaries for your toolchain. Disable SUDACHI_USE_BUNDLED_SDL3 and provide your own.")
endif() endif()
if (DEFINED SDL2_VER) if (DEFINED SDL3_VER)
download_bundled_external("sdl2/" ${SDL2_VER} SDL2_PREFIX) download_bundled_external("sdl3/" ${SDL3_VER} SDL3_PREFIX)
endif() endif()
set(SDL2_FOUND YES) set(SDL3_FOUND YES)
set(SDL2_INCLUDE_DIR "${SDL2_PREFIX}/include" CACHE PATH "Path to SDL2 headers") set(SDL3_INCLUDE_DIR "${SDL3_PREFIX}/include" CACHE PATH "Path to SDL3 headers")
set(SDL2_LIBRARY "${SDL2_PREFIX}/lib/x64/SDL2.lib" CACHE PATH "Path to SDL2 library") set(SDL3_LIBRARY "${SDL3_PREFIX}/lib/x64/SDL3.lib" CACHE PATH "Path to SDL3 library")
set(SDL2_DLL_DIR "${SDL2_PREFIX}/lib/x64/" CACHE PATH "Path to SDL2.dll") set(SDL3_DLL_DIR "${SDL3_PREFIX}/lib/x64/" CACHE PATH "Path to SDL3.dll")
add_library(SDL2::SDL2 INTERFACE IMPORTED) add_library(SDL3::SDL3 INTERFACE IMPORTED)
target_link_libraries(SDL2::SDL2 INTERFACE "${SDL2_LIBRARY}") target_link_libraries(SDL3::SDL3 INTERFACE "${SDL3_LIBRARY}")
target_include_directories(SDL2::SDL2 INTERFACE "${SDL2_INCLUDE_DIR}") target_include_directories(SDL3::SDL3 INTERFACE "${SDL3_INCLUDE_DIR}")
elseif (SUDACHI_USE_EXTERNAL_SDL2) elseif (SUDACHI_USE_EXTERNAL_SDL3)
message(STATUS "Using SDL2 from externals.") message(STATUS "Using SDL3 from externals.")
else() else()
find_package(SDL2 2.26.4 REQUIRED) find_package(SDL3 3.1.2 REQUIRED)
endif() endif()
endif() endif()

View File

@ -4,5 +4,5 @@
function(copy_sudachi_SDL_deps target_dir) function(copy_sudachi_SDL_deps target_dir)
include(WindowsCopyFiles) include(WindowsCopyFiles)
set(DLL_DEST "$<TARGET_FILE_DIR:${target_dir}>/") set(DLL_DEST "$<TARGET_FILE_DIR:${target_dir}>/")
windows_copy_files(${target_dir} ${SDL2_DLL_DIR} ${DLL_DEST} SDL2.dll) windows_copy_files(${target_dir} ${SDL3_DLL_DIR} ${DLL_DEST} SDL3.dll)
endfunction(copy_sudachi_SDL_deps) endfunction(copy_sudachi_SDL_deps)

View File

@ -6,7 +6,7 @@ set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64) set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX}) set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
set(SDL2_PATH ${MINGW_PREFIX}) set(SDL3_PATH ${MINGW_PREFIX})
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-) set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
# Specify the cross compiler # Specify the cross compiler

View File

@ -9,7 +9,7 @@ set(CMAKE_HOST_WIN32 TRUE)
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX}) set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
set(SDL2_PATH ${MINGW_PREFIX}) set(SDL3_PATH ${MINGW_PREFIX})
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-) set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
# Specify the cross compiler # Specify the cross compiler

View File

@ -62,8 +62,8 @@ if (ENABLE_LIBUSB AND NOT TARGET libusb::usb)
add_subdirectory(libusb) add_subdirectory(libusb)
endif() endif()
# SDL2 # SDL3
if (SUDACHI_USE_EXTERNAL_SDL2) if (SUDACHI_USE_EXTERNAL_SDL3)
if (NOT WIN32) if (NOT WIN32)
# Sudachi itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers # Sudachi itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095) # Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)

2
externals/SDL vendored

@ -1 +1 @@
Subproject commit cc016b0046d563287f0aa9f09b958b5e70d43696 Subproject commit 84cb065da2fcae9482d2ab7702d8bc4218ff80ed

View File

@ -201,7 +201,7 @@ if (SUDACHI_TESTS)
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()
if (ENABLE_SDL2) if (ENABLE_SDL3)
add_subdirectory(sudachi_cmd) add_subdirectory(sudachi_cmd)
endif() endif()

View File

@ -169,7 +169,7 @@ android {
cmake { cmake {
arguments( arguments(
"-DENABLE_QT=0", // Don't use QT "-DENABLE_QT=0", // Don't use QT
"-DENABLE_SDL2=0", // Don't use SDL "-DENABLE_SDL3=0", // Don't use SDL
"-DENABLE_WEB_SERVICE=0", // Don't use telemetry "-DENABLE_WEB_SERVICE=0", // Don't use telemetry
"-DBUNDLE_SPEEX=ON", "-DBUNDLE_SPEEX=ON",
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work "-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work

View File

@ -382,7 +382,7 @@ bg_green =
# Which audio output engine to use. # Which audio output engine to use.
# auto (default): Auto-select # auto (default): Auto-select
# cubeb: Cubeb audio engine (if available) # cubeb: Cubeb audio engine (if available)
# sdl2: SDL2 audio engine (if available) # sdl3: SDL3 audio engine (if available)
# null: No audio output # null: No audio output
output_engine = output_engine =

View File

@ -241,14 +241,14 @@ if (ENABLE_CUBEB)
target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1) target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
endif() endif()
if (ENABLE_SDL2) if (ENABLE_SDL3)
target_sources(audio_core PRIVATE target_sources(audio_core PRIVATE
sink/sdl2_sink.cpp sink/sdl3_sink.cpp
sink/sdl2_sink.h sink/sdl3_sink.h
) )
target_link_libraries(audio_core PRIVATE SDL2::SDL2) target_link_libraries(audio_core PRIVATE SDL3::SDL3)
target_compile_definitions(audio_core PRIVATE HAVE_SDL2) target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
endif() endif()
if (ANDROID) if (ANDROID)

View File

@ -6,7 +6,7 @@
#include <SDL.h> #include <SDL.h>
#include "audio_core/common/common.h" #include "audio_core/common/common.h"
#include "audio_core/sink/sdl2_sink.h" #include "audio_core/sink/sdl3_sink.h"
#include "audio_core/sink/sink_stream.h" #include "audio_core/sink/sink_stream.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
@ -35,6 +35,29 @@ public:
system_channels = system_channels_; system_channels = system_channels_;
device_channels = device_channels_; device_channels = device_channels_;
SDL_AudioSpec spec{.format = SDL_AUDIO_S16,
.channels = static_cast<u8>(device_channels),
.freq = TargetSampleRate};
std::string device_name{output_device};
SDL_AudioDeviceID devid = SDL_AUDIO_DEVICE_DEFAULT_OUTPUT;
if (type == StreamType::In) {
device_name = input_device;
devid = SDL_AUDIO_DEVICE_DEFAULT_OUTPUT;
}
device = SDL_OpenAudioDevice(devid, &spec);
if (device == 0) {
LOG_CRITICAL(Audio_Sink, "Error opening SDL audio device: {}", SDL_GetError());
return;
}
LOG_INFO(Service_Audio, "Opening SDL stream for: {}", SDL_GetAudioDeviceName(device));
stream = SDL_OpenAudioDeviceStream(device, &spec, &SDLSinkStream::DataCallback, this);
/*
SDL_AudioSpec spec; SDL_AudioSpec spec;
spec.freq = TargetSampleRate; spec.freq = TargetSampleRate;
spec.channels = static_cast<u8>(device_channels); spec.channels = static_cast<u8>(device_channels);
@ -66,6 +89,7 @@ public:
"Opening SDL stream {} with: rate {} channels {} (system channels {}) " "Opening SDL stream {} with: rate {} channels {} (system channels {}) "
" samples {}", " samples {}",
device, obtained.freq, obtained.channels, system_channels, obtained.samples); device, obtained.freq, obtained.channels, system_channels, obtained.samples);
*/
} }
/** /**
@ -85,7 +109,7 @@ public:
} }
Stop(); Stop();
SDL_ClearQueuedAudio(device); SDL_ClearAudioStream(stream);
SDL_CloseAudioDevice(device); SDL_CloseAudioDevice(device);
} }
@ -101,7 +125,7 @@ public:
} }
paused = false; paused = false;
SDL_PauseAudioDevice(device, 0); SDL_ResumeAudioDevice(device);
} }
/** /**
@ -112,7 +136,7 @@ public:
return; return;
} }
SignalPause(); SignalPause();
SDL_PauseAudioDevice(device, 1); SDL_PauseAudioDevice(device);
} }
private: private:
@ -124,7 +148,8 @@ private:
* @param stream - Buffer of samples to be filled or read. * @param stream - Buffer of samples to be filled or read.
* @param len - Length of the stream in bytes. * @param len - Length of the stream in bytes.
*/ */
static void DataCallback(void* userdata, Uint8* stream, int len) { static void DataCallback(void* userdata, SDL_AudioStream* stream, int additional_amount,
int total_amount) {
auto* impl = static_cast<SDLSinkStream*>(userdata); auto* impl = static_cast<SDLSinkStream*>(userdata);
if (!impl) { if (!impl) {
@ -133,7 +158,7 @@ private:
const std::size_t num_channels = impl->GetDeviceChannels(); const std::size_t num_channels = impl->GetDeviceChannels();
const std::size_t frame_size = num_channels; const std::size_t frame_size = num_channels;
const std::size_t num_frames{len / num_channels / sizeof(s16)}; const std::size_t num_frames{total_amount / num_channels / sizeof(s16)};
if (impl->type == StreamType::In) { if (impl->type == StreamType::In) {
std::span<const s16> input_buffer{reinterpret_cast<const s16*>(stream), std::span<const s16> input_buffer{reinterpret_cast<const s16*>(stream),
@ -147,6 +172,7 @@ private:
/// SDL device id of the opened input/output device /// SDL device id of the opened input/output device
SDL_AudioDeviceID device{}; SDL_AudioDeviceID device{};
SDL_AudioStream* stream = nullptr;
}; };
SDLSink::SDLSink(std::string_view target_device_name) { SDLSink::SDLSink(std::string_view target_device_name) {
@ -220,18 +246,34 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
} }
} }
const int device_count = SDL_GetNumAudioDevices(capture); switch (capture) {
for (int i = 0; i < device_count; ++i) { case false: {
if (const char* name = SDL_GetAudioDeviceName(i, capture)) { int count = 0;
device_list.emplace_back(name); SDL_GetAudioOutputDevices(&count);
for (int i = 0; i < count; ++i) {
if (const char* name = SDL_GetAudioDeviceName(i)) {
device_list.emplace_back(name);
}
} }
break;
}
case true: {
int count = 0;
SDL_GetAudioCaptureDevices(&count);
for (int i = 0; i < count; ++i) {
if (const char* name = SDL_GetAudioDeviceName(i)) {
device_list.emplace_back(name);
}
}
break;
}
} }
return device_list; return device_list;
} }
bool IsSDLSuitable() { bool IsSDLSuitable() {
#if !defined(HAVE_SDL2) #if !defined(HAVE_SDL3)
return false; return false;
#else #else
// Check SDL can init // Check SDL can init
@ -246,6 +288,7 @@ bool IsSDLSuitable() {
// We can set any latency frequency we want with SDL, so no need to check that. // We can set any latency frequency we want with SDL, so no need to check that.
// Check we can open a device with standard parameters // Check we can open a device with standard parameters
/*
SDL_AudioSpec spec; SDL_AudioSpec spec;
spec.freq = TargetSampleRate; spec.freq = TargetSampleRate;
spec.channels = 2u; spec.channels = 2u;
@ -256,6 +299,10 @@ bool IsSDLSuitable() {
SDL_AudioSpec obtained; SDL_AudioSpec obtained;
auto device = SDL_OpenAudioDevice(nullptr, false, &spec, &obtained, false); auto device = SDL_OpenAudioDevice(nullptr, false, &spec, &obtained, false);
*/
SDL_AudioSpec spec{.format = SDL_AUDIO_S16, .channels = 2, .freq = TargetSampleRate};
const auto device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec);
if (device == 0) { if (device == 0) {
LOG_ERROR(Audio_Sink, "SDL failed to open a device, it is not suitable. Error: {}", LOG_ERROR(Audio_Sink, "SDL failed to open a device, it is not suitable. Error: {}",

View File

@ -13,8 +13,8 @@
#ifdef HAVE_CUBEB #ifdef HAVE_CUBEB
#include "audio_core/sink/cubeb_sink.h" #include "audio_core/sink/cubeb_sink.h"
#endif #endif
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
#include "audio_core/sink/sdl2_sink.h" #include "audio_core/sink/sdl3_sink.h"
#endif #endif
#include "audio_core/sink/null_sink.h" #include "audio_core/sink/null_sink.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -59,9 +59,9 @@ constexpr SinkDetails sink_details[] = {
&IsCubebSuitable, &IsCubebSuitable,
}, },
#endif #endif
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
SinkDetails{ SinkDetails{
Settings::AudioEngine::Sdl2, Settings::AudioEngine::Sdl3,
[](std::string_view device_id) -> std::unique_ptr<Sink> { [](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<SDLSink>(device_id); return std::make_unique<SDLSink>(device_id);
}, },

View File

@ -80,7 +80,7 @@ struct EnumMetadata {
enum class AudioEngine : u32 { enum class AudioEngine : u32 {
Auto, Auto,
Cubeb, Cubeb,
Sdl2, Sdl3,
Null, Null,
Oboe, Oboe,
}; };
@ -89,7 +89,7 @@ template <>
inline std::vector<std::pair<std::string, AudioEngine>> inline std::vector<std::pair<std::string, AudioEngine>>
EnumMetadata<AudioEngine>::Canonicalizations() { EnumMetadata<AudioEngine>::Canonicalizations() {
return { return {
{"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2}, {"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl3", AudioEngine::Sdl3},
{"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe}, {"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
}; };
} }

View File

@ -51,7 +51,7 @@ void Config::Initialize(const std::string& config_name) {
void Config::Initialize(const std::optional<std::string> config_path) { void Config::Initialize(const std::optional<std::string> config_path) {
const std::filesystem::path default_sdl_config_path = const std::filesystem::path default_sdl_config_path =
FS::GetSudachiPath(FS::SudachiPath::ConfigDir) / "sdl2-config.ini"; FS::GetSudachiPath(FS::SudachiPath::ConfigDir) / "sdl3-config.ini";
config_loc = config_path.value_or(FS::PathToUTF8String(default_sdl_config_path)); config_loc = config_path.value_or(FS::PathToUTF8String(default_sdl_config_path));
void(FS::CreateParentDir(config_loc)); void(FS::CreateParentDir(config_loc));
SetUpIni(); SetUpIni();

View File

@ -47,7 +47,7 @@ else()
) )
endif() endif()
if (ENABLE_SDL2) if (ENABLE_SDL3)
target_sources(input_common PRIVATE target_sources(input_common PRIVATE
drivers/joycon.cpp drivers/joycon.cpp
drivers/joycon.h drivers/joycon.h
@ -73,8 +73,8 @@ if (ENABLE_SDL2)
helpers/joycon_protocol/rumble.cpp helpers/joycon_protocol/rumble.cpp
helpers/joycon_protocol/rumble.h helpers/joycon_protocol/rumble.h
) )
target_link_libraries(input_common PRIVATE SDL2::SDL2) target_link_libraries(input_common PRIVATE SDL3::SDL3)
target_compile_definitions(input_common PRIVATE HAVE_SDL2) target_compile_definitions(input_common PRIVATE HAVE_SDL3)
endif() endif()
if (ENABLE_LIBUSB) if (ENABLE_LIBUSB)

File diff suppressed because it is too large Load Diff

View File

@ -15,18 +15,14 @@
#include "input_common/input_engine.h" #include "input_common/input_engine.h"
union SDL_Event; union SDL_Event;
using SDL_GameController = struct _SDL_GameController;
using SDL_Joystick = struct _SDL_Joystick;
using SDL_JoystickID = s32;
namespace InputCommon { namespace InputCommon {
class SDLJoystick; class SDLJoystick;
class SDLGamepad;
using ButtonBindings = using ButtonBindings = std::array<std::pair<Settings::NativeButton::Values, SDL_GamepadButton>, 20>;
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 20>; using ZButtonBindings = std::array<std::pair<Settings::NativeButton::Values, SDL_GamepadAxis>, 2>;
using ZButtonBindings =
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
class SDLDriver : public InputEngine { class SDLDriver : public InputEngine {
public: public:
@ -38,11 +34,13 @@ public:
void PumpEvents() const; void PumpEvents() const;
/// Handle SDL_Events for joysticks from SDL_PollEvent /// Handle SDL_Events for gamepads from SDL_PollEvent
void HandleGameControllerEvent(const SDL_Event& event); void HandleGameControllerEvent(const SDL_Event& event);
void HandleGamepadEvent(const SDL_Event& event);
/// Get the nth joystick with the corresponding GUID /// Get the nth joystick with the corresponding GUID
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id); std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
std::shared_ptr<SDLGamepad> GetSDLGamepadBySDLID(SDL_JoystickID sdl_id);
/** /**
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so
@ -50,6 +48,8 @@ public:
*/ */
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const Common::UUID& guid, int port); std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const Common::UUID& guid, int port);
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port); std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
std::shared_ptr<SDLGamepad> GetSDLGamepadByGUID(const Common::UUID& guid, int port);
std::shared_ptr<SDLGamepad> GetSDLGamepadByGUID(const std::string& guid, int port);
std::vector<Common::ParamPackage> GetInputDevices() const override; std::vector<Common::ParamPackage> GetInputDevices() const override;
@ -71,9 +71,12 @@ public:
private: private:
void InitJoystick(int joystick_index); void InitJoystick(int joystick_index);
void CloseJoystick(SDL_Joystick* sdl_joystick); void CloseJoystick(SDL_Joystick* sdl_joystick);
void InitGamepad(int gamepad_index);
void CloseGamepad(SDL_Gamepad* sdl_gamepad);
/// Needs to be called before SDL_QuitSubSystem. /// Needs to be called before SDL_QuitSubSystem.
void CloseJoysticks(); void CloseJoysticks();
void CloseGamepads();
/// Takes all vibrations from the queue and sends the command to the controller /// Takes all vibrations from the queue and sends the command to the controller
void SendVibrations(); void SendVibrations();
@ -88,8 +91,8 @@ private:
Common::ParamPackage BuildMotionParam(int port, const Common::UUID& guid) const; Common::ParamPackage BuildMotionParam(int port, const Common::UUID& guid) const;
Common::ParamPackage BuildParamPackageForBinding( Common::ParamPackage BuildParamPackageForBinding(int port, const Common::UUID& guid,
int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const; const SDL_GamepadBinding& binding) const;
Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x, Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
int axis_y, float offset_x, int axis_y, float offset_x,
@ -97,17 +100,25 @@ private:
/// Returns the default button bindings list /// Returns the default button bindings list
ButtonBindings GetDefaultButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const; ButtonBindings GetDefaultButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const;
ButtonBindings GetDefaultButtonBinding(const std::shared_ptr<SDLGamepad>& gamepad) const;
/// Returns the button mappings from a single controller /// Returns the button mappings from a single controller
ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
const ButtonBindings& switch_to_sdl_button, const ButtonBindings& switch_to_sdl_button,
const ZButtonBindings& switch_to_sdl_axis) const; const ZButtonBindings& switch_to_sdl_axis) const;
ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLGamepad>& gamepad,
const ButtonBindings& switch_to_sdl_button,
const ZButtonBindings& switch_to_sdl_axis) const;
/// Returns the button mappings from two different controllers /// Returns the button mappings from two different controllers
ButtonMapping GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, ButtonMapping GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
const std::shared_ptr<SDLJoystick>& joystick2, const std::shared_ptr<SDLJoystick>& joystick2,
const ButtonBindings& switch_to_sdl_button, const ButtonBindings& switch_to_sdl_button,
const ZButtonBindings& switch_to_sdl_axis) const; const ZButtonBindings& switch_to_sdl_axis) const;
ButtonMapping GetDualControllerMapping(const std::shared_ptr<SDLGamepad>& gamepad,
const std::shared_ptr<SDLGamepad>& gamepad2,
const ButtonBindings& switch_to_sdl_button,
const ZButtonBindings& switch_to_sdl_axis) const;
/// Returns true if the button is on the left joycon /// Returns true if the button is on the left joycon
bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const; bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const;
@ -118,6 +129,8 @@ private:
/// Map of GUID of a list of corresponding virtual Joysticks /// Map of GUID of a list of corresponding virtual Joysticks
std::unordered_map<Common::UUID, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; std::unordered_map<Common::UUID, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
std::mutex joystick_map_mutex; std::mutex joystick_map_mutex;
std::unordered_map<Common::UUID, std::vector<std::shared_ptr<SDLGamepad>>> gamepad_map;
std::mutex gamepad_map_mutex;
bool start_thread = false; bool start_thread = false;
std::atomic<bool> initialized = false; std::atomic<bool> initialized = false;

View File

@ -22,7 +22,7 @@
#ifdef HAVE_LIBUSB #ifdef HAVE_LIBUSB
#include "input_common/drivers/gc_adapter.h" #include "input_common/drivers/gc_adapter.h"
#endif #endif
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
#include "input_common/drivers/joycon.h" #include "input_common/drivers/joycon.h"
#include "input_common/drivers/sdl_driver.h" #include "input_common/drivers/sdl_driver.h"
#endif #endif
@ -87,7 +87,7 @@ struct InputSubsystem::Impl {
#endif #endif
RegisterEngine("virtual_amiibo", virtual_amiibo); RegisterEngine("virtual_amiibo", virtual_amiibo);
RegisterEngine("virtual_gamepad", virtual_gamepad); RegisterEngine("virtual_gamepad", virtual_gamepad);
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
RegisterEngine("sdl", sdl); RegisterEngine("sdl", sdl);
RegisterEngine("joycon", joycon); RegisterEngine("joycon", joycon);
#endif #endif
@ -121,7 +121,7 @@ struct InputSubsystem::Impl {
#endif #endif
UnregisterEngine(virtual_amiibo); UnregisterEngine(virtual_amiibo);
UnregisterEngine(virtual_gamepad); UnregisterEngine(virtual_gamepad);
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
UnregisterEngine(sdl); UnregisterEngine(sdl);
UnregisterEngine(joycon); UnregisterEngine(joycon);
#endif #endif
@ -151,7 +151,7 @@ struct InputSubsystem::Impl {
#endif #endif
auto udp_devices = udp_client->GetInputDevices(); auto udp_devices = udp_client->GetInputDevices();
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
auto joycon_devices = joycon->GetInputDevices(); auto joycon_devices = joycon->GetInputDevices();
devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end()); devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end());
auto sdl_devices = sdl->GetInputDevices(); auto sdl_devices = sdl->GetInputDevices();
@ -186,7 +186,7 @@ struct InputSubsystem::Impl {
if (engine == udp_client->GetEngineName()) { if (engine == udp_client->GetEngineName()) {
return udp_client; return udp_client;
} }
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
if (engine == sdl->GetEngineName()) { if (engine == sdl->GetEngineName()) {
return sdl; return sdl;
} }
@ -277,7 +277,7 @@ struct InputSubsystem::Impl {
if (engine == virtual_gamepad->GetEngineName()) { if (engine == virtual_gamepad->GetEngineName()) {
return true; return true;
} }
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
if (engine == sdl->GetEngineName()) { if (engine == sdl->GetEngineName()) {
return true; return true;
} }
@ -298,7 +298,7 @@ struct InputSubsystem::Impl {
gcadapter->BeginConfiguration(); gcadapter->BeginConfiguration();
#endif #endif
udp_client->BeginConfiguration(); udp_client->BeginConfiguration();
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
sdl->BeginConfiguration(); sdl->BeginConfiguration();
joycon->BeginConfiguration(); joycon->BeginConfiguration();
#endif #endif
@ -314,7 +314,7 @@ struct InputSubsystem::Impl {
gcadapter->EndConfiguration(); gcadapter->EndConfiguration();
#endif #endif
udp_client->EndConfiguration(); udp_client->EndConfiguration();
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
sdl->EndConfiguration(); sdl->EndConfiguration();
joycon->EndConfiguration(); joycon->EndConfiguration();
#endif #endif
@ -322,7 +322,7 @@ struct InputSubsystem::Impl {
void PumpEvents() const { void PumpEvents() const {
update_engine->PumpEvents(); update_engine->PumpEvents();
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
sdl->PumpEvents(); sdl->PumpEvents();
#endif #endif
} }
@ -347,7 +347,7 @@ struct InputSubsystem::Impl {
std::shared_ptr<GCAdapter> gcadapter; std::shared_ptr<GCAdapter> gcadapter;
#endif #endif
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
std::shared_ptr<SDLDriver> sdl; std::shared_ptr<SDLDriver> sdl;
std::shared_ptr<Joycons> joycon; std::shared_ptr<Joycons> joycon;
#endif #endif

View File

@ -453,9 +453,9 @@ if (SUDACHI_USE_BUNDLED_QT AND QT_VERSION VERSION_LESS 6)
copy_sudachi_Qt5_deps(sudachi) copy_sudachi_Qt5_deps(sudachi)
endif() endif()
if (ENABLE_SDL2) if (ENABLE_SDL3)
target_link_libraries(sudachi PRIVATE SDL2::SDL2) target_link_libraries(sudachi PRIVATE SDL3::SDL3)
target_compile_definitions(sudachi PRIVATE HAVE_SDL2) target_compile_definitions(sudachi PRIVATE HAVE_SDL3)
endif() endif()
if (MSVC) if (MSVC)

View File

@ -87,7 +87,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include <QUrl> #include <QUrl>
#include <QtConcurrent/QtConcurrent> #include <QtConcurrent/QtConcurrent>
#ifdef HAVE_SDL2 #ifdef HAVE_SDL3
#include <SDL.h> // For SDL ScreenSaver functions #include <SDL.h> // For SDL ScreenSaver functions
#endif #endif
@ -468,7 +468,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
VkDeviceInfo::PopulateRecords(vk_device_records, this->window()->windowHandle()); VkDeviceInfo::PopulateRecords(vk_device_records, this->window()->windowHandle());
} }
#if defined(HAVE_SDL2) && !defined(_WIN32) #if defined(HAVE_SDL3) && !defined(_WIN32)
SDL_InitSubSystem(SDL_INIT_VIDEO); SDL_InitSubSystem(SDL_INIT_VIDEO);
// Set a screensaver inhibition reason string. Currently passed to DBus by SDL and visible to // Set a screensaver inhibition reason string. Currently passed to DBus by SDL and visible to
@ -1760,7 +1760,7 @@ void GMainWindow::OnSigInterruptNotifierActivated() {
void GMainWindow::PreventOSSleep() { void GMainWindow::PreventOSSleep() {
#ifdef _WIN32 #ifdef _WIN32
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
#elif defined(HAVE_SDL2) #elif defined(HAVE_SDL3)
SDL_DisableScreenSaver(); SDL_DisableScreenSaver();
#endif #endif
} }
@ -1768,7 +1768,7 @@ void GMainWindow::PreventOSSleep() {
void GMainWindow::AllowOSSleep() { void GMainWindow::AllowOSSleep() {
#ifdef _WIN32 #ifdef _WIN32
SetThreadExecutionState(ES_CONTINUOUS); SetThreadExecutionState(ES_CONTINUOUS);
#elif defined(HAVE_SDL2) #elif defined(HAVE_SDL3)
SDL_EnableScreenSaver(); SDL_EnableScreenSaver();
#endif #endif
} }

View File

@ -13,14 +13,14 @@ function(create_resource file output filename)
endfunction() endfunction()
add_executable(sudachi-cmd add_executable(sudachi-cmd
emu_window/emu_window_sdl2.cpp emu_window/emu_window_sdl3.cpp
emu_window/emu_window_sdl2.h emu_window/emu_window_sdl3.h
emu_window/emu_window_sdl2_gl.cpp emu_window/emu_window_sdl3_gl.cpp
emu_window/emu_window_sdl2_gl.h emu_window/emu_window_sdl3_gl.h
emu_window/emu_window_sdl2_null.cpp emu_window/emu_window_sdl3_null.cpp
emu_window/emu_window_sdl2_null.h emu_window/emu_window_sdl3_null.h
emu_window/emu_window_sdl2_vk.cpp emu_window/emu_window_sdl3_vk.cpp
emu_window/emu_window_sdl2_vk.h emu_window/emu_window_sdl3_vk.h
precompiled_headers.h precompiled_headers.h
sdl_config.cpp sdl_config.cpp
sdl_config.h sdl_config.h
@ -38,7 +38,7 @@ target_link_libraries(sudachi-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads
create_resource("../../dist/sudachi.bmp" "sudachi_cmd/sudachi_icon.h" "sudachi_icon") create_resource("../../dist/sudachi.bmp" "sudachi_cmd/sudachi_icon.h" "sudachi_icon")
target_include_directories(sudachi-cmd PRIVATE ${RESOURCES_DIR}) target_include_directories(sudachi-cmd PRIVATE ${RESOURCES_DIR})
target_link_libraries(sudachi-cmd PRIVATE SDL2::SDL2 Vulkan::Headers) target_link_libraries(sudachi-cmd PRIVATE SDL3::SDL3 Vulkan::Headers)
if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)
install(TARGETS sudachi-cmd) install(TARGETS sudachi-cmd)

View File

@ -19,27 +19,27 @@
namespace FS = Common::FS; namespace FS = Common::FS;
const std::filesystem::path default_config_path = const std::filesystem::path default_config_path =
FS::GetSudachiPath(FS::SudachiPath::ConfigDir) / "sdl2-config.ini"; FS::GetSudachiPath(FS::SudachiPath::ConfigDir) / "sdl3-config.ini";
Config::Config(std::optional<std::filesystem::path> config_path) Config::Config(std::optional<std::filesystem::path> config_path)
: sdl2_config_loc{config_path.value_or(default_config_path)}, : sdl3_config_loc{config_path.value_or(default_config_path)},
sdl2_config{std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc))} { sdl3_config{std::make_unique<INIReader>(FS::PathToUTF8String(sdl3_config_loc))} {
Reload(); Reload();
} }
Config::~Config() = default; Config::~Config() = default;
bool Config::LoadINI(const std::string& default_contents, bool retry) { bool Config::LoadINI(const std::string& default_contents, bool retry) {
const auto config_loc_str = FS::PathToUTF8String(sdl2_config_loc); const auto config_loc_str = FS::PathToUTF8String(sdl3_config_loc);
if (sdl2_config->ParseError() < 0) { if (sdl3_config->ParseError() < 0) {
if (retry) { if (retry) {
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...",
config_loc_str); config_loc_str);
void(FS::CreateParentDir(sdl2_config_loc)); void(FS::CreateParentDir(sdl3_config_loc));
void(FS::WriteStringToFile(sdl2_config_loc, FS::FileType::TextFile, default_contents)); void(FS::WriteStringToFile(sdl3_config_loc, FS::FileType::TextFile, default_contents));
sdl2_config = std::make_unique<INIReader>(config_loc_str); sdl3_config = std::make_unique<INIReader>(config_loc_str);
return LoadINI(default_contents, false); return LoadINI(default_contents, false);
} }
@ -80,7 +80,7 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs>
template <> template <>
void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) { void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {
std::string setting_value = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); std::string setting_value = sdl3_config->Get(group, setting.GetLabel(), setting.GetDefault());
if (setting_value.empty()) { if (setting_value.empty()) {
setting_value = setting.GetDefault(); setting_value = setting.GetDefault();
} }
@ -89,12 +89,12 @@ void Config::ReadSetting(const std::string& group, Settings::Setting<std::string
template <> template <>
void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) { void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) {
setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); setting = sdl3_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
} }
template <typename Type, bool ranged> template <typename Type, bool ranged>
void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) { void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(), setting = static_cast<Type>(sdl3_config->GetInteger(group, setting.GetLabel(),
static_cast<long>(setting.GetDefault()))); static_cast<long>(setting.GetDefault())));
} }
@ -109,7 +109,7 @@ void Config::ReadCategory(Settings::Category category) {
} }
}(); }();
std::string setting_value = std::string setting_value =
sdl2_config->Get(category_name, setting->GetLabel(), setting->DefaultToString()); sdl3_config->Get(category_name, setting->GetLabel(), setting->DefaultToString());
setting->LoadString(setting_value); setting->LoadString(setting_value);
} }
} }
@ -125,7 +125,7 @@ void Config::ReadValues() {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
player.buttons[i] = player.buttons[i] =
sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param); sdl3_config->Get(group, Settings::NativeButton::mapping[i], default_param);
if (player.buttons[i].empty()) { if (player.buttons[i].empty()) {
player.buttons[i] = default_param; player.buttons[i] = default_param;
} }
@ -136,7 +136,7 @@ void Config::ReadValues() {
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_analogs[i][4], 0.5f); default_analogs[i][3], default_analogs[i][4], 0.5f);
player.analogs[i] = player.analogs[i] =
sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param); sdl3_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
if (player.analogs[i].empty()) { if (player.analogs[i].empty()) {
player.analogs[i] = default_param; player.analogs[i] = default_param;
} }
@ -148,18 +148,18 @@ void Config::ReadValues() {
auto& player_motions = player.motions[i]; auto& player_motions = player.motions[i];
player_motions = player_motions =
sdl2_config->Get(group, Settings::NativeMotion::mapping[i], default_param); sdl3_config->Get(group, Settings::NativeMotion::mapping[i], default_param);
if (player_motions.empty()) { if (player_motions.empty()) {
player_motions = default_param; player_motions = default_param;
} }
} }
player.connected = sdl2_config->GetBoolean(group, "connected", false); player.connected = sdl3_config->GetBoolean(group, "connected", false);
} }
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
Settings::values.debug_pad_buttons[i] = sdl2_config->Get( Settings::values.debug_pad_buttons[i] = sdl3_config->Get(
"ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i], "ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i],
default_param); default_param);
if (Settings::values.debug_pad_buttons[i].empty()) if (Settings::values.debug_pad_buttons[i].empty())
@ -170,7 +170,7 @@ void Config::ReadValues() {
std::string default_param = InputCommon::GenerateAnalogParamFromKeys( std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_analogs[i][4], 0.5f); default_analogs[i][3], default_analogs[i][4], 0.5f);
Settings::values.debug_pad_analogs[i] = sdl2_config->Get( Settings::values.debug_pad_analogs[i] = sdl3_config->Get(
"ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i], "ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i],
default_param); default_param);
if (Settings::values.debug_pad_analogs[i].empty()) if (Settings::values.debug_pad_analogs[i].empty())
@ -178,24 +178,24 @@ void Config::ReadValues() {
} }
Settings::values.touchscreen.enabled = Settings::values.touchscreen.enabled =
sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); sdl3_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
Settings::values.touchscreen.rotation_angle = Settings::values.touchscreen.rotation_angle =
sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0); sdl3_config->GetInteger("ControlsGeneral", "touch_angle", 0);
Settings::values.touchscreen.diameter_x = Settings::values.touchscreen.diameter_x =
sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15); sdl3_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
Settings::values.touchscreen.diameter_y = Settings::values.touchscreen.diameter_y =
sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15); sdl3_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
int num_touch_from_button_maps = int num_touch_from_button_maps =
sdl2_config->GetInteger("ControlsGeneral", "touch_from_button_map", 0); sdl3_config->GetInteger("ControlsGeneral", "touch_from_button_map", 0);
if (num_touch_from_button_maps > 0) { if (num_touch_from_button_maps > 0) {
for (int i = 0; i < num_touch_from_button_maps; ++i) { for (int i = 0; i < num_touch_from_button_maps; ++i) {
Settings::TouchFromButtonMap map; Settings::TouchFromButtonMap map;
map.name = sdl2_config->Get("ControlsGeneral", map.name = sdl3_config->Get("ControlsGeneral",
std::string("touch_from_button_maps_") + std::to_string(i) + std::string("touch_from_button_maps_") + std::to_string(i) +
std::string("_name"), std::string("_name"),
"default"); "default");
const int num_touch_maps = sdl2_config->GetInteger( const int num_touch_maps = sdl3_config->GetInteger(
"ControlsGeneral", "ControlsGeneral",
std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"), std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"),
0); 0);
@ -203,7 +203,7 @@ void Config::ReadValues() {
for (int j = 0; j < num_touch_maps; ++j) { for (int j = 0; j < num_touch_maps; ++j) {
std::string touch_mapping = std::string touch_mapping =
sdl2_config->Get("ControlsGeneral", sdl3_config->Get("ControlsGeneral",
std::string("touch_from_button_maps_") + std::to_string(i) + std::string("touch_from_button_maps_") + std::to_string(i) +
std::string("_bind_") + std::to_string(j), std::string("_bind_") + std::to_string(j),
""); "");
@ -239,28 +239,28 @@ void Config::ReadValues() {
// Data Storage // Data Storage
FS::SetSudachiPath(FS::SudachiPath::NANDDir, FS::SetSudachiPath(FS::SudachiPath::NANDDir,
sdl2_config->Get("Data Storage", "nand_directory", sdl3_config->Get("Data Storage", "nand_directory",
FS::GetSudachiPathString(FS::SudachiPath::NANDDir))); FS::GetSudachiPathString(FS::SudachiPath::NANDDir)));
FS::SetSudachiPath(FS::SudachiPath::SDMCDir, FS::SetSudachiPath(FS::SudachiPath::SDMCDir,
sdl2_config->Get("Data Storage", "sdmc_directory", sdl3_config->Get("Data Storage", "sdmc_directory",
FS::GetSudachiPathString(FS::SudachiPath::SDMCDir))); FS::GetSudachiPathString(FS::SudachiPath::SDMCDir)));
FS::SetSudachiPath(FS::SudachiPath::LoadDir, FS::SetSudachiPath(FS::SudachiPath::LoadDir,
sdl2_config->Get("Data Storage", "load_directory", sdl3_config->Get("Data Storage", "load_directory",
FS::GetSudachiPathString(FS::SudachiPath::LoadDir))); FS::GetSudachiPathString(FS::SudachiPath::LoadDir)));
FS::SetSudachiPath(FS::SudachiPath::DumpDir, FS::SetSudachiPath(FS::SudachiPath::DumpDir,
sdl2_config->Get("Data Storage", "dump_directory", sdl3_config->Get("Data Storage", "dump_directory",
FS::GetSudachiPathString(FS::SudachiPath::DumpDir))); FS::GetSudachiPathString(FS::SudachiPath::DumpDir)));
// Debugging // Debugging
Settings::values.record_frame_times = Settings::values.record_frame_times =
sdl2_config->GetBoolean("Debugging", "record_frame_times", false); sdl3_config->GetBoolean("Debugging", "record_frame_times", false);
const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); const auto title_list = sdl3_config->Get("AddOns", "title_ids", "");
std::stringstream ss(title_list); std::stringstream ss(title_list);
std::string line; std::string line;
while (std::getline(ss, line, '|')) { while (std::getline(ss, line, '|')) {
const auto title_id = std::strtoul(line.c_str(), nullptr, 16); const auto title_id = std::strtoul(line.c_str(), nullptr, 16);
const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, ""); const auto disabled_list = sdl3_config->Get("AddOns", "disabled_" + line, "");
std::stringstream inner_ss(disabled_list); std::stringstream inner_ss(disabled_list);
std::string inner_line; std::string inner_line;
@ -274,6 +274,6 @@ void Config::ReadValues() {
} }
void Config::Reload() { void Config::Reload() {
LoadINI(DefaultINI::sdl2_config_file); LoadINI(DefaultINI::sdl3_config_file);
ReadValues(); ReadValues();
} }

View File

@ -13,8 +13,8 @@
class INIReader; class INIReader;
class Config { class Config {
std::filesystem::path sdl2_config_loc; std::filesystem::path sdl3_config_loc;
std::unique_ptr<INIReader> sdl2_config; std::unique_ptr<INIReader> sdl3_config;
bool LoadINI(const std::string& default_contents = "", bool retry = true); bool LoadINI(const std::string& default_contents = "", bool retry = true);
void ReadValues(); void ReadValues();
@ -27,7 +27,7 @@ public:
private: private:
/** /**
* Applies a value read from the sdl2_config to a Setting. * Applies a value read from the sdl3_config to a Setting.
* *
* @param group The name of the INI group * @param group The name of the INI group
* @param setting The sudachi setting to modify * @param setting The sudachi setting to modify

View File

@ -5,7 +5,7 @@
namespace DefaultINI { namespace DefaultINI {
const char* sdl2_config_file = const char* sdl3_config_file =
R"( R"(
[ControlsP0] [ControlsP0]
# The input devices and parameters for each Switch native input # The input devices and parameters for each Switch native input
@ -424,7 +424,7 @@ bg_green =
# Which audio output engine to use. # Which audio output engine to use.
# auto (default): Auto-select # auto (default): Auto-select
# cubeb: Cubeb audio engine (if available) # cubeb: Cubeb audio engine (if available)
# sdl2: SDL2 audio engine (if available) # sdl3: SDL3 audio engine (if available)
# null: No audio output # null: No audio output
output_engine = output_engine =

View File

@ -1,93 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2018 sudachi Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib>
#include <memory>
#include <string>
#include <fmt/format.h>
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "sudachi_cmd/emu_window/emu_window_sdl2_vk.h"
#include <SDL.h>
#include <SDL_syswm.h>
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_,
Core::System& system_, bool fullscreen)
: EmuWindow_SDL2{input_subsystem_, system_} {
const std::string window_title = fmt::format("sudachi {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
render_window =
SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_SysWMinfo wm;
SDL_VERSION(&wm.version);
if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
LOG_CRITICAL(Frontend, "Failed to get information from the window manager: {}",
SDL_GetError());
std::exit(EXIT_FAILURE);
}
SetWindowIcon();
if (fullscreen) {
Fullscreen();
ShowCursor(false);
}
switch (wm.subsystem) {
#ifdef SDL_VIDEO_DRIVER_WINDOWS
case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
window_info.type = Core::Frontend::WindowSystemType::Windows;
window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window);
break;
#endif
#ifdef SDL_VIDEO_DRIVER_X11
case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
window_info.type = Core::Frontend::WindowSystemType::X11;
window_info.display_connection = wm.info.x11.display;
window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window);
break;
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND
case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
window_info.type = Core::Frontend::WindowSystemType::Wayland;
window_info.display_connection = wm.info.wl.display;
window_info.render_surface = wm.info.wl.surface;
break;
#endif
#ifdef SDL_VIDEO_DRIVER_COCOA
case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA:
window_info.type = Core::Frontend::WindowSystemType::Cocoa;
window_info.render_surface = SDL_Metal_CreateView(render_window);
break;
#endif
#ifdef SDL_VIDEO_DRIVER_ANDROID
case SDL_SYSWM_TYPE::SDL_SYSWM_ANDROID:
window_info.type = Core::Frontend::WindowSystemType::Android;
window_info.render_surface = reinterpret_cast<void*>(wm.info.android.window);
break;
#endif
default:
LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem);
std::exit(EXIT_FAILURE);
break;
}
OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents();
LOG_INFO(Frontend, "sudachi Version: {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
}
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
return std::make_unique<DummyContext>();
}

View File

@ -1,7 +1,9 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#define SDL_MAIN_HANDLED
#include <SDL.h> #include <SDL.h>
#include <SDL_main.h>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
@ -13,26 +15,27 @@
#include "input_common/drivers/mouse.h" #include "input_common/drivers/mouse.h"
#include "input_common/drivers/touch_screen.h" #include "input_common/drivers/touch_screen.h"
#include "input_common/main.h" #include "input_common/main.h"
#include "sudachi_cmd/emu_window/emu_window_sdl2.h" #include "sudachi_cmd/emu_window/emu_window_sdl3.h"
#include "sudachi_cmd/sudachi_icon.h" #include "sudachi_cmd/sudachi_icon.h"
EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_) EmuWindow_SDL3::EmuWindow_SDL3(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
: input_subsystem{input_subsystem_}, system{system_} { : input_subsystem{input_subsystem_}, system{system_} {
input_subsystem->Initialize(); input_subsystem->Initialize();
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD) < 0) {
LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}, Exiting...", SDL_GetError()); LOG_CRITICAL(Frontend, "Failed to initialize SDL3: {}, Exiting...", SDL_GetError());
exit(1); exit(1);
} }
SDL_SetMainReady(); SDL_SetMainReady();
} }
EmuWindow_SDL2::~EmuWindow_SDL2() { EmuWindow_SDL3::~EmuWindow_SDL3() {
system.HIDCore().UnloadInputDevices(); system.HIDCore().UnloadInputDevices();
input_subsystem->Shutdown(); input_subsystem->Shutdown();
SDL_Quit(); SDL_Quit();
} }
InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const { InputCommon::MouseButton EmuWindow_SDL3::SDLButtonToMouseButton(u32 button) const {
switch (button) { switch (button) {
case SDL_BUTTON_LEFT: case SDL_BUTTON_LEFT:
return InputCommon::MouseButton::Left; return InputCommon::MouseButton::Left;
@ -49,7 +52,7 @@ InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) cons
} }
} }
std::pair<float, float> EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 touch_y) const { std::pair<float, float> EmuWindow_SDL3::MouseToTouchPos(s32 touch_x, s32 touch_y) const {
int w, h; int w, h;
SDL_GetWindowSize(render_window, &w, &h); SDL_GetWindowSize(render_window, &w, &h);
const float fx = static_cast<float>(touch_x) / w; const float fx = static_cast<float>(touch_x) / w;
@ -58,7 +61,7 @@ std::pair<float, float> EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 touch_y
return {std::clamp<float>(fx, 0.0f, 1.0f), std::clamp<float>(fy, 0.0f, 1.0f)}; return {std::clamp<float>(fx, 0.0f, 1.0f), std::clamp<float>(fy, 0.0f, 1.0f)};
} }
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { void EmuWindow_SDL3::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
const auto mouse_button = SDLButtonToMouseButton(button); const auto mouse_button = SDLButtonToMouseButton(button);
if (state == SDL_PRESSED) { if (state == SDL_PRESSED) {
const auto [touch_x, touch_y] = MouseToTouchPos(x, y); const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
@ -70,26 +73,26 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
} }
} }
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { void EmuWindow_SDL3::OnMouseMotion(s32 x, s32 y) {
const auto [touch_x, touch_y] = MouseToTouchPos(x, y); const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
input_subsystem->GetMouse()->Move(x, y, 0, 0); input_subsystem->GetMouse()->Move(x, y, 0, 0);
input_subsystem->GetMouse()->MouseMove(touch_x, touch_y); input_subsystem->GetMouse()->MouseMove(touch_x, touch_y);
input_subsystem->GetMouse()->TouchMove(touch_x, touch_y); input_subsystem->GetMouse()->TouchMove(touch_x, touch_y);
} }
void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) { void EmuWindow_SDL3::OnFingerDown(float x, float y, std::size_t id) {
input_subsystem->GetTouchScreen()->TouchPressed(x, y, id); input_subsystem->GetTouchScreen()->TouchPressed(x, y, id);
} }
void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) { void EmuWindow_SDL3::OnFingerMotion(float x, float y, std::size_t id) {
input_subsystem->GetTouchScreen()->TouchMoved(x, y, id); input_subsystem->GetTouchScreen()->TouchMoved(x, y, id);
} }
void EmuWindow_SDL2::OnFingerUp() { void EmuWindow_SDL3::OnFingerUp() {
input_subsystem->GetTouchScreen()->ReleaseAllTouch(); input_subsystem->GetTouchScreen()->ReleaseAllTouch();
} }
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { void EmuWindow_SDL3::OnKeyEvent(int key, u8 state) {
if (state == SDL_PRESSED) { if (state == SDL_PRESSED) {
input_subsystem->GetKeyboard()->PressKey(static_cast<std::size_t>(key)); input_subsystem->GetKeyboard()->PressKey(static_cast<std::size_t>(key));
} else if (state == SDL_RELEASED) { } else if (state == SDL_RELEASED) {
@ -97,32 +100,33 @@ void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
} }
} }
bool EmuWindow_SDL2::IsOpen() const { bool EmuWindow_SDL3::IsOpen() const {
return is_open; return is_open;
} }
bool EmuWindow_SDL2::IsShown() const { bool EmuWindow_SDL3::IsShown() const {
return is_shown; return is_shown;
} }
void EmuWindow_SDL2::OnResize() { void EmuWindow_SDL3::OnResize() {
int width, height; int width, height;
SDL_GL_GetDrawableSize(render_window, &width, &height); SDL_GetWindowSizeInPixels(render_window, &width, &height);
UpdateCurrentFramebufferLayout(width, height); UpdateCurrentFramebufferLayout(width, height);
} }
void EmuWindow_SDL2::ShowCursor(bool show_cursor) { void EmuWindow_SDL3::ShowCursor(bool show_cursor) {
SDL_ShowCursor(show_cursor ? SDL_ENABLE : SDL_DISABLE); show_cursor ? SDL_ShowCursor() : SDL_HideCursor();
} }
void EmuWindow_SDL2::Fullscreen() { void EmuWindow_SDL3::Fullscreen() {
SDL_DisplayMode display_mode; const SDL_DisplayMode* display_mode = nullptr;
switch (Settings::values.fullscreen_mode.GetValue()) { switch (Settings::values.fullscreen_mode.GetValue()) {
case Settings::FullscreenMode::Exclusive: case Settings::FullscreenMode::Exclusive:
// Set window size to render size before entering fullscreen -- SDL2 does not resize window // Set window size to render size before entering fullscreen -- SDL3 does not resize window
// to display dimensions automatically in this mode. // to display dimensions automatically in this mode.
if (SDL_GetDesktopDisplayMode(0, &display_mode) == 0) { display_mode = SDL_GetDesktopDisplayMode(0);
SDL_SetWindowSize(render_window, display_mode.w, display_mode.h); if (display_mode == NULL) {
SDL_SetWindowSize(render_window, display_mode->w, display_mode->h);
} else { } else {
LOG_ERROR(Frontend, "SDL_GetDesktopDisplayMode failed: {}", SDL_GetError()); LOG_ERROR(Frontend, "SDL_GetDesktopDisplayMode failed: {}", SDL_GetError());
} }
@ -135,7 +139,7 @@ void EmuWindow_SDL2::Fullscreen() {
LOG_INFO(Frontend, "Attempting to use borderless fullscreen..."); LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
[[fallthrough]]; [[fallthrough]];
case Settings::FullscreenMode::Borderless: case Settings::FullscreenMode::Borderless:
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) { if (SDL_SetWindowFullscreen(render_window, SDL_TRUE) == 0) {
return; return;
} }
@ -150,7 +154,7 @@ void EmuWindow_SDL2::Fullscreen() {
} }
} }
void EmuWindow_SDL2::WaitEvent() { void EmuWindow_SDL3::WaitEvent() {
// Called on main thread // Called on main thread
SDL_Event event; SDL_Event event;
@ -168,59 +172,55 @@ void EmuWindow_SDL2::WaitEvent() {
} }
switch (event.type) { switch (event.type) {
case SDL_WINDOWEVENT: case SDL_EVENT_WINDOW_RESIZED:
switch (event.window.event) { case SDL_EVENT_WINDOW_MAXIMIZED:
case SDL_WINDOWEVENT_SIZE_CHANGED: case SDL_EVENT_WINDOW_RESTORED:
case SDL_WINDOWEVENT_RESIZED: OnResize();
case SDL_WINDOWEVENT_MAXIMIZED:
case SDL_WINDOWEVENT_RESTORED:
OnResize();
break;
case SDL_WINDOWEVENT_MINIMIZED:
case SDL_WINDOWEVENT_EXPOSED:
is_shown = event.window.event == SDL_WINDOWEVENT_EXPOSED;
OnResize();
break;
case SDL_WINDOWEVENT_CLOSE:
is_open = false;
break;
}
break; break;
case SDL_KEYDOWN: case SDL_EVENT_WINDOW_MINIMIZED:
case SDL_KEYUP: case SDL_EVENT_WINDOW_EXPOSED:
is_shown = event.window.type == SDL_EVENT_WINDOW_EXPOSED;
OnResize();
break;
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
is_open = false;
break;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state); OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
break; break;
case SDL_MOUSEMOTION: case SDL_EVENT_MOUSE_MOTION:
// ignore if it came from touch // ignore if it came from touch
if (event.button.which != SDL_TOUCH_MOUSEID) if (event.button.which != SDL_TOUCH_MOUSEID)
OnMouseMotion(event.motion.x, event.motion.y); OnMouseMotion(static_cast<s32>(event.motion.x), static_cast<s32>(event.motion.y));
break; break;
case SDL_MOUSEBUTTONDOWN: case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_MOUSEBUTTONUP: case SDL_EVENT_MOUSE_BUTTON_UP:
// ignore if it came from touch // ignore if it came from touch
if (event.button.which != SDL_TOUCH_MOUSEID) { if (event.button.which != SDL_TOUCH_MOUSEID) {
OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y); OnMouseButton(event.button.button, event.button.state, static_cast<s32>(event.button.x),
static_cast<s32>(event.button.y));
} }
break; break;
case SDL_FINGERDOWN: case SDL_EVENT_FINGER_DOWN:
OnFingerDown(event.tfinger.x, event.tfinger.y, OnFingerDown(event.tfinger.x, event.tfinger.y,
static_cast<std::size_t>(event.tfinger.touchId)); static_cast<std::size_t>(event.tfinger.touchID));
break; break;
case SDL_FINGERMOTION: case SDL_EVENT_FINGER_MOTION:
OnFingerMotion(event.tfinger.x, event.tfinger.y, OnFingerMotion(event.tfinger.x, event.tfinger.y,
static_cast<std::size_t>(event.tfinger.touchId)); static_cast<std::size_t>(event.tfinger.touchID));
break; break;
case SDL_FINGERUP: case SDL_EVENT_FINGER_UP:
OnFingerUp(); OnFingerUp();
break; break;
case SDL_QUIT: case SDL_EVENT_QUIT:
is_open = false; is_open = false;
break; break;
default: default:
break; break;
} }
const u32 current_time = SDL_GetTicks(); const u64 current_time = SDL_GetTicks();
if (current_time > last_time + 2000) { if (current_time > last_time + 2000) {
const auto results = system.GetAndResetPerfStats(); const auto results = system.GetAndResetPerfStats();
const auto title = const auto title =
@ -233,22 +233,23 @@ void EmuWindow_SDL2::WaitEvent() {
} }
// Credits to Samantas5855 and others for this function. // Credits to Samantas5855 and others for this function.
void EmuWindow_SDL2::SetWindowIcon() { void EmuWindow_SDL3::SetWindowIcon() {
SDL_RWops* const sudachi_icon_stream = SDL_RWFromConstMem((void*)sudachi_icon, sudachi_icon_size); SDL_IOStream* const sudachi_icon_stream =
SDL_IOFromConstMem((void*)sudachi_icon, sudachi_icon_size);
if (sudachi_icon_stream == nullptr) { if (sudachi_icon_stream == nullptr) {
LOG_WARNING(Frontend, "Failed to create sudachi icon stream."); LOG_WARNING(Frontend, "Failed to create sudachi icon stream.");
return; return;
} }
SDL_Surface* const window_icon = SDL_LoadBMP_RW(sudachi_icon_stream, 1); SDL_Surface* const window_icon = SDL_LoadBMP_IO(sudachi_icon_stream, 1);
if (window_icon == nullptr) { if (window_icon == nullptr) {
LOG_WARNING(Frontend, "Failed to read BMP from stream."); LOG_WARNING(Frontend, "Failed to read BMP from stream.");
return; return;
} }
// The icon is attached to the window pointer // The icon is attached to the window pointer
SDL_SetWindowIcon(render_window, window_icon); SDL_SetWindowIcon(render_window, window_icon);
SDL_FreeSurface(window_icon); SDL_DestroySurface(window_icon);
} }
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) { void EmuWindow_SDL3::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
} }

View File

@ -19,10 +19,10 @@ class InputSubsystem;
enum class MouseButton; enum class MouseButton;
} // namespace InputCommon } // namespace InputCommon
class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { class EmuWindow_SDL3 : public Core::Frontend::EmuWindow {
public: public:
explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_); explicit EmuWindow_SDL3(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_);
~EmuWindow_SDL2(); ~EmuWindow_SDL3();
/// Whether the window is still open, and a close request hasn't yet been sent /// Whether the window is still open, and a close request hasn't yet been sent
bool IsOpen() const; bool IsOpen() const;
@ -79,11 +79,11 @@ protected:
/// Is the window being shown? /// Is the window being shown?
bool is_shown = true; bool is_shown = true;
/// Internal SDL2 render window /// Internal SDL3 render window
SDL_Window* render_window{}; SDL_Window* render_window{};
/// Keeps track of how often to update the title bar during gameplay /// Keeps track of how often to update the title bar during gameplay
u32 last_time = 0; u64 last_time = 0;
/// Input subsystem to use with this window. /// Input subsystem to use with this window.
InputCommon::InputSubsystem* input_subsystem; InputCommon::InputSubsystem* input_subsystem;

View File

@ -16,8 +16,8 @@
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h" #include "core/core.h"
#include "input_common/main.h" #include "input_common/main.h"
#include "sudachi_cmd/emu_window/emu_window_sdl3_gl.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "sudachi_cmd/emu_window/emu_window_sdl2_gl.h"
class SDLGLContext : public Core::Frontend::GraphicsContext { class SDLGLContext : public Core::Frontend::GraphicsContext {
public: public:
@ -55,7 +55,7 @@ private:
bool is_current = false; bool is_current = false;
}; };
bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { bool EmuWindow_SDL3_GL::SupportsRequiredGLExtensions() {
std::vector<std::string_view> unsupported_ext; std::vector<std::string_view> unsupported_ext;
// Extensions required to support some texture formats. // Extensions required to support some texture formats.
@ -73,9 +73,9 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
return unsupported_ext.empty(); return unsupported_ext.empty();
} }
EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem_, EmuWindow_SDL3_GL::EmuWindow_SDL3_GL(InputCommon::InputSubsystem* input_subsystem_,
Core::System& system_, bool fullscreen) Core::System& system_, bool fullscreen)
: EmuWindow_SDL2{input_subsystem_, system_} { : EmuWindow_SDL3{input_subsystem_, system_} {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
@ -93,14 +93,11 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste
std::string window_title = fmt::format("sudachi {} | {}-{}", Common::g_build_fullname, std::string window_title = fmt::format("sudachi {} | {}-{}", Common::g_build_fullname,
Common::g_scm_branch, Common::g_scm_desc); Common::g_scm_branch, Common::g_scm_desc);
render_window = render_window =
SDL_CreateWindow(window_title.c_str(), SDL_CreateWindow(window_title.c_str(), Layout::ScreenUndocked::Width,
SDL_WINDOWPOS_UNDEFINED, // x position Layout::ScreenUndocked::Height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
SDL_WINDOWPOS_UNDEFINED, // y position
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
if (render_window == nullptr) { if (render_window == nullptr) {
LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError()); LOG_CRITICAL(Frontend, "Failed to create SDL3 window! {}", SDL_GetError());
exit(1); exit(1);
} }
@ -117,15 +114,15 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste
core_context = CreateSharedContext(); core_context = CreateSharedContext();
if (window_context == nullptr) { if (window_context == nullptr) {
LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: {}", SDL_GetError()); LOG_CRITICAL(Frontend, "Failed to create SDL3 GL context: {}", SDL_GetError());
exit(1); exit(1);
} }
if (core_context == nullptr) { if (core_context == nullptr) {
LOG_CRITICAL(Frontend, "Failed to create shared SDL2 GL context: {}", SDL_GetError()); LOG_CRITICAL(Frontend, "Failed to create shared SDL3 GL context: {}", SDL_GetError());
exit(1); exit(1);
} }
if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError()); LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError());
exit(1); exit(1);
} }
@ -138,16 +135,16 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste
OnResize(); OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents(); SDL_PumpEvents();
LOG_INFO(Frontend, "sudachi Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, LOG_INFO(Frontend, "sudachi Version: {} | {}-{}", Common::g_build_fullname,
Common::g_scm_desc); Common::g_scm_branch, Common::g_scm_desc);
Settings::LogSettings(); Settings::LogSettings();
} }
EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { EmuWindow_SDL3_GL::~EmuWindow_SDL3_GL() {
core_context.reset(); core_context.reset();
SDL_GL_DeleteContext(window_context); SDL_GL_DeleteContext(window_context);
} }
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL3_GL::CreateSharedContext() const {
return std::make_unique<SDLGLContext>(render_window); return std::make_unique<SDLGLContext>(render_window);
} }

View File

@ -5,7 +5,7 @@
#include <memory> #include <memory>
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "sudachi_cmd/emu_window/emu_window_sdl2.h" #include "sudachi_cmd/emu_window/emu_window_sdl3.h"
namespace Core { namespace Core {
class System; class System;
@ -15,11 +15,11 @@ namespace InputCommon {
class InputSubsystem; class InputSubsystem;
} }
class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { class EmuWindow_SDL3_GL final : public EmuWindow_SDL3 {
public: public:
explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_, explicit EmuWindow_SDL3_GL(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_,
bool fullscreen); bool fullscreen);
~EmuWindow_SDL2_GL(); ~EmuWindow_SDL3_GL();
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;

View File

@ -9,10 +9,10 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "sudachi_cmd/emu_window/emu_window_sdl2_null.h" #include "sudachi_cmd/emu_window/emu_window_sdl3_null.h"
#include "video_core/renderer_null/renderer_null.h" #include "video_core/renderer_null/renderer_null.h"
#ifdef SUDACHI_USE_EXTERNAL_SDL2 #ifdef SUDACHI_USE_EXTERNAL_SDL3
// Include this before SDL.h to prevent the external from including a dummy // Include this before SDL.h to prevent the external from including a dummy
#define USING_GENERATED_CONFIG_H #define USING_GENERATED_CONFIG_H
#include <SDL_config.h> #include <SDL_config.h>
@ -20,16 +20,14 @@
#include <SDL.h> #include <SDL.h>
EmuWindow_SDL2_Null::EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_, EmuWindow_SDL3_Null::EmuWindow_SDL3_Null(InputCommon::InputSubsystem* input_subsystem_,
Core::System& system_, bool fullscreen) Core::System& system_, bool fullscreen)
: EmuWindow_SDL2{input_subsystem_, system_} { : EmuWindow_SDL3{input_subsystem_, system_} {
const std::string window_title = const std::string window_title =
fmt::format("sudachi {} | {}-{} (Vulkan)", Common::g_build_name, Common::g_scm_branch, fmt::format("sudachi {} | {}-{} (Vulkan)", Common::g_build_name, Common::g_scm_branch,
Common::g_scm_desc); Common::g_scm_desc);
render_window = render_window = SDL_CreateWindow(window_title.c_str(), Layout::ScreenUndocked::Width,
SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Layout::ScreenUndocked::Height, SDL_WINDOW_RESIZABLE);
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
SetWindowIcon(); SetWindowIcon();
@ -45,8 +43,8 @@ EmuWindow_SDL2_Null::EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subs
Common::g_scm_branch, Common::g_scm_desc); Common::g_scm_branch, Common::g_scm_desc);
} }
EmuWindow_SDL2_Null::~EmuWindow_SDL2_Null() = default; EmuWindow_SDL3_Null::~EmuWindow_SDL3_Null() = default;
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Null::CreateSharedContext() const { std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL3_Null::CreateSharedContext() const {
return std::make_unique<DummyContext>(); return std::make_unique<DummyContext>();
} }

View File

@ -6,7 +6,7 @@
#include <memory> #include <memory>
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "sudachi_cmd/emu_window/emu_window_sdl2.h" #include "sudachi_cmd/emu_window/emu_window_sdl3.h"
namespace Core { namespace Core {
class System; class System;
@ -16,11 +16,11 @@ namespace InputCommon {
class InputSubsystem; class InputSubsystem;
} }
class EmuWindow_SDL2_Null final : public EmuWindow_SDL2 { class EmuWindow_SDL3_Null final : public EmuWindow_SDL3 {
public: public:
explicit EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_, explicit EmuWindow_SDL3_Null(InputCommon::InputSubsystem* input_subsystem_,
Core::System& system, bool fullscreen); Core::System& system, bool fullscreen);
~EmuWindow_SDL2_Null() override; ~EmuWindow_SDL3_Null() override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
}; };

View File

@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: Copyright 2018 sudachi Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib>
#include <memory>
#include <string>
#include <fmt/format.h>
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "sudachi_cmd/emu_window/emu_window_sdl3_vk.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include <SDL.h>
EmuWindow_SDL3_VK::EmuWindow_SDL3_VK(InputCommon::InputSubsystem* input_subsystem_,
Core::System& system_, bool fullscreen)
: EmuWindow_SDL3{input_subsystem_, system_} {
const std::string window_title =
fmt::format("sudachi {} | {}-{} (Vulkan)", Common::g_build_name, Common::g_scm_branch,
Common::g_scm_desc);
render_window = SDL_CreateWindow(window_title.c_str(), Layout::ScreenUndocked::Width,
Layout::ScreenUndocked::Height, SDL_WINDOW_RESIZABLE);
// SDL_SysWMinfo wm;
// SDL_VERSION(&wm.version);
// if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
// LOG_CRITICAL(Frontend, "Failed to get information from the window manager: {}",
// SDL_GetError());
// std::exit(EXIT_FAILURE);
// }
SetWindowIcon();
if (fullscreen) {
Fullscreen();
ShowCursor(false);
}
#if defined(SDL_PLATFORM_WIN32)
HWND hwnd = (HWND)SDL_GetProperty(SDL_GetWindowProperties(render_window),
SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
window_info.type = Core::Frontend::WindowSystemType::Windows;
window_info.render_surface = reinterpret_cast<void*>(hwnd);
#endif
/*
switch (wm.subsystem) {
#ifdef SDL_VIDEO_DRIVER_WINDOWS
case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
window_info.type = Core::Frontend::WindowSystemType::Windows;
window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window);
break;
#endif
#ifdef SDL_VIDEO_DRIVER_X11
case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
window_info.type = Core::Frontend::WindowSystemType::X11;
window_info.display_connection = wm.info.x11.display;
window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window);
break;
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND
case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
window_info.type = Core::Frontend::WindowSystemType::Wayland;
window_info.display_connection = wm.info.wl.display;
window_info.render_surface = wm.info.wl.surface;
break;
#endif
#ifdef SDL_VIDEO_DRIVER_COCOA
case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA:
window_info.type = Core::Frontend::WindowSystemType::Cocoa;
window_info.render_surface = SDL_Metal_CreateView(render_window);
break;
#endif
#ifdef SDL_VIDEO_DRIVER_ANDROID
case SDL_SYSWM_TYPE::SDL_SYSWM_ANDROID:
window_info.type = Core::Frontend::WindowSystemType::Android;
window_info.render_surface = reinterpret_cast<void*>(wm.info.android.window);
break;
#endif
default:
LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem);
std::exit(EXIT_FAILURE);
break;
}
*/
OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents();
LOG_INFO(Frontend, "sudachi Version: {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
}
EmuWindow_SDL3_VK::~EmuWindow_SDL3_VK() = default;
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL3_VK::CreateSharedContext() const {
return std::make_unique<DummyContext>();
}

View File

@ -6,7 +6,7 @@
#include <memory> #include <memory>
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "sudachi_cmd/emu_window/emu_window_sdl2.h" #include "sudachi_cmd/emu_window/emu_window_sdl3.h"
namespace Core { namespace Core {
class System; class System;
@ -16,11 +16,11 @@ namespace InputCommon {
class InputSubsystem; class InputSubsystem;
} }
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { class EmuWindow_SDL3_VK final : public EmuWindow_SDL3 {
public: public:
explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_, Core::System& system, explicit EmuWindow_SDL3_VK(InputCommon::InputSubsystem* input_subsystem_, Core::System& system,
bool fullscreen); bool fullscreen);
~EmuWindow_SDL2_VK() override; ~EmuWindow_SDL3_VK() override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
}; };

View File

@ -34,11 +34,11 @@
#include "input_common/main.h" #include "input_common/main.h"
#include "network/network.h" #include "network/network.h"
#include "sdl_config.h" #include "sdl_config.h"
#include "sudachi_cmd/emu_window/emu_window_sdl3.h"
#include "sudachi_cmd/emu_window/emu_window_sdl3_gl.h"
#include "sudachi_cmd/emu_window/emu_window_sdl3_null.h"
#include "sudachi_cmd/emu_window/emu_window_sdl3_vk.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "sudachi_cmd/emu_window/emu_window_sdl2.h"
#include "sudachi_cmd/emu_window/emu_window_sdl2_gl.h"
#include "sudachi_cmd/emu_window/emu_window_sdl2_null.h"
#include "sudachi_cmd/emu_window/emu_window_sdl2_vk.h"
#ifdef _WIN32 #ifdef _WIN32
// windows.h needs to be included before shellapi.h // windows.h needs to be included before shellapi.h
@ -346,16 +346,16 @@ int main(int argc, char** argv) {
// Apply the command line arguments // Apply the command line arguments
system.ApplySettings(); system.ApplySettings();
std::unique_ptr<EmuWindow_SDL2> emu_window; std::unique_ptr<EmuWindow_SDL3> emu_window;
switch (Settings::values.renderer_backend.GetValue()) { switch (Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL: case Settings::RendererBackend::OpenGL:
emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, system, fullscreen); emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, system, fullscreen);
break; break;
case Settings::RendererBackend::Vulkan: case Settings::RendererBackend::Vulkan:
emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen); emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, system, fullscreen);
break; break;
case Settings::RendererBackend::Null: case Settings::RendererBackend::Null:
emu_window = std::make_unique<EmuWindow_SDL2_Null>(&input_subsystem, system, fullscreen); emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, system, fullscreen);
break; break;
} }
@ -394,11 +394,12 @@ int main(int argc, char** argv) {
static_cast<u32>(Core::SystemResultStatus::ErrorLoader)) { static_cast<u32>(Core::SystemResultStatus::ErrorLoader)) {
const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader); const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader);
const u16 error_id = static_cast<u16>(load_result) - loader_id; const u16 error_id = static_cast<u16>(load_result) - loader_id;
LOG_CRITICAL(Frontend, LOG_CRITICAL(
"While attempting to load the ROM requested, an error occurred. Please " Frontend,
"refer to the sudachi wiki for more information or the sudachi discord for " "While attempting to load the ROM requested, an error occurred. Please "
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", "refer to the sudachi wiki for more information or the sudachi discord for "
loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
} }
break; break;
} }