summaryrefslogtreecommitdiff
path: root/src/desktop/rule/windowRule/WindowRule.cpp
diff options
context:
space:
mode:
authorVaxry <43317083+vaxerski@users.noreply.github.com>2025-11-17 18:34:02 +0000
committerGitHub <noreply@github.com>2025-11-17 18:34:02 +0000
commitc2670e9ab90bd657e87a1da2c5322e9007dce01f (patch)
treecd033fe3a9b87b1b0d457df043ecc50929ad3c8d /src/desktop/rule/windowRule/WindowRule.cpp
parent95ee08b3403b90e2930d46425cada1d280f3524e (diff)
windowrules: rewrite completely (#12269)
Reworks the window rule syntax completely --------- Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
Diffstat (limited to 'src/desktop/rule/windowRule/WindowRule.cpp')
-rw-r--r--src/desktop/rule/windowRule/WindowRule.cpp186
1 files changed, 186 insertions, 0 deletions
diff --git a/src/desktop/rule/windowRule/WindowRule.cpp b/src/desktop/rule/windowRule/WindowRule.cpp
new file mode 100644
index 00000000..b0387b67
--- /dev/null
+++ b/src/desktop/rule/windowRule/WindowRule.cpp
@@ -0,0 +1,186 @@
+#include "WindowRule.hpp"
+#include "../../Window.hpp"
+#include "../../../helpers/Monitor.hpp"
+#include "../../../Compositor.hpp"
+#include "../../../managers/TokenManager.hpp"
+
+using namespace Desktop;
+using namespace Desktop::Rule;
+
+std::optional<Vector2D> Rule::parseRelativeVector(PHLWINDOW w, const std::string& s) {
+ try {
+ const auto VALUE = s.substr(s.find(' ') + 1);
+ const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
+ const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
+
+ const auto MAXSIZE = w->requestedMaxSize();
+
+ const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, g_pCompositor->m_lastMonitor->m_size.x) :
+ stringToPercentage(SIZEXSTR, g_pCompositor->m_lastMonitor->m_size.x);
+
+ const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, g_pCompositor->m_lastMonitor->m_size.y) :
+ stringToPercentage(SIZEYSTR, g_pCompositor->m_lastMonitor->m_size.y);
+
+ return Vector2D{SIZEX, SIZEY};
+
+ } catch (...) { Debug::log(LOG, "Rule size failed, rule: {}", s); }
+
+ return std::nullopt;
+}
+
+CWindowRule::CWindowRule(const std::string& name) : IRule(name) {
+ ;
+}
+
+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));
+ m_effectSet.emplace(e);
+}
+
+const std::vector<std::pair<CWindowRule::storageType, std::string>>& CWindowRule::effects() {
+ return m_effects;
+}
+
+bool CWindowRule::matches(PHLWINDOW w, bool allowEnvLookup) {
+ if (m_matchEngines.empty())
+ return false;
+
+ for (const auto& [prop, engine] : m_matchEngines) {
+ switch (prop) {
+ default: {
+ Debug::log(TRACE, "CWindowRule::matches: skipping prop entry {}", sc<std::underlying_type_t<eRuleProperty>>(prop));
+ break;
+ }
+
+ case RULE_PROP_TITLE:
+ if (!engine->match(w->m_title))
+ return false;
+ break;
+ case RULE_PROP_INITIAL_TITLE:
+ if (!engine->match(w->m_initialTitle))
+ return false;
+ break;
+ case RULE_PROP_CLASS:
+ if (!engine->match(w->m_class))
+ return false;
+ break;
+ case RULE_PROP_INITIAL_CLASS:
+ if (!engine->match(w->m_initialClass))
+ return false;
+ break;
+ case RULE_PROP_FLOATING:
+ if (!engine->match(w->m_isFloating))
+ return false;
+ break;
+ case RULE_PROP_TAG:
+ if (!engine->match(w->m_ruleApplicator->m_tagKeeper))
+ return false;
+ break;
+ case RULE_PROP_XWAYLAND:
+ if (!engine->match(w->m_isX11))
+ return false;
+ break;
+ case RULE_PROP_FULLSCREEN:
+ if (!engine->match(w->m_fullscreenState.internal != 0))
+ return false;
+ break;
+ case RULE_PROP_PINNED:
+ if (!engine->match(w->m_pinned))
+ return false;
+ break;
+ case RULE_PROP_FOCUS:
+ if (!engine->match(g_pCompositor->m_lastWindow == w))
+ return false;
+ break;
+ case RULE_PROP_GROUP:
+ if (!engine->match(w->m_groupData.pNextWindow))
+ return false;
+ break;
+ case RULE_PROP_MODAL:
+ if (!engine->match(w->isModal()))
+ return false;
+ break;
+ case RULE_PROP_FULLSCREENSTATE_INTERNAL:
+ if (!engine->match(w->m_fullscreenState.internal))
+ return false;
+ break;
+ case RULE_PROP_FULLSCREENSTATE_CLIENT:
+ if (!engine->match(w->m_fullscreenState.client))
+ return false;
+ break;
+ case RULE_PROP_ON_WORKSPACE:
+ if (!engine->match(w->m_workspace))
+ return false;
+ break;
+ case RULE_PROP_CONTENT:
+ if (!engine->match(NContentType::toString(w->getContentType())))
+ return false;
+ break;
+ case RULE_PROP_XDG_TAG:
+ if (w->xdgTag().has_value() && !engine->match(*w->xdgTag()))
+ return false;
+ break;
+ case RULE_PROP_EXEC_TOKEN:
+ // this is only allowed on static rules, we don't need it on dynamic plus it's expensive
+ if (!allowEnvLookup)
+ break;
+
+ const auto ENV = w->getEnv();
+ if (ENV.contains(EXEC_RULE_ENV_NAME)) {
+ const auto TKN = ENV.at(EXEC_RULE_ENV_NAME);
+ if (!engine->match(TKN))
+ return false;
+ break;
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+SP<CWindowRule> CWindowRule::buildFromExecString(std::string&& s) {
+ CVarList2 varlist(std::move(s), 0, ';');
+ SP<CWindowRule> wr = makeShared<CWindowRule>("__exec_rule");
+
+ for (const auto& el : varlist) {
+ // split element by space, can't do better
+ size_t spacePos = el.find(' ');
+ if (spacePos != std::string::npos) {
+ // great, split and try to parse
+ auto LHS = el.substr(0, spacePos);
+ const auto EFFECT = windowEffects()->get(LHS);
+
+ if (!EFFECT.has_value() || *EFFECT == WINDOW_RULE_EFFECT_NONE)
+ continue; // invalid...
+
+ wr->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)});
+ continue;
+ }
+
+ // assume 1 maybe...
+
+ const auto EFFECT = windowEffects()->get(el);
+
+ if (!EFFECT.has_value() || *EFFECT == WINDOW_RULE_EFFECT_NONE)
+ continue; // invalid...
+
+ wr->addEffect(*EFFECT, std::string{"1"});
+ }
+
+ const auto TOKEN = g_pTokenManager->registerNewToken(nullptr, std::chrono::seconds(1));
+
+ wr->markAsExecRule(TOKEN, false /* TODO: could be nice. */);
+ wr->registerMatch(RULE_PROP_EXEC_TOKEN, TOKEN);
+
+ return wr;
+}
+
+const std::unordered_set<CWindowRule::storageType>& CWindowRule::effectsSet() {
+ return m_effectSet;
+}