From f06f7b795dc945220142cac9e843fb8415f20f89 Mon Sep 17 00:00:00 2001 From: Reion Wong Date: Mon, 31 Aug 2026 02:28:53 -0400 Subject: [PATCH] feat(power): use KWin Wayland DPMS for screen timeout --- debian/control | 1 + powerman/CMakeLists.txt | 2 + powerman/dimdisplayaction.cpp | 133 ++++++++++++++++-- powerman/dimdisplayaction.h | 30 +++- .../brightness/brightnessmanager.cpp | 1 - 5 files changed, 150 insertions(+), 17 deletions(-) diff --git a/debian/control b/debian/control index e5784dd..37d7d2a 100644 --- a/debian/control +++ b/debian/control @@ -10,6 +10,7 @@ Build-Depends: cmake, libpolkit-agent-1-dev, libkf6coreaddons-dev, libkf6idletime-dev, + kwayland-dev, qt6-base-dev, qt6-declarative-dev, qt6-tools-dev, diff --git a/powerman/CMakeLists.txt b/powerman/CMakeLists.txt index 15ab708..8d30123 100644 --- a/powerman/CMakeLists.txt +++ b/powerman/CMakeLists.txt @@ -2,6 +2,7 @@ project(cutefish-powerman) set(TARGET cutefish-powerman) find_package(KF6IdleTime) +find_package(KWayland REQUIRED) set(SOURCES main.cpp application.cpp @@ -31,6 +32,7 @@ target_link_libraries(${TARGET} Qt6::DBus KF6::IdleTime + Plasma::KWaylandClient ) install(TARGETS ${TARGET} DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/powerman/dimdisplayaction.cpp b/powerman/dimdisplayaction.cpp index 2c8f36b..77b9586 100644 --- a/powerman/dimdisplayaction.cpp +++ b/powerman/dimdisplayaction.cpp @@ -21,17 +21,113 @@ #include "dimdisplayaction.h" #include "screenlocker_interface.h" +#include #include #include #include #include +#include + +#include +#include +#include +#include + DimDisplayAction::DimDisplayAction(QObject *parent) : Action(parent) , m_iface("com.cutefish.Settings", "/Brightness", "com.cutefish.Brightness", QDBusConnection::sessionBus()) { + setupWaylandDpms(); +} + +void DimDisplayAction::setupWaylandDpms() +{ + if (!QGuiApplication::platformName().startsWith(QStringLiteral("wayland"))) + return; + + m_waylandConnection = KWayland::Client::ConnectionThread::fromApplication(this); + if (!m_waylandConnection) + return; + + m_waylandRegistry = new KWayland::Client::Registry(this); + m_waylandRegistry->create(m_waylandConnection); + + connect(m_waylandRegistry, &KWayland::Client::Registry::outputAnnounced, + this, &DimDisplayAction::setupWaylandOutput); + connect(m_waylandRegistry, &KWayland::Client::Registry::dpmsAnnounced, + this, &DimDisplayAction::setupWaylandDpmsManager); + + m_waylandRegistry->setup(); + m_waylandConnection->roundtrip(); + // DPMS support is announced after the output objects are created. + m_waylandConnection->roundtrip(); +} + +void DimDisplayAction::setupWaylandOutput(quint32 name, quint32 version) +{ + auto *output = m_waylandRegistry->createOutput(name, version, this); + if (!output || !output->isValid()) + return; + + m_waylandOutputs.append(output); + addWaylandDpms(output); +} + +void DimDisplayAction::setupWaylandDpmsManager(quint32 name, quint32 version) +{ + if (m_waylandDpmsManager) + return; + + m_waylandDpmsManager = m_waylandRegistry->createDpmsManager(name, version, this); + if (!m_waylandDpmsManager || !m_waylandDpmsManager->isValid()) { + m_waylandDpmsManager = nullptr; + return; + } + + for (const auto &output : std::as_const(m_waylandOutputs)) { + if (output) + addWaylandDpms(output); + } +} + +void DimDisplayAction::addWaylandDpms(KWayland::Client::Output *output) +{ + if (!m_waylandDpmsManager || !output) + return; + + auto *dpms = m_waylandDpmsManager->getDpms(output, this); + if (dpms && dpms->isValid()) + m_waylandDpmsOutputs.append(dpms); +} + +bool DimDisplayAction::hasSupportedWaylandDpms() const +{ + for (const auto &dpms : std::as_const(m_waylandDpmsOutputs)) { + if (dpms && dpms->isSupported()) + return true; + } + + return false; +} + +bool DimDisplayAction::setDisplayPower(bool on) +{ + bool requested = false; + const auto mode = on ? KWayland::Client::Dpms::Mode::On + : KWayland::Client::Dpms::Mode::Off; + + for (const auto &dpms : std::as_const(m_waylandDpmsOutputs)) { + if (!dpms || !dpms->isSupported()) + continue; + + dpms->requestMode(mode); + requested = true; + } + + return requested; } void DimDisplayAction::onWakeupFromIdle() @@ -40,15 +136,19 @@ void DimDisplayAction::onWakeupFromIdle() return; } - if (m_oldScreenBrightness < 0) - m_oldScreenBrightness = 1; - // An active inhibition may not let us restore the brightness. // We should wait a bit screen to wake-up from sleep QTimer::singleShot(0, this, [this]() { - m_iface.asyncCall("setValue", QVariant::fromValue(m_oldScreenBrightness)); + if (m_displayPoweredOff) { + setDisplayPower(true); + m_displayPoweredOff = false; + } + + if (m_oldScreenBrightness >= 0) + m_iface.asyncCall("setValue", QVariant::fromValue(m_oldScreenBrightness)); }); + m_oldScreenBrightness = -1; m_dimmed = false; } @@ -56,11 +156,11 @@ void DimDisplayAction::onIdleTimeout(int msec) { int sec = msec / 1000; - if (m_iface.property("brightness").toInt() == 0) - return; - if (sec == m_dimOnIdleTime) { - m_iface.asyncCall("setValue", QVariant::fromValue(0)); + m_displayPoweredOff = setDisplayPower(false); + + if (!m_displayPoweredOff && m_iface.property("brightness").toInt() > 0) + m_iface.asyncCall("setValue", QVariant::fromValue(0)); // Sleep if (m_sleep) { @@ -81,15 +181,18 @@ void DimDisplayAction::onIdleTimeout(int msec) screenSaver.Lock(); } - } else if (sec == (m_dimOnIdleTime * 3 / 4)) { - const int newBrightness = qRound(m_oldScreenBrightness / 8.0); - m_iface.asyncCall("setValue", QVariant::fromValue(newBrightness)); - } else if (sec == (m_dimOnIdleTime * 1 / 2)) { + } else if (!hasSupportedWaylandDpms() && sec == (m_dimOnIdleTime * 3 / 4)) { + if (m_oldScreenBrightness > 0) { + const int newBrightness = qRound(m_oldScreenBrightness / 8.0); + m_iface.asyncCall("setValue", QVariant::fromValue(newBrightness)); + } + } else if (!hasSupportedWaylandDpms() && sec == (m_dimOnIdleTime * 1 / 2)) { m_oldScreenBrightness = m_iface.property("brightness").toInt(); - const int newBrightness = qRound(m_oldScreenBrightness / 2.0); - - m_iface.asyncCall("setValue", QVariant::fromValue(newBrightness)); + if (m_oldScreenBrightness > 0) { + const int newBrightness = qRound(m_oldScreenBrightness / 2.0); + m_iface.asyncCall("setValue", QVariant::fromValue(newBrightness)); + } } m_dimmed = true; diff --git a/powerman/dimdisplayaction.h b/powerman/dimdisplayaction.h index 5dba415..d44af86 100644 --- a/powerman/dimdisplayaction.h +++ b/powerman/dimdisplayaction.h @@ -22,7 +22,21 @@ #define DIMDISPLAYACTION_H #include "action.h" +#include #include +#include + +namespace KWayland +{ +namespace Client +{ +class ConnectionThread; +class Dpms; +class DpmsManager; +class Output; +class Registry; +} +} class DimDisplayAction : public Action { @@ -38,12 +52,26 @@ public: void setLock(bool lock); private: + void setupWaylandDpms(); + void setupWaylandOutput(quint32 name, quint32 version); + void setupWaylandDpmsManager(quint32 name, quint32 version); + void addWaylandDpms(KWayland::Client::Output *output); + bool hasSupportedWaylandDpms() const; + bool setDisplayPower(bool on); + QDBusInterface m_iface; int m_dimOnIdleTime = 0; - int m_oldScreenBrightness = 0; + int m_oldScreenBrightness = -1; bool m_dimmed = false; bool m_sleep = false; bool m_lock = false; + bool m_displayPoweredOff = false; + + KWayland::Client::ConnectionThread *m_waylandConnection = nullptr; + KWayland::Client::Registry *m_waylandRegistry = nullptr; + KWayland::Client::DpmsManager *m_waylandDpmsManager = nullptr; + QList> m_waylandOutputs; + QList> m_waylandDpmsOutputs; }; #endif // DIMDISPLAYACTION_H diff --git a/settings-daemon/brightness/brightnessmanager.cpp b/settings-daemon/brightness/brightnessmanager.cpp index 4dfe2c4..4b28223 100644 --- a/settings-daemon/brightness/brightnessmanager.cpp +++ b/settings-daemon/brightness/brightnessmanager.cpp @@ -158,7 +158,6 @@ void BrightnessManager::initUsingWhitelist() void BrightnessManager::initUsingBacklightType() { - QString m_dirname; QDir dir(PREFIX); dir.setFilter(QDir::AllDirs | QDir::NoDot | QDir::NoDotDot | QDir::NoDotAndDotDot | QDir::Readable); dir.setSorting(QDir::Name | QDir::Reversed);// Reverse is needed to priorize acpi_video1 over 0