Hyprland/src/desktop/rule/windowRule/WindowRule.cpp
Nikolai Nechaev 40d8fa8491
compositor: Configurable behavior when window to be focused conflicts with fullscreen (#12033)
Renames `misc:new_window_takes_over_fullscreen` into
`misc:on_focus_under_fullscreen` and implements the following behavior:

- By default, when a tiling window is being focused on a workspace where
  a fullscreen/maximized window exists, respect
  the `misc:on_focus_under_fullscreen` config variable.
2025-11-25 22:44:26 +00:00

166 lines
5.5 KiB
C++

#include "WindowRule.hpp"
#include "../../Window.hpp"
#include "../../../helpers/Monitor.hpp"
#include "../../../Compositor.hpp"
#include "../../../managers/TokenManager.hpp"
#include "../../../desktop/state/FocusState.hpp"
using namespace Desktop;
using namespace Desktop::Rule;
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(Desktop::focusState()->window() == 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;
}