From bc6c46a02f3a74351d1e0b73c2eb393705a25034 Mon Sep 17 00:00:00 2001 From: reionwong Date: Wed, 17 Nov 2021 00:49:17 +0800 Subject: [PATCH] PowerManager: add dim display feature --- README.md | 2 +- debian/control | 1 + powerman/CMakeLists.txt | 11 +++ powerman/action.cpp | 60 ++++++++++++++++ powerman/action.h | 89 ++++++++++++++++++++++++ powerman/application.cpp | 17 +++++ powerman/application.h | 14 ++++ powerman/com.cutefish.PowerManager.xml | 8 +++ powerman/dimdisplayaction.cpp | 79 +++++++++++++++++++++ powerman/dimdisplayaction.h | 45 ++++++++++++ powerman/idlemanager.cpp | 89 ++++++++++++++++++++++++ powerman/idlemanager.h | 52 ++++++++++++++ powerman/org.freedesktop.ScreenSaver.xml | 41 +++++++++++ screen-brightness/brightnesshelper.cpp | 26 ++++--- session/processmanager.cpp | 2 +- 15 files changed, 524 insertions(+), 12 deletions(-) create mode 100644 powerman/action.cpp create mode 100644 powerman/action.h create mode 100644 powerman/com.cutefish.PowerManager.xml create mode 100644 powerman/dimdisplayaction.cpp create mode 100644 powerman/dimdisplayaction.h create mode 100644 powerman/idlemanager.cpp create mode 100644 powerman/idlemanager.h create mode 100644 powerman/org.freedesktop.ScreenSaver.xml diff --git a/README.md b/README.md index 009538c..72716a3 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ sudo pacman -S extra-cmake-modules pkgconf qt5-base qt5-quickcontrols2 qt5-x11ex For Ubuntu: ```shell sudo apt install libpolkit-agent-1-dev libpolkit-qt5-1-dev libpulse-dev libsm-dev libxtst-dev\ - libxcb-randr0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxcb-composite0-dev libxcb-damage0-dev libxcb-image0-dev libxcb-util0-dev + libxcb-randr0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxcb-composite0-dev libxcb-damage0-dev libxcb-image0-dev libxcb-util0-dev libkf5idletime-dev ``` (Yes it's annoying that so many xcb's packages here is needed to install. Isn't there a way to install one package and these `libxcb`s all get ready?) diff --git a/debian/control b/debian/control index 8aea7ff..3d9f543 100644 --- a/debian/control +++ b/debian/control @@ -34,6 +34,7 @@ Build-Depends: cmake, libkf5windowsystem-dev, libkf5globalaccel-dev, libkf5coreaddons-dev, + libkf5idletime-dev, libqt5x11extras5-dev, qtbase5-dev, qtdeclarative5-dev, diff --git a/powerman/CMakeLists.txt b/powerman/CMakeLists.txt index a0be85c..9fefcdf 100644 --- a/powerman/CMakeLists.txt +++ b/powerman/CMakeLists.txt @@ -1,10 +1,15 @@ project(cutefish-powerman) set(TARGET cutefish-powerman) +find_package(KF5IdleTime) + set(SOURCES main.cpp application.cpp lidwatcher.cpp + action.cpp + idlemanager.cpp + dimdisplayaction.cpp cpu/cpuitem.cpp cpu/cpumanagement.cpp ) @@ -12,6 +17,10 @@ set(SOURCES qt5_add_dbus_adaptor(DBUS_SOURCES cpu/com.cutefish.CPUManagement.xml cpu/cpumanagement.h CPUManagement) +qt5_add_dbus_adaptor(DBUS_SOURCES + com.cutefish.PowerManager.xml + application.h Application) +qt_add_dbus_interface(DBUS_SOURCES org.freedesktop.ScreenSaver.xml screenlocker_interface) set_source_files_properties(${DBUS_SOURCES} PROPERTIES SKIP_AUTOGEN ON) add_executable(${TARGET} ${SOURCES} ${DBUS_SOURCES}) @@ -22,6 +31,8 @@ target_link_libraries(${TARGET} Qt5::Quick Qt5::DBus Qt5::X11Extras + + KF5::IdleTime ) install(TARGETS ${TARGET} DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/powerman/action.cpp b/powerman/action.cpp new file mode 100644 index 0000000..de64432 --- /dev/null +++ b/powerman/action.cpp @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2010 by Dario Freddi * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "action.h" +#include "idlemanager.h" + +#include + +class Action::Private +{ +public: + Private() {} + ~Private() {} + + QVector< int > registeredIdleTimeouts; +}; + +Action::Action(QObject* parent) + : QObject(parent) + , d(new Private) +{ +} + +Action::~Action() +{ + delete d; +} + +void Action::registerIdleTimeout(int msec) +{ + d->registeredIdleTimeouts.append(msec); + IdleManager::self()->registerActionTimeout(this, msec); +} + +bool Action::isSupported() +{ + return true; +} + +void Action::setTimeout(int timeout) +{ + Q_UNUSED(timeout) +} + diff --git a/powerman/action.h b/powerman/action.h new file mode 100644 index 0000000..141acac --- /dev/null +++ b/powerman/action.h @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (C) 2010 by Dario Freddi * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + + +#ifndef POWERDEVIL_POWERDEVILACTION_H +#define POWERDEVIL_POWERDEVILACTION_H + +#include +#include + +class IDleManager; +class Q_DECL_EXPORT Action : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(Action) + +public: + /** + * Default constructor + */ + explicit Action(QObject *parent); + /** + * Default destructor + */ + ~Action() override; + + /** + * This function is meant to find out if this action is available on this system. Actions + * CAN reimplement this function if they are dependent on specific hardware/software requirements. + * By default, this function will always return true. + * + * Should this function return false, the core will delete and ignore the action right after creation. + * + * @returns Whether this action is supported or not by the current system + */ + virtual bool isSupported(); + + virtual void setTimeout(int timeout); + +protected: + /** + * Registers an idle timeout for this action. Call this function and not KIdleTime directly to take advantage + * of Action's automated handling of idle timeouts. Also, please reimplement onIdleTimeout instead of listening + * to KIdleTime's signals to catch idle timeout events. + * + * @param msec The idle timeout to be registered in milliseconds. + */ + void registerIdleTimeout(int msec); + +public Q_SLOTS: + /** + * This slot is triggered whenever an idle timeout registered with registerIdleTimeout is reached. + * + * @param msec The idle timeout reached in milliseconds + */ + virtual void onIdleTimeout(int msec) = 0; + /** + * This slot is triggered whenever the PC wakes up from an Idle state. It is @b always called after a registered + * idle timeout has been reached. + */ + virtual void onWakeupFromIdle() = 0; + +Q_SIGNALS: + void actionTriggered(bool result, const QString &error = QString()); + +private: + class Private; + Private * const d; + + friend class IDleManager; +}; + +#endif // POWERDEVIL_POWERDEVILACTION_H diff --git a/powerman/application.cpp b/powerman/application.cpp index d0644ac..5396112 100644 --- a/powerman/application.cpp +++ b/powerman/application.cpp @@ -18,12 +18,29 @@ */ #include "application.h" +#include "powermanageradaptor.h" + #include +#include Application::Application(QObject *parent) : QObject(parent) , m_lidWatcher(new LidWatcher) , m_cpuManagement(new CPUManagement) + , m_settings("cutefishos", "power") + , m_dimDisplayAction(new DimDisplayAction) { + m_closeScreenTimeout = m_settings.value("CloseScreenTimeout", 600).toInt(); + + m_dimDisplayAction->setTimeout(m_closeScreenTimeout); + QDBusConnection::sessionBus().registerService(QStringLiteral("com.cutefish.PowerManager")); + QDBusConnection::sessionBus().registerObject(QStringLiteral("/PowerManager"), this); + new PowerManagerAdaptor(this); +} + +void Application::setDimDisplayTimeout(int timeout) +{ + m_closeScreenTimeout = timeout; + m_dimDisplayAction->setTimeout(timeout); } diff --git a/powerman/application.h b/powerman/application.h index 45402c5..317eaf2 100644 --- a/powerman/application.h +++ b/powerman/application.h @@ -21,7 +21,14 @@ #define APPLICATION_H #include +#include +#include +#include + #include "lidwatcher.h" +#include "idlemanager.h" +#include "dimdisplayaction.h" + #include "cpu/cpumanagement.h" class Application : public QObject @@ -31,9 +38,16 @@ class Application : public QObject public: explicit Application(QObject *parent = nullptr); + void setDimDisplayTimeout(int timeout); + private: LidWatcher *m_lidWatcher; CPUManagement *m_cpuManagement; + QSettings m_settings; + + int m_closeScreenTimeout; + + DimDisplayAction *m_dimDisplayAction; }; #endif // APPLICATION_H diff --git a/powerman/com.cutefish.PowerManager.xml b/powerman/com.cutefish.PowerManager.xml new file mode 100644 index 0000000..a7ab292 --- /dev/null +++ b/powerman/com.cutefish.PowerManager.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/powerman/dimdisplayaction.cpp b/powerman/dimdisplayaction.cpp new file mode 100644 index 0000000..46f0d7f --- /dev/null +++ b/powerman/dimdisplayaction.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2021 by Reion Wong * + * Copyright (C) 2010 by Dario Freddi * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "dimdisplayaction.h" +#include +#include +#include +#include + +DimDisplayAction::DimDisplayAction(QObject *parent) + : Action(parent) + , m_iface("com.cutefish.Settings", + "/Brightness", + "com.cutefish.Brightness", QDBusConnection::sessionBus()) +{ +} + +void DimDisplayAction::onWakeupFromIdle() +{ + if (!m_dimmed) { + return; + } + + // 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)); + }); + + m_dimmed = false; +} + +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)); + } 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)) { + m_oldScreenBrightness = m_iface.property("brightness").toInt(); + + const int newBrightness = qRound(m_oldScreenBrightness / 2.0); + + m_iface.asyncCall("setValue", QVariant::fromValue(newBrightness)); + } + + m_dimmed = true; +} + +void DimDisplayAction::setTimeout(int timeout) +{ + m_dimOnIdleTime = timeout; + registerIdleTimeout(m_dimOnIdleTime * 3 / 4); + registerIdleTimeout(m_dimOnIdleTime / 2); + registerIdleTimeout(m_dimOnIdleTime); +} diff --git a/powerman/dimdisplayaction.h b/powerman/dimdisplayaction.h new file mode 100644 index 0000000..ef0908d --- /dev/null +++ b/powerman/dimdisplayaction.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2021 by Reion Wong * + * Copyright (C) 2010 by Dario Freddi * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef DIMDISPLAYACTION_H +#define DIMDISPLAYACTION_H + +#include "action.h" +#include + +class DimDisplayAction : public Action +{ + Q_OBJECT + +public: + explicit DimDisplayAction(QObject *parent = nullptr); + + void onWakeupFromIdle() override; + void onIdleTimeout(int msec) override; + void setTimeout(int timeout) override; + +private: + QDBusInterface m_iface; + int m_dimOnIdleTime = 0; + int m_oldScreenBrightness = 0; + bool m_dimmed = false; +}; + +#endif // DIMDISPLAYACTION_H diff --git a/powerman/idlemanager.cpp b/powerman/idlemanager.cpp new file mode 100644 index 0000000..45b6948 --- /dev/null +++ b/powerman/idlemanager.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (C) 2021 by Reion Wong * + * Copyright (C) 2010 by Dario Freddi * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "idlemanager.h" + +#include + +IdleManager *IdleManager::self() +{ + static IdleManager mgr; + return &mgr; +} + +IdleManager::IdleManager(QObject *parent) + : QObject(parent) +{ + connect(KIdleTime::instance(), SIGNAL(timeoutReached(int,int)), + this, SLOT(onKIdleTimeoutReached(int,int))); + connect(KIdleTime::instance(), &KIdleTime::resumingFromIdle, + this, &IdleManager::onResumingFromIdle); +} + +void IdleManager::registerActionTimeout(Action *action, int secs) +{ + int identifier = KIdleTime::instance()->addIdleTimeout(secs * 1000); + + QList timeouts = m_registeredActionTimeouts[action]; + timeouts.append(identifier); + + m_registeredActionTimeouts[action] = timeouts; +} + +void IdleManager::unregisterActionTimeouts(Action *action) +{ + // Clear all timeouts from the action + const QList< int > timeoutsToClean = m_registeredActionTimeouts[action]; + + for (int id : timeoutsToClean) { + KIdleTime::instance()->removeIdleTimeout(id); + } + + m_registeredActionTimeouts.remove(action); +} + +void IdleManager::onKIdleTimeoutReached(int identifier, int timeout) +{ + for (auto i = m_registeredActionTimeouts.constBegin(), end = m_registeredActionTimeouts.constEnd(); i != end; ++i) { + if (i.value().contains(identifier)) { + i.key()->onIdleTimeout(timeout); + + // And it will need to be awaken + m_pendingResumeFromIdleActions.insert(i.key()); + + break; + } + } + + // Catch the next resume event if some actions require it + if (!m_pendingResumeFromIdleActions.isEmpty()) { + KIdleTime::instance()->catchNextResumeEvent(); + } +} + +void IdleManager::onResumingFromIdle() +{ + KIdleTime::instance()->simulateUserActivity(); + // Wake up the actions in which an idle action was triggered + std::for_each(m_pendingResumeFromIdleActions.cbegin(), m_pendingResumeFromIdleActions.cend(), + std::mem_fn(&Action::onWakeupFromIdle)); + + m_pendingResumeFromIdleActions.clear(); +} diff --git a/powerman/idlemanager.h b/powerman/idlemanager.h new file mode 100644 index 0000000..a7b65aa --- /dev/null +++ b/powerman/idlemanager.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2021 by Reion Wong * + * Copyright (C) 2010 by Dario Freddi * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef IDLEMANAGER_H +#define IDLEMANAGER_H + +#include +#include +#include + +#include "action.h" + +class IdleManager : public QObject +{ + Q_OBJECT + +public: + static IdleManager *self(); + explicit IdleManager(QObject *parent = nullptr); + + void registerActionTimeout(Action *action, int secs); + void unregisterActionTimeouts(Action *action); + +private slots: + void onKIdleTimeoutReached(int, int); + void onResumingFromIdle(); + +private: + friend class Action; + + QHash> m_registeredActionTimeouts; + QSet m_pendingResumeFromIdleActions; +}; + +#endif // IDLEMANAGER_H diff --git a/powerman/org.freedesktop.ScreenSaver.xml b/powerman/org.freedesktop.ScreenSaver.xml new file mode 100644 index 0000000..5efd943 --- /dev/null +++ b/powerman/org.freedesktop.ScreenSaver.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/screen-brightness/brightnesshelper.cpp b/screen-brightness/brightnesshelper.cpp index 7e479c0..a11fa3e 100644 --- a/screen-brightness/brightnesshelper.cpp +++ b/screen-brightness/brightnesshelper.cpp @@ -20,7 +20,8 @@ #include "brightnesshelper.h" -BrightnessHelper::BrightnessHelper(QObject *parent) : QObject(parent) +BrightnessHelper::BrightnessHelper(QObject *parent) + : QObject(parent) { init(); anime.setEasingCurve(QEasingCurve::Linear); @@ -30,12 +31,13 @@ BrightnessHelper::BrightnessHelper(QObject *parent) : QObject(parent) writeBrightness(value.toInt()); } }); - connect(&anime, &QVariantAnimation::finished, this, [this](){ + connect(&anime, &QVariantAnimation::finished, this, [this]() { qApp->quit(); }); } -BrightnessHelper::~BrightnessHelper(){ +BrightnessHelper::~BrightnessHelper() +{ } void BrightnessHelper::init() @@ -86,9 +88,10 @@ void BrightnessHelper::init() maxValue = readFile(QDir(name).filePath(kMaxFile)).toInt(); } -void BrightnessHelper::setBrightness(int value){ - if (value <= 0) - value = 1; +void BrightnessHelper::setBrightness(int value) +{ + if (value < 0) + value = 0; if (value > 100) value = 100; anime.setDuration(85); @@ -98,21 +101,24 @@ void BrightnessHelper::setBrightness(int value){ anime.start(); } -void BrightnessHelper::increaseBrightness() { +void BrightnessHelper::increaseBrightness() +{ int value = static_cast(actual / (double)maxValue * 100); setBrightness(value + 20); } -void BrightnessHelper::decreaseBrightness() { +void BrightnessHelper::decreaseBrightness() +{ int value = static_cast(actual / (double)maxValue * 100); setBrightness(value - 20); } -void BrightnessHelper::writeBrightness(int brightness) { +void BrightnessHelper::writeBrightness(int brightness) +{ if (brightness < 0) brightness = 0; if (brightness > maxValue) brightness = maxValue; QString dbg_s = QDir(name).filePath("brightness"); writeFile(QDir(name).filePath("brightness"), QString::number(brightness)); -} \ No newline at end of file +} diff --git a/session/processmanager.cpp b/session/processmanager.cpp index 65b8db4..1e57665 100644 --- a/session/processmanager.cpp +++ b/session/processmanager.cpp @@ -157,9 +157,9 @@ void ProcessManager::startDaemonProcess() { QList> list; list << qMakePair(QString("cutefish-settings-daemon"), QStringList()); - list << qMakePair(QString("cutefish-powerman"), QStringList()); list << qMakePair(QString("cutefish-xembedsniproxy"), QStringList()); list << qMakePair(QString("cutefish-gmenuproxy"), QStringList()); + list << qMakePair(QString("cutefish-powerman"), QStringList()); list << qMakePair(QString("chotkeys"), QStringList()); for (QPair pair : list) {