diff --git a/CMakeLists.txt b/CMakeLists.txt index a00879fe..64c70e68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,7 +139,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) @@ -202,7 +202,6 @@ if (ENABLE_DISCORD_RPC) endif() if (ENABLE_HIDAPI) - find_package(hidapi REQUIRED) set(SUPPORTS_WIIMOTE ON) add_compile_definitions(HAS_HIDAPI) endif () diff --git a/dependencies/vcpkg b/dependencies/vcpkg index af752f21..c3867e71 160000 --- a/dependencies/vcpkg +++ b/dependencies/vcpkg @@ -1 +1 @@ -Subproject commit af752f21c9d79ba3df9cb0250ce2233933f58486 +Subproject commit c3867e714dd3a51c272826eea77267876517ed99 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3251b234..d84ab342 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -166,7 +166,7 @@ target_link_libraries(CemuBin PRIVATE CemuGui CemuInput CemuUtil - SDL2::SDL2 + SDL3::SDL3 ) if(UNIX AND NOT APPLE) diff --git a/src/gui/wxgui/CMakeLists.txt b/src/gui/wxgui/CMakeLists.txt index 12fc62d9..73e4f61a 100644 --- a/src/gui/wxgui/CMakeLists.txt +++ b/src/gui/wxgui/CMakeLists.txt @@ -145,7 +145,7 @@ target_link_libraries(CemuWxGui PRIVATE libzip::zip ZArchive::zarchive CemuComponents - SDL2::SDL2 + SDL3::SDL3 pugixml::pugixml CemuCafe PUBLIC diff --git a/src/gui/wxgui/CemuApp.cpp b/src/gui/wxgui/CemuApp.cpp index a900f10b..f72fea87 100644 --- a/src/gui/wxgui/CemuApp.cpp +++ b/src/gui/wxgui/CemuApp.cpp @@ -391,6 +391,7 @@ int CemuApp::OnExit() { wxApp::OnExit(); wxTheClipboard->Flush(); + InputManager::instance().Shutdown(); #if BOOST_OS_WINDOWS ExitProcess(0); #else diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index d24a5ca0..48c7d713 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -94,10 +94,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 () diff --git a/src/input/ControllerFactory.cpp b/src/input/ControllerFactory.cpp index 70a85a2d..f4f0eeab 100644 --- a/src/input/ControllerFactory.cpp +++ b/src/input/ControllerFactory.cpp @@ -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(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(guid, guid_index); diff --git a/src/input/InputManager.cpp b/src/input/InputManager.cpp index 64b238fc..47257d1d 100644 --- a/src/input/InputManager.cpp +++ b/src/input/InputManager.cpp @@ -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(); + } +} diff --git a/src/input/InputManager.h b/src/input/InputManager.h index 715d8f2e..7957fbf7 100644 --- a/src/input/InputManager.h +++ b/src/input/InputManager.h @@ -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& controller); diff --git a/src/input/api/DSU/DSUControllerProvider.cpp b/src/input/api/DSU/DSUControllerProvider.cpp index fa00277c..885f3284 100644 --- a/src/input/api/DSU/DSUControllerProvider.cpp +++ b/src/input/api/DSU/DSUControllerProvider.cpp @@ -42,8 +42,25 @@ DSUControllerProvider::~DSUControllerProvider() if (m_running) { m_running = false; - m_writer_thread.join(); - m_reader_thread.join(); + + boost::system::error_code ec; + m_socket.shutdown(boost::asio::ip::udp::socket::shutdown_both, ec); + m_socket.close(ec); + + if (m_reader_thread.joinable()) + { + m_reader_thread.join(); + } + + { + std::scoped_lock lock(m_writer_mutex); + m_writer_cond.notify_all(); + } + + if (m_writer_thread.joinable()) + { + m_writer_thread.join(); + } } } @@ -261,6 +278,11 @@ void DSUControllerProvider::reader_thread() const size_t len = m_socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint, 0, ec); if (ec) { + if (!m_running.load(std::memory_order_relaxed)) + { + break; + } + #ifdef DEBUG_DSU_CLIENT printf(" DSUControllerProvider::ReaderThread: exception %s\n", ec.what()); #endif diff --git a/src/input/api/SDL/SDLController.cpp b/src/input/api/SDL/SDLController.cpp index 57971ecc..2e700196 100644 --- a/src/input/api/SDL/SDLController.cpp +++ b/src/input/api/SDL/SDLController.cpp @@ -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; } @@ -54,63 +58,83 @@ bool SDLController::connect() 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) - return false; - m_controller = SDL_GameControllerOpen(index); + int gamepad_count = 0; + + SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count); + + if (!gamepad_ids) + { + return false; + } + + 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) + for (size_t i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i) { - m_axis[i] = SDL_GameControllerHasAxis(m_controller, (SDL_GameControllerAxis)i); + m_buttons[i] = SDL_GamepadHasButton(m_controller, (SDL_GamepadButton)i); } - if (SDL_GameControllerHasSensor(m_controller, SDL_SENSOR_ACCEL)) + for (size_t i = 0; i < SDL_GAMEPAD_AXIS_COUNT; ++i) { - m_has_accel = true; - SDL_GameControllerSetSensorEnabled(m_controller, SDL_SENSOR_ACCEL, SDL_TRUE); + m_axis[i] = SDL_GamepadHasAxis(m_controller, (SDL_GamepadAxis)i); } - if (SDL_GameControllerHasSensor(m_controller, SDL_SENSOR_GYRO)) + if (SDL_GamepadHasSensor(m_controller, SDL_SENSOR_ACCEL)) { - m_has_gyro = true; - SDL_GameControllerSetSensorEnabled(m_controller, SDL_SENSOR_GYRO, SDL_TRUE); + m_has_accel = SDL_SetGamepadSensorEnabled(m_controller, SDL_SENSOR_ACCEL, true); } - m_has_rumble = SDL_GameControllerRumble(m_controller, 0, 0, 0) == 0; + 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; } 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() { std::scoped_lock lock(m_controller_mutex); - if (is_connected() && !m_has_rumble) - return; - SDL_GameControllerRumble(m_controller, 0, 0, 0); + if (is_connected() && !m_has_rumble) + { + return; + } + + SDL_RumbleGamepad(m_controller, 0, 0, 0); } MotionSample SDLController::get_motion_sample() @@ -125,8 +149,10 @@ MotionSample SDLController::get_motion_sample() 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); } @@ -136,32 +162,49 @@ 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) + if (!is_connected()) { - if (m_buttons[i] && SDL_GameControllerGetButton(m_controller, (SDL_GameControllerButton)i)) - result.buttons.SetButtonState(i, true); + return result; } - if (m_axis[SDL_CONTROLLER_AXIS_LEFTX]) - result.axis.x = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_LEFTX) / 32767.0f; + for (size_t i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i) + { + if (m_buttons[i] && SDL_GetGamepadButton(m_controller, (SDL_GamepadButton)i)) + { + result.buttons.SetButtonState(i, true); + } + } - 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_GAMEPAD_AXIS_LEFTX]) + { + result.axis.x = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_LEFTX) / 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_GAMEPAD_AXIS_LEFTY]) + { + result.axis.y = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_LEFTY) / 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_GAMEPAD_AXIS_RIGHTX]) + { + result.rotation.x = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_RIGHTX) / 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_GAMEPAD_AXIS_RIGHTY]) + { + result.rotation.y = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_RIGHTY) / 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_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; } diff --git a/src/input/api/SDL/SDLController.h b/src/input/api/SDL/SDLController.h index 0a0aa183..1a4dd8dc 100644 --- a/src/input/api/SDL/SDLController.h +++ b/src/input/api/SDL/SDLController.h @@ -3,13 +3,13 @@ #include "input/api/Controller.h" #include "input/api/SDL/SDLControllerProvider.h" -#include +#include class SDLController : public Controller { 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 m_buttons{}; - std::array m_axis{}; + std::array m_buttons{}; + std::array m_axis{}; }; diff --git a/src/input/api/SDL/SDLControllerProvider.cpp b/src/input/api/SDL/SDLControllerProvider.cpp index 9b21b306..f5f9cfea 100644 --- a/src/input/api/SDL/SDLControllerProvider.cpp +++ b/src/input/api/SDL/SDLControllerProvider.cpp @@ -3,12 +3,12 @@ #include "input/api/SDL/SDLController.h" #include "util/helpers/TempState.h" -#include +#include #include 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,28 +16,7 @@ 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()); - } + m_motion_states.reserve(8); m_running = true; m_thread = std::thread(&SDLControllerProvider::event_thread, this); @@ -50,81 +29,122 @@ SDLControllerProvider::~SDLControllerProvider() 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_zero(evt); + evt.type = SDL_EVENT_QUIT; SDL_PushEvent(&evt); - // wait until thread exited - m_thread.join(); - } - SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC | SDL_INIT_EVENTS); + if (m_thread.joinable()) + { + m_thread.join(); + } + } } std::vector> SDLControllerProvider::get_controllers() { std::vector> result; - std::unordered_map guid_counter; + std::unordered_map 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)) + if (const char* name = SDL_GetGamepadNameForID(gamepad_ids[i])) { - const char* name = SDL_GameControllerName(controller); - result.emplace_back(std::make_shared(guid, it.first->second, name)); - SDL_GameControllerClose(controller); } else + { result.emplace_back(std::make_shared(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(m_mutex); + + auto it = m_motion_states.find(diid); + + return (it != m_motion_states.end()) ? it->second.data : MotionSample{}; } void SDLControllerProvider::event_thread() { SetThreadName("SDL_events"); + + 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()); + } + while (m_running.load(std::memory_order_relaxed)) { SDL_Event event{}; @@ -132,113 +152,129 @@ void SDLControllerProvider::event_thread() switch (event.type) { - case SDL_QUIT: + case SDL_EVENT_QUIT: + { m_running = false; - return; - - case SDL_CONTROLLERAXISMOTION: /**< Game controller axis motion */ + break; + } + case SDL_EVENT_GAMEPAD_AXIS_MOTION: /**< Game controller axis motion */ { break; } - case SDL_CONTROLLERBUTTONDOWN: /**< Game controller button pressed */ + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: /**< Game controller button pressed */ { break; } - case SDL_CONTROLLERBUTTONUP: /**< Game controller button released */ + case SDL_EVENT_GAMEPAD_BUTTON_UP: /**< Game controller button released */ { break; } - case SDL_CONTROLLERDEVICEADDED: /**< A new Game controller has been inserted into the system */ + case SDL_EVENT_GAMEPAD_ADDED: /**< 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 */ + case SDL_EVENT_GAMEPAD_REMOVED: /**< An opened Game controller has been removed */ { InputManager::instance().on_device_changed(); + m_motion_states.erase(event.gdevice.which); break; } - case SDL_CONTROLLERDEVICEREMAPPED: /**< The controller mapping was updated */ + case SDL_EVENT_GAMEPAD_REMAPPED: /**< The controller mapping was updated */ { break; } - case SDL_CONTROLLERTOUCHPADDOWN: /**< Game controller touchpad was touched */ + case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: /**< Game controller touchpad was touched */ { break; } - case SDL_CONTROLLERTOUCHPADMOTION: /**< Game controller touchpad finger was moved */ + case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: /**< Game controller touchpad finger was moved */ { break; } - case SDL_CONTROLLERTOUCHPADUP: /**< Game controller touchpad finger was lifted */ + case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: /**< Game controller touchpad finger was lifted */ { break; } - case SDL_CONTROLLERSENSORUPDATE: /**< Game controller sensor was updated */ + case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: /**< Game controller sensor was updated */ { - const auto index = event.csensor.which; - const auto ts = event.csensor.timestamp; - auto& motionTracking = m_motion_tracking[index]; + SDL_JoystickID id = event.gsensor.which; + uint64_t ts = event.gsensor.timestamp; - if (event.csensor.sensor == SDL_SENSOR_ACCEL) + std::scoped_lock lock(m_mutex); + + auto& state = m_motion_states[id]; + auto& tracking = state.tracking; + + if (event.gsensor.sensor == SDL_SENSOR_ACCEL) { - const auto dif = ts - motionTracking.lastTimestampAccel; + const auto dif = ts - tracking.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) + if (dif >= 10000000000) { - motionTracking.hasAcc = false; - motionTracking.hasGyro = false; - motionTracking.lastTimestampGyro = ts; + tracking.hasAcc = false; + tracking.hasGyro = false; + tracking.lastTimestampAccel = 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; + + 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 (motionTracking.hasAcc && motionTracking.hasGyro) + if (event.gsensor.sensor == SDL_SENSOR_GYRO) { - auto ts = std::max(motionTracking.lastTimestampGyro, motionTracking.lastTimestampAccel); - if (ts > motionTracking.lastTimestampIntegrate) + const auto dif = ts - tracking.lastTimestampGyro; + if (dif <= 0) { - const auto tsDif = ts - motionTracking.lastTimestampIntegrate; - motionTracking.lastTimestampIntegrate = ts; - float tsDifD = (float)tsDif / 1000.0f; + 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; - 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(); + 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(); } - motionTracking.hasAcc = false; - motionTracking.hasGyro = false; + + tracking.hasAcc = false; + tracking.hasGyro = false; } break; } } } + SDL_QuitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC); } diff --git a/src/input/api/SDL/SDLControllerProvider.h b/src/input/api/SDL/SDLControllerProvider.h index b736ba75..d06712e0 100644 --- a/src/input/api/SDL/SDLControllerProvider.h +++ b/src/input/api/SDL/SDLControllerProvider.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #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,16 @@ public: std::vector> 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); private: void event_thread(); - + std::atomic_bool m_running = false; std::thread m_thread; - - std::array m_motion_handler{}; - std::array m_motion_data{}; - std::array m_motion_data_mtx{}; + mutable std::shared_mutex m_mutex; struct MotionInfoTracking { @@ -49,6 +46,14 @@ private: glm::vec3 acc{}; }; - std::array m_motion_tracking{}; + struct MotionState + { + WiiUMotionHandler handler; + MotionSample data; + MotionInfoTracking tracking; + MotionState() = default; + }; + + std::unordered_map m_motion_states{}; }; diff --git a/src/input/api/Wiimote/WiimoteControllerProvider.cpp b/src/input/api/Wiimote/WiimoteControllerProvider.cpp index b55c9cd4..76b8f63b 100644 --- a/src/input/api/Wiimote/WiimoteControllerProvider.cpp +++ b/src/input/api/Wiimote/WiimoteControllerProvider.cpp @@ -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 lock(m_connectionMutex); + m_connectionCond.wait_for(lock, std::chrono::seconds(2)); } } diff --git a/src/input/api/Wiimote/WiimoteControllerProvider.h b/src/input/api/Wiimote/WiimoteControllerProvider.h index 90f28d5c..4e813023 100644 --- a/src/input/api/Wiimote/WiimoteControllerProvider.h +++ b/src/input/api/Wiimote/WiimoteControllerProvider.h @@ -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 m_connectedDevices; std::mutex m_connectedDeviceMutex; struct Wiimote diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp index 5780909f..02e12cee 100644 --- a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp @@ -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 &data) { - return hid_write(m_handle, data.data(), data.size()) >= 0; + return SDL_hid_write(m_handle, data.data(), data.size()) >= 0; } std::optional> HidapiWiimote::read_data() { std::array 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> HidapiWiimote::read_data() { std::vector HidapiWiimote::get_devices() { std::vector 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(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); } diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.h b/src/input/api/Wiimote/hidapi/HidapiWiimote.h index 952a36f0..ffb83541 100644 --- a/src/input/api/Wiimote/hidapi/HidapiWiimote.h +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.h @@ -1,11 +1,11 @@ #pragma once #include -#include +#include 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 &data) override; @@ -15,7 +15,7 @@ public: static std::vector get_devices(); private: - hid_device* m_handle; + SDL_hid_device* m_handle; const std::string m_path; }; diff --git a/src/main.cpp b/src/main.cpp index adce2680..05c3246b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,7 +31,8 @@ #endif #define SDL_MAIN_HANDLED -#include +#include +#include #if BOOST_OS_LINUX #define _putenv(__s) putenv((char*)(__s)) diff --git a/src/util/ScreenSaver/ScreenSaver.h b/src/util/ScreenSaver/ScreenSaver.h index 45291122..30bc5053 100644 --- a/src/util/ScreenSaver/ScreenSaver.h +++ b/src/util/ScreenSaver/ScreenSaver.h @@ -1,40 +1,65 @@ #include "Cemu/Logging/CemuLogging.h" -#include +#include class ScreenSaver { public: - static void SetInhibit(bool inhibit) - { - // temporary workaround because feature crashes on macOS + static void SetInhibit(bool inhibit) + { + // temporary workaround because feature crashes on macOS #if BOOST_OS_MACOS - return; + 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)"); - } - } - }; + 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; + } + + bool inhibit = *static_cast(userdata); + SDL_free(userdata); + + 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)"); + } + } + } }; diff --git a/vcpkg.json b/vcpkg.json index 634b7e5a..576c7b27 100644 --- a/vcpkg.json +++ b/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",