mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-05-20 18:27:02 +02:00
[externals] Update to SDL3 (#3952)
Since the launch of the steam controller I think it's only best to push towards updating to SDL3 allowing for a wider range of controller support I went ahead and started on getting it working. Everything here should be functional, I've personally tested it all on Arch Linux. Still untested on windows, so looking for feedback on that Any feedback and help would be appreciated! Main changes: - Bump everything to SDL3 - Handle SDL3 audio and input - Add steam controller support, including HD Rumble - Improved battery reporting via the status icon by using real % rather than state alone Co-authored-by: crueter <crueter@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3952 Reviewed-by: crueter <crueter@eden-emu.dev> Reviewed-by: MaranBr <maranbr@eden-emu.dev> Reviewed-by: Lizzie <lizzie@eden-emu.dev>
This commit is contained in:
parent
02521882e7
commit
ad2e1cc554
34 changed files with 768 additions and 537 deletions
|
|
@ -247,11 +247,11 @@ if(ANDROID)
|
|||
target_compile_definitions(audio_core PUBLIC HAVE_OBOE)
|
||||
else()
|
||||
target_sources(audio_core PRIVATE
|
||||
sink/sdl2_sink.cpp
|
||||
sink/sdl2_sink.h)
|
||||
sink/sdl3_sink.cpp
|
||||
sink/sdl3_sink.h)
|
||||
|
||||
target_link_libraries(audio_core PRIVATE SDL2::SDL2)
|
||||
target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
|
||||
target_link_libraries(audio_core PRIVATE SDL3::SDL3)
|
||||
target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(audio_core)
|
||||
|
|
|
|||
|
|
@ -7,16 +7,40 @@
|
|||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL3/SDL.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 "common/logging.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
|
||||
namespace AudioCore::Sink {
|
||||
|
||||
namespace {
|
||||
SDL_AudioDeviceID FindAudioDeviceByName(const std::string& device_name, bool capture) {
|
||||
int device_count = 0;
|
||||
SDL_AudioDeviceID* devices = capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||
: SDL_GetAudioPlaybackDevices(&device_count);
|
||||
if (devices == nullptr) {
|
||||
return capture ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
||||
}
|
||||
|
||||
SDL_AudioDeviceID selected = capture ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING
|
||||
: SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
||||
for (int i = 0; i < device_count; ++i) {
|
||||
const char* current_name = SDL_GetAudioDeviceName(devices[i]);
|
||||
if (current_name != nullptr && device_name == current_name) {
|
||||
selected = devices[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDL_free(devices);
|
||||
return selected;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
/**
|
||||
* SDL sink stream, responsible for sinking samples to hardware.
|
||||
*/
|
||||
|
|
@ -39,13 +63,10 @@ public:
|
|||
system_channels = system_channels_;
|
||||
device_channels = device_channels_;
|
||||
|
||||
SDL_AudioSpec spec;
|
||||
SDL_AudioSpec spec{};
|
||||
spec.freq = TargetSampleRate;
|
||||
spec.channels = static_cast<u8>(device_channels);
|
||||
spec.format = AUDIO_S16SYS;
|
||||
spec.samples = TargetSampleCount * 2;
|
||||
spec.callback = &SDLSinkStream::DataCallback;
|
||||
spec.userdata = this;
|
||||
spec.format = SDL_AUDIO_S16;
|
||||
|
||||
std::string device_name{output_device};
|
||||
bool capture{false};
|
||||
|
|
@ -54,22 +75,28 @@ public:
|
|||
capture = true;
|
||||
}
|
||||
|
||||
SDL_AudioSpec obtained;
|
||||
if (device_name.empty()) {
|
||||
device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false);
|
||||
} else {
|
||||
device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
|
||||
}
|
||||
const SDL_AudioDeviceID audio_device =
|
||||
device_name.empty() ? (capture ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING
|
||||
: SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK)
|
||||
: FindAudioDeviceByName(device_name, capture);
|
||||
|
||||
if (device == 0) {
|
||||
stream = SDL_OpenAudioDeviceStream(audio_device, &spec, &SDLSinkStream::DataCallback,
|
||||
this);
|
||||
|
||||
if (stream == nullptr) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error opening SDL audio device: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_AudioSpec stream_in{};
|
||||
SDL_AudioSpec stream_out{};
|
||||
static_cast<void>(SDL_GetAudioStreamFormat(stream, &stream_in, &stream_out));
|
||||
|
||||
LOG_INFO(Service_Audio,
|
||||
"Opening SDL stream {} with: rate {} channels {} (system channels {}) "
|
||||
" samples {}",
|
||||
device, obtained.freq, obtained.channels, system_channels, obtained.samples);
|
||||
" format {}",
|
||||
static_cast<const void*>(stream), stream_out.freq, stream_out.channels,
|
||||
system_channels, static_cast<int>(stream_out.format));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -84,13 +111,14 @@ public:
|
|||
* Finalize the sink stream.
|
||||
*/
|
||||
void Finalize() override {
|
||||
if (device == 0) {
|
||||
if (stream == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Stop();
|
||||
SDL_ClearQueuedAudio(device);
|
||||
SDL_CloseAudioDevice(device);
|
||||
SDL_ClearAudioStream(stream);
|
||||
SDL_DestroyAudioStream(stream);
|
||||
stream = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -100,23 +128,23 @@ public:
|
|||
* Default false.
|
||||
*/
|
||||
void Start(bool resume = false) override {
|
||||
if (device == 0 || !paused) {
|
||||
if (stream == nullptr || !paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
paused = false;
|
||||
SDL_PauseAudioDevice(device, 0);
|
||||
static_cast<void>(SDL_ResumeAudioStreamDevice(stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the sink stream.
|
||||
*/
|
||||
void Stop() override {
|
||||
if (device == 0 || paused) {
|
||||
if (stream == nullptr || paused) {
|
||||
return;
|
||||
}
|
||||
SignalPause();
|
||||
SDL_PauseAudioDevice(device, 1);
|
||||
static_cast<void>(SDL_PauseAudioStreamDevice(stream));
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -128,7 +156,8 @@ private:
|
|||
* @param stream - Buffer of samples to be filled or read.
|
||||
* @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);
|
||||
|
||||
if (!impl) {
|
||||
|
|
@ -137,25 +166,46 @@ private:
|
|||
|
||||
const std::size_t num_channels = impl->GetDeviceChannels();
|
||||
const std::size_t frame_size = num_channels;
|
||||
const std::size_t num_frames{len / num_channels / sizeof(s16)};
|
||||
|
||||
if (impl->type == StreamType::In) {
|
||||
std::span<const s16> input_buffer{reinterpret_cast<const s16*>(stream),
|
||||
num_frames * frame_size};
|
||||
const int bytes_available = SDL_GetAudioStreamAvailable(stream);
|
||||
if (bytes_available <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<s16> input(bytes_available / static_cast<int>(sizeof(s16)));
|
||||
const int bytes_read = SDL_GetAudioStreamData(stream, input.data(), bytes_available);
|
||||
if (bytes_read <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::size_t num_frames =
|
||||
static_cast<std::size_t>(bytes_read) / sizeof(s16) / frame_size;
|
||||
std::span<const s16> input_buffer{input.data(),
|
||||
static_cast<std::size_t>(bytes_read) / sizeof(s16)};
|
||||
impl->ProcessAudioIn(input_buffer, num_frames);
|
||||
} else {
|
||||
std::span<s16> output_buffer{reinterpret_cast<s16*>(stream), num_frames * frame_size};
|
||||
if (additional_amount <= 0 && total_amount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int bytes_requested = additional_amount > 0 ? additional_amount : total_amount;
|
||||
std::vector<s16> output(bytes_requested / static_cast<int>(sizeof(s16)));
|
||||
const std::size_t num_frames =
|
||||
static_cast<std::size_t>(bytes_requested) / sizeof(s16) / frame_size;
|
||||
std::span<s16> output_buffer{output.data(), output.size()};
|
||||
impl->ProcessAudioOutAndRender(output_buffer, num_frames);
|
||||
static_cast<void>(SDL_PutAudioStreamData(stream, output.data(), bytes_requested));
|
||||
}
|
||||
}
|
||||
|
||||
/// SDL device id of the opened input/output device
|
||||
SDL_AudioDeviceID device{};
|
||||
/// SDL stream attached to an opened input/output device
|
||||
SDL_AudioStream* stream{};
|
||||
};
|
||||
|
||||
SDLSink::SDLSink(std::string_view target_device_name) {
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
|
@ -218,18 +268,26 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
|||
std::vector<std::string> device_list;
|
||||
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const int device_count = SDL_GetNumAudioDevices(capture);
|
||||
int device_count = 0;
|
||||
SDL_AudioDeviceID* devices =
|
||||
capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||
: SDL_GetAudioPlaybackDevices(&device_count);
|
||||
if (devices == nullptr) {
|
||||
return device_list;
|
||||
}
|
||||
|
||||
for (int i = 0; i < device_count; ++i) {
|
||||
if (const char* name = SDL_GetAudioDeviceName(i, capture)) {
|
||||
if (const char* name = SDL_GetAudioDeviceName(devices[i])) {
|
||||
device_list.emplace_back(name);
|
||||
}
|
||||
}
|
||||
SDL_free(devices);
|
||||
|
||||
return device_list;
|
||||
}
|
||||
|
|
@ -242,7 +300,7 @@ u32 GetSDLLatency() {
|
|||
// REVERTED back to 3833 - Below function IsSDLSuitable() removed, reverting to GetSDLLatency() above. - DIABLO 3 FIX
|
||||
/*
|
||||
bool IsSDLSuitable() {
|
||||
#if !defined(HAVE_SDL2)
|
||||
#if !defined(HAVE_SDL3)
|
||||
return false;
|
||||
#else
|
||||
// Check SDL can init
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
|
|
@ -16,8 +16,8 @@
|
|||
#ifdef HAVE_CUBEB
|
||||
#include "audio_core/sink/cubeb_sink.h"
|
||||
#endif
|
||||
#ifdef HAVE_SDL2
|
||||
#include "audio_core/sink/sdl2_sink.h"
|
||||
#ifdef HAVE_SDL3
|
||||
#include "audio_core/sink/sdl3_sink.h"
|
||||
#endif
|
||||
#include "audio_core/sink/null_sink.h"
|
||||
#include "common/logging.h"
|
||||
|
|
@ -71,9 +71,9 @@ constexpr SinkDetails sink_details[] = {
|
|||
&GetCubebLatency,
|
||||
},
|
||||
#endif
|
||||
#ifdef HAVE_SDL2
|
||||
#ifdef HAVE_SDL3
|
||||
SinkDetails{
|
||||
Settings::AudioEngine::Sdl2,
|
||||
Settings::AudioEngine::Sdl3,
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<SDLSink>(device_id);
|
||||
},
|
||||
|
|
@ -115,10 +115,10 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
|
|||
// BEGIN REINTRODUCED FROM 3833 - REPLACED CODE BLOCK ABOVE - DIABLO 3 FIX
|
||||
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
|
||||
// causes audio issues, in that case go with SDL.
|
||||
#if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
|
||||
#if defined(HAVE_CUBEB) && defined(HAVE_SDL3)
|
||||
iter = find_backend(Settings::AudioEngine::Cubeb);
|
||||
if (iter->latency() > TargetSampleCount * 3) {
|
||||
iter = find_backend(Settings::AudioEngine::Sdl2);
|
||||
iter = find_backend(Settings::AudioEngine::Sdl3);
|
||||
}
|
||||
#else
|
||||
iter = std::begin(sink_details);
|
||||
|
|
|
|||
|
|
@ -92,11 +92,13 @@ struct EnumMetadata {
|
|||
// AudioEngine must be specified discretely due to having existing but slightly different
|
||||
// canonicalizations
|
||||
// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
|
||||
enum class AudioEngine : u32 { Auto, Cubeb, Sdl2, Null, Oboe, };
|
||||
enum class AudioEngine : u32 { Auto, Cubeb, Sdl3, Null, Oboe, };
|
||||
template<>
|
||||
inline std::vector<std::pair<std::string_view, AudioEngine>> EnumMetadata<AudioEngine>::Canonicalizations() {
|
||||
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},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@ else()
|
|||
helpers/joycon_protocol/rumble.cpp
|
||||
helpers/joycon_protocol/rumble.h)
|
||||
|
||||
target_link_libraries(input_common PRIVATE SDL2::SDL2)
|
||||
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
|
||||
target_link_libraries(input_common PRIVATE SDL3::SDL3)
|
||||
target_compile_definitions(input_common PRIVATE HAVE_SDL3)
|
||||
endif()
|
||||
|
||||
if (ENABLE_LIBUSB)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -6,7 +9,7 @@
|
|||
#include <array>
|
||||
#include <span>
|
||||
#include <thread>
|
||||
#include <SDL_hidapi.h>
|
||||
#include <SDL3/SDL_hidapi.h>
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -15,29 +15,79 @@ namespace InputCommon {
|
|||
|
||||
namespace {
|
||||
Common::UUID GetGUID(SDL_Joystick* joystick) {
|
||||
const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
|
||||
const SDL_GUID guid = SDL_GetJoystickGUID(joystick);
|
||||
std::array<u8, 16> data{};
|
||||
std::memcpy(data.data(), guid.data, sizeof(data));
|
||||
// Clear controller name crc
|
||||
std::memset(data.data() + 2, 0, sizeof(u16));
|
||||
return Common::UUID{data};
|
||||
}
|
||||
|
||||
using GamepadBindings = std::vector<SDL_GamepadBinding>;
|
||||
|
||||
SDL_GamepadBinding EmptyBinding() {
|
||||
SDL_GamepadBinding binding{};
|
||||
binding.input_type = SDL_GAMEPAD_BINDTYPE_NONE;
|
||||
return binding;
|
||||
}
|
||||
|
||||
GamepadBindings GetBindings(SDL_Gamepad* controller) {
|
||||
if (controller == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int binding_count = 0;
|
||||
SDL_GamepadBinding** bindings = SDL_GetGamepadBindings(controller, &binding_count);
|
||||
if (bindings == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
GamepadBindings cached_bindings{};
|
||||
cached_bindings.reserve(static_cast<std::size_t>(binding_count));
|
||||
for (int i = 0; i < binding_count; ++i) {
|
||||
if (const auto* current = bindings[i]) {
|
||||
cached_bindings.emplace_back(*current);
|
||||
}
|
||||
}
|
||||
SDL_free(bindings);
|
||||
return cached_bindings;
|
||||
}
|
||||
|
||||
template <typename Predicate>
|
||||
SDL_GamepadBinding FindBinding(const GamepadBindings& bindings, Predicate matches) {
|
||||
const auto it = std::find_if(bindings.begin(), bindings.end(), matches);
|
||||
return it != bindings.end() ? *it : EmptyBinding();
|
||||
}
|
||||
|
||||
SDL_GamepadBinding GetBindingForButton(const GamepadBindings& bindings, SDL_GamepadButton button) {
|
||||
return FindBinding(bindings, [button](const SDL_GamepadBinding& current) {
|
||||
return current.output_type == SDL_GAMEPAD_BINDTYPE_BUTTON &&
|
||||
current.output.button == static_cast<SDL_GamepadButton>(button);
|
||||
});
|
||||
}
|
||||
|
||||
SDL_GamepadBinding GetBindingForAxis(const GamepadBindings& bindings, SDL_GamepadAxis axis) {
|
||||
return FindBinding(bindings, [axis](const SDL_GamepadBinding& current) {
|
||||
return current.output_type == SDL_GAMEPAD_BINDTYPE_AXIS &&
|
||||
current.output.axis.axis == static_cast<SDL_GamepadAxis>(axis);
|
||||
});
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
static int SDLEventWatcher(void* user_data, SDL_Event* event) {
|
||||
static bool SDLEventWatcher(void* user_data, SDL_Event* event) {
|
||||
auto* const sdl_state = static_cast<SDLDriver*>(user_data);
|
||||
|
||||
sdl_state->HandleGameControllerEvent(*event);
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
class SDLJoystick {
|
||||
public:
|
||||
SDLJoystick(Common::UUID guid_, int port_, SDL_Joystick* joystick,
|
||||
SDL_GameController* game_controller)
|
||||
: guid{guid_}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
|
||||
sdl_controller{game_controller, &SDL_GameControllerClose} {
|
||||
SDL_Gamepad* game_controller)
|
||||
: guid{guid_}, port{port_}, sdl_joystick{joystick, &SDL_CloseJoystick},
|
||||
sdl_controller{game_controller, &SDL_CloseGamepad} {
|
||||
EnableMotion();
|
||||
}
|
||||
|
||||
|
|
@ -45,30 +95,49 @@ public:
|
|||
if (!sdl_controller) {
|
||||
return;
|
||||
}
|
||||
SDL_GameController* controller = sdl_controller.get();
|
||||
SDL_Gamepad* controller = sdl_controller.get();
|
||||
if (HasMotion()) {
|
||||
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_FALSE);
|
||||
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_FALSE);
|
||||
SDL_SetGamepadSensorEnabled(controller, SDL_SENSOR_ACCEL, false);
|
||||
SDL_SetGamepadSensorEnabled(controller, SDL_SENSOR_GYRO, false);
|
||||
}
|
||||
has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
|
||||
has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
|
||||
has_accel = SDL_GamepadHasSensor(controller, SDL_SENSOR_ACCEL);
|
||||
has_gyro = SDL_GamepadHasSensor(controller, SDL_SENSOR_GYRO);
|
||||
if (has_accel) {
|
||||
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
|
||||
if (!SDL_SetGamepadSensorEnabled(controller, SDL_SENSOR_ACCEL, true)) {
|
||||
LOG_WARNING(Input, "Failed to enable accelerometer sensor: {}", SDL_GetError());
|
||||
}
|
||||
}
|
||||
if (has_gyro) {
|
||||
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
|
||||
if (!SDL_SetGamepadSensorEnabled(controller, SDL_SENSOR_GYRO, true)) {
|
||||
LOG_WARNING(Input, "Failed to enable gyroscope sensor: {}", SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO(Input, "Controller motion capabilities: accel={} gyro={}", has_accel, has_gyro);
|
||||
}
|
||||
|
||||
bool HasMotion() const {
|
||||
return has_gyro || has_accel;
|
||||
}
|
||||
|
||||
bool UpdateMotion(SDL_ControllerSensorEvent event) {
|
||||
bool UpdateMotion(SDL_GamepadSensorEvent event) {
|
||||
constexpr float gravity_constant = 9.80665f;
|
||||
std::scoped_lock lock{mutex};
|
||||
const u64 time_difference = event.timestamp - last_motion_update;
|
||||
last_motion_update = event.timestamp;
|
||||
const u64 sensor_timestamp = event.sensor_timestamp != 0 ? event.sensor_timestamp
|
||||
: event.timestamp;
|
||||
|
||||
if (last_motion_update == 0) {
|
||||
last_motion_update = sensor_timestamp;
|
||||
return false;
|
||||
}
|
||||
if (sensor_timestamp < last_motion_update) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SDL3 reports sensor timestamps in nanoseconds, while the input stack expects
|
||||
// delta timestamps in microseconds.
|
||||
const u64 time_difference = (sensor_timestamp - last_motion_update) / 1000;
|
||||
last_motion_update = sensor_timestamp;
|
||||
switch (event.sensor) {
|
||||
case SDL_SENSOR_ACCEL: {
|
||||
motion.accel_x = -event.data[0] / gravity_constant;
|
||||
|
|
@ -102,7 +171,7 @@ public:
|
|||
}
|
||||
|
||||
motion_error_count = 0;
|
||||
motion.delta_timestamp = time_difference * 1000;
|
||||
motion.delta_timestamp = time_difference;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -136,26 +205,41 @@ public:
|
|||
f32 high_amplitude = vibration.high_amplitude * high_frequency_scale;
|
||||
|
||||
if (sdl_controller) {
|
||||
return SDL_GameControllerRumble(sdl_controller.get(), static_cast<u16>(low_amplitude),
|
||||
static_cast<u16>(high_amplitude),
|
||||
rumble_max_duration_ms) != -1;
|
||||
return SDL_RumbleGamepad(sdl_controller.get(), static_cast<u16>(low_amplitude),
|
||||
static_cast<u16>(high_amplitude), rumble_max_duration_ms);
|
||||
} else if (sdl_joystick) {
|
||||
return SDL_JoystickRumble(sdl_joystick.get(), static_cast<u16>(low_amplitude),
|
||||
return SDL_RumbleJoystick(sdl_joystick.get(), static_cast<u16>(low_amplitude),
|
||||
static_cast<u16>(high_amplitude),
|
||||
rumble_max_duration_ms) != -1;
|
||||
rumble_max_duration_ms);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HasHDRumble() const {
|
||||
constexpr Uint16 valve_vendor_id = 0x28DE;
|
||||
const auto is_known_hd_type = [](SDL_GamepadType type) {
|
||||
return type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO ||
|
||||
type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||
|
||||
type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT ||
|
||||
type == SDL_GAMEPAD_TYPE_PS5;
|
||||
};
|
||||
|
||||
// Valve hardware doesn't have any enums in SDL, so we have to support it manually.
|
||||
// Since they have HD rumble, we can assume that all their hardware supports it, even if we can't detect the exact type.
|
||||
if (sdl_controller) {
|
||||
const auto type = SDL_GameControllerGetType(sdl_controller.get());
|
||||
return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) ||
|
||||
(type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT) ||
|
||||
(type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) ||
|
||||
(type == SDL_CONTROLLER_TYPE_PS5);
|
||||
if (is_known_hd_type(SDL_GetGamepadType(sdl_controller.get())) ||
|
||||
SDL_GetGamepadVendor(sdl_controller.get()) == valve_vendor_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (sdl_joystick) {
|
||||
if (SDL_GetJoystickVendor(sdl_joystick.get()) == valve_vendor_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -201,11 +285,11 @@ public:
|
|||
return sdl_joystick.get();
|
||||
}
|
||||
|
||||
SDL_GameController* GetSDLGameController() const {
|
||||
SDL_Gamepad* GetSDLGameController() const {
|
||||
return sdl_controller.get();
|
||||
}
|
||||
|
||||
void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
|
||||
void SetSDLJoystick(SDL_Joystick* joystick, SDL_Gamepad* controller) {
|
||||
sdl_joystick.reset(joystick);
|
||||
sdl_controller.reset(controller);
|
||||
}
|
||||
|
|
@ -232,20 +316,36 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
Common::Input::BatteryLevel GetBatteryLevel(SDL_JoystickPowerLevel battery_level) {
|
||||
switch (battery_level) {
|
||||
case SDL_JOYSTICK_POWER_EMPTY:
|
||||
return Common::Input::BatteryLevel::Empty;
|
||||
case SDL_JOYSTICK_POWER_LOW:
|
||||
return Common::Input::BatteryLevel::Low;
|
||||
case SDL_JOYSTICK_POWER_MEDIUM:
|
||||
return Common::Input::BatteryLevel::Medium;
|
||||
case SDL_JOYSTICK_POWER_FULL:
|
||||
case SDL_JOYSTICK_POWER_MAX:
|
||||
return Common::Input::BatteryLevel::Full;
|
||||
case SDL_JOYSTICK_POWER_WIRED:
|
||||
Common::Input::BatteryLevel GetBatteryLevel(SDL_PowerState battery_level, int percent) {
|
||||
if (battery_level == SDL_POWERSTATE_CHARGING) {
|
||||
return Common::Input::BatteryLevel::Charging;
|
||||
case SDL_JOYSTICK_POWER_UNKNOWN:
|
||||
}
|
||||
|
||||
if (percent >= 0 && percent <= 100) {
|
||||
if (percent <= 5) {
|
||||
return Common::Input::BatteryLevel::Empty;
|
||||
}
|
||||
if (percent <= 20) {
|
||||
return Common::Input::BatteryLevel::Critical;
|
||||
}
|
||||
if (percent <= 40) {
|
||||
return Common::Input::BatteryLevel::Low;
|
||||
}
|
||||
if (percent <= 70) {
|
||||
return Common::Input::BatteryLevel::Medium;
|
||||
}
|
||||
return Common::Input::BatteryLevel::Full;
|
||||
}
|
||||
|
||||
switch (battery_level) {
|
||||
case SDL_POWERSTATE_ON_BATTERY:
|
||||
return Common::Input::BatteryLevel::Medium;
|
||||
case SDL_POWERSTATE_NO_BATTERY:
|
||||
return Common::Input::BatteryLevel::None;
|
||||
case SDL_POWERSTATE_CHARGED:
|
||||
return Common::Input::BatteryLevel::Full;
|
||||
case SDL_POWERSTATE_ERROR:
|
||||
case SDL_POWERSTATE_UNKNOWN:
|
||||
default:
|
||||
return Common::Input::BatteryLevel::None;
|
||||
}
|
||||
|
|
@ -253,28 +353,28 @@ public:
|
|||
|
||||
std::string GetControllerName() const {
|
||||
if (sdl_controller) {
|
||||
switch (SDL_GameControllerGetType(sdl_controller.get())) {
|
||||
case SDL_CONTROLLER_TYPE_XBOX360:
|
||||
switch (SDL_GetGamepadType(sdl_controller.get())) {
|
||||
case SDL_GAMEPAD_TYPE_XBOX360:
|
||||
return "Xbox 360 Controller";
|
||||
case SDL_CONTROLLER_TYPE_XBOXONE:
|
||||
case SDL_GAMEPAD_TYPE_XBOXONE:
|
||||
return "Xbox One Controller";
|
||||
case SDL_CONTROLLER_TYPE_PS3:
|
||||
case SDL_GAMEPAD_TYPE_PS3:
|
||||
return "DualShock 3 Controller";
|
||||
case SDL_CONTROLLER_TYPE_PS4:
|
||||
case SDL_GAMEPAD_TYPE_PS4:
|
||||
return "DualShock 4 Controller";
|
||||
case SDL_CONTROLLER_TYPE_PS5:
|
||||
case SDL_GAMEPAD_TYPE_PS5:
|
||||
return "DualSense Controller";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const auto name = SDL_GameControllerName(sdl_controller.get());
|
||||
const auto name = SDL_GetGamepadName(sdl_controller.get());
|
||||
if (name) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
if (sdl_joystick) {
|
||||
const auto name = SDL_JoystickName(sdl_joystick.get());
|
||||
const auto name = SDL_GetJoystickName(sdl_joystick.get());
|
||||
if (name) {
|
||||
return name;
|
||||
}
|
||||
|
|
@ -286,8 +386,8 @@ public:
|
|||
private:
|
||||
Common::UUID guid;
|
||||
int port;
|
||||
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
|
||||
std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
|
||||
std::unique_ptr<SDL_Joystick, decltype(&SDL_CloseJoystick)> sdl_joystick;
|
||||
std::unique_ptr<SDL_Gamepad, decltype(&SDL_CloseGamepad)> sdl_controller;
|
||||
mutable std::mutex mutex;
|
||||
|
||||
u64 last_motion_update{};
|
||||
|
|
@ -323,7 +423,10 @@ std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string&
|
|||
}
|
||||
|
||||
std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
|
||||
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
|
||||
auto sdl_joystick = SDL_GetJoystickFromID(sdl_id);
|
||||
if (sdl_joystick == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto guid = GetGUID(sdl_joystick);
|
||||
|
||||
std::scoped_lock lock{joystick_map_mutex};
|
||||
|
|
@ -345,34 +448,70 @@ std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl
|
|||
return *vec_it;
|
||||
}
|
||||
|
||||
void SDLDriver::InitJoystick(int joystick_index) {
|
||||
SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
|
||||
SDL_GameController* sdl_gamecontroller = nullptr;
|
||||
std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGamepadID(SDL_JoystickID sdl_id) {
|
||||
auto* const sdl_gamepad = SDL_GetGamepadFromID(sdl_id);
|
||||
if (sdl_gamepad == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (SDL_IsGameController(joystick_index)) {
|
||||
sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
|
||||
auto* const sdl_joystick = SDL_GetGamepadJoystick(sdl_gamepad);
|
||||
if (sdl_joystick == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto guid = GetGUID(sdl_joystick);
|
||||
|
||||
std::scoped_lock lock{joystick_map_mutex};
|
||||
const auto map_it = joystick_map.find(guid);
|
||||
if (map_it == joystick_map.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
|
||||
[sdl_joystick, sdl_gamepad](const auto& joystick) {
|
||||
return joystick->GetSDLJoystick() == sdl_joystick ||
|
||||
joystick->GetSDLGameController() == sdl_gamepad;
|
||||
});
|
||||
|
||||
if (vec_it == map_it->second.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return *vec_it;
|
||||
}
|
||||
|
||||
void SDLDriver::InitJoystick(SDL_JoystickID joystick_id) {
|
||||
SDL_Joystick* sdl_joystick = SDL_OpenJoystick(joystick_id);
|
||||
SDL_Gamepad* sdl_gamecontroller = nullptr;
|
||||
int battery_percent = -1;
|
||||
SDL_PowerState battery_state = SDL_POWERSTATE_UNKNOWN;
|
||||
|
||||
if (SDL_IsGamepad(joystick_id)) {
|
||||
sdl_gamecontroller = SDL_OpenGamepad(joystick_id);
|
||||
}
|
||||
|
||||
if (!sdl_joystick) {
|
||||
LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
|
||||
LOG_ERROR(Input, "Failed to open joystick {}", joystick_id);
|
||||
return;
|
||||
}
|
||||
|
||||
battery_state = SDL_GetJoystickPowerInfo(sdl_joystick, &battery_percent);
|
||||
|
||||
const auto guid = GetGUID(sdl_joystick);
|
||||
|
||||
if (Settings::values.enable_joycon_driver) {
|
||||
if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e &&
|
||||
(guid.uuid[8] == 0x06 || guid.uuid[8] == 0x07)) {
|
||||
LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index);
|
||||
SDL_JoystickClose(sdl_joystick);
|
||||
LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_id);
|
||||
SDL_CloseJoystick(sdl_joystick);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings::values.enable_procon_driver) {
|
||||
if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e && guid.uuid[8] == 0x09) {
|
||||
LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index);
|
||||
SDL_JoystickClose(sdl_joystick);
|
||||
LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_id);
|
||||
SDL_CloseJoystick(sdl_joystick);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -382,6 +521,8 @@ void SDLDriver::InitJoystick(int joystick_index) {
|
|||
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
|
||||
PreSetController(joystick->GetPadIdentifier());
|
||||
joystick->EnableMotion();
|
||||
SetBattery(joystick->GetPadIdentifier(),
|
||||
joystick->GetBatteryLevel(battery_state, battery_percent));
|
||||
joystick_map[guid].emplace_back(std::move(joystick));
|
||||
return;
|
||||
}
|
||||
|
|
@ -394,6 +535,8 @@ void SDLDriver::InitJoystick(int joystick_index) {
|
|||
if (joystick_it != joystick_guid_list.end()) {
|
||||
(*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
|
||||
(*joystick_it)->EnableMotion();
|
||||
SetBattery((*joystick_it)->GetPadIdentifier(),
|
||||
(*joystick_it)->GetBatteryLevel(battery_state, battery_percent));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -401,6 +544,8 @@ void SDLDriver::InitJoystick(int joystick_index) {
|
|||
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
|
||||
PreSetController(joystick->GetPadIdentifier());
|
||||
joystick->EnableMotion();
|
||||
SetBattery(joystick->GetPadIdentifier(),
|
||||
joystick->GetBatteryLevel(battery_state, battery_percent));
|
||||
joystick_guid_list.emplace_back(std::move(joystick));
|
||||
}
|
||||
|
||||
|
|
@ -428,55 +573,60 @@ void SDLDriver::PumpEvents() const {
|
|||
|
||||
void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
|
||||
switch (event.type) {
|
||||
case SDL_JOYBUTTONUP: {
|
||||
case SDL_EVENT_JOYSTICK_BUTTON_UP: {
|
||||
if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
|
||||
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
||||
SetButton(identifier, event.jbutton.button, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_JOYBUTTONDOWN: {
|
||||
case SDL_EVENT_JOYSTICK_BUTTON_DOWN: {
|
||||
if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
|
||||
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
||||
SetButton(identifier, event.jbutton.button, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_JOYHATMOTION: {
|
||||
case SDL_EVENT_JOYSTICK_HAT_MOTION: {
|
||||
if (const auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) {
|
||||
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
||||
SetHatButton(identifier, event.jhat.hat, event.jhat.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_JOYAXISMOTION: {
|
||||
case SDL_EVENT_JOYSTICK_AXIS_MOTION: {
|
||||
if (const auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) {
|
||||
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
||||
SetAxis(identifier, event.jaxis.axis, event.jaxis.value / 32767.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_CONTROLLERSENSORUPDATE: {
|
||||
if (auto joystick = GetSDLJoystickBySDLID(event.csensor.which)) {
|
||||
if (joystick->UpdateMotion(event.csensor)) {
|
||||
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: {
|
||||
auto joystick = GetSDLJoystickByGamepadID(event.gsensor.which);
|
||||
if (!joystick) {
|
||||
joystick = GetSDLJoystickBySDLID(event.gsensor.which);
|
||||
}
|
||||
if (joystick) {
|
||||
if (joystick->UpdateMotion(event.gsensor)) {
|
||||
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
||||
SetMotion(identifier, 0, joystick->GetMotion());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_JOYBATTERYUPDATED: {
|
||||
case SDL_EVENT_JOYSTICK_BATTERY_UPDATED: {
|
||||
if (auto joystick = GetSDLJoystickBySDLID(event.jbattery.which)) {
|
||||
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
||||
SetBattery(identifier, joystick->GetBatteryLevel(event.jbattery.level));
|
||||
SetBattery(identifier,
|
||||
joystick->GetBatteryLevel(event.jbattery.state, event.jbattery.percent));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_JOYDEVICEREMOVED:
|
||||
case SDL_EVENT_JOYSTICK_REMOVED:
|
||||
LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
|
||||
CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
|
||||
CloseJoystick(SDL_GetJoystickFromID(event.jdevice.which));
|
||||
break;
|
||||
case SDL_JOYDEVICEADDED:
|
||||
case SDL_EVENT_JOYSTICK_ADDED:
|
||||
LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
|
||||
InitJoystick(event.jdevice.which);
|
||||
break;
|
||||
|
|
@ -496,12 +646,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
|
|||
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, Settings::values.enable_raw_input ? "1" : "0");
|
||||
|
||||
// Prevent SDL from adding undesired axis
|
||||
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
|
||||
// SDL3 defaults Steam Controller Bluetooth HIDAPI support to off, which can disable gyro.
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1");
|
||||
SDL_SetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION, "1");
|
||||
SDL_SetHint(SDL_HINT_AUTO_UPDATE_SENSORS, "1");
|
||||
|
||||
// Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
||||
|
||||
// Disable hidapi drivers for joycon controllers when the custom joycon driver is enabled
|
||||
|
|
@ -523,16 +674,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
|
|||
}
|
||||
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1");
|
||||
// Share the same button mapping with non-Nintendo controllers
|
||||
SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0");
|
||||
|
||||
// Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
|
||||
// driver on Linux.
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0");
|
||||
|
||||
// If the frontend is going to manage the event loop, then we don't start one here
|
||||
start_thread = SDL_WasInit(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) == 0;
|
||||
if (start_thread && SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
|
||||
start_thread = SDL_WasInit(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD) == 0;
|
||||
if (start_thread && !SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD)) {
|
||||
LOG_CRITICAL(Input, "SDL_Init failed with: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
|
@ -552,19 +700,24 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
|
|||
}
|
||||
// Because the events for joystick connection happens before we have our event watcher added, we
|
||||
// can just open all the joysticks right here
|
||||
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
|
||||
InitJoystick(i);
|
||||
int joystick_count = 0;
|
||||
SDL_JoystickID* joysticks = SDL_GetJoysticks(&joystick_count);
|
||||
if (joysticks != nullptr) {
|
||||
for (int i = 0; i < joystick_count; ++i) {
|
||||
InitJoystick(joysticks[i]);
|
||||
}
|
||||
SDL_free(joysticks);
|
||||
}
|
||||
}
|
||||
|
||||
SDLDriver::~SDLDriver() {
|
||||
CloseJoysticks();
|
||||
SDL_DelEventWatch(&SDLEventWatcher, this);
|
||||
SDL_RemoveEventWatch(&SDLEventWatcher, this);
|
||||
|
||||
initialized = false;
|
||||
if (start_thread) {
|
||||
vibration_thread.join();
|
||||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
|
||||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -758,17 +911,17 @@ Common::ParamPackage SDLDriver::BuildMotionParam(int port, const Common::UUID& g
|
|||
}
|
||||
|
||||
Common::ParamPackage SDLDriver::BuildParamPackageForBinding(
|
||||
int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const {
|
||||
switch (binding.bindType) {
|
||||
case SDL_CONTROLLER_BINDTYPE_NONE:
|
||||
int port, const Common::UUID& guid, const SDL_GamepadBinding& binding) const {
|
||||
switch (binding.input_type) {
|
||||
case SDL_GAMEPAD_BINDTYPE_NONE:
|
||||
break;
|
||||
case SDL_CONTROLLER_BINDTYPE_AXIS:
|
||||
return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
|
||||
case SDL_CONTROLLER_BINDTYPE_BUTTON:
|
||||
return BuildButtonParamPackageForButton(port, guid, binding.value.button);
|
||||
case SDL_CONTROLLER_BINDTYPE_HAT:
|
||||
return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
|
||||
static_cast<u8>(binding.value.hat.hat_mask));
|
||||
case SDL_GAMEPAD_BINDTYPE_AXIS:
|
||||
return BuildAnalogParamPackageForButton(port, guid, binding.input.axis.axis);
|
||||
case SDL_GAMEPAD_BINDTYPE_BUTTON:
|
||||
return BuildButtonParamPackageForButton(port, guid, binding.input.button);
|
||||
case SDL_GAMEPAD_BINDTYPE_HAT:
|
||||
return BuildHatParamPackageForButton(port, guid, binding.input.hat.hat,
|
||||
static_cast<u8>(binding.input.hat.hat_mask));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
@ -808,8 +961,8 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
|
|||
|
||||
// Add the missing bindings for ZL/ZR
|
||||
static constexpr ZButtonBindings switch_to_sdl_axis{{
|
||||
{Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
|
||||
{Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
|
||||
{Settings::NativeButton::ZL, SDL_GAMEPAD_AXIS_LEFT_TRIGGER},
|
||||
{Settings::NativeButton::ZR, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER},
|
||||
}};
|
||||
|
||||
// Parameters contain two joysticks return dual
|
||||
|
|
@ -828,41 +981,41 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
|
|||
ButtonBindings SDLDriver::GetDefaultButtonBinding(
|
||||
const std::shared_ptr<SDLJoystick>& joystick) const {
|
||||
// Default SL/SR mapping for other controllers
|
||||
auto sll_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
|
||||
auto srl_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
|
||||
auto slr_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
|
||||
auto srr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
|
||||
auto sll_button = SDL_GAMEPAD_BUTTON_LEFT_SHOULDER;
|
||||
auto srl_button = SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER;
|
||||
auto slr_button = SDL_GAMEPAD_BUTTON_LEFT_SHOULDER;
|
||||
auto srr_button = SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER;
|
||||
|
||||
if (joystick->IsJoyconLeft()) {
|
||||
sll_button = SDL_CONTROLLER_BUTTON_PADDLE2;
|
||||
srl_button = SDL_CONTROLLER_BUTTON_PADDLE4;
|
||||
sll_button = SDL_GAMEPAD_BUTTON_LEFT_PADDLE1;
|
||||
srl_button = SDL_GAMEPAD_BUTTON_LEFT_PADDLE2;
|
||||
}
|
||||
if (joystick->IsJoyconRight()) {
|
||||
slr_button = SDL_CONTROLLER_BUTTON_PADDLE3;
|
||||
srr_button = SDL_CONTROLLER_BUTTON_PADDLE1;
|
||||
slr_button = SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2;
|
||||
srr_button = SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1;
|
||||
}
|
||||
|
||||
return {
|
||||
std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
|
||||
{Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
|
||||
{Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
|
||||
{Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
|
||||
{Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
|
||||
{Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
|
||||
{Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
|
||||
{Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
|
||||
{Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
|
||||
{Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
|
||||
{Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
|
||||
{Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
|
||||
{Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
|
||||
{Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
|
||||
std::pair{Settings::NativeButton::A, SDL_GAMEPAD_BUTTON_EAST},
|
||||
{Settings::NativeButton::B, SDL_GAMEPAD_BUTTON_SOUTH},
|
||||
{Settings::NativeButton::X, SDL_GAMEPAD_BUTTON_NORTH},
|
||||
{Settings::NativeButton::Y, SDL_GAMEPAD_BUTTON_WEST},
|
||||
{Settings::NativeButton::LStick, SDL_GAMEPAD_BUTTON_LEFT_STICK},
|
||||
{Settings::NativeButton::RStick, SDL_GAMEPAD_BUTTON_RIGHT_STICK},
|
||||
{Settings::NativeButton::L, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER},
|
||||
{Settings::NativeButton::R, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER},
|
||||
{Settings::NativeButton::Plus, SDL_GAMEPAD_BUTTON_START},
|
||||
{Settings::NativeButton::Minus, SDL_GAMEPAD_BUTTON_BACK},
|
||||
{Settings::NativeButton::DLeft, SDL_GAMEPAD_BUTTON_DPAD_LEFT},
|
||||
{Settings::NativeButton::DUp, SDL_GAMEPAD_BUTTON_DPAD_UP},
|
||||
{Settings::NativeButton::DRight, SDL_GAMEPAD_BUTTON_DPAD_RIGHT},
|
||||
{Settings::NativeButton::DDown, SDL_GAMEPAD_BUTTON_DPAD_DOWN},
|
||||
{Settings::NativeButton::SLLeft, sll_button},
|
||||
{Settings::NativeButton::SRLeft, srl_button},
|
||||
{Settings::NativeButton::SLRight, slr_button},
|
||||
{Settings::NativeButton::SRRight, srr_button},
|
||||
{Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
|
||||
{Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1},
|
||||
{Settings::NativeButton::Home, SDL_GAMEPAD_BUTTON_GUIDE},
|
||||
{Settings::NativeButton::Screenshot, SDL_GAMEPAD_BUTTON_MISC1},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -872,15 +1025,16 @@ ButtonMapping SDLDriver::GetSingleControllerMapping(
|
|||
ButtonMapping mapping;
|
||||
mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
|
||||
auto* controller = joystick->GetSDLGameController();
|
||||
const auto bindings = GetBindings(controller);
|
||||
|
||||
for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
|
||||
const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
|
||||
const auto binding = GetBindingForButton(bindings, sdl_button);
|
||||
mapping.insert_or_assign(
|
||||
switch_button,
|
||||
BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
|
||||
}
|
||||
for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
|
||||
const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
|
||||
const auto binding = GetBindingForAxis(bindings, sdl_axis);
|
||||
mapping.insert_or_assign(
|
||||
switch_button,
|
||||
BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
|
||||
|
|
@ -897,29 +1051,31 @@ ButtonMapping SDLDriver::GetDualControllerMapping(const std::shared_ptr<SDLJoyst
|
|||
mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
|
||||
auto* controller = joystick->GetSDLGameController();
|
||||
auto* controller2 = joystick2->GetSDLGameController();
|
||||
const auto bindings = GetBindings(controller);
|
||||
const auto bindings2 = GetBindings(controller2);
|
||||
|
||||
for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
|
||||
if (IsButtonOnLeftSide(switch_button)) {
|
||||
const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button);
|
||||
const auto binding = GetBindingForButton(bindings2, sdl_button);
|
||||
mapping.insert_or_assign(
|
||||
switch_button,
|
||||
BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
|
||||
continue;
|
||||
}
|
||||
const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
|
||||
const auto binding = GetBindingForButton(bindings, sdl_button);
|
||||
mapping.insert_or_assign(
|
||||
switch_button,
|
||||
BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
|
||||
}
|
||||
for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
|
||||
if (IsButtonOnLeftSide(switch_button)) {
|
||||
const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis);
|
||||
const auto binding = GetBindingForAxis(bindings2, sdl_axis);
|
||||
mapping.insert_or_assign(
|
||||
switch_button,
|
||||
BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
|
||||
continue;
|
||||
}
|
||||
const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
|
||||
const auto binding = GetBindingForAxis(bindings, sdl_axis);
|
||||
mapping.insert_or_assign(
|
||||
switch_button,
|
||||
BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
|
||||
|
|
@ -957,46 +1113,43 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p
|
|||
}
|
||||
|
||||
AnalogMapping mapping = {};
|
||||
const auto& binding_left_x =
|
||||
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
|
||||
const auto& binding_left_y =
|
||||
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
|
||||
const auto bindings = GetBindings(controller);
|
||||
const auto binding_left_x = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_LEFTX);
|
||||
const auto binding_left_y = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_LEFTY);
|
||||
if (params.Has("guid2")) {
|
||||
const auto identifier = joystick2->GetPadIdentifier();
|
||||
PreSetController(identifier);
|
||||
PreSetAxis(identifier, binding_left_x.value.axis);
|
||||
PreSetAxis(identifier, binding_left_y.value.axis);
|
||||
const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis);
|
||||
const auto left_offset_y = GetAxis(identifier, binding_left_y.value.axis);
|
||||
PreSetAxis(identifier, binding_left_x.input.axis.axis);
|
||||
PreSetAxis(identifier, binding_left_y.input.axis.axis);
|
||||
const auto left_offset_x = -GetAxis(identifier, binding_left_x.input.axis.axis);
|
||||
const auto left_offset_y = GetAxis(identifier, binding_left_y.input.axis.axis);
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::LStick,
|
||||
BuildParamPackageForAnalog(identifier, binding_left_x.value.axis,
|
||||
binding_left_y.value.axis,
|
||||
BuildParamPackageForAnalog(identifier, binding_left_x.input.axis.axis,
|
||||
binding_left_y.input.axis.axis,
|
||||
left_offset_x, left_offset_y));
|
||||
} else {
|
||||
const auto identifier = joystick->GetPadIdentifier();
|
||||
PreSetController(identifier);
|
||||
PreSetAxis(identifier, binding_left_x.value.axis);
|
||||
PreSetAxis(identifier, binding_left_y.value.axis);
|
||||
const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis);
|
||||
const auto left_offset_y = GetAxis(identifier, binding_left_y.value.axis);
|
||||
PreSetAxis(identifier, binding_left_x.input.axis.axis);
|
||||
PreSetAxis(identifier, binding_left_y.input.axis.axis);
|
||||
const auto left_offset_x = -GetAxis(identifier, binding_left_x.input.axis.axis);
|
||||
const auto left_offset_y = GetAxis(identifier, binding_left_y.input.axis.axis);
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::LStick,
|
||||
BuildParamPackageForAnalog(identifier, binding_left_x.value.axis,
|
||||
binding_left_y.value.axis,
|
||||
BuildParamPackageForAnalog(identifier, binding_left_x.input.axis.axis,
|
||||
binding_left_y.input.axis.axis,
|
||||
left_offset_x, left_offset_y));
|
||||
}
|
||||
const auto& binding_right_x =
|
||||
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||
const auto& binding_right_y =
|
||||
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||
const auto binding_right_x = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_RIGHTX);
|
||||
const auto binding_right_y = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_RIGHTY);
|
||||
const auto identifier = joystick->GetPadIdentifier();
|
||||
PreSetController(identifier);
|
||||
PreSetAxis(identifier, binding_right_x.value.axis);
|
||||
PreSetAxis(identifier, binding_right_y.value.axis);
|
||||
const auto right_offset_x = -GetAxis(identifier, binding_right_x.value.axis);
|
||||
const auto right_offset_y = GetAxis(identifier, binding_right_y.value.axis);
|
||||
PreSetAxis(identifier, binding_right_x.input.axis.axis);
|
||||
PreSetAxis(identifier, binding_right_y.input.axis.axis);
|
||||
const auto right_offset_x = -GetAxis(identifier, binding_right_x.input.axis.axis);
|
||||
const auto right_offset_y = GetAxis(identifier, binding_right_y.input.axis.axis);
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::RStick,
|
||||
BuildParamPackageForAnalog(identifier, binding_right_x.value.axis,
|
||||
binding_right_y.value.axis, right_offset_x,
|
||||
BuildParamPackageForAnalog(identifier, binding_right_x.input.axis.axis,
|
||||
binding_right_y.input.axis.axis, right_offset_x,
|
||||
right_offset_y));
|
||||
return mapping;
|
||||
}
|
||||
|
|
@ -1102,19 +1255,16 @@ bool SDLDriver::IsStickInverted(const Common::ParamPackage& params) {
|
|||
|
||||
const auto& axis_x = params.Get("axis_x", 0);
|
||||
const auto& axis_y = params.Get("axis_y", 0);
|
||||
const auto& binding_left_x =
|
||||
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
|
||||
const auto& binding_right_x =
|
||||
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||
const auto& binding_left_y =
|
||||
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
|
||||
const auto& binding_right_y =
|
||||
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||
const auto bindings = GetBindings(controller);
|
||||
const auto binding_left_x = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_LEFTX);
|
||||
const auto binding_right_x = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_RIGHTX);
|
||||
const auto binding_left_y = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_LEFTY);
|
||||
const auto binding_right_y = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_RIGHTY);
|
||||
|
||||
if (axis_x != binding_left_y.value.axis && axis_x != binding_right_y.value.axis) {
|
||||
if (axis_x != binding_left_y.input.axis.axis && axis_x != binding_right_y.input.axis.axis) {
|
||||
return false;
|
||||
}
|
||||
if (axis_y != binding_left_x.value.axis && axis_y != binding_right_x.value.axis) {
|
||||
if (axis_y != binding_left_x.input.axis.axis && axis_y != binding_right_x.input.axis.axis) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -11,25 +11,22 @@
|
|||
#include <thread>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
union SDL_Event;
|
||||
using SDL_GameController = struct _SDL_GameController;
|
||||
using SDL_Joystick = struct _SDL_Joystick;
|
||||
using SDL_JoystickID = s32;
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class SDLJoystick;
|
||||
|
||||
using ButtonBindings =
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 20>;
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GamepadButton>, 20>;
|
||||
using ZButtonBindings =
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GamepadAxis>, 2>;
|
||||
|
||||
class SDLDriver : public InputEngine {
|
||||
public:
|
||||
|
|
@ -46,6 +43,7 @@ public:
|
|||
|
||||
/// Get the nth joystick with the corresponding GUID
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickByGamepadID(SDL_JoystickID sdl_id);
|
||||
|
||||
/**
|
||||
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so
|
||||
|
|
@ -72,7 +70,7 @@ public:
|
|||
bool IsVibrationEnabled(const PadIdentifier& identifier) override;
|
||||
|
||||
private:
|
||||
void InitJoystick(int joystick_index);
|
||||
void InitJoystick(SDL_JoystickID joystick_id);
|
||||
void CloseJoystick(SDL_Joystick* sdl_joystick);
|
||||
|
||||
/// Needs to be called before SDL_QuitSubSystem.
|
||||
|
|
@ -92,7 +90,7 @@ private:
|
|||
Common::ParamPackage BuildMotionParam(int port, const Common::UUID& guid) const;
|
||||
|
||||
Common::ParamPackage BuildParamPackageForBinding(
|
||||
int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const;
|
||||
int port, const Common::UUID& guid, const SDL_GamepadBinding& binding) const;
|
||||
|
||||
Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
|
||||
int axis_y, float offset_x,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -10,7 +13,7 @@
|
|||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <SDL_hidapi.h>
|
||||
#include <SDL3/SDL_hidapi.h>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
#ifdef ENABLE_LIBUSB
|
||||
#include "input_common/drivers/gc_adapter.h"
|
||||
#endif
|
||||
#ifdef HAVE_SDL2
|
||||
#ifdef HAVE_SDL3
|
||||
#include "input_common/drivers/joycon.h"
|
||||
#include "input_common/drivers/sdl_driver.h"
|
||||
#endif
|
||||
|
|
@ -90,7 +90,7 @@ struct InputSubsystem::Impl {
|
|||
#endif
|
||||
RegisterEngine("virtual_amiibo", virtual_amiibo);
|
||||
RegisterEngine("virtual_gamepad", virtual_gamepad);
|
||||
#ifdef HAVE_SDL2
|
||||
#ifdef HAVE_SDL3
|
||||
RegisterEngine("sdl", sdl);
|
||||
RegisterEngine("joycon", joycon);
|
||||
#endif
|
||||
|
|
@ -124,7 +124,7 @@ struct InputSubsystem::Impl {
|
|||
#endif
|
||||
UnregisterEngine(virtual_amiibo);
|
||||
UnregisterEngine(virtual_gamepad);
|
||||
#ifdef HAVE_SDL2
|
||||
#ifdef HAVE_SDL3
|
||||
UnregisterEngine(sdl);
|
||||
UnregisterEngine(joycon);
|
||||
#endif
|
||||
|
|
@ -154,7 +154,7 @@ struct InputSubsystem::Impl {
|
|||
#endif
|
||||
auto udp_devices = udp_client->GetInputDevices();
|
||||
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
|
||||
#ifdef HAVE_SDL2
|
||||
#ifdef HAVE_SDL3
|
||||
auto joycon_devices = joycon->GetInputDevices();
|
||||
devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end());
|
||||
auto sdl_devices = sdl->GetInputDevices();
|
||||
|
|
@ -189,7 +189,7 @@ struct InputSubsystem::Impl {
|
|||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client;
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
#ifdef HAVE_SDL3
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl;
|
||||
}
|
||||
|
|
@ -280,7 +280,7 @@ struct InputSubsystem::Impl {
|
|||
if (engine == virtual_gamepad->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
#ifdef HAVE_SDL3
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -301,7 +301,7 @@ struct InputSubsystem::Impl {
|
|||
gcadapter->BeginConfiguration();
|
||||
#endif
|
||||
udp_client->BeginConfiguration();
|
||||
#ifdef HAVE_SDL2
|
||||
#ifdef HAVE_SDL3
|
||||
sdl->BeginConfiguration();
|
||||
joycon->BeginConfiguration();
|
||||
#endif
|
||||
|
|
@ -317,7 +317,7 @@ struct InputSubsystem::Impl {
|
|||
gcadapter->EndConfiguration();
|
||||
#endif
|
||||
udp_client->EndConfiguration();
|
||||
#ifdef HAVE_SDL2
|
||||
#ifdef HAVE_SDL3
|
||||
sdl->EndConfiguration();
|
||||
joycon->EndConfiguration();
|
||||
#endif
|
||||
|
|
@ -325,7 +325,7 @@ struct InputSubsystem::Impl {
|
|||
|
||||
void PumpEvents() const {
|
||||
update_engine->PumpEvents();
|
||||
#ifdef HAVE_SDL2
|
||||
#ifdef HAVE_SDL3
|
||||
sdl->PumpEvents();
|
||||
#endif
|
||||
}
|
||||
|
|
@ -350,7 +350,7 @@ struct InputSubsystem::Impl {
|
|||
std::shared_ptr<GCAdapter> gcadapter;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
#ifdef HAVE_SDL3
|
||||
std::shared_ptr<SDLDriver> sdl;
|
||||
std::shared_ptr<Joycons> joycon;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -466,6 +466,6 @@ if (NOT MSVC AND (APPLE OR NOT YUZU_STATIC_BUILD))
|
|||
endif()
|
||||
|
||||
# Remember that the linker is incredibly stupid.
|
||||
target_link_libraries(yuzu PRIVATE OpenSSL::SSL OpenSSL::Crypto SDL2::SDL2)
|
||||
target_link_libraries(yuzu PRIVATE OpenSSL::SSL OpenSSL::Crypto SDL3::SDL3)
|
||||
|
||||
create_target_directory_groups(yuzu)
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
|||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/shader_notify.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,20 +17,20 @@ endfunction()
|
|||
|
||||
if (ENABLE_OPENGL)
|
||||
list(APPEND OPENGL_SOURCES
|
||||
emu_window/emu_window_sdl2_gl.cpp
|
||||
emu_window/emu_window_sdl2_gl.h
|
||||
emu_window/emu_window_sdl3_gl.cpp
|
||||
emu_window/emu_window_sdl3_gl.h
|
||||
)
|
||||
else()
|
||||
set(OPENGL_SOURCES "")
|
||||
endif()
|
||||
|
||||
add_executable(yuzu-cmd
|
||||
emu_window/emu_window_sdl2.cpp
|
||||
emu_window/emu_window_sdl2.h
|
||||
emu_window/emu_window_sdl2_null.cpp
|
||||
emu_window/emu_window_sdl2_null.h
|
||||
emu_window/emu_window_sdl2_vk.cpp
|
||||
emu_window/emu_window_sdl2_vk.h
|
||||
emu_window/emu_window_sdl3.cpp
|
||||
emu_window/emu_window_sdl3.h
|
||||
emu_window/emu_window_sdl3_null.cpp
|
||||
emu_window/emu_window_sdl3_null.h
|
||||
emu_window/emu_window_sdl3_vk.cpp
|
||||
emu_window/emu_window_sdl3_vk.h
|
||||
sdl_config.cpp
|
||||
sdl_config.h
|
||||
yuzu.cpp
|
||||
|
|
@ -50,7 +50,7 @@ target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
|||
create_resource("../../dist/eden.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon")
|
||||
target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR})
|
||||
|
||||
target_link_libraries(yuzu-cmd PRIVATE SDL2::SDL2)
|
||||
target_link_libraries(yuzu-cmd PRIVATE SDL3::SDL3)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
install(TARGETS yuzu-cmd)
|
||||
|
|
|
|||
|
|
@ -1,98 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
#include "yuzu_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("Eden {} | {}-{} (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, "Eden 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>();
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/scm_rev.h"
|
||||
|
|
@ -15,26 +16,25 @@
|
|||
#include "input_common/drivers/mouse.h"
|
||||
#include "input_common/drivers/touch_screen.h"
|
||||
#include "input_common/main.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl3.h"
|
||||
#include "yuzu_cmd/yuzu_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->Initialize();
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}, Exiting...", SDL_GetError());
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD)) {
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize SDL3: {}, Exiting...", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
SDL_SetMainReady();
|
||||
}
|
||||
|
||||
EmuWindow_SDL2::~EmuWindow_SDL2() {
|
||||
EmuWindow_SDL3::~EmuWindow_SDL3() {
|
||||
system.HIDCore().UnloadInputDevices();
|
||||
input_subsystem->Shutdown();
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
|
||||
InputCommon::MouseButton EmuWindow_SDL3::SDLButtonToMouseButton(u32 button) const {
|
||||
switch (button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
return InputCommon::MouseButton::Left;
|
||||
|
|
@ -52,7 +52,7 @@ InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) cons
|
|||
}
|
||||
|
||||
/// @brief Translates pixel position to float position
|
||||
EmuWindow_SDL2::FloatPairNonHFA EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 touch_y) const {
|
||||
EmuWindow_SDL3::FloatPairNonHFA EmuWindow_SDL3::MouseToTouchPos(s32 touch_x, s32 touch_y) const {
|
||||
int w = 0, h = 0;
|
||||
SDL_GetWindowSize(render_window, &w, &h);
|
||||
const float fx = float(touch_x) / w;
|
||||
|
|
@ -64,9 +64,9 @@ EmuWindow_SDL2::FloatPairNonHFA EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32
|
|||
};
|
||||
}
|
||||
|
||||
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);
|
||||
if (state == SDL_PRESSED) {
|
||||
if (state != 0) {
|
||||
auto const [touch_x, touch_y, _] = MouseToTouchPos(x, y);
|
||||
input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
|
||||
input_subsystem->GetMouse()->PressMouseButton(mouse_button);
|
||||
|
|
@ -76,64 +76,70 @@ 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) {
|
||||
auto const [touch_x, touch_y, _] = MouseToTouchPos(x, y);
|
||||
input_subsystem->GetMouse()->Move(x, y, 0, 0);
|
||||
input_subsystem->GetMouse()->MouseMove(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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerUp() {
|
||||
void EmuWindow_SDL3::OnFingerUp() {
|
||||
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
|
||||
if (state == SDL_PRESSED) {
|
||||
void EmuWindow_SDL3::OnKeyEvent(int key, u8 state) {
|
||||
if (state != 0) {
|
||||
input_subsystem->GetKeyboard()->PressKey(static_cast<std::size_t>(key));
|
||||
} else if (state == SDL_RELEASED) {
|
||||
} else {
|
||||
input_subsystem->GetKeyboard()->ReleaseKey(static_cast<std::size_t>(key));
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuWindow_SDL2::IsOpen() const {
|
||||
bool EmuWindow_SDL3::IsOpen() const {
|
||||
return is_open;
|
||||
}
|
||||
|
||||
bool EmuWindow_SDL2::IsShown() const {
|
||||
bool EmuWindow_SDL3::IsShown() const {
|
||||
return is_shown;
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnResize() {
|
||||
void EmuWindow_SDL3::OnResize() {
|
||||
int width, height;
|
||||
SDL_GL_GetDrawableSize(render_window, &width, &height);
|
||||
SDL_GetWindowSizeInPixels(render_window, &width, &height);
|
||||
UpdateCurrentFramebufferLayout(width, height);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::ShowCursor(bool show_cursor) {
|
||||
SDL_ShowCursor(show_cursor ? SDL_ENABLE : SDL_DISABLE);
|
||||
void EmuWindow_SDL3::ShowCursor(bool show_cursor) {
|
||||
if (show_cursor) {
|
||||
SDL_ShowCursor();
|
||||
} else {
|
||||
SDL_HideCursor();
|
||||
}
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::Fullscreen() {
|
||||
void EmuWindow_SDL3::Fullscreen() {
|
||||
SDL_DisplayMode display_mode;
|
||||
switch (Settings::values.fullscreen_mode.GetValue()) {
|
||||
case Settings::FullscreenMode::Exclusive:
|
||||
// Set window size to render size before entering fullscreen -- SDL2 does not resize window
|
||||
// to display dimensions automatically in this mode.
|
||||
if (SDL_GetDesktopDisplayMode(0, &display_mode) == 0) {
|
||||
// Set window size to render size before entering fullscreen in exclusive mode.
|
||||
if (const SDL_DisplayMode* display_mode_ptr =
|
||||
SDL_GetDesktopDisplayMode(SDL_GetDisplayForWindow(render_window))) {
|
||||
display_mode = *display_mode_ptr;
|
||||
SDL_SetWindowSize(render_window, display_mode.w, display_mode.h);
|
||||
SDL_SetWindowFullscreenMode(render_window, &display_mode);
|
||||
} else {
|
||||
LOG_ERROR(Frontend, "SDL_GetDesktopDisplayMode failed: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
|
||||
if (SDL_SetWindowFullscreen(render_window, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +147,8 @@ void EmuWindow_SDL2::Fullscreen() {
|
|||
LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
|
||||
[[fallthrough]];
|
||||
case Settings::FullscreenMode::Borderless:
|
||||
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
|
||||
SDL_SetWindowFullscreenMode(render_window, nullptr);
|
||||
if (SDL_SetWindowFullscreen(render_window, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +163,7 @@ void EmuWindow_SDL2::Fullscreen() {
|
|||
}
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::WaitEvent() {
|
||||
void EmuWindow_SDL3::WaitEvent() {
|
||||
// Called on main thread
|
||||
SDL_Event event;
|
||||
|
||||
|
|
@ -174,52 +181,52 @@ void EmuWindow_SDL2::WaitEvent() {
|
|||
}
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
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;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_RESIZED:
|
||||
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
||||
case SDL_EVENT_WINDOW_MAXIMIZED:
|
||||
case SDL_EVENT_WINDOW_RESTORED:
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
|
||||
case SDL_EVENT_WINDOW_MINIMIZED:
|
||||
is_shown = false;
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
case SDL_EVENT_WINDOW_EXPOSED:
|
||||
is_shown = true;
|
||||
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.scancode), event.key.down ? 1 : 0);
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
// ignore if it came from touch
|
||||
if (event.button.which != SDL_TOUCH_MOUSEID)
|
||||
OnMouseMotion(event.motion.x, event.motion.y);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
// ignore if it came from touch
|
||||
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.down ? 1 : 0,
|
||||
static_cast<s32>(event.button.x), static_cast<s32>(event.button.y));
|
||||
}
|
||||
break;
|
||||
case SDL_FINGERDOWN:
|
||||
case SDL_EVENT_FINGER_DOWN:
|
||||
OnFingerDown(event.tfinger.x, event.tfinger.y,
|
||||
static_cast<std::size_t>(event.tfinger.touchId));
|
||||
static_cast<std::size_t>(event.tfinger.touchID));
|
||||
break;
|
||||
case SDL_FINGERMOTION:
|
||||
case SDL_EVENT_FINGER_MOTION:
|
||||
OnFingerMotion(event.tfinger.x, event.tfinger.y,
|
||||
static_cast<std::size_t>(event.tfinger.touchId));
|
||||
static_cast<std::size_t>(event.tfinger.touchID));
|
||||
break;
|
||||
case SDL_FINGERUP:
|
||||
case SDL_EVENT_FINGER_UP:
|
||||
OnFingerUp();
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
case SDL_EVENT_QUIT:
|
||||
is_open = false;
|
||||
break;
|
||||
default:
|
||||
|
|
@ -241,22 +248,22 @@ void EmuWindow_SDL2::WaitEvent() {
|
|||
}
|
||||
|
||||
// Credits to Samantas5855 and others for this function.
|
||||
void EmuWindow_SDL2::SetWindowIcon() {
|
||||
SDL_RWops* const yuzu_icon_stream = SDL_RWFromConstMem((void*)yuzu_icon, yuzu_icon_size);
|
||||
void EmuWindow_SDL3::SetWindowIcon() {
|
||||
SDL_IOStream* const yuzu_icon_stream = SDL_IOFromConstMem((void*)yuzu_icon, yuzu_icon_size);
|
||||
if (yuzu_icon_stream == nullptr) {
|
||||
LOG_WARNING(Frontend, "Failed to create Eden icon stream.");
|
||||
return;
|
||||
}
|
||||
SDL_Surface* const window_icon = SDL_LoadBMP_RW(yuzu_icon_stream, 1);
|
||||
SDL_Surface* const window_icon = SDL_LoadBMP_IO(yuzu_icon_stream, true);
|
||||
if (window_icon == nullptr) {
|
||||
LOG_WARNING(Frontend, "Failed to read BMP from stream.");
|
||||
return;
|
||||
}
|
||||
// The icon is attached to the window pointer
|
||||
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);
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -22,10 +23,10 @@ class InputSubsystem;
|
|||
enum class MouseButton;
|
||||
} // namespace InputCommon
|
||||
|
||||
class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
|
||||
class EmuWindow_SDL3 : public Core::Frontend::EmuWindow {
|
||||
public:
|
||||
explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_);
|
||||
~EmuWindow_SDL2();
|
||||
explicit EmuWindow_SDL3(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_);
|
||||
~EmuWindow_SDL3();
|
||||
|
||||
/// Whether the window is still open, and a close request hasn't yet been sent
|
||||
bool IsOpen() const;
|
||||
|
|
@ -85,7 +86,7 @@ protected:
|
|||
/// Is the window being shown?
|
||||
bool is_shown = true;
|
||||
|
||||
/// Internal SDL2 render window
|
||||
/// Internal SDL3 render window
|
||||
SDL_Window* render_window{};
|
||||
|
||||
/// Keeps track of how often to update the title bar during gameplay
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
#include <string>
|
||||
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <fmt/ranges.h>
|
||||
#include <glad/glad.h>
|
||||
|
|
@ -20,7 +20,13 @@
|
|||
#include "core/core.h"
|
||||
#include "input_common/main.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl3_gl.h"
|
||||
|
||||
namespace {
|
||||
void* SDLGLGetProcAddress(const char* proc_name) {
|
||||
return reinterpret_cast<void*>(SDL_GL_GetProcAddress(proc_name));
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
class SDLGLContext : public Core::Frontend::GraphicsContext {
|
||||
public:
|
||||
|
|
@ -30,7 +36,7 @@ public:
|
|||
|
||||
~SDLGLContext() {
|
||||
DoneCurrent();
|
||||
SDL_GL_DeleteContext(context);
|
||||
SDL_GL_DestroyContext(context);
|
||||
}
|
||||
|
||||
void SwapBuffers() override {
|
||||
|
|
@ -58,7 +64,7 @@ private:
|
|||
bool is_current = false;
|
||||
};
|
||||
|
||||
bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
|
||||
bool EmuWindow_SDL3_GL::SupportsRequiredGLExtensions() {
|
||||
std::vector<std::string_view> unsupported_ext{};
|
||||
#ifdef HAS_OPENGL
|
||||
// Extensions required to support some texture formats.
|
||||
|
|
@ -72,9 +78,9 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
|
|||
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)
|
||||
: 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_MINOR_VERSION, 6);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
|
||||
|
|
@ -92,14 +98,13 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste
|
|||
std::string window_title = fmt::format("{} | {}-{}", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
render_window =
|
||||
SDL_CreateWindow(window_title.c_str(),
|
||||
SDL_WINDOWPOS_UNDEFINED, // x position
|
||||
SDL_WINDOWPOS_UNDEFINED, // y position
|
||||
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
SDL_CreateWindow(window_title.c_str(), Layout::ScreenUndocked::Width,
|
||||
Layout::ScreenUndocked::Height,
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE |
|
||||
SDL_WINDOW_HIGH_PIXEL_DENSITY);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -116,15 +121,15 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste
|
|||
core_context = CreateSharedContext();
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
|
||||
if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDLGLGetProcAddress))) {
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
|
@ -142,11 +147,11 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste
|
|||
Settings::LogSettings();
|
||||
}
|
||||
|
||||
EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
|
||||
EmuWindow_SDL3_GL::~EmuWindow_SDL3_GL() {
|
||||
core_context.reset();
|
||||
SDL_GL_DeleteContext(window_context);
|
||||
SDL_GL_DestroyContext(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);
|
||||
}
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <SDL3/SDL.h>
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl3.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
|
|
@ -15,11 +19,11 @@ namespace InputCommon {
|
|||
class InputSubsystem;
|
||||
}
|
||||
|
||||
class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
|
||||
class EmuWindow_SDL3_GL final : public EmuWindow_SDL3 {
|
||||
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);
|
||||
~EmuWindow_SDL2_GL();
|
||||
~EmuWindow_SDL3_GL();
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
|
||||
|
|
@ -27,8 +31,6 @@ private:
|
|||
/// Whether the GPU and driver supports the OpenGL extension required
|
||||
bool SupportsRequiredGLExtensions();
|
||||
|
||||
using SDL_GLContext = void*;
|
||||
|
||||
/// The OpenGL context associated with the window
|
||||
SDL_GLContext window_context;
|
||||
|
||||
|
|
@ -13,25 +13,25 @@
|
|||
#include "common/logging.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "video_core/renderer_null/renderer_null.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl3_null.h"
|
||||
|
||||
#ifdef YUZU_USE_EXTERNAL_SDL2
|
||||
#ifdef YUZU_USE_EXTERNAL_SDL3
|
||||
// Include this before SDL.h to prevent the external from including a dummy
|
||||
#define USING_GENERATED_CONFIG_H
|
||||
#include <SDL_config.h>
|
||||
#include <SDL3/SDL_config.h>
|
||||
#endif
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL3/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)
|
||||
: EmuWindow_SDL2{input_subsystem_, system_} {
|
||||
: EmuWindow_SDL3{input_subsystem_, system_} {
|
||||
const std::string window_title = fmt::format("Eden {} | {}-{} (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_CreateWindow(window_title.c_str(), Layout::ScreenUndocked::Width,
|
||||
Layout::ScreenUndocked::Height,
|
||||
SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY);
|
||||
|
||||
SetWindowIcon();
|
||||
|
||||
|
|
@ -47,8 +47,8 @@ EmuWindow_SDL2_Null::EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subs
|
|||
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>();
|
||||
}
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -6,7 +9,7 @@
|
|||
#include <memory>
|
||||
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl3.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
|
|
@ -16,11 +19,11 @@ namespace InputCommon {
|
|||
class InputSubsystem;
|
||||
}
|
||||
|
||||
class EmuWindow_SDL2_Null final : public EmuWindow_SDL2 {
|
||||
class EmuWindow_SDL3_Null final : public EmuWindow_SDL3 {
|
||||
public:
|
||||
explicit EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_,
|
||||
explicit EmuWindow_SDL3_Null(InputCommon::InputSubsystem* input_subsystem_,
|
||||
Core::System& system, bool fullscreen);
|
||||
~EmuWindow_SDL2_Null() override;
|
||||
~EmuWindow_SDL3_Null() override;
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
};
|
||||
99
src/yuzu_cmd/emu_window/emu_window_sdl3_vk.cpp
Normal file
99
src/yuzu_cmd/emu_window/emu_window_sdl3_vk.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl3_vk.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_properties.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("Eden {} | {}-{} (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_WINDOW_HIGH_PIXEL_DENSITY);
|
||||
|
||||
const SDL_PropertiesID window_props = SDL_GetWindowProperties(render_window);
|
||||
|
||||
SetWindowIcon();
|
||||
|
||||
if (fullscreen) {
|
||||
Fullscreen();
|
||||
ShowCursor(false);
|
||||
}
|
||||
|
||||
if (void* hwnd =
|
||||
SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr)) {
|
||||
window_info.type = Core::Frontend::WindowSystemType::Windows;
|
||||
window_info.render_surface = hwnd;
|
||||
} else if (void* wl_display =
|
||||
SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER,
|
||||
nullptr);
|
||||
wl_display != nullptr) {
|
||||
void* wl_surface =
|
||||
SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
|
||||
if (wl_surface == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Wayland surface is unavailable");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
window_info.type = Core::Frontend::WindowSystemType::Wayland;
|
||||
window_info.display_connection = wl_display;
|
||||
window_info.render_surface = wl_surface;
|
||||
} else if (void* x11_display =
|
||||
SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER,
|
||||
nullptr);
|
||||
x11_display != nullptr) {
|
||||
const auto x11_window =
|
||||
SDL_GetNumberProperty(window_props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
||||
if (x11_window == 0) {
|
||||
LOG_CRITICAL(Frontend, "X11 window handle is unavailable");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
window_info.type = Core::Frontend::WindowSystemType::X11;
|
||||
window_info.display_connection = x11_display;
|
||||
window_info.render_surface = reinterpret_cast<void*>(static_cast<uintptr_t>(x11_window));
|
||||
} else if (SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER,
|
||||
nullptr) != nullptr) {
|
||||
window_info.type = Core::Frontend::WindowSystemType::Cocoa;
|
||||
window_info.render_surface = SDL_Metal_CreateView(render_window);
|
||||
} else if (void* android_window =
|
||||
SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER,
|
||||
nullptr);
|
||||
android_window != nullptr) {
|
||||
window_info.type = Core::Frontend::WindowSystemType::Android;
|
||||
window_info.render_surface = android_window;
|
||||
} else {
|
||||
LOG_CRITICAL(Frontend, "Unable to determine native window backend from SDL properties");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
OnResize();
|
||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||
SDL_PumpEvents();
|
||||
LOG_INFO(Frontend, "Eden 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>();
|
||||
}
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -6,7 +9,7 @@
|
|||
#include <memory>
|
||||
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl3.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
|
|
@ -16,11 +19,11 @@ namespace InputCommon {
|
|||
class InputSubsystem;
|
||||
}
|
||||
|
||||
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
|
||||
class EmuWindow_SDL3_VK final : public EmuWindow_SDL3 {
|
||||
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);
|
||||
~EmuWindow_SDL2_VK() override;
|
||||
~EmuWindow_SDL3_VK() override;
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
};
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
// SDL will break our main function in yuzu-cmd if we don't define this before adding SDL.h
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "input_common/main.h"
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@
|
|||
#include "network/network.h"
|
||||
#include "sdl_config.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl3.h"
|
||||
#ifdef HAS_OPENGL
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl3_gl.h"
|
||||
#endif
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl3_null.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl3_vk.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
// windows.h needs to be included before shellapi.h
|
||||
|
|
@ -349,20 +349,20 @@ int main(int argc, char** argv) {
|
|||
// Apply the command line arguments
|
||||
system.ApplySettings();
|
||||
|
||||
std::unique_ptr<EmuWindow_SDL2> emu_window;
|
||||
std::unique_ptr<EmuWindow_SDL3> emu_window;
|
||||
switch (Settings::values.renderer_backend.GetValue()) {
|
||||
#ifdef HAS_OPENGL
|
||||
case Settings::RendererBackend::OpenGL_GLSL:
|
||||
case Settings::RendererBackend::OpenGL_GLASM:
|
||||
case Settings::RendererBackend::OpenGL_SPIRV:
|
||||
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;
|
||||
#endif
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
LOG_CRITICAL(Frontend, "Invalid renderer backend");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue