diff options
Diffstat (limited to 'src/desktop/rule/windowRule/WindowRule.cpp')
| -rw-r--r-- | src/desktop/rule/windowRule/WindowRule.cpp | 304 |
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; |
