mirror of https://github.com/PCSX2/pcsx2.git
SDLInputSource: Support joysticks as well as controllers
This commit is contained in:
parent
8dba6a186f
commit
4ebb5a87b2
|
@ -88,6 +88,25 @@ namespace StringUtil
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
|
||||||
|
inline std::optional<T> FromChars(const std::string_view& str, int base, std::string_view* endptr)
|
||||||
|
{
|
||||||
|
T value;
|
||||||
|
|
||||||
|
const char* ptr = str.data();
|
||||||
|
const char* end = ptr + str.length();
|
||||||
|
const std::from_chars_result result = std::from_chars(ptr, end, value, base);
|
||||||
|
if (result.ec != std::errc())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (endptr)
|
||||||
|
{
|
||||||
|
const size_t remaining_len = end - ptr - 1;
|
||||||
|
*endptr = (remaining_len > 0) ? std::string_view(result.ptr, remaining_len) : std::string_view();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
|
template <typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
|
||||||
inline std::optional<T> FromChars(const std::string_view& str)
|
inline std::optional<T> FromChars(const std::string_view& str)
|
||||||
|
@ -100,6 +119,25 @@ namespace StringUtil
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
template <typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
|
||||||
|
inline std::optional<T> FromChars(const std::string_view& str, std::string_view* endptr)
|
||||||
|
{
|
||||||
|
T value;
|
||||||
|
|
||||||
|
const char* ptr = str.data();
|
||||||
|
const char* end = ptr + str.length();
|
||||||
|
const fast_float::from_chars_result result = fast_float::from_chars(ptr, end, value);
|
||||||
|
if (result.ec != std::errc())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (endptr)
|
||||||
|
{
|
||||||
|
const size_t remaining_len = end - ptr - 1;
|
||||||
|
*endptr = (remaining_len > 0) ? std::string_view(result.ptr, remaining_len) : std::string_view();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper around std::to_chars
|
/// Wrapper around std::to_chars
|
||||||
template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
|
template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
|
||||||
|
|
|
@ -53,8 +53,9 @@ enum class InputSubclass : u32
|
||||||
|
|
||||||
ControllerButton = 0,
|
ControllerButton = 0,
|
||||||
ControllerAxis = 1,
|
ControllerAxis = 1,
|
||||||
ControllerMotor = 2,
|
ControllerHat = 2,
|
||||||
ControllerHaptic = 3,
|
ControllerMotor = 3,
|
||||||
|
ControllerHaptic = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class InputModifier : u32
|
enum class InputModifier : u32
|
||||||
|
|
|
@ -50,6 +50,16 @@ InputBindingKey InputSource::MakeGenericControllerButtonKey(
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InputBindingKey InputSource::MakeGenericControllerHatKey(InputSourceType clazz, u32 controller_index, s32 hat_index, u8 hat_direction, u32 num_directions)
|
||||||
|
{
|
||||||
|
InputBindingKey key = {};
|
||||||
|
key.source_type = clazz;
|
||||||
|
key.source_index = controller_index;
|
||||||
|
key.source_subtype = InputSubclass::ControllerHat;
|
||||||
|
key.data = static_cast<u32>(hat_index) * num_directions + hat_direction;
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
InputBindingKey InputSource::MakeGenericControllerMotorKey(InputSourceType clazz, u32 controller_index, s32 motor_index)
|
InputBindingKey InputSource::MakeGenericControllerMotorKey(InputSourceType clazz, u32 controller_index, s32 motor_index)
|
||||||
{
|
{
|
||||||
InputBindingKey key = {};
|
InputBindingKey key = {};
|
||||||
|
|
|
@ -63,6 +63,10 @@ public:
|
||||||
/// Creates a key for a generic controller button event.
|
/// Creates a key for a generic controller button event.
|
||||||
static InputBindingKey MakeGenericControllerButtonKey(InputSourceType clazz, u32 controller_index, s32 button_index);
|
static InputBindingKey MakeGenericControllerButtonKey(InputSourceType clazz, u32 controller_index, s32 button_index);
|
||||||
|
|
||||||
|
/// Creates a key for a generic controller hat event.
|
||||||
|
static InputBindingKey MakeGenericControllerHatKey(
|
||||||
|
InputSourceType clazz, u32 controller_index, s32 hat_index, u8 hat_direction, u32 num_directions);
|
||||||
|
|
||||||
/// Creates a key for a generic controller motor event.
|
/// Creates a key for a generic controller motor event.
|
||||||
static InputBindingKey MakeGenericControllerMotorKey(InputSourceType clazz, u32 controller_index, s32 motor_index);
|
static InputBindingKey MakeGenericControllerMotorKey(InputSourceType clazz, u32 controller_index, s32 motor_index);
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,9 @@
|
||||||
#include "common/Console.h"
|
#include "common/Console.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
static const char* s_sdl_axis_names[] = {
|
#include "GS/GSIntrin.h" // _BitScanForward
|
||||||
|
|
||||||
|
static constexpr const char* s_sdl_axis_names[] = {
|
||||||
"LeftX", // SDL_CONTROLLER_AXIS_LEFTX
|
"LeftX", // SDL_CONTROLLER_AXIS_LEFTX
|
||||||
"LeftY", // SDL_CONTROLLER_AXIS_LEFTY
|
"LeftY", // SDL_CONTROLLER_AXIS_LEFTY
|
||||||
"RightX", // SDL_CONTROLLER_AXIS_RIGHTX
|
"RightX", // SDL_CONTROLLER_AXIS_RIGHTX
|
||||||
|
@ -31,7 +33,7 @@ static const char* s_sdl_axis_names[] = {
|
||||||
"LeftTrigger", // SDL_CONTROLLER_AXIS_TRIGGERLEFT
|
"LeftTrigger", // SDL_CONTROLLER_AXIS_TRIGGERLEFT
|
||||||
"RightTrigger", // SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
"RightTrigger", // SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
||||||
};
|
};
|
||||||
static const GenericInputBinding s_sdl_generic_binding_axis_mapping[][2] = {
|
static constexpr const GenericInputBinding s_sdl_generic_binding_axis_mapping[][2] = {
|
||||||
{GenericInputBinding::LeftStickLeft, GenericInputBinding::LeftStickRight}, // SDL_CONTROLLER_AXIS_LEFTX
|
{GenericInputBinding::LeftStickLeft, GenericInputBinding::LeftStickRight}, // SDL_CONTROLLER_AXIS_LEFTX
|
||||||
{GenericInputBinding::LeftStickUp, GenericInputBinding::LeftStickDown}, // SDL_CONTROLLER_AXIS_LEFTY
|
{GenericInputBinding::LeftStickUp, GenericInputBinding::LeftStickDown}, // SDL_CONTROLLER_AXIS_LEFTY
|
||||||
{GenericInputBinding::RightStickLeft, GenericInputBinding::RightStickRight}, // SDL_CONTROLLER_AXIS_RIGHTX
|
{GenericInputBinding::RightStickLeft, GenericInputBinding::RightStickRight}, // SDL_CONTROLLER_AXIS_RIGHTX
|
||||||
|
@ -40,7 +42,7 @@ static const GenericInputBinding s_sdl_generic_binding_axis_mapping[][2] = {
|
||||||
{GenericInputBinding::Unknown, GenericInputBinding::R2}, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
{GenericInputBinding::Unknown, GenericInputBinding::R2}, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* s_sdl_button_names[] = {
|
static constexpr const char* s_sdl_button_names[] = {
|
||||||
"A", // SDL_CONTROLLER_BUTTON_A
|
"A", // SDL_CONTROLLER_BUTTON_A
|
||||||
"B", // SDL_CONTROLLER_BUTTON_B
|
"B", // SDL_CONTROLLER_BUTTON_B
|
||||||
"X", // SDL_CONTROLLER_BUTTON_X
|
"X", // SDL_CONTROLLER_BUTTON_X
|
||||||
|
@ -63,7 +65,7 @@ static const char* s_sdl_button_names[] = {
|
||||||
"Paddle4", // SDL_CONTROLLER_BUTTON_PADDLE4
|
"Paddle4", // SDL_CONTROLLER_BUTTON_PADDLE4
|
||||||
"Touchpad", // SDL_CONTROLLER_BUTTON_TOUCHPAD
|
"Touchpad", // SDL_CONTROLLER_BUTTON_TOUCHPAD
|
||||||
};
|
};
|
||||||
static const GenericInputBinding s_sdl_generic_binding_button_mapping[] = {
|
static constexpr const GenericInputBinding s_sdl_generic_binding_button_mapping[] = {
|
||||||
GenericInputBinding::Cross, // SDL_CONTROLLER_BUTTON_A
|
GenericInputBinding::Cross, // SDL_CONTROLLER_BUTTON_A
|
||||||
GenericInputBinding::Circle, // SDL_CONTROLLER_BUTTON_B
|
GenericInputBinding::Circle, // SDL_CONTROLLER_BUTTON_B
|
||||||
GenericInputBinding::Square, // SDL_CONTROLLER_BUTTON_X
|
GenericInputBinding::Square, // SDL_CONTROLLER_BUTTON_X
|
||||||
|
@ -87,6 +89,15 @@ static const GenericInputBinding s_sdl_generic_binding_button_mapping[] = {
|
||||||
GenericInputBinding::Unknown, // SDL_CONTROLLER_BUTTON_TOUCHPAD
|
GenericInputBinding::Unknown, // SDL_CONTROLLER_BUTTON_TOUCHPAD
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr const char* s_sdl_hat_direction_names[] = {
|
||||||
|
// clang-format off
|
||||||
|
"North",
|
||||||
|
"East",
|
||||||
|
"South",
|
||||||
|
"West",
|
||||||
|
// clang-format on
|
||||||
|
};
|
||||||
|
|
||||||
SDLInputSource::SDLInputSource() = default;
|
SDLInputSource::SDLInputSource() = default;
|
||||||
|
|
||||||
SDLInputSource::~SDLInputSource()
|
SDLInputSource::~SDLInputSource()
|
||||||
|
@ -171,7 +182,7 @@ bool SDLInputSource::InitializeSubsystem()
|
||||||
void SDLInputSource::ShutdownSubsystem()
|
void SDLInputSource::ShutdownSubsystem()
|
||||||
{
|
{
|
||||||
while (!m_controllers.empty())
|
while (!m_controllers.empty())
|
||||||
CloseGameController(m_controllers.begin()->joystick_id);
|
CloseDevice(m_controllers.begin()->joystick_id);
|
||||||
|
|
||||||
if (m_sdl_subsystem_initialized)
|
if (m_sdl_subsystem_initialized)
|
||||||
{
|
{
|
||||||
|
@ -200,7 +211,7 @@ std::vector<std::pair<std::string, std::string>> SDLInputSource::EnumerateDevice
|
||||||
{
|
{
|
||||||
std::string id(StringUtil::StdStringFromFormat("SDL-%d", cd.player_id));
|
std::string id(StringUtil::StdStringFromFormat("SDL-%d", cd.player_id));
|
||||||
|
|
||||||
const char* name = SDL_GameControllerName(cd.game_controller);
|
const char* name = cd.game_controller ? SDL_GameControllerName(cd.game_controller) : SDL_JoystickName(cd.joystick);
|
||||||
if (name)
|
if (name)
|
||||||
ret.emplace_back(std::move(id), name);
|
ret.emplace_back(std::move(id), name);
|
||||||
else
|
else
|
||||||
|
@ -276,14 +287,32 @@ std::optional<InputBindingKey> SDLInputSource::ParseKeyString(const std::string_
|
||||||
}
|
}
|
||||||
else if (StringUtil::StartsWith(binding, "FullAxis"))
|
else if (StringUtil::StartsWith(binding, "FullAxis"))
|
||||||
{
|
{
|
||||||
if (auto value = StringUtil::FromChars<u32>(binding.substr(8)))
|
std::string_view end;
|
||||||
|
if (auto value = StringUtil::FromChars<u32>(binding.substr(8), 10, &end))
|
||||||
{
|
{
|
||||||
key.source_subtype = InputSubclass::ControllerAxis;
|
key.source_subtype = InputSubclass::ControllerAxis;
|
||||||
key.data = *value;
|
key.data = *value;
|
||||||
key.modifier = InputModifier::FullAxis;
|
key.modifier = InputModifier::FullAxis;
|
||||||
|
key.invert = (end == "~");
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (StringUtil::StartsWith(binding, "Hat"))
|
||||||
|
{
|
||||||
|
std::string_view hat_dir;
|
||||||
|
if (auto value = StringUtil::FromChars<u32>(binding.substr(3), 10, &hat_dir); value.has_value() && !hat_dir.empty())
|
||||||
|
{
|
||||||
|
for (u8 dir = 0; dir < static_cast<u8>(std::size(s_sdl_hat_direction_names)); dir++)
|
||||||
|
{
|
||||||
|
if (hat_dir == s_sdl_hat_direction_names[dir])
|
||||||
|
{
|
||||||
|
key.source_subtype = InputSubclass::ControllerHat;
|
||||||
|
key.data = value.value() * std::size(s_sdl_hat_direction_names) + dir;
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// must be a button
|
// must be a button
|
||||||
|
@ -338,6 +367,12 @@ std::string SDLInputSource::ConvertKeyToString(InputBindingKey key)
|
||||||
else
|
else
|
||||||
ret = StringUtil::StdStringFromFormat("SDL-%u/Button%u", key.source_index, key.data);
|
ret = StringUtil::StdStringFromFormat("SDL-%u/Button%u", key.source_index, key.data);
|
||||||
}
|
}
|
||||||
|
else if (key.source_subtype == InputSubclass::ControllerHat)
|
||||||
|
{
|
||||||
|
const u32 hat_index = key.data / static_cast<u32>(std::size(s_sdl_hat_direction_names));
|
||||||
|
const u32 hat_direction = key.data % static_cast<u32>(std::size(s_sdl_hat_direction_names));
|
||||||
|
ret = StringUtil::StdStringFromFormat("SDL-%u/Hat%u%s", key.source_index, hat_index, s_sdl_hat_direction_names[hat_direction]);
|
||||||
|
}
|
||||||
else if (key.source_subtype == InputSubclass::ControllerMotor)
|
else if (key.source_subtype == InputSubclass::ControllerMotor)
|
||||||
{
|
{
|
||||||
ret = StringUtil::StdStringFromFormat("SDL-%u/%sMotor", key.source_index, key.data ? "Large" : "Small");
|
ret = StringUtil::StdStringFromFormat("SDL-%u/%sMotor", key.source_index, key.data ? "Large" : "Small");
|
||||||
|
@ -358,14 +393,36 @@ bool SDLInputSource::ProcessSDLEvent(const SDL_Event* event)
|
||||||
case SDL_CONTROLLERDEVICEADDED:
|
case SDL_CONTROLLERDEVICEADDED:
|
||||||
{
|
{
|
||||||
Console.WriteLn("(SDLInputSource) Controller %d inserted", event->cdevice.which);
|
Console.WriteLn("(SDLInputSource) Controller %d inserted", event->cdevice.which);
|
||||||
OpenGameController(event->cdevice.which);
|
OpenDevice(event->cdevice.which, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SDL_CONTROLLERDEVICEREMOVED:
|
case SDL_CONTROLLERDEVICEREMOVED:
|
||||||
{
|
{
|
||||||
Console.WriteLn("(SDLInputSource) Controller %d removed", event->cdevice.which);
|
Console.WriteLn("(SDLInputSource) Controller %d removed", event->cdevice.which);
|
||||||
CloseGameController(event->cdevice.which);
|
CloseDevice(event->cdevice.which);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SDL_JOYDEVICEADDED:
|
||||||
|
{
|
||||||
|
// Let game controller handle.. well.. game controllers.
|
||||||
|
if (SDL_IsGameController(event->jdevice.which))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Console.WriteLn("(SDLInputSource) Joystick %d inserted", event->jdevice.which);
|
||||||
|
OpenDevice(event->cdevice.which, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_JOYDEVICEREMOVED:
|
||||||
|
{
|
||||||
|
if (auto it = GetControllerDataForJoystickId(event->cdevice.which); it != m_controllers.end() && it->game_controller)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Console.WriteLn("(SDLInputSource) Joystick %d removed", event->jdevice.which);
|
||||||
|
CloseDevice(event->cdevice.which);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,11 +440,30 @@ bool SDLInputSource::ProcessSDLEvent(const SDL_Event* event)
|
||||||
case SDL_JOYBUTTONUP:
|
case SDL_JOYBUTTONUP:
|
||||||
return HandleJoystickButtonEvent(&event->jbutton);
|
return HandleJoystickButtonEvent(&event->jbutton);
|
||||||
|
|
||||||
|
case SDL_JOYHATMOTION:
|
||||||
|
return HandleJoystickHatEvent(&event->jhat);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_Joystick* SDLInputSource::GetJoystickForDevice(const std::string_view& device)
|
||||||
|
{
|
||||||
|
if (!StringUtil::StartsWith(device, "SDL-"))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const std::optional<s32> player_id = StringUtil::FromChars<s32>(device.substr(4));
|
||||||
|
if (!player_id.has_value() || player_id.value() < 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto it = GetControllerDataForPlayerId(player_id.value());
|
||||||
|
if (it == m_controllers.end())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return it->joystick;
|
||||||
|
}
|
||||||
|
|
||||||
SDLInputSource::ControllerDataVector::iterator SDLInputSource::GetControllerDataForJoystickId(int id)
|
SDLInputSource::ControllerDataVector::iterator SDLInputSource::GetControllerDataForJoystickId(int id)
|
||||||
{
|
{
|
||||||
return std::find_if(m_controllers.begin(), m_controllers.end(), [id](const ControllerData& cd) { return cd.joystick_id == id; });
|
return std::find_if(m_controllers.begin(), m_controllers.end(), [id](const ControllerData& cd) { return cd.joystick_id == id; });
|
||||||
|
@ -415,11 +491,23 @@ int SDLInputSource::GetFreePlayerId() const
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLInputSource::OpenGameController(int index)
|
bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller)
|
||||||
{
|
{
|
||||||
SDL_GameController* gcontroller = SDL_GameControllerOpen(index);
|
SDL_GameController* gcontroller;
|
||||||
SDL_Joystick* joystick = gcontroller ? SDL_GameControllerGetJoystick(gcontroller) : nullptr;
|
SDL_Joystick* joystick;
|
||||||
if (!gcontroller || !joystick)
|
|
||||||
|
if (is_gamecontroller)
|
||||||
|
{
|
||||||
|
gcontroller = SDL_GameControllerOpen(index);
|
||||||
|
joystick = gcontroller ? SDL_GameControllerGetJoystick(gcontroller) : nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gcontroller = nullptr;
|
||||||
|
joystick = SDL_JoystickOpen(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gcontroller && !joystick)
|
||||||
{
|
{
|
||||||
Console.Error("(SDLInputSource) Failed to open controller %d", index);
|
Console.Error("(SDLInputSource) Failed to open controller %d", index);
|
||||||
if (gcontroller)
|
if (gcontroller)
|
||||||
|
@ -429,7 +517,7 @@ bool SDLInputSource::OpenGameController(int index)
|
||||||
}
|
}
|
||||||
|
|
||||||
const int joystick_id = SDL_JoystickInstanceID(joystick);
|
const int joystick_id = SDL_JoystickInstanceID(joystick);
|
||||||
int player_id = SDL_GameControllerGetPlayerIndex(gcontroller);
|
int player_id = gcontroller ? SDL_GameControllerGetPlayerIndex(gcontroller) : SDL_JoystickGetPlayerIndex(joystick);
|
||||||
if (player_id < 0 || GetControllerDataForPlayerId(player_id) != m_controllers.end())
|
if (player_id < 0 || GetControllerDataForPlayerId(player_id) != m_controllers.end())
|
||||||
{
|
{
|
||||||
const int free_player_id = GetFreePlayerId();
|
const int free_player_id = GetFreePlayerId();
|
||||||
|
@ -439,34 +527,49 @@ bool SDLInputSource::OpenGameController(int index)
|
||||||
player_id = free_player_id;
|
player_id = free_player_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLn("(SDLInputSource) Opened controller %d (instance id %d, player id %d): %s", index, joystick_id, player_id,
|
const char* name = gcontroller ? SDL_GameControllerName(gcontroller) : SDL_JoystickName(joystick);
|
||||||
SDL_GameControllerName(gcontroller));
|
if (!name)
|
||||||
|
name = "Unknown Device";
|
||||||
|
|
||||||
|
Console.WriteLn("(SDLInputSource) Opened %s %d (instance id %d, player id %d): %s", is_gamecontroller ? "game controller" : "joystick",
|
||||||
|
index, joystick_id, player_id, name);
|
||||||
|
|
||||||
ControllerData cd = {};
|
ControllerData cd = {};
|
||||||
cd.player_id = player_id;
|
cd.player_id = player_id;
|
||||||
cd.joystick_id = joystick_id;
|
cd.joystick_id = joystick_id;
|
||||||
cd.haptic_left_right_effect = -1;
|
cd.haptic_left_right_effect = -1;
|
||||||
cd.game_controller = gcontroller;
|
cd.game_controller = gcontroller;
|
||||||
|
cd.joystick = joystick;
|
||||||
|
|
||||||
const int num_axes = SDL_JoystickNumAxes(joystick);
|
if (gcontroller)
|
||||||
const int num_buttons = SDL_JoystickNumButtons(joystick);
|
{
|
||||||
cd.joy_axis_used_in_gc.resize(num_axes, false);
|
const int num_axes = SDL_JoystickNumAxes(joystick);
|
||||||
cd.joy_button_used_in_gc.resize(num_buttons, false);
|
const int num_buttons = SDL_JoystickNumButtons(joystick);
|
||||||
auto mark_bind = [&](SDL_GameControllerButtonBind bind) {
|
cd.joy_axis_used_in_gc.resize(num_axes, false);
|
||||||
if (bind.bindType == SDL_CONTROLLER_BINDTYPE_AXIS && bind.value.axis < num_axes)
|
cd.joy_button_used_in_gc.resize(num_buttons, false);
|
||||||
cd.joy_axis_used_in_gc[bind.value.axis] = true;
|
auto mark_bind = [&](SDL_GameControllerButtonBind bind) {
|
||||||
if (bind.bindType == SDL_CONTROLLER_BINDTYPE_BUTTON && bind.value.button < num_buttons)
|
if (bind.bindType == SDL_CONTROLLER_BINDTYPE_AXIS && bind.value.axis < num_axes)
|
||||||
cd.joy_button_used_in_gc[bind.value.button] = true;
|
cd.joy_axis_used_in_gc[bind.value.axis] = true;
|
||||||
};
|
if (bind.bindType == SDL_CONTROLLER_BINDTYPE_BUTTON && bind.value.button < num_buttons)
|
||||||
for (size_t i = 0; i < std::size(s_sdl_axis_names); i++)
|
cd.joy_button_used_in_gc[bind.value.button] = true;
|
||||||
mark_bind(SDL_GameControllerGetBindForAxis(gcontroller, static_cast<SDL_GameControllerAxis>(i)));
|
};
|
||||||
for (size_t i = 0; i < std::size(s_sdl_button_names); i++)
|
for (size_t i = 0; i < std::size(s_sdl_axis_names); i++)
|
||||||
mark_bind(SDL_GameControllerGetBindForButton(gcontroller, static_cast<SDL_GameControllerButton>(i)));
|
mark_bind(SDL_GameControllerGetBindForAxis(gcontroller, static_cast<SDL_GameControllerAxis>(i)));
|
||||||
|
for (size_t i = 0; i < std::size(s_sdl_button_names); i++)
|
||||||
|
mark_bind(SDL_GameControllerGetBindForButton(gcontroller, static_cast<SDL_GameControllerButton>(i)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// GC doesn't have the concept of hats, so we only need to do this for joysticks.
|
||||||
|
const int num_hats = SDL_JoystickNumHats(joystick);
|
||||||
|
if (num_hats > 0)
|
||||||
|
cd.last_hat_state.resize(static_cast<size_t>(num_hats), u8(0));
|
||||||
|
}
|
||||||
|
|
||||||
cd.use_game_controller_rumble = (SDL_GameControllerRumble(gcontroller, 0, 0, 0) == 0);
|
cd.use_game_controller_rumble = (gcontroller && SDL_GameControllerRumble(gcontroller, 0, 0, 0) == 0);
|
||||||
if (cd.use_game_controller_rumble)
|
if (cd.use_game_controller_rumble)
|
||||||
{
|
{
|
||||||
Console.WriteLn("(SDLInputSource) Rumble is supported on '%s' via gamecontroller", SDL_GameControllerName(gcontroller));
|
Console.WriteLn("(SDLInputSource) Rumble is supported on '%s' via gamecontroller", name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -499,34 +602,35 @@ bool SDLInputSource::OpenGameController(int index)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cd.haptic)
|
if (cd.haptic)
|
||||||
Console.WriteLn("(SDLInputSource) Rumble is supported on '%s' via haptic", SDL_GameControllerName(gcontroller));
|
Console.WriteLn("(SDLInputSource) Rumble is supported on '%s' via haptic", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cd.haptic && !cd.use_game_controller_rumble)
|
if (!cd.haptic && !cd.use_game_controller_rumble)
|
||||||
Console.Warning("(SDLInputSource) Rumble is not supported on '%s'", SDL_GameControllerName(gcontroller));
|
Console.Warning("(SDLInputSource) Rumble is not supported on '%s'", name);
|
||||||
|
|
||||||
m_controllers.push_back(std::move(cd));
|
m_controllers.push_back(std::move(cd));
|
||||||
|
|
||||||
const char* name = SDL_GameControllerName(cd.game_controller);
|
Host::OnInputDeviceConnected(StringUtil::StdStringFromFormat("SDL-%d", player_id), name);
|
||||||
Host::OnInputDeviceConnected(StringUtil::StdStringFromFormat("SDL-%d", player_id), name ? name : "Unknown Device");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLInputSource::CloseGameController(int joystick_index)
|
bool SDLInputSource::CloseDevice(int joystick_index)
|
||||||
{
|
{
|
||||||
auto it = GetControllerDataForJoystickId(joystick_index);
|
auto it = GetControllerDataForJoystickId(joystick_index);
|
||||||
if (it == m_controllers.end())
|
if (it == m_controllers.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
Host::OnInputDeviceDisconnected(StringUtil::StdStringFromFormat("SDL-%d", it->player_id));
|
||||||
|
|
||||||
if (it->haptic)
|
if (it->haptic)
|
||||||
SDL_HapticClose(static_cast<SDL_Haptic*>(it->haptic));
|
SDL_HapticClose(it->haptic);
|
||||||
|
|
||||||
SDL_GameControllerClose(static_cast<SDL_GameController*>(it->game_controller));
|
if (it->game_controller)
|
||||||
|
SDL_GameControllerClose(it->game_controller);
|
||||||
|
else
|
||||||
|
SDL_JoystickClose(it->joystick);
|
||||||
|
|
||||||
const int player_id = it->player_id;
|
|
||||||
m_controllers.erase(it);
|
m_controllers.erase(it);
|
||||||
|
|
||||||
Host::OnInputDeviceDisconnected(StringUtil::StdStringFromFormat("SDL-%d", player_id));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,7 +646,8 @@ bool SDLInputSource::HandleControllerAxisEvent(const SDL_ControllerAxisEvent* ev
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const InputBindingKey key(MakeGenericControllerAxisKey(InputSourceType::SDL, it->player_id, ev->axis));
|
const InputBindingKey key(MakeGenericControllerAxisKey(InputSourceType::SDL, it->player_id, ev->axis));
|
||||||
return InputManager::InvokeEvents(key, NormalizeS16(ev->value));
|
InputManager::InvokeEvents(key, NormalizeS16(ev->value));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLInputSource::HandleControllerButtonEvent(const SDL_ControllerButtonEvent* ev)
|
bool SDLInputSource::HandleControllerButtonEvent(const SDL_ControllerButtonEvent* ev)
|
||||||
|
@ -555,7 +660,8 @@ bool SDLInputSource::HandleControllerButtonEvent(const SDL_ControllerButtonEvent
|
||||||
const GenericInputBinding generic_key = (ev->button < std::size(s_sdl_generic_binding_button_mapping)) ?
|
const GenericInputBinding generic_key = (ev->button < std::size(s_sdl_generic_binding_button_mapping)) ?
|
||||||
s_sdl_generic_binding_button_mapping[ev->button] :
|
s_sdl_generic_binding_button_mapping[ev->button] :
|
||||||
GenericInputBinding::Unknown;
|
GenericInputBinding::Unknown;
|
||||||
return InputManager::InvokeEvents(key, (ev->state == SDL_PRESSED) ? 1.0f : 0.0f, generic_key);
|
InputManager::InvokeEvents(key, (ev->state == SDL_PRESSED) ? 1.0f : 0.0f, generic_key);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLInputSource::HandleJoystickAxisEvent(const SDL_JoyAxisEvent* ev)
|
bool SDLInputSource::HandleJoystickAxisEvent(const SDL_JoyAxisEvent* ev)
|
||||||
|
@ -567,7 +673,8 @@ bool SDLInputSource::HandleJoystickAxisEvent(const SDL_JoyAxisEvent* ev)
|
||||||
return false; // Will get handled by GC event
|
return false; // Will get handled by GC event
|
||||||
const u32 axis = ev->axis + std::size(s_sdl_axis_names); // Ensure we don't conflict with GC axes
|
const u32 axis = ev->axis + std::size(s_sdl_axis_names); // Ensure we don't conflict with GC axes
|
||||||
const InputBindingKey key(MakeGenericControllerAxisKey(InputSourceType::SDL, it->player_id, axis));
|
const InputBindingKey key(MakeGenericControllerAxisKey(InputSourceType::SDL, it->player_id, axis));
|
||||||
return InputManager::InvokeEvents(key, NormalizeS16(ev->value));
|
InputManager::InvokeEvents(key, NormalizeS16(ev->value));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLInputSource::HandleJoystickButtonEvent(const SDL_JoyButtonEvent* ev)
|
bool SDLInputSource::HandleJoystickButtonEvent(const SDL_JoyButtonEvent* ev)
|
||||||
|
@ -579,7 +686,34 @@ bool SDLInputSource::HandleJoystickButtonEvent(const SDL_JoyButtonEvent* ev)
|
||||||
return false; // Will get handled by GC event
|
return false; // Will get handled by GC event
|
||||||
const u32 button = ev->button + std::size(s_sdl_button_names); // Ensure we don't conflict with GC buttons
|
const u32 button = ev->button + std::size(s_sdl_button_names); // Ensure we don't conflict with GC buttons
|
||||||
const InputBindingKey key(MakeGenericControllerAxisKey(InputSourceType::SDL, it->player_id, button));
|
const InputBindingKey key(MakeGenericControllerAxisKey(InputSourceType::SDL, it->player_id, button));
|
||||||
return InputManager::InvokeEvents(key, (ev->state == SDL_PRESSED) ? 1.0f : 0.0f);
|
InputManager::InvokeEvents(key, (ev->state == SDL_PRESSED) ? 1.0f : 0.0f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDLInputSource::HandleJoystickHatEvent(const SDL_JoyHatEvent* ev)
|
||||||
|
{
|
||||||
|
auto it = GetControllerDataForJoystickId(ev->which);
|
||||||
|
if (it == m_controllers.end() || ev->hat >= it->last_hat_state.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const unsigned long last_direction = it->last_hat_state[ev->hat];
|
||||||
|
it->last_hat_state[ev->hat] = ev->value;
|
||||||
|
|
||||||
|
unsigned long changed_direction = last_direction ^ ev->value;
|
||||||
|
while (changed_direction != 0)
|
||||||
|
{
|
||||||
|
unsigned long pos;
|
||||||
|
_BitScanForward(&pos, changed_direction);
|
||||||
|
|
||||||
|
const unsigned long mask = (1u << pos);
|
||||||
|
changed_direction &= ~mask;
|
||||||
|
|
||||||
|
const InputBindingKey key(
|
||||||
|
MakeGenericControllerHatKey(InputSourceType::SDL, it->player_id, ev->hat, pos, std::size(s_sdl_hat_direction_names)));
|
||||||
|
InputManager::InvokeEvents(key, (last_direction & mask) ? 0.0f : 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<InputBindingKey> SDLInputSource::EnumerateMotors()
|
std::vector<InputBindingKey> SDLInputSource::EnumerateMotors()
|
||||||
|
|
|
@ -46,11 +46,14 @@ public:
|
||||||
|
|
||||||
bool ProcessSDLEvent(const SDL_Event* event);
|
bool ProcessSDLEvent(const SDL_Event* event);
|
||||||
|
|
||||||
|
SDL_Joystick* GetJoystickForDevice(const std::string_view& device);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ControllerData
|
struct ControllerData
|
||||||
{
|
{
|
||||||
SDL_Haptic* haptic;
|
SDL_Haptic* haptic;
|
||||||
SDL_GameController* game_controller;
|
SDL_GameController* game_controller;
|
||||||
|
SDL_Joystick* joystick;
|
||||||
u16 rumble_intensity[2];
|
u16 rumble_intensity[2];
|
||||||
int haptic_left_right_effect;
|
int haptic_left_right_effect;
|
||||||
int joystick_id;
|
int joystick_id;
|
||||||
|
@ -60,6 +63,9 @@ private:
|
||||||
// Used to disable Joystick controls that are used in GameController inputs so we don't get double events
|
// Used to disable Joystick controls that are used in GameController inputs so we don't get double events
|
||||||
std::vector<bool> joy_button_used_in_gc;
|
std::vector<bool> joy_button_used_in_gc;
|
||||||
std::vector<bool> joy_axis_used_in_gc;
|
std::vector<bool> joy_axis_used_in_gc;
|
||||||
|
|
||||||
|
// Track last hat state so we can send "unpressed" events.
|
||||||
|
std::vector<u8> last_hat_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
using ControllerDataVector = std::vector<ControllerData>;
|
using ControllerDataVector = std::vector<ControllerData>;
|
||||||
|
@ -73,12 +79,13 @@ private:
|
||||||
ControllerDataVector::iterator GetControllerDataForPlayerId(int id);
|
ControllerDataVector::iterator GetControllerDataForPlayerId(int id);
|
||||||
int GetFreePlayerId() const;
|
int GetFreePlayerId() const;
|
||||||
|
|
||||||
bool OpenGameController(int index);
|
bool OpenDevice(int index, bool is_gamecontroller);
|
||||||
bool CloseGameController(int joystick_index);
|
bool CloseDevice(int joystick_index);
|
||||||
bool HandleControllerAxisEvent(const SDL_ControllerAxisEvent* event);
|
bool HandleControllerAxisEvent(const SDL_ControllerAxisEvent* ev);
|
||||||
bool HandleControllerButtonEvent(const SDL_ControllerButtonEvent* event);
|
bool HandleControllerButtonEvent(const SDL_ControllerButtonEvent* ev);
|
||||||
bool HandleJoystickAxisEvent(const SDL_JoyAxisEvent* event);
|
bool HandleJoystickAxisEvent(const SDL_JoyAxisEvent* ev);
|
||||||
bool HandleJoystickButtonEvent(const SDL_JoyButtonEvent* event);
|
bool HandleJoystickButtonEvent(const SDL_JoyButtonEvent* ev);
|
||||||
|
bool HandleJoystickHatEvent(const SDL_JoyHatEvent* ev);
|
||||||
void SendRumbleUpdate(ControllerData* cd);
|
void SendRumbleUpdate(ControllerData* cd);
|
||||||
|
|
||||||
ControllerDataVector m_controllers;
|
ControllerDataVector m_controllers;
|
||||||
|
|
Loading…
Reference in New Issue