File indexing completed on 2024-12-15 05:06:52

0001 /*
0002  * SPDX-FileCopyrightText: 2020 Kai Uwe Broulik <kde@broulik.de>
0003  * SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006  */
0007 
0008 #include "powerprofile.h"
0009 
0010 #include "power_profiles_interface.h"
0011 #include "powerprofileadaptor.h"
0012 #include "properties_interface.h"
0013 
0014 #include <PowerDevilProfileSettings.h>
0015 #include <powerdevil_debug.h>
0016 
0017 #include <KPluginFactory>
0018 
0019 using namespace PowerDevil::BundledActions;
0020 
0021 K_PLUGIN_CLASS_WITH_JSON(PowerProfile, "powerdevilpowerprofileaction.json")
0022 
0023 static const QString activeProfileProperty = QStringLiteral("ActiveProfile");
0024 static const QString profilesProperty = QStringLiteral("Profiles");
0025 static const QString performanceInhibitedProperty = QStringLiteral("PerformanceInhibited");
0026 static const QString performanceDegradedProperty = QStringLiteral("PerformanceDegraded");
0027 static const QString profileHoldsProperty = QStringLiteral("ActiveProfileHolds");
0028 
0029 static const QString ppdName = QStringLiteral("net.hadess.PowerProfiles");
0030 static const QString ppdPath = QStringLiteral("/net/hadess/PowerProfiles");
0031 
0032 PowerProfile::PowerProfile(QObject *parent)
0033     : Action(parent)
0034     , m_powerProfilesInterface(new NetHadessPowerProfilesInterface(ppdName, ppdPath, QDBusConnection::systemBus(), this))
0035     , m_powerProfilesPropertiesInterface(new OrgFreedesktopDBusPropertiesInterface(ppdName, ppdPath, QDBusConnection::systemBus(), this))
0036     , m_holdWatcher(new QDBusServiceWatcher(QString(), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForUnregistration, this))
0037 {
0038     new PowerProfileAdaptor(this);
0039 
0040     connect(m_holdWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &PowerProfile::serviceUnregistered);
0041     connect(m_powerProfilesPropertiesInterface, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged, this, &PowerProfile::propertiesChanged);
0042     connect(m_powerProfilesInterface, &NetHadessPowerProfilesInterface::ProfileReleased, this, [this](unsigned int cookie) {
0043         auto it = std::find(m_holdMap.begin(), m_holdMap.end(), cookie);
0044         if (it != m_holdMap.end()) {
0045             if (m_holdMap.count(it.key()) == 1) {
0046                 m_holdWatcher->removeWatchedService(it.key());
0047             }
0048             m_holdMap.erase(it);
0049         }
0050     });
0051 
0052     auto watcher = new QDBusPendingCallWatcher(m_powerProfilesPropertiesInterface->GetAll(m_powerProfilesInterface->interface()));
0053     connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher] {
0054         watcher->deleteLater();
0055         QDBusPendingReply<QVariantMap> reply = *watcher;
0056         if (watcher->isError()) {
0057             return;
0058         }
0059         readProperties(reply.value());
0060     });
0061     qDBusRegisterMetaType<QList<QVariantMap>>();
0062 }
0063 
0064 PowerProfile::~PowerProfile() = default;
0065 
0066 void PowerProfile::onProfileLoad(const QString & /*previousProfile*/, const QString & /*newProfile*/)
0067 {
0068     if (!m_configuredProfile.isEmpty()) {
0069         setProfile(m_configuredProfile);
0070     }
0071 }
0072 
0073 void PowerProfile::triggerImpl(const QVariantMap &args)
0074 {
0075     Q_UNUSED(args);
0076 }
0077 
0078 bool PowerProfile::loadAction(const PowerDevil::ProfileSettings &profileSettings)
0079 {
0080     m_configuredProfile = profileSettings.powerProfile();
0081     return !m_configuredProfile.isEmpty();
0082 }
0083 
0084 bool PowerProfile::isSupported()
0085 {
0086     return QDBusConnection::systemBus().interface()->activatableServiceNames().value().contains(ppdName);
0087 }
0088 
0089 QStringList PowerProfile::profileChoices() const
0090 {
0091     return m_profileChoices;
0092 }
0093 
0094 QString PowerProfile::currentProfile() const
0095 {
0096     return m_currentProfile;
0097 }
0098 
0099 QString PowerProfile::performanceDegradedReason() const
0100 {
0101     return m_degradationReason;
0102 }
0103 
0104 QString PowerProfile::performanceInhibitedReason() const
0105 {
0106     return m_performanceInhibitedReason;
0107 }
0108 
0109 QList<QVariantMap> PowerProfile::profileHolds() const
0110 {
0111     return m_profileHolds;
0112 }
0113 
0114 void PowerProfile::setProfile(const QString &profile)
0115 {
0116     auto call = m_powerProfilesPropertiesInterface->Set(m_powerProfilesInterface->interface(), activeProfileProperty, QDBusVariant(profile));
0117     if (calledFromDBus()) {
0118         setDelayedReply(true);
0119         const auto msg = message();
0120         auto watcher = new QDBusPendingCallWatcher(call);
0121         connect(watcher, &QDBusPendingCallWatcher::finished, this, [msg, watcher] {
0122             watcher->deleteLater();
0123             if (watcher->isError()) {
0124                 QDBusConnection::sessionBus().send(msg.createErrorReply(watcher->error()));
0125             } else {
0126                 QDBusConnection::sessionBus().send(msg.createReply());
0127             }
0128         });
0129     }
0130 }
0131 
0132 unsigned int PowerProfile::holdProfile(const QString &profile, const QString &reason, const QString &applicationId)
0133 {
0134     if (!m_profileChoices.contains(profile)) {
0135         sendErrorReply(QDBusError::InvalidArgs, QStringLiteral("%1 is not a valid profile").arg(profile));
0136         return 0; // ignored by QtDBus
0137     }
0138     setDelayedReply(true);
0139     const auto msg = message();
0140     auto call = m_powerProfilesInterface->HoldProfile(profile, reason, applicationId);
0141     auto watcher = new QDBusPendingCallWatcher(call);
0142     connect(watcher, &QDBusPendingCallWatcher::finished, this, [msg, watcher, this] {
0143         watcher->deleteLater();
0144         QDBusPendingReply<unsigned int> reply = *watcher;
0145         if (reply.isError()) {
0146             QDBusConnection::sessionBus().send(msg.createErrorReply(watcher->error()));
0147         } else {
0148             m_holdWatcher->addWatchedService(msg.service());
0149             m_holdMap.insert(msg.service(), reply.value());
0150             QDBusConnection::sessionBus().send(msg.createReply(reply.value()));
0151         }
0152     });
0153     return 0; // ignored by QtDBus
0154 }
0155 
0156 void PowerProfile::releaseProfile(unsigned int cookie)
0157 {
0158     setDelayedReply(true);
0159     const auto msg = message();
0160     auto call = m_powerProfilesInterface->ReleaseProfile(cookie);
0161     auto watcher = new QDBusPendingCallWatcher(call);
0162     connect(watcher, &QDBusPendingCallWatcher::finished, this, [msg, watcher, this] {
0163         watcher->deleteLater();
0164         if (watcher->isError()) {
0165             QDBusConnection::sessionBus().send(msg.createErrorReply(watcher->error()));
0166         } else {
0167             m_holdMap.remove(msg.service(), msg.arguments()[0].toUInt());
0168             if (m_holdMap.count(msg.service())) {
0169                 m_holdWatcher->removeWatchedService(msg.service());
0170             }
0171             QDBusConnection::sessionBus().send(msg.createReply());
0172         }
0173     });
0174 }
0175 
0176 void PowerProfile::serviceUnregistered(const QString &name)
0177 {
0178     const auto cookies = m_holdMap.equal_range(name);
0179     for (auto it = cookies.first; it != cookies.second; ++it) {
0180         m_powerProfilesInterface->ReleaseProfile(*it);
0181         m_holdMap.erase(it);
0182     }
0183     m_holdWatcher->removeWatchedService(name);
0184 }
0185 
0186 void PowerProfile::readProperties(const QVariantMap &properties)
0187 {
0188     if (properties.contains(activeProfileProperty)) {
0189         m_currentProfile = properties[activeProfileProperty].toString();
0190         Q_EMIT currentProfileChanged(m_currentProfile);
0191     }
0192 
0193     if (properties.contains(profilesProperty)) {
0194         QList<QVariantMap> profiles;
0195         properties[profilesProperty].value<QDBusArgument>() >> profiles;
0196         m_profileChoices.clear();
0197         if (profiles.first()[QStringLiteral("Driver")] != QLatin1String("placeholder")) {
0198             std::transform(profiles.cbegin(), profiles.cend(), std::back_inserter(m_profileChoices), [](const QVariantMap &dict) {
0199                 return dict[QStringLiteral("Profile")].toString();
0200             });
0201         }
0202         Q_EMIT profileChoicesChanged(m_profileChoices);
0203     }
0204 
0205     if (properties.contains(performanceInhibitedProperty)) {
0206         m_performanceInhibitedReason = properties[performanceInhibitedProperty].toString();
0207         Q_EMIT performanceInhibitedReasonChanged(m_performanceInhibitedReason);
0208     }
0209 
0210     if (properties.contains(performanceDegradedProperty)) {
0211         m_degradationReason = properties[performanceDegradedProperty].toString();
0212         Q_EMIT performanceDegradedReasonChanged(m_degradationReason);
0213     }
0214 
0215     if (properties.contains(profileHoldsProperty)) {
0216         properties[profileHoldsProperty].value<QDBusArgument>() >> m_profileHolds;
0217         Q_EMIT profileHoldsChanged(m_profileHolds);
0218     }
0219 }
0220 
0221 void PowerProfile::propertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalidated)
0222 {
0223     Q_UNUSED(invalidated)
0224     if (interface != m_powerProfilesInterface->interface()) {
0225         return;
0226     }
0227     readProperties(changed);
0228 }
0229 
0230 #include "powerprofile.moc"
0231 
0232 #include "moc_powerprofile.cpp"