on xenial help sign in
Provide DBUS compatibility with various session services (suspend/hibernate, lock/unlock, screensaver, etc)

Approved by: Michael Zanetti

commit145ea7281992370a8440ec44102952eb011b8854 authorLukáš Tinkl <lukas.tinkl@canonical.com> dateWed 29 Jul 2015, 12:38 (11 years ago) parent47c389c39aae47
6 files changed+623−55
plugins/AccountsService/AccountsService.cpp+1−2 plugins/Unity/Session/CMakeLists.txt+1 plugins/Unity/Session/dbusunitysessionservice.cpp+371−28 plugins/Unity/Session/dbusunitysessionservice.h+194−17 plugins/Unity/Session/plugin.cpp+2 tests/plugins/Unity/Session/sessionbackendtest.cpp+54−8
plugins/AccountsService/AccountsService.cpp+1−2
@@ -141,9 +141,9 @@ void AccountsService::updateDemoEdges(bool async)
141141 this, [this](QDBusPendingCallWatcher* watcher) {
142142
143143 QDBusPendingReply<QDBusVariant> reply = *watcher;
144+ watcher->deleteLater();
144145 if (reply.isError()) {
145146 qWarning() << "Failed to get 'demo-edges' property - " << reply.error().message();
146− watcher->deleteLater();
147147 return;
148148 }
149149
@@ -152,7 +152,6 @@ void AccountsService::updateDemoEdges(bool async)
152152 m_demoEdges = demoEdges;
153153 Q_EMIT demoEdgesChanged();
154154 }
155− watcher->deleteLater();
156155 });
157156 if (!async) {
158157 watcher->waitForFinished();
plugins/Unity/Session/CMakeLists.txt+1
@@ -2,6 +2,7 @@ pkg_search_module(GIO REQUIRED gio-2.0)
22
33include_directories(
44 ${CMAKE_CURRENT_SOURCE_DIR}
5+ ${CMAKE_CURRENT_BINARY_DIR}
56 ${GIO_INCLUDE_DIRS}
67 ${libunity8-private_SOURCE_DIR}
78)
plugins/Unity/Session/dbusunitysessionservice.cpp+371−28
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014, 2015 Canonical, Ltd.
33 *
44 * This program is free software: you can redistribute it and/or modify it under
55 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -17,18 +17,186 @@
1717// local
1818#include "dbusunitysessionservice.h"
1919
20+// system
21+#include <sys/types.h>
22+#include <unistd.h>
23+#include <pwd.h>
24+
2025// Qt
26+#include <QDebug>
2127#include <QDBusPendingCall>
28+#include <QDBusReply>
29+#include <QElapsedTimer>
30+#include <QDateTime>
31+
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")
36+
37+#define ACTIVE_KEY QStringLiteral("Active")
38+#define IDLE_SINCE_KEY QStringLiteral("IdleSinceHint")
39+
40+class DBusUnitySessionServicePrivate: public QObject
41+{
42+ Q_OBJECT
43+public:
44+ QString logindSessionPath;
45+ bool isSessionActive = true;
46+ QElapsedTimer screensaverActiveTimer;
47+
48+ DBusUnitySessionServicePrivate(): QObject() {
49+ init();
50+ checkActive();
51+ }
52+
53+ void init()
54+ {
55+ // get our logind session path
56+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
57+ LOGIN1_PATH,
58+ LOGIN1_IFACE,
59+ "GetSessionByPID");
60+ msg << (quint32) getpid();
61+
62+ QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().asyncCall(msg);
63+ if (reply.isValid()) {
64+ logindSessionPath = reply.value().path();
65+
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)));
69+ } else {
70+ qWarning() << "Failed to get logind session path" << reply.error().message();
71+ }
72+ }
73+
74+ bool checkLogin1Call(const QString &method) const
75+ {
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"));
79+ }
80+
81+ void makeLogin1Call(const QString &method, const QVariantList &args)
82+ {
83+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
84+ LOGIN1_PATH,
85+ LOGIN1_IFACE,
86+ method);
87+ msg.setArguments(args);
88+ QDBusConnection::systemBus().asyncCall(msg);
89+ }
90+
91+ void checkActive()
92+ {
93+ if (logindSessionPath.isEmpty()) {
94+ qWarning() << "Invalid session path";
95+ return;
96+ }
97+
98+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
99+ logindSessionPath,
100+ "org.freedesktop.DBus.Properties",
101+ "Get");
102+ msg << LOGIN1_SESSION_IFACE;
103+ msg << ACTIVE_KEY;
104+
105+ QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
106+ if (reply.isValid()) {
107+ isSessionActive = reply.value().toBool();
108+ qDebug() << "Session" << logindSessionPath << "is active:" << isSessionActive;
109+ } else {
110+ qWarning() << "Failed to get Active property" << reply.error().message();
111+ }
112+ }
113+
114+ quint32 screensaverActiveTime() const
115+ {
116+ if (!isSessionActive && screensaverActiveTimer.isValid()) {
117+ return screensaverActiveTimer.elapsed() / 1000;
118+ }
119+
120+ return 0;
121+ }
122+
123+ quint64 idleSinceUSecTimestamp() const
124+ {
125+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
126+ logindSessionPath,
127+ "org.freedesktop.DBus.Properties",
128+ "Get");
129+ msg << LOGIN1_SESSION_IFACE;
130+ msg << IDLE_SINCE_KEY;
131+
132+ QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
133+ if (reply.isValid()) {
134+ return reply.value().value<quint64>();
135+ } else {
136+ qWarning() << "Failed to get IdleSinceHint property" << reply.error().message();
137+ }
138+
139+ return 0;
140+ }
141+
142+ void setIdleHint(bool idle)
143+ {
144+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
145+ logindSessionPath,
146+ LOGIN1_SESSION_IFACE,
147+ "SetIdleHint");
148+ msg << idle;
149+ QDBusConnection::systemBus().asyncCall(msg);
150+ }
151+
152+private Q_SLOTS:
153+ void onPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
154+ {
155+ Q_UNUSED(iface)
156+
157+ if (changedProps.contains(ACTIVE_KEY) || invalidatedProps.contains(ACTIVE_KEY)) {
158+ if (changedProps.value(ACTIVE_KEY).isValid()) {
159+ isSessionActive = changedProps.value(ACTIVE_KEY).toBool();
160+ } else {
161+ checkActive();
162+ }
163+
164+ Q_EMIT screensaverActiveChanged(!isSessionActive);
165+
166+ if (isSessionActive) {
167+ screensaverActiveTimer.invalidate();
168+ setIdleHint(false);
169+ } else {
170+ screensaverActiveTimer.start();
171+ setIdleHint(true);
172+ }
173+ }
174+ }
175+
176+Q_SIGNALS:
177+ void screensaverActiveChanged(bool active);
178+};
179+
180+Q_GLOBAL_STATIC(DBusUnitySessionServicePrivate, d)
22181
23182DBusUnitySessionService::DBusUnitySessionService()
24183 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
25184{
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()));
191+ } else {
192+ qWarning() << "Failed to connect to logind's session Lock/Unlock signals";
193+ }
26194}
27195
28196void DBusUnitySessionService::Logout()
29197{
30198 // TODO ask the apps to quit and then emit the signal
31− Q_EMIT logoutReady();
199+ Q_EMIT LogoutReady();
32200}
33201
34202void DBusUnitySessionService::EndSession()
@@ -40,39 +208,136 @@ void DBusUnitySessionService::EndSession()
40208 QDBusConnection::sessionBus().asyncCall(msg);
41209}
42210
211+bool DBusUnitySessionService::CanHibernate() const
212+{
213+ return d->checkLogin1Call("CanHibernate");
214+}
215+
216+bool DBusUnitySessionService::CanSuspend() const
217+{
218+ return d->checkLogin1Call("CanSuspend");
219+}
220+
221+bool DBusUnitySessionService::CanHybridSleep() const
222+{
223+ return d->checkLogin1Call("CanHybridSleep");
224+}
225+
226+bool DBusUnitySessionService::CanReboot() const
227+{
228+ return d->checkLogin1Call("CanReboot");
229+}
230+
231+bool DBusUnitySessionService::CanShutdown() const
232+{
233+ return d->checkLogin1Call("CanPowerOff");
234+}
235+
236+bool DBusUnitySessionService::CanLock() const
237+{
238+ return true; // FIXME
239+}
240+
241+QString DBusUnitySessionService::UserName() const
242+{
243+ struct passwd *p = getpwuid(geteuid());
244+ if (p) {
245+ return QString::fromUtf8(p->pw_name);
246+ }
247+
248+ return QString();
249+}
250+
251+QString DBusUnitySessionService::RealName() const
252+{
253+ struct passwd *p = getpwuid(geteuid());
254+ if (p) {
255+ const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
256+ if (!gecos.isEmpty()) {
257+ return gecos.split(QLatin1Char(',')).first();
258+ }
259+ }
260+
261+ return QString();
262+}
263+
264+QString DBusUnitySessionService::HostName() const
265+{
266+ char hostName[512];
267+ if (gethostname(hostName, sizeof(hostName)) == -1) {
268+ qWarning() << "Could not determine local hostname";
269+ return QString();
270+ }
271+ hostName[sizeof(hostName) - 1] = '\0';
272+ return QString::fromLocal8Bit(hostName);
273+}
274+
275+void DBusUnitySessionService::PromptLock()
276+{
277+ Q_EMIT LockRequested();
278+}
279+
280+void DBusUnitySessionService::Lock()
281+{
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",
285+ sessionPath,
286+ "org.freedesktop.DisplayManager.Session",
287+ "Lock");
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();
292+ } else {
293+ // emit Locked when the call succeeds
294+ Q_EMIT Locked();
295+ }
296+}
297+
298+bool DBusUnitySessionService::IsLocked() const
299+{
300+ return !d->isSessionActive;
301+}
302+
43303void DBusUnitySessionService::RequestLogout()
44304{
45− Q_EMIT logoutRequested(false);
305+ Q_EMIT LogoutRequested(false);
46306}
47307
48308void DBusUnitySessionService::Reboot()
49309{
50− QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
51− "/org/freedesktop/login1",
52− "org.freedesktop.login1.Manager",
53− "Reboot");
54− msg << false;
55− QDBusConnection::systemBus().asyncCall(msg);
310+ d->makeLogin1Call("Reboot", {false});
56311}
57312
58313void DBusUnitySessionService::RequestReboot()
59314{
60− Q_EMIT rebootRequested(false);
315+ Q_EMIT RebootRequested(false);
61316}
62317
63318void DBusUnitySessionService::Shutdown()
64319{
65− QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
66− "/org/freedesktop/login1",
67− "org.freedesktop.login1.Manager",
68− "PowerOff");
69− msg << false;
70− QDBusConnection::systemBus().asyncCall(msg);
320+ d->makeLogin1Call("PowerOff", {false});
321+}
322+
323+void DBusUnitySessionService::Suspend()
324+{
325+ d->makeLogin1Call("Suspend", {false});
326+}
327+
328+void DBusUnitySessionService::Hibernate()
329+{
330+ d->makeLogin1Call("Hibernate", {false});
331+}
332+
333+void DBusUnitySessionService::HybridSleep()
334+{
335+ d->makeLogin1Call("HybridSleep", {false});
71336}
72337
73338void DBusUnitySessionService::RequestShutdown()
74339{
75− Q_EMIT shutdownRequested(false);
340+ Q_EMIT ShutdownRequested(false);
76341}
77342
78343enum class Action : unsigned
@@ -83,12 +348,8 @@ enum class Action : unsigned
83348 NONE
84349};
85350
86−DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
87− : UnityDBusObject("/org/gnome/SessionManager/EndSessionDialog", "com.canonical.Unity")
88−{
89−}
90351
91−void DBusGnomeSessionManagerWrapper::performAsyncCall(const QString &method)
352+void performAsyncUnityCall(const QString &method)
92353{
93354 const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.Unity",
94355 "/com/canonical/Unity/Session",
@@ -97,29 +358,111 @@ void DBusGnomeSessionManagerWrapper::performAsyncCall(const QString &method)
97358 QDBusConnection::sessionBus().asyncCall(msg);
98359}
99360
361+
362+DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
363+ : UnityDBusObject("/org/gnome/SessionManager/EndSessionDialog", "com.canonical.Unity")
364+{
365+}
366+
100367void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
101368{
102369 Q_UNUSED(arg_1);
103370 Q_UNUSED(max_wait);
104371 Q_UNUSED(inhibitors);
105372
106− const Action action = (Action)type;
107−
108− switch (action)
373+ switch (static_cast<Action>(type))
109374 {
110375 case Action::LOGOUT:
111− performAsyncCall("RequestLogout");
376+ performAsyncUnityCall("RequestLogout");
112377 break;
113378
114379 case Action::REBOOT:
115− performAsyncCall("RequestReboot");
380+ performAsyncUnityCall("RequestReboot");
116381 break;
117382
118383 case Action::SHUTDOWN:
119− performAsyncCall("RequestShutdown");
384+ performAsyncUnityCall("RequestShutdown");
120385 break;
121386
122387 default:
123388 break;
124389 }
125390}
391+
392+
393+DBusGnomeScreensaverWrapper::DBusGnomeScreensaverWrapper()
394+ : UnityDBusObject("/org/gnome/ScreenSaver", "org.gnome.ScreenSaver")
395+{
396+ connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusGnomeScreensaverWrapper::ActiveChanged);
397+}
398+
399+bool DBusGnomeScreensaverWrapper::GetActive() const
400+{
401+ return !d->isSessionActive; // return whether the session is not active
402+}
403+
404+void DBusGnomeScreensaverWrapper::SetActive(bool lock)
405+{
406+ if (lock) {
407+ Lock();
408+ }
409+}
410+
411+void DBusGnomeScreensaverWrapper::Lock()
412+{
413+ performAsyncUnityCall("Lock");
414+}
415+
416+quint32 DBusGnomeScreensaverWrapper::GetActiveTime() const
417+{
418+ return d->screensaverActiveTime();
419+}
420+
421+void DBusGnomeScreensaverWrapper::SimulateUserActivity()
422+{
423+ d->setIdleHint(false);
424+}
425+
426+
427+DBusScreensaverWrapper::DBusScreensaverWrapper()
428+ : UnityDBusObject("/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver")
429+{
430+ QDBusConnection::sessionBus().registerObject("/ScreenSaver", this, QDBusConnection::ExportScriptableContents); // compat path, also register here
431+ connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusScreensaverWrapper::ActiveChanged);
432+}
433+
434+bool DBusScreensaverWrapper::GetActive() const
435+{
436+ return !d->isSessionActive; // return whether the session is not active
437+}
438+
439+bool DBusScreensaverWrapper::SetActive(bool lock)
440+{
441+ if (lock) {
442+ Lock();
443+ return true;
444+ }
445+ return false;
446+}
447+
448+void DBusScreensaverWrapper::Lock()
449+{
450+ performAsyncUnityCall("Lock");
451+}
452+
453+quint32 DBusScreensaverWrapper::GetActiveTime() const
454+{
455+ return d->screensaverActiveTime();
456+}
457+
458+quint32 DBusScreensaverWrapper::GetSessionIdleTime() const
459+{
460+ return QDateTime::fromMSecsSinceEpoch(d->idleSinceUSecTimestamp()/1000).secsTo(QDateTime::currentDateTime());
461+}
462+
463+void DBusScreensaverWrapper::SimulateUserActivity()
464+{
465+ d->setIdleHint(false);
466+}
467+
468+#include "dbusunitysessionservice.moc"
plugins/Unity/Session/dbusunitysessionservice.h+194−17
@@ -1,5 +1,5 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
1+/*
2+ * Copyright (C) 2014, 2015 Canonical, Ltd.
33 *
44 * This program is free software: you can redistribute it and/or modify it under
55 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -29,7 +29,7 @@ Q_DECLARE_METATYPE(QList<QDBusObjectPath>)
2929 * interface.
3030 *
3131 * com.canonical.Unity.Session interface provides public methods
32− * and signals to handle Logout/Reboot/Shutdown.
32+ * and signals to handle eg. Logout/Reboot/Shutdown.
3333 */
3434class DBusUnitySessionService : public UnityDBusObject
3535{
@@ -48,43 +48,57 @@ public:
4848
4949Q_SIGNALS:
5050 /**
51− * logoutRequested signal
51+ * LogoutRequested signal
5252 *
5353 * This signal is emitted when some applications request the system to
5454 * logout.
5555 * @param have_inhibitors if there are any special running applications
5656 * which inhibit the logout.
5757 */
58− Q_SCRIPTABLE void logoutRequested(bool have_inhibitors);
58+ Q_SCRIPTABLE void LogoutRequested(bool have_inhibitors);
5959
6060 /**
61− * rebootRequested signal
61+ * RebootRequested signal
6262 *
6363 * This signal is emitted when some applications request the system to
6464 * reboot.
6565 * @param have_inhibitors if there are any special running applications
6666 * which inhibit the reboot.
6767 */
68− Q_SCRIPTABLE void rebootRequested(bool have_inhibitors);
68+ Q_SCRIPTABLE void RebootRequested(bool have_inhibitors);
6969
7070 /**
71− * shutdownRequested signal
71+ * ShutdownRequested signal
7272 *
7373 * This signal is emitted when some applications request the system to
7474 * shutdown.
7575 * @param have_inhibitors if there are any special running applications
7676 * which inhibit the shutdown.
7777 */
78− Q_SCRIPTABLE void shutdownRequested(bool have_inhibitors);
79−
78+ Q_SCRIPTABLE void ShutdownRequested(bool have_inhibitors);
8079
8180 /**
82− * logoutReady signal
81+ * LogoutReady signal
8382 *
8483 * This signal is emitted when all the apps are closed. And the system
8584 * is safe to logout.
8685 */
87− void logoutReady();
86+ Q_SCRIPTABLE void LogoutReady();
87+
88+ /**
89+ * Emitted as a result of calling PromptLock()
90+ */
91+ Q_SCRIPTABLE void LockRequested();
92+
93+ /**
94+ * Emitted after the session has been locked.
95+ */
96+ Q_SCRIPTABLE void Locked();
97+
98+ /**
99+ * Emitted after the session has been unlocked.
100+ */
101+ Q_SCRIPTABLE void Unlocked();
88102
89103public Q_SLOTS:
90104 /**
@@ -114,10 +128,33 @@ public Q_SLOTS:
114128 */
115129 Q_SCRIPTABLE void Shutdown();
116130
131+ /**
132+ * Suspend the system
133+ *
134+ * This method puts the system into sleep without user's confirmation.
135+ */
136+ Q_SCRIPTABLE void Suspend();
137+
138+ /**
139+ * Hibernate the system
140+ *
141+ * This method puts the system into hibernation without user's confirmation.
142+ */
143+ Q_SCRIPTABLE void Hibernate();
144+
145+ /**
146+ * Hybrid sleep
147+ *
148+ * This method puts the system into hybrid sleep without user's confirmation.
149+ *
150+ * @since unity8
151+ */
152+ Q_SCRIPTABLE void HybridSleep();
153+
117154 /**
118155 * Issue a logout request.
119156 *
120− * This method emits the logoutRequested signal to the shell with a boolean
157+ * This method emits the LogoutRequested signal to the shell with a boolean
121158 * which indicates if there's any inhibitors. The shell should receive
122159 * this signal and display a dialog to ask the user to confirm the logout
123160 * action. If confirmed, shell can call Logout() method to logout.
@@ -127,21 +164,21 @@ public Q_SLOTS:
127164 /**
128165 * Issue a reboot request.
129166 *
130− * This method emits the rebootRequested signal to the shell with a boolean
167+ * This method emits the RebootRequested signal to the shell with a boolean
131168 * which indicates if there's any inhibitors. The shell should receive
132169 * this signal and display a dialog to ask the user to confirm the reboot
133− * action. If confirmed, shell can call Reboot() method to reboot
170+ * action. If confirmed, shell can call Reboot() method to reboot.
134171 */
135172 Q_SCRIPTABLE void RequestReboot();
136173
137174 /**
138175 * Issue a shutdown request.
139176 *
140− * This method emits the shutdownRequested signal to the shell with a
177+ * This method emits the ShutdownRequested signal to the shell with a
141178 * boolean which indicates if there's any inhibitors.
142179 * The shell should receive
143180 * this signal and display a dialog to ask the user to confirm the reboot
144− * action. If confirmed, shell can call Shutdown() method to reboot
181+ * action. If confirmed, shell can call Shutdown() method to shutdown.
145182 */
146183 Q_SCRIPTABLE void RequestShutdown();
147184
@@ -152,6 +189,68 @@ public Q_SLOTS:
152189 * current DBus session bus.
153190 */
154191 Q_SCRIPTABLE void EndSession();
192+
193+ /**
194+ * @return whether the system is capable of hibernating
195+ */
196+ Q_SCRIPTABLE bool CanHibernate() const;
197+
198+ /**
199+ * @return whether the system is capable of suspending
200+ */
201+ Q_SCRIPTABLE bool CanSuspend() const;
202+
203+ /**
204+ * @return whether the system is capable of hybrid sleep
205+ * @since unity8
206+ */
207+ Q_SCRIPTABLE bool CanHybridSleep() const;
208+
209+ /**
210+ * @return whether the system is capable of rebooting
211+ * @since unity8
212+ */
213+ Q_SCRIPTABLE bool CanReboot() const;
214+
215+ /**
216+ * @return whether the system is capable of shutting down
217+ */
218+ Q_SCRIPTABLE bool CanShutdown() const;
219+
220+ /**
221+ * @return whether the system is capable of locking the session
222+ */
223+ Q_SCRIPTABLE bool CanLock() const;
224+
225+ /**
226+ * @return the login name of the current user
227+ */
228+ Q_SCRIPTABLE QString UserName() const;
229+
230+ /**
231+ * @return the real name of the current user
232+ */
233+ Q_SCRIPTABLE QString RealName() const;
234+
235+ /**
236+ * @return the local hostname
237+ */
238+ Q_SCRIPTABLE QString HostName() const;
239+
240+ /**
241+ * Request that the session get locked, emits signal LockRequested()
242+ */
243+ Q_SCRIPTABLE void PromptLock();
244+
245+ /**
246+ * Locks the session immediately
247+ */
248+ Q_SCRIPTABLE void Lock();
249+
250+ /**
251+ * @return whether the session is currently locked
252+ */
253+ Q_SCRIPTABLE bool IsLocked() const;
155254};
156255
157256class DBusGnomeSessionManagerWrapper : public UnityDBusObject
@@ -169,4 +268,82 @@ public Q_SLOTS:
169268private:
170269 void performAsyncCall(const QString &method);
171270};
271+
272+class DBusGnomeScreensaverWrapper: public UnityDBusObject
273+{
274+ Q_OBJECT
275+ Q_CLASSINFO("D-Bus Interface", "org.gnome.ScreenSaver")
276+
277+public:
278+ DBusGnomeScreensaverWrapper();
279+ ~DBusGnomeScreensaverWrapper() = default;
280+
281+public Q_SLOTS:
282+ /**
283+ * @return whether the session is currently locked (screensaver is on)
284+ */
285+ Q_SCRIPTABLE bool GetActive() const;
286+
287+ /**
288+ * Locks the session immediately if @p lock is true
289+ */
290+ Q_SCRIPTABLE void SetActive(bool lock);
291+
292+ /**
293+ * Locks the session immediately
294+ */
295+ Q_SCRIPTABLE void Lock();
296+
297+ /**
298+ * @return the number of seconds elapsed since the screensaver had been active
299+ */
300+ Q_SCRIPTABLE quint32 GetActiveTime() const;
301+
302+ Q_SCRIPTABLE void SimulateUserActivity();
303+
304+Q_SIGNALS:
305+ void ActiveChanged(bool active);
306+};
307+
308+class DBusScreensaverWrapper: public UnityDBusObject
309+{
310+ Q_OBJECT
311+ Q_CLASSINFO("D-Bus Interface", "org.freedesktop.ScreenSaver")
312+
313+public:
314+ DBusScreensaverWrapper();
315+ ~DBusScreensaverWrapper() = default;
316+
317+public Q_SLOTS:
318+ /**
319+ * @return whether the session is currently locked (screensaver is on)
320+ */
321+ Q_SCRIPTABLE bool GetActive() const;
322+
323+ /**
324+ * Locks the session immediately if @p lock is true
325+ */
326+ Q_SCRIPTABLE bool SetActive(bool lock);
327+
328+ /**
329+ * Locks the session immediately
330+ */
331+ Q_SCRIPTABLE void Lock();
332+
333+ /**
334+ * @return the number of seconds elapsed since the screensaver had been active
335+ */
336+ Q_SCRIPTABLE quint32 GetActiveTime() const;
337+
338+ /**
339+ * @return the number of seconds that this session has been idle
340+ */
341+ Q_SCRIPTABLE quint32 GetSessionIdleTime() const;
342+
343+ Q_SCRIPTABLE void SimulateUserActivity();
344+
345+Q_SIGNALS:
346+ void ActiveChanged(bool active);
347+};
348+
172349#endif // DBUSUNITYSESSIONSERVICE_H
plugins/Unity/Session/plugin.cpp+2
@@ -27,6 +27,8 @@
2727static QObject *dbusunitysessionservice_provider(QQmlEngine */*engine*/, QJSEngine */*jsEngine*/)
2828{
2929 new DBusGnomeSessionManagerWrapper();
30+ new DBusGnomeScreensaverWrapper();
31+ new DBusScreensaverWrapper();
3032 return new DBusUnitySessionService();
3133}
3234
tests/plugins/Unity/Session/sessionbackendtest.cpp+54−8
@@ -24,6 +24,11 @@
2424#include <QDBusInterface>
2525#include <QDBusReply>
2626#include <QDBusVariant>
27+#include <QDebug>
28+#include <QDBusObjectPath>
29+
30+#include <unistd.h>
31+#include <sys/types.h>
2732
2833#include "dbusunitysessionservice.h"
2934
@@ -52,9 +57,10 @@ private Q_SLOTS:
5257 QTest::addColumn<QString>("method");
5358 QTest::addColumn<QString>("signal");
5459
55− QTest::newRow("Logout") << "RequestLogout" << "logoutRequested(bool)";
56− QTest::newRow("Reboot") << "RequestReboot" << "rebootRequested(bool)";
57− QTest::newRow("Shutdown") << "RequestShutdown" << "shutdownRequested(bool)";
60+ QTest::newRow("Logout") << "RequestLogout" << "LogoutRequested(bool)";
61+ QTest::newRow("Reboot") << "RequestReboot" << "RebootRequested(bool)";
62+ QTest::newRow("Shutdown") << "RequestShutdown" << "ShutdownRequested(bool)";
63+ QTest::newRow("PromptLock") << "PromptLock" << "LockRequested()";
5864 }
5965
6066 void testUnitySessionLogoutRequested() {
@@ -65,7 +71,7 @@ private Q_SLOTS:
6571 QCoreApplication::processEvents(); // to let the service register on DBus
6672
6773 // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
68− QSignalSpy spy(&dbusUnitySessionService, signal.prepend(QSIGNAL_CODE).toLocal8Bit().constData());
74+ QSignalSpy spy(&dbusUnitySessionService, qPrintable(signal.prepend(QSIGNAL_CODE)));
6975
7076 QDBusReply<void> reply = dbusUnitySession->call(method);
7177 QCOMPARE(reply.isValid(), true);
@@ -77,9 +83,9 @@ private Q_SLOTS:
7783 QTest::addColumn<uint>("method");
7884 QTest::addColumn<QString>("signal");
7985
80− QTest::newRow("Logout") << (uint)Action::LOGOUT << "logoutRequested(bool)";
81− QTest::newRow("Shutdown") << (unsigned)Action::SHUTDOWN << "shutdownRequested(bool)";
82− QTest::newRow("Reboot") << (unsigned)Action::REBOOT << "rebootRequested(bool)";
86+ QTest::newRow("Logout") << (uint)Action::LOGOUT << "LogoutRequested(bool)";
87+ QTest::newRow("Shutdown") << (uint)Action::SHUTDOWN << "ShutdownRequested(bool)";
88+ QTest::newRow("Reboot") << (uint)Action::REBOOT << "RebootRequested(bool)";
8389 }
8490
8591 void testGnomeSessionWrapper() {
@@ -92,7 +98,7 @@ private Q_SLOTS:
9298 // Spy on the given signal on the /com/canonical/Unity/Session object
9399 // as proof we are actually calling the actual method.
94100 // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
95− QSignalSpy spy(&dbusUnitySessionService, signal.prepend(QSIGNAL_CODE).toLocal8Bit().constData());
101+ QSignalSpy spy(&dbusUnitySessionService, qPrintable(signal.prepend(QSIGNAL_CODE)));
96102
97103 DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
98104 QCoreApplication::processEvents(); // to let the service register on DBus
@@ -116,6 +122,46 @@ private Q_SLOTS:
116122 QCOMPARE(spy.count(), 1);
117123 }
118124
125+ void testUserName() {
126+ DBusUnitySessionService dbusUnitySessionService;
127+ QCoreApplication::processEvents(); // to let the service register on DBus
128+
129+ QProcess * proc = new QProcess(this);
130+ proc->start("id -un", QProcess::ReadOnly);
131+ proc->waitForFinished();
132+ const QByteArray out = proc->readAll().trimmed();
133+
134+ QCOMPARE(dbusUnitySessionService.UserName(), QString::fromUtf8(out));
135+ }
136+
137+ void testRealName() {
138+ DBusUnitySessionService dbusUnitySessionService;
139+ QCoreApplication::processEvents(); // to let the service register on DBus
140+
141+ QDBusInterface accIface("org.freedesktop.Accounts", "/org/freedesktop/Accounts", "org.freedesktop.Accounts", QDBusConnection::systemBus());
142+ if (accIface.isValid()) {
143+ QDBusReply<QDBusObjectPath> userPath = accIface.asyncCall("FindUserById", static_cast<qint64>(geteuid()));
144+ if (userPath.isValid()) {
145+ QDBusInterface userAccIface("org.freedesktop.Accounts", userPath.value().path(), "org.freedesktop.Accounts.User", QDBusConnection::systemBus());
146+ QCOMPARE(dbusUnitySessionService.RealName(), userAccIface.property("RealName").toString());
147+ }
148+ }
149+ }
150+
151+ void testLogin1Capabilities() {
152+ DBusUnitySessionService dbusUnitySessionService;
153+ QDBusInterface login1face("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", QDBusConnection::systemBus());
154+ QCoreApplication::processEvents(); // to let the services register on DBus
155+
156+ if (login1face.isValid()) {
157+ QCOMPARE(dbusUnitySessionService.CanHibernate(), (login1face.call("CanHibernate").arguments().first().toString() != "no"));
158+ QCOMPARE(dbusUnitySessionService.CanSuspend(), (login1face.call("CanSuspend").arguments().first().toString() != "no"));
159+ QCOMPARE(dbusUnitySessionService.CanReboot(), (login1face.call("CanReboot").arguments().first().toString() != "no"));
160+ QCOMPARE(dbusUnitySessionService.CanShutdown(), (login1face.call("CanPowerOff").arguments().first().toString() != "no"));
161+ QCOMPARE(dbusUnitySessionService.CanHybridSleep(), (login1face.call("CanHybridSleep").arguments().first().toString() != "no"));
162+ }
163+ }
164+
119165private:
120166 QDBusInterface *dbusUnitySession;
121167};