From 6f1d2e771dca1b5eea5ec344ca1b6a80d4fd4ee5 Mon Sep 17 00:00:00 2001 From: ItsOhen Date: Sat, 27 Sep 2025 01:04:22 +0200 Subject: [PATCH] config: fix rules with no parameters not being counted as invalid (#11849) Quite a big whoopsie to insert invalid rules. Also adds special: cases. --- hyprtester/src/tests/main/window.cpp | 23 ++++++++ src/config/ConfigManager.cpp | 85 ++++++++++++++++------------ 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/hyprtester/src/tests/main/window.cpp b/hyprtester/src/tests/main/window.cpp index 4645a26c..9880997e 100644 --- a/hyprtester/src/tests/main/window.cpp +++ b/hyprtester/src/tests/main/window.cpp @@ -237,7 +237,30 @@ static bool test() { EXPECT_CONTAINS(str, "floating: 1"); EXPECT_CONTAINS(str, std::format("size: {},{}", SIZE, SIZE)); EXPECT_NOT_CONTAINS(str, "pinned: 1"); + + OK(getFromSocket("/keyword windowrule plugin:someplugin:variable, class:wr_kitty")); + OK(getFromSocket("/keyword windowrule plugin:someplugin:variable 10, class:wr_kitty")); + OK(getFromSocket("/keyword windowrule workspace 1, class:wr_kitty")); + OK(getFromSocket("/keyword windowrule workspace special:magic, class:magic_kitty")); + + if (!spawnKitty("magic_kitty")) + return false; + EXPECT_CONTAINS(getFromSocket("/activewindow"), "special:magic"); } + NLog::log("{}Testing faulty rules", Colors::YELLOW); + { + const auto PARAM = "Invalid parameter"; + const auto RULE = "Invalid value"; + const auto NORULE = "no rules provided"; + EXPECT_CONTAINS(getFromSocket("/keyword windowrule notarule, class:wr_kitty"), RULE) + EXPECT_CONTAINS(getFromSocket("/keyword windowrule class:wr_kitty"), NORULE) + EXPECT_CONTAINS(getFromSocket("/keyword windowrule float, class:wr_kitty, size"), PARAM) + EXPECT_CONTAINS(getFromSocket("/keyword windowrule float, classI:wr_kitty"), PARAM) + EXPECT_CONTAINS(getFromSocket("/keyword windowrule workspace:, class:wr_kitty"), NORULE) + } + + NLog::log("{}Reloading config", Colors::YELLOW); + OK(getFromSocket("/reload")); NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 70f0d2a3..c238a9ee 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -2664,21 +2664,24 @@ std::optional CConfigManager::handleWindowRule(const std::string& c 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; - if (!parsingParams && var.find(':') == std::string_view::npos) { - tokens.emplace_back(var); - } else { + if (!parsingParams) { + // Don't be alarmed, ends_with is a single memcmp, i went and checked. + if (sep == std::string_view::npos || key.ends_with("plugin") || key.ends_with("special")) { + tokens.emplace_back(var); + continue; + } parsingParams = true; - auto sep = var.find(':'); - if (sep == std::string_view::npos) - return std::format("Invalid rule: {}, Invalid parameter: {}", value, std::string(var)); - - std::string_view key = var.substr(0, sep); - // somewhat ugly trim. But since CVarList string_view trim isn't available, let's be lazy. - std::string_view val = var.substr(var.find_first_not_of(' ', sep + 1)); - - params[key] = val; } + + 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 { @@ -2687,47 +2690,53 @@ std::optional CConfigManager::handleWindowRule(const std::string& c return {}; }; - auto applyParams = [&](SP rule) -> void { + auto applyParams = [&](SP rule) -> bool { + bool set = false; + if (auto v = get("class"); !v.empty()) { - rule->m_class = v; + set |= (rule->m_class = v, true); rule->m_classRegex = {std::string(v)}; } if (auto v = get("title"); !v.empty()) { - rule->m_title = v; + set |= (rule->m_title = v, true); rule->m_titleRegex = {std::string(v)}; } if (auto v = get("tag"); !v.empty()) - rule->m_tag = v; + set |= (rule->m_tag = v, true); if (auto v = get("initialClass"); !v.empty()) { - rule->m_initialClass = v; + set |= (rule->m_initialClass = v, true); rule->m_initialClassRegex = {std::string(v)}; } if (auto v = get("initialTitle"); !v.empty()) { - rule->m_initialTitle = v; + set |= (rule->m_initialTitle = v, true); rule->m_initialTitleRegex = {std::string(v)}; } + if (auto v = get("xwayland"); !v.empty()) - rule->m_X11 = (v == "1"); + set |= (rule->m_X11 = (v == "1"), true); if (auto v = get("floating"); !v.empty()) - rule->m_floating = (v == "1"); + set |= (rule->m_floating = (v == "1"), true); if (auto v = get("fullscreen"); !v.empty()) - rule->m_fullscreen = (v == "1"); + set |= (rule->m_fullscreen = (v == "1"), true); if (auto v = get("pinned"); !v.empty()) - rule->m_pinned = (v == "1"); - if (auto v = get("fullscreenstate"); !v.empty()) - rule->m_fullscreenState = v; - if (auto v = get("workspace"); !v.empty()) - rule->m_workspace = v; + set |= (rule->m_pinned = (v == "1"), true); if (auto v = get("focus"); !v.empty()) - rule->m_focus = (v == "1"); - if (auto v = get("onworkspace"); !v.empty()) - rule->m_onWorkspace = v; - if (auto v = get("content"); !v.empty()) - rule->m_contentType = v; - if (auto v = get("xdgTag"); !v.empty()) - rule->m_xdgTag = v; + set |= (rule->m_focus = (v == "1"), true); if (auto v = get("group"); !v.empty()) - rule->m_group = (v == "1"); + set |= (rule->m_group = (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; @@ -2785,8 +2794,12 @@ std::optional CConfigManager::handleWindowRule(const std::string& c Debug::log(ERR, "Invalid rule found: {}, Invalid value: {}", value, token); return std::format("Invalid rule found: {}, Invalid value: {}", value, token); } - applyParams(rule); - rules.emplace_back(rule); + 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); + } } }