service/upower: add power-profiles support

This commit is contained in:
outfoxxed 2025-01-02 21:54:36 -08:00
parent 66b9917e70
commit 47bcf8ee61
Signed by untrusted user: outfoxxed
GPG key ID: 4C88A185FB89301E
6 changed files with 462 additions and 2 deletions

View file

@ -0,0 +1,213 @@
#include "powerprofiles.hpp"
#include <qcontainerfwd.h>
#include <qdbusconnection.h>
#include <qdbuserror.h>
#include <qdbusinterface.h>
#include <qdbusmetatype.h>
#include <qdebug.h>
#include <qlist.h>
#include <qlogging.h>
#include <qloggingcategory.h>
#include <qobject.h>
#include <qstringliteral.h>
#include "../../dbus/bus.hpp"
#include "../../dbus/properties.hpp"
namespace qs::service::upower {
namespace {
Q_LOGGING_CATEGORY(logPowerProfiles, "quickshell.service.powerprofiles", QtWarningMsg);
}
QString PowerProfile::toString(PowerProfile::Enum profile) {
switch (profile) {
case PowerProfile::PowerSaver: return QStringLiteral("PowerSaver");
case PowerProfile::Balanced: return QStringLiteral("Balanced");
case PowerProfile::Performance: return QStringLiteral("Performance");
default: return QStringLiteral("Invalid");
}
}
QString PerformanceDegradationReason::toString(PerformanceDegradationReason::Enum reason) {
switch (reason) {
case PerformanceDegradationReason::LapDetected: return QStringLiteral("LapDetected");
case PerformanceDegradationReason::HighTemperature: return QStringLiteral("HighTemperature");
default: return QStringLiteral("Invalid");
}
}
bool PowerProfileHold::operator==(const PowerProfileHold& other) const {
return other.profile == this->profile && other.applicationId == this->applicationId
&& other.reason == this->reason;
}
QDebug& operator<<(QDebug& debug, const PowerProfileHold& hold) {
auto saver = QDebugStateSaver(debug);
debug.nospace();
debug << "PowerProfileHold(profile=" << hold.profile << ", applicationId=" << hold.applicationId
<< ", reason=" << hold.reason << ')';
return debug;
}
PowerProfiles::PowerProfiles() {
qDBusRegisterMetaType<QList<QVariantMap>>();
this->bHasPerformanceProfile.setBinding([this]() {
return this->bProfiles.value().contains(PowerProfile::Performance);
});
qCDebug(logPowerProfiles) << "Starting PowerProfiles Service.";
auto bus = QDBusConnection::systemBus();
if (!bus.isConnected()) {
qCWarning(logPowerProfiles
) << "Could not connect to DBus. PowerProfiles services will not work.";
}
this->service = new QDBusInterface(
"org.freedesktop.UPower.PowerProfiles",
"/org/freedesktop/UPower/PowerProfiles",
"org.freedesktop.UPower.PowerProfiles",
bus,
this
);
if (!this->service->isValid()) {
qCDebug(logPowerProfiles
) << "PowerProfilesDaemon is not currently running, attempting to start it.";
dbus::tryLaunchService(this, bus, "org.freedesktop.UPower.PowerProfiles", [this](bool success) {
if (success) {
qCDebug(logPowerProfiles) << "Successfully launched PowerProfiles service.";
this->init();
} else {
qCWarning(logPowerProfiles)
<< "Could not start PowerProfilesDaemon. The PowerProfiles service will not work.";
}
});
} else {
this->init();
}
}
void PowerProfiles::init() {
this->properties.setInterface(this->service);
this->properties.updateAllViaGetAll();
}
void PowerProfiles::setProfile(PowerProfile::Enum profile) {
if (profile == PowerProfile::Performance && !this->bHasPerformanceProfile) {
qCCritical(logPowerProfiles
) << "Cannot request performance profile as it is not present for this device.";
return;
} else if (profile < PowerProfile::PowerSaver || profile > PowerProfile::Performance) {
qCCritical(logPowerProfiles) << "Tried to request invalid power profile" << profile;
return;
}
this->bProfile = profile;
this->pProfile.write();
}
PowerProfiles* PowerProfiles::instance() {
static auto* instance = new PowerProfiles(); // NOLINT
return instance;
}
PowerProfilesQml::PowerProfilesQml(QObject* parent): QObject(parent) {
auto* instance = PowerProfiles::instance();
this->bProfile.setBinding([instance]() { return instance->bProfile.value(); });
this->bHasPerformanceProfile.setBinding([instance]() {
return instance->bHasPerformanceProfile.value();
});
this->bDegradationReason.setBinding([instance]() { return instance->bDegradationReason.value(); }
);
this->bHolds.setBinding([instance]() { return instance->bHolds.value(); });
}
} // namespace qs::service::upower
namespace qs::dbus {
using namespace qs::service::upower;
DBusResult<PowerProfile::Enum> DBusDataTransform<PowerProfile::Enum>::fromWire(const Wire& wire) {
if (wire == QStringLiteral("power-saver")) {
return PowerProfile::PowerSaver;
} else if (wire == QStringLiteral("balanced")) {
return PowerProfile::Balanced;
} else if (wire == QStringLiteral("performance")) {
return PowerProfile::Performance;
} else {
return QDBusError(QDBusError::InvalidArgs, QString("Invalid PowerProfile: %1").arg(wire));
}
}
QString DBusDataTransform<PowerProfile::Enum>::toWire(Data data) {
switch (data) {
case PowerProfile::PowerSaver: return QStringLiteral("power-saver");
case PowerProfile::Balanced: return QStringLiteral("balanced");
case PowerProfile::Performance: return QStringLiteral("performance");
}
}
DBusResult<QList<PowerProfile::Enum>>
DBusDataTransform<QList<PowerProfile::Enum>>::fromWire(const Wire& wire) {
QList<PowerProfile::Enum> profiles;
for (const auto& entry: wire) {
auto profile =
DBusDataTransform<PowerProfile::Enum>::fromWire(entry.value("Profile").value<QString>());
if (!profile.isValid()) return profile.error;
profiles.append(profile.value);
}
return profiles;
}
DBusResult<PerformanceDegradationReason::Enum>
DBusDataTransform<PerformanceDegradationReason::Enum>::fromWire(const Wire& wire) {
if (wire.isEmpty()) {
return PerformanceDegradationReason::None;
} else if (wire == QStringLiteral("lap-detected")) {
return PerformanceDegradationReason::LapDetected;
} else if (wire == QStringLiteral("high-operating-temperature")) {
return PerformanceDegradationReason::HighTemperature;
} else {
return QDBusError(
QDBusError::InvalidArgs,
QString("Invalid PerformanceDegradationReason: %1").arg(wire)
);
}
}
DBusResult<QList<PowerProfileHold>>
DBusDataTransform<QList<PowerProfileHold>>::fromWire(const Wire& wire) {
QList<PowerProfileHold> holds;
for (const auto& entry: wire) {
auto profile =
DBusDataTransform<PowerProfile::Enum>::fromWire(entry.value("Profile").value<QString>());
if (!profile.isValid()) return profile.error;
auto applicationId = entry.value("ApplicationId").value<QString>();
auto reason = entry.value("Reason").value<QString>();
holds.append(PowerProfileHold(profile.value, applicationId, reason));
}
return holds;
}
} // namespace qs::dbus