Commit a0476890 authored by Martin Flöser's avatar Martin Flöser

Suspend/Hibernate through daemon instead of greeter

Summary:
This change addresses multiple problems with the implementation of
PowerManagement's suspend/hibernate in the greeter.

First of all it removes the usage of KDELibs4Support library by
dropping the usage of the PowerManagement API.

The previous variant performed blocking DBus calls which we don't want
in the greeter. The new implementation is designed in an async way, so
that it cannot block. The new implementation is also LGPL licensed to
make it easy to use it as a base for a new implementation in frameworks.
The new implementation also comes with a small test application to
demonstrate the usage.

Last but not least the power management query is moved from the greeter
into the daemon. This brings multiple advantages. It brings us closer to
no DBus in the greeter and means we don't need to query DBus every time
the screen gets locked. Instead the value is cached in the deamon and
pushed to the greeter on start (through the custom Wayland prtocol).

Reviewers: bshah, broulik

Differential Revision: https://phabricator.kde.org/D827
parent b5aef68a
......@@ -89,6 +89,7 @@ set(ksld_SRCS
waylandlocker.cpp
logind.cpp
waylandserver.cpp
powermanagement.cpp
)
qt5_add_dbus_adaptor(ksld_SRCS ${screensaver_dbusXML} interface.h ScreenLocker::Interface)
qt5_add_dbus_adaptor(ksld_SRCS ${kscreensaver_dbusXML} interface.h ScreenLocker::Interface kscreensaveradaptor KScreenSaverAdaptor)
......
......@@ -25,18 +25,17 @@ ecm_add_wayland_client_protocol(kscreenlocker_greet_SRCS
add_executable(kscreenlocker_greet ${kscreenlocker_greet_SRCS})
target_link_libraries(kscreenlocker_greet
KF5::Solid
KF5::Package
KF5::Crash
KF5::I18n
KF5::ConfigGui
KF5::Declarative
KF5::QuickAddons
KF5::WindowSystem
Qt5::Quick
Qt5::Qml
Qt5::X11Extras
${X11_LIBRARIES}
KF5::KDELibs4Support
KF5::WaylandClient
Wayland::Client
)
......
......@@ -29,7 +29,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <kdeclarative/kdeclarative.h>
#include <KDeclarative/KQuickAddons/QuickViewSharedEngine>
#include <KUser>
#include <Solid/PowerManagement>
//Plasma
#include <KPackage/Package>
#include <KPackage/PackageStructure>
......@@ -183,7 +182,6 @@ void UnlockApp::desktopResized()
// extend views and savers to current demand
const bool canLogout = KAuthorized::authorizeKAction(QStringLiteral("logout")) && KAuthorized::authorize(QStringLiteral("logout"));
const QSet<Solid::PowerManagement::SleepState> spdMethods = Solid::PowerManagement::supportedSleepStates();
for (int i = m_views.count(); i < nScreens; ++i) {
connect(QGuiApplication::screens()[i], &QObject::destroyed, this, &UnlockApp::desktopResized);
// create the view
......@@ -250,16 +248,14 @@ void UnlockApp::desktopResized()
lockProperty.write(m_immediateLock || (!m_noLock && !m_delayedLockTimer));
QQmlProperty sleepProperty(view->rootObject(), QStringLiteral("suspendToRamSupported"));
sleepProperty.write(spdMethods.contains(Solid::PowerManagement::SuspendState));
if (spdMethods.contains(Solid::PowerManagement::SuspendState) &&
view->rootObject()->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("suspendToRam()").constData()) != -1) {
sleepProperty.write(m_canSuspend);
if (view->rootObject()->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("suspendToRam()").constData()) != -1) {
connect(view->rootObject(), SIGNAL(suspendToRam()), SLOT(suspendToRam()));
}
QQmlProperty hibernateProperty(view->rootObject(), QStringLiteral("suspendToDiskSupported"));
hibernateProperty.write(spdMethods.contains(Solid::PowerManagement::HibernateState));
if (spdMethods.contains(Solid::PowerManagement::HibernateState) &&
view->rootObject()->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("suspendToDisk()").constData()) != -1) {
hibernateProperty.write(m_canHibernate);
if (view->rootObject()->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("suspendToDisk()").constData()) != -1) {
connect(view->rootObject(), SIGNAL(suspendToDisk()), SLOT(suspendToDisk()));
}
......@@ -386,7 +382,7 @@ void UnlockApp::suspendToRam()
m_ignoreRequests = true;
m_resetRequestIgnoreTimer->start();
Solid::PowerManagement::requestSleep(Solid::PowerManagement::SuspendState, 0, 0);
org_kde_ksld_suspendSystem(m_ksldInterface);
}
......@@ -399,7 +395,7 @@ void UnlockApp::suspendToDisk()
m_ignoreRequests = true;
m_resetRequestIgnoreTimer->start();
Solid::PowerManagement::requestSleep(Solid::PowerManagement::HibernateState, 0, 0);
org_kde_ksld_hibernateSystem(m_ksldInterface);
}
void UnlockApp::setTesting(bool enable)
......@@ -523,9 +519,23 @@ static void osdText(void *data, org_kde_ksld *org_kde_ksld, const char *icon, co
reinterpret_cast<UnlockApp*>(data)->osdText(QString::fromUtf8(icon), QString::fromUtf8(text));
}
static void canSuspend(void *data, org_kde_ksld *org_kde_ksld, uint suspend)
{
Q_UNUSED(org_kde_ksld)
reinterpret_cast<UnlockApp*>(data)->updateCanSuspend(suspend);
}
static void canHibernate(void *data, org_kde_ksld *org_kde_ksld, uint hibernate)
{
Q_UNUSED(org_kde_ksld)
reinterpret_cast<UnlockApp*>(data)->updateCanHibernate(hibernate);
}
static const struct org_kde_ksld_listener s_listener {
osdProgress,
osdText
osdText,
canSuspend,
canHibernate
};
void UnlockApp::setKsldSocket(int socket)
......@@ -598,5 +608,29 @@ void UnlockApp::osdText(const QString &icon, const QString &additionalText)
}
}
void UnlockApp::updateCanSuspend(bool set)
{
if (m_canSuspend == set) {
return;
}
m_canSuspend = set;
for (auto it = m_views.constBegin(), end = m_views.constEnd(); it != end; ++it) {
QQmlProperty sleepProperty((*it)->rootObject(), QStringLiteral("suspendToRamSupported"));
sleepProperty.write(m_canSuspend);
}
}
void UnlockApp::updateCanHibernate(bool set)
{
if (m_canHibernate == set) {
return;
}
m_canHibernate = set;
for (auto it = m_views.constBegin(), end = m_views.constEnd(); it != end; ++it) {
QQmlProperty hibernateProperty((*it)->rootObject(), QStringLiteral("suspendToDiskSupported"));
hibernateProperty.write(m_canHibernate);
}
}
} // namespace
......@@ -61,6 +61,8 @@ public:
void osdProgress(const QString &icon, int percent, const QString &additionalText);
void osdText(const QString &icon, const QString &additionalText);
void updateCanSuspend(bool set);
void updateCanHibernate(bool set);
public Q_SLOTS:
void desktopResized();
......@@ -94,6 +96,9 @@ private:
int m_graceTime;
bool m_noLock;
bool m_canSuspend = false;
bool m_canHibernate = false;
KWayland::Client::ConnectionThread *m_ksldConnection = nullptr;
KWayland::Client::Registry *m_ksldRegistry = nullptr;
QThread *m_ksldConnectionThread = nullptr;
......
/********************************************************************
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "powermanagement.h"
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusServiceWatcher>
static const QString s_fdoPowerService = QStringLiteral("org.freedesktop.PowerManagement");
static const QString s_fdoPowerPath = QStringLiteral("/org/freedesktop/PowerManagement");
class PowerManagementInstance : public PowerManagement
{
Q_OBJECT
public:
explicit PowerManagementInstance() : PowerManagement() {}
};
Q_GLOBAL_STATIC(PowerManagementInstance, s_instance)
class PowerManagement::Private
{
public:
Private(PowerManagement *q);
void update();
void setCanSuspend(bool set);
void setCanHibernate(bool set);
bool serviceRegistered;
bool canSuspend;
bool canHibernate;
QScopedPointer<QDBusServiceWatcher> fdoPowerServiceWatcher;
private:
void updateProperty(const QString &dbusName, void (Private::*setter)(bool));
PowerManagement *q;
};
PowerManagement::Private::Private(PowerManagement *q)
: serviceRegistered(false)
, canSuspend(false)
, canHibernate(false)
, fdoPowerServiceWatcher(new QDBusServiceWatcher(s_fdoPowerService, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration))
, q(q)
{
}
void PowerManagement::Private::update()
{
serviceRegistered = true;
updateProperty(QStringLiteral("CanSuspend"), &Private::setCanSuspend);
updateProperty(QStringLiteral("CanHibernate"), &Private::setCanHibernate);
}
void PowerManagement::Private::updateProperty(const QString &dbusName, void (Private::*setter)(bool))
{
QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService,
s_fdoPowerPath,
s_fdoPowerService,
dbusName);
QDBusPendingReply<bool> reply = QDBusConnection::sessionBus().asyncCall(message);
QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(reply, q);
QObject::connect(callWatcher, &QDBusPendingCallWatcher::finished, q,
[this, setter](QDBusPendingCallWatcher *self) {
QDBusPendingReply<bool> reply = *self;
self->deleteLater();
if (!reply.isValid()) {
return;
}
((this)->*setter)(reply.value());
}
);
}
void PowerManagement::Private::setCanHibernate(bool set)
{
if (canHibernate == set) {
return;
}
canHibernate = set;
emit q->canHibernateChanged();
}
void PowerManagement::Private::setCanSuspend(bool set)
{
if (canSuspend == set) {
return;
}
canSuspend = set;
emit q->canSuspendChanged();
}
PowerManagement *PowerManagement::instance()
{
return s_instance;
}
PowerManagement::PowerManagement()
: QObject()
, d(new Private(this))
{
connect(d->fdoPowerServiceWatcher.data(), &QDBusServiceWatcher::serviceRegistered, this, [this] { d->update(); });
connect(d->fdoPowerServiceWatcher.data(), &QDBusServiceWatcher::serviceUnregistered, this,
[this] {
d->serviceRegistered = false;
d->setCanSuspend(false);
d->setCanHibernate(false);
}
);
// check whether the service is registered
QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"),
QStringLiteral("/"),
QStringLiteral("org.freedesktop.DBus"),
QStringLiteral("ListNames"));
QDBusPendingReply<QStringList> async = QDBusConnection::sessionBus().asyncCall(message);
QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this);
connect(callWatcher, &QDBusPendingCallWatcher::finished, this,
[this](QDBusPendingCallWatcher *self) {
QDBusPendingReply<QStringList> reply = *self;
self->deleteLater();
if (!reply.isValid()) {
return;
}
if (reply.value().contains(s_fdoPowerService)) {
d->update();
}
}
);
}
PowerManagement::~PowerManagement()
{
}
void PowerManagement::suspend()
{
if (!d->serviceRegistered) {
return;
}
if (!d->canSuspend) {
return;
}
QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService,
s_fdoPowerPath,
s_fdoPowerService,
QStringLiteral("Suspend"));
QDBusConnection::sessionBus().asyncCall(message);
}
void PowerManagement::hibernate()
{
if (!d->serviceRegistered) {
return;
}
if (!d->canHibernate) {
return;
}
QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService,
s_fdoPowerPath,
s_fdoPowerService,
QStringLiteral("Hibernate"));
QDBusConnection::sessionBus().asyncCall(message);
}
bool PowerManagement::canSuspend() const
{
return d->canSuspend;
}
bool PowerManagement::canHibernate() const
{
return d->canHibernate;
}
#include "powermanagement.moc"
/********************************************************************
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef POWERMANAGEMENT_H
#define POWERMANAGEMENT_H
#include <QObject>
class PowerManagement : public QObject
{
Q_OBJECT
Q_PROPERTY(bool canSuspend READ canSuspend NOTIFY canSuspendChanged)
Q_PROPERTY(bool canHibernate READ canHibernate NOTIFY canHibernateChanged)
public:
virtual ~PowerManagement();
bool canSuspend() const;
bool canHibernate() const;
static PowerManagement *instance();
public Q_SLOTS:
void suspend();
void hibernate();
Q_SIGNALS:
void canSuspendChanged();
void canHibernateChanged();
protected:
explicit PowerManagement();
private:
class Private;
QScopedPointer<Private> d;
};
#endif
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ksld">
<interface name="org_kde_ksld" version="2">
<interface name="org_kde_ksld" version="3">
<request name="x11window">
<arg name="id" type="uint"/>
</request>
<request name="suspendSystem" since="3"/>
<request name="hibernateSystem" since="3"/>
<event name="osdProgress" since="2">
<arg name="icon" type="string"/>
<arg name="percent" type="int"/>
......@@ -13,6 +15,12 @@
<arg name="icon" type="string"/>
<arg name="text" type="string"/>
</event>
<event name="canSuspendSystem" since="3">
<arg name="enabled" type="uint"/>
</event>
<event name="canHibernateSystem" since="3">
<arg name="enabled" type="uint"/>
</event>
</interface>
</protocol>
add_executable(kscreenlocker_test kscreenlocker_main.cpp)
target_link_libraries(kscreenlocker_test KScreenLocker Qt5::Widgets KF5::I18n)
ecm_mark_as_test(kscreenlocker_test)
add_definitions(-DQML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/powermanagement.qml")
add_executable(powermanagment_test powermanagementtest.cpp ../powermanagement.cpp)
target_link_libraries(powermanagment_test Qt5::Gui Qt5::DBus Qt5::Quick)
ecm_mark_as_test(powermanagment_test)
/********************************************************************
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
import QtQuick 2.0
import QtQuick.Layouts 1.2
import QtQuick.Controls 1.4
Rectangle {
color: "white"
RowLayout {
Button {
enabled: powerManagement.canSuspend
text: "Suspend"
onClicked: powerManagement.suspend()
}
Button {
enabled: powerManagement.canHibernate
text: "Hibernate"
onClicked: powerManagement.hibernate()
}
}
}
/********************************************************************
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "../powermanagement.h"
#include <QGuiApplication>
#include <QQmlContext>
#include <QQuickView>
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
QQuickView view;
view.rootContext()->setContextProperty("powerManagement", PowerManagement::instance());
view.setSource(QUrl::fromLocalFile(QStringLiteral(QML_PATH)));
view.show();
return app.exec();
}
......@@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "waylandserver.h"
#include "powermanagement.h"
// ksld
#include <config-kscreenlocker.h>
// Wayland
......@@ -54,6 +55,8 @@ WaylandServer::WaylandServer(QObject *parent)
s_osdServiceInterface,
QStringLiteral("osdText"),
this, SLOT(osdText(QString,QString)));
connect(PowerManagement::instance(), &PowerManagement::canSuspendChanged, this, &WaylandServer::sendCanSuspend);
connect(PowerManagement::instance(), &PowerManagement::canHibernateChanged, this, &WaylandServer::sendCanHibernate);
}
WaylandServer::~WaylandServer()
......@@ -85,7 +88,7 @@ int WaylandServer::start()
return -1;
}
connect(m_allowedClient, &KWayland::Server::ClientConnection::disconnected, this, [this] { m_allowedClient = nullptr; });
m_interface = wl_global_create(*m_display.data(), &org_kde_ksld_interface, 2, this, bind);
m_interface = wl_global_create(*m_display.data(), &org_kde_ksld_interface, 3, this, bind);
return socketPair[1];
}
......@@ -110,17 +113,21 @@ void WaylandServer::bind(wl_client *client, void *data, uint32_t version, uint32
wl_client_post_no_memory(client);
return;
}
wl_resource *r = s->m_allowedClient->createResource(&org_kde_ksld_interface, qMin(version, 2u), id);
wl_resource *r = s->m_allowedClient->createResource(&org_kde_ksld_interface, qMin(version, 3u), id);
if (!r) {
wl_client_post_no_memory(client);
return;
}
static const struct org_kde_ksld_interface s_interface = {
x11WindowCallback
x11WindowCallback,
suspendSystemCallback,
hibernateSystemCallback
};
wl_resource_set_implementation(r, &s_interface, s, unbind);
s->addResource(r);
s->sendCanSuspend();
s->sendCanHibernate();
s->m_allowedClient->flush();
}
......@@ -138,6 +145,20 @@ void WaylandServer::x11WindowCallback(wl_client *client, wl_resource *resource,
emit s->x11WindowAdded(id);
}
void WaylandServer::suspendSystemCallback(wl_client *client, wl_resource *resource)
{
Q_UNUSED(client)
Q_UNUSED(resource)
PowerManagement::instance()->suspend();
}
void WaylandServer::hibernateSystemCallback(wl_client *client, wl_resource *resource)
{
Q_UNUSED(client)
Q_UNUSED(resource)
PowerManagement::instance()->hibernate();
}
void WaylandServer::addResource(wl_resource *r)
{
m_resources.append(r);
......@@ -176,4 +197,32 @@ void WaylandServer::osdText(const QString &icon, const QString &additionalText)
}
}
void WaylandServer::sendCanSuspend()
{
if (!m_allowedClient) {
return;
}
Q_FOREACH (auto r, m_resources) {
if (wl_resource_get_version(r) < ORG_KDE_KSLD_CANSUSPENDSYSTEM_SINCE_VERSION) {
continue;
}
org_kde_ksld_send_canSuspendSystem(r, PowerManagement::instance()->canSuspend() ? 1 : 0);
}
m_allowedClient->flush();
}
void WaylandServer::sendCanHibernate()
{
if (!m_allowedClient) {
return;
}
Q_FOREACH (auto r, m_resources) {
if (wl_resource_get_version(r) < ORG_KDE_KSLD_CANHIBERNATESYSTEM_SINCE_VERSION) {
continue;
}
org_kde_ksld_send_canHibernateSystem(r, PowerManagement::instance()->canHibernate() ? 1 : 0);
}
m_allowedClient->flush();
}
}
......@@ -58,8 +58,12 @@ private:
static void bind(wl_client *client, void *data, uint32_t version, uint32_t id);
static void unbind(wl_resource *resource);
static void x11WindowCallback(wl_client *client, wl_resource *resource, uint32_t id);
static void suspendSystemCallback(wl_client *client, wl_resource *resource);
static void hibernateSystemCallback(wl_client *client, wl_resource *resource);
void addResource(wl_resource *r);
void removeResource(wl_resource *r);
void sendCanSuspend();
void sendCanHibernate();
QScopedPointer<KWayland::Server::Display> m_display;
KWayland::Server::ClientConnection *m_allowedClient = nullptr;
wl_global *m_interface = nullptr;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment