mirror of
https://github.com/hyprwm/Hyprland
synced 2024-12-22 19:29:49 +01:00
internal: Further unsafe state improvements (#3404)
Instead of allowing Hyprland to sit in a state where there are no monitors, which various parts of the code don't like, we create a fake headless output on all monitor disconnect, and then remove it when a monitor appears
This commit is contained in:
parent
352ceb1117
commit
46d66f4bcc
5 changed files with 63 additions and 11 deletions
|
@ -2604,3 +2604,43 @@ void CCompositor::arrangeMonitors() {
|
|||
m->xwaylandScale = 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
void CCompositor::enterUnsafeState() {
|
||||
if (m_bUnsafeState)
|
||||
return;
|
||||
|
||||
Debug::log(LOG, "Entering unsafe state");
|
||||
|
||||
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() {
|
||||
if (!m_bUnsafeState)
|
||||
return;
|
||||
|
||||
Debug::log(LOG, "Leaving unsafe state");
|
||||
|
||||
m_bUnsafeState = false;
|
||||
|
||||
if (m_pUnsafeOutput)
|
||||
wlr_output_destroy(m_pUnsafeOutput);
|
||||
|
||||
m_pUnsafeOutput = nullptr;
|
||||
}
|
||||
|
|
|
@ -119,7 +119,8 @@ class CCompositor {
|
|||
bool m_bReadyToProcess = false;
|
||||
bool m_bSessionActive = 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_bIsShuttingDown = false;
|
||||
|
||||
// ------------------------------------------------- //
|
||||
|
@ -202,6 +203,8 @@ class CCompositor {
|
|||
void notifyIdleActivity();
|
||||
void setIdleActivityInhibit(bool inhibit);
|
||||
void arrangeMonitors();
|
||||
void enterUnsafeState();
|
||||
void leaveUnsafeState();
|
||||
|
||||
std::string explicitConfigPath;
|
||||
|
||||
|
|
|
@ -2020,9 +2020,6 @@ void CConfigManager::performMonitorReload() {
|
|||
if (overAgain)
|
||||
performMonitorReload();
|
||||
|
||||
if (!g_pCompositor->m_vMonitors.empty()) // reset unsafe state if we have monitors
|
||||
g_pCompositor->m_bUnsafeState = false;
|
||||
|
||||
m_bWantsMonitorReload = false;
|
||||
|
||||
EMIT_HOOK_EVENT("monitorLayoutChanged", nullptr);
|
||||
|
|
|
@ -87,13 +87,21 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
|
|||
if ((!g_pHyprRenderer->m_pMostHzMonitor || PNEWMONITOR->refreshRate > g_pHyprRenderer->m_pMostHzMonitor->refreshRate) && PNEWMONITOR->m_bEnabled)
|
||||
g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR;
|
||||
|
||||
// ready to process cuz we have a monitor
|
||||
if (PNEWMONITOR->m_bEnabled) {
|
||||
// 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) {
|
||||
g_pCompositor->moveWorkspaceToMonitor(ws.get(), PNEWMONITOR);
|
||||
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;
|
||||
|
@ -104,7 +112,6 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
|
|||
}
|
||||
|
||||
g_pCompositor->m_bReadyToProcess = true;
|
||||
g_pCompositor->m_bUnsafeState = false;
|
||||
}
|
||||
|
||||
g_pConfigManager->m_bWantsMonitorReload = true;
|
||||
|
@ -131,8 +138,10 @@ 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)
|
||||
g_pConfigManager->performMonitorReload();
|
||||
if (g_pCompositor->m_bUnsafeState && PMONITOR->output != g_pCompositor->m_pUnsafeOutput) {
|
||||
// restore from unsafe state
|
||||
g_pCompositor->leaveUnsafeState();
|
||||
}
|
||||
|
||||
return; // cannot draw on session inactive (different tty)
|
||||
}
|
||||
|
@ -196,6 +205,9 @@ 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<CMonitor>& el) { return el.get() == pMonitor; });
|
||||
|
|
|
@ -243,7 +243,7 @@ void CMonitor::onDisconnect() {
|
|||
|
||||
if (!BACKUPMON) {
|
||||
Debug::log(WARN, "Unplugged last monitor, entering an unsafe state. Good luck my friend.");
|
||||
g_pCompositor->m_bUnsafeState = true;
|
||||
g_pCompositor->enterUnsafeState();
|
||||
}
|
||||
|
||||
if (BACKUPMON) {
|
||||
|
|
Loading…
Reference in a new issue