From 44f5b6a9466c6ab9b28e3b5c27454c19ad8d8a2d Mon Sep 17 00:00:00 2001 From: Chow Loong Jin Date: Wed, 22 Oct 2025 10:49:08 +0800 Subject: [PATCH] Backport patch to fix monitors getting stuck off --- .../patches/008-fix-dpms-invalid-state.patch | 80 +++++++++++++++++++ debian/patches/series | 1 + 2 files changed, 81 insertions(+) create mode 100644 debian/patches/008-fix-dpms-invalid-state.patch diff --git a/debian/patches/008-fix-dpms-invalid-state.patch b/debian/patches/008-fix-dpms-invalid-state.patch new file mode 100644 index 0000000..16ae2ec --- /dev/null +++ b/debian/patches/008-fix-dpms-invalid-state.patch @@ -0,0 +1,80 @@ +Origin: https://github.com/hyprwm/hyprland/commit/560c53d87dedf7df8185eb370cfbf3575826e85c +Author: Vaxry +Bug: https://github.com/hyprwm/Hyprland/discussions/12045 +Applied-Upstream: yes +Last-Update: 2025-11-05 +Subject: monitor/dpms: fix possible invalid state + +If dpms gets immediately re-enabled, a commit could fail, not schedule any frames anymore, and the monitor would be stuck off. Fix this by adding a timer to retry if commit fails. +Index: hyprland/src/helpers/Monitor.cpp +=================================================================== +--- hyprland.orig/src/helpers/Monitor.cpp 2025-11-05 14:47:00.871682876 +0800 ++++ hyprland/src/helpers/Monitor.cpp 2025-11-05 14:47:00.863682970 +0800 +@@ -1865,13 +1865,14 @@ + + if (on) { + // enable the monitor. Wait for the frame to be presented, then begin animation +- m_dpmsBlackOpacity->setValueAndWarp(1.F); + m_dpmsBlackOpacity->setCallbackOnEnd(nullptr); ++ m_dpmsBlackOpacity->setValueAndWarp(1.F); + m_pendingDpmsAnimation = true; + m_pendingDpmsAnimationCounter = 0; + commitDPMSState(true); + } else { + // disable the monitor. Begin the animation, then do dpms on its end. ++ m_dpmsBlackOpacity->setCallbackOnEnd(nullptr); + m_dpmsBlackOpacity->setValueAndWarp(0.F); + *m_dpmsBlackOpacity = 1.F; + m_dpmsBlackOpacity->setCallbackOnEnd( +@@ -1891,7 +1892,29 @@ + m_output->state->setEnabled(state); + + if (!m_state.commit()) { +- Debug::log(ERR, "Couldn't commit output {} for DPMS = {}", m_name, state); ++ Debug::log(ERR, "Couldn't commit output {} for DPMS = {}, will retry.", m_name, state); ++ ++ // retry in 2 frames. This could happen when the DRM backend rejects our commit ++ // because disable + enable were sent almost instantly ++ ++ m_dpmsRetryTimer = makeShared( ++ std::chrono::milliseconds(2000 / sc(m_refreshRate)), ++ [this, self = m_self](SP s, void* d) { ++ if (!self) ++ return; ++ ++ m_output->state->resetExplicitFences(); ++ m_output->state->setEnabled(m_dpmsStatus); ++ if (!m_state.commit()) { ++ Debug::log(ERR, "Couldn't retry committing output {} for DPMS = {}", m_name, m_dpmsStatus); ++ return; ++ } ++ ++ m_dpmsRetryTimer.reset(); ++ }, ++ nullptr); ++ g_pEventLoopManager->addTimer(m_dpmsRetryTimer); ++ + return; + } + +Index: hyprland/src/helpers/Monitor.hpp +=================================================================== +--- hyprland.orig/src/helpers/Monitor.hpp 2025-11-05 14:47:00.871682876 +0800 ++++ hyprland/src/helpers/Monitor.hpp 2025-11-05 14:47:00.864416147 +0800 +@@ -76,6 +76,7 @@ + class CMonitor; + class CSyncTimeline; + class CEGLSync; ++class CEventLoopTimer; + + class CMonitorState { + public: +@@ -146,6 +147,8 @@ + bool m_createdByUser = false; + bool m_isUnsafeFallback = false; + ++ SP m_dpmsRetryTimer; ++ + bool m_pendingFrame = false; // if we schedule a frame during rendering, reschedule it after + bool m_renderingActive = false; + diff --git a/debian/patches/series b/debian/patches/series index 885f080..2f44b3f 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -5,3 +5,4 @@ 005-libcpp-port.patch 006-hyprpm-global-headers.patch 007-backport-cm-chrome-crash-fix.patch +008-fix-dpms-invalid-state.patch