diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp index 7963fc22d1..4b887c10fa 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp @@ -93,6 +93,12 @@ QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& dev if (!full_expression.isEmpty()) full_expression += QChar::fromLatin1('+'); + // Return the parent-most name if there is one for better hotkey strings. + // Detection of L/R_Ctrl will be changed to just Ctrl. + // Users can manually map L_Ctrl if they so desire. + if (quote == Quote::On) + input = device->GetParentMostInput(input); + full_expression += MappingCommon::GetExpressionForControl( QString::fromStdString(input->GetName()), device_qualifier, default_device, quote); } diff --git a/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp b/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp index 9e36c49a05..cbc4a1462b 100644 --- a/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp +++ b/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp @@ -291,9 +291,14 @@ bool HotkeySuppressions::IsSuppressedIgnoringModifiers(Device::Input* input, auto it = m_suppressions.lower_bound({input, nullptr}); auto it_end = m_suppressions.lower_bound({input + 1, nullptr}); + // We need to ignore L_Ctrl R_Ctrl when supplied Ctrl and vice-versa. + const auto is_same_modifier = [](Device::Input* i1, Device::Input* i2) { + return i1 == i2 || i1->IsChild(i2) || i2->IsChild(i1); + }; + return std::any_of(it, it_end, [&](auto& s) { return std::none_of(begin(ignore_modifiers), end(ignore_modifiers), - [&](auto& m) { return m->GetInput() == s.first.second; }); + [&](auto& m) { return is_same_modifier(m->GetInput(), s.first.second); }); }); } diff --git a/Source/Core/InputCommon/ControllerInterface/Device.cpp b/Source/Core/InputCommon/ControllerInterface/Device.cpp index e57e1c7682..271a42c9ab 100644 --- a/Source/Core/InputCommon/ControllerInterface/Device.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Device.cpp @@ -21,6 +21,38 @@ namespace ciface::Core // Note: Detect() logic assumes this is greater than 0.5. constexpr ControlState INPUT_DETECT_THRESHOLD = 0.55; +class CombinedInput final : public Device::Input +{ +public: + using Inputs = std::pair; + + CombinedInput(std::string name, const Inputs& inputs) : m_name(std::move(name)), m_inputs(inputs) + { + } + ControlState GetState() const override + { + ControlState result = 0; + + if (m_inputs.first) + result = m_inputs.first->GetState(); + + if (m_inputs.second) + result = std::max(result, m_inputs.second->GetState()); + + return result; + } + std::string GetName() const override { return m_name; } + bool IsDetectable() const override { return false; } + bool IsChild(const Input* input) const override + { + return m_inputs.first == input || m_inputs.second == input; + } + +private: + const std::string m_name; + const std::pair m_inputs; +}; + Device::~Device() { // delete inputs @@ -52,6 +84,20 @@ std::string Device::GetQualifiedName() const return fmt::format("{}/{}/{}", GetSource(), GetId(), GetName()); } +auto Device::GetParentMostInput(Input* child) const -> Input* +{ + for (auto* input : m_inputs) + { + if (input->IsChild(child)) + { + // Running recursively is currently unnecessary but it doesn't hurt. + return GetParentMostInput(input); + } + } + + return child; +} + Device::Input* Device::FindInput(std::string_view name) const { for (Input* input : m_inputs) @@ -103,34 +149,6 @@ bool Device::FullAnalogSurface::IsMatchingName(std::string_view name) const return old_name == name; } -Device::CombinedInput::CombinedInput(std::string name, const Inputs& inputs) - : m_name(std::move(name)), m_inputs(inputs) -{ -} - -ControlState Device::CombinedInput::GetState() const -{ - ControlState result = 0; - - if (m_inputs.first) - result = m_inputs.first->GetState(); - - if (m_inputs.second) - result = std::max(result, m_inputs.second->GetState()); - - return result; -} - -std::string Device::CombinedInput::GetName() const -{ - return m_name; -} - -bool Device::CombinedInput::IsDetectable() -{ - return false; -} - void Device::AddCombinedInput(std::string name, const std::pair& inputs) { AddInput(new CombinedInput(std::move(name), {FindInput(inputs.first), FindInput(inputs.second)})); diff --git a/Source/Core/InputCommon/ControllerInterface/Device.h b/Source/Core/InputCommon/ControllerInterface/Device.h index 495c64cbde..fa97a79a59 100644 --- a/Source/Core/InputCommon/ControllerInterface/Device.h +++ b/Source/Core/InputCommon/ControllerInterface/Device.h @@ -85,6 +85,11 @@ public: virtual ControlState GetState() const = 0; Input* ToInput() override { return this; } + + // Overridden by CombinedInput, + // so hotkey logic knows Ctrl, L_Ctrl, and R_Ctrl are the same, + // and so input detection can return the parent name. + virtual bool IsChild(const Input*) const { return false; } }; // @@ -119,6 +124,8 @@ public: const std::vector& Inputs() const { return m_inputs; } const std::vector& Outputs() const { return m_outputs; } + Input* GetParentMostInput(Input* input) const; + Input* FindInput(std::string_view name) const; Output* FindOutput(std::string_view name) const; @@ -147,21 +154,6 @@ protected: AddInput(new FullAnalogSurface(high, low)); } - class CombinedInput final : public Input - { - public: - using Inputs = std::pair; - - CombinedInput(std::string name, const Inputs& inputs); - ControlState GetState() const override; - std::string GetName() const override; - bool IsDetectable() override; - - private: - const std::string m_name; - const std::pair m_inputs; - }; - void AddCombinedInput(std::string name, const std::pair& inputs); private: