From c2670e9ab90bd657e87a1da2c5322e9007dce01f Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Mon, 17 Nov 2025 18:34:02 +0000 Subject: windowrules: rewrite completely (#12269) Reworks the window rule syntax completely --------- Co-authored-by: Mihai Fufezan --- src/config/ConfigManager.cpp | 728 ++++++++++++------------------------------- 1 file changed, 206 insertions(+), 522 deletions(-) (limited to 'src/config/ConfigManager.cpp') diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index b4b6ed3d..8efbe945 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -8,12 +8,15 @@ #include "../render/decorations/CHyprGroupBarDecoration.hpp" #include "config/ConfigDataValues.hpp" #include "config/ConfigValue.hpp" -#include "../desktop/WindowRule.hpp" #include "../protocols/LayerShell.hpp" #include "../xwayland/XWayland.hpp" #include "../protocols/OutputManagement.hpp" #include "../managers/animation/AnimationManager.hpp" #include "../desktop/LayerSurface.hpp" +#include "../desktop/rule/Engine.hpp" +#include "../desktop/rule/windowRule/WindowRule.hpp" +#include "../desktop/rule/layerRule/LayerRule.hpp" +#include "../debug/HyprCtl.hpp" #include "defaultConfig.hpp" #include "../render/Renderer.hpp" @@ -299,35 +302,11 @@ static Hyprlang::CParseResult handleUnbind(const char* c, const char* v) { return result; } -static Hyprlang::CParseResult handleWindowRule(const char* c, const char* v) { - const std::string VALUE = v; - const std::string COMMAND = c; - - const auto RESULT = g_pConfigManager->handleWindowRule(COMMAND, VALUE); - - Hyprlang::CParseResult result; - if (RESULT.has_value()) - result.setError(RESULT.value().c_str()); - return result; -} - -static Hyprlang::CParseResult handleLayerRule(const char* c, const char* v) { - const std::string VALUE = v; - const std::string COMMAND = c; - - const auto RESULT = g_pConfigManager->handleLayerRule(COMMAND, VALUE); - - Hyprlang::CParseResult result; - if (RESULT.has_value()) - result.setError(RESULT.value().c_str()); - return result; -} - -static Hyprlang::CParseResult handleWindowRuleV2(const char* c, const char* v) { +static Hyprlang::CParseResult handleWorkspaceRules(const char* c, const char* v) { const std::string VALUE = v; const std::string COMMAND = c; - const auto RESULT = g_pConfigManager->handleWindowRule(COMMAND, VALUE); + const auto RESULT = g_pConfigManager->handleWorkspaceRules(COMMAND, VALUE); Hyprlang::CParseResult result; if (RESULT.has_value()) @@ -335,11 +314,11 @@ static Hyprlang::CParseResult handleWindowRuleV2(const char* c, const char* v) { return result; } -static Hyprlang::CParseResult handleBlurLS(const char* c, const char* v) { +static Hyprlang::CParseResult handleSubmap(const char* c, const char* v) { const std::string VALUE = v; const std::string COMMAND = c; - const auto RESULT = g_pConfigManager->handleBlurLS(COMMAND, VALUE); + const auto RESULT = g_pConfigManager->handleSubmap(COMMAND, VALUE); Hyprlang::CParseResult result; if (RESULT.has_value()) @@ -347,11 +326,11 @@ static Hyprlang::CParseResult handleBlurLS(const char* c, const char* v) { return result; } -static Hyprlang::CParseResult handleWorkspaceRules(const char* c, const char* v) { +static Hyprlang::CParseResult handleSource(const char* c, const char* v) { const std::string VALUE = v; const std::string COMMAND = c; - const auto RESULT = g_pConfigManager->handleWorkspaceRules(COMMAND, VALUE); + const auto RESULT = g_pConfigManager->handleSource(COMMAND, VALUE); Hyprlang::CParseResult result; if (RESULT.has_value()) @@ -359,11 +338,11 @@ static Hyprlang::CParseResult handleWorkspaceRules(const char* c, const char* v) return result; } -static Hyprlang::CParseResult handleSubmap(const char* c, const char* v) { +static Hyprlang::CParseResult handleEnv(const char* c, const char* v) { const std::string VALUE = v; const std::string COMMAND = c; - const auto RESULT = g_pConfigManager->handleSubmap(COMMAND, VALUE); + const auto RESULT = g_pConfigManager->handleEnv(COMMAND, VALUE); Hyprlang::CParseResult result; if (RESULT.has_value()) @@ -371,11 +350,11 @@ static Hyprlang::CParseResult handleSubmap(const char* c, const char* v) { return result; } -static Hyprlang::CParseResult handleSource(const char* c, const char* v) { +static Hyprlang::CParseResult handlePlugin(const char* c, const char* v) { const std::string VALUE = v; const std::string COMMAND = c; - const auto RESULT = g_pConfigManager->handleSource(COMMAND, VALUE); + const auto RESULT = g_pConfigManager->handlePlugin(COMMAND, VALUE); Hyprlang::CParseResult result; if (RESULT.has_value()) @@ -383,11 +362,11 @@ static Hyprlang::CParseResult handleSource(const char* c, const char* v) { return result; } -static Hyprlang::CParseResult handleEnv(const char* c, const char* v) { +static Hyprlang::CParseResult handlePermission(const char* c, const char* v) { const std::string VALUE = v; const std::string COMMAND = c; - const auto RESULT = g_pConfigManager->handleEnv(COMMAND, VALUE); + const auto RESULT = g_pConfigManager->handlePermission(COMMAND, VALUE); Hyprlang::CParseResult result; if (RESULT.has_value()) @@ -395,11 +374,11 @@ static Hyprlang::CParseResult handleEnv(const char* c, const char* v) { return result; } -static Hyprlang::CParseResult handlePlugin(const char* c, const char* v) { +static Hyprlang::CParseResult handleGesture(const char* c, const char* v) { const std::string VALUE = v; const std::string COMMAND = c; - const auto RESULT = g_pConfigManager->handlePlugin(COMMAND, VALUE); + const auto RESULT = g_pConfigManager->handleGesture(COMMAND, VALUE); Hyprlang::CParseResult result; if (RESULT.has_value()) @@ -407,11 +386,11 @@ static Hyprlang::CParseResult handlePlugin(const char* c, const char* v) { return result; } -static Hyprlang::CParseResult handlePermission(const char* c, const char* v) { +static Hyprlang::CParseResult handleWindowrule(const char* c, const char* v) { const std::string VALUE = v; const std::string COMMAND = c; - const auto RESULT = g_pConfigManager->handlePermission(COMMAND, VALUE); + const auto RESULT = g_pConfigManager->handleWindowrule(COMMAND, VALUE); Hyprlang::CParseResult result; if (RESULT.has_value()) @@ -419,11 +398,11 @@ static Hyprlang::CParseResult handlePermission(const char* c, const char* v) { return result; } -static Hyprlang::CParseResult handleGesture(const char* c, const char* v) { +static Hyprlang::CParseResult handleLayerrule(const char* c, const char* v) { const std::string VALUE = v; const std::string COMMAND = c; - const auto RESULT = g_pConfigManager->handleGesture(COMMAND, VALUE); + const auto RESULT = g_pConfigManager->handleLayerrule(COMMAND, VALUE); Hyprlang::CParseResult result; if (RESULT.has_value()) @@ -463,7 +442,6 @@ CConfigManager::CConfigManager() { m_config = makeUnique(m_configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true}); registerConfigVar("general:border_size", Hyprlang::INT{1}); - registerConfigVar("general:no_border_on_floating", Hyprlang::INT{0}); registerConfigVar("general:gaps_in", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "5"}); registerConfigVar("general:gaps_out", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "20"}); registerConfigVar("general:float_gaps", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "0"}); @@ -858,6 +836,16 @@ CConfigManager::CConfigManager() { m_config->addSpecialConfigValue("monitorv2", "max_luminance", Hyprlang::INT{-1}); m_config->addSpecialConfigValue("monitorv2", "max_avg_luminance", Hyprlang::INT{-1}); + // windowrule v3 + m_config->addSpecialCategory("windowrule", {.key = "name"}); + m_config->addSpecialConfigValue("windowrule", "enable", Hyprlang::INT{1}); + + // layerrule v2 + m_config->addSpecialCategory("layerrule", {.key = "name"}); + m_config->addSpecialConfigValue("layerrule", "enable", Hyprlang::INT{1}); + + reloadRuleConfigs(); + // keywords m_config->registerHandler(&::handleExec, "exec", {false}); m_config->registerHandler(&::handleRawExec, "execr", {false}); @@ -868,14 +856,12 @@ CConfigManager::CConfigManager() { m_config->registerHandler(&::handleBind, "bind", {true}); m_config->registerHandler(&::handleUnbind, "unbind", {false}); m_config->registerHandler(&::handleWorkspaceRules, "workspace", {false}); - m_config->registerHandler(&::handleWindowRule, "windowrule", {false}); - m_config->registerHandler(&::handleLayerRule, "layerrule", {false}); - m_config->registerHandler(&::handleWindowRuleV2, "windowrulev2", {false}); + m_config->registerHandler(&::handleWindowrule, "windowrule", {false}); + m_config->registerHandler(&::handleLayerrule, "layerrule", {false}); m_config->registerHandler(&::handleBezier, "bezier", {false}); m_config->registerHandler(&::handleAnimation, "animation", {false}); m_config->registerHandler(&::handleSource, "source", {false}); m_config->registerHandler(&::handleSubmap, "submap", {false}); - m_config->registerHandler(&::handleBlurLS, "blurls", {false}); m_config->registerHandler(&::handlePlugin, "plugin", {false}); m_config->registerHandler(&::handlePermission, "permission", {false}); m_config->registerHandler(&::handleGesture, "gesture", {false}); @@ -905,6 +891,26 @@ CConfigManager::CConfigManager() { g_pEventLoopManager->doLater([ERR] { g_pHyprError->queueCreate(ERR.value(), CHyprColor{1.0, 0.1, 0.1, 1.0}); }); } +void CConfigManager::reloadRuleConfigs() { + // FIXME: this should also remove old values if they are removed + + for (const auto& r : Desktop::Rule::allMatchPropStrings()) { + m_config->addSpecialConfigValue("windowrule", ("match:" + r).c_str(), Hyprlang::STRING{""}); + } + + for (const auto& r : Desktop::Rule::windowEffects()->allEffectStrings()) { + m_config->addSpecialConfigValue("windowrule", r.c_str(), Hyprlang::STRING{""}); + } + + for (const auto& r : Desktop::Rule::allMatchPropStrings()) { + m_config->addSpecialConfigValue("layerrule", ("match:" + r).c_str(), Hyprlang::STRING{""}); + } + + for (const auto& r : Desktop::Rule::layerEffects()->allEffectStrings()) { + m_config->addSpecialConfigValue("layerrule", r.c_str(), Hyprlang::STRING{""}); + } +} + std::optional CConfigManager::generateConfig(std::string configPath) { std::string parentPath = std::filesystem::path(configPath).parent_path(); @@ -991,6 +997,7 @@ void CConfigManager::reload() { m_configCurrentPath = getMainConfigPath(); const auto ERR = m_config->parse(); const auto monitorError = handleMonitorv2(); + const auto ruleError = reloadRules(); m_lastConfigVerificationWasSuccessful = !ERR.error && !monitorError.error; postConfigReload(ERR.error || !monitorError.error ? ERR : monitorError); } @@ -1058,20 +1065,18 @@ void CConfigManager::setDefaultAnimationVars() { std::optional CConfigManager::resetHLConfig() { m_monitorRules.clear(); - m_windowRules.clear(); g_pKeybindManager->clearKeybinds(); g_pAnimationManager->removeAllBeziers(); g_pAnimationManager->addBezierWithName("linear", Vector2D(0.0, 0.0), Vector2D(1.0, 1.0)); g_pTrackpadGestures->clearGestures(); m_mAdditionalReservedAreas.clear(); - m_blurLSNamespaces.clear(); m_workspaceRules.clear(); setDefaultAnimationVars(); // reset anims m_declaredPlugins.clear(); - m_layerRules.clear(); m_failedPluginConfigValues.clear(); m_finalExecRequests.clear(); + m_keywordRules.clear(); // paths m_configPaths.clear(); @@ -1081,6 +1086,8 @@ std::optional CConfigManager::resetHLConfig() { const auto RET = verifyConfigExists(); + reloadRuleConfigs(); + return RET; } @@ -1179,6 +1186,77 @@ Hyprlang::CParseResult CConfigManager::handleMonitorv2() { return result; } +std::optional CConfigManager::addRuleFromConfigKey(const std::string& name) { + const auto ENABLED = m_config->getSpecialConfigValuePtr("windowrule", "enable", name.c_str()); + if (ENABLED && ENABLED->m_bSetByUser && std::any_cast(ENABLED->getValue()) == 0) + return std::nullopt; + + SP rule = makeShared(name); + + for (const auto& r : Desktop::Rule::allMatchPropStrings()) { + auto VAL = m_config->getSpecialConfigValuePtr("windowrule", ("match:" + r).c_str(), name.c_str()); + if (VAL && VAL->m_bSetByUser) + rule->registerMatch(Desktop::Rule::matchPropFromString(r).value_or(Desktop::Rule::RULE_PROP_NONE), std::any_cast(VAL->getValue())); + } + + 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())); + } + + Desktop::Rule::ruleEngine()->registerRule(std::move(rule)); + return std::nullopt; +} + +std::optional CConfigManager::addLayerRuleFromConfigKey(const std::string& name) { + + const auto ENABLED = m_config->getSpecialConfigValuePtr("layerrule", "enable", name.c_str()); + if (ENABLED && ENABLED->m_bSetByUser && std::any_cast(ENABLED->getValue()) != 0) + return std::nullopt; + + SP rule = makeShared(name); + + for (const auto& r : Desktop::Rule::allMatchPropStrings()) { + auto VAL = m_config->getSpecialConfigValuePtr("layerrule", ("match:" + r).c_str(), name.c_str()); + if (VAL && VAL->m_bSetByUser) + rule->registerMatch(Desktop::Rule::matchPropFromString(r).value_or(Desktop::Rule::RULE_PROP_NONE), std::any_cast(VAL->getValue())); + } + + for (const auto& e : Desktop::Rule::layerEffects()->allEffectStrings()) { + auto VAL = m_config->getSpecialConfigValuePtr("layerrule", e.c_str(), name.c_str()); + if (VAL && VAL->m_bSetByUser) + rule->addEffect(Desktop::Rule::layerEffects()->get(e).value_or(Desktop::Rule::LAYER_RULE_EFFECT_NONE), std::any_cast(VAL->getValue())); + } + + Desktop::Rule::ruleEngine()->registerRule(std::move(rule)); + return std::nullopt; +} + +Hyprlang::CParseResult CConfigManager::reloadRules() { + Desktop::Rule::ruleEngine()->clearAllRules(); + + Hyprlang::CParseResult result; + for (const auto& name : m_config->listKeysForSpecialCategory("windowrule")) { + const auto error = addRuleFromConfigKey(name); + if (error.has_value()) + result.setError(error.value().c_str()); + } + for (const auto& name : m_config->listKeysForSpecialCategory("layerrule")) { + const auto error = addLayerRuleFromConfigKey(name); + if (error.has_value()) + result.setError(error.value().c_str()); + } + + for (auto& rule : m_keywordRules) { + Desktop::Rule::ruleEngine()->registerRule(SP{rule}); + } + + Desktop::Rule::ruleEngine()->updateAllRules(); + + return result; +} + void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) { updateWatcher(); @@ -1504,229 +1582,6 @@ SWorkspaceRule CConfigManager::mergeWorkspaceRules(const SWorkspaceRule& rule1, return mergedRule; } -std::vector> CConfigManager::getMatchingRules(PHLWINDOW pWindow, bool dynamic, bool shadowExec) { - if (!valid(pWindow)) - return std::vector>(); - - // if the window is unmapped, don't process exec rules yet. - shadowExec = shadowExec || !pWindow->m_isMapped; - - std::vector> returns; - - Debug::log(LOG, "Searching for matching rules for {} (title: {})", pWindow->m_class, pWindow->m_title); - - // since some rules will be applied later, we need to store some flags - bool hasFloating = pWindow->m_isFloating; - bool hasFullscreen = pWindow->isFullscreen(); - bool isGrouped = pWindow->m_groupData.pNextWindow; - - // local tags for dynamic tag rule match - auto tags = pWindow->m_tags; - - for (auto const& rule : m_windowRules) { - // check if we have a matching rule - if (!rule->m_v2) { - try { - if (rule->m_value.starts_with("tag:") && !tags.isTagged(rule->m_value.substr(4))) - continue; - - if (rule->m_value.starts_with("title:") && !rule->m_v1Regex.passes(pWindow->m_title)) - continue; - - if (!rule->m_v1Regex.passes(pWindow->m_class)) - continue; - - } catch (...) { - Debug::log(ERR, "Regex error at {}", rule->m_value); - continue; - } - } else { - try { - if (rule->m_X11 != -1) { - if (pWindow->m_isX11 != rule->m_X11) - continue; - } - - if (rule->m_floating != -1) { - if (hasFloating != rule->m_floating) - continue; - } - - if (rule->m_fullscreen != -1) { - if (hasFullscreen != rule->m_fullscreen) - continue; - } - - if (rule->m_pinned != -1) { - if (pWindow->m_pinned != rule->m_pinned) - continue; - } - - if (rule->m_focus != -1) { - if (rule->m_focus != (g_pCompositor->m_lastWindow.lock() == pWindow)) - continue; - } - - if (rule->m_group != -1) { - if (rule->m_group != isGrouped) - continue; - } - - if (rule->m_modal != -1) { - if (rule->m_modal != pWindow->isModal()) - continue; - } - - if (!rule->m_fullscreenState.empty()) { - const auto ARGS = CVarList(rule->m_fullscreenState, 2, ' '); - // - std::optional internalMode, clientMode; - - if (ARGS[0] == "*") - internalMode = std::nullopt; - else if (isNumber(ARGS[0])) - internalMode = sc(std::stoi(ARGS[0])); - else - throw std::runtime_error("szFullscreenState internal mode not valid"); - - if (ARGS[1] == "*") - clientMode = std::nullopt; - else if (isNumber(ARGS[1])) - clientMode = sc(std::stoi(ARGS[1])); - else - throw std::runtime_error("szFullscreenState client mode not valid"); - - if (internalMode.has_value() && pWindow->m_fullscreenState.internal != internalMode) - continue; - - if (clientMode.has_value() && pWindow->m_fullscreenState.client != clientMode) - continue; - } - - if (!rule->m_onWorkspace.empty()) { - const auto PWORKSPACE = pWindow->m_workspace; - if (!PWORKSPACE || !PWORKSPACE->matchesStaticSelector(rule->m_onWorkspace)) - continue; - } - - if (!rule->m_contentType.empty()) { - try { - const auto contentType = NContentType::fromString(rule->m_contentType); - if (pWindow->getContentType() != contentType) - continue; - } catch (std::exception& e) { Debug::log(ERR, "Rule \"content:{}\" failed with: {}", rule->m_contentType, e.what()); } - } - - if (!rule->m_xdgTag.empty()) { - if (pWindow->xdgTag().value_or("") != rule->m_xdgTag) - continue; - } - - if (!rule->m_workspace.empty()) { - const auto PWORKSPACE = pWindow->m_workspace; - - if (!PWORKSPACE) - continue; - - if (rule->m_workspace.starts_with("name:")) { - if (PWORKSPACE->m_name != rule->m_workspace.substr(5)) - continue; - } else { - // number - if (!isNumber(rule->m_workspace)) - throw std::runtime_error("szWorkspace not name: or number"); - - const int64_t ID = std::stoll(rule->m_workspace); - - if (PWORKSPACE->m_id != ID) - continue; - } - } - - if (!rule->m_tag.empty() && !tags.isTagged(rule->m_tag)) - continue; - - if (!rule->m_class.empty() && !rule->m_classRegex.passes(pWindow->m_class)) - continue; - - if (!rule->m_title.empty() && !rule->m_titleRegex.passes(pWindow->m_title)) - continue; - - if (!rule->m_initialTitle.empty() && !rule->m_initialTitleRegex.passes(pWindow->m_initialTitle)) - continue; - - if (!rule->m_initialClass.empty() && !rule->m_initialClassRegex.passes(pWindow->m_initialClass)) - continue; - - } catch (std::exception& e) { - Debug::log(ERR, "Regex error at {} ({})", rule->m_value, e.what()); - continue; - } - } - - // applies. Read the rule and behave accordingly - Debug::log(LOG, "Window rule {} -> {} matched {}", rule->m_rule, rule->m_value, pWindow); - - returns.emplace_back(rule); - - // apply tag with local tags - if (rule->m_ruleType == CWindowRule::RULE_TAG) { - CVarList vars{rule->m_rule, 0, 's', true}; - if (vars.size() == 2 && vars[0] == "tag") - tags.applyTag(vars[1], true); - } - - if (dynamic) - continue; - - if (rule->m_rule == "float") - hasFloating = true; - else if (rule->m_rule == "fullscreen") - hasFullscreen = true; - } - - std::vector PIDs = {sc(pWindow->getPID())}; - while (getPPIDof(PIDs.back()) > 10) - PIDs.push_back(getPPIDof(PIDs.back())); - - bool anyExecFound = false; - - for (auto const& er : m_execRequestedRules) { - if (std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == er.iPid; })) { - returns.emplace_back(makeShared(er.szRule, "", false, true)); - anyExecFound = true; - } - } - - if (anyExecFound && !shadowExec) // remove exec rules to unclog searches in the future, why have the garbage here. - std::erase_if(m_execRequestedRules, [&](const SExecRequestedRule& other) { return std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == other.iPid; }); }); - - return returns; -} - -std::vector> CConfigManager::getMatchingRules(PHLLS pLS) { - std::vector> returns; - - if (!pLS->m_layerSurface || pLS->m_fadingOut) - return returns; - - for (auto const& lr : m_layerRules) { - if (lr->m_targetNamespace.starts_with("address:0x")) { - if (std::format("address:0x{:x}", rc(pLS.get())) != lr->m_targetNamespace) - continue; - } else if (!lr->m_targetNamespaceRegex.passes(pLS->m_layerSurface->m_layerNamespace)) - continue; - - // hit - returns.emplace_back(lr); - } - - if (shouldBlurLS(pLS->m_layerSurface->m_layerNamespace)) - returns.emplace_back(makeShared(pLS->m_layerSurface->m_layerNamespace, "blur")); - - return returns; -} - void CConfigManager::dispatchExecOnce() { if (m_firstExecDispatched || m_isFirstLaunch) return; @@ -1832,16 +1687,6 @@ bool CConfigManager::deviceConfigExists(const std::string& dev) { return m_config->specialCategoryExistsForKey("device", copy.c_str()); } -bool CConfigManager::shouldBlurLS(const std::string& ns) { - for (auto const& bls : m_blurLSNamespaces) { - if (bls == ns) { - return true; - } - } - - return false; -} - void CConfigManager::ensureMonitorStatus() { for (auto const& rm : g_pCompositor->m_realMonitors) { if (!rm->m_output || rm->m_isUnsafeFallback) @@ -1895,7 +1740,7 @@ void CConfigManager::ensureVRR(PHLMONITOR pMonitor) { return; // ??? bool wantVRR = PWORKSPACE->m_hasFullscreenWindow && (PWORKSPACE->m_fullscreenMode & FSMODE_FULLSCREEN); - if (wantVRR && PWORKSPACE->getFullscreenWindow()->m_windowData.noVRR.valueOrDefault()) + if (wantVRR && PWORKSPACE->getFullscreenWindow()->m_ruleApplicator->noVRR().valueOrDefault()) wantVRR = false; if (wantVRR && USEVRR == 3) { @@ -1964,10 +1809,6 @@ const std::vector& CConfigManager::getAllWorkspaceRules() { return m_workspaceRules; } -void CConfigManager::addExecRule(const SExecRequestedRule& rule) { - m_execRequestedRules.push_back(rule); -} - void CConfigManager::handlePluginLoads() { if (!g_pPluginSystem) return; @@ -2676,239 +2517,6 @@ std::optional CConfigManager::handleUnbind(const std::string& comma return {}; } -std::optional CConfigManager::handleWindowRule(const std::string& command, const std::string& value) { - const auto VARLIST = CVarList(value, 0, ',', true); - - std::vector tokens; - std::unordered_map params; - - bool parsingParams = false; - - for (const auto& varStr : VARLIST) { - std::string_view var = varStr; - auto sep = var.find(':'); - std::string_view key = (sep != std::string_view::npos) ? var.substr(0, sep) : var; - bool isParam = (sep != std::string_view::npos && !(key.starts_with("workspace ") || (key.starts_with("monitor ")) || key.ends_with("plugin"))); - - if (!parsingParams) { - if (!isParam) { - tokens.emplace_back(var); - continue; - } - - parsingParams = true; - } - - if (sep == std::string_view::npos) - return std::format("Invalid rule: {}, Invalid parameter: {}", value, std::string(var)); - - auto pos = var.find_first_not_of(' ', sep + 1); - std::string_view val = (pos != std::string_view::npos) ? var.substr(pos) : std::string_view{}; - params[key] = val; - } - - auto get = [&](std::string_view key) -> std::string_view { - if (auto it = params.find(key); it != params.end()) - return it->second; - return {}; - }; - - auto applyParams = [&](SP rule) -> bool { - bool set = false; - - if (auto v = get("class"); !v.empty()) { - set |= (rule->m_class = v, true); - rule->m_classRegex = {std::string(v)}; - } - if (auto v = get("title"); !v.empty()) { - set |= (rule->m_title = v, true); - rule->m_titleRegex = {std::string(v)}; - } - if (auto v = get("tag"); !v.empty()) - set |= (rule->m_tag = v, true); - if (auto v = get("initialClass"); !v.empty()) { - set |= (rule->m_initialClass = v, true); - rule->m_initialClassRegex = {std::string(v)}; - } - if (auto v = get("initialTitle"); !v.empty()) { - set |= (rule->m_initialTitle = v, true); - rule->m_initialTitleRegex = {std::string(v)}; - } - - if (auto v = get("xwayland"); !v.empty()) - set |= (rule->m_X11 = (v == "1"), true); - if (auto v = get("floating"); !v.empty()) - set |= (rule->m_floating = (v == "1"), true); - if (auto v = get("fullscreen"); !v.empty()) - set |= (rule->m_fullscreen = (v == "1"), true); - if (auto v = get("pinned"); !v.empty()) - set |= (rule->m_pinned = (v == "1"), true); - if (auto v = get("focus"); !v.empty()) - set |= (rule->m_focus = (v == "1"), true); - if (auto v = get("group"); !v.empty()) - set |= (rule->m_group = (v == "1"), true); - if (auto v = get("modal"); !v.empty()) - set |= (rule->m_modal = (v == "1"), true); - - if (auto v = get("fullscreenstate"); !v.empty()) - set |= (rule->m_fullscreenState = v, true); - if (auto v = get("workspace"); !v.empty()) - set |= (rule->m_workspace = v, true); - if (auto v = get("onworkspace"); !v.empty()) - set |= (rule->m_onWorkspace = v, true); - if (auto v = get("content"); !v.empty()) - set |= (rule->m_contentType = v, true); - if (auto v = get("xdgTag"); !v.empty()) - set |= (rule->m_xdgTag = v, true); - - return set; - }; - - std::vector> rules; - - for (auto token : tokens) { - if (token.starts_with("unset")) { - std::string ruleName = ""; - if (token.size() <= 6 || token.contains("all")) - ruleName = "all"; - else - ruleName = std::string(token.substr(6)); - auto rule = makeShared(ruleName, value, true); - applyParams(rule); - std::erase_if(m_windowRules, [&](const auto& other) { - if (!other->m_v2) - return other->m_class == rule->m_class && !rule->m_class.empty(); - - if (rule->m_ruleType != other->m_ruleType && ruleName != "all") - return false; - if (!rule->m_tag.empty() && rule->m_tag != other->m_tag) - return false; - if (!rule->m_class.empty() && rule->m_class != other->m_class) - return false; - if (!rule->m_title.empty() && rule->m_title != other->m_title) - return false; - if (!rule->m_initialClass.empty() && rule->m_initialClass != other->m_initialClass) - return false; - if (!rule->m_initialTitle.empty() && rule->m_initialTitle != other->m_initialTitle) - return false; - if (rule->m_X11 != -1 && rule->m_X11 != other->m_X11) - return false; - if (rule->m_floating != -1 && rule->m_floating != other->m_floating) - return false; - if (rule->m_fullscreen != -1 && rule->m_fullscreen != other->m_fullscreen) - return false; - if (rule->m_pinned != -1 && rule->m_pinned != other->m_pinned) - return false; - if (!rule->m_fullscreenState.empty() && rule->m_fullscreenState != other->m_fullscreenState) - return false; - if (!rule->m_workspace.empty() && rule->m_workspace != other->m_workspace) - return false; - if (rule->m_focus != -1 && rule->m_focus != other->m_focus) - return false; - if (!rule->m_onWorkspace.empty() && rule->m_onWorkspace != other->m_onWorkspace) - return false; - if (!rule->m_contentType.empty() && rule->m_contentType != other->m_contentType) - return false; - if (rule->m_group != -1 && rule->m_group != other->m_group) - return false; - if (rule->m_modal != -1 && rule->m_modal != other->m_modal) - return false; - return true; - }); - } else { - auto rule = makeShared(std::string(token), value, true); - if (rule->m_ruleType == CWindowRule::RULE_INVALID) { - Debug::log(ERR, "Invalid rule found: {}, Invalid value: {}", value, token); - return std::format("Invalid rule found: {}, Invalid value: {}", value, token); - } - if (applyParams(rule)) - rules.emplace_back(rule); - else { - Debug::log(INFO, "===== Skipping rule: {}, Invalid parameters", rule->m_value); - return std::format("Invalid parameters found in: {}", value); - } - } - } - - if (rules.empty() && tokens.empty()) - return "Invalid rule syntax: no rules provided"; - - for (auto& rule : rules) { - if (rule->m_ruleType == CWindowRule::RULE_SIZE || rule->m_ruleType == CWindowRule::RULE_MAXSIZE || rule->m_ruleType == CWindowRule::RULE_MINSIZE) - m_windowRules.insert(m_windowRules.begin(), rule); - else - m_windowRules.emplace_back(rule); - } - - return {}; -} - -std::optional CConfigManager::handleLayerRule(const std::string& command, const std::string& value) { - const auto RULE = trim(value.substr(0, value.find_first_of(','))); - const auto VALUE = trim(value.substr(value.find_first_of(',') + 1)); - - // check rule and value - if (RULE.empty() || VALUE.empty()) - return "empty rule?"; - - if (RULE == "unset") { - std::erase_if(m_layerRules, [&](const auto& other) { return other->m_targetNamespace == VALUE; }); - return {}; - } - - auto rule = makeShared(RULE, VALUE); - - if (rule->m_ruleType == CLayerRule::RULE_INVALID) { - Debug::log(ERR, "Invalid rule found: {}", RULE); - return "Invalid rule found: " + RULE; - } - - rule->m_targetNamespaceRegex = {VALUE}; - - m_layerRules.emplace_back(rule); - - for (auto const& m : g_pCompositor->m_monitors) - for (auto const& lsl : m->m_layerSurfaceLayers) - for (auto const& ls : lsl) - ls->applyRules(); - - return {}; -} - -void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBlur) { - const bool BYADDRESS = name.starts_with("address:"); - std::string matchName = name; - - if (BYADDRESS) - matchName = matchName.substr(8); - - for (auto const& m : g_pCompositor->m_monitors) { - for (auto const& lsl : m->m_layerSurfaceLayers) { - for (auto const& ls : lsl) { - if (BYADDRESS) { - if (std::format("0x{:x}", rc(ls.get())) == matchName) - ls->m_forceBlur = forceBlur; - } else if (ls->m_namespace == matchName) - ls->m_forceBlur = forceBlur; - } - } - } -} - -std::optional CConfigManager::handleBlurLS(const std::string& command, const std::string& value) { - if (value.starts_with("remove,")) { - const auto TOREMOVE = trim(value.substr(7)); - if (std::erase_if(m_blurLSNamespaces, [&](const auto& other) { return other == TOREMOVE; })) - updateBlurredLS(TOREMOVE, false); - return {}; - } - - m_blurLSNamespaces.emplace_back(value); - updateBlurredLS(value, true); - - return {}; -} - std::optional CConfigManager::handleWorkspaceRules(const std::string& command, const std::string& value) { // This can either be the monitor or the workspace identifier const auto FIRST_DELIM = value.find_first_of(','); @@ -3229,6 +2837,82 @@ std::optional CConfigManager::handleGesture(const std::string& comm return std::nullopt; } +std::optional CConfigManager::handleWindowrule(const std::string& command, const std::string& value) { + CVarList2 data(std::string{value}, 0, ','); + + SP rule = makeShared(); + + const auto& PROPS = Desktop::Rule::allMatchPropStrings(); + const auto& EFFECTS = Desktop::Rule::windowEffects()->allEffectStrings(); + + for (const auto& el : data) { + // split on space, no need for a CVarList here + size_t spacePos = el.find(' '); + if (spacePos == std::string::npos) + return std::format("invalid field {}: missing a value", el); + + const bool FIRST_IS_PROP = el.starts_with("match:"); + const auto FIRST = FIRST_IS_PROP ? el.substr(6, spacePos - 6) : el.substr(0, spacePos); + if (FIRST_IS_PROP && std::ranges::contains(PROPS, FIRST)) { + // it's a prop + const auto PROP = Desktop::Rule::matchPropFromString(FIRST); + if (!PROP.has_value()) + return std::format("invalid prop {}", el); + rule->registerMatch(*PROP, std::string{el.substr(spacePos + 1)}); + } else if (!FIRST_IS_PROP && std::ranges::contains(EFFECTS, FIRST)) { + // it's an effect + 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)}); + } else + return std::format("invalid field type {}", FIRST); + } + + m_keywordRules.emplace_back(std::move(rule)); + if (g_pHyprCtl && g_pHyprCtl->m_currentRequestParams.isDynamicKeyword) + Desktop::Rule::ruleEngine()->registerRule(SP{m_keywordRules.back()}); + + return std::nullopt; +} + +std::optional CConfigManager::handleLayerrule(const std::string& command, const std::string& value) { + CVarList2 data(std::string{value}, 0, ','); + + SP rule = makeShared(); + + const auto& PROPS = Desktop::Rule::allMatchPropStrings(); + const auto& EFFECTS = Desktop::Rule::layerEffects()->allEffectStrings(); + + for (const auto& el : data) { + // split on space, no need for a CVarList here + size_t spacePos = el.find(' '); + if (spacePos == std::string::npos) + return std::format("invalid field {}: missing a value", el); + + const bool FIRST_IS_PROP = el.starts_with("match:"); + const auto FIRST = FIRST_IS_PROP ? el.substr(6, spacePos - 6) : el.substr(0, spacePos); + if (FIRST_IS_PROP && std::ranges::contains(PROPS, FIRST)) { + // it's a prop + const auto PROP = Desktop::Rule::matchPropFromString(FIRST); + if (!PROP.has_value()) + return std::format("invalid prop {}", el); + rule->registerMatch(*PROP, std::string{el.substr(spacePos + 1)}); + } else if (!FIRST_IS_PROP && std::ranges::contains(EFFECTS, FIRST)) { + // it's an effect + const auto EFFECT = Desktop::Rule::layerEffects()->get(FIRST); + if (!EFFECT.has_value()) + return std::format("invalid effect {}", el); + rule->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)}); + } else + return std::format("invalid field type {}", FIRST); + } + + m_keywordRules.emplace_back(std::move(rule)); + + return std::nullopt; +} + const std::vector& CConfigManager::getAllDescriptions() { return CONFIG_OPTIONS; } -- cgit v1.2.3