windowrules: rewrite completely (#12269)
Reworks the window rule syntax completely --------- Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
This commit is contained in:
parent
95ee08b340
commit
c2670e9ab9
93 changed files with 3574 additions and 2255 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
56
src/desktop/rule/Engine.cpp
Normal file
56
src/desktop/rule/Engine.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
24
src/desktop/rule/Engine.hpp
Normal file
24
src/desktop/rule/Engine.hpp
Normal file
|
|
@ -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();
|
||||
}
|
||||
149
src/desktop/rule/Rule.cpp
Normal file
149
src/desktop/rule/Rule.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
84
src/desktop/rule/Rule.hpp
Normal file
84
src/desktop/rule/Rule.hpp
Normal file
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
81
src/desktop/rule/effect/EffectContainer.hpp
Normal file
81
src/desktop/rule/effect/EffectContainer.hpp
Normal file
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
43
src/desktop/rule/layerRule/LayerRule.cpp
Normal file
43
src/desktop/rule/layerRule/LayerRule.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
25
src/desktop/rule/layerRule/LayerRule.hpp
Normal file
25
src/desktop/rule/layerRule/LayerRule.hpp
Normal file
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
128
src/desktop/rule/layerRule/LayerRuleApplicator.cpp
Normal file
128
src/desktop/rule/layerRule/LayerRuleApplicator.cpp
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
60
src/desktop/rule/layerRule/LayerRuleApplicator.hpp
Normal file
60
src/desktop/rule/layerRule/LayerRuleApplicator.hpp
Normal file
|
|
@ -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);
|
||||
};
|
||||
};
|
||||
33
src/desktop/rule/layerRule/LayerRuleEffectContainer.cpp
Normal file
33
src/desktop/rule/layerRule/LayerRuleEffectContainer.cpp
Normal file
|
|
@ -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}) {
|
||||
;
|
||||
}
|
||||
33
src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp
Normal file
33
src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp
Normal file
|
|
@ -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();
|
||||
};
|
||||
12
src/desktop/rule/matchEngine/BoolMatchEngine.cpp
Normal file
12
src/desktop/rule/matchEngine/BoolMatchEngine.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
16
src/desktop/rule/matchEngine/BoolMatchEngine.hpp
Normal file
16
src/desktop/rule/matchEngine/BoolMatchEngine.hpp
Normal file
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
14
src/desktop/rule/matchEngine/IntMatchEngine.cpp
Normal file
14
src/desktop/rule/matchEngine/IntMatchEngine.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
16
src/desktop/rule/matchEngine/IntMatchEngine.hpp
Normal file
16
src/desktop/rule/matchEngine/IntMatchEngine.hpp
Normal file
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
23
src/desktop/rule/matchEngine/MatchEngine.cpp
Normal file
23
src/desktop/rule/matchEngine/MatchEngine.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
28
src/desktop/rule/matchEngine/MatchEngine.hpp
Normal file
28
src/desktop/rule/matchEngine/MatchEngine.hpp
Normal file
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
17
src/desktop/rule/matchEngine/RegexMatchEngine.cpp
Normal file
17
src/desktop/rule/matchEngine/RegexMatchEngine.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
23
src/desktop/rule/matchEngine/RegexMatchEngine.hpp
Normal file
23
src/desktop/rule/matchEngine/RegexMatchEngine.hpp
Normal file
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
12
src/desktop/rule/matchEngine/TagMatchEngine.cpp
Normal file
12
src/desktop/rule/matchEngine/TagMatchEngine.cpp
Normal file
|
|
@ -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);
|
||||
}
|
||||
16
src/desktop/rule/matchEngine/TagMatchEngine.hpp
Normal file
16
src/desktop/rule/matchEngine/TagMatchEngine.hpp
Normal file
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
12
src/desktop/rule/matchEngine/WorkspaceMatchEngine.cpp
Normal file
12
src/desktop/rule/matchEngine/WorkspaceMatchEngine.cpp
Normal file
|
|
@ -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);
|
||||
}
|
||||
16
src/desktop/rule/matchEngine/WorkspaceMatchEngine.hpp
Normal file
16
src/desktop/rule/matchEngine/WorkspaceMatchEngine.hpp
Normal file
|
|
@ -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 = "";
|
||||
};
|
||||
}
|
||||
17
src/desktop/rule/utils/SetUtils.hpp
Normal file
17
src/desktop/rule/utils/SetUtils.hpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
186
src/desktop/rule/windowRule/WindowRule.cpp
Normal file
186
src/desktop/rule/windowRule/WindowRule.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
37
src/desktop/rule/windowRule/WindowRule.hpp
Normal file
37
src/desktop/rule/windowRule/WindowRule.hpp
Normal file
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
642
src/desktop/rule/windowRule/WindowRuleApplicator.cpp
Normal file
642
src/desktop/rule/windowRule/WindowRuleApplicator.cpp
Normal file
|
|
@ -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());
|
||||
}
|
||||
148
src/desktop/rule/windowRule/WindowRuleApplicator.hpp
Normal file
148
src/desktop/rule/windowRule/WindowRuleApplicator.hpp
Normal file
|
|
@ -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);
|
||||
};
|
||||
};
|
||||
76
src/desktop/rule/windowRule/WindowRuleEffectContainer.cpp
Normal file
76
src/desktop/rule/windowRule/WindowRuleEffectContainer.cpp
Normal file
|
|
@ -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}) {
|
||||
;
|
||||
}
|
||||
79
src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp
Normal file
79
src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp
Normal file
|
|
@ -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();
|
||||
};
|
||||
153
src/desktop/types/OverridableVar.hpp
Normal file
153
src/desktop/types/OverridableVar.hpp
Normal file
|
|
@ -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
|
||||
};
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue