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
|
|
@ -8,12 +8,15 @@
|
|||
#include "../render/decorations/CHyprGroupBarDecoration.hpp"
|
||||
#include "config/ConfigDataValues.hpp"
|
||||
#include "config/ConfigValue.hpp"
|
||||
#include "../desktop/WindowRule.hpp"
|
||||
#include "../protocols/LayerShell.hpp"
|
||||
#include "../xwayland/XWayland.hpp"
|
||||
#include "../protocols/OutputManagement.hpp"
|
||||
#include "../managers/animation/AnimationManager.hpp"
|
||||
#include "../desktop/LayerSurface.hpp"
|
||||
#include "../desktop/rule/Engine.hpp"
|
||||
#include "../desktop/rule/windowRule/WindowRule.hpp"
|
||||
#include "../desktop/rule/layerRule/LayerRule.hpp"
|
||||
#include "../debug/HyprCtl.hpp"
|
||||
#include "defaultConfig.hpp"
|
||||
|
||||
#include "../render/Renderer.hpp"
|
||||
|
|
@ -299,54 +302,6 @@ static Hyprlang::CParseResult handleUnbind(const char* c, const char* v) {
|
|||
return result;
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleWindowRule(const char* c, const char* v) {
|
||||
const std::string VALUE = v;
|
||||
const std::string COMMAND = c;
|
||||
|
||||
const auto RESULT = g_pConfigManager->handleWindowRule(COMMAND, VALUE);
|
||||
|
||||
Hyprlang::CParseResult result;
|
||||
if (RESULT.has_value())
|
||||
result.setError(RESULT.value().c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleLayerRule(const char* c, const char* v) {
|
||||
const std::string VALUE = v;
|
||||
const std::string COMMAND = c;
|
||||
|
||||
const auto RESULT = g_pConfigManager->handleLayerRule(COMMAND, VALUE);
|
||||
|
||||
Hyprlang::CParseResult result;
|
||||
if (RESULT.has_value())
|
||||
result.setError(RESULT.value().c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleWindowRuleV2(const char* c, const char* v) {
|
||||
const std::string VALUE = v;
|
||||
const std::string COMMAND = c;
|
||||
|
||||
const auto RESULT = g_pConfigManager->handleWindowRule(COMMAND, VALUE);
|
||||
|
||||
Hyprlang::CParseResult result;
|
||||
if (RESULT.has_value())
|
||||
result.setError(RESULT.value().c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleBlurLS(const char* c, const char* v) {
|
||||
const std::string VALUE = v;
|
||||
const std::string COMMAND = c;
|
||||
|
||||
const auto RESULT = g_pConfigManager->handleBlurLS(COMMAND, VALUE);
|
||||
|
||||
Hyprlang::CParseResult result;
|
||||
if (RESULT.has_value())
|
||||
result.setError(RESULT.value().c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleWorkspaceRules(const char* c, const char* v) {
|
||||
const std::string VALUE = v;
|
||||
const std::string COMMAND = c;
|
||||
|
|
@ -431,6 +386,30 @@ static Hyprlang::CParseResult handleGesture(const char* c, const char* v) {
|
|||
return result;
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleWindowrule(const char* c, const char* v) {
|
||||
const std::string VALUE = v;
|
||||
const std::string COMMAND = c;
|
||||
|
||||
const auto RESULT = g_pConfigManager->handleWindowrule(COMMAND, VALUE);
|
||||
|
||||
Hyprlang::CParseResult result;
|
||||
if (RESULT.has_value())
|
||||
result.setError(RESULT.value().c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleLayerrule(const char* c, const char* v) {
|
||||
const std::string VALUE = v;
|
||||
const std::string COMMAND = c;
|
||||
|
||||
const auto RESULT = g_pConfigManager->handleLayerrule(COMMAND, VALUE);
|
||||
|
||||
Hyprlang::CParseResult result;
|
||||
if (RESULT.has_value())
|
||||
result.setError(RESULT.value().c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
void CConfigManager::registerConfigVar(const char* name, const Hyprlang::INT& val) {
|
||||
m_configValueNumber++;
|
||||
m_config->addConfigValue(name, val);
|
||||
|
|
@ -463,7 +442,6 @@ CConfigManager::CConfigManager() {
|
|||
m_config = makeUnique<Hyprlang::CConfig>(m_configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true});
|
||||
|
||||
registerConfigVar("general:border_size", Hyprlang::INT{1});
|
||||
registerConfigVar("general:no_border_on_floating", Hyprlang::INT{0});
|
||||
registerConfigVar("general:gaps_in", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "5"});
|
||||
registerConfigVar("general:gaps_out", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "20"});
|
||||
registerConfigVar("general:float_gaps", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "0"});
|
||||
|
|
@ -858,6 +836,16 @@ CConfigManager::CConfigManager() {
|
|||
m_config->addSpecialConfigValue("monitorv2", "max_luminance", Hyprlang::INT{-1});
|
||||
m_config->addSpecialConfigValue("monitorv2", "max_avg_luminance", Hyprlang::INT{-1});
|
||||
|
||||
// windowrule v3
|
||||
m_config->addSpecialCategory("windowrule", {.key = "name"});
|
||||
m_config->addSpecialConfigValue("windowrule", "enable", Hyprlang::INT{1});
|
||||
|
||||
// layerrule v2
|
||||
m_config->addSpecialCategory("layerrule", {.key = "name"});
|
||||
m_config->addSpecialConfigValue("layerrule", "enable", Hyprlang::INT{1});
|
||||
|
||||
reloadRuleConfigs();
|
||||
|
||||
// keywords
|
||||
m_config->registerHandler(&::handleExec, "exec", {false});
|
||||
m_config->registerHandler(&::handleRawExec, "execr", {false});
|
||||
|
|
@ -868,14 +856,12 @@ CConfigManager::CConfigManager() {
|
|||
m_config->registerHandler(&::handleBind, "bind", {true});
|
||||
m_config->registerHandler(&::handleUnbind, "unbind", {false});
|
||||
m_config->registerHandler(&::handleWorkspaceRules, "workspace", {false});
|
||||
m_config->registerHandler(&::handleWindowRule, "windowrule", {false});
|
||||
m_config->registerHandler(&::handleLayerRule, "layerrule", {false});
|
||||
m_config->registerHandler(&::handleWindowRuleV2, "windowrulev2", {false});
|
||||
m_config->registerHandler(&::handleWindowrule, "windowrule", {false});
|
||||
m_config->registerHandler(&::handleLayerrule, "layerrule", {false});
|
||||
m_config->registerHandler(&::handleBezier, "bezier", {false});
|
||||
m_config->registerHandler(&::handleAnimation, "animation", {false});
|
||||
m_config->registerHandler(&::handleSource, "source", {false});
|
||||
m_config->registerHandler(&::handleSubmap, "submap", {false});
|
||||
m_config->registerHandler(&::handleBlurLS, "blurls", {false});
|
||||
m_config->registerHandler(&::handlePlugin, "plugin", {false});
|
||||
m_config->registerHandler(&::handlePermission, "permission", {false});
|
||||
m_config->registerHandler(&::handleGesture, "gesture", {false});
|
||||
|
|
@ -905,6 +891,26 @@ CConfigManager::CConfigManager() {
|
|||
g_pEventLoopManager->doLater([ERR] { g_pHyprError->queueCreate(ERR.value(), CHyprColor{1.0, 0.1, 0.1, 1.0}); });
|
||||
}
|
||||
|
||||
void CConfigManager::reloadRuleConfigs() {
|
||||
// FIXME: this should also remove old values if they are removed
|
||||
|
||||
for (const auto& r : Desktop::Rule::allMatchPropStrings()) {
|
||||
m_config->addSpecialConfigValue("windowrule", ("match:" + r).c_str(), Hyprlang::STRING{""});
|
||||
}
|
||||
|
||||
for (const auto& r : Desktop::Rule::windowEffects()->allEffectStrings()) {
|
||||
m_config->addSpecialConfigValue("windowrule", r.c_str(), Hyprlang::STRING{""});
|
||||
}
|
||||
|
||||
for (const auto& r : Desktop::Rule::allMatchPropStrings()) {
|
||||
m_config->addSpecialConfigValue("layerrule", ("match:" + r).c_str(), Hyprlang::STRING{""});
|
||||
}
|
||||
|
||||
for (const auto& r : Desktop::Rule::layerEffects()->allEffectStrings()) {
|
||||
m_config->addSpecialConfigValue("layerrule", r.c_str(), Hyprlang::STRING{""});
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> CConfigManager::generateConfig(std::string configPath) {
|
||||
std::string parentPath = std::filesystem::path(configPath).parent_path();
|
||||
|
||||
|
|
@ -991,6 +997,7 @@ void CConfigManager::reload() {
|
|||
m_configCurrentPath = getMainConfigPath();
|
||||
const auto ERR = m_config->parse();
|
||||
const auto monitorError = handleMonitorv2();
|
||||
const auto ruleError = reloadRules();
|
||||
m_lastConfigVerificationWasSuccessful = !ERR.error && !monitorError.error;
|
||||
postConfigReload(ERR.error || !monitorError.error ? ERR : monitorError);
|
||||
}
|
||||
|
|
@ -1058,20 +1065,18 @@ void CConfigManager::setDefaultAnimationVars() {
|
|||
|
||||
std::optional<std::string> CConfigManager::resetHLConfig() {
|
||||
m_monitorRules.clear();
|
||||
m_windowRules.clear();
|
||||
g_pKeybindManager->clearKeybinds();
|
||||
g_pAnimationManager->removeAllBeziers();
|
||||
g_pAnimationManager->addBezierWithName("linear", Vector2D(0.0, 0.0), Vector2D(1.0, 1.0));
|
||||
g_pTrackpadGestures->clearGestures();
|
||||
|
||||
m_mAdditionalReservedAreas.clear();
|
||||
m_blurLSNamespaces.clear();
|
||||
m_workspaceRules.clear();
|
||||
setDefaultAnimationVars(); // reset anims
|
||||
m_declaredPlugins.clear();
|
||||
m_layerRules.clear();
|
||||
m_failedPluginConfigValues.clear();
|
||||
m_finalExecRequests.clear();
|
||||
m_keywordRules.clear();
|
||||
|
||||
// paths
|
||||
m_configPaths.clear();
|
||||
|
|
@ -1081,6 +1086,8 @@ std::optional<std::string> CConfigManager::resetHLConfig() {
|
|||
|
||||
const auto RET = verifyConfigExists();
|
||||
|
||||
reloadRuleConfigs();
|
||||
|
||||
return RET;
|
||||
}
|
||||
|
||||
|
|
@ -1179,6 +1186,77 @@ Hyprlang::CParseResult CConfigManager::handleMonitorv2() {
|
|||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::string> CConfigManager::addRuleFromConfigKey(const std::string& name) {
|
||||
const auto ENABLED = m_config->getSpecialConfigValuePtr("windowrule", "enable", name.c_str());
|
||||
if (ENABLED && ENABLED->m_bSetByUser && std::any_cast<Hyprlang::INT>(ENABLED->getValue()) == 0)
|
||||
return std::nullopt;
|
||||
|
||||
SP<Desktop::Rule::CWindowRule> rule = makeShared<Desktop::Rule::CWindowRule>(name);
|
||||
|
||||
for (const auto& r : Desktop::Rule::allMatchPropStrings()) {
|
||||
auto VAL = m_config->getSpecialConfigValuePtr("windowrule", ("match:" + r).c_str(), name.c_str());
|
||||
if (VAL && VAL->m_bSetByUser)
|
||||
rule->registerMatch(Desktop::Rule::matchPropFromString(r).value_or(Desktop::Rule::RULE_PROP_NONE), std::any_cast<Hyprlang::STRING>(VAL->getValue()));
|
||||
}
|
||||
|
||||
for (const auto& e : Desktop::Rule::windowEffects()->allEffectStrings()) {
|
||||
auto VAL = m_config->getSpecialConfigValuePtr("windowrule", e.c_str(), name.c_str());
|
||||
if (VAL && VAL->m_bSetByUser)
|
||||
rule->addEffect(Desktop::Rule::windowEffects()->get(e).value_or(Desktop::Rule::WINDOW_RULE_EFFECT_NONE), std::any_cast<Hyprlang::STRING>(VAL->getValue()));
|
||||
}
|
||||
|
||||
Desktop::Rule::ruleEngine()->registerRule(std::move(rule));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> CConfigManager::addLayerRuleFromConfigKey(const std::string& name) {
|
||||
|
||||
const auto ENABLED = m_config->getSpecialConfigValuePtr("layerrule", "enable", name.c_str());
|
||||
if (ENABLED && ENABLED->m_bSetByUser && std::any_cast<Hyprlang::INT>(ENABLED->getValue()) != 0)
|
||||
return std::nullopt;
|
||||
|
||||
SP<Desktop::Rule::CLayerRule> rule = makeShared<Desktop::Rule::CLayerRule>(name);
|
||||
|
||||
for (const auto& r : Desktop::Rule::allMatchPropStrings()) {
|
||||
auto VAL = m_config->getSpecialConfigValuePtr("layerrule", ("match:" + r).c_str(), name.c_str());
|
||||
if (VAL && VAL->m_bSetByUser)
|
||||
rule->registerMatch(Desktop::Rule::matchPropFromString(r).value_or(Desktop::Rule::RULE_PROP_NONE), std::any_cast<Hyprlang::STRING>(VAL->getValue()));
|
||||
}
|
||||
|
||||
for (const auto& e : Desktop::Rule::layerEffects()->allEffectStrings()) {
|
||||
auto VAL = m_config->getSpecialConfigValuePtr("layerrule", e.c_str(), name.c_str());
|
||||
if (VAL && VAL->m_bSetByUser)
|
||||
rule->addEffect(Desktop::Rule::layerEffects()->get(e).value_or(Desktop::Rule::LAYER_RULE_EFFECT_NONE), std::any_cast<Hyprlang::STRING>(VAL->getValue()));
|
||||
}
|
||||
|
||||
Desktop::Rule::ruleEngine()->registerRule(std::move(rule));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Hyprlang::CParseResult CConfigManager::reloadRules() {
|
||||
Desktop::Rule::ruleEngine()->clearAllRules();
|
||||
|
||||
Hyprlang::CParseResult result;
|
||||
for (const auto& name : m_config->listKeysForSpecialCategory("windowrule")) {
|
||||
const auto error = addRuleFromConfigKey(name);
|
||||
if (error.has_value())
|
||||
result.setError(error.value().c_str());
|
||||
}
|
||||
for (const auto& name : m_config->listKeysForSpecialCategory("layerrule")) {
|
||||
const auto error = addLayerRuleFromConfigKey(name);
|
||||
if (error.has_value())
|
||||
result.setError(error.value().c_str());
|
||||
}
|
||||
|
||||
for (auto& rule : m_keywordRules) {
|
||||
Desktop::Rule::ruleEngine()->registerRule(SP<Desktop::Rule::IRule>{rule});
|
||||
}
|
||||
|
||||
Desktop::Rule::ruleEngine()->updateAllRules();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
|
||||
updateWatcher();
|
||||
|
||||
|
|
@ -1504,229 +1582,6 @@ SWorkspaceRule CConfigManager::mergeWorkspaceRules(const SWorkspaceRule& rule1,
|
|||
return mergedRule;
|
||||
}
|
||||
|
||||
std::vector<SP<CWindowRule>> CConfigManager::getMatchingRules(PHLWINDOW pWindow, bool dynamic, bool shadowExec) {
|
||||
if (!valid(pWindow))
|
||||
return std::vector<SP<CWindowRule>>();
|
||||
|
||||
// if the window is unmapped, don't process exec rules yet.
|
||||
shadowExec = shadowExec || !pWindow->m_isMapped;
|
||||
|
||||
std::vector<SP<CWindowRule>> returns;
|
||||
|
||||
Debug::log(LOG, "Searching for matching rules for {} (title: {})", pWindow->m_class, pWindow->m_title);
|
||||
|
||||
// since some rules will be applied later, we need to store some flags
|
||||
bool hasFloating = pWindow->m_isFloating;
|
||||
bool hasFullscreen = pWindow->isFullscreen();
|
||||
bool isGrouped = pWindow->m_groupData.pNextWindow;
|
||||
|
||||
// local tags for dynamic tag rule match
|
||||
auto tags = pWindow->m_tags;
|
||||
|
||||
for (auto const& rule : m_windowRules) {
|
||||
// check if we have a matching rule
|
||||
if (!rule->m_v2) {
|
||||
try {
|
||||
if (rule->m_value.starts_with("tag:") && !tags.isTagged(rule->m_value.substr(4)))
|
||||
continue;
|
||||
|
||||
if (rule->m_value.starts_with("title:") && !rule->m_v1Regex.passes(pWindow->m_title))
|
||||
continue;
|
||||
|
||||
if (!rule->m_v1Regex.passes(pWindow->m_class))
|
||||
continue;
|
||||
|
||||
} catch (...) {
|
||||
Debug::log(ERR, "Regex error at {}", rule->m_value);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (rule->m_X11 != -1) {
|
||||
if (pWindow->m_isX11 != rule->m_X11)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rule->m_floating != -1) {
|
||||
if (hasFloating != rule->m_floating)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rule->m_fullscreen != -1) {
|
||||
if (hasFullscreen != rule->m_fullscreen)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rule->m_pinned != -1) {
|
||||
if (pWindow->m_pinned != rule->m_pinned)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rule->m_focus != -1) {
|
||||
if (rule->m_focus != (g_pCompositor->m_lastWindow.lock() == pWindow))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rule->m_group != -1) {
|
||||
if (rule->m_group != isGrouped)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rule->m_modal != -1) {
|
||||
if (rule->m_modal != pWindow->isModal())
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!rule->m_fullscreenState.empty()) {
|
||||
const auto ARGS = CVarList(rule->m_fullscreenState, 2, ' ');
|
||||
//
|
||||
std::optional<eFullscreenMode> internalMode, clientMode;
|
||||
|
||||
if (ARGS[0] == "*")
|
||||
internalMode = std::nullopt;
|
||||
else if (isNumber(ARGS[0]))
|
||||
internalMode = sc<eFullscreenMode>(std::stoi(ARGS[0]));
|
||||
else
|
||||
throw std::runtime_error("szFullscreenState internal mode not valid");
|
||||
|
||||
if (ARGS[1] == "*")
|
||||
clientMode = std::nullopt;
|
||||
else if (isNumber(ARGS[1]))
|
||||
clientMode = sc<eFullscreenMode>(std::stoi(ARGS[1]));
|
||||
else
|
||||
throw std::runtime_error("szFullscreenState client mode not valid");
|
||||
|
||||
if (internalMode.has_value() && pWindow->m_fullscreenState.internal != internalMode)
|
||||
continue;
|
||||
|
||||
if (clientMode.has_value() && pWindow->m_fullscreenState.client != clientMode)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!rule->m_onWorkspace.empty()) {
|
||||
const auto PWORKSPACE = pWindow->m_workspace;
|
||||
if (!PWORKSPACE || !PWORKSPACE->matchesStaticSelector(rule->m_onWorkspace))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!rule->m_contentType.empty()) {
|
||||
try {
|
||||
const auto contentType = NContentType::fromString(rule->m_contentType);
|
||||
if (pWindow->getContentType() != contentType)
|
||||
continue;
|
||||
} catch (std::exception& e) { Debug::log(ERR, "Rule \"content:{}\" failed with: {}", rule->m_contentType, e.what()); }
|
||||
}
|
||||
|
||||
if (!rule->m_xdgTag.empty()) {
|
||||
if (pWindow->xdgTag().value_or("") != rule->m_xdgTag)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!rule->m_workspace.empty()) {
|
||||
const auto PWORKSPACE = pWindow->m_workspace;
|
||||
|
||||
if (!PWORKSPACE)
|
||||
continue;
|
||||
|
||||
if (rule->m_workspace.starts_with("name:")) {
|
||||
if (PWORKSPACE->m_name != rule->m_workspace.substr(5))
|
||||
continue;
|
||||
} else {
|
||||
// number
|
||||
if (!isNumber(rule->m_workspace))
|
||||
throw std::runtime_error("szWorkspace not name: or number");
|
||||
|
||||
const int64_t ID = std::stoll(rule->m_workspace);
|
||||
|
||||
if (PWORKSPACE->m_id != ID)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rule->m_tag.empty() && !tags.isTagged(rule->m_tag))
|
||||
continue;
|
||||
|
||||
if (!rule->m_class.empty() && !rule->m_classRegex.passes(pWindow->m_class))
|
||||
continue;
|
||||
|
||||
if (!rule->m_title.empty() && !rule->m_titleRegex.passes(pWindow->m_title))
|
||||
continue;
|
||||
|
||||
if (!rule->m_initialTitle.empty() && !rule->m_initialTitleRegex.passes(pWindow->m_initialTitle))
|
||||
continue;
|
||||
|
||||
if (!rule->m_initialClass.empty() && !rule->m_initialClassRegex.passes(pWindow->m_initialClass))
|
||||
continue;
|
||||
|
||||
} catch (std::exception& e) {
|
||||
Debug::log(ERR, "Regex error at {} ({})", rule->m_value, e.what());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// applies. Read the rule and behave accordingly
|
||||
Debug::log(LOG, "Window rule {} -> {} matched {}", rule->m_rule, rule->m_value, pWindow);
|
||||
|
||||
returns.emplace_back(rule);
|
||||
|
||||
// apply tag with local tags
|
||||
if (rule->m_ruleType == CWindowRule::RULE_TAG) {
|
||||
CVarList vars{rule->m_rule, 0, 's', true};
|
||||
if (vars.size() == 2 && vars[0] == "tag")
|
||||
tags.applyTag(vars[1], true);
|
||||
}
|
||||
|
||||
if (dynamic)
|
||||
continue;
|
||||
|
||||
if (rule->m_rule == "float")
|
||||
hasFloating = true;
|
||||
else if (rule->m_rule == "fullscreen")
|
||||
hasFullscreen = true;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> PIDs = {sc<uint64_t>(pWindow->getPID())};
|
||||
while (getPPIDof(PIDs.back()) > 10)
|
||||
PIDs.push_back(getPPIDof(PIDs.back()));
|
||||
|
||||
bool anyExecFound = false;
|
||||
|
||||
for (auto const& er : m_execRequestedRules) {
|
||||
if (std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == er.iPid; })) {
|
||||
returns.emplace_back(makeShared<CWindowRule>(er.szRule, "", false, true));
|
||||
anyExecFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (anyExecFound && !shadowExec) // remove exec rules to unclog searches in the future, why have the garbage here.
|
||||
std::erase_if(m_execRequestedRules, [&](const SExecRequestedRule& other) { return std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == other.iPid; }); });
|
||||
|
||||
return returns;
|
||||
}
|
||||
|
||||
std::vector<SP<CLayerRule>> CConfigManager::getMatchingRules(PHLLS pLS) {
|
||||
std::vector<SP<CLayerRule>> returns;
|
||||
|
||||
if (!pLS->m_layerSurface || pLS->m_fadingOut)
|
||||
return returns;
|
||||
|
||||
for (auto const& lr : m_layerRules) {
|
||||
if (lr->m_targetNamespace.starts_with("address:0x")) {
|
||||
if (std::format("address:0x{:x}", rc<uintptr_t>(pLS.get())) != lr->m_targetNamespace)
|
||||
continue;
|
||||
} else if (!lr->m_targetNamespaceRegex.passes(pLS->m_layerSurface->m_layerNamespace))
|
||||
continue;
|
||||
|
||||
// hit
|
||||
returns.emplace_back(lr);
|
||||
}
|
||||
|
||||
if (shouldBlurLS(pLS->m_layerSurface->m_layerNamespace))
|
||||
returns.emplace_back(makeShared<CLayerRule>(pLS->m_layerSurface->m_layerNamespace, "blur"));
|
||||
|
||||
return returns;
|
||||
}
|
||||
|
||||
void CConfigManager::dispatchExecOnce() {
|
||||
if (m_firstExecDispatched || m_isFirstLaunch)
|
||||
return;
|
||||
|
|
@ -1832,16 +1687,6 @@ bool CConfigManager::deviceConfigExists(const std::string& dev) {
|
|||
return m_config->specialCategoryExistsForKey("device", copy.c_str());
|
||||
}
|
||||
|
||||
bool CConfigManager::shouldBlurLS(const std::string& ns) {
|
||||
for (auto const& bls : m_blurLSNamespaces) {
|
||||
if (bls == ns) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CConfigManager::ensureMonitorStatus() {
|
||||
for (auto const& rm : g_pCompositor->m_realMonitors) {
|
||||
if (!rm->m_output || rm->m_isUnsafeFallback)
|
||||
|
|
@ -1895,7 +1740,7 @@ void CConfigManager::ensureVRR(PHLMONITOR pMonitor) {
|
|||
return; // ???
|
||||
|
||||
bool wantVRR = PWORKSPACE->m_hasFullscreenWindow && (PWORKSPACE->m_fullscreenMode & FSMODE_FULLSCREEN);
|
||||
if (wantVRR && PWORKSPACE->getFullscreenWindow()->m_windowData.noVRR.valueOrDefault())
|
||||
if (wantVRR && PWORKSPACE->getFullscreenWindow()->m_ruleApplicator->noVRR().valueOrDefault())
|
||||
wantVRR = false;
|
||||
|
||||
if (wantVRR && USEVRR == 3) {
|
||||
|
|
@ -1964,10 +1809,6 @@ const std::vector<SWorkspaceRule>& CConfigManager::getAllWorkspaceRules() {
|
|||
return m_workspaceRules;
|
||||
}
|
||||
|
||||
void CConfigManager::addExecRule(const SExecRequestedRule& rule) {
|
||||
m_execRequestedRules.push_back(rule);
|
||||
}
|
||||
|
||||
void CConfigManager::handlePluginLoads() {
|
||||
if (!g_pPluginSystem)
|
||||
return;
|
||||
|
|
@ -2676,239 +2517,6 @@ std::optional<std::string> CConfigManager::handleUnbind(const std::string& comma
|
|||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::string> CConfigManager::handleWindowRule(const std::string& command, const std::string& value) {
|
||||
const auto VARLIST = CVarList(value, 0, ',', true);
|
||||
|
||||
std::vector<std::string_view> tokens;
|
||||
std::unordered_map<std::string_view, std::string_view> params;
|
||||
|
||||
bool parsingParams = false;
|
||||
|
||||
for (const auto& varStr : VARLIST) {
|
||||
std::string_view var = varStr;
|
||||
auto sep = var.find(':');
|
||||
std::string_view key = (sep != std::string_view::npos) ? var.substr(0, sep) : var;
|
||||
bool isParam = (sep != std::string_view::npos && !(key.starts_with("workspace ") || (key.starts_with("monitor ")) || key.ends_with("plugin")));
|
||||
|
||||
if (!parsingParams) {
|
||||
if (!isParam) {
|
||||
tokens.emplace_back(var);
|
||||
continue;
|
||||
}
|
||||
|
||||
parsingParams = true;
|
||||
}
|
||||
|
||||
if (sep == std::string_view::npos)
|
||||
return std::format("Invalid rule: {}, Invalid parameter: {}", value, std::string(var));
|
||||
|
||||
auto pos = var.find_first_not_of(' ', sep + 1);
|
||||
std::string_view val = (pos != std::string_view::npos) ? var.substr(pos) : std::string_view{};
|
||||
params[key] = val;
|
||||
}
|
||||
|
||||
auto get = [&](std::string_view key) -> std::string_view {
|
||||
if (auto it = params.find(key); it != params.end())
|
||||
return it->second;
|
||||
return {};
|
||||
};
|
||||
|
||||
auto applyParams = [&](SP<CWindowRule> rule) -> bool {
|
||||
bool set = false;
|
||||
|
||||
if (auto v = get("class"); !v.empty()) {
|
||||
set |= (rule->m_class = v, true);
|
||||
rule->m_classRegex = {std::string(v)};
|
||||
}
|
||||
if (auto v = get("title"); !v.empty()) {
|
||||
set |= (rule->m_title = v, true);
|
||||
rule->m_titleRegex = {std::string(v)};
|
||||
}
|
||||
if (auto v = get("tag"); !v.empty())
|
||||
set |= (rule->m_tag = v, true);
|
||||
if (auto v = get("initialClass"); !v.empty()) {
|
||||
set |= (rule->m_initialClass = v, true);
|
||||
rule->m_initialClassRegex = {std::string(v)};
|
||||
}
|
||||
if (auto v = get("initialTitle"); !v.empty()) {
|
||||
set |= (rule->m_initialTitle = v, true);
|
||||
rule->m_initialTitleRegex = {std::string(v)};
|
||||
}
|
||||
|
||||
if (auto v = get("xwayland"); !v.empty())
|
||||
set |= (rule->m_X11 = (v == "1"), true);
|
||||
if (auto v = get("floating"); !v.empty())
|
||||
set |= (rule->m_floating = (v == "1"), true);
|
||||
if (auto v = get("fullscreen"); !v.empty())
|
||||
set |= (rule->m_fullscreen = (v == "1"), true);
|
||||
if (auto v = get("pinned"); !v.empty())
|
||||
set |= (rule->m_pinned = (v == "1"), true);
|
||||
if (auto v = get("focus"); !v.empty())
|
||||
set |= (rule->m_focus = (v == "1"), true);
|
||||
if (auto v = get("group"); !v.empty())
|
||||
set |= (rule->m_group = (v == "1"), true);
|
||||
if (auto v = get("modal"); !v.empty())
|
||||
set |= (rule->m_modal = (v == "1"), true);
|
||||
|
||||
if (auto v = get("fullscreenstate"); !v.empty())
|
||||
set |= (rule->m_fullscreenState = v, true);
|
||||
if (auto v = get("workspace"); !v.empty())
|
||||
set |= (rule->m_workspace = v, true);
|
||||
if (auto v = get("onworkspace"); !v.empty())
|
||||
set |= (rule->m_onWorkspace = v, true);
|
||||
if (auto v = get("content"); !v.empty())
|
||||
set |= (rule->m_contentType = v, true);
|
||||
if (auto v = get("xdgTag"); !v.empty())
|
||||
set |= (rule->m_xdgTag = v, true);
|
||||
|
||||
return set;
|
||||
};
|
||||
|
||||
std::vector<SP<CWindowRule>> rules;
|
||||
|
||||
for (auto token : tokens) {
|
||||
if (token.starts_with("unset")) {
|
||||
std::string ruleName = "";
|
||||
if (token.size() <= 6 || token.contains("all"))
|
||||
ruleName = "all";
|
||||
else
|
||||
ruleName = std::string(token.substr(6));
|
||||
auto rule = makeShared<CWindowRule>(ruleName, value, true);
|
||||
applyParams(rule);
|
||||
std::erase_if(m_windowRules, [&](const auto& other) {
|
||||
if (!other->m_v2)
|
||||
return other->m_class == rule->m_class && !rule->m_class.empty();
|
||||
|
||||
if (rule->m_ruleType != other->m_ruleType && ruleName != "all")
|
||||
return false;
|
||||
if (!rule->m_tag.empty() && rule->m_tag != other->m_tag)
|
||||
return false;
|
||||
if (!rule->m_class.empty() && rule->m_class != other->m_class)
|
||||
return false;
|
||||
if (!rule->m_title.empty() && rule->m_title != other->m_title)
|
||||
return false;
|
||||
if (!rule->m_initialClass.empty() && rule->m_initialClass != other->m_initialClass)
|
||||
return false;
|
||||
if (!rule->m_initialTitle.empty() && rule->m_initialTitle != other->m_initialTitle)
|
||||
return false;
|
||||
if (rule->m_X11 != -1 && rule->m_X11 != other->m_X11)
|
||||
return false;
|
||||
if (rule->m_floating != -1 && rule->m_floating != other->m_floating)
|
||||
return false;
|
||||
if (rule->m_fullscreen != -1 && rule->m_fullscreen != other->m_fullscreen)
|
||||
return false;
|
||||
if (rule->m_pinned != -1 && rule->m_pinned != other->m_pinned)
|
||||
return false;
|
||||
if (!rule->m_fullscreenState.empty() && rule->m_fullscreenState != other->m_fullscreenState)
|
||||
return false;
|
||||
if (!rule->m_workspace.empty() && rule->m_workspace != other->m_workspace)
|
||||
return false;
|
||||
if (rule->m_focus != -1 && rule->m_focus != other->m_focus)
|
||||
return false;
|
||||
if (!rule->m_onWorkspace.empty() && rule->m_onWorkspace != other->m_onWorkspace)
|
||||
return false;
|
||||
if (!rule->m_contentType.empty() && rule->m_contentType != other->m_contentType)
|
||||
return false;
|
||||
if (rule->m_group != -1 && rule->m_group != other->m_group)
|
||||
return false;
|
||||
if (rule->m_modal != -1 && rule->m_modal != other->m_modal)
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
auto rule = makeShared<CWindowRule>(std::string(token), value, true);
|
||||
if (rule->m_ruleType == CWindowRule::RULE_INVALID) {
|
||||
Debug::log(ERR, "Invalid rule found: {}, Invalid value: {}", value, token);
|
||||
return std::format("Invalid rule found: {}, Invalid value: {}", value, token);
|
||||
}
|
||||
if (applyParams(rule))
|
||||
rules.emplace_back(rule);
|
||||
else {
|
||||
Debug::log(INFO, "===== Skipping rule: {}, Invalid parameters", rule->m_value);
|
||||
return std::format("Invalid parameters found in: {}", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rules.empty() && tokens.empty())
|
||||
return "Invalid rule syntax: no rules provided";
|
||||
|
||||
for (auto& rule : rules) {
|
||||
if (rule->m_ruleType == CWindowRule::RULE_SIZE || rule->m_ruleType == CWindowRule::RULE_MAXSIZE || rule->m_ruleType == CWindowRule::RULE_MINSIZE)
|
||||
m_windowRules.insert(m_windowRules.begin(), rule);
|
||||
else
|
||||
m_windowRules.emplace_back(rule);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::string> CConfigManager::handleLayerRule(const std::string& command, const std::string& value) {
|
||||
const auto RULE = trim(value.substr(0, value.find_first_of(',')));
|
||||
const auto VALUE = trim(value.substr(value.find_first_of(',') + 1));
|
||||
|
||||
// check rule and value
|
||||
if (RULE.empty() || VALUE.empty())
|
||||
return "empty rule?";
|
||||
|
||||
if (RULE == "unset") {
|
||||
std::erase_if(m_layerRules, [&](const auto& other) { return other->m_targetNamespace == VALUE; });
|
||||
return {};
|
||||
}
|
||||
|
||||
auto rule = makeShared<CLayerRule>(RULE, VALUE);
|
||||
|
||||
if (rule->m_ruleType == CLayerRule::RULE_INVALID) {
|
||||
Debug::log(ERR, "Invalid rule found: {}", RULE);
|
||||
return "Invalid rule found: " + RULE;
|
||||
}
|
||||
|
||||
rule->m_targetNamespaceRegex = {VALUE};
|
||||
|
||||
m_layerRules.emplace_back(rule);
|
||||
|
||||
for (auto const& m : g_pCompositor->m_monitors)
|
||||
for (auto const& lsl : m->m_layerSurfaceLayers)
|
||||
for (auto const& ls : lsl)
|
||||
ls->applyRules();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBlur) {
|
||||
const bool BYADDRESS = name.starts_with("address:");
|
||||
std::string matchName = name;
|
||||
|
||||
if (BYADDRESS)
|
||||
matchName = matchName.substr(8);
|
||||
|
||||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
for (auto const& lsl : m->m_layerSurfaceLayers) {
|
||||
for (auto const& ls : lsl) {
|
||||
if (BYADDRESS) {
|
||||
if (std::format("0x{:x}", rc<uintptr_t>(ls.get())) == matchName)
|
||||
ls->m_forceBlur = forceBlur;
|
||||
} else if (ls->m_namespace == matchName)
|
||||
ls->m_forceBlur = forceBlur;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> CConfigManager::handleBlurLS(const std::string& command, const std::string& value) {
|
||||
if (value.starts_with("remove,")) {
|
||||
const auto TOREMOVE = trim(value.substr(7));
|
||||
if (std::erase_if(m_blurLSNamespaces, [&](const auto& other) { return other == TOREMOVE; }))
|
||||
updateBlurredLS(TOREMOVE, false);
|
||||
return {};
|
||||
}
|
||||
|
||||
m_blurLSNamespaces.emplace_back(value);
|
||||
updateBlurredLS(value, true);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::string& command, const std::string& value) {
|
||||
// This can either be the monitor or the workspace identifier
|
||||
const auto FIRST_DELIM = value.find_first_of(',');
|
||||
|
|
@ -3229,6 +2837,82 @@ std::optional<std::string> CConfigManager::handleGesture(const std::string& comm
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> CConfigManager::handleWindowrule(const std::string& command, const std::string& value) {
|
||||
CVarList2 data(std::string{value}, 0, ',');
|
||||
|
||||
SP<Desktop::Rule::CWindowRule> rule = makeShared<Desktop::Rule::CWindowRule>();
|
||||
|
||||
const auto& PROPS = Desktop::Rule::allMatchPropStrings();
|
||||
const auto& EFFECTS = Desktop::Rule::windowEffects()->allEffectStrings();
|
||||
|
||||
for (const auto& el : data) {
|
||||
// split on space, no need for a CVarList here
|
||||
size_t spacePos = el.find(' ');
|
||||
if (spacePos == std::string::npos)
|
||||
return std::format("invalid field {}: missing a value", el);
|
||||
|
||||
const bool FIRST_IS_PROP = el.starts_with("match:");
|
||||
const auto FIRST = FIRST_IS_PROP ? el.substr(6, spacePos - 6) : el.substr(0, spacePos);
|
||||
if (FIRST_IS_PROP && std::ranges::contains(PROPS, FIRST)) {
|
||||
// it's a prop
|
||||
const auto PROP = Desktop::Rule::matchPropFromString(FIRST);
|
||||
if (!PROP.has_value())
|
||||
return std::format("invalid prop {}", el);
|
||||
rule->registerMatch(*PROP, std::string{el.substr(spacePos + 1)});
|
||||
} else if (!FIRST_IS_PROP && std::ranges::contains(EFFECTS, FIRST)) {
|
||||
// it's an effect
|
||||
const auto EFFECT = Desktop::Rule::windowEffects()->get(FIRST);
|
||||
if (!EFFECT.has_value())
|
||||
return std::format("invalid effect {}", el);
|
||||
rule->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)});
|
||||
} else
|
||||
return std::format("invalid field type {}", FIRST);
|
||||
}
|
||||
|
||||
m_keywordRules.emplace_back(std::move(rule));
|
||||
if (g_pHyprCtl && g_pHyprCtl->m_currentRequestParams.isDynamicKeyword)
|
||||
Desktop::Rule::ruleEngine()->registerRule(SP<Desktop::Rule::IRule>{m_keywordRules.back()});
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> CConfigManager::handleLayerrule(const std::string& command, const std::string& value) {
|
||||
CVarList2 data(std::string{value}, 0, ',');
|
||||
|
||||
SP<Desktop::Rule::CLayerRule> rule = makeShared<Desktop::Rule::CLayerRule>();
|
||||
|
||||
const auto& PROPS = Desktop::Rule::allMatchPropStrings();
|
||||
const auto& EFFECTS = Desktop::Rule::layerEffects()->allEffectStrings();
|
||||
|
||||
for (const auto& el : data) {
|
||||
// split on space, no need for a CVarList here
|
||||
size_t spacePos = el.find(' ');
|
||||
if (spacePos == std::string::npos)
|
||||
return std::format("invalid field {}: missing a value", el);
|
||||
|
||||
const bool FIRST_IS_PROP = el.starts_with("match:");
|
||||
const auto FIRST = FIRST_IS_PROP ? el.substr(6, spacePos - 6) : el.substr(0, spacePos);
|
||||
if (FIRST_IS_PROP && std::ranges::contains(PROPS, FIRST)) {
|
||||
// it's a prop
|
||||
const auto PROP = Desktop::Rule::matchPropFromString(FIRST);
|
||||
if (!PROP.has_value())
|
||||
return std::format("invalid prop {}", el);
|
||||
rule->registerMatch(*PROP, std::string{el.substr(spacePos + 1)});
|
||||
} else if (!FIRST_IS_PROP && std::ranges::contains(EFFECTS, FIRST)) {
|
||||
// it's an effect
|
||||
const auto EFFECT = Desktop::Rule::layerEffects()->get(FIRST);
|
||||
if (!EFFECT.has_value())
|
||||
return std::format("invalid effect {}", el);
|
||||
rule->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)});
|
||||
} else
|
||||
return std::format("invalid field type {}", FIRST);
|
||||
}
|
||||
|
||||
m_keywordRules.emplace_back(std::move(rule));
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::vector<SConfigOptionDescription>& CConfigManager::getAllDescriptions() {
|
||||
return CONFIG_OPTIONS;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue