From e896835f86e52051b1998a344549dfa8ba79847a Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sun, 30 Dec 2018 16:06:29 -0600 Subject: [PATCH] ExpressionParser: Renamed ControlFinder to ControlEnvironment. Added support for variables and assignment operator. ControlExpression objects now reference a matching input and output so the two can me mixed in any expression. (you can set rumble directly from inputs) --- .../Config/Mapping/MappingButton.cpp | 4 +- .../ControlReference/ControlReference.cpp | 8 +- .../ControlReference/ControlReference.h | 3 +- .../ControlReference/ExpressionParser.cpp | 124 ++++++++++++++---- .../ControlReference/ExpressionParser.h | 20 ++- .../ControllerEmu/ControllerEmu.cpp | 21 ++- .../InputCommon/ControllerEmu/ControllerEmu.h | 10 ++ 7 files changed, 144 insertions(+), 46 deletions(-) diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp index 435033c8c1..a9a142276f 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp @@ -100,7 +100,7 @@ void MappingButton::Detect() return; m_reference->SetExpression(expression.toStdString()); - m_parent->GetController()->UpdateReferences(g_controller_interface); + m_parent->GetController()->UpdateSingleControlReference(g_controller_interface, m_reference); ConfigChanged(); m_parent->SaveSettings(); @@ -111,7 +111,7 @@ void MappingButton::Clear() m_reference->range = 100.0 / SLIDER_TICK_COUNT; m_reference->SetExpression(""); - m_parent->GetController()->UpdateReferences(g_controller_interface); + m_parent->GetController()->UpdateSingleControlReference(g_controller_interface, m_reference); m_parent->SaveSettings(); ConfigChanged(); diff --git a/Source/Core/InputCommon/ControlReference/ControlReference.cpp b/Source/Core/InputCommon/ControlReference/ControlReference.cpp index cfc6fa658e..c17cfb3c86 100644 --- a/Source/Core/InputCommon/ControlReference/ControlReference.cpp +++ b/Source/Core/InputCommon/ControlReference/ControlReference.cpp @@ -25,12 +25,12 @@ bool ControlReference::InputGateOn() // Updates a controlreference's binded devices/controls // need to call this to re-bind a control reference after changing its expression // -void ControlReference::UpdateReference(const ciface::Core::DeviceContainer& devices, - const ciface::Core::DeviceQualifier& default_device) +void ControlReference::UpdateReference(ciface::ExpressionParser::ControlEnvironment& env) { - ControlFinder finder(devices, default_device, IsInput()); if (m_parsed_expression) - m_parsed_expression->UpdateReferences(finder); + { + m_parsed_expression->UpdateReferences(env); + } } int ControlReference::BoundCount() const diff --git a/Source/Core/InputCommon/ControlReference/ControlReference.h b/Source/Core/InputCommon/ControlReference/ControlReference.h index 83fa288676..3f2c205501 100644 --- a/Source/Core/InputCommon/ControlReference/ControlReference.h +++ b/Source/Core/InputCommon/ControlReference/ControlReference.h @@ -30,8 +30,7 @@ public: int BoundCount() const; ciface::ExpressionParser::ParseStatus GetParseStatus() const; - void UpdateReference(const ciface::Core::DeviceContainer& devices, - const ciface::Core::DeviceQualifier& default_device); + void UpdateReference(ciface::ExpressionParser::ControlEnvironment& env); std::string GetExpression() const; void SetExpression(std::string expr); diff --git a/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp b/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp index 2de2650699..f375c465d7 100644 --- a/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp +++ b/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp @@ -35,8 +35,10 @@ enum TokenType TOK_MUL, TOK_DIV, TOK_MOD, + TOK_ASSIGN, TOK_CONTROL, TOK_LITERAL, + TOK_VARIABLE, }; inline std::string OpName(TokenType op) @@ -57,6 +59,10 @@ inline std::string OpName(TokenType op) return "Div"; case TOK_MOD: return "Mod"; + case TOK_ASSIGN: + return "Assign"; + case TOK_VARIABLE: + return "Var"; default: assert(false); return ""; @@ -97,10 +103,14 @@ public: return "/"; case TOK_MOD: return "%"; + case TOK_ASSIGN: + return "="; case TOK_CONTROL: return "Device(" + data + ")"; case TOK_LITERAL: return '\'' + data + '\''; + case TOK_VARIABLE: + return '$' + data; case TOK_INVALID: break; } @@ -131,21 +141,23 @@ public: return false; } - Token GetUnaryFunction() + std::string FetchWordChars() { - std::string name; + std::string word; std::regex valid_name_char("[a-z0-9_]", std::regex_constants::icase); while (it != expr.end() && std::regex_match(std::string(1, *it), valid_name_char)) { - name += *it; + word += *it; ++it; } - return Token(TOK_UNARY, name); + return word; } + Token GetUnaryFunction() { return Token(TOK_UNARY, FetchWordChars()); } + Token GetLiteral() { std::string value; @@ -153,6 +165,8 @@ public: return Token(TOK_LITERAL, value); } + Token GetVariable() { return Token(TOK_VARIABLE, FetchWordChars()); } + Token GetFullyQualifiedControl() { std::string value; @@ -210,8 +224,12 @@ public: return Token(TOK_DIV); case '%': return Token(TOK_MOD); + case '=': + return Token(TOK_ASSIGN); case '\'': return GetLiteral(); + case '$': + return GetVariable(); case '`': return GetFullyQualifiedControl(); default: @@ -249,15 +267,14 @@ public: class ControlExpression : public Expression { public: - ControlQualifier qualifier; - Device::Control* control = nullptr; // Keep a shared_ptr to the device so the control pointer doesn't become invalid + // TODO: This is causing devices to be destructed after backends are shutdown: std::shared_ptr m_device; explicit ControlExpression(ControlQualifier qualifier_) : qualifier(qualifier_) {} ControlState GetValue() const override { - if (!control) + if (!input) return 0.0; // Note: Inputs may return negative values in situations where opposing directions are @@ -266,20 +283,26 @@ public: // FYI: Clamping values greater than 1.0 is purposely not done to support unbounded values in // the future. (e.g. raw accelerometer/gyro data) - return std::max(0.0, control->ToInput()->GetState()); + return std::max(0.0, input->GetState()); } void SetValue(ControlState value) override { - if (control) - control->ToOutput()->SetState(value); + if (output) + output->SetState(value); } - int CountNumControls() const override { return control ? 1 : 0; } - void UpdateReferences(ControlFinder& finder) override + int CountNumControls() const override { return (input || output) ? 1 : 0; } + void UpdateReferences(ControlEnvironment& env) override { - m_device = finder.FindDevice(qualifier); - control = finder.FindControl(qualifier); + m_device = env.FindDevice(qualifier); + input = env.FindInput(qualifier); + output = env.FindOutput(qualifier); } operator std::string() const override { return "`" + static_cast(qualifier) + "`"; } + +private: + ControlQualifier qualifier; + Device::Input* input = nullptr; + Device::Output* output = nullptr; }; class BinaryExpression : public Expression @@ -319,6 +342,12 @@ public: const ControlState result = std::fmod(lhsValue, rhsValue); return std::isnan(result) ? 0.0 : result; } + case TOK_ASSIGN: + { + lhs->SetValue(rhsValue); + // TODO: Should this instead GetValue(lhs) ? + return rhsValue; + } default: assert(false); return 0; @@ -338,10 +367,10 @@ public: return lhs->CountNumControls() + rhs->CountNumControls(); } - void UpdateReferences(ControlFinder& finder) override + void UpdateReferences(ControlEnvironment& env) override { - lhs->UpdateReferences(finder); - rhs->UpdateReferences(finder); + lhs->UpdateReferences(env); + rhs->UpdateReferences(env); } operator std::string() const override @@ -356,7 +385,7 @@ public: UnaryExpression(std::unique_ptr&& inner_) : inner(std::move(inner_)) {} int CountNumControls() const override { return inner->CountNumControls(); } - void UpdateReferences(ControlFinder& finder) override { inner->UpdateReferences(finder); } + void UpdateReferences(ControlEnvironment& env) override { inner->UpdateReferences(env); } operator std::string() const override { @@ -462,7 +491,7 @@ public: int CountNumControls() const override { return 1; } - void UpdateReferences(ControlFinder&) override + void UpdateReferences(ControlEnvironment&) override { // Nothing needed. } @@ -523,6 +552,29 @@ std::unique_ptr MakeLiteralExpression(std::string name) } } +class VariableExpression : public Expression +{ +public: + VariableExpression(std::string name) : m_name(name) {} + + ControlState GetValue() const override { return *m_value_ptr; } + + void SetValue(ControlState value) override { *m_value_ptr = value; } + + int CountNumControls() const override { return 1; } + + void UpdateReferences(ControlEnvironment& env) override + { + m_value_ptr = env.GetVariablePtr(m_name); + } + + operator std::string() const override { return '$' + m_name; } + +protected: + const std::string m_name; + ControlState* m_value_ptr{}; +}; + // This class proxies all methods to its either left-hand child if it has bound controls, or its // right-hand child. Its intended use is for supporting old-style barewords expressions. class CoalesceExpression : public Expression @@ -543,10 +595,10 @@ public: static_cast(*m_rhs) + ')'; } - void UpdateReferences(ControlFinder& finder) override + void UpdateReferences(ControlEnvironment& env) override { - m_lhs->UpdateReferences(finder); - m_rhs->UpdateReferences(finder); + m_lhs->UpdateReferences(env); + m_rhs->UpdateReferences(env); } private: @@ -559,7 +611,7 @@ private: std::unique_ptr m_rhs; }; -std::shared_ptr ControlFinder::FindDevice(ControlQualifier qualifier) const +std::shared_ptr ControlEnvironment::FindDevice(ControlQualifier qualifier) const { if (qualifier.has_device) return container.FindDevice(qualifier.device_qualifier); @@ -567,16 +619,27 @@ std::shared_ptr ControlFinder::FindDevice(ControlQualifier qualifier) co return container.FindDevice(default_device); } -Device::Control* ControlFinder::FindControl(ControlQualifier qualifier) const +Device::Input* ControlEnvironment::FindInput(ControlQualifier qualifier) const { const std::shared_ptr device = FindDevice(qualifier); if (!device) return nullptr; - if (is_input) - return device->FindInput(qualifier.control_name); - else - return device->FindOutput(qualifier.control_name); + return device->FindInput(qualifier.control_name); +} + +Device::Output* ControlEnvironment::FindOutput(ControlQualifier qualifier) const +{ + const std::shared_ptr device = FindDevice(qualifier); + if (!device) + return nullptr; + + return device->FindOutput(qualifier.control_name); +} + +ControlState* ControlEnvironment::GetVariablePtr(const std::string& name) +{ + return &m_variables[name]; } struct ParseResult @@ -623,6 +686,10 @@ private: { return {ParseStatus::Successful, MakeLiteralExpression(tok.data)}; } + case TOK_VARIABLE: + { + return {ParseStatus::Successful, std::make_unique(tok.data)}; + } case TOK_LPAREN: return Paren(); default: @@ -665,6 +732,7 @@ private: case TOK_MUL: case TOK_DIV: case TOK_MOD: + case TOK_ASSIGN: return true; default: return false; diff --git a/Source/Core/InputCommon/ControlReference/ExpressionParser.h b/Source/Core/InputCommon/ControlReference/ExpressionParser.h index 0a42d90c62..d9f7a5eb2b 100644 --- a/Source/Core/InputCommon/ControlReference/ExpressionParser.h +++ b/Source/Core/InputCommon/ControlReference/ExpressionParser.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -46,21 +47,26 @@ public: } }; -class ControlFinder +class ControlEnvironment { public: - ControlFinder(const Core::DeviceContainer& container_, const Core::DeviceQualifier& default_, - const bool is_input_) - : container(container_), default_device(default_), is_input(is_input_) + using VariableContainer = std::map; + + ControlEnvironment(const Core::DeviceContainer& container_, const Core::DeviceQualifier& default_, + VariableContainer& vars) + : m_variables(vars), container(container_), default_device(default_) { } + std::shared_ptr FindDevice(ControlQualifier qualifier) const; - Core::Device::Control* FindControl(ControlQualifier qualifier) const; + Core::Device::Input* FindInput(ControlQualifier qualifier) const; + Core::Device::Output* FindOutput(ControlQualifier qualifier) const; + ControlState* GetVariablePtr(const std::string& name); private: + VariableContainer& m_variables; const Core::DeviceContainer& container; const Core::DeviceQualifier& default_device; - bool is_input; }; class Expression @@ -70,7 +76,7 @@ public: virtual ControlState GetValue() const = 0; virtual void SetValue(ControlState state) = 0; virtual int CountNumControls() const = 0; - virtual void UpdateReferences(ControlFinder& finder) = 0; + virtual void UpdateReferences(ControlEnvironment& finder) = 0; virtual operator std::string() const = 0; }; diff --git a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp index de0579a2b2..c5ff0705a2 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp @@ -38,23 +38,38 @@ std::unique_lock EmulatedController::GetStateLock() void EmulatedController::UpdateReferences(const ControllerInterface& devi) { - const auto lock = GetStateLock(); m_default_device_is_connected = devi.HasConnectedDevice(m_default_device); + ciface::ExpressionParser::ControlEnvironment env(devi, GetDefaultDevice(), m_expression_vars); + + UpdateReferences(env); +} + +void EmulatedController::UpdateReferences(ciface::ExpressionParser::ControlEnvironment& env) +{ + const auto lock = GetStateLock(); + for (auto& ctrlGroup : groups) { for (auto& control : ctrlGroup->controls) - control->control_ref.get()->UpdateReference(devi, GetDefaultDevice()); + control->control_ref->UpdateReference(env); // Attachments: if (ctrlGroup->type == GroupType::Attachments) { for (auto& attachment : static_cast(ctrlGroup.get())->GetAttachmentList()) - attachment->UpdateReferences(devi); + attachment->UpdateReferences(env); } } } +void EmulatedController::UpdateSingleControlReference(const ControllerInterface& devi, + ControlReference* ref) +{ + ciface::ExpressionParser::ControlEnvironment env(devi, GetDefaultDevice(), m_expression_vars); + ref->UpdateReference(env); +} + bool EmulatedController::IsDefaultDeviceConnected() const { return m_default_device_is_connected; diff --git a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h index bf2ff666fa..2611880ee9 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h +++ b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h @@ -13,6 +13,7 @@ #include "Common/Common.h" #include "Common/IniFile.h" +#include "InputCommon/ControlReference/ExpressionParser.h" #include "InputCommon/ControllerInterface/Device.h" class ControllerInterface; @@ -20,6 +21,8 @@ class ControllerInterface; const char* const named_directions[] = {_trans("Up"), _trans("Down"), _trans("Left"), _trans("Right")}; +class ControlReference; + namespace ControllerEmu { class ControlGroup; @@ -43,6 +46,7 @@ public: void SetDefaultDevice(ciface::Core::DeviceQualifier devq); void UpdateReferences(const ControllerInterface& devi); + void UpdateSingleControlReference(const ControllerInterface& devi, ControlReference* ref); // This returns a lock that should be held before calling State() on any control // references and GetState(), by extension. This prevents a race condition @@ -75,6 +79,12 @@ public: return T(std::lround((zero_value - neg_1_value) * input_value + zero_value)); } +protected: + // TODO: Wiimote attachment has its own member that isn't being used.. + ciface::ExpressionParser::ControlEnvironment::VariableContainer m_expression_vars; + + void UpdateReferences(ciface::ExpressionParser::ControlEnvironment& env); + private: ciface::Core::DeviceQualifier m_default_device; bool m_default_device_is_connected{false};