Core: various unsafe state improvements (#3713)

Fixes #3637
This commit is contained in:
Vaxry 2023-11-01 18:53:36 +00:00 committed by GitHub
parent 7b32b4214d
commit 21e9313c10
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 128 additions and 121 deletions

View file

@ -463,6 +463,25 @@ void CCompositor::removeLockFile() {
std::filesystem::remove(PATH); 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() { void CCompositor::startCompositor() {
initAllSignals(); initAllSignals();
@ -514,6 +533,8 @@ void CCompositor::startCompositor() {
throwError("The backend could not start!"); throwError("The backend could not start!");
} }
prepareFallbackOutput();
g_pHyprRenderer->setCursorFromName("left_ptr"); g_pHyprRenderer->setCursorFromName("left_ptr");
#ifdef USES_SYSTEMD #ifdef USES_SYSTEMD
@ -2657,24 +2678,10 @@ void CCompositor::enterUnsafeState() {
Debug::log(LOG, "Entering unsafe state"); Debug::log(LOG, "Entering unsafe state");
if (!m_pUnsafeOutput->m_bEnabled)
m_pUnsafeOutput->onConnect(false);
m_bUnsafeState = true; 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() { void CCompositor::leaveUnsafeState() {
@ -2685,10 +2692,22 @@ void CCompositor::leaveUnsafeState() {
m_bUnsafeState = false; m_bUnsafeState = false;
if (m_pUnsafeOutput) CMonitor* pNewMonitor = nullptr;
wlr_output_destroy(m_pUnsafeOutput); 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) { void CCompositor::setPreferredScaleForSurface(wlr_surface* pSurface, double scale) {

View file

@ -121,7 +121,8 @@ class CCompositor {
bool m_bSessionActive = true; bool m_bSessionActive = true;
bool m_bDPMSStateON = true; bool m_bDPMSStateON = true;
bool m_bUnsafeState = false; // unsafe state is when there is no monitors. 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; bool m_bIsShuttingDown = false;
// ------------------------------------------------- // // ------------------------------------------------- //
@ -215,6 +216,7 @@ class CCompositor {
void initAllSignals(); void initAllSignals();
void setRandomSplash(); void setRandomSplash();
void initManagers(eManagersInitStage stage); void initManagers(eManagersInitStage stage);
void prepareFallbackOutput();
uint64_t m_iHyprlandPID = 0; uint64_t m_iHyprlandPID = 0;
}; };

View file

@ -223,6 +223,9 @@ pid_t CWindow::getPID() {
wl_client_get_credentials(wl_resource_get_client(m_uSurface.xdg->resource), &PID, nullptr, nullptr); wl_client_get_credentials(wl_resource_get_client(m_uSurface.xdg->resource), &PID, nullptr, nullptr);
} else { } else {
if (!m_bIsMapped || !m_bMappedX11)
return -1;
PID = m_uSurface.xwayland->pid; PID = m_uSurface.xwayland->pid;
} }

View file

@ -2066,7 +2066,7 @@ void CConfigManager::performMonitorReload() {
bool overAgain = false; bool overAgain = false;
for (auto& m : g_pCompositor->m_vRealMonitors) { for (auto& m : g_pCompositor->m_vRealMonitors) {
if (!m->output) if (!m->output || m->isUnsafeFallback)
continue; continue;
auto rule = getMonitorRuleFor(m->szName, m->output->description ? m->output->description : ""); auto rule = getMonitorRuleFor(m->szName, m->output->description ? m->output->description : "");
@ -2147,17 +2147,15 @@ bool CConfigManager::shouldBlurLS(const std::string& ns) {
void CConfigManager::ensureMonitorStatus() { void CConfigManager::ensureMonitorStatus() {
for (auto& rm : g_pCompositor->m_vRealMonitors) { for (auto& rm : g_pCompositor->m_vRealMonitors) {
if (!rm->output) if (!rm->output || rm->isUnsafeFallback)
continue; continue;
auto rule = getMonitorRuleFor(rm->szName, rm->output->description ? rm->output->description : ""); auto rule = getMonitorRuleFor(rm->szName, rm->output->description ? rm->output->description : "");
if (rule.disabled == rm->m_bEnabled) { if (rule.disabled == rm->m_bEnabled)
rm->m_pThisWrap = &rm;
g_pHyprRenderer->applyMonitorRule(rm.get(), &rule); g_pHyprRenderer->applyMonitorRule(rm.get(), &rule);
} }
} }
}
void CConfigManager::ensureVRR(CMonitor* pMonitor) { void CConfigManager::ensureVRR(CMonitor* pMonitor) {
static auto* const PVRR = &getConfigValuePtr("misc:vrr")->intValue; static auto* const PVRR = &getConfigValuePtr("misc:vrr")->intValue;

View file

@ -231,7 +231,7 @@ static std::string getWorkspaceData(CWorkspace* w, HyprCtl::eHyprCtlOutputFormat
"lastwindowtitle": "{}" "lastwindowtitle": "{}"
}})#", }})#",
w->m_iID, escapeJSONStrings(w->m_szName), escapeJSONStrings(PMONITOR ? PMONITOR->szName : "?"), 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) : ""); ((int)w->m_bHasFullscreenWindow == 1 ? "true" : "false"), (uintptr_t)PLASTW, PLASTW ? escapeJSONStrings(PLASTW->m_szTitle) : "");
} else { } 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, return std::format("workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\n", w->m_iID,

View file

@ -67,52 +67,31 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
return; return;
} }
if (g_pCompositor->m_bUnsafeState)
Debug::log(WARN, "Recovering from an unsafe state. May you be lucky.");
// add it to real // add it to real
std::shared_ptr<CMonitor>* PNEWMONITORWRAP = nullptr; std::shared_ptr<CMonitor>* PNEWMONITORWRAP = nullptr;
PNEWMONITORWRAP = &g_pCompositor->m_vRealMonitors.emplace_back(std::make_shared<CMonitor>()); PNEWMONITORWRAP = &g_pCompositor->m_vRealMonitors.emplace_back(std::make_shared<CMonitor>());
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(); const auto PNEWMONITOR = PNEWMONITORWRAP->get();
PNEWMONITOR->isUnsafeFallback = FALLBACK;
PNEWMONITOR->output = OUTPUT; if (!FALLBACK)
PNEWMONITOR->m_pThisWrap = PNEWMONITORWRAP;
PNEWMONITOR->onConnect(false); PNEWMONITOR->onConnect(false);
if (!PNEWMONITOR->m_bEnabled || FALLBACK)
return;
// ready to process if we have a real monitor
if ((!g_pHyprRenderer->m_pMostHzMonitor || PNEWMONITOR->refreshRate > g_pHyprRenderer->m_pMostHzMonitor->refreshRate) && PNEWMONITOR->m_bEnabled) if ((!g_pHyprRenderer->m_pMostHzMonitor || PNEWMONITOR->refreshRate > g_pHyprRenderer->m_pMostHzMonitor->refreshRate) && PNEWMONITOR->m_bEnabled)
g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR; 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<CWorkspace*> 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_pConfigManager->m_bWantsMonitorReload = true;
g_pCompositor->scheduleFrameForMonitor(PNEWMONITOR); 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) { 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!"); 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 // restore from unsafe state
g_pCompositor->leaveUnsafeState(); g_pCompositor->leaveUnsafeState();
} }
@ -218,9 +199,6 @@ void Events::listener_monitorDestroy(void* owner, void* data) {
pMonitor->output = nullptr; pMonitor->output = nullptr;
pMonitor->m_bRenderingInitPassed = false; pMonitor->m_bRenderingInitPassed = false;
if (g_pCompositor->m_pUnsafeOutput == OUTPUT)
g_pCompositor->m_pUnsafeOutput = nullptr;
Debug::log(LOG, "Removing monitor {} from realMonitors", pMonitor->szName); Debug::log(LOG, "Removing monitor {} from realMonitors", pMonitor->szName);
std::erase_if(g_pCompositor->m_vRealMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == pMonitor; }); std::erase_if(g_pCompositor->m_vRealMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == pMonitor; });

View file

@ -851,8 +851,8 @@ void Events::listener_destroyWindow(void* owner, void* data) {
PWINDOW->m_bReadyToDelete = true; PWINDOW->m_bReadyToDelete = true;
if (!PWINDOW->m_bFadingOut) { if (!PWINDOW->m_bFadingOut) {
g_pCompositor->removeWindowFromVectorSafe(PWINDOW); // most likely X11 unmanaged or sumn
Debug::log(LOG, "Unmapped {} removed instantly", PWINDOW); Debug::log(LOG, "Unmapped {} removed instantly", PWINDOW);
g_pCompositor->removeWindowFromVectorSafe(PWINDOW); // most likely X11 unmanaged or sumn
} }
} }

View file

@ -109,20 +109,20 @@ void CMonitor::onConnect(bool noRule) {
m_bRenderingInitPassed = true; m_bRenderingInitPassed = true;
} }
if (!m_pThisWrap) { std::shared_ptr<CMonitor>* thisWrapper = nullptr;
// find the wrap // find the wrap
for (auto& m : g_pCompositor->m_vRealMonitors) { for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->ID == ID) { if (m->ID == ID) {
m_pThisWrap = &m; thisWrapper = &m;
break; 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()) { RASSERT(thisWrapper->get(), "CMonitor::onConnect: Had no wrapper???");
g_pCompositor->m_vMonitors.push_back(*m_pThisWrap);
} 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; m_bEnabled = true;
@ -132,6 +132,8 @@ void CMonitor::onConnect(bool noRule) {
if (!noRule) if (!noRule)
g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true); g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true);
wlr_output_commit(output);
wlr_damage_ring_set_bounds(&damage, vecTransformedSize.x, vecTransformedSize.y); wlr_damage_ring_set_bounds(&damage, vecTransformedSize.x, vecTransformedSize.y);
wlr_xcursor_manager_load(g_pCompositor->m_sWLRXCursorMgr, scale); wlr_xcursor_manager_load(g_pCompositor->m_sWLRXCursorMgr, scale);
@ -152,8 +154,6 @@ void CMonitor::onConnect(bool noRule) {
if (scale < 0.1) if (scale < 0.1)
scale = getDefaultScale(); scale = getDefaultScale();
m_pThisWrap = nullptr;
forceFullFrames = 3; // force 3 full frames to make sure there is no blinking due to double-buffering. 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); g_pCompositor->setActiveMonitor(this);
renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this); renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this);
g_pCompositor->scheduleFrameForMonitor(this);
} }
void CMonitor::onDisconnect() { void CMonitor::onDisconnect() {
@ -221,9 +223,6 @@ void CMonitor::onDisconnect() {
g_pConfigManager->m_bWantsMonitorReload = true; g_pConfigManager->m_bWantsMonitorReload = true;
} }
m_bEnabled = false;
m_bRenderingInitPassed = false;
hyprListener_monitorFrame.removeCallback(); hyprListener_monitorFrame.removeCallback();
hyprListener_monitorDamage.removeCallback(); hyprListener_monitorDamage.removeCallback();
hyprListener_monitorNeedsFrame.removeCallback(); hyprListener_monitorNeedsFrame.removeCallback();
@ -248,6 +247,9 @@ void CMonitor::onDisconnect() {
g_pCompositor->enterUnsafeState(); g_pCompositor->enterUnsafeState();
} }
m_bEnabled = false;
m_bRenderingInitPassed = false;
if (BACKUPMON) { if (BACKUPMON) {
// snap cursor // snap cursor
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, BACKUPMON->vecPosition.x + BACKUPMON->vecTransformedSize.x / 2.f, 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 // move workspaces
std::deque<CWorkspace*> wspToMove; std::deque<CWorkspace*> wspToMove;
for (auto& w : g_pCompositor->m_vWorkspaces) { 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()); wspToMove.push_back(w.get());
} }
} }
@ -414,19 +416,22 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
vecPosition = RULE.offset; vecPosition = RULE.offset;
// push to mvmonitors // push to mvmonitors
if (!m_pThisWrap) {
std::shared_ptr<CMonitor>* thisWrapper = nullptr;
// find the wrap // find the wrap
for (auto& m : g_pCompositor->m_vRealMonitors) { for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->ID == ID) { if (m->ID == ID) {
m_pThisWrap = &m; thisWrapper = &m;
break; 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; }) == 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.end()) {
g_pCompositor->m_vMonitors.push_back(*m_pThisWrap); g_pCompositor->m_vMonitors.push_back(*thisWrapper);
} }
setupDefaultWS(RULE); setupDefaultWS(RULE);

View file

@ -66,6 +66,7 @@ class CMonitor {
bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed. bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
bool createdByUser = false; bool createdByUser = false;
uint32_t drmFormat = DRM_FORMAT_INVALID; uint32_t drmFormat = DRM_FORMAT_INVALID;
bool isUnsafeFallback = false;
bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after
bool renderingActive = false; bool renderingActive = false;
@ -123,7 +124,6 @@ class CMonitor {
void moveTo(const Vector2D& pos); void moveTo(const Vector2D& pos);
Vector2D middle(); Vector2D middle();
std::shared_ptr<CMonitor>* m_pThisWrap = nullptr;
bool m_bEnabled = false; bool m_bEnabled = false;
bool m_bRenderingInitPassed = false; bool m_bRenderingInitPassed = false;

View file

@ -74,14 +74,8 @@ CXDGOutputProtocol::CXDGOutputProtocol(const wl_interface* iface, const int& ver
void CXDGOutputProtocol::onManagerGetXDGOutput(wl_client* client, wl_resource* resource, uint32_t id, wl_resource* outputResource) { 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); const auto PMONITOR = g_pCompositor->getMonitorFromOutput(OUTPUT);
if (!PMONITOR)
return;
SXDGOutput* pXDGOutput = m_vXDGOutputs.emplace_back(std::make_unique<SXDGOutput>(PMONITOR)).get(); SXDGOutput* pXDGOutput = m_vXDGOutputs.emplace_back(std::make_unique<SXDGOutput>(PMONITOR)).get();
#ifndef NO_XWAYLAND #ifndef NO_XWAYLAND
if (g_pXWaylandManager->m_sWLRXWayland && g_pXWaylandManager->m_sWLRXWayland->server && g_pXWaylandManager->m_sWLRXWayland->server->client == client) if (g_pXWaylandManager->m_sWLRXWayland && g_pXWaylandManager->m_sWLRXWayland->server && g_pXWaylandManager->m_sWLRXWayland->server->client == client)
@ -99,6 +93,10 @@ void CXDGOutputProtocol::onManagerGetXDGOutput(wl_client* client, wl_resource* r
pXDGOutput->resource->setImplementation(&OUTPUT_IMPL, nullptr); pXDGOutput->resource->setImplementation(&OUTPUT_IMPL, nullptr);
pXDGOutput->resource->setData(this); pXDGOutput->resource->setData(this);
if (!PMONITOR)
return;
const auto XDGVER = pXDGOutput->resource->version(); const auto XDGVER = pXDGOutput->resource->version();
if (XDGVER >= ZXDG_OUTPUT_V1_NAME_SINCE_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) { void CXDGOutputProtocol::updateOutputDetails(SXDGOutput* pOutput) {
static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue; static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue;
if (!pOutput->resource->good()) if (!pOutput->resource->good() || !pOutput->monitor)
return; return;
const auto POS = pOutput->isXWayland ? pOutput->monitor->vecXWaylandPosition : pOutput->monitor->vecPosition; const auto POS = pOutput->isXWayland ? pOutput->monitor->vecXWaylandPosition : pOutput->monitor->vecPosition;
@ -133,6 +131,10 @@ void CXDGOutputProtocol::updateOutputDetails(SXDGOutput* pOutput) {
void CXDGOutputProtocol::updateAllOutputs() { void CXDGOutputProtocol::updateAllOutputs() {
for (auto& o : m_vXDGOutputs) { for (auto& o : m_vXDGOutputs) {
if (!o->monitor)
continue;
updateOutputDetails(o.get()); updateOutputDetails(o.get());
wlr_output_schedule_done(o->monitor->output); wlr_output_schedule_done(o->monitor->output);