mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-05-09 04:12:11 -05:00
Migrate from SDL2 to SDL3 3.4.2 (#1847)
This commit is contained in:
parent
0d832c48b1
commit
f3fecba313
|
|
@ -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
2
dependencies/vcpkg
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit af752f21c9d79ba3df9cb0250ce2233933f58486
|
||||
Subproject commit c3867e714dd3a51c272826eea77267876517ed99
|
||||
|
|
@ -207,7 +207,7 @@ target_link_libraries(CemuBin PRIVATE
|
|||
CemuGui
|
||||
CemuInput
|
||||
CemuUtil
|
||||
SDL2::SDL2
|
||||
SDL3::SDL3
|
||||
)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ target_link_libraries(CemuWxGui PRIVATE
|
|||
libzip::zip
|
||||
ZArchive::zarchive
|
||||
CemuComponents
|
||||
SDL2::SDL2
|
||||
SDL3::SDL3
|
||||
pugixml::pugixml
|
||||
CemuCafe
|
||||
PUBLIC
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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{};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
18
vcpkg.json
18
vcpkg.json
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user