https://bugs.kde.org/show_bug.cgi?id=523605
Bug ID: 523605
Summary: beginSuppressingScreenPowerManagement silently no-ops
after the first release because
stopSuppressingScreenPowerManagement never clears its
cookie
Classification: Plasma
Product: plasmashell
Version First 6.7.3
Reported In:
Platform: Compiled Sources
OS: Linux
Status: REPORTED
Severity: normal
Priority: NOR
Component: DataEngines
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: 1.0
SUMMARY
In PowerManagementJob, stopSuppressingScreenPowerManagement declares the wrong
QDBusReply type, so its process-wide cookie is never cleared. Every subsequent
beginSuppressingScreenPowerManagement then silently no-ops while reporting
success. Screen power management can be inhibited exactly once per plasmashell
process; after the first release it can never be re-acquired.
stopSuppressingSleep is unaffected, because it uses the correct specialisation
for the identical situation 30 lines earlier in the same function.
MECHANISM
src/dataengines/powermanagement/powermanagementjob.cpp:90-99
} else if (operation ==
QLatin1String("stopSuppressingScreenPowerManagement")) {
QDBusMessage msg =
QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.ScreenSaver"),
QStringLiteral("/ScreenSaver"),
QStringLiteral("org.freedesktop.ScreenSaver"),
QStringLiteral("UnInhibit"));
msg << m_lockInhibitionCookie;
QDBusReply<uint> reply = QDBusConnection::sessionBus().call(msg);
// <-- wrong type
m_lockInhibitionCookie = reply.isValid() ? -1 : m_lockInhibitionCookie;
setResult(reply.isValid());
return;
org.freedesktop.ScreenSaver.UnInhibit returns void. Introspection confirms:
method void org.freedesktop.ScreenSaver.UnInhibit(uint cookie)
A void reply carries zero arguments, so Qt's qDBusReplyFill falls through its
type check (qtbase src/dbus/qdbusreply.cpp:172):
if (reply.arguments().size() >= 1 && reply.arguments().at(0).metaType() ==
data.metaType()) {
and sets QDBusError::InvalidSignature. reply.isValid() is therefore ALWAYS
false, and m_lockInhibitionCookie is never reset to -1.
The UnInhibit call itself succeeds, so the inhibition really is released. But
m_lockInhibitionCookie is inline static (powermanagementjob.h:30), i.e.
process-wide, so every later begin hits the guard added in bea735c5:
} else if (operation ==
QLatin1String("beginSuppressingScreenPowerManagement")) {
if (m_lockInhibitionCookie != -1) { // an inhibition request is already
active
setResult(true);
return;
}
and returns success without contacting PowerDevil at all.
For comparison, the sleep path at line 64 is correct:
QDBusReply<void> reply = QDBusConnection::sessionBus().call(msg);
HOW THIS AROSE
Two commits, neither wrong on its own:
6379d1ec (2021-09-06) "Call UnInhibit with correct signature in
powermanagement dataengine" fixed the ARGUMENT type on both UnInhibit calls
(toInt() -> toUInt()) but left the screen path's reply type as uint. Harmless
at the time: isValid() only fed setResult(), which nothing consumed.
bea735c5 (2024-01-23) "don't trigger another inhibition request while one is
already in place" added the double-begin guard and made the cookie reset
conditional on reply.isValid(). Correct for sleep. This is what made the
pre-existing wrong reply type on the screen path load-bearing.
The file then moved to plasma5support (d91b8300, 2024-09-27) and has not been
modified since.
WHY THIS HAS GONE UNREPORTED
No in-tree component calls this operation. On an Arch system running Plasma 6
with plasma5support 6.7.3, the only files mentioning
beginSuppressingScreenPowerManagement are the dataengine that implements it and
/usr/share/plasma5support/services/powermanagementservice.operations, which
declares it. The Power and Battery applet's "Manually block sleep and screen
locking" checkbox does not use this dataengine; it holds its own C++ cookie
against PowerDevil directly.
The code path is therefore dead in-tree and reachable only by third-party
plasmoids using the plasma5support powermanagement dataengine. That is why this
has been latent since 2021 and functionally broken since January 2024 without a
report.
STEPS TO REPRODUCE
There is no stock-Plasma reproducer, for the reason above. Any plasmoid using
the dataengine will do; this suffices:
Plasma5Support.DataSource {
id: pm
engine: "powermanagement"
connectedSources: ["PowerDevil"]
}
function screen(on) {
const s = pm.serviceForSource("PowerDevil");
const op = s.operationDescription(on ?
"beginSuppressingScreenPowerManagement"
:
"stopSuppressingScreenPowerManagement");
if (on) op.reason = "test";
s.startOperationCall(op);
}
PowerDevil debounces enforcement by about 5 seconds, so wait ~6s before each
check.
1. screen(true), wait, then:
qdbus6 org.kde.Solid.PowerManagement \
/org/kde/Solid/PowerManagement/PolicyAgent HasInhibition 4
-> true
2. screen(false), wait, check again
-> false (correct)
3. screen(true), wait, check again
-> false <-- THE BUG
OBSERVED RESULT
Step 3 leaves HasInhibition(4) false. The PolicyAgent's RequestedInhibitions
property lists only the unrelated sleep entry, confirming the screen request
was
never sent. The operation reported success. Nothing is logged. Recovery
requires
restarting plasmashell.
EXPECTED RESULT
Step 3 returns true, as the equivalent sleep sequence does for
HasInhibition(1).
PATCH
--- a/src/dataengines/powermanagement/powermanagementjob.cpp
+++ b/src/dataengines/powermanagement/powermanagementjob.cpp
@@ -93,7 +93,7 @@ void PowerManagementJob::start()
QStringLiteral("org.freedesktop.ScreenSaver"),
QStringLiteral("UnInhibit"));
msg << m_lockInhibitionCookie;
- QDBusReply<uint> reply = QDBusConnection::sessionBus().call(msg);
+ QDBusReply<void> reply = QDBusConnection::sessionBus().call(msg);
m_lockInhibitionCookie = reply.isValid() ? -1 :
m_lockInhibitionCookie; // reset cookie if the stop request was successful
setResult(reply.isValid());
return;
Note: the QDBusReply<uint> used for ScreenSaver.Inhibit in the begin branch is
correct and must not be changed. Inhibit genuinely returns a uint cookie.
SOFTWARE/OS VERSIONS
Arch Linux, Plasma 6, plasma5support 6.7.3. Present in plasma5support master as
of 2026-07-28.
POSSIBLY RELATED, IN THE SAME DATAENGINE
powermanagementengine.cpp:304 demarshals PolicyAgent.ListInhibitions into
QList<InhibitionInfo>, where InhibitionInfo is QPair<QString, QString>, i.e.
wire signature a(ss). The method advertises QMap<QString,QString> in
introspection and renders as aas on the wire. Neither matches, so the
conversion
fails, the async callback never runs, and the engine's "Inhibitions" source is
permanently empty.
Verified by connecting a plasmoid to that source while two inhibitions were
held: no data was ever delivered. Note that ListInhibitions is marked
org.freedesktop.DBus.Deprecated=true in PowerDevil's introspection, so porting
the engine to the ActiveInhibitions/RequestedInhibitions properties may be the
better fix. Happy to file this separately if preferred.
--
You are receiving this mail because:
You are watching all bug changes.