summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/actions/setup_base/action.yml1
-rw-r--r--CMakeLists.txt3
-rw-r--r--example/hyprland.conf47
-rw-r--r--hyprtester/plugin/src/main.cpp28
-rw-r--r--hyprtester/src/tests/clients/pointer-scroll.cpp4
-rw-r--r--hyprtester/src/tests/clients/pointer-warp.cpp2
-rw-r--r--hyprtester/src/tests/main/hyprctl.cpp160
-rw-r--r--hyprtester/src/tests/main/tags.cpp21
-rw-r--r--hyprtester/src/tests/main/window.cpp137
-rw-r--r--hyprtester/src/tests/main/workspaces.cpp2
-rw-r--r--hyprtester/test.conf80
-rw-r--r--nix/default.nix2
-rw-r--r--src/Compositor.cpp144
-rw-r--r--src/Compositor.hpp1
-rw-r--r--src/config/ConfigDescriptions.hpp6
-rw-r--r--src/config/ConfigManager.cpp728
-rw-r--r--src/config/ConfigManager.hpp29
-rw-r--r--src/debug/HyprCtl.cpp134
-rw-r--r--src/debug/HyprCtl.hpp7
-rw-r--r--src/desktop/LayerRule.cpp42
-rw-r--r--src/desktop/LayerRule.hpp33
-rw-r--r--src/desktop/LayerSurface.cpp85
-rw-r--r--src/desktop/LayerSurface.hpp54
-rw-r--r--src/desktop/Rule.cpp22
-rw-r--r--src/desktop/Rule.hpp21
-rw-r--r--src/desktop/Window.cpp418
-rw-r--r--src/desktop/Window.hpp129
-rw-r--r--src/desktop/WindowOverridableVar.hpp132
-rw-r--r--src/desktop/WindowRule.cpp99
-rw-r--r--src/desktop/WindowRule.hpp76
-rw-r--r--src/desktop/Workspace.cpp2
-rw-r--r--src/desktop/rule/Engine.cpp56
-rw-r--r--src/desktop/rule/Engine.hpp24
-rw-r--r--src/desktop/rule/Rule.cpp149
-rw-r--r--src/desktop/rule/Rule.hpp84
-rw-r--r--src/desktop/rule/effect/EffectContainer.hpp81
-rw-r--r--src/desktop/rule/layerRule/LayerRule.cpp43
-rw-r--r--src/desktop/rule/layerRule/LayerRule.hpp25
-rw-r--r--src/desktop/rule/layerRule/LayerRuleApplicator.cpp128
-rw-r--r--src/desktop/rule/layerRule/LayerRuleApplicator.hpp60
-rw-r--r--src/desktop/rule/layerRule/LayerRuleEffectContainer.cpp33
-rw-r--r--src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp33
-rw-r--r--src/desktop/rule/matchEngine/BoolMatchEngine.cpp12
-rw-r--r--src/desktop/rule/matchEngine/BoolMatchEngine.hpp16
-rw-r--r--src/desktop/rule/matchEngine/IntMatchEngine.cpp14
-rw-r--r--src/desktop/rule/matchEngine/IntMatchEngine.hpp16
-rw-r--r--src/desktop/rule/matchEngine/MatchEngine.cpp23
-rw-r--r--src/desktop/rule/matchEngine/MatchEngine.hpp28
-rw-r--r--src/desktop/rule/matchEngine/RegexMatchEngine.cpp17
-rw-r--r--src/desktop/rule/matchEngine/RegexMatchEngine.hpp23
-rw-r--r--src/desktop/rule/matchEngine/TagMatchEngine.cpp12
-rw-r--r--src/desktop/rule/matchEngine/TagMatchEngine.hpp16
-rw-r--r--src/desktop/rule/matchEngine/WorkspaceMatchEngine.cpp12
-rw-r--r--src/desktop/rule/matchEngine/WorkspaceMatchEngine.hpp16
-rw-r--r--src/desktop/rule/utils/SetUtils.hpp17
-rw-r--r--src/desktop/rule/windowRule/WindowRule.cpp186
-rw-r--r--src/desktop/rule/windowRule/WindowRule.hpp37
-rw-r--r--src/desktop/rule/windowRule/WindowRuleApplicator.cpp642
-rw-r--r--src/desktop/rule/windowRule/WindowRuleApplicator.hpp148
-rw-r--r--src/desktop/rule/windowRule/WindowRuleEffectContainer.cpp76
-rw-r--r--src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp79
-rw-r--r--src/desktop/types/OverridableVar.hpp153
-rw-r--r--src/events/Windows.cpp401
-rw-r--r--src/helpers/MiscFunctions.cpp10
-rw-r--r--src/helpers/MiscFunctions.hpp1
-rw-r--r--src/helpers/Monitor.cpp6
-rw-r--r--src/helpers/TagKeeper.cpp6
-rw-r--r--src/helpers/TagKeeper.hpp4
-rw-r--r--src/helpers/math/Expression.cpp22
-rw-r--r--src/helpers/math/Expression.hpp28
-rw-r--r--src/helpers/varlist/VarList.hpp1
-rw-r--r--src/layout/DwindleLayout.cpp24
-rw-r--r--src/layout/IHyprLayout.cpp63
-rw-r--r--src/layout/MasterLayout.cpp20
-rw-r--r--src/managers/KeybindManager.cpp230
-rw-r--r--src/managers/KeybindManager.hpp2
-rw-r--r--src/managers/animation/AnimationManager.cpp4
-rw-r--r--src/managers/animation/DesktopAnimationManager.cpp6
-rw-r--r--src/managers/input/IdleInhibitor.cpp6
-rw-r--r--src/managers/input/InputManager.cpp2
-rw-r--r--src/managers/input/trackpad/gestures/CloseGesture.cpp2
-rw-r--r--src/protocols/Screencopy.cpp6
-rw-r--r--src/protocols/ShortcutsInhibit.cpp2
-rw-r--r--src/protocols/ToplevelExport.cpp4
-rw-r--r--src/protocols/XDGDialog.cpp2
-rw-r--r--src/protocols/XDGTag.cpp3
-rw-r--r--src/protocols/types/ContentType.cpp10
-rw-r--r--src/protocols/types/ContentType.hpp1
-rw-r--r--src/render/OpenGL.cpp16
-rw-r--r--src/render/Renderer.cpp36
-rw-r--r--src/render/decorations/CHyprBorderDecoration.cpp3
-rw-r--r--src/render/decorations/CHyprDropShadowDecoration.cpp4
-rw-r--r--src/render/decorations/CHyprGroupBarDecoration.cpp2
93 files changed, 3577 insertions, 2258 deletions
diff --git a/.github/actions/setup_base/action.yml b/.github/actions/setup_base/action.yml
index d7b52a79..665d7f07 100644
--- a/.github/actions/setup_base/action.yml
+++ b/.github/actions/setup_base/action.yml
@@ -45,6 +45,7 @@ runs:
libxkbfile \
lld \
meson \
+ muparser \
ninja \
pango \
pixman \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 014f386a..ee0c34a3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -233,7 +233,8 @@ pkg_check_modules(
libinput>=1.28
gbm
gio-2.0
- re2)
+ re2
+ muparser)
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
diff --git a/example/hyprland.conf b/example/hyprland.conf
index a1408dc3..1bccaa2a 100644
--- a/example/hyprland.conf
+++ b/example/hyprland.conf
@@ -159,10 +159,23 @@ animations {
# uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
-# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
-# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
-# windowrule = bordersize 0, floating:0, onworkspace:f[1]
-# windowrule = rounding 0, floating:0, onworkspace:f[1]
+# windowrule {
+# name = smart-gaps-1
+# floating = false
+# on_workspace = n[s:window] w[tv1]
+#
+# border_size = 0
+# rounding = 0
+# }
+#
+# windowrule {
+# name = smart-gaps-2
+# floating = false
+# on_workspace = n[s:window] f[1]
+#
+# border_size = 0
+# rounding = 0
+# }
# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more
dwindle {
@@ -294,11 +307,25 @@ bindl = , XF86AudioPrev, exec, playerctl previous
# See https://wiki.hypr.land/Configuring/Window-Rules/ for more
# See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules
-# Example windowrule
-# windowrule = float,class:^(kitty)$,title:^(kitty)$
+# Example windowrules that are useful
-# Ignore maximize requests from apps. You'll probably like this.
-windowrule = suppressevent maximize, class:.*
+windowrule {
+ # Ignore maximize requests from all apps. You'll probably like this.
+ name = suppress-maximize-events
+ match:class = .*
-# Fix some dragging issues with XWayland
-windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
+ suppress_event = maximize
+}
+
+windowrule {
+ # Fix some dragging issues with XWayland
+ match:name = fix-xwayland-drags
+ match:class = ^$
+ match:title = ^$
+ match:xwayland = true
+ match:float = true
+ match:fullscreen = false
+ match:pin = false
+
+ no_focus = true
+}
diff --git a/hyprtester/plugin/src/main.cpp b/hyprtester/plugin/src/main.cpp
index 1d0b68dc..72120eac 100644
--- a/hyprtester/plugin/src/main.cpp
+++ b/hyprtester/plugin/src/main.cpp
@@ -11,6 +11,8 @@
#include <src/managers/input/InputManager.hpp>
#include <src/managers/PointerManager.hpp>
#include <src/managers/input/trackpad/TrackpadGestures.hpp>
+#include <src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
+#include <src/desktop/rule/windowRule/WindowRuleApplicator.hpp>
#include <src/Compositor.hpp>
#undef private
@@ -245,6 +247,30 @@ static SDispatchResult keybind(std::string in) {
return {};
}
+static Desktop::Rule::CWindowRuleEffectContainer::storageType ruleIDX = 0;
+
+//
+static SDispatchResult addRule(std::string in) {
+ ruleIDX = Desktop::Rule::windowEffects()->registerEffect("plugin_rule");
+
+ if (Desktop::Rule::windowEffects()->registerEffect("plugin_rule") != ruleIDX)
+ return {.success = false, .error = "re-registering returned a different id?"};
+ return {};
+}
+
+static SDispatchResult checkRule(std::string in) {
+ if (!g_pCompositor->m_lastWindow)
+ return {.success = false, .error = "No window"};
+
+ if (!g_pCompositor->m_lastWindow->m_ruleApplicator->m_otherProps.props.contains(ruleIDX))
+ return {.success = false, .error = "No rule"};
+
+ if (g_pCompositor->m_lastWindow->m_ruleApplicator->m_otherProps.props[ruleIDX]->effect != "effect")
+ return {.success = false, .error = "Effect isn't \"effect\""};
+
+ return {};
+}
+
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
PHANDLE = handle;
@@ -255,6 +281,8 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:gesture", ::simulateGesture);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind);
+ HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_rule", ::addRule);
+ HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_rule", ::checkRule);
// init mouse
g_mouse = CTestMouse::create(false);
diff --git a/hyprtester/src/tests/clients/pointer-scroll.cpp b/hyprtester/src/tests/clients/pointer-scroll.cpp
index e1ba237f..2ea93a14 100644
--- a/hyprtester/src/tests/clients/pointer-scroll.cpp
+++ b/hyprtester/src/tests/clients/pointer-scroll.cpp
@@ -64,7 +64,7 @@ static bool startClient(SClient& client) {
// wait for window to appear
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
- if (getFromSocket(std::format("/dispatch setprop pid:{} noanim 1", client.proc->pid())) != "ok") {
+ if (getFromSocket(std::format("/dispatch setprop pid:{} no_anim 1", client.proc->pid())) != "ok") {
NLog::log("{}Failed to disable animations for client window", Colors::RED, ret);
return false;
}
@@ -130,7 +130,7 @@ static bool test() {
EXPECT(sendScroll(10), true);
EXPECT(getLastDelta(client), 30);
- EXPECT(getFromSocket("r/dispatch setprop active scrollmouse 4"), "ok");
+ EXPECT(getFromSocket("r/dispatch setprop active scroll_mouse 4"), "ok");
EXPECT(sendScroll(10), true);
EXPECT(getLastDelta(client), 40);
diff --git a/hyprtester/src/tests/clients/pointer-warp.cpp b/hyprtester/src/tests/clients/pointer-warp.cpp
index f37b94c3..bb03afd2 100644
--- a/hyprtester/src/tests/clients/pointer-warp.cpp
+++ b/hyprtester/src/tests/clients/pointer-warp.cpp
@@ -64,7 +64,7 @@ static bool startClient(SClient& client) {
// wait for window to appear
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
- if (getFromSocket(std::format("/dispatch setprop pid:{} noanim 1", client.proc->pid())) != "ok") {
+ if (getFromSocket(std::format("/dispatch setprop pid:{} no_anim 1", client.proc->pid())) != "ok") {
NLog::log("{}Failed to disable animations for client window", Colors::RED, ret);
return false;
}
diff --git a/hyprtester/src/tests/main/hyprctl.cpp b/hyprtester/src/tests/main/hyprctl.cpp
index 95f7caae..e8759d28 100644
--- a/hyprtester/src/tests/main/hyprctl.cpp
+++ b/hyprtester/src/tests/main/hyprctl.cpp
@@ -56,82 +56,82 @@ static bool testGetprop() {
return false;
}
- // animationstyle
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle"), "(unset)");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle -j"), R"({"animationstyle": ""})");
- getFromSocket("/dispatch setprop class:kitty animationstyle teststyle");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle"), "teststyle");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle -j"), R"({"animationstyle": "teststyle"})");
-
- // maxsize
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize"), "inf inf");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize -j"), R"({"maxsize": [null,null]})");
- getFromSocket("/dispatch setprop class:kitty maxsize 200 150");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize"), "200 150");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize -j"), R"({"maxsize": [200,150]})");
-
- // minsize
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize"), "20 20");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize -j"), R"({"minsize": [20,20]})");
- getFromSocket("/dispatch setprop class:kitty minsize 100 50");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize"), "100 50");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize -j"), R"({"minsize": [100,50]})");
-
- // alpha
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha"), "1");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha -j"), R"({"alpha": 1})");
- getFromSocket("/dispatch setprop class:kitty alpha 0.3");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha"), "0.3");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha -j"), R"({"alpha": 0.3})");
-
- // alphainactive
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive"), "1");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive -j"), R"({"alphainactive": 1})");
- getFromSocket("/dispatch setprop class:kitty alphainactive 0.5");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive"), "0.5");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive -j"), R"({"alphainactive": 0.5})");
-
- // alphafullscreen
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen"), "1");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen -j"), R"({"alphafullscreen": 1})");
- getFromSocket("/dispatch setprop class:kitty alphafullscreen 0.75");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen"), "0.75");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen -j"), R"({"alphafullscreen": 0.75})");
-
- // alphaoverride
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride"), "false");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride -j"), R"({"alphaoverride": false})");
- getFromSocket("/dispatch setprop class:kitty alphaoverride true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride"), "true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride -j"), R"({"alphaoverride": true})");
-
- // alphainactiveoverride
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride"), "false");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride -j"), R"({"alphainactiveoverride": false})");
- getFromSocket("/dispatch setprop class:kitty alphainactiveoverride true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride"), "true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride -j"), R"({"alphainactiveoverride": true})");
-
- // alphafullscreenoverride
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride"), "false");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride -j"), R"({"alphafullscreenoverride": false})");
- getFromSocket("/dispatch setprop class:kitty alphafullscreenoverride true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride"), "true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride -j"), R"({"alphafullscreenoverride": true})");
-
- // activebordercolor
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor"), "ee33ccff ee00ff99 45deg");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor -j"), R"({"activebordercolor": "ee33ccff ee00ff99 45deg"})");
- getFromSocket("/dispatch setprop class:kitty activebordercolor rgb(abcdef)");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor"), "ffabcdef 0deg");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor -j"), R"({"activebordercolor": "ffabcdef 0deg"})");
+ // animation
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation"), "(unset)");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation -j"), R"({"animation": ""})");
+ getFromSocket("/dispatch setprop class:kitty animation teststyle");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation"), "teststyle");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation -j"), R"({"animation": "teststyle"})");
+
+ // max_size
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size"), "inf inf");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size -j"), R"({"max_size": [null,null]})");
+ getFromSocket("/dispatch setprop class:kitty max_size 200 150");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size"), "200 150");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size -j"), R"({"max_size": [200,150]})");
+
+ // min_size
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size"), "20 20");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size -j"), R"({"min_size": [20,20]})");
+ getFromSocket("/dispatch setprop class:kitty min_size 100 50");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size"), "100 50");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size -j"), R"({"min_size": [100,50]})");
+
+ // opacity
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity"), "1");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity -j"), R"({"opacity": 1})");
+ getFromSocket("/dispatch setprop class:kitty opacity 0.3");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity"), "0.3");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity -j"), R"({"opacity": 0.3})");
+
+ // opacity_inactive
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive"), "1");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive -j"), R"({"opacity_inactive": 1})");
+ getFromSocket("/dispatch setprop class:kitty opacity_inactive 0.5");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive"), "0.5");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive -j"), R"({"opacity_inactive": 0.5})");
+
+ // opacity_fullscreen
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen"), "1");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen -j"), R"({"opacity_fullscreen": 1})");
+ getFromSocket("/dispatch setprop class:kitty opacity_fullscreen 0.75");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen"), "0.75");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen -j"), R"({"opacity_fullscreen": 0.75})");
+
+ // opacity_override
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override"), "false");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override -j"), R"({"opacity_override": false})");
+ getFromSocket("/dispatch setprop class:kitty opacity_override true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override"), "true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override -j"), R"({"opacity_override": true})");
+
+ // opacity_inactive_override
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override"), "false");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override -j"), R"({"opacity_inactive_override": false})");
+ getFromSocket("/dispatch setprop class:kitty opacity_inactive_override true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override"), "true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override -j"), R"({"opacity_inactive_override": true})");
+
+ // opacity_fullscreen_override
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override"), "false");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override -j"), R"({"opacity_fullscreen_override": false})");
+ getFromSocket("/dispatch setprop class:kitty opacity_fullscreen_override true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override"), "true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override -j"), R"({"opacity_fullscreen_override": true})");
+
+ // active_border_color
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color"), "ee33ccff ee00ff99 45deg");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color -j"), R"({"active_border_color": "ee33ccff ee00ff99 45deg"})");
+ getFromSocket("/dispatch setprop class:kitty active_border_color rgb(abcdef)");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color"), "ffabcdef 0deg");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color -j"), R"({"active_border_color": "ffabcdef 0deg"})");
// bool window properties
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput"), "false");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput -j"), R"({"allowsinput": false})");
- getFromSocket("/dispatch setprop class:kitty allowsinput true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput"), "true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput -j"), R"({"allowsinput": true})");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input"), "false");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input -j"), R"({"allows_input": false})");
+ getFromSocket("/dispatch setprop class:kitty allows_input true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input"), "true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input -j"), R"({"allows_input": true})");
// int window properties
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding"), "10");
@@ -141,16 +141,16 @@ static bool testGetprop() {
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding -j"), R"({"rounding": 4})");
// float window properties
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower"), "2");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower -j"), R"({"roundingpower": 2})");
- getFromSocket("/dispatch setprop class:kitty roundingpower 1.25");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower"), "1.25");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower -j"), R"({"roundingpower": 1.25})");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power"), "2");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power -j"), R"({"rounding_power": 2})");
+ getFromSocket("/dispatch setprop class:kitty rounding_power 1.25");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power"), "1.25");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power -j"), R"({"rounding_power": 1.25})");
// errors
EXPECT(getCommandStdOut("hyprctl getprop"), "not enough args");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty"), "not enough args");
- EXPECT(getCommandStdOut("hyprctl getprop class:nonexistantclass animationstyle"), "window not found");
+ EXPECT(getCommandStdOut("hyprctl getprop class:nonexistantclass animation"), "window not found");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty nonexistantprop"), "prop not found");
// kill all
diff --git a/hyprtester/src/tests/main/tags.cpp b/hyprtester/src/tests/main/tags.cpp
index 22bedcde..c345fe71 100644
--- a/hyprtester/src/tests/main/tags.cpp
+++ b/hyprtester/src/tests/main/tags.cpp
@@ -21,21 +21,24 @@ static bool testTags() {
NLog::log("{}Testing testTag tags", Colors::YELLOW);
- OK(getFromSocket("/keyword windowrule tag +testTag, class:tagged"));
- OK(getFromSocket("/keyword windowrule noshadow, tag:negative:testTag"));
- OK(getFromSocket("/keyword windowrule noborder, tag:testTag"));
+ OK(getFromSocket("/keyword windowrule[tag-test-1]:tag +testTag"));
+ OK(getFromSocket("/keyword windowrule[tag-test-1]:match:class tagged"));
+ OK(getFromSocket("/keyword windowrule[tag-test-2]:match:tag negative:testTag"));
+ OK(getFromSocket("/keyword windowrule[tag-test-2]:no_shadow true"));
+ OK(getFromSocket("/keyword windowrule[tag-test-3]:match:tag testTag"));
+ OK(getFromSocket("/keyword windowrule[tag-test-3]:no_dim true"));
EXPECT(Tests::windowCount(), 2);
OK(getFromSocket("/dispatch focuswindow class:tagged"));
- NLog::log("{}Testing tagged window for noborder & noshadow", Colors::YELLOW);
+ NLog::log("{}Testing tagged window for no_dim 0 & no_shadow", Colors::YELLOW);
EXPECT_CONTAINS(getFromSocket("/activewindow"), "testTag");
- EXPECT_CONTAINS(getFromSocket("/getprop activewindow noborder"), "true");
- EXPECT_CONTAINS(getFromSocket("/getprop activewindow noshadow"), "false");
- NLog::log("{}Testing untagged window for noborder & noshadow", Colors::YELLOW);
+ EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_dim"), "true");
+ EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_shadow"), "false");
+ NLog::log("{}Testing untagged window for no_dim & no_shadow", Colors::YELLOW);
OK(getFromSocket("/dispatch focuswindow class:untagged"));
EXPECT_NOT_CONTAINS(getFromSocket("/activewindow"), "testTag");
- EXPECT_CONTAINS(getFromSocket("/getprop activewindow noborder"), "false");
- EXPECT_CONTAINS(getFromSocket("/getprop activewindow noshadow"), "true");
+ EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_shadow"), "true");
+ EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_dim"), "false");
Tests::killAllWindows();
EXPECT(Tests::windowCount(), 0);
diff --git a/hyprtester/src/tests/main/window.cpp b/hyprtester/src/tests/main/window.cpp
index 6cfa061c..2cf42eef 100644
--- a/hyprtester/src/tests/main/window.cpp
+++ b/hyprtester/src/tests/main/window.cpp
@@ -246,12 +246,15 @@ static bool test() {
testSwapWindow();
+ getFromSocket("/dispatch workspace 1");
+
NLog::log("{}Testing minsize/maxsize rules for tiled windows", Colors::YELLOW);
{
// Enable the config for testing, test max/minsize for tiled windows and centering
OK(getFromSocket("/keyword misc:size_limits_tiled 1"));
- OK(getFromSocket("/keyword windowrule maxsize 1500 500, class:kitty_maxsize"));
- OK(getFromSocket("/keyword windowrule minsize 1200 500, class:kitty_maxsize"));
+ OK(getFromSocket("/keyword windowrule[kitty-max-rule]:match:class kitty_maxsize"));
+ OK(getFromSocket("/keyword windowrule[kitty-max-rule]:max_size 1500 500"));
+ OK(getFromSocket("r/keyword windowrule[kitty-max-rule]:min_size 1200 500"));
if (!spawnKitty("kitty_maxsize"))
return false;
@@ -297,29 +300,127 @@ 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");
+ OK(getFromSocket("/keyword windowrule[wr-kitty-stuff]:opacity 0.5 0.5 override"));
+
+ {
+ auto str = getFromSocket("/getprop active opacity");
+ EXPECT_CONTAINS(str, "0.5");
+ }
+
+ OK(getFromSocket("/keyword windowrule[special-magic-kitty]:match:class magic_kitty"));
+ OK(getFromSocket("/keyword windowrule[special-magic-kitty]:workspace special:magic"));
+
+ if (!spawnKitty("magic_kitty"))
+ return false;
+
+ {
+ auto str = getFromSocket("/activewindow");
+ EXPECT_CONTAINS(str, "special:magic");
EXPECT_NOT_CONTAINS(str, "workspace: 9");
}
- NLog::log("{}Testing faulty rules", Colors::YELLOW);
+ if (auto str = getFromSocket("/monitors"); str.contains("magic)")) {
+ OK(getFromSocket("/dispatch togglespecialworkspace magic"));
+ }
+
+ Tests::killAllWindows();
+
+ if (!spawnKitty("tag_kitty"))
+ return false;
+
+ {
+ auto str = getFromSocket("/activewindow");
+ EXPECT_CONTAINS(str, "floating: 1");
+ }
+
+ OK(getFromSocket("/reload"));
+ Tests::killAllWindows();
+
+ // test rules that overlap effects but don't overlap props
+ OK(getFromSocket("/keyword windowrule match:class overlap_kitty, border_size 0"));
+ OK(getFromSocket("/keyword windowrule match:fullscreen false, border_size 10"));
+
+ if (!spawnKitty("overlap_kitty"))
+ return false;
+
+ {
+ auto str = getFromSocket("/getprop active border_size");
+ EXPECT_CONTAINS(str, "10");
+ }
+
+ OK(getFromSocket("/reload"));
+ Tests::killAllWindows();
+
+ OK(getFromSocket("/keyword general:border_size 0"));
+ OK(getFromSocket("/keyword windowrule match:float true, border_size 10"));
+
+ if (!spawnKitty("border_kitty"))
+ return false;
+
+ {
+ auto str = getFromSocket("/getprop active border_size");
+ EXPECT_CONTAINS(str, "0");
+ }
+
+ OK(getFromSocket("/dispatch togglefloating"));
+
+ {
+ auto str = getFromSocket("/getprop active border_size");
+ EXPECT_CONTAINS(str, "10");
+ }
+
+ OK(getFromSocket("/dispatch togglefloating"));
+
+ {
+ auto str = getFromSocket("/getprop active border_size");
+ EXPECT_CONTAINS(str, "0");
+ }
+
+ OK(getFromSocket("/reload"));
+ Tests::killAllWindows();
+
+ // test expression rules
+ OK(getFromSocket("/keyword windowrule match:class expr_kitty, float yes, size monitor_w*0.5 monitor_h*0.5, move 20+(monitor_w*0.1) monitor_h*0.5"));
+
+ if (!spawnKitty("expr_kitty"))
+ return false;
+
{
- 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)
+ auto str = getFromSocket("/activewindow");
+ EXPECT_CONTAINS(str, "floating: 1");
+ EXPECT_CONTAINS(str, "at: 212,540");
+ EXPECT_CONTAINS(str, "size: 960,540");
}
+ OK(getFromSocket("/reload"));
+ Tests::killAllWindows();
+
+ OK(getFromSocket("/dispatch plugin:test:add_rule"));
+ OK(getFromSocket("/reload"));
+
+ OK(getFromSocket("/keyword windowrule match:class plugin_kitty, plugin_rule effect"));
+
+ if (!spawnKitty("plugin_kitty"))
+ return false;
+
+ OK(getFromSocket("/dispatch plugin:test:check_rule"));
+
+ OK(getFromSocket("/reload"));
+ Tests::killAllWindows();
+
+ OK(getFromSocket("/dispatch plugin:test:add_rule"));
+ OK(getFromSocket("/reload"));
+
+ OK(getFromSocket("/keyword windowrule[test-plugin-rule]:match:class plugin_kitty"));
+ OK(getFromSocket("/keyword windowrule[test-plugin-rule]:plugin_rule effect"));
+
+ if (!spawnKitty("plugin_kitty"))
+ return false;
+
+ OK(getFromSocket("/dispatch plugin:test:check_rule"));
+
NLog::log("{}Reloading config", Colors::YELLOW);
OK(getFromSocket("/reload"));
diff --git a/hyprtester/src/tests/main/workspaces.cpp b/hyprtester/src/tests/main/workspaces.cpp
index def35d08..9d380281 100644
--- a/hyprtester/src/tests/main/workspaces.cpp
+++ b/hyprtester/src/tests/main/workspaces.cpp
@@ -27,7 +27,7 @@ static bool test() {
// test on workspace "window"
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
- OK(getFromSocket("/dispatch workspace 1"));
+ getFromSocket("/dispatch workspace 1");
NLog::log("{}Checking persistent no-mon", Colors::YELLOW);
OK(getFromSocket("r/keyword workspace 966,persistent:1"));
diff --git a/hyprtester/test.conf b/hyprtester/test.conf
index 047001c2..ac28bc5a 100644
--- a/hyprtester/test.conf
+++ b/hyprtester/test.conf
@@ -318,28 +318,70 @@ submap = reset
### WINDOWS AND WORKSPACES ###
##############################
-# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
-# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
+windowrule {
+ # Ignore maximize requests from apps. You'll probably like this.
+ name = suppress-maximize-events
+ match:class = .*
-# Example windowrule v1
-# windowrule = float, ^(kitty)$
-
-# Example windowrule v2
-# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
-
-# Ignore maximize requests from apps. You'll probably like this.
-windowrulev2 = suppressevent maximize, class:.*
+ suppress_event = maximize
+}
-# Fix some dragging issues with XWayland
-windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
+windowrule {
+ # Fix some dragging issues with XWayland
+ name = fix-xwayland-drags
+ match:class = ^$
+ match:title = ^$
+ match:xwayland = true
+ match:float = true
+ match:fullscreen = false
+ match:pin = false
+
+ no_focus = true
+}
-# Workspace "windows" is a smart gaps one
workspace = n[s:window] w[tv1], gapsout:0, gapsin:0
workspace = n[s:window] f[1], gapsout:0, gapsin:0
-windowrulev2 = bordersize 0, floating:0, onworkspace:n[s:window] w[tv1]
-windowrulev2 = rounding 0, floating:0, onworkspace:n[s:window] w[tv1]
-windowrulev2 = bordersize 0, floating:0, onworkspace:n[s:window] f[1]
-windowrulev2 = rounding 0, floating:0, onworkspace:n[s:window] f[1]
+
+windowrule {
+ name = smart-gaps-1
+ match:float = false
+ match:workspace = n[s:window] w[tv1]
+
+ border_size = 0
+ rounding = 0
+}
+
+windowrule {
+ name = smart-gaps-2
+ match:float = false
+ match:workspace = n[s:window] f[1]
+
+ border_size = 0
+ rounding = 0
+}
+
+windowrule {
+ name = wr-kitty-stuff
+ match:class = wr_kitty
+
+ float = true
+ size = 200 200
+ pin = false
+}
+
+windowrule {
+ name = tagged-kitty-floats
+ match:tag = tag_kitty
+
+ float = true
+}
+
+windowrule {
+ name = static-kitty-tag
+ match:class = tag_kitty
+
+ tag = +tag_kitty
+}
gesture = 3, left, dispatcher, exec, kitty
gesture = 3, right, float
@@ -356,7 +398,3 @@ gesture = 5, left, dispatcher, sendshortcut, , i, activewindow
gesture = 5, right, dispatcher, sendshortcut, , t, activewindow
gesture = 4, right, dispatcher, sendshortcut, , return, activewindow
gesture = 4, left, dispatcher, movecursortocorner, 1
-
-windowrule = float, pin, class:wr_kitty
-windowrule = size 200 200, class:wr_kitty
-windowrule = unset pin, class:wr_kitty
diff --git a/nix/default.nix b/nix/default.nix
index 867b5b0c..45fd273b 100644
--- a/nix/default.nix
+++ b/nix/default.nix
@@ -26,6 +26,7 @@
libxkbcommon,
libuuid,
libgbm,
+ muparser,
pango,
pciutils,
re2,
@@ -149,6 +150,7 @@ in
libuuid
libxkbcommon
libgbm
+ muparser
pango
pciutils
re2
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index a7f26ba6..0f24a8bf 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -901,8 +901,8 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper
if (ONLY_PRIORITY && !w->priorityFocus())
continue;
- if (w->m_isFloating && w->m_isMapped && !w->isHidden() && !w->m_X11ShouldntFocus && w->m_pinned && !w->m_windowData.noFocus.valueOrDefault() && w != pIgnoreWindow &&
- !isShadowedByModal(w)) {
+ if (w->m_isFloating && w->m_isMapped && !w->isHidden() && !w->m_X11ShouldntFocus && w->m_pinned && !w->m_ruleApplicator->noFocus().valueOrDefault() &&
+ w != pIgnoreWindow && !isShadowedByModal(w)) {
const auto BB = w->getWindowBoxUnified(properties);
CBox box = BB.copy().expand(!w->isX11OverrideRedirect() ? BORDER_GRAB_AREA : 0);
if (box.containsPoint(g_pPointerManager->position()))
@@ -939,7 +939,7 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper
continue;
}
- if (w->m_isFloating && w->m_isMapped && w->m_workspace->isVisible() && !w->isHidden() && !w->m_pinned && !w->m_windowData.noFocus.valueOrDefault() &&
+ if (w->m_isFloating && w->m_isMapped && w->m_workspace->isVisible() && !w->isHidden() && !w->m_pinned && !w->m_ruleApplicator->noFocus().valueOrDefault() &&
w != pIgnoreWindow && (!aboveFullscreen || w->m_createdOverFullscreen) && !isShadowedByModal(w)) {
// OR windows should add focus to parent
if (w->m_X11ShouldntFocus && !w->isX11OverrideRedirect())
@@ -1000,7 +1000,7 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper
continue;
if (!w->m_isX11 && !w->m_isFloating && w->m_isMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_X11ShouldntFocus &&
- !w->m_windowData.noFocus.valueOrDefault() && w != pIgnoreWindow && !isShadowedByModal(w)) {
+ !w->m_ruleApplicator->noFocus().valueOrDefault() && w != pIgnoreWindow && !isShadowedByModal(w)) {
if (w->hasPopupAt(pos))
return w;
}
@@ -1016,7 +1016,7 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper
if (!w->m_workspace)
continue;
- if (!w->m_isFloating && w->m_isMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_X11ShouldntFocus && !w->m_windowData.noFocus.valueOrDefault() &&
+ if (!w->m_isFloating && w->m_isMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_X11ShouldntFocus && !w->m_ruleApplicator->noFocus().valueOrDefault() &&
w != pIgnoreWindow && !isShadowedByModal(w)) {
CBox box = (properties & USE_PROP_TILED) ? w->getWindowBoxUnified(properties) : CBox{w->m_position, w->m_size};
if (box.containsPoint(pos))
@@ -1152,7 +1152,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface
m_lastWindow.reset();
if (PLASTWINDOW && PLASTWINDOW->m_isMapped) {
- updateWindowAnimatedDecorationValues(PLASTWINDOW);
+ PLASTWINDOW->updateDecorationValues();
g_pXWaylandManager->activateWindow(PLASTWINDOW, false);
}
@@ -1172,7 +1172,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface
return;
}
- if (pWindow->m_windowData.noFocus.valueOrDefault()) {
+ if (pWindow->m_ruleApplicator->noFocus().valueOrDefault()) {
Debug::log(LOG, "Ignoring focus to nofocus window!");
return;
}
@@ -1209,9 +1209,9 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface
// we need to make the PLASTWINDOW not equal to m_pLastWindow so that RENDERDATA is correct for an unfocused window
if (PLASTWINDOW && PLASTWINDOW->m_isMapped) {
- PLASTWINDOW->updateDynamicRules();
+ PLASTWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FOCUS);
- updateWindowAnimatedDecorationValues(PLASTWINDOW);
+ PLASTWINDOW->updateDecorationValues();
if (!pWindow->m_isX11 || !pWindow->isX11OverrideRedirect())
g_pXWaylandManager->activateWindow(PLASTWINDOW, false);
@@ -1225,10 +1225,10 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface
g_pXWaylandManager->activateWindow(pWindow, true); // sets the m_pLastWindow
- pWindow->updateDynamicRules();
+ pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FOCUS);
pWindow->onFocusAnimUpdate();
- updateWindowAnimatedDecorationValues(pWindow);
+ pWindow->updateDecorationValues();
if (pWindow->m_isUrgent)
pWindow->m_isUrgent = false;
@@ -1334,7 +1334,7 @@ SP<CWLSurfaceResource> CCompositor::vectorToLayerSurface(const Vector2D& pos, st
for (auto const& ls : *layerSurfaces | std::views::reverse) {
if (!ls->m_mapped || ls->m_fadingOut || !ls->m_layerSurface || (ls->m_layerSurface && !ls->m_layerSurface->m_surface->m_mapped) || ls->m_alpha->value() == 0.f ||
- (aboveLockscreen && (!ls->m_aboveLockscreen || !ls->m_aboveLockscreenInteractable)))
+ (aboveLockscreen && ls->m_ruleApplicator->aboveLock().valueOrDefault() != 2))
continue;
auto [surf, local] = ls->m_layerSurface->m_surface->at(pos - ls->m_geometry.pos(), true);
@@ -1715,7 +1715,7 @@ static bool isFloatingMatches(WINDOWPTR w, std::optional<bool> floating) {
template <typename WINDOWPTR>
static bool isWindowAvailableForCycle(WINDOWPTR pWindow, WINDOWPTR w, bool focusableOnly, std::optional<bool> floating, bool anyWorkspace = false) {
return isFloatingMatches(w, floating) &&
- (w != pWindow && isWorkspaceMatches(pWindow, w, anyWorkspace) && w->m_isMapped && !w->isHidden() && (!focusableOnly || !w->m_windowData.noFocus.valueOrDefault()));
+ (w != pWindow && isWorkspaceMatches(pWindow, w, anyWorkspace) && w->m_isMapped && !w->isHidden() && (!focusableOnly || !w->m_ruleApplicator->noFocus().valueOrDefault()));
}
template <typename Iterator>
@@ -1906,101 +1906,8 @@ void CCompositor::updateAllWindowsAnimatedDecorationValues() {
if (!w->m_isMapped)
continue;
- updateWindowAnimatedDecorationValues(w);
- }
-}
-
-void CCompositor::updateWindowAnimatedDecorationValues(PHLWINDOW pWindow) {
- // optimization
- static auto PACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.active_border");
- static auto PINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.inactive_border");
- static auto PNOGROUPACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.nogroup_border_active");
- static auto PNOGROUPINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.nogroup_border");
- static auto PGROUPACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_active");
- static auto PGROUPINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_inactive");
- static auto PGROUPACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_active");
- static auto PGROUPINACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_inactive");
- static auto PINACTIVEALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:inactive_opacity");
- static auto PACTIVEALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:active_opacity");
- static auto PFULLSCREENALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:fullscreen_opacity");
- static auto PSHADOWCOL = CConfigValue<Hyprlang::INT>("decoration:shadow:color");
- static auto PSHADOWCOLINACTIVE = CConfigValue<Hyprlang::INT>("decoration:shadow:color_inactive");
- static auto PDIMSTRENGTH = CConfigValue<Hyprlang::FLOAT>("decoration:dim_strength");
- static auto PDIMENABLED = CConfigValue<Hyprlang::INT>("decoration:dim_inactive");
- static auto PDIMMODAL = CConfigValue<Hyprlang::INT>("decoration:dim_modal");
-
- auto* const ACTIVECOL = sc<CGradientValueData*>((PACTIVECOL.ptr())->getData());
- auto* const INACTIVECOL = sc<CGradientValueData*>((PINACTIVECOL.ptr())->getData());
- auto* const NOGROUPACTIVECOL = sc<CGradientValueData*>((PNOGROUPACTIVECOL.ptr())->getData());
- auto* const NOGROUPINACTIVECOL = sc<CGradientValueData*>((PNOGROUPINACTIVECOL.ptr())->getData());
- auto* const GROUPACTIVECOL = sc<CGradientValueData*>((PGROUPACTIVECOL.ptr())->getData());
- auto* const GROUPINACTIVECOL = sc<CGradientValueData*>((PGROUPINACTIVECOL.ptr())->getData());
- auto* const GROUPACTIVELOCKEDCOL = sc<CGradientValueData*>((PGROUPACTIVELOCKEDCOL.ptr())->getData());
- auto* const GROUPINACTIVELOCKEDCOL = sc<CGradientValueData*>((PGROUPINACTIVELOCKEDCOL.ptr())->getData());
-
- auto setBorderColor = [&](CGradientValueData grad) -> void {
- if (grad == pWindow->m_realBorderColor)
- return;
-
- pWindow->m_realBorderColorPrevious = pWindow->m_realBorderColor;
- pWindow->m_realBorderColor = grad;
- pWindow->m_borderFadeAnimationProgress->setValueAndWarp(0.f);
- *pWindow->m_borderFadeAnimationProgress = 1.f;
- };
-
- const bool IS_SHADOWED_BY_MODAL = pWindow->m_xdgSurface && pWindow->m_xdgSurface->m_toplevel && pWindow->m_xdgSurface->m_toplevel->anyChildModal();
-
- // border
- const auto RENDERDATA = g_pLayoutManager->getCurrentLayout()->requestRenderHints(pWindow);
- if (RENDERDATA.isBorderGradient)
- setBorderColor(*RENDERDATA.borderGradient);
- else {
- const bool GROUPLOCKED = pWindow->m_groupData.pNextWindow.lock() ? pWindow->getGroupHead()->m_groupData.locked : false;
- if (pWindow == m_lastWindow) {
- const auto* const ACTIVECOLOR =
- !pWindow->m_groupData.pNextWindow.lock() ? (!pWindow->m_groupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
- setBorderColor(pWindow->m_windowData.activeBorderColor.valueOr(*ACTIVECOLOR));
- } else {
- const auto* const INACTIVECOLOR = !pWindow->m_groupData.pNextWindow.lock() ? (!pWindow->m_groupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) :
- (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
- setBorderColor(pWindow->m_windowData.inactiveBorderColor.valueOr(*INACTIVECOLOR));
- }
+ w->updateDecorationValues();
}
-
- // opacity
- const auto PWORKSPACE = pWindow->m_workspace;
- if (pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) {
- *pWindow->m_activeInactiveAlpha = pWindow->m_windowData.alphaFullscreen.valueOrDefault().applyAlpha(*PFULLSCREENALPHA);
- } else {
- if (pWindow == m_lastWindow)
- *pWindow->m_activeInactiveAlpha = pWindow->m_windowData.alpha.valueOrDefault().applyAlpha(*PACTIVEALPHA);
- else
- *pWindow->m_activeInactiveAlpha = pWindow->m_windowData.alphaInactive.valueOrDefault().applyAlpha(*PINACTIVEALPHA);
- }
-
- // dim
- float goalDim = 1.F;
- if (pWindow == m_lastWindow.lock() || pWindow->m_windowData.noDim.valueOrDefault() || !*PDIMENABLED)
- goalDim = 0;
- else
- goalDim = *PDIMSTRENGTH;
-
- if (IS_SHADOWED_BY_MODAL && *PDIMMODAL)
- goalDim += (1.F - goalDim) / 2.F;
-
- *pWindow->m_dimPercent = goalDim;
-
- // shadow
- if (!pWindow->isX11OverrideRedirect() && !pWindow->m_X11DoesntWantBorders) {
- if (pWindow == m_lastWindow)
- *pWindow->m_realShadowColor = CHyprColor(*PSHADOWCOL);
- else
- *pWindow->m_realShadowColor = CHyprColor(*PSHADOWCOLINACTIVE != -1 ? *PSHADOWCOLINACTIVE : *PSHADOWCOL);
- } else {
- pWindow->m_realShadowColor->setValueAndWarp(CHyprColor(0, 0, 0, 0)); // no shadow
- }
-
- pWindow->updateWindowDecos();
}
MONITORID CCompositor::getNextAvailableMonitorID(std::string const& name) {
@@ -2341,14 +2248,14 @@ void CCompositor::changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, cons
}
void CCompositor::setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) {
- if (PWINDOW->m_windowData.syncFullscreen.valueOrDefault())
+ if (PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault())
setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = MODE});
else
setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = PWINDOW->m_fullscreenState.client});
}
void CCompositor::setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) {
- if (PWINDOW->m_windowData.syncFullscreen.valueOrDefault())
+ if (PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault())
setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = MODE});
else
setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = PWINDOW->m_fullscreenState.internal, .client = MODE});
@@ -2389,15 +2296,16 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, SFullscreenS
}
// TODO: update the state on syncFullscreen changes
- if (!CHANGEINTERNAL && PWINDOW->m_windowData.syncFullscreen.valueOrDefault())
+ if (!CHANGEINTERNAL && PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault())
return;
PWINDOW->m_fullscreenState.client = state.client;
g_pXWaylandManager->setWindowFullscreen(PWINDOW, state.client & FSMODE_FULLSCREEN);
if (!CHANGEINTERNAL) {
- PWINDOW->updateDynamicRules();
- updateWindowAnimatedDecorationValues(PWINDOW);
+ PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT |
+ Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
+ PWINDOW->updateDecorationValues();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID());
return;
}
@@ -2411,8 +2319,10 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, SFullscreenS
g_pEventManager->postEvent(SHyprIPCEvent{.event = "fullscreen", .data = std::to_string(sc<int>(EFFECTIVE_MODE) != FSMODE_NONE)});
EMIT_HOOK_EVENT("fullscreen", PWINDOW);
- PWINDOW->updateDynamicRules();
- updateWindowAnimatedDecorationValues(PWINDOW);
+ PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT |
+ Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
+
+ PWINDOW->updateDecorationValues();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID());
// make all windows on the same workspace under the fullscreen window
@@ -2552,7 +2462,7 @@ PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp_) {
}
case MODE_TAG_REGEX: {
bool tagMatched = false;
- for (auto const& t : w->m_tags.getTags()) {
+ for (auto const& t : w->m_ruleApplicator->m_tagKeeper.getTags()) {
if (RE2::FullMatch(t, regexCheck)) {
tagMatched = true;
break;
@@ -2843,7 +2753,7 @@ void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWor
}
pWindow->updateToplevel();
- pWindow->updateDynamicRules();
+ pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE);
pWindow->uncacheWindowDecos();
pWindow->updateGroupOutputs();
@@ -2874,7 +2784,7 @@ PHLWINDOW CCompositor::getForceFocus() {
if (!w->m_isMapped || w->isHidden() || !w->m_workspace || !w->m_workspace->isVisible())
continue;
- if (!w->m_stayFocused)
+ if (!w->m_ruleApplicator->stayFocused().valueOrDefault())
continue;
return w;
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index bf6401e5..30b0f1bd 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -129,7 +129,6 @@ class CCompositor {
PHLMONITOR getMonitorInDirection(const char&);
PHLMONITOR getMonitorInDirection(PHLMONITOR, const char&);
void updateAllWindowsAnimatedDecorationValues();
- void updateWindowAnimatedDecorationValues(PHLWINDOW);
MONITORID getNextAvailableMonitorID(std::string const& name);
void moveWorkspaceToMonitor(PHLWORKSPACE, PHLMONITOR, bool noWarpCursor = false);
void swapActiveWorkspaces(PHLMONITOR, PHLMONITOR);
diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp
index 5e503451..f819e293 100644
--- a/src/config/ConfigDescriptions.hpp
+++ b/src/config/ConfigDescriptions.hpp
@@ -16,12 +16,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.data = SConfigOptionDescription::SRangeData{1, 0, 20},
},
SConfigOptionDescription{
- .value = "general:no_border_on_floating",
- .description = "disable borders for floating windows",
- .type = CONFIG_OPTION_BOOL,
- .data = SConfigOptionDescription::SBoolData{false},
- },
- SConfigOptionDescription{
.value = "general:gaps_in",
.description = "gaps between windows\n\nsupports css style gaps (top, right, bottom, left -> 5 10 15 20)",
.type = CONFIG_OPTION_STRING_SHORT,
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<Hyprlang::CConfig>(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<std::string> 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<std::string> 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<std::string> CConfigManager::resetHLConfig() {
const auto RET = verifyConfigExists();
+ reloadRuleConfigs();
+
return RET;
}
@@ -1179,6 +1186,77 @@ Hyprlang::CParseResult CConfigManager::handleMonitorv2() {
return result;
}
+std::optional<std::string> 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<Hyprlang::INT>(ENABLED->getValue()) == 0)
+ return std::nullopt;
+
+ SP<Desktop::Rule::CWindowRule> rule = makeShared<Desktop::Rule::CWindowRule>(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<Hyprlang::STRING>(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<Hyprlang::STRING>(VAL->getValue()));
+ }
+
+ Desktop::Rule::ruleEngine()->registerRule(std::move(rule));
+ return std::nullopt;
+}
+
+std::optional<std::string> 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<Hyprlang::INT>(ENABLED->getValue()) != 0)
+ return std::nullopt;
+
+ SP<Desktop::Rule::CLayerRule> rule = makeShared<Desktop::Rule::CLayerRule>(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<Hyprlang::STRING>(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<Hyprlang::STRING>(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<Desktop::Rule::IRule>{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<SP<CWindowRule>> CConfigManager::getMatchingRules(PHLWINDOW pWindow, bool dynamic, bool shadowExec) {
- if (!valid(pWindow))
- return std::vector<SP<CWindowRule>>();
-
- // if the window is unmapped, don't process exec rules yet.
- shadowExec = shadowExec || !pWindow->m_isMapped;
-
- std::vector<SP<CWindowRule>> 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<eFullscreenMode> internalMode, clientMode;
-
- if (ARGS[0] == "*")
- internalMode = std::nullopt;
- else if (isNumber(ARGS[0]))
- internalMode = sc<eFullscreenMode>(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<eFullscreenMode>(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<uint64_t> PIDs = {sc<uint64_t>(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<CWindowRule>(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<SP<CLayerRule>> CConfigManager::getMatchingRules(PHLLS pLS) {
- std::vector<SP<CLayerRule>> 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<uintptr_t>(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<CLayerRule>(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<SWorkspaceRule>& 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<std::string> CConfigManager::handleUnbind(const std::string& comma
return {};
}
-std::optional<std::string> CConfigManager::handleWindowRule(const std::string& command, const std::string& value) {
- const auto VARLIST = CVarList(value, 0, ',', true);
-
- std::vector<std::string_view> tokens;
- std::unordered_map<std::string_view, std::string_view> 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<CWindowRule> 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<SP<CWindowRule>> 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<CWindowRule>(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<CWindowRule>(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<std::string> 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<CLayerRule>(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<uintptr_t>(ls.get())) == matchName)
- ls->m_forceBlur = forceBlur;
- } else if (ls->m_namespace == matchName)
- ls->m_forceBlur = forceBlur;
- }
- }
- }
-}
-
-std::optional<std::string> 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<std::string> 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<std::string> CConfigManager::handleGesture(const std::string& comm
return std::nullopt;
}
+std::optional<std::string> CConfigManager::handleWindowrule(const std::string& command, const std::string& value) {
+ CVarList2 data(std::string{value}, 0, ',');
+
+ SP<Desktop::Rule::CWindowRule> rule = makeShared<Desktop::Rule::CWindowRule>();
+
+ 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<Desktop::Rule::IRule>{m_keywordRules.back()});
+
+ return std::nullopt;
+}
+
+std::optional<std::string> CConfigManager::handleLayerrule(const std::string& command, const std::string& value) {
+ CVarList2 data(std::string{value}, 0, ',');
+
+ SP<Desktop::Rule::CLayerRule> rule = makeShared<Desktop::Rule::CLayerRule>();
+
+ 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<SConfigOptionDescription>& CConfigManager::getAllDescriptions() {
return CONFIG_OPTIONS;
}
diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp
index 7f32be41..599ee8e7 100644
--- a/src/config/ConfigManager.hpp
+++ b/src/config/ConfigManager.hpp
@@ -13,14 +13,12 @@
#include <xf86drmMode.h>
#include "../helpers/Monitor.hpp"
#include "../desktop/Window.hpp"
-#include "../desktop/LayerRule.hpp"
#include "ConfigDataValues.hpp"
#include "../SharedDefs.hpp"
#include "../helpers/Color.hpp"
#include "../desktop/DesktopTypes.hpp"
#include "../helpers/memory/Memory.hpp"
-#include "../desktop/WindowRule.hpp"
#include "../managers/XWaylandManager.hpp"
#include "../managers/KeybindManager.hpp"
@@ -68,11 +66,6 @@ struct SPluginVariable {
std::string name = "";
};
-struct SExecRequestedRule {
- std::string szRule = "";
- uint64_t iPid = 0;
-};
-
enum eConfigOptionType : uint8_t {
CONFIG_OPTION_BOOL = 0,
CONFIG_OPTION_INT = 1, /* e.g. 0/1/2*/
@@ -214,7 +207,6 @@ class CConfigManager {
bool deviceConfigExplicitlySet(const std::string&, const std::string&);
bool deviceConfigExists(const std::string&);
Hyprlang::CConfigValue* getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback);
- bool shouldBlurLS(const std::string&);
void* const* getConfigValuePtr(const std::string&);
Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = "");
@@ -229,8 +221,6 @@ class CConfigManager {
std::string getBoundMonitorStringForWS(const std::string&);
const std::vector<SWorkspaceRule>& getAllWorkspaceRules();
- std::vector<SP<CWindowRule>> getMatchingRules(PHLWINDOW, bool dynamic = true, bool shadowExec = false);
- std::vector<SP<CLayerRule>> getMatchingRules(PHLLS);
void ensurePersistentWorkspacesPresent();
const std::vector<SConfigOptionDescription>& getAllDescriptions();
@@ -260,8 +250,6 @@ class CConfigManager {
SP<Hyprutils::Animation::SAnimationPropertyConfig> getAnimationPropertyConfig(const std::string&);
- void addExecRule(const SExecRequestedRule&);
-
void handlePluginLoads();
std::string getErrors();
@@ -274,22 +262,24 @@ class CConfigManager {
std::optional<std::string> handleMonitor(const std::string&, const std::string&);
std::optional<std::string> handleBind(const std::string&, const std::string&);
std::optional<std::string> handleUnbind(const std::string&, const std::string&);
- std::optional<std::string> handleWindowRule(const std::string&, const std::string&);
- std::optional<std::string> handleLayerRule(const std::string&, const std::string&);
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
std::optional<std::string> handleBezier(const std::string&, const std::string&);
std::optional<std::string> handleAnimation(const std::string&, const std::string&);
std::optional<std::string> handleSource(const std::string&, const std::string&);
std::optional<std::string> handleSubmap(const std::string&, const std::string&);
- std::optional<std::string> handleBlurLS(const std::string&, const std::string&);
std::optional<std::string> handleBindWS(const std::string&, const std::string&);
std::optional<std::string> handleEnv(const std::string&, const std::string&);
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
std::optional<std::string> handlePermission(const std::string&, const std::string&);
std::optional<std::string> handleGesture(const std::string&, const std::string&);
+ std::optional<std::string> handleWindowrule(const std::string&, const std::string&);
+ std::optional<std::string> handleLayerrule(const std::string&, const std::string&);
std::optional<std::string> handleMonitorv2(const std::string& output);
Hyprlang::CParseResult handleMonitorv2();
+ std::optional<std::string> addRuleFromConfigKey(const std::string& name);
+ std::optional<std::string> addLayerRuleFromConfigKey(const std::string& name);
+ Hyprlang::CParseResult reloadRules();
std::string m_configCurrentPath;
@@ -310,19 +300,16 @@ class CConfigManager {
SSubmap m_currentSubmap;
- std::vector<SExecRequestedRule> m_execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
-
std::vector<std::string> m_declaredPlugins;
std::vector<SPluginKeyword> m_pluginKeywords;
std::vector<SPluginVariable> m_pluginVariables;
+ std::vector<SP<Desktop::Rule::IRule>> m_keywordRules;
+
bool m_isFirstLaunch = true; // For exec-once
std::vector<SMonitorRule> m_monitorRules;
std::vector<SWorkspaceRule> m_workspaceRules;
- std::vector<SP<CWindowRule>> m_windowRules;
- std::vector<SP<CLayerRule>> m_layerRules;
- std::vector<std::string> m_blurLSNamespaces;
bool m_firstExecDispatched = false;
bool m_manualCrashInitiated = false;
@@ -336,11 +323,11 @@ class CConfigManager {
uint32_t m_configValueNumber = 0;
// internal methods
- void updateBlurredLS(const std::string&, const bool);
void setDefaultAnimationVars();
std::optional<std::string> resetHLConfig();
std::optional<std::string> generateConfig(std::string configPath);
std::optional<std::string> verifyConfigExists();
+ void reloadRuleConfigs();
void postConfigReload(const Hyprlang::CParseResult& result);
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp
index b5de6503..82a69715 100644
--- a/src/debug/HyprCtl.cpp
+++ b/src/debug/HyprCtl.cpp
@@ -44,6 +44,7 @@ using namespace Hyprutils::OS;
#include "config/ConfigManager.hpp"
#include "helpers/MiscFunctions.hpp"
#include "../desktop/LayerSurface.hpp"
+#include "../desktop/rule/Engine.hpp"
#include "../version.h"
#include "../Compositor.hpp"
@@ -317,7 +318,7 @@ static std::string monitorsRequest(eHyprCtlOutputFormat format, std::string requ
}
static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) {
- const auto tags = w->m_tags.getTags();
+ const auto tags = w->m_ruleApplicator->m_tagKeeper.getTags();
if (format == eHyprCtlOutputFormat::FORMAT_JSON)
return std::ranges::fold_left(tags, std::string(),
@@ -1272,8 +1273,12 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
if (COMMAND.empty())
return "Invalid input: command is empty";
+ g_pHyprCtl->m_currentRequestParams.isDynamicKeyword = true;
+
std::string retval = g_pConfigManager->parseKeyword(COMMAND, VALUE);
+ g_pHyprCtl->m_currentRequestParams.isDynamicKeyword = false;
+
// if we are executing a dynamic source we have to reload everything, so every if will have a check for source.
if (COMMAND == "monitor" || COMMAND == "source")
g_pConfigManager->m_wantsMonitorReload = true; // for monitor keywords
@@ -1306,8 +1311,7 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
g_pConfigManager->updateWatcher();
// decorations will probably need a repaint
- if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source" ||
- COMMAND.starts_with("windowrule")) {
+ if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source") {
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
for (auto const& m : g_pCompositor->m_monitors) {
*(m->m_cursorZoom) = *PZOOMFACTOR;
@@ -1316,6 +1320,9 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
}
}
+ if (COMMAND.contains("windowrule ") || COMMAND.contains("windowrule["))
+ g_pConfigManager->reloadRules();
+
if (COMMAND.contains("workspace"))
g_pConfigManager->ensurePersistentWorkspacesPresent();
@@ -1521,11 +1528,6 @@ static std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string req
return "ok";
}
-static std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
- auto result = g_pKeybindManager->m_dispatchers["setprop"](request.substr(request.find_first_of(' ') + 1));
- return "DEPRECATED: use hyprctl dispatch setprop instead" + (result.success ? "" : "\n" + result.error);
-}
-
static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
@@ -1543,9 +1545,9 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
const bool FORMNORM = format == FORMAT_NORMAL;
auto sizeToString = [&](bool max) -> std::string {
- auto sizeValue = PWINDOW->m_windowData.minSize.valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE));
+ auto sizeValue = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE));
if (max)
- sizeValue = PWINDOW->m_windowData.maxSize.valueOr(Vector2D(INFINITY, INFINITY));
+ sizeValue = PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D(INFINITY, INFINITY));
if (FORMNORM)
return std::format("{} {}", sizeValue.x, sizeValue.y);
@@ -1556,7 +1558,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
}
};
- auto alphaToString = [&](CWindowOverridableVar<SAlphaValue>& alpha, bool getAlpha) -> std::string {
+ auto alphaToString = [&](Desktop::Types::COverridableVar<Desktop::Types::SAlphaValue>& alpha, bool getAlpha) -> std::string {
if (FORMNORM) {
if (getAlpha)
return std::format("{}", alpha.valueOrDefault().alpha);
@@ -1590,7 +1592,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
const auto* const ACTIVECOLOR =
!PWINDOW->m_groupData.pNextWindow.lock() ? (!PWINDOW->m_groupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
- std::string borderColorString = PWINDOW->m_windowData.activeBorderColor.valueOr(*ACTIVECOLOR).toString();
+ std::string borderColorString = PWINDOW->m_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR).toString();
if (FORMNORM)
return borderColorString;
else
@@ -1603,7 +1605,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
const auto* const INACTIVECOLOR = !PWINDOW->m_groupData.pNextWindow.lock() ? (!PWINDOW->m_groupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) :
(GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
- std::string borderColorString = PWINDOW->m_windowData.inactiveBorderColor.valueOr(*INACTIVECOLOR).toString();
+ std::string borderColorString = PWINDOW->m_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR).toString();
if (FORMNORM)
return borderColorString;
else
@@ -1618,38 +1620,92 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
return std::format(R"({{"{}": {}}})", PROP, prop.valueOrDefault());
};
- if (PROP == "animationstyle") {
- auto& animationStyle = PWINDOW->m_windowData.animationStyle;
+ if (PROP == "animation") {
+ auto& animationStyle = PWINDOW->m_ruleApplicator->animationStyle();
if (FORMNORM)
return animationStyle.valueOr("(unset)");
else
return std::format(R"({{"{}": "{}"}})", PROP, animationStyle.valueOr(""));
- } else if (PROP == "maxsize")
+ } else if (PROP == "max_size")
return sizeToString(true);
- else if (PROP == "minsize")
+ else if (PROP == "min_size")
return sizeToString(false);
- else if (PROP == "alpha")
- return alphaToString(PWINDOW->m_windowData.alpha, true);
- else if (PROP == "alphainactive")
- return alphaToString(PWINDOW->m_windowData.alphaInactive, true);
- else if (PROP == "alphafullscreen")
- return alphaToString(PWINDOW->m_windowData.alphaFullscreen, true);
- else if (PROP == "alphaoverride")
- return alphaToString(PWINDOW->m_windowData.alpha, false);
- else if (PROP == "alphainactiveoverride")
- return alphaToString(PWINDOW->m_windowData.alphaInactive, false);
- else if (PROP == "alphafullscreenoverride")
- return alphaToString(PWINDOW->m_windowData.alphaFullscreen, false);
- else if (PROP == "activebordercolor")
+ else if (PROP == "opacity")
+ return alphaToString(PWINDOW->m_ruleApplicator->alpha(), true);
+ else if (PROP == "opacity_inactive")
+ return alphaToString(PWINDOW->m_ruleApplicator->alphaInactive(), true);
+ else if (PROP == "opacity_fullscreen")
+ return alphaToString(PWINDOW->m_ruleApplicator->alphaFullscreen(), true);
+ else if (PROP == "opacity_override")
+ return alphaToString(PWINDOW->m_ruleApplicator->alpha(), false);
+ else if (PROP == "opacity_inactive_override")
+ return alphaToString(PWINDOW->m_ruleApplicator->alphaInactive(), false);
+ else if (PROP == "opacity_fullscreen_override")
+ return alphaToString(PWINDOW->m_ruleApplicator->alphaFullscreen(), false);
+ else if (PROP == "active_border_color")
return borderColorToString(true);
- else if (PROP == "inactivebordercolor")
+ else if (PROP == "inactive_border_color")
return borderColorToString(false);
- else if (auto search = NWindowProperties::boolWindowProperties.find(PROP); search != NWindowProperties::boolWindowProperties.end())
- return windowPropToString(*search->second(PWINDOW));
- else if (auto search = NWindowProperties::intWindowProperties.find(PROP); search != NWindowProperties::intWindowProperties.end())
- return windowPropToString(*search->second(PWINDOW));
- else if (auto search = NWindowProperties::floatWindowProperties.find(PROP); search != NWindowProperties::floatWindowProperties.end())
- return windowPropToString(*search->second(PWINDOW));
+ else if (PROP == "allows_input")
+ return windowPropToString(PWINDOW->m_ruleApplicator->allowsInput());
+ else if (PROP == "decorate")
+ return windowPropToString(PWINDOW->m_ruleApplicator->decorate());
+ else if (PROP == "focus_on_activate")
+ return windowPropToString(PWINDOW->m_ruleApplicator->focusOnActivate());
+ else if (PROP == "keep_aspect_ratio")
+ return windowPropToString(PWINDOW->m_ruleApplicator->keepAspectRatio());
+ else if (PROP == "nearest_neighbor")
+ return windowPropToString(PWINDOW->m_ruleApplicator->nearestNeighbor());
+ else if (PROP == "no_anim")
+ return windowPropToString(PWINDOW->m_ruleApplicator->noAnim());
+ else if (PROP == "no_blur")
+ return windowPropToString(PWINDOW->m_ruleApplicator->noBlur());
+ else if (PROP == "no_dim")
+ return windowPropToString(PWINDOW->m_ruleApplicator->noDim());
+ else if (PROP == "no_focus")
+ return windowPropToString(PWINDOW->m_ruleApplicator->noFocus());
+ else if (PROP == "no_max_size")
+ return windowPropToString(PWINDOW->m_ruleApplicator->noMaxSize());
+ else if (PROP == "no_shadow")
+ return windowPropToString(PWINDOW->m_ruleApplicator->noShadow());
+ else if (PROP == "no_shortcuts_inhibit")
+ return windowPropToString(PWINDOW->m_ruleApplicator->noShortcutsInhibit());
+ else if (PROP == "opaque")
+ return windowPropToString(PWINDOW->m_ruleApplicator->opaque());
+ else if (PROP == "dim_around")
+ return windowPropToString(PWINDOW->m_ruleApplicator->dimAround());
+ else if (PROP == "force_rgbx")
+ return windowPropToString(PWINDOW->m_ruleApplicator->RGBX());
+ else if (PROP == "sync_fullscreen")
+ return windowPropToString(PWINDOW->m_ruleApplicator->syncFullscreen());
+ else if (PROP == "immediate")
+ return windowPropToString(PWINDOW->m_ruleApplicator->tearing());
+ else if (PROP == "xray")
+ return windowPropToString(PWINDOW->m_ruleApplicator->xray());
+ else if (PROP == "render_unfocused")
+ return windowPropToString(PWINDOW->m_ruleApplicator->renderUnfocused());
+ else if (PROP == "no_follow_mouse")
+ return windowPropToString(PWINDOW->m_ruleApplicator->noFollowMouse());
+ else if (PROP == "no_screen_share")
+ return windowPropToString(PWINDOW->m_ruleApplicator->noScreenShare());
+ else if (PROP == "no_vrr")
+ return windowPropToString(PWINDOW->m_ruleApplicator->noVRR());
+ else if (PROP == "persistent_size")
+ return windowPropToString(PWINDOW->m_ruleApplicator->persistentSize());
+ else if (PROP == "stay_focused")
+ return windowPropToString(PWINDOW->m_ruleApplicator->stayFocused());
+ else if (PROP == "idle_inhibit")
+ return windowPropToString(PWINDOW->m_ruleApplicator->idleInhibitMode());
+ else if (PROP == "border_size")
+ return windowPropToString(PWINDOW->m_ruleApplicator->borderSize());
+ else if (PROP == "rounding")
+ return windowPropToString(PWINDOW->m_ruleApplicator->rounding());
+ else if (PROP == "rounding_power")
+ return windowPropToString(PWINDOW->m_ruleApplicator->roundingPower());
+ else if (PROP == "scroll_mouse")
+ return windowPropToString(PWINDOW->m_ruleApplicator->scrollMouse());
+ else if (PROP == "scroll_touchpad")
+ return windowPropToString(PWINDOW->m_ruleApplicator->scrollTouchpad());
return "prop not found";
}
@@ -2014,7 +2070,6 @@ CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});
registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify});
registerCommand(SHyprCtlCommand{"dismissnotify", false, dispatchDismissNotify});
- registerCommand(SHyprCtlCommand{"setprop", false, dispatchSetProp});
registerCommand(SHyprCtlCommand{"getprop", false, dispatchGetProp});
registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror});
registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest});
@@ -2130,8 +2185,7 @@ std::string CHyprCtl::getReply(std::string request) {
if (!w->m_isMapped || !w->m_workspace || !w->m_workspace->isVisible())
continue;
- w->updateDynamicRules();
- g_pCompositor->updateWindowAnimatedDecorationValues(w);
+ Desktop::Rule::ruleEngine()->updateAllRules();
}
for (auto const& m : g_pCompositor->m_monitors) {
diff --git a/src/debug/HyprCtl.hpp b/src/debug/HyprCtl.hpp
index 95bb65b8..d4f7aa14 100644
--- a/src/debug/HyprCtl.hpp
+++ b/src/debug/HyprCtl.hpp
@@ -25,9 +25,10 @@ class CHyprCtl {
Hyprutils::OS::CFileDescriptor m_socketFD;
struct {
- bool all = false;
- bool sysInfoConfig = false;
- pid_t pid = 0;
+ bool all = false;
+ bool sysInfoConfig = false;
+ bool isDynamicKeyword = false;
+ pid_t pid = 0;
SP<CPromise<std::string>> pendingPromise;
} m_currentRequestParams;
diff --git a/src/desktop/LayerRule.cpp b/src/desktop/LayerRule.cpp
deleted file mode 100644
index d6bfcadf..00000000
--- a/src/desktop/LayerRule.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include <re2/re2.h>
-#include "LayerRule.hpp"
-#include <unordered_set>
-#include <algorithm>
-#include "../debug/Log.hpp"
-
-static const auto RULES = std::unordered_set<std::string>{"noanim", "blur", "blurpopups", "dimaround", "noscreenshare"};
-static const auto RULES_PREFIX = std::unordered_set<std::string>{"ignorealpha", "ignorezero", "xray", "animation", "order", "abovelock"};
-
-CLayerRule::CLayerRule(const std::string& rule_, const std::string& ns_) : m_targetNamespace(ns_), m_rule(rule_) {
- const bool VALID = RULES.contains(m_rule) || std::ranges::any_of(RULES_PREFIX, [&rule_](const auto& prefix) { return rule_.starts_with(prefix); });
-
- if (!VALID)
- return;
-
- if (m_rule == "noanim")
- m_ruleType = RULE_NOANIM;
- else if (m_rule == "blur")
- m_ruleType = RULE_BLUR;
- else if (m_rule == "blurpopups")
- m_ruleType = RULE_BLURPOPUPS;
- else if (m_rule == "dimaround")
- m_ruleType = RULE_DIMAROUND;
- else if (m_rule == "noscreenshare")
- m_ruleType = RULE_NOSCREENSHARE;
- else if (m_rule.starts_with("ignorealpha"))
- m_ruleType = RULE_IGNOREALPHA;
- else if (m_rule.starts_with("ignorezero"))
- m_ruleType = RULE_IGNOREZERO;
- else if (m_rule.starts_with("xray"))
- m_ruleType = RULE_XRAY;
- else if (m_rule.starts_with("animation"))
- m_ruleType = RULE_ANIMATION;
- else if (m_rule.starts_with("order"))
- m_ruleType = RULE_ORDER;
- else if (m_rule.starts_with("abovelock"))
- m_ruleType = RULE_ABOVELOCK;
- else {
- Debug::log(ERR, "CLayerRule: didn't match a rule that was found valid?!");
- m_ruleType = RULE_INVALID;
- }
-}
diff --git a/src/desktop/LayerRule.hpp b/src/desktop/LayerRule.hpp
deleted file mode 100644
index 7b6c8a6d..00000000
--- a/src/desktop/LayerRule.hpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-
-#include <string>
-#include <cstdint>
-#include "Rule.hpp"
-
-class CLayerRule {
- public:
- CLayerRule(const std::string& rule, const std::string& targetNS);
-
- enum eRuleType : uint8_t {
- RULE_INVALID = 0,
- RULE_NOANIM,
- RULE_BLUR,
- RULE_BLURPOPUPS,
- RULE_DIMAROUND,
- RULE_ABOVELOCK,
- RULE_IGNOREALPHA,
- RULE_IGNOREZERO,
- RULE_XRAY,
- RULE_ANIMATION,
- RULE_ORDER,
- RULE_ZUMBA,
- RULE_NOSCREENSHARE
- };
-
- eRuleType m_ruleType = RULE_INVALID;
-
- const std::string m_targetNamespace;
- const std::string m_rule;
-
- CRuleRegexContainer m_targetNamespaceRegex;
-};
diff --git a/src/desktop/LayerSurface.cpp b/src/desktop/LayerSurface.cpp
index 6278078d..aab0b15a 100644
--- a/src/desktop/LayerSurface.cpp
+++ b/src/desktop/LayerSurface.cpp
@@ -37,7 +37,7 @@ PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
pLS->m_monitor = pMonitor;
pMonitor->m_layerSurfaceLayers[resource->m_current.layer].emplace_back(pLS);
- pLS->m_forceBlur = g_pConfigManager->shouldBlurLS(pLS->m_namespace);
+ pLS->m_ruleApplicator = makeUnique<Desktop::Rule::CLayerRuleApplicator>(pLS);
g_pAnimationManager->createAnimation(0.f, pLS->m_alpha, g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"), pLS, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
@@ -55,7 +55,7 @@ PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
void CLayerSurface::registerCallbacks() {
m_alpha->setUpdateCallback([this](auto) {
- if (m_dimAround && m_monitor)
+ if (m_ruleApplicator->dimAround().valueOrDefault() && m_monitor)
g_pHyprRenderer->damageMonitor(m_monitor.lock());
});
}
@@ -137,6 +137,8 @@ void CLayerSurface::onMap() {
m_mapped = true;
m_interactivity = m_layerSurface->m_current.interactivity;
+ m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ALL);
+
m_layerSurface->m_surface->map();
// this layer might be re-mapped.
@@ -149,8 +151,6 @@ void CLayerSurface::onMap() {
if (!PMONITOR)
return;
- applyRules();
-
PMONITOR->m_scheduledRecalc = true;
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
@@ -398,83 +398,6 @@ void CLayerSurface::onCommit() {
g_pCompositor->setPreferredTransformForSurface(m_surface->resource(), PMONITOR->m_transform);
}
-void CLayerSurface::applyRules() {
- m_noAnimations = false;
- m_forceBlur = false;
- m_ignoreAlpha = false;
- m_dimAround = false;
- m_noScreenShare = false;
- m_ignoreAlphaValue = 0.f;
- m_xray = -1;
- m_animationStyle.reset();
-
- for (auto const& rule : g_pConfigManager->getMatchingRules(m_self.lock())) {
- switch (rule->m_ruleType) {
- case CLayerRule::RULE_NOANIM: {
- m_noAnimations = true;
- break;
- }
- case CLayerRule::RULE_BLUR: {
- m_forceBlur = true;
- break;
- }
- case CLayerRule::RULE_BLURPOPUPS: {
- m_forceBlurPopups = true;
- break;
- }
- case CLayerRule::RULE_IGNOREALPHA:
- case CLayerRule::RULE_IGNOREZERO: {
- const auto FIRST_SPACE_POS = rule->m_rule.find_first_of(' ');
- std::string alphaValue = "";
- if (FIRST_SPACE_POS != std::string::npos)
- alphaValue = rule->m_rule.substr(FIRST_SPACE_POS + 1);
-
- try {
- m_ignoreAlpha = true;
- if (!alphaValue.empty())
- m_ignoreAlphaValue = std::stof(alphaValue);
- } catch (...) { Debug::log(ERR, "Invalid value passed to ignoreAlpha"); }
- break;
- }
- case CLayerRule::RULE_DIMAROUND: {
- m_dimAround = true;
- break;
- }
- case CLayerRule::RULE_NOSCREENSHARE: {
- m_noScreenShare = true;
- break;
- }
- case CLayerRule::RULE_XRAY: {
- CVarList vars{rule->m_rule, 0, ' '};
- m_xray = configStringToInt(vars[1]).value_or(false);
-
- break;
- }
- case CLayerRule::RULE_ANIMATION: {
- CVarList vars{rule->m_rule, 2, 's'};
- m_animationStyle = vars[1];
- break;
- }
- case CLayerRule::RULE_ORDER: {
- CVarList vars{rule->m_rule, 2, 's'};
- try {
- m_order = std::stoi(vars[1]);
- } catch (...) { Debug::log(ERR, "Invalid value passed to order"); }
- break;
- }
- case CLayerRule::RULE_ABOVELOCK: {
- m_aboveLockscreen = true;
-
- CVarList vars{rule->m_rule, 0, ' '};
- m_aboveLockscreenInteractable = configStringToInt(vars[1]).value_or(false);
-
- break;
- }
- default: break;
- }
- }
-}
-
bool CLayerSurface::isFadedOut() {
if (!m_fadingOut)
return false;
diff --git a/src/desktop/LayerSurface.hpp b/src/desktop/LayerSurface.hpp
index b70739cd..5676e4d2 100644
--- a/src/desktop/LayerSurface.hpp
+++ b/src/desktop/LayerSurface.hpp
@@ -3,6 +3,7 @@
#include <string>
#include "../defines.hpp"
#include "WLSurface.hpp"
+#include "rule/layerRule/LayerRuleApplicator.hpp"
#include "../helpers/AnimatedVariable.hpp"
class CLayerShellResource;
@@ -17,7 +18,6 @@ class CLayerSurface {
public:
~CLayerSurface();
- void applyRules();
bool isFadedOut();
int popupsCount();
@@ -28,47 +28,35 @@ class CLayerSurface {
WP<CLayerShellResource> m_layerSurface;
// the header providing the enum type cannot be imported here
- int m_interactivity = 0;
+ int m_interactivity = 0;
- SP<CWLSurface> m_surface;
+ SP<CWLSurface> m_surface;
- bool m_mapped = false;
- uint32_t m_layer = 0;
+ bool m_mapped = false;
+ uint32_t m_layer = 0;
- PHLMONITORREF m_monitor;
+ PHLMONITORREF m_monitor;
- bool m_fadingOut = false;
- bool m_readyToDelete = false;
- bool m_noProcess = false;
- bool m_noAnimations = false;
+ bool m_fadingOut = false;
+ bool m_readyToDelete = false;
+ bool m_noProcess = false;
- bool m_forceBlur = false;
- bool m_forceBlurPopups = false;
- int64_t m_xray = -1;
- bool m_ignoreAlpha = false;
- float m_ignoreAlphaValue = 0.f;
- bool m_dimAround = false;
- bool m_noScreenShare = false;
- int64_t m_order = 0;
- bool m_aboveLockscreen = false;
- bool m_aboveLockscreenInteractable = false;
+ UP<Desktop::Rule::CLayerRuleApplicator> m_ruleApplicator;
- std::optional<std::string> m_animationStyle;
+ PHLLSREF m_self;
- PHLLSREF m_self;
+ CBox m_geometry = {0, 0, 0, 0};
+ Vector2D m_position;
+ std::string m_namespace = "";
+ UP<CPopup> m_popupHead;
- CBox m_geometry = {0, 0, 0, 0};
- Vector2D m_position;
- std::string m_namespace = "";
- UP<CPopup> m_popupHead;
+ pid_t getPID();
- pid_t getPID();
-
- void onDestroy();
- void onMap();
- void onUnmap();
- void onCommit();
- MONITORID monitorID();
+ void onDestroy();
+ void onMap();
+ void onUnmap();
+ void onCommit();
+ MONITORID monitorID();
private:
struct {
diff --git a/src/desktop/Rule.cpp b/src/desktop/Rule.cpp
deleted file mode 100644
index 93f38de0..00000000
--- a/src/desktop/Rule.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <re2/re2.h>
-#include "../helpers/memory/Memory.hpp"
-#include "Rule.hpp"
-#include "../debug/Log.hpp"
-
-CRuleRegexContainer::CRuleRegexContainer(const std::string& regex_) {
- const bool NEGATIVE = regex_.starts_with("negative:");
-
- m_negative = NEGATIVE;
- m_regex = makeUnique<RE2>(NEGATIVE ? regex_.substr(9) : regex_);
-
- // TODO: maybe pop an error?
- if (!m_regex->ok())
- Debug::log(ERR, "RuleRegexContainer: regex {} failed to parse!", regex_);
-}
-
-bool CRuleRegexContainer::passes(const std::string& str) const {
- if (!m_regex)
- return false;
-
- return RE2::FullMatch(str, *m_regex) != m_negative;
-} \ No newline at end of file
diff --git a/src/desktop/Rule.hpp b/src/desktop/Rule.hpp
deleted file mode 100644
index 9d3de70e..00000000
--- a/src/desktop/Rule.hpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include <hyprutils/memory/UniquePtr.hpp>
-
-//NOLINTNEXTLINE
-namespace re2 {
- class RE2;
-};
-
-class CRuleRegexContainer {
- public:
- CRuleRegexContainer() = default;
-
- CRuleRegexContainer(const std::string& regex);
-
- bool passes(const std::string& str) const;
-
- private:
- Hyprutils::Memory::CUniquePointer<re2::RE2> m_regex;
- bool m_negative = false;
-}; \ No newline at end of file
diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp
index 8671c003..b2712886 100644
--- a/src/desktop/Window.cpp
+++ b/src/desktop/Window.cpp
@@ -24,6 +24,7 @@
#include "../protocols/FractionalScale.hpp"
#include "../xwayland/XWayland.hpp"
#include "../helpers/Color.hpp"
+#include "../helpers/math/Expression.hpp"
#include "../events/Events.hpp"
#include "../managers/XWaylandManager.hpp"
#include "../render/Renderer.hpp"
@@ -41,8 +42,9 @@ using enum NContentType::eContentType;
PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) {
PHLWINDOW pWindow = SP<CWindow>(new CWindow(surface));
- pWindow->m_self = pWindow;
- pWindow->m_isX11 = true;
+ pWindow->m_self = pWindow;
+ pWindow->m_isX11 = true;
+ pWindow->m_ruleApplicator = makeUnique<Desktop::Rule::CWindowRuleApplicator>(pWindow);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realSize, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
@@ -67,6 +69,7 @@ PHLWINDOW CWindow::create(SP<CXDGSurfaceResource> resource) {
pWindow->m_self = pWindow;
resource->m_toplevel->m_window = pWindow;
+ pWindow->m_ruleApplicator = makeUnique<Desktop::Rule::CWindowRuleApplicator>(pWindow);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realSize, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
@@ -138,7 +141,7 @@ SBoxExtents CWindow::getFullWindowExtents() {
const int BORDERSIZE = getRealBorderSize();
- if (m_windowData.dimAround.valueOrDefault()) {
+ if (m_ruleApplicator->dimAround().valueOrDefault()) {
if (const auto PMONITOR = m_monitor.lock(); PMONITOR)
return {.topLeft = {m_realPosition->value().x - PMONITOR->m_position.x, m_realPosition->value().y - PMONITOR->m_position.y},
.bottomRight = {PMONITOR->m_size.x - (m_realPosition->value().x - PMONITOR->m_position.x),
@@ -191,7 +194,7 @@ SBoxExtents CWindow::getFullWindowExtents() {
}
CBox CWindow::getFullWindowBoundingBox() {
- if (m_windowData.dimAround.valueOrDefault()) {
+ if (m_ruleApplicator->dimAround().valueOrDefault()) {
if (const auto PMONITOR = m_monitor.lock(); PMONITOR)
return {PMONITOR->m_position.x, PMONITOR->m_position.y, PMONITOR->m_size.x, PMONITOR->m_size.y};
}
@@ -251,7 +254,7 @@ SBoxExtents CWindow::getWindowExtentsUnified(uint64_t properties) {
}
CBox CWindow::getWindowBoxUnified(uint64_t properties) {
- if (m_windowData.dimAround.valueOrDefault()) {
+ if (m_ruleApplicator->dimAround().valueOrDefault()) {
const auto PMONITOR = m_monitor.lock();
if (PMONITOR)
return {PMONITOR->m_position.x, PMONITOR->m_position.y, PMONITOR->m_size.x, PMONITOR->m_size.y};
@@ -636,222 +639,6 @@ bool CWindow::isHidden() {
return m_hidden;
}
-void CWindow::applyDynamicRule(const SP<CWindowRule>& r) {
- const eOverridePriority priority = r->m_execRule ? PRIORITY_SET_PROP : PRIORITY_WINDOW_RULE;
- static auto PCLAMP_TILED = CConfigValue<Hyprlang::INT>("misc:size_limits_tiled");
-
- switch (r->m_ruleType) {
- case CWindowRule::RULE_TAG: {
- CVarList vars{r->m_rule, 0, 's', true};
-
- if (vars.size() == 2 && vars[0] == "tag")
- m_tags.applyTag(vars[1], true);
- else
- Debug::log(ERR, "Tag rule invalid: {}", r->m_rule);
- break;
- }
- case CWindowRule::RULE_OPACITY: {
- try {
- CVarList vars(r->m_rule, 0, ' ');
-
- int opacityIDX = 0;
-
- for (auto const& r : vars) {
- if (r == "opacity")
- continue;
-
- if (r == "override") {
- if (opacityIDX == 1)
- m_windowData.alpha = CWindowOverridableVar(SAlphaValue{.alpha = m_windowData.alpha.value().alpha, .overridden = true}, priority);
- else if (opacityIDX == 2)
- m_windowData.alphaInactive = CWindowOverridableVar(SAlphaValue{.alpha = m_windowData.alphaInactive.value().alpha, .overridden = true}, priority);
- else if (opacityIDX == 3)
- m_windowData.alphaFullscreen = CWindowOverridableVar(SAlphaValue{.alpha = m_windowData.alphaFullscreen.value().alpha, .overridden = true}, priority);
- } else {
- if (opacityIDX == 0) {
- m_windowData.alpha = CWindowOverridableVar(SAlphaValue{.alpha = std::stof(r), .overridden = false}, priority);
- } else if (opacityIDX == 1) {
- m_windowData.alphaInactive = CWindowOverridableVar(SAlphaValue{.alpha = std::stof(r), .overridden = false}, priority);
- } else if (opacityIDX == 2) {
- m_windowData.alphaFullscreen = CWindowOverridableVar(SAlphaValue{.alpha = std::stof(r), .overridden = false}, priority);
- } else {
- throw std::runtime_error("more than 3 alpha values");
- }
-
- opacityIDX++;
- }
- }
-
- if (opacityIDX == 1) {
- m_windowData.alphaInactive = m_windowData.alpha;
- m_windowData.alphaFullscreen = m_windowData.alpha;
- }
- } catch (std::exception& e) { Debug::log(ERR, "Opacity rule \"{}\" failed with: {}", r->m_rule, e.what()); }
- break;
- }
- case CWindowRule::RULE_ANIMATION: {
- auto STYLE = r->m_rule.substr(r->m_rule.find_first_of(' ') + 1);
- m_windowData.animationStyle = CWindowOverridableVar(STYLE, priority);
- break;
- }
- case CWindowRule::RULE_BORDERCOLOR: {
- try {
- // Each vector will only get used if it has at least one color
- CGradientValueData activeBorderGradient = {};
- CGradientValueData inactiveBorderGradient = {};
- bool active = true;
- CVarList colorsAndAngles = CVarList(trim(r->m_rule.substr(r->m_rule.find_first_of(' ') + 1)), 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_windowData.activeBorderColor = CWindowOverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[0]).value_or(0))), priority);
- m_windowData.inactiveBorderColor = CWindowOverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[1]).value_or(0))), priority);
- return;
- }
-
- 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)
- Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", r->m_rule);
- else if (activeBorderGradient.m_colors.empty())
- Debug::log(WARN, "Bordercolor rule \"{}\" has no colors, ignoring", r->m_rule);
- else if (inactiveBorderGradient.m_colors.empty())
- m_windowData.activeBorderColor = CWindowOverridableVar(activeBorderGradient, priority);
- else {
- m_windowData.activeBorderColor = CWindowOverridableVar(activeBorderGradient, priority);
- m_windowData.inactiveBorderColor = CWindowOverridableVar(inactiveBorderGradient, priority);
- }
- } catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r->m_rule, e.what()); }
- break;
- }
- case CWindowRule::RULE_IDLEINHIBIT: {
- auto IDLERULE = r->m_rule.substr(r->m_rule.find_first_of(' ') + 1);
-
- if (IDLERULE == "none")
- m_idleInhibitMode = IDLEINHIBIT_NONE;
- else if (IDLERULE == "always")
- m_idleInhibitMode = IDLEINHIBIT_ALWAYS;
- else if (IDLERULE == "focus")
- m_idleInhibitMode = IDLEINHIBIT_FOCUS;
- else if (IDLERULE == "fullscreen")
- m_idleInhibitMode = IDLEINHIBIT_FULLSCREEN;
- else
- Debug::log(ERR, "Rule idleinhibit: unknown mode {}", IDLERULE);
- break;
- }
- case CWindowRule::RULE_MAXSIZE: {
- try {
- if (!m_isFloating && !sc<bool>(*PCLAMP_TILED))
- return;
- const auto VEC = configStringToVector2D(r->m_rule.substr(8));
- if (VEC.x < 1 || VEC.y < 1) {
- Debug::log(ERR, "Invalid size for maxsize");
- return;
- }
-
- m_windowData.maxSize = CWindowOverridableVar(VEC, priority);
- clampWindowSize(std::nullopt, m_windowData.maxSize.value());
-
- } catch (std::exception& e) { Debug::log(ERR, "maxsize rule \"{}\" failed with: {}", r->m_rule, e.what()); }
- break;
- }
- case CWindowRule::RULE_MINSIZE: {
- try {
- if (!m_isFloating && !sc<bool>(*PCLAMP_TILED))
- return;
- const auto VEC = configStringToVector2D(r->m_rule.substr(8));
- if (VEC.x < 1 || VEC.y < 1) {
- Debug::log(ERR, "Invalid size for minsize");
- return;
- }
-
- m_windowData.minSize = CWindowOverridableVar(VEC, priority);
- clampWindowSize(m_windowData.minSize.value(), std::nullopt);
-
- if (m_groupData.pNextWindow.expired())
- setHidden(false);
- } catch (std::exception& e) { Debug::log(ERR, "minsize rule \"{}\" failed with: {}", r->m_rule, e.what()); }
- break;
- }
- case CWindowRule::RULE_RENDERUNFOCUSED: {
- m_windowData.renderUnfocused = CWindowOverridableVar(true, priority);
- g_pHyprRenderer->addWindowToRenderUnfocused(m_self.lock());
- break;
- }
- case CWindowRule::RULE_PROP: {
- const CVarList VARS(r->m_rule, 0, ' ');
- if (auto search = NWindowProperties::intWindowProperties.find(VARS[1]); search != NWindowProperties::intWindowProperties.end()) {
- try {
- *(search->second(m_self.lock())) = CWindowOverridableVar(sc<Hyprlang::INT>(std::stoi(VARS[2])), priority);
- } catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->m_rule, e.what()); }
- } else if (auto search = NWindowProperties::floatWindowProperties.find(VARS[1]); search != NWindowProperties::floatWindowProperties.end()) {
- try {
- *(search->second(m_self.lock())) = CWindowOverridableVar(std::stof(VARS[2]), priority);
- } catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->m_rule, e.what()); }
- } else if (auto search = NWindowProperties::boolWindowProperties.find(VARS[1]); search != NWindowProperties::boolWindowProperties.end()) {
- try {
- *(search->second(m_self.lock())) = CWindowOverridableVar(VARS[2].empty() ? true : sc<bool>(std::stoi(VARS[2])), priority);
- } catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->m_rule, e.what()); }
- }
- break;
- }
- case CWindowRule::RULE_PERSISTENTSIZE: {
- m_windowData.persistentSize = CWindowOverridableVar(true, PRIORITY_WINDOW_RULE);
- break;
- }
- case CWindowRule::RULE_NOVRR: {
- m_windowData.noVRR = CWindowOverridableVar(true, priority);
- break;
- }
- default: break;
- }
-}
-
-void CWindow::updateDynamicRules() {
- m_windowData.alpha.unset(PRIORITY_WINDOW_RULE);
- m_windowData.alphaInactive.unset(PRIORITY_WINDOW_RULE);
- m_windowData.alphaFullscreen.unset(PRIORITY_WINDOW_RULE);
-
- unsetWindowData(PRIORITY_WINDOW_RULE);
-
- m_windowData.animationStyle.unset(PRIORITY_WINDOW_RULE);
- m_windowData.maxSize.unset(PRIORITY_WINDOW_RULE);
- m_windowData.minSize.unset(PRIORITY_WINDOW_RULE);
-
- m_windowData.activeBorderColor.unset(PRIORITY_WINDOW_RULE);
- m_windowData.inactiveBorderColor.unset(PRIORITY_WINDOW_RULE);
-
- m_windowData.renderUnfocused.unset(PRIORITY_WINDOW_RULE);
- m_windowData.noVRR.unset(PRIORITY_WINDOW_RULE);
-
- m_idleInhibitMode = IDLEINHIBIT_NONE;
-
- m_tags.removeDynamicTags();
-
- m_matchedRules = g_pConfigManager->getMatchingRules(m_self.lock());
- for (const auto& r : m_matchedRules) {
- applyDynamicRule(r);
- }
-
- EMIT_HOOK_EVENT("windowUpdateRules", m_self.lock());
-
- g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
-}
-
// check if the point is "hidden" under a rounded corner of the window
// it is assumed that the point is within the real window box (m_vRealPosition, m_vRealSize)
// otherwise behaviour is undefined
@@ -924,6 +711,8 @@ void CWindow::createGroup() {
g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("1,{:x}", rc<uintptr_t>(this))});
}
+
+ m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
}
void CWindow::destroyGroup() {
@@ -943,6 +732,7 @@ void CWindow::destroyGroup() {
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("0,{:x}", rc<uintptr_t>(this))});
+ m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
return;
}
@@ -969,6 +759,7 @@ void CWindow::destroyGroup() {
g_pKeybindManager->m_groupsLocked = true;
for (auto const& w : members) {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(w);
+ w->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
w->updateWindowDecos();
}
g_pKeybindManager->m_groupsLocked = GROUPSLOCKEDPREV;
@@ -982,6 +773,8 @@ void CWindow::destroyGroup() {
if (!addresses.empty())
addresses.pop_back();
+
+ m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("0,{}", addresses)});
}
@@ -1217,16 +1010,16 @@ float CWindow::rounding() {
static auto PROUNDING = CConfigValue<Hyprlang::INT>("decoration:rounding");
static auto PROUNDINGPOWER = CConfigValue<Hyprlang::FLOAT>("decoration:rounding_power");
- float roundingPower = m_windowData.roundingPower.valueOr(*PROUNDINGPOWER);
- float rounding = m_windowData.rounding.valueOr(*PROUNDING) * (roundingPower / 2.0); /* Make perceived roundness consistent. */
+ float roundingPower = m_ruleApplicator->roundingPower().valueOr(*PROUNDINGPOWER);
+ float rounding = m_ruleApplicator->rounding().valueOr(*PROUNDING) * (roundingPower / 2.0); /* Make perceived roundness consistent. */
- return m_windowData.noRounding.valueOrDefault() ? 0 : rounding;
+ return rounding;
}
float CWindow::roundingPower() {
static auto PROUNDINGPOWER = CConfigValue<Hyprlang::FLOAT>("decoration:rounding_power");
- return m_windowData.roundingPower.valueOr(std::clamp(*PROUNDINGPOWER, 1.F, 10.F));
+ return m_ruleApplicator->roundingPower().valueOr(std::clamp(*PROUNDINGPOWER, 1.F, 10.F));
}
void CWindow::updateWindowData() {
@@ -1236,50 +1029,43 @@ void CWindow::updateWindowData() {
}
void CWindow::updateWindowData(const SWorkspaceRule& workspaceRule) {
- static auto PNOBORDERONFLOATING = CConfigValue<Hyprlang::INT>("general:no_border_on_floating");
-
- if (*PNOBORDERONFLOATING)
- m_windowData.noBorder = CWindowOverridableVar(m_isFloating, PRIORITY_LAYOUT);
- else
- m_windowData.noBorder.unset(PRIORITY_LAYOUT);
-
- m_windowData.borderSize.matchOptional(workspaceRule.borderSize, PRIORITY_WORKSPACE_RULE);
- m_windowData.decorate.matchOptional(workspaceRule.decorate, PRIORITY_WORKSPACE_RULE);
- m_windowData.noBorder.matchOptional(workspaceRule.noBorder, PRIORITY_WORKSPACE_RULE);
- m_windowData.noRounding.matchOptional(workspaceRule.noRounding, PRIORITY_WORKSPACE_RULE);
- m_windowData.noShadow.matchOptional(workspaceRule.noShadow, PRIORITY_WORKSPACE_RULE);
+ m_ruleApplicator->borderSize().matchOptional(workspaceRule.borderSize, Desktop::Types::PRIORITY_WORKSPACE_RULE);
+ m_ruleApplicator->decorate().matchOptional(workspaceRule.decorate, Desktop::Types::PRIORITY_WORKSPACE_RULE);
+ m_ruleApplicator->borderSize().matchOptional(workspaceRule.noBorder ? std::optional<Hyprlang::INT>(0) : std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE);
+ m_ruleApplicator->rounding().matchOptional(workspaceRule.noRounding.value_or(false) ? std::optional<Hyprlang::INT>(0) : std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE);
+ m_ruleApplicator->noShadow().matchOptional(workspaceRule.noShadow, Desktop::Types::PRIORITY_WORKSPACE_RULE);
}
int CWindow::getRealBorderSize() {
- if (m_windowData.noBorder.valueOrDefault() || (m_workspace && isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) || !m_windowData.decorate.valueOrDefault())
+ if ((m_workspace && isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) || !m_ruleApplicator->decorate().valueOrDefault())
return 0;
static auto PBORDERSIZE = CConfigValue<Hyprlang::INT>("general:border_size");
- return m_windowData.borderSize.valueOr(*PBORDERSIZE);
+ return m_ruleApplicator->borderSize().valueOr(*PBORDERSIZE);
}
float CWindow::getScrollMouse() {
static auto PINPUTSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:scroll_factor");
- return m_windowData.scrollMouse.valueOr(*PINPUTSCROLLFACTOR);
+ return m_ruleApplicator->scrollMouse().valueOr(*PINPUTSCROLLFACTOR);
}
float CWindow::getScrollTouchpad() {
static auto PTOUCHPADSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:touchpad:scroll_factor");
- return m_windowData.scrollTouchpad.valueOr(*PTOUCHPADSCROLLFACTOR);
+ return m_ruleApplicator->scrollTouchpad().valueOr(*PTOUCHPADSCROLLFACTOR);
}
bool CWindow::isScrollMouseOverridden() {
- return m_windowData.scrollMouse.hasValue();
+ return m_ruleApplicator->scrollMouse().hasValue();
}
bool CWindow::isScrollTouchpadOverridden() {
- return m_windowData.scrollTouchpad.hasValue();
+ return m_ruleApplicator->scrollTouchpad().hasValue();
}
bool CWindow::canBeTorn() {
static auto PTEARING = CConfigValue<Hyprlang::INT>("general:allow_tearing");
- return m_windowData.tearing.valueOr(m_tearingHint) && *PTEARING;
+ return m_ruleApplicator->tearing().valueOr(m_tearingHint) && *PTEARING;
}
void CWindow::setSuspended(bool suspend) {
@@ -1454,7 +1240,8 @@ void CWindow::activate(bool force) {
g_pEventManager->postEvent(SHyprIPCEvent{.event = "urgent", .data = std::format("{:x}", rc<uintptr_t>(this))});
EMIT_HOOK_EVENT("urgent", m_self.lock());
- if (!force && (!m_windowData.focusOnActivate.valueOr(*PFOCUSONACTIVATE) || (m_suppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY) || (m_suppressedEvents & SUPPRESS_ACTIVATE)))
+ if (!force &&
+ (!m_ruleApplicator->focusOnActivate().valueOr(*PFOCUSONACTIVATE) || (m_suppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY) || (m_suppressedEvents & SUPPRESS_ACTIVATE)))
return;
if (!m_isMapped) {
@@ -1539,8 +1326,7 @@ void CWindow::onUpdateMeta() {
}
if (doUpdate) {
- updateDynamicRules();
- g_pCompositor->updateWindowAnimatedDecorationValues(m_self.lock());
+ m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_TITLE | Desktop::Rule::RULE_PROP_CLASS);
updateToplevel();
}
}
@@ -1718,18 +1504,6 @@ PHLWINDOW CWindow::getSwallower() {
return candidates[0];
}
-void CWindow::unsetWindowData(eOverridePriority priority) {
- for (auto const& element : NWindowProperties::boolWindowProperties) {
- element.second(m_self.lock())->unset(priority);
- }
- for (auto const& element : NWindowProperties::intWindowProperties) {
- element.second(m_self.lock())->unset(priority);
- }
- for (auto const& element : NWindowProperties::floatWindowProperties) {
- element.second(m_self.lock())->unset(priority);
- }
-}
-
bool CWindow::isX11OverrideRedirect() {
return m_xwaylandSurface && m_xwaylandSurface->m_overrideRedirect;
}
@@ -1753,7 +1527,7 @@ Vector2D CWindow::requestedMinSize() {
Vector2D CWindow::requestedMaxSize() {
constexpr int NO_MAX_SIZE_LIMIT = 99999;
- if (((m_isX11 && !m_xwaylandSurface->m_sizeHints) || (!m_isX11 && (!m_xdgSurface || !m_xdgSurface->m_toplevel)) || m_windowData.noMaxSize.valueOrDefault()))
+ if (((m_isX11 && !m_xwaylandSurface->m_sizeHints) || (!m_isX11 && (!m_xdgSurface || !m_xdgSurface->m_toplevel)) || m_ruleApplicator->noMaxSize().valueOrDefault()))
return Vector2D(NO_MAX_SIZE_LIMIT, NO_MAX_SIZE_LIMIT);
Vector2D maxSize = m_isX11 ? Vector2D(m_xwaylandSurface->m_sizeHints->max_width, m_xwaylandSurface->m_sizeHints->max_height) : m_xdgSurface->m_toplevel->layoutMaxSize();
@@ -1951,3 +1725,127 @@ Vector2D CWindow::getReportedSize() {
return m_wlSurface->resource()->m_current.ackedSize;
return m_reportedSize;
}
+
+void CWindow::updateDecorationValues() {
+ static auto PACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.active_border");
+ static auto PINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.inactive_border");
+ static auto PNOGROUPACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.nogroup_border_active");
+ static auto PNOGROUPINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.nogroup_border");
+ static auto PGROUPACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_active");
+ static auto PGROUPINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_inactive");
+ static auto PGROUPACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_active");
+ static auto PGROUPINACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_inactive");
+ static auto PINACTIVEALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:inactive_opacity");
+ static auto PACTIVEALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:active_opacity");
+ static auto PFULLSCREENALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:fullscreen_opacity");
+ static auto PSHADOWCOL = CConfigValue<Hyprlang::INT>("decoration:shadow:color");
+ static auto PSHADOWCOLINACTIVE = CConfigValue<Hyprlang::INT>("decoration:shadow:color_inactive");
+ static auto PDIMSTRENGTH = CConfigValue<Hyprlang::FLOAT>("decoration:dim_strength");
+ static auto PDIMENABLED = CConfigValue<Hyprlang::INT>("decoration:dim_inactive");
+ static auto PDIMMODAL = CConfigValue<Hyprlang::INT>("decoration:dim_modal");
+
+ auto* const ACTIVECOL = sc<CGradientValueData*>((PACTIVECOL.ptr())->getData());
+ auto* const INACTIVECOL = sc<CGradientValueData*>((PINACTIVECOL.ptr())->getData());
+ auto* const NOGROUPACTIVECOL = sc<CGradientValueData*>((PNOGROUPACTIVECOL.ptr())->getData());
+ auto* const NOGROUPINACTIVECOL = sc<CGradientValueData*>((PNOGROUPINACTIVECOL.ptr())->getData());
+ auto* const GROUPACTIVECOL = sc<CGradientValueData*>((PGROUPACTIVECOL.ptr())->getData());
+ auto* const GROUPINACTIVECOL = sc<CGradientValueData*>((PGROUPINACTIVECOL.ptr())->getData());
+ auto* const GROUPACTIVELOCKEDCOL = sc<CGradientValueData*>((PGROUPACTIVELOCKEDCOL.ptr())->getData());
+ auto* const GROUPINACTIVELOCKEDCOL = sc<CGradientValueData*>((PGROUPINACTIVELOCKEDCOL.ptr())->getData());
+
+ auto setBorderColor = [&](CGradientValueData grad) -> void {
+ if (grad == m_realBorderColor)
+ return;
+
+ m_realBorderColorPrevious = m_realBorderColor;
+ m_realBorderColor = grad;
+ m_borderFadeAnimationProgress->setValueAndWarp(0.f);
+ *m_borderFadeAnimationProgress = 1.f;
+ };
+
+ const bool IS_SHADOWED_BY_MODAL = m_xdgSurface && m_xdgSurface->m_toplevel && m_xdgSurface->m_toplevel->anyChildModal();
+
+ // border
+ const auto RENDERDATA = g_pLayoutManager->getCurrentLayout()->requestRenderHints(m_self.lock());
+ if (RENDERDATA.isBorderGradient)
+ setBorderColor(*RENDERDATA.borderGradient);
+ else {
+ const bool GROUPLOCKED = m_groupData.pNextWindow.lock() ? getGroupHead()->m_groupData.locked : false;
+ if (m_self == g_pCompositor->m_lastWindow) {
+ const auto* const ACTIVECOLOR =
+ !m_groupData.pNextWindow.lock() ? (!m_groupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
+ setBorderColor(m_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR));
+ } else {
+ const auto* const INACTIVECOLOR =
+ !m_groupData.pNextWindow.lock() ? (!m_groupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) : (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
+ setBorderColor(m_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR));
+ }
+ }
+
+ // opacity
+ const auto PWORKSPACE = m_workspace;
+ if (isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) {
+ *m_activeInactiveAlpha = m_ruleApplicator->alphaFullscreen().valueOrDefault().applyAlpha(*PFULLSCREENALPHA);
+ } else {
+ if (m_self == g_pCompositor->m_lastWindow)
+ *m_activeInactiveAlpha = m_ruleApplicator->alpha().valueOrDefault().applyAlpha(*PACTIVEALPHA);
+ else
+ *m_activeInactiveAlpha = m_ruleApplicator->alphaInactive().valueOrDefault().applyAlpha(*PINACTIVEALPHA);
+ }
+
+ // dim
+ float goalDim = 1.F;
+ if (m_self == g_pCompositor->m_lastWindow.lock() || m_ruleApplicator->noDim().valueOrDefault() || !*PDIMENABLED)
+ goalDim = 0;
+ else
+ goalDim = *PDIMSTRENGTH;
+
+ if (IS_SHADOWED_BY_MODAL && *PDIMMODAL)
+ goalDim += (1.F - goalDim) / 2.F;
+
+ *m_dimPercent = goalDim;
+
+ // shadow
+ if (!isX11OverrideRedirect() && !m_X11DoesntWantBorders) {
+ if (m_self == g_pCompositor->m_lastWindow)
+ *m_realShadowColor = CHyprColor(*PSHADOWCOL);
+ else
+ *m_realShadowColor = CHyprColor(*PSHADOWCOLINACTIVE != -1 ? *PSHADOWCOLINACTIVE : *PSHADOWCOL);
+ } else
+ m_realShadowColor->setValueAndWarp(CHyprColor(0, 0, 0, 0)); // no shadow
+
+ updateWindowDecos();
+}
+
+std::optional<double> CWindow::calculateSingleExpr(const std::string& s) {
+ const auto PMONITOR = m_monitor ? m_monitor : g_pCompositor->m_lastMonitor;
+ const auto CURSOR_LOCAL = g_pInputManager->getMouseCoordsInternal() - (PMONITOR ? PMONITOR->m_position : Vector2D{});
+
+ Math::CExpression expr;
+ expr.addVariable("window_w", m_realSize->goal().x);
+ expr.addVariable("window_h", m_realSize->goal().y);
+ expr.addVariable("window_x", m_realPosition->goal().x - (PMONITOR ? PMONITOR->m_position.x : 0));
+ expr.addVariable("window_y", m_realPosition->goal().y - (PMONITOR ? PMONITOR->m_position.y : 0));
+
+ expr.addVariable("monitor_w", PMONITOR ? PMONITOR->m_size.x : 1920);
+ expr.addVariable("monitor_h", PMONITOR ? PMONITOR->m_size.y : 1080);
+
+ expr.addVariable("cursor_x", CURSOR_LOCAL.x);
+ expr.addVariable("cursor_y", CURSOR_LOCAL.y);
+
+ return expr.compute(s);
+}
+
+std::optional<Vector2D> CWindow::calculateExpression(const std::string& s) {
+ auto spacePos = s.find(' ');
+ if (spacePos == std::string::npos)
+ return std::nullopt;
+
+ const auto LHS = calculateSingleExpr(s.substr(0, spacePos));
+ const auto RHS = calculateSingleExpr(s.substr(spacePos + 1));
+
+ if (!LHS || !RHS)
+ return std::nullopt;
+
+ return Vector2D{*LHS, *RHS};
+}
diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp
index 0a7e207c..63492682 100644
--- a/src/desktop/Window.hpp
+++ b/src/desktop/Window.hpp
@@ -16,20 +16,12 @@
#include "Subsurface.hpp"
#include "WLSurface.hpp"
#include "Workspace.hpp"
-#include "WindowRule.hpp"
-#include "WindowOverridableVar.hpp"
+#include "rule/windowRule/WindowRuleApplicator.hpp"
#include "../protocols/types/ContentType.hpp"
class CXDGSurfaceResource;
class CXWaylandSurface;
-enum eIdleInhibitMode : uint8_t {
- IDLEINHIBIT_NONE = 0,
- IDLEINHIBIT_ALWAYS,
- IDLEINHIBIT_FULLSCREEN,
- IDLEINHIBIT_FOCUS
-};
-
enum eGroupRules : uint8_t {
// effective only during first map, except for _ALWAYS variant
GROUP_NONE = 0,
@@ -65,65 +57,6 @@ enum eSuppressEvents : uint8_t {
class IWindowTransformer;
-struct SAlphaValue {
- float alpha;
- bool overridden;
-
- float applyAlpha(float a) const {
- if (overridden)
- return alpha;
- else
- return alpha * a;
- };
-};
-
-struct SWindowData {
- CWindowOverridableVar<SAlphaValue> alpha = SAlphaValue{.alpha = 1.f, .overridden = false};
- CWindowOverridableVar<SAlphaValue> alphaInactive = SAlphaValue{.alpha = 1.f, .overridden = false};
- CWindowOverridableVar<SAlphaValue> alphaFullscreen = SAlphaValue{.alpha = 1.f, .overridden = false};
-
- CWindowOverridableVar<bool> allowsInput = false;
- CWindowOverridableVar<bool> dimAround = false;
- CWindowOverridableVar<bool> decorate = true;
- CWindowOverridableVar<bool> focusOnActivate = false;
- CWindowOverridableVar<bool> keepAspectRatio = false;
- CWindowOverridableVar<bool> nearestNeighbor = false;
- CWindowOverridableVar<bool> noAnim = false;
- CWindowOverridableVar<bool> noBorder = false;
- CWindowOverridableVar<bool> noBlur = false;
- CWindowOverridableVar<bool> noDim = false;
- CWindowOverridableVar<bool> noFocus = false;
- CWindowOverridableVar<bool> noMaxSize = false;
- CWindowOverridableVar<bool> noRounding = false;
- CWindowOverridableVar<bool> noShadow = false;
- CWindowOverridableVar<bool> noShortcutsInhibit = false;
- CWindowOverridableVar<bool> opaque = false;
- CWindowOverridableVar<bool> RGBX = false;
- CWindowOverridableVar<bool> syncFullscreen = true;
- CWindowOverridableVar<bool> tearing = false;
- CWindowOverridableVar<bool> xray = false;
- CWindowOverridableVar<bool> renderUnfocused = false;
- CWindowOverridableVar<bool> noFollowMouse = false;
- CWindowOverridableVar<bool> noScreenShare = false;
- CWindowOverridableVar<bool> noVRR = false;
-
- CWindowOverridableVar<Hyprlang::INT> borderSize = {std::string("general:border_size"), sc<Hyprlang::INT>(0), std::nullopt};
- CWindowOverridableVar<Hyprlang::INT> rounding = {std::string("decoration:rounding"), sc<Hyprlang::INT>(0), std::nullopt};
-
- CWindowOverridableVar<Hyprlang::FLOAT> roundingPower = {std::string("decoration:rounding_power")};
- CWindowOverridableVar<Hyprlang::FLOAT> scrollMouse = {std::string("input:scroll_factor")};
- CWindowOverridableVar<Hyprlang::FLOAT> scrollTouchpad = {std::string("input:touchpad:scroll_factor")};
-
- CWindowOverridableVar<std::string> animationStyle;
- CWindowOverridableVar<Vector2D> maxSize;
- CWindowOverridableVar<Vector2D> minSize;
-
- CWindowOverridableVar<CGradientValueData> activeBorderColor;
- CWindowOverridableVar<CGradientValueData> inactiveBorderColor;
-
- CWindowOverridableVar<bool> persistentSize;
-};
-
struct SInitialWorkspaceToken {
PHLWINDOWREF primaryOwner;
std::string workspace;
@@ -256,7 +189,7 @@ class CWindow {
std::vector<IHyprWindowDecoration*> m_decosToRemove;
// Special render data, rules, etc
- SWindowData m_windowData;
+ UP<Desktop::Rule::CWindowRuleApplicator> m_ruleApplicator;
// Transformers
std::vector<UP<IWindowTransformer>> m_transformers;
@@ -280,15 +213,9 @@ class CWindow {
bool m_currentlySwallowed = false;
bool m_groupSwallowed = false;
- // focus stuff
- bool m_stayFocused = false;
-
// for toplevel monitor events
MONITORID m_lastSurfaceMonitorID = -1;
- // for idle inhibiting windows
- eIdleInhibitMode m_idleInhibitMode = IDLEINHIBIT_NONE;
-
// initial token. Will be unregistered on workspace change or timeout of 2 minutes
std::string m_initialWorkspaceToken = "";
@@ -303,12 +230,6 @@ class CWindow {
bool m_tearingHint = false;
- // stores the currently matched window rules
- std::vector<SP<CWindowRule>> m_matchedRules;
-
- // window tags
- CTagKeeper m_tags;
-
// ANR
PHLANIMVAR<float> m_notRespondingTint;
@@ -342,8 +263,7 @@ class CWindow {
void onMap();
void setHidden(bool hidden);
bool isHidden();
- void applyDynamicRule(const SP<CWindowRule>& r);
- void updateDynamicRules();
+ void updateDecorationValues();
SBoxExtents getFullWindowReservedArea();
Vector2D middle();
bool opaque();
@@ -397,7 +317,6 @@ class CWindow {
std::string fetchClass();
void warpCursor(bool force = false);
PHLWINDOW getSwallower();
- void unsetWindowData(eOverridePriority priority);
bool isX11OverrideRedirect();
bool isModal();
Vector2D requestedMinSize();
@@ -418,6 +337,7 @@ class CWindow {
bool priorityFocus();
SP<CWLSurfaceResource> getSolitaryResource();
Vector2D getReportedSize();
+ std::optional<Vector2D> calculateExpression(const std::string& s);
CBox getWindowMainSurfaceBox() const {
return {m_realPosition->value().x, m_realPosition->value().y, m_realSize->value().x, m_realSize->value().y};
@@ -448,6 +368,8 @@ class CWindow {
} m_listeners;
private:
+ std::optional<double> calculateSingleExpr(const std::string& s);
+
// For hidden windows and stuff
bool m_hidden = false;
bool m_suspended = false;
@@ -474,45 +396,6 @@ inline bool validMapped(PHLWINDOWREF w) {
return w->m_isMapped;
}
-namespace NWindowProperties {
- static const std::unordered_map<std::string, std::function<CWindowOverridableVar<bool>*(const PHLWINDOW&)>> boolWindowProperties = {
- {"allowsinput", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.allowsInput; }},
- {"dimaround", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.dimAround; }},
- {"decorate", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.decorate; }},
- {"focusonactivate", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.focusOnActivate; }},
- {"keepaspectratio", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.keepAspectRatio; }},
- {"nearestneighbor", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.nearestNeighbor; }},
- {"noanim", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noAnim; }},
- {"noblur", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noBlur; }},
- {"noborder", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noBorder; }},
- {"nodim", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noDim; }},
- {"nofocus", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noFocus; }},
- {"nomaxsize", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noMaxSize; }},
- {"norounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noRounding; }},
- {"noshadow", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noShadow; }},
- {"noshortcutsinhibit", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noShortcutsInhibit; }},
- {"opaque", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.opaque; }},
- {"forcergbx", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.RGBX; }},
- {"syncfullscreen", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.syncFullscreen; }},
- {"novrr", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noVRR; }},
- {"immediate", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.tearing; }},
- {"xray", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.xray; }},
- {"nofollowmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noFollowMouse; }},
- {"noscreenshare", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noScreenShare; }},
- };
-
- const std::unordered_map<std::string, std::function<CWindowOverridableVar<Hyprlang::INT>*(const PHLWINDOW&)>> intWindowProperties = {
- {"rounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.rounding; }},
- {"bordersize", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.borderSize; }},
- };
-
- const std::unordered_map<std::string, std::function<CWindowOverridableVar<Hyprlang::FLOAT>*(PHLWINDOW)>> floatWindowProperties = {
- {"roundingpower", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.roundingPower; }},
- {"scrollmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.scrollMouse; }},
- {"scrolltouchpad", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.scrollTouchpad; }},
- };
-};
-
/**
format specification
- 'x', only address, equivalent of (uintpr_t)CWindow*
diff --git a/src/desktop/WindowOverridableVar.hpp b/src/desktop/WindowOverridableVar.hpp
deleted file mode 100644
index ea113d3e..00000000
--- a/src/desktop/WindowOverridableVar.hpp
+++ /dev/null
@@ -1,132 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <type_traits>
-#include <any>
-#include "../config/ConfigValue.hpp"
-
-enum eOverridePriority : uint8_t {
- PRIORITY_LAYOUT = 0,
- PRIORITY_WORKSPACE_RULE,
- PRIORITY_WINDOW_RULE,
- PRIORITY_SET_PROP,
-};
-
-template <typename T>
-T clampOptional(T const& value, std::optional<T> const& min, std::optional<T> const& max) {
- return std::clamp(value, min.value_or(std::numeric_limits<T>::min()), max.value_or(std::numeric_limits<T>::max()));
-}
-
-template <typename T, bool Extended = std::is_same_v<T, bool> || std::is_same_v<T, Hyprlang::INT> || std::is_same_v<T, Hyprlang::FLOAT>>
-class CWindowOverridableVar {
- public:
- CWindowOverridableVar(T const& value, eOverridePriority priority) {
- m_values[priority] = value;
- }
-
- CWindowOverridableVar(T const& value) : m_defaultValue{value} {}
- CWindowOverridableVar(T const& value, std::optional<T> const& min, std::optional<T> const& max = std::nullopt) : m_defaultValue{value}, m_minValue{min}, m_maxValue{max} {}
- CWindowOverridableVar(std::string const& value)
- requires(Extended && !std::is_same_v<T, bool>)
- : m_configValue(SP<CConfigValue<T>>(new CConfigValue<T>(value))) {}
- CWindowOverridableVar(std::string const& value, std::optional<T> const& min, std::optional<T> const& max = std::nullopt)
- requires(Extended && !std::is_same_v<T, bool>)
- : m_minValue(min), m_maxValue(max), m_configValue(SP<CConfigValue<T>>(new CConfigValue<T>(value))) {}
-
- CWindowOverridableVar() = default;
- ~CWindowOverridableVar() = default;
-
- CWindowOverridableVar& operator=(CWindowOverridableVar<T> const& other) {
- // Self-assignment check
- if (this == &other)
- return *this;
-
- for (auto const& value : other.m_values) {
- if constexpr (Extended && !std::is_same_v<T, bool>)
- m_values[value.first] = clampOptional(value.second, m_minValue, m_maxValue);
- else
- m_values[value.first] = value.second;
- }
-
- return *this;
- }
-
- void unset(eOverridePriority priority) {
- m_values.erase(priority);
- }
-
- bool hasValue() {
- return !m_values.empty();
- }
-
- T value() {
- if (!m_values.empty())
- return std::prev(m_values.end())->second;
- else
- throw std::bad_optional_access();
- }
-
- T valueOr(T const& other) {
- if (hasValue())
- return value();
- else
- return other;
- }
-
- T valueOrDefault()
- requires(Extended && !std::is_same_v<T, bool>)
- {
- if (hasValue())
- return value();
- else if (m_defaultValue.has_value())
- return m_defaultValue.value();
- else
- return **std::any_cast<SP<CConfigValue<T>>>(m_configValue);
- }
-
- T valueOrDefault()
- requires(!Extended || std::is_same_v<T, bool>)
- {
- if (hasValue())
- return value();
- else if (!m_defaultValue.has_value())
- throw std::bad_optional_access();
- else
- return m_defaultValue.value();
- }
-
- eOverridePriority getPriority() {
- if (!m_values.empty())
- return std::prev(m_values.end())->first;
- else
- throw std::bad_optional_access();
- }
-
- void increment(T const& other, eOverridePriority priority) {
- if constexpr (std::is_same_v<T, bool>)
- m_values[priority] = valueOr(false) ^ other;
- else
- m_values[priority] = clampOptional(valueOrDefault() + other, m_minValue, m_maxValue);
- }
-
- void matchOptional(std::optional<T> const& optValue, eOverridePriority priority) {
- if (optValue.has_value())
- m_values[priority] = optValue.value();
- else
- unset(priority);
- }
-
- operator std::optional<T>() {
- if (hasValue())
- return value();
- else
- return std::nullopt;
- }
-
- private:
- std::map<eOverridePriority, T> m_values;
- std::optional<T> m_defaultValue; // used for toggling, so required for bool
- std::optional<T> m_minValue;
- std::optional<T> m_maxValue;
- std::any m_configValue; // only there for select variables
-};
diff --git a/src/desktop/WindowRule.cpp b/src/desktop/WindowRule.cpp
deleted file mode 100644
index dc6564ca..00000000
--- a/src/desktop/WindowRule.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-#include "WindowRule.hpp"
-#include <unordered_set>
-#include <algorithm>
-#include <re2/re2.h>
-#include "../config/ConfigManager.hpp"
-
-static const auto RULES = std::unordered_set<std::string>{
- "float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", "renderunfocused", "persistentsize",
-};
-static const auto RULES_PREFIX = std::unordered_set<std::string>{
- "animation", "bordercolor", "bordersize", "center", "content", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize", "monitor",
- "move", "noclosefor", "opacity", "plugin:", "prop", "pseudo", "rounding", "roundingpower", "scrollmouse", "scrolltouchpad", "size",
- "suppressevent", "tag", "workspace", "xray", "novrr",
-};
-
-CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool isV2, bool isExecRule) : m_value(value), m_rule(rule), m_v2(isV2), m_execRule(isExecRule) {
- const auto VALS = CVarList(rule, 2, ' ');
- const bool VALID = RULES.contains(rule) || std::ranges::any_of(RULES_PREFIX, [&rule](auto prefix) { return rule.starts_with(prefix); }) ||
- (NWindowProperties::boolWindowProperties.contains(VALS[0])) || (NWindowProperties::intWindowProperties.contains(VALS[0])) ||
- (NWindowProperties::floatWindowProperties.contains(VALS[0]));
-
- if (!VALID)
- return;
-
- if (rule == "float")
- m_ruleType = RULE_FLOAT;
- else if (rule == "fullscreen")
- m_ruleType = RULE_FULLSCREEN;
- else if (rule == "maximize")
- m_ruleType = RULE_MAXIMIZE;
- else if (rule == "noinitialfocus")
- m_ruleType = RULE_NOINITIALFOCUS;
- else if (rule == "pin")
- m_ruleType = RULE_PIN;
- else if (rule == "stayfocused")
- m_ruleType = RULE_STAYFOCUSED;
- else if (rule == "tile")
- m_ruleType = RULE_TILE;
- else if (rule == "renderunfocused")
- m_ruleType = RULE_RENDERUNFOCUSED;
- else if (rule == "persistentsize")
- m_ruleType = RULE_PERSISTENTSIZE;
- else if (rule.starts_with("animation"))
- m_ruleType = RULE_ANIMATION;
- else if (rule.starts_with("bordercolor"))
- m_ruleType = RULE_BORDERCOLOR;
- else if (rule.starts_with("center"))
- m_ruleType = RULE_CENTER;
- else if (rule.starts_with("fullscreenstate"))
- m_ruleType = RULE_FULLSCREENSTATE;
- else if (rule.starts_with("group"))
- m_ruleType = RULE_GROUP;
- else if (rule.starts_with("idleinhibit"))
- m_ruleType = RULE_IDLEINHIBIT;
- else if (rule.starts_with("maxsize"))
- m_ruleType = RULE_MAXSIZE;
- else if (rule.starts_with("minsize"))
- m_ruleType = RULE_MINSIZE;
- else if (rule.starts_with("monitor"))
- m_ruleType = RULE_MONITOR;
- else if (rule.starts_with("move"))
- m_ruleType = RULE_MOVE;
- else if (rule.starts_with("opacity"))
- m_ruleType = RULE_OPACITY;
- else if (rule.starts_with("plugin:"))
- m_ruleType = RULE_PLUGIN;
- else if (rule.starts_with("pseudo"))
- m_ruleType = RULE_PSEUDO;
- else if (rule.starts_with("size"))
- m_ruleType = RULE_SIZE;
- else if (rule.starts_with("suppressevent"))
- m_ruleType = RULE_SUPPRESSEVENT;
- else if (rule.starts_with("novrr"))
- m_ruleType = RULE_NOVRR;
- else if (rule.starts_with("tag"))
- m_ruleType = RULE_TAG;
- else if (rule.starts_with("workspace"))
- m_ruleType = RULE_WORKSPACE;
- else if (rule.starts_with("prop"))
- m_ruleType = RULE_PROP;
- else if (rule.starts_with("content"))
- m_ruleType = RULE_CONTENT;
- else if (rule.starts_with("noclosefor"))
- m_ruleType = RULE_NOCLOSEFOR;
- else {
- // check if this is a prop.
- const CVarList VARS(rule, 0, 's', true);
- const bool ISPROP = NWindowProperties::intWindowProperties.contains(VARS[0]) || NWindowProperties::boolWindowProperties.contains(VARS[0]) ||
- NWindowProperties::floatWindowProperties.contains(VARS[0]);
- if (ISPROP) {
- *const_cast<std::string*>(&m_rule) = "prop " + rule;
- m_ruleType = RULE_PROP;
- Debug::log(LOG, "CWindowRule: direct prop rule found, rewritten {} -> {}", rule, m_rule);
- } else {
- Debug::log(ERR, "CWindowRule: didn't match a rule that was found valid?!");
- m_ruleType = RULE_INVALID;
- }
- }
-}
diff --git a/src/desktop/WindowRule.hpp b/src/desktop/WindowRule.hpp
deleted file mode 100644
index 5bf462e9..00000000
--- a/src/desktop/WindowRule.hpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#pragma once
-
-#include <string>
-#include <cstdint>
-#include "Rule.hpp"
-
-class CWindowRule {
- public:
- CWindowRule(const std::string& rule, const std::string& value, bool isV2 = false, bool isExecRule = false);
-
- enum eRuleType : uint8_t {
- RULE_INVALID = 0,
- RULE_FLOAT,
- RULE_FULLSCREEN,
- RULE_MAXIMIZE,
- RULE_NOINITIALFOCUS,
- RULE_PIN,
- RULE_STAYFOCUSED,
- RULE_TILE,
- RULE_RENDERUNFOCUSED,
- RULE_ANIMATION,
- RULE_BORDERCOLOR,
- RULE_CENTER,
- RULE_FULLSCREENSTATE,
- RULE_GROUP,
- RULE_IDLEINHIBIT,
- RULE_MAXSIZE,
- RULE_MINSIZE,
- RULE_MONITOR,
- RULE_MOVE,
- RULE_OPACITY,
- RULE_PLUGIN,
- RULE_PSEUDO,
- RULE_SIZE,
- RULE_SUPPRESSEVENT,
- RULE_TAG,
- RULE_WORKSPACE,
- RULE_PROP,
- RULE_CONTENT,
- RULE_PERSISTENTSIZE,
- RULE_NOCLOSEFOR,
- RULE_NOVRR,
- };
-
- eRuleType m_ruleType = RULE_INVALID;
-
- const std::string m_value;
- const std::string m_rule;
- const bool m_v2 = false;
- const bool m_execRule = false;
-
- std::string m_title;
- std::string m_class;
- std::string m_initialTitle;
- std::string m_initialClass;
- std::string m_tag;
- int m_X11 = -1; // -1 means "ANY"
- int m_floating = -1;
- int m_fullscreen = -1;
- int m_pinned = -1;
- int m_focus = -1;
- int m_group = -1;
- int m_modal = -1;
- std::string m_fullscreenState = ""; // empty means any
- std::string m_onWorkspace = ""; // empty means any
- std::string m_workspace = ""; // empty means any
- std::string m_contentType = ""; // empty means any
- std::string m_xdgTag = ""; // empty means any
-
- // precompiled regexes
- CRuleRegexContainer m_titleRegex;
- CRuleRegexContainer m_classRegex;
- CRuleRegexContainer m_initialTitleRegex;
- CRuleRegexContainer m_initialClassRegex;
- CRuleRegexContainer m_v1Regex;
-};
diff --git a/src/desktop/Workspace.cpp b/src/desktop/Workspace.cpp
index ee35313a..7e1dcd5b 100644
--- a/src/desktop/Workspace.cpp
+++ b/src/desktop/Workspace.cpp
@@ -542,7 +542,7 @@ void CWorkspace::updateWindows() {
if (!w->m_isMapped || w->m_workspace != m_self)
continue;
- w->updateDynamicRules();
+ w->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE);
}
}
diff --git a/src/desktop/rule/Engine.cpp b/src/desktop/rule/Engine.cpp
new file mode 100644
index 00000000..3232035d
--- /dev/null
+++ b/src/desktop/rule/Engine.cpp
@@ -0,0 +1,56 @@
+#include "Engine.hpp"
+#include "Rule.hpp"
+#include "../LayerSurface.hpp"
+#include "../../Compositor.hpp"
+
+using namespace Desktop;
+using namespace Desktop::Rule;
+
+SP<CRuleEngine> Rule::ruleEngine() {
+ static SP<CRuleEngine> engine = makeShared<CRuleEngine>();
+ return engine;
+}
+
+void CRuleEngine::registerRule(SP<IRule>&& rule) {
+ m_rules.emplace_back(std::move(rule));
+}
+
+void CRuleEngine::unregisterRule(const std::string& name) {
+ if (name.empty())
+ return;
+
+ std::erase_if(m_rules, [&name](const auto& el) { return el->name() == name; });
+}
+
+void CRuleEngine::unregisterRule(const SP<IRule>& rule) {
+ std::erase(m_rules, rule);
+ cleanExecRules();
+}
+
+void CRuleEngine::cleanExecRules() {
+ std::erase_if(m_rules, [](const auto& e) { return e->isExecRule() && e->execExpired(); });
+}
+
+void CRuleEngine::updateAllRules() {
+ cleanExecRules();
+ for (const auto& w : g_pCompositor->m_windows) {
+ if (!validMapped(w) || w->isHidden())
+ continue;
+
+ w->m_ruleApplicator->propertiesChanged(RULE_PROP_ALL);
+ }
+ for (const auto& ls : g_pCompositor->m_layers) {
+ if (!validMapped(ls))
+ continue;
+
+ ls->m_ruleApplicator->propertiesChanged(RULE_PROP_ALL);
+ }
+}
+
+void CRuleEngine::clearAllRules() {
+ std::erase_if(m_rules, [](const auto& e) { return !e->isExecRule() || e->execExpired(); });
+}
+
+const std::vector<SP<IRule>>& CRuleEngine::rules() {
+ return m_rules;
+}
diff --git a/src/desktop/rule/Engine.hpp b/src/desktop/rule/Engine.hpp
new file mode 100644
index 00000000..b0ea118e
--- /dev/null
+++ b/src/desktop/rule/Engine.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "Rule.hpp"
+
+namespace Desktop::Rule {
+ class CRuleEngine {
+ public:
+ CRuleEngine() = default;
+ ~CRuleEngine() = default;
+
+ void registerRule(SP<IRule>&& rule);
+ void unregisterRule(const std::string& name);
+ void unregisterRule(const SP<IRule>& rule);
+ void updateAllRules();
+ void cleanExecRules();
+ void clearAllRules();
+ const std::vector<SP<IRule>>& rules();
+
+ private:
+ std::vector<SP<IRule>> m_rules;
+ };
+
+ SP<CRuleEngine> ruleEngine();
+} \ No newline at end of file
diff --git a/src/desktop/rule/Rule.cpp b/src/desktop/rule/Rule.cpp
new file mode 100644
index 00000000..fe7271a6
--- /dev/null
+++ b/src/desktop/rule/Rule.cpp
@@ -0,0 +1,149 @@
+#include "Rule.hpp"
+#include "../../debug/Log.hpp"
+#include <re2/re2.h>
+
+#include "matchEngine/RegexMatchEngine.hpp"
+#include "matchEngine/BoolMatchEngine.hpp"
+#include "matchEngine/IntMatchEngine.hpp"
+#include "matchEngine/WorkspaceMatchEngine.hpp"
+#include "matchEngine/TagMatchEngine.hpp"
+
+using namespace Desktop;
+using namespace Desktop::Rule;
+
+static const std::unordered_map<eRuleProperty, std::string> MATCH_PROP_STRINGS = {
+ {RULE_PROP_CLASS, "class"}, //
+ {RULE_PROP_TITLE, "title"}, //
+ {RULE_PROP_INITIAL_CLASS, "initial_class"}, //
+ {RULE_PROP_INITIAL_TITLE, "initial_title"}, //
+ {RULE_PROP_FLOATING, "float"}, //
+ {RULE_PROP_TAG, "tag"}, //
+ {RULE_PROP_XWAYLAND, "xwayland"}, //
+ {RULE_PROP_FULLSCREEN, "fullscreen"}, //
+ {RULE_PROP_PINNED, "pin"}, //
+ {RULE_PROP_FOCUS, "focus"}, //
+ {RULE_PROP_GROUP, "group"}, //
+ {RULE_PROP_MODAL, "modal"}, //
+ {RULE_PROP_FULLSCREENSTATE_INTERNAL, "fullscreen_state_internal"}, //
+ {RULE_PROP_FULLSCREENSTATE_CLIENT, "fullscreen_state_client"}, //
+ {RULE_PROP_ON_WORKSPACE, "workspace"}, //
+ {RULE_PROP_CONTENT, "content"}, //
+ {RULE_PROP_XDG_TAG, "xdg_tag"}, //
+ {RULE_PROP_NAMESPACE, "namespace"}, //
+};
+
+static const std::unordered_map<eRuleProperty, eRuleMatchEngine> RULE_ENGINES = {
+ {RULE_PROP_CLASS, RULE_MATCH_ENGINE_REGEX}, //
+ {RULE_PROP_TITLE, RULE_MATCH_ENGINE_REGEX}, //
+ {RULE_PROP_INITIAL_CLASS, RULE_MATCH_ENGINE_REGEX}, //
+ {RULE_PROP_INITIAL_TITLE, RULE_MATCH_ENGINE_REGEX}, //
+ {RULE_PROP_FLOATING, RULE_MATCH_ENGINE_BOOL}, //
+ {RULE_PROP_TAG, RULE_MATCH_ENGINE_TAG}, //
+ {RULE_PROP_XWAYLAND, RULE_MATCH_ENGINE_BOOL}, //
+ {RULE_PROP_FULLSCREEN, RULE_MATCH_ENGINE_BOOL}, //
+ {RULE_PROP_PINNED, RULE_MATCH_ENGINE_BOOL}, //
+ {RULE_PROP_FOCUS, RULE_MATCH_ENGINE_BOOL}, //
+ {RULE_PROP_GROUP, RULE_MATCH_ENGINE_BOOL}, //
+ {RULE_PROP_MODAL, RULE_MATCH_ENGINE_BOOL}, //
+ {RULE_PROP_FULLSCREENSTATE_INTERNAL, RULE_MATCH_ENGINE_INT}, //
+ {RULE_PROP_FULLSCREENSTATE_CLIENT, RULE_MATCH_ENGINE_INT}, //
+ {RULE_PROP_ON_WORKSPACE, RULE_MATCH_ENGINE_WORKSPACE}, //
+ {RULE_PROP_CONTENT, RULE_MATCH_ENGINE_INT}, //
+ {RULE_PROP_XDG_TAG, RULE_MATCH_ENGINE_REGEX}, //
+ {RULE_PROP_NAMESPACE, RULE_MATCH_ENGINE_REGEX}, //
+ {RULE_PROP_EXEC_TOKEN, RULE_MATCH_ENGINE_REGEX}, //
+};
+
+const std::vector<std::string>& Rule::allMatchPropStrings() {
+ static std::vector<std::string> strings;
+ static bool once = true;
+ if (once) {
+ for (const auto& [k, v] : MATCH_PROP_STRINGS) {
+ strings.emplace_back(v);
+ }
+ once = false;
+ }
+ return strings;
+}
+
+std::optional<eRuleProperty> Rule::matchPropFromString(const std::string_view& s) {
+ const auto IT = std::ranges::find_if(MATCH_PROP_STRINGS, [&s](const auto& el) { return el.second == s; });
+ if (IT == MATCH_PROP_STRINGS.end())
+ return std::nullopt;
+
+ return IT->first;
+}
+
+std::optional<eRuleProperty> Rule::matchPropFromString(const std::string& s) {
+ return matchPropFromString(std::string_view{s});
+}
+
+IRule::IRule(const std::string& name) : m_name(name) {
+ ;
+}
+
+void IRule::registerMatch(eRuleProperty p, const std::string& s) {
+ if (!RULE_ENGINES.contains(p)) {
+ Debug::log(ERR, "BUG THIS: IRule: RULE_ENGINES does not contain rule idx {}", sc<std::underlying_type_t<eRuleProperty>>(p));
+ return;
+ }
+
+ switch (RULE_ENGINES.at(p)) {
+ case RULE_MATCH_ENGINE_REGEX: m_matchEngines[p] = makeUnique<CRegexMatchEngine>(s); break;
+ case RULE_MATCH_ENGINE_BOOL: m_matchEngines[p] = makeUnique<CBoolMatchEngine>(s); break;
+ case RULE_MATCH_ENGINE_INT: m_matchEngines[p] = makeUnique<CIntMatchEngine>(s); break;
+ case RULE_MATCH_ENGINE_WORKSPACE: m_matchEngines[p] = makeUnique<CWorkspaceMatchEngine>(s); break;
+ case RULE_MATCH_ENGINE_TAG: m_matchEngines[p] = makeUnique<CTagMatchEngine>(s); break;
+ }
+
+ m_mask |= p;
+}
+
+std::underlying_type_t<eRuleProperty> IRule::getPropertiesMask() {
+ return m_mask;
+}
+
+bool IRule::has(eRuleProperty p) {
+ return m_matchEngines.contains(p);
+}
+
+bool IRule::matches(eRuleProperty p, const std::string& s) {
+ if (!has(p))
+ return false;
+
+ return m_matchEngines[p]->match(s);
+}
+
+bool IRule::matches(eRuleProperty p, bool b) {
+ if (!has(p))
+ return false;
+
+ return m_matchEngines[p]->match(b);
+}
+
+const std::string& IRule::name() {
+ return m_name;
+}
+
+void IRule::markAsExecRule(const std::string& token, bool persistent) {
+ m_execData.isExecRule = true;
+ m_execData.isExecPersistent = persistent;
+ m_execData.token = token;
+ m_execData.expiresAt = Time::steadyNow() + std::chrono::minutes(1);
+}
+
+bool IRule::isExecRule() {
+ return m_execData.isExecRule;
+}
+
+bool IRule::isExecPersistent() {
+ return m_execData.isExecPersistent;
+}
+
+bool IRule::execExpired() {
+ return Time::steadyNow() > m_execData.expiresAt;
+}
+
+const std::string& IRule::execToken() {
+ return m_execData.token;
+}
diff --git a/src/desktop/rule/Rule.hpp b/src/desktop/rule/Rule.hpp
new file mode 100644
index 00000000..2b852b3a
--- /dev/null
+++ b/src/desktop/rule/Rule.hpp
@@ -0,0 +1,84 @@
+#pragma once
+
+#include "matchEngine/MatchEngine.hpp"
+
+#include "../../helpers/memory/Memory.hpp"
+#include "../../helpers/time/Time.hpp"
+#include <vector>
+#include <unordered_map>
+#include <cstdint>
+#include <optional>
+
+namespace Desktop::Rule {
+ enum eRuleProperty : uint32_t {
+ RULE_PROP_NONE = 0,
+ RULE_PROP_CLASS = (1 << 0),
+ RULE_PROP_TITLE = (1 << 1),
+ RULE_PROP_INITIAL_CLASS = (1 << 2),
+ RULE_PROP_INITIAL_TITLE = (1 << 3),
+ RULE_PROP_FLOATING = (1 << 4),
+ RULE_PROP_TAG = (1 << 5),
+ RULE_PROP_XWAYLAND = (1 << 6),
+ RULE_PROP_FULLSCREEN = (1 << 7),
+ RULE_PROP_PINNED = (1 << 8),
+ RULE_PROP_FOCUS = (1 << 9),
+ RULE_PROP_GROUP = (1 << 10),
+ RULE_PROP_MODAL = (1 << 11),
+ RULE_PROP_FULLSCREENSTATE_INTERNAL = (1 << 12),
+ RULE_PROP_FULLSCREENSTATE_CLIENT = (1 << 13),
+ RULE_PROP_ON_WORKSPACE = (1 << 14),
+ RULE_PROP_CONTENT = (1 << 15),
+ RULE_PROP_XDG_TAG = (1 << 16),
+ RULE_PROP_NAMESPACE = (1 << 17),
+ RULE_PROP_EXEC_TOKEN = (1 << 18),
+
+ RULE_PROP_ALL = std::numeric_limits<std::underlying_type_t<eRuleProperty>>::max(),
+ };
+
+ enum eRuleType : uint8_t {
+ RULE_TYPE_WINDOW = 0,
+ RULE_TYPE_LAYER,
+ };
+
+ std::optional<eRuleProperty> matchPropFromString(const std::string& s);
+ std::optional<eRuleProperty> matchPropFromString(const std::string_view& s);
+ const std::vector<std::string>& allMatchPropStrings();
+
+ class IRule {
+ public:
+ virtual ~IRule() = default;
+
+ virtual eRuleType type() = 0;
+ virtual std::underlying_type_t<eRuleProperty> getPropertiesMask();
+
+ void registerMatch(eRuleProperty, const std::string&);
+ void markAsExecRule(const std::string& token, bool persistent = false);
+ bool isExecRule();
+ bool isExecPersistent();
+ bool execExpired();
+ const std::string& execToken();
+
+ const std::string& name();
+
+ protected:
+ IRule(const std::string& name = "");
+
+ bool matches(eRuleProperty, const std::string& s);
+ bool matches(eRuleProperty, bool b);
+ bool has(eRuleProperty);
+
+ //
+ std::unordered_map<eRuleProperty, UP<IMatchEngine>> m_matchEngines;
+
+ private:
+ std::underlying_type_t<eRuleProperty> m_mask = 0;
+ std::string m_name = "";
+
+ struct {
+ bool isExecRule = false;
+ bool isExecPersistent = false;
+ std::string token;
+ Time::steady_tp expiresAt;
+ } m_execData;
+ };
+} \ No newline at end of file
diff --git a/src/desktop/rule/effect/EffectContainer.hpp b/src/desktop/rule/effect/EffectContainer.hpp
new file mode 100644
index 00000000..cb2157a6
--- /dev/null
+++ b/src/desktop/rule/effect/EffectContainer.hpp
@@ -0,0 +1,81 @@
+#pragma once
+
+#include <string>
+#include <vector>
+#include <type_traits>
+#include <cstdint>
+#include <optional>
+
+namespace Desktop::Rule {
+ template <typename T>
+ class IEffectContainer {
+ static_assert(std::is_enum_v<T>);
+
+ protected:
+ const std::string DEFAULT_MISSING_KEY = "";
+
+ public:
+ // Make sure we're using at least a uint16_t for dynamic registrations to not overflow.
+ // 32k should be enough
+ using storageType = std::conditional_t<(sizeof(std::underlying_type_t<T>) >= 2), std::underlying_type_t<T>, uint16_t>;
+
+ IEffectContainer(std::vector<std::string>&& defaultKeys) : m_keys(std::move(defaultKeys)), m_originalSize(m_keys.size()) {
+ ;
+ }
+ virtual ~IEffectContainer() = default;
+
+ virtual storageType registerEffect(std::string&& name) {
+ if (m_keys.size() >= std::numeric_limits<storageType>::max())
+ return 0;
+ if (auto it = std::ranges::find(m_keys, name); it != m_keys.end())
+ return it - m_keys.begin();
+ m_keys.emplace_back(std::move(name));
+ return m_keys.size() - 1;
+ }
+
+ virtual void unregisterEffect(storageType id) {
+ if (id >= m_keys.size())
+ return;
+
+ m_keys[id] = DEFAULT_MISSING_KEY;
+ }
+
+ virtual void unregisterEffect(const std::string& name) {
+ for (auto& key : m_keys) {
+ if (key == name) {
+ key = DEFAULT_MISSING_KEY;
+ break;
+ }
+ }
+ }
+
+ virtual const std::string& get(storageType idx) {
+ if (idx >= m_keys.size())
+ return DEFAULT_MISSING_KEY;
+
+ return m_keys[idx];
+ }
+
+ virtual std::optional<storageType> get(const std::string_view& s) {
+ for (storageType i = 0; i < m_keys.size(); ++i) {
+ if (m_keys[i] == s)
+ return i;
+ }
+
+ return std::nullopt;
+ }
+
+ virtual const std::vector<std::string>& allEffectStrings() {
+ return m_keys;
+ }
+
+ // whether the effect has been added dynamically as opposed to in the ctor.
+ virtual bool isEffectDynamic(storageType i) {
+ return i >= m_originalSize;
+ }
+
+ protected:
+ std::vector<std::string> m_keys;
+ size_t m_originalSize = 0;
+ };
+}; \ No newline at end of file
diff --git a/src/desktop/rule/layerRule/LayerRule.cpp b/src/desktop/rule/layerRule/LayerRule.cpp
new file mode 100644
index 00000000..be757672
--- /dev/null
+++ b/src/desktop/rule/layerRule/LayerRule.cpp
@@ -0,0 +1,43 @@
+#include "LayerRule.hpp"
+#include "../../../debug/Log.hpp"
+#include "../../LayerSurface.hpp"
+
+using namespace Desktop;
+using namespace Desktop::Rule;
+
+CLayerRule::CLayerRule(const std::string& name) : IRule(name) {
+ ;
+}
+
+eRuleType CLayerRule::type() {
+ return RULE_TYPE_LAYER;
+}
+
+void CLayerRule::addEffect(CLayerRule::storageType e, const std::string& result) {
+ m_effects.emplace_back(std::make_pair<>(e, result));
+}
+
+const std::vector<std::pair<CLayerRule::storageType, std::string>>& CLayerRule::effects() {
+ return m_effects;
+}
+
+bool CLayerRule::matches(PHLLS ls) {
+ if (m_matchEngines.empty())
+ return false;
+
+ for (const auto& [prop, engine] : m_matchEngines) {
+ switch (prop) {
+ default: {
+ Debug::log(TRACE, "CLayerRule::matches: skipping prop entry {}", sc<std::underlying_type_t<eRuleProperty>>(prop));
+ break;
+ }
+
+ case RULE_PROP_NAMESPACE:
+ if (!engine->match(ls->m_namespace))
+ return false;
+ break;
+ }
+ }
+
+ return true;
+}
diff --git a/src/desktop/rule/layerRule/LayerRule.hpp b/src/desktop/rule/layerRule/LayerRule.hpp
new file mode 100644
index 00000000..990796c1
--- /dev/null
+++ b/src/desktop/rule/layerRule/LayerRule.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "../Rule.hpp"
+#include "../../DesktopTypes.hpp"
+#include "LayerRuleEffectContainer.hpp"
+
+namespace Desktop::Rule {
+ class CLayerRule : public IRule {
+ public:
+ using storageType = CLayerRuleEffectContainer::storageType;
+
+ CLayerRule(const std::string& name = "");
+ virtual ~CLayerRule() = default;
+
+ virtual eRuleType type();
+
+ void addEffect(storageType e, const std::string& result);
+ const std::vector<std::pair<storageType, std::string>>& effects();
+
+ bool matches(PHLLS w);
+
+ private:
+ std::vector<std::pair<storageType, std::string>> m_effects;
+ };
+};
diff --git a/src/desktop/rule/layerRule/LayerRuleApplicator.cpp b/src/desktop/rule/layerRule/LayerRuleApplicator.cpp
new file mode 100644
index 00000000..bb7da97f
--- /dev/null
+++ b/src/desktop/rule/layerRule/LayerRuleApplicator.cpp
@@ -0,0 +1,128 @@
+#include "LayerRuleApplicator.hpp"
+#include "LayerRule.hpp"
+#include "../Engine.hpp"
+#include "../../LayerSurface.hpp"
+#include "../../types/OverridableVar.hpp"
+#include "../../../helpers/MiscFunctions.hpp"
+
+using namespace Desktop;
+using namespace Desktop::Rule;
+
+CLayerRuleApplicator::CLayerRuleApplicator(PHLLS ls) : m_ls(ls) {
+ ;
+}
+
+void CLayerRuleApplicator::resetProps(std::underlying_type_t<eRuleProperty> props, Types::eOverridePriority prio) {
+ // TODO: fucking kill me, is there a better way to do this?
+
+#define UNSET(x) \
+ if (m_##x.second & props) { \
+ if (prio == Types::PRIORITY_WINDOW_RULE) \
+ m_##x.second &= ~props; \
+ m_##x.first.unset(prio); \
+ }
+
+ UNSET(noanim)
+ UNSET(blur)
+ UNSET(blurPopups)
+ UNSET(dimAround)
+ UNSET(xray)
+ UNSET(noScreenShare)
+ UNSET(order)
+ UNSET(aboveLock)
+ UNSET(ignoreAlpha)
+ UNSET(animationStyle)
+}
+
+void CLayerRuleApplicator::applyDynamicRule(const SP<CLayerRule>& rule) {
+ for (const auto& [key, effect] : rule->effects()) {
+ switch (key) {
+ case LAYER_RULE_EFFECT_NONE: {
+ Debug::log(ERR, "CLayerRuleApplicator::applyDynamicRule: BUG THIS: LAYER_RULE_EFFECT_NONE??");
+ break;
+ }
+ case LAYER_RULE_EFFECT_NO_ANIM: {
+ m_noanim.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
+ m_noanim.second |= rule->getPropertiesMask();
+ break;
+ }
+ case LAYER_RULE_EFFECT_BLUR: {
+ m_blur.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
+ m_blur.second |= rule->getPropertiesMask();
+ break;
+ }
+ case LAYER_RULE_EFFECT_BLUR_POPUPS: {
+ m_blurPopups.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
+ m_blurPopups.second |= rule->getPropertiesMask();
+ break;
+ }
+ case LAYER_RULE_EFFECT_DIM_AROUND: {
+ m_dimAround.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
+ m_dimAround.second |= rule->getPropertiesMask();
+ break;
+ }
+ case LAYER_RULE_EFFECT_XRAY: {
+ m_xray.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
+ m_xray.second |= rule->getPropertiesMask();
+ break;
+ }
+ case LAYER_RULE_EFFECT_NO_SCREEN_SHARE: {
+ m_noScreenShare.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
+ m_noScreenShare.second |= rule->getPropertiesMask();
+ break;
+ }
+ case LAYER_RULE_EFFECT_ORDER: {
+ try {
+ m_noScreenShare.first.set(std::stoi(effect), Types::PRIORITY_WINDOW_RULE);
+ m_noScreenShare.second |= rule->getPropertiesMask();
+ } catch (...) { Debug::log(ERR, "CLayerRuleApplicator::applyDynamicRule: invalid order {}", effect); }
+ break;
+ }
+ case LAYER_RULE_EFFECT_ABOVE_LOCK: {
+ try {
+ m_aboveLock.first.set(std::clamp(std::stoull(effect), 0ULL, 2ULL), Types::PRIORITY_WINDOW_RULE);
+ m_aboveLock.second |= rule->getPropertiesMask();
+ } catch (...) { Debug::log(ERR, "CLayerRuleApplicator::applyDynamicRule: invalid order {}", effect); }
+ break;
+ }
+ case LAYER_RULE_EFFECT_IGNORE_ALPHA: {
+ try {
+ m_ignoreAlpha.first.set(std::clamp(std::stof(effect), 0.F, 1.F), Types::PRIORITY_WINDOW_RULE);
+ m_ignoreAlpha.second |= rule->getPropertiesMask();
+ } catch (...) { Debug::log(ERR, "CLayerRuleApplicator::applyDynamicRule: invalid order {}", effect); }
+ break;
+ }
+ case LAYER_RULE_EFFECT_ANIMATION: {
+ m_animationStyle.first.set(effect, Types::PRIORITY_WINDOW_RULE);
+ m_animationStyle.second |= rule->getPropertiesMask();
+ break;
+ }
+ }
+ }
+}
+
+void CLayerRuleApplicator::propertiesChanged(std::underlying_type_t<eRuleProperty> props) {
+ if (!m_ls)
+ return;
+
+ resetProps(props);
+
+ // FIXME: this will not update properties correctly if we implement dynamic rules for
+ // layers, due to effects overlapping on 0 prop intersection.
+ // See WindowRule.cpp, and ::propertiesChanged there.
+
+ for (const auto& r : ruleEngine()->rules()) {
+ if (r->type() != RULE_TYPE_LAYER)
+ continue;
+
+ if (!(r->getPropertiesMask() & props))
+ continue;
+
+ auto wr = reinterpretPointerCast<CLayerRule>(r);
+
+ if (!wr->matches(m_ls.lock()))
+ continue;
+
+ applyDynamicRule(wr);
+ }
+}
diff --git a/src/desktop/rule/layerRule/LayerRuleApplicator.hpp b/src/desktop/rule/layerRule/LayerRuleApplicator.hpp
new file mode 100644
index 00000000..97f15b04
--- /dev/null
+++ b/src/desktop/rule/layerRule/LayerRuleApplicator.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "../../DesktopTypes.hpp"
+#include "../Rule.hpp"
+#include "../../types/OverridableVar.hpp"
+#include "../../../helpers/math/Math.hpp"
+#include "../../../config/ConfigDataValues.hpp"
+
+namespace Desktop::Rule {
+ class CLayerRule;
+
+ class CLayerRuleApplicator {
+ public:
+ CLayerRuleApplicator(PHLLS ls);
+ ~CLayerRuleApplicator() = default;
+
+ CLayerRuleApplicator(const CLayerRuleApplicator&) = delete;
+ CLayerRuleApplicator(CLayerRuleApplicator&) = delete;
+ CLayerRuleApplicator(CLayerRuleApplicator&&) = delete;
+
+ void propertiesChanged(std::underlying_type_t<eRuleProperty> props);
+ void resetProps(std::underlying_type_t<eRuleProperty> props, Types::eOverridePriority prio = Types::PRIORITY_WINDOW_RULE);
+
+#define COMMA ,
+#define DEFINE_PROP(type, name, def) \
+ private: \
+ std::pair<Types::COverridableVar<type>, std::underlying_type_t<eRuleProperty>> m_##name = {def, RULE_PROP_NONE}; \
+ \
+ public: \
+ Types::COverridableVar<type>& name() { \
+ return m_##name.first; \
+ } \
+ void name##Override(const Types::COverridableVar<type>& other) { \
+ m_##name.first = other; \
+ }
+
+ // dynamic props
+ DEFINE_PROP(bool, noanim, false)
+ DEFINE_PROP(bool, blur, false)
+ DEFINE_PROP(bool, blurPopups, false)
+ DEFINE_PROP(bool, dimAround, false)
+ DEFINE_PROP(bool, xray, false)
+ DEFINE_PROP(bool, noScreenShare, false)
+
+ DEFINE_PROP(Hyprlang::INT, order, 0)
+ DEFINE_PROP(Hyprlang::INT, aboveLock, 0)
+
+ DEFINE_PROP(Hyprlang::FLOAT, ignoreAlpha, 0.F)
+
+ DEFINE_PROP(std::string, animationStyle, std::string(""))
+
+#undef COMMA
+#undef DEFINE_PROP
+
+ private:
+ PHLLSREF m_ls;
+
+ void applyDynamicRule(const SP<CLayerRule>& rule);
+ };
+};
diff --git a/src/desktop/rule/layerRule/LayerRuleEffectContainer.cpp b/src/desktop/rule/layerRule/LayerRuleEffectContainer.cpp
new file mode 100644
index 00000000..17394239
--- /dev/null
+++ b/src/desktop/rule/layerRule/LayerRuleEffectContainer.cpp
@@ -0,0 +1,33 @@
+#include "LayerRuleEffectContainer.hpp"
+
+using namespace Desktop;
+using namespace Desktop::Rule;
+
+//
+SP<CLayerRuleEffectContainer> Rule::layerEffects() {
+ static SP<CLayerRuleEffectContainer> container = makeShared<CLayerRuleEffectContainer>();
+ return container;
+}
+
+static const std::vector<std::string> EFFECT_STRINGS = {
+ "__internal_none", //
+ "no_anim", //
+ "blur", //
+ "blur_popups", //
+ "ignore_alpha", //
+ "dim_around", //
+ "xray", //
+ "animation", //
+ "order", //
+ "above_lock", //
+ "no_screen_share", //
+ "__internal_last_static", //
+};
+
+// This is here so that if we change the rules, we get reminded to update
+// the strings.
+static_assert(LAYER_RULE_EFFECT_LAST_STATIC == 11);
+
+CLayerRuleEffectContainer::CLayerRuleEffectContainer() : IEffectContainer<eLayerRuleEffect>(std::vector<std::string>{EFFECT_STRINGS}) {
+ ;
+}
diff --git a/src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp b/src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp
new file mode 100644
index 00000000..e3b3d26c
--- /dev/null
+++ b/src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "../effect/EffectContainer.hpp"
+#include "../../../helpers/memory/Memory.hpp"
+
+#pragma once
+
+namespace Desktop::Rule {
+ enum eLayerRuleEffect : uint8_t {
+ LAYER_RULE_EFFECT_NONE = 0,
+
+ LAYER_RULE_EFFECT_NO_ANIM,
+ LAYER_RULE_EFFECT_BLUR,
+ LAYER_RULE_EFFECT_BLUR_POPUPS,
+ LAYER_RULE_EFFECT_IGNORE_ALPHA,
+ LAYER_RULE_EFFECT_DIM_AROUND,
+ LAYER_RULE_EFFECT_XRAY,
+ LAYER_RULE_EFFECT_ANIMATION,
+ LAYER_RULE_EFFECT_ORDER,
+ LAYER_RULE_EFFECT_ABOVE_LOCK,
+ LAYER_RULE_EFFECT_NO_SCREEN_SHARE,
+
+ LAYER_RULE_EFFECT_LAST_STATIC,
+ };
+
+ class CLayerRuleEffectContainer : public IEffectContainer<eLayerRuleEffect> {
+ public:
+ CLayerRuleEffectContainer();
+ virtual ~CLayerRuleEffectContainer() = default;
+ };
+
+ SP<CLayerRuleEffectContainer> layerEffects();
+}; \ No newline at end of file
diff --git a/src/desktop/rule/matchEngine/BoolMatchEngine.cpp b/src/desktop/rule/matchEngine/BoolMatchEngine.cpp
new file mode 100644
index 00000000..f5c47227
--- /dev/null
+++ b/src/desktop/rule/matchEngine/BoolMatchEngine.cpp
@@ -0,0 +1,12 @@
+#include "BoolMatchEngine.hpp"
+#include "../../../helpers/MiscFunctions.hpp"
+
+using namespace Desktop::Rule;
+
+CBoolMatchEngine::CBoolMatchEngine(const std::string& s) : m_value(truthy(s)) {
+ ;
+}
+
+bool CBoolMatchEngine::match(bool other) {
+ return other == m_value;
+}
diff --git a/src/desktop/rule/matchEngine/BoolMatchEngine.hpp b/src/desktop/rule/matchEngine/BoolMatchEngine.hpp
new file mode 100644
index 00000000..bd162cda
--- /dev/null
+++ b/src/desktop/rule/matchEngine/BoolMatchEngine.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "MatchEngine.hpp"
+
+namespace Desktop::Rule {
+ class CBoolMatchEngine : public IMatchEngine {
+ public:
+ CBoolMatchEngine(const std::string&);
+ virtual ~CBoolMatchEngine() = default;
+
+ virtual bool match(bool other);
+
+ private:
+ bool m_value = false;
+ };
+} \ No newline at end of file
diff --git a/src/desktop/rule/matchEngine/IntMatchEngine.cpp b/src/desktop/rule/matchEngine/IntMatchEngine.cpp
new file mode 100644
index 00000000..c5bc87f6
--- /dev/null
+++ b/src/desktop/rule/matchEngine/IntMatchEngine.cpp
@@ -0,0 +1,14 @@
+#include "IntMatchEngine.hpp"
+#include "../../../debug/Log.hpp"
+
+using namespace Desktop::Rule;
+
+CIntMatchEngine::CIntMatchEngine(const std::string& s) {
+ try {
+ m_value = std::stoi(s);
+ } catch (...) { Debug::log(ERR, "CIntMatchEngine: invalid input {}", s); }
+}
+
+bool CIntMatchEngine::match(int other) {
+ return m_value == other;
+}
diff --git a/src/desktop/rule/matchEngine/IntMatchEngine.hpp b/src/desktop/rule/matchEngine/IntMatchEngine.hpp
new file mode 100644
index 00000000..2eda492c
--- /dev/null
+++ b/src/desktop/rule/matchEngine/IntMatchEngine.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "MatchEngine.hpp"
+
+namespace Desktop::Rule {
+ class CIntMatchEngine : public IMatchEngine {
+ public:
+ CIntMatchEngine(const std::string&);
+ virtual ~CIntMatchEngine() = default;
+
+ virtual bool match(int other);
+
+ private:
+ int m_value = 0;
+ };
+} \ No newline at end of file
diff --git a/src/desktop/rule/matchEngine/MatchEngine.cpp b/src/desktop/rule/matchEngine/MatchEngine.cpp
new file mode 100644
index 00000000..0bc89d7f
--- /dev/null
+++ b/src/desktop/rule/matchEngine/MatchEngine.cpp
@@ -0,0 +1,23 @@
+#include "MatchEngine.hpp"
+
+using namespace Desktop::Rule;
+
+bool IMatchEngine::match(const std::string&) {
+ return false;
+}
+
+bool IMatchEngine::match(bool) {
+ return false;
+}
+
+bool IMatchEngine::match(int) {
+ return false;
+}
+
+bool IMatchEngine::match(PHLWORKSPACE) {
+ return false;
+}
+
+bool IMatchEngine::match(const CTagKeeper& keeper) {
+ return false;
+}
diff --git a/src/desktop/rule/matchEngine/MatchEngine.hpp b/src/desktop/rule/matchEngine/MatchEngine.hpp
new file mode 100644
index 00000000..9588ac05
--- /dev/null
+++ b/src/desktop/rule/matchEngine/MatchEngine.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "../../DesktopTypes.hpp"
+
+class CTagKeeper;
+
+namespace Desktop::Rule {
+ enum eRuleMatchEngine : uint8_t {
+ RULE_MATCH_ENGINE_REGEX = 0,
+ RULE_MATCH_ENGINE_BOOL,
+ RULE_MATCH_ENGINE_INT,
+ RULE_MATCH_ENGINE_WORKSPACE,
+ RULE_MATCH_ENGINE_TAG,
+ };
+
+ class IMatchEngine {
+ public:
+ virtual ~IMatchEngine() = default;
+ virtual bool match(const std::string&);
+ virtual bool match(bool);
+ virtual bool match(int);
+ virtual bool match(PHLWORKSPACE);
+ virtual bool match(const CTagKeeper& keeper);
+
+ protected:
+ IMatchEngine() = default;
+ };
+}; \ No newline at end of file
diff --git a/src/desktop/rule/matchEngine/RegexMatchEngine.cpp b/src/desktop/rule/matchEngine/RegexMatchEngine.cpp
new file mode 100644
index 00000000..14e30af1
--- /dev/null
+++ b/src/desktop/rule/matchEngine/RegexMatchEngine.cpp
@@ -0,0 +1,17 @@
+#include "RegexMatchEngine.hpp"
+#include <re2/re2.h>
+
+using namespace Desktop::Rule;
+
+CRegexMatchEngine::CRegexMatchEngine(const std::string& regex) {
+ if (regex.starts_with("negative:")) {
+ m_negative = true;
+ m_regex = makeUnique<re2::RE2>(regex.substr(9));
+ return;
+ }
+ m_regex = makeUnique<re2::RE2>(regex);
+}
+
+bool CRegexMatchEngine::match(const std::string& other) {
+ return re2::RE2::FullMatch(other, *m_regex) != m_negative;
+} \ No newline at end of file
diff --git a/src/desktop/rule/matchEngine/RegexMatchEngine.hpp b/src/desktop/rule/matchEngine/RegexMatchEngine.hpp
new file mode 100644
index 00000000..e980ce70
--- /dev/null
+++ b/src/desktop/rule/matchEngine/RegexMatchEngine.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "MatchEngine.hpp"
+#include "../../../helpers/memory/Memory.hpp"
+
+//NOLINTNEXTLINE
+namespace re2 {
+ class RE2;
+};
+
+namespace Desktop::Rule {
+ class CRegexMatchEngine : public IMatchEngine {
+ public:
+ CRegexMatchEngine(const std::string& regex);
+ virtual ~CRegexMatchEngine() = default;
+
+ virtual bool match(const std::string& other);
+
+ private:
+ UP<re2::RE2> m_regex;
+ bool m_negative = false;
+ };
+} \ No newline at end of file
diff --git a/src/desktop/rule/matchEngine/TagMatchEngine.cpp b/src/desktop/rule/matchEngine/TagMatchEngine.cpp
new file mode 100644
index 00000000..d669822a
--- /dev/null
+++ b/src/desktop/rule/matchEngine/TagMatchEngine.cpp
@@ -0,0 +1,12 @@
+#include "TagMatchEngine.hpp"
+#include "../../../helpers/TagKeeper.hpp"
+
+using namespace Desktop::Rule;
+
+CTagMatchEngine::CTagMatchEngine(const std::string& tag) : m_tag(tag) {
+ ;
+}
+
+bool CTagMatchEngine::match(const CTagKeeper& keeper) {
+ return keeper.isTagged(m_tag);
+} \ No newline at end of file
diff --git a/src/desktop/rule/matchEngine/TagMatchEngine.hpp b/src/desktop/rule/matchEngine/TagMatchEngine.hpp
new file mode 100644
index 00000000..f8ef3e22
--- /dev/null
+++ b/src/desktop/rule/matchEngine/TagMatchEngine.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "MatchEngine.hpp"
+
+namespace Desktop::Rule {
+ class CTagMatchEngine : public IMatchEngine {
+ public:
+ CTagMatchEngine(const std::string& tag);
+ virtual ~CTagMatchEngine() = default;
+
+ virtual bool match(const CTagKeeper& keeper);
+
+ private:
+ std::string m_tag;
+ };
+} \ No newline at end of file
diff --git a/src/desktop/rule/matchEngine/WorkspaceMatchEngine.cpp b/src/desktop/rule/matchEngine/WorkspaceMatchEngine.cpp
new file mode 100644
index 00000000..abaa1657
--- /dev/null
+++ b/src/desktop/rule/matchEngine/WorkspaceMatchEngine.cpp
@@ -0,0 +1,12 @@
+#include "WorkspaceMatchEngine.hpp"
+#include "../../Workspace.hpp"
+
+using namespace Desktop::Rule;
+
+CWorkspaceMatchEngine::CWorkspaceMatchEngine(const std::string& s) : m_value(s) {
+ ;
+}
+
+bool CWorkspaceMatchEngine::match(PHLWORKSPACE ws) {
+ return ws->matchesStaticSelector(m_value);
+}
diff --git a/src/desktop/rule/matchEngine/WorkspaceMatchEngine.hpp b/src/desktop/rule/matchEngine/WorkspaceMatchEngine.hpp
new file mode 100644
index 00000000..c70bc8b4
--- /dev/null
+++ b/src/desktop/rule/matchEngine/WorkspaceMatchEngine.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "MatchEngine.hpp"
+
+namespace Desktop::Rule {
+ class CWorkspaceMatchEngine : public IMatchEngine {
+ public:
+ CWorkspaceMatchEngine(const std::string&);
+ virtual ~CWorkspaceMatchEngine() = default;
+
+ virtual bool match(PHLWORKSPACE ws);
+
+ private:
+ std::string m_value = "";
+ };
+} \ No newline at end of file
diff --git a/src/desktop/rule/utils/SetUtils.hpp b/src/desktop/rule/utils/SetUtils.hpp
new file mode 100644
index 00000000..75fd4739
--- /dev/null
+++ b/src/desktop/rule/utils/SetUtils.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <unordered_set>
+
+namespace Desktop::Rule {
+ template <class T>
+ bool setsIntersect(const std::unordered_set<T>& A, const std::unordered_set<T>& B) {
+ if (A.size() > B.size())
+ return setsIntersect(B, A);
+
+ for (const auto& e : A) {
+ if (B.contains(e))
+ return true;
+ }
+ return false;
+ }
+}; \ No newline at end of file
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;
+}
diff --git a/src/desktop/rule/windowRule/WindowRule.hpp b/src/desktop/rule/windowRule/WindowRule.hpp
new file mode 100644
index 00000000..944614ce
--- /dev/null
+++ b/src/desktop/rule/windowRule/WindowRule.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "../Rule.hpp"
+#include "../../DesktopTypes.hpp"
+#include "WindowRuleEffectContainer.hpp"
+#include "../../../helpers/math/Math.hpp"
+
+#include <unordered_set>
+
+namespace Desktop::Rule {
+ constexpr const char* EXEC_RULE_ENV_NAME = "HL_EXEC_RULE_TOKEN";
+
+ std::optional<Vector2D> parseRelativeVector(PHLWINDOW w, const std::string& s);
+
+ class CWindowRule : public IRule {
+ private:
+ using storageType = CWindowRuleEffectContainer::storageType;
+
+ public:
+ CWindowRule(const std::string& name = "");
+ virtual ~CWindowRule() = default;
+
+ static SP<CWindowRule> buildFromExecString(std::string&&);
+
+ virtual eRuleType type();
+
+ void addEffect(storageType e, const std::string& result);
+ const std::vector<std::pair<storageType, std::string>>& effects();
+ const std::unordered_set<storageType>& effectsSet();
+
+ bool matches(PHLWINDOW w, bool allowEnvLookup = false);
+
+ private:
+ std::vector<std::pair<storageType, std::string>> m_effects;
+ std::unordered_set<storageType> m_effectSet;
+ };
+};
diff --git a/src/desktop/rule/windowRule/WindowRuleApplicator.cpp b/src/desktop/rule/windowRule/WindowRuleApplicator.cpp
new file mode 100644
index 00000000..e6e0c655
--- /dev/null
+++ b/src/desktop/rule/windowRule/WindowRuleApplicator.cpp
@@ -0,0 +1,642 @@
+#include "WindowRuleApplicator.hpp"
+#include "WindowRule.hpp"
+#include "../Engine.hpp"
+#include "../utils/SetUtils.hpp"
+#include "../../Window.hpp"
+#include "../../types/OverridableVar.hpp"
+#include "../../../managers/LayoutManager.hpp"
+
+#include <hyprutils/string/String.hpp>
+
+using namespace Hyprutils::String;
+
+using namespace Desktop;
+using namespace Desktop::Rule;
+
+CWindowRuleApplicator::CWindowRuleApplicator(PHLWINDOW w) : m_window(w) {
+ ;
+}
+
+void CWindowRuleApplicator::resetProps(std::underlying_type_t<eRuleProperty> props, Types::eOverridePriority prio) {
+ // TODO: fucking kill me, is there a better way to do this?
+
+#define UNSET(x) \
+ if (m_##x.second & props) { \
+ if (prio == Types::PRIORITY_WINDOW_RULE) \
+ m_##x.second &= ~props; \
+ m_##x.first.unset(prio); \
+ }
+
+ UNSET(alpha)
+ UNSET(alphaInactive)
+ UNSET(alphaFullscreen)
+ UNSET(allowsInput)
+ UNSET(decorate)
+ UNSET(focusOnActivate)
+ UNSET(keepAspectRatio)
+ UNSET(nearestNeighbor)
+ UNSET(noAnim)
+ UNSET(noBlur)
+ UNSET(noDim)
+ UNSET(noFocus)
+ UNSET(noMaxSize)
+ UNSET(noShadow)
+ UNSET(noShortcutsInhibit)
+ UNSET(opaque)
+ UNSET(dimAround)
+ UNSET(RGBX)
+ UNSET(syncFullscreen)
+ UNSET(tearing)
+ UNSET(xray)
+ UNSET(renderUnfocused)
+ UNSET(noFollowMouse)
+ UNSET(noScreenShare)
+ UNSET(noVRR)
+ UNSET(persistentSize)
+ UNSET(stayFocused)
+ UNSET(idleInhibitMode)
+ UNSET(borderSize)
+ UNSET(rounding)
+ UNSET(roundingPower)
+ UNSET(scrollMouse)
+ UNSET(scrollTouchpad)
+ UNSET(animationStyle)
+ UNSET(maxSize)
+ UNSET(minSize)
+ UNSET(activeBorderColor)
+ UNSET(inactiveBorderColor)
+
+#undef UNSET
+
+ if (prio == Types::PRIORITY_WINDOW_RULE) {
+ std::erase_if(m_dynamicTags, [props, this](const auto& el) {
+ const bool REMOVE = el.second & props;
+
+ if (REMOVE)
+ m_tagKeeper.removeDynamicTag(el.first);
+
+ return REMOVE;
+ });
+
+ std::erase_if(m_otherProps.props, [props](const auto& el) { return !el.second || el.second->propMask & props; });
+ }
+}
+
+CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyDynamicRule(const SP<CWindowRule>& rule) {
+ SRuleResult result;
+
+ for (const auto& [key, effect] : rule->effects()) {
+ switch (key) {
+ default: {
+ if (key <= WINDOW_RULE_EFFECT_LAST_STATIC) {
+ Debug::log(TRACE, "CWindowRuleApplicator::applyDynamicRule: Skipping effect {}, not dynamic", sc<std::underlying_type_t<eWindowRuleEffect>>(key));
+ break;
+ }
+
+ // custom type, add to our vec
+ if (!m_otherProps.props.contains(key)) {
+ m_otherProps.props.emplace(key,
+ makeUnique<SCustomPropContainer>(SCustomPropContainer{
+ .idx = key,
+ .propMask = rule->getPropertiesMask(),
+ .effect = effect,
+ }));
+ } else {
+ auto& e = m_otherProps.props[key];
+ e->propMask |= rule->getPropertiesMask();
+ e->effect = effect;
+ }
+
+ break;
+ }
+
+ case WINDOW_RULE_EFFECT_NONE: {
+ Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: BUG THIS: WINDOW_RULE_EFFECT_NONE??");
+ break;
+ }
+ case WINDOW_RULE_EFFECT_ROUNDING: {
+ try {
+ m_rounding.first.set(std::stoull(effect), Types::PRIORITY_WINDOW_RULE);
+ m_rounding.second |= rule->getPropertiesMask();
+ } catch (...) { Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: invalid rounding {}", effect); }
+ 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 (...) { Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: invalid rounding_power {}", effect); }
+ break;
+ }
+ case WINDOW_RULE_EFFECT_PERSISTENT_SIZE: {
+ try {
+ m_persistentSize.first.set(std::stoi(effect), Types::PRIORITY_WINDOW_RULE);
+ m_persistentSize.second |= rule->getPropertiesMask();
+ } catch (...) { Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: invalid rounding_power {}", effect); }
+ break;
+ }
+ case WINDOW_RULE_EFFECT_ANIMATION: {
+ m_animationStyle.first.set(effect, 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
+ CGradientValueData activeBorderGradient = {};
+ CGradientValueData inactiveBorderGradient = {};
+ bool active = true;
+ CVarList colorsAndAngles = CVarList(trim(effect.substr(effect.find_first_of(' ') + 1)), 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(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[0]).value_or(0))), Types::PRIORITY_WINDOW_RULE);
+ m_inactiveBorderColor.first =
+ Types::COverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[1]).value_or(0))), Types::PRIORITY_WINDOW_RULE);
+ 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)
+ Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", effect);
+ else if (activeBorderGradient.m_colors.empty())
+ Debug::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) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", effect, e.what()); }
+ 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
+ Debug::log(ERR, "Rule idleinhibit: unknown mode {}", effect);
+ 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) { Debug::log(ERR, "Opacity rule \"{}\" failed with: {}", effect, e.what()); }
+ m_alpha.second = rule->getPropertiesMask();
+ m_alphaInactive.second = rule->getPropertiesMask();
+ m_alphaFullscreen.second = rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_TAG: {
+ m_dynamicTags.emplace_back(std::make_pair<>(effect, rule->getPropertiesMask()));
+ m_tagKeeper.applyTag(effect, true);
+ result.tagsChanged = true;
+ break;
+ }
+ case WINDOW_RULE_EFFECT_MAX_SIZE: {
+ try {
+ static auto PCLAMP_TILED = CConfigValue<Hyprlang::INT>("misc:size_limits_tiled");
+
+ if (!m_window)
+ break;
+
+ if (!m_window->m_isFloating && !sc<bool>(*PCLAMP_TILED))
+ break;
+
+ const auto VEC = configStringToVector2D(effect);
+ if (VEC.x < 1 || VEC.y < 1) {
+ Debug::log(ERR, "Invalid size for maxsize");
+ break;
+ }
+
+ m_maxSize.first = Types::COverridableVar(VEC, Types::PRIORITY_WINDOW_RULE);
+ m_window->clampWindowSize(std::nullopt, m_maxSize.first.value());
+
+ } catch (std::exception& e) { Debug::log(ERR, "maxsize rule \"{}\" failed with: {}", effect, e.what()); }
+ m_maxSize.second = rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_MIN_SIZE: {
+ try {
+ static auto PCLAMP_TILED = CConfigValue<Hyprlang::INT>("misc:size_limits_tiled");
+
+ if (!m_window)
+ break;
+
+ if (!m_window->m_isFloating && !sc<bool>(*PCLAMP_TILED))
+ break;
+
+ const auto VEC = configStringToVector2D(effect);
+ if (VEC.x < 1 || VEC.y < 1) {
+ Debug::log(ERR, "Invalid size for maxsize");
+ break;
+ }
+
+ m_minSize.first = Types::COverridableVar(VEC, Types::PRIORITY_WINDOW_RULE);
+ m_window->clampWindowSize(std::nullopt, m_minSize.first.value());
+ } catch (std::exception& e) { Debug::log(ERR, "minsize rule \"{}\" failed with: {}", effect, e.what()); }
+ m_minSize.second = rule->getPropertiesMask();
+ 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 (...) { Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: invalid border_size {}", effect); }
+ break;
+ }
+ case WINDOW_RULE_EFFECT_ALLOWS_INPUT: {
+ m_allowsInput.first.set(truthy(effect), 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.second |= rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_DECORATE: {
+ m_decorate.first.set(truthy(effect), 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.second |= rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_KEEP_ASPECT_RATIO: {
+ m_keepAspectRatio.first.set(truthy(effect), 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.second |= rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_NO_ANIM: {
+ m_noAnim.first.set(truthy(effect), 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.second |= rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_NO_DIM: {
+ m_noDim.first.set(truthy(effect), 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.second |= rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_NO_FOLLOW_MOUSE: {
+ m_noFollowMouse.first.set(truthy(effect), 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.second |= rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_NO_SHADOW: {
+ m_noShadow.first.set(truthy(effect), 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.second |= rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_OPAQUE: {
+ m_opaque.first.set(truthy(effect), 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.second |= rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_SYNC_FULLSCREEN: {
+ m_syncFullscreen.first.set(truthy(effect), 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.second |= rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_XRAY: {
+ m_xray.first.set(truthy(effect), 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.second |= rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_NO_SCREEN_SHARE: {
+ m_noScreenShare.first.set(truthy(effect), 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.second |= rule->getPropertiesMask();
+ break;
+ }
+ case WINDOW_RULE_EFFECT_STAY_FOCUSED: {
+ m_stayFocused.first.set(truthy(effect), 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 (...) { Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: invalid scroll_mouse {}", effect); }
+ 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 (...) { Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: invalid scroll_touchpad {}", effect); }
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyStaticRule(const SP<CWindowRule>& rule) {
+ for (const auto& [key, effect] : rule->effects()) {
+ switch (key) {
+ default: {
+ Debug::log(TRACE, "CWindowRuleApplicator::applyStaticRule: Skipping effect {}, not static", sc<std::underlying_type_t<eWindowRuleEffect>>(key));
+ break;
+ }
+
+ case WINDOW_RULE_EFFECT_FLOAT: {
+ static_.floating = truthy(effect);
+ break;
+ }
+ case WINDOW_RULE_EFFECT_TILE: {
+ static_.floating = !truthy(effect);
+ break;
+ }
+ case WINDOW_RULE_EFFECT_FULLSCREEN: {
+ static_.fullscreen = truthy(effect);
+ break;
+ }
+ case WINDOW_RULE_EFFECT_MAXIMIZE: {
+ static_.maximize = truthy(effect);
+ 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 (...) { Debug::log(ERR, "CWindowRuleApplicator::applyStaticRule: invalid fullscreen state {}", effect); }
+ break;
+ }
+ case WINDOW_RULE_EFFECT_MOVE: {
+ static_.position = effect;
+ break;
+ }
+ case WINDOW_RULE_EFFECT_SIZE: {
+ static_.size = effect;
+ break;
+ }
+ case WINDOW_RULE_EFFECT_CENTER: {
+ static_.center = truthy(effect);
+ break;
+ }
+ case WINDOW_RULE_EFFECT_PSEUDO: {
+ static_.pseudo = truthy(effect);
+ break;
+ }
+ case WINDOW_RULE_EFFECT_MONITOR: {
+ static_.monitor = effect;
+ break;
+ }
+ case WINDOW_RULE_EFFECT_WORKSPACE: {
+ static_.workspace = effect;
+ break;
+ }
+ case WINDOW_RULE_EFFECT_NOINITIALFOCUS: {
+ static_.noInitialFocus = truthy(effect);
+ break;
+ }
+ case WINDOW_RULE_EFFECT_PIN: {
+ static_.pin = truthy(effect);
+ break;
+ }
+ case WINDOW_RULE_EFFECT_GROUP: {
+ static_.group = effect;
+ break;
+ }
+ case WINDOW_RULE_EFFECT_SUPPRESSEVENT: {
+ CVarList2 varlist(std::string{effect}, 0, 's');
+ for (const auto& e : varlist) {
+ static_.suppressEvent.emplace_back(e);
+ }
+ break;
+ }
+ case WINDOW_RULE_EFFECT_CONTENT: {
+ static_.content = NContentType::fromString(effect);
+ break;
+ }
+ case WINDOW_RULE_EFFECT_NOCLOSEFOR: {
+ try {
+ static_.noCloseFor = std::stoi(effect);
+ } catch (...) { Debug::log(ERR, "CWindowRuleApplicator::applyStaticRule: invalid no close for {}", effect); }
+ break;
+ }
+ }
+ }
+
+ return SRuleResult{};
+}
+
+void CWindowRuleApplicator::readStaticRules() {
+ if (!m_window)
+ return;
+
+ static_ = {};
+
+ std::vector<SP<IRule>> toRemove;
+ bool tagsWereChanged = false;
+
+ for (const auto& r : ruleEngine()->rules()) {
+ if (r->type() != RULE_TYPE_WINDOW)
+ continue;
+
+ auto wr = reinterpretPointerCast<CWindowRule>(r);
+
+ if (!wr->matches(m_window.lock(), true))
+ continue;
+
+ applyStaticRule(wr);
+ const auto RES = applyDynamicRule(wr); // also apply dynamic, because we won't recheck it before layout gets data
+ tagsWereChanged = tagsWereChanged || RES.tagsChanged;
+
+ if (wr->isExecRule())
+ toRemove.emplace_back(wr);
+ }
+
+ for (const auto& wr : toRemove) {
+ ruleEngine()->unregisterRule(wr);
+ }
+
+ // recheck some props people might wanna use for static rules.
+ std::underlying_type_t<eRuleProperty> propsToRecheck = RULE_PROP_NONE;
+ if (tagsWereChanged)
+ propsToRecheck |= RULE_PROP_TAG;
+ if (static_.content != NContentType::CONTENT_TYPE_NONE)
+ propsToRecheck |= RULE_PROP_CONTENT;
+
+ if (propsToRecheck != RULE_PROP_NONE) {
+ for (const auto& r : ruleEngine()->rules()) {
+ if (r->type() != RULE_TYPE_WINDOW)
+ continue;
+
+ if (!(r->getPropertiesMask() & propsToRecheck))
+ continue;
+
+ auto wr = reinterpretPointerCast<CWindowRule>(r);
+
+ if (!wr->matches(m_window.lock(), true))
+ continue;
+
+ applyStaticRule(wr);
+ }
+ }
+}
+
+void CWindowRuleApplicator::propertiesChanged(std::underlying_type_t<eRuleProperty> props) {
+ if (!m_window || !m_window->m_isMapped || m_window->isHidden())
+ return;
+
+ resetProps(props);
+
+ bool needsRelayout = false;
+
+ std::unordered_set<CWindowRuleEffectContainer::storageType> effectsNeedingRecheck;
+ std::unordered_set<SP<CWindowRule>> passedWrs;
+
+ for (const auto& r : ruleEngine()->rules()) {
+ if (r->type() != RULE_TYPE_WINDOW)
+ continue;
+
+ if (!(r->getPropertiesMask() & props))
+ continue;
+
+ auto wr = reinterpretPointerCast<CWindowRule>(r);
+
+ if (!wr->matches(m_window.lock()))
+ continue;
+
+ for (const auto& [type, eff] : wr->effects()) {
+ effectsNeedingRecheck.emplace(type);
+ }
+
+ passedWrs.emplace(std::move(wr));
+ }
+
+ for (const auto& r : ruleEngine()->rules()) {
+ if (r->type() != RULE_TYPE_WINDOW)
+ continue;
+
+ const auto WR = reinterpretPointerCast<CWindowRule>(r);
+
+ if (!(WR->getPropertiesMask() & props) && !setsIntersect(WR->effectsSet(), effectsNeedingRecheck))
+ continue;
+
+ if (!std::ranges::contains(passedWrs, WR) && !WR->matches(m_window.lock()))
+ continue;
+
+ const auto RES = applyDynamicRule(WR);
+ needsRelayout = needsRelayout || RES.needsRelayout;
+ }
+
+ m_window->updateDecorationValues();
+
+ if (needsRelayout)
+ g_pDecorationPositioner->forceRecalcFor(m_window.lock());
+}
diff --git a/src/desktop/rule/windowRule/WindowRuleApplicator.hpp b/src/desktop/rule/windowRule/WindowRuleApplicator.hpp
new file mode 100644
index 00000000..ad80a081
--- /dev/null
+++ b/src/desktop/rule/windowRule/WindowRuleApplicator.hpp
@@ -0,0 +1,148 @@
+#pragma once
+
+#include <unordered_map>
+
+#include "WindowRuleEffectContainer.hpp"
+#include "../../DesktopTypes.hpp"
+#include "../Rule.hpp"
+#include "../../types/OverridableVar.hpp"
+#include "../../../helpers/math/Math.hpp"
+#include "../../../helpers/TagKeeper.hpp"
+#include "../../../config/ConfigDataValues.hpp"
+
+namespace Desktop::Rule {
+ class CWindowRule;
+
+ enum eIdleInhibitMode : uint8_t {
+ IDLEINHIBIT_NONE = 0,
+ IDLEINHIBIT_ALWAYS,
+ IDLEINHIBIT_FULLSCREEN,
+ IDLEINHIBIT_FOCUS
+ };
+
+ class CWindowRuleApplicator {
+ public:
+ CWindowRuleApplicator(PHLWINDOW w);
+ ~CWindowRuleApplicator() = default;
+
+ CWindowRuleApplicator(const CWindowRuleApplicator&) = delete;
+ CWindowRuleApplicator(CWindowRuleApplicator&) = delete;
+ CWindowRuleApplicator(CWindowRuleApplicator&&) = delete;
+
+ void propertiesChanged(std::underlying_type_t<eRuleProperty> props);
+ void resetProps(std::underlying_type_t<eRuleProperty> props, Types::eOverridePriority prio = Types::PRIORITY_WINDOW_RULE);
+ void readStaticRules();
+ void applyStaticRules();
+
+ // static props
+ struct {
+ std::string monitor, workspace, group;
+
+ std::optional<bool> floating;
+
+ bool fullscreen = false;
+ bool maximize = false;
+ bool pseudo = false;
+ bool pin = false;
+ bool noInitialFocus = false;
+
+ std::optional<int> fullscreenStateClient;
+ std::optional<int> fullscreenStateInternal;
+ std::optional<int> center;
+ std::optional<int> content;
+ std::optional<int> noCloseFor;
+
+ std::string size, position;
+
+ std::vector<std::string> suppressEvent;
+ } static_;
+
+ struct SCustomPropContainer {
+ CWindowRuleEffectContainer::storageType idx = WINDOW_RULE_EFFECT_NONE;
+ std::underlying_type_t<eRuleProperty> propMask = RULE_PROP_NONE;
+ std::string effect;
+ };
+
+ // This struct holds props that were dynamically registered. Plugins may read this.
+ struct {
+ std::unordered_map<CWindowRuleEffectContainer::storageType, UP<SCustomPropContainer>> props;
+ } m_otherProps;
+
+#define COMMA ,
+#define DEFINE_PROP(type, name, def) \
+ private: \
+ std::pair<Types::COverridableVar<type>, std::underlying_type_t<eRuleProperty>> m_##name = {def, RULE_PROP_NONE}; \
+ \
+ public: \
+ Types::COverridableVar<type>& name() { \
+ return m_##name.first; \
+ } \
+ void name##Override(const Types::COverridableVar<type>& other) { \
+ m_##name.first = other; \
+ }
+
+ // dynamic props
+ DEFINE_PROP(Types::SAlphaValue, alpha, Types::SAlphaValue{})
+ DEFINE_PROP(Types::SAlphaValue, alphaInactive, Types::SAlphaValue{})
+ DEFINE_PROP(Types::SAlphaValue, alphaFullscreen, Types::SAlphaValue{})
+
+ DEFINE_PROP(bool, allowsInput, false)
+ DEFINE_PROP(bool, decorate, true)
+ DEFINE_PROP(bool, focusOnActivate, false)
+ DEFINE_PROP(bool, keepAspectRatio, false)
+ DEFINE_PROP(bool, nearestNeighbor, false)
+ DEFINE_PROP(bool, noAnim, false)
+ DEFINE_PROP(bool, noBlur, false)
+ DEFINE_PROP(bool, noDim, false)
+ DEFINE_PROP(bool, noFocus, false)
+ DEFINE_PROP(bool, noMaxSize, false)
+ DEFINE_PROP(bool, noShadow, false)
+ DEFINE_PROP(bool, noShortcutsInhibit, false)
+ DEFINE_PROP(bool, opaque, false)
+ DEFINE_PROP(bool, dimAround, false)
+ DEFINE_PROP(bool, RGBX, false)
+ DEFINE_PROP(bool, syncFullscreen, true)
+ DEFINE_PROP(bool, tearing, false)
+ DEFINE_PROP(bool, xray, false)
+ DEFINE_PROP(bool, renderUnfocused, false)
+ DEFINE_PROP(bool, noFollowMouse, false)
+ DEFINE_PROP(bool, noScreenShare, false)
+ DEFINE_PROP(bool, noVRR, false)
+ DEFINE_PROP(bool, persistentSize, false)
+ DEFINE_PROP(bool, stayFocused, false)
+
+ DEFINE_PROP(int, idleInhibitMode, false)
+
+ DEFINE_PROP(Hyprlang::INT, borderSize, {std::string("general:border_size") COMMA sc<Hyprlang::INT>(0) COMMA std::nullopt})
+ DEFINE_PROP(Hyprlang::INT, rounding, {std::string("decoration:rounding") COMMA sc<Hyprlang::INT>(0) COMMA std::nullopt})
+
+ DEFINE_PROP(Hyprlang::FLOAT, roundingPower, {std::string("decoration:rounding_power")})
+ DEFINE_PROP(Hyprlang::FLOAT, scrollMouse, {std::string("input:scroll_factor")})
+ DEFINE_PROP(Hyprlang::FLOAT, scrollTouchpad, {std::string("input:touchpad:scroll_factor")})
+
+ DEFINE_PROP(std::string, animationStyle, std::string(""))
+
+ DEFINE_PROP(Vector2D, maxSize, Vector2D{})
+ DEFINE_PROP(Vector2D, minSize, Vector2D{})
+
+ DEFINE_PROP(CGradientValueData, activeBorderColor, {})
+ DEFINE_PROP(CGradientValueData, inactiveBorderColor, {})
+
+ std::vector<std::pair<std::string, std::underlying_type_t<eRuleProperty>>> m_dynamicTags;
+ CTagKeeper m_tagKeeper;
+
+#undef COMMA
+#undef DEFINE_PROP
+
+ private:
+ PHLWINDOWREF m_window;
+
+ struct SRuleResult {
+ bool needsRelayout = false;
+ bool tagsChanged = false;
+ };
+
+ SRuleResult applyDynamicRule(const SP<CWindowRule>& rule);
+ SRuleResult applyStaticRule(const SP<CWindowRule>& rule);
+ };
+};
diff --git a/src/desktop/rule/windowRule/WindowRuleEffectContainer.cpp b/src/desktop/rule/windowRule/WindowRuleEffectContainer.cpp
new file mode 100644
index 00000000..660bf871
--- /dev/null
+++ b/src/desktop/rule/windowRule/WindowRuleEffectContainer.cpp
@@ -0,0 +1,76 @@
+#include "WindowRuleEffectContainer.hpp"
+
+using namespace Desktop;
+using namespace Desktop::Rule;
+
+//
+SP<CWindowRuleEffectContainer> Rule::windowEffects() {
+ static SP<CWindowRuleEffectContainer> container = makeShared<CWindowRuleEffectContainer>();
+ return container;
+}
+
+static const std::vector<std::string> EFFECT_STRINGS = {
+ "__internal_none", //
+ "float", //
+ "tile", //
+ "fullscreen", //
+ "maximize", //
+ "fullscreen_state", //
+ "move", //
+ "size", //
+ "center", //
+ "pseudo", //
+ "monitor", //
+ "workspace", //
+ "no_initial_focus", //
+ "pin", //
+ "group", //
+ "suppress_event", //
+ "content", //
+ "no_close_for", //
+ "rounding", //
+ "rounding_power", //
+ "persistent_size", //
+ "animation", //
+ "border_color", //
+ "idle_inhibit", //
+ "opacity", //
+ "tag", //
+ "max_size", //
+ "min_size", //
+ "border_size", //
+ "allows_input", //
+ "dim_around", //
+ "decorate", //
+ "focus_on_activate", //
+ "keep_aspect_ratio", //
+ "nearest_neighbor", //
+ "no_anim", //
+ "no_blur", //
+ "no_dim", //
+ "no_focus", //
+ "no_follow_mouse", //
+ "no_max_size", //
+ "no_shadow", //
+ "no_shortcuts_inhibit", //
+ "opaque", //
+ "force_rgbx", //
+ "sync_fullscreen", //
+ "immediate", //
+ "xray", //
+ "render_unfocused", //
+ "no_screen_share", //
+ "no_vrr", //
+ "scroll_mouse", //
+ "scroll_touchpad", //
+ "stay_focused", //
+ "__internal_last_static", //
+};
+
+// This is here so that if we change the rules, we get reminded to update
+// the strings.
+static_assert(WINDOW_RULE_EFFECT_LAST_STATIC == 54);
+
+CWindowRuleEffectContainer::CWindowRuleEffectContainer() : IEffectContainer<eWindowRuleEffect>(std::vector<std::string>{EFFECT_STRINGS}) {
+ ;
+}
diff --git a/src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp b/src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp
new file mode 100644
index 00000000..0827d462
--- /dev/null
+++ b/src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp
@@ -0,0 +1,79 @@
+#pragma once
+
+#include "../effect/EffectContainer.hpp"
+#include "../../../helpers/memory/Memory.hpp"
+
+#pragma once
+
+namespace Desktop::Rule {
+ enum eWindowRuleEffect : uint8_t {
+ WINDOW_RULE_EFFECT_NONE = 0,
+
+ // static
+ WINDOW_RULE_EFFECT_FLOAT,
+ WINDOW_RULE_EFFECT_TILE,
+ WINDOW_RULE_EFFECT_FULLSCREEN,
+ WINDOW_RULE_EFFECT_MAXIMIZE,
+ WINDOW_RULE_EFFECT_FULLSCREENSTATE,
+ WINDOW_RULE_EFFECT_MOVE,
+ WINDOW_RULE_EFFECT_SIZE,
+ WINDOW_RULE_EFFECT_CENTER,
+ WINDOW_RULE_EFFECT_PSEUDO,
+ WINDOW_RULE_EFFECT_MONITOR,
+ WINDOW_RULE_EFFECT_WORKSPACE,
+ WINDOW_RULE_EFFECT_NOINITIALFOCUS,
+ WINDOW_RULE_EFFECT_PIN,
+ WINDOW_RULE_EFFECT_GROUP,
+ WINDOW_RULE_EFFECT_SUPPRESSEVENT,
+ WINDOW_RULE_EFFECT_CONTENT,
+ WINDOW_RULE_EFFECT_NOCLOSEFOR,
+
+ // dynamic
+ WINDOW_RULE_EFFECT_ROUNDING,
+ WINDOW_RULE_EFFECT_ROUNDING_POWER,
+ WINDOW_RULE_EFFECT_PERSISTENT_SIZE,
+ WINDOW_RULE_EFFECT_ANIMATION,
+ WINDOW_RULE_EFFECT_BORDER_COLOR,
+ WINDOW_RULE_EFFECT_IDLE_INHIBIT,
+ WINDOW_RULE_EFFECT_OPACITY,
+ WINDOW_RULE_EFFECT_TAG,
+ WINDOW_RULE_EFFECT_MAX_SIZE,
+ WINDOW_RULE_EFFECT_MIN_SIZE,
+ WINDOW_RULE_EFFECT_BORDER_SIZE,
+ WINDOW_RULE_EFFECT_ALLOWS_INPUT,
+ WINDOW_RULE_EFFECT_DIM_AROUND,
+ WINDOW_RULE_EFFECT_DECORATE,
+ WINDOW_RULE_EFFECT_FOCUS_ON_ACTIVATE,
+ WINDOW_RULE_EFFECT_KEEP_ASPECT_RATIO,
+ WINDOW_RULE_EFFECT_NEAREST_NEIGHBOR,
+ WINDOW_RULE_EFFECT_NO_ANIM,
+ WINDOW_RULE_EFFECT_NO_BLUR,
+ WINDOW_RULE_EFFECT_NO_DIM,
+ WINDOW_RULE_EFFECT_NO_FOCUS,
+ WINDOW_RULE_EFFECT_NO_FOLLOW_MOUSE,
+ WINDOW_RULE_EFFECT_NO_MAX_SIZE,
+ WINDOW_RULE_EFFECT_NO_SHADOW,
+ WINDOW_RULE_EFFECT_NO_SHORTCUTS_INHIBIT,
+ WINDOW_RULE_EFFECT_OPAQUE,
+ WINDOW_RULE_EFFECT_FORCE_RGBX,
+ WINDOW_RULE_EFFECT_SYNC_FULLSCREEN,
+ WINDOW_RULE_EFFECT_IMMEDIATE,
+ WINDOW_RULE_EFFECT_XRAY,
+ WINDOW_RULE_EFFECT_RENDER_UNFOCUSED,
+ WINDOW_RULE_EFFECT_NO_SCREEN_SHARE,
+ WINDOW_RULE_EFFECT_NO_VRR,
+ WINDOW_RULE_EFFECT_SCROLL_MOUSE,
+ WINDOW_RULE_EFFECT_SCROLL_TOUCHPAD,
+ WINDOW_RULE_EFFECT_STAY_FOCUSED,
+
+ WINDOW_RULE_EFFECT_LAST_STATIC,
+ };
+
+ class CWindowRuleEffectContainer : public IEffectContainer<eWindowRuleEffect> {
+ public:
+ CWindowRuleEffectContainer();
+ virtual ~CWindowRuleEffectContainer() = default;
+ };
+
+ SP<CWindowRuleEffectContainer> windowEffects();
+}; \ No newline at end of file
diff --git a/src/desktop/types/OverridableVar.hpp b/src/desktop/types/OverridableVar.hpp
new file mode 100644
index 00000000..9ecfc890
--- /dev/null
+++ b/src/desktop/types/OverridableVar.hpp
@@ -0,0 +1,153 @@
+#pragma once
+
+#include <cstdint>
+#include <type_traits>
+#include <any>
+#include <map>
+#include "../../config/ConfigValue.hpp"
+
+namespace Desktop::Types {
+
+ struct SAlphaValue {
+ float alpha = 1.F;
+ bool overridden = false;
+
+ float applyAlpha(float a) const {
+ if (overridden)
+ return alpha;
+ else
+ return alpha * a;
+ };
+ };
+
+ enum eOverridePriority : uint8_t {
+ PRIORITY_LAYOUT = 0,
+ PRIORITY_WORKSPACE_RULE,
+ PRIORITY_WINDOW_RULE,
+ PRIORITY_SET_PROP,
+ };
+
+ template <typename T>
+ T clampOptional(T const& value, std::optional<T> const& min, std::optional<T> const& max) {
+ return std::clamp(value, min.value_or(std::numeric_limits<T>::min()), max.value_or(std::numeric_limits<T>::max()));
+ }
+
+ template <typename T, bool Extended = std::is_same_v<T, bool> || std::is_same_v<T, Hyprlang::INT> || std::is_same_v<T, Hyprlang::FLOAT>>
+ class COverridableVar {
+ public:
+ COverridableVar(T const& value, eOverridePriority priority) {
+ m_values[priority] = value;
+ }
+
+ COverridableVar(T const& value) : m_defaultValue{value} {}
+ COverridableVar(T const& value, std::optional<T> const& min, std::optional<T> const& max = std::nullopt) : m_defaultValue{value}, m_minValue{min}, m_maxValue{max} {}
+ COverridableVar(std::string const& value)
+ requires(Extended && !std::is_same_v<T, bool>)
+ : m_configValue(SP<CConfigValue<T>>(new CConfigValue<T>(value))) {}
+ COverridableVar(std::string const& value, std::optional<T> const& min, std::optional<T> const& max = std::nullopt)
+ requires(Extended && !std::is_same_v<T, bool>)
+ : m_minValue(min), m_maxValue(max), m_configValue(SP<CConfigValue<T>>(new CConfigValue<T>(value))) {}
+
+ COverridableVar() = default;
+ ~COverridableVar() = default;
+
+ COverridableVar& operator=(COverridableVar<T> const& other) {
+ // Self-assignment check
+ if (this == &other)
+ return *this;
+
+ for (auto const& value : other.m_values) {
+ if constexpr (Extended && !std::is_same_v<T, bool>)
+ m_values[value.first] = clampOptional(value.second, m_minValue, m_maxValue);
+ else
+ m_values[value.first] = value.second;
+ }
+
+ return *this;
+ }
+
+ void set(T value, eOverridePriority priority) {
+ m_values[priority] = value;
+ }
+
+ void unset(eOverridePriority priority) {
+ m_values.erase(priority);
+ }
+
+ bool hasValue() const {
+ return !m_values.empty();
+ }
+
+ T value() const {
+ if (!m_values.empty())
+ return std::prev(m_values.end())->second;
+ else
+ throw std::bad_optional_access();
+ }
+
+ T valueOr(T const& other) const {
+ if (hasValue())
+ return value();
+ else
+ return other;
+ }
+
+ T valueOrDefault() const
+ requires(Extended && !std::is_same_v<T, bool>)
+ {
+ if (hasValue())
+ return value();
+ else if (m_defaultValue.has_value())
+ return m_defaultValue.value();
+ else
+ return **std::any_cast<SP<CConfigValue<T>>>(m_configValue);
+ }
+
+ T valueOrDefault() const
+ requires(!Extended || std::is_same_v<T, bool>)
+ {
+ if (hasValue())
+ return value();
+ else if (!m_defaultValue.has_value())
+ throw std::bad_optional_access();
+ else
+ return m_defaultValue.value();
+ }
+
+ eOverridePriority getPriority() const {
+ if (!m_values.empty())
+ return std::prev(m_values.end())->first;
+ else
+ throw std::bad_optional_access();
+ }
+
+ void increment(T const& other, eOverridePriority priority) {
+ if constexpr (std::is_same_v<T, bool>)
+ m_values[priority] = valueOr(false) ^ other;
+ else
+ m_values[priority] = clampOptional(valueOrDefault() + other, m_minValue, m_maxValue);
+ }
+
+ void matchOptional(std::optional<T> const& optValue, eOverridePriority priority) {
+ if (optValue.has_value())
+ m_values[priority] = optValue.value();
+ else
+ unset(priority);
+ }
+
+ operator std::optional<T>() {
+ if (hasValue())
+ return value();
+ else
+ return std::nullopt;
+ }
+
+ private:
+ std::map<eOverridePriority, T> m_values;
+ std::optional<T> m_defaultValue; // used for toggling, so required for bool
+ std::optional<T> m_minValue;
+ std::optional<T> m_maxValue;
+ std::any m_configValue; // only there for select variables
+ };
+
+} \ No newline at end of file
diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp
index 9e3b49d4..dfbb9f2e 100644
--- a/src/events/Windows.cpp
+++ b/src/events/Windows.cpp
@@ -135,31 +135,23 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_X11ShouldntFocus = PWINDOW->m_X11ShouldntFocus || (PWINDOW->m_isX11 && PWINDOW->isX11OverrideRedirect() && !PWINDOW->m_xwaylandSurface->wantsFocus());
// window rules
- PWINDOW->m_matchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false);
std::optional<eFullscreenMode> requestedInternalFSMode, requestedClientFSMode;
std::optional<SFullscreenState> requestedFSState;
if (PWINDOW->m_wantsInitialFullscreen || (PWINDOW->m_isX11 && PWINDOW->m_xwaylandSurface->m_fullscreen))
requestedClientFSMode = FSMODE_FULLSCREEN;
MONITORID requestedFSMonitor = PWINDOW->m_wantsInitialFullscreenMonitor;
- for (auto const& r : PWINDOW->m_matchedRules) {
- switch (r->m_ruleType) {
- case CWindowRule::RULE_MONITOR: {
- try {
- const auto MONITORSTR = trim(r->m_rule.substr(r->m_rule.find(' ')));
-
- if (MONITORSTR == "unset")
- PWINDOW->m_monitor = PMONITOR;
- else {
- const auto MONITOR = g_pCompositor->getMonitorFromString(MONITORSTR);
-
- if (MONITOR)
- PWINDOW->m_monitor = MONITOR;
- else {
- Debug::log(ERR, "No monitor in monitor {} rule", MONITORSTR);
- continue;
- }
- }
+ PWINDOW->m_ruleApplicator->readStaticRules();
+ {
+ if (!PWINDOW->m_ruleApplicator->static_.monitor.empty()) {
+ const auto& MONITORSTR = PWINDOW->m_ruleApplicator->static_.monitor;
+ if (MONITORSTR == "unset")
+ PWINDOW->m_monitor = PMONITOR;
+ else {
+ const auto MONITOR = g_pCompositor->getMonitorFromString(MONITORSTR);
+
+ if (MONITOR) {
+ PWINDOW->m_monitor = MONITOR;
const auto PMONITORFROMID = PWINDOW->m_monitor.lock();
@@ -172,100 +164,73 @@ void Events::listener_mapWindow(void* owner, void* data) {
Debug::log(LOG, "Rule monitor, applying to {:mw}", PWINDOW);
requestedFSMonitor = MONITOR_INVALID;
- } catch (std::exception& e) { Debug::log(ERR, "Rule monitor failed, rule: {} -> {} | err: {}", r->m_rule, r->m_value, e.what()); }
- break;
+ } else
+ Debug::log(ERR, "No monitor in monitor {} rule", MONITORSTR);
}
- case CWindowRule::RULE_WORKSPACE: {
- // check if it isn't unset
- const auto WORKSPACERQ = r->m_rule.substr(r->m_rule.find_first_of(' ') + 1);
+ }
- if (WORKSPACERQ == "unset")
- requestedWorkspace = "";
- else
- requestedWorkspace = WORKSPACERQ;
+ if (!PWINDOW->m_ruleApplicator->static_.workspace.empty()) {
+ const auto WORKSPACERQ = PWINDOW->m_ruleApplicator->static_.workspace;
- const auto JUSTWORKSPACE = WORKSPACERQ.contains(' ') ? WORKSPACERQ.substr(0, WORKSPACERQ.find_first_of(' ')) : WORKSPACERQ;
+ if (WORKSPACERQ == "unset")
+ requestedWorkspace = "";
+ else
+ requestedWorkspace = WORKSPACERQ;
- if (JUSTWORKSPACE == PWORKSPACE->m_name || JUSTWORKSPACE == "name:" + PWORKSPACE->m_name)
- requestedWorkspace = "";
+ const auto JUSTWORKSPACE = WORKSPACERQ.contains(' ') ? WORKSPACERQ.substr(0, WORKSPACERQ.find_first_of(' ')) : WORKSPACERQ;
- Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, r->m_value);
- requestedFSMonitor = MONITOR_INVALID;
- break;
- }
- case CWindowRule::RULE_FLOAT: {
- PWINDOW->m_isFloating = true;
- break;
- }
- case CWindowRule::RULE_TILE: {
- PWINDOW->m_isFloating = false;
- break;
- }
- case CWindowRule::RULE_PSEUDO: {
- PWINDOW->m_isPseudotiled = true;
- break;
- }
- case CWindowRule::RULE_NOINITIALFOCUS: {
- PWINDOW->m_noInitialFocus = true;
- break;
- }
- case CWindowRule::RULE_FULLSCREENSTATE: {
- const auto ARGS = CVarList(r->m_rule.substr(r->m_rule.find_first_of(' ') + 1), 2, ' ');
- int internalMode, clientMode;
- try {
- internalMode = std::stoi(ARGS[0]);
- } catch (std::exception& e) { internalMode = 0; }
- try {
- clientMode = std::stoi(ARGS[1]);
- } catch (std::exception& e) { clientMode = 0; }
- requestedFSState = SFullscreenState{.internal = sc<eFullscreenMode>(internalMode), .client = sc<eFullscreenMode>(clientMode)};
- break;
- }
- case CWindowRule::RULE_SUPPRESSEVENT: {
- CVarList vars(r->m_rule, 0, 's', true);
- for (size_t i = 1; i < vars.size(); ++i) {
- if (vars[i] == "fullscreen")
- PWINDOW->m_suppressedEvents |= SUPPRESS_FULLSCREEN;
- else if (vars[i] == "maximize")
- PWINDOW->m_suppressedEvents |= SUPPRESS_MAXIMIZE;
- else if (vars[i] == "activate")
- PWINDOW->m_suppressedEvents |= SUPPRESS_ACTIVATE;
- else if (vars[i] == "activatefocus")
- PWINDOW->m_suppressedEvents |= SUPPRESS_ACTIVATE_FOCUSONLY;
- else if (vars[i] == "fullscreenoutput")
- PWINDOW->m_suppressedEvents |= SUPPRESS_FULLSCREEN_OUTPUT;
- else
- Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", vars[i]);
- }
- break;
- }
- case CWindowRule::RULE_PIN: {
- PWINDOW->m_pinned = true;
- break;
- }
- case CWindowRule::RULE_FULLSCREEN: {
- requestedInternalFSMode = FSMODE_FULLSCREEN;
- break;
- }
- case CWindowRule::RULE_MAXIMIZE: {
- requestedInternalFSMode = FSMODE_MAXIMIZED;
- break;
- }
- case CWindowRule::RULE_STAYFOCUSED: {
- PWINDOW->m_stayFocused = true;
- break;
+ if (JUSTWORKSPACE == PWORKSPACE->m_name || JUSTWORKSPACE == "name:" + PWORKSPACE->m_name)
+ requestedWorkspace = "";
+
+ Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, PWINDOW->m_ruleApplicator->static_.workspace);
+ requestedFSMonitor = MONITOR_INVALID;
+ }
+
+ if (PWINDOW->m_ruleApplicator->static_.floating.has_value())
+ PWINDOW->m_isFloating = PWINDOW->m_ruleApplicator->static_.floating.value();
+
+ if (PWINDOW->m_ruleApplicator->static_.pseudo)
+ PWINDOW->m_isPseudotiled = true;
+
+ if (PWINDOW->m_ruleApplicator->static_.noInitialFocus)
+ PWINDOW->m_noInitialFocus = true;
+
+ if (PWINDOW->m_ruleApplicator->static_.fullscreenStateClient || PWINDOW->m_ruleApplicator->static_.fullscreenStateInternal) {
+ requestedFSState = SFullscreenState{
+ .internal = sc<eFullscreenMode>(PWINDOW->m_ruleApplicator->static_.fullscreenStateInternal.value_or(0)),
+ .client = sc<eFullscreenMode>(PWINDOW->m_ruleApplicator->static_.fullscreenStateClient.value_or(0)),
+ };
+ }
+
+ if (!PWINDOW->m_ruleApplicator->static_.suppressEvent.empty()) {
+ for (const auto& var : PWINDOW->m_ruleApplicator->static_.suppressEvent) {
+ if (var == "fullscreen")
+ PWINDOW->m_suppressedEvents |= SUPPRESS_FULLSCREEN;
+ else if (var == "maximize")
+ PWINDOW->m_suppressedEvents |= SUPPRESS_MAXIMIZE;
+ else if (var == "activate")
+ PWINDOW->m_suppressedEvents |= SUPPRESS_ACTIVATE;
+ else if (var == "activatefocus")
+ PWINDOW->m_suppressedEvents |= SUPPRESS_ACTIVATE_FOCUSONLY;
+ else if (var == "fullscreenoutput")
+ PWINDOW->m_suppressedEvents |= SUPPRESS_FULLSCREEN_OUTPUT;
+ else
+ Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", var);
}
- case CWindowRule::RULE_GROUP: {
- if (PWINDOW->m_groupRules & GROUP_OVERRIDE)
- continue;
-
- // `group` is a shorthand of `group set`
- if (trim(r->m_rule) == "group") {
- PWINDOW->m_groupRules |= GROUP_SET;
- continue;
- }
+ }
+
+ if (PWINDOW->m_ruleApplicator->static_.pin)
+ PWINDOW->m_pinned = true;
+
+ if (PWINDOW->m_ruleApplicator->static_.fullscreen)
+ requestedInternalFSMode = FSMODE_FULLSCREEN;
- CVarList vars(r->m_rule, 0, 's');
+ if (PWINDOW->m_ruleApplicator->static_.maximize)
+ requestedInternalFSMode = FSMODE_MAXIMIZED;
+
+ if (!PWINDOW->m_ruleApplicator->static_.group.empty()) {
+ if (!(PWINDOW->m_groupRules & GROUP_OVERRIDE) && trim(PWINDOW->m_ruleApplicator->static_.group) != "group") {
+ CVarList2 vars(std::string{PWINDOW->m_ruleApplicator->static_.group}, 0, 's');
std::string vPrev = "";
for (auto const& v : vars) {
@@ -302,26 +267,14 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
vPrev = v;
}
- break;
- }
- case CWindowRule::RULE_CONTENT: {
- const CVarList VARS(r->m_rule, 0, ' ');
- try {
- PWINDOW->setContentType(NContentType::fromString(VARS[1]));
- } catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->m_rule, e.what()); }
- break;
}
- case CWindowRule::RULE_NOCLOSEFOR: {
- const CVarList VARS(r->m_rule, 0, ' ');
- try {
- PWINDOW->m_closeableSince = Time::steadyNow() + std::chrono::milliseconds(std::stoull(VARS[1]));
- } catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->m_rule, e.what()); }
- break;
- }
- default: break;
}
- PWINDOW->applyDynamicRule(r);
+ if (PWINDOW->m_ruleApplicator->static_.content)
+ PWINDOW->setContentType(sc<NContentType::eContentType>(PWINDOW->m_ruleApplicator->static_.content.value()));
+
+ if (PWINDOW->m_ruleApplicator->static_.noCloseFor)
+ PWINDOW->m_closeableSince = Time::steadyNow() + std::chrono::milliseconds(PWINDOW->m_ruleApplicator->static_.noCloseFor.value());
}
// make it uncloseable if it's a Hyprland dialog
@@ -333,7 +286,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (PWINDOW->m_pinned && !PWINDOW->m_isFloating)
PWINDOW->m_pinned = false;
- CVarList WORKSPACEARGS = CVarList(requestedWorkspace, 0, ' ');
+ CVarList2 WORKSPACEARGS = CVarList2(std::move(requestedWorkspace), 0, ' ', false, false);
if (!WORKSPACEARGS[0].empty()) {
WORKSPACEID requestedWorkspaceID;
@@ -420,140 +373,31 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW);
PWINDOW->m_createdOverFullscreen = true;
- // size and move rules
- for (auto const& r : PWINDOW->m_matchedRules) {
- switch (r->m_ruleType) {
- case CWindowRule::RULE_SIZE: {
- try {
- auto stringToFloatClamp = [](const std::string& VALUE, const float CURR, const float REL) {
- if (VALUE.starts_with('<'))
- return std::min(CURR, stringToPercentage(VALUE.substr(1, VALUE.length() - 1), REL));
- else if (VALUE.starts_with('>'))
- return std::max(CURR, stringToPercentage(VALUE.substr(1, VALUE.length() - 1), REL));
-
- return stringToPercentage(VALUE, REL);
- };
-
- const auto VALUE = r->m_rule.substr(r->m_rule.find(' ') + 1);
- const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
- const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
-
- const auto MAXSIZE = PWINDOW->requestedMaxSize();
-
- const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, PMONITOR->m_size.x) :
- stringToFloatClamp(SIZEXSTR, PWINDOW->m_realSize->goal().x, PMONITOR->m_size.x);
-
- const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, PMONITOR->m_size.y) :
- stringToFloatClamp(SIZEYSTR, PWINDOW->m_realSize->goal().y, PMONITOR->m_size.y);
-
- Debug::log(LOG, "Rule size, applying to {}", PWINDOW);
-
- PWINDOW->clampWindowSize(Vector2D{SIZEXSTR.starts_with("<") ? 0 : SIZEX, SIZEYSTR.starts_with("<") ? 0 : SIZEY}, Vector2D{SIZEX, SIZEY});
-
- PWINDOW->setHidden(false);
- } catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r->m_rule, r->m_value); }
- break;
- }
- case CWindowRule::RULE_MOVE: {
- try {
- auto value = r->m_rule.substr(r->m_rule.find(' ') + 1);
-
- const bool ONSCREEN = value.starts_with("onscreen");
-
- if (ONSCREEN)
- value = value.substr(value.find_first_of(' ') + 1);
-
- const bool CURSOR = value.starts_with("cursor");
-
- if (CURSOR)
- value = value.substr(value.find_first_of(' ') + 1);
-
- const auto POSXSTR = value.substr(0, value.find(' '));
- const auto POSYSTR = value.substr(value.find(' ') + 1);
-
- int posX = 0;
- int posY = 0;
-
- if (POSXSTR.starts_with("100%-")) {
- const bool subtractWindow = POSXSTR.starts_with("100%-w-");
- const auto POSXRAW = (subtractWindow) ? POSXSTR.substr(7) : POSXSTR.substr(5);
- posX =
- PMONITOR->m_size.x - (!POSXRAW.contains('%') ? std::stoi(POSXRAW) : std::stof(POSXRAW.substr(0, POSXRAW.length() - 1)) * 0.01 * PMONITOR->m_size.x);
-
- if (subtractWindow)
- posX -= PWINDOW->m_realSize->goal().x;
-
- if (CURSOR)
- Debug::log(ERR, "Cursor is not compatible with 100%-, ignoring cursor!");
- } else if (!CURSOR) {
- posX = !POSXSTR.contains('%') ? std::stoi(POSXSTR) : std::stof(POSXSTR.substr(0, POSXSTR.length() - 1)) * 0.01 * PMONITOR->m_size.x;
- } else {
- // cursor
- if (POSXSTR == "cursor") {
- posX = g_pInputManager->getMouseCoordsInternal().x - PMONITOR->m_position.x;
- } else {
- posX = g_pInputManager->getMouseCoordsInternal().x - PMONITOR->m_position.x +
- (!POSXSTR.contains('%') ? std::stoi(POSXSTR) : std::stof(POSXSTR.substr(0, POSXSTR.length() - 1)) * 0.01 * PWINDOW->m_realSize->goal().x);
- }
- }
-
- if (POSYSTR.starts_with("100%-")) {
- const bool subtractWindow = POSYSTR.starts_with("100%-h-");
- const auto POSYRAW = (subtractWindow) ? POSYSTR.substr(7) : POSYSTR.substr(5);
- posY =
- PMONITOR->m_size.y - (!POSYRAW.contains('%') ? std::stoi(POSYRAW) : std::stof(POSYRAW.substr(0, POSYRAW.length() - 1)) * 0.01 * PMONITOR->m_size.y);
-
- if (subtractWindow)
- posY -= PWINDOW->m_realSize->goal().y;
-
- if (CURSOR)
- Debug::log(ERR, "Cursor is not compatible with 100%-, ignoring cursor!");
- } else if (!CURSOR) {
- posY = !POSYSTR.contains('%') ? std::stoi(POSYSTR) : std::stof(POSYSTR.substr(0, POSYSTR.length() - 1)) * 0.01 * PMONITOR->m_size.y;
- } else {
- // cursor
- if (POSYSTR == "cursor") {
- posY = g_pInputManager->getMouseCoordsInternal().y - PMONITOR->m_position.y;
- } else {
- posY = g_pInputManager->getMouseCoordsInternal().y - PMONITOR->m_position.y +
- (!POSYSTR.contains('%') ? std::stoi(POSYSTR) : std::stof(POSYSTR.substr(0, POSYSTR.length() - 1)) * 0.01 * PWINDOW->m_realSize->goal().y);
- }
- }
-
- if (ONSCREEN) {
- int borderSize = PWINDOW->getRealBorderSize();
-
- posX = std::clamp(posX, sc<int>(PMONITOR->m_reservedTopLeft.x + borderSize),
- std::max(sc<int>(PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x - PWINDOW->m_realSize->goal().x - borderSize),
- sc<int>(PMONITOR->m_reservedTopLeft.x + borderSize + 1)));
-
- posY = std::clamp(posY, sc<int>(PMONITOR->m_reservedTopLeft.y + borderSize),
- std::max(sc<int>(PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y - PWINDOW->m_realSize->goal().y - borderSize),
- sc<int>(PMONITOR->m_reservedTopLeft.y + borderSize + 1)));
- }
-
- Debug::log(LOG, "Rule move, applying to {}", PWINDOW);
-
- *PWINDOW->m_realPosition = Vector2D(posX, posY) + PMONITOR->m_position;
-
- PWINDOW->setHidden(false);
- } catch (...) { Debug::log(LOG, "Rule move failed, rule: {} -> {}", r->m_rule, r->m_value); }
- break;
- }
- case CWindowRule::RULE_CENTER: {
- auto RESERVEDOFFSET = Vector2D();
- const auto ARGS = CVarList(r->m_rule, 2, ' ');
- if (ARGS[1] == "1")
- RESERVEDOFFSET = (PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight) / 2.f;
-
- *PWINDOW->m_realPosition = PMONITOR->middle() - PWINDOW->m_realSize->goal() / 2.f + RESERVEDOFFSET;
- break;
- }
+ if (!PWINDOW->m_ruleApplicator->static_.size.empty()) {
+ const auto COMPUTED = PWINDOW->calculateExpression(PWINDOW->m_ruleApplicator->static_.size);
+ if (!COMPUTED)
+ Debug::log(ERR, "failed to parse {} as an expression", PWINDOW->m_ruleApplicator->static_.size);
+ else {
+ *PWINDOW->m_realSize = *COMPUTED;
+ PWINDOW->setHidden(false);
+ }
+ }
- default: break;
+ if (!PWINDOW->m_ruleApplicator->static_.position.empty()) {
+ const auto COMPUTED = PWINDOW->calculateExpression(PWINDOW->m_ruleApplicator->static_.position);
+ if (!COMPUTED)
+ Debug::log(ERR, "failed to parse {} as an expression", PWINDOW->m_ruleApplicator->static_.position);
+ else {
+ *PWINDOW->m_realPosition = *COMPUTED + PMONITOR->m_position;
+ PWINDOW->setHidden(false);
}
}
+ if (PWINDOW->m_ruleApplicator->static_.center) {
+ auto RESERVEDOFFSET = (PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight) / 2.f;
+ *PWINDOW->m_realPosition = PMONITOR->middle() - PWINDOW->m_realSize->goal() / 2.f + RESERVEDOFFSET;
+ }
+
// set the pseudo size to the GOAL of our current size
// because the windows are animated on RealSize
PWINDOW->m_pseudoSize = PWINDOW->m_realSize->goal();
@@ -564,28 +408,15 @@ void Events::listener_mapWindow(void* owner, void* data) {
bool setPseudo = false;
- for (auto const& r : PWINDOW->m_matchedRules) {
- if (r->m_ruleType != CWindowRule::RULE_SIZE)
- continue;
-
- try {
- const auto VALUE = r->m_rule.substr(r->m_rule.find(' ') + 1);
- const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
- const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
-
- const auto MAXSIZE = PWINDOW->requestedMaxSize();
-
- const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, PMONITOR->m_size.x) : stringToPercentage(SIZEXSTR, PMONITOR->m_size.x);
-
- const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, PMONITOR->m_size.y) : stringToPercentage(SIZEYSTR, PMONITOR->m_size.y);
-
- Debug::log(LOG, "Rule size (tiled), applying to {}", PWINDOW);
-
+ if (!PWINDOW->m_ruleApplicator->static_.size.empty()) {
+ const auto COMPUTED = PWINDOW->calculateExpression(PWINDOW->m_ruleApplicator->static_.size);
+ if (!COMPUTED)
+ Debug::log(ERR, "failed to parse {} as an expression", PWINDOW->m_ruleApplicator->static_.size);
+ else {
setPseudo = true;
- PWINDOW->m_pseudoSize = Vector2D(SIZEX, SIZEY);
-
+ PWINDOW->m_pseudoSize = *COMPUTED;
PWINDOW->setHidden(false);
- } catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r->m_rule, r->m_value); }
+ }
}
if (!setPseudo)
@@ -594,10 +425,10 @@ void Events::listener_mapWindow(void* owner, void* data) {
const auto PFOCUSEDWINDOWPREV = g_pCompositor->m_lastWindow.lock();
- if (PWINDOW->m_windowData.allowsInput.valueOrDefault()) { // if default value wasn't set to false getPriority() would throw an exception
- PWINDOW->m_windowData.noFocus = CWindowOverridableVar(false, PWINDOW->m_windowData.allowsInput.getPriority());
- PWINDOW->m_noInitialFocus = false;
- PWINDOW->m_X11ShouldntFocus = false;
+ if (PWINDOW->m_ruleApplicator->allowsInput().valueOrDefault()) { // if default value wasn't set to false getPriority() would throw an exception
+ PWINDOW->m_ruleApplicator->noFocusOverride(Desktop::Types::COverridableVar(false, PWINDOW->m_ruleApplicator->allowsInput().getPriority()));
+ PWINDOW->m_noInitialFocus = false;
+ PWINDOW->m_X11ShouldntFocus = false;
}
// check LS focus grab
@@ -615,12 +446,12 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pCompositor->setWindowFullscreenInternal(PWINDOW->m_workspace->getFullscreenWindow(), FSMODE_NONE);
}
- if (!PWINDOW->m_windowData.noFocus.valueOrDefault() && !PWINDOW->m_noInitialFocus &&
+ if (!PWINDOW->m_ruleApplicator->noFocus().valueOrDefault() && !PWINDOW->m_noInitialFocus &&
(!PWINDOW->isX11OverrideRedirect() || (PWINDOW->m_isX11 && PWINDOW->m_xwaylandSurface->wantsFocus())) && !workspaceSilent && (!PFORCEFOCUS || PFORCEFOCUS == PWINDOW) &&
!g_pInputManager->isConstrained()) {
g_pCompositor->focusWindow(PWINDOW);
PWINDOW->m_activeInactiveAlpha->setValueAndWarp(*PACTIVEALPHA);
- PWINDOW->m_dimPercent->setValueAndWarp(PWINDOW->m_windowData.noDim.valueOrDefault() ? 0.f : *PDIMSTRENGTH);
+ PWINDOW->m_dimPercent->setValueAndWarp(PWINDOW->m_ruleApplicator->noDim().valueOrDefault() ? 0.f : *PDIMSTRENGTH);
} else {
PWINDOW->m_activeInactiveAlpha->setValueAndWarp(*PINACTIVEALPHA);
PWINDOW->m_dimPercent->setValueAndWarp(0);
@@ -639,9 +470,9 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_realPosition->warp();
PWINDOW->m_realSize->warp();
if (requestedFSState.has_value()) {
- PWINDOW->m_windowData.syncFullscreen = CWindowOverridableVar(false, PRIORITY_WINDOW_RULE);
+ PWINDOW->m_ruleApplicator->syncFullscreenOverride(Desktop::Types::COverridableVar(false, Desktop::Types::PRIORITY_WINDOW_RULE));
g_pCompositor->setWindowFullscreenState(PWINDOW, requestedFSState.value());
- } else if (requestedInternalFSMode.has_value() && requestedClientFSMode.has_value() && !PWINDOW->m_windowData.syncFullscreen.valueOrDefault())
+ } else if (requestedInternalFSMode.has_value() && requestedClientFSMode.has_value() && !PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault())
g_pCompositor->setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = requestedInternalFSMode.value(), .client = requestedClientFSMode.value()});
else if (requestedInternalFSMode.has_value())
g_pCompositor->setWindowFullscreenInternal(PWINDOW, requestedInternalFSMode.value());
@@ -653,6 +484,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pInputManager->recheckIdleInhibitorStatus();
PWINDOW->updateToplevel();
+ PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ALL);
if (workspaceSilent) {
if (validMapped(PFOCUSEDWINDOWPREV)) {
@@ -689,7 +521,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_realSize->setCallbackOnEnd(setVector2DAnimToMove);
// recalc the values for this window
- g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
+ PWINDOW->updateDecorationValues();
// avoid this window being visible
if (PWORKSPACE->m_hasFullscreenWindow && !PWINDOW->isFullscreen() && !PWINDOW->m_isFloating)
PWINDOW->m_alpha->setValueAndWarp(0.f);
@@ -736,8 +568,7 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", std::format("{:x}", PWINDOW)});
EMIT_HOOK_EVENT("closeWindow", PWINDOW);
- if (PWINDOW->m_isFloating && !PWINDOW->m_isX11 &&
- std::ranges::any_of(PWINDOW->m_matchedRules, [](const auto& r) { return r->m_ruleType == CWindowRule::RULE_PERSISTENTSIZE; })) {
+ if (PWINDOW->m_isFloating && !PWINDOW->m_isX11 && PWINDOW->m_ruleApplicator->persistentSize().valueOrDefault()) {
Debug::log(LOG, "storing floating size {}x{} for window {}::{} on close", PWINDOW->m_realSize->value().x, PWINDOW->m_realSize->value().y, PWINDOW->m_class,
PWINDOW->m_title);
g_pConfigManager->storeFloatingSize(PWINDOW, PWINDOW->m_realSize->value());
diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp
index 4cf3c671..d11e1be6 100644
--- a/src/helpers/MiscFunctions.cpp
+++ b/src/helpers/MiscFunctions.cpp
@@ -975,3 +975,13 @@ std::string getBuiltSystemLibraryNames() {
result += std::format("Aquamarine: built against {}, system has {}\n", AQUAMARINE_VERSION, getSystemLibraryVersion("aquamarine"));
return result;
}
+
+bool truthy(const std::string& str) {
+ if (str == "1")
+ return true;
+
+ std::string cpy = str;
+ std::ranges::transform(cpy, cpy.begin(), ::tolower);
+
+ return cpy.starts_with("true") || cpy.starts_with("yes") || cpy.starts_with("on");
+}
diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp
index 5feb2de9..183b6fac 100644
--- a/src/helpers/MiscFunctions.hpp
+++ b/src/helpers/MiscFunctions.hpp
@@ -46,6 +46,7 @@ std::expected<std::string, std::string> binaryNameForPid(pid_t pid);
std::string deviceNameToInternalString(std::string in);
std::string getSystemLibraryVersion(const std::string& name);
std::string getBuiltSystemLibraryNames();
+bool truthy(const std::string& str);
template <typename... Args>
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) {
diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp
index 1a7b4ac6..ce9c6990 100644
--- a/src/helpers/Monitor.cpp
+++ b/src/helpers/Monitor.cpp
@@ -177,7 +177,11 @@ void CMonitor::onConnect(bool noRule) {
m_forceSize = SIZE;
SMonitorRule rule = m_activeMonitorRule;
- rule.resolution = SIZE;
+
+ if (SIZE == rule.resolution)
+ return;
+
+ rule.resolution = SIZE;
applyMonitorRule(&rule);
});
diff --git a/src/helpers/TagKeeper.cpp b/src/helpers/TagKeeper.cpp
index 3c7071d5..7f377657 100644
--- a/src/helpers/TagKeeper.cpp
+++ b/src/helpers/TagKeeper.cpp
@@ -1,6 +1,6 @@
#include "TagKeeper.hpp"
-bool CTagKeeper::isTagged(const std::string& tag, bool strict) {
+bool CTagKeeper::isTagged(const std::string& tag, bool strict) const {
const bool NEGATIVE = tag.starts_with("negative");
const auto MATCH = NEGATIVE ? tag.substr(9) : tag;
const bool TAGGED = m_tags.contains(MATCH) || (!strict && m_tags.contains(MATCH + "*"));
@@ -38,6 +38,6 @@ bool CTagKeeper::applyTag(const std::string& tag, bool dynamic) {
return true;
}
-bool CTagKeeper::removeDynamicTags() {
- return std::erase_if(m_tags, [](const auto& tag) { return tag.ends_with("*"); });
+bool CTagKeeper::removeDynamicTag(const std::string& s) {
+ return std::erase_if(m_tags, [&s](const auto& tag) { return tag == s + "*"; });
}
diff --git a/src/helpers/TagKeeper.hpp b/src/helpers/TagKeeper.hpp
index f4732005..d18a0d29 100644
--- a/src/helpers/TagKeeper.hpp
+++ b/src/helpers/TagKeeper.hpp
@@ -5,9 +5,9 @@
class CTagKeeper {
public:
- bool isTagged(const std::string& tag, bool strict = false);
+ bool isTagged(const std::string& tag, bool strict = false) const;
bool applyTag(const std::string& tag, bool dynamic = false);
- bool removeDynamicTags();
+ bool removeDynamicTag(const std::string& tag);
const auto& getTags() const {
return m_tags;
diff --git a/src/helpers/math/Expression.cpp b/src/helpers/math/Expression.cpp
new file mode 100644
index 00000000..fb28628d
--- /dev/null
+++ b/src/helpers/math/Expression.cpp
@@ -0,0 +1,22 @@
+#include "Expression.hpp"
+#include "muParser.h"
+#include "../../debug/Log.hpp"
+
+using namespace Math;
+
+CExpression::CExpression() : m_parser(makeUnique<mu::Parser>()) {
+ ;
+}
+
+void CExpression::addVariable(const std::string& name, double val) {
+ m_parser->DefineConst(name, val);
+}
+
+std::optional<double> CExpression::compute(const std::string& expr) {
+ try {
+ m_parser->SetExpr(expr);
+ return m_parser->Eval();
+ } catch (mu::Parser::exception_type& e) { Debug::log(ERR, "CExpression::compute: mu threw: {}", e.GetMsg()); }
+
+ return std::nullopt;
+}
diff --git a/src/helpers/math/Expression.hpp b/src/helpers/math/Expression.hpp
new file mode 100644
index 00000000..1780e3ee
--- /dev/null
+++ b/src/helpers/math/Expression.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "../memory/Memory.hpp"
+#include <string>
+#include <optional>
+
+namespace mu {
+ class Parser;
+};
+
+namespace Math {
+ class CExpression {
+ public:
+ CExpression();
+ ~CExpression() = default;
+
+ CExpression(const CExpression&) = delete;
+ CExpression(CExpression&) = delete;
+ CExpression(CExpression&&) = delete;
+
+ void addVariable(const std::string& name, double val);
+
+ std::optional<double> compute(const std::string& expr);
+
+ private:
+ UP<mu::Parser> m_parser;
+ };
+}; \ No newline at end of file
diff --git a/src/helpers/varlist/VarList.hpp b/src/helpers/varlist/VarList.hpp
index 4cdc1728..ca68751e 100644
--- a/src/helpers/varlist/VarList.hpp
+++ b/src/helpers/varlist/VarList.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <hyprutils/string/VarList.hpp>
+#include <hyprutils/string/VarList2.hpp>
//NOLINTNEXTLINE
using namespace Hyprutils::String;
diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp
index 6df54445..c653a47e 100644
--- a/src/layout/DwindleLayout.cpp
+++ b/src/layout/DwindleLayout.cpp
@@ -183,7 +183,7 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
if (PWINDOW->isFullscreen() && !pNode->ignoreFullscreenChecks)
return;
- PWINDOW->unsetWindowData(PRIORITY_LAYOUT);
+ PWINDOW->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
PWINDOW->updateWindowData();
static auto PGAPSINDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_in");
@@ -272,10 +272,10 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
Vector2D monitorAvailable = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight -
Vector2D{(double)(gapsOut.m_left + gapsOut.m_right), (double)(gapsOut.m_top + gapsOut.m_bottom)} - Vector2D{2.0 * borderSize, 2.0 * borderSize};
- Vector2D minSize = PWINDOW->m_windowData.minSize.valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable);
- Vector2D maxSize =
- PWINDOW->isFullscreen() ? Vector2D{INFINITY, INFINITY} : PWINDOW->m_windowData.maxSize.valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable);
- calcSize = calcSize.clamp(minSize, maxSize);
+ Vector2D minSize = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable);
+ Vector2D maxSize = PWINDOW->isFullscreen() ? Vector2D{INFINITY, INFINITY} :
+ PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable);
+ calcSize = calcSize.clamp(minSize, maxSize);
calcPos += (availableSpace - calcSize) / 2.0;
@@ -547,7 +547,7 @@ void CHyprDwindleLayout::onWindowRemovedTiling(PHLWINDOW pWindow) {
return;
}
- pWindow->unsetWindowData(PRIORITY_LAYOUT);
+ pWindow->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
pWindow->updateWindowData();
if (pWindow->isFullscreen())
@@ -664,9 +664,9 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn
const auto PNODE = getNodeFromWindow(PWINDOW);
if (!PNODE) {
- *PWINDOW->m_realSize =
- (PWINDOW->m_realSize->goal() + pixResize)
- .clamp(PWINDOW->m_windowData.minSize.valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}), PWINDOW->m_windowData.maxSize.valueOr(Vector2D{INFINITY, INFINITY}));
+ *PWINDOW->m_realSize = (PWINDOW->m_realSize->goal() + pixResize)
+ .clamp(PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}),
+ PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}));
PWINDOW->updateWindowDecos();
return;
}
@@ -709,8 +709,8 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn
CBox wbox = PNODE->box;
wbox.round();
- Vector2D minSize = PWINDOW->m_windowData.minSize.valueOr(Vector2D{30.0, 30.0});
- Vector2D maxSize = PWINDOW->m_windowData.maxSize.valueOr(Vector2D{INFINITY, INFINITY});
+ Vector2D minSize = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{30.0, 30.0});
+ Vector2D maxSize = PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY});
Vector2D upperBound = Vector2D{std::min(maxSize.x, wbox.w), std::min(maxSize.y, wbox.h)};
PWINDOW->m_pseudoSize = PWINDOW->m_pseudoSize.clamp(minSize, upperBound);
@@ -870,7 +870,7 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFu
*pWindow->m_realPosition = pWindow->m_lastFloatingPosition;
*pWindow->m_realSize = pWindow->m_lastFloatingSize;
- pWindow->unsetWindowData(PRIORITY_LAYOUT);
+ pWindow->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
pWindow->updateWindowData();
}
} else {
diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp
index 702d6ac9..85b401bd 100644
--- a/src/layout/IHyprLayout.cpp
+++ b/src/layout/IHyprLayout.cpp
@@ -13,11 +13,12 @@
#include "../managers/EventManager.hpp"
#include "../managers/HookSystemManager.hpp"
#include "../managers/cursor/CursorShapeOverrideController.hpp"
+#include "../desktop/rule/windowRule/WindowRule.hpp"
void IHyprLayout::onWindowCreated(PHLWINDOW pWindow, eDirection direction) {
CBox desiredGeometry = g_pXWaylandManager->getGeometryForWindow(pWindow);
- const bool HASPERSISTENTSIZE = std::ranges::any_of(pWindow->m_matchedRules, [](const auto& rule) { return rule->m_ruleType == CWindowRule::RULE_PERSISTENTSIZE; });
+ const bool HASPERSISTENTSIZE = pWindow->m_ruleApplicator->persistentSize().valueOrDefault();
const auto STOREDSIZE = HASPERSISTENTSIZE ? g_pConfigManager->getStoredFloatingSize(pWindow) : std::nullopt;
@@ -77,7 +78,7 @@ void IHyprLayout::onWindowRemoved(PHLWINDOW pWindow) {
pWindow->updateWindowDecos();
PWINDOWPREV->getGroupCurrent()->updateWindowDecos();
- g_pCompositor->updateWindowAnimatedDecorationValues(pWindow);
+ pWindow->updateDecorationValues();
return;
}
@@ -637,10 +638,10 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
} else if (g_pInputManager->m_dragMode == MBIND_RESIZE || g_pInputManager->m_dragMode == MBIND_RESIZE_FORCE_RATIO || g_pInputManager->m_dragMode == MBIND_RESIZE_BLOCK_RATIO) {
if (DRAGGINGWINDOW->m_isFloating) {
- Vector2D MINSIZE = DRAGGINGWINDOW->requestedMinSize().clamp(DRAGGINGWINDOW->m_windowData.minSize.valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE)));
+ Vector2D MINSIZE = DRAGGINGWINDOW->requestedMinSize().clamp(DRAGGINGWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE)));
Vector2D MAXSIZE;
- if (DRAGGINGWINDOW->m_windowData.maxSize.hasValue())
- MAXSIZE = DRAGGINGWINDOW->requestedMaxSize().clamp({}, DRAGGINGWINDOW->m_windowData.maxSize.value());
+ if (DRAGGINGWINDOW->m_ruleApplicator->maxSize().hasValue())
+ MAXSIZE = DRAGGINGWINDOW->requestedMaxSize().clamp({}, DRAGGINGWINDOW->m_ruleApplicator->maxSize().value());
else
MAXSIZE = DRAGGINGWINDOW->requestedMaxSize().clamp({}, Vector2D(std::numeric_limits<double>::max(), std::numeric_limits<double>::max()));
@@ -657,7 +658,7 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
newSize = newSize + Vector2D(-DELTA.x, DELTA.y);
eMouseBindMode mode = g_pInputManager->m_dragMode;
- if (DRAGGINGWINDOW->m_windowData.keepAspectRatio.valueOrDefault() && mode != MBIND_RESIZE_BLOCK_RATIO)
+ if (DRAGGINGWINDOW->m_ruleApplicator->keepAspectRatio().valueOrDefault() && mode != MBIND_RESIZE_BLOCK_RATIO)
mode = MBIND_RESIZE_FORCE_RATIO;
if (m_beginDragSizeXY.x >= 1 && m_beginDragSizeXY.y >= 1 && mode == MBIND_RESIZE_FORCE_RATIO) {
@@ -803,14 +804,15 @@ void IHyprLayout::changeWindowFloatingMode(PHLWINDOW pWindow) {
g_pHyprRenderer->damageMonitor(pWindow->m_monitor.lock());
- pWindow->unsetWindowData(PRIORITY_LAYOUT);
+ pWindow->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
pWindow->updateWindowData();
if (pWindow == m_lastTiledWindow)
m_lastTiledWindow.reset();
}
- g_pCompositor->updateWindowAnimatedDecorationValues(pWindow);
+ pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE | Desktop::Rule::RULE_PROP_FLOATING);
+ pWindow->updateDecorationValues();
pWindow->updateToplevel();
g_pHyprRenderer->damageWindow(pWindow);
}
@@ -885,7 +887,7 @@ PHLWINDOW IHyprLayout::getNextWindowCandidate(PHLWINDOW pWindow) {
// find whether there is a floating window below this one
for (auto const& w : g_pCompositor->m_windows) {
if (w->m_isMapped && !w->isHidden() && w->m_isFloating && !w->isX11OverrideRedirect() && w->m_workspace == pWindow->m_workspace && !w->m_X11ShouldntFocus &&
- !w->m_windowData.noFocus.valueOrDefault() && w != pWindow) {
+ !w->m_ruleApplicator->noFocus().valueOrDefault() && w != pWindow) {
if (VECINRECT((pWindow->m_size / 2.f + pWindow->m_position), w->m_position.x, w->m_position.y, w->m_position.x + w->m_size.x, w->m_position.y + w->m_size.y)) {
return w;
}
@@ -904,7 +906,7 @@ PHLWINDOW IHyprLayout::getNextWindowCandidate(PHLWINDOW pWindow) {
// if not, floating window
for (auto const& w : g_pCompositor->m_windows) {
if (w->m_isMapped && !w->isHidden() && w->m_isFloating && !w->isX11OverrideRedirect() && w->m_workspace == pWindow->m_workspace && !w->m_X11ShouldntFocus &&
- !w->m_windowData.noFocus.valueOrDefault() && w != pWindow)
+ !w->m_ruleApplicator->noFocus().valueOrDefault() && w != pWindow)
return w;
}
@@ -953,7 +955,7 @@ Vector2D IHyprLayout::predictSizeForNewWindowFloating(PHLWINDOW pWindow) { // ge
if (g_pCompositor->m_lastMonitor) {
// If `persistentsize` is set, use the stored size if available.
- const bool HASPERSISTENTSIZE = std::ranges::any_of(pWindow->m_matchedRules, [](const auto& rule) { return rule->m_ruleType == CWindowRule::RULE_PERSISTENTSIZE; });
+ const bool HASPERSISTENTSIZE = pWindow->m_ruleApplicator->persistentSize().valueOrDefault();
const auto STOREDSIZE = HASPERSISTENTSIZE ? g_pConfigManager->getStoredFloatingSize(pWindow) : std::nullopt;
@@ -962,27 +964,10 @@ Vector2D IHyprLayout::predictSizeForNewWindowFloating(PHLWINDOW pWindow) { // ge
return STOREDSIZE.value();
}
- for (auto const& r : g_pConfigManager->getMatchingRules(pWindow, true, true)) {
- if (r->m_ruleType != CWindowRule::RULE_SIZE)
- continue;
-
- try {
- const auto VALUE = r->m_rule.substr(r->m_rule.find(' ') + 1);
- const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
- const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
-
- const auto MAXSIZE = pWindow->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);
-
- sizeOverride = {SIZEX, SIZEY};
-
- } catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r->m_rule, r->m_value); }
- break;
+ if (!pWindow->m_ruleApplicator->static_.size.empty()) {
+ const auto SIZE = Desktop::Rule::parseRelativeVector(pWindow, pWindow->m_ruleApplicator->static_.size);
+ if (SIZE)
+ return SIZE.value();
}
}
@@ -990,17 +975,7 @@ Vector2D IHyprLayout::predictSizeForNewWindowFloating(PHLWINDOW pWindow) { // ge
}
Vector2D IHyprLayout::predictSizeForNewWindow(PHLWINDOW pWindow) {
- bool shouldBeFloated = g_pXWaylandManager->shouldBeFloated(pWindow, true);
-
- if (!shouldBeFloated) {
- for (auto const& r : g_pConfigManager->getMatchingRules(pWindow, true, true)) {
- if (r->m_ruleType != CWindowRule::RULE_FLOAT)
- continue;
-
- shouldBeFloated = true;
- break;
- }
- }
+ bool shouldBeFloated = g_pXWaylandManager->shouldBeFloated(pWindow, true) || pWindow->m_ruleApplicator->static_.floating.value_or(false);
Vector2D sizePredicted = {};
@@ -1043,7 +1018,7 @@ bool IHyprLayout::updateDragWindow() {
const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
*DRAGGINGWINDOW->m_realPosition = MOUSECOORDS - DRAGGINGWINDOW->m_realSize->goal() / 2.f;
} else if (!DRAGGINGWINDOW->m_isFloating && g_pInputManager->m_dragMode == MBIND_MOVE) {
- Vector2D MINSIZE = DRAGGINGWINDOW->requestedMinSize().clamp(DRAGGINGWINDOW->m_windowData.minSize.valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE)));
+ Vector2D MINSIZE = DRAGGINGWINDOW->requestedMinSize().clamp(DRAGGINGWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE)));
DRAGGINGWINDOW->m_lastFloatingSize = (DRAGGINGWINDOW->m_realSize->goal() * 0.8489).clamp(MINSIZE, Vector2D{}).floor();
*DRAGGINGWINDOW->m_realPosition = g_pInputManager->getMouseCoordsInternal() - DRAGGINGWINDOW->m_realSize->goal() / 2.f;
if (g_pInputManager->m_dragThresholdReached) {
diff --git a/src/layout/MasterLayout.cpp b/src/layout/MasterLayout.cpp
index 5dde65d6..5b2284c5 100644
--- a/src/layout/MasterLayout.cpp
+++ b/src/layout/MasterLayout.cpp
@@ -242,7 +242,7 @@ void CHyprMasterLayout::onWindowRemovedTiling(PHLWINDOW pWindow) {
const auto MASTERSLEFT = getMastersOnWorkspace(WORKSPACEID);
static auto SMALLSPLIT = CConfigValue<Hyprlang::INT>("master:allow_small_split");
- pWindow->unsetWindowData(PRIORITY_LAYOUT);
+ pWindow->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
pWindow->updateWindowData();
if (pWindow->isFullscreen())
@@ -663,7 +663,7 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
if (PWINDOW->isFullscreen() && !pNode->ignoreFullscreenChecks)
return;
- PWINDOW->unsetWindowData(PRIORITY_LAYOUT);
+ PWINDOW->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
PWINDOW->updateWindowData();
static auto PANIMATE = CConfigValue<Hyprlang::INT>("misc:animate_manual_resizes");
@@ -708,10 +708,10 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
Vector2D monitorAvailable = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight -
Vector2D{(double)(gapsOut.m_left + gapsOut.m_right), (double)(gapsOut.m_top + gapsOut.m_bottom)} - Vector2D{2.0 * borderSize, 2.0 * borderSize};
- Vector2D minSize = PWINDOW->m_windowData.minSize.valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable);
- Vector2D maxSize =
- PWINDOW->isFullscreen() ? Vector2D{INFINITY, INFINITY} : PWINDOW->m_windowData.maxSize.valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable);
- calcSize = calcSize.clamp(minSize, maxSize);
+ Vector2D minSize = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable);
+ Vector2D maxSize = PWINDOW->isFullscreen() ? Vector2D{INFINITY, INFINITY} :
+ PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable);
+ calcSize = calcSize.clamp(minSize, maxSize);
calcPos += (availableSpace - calcSize) / 2.0;
@@ -762,9 +762,9 @@ void CHyprMasterLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorne
const auto PNODE = getNodeFromWindow(PWINDOW);
if (!PNODE) {
- *PWINDOW->m_realSize =
- (PWINDOW->m_realSize->goal() + pixResize)
- .clamp(PWINDOW->m_windowData.minSize.valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}), PWINDOW->m_windowData.maxSize.valueOr(Vector2D{INFINITY, INFINITY}));
+ *PWINDOW->m_realSize = (PWINDOW->m_realSize->goal() + pixResize)
+ .clamp(PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}),
+ PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}));
PWINDOW->updateWindowDecos();
return;
}
@@ -919,7 +919,7 @@ void CHyprMasterLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFul
*pWindow->m_realPosition = pWindow->m_lastFloatingPosition;
*pWindow->m_realSize = pWindow->m_lastFloatingSize;
- pWindow->unsetWindowData(PRIORITY_LAYOUT);
+ pWindow->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
pWindow->updateWindowData();
}
} else {
diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp
index 8194f739..feee0369 100644
--- a/src/managers/KeybindManager.cpp
+++ b/src/managers/KeybindManager.cpp
@@ -21,6 +21,8 @@
#include "../render/Renderer.hpp"
#include "../hyprerror/HyprError.hpp"
#include "../config/ConfigManager.hpp"
+#include "../desktop/rule/windowRule/WindowRule.hpp"
+#include "../desktop/rule/Engine.hpp"
#include <optional>
#include <iterator>
@@ -929,18 +931,20 @@ uint64_t CKeybindManager::spawnWithRules(std::string args, PHLWORKSPACE pInitial
args = args.substr(args.find_first_of(']') + 1);
}
- const uint64_t PROC = spawnRawProc(args, pInitialWorkspace);
+ std::string execToken = "";
if (!RULES.empty()) {
- const auto RULESLIST = CVarList(RULES, 0, ';');
+ auto rule = Desktop::Rule::CWindowRule::buildFromExecString(std::move(RULES));
- for (auto const& r : RULESLIST) {
- g_pConfigManager->addExecRule({r, sc<unsigned long>(PROC)});
- }
+ execToken = rule->execToken();
+
+ Desktop::Rule::ruleEngine()->registerRule(std::move(rule));
- Debug::log(LOG, "Applied {} rule arguments for exec.", RULESLIST.size());
+ Debug::log(LOG, "Applied rule arguments for exec.");
}
+ const uint64_t PROC = spawnRawProc(args, pInitialWorkspace, execToken);
+
return PROC;
}
@@ -949,7 +953,7 @@ SDispatchResult CKeybindManager::spawnRaw(std::string args) {
return {.success = PROC > 0, .error = std::format("Failed to start process {}", args)};
}
-uint64_t CKeybindManager::spawnRawProc(std::string args, PHLWORKSPACE pInitialWorkspace) {
+uint64_t CKeybindManager::spawnRawProc(std::string args, PHLWORKSPACE pInitialWorkspace, const std::string& execRuleToken) {
Debug::log(LOG, "Executing {}", args);
const auto HLENV = getHyprlandLaunchEnv(pInitialWorkspace);
@@ -971,6 +975,8 @@ uint64_t CKeybindManager::spawnRawProc(std::string args, PHLWORKSPACE pInitialWo
setenv(e.first.c_str(), e.second.c_str(), 1);
}
setenv("WAYLAND_DISPLAY", g_pCompositor->m_wlDisplaySocket.c_str(), 1);
+ if (!execRuleToken.empty())
+ setenv(Desktop::Rule::EXEC_RULE_ENV_NAME, execRuleToken.c_str(), true);
int devnull = open("/dev/null", O_WRONLY | O_CLOEXEC);
if (devnull != -1) {
@@ -1344,7 +1350,7 @@ SDispatchResult CKeybindManager::fullscreenStateActive(std::string args) {
if (!PWINDOW)
return {.success = false, .error = "Window not found"};
- PWINDOW->m_windowData.syncFullscreen = CWindowOverridableVar(false, PRIORITY_SET_PROP);
+ PWINDOW->m_ruleApplicator->syncFullscreenOverride(Desktop::Types::COverridableVar(false, Desktop::Types::PRIORITY_SET_PROP));
int internalMode, clientMode;
try {
@@ -1370,7 +1376,8 @@ SDispatchResult CKeybindManager::fullscreenStateActive(std::string args) {
g_pCompositor->setWindowFullscreenState(PWINDOW, STATE);
}
- PWINDOW->m_windowData.syncFullscreen = CWindowOverridableVar(PWINDOW->m_fullscreenState.internal == PWINDOW->m_fullscreenState.client, PRIORITY_SET_PROP);
+ PWINDOW->m_ruleApplicator->syncFullscreenOverride(
+ Desktop::Types::COverridableVar(PWINDOW->m_fullscreenState.internal == PWINDOW->m_fullscreenState.client, Desktop::Types::PRIORITY_SET_PROP));
return {};
}
@@ -2363,9 +2370,9 @@ SDispatchResult CKeybindManager::tagWindow(std::string args) {
else
return {.success = false, .error = "Invalid number of arguments, expected 1 or 2 arguments"};
- if (PWINDOW && PWINDOW->m_tags.applyTag(vars[0])) {
- PWINDOW->updateDynamicRules();
- g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW->m_self.lock());
+ if (PWINDOW && PWINDOW->m_ruleApplicator->m_tagKeeper.applyTag(vars[0])) {
+ PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_TAG);
+ PWINDOW->updateDecorationValues();
}
return {};
@@ -2756,8 +2763,7 @@ SDispatchResult CKeybindManager::pinActive(std::string args) {
PWINDOW->m_workspace = PMONITOR->m_activeWorkspace;
- PWINDOW->updateDynamicRules();
- g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
+ PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_PINNED);
const auto PWORKSPACE = PWINDOW->m_workspace;
@@ -2887,7 +2893,7 @@ SDispatchResult CKeybindManager::lockActiveGroup(std::string args) {
else
PHEAD->m_groupData.locked = false;
- g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
+ PWINDOW->updateDecorationValues();
return {};
}
@@ -3064,7 +3070,7 @@ SDispatchResult CKeybindManager::moveWindowOrGroup(std::string args) {
PWINDOW->warpCursor();
}
- g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
+ PWINDOW->updateDecorationValues();
return {};
}
@@ -3092,7 +3098,7 @@ SDispatchResult CKeybindManager::denyWindowFromGroup(std::string args) {
else
PWINDOW->m_groupData.deny = args == "on";
- g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
+ PWINDOW->updateDecorationValues();
return {};
}
@@ -3142,6 +3148,39 @@ SDispatchResult CKeybindManager::event(std::string args) {
#include <utility>
#include <type_traits>
+template <typename T>
+static void parsePropTrivial(Desktop::Types::COverridableVar<T>& prop, const std::string& s) {
+ static_assert(std::is_same_v<T, bool> || std::is_same_v<T, Hyprlang::INT> || std::is_same_v<T, int> || std::is_same_v<T, Hyprlang::FLOAT> || std::is_same_v<T, std::string>,
+ "Invalid type passed to parsePropTrivial");
+
+ if (s == "unset") {
+ prop.unset(Desktop::Types::PRIORITY_SET_PROP);
+ return;
+ }
+
+ try {
+ if constexpr (std::is_same_v<T, bool>) {
+ if (s == "toggle")
+ prop.increment(true, Desktop::Types::PRIORITY_SET_PROP);
+ else
+ prop = Desktop::Types::COverridableVar<T>(truthy(s), Desktop::Types::PRIORITY_SET_PROP);
+ } else if constexpr (std::is_same_v<T, Hyprlang::INT> || std::is_same_v<T, int>) {
+ if (s.starts_with("relative")) {
+ const auto VAL = std::stoi(s.substr(s.find(' ') + 1));
+ prop.increment(VAL, Desktop::Types::PRIORITY_SET_PROP);
+ } else
+ prop = Desktop::Types::COverridableVar<T>(std::stoull(s), Desktop::Types::PRIORITY_SET_PROP);
+ } else if constexpr (std::is_same_v<T, Hyprlang::FLOAT>) {
+ if (s.starts_with("relative")) {
+ const auto VAL = std::stof(s.substr(s.find(' ') + 1));
+ prop.increment(VAL, Desktop::Types::PRIORITY_SET_PROP);
+ } else
+ prop = Desktop::Types::COverridableVar<T>(std::stof(s), Desktop::Types::PRIORITY_SET_PROP);
+ } else if constexpr (std::is_same_v<T, std::string>)
+ prop = Desktop::Types::COverridableVar<T>(s, Desktop::Types::PRIORITY_SET_PROP);
+ } catch (...) { Debug::log(ERR, "Hyprctl: parsePropTrivial: failed to parse setprop for {}", s); }
+}
+
SDispatchResult CKeybindManager::setProp(std::string args) {
CVarList vars(args, 3, ' ');
@@ -3157,37 +3196,18 @@ SDispatchResult CKeybindManager::setProp(std::string args) {
const auto PROP = vars[1];
const auto VAL = vars[2];
- bool noFocus = PWINDOW->m_windowData.noFocus.valueOrDefault();
+ bool noFocus = PWINDOW->m_ruleApplicator->noFocus().valueOrDefault();
try {
- if (PROP == "animationstyle") {
- PWINDOW->m_windowData.animationStyle = CWindowOverridableVar(VAL, PRIORITY_SET_PROP);
- } else if (PROP == "maxsize") {
- PWINDOW->m_windowData.maxSize = CWindowOverridableVar(configStringToVector2D(VAL), PRIORITY_SET_PROP);
- PWINDOW->clampWindowSize(std::nullopt, PWINDOW->m_windowData.maxSize.value());
+ if (PROP == "max_size") {
+ PWINDOW->m_ruleApplicator->maxSizeOverride(Desktop::Types::COverridableVar(configStringToVector2D(VAL), Desktop::Types::PRIORITY_SET_PROP));
+ PWINDOW->clampWindowSize(std::nullopt, PWINDOW->m_ruleApplicator->maxSize().value());
PWINDOW->setHidden(false);
- } else if (PROP == "minsize") {
- PWINDOW->m_windowData.minSize = CWindowOverridableVar(configStringToVector2D(VAL), PRIORITY_SET_PROP);
- PWINDOW->clampWindowSize(PWINDOW->m_windowData.minSize.value(), std::nullopt);
+ } else if (PROP == "min_size") {
+ PWINDOW->m_ruleApplicator->minSizeOverride(Desktop::Types::COverridableVar(configStringToVector2D(VAL), Desktop::Types::PRIORITY_SET_PROP));
+ PWINDOW->clampWindowSize(std::nullopt, PWINDOW->m_ruleApplicator->minSize().value());
PWINDOW->setHidden(false);
- } else if (PROP == "alpha") {
- PWINDOW->m_windowData.alpha = CWindowOverridableVar(SAlphaValue{std::stof(VAL), PWINDOW->m_windowData.alpha.valueOrDefault().overridden}, PRIORITY_SET_PROP);
- } else if (PROP == "alphainactive") {
- PWINDOW->m_windowData.alphaInactive =
- CWindowOverridableVar(SAlphaValue{std::stof(VAL), PWINDOW->m_windowData.alphaInactive.valueOrDefault().overridden}, PRIORITY_SET_PROP);
- } else if (PROP == "alphafullscreen") {
- PWINDOW->m_windowData.alphaFullscreen =
- CWindowOverridableVar(SAlphaValue{std::stof(VAL), PWINDOW->m_windowData.alphaFullscreen.valueOrDefault().overridden}, PRIORITY_SET_PROP);
- } else if (PROP == "alphaoverride") {
- PWINDOW->m_windowData.alpha =
- CWindowOverridableVar(SAlphaValue{PWINDOW->m_windowData.alpha.valueOrDefault().alpha, sc<bool>(configStringToInt(VAL).value_or(0))}, PRIORITY_SET_PROP);
- } else if (PROP == "alphainactiveoverride") {
- PWINDOW->m_windowData.alphaInactive =
- CWindowOverridableVar(SAlphaValue{PWINDOW->m_windowData.alphaInactive.valueOrDefault().alpha, sc<bool>(configStringToInt(VAL).value_or(0))}, PRIORITY_SET_PROP);
- } else if (PROP == "alphafullscreenoverride") {
- PWINDOW->m_windowData.alphaFullscreen =
- CWindowOverridableVar(SAlphaValue{PWINDOW->m_windowData.alphaFullscreen.valueOrDefault().alpha, sc<bool>(configStringToInt(VAL).value_or(0))}, PRIORITY_SET_PROP);
- } else if (PROP == "activebordercolor" || PROP == "inactivebordercolor") {
+ } else if (PROP == "active_border_color" || PROP == "inactive_border_color") {
CGradientValueData colorData = {};
if (vars.size() > 4) {
for (int i = 3; i < sc<int>(vars.size()); ++i) {
@@ -3208,43 +3228,101 @@ SDispatchResult CKeybindManager::setProp(std::string args) {
colorData.updateColorsOk();
- if (PROP == "activebordercolor")
- PWINDOW->m_windowData.activeBorderColor = CWindowOverridableVar(colorData, PRIORITY_SET_PROP);
+ if (PROP == "active_border_color")
+ PWINDOW->m_ruleApplicator->activeBorderColorOverride(Desktop::Types::COverridableVar(colorData, Desktop::Types::PRIORITY_SET_PROP));
else
- PWINDOW->m_windowData.inactiveBorderColor = CWindowOverridableVar(colorData, PRIORITY_SET_PROP);
- } else if (auto search = NWindowProperties::boolWindowProperties.find(PROP); search != NWindowProperties::boolWindowProperties.end()) {
- auto pWindowDataElement = search->second(PWINDOW);
- if (VAL == "toggle")
- pWindowDataElement->increment(true, PRIORITY_SET_PROP);
- else if (VAL == "unset")
- pWindowDataElement->unset(PRIORITY_SET_PROP);
- else
- *pWindowDataElement = CWindowOverridableVar(sc<bool>(configStringToInt(VAL).value_or(0)), PRIORITY_SET_PROP);
- } else if (auto search = NWindowProperties::intWindowProperties.find(PROP); search != NWindowProperties::intWindowProperties.end()) {
- if (VAL == "unset")
- search->second(PWINDOW)->unset(PRIORITY_SET_PROP);
- else if (VAL.starts_with("relative")) {
- const Hyprlang::INT V = std::stoi(VAL.substr(VAL.find(' ')));
- search->second(PWINDOW)->increment(V, PRIORITY_SET_PROP);
- } else if (const auto V = configStringToInt(VAL); V)
- *(search->second(PWINDOW)) = CWindowOverridableVar(*V, PRIORITY_SET_PROP);
- } else if (auto search = NWindowProperties::floatWindowProperties.find(PROP); search != NWindowProperties::floatWindowProperties.end()) {
- if (VAL == "unset")
- search->second(PWINDOW)->unset(PRIORITY_SET_PROP);
- else if (VAL.starts_with("relative")) {
- const auto V = std::stof(VAL.substr(VAL.find(' ')));
- search->second(PWINDOW)->increment(V, PRIORITY_SET_PROP);
- } else {
- const auto V = std::stof(VAL);
- *(search->second(PWINDOW)) = CWindowOverridableVar(V, PRIORITY_SET_PROP);
- }
- } else
- return {.success = false, .error = "Prop not found"};
+ PWINDOW->m_ruleApplicator->inactiveBorderColorOverride(Desktop::Types::COverridableVar(colorData, Desktop::Types::PRIORITY_SET_PROP));
+ } else if (PROP == "opacity") {
+ PWINDOW->m_ruleApplicator->alphaOverride(Desktop::Types::COverridableVar(
+ Desktop::Types::SAlphaValue{std::stof(VAL), PWINDOW->m_ruleApplicator->alpha().valueOrDefault().overridden}, Desktop::Types::PRIORITY_SET_PROP));
+ } else if (PROP == "opacity_inactive") {
+ PWINDOW->m_ruleApplicator->alphaInactiveOverride(Desktop::Types::COverridableVar(
+ Desktop::Types::SAlphaValue{std::stof(VAL), PWINDOW->m_ruleApplicator->alphaInactive().valueOrDefault().overridden}, Desktop::Types::PRIORITY_SET_PROP));
+ } else if (PROP == "opacity_fullscreen") {
+ PWINDOW->m_ruleApplicator->alphaFullscreenOverride(Desktop::Types::COverridableVar(
+ Desktop::Types::SAlphaValue{std::stof(VAL), PWINDOW->m_ruleApplicator->alphaFullscreen().valueOrDefault().overridden}, Desktop::Types::PRIORITY_SET_PROP));
+ } else if (PROP == "opacity_override") {
+ PWINDOW->m_ruleApplicator->alphaOverride(Desktop::Types::COverridableVar(
+ Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alpha().valueOrDefault().alpha, sc<bool>(configStringToInt(VAL).value_or(0))},
+ Desktop::Types::PRIORITY_SET_PROP));
+ } else if (PROP == "opacity_inactive_override") {
+ PWINDOW->m_ruleApplicator->alphaInactiveOverride(Desktop::Types::COverridableVar(
+ Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alphaInactive().valueOrDefault().alpha, sc<bool>(configStringToInt(VAL).value_or(0))},
+ Desktop::Types::PRIORITY_SET_PROP));
+ } else if (PROP == "opacity_fullscreen_override") {
+ PWINDOW->m_ruleApplicator->alphaFullscreenOverride(Desktop::Types::COverridableVar(
+ Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alphaFullscreen().valueOrDefault().alpha, sc<bool>(configStringToInt(VAL).value_or(0))},
+ Desktop::Types::PRIORITY_SET_PROP));
+ } else if (PROP == "allows_input")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->allowsInput(), VAL);
+ else if (PROP == "decorate")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->decorate(), VAL);
+ else if (PROP == "focus_on_activate")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->focusOnActivate(), VAL);
+ else if (PROP == "keep_aspect_ratio")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->keepAspectRatio(), VAL);
+ else if (PROP == "nearest_neighbor")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->nearestNeighbor(), VAL);
+ else if (PROP == "no_anim")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->noAnim(), VAL);
+ else if (PROP == "no_blur")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->noBlur(), VAL);
+ else if (PROP == "no_dim")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->noDim(), VAL);
+ else if (PROP == "no_focus")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->noFocus(), VAL);
+ else if (PROP == "no_max_size")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->noMaxSize(), VAL);
+ else if (PROP == "no_shadow")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->noShadow(), VAL);
+ else if (PROP == "no_shortcuts_inhibit")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->noShortcutsInhibit(), VAL);
+ else if (PROP == "dim_around")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->dimAround(), VAL);
+ else if (PROP == "opaque")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->opaque(), VAL);
+ else if (PROP == "force_rgbx")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->RGBX(), VAL);
+ else if (PROP == "sync_fullscreen")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->syncFullscreen(), VAL);
+ else if (PROP == "immediate")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->tearing(), VAL);
+ else if (PROP == "xray")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->xray(), VAL);
+ else if (PROP == "render_unfocused")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->renderUnfocused(), VAL);
+ else if (PROP == "no_follow_mouse")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->noFollowMouse(), VAL);
+ else if (PROP == "no_screen_share")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->noScreenShare(), VAL);
+ else if (PROP == "no_vrr")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->noVRR(), VAL);
+ else if (PROP == "persistent_size")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->persistentSize(), VAL);
+ else if (PROP == "stay_focused")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->stayFocused(), VAL);
+ else if (PROP == "idle_inhibit")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->idleInhibitMode(), VAL);
+ else if (PROP == "border_size")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->borderSize(), VAL);
+ else if (PROP == "rounding")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->rounding(), VAL);
+ else if (PROP == "rounding_power")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->roundingPower(), VAL);
+ else if (PROP == "scroll_mouse")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->scrollMouse(), VAL);
+ else if (PROP == "scroll_touchpad")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->scrollTouchpad(), VAL);
+ else if (PROP == "animation")
+ parsePropTrivial(PWINDOW->m_ruleApplicator->animationStyle(), VAL);
+ else
+ return {.success = false, .error = "prop not found"};
+
} catch (std::exception& e) { return {.success = false, .error = std::format("Error parsing prop value: {}", std::string(e.what()))}; }
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
- if (!(PWINDOW->m_windowData.noFocus.valueOrDefault() == noFocus)) {
+ if (!(PWINDOW->m_ruleApplicator->noFocus().valueOrDefault() == noFocus)) {
g_pCompositor->focusWindow(nullptr);
g_pCompositor->focusWindow(PWINDOW);
g_pCompositor->focusWindow(PLASTWINDOW);
diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp
index e3433a10..b4100beb 100644
--- a/src/managers/KeybindManager.hpp
+++ b/src/managers/KeybindManager.hpp
@@ -164,7 +164,7 @@ class CKeybindManager {
static void moveWindowOutOfGroup(PHLWINDOW pWindow, const std::string& dir = "");
static void moveWindowIntoGroup(PHLWINDOW pWindow, PHLWINDOW pWindowInDirection);
static void switchToWindow(PHLWINDOW PWINDOWTOCHANGETO, bool preserveFocusHistory = false);
- static uint64_t spawnRawProc(std::string, PHLWORKSPACE pInitialWorkspace);
+ static uint64_t spawnRawProc(std::string, PHLWORKSPACE pInitialWorkspace, const std::string& execRuleToken = "");
static uint64_t spawnWithRules(std::string, PHLWORKSPACE pInitialWorkspace);
// -------------- Dispatchers -------------- //
diff --git a/src/managers/animation/AnimationManager.cpp b/src/managers/animation/AnimationManager.cpp
index f38f4ccf..38efb829 100644
--- a/src/managers/animation/AnimationManager.cpp
+++ b/src/managers/animation/AnimationManager.cpp
@@ -98,7 +98,7 @@ static void handleUpdate(CAnimatedVariable<VarType>& av, bool warp) {
if (!PMONITOR)
return;
- animationsDisabled = PWINDOW->m_windowData.noAnim.valueOr(animationsDisabled);
+ animationsDisabled = PWINDOW->m_ruleApplicator->noAnim().valueOr(animationsDisabled);
} else if (PWORKSPACE) {
PMONITOR = PWORKSPACE->m_monitor.lock();
if (!PMONITOR)
@@ -142,7 +142,7 @@ static void handleUpdate(CAnimatedVariable<VarType>& av, bool warp) {
PMONITOR = g_pCompositor->getMonitorFromVector(PLAYER->m_realPosition->goal() + PLAYER->m_realSize->goal() / 2.F);
if (!PMONITOR)
return;
- animationsDisabled = animationsDisabled || PLAYER->m_noAnimations;
+ animationsDisabled = animationsDisabled || PLAYER->m_ruleApplicator->noanim().valueOrDefault();
}
const auto SPENT = av.getPercent();
diff --git a/src/managers/animation/DesktopAnimationManager.cpp b/src/managers/animation/DesktopAnimationManager.cpp
index f156dfa9..8a340554 100644
--- a/src/managers/animation/DesktopAnimationManager.cpp
+++ b/src/managers/animation/DesktopAnimationManager.cpp
@@ -41,8 +41,8 @@ void CDesktopAnimationManager::startAnimation(PHLWINDOW pWindow, eAnimationType
if (!pWindow->m_realPosition->enabled() && !force)
return;
- if (pWindow->m_windowData.animationStyle.hasValue()) {
- const auto STYLE = pWindow->m_windowData.animationStyle.value();
+ if (pWindow->m_ruleApplicator->animationStyle().hasValue()) {
+ const auto STYLE = pWindow->m_ruleApplicator->animationStyle().value();
// the window has config'd special anim
if (STYLE.starts_with("slide")) {
CVarList animList2(STYLE, 0, 's');
@@ -106,7 +106,7 @@ void CDesktopAnimationManager::startAnimation(PHLLS ls, eAnimationType type, boo
ls->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersOut"));
}
- const auto ANIMSTYLE = ls->m_animationStyle.value_or(ls->m_realPosition->getStyle());
+ const auto ANIMSTYLE = ls->m_ruleApplicator->animationStyle().valueOr(ls->m_realPosition->getStyle());
if (ANIMSTYLE.starts_with("slide")) {
// get closest edge
const auto MIDDLE = ls->m_geometry.middle();
diff --git a/src/managers/input/IdleInhibitor.cpp b/src/managers/input/IdleInhibitor.cpp
index 82f43f47..851e917a 100644
--- a/src/managers/input/IdleInhibitor.cpp
+++ b/src/managers/input/IdleInhibitor.cpp
@@ -61,13 +61,13 @@ void CInputManager::recheckIdleInhibitorStatus() {
}
bool CInputManager::isWindowInhibiting(const PHLWINDOW& w, bool onlyHl) {
- if (w->m_idleInhibitMode == IDLEINHIBIT_ALWAYS)
+ if (w->m_ruleApplicator->idleInhibitMode().valueOrDefault() == Desktop::Rule::IDLEINHIBIT_ALWAYS)
return true;
- if (w->m_idleInhibitMode == IDLEINHIBIT_FOCUS && g_pCompositor->isWindowActive(w))
+ if (w->m_ruleApplicator->idleInhibitMode().valueOrDefault() == Desktop::Rule::IDLEINHIBIT_FOCUS && g_pCompositor->isWindowActive(w))
return true;
- if (w->m_idleInhibitMode == IDLEINHIBIT_FULLSCREEN && w->isFullscreen() && w->m_workspace && w->m_workspace->isVisible())
+ if (w->m_ruleApplicator->idleInhibitMode().valueOrDefault() == Desktop::Rule::IDLEINHIBIT_FULLSCREEN && w->isFullscreen() && w->m_workspace && w->m_workspace->isVisible())
return true;
if (onlyHl)
diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp
index f44f2101..d1d8ec15 100644
--- a/src/managers/input/InputManager.cpp
+++ b/src/managers/input/InputManager.cpp
@@ -585,7 +585,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st
// Temp fix until that's figured out. Otherwise spams windowrule lookups and other shit.
if (m_lastMouseFocus.lock() != pFoundWindow || g_pCompositor->m_lastWindow.lock() != pFoundWindow) {
if (m_mousePosDelta > *PFOLLOWMOUSETHRESHOLD || refocus) {
- const bool hasNoFollowMouse = pFoundWindow && pFoundWindow->m_windowData.noFollowMouse.valueOrDefault();
+ const bool hasNoFollowMouse = pFoundWindow && pFoundWindow->m_ruleApplicator->noFollowMouse().valueOrDefault();
if (refocus || !hasNoFollowMouse)
g_pCompositor->focusWindow(pFoundWindow, foundSurface);
diff --git a/src/managers/input/trackpad/gestures/CloseGesture.cpp b/src/managers/input/trackpad/gestures/CloseGesture.cpp
index 59beb712..ad2d0f45 100644
--- a/src/managers/input/trackpad/gestures/CloseGesture.cpp
+++ b/src/managers/input/trackpad/gestures/CloseGesture.cpp
@@ -133,7 +133,7 @@ void CCloseTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e)
return;
g_pLayoutManager->getCurrentLayout()->recalculateWindow(window.lock());
- g_pCompositor->updateWindowAnimatedDecorationValues(window.lock());
+ window->updateDecorationValues();
window->sendWindowSize(true);
*window->m_alpha = 1.F;
},
diff --git a/src/protocols/Screencopy.cpp b/src/protocols/Screencopy.cpp
index e2a32c91..84487a18 100644
--- a/src/protocols/Screencopy.cpp
+++ b/src/protocols/Screencopy.cpp
@@ -230,7 +230,7 @@ void CScreencopyFrame::renderMon() {
};
for (auto const& l : g_pCompositor->m_layers) {
- if (!l->m_noScreenShare)
+ if (!l->m_ruleApplicator->noScreenShare().valueOrDefault())
continue;
if UNLIKELY ((!l->m_mapped && !l->m_fadingOut) || l->m_alpha->value() == 0.f)
@@ -251,7 +251,7 @@ void CScreencopyFrame::renderMon() {
}
for (auto const& w : g_pCompositor->m_windows) {
- if (!w->m_windowData.noScreenShare.valueOrDefault())
+ if (!w->m_ruleApplicator->noScreenShare().valueOrDefault())
continue;
if (!g_pHyprRenderer->shouldRenderWindow(w, m_monitor.lock()))
@@ -272,7 +272,7 @@ void CScreencopyFrame::renderMon() {
.scale(m_monitor->m_scale)
.translate(-m_box.pos());
- const auto dontRound = w->isEffectiveInternalFSMode(FSMODE_FULLSCREEN) || w->m_windowData.noRounding.valueOrDefault();
+ const auto dontRound = w->isEffectiveInternalFSMode(FSMODE_FULLSCREEN);
const auto rounding = dontRound ? 0 : w->rounding() * m_monitor->m_scale;
const auto roundingPower = dontRound ? 2.0f : w->roundingPower();
diff --git a/src/protocols/ShortcutsInhibit.cpp b/src/protocols/ShortcutsInhibit.cpp
index b33db998..749390cd 100644
--- a/src/protocols/ShortcutsInhibit.cpp
+++ b/src/protocols/ShortcutsInhibit.cpp
@@ -70,7 +70,7 @@ bool CKeyboardShortcutsInhibitProtocol::isInhibited() {
if (!g_pCompositor->m_lastFocus)
return false;
- if (const auto PWINDOW = g_pCompositor->getWindowFromSurface(g_pCompositor->m_lastFocus.lock()); PWINDOW && PWINDOW->m_windowData.noShortcutsInhibit.valueOrDefault())
+ if (const auto PWINDOW = g_pCompositor->getWindowFromSurface(g_pCompositor->m_lastFocus.lock()); PWINDOW && PWINDOW->m_ruleApplicator->noShortcutsInhibit().valueOrDefault())
return false;
for (auto const& in : m_inhibitors) {
diff --git a/src/protocols/ToplevelExport.cpp b/src/protocols/ToplevelExport.cpp
index eb0a39aa..c66c1f2b 100644
--- a/src/protocols/ToplevelExport.cpp
+++ b/src/protocols/ToplevelExport.cpp
@@ -257,7 +257,7 @@ bool CToplevelExportFrame::copyShm(const Time::steady_tp& now) {
// render client at 0,0
if (PERM == PERMISSION_RULE_ALLOW_MODE_ALLOW) {
- if (!m_window->m_windowData.noScreenShare.valueOrDefault()) {
+ if (!m_window->m_ruleApplicator->noScreenShare().valueOrDefault()) {
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(m_window); // block the feedback to avoid spamming the surface if it's visible
g_pHyprRenderer->renderWindow(m_window, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
@@ -339,7 +339,7 @@ bool CToplevelExportFrame::copyDmabuf(const Time::steady_tp& now) {
g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 1.0));
if (PERM == PERMISSION_RULE_ALLOW_MODE_ALLOW) {
- if (!m_window->m_windowData.noScreenShare.valueOrDefault()) {
+ if (!m_window->m_ruleApplicator->noScreenShare().valueOrDefault()) {
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(m_window); // block the feedback to avoid spamming the surface if it's visible
g_pHyprRenderer->renderWindow(m_window, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
diff --git a/src/protocols/XDGDialog.cpp b/src/protocols/XDGDialog.cpp
index c38a1077..c64a8379 100644
--- a/src/protocols/XDGDialog.cpp
+++ b/src/protocols/XDGDialog.cpp
@@ -30,7 +30,7 @@ void CXDGDialogV1Resource::updateWindow() {
if UNLIKELY (!HLSurface || !HLSurface->getWindow())
return;
- g_pCompositor->updateWindowAnimatedDecorationValues(HLSurface->getWindow());
+ HLSurface->getWindow()->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_MODAL);
}
bool CXDGDialogV1Resource::good() {
diff --git a/src/protocols/XDGTag.cpp b/src/protocols/XDGTag.cpp
index 2966ac90..98c8651f 100644
--- a/src/protocols/XDGTag.cpp
+++ b/src/protocols/XDGTag.cpp
@@ -1,5 +1,6 @@
#include "XDGTag.hpp"
#include "XDGShell.hpp"
+#include "../desktop/Window.hpp"
CXDGToplevelTagManagerResource::CXDGToplevelTagManagerResource(UP<CXdgToplevelTagManagerV1>&& resource) : m_resource(std::move(resource)) {
if UNLIKELY (!good())
@@ -17,6 +18,8 @@ CXDGToplevelTagManagerResource::CXDGToplevelTagManagerResource(UP<CXdgToplevelTa
}
TOPLEVEL->m_toplevelTag = tag;
+ if (TOPLEVEL->m_window)
+ TOPLEVEL->m_window->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_XDG_TAG);
});
m_resource->setSetToplevelDescription([](CXdgToplevelTagManagerV1* r, wl_resource* toplevel, const char* description) {
diff --git a/src/protocols/types/ContentType.cpp b/src/protocols/types/ContentType.cpp
index c0a3d30f..b5b0041c 100644
--- a/src/protocols/types/ContentType.cpp
+++ b/src/protocols/types/ContentType.cpp
@@ -12,7 +12,15 @@ namespace NContentType {
if (it != table.end())
return it->second;
else
- throw std::invalid_argument(std::format("Unknown content type {}", name));
+ return CONTENT_TYPE_NONE;
+ }
+
+ std::string toString(eContentType type) {
+ for (const auto& [k, v] : table) {
+ if (v == type)
+ return k;
+ }
+ return "";
}
eContentType fromWP(wpContentTypeV1Type contentType) {
diff --git a/src/protocols/types/ContentType.hpp b/src/protocols/types/ContentType.hpp
index 66fcbca7..68bc7a41 100644
--- a/src/protocols/types/ContentType.hpp
+++ b/src/protocols/types/ContentType.hpp
@@ -13,6 +13,7 @@ namespace NContentType {
};
eContentType fromString(const std::string name);
+ std::string toString(eContentType);
eContentType fromWP(wpContentTypeV1Type contentType);
uint16_t toDRM(eContentType contentType);
} \ No newline at end of file
diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp
index 548b2f8b..b2ec69f3 100644
--- a/src/render/OpenGL.cpp
+++ b/src/render/OpenGL.cpp
@@ -1670,7 +1670,7 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c
}
}
- if (m_renderData.currentWindow && m_renderData.currentWindow->m_windowData.RGBX.valueOrDefault()) {
+ if (m_renderData.currentWindow && m_renderData.currentWindow->m_ruleApplicator->RGBX().valueOrDefault()) {
shader = &m_shaders->m_shRGBX;
texType = TEXTURE_RGBX;
}
@@ -2195,7 +2195,7 @@ void CHyprOpenGLImpl::preRender(PHLMONITOR pMonitor) {
if (!pWindow)
return false;
- if (pWindow->m_windowData.noBlur.valueOrDefault())
+ if (pWindow->m_ruleApplicator->noBlur().valueOrDefault())
return false;
if (pWindow->m_wlSurface->small() && !pWindow->m_wlSurface->m_fillIgnoreSmall)
@@ -2239,7 +2239,7 @@ void CHyprOpenGLImpl::preRender(PHLMONITOR pMonitor) {
for (auto const& m : g_pCompositor->m_monitors) {
for (auto const& lsl : m->m_layerSurfaceLayers) {
for (auto const& ls : lsl) {
- if (!ls->m_layerSurface || ls->m_xray != 1)
+ if (!ls->m_layerSurface || ls->m_ruleApplicator->xray().valueOrDefault() != 1)
continue;
// if (ls->layerSurface->surface->opaque && ls->alpha->value() >= 1.f)
@@ -2311,16 +2311,16 @@ bool CHyprOpenGLImpl::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWin
if (!m_renderData.pCurrentMonData->blurFB.getTexture())
return false;
- if (pWindow && pWindow->m_windowData.xray.hasValue() && !pWindow->m_windowData.xray.valueOrDefault())
+ if (pWindow && pWindow->m_ruleApplicator->xray().hasValue() && !pWindow->m_ruleApplicator->xray().valueOrDefault())
return false;
- if (pLayer && pLayer->m_xray == 0)
+ if (pLayer && pLayer->m_ruleApplicator->xray().valueOrDefault() == 0)
return false;
if ((*PBLURNEWOPTIMIZE && pWindow && !pWindow->m_isFloating && !pWindow->onSpecialWorkspace()) || *PBLURXRAY)
return true;
- if ((pLayer && pLayer->m_xray == 1) || (pWindow && pWindow->m_windowData.xray.valueOrDefault()))
+ if ((pLayer && pLayer->m_ruleApplicator->xray().valueOrDefault() == 1) || (pWindow && pWindow->m_ruleApplicator->xray().valueOrDefault()))
return true;
return false;
@@ -2474,7 +2474,7 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr
TRACY_GPU_ZONE("RenderBorder");
- if (m_renderData.damage.empty() || (m_renderData.currentWindow && m_renderData.currentWindow->m_windowData.noBorder.valueOrDefault()))
+ if (m_renderData.damage.empty())
return;
CBox newBox = box;
@@ -2558,7 +2558,7 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr
TRACY_GPU_ZONE("RenderBorder2");
- if (m_renderData.damage.empty() || (m_renderData.currentWindow && m_renderData.currentWindow->m_windowData.noBorder.valueOrDefault()))
+ if (m_renderData.damage.empty())
return;
CBox newBox = box;
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index b115bab9..aec2bbc6 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -167,7 +167,7 @@ CHyprRenderer::CHyprRenderer() {
}
if (dirty)
- std::erase_if(m_renderUnfocused, [](const auto& e) { return !e || !e->m_windowData.renderUnfocused.valueOr(false); });
+ std::erase_if(m_renderUnfocused, [](const auto& e) { return !e || !e->m_ruleApplicator->renderUnfocused().valueOr(false); });
if (!m_renderUnfocused.empty())
m_renderUnfocusedTimer->updateTimeout(std::chrono::milliseconds(1000 / *PFPS));
@@ -509,7 +509,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T
const bool USE_WORKSPACE_FADE_ALPHA = pWindow->m_monitorMovedFrom != -1 && (!PWORKSPACE || !PWORKSPACE->isVisible());
renderdata.surface = pWindow->m_wlSurface->resource();
- renderdata.dontRound = pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN) || pWindow->m_windowData.noRounding.valueOrDefault();
+ renderdata.dontRound = pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN);
renderdata.fadeAlpha = pWindow->m_alpha->value() * (pWindow->m_pinned || USE_WORKSPACE_FADE_ALPHA ? 1.f : PWORKSPACE->m_alpha->value()) *
(USE_WORKSPACE_FADE_ALPHA ? pWindow->m_movingToWorkspaceAlpha->value() : 1.F) * pWindow->m_movingFromWorkspaceAlpha->value();
renderdata.alpha = pWindow->m_activeInactiveAlpha->value();
@@ -525,7 +525,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T
}
// apply opaque
- if (pWindow->m_windowData.opaque.valueOrDefault())
+ if (pWindow->m_ruleApplicator->opaque().valueOrDefault())
renderdata.alpha = 1.f;
renderdata.pWindow = pWindow;
@@ -537,7 +537,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T
const auto fullAlpha = renderdata.alpha * renderdata.fadeAlpha;
- if (*PDIMAROUND && pWindow->m_windowData.dimAround.valueOrDefault() && !m_bRenderingSnapshot && mode != RENDER_PASS_POPUP) {
+ if (*PDIMAROUND && pWindow->m_ruleApplicator->dimAround().valueOrDefault() && !m_bRenderingSnapshot && mode != RENDER_PASS_POPUP) {
CBox monbox = {0, 0, g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize.x, g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize.y};
CRectPassElement::SRectData data;
data.color = CHyprColor(0, 0, 0, *PDIMAROUND * fullAlpha);
@@ -585,7 +585,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T
}
static auto PXWLUSENN = CConfigValue<Hyprlang::INT>("xwayland:use_nearest_neighbor");
- if ((pWindow->m_isX11 && *PXWLUSENN) || pWindow->m_windowData.nearestNeighbor.valueOrDefault())
+ if ((pWindow->m_isX11 && *PXWLUSENN) || pWindow->m_ruleApplicator->nearestNeighbor().valueOrDefault())
renderdata.useNearestNeighbor = true;
if (pWindow->m_wlSurface->small() && !pWindow->m_wlSurface->m_fillIgnoreSmall && renderdata.blur) {
@@ -657,7 +657,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T
renderdata.discardOpacity = *PBLURIGNOREA;
}
- if (pWindow->m_windowData.nearestNeighbor.valueOrDefault())
+ if (pWindow->m_ruleApplicator->nearestNeighbor().valueOrDefault())
renderdata.useNearestNeighbor = true;
renderdata.surfaceCounter = 0;
@@ -714,12 +714,13 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::s
return;
// skip rendering based on abovelock rule and make sure to not render abovelock layers twice
- if ((pLayer->m_aboveLockscreen && !lockscreen && g_pSessionLockManager->isSessionLocked()) || (lockscreen && !pLayer->m_aboveLockscreen))
+ if ((pLayer->m_ruleApplicator->aboveLock().valueOrDefault() && !lockscreen && g_pSessionLockManager->isSessionLocked()) ||
+ (lockscreen && !pLayer->m_ruleApplicator->aboveLock().valueOrDefault()))
return;
static auto PDIMAROUND = CConfigValue<Hyprlang::FLOAT>("decoration:dim_around");
- if (*PDIMAROUND && pLayer->m_dimAround && !m_bRenderingSnapshot && !popups) {
+ if (*PDIMAROUND && pLayer->m_ruleApplicator->dimAround().valueOrDefault() && !m_bRenderingSnapshot && !popups) {
CRectPassElement::SRectData data;
data.box = {0, 0, g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize.x, g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize.y};
data.color = CHyprColor(0, 0, 0, *PDIMAROUND * pLayer->m_alpha->value());
@@ -749,9 +750,9 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::s
renderdata.clipBox = CBox{0, 0, pMonitor->m_size.x, pMonitor->m_size.y}.scale(pMonitor->m_scale);
- if (renderdata.blur && pLayer->m_ignoreAlpha) {
+ if (renderdata.blur && pLayer->m_ruleApplicator->ignoreAlpha().hasValue()) {
renderdata.discardMode |= DISCARD_ALPHA;
- renderdata.discardOpacity = pLayer->m_ignoreAlphaValue;
+ renderdata.discardOpacity = pLayer->m_ruleApplicator->ignoreAlpha().valueOrDefault();
}
if (!popups)
@@ -769,7 +770,7 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::s
renderdata.squishOversized = false; // don't squish popups
renderdata.dontRound = true;
renderdata.popup = true;
- renderdata.blur = pLayer->m_forceBlurPopups;
+ renderdata.blur = pLayer->m_ruleApplicator->blurPopups().valueOrDefault();
renderdata.surfaceCounter = 0;
if (popups) {
pLayer->m_popupHead->breadthfirst(
@@ -1859,7 +1860,8 @@ void CHyprRenderer::arrangeLayersForMonitor(const MONITORID& monitor) {
}
for (auto& la : PMONITOR->m_layerSurfaceLayers) {
- std::ranges::stable_sort(la, [](const PHLLSREF& a, const PHLLSREF& b) { return a->m_order > b->m_order; });
+ std::ranges::stable_sort(
+ la, [](const PHLLSREF& a, const PHLLSREF& b) { return a->m_ruleApplicator->order().valueOrDefault() > b->m_ruleApplicator->order().valueOrDefault(); });
}
for (auto const& la : PMONITOR->m_layerSurfaceLayers)
@@ -2564,7 +2566,7 @@ void CHyprRenderer::renderSnapshot(PHLWINDOW pWindow) {
CRegion fakeDamage{0, 0, PMONITOR->m_transformedSize.x, PMONITOR->m_transformedSize.y};
- if (*PDIMAROUND && pWindow->m_windowData.dimAround.valueOrDefault()) {
+ if (*PDIMAROUND && pWindow->m_ruleApplicator->dimAround().valueOrDefault()) {
CRectPassElement::SRectData data;
data.box = {0, 0, g_pHyprOpenGL->m_renderData.pMonitor->m_pixelSize.x, g_pHyprOpenGL->m_renderData.pMonitor->m_pixelSize.y};
@@ -2581,7 +2583,7 @@ void CHyprRenderer::renderSnapshot(PHLWINDOW pWindow) {
data.blurA = sqrt(pWindow->m_alpha->value()); // sqrt makes the blur fadeout more realistic.
data.round = pWindow->rounding();
data.roundingPower = pWindow->roundingPower();
- data.xray = pWindow->m_windowData.xray.valueOr(false);
+ data.xray = pWindow->m_ruleApplicator->xray().valueOr(false);
m_renderPass.add(makeUnique<CRectPassElement>(std::move(data)));
}
@@ -2633,7 +2635,7 @@ void CHyprRenderer::renderSnapshot(PHLLS pLayer) {
data.blur = SHOULD_BLUR;
data.blurA = sqrt(pLayer->m_alpha->value()); // sqrt makes the blur fadeout more realistic.
if (SHOULD_BLUR)
- data.ignoreAlpha = pLayer->m_ignoreAlpha ? pLayer->m_ignoreAlphaValue : 0.01F /* ignore the alpha 0 regions */;
+ data.ignoreAlpha = pLayer->m_ruleApplicator->ignoreAlpha().valueOr(0.01F) /* ignore the alpha 0 regions */;
m_renderPass.add(makeUnique<CTexPassElement>(std::move(data)));
}
@@ -2678,7 +2680,7 @@ bool CHyprRenderer::shouldBlur(PHLLS ls) {
return false;
static auto PBLUR = CConfigValue<Hyprlang::INT>("decoration:blur:enabled");
- return *PBLUR && ls->m_forceBlur;
+ return *PBLUR && ls->m_ruleApplicator->blur().valueOrDefault();
}
bool CHyprRenderer::shouldBlur(PHLWINDOW w) {
@@ -2686,7 +2688,7 @@ bool CHyprRenderer::shouldBlur(PHLWINDOW w) {
return false;
static auto PBLUR = CConfigValue<Hyprlang::INT>("decoration:blur:enabled");
- const bool DONT_BLUR = w->m_windowData.noBlur.valueOrDefault() || w->m_windowData.RGBX.valueOrDefault() || w->opaque();
+ const bool DONT_BLUR = w->m_ruleApplicator->noBlur().valueOrDefault() || w->m_ruleApplicator->RGBX().valueOrDefault() || w->opaque();
return *PBLUR && !DONT_BLUR;
}
diff --git a/src/render/decorations/CHyprBorderDecoration.cpp b/src/render/decorations/CHyprBorderDecoration.cpp
index 75298ff7..a082f073 100644
--- a/src/render/decorations/CHyprBorderDecoration.cpp
+++ b/src/render/decorations/CHyprBorderDecoration.cpp
@@ -161,6 +161,5 @@ std::string CHyprBorderDecoration::getDisplayName() {
}
bool CHyprBorderDecoration::doesntWantBorders() {
- return m_window->m_windowData.noBorder.valueOrDefault() || m_window->m_X11DoesntWantBorders || m_window->getRealBorderSize() == 0 ||
- !m_window->m_windowData.decorate.valueOrDefault();
+ return m_window->m_X11DoesntWantBorders || m_window->getRealBorderSize() == 0 || !m_window->m_ruleApplicator->decorate().valueOrDefault();
}
diff --git a/src/render/decorations/CHyprDropShadowDecoration.cpp b/src/render/decorations/CHyprDropShadowDecoration.cpp
index bcc4c84e..dd82abc5 100644
--- a/src/render/decorations/CHyprDropShadowDecoration.cpp
+++ b/src/render/decorations/CHyprDropShadowDecoration.cpp
@@ -104,10 +104,10 @@ void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) {
if (PWINDOW->m_realShadowColor->value() == CHyprColor(0, 0, 0, 0))
return; // don't draw invisible shadows
- if (!PWINDOW->m_windowData.decorate.valueOrDefault())
+ if (!PWINDOW->m_ruleApplicator->decorate().valueOrDefault())
return;
- if (PWINDOW->m_windowData.noShadow.valueOrDefault())
+ if (PWINDOW->m_ruleApplicator->noShadow().valueOrDefault())
return;
static auto PSHADOWS = CConfigValue<Hyprlang::INT>("decoration:shadow:enabled");
diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp
index dbf66b60..3b95d749 100644
--- a/src/render/decorations/CHyprGroupBarDecoration.cpp
+++ b/src/render/decorations/CHyprGroupBarDecoration.cpp
@@ -597,5 +597,5 @@ CBox CHyprGroupBarDecoration::assignedBoxGlobal() {
bool CHyprGroupBarDecoration::visible() {
static auto PENABLED = CConfigValue<Hyprlang::INT>("group:groupbar:enabled");
- return *PENABLED && m_window->m_windowData.decorate.valueOrDefault();
+ return *PENABLED && m_window->m_ruleApplicator->decorate().valueOrDefault();
}