Cemu/src/input/api/SDL/SDLController.cpp
Exzap 3df8217a02 SDLController: Do not manually recycle controller instance
SDL_GameControllerFromInstanceID does not increase internal ref counter.
Fixes a crash when removing an SDL controller that is in use by another profile
2022-11-06 00:32:33 +01:00

167 lines
4.3 KiB
C++

#include "input/api/SDL/SDLController.h"
#include "input/api/SDL/SDLControllerProvider.h"
SDLController::SDLController(const SDL_JoystickGUID& 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));
m_uuid += tmp;
}
SDLController::SDLController(const SDL_JoystickGUID& 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));
m_uuid += tmp;
}
SDLController::~SDLController()
{
if (m_controller)
SDL_GameControllerClose(m_controller);
}
bool SDLController::is_connected()
{
std::scoped_lock lock(m_controller_mutex);
if (!m_controller)
{
return false;
}
if (!SDL_GameControllerGetAttached(m_controller))
{
m_controller = nullptr;
return false;
}
return true;
}
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)
return false;
m_controller = SDL_GameControllerOpen(index);
if (!m_controller)
return false;
if (const char* name = SDL_GameControllerName(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;
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);
}
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);
}
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))
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)
{
if (m_buttons[i] && SDL_GameControllerGetButton(m_controller, (SDL_GameControllerButton)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;
return result;
}