File indexing completed on 2024-05-12 05:38:44
0001 /* This file is part of the KDE project 0002 * SPDX-FileCopyrightText: 2010-2011 Lukas Tinkl <ltinkl@redhat.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-only 0005 * 0006 */ 0007 0008 #include "backlighthelper_linux.h" 0009 0010 #include <powerdevil_debug.h> 0011 0012 #include <QDebug> 0013 #include <QDir> 0014 0015 #include <KLocalizedString> 0016 0017 #include <algorithm> 0018 #include <climits> 0019 #include <sys/utsname.h> 0020 0021 #define BACKLIGHT_SYSFS_PATH "/sys/class/backlight/" 0022 #define LED_SYSFS_PATH "/sys/class/leds/" 0023 0024 BacklightHelper::BacklightHelper(QObject *parent) 0025 : QObject(parent) 0026 { 0027 init(); 0028 } 0029 0030 void BacklightHelper::init() 0031 { 0032 initUsingBacklightType(); 0033 0034 if (m_devices.isEmpty()) { 0035 qCWarning(POWERDEVIL) << "no kernel backlight interface found"; 0036 return; 0037 } 0038 0039 m_anim.setEasingCurve(QEasingCurve::InOutQuad); 0040 connect(&m_anim, &QVariantAnimation::valueChanged, this, [this](const QVariant &value) { 0041 // When animating to zero, it emits a value change to 0 before starting the animation... 0042 if (m_anim.state() == QAbstractAnimation::Running) { 0043 writeBrightness(value.toInt()); 0044 } 0045 }); 0046 0047 m_isSupported = true; 0048 } 0049 0050 int BacklightHelper::readFromDevice(const QString &device, const QString &property) const 0051 { 0052 int value = -1; 0053 0054 QFile file(device + "/" + property); 0055 if (!file.open(QIODevice::ReadOnly)) { 0056 qCWarning(POWERDEVIL) << "reading from device " << device << "/" << property << " failed with error code " << file.error() << file.errorString(); 0057 return value; 0058 } 0059 0060 QTextStream stream(&file); 0061 stream >> value; 0062 file.close(); 0063 0064 return value; 0065 } 0066 0067 bool BacklightHelper::writeToDevice(const QString &device, int brightness) const 0068 { 0069 QFile file(device + QLatin1String("/brightness")); 0070 if (!file.open(QIODevice::WriteOnly)) { 0071 qCWarning(POWERDEVIL) << "writing to device " << device << "/brightness failed with error code " << file.error() << file.errorString(); 0072 return false; 0073 } 0074 0075 const int bytesWritten = file.write(QByteArray::number(brightness)); 0076 if (bytesWritten == -1) { 0077 qCWarning(POWERDEVIL) << "writing to device " << device << "/brightness failed with error code " << file.error() << file.errorString(); 0078 return false; 0079 } 0080 0081 return true; 0082 } 0083 0084 QStringList BacklightHelper::getBacklightTypeDevices() const 0085 { 0086 QDir ledsDir(LED_SYSFS_PATH); 0087 ledsDir.setFilter(QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::NoDotAndDotDot | QDir::Readable); 0088 ledsDir.setNameFilters({QStringLiteral("*lcd*"), QStringLiteral("*wled*")}); 0089 0090 QStringList ledInterfaces = ledsDir.entryList(); 0091 0092 if (!ledInterfaces.isEmpty()) { 0093 QStringList output; 0094 for (const QString &interface : ledInterfaces) { 0095 output.append(LED_SYSFS_PATH + interface); 0096 } 0097 return output; 0098 } 0099 0100 QDir backlightDir(BACKLIGHT_SYSFS_PATH); 0101 backlightDir.setFilter(QDir::AllDirs | QDir::NoDot | QDir::NoDotDot | QDir::NoDotAndDotDot | QDir::Readable); 0102 backlightDir.setSorting(QDir::Name | QDir::Reversed); // Reverse is needed to priorize acpi_video1 over 0 0103 0104 const QStringList interfaces = backlightDir.entryList(); 0105 0106 QFile file; 0107 QByteArray buffer; 0108 QStringList firmware, platform, rawEnabled, rawAll; 0109 0110 for (const QString &interface : interfaces) { 0111 file.setFileName(BACKLIGHT_SYSFS_PATH + interface + "/type"); 0112 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { 0113 continue; 0114 } 0115 0116 buffer = file.readLine().trimmed(); 0117 if (buffer == "firmware") { 0118 firmware.append(BACKLIGHT_SYSFS_PATH + interface); 0119 } else if (buffer == "platform") { 0120 platform.append(BACKLIGHT_SYSFS_PATH + interface); 0121 } else if (buffer == "raw") { 0122 QFile enabled(BACKLIGHT_SYSFS_PATH + interface + "/device/enabled"); 0123 rawAll.append(BACKLIGHT_SYSFS_PATH + interface); 0124 if (enabled.open(QIODevice::ReadOnly | QIODevice::Text) && enabled.readLine().trimmed() == "enabled") { 0125 // this backlight device is connected to a display, so append 0126 // it to rawEnabled list 0127 rawEnabled.append(BACKLIGHT_SYSFS_PATH + interface); 0128 } 0129 } else { 0130 qCWarning(POWERDEVIL) << "Interface type not handled" << buffer; 0131 } 0132 0133 file.close(); 0134 } 0135 0136 if (!firmware.isEmpty()) 0137 return firmware; 0138 0139 if (!platform.isEmpty()) 0140 return platform; 0141 0142 if (!rawEnabled.isEmpty()) 0143 return rawEnabled; 0144 0145 if (!rawAll.isEmpty()) 0146 return rawAll; 0147 0148 return {}; 0149 } 0150 0151 void BacklightHelper::initUsingBacklightType() 0152 { 0153 m_devices.clear(); 0154 QStringList devices = getBacklightTypeDevices(); 0155 0156 for (const QString &interface : devices) { 0157 int max_brightness = readFromDevice(interface, "max_brightness"); 0158 m_devices.append(qMakePair(interface, max_brightness)); 0159 } 0160 0161 return; 0162 } 0163 0164 ActionReply BacklightHelper::brightness(const QVariantMap &args) 0165 { 0166 Q_UNUSED(args); 0167 const int brightness = readBrightness(); 0168 0169 if (brightness == -1) { 0170 return ActionReply::HelperErrorReply(); 0171 } 0172 0173 ActionReply reply; 0174 reply.addData(QStringLiteral("brightness"), brightness); 0175 return reply; 0176 } 0177 0178 int BacklightHelper::readBrightness() const 0179 { 0180 if (!m_isSupported) { 0181 return -1; 0182 } 0183 0184 return readFromDevice(m_devices.constFirst().first, QLatin1String("brightness")); 0185 } 0186 0187 ActionReply BacklightHelper::setbrightness(const QVariantMap &args) 0188 { 0189 if (!m_isSupported) { 0190 return ActionReply::HelperErrorReply(); 0191 } 0192 0193 const int brightness = args.value(QStringLiteral("brightness")).toInt(); 0194 const int animationDuration = args.value(QStringLiteral("animationDuration")).toInt(); 0195 0196 m_anim.stop(); 0197 0198 if (animationDuration <= 0) { 0199 writeBrightness(brightness); 0200 return ActionReply::SuccessReply(); 0201 } 0202 0203 m_anim.setDuration(animationDuration); 0204 m_anim.setStartValue(readBrightness()); 0205 m_anim.setEndValue(brightness); 0206 m_anim.start(); 0207 0208 return ActionReply::SuccessReply(); 0209 } 0210 0211 bool BacklightHelper::writeBrightness(int brightness) const 0212 { 0213 if (!m_devices.isEmpty()) { 0214 const int first_maxbrightness = std::max(1, m_devices.constFirst().second); 0215 for (const auto &device : m_devices) { 0216 // Some monitor brightness values are ridiculously high, and can easily overflow during computation 0217 const qint64 new_brightness_64 = static_cast<qint64>(brightness) * static_cast<qint64>(device.second) / static_cast<qint64>(first_maxbrightness); 0218 // cautiously truncate it back 0219 const int new_brightness = static_cast<int>(std::min(static_cast<qint64>(std::numeric_limits<int>::max()), new_brightness_64)); 0220 writeToDevice(device.first, new_brightness); 0221 } 0222 } 0223 0224 return true; 0225 } 0226 0227 ActionReply BacklightHelper::syspath(const QVariantMap &args) 0228 { 0229 Q_UNUSED(args); 0230 0231 ActionReply reply; 0232 0233 if (!m_isSupported || m_devices.isEmpty()) { 0234 reply = ActionReply::HelperErrorReply(); 0235 return reply; 0236 } 0237 0238 reply.addData(QStringLiteral("syspath"), m_devices.constFirst().first); 0239 0240 return reply; 0241 } 0242 0243 ActionReply BacklightHelper::brightnessmax(const QVariantMap &args) 0244 { 0245 Q_UNUSED(args); 0246 0247 ActionReply reply; 0248 0249 if (!m_isSupported) { 0250 reply = ActionReply::HelperErrorReply(); 0251 return -1; 0252 } 0253 0254 // maximum brightness 0255 int max_brightness = readFromDevice(m_devices.constFirst().first, QLatin1String("max_brightness")); 0256 0257 if (max_brightness <= 0) { 0258 reply = ActionReply::HelperErrorReply(); 0259 return reply; 0260 } 0261 0262 reply.addData(QStringLiteral("brightnessmax"), max_brightness); 0263 // qCDebug(POWERDEVIL) << "data contains:" << reply.data()["brightnessmax"]; 0264 0265 return reply; 0266 } 0267 0268 KAUTH_HELPER_MAIN("org.kde.powerdevil.backlighthelper", BacklightHelper) 0269 0270 #include "moc_backlighthelper_linux.cpp"