@@ -1,5 +1,5 @@
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014, 2015 Canonical, Ltd.
4 * This program is free software: you can redistribute it and/or modify it under
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -17,18 +17,186 @@
18#include "dbusunitysessionservice.h"
18#include "dbusunitysessionservice.h"
21+#include <sys/types.h>
21#include <QDBusPendingCall>
27#include <QDBusPendingCall>
29+#include <QElapsedTimer>
32+#define LOGIN1_SERVICE QStringLiteral("org.freedesktop.login1")
33+#define LOGIN1_PATH QStringLiteral("/org/freedesktop/login1")
34+#define LOGIN1_IFACE QStringLiteral("org.freedesktop.login1.Manager")
35+#define LOGIN1_SESSION_IFACE QStringLiteral("org.freedesktop.login1.Session")
37+#define ACTIVE_KEY QStringLiteral("Active")
38+#define IDLE_SINCE_KEY QStringLiteral("IdleSinceHint")
40+class DBusUnitySessionServicePrivate: public QObject
44+ QString logindSessionPath;
45+ bool isSessionActive = true;
46+ QElapsedTimer screensaverActiveTimer;
48+ DBusUnitySessionServicePrivate(): QObject() {
55+ // get our logind session path
56+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
60+ msg << (quint32) getpid();
62+ QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().asyncCall(msg);
63+ if (reply.isValid()) {
64+ logindSessionPath = reply.value().path();
66+ // start watching the Active property
67+ QDBusConnection::systemBus().connect(LOGIN1_SERVICE, logindSessionPath, "org.freedesktop.DBus.Properties", "PropertiesChanged",
68+ this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
70+ qWarning() << "Failed to get logind session path" << reply.error().message();
74+ bool checkLogin1Call(const QString &method) const
76+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
77+ QDBusReply<QString> reply = QDBusConnection::systemBus().asyncCall(msg);
78+ return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
81+ void makeLogin1Call(const QString &method, const QVariantList &args)
83+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
87+ msg.setArguments(args);
88+ QDBusConnection::systemBus().asyncCall(msg);
93+ if (logindSessionPath.isEmpty()) {
94+ qWarning() << "Invalid session path";
98+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
100+ "org.freedesktop.DBus.Properties",
102+ msg << LOGIN1_SESSION_IFACE;
105+ QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
106+ if (reply.isValid()) {
107+ isSessionActive = reply.value().toBool();
108+ qDebug() << "Session" << logindSessionPath << "is active:" << isSessionActive;
110+ qWarning() << "Failed to get Active property" << reply.error().message();
114+ quint32 screensaverActiveTime() const
116+ if (!isSessionActive && screensaverActiveTimer.isValid()) {
117+ return screensaverActiveTimer.elapsed() / 1000;
123+ quint64 idleSinceUSecTimestamp() const
125+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
127+ "org.freedesktop.DBus.Properties",
129+ msg << LOGIN1_SESSION_IFACE;
130+ msg << IDLE_SINCE_KEY;
132+ QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
133+ if (reply.isValid()) {
134+ return reply.value().value<quint64>();
136+ qWarning() << "Failed to get IdleSinceHint property" << reply.error().message();
142+ void setIdleHint(bool idle)
144+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
146+ LOGIN1_SESSION_IFACE,
149+ QDBusConnection::systemBus().asyncCall(msg);
153+ void onPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
157+ if (changedProps.contains(ACTIVE_KEY) || invalidatedProps.contains(ACTIVE_KEY)) {
158+ if (changedProps.value(ACTIVE_KEY).isValid()) {
159+ isSessionActive = changedProps.value(ACTIVE_KEY).toBool();
164+ Q_EMIT screensaverActiveChanged(!isSessionActive);
166+ if (isSessionActive) {
167+ screensaverActiveTimer.invalidate();
170+ screensaverActiveTimer.start();
177+ void screensaverActiveChanged(bool active);
180+Q_GLOBAL_STATIC(DBusUnitySessionServicePrivate, d)
23DBusUnitySessionService::DBusUnitySessionService()
182DBusUnitySessionService::DBusUnitySessionService()
24 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
183 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
185+ if (!d->logindSessionPath.isEmpty()) {
186+ // connect our Lock() slot to the logind's session Lock() signal
187+ QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, "Lock", this, SLOT(Lock()));
188+ // ... and our Unlocked() signal to the logind's session Unlock() signal
189+ // (lightdm handles the unlocking by calling logind's Unlock method which in turn emits this signal we connect to)
190+ QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, "Unlock", this, SIGNAL(Unlocked()));
192+ qWarning() << "Failed to connect to logind's session Lock/Unlock signals";
28void DBusUnitySessionService::Logout()
196void DBusUnitySessionService::Logout()
30 // TODO ask the apps to quit and then emit the signal
198 // TODO ask the apps to quit and then emit the signal
31− Q_EMIT logoutReady();
199+ Q_EMIT LogoutReady();
34void DBusUnitySessionService::EndSession()
202void DBusUnitySessionService::EndSession()
@@ -40,39 +208,136 @@ void DBusUnitySessionService::EndSession()
40 QDBusConnection::sessionBus().asyncCall(msg);
208 QDBusConnection::sessionBus().asyncCall(msg);
211+bool DBusUnitySessionService::CanHibernate() const
213+ return d->checkLogin1Call("CanHibernate");
216+bool DBusUnitySessionService::CanSuspend() const
218+ return d->checkLogin1Call("CanSuspend");
221+bool DBusUnitySessionService::CanHybridSleep() const
223+ return d->checkLogin1Call("CanHybridSleep");
226+bool DBusUnitySessionService::CanReboot() const
228+ return d->checkLogin1Call("CanReboot");
231+bool DBusUnitySessionService::CanShutdown() const
233+ return d->checkLogin1Call("CanPowerOff");
236+bool DBusUnitySessionService::CanLock() const
238+ return true; // FIXME
241+QString DBusUnitySessionService::UserName() const
243+ struct passwd *p = getpwuid(geteuid());
245+ return QString::fromUtf8(p->pw_name);
251+QString DBusUnitySessionService::RealName() const
253+ struct passwd *p = getpwuid(geteuid());
255+ const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
256+ if (!gecos.isEmpty()) {
257+ return gecos.split(QLatin1Char(',')).first();
264+QString DBusUnitySessionService::HostName() const
267+ if (gethostname(hostName, sizeof(hostName)) == -1) {
268+ qWarning() << "Could not determine local hostname";
271+ hostName[sizeof(hostName) - 1] = '\0';
272+ return QString::fromLocal8Bit(hostName);
275+void DBusUnitySessionService::PromptLock()
277+ Q_EMIT LockRequested();
280+void DBusUnitySessionService::Lock()
282+ // lock the session using the org.freedesktop.DisplayManager system DBUS service
283+ const QString sessionPath = QString::fromLocal8Bit(qgetenv("XDG_SESSION_PATH"));
284+ QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DisplayManager",
286+ "org.freedesktop.DisplayManager.Session",
288+ qDebug() << "Locking session" << msg.path();
289+ QDBusReply<void> reply = QDBusConnection::systemBus().asyncCall(msg);
290+ if (!reply.isValid()) {
291+ qWarning() << "Lock call failed" << reply.error().message();
293+ // emit Locked when the call succeeds
298+bool DBusUnitySessionService::IsLocked() const
300+ return !d->isSessionActive;
43void DBusUnitySessionService::RequestLogout()
303void DBusUnitySessionService::RequestLogout()
45− Q_EMIT logoutRequested(false);
305+ Q_EMIT LogoutRequested(false);
48void DBusUnitySessionService::Reboot()
308void DBusUnitySessionService::Reboot()
50− QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
310+ d->makeLogin1Call("Reboot", {false});
51− "/org/freedesktop/login1",
52− "org.freedesktop.login1.Manager",
55− QDBusConnection::systemBus().asyncCall(msg);
58void DBusUnitySessionService::RequestReboot()
313void DBusUnitySessionService::RequestReboot()
60− Q_EMIT rebootRequested(false);
315+ Q_EMIT RebootRequested(false);
63void DBusUnitySessionService::Shutdown()
318void DBusUnitySessionService::Shutdown()
65− QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
66− "/org/freedesktop/login1",
67− "org.freedesktop.login1.Manager",
68− "PowerOff");
320+ d->makeLogin1Call("PowerOff", {false});
70− QDBusConnection::systemBus().asyncCall(msg);
322+
323+void DBusUnitySessionService::Suspend()
325+ d->makeLogin1Call("Suspend", {false});
328+void DBusUnitySessionService::Hibernate()
330+ d->makeLogin1Call("Hibernate", {false});
333+void DBusUnitySessionService::HybridSleep()
335+ d->makeLogin1Call("HybridSleep", {false});
73void DBusUnitySessionService::RequestShutdown()
338void DBusUnitySessionService::RequestShutdown()
75− Q_EMIT shutdownRequested(false);
340+ Q_EMIT ShutdownRequested(false);
78enum class Action : unsigned
343enum class Action : unsigned
@@ -83,12 +348,8 @@ enum class Action : unsigned
86−DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
87− : UnityDBusObject("/org/gnome/SessionManager/EndSessionDialog", "com.canonical.Unity")
91−void DBusGnomeSessionManagerWrapper::performAsyncCall(const QString &method)
352+void performAsyncUnityCall(const QString &method)
93 const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.Unity",
354 const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.Unity",
94 "/com/canonical/Unity/Session",
355 "/com/canonical/Unity/Session",
@@ -97,29 +358,111 @@ void DBusGnomeSessionManagerWrapper::performAsyncCall(const QString &method)
97 QDBusConnection::sessionBus().asyncCall(msg);
358 QDBusConnection::sessionBus().asyncCall(msg);
362+DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
363+ : UnityDBusObject("/org/gnome/SessionManager/EndSessionDialog", "com.canonical.Unity")
100void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
367void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
102 Q_UNUSED(arg_1);
369 Q_UNUSED(arg_1);
103 Q_UNUSED(max_wait);
370 Q_UNUSED(max_wait);
104 Q_UNUSED(inhibitors);
371 Q_UNUSED(inhibitors);
106− const Action action = (Action)type;
373+ switch (static_cast<Action>(type))
110 case Action::LOGOUT:
375 case Action::LOGOUT:
111− performAsyncCall("RequestLogout");
376+ performAsyncUnityCall("RequestLogout");
114 case Action::REBOOT:
379 case Action::REBOOT:
115− performAsyncCall("RequestReboot");
380+ performAsyncUnityCall("RequestReboot");
118 case Action::SHUTDOWN:
383 case Action::SHUTDOWN:
119− performAsyncCall("RequestShutdown");
384+ performAsyncUnityCall("RequestShutdown");
393+DBusGnomeScreensaverWrapper::DBusGnomeScreensaverWrapper()
394+ : UnityDBusObject("/org/gnome/ScreenSaver", "org.gnome.ScreenSaver")
396+ connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusGnomeScreensaverWrapper::ActiveChanged);
399+bool DBusGnomeScreensaverWrapper::GetActive() const
401+ return !d->isSessionActive; // return whether the session is not active
404+void DBusGnomeScreensaverWrapper::SetActive(bool lock)
411+void DBusGnomeScreensaverWrapper::Lock()
413+ performAsyncUnityCall("Lock");
416+quint32 DBusGnomeScreensaverWrapper::GetActiveTime() const
418+ return d->screensaverActiveTime();
421+void DBusGnomeScreensaverWrapper::SimulateUserActivity()
423+ d->setIdleHint(false);
427+DBusScreensaverWrapper::DBusScreensaverWrapper()
428+ : UnityDBusObject("/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver")
430+ QDBusConnection::sessionBus().registerObject("/ScreenSaver", this, QDBusConnection::ExportScriptableContents); // compat path, also register here
431+ connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusScreensaverWrapper::ActiveChanged);
434+bool DBusScreensaverWrapper::GetActive() const
436+ return !d->isSessionActive; // return whether the session is not active
439+bool DBusScreensaverWrapper::SetActive(bool lock)
448+void DBusScreensaverWrapper::Lock()
450+ performAsyncUnityCall("Lock");
453+quint32 DBusScreensaverWrapper::GetActiveTime() const
455+ return d->screensaverActiveTime();
458+quint32 DBusScreensaverWrapper::GetSessionIdleTime() const
460+ return QDateTime::fromMSecsSinceEpoch(d->idleSinceUSecTimestamp()/1000).secsTo(QDateTime::currentDateTime());
463+void DBusScreensaverWrapper::SimulateUserActivity()
465+ d->setIdleHint(false);
468+#include "dbusunitysessionservice.moc"