diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 2068798d..918d50f4 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -3067,6 +3067,27 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vectorm_isSpecialWorkspace) + continue; + + const auto RULE = g_pConfigManager->getWorkspaceRuleFor(ws); + if (RULE.monitor.empty()) + continue; + + const auto PMONITOR = getMonitorFromString(RULE.monitor); + if (!PMONITOR) + continue; + + if (ws->m_monitor == PMONITOR) + continue; + + Log::logger->log(Log::DEBUG, "ensureWorkspacesOnAssignedMonitors: moving workspace {} to {}", ws->m_name, PMONITOR->m_name); + moveWorkspaceToMonitor(ws, PMONITOR, true); + } +} + std::optional CCompositor::getVTNr() { if (!m_aqBackend->hasSession()) return std::nullopt; diff --git a/src/Compositor.hpp b/src/Compositor.hpp index d5317e9b..11aec350 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -161,6 +161,7 @@ class CCompositor { void updateSuspendedStates(); void onNewMonitor(SP output); void ensurePersistentWorkspacesPresent(const std::vector& rules, PHLWORKSPACE pWorkspace = nullptr); + void ensureWorkspacesOnAssignedMonitors(); std::optional getVTNr(); bool isVRRActiveOnAnyMonitor() const; diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index f287bff3..d77a7e76 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -82,7 +82,10 @@ void CMonitor::onConnect(bool noRule) { m_zoomAnimProgress->setValueAndWarp(0.F); m_zoomAnimFrameCounter = 0; - g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); }); + g_pEventLoopManager->doLater([] { + g_pConfigManager->ensurePersistentWorkspacesPresent(); + g_pCompositor->ensureWorkspacesOnAssignedMonitors(); + }); m_listeners.frame = m_output->events.frame.listen([this] { if (m_frameScheduler) @@ -290,10 +293,16 @@ void CMonitor::onConnect(bool noRule) { if (!valid(ws)) continue; - if (ws->m_lastMonitor == m_name || g_pCompositor->m_monitors.size() == 1 /* avoid lost workspaces on recover */) { + const auto CURRENTMON = ws->m_monitor.lock(); + const bool ORPHANED = !CURRENTMON || std::ranges::none_of(g_pCompositor->m_monitors, [&](const auto& mon) { return mon == CURRENTMON; }); + const bool RETURNING = ws->m_lastMonitor == m_name; + const bool RECOVERY = g_pCompositor->m_monitors.size() == 1 && ORPHANED; // temporarily recover orphaned workspaces + + if (RETURNING || RECOVERY) { g_pCompositor->moveWorkspaceToMonitor(ws, m_self.lock()); g_pDesktopAnimationManager->startAnimation(ws, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true); - ws->m_lastMonitor = ""; + if (RETURNING) + ws->m_lastMonitor = ""; } } @@ -429,19 +438,24 @@ void CMonitor::onDisconnect(bool destroy) { m_enabled = false; m_renderingInitPassed = false; + std::vector wspToMove; + for (auto const& w : g_pCompositor->getWorkspaces()) { + if (w->m_monitor == m_self || !w->m_monitor) + wspToMove.emplace_back(w.lock()); + } + + // Preserve ownership across cascaded monitor disconnects. + // The first disconnected monitor "owns" where a workspace should return. + for (auto const& w : wspToMove) { + if (w && w->m_lastMonitor.empty()) + w->m_lastMonitor = m_name; + } + if (BACKUPMON) { // snap cursor g_pCompositor->warpCursorTo(BACKUPMON->m_position + BACKUPMON->m_transformedSize / 2.F, true); - // move workspaces - std::vector wspToMove; - for (auto const& w : g_pCompositor->getWorkspaces()) { - if (w->m_monitor == m_self || !w->m_monitor) - wspToMove.emplace_back(w.lock()); - } - for (auto const& w : wspToMove) { - w->m_lastMonitor = m_name; g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON); g_pDesktopAnimationManager->startAnimation(w, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true); }