From 251288ec5942b3544ad31de1299569284d80f0d7 Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Sun, 17 Aug 2025 08:37:13 +0100 Subject: [PATCH] renderer: add dpms animations (#11452) --- src/config/ConfigManager.cpp | 1 + src/helpers/Monitor.cpp | 48 +++++++++++++++++++++++++++++++++ src/helpers/Monitor.hpp | 6 +++++ src/managers/KeybindManager.cpp | 16 +---------- src/render/Renderer.cpp | 8 ++++++ 5 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 2d817534..7c74534a 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -1007,6 +1007,7 @@ void CConfigManager::setDefaultAnimationVars() { m_animationTree.createNode("fadePopups", "fade"); m_animationTree.createNode("fadePopupsIn", "fadePopups"); m_animationTree.createNode("fadePopupsOut", "fadePopups"); + m_animationTree.createNode("fadeDpms", "fade"); // workspaces m_animationTree.createNode("workspacesIn", "workspaces"); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 93a798a0..79961bd4 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -51,6 +51,8 @@ CMonitor::CMonitor(SP output_) : m_state(this), m_output(ou m_cursorZoom->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); }); g_pAnimationManager->createAnimation(0.F, m_zoomAnimProgress, g_pConfigManager->getAnimationPropertyConfig("monitorAdded"), AVARDAMAGE_NONE); m_zoomAnimProgress->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); }); + g_pAnimationManager->createAnimation(0.F, m_dpmsBlackOpacity, g_pConfigManager->getAnimationPropertyConfig("fadeDpms"), AVARDAMAGE_NONE); + m_dpmsBlackOpacity->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); }); } CMonitor::~CMonitor() { @@ -80,6 +82,13 @@ void CMonitor::onConnect(bool noRule) { m_listeners.needsFrame = m_output->events.needsFrame.listen([this] { g_pCompositor->scheduleFrameForMonitor(m_self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); }); m_listeners.presented = m_output->events.present.listen([this](const Aquamarine::IOutput::SPresentEvent& event) { + if (m_pendingDpmsAnimation) { + // the first frame after a dpms on has been presented. Let's start the animation + m_dpmsBlackOpacity->setValueAndWarp(1.F); + *m_dpmsBlackOpacity = 0.F; + m_pendingDpmsAnimation = false; + } + timespec* ts = event.when; if (ts && ts->tv_sec <= 2) { @@ -1563,6 +1572,45 @@ bool CMonitor::attemptDirectScanout() { return true; } +void CMonitor::setDPMS(bool on) { + m_dpmsStatus = on; + m_events.dpmsChanged.emit(); + + 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_pendingDpmsAnimation = true; + commitDPMSState(true); + } else { + // disable the monitor. Begin the animation, then do dpms on its end. + m_dpmsBlackOpacity->setValueAndWarp(0.F); + *m_dpmsBlackOpacity = 1.F; + m_dpmsBlackOpacity->setCallbackOnEnd( + [this, self = m_self](auto) { + if (!self) + return; + + // commit DPMS to disable the monitor, it's fully black now + commitDPMSState(false); + }, + true); + } +} + +void CMonitor::commitDPMSState(bool state) { + m_output->state->resetExplicitFences(); + m_output->state->setEnabled(state); + + if (!m_state.commit()) { + Debug::log(ERR, "Couldn't commit output {} for DPMS = {}", m_name, state); + return; + } + + if (state) + g_pHyprRenderer->damageMonitor(m_self.lock()); +} + void CMonitor::debugLastPresentation(const std::string& message) { Debug::log(TRACE, "{} (last presentation {} - {} fps)", message, m_lastPresentationTimer.getMillis(), m_lastPresentationTimer.getMillis() > 0 ? 1000.0f / m_lastPresentationTimer.getMillis() : 0.0f); diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index a857cd49..1fa3d67d 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -181,6 +181,10 @@ class CMonitor { // for special fade/blur PHLANIMVAR m_specialFade; + // for dpms off anim + PHLANIMVAR m_dpmsBlackOpacity; + bool m_pendingDpmsAnimation = false; + PHLANIMVAR m_cursorZoom; // for initial zoom anim @@ -232,6 +236,7 @@ class CMonitor { bool attemptDirectScanout(); void setCTM(const Mat3x3& ctm); void onCursorMovedOnMonitor(); + void setDPMS(bool on); void debugLastPresentation(const std::string& message); @@ -259,6 +264,7 @@ class CMonitor { private: void setupDefaultWS(const SMonitorRule&); WORKSPACEID findAvailableDefaultWS(); + void commitDPMSState(bool state); bool m_doneScheduled = false; std::stack m_prevWorkSpaces; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 5f3aac1c..ef0d3da7 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -2670,21 +2670,7 @@ SDispatchResult CKeybindManager::dpms(std::string arg) { if (isToggle) enable = !m->m_dpmsStatus; - m->m_output->state->resetExplicitFences(); - m->m_output->state->setEnabled(enable); - - m->m_dpmsStatus = enable; - - if (!m->m_state.commit()) { - Debug::log(ERR, "Couldn't commit output {}", m->m_name); - res.success = false; - res.error = "Couldn't commit output {}"; - } - - if (enable) - g_pHyprRenderer->damageMonitor(m); - - m->m_events.dpmsChanged.emit(); + m->setDPMS(enable); } g_pCompositor->m_dpmsStateOn = enable; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 326f71bb..be982ed4 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1394,6 +1394,14 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { g_pPointerManager->renderSoftwareCursorsFor(pMonitor->m_self.lock(), NOW, g_pHyprOpenGL->m_renderData.damage); } + if (pMonitor->m_dpmsBlackOpacity->value() != 0.F) { + // render the DPMS black if we are animating + CRectPassElement::SRectData data; + data.box = {0, 0, pMonitor->m_transformedSize.x, pMonitor->m_transformedSize.y}; + data.color = Colors::BLACK.modifyA(pMonitor->m_dpmsBlackOpacity->value()); + m_renderPass.add(makeUnique(data)); + } + EMIT_HOOK_EVENT("render", RENDER_LAST_MOMENT); endRender();