summaryrefslogtreecommitdiff
path: root/src/desktop/rule/windowRule/WindowRule.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/desktop/rule/windowRule/WindowRule.cpp')
-rw-r--r--src/desktop/rule/windowRule/WindowRule.cpp304
1 files changed, 298 insertions, 6 deletions
diff --git a/src/desktop/rule/windowRule/WindowRule.cpp b/src/desktop/rule/windowRule/WindowRule.cpp
index fbabb42d..f2baaa85 100644
--- a/src/desktop/rule/windowRule/WindowRule.cpp
+++ b/src/desktop/rule/windowRule/WindowRule.cpp
@@ -1,16 +1,298 @@
#include "WindowRule.hpp"
#include "../../view/Window.hpp"
#include "../../../helpers/Monitor.hpp"
+#include "../../../helpers/MiscFunctions.hpp"
#include "../../../Compositor.hpp"
#include "../../../managers/TokenManager.hpp"
#include "../../../desktop/state/FocusState.hpp"
+#include "../../../protocols/types/ContentType.hpp"
+#include <hyprutils/string/String.hpp>
+#include <hyprutils/string/VarList.hpp>
#include <hyprutils/string/VarList2.hpp>
+#include <algorithm>
+#include <format>
using namespace Desktop;
using namespace Desktop::Rule;
using namespace Hyprutils::String;
+static std::expected<int64_t, std::string> parseInt(std::string_view effectName, const std::string& raw) {
+ try {
+ return std::stoll(raw);
+ } catch (std::exception& e) { return std::unexpected(std::format("{} rule \"{}\" failed with: {}", effectName, raw, e.what())); }
+}
+
+static std::expected<float, std::string> parseFloat(std::string_view effectName, const std::string& raw) {
+ try {
+ return std::stof(raw);
+ } catch (std::exception& e) { return std::unexpected(std::format("{} rule \"{}\" failed with: {}", effectName, raw, e.what())); }
+}
+
+static std::expected<CHyprColor, std::string> parseBorderColorToken(const std::string& raw, const std::string& token) {
+ auto parsed = configStringToInt(token);
+ if (!parsed)
+ return std::unexpected(std::format(R"(border_color rule "{}" has invalid color "{}": {})", raw, token, parsed.error()));
+
+ return CHyprColor(*parsed);
+}
+
+static std::expected<SFullscreenStateRule, std::string> parseFullscreenState(const std::string& raw) {
+ CVarList2 vars(std::string{raw}, 0, 's');
+
+ try {
+ SFullscreenStateRule result;
+ result.internal = std::stoi(std::string{vars[0]});
+ if (!vars[1].empty())
+ result.client = std::stoi(std::string{vars[1]});
+ return result;
+ } catch (std::exception& e) { return std::unexpected(std::format("fullscreen_state rule \"{}\" failed with: {}", raw, e.what())); }
+}
+
+static std::expected<int64_t, std::string> parseIdleInhibitMode(const std::string& raw) {
+ if (raw == "none")
+ return IDLEINHIBIT_NONE;
+ if (raw == "always")
+ return IDLEINHIBIT_ALWAYS;
+ if (raw == "focus")
+ return IDLEINHIBIT_FOCUS;
+ if (raw == "fullscreen")
+ return IDLEINHIBIT_FULLSCREEN;
+
+ return std::unexpected(std::format("idle_inhibit rule has unknown mode \"{}\"", raw));
+}
+
+static std::expected<SOpacityRule, std::string> parseOpacityRule(const std::string& raw) {
+ try {
+ CVarList2 vars(std::string{raw}, 0, ' ');
+
+ int opacityIDX = 0;
+ SOpacityRule result;
+
+ for (const auto& r : vars) {
+ if (r == "opacity")
+ continue;
+
+ if (r == "override") {
+ if (opacityIDX == 1)
+ result.alpha.overridden = true;
+ else if (opacityIDX == 2)
+ result.alphaInactive.overridden = true;
+ else if (opacityIDX == 3)
+ result.alphaFullscreen.overridden = true;
+ } else {
+ if (opacityIDX == 0)
+ result.alpha.alpha = std::stof(std::string{r});
+ else if (opacityIDX == 1)
+ result.alphaInactive.alpha = std::stof(std::string{r});
+ else if (opacityIDX == 2)
+ result.alphaFullscreen.alpha = std::stof(std::string{r});
+ else
+ throw std::runtime_error("more than 3 alpha values");
+
+ opacityIDX++;
+ }
+ }
+
+ if (opacityIDX == 1) {
+ result.alphaInactive = result.alpha;
+ result.alphaFullscreen = result.alpha;
+ }
+
+ return result;
+ } catch (std::exception& e) { return std::unexpected(std::format("opacity rule \"{}\" failed with: {}", raw, e.what())); }
+}
+
+static std::expected<SBorderColorRule, std::string> parseBorderColorRule(const std::string& raw) {
+ try {
+ Config::CGradientValueData activeBorderGradient = {};
+ Config::CGradientValueData inactiveBorderGradient = {};
+ bool active = true;
+ CVarList colorsAndAngles = CVarList(trim(raw), 0, 's', true);
+
+ if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) {
+ auto activeColor = parseBorderColorToken(raw, colorsAndAngles[0]);
+ if (!activeColor)
+ return std::unexpected(activeColor.error());
+
+ auto inactiveColor = parseBorderColorToken(raw, colorsAndAngles[1]);
+ if (!inactiveColor)
+ return std::unexpected(inactiveColor.error());
+
+ return SBorderColorRule{
+ .active = Config::CGradientValueData(*activeColor),
+ .inactive = Config::CGradientValueData(*inactiveColor),
+ };
+ }
+
+ for (auto const& token : colorsAndAngles) {
+ if (active && token.contains("deg")) {
+ activeBorderGradient.m_angle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
+ active = false;
+ } else if (token.contains("deg"))
+ inactiveBorderGradient.m_angle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
+ else {
+ auto color = parseBorderColorToken(raw, token);
+ if (!color)
+ return std::unexpected(color.error());
+
+ if (active)
+ activeBorderGradient.m_colors.emplace_back(*color);
+ else
+ inactiveBorderGradient.m_colors.emplace_back(*color);
+ }
+ }
+
+ activeBorderGradient.updateColorsOk();
+
+ if (activeBorderGradient.m_colors.size() > 10 || inactiveBorderGradient.m_colors.size() > 10)
+ return std::unexpected(std::format("border_color rule \"{}\" has more than 10 colors in one gradient", raw));
+ if (activeBorderGradient.m_colors.empty())
+ return std::unexpected(std::format("border_color rule \"{}\" has no colors", raw));
+
+ SBorderColorRule result{.active = activeBorderGradient};
+ if (!inactiveBorderGradient.m_colors.empty())
+ result.inactive = inactiveBorderGradient;
+
+ return result;
+ } catch (std::exception& e) { return std::unexpected(std::format("border_color rule \"{}\" failed with: {}", raw, e.what())); }
+}
+
+static std::vector<std::string> parseStringList(const std::string& raw) {
+ std::vector<std::string> result;
+ CVarList2 varlist(std::string{raw}, 0, 's');
+
+ for (const auto& e : varlist) {
+ result.emplace_back(e);
+ }
+
+ return result;
+}
+
+static std::expected<WindowRuleEffectValue, std::string> parseWindowRuleEffect(CWindowRuleEffectContainer::storageType e, const std::string& raw) {
+ if (windowEffects()->isEffectDynamic(e))
+ return std::string{raw};
+
+ const auto EFFECT_NAME = windowEffects()->get(e);
+
+ switch (e) {
+ default: return std::unexpected(std::format("unknown window rule effect {}", e));
+
+ case WINDOW_RULE_EFFECT_NONE: return std::monostate{};
+
+ case WINDOW_RULE_EFFECT_FLOAT:
+ case WINDOW_RULE_EFFECT_TILE:
+ case WINDOW_RULE_EFFECT_FULLSCREEN:
+ case WINDOW_RULE_EFFECT_MAXIMIZE:
+ case WINDOW_RULE_EFFECT_CENTER:
+ case WINDOW_RULE_EFFECT_PSEUDO:
+ case WINDOW_RULE_EFFECT_NOINITIALFOCUS:
+ case WINDOW_RULE_EFFECT_PIN:
+ case WINDOW_RULE_EFFECT_PERSISTENT_SIZE:
+ case WINDOW_RULE_EFFECT_ALLOWS_INPUT:
+ case WINDOW_RULE_EFFECT_DIM_AROUND:
+ case WINDOW_RULE_EFFECT_DECORATE:
+ case WINDOW_RULE_EFFECT_FOCUS_ON_ACTIVATE:
+ case WINDOW_RULE_EFFECT_KEEP_ASPECT_RATIO:
+ case WINDOW_RULE_EFFECT_NEAREST_NEIGHBOR:
+ case WINDOW_RULE_EFFECT_NO_ANIM:
+ case WINDOW_RULE_EFFECT_NO_BLUR:
+ case WINDOW_RULE_EFFECT_NO_DIM:
+ case WINDOW_RULE_EFFECT_NO_FOCUS:
+ case WINDOW_RULE_EFFECT_NO_FOLLOW_MOUSE:
+ case WINDOW_RULE_EFFECT_NO_MAX_SIZE:
+ case WINDOW_RULE_EFFECT_NO_SHADOW:
+ case WINDOW_RULE_EFFECT_NO_SHORTCUTS_INHIBIT:
+ case WINDOW_RULE_EFFECT_OPAQUE:
+ case WINDOW_RULE_EFFECT_FORCE_RGBX:
+ case WINDOW_RULE_EFFECT_SYNC_FULLSCREEN:
+ case WINDOW_RULE_EFFECT_IMMEDIATE:
+ case WINDOW_RULE_EFFECT_XRAY:
+ case WINDOW_RULE_EFFECT_RENDER_UNFOCUSED:
+ case WINDOW_RULE_EFFECT_NO_SCREEN_SHARE:
+ case WINDOW_RULE_EFFECT_NO_VRR:
+ case WINDOW_RULE_EFFECT_STAY_FOCUSED: return truthy(raw);
+
+ case WINDOW_RULE_EFFECT_FULLSCREENSTATE: {
+ auto parsed = parseFullscreenState(raw);
+ if (!parsed)
+ return std::unexpected(parsed.error());
+ return *parsed;
+ }
+
+ case WINDOW_RULE_EFFECT_MOVE:
+ case WINDOW_RULE_EFFECT_SIZE:
+ case WINDOW_RULE_EFFECT_MONITOR:
+ case WINDOW_RULE_EFFECT_WORKSPACE:
+ case WINDOW_RULE_EFFECT_GROUP:
+ case WINDOW_RULE_EFFECT_ANIMATION:
+ case WINDOW_RULE_EFFECT_TAG:
+ case WINDOW_RULE_EFFECT_MAX_SIZE:
+ case WINDOW_RULE_EFFECT_MIN_SIZE: return std::string{raw};
+
+ case WINDOW_RULE_EFFECT_SUPPRESSEVENT: return parseStringList(raw);
+
+ case WINDOW_RULE_EFFECT_CONTENT: return sc<int64_t>(NContentType::fromString(raw));
+
+ case WINDOW_RULE_EFFECT_NOCLOSEFOR:
+ case WINDOW_RULE_EFFECT_BORDER_SIZE: {
+ auto parsed = parseInt(EFFECT_NAME, raw);
+ if (!parsed)
+ return std::unexpected(parsed.error());
+ return *parsed;
+ }
+
+ case WINDOW_RULE_EFFECT_ROUNDING: {
+ auto parsed = parseInt(EFFECT_NAME, raw);
+ if (!parsed)
+ return std::unexpected(parsed.error());
+ if (*parsed < 0)
+ return std::unexpected(std::format("{} rule \"{}\" must be non-negative", EFFECT_NAME, raw));
+ return *parsed;
+ }
+
+ case WINDOW_RULE_EFFECT_SCROLLING_WIDTH: {
+ auto parsed = parseFloat(EFFECT_NAME, raw);
+ if (!parsed)
+ return std::unexpected(parsed.error());
+ return *parsed;
+ }
+
+ case WINDOW_RULE_EFFECT_ROUNDING_POWER: {
+ auto parsed = parseFloat(EFFECT_NAME, raw);
+ if (!parsed)
+ return std::unexpected(parsed.error());
+ return std::clamp(*parsed, 1.F, 10.F);
+ }
+ case WINDOW_RULE_EFFECT_SCROLL_MOUSE:
+ case WINDOW_RULE_EFFECT_SCROLL_TOUCHPAD: {
+ auto parsed = parseFloat(EFFECT_NAME, raw);
+ if (!parsed)
+ return std::unexpected(parsed.error());
+ return std::clamp(*parsed, 0.01F, 10.F);
+ }
+
+ case WINDOW_RULE_EFFECT_BORDER_COLOR: {
+ auto parsed = parseBorderColorRule(raw);
+ if (!parsed)
+ return std::unexpected(parsed.error());
+ return *parsed;
+ }
+ case WINDOW_RULE_EFFECT_IDLE_INHIBIT: {
+ auto parsed = parseIdleInhibitMode(raw);
+ if (!parsed)
+ return std::unexpected(parsed.error());
+ return *parsed;
+ }
+ case WINDOW_RULE_EFFECT_OPACITY: {
+ auto parsed = parseOpacityRule(raw);
+ if (!parsed)
+ return std::unexpected(parsed.error());
+ return *parsed;
+ }
+ }
+}
+
CWindowRule::CWindowRule(const std::string& name) : IRule(name) {
;
}
@@ -19,12 +301,18 @@ eRuleType CWindowRule::type() {
return RULE_TYPE_WINDOW;
}
-void CWindowRule::addEffect(CWindowRule::storageType e, const std::string& result) {
- m_effects.emplace_back(std::make_pair<>(e, result));
+std::expected<void, std::string> CWindowRule::addEffect(CWindowRule::storageType e, const std::string& result) {
+ auto parsed = parseWindowRuleEffect(e, result);
+ if (!parsed)
+ return std::unexpected(parsed.error());
+
+ m_effects.emplace_back(SWindowRuleEffect{.key = e, .raw = result, .value = std::move(*parsed)});
m_effectSet.emplace(e);
+
+ return {};
}
-const std::vector<std::pair<CWindowRule::storageType, std::string>>& CWindowRule::effects() {
+const std::vector<SWindowRuleEffect>& CWindowRule::effects() {
return m_effects;
}
@@ -131,7 +419,7 @@ bool CWindowRule::matches(PHLWINDOW w, bool allowEnvLookup) {
return true;
}
-SP<CWindowRule> CWindowRule::buildFromExecString(std::string&& s) {
+std::expected<SP<CWindowRule>, std::string> CWindowRule::buildFromExecString(std::string&& s) {
CVarList2 varlist(std::move(s), 0, ';');
SP<CWindowRule> wr = makeShared<CWindowRule>("__exec_rule");
@@ -146,7 +434,9 @@ SP<CWindowRule> CWindowRule::buildFromExecString(std::string&& s) {
if (!EFFECT.has_value() || *EFFECT == WINDOW_RULE_EFFECT_NONE)
continue; // invalid...
- wr->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)});
+ auto res = wr->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)});
+ if (!res)
+ return std::unexpected(res.error());
continue;
}
@@ -157,7 +447,9 @@ SP<CWindowRule> CWindowRule::buildFromExecString(std::string&& s) {
if (!EFFECT.has_value() || *EFFECT == WINDOW_RULE_EFFECT_NONE)
continue; // invalid...
- wr->addEffect(*EFFECT, std::string{"1"});
+ auto res = wr->addEffect(*EFFECT, std::string{"1"});
+ if (!res)
+ return std::unexpected(res.error());
}
return wr;