InputManager: Add generic input mappings

This commit is contained in:
Connor McLaughlin 2022-03-25 22:15:53 +10:00 committed by refractionpcsx2
parent 9504671919
commit 51d47a1455
7 changed files with 257 additions and 0 deletions

View File

@ -935,6 +935,61 @@ std::vector<InputBindingKey> InputManager::EnumerateMotors()
return ret;
}
static void GetKeyboardGenericBindingMapping(std::vector<std::pair<GenericInputBinding, std::string>>* mapping)
{
mapping->emplace_back(GenericInputBinding::DPadUp, "Keyboard/Up");
mapping->emplace_back(GenericInputBinding::DPadRight, "Keyboard/Right");
mapping->emplace_back(GenericInputBinding::DPadDown, "Keyboard/Down");
mapping->emplace_back(GenericInputBinding::DPadLeft, "Keyboard/Left");
mapping->emplace_back(GenericInputBinding::LeftStickUp, "Keyboard/W");
mapping->emplace_back(GenericInputBinding::LeftStickRight, "Keyboard/D");
mapping->emplace_back(GenericInputBinding::LeftStickDown, "Keyboard/S");
mapping->emplace_back(GenericInputBinding::LeftStickLeft, "Keyboard/A");
mapping->emplace_back(GenericInputBinding::RightStickUp, "Keyboard/T");
mapping->emplace_back(GenericInputBinding::RightStickRight, "Keyboard/H");
mapping->emplace_back(GenericInputBinding::RightStickDown, "Keyboard/G");
mapping->emplace_back(GenericInputBinding::RightStickLeft, "Keyboard/F");
mapping->emplace_back(GenericInputBinding::Start, "Keyboard/Return");
mapping->emplace_back(GenericInputBinding::Select, "Keyboard/Backspace");
mapping->emplace_back(GenericInputBinding::Triangle, "Keyboard/I");
mapping->emplace_back(GenericInputBinding::Circle, "Keyboard/L");
mapping->emplace_back(GenericInputBinding::Cross, "Keyboard/K");
mapping->emplace_back(GenericInputBinding::Square, "Keyboard/J");
mapping->emplace_back(GenericInputBinding::L1, "Keyboard/Q");
mapping->emplace_back(GenericInputBinding::L2, "Keyboard/1");
mapping->emplace_back(GenericInputBinding::L3, "Keyboard/2");
mapping->emplace_back(GenericInputBinding::R1, "Keyboard/E");
mapping->emplace_back(GenericInputBinding::R2, "Keyboard/2");
mapping->emplace_back(GenericInputBinding::R3, "Keyboard/4");
}
static bool GetInternalGenericBindingMapping(const std::string_view& device, GenericInputBindingMapping* mapping)
{
if (device == "Keyboard")
{
GetKeyboardGenericBindingMapping(mapping);
return true;
}
return false;
}
GenericInputBindingMapping InputManager::GetGenericBindingMapping(const std::string_view& device)
{
GenericInputBindingMapping mapping;
if (!GetInternalGenericBindingMapping(device, &mapping))
{
for (u32 i = FIRST_EXTERNAL_INPUT_SOURCE; i < LAST_EXTERNAL_INPUT_SOURCE; i++)
{
if (s_input_sources[i] && s_input_sources[i]->GetGenericBindingMapping(device, &mapping))
break;
}
}
return mapping;
}
template <typename T>
static void UpdateInputSourceState(SettingsInterface& si, InputSourceType type, bool default_state)
{

View File

@ -125,6 +125,50 @@ DECLARE_HOTKEY_LIST(g_vm_manager_hotkeys);
DECLARE_HOTKEY_LIST(g_gs_hotkeys);
DECLARE_HOTKEY_LIST(g_host_hotkeys);
/// Generic input bindings. These roughly match a DualShock 4 or XBox One controller.
/// They are used for automatic binding to PS2 controller types.
enum class GenericInputBinding : u8
{
Unknown,
DPadUp,
DPadRight,
DPadLeft,
DPadDown,
LeftStickUp,
LeftStickRight,
LeftStickDown,
LeftStickLeft,
L3,
RightStickUp,
RightStickRight,
RightStickDown,
RightStickLeft,
R3,
Triangle, // Y on XBox pads.
Circle, // B on XBox pads.
Cross, // A on XBox pads.
Square, // X on XBox pads.
Select, // Share on DS4, View on XBox pads.
Start, // Options on DS4, Menu on XBox pads.
System, // PS button on DS4, Guide button on XBox pads.
L1, // LB on Xbox pads.
L2, // Left trigger on XBox pads.
R1, // RB on XBox pads.
R2, // Right trigger on Xbox pads.
SmallMotor, // High frequency vibration.
LargeMotor, // Low frequency vibration.
Count,
};
using GenericInputBindingMapping = std::vector<std::pair<GenericInputBinding, std::string>>;
/// External input source class.
class InputSource;
@ -178,6 +222,9 @@ namespace InputManager
/// Enumerates available vibration motors at the time of call.
std::vector<InputBindingKey> EnumerateMotors();
/// Retrieves bindings that match the generic bindings for the specified device.
GenericInputBindingMapping GetGenericBindingMapping(const std::string_view& device);
/// Re-parses the config and registers all hotkey and pad bindings.
void ReloadBindings(SettingsInterface& si);

View File

@ -47,6 +47,10 @@ public:
/// Enumerates available vibration motors at the time of call.
virtual std::vector<InputBindingKey> EnumerateMotors() = 0;
/// Retrieves bindings that match the generic bindings for the specified device.
/// Returns false if it's not one of our devices.
virtual bool GetGenericBindingMapping(const std::string_view& device, GenericInputBindingMapping* mapping) = 0;
/// Informs the source of a new vibration motor state. Changes may not take effect until the next PollEvents() call.
virtual void UpdateMotorState(InputBindingKey key, float intensity) = 0;

View File

@ -31,6 +31,14 @@ static const char* s_sdl_axis_names[] = {
"LeftTrigger", // SDL_CONTROLLER_AXIS_TRIGGERLEFT
"RightTrigger", // SDL_CONTROLLER_AXIS_TRIGGERRIGHT
};
static const GenericInputBinding s_sdl_generic_binding_axis_mapping[][2] = {
{GenericInputBinding::LeftStickLeft, GenericInputBinding::LeftStickRight}, // SDL_CONTROLLER_AXIS_LEFTX
{GenericInputBinding::LeftStickUp, GenericInputBinding::LeftStickDown}, // SDL_CONTROLLER_AXIS_LEFTY
{GenericInputBinding::RightStickLeft, GenericInputBinding::RightStickRight}, // SDL_CONTROLLER_AXIS_RIGHTX
{GenericInputBinding::RightStickUp, GenericInputBinding::RightStickDown}, // SDL_CONTROLLER_AXIS_RIGHTY
{GenericInputBinding::Unknown, GenericInputBinding::L2}, // SDL_CONTROLLER_AXIS_TRIGGERLEFT
{GenericInputBinding::Unknown, GenericInputBinding::R2}, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT
};
static const char* s_sdl_button_names[] = {
"A", // SDL_CONTROLLER_BUTTON_A
@ -55,6 +63,29 @@ static const char* s_sdl_button_names[] = {
"Paddle4", // SDL_CONTROLLER_BUTTON_PADDLE4
"Touchpad", // SDL_CONTROLLER_BUTTON_TOUCHPAD
};
static const GenericInputBinding s_sdl_generic_binding_button_mapping[] = {
GenericInputBinding::Cross, // SDL_CONTROLLER_BUTTON_A
GenericInputBinding::Circle, // SDL_CONTROLLER_BUTTON_B
GenericInputBinding::Square, // SDL_CONTROLLER_BUTTON_X
GenericInputBinding::Triangle, // SDL_CONTROLLER_BUTTON_Y
GenericInputBinding::Select, // SDL_CONTROLLER_BUTTON_BACK
GenericInputBinding::System, // SDL_CONTROLLER_BUTTON_GUIDE
GenericInputBinding::Start, // SDL_CONTROLLER_BUTTON_START
GenericInputBinding::L3, // SDL_CONTROLLER_BUTTON_LEFTSTICK
GenericInputBinding::R3, // SDL_CONTROLLER_BUTTON_RIGHTSTICK
GenericInputBinding::L1, // SDL_CONTROLLER_BUTTON_LEFTSHOULDER
GenericInputBinding::R1, // SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
GenericInputBinding::DPadUp, // SDL_CONTROLLER_BUTTON_DPAD_UP
GenericInputBinding::DPadDown, // SDL_CONTROLLER_BUTTON_DPAD_DOWN
GenericInputBinding::DPadLeft, // SDL_CONTROLLER_BUTTON_DPAD_LEFT
GenericInputBinding::DPadRight, // SDL_CONTROLLER_BUTTON_DPAD_RIGHT
GenericInputBinding::Unknown, // SDL_CONTROLLER_BUTTON_MISC1
GenericInputBinding::Unknown, // SDL_CONTROLLER_BUTTON_PADDLE1
GenericInputBinding::Unknown, // SDL_CONTROLLER_BUTTON_PADDLE2
GenericInputBinding::Unknown, // SDL_CONTROLLER_BUTTON_PADDLE3
GenericInputBinding::Unknown, // SDL_CONTROLLER_BUTTON_PADDLE4
GenericInputBinding::Unknown, // SDL_CONTROLLER_BUTTON_TOUCHPAD
};
SDLInputSource::SDLInputSource() = default;
@ -478,6 +509,60 @@ std::vector<InputBindingKey> SDLInputSource::EnumerateMotors()
return ret;
}
bool SDLInputSource::GetGenericBindingMapping(const std::string_view& device, GenericInputBindingMapping* mapping)
{
if (!StringUtil::StartsWith(device, "SDL-"))
return false;
const std::optional<s32> player_id = StringUtil::FromChars<s32>(device.substr(4));
if (!player_id.has_value() || player_id.value() < 0)
return false;
ControllerDataVector::iterator it = GetControllerDataForPlayerId(player_id.value());
if (it == m_controllers.end())
return false;
if (it->game_controller)
{
// assume all buttons are present.
const s32 pid = player_id.value();
for (u32 i = 0; i < std::size(s_sdl_generic_binding_axis_mapping); i++)
{
const GenericInputBinding negative = s_sdl_generic_binding_axis_mapping[i][0];
const GenericInputBinding positive = s_sdl_generic_binding_axis_mapping[i][1];
if (negative != GenericInputBinding::Unknown)
mapping->emplace_back(negative, StringUtil::StdStringFromFormat("SDL-%d/-%s", pid, s_sdl_axis_names[i]));
if (positive != GenericInputBinding::Unknown)
mapping->emplace_back(positive, StringUtil::StdStringFromFormat("SDL-%d/+%s", pid, s_sdl_axis_names[i]));
}
for (u32 i = 0; i < std::size(s_sdl_generic_binding_button_mapping); i++)
{
const GenericInputBinding binding = s_sdl_generic_binding_button_mapping[i];
if (binding != GenericInputBinding::Unknown)
mapping->emplace_back(binding, StringUtil::StdStringFromFormat("SDL-%d/%s", pid, s_sdl_button_names[i]));
}
if (it->use_game_controller_rumble || it->haptic_left_right_effect)
{
mapping->emplace_back(GenericInputBinding::SmallMotor, StringUtil::StdStringFromFormat("SDL-%d/SmallMotor", pid));
mapping->emplace_back(GenericInputBinding::LargeMotor, StringUtil::StdStringFromFormat("SDL-%d/LargeMotor", pid));
}
else
{
mapping->emplace_back(GenericInputBinding::SmallMotor, StringUtil::StdStringFromFormat("SDL-%d/Haptic", pid));
mapping->emplace_back(GenericInputBinding::LargeMotor, StringUtil::StdStringFromFormat("SDL-%d/Haptic", pid));
}
return true;
}
else
{
// joysticks, which we haven't implemented yet anyway.
return false;
}
}
void SDLInputSource::UpdateMotorState(InputBindingKey key, float intensity)
{
if (key.source_subtype != InputSubclass::ControllerMotor && key.source_subtype != InputSubclass::ControllerHaptic)

View File

@ -36,6 +36,7 @@ public:
void PollEvents() override;
std::vector<std::pair<std::string, std::string>> EnumerateDevices() override;
std::vector<InputBindingKey> EnumerateMotors() override;
bool GetGenericBindingMapping(const std::string_view& device, GenericInputBindingMapping* mapping) override;
void UpdateMotorState(InputBindingKey key, float intensity) override;
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override;

View File

@ -29,6 +29,14 @@ const char* XInputSource::s_axis_names[XInputSource::NUM_AXES] = {
"LeftTrigger", // AXIS_TRIGGERLEFT
"RightTrigger", // AXIS_TRIGGERRIGHT
};
static const GenericInputBinding s_sdl_generic_binding_axis_mapping[][2] = {
{GenericInputBinding::LeftStickLeft, GenericInputBinding::LeftStickRight}, // AXIS_LEFTX
{GenericInputBinding::LeftStickUp, GenericInputBinding::LeftStickDown}, // AXIS_LEFTY
{GenericInputBinding::RightStickLeft, GenericInputBinding::RightStickRight}, // AXIS_RIGHTX
{GenericInputBinding::RightStickUp, GenericInputBinding::RightStickDown}, // AXIS_RIGHTY
{GenericInputBinding::Unknown, GenericInputBinding::L2}, // AXIS_TRIGGERLEFT
{GenericInputBinding::Unknown, GenericInputBinding::R2}, // AXIS_TRIGGERRIGHT
};
const char* XInputSource::s_button_names[XInputSource::NUM_BUTTONS] = {
"DPadUp", // XINPUT_GAMEPAD_DPAD_UP
@ -64,6 +72,23 @@ const u16 XInputSource::s_button_masks[XInputSource::NUM_BUTTONS] = {
XINPUT_GAMEPAD_Y,
0x400, // XINPUT_GAMEPAD_GUIDE
};
static const GenericInputBinding s_sdl_generic_binding_button_mapping[] = {
GenericInputBinding::DPadUp, // XINPUT_GAMEPAD_DPAD_UP
GenericInputBinding::DPadDown, // XINPUT_GAMEPAD_DPAD_DOWN
GenericInputBinding::DPadLeft, // XINPUT_GAMEPAD_DPAD_LEFT
GenericInputBinding::DPadRight, // XINPUT_GAMEPAD_DPAD_RIGHT
GenericInputBinding::Start, // XINPUT_GAMEPAD_START
GenericInputBinding::Select, // XINPUT_GAMEPAD_BACK
GenericInputBinding::L3, // XINPUT_GAMEPAD_LEFT_THUMB
GenericInputBinding::R3, // XINPUT_GAMEPAD_RIGHT_THUMB
GenericInputBinding::L1, // XINPUT_GAMEPAD_LEFT_SHOULDER
GenericInputBinding::R1, // XINPUT_GAMEPAD_RIGHT_SHOULDER
GenericInputBinding::Cross, // XINPUT_GAMEPAD_A
GenericInputBinding::Circle, // XINPUT_GAMEPAD_B
GenericInputBinding::Square, // XINPUT_GAMEPAD_X
GenericInputBinding::Triangle, // XINPUT_GAMEPAD_Y
GenericInputBinding::System, // XINPUT_GAMEPAD_GUIDE
};
XInputSource::XInputSource() = default;
@ -286,6 +311,45 @@ std::vector<InputBindingKey> XInputSource::EnumerateMotors()
return ret;
}
bool XInputSource::GetGenericBindingMapping(const std::string_view& device, GenericInputBindingMapping* mapping)
{
if (!StringUtil::StartsWith(device, "XInput-"))
return false;
const std::optional<s32> player_id = StringUtil::FromChars<s32>(device.substr(7));
if (!player_id.has_value() || player_id.value() < 0)
return false;
if (player_id.value() < 0 || player_id.value() >= static_cast<s32>(XUSER_MAX_COUNT))
return false;
// assume all buttons are present.
const s32 pid = player_id.value();
for (u32 i = 0; i < std::size(s_sdl_generic_binding_axis_mapping); i++)
{
const GenericInputBinding negative = s_sdl_generic_binding_axis_mapping[i][0];
const GenericInputBinding positive = s_sdl_generic_binding_axis_mapping[i][1];
if (negative != GenericInputBinding::Unknown)
mapping->emplace_back(negative, StringUtil::StdStringFromFormat("XInput-%d/-%s", pid, s_axis_names[i]));
if (positive != GenericInputBinding::Unknown)
mapping->emplace_back(positive, StringUtil::StdStringFromFormat("XInput-%d/+%s", pid, s_axis_names[i]));
}
for (u32 i = 0; i < std::size(s_sdl_generic_binding_button_mapping); i++)
{
const GenericInputBinding binding = s_sdl_generic_binding_button_mapping[i];
if (binding != GenericInputBinding::Unknown)
mapping->emplace_back(binding, StringUtil::StdStringFromFormat("XInput-%d/%s", pid, s_button_names[i]));
}
if (m_controllers[pid].has_small_motor)
mapping->emplace_back(GenericInputBinding::SmallMotor, StringUtil::StdStringFromFormat("XInput-%d/SmallMotor", pid));
if (m_controllers[pid].has_large_motor)
mapping->emplace_back(GenericInputBinding::LargeMotor, StringUtil::StdStringFromFormat("XInput-%d/LargeMotor", pid));
return true;
}
void XInputSource::HandleControllerConnection(u32 index)
{
Console.WriteLn("XInput controller %u connected.", index);

View File

@ -36,6 +36,7 @@ public:
void PollEvents() override;
std::vector<std::pair<std::string, std::string>> EnumerateDevices() override;
std::vector<InputBindingKey> EnumerateMotors() override;
bool GetGenericBindingMapping(const std::string_view& device, GenericInputBindingMapping* mapping) override;
void UpdateMotorState(InputBindingKey key, float intensity) override;
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override;