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;
}
TinyString DInputSource::ConvertKeyToString(InputBindingKey key)
TinyString DInputSource::ConvertKeyToString(InputBindingKey key, bool migration)
{
TinyString ret;
@ -388,7 +388,7 @@ TinyString DInputSource::ConvertKeyToString(InputBindingKey key)
if (key.source_subtype == InputSubclass::ControllerAxis)
{
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)
{

View File

@ -47,7 +47,7 @@ public:
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;
TinyString ConvertKeyToString(InputBindingKey key) override;
TinyString ConvertKeyToString(InputBindingKey key, bool migration = false) override;
TinyString ConvertKeyToIcon(InputBindingKey key) override;
private:

View File

@ -8,6 +8,7 @@
#include "SIO/Sio.h"
#include "USB/USB.h"
#include "VMManager.h"
#include "LayeredSettingsInterface.h"
#include "common/Assertions.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 void PrettifyInputBindingPart(const std::string_view binding, SmallString& ret, bool& changed);
static void AddBinding(const std::string_view binding, const InputEventHandler& handler);
static void AddBindings(const std::vector<std::string>& bindings, const InputEventHandler& handler);
static std::shared_ptr<InputBinding> AddBinding(const std::string_view binding, 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 IsAxisHandler(const InputEventHandler& handler);
@ -267,7 +270,7 @@ bool InputManager::ParseBindingAndGetSource(const std::string_view binding, Inpu
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)
{
@ -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)])
{
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 {};
}
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
if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::Device)
{
// so only take the first
if (num_keys > 0)
return ConvertInputBindingKeyToString(binding_type, keys[0]);
return ConvertInputBindingKeyToString(binding_type, keys[0], migration);
}
std::stringstream ss;
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())
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;
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)
return;
return nullptr;
// plop it in the input map for all the keys
for (u32 i = 0; i < ibinding->num_keys; i++)
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)
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())
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(
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));
}});
}},
bi.bind_type, si, section.c_str(), bi.name);
}
}
break;
@ -771,10 +823,12 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index)
if (!bindings.empty())
{
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) {
const bool state = (value > deadzone);
Pad::SetMacroButtonState(key, pad_index, macro_button_index, state);
}});
AddBindings(
bindings, InputAxisEventHandler{[pad_index, macro_button_index, deadzone](InputBindingKey key, float value) {
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 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) {
USB::SetDeviceBindValue(port, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
}});
AddBindings(
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;

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)
InputModifier modifier : 2;
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;
};
@ -80,6 +81,7 @@ union InputBindingKey
r.bits = bits;
r.modifier = InputModifier::None;
r.invert = 0;
r.needs_migration = false;
return r;
}
};
@ -203,10 +205,10 @@ namespace InputManager
std::optional<InputBindingKey> ParseInputBindingKey(const std::string_view binding);
/// 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.
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.
bool PrettifyInputBinding(SmallStringBase& binding);

View File

@ -28,7 +28,7 @@ public:
virtual void PollEvents() = 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;
/// 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_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"))
{
key.source_subtype = InputSubclass::ControllerMotor;
@ -613,7 +657,7 @@ std::optional<InputBindingKey> SDLInputSource::ParseKeyString(const std::string_
return std::nullopt;
}
TinyString SDLInputSource::ConvertKeyToString(InputBindingKey key)
TinyString SDLInputSource::ConvertKeyToString(InputBindingKey key, bool migration)
{
TinyString ret;
@ -625,7 +669,7 @@ TinyString SDLInputSource::ConvertKeyToString(InputBindingKey key)
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]);
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)
{

View File

@ -35,7 +35,7 @@ public:
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;
TinyString ConvertKeyToString(InputBindingKey key) override;
TinyString ConvertKeyToString(InputBindingKey key, bool migration = false) override;
TinyString ConvertKeyToIcon(InputBindingKey key) override;
bool ProcessSDLEvent(const SDL_Event* event);

View File

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

View File

@ -84,7 +84,7 @@ public:
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;
TinyString ConvertKeyToString(InputBindingKey key) override;
TinyString ConvertKeyToString(InputBindingKey key, bool migration = false) override;
TinyString ConvertKeyToIcon(InputBindingKey key) override;
private: