diff --git a/hyprtester/src/tests/main/workspaces.cpp b/hyprtester/src/tests/main/workspaces.cpp index c1b9690a..036ddaf5 100644 --- a/hyprtester/src/tests/main/workspaces.cpp +++ b/hyprtester/src/tests/main/workspaces.cpp @@ -20,6 +20,91 @@ using namespace Hyprutils::Utils; #define UP CUniquePointer #define SP CSharedPointer +static bool testSpecialWorkspaceFullscreen() { + NLog::log("{}Testing special workspace fullscreen detection", Colors::YELLOW); + + CScopeGuard guard = {[&]() { + NLog::log("{}Cleaning up special workspace fullscreen test", Colors::YELLOW); + // Close special workspace if open + auto monitors = getFromSocket("/monitors"); + if (monitors.contains("(special:test_fs_special)") && !monitors.contains("special workspace: 0 ()")) + getFromSocket("/dispatch togglespecialworkspace test_fs_special"); + Tests::killAllWindows(); + OK(getFromSocket("/reload")); + }}; + + getFromSocket("/dispatch workspace 1"); + EXPECT(Tests::windowCount(), 0); + + NLog::log("{}Test 1: Fullscreen detection on special workspace", Colors::YELLOW); + + OK(getFromSocket("/dispatch workspace special:test_fs_special")); + + if (!Tests::spawnKitty("kitty_special")) + return false; + + { + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitty_special"); + EXPECT_CONTAINS(str, "(special:test_fs_special)"); + } + + OK(getFromSocket("/dispatch fullscreen 0")); + + { + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "fullscreen: 2"); + } + + { + auto str = getFromSocket("/monitors"); + EXPECT_CONTAINS(str, "(special:test_fs_special)"); + } + + NLog::log("{}Test 2: Special workspace fullscreen precedence", Colors::YELLOW); + + // Close special workspace before spawning on regular workspace + OK(getFromSocket("/dispatch togglespecialworkspace test_fs_special")); + getFromSocket("/dispatch workspace 1"); + + if (!Tests::spawnKitty("kitty_regular")) + return false; + + OK(getFromSocket("/dispatch fullscreen 0")); + + { + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitty_regular"); + EXPECT_CONTAINS(str, "fullscreen: 2"); + } + + OK(getFromSocket("/dispatch togglespecialworkspace test_fs_special")); + OK(getFromSocket("/dispatch focuswindow class:kitty_special")); + + { + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitty_special"); + } + + NLog::log("{}Test 3: Toggle special workspace hides it", Colors::YELLOW); + + OK(getFromSocket("/dispatch togglespecialworkspace test_fs_special")); + OK(getFromSocket("/dispatch focuswindow class:kitty_regular")); + + { + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitty_regular"); + EXPECT_CONTAINS(str, "fullscreen: 2"); + } + + { + auto str = getFromSocket("/monitors"); + EXPECT_CONTAINS(str, "special workspace: 0 ()"); + } + + return true; +} + static bool testAsymmetricGaps() { NLog::log("{}Testing asymmetric gap splits", Colors::YELLOW); { @@ -449,6 +534,8 @@ static bool test() { NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); + testSpecialWorkspaceFullscreen(); + testAsymmetricGaps(); NLog::log("{}Expecting 0 windows", Colors::YELLOW); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 5069f5d8..397df6ee 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1026,8 +1026,8 @@ bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() { static auto PMINRR = CConfigValue("cursor:min_refresh_rate"); // skip scheduling extra frames for fullsreen apps with vrr - const bool shouldSkip = inFullscreenMode() && (*PNOBREAK == 1 || (*PNOBREAK == 2 && m_activeWorkspace->getFullscreenWindow()->getContentType() == CONTENT_TYPE_GAME)) && - m_output->state->state().adaptiveSync; + const auto FS_WINDOW = getFullscreenWindow(); + const bool shouldSkip = FS_WINDOW && (*PNOBREAK == 1 || (*PNOBREAK == 2 && FS_WINDOW->getContentType() == CONTENT_TYPE_GAME)) && m_output->state->state().adaptiveSync; // keep requested minimum refresh rate if (shouldSkip && *PMINRR && m_lastPresentationTimer.getMillis() > 1000.0f / *PMINRR) { @@ -1357,6 +1357,12 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) { g_pDesktopAnimationManager->startAnimation(m_activeSpecialWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_OUT, false); g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + m_name}); g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + m_name}); + + // Reset layer surface state when closing special workspace + for (auto const& ls : g_pCompositor->m_layers) { + if (ls->m_monitor == m_self) + ls->m_aboveFullscreen = false; + } } m_activeSpecialWorkspace.reset(); @@ -1396,6 +1402,12 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) { g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMWSOWNER->m_name}); g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + PMWSOWNER->m_name}); + // Reset layer surfaces on the old monitor when special workspace is stolen + for (auto const& ls : g_pCompositor->m_layers) { + if (ls->m_monitor == PMWSOWNER) + ls->m_aboveFullscreen = false; + } + const auto PACTIVEWORKSPACE = PMWSOWNER->m_activeWorkspace; g_pDesktopAnimationManager->setFullscreenFadeAnimation(PACTIVEWORKSPACE, PACTIVEWORKSPACE && PACTIVEWORKSPACE->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : @@ -1409,6 +1421,12 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) { m_activeSpecialWorkspace = pWorkspace; m_activeSpecialWorkspace->m_visible = true; + // Reset layer surface state when opening special workspace + for (auto const& ls : g_pCompositor->m_layers) { + if (ls->m_monitor == m_self) + ls->m_aboveFullscreen = false; + } + if (POLDSPECIAL) POLDSPECIAL->m_events.activeChanged.emit(); @@ -2017,16 +2035,28 @@ bool CMonitor::inHDR() { } bool CMonitor::inFullscreenMode() { + // Check special workspace first since it renders on top of regular workspaces + if (m_activeSpecialWorkspace && m_activeSpecialWorkspace->m_hasFullscreenWindow && m_activeSpecialWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) + return true; return m_activeWorkspace && m_activeWorkspace->m_hasFullscreenWindow && m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN; } +PHLWINDOW CMonitor::getFullscreenWindow() { + // Check special workspace first since it renders on top of regular workspaces + if (m_activeSpecialWorkspace && m_activeSpecialWorkspace->m_hasFullscreenWindow && m_activeSpecialWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) + return m_activeSpecialWorkspace->getFullscreenWindow(); + if (m_activeWorkspace && m_activeWorkspace->m_hasFullscreenWindow && m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) + return m_activeWorkspace->getFullscreenWindow(); + return nullptr; +} + std::optional CMonitor::getFSImageDescription() { if (!inFullscreenMode()) return {}; - const auto FS_WINDOW = m_activeWorkspace->getFullscreenWindow(); + const auto FS_WINDOW = getFullscreenWindow(); if (!FS_WINDOW) - return {}; // should be unreachable + return {}; const auto ROOT_SURF = FS_WINDOW->wlSurface()->resource(); const auto SURF = ROOT_SURF->findWithCM(); diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 98d672e6..339497a5 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -327,8 +327,10 @@ class CMonitor { bool inHDR(); - /// Has an active workspace with a real fullscreen window - bool inFullscreenMode(); + /// Has an active workspace with a real fullscreen window (includes special workspace) + bool inFullscreenMode(); + /// Get fullscreen window from active or special workspace + PHLWINDOW getFullscreenWindow(); std::optional getFSImageDescription(); bool needsCM(); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 1aa85f15..6a24c0d3 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1567,7 +1567,7 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { const bool configuredHDR = (pMonitor->m_cmType == NCMType::CM_HDR_EDID || pMonitor->m_cmType == NCMType::CM_HDR); bool wantHDR = configuredHDR; - const auto FS_WINDOW = pMonitor->inFullscreenMode() ? pMonitor->m_activeWorkspace->getFullscreenWindow() : nullptr; + const auto FS_WINDOW = pMonitor->getFullscreenWindow(); if (pMonitor->supportsHDR()) { // HDR metadata determined by