diff --git a/src/Compositor.cpp b/src/Compositor.cpp index f1ac7b2b..681cbf86 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -463,6 +463,25 @@ void CCompositor::removeLockFile() { std::filesystem::remove(PATH); } +void CCompositor::prepareFallbackOutput() { + // create a backup monitor + wlr_backend* headless = nullptr; + wlr_multi_for_each_backend( + m_sWLRBackend, + [](wlr_backend* b, void* data) { + if (wlr_backend_is_headless(b)) + *((wlr_backend**)data) = b; + }, + &headless); + + if (!headless) { + Debug::log(WARN, "Unsafe state will be ineffective, no fallback output"); + return; + } + + wlr_headless_add_output(headless, 1920, 1080); +} + void CCompositor::startCompositor() { initAllSignals(); @@ -514,6 +533,8 @@ void CCompositor::startCompositor() { throwError("The backend could not start!"); } + prepareFallbackOutput(); + g_pHyprRenderer->setCursorFromName("left_ptr"); #ifdef USES_SYSTEMD @@ -2657,24 +2678,10 @@ void CCompositor::enterUnsafeState() { Debug::log(LOG, "Entering unsafe state"); + if (!m_pUnsafeOutput->m_bEnabled) + m_pUnsafeOutput->onConnect(false); + m_bUnsafeState = true; - - // create a backup monitor - wlr_backend* headless = nullptr; - wlr_multi_for_each_backend( - m_sWLRBackend, - [](wlr_backend* b, void* data) { - if (wlr_backend_is_headless(b)) - *((wlr_backend**)data) = b; - }, - &headless); - - if (!headless) { - Debug::log(WARN, "Entering an unsafe state without a headless backend"); - return; - } - - m_pUnsafeOutput = wlr_headless_add_output(headless, 1920, 1080); } void CCompositor::leaveUnsafeState() { @@ -2685,10 +2692,22 @@ void CCompositor::leaveUnsafeState() { m_bUnsafeState = false; - if (m_pUnsafeOutput) - wlr_output_destroy(m_pUnsafeOutput); + CMonitor* pNewMonitor = nullptr; + for (auto& pMonitor : m_vMonitors) { + if (pMonitor->output != m_pUnsafeOutput->output) { + pNewMonitor = pMonitor.get(); + break; + } + } - m_pUnsafeOutput = nullptr; + RASSERT(pNewMonitor, "Tried to leave unsafe without a monitor"); + + if (m_pUnsafeOutput->m_bEnabled) + m_pUnsafeOutput->onDisconnect(); + + for (auto& m : m_vMonitors) { + scheduleFrameForMonitor(m.get()); + } } void CCompositor::setPreferredScaleForSurface(wlr_surface* pSurface, double scale) { diff --git a/src/Compositor.hpp b/src/Compositor.hpp index ae536c91..59fc9136 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -121,7 +121,8 @@ class CCompositor { bool m_bSessionActive = true; bool m_bDPMSStateON = true; bool m_bUnsafeState = false; // unsafe state is when there is no monitors. - wlr_output* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state + bool m_bNextIsUnsafe = false; // because wlroots + CMonitor* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state bool m_bIsShuttingDown = false; // ------------------------------------------------- // @@ -215,6 +216,7 @@ class CCompositor { void initAllSignals(); void setRandomSplash(); void initManagers(eManagersInitStage stage); + void prepareFallbackOutput(); uint64_t m_iHyprlandPID = 0; }; diff --git a/src/Window.cpp b/src/Window.cpp index 77960dc6..2f0809bf 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -223,6 +223,9 @@ pid_t CWindow::getPID() { wl_client_get_credentials(wl_resource_get_client(m_uSurface.xdg->resource), &PID, nullptr, nullptr); } else { + if (!m_bIsMapped || !m_bMappedX11) + return -1; + PID = m_uSurface.xwayland->pid; } diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 5d116128..e016ba75 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -2066,7 +2066,7 @@ void CConfigManager::performMonitorReload() { bool overAgain = false; for (auto& m : g_pCompositor->m_vRealMonitors) { - if (!m->output) + if (!m->output || m->isUnsafeFallback) continue; auto rule = getMonitorRuleFor(m->szName, m->output->description ? m->output->description : ""); @@ -2147,15 +2147,13 @@ bool CConfigManager::shouldBlurLS(const std::string& ns) { void CConfigManager::ensureMonitorStatus() { for (auto& rm : g_pCompositor->m_vRealMonitors) { - if (!rm->output) + if (!rm->output || rm->isUnsafeFallback) continue; auto rule = getMonitorRuleFor(rm->szName, rm->output->description ? rm->output->description : ""); - if (rule.disabled == rm->m_bEnabled) { - rm->m_pThisWrap = &rm; + if (rule.disabled == rm->m_bEnabled) g_pHyprRenderer->applyMonitorRule(rm.get(), &rule); - } } } diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index e7191ae1..b274b244 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -231,7 +231,7 @@ static std::string getWorkspaceData(CWorkspace* w, HyprCtl::eHyprCtlOutputFormat "lastwindowtitle": "{}" }})#", w->m_iID, escapeJSONStrings(w->m_szName), escapeJSONStrings(PMONITOR ? PMONITOR->szName : "?"), - escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->ID) : "undefined"), g_pCompositor->getWindowsOnWorkspace(w->m_iID), + escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->ID) : "null"), g_pCompositor->getWindowsOnWorkspace(w->m_iID), ((int)w->m_bHasFullscreenWindow == 1 ? "true" : "false"), (uintptr_t)PLASTW, PLASTW ? escapeJSONStrings(PLASTW->m_szTitle) : ""); } else { return std::format("workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\n", w->m_iID, diff --git a/src/events/Monitors.cpp b/src/events/Monitors.cpp index fa36c53c..f6375edd 100644 --- a/src/events/Monitors.cpp +++ b/src/events/Monitors.cpp @@ -67,52 +67,31 @@ void Events::listener_newOutput(wl_listener* listener, void* data) { return; } - if (g_pCompositor->m_bUnsafeState) - Debug::log(WARN, "Recovering from an unsafe state. May you be lucky."); - // add it to real std::shared_ptr* PNEWMONITORWRAP = nullptr; PNEWMONITORWRAP = &g_pCompositor->m_vRealMonitors.emplace_back(std::make_shared()); + if (std::string("HEADLESS-1") == OUTPUT->name) + g_pCompositor->m_pUnsafeOutput = PNEWMONITORWRAP->get(); - (*PNEWMONITORWRAP)->ID = g_pCompositor->getNextAvailableMonitorID(OUTPUT->name); + (*PNEWMONITORWRAP)->output = OUTPUT; + const bool FALLBACK = g_pCompositor->m_pUnsafeOutput ? OUTPUT == g_pCompositor->m_pUnsafeOutput->output : false; + (*PNEWMONITORWRAP)->ID = FALLBACK ? -1 : g_pCompositor->getNextAvailableMonitorID(OUTPUT->name); + const auto PNEWMONITOR = PNEWMONITORWRAP->get(); + PNEWMONITOR->isUnsafeFallback = FALLBACK; - const auto PNEWMONITOR = PNEWMONITORWRAP->get(); + if (!FALLBACK) + PNEWMONITOR->onConnect(false); - PNEWMONITOR->output = OUTPUT; - PNEWMONITOR->m_pThisWrap = PNEWMONITORWRAP; + if (!PNEWMONITOR->m_bEnabled || FALLBACK) + return; - PNEWMONITOR->onConnect(false); + // ready to process if we have a real monitor if ((!g_pHyprRenderer->m_pMostHzMonitor || PNEWMONITOR->refreshRate > g_pHyprRenderer->m_pMostHzMonitor->refreshRate) && PNEWMONITOR->m_bEnabled) g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR; - // wlroots will instantly call this handler before we get a return to the wlr_output* in CCompositor::enterUnsafeState - const bool PROBABLYFALLBACK = (g_pCompositor->m_bUnsafeState && !g_pCompositor->m_pUnsafeOutput) || OUTPUT == g_pCompositor->m_pUnsafeOutput; - - // ready to process if we have a real monitor - if (PNEWMONITOR->m_bEnabled && !PROBABLYFALLBACK) { - // leave unsafe state - if (g_pCompositor->m_bUnsafeState) { - // recover workspaces - std::vector wsp; - for (auto& ws : g_pCompositor->m_vWorkspaces) { - wsp.push_back(ws.get()); - } - for (auto& ws : wsp) { - // because this can realloc the vec - g_pCompositor->moveWorkspaceToMonitor(ws, PNEWMONITOR); - } - - g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR; - - const auto POS = PNEWMONITOR->middle(); - if (g_pCompositor->m_sSeat.mouse) - wlr_cursor_warp(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sSeat.mouse->mouse, POS.x, POS.y); - } - - g_pCompositor->m_bReadyToProcess = true; - } + g_pCompositor->m_bReadyToProcess = true; g_pConfigManager->m_bWantsMonitorReload = true; g_pCompositor->scheduleFrameForMonitor(PNEWMONITOR); @@ -138,7 +117,9 @@ void Events::listener_monitorFrame(void* owner, void* data) { if ((g_pCompositor->m_sWLRSession && !g_pCompositor->m_sWLRSession->active) || !g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) { Debug::log(WARN, "Attempted to render frame on inactive session!"); - if (g_pCompositor->m_bUnsafeState && PMONITOR->output != g_pCompositor->m_pUnsafeOutput) { + if (g_pCompositor->m_bUnsafeState && std::ranges::any_of(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& m) { + return m->output != g_pCompositor->m_pUnsafeOutput->output; + })) { // restore from unsafe state g_pCompositor->leaveUnsafeState(); } @@ -218,9 +199,6 @@ void Events::listener_monitorDestroy(void* owner, void* data) { pMonitor->output = nullptr; pMonitor->m_bRenderingInitPassed = false; - if (g_pCompositor->m_pUnsafeOutput == OUTPUT) - g_pCompositor->m_pUnsafeOutput = nullptr; - Debug::log(LOG, "Removing monitor {} from realMonitors", pMonitor->szName); std::erase_if(g_pCompositor->m_vRealMonitors, [&](std::shared_ptr& el) { return el.get() == pMonitor; }); diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index c0910abe..25b54c6c 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -851,8 +851,8 @@ void Events::listener_destroyWindow(void* owner, void* data) { PWINDOW->m_bReadyToDelete = true; if (!PWINDOW->m_bFadingOut) { - g_pCompositor->removeWindowFromVectorSafe(PWINDOW); // most likely X11 unmanaged or sumn Debug::log(LOG, "Unmapped {} removed instantly", PWINDOW); + g_pCompositor->removeWindowFromVectorSafe(PWINDOW); // most likely X11 unmanaged or sumn } } diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index e96da35c..6d6b40e0 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -109,20 +109,20 @@ void CMonitor::onConnect(bool noRule) { m_bRenderingInitPassed = true; } - if (!m_pThisWrap) { + std::shared_ptr* thisWrapper = nullptr; - // find the wrap - for (auto& m : g_pCompositor->m_vRealMonitors) { - if (m->ID == ID) { - m_pThisWrap = &m; - break; - } + // find the wrap + for (auto& m : g_pCompositor->m_vRealMonitors) { + if (m->ID == ID) { + thisWrapper = &m; + break; } } - if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) == g_pCompositor->m_vMonitors.end()) { - g_pCompositor->m_vMonitors.push_back(*m_pThisWrap); - } + RASSERT(thisWrapper->get(), "CMonitor::onConnect: Had no wrapper???"); + + if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) == g_pCompositor->m_vMonitors.end()) + g_pCompositor->m_vMonitors.push_back(*thisWrapper); m_bEnabled = true; @@ -132,6 +132,8 @@ void CMonitor::onConnect(bool noRule) { if (!noRule) g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true); + wlr_output_commit(output); + wlr_damage_ring_set_bounds(&damage, vecTransformedSize.x, vecTransformedSize.y); wlr_xcursor_manager_load(g_pCompositor->m_sWLRXCursorMgr, scale); @@ -152,8 +154,6 @@ void CMonitor::onConnect(bool noRule) { if (scale < 0.1) scale = getDefaultScale(); - m_pThisWrap = nullptr; - forceFullFrames = 3; // force 3 full frames to make sure there is no blinking due to double-buffering. // @@ -184,6 +184,8 @@ void CMonitor::onConnect(bool noRule) { g_pCompositor->setActiveMonitor(this); renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this); + + g_pCompositor->scheduleFrameForMonitor(this); } void CMonitor::onDisconnect() { @@ -221,9 +223,6 @@ void CMonitor::onDisconnect() { g_pConfigManager->m_bWantsMonitorReload = true; } - m_bEnabled = false; - m_bRenderingInitPassed = false; - hyprListener_monitorFrame.removeCallback(); hyprListener_monitorDamage.removeCallback(); hyprListener_monitorNeedsFrame.removeCallback(); @@ -248,6 +247,9 @@ void CMonitor::onDisconnect() { g_pCompositor->enterUnsafeState(); } + m_bEnabled = false; + m_bRenderingInitPassed = false; + if (BACKUPMON) { // snap cursor wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, BACKUPMON->vecPosition.x + BACKUPMON->vecTransformedSize.x / 2.f, @@ -256,7 +258,7 @@ void CMonitor::onDisconnect() { // move workspaces std::deque wspToMove; for (auto& w : g_pCompositor->m_vWorkspaces) { - if (w->m_iMonitorID == ID) { + if (w->m_iMonitorID == ID || !g_pCompositor->getMonitorFromID(w->m_iMonitorID)) { wspToMove.push_back(w.get()); } } @@ -414,19 +416,22 @@ void CMonitor::setMirror(const std::string& mirrorOf) { vecPosition = RULE.offset; // push to mvmonitors - if (!m_pThisWrap) { - // find the wrap - for (auto& m : g_pCompositor->m_vRealMonitors) { - if (m->ID == ID) { - m_pThisWrap = &m; - break; - } + + std::shared_ptr* thisWrapper = nullptr; + + // find the wrap + for (auto& m : g_pCompositor->m_vRealMonitors) { + if (m->ID == ID) { + thisWrapper = &m; + break; } } + RASSERT(thisWrapper->get(), "CMonitor::setMirror: Had no wrapper???"); + if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) == g_pCompositor->m_vMonitors.end()) { - g_pCompositor->m_vMonitors.push_back(*m_pThisWrap); + g_pCompositor->m_vMonitors.push_back(*thisWrapper); } setupDefaultWS(RULE); diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 5e0df858..5e861b50 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -61,11 +61,12 @@ class CMonitor { bool gammaChanged = false; float xwaylandScale = 1.f; - bool dpmsStatus = true; - bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it. - bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed. - bool createdByUser = false; - uint32_t drmFormat = DRM_FORMAT_INVALID; + bool dpmsStatus = true; + bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it. + bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed. + bool createdByUser = false; + uint32_t drmFormat = DRM_FORMAT_INVALID; + bool isUnsafeFallback = false; bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after bool renderingActive = false; @@ -108,24 +109,23 @@ class CMonitor { DYNLISTENER(monitorBind); // methods - void onConnect(bool noRule); - void onDisconnect(); - void addDamage(const pixman_region32_t* rg); - void addDamage(const CRegion* rg); - void addDamage(const wlr_box* box); - void setMirror(const std::string&); - bool isMirror(); - float getDefaultScale(); - void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false); - void changeWorkspace(const int& id, bool internal = false); - void setSpecialWorkspace(CWorkspace* const pWorkspace); - void setSpecialWorkspace(const int& id); - void moveTo(const Vector2D& pos); - Vector2D middle(); + void onConnect(bool noRule); + void onDisconnect(); + void addDamage(const pixman_region32_t* rg); + void addDamage(const CRegion* rg); + void addDamage(const wlr_box* box); + void setMirror(const std::string&); + bool isMirror(); + float getDefaultScale(); + void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false); + void changeWorkspace(const int& id, bool internal = false); + void setSpecialWorkspace(CWorkspace* const pWorkspace); + void setSpecialWorkspace(const int& id); + void moveTo(const Vector2D& pos); + Vector2D middle(); - std::shared_ptr* m_pThisWrap = nullptr; - bool m_bEnabled = false; - bool m_bRenderingInitPassed = false; + bool m_bEnabled = false; + bool m_bRenderingInitPassed = false; // For the list lookup diff --git a/src/protocols/XDGOutput.cpp b/src/protocols/XDGOutput.cpp index 2ef0c3a6..49845b94 100644 --- a/src/protocols/XDGOutput.cpp +++ b/src/protocols/XDGOutput.cpp @@ -72,15 +72,9 @@ CXDGOutputProtocol::CXDGOutputProtocol(const wl_interface* iface, const int& ver } void CXDGOutputProtocol::onManagerGetXDGOutput(wl_client* client, wl_resource* resource, uint32_t id, wl_resource* outputResource) { - const auto OUTPUT = wlr_output_from_resource(outputResource); + const auto OUTPUT = wlr_output_from_resource(outputResource); - if (!OUTPUT) - return; - - const auto PMONITOR = g_pCompositor->getMonitorFromOutput(OUTPUT); - - if (!PMONITOR) - return; + const auto PMONITOR = g_pCompositor->getMonitorFromOutput(OUTPUT); SXDGOutput* pXDGOutput = m_vXDGOutputs.emplace_back(std::make_unique(PMONITOR)).get(); #ifndef NO_XWAYLAND @@ -99,6 +93,10 @@ void CXDGOutputProtocol::onManagerGetXDGOutput(wl_client* client, wl_resource* r pXDGOutput->resource->setImplementation(&OUTPUT_IMPL, nullptr); pXDGOutput->resource->setData(this); + + if (!PMONITOR) + return; + const auto XDGVER = pXDGOutput->resource->version(); if (XDGVER >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) @@ -116,7 +114,7 @@ void CXDGOutputProtocol::onManagerGetXDGOutput(wl_client* client, wl_resource* r void CXDGOutputProtocol::updateOutputDetails(SXDGOutput* pOutput) { static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue; - if (!pOutput->resource->good()) + if (!pOutput->resource->good() || !pOutput->monitor) return; const auto POS = pOutput->isXWayland ? pOutput->monitor->vecXWaylandPosition : pOutput->monitor->vecPosition; @@ -133,6 +131,10 @@ void CXDGOutputProtocol::updateOutputDetails(SXDGOutput* pOutput) { void CXDGOutputProtocol::updateAllOutputs() { for (auto& o : m_vXDGOutputs) { + + if (!o->monitor) + continue; + updateOutputDetails(o.get()); wlr_output_schedule_done(o->monitor->output);