@@ -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"