diff --git a/pcsx2/Input/DInputSource.cpp b/pcsx2/Input/DInputSource.cpp index aee9ad83b4..7e0fd9fdda 100644 --- a/pcsx2/Input/DInputSource.cpp +++ b/pcsx2/Input/DInputSource.cpp @@ -379,7 +379,7 @@ std::optional 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) { diff --git a/pcsx2/Input/DInputSource.h b/pcsx2/Input/DInputSource.h index b210004044..503ff1aa49 100644 --- a/pcsx2/Input/DInputSource.h +++ b/pcsx2/Input/DInputSource.h @@ -47,7 +47,7 @@ public: void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; std::optional 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: diff --git a/pcsx2/Input/InputManager.cpp b/pcsx2/Input/InputManager.cpp index 8929a4a90f..014d329deb 100644 --- a/pcsx2/Input/InputManager.cpp +++ b/pcsx2/Input/InputManager.cpp @@ -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& bindings, const InputEventHandler& handler); + static std::shared_ptr 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& 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(key.source_type)]) { - return std::string(s_input_sources[static_cast(key.source_type)]->ConvertKeyToString(key)); + return std::string(s_input_sources[static_cast(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 InputManager::AddBinding(const std::string_view binding, const InputEventHandler& handler) { std::shared_ptr ibinding; const std::vector 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& bindings, const InputEventHandler& handler) +void InputManager::AddBindings(const std::vector& bindings, const InputEventHandler& handler, + InputBindingInfo::Type binding_type, SettingsInterface& si, const char* section, const char* key) { + std::vector> ibindings; + + bool migrate = false; for (const std::string& binding : bindings) - AddBinding(binding, handler); + { + std::shared_ptr 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 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(si); + for (int i = 0; i < LayeredSettingsInterface::NUM_LAYERS; i++) + { + SettingsInterface* layer = lsi.GetLayer(static_cast(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; diff --git a/pcsx2/Input/InputManager.h b/pcsx2/Input/InputManager.h index af27fafb5b..16b6e2fba4 100644 --- a/pcsx2/Input/InputManager.h +++ b/pcsx2/Input/InputManager.h @@ -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 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); diff --git a/pcsx2/Input/InputSource.h b/pcsx2/Input/InputSource.h index 7c8e215c26..09beae28d3 100644 --- a/pcsx2/Input/InputSource.h +++ b/pcsx2/Input/InputSource.h @@ -28,7 +28,7 @@ public: virtual void PollEvents() = 0; virtual std::optional 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. diff --git a/pcsx2/Input/SDLInputSource.cpp b/pcsx2/Input/SDLInputSource.cpp index 18b8668a70..80f6d0a352 100644 --- a/pcsx2/Input/SDLInputSource.cpp +++ b/pcsx2/Input/SDLInputSource.cpp @@ -505,6 +505,50 @@ std::optional SDLInputSource::ParseKeyString(const std::string_ key.source_type = InputSourceType::SDL; key.source_index = static_cast(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(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(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(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 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(key.source_index), modifier, s_sdl_axis_names[key.data]); else - ret.format("SDL-{}/{}JoyAxis{}{}", static_cast(key.source_index), modifier, key.data - std::size(s_sdl_axis_names), (key.invert && !ShouldIgnoreInversion()) ? "~" : ""); + ret.format("SDL-{}/{}JoyAxis{}{}", static_cast(key.source_index), modifier, key.data - std::size(s_sdl_axis_names), (key.invert && (migration || !ShouldIgnoreInversion())) ? "~" : "");) } else if (key.source_subtype == InputSubclass::ControllerButton) { diff --git a/pcsx2/Input/SDLInputSource.h b/pcsx2/Input/SDLInputSource.h index 98cd6cc512..1862141d5e 100644 --- a/pcsx2/Input/SDLInputSource.h +++ b/pcsx2/Input/SDLInputSource.h @@ -35,7 +35,7 @@ public: void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; std::optional 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); diff --git a/pcsx2/Input/XInputSource.cpp b/pcsx2/Input/XInputSource.cpp index 138776f04f..a71789b84b 100644 --- a/pcsx2/Input/XInputSource.cpp +++ b/pcsx2/Input/XInputSource.cpp @@ -317,7 +317,7 @@ std::optional XInputSource::ParseKeyString(const std::string_vi return std::nullopt; } -TinyString XInputSource::ConvertKeyToString(InputBindingKey key) +TinyString XInputSource::ConvertKeyToString(InputBindingKey key, bool migration) { TinyString ret; diff --git a/pcsx2/Input/XInputSource.h b/pcsx2/Input/XInputSource.h index 7ec20548c0..e5e7b6d7ee 100644 --- a/pcsx2/Input/XInputSource.h +++ b/pcsx2/Input/XInputSource.h @@ -84,7 +84,7 @@ public: void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; std::optional 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: