File indexing completed on 2024-05-12 05:38:43

0001 /*  This file is part of the KDE project
0002     SPDX-FileCopyrightText: 2006 Kevin Ottens <ervin@kde.org>
0003     SPDX-FileCopyrightText: 2008-2010 Dario Freddi <drf@kde.org>
0004     SPDX-FileCopyrightText: 2010 Alejandro Fiestas <alex@eyeos.org>
0005     SPDX-FileCopyrightText: 2010-2013 Lukáš Tinkl <ltinkl@redhat.com>
0006     SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de>
0007     SPDX-FileCopyrightText: 2023 Jakob Petsovits <jpetso@petsovits.com>
0008 
0009     SPDX-License-Identifier: LGPL-2.0-only
0010 */
0011 
0012 #include "backlightbrightness.h"
0013 
0014 #include <powerdevil_debug.h>
0015 
0016 #include <QDebug>
0017 #include <QFileInfo>
0018 #include <QTimer>
0019 
0020 #include <KAuth/Action>
0021 #include <KAuth/ActionReply>
0022 #include <KAuth/ExecuteJob>
0023 #include <KAuth/HelperSupport>
0024 
0025 #include "udevqt.h"
0026 
0027 #define HELPER_ID "org.kde.powerdevil.backlighthelper"
0028 
0029 BacklightBrightness::BacklightBrightness(QObject *parent)
0030     : QObject(parent)
0031 {
0032 }
0033 
0034 void BacklightBrightness::detect()
0035 {
0036     KAuth::Action brightnessAction("org.kde.powerdevil.backlighthelper.brightness");
0037     brightnessAction.setHelperId(HELPER_ID);
0038     KAuth::ExecuteJob *brightnessJob = brightnessAction.execute();
0039     connect(brightnessJob, &KJob::result, this, [this, brightnessJob] {
0040         if (brightnessJob->error()) {
0041             qCWarning(POWERDEVIL) << "org.kde.powerdevil.backlighthelper.brightness failed";
0042             qCDebug(POWERDEVIL) << brightnessJob->errorText();
0043             Q_EMIT detectionFinished(isSupported());
0044             return;
0045         }
0046         m_cachedBrightness = brightnessJob->data()["brightness"].toFloat();
0047 
0048         KAuth::Action brightnessMaxAction("org.kde.powerdevil.backlighthelper.brightnessmax");
0049         brightnessMaxAction.setHelperId(HELPER_ID);
0050         KAuth::ExecuteJob *brightnessMaxJob = brightnessMaxAction.execute();
0051         connect(brightnessMaxJob, &KJob::result, this, [this, brightnessMaxJob] {
0052             if (brightnessMaxJob->error()) {
0053                 qCWarning(POWERDEVIL) << "org.kde.powerdevil.backlighthelper.brightnessmax failed";
0054                 qCDebug(POWERDEVIL) << brightnessMaxJob->errorText();
0055             } else {
0056                 m_maxBrightness = brightnessMaxJob->data()["brightnessmax"].toInt();
0057             }
0058 
0059 #ifdef Q_OS_FREEBSD
0060             // FreeBSD doesn't have the sysfs interface that the bits below expect;
0061             // the sysfs calls always fail, leading to detectionFinished(false) emission.
0062             // Skip that command and carry on with the information that we do have.
0063             Q_EMIT detectionFinished(isSupported());
0064 #else
0065             KAuth::Action syspathAction("org.kde.powerdevil.backlighthelper.syspath");
0066             syspathAction.setHelperId(HELPER_ID);
0067             KAuth::ExecuteJob* syspathJob = syspathAction.execute();
0068             connect(syspathJob, &KJob::result, this, [this, syspathJob] {
0069                 if (syspathJob->error()) {
0070                     qCWarning(POWERDEVIL) << "org.kde.powerdevil.backlighthelper.syspath failed";
0071                     qCDebug(POWERDEVIL) << syspathJob->errorText();
0072                     m_maxBrightness = 0; // i.e. isSupported() == false
0073                     Q_EMIT detectionFinished(isSupported());
0074                     return;
0075                 }
0076                 m_syspath = syspathJob->data()["syspath"].toString();
0077                 m_syspath = QFileInfo(m_syspath).symLinkTarget();
0078 
0079                 // Kernel doesn't send uevent for leds-class devices, or at least that's what
0080                 // commit 26a48f9db claimed (although the monitored subsystem was already hardcoded
0081                 // to "backlight" then). That's okay because we emit brightnessChanged() at least
0082                 // once when setBrightness() starts successfully.
0083                 if (!m_syspath.contains(QLatin1String("/leds/"))) {
0084                     UdevQt::Client *client =  new UdevQt::Client(QStringList("backlight"), this);
0085                     connect(client, &UdevQt::Client::deviceChanged, this, &BacklightBrightness::onDeviceChanged);
0086                 }
0087 
0088                 Q_EMIT detectionFinished(isSupported());
0089             });
0090             syspathJob->start();
0091 #endif
0092         });
0093         brightnessMaxJob->start();
0094     });
0095     brightnessJob->start();
0096 }
0097 
0098 bool BacklightBrightness::isSupported() const
0099 {
0100     return m_maxBrightness > 0;
0101 }
0102 
0103 void BacklightBrightness::onDeviceChanged(const UdevQt::Device &device)
0104 {
0105     // If we're currently in the process of changing brightness, ignore any such events
0106     if (m_brightnessAnimationTimer && m_brightnessAnimationTimer->isActive()) {
0107         return;
0108     }
0109 
0110     qCDebug(POWERDEVIL) << "Udev device changed" << m_syspath << device.sysfsPath();
0111     if (device.sysfsPath() != m_syspath) {
0112         return;
0113     }
0114 
0115     int maxBrightness = device.sysfsProperty("max_brightness").toInt();
0116     if (maxBrightness <= 0) {
0117         return;
0118     }
0119     int newBrightness = device.sysfsProperty("brightness").toInt();
0120 
0121     if (newBrightness != m_cachedBrightness) {
0122         m_cachedBrightness = newBrightness;
0123         Q_EMIT brightnessChanged(newBrightness, maxBrightness);
0124     }
0125 }
0126 
0127 int BacklightBrightness::maxBrightness() const
0128 {
0129     return m_maxBrightness;
0130 }
0131 
0132 int BacklightBrightness::brightness() const
0133 {
0134     return m_cachedBrightness;
0135 }
0136 
0137 void BacklightBrightness::setBrightness(int newBrightness, int animationDurationMsec)
0138 {
0139     if (!isSupported()) {
0140         qCWarning(POWERDEVIL) << "backlight not supported, setBrightness() should not be called";
0141         return;
0142     }
0143 
0144     KAuth::Action action("org.kde.powerdevil.backlighthelper.setbrightness");
0145     action.setHelperId(HELPER_ID);
0146     action.addArgument("brightness", newBrightness);
0147     if (brightness() >= m_brightnessAnimationThreshold) {
0148         action.addArgument("animationDuration", animationDurationMsec);
0149     }
0150     auto *job = action.execute();
0151 
0152     connect(job, &KAuth::ExecuteJob::result, this, [this, job, newBrightness, animationDurationMsec] {
0153         if (job->error()) {
0154             qCWarning(POWERDEVIL) << "Failed to set screen brightness" << job->errorText();
0155             return;
0156         }
0157 
0158         // So we ignore any brightness changes during the animation
0159         if (!m_brightnessAnimationTimer) {
0160             m_brightnessAnimationTimer = new QTimer(this);
0161             m_brightnessAnimationTimer->setSingleShot(true);
0162         }
0163         m_brightnessAnimationTimer->start(animationDurationMsec);
0164 
0165         // Immediately announce the new brightness to everyone while we still animate to it
0166         m_cachedBrightness = newBrightness;
0167         Q_EMIT brightnessChanged(newBrightness, maxBrightness());
0168     });
0169     job->start();
0170 }
0171 
0172 #include "moc_backlightbrightness.cpp"