Input: Add migrations for SDL Buttons/Axis rename

This commit is contained in:
TheLastRar 2025-02-17 20:31:40 +00:00
parent 3bc658aaf6
commit bd18d3ae48
9 changed files with 134 additions and 32 deletions

View File

@ -379,7 +379,7 @@ std::optional<InputBindingKey> DInputSource::ParseKeyString(const std::string_vi
return std::nullopt; return std::nullopt;
} }
TinyString DInputSource::ConvertKeyToString(InputBindingKey key) TinyString DInputSource::ConvertKeyToString(InputBindingKey key, bool migration)
{ {
TinyString ret; TinyString ret;
@ -388,7 +388,7 @@ TinyString DInputSource::ConvertKeyToString(InputBindingKey key)
if (key.source_subtype == InputSubclass::ControllerAxis) if (key.source_subtype == InputSubclass::ControllerAxis)
{ {
const char* modifier = (key.modifier == InputModifier::FullAxis ? "Full" : (key.modifier == InputModifier::Negate ? "-" : "+")); const char* modifier = (key.modifier == InputModifier::FullAxis ? "Full" : (key.modifier == InputModifier::Negate ? "-" : "+"));
ret.format("DInput-{}/{}Axis{}{}", u32(key.source_index), modifier, u32(key.data), (key.invert && !ShouldIgnoreInversion()) ? "~" : ""); ret.format("DInput-{}/{}Axis{}{}", u32(key.source_index), modifier, u32(key.data), (key.invert && (migration || !ShouldIgnoreInversion())) ? "~" : "");
} }
else if (key.source_subtype == InputSubclass::ControllerButton && key.data >= MAX_NUM_BUTTONS) else if (key.source_subtype == InputSubclass::ControllerButton && key.data >= MAX_NUM_BUTTONS)
{ {

View File

@ -47,7 +47,7 @@ public:
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override;
std::optional<InputBindingKey> ParseKeyString(const std::string_view device, const std::string_view binding) override; std::optional<InputBindingKey> ParseKeyString(const std::string_view device, const std::string_view binding) override;
TinyString ConvertKeyToString(InputBindingKey key) override; TinyString ConvertKeyToString(InputBindingKey key, bool migration = false) override;
TinyString ConvertKeyToIcon(InputBindingKey key) override; TinyString ConvertKeyToIcon(InputBindingKey key) override;
private: private:

View File

@ -8,6 +8,7 @@
#include "SIO/Sio.h" #include "SIO/Sio.h"
#include "USB/USB.h" #include "USB/USB.h"
#include "VMManager.h" #include "VMManager.h"
#include "LayeredSettingsInterface.h"
#include "common/Assertions.h" #include "common/Assertions.h"
#include "common/Console.h" #include "common/Console.h"
@ -94,8 +95,10 @@ namespace InputManager
static bool SplitBinding(const std::string_view binding, std::string_view* source, std::string_view* sub_binding); static bool SplitBinding(const std::string_view binding, std::string_view* source, std::string_view* sub_binding);
static void PrettifyInputBindingPart(const std::string_view binding, SmallString& ret, bool& changed); static void PrettifyInputBindingPart(const std::string_view binding, SmallString& ret, bool& changed);
static void AddBinding(const std::string_view binding, const InputEventHandler& handler); static std::shared_ptr<InputBinding> AddBinding(const std::string_view binding, const InputEventHandler& handler);
static void AddBindings(const std::vector<std::string>& bindings, const InputEventHandler& handler); // Will also apply SDL2-SDL3 migrations and update the provided section & key
static void AddBindings(const std::vector<std::string>& bindings, const InputEventHandler& handler,
InputBindingInfo::Type binding_type, SettingsInterface& si, const char* section, const char* key);
static bool ParseBindingAndGetSource(const std::string_view binding, InputBindingKey* key, InputSource** source); static bool ParseBindingAndGetSource(const std::string_view binding, InputBindingKey* key, InputSource** source);
static bool IsAxisHandler(const InputEventHandler& handler); static bool IsAxisHandler(const InputEventHandler& handler);
@ -267,7 +270,7 @@ bool InputManager::ParseBindingAndGetSource(const std::string_view binding, Inpu
return false; return false;
} }
std::string InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type binding_type, InputBindingKey key) std::string InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type binding_type, InputBindingKey key, bool migration)
{ {
if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::Device) if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::Device)
{ {
@ -315,27 +318,27 @@ std::string InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type
} }
else if (key.source_type < InputSourceType::Count && s_input_sources[static_cast<u32>(key.source_type)]) else if (key.source_type < InputSourceType::Count && s_input_sources[static_cast<u32>(key.source_type)])
{ {
return std::string(s_input_sources[static_cast<u32>(key.source_type)]->ConvertKeyToString(key)); return std::string(s_input_sources[static_cast<u32>(key.source_type)]->ConvertKeyToString(key, migration));
} }
} }
return {}; return {};
} }
std::string InputManager::ConvertInputBindingKeysToString(InputBindingInfo::Type binding_type, const InputBindingKey* keys, size_t num_keys) std::string InputManager::ConvertInputBindingKeysToString(InputBindingInfo::Type binding_type, const InputBindingKey* keys, size_t num_keys, bool migration)
{ {
// can't have a chord of devices/pointers // can't have a chord of devices/pointers
if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::Device) if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::Device)
{ {
// so only take the first // so only take the first
if (num_keys > 0) if (num_keys > 0)
return ConvertInputBindingKeyToString(binding_type, keys[0]); return ConvertInputBindingKeyToString(binding_type, keys[0], migration);
} }
std::stringstream ss; std::stringstream ss;
for (size_t i = 0; i < num_keys; i++) for (size_t i = 0; i < num_keys; i++)
{ {
const std::string keystr(ConvertInputBindingKeyToString(binding_type, keys[i])); const std::string keystr(ConvertInputBindingKeyToString(binding_type, keys[i], migration));
if (keystr.empty()) if (keystr.empty())
return std::string(); return std::string();
@ -459,7 +462,7 @@ void InputManager::PrettifyInputBindingPart(const std::string_view binding, Smal
} }
void InputManager::AddBinding(const std::string_view binding, const InputEventHandler& handler) std::shared_ptr<InputBinding> InputManager::AddBinding(const std::string_view binding, const InputEventHandler& handler)
{ {
std::shared_ptr<InputBinding> ibinding; std::shared_ptr<InputBinding> ibinding;
const std::vector<std::string_view> chord_bindings(SplitChord(binding)); const std::vector<std::string_view> chord_bindings(SplitChord(binding));
@ -493,17 +496,65 @@ void InputManager::AddBinding(const std::string_view binding, const InputEventHa
} }
if (!ibinding) if (!ibinding)
return; return nullptr;
// plop it in the input map for all the keys // plop it in the input map for all the keys
for (u32 i = 0; i < ibinding->num_keys; i++) for (u32 i = 0; i < ibinding->num_keys; i++)
s_binding_map.emplace(ibinding->keys[i].MaskDirection(), ibinding); s_binding_map.emplace(ibinding->keys[i].MaskDirection(), ibinding);
return ibinding;
} }
void InputManager::AddBindings(const std::vector<std::string>& bindings, const InputEventHandler& handler) void InputManager::AddBindings(const std::vector<std::string>& bindings, const InputEventHandler& handler,
InputBindingInfo::Type binding_type, SettingsInterface& si, const char* section, const char* key)
{ {
std::vector<std::shared_ptr<InputBinding>> ibindings;
bool migrate = false;
for (const std::string& binding : bindings) for (const std::string& binding : bindings)
AddBinding(binding, handler); {
std::shared_ptr<InputBinding> ibinding = AddBinding(binding, handler);
ibindings.push_back(ibinding);
if (ibinding)
{
// Check for SDL2-3 migrations
for (u32 i = 0; i < ibinding->num_keys; i++)
{
if (ibinding->keys[i].needs_migration)
migrate = true;
}
}
}
// Save migrations
if (migrate)
{
std::vector<std::string> new_bindings;
new_bindings.reserve(bindings.size());
for (int i = 0; i < bindings.size(); i++)
{
if (ibindings[i])
new_bindings.push_back(ConvertInputBindingKeysToString(binding_type, ibindings[i]->keys, ibindings[i]->num_keys, true));
else
// Retain invalid bindings as is
new_bindings.push_back(bindings[i]);
}
// Need to find where our binding came from
LayeredSettingsInterface& lsi = static_cast<LayeredSettingsInterface&>(si);
for (int i = 0; i < LayeredSettingsInterface::NUM_LAYERS; i++)
{
SettingsInterface* layer = lsi.GetLayer(static_cast<LayeredSettingsInterface::Layer>(i));
if (layer && layer->GetStringList(section, key) == bindings)
{
// Layer found, update settings
layer->SetStringList(section, key, new_bindings);
layer->Save();
}
}
}
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -711,7 +762,7 @@ void InputManager::AddHotkeyBindings(SettingsInterface& si)
if (bindings.empty()) if (bindings.empty())
continue; continue;
AddBindings(bindings, InputButtonEventHandler{hotkey->handler}); AddBindings(bindings, InputButtonEventHandler{hotkey->handler}, InputBindingInfo::Type::Unknown, si, "Hotkeys", hotkey->name);
} }
} }
} }
@ -753,7 +804,8 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index)
AddBindings( AddBindings(
bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index, sensitivity, deadzone](InputBindingKey key, float value) { bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index, sensitivity, deadzone](InputBindingKey key, float value) {
Pad::SetControllerState(pad_index, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value)); Pad::SetControllerState(pad_index, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
}}); }},
bi.bind_type, si, section.c_str(), bi.name);
} }
} }
break; break;
@ -771,10 +823,12 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index)
if (!bindings.empty()) if (!bindings.empty())
{ {
const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("Macro{}Deadzone", macro_button_index + 1).c_str(), 0.0f); const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("Macro{}Deadzone", macro_button_index + 1).c_str(), 0.0f);
AddBindings(bindings, InputAxisEventHandler{[pad_index, macro_button_index, deadzone](InputBindingKey key, float value) { AddBindings(
const bool state = (value > deadzone); bindings, InputAxisEventHandler{[pad_index, macro_button_index, deadzone](InputBindingKey key, float value) {
Pad::SetMacroButtonState(key, pad_index, macro_button_index, state); const bool state = (value > deadzone);
}}); Pad::SetMacroButtonState(key, pad_index, macro_button_index, state);
}},
InputBindingInfo::Type::Macro, si, section.c_str(), fmt::format("Macro{}", macro_button_index + 1).c_str());
} }
} }
@ -835,9 +889,11 @@ void InputManager::AddUSBBindings(SettingsInterface& si, u32 port)
{ {
const float sensitivity = si.GetFloatValue(section.c_str(), fmt::format("{}Scale", bi.name).c_str(), 1.0f); const float sensitivity = si.GetFloatValue(section.c_str(), fmt::format("{}Scale", bi.name).c_str(), 1.0f);
const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str(), 0.0f); const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str(), 0.0f);
AddBindings(bindings, InputAxisEventHandler{[port, bind_index = bi.bind_index, sensitivity, deadzone](InputBindingKey key, float value) { AddBindings(
USB::SetDeviceBindValue(port, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value)); bindings, InputAxisEventHandler{[port, bind_index = bi.bind_index, sensitivity, deadzone](InputBindingKey key, float value) {
}}); USB::SetDeviceBindValue(port, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
}},
bi.bind_type, si, section.c_str(), bind_name.c_str());
} }
} }
break; break;

View File

@ -63,7 +63,8 @@ union InputBindingKey
InputSubclass source_subtype : 3; ///< if 1, binding is for an axis and not a button (used for controllers) InputSubclass source_subtype : 3; ///< if 1, binding is for an axis and not a button (used for controllers)
InputModifier modifier : 2; InputModifier modifier : 2;
u32 invert : 1; ///< if 1, value is inverted prior to being sent to the sink u32 invert : 1; ///< if 1, value is inverted prior to being sent to the sink
u32 unused : 14; u32 needs_migration : 1; //< Used for SDL2-3 Migration, binding should be saved to complete migration
u32 unused : 13;
u32 data; u32 data;
}; };
@ -80,6 +81,7 @@ union InputBindingKey
r.bits = bits; r.bits = bits;
r.modifier = InputModifier::None; r.modifier = InputModifier::None;
r.invert = 0; r.invert = 0;
r.needs_migration = false;
return r; return r;
} }
}; };
@ -203,10 +205,10 @@ namespace InputManager
std::optional<InputBindingKey> ParseInputBindingKey(const std::string_view binding); std::optional<InputBindingKey> ParseInputBindingKey(const std::string_view binding);
/// Converts a input key to a string. /// Converts a input key to a string.
std::string ConvertInputBindingKeyToString(InputBindingInfo::Type binding_type, InputBindingKey key); std::string ConvertInputBindingKeyToString(InputBindingInfo::Type binding_type, InputBindingKey key, bool migration = false);
/// Converts a chord of binding keys to a string. /// Converts a chord of binding keys to a string.
std::string ConvertInputBindingKeysToString(InputBindingInfo::Type binding_type, const InputBindingKey* keys, size_t num_keys); std::string ConvertInputBindingKeysToString(InputBindingInfo::Type binding_type, const InputBindingKey* keys, size_t num_keys, bool migration = false);
/// Represents a binding with icon fonts, if available. /// Represents a binding with icon fonts, if available.
bool PrettifyInputBinding(SmallStringBase& binding); bool PrettifyInputBinding(SmallStringBase& binding);

View File

@ -28,7 +28,7 @@ public:
virtual void PollEvents() = 0; virtual void PollEvents() = 0;
virtual std::optional<InputBindingKey> ParseKeyString(const std::string_view device, const std::string_view binding) = 0; virtual std::optional<InputBindingKey> ParseKeyString(const std::string_view device, const std::string_view binding) = 0;
virtual TinyString ConvertKeyToString(InputBindingKey key) = 0; virtual TinyString ConvertKeyToString(InputBindingKey key, bool migration = false) = 0;
virtual TinyString ConvertKeyToIcon(InputBindingKey key) = 0; virtual TinyString ConvertKeyToIcon(InputBindingKey key) = 0;
/// Enumerates available devices. Returns a pair of the prefix (e.g. SDL-0) and the device name. /// Enumerates available devices. Returns a pair of the prefix (e.g. SDL-0) and the device name.

View File

@ -505,6 +505,50 @@ std::optional<InputBindingKey> SDLInputSource::ParseKeyString(const std::string_
key.source_type = InputSourceType::SDL; key.source_type = InputSourceType::SDL;
key.source_index = static_cast<u32>(player_id.value()); key.source_index = static_cast<u32>(player_id.value());
// SDL2-SDL3 migrations
if (binding.starts_with("+Axis") || binding.starts_with("-Axis"))
{
const std::string_view axis_name(binding.substr(1));
std::string_view end;
if (auto value = StringUtil::FromChars<u32>(axis_name.substr(4), 10, &end))
{
key.source_subtype = InputSubclass::ControllerAxis;
key.data = *value - 6 + std::size(s_sdl_axis_names);
key.modifier = (binding[0] == '-') ? InputModifier::Negate : InputModifier::None;
key.invert = (end == "~");
key.needs_migration = true;
return key;
}
}
else if (binding.starts_with("FullAxis"))
{
std::string_view end;
if (auto value = StringUtil::FromChars<u32>(binding.substr(8), 10, &end))
{
key.source_subtype = InputSubclass::ControllerAxis;
key.data = *value - 6 + std::size(s_sdl_axis_names);
key.modifier = InputModifier::FullAxis;
key.invert = (end == "~");
key.needs_migration = true;
return key;
}
}
else if (binding.starts_with("Button"))
{
if (auto value = StringUtil::FromChars<u32>(binding.substr(6)))
{
key.source_subtype = InputSubclass::ControllerButton;
key.data = *value - 21 + std::size(s_sdl_button_names);
key.needs_migration = true;
return key;
}
}
// End Migrations
if (binding.ends_with("Motor")) if (binding.ends_with("Motor"))
{ {
key.source_subtype = InputSubclass::ControllerMotor; key.source_subtype = InputSubclass::ControllerMotor;
@ -613,7 +657,7 @@ std::optional<InputBindingKey> SDLInputSource::ParseKeyString(const std::string_
return std::nullopt; return std::nullopt;
} }
TinyString SDLInputSource::ConvertKeyToString(InputBindingKey key) TinyString SDLInputSource::ConvertKeyToString(InputBindingKey key, bool migration)
{ {
TinyString ret; TinyString ret;
@ -625,7 +669,7 @@ TinyString SDLInputSource::ConvertKeyToString(InputBindingKey key)
if (key.data < std::size(s_sdl_axis_names)) if (key.data < std::size(s_sdl_axis_names))
ret.format("SDL-{}/{}{}", static_cast<u32>(key.source_index), modifier, s_sdl_axis_names[key.data]); ret.format("SDL-{}/{}{}", static_cast<u32>(key.source_index), modifier, s_sdl_axis_names[key.data]);
else else
ret.format("SDL-{}/{}JoyAxis{}{}", static_cast<u32>(key.source_index), modifier, key.data - std::size(s_sdl_axis_names), (key.invert && !ShouldIgnoreInversion()) ? "~" : ""); ret.format("SDL-{}/{}JoyAxis{}{}", static_cast<u32>(key.source_index), modifier, key.data - std::size(s_sdl_axis_names), (key.invert && (migration || !ShouldIgnoreInversion())) ? "~" : "");)
} }
else if (key.source_subtype == InputSubclass::ControllerButton) else if (key.source_subtype == InputSubclass::ControllerButton)
{ {

View File

@ -35,7 +35,7 @@ public:
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override;
std::optional<InputBindingKey> ParseKeyString(const std::string_view device, const std::string_view binding) override; std::optional<InputBindingKey> ParseKeyString(const std::string_view device, const std::string_view binding) override;
TinyString ConvertKeyToString(InputBindingKey key) override; TinyString ConvertKeyToString(InputBindingKey key, bool migration = false) override;
TinyString ConvertKeyToIcon(InputBindingKey key) override; TinyString ConvertKeyToIcon(InputBindingKey key) override;
bool ProcessSDLEvent(const SDL_Event* event); bool ProcessSDLEvent(const SDL_Event* event);

View File

@ -317,7 +317,7 @@ std::optional<InputBindingKey> XInputSource::ParseKeyString(const std::string_vi
return std::nullopt; return std::nullopt;
} }
TinyString XInputSource::ConvertKeyToString(InputBindingKey key) TinyString XInputSource::ConvertKeyToString(InputBindingKey key, bool migration)
{ {
TinyString ret; TinyString ret;

View File

@ -84,7 +84,7 @@ public:
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override;
std::optional<InputBindingKey> ParseKeyString(const std::string_view device, const std::string_view binding) override; std::optional<InputBindingKey> ParseKeyString(const std::string_view device, const std::string_view binding) override;
TinyString ConvertKeyToString(InputBindingKey key) override; TinyString ConvertKeyToString(InputBindingKey key, bool migration = false) override;
TinyString ConvertKeyToIcon(InputBindingKey key) override; TinyString ConvertKeyToIcon(InputBindingKey key) override;
private: private: