Skip to content

Commit 47bcf8e

Browse files
committed
service/upower: add power-profiles support
1 parent 66b9917 commit 47bcf8e

File tree

6 files changed

+462
-2
lines changed

6 files changed

+462
-2
lines changed

src/dbus/properties.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ template <typename T>
3636
class DBusResult {
3737
public:
3838
explicit DBusResult() = default;
39-
explicit DBusResult(T value): value(std::move(value)) {}
40-
explicit DBusResult(QDBusError error): error(std::move(error)) {}
39+
DBusResult(T value): value(std::move(value)) {}
40+
DBusResult(QDBusError error): error(std::move(error)) {}
4141
explicit DBusResult(T value, QDBusError error)
4242
: value(std::move(value))
4343
, error(std::move(error)) {}

src/services/upower/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ qt_add_dbus_interface(DBUS_INTERFACES
2121
qt_add_library(quickshell-service-upower STATIC
2222
core.cpp
2323
device.cpp
24+
powerprofiles.cpp
2425
${DBUS_INTERFACES}
2526
)
2627

src/services/upower/core.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ private slots:
5454
DBusUPowerService* service = nullptr;
5555
};
5656

57+
///! Provides access to the UPower service.
58+
/// An interface to the [UPower daemon], which can be used to
59+
/// view battery and power statistics for your computer and
60+
/// connected devices.
61+
///
62+
/// > [!NOTE] The UPower daemon must be installed to use this service.
63+
///
64+
/// [UPower daemon]: https://upower.freedesktop.org
5765
class UPowerQml: public QObject {
5866
Q_OBJECT;
5967
QML_NAMED_ELEMENT(UPower);

src/services/upower/module.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ description = "UPower Service"
33
headers = [
44
"core.hpp",
55
"device.hpp",
6+
"powerprofiles.hpp",
67
]
78
-----

src/services/upower/powerprofiles.cpp

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
#include "powerprofiles.hpp"
2+
3+
#include <qcontainerfwd.h>
4+
#include <qdbusconnection.h>
5+
#include <qdbuserror.h>
6+
#include <qdbusinterface.h>
7+
#include <qdbusmetatype.h>
8+
#include <qdebug.h>
9+
#include <qlist.h>
10+
#include <qlogging.h>
11+
#include <qloggingcategory.h>
12+
#include <qobject.h>
13+
#include <qstringliteral.h>
14+
15+
#include "../../dbus/bus.hpp"
16+
#include "../../dbus/properties.hpp"
17+
18+
namespace qs::service::upower {
19+
20+
namespace {
21+
Q_LOGGING_CATEGORY(logPowerProfiles, "quickshell.service.powerprofiles", QtWarningMsg);
22+
}
23+
24+
QString PowerProfile::toString(PowerProfile::Enum profile) {
25+
switch (profile) {
26+
case PowerProfile::PowerSaver: return QStringLiteral("PowerSaver");
27+
case PowerProfile::Balanced: return QStringLiteral("Balanced");
28+
case PowerProfile::Performance: return QStringLiteral("Performance");
29+
default: return QStringLiteral("Invalid");
30+
}
31+
}
32+
33+
QString PerformanceDegradationReason::toString(PerformanceDegradationReason::Enum reason) {
34+
switch (reason) {
35+
case PerformanceDegradationReason::LapDetected: return QStringLiteral("LapDetected");
36+
case PerformanceDegradationReason::HighTemperature: return QStringLiteral("HighTemperature");
37+
default: return QStringLiteral("Invalid");
38+
}
39+
}
40+
41+
bool PowerProfileHold::operator==(const PowerProfileHold& other) const {
42+
return other.profile == this->profile && other.applicationId == this->applicationId
43+
&& other.reason == this->reason;
44+
}
45+
46+
QDebug& operator<<(QDebug& debug, const PowerProfileHold& hold) {
47+
auto saver = QDebugStateSaver(debug);
48+
49+
debug.nospace();
50+
debug << "PowerProfileHold(profile=" << hold.profile << ", applicationId=" << hold.applicationId
51+
<< ", reason=" << hold.reason << ')';
52+
53+
return debug;
54+
}
55+
56+
PowerProfiles::PowerProfiles() {
57+
qDBusRegisterMetaType<QList<QVariantMap>>();
58+
59+
this->bHasPerformanceProfile.setBinding([this]() {
60+
return this->bProfiles.value().contains(PowerProfile::Performance);
61+
});
62+
63+
qCDebug(logPowerProfiles) << "Starting PowerProfiles Service.";
64+
65+
auto bus = QDBusConnection::systemBus();
66+
67+
if (!bus.isConnected()) {
68+
qCWarning(logPowerProfiles
69+
) << "Could not connect to DBus. PowerProfiles services will not work.";
70+
}
71+
72+
this->service = new QDBusInterface(
73+
"org.freedesktop.UPower.PowerProfiles",
74+
"/org/freedesktop/UPower/PowerProfiles",
75+
"org.freedesktop.UPower.PowerProfiles",
76+
bus,
77+
this
78+
);
79+
80+
if (!this->service->isValid()) {
81+
qCDebug(logPowerProfiles
82+
) << "PowerProfilesDaemon is not currently running, attempting to start it.";
83+
84+
dbus::tryLaunchService(this, bus, "org.freedesktop.UPower.PowerProfiles", [this](bool success) {
85+
if (success) {
86+
qCDebug(logPowerProfiles) << "Successfully launched PowerProfiles service.";
87+
this->init();
88+
} else {
89+
qCWarning(logPowerProfiles)
90+
<< "Could not start PowerProfilesDaemon. The PowerProfiles service will not work.";
91+
}
92+
});
93+
} else {
94+
this->init();
95+
}
96+
}
97+
98+
void PowerProfiles::init() {
99+
this->properties.setInterface(this->service);
100+
this->properties.updateAllViaGetAll();
101+
}
102+
103+
void PowerProfiles::setProfile(PowerProfile::Enum profile) {
104+
if (profile == PowerProfile::Performance && !this->bHasPerformanceProfile) {
105+
qCCritical(logPowerProfiles
106+
) << "Cannot request performance profile as it is not present for this device.";
107+
return;
108+
} else if (profile < PowerProfile::PowerSaver || profile > PowerProfile::Performance) {
109+
qCCritical(logPowerProfiles) << "Tried to request invalid power profile" << profile;
110+
return;
111+
}
112+
113+
this->bProfile = profile;
114+
this->pProfile.write();
115+
}
116+
117+
PowerProfiles* PowerProfiles::instance() {
118+
static auto* instance = new PowerProfiles(); // NOLINT
119+
return instance;
120+
}
121+
122+
PowerProfilesQml::PowerProfilesQml(QObject* parent): QObject(parent) {
123+
auto* instance = PowerProfiles::instance();
124+
125+
this->bProfile.setBinding([instance]() { return instance->bProfile.value(); });
126+
127+
this->bHasPerformanceProfile.setBinding([instance]() {
128+
return instance->bHasPerformanceProfile.value();
129+
});
130+
131+
this->bDegradationReason.setBinding([instance]() { return instance->bDegradationReason.value(); }
132+
);
133+
134+
this->bHolds.setBinding([instance]() { return instance->bHolds.value(); });
135+
}
136+
137+
} // namespace qs::service::upower
138+
139+
namespace qs::dbus {
140+
141+
using namespace qs::service::upower;
142+
143+
DBusResult<PowerProfile::Enum> DBusDataTransform<PowerProfile::Enum>::fromWire(const Wire& wire) {
144+
if (wire == QStringLiteral("power-saver")) {
145+
return PowerProfile::PowerSaver;
146+
} else if (wire == QStringLiteral("balanced")) {
147+
return PowerProfile::Balanced;
148+
} else if (wire == QStringLiteral("performance")) {
149+
return PowerProfile::Performance;
150+
} else {
151+
return QDBusError(QDBusError::InvalidArgs, QString("Invalid PowerProfile: %1").arg(wire));
152+
}
153+
}
154+
155+
QString DBusDataTransform<PowerProfile::Enum>::toWire(Data data) {
156+
switch (data) {
157+
case PowerProfile::PowerSaver: return QStringLiteral("power-saver");
158+
case PowerProfile::Balanced: return QStringLiteral("balanced");
159+
case PowerProfile::Performance: return QStringLiteral("performance");
160+
}
161+
}
162+
163+
DBusResult<QList<PowerProfile::Enum>>
164+
DBusDataTransform<QList<PowerProfile::Enum>>::fromWire(const Wire& wire) {
165+
QList<PowerProfile::Enum> profiles;
166+
167+
for (const auto& entry: wire) {
168+
auto profile =
169+
DBusDataTransform<PowerProfile::Enum>::fromWire(entry.value("Profile").value<QString>());
170+
171+
if (!profile.isValid()) return profile.error;
172+
profiles.append(profile.value);
173+
}
174+
175+
return profiles;
176+
}
177+
178+
DBusResult<PerformanceDegradationReason::Enum>
179+
DBusDataTransform<PerformanceDegradationReason::Enum>::fromWire(const Wire& wire) {
180+
if (wire.isEmpty()) {
181+
return PerformanceDegradationReason::None;
182+
} else if (wire == QStringLiteral("lap-detected")) {
183+
return PerformanceDegradationReason::LapDetected;
184+
} else if (wire == QStringLiteral("high-operating-temperature")) {
185+
return PerformanceDegradationReason::HighTemperature;
186+
} else {
187+
return QDBusError(
188+
QDBusError::InvalidArgs,
189+
QString("Invalid PerformanceDegradationReason: %1").arg(wire)
190+
);
191+
}
192+
}
193+
194+
DBusResult<QList<PowerProfileHold>>
195+
DBusDataTransform<QList<PowerProfileHold>>::fromWire(const Wire& wire) {
196+
QList<PowerProfileHold> holds;
197+
198+
for (const auto& entry: wire) {
199+
auto profile =
200+
DBusDataTransform<PowerProfile::Enum>::fromWire(entry.value("Profile").value<QString>());
201+
202+
if (!profile.isValid()) return profile.error;
203+
204+
auto applicationId = entry.value("ApplicationId").value<QString>();
205+
auto reason = entry.value("Reason").value<QString>();
206+
207+
holds.append(PowerProfileHold(profile.value, applicationId, reason));
208+
}
209+
210+
return holds;
211+
}
212+
213+
} // namespace qs::dbus

0 commit comments

Comments
 (0)