Migrate from SDL2 to SDL3 3.4.2 (#1847)
Some checks are pending
Build check / build (push) Waiting to run
Generate translation template / generate-pot (push) Waiting to run

This commit is contained in:
bslhq 2026-05-08 23:11:19 +08:00 committed by GitHub
parent 0d832c48b1
commit f3fecba313
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 538 additions and 385 deletions

View File

@ -148,7 +148,7 @@ option(ENABLE_CUBEB "Enabled cubeb backend" ON)
option(ENABLE_WXWIDGETS "Build with wxWidgets UI (Currently required)" ON)
find_package(Threads REQUIRED)
find_package(SDL2 REQUIRED)
find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
find_package(CURL REQUIRED)
find_package(pugixml REQUIRED)
find_package(RapidJSON REQUIRED)
@ -211,7 +211,6 @@ if (ENABLE_DISCORD_RPC)
endif()
if (ENABLE_HIDAPI)
find_package(hidapi REQUIRED)
set(SUPPORTS_WIIMOTE ON)
add_compile_definitions(HAS_HIDAPI)
endif ()

2
dependencies/vcpkg vendored

@ -1 +1 @@
Subproject commit af752f21c9d79ba3df9cb0250ce2233933f58486
Subproject commit c3867e714dd3a51c272826eea77267876517ed99

View File

@ -207,7 +207,7 @@ target_link_libraries(CemuBin PRIVATE
CemuGui
CemuInput
CemuUtil
SDL2::SDL2
SDL3::SDL3
)
if(UNIX AND NOT APPLE)

View File

@ -147,7 +147,7 @@ target_link_libraries(CemuWxGui PRIVATE
libzip::zip
ZArchive::zarchive
CemuComponents
SDL2::SDL2
SDL3::SDL3
pugixml::pugixml
CemuCafe
PUBLIC

View File

@ -9,6 +9,7 @@
#include "config/LaunchSettings.h"
#include "wxgui/GettingStartedDialog.h"
#include "input/InputManager.h"
#include "input/api/SDL/SDLControllerProvider.h"
#include "wxgui/helpers/wxHelpers.h"
#include "Cemu/ncrypto/ncrypto.h"
#include "wxgui/input/HotkeySettings.h"
@ -26,6 +27,7 @@
#include <wx/filename.h>
#include <wx/stdpaths.h>
#include <wx/clipbrd.h>
#include <wx/timer.h>
#include "wxHelper.h"
#include "Cafe/TitleList/TitleList.h"
@ -332,8 +334,18 @@ bool CemuApp::OnInit()
#ifdef CEMU_DEBUG_ASSERT
UnitTests();
#endif
#if BOOST_OS_MACOS
SDLControllerProvider::InitSDL();
#endif
CemuCommonInit();
#if BOOST_OS_MACOS
m_sdlEventPumpTimer = new wxTimer(this);
Bind(wxEVT_TIMER, &CemuApp::OnSDLEventPumpTimer, this);
m_sdlEventPumpTimer->Start(5, wxTIMER_CONTINUOUS);
#endif
wxInitAllImageHandlers();
// fill colour db
@ -389,8 +401,22 @@ bool CemuApp::OnInit()
int CemuApp::OnExit()
{
#if BOOST_OS_MACOS
if (m_sdlEventPumpTimer)
{
m_sdlEventPumpTimer->Stop();
Unbind(wxEVT_TIMER, &CemuApp::OnSDLEventPumpTimer, this);
delete m_sdlEventPumpTimer;
m_sdlEventPumpTimer = nullptr;
}
#endif
wxApp::OnExit();
wxTheClipboard->Flush();
InputManager::instance().Shutdown();
#if BOOST_OS_MACOS
SDLControllerProvider::ShutdownSDL();
#endif
#if BOOST_OS_WINDOWS
ExitProcess(0);
#else
@ -398,6 +424,15 @@ int CemuApp::OnExit()
#endif
}
#if BOOST_OS_MACOS
void CemuApp::OnSDLEventPumpTimer(wxTimerEvent& event)
{
// this callback is only used on macOS where SDL event functions need to be called on the main thread
// on other platforms SDLControllerProvider creates a separate thread for SDL event polling
SDLControllerProvider::PumpSDLEvents();
}
#endif
#if BOOST_OS_WINDOWS
void DumpThreadStackTrace();
#endif

View File

@ -3,6 +3,8 @@
#include <wx/app.h>
class MainWindow;
class wxTimer;
class wxTimerEvent;
class CemuApp : public wxApp
{
@ -30,6 +32,10 @@ private:
static std::vector<const wxLanguageInfo*> GetAvailableTranslationLanguages(wxTranslations* translationsMgr);
MainWindow* m_mainFrame = nullptr;
#if BOOST_OS_MACOS
void OnSDLEventPumpTimer(wxTimerEvent& event);
wxTimer* m_sdlEventPumpTimer = nullptr;
#endif
wxLocale m_locale;
std::vector<const wxLanguageInfo*> m_availableTranslations;

View File

@ -99,10 +99,6 @@ target_link_libraries(CemuInput PRIVATE
CemuGui
)
if (ENABLE_HIDAPI)
target_link_libraries(CemuInput PRIVATE hidapi::hidapi)
endif()
if (ENABLE_BLUEZ)
target_link_libraries(CemuInput PRIVATE bluez::bluez)
endif ()

View File

@ -68,7 +68,7 @@ ControllerPtr ControllerFactory::CreateController(InputAPI::Type api, std::strin
throw std::invalid_argument(fmt::format("invalid sdl uuid format: {}", uuid));
const auto guid_index = ConvertString<size_t>(uuid.substr(0, index));
const auto guid = SDL_JoystickGetGUIDFromString(std::string{uuid.substr(index + 1)}.c_str());
const auto guid = SDL_StringToGUID(std::string{uuid.substr(index + 1)}.c_str());
if (display_name.empty())
return std::make_shared<SDLController>(guid, guid_index);

View File

@ -53,8 +53,7 @@ InputManager::InputManager()
InputManager::~InputManager()
{
m_update_thread_shutdown.store(true);
m_update_thread.join();
// destructors will not invoked forever, so we manually release resources in Shutdown().
}
void InputManager::load() noexcept
@ -952,3 +951,21 @@ void InputManager::update_thread()
std::this_thread::yield();
}
}
void InputManager::Shutdown()
{
m_update_thread_shutdown = true;
if (m_update_thread.joinable())
{
m_update_thread.join();
}
for (auto& pad : m_vpad) pad.reset();
for (auto& pad : m_wpad) pad.reset();
for (auto& providers : m_api_available)
{
providers.clear();
}
}

View File

@ -46,6 +46,8 @@ public:
bool is_gameprofile_set(size_t player_index) const;
void Shutdown();
EmulatedControllerPtr set_controller(EmulatedControllerPtr controller);
EmulatedControllerPtr set_controller(size_t player_index, EmulatedController::Type type);
EmulatedControllerPtr set_controller(size_t player_index, EmulatedController::Type type, const std::shared_ptr<ControllerBase>& controller);

View File

@ -42,8 +42,24 @@ DSUControllerProvider::~DSUControllerProvider()
if (m_running)
{
m_running = false;
m_writer_thread.join();
// wake up the reader thread by sending a packet to self
if (m_socketWakeupEndpoint.port() != 0)
{
boost::asio::io_context io_context;
boost::asio::ip::udp::socket socket(io_context);
boost::system::error_code ec;
socket.open(m_socketWakeupEndpoint.protocol(), ec);
if (!ec)
{
std::array<char, 1> data{};
socket.send_to(boost::asio::buffer(data), m_socketWakeupEndpoint, 0, ec);
}
else
cemuLog_log(LogType::Force, "DSUControllerProvider wakeup failed");
}
m_reader_thread.join();
m_writerJobs.push(nullptr); // wake up writer thread by pushing an empty message
m_writer_thread.join();
}
}
@ -84,14 +100,8 @@ bool DSUControllerProvider::connect()
m_socket.close();
m_socket.open(ip::udp::v4());
// set timeout for our threads to give a chance to exit
#if BOOST_OS_WINDOWS
m_socket.set_option(boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO>{200});
#elif BOOST_OS_LINUX || BOOST_OS_MACOS
timeval timeout{.tv_usec = 200 * 1000};
setsockopt(m_socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval));
#endif
m_socket.bind(ip::udp::endpoint(ip::udp::v4(), 0));
m_socketWakeupEndpoint = ip::udp::endpoint(boost::asio::ip::address_v4::loopback(), m_socket.local_endpoint().port());
// reset data
m_state = {};
m_prev_state = {};
@ -189,19 +199,13 @@ uint32_t DSUControllerProvider::get_packet_index(uint8_t index) const
void DSUControllerProvider::request_version()
{
auto msg = std::make_unique<VersionRequest>(m_uid);
std::scoped_lock lock(m_writer_mutex);
m_writer_jobs.push(std::move(msg));
m_writer_cond.notify_one();
m_writerJobs.push(std::move(msg));
}
void DSUControllerProvider::request_pad_info()
{
auto msg = std::make_unique<ListPorts>(m_uid, 4, std::array<uint8_t, 4>{0, 1, 2, 3});
std::scoped_lock lock(m_writer_mutex);
m_writer_jobs.push(std::move(msg));
m_writer_cond.notify_one();
m_writerJobs.push(std::move(msg));
}
void DSUControllerProvider::request_pad_info(uint8_t index)
@ -210,19 +214,13 @@ void DSUControllerProvider::request_pad_info(uint8_t index)
return;
auto msg = std::make_unique<ListPorts>(m_uid, 1, std::array<uint8_t, 4>{index});
std::scoped_lock lock(m_writer_mutex);
m_writer_jobs.push(std::move(msg));
m_writer_cond.notify_one();
m_writerJobs.push(std::move(msg));
}
void DSUControllerProvider::request_pad_data()
{
auto msg = std::make_unique<DataRequest>(m_uid);
std::scoped_lock lock(m_writer_mutex);
m_writer_jobs.push(std::move(msg));
m_writer_cond.notify_one();
m_writerJobs.push(std::move(msg));
}
void DSUControllerProvider::request_pad_data(uint8_t index)
@ -231,10 +229,7 @@ void DSUControllerProvider::request_pad_data(uint8_t index)
return;
auto msg = std::make_unique<DataRequest>(m_uid, index);
std::scoped_lock lock(m_writer_mutex);
m_writer_jobs.push(std::move(msg));
m_writer_cond.notify_one();
m_writerJobs.push(std::move(msg));
}
MotionSample DSUControllerProvider::get_motion_sample(uint8_t index) const
@ -259,8 +254,11 @@ void DSUControllerProvider::reader_thread()
boost::asio::ip::udp::endpoint sender_endpoint;
boost::system::error_code ec{};
const size_t len = m_socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint, 0, ec);
if (!m_running.load(std::memory_order_relaxed))
break;
if (ec)
{
#ifdef DEBUG_DSU_CLIENT
printf(" DSUControllerProvider::ReaderThread: exception %s\n", ec.what());
#endif
@ -336,6 +334,7 @@ void DSUControllerProvider::reader_thread()
}
index = info->GetIndex();
cemu_assert(index < kMaxClients);
#ifdef DEBUG_DSU_CLIENT
printf(" DSUControllerProvider::ReaderThread: received PortInfo for index %d\n", index);
#endif
@ -359,6 +358,7 @@ void DSUControllerProvider::reader_thread()
}
index = rsp->GetIndex();
cemu_assert(index < kMaxClients);
#ifdef DEBUG_DSU_CLIENT
printf(" DSUControllerProvider::ReaderThread: received DataResponse for index %d\n", index);
#endif
@ -384,20 +384,10 @@ void DSUControllerProvider::writer_thread()
SetThreadName("DSU-writer");
while (m_running.load(std::memory_order_relaxed))
{
std::unique_lock lock(m_writer_mutex);
while (m_writer_jobs.empty())
{
if (m_writer_cond.wait_for(lock, std::chrono::milliseconds(250)) == std::cv_status::timeout)
{
if (!m_running.load(std::memory_order_relaxed))
return;
}
}
const auto msg = std::move(m_writer_jobs.front());
m_writer_jobs.pop();
lock.unlock();
std::unique_ptr<ClientMessage> msg = m_writerJobs.pop();
if (!m_running.load(std::memory_order_relaxed))
return;
cemu_assert_debug(msg.get());
#ifdef DEBUG_DSU_CLIENT
printf(" DSUControllerProvider::WriterThread: sending message: 0x%x (len: 0x%x)\n", (int)msg->GetMessageType(), msg->GetSize());
#endif
@ -457,4 +447,4 @@ DSUControllerProvider::ControllerState& DSUControllerProvider::ControllerState::
this->operator=(static_cast<const PortInfo&>(data_response));
data = data_response.GetData();
return *this;
}
}

View File

@ -4,6 +4,7 @@
#include "input/api/DSU/DSUMessages.h"
#include "input/api/ControllerProvider.h"
#include "util/helpers/ConcurrentQueue.h"
#include <boost/asio.hpp>
@ -87,7 +88,6 @@ public:
void request_pad_data();
void request_pad_data(uint8_t index);
private:
uint16 m_server_version = 0;
@ -98,20 +98,18 @@ private:
void writer_thread();
void integrate_motion(uint8_t index, const DataResponse& data_response);
std::mutex m_writer_mutex;
std::condition_variable m_writer_cond;
uint32 m_uid;
boost::asio::io_context m_io_service;
boost::asio::ip::udp::endpoint m_receiver_endpoint;
boost::asio::ip::udp::socket m_socket;
boost::asio::ip::udp::endpoint m_socketWakeupEndpoint;
std::array<ControllerState, kMaxClients> m_state{};
std::array<ControllerState, kMaxClients> m_prev_state{};
mutable std::array<std::mutex, kMaxClients> m_mutex;
mutable std::array<std::condition_variable, kMaxClients> m_wait_cond;
std::queue<std::unique_ptr<ClientMessage>> m_writer_jobs;
ConcurrentQueue<std::unique_ptr<ClientMessage>> m_writerJobs;
std::array<WiiUMotionHandler, kMaxClients> m_motion_handler;
std::array<uint64, kMaxClients> m_last_motion_timestamp{};

View File

@ -2,27 +2,30 @@
#include "input/api/SDL/SDLControllerProvider.h"
SDLController::SDLController(const SDL_JoystickGUID& guid, size_t guid_index)
SDLController::SDLController(const SDL_GUID& guid, size_t guid_index)
: base_type(fmt::format("{}_", guid_index), fmt::format("Controller {}", guid_index + 1)), m_guid_index(guid_index),
m_guid(guid)
{
char tmp[64];
SDL_JoystickGetGUIDString(m_guid, tmp, std::size(tmp));
SDL_GUIDToString(m_guid, tmp, std::size(tmp));
m_uuid += tmp;
}
SDLController::SDLController(const SDL_JoystickGUID& guid, size_t guid_index, std::string_view display_name)
SDLController::SDLController(const SDL_GUID& guid, size_t guid_index, std::string_view display_name)
: base_type(fmt::format("{}_", guid_index), display_name), m_guid_index(guid_index), m_guid(guid)
{
char tmp[64];
SDL_JoystickGetGUIDString(m_guid, tmp, std::size(tmp));
SDL_GUIDToString(m_guid, tmp, std::size(tmp));
m_uuid += tmp;
}
SDLController::~SDLController()
{
if (m_controller)
SDL_GameControllerClose(m_controller);
{
SDL_CloseGamepad(m_controller);
m_controller = nullptr;
}
}
bool SDLController::is_connected()
@ -33,8 +36,9 @@ bool SDLController::is_connected()
return false;
}
if (!SDL_GameControllerGetAttached(m_controller))
if (!SDL_GamepadConnected(m_controller))
{
SDL_CloseGamepad(m_controller);
m_controller = nullptr;
return false;
}
@ -45,49 +49,39 @@ bool SDLController::is_connected()
bool SDLController::connect()
{
if (is_connected())
{
return true;
}
m_has_rumble = false;
const auto index = m_provider->get_index(m_guid_index, m_guid);
std::scoped_lock lock(m_controller_mutex);
m_diid = SDL_JoystickGetDeviceInstanceID(index);
if (m_diid == -1)
int gamepad_count = 0;
SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count);
if (!gamepad_ids || index < 0 || index >= gamepad_count)
return false;
m_controller = SDL_GameControllerOpen(index);
m_diid = gamepad_ids[index];
SDL_free(gamepad_ids);
m_controller = SDL_OpenGamepad(m_diid);
if (!m_controller)
return false;
if (const char* name = SDL_GameControllerName(m_controller))
if (const char* name = SDL_GetGamepadName(m_controller))
m_display_name = name;
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i)
{
m_buttons[i] = SDL_GameControllerHasButton(m_controller, (SDL_GameControllerButton)i);
}
for (int i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i)
{
m_axis[i] = SDL_GameControllerHasAxis(m_controller, (SDL_GameControllerAxis)i);
}
if (SDL_GameControllerHasSensor(m_controller, SDL_SENSOR_ACCEL))
{
m_has_accel = true;
SDL_GameControllerSetSensorEnabled(m_controller, SDL_SENSOR_ACCEL, SDL_TRUE);
}
if (SDL_GameControllerHasSensor(m_controller, SDL_SENSOR_GYRO))
{
m_has_gyro = true;
SDL_GameControllerSetSensorEnabled(m_controller, SDL_SENSOR_GYRO, SDL_TRUE);
}
m_has_rumble = SDL_GameControllerRumble(m_controller, 0, 0, 0) == 0;
for (size_t i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i)
m_buttons[i] = SDL_GamepadHasButton(m_controller, (SDL_GamepadButton)i);
for (size_t i = 0; i < SDL_GAMEPAD_AXIS_COUNT; ++i)
m_axis[i] = SDL_GamepadHasAxis(m_controller, (SDL_GamepadAxis)i);
if (SDL_GamepadHasSensor(m_controller, SDL_SENSOR_ACCEL))
m_has_accel = SDL_SetGamepadSensorEnabled(m_controller, SDL_SENSOR_ACCEL, true);
if (SDL_GamepadHasSensor(m_controller, SDL_SENSOR_GYRO))
m_has_gyro = SDL_SetGamepadSensorEnabled(m_controller, SDL_SENSOR_GYRO, true);
m_has_rumble = SDL_RumbleGamepad(m_controller, 0, 0, 0);
return true;
}
@ -96,12 +90,9 @@ void SDLController::start_rumble()
std::scoped_lock lock(m_controller_mutex);
if (is_connected() && !m_has_rumble)
return;
if (m_settings.rumble <= 0)
return;
SDL_GameControllerRumble(m_controller, (Uint16)(m_settings.rumble * 0xFFFF), (Uint16)(m_settings.rumble * 0xFFFF),
5 * 1000);
SDL_RumbleGamepad(m_controller, (Uint16)(m_settings.rumble * 0xFFFF), (Uint16)(m_settings.rumble * 0xFFFF), 5 * 1000);
}
void SDLController::stop_rumble()
@ -109,59 +100,47 @@ void SDLController::stop_rumble()
std::scoped_lock lock(m_controller_mutex);
if (is_connected() && !m_has_rumble)
return;
SDL_GameControllerRumble(m_controller, 0, 0, 0);
SDL_RumbleGamepad(m_controller, 0, 0, 0);
}
MotionSample SDLController::get_motion_sample()
{
if (is_connected() && has_motion())
{
return m_provider->motion_sample(m_diid);
}
return {};
}
std::string SDLController::get_button_name(uint64 button) const
{
if (const char* name = SDL_GameControllerGetStringForButton((SDL_GameControllerButton)button))
if (const char* name = SDL_GetGamepadStringForButton((SDL_GamepadButton)button))
return name;
return base_type::get_button_name(button);
}
ControllerState SDLController::raw_state()
{
ControllerState result{};
std::scoped_lock lock(m_controller_mutex);
if (!is_connected())
return result;
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i)
for (size_t i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i)
{
if (m_buttons[i] && SDL_GameControllerGetButton(m_controller, (SDL_GameControllerButton)i))
if (m_buttons[i] && SDL_GetGamepadButton(m_controller, (SDL_GamepadButton)i))
result.buttons.SetButtonState(i, true);
}
if (m_axis[SDL_CONTROLLER_AXIS_LEFTX])
result.axis.x = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_LEFTX) / 32767.0f;
if (m_axis[SDL_CONTROLLER_AXIS_LEFTY])
result.axis.y = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_LEFTY) / 32767.0f;
if (m_axis[SDL_CONTROLLER_AXIS_RIGHTX])
result.rotation.x = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_RIGHTX) / 32767.0f;
if (m_axis[SDL_CONTROLLER_AXIS_RIGHTY])
result.rotation.y = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_RIGHTY) / 32767.0f;
if (m_axis[SDL_CONTROLLER_AXIS_TRIGGERLEFT])
result.trigger.x = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) / 32767.0f;
if (m_axis[SDL_CONTROLLER_AXIS_TRIGGERRIGHT])
result.trigger.y = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) / 32767.0f;
if (m_axis[SDL_GAMEPAD_AXIS_LEFTX])
result.axis.x = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_LEFTX) / 32767.0f;
if (m_axis[SDL_GAMEPAD_AXIS_LEFTY])
result.axis.y = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_LEFTY) / 32767.0f;
if (m_axis[SDL_GAMEPAD_AXIS_RIGHTX])
result.rotation.x = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_RIGHTX) / 32767.0f;
if (m_axis[SDL_GAMEPAD_AXIS_RIGHTY])
result.rotation.y = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_RIGHTY) / 32767.0f;
if (m_axis[SDL_GAMEPAD_AXIS_LEFT_TRIGGER])
result.trigger.x = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) / 32767.0f;
if (m_axis[SDL_GAMEPAD_AXIS_RIGHT_TRIGGER])
result.trigger.y = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) / 32767.0f;
return result;
}

View File

@ -3,13 +3,13 @@
#include "input/api/Controller.h"
#include "input/api/SDL/SDLControllerProvider.h"
#include <SDL2/SDL_gamecontroller.h>
#include <SDL3/SDL_gamepad.h>
class SDLController : public Controller<SDLControllerProvider>
{
public:
SDLController(const SDL_JoystickGUID& guid, size_t guid_index);
SDLController(const SDL_JoystickGUID& guid, size_t guid_index, std::string_view display_name);
SDLController(const SDL_GUID& guid, size_t guid_index);
SDLController(const SDL_GUID& guid, size_t guid_index, std::string_view display_name);
~SDLController() override;
@ -32,29 +32,29 @@ public:
MotionSample get_motion_sample() override;
std::string get_button_name(uint64 button) const override;
const SDL_JoystickGUID& get_guid() const { return m_guid; }
const SDL_GUID& get_guid() const { return m_guid; }
constexpr static SDL_JoystickGUID kLeftJoyCon{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00,0x68 ,0x00 };
constexpr static SDL_JoystickGUID kRightJoyCon{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00 };
constexpr static SDL_JoystickGUID kSwitchProController{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00 };
constexpr static SDL_GUID kLeftJoyCon{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00,0x68 ,0x00 };
constexpr static SDL_GUID kRightJoyCon{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00 };
constexpr static SDL_GUID kSwitchProController{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00 };
protected:
ControllerState raw_state() override;
private:
inline static SDL_JoystickGUID kEmptyGUID{};
inline static SDL_GUID kEmptyGUID{};
size_t m_guid_index;
SDL_JoystickGUID m_guid;
SDL_GUID m_guid;
std::recursive_mutex m_controller_mutex;
SDL_GameController* m_controller = nullptr;
SDL_Gamepad* m_controller = nullptr;
SDL_JoystickID m_diid = -1;
bool m_has_gyro = false;
bool m_has_accel = false;
bool m_has_rumble = false;
std::array<bool, SDL_CONTROLLER_BUTTON_MAX> m_buttons{};
std::array<bool, SDL_CONTROLLER_AXIS_MAX> m_axis{};
std::array<bool, SDL_GAMEPAD_BUTTON_COUNT> m_buttons{};
std::array<bool, SDL_GAMEPAD_AXIS_COUNT> m_axis{};
};

View File

@ -3,12 +3,12 @@
#include "input/api/SDL/SDLController.h"
#include "util/helpers/TempState.h"
#include <SDL2/SDL.h>
#include <SDL3/SDL.h>
#include <boost/functional/hash.hpp>
struct SDL_JoystickGUIDHash
{
std::size_t operator()(const SDL_JoystickGUID& guid) const
std::size_t operator()(const SDL_GUID& guid) const
{
return boost::hash_value(guid.data);
}
@ -16,229 +16,285 @@ struct SDL_JoystickGUIDHash
SDLControllerProvider::SDLControllerProvider()
{
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STADIA, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_LUNA, "1");
if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC | SDL_INIT_EVENTS) < 0)
throw std::runtime_error(fmt::format("couldn't initialize SDL: {}", SDL_GetError()));
if (SDL_GameControllerEventState(SDL_ENABLE) < 0) {
cemuLog_log(LogType::Force, "Couldn't enable SDL gamecontroller event polling: {}", SDL_GetError());
#if !BOOST_OS_MACOS
std::scoped_lock _l(s_mutex);
if (s_initCount.fetch_add(1) == 0)
{
s_running = true;
s_thread = std::thread(&SDLControllerProvider::event_thread, this);
}
m_running = true;
m_thread = std::thread(&SDLControllerProvider::event_thread, this);
#endif
}
SDLControllerProvider::~SDLControllerProvider()
{
if (m_running)
#if !BOOST_OS_MACOS
bool shutdownSDL = false;
{
m_running = false;
// wake the thread with a quit event if it's currently waiting for events
SDL_Event evt;
evt.type = SDL_QUIT;
SDL_PushEvent(&evt);
// wait until thread exited
m_thread.join();
std::scoped_lock _l(s_mutex);
if (s_initCount.fetch_sub(1) == 1)
{
cemu_assert_debug(s_running);
s_running = false;
shutdownSDL = true;
}
}
SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC | SDL_INIT_EVENTS);
if (shutdownSDL)
{
// wake the thread with a quit event if it's currently waiting for events
SDL_Event evt;
SDL_zero(evt);
evt.type = SDL_EVENT_QUIT;
SDL_PushEvent(&evt);
if (s_thread.joinable())
{
s_thread.join();
}
}
#endif
}
std::vector<std::shared_ptr<ControllerBase>> SDLControllerProvider::get_controllers()
{
std::vector<std::shared_ptr<ControllerBase>> result;
std::unordered_map<SDL_JoystickGUID, size_t, SDL_JoystickGUIDHash> guid_counter;
std::unordered_map<SDL_GUID, size_t, SDL_JoystickGUIDHash> guid_counter;
TempState lock(SDL_LockJoysticks, SDL_UnlockJoysticks);
for (int i = 0; i < SDL_NumJoysticks(); ++i)
int gamepad_count = 0;
SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count);
if (gamepad_ids)
{
if (SDL_JoystickGetDeviceType(i) == SDL_JOYSTICK_TYPE_GAMECONTROLLER)
for (size_t i = 0; i < gamepad_count; ++i)
{
const auto guid = SDL_JoystickGetDeviceGUID(i);
const auto guid = SDL_GetGamepadGUIDForID(gamepad_ids[i]);
const auto it = guid_counter.try_emplace(guid, 0);
if (auto* controller = SDL_GameControllerOpen(i))
{
const char* name = SDL_GameControllerName(controller);
if (const char* name = SDL_GetGamepadNameForID(gamepad_ids[i]))
result.emplace_back(std::make_shared<SDLController>(guid, it.first->second, name));
SDL_GameControllerClose(controller);
}
else
result.emplace_back(std::make_shared<SDLController>(guid, it.first->second));
++it.first->second;
}
SDL_free(gamepad_ids);
}
return result;
}
int SDLControllerProvider::get_index(size_t guid_index, const SDL_JoystickGUID& guid) const
int SDLControllerProvider::get_index(size_t guid_index, const SDL_GUID& guid) const
{
size_t index = 0;
int gamepad_count = 0;
TempState lock(SDL_LockJoysticks, SDL_UnlockJoysticks);
for (int i = 0; i < SDL_NumJoysticks(); ++i)
SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count);
if (gamepad_ids)
{
if (SDL_JoystickGetDeviceType(i) == SDL_JOYSTICK_TYPE_GAMECONTROLLER)
for (size_t i = 0; i < gamepad_count; ++i)
{
if(guid == SDL_JoystickGetDeviceGUID(i))
if (guid == SDL_GetGamepadGUIDForID(gamepad_ids[i]))
{
if (index == guid_index)
{
SDL_free(gamepad_ids);
return i;
}
++index;
}
}
SDL_free(gamepad_ids);
}
return -1;
}
MotionSample SDLControllerProvider::motion_sample(int diid)
MotionSample SDLControllerProvider::motion_sample(SDL_JoystickID diid)
{
std::scoped_lock lock(m_motion_data_mtx[diid]);
return m_motion_data[diid];
std::shared_lock lock(s_mutex);
auto it = s_motion_states.find(diid);
return (it != s_motion_states.end()) ? it->second.data : MotionSample{};
}
void SDLControllerProvider::InitSDL()
{
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH2, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STADIA, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_LUNA, "1");
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC))
{
throw std::runtime_error(fmt::format("couldn't initialize SDL: {}", SDL_GetError()));
}
SDL_SetGamepadEventsEnabled(true);
if (!SDL_GamepadEventsEnabled())
{
cemuLog_log(LogType::Force, "Couldn't enable SDL gamecontroller event polling: {}", SDL_GetError());
}
}
void SDLControllerProvider::ShutdownSDL()
{
SDL_QuitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC);
}
#if BOOST_OS_MACOS
void SDLControllerProvider::PumpSDLEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event))
HandleSDLEvent(event);
}
#endif
void SDLControllerProvider::HandleSDLEvent(SDL_Event& event)
{
switch (event.type)
{
case SDL_EVENT_QUIT:
{
std::scoped_lock _l(s_mutex);
s_running = false;
break;
}
case SDL_EVENT_GAMEPAD_AXIS_MOTION: /**< Game controller axis motion */
{
break;
}
case SDL_EVENT_GAMEPAD_BUTTON_DOWN: /**< Game controller button pressed */
{
break;
}
case SDL_EVENT_GAMEPAD_BUTTON_UP: /**< Game controller button released */
{
break;
}
case SDL_EVENT_GAMEPAD_ADDED: /**< A new Game controller has been inserted into the system */
{
std::scoped_lock _l(s_mutex);
InputManager::instance().on_device_changed();
break;
}
case SDL_EVENT_GAMEPAD_REMOVED: /**< An opened Game controller has been removed */
{
std::scoped_lock _l(s_mutex);
InputManager::instance().on_device_changed();
s_motion_states.erase(event.gdevice.which);
break;
}
case SDL_EVENT_GAMEPAD_REMAPPED: /**< The controller mapping was updated */
{
break;
}
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: /**< Game controller touchpad was touched */
{
break;
}
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: /**< Game controller touchpad finger was moved */
{
break;
}
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: /**< Game controller touchpad finger was lifted */
{
break;
}
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: /**< Game controller sensor was updated */
{
SDL_JoystickID id = event.gsensor.which;
uint64_t ts = event.gsensor.timestamp;
std::scoped_lock _l(s_mutex);
auto& state = s_motion_states[id];
auto& tracking = state.tracking;
if (event.gsensor.sensor == SDL_SENSOR_ACCEL)
{
const auto dif = ts - tracking.lastTimestampAccel;
if (dif <= 0)
{
break;
}
if (dif >= 10000000000)
{
tracking.hasAcc = false;
tracking.hasGyro = false;
tracking.lastTimestampAccel = ts;
break;
}
tracking.lastTimestampAccel = ts;
tracking.acc[0] = -event.gsensor.data[0] / 9.81f;
tracking.acc[1] = -event.gsensor.data[1] / 9.81f;
tracking.acc[2] = -event.gsensor.data[2] / 9.81f;
tracking.hasAcc = true;
}
if (event.gsensor.sensor == SDL_SENSOR_GYRO)
{
const auto dif = ts - tracking.lastTimestampGyro;
if (dif <= 0)
{
break;
}
if (dif >= 10000000000)
{
tracking.hasAcc = false;
tracking.hasGyro = false;
tracking.lastTimestampGyro = ts;
break;
}
tracking.lastTimestampGyro = ts;
tracking.gyro[0] = event.gsensor.data[0];
tracking.gyro[1] = -event.gsensor.data[1];
tracking.gyro[2] = -event.gsensor.data[2];
tracking.hasGyro = true;
}
if (tracking.hasAcc && tracking.hasGyro)
{
auto ts = std::max(tracking.lastTimestampGyro, tracking.lastTimestampAccel);
if (ts > tracking.lastTimestampIntegrate)
{
const auto tsDif = ts - tracking.lastTimestampIntegrate;
tracking.lastTimestampIntegrate = ts;
float tsDifD = (float)tsDif / 1000000000.0f;
if (tsDifD >= 1.0f)
{
tsDifD = 1.0f;
}
state.handler.processMotionSample(tsDifD, tracking.gyro.x, tracking.gyro.y, tracking.gyro.z, tracking.acc.x, -tracking.acc.y, -tracking.acc.z);
state.data = state.handler.getMotionSample();
}
tracking.hasAcc = false;
tracking.hasGyro = false;
}
break;
}
}
}
void SDLControllerProvider::event_thread()
{
#if BOOST_OS_MACOS
cemu_assert(false);
#endif
SetThreadName("SDL_events");
while (m_running.load(std::memory_order_relaxed))
InitSDL();
while (s_running.load(std::memory_order_relaxed))
{
SDL_Event event{};
SDL_WaitEvent(&event);
switch (event.type)
{
case SDL_QUIT:
m_running = false;
return;
case SDL_CONTROLLERAXISMOTION: /**< Game controller axis motion */
{
break;
}
case SDL_CONTROLLERBUTTONDOWN: /**< Game controller button pressed */
{
break;
}
case SDL_CONTROLLERBUTTONUP: /**< Game controller button released */
{
break;
}
case SDL_CONTROLLERDEVICEADDED: /**< A new Game controller has been inserted into the system */
{
InputManager::instance().on_device_changed();
break;
}
case SDL_CONTROLLERDEVICEREMOVED: /**< An opened Game controller has been removed */
{
InputManager::instance().on_device_changed();
break;
}
case SDL_CONTROLLERDEVICEREMAPPED: /**< The controller mapping was updated */
{
break;
}
case SDL_CONTROLLERTOUCHPADDOWN: /**< Game controller touchpad was touched */
{
break;
}
case SDL_CONTROLLERTOUCHPADMOTION: /**< Game controller touchpad finger was moved */
{
break;
}
case SDL_CONTROLLERTOUCHPADUP: /**< Game controller touchpad finger was lifted */
{
break;
}
case SDL_CONTROLLERSENSORUPDATE: /**< Game controller sensor was updated */
{
const auto index = event.csensor.which;
const auto ts = event.csensor.timestamp;
auto& motionTracking = m_motion_tracking[index];
if (event.csensor.sensor == SDL_SENSOR_ACCEL)
{
const auto dif = ts - motionTracking.lastTimestampAccel;
if (dif <= 0)
break;
if (dif >= 10000)
{
motionTracking.hasAcc = false;
motionTracking.hasGyro = false;
motionTracking.lastTimestampAccel = ts;
break;
}
motionTracking.lastTimestampAccel = ts;
motionTracking.acc[0] = -event.csensor.data[0] / 9.81f;
motionTracking.acc[1] = -event.csensor.data[1] / 9.81f;
motionTracking.acc[2] = -event.csensor.data[2] / 9.81f;
motionTracking.hasAcc = true;
}
if (event.csensor.sensor == SDL_SENSOR_GYRO)
{
const auto dif = ts - motionTracking.lastTimestampGyro;
if (dif <= 0)
break;
if (dif >= 10000)
{
motionTracking.hasAcc = false;
motionTracking.hasGyro = false;
motionTracking.lastTimestampGyro = ts;
break;
}
motionTracking.lastTimestampGyro = ts;
motionTracking.gyro[0] = event.csensor.data[0];
motionTracking.gyro[1] = -event.csensor.data[1];
motionTracking.gyro[2] = -event.csensor.data[2];
motionTracking.hasGyro = true;
}
if (motionTracking.hasAcc && motionTracking.hasGyro)
{
auto ts = std::max(motionTracking.lastTimestampGyro, motionTracking.lastTimestampAccel);
if (ts > motionTracking.lastTimestampIntegrate)
{
const auto tsDif = ts - motionTracking.lastTimestampIntegrate;
motionTracking.lastTimestampIntegrate = ts;
float tsDifD = (float)tsDif / 1000.0f;
if (tsDifD >= 1.0f)
tsDifD = 1.0f;
m_motion_handler[index].processMotionSample(tsDifD, motionTracking.gyro.x, motionTracking.gyro.y, motionTracking.gyro.z, motionTracking.acc.x, -motionTracking.acc.y, -motionTracking.acc.z);
std::scoped_lock lock(m_motion_data_mtx[index]);
m_motion_data[index] = m_motion_handler[index].getMotionSample();
}
motionTracking.hasAcc = false;
motionTracking.hasGyro = false;
}
break;
}
}
HandleSDLEvent(event);
}
ShutdownSDL();
}

View File

@ -1,5 +1,5 @@
#pragma once
#include <SDL2/SDL_joystick.h>
#include <SDL3/SDL_joystick.h>
#include "input/motion/MotionHandler.h"
#include "input/api/ControllerProvider.h"
@ -7,9 +7,9 @@
#define HAS_SDL 1
#endif
static bool operator==(const SDL_JoystickGUID& g1, const SDL_JoystickGUID& g2)
static bool operator==(const SDL_GUID& g1, const SDL_GUID& g2)
{
return memcmp(&g1, &g2, sizeof(SDL_JoystickGUID)) == 0;
return memcmp(&g1, &g2, sizeof(SDL_GUID)) == 0;
}
class SDLControllerProvider : public ControllerProviderBase
@ -24,19 +24,30 @@ public:
std::vector<std::shared_ptr<ControllerBase>> get_controllers() override;
int get_index(size_t guid_index, const SDL_JoystickGUID& guid) const;
int get_index(size_t guid_index, const SDL_GUID& guid) const;
MotionSample motion_sample(int diid);
MotionSample motion_sample(SDL_JoystickID diid);
// exposed for manual event handling on macOS
#if BOOST_OS_MACOS
static void InitSDL();
static void ShutdownSDL();
static void PumpSDLEvents();
#endif
private:
void event_thread();
std::atomic_bool m_running = false;
std::thread m_thread;
static void HandleSDLEvent(union SDL_Event& event);
#if !BOOST_OS_MACOS
static void InitSDL();
static void ShutdownSDL();
#endif
std::array<WiiUMotionHandler, 8> m_motion_handler{};
std::array<MotionSample, 8> m_motion_data{};
std::array<std::mutex, 8> m_motion_data_mtx{};
// there is only one SDL instance, for this reason all of our state can be static
inline static std::atomic_int s_initCount{0};
inline static std::shared_mutex s_mutex;
inline static std::atomic_bool s_running = false;
inline static std::thread s_thread;
struct MotionInfoTracking
{
@ -49,6 +60,14 @@ private:
glm::vec3 acc{};
};
std::array<MotionInfoTracking, 8> m_motion_tracking{};
struct MotionState
{
WiiUMotionHandler handler;
MotionSample data;
MotionInfoTracking tracking;
MotionState() = default;
};
inline static std::unordered_map<SDL_JoystickID, MotionState> s_motion_states{};
};

View File

@ -25,9 +25,31 @@ WiimoteControllerProvider::~WiimoteControllerProvider()
if (m_running)
{
m_running = false;
m_writer_thread.join();
m_reader_thread.join();
m_connectionThread.join();
{
std::scoped_lock lock(m_writer_mutex);
m_writer_cond.notify_all();
}
{
std::scoped_lock lock(m_connectionMutex);
m_connectionCond.notify_all();
}
if (m_writer_thread.joinable())
{
m_writer_thread.join();
}
if (m_reader_thread.joinable())
{
m_reader_thread.join();
}
if (m_connectionThread.joinable())
{
m_connectionThread.join();
}
}
}
@ -169,7 +191,8 @@ void WiimoteControllerProvider::connectionThread()
m_connectedDevices.clear();
std::ranges::move(devices, std::back_inserter(m_connectedDevices));
}
std::this_thread::sleep_for(std::chrono::seconds(2));
std::unique_lock<std::mutex> lock(m_connectionMutex);
m_connectionCond.wait_for(lock, std::chrono::seconds(2));
}
}

View File

@ -80,6 +80,8 @@ private:
std::shared_mutex m_device_mutex;
std::thread m_connectionThread;
std::mutex m_connectionMutex;
std::condition_variable m_connectionCond;
std::vector<WiimoteDevicePtr> m_connectedDevices;
std::mutex m_connectedDeviceMutex;
struct Wiimote

View File

@ -7,18 +7,18 @@ static constexpr uint16 WIIMOTE_MP_PRODUCT_ID = 0x0330;
static constexpr uint16 WIIMOTE_MAX_INPUT_REPORT_LENGTH = 22;
static constexpr auto PRO_CONTROLLER_NAME = L"Nintendo RVL-CNT-01-UC";
HidapiWiimote::HidapiWiimote(hid_device* dev, std::string_view path)
HidapiWiimote::HidapiWiimote(SDL_hid_device* dev, std::string_view path)
: m_handle(dev), m_path(path) {
}
bool HidapiWiimote::write_data(const std::vector<uint8> &data) {
return hid_write(m_handle, data.data(), data.size()) >= 0;
return SDL_hid_write(m_handle, data.data(), data.size()) >= 0;
}
std::optional<std::vector<uint8>> HidapiWiimote::read_data() {
std::array<uint8, WIIMOTE_MAX_INPUT_REPORT_LENGTH> read_data{};
const auto result = hid_read(m_handle, read_data.data(), WIIMOTE_MAX_INPUT_REPORT_LENGTH);
const auto result = SDL_hid_read(m_handle, read_data.data(), WIIMOTE_MAX_INPUT_REPORT_LENGTH);
if (result < 0)
return {};
return {{read_data.cbegin(), read_data.cbegin() + result}};
@ -26,24 +26,24 @@ std::optional<std::vector<uint8>> HidapiWiimote::read_data() {
std::vector<WiimoteDevicePtr> HidapiWiimote::get_devices() {
std::vector<WiimoteDevicePtr> wiimote_devices;
hid_init();
const auto device_enumeration = hid_enumerate(WIIMOTE_VENDOR_ID, 0x0);
SDL_hid_init();
const auto device_enumeration = SDL_hid_enumerate(WIIMOTE_VENDOR_ID, 0x0);
for (auto it = device_enumeration; it != nullptr; it = it->next){
if (it->product_id != WIIMOTE_PRODUCT_ID && it->product_id != WIIMOTE_MP_PRODUCT_ID)
continue;
if (std::wcscmp(it->product_string, PRO_CONTROLLER_NAME) == 0)
continue;
auto dev = hid_open_path(it->path);
auto dev = SDL_hid_open_path(it->path);
if (!dev){
cemuLog_logDebug(LogType::Force, "Unable to open Wiimote device at {}: {}", it->path, boost::nowide::narrow(hid_error(nullptr)));
cemuLog_logDebug(LogType::Force, "Unable to open Wiimote device at {}: {}", it->path, SDL_GetError());
}
else {
hid_set_nonblocking(dev, true);
SDL_hid_set_nonblocking(dev, true);
wiimote_devices.push_back(std::make_shared<HidapiWiimote>(dev, it->path));
}
}
hid_free_enumeration(device_enumeration);
SDL_hid_free_enumeration(device_enumeration);
return wiimote_devices;
}
@ -55,5 +55,5 @@ bool HidapiWiimote::operator==(const WiimoteDevice& rhs) const {
}
HidapiWiimote::~HidapiWiimote() {
hid_close(m_handle);
SDL_hid_close(m_handle);
}

View File

@ -1,11 +1,11 @@
#pragma once
#include <api/Wiimote/WiimoteDevice.h>
#include <hidapi.h>
#include <SDL3/SDL.h>
class HidapiWiimote : public WiimoteDevice {
public:
HidapiWiimote(hid_device* dev, std::string_view path);
HidapiWiimote(SDL_hid_device* dev, std::string_view path);
~HidapiWiimote() override;
bool write_data(const std::vector<uint8> &data) override;
@ -15,7 +15,7 @@ public:
static std::vector<WiimoteDevicePtr> get_devices();
private:
hid_device* m_handle;
SDL_hid_device* m_handle;
const std::string m_path;
};

View File

@ -31,7 +31,8 @@
#endif
#define SDL_MAIN_HANDLED
#include <SDL.h>
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#if BOOST_OS_LINUX
#define _putenv(__s) putenv((char*)(__s))

View File

@ -1,40 +1,62 @@
#include "Cemu/Logging/CemuLogging.h"
#include <SDL2/SDL.h>
#include <SDL3/SDL.h>
class ScreenSaver
{
public:
static void SetInhibit(bool inhibit)
{
// temporary workaround because feature crashes on macOS
#if BOOST_OS_MACOS
return;
#endif
// Initialize video subsystem if necessary
if (SDL_WasInit(SDL_INIT_VIDEO) == 0)
{
int initErr = SDL_InitSubSystem(SDL_INIT_VIDEO);
if (initErr)
{
cemuLog_log(LogType::Force, "Could not disable screen saver (SDL video subsystem initialization error)");
}
}
// Toggle SDL's screen saver inhibition
if (inhibit)
{
SDL_DisableScreenSaver();
if (SDL_IsScreenSaverEnabled() == SDL_TRUE)
{
cemuLog_log(LogType::Force, "Could not verify if screen saver was disabled (`SDL_IsScreenSaverEnabled()` returned SDL_TRUE)");
}
}
else
{
SDL_EnableScreenSaver();
if (SDL_IsScreenSaverEnabled() == SDL_FALSE)
{
cemuLog_log(LogType::Force, "Could not verify if screen saver was re-enabled (`SDL_IsScreenSaverEnabled()` returned SDL_FALSE)");
}
}
};
static void SetInhibit(bool inhibit)
{
bool* inhibitArg = new bool(inhibit);
if (!SDL_RunOnMainThread(SetInhibitCallback, inhibitArg, false))
{
delete inhibitArg;
cemuLog_log(LogType::Force, "Failed to schedule screen saver logic on main thread: {}", SDL_GetError());
}
}
private:
static void SDLCALL SetInhibitCallback(void* userdata)
{
if (!userdata)
{
return;
}
auto* inhibitArg = static_cast<bool*>(userdata);
bool inhibit = *inhibitArg;
delete inhibitArg;
if (SDL_WasInit(SDL_INIT_VIDEO) == 0)
{
if (!SDL_InitSubSystem(SDL_INIT_VIDEO))
{
cemuLog_log(LogType::Force, "Could not disable screen saver (SDL video subsystem initialization error: {})", SDL_GetError());
return;
}
}
if (inhibit)
{
if (!SDL_DisableScreenSaver())
{
cemuLog_log(LogType::Force, "Could not disable screen saver: {}", SDL_GetError());
}
else if (SDL_ScreenSaverEnabled())
{
cemuLog_log(LogType::Force, "Could not verify if screen saver was disabled (`SDL_IsScreenSaverEnabled()` returned true)");
}
}
else
{
if (!SDL_EnableScreenSaver())
{
cemuLog_log(LogType::Force, "Could not enable screen saver: {}", SDL_GetError());
}
else if (!SDL_ScreenSaverEnabled())
{
cemuLog_log(LogType::Force, "Could not verify if screen saver was re-enabled (`SDL_IsScreenSaverEnabled()` returned false)");
}
}
}
};

View File

@ -62,7 +62,7 @@ public:
m_condVar.wait(mlock);
}
auto val = m_queue.front();
auto val = std::move(m_queue.front());
m_queue.pop();
return val;
}
@ -75,7 +75,7 @@ public:
m_condVar.wait(mlock);
}
item = m_queue.front();
item = std::move(m_queue.front());
m_queue.pop();
}

View File

@ -11,7 +11,10 @@
"default-features": false
},
"rapidjson",
"sdl2",
{
"name": "sdl3",
"default-features": false
},
"boost-tokenizer",
"boost-container",
"boost-program-options",
@ -29,7 +32,6 @@
"name": "fmt",
"version>=": "12.1.0"
},
"hidapi",
"libpng",
"glm",
{
@ -37,7 +39,13 @@
"default-features": false
},
"zstd",
"wxwidgets",
{
"name": "wxwidgets",
"default-features": false,
"features": [
"debug-support"
]
},
"openssl",
{
"name": "curl",
@ -67,8 +75,8 @@
"version": "15.1.0"
},
{
"name": "sdl2",
"version": "2.32.10"
"name": "sdl3",
"version": "3.4.2"
},
{
"name": "wxwidgets",