From cbefb357a57ce5f6dd161a400a5867609022ee66 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Tue, 28 Apr 2026 15:42:09 +0100 Subject: [PATCH] desktop/windowRule: use variants for storage internally Improves storage: less dynamic parsing, immediate config errors, faster. --- src/config/legacy/ConfigManager.cpp | 11 +- .../lua/bindings/LuaBindingsConfigRules.cpp | 22 +- .../lua/bindings/LuaBindingsInternal.cpp | 25 +- .../supplementary/executor/Executor.cpp | 8 +- src/desktop/rule/windowRule/WindowRule.cpp | 304 +++++++++++++++++- src/desktop/rule/windowRule/WindowRule.hpp | 50 ++- .../rule/windowRule/WindowRuleApplicator.cpp | 280 +++++----------- src/helpers/AsyncDialogBox.cpp | 11 +- 8 files changed, 482 insertions(+), 229 deletions(-) diff --git a/src/config/legacy/ConfigManager.cpp b/src/config/legacy/ConfigManager.cpp index a19993f00..5d74528d7 100644 --- a/src/config/legacy/ConfigManager.cpp +++ b/src/config/legacy/ConfigManager.cpp @@ -895,8 +895,11 @@ std::optional CConfigManager::addRuleFromConfigKey(const std::strin for (const auto& e : Desktop::Rule::windowEffects()->allEffectStrings()) { auto VAL = m_config->getSpecialConfigValuePtr("windowrule", e.c_str(), name.c_str()); - if (VAL && VAL->m_bSetByUser) - rule->addEffect(Desktop::Rule::windowEffects()->get(e).value_or(Desktop::Rule::WINDOW_RULE_EFFECT_NONE), std::any_cast(VAL->getValue())); + if (VAL && VAL->m_bSetByUser) { + auto res = rule->addEffect(Desktop::Rule::windowEffects()->get(e).value_or(Desktop::Rule::WINDOW_RULE_EFFECT_NONE), std::any_cast(VAL->getValue())); + if (!res) + return res.error(); + } } Desktop::Rule::ruleEngine()->registerRule(std::move(rule)); @@ -1981,7 +1984,9 @@ std::optional CConfigManager::handleWindowrule(const std::string& c const auto EFFECT = Desktop::Rule::windowEffects()->get(FIRST); if (!EFFECT.has_value()) return std::format("invalid effect {}", el); - rule->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)}); + auto res = rule->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)}); + if (!res) + return res.error(); } else return std::format("invalid field type {}", FIRST); } diff --git a/src/config/lua/bindings/LuaBindingsConfigRules.cpp b/src/config/lua/bindings/LuaBindingsConfigRules.cpp index 2faab8c67..4f9fc8618 100644 --- a/src/config/lua/bindings/LuaBindingsConfigRules.cpp +++ b/src/config/lua/bindings/LuaBindingsConfigRules.cpp @@ -1066,8 +1066,11 @@ static int hlWindowRule(lua_State* L) { auto val = Internal::ruleValueToString(L); if (!val) self->addError(std::format("{}: hl.window_rule: field '{}': {}", sourceInfo, key, val.error())); - else - rule->addEffect(*dynamicEffect, *val); + else { + auto res = rule->addEffect(*dynamicEffect, *val); + if (!res) + self->addError(std::format("{}: hl.window_rule: field '{}': {}", sourceInfo, key, res.error())); + } lua_pop(L, 1); continue; @@ -1077,12 +1080,17 @@ static int hlWindowRule(lua_State* L) { auto err = val->parse(L); if (err.errorCode != PARSE_ERROR_OK) { const bool allowLegacyString = (key == "max_size" || key == "min_size" || key == "border_color") && lua_isstring(L, -1); - if (allowLegacyString) - rule->addEffect(desc->effect, lua_tostring(L, -1)); - else + if (allowLegacyString) { + auto res = rule->addEffect(desc->effect, lua_tostring(L, -1)); + if (!res) + self->addError(std::format("{}: hl.window_rule: field '{}': {}", sourceInfo, key, res.error())); + } else self->addError(std::format("{}: hl.window_rule: field '{}': {}", sourceInfo, key, err.message)); - } else - rule->addEffect(desc->effect, val->toString()); + } else { + auto res = rule->addEffect(desc->effect, val->toString()); + if (!res) + self->addError(std::format("{}: hl.window_rule: field '{}': {}", sourceInfo, key, res.error())); + } lua_pop(L, 1); } diff --git a/src/config/lua/bindings/LuaBindingsInternal.cpp b/src/config/lua/bindings/LuaBindingsInternal.cpp index 02b7c060a..e5a65bf73 100644 --- a/src/config/lua/bindings/LuaBindingsInternal.cpp +++ b/src/config/lua/bindings/LuaBindingsInternal.cpp @@ -527,7 +527,11 @@ std::expected, int> Internal::buildRuleFromTable( return std::unexpected(Internal::configError(L, "buildRuleFromTable: effect '{}': {}", key, val.error())); } - rule->addEffect(*dynamicEffect, *val); + auto res = rule->addEffect(*dynamicEffect, *val); + if (!res) { + lua_pop(L, 1); + return std::unexpected(Internal::configError(L, "buildRuleFromTable: effect '{}': {}", key, res.error())); + } hasRuleEffects = true; lua_pop(L, 1); @@ -538,14 +542,23 @@ std::expected, int> Internal::buildRuleFromTable( auto err = val->parse(L); if (err.errorCode != PARSE_ERROR_OK) { const bool allowLegacyString = (key == "max_size" || key == "min_size" || key == "border_color") && lua_isstring(L, -1); - if (allowLegacyString) - rule->addEffect(desc->effect, lua_tostring(L, -1)); - else { + if (allowLegacyString) { + auto res = rule->addEffect(desc->effect, lua_tostring(L, -1)); + if (!res) { + lua_pop(L, 1); + return std::unexpected(Internal::configError(L, "buildRuleFromTable: effect '{}': {}", key, res.error())); + } + } else { lua_pop(L, 1); return std::unexpected(Internal::configError(L, "buildRuleFromTable: effect '{}': {}", key, err.message)); } - } else - rule->addEffect(desc->effect, val->toString()); + } else { + auto res = rule->addEffect(desc->effect, val->toString()); + if (!res) { + lua_pop(L, 1); + return std::unexpected(Internal::configError(L, "buildRuleFromTable: effect '{}': {}", key, res.error())); + } + } hasRuleEffects = true; lua_pop(L, 1); diff --git a/src/config/supplementary/executor/Executor.cpp b/src/config/supplementary/executor/Executor.cpp index 70b47a690..b5d3a0215 100644 --- a/src/config/supplementary/executor/Executor.cpp +++ b/src/config/supplementary/executor/Executor.cpp @@ -123,7 +123,11 @@ std::optional CExecutor::spawnWithRules(std::string args, PHLWORKSPACE std::string execToken = ""; if (!RULES.empty()) { - auto rule = Desktop::Rule::CWindowRule::buildFromExecString(std::move(RULES)); + auto rule = Desktop::Rule::CWindowRule::buildFromExecString(std::move(RULES)); + if (!rule) { + Log::logger->log(Log::ERR, "Failed to parse exec rule: {}", rule.error()); + return std::nullopt; + } const auto TOKEN = g_pTokenManager->registerNewToken(nullptr, std::chrono::seconds(1)); @@ -132,7 +136,7 @@ std::optional CExecutor::spawnWithRules(std::string args, PHLWORKSPACE if (!PROC) return std::nullopt; - applyRuleToProc(rule, *PROC, TOKEN); + applyRuleToProc(*rule, *PROC, TOKEN); return PROC; } diff --git a/src/desktop/rule/windowRule/WindowRule.cpp b/src/desktop/rule/windowRule/WindowRule.cpp index fbabb42d2..f2baaa854 100644 --- a/src/desktop/rule/windowRule/WindowRule.cpp +++ b/src/desktop/rule/windowRule/WindowRule.cpp @@ -1,16 +1,298 @@ #include "WindowRule.hpp" #include "../../view/Window.hpp" #include "../../../helpers/Monitor.hpp" +#include "../../../helpers/MiscFunctions.hpp" #include "../../../Compositor.hpp" #include "../../../managers/TokenManager.hpp" #include "../../../desktop/state/FocusState.hpp" +#include "../../../protocols/types/ContentType.hpp" +#include +#include #include +#include +#include using namespace Desktop; using namespace Desktop::Rule; using namespace Hyprutils::String; +static std::expected parseInt(std::string_view effectName, const std::string& raw) { + try { + return std::stoll(raw); + } catch (std::exception& e) { return std::unexpected(std::format("{} rule \"{}\" failed with: {}", effectName, raw, e.what())); } +} + +static std::expected parseFloat(std::string_view effectName, const std::string& raw) { + try { + return std::stof(raw); + } catch (std::exception& e) { return std::unexpected(std::format("{} rule \"{}\" failed with: {}", effectName, raw, e.what())); } +} + +static std::expected parseBorderColorToken(const std::string& raw, const std::string& token) { + auto parsed = configStringToInt(token); + if (!parsed) + return std::unexpected(std::format(R"(border_color rule "{}" has invalid color "{}": {})", raw, token, parsed.error())); + + return CHyprColor(*parsed); +} + +static std::expected parseFullscreenState(const std::string& raw) { + CVarList2 vars(std::string{raw}, 0, 's'); + + try { + SFullscreenStateRule result; + result.internal = std::stoi(std::string{vars[0]}); + if (!vars[1].empty()) + result.client = std::stoi(std::string{vars[1]}); + return result; + } catch (std::exception& e) { return std::unexpected(std::format("fullscreen_state rule \"{}\" failed with: {}", raw, e.what())); } +} + +static std::expected parseIdleInhibitMode(const std::string& raw) { + if (raw == "none") + return IDLEINHIBIT_NONE; + if (raw == "always") + return IDLEINHIBIT_ALWAYS; + if (raw == "focus") + return IDLEINHIBIT_FOCUS; + if (raw == "fullscreen") + return IDLEINHIBIT_FULLSCREEN; + + return std::unexpected(std::format("idle_inhibit rule has unknown mode \"{}\"", raw)); +} + +static std::expected parseOpacityRule(const std::string& raw) { + try { + CVarList2 vars(std::string{raw}, 0, ' '); + + int opacityIDX = 0; + SOpacityRule result; + + for (const auto& r : vars) { + if (r == "opacity") + continue; + + if (r == "override") { + if (opacityIDX == 1) + result.alpha.overridden = true; + else if (opacityIDX == 2) + result.alphaInactive.overridden = true; + else if (opacityIDX == 3) + result.alphaFullscreen.overridden = true; + } else { + if (opacityIDX == 0) + result.alpha.alpha = std::stof(std::string{r}); + else if (opacityIDX == 1) + result.alphaInactive.alpha = std::stof(std::string{r}); + else if (opacityIDX == 2) + result.alphaFullscreen.alpha = std::stof(std::string{r}); + else + throw std::runtime_error("more than 3 alpha values"); + + opacityIDX++; + } + } + + if (opacityIDX == 1) { + result.alphaInactive = result.alpha; + result.alphaFullscreen = result.alpha; + } + + return result; + } catch (std::exception& e) { return std::unexpected(std::format("opacity rule \"{}\" failed with: {}", raw, e.what())); } +} + +static std::expected parseBorderColorRule(const std::string& raw) { + try { + Config::CGradientValueData activeBorderGradient = {}; + Config::CGradientValueData inactiveBorderGradient = {}; + bool active = true; + CVarList colorsAndAngles = CVarList(trim(raw), 0, 's', true); + + if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) { + auto activeColor = parseBorderColorToken(raw, colorsAndAngles[0]); + if (!activeColor) + return std::unexpected(activeColor.error()); + + auto inactiveColor = parseBorderColorToken(raw, colorsAndAngles[1]); + if (!inactiveColor) + return std::unexpected(inactiveColor.error()); + + return SBorderColorRule{ + .active = Config::CGradientValueData(*activeColor), + .inactive = Config::CGradientValueData(*inactiveColor), + }; + } + + for (auto const& token : colorsAndAngles) { + if (active && token.contains("deg")) { + activeBorderGradient.m_angle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); + active = false; + } else if (token.contains("deg")) + inactiveBorderGradient.m_angle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); + else { + auto color = parseBorderColorToken(raw, token); + if (!color) + return std::unexpected(color.error()); + + if (active) + activeBorderGradient.m_colors.emplace_back(*color); + else + inactiveBorderGradient.m_colors.emplace_back(*color); + } + } + + activeBorderGradient.updateColorsOk(); + + if (activeBorderGradient.m_colors.size() > 10 || inactiveBorderGradient.m_colors.size() > 10) + return std::unexpected(std::format("border_color rule \"{}\" has more than 10 colors in one gradient", raw)); + if (activeBorderGradient.m_colors.empty()) + return std::unexpected(std::format("border_color rule \"{}\" has no colors", raw)); + + SBorderColorRule result{.active = activeBorderGradient}; + if (!inactiveBorderGradient.m_colors.empty()) + result.inactive = inactiveBorderGradient; + + return result; + } catch (std::exception& e) { return std::unexpected(std::format("border_color rule \"{}\" failed with: {}", raw, e.what())); } +} + +static std::vector parseStringList(const std::string& raw) { + std::vector result; + CVarList2 varlist(std::string{raw}, 0, 's'); + + for (const auto& e : varlist) { + result.emplace_back(e); + } + + return result; +} + +static std::expected parseWindowRuleEffect(CWindowRuleEffectContainer::storageType e, const std::string& raw) { + if (windowEffects()->isEffectDynamic(e)) + return std::string{raw}; + + const auto EFFECT_NAME = windowEffects()->get(e); + + switch (e) { + default: return std::unexpected(std::format("unknown window rule effect {}", e)); + + case WINDOW_RULE_EFFECT_NONE: return std::monostate{}; + + case WINDOW_RULE_EFFECT_FLOAT: + case WINDOW_RULE_EFFECT_TILE: + case WINDOW_RULE_EFFECT_FULLSCREEN: + case WINDOW_RULE_EFFECT_MAXIMIZE: + case WINDOW_RULE_EFFECT_CENTER: + case WINDOW_RULE_EFFECT_PSEUDO: + case WINDOW_RULE_EFFECT_NOINITIALFOCUS: + case WINDOW_RULE_EFFECT_PIN: + case WINDOW_RULE_EFFECT_PERSISTENT_SIZE: + case WINDOW_RULE_EFFECT_ALLOWS_INPUT: + case WINDOW_RULE_EFFECT_DIM_AROUND: + case WINDOW_RULE_EFFECT_DECORATE: + case WINDOW_RULE_EFFECT_FOCUS_ON_ACTIVATE: + case WINDOW_RULE_EFFECT_KEEP_ASPECT_RATIO: + case WINDOW_RULE_EFFECT_NEAREST_NEIGHBOR: + case WINDOW_RULE_EFFECT_NO_ANIM: + case WINDOW_RULE_EFFECT_NO_BLUR: + case WINDOW_RULE_EFFECT_NO_DIM: + case WINDOW_RULE_EFFECT_NO_FOCUS: + case WINDOW_RULE_EFFECT_NO_FOLLOW_MOUSE: + case WINDOW_RULE_EFFECT_NO_MAX_SIZE: + case WINDOW_RULE_EFFECT_NO_SHADOW: + case WINDOW_RULE_EFFECT_NO_SHORTCUTS_INHIBIT: + case WINDOW_RULE_EFFECT_OPAQUE: + case WINDOW_RULE_EFFECT_FORCE_RGBX: + case WINDOW_RULE_EFFECT_SYNC_FULLSCREEN: + case WINDOW_RULE_EFFECT_IMMEDIATE: + case WINDOW_RULE_EFFECT_XRAY: + case WINDOW_RULE_EFFECT_RENDER_UNFOCUSED: + case WINDOW_RULE_EFFECT_NO_SCREEN_SHARE: + case WINDOW_RULE_EFFECT_NO_VRR: + case WINDOW_RULE_EFFECT_STAY_FOCUSED: return truthy(raw); + + case WINDOW_RULE_EFFECT_FULLSCREENSTATE: { + auto parsed = parseFullscreenState(raw); + if (!parsed) + return std::unexpected(parsed.error()); + return *parsed; + } + + case WINDOW_RULE_EFFECT_MOVE: + case WINDOW_RULE_EFFECT_SIZE: + case WINDOW_RULE_EFFECT_MONITOR: + case WINDOW_RULE_EFFECT_WORKSPACE: + case WINDOW_RULE_EFFECT_GROUP: + case WINDOW_RULE_EFFECT_ANIMATION: + case WINDOW_RULE_EFFECT_TAG: + case WINDOW_RULE_EFFECT_MAX_SIZE: + case WINDOW_RULE_EFFECT_MIN_SIZE: return std::string{raw}; + + case WINDOW_RULE_EFFECT_SUPPRESSEVENT: return parseStringList(raw); + + case WINDOW_RULE_EFFECT_CONTENT: return sc(NContentType::fromString(raw)); + + case WINDOW_RULE_EFFECT_NOCLOSEFOR: + case WINDOW_RULE_EFFECT_BORDER_SIZE: { + auto parsed = parseInt(EFFECT_NAME, raw); + if (!parsed) + return std::unexpected(parsed.error()); + return *parsed; + } + + case WINDOW_RULE_EFFECT_ROUNDING: { + auto parsed = parseInt(EFFECT_NAME, raw); + if (!parsed) + return std::unexpected(parsed.error()); + if (*parsed < 0) + return std::unexpected(std::format("{} rule \"{}\" must be non-negative", EFFECT_NAME, raw)); + return *parsed; + } + + case WINDOW_RULE_EFFECT_SCROLLING_WIDTH: { + auto parsed = parseFloat(EFFECT_NAME, raw); + if (!parsed) + return std::unexpected(parsed.error()); + return *parsed; + } + + case WINDOW_RULE_EFFECT_ROUNDING_POWER: { + auto parsed = parseFloat(EFFECT_NAME, raw); + if (!parsed) + return std::unexpected(parsed.error()); + return std::clamp(*parsed, 1.F, 10.F); + } + case WINDOW_RULE_EFFECT_SCROLL_MOUSE: + case WINDOW_RULE_EFFECT_SCROLL_TOUCHPAD: { + auto parsed = parseFloat(EFFECT_NAME, raw); + if (!parsed) + return std::unexpected(parsed.error()); + return std::clamp(*parsed, 0.01F, 10.F); + } + + case WINDOW_RULE_EFFECT_BORDER_COLOR: { + auto parsed = parseBorderColorRule(raw); + if (!parsed) + return std::unexpected(parsed.error()); + return *parsed; + } + case WINDOW_RULE_EFFECT_IDLE_INHIBIT: { + auto parsed = parseIdleInhibitMode(raw); + if (!parsed) + return std::unexpected(parsed.error()); + return *parsed; + } + case WINDOW_RULE_EFFECT_OPACITY: { + auto parsed = parseOpacityRule(raw); + if (!parsed) + return std::unexpected(parsed.error()); + return *parsed; + } + } +} + CWindowRule::CWindowRule(const std::string& name) : IRule(name) { ; } @@ -19,12 +301,18 @@ eRuleType CWindowRule::type() { return RULE_TYPE_WINDOW; } -void CWindowRule::addEffect(CWindowRule::storageType e, const std::string& result) { - m_effects.emplace_back(std::make_pair<>(e, result)); +std::expected CWindowRule::addEffect(CWindowRule::storageType e, const std::string& result) { + auto parsed = parseWindowRuleEffect(e, result); + if (!parsed) + return std::unexpected(parsed.error()); + + m_effects.emplace_back(SWindowRuleEffect{.key = e, .raw = result, .value = std::move(*parsed)}); m_effectSet.emplace(e); + + return {}; } -const std::vector>& CWindowRule::effects() { +const std::vector& CWindowRule::effects() { return m_effects; } @@ -131,7 +419,7 @@ bool CWindowRule::matches(PHLWINDOW w, bool allowEnvLookup) { return true; } -SP CWindowRule::buildFromExecString(std::string&& s) { +std::expected, std::string> CWindowRule::buildFromExecString(std::string&& s) { CVarList2 varlist(std::move(s), 0, ';'); SP wr = makeShared("__exec_rule"); @@ -146,7 +434,9 @@ SP CWindowRule::buildFromExecString(std::string&& s) { if (!EFFECT.has_value() || *EFFECT == WINDOW_RULE_EFFECT_NONE) continue; // invalid... - wr->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)}); + auto res = wr->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)}); + if (!res) + return std::unexpected(res.error()); continue; } @@ -157,7 +447,9 @@ SP CWindowRule::buildFromExecString(std::string&& s) { if (!EFFECT.has_value() || *EFFECT == WINDOW_RULE_EFFECT_NONE) continue; // invalid... - wr->addEffect(*EFFECT, std::string{"1"}); + auto res = wr->addEffect(*EFFECT, std::string{"1"}); + if (!res) + return std::unexpected(res.error()); } return wr; diff --git a/src/desktop/rule/windowRule/WindowRule.hpp b/src/desktop/rule/windowRule/WindowRule.hpp index 875c9ece7..19560c153 100644 --- a/src/desktop/rule/windowRule/WindowRule.hpp +++ b/src/desktop/rule/windowRule/WindowRule.hpp @@ -2,14 +2,42 @@ #include "../Rule.hpp" #include "../../DesktopTypes.hpp" +#include "../../types/OverridableVar.hpp" #include "WindowRuleEffectContainer.hpp" +#include "../../../config/shared/complex/ComplexDataTypes.hpp" #include "../../../helpers/math/Math.hpp" +#include #include +#include namespace Desktop::Rule { constexpr const char* EXEC_RULE_ENV_NAME = "HL_EXEC_RULE_TOKEN"; + struct SFullscreenStateRule { + int internal = 0; + std::optional client; + }; + + struct SOpacityRule { + Types::SAlphaValue alpha; + Types::SAlphaValue alphaInactive; + Types::SAlphaValue alphaFullscreen; + }; + + struct SBorderColorRule { + Config::CGradientValueData active; + std::optional inactive; + }; + + using WindowRuleEffectValue = std::variant, SFullscreenStateRule, SOpacityRule, SBorderColorRule>; + + struct SWindowRuleEffect { + CWindowRuleEffectContainer::storageType key = WINDOW_RULE_EFFECT_NONE; + std::string raw; + WindowRuleEffectValue value; + }; + class CWindowRule : public IRule { private: using storageType = CWindowRuleEffectContainer::storageType; @@ -22,22 +50,22 @@ namespace Desktop::Rule { CWindowRule(CWindowRule&) = default; CWindowRule(CWindowRule&&) = default; - static SP buildFromExecString(std::string&&); + static std::expected, std::string> buildFromExecString(std::string&&); - virtual eRuleType type(); + virtual eRuleType type(); - void addEffect(storageType e, const std::string& result); - const std::vector>& effects(); - const std::unordered_set& effectsSet(); + std::expected addEffect(storageType e, const std::string& result); + const std::vector& effects(); + const std::unordered_set& effectsSet(); - void setEnabled(bool enable); - bool isEnabled() const; + void setEnabled(bool enable); + bool isEnabled() const; - bool matches(PHLWINDOW w, bool allowEnvLookup = false); + bool matches(PHLWINDOW w, bool allowEnvLookup = false); private: - std::vector> m_effects; - std::unordered_set m_effectSet; - bool m_enabled = true; + std::vector m_effects; + std::unordered_set m_effectSet; + bool m_enabled = true; }; }; diff --git a/src/desktop/rule/windowRule/WindowRuleApplicator.cpp b/src/desktop/rule/windowRule/WindowRuleApplicator.cpp index ad6f9e626..b51049e6c 100644 --- a/src/desktop/rule/windowRule/WindowRuleApplicator.cpp +++ b/src/desktop/rule/windowRule/WindowRuleApplicator.cpp @@ -6,33 +6,26 @@ #include "../../types/OverridableVar.hpp" #include "../../../event/EventBus.hpp" -#include -#include -#include #include -using namespace Hyprutils::String; - using namespace Desktop; using namespace Desktop::Rule; -namespace { - template - void resetRuleProp(std::pair, std::underlying_type_t>& prop, - std::underlying_type_t props, Desktop::Types::eOverridePriority prio, - std::unordered_set& effectsNuked, TEffect&& effect) { - auto& [value, propMask] = prop; +template +static void resetRuleProp(std::pair, std::underlying_type_t>& prop, + std::underlying_type_t props, Desktop::Types::eOverridePriority prio, + std::unordered_set& effectsNuked, TEffect&& effect) { + auto& [value, propMask] = prop; - if (!(propMask & props)) - return; + if (!(propMask & props)) + return; - if (prio == Desktop::Types::PRIORITY_WINDOW_RULE) { - effectsNuked.emplace(effect()); - propMask &= ~props; - } - - value.unset(prio); + if (prio == Desktop::Types::PRIORITY_WINDOW_RULE) { + effectsNuked.emplace(effect()); + propMask &= ~props; } + + value.unset(prio); } CWindowRuleApplicator::CWindowRuleApplicator(PHLWINDOW w) : m_window(w) { @@ -85,7 +78,11 @@ std::unordered_set CWindowRuleApplicato CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyDynamicRule(const SP& rule) { SRuleResult result; - for (const auto& [key, effect] : rule->effects()) { + for (const auto& effectData : rule->effects()) { + const auto key = effectData.key; + const auto& effect = effectData.raw; + const auto& value = effectData.value; + switch (key) { default: { if (key <= WINDOW_RULE_EFFECT_LAST_STATIC) { @@ -115,133 +112,44 @@ CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyDynamicRule(const break; } case WINDOW_RULE_EFFECT_ROUNDING: { - try { - m_rounding.first.set(std::stoull(effect), Types::PRIORITY_WINDOW_RULE); - m_rounding.second |= rule->getPropertiesMask(); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyDynamicRule: invalid rounding {}", effect); } + m_rounding.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); + m_rounding.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_ROUNDING_POWER: { - try { - m_roundingPower.first.set(std::clamp(std::stof(effect), 1.F, 10.F), Types::PRIORITY_WINDOW_RULE); - m_roundingPower.second |= rule->getPropertiesMask(); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyDynamicRule: invalid rounding_power {}", effect); } + m_roundingPower.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); + m_roundingPower.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_PERSISTENT_SIZE: { - m_persistentSize.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_persistentSize.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_persistentSize.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_ANIMATION: { - m_animationStyle.first.set(effect, Types::PRIORITY_WINDOW_RULE); + m_animationStyle.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_animationStyle.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_BORDER_COLOR: { - try { - // Each vector will only get used if it has at least one color - Config::CGradientValueData activeBorderGradient = {}; - Config::CGradientValueData inactiveBorderGradient = {}; - bool active = true; - CVarList colorsAndAngles = CVarList(trim(effect), 0, 's', true); - - // Basic form has only two colors, everything else can be parsed as a gradient - if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) { - m_activeBorderColor.first = - Types::COverridableVar(Config::CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[0]).value_or(0))), Types::PRIORITY_WINDOW_RULE); - m_inactiveBorderColor.first = - Types::COverridableVar(Config::CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[1]).value_or(0))), Types::PRIORITY_WINDOW_RULE); - m_activeBorderColor.second |= rule->getPropertiesMask(); - m_inactiveBorderColor.second |= rule->getPropertiesMask(); - break; - } - - for (auto const& token : colorsAndAngles) { - // The first angle, or an explicit "0deg", splits the two gradients - if (active && token.contains("deg")) { - activeBorderGradient.m_angle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); - active = false; - } else if (token.contains("deg")) - inactiveBorderGradient.m_angle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); - else if (active) - activeBorderGradient.m_colors.emplace_back(configStringToInt(token).value_or(0)); - else - inactiveBorderGradient.m_colors.emplace_back(configStringToInt(token).value_or(0)); - } - - activeBorderGradient.updateColorsOk(); - - // Includes sanity checks for the number of colors in each gradient - if (activeBorderGradient.m_colors.size() > 10 || inactiveBorderGradient.m_colors.size() > 10) - Log::logger->log(Log::WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", effect); - else if (activeBorderGradient.m_colors.empty()) - Log::logger->log(Log::WARN, "Bordercolor rule \"{}\" has no colors, ignoring", effect); - else if (inactiveBorderGradient.m_colors.empty()) - m_activeBorderColor.first = Types::COverridableVar(activeBorderGradient, Types::PRIORITY_WINDOW_RULE); - else { - m_activeBorderColor.first = Types::COverridableVar(activeBorderGradient, Types::PRIORITY_WINDOW_RULE); - m_inactiveBorderColor.first = Types::COverridableVar(inactiveBorderGradient, Types::PRIORITY_WINDOW_RULE); - } - } catch (std::exception& e) { Log::logger->log(Log::ERR, "BorderColor rule \"{}\" failed with: {}", effect, e.what()); } + const auto& borderColor = std::get(value); + m_activeBorderColor.first = Types::COverridableVar(borderColor.active, Types::PRIORITY_WINDOW_RULE); + if (borderColor.inactive) + m_inactiveBorderColor.first = Types::COverridableVar(*borderColor.inactive, Types::PRIORITY_WINDOW_RULE); m_activeBorderColor.second = rule->getPropertiesMask(); m_inactiveBorderColor.second = rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_IDLE_INHIBIT: { - if (effect == "none") - m_idleInhibitMode.first.set(IDLEINHIBIT_NONE, Types::PRIORITY_WINDOW_RULE); - else if (effect == "always") - m_idleInhibitMode.first.set(IDLEINHIBIT_ALWAYS, Types::PRIORITY_WINDOW_RULE); - else if (effect == "focus") - m_idleInhibitMode.first.set(IDLEINHIBIT_FOCUS, Types::PRIORITY_WINDOW_RULE); - else if (effect == "fullscreen") - m_idleInhibitMode.first.set(IDLEINHIBIT_FULLSCREEN, Types::PRIORITY_WINDOW_RULE); - else - Log::logger->log(Log::ERR, "Rule idleinhibit: unknown mode {}", effect); + m_idleInhibitMode.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_idleInhibitMode.second = rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_OPACITY: { - try { - CVarList2 vars(std::string{effect}, 0, ' '); - - int opacityIDX = 0; - - for (const auto& r : vars) { - if (r == "opacity") - continue; - - if (r == "override") { - if (opacityIDX == 1) - m_alpha.first = Types::COverridableVar(Types::SAlphaValue{.alpha = m_alpha.first.value().alpha, .overridden = true}, Types::PRIORITY_WINDOW_RULE); - else if (opacityIDX == 2) - m_alphaInactive.first = - Types::COverridableVar(Types::SAlphaValue{.alpha = m_alphaInactive.first.value().alpha, .overridden = true}, Types::PRIORITY_WINDOW_RULE); - else if (opacityIDX == 3) - m_alphaFullscreen.first = - Types::COverridableVar(Types::SAlphaValue{.alpha = m_alphaFullscreen.first.value().alpha, .overridden = true}, Types::PRIORITY_WINDOW_RULE); - } else { - if (opacityIDX == 0) - m_alpha.first = Types::COverridableVar(Types::SAlphaValue{.alpha = std::stof(std::string{r}), .overridden = false}, Types::PRIORITY_WINDOW_RULE); - else if (opacityIDX == 1) - m_alphaInactive.first = - Types::COverridableVar(Types::SAlphaValue{.alpha = std::stof(std::string{r}), .overridden = false}, Types::PRIORITY_WINDOW_RULE); - else if (opacityIDX == 2) - m_alphaFullscreen.first = - Types::COverridableVar(Types::SAlphaValue{.alpha = std::stof(std::string{r}), .overridden = false}, Types::PRIORITY_WINDOW_RULE); - else - throw std::runtime_error("more than 3 alpha values"); - - opacityIDX++; - } - } - - if (opacityIDX == 1) { - m_alphaInactive.first = m_alpha.first; - m_alphaFullscreen.first = m_alpha.first; - } - } catch (std::exception& e) { Log::logger->log(Log::ERR, "Opacity rule \"{}\" failed with: {}", effect, e.what()); } + const auto& opacity = std::get(value); + m_alpha.first = Types::COverridableVar(opacity.alpha, Types::PRIORITY_WINDOW_RULE); + m_alphaInactive.first = Types::COverridableVar(opacity.alphaInactive, Types::PRIORITY_WINDOW_RULE); + m_alphaFullscreen.first = Types::COverridableVar(opacity.alphaFullscreen, Types::PRIORITY_WINDOW_RULE); m_alpha.second = rule->getPropertiesMask(); m_alphaInactive.second = rule->getPropertiesMask(); m_alphaFullscreen.second = rule->getPropertiesMask(); @@ -304,142 +212,136 @@ CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyDynamicRule(const break; } case WINDOW_RULE_EFFECT_BORDER_SIZE: { - try { - auto oldBorderSize = m_borderSize.first.valueOrDefault(); - m_borderSize.first.set(std::stoi(effect), Types::PRIORITY_WINDOW_RULE); - m_borderSize.second |= rule->getPropertiesMask(); - if (oldBorderSize != m_borderSize.first.valueOrDefault()) - result.needsRelayout = true; - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyDynamicRule: invalid border_size {}", effect); } + auto oldBorderSize = m_borderSize.first.valueOrDefault(); + m_borderSize.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); + m_borderSize.second |= rule->getPropertiesMask(); + if (oldBorderSize != m_borderSize.first.valueOrDefault()) + result.needsRelayout = true; break; } case WINDOW_RULE_EFFECT_ALLOWS_INPUT: { - m_allowsInput.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_allowsInput.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_allowsInput.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_DIM_AROUND: { - m_dimAround.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_dimAround.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_dimAround.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_DECORATE: { - m_decorate.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_decorate.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_decorate.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_FOCUS_ON_ACTIVATE: { - m_focusOnActivate.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_focusOnActivate.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_focusOnActivate.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_KEEP_ASPECT_RATIO: { - m_keepAspectRatio.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_keepAspectRatio.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_keepAspectRatio.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_NEAREST_NEIGHBOR: { - m_nearestNeighbor.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_nearestNeighbor.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_nearestNeighbor.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_NO_ANIM: { - m_noAnim.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_noAnim.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_noAnim.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_NO_BLUR: { - m_noBlur.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_noBlur.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_noBlur.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_NO_DIM: { - m_noDim.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_noDim.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_noDim.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_NO_FOCUS: { - m_noFocus.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_noFocus.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_noFocus.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_NO_FOLLOW_MOUSE: { - m_noFollowMouse.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_noFollowMouse.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_noFollowMouse.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_NO_MAX_SIZE: { - m_noMaxSize.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_noMaxSize.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_noMaxSize.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_NO_SHADOW: { - m_noShadow.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_noShadow.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_noShadow.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_NO_SHORTCUTS_INHIBIT: { - m_noShortcutsInhibit.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_noShortcutsInhibit.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_noShortcutsInhibit.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_OPAQUE: { - m_opaque.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_opaque.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_opaque.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_FORCE_RGBX: { - m_RGBX.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_RGBX.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_RGBX.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_SYNC_FULLSCREEN: { - m_syncFullscreen.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_syncFullscreen.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_syncFullscreen.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_IMMEDIATE: { - m_tearing.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_tearing.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_tearing.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_XRAY: { - m_xray.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_xray.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_xray.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_RENDER_UNFOCUSED: { - m_renderUnfocused.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_renderUnfocused.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_renderUnfocused.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_NO_SCREEN_SHARE: { - m_noScreenShare.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_noScreenShare.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_noScreenShare.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_NO_VRR: { - m_noVRR.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_noVRR.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_noVRR.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_STAY_FOCUSED: { - m_stayFocused.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); + m_stayFocused.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); m_stayFocused.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_SCROLL_MOUSE: { - try { - m_scrollMouse.first.set(std::clamp(std::stof(effect), 0.01F, 10.F), Types::PRIORITY_WINDOW_RULE); - m_scrollMouse.second |= rule->getPropertiesMask(); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyDynamicRule: invalid scroll_mouse {}", effect); } + m_scrollMouse.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); + m_scrollMouse.second |= rule->getPropertiesMask(); break; } case WINDOW_RULE_EFFECT_SCROLL_TOUCHPAD: { - try { - m_scrollTouchpad.first.set(std::clamp(std::stof(effect), 0.01F, 10.F), Types::PRIORITY_WINDOW_RULE); - m_scrollTouchpad.second |= rule->getPropertiesMask(); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyDynamicRule: invalid scroll_touchpad {}", effect); } + m_scrollTouchpad.first.set(std::get(value), Types::PRIORITY_WINDOW_RULE); + m_scrollTouchpad.second |= rule->getPropertiesMask(); break; } } @@ -448,7 +350,10 @@ CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyDynamicRule(const } CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyStaticRule(const SP& rule) { - for (const auto& [key, effect] : rule->effects()) { + for (const auto& effectData : rule->effects()) { + const auto key = effectData.key; + const auto& value = effectData.value; + switch (key) { default: { Log::logger->log(Log::TRACE, "CWindowRuleApplicator::applyStaticRule: Skipping effect {}, not static", sc>(key)); @@ -456,89 +361,82 @@ CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyStaticRule(const } case WINDOW_RULE_EFFECT_FLOAT: { - static_.floating = truthy(effect); + static_.floating = std::get(value); break; } case WINDOW_RULE_EFFECT_TILE: { - static_.floating = !truthy(effect); + static_.floating = !std::get(value); break; } case WINDOW_RULE_EFFECT_FULLSCREEN: { - static_.fullscreen = truthy(effect); + static_.fullscreen = std::get(value); break; } case WINDOW_RULE_EFFECT_MAXIMIZE: { - static_.maximize = truthy(effect); + static_.maximize = std::get(value); break; } case WINDOW_RULE_EFFECT_FULLSCREENSTATE: { - CVarList2 vars(std::string{effect}, 0, 's'); - try { - static_.fullscreenStateInternal = std::stoi(std::string{vars[0]}); - if (!vars[1].empty()) - static_.fullscreenStateClient = std::stoi(std::string{vars[1]}); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyStaticRule: invalid fullscreen state {}", effect); } + const auto& fullscreenState = std::get(value); + static_.fullscreenStateInternal = fullscreenState.internal; + if (fullscreenState.client) + static_.fullscreenStateClient = *fullscreenState.client; break; } case WINDOW_RULE_EFFECT_MOVE: { static_.center = std::nullopt; - static_.position = effect; + static_.position = std::get(value); break; } case WINDOW_RULE_EFFECT_SIZE: { - static_.size = effect; + static_.size = std::get(value); break; } case WINDOW_RULE_EFFECT_CENTER: { static_.position.clear(); - static_.center = truthy(effect); + static_.center = std::get(value); break; } case WINDOW_RULE_EFFECT_PSEUDO: { - static_.pseudo = truthy(effect); + static_.pseudo = std::get(value); break; } case WINDOW_RULE_EFFECT_MONITOR: { - static_.monitor = effect; + static_.monitor = std::get(value); break; } case WINDOW_RULE_EFFECT_WORKSPACE: { - static_.workspace = effect; + static_.workspace = std::get(value); break; } case WINDOW_RULE_EFFECT_NOINITIALFOCUS: { - static_.noInitialFocus = truthy(effect); + static_.noInitialFocus = std::get(value); break; } case WINDOW_RULE_EFFECT_PIN: { - static_.pin = truthy(effect); + static_.pin = std::get(value); break; } case WINDOW_RULE_EFFECT_GROUP: { - static_.group = effect; + static_.group = std::get(value); break; } case WINDOW_RULE_EFFECT_SUPPRESSEVENT: { - CVarList2 varlist(std::string{effect}, 0, 's'); - for (const auto& e : varlist) { + for (const auto& e : std::get>(value)) { static_.suppressEvent.emplace_back(e); } break; } case WINDOW_RULE_EFFECT_CONTENT: { - static_.content = NContentType::fromString(effect); + static_.content = std::get(value); break; } case WINDOW_RULE_EFFECT_NOCLOSEFOR: { - try { - static_.noCloseFor = std::stoi(effect); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyStaticRule: invalid no close for {}", effect); } + static_.noCloseFor = std::get(value); break; } case WINDOW_RULE_EFFECT_SCROLLING_WIDTH: { - try { - static_.scrollingWidth = std::stof(effect); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyStaticRule: invalid scrolling width {}", effect); } + static_.scrollingWidth = std::get(value); break; } } diff --git a/src/helpers/AsyncDialogBox.cpp b/src/helpers/AsyncDialogBox.cpp index 8c2c7cd70..570592757 100644 --- a/src/helpers/AsyncDialogBox.cpp +++ b/src/helpers/AsyncDialogBox.cpp @@ -161,7 +161,12 @@ SP CAsyncDialogBox::lockSelf() { } void CAsyncDialogBox::setExecRule(std::string&& s) { - auto rule = Desktop::Rule::CWindowRule::buildFromExecString(std::move(s)); - m_execRuleToken = rule->execToken(); - Desktop::Rule::ruleEngine()->registerRule(std::move(rule)); + auto rule = Desktop::Rule::CWindowRule::buildFromExecString(std::move(s)); + if (!rule) { + Log::logger->log(Log::ERR, "CAsyncDialogBox: failed to parse exec rule: {}", rule.error()); + return; + } + + m_execRuleToken = (*rule)->execToken(); + Desktop::Rule::ruleEngine()->registerRule(std::move(*rule)); }