From b6ada0c1dd4e7662f175d267e2ede0b10f36bdab Mon Sep 17 00:00:00 2001 From: Kon <96987701+ChaosInfinited@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:10:53 +0000 Subject: [PATCH] view: consolidate group flags and apply window rules (#13694) * view: consolidate group flags and apply window rules Replace individual boolean flags with a bitmask for group policies and ensure that window-specific group rules are correctly inherited when windows are initialized or added to a group. * tests: add cases for locked groups and invade rule Add tests to verify window grouping behavior, specifically checking that locked groups prevent merging by default and that the 'invade' group rule correctly overrides this lock policy. --- hyprtester/src/tests/main/groups.cpp | 83 ++++++++++++++++++++++++++++ src/desktop/view/Group.cpp | 16 ++++-- src/desktop/view/Group.hpp | 7 +-- 3 files changed, 98 insertions(+), 8 deletions(-) diff --git a/hyprtester/src/tests/main/groups.cpp b/hyprtester/src/tests/main/groups.cpp index 2f9c50629..596bad31d 100644 --- a/hyprtester/src/tests/main/groups.cpp +++ b/hyprtester/src/tests/main/groups.cpp @@ -294,6 +294,89 @@ static bool test() { Tests::killAllWindows(); EXPECT(Tests::windowCount(), 0); + // Tests for grouping/merging logic + NLog::log("{}Testing locked groups w/ invade", Colors::GREEN); + + Tests::killAllWindows(); + EXPECT(Tests::windowCount(), 0); + + // Test normal, unlocked groups + { + auto winA = Tests::spawnKitty("unlocked"); + if (!winA) { + NLog::log("{}Error: unlocked kitty did not spawn", Colors::RED); + return false; + } + OK(getFromSocket("/dispatch togglegroup")); + + auto winB = Tests::spawnKitty("top"); + if (!winB) { + NLog::log("{}Error: top kitty did not spawn", Colors::RED); + return false; + } + + // Verify it DID merge into a group + { + auto str = getFromSocket("/clients"); + EXPECT_COUNT_STRING(str, "at: 22,22", 2); + } + } + + Tests::killAllWindows(); + EXPECT(Tests::windowCount(), 0); + + // Test locked groups + { + auto lockedWin = Tests::spawnKitty("locked"); + if (!lockedWin) { + NLog::log("{}Error: locked kitty did not spawn", Colors::RED); + return false; + } + OK(getFromSocket("/dispatch togglegroup")); + OK(getFromSocket(std::format("/dispatch focuswindow pid:{}", lockedWin->pid()))); + OK(getFromSocket("/dispatch lockactivegroup lock")); + + auto winB = Tests::spawnKitty("top"); + if (!winB) { + NLog::log("{}Error: top kitty did not spawn", Colors::RED); + return false; + } + + // Verify it did NOT merge into the locked group + { + auto str = getFromSocket("/clients"); + EXPECT_COUNT_STRING(str, "at: 22,22", 1); + } + } + + Tests::killAllWindows(); + EXPECT(Tests::windowCount(), 0); + + // Test locked groups WITH invade rule + { + OK(getFromSocket("/keyword windowrule[locked-im]:match:class ^locked|invade$")); + OK(getFromSocket("/keyword windowrule[locked-im]:group set always lock invade")); + + auto lockedWin = Tests::spawnKitty("locked"); + if (!lockedWin) { + NLog::log("{}Error: locked kitty did not spawn", Colors::RED); + return false; + } + + auto invadingWin = Tests::spawnKitty("invade"); + if (!invadingWin) { + NLog::log("{}Error: invading kitty did not spawn", Colors::RED); + return false; + } + + // Verify it DID merge into the locked group + auto str = getFromSocket("/clients"); + EXPECT_COUNT_STRING(str, "at: 22,22", 2); + } + + Tests::killAllWindows(); + EXPECT(Tests::windowCount(), 0); + return !ret; } diff --git a/src/desktop/view/Group.cpp b/src/desktop/view/Group.cpp index 06884cff3..287276b22 100644 --- a/src/desktop/view/Group.cpp +++ b/src/desktop/view/Group.cpp @@ -46,6 +46,7 @@ void CGroup::init() { for (const auto& w : m_windows) { RASSERT(!w->m_group, "CGroup: windows cannot contain grouped in init, this will explode"); w->m_group = m_self.lock(); + m_groupPolicyFlags |= w->m_groupRules; } g_layoutManager->switchTargets(m_windows.at(0)->m_target, m_target); @@ -104,6 +105,7 @@ void CGroup::add(PHLWINDOW w) { } w->m_group = m_self.lock(); + m_groupPolicyFlags |= w->m_groupRules; w->m_target->setSpaceGhost(m_target->space()); w->m_target->setFloating(m_target->floating()); @@ -296,19 +298,25 @@ size_t CGroup::size() const { } bool CGroup::locked() const { - return m_locked; + return m_groupPolicyFlags & GROUP_LOCK; } void CGroup::setLocked(bool x) { - m_locked = x; + if (x) + m_groupPolicyFlags |= GROUP_LOCK; + else + m_groupPolicyFlags &= ~GROUP_LOCK; } bool CGroup::denied() const { - return m_deny; + return m_groupPolicyFlags & GROUP_DENY; } void CGroup::setDenied(bool x) { - m_deny = x; + if (x) + m_groupPolicyFlags |= GROUP_DENY; + else + m_groupPolicyFlags &= ~GROUP_DENY; } void CGroup::updateWorkspace(PHLWORKSPACE ws) { diff --git a/src/desktop/view/Group.hpp b/src/desktop/view/Group.hpp index 36c4baae1..048c5023b 100644 --- a/src/desktop/view/Group.hpp +++ b/src/desktop/view/Group.hpp @@ -59,11 +59,10 @@ namespace Desktop::View { std::vector m_windows; - bool m_locked = false; - bool m_deny = false; - size_t m_current = 0; + + uint32_t m_groupPolicyFlags = 0; }; std::vector>& groups(); -}; \ No newline at end of file +};