Bug 523605 - beginSuppressingScreenPowerManagement silently no-ops after the first release because stopSuppressingScreenPowerManagement never clears its cookie
Summary: beginSuppressingScreenPowerManagement silently no-ops after the first release...
Status: RESOLVED FIXED
Alias: None
Product: plasmashell
Classification: Plasma
Component: DataEngines (other bugs)
Version First Reported In: 6.7.3
Platform: Compiled Sources Linux
: NOR normal
Target Milestone: 1.0
Assignee: Plasma Bugs List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2026-07-28 17:11 UTC by vincent.de.robert
Modified: 2026-07-29 17:45 UTC (History)
2 users (show)

See Also:
Latest Commit:
Version Fixed/Implemented In: 6.6.7
Sentry Crash Report:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description vincent.de.robert 2026-07-28 17:11:03 UTC
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.
Comment 1 TraceyC 2026-07-28 19:07:36 UTC
Thanks for the patch! The next step is to create a merge request. In the MR, please add BUG: [the number of this Bugzilla ticket] so it gets linked to this report.

https://community.kde.org/Infrastructure/GitLab#Submitting_a_merge_request
If you want to discuss the development work, please reach out in our Matrix channel for new contributors
https://community.kde.org/Get_Involved#New_Contributor?_Say_Hello_In_Matrix

Thanks again!
Comment 2 vincent.de.robert 2026-07-29 08:08:53 UTC
Merge request opened: https://invent.kde.org/plasma/plasma5support/-/merge_requests/57
Comment 3 vincent.de.robert 2026-07-29 08:09:06 UTC
Merge request opened: https://invent.kde.org/plasma/plasma5support/-/merge_requests/57
Comment 4 vincent.de.robert 2026-07-29 17:26:41 UTC
Git commit 5d9f76c0acad446537b849693864e976eaf09a18 by Vincent de Robert.
Committed on 29/07/2026 at 07:43.
Pushed by ngraham into branch 'master'.

Fix screen power management inhibition never being re-acquirable

stopSuppressingScreenPowerManagement calls org.freedesktop.ScreenSaver.UnInhibit,
which returns void, but declared the reply as QDBusReply<uint>. A void reply
carries no arguments, so Qt's qDBusReplyFill falls through its type check and
sets QDBusError::InvalidSignature, making isValid() always false.

The cookie reset introduced in bea735c5 is conditional on isValid(), so
m_lockInhibitionCookie is never cleared. Since that cookie is inline static and
therefore process-wide, every subsequent beginSuppressingScreenPowerManagement
hits its guard, returns success, and never contacts PowerDevil. Screen power
management can only be inhibited once per process.

Use QDBusReply<void>, matching stopSuppressingSleep in the same function.

M  +1    -1    src/dataengines/powermanagement/powermanagementjob.cpp

https://invent.kde.org/plasma/plasma5support/-/commit/5d9f76c0acad446537b849693864e976eaf09a18
Comment 5 Nate Graham 2026-07-29 17:35:18 UTC
Git commit 1812089139a10ceb9e35552d9fce4c20142130c6 by Nate Graham.
Committed on 29/07/2026 at 17:34.
Pushed by ngraham into branch 'Plasma/6.6'.

Fix screen power management inhibition never being re-acquirable

stopSuppressingScreenPowerManagement calls org.freedesktop.ScreenSaver.UnInhibit,
which returns void, but declared the reply as QDBusReply<uint>. A void reply
carries no arguments, so Qt's qDBusReplyFill falls through its type check and
sets QDBusError::InvalidSignature, making isValid() always false.

The cookie reset introduced in bea735c5 is conditional on isValid(), so
m_lockInhibitionCookie is never cleared. Since that cookie is inline static and
therefore process-wide, every subsequent beginSuppressingScreenPowerManagement
hits its guard, returns success, and never contacts PowerDevil. Screen power
management can only be inhibited once per process.

Use QDBusReply<void>, matching stopSuppressingSleep in the same function.


(cherry picked from commit 5d9f76c0acad446537b849693864e976eaf09a18)

Co-authored-by: Vincent de Robert <vincent.de.robert@gmail.com>

M  +1    -1    src/dataengines/powermanagement/powermanagementjob.cpp

https://invent.kde.org/plasma/plasma5support/-/commit/1812089139a10ceb9e35552d9fce4c20142130c6
Comment 6 Nate Graham 2026-07-29 17:45:56 UTC
Git commit 4039eed5cd578cc8da696961f25bbf361bea0eab by Nate Graham.
Committed on 29/07/2026 at 17:27.
Pushed by ngraham into branch 'Plasma/6.7'.

Fix screen power management inhibition never being re-acquirable

stopSuppressingScreenPowerManagement calls org.freedesktop.ScreenSaver.UnInhibit,
which returns void, but declared the reply as QDBusReply<uint>. A void reply
carries no arguments, so Qt's qDBusReplyFill falls through its type check and
sets QDBusError::InvalidSignature, making isValid() always false.

The cookie reset introduced in bea735c5 is conditional on isValid(), so
m_lockInhibitionCookie is never cleared. Since that cookie is inline static and
therefore process-wide, every subsequent beginSuppressingScreenPowerManagement
hits its guard, returns success, and never contacts PowerDevil. Screen power
management can only be inhibited once per process.

Use QDBusReply<void>, matching stopSuppressingSleep in the same function.


(cherry picked from commit 5d9f76c0acad446537b849693864e976eaf09a18)

Co-authored-by: Vincent de Robert <vincent.de.robert@gmail.com>

M  +1    -1    src/dataengines/powermanagement/powermanagementjob.cpp

https://invent.kde.org/plasma/plasma5support/-/commit/4039eed5cd578cc8da696961f25bbf361bea0eab