File indexing completed on 2025-01-19 04:23:50

0001 /*
0002    SPDX-FileCopyrightText: 2019 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
0003 
0004    SPDX-License-Identifier: LGPL-3.0-or-later
0005  */
0006 
0007 #include "powermanagementinterface.h"
0008 
0009 #include <KLocalizedString>
0010 
0011 #if defined Q_OS_LINUX && !defined Q_OS_ANDROID
0012 #define Qt5DBus_FOUND true
0013 #else
0014 #define Qt5DBus_FOUND false
0015 #endif
0016 
0017 #if Qt5DBus_FOUND
0018 #include <QDBusConnection>
0019 #include <QDBusMessage>
0020 #include <QDBusPendingCall>
0021 #include <QDBusPendingCallWatcher>
0022 #include <QDBusPendingReply>
0023 #include <QDBusUnixFileDescriptor>
0024 #endif
0025 
0026 #if defined Q_OS_WIN
0027 #include <windows.h>
0028 #include <winbase.h>
0029 #endif
0030 
0031 #include <QString>
0032 #include <QDebug>
0033 #include <QGuiApplication>
0034 
0035 class PowerManagementInterfacePrivate
0036 {
0037 public:
0038 
0039     bool mPreventSleep = false;
0040 
0041     bool mInhibitedSleep = false;
0042 
0043     uint mInhibitSleepCookie = 0;
0044 
0045 };
0046 
0047 PowerManagementInterface::PowerManagementInterface(QObject *parent) : QObject(parent), d(std::make_unique<PowerManagementInterfacePrivate>())
0048 {
0049 #if Qt5DBus_FOUND
0050     auto sessionBus = QDBusConnection::sessionBus();
0051 
0052     sessionBus.connect(QStringLiteral("org.freedesktop.PowerManagement.Inhibit"),
0053                        QStringLiteral("/org/freedesktop/PowerManagement/Inhibit"),
0054                        QStringLiteral("org.freedesktop.PowerManagement.Inhibit"),
0055                        QStringLiteral("HasInhibitChanged"), this, SLOT(hostSleepInhibitChanged()));
0056 #endif
0057 }
0058 
0059 PowerManagementInterface::~PowerManagementInterface() = default;
0060 
0061 bool PowerManagementInterface::preventSleep() const
0062 {
0063     return d->mPreventSleep;
0064 }
0065 
0066 bool PowerManagementInterface::sleepInhibited() const
0067 {
0068     return d->mInhibitedSleep;
0069 }
0070 
0071 void PowerManagementInterface::setPreventSleep(bool value)
0072 {
0073     if (d->mPreventSleep == value) {
0074         return;
0075     }
0076 
0077     if (value) {
0078         inhibitSleepPlasmaWorkspace();
0079         inhibitSleepGnomeWorkspace();
0080         d->mPreventSleep = true;
0081     } else {
0082         uninhibitSleepPlasmaWorkspace();
0083         uninhibitSleepGnomeWorkspace();
0084         d->mPreventSleep = false;
0085     }
0086 
0087     Q_EMIT preventSleepChanged();
0088 }
0089 
0090 void PowerManagementInterface::retryInhibitingSleep()
0091 {
0092     if (d->mPreventSleep && !d->mInhibitedSleep) {
0093         inhibitSleepPlasmaWorkspace();
0094         inhibitSleepGnomeWorkspace();
0095     }
0096 }
0097 
0098 void PowerManagementInterface::hostSleepInhibitChanged()
0099 {
0100 }
0101 
0102 void PowerManagementInterface::inhibitDBusCallFinishedPlasmaWorkspace(QDBusPendingCallWatcher *aWatcher)
0103 {
0104 #if Qt5DBus_FOUND
0105     QDBusPendingReply<uint> reply = *aWatcher;
0106     if (reply.isError()) {
0107     } else {
0108         d->mInhibitSleepCookie = reply.argumentAt<0>();
0109         d->mInhibitedSleep = true;
0110 
0111         Q_EMIT sleepInhibitedChanged();
0112     }
0113     aWatcher->deleteLater();
0114 #endif
0115 }
0116 
0117 void PowerManagementInterface::uninhibitDBusCallFinishedPlasmaWorkspace(QDBusPendingCallWatcher *aWatcher)
0118 {
0119 #if Qt5DBus_FOUND
0120     QDBusPendingReply<> reply = *aWatcher;
0121     if (reply.isError()) {
0122         qDebug() << "PowerManagementInterface::uninhibitDBusCallFinished" << reply.error();
0123     } else {
0124         d->mInhibitedSleep = false;
0125 
0126         Q_EMIT sleepInhibitedChanged();
0127     }
0128     aWatcher->deleteLater();
0129 #endif
0130 }
0131 
0132 void PowerManagementInterface::inhibitDBusCallFinishedGnomeWorkspace(QDBusPendingCallWatcher *aWatcher)
0133 {
0134 #if Qt5DBus_FOUND
0135     QDBusPendingReply<uint> reply = *aWatcher;
0136     if (reply.isError()) {
0137         qDebug() << "PowerManagementInterface::inhibitDBusCallFinishedGnomeWorkspace" << reply.error();
0138     } else {
0139         d->mInhibitSleepCookie = reply.argumentAt<0>();
0140         d->mInhibitedSleep = true;
0141 
0142         Q_EMIT sleepInhibitedChanged();
0143     }
0144     aWatcher->deleteLater();
0145 #endif
0146 }
0147 
0148 void PowerManagementInterface::uninhibitDBusCallFinishedGnomeWorkspace(QDBusPendingCallWatcher *aWatcher)
0149 {
0150 #if Qt5DBus_FOUND
0151     QDBusPendingReply<> reply = *aWatcher;
0152     if (reply.isError()) {
0153         qDebug() << "PowerManagementInterface::uninhibitDBusCallFinished" << reply.error();
0154     } else {
0155         d->mInhibitedSleep = false;
0156 
0157         Q_EMIT sleepInhibitedChanged();
0158     }
0159     aWatcher->deleteLater();
0160 #endif
0161 }
0162 
0163 void PowerManagementInterface::inhibitSleepPlasmaWorkspace()
0164 {
0165 #if Qt5DBus_FOUND
0166     auto sessionBus = QDBusConnection::sessionBus();
0167 
0168     auto inhibitCall = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.PowerManagement.Inhibit"),
0169                                                       QStringLiteral("/org/freedesktop/PowerManagement/Inhibit"),
0170                                                       QStringLiteral("org.freedesktop.PowerManagement.Inhibit"),
0171                                                       QStringLiteral("Inhibit"));
0172 
0173     inhibitCall.setArguments({{QGuiApplication::desktopFileName()}, {i18nc("explanation for sleep inhibit during play of music", "Playing music")}});
0174 
0175     auto asyncReply = sessionBus.asyncCall(inhibitCall);
0176 
0177     auto replyWatcher = new QDBusPendingCallWatcher(asyncReply, this);
0178 
0179     QObject::connect(replyWatcher, &QDBusPendingCallWatcher::finished,
0180                      this, &PowerManagementInterface::inhibitDBusCallFinishedPlasmaWorkspace);
0181 #endif
0182 }
0183 
0184 void PowerManagementInterface::uninhibitSleepPlasmaWorkspace()
0185 {
0186 #if Qt5DBus_FOUND
0187     auto sessionBus = QDBusConnection::sessionBus();
0188 
0189     auto uninhibitCall = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.PowerManagement.Inhibit"),
0190                                                       QStringLiteral("/org/freedesktop/PowerManagement/Inhibit"),
0191                                                       QStringLiteral("org.freedesktop.PowerManagement.Inhibit"),
0192                                                       QStringLiteral("UnInhibit"));
0193 
0194     uninhibitCall.setArguments({{d->mInhibitSleepCookie}});
0195 
0196     auto asyncReply = sessionBus.asyncCall(uninhibitCall);
0197 
0198     auto replyWatcher = new QDBusPendingCallWatcher(asyncReply, this);
0199 
0200     QObject::connect(replyWatcher, &QDBusPendingCallWatcher::finished,
0201                      this, &PowerManagementInterface::uninhibitDBusCallFinishedPlasmaWorkspace);
0202 #endif
0203 }
0204 
0205 void PowerManagementInterface::inhibitSleepGnomeWorkspace()
0206 {
0207 #if Qt5DBus_FOUND
0208     auto sessionBus = QDBusConnection::sessionBus();
0209 
0210     auto inhibitCall = QDBusMessage::createMethodCall(QStringLiteral("org.gnome.SessionManager"),
0211                                                       QStringLiteral("/org/gnome/SessionManager"),
0212                                                       QStringLiteral("org.gnome.SessionManager"),
0213                                                       QStringLiteral("Inhibit"));
0214 
0215     // See: https://gitlab.gnome.org/GNOME/gnome-session/-/blob/master/gnome-session/org.gnome.SessionManager.xml
0216     // The last argument are flag settings to specify what should be inhibited:
0217     //    1  = Inhibit logging out
0218     //    2  = Inhibit user switching
0219     //    4  = Inhibit suspending the session or computer
0220     //    8  = Inhibit the session being marked as idle
0221     //    16 = Inhibit auto-mounting removable media for the session
0222     inhibitCall.setArguments({{QCoreApplication::applicationName()}, {uint(0)},
0223                               {i18nc("explanation for sleep inhibit during play of music", "Playing music")}, {uint(12)}});
0224 
0225     auto asyncReply = sessionBus.asyncCall(inhibitCall);
0226 
0227     auto replyWatcher = new QDBusPendingCallWatcher(asyncReply, this);
0228 
0229     QObject::connect(replyWatcher, &QDBusPendingCallWatcher::finished,
0230                      this, &PowerManagementInterface::inhibitDBusCallFinishedGnomeWorkspace);
0231 #endif
0232 }
0233 
0234 void PowerManagementInterface::uninhibitSleepGnomeWorkspace()
0235 {
0236 #if Qt5DBus_FOUND
0237     auto sessionBus = QDBusConnection::sessionBus();
0238 
0239     auto uninhibitCall = QDBusMessage::createMethodCall(QStringLiteral("org.gnome.SessionManager"),
0240                                                         QStringLiteral("/org/gnome/SessionManager"),
0241                                                         QStringLiteral("org.gnome.SessionManager"),
0242                                                         QStringLiteral("UnInhibit"));
0243 
0244     uninhibitCall.setArguments({{d->mInhibitSleepCookie}});
0245 
0246     auto asyncReply = sessionBus.asyncCall(uninhibitCall);
0247 
0248     auto replyWatcher = new QDBusPendingCallWatcher(asyncReply, this);
0249 
0250     QObject::connect(replyWatcher, &QDBusPendingCallWatcher::finished,
0251                      this, &PowerManagementInterface::uninhibitDBusCallFinishedGnomeWorkspace);
0252 #endif
0253 }
0254 
0255 void PowerManagementInterface::inhibitSleepWindowsWorkspace()
0256 {
0257 #if defined Q_OS_WIN
0258     SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
0259 #endif
0260 }
0261 
0262 void PowerManagementInterface::uninhibitSleepWindowsWorkspace()
0263 {
0264 #if defined Q_OS_WIN
0265     SetThreadExecutionState(ES_CONTINUOUS);
0266 #endif
0267 }
0268 
0269 
0270 #include "moc_powermanagementinterface.cpp"