From d2729df281485651cbc3c27e4c9419f43fe61408 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Fri, 25 Oct 2019 18:49:06 -0500 Subject: [PATCH] ExpressionParser: Allow duplicate and superset modifier hotkeys to function. --- .../ControlReference/ExpressionParser.cpp | 138 +++++++++++++----- 1 file changed, 102 insertions(+), 36 deletions(-) diff --git a/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp b/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp index 04ddef976e..7747ae956d 100644 --- a/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp +++ b/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp @@ -24,28 +24,43 @@ namespace ciface::ExpressionParser { using namespace ciface::Core; +class ControlExpression; + class HotkeySuppressions { public: - bool IsSuppressed(Device::Input* input) const { return m_suppressions.count(input) != 0; } - using Suppressor = std::unique_ptr; - Suppressor MakeSuppressor(Device::Input* input) + bool IsSuppressed(Device::Input* input) const { - ++m_suppressions[input]; - return std::make_unique([this, input]() { RemoveSuppression(input); }); + // An input is suppressed if it exists in the map (with any modifier). + auto it = m_suppressions.lower_bound({input, nullptr}); + return it != m_suppressions.end() && (it->first.first == input); } + // Suppresses each input + modifier pair. + // The returned object removes the suppression on destruction. + Suppressor MakeSuppressor(const std::vector>& modifiers, + const std::unique_ptr& final_input); + + // Removes suppression for each input + modifier pair. + // The returned object restores the original suppression on destruction. + Suppressor MakeAntiSuppressor(const std::vector>& modifiers, + const std::unique_ptr& final_input); + private: - void RemoveSuppression(Device::Input* input) + using Suppression = std::pair; + using SuppressionLevel = u16; + + void RemoveSuppression(Device::Input* modifier, Device::Input* final_input) { - auto it = m_suppressions.find(input); - if (--(it->second) == 0) + auto it = m_suppressions.find({final_input, modifier}); + if ((--it->second) == 0) m_suppressions.erase(it); } - std::map m_suppressions; + // Holds counts of suppressions for each input/modifier pair. + std::map m_suppressions; }; static HotkeySuppressions s_hotkey_suppressions; @@ -227,9 +242,18 @@ public: std::shared_ptr m_device; explicit ControlExpression(ControlQualifier qualifier_) : qualifier(qualifier_) {} + ControlState GetValue() const override { - if (!input || s_hotkey_suppressions.IsSuppressed(input)) + if (s_hotkey_suppressions.IsSuppressed(input)) + return 0; + else + return GetValueIgnoringSuppression(); + } + + ControlState GetValueIgnoringSuppression() const + { + if (!input) return 0.0; // Note: Inputs may return negative values in situations where opposing directions are @@ -261,6 +285,35 @@ private: Device::Output* output = nullptr; }; +HotkeySuppressions::Suppressor +HotkeySuppressions::MakeSuppressor(const std::vector>& modifiers, + const std::unique_ptr& final_input) +{ + for (auto& modifier : modifiers) + ++m_suppressions[{final_input->GetInput(), modifier->GetInput()}]; + + return std::make_unique([this, &modifiers, &final_input]() { + for (auto& modifier : modifiers) + RemoveSuppression(modifier->GetInput(), final_input->GetInput()); + }); +} + +HotkeySuppressions::Suppressor HotkeySuppressions::MakeAntiSuppressor( + const std::vector>& modifiers, + const std::unique_ptr& final_input) +{ + decltype(m_suppressions) unsuppressed_modifiers; + + for (auto& modifier : modifiers) + unsuppressed_modifiers.insert( + m_suppressions.extract({final_input->GetInput(), modifier->GetInput()})); + + return std::make_unique( + [this, unsuppressed_modifiers{std::move(unsuppressed_modifiers)}]() mutable { + m_suppressions.merge(unsuppressed_modifiers); + }); +} + class BinaryExpression : public Expression { public: @@ -411,42 +464,45 @@ class HotkeyExpression : public Expression { public: HotkeyExpression(std::vector> inputs) - : m_inputs(std::move(inputs)) + : m_modifiers(std::move(inputs)) { + m_final_input = std::move(m_modifiers.back()); + m_modifiers.pop_back(); } ControlState GetValue() const override { - if (m_inputs.empty()) - return 0; - - const bool modifiers_pressed = std::all_of(m_inputs.begin(), std::prev(m_inputs.end()), + const bool modifiers_pressed = std::all_of(m_modifiers.begin(), m_modifiers.end(), [](const std::unique_ptr& input) { // TODO: kill magic number. return input->GetValue() > 0.5; }); + const auto final_input_state = m_final_input->GetValueIgnoringSuppression(); + if (modifiers_pressed) { - auto& final_input = **m_inputs.rbegin(); - - // Remove supression before getting value. - m_suppressor = {}; - - const ControlState final_input_state = final_input.GetValue(); - - // TODO: kill magic number. if (final_input_state < 0.5) - m_is_ready = true; - - if (m_is_ready) { - // Only suppress input when we have at least one modifier. - if (m_inputs.size() > 1) - m_suppressor = s_hotkey_suppressions.MakeSuppressor(final_input.GetInput()); + if (!m_suppressor) + EnableSuppression(); - return final_input_state; + m_is_ready = true; } + + // Ignore suppression of our own modifiers. This also allows superset modifiers to function. + const auto anti_suppression = + s_hotkey_suppressions.MakeAntiSuppressor(m_modifiers, m_final_input); + + const bool is_suppressed = s_hotkey_suppressions.IsSuppressed(m_final_input->GetInput()); + + // If some other hotkey suppressed us, require a release of final input to be ready again. + if (is_suppressed) + m_is_ready = false; + + // Our modifiers are active. Pass through the final input. + if (m_is_ready) + return final_input_state; } else { @@ -462,21 +518,31 @@ public: int CountNumControls() const override { int result = 0; - for (auto& input : m_inputs) + for (auto& input : m_modifiers) result += input->CountNumControls(); - return result; + return result + m_final_input->CountNumControls(); } void UpdateReferences(ControlEnvironment& env) override { - m_suppressor = {}; - - for (auto& input : m_inputs) + for (auto& input : m_modifiers) input->UpdateReferences(env); + + m_final_input->UpdateReferences(env); + + // We must update our suppression with valid pointers. + if (m_suppressor) + EnableSuppression(); } private: - std::vector> m_inputs; + void EnableSuppression() const + { + m_suppressor = s_hotkey_suppressions.MakeSuppressor(m_modifiers, m_final_input); + } + + std::vector> m_modifiers; + std::unique_ptr m_final_input; mutable HotkeySuppressions::Suppressor m_suppressor; mutable bool m_is_ready = false; };