diff --git a/daemon/backends/upower/backlighthelper.cpp b/daemon/backends/upower/backlighthelper.cpp index 5b791ef5..333b8e51 100644 --- a/daemon/backends/upower/backlighthelper.cpp +++ b/daemon/backends/upower/backlighthelper.cpp @@ -1,339 +1,348 @@ /* This file is part of the KDE project * Copyright (C) 2010-2011 Lukas Tinkl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "backlighthelper.h" #include #include #include +#include #include #include #include #ifdef Q_OS_FREEBSD #define USE_SYSCTL #endif #ifdef USE_SYSCTL #include #include #define HAS_SYSCTL(n) (sysctlbyname(n, nullptr, nullptr, nullptr, 0) == 0) #endif #define BACKLIGHT_SYSFS_PATH "/sys/class/backlight/" #define LED_SYSFS_PATH "/sys/class/leds/" BacklightHelper::BacklightHelper(QObject *parent) : QObject(parent) { init(); } void BacklightHelper::init() { initUsingBacklightType(); if (m_dirname.isEmpty()) { initUsingSysctl(); if (m_sysctlDevice.isEmpty() || m_sysctlBrightnessLevels.isEmpty()) { qCWarning(POWERDEVIL) << "no kernel backlight interface found"; return; } } + m_anim.setEasingCurve(QEasingCurve::InOutQuad); + connect(&m_anim, &QVariantAnimation::valueChanged, this, [this](const QVariant &value) { + writeBrightness(value.toInt()); + }); + m_isSupported = true; } void BacklightHelper::initUsingBacklightType() { QDir backlightDir(BACKLIGHT_SYSFS_PATH); backlightDir.setFilter(QDir::AllDirs | QDir::NoDot | QDir::NoDotDot | QDir::NoDotAndDotDot | QDir::Readable); backlightDir.setSorting(QDir::Name | QDir::Reversed);// Reverse is needed to priorize acpi_video1 over 0 const QStringList interfaces = backlightDir.entryList(); QFile file; QByteArray buffer; QStringList firmware, platform, raw, leds; for (const QString & interface : interfaces) { file.setFileName(BACKLIGHT_SYSFS_PATH + interface + "/type"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { continue; } buffer = file.readLine().trimmed(); if (buffer == "firmware") { firmware.append(interface); } else if(buffer == "platform") { platform.append(interface); } else if (buffer == "raw") { raw.append(interface); } else { qCWarning(POWERDEVIL) << "Interface type not handled" << buffer; } file.close(); } QDir ledsDir(LED_SYSFS_PATH); ledsDir.setFilter(QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::NoDotAndDotDot | QDir::Readable); ledsDir.setNameFilters({QStringLiteral("*lcd*"), QStringLiteral("*wled*")}); QStringList ledInterfaces = ledsDir.entryList(); if (!ledInterfaces.isEmpty()) { m_dirname = LED_SYSFS_PATH + ledInterfaces.constFirst(); return; } if (!firmware.isEmpty()) { m_dirname = BACKLIGHT_SYSFS_PATH + firmware.constFirst(); return; } if (!platform.isEmpty()) { m_dirname = BACKLIGHT_SYSFS_PATH + platform.constFirst(); return; } if (!raw.isEmpty()) { m_dirname = BACKLIGHT_SYSFS_PATH + raw.constFirst(); return; } } void BacklightHelper::initUsingSysctl() { #ifdef USE_SYSCTL /* * lcd0 is, in theory, the correct device, but some vendors have custom ACPI implementations * which cannot be interpreted. In that case, devices should be reported as "out", but * FreeBSD doesn't care (yet), so they can appear as any other type. Let's search for the first * device with brightness management, then. */ QStringList types; types << QStringLiteral("lcd") << QStringLiteral("out") << QStringLiteral("crt") << QStringLiteral("tv") << QStringLiteral("ext"); for (const QString &type : types) { for (int i = 0; m_sysctlDevice.isEmpty(); i++) { QString device = QStringLiteral("%1%2").arg(type, QString::number(i)); // We don't care about the value, we only want the sysctl to be there. if (!HAS_SYSCTL(qPrintable(QStringLiteral("hw.acpi.video.%1.active").arg(device)))) { break; } if (HAS_SYSCTL(qPrintable(QStringLiteral("hw.acpi.video.%1.brightness").arg(device))) && HAS_SYSCTL(qPrintable(QStringLiteral("hw.acpi.video.%1.levels").arg(device)))) { m_sysctlDevice = device; break; } } } if (m_sysctlDevice.isEmpty()) { return; } size_t len; if (sysctlbyname(qPrintable(QStringLiteral("hw.acpi.video.%1.levels").arg(m_sysctlDevice)), nullptr, &len, nullptr, 0) != 0 || len == 0) { return; } int *levels = (int *)malloc(len); if (!levels) { return; } if (sysctlbyname(qPrintable(QString("hw.acpi.video.%1.levels").arg(m_sysctlDevice)), levels, &len, nullptr, 0) != 0) { free(levels); return; } // acpi_video(4) supports only some predefined brightness levels. int nlevels = len / sizeof(int); for (int i = 0; i < nlevels; i++) { m_sysctlBrightnessLevels << levels[i]; } free(levels); // Sorting helps when finding max value and when scanning for the nearest level in setbrightness(). std::sort(m_sysctlBrightnessLevels.begin(), m_sysctlBrightnessLevels.end()); #endif } ActionReply BacklightHelper::brightness(const QVariantMap &args) { Q_UNUSED(args); + const int brightness = readBrightness(); + + if (brightness == -1) { + return ActionReply::HelperErrorReply(); + } ActionReply reply; + reply.addData("brightness", brightness); + return reply; +} +int BacklightHelper::readBrightness() const +{ if (!m_isSupported) { - reply = ActionReply::HelperErrorReply(); - return reply; + return -1; } - // current brightness int brightness; #ifdef USE_SYSCTL size_t len = sizeof(int); if (sysctlbyname(qPrintable(QStringLiteral("hw.acpi.video.%1.brightness").arg(m_sysctlDevice)), &brightness, &len, nullptr, 0) != 0) { - reply = ActionReply::HelperErrorReply(); - return reply; + return -1; } #else QFile file(m_dirname + "/brightness"); if (!file.open(QIODevice::ReadOnly)) { - reply = ActionReply(ActionReply::HelperErrorType); - reply.setErrorDescription(i18n("Can't open file")); qCWarning(POWERDEVIL) << "reading brightness failed with error code " << file.error() << file.errorString(); - return reply; + return -1; } QTextStream stream(&file); stream >> brightness; file.close(); #endif - //qCDebug(POWERDEVIL) << "brightness:" << brightness; - reply.addData("brightness", brightness); - //qCDebug(POWERDEVIL) << "data contains:" << reply.data()["brightness"]; - - return reply; + return brightness; } ActionReply BacklightHelper::setbrightness(const QVariantMap &args) { - ActionReply reply; - - int actual_brightness = args.value(QStringLiteral("brightness")).toInt(); - if (!m_isSupported) { - reply = ActionReply::HelperErrorReply(); - return reply; + return ActionReply::HelperErrorReply(); } - //qCDebug(POWERDEVIL) << "setting brightness:" << actual_brightness; + const int brightness = args.value(QStringLiteral("brightness")).toInt(); + const int animationDuration = args.value(QStringLiteral("animationDuration")).toInt(); + + m_anim.stop(); + m_anim.setDuration(animationDuration); + m_anim.setStartValue(readBrightness()); + m_anim.setEndValue(brightness); + m_anim.start(); + + return ActionReply::SuccessReply(); +} +bool BacklightHelper::writeBrightness(int brightness) const +{ #ifdef USE_SYSCTL int actual_level = -1; int d1 = 101; // Search for the nearest level. for (int level : m_sysctlBrightnessLevels) { int d2 = qAbs(level - actual_brightness); /* * The list is sorted, so we break when it starts diverging. There may be repeated values, * so we keep going on equal gap (e.g., value = 7.5, levels = 0 0 10 ...: we don't break at * the second '0' so we can get to the '10'). This also means that the value will always * round off to the bigger level when in the middle (e.g., value = 5, levels = 0 10 ...: * value rounds off to 10). */ if (d2 > d1) { break; } actual_level = level; d1 = d2; } size_t len = sizeof(int); - if (sysctlbyname(qPrintable(QStringLiteral("hw.acpi.video.%1.brightness").arg(m_sysctlDevice)), nullptr, nullptr, &actual_level, len) != 0) { - reply = ActionReply::HelperErrorReply(); - return reply; - } + return sysctlbyname(qPrintable(QStringLiteral("hw.acpi.video.%1.brightness").arg(m_sysctlDevice)), nullptr, nullptr, &actual_level, len) == 0; #else QFile file(m_dirname + QLatin1String("/brightness")); if (!file.open(QIODevice::WriteOnly)) { - reply = ActionReply::HelperErrorReply(); -// reply.setErrorCode(ActionReply::ActionReply::UserCancelledError); qCWarning(POWERDEVIL) << "writing brightness failed with error code " << file.error() << file.errorString(); - return reply; + return false; } - int result = file.write(QByteArray::number(actual_brightness)); - file.close(); - - if (result == -1) { - reply = ActionReply::HelperErrorReply(); -// reply.setErrorCode(file.error()); + const int bytesWritten = file.write(QByteArray::number(brightness)); + if (bytesWritten == -1) { qCWarning(POWERDEVIL) << "writing brightness failed with error code " << file.error() << file.errorString(); + return false; } + + return true; #endif - return reply; + + return false; } ActionReply BacklightHelper::syspath(const QVariantMap &args) { Q_UNUSED(args); ActionReply reply; if (!m_isSupported || m_dirname.isEmpty()) { reply = ActionReply::HelperErrorReply(); return reply; } reply.addData("syspath", m_dirname); return reply; } ActionReply BacklightHelper::brightnessmax(const QVariantMap &args) { Q_UNUSED(args); ActionReply reply; if (!m_isSupported) { reply = ActionReply::HelperErrorReply(); return -1; } // maximum brightness int max_brightness; #ifdef USE_SYSCTL max_brightness = m_sysctlBrightnessLevels.last(); #else QFile file(m_dirname + QLatin1String("/max_brightness")); if (!file.open(QIODevice::ReadOnly)) { reply = ActionReply::HelperErrorReply(); // reply.setErrorCode(file.error()); qCWarning(POWERDEVIL) << "reading max brightness failed with error code " << file.error() << file.errorString(); return reply; } QTextStream stream(&file); stream >> max_brightness; file.close(); #endif //qCDebug(POWERDEVIL) << "max brightness:" << max_brightness; if (max_brightness <= 0) { reply = ActionReply::HelperErrorReply(); return reply; } reply.addData("brightnessmax", max_brightness); //qCDebug(POWERDEVIL) << "data contains:" << reply.data()["brightnessmax"]; return reply; } KAUTH_HELPER_MAIN("org.kde.powerdevil.backlighthelper", BacklightHelper) diff --git a/daemon/backends/upower/backlighthelper.h b/daemon/backends/upower/backlighthelper.h index b07bce3d..9b251566 100644 --- a/daemon/backends/upower/backlighthelper.h +++ b/daemon/backends/upower/backlighthelper.h @@ -1,61 +1,68 @@ /* This file is part of the KDE project * Copyright (C) 2010 Lukas Tinkl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #ifndef BACKLIGHTHELPER_H #define BACKLIGHTHELPER_H #include +#include + #include using namespace KAuth; class BacklightHelper: public QObject { Q_OBJECT public: explicit BacklightHelper(QObject *parent = nullptr); public Q_SLOTS: ActionReply brightness(const QVariantMap &args); ActionReply brightnessmax(const QVariantMap &args); ActionReply setbrightness(const QVariantMap &args); ActionReply syspath(const QVariantMap &args); private: void init(); + int readBrightness() const; + bool writeBrightness(int brightness) const; + /** * The kernel offer from version 2.6.37 the type of the interface, and based on that * we can decide which interface is better for us, being the order * firmware-platform-raw */ void initUsingBacklightType(); /** * FreeBSD (and other BSDs) can control backlight via acpi_video(4) */ void initUsingSysctl(); bool m_isSupported = false; QString m_dirname; QString m_sysctlDevice; QList m_sysctlBrightnessLevels; + + QVariantAnimation m_anim; }; #endif // BACKLIGHTHELPER_H diff --git a/daemon/backends/upower/powerdevilupowerbackend.cpp b/daemon/backends/upower/powerdevilupowerbackend.cpp index 7dac54b1..3622c73f 100644 --- a/daemon/backends/upower/powerdevilupowerbackend.cpp +++ b/daemon/backends/upower/powerdevilupowerbackend.cpp @@ -1,729 +1,753 @@ /* This file is part of the KDE project Copyright (C) 2006 Kevin Ottens Copyright (C) 2008-2010 Dario Freddi Copyright (C) 2010 Alejandro Fiestas Copyright (C) 2010-2013 Lukáš Tinkl Copyright (C) 2015 Kai Uwe Broulik This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "powerdevilupowerbackend.h" #include #include #include #include #include #include +#include -#include +#include +#include #include #include -#include #include "xrandrxcbhelper.h" #include "xrandrbrightness.h" #include "ddcutilbrightness.h" #include "upowersuspendjob.h" #include "login1suspendjob.h" #include "udevqt.h" #define HELPER_ID "org.kde.powerdevil.backlighthelper" PowerDevilUPowerBackend::PowerDevilUPowerBackend(QObject* parent) : BackendInterface(parent) , m_displayDevice(nullptr) , m_brightnessControl(nullptr) , m_randrHelper(nullptr) , m_upowerInterface(nullptr) , m_kbdBacklight(nullptr) , m_kbdMaxBrightness(0) , m_lidIsPresent(false) , m_lidIsClosed(false) , m_onBattery(false) , m_isLedBrightnessControl(false) { } PowerDevilUPowerBackend::~PowerDevilUPowerBackend() { delete m_brightnessControl; } bool PowerDevilUPowerBackend::isAvailable() { if (!QDBusConnection::systemBus().interface()->isServiceRegistered(UPOWER_SERVICE)) { // Is it pending activation? qCDebug(POWERDEVIL) << "UPower service, " << UPOWER_SERVICE << ", is not registered on the bus. Trying to find out if it is activated."; QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListActivatableNames"); QDBusPendingReply< QStringList > reply = QDBusConnection::systemBus().asyncCall(message); reply.waitForFinished(); if (reply.isValid()) { if (reply.value().contains(UPOWER_SERVICE)) { qCDebug(POWERDEVIL) << "UPower was found, activating service..."; QDBusConnection::systemBus().interface()->startService(UPOWER_SERVICE); if (!QDBusConnection::systemBus().interface()->isServiceRegistered(UPOWER_SERVICE)) { // Wait for it QEventLoop e; QTimer *timer = new QTimer; timer->setInterval(10000); timer->setSingleShot(true); connect(QDBusConnection::systemBus().interface(), SIGNAL(serviceRegistered(QString)), &e, SLOT(quit())); connect(timer, SIGNAL(timeout()), &e, SLOT(quit())); timer->start(); while (!QDBusConnection::systemBus().interface()->isServiceRegistered(UPOWER_SERVICE)) { e.exec(); if (!timer->isActive()) { qCDebug(POWERDEVIL) << "Activation of UPower timed out. There is likely a problem with your configuration."; timer->deleteLater(); return false; } } timer->deleteLater(); } return true; } else { qCDebug(POWERDEVIL) << "UPower cannot be found on this system."; return false; } } else { qCWarning(POWERDEVIL) << "Could not request activatable names to DBus!"; return false; } } else { return true; } } void PowerDevilUPowerBackend::init() { // interfaces if (!QDBusConnection::systemBus().interface()->isServiceRegistered(LOGIN1_SERVICE)) { // Activate it. QDBusConnection::systemBus().interface()->startService(LOGIN1_SERVICE); } if (!QDBusConnection::systemBus().interface()->isServiceRegistered(CONSOLEKIT2_SERVICE)) { // Activate it. QDBusConnection::systemBus().interface()->startService(CONSOLEKIT2_SERVICE); } if (!QDBusConnection::systemBus().interface()->isServiceRegistered(UPOWER_SERVICE)) { // Activate it. QDBusConnection::systemBus().interface()->startService(UPOWER_SERVICE); } if (QDBusConnection::systemBus().interface()->isServiceRegistered(LOGIN1_SERVICE)) { m_login1Interface = new QDBusInterface(LOGIN1_SERVICE, "/org/freedesktop/login1", "org.freedesktop.login1.Manager", QDBusConnection::systemBus(), this); } // if login1 isn't available, try using the same interface with ConsoleKit2 if (!m_login1Interface && QDBusConnection::systemBus().interface()->isServiceRegistered(CONSOLEKIT2_SERVICE)) { m_login1Interface = new QDBusInterface(CONSOLEKIT2_SERVICE, "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", QDBusConnection::systemBus(), this); } connect(this, &PowerDevilUPowerBackend::brightnessSupportQueried, this, &PowerDevilUPowerBackend::initWithBrightness); m_upowerInterface = new OrgFreedesktopUPowerInterface(UPOWER_SERVICE, "/org/freedesktop/UPower", QDBusConnection::systemBus(), this); m_brightnessControl = new XRandrBrightness(); if (!m_brightnessControl->isSupported()) { qCWarning(POWERDEVIL)<<"Xrandr not supported, trying ddc, helper"; m_ddcBrightnessControl = new DDCutilBrightness(); m_ddcBrightnessControl->detect(); if (!m_ddcBrightnessControl->isSupported()) { qCDebug(POWERDEVIL) << "Falling back to helper to get brightness"; KAuth::Action brightnessAction("org.kde.powerdevil.backlighthelper.brightness"); brightnessAction.setHelperId(HELPER_ID); KAuth::ExecuteJob *brightnessJob = brightnessAction.execute(); connect(brightnessJob, &KJob::result, this, [this, brightnessJob] { if (brightnessJob->error()) { qCWarning(POWERDEVIL) << "org.kde.powerdevil.backlighthelper.brightness failed"; qCDebug(POWERDEVIL) << brightnessJob->errorText(); Q_EMIT brightnessSupportQueried(false); return; } m_cachedBrightnessMap.insert(Screen, brightnessJob->data()["brightness"].toFloat()); KAuth::Action brightnessMaxAction("org.kde.powerdevil.backlighthelper.brightnessmax"); brightnessMaxAction.setHelperId(HELPER_ID); KAuth::ExecuteJob *brightnessMaxJob = brightnessMaxAction.execute(); connect(brightnessMaxJob, &KJob::result, this, [this, brightnessMaxJob] { if (brightnessMaxJob->error()) { qCWarning(POWERDEVIL) << "org.kde.powerdevil.backlighthelper.brightnessmax failed"; qCDebug(POWERDEVIL) << brightnessMaxJob->errorText(); } else { m_brightnessMax = brightnessMaxJob->data()["brightnessmax"].toInt(); } KAuth::Action syspathAction("org.kde.powerdevil.backlighthelper.syspath"); syspathAction.setHelperId(HELPER_ID); KAuth::ExecuteJob* syspathJob = syspathAction.execute(); connect(syspathJob, &KJob::result, this, [this, syspathJob] { if (syspathJob->error()) { qCWarning(POWERDEVIL) << "org.kde.powerdevil.backlighthelper.syspath failed"; qCDebug(POWERDEVIL) << syspathJob->errorText(); Q_EMIT brightnessSupportQueried(false); return; } m_syspath = syspathJob->data()["syspath"].toString(); m_syspath = QFileInfo(m_syspath).symLinkTarget(); m_isLedBrightnessControl = m_syspath.contains(QLatin1String("/leds/")); if (!m_isLedBrightnessControl) { UdevQt::Client *client = new UdevQt::Client(QStringList("backlight"), this); connect(client, SIGNAL(deviceChanged(UdevQt::Device)), SLOT(onDeviceChanged(UdevQt::Device))); } Q_EMIT brightnessSupportQueried(m_brightnessMax > 0); } ); syspathJob->start(); } ); brightnessMaxJob->start(); } ); brightnessJob->start(); } else{ qCDebug(POWERDEVIL) << "Using DDCutillib"; m_cachedBrightnessMap.insert(Screen, brightness(Screen)); const int duration = PowerDevilSettings::brightnessAnimationDuration(); if (duration > 0 && brightnessMax() >= PowerDevilSettings::brightnessAnimationThreshold()) { m_brightnessAnimation = new QPropertyAnimation(this); m_brightnessAnimation->setTargetObject(this); m_brightnessAnimation->setDuration(duration); - m_brightnessAnimation->setEasingCurve(QEasingCurve::InOutQuad); + m_brightnessAnimation->setEasingCurve(QEasingCurve::OutQuad); connect(m_brightnessAnimation, &QPropertyAnimation::valueChanged, this, &PowerDevilUPowerBackend::animationValueChanged); connect(m_brightnessAnimation, &QPropertyAnimation::finished, this, &PowerDevilUPowerBackend::slotScreenBrightnessChanged); } Q_EMIT brightnessSupportQueried(true); } } else { qCDebug(POWERDEVIL) << "Using XRandR"; m_randrHelper = XRandRXCBHelper::self(); Q_ASSERT(m_randrHelper); connect(m_randrHelper, &XRandRXCBHelper::brightnessChanged, this, &PowerDevilUPowerBackend::slotScreenBrightnessChanged); m_cachedBrightnessMap.insert(Screen, brightness(Screen)); const int duration = PowerDevilSettings::brightnessAnimationDuration(); if (duration > 0 && brightnessMax() >= PowerDevilSettings::brightnessAnimationThreshold()) { m_brightnessAnimation = new QPropertyAnimation(this); m_brightnessAnimation->setTargetObject(this); m_brightnessAnimation->setDuration(duration); m_brightnessAnimation->setEasingCurve(QEasingCurve::InOutQuad); connect(m_brightnessAnimation, &QPropertyAnimation::valueChanged, this, &PowerDevilUPowerBackend::animationValueChanged); connect(m_brightnessAnimation, &QPropertyAnimation::finished, this, &PowerDevilUPowerBackend::slotScreenBrightnessChanged); } Q_EMIT brightnessSupportQueried(true); } } void PowerDevilUPowerBackend::initWithBrightness(bool screenBrightnessAvailable) { disconnect(this, &PowerDevilUPowerBackend::brightnessSupportQueried, this, &PowerDevilUPowerBackend::initWithBrightness); // Capabilities setCapabilities(SignalResumeFromSuspend); // devices enumerateDevices(); connect(m_upowerInterface, SIGNAL(Changed()), this, SLOT(slotPropertyChanged())); // for UPower >= 0.99.0, missing Changed() signal QDBusConnection::systemBus().connect(UPOWER_SERVICE, UPOWER_PATH, "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList))); connect(m_upowerInterface, SIGNAL(DeviceAdded(QString)), this, SLOT(slotDeviceAdded(QString))); connect(m_upowerInterface, SIGNAL(DeviceRemoved(QString)), this, SLOT(slotDeviceRemoved(QString))); // for UPower >= 0.99.0, changed signature :o/ QDBusConnection::systemBus().connect(UPOWER_SERVICE, UPOWER_PATH, UPOWER_IFACE, "DeviceAdded", this, SLOT(slotDeviceAdded(QDBusObjectPath))); QDBusConnection::systemBus().connect(UPOWER_SERVICE, UPOWER_PATH, UPOWER_IFACE, "DeviceRemoved", this, SLOT(slotDeviceRemoved(QDBusObjectPath))); connect(m_upowerInterface, SIGNAL(DeviceChanged(QString)), this, SLOT(slotDeviceChanged(QString))); // for UPower >= 0.99.0, see slotDeviceAdded(const QString & device) // Brightness Controls available BrightnessControlsList controls; if (screenBrightnessAvailable) { controls.insert(QLatin1String("LVDS1"), Screen); qCDebug(POWERDEVIL) << "current screen brightness value: " << m_cachedBrightnessMap.value(Screen); } m_kbdBacklight = new OrgFreedesktopUPowerKbdBacklightInterface(UPOWER_SERVICE, "/org/freedesktop/UPower/KbdBacklight", QDBusConnection::systemBus(), this); if (m_kbdBacklight->isValid()) { // Cache max value QDBusPendingReply rep = m_kbdBacklight->GetMaxBrightness(); rep.waitForFinished(); if (rep.isValid()) { m_kbdMaxBrightness = rep.value(); } // TODO Do a proper check if the kbd backlight dbus object exists. But that should work for now .. if (m_kbdMaxBrightness) { controls.insert(QLatin1String("KBD"), Keyboard); m_cachedBrightnessMap.insert(Keyboard, brightness(Keyboard)); qCDebug(POWERDEVIL) << "current keyboard backlight brightness value: " << m_cachedBrightnessMap.value(Keyboard); connect(m_kbdBacklight, SIGNAL(BrightnessChanged(int)), this, SLOT(onKeyboardBrightnessChanged(int))); } } // Supported suspend methods SuspendMethods supported = UnknownSuspendMethod; if (m_login1Interface) { QDBusPendingReply canSuspend = m_login1Interface.data()->asyncCall("CanSuspend"); canSuspend.waitForFinished(); if (canSuspend.isValid() && (canSuspend.value() == QLatin1String("yes") || canSuspend.value() == QLatin1String("challenge"))) supported |= ToRam; QDBusPendingReply canHibernate = m_login1Interface.data()->asyncCall("CanHibernate"); canHibernate.waitForFinished(); if (canHibernate.isValid() && (canHibernate.value() == QLatin1String("yes") || canHibernate.value() == QLatin1String("challenge"))) supported |= ToDisk; QDBusPendingReply canHybridSleep = m_login1Interface.data()->asyncCall("CanHybridSleep"); canHybridSleep.waitForFinished(); if (canHybridSleep.isValid() && (canHybridSleep.value() == QLatin1String("yes") || canHybridSleep.value() == QLatin1String("challenge"))) supported |= HybridSuspend; QDBusPendingReply canSuspendThenHibernate = m_login1Interface.data()->asyncCall("CanSuspendThenHibernate"); canSuspendThenHibernate.waitForFinished(); if (canSuspendThenHibernate.isValid() && (canSuspendThenHibernate.value() == QLatin1String("yes") || canSuspendThenHibernate.value() == QLatin1String("challenge"))) supported |= SuspendThenHibernate; } /* There's a chance we're using ConsoleKit rather than ConsoleKit2 as the * m_login1Interface, so check if we can suspend/hibernate with UPower < 0.99 */ if (supported == UnknownSuspendMethod) { if (m_upowerInterface->canSuspend() && m_upowerInterface->SuspendAllowed()) { qCDebug(POWERDEVIL) << "Can suspend"; supported |= ToRam; } if (m_upowerInterface->canHibernate() && m_upowerInterface->HibernateAllowed()) { qCDebug(POWERDEVIL) << "Can hibernate"; supported |= ToDisk; } if (supported != UnknownSuspendMethod) { m_useUPowerSuspend = true; } } // "resuming" signal if (m_login1Interface && !m_useUPowerSuspend) { connect(m_login1Interface.data(), SIGNAL(PrepareForSleep(bool)), this, SLOT(slotLogin1PrepareForSleep(bool))); } else { connect(m_upowerInterface, &OrgFreedesktopUPowerInterface::Sleeping, this, &PowerDevilUPowerBackend::aboutToSuspend); connect(m_upowerInterface, &OrgFreedesktopUPowerInterface::Resuming, this, &PowerDevilUPowerBackend::resumeFromSuspend); } // battery for (const OrgFreedesktopUPowerDeviceInterface * upowerDevice : qAsConst(m_devices)) { if (upowerDevice->type() == 2 && upowerDevice->powerSupply()) { QString udi = upowerDevice->path(); setCapacityForBattery(udi, qRound(upowerDevice->capacity())); // acknowledge capacity } } // backend ready setBackendIsReady(controls, supported); } void PowerDevilUPowerBackend::onDeviceChanged(const UdevQt::Device &device) { + // If we're currently in the process of changing brightness, ignore any such events + if (m_brightnessAnimationTimer && m_brightnessAnimationTimer->isActive()) { + return; + } + qCDebug(POWERDEVIL) << "Udev device changed" << m_syspath << device.sysfsPath(); if (device.sysfsPath() != m_syspath) { return; } int maxBrightness = device.sysfsProperty("max_brightness").toInt(); if (maxBrightness <= 0) { return; } int newBrightness = device.sysfsProperty("brightness").toInt(); if (newBrightness != m_cachedBrightnessMap[Screen]) { m_cachedBrightnessMap[Screen] = newBrightness; onBrightnessChanged(Screen, newBrightness, maxBrightness); } } int PowerDevilUPowerBackend::brightnessKeyPressed(PowerDevil::BrightnessLogic::BrightnessKeyType type, BrightnessControlType controlType) { BrightnessControlsList allControls = brightnessControlsAvailable(); QList controls = allControls.keys(controlType); if (controls.isEmpty()) { return -1; // ignore as we are not able to determine the brightness level } int currentBrightness = brightness(controlType); // m_cachedBrightnessMap is not being updated during animation, thus checking the m_cachedBrightnessMap // value here doesn't make much sense, use the endValue from brightness() anyway. // This prevents brightness key being ignored during the animation. if (!(controlType == Screen && m_brightnessAnimation && m_brightnessAnimation->state() == QPropertyAnimation::Running) && currentBrightness != m_cachedBrightnessMap.value(controlType)) { m_cachedBrightnessMap[controlType] = currentBrightness; return currentBrightness; } int maxBrightness = brightnessMax(controlType); int newBrightness = calculateNextStep(currentBrightness, maxBrightness, controlType, type); if (newBrightness < 0) { return -1; } setBrightness(newBrightness, controlType); return newBrightness; } int PowerDevilUPowerBackend::brightness(PowerDevil::BackendInterface::BrightnessControlType type) const { int result = 0; if (type == Screen) { if (m_brightnessControl->isSupported()) { if (m_brightnessAnimation && m_brightnessAnimation->state() == QPropertyAnimation::Running) { result = m_brightnessAnimation->endValue().toInt(); } else { //qCDebug(POWERDEVIL) << "Calling xrandr brightness"; result = (int) m_brightnessControl->brightness(); } } else if (m_ddcBrightnessControl->isSupported()){ if (m_brightnessAnimation && m_brightnessAnimation->state() == QPropertyAnimation::Running) { result = m_brightnessAnimation->endValue().toInt(); } else { result = (int)m_ddcBrightnessControl->brightness(); } }else{ result = m_cachedBrightnessMap[Screen]; } qCDebug(POWERDEVIL) << "Screen brightness value: " << result; } else if (type == Keyboard) { result = m_kbdBacklight->GetBrightness(); qCDebug(POWERDEVIL) << "Kbd backlight brightness value: " << result; } return result; } int PowerDevilUPowerBackend::brightnessMax(PowerDevil::BackendInterface::BrightnessControlType type) const { int result = 0; if (type == Screen) { if (m_brightnessControl->isSupported()) { //qCDebug(POWERDEVIL) << "Calling xrandr brightness"; result = (int) m_brightnessControl->brightnessMax(); } else if (m_ddcBrightnessControl->isSupported()){ result = (int)m_ddcBrightnessControl->brightnessMax(); }else{ result = m_brightnessMax; } qCDebug(POWERDEVIL) << "Screen brightness value max: " << result; } else if (type == Keyboard) { result = m_kbdMaxBrightness; qCDebug(POWERDEVIL) << "Kbd backlight brightness value max: " << result; } return result; } void PowerDevilUPowerBackend::setBrightness(int value, PowerDevil::BackendInterface::BrightnessControlType type) { if (type == Screen) { qCDebug(POWERDEVIL) << "set screen brightness value: " << value; if (m_brightnessControl->isSupported()) { if (m_brightnessAnimation) { m_brightnessAnimation->stop(); disconnect(m_brightnessAnimation, &QPropertyAnimation::valueChanged, this, &PowerDevilUPowerBackend::animationValueChanged); m_brightnessAnimation->setStartValue(brightness()); m_brightnessAnimation->setEndValue(value); connect(m_brightnessAnimation, &QPropertyAnimation::valueChanged, this, &PowerDevilUPowerBackend::animationValueChanged); m_brightnessAnimation->start(); } else { m_brightnessControl->setBrightness(value); } } else if (m_ddcBrightnessControl->isSupported()){ if (m_brightnessAnimation) { m_brightnessAnimation->stop(); disconnect(m_brightnessAnimation, &QPropertyAnimation::valueChanged, this, &PowerDevilUPowerBackend::animationValueChanged); m_brightnessAnimation->setStartValue(brightness()); m_brightnessAnimation->setEndValue(value); connect(m_brightnessAnimation, &QPropertyAnimation::valueChanged, this, &PowerDevilUPowerBackend::animationValueChanged); m_brightnessAnimation->start(); } else { m_ddcBrightnessControl->setBrightness((long)value); } } else { //qCDebug(POWERDEVIL) << "Falling back to helper to set brightness"; + KAuth::Action action("org.kde.powerdevil.backlighthelper.setbrightness"); action.setHelperId(HELPER_ID); action.addArgument("brightness", value); - KAuth::ExecuteJob *job = action.execute(); - // we don't care about the result since executing the job sync is bad - job->start(); - if (m_isLedBrightnessControl) { + action.addArgument("animationDuration", PowerDevilSettings::brightnessAnimationDuration()); + auto *job = action.execute(); + connect(job, &KAuth::ExecuteJob::result, this, [this, job, value] { + if (job->error()) { + qCWarning(POWERDEVIL) << "Failed to set screen brightness" << job->errorText(); + return; + } + + // Immediately announce the new brightness to everyone while we still animate to it m_cachedBrightnessMap[Screen] = value; - slotScreenBrightnessChanged(); - } + onBrightnessChanged(Screen, value, brightnessMax(Screen)); + + // So we ignore any brightness changes during the animation + if (!m_brightnessAnimationTimer) { + m_brightnessAnimationTimer = new QTimer(this); + m_brightnessAnimationTimer->setSingleShot(true); + } + m_brightnessAnimationTimer->start(PowerDevilSettings::brightnessAnimationDuration()); + }); + job->start(); } } else if (type == Keyboard) { qCDebug(POWERDEVIL) << "set kbd backlight value: " << value; m_kbdBacklight->SetBrightness(value); } } void PowerDevilUPowerBackend::slotScreenBrightnessChanged() { if (m_brightnessAnimation && m_brightnessAnimation->state() != QPropertyAnimation::Stopped) { return; } + if (m_brightnessAnimationTimer && m_brightnessAnimationTimer->isActive()) { + return; + } + int value = brightness(Screen); if (value != m_cachedBrightnessMap[Screen] || m_isLedBrightnessControl) { m_cachedBrightnessMap[Screen] = value; onBrightnessChanged(Screen, value, brightnessMax(Screen)); } } void PowerDevilUPowerBackend::onKeyboardBrightnessChanged(int value) { qCDebug(POWERDEVIL) << "Keyboard brightness changed!!"; if (value != m_cachedBrightnessMap[Keyboard]) { m_cachedBrightnessMap[Keyboard] = value; onBrightnessChanged(Keyboard, value, brightnessMax(Keyboard)); } } KJob* PowerDevilUPowerBackend::suspend(PowerDevil::BackendInterface::SuspendMethod method) { if (m_login1Interface && !m_useUPowerSuspend) { return new Login1SuspendJob(m_login1Interface.data(), method, supportedSuspendMethods()); } else { return new UPowerSuspendJob(m_upowerInterface, method, supportedSuspendMethods()); } } void PowerDevilUPowerBackend::enumerateDevices() { m_lidIsPresent = m_upowerInterface->lidIsPresent(); setLidPresent(m_lidIsPresent); m_lidIsClosed = m_upowerInterface->lidIsClosed(); m_onBattery = m_upowerInterface->onBattery(); const QList deviceList = m_upowerInterface->EnumerateDevices(); for (const QDBusObjectPath & device : deviceList) { addDevice(device.path()); } QDBusReply reply = m_upowerInterface->call("GetDisplayDevice"); if (reply.isValid()) { const QString path = reply.value().path(); if (!path.isEmpty() && path != QStringLiteral("/")) { m_displayDevice = new OrgFreedesktopUPowerDeviceInterface(UPOWER_SERVICE, path, QDBusConnection::systemBus(), this); } } updateDeviceProps(); if (m_onBattery) setAcAdapterState(Unplugged); else setAcAdapterState(Plugged); } void PowerDevilUPowerBackend::addDevice(const QString & device) { OrgFreedesktopUPowerDeviceInterface * upowerDevice = new OrgFreedesktopUPowerDeviceInterface(UPOWER_SERVICE, device, QDBusConnection::systemBus(), this); m_devices.insert(device, upowerDevice); // for UPower >= 0.99.0 which doesn't emit the DeviceChanged(QString) signal QDBusConnection::systemBus().connect(UPOWER_SERVICE, device, "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(onDevicePropertiesChanged(QString,QVariantMap,QStringList))); } void PowerDevilUPowerBackend::slotDeviceAdded(const QString & device) { addDevice(device); updateDeviceProps(); } void PowerDevilUPowerBackend::slotDeviceRemoved(const QString & device) { OrgFreedesktopUPowerDeviceInterface * upowerDevice = m_devices.take(device); delete upowerDevice; updateDeviceProps(); } void PowerDevilUPowerBackend::slotDeviceAdded(const QDBusObjectPath &path) { slotDeviceAdded(path.path()); } void PowerDevilUPowerBackend::slotDeviceRemoved(const QDBusObjectPath &path) { slotDeviceRemoved(path.path()); } void PowerDevilUPowerBackend::slotDeviceChanged(const QString & /*device*/) { updateDeviceProps(); } void PowerDevilUPowerBackend::updateDeviceProps() { qlonglong remainingTime = 0; if (m_displayDevice && m_displayDevice->isPresent()) { const uint state = m_displayDevice->state(); if (state == 1) // charging remainingTime = m_displayDevice->timeToFull(); else if (state == 2) //discharging remainingTime = m_displayDevice->timeToEmpty(); } else { qreal energyTotal = 0.0; qreal energyRateTotal = 0.0; qreal energyFullTotal = 0.0; uint stateTotal = 0; for (const OrgFreedesktopUPowerDeviceInterface * upowerDevice : qAsConst(m_devices)) { const uint type = upowerDevice->type(); if (( type == 2 || type == 3) && upowerDevice->powerSupply()) { const uint state = upowerDevice->state(); energyFullTotal += upowerDevice->energyFull(); energyTotal += upowerDevice->energy(); energyRateTotal += upowerDevice->energyRate(); if (state == 1) { // total is charging stateTotal = 1; } else if (state == 2 && stateTotal != 1) { // total is discharging stateTotal = 2; } else if (state == 4 && stateTotal != 0) { // total is fully-charged stateTotal = 4; } if (state == 1) { // charging remainingTime += upowerDevice->timeToFull(); } else if (state == 2) { // discharging remainingTime += upowerDevice->timeToEmpty(); } } } if (energyRateTotal > 0) { if (stateTotal == 1) { // charging remainingTime = 3600 * ((energyFullTotal - energyTotal) / energyRateTotal); } else if (stateTotal == 2) { // discharging remainingTime = 3600 * (energyTotal / energyRateTotal); } } } setBatteryRemainingTime(remainingTime * 1000); } void PowerDevilUPowerBackend::slotPropertyChanged() { // check for lid button changes if (m_lidIsPresent) { const bool lidIsClosed = m_upowerInterface->lidIsClosed(); if (lidIsClosed != m_lidIsClosed) { if (lidIsClosed) setButtonPressed(LidClose); else setButtonPressed(LidOpen); } m_lidIsClosed = lidIsClosed; } // check for AC adapter changes const bool onBattery = m_upowerInterface->onBattery(); if (m_onBattery != onBattery) { if (onBattery) setAcAdapterState(Unplugged); else setAcAdapterState(Plugged); } m_onBattery = onBattery; } void PowerDevilUPowerBackend::onPropertiesChanged(const QString &ifaceName, const QVariantMap &changedProps, const QStringList &invalidatedProps) { Q_UNUSED(changedProps); Q_UNUSED(invalidatedProps); if (ifaceName == UPOWER_IFACE) { slotPropertyChanged(); // TODO maybe process the 2 properties separately? } } void PowerDevilUPowerBackend::onDevicePropertiesChanged(const QString &ifaceName, const QVariantMap &changedProps, const QStringList &invalidatedProps) { Q_UNUSED(changedProps); Q_UNUSED(invalidatedProps); if (ifaceName == UPOWER_IFACE_DEVICE) { updateDeviceProps(); // TODO maybe process the properties separately? } } void PowerDevilUPowerBackend::slotLogin1PrepareForSleep(bool active) { if (active) { Q_EMIT aboutToSuspend(); } else { Q_EMIT resumeFromSuspend(); } } void PowerDevilUPowerBackend::animationValueChanged(const QVariant &value) { if (m_brightnessControl->isSupported()) { m_brightnessControl->setBrightness(value.toInt()); }else if (m_ddcBrightnessControl->isSupported()) { m_ddcBrightnessControl->setBrightness(value.toInt()); } else{ qCInfo(POWERDEVIL)<<"PowerDevilUPowerBackend::animationValueChanged: brightness control not supported"; } } diff --git a/daemon/backends/upower/powerdevilupowerbackend.h b/daemon/backends/upower/powerdevilupowerbackend.h index 299009d8..0dd9f72b 100644 --- a/daemon/backends/upower/powerdevilupowerbackend.h +++ b/daemon/backends/upower/powerdevilupowerbackend.h @@ -1,128 +1,131 @@ /* This file is part of the KDE project Copyright (C) 2006 Kevin Ottens Copyright (C) 2008-2010 Dario Freddi Copyright (C) 2010 Alejandro Fiestas Copyright (C) 2015 Kai Uwe Broulik This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef POWERDEVILUPOWERBACKEND_H #define POWERDEVILUPOWERBACKEND_H #include #include #include #include "upower_device_interface.h" #include "upower_interface.h" #include "upower_kbdbacklight_interface.h" #include "udevqt.h" #define UPOWER_SERVICE "org.freedesktop.UPower" #define UPOWER_PATH "/org/freedesktop/UPower" #define UPOWER_IFACE "org.freedesktop.UPower" #define UPOWER_IFACE_DEVICE "org.freedesktop.UPower.Device" #define LOGIN1_SERVICE "org.freedesktop.login1" #define CONSOLEKIT2_SERVICE "org.freedesktop.ConsoleKit" class XRandRXCBHelper; class XRandrBrightness; class QPropertyAnimation; +class QTimer; class DDCutilBrightness; class Q_DECL_EXPORT PowerDevilUPowerBackend : public PowerDevil::BackendInterface { Q_OBJECT Q_DISABLE_COPY(PowerDevilUPowerBackend) Q_PLUGIN_METADATA(IID "org.kde.powerdevil.upowerbackend"); public: explicit PowerDevilUPowerBackend(QObject* parent = nullptr); ~PowerDevilUPowerBackend() override; void init() override; static bool isAvailable(); int brightness(BrightnessControlType type = Screen) const override; int brightnessMax(BrightnessControlType type = Screen) const override; int brightnessKeyPressed(PowerDevil::BrightnessLogic::BrightnessKeyType type, BrightnessControlType controlType) override; void setBrightness(int value, PowerDevil::BackendInterface::BrightnessControlType type = Screen) override; KJob* suspend(PowerDevil::BackendInterface::SuspendMethod method) override; Q_SIGNALS: void brightnessSupportQueried(bool available); private: void enumerateDevices(); void addDevice(const QString &); private Q_SLOTS: void updateDeviceProps(); void slotDeviceAdded(const QString &); void slotDeviceRemoved(const QString &); void slotDeviceAdded(const QDBusObjectPath & path); void slotDeviceRemoved(const QDBusObjectPath & path); void slotDeviceChanged(const QString &); void slotPropertyChanged(); void slotLogin1PrepareForSleep(bool active); void slotScreenBrightnessChanged(); void onDeviceChanged(const UdevQt::Device &device); void onKeyboardBrightnessChanged(int); void onPropertiesChanged(const QString &ifaceName, const QVariantMap &changedProps, const QStringList &invalidatedProps); void onDevicePropertiesChanged(const QString &ifaceName, const QVariantMap &changedProps, const QStringList &invalidatedProps); private: void animationValueChanged(const QVariant &value); void initWithBrightness(bool brightnessSupport); // upower devices QMap m_devices; OrgFreedesktopUPowerDeviceInterface *m_displayDevice; // brightness QMap m_cachedBrightnessMap; XRandrBrightness *m_brightnessControl; XRandRXCBHelper *m_randrHelper; DDCutilBrightness *m_ddcBrightnessControl; OrgFreedesktopUPowerInterface *m_upowerInterface; OrgFreedesktopUPowerKbdBacklightInterface *m_kbdBacklight; int m_kbdMaxBrightness; int m_brightnessMax = 0; QPropertyAnimation *m_brightnessAnimation = nullptr; + QTimer *m_brightnessAnimationTimer = nullptr; + // login1 interface QPointer m_login1Interface; bool m_useUPowerSuspend = false; // buttons bool m_lidIsPresent; bool m_lidIsClosed; bool m_onBattery; // property if brightness control is leds subsystem bool m_isLedBrightnessControl; //helper path QString m_syspath; }; #endif // POWERDEVILUPOWERBACKEND_H