1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
Origin: https://github.com/hyprwm/hyprland/commit/560c53d87dedf7df8185eb370cfbf3575826e85c
Author: Vaxry <vaxry@vaxry.net>
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<CEventLoopTimer>(
+ std::chrono::milliseconds(2000 / sc<int>(m_refreshRate)),
+ [this, self = m_self](SP<CEventLoopTimer> 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<CEventLoopTimer> m_dpmsRetryTimer;
+
bool m_pendingFrame = false; // if we schedule a frame during rendering, reschedule it after
bool m_renderingActive = false;
|