on xenial help sign in
Provide DBUS compatibility with various session services (suspend/hibernate, lock/unlock, screensaver, etc)
145ea72Lukáš Tinkl
plugins/AccountsService/AccountsService.cpp+1−2
@@ -141,9 +141,9 @@ void AccountsService::updateDemoEdges(bool async)
141 this, [this](QDBusPendingCallWatcher* watcher) {
141 this, [this](QDBusPendingCallWatcher* watcher) {
142
142
143 QDBusPendingReply<QDBusVariant> reply = *watcher;
143 QDBusPendingReply<QDBusVariant> reply = *watcher;
144+ watcher->deleteLater();
144 if (reply.isError()) {
145 if (reply.isError()) {
145 qWarning() << "Failed to get 'demo-edges' property - " << reply.error().message();
146 qWarning() << "Failed to get 'demo-edges' property - " << reply.error().message();
146− watcher->deleteLater();
147 return;
147 return;
148 }
148 }
149
149
@@ -152,7 +152,6 @@ void AccountsService::updateDemoEdges(bool async)
152 m_demoEdges = demoEdges;
152 m_demoEdges = demoEdges;
153 Q_EMIT demoEdgesChanged();
153 Q_EMIT demoEdgesChanged();
154 }
154 }
155− watcher->deleteLater();
156 });
155 });
157 if (!async) {
156 if (!async) {
158 watcher->waitForFinished();
157 watcher->waitForFinished();
plugins/Unity/Session/CMakeLists.txt+1
@@ -2,6 +2,7 @@ pkg_search_module(GIO REQUIRED gio-2.0)
2
2
3include_directories(
3include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
5+ ${CMAKE_CURRENT_BINARY_DIR}
5 ${GIO_INCLUDE_DIRS}
6 ${GIO_INCLUDE_DIRS}
6 ${libunity8-private_SOURCE_DIR}
7 ${libunity8-private_SOURCE_DIR}
7)
8)
plugins/Unity/Session/dbusunitysessionservice.cpp+371−28
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014, 2015 Canonical, Ltd.
3 *
3 *
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 @@
17// local
17// local
18#include "dbusunitysessionservice.h"
18#include "dbusunitysessionservice.h"
19
19
20+// system
21+#include <sys/types.h>
22+#include <unistd.h>
23+#include <pwd.h>
24+
20// Qt
25// Qt
26+#include <QDebug>
21#include <QDBusPendingCall>
27#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)
22
181
23DBusUnitySessionService::DBusUnitySessionService()
182DBusUnitySessionService::DBusUnitySessionService()
24 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
183 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
25{
184{
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+ }
26}
194}
27
195
28void DBusUnitySessionService::Logout()
196void DBusUnitySessionService::Logout()
29{
197{
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();
32}
200}
33
201
34void DBusUnitySessionService::EndSession()
202void DBusUnitySessionService::EndSession()
@@ -40,39 +208,136 @@ void DBusUnitySessionService::EndSession()
40 QDBusConnection::sessionBus().asyncCall(msg);
208 QDBusConnection::sessionBus().asyncCall(msg);
41}
209}
42
210
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+
43void DBusUnitySessionService::RequestLogout()
303void DBusUnitySessionService::RequestLogout()
44{
304{
45− Q_EMIT logoutRequested(false);
305+ Q_EMIT LogoutRequested(false);
46}
306}
47
307
48void DBusUnitySessionService::Reboot()
308void DBusUnitySessionService::Reboot()
49{
309{
50− QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
310+ d->makeLogin1Call("Reboot", {false});
51− "/org/freedesktop/login1",
52− "org.freedesktop.login1.Manager",
53− "Reboot");
54− msg << false;
55− QDBusConnection::systemBus().asyncCall(msg);
56}
311}
57
312
58void DBusUnitySessionService::RequestReboot()
313void DBusUnitySessionService::RequestReboot()
59{
314{
60− Q_EMIT rebootRequested(false);
315+ Q_EMIT RebootRequested(false);
61}
316}
62
317
63void DBusUnitySessionService::Shutdown()
318void DBusUnitySessionService::Shutdown()
64{
319{
65− QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
66− "/org/freedesktop/login1",
67− "org.freedesktop.login1.Manager",
68− "PowerOff");
320+ d->makeLogin1Call("PowerOff", {false});
69− msg << false;
321+}
70− QDBusConnection::systemBus().asyncCall(msg);
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});
71}
336}
72
337
73void DBusUnitySessionService::RequestShutdown()
338void DBusUnitySessionService::RequestShutdown()
74{
339{
75− Q_EMIT shutdownRequested(false);
340+ Q_EMIT ShutdownRequested(false);
76}
341}
77
342
78enum class Action : unsigned
343enum class Action : unsigned
@@ -83,12 +348,8 @@ enum class Action : unsigned
83 NONE
348 NONE
84};
349};
85
350
86−DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
87− : UnityDBusObject("/org/gnome/SessionManager/EndSessionDialog", "com.canonical.Unity")
88−{
89−}
90
351
91−void DBusGnomeSessionManagerWrapper::performAsyncCall(const QString &method)
352+void performAsyncUnityCall(const QString &method)
92{
353{
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);
98}
359}
99
360
361+
362+DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
363+ : UnityDBusObject("/org/gnome/SessionManager/EndSessionDialog", "com.canonical.Unity")
364+{
365+}
366+
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)
101{
368{
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);
105
372
106− const Action action = (Action)type;
373+ switch (static_cast<Action>(type))
107−
108− switch (action)
109 {
374 {
110 case Action::LOGOUT:
375 case Action::LOGOUT:
111− performAsyncCall("RequestLogout");
376+ performAsyncUnityCall("RequestLogout");
112 break;
377 break;
113
378
114 case Action::REBOOT:
379 case Action::REBOOT:
115− performAsyncCall("RequestReboot");
380+ performAsyncUnityCall("RequestReboot");
116 break;
381 break;
117
382
118 case Action::SHUTDOWN:
383 case Action::SHUTDOWN:
119− performAsyncCall("RequestShutdown");
384+ performAsyncUnityCall("RequestShutdown");
120 break;
385 break;
121
386
122 default:
387 default:
123 break;
388 break;
124 }
389 }
125}
390}
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−/*
1+/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014, 2015 Canonical, Ltd.
3 *
3 *
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
@@ -29,7 +29,7 @@ Q_DECLARE_METATYPE(QList<QDBusObjectPath>)
29 * interface.
29 * interface.
30 *
30 *
31 * com.canonical.Unity.Session interface provides public methods
31 * 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.
33 */
33 */
34class DBusUnitySessionService : public UnityDBusObject
34class DBusUnitySessionService : public UnityDBusObject
35{
35{
@@ -48,43 +48,57 @@ public:
48
48
49Q_SIGNALS:
49Q_SIGNALS:
50 /**
50 /**
51− * logoutRequested signal
51+ * LogoutRequested signal
52 *
52 *
53 * This signal is emitted when some applications request the system to
53 * This signal is emitted when some applications request the system to
54 * logout.
54 * logout.
55 * @param have_inhibitors if there are any special running applications
55 * @param have_inhibitors if there are any special running applications
56 * which inhibit the logout.
56 * which inhibit the logout.
57 */
57 */
58− Q_SCRIPTABLE void logoutRequested(bool have_inhibitors);
58+ Q_SCRIPTABLE void LogoutRequested(bool have_inhibitors);
59
59
60 /**
60 /**
61− * rebootRequested signal
61+ * RebootRequested signal
62 *
62 *
63 * This signal is emitted when some applications request the system to
63 * This signal is emitted when some applications request the system to
64 * reboot.
64 * reboot.
65 * @param have_inhibitors if there are any special running applications
65 * @param have_inhibitors if there are any special running applications
66 * which inhibit the reboot.
66 * which inhibit the reboot.
67 */
67 */
68− Q_SCRIPTABLE void rebootRequested(bool have_inhibitors);
68+ Q_SCRIPTABLE void RebootRequested(bool have_inhibitors);
69
69
70 /**
70 /**
71− * shutdownRequested signal
71+ * ShutdownRequested signal
72 *
72 *
73 * This signal is emitted when some applications request the system to
73 * This signal is emitted when some applications request the system to
74 * shutdown.
74 * shutdown.
75 * @param have_inhibitors if there are any special running applications
75 * @param have_inhibitors if there are any special running applications
76 * which inhibit the shutdown.
76 * which inhibit the shutdown.
77 */
77 */
78− Q_SCRIPTABLE void shutdownRequested(bool have_inhibitors);
78+ Q_SCRIPTABLE void ShutdownRequested(bool have_inhibitors);
79−
80
79
81 /**
80 /**
82− * logoutReady signal
81+ * LogoutReady signal
83 *
82 *
84 * This signal is emitted when all the apps are closed. And the system
83 * This signal is emitted when all the apps are closed. And the system
85 * is safe to logout.
84 * is safe to logout.
86 */
85 */
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();
88
102
89public Q_SLOTS:
103public Q_SLOTS:
90 /**
104 /**
@@ -114,10 +128,33 @@ public Q_SLOTS:
114 */
128 */
115 Q_SCRIPTABLE void Shutdown();
129 Q_SCRIPTABLE void Shutdown();
116
130
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+
117 /**
154 /**
118 * Issue a logout request.
155 * Issue a logout request.
119 *
156 *
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
121 * which indicates if there's any inhibitors. The shell should receive
158 * which indicates if there's any inhibitors. The shell should receive
122 * this signal and display a dialog to ask the user to confirm the logout
159 * this signal and display a dialog to ask the user to confirm the logout
123 * action. If confirmed, shell can call Logout() method to logout.
160 * action. If confirmed, shell can call Logout() method to logout.
@@ -127,21 +164,21 @@ public Q_SLOTS:
127 /**
164 /**
128 * Issue a reboot request.
165 * Issue a reboot request.
129 *
166 *
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
131 * which indicates if there's any inhibitors. The shell should receive
168 * which indicates if there's any inhibitors. The shell should receive
132 * this signal and display a dialog to ask the user to confirm the reboot
169 * 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.
134 */
171 */
135 Q_SCRIPTABLE void RequestReboot();
172 Q_SCRIPTABLE void RequestReboot();
136
173
137 /**
174 /**
138 * Issue a shutdown request.
175 * Issue a shutdown request.
139 *
176 *
140− * This method emits the shutdownRequested signal to the shell with a
177+ * This method emits the ShutdownRequested signal to the shell with a
141 * boolean which indicates if there's any inhibitors.
178 * boolean which indicates if there's any inhibitors.
142 * The shell should receive
179 * The shell should receive
143 * this signal and display a dialog to ask the user to confirm the reboot
180 * 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.
145 */
182 */
146 Q_SCRIPTABLE void RequestShutdown();
183 Q_SCRIPTABLE void RequestShutdown();
147
184
@@ -152,6 +189,68 @@ public Q_SLOTS:
152 * current DBus session bus.
189 * current DBus session bus.
153 */
190 */
154 Q_SCRIPTABLE void EndSession();
191 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;
155};
254};
156
255
157class DBusGnomeSessionManagerWrapper : public UnityDBusObject
256class DBusGnomeSessionManagerWrapper : public UnityDBusObject
@@ -169,4 +268,82 @@ public Q_SLOTS:
169private:
268private:
170 void performAsyncCall(const QString &method);
269 void performAsyncCall(const QString &method);
171};
270};
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+
172#endif // DBUSUNITYSESSIONSERVICE_H
349#endif // DBUSUNITYSESSIONSERVICE_H
plugins/Unity/Session/plugin.cpp+2
@@ -27,6 +27,8 @@
27static QObject *dbusunitysessionservice_provider(QQmlEngine */*engine*/, QJSEngine */*jsEngine*/)
27static QObject *dbusunitysessionservice_provider(QQmlEngine */*engine*/, QJSEngine */*jsEngine*/)
28{
28{
29 new DBusGnomeSessionManagerWrapper();
29 new DBusGnomeSessionManagerWrapper();
30+ new DBusGnomeScreensaverWrapper();
31+ new DBusScreensaverWrapper();
30 return new DBusUnitySessionService();
32 return new DBusUnitySessionService();
31}
33}
32
34
tests/plugins/Unity/Session/sessionbackendtest.cpp+54−8
@@ -24,6 +24,11 @@
24#include <QDBusInterface>
24#include <QDBusInterface>
25#include <QDBusReply>
25#include <QDBusReply>
26#include <QDBusVariant>
26#include <QDBusVariant>
27+#include <QDebug>
28+#include <QDBusObjectPath>
29+
30+#include <unistd.h>
31+#include <sys/types.h>
27
32
28#include "dbusunitysessionservice.h"
33#include "dbusunitysessionservice.h"
29
34
@@ -52,9 +57,10 @@ private Q_SLOTS:
52 QTest::addColumn<QString>("method");
57 QTest::addColumn<QString>("method");
53 QTest::addColumn<QString>("signal");
58 QTest::addColumn<QString>("signal");
54
59
55− QTest::newRow("Logout") << "RequestLogout" << "logoutRequested(bool)";
60+ QTest::newRow("Logout") << "RequestLogout" << "LogoutRequested(bool)";
56− QTest::newRow("Reboot") << "RequestReboot" << "rebootRequested(bool)";
61+ QTest::newRow("Reboot") << "RequestReboot" << "RebootRequested(bool)";
57− QTest::newRow("Shutdown") << "RequestShutdown" << "shutdownRequested(bool)";
62+ QTest::newRow("Shutdown") << "RequestShutdown" << "ShutdownRequested(bool)";
63+ QTest::newRow("PromptLock") << "PromptLock" << "LockRequested()";
58 }
64 }
59
65
60 void testUnitySessionLogoutRequested() {
66 void testUnitySessionLogoutRequested() {
@@ -65,7 +71,7 @@ private Q_SLOTS:
65 QCoreApplication::processEvents(); // to let the service register on DBus
71 QCoreApplication::processEvents(); // to let the service register on DBus
66
72
67 // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
73 // .. 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)));
69
75
70 QDBusReply<void> reply = dbusUnitySession->call(method);
76 QDBusReply<void> reply = dbusUnitySession->call(method);
71 QCOMPARE(reply.isValid(), true);
77 QCOMPARE(reply.isValid(), true);
@@ -77,9 +83,9 @@ private Q_SLOTS:
77 QTest::addColumn<uint>("method");
83 QTest::addColumn<uint>("method");
78 QTest::addColumn<QString>("signal");
84 QTest::addColumn<QString>("signal");
79
85
80− QTest::newRow("Logout") << (uint)Action::LOGOUT << "logoutRequested(bool)";
86+ QTest::newRow("Logout") << (uint)Action::LOGOUT << "LogoutRequested(bool)";
81− QTest::newRow("Shutdown") << (unsigned)Action::SHUTDOWN << "shutdownRequested(bool)";
87+ QTest::newRow("Shutdown") << (uint)Action::SHUTDOWN << "ShutdownRequested(bool)";
82− QTest::newRow("Reboot") << (unsigned)Action::REBOOT << "rebootRequested(bool)";
88+ QTest::newRow("Reboot") << (uint)Action::REBOOT << "RebootRequested(bool)";
83 }
89 }
84
90
85 void testGnomeSessionWrapper() {
91 void testGnomeSessionWrapper() {
@@ -92,7 +98,7 @@ private Q_SLOTS:
92 // Spy on the given signal on the /com/canonical/Unity/Session object
98 // Spy on the given signal on the /com/canonical/Unity/Session object
93 // as proof we are actually calling the actual method.
99 // as proof we are actually calling the actual method.
94 // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
100 // .. 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)));
96
102
97 DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
103 DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
98 QCoreApplication::processEvents(); // to let the service register on DBus
104 QCoreApplication::processEvents(); // to let the service register on DBus
@@ -116,6 +122,46 @@ private Q_SLOTS:
116 QCOMPARE(spy.count(), 1);
122 QCOMPARE(spy.count(), 1);
117 }
123 }
118
124
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+
119private:
165private:
120 QDBusInterface *dbusUnitySession;
166 QDBusInterface *dbusUnitySession;
121};
167};