SDLInputSource: Support joysticks as well as controllers

This commit is contained in:
Connor McLaughlin 2022-12-16 21:59:37 +10:00 committed by refractionpcsx2
parent 8dba6a186f
commit 4ebb5a87b2
6 changed files with 248 additions and 54 deletions

View File

@ -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>

View File

@ -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

View File

@ -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 = {};

View File

@ -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);

View File

@ -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()

View File

@ -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;