diff --git a/CMakeLists.txt b/CMakeLists.txt index 04299f3e..5c95972f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,81 +1,82 @@ cmake_minimum_required(VERSION 3.0) project(PowerDevil) set(PROJECT_VERSION "5.17.80") set(PROJECT_VERSION_MAJOR 5) set(QT_MIN_VERSION "5.12.0") set(KF5_MIN_VERSION "5.62.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(ECMSetupVersion) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) 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 RANDR DPMS) option(HAVE_DDCUTIL "DDCUtil library support" OFF) if(HAVE_DDCUTIL) find_package(DDCUtil REQUIRED) add_compile_definitions(WITH_DDCUTIL) set_package_properties(DDCUtil PROPERTIES DESCRIPTION "DDCUtil library support" TYPE OPTIONAL PURPOSE "Set monitor settings over DDC/CI channel" ) else() add_feature_info("DDCUtil" HAVE_DDCUTIL "DDCUtil library support is disabled by default as recomended by authors, add -DHAVE_DDCUTIL=On to enable") endif() include_directories ( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/daemon ) add_definitions(-DQT_NO_KEYWORDS) +add_definitions(-DQT_NO_FOREACH) if (EXISTS "${CMAKE_SOURCE_DIR}/.git") add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) endif() 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/actions/bundled/handlebuttoneventsconfig.cpp b/daemon/actions/bundled/handlebuttoneventsconfig.cpp index 2291c2f0..3469d9f4 100644 --- a/daemon/actions/bundled/handlebuttoneventsconfig.cpp +++ b/daemon/actions/bundled/handlebuttoneventsconfig.cpp @@ -1,178 +1,178 @@ /*************************************************************************** * Copyright (C) 2010 by Dario Freddi * * Copyright (C) 2015 by Kai Uwe Broulik * * * * 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 "handlebuttoneventsconfig.h" #include "suspendsession.h" #include "upower_interface.h" #include "powerdevilpowermanagement.h" #include #include #include #include #include #include K_PLUGIN_FACTORY(PowerDevilSuspendSessionConfigFactory, registerPlugin(); ) namespace PowerDevil { namespace BundledActions { HandleButtonEventsConfig::HandleButtonEventsConfig(QObject* parent, const QVariantList& ) : ActionConfig(parent) { } HandleButtonEventsConfig::~HandleButtonEventsConfig() { } void HandleButtonEventsConfig::save() { if (m_lidCloseCombo) { configGroup().writeEntry("lidAction", m_lidCloseCombo->itemData(m_lidCloseCombo->currentIndex()).toUInt()); } if (m_triggerLidActionWhenExternalMonitorPresent) { configGroup().writeEntry("triggerLidActionWhenExternalMonitorPresent", m_triggerLidActionWhenExternalMonitorPresent->isChecked()); } if (m_powerButtonCombo) { configGroup().writeEntry("powerButtonAction", m_powerButtonCombo->itemData(m_powerButtonCombo->currentIndex()).toUInt()); } configGroup().sync(); } void HandleButtonEventsConfig::load() { configGroup().config()->reparseConfiguration(); if (m_lidCloseCombo) { m_lidCloseCombo->setCurrentIndex(m_lidCloseCombo->findData(QVariant::fromValue(configGroup().readEntry("lidAction", 0)))); } if (m_triggerLidActionWhenExternalMonitorPresent) { m_triggerLidActionWhenExternalMonitorPresent->setChecked(configGroup().readEntry("triggerLidActionWhenExternalMonitorPresent", false)); } if (m_powerButtonCombo) { m_powerButtonCombo->setCurrentIndex(m_powerButtonCombo->findData(QVariant::fromValue(configGroup().readEntry("powerButtonAction", 0)))); } } QList< QPair< QString, QWidget* > > HandleButtonEventsConfig::buildUi() { // Create the boxes m_lidCloseCombo = new QComboBox; m_triggerLidActionWhenExternalMonitorPresent = new QCheckBox( i18nc("Execute action on lid close even when external monitor is connected", "Even when an external monitor is connected") ); m_powerButtonCombo = new QComboBox; // Fill the boxes with options! { QList boxes; boxes << m_lidCloseCombo << m_powerButtonCombo; - Q_FOREACH (QComboBox *box, boxes) { + for (QComboBox *box : qAsConst(boxes)) { box->addItem(QIcon::fromTheme("dialog-cancel"), i18n("Do nothing"), (uint)SuspendSession::None); if (PowerManagement::instance()->canSuspend()) { box->addItem(QIcon::fromTheme("system-suspend"), i18nc("Suspend to RAM", "Sleep"), (uint)SuspendSession::ToRamMode); } if (PowerManagement::instance()->canHibernate()) { box->addItem(QIcon::fromTheme("system-suspend-hibernate"), i18n("Hibernate"), (uint)SuspendSession::ToDiskMode); } if (PowerManagement::instance()->canHybridSuspend()) { box->addItem(QIcon::fromTheme("system-suspend-hybrid"), i18n("Hybrid sleep"), (uint)SuspendSession::SuspendHybridMode); } box->addItem(QIcon::fromTheme("system-shutdown"), i18n("Shut down"), (uint)SuspendSession::ShutdownMode); box->addItem(QIcon::fromTheme("system-lock-screen"), i18n("Lock screen"), (uint)SuspendSession::LockScreenMode); if (box != m_lidCloseCombo) { box->addItem(QIcon::fromTheme("system-log-out"), i18n("Prompt log out dialog"), (uint)SuspendSession::LogoutDialogMode); } box->addItem(QIcon::fromTheme("preferences-desktop-screensaver"), i18n("Turn off screen"), (uint)SuspendSession::TurnOffScreenMode); } } connect(m_lidCloseCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setChanged())); connect(m_triggerLidActionWhenExternalMonitorPresent, SIGNAL(stateChanged(int)), this, SLOT(setChanged())); connect(m_powerButtonCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setChanged())); bool lidFound = false; bool powerFound = true; // HACK This needs proper API!! // get a list of all devices that are Buttons /* Q_FOREACH (Solid::Device device, Solid::Device::listFromType(Solid::DeviceInterface::Button, QString())) { Solid::Button *button = device.as(); if (button->type() == Solid::Button::LidButton) { lidFound = true; } else if (button->type() == Solid::Button::PowerButton) { powerFound = true; } }*/ auto m_upowerInterface = new OrgFreedesktopUPowerInterface("org.freedesktop.UPower", "/org/freedesktop/UPower", QDBusConnection::systemBus(), this); lidFound = m_upowerInterface->lidIsPresent(); QList< QPair< QString, QWidget* > > retlist; if (lidFound) { retlist.append(qMakePair(i18n("When laptop lid closed"), m_lidCloseCombo)); // an empty label will make it treat the widget as title checkbox and left-align it retlist.append(qMakePair(QLatin1String("NONE"), m_triggerLidActionWhenExternalMonitorPresent)); } else { m_lidCloseCombo->deleteLater(); m_lidCloseCombo = nullptr; m_triggerLidActionWhenExternalMonitorPresent->deleteLater(); m_triggerLidActionWhenExternalMonitorPresent = nullptr; } if (powerFound) { retlist.append(qMakePair< QString, QWidget* >(i18n("When power button pressed"), m_powerButtonCombo)); } else { m_powerButtonCombo->deleteLater(); m_powerButtonCombo = nullptr; } // unified width for the comboboxes int comboBoxMaxWidth = 300; if (m_lidCloseCombo) { comboBoxMaxWidth = qMax(comboBoxMaxWidth, m_lidCloseCombo->sizeHint().width()); } if (m_powerButtonCombo) { comboBoxMaxWidth = qMax(comboBoxMaxWidth, m_powerButtonCombo->sizeHint().width()); } if (m_lidCloseCombo) { m_lidCloseCombo->setMinimumWidth(300); m_lidCloseCombo->setMaximumWidth(comboBoxMaxWidth); } if (m_powerButtonCombo) { m_powerButtonCombo->setMinimumWidth(300); m_powerButtonCombo->setMaximumWidth(comboBoxMaxWidth); } return retlist; } } } #include "handlebuttoneventsconfig.moc" diff --git a/daemon/backends/upower/backlighthelper.cpp b/daemon/backends/upower/backlighthelper.cpp index 8148b61e..de29caf5 100644 --- a/daemon/backends/upower/backlighthelper.cpp +++ b/daemon/backends/upower/backlighthelper.cpp @@ -1,338 +1,338 @@ /* 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 #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_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 - QStringList interfaces = backlightDir.entryList(); + const QStringList interfaces = backlightDir.entryList(); QFile file; QByteArray buffer; QStringList firmware, platform, raw, leds; - Q_FOREACH(const QString & interface, interfaces) { + 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"); Q_FOREACH (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(). qSort(m_sysctlBrightnessLevels.begin(), m_sysctlBrightnessLevels.end()); #endif } ActionReply BacklightHelper::brightness(const QVariantMap &args) { Q_UNUSED(args); ActionReply reply; if (!m_isSupported) { reply = ActionReply::HelperErrorReply(); return reply; } // 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; } #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; } 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; } ActionReply BacklightHelper::setbrightness(const QVariantMap &args) { ActionReply reply; int actual_brightness = args.value(QStringLiteral("brightness")).toInt(); if (!m_isSupported) { reply = ActionReply::HelperErrorReply(); return reply; } //qCDebug(POWERDEVIL) << "setting brightness:" << actual_brightness; #ifdef USE_SYSCTL int actual_level = -1; int d1 = 101; // Search for the nearest level. Q_FOREACH (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; } #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; } int result = file.write(QByteArray::number(actual_brightness)); file.close(); if (result == -1) { reply = ActionReply::HelperErrorReply(); // reply.setErrorCode(file.error()); qCWarning(POWERDEVIL) << "writing brightness failed with error code " << file.error() << file.errorString(); } #endif return reply; } 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/powerdevilupowerbackend.cpp b/daemon/backends/upower/powerdevilupowerbackend.cpp index 923e4cd9..7dac54b1 100644 --- a/daemon/backends/upower/powerdevilupowerbackend.cpp +++ b/daemon/backends/upower/powerdevilupowerbackend.cpp @@ -1,729 +1,729 @@ /* 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() { 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); 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 - Q_FOREACH(OrgFreedesktopUPowerDeviceInterface * upowerDevice, m_devices) { + 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) { 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) { 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) { + 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; - Q_FOREACH(OrgFreedesktopUPowerDeviceInterface * upowerDevice, m_devices) { + 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/udevqtclient.cpp b/daemon/backends/upower/udevqtclient.cpp index 6c33a49a..4bd06af9 100644 --- a/daemon/backends/upower/udevqtclient.cpp +++ b/daemon/backends/upower/udevqtclient.cpp @@ -1,258 +1,258 @@ /* Copyright 2009 Benjamin K. Stuhl This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "udevqtclient.h" #include "udevqt_p.h" #include #include #include namespace UdevQt { ClientPrivate::ClientPrivate(Client *q_) : udev(nullptr), monitor(nullptr), q(q_), monitorNotifier(nullptr) { } ClientPrivate::~ClientPrivate() { udev_unref(udev); delete monitorNotifier; if (monitor) udev_monitor_unref(monitor); } void ClientPrivate::init(const QStringList &subsystemList, ListenToWhat what) { udev = udev_new(); if (what != ListenToNone) { setWatchedSubsystems(subsystemList); } } void ClientPrivate::setWatchedSubsystems(const QStringList &subsystemList) { // create a listener struct udev_monitor *newM = udev_monitor_new_from_netlink(udev, "udev"); if (!newM) { qCWarning(POWERDEVIL, "UdevQt: unable to create udev monitor connection"); return; } // apply our filters; an empty list means listen to everything - Q_FOREACH (const QString& subsysDevtype, subsystemList) { + for (const QString& subsysDevtype : subsystemList) { int ix = subsysDevtype.indexOf(u'/'); if (ix > 0) { QByteArray subsystem = subsysDevtype.leftRef(ix).toLatin1(); QByteArray devType = subsysDevtype.midRef(ix + 1).toLatin1(); udev_monitor_filter_add_match_subsystem_devtype(newM, subsystem.constData(), devType.constData()); } else { udev_monitor_filter_add_match_subsystem_devtype(newM, subsysDevtype.toLatin1().constData(), nullptr); } } // start the new monitor receiving udev_monitor_enable_receiving(newM); QSocketNotifier *sn = new QSocketNotifier(udev_monitor_get_fd(newM), QSocketNotifier::Read); QObject::connect(sn, SIGNAL(activated(int)), q, SLOT(_uq_monitorReadyRead(int))); // kill any previous monitor delete monitorNotifier; if (monitor) udev_monitor_unref(monitor); // and save our new one monitor = newM; monitorNotifier = sn; watchedSubsystems = subsystemList; } void ClientPrivate::_uq_monitorReadyRead(int fd) { Q_UNUSED(fd); monitorNotifier->setEnabled(false); struct udev_device *dev = udev_monitor_receive_device(monitor); monitorNotifier->setEnabled(true); if (!dev) return; Device device(new DevicePrivate(dev, false)); QByteArray action(udev_device_get_action(dev)); if (action == "add") { Q_EMIT q->deviceAdded(device); } else if (action == "remove") { Q_EMIT q->deviceRemoved(device); } else if (action == "change") { Q_EMIT q->deviceChanged(device); } else if (action == "online") { Q_EMIT q->deviceOnlined(device); } else if (action == "offline") { Q_EMIT q->deviceOfflined(device); } else { qCWarning(POWERDEVIL, "UdevQt: unhandled device action \"%s\"", action.constData()); } } DeviceList ClientPrivate::deviceListFromEnumerate(struct udev_enumerate *en) { DeviceList ret; struct udev_list_entry *list, *entry; udev_enumerate_scan_devices(en); list = udev_enumerate_get_list_entry(en); udev_list_entry_foreach(entry, list) { struct udev_device *ud = udev_device_new_from_syspath(udev_enumerate_get_udev(en), udev_list_entry_get_name(entry)); if (!ud) continue; ret << Device(new DevicePrivate(ud, false)); } udev_enumerate_unref(en); return ret; } Client::Client(QObject *parent) : QObject(parent) , d(new ClientPrivate(this)) { d->init(QStringList(), ClientPrivate::ListenToNone); } Client::Client(const QStringList& subsystemList, QObject *parent) : QObject(parent) , d(new ClientPrivate(this)) { d->init(subsystemList, ClientPrivate::ListenToList); } Client::~Client() { delete d; } QStringList Client::watchedSubsystems() const { // we're watching a specific list if (!d->watchedSubsystems.isEmpty()) return d->watchedSubsystems; // we're not watching anything if (!d->monitor) return QStringList(); // we're watching everything: figure out what "everything" currently is // we don't cache it, since it may be subject to change, depending on hotplug struct udev_enumerate *en = udev_enumerate_new(d->udev); udev_enumerate_scan_subsystems(en); QStringList s = listFromListEntry(udev_enumerate_get_list_entry(en)); udev_enumerate_unref(en); return s; } void Client::setWatchedSubsystems(const QStringList &subsystemList) { d->setWatchedSubsystems(subsystemList); } DeviceList Client::devicesByProperty(const QString &property, const QVariant &value) { struct udev_enumerate *en = udev_enumerate_new(d->udev); if (value.isValid()) { udev_enumerate_add_match_property(en, property.toLatin1().constData(), value.toString().toLatin1().constData()); } else { udev_enumerate_add_match_property(en, property.toLatin1().constData(), nullptr); } return d->deviceListFromEnumerate(en); } DeviceList Client::allDevices() { struct udev_enumerate *en = udev_enumerate_new(d->udev); return d->deviceListFromEnumerate(en); } DeviceList Client::devicesBySubsystem(const QString &subsystem) { struct udev_enumerate *en = udev_enumerate_new(d->udev); udev_enumerate_add_match_subsystem(en, subsystem.toLatin1().constData()); return d->deviceListFromEnumerate(en); } Device Client::deviceByDeviceFile(const QString &deviceFile) { QT_STATBUF sb; if (QT_STAT(deviceFile.toLatin1().constData(), &sb) != 0) return Device(); struct udev_device *ud = nullptr; if (S_ISBLK(sb.st_mode)) ud = udev_device_new_from_devnum(d->udev, 'b', sb.st_rdev); else if (S_ISCHR(sb.st_mode)) ud = udev_device_new_from_devnum(d->udev, 'c', sb.st_rdev); if (!ud) return Device(); return Device(new DevicePrivate(ud, false)); } Device Client::deviceBySysfsPath(const QString &sysfsPath) { struct udev_device *ud = udev_device_new_from_syspath(d->udev, sysfsPath.toLatin1().constData()); if (!ud) return Device(); return Device(new DevicePrivate(ud, false)); } Device Client::deviceBySubsystemAndName(const QString &subsystem, const QString &name) { struct udev_device *ud = udev_device_new_from_subsystem_sysname(d->udev, subsystem.toLatin1().constData(), name.toLatin1().constData()); if (!ud) return Device(); return Device(new DevicePrivate(ud, false)); } } #include "moc_udevqtclient.cpp" diff --git a/daemon/powerdevilactionpool.cpp b/daemon/powerdevilactionpool.cpp index a004c060..cc87fa68 100644 --- a/daemon/powerdevilactionpool.cpp +++ b/daemon/powerdevilactionpool.cpp @@ -1,200 +1,200 @@ /*************************************************************************** * Copyright (C) 2010 by Dario Freddi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "powerdevilactionpool.h" #include "powerdevilaction.h" #include "powerdevilcore.h" #include "powerdevil_debug.h" #include #include #include #include #include #include // Bundled actions: #include "actions/bundled/suspendsession.h" #include "actions/bundled/brightnesscontrol.h" #include "actions/bundled/keyboardbrightnesscontrol.h" #include "actions/bundled/dimdisplay.h" #include "actions/bundled/runscript.h" #include "actions/bundled/handlebuttonevents.h" #ifdef HAVE_WIRELESS_SUPPORT #include "actions/bundled/wirelesspowersaving.h" #endif namespace PowerDevil { class ActionPoolHelper { public: ActionPoolHelper() : q(nullptr) {} ~ActionPoolHelper() { delete q; } ActionPool *q; }; Q_GLOBAL_STATIC(ActionPoolHelper, s_globalActionPool) ActionPool *ActionPool::instance() { if (!s_globalActionPool->q) { new ActionPool; } return s_globalActionPool->q; } ActionPool::ActionPool() { Q_ASSERT(!s_globalActionPool->q); s_globalActionPool->q = this; } ActionPool::~ActionPool() { clearCache(); } void ActionPool::clearCache() { QHash< QString, Action* >::iterator i = m_actionPool.begin(); while (i != m_actionPool.end()) { // Delete the associated action and erase i.value()->deleteLater(); i = m_actionPool.erase(i); } } void ActionPool::init(PowerDevil::Core *parent) { // Load all the actions - KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("PowerDevil/Action"), + const KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("PowerDevil/Action"), QStringLiteral("[X-KDE-PowerDevil-Action-IsBundled] == FALSE")); - Q_FOREACH (KService::Ptr offer, offers) { + for (const KService::Ptr offer : offers) { QString actionId = offer->property(QStringLiteral("X-KDE-PowerDevil-Action-ID"), QVariant::String).toString(); qCDebug(POWERDEVIL) << "Got a valid offer for " << actionId; if (!offer->showOnCurrentPlatform()) { qCDebug(POWERDEVIL) << "Doesn't support the windowing system"; continue; } //try to load the specified library PowerDevil::Action *retaction = offer->createInstance< PowerDevil::Action >(parent); if (!retaction) { // Troubles... qCWarning(POWERDEVIL) << "failed to load" << offer->desktopEntryName(); continue; } // Is the action available and supported? if (!retaction->isSupported()) { // Skip that retaction->deleteLater(); continue; } // Insert m_actionPool.insert(actionId, retaction); } // Load bundled actions now m_actionPool.insert(QStringLiteral("SuspendSession"), new BundledActions::SuspendSession(parent)); m_actionPool.insert(QStringLiteral("BrightnessControl"), new BundledActions::BrightnessControl(parent)); m_actionPool.insert(QStringLiteral("KeyboardBrightnessControl"), new BundledActions::KeyboardBrightnessControl(parent)); m_actionPool.insert(QStringLiteral("DimDisplay"), new BundledActions::DimDisplay(parent)); m_actionPool.insert(QStringLiteral("RunScript"), new BundledActions::RunScript(parent)); m_actionPool.insert(QStringLiteral("HandleButtonEvents"), new BundledActions::HandleButtonEvents(parent)); #ifdef HAVE_WIRELESS_SUPPORT m_actionPool.insert(QStringLiteral("WirelessPowerSaving"), new BundledActions::WirelessPowerSaving(parent)); #endif // Verify support QHash::iterator i = m_actionPool.begin(); while (i != m_actionPool.end()) { Action *action = i.value(); if (!action->isSupported()) { i = m_actionPool.erase(i); action->deleteLater(); } else { ++i; } } // Register DBus objects { - KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("PowerDevil/Action"), + const KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("PowerDevil/Action"), QStringLiteral("[X-KDE-PowerDevil-Action-RegistersDBusInterface] == TRUE")); - Q_FOREACH (KService::Ptr offer, offers) { + for (const KService::Ptr offer : offers) { QString actionId = offer->property(QStringLiteral("X-KDE-PowerDevil-Action-ID"), QVariant::String).toString(); if (m_actionPool.contains(actionId)) { QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/Solid/PowerManagement/Actions/") + actionId, m_actionPool[actionId]); } } } } Action* ActionPool::loadAction(const QString& actionId, const KConfigGroup& group, PowerDevil::Core *parent) { Q_UNUSED(parent); // Let's retrieve the action if (m_actionPool.contains(actionId)) { Action *retaction = m_actionPool[actionId]; if (group.isValid()) { if (m_activeActions.contains(actionId)) { // We are reloading the action: let's unload it first then. retaction->onProfileUnload(); retaction->unloadAction(); m_activeActions.removeOne(actionId); } retaction->loadAction(group); m_activeActions.append(actionId); } return retaction; } else { // Hmm... troubles in configuration. Np, let's just return 0 and let the core handle this return nullptr; } } void ActionPool::unloadAllActiveActions() { - Q_FOREACH (const QString &action, m_activeActions) { + for (const QString &action : qAsConst(m_activeActions)) { m_actionPool[action]->onProfileUnload(); m_actionPool[action]->unloadAction(); } m_activeActions.clear(); } } diff --git a/daemon/powerdevilapp.cpp b/daemon/powerdevilapp.cpp index 9c0f0b45..36890309 100644 --- a/daemon/powerdevilapp.cpp +++ b/daemon/powerdevilapp.cpp @@ -1,222 +1,222 @@ /*************************************************************************** * Copyright (C) 2010 by Dario Freddi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "powerdevilapp.h" #include "powerdevilfdoconnector.h" #include "powermanagementadaptor.h" #include "powermanagementpolicyagentadaptor.h" #include "powerdevilcore.h" #include "powerdevil_debug.h" #include "powerdevil_version.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include PowerDevilApp::PowerDevilApp(int &argc, char **argv) : QGuiApplication(argc, argv) , m_core(nullptr) { migratePre512KeyboardShortcuts(); } PowerDevilApp::~PowerDevilApp() { delete m_core; } void PowerDevilApp::init() { KLocalizedString::setApplicationDomain("powerdevil"); KAboutData aboutData(QStringLiteral("org_kde_powerdevil"), i18n("KDE Power Management System"), QStringLiteral(POWERDEVIL_VERSION_STRING), i18nc("@title", "PowerDevil, an advanced, modular and lightweight power management daemon"), KAboutLicense::GPL, i18nc("@info:credit", "(c) 2015-2019 Kai Uwe Broulik")); aboutData.addAuthor(i18nc("@info:credit", "Kai Uwe Broulik"), i18nc("@info:credit", "Maintainer"), QStringLiteral("kde@privat.broulik.de")); aboutData.addAuthor(i18nc("@info:credit", "Dario Freddi"), i18nc("@info:credit", "Previous maintainer"), QStringLiteral("drf@kde.org")); aboutData.setProductName("Powerdevil"); KAboutData::setApplicationData(aboutData); if (QDBusConnection::systemBus().interface()->isServiceRegistered(QLatin1String("org.freedesktop.PowerManagement")) || QDBusConnection::systemBus().interface()->isServiceRegistered(QLatin1String("com.novell.powersave")) || QDBusConnection::systemBus().interface()->isServiceRegistered(QLatin1String("org.freedesktop.Policy.Power"))) { qCCritical(POWERDEVIL) << "KDE Power Management system not initialized, another power manager has been detected"; return; } // not parenting Core to PowerDevilApp as it is the deleted too late on teardown // where the X connection is already lost leading to a a crash (Bug 371127) m_core = new PowerDevil::Core(nullptr/*, KComponentData(aboutData)*/); connect(m_core, SIGNAL(coreReady()), this, SLOT(onCoreReady())); // Before doing anything, let's set up our backend const QStringList paths = QCoreApplication::libraryPaths(); QFileInfoList fileInfos; for (const QString &path : paths) { QDir dir(path + QLatin1String("/kf5/powerdevil/"), QStringLiteral("*"), QDir::SortFlags(QDir::QDir::Name), QDir::NoDotAndDotDot | QDir::Files); fileInfos.append(dir.entryInfoList()); } QFileInfo backendFileInfo; - Q_FOREACH (const QFileInfo &f, fileInfos) { + for (const QFileInfo &f : qAsConst(fileInfos)) { if (f.baseName().toLower() == QLatin1String("powerdevilupowerbackend")) { backendFileInfo = f; break; } } QPluginLoader *loader = new QPluginLoader(backendFileInfo.filePath(), m_core); QObject *instance = loader->instance(); if (!instance) { qCDebug(POWERDEVIL) << loader->errorString(); qCCritical(POWERDEVIL) << "KDE Power Management System init failed!"; m_core->loadCore(nullptr); return; } auto interface = qobject_cast(instance); if (!interface) { qCDebug(POWERDEVIL) << "Failed to cast plugin instance to BackendInterface, check your plugin"; qCCritical(POWERDEVIL) << "KDE Power Management System init failed!"; m_core->loadCore(nullptr); return; } qCDebug(POWERDEVIL) << "Backend loaded, loading core"; m_core->loadCore(interface); } void PowerDevilApp::onCoreReady() { qCDebug(POWERDEVIL) << "Core is ready, registering various services on the bus..."; //DBus logic for the core new PowerManagementAdaptor(m_core); new PowerDevil::FdoConnector(m_core); QDBusConnection::sessionBus().registerService(QLatin1String("org.kde.Solid.PowerManagement")); QDBusConnection::sessionBus().registerObject(QLatin1String("/org/kde/Solid/PowerManagement"), m_core); QDBusConnection::systemBus().interface()->registerService("org.freedesktop.Policy.Power"); // Start the Policy Agent service qDBusRegisterMetaType>(); qDBusRegisterMetaType(); new PowerManagementPolicyAgentAdaptor(PowerDevil::PolicyAgent::instance()); QDBusConnection::sessionBus().registerService(QLatin1String("org.kde.Solid.PowerManagement.PolicyAgent")); QDBusConnection::sessionBus().registerObject(QLatin1String("/org/kde/Solid/PowerManagement/PolicyAgent"), PowerDevil::PolicyAgent::instance()); } /* * 5.11 -> 5.12 migrated shortcuts from kded5 to the correct component name org_kde_powerdevil for good reasons however despite a kconfupdate script working correctly and moving the old keys, because kglobalaccel is running whilst we update it synced the old values, and then ignores the powerdevil copy on future loads this removes the old powermanagent entries in the kded5 component at powerdevil startup //which is at runtime where we can talk to kglobalaccel and then re-register ours this method can probably be deleted at some point in the future */ void PowerDevilApp::migratePre512KeyboardShortcuts() { auto configGroup = KSharedConfig::openConfig("powermanagementprofilesrc")->group("migration"); if (!configGroup.hasKey("kdedShortcutMigration")) { const QStringList actionIds({ "Decrease Keyboard Brightness", "Decrease Screen Brightness", "Hibernate", "Increase Keyboard Brightness", "Increase Screen Brightness", "PowerOff", "Sleep", "Toggle Keyboard Backlight" }); for (const QString &actionId: actionIds) { QAction oldAction; oldAction.setObjectName(actionId); oldAction.setProperty("componentName", "kded5"); //claim the old shortcut so we can remove it.. KGlobalAccel::self()->setShortcut(&oldAction, QList(), KGlobalAccel::Autoloading); auto shortcuts = KGlobalAccel::self()->shortcut(&oldAction); KGlobalAccel::self()->removeAllShortcuts(&oldAction); QAction newAction; newAction.setObjectName(actionId); newAction.setProperty("componentName", "org_kde_powerdevil"); if (!shortcuts.isEmpty()) { //register with no autoloading to sync config, we then delete our QAction, and powerdevil will //re-register as normal KGlobalAccel::self()->setShortcut(&newAction, shortcuts, KGlobalAccel::NoAutoloading); } } } configGroup.writeEntry(QStringLiteral("kdedShortcutMigration"), true); configGroup.sync(); } int main(int argc, char **argv) { QGuiApplication::setDesktopSettingsAware(false); KWorkSpace::detectPlatform(argc, argv); PowerDevilApp app(argc, argv); auto disableSessionManagement = [](QSessionManager &sm) { sm.setRestartHint(QSessionManager::RestartNever); }; QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement); QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement); KDBusService service(KDBusService::Unique); KCrash::setFlags(KCrash::AutoRestart); app.setQuitOnLastWindowClosed(false); app.init(); return app.exec(); } diff --git a/daemon/powerdevilcore.cpp b/daemon/powerdevilcore.cpp index 0279a35d..20cb3480 100644 --- a/daemon/powerdevilcore.cpp +++ b/daemon/powerdevilcore.cpp @@ -1,900 +1,902 @@ /*************************************************************************** * Copyright (C) 2010 by Dario Freddi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "powerdevilcore.h" #include "PowerDevilSettings.h" #include "powerdevilaction.h" #include "powerdevilactionpool.h" #include "powerdevilbackendinterface.h" #include "powerdevilpolicyagent.h" #include "powerdevilprofilegenerator.h" #include "powerdevil_debug.h" #include "actions/bundled/suspendsession.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace PowerDevil { Core::Core(QObject* parent) : QObject(parent) , m_hasDualGpu(false) , m_backend(nullptr) , m_notificationsWatcher(nullptr) , m_criticalBatteryTimer(new QTimer(this)) , m_activityConsumer(new KActivities::Consumer(this)) , m_pendingWakeupEvent(true) { KAuth::Action discreteGpuAction(QStringLiteral("org.kde.powerdevil.discretegpuhelper.hasdualgpu")); discreteGpuAction.setHelperId(QStringLiteral("org.kde.powerdevil.discretegpuhelper")); KAuth::ExecuteJob *discreteGpuJob = discreteGpuAction.execute(); connect(discreteGpuJob, &KJob::result, this, [this, discreteGpuJob] { if (discreteGpuJob->error()) { qCWarning(POWERDEVIL) << "org.kde.powerdevil.discretegpuhelper.hasdualgpu failed"; qCDebug(POWERDEVIL) << discreteGpuJob->errorText(); return; } m_hasDualGpu = discreteGpuJob->data()[QStringLiteral("hasdualgpu")].toBool(); }); discreteGpuJob->start(); } Core::~Core() { qCDebug(POWERDEVIL) << "Core unloading"; // Unload all actions before exiting, and clear the cache ActionPool::instance()->unloadAllActiveActions(); ActionPool::instance()->clearCache(); } void Core::loadCore(BackendInterface* backend) { if (!backend) { return; } m_backend = backend; // Async backend init - so that KDED gets a bit of a speed up qCDebug(POWERDEVIL) << "Core loaded, initializing backend"; connect(m_backend, SIGNAL(backendReady()), this, SLOT(onBackendReady())); m_backend->init(); } void Core::onBackendReady() { qCDebug(POWERDEVIL) << "Backend ready, KDE Power Management system initialized"; m_profilesConfig = KSharedConfig::openConfig(QStringLiteral("powermanagementprofilesrc"), KConfig::CascadeConfig); QStringList groups = m_profilesConfig->groupList(); // the "migration" key is for shortcuts migration in added by migratePre512KeyboardShortcuts // and as such our configuration would never be considered empty, ignore it! groups.removeOne(QStringLiteral("migration")); // Is it brand new? if (groups.isEmpty()) { // Generate defaults qCDebug(POWERDEVIL) << "Generating a default configuration"; bool toRam = m_backend->supportedSuspendMethods() & PowerDevil::BackendInterface::ToRam; bool toDisk = m_backend->supportedSuspendMethods() & PowerDevil::BackendInterface::ToDisk; ProfileGenerator::generateProfiles(toRam, toDisk); m_profilesConfig->reparseConfiguration(); } // Get the battery devices ready { using namespace Solid; connect(DeviceNotifier::instance(), SIGNAL(deviceAdded(QString)), this, SLOT(onDeviceAdded(QString))); connect(DeviceNotifier::instance(), SIGNAL(deviceRemoved(QString)), this, SLOT(onDeviceRemoved(QString))); // Force the addition of already existent batteries - Q_FOREACH (const Device &device, Device::listFromType(DeviceInterface::Battery, QString())) { + const auto devices = Device::listFromType(DeviceInterface::Battery, QString()); + for (const Device &device : devices) { onDeviceAdded(device.udi()); } } connect(m_backend, SIGNAL(acAdapterStateChanged(PowerDevil::BackendInterface::AcAdapterState)), this, SLOT(onAcAdapterStateChanged(PowerDevil::BackendInterface::AcAdapterState))); connect(m_backend, SIGNAL(batteryRemainingTimeChanged(qulonglong)), this, SLOT(onBatteryRemainingTimeChanged(qulonglong))); connect(m_backend, SIGNAL(lidClosedChanged(bool)), this, SLOT(onLidClosedChanged(bool))); connect(m_backend, &BackendInterface::aboutToSuspend, this, &Core::onAboutToSuspend); connect(KIdleTime::instance(), SIGNAL(timeoutReached(int,int)), this, SLOT(onKIdleTimeoutReached(int,int))); connect(KIdleTime::instance(), SIGNAL(resumingFromIdle()), this, SLOT(onResumingFromIdle())); connect(m_activityConsumer, &KActivities::Consumer::currentActivityChanged, this, [this]() { loadProfile(); }); // Set up the policy agent PowerDevil::PolicyAgent::instance()->init(); // When inhibitions change, simulate user activity, see Bug 315438 connect(PowerDevil::PolicyAgent::instance(), &PowerDevil::PolicyAgent::unavailablePoliciesChanged, this, [](PowerDevil::PolicyAgent::RequiredPolicies newPolicies) { Q_UNUSED(newPolicies); KIdleTime::instance()->simulateUserActivity(); }); // Bug 354250: Simulate user activity when session becomes inactive, // this keeps us from sending the computer to sleep when switching to an idle session. // (this is just being lazy as it will result in us clearing everything connect(PowerDevil::PolicyAgent::instance(), &PowerDevil::PolicyAgent::sessionActiveChanged, this, [this](bool active) { if (active) { // force reload profile so all actions re-register their idle timeouts loadProfile(true /*force*/); } else { // Bug 354250: Keep us from sending the computer to sleep when switching // to an idle session by removing all idle timeouts KIdleTime::instance()->removeAllIdleTimeouts(); m_registeredActionTimeouts.clear(); } }); // Initialize the action pool, which will also load the needed startup actions. PowerDevil::ActionPool::instance()->init(this); // Set up the critical battery timer m_criticalBatteryTimer->setSingleShot(true); m_criticalBatteryTimer->setInterval(60000); connect(m_criticalBatteryTimer, SIGNAL(timeout()), this, SLOT(onCriticalBatteryTimerExpired())); // wait until the notification system is set up before firing notifications // to avoid them showing ontop of ksplash... if (QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.Notifications"))) { onServiceRegistered(QString()); } else { m_notificationsWatcher = new QDBusServiceWatcher(QStringLiteral("org.freedesktop.Notifications"), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForRegistration, this); connect(m_notificationsWatcher, SIGNAL(serviceRegistered(QString)), this, SLOT(onServiceRegistered(QString))); // ...but fire them after 30s nonetheless to ensure they've been shown QTimer::singleShot(30000, this, SLOT(onNotificationTimeout())); } // All systems up Houston, let's go! Q_EMIT coreReady(); refreshStatus(); } bool Core::isActionSupported(const QString& actionName) { Action *action = ActionPool::instance()->loadAction(actionName, KConfigGroup(), this); if (!action) { return false; } else { return action->isSupported(); } } void Core::refreshStatus() { /* The configuration could have changed if this function was called, so * let's resync it. */ reparseConfiguration(); loadProfile(true); } void Core::reparseConfiguration() { PowerDevilSettings::self()->load(); m_profilesConfig->reparseConfiguration(); // Config reloaded Q_EMIT configurationReloaded(); } QString Core::currentProfile() const { return m_currentProfile; } void Core::loadProfile(bool force) { QString profileId; // Policy check if (PolicyAgent::instance()->requirePolicyCheck(PolicyAgent::ChangeProfile) != PolicyAgent::None) { qCDebug(POWERDEVIL) << "Policy Agent prevention: on"; return; } KConfigGroup config; // Check the activity in which we are in QString activity = m_activityConsumer->currentActivity(); qCDebug(POWERDEVIL) << "Currently using activity " << activity; KConfigGroup activitiesConfig(m_profilesConfig, "Activities"); qCDebug(POWERDEVIL) << activitiesConfig.groupList() << activitiesConfig.keyList(); // Are we mirroring an activity? if (activitiesConfig.group(activity).readEntry("mode", "None") == QStringLiteral("ActLike") && activitiesConfig.group(activity).readEntry("actLike", QString()) != QStringLiteral("AC") && activitiesConfig.group(activity).readEntry("actLike", QString()) != QStringLiteral("Battery") && activitiesConfig.group(activity).readEntry("actLike", QString()) != QStringLiteral("LowBattery")) { // Yes, let's use that then activity = activitiesConfig.group(activity).readEntry("actLike", QString()); qCDebug(POWERDEVIL) << "Activity is a mirror"; } KConfigGroup activityConfig = activitiesConfig.group(activity); qCDebug(POWERDEVIL) << activityConfig.groupList() << activityConfig.keyList(); // See if this activity has priority if (activityConfig.readEntry("mode", "None") == QStringLiteral("SeparateSettings")) { // Prioritize this profile over anything config = activityConfig.group("SeparateSettings"); qCDebug(POWERDEVIL) << "Activity is enforcing a different profile"; profileId = activity; } else { // It doesn't, let's load the current state's profile if (m_batteriesPercent.isEmpty()) { qCDebug(POWERDEVIL) << "No batteries found, loading AC"; profileId = QStringLiteral("AC"); } else if (activityConfig.readEntry("mode", "None") == QStringLiteral("ActLike")) { if (activityConfig.readEntry("actLike", QString()) == QStringLiteral("AC") || activityConfig.readEntry("actLike", QString()) == QStringLiteral("Battery") || activityConfig.readEntry("actLike", QString()) == QStringLiteral("LowBattery")) { // Same as above, but with an existing profile config = m_profilesConfig.data()->group(activityConfig.readEntry("actLike", QString())); profileId = activityConfig.readEntry("actLike", QString()); qCDebug(POWERDEVIL) << "Activity is mirroring a different profile"; } } else { // Compute the previous and current global percentage const int percent = currentChargePercent(); if (backend()->acAdapterState() == BackendInterface::Plugged) { profileId = QStringLiteral("AC"); qCDebug(POWERDEVIL) << "Loading profile for plugged AC"; } else if (percent <= PowerDevilSettings::batteryLowLevel()) { profileId = QStringLiteral("LowBattery"); qCDebug(POWERDEVIL) << "Loading profile for low battery"; } else { profileId = QStringLiteral("Battery"); qCDebug(POWERDEVIL) << "Loading profile for unplugged AC"; } } config = m_profilesConfig.data()->group(profileId); qCDebug(POWERDEVIL) << "Activity is not forcing a profile"; } // Release any special inhibitions { QHash::iterator i = m_sessionActivityInhibit.begin(); while (i != m_sessionActivityInhibit.end()) { PolicyAgent::instance()->ReleaseInhibition(i.value()); i = m_sessionActivityInhibit.erase(i); } i = m_screenActivityInhibit.begin(); while (i != m_screenActivityInhibit.end()) { PolicyAgent::instance()->ReleaseInhibition(i.value()); i = m_screenActivityInhibit.erase(i); } } if (!config.isValid()) { qCWarning(POWERDEVIL) << "Profile " << profileId << "has been selected but does not exist."; return; } // Check: do we need to change profile at all? if (m_currentProfile == profileId && !force) { // No, let's leave things as they are qCDebug(POWERDEVIL) << "Skipping action reload routine as profile has not changed"; // Do we need to force a wakeup? if (m_pendingWakeupEvent) { // Fake activity at this stage, when no timeouts are registered onResumingFromIdle(); m_pendingWakeupEvent = false; } } else { // First of all, let's clean the old actions. This will also call the onProfileUnload callback ActionPool::instance()->unloadAllActiveActions(); // Do we need to force a wakeup? if (m_pendingWakeupEvent) { // Fake activity at this stage, when no timeouts are registered onResumingFromIdle(); m_pendingWakeupEvent = false; } // Cool, now let's load the needed actions - Q_FOREACH (const QString &actionName, config.groupList()) { + const auto groupList = config.groupList(); + for (const QString &actionName : groupList) { Action *action = ActionPool::instance()->loadAction(actionName, config.group(actionName), this); if (action) { action->onProfileLoad(); } else { // Ouch, error. But let's just warn and move on anyway //TODO Maybe Remove from the configuration if unsupported qCWarning(POWERDEVIL) << "The profile " << profileId << "tried to activate" << actionName << "a non-existent action. This is usually due to an installation problem," " a configuration problem, or because the action is not supported"; } } // We are now on a different profile m_currentProfile = profileId; Q_EMIT profileChanged(m_currentProfile); } // Now... any special behaviors we'd like to consider? if (activityConfig.readEntry("mode", "None") == QStringLiteral("SpecialBehavior")) { qCDebug(POWERDEVIL) << "Activity has special behaviors"; KConfigGroup behaviorGroup = activityConfig.group("SpecialBehavior"); if (behaviorGroup.readEntry("performAction", false)) { // Let's override the configuration for this action at all times ActionPool::instance()->loadAction(QStringLiteral("SuspendSession"), behaviorGroup.group("ActionConfig"), this); qCDebug(POWERDEVIL) << "Activity overrides suspend session action"; // debug hence not sleep } if (behaviorGroup.readEntry("noSuspend", false)) { qCDebug(POWERDEVIL) << "Activity triggers a suspend inhibition"; // debug hence not sleep // Trigger a special inhibition - if we don't have one yet if (!m_sessionActivityInhibit.contains(activity)) { int cookie = PolicyAgent::instance()->AddInhibition(PolicyAgent::InterruptSession, i18n("Activity Manager"), i18n("This activity's policies prevent the system from going to sleep")); m_sessionActivityInhibit.insert(activity, cookie); } } if (behaviorGroup.readEntry("noScreenManagement", false)) { qCDebug(POWERDEVIL) << "Activity triggers a screen management inhibition"; // Trigger a special inhibition - if we don't have one yet if (!m_screenActivityInhibit.contains(activity)) { int cookie = PolicyAgent::instance()->AddInhibition(PolicyAgent::ChangeScreenSettings, i18n("Activity Manager"), i18n("This activity's policies prevent screen power management")); m_screenActivityInhibit.insert(activity, cookie); } } } // If the lid is closed, retrigger the lid close signal // so that "switching profile then closing the lid" has the same result as // "closing lid then switching profile". if (m_backend->isLidClosed()) { Q_EMIT m_backend->buttonPressed(PowerDevil::BackendInterface::LidClose); } } void Core::onDeviceAdded(const QString &udi) { if (m_batteriesPercent.contains(udi) || m_peripheralBatteriesPercent.contains(udi)) { // We already know about this device return; } using namespace Solid; Device device(udi); Battery *b = qobject_cast(device.asDeviceInterface(DeviceInterface::Battery)); if (!b) { return; } connect(b, &Battery::chargePercentChanged, this, &Core::onBatteryChargePercentChanged); connect(b, &Battery::chargeStateChanged, this, &Core::onBatteryChargeStateChanged); qCDebug(POWERDEVIL) << "Battery with UDI" << udi << "was detected"; if (b->isPowerSupply()) { m_batteriesPercent[udi] = b->chargePercent(); m_batteriesCharged[udi] = (b->chargeState() == Solid::Battery::FullyCharged); } else { // non-power supply batteries are treated separately m_peripheralBatteriesPercent[udi] = b->chargePercent(); // notify the user about the empty mouse/keyboard when plugging it in; don't when // notifications aren't ready yet so we avoid showing them ontop of ksplash; // also we'll notify about all devices when notifications are ready anyway if (m_notificationsReady) { emitBatteryChargePercentNotification(b->chargePercent(), 1000 /* so current is always lower than previous */, udi); } } // If a new battery has been added, let's clear some pending suspend actions if the new global batteries percentage is // higher than the battery critical level. (See bug 329537) if (m_criticalBatteryTimer->isActive() && currentChargePercent() > PowerDevilSettings::batteryCriticalLevel()) { m_criticalBatteryTimer->stop(); if (m_criticalBatteryNotification) { m_criticalBatteryNotification->close(); } emitRichNotification(QStringLiteral("pluggedin"), i18n("Extra Battery Added"), i18n("The computer will no longer go to sleep.")); } } void Core::onDeviceRemoved(const QString &udi) { if (!m_batteriesPercent.contains(udi) && !m_peripheralBatteriesPercent.contains(udi)) { // We don't know about this device return; } using namespace Solid; Device device(udi); Battery *b = qobject_cast(device.asDeviceInterface(DeviceInterface::Battery)); disconnect(b, &Battery::chargePercentChanged, this, &Core::onBatteryChargePercentChanged); disconnect(b, &Battery::chargeStateChanged, this, &Core::onBatteryChargeStateChanged); qCDebug(POWERDEVIL) << "Battery with UDI" << udi << "has been removed"; m_batteriesPercent.remove(udi); m_peripheralBatteriesPercent.remove(udi); m_batteriesCharged.remove(udi); } void Core::emitNotification(const QString &evid, const QString &message, const QString &iconname) { if (!iconname.isEmpty()) { KNotification::event(evid, message, QIcon::fromTheme(iconname).pixmap(48,48), nullptr, KNotification::CloseOnTimeout, QStringLiteral("powerdevil")); } else { KNotification::event(evid, message, QPixmap(), nullptr, KNotification::CloseOnTimeout, QStringLiteral("powerdevil")); } } void Core::emitNotification(const QString &eventId, const QString &title, const QString &message, const QString &iconName) { KNotification::event(eventId, title, message, iconName, nullptr, KNotification::CloseOnTimeout, QStringLiteral("powerdevil")); } void Core::emitRichNotification(const QString &evid, const QString &title, const QString &message) { KNotification::event(evid, title, message, QPixmap(), nullptr, KNotification::CloseOnTimeout, QStringLiteral("powerdevil")); } bool Core::emitBatteryChargePercentNotification(int currentPercent, int previousPercent, const QString &udi) { using namespace Solid; Device device(udi); Battery *b = qobject_cast(device.asDeviceInterface(DeviceInterface::Battery)); if (b && !b->isPowerSupply()) { // if you leave the device out of reach or it has not been initialized yet // it won't be "there" and report 0%, don't show anything in this case if (!b->isPresent() || b->chargeState() != Battery::Discharging) { return false; } if (currentPercent <= PowerDevilSettings::peripheralBatteryLowLevel() && previousPercent > PowerDevilSettings::peripheralBatteryLowLevel()) { QString name = device.product(); if (!device.vendor().isEmpty()) { name = i18nc("%1 is vendor name, %2 is product name", "%1 %2", device.vendor(), device.product()); } QString title; QString msg; QString icon; switch(b->type()) { case Battery::MouseBattery: title = i18n("Mouse Battery Low (%1% Remaining)", currentPercent); msg = i18nc("Placeholder is device name", "The battery in (\"%1\") is running low, and the device may turn off at any time. " "Please recharge or replace the battery.", name); icon = QStringLiteral("input-mouse"); break; case Battery::KeyboardBattery: title = i18n("Keyboard Battery Low (%1% Remaining)", currentPercent); msg = i18nc("Placeholder is device name", "The battery in (\"%1\") is running low, and the device may turn off at any time. " "Please recharge or replace the battery.", name); icon = QStringLiteral("input-keyboard"); break; default: title = i18nc("The battery in an external device", "Device Battery Low (%1% Remaining)", currentPercent); msg = i18nc("Placeholder is device name", "The battery in (\"%1\") is running low, and the device may turn off at any time. " "Please recharge or replace the battery.", name); icon = QStringLiteral("battery-caution"); break; } emitNotification(QStringLiteral("lowperipheralbattery"), title, msg, icon); return true; } return false; } if (m_backend->acAdapterState() == BackendInterface::Plugged) { return false; } if (currentPercent <= PowerDevilSettings::batteryCriticalLevel() && previousPercent > PowerDevilSettings::batteryCriticalLevel()) { handleCriticalBattery(currentPercent); return true; } else if (currentPercent <= PowerDevilSettings::batteryLowLevel() && previousPercent > PowerDevilSettings::batteryLowLevel()) { emitRichNotification(QStringLiteral("lowbattery"), i18n("Battery Low (%1% Remaining)", currentPercent), i18n("Battery running low - to continue using your computer, plug it in or shut it down and change the battery.")); return true; } return false; } void Core::handleCriticalBattery(int percent) { // no parent, but it won't leak, since it will be closed both in case of timeout or direct action m_criticalBatteryNotification = new KNotification(QStringLiteral("criticalbattery"), KNotification::Persistent, nullptr); m_criticalBatteryNotification->setComponentName(QStringLiteral("powerdevil")); m_criticalBatteryNotification->setTitle(i18n("Battery Critical (%1% Remaining)", percent)); const QStringList actions = {i18nc("Cancel timeout that will automatically put system to sleep because of low battery", "Cancel")}; connect(m_criticalBatteryNotification.data(), &KNotification::action1Activated, this, [this] { m_criticalBatteryTimer->stop(); m_criticalBatteryNotification->close(); }); switch (PowerDevilSettings::batteryCriticalAction()) { case PowerDevil::BundledActions::SuspendSession::ShutdownMode: m_criticalBatteryNotification->setText(i18n("Battery level critical. Your computer will shut down in 60 seconds.")); m_criticalBatteryNotification->setActions(actions); m_criticalBatteryTimer->start(); break; case PowerDevil::BundledActions::SuspendSession::ToDiskMode: m_criticalBatteryNotification->setText(i18n("Battery level critical. Your computer will enter hibernation mode in 60 seconds.")); m_criticalBatteryNotification->setActions(actions); m_criticalBatteryTimer->start(); break; case PowerDevil::BundledActions::SuspendSession::ToRamMode: m_criticalBatteryNotification->setText(i18n("Battery level critical. Your computer will go to sleep in 60 seconds.")); m_criticalBatteryNotification->setActions(actions); m_criticalBatteryTimer->start(); break; default: m_criticalBatteryNotification->setText(i18n("Battery level critical. Please save your work.")); // no timer, no actions break; } m_criticalBatteryNotification->sendEvent(); } void Core::onAcAdapterStateChanged(PowerDevil::BackendInterface::AcAdapterState state) { qCDebug(POWERDEVIL); // Post request for faking an activity event - usually adapters don't plug themselves out :) m_pendingWakeupEvent = true; loadProfile(); if (state == BackendInterface::Plugged) { // If the AC Adaptor has been plugged in, let's clear some pending suspend actions if (m_criticalBatteryTimer->isActive()) { m_criticalBatteryTimer->stop(); if (m_criticalBatteryNotification) { m_criticalBatteryNotification->close(); } emitRichNotification(QStringLiteral("pluggedin"), i18n("AC Adapter Plugged In"), i18n("The computer will no longer go to sleep.")); } else { emitRichNotification(QStringLiteral("pluggedin"), i18n("Running on AC power"), i18n("The power adapter has been plugged in.")); } } else if (state == BackendInterface::Unplugged) { emitRichNotification(QStringLiteral("unplugged"), i18n("Running on Battery Power"), i18n("The power adapter has been unplugged.")); } } void Core::onBatteryChargePercentChanged(int percent, const QString &udi) { if (m_peripheralBatteriesPercent.contains(udi)) { const int previousPercent = m_peripheralBatteriesPercent.value(udi); m_peripheralBatteriesPercent[udi] = percent; if (percent < previousPercent) { emitBatteryChargePercentNotification(percent, previousPercent, udi); } return; } // Compute the previous and current global percentage const int previousPercent = currentChargePercent(); const int currentPercent = previousPercent - (m_batteriesPercent[udi] - percent); // Update the battery percentage m_batteriesPercent[udi] = percent; if (currentPercent < previousPercent) { if (emitBatteryChargePercentNotification(currentPercent, previousPercent, udi)) { // Only refresh status if a notification has actually been emitted loadProfile(); } } } void Core::onBatteryChargeStateChanged(int state, const QString &udi) { if (!m_batteriesCharged.contains(udi)) { return; } bool previousCharged = true; for (auto i = m_batteriesCharged.constBegin(); i != m_batteriesCharged.constEnd(); ++i) { if (!i.value()) { previousCharged = false; break; } } m_batteriesCharged[udi] = (state == Solid::Battery::FullyCharged); if (m_backend->acAdapterState() != BackendInterface::Plugged) { return; } bool currentCharged = true; for (auto i = m_batteriesCharged.constBegin(); i != m_batteriesCharged.constEnd(); ++i) { if (!i.value()) { currentCharged = false; break; } } if (!previousCharged && currentCharged) { emitRichNotification(QStringLiteral("fullbattery"), i18n("Charging Complete"), i18n("Battery now fully charged.")); loadProfile(); } } void Core::onCriticalBatteryTimerExpired() { if (m_criticalBatteryNotification) { m_criticalBatteryNotification->close(); } // Do that only if we're not on AC if (m_backend->acAdapterState() == BackendInterface::Unplugged) { // We consider this as a very special button PowerDevil::Action *helperAction = ActionPool::instance()->loadAction(QStringLiteral("HandleButtonEvents"), KConfigGroup(), this); if (helperAction) { QVariantMap args; args[QStringLiteral("Button")] = 32; args[QStringLiteral("Type")] = QVariant::fromValue(PowerDevilSettings::batteryCriticalAction()); args[QStringLiteral("Explicit")] = true; helperAction->trigger(args); } } } void Core::onBatteryRemainingTimeChanged(qulonglong time) { Q_EMIT batteryRemainingTimeChanged(time); } void Core::onKIdleTimeoutReached(int identifier, int msec) { // Find which action(s) requested this idle timeout for (auto i = m_registeredActionTimeouts.constBegin(), end = m_registeredActionTimeouts.constEnd(); i != end; ++i) { if (i.value().contains(identifier)) { i.key()->onIdleTimeout(msec); // And it will need to be awaken m_pendingResumeFromIdleActions.insert(i.key()); break; } } // Catch the next resume event if some actions require it if (!m_pendingResumeFromIdleActions.isEmpty()) { KIdleTime::instance()->catchNextResumeEvent(); } } void Core::onLidClosedChanged(bool closed) { Q_EMIT lidClosedChanged(closed); } void Core::onAboutToSuspend() { if (PowerDevilSettings::pausePlayersOnSuspend()) { qCDebug(POWERDEVIL) << "Pausing all media players before sleep"; QDBusPendingCall listNamesCall = QDBusConnection::sessionBus().interface()->asyncCall(QStringLiteral("ListNames")); QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(listNamesCall, this); connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; watcher->deleteLater(); if (reply.isError()) { qCWarning(POWERDEVIL) << "Failed to fetch list of DBus service names for pausing players on entering sleep" << reply.error().message(); return; } const QStringList &services = reply.value(); for (const QString &serviceName : services) { if (!serviceName.startsWith(QLatin1String("org.mpris.MediaPlayer2."))) { continue; } qCDebug(POWERDEVIL) << "Pausing media player with service name" << serviceName; QDBusMessage pauseMsg = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/org/mpris/MediaPlayer2"), QStringLiteral("org.mpris.MediaPlayer2.Player"), QStringLiteral("Pause")); QDBusConnection::sessionBus().asyncCall(pauseMsg); } }); } } void Core::registerActionTimeout(Action* action, int timeout) { // Register the timeout with KIdleTime int identifier = KIdleTime::instance()->addIdleTimeout(timeout); // Add the identifier to the action hash QList< int > timeouts = m_registeredActionTimeouts[action]; timeouts.append(identifier); m_registeredActionTimeouts[action] = timeouts; } void Core::unregisterActionTimeouts(Action* action) { // Clear all timeouts from the action - QList< int > timeoutsToClean = m_registeredActionTimeouts[action]; + const QList< int > timeoutsToClean = m_registeredActionTimeouts[action]; - Q_FOREACH (int id, timeoutsToClean) { + for (int id : timeoutsToClean) { KIdleTime::instance()->removeIdleTimeout(id); } m_registeredActionTimeouts.remove(action); } int Core::currentChargePercent() const { int chargePercent = 0; for (auto it = m_batteriesPercent.constBegin(); it != m_batteriesPercent.constEnd(); ++it) { chargePercent += it.value(); } return chargePercent; } void Core::onResumingFromIdle() { KIdleTime::instance()->simulateUserActivity(); // Wake up the actions in which an idle action was triggered std::for_each(m_pendingResumeFromIdleActions.cbegin(), m_pendingResumeFromIdleActions.cend(), std::mem_fn(&PowerDevil::Action::onWakeupFromIdle)); m_pendingResumeFromIdleActions.clear(); } void Core::onNotificationTimeout() { // cannot connect QTimer::singleShot directly to the other method onServiceRegistered(QString()); } void Core::onServiceRegistered(const QString &service) { Q_UNUSED(service); if (m_notificationsReady) { return; } bool needsRefresh = false; // show warning about low batteries right on session startup, force it to show // by making sure the "old" percentage (that magic number) is always higher than the current one if (emitBatteryChargePercentNotification(currentChargePercent(), 1000)) { needsRefresh = true; } // now also emit notifications for all peripheral batteries for (auto it = m_peripheralBatteriesPercent.constBegin(), end = m_peripheralBatteriesPercent.constEnd(); it != end; ++it) { if (emitBatteryChargePercentNotification(it.value() /*currentPercent*/, 1000, it.key() /*udi*/)) { needsRefresh = true; } } // need to refresh status to prevent the notification from showing again when charge percentage changes if (needsRefresh) { refreshStatus(); } m_notificationsReady = true; if (m_notificationsWatcher) { delete m_notificationsWatcher; m_notificationsWatcher = nullptr; } } BackendInterface* Core::backend() { return m_backend; } bool Core::isLidClosed() const { return m_backend->isLidClosed(); } bool Core::isLidPresent() const { return m_backend->isLidPresent(); } bool Core::hasDualGpu() const { return m_hasDualGpu; } qulonglong Core::batteryRemainingTime() const { return m_backend->batteryRemainingTime(); } uint Core::backendCapabilities() { return m_backend->capabilities(); } } diff --git a/daemon/powerdevilpolicyagent.cpp b/daemon/powerdevilpolicyagent.cpp index 30ff26e1..c888eba9 100644 --- a/daemon/powerdevilpolicyagent.cpp +++ b/daemon/powerdevilpolicyagent.cpp @@ -1,691 +1,691 @@ /*************************************************************************** * Copyright (C) 2010 by Dario Freddi * * Copyright (C) 2012 Lukáš Tinkl * * Copyright (C) 2016 Kai Uwe Broulik * * * * 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 #include #include #include #include #include #include #include #include #include #include #include #include "powerdevilpolicyagent.h" #include "powerdevil_debug.h" #include "screenlocker_interface.h" struct NamedDBusObjectPath { QString name; QDBusObjectPath path; }; // Marshall the NamedDBusObjectPath data into a D-Bus argument QDBusArgument &operator<<(QDBusArgument &argument, const NamedDBusObjectPath &namedPath) { argument.beginStructure(); argument << namedPath.name << namedPath.path; argument.endStructure(); return argument; } // Retrieve the NamedDBusObjectPath data from the D-Bus argument const QDBusArgument &operator>>(const QDBusArgument &argument, NamedDBusObjectPath &namedPath) { argument.beginStructure(); argument >> namedPath.name >> namedPath.path; argument.endStructure(); return argument; } Q_DECLARE_METATYPE(NamedDBusObjectPath) Q_DECLARE_METATYPE(InhibitionInfo) Q_DECLARE_METATYPE(QList) namespace PowerDevil { static const QString SCREEN_LOCKER_SERVICE_NAME = QStringLiteral("org.freedesktop.ScreenSaver"); class PolicyAgentHelper { public: PolicyAgentHelper() : q(nullptr) { } ~PolicyAgentHelper() { delete q; } PolicyAgent *q; }; Q_GLOBAL_STATIC(PolicyAgentHelper, s_globalPolicyAgent) PolicyAgent *PolicyAgent::instance() { if (!s_globalPolicyAgent->q) { new PolicyAgent; } return s_globalPolicyAgent->q; } PolicyAgent::PolicyAgent(QObject* parent) : QObject(parent) , m_screenLockerWatcher(new QDBusServiceWatcher(this)) , m_sdAvailable(false) , m_systemdInhibitFd(-1) , m_ckAvailable(false) , m_sessionIsBeingInterrupted(false) , m_lastCookie(0) , m_busWatcher(new QDBusServiceWatcher(this)) , m_sdWatcher(new QDBusServiceWatcher(this)) , m_ckWatcher(new QDBusServiceWatcher(this)) , m_wasLastActiveSession(false) { Q_ASSERT(!s_globalPolicyAgent->q); s_globalPolicyAgent->q = this; } PolicyAgent::~PolicyAgent() { } void PolicyAgent::init() { qDBusRegisterMetaType(); qDBusRegisterMetaType>(); // Watch over the systemd service m_sdWatcher.data()->setConnection(QDBusConnection::systemBus()); m_sdWatcher.data()->setWatchMode(QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration); m_sdWatcher.data()->addWatchedService(SYSTEMD_LOGIN1_SERVICE); connect(m_sdWatcher.data(), SIGNAL(serviceRegistered(QString)), this, SLOT(onSessionHandlerRegistered(QString))); connect(m_sdWatcher.data(), SIGNAL(serviceUnregistered(QString)), this, SLOT(onSessionHandlerUnregistered(QString))); // If it's up and running already, let's cache it if (QDBusConnection::systemBus().interface()->isServiceRegistered(SYSTEMD_LOGIN1_SERVICE)) { onSessionHandlerRegistered(SYSTEMD_LOGIN1_SERVICE); } // Watch over the ConsoleKit service m_ckWatcher.data()->setConnection(QDBusConnection::sessionBus()); m_ckWatcher.data()->setWatchMode(QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration); m_ckWatcher.data()->addWatchedService(CONSOLEKIT_SERVICE); connect(m_ckWatcher.data(), SIGNAL(serviceRegistered(QString)), this, SLOT(onSessionHandlerRegistered(QString))); connect(m_ckWatcher.data(), SIGNAL(serviceUnregistered(QString)), this, SLOT(onSessionHandlerUnregistered(QString))); // If it's up and running already, let's cache it if (QDBusConnection::systemBus().interface()->isServiceRegistered(CONSOLEKIT_SERVICE)) { onSessionHandlerRegistered(CONSOLEKIT_SERVICE); } // Now set up our service watcher m_busWatcher.data()->setConnection(QDBusConnection::sessionBus()); m_busWatcher.data()->setWatchMode(QDBusServiceWatcher::WatchForUnregistration); connect(m_busWatcher.data(), SIGNAL(serviceUnregistered(QString)), this, SLOT(onServiceUnregistered(QString))); // Setup the screen locker watcher and check whether the screen is currently locked connect(m_screenLockerWatcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &PolicyAgent::onScreenLockerOwnerChanged); m_screenLockerWatcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange); m_screenLockerWatcher->addWatchedService(SCREEN_LOCKER_SERVICE_NAME); // async variant of QDBusConnectionInterface::serviceOwner ... auto msg = QDBusMessage::createMethodCall( QStringLiteral("org.freedesktop.DBus"), QStringLiteral("/"), QStringLiteral("org.freedesktop.DBus"), QStringLiteral("GetNameOwner") ); msg.setArguments({SCREEN_LOCKER_SERVICE_NAME}); auto *watcher = new QDBusPendingCallWatcher(QDBusConnection::sessionBus().asyncCall(msg), this); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; if (!reply.isError()) { onScreenLockerOwnerChanged(SCREEN_LOCKER_SERVICE_NAME, {}, reply.value()); } watcher->deleteLater(); }); } QString PolicyAgent::getNamedPathProperty(const QString &path, const QString &iface, const QString &prop) const { QDBusMessage message = QDBusMessage::createMethodCall(SYSTEMD_LOGIN1_SERVICE, path, QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Get")); message << iface << prop; QDBusMessage reply = QDBusConnection::systemBus().call(message); QVariantList args = reply.arguments(); if (!args.isEmpty()) { NamedDBusObjectPath namedPath; args.at(0).value().variant().value() >> namedPath; return namedPath.path.path(); } return QString(); } void PolicyAgent::onSessionHandlerRegistered(const QString & serviceName) { if (serviceName == SYSTEMD_LOGIN1_SERVICE) { m_sdAvailable = true; qRegisterMetaType(); qDBusRegisterMetaType(); // get the current session m_managerIface.reset(new QDBusInterface(SYSTEMD_LOGIN1_SERVICE, SYSTEMD_LOGIN1_PATH, SYSTEMD_LOGIN1_MANAGER_IFACE, QDBusConnection::systemBus())); if (!m_managerIface.data()->isValid()) { qCDebug(POWERDEVIL) << "Can't connect to systemd"; m_sdAvailable = false; return; } QDBusPendingReply session = m_managerIface.data()->asyncCall(QLatin1String("GetSessionByPID"), (quint32) QCoreApplication::applicationPid()); session.waitForFinished(); if (!session.isValid()) { qCDebug(POWERDEVIL) << "The session is not registered with systemd"; m_sdAvailable = false; return; } QString sessionPath = session.value().path(); qCDebug(POWERDEVIL) << "Session path:" << sessionPath; m_sdSessionInterface = new QDBusInterface(SYSTEMD_LOGIN1_SERVICE, sessionPath, SYSTEMD_LOGIN1_SESSION_IFACE, QDBusConnection::systemBus(), this); if (!m_sdSessionInterface.data()->isValid()) { // As above qCDebug(POWERDEVIL) << "Can't contact session iface"; m_sdAvailable = false; delete m_sdSessionInterface.data(); return; } // now let's obtain the seat QString seatPath = getNamedPathProperty(sessionPath, SYSTEMD_LOGIN1_SESSION_IFACE, "Seat"); if (seatPath.isEmpty() || seatPath == "/") { qCDebug(POWERDEVIL) << "Unable to associate systemd session with a seat" << seatPath; m_sdAvailable = false; return; } // get the current seat m_sdSeatInterface = new QDBusInterface(SYSTEMD_LOGIN1_SERVICE, seatPath, SYSTEMD_LOGIN1_SEAT_IFACE, QDBusConnection::systemBus(), this); if (!m_sdSeatInterface.data()->isValid()) { // As above qCDebug(POWERDEVIL) << "Can't contact seat iface"; m_sdAvailable = false; delete m_sdSeatInterface.data(); return; } // finally get the active session path and watch for its changes m_activeSessionPath = getNamedPathProperty(seatPath, SYSTEMD_LOGIN1_SEAT_IFACE, "ActiveSession"); qCDebug(POWERDEVIL) << "ACTIVE SESSION PATH:" << m_activeSessionPath; QDBusConnection::systemBus().connect(SYSTEMD_LOGIN1_SERVICE, seatPath, "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(onActiveSessionChanged(QString,QVariantMap,QStringList))); onActiveSessionChanged(m_activeSessionPath); setupSystemdInhibition(); qCDebug(POWERDEVIL) << "systemd support initialized"; } else if (serviceName == CONSOLEKIT_SERVICE) { m_ckAvailable = true; // Otherwise, let's ask ConsoleKit m_managerIface.reset(new QDBusInterface(CONSOLEKIT_SERVICE, CONSOLEKIT_MANAGER_PATH, CONSOLEKIT_MANAGER_IFACE, QDBusConnection::systemBus())); if (!m_managerIface.data()->isValid()) { qCDebug(POWERDEVIL) << "Can't connect to ConsoleKit"; m_ckAvailable = false; return; } QDBusPendingReply sessionPath = m_managerIface.data()->asyncCall("GetCurrentSession"); sessionPath.waitForFinished(); if (!sessionPath.isValid() || sessionPath.value().path().isEmpty()) { qCDebug(POWERDEVIL) << "The session is not registered with ck"; m_ckAvailable = false; return; } m_ckSessionInterface = new QDBusInterface(CONSOLEKIT_SERVICE, sessionPath.value().path(), "org.freedesktop.ConsoleKit.Session", QDBusConnection::systemBus()); if (!m_ckSessionInterface.data()->isValid()) { // As above qCDebug(POWERDEVIL) << "Can't contact iface"; m_ckAvailable = false; return; } // Now let's obtain the seat QDBusPendingReply< QDBusObjectPath > seatPath = m_ckSessionInterface.data()->asyncCall(QStringLiteral("GetSeatId")); seatPath.waitForFinished(); if (!seatPath.isValid() || seatPath.value().path().isEmpty()) { qCDebug(POWERDEVIL) << "Unable to associate ck session with a seat"; m_ckAvailable = false; return; } if (!QDBusConnection::systemBus().connect(CONSOLEKIT_SERVICE, seatPath.value().path(), "org.freedesktop.ConsoleKit.Seat", "ActiveSessionChanged", this, SLOT(onActiveSessionChanged(QString)))) { qCDebug(POWERDEVIL) << "Unable to connect to ActiveSessionChanged"; m_ckAvailable = false; return; } // Force triggering of active session changed QDBusMessage call = QDBusMessage::createMethodCall(CONSOLEKIT_SERVICE, seatPath.value().path(), "org.freedesktop.ConsoleKit.Seat", "GetActiveSession"); QDBusPendingReply< QDBusObjectPath > activeSession = QDBusConnection::systemBus().asyncCall(call); activeSession.waitForFinished(); onActiveSessionChanged(activeSession.value().path()); setupSystemdInhibition(); qCDebug(POWERDEVIL) << "ConsoleKit support initialized"; } else qCWarning(POWERDEVIL) << "Unhandled service registered:" << serviceName; } void PolicyAgent::onSessionHandlerUnregistered(const QString & serviceName) { if (serviceName == QLatin1String(SYSTEMD_LOGIN1_SERVICE)) { m_sdAvailable = false; delete m_sdSessionInterface.data(); } else if (serviceName == QLatin1String(CONSOLEKIT_SERVICE)) { m_ckAvailable = false; delete m_ckSessionInterface.data(); } } void PolicyAgent::onActiveSessionChanged(const QString & ifaceName, const QVariantMap & changedProps, const QStringList & invalidatedProps) { const QString key = QLatin1String("ActiveSession"); if (ifaceName == SYSTEMD_LOGIN1_SEAT_IFACE && (changedProps.contains(key) || invalidatedProps.contains(key))) { m_activeSessionPath = getNamedPathProperty(m_sdSeatInterface.data()->path(), SYSTEMD_LOGIN1_SEAT_IFACE, key); qCDebug(POWERDEVIL) << "ACTIVE SESSION PATH CHANGED:" << m_activeSessionPath; onActiveSessionChanged(m_activeSessionPath); } } void PolicyAgent::onActiveSessionChanged(const QString& activeSession) { if (activeSession.isEmpty() || activeSession == QLatin1String("/")) { qCDebug(POWERDEVIL) << "Switched to inactive session - leaving unchanged"; return; } else if ((!m_sdSessionInterface.isNull() && activeSession == m_sdSessionInterface.data()->path()) || (!m_ckSessionInterface.isNull() && activeSession == m_ckSessionInterface.data()->path())) { qCDebug(POWERDEVIL) << "Current session is now active"; if (!m_wasLastActiveSession) { m_wasLastActiveSession = true; Q_EMIT sessionActiveChanged(true); } } else { qCDebug(POWERDEVIL) << "Current session is now inactive"; if (m_wasLastActiveSession) { m_wasLastActiveSession = false; Q_EMIT sessionActiveChanged(false); } } } void PolicyAgent::onServiceUnregistered(const QString& serviceName) { // Ouch - the application quit or crashed without releasing its inhibitions. Let's fix that. // ReleaseInhibition removes the cookies from the hash, so we need to operate on a copy const auto cookieToBusService = m_cookieToBusService; for (auto it = cookieToBusService.constBegin(); it != cookieToBusService.constEnd(); ++it) { if (it.value() == serviceName) { ReleaseInhibition(it.key()); } } } PolicyAgent::RequiredPolicies PolicyAgent::unavailablePolicies() { RequiredPolicies retpolicies = None; if (!m_typesToCookie[ChangeProfile].isEmpty()) { retpolicies |= ChangeProfile; } // when screen locker is active it makes no sense to keep the screen on if (!m_screenLockerActive && !m_typesToCookie[ChangeScreenSettings].isEmpty()) { retpolicies |= ChangeScreenSettings; } if (!m_typesToCookie[InterruptSession].isEmpty()) { retpolicies |= InterruptSession; } return retpolicies; } PolicyAgent::RequiredPolicies PolicyAgent::requirePolicyCheck(PolicyAgent::RequiredPolicies policies) { if (!m_sdAvailable) { // No way to determine if we are on the current session, simply suppose we are qCDebug(POWERDEVIL) << "Can't contact systemd"; } else if (!m_sdSessionInterface.isNull()) { bool isActive = m_sdSessionInterface.data()->property("Active").toBool(); if (!isActive && !m_wasLastActiveSession) { return policies; } } if (!m_ckAvailable) { // No way to determine if we are on the current session, simply suppose we are qCDebug(POWERDEVIL) << "Can't contact ck"; } else if (!m_ckSessionInterface.isNull()) { QDBusPendingReply< bool > rp = m_ckSessionInterface.data()->asyncCall(QStringLiteral("IsActive")); rp.waitForFinished(); if (!(rp.isValid() && rp.value()) && !m_wasLastActiveSession) { return policies; } } // Ok, let's go then RequiredPolicies retpolicies = None; if (policies & ChangeProfile) { if (!m_typesToCookie[ChangeProfile].isEmpty()) { retpolicies |= ChangeProfile; } } if (policies & ChangeScreenSettings) { if (!m_typesToCookie[ChangeScreenSettings].isEmpty()) { retpolicies |= ChangeScreenSettings; } } if (policies & InterruptSession) { if (m_sessionIsBeingInterrupted || !m_typesToCookie[InterruptSession].isEmpty()) { retpolicies |= InterruptSession; } } return retpolicies; } void PolicyAgent::startSessionInterruption() { m_sessionIsBeingInterrupted = true; } void PolicyAgent::finishSessionInterruption() { m_sessionIsBeingInterrupted = false; } void PolicyAgent::onScreenLockerOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(oldOwner); if (serviceName != SCREEN_LOCKER_SERVICE_NAME) { return; } delete m_screenLockerInterface; m_screenLockerInterface = nullptr; m_screenLockerActive = false; if (!newOwner.isEmpty()) { m_screenLockerInterface = new OrgFreedesktopScreenSaverInterface(newOwner, QStringLiteral("/ScreenSaver"), QDBusConnection::sessionBus(), this); connect(m_screenLockerInterface, &OrgFreedesktopScreenSaverInterface::ActiveChanged, this, &PolicyAgent::onScreenLockerActiveChanged); auto *activeReplyWatcher = new QDBusPendingCallWatcher(m_screenLockerInterface->GetActive()); connect(activeReplyWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; if (!reply.isError()) { onScreenLockerActiveChanged(reply.value()); } watcher->deleteLater(); }); } } void PolicyAgent::onScreenLockerActiveChanged(bool active) { const auto oldPolicies = unavailablePolicies(); m_screenLockerActive = active; const auto newPolicies = unavailablePolicies(); if (oldPolicies != newPolicies) { qCDebug(POWERDEVIL) << "Screen saver active" << active << "- we have different inhibition policy now because of that"; Q_EMIT unavailablePoliciesChanged(newPolicies); } } uint PolicyAgent::addInhibitionWithExplicitDBusService(uint types, const QString &appName, const QString &reason, const QString &service) { ++m_lastCookie; const int cookie = m_lastCookie; // when the Timer below fires, m_lastCookie might be different already m_pendingInhibitions.append(cookie); qCDebug(POWERDEVIL) << "Scheduling inhibition from" << service << appName << "with cookie" << cookie << "and reason" << reason; // wait 5s before actually enforcing the inhibition // there might be short interruptions (such as receiving a message) where an app might automatically // post an inhibition but we don't want the system to constantly wakeup because of this QTimer::singleShot(5000, this, [=] { qCDebug(POWERDEVIL) << "Enforcing inhibition from" << service << appName << "with cookie" << cookie << "and reason" << reason; if (!m_pendingInhibitions.contains(cookie)) { qCDebug(POWERDEVIL) << "By the time we wanted to enforce the inhibition it was already gone; discarding it"; return; } m_cookieToAppName.insert(cookie, qMakePair(appName, reason)); if (!m_busWatcher.isNull() && !service.isEmpty()) { m_cookieToBusService.insert(cookie, service); m_busWatcher.data()->addWatchedService(service); } addInhibitionTypeHelper(cookie, static_cast< PolicyAgent::RequiredPolicies >(types)); Q_EMIT InhibitionsChanged({ {qMakePair(appName, reason)} }, {}); m_pendingInhibitions.removeOne(cookie); }); return cookie; } uint PolicyAgent::AddInhibition(uint types, const QString &appName, const QString &reason) { if (calledFromDBus()) { return addInhibitionWithExplicitDBusService(types, appName, reason, message().service()); } else { return addInhibitionWithExplicitDBusService(types, appName, reason, QString()); } } void PolicyAgent::addInhibitionTypeHelper(uint cookie, PolicyAgent::RequiredPolicies types) { // Look through all of the inhibition types bool notify = false; if (types & ChangeProfile) { // Check if we have to notify if (m_typesToCookie[ChangeProfile].isEmpty()) { qCDebug(POWERDEVIL) << "Added change profile"; notify = true; } m_typesToCookie[ChangeProfile].append(cookie); } if (types & ChangeScreenSettings) { // Check if we have to notify qCDebug(POWERDEVIL) << "Added change screen settings"; if (m_typesToCookie[ChangeScreenSettings].isEmpty()) { notify = true; } m_typesToCookie[ChangeScreenSettings].append(cookie); types |= InterruptSession; // implied by ChangeScreenSettings } if (types & InterruptSession) { // Check if we have to notify qCDebug(POWERDEVIL) << "Added interrupt session"; if (m_typesToCookie[InterruptSession].isEmpty()) { notify = true; } m_typesToCookie[InterruptSession].append(cookie); } if (notify) { // emit the signal - inhibition has changed Q_EMIT unavailablePoliciesChanged(unavailablePolicies()); } } void PolicyAgent::ReleaseInhibition(uint cookie) { qCDebug(POWERDEVIL) << "Releasing inhibition with cookie " << cookie; if (m_pendingInhibitions.contains(cookie)) { qCDebug(POWERDEVIL) << "It was only scheduled for inhibition but not enforced yet, just discarding it"; m_pendingInhibitions.removeOne(cookie); return; } Q_EMIT InhibitionsChanged(QList(), { {m_cookieToAppName.value(cookie).first} }); m_cookieToAppName.remove(cookie); QString service = m_cookieToBusService.take(cookie); if (!m_busWatcher.isNull() && !service.isEmpty() && !m_cookieToBusService.key(service)) { // no cookies from service left m_busWatcher.data()->removeWatchedService(service); } // Look through all of the inhibition types bool notify = false; if (m_typesToCookie[ChangeProfile].contains(cookie)) { m_typesToCookie[ChangeProfile].removeOne(cookie); // Check if we have to notify if (m_typesToCookie[ChangeProfile].isEmpty()) { notify = true; } } if (m_typesToCookie[ChangeScreenSettings].contains(cookie)) { m_typesToCookie[ChangeScreenSettings].removeOne(cookie); // Check if we have to notify if (m_typesToCookie[ChangeScreenSettings].isEmpty()) { notify = true; } } if (m_typesToCookie[InterruptSession].contains(cookie)) { m_typesToCookie[InterruptSession].removeOne(cookie); // Check if we have to notify if (m_typesToCookie[InterruptSession].isEmpty()) { notify = true; } } if (notify) { // Emit the signal - inhibition has changed Q_EMIT unavailablePoliciesChanged(unavailablePolicies()); } } QList PolicyAgent::ListInhibitions() const { return m_cookieToAppName.values(); } bool PolicyAgent::HasInhibition(/*PolicyAgent::RequiredPolicies*/ uint types) { return requirePolicyCheck(static_cast(types)) != PolicyAgent::None; } void PolicyAgent::releaseAllInhibitions() { - QList< uint > allCookies = m_cookieToAppName.keys(); - Q_FOREACH (uint cookie, allCookies) { + const QList< uint > allCookies = m_cookieToAppName.keys(); + for (uint cookie : allCookies) { ReleaseInhibition(cookie); } } void PolicyAgent::setupSystemdInhibition() { if (m_systemdInhibitFd.fileDescriptor() != -1) return; if (!m_managerIface) return; // inhibit systemd/ConsoleKit2 handling of power/sleep/lid buttons // http://www.freedesktop.org/wiki/Software/systemd/inhibit // http://consolekit2.github.io/ConsoleKit2/#Manager.Inhibit qCDebug(POWERDEVIL) << "fd passing available:" << bool(m_managerIface.data()->connection().connectionCapabilities() & QDBusConnection::UnixFileDescriptorPassing); QVariantList args; args << QStringLiteral("handle-power-key:handle-suspend-key:handle-hibernate-key:handle-lid-switch"); // what args << QStringLiteral("PowerDevil"); // who args << QStringLiteral("KDE handles power events"); // why args << QStringLiteral("block"); // mode QDBusPendingReply desc = m_managerIface.data()->asyncCallWithArgumentList(QStringLiteral("Inhibit"), args); desc.waitForFinished(); if (desc.isValid()) { m_systemdInhibitFd = desc.value(); qCDebug(POWERDEVIL) << "systemd powersave events handling inhibited, descriptor:" << m_systemdInhibitFd.fileDescriptor(); } else qCWarning(POWERDEVIL) << "failed to inhibit systemd powersave handling"; } } diff --git a/daemon/powerdevilprofilegenerator.cpp b/daemon/powerdevilprofilegenerator.cpp index 2b1573e8..8bd4a81e 100644 --- a/daemon/powerdevilprofilegenerator.cpp +++ b/daemon/powerdevilprofilegenerator.cpp @@ -1,152 +1,153 @@ /*************************************************************************** * Copyright (C) 2010 by Dario Freddi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "powerdevilprofilegenerator.h" #include #include #include #include #include namespace PowerDevil { void ProfileGenerator::generateProfiles(bool toRam, bool toDisk) { // Change critical action if default (hibernate) is unavailable if (!toDisk) { if (!toRam) { PowerDevilSettings::setBatteryCriticalAction(0); } else { PowerDevilSettings::setBatteryCriticalAction(1); } PowerDevilSettings::self()->save(); } // Ok, let's get our config file. KSharedConfigPtr profilesConfig = KSharedConfig::openConfig("powermanagementprofilesrc", KConfig::SimpleConfig); // And clear it - Q_FOREACH (const QString &group, profilesConfig->groupList()) { + const QStringList groupList = profilesConfig->groupList(); + for (const QString &group : groupList) { // Don't delete activity-specific settings if (group != "Activities") { profilesConfig->deleteGroup(group); } } // Let's start: AC profile before anything else KConfigGroup acProfile(profilesConfig, "AC"); acProfile.writeEntry("icon", "battery-charging"); // We want to dim the screen after a while, definitely { KConfigGroup dimDisplay(&acProfile, "DimDisplay"); dimDisplay.writeEntry< int >("idleTime", 300000); } // Show the dialog when power button is pressed and suspend on suspend button pressed and lid closed (if supported) { KConfigGroup handleButtonEvents(&acProfile, "HandleButtonEvents"); handleButtonEvents.writeEntry< uint >("powerButtonAction", LogoutDialogMode); if (toRam) { handleButtonEvents.writeEntry< uint >("lidAction", ToRamMode); } else { handleButtonEvents.writeEntry< uint >("lidAction", TurnOffScreenMode); } } // And we also want to turn off the screen after another while { KConfigGroup dpmsControl(&acProfile, "DPMSControl"); dpmsControl.writeEntry< uint >("idleTime", 600); } // Powersave KConfigGroup batteryProfile(profilesConfig, "Battery"); batteryProfile.writeEntry("icon", "battery-060"); // We want to dim the screen after a while, definitely { KConfigGroup dimDisplay(&batteryProfile, "DimDisplay"); dimDisplay.writeEntry< int >("idleTime", 120000); } // Show the dialog when power button is pressed and suspend on suspend button pressed and lid closed (if supported) { KConfigGroup handleButtonEvents(&batteryProfile, "HandleButtonEvents"); handleButtonEvents.writeEntry< uint >("powerButtonAction", LogoutDialogMode); if (toRam) { handleButtonEvents.writeEntry< uint >("lidAction", ToRamMode); } else { handleButtonEvents.writeEntry< uint >("lidAction", TurnOffScreenMode); } } // We want to turn off the screen after another while { KConfigGroup dpmsControl(&batteryProfile, "DPMSControl"); dpmsControl.writeEntry< uint >("idleTime", 300); } // Last but not least, we want to suspend after a rather long period of inactivity if (toRam) { KConfigGroup suspendSession(&batteryProfile, "SuspendSession"); suspendSession.writeEntry< uint >("idleTime", 600000); suspendSession.writeEntry< uint >("suspendType", ToRamMode); } // Ok, now for aggressive powersave KConfigGroup lowBatteryProfile(profilesConfig, "LowBattery"); lowBatteryProfile.writeEntry("icon", "battery-low"); // Less brightness. { KConfigGroup brightnessControl(&lowBatteryProfile, "BrightnessControl"); brightnessControl.writeEntry< int >("value", 30); } // We want to dim the screen after a while, definitely { KConfigGroup dimDisplay(&lowBatteryProfile, "DimDisplay"); dimDisplay.writeEntry< int >("idleTime", 60000); } // Show the dialog when power button is pressed and suspend on suspend button pressed and lid closed (if supported) { KConfigGroup handleButtonEvents(&lowBatteryProfile, "HandleButtonEvents"); handleButtonEvents.writeEntry< uint >("powerButtonAction", LogoutDialogMode); if (toRam) { handleButtonEvents.writeEntry< uint >("lidAction", ToRamMode); } else { handleButtonEvents.writeEntry< uint >("lidAction", TurnOffScreenMode); } } // We want to turn off the screen after another while { KConfigGroup dpmsControl(&lowBatteryProfile, "DPMSControl"); dpmsControl.writeEntry< uint >("idleTime", 120); } // Last but not least, we want to suspend after a rather long period of inactivity if (toRam) { KConfigGroup suspendSession(&lowBatteryProfile, "SuspendSession"); suspendSession.writeEntry< uint >("idleTime", 300000); suspendSession.writeEntry< uint >("suspendType", ToRamMode); } // Save and be happy profilesConfig->sync(); } } diff --git a/kcmodule/activities/activitypage.cpp b/kcmodule/activities/activitypage.cpp index 83fa03ec..100fbdcc 100644 --- a/kcmodule/activities/activitypage.cpp +++ b/kcmodule/activities/activitypage.cpp @@ -1,249 +1,250 @@ /*************************************************************************** * Copyright (C) 2011 by Dario Freddi * * Copyright (C) 2015 by Kai Uwe Broulik * * * * 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 "activitypage.h" #include "activitywidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY(PowerDevilActivitiesKCMFactory, registerPlugin(); ) K_EXPORT_PLUGIN(PowerDevilActivitiesKCMFactory("powerdevilactivitiesconfig","powerdevil")) ActivityPage::ActivityPage(QWidget *parent, const QVariantList &args) : KCModule(nullptr, parent, args) , m_activityConsumer(new KActivities::Consumer(this)) { setButtons(Apply | Help); /*KAboutData *about = new KAboutData("powerdevilactivitiesconfig", "powerdevilactivitiesconfig", ki18n("Activities Power Management Configuration"), "", ki18n("A per-activity configurator of KDE Power Management System"), KAboutData::License_GPL, ki18n("(c), 2010 Dario Freddi"), ki18n("From this module, you can fine tune power management settings for each of your activities.")); about->addAuthor(ki18n("Dario Freddi"), ki18n("Maintainer") , "drf@kde.org", "http://drfav.wordpress.com"); setAboutData(about);*/ // Build the UI QVBoxLayout *lay = new QVBoxLayout(); // Message widget m_messageWidget = new KMessageWidget(i18n("The activity service is running with bare functionalities.\n" "Names and icons of the activities might not be available.")); m_messageWidget->setMessageType(KMessageWidget::Warning); m_messageWidget->hide(); // Tab widget (must set size here since tabs are loaded after initial layout size is calculated) m_tabWidget = new QTabWidget(); m_tabWidget->setMinimumSize(676, 474); lay->addWidget(m_messageWidget); lay->addWidget(m_tabWidget); setLayout(lay); onActivityServiceStatusChanged(m_activityConsumer->serviceStatus()); connect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &ActivityPage::onActivityServiceStatusChanged); QDBusServiceWatcher *watcher = new QDBusServiceWatcher("org.kde.Solid.PowerManagement", QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration, this); connect(watcher, SIGNAL(serviceRegistered(QString)), this, SLOT(onServiceRegistered(QString))); connect(watcher, SIGNAL(serviceUnregistered(QString)), this, SLOT(onServiceUnregistered(QString))); if (QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.Solid.PowerManagement")) { onServiceRegistered("org.kde.Solid.PowerManagement"); } else { onServiceUnregistered("org.kde.Solid.PowerManagement"); } } ActivityPage::~ActivityPage() { } void ActivityPage::load() { - Q_FOREACH (ActivityWidget *widget, m_activityWidgets) { + for (ActivityWidget *widget : qAsConst(m_activityWidgets)) { widget->load(); } Q_EMIT changed(false); } void ActivityPage::save() { - Q_FOREACH (ActivityWidget *widget, m_activityWidgets) { + for (ActivityWidget *widget : qAsConst(m_activityWidgets)) { widget->save(); } Q_EMIT changed(false); // Ask to refresh status QDBusMessage call = QDBusMessage::createMethodCall("org.kde.Solid.PowerManagement", "/org/kde/Solid/PowerManagement", "org.kde.Solid.PowerManagement", "refreshStatus"); // Perform call QDBusConnection::sessionBus().asyncCall(call); } void ActivityPage::fillUi() { } void ActivityPage::onActivityServiceStatusChanged(KActivities::Consumer::ServiceStatus status) { switch (status) { case KActivities::Consumer::Unknown: // fall through case KActivities::Consumer::NotRunning: // Create error overlay, if not present if (!m_errorOverlay) { m_errorOverlay = new ErrorOverlay(this, i18n("The activity service is not running.\n" "It is necessary to have the activity manager running " "to configure activity-specific power management behavior."), this); } break; case KActivities::Consumer::Running: if (m_previousServiceStatus != KActivities::Consumer::Running) { if (m_errorOverlay) { m_errorOverlay->deleteLater(); m_errorOverlay = nullptr; if (QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.Solid.PowerManagement")) { onServiceRegistered("org.kde.Solid.PowerManagement"); } else { onServiceUnregistered("org.kde.Solid.PowerManagement"); } } populateTabs(); } if (m_messageWidget->isVisible()) { m_messageWidget->hide(); } break; } m_previousServiceStatus = status; } void ActivityPage::populateTabs() { if (m_activityConsumer->serviceStatus() != KActivities::Consumer::Running) { return; } int index = 0; - Q_FOREACH (const QString &activity, m_activityConsumer->activities()) { + const QStringList activities = m_activityConsumer->activities(); + for (const QString &activity : activities) { KActivities::Info *info = new KActivities::Info(activity, this); const QString icon = info->icon(); const QString name = info->name(); qCDebug(POWERDEVIL) << activity << info->isValid() << info->availability(); QScrollArea *scrollArea = new QScrollArea(); scrollArea->setFrameShape(QFrame::NoFrame); scrollArea->setFrameShadow(QFrame::Plain); scrollArea->setLineWidth(0); scrollArea->setWidgetResizable(true); ActivityWidget *activityWidget = new ActivityWidget(activity); scrollArea->setWidget(activityWidget); activityWidget->load(); m_activityWidgets.append(activityWidget); connect(activityWidget, SIGNAL(changed(bool)), this, SIGNAL(changed(bool))); if (!icon.isEmpty()) { m_tabWidget->addTab(scrollArea, QIcon::fromTheme(icon), name); } else { m_tabWidget->addTab(scrollArea, name); } if (m_activityConsumer->currentActivity() == activity) { m_tabWidget->setCurrentIndex(index); } ++index; } } void ActivityPage::defaults() { KCModule::defaults(); } void ActivityPage::onServiceRegistered(const QString& service) { Q_UNUSED(service); if (m_errorOverlay) { m_errorOverlay->deleteLater(); m_errorOverlay = nullptr; } } void ActivityPage::onServiceUnregistered(const QString& service) { Q_UNUSED(service); if (m_errorOverlay) { return; } m_errorOverlay = new ErrorOverlay(this, i18n("The Power Management Service appears not to be running.\n" "This can be solved by starting or scheduling it inside \"Startup and Shutdown\""), this); } #include "activitypage.moc" diff --git a/kcmodule/activities/activitywidget.cpp b/kcmodule/activities/activitywidget.cpp index 64d4a373..0298a946 100644 --- a/kcmodule/activities/activitywidget.cpp +++ b/kcmodule/activities/activitywidget.cpp @@ -1,200 +1,202 @@ /*************************************************************************** * Copyright (C) 2011 by Dario Freddi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "activitywidget.h" #include "ui_activityWidget.h" #include "../daemon/actions/bundled/suspendsession.h" #include "powerdevilpowermanagement.h" #include #include #include #include #include #include ActivityWidget::ActivityWidget(const QString& activity, QWidget* parent) : QWidget(parent) , m_ui(new Ui::ActivityWidget) , m_profilesConfig(KSharedConfig::openConfig("powermanagementprofilesrc", KConfig::SimpleConfig | KConfig::CascadeConfig)) , m_activity(activity) , m_activityConsumer(new KActivities::Consumer(this)) , m_actionEditWidget(new ActionEditWidget(QString("Activities/%1/SeparateSettings").arg(activity))) { m_ui->setupUi(this); m_ui->separateSettingsLayout->addWidget(m_actionEditWidget); for (int i = 0; i < m_ui->specialBehaviorLayout->count(); ++i) { QWidget *widget = m_ui->specialBehaviorLayout->itemAt(i)->widget(); if (widget) { widget->setVisible(false); connect(m_ui->specialBehaviorRadio, SIGNAL(toggled(bool)), widget, SLOT(setVisible(bool))); } else { QLayout *layout = m_ui->specialBehaviorLayout->itemAt(i)->layout(); if (layout) { for (int j = 0; j < layout->count(); ++j) { QWidget *widget = layout->itemAt(j)->widget(); if (widget) { widget->setVisible(false); connect(m_ui->specialBehaviorRadio, SIGNAL(toggled(bool)), widget, SLOT(setVisible(bool))); } } } } } m_actionEditWidget->setVisible(false); m_actionEditWidget->load(); connect(m_ui->separateSettingsRadio, SIGNAL(toggled(bool)), m_actionEditWidget, SLOT(setVisible(bool))); connect(m_ui->actLikeRadio, SIGNAL(toggled(bool)), this, SLOT(setChanged())); connect(m_ui->noSettingsRadio, SIGNAL(toggled(bool)), this, SLOT(setChanged())); connect(m_ui->separateSettingsRadio, SIGNAL(toggled(bool)), this, SLOT(setChanged())); connect(m_ui->specialBehaviorRadio, SIGNAL(toggled(bool)), this, SLOT(setChanged())); connect(m_ui->actLikeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setChanged())); connect(m_ui->alwaysActionBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setChanged())); connect(m_ui->alwaysAfterSpin, SIGNAL(valueChanged(int)), this, SLOT(setChanged())); connect(m_actionEditWidget, SIGNAL(changed(bool)), this, SIGNAL(changed(bool))); } ActivityWidget::~ActivityWidget() { } void ActivityWidget::load() { KConfigGroup activitiesGroup(m_profilesConfig, "Activities"); KConfigGroup config = activitiesGroup.group(m_activity); using namespace PowerDevil::BundledActions; if (PowerDevil::PowerManagement::instance()->canSuspend()) { m_ui->alwaysActionBox->addItem(QIcon::fromTheme("system-suspend"), i18nc("Suspend to RAM", "Sleep"), (uint)SuspendSession::ToRamMode); } if (PowerDevil::PowerManagement::instance()->canHibernate()) { m_ui->alwaysActionBox->addItem(QIcon::fromTheme("system-suspend-hibernate"), i18n("Hibernate"), (uint)SuspendSession::ToDiskMode); } m_ui->alwaysActionBox->addItem(QIcon::fromTheme("system-shutdown"), i18n("Shut down"), (uint)SuspendSession::ShutdownMode); m_ui->actLikeComboBox->clear(); m_ui->actLikeComboBox->addItem(QIcon::fromTheme("battery-charging"), i18n("PC running on AC power"), "AC"); m_ui->actLikeComboBox->addItem(QIcon::fromTheme("battery-060"), i18n("PC running on battery power"), "Battery"); m_ui->actLikeComboBox->addItem(QIcon::fromTheme("battery-low"), i18n("PC running on low battery"), "LowBattery"); bool hasBattery = false; - Q_FOREACH (const Solid::Device &device, Solid::Device::listFromType(Solid::DeviceInterface::Battery, QString())) { + const auto batteries = Solid::Device::listFromType(Solid::DeviceInterface::Battery, QString()); + for (const Solid::Device &device : batteries) { const Solid::Battery *b = qobject_cast (device.asDeviceInterface(Solid::DeviceInterface::Battery)); if (b->type() == Solid::Battery::PrimaryBattery || b->type() == Solid::Battery::UpsBattery) { hasBattery = false; break; } } m_ui->actLikeRadio->setVisible(hasBattery); m_ui->actLikeComboBox->setVisible(hasBattery); - Q_FOREACH (const QString &activity, m_activityConsumer->activities()) { + const QStringList activities = m_activityConsumer->activities(); + for (const QString &activity : activities) { if (activity == m_activity) { continue; } if (activitiesGroup.group(activity).readEntry("mode", "None") == "None" || activitiesGroup.group(activity).readEntry("mode", "None") == "ActLike") { continue; } KActivities::Info *info = new KActivities::Info(activity, this); QString icon = info->icon(); QString name = i18nc("This is meant to be: Act like activity %1", "Activity \"%1\"", info->name()); m_ui->actLikeComboBox->addItem(QIcon::fromTheme(icon), name, activity); } // Proper loading routine if (config.readEntry("mode", QString()) == "ActLike") { m_ui->actLikeRadio->setChecked(true); m_ui->actLikeComboBox->setCurrentIndex(m_ui->actLikeComboBox->findData(config.readEntry("actLike", QString()))); } else if (config.readEntry("mode", QString()) == "SpecialBehavior") { m_ui->specialBehaviorRadio->setChecked(true); KConfigGroup behaviorGroup = config.group("SpecialBehavior"); m_ui->noShutdownPCBox->setChecked(behaviorGroup.readEntry("noSuspend", false)); m_ui->noShutdownScreenBox->setChecked(behaviorGroup.readEntry("noScreenManagement", false)); m_ui->alwaysBox->setChecked(behaviorGroup.readEntry("performAction", false)); KConfigGroup actionConfig = behaviorGroup.group("ActionConfig"); m_ui->alwaysActionBox->setCurrentIndex(m_ui->alwaysActionBox->findData(actionConfig.readEntry("suspendType", 0))); m_ui->alwaysAfterSpin->setValue(actionConfig.readEntry("idleTime", 600000) / 60 / 1000); } else if (config.readEntry("mode", QString()) == "SeparateSettings") { m_ui->separateSettingsRadio->setChecked(true); m_actionEditWidget->load(); } } void ActivityWidget::save() { KConfigGroup activitiesGroup(m_profilesConfig, "Activities"); KConfigGroup config = activitiesGroup.group(m_activity); if (m_ui->actLikeRadio->isChecked()) { config.writeEntry("mode", "ActLike"); config.writeEntry("actLike", m_ui->actLikeComboBox->itemData(m_ui->actLikeComboBox->currentIndex()).toString()); } else if (m_ui->specialBehaviorRadio->isChecked()) { config.writeEntry("mode", "SpecialBehavior"); KConfigGroup behaviorGroup = config.group("SpecialBehavior"); behaviorGroup.writeEntry("noSuspend", m_ui->noShutdownPCBox->isChecked()); behaviorGroup.writeEntry("noScreenManagement", m_ui->noShutdownScreenBox->isChecked()); behaviorGroup.writeEntry("performAction", m_ui->alwaysBox->isChecked()); KConfigGroup actionConfig = behaviorGroup.group("ActionConfig"); actionConfig.writeEntry("suspendType", m_ui->alwaysActionBox->itemData(m_ui->alwaysActionBox->currentIndex())); actionConfig.writeEntry("idleTime", m_ui->alwaysAfterSpin->value() * 60 * 1000); actionConfig.sync(); behaviorGroup.sync(); } else if (m_ui->separateSettingsRadio->isChecked()) { config.writeEntry("mode", "SeparateSettings"); m_actionEditWidget->save(); } else { config.writeEntry("mode", "None"); } config.sync(); } void ActivityWidget::setChanged() { Q_EMIT changed(true); } diff --git a/kcmodule/common/actioneditwidget.cpp b/kcmodule/common/actioneditwidget.cpp index 6498840b..8749f920 100644 --- a/kcmodule/common/actioneditwidget.cpp +++ b/kcmodule/common/actioneditwidget.cpp @@ -1,198 +1,198 @@ /*************************************************************************** * Copyright (C) 2008-2011 by Dario Freddi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "actioneditwidget.h" #include "actionconfigwidget.h" #include #include #include #include #include #include #include #include #include #include #include ActionEditWidget::ActionEditWidget(const QString &configName, QWidget *parent) : QWidget(parent) , m_configName(configName) { m_profilesConfig = KSharedConfig::openConfig("powermanagementprofilesrc", KConfig::SimpleConfig | KConfig::CascadeConfig); ActionConfigWidget *actionConfigWidget = new ActionConfigWidget(nullptr); QMap< int, QList > > widgets; // Load all the services - KService::List offers = KServiceTypeTrader::self()->query("PowerDevil/Action", "(Type == 'Service')"); + const KService::List offers = KServiceTypeTrader::self()->query("PowerDevil/Action", "(Type == 'Service')"); - Q_FOREACH (const KService::Ptr &offer, offers) { + for (const KService::Ptr &offer : offers) { // Does it have a runtime requirement? if (offer->property("X-KDE-PowerDevil-Action-HasRuntimeRequirement", QVariant::Bool).toBool()) { qCDebug(POWERDEVIL) << offer->name() << " has a runtime requirement"; QDBusMessage call = QDBusMessage::createMethodCall("org.kde.Solid.PowerManagement", "/org/kde/Solid/PowerManagement", "org.kde.Solid.PowerManagement", "isActionSupported"); call.setArguments(QVariantList() << offer->property("X-KDE-PowerDevil-Action-ID", QVariant::String)); QDBusPendingReply< bool > reply = QDBusConnection::sessionBus().asyncCall(call); reply.waitForFinished(); if (reply.isValid()) { if (!reply.value()) { qCDebug(POWERDEVIL) << "The action " << offer->property("X-KDE-PowerDevil-Action-ID", QVariant::String) << " appears not to be supported by the core."; continue; } } else { qCDebug(POWERDEVIL) << "There was a problem in contacting DBus!! Assuming the action is ok."; } } //try to load the specified library KPluginFactory *factory = KPluginLoader(offer->property("X-KDE-PowerDevil-Action-UIComponentLibrary", QVariant::String).toString()).factory(); if (!factory) { qCWarning(POWERDEVIL) << "KPluginFactory could not load the plugin:" << offer->property("X-KDE-PowerDevil-Action-UIComponentLibrary", QVariant::String).toString(); continue; } PowerDevil::ActionConfig *actionConfig = factory->create(); if (!actionConfig) { qCWarning(POWERDEVIL) << "KPluginFactory could not load the plugin:" << offer->property("X-KDE-PowerDevil-Action-UIComponentLibrary", QVariant::String).toString(); continue; } connect(actionConfig, SIGNAL(changed()), this, SLOT(onChanged())); QCheckBox *checkbox = new QCheckBox(offer->name()); connect(checkbox, SIGNAL(stateChanged(int)), this, SLOT(onChanged())); m_actionsHash.insert(offer->property("X-KDE-PowerDevil-Action-ID", QVariant::String).toString(), checkbox); m_actionsConfigHash.insert(offer->property("X-KDE-PowerDevil-Action-ID", QVariant::String).toString(), actionConfig); QList > offerWidgets = actionConfig->buildUi(); offerWidgets.prepend(qMakePair(QString(), checkbox)); widgets.insertMulti(100 - offer->property("X-KDE-PowerDevil-Action-ConfigPriority", QVariant::Int).toInt(), offerWidgets); } for (QMap< int, QList > >::const_iterator i = widgets.constBegin(); i != widgets.constEnd(); ++i) { actionConfigWidget->addWidgets(i.value()); } QVBoxLayout *lay = new QVBoxLayout(this); lay->addWidget(actionConfigWidget); lay->addStretch(); setLayout(lay); } ActionEditWidget::~ActionEditWidget() { } void ActionEditWidget::load() { KConfigGroup group = configGroup(); qCDebug(POWERDEVIL) << m_profilesConfig.data()->entryMap().keys(); if (!group.isValid()) { return; } qCDebug(POWERDEVIL) << "Ok, KConfigGroup ready" << group.entryMap().keys(); // Iterate over the possible actions for (QHash< QString, QCheckBox* >::const_iterator i = m_actionsHash.constBegin(); i != m_actionsHash.constEnd(); ++i) { i.value()->setChecked(group.groupList().contains(i.key())); KConfigGroup actionGroup = group.group(i.key()); m_actionsConfigHash[i.key()]->setConfigGroup(actionGroup); m_actionsConfigHash[i.key()]->load(); } Q_EMIT changed(false); } void ActionEditWidget::save() { KConfigGroup group = configGroup(); if (!group.isValid()) { qCDebug(POWERDEVIL) << "Could not perform a save operation, group is not valid!"; return; } // Iterate over the possible actions for (QHash< QString, QCheckBox* >::const_iterator i = m_actionsHash.constBegin(); i != m_actionsHash.constEnd(); ++i) { if (i.value()->isChecked()) { // Perform the actual save m_actionsConfigHash[i.key()]->save(); } else { // Erase the group group.deleteGroup(i.key()); } } group.sync(); // After saving, reload the config to make sure we'll pick up changes. m_profilesConfig.data()->reparseConfiguration(); Q_EMIT changed(false); } void ActionEditWidget::onChanged() { Q_EMIT changed(true); } QString ActionEditWidget::configName() const { return m_configName; } KConfigGroup ActionEditWidget::configGroup() { if (!m_configName.contains('/')) { return KConfigGroup(m_profilesConfig, m_configName); } else { QStringList names = m_configName.split('/'); KConfigGroup retgroup(m_profilesConfig, names.first()); QStringList::const_iterator i = names.constBegin(); ++i; while (i != names.constEnd()) { retgroup = retgroup.group(*i); ++i; } return retgroup; } } diff --git a/kcmodule/global/GeneralPage.cpp b/kcmodule/global/GeneralPage.cpp index 858441d1..e3746ce2 100644 --- a/kcmodule/global/GeneralPage.cpp +++ b/kcmodule/global/GeneralPage.cpp @@ -1,214 +1,215 @@ /*************************************************************************** * Copyright (C) 2008 by Dario Freddi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "GeneralPage.h" #include "ErrorOverlay.h" #include "PowerDevilSettings.h" #include "actions/bundled/suspendsession.h" #include "powerdevilpowermanagement.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY(PowerDevilGeneralKCMFactory, registerPlugin(); ) GeneralPage::GeneralPage(QWidget *parent, const QVariantList &args) : KCModule(nullptr, parent, args) { setButtons(Apply | Help); // KAboutData *about = // new KAboutData("powerdevilglobalconfig", "powerdevilglobalconfig", ki18n("Global Power Management Configuration"), // "", ki18n("A global power management configurator for KDE Power Management System"), // KAboutData::License_GPL, ki18n("(c), 2010 Dario Freddi"), // ki18n("From this module, you can configure the main Power Management daemon, assign profiles to " // "states, and do some advanced fine tuning on battery handling")); // // about->addAuthor(ki18n("Dario Freddi"), ki18n("Maintainer") , "drf@kde.org", // "http://drfav.wordpress.com"); // // setAboutData(about); setupUi(this); fillUi(); QDBusServiceWatcher *watcher = new QDBusServiceWatcher("org.kde.Solid.PowerManagement", QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration, this); connect(watcher, SIGNAL(serviceRegistered(QString)), this, SLOT(onServiceRegistered(QString))); connect(watcher, SIGNAL(serviceUnregistered(QString)), this, SLOT(onServiceUnregistered(QString))); if (QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.Solid.PowerManagement")) { onServiceRegistered("org.kde.Solid.PowerManagement"); } else { onServiceUnregistered("org.kde.Solid.PowerManagement"); } } GeneralPage::~GeneralPage() { } void GeneralPage::fillUi() { bool hasPowerSupplyBattery = false; bool hasPeripheralBattery = false; - Q_FOREACH (const Solid::Device &device, Solid::Device::listFromType(Solid::DeviceInterface::Battery, QString())) { + const auto devices = Solid::Device::listFromType(Solid::DeviceInterface::Battery, QString()); + for (const Solid::Device &device : devices) { const Solid::Battery *b = qobject_cast (device.asDeviceInterface(Solid::DeviceInterface::Battery)); if (b->isPowerSupply()) { hasPowerSupplyBattery = true; } else { hasPeripheralBattery = true; } } BatteryCriticalCombo->addItem(QIcon::fromTheme("dialog-cancel"), i18n("Do nothing"), PowerDevil::BundledActions::SuspendSession::None); if (PowerDevil::PowerManagement::instance()->canSuspend()) { BatteryCriticalCombo->addItem(QIcon::fromTheme("system-suspend"), i18nc("Suspend to RAM", "Sleep"), PowerDevil::BundledActions::SuspendSession::ToRamMode); } if (PowerDevil::PowerManagement::instance()->canHibernate()) { BatteryCriticalCombo->addItem(QIcon::fromTheme("system-suspend-hibernate"), i18n("Hibernate"), PowerDevil::BundledActions::SuspendSession::ToDiskMode); } BatteryCriticalCombo->addItem(QIcon::fromTheme("system-shutdown"), i18n("Shut down"), PowerDevil::BundledActions::SuspendSession::ShutdownMode); notificationsButton->setIcon(QIcon::fromTheme("preferences-desktop-notification")); // modified fields... connect(notificationsButton, SIGNAL(clicked()), SLOT(configureNotifications())); connect(lowSpin, SIGNAL(valueChanged(int)), SLOT(changed())); connect(criticalSpin, SIGNAL(valueChanged(int)), SLOT(changed())); connect(lowPeripheralSpin, SIGNAL(valueChanged(int)), SLOT(changed())); connect(BatteryCriticalCombo, SIGNAL(currentIndexChanged(int)), SLOT(changed())); connect(pausePlayersCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); if (!hasPowerSupplyBattery) { BatteryCriticalLabel->hide(); BatteryCriticalCombo->hide(); lowLabel->hide(); lowSpin->hide(); criticalLabel->hide(); criticalSpin->hide(); } if (!hasPeripheralBattery) { lowPeripheralLabel->hide(); lowPeripheralSpin->hide(); } if (!hasPowerSupplyBattery && !hasPeripheralBattery) { batteryLevelsLabel->hide(); } } void GeneralPage::load() { lowSpin->setValue(PowerDevilSettings::batteryLowLevel()); criticalSpin->setValue(PowerDevilSettings::batteryCriticalLevel()); lowPeripheralSpin->setValue(PowerDevilSettings::peripheralBatteryLowLevel()); BatteryCriticalCombo->setCurrentIndex(BatteryCriticalCombo->findData(PowerDevilSettings::batteryCriticalAction())); pausePlayersCheckBox->setChecked(PowerDevilSettings::pausePlayersOnSuspend()); } void GeneralPage::configureNotifications() { KNotifyConfigWidget::configure(this, "powerdevil"); } void GeneralPage::save() { PowerDevilSettings::setBatteryLowLevel(lowSpin->value()); PowerDevilSettings::setBatteryCriticalLevel(criticalSpin->value()); PowerDevilSettings::setPeripheralBatteryLowLevel(lowPeripheralSpin->value()); PowerDevilSettings::setBatteryCriticalAction(BatteryCriticalCombo->itemData(BatteryCriticalCombo->currentIndex()).toInt()); PowerDevilSettings::setPausePlayersOnSuspend(pausePlayersCheckBox->checkState() == Qt::Checked); PowerDevilSettings::self()->save(); // Notify Daemon QDBusMessage call = QDBusMessage::createMethodCall("org.kde.Solid.PowerManagement", "/org/kde/Solid/PowerManagement", "org.kde.Solid.PowerManagement", "refreshStatus"); // Perform call QDBusConnection::sessionBus().asyncCall(call); // And now we are set with no change Q_EMIT changed(false); } void GeneralPage::defaults() { KCModule::defaults(); } void GeneralPage::onServiceRegistered(const QString& service) { Q_UNUSED(service); if (m_errorOverlay) { m_errorOverlay->deleteLater(); m_errorOverlay = nullptr; } } void GeneralPage::onServiceUnregistered(const QString& service) { Q_UNUSED(service); if (m_errorOverlay) { m_errorOverlay->deleteLater(); } m_errorOverlay = new ErrorOverlay(this, i18n("The Power Management Service appears not to be running.\n" "This can be solved by starting or scheduling it inside \"Startup and Shutdown\""), this); } #include "GeneralPage.moc" diff --git a/kcmodule/profiles/EditPage.cpp b/kcmodule/profiles/EditPage.cpp index f8473d72..f7bf33f3 100644 --- a/kcmodule/profiles/EditPage.cpp +++ b/kcmodule/profiles/EditPage.cpp @@ -1,279 +1,280 @@ /*************************************************************************** * Copyright (C) 2008-2010 by Dario Freddi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "EditPage.h" #include "actioneditwidget.h" #include "ErrorOverlay.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY(PowerDevilProfilesKCMFactory, registerPlugin(); ) EditPage::EditPage(QWidget *parent, const QVariantList &args) : KCModule(nullptr, parent, args) { setButtons(Apply | Help | Default); // KAboutData *about = // new KAboutData("powerdevilprofilesconfig", "powerdevilprofilesconfig", ki18n("Power Profiles Configuration"), // "", ki18n("A profile configurator for KDE Power Management System"), // KAboutData::License_GPL, ki18n("(c), 2010 Dario Freddi"), // ki18n("From this module, you can manage KDE Power Management System's power profiles, by tweaking " // "existing ones or creating new ones.")); // // about->addAuthor(ki18n("Dario Freddi"), ki18n("Maintainer") , "drf@kde.org", // "http://drfav.wordpress.com"); // // setAboutData(about); setupUi(this); m_profilesConfig = KSharedConfig::openConfig("powermanagementprofilesrc", KConfig::SimpleConfig | KConfig::CascadeConfig); if (m_profilesConfig->groupList().isEmpty()) { // Use the generator PowerDevil::ProfileGenerator::generateProfiles( PowerDevil::PowerManagement::instance()->canSuspend(), PowerDevil::PowerManagement::instance()->canHibernate() ); m_profilesConfig->reparseConfiguration(); } qCDebug(POWERDEVIL) << m_profilesConfig.data()->groupList() << m_profilesConfig.data()->entryMap().keys(); // Create widgets for each profile ActionEditWidget *editWidget = new ActionEditWidget("AC", tabWidget); m_editWidgets.insert("AC", editWidget); acWidgetLayout->addWidget(editWidget); connect(editWidget, SIGNAL(changed(bool)), this, SLOT(onChanged(bool))); editWidget = new ActionEditWidget("Battery", tabWidget); m_editWidgets.insert("Battery", editWidget); batteryWidgetLayout->addWidget(editWidget); connect(editWidget, SIGNAL(changed(bool)), this, SLOT(onChanged(bool))); editWidget = new ActionEditWidget("LowBattery", tabWidget); m_editWidgets.insert("LowBattery", editWidget); lowBatteryWidgetLayout->addWidget(editWidget); connect(editWidget, SIGNAL(changed(bool)), this, SLOT(onChanged(bool))); QDBusServiceWatcher *watcher = new QDBusServiceWatcher("org.kde.Solid.PowerManagement", QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration, this); connect(watcher, SIGNAL(serviceRegistered(QString)), this, SLOT(onServiceRegistered(QString))); connect(watcher, SIGNAL(serviceUnregistered(QString)), this, SLOT(onServiceUnregistered(QString))); bool hasBattery = false; - Q_FOREACH(const Solid::Device &device, Solid::Device::listFromType(Solid::DeviceInterface::Battery, QString())) { + const auto batteries = Solid::Device::listFromType(Solid::DeviceInterface::Battery, QString()); + for(const Solid::Device &device : batteries) { const Solid::Battery *b = qobject_cast (device.asDeviceInterface(Solid::DeviceInterface::Battery)); if (b->isPowerSupply() && (b->type() == Solid::Battery::PrimaryBattery || b->type() == Solid::Battery::UpsBattery)) { hasBattery = true; break; } } if (!hasBattery) { tabWidget->setTabEnabled(1, false); tabWidget->setTabEnabled(2, false); tabWidget->tabBar()->hide(); } if (QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.Solid.PowerManagement")) { onServiceRegistered("org.kde.Solid.PowerManagement"); } else { onServiceUnregistered("org.kde.Solid.PowerManagement"); } } void EditPage::onChanged(bool value) { ActionEditWidget *editWidget = qobject_cast< ActionEditWidget* >(sender()); if (!editWidget) { return; } m_profileEdited[editWidget->configName()] = value; if (value) { Q_EMIT changed(true); } checkAndEmitChanged(); } void EditPage::load() { qCDebug(POWERDEVIL) << "Loading routine called"; for (QHash< QString, ActionEditWidget* >::const_iterator i = m_editWidgets.constBegin(); i != m_editWidgets.constEnd(); ++i) { i.value()->load(); m_profileEdited[i.value()->configName()] = false; } } void EditPage::save() { for (auto it = m_editWidgets.constBegin(); it != m_editWidgets.constEnd(); ++it) { (*it)->save(); } notifyDaemon(); Q_EMIT changed(false); } void EditPage::notifyDaemon() { QDBusConnection::sessionBus().asyncCall( QDBusMessage::createMethodCall( QStringLiteral("org.kde.Solid.PowerManagement"), QStringLiteral("/org/kde/Solid/PowerManagement"), QStringLiteral("org.kde.Solid.PowerManagement"), QStringLiteral("refreshStatus") ) ); } void EditPage::restoreDefaultProfiles() { // Confirm int ret = KMessageBox::warningContinueCancel(this, i18n("The KDE Power Management System will now generate a set of defaults " "based on your computer's capabilities. This will also erase " "all existing modifications you made. " "Are you sure you want to continue?"), i18n("Restore Default Profiles")); if (ret == KMessageBox::Continue) { qCDebug(POWERDEVIL) << "Restoring defaults."; PowerDevil::ProfileGenerator::generateProfiles( PowerDevil::PowerManagement::instance()->canSuspend(), PowerDevil::PowerManagement::instance()->canHibernate() ); load(); notifyDaemon(); } } void EditPage::openUrl(const QString &url) { new KRun(QUrl(url), this); } void EditPage::defaults() { restoreDefaultProfiles(); } void EditPage::checkAndEmitChanged() { bool value = false; for (QHash< QString, bool >::const_iterator i = m_profileEdited.constBegin(); i != m_profileEdited.constEnd(); ++i) { if (i.value()) { value = i.value(); } } Q_EMIT changed(value); } void EditPage::onServiceRegistered(const QString& service) { Q_UNUSED(service); QDBusPendingCallWatcher *currentProfileWatcher = new QDBusPendingCallWatcher(QDBusConnection::sessionBus().asyncCall( QDBusMessage::createMethodCall( QStringLiteral("org.kde.Solid.PowerManagement"), QStringLiteral("/org/kde/Solid/PowerManagement"), QStringLiteral("org.kde.Solid.PowerManagement"), QStringLiteral("currentProfile") ) ), this); QObject::connect(currentProfileWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; if (!reply.isError()) { const QString ¤tProfile = reply.value(); if (currentProfile == QLatin1String("Battery")) { tabWidget->setCurrentIndex(1); } else if (currentProfile == QLatin1String("LowBattery")) { tabWidget->setCurrentIndex(2); } } watcher->deleteLater(); }); if (m_errorOverlay) { m_errorOverlay->deleteLater(); m_errorOverlay = nullptr; } } void EditPage::onServiceUnregistered(const QString& service) { Q_UNUSED(service); if (m_errorOverlay) { m_errorOverlay->deleteLater(); } m_errorOverlay = new ErrorOverlay(this, i18n("The Power Management Service appears not to be running.\n" "This can be solved by starting or scheduling it inside \"Startup and Shutdown\""), this); } #include "EditPage.moc"