Plasma-workspace

sessionmanagement.cpp
1/*
2 SPDX-FileCopyrightText: 2019 David Edmundson <davidedmundson@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "sessionmanagement.h"
8
9#include "sessionmanagementbackend.h"
10
11#include <KAuthorized>
12#include <KConfigGroup>
13#include <KSharedConfig>
14
15#include <QDBusPendingCallWatcher>
16#include <QEventLoopLocker>
17
18#include <iostream>
19
20#include "logoutprompt_interface.h"
21#include "screenlocker_interface.h"
22#include "shutdown_interface.h"
23
24#include "libkworkspace_debug.h"
25
26using namespace Qt::StringLiterals;
27
28static void lockQuitUntilFinished(QDBusPendingCall pendingCall)
29{
30 auto watcher = new QDBusPendingCallWatcher(pendingCall);
31 QEventLoopLocker eventLoopLocker;
32
33 // Keep event loop locker alive whist the call is in progress to keep application running
34
35 // If the recipient is dbus activated and the sender quits before the target is up
36 // the method may not be dispatched.
37 // See https://gitlab.freedesktop.org/dbus/dbus/-/issues/72
38 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, [watcher, eventLoopLocker = std::move(eventLoopLocker)]() {
39 watcher->deleteLater();
40 });
41}
42
43// add a constructor with the service names and paths pre-populated
44class LogoutPromptIface : public OrgKdeLogoutPromptInterface
45{
46 Q_OBJECT
47public:
48 LogoutPromptIface()
49 : OrgKdeLogoutPromptInterface(QStringLiteral("org.kde.LogoutPrompt"), QStringLiteral("/LogoutPrompt"), QDBusConnection::sessionBus())
50 {
51 }
52};
53
54class ShutdownIface : public OrgKdeShutdownInterface
55{
56 Q_OBJECT
57public:
58 ShutdownIface()
59 : OrgKdeShutdownInterface(QStringLiteral("org.kde.Shutdown"), QStringLiteral("/Shutdown"), QDBusConnection::sessionBus())
60 {
61 }
62};
63
64SessionManagement::SessionManagement(QObject *parent)
65 : QObject(parent)
66{
67 auto backend = SessionBackend::self();
68 connect(backend, &SessionBackend::stateChanged, this, &SessionManagement::stateChanged);
69 connect(backend, &SessionBackend::canShutdownChanged, this, &SessionManagement::canShutdownChanged);
70 connect(backend, &SessionBackend::canRebootChanged, this, &SessionManagement::canRebootChanged);
71 connect(backend, &SessionBackend::canSuspendChanged, this, &SessionManagement::canSuspendChanged);
72 connect(backend, &SessionBackend::canHybridSuspendChanged, this, &SessionManagement::canHybridSuspendChanged);
73 connect(backend, &SessionBackend::canHibernateChanged, this, &SessionManagement::canHibernateChanged);
74 connect(backend, &SessionBackend::canSuspendThenHibernateChanged, this, &SessionManagement::canSuspendThenHibernateChanged);
75 connect(backend, &SessionBackend::aboutToSuspend, this, &SessionManagement::aboutToSuspend);
76 connect(backend, &SessionBackend::resumingFromSuspend, this, &SessionManagement::resumingFromSuspend);
77}
78
79bool SessionManagement::canShutdown() const
80{
81 return canLogout() && SessionBackend::self()->canShutdown();
82}
83
84bool SessionManagement::canReboot() const
85{
86 return canLogout() && SessionBackend::self()->canReboot();
87}
88
89bool SessionManagement::canLogout() const
90{
91 // checking both is for compatibility with old kiosk configs
92 // authorizeAction is the "correct" one
93 return KAuthorized::authorizeAction(QStringLiteral("logout")) && KAuthorized::authorize(QStringLiteral("logout"));
94}
95
96bool SessionManagement::canSuspend() const
97{
98 return SessionBackend::self()->canSuspend();
99}
100
101bool SessionManagement::canHybridSuspend() const
102{
103 return SessionBackend::self()->canHybridSuspend();
104}
105
106bool SessionManagement::canHibernate() const
107{
108 return SessionBackend::self()->canHibernate();
109}
110
111bool SessionManagement::canSuspendThenHibernate() const
112{
113 return SessionBackend::self()->canSuspendThenHibernate();
114}
115
116bool SessionManagement::canSwitchUser() const
117{
118 return KAuthorized::authorizeAction(QStringLiteral("start_new_session")) && SessionBackend::self()->canSwitchUser();
119}
120
121bool SessionManagement::canLock() const
122{
123 return KAuthorized::authorizeAction(QStringLiteral("lock_screen"));
124}
125
126bool SessionManagement::canSaveSession() const
127{
128 const KConfigGroup c(KSharedConfig::openConfig(u"ksmserverrc"_s), u"General"_s);
129 return canLogout() && c.readEntry("loginMode") == QLatin1String("restoreSavedSession");
130}
131
132SessionManagement::State SessionManagement::state() const
133{
134 return SessionBackend::self()->state();
135}
136
138{
139 if (qEnvironmentVariableIntValue("PLASMA_SESSION_GUI_TEST")) {
140 std::cout << "show logout screen " << std::endl;
141 return;
142 }
143
144 // Don't bother to check for whether the user normally wants confirmation or
145 // not; if this function was invoked, it means they do want to see the logout
146 // prompt right now
147 LogoutPromptIface iface;
148 lockQuitUntilFinished(iface.promptAll());
149}
150
152{
153 if (!canShutdown()) {
154 return;
155 }
156
157 if (qEnvironmentVariableIntValue("PLASMA_SESSION_GUI_TEST")) {
158 std::cout << "shutdown" << std::endl;
159 return;
160 }
161
162 bool confirm = confirmationMode == ConfirmationMode::ForcePrompt;
163 if (confirmationMode == ConfirmationMode::Default) {
164 confirm = SessionBackend::self()->confirmLogout();
165 }
166 if (confirm) {
167 LogoutPromptIface iface;
168 lockQuitUntilFinished(iface.promptShutDown());
169
170 } else {
171 ShutdownIface iface;
172 lockQuitUntilFinished(iface.logoutAndShutdown());
173 }
174}
175
176void SessionManagement::requestReboot(ConfirmationMode confirmationMode)
177{
178 if (!canReboot()) {
179 return;
180 }
181
182 if (qEnvironmentVariableIntValue("PLASMA_SESSION_GUI_TEST")) {
183 std::cout << "reboot" << std::endl;
184 return;
185 }
186
187 bool confirm = confirmationMode == ConfirmationMode::ForcePrompt;
188 if (confirmationMode == ConfirmationMode::Default) {
189 confirm = SessionBackend::self()->confirmLogout();
190 }
191 if (confirm) {
192 LogoutPromptIface iface;
193 lockQuitUntilFinished(iface.promptReboot());
194 } else {
195 ShutdownIface iface;
196 lockQuitUntilFinished(iface.logoutAndReboot());
197 }
198}
199
200void SessionManagement::requestLogout(ConfirmationMode confirmationMode)
201{
202 if (!canLogout()) {
203 return;
204 }
205 bool confirm = confirmationMode == ConfirmationMode::ForcePrompt;
206 if (confirmationMode == ConfirmationMode::Default) {
207 confirm = SessionBackend::self()->confirmLogout();
208 }
209 if (confirm) {
210 LogoutPromptIface iface;
211 lockQuitUntilFinished(iface.promptLogout());
212 } else {
213 ShutdownIface iface;
214 lockQuitUntilFinished(iface.logout());
215 }
216}
217
218void SessionManagement::suspend()
219{
220 if (!canSuspend()) {
221 return;
222 }
223 SessionBackend::self()->suspend();
224}
225
226void SessionManagement::hybridSuspend()
227{
228 if (!canHybridSuspend()) {
229 return;
230 }
231 SessionBackend::self()->hybridSuspend();
232}
233
234void SessionManagement::hibernate()
235{
236 if (!canHibernate()) {
237 return;
238 }
239 SessionBackend::self()->hibernate();
240}
241
242void SessionManagement::suspendThenHibernate()
243{
244 if (!canSuspendThenHibernate()) {
245 return;
246 }
247 SessionBackend::self()->suspendThenHibernate();
248}
249
250void SessionManagement::lock()
251{
252 if (!canLock()) {
253 return;
254 }
255 OrgFreedesktopScreenSaverInterface iface(QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/ScreenSaver"), QDBusConnection::sessionBus());
256 iface.Lock();
257}
258
259void SessionManagement::switchUser()
260{
261 if (!canSwitchUser() || !canLock()) {
262 return;
263 }
264
265 if (!qEnvironmentVariableIsSet("XDG_SEAT_PATH")) {
266 qCWarning(LIBKWORKSPACE_DEBUG) << "Cannot switch user: XDG_SEAT_PATH not set";
267 return;
268 }
269
270 // lock first
271 OrgFreedesktopScreenSaverInterface screenSaverIface(QStringLiteral("org.freedesktop.ScreenSaver"),
272 QStringLiteral("/ScreenSaver"),
274 QDBusPendingReply<> pendingLock = screenSaverIface.Lock();
275
276 // then tell the display manager to switch
277 auto watcher = new QDBusPendingCallWatcher(pendingLock, this);
280 if (watcher->isError()) {
281 qCWarning(LIBKWORKSPACE_DEBUG) << "Failed to lock screen before switching user:" << watcher->error().message();
282 return;
283 }
284 QDBusMessage switchToGreeterMessage = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DisplayManager"),
285 qEnvironmentVariable("XDG_SEAT_PATH"),
286 QStringLiteral("org.freedesktop.DisplayManager.Seat"),
287 u"SwitchToGreeter"_s);
288
289 QDBusConnection::systemBus().asyncCall(switchToGreeterMessage);
290 });
291}
292
293void SessionManagement::saveSession()
294{
295 if (!canSaveSession()) {
296 return;
297 }
298 ShutdownIface iface;
299 iface.saveSession();
300}
301
302#include "sessionmanagement.moc"
static Q_INVOKABLE bool authorize(const QString &action)
static Q_INVOKABLE bool authorizeAction(const QString &action)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
void requestShutdown(ConfirmationMode=ConfirmationMode::Default)
These requestX methods will either launch a prompt to shutdown or The user may cancel it at any point...
void requestLogoutPrompt()
...And this one will always show the prompt with all options, irrespective of whether it's ordinarily...
@ ForcePrompt
Always confirm, ask even if the user turned it off.
@ Default
Obey the user's confirmation setting.
QDBusPendingCall asyncCall(const QDBusMessage &message, int timeout) const const
QDBusConnection sessionBus()
QDBusConnection systemBus()
QDBusMessage createMethodCall(const QString &service, const QString &path, const QString &interface, const QString &method)
void finished(QDBusPendingCallWatcher *self)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:14:59 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.