diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d73ce4d..2782a090 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,84 +1,84 @@ cmake_minimum_required(VERSION 3.0) project(PowerDevil) set(PROJECT_VERSION "5.13.90") set(PROJECT_VERSION_MAJOR 5) set(QT_MIN_VERSION "5.11.0") set(KF5_MIN_VERSION "5.50.0") find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) # require at least gcc 4.8 if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") if ("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "4.8") message(SEND_ERROR "Version ${CMAKE_CXX_COMPILER_VERSION} of the ${CMAKE_CXX_COMPILER_ID} C++ compiler is not supported. Please use version 4.8 or later.") endif() endif() find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Widgets DBus X11Extras) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Activities Auth IdleTime Config DBusAddons Solid I18n GlobalAccel KIO NotifyConfig Wayland DocTools Crash Notifications) find_package(KF5Screen CONFIG REQUIRED) find_package(LibKWorkspace CONFIG REQUIRED) find_package(KF5BluezQt ${KF5_MIN_VERSION}) set_package_properties(KF5BluezQt PROPERTIES DESCRIPTION "Qt wrapper for BlueZ 5 DBus API" TYPE OPTIONAL PURPOSE "Support for wireless energy saving actions" ) find_package(KF5NetworkManagerQt ${KF5_MIN_VERSION}) set_package_properties(KF5NetworkManagerQt PROPERTIES DESCRIPTION "Qt wrapper for NetworkManager API" TYPE OPTIONAL PURPOSE "Support for wireless energy saving actions" ) set(HAVE_WIRELESS_SUPPORT FALSE) if(KF5NetworkManagerQt_FOUND AND KF5BluezQt_FOUND) set(HAVE_WIRELESS_SUPPORT TRUE) endif() add_feature_info( "Wireless power saving" HAVE_WIRELESS_SUPPORT "Support turning off signal-transmitting devices to save energy" ) find_package(LibKWorkspace ${PROJECT_VERSION} REQUIRED) find_package(UDev REQUIRED) -find_package(XCB REQUIRED COMPONENTS XCB DPMS) +find_package(XCB REQUIRED COMPONENTS XCB RANDR DPMS) option(WITH_DDCUTIL "DDCUtil library support" OFF) if(WITH_DDCUTIL) find_package(DDCUtil REQUIRED) set_package_properties(DDCUtil PROPERTIES DESCRIPTION "DDCUtil library support" TYPE OPTIONAL PURPOSE "Set monitor settings over DDC/CI channel" ) else() add_feature_info("DDCUtil" "Off" "DDCUtil library support is disabled by default as recomemded by authors, add -DWITH_DDCUTIL=On to enable") endif() include_directories ( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/daemon ) add_definitions(-DQT_NO_KEYWORDS) add_subdirectory(daemon) add_subdirectory(kcmodule) add_subdirectory(doc) install( FILES powerdevil.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR} ) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/daemon/backends/CMakeLists.txt b/daemon/backends/CMakeLists.txt index 2dc465a3..8c5e203b 100644 --- a/daemon/backends/CMakeLists.txt +++ b/daemon/backends/CMakeLists.txt @@ -1,64 +1,69 @@ ########################## UPower Backend ##################################### include_directories(${CMAKE_CURRENT_SOURCE_DIR}/upower - ${X11_INCLUDE_DIR}) + ${X11_INCLUDE_DIR} + ${X11_Xrandr_INCLUDE_PATH}) set(powerdevilupowerbackend_SRCS ${PowerDevil_SOURCE_DIR}/daemon/powerdevil_debug.cpp upower/upowersuspendjob.cpp upower/login1suspendjob.cpp upower/powerdevilupowerbackend.cpp + upower/xrandrbrightness.cpp + upower/xrandrxcbhelper.cpp upower/udevqtclient.cpp upower/udevqtdevice.cpp upower/ddcutilbrightness.cpp ) set_source_files_properties( ${CMAKE_CURRENT_SOURCE_DIR}/upower/dbus/org.freedesktop.UPower.xml ${CMAKE_CURRENT_SOURCE_DIR}/upower/dbus/org.freedesktop.UPower.Device.xml PROPERTIES NO_NAMESPACE TRUE) qt5_add_dbus_interface(powerdevilupowerbackend_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/upower/dbus/org.freedesktop.UPower.xml upower_interface) qt5_add_dbus_interface(powerdevilupowerbackend_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/upower/dbus/org.freedesktop.UPower.Device.xml upower_device_interface) qt5_add_dbus_interface(powerdevilupowerbackend_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/upower/dbus/org.freedesktop.UPower.KbdBacklight.xml upower_kbdbacklight_interface) ## backlight helper executable add_executable(backlighthelper upower/backlighthelper.cpp ${PowerDevil_SOURCE_DIR}/daemon/powerdevil_debug.cpp ${backlighthelper_mocs}) target_link_libraries(backlighthelper Qt5::Core KF5::Auth KF5::I18n) install(TARGETS backlighthelper DESTINATION ${KAUTH_HELPER_INSTALL_DIR}) kauth_install_helper_files(backlighthelper org.kde.powerdevil.backlighthelper root) kauth_install_actions(org.kde.powerdevil.backlighthelper ${CMAKE_CURRENT_SOURCE_DIR}/upower/backlight_helper_actions.actions) ## discrete gpu helper executable add_executable(discretegpuhelper upower/discretegpuhelper.cpp ${PowerDevil_SOURCE_DIR}/daemon/powerdevil_debug.cpp ${discretegpuhelper_mocs}) target_link_libraries(discretegpuhelper Qt5::Core KF5::Auth) install(TARGETS discretegpuhelper DESTINATION ${KAUTH_HELPER_INSTALL_DIR}) kauth_install_helper_files(discretegpuhelper org.kde.powerdevil.discretegpuhelper root) kauth_install_actions(org.kde.powerdevil.discretegpuhelper ${CMAKE_CURRENT_SOURCE_DIR}/upower/discretegpu_helper_actions.actions) add_library(powerdevilupowerbackend ${powerdevilupowerbackend_SRCS}) set_target_properties(powerdevilupowerbackend PROPERTIES PREFIX "") target_link_libraries(powerdevilupowerbackend Qt5::Widgets KF5::Auth KF5::ConfigCore KF5::CoreAddons KF5::DBusAddons KF5::I18n ${UDEV_LIBS} ${X11_LIBRARIES} + ${X11_Xrandr_LIB} ${XCB_XCB_LIBRARY} + ${XCB_RANDR_LIBRARY} powerdevilcore ) if(DDCUTIL_FOUND) target_link_libraries(powerdevilupowerbackend ${LIBDDCUTIL_LIBRARY}) endif() install(TARGETS powerdevilupowerbackend DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/powerdevil) diff --git a/daemon/backends/upower/powerdevilupowerbackend.cpp b/daemon/backends/upower/powerdevilupowerbackend.cpp index 454ba698..7af70599 100644 --- a/daemon/backends/upower/powerdevilupowerbackend.cpp +++ b/daemon/backends/upower/powerdevilupowerbackend.cpp @@ -1,670 +1,724 @@ /* 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 "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() = default; +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_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(); + 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; } - - 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_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(); } - m_syspath = syspathJob->data()["syspath"].toString(); - m_syspath = QFileInfo(m_syspath).readLink(); - 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); + 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).readLink(); + + 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(); } ); - syspathJob->start(); + brightnessMaxJob->start(); } ); - brightnessMaxJob->start(); - }); - brightnessJob->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); + connect(m_brightnessAnimation, &QPropertyAnimation::valueChanged, this, &PowerDevilUPowerBackend::animationValueChanged); + connect(m_brightnessAnimation, &QPropertyAnimation::finished, this, &PowerDevilUPowerBackend::slotScreenBrightnessChanged); + } + Q_EMIT brightnessSupportQueried(true); + } } else { - qCDebug(POWERDEVIL) << "Using DDCutillib"; + 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; } /* 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 Q_FOREACH(OrgFreedesktopUPowerDeviceInterface * upowerDevice, 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) { 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_ddcBrightnessControl->isSupported()){ + 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 { + }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_ddcBrightnessControl->isSupported()){ + 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 { + }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_ddcBrightnessControl->isSupported()){ + 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) { m_cachedBrightnessMap[Screen] = value; slotScreenBrightnessChanged(); } } } 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; } 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(); QList deviceList = m_upowerInterface->EnumerateDevices(); Q_FOREACH (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; Q_FOREACH(OrgFreedesktopUPowerDeviceInterface * upowerDevice, 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_ddcBrightnessControl->isSupported()) { + 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 cdddbf92..bf900975 100644 --- a/daemon/backends/upower/powerdevilupowerbackend.h +++ b/daemon/backends/upower/powerdevilupowerbackend.h @@ -1,125 +1,129 @@ /* 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 UdevHelper; +class XRandRXCBHelper; +class XRandrBrightness; class QPropertyAnimation; 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; // 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 diff --git a/daemon/backends/upower/xrandrbrightness.cpp b/daemon/backends/upower/xrandrbrightness.cpp new file mode 100644 index 00000000..586801cc --- /dev/null +++ b/daemon/backends/upower/xrandrbrightness.cpp @@ -0,0 +1,221 @@ +/* This file is part of the KDE project + * Copyright (C) 2010 Lukas 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 + +#include + +#include "xrandrbrightness.h" + +XRandrBrightness::XRandrBrightness() +{ + if (!QX11Info::isPlatformX11()) { + return; + } + + auto *c = QX11Info::connection(); + + xcb_prefetch_extension_data(c, &xcb_randr_id); + // this reply, for once, does not need to be managed by us + auto *extension = xcb_get_extension_data(c, &xcb_randr_id); + if (!extension || !extension->present) { + qCWarning(POWERDEVIL) << "XRandR extension not available"; + return; + } + + ScopedCPointer versionReply(xcb_randr_query_version_reply(c, + xcb_randr_query_version(c, 1, 2), + nullptr)); + + if (!versionReply) { + qCWarning(POWERDEVIL) << "RandR Query version returned null"; + return; + } + + if (versionReply->major_version < 1 || (versionReply->major_version == 1 && versionReply->minor_version < 2)) { + qCWarning(POWERDEVIL, "RandR version %d.%d too old", versionReply->major_version, versionReply->minor_version); + return; + } + + ScopedCPointer backlightReply(xcb_intern_atom_reply(c, + xcb_intern_atom(c, 1, strlen("Backlight"), "Backlight"), + nullptr)); + + if (!backlightReply) { + qCWarning(POWERDEVIL, "Intern Atom for Backlight returned null"); + return; + } + + m_backlight = backlightReply->atom; + + if (m_backlight == XCB_NONE) { + qCWarning(POWERDEVIL, "No outputs have backlight property"); + return; + } + + xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(c)); + if (!iter.rem) { + qCWarning(POWERDEVIL, "XCB Screen Roots Iterator rem was null"); + return; + } + + xcb_screen_t *screen = iter.data; + xcb_window_t root = screen->root; + + m_resources.reset(xcb_randr_get_screen_resources_current_reply(c, + xcb_randr_get_screen_resources_current(c, root) + , nullptr)); + + if (!m_resources) { + qCWarning(POWERDEVIL, "RANDR Get Screen Resources returned null"); + return; + } +} + +bool XRandrBrightness::isSupported() const +{ + if (!m_resources) { + return false; + } + + auto *outputs = xcb_randr_get_screen_resources_current_outputs(m_resources.data()); + for (int i = 0; i < m_resources->num_outputs; ++i) { + if (backlight_get(outputs[i]) != -1) { + return true; + } + } + + return false; +} + +long XRandrBrightness::brightness() const +{ + if (!m_resources) { + return 0; + } + + auto *outputs = xcb_randr_get_screen_resources_current_outputs(m_resources.data()); + for (int i = 0; i < m_resources->num_outputs; ++i) { + auto output = outputs[i]; + + long cur, min, max; + if (backlight_get_with_range(output, cur, min, max)) { + // FIXME for now just return the first output's value + return cur - min; + } + } + + return 0; +} + +long XRandrBrightness::brightnessMax() const +{ + if (!m_resources) { + return 0; + } + + auto *outputs = xcb_randr_get_screen_resources_current_outputs(m_resources.data()); + for (int i = 0; i < m_resources->num_outputs; ++i) { + auto output = outputs[i]; + + long cur, min, max; + if (backlight_get_with_range(output, cur, min, max)) { + // FIXME for now just return the first output's value + return max - min; + } + } + + return 0; +} + +void XRandrBrightness::setBrightness(long value) +{ + if (!m_resources) { + return; + } + + auto *outputs = xcb_randr_get_screen_resources_current_outputs(m_resources.data()); + for (int i = 0; i < m_resources->num_outputs; ++i) { + auto output = outputs[i]; + + long cur, min, max; + if (backlight_get_with_range(output, cur, min, max)) { + // FIXME for now just set the first output's value + backlight_set(output, min + value); + } + } + + free(xcb_get_input_focus_reply(QX11Info::connection(), xcb_get_input_focus(QX11Info::connection()), nullptr)); // sync +} + +bool XRandrBrightness::backlight_get_with_range(xcb_randr_output_t output, long &value, long &min, long &max) const { + long cur = backlight_get(output); + if (cur == -1) { + return false; + } + + ScopedCPointer propertyReply(xcb_randr_query_output_property_reply(QX11Info::connection(), + xcb_randr_query_output_property(QX11Info::connection(), output, m_backlight) + , nullptr)); + + if (!propertyReply) { + return -1; + } + + if (propertyReply->range && xcb_randr_query_output_property_valid_values_length(propertyReply.data()) == 2) { + int32_t *values = xcb_randr_query_output_property_valid_values(propertyReply.data()); + value = cur; + min = values[0]; + max = values[1]; + return true; + } + + return false; +} + +long XRandrBrightness::backlight_get(xcb_randr_output_t output) const +{ + ScopedCPointer propertyReply; + long value; + + if (m_backlight != XCB_ATOM_NONE) { + propertyReply.reset(xcb_randr_get_output_property_reply(QX11Info::connection(), + xcb_randr_get_output_property(QX11Info::connection(), output, m_backlight, XCB_ATOM_NONE, 0, 4, 0, 0) + , nullptr)); + + if (!propertyReply) { + return -1; + } + } + + if (!propertyReply || propertyReply->type != XCB_ATOM_INTEGER || propertyReply->num_items != 1 || propertyReply->format != 32) { + value = -1; + } else { + value = *(reinterpret_cast(xcb_randr_get_output_property_data(propertyReply.data()))); + } + return value; +} + +void XRandrBrightness::backlight_set(xcb_randr_output_t output, long value) +{ + xcb_randr_change_output_property(QX11Info::connection(), output, m_backlight, XCB_ATOM_INTEGER, + 32, XCB_PROP_MODE_REPLACE, + 1, reinterpret_cast(&value)); +} diff --git a/daemon/backends/upower/xrandrbrightness.h b/daemon/backends/upower/xrandrbrightness.h new file mode 100644 index 00000000..67d44a8b --- /dev/null +++ b/daemon/backends/upower/xrandrbrightness.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE project + * Copyright (C) 2010 Lukas 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. + * + */ + +#ifndef XRANDRBRIGHTNESS_H +#define XRANDRBRIGHTNESS_H + +#include +#include + +#include + +template using ScopedCPointer = QScopedPointer; + +class XRandrBrightness +{ +public: + XRandrBrightness(); + ~XRandrBrightness() = default; + bool isSupported() const; + long brightness() const; + long brightnessMax() const; + void setBrightness(long value); + +private: + bool backlight_get_with_range(xcb_randr_output_t output, long &value, long &min, long &max) const; + long backlight_get(xcb_randr_output_t output) const; + void backlight_set(xcb_randr_output_t output, long value); + + xcb_atom_t m_backlight = XCB_ATOM_NONE; + ScopedCPointer m_resources; + +}; + +#endif // XRANDRBRIGHTNESS_H diff --git a/daemon/backends/upower/xrandrxcbhelper.cpp b/daemon/backends/upower/xrandrxcbhelper.cpp new file mode 100644 index 00000000..ee8c2fea --- /dev/null +++ b/daemon/backends/upower/xrandrxcbhelper.cpp @@ -0,0 +1,124 @@ +/************************************************************************************* + * Copyright (C) 2013 by Alejandro Fiestas Olivares * + * * + * 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 "xrandrxcbhelper.h" + +#include +#include +#include + +bool XRandRXCBHelper::s_init = false; +XRandRInfo XRandRXCBHelper::s_xrandrInfo; + +XRandRXCBHelper::XRandRXCBHelper() : QObject() + , m_window(0) +{ + if (!s_init) { + init(); + } +} + +XRandRXCBHelper::~XRandRXCBHelper() +{ + xcb_destroy_window(conn(), m_window); +} + +bool XRandRXCBHelper::nativeEventFilter(const QByteArray& eventType, void* message, long int* result) +{ + Q_UNUSED(result); + + if (eventType != "xcb_generic_event_t") { + return false; + } + + xcb_generic_event_t* e = static_cast(message); + const uint8_t xEventType = e->response_type & ~0x80; + + //If this event is not xcb_randr_notify, we don't want it + if (xEventType != s_xrandrInfo.eventType) { + return false; + } + + xcb_randr_notify_event_t* + randrEvent = reinterpret_cast(e); + + //If the event is not about a change in an output property, we don't want it + if (randrEvent->subCode != XCB_RANDR_NOTIFY_OUTPUT_PROPERTY) { + return false; + } + + //If the event is not about a new value, we don't want it + if (randrEvent->u.op.status != XCB_PROPERTY_NEW_VALUE) { + return false; + } + + //If the modified property is not backlight, we don't care + if (randrEvent->u.op.atom != s_xrandrInfo.backlightAtom) { + return false; + } + + Q_EMIT brightnessChanged(); + + return false; +} + +void XRandRXCBHelper::init() +{ + xcb_connection_t* c = conn(); + + xcb_prefetch_extension_data(c, &xcb_randr_id); + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(c, &xcb_randr_id); + if (!reply) { + s_xrandrInfo.isPresent = false; + return; + } + + s_xrandrInfo.isPresent = reply->present; + s_xrandrInfo.eventBase = reply->first_event; + s_xrandrInfo.errorBase = reply->first_error; + s_xrandrInfo.eventType = s_xrandrInfo.eventBase + XCB_RANDR_NOTIFY; + s_xrandrInfo.majorOpcode = reply->major_opcode; + + QByteArray backlight = QByteArrayLiteral("Backlight"); + + /*This is KDE5... the world of opengl and wayland (almost), I don't think we really need to + check the old BACKLIGHT atom*/ +// QByteArray backlightCaps("BACKLIGHT"); +// xcb_intern_atom(c, true, backlightCaps.length(), backlightCaps.constData()); + xcb_intern_atom_reply_t* atomReply = + xcb_intern_atom_reply(c, xcb_intern_atom(c, true, backlight.length(), backlight.constData()), nullptr); + + //If backlight atom doesn't exist, means that no driver is actually supporting it + if (!atomReply) { + return; + } + + s_xrandrInfo.backlightAtom = atomReply->atom; + + uint32_t rWindow = rootWindow(c, 0); + m_window = xcb_generate_id(c); + xcb_create_window(c, XCB_COPY_FROM_PARENT, m_window, + rWindow, + 0, 0, 1, 1, 0, XCB_COPY_FROM_PARENT, + XCB_COPY_FROM_PARENT, 0, nullptr); + + xcb_randr_select_input(c, m_window, XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY); + qApp->installNativeEventFilter(this); + + s_init = true; +} diff --git a/daemon/backends/upower/xrandrxcbhelper.h b/daemon/backends/upower/xrandrxcbhelper.h new file mode 100644 index 00000000..ce0e1293 --- /dev/null +++ b/daemon/backends/upower/xrandrxcbhelper.h @@ -0,0 +1,99 @@ +/************************************************************************************* + * Copyright (C) 2013 by Alejandro Fiestas Olivares * + * * + * 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 XRANDR_XCB_HELPER_H +#define XRANDR_XCB_HELPER_H + +#include +#include +#include +#include +#include + +class XRandRInfo +{ +public: + int version; + int eventBase; + int errorBase; + int majorOpcode; + int eventType; + xcb_atom_t backlightAtom; + bool isPresent; +}; + +class XRandRXCBHelper : public QObject, public QAbstractNativeEventFilter +{ + Q_OBJECT +public: + static inline XRandRXCBHelper* self() + { + static XRandRXCBHelper* s_instance = nullptr; + if (!s_instance) { + s_instance = new XRandRXCBHelper(); + if (!s_instance->isValid()) { + s_instance = nullptr; + } + } + + return s_instance; + } + + bool nativeEventFilter(const QByteArray& eventType, void* message, long int* result) override; + +Q_SIGNALS: + void brightnessChanged(); + +private: + XRandRXCBHelper(); + ~XRandRXCBHelper() override; + void init(); + + inline bool isValid() + { + return s_xrandrInfo.isPresent; + } + + inline xcb_connection_t *conn() + { + static xcb_connection_t *s_con = nullptr; + if (!s_con) { + s_con = QX11Info::connection(); + } + return s_con; + } + + inline xcb_window_t rootWindow(xcb_connection_t *c, int screen) + { + xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(c)); + for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(c)); + it.rem; + --screen, xcb_screen_next(&it)) { + if (screen == 0) { + return iter.data->root; + } + } + return XCB_WINDOW_NONE; + } + + uint32_t m_window; + static bool s_init; + static XRandRInfo s_xrandrInfo; +}; + +#endif //XRANDR_XCB_HELPER_H