Corrected overzealous removal of SDL2 frontend

This commit is contained in:
OpenSauce04 2024-09-04 00:32:57 +01:00 committed by OpenSauce
parent 48adc58f88
commit 91294919bb
17 changed files with 1513 additions and 0 deletions

3
.gitmodules vendored
View file

@ -52,6 +52,9 @@
[submodule "libyuv"]
path = externals/libyuv
url = https://github.com/lemenkov/libyuv.git
[submodule "sdl2"]
path = externals/sdl2/SDL
url = https://github.com/libsdl-org/SDL
[submodule "cryptopp-cmake"]
path = externals/cryptopp-cmake
url = https://github.com/abdes/cryptopp-cmake.git

View file

@ -56,6 +56,9 @@ else()
set(DEFAULT_ENABLE_LTO OFF)
endif()
option(ENABLE_SDL2 "Enable using SDL2" ON)
option(USE_SYSTEM_SDL2 "Use the system SDL2 lib (instead of the bundled one)" OFF)
# Set bundled qt as dependent options.
option(ENABLE_QT "Enable the Qt frontend" ON)
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
@ -393,6 +396,15 @@ if (NOT USE_SYSTEM_BOOST)
add_library(Boost::iostreams ALIAS boost_iostreams)
endif()
# SDL2
if (ENABLE_SDL2 AND USE_SYSTEM_SDL2)
find_package(SDL2 REQUIRED)
add_library(SDL2 INTERFACE)
target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARY}")
target_include_directories(SDL2 INTERFACE "${SDL2_INCLUDE_DIR}")
add_library(SDL2::SDL2 ALIAS SDL2)
endif()
if (ENABLE_LIBUSB AND USE_SYSTEM_LIBUSB)
include(FindPkgConfig)
find_package(LibUSB)

View file

@ -183,6 +183,14 @@ endif()
# Teakra
add_subdirectory(teakra EXCLUDE_FROM_ALL)
# SDL2
if (ENABLE_SDL2 AND NOT USE_SYSTEM_SDL2)
if (MSVC)
set (SDL_LIBC ON)
endif()
add_subdirectory(sdl2)
endif()
# libusb
if (ENABLE_LIBUSB AND NOT USE_SYSTEM_LIBUSB)
add_subdirectory(libusb)

25
externals/sdl2/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,25 @@
# Configure static library build
set(SDL_SHARED OFF CACHE BOOL "")
set(SDL_STATIC ON CACHE BOOL "")
# Subsystems
set(SDL_ATOMIC ON CACHE BOOL "")
set(SDL_AUDIO ON CACHE BOOL "")
set(SDL_VIDEO ON CACHE BOOL "")
set(SDL_RENDER OFF CACHE BOOL "")
set(SDL_EVENTS ON CACHE BOOL "")
set(SDL_JOYSTICK ON CACHE BOOL "")
set(SDL_HAPTIC OFF CACHE BOOL "")
set(SDL_HIDAPI ON CACHE BOOL "")
set(SDL_POWER OFF CACHE BOOL "")
set(SDL_THREADS ON CACHE BOOL "")
set(SDL_TIMERS ON CACHE BOOL "")
set(SDL_FILE ON CACHE BOOL "")
set(SDL_LOADSO ON CACHE BOOL "")
set(SDL_CPUINFO ON CACHE BOOL "")
set(SDL_FILESYSTEM OFF CACHE BOOL "")
set(SDL_DLOPEN ON CACHE BOOL "")
set(SDL_SENSOR OFF CACHE BOOL "")
set(SDL_LOCALE OFF CACHE BOOL "")
add_subdirectory(SDL)

1
externals/sdl2/SDL vendored Submodule

@ -0,0 +1 @@
Subproject commit ba2f78a0069118a6c583f1fbf1420144ffa35bad

View file

@ -13,6 +13,7 @@
#include "common/math_util.h"
#include "common/param_package.h"
#include "input_common/main.h"
#include "input_common/sdl/sdl.h"
#include "jni/input_manager.h"
#include "jni/ndk_motion.h"

View file

@ -36,6 +36,7 @@ add_library(audio_core STATIC
time_stretch.cpp
time_stretch.h
$<$<BOOL:${ENABLE_SDL2}>:sdl2_sink.cpp sdl2_sink.h>
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h cubeb_input.cpp cubeb_input.h>
$<$<BOOL:${ENABLE_OPENAL}>:openal_input.cpp openal_input.h openal_sink.cpp openal_sink.h>
)
@ -45,6 +46,11 @@ create_target_directory_groups(audio_core)
target_link_libraries(audio_core PUBLIC lime_common lime_core)
target_link_libraries(audio_core PRIVATE faad2 SoundTouch teakra)
if(ENABLE_SDL2)
target_link_libraries(audio_core PRIVATE SDL2::SDL2)
target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
endif()
if(ENABLE_CUBEB)
target_link_libraries(audio_core PRIVATE cubeb)
target_compile_definitions(audio_core PUBLIC HAVE_CUBEB)

View file

@ -0,0 +1,108 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string>
#include <vector>
#include <SDL.h>
#include "audio_core/audio_types.h"
#include "audio_core/sdl2_sink.h"
#include "common/assert.h"
#include "common/logging/log.h"
namespace AudioCore {
struct SDL2Sink::Impl {
unsigned int sample_rate = 0;
SDL_AudioDeviceID audio_device_id = 0;
std::function<void(s16*, std::size_t)> cb;
static void Callback(void* impl_, u8* buffer, int buffer_size_in_bytes);
};
SDL2Sink::SDL2Sink(std::string device_name) : impl(std::make_unique<Impl>()) {
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed with: {}", SDL_GetError());
impl->audio_device_id = 0;
return;
}
SDL_AudioSpec desired_audiospec;
SDL_zero(desired_audiospec);
desired_audiospec.format = AUDIO_S16;
desired_audiospec.channels = 2;
desired_audiospec.freq = native_sample_rate;
desired_audiospec.samples = 512;
desired_audiospec.userdata = impl.get();
desired_audiospec.callback = &Impl::Callback;
SDL_AudioSpec obtained_audiospec;
SDL_zero(obtained_audiospec);
const char* device = nullptr;
if (device_name != auto_device_name && !device_name.empty()) {
device = device_name.c_str();
}
impl->audio_device_id =
SDL_OpenAudioDevice(device, false, &desired_audiospec, &obtained_audiospec, 0);
if (impl->audio_device_id <= 0) {
LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed with code {} for device \"{}\"",
impl->audio_device_id, device_name);
return;
}
impl->sample_rate = obtained_audiospec.freq;
// SDL2 audio devices start out paused, unpause it:
SDL_PauseAudioDevice(impl->audio_device_id, 0);
}
SDL2Sink::~SDL2Sink() {
if (impl->audio_device_id <= 0)
return;
SDL_CloseAudioDevice(impl->audio_device_id);
}
unsigned int SDL2Sink::GetNativeSampleRate() const {
if (impl->audio_device_id <= 0)
return native_sample_rate;
return impl->sample_rate;
}
void SDL2Sink::SetCallback(std::function<void(s16*, std::size_t)> cb) {
impl->cb = cb;
}
void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) {
Impl* impl = reinterpret_cast<Impl*>(impl_);
if (!impl || !impl->cb)
return;
const std::size_t num_frames = buffer_size_in_bytes / (2 * sizeof(s16));
impl->cb(reinterpret_cast<s16*>(buffer), num_frames);
}
std::vector<std::string> ListSDL2SinkDevices() {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem failed with: {}", SDL_GetError());
return {};
}
std::vector<std::string> device_list;
const int device_count = SDL_GetNumAudioDevices(0);
for (int i = 0; i < device_count; ++i) {
device_list.push_back(SDL_GetAudioDeviceName(i, 0));
}
SDL_QuitSubSystem(SDL_INIT_AUDIO);
return device_list;
}
} // namespace AudioCore

View file

@ -0,0 +1,29 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <memory>
#include "audio_core/sink.h"
namespace AudioCore {
class SDL2Sink final : public Sink {
public:
explicit SDL2Sink(std::string device_id);
~SDL2Sink() override;
unsigned int GetNativeSampleRate() const override;
void SetCallback(std::function<void(s16*, std::size_t)> cb) override;
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
std::vector<std::string> ListSDL2SinkDevices();
} // namespace AudioCore

View file

@ -8,6 +8,9 @@
#include <vector>
#include "audio_core/null_sink.h"
#include "audio_core/sink_details.h"
#ifdef HAVE_SDL2
#include "audio_core/sdl2_sink.h"
#endif
#ifdef HAVE_CUBEB
#include "audio_core/cubeb_sink.h"
#endif
@ -33,6 +36,13 @@ constexpr std::array sink_details = {
return std::make_unique<OpenALSink>(std::string(device_id));
},
&ListOpenALSinkDevices},
#endif
#ifdef HAVE_SDL2
SinkDetails{SinkType::SDL2, "SDL2",
[](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<SDL2Sink>(std::string(device_id));
},
&ListSDL2SinkDevices},
#endif
SinkDetails{SinkType::Null, "None",
[](std::string_view device_id) -> std::unique_ptr<Sink> {

View file

@ -10,6 +10,8 @@ add_library(input_common STATIC
precompiled_headers.h
touch_from_button.cpp
touch_from_button.h
sdl/sdl.cpp
sdl/sdl.h
udp/client.cpp
udp/client.h
udp/protocol.cpp
@ -18,6 +20,15 @@ add_library(input_common STATIC
udp/udp.h
)
if(ENABLE_SDL2)
target_sources(input_common PRIVATE
sdl/sdl_impl.cpp
sdl/sdl_impl.h
)
target_link_libraries(input_common PRIVATE SDL2::SDL2)
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
endif()
if(ENABLE_LIBUSB)
target_sources(input_common PRIVATE
gcadapter/gc_adapter.cpp

View file

@ -13,6 +13,8 @@
#include "input_common/keyboard.h"
#include "input_common/main.h"
#include "input_common/motion_emu.h"
#include "input_common/sdl/sdl.h"
#include "input_common/sdl/sdl_impl.h"
#include "input_common/touch_from_button.h"
#include "input_common/udp/udp.h"
@ -26,6 +28,7 @@ std::shared_ptr<GCAdapter::Adapter> gcadapter;
static std::shared_ptr<Keyboard> keyboard;
static std::shared_ptr<MotionEmu> motion_emu;
static std::unique_ptr<CemuhookUDP::State> udp;
static std::unique_ptr<SDL::State> sdl;
void Init() {
#ifdef ENABLE_GCADAPTER
@ -44,6 +47,8 @@ void Init() {
Input::RegisterFactory<Input::TouchDevice>("touch_from_button",
std::make_shared<TouchFromButtonFactory>());
sdl = SDL::Init();
udp = CemuhookUDP::Init();
}
@ -61,6 +66,7 @@ void Shutdown() {
motion_emu.reset();
Input::UnregisterFactory<Input::TouchDevice>("emu_window");
Input::UnregisterFactory<Input::TouchDevice>("touch_from_button");
sdl.reset();
udp.reset();
}
@ -97,6 +103,10 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
Common::ParamPackage GetControllerButtonBinds(const Common::ParamPackage& params, int button) {
const auto native_button{static_cast<Settings::NativeButton::Values>(button)};
const auto engine{params.Get("engine", "")};
if (engine == "sdl") {
return dynamic_cast<SDL::SDLState*>(sdl.get())->GetSDLControllerButtonBindByGUID(
params.Get("guid", "0"), params.Get("port", 0), native_button);
}
#ifdef ENABLE_GCADAPTER
if (engine == "gcpad") {
return gcbuttons->GetGcTo3DSMappedButton(params.Get("port", 0), native_button);
@ -108,6 +118,10 @@ Common::ParamPackage GetControllerButtonBinds(const Common::ParamPackage& params
Common::ParamPackage GetControllerAnalogBinds(const Common::ParamPackage& params, int analog) {
const auto native_analog{static_cast<Settings::NativeAnalog::Values>(analog)};
const auto engine{params.Get("engine", "")};
if (engine == "sdl") {
return dynamic_cast<SDL::SDLState*>(sdl.get())->GetSDLControllerAnalogBindByGUID(
params.Get("guid", "0"), params.Get("port", 0), native_analog);
}
#ifdef ENABLE_GCADAPTER
if (engine == "gcpad") {
return gcanalog->GetGcTo3DSMappedAnalog(params.Get("port", 0), native_analog);
@ -128,6 +142,9 @@ namespace Polling {
std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
std::vector<std::unique_ptr<DevicePoller>> pollers;
#ifdef HAVE_SDL2
pollers = sdl->GetPollers(type);
#endif
#ifdef ENABLE_GCADAPTER
switch (type) {
case DeviceType::Analog:

View file

@ -0,0 +1,19 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "input_common/sdl/sdl.h"
#ifdef HAVE_SDL2
#include "input_common/sdl/sdl_impl.h"
#endif
namespace InputCommon::SDL {
std::unique_ptr<State> Init() {
#ifdef HAVE_SDL2
return std::make_unique<SDLState>();
#else
return std::make_unique<NullState>();
#endif
}
} // namespace InputCommon::SDL

View file

@ -0,0 +1,44 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <vector>
#include "core/frontend/input.h"
#include "input_common/main.h"
union SDL_Event;
namespace Common {
class ParamPackage;
} // namespace Common
namespace InputCommon::Polling {
class DevicePoller;
enum class DeviceType;
} // namespace InputCommon::Polling
namespace InputCommon::SDL {
class State {
public:
using Pollers = std::vector<std::unique_ptr<Polling::DevicePoller>>;
/// Unregisters SDL device factories and shut them down.
virtual ~State() = default;
virtual Pollers GetPollers(Polling::DeviceType type) = 0;
};
class NullState : public State {
public:
Pollers GetPollers(Polling::DeviceType type) override {
return {};
}
};
std::unique_ptr<State> Init();
} // namespace InputCommon::SDL

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,74 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <atomic>
#include <memory>
#include <thread>
#include <unordered_map>
#include "common/settings.h"
#include "common/threadsafe_queue.h"
#include "input_common/sdl/sdl.h"
union SDL_Event;
using SDL_Joystick = struct _SDL_Joystick;
using SDL_JoystickID = s32;
using SDL_GameController = struct _SDL_GameController;
namespace InputCommon::SDL {
class SDLJoystick;
class SDLGameController;
class SDLButtonFactory;
class SDLAnalogFactory;
class SDLMotionFactory;
class SDLState : public State {
public:
/// Initializes and registers SDL device factories
SDLState();
/// Unregisters SDL device factories and shut them down.
~SDLState() override;
/// Handle SDL_Events for joysticks from SDL_PollEvent
void HandleGameControllerEvent(const SDL_Event& event);
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
Common::ParamPackage GetSDLControllerButtonBindByGUID(const std::string& guid, int port,
Settings::NativeButton::Values button);
Common::ParamPackage GetSDLControllerAnalogBindByGUID(const std::string& guid, int port,
Settings::NativeAnalog::Values analog);
/// Get all DevicePoller that use the SDL backend for a specific device type
Pollers GetPollers(Polling::DeviceType type) override;
/// Used by the Pollers during config
std::atomic<bool> polling = false;
Common::SPSCQueue<SDL_Event> event_queue;
private:
void InitJoystick(int joystick_index);
void CloseJoystick(SDL_Joystick* sdl_joystick);
/// Needs to be called before SDL_QuitSubSystem.
void CloseJoysticks();
/// Map of GUID of a list of corresponding virtual Joysticks
std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
std::mutex joystick_map_mutex;
std::shared_ptr<SDLButtonFactory> button_factory;
std::shared_ptr<SDLAnalogFactory> analog_factory;
std::shared_ptr<SDLMotionFactory> motion_factory;
bool start_thread = false;
std::atomic<bool> initialized = false;
std::thread poll_thread;
};
} // namespace InputCommon::SDL

View file

@ -119,6 +119,10 @@ __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
}
#endif
#ifdef HAVE_SDL2
#include <SDL.h>
#endif
constexpr int default_mouse_timeout = 2500;
/**
@ -1129,9 +1133,58 @@ void GMainWindow::MigrateUserData() {
QMessageBox::Ok);
}
#if defined(HAVE_SDL2) && defined(__unix__) && !defined(__APPLE__)
static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
if (!QDBusConnection::sessionBus().isConnected()) {
return {};
}
// reference: https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.Inhibit
QDBusInterface xdp(QStringLiteral("org.freedesktop.portal.Desktop"),
QStringLiteral("/org/freedesktop/portal/desktop"),
QStringLiteral("org.freedesktop.portal.Inhibit"));
if (!xdp.isValid()) {
LOG_WARNING(Frontend, "Couldn't connect to XDP D-Bus endpoint");
return {};
}
QVariantMap options = {};
//: TRANSLATORS: This string is shown to the user to explain why Lime3DS needs to prevent the
//: computer from sleeping
options.insert(QString::fromLatin1("reason"),
QCoreApplication::translate("GMainWindow", "Lime3DS is running a game"));
// 0x4: Suspend lock; 0x8: Idle lock
QDBusReply<QDBusObjectPath> reply =
xdp.call(QString::fromLatin1("Inhibit"),
QString::fromLatin1("x11:") + QString::number(window_id, 16), 12U, options);
if (reply.isValid()) {
return reply.value();
}
LOG_WARNING(Frontend, "Couldn't read Inhibit reply from XDP: {}",
reply.error().message().toStdString());
return {};
}
static void ReleaseWakeLockLinux(const QDBusObjectPath& lock) {
if (!QDBusConnection::sessionBus().isConnected()) {
return;
}
QDBusInterface unlocker(QString::fromLatin1("org.freedesktop.portal.Desktop"), lock.path(),
QString::fromLatin1("org.freedesktop.portal.Request"));
unlocker.call(QString::fromLatin1("Close"));
}
#endif // __unix__
void GMainWindow::PreventOSSleep() {
#ifdef _WIN32
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
#elif defined(HAVE_SDL2)
SDL_DisableScreenSaver();
#if defined(__unix__) && !defined(__APPLE__)
auto reply = HoldWakeLockLinux(winId());
if (reply) {
wake_lock = std::move(reply.value());
}
#endif // defined(__unix__) && !defined(__APPLE__)
#endif // _WIN32
}