input_common: sdl: Port yuzu sdl fixes (#6577)

This commit is contained in:
Narr the Reg 2023-05-31 04:01:46 -06:00 committed by GitHub
parent b91fbf3f8e
commit e33a8a9b26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 88 additions and 158 deletions

View File

@ -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<SDL_Joystick, SDLJoystickDeleter>(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, SDLJoystickDeleter> sdl_joystick;
bool has_gyro{false};
bool has_accel{false};
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
mutable std::mutex mutex;
};
@ -301,32 +335,16 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g
const auto it = joystick_map.find(guid);
if (it != joystick_map.end()) {
while (it->second.size() <= static_cast<std::size_t>(port)) {
auto joystick =
std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr);
auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
nullptr, nullptr);
it->second.emplace_back(std::move(joystick));
}
return it->second[port];
return it->second[static_cast<std::size_t>(port)];
}
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr);
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
return joystick_map[guid].emplace_back(std::move(joystick));
}
std::shared_ptr<SDLGameController> 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<std::size_t>(port)) {
auto controller = std::make_shared<SDLGameController>(
guid, static_cast<int>(it->second.size()), nullptr);
it->second.emplace_back(std::move(controller));
}
return it->second[port];
}
auto controller = std::make_shared<SDLGameController>(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<SDLJoystick> 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<SDLJoystick>& joystick) {
return sdl_joystick == joystick->GetSDLJoystick();
if (map_it == joystick_map.end()) {
return nullptr;
}
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()) {
// This is the common case: There is already an existing SDL_Joystick maped to a
// SDLJoystick. return the SDLJoystick
if (vec_it == map_it->second.end()) {
return nullptr;
}
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<SDLJoystick>& 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<SDLJoystick>(guid, static_cast<int>(map_it->second.size()),
sdl_joystick);
return map_it->second.emplace_back(std::move(joystick));
}
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
return joystick_map[guid].emplace_back(std::move(joystick));
}
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<SDLJoystick>(guid, 0, sdl_joystick);
auto joystick = std::make_shared<SDLJoystick>(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<SDLJoystick>& 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<SDLJoystick>(guid, static_cast<int>(joystick_guid_list.size()),
sdl_joystick);
const int port = static_cast<int>(joystick_guid_list.size());
auto joystick = std::make_shared<SDLJoystick>(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<SDLGameController>(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<SDLGameController>& controller) {
return !controller->GetSDLGameController();
});
if (it != controller_guid_list.end()) {
(*it)->SetSDLGameController(sdl_controller);
return;
}
auto controller = std::make_shared<SDLGameController>(
guid, static_cast<int>(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<SDLJoystick> joystick;
{
std::lock_guard lock{joystick_map_mutex};
const auto guid = GetGUID(sdl_joystick);
std::scoped_lock 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<SDLJoystick>& joystick) {
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;
});
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);
}
void SDLState::CloseGameController(SDL_GameController* sdl_controller) {
std::string guid = GetGUID(SDL_GameControllerGetJoystick(sdl_controller));
std::shared_ptr<SDLGameController> 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<SDLGameController>& controller) {
return controller->GetSDLGameController() == sdl_controller;
});
controller = *controller_it;
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<SDLJoystick> 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<MotionDevice>("sdl");
CloseJoysticks();
CloseGameControllers();
SDL_DelEventWatch(&SDLEventWatcher, this);
initialized = false;

View File

@ -39,9 +39,6 @@ public:
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
std::shared_ptr<SDLGameController> 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<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
std::mutex joystick_map_mutex;
/// Map of GUID of a list of corresponding virtual Controllers
std::unordered_map<std::string, std::vector<std::shared_ptr<SDLGameController>>> controller_map;
std::mutex controller_map_mutex;
std::shared_ptr<SDLButtonFactory> button_factory;
std::shared_ptr<SDLAnalogFactory> analog_factory;
std::shared_ptr<SDLMotionFactory> motion_factory;