desktop/windowRule: add confine_pointer window rule (#13379)

This commit is contained in:
fazzi 2026-05-02 20:34:43 +01:00 committed by GitHub
parent 30cd345add
commit e180c59b46
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 54 additions and 26 deletions

View file

@ -101,6 +101,7 @@ namespace Config::Lua::Bindings::Internal {
{"no_screen_share", []() -> ILuaConfigValue* { return new CLuaConfigBool(false); }, WE::WINDOW_RULE_EFFECT_NO_SCREEN_SHARE},
{"no_vrr", []() -> ILuaConfigValue* { return new CLuaConfigBool(false); }, WE::WINDOW_RULE_EFFECT_NO_VRR},
{"stay_focused", []() -> ILuaConfigValue* { return new CLuaConfigBool(false); }, WE::WINDOW_RULE_EFFECT_STAY_FOCUSED},
{"confine_pointer", []() -> ILuaConfigValue* { return new CLuaConfigBool(false); }, WE::WINDOW_RULE_EFFECT_CONFINE_POINTER},
};
std::string argStr(lua_State* L, int idx);

View file

@ -52,9 +52,9 @@ std::unordered_set<CWindowRuleEffectContainer::storageType> CWindowRuleApplicato
std::pair{std::ref(m_noFollowMouse), [this] { return noFollowMouseEffect(); }}, std::pair{std::ref(m_noScreenShare), [this] { return noScreenShareEffect(); }},
std::pair{std::ref(m_noVRR), [this] { return noVRREffect(); }}, std::pair{std::ref(m_persistentSize), [this] { return persistentSizeEffect(); }},
std::pair{std::ref(m_stayFocused), [this] { return stayFocusedEffect(); }}, std::pair{std::ref(m_idleInhibitMode), [this] { return idleInhibitModeEffect(); }},
std::pair{std::ref(m_borderSize), [this] { return borderSizeEffect(); }}, std::pair{std::ref(m_rounding), [this] { return roundingEffect(); }},
std::pair{std::ref(m_roundingPower), [this] { return roundingPowerEffect(); }}, std::pair{std::ref(m_scrollMouse), [this] { return scrollMouseEffect(); }},
std::pair{std::ref(m_scrollTouchpad), [this] { return scrollTouchpadEffect(); }},
std::pair{std::ref(m_confinePointer), [this] { return confinePointerEffect(); }}, std::pair{std::ref(m_borderSize), [this] { return borderSizeEffect(); }},
std::pair{std::ref(m_rounding), [this] { return roundingEffect(); }}, std::pair{std::ref(m_roundingPower), [this] { return roundingPowerEffect(); }},
std::pair{std::ref(m_scrollMouse), [this] { return scrollMouseEffect(); }}, std::pair{std::ref(m_scrollTouchpad), [this] { return scrollTouchpadEffect(); }},
std::pair{std::ref(m_animationStyle), [this] { return animationStyleEffect(); }}, std::pair{std::ref(m_maxSize), [this] { return maxSizeEffect(); }},
std::pair{std::ref(m_minSize), [this] { return minSizeEffect(); }}, std::pair{std::ref(m_activeBorderColor), [this] { return activeBorderColorEffect(); }},
std::pair{std::ref(m_inactiveBorderColor), [this] { return inactiveBorderColorEffect(); }}));
@ -342,6 +342,11 @@ CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyDynamicRule(const
m_stayFocused.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_CONFINE_POINTER: {
m_confinePointer.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_confinePointer.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_SCROLL_MOUSE: {
m_scrollMouse.first.set(std::get<float>(value), Types::PRIORITY_WINDOW_RULE);
m_scrollMouse.second |= rule->getPropertiesMask();

View file

@ -116,6 +116,7 @@ namespace Desktop::Rule {
DEFINE_PROP(bool, noVRR, false, WINDOW_RULE_EFFECT_NO_VRR)
DEFINE_PROP(bool, persistentSize, false, WINDOW_RULE_EFFECT_PERSISTENT_SIZE)
DEFINE_PROP(bool, stayFocused, false, WINDOW_RULE_EFFECT_STAY_FOCUSED)
DEFINE_PROP(bool, confinePointer, false, WINDOW_RULE_EFFECT_CONFINE_POINTER)
DEFINE_PROP(int, idleInhibitMode, false, WINDOW_RULE_EFFECT_IDLE_INHIBIT)

View file

@ -65,12 +65,13 @@ static const std::vector<std::string> EFFECT_STRINGS = {
"scroll_mouse", //
"scroll_touchpad", //
"stay_focused", //
"confine_pointer", //
"__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 == 55);
static_assert(WINDOW_RULE_EFFECT_LAST_STATIC == 56);
CWindowRuleEffectContainer::CWindowRuleEffectContainer() : IEffectContainer<eWindowRuleEffect>(std::vector<std::string>{EFFECT_STRINGS}) {
;

View file

@ -66,6 +66,7 @@ namespace Desktop::Rule {
WINDOW_RULE_EFFECT_SCROLL_MOUSE,
WINDOW_RULE_EFFECT_SCROLL_TOUCHPAD,
WINDOW_RULE_EFFECT_STAY_FOCUSED,
WINDOW_RULE_EFFECT_CONFINE_POINTER,
WINDOW_RULE_EFFECT_LAST_STATIC,
};

View file

@ -265,31 +265,50 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st
g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_MOVE);
// constraints
if (!overridePos.has_value() && !g_pSeatManager->m_mouse.expired() && isConstrained()) {
const auto SURF = Desktop::View::CWLSurface::fromResource(Desktop::focusState()->surface());
const auto CONSTRAINT = SURF ? SURF->constraint() : nullptr;
auto confineToRegion = [&](const CRegion& rg, SP<Desktop::View::CWLSurface> surf) {
const auto CLOSEST = rg.closestPoint(mouseCoords);
const auto BOX = surf->getSurfaceBoxGlobal();
const auto WINDOW = Desktop::View::CWindow::fromView(surf->view());
const auto CLOSESTLOCAL = (CLOSEST - (BOX.has_value() ? BOX->pos() : Vector2D{})) * (WINDOW ? WINDOW->m_X11SurfaceScaledBy : 1.0);
if (CONSTRAINT) {
if (CONSTRAINT->isLocked()) {
const auto HINT = CONSTRAINT->logicPositionHint();
g_pCompositor->warpCursorTo(HINT, true);
g_pCompositor->warpCursorTo(CLOSEST, true);
g_pSeatManager->sendPointerMotion(time, CLOSESTLOCAL);
PROTO::relativePointer->sendRelativeMotion(sc<uint64_t>(time) * 1000, {}, {});
};
if (!g_pSeatManager->m_mouse.expired()) {
const auto SURF = Desktop::View::CWLSurface::fromResource(Desktop::focusState()->surface());
if (isConstrained()) {
const auto CONSTRAINT = SURF ? SURF->constraint() : nullptr;
if (CONSTRAINT) {
if (CONSTRAINT->isLocked()) {
const auto HINT = CONSTRAINT->logicPositionHint();
g_pCompositor->warpCursorTo(HINT, true);
} else {
confineToRegion(CONSTRAINT->logicConstraintRegion(), SURF);
}
return;
} else {
const auto RG = CONSTRAINT->logicConstraintRegion();
const auto CLOSEST = RG.closestPoint(mouseCoords);
const auto BOX = SURF->getSurfaceBoxGlobal();
const auto WINDOW = Desktop::View::CWindow::fromView(SURF->view());
const auto CLOSESTLOCAL = (CLOSEST - (BOX.has_value() ? BOX->pos() : Vector2D{})) * (WINDOW ? WINDOW->m_X11SurfaceScaledBy : 1.0);
g_pCompositor->warpCursorTo(CLOSEST, true);
g_pSeatManager->sendPointerMotion(time, CLOSESTLOCAL);
PROTO::relativePointer->sendRelativeMotion(sc<uint64_t>(time) * 1000, {}, {});
Log::logger->log(Log::ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", rc<uintptr_t>(SURF.get()),
rc<uintptr_t>(CONSTRAINT.get()));
}
return;
} else
Log::logger->log(Log::ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", rc<uintptr_t>(SURF.get()),
rc<uintptr_t>(CONSTRAINT.get()));
} else {
const auto WINDOW = SURF ? Desktop::View::CWindow::fromView(SURF->view()) : nullptr;
if (WINDOW) {
if (WINDOW->m_ruleApplicator->confinePointer().valueOrDefault()) {
const auto BOX = SURF->getSurfaceBoxGlobal();
if (BOX.has_value()) {
CRegion rg;
rg.set(*BOX);
confineToRegion(rg, SURF);
}
return;
}
}
}
}
if (PMONITOR != Desktop::focusState()->monitor() && (*PMOUSEFOCUSMON || refocus) && m_forcedFocus.expired())