diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 2e2589b35b..d88bdd8964 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -148,8 +148,38 @@ struct SDLJoystickDeleter { }; class SDLJoystick { public: - SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick) - : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick} {} + SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, + SDL_GameController* game_controller) + : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, + sdl_controller{game_controller, &SDL_GameControllerClose} { + EnableMotion(); + } + + void EnableMotion() { + if (!sdl_controller) { + return; + } +#if SDL_VERSION_ATLEAST(2, 0, 14) + SDL_GameController* controller = sdl_controller.get(); + + if (HasMotion()) { + SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_FALSE); + SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_FALSE); + } + has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE; + has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE; + if (has_accel) { + SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); + } + if (has_gyro) { + SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); + } +#endif + } + + bool HasMotion() const { + return has_gyro || has_accel; + } void SetButton(int button, bool value) { std::lock_guard lock{mutex}; @@ -233,12 +263,13 @@ public: return sdl_joystick.get(); } - void SetSDLJoystick(SDL_Joystick* joystick) { - sdl_joystick = std::unique_ptr(joystick); + SDL_GameController* GetSDLGameController() const { + return sdl_controller.get(); } - SDL_GameController* GetGameController() const { - return SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(sdl_joystick.get())); + void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) { + sdl_joystick.reset(joystick); + sdl_controller.reset(controller); } private: @@ -251,7 +282,10 @@ private: } state; std::string guid; int port; - std::unique_ptr sdl_joystick; + bool has_gyro{false}; + bool has_accel{false}; + std::unique_ptr sdl_joystick; + std::unique_ptr sdl_controller; mutable std::mutex mutex; }; @@ -301,32 +335,16 @@ std::shared_ptr SDLState::GetSDLJoystickByGUID(const std::string& g const auto it = joystick_map.find(guid); if (it != joystick_map.end()) { while (it->second.size() <= static_cast(port)) { - auto joystick = - std::make_shared(guid, static_cast(it->second.size()), nullptr); + auto joystick = std::make_shared(guid, static_cast(it->second.size()), + nullptr, nullptr); it->second.emplace_back(std::move(joystick)); } - return it->second[port]; + return it->second[static_cast(port)]; } - auto joystick = std::make_shared(guid, 0, nullptr); + auto joystick = std::make_shared(guid, 0, nullptr, nullptr); return joystick_map[guid].emplace_back(std::move(joystick)); } -std::shared_ptr SDLState::GetSDLGameControllerByGUID(const std::string& guid, - int port) { - std::lock_guard lock{controller_map_mutex}; - const auto it = controller_map.find(guid); - if (it != controller_map.end()) { - while (it->second.size() <= static_cast(port)) { - auto controller = std::make_shared( - guid, static_cast(it->second.size()), nullptr); - it->second.emplace_back(std::move(controller)); - } - return it->second[port]; - } - auto controller = std::make_shared(guid, 0, nullptr); - return controller_map[guid].emplace_back(std::move(controller)); -} - /** * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie * it to a SDLJoystick with the same guid and that port @@ -337,34 +355,21 @@ std::shared_ptr SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_ std::lock_guard lock{joystick_map_mutex}; auto map_it = joystick_map.find(guid); - if (map_it != joystick_map.end()) { - auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(), - [&sdl_joystick](const std::shared_ptr& joystick) { - return sdl_joystick == joystick->GetSDLJoystick(); - }); - if (vec_it != map_it->second.end()) { - // This is the common case: There is already an existing SDL_Joystick maped to a - // SDLJoystick. return the SDLJoystick - return *vec_it; - } - // Search for a SDLJoystick without a mapped SDL_Joystick... - auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(), - [](const std::shared_ptr& joystick) { - return !joystick->GetSDLJoystick(); - }); - if (nullptr_it != map_it->second.end()) { - // ... and map it - (*nullptr_it)->SetSDLJoystick(sdl_joystick); - return *nullptr_it; - } - // There is no SDLJoystick without a mapped SDL_Joystick - // Create a new SDLJoystick - auto joystick = std::make_shared(guid, static_cast(map_it->second.size()), - sdl_joystick); - return map_it->second.emplace_back(std::move(joystick)); + + if (map_it == joystick_map.end()) { + return nullptr; } - auto joystick = std::make_shared(guid, 0, sdl_joystick); - return joystick_map[guid].emplace_back(std::move(joystick)); + + const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(), + [&sdl_joystick](const auto& joystick) { + return joystick->GetSDLJoystick() == sdl_joystick; + }); + + if (vec_it == map_it->second.end()) { + return nullptr; + } + + return *vec_it; } Common::ParamPackage SDLState::GetSDLControllerButtonBindByGUID( @@ -372,7 +377,7 @@ Common::ParamPackage SDLState::GetSDLControllerButtonBindByGUID( Common::ParamPackage params({{"engine", "sdl"}}); params.Set("guid", guid); params.Set("port", port); - SDL_GameController* controller = GetSDLGameControllerByGUID(guid, port)->GetSDLGameController(); + SDL_GameController* controller = GetSDLJoystickByGUID(guid, port)->GetSDLGameController(); SDL_GameControllerButtonBind button_bind; if (!controller) { @@ -456,7 +461,7 @@ Common::ParamPackage SDLState::GetSDLControllerAnalogBindByGUID( Common::ParamPackage params({{"engine", "sdl"}}); params.Set("guid", guid); params.Set("port", port); - SDL_GameController* controller = GetSDLGameControllerByGUID(guid, port)->GetSDLGameController(); + SDL_GameController* controller = GetSDLJoystickByGUID(guid, port)->GetSDLGameController(); SDL_GameControllerButtonBind button_bind_x; SDL_GameControllerButtonBind button_bind_y; @@ -487,6 +492,12 @@ Common::ParamPackage SDLState::GetSDLControllerAnalogBindByGUID( void SDLState::InitJoystick(int joystick_index) { SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); + SDL_GameController* sdl_gamecontroller = nullptr; + + if (SDL_IsGameController(joystick_index)) { + sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); + } + if (!sdl_joystick) { LOG_ERROR(Input, "failed to open joystick {}, with error: {}", joystick_index, SDL_GetError()); @@ -496,93 +507,40 @@ void SDLState::InitJoystick(int joystick_index) { std::lock_guard lock{joystick_map_mutex}; if (joystick_map.find(guid) == joystick_map.end()) { - auto joystick = std::make_shared(guid, 0, sdl_joystick); + auto joystick = std::make_shared(guid, 0, sdl_joystick, sdl_gamecontroller); + joystick->EnableMotion(); joystick_map[guid].emplace_back(std::move(joystick)); return; } + auto& joystick_guid_list = joystick_map[guid]; - const auto it = std::find_if( - joystick_guid_list.begin(), joystick_guid_list.end(), - [](const std::shared_ptr& joystick) { return !joystick->GetSDLJoystick(); }); + const auto it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), + [](const auto& joystick) { return !joystick->GetSDLJoystick(); }); if (it != joystick_guid_list.end()) { - (*it)->SetSDLJoystick(sdl_joystick); + (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); + (*it)->EnableMotion(); return; } - auto joystick = std::make_shared(guid, static_cast(joystick_guid_list.size()), - sdl_joystick); + const int port = static_cast(joystick_guid_list.size()); + auto joystick = std::make_shared(guid, port, sdl_joystick, sdl_gamecontroller); + joystick->EnableMotion(); joystick_guid_list.emplace_back(std::move(joystick)); } -void SDLState::InitGameController(int controller_index) { - SDL_GameController* sdl_controller = SDL_GameControllerOpen(controller_index); - if (!sdl_controller) { - LOG_WARNING(Input, "failed to open joystick {} as controller", controller_index); - return; - } -#if SDL_VERSION_ATLEAST(2, 0, 14) - if (SDL_GameControllerHasSensor(sdl_controller, SDL_SENSOR_ACCEL)) { - SDL_GameControllerSetSensorEnabled(sdl_controller, SDL_SENSOR_ACCEL, SDL_TRUE); - } - if (SDL_GameControllerHasSensor(sdl_controller, SDL_SENSOR_GYRO)) { - SDL_GameControllerSetSensorEnabled(sdl_controller, SDL_SENSOR_GYRO, SDL_TRUE); - } -#endif - const std::string guid = GetGUID(SDL_GameControllerGetJoystick(sdl_controller)); - - LOG_INFO(Input, "opened joystick {} as controller", controller_index); - std::lock_guard lock{controller_map_mutex}; - if (controller_map.find(guid) == controller_map.end()) { - auto controller = std::make_shared(guid, 0, sdl_controller); - controller_map[guid].emplace_back(std::move(controller)); - return; - } - auto& controller_guid_list = controller_map[guid]; - const auto it = std::find_if(controller_guid_list.begin(), controller_guid_list.end(), - [](const std::shared_ptr& controller) { - return !controller->GetSDLGameController(); - }); - if (it != controller_guid_list.end()) { - (*it)->SetSDLGameController(sdl_controller); - return; - } - auto controller = std::make_shared( - guid, static_cast(controller_guid_list.size()), sdl_controller); - controller_guid_list.emplace_back(std::move(controller)); -} - void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { - std::string guid = GetGUID(sdl_joystick); - std::shared_ptr joystick; - { - std::lock_guard lock{joystick_map_mutex}; - // This call to guid is safe since the joystick is guaranteed to be in the map - auto& joystick_guid_list = joystick_map[guid]; - const auto joystick_it = - std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), - [&sdl_joystick](const std::shared_ptr& joystick) { - return joystick->GetSDLJoystick() == sdl_joystick; - }); - joystick = *joystick_it; - } - // Destruct SDL_Joystick outside the lock guard because SDL can internally call event calback - // which locks the mutex again - joystick->SetSDLJoystick(nullptr); -} + const auto guid = GetGUID(sdl_joystick); -void SDLState::CloseGameController(SDL_GameController* sdl_controller) { - std::string guid = GetGUID(SDL_GameControllerGetJoystick(sdl_controller)); - std::shared_ptr controller; - { - std::lock_guard lock{controller_map_mutex}; - auto& controller_guid_list = controller_map[guid]; - const auto controller_it = - std::find_if(controller_guid_list.begin(), controller_guid_list.end(), - [&sdl_controller](const std::shared_ptr& controller) { - return controller->GetSDLGameController() == sdl_controller; - }); - controller = *controller_it; + std::scoped_lock lock{joystick_map_mutex}; + // This call to guid is safe since the joystick is guaranteed to be in the map + const auto& joystick_guid_list = joystick_map[guid]; + const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), + [&sdl_joystick](const auto& joystick) { + return joystick->GetSDLJoystick() == sdl_joystick; + }); + + if (joystick_it != joystick_guid_list.end()) { + (*joystick_it)->SetSDLJoystick(nullptr, nullptr); } - controller->SetSDLGameController(nullptr); } void SDLState::HandleGameControllerEvent(const SDL_Event& event) { @@ -638,14 +596,6 @@ void SDLState::HandleGameControllerEvent(const SDL_Event& event) { LOG_DEBUG(Input, "Joystick connected with device index {}", event.jdevice.which); InitJoystick(event.jdevice.which); break; - case SDL_CONTROLLERDEVICEREMOVED: - LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.cdevice.which); - CloseGameController(SDL_GameControllerFromInstanceID(event.cdevice.which)); - break; - case SDL_CONTROLLERDEVICEADDED: - LOG_DEBUG(Input, "Controller connected with device index {}", event.cdevice.which); - InitGameController(event.cdevice.which); - break; } } @@ -654,11 +604,6 @@ void SDLState::CloseJoysticks() { joystick_map.clear(); } -void SDLState::CloseGameControllers() { - std::lock_guard lock{controller_map_mutex}; - controller_map.clear(); -} - class SDLButton final : public Input::ButtonDevice { public: explicit SDLButton(std::shared_ptr joystick_, int button_) @@ -904,9 +849,6 @@ SDLState::SDLState() { // Because the events for joystick connection happens before we have our event watcher added, we // can just open all the joysticks right here for (int i = 0; i < SDL_NumJoysticks(); ++i) { - if (SDL_IsGameController(i)) { - InitGameController(i); - } InitJoystick(i); } } @@ -918,7 +860,6 @@ SDLState::~SDLState() { UnregisterFactory("sdl"); CloseJoysticks(); - CloseGameControllers(); SDL_DelEventWatch(&SDLEventWatcher, this); initialized = false; diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index 0ce094b622..56f53121bb 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -39,9 +39,6 @@ public: std::shared_ptr GetSDLJoystickBySDLID(SDL_JoystickID sdl_id); std::shared_ptr GetSDLJoystickByGUID(const std::string& guid, int port); - std::shared_ptr GetSDLGameControllerByGUID(const std::string& guid, - int port); - Common::ParamPackage GetSDLControllerButtonBindByGUID(const std::string& guid, int port, Settings::NativeButton::Values button); Common::ParamPackage GetSDLControllerAnalogBindByGUID(const std::string& guid, int port, @@ -58,21 +55,13 @@ private: void InitJoystick(int joystick_index); void CloseJoystick(SDL_Joystick* sdl_joystick); - void InitGameController(int joystick_index); - void CloseGameController(SDL_GameController* sdl_controller); - /// Needs to be called before SDL_QuitSubSystem. void CloseJoysticks(); - void CloseGameControllers(); /// Map of GUID of a list of corresponding virtual Joysticks std::unordered_map>> joystick_map; std::mutex joystick_map_mutex; - /// Map of GUID of a list of corresponding virtual Controllers - std::unordered_map>> controller_map; - std::mutex controller_map_mutex; - std::shared_ptr button_factory; std::shared_ptr analog_factory; std::shared_ptr motion_factory;