25#include <QSessionManager>
26#if KWIN_BUILD_NOTIFICATIONS
27#include <KLocalizedString>
28#include <KNotification>
32#include "sessionadaptor.h"
34using namespace Qt::StringLiterals;
39static KConfig *sessionConfig(QString
id, QString key)
41 static KConfig *config =
nullptr;
42 static QString lastId;
43 static QString lastKey;
44 static QString pattern = QString(QLatin1String(
"session/%1_%2_%3")).arg(qApp->applicationName());
45 if (
id != lastId || key != lastKey) {
52 config =
new KConfig(pattern.arg(
id, key), KConfig::SimpleConfig);
57static const char *
const window_type_names[] = {
58 "Unknown",
"Normal",
"Desktop",
"Dock",
"Toolbar",
"Menu",
"Dialog",
59 "Override",
"TopMenu",
"Utility",
"Splash"};
62static const char *windowTypeToTxt(NET::WindowType
type)
64 if (
type >= NET::Unknown &&
type <= NET::Splash) {
65 return window_type_names[
type + 1];
70 qFatal(
"Unknown Window Type");
74static NET::WindowType txtToWindowType(
const char *txt)
76 for (
int i = NET::Unknown;
79 if (qstrcmp(txt, window_type_names[i + 1]) == 0) {
80 return static_cast<NET::WindowType
>(i);
83 return static_cast<NET::WindowType
>(-2);
91void SessionManager::storeSession(
const QString &sessionName, SMSavePhase phase)
93 qCDebug(KWIN_CORE) <<
"storing session" << sessionName <<
"in phase" << phase;
94 KConfig *config = sessionConfig(sessionName, QString());
96 KConfigGroup cg(config, QStringLiteral(
"Session"));
98 int active_client = -1;
101 for (
auto it = windows.begin(); it != windows.end(); ++it) {
102 X11Window *c = qobject_cast<X11Window *>(*it);
103 if (!c || c->isUnmanaged()) {
106 if (c->windowType() > NET::Splash) {
111 QByteArray sessionId = c->sessionId();
112 QString wmCommand = c->wmCommand();
113 if (sessionId.isEmpty()) {
116 if (wmCommand.isEmpty()) {
122 active_client = count;
125 storeClient(cg, count, c);
132 m_sessionActiveClient = active_client;
133 m_sessionDesktop = VirtualDesktopManager::self()->current();
135 cg.writeEntry(
"count", count);
136 cg.writeEntry(
"active", m_sessionActiveClient);
137 cg.writeEntry(
"desktop", m_sessionDesktop);
139 cg.writeEntry(
"count", count);
140 cg.writeEntry(
"active", m_sessionActiveClient);
141 cg.writeEntry(
"desktop", VirtualDesktopManager::self()->current());
146void SessionManager::storeClient(KConfigGroup &cg,
int num, X11Window *c)
148 c->setSessionActivityOverride(
false);
149 QString n = QString::number(num);
150 cg.writeEntry(QLatin1String(
"sessionId") + n, c->sessionId().constData());
151 cg.writeEntry(QLatin1String(
"windowRole") + n, c->windowRole());
152 cg.writeEntry(QLatin1String(
"wmCommand") + n, c->wmCommand());
153 cg.writeEntry(QLatin1String(
"resourceName") + n, c->resourceName());
154 cg.writeEntry(QLatin1String(
"resourceClass") + n, c->resourceClass());
155 cg.writeEntry(QLatin1String(
"geometry") + n, QRectF(c->calculateGravitation(
true), c->clientSize()).toRect());
156 cg.writeEntry(QLatin1String(
"restore") + n, c->geometryRestore());
157 cg.writeEntry(QLatin1String(
"fsrestore") + n, c->fullscreenGeometryRestore());
158 cg.writeEntry(QLatin1String(
"maximize") + n, (
int)c->maximizeMode());
159 cg.writeEntry(QLatin1String(
"fullscreen") + n, (
int)c->fullScreenMode());
160 cg.writeEntry(QLatin1String(
"desktop") + n, c->desktopId());
163 cg.writeEntry(QLatin1String(
"iconified") + n, c->isMinimized());
164 cg.writeEntry(QLatin1String(
"opacity") + n, c->opacity());
166 cg.writeEntry(QLatin1String(
"sticky") + n, c->isOnAllDesktops());
167 cg.writeEntry(QLatin1String(
"shaded") + n, c->isShade());
169 cg.writeEntry(QLatin1String(
"staysOnTop") + n, c->keepAbove());
170 cg.writeEntry(QLatin1String(
"keepBelow") + n, c->keepBelow());
171 cg.writeEntry(QLatin1String(
"skipTaskbar") + n, c->originalSkipTaskbar());
172 cg.writeEntry(QLatin1String(
"skipPager") + n, c->skipPager());
173 cg.writeEntry(QLatin1String(
"skipSwitcher") + n, c->skipSwitcher());
175 cg.writeEntry(QLatin1String(
"userNoBorder") + n, c->userNoBorder());
176 cg.writeEntry(QLatin1String(
"windowType") + n, windowTypeToTxt(c->windowType()));
177 cg.writeEntry(QLatin1String(
"shortcut") + n, c->shortcut().toString());
178 cg.writeEntry(QLatin1String(
"stackingOrder") + n,
workspace()->unconstrainedStackingOrder().indexOf(c));
179 cg.writeEntry(QLatin1String(
"activities") + n, c->activities());
185 KConfigGroup cg(KSharedConfig::openConfig(), QLatin1String(
"SubSession: ") + name);
187 int active_client = -1;
190 for (
auto it = windows.begin(); it != windows.end(); ++it) {
191 X11Window *c = qobject_cast<X11Window *>(*it);
200 if (sessionId.isEmpty()) {
203 if (wmCommand.isEmpty()) {
207 if (!sessionIds.contains(sessionId)) {
211 qCDebug(KWIN_CORE) <<
"storing" << sessionId;
214 active_client = count;
216 storeClient(cg, count, c);
218 cg.writeEntry(
"count", count);
219 cg.writeEntry(
"active", active_client);
231 KConfigGroup cg(sessionConfig(sessionName, QString()), QStringLiteral(
"Session"));
236void SessionManager::addSessionInfo(KConfigGroup &cg)
239 int count = cg.readEntry(
"count", 0);
240 int active_client = cg.readEntry(
"active", 0);
241 for (
int i = 1; i <= count; i++) {
242 QString n = QString::number(i);
244 session.append(info);
245 info->
sessionId = cg.readEntry(QLatin1String(
"sessionId") + n, QString()).toLatin1();
246 info->
windowRole = cg.readEntry(QLatin1String(
"windowRole") + n, QString());
247 info->
wmCommand = cg.readEntry(QLatin1String(
"wmCommand") + n, QString()).toLatin1();
248 info->
resourceName = cg.readEntry(QLatin1String(
"resourceName") + n, QString());
249 info->
resourceClass = cg.readEntry(QLatin1String(
"resourceClass") + n, QString()).toLower();
250 info->
geometry = cg.readEntry(QLatin1String(
"geometry") + n, QRect());
251 info->
restore = cg.readEntry(QLatin1String(
"restore") + n, QRect());
252 info->
fsrestore = cg.readEntry(QLatin1String(
"fsrestore") + n, QRect());
253 info->
maximized = cg.readEntry(QLatin1String(
"maximize") + n, 0);
254 info->
fullscreen = cg.readEntry(QLatin1String(
"fullscreen") + n, 0);
255 info->
desktop = cg.readEntry(QLatin1String(
"desktop") + n, 0);
256 info->
minimized = cg.readEntry(QLatin1String(
"iconified") + n,
false);
257 info->
opacity = cg.readEntry(QLatin1String(
"opacity") + n, 1.0);
258 info->
onAllDesktops = cg.readEntry(QLatin1String(
"sticky") + n,
false);
259 info->
shaded = cg.readEntry(QLatin1String(
"shaded") + n,
false);
260 info->
keepAbove = cg.readEntry(QLatin1String(
"staysOnTop") + n,
false);
261 info->
keepBelow = cg.readEntry(QLatin1String(
"keepBelow") + n,
false);
262 info->
skipTaskbar = cg.readEntry(QLatin1String(
"skipTaskbar") + n,
false);
263 info->
skipPager = cg.readEntry(QLatin1String(
"skipPager") + n,
false);
264 info->
skipSwitcher = cg.readEntry(QLatin1String(
"skipSwitcher") + n,
false);
265 info->
noBorder = cg.readEntry(QLatin1String(
"userNoBorder") + n,
false);
266 info->
windowType = txtToWindowType(cg.readEntry(QLatin1String(
"windowType") + n, QString()).toLatin1().constData());
267 info->
shortcut = cg.readEntry(QLatin1String(
"shortcut") + n, QString());
268 info->
active = (active_client == i);
269 info->
stackingOrder = cg.readEntry(QLatin1String(
"stackingOrder") + n, -1);
270 info->
activities = cg.readEntry(QLatin1String(
"activities") + n, QStringList());
276 KConfigGroup cg(KSharedConfig::openConfig(), QLatin1String(
"SubSession: ") + name);
308 if (!sessionId.isEmpty()) {
314 if (info->
sessionId == sessionId && sessionInfoWindowTypeMatch(c, info)) {
315 if (!windowRole.isEmpty()) {
318 session.removeAll(info);
325 session.removeAll(info);
338 && sessionInfoWindowTypeMatch(c, info)) {
339 if (wmCommand.isEmpty() || info->
wmCommand == wmCommand) {
341 session.removeAll(info);
352 new SessionAdaptor(
this);
353 QDBusConnection::sessionBus().registerObject(QStringLiteral(
"/Session"),
this);
363 return m_sessionState;
383 if (
state == m_sessionState) {
394 client->setSessionActivityOverride(
false);
398 m_sessionState =
state;
416 Q_ASSERT(calledFromDBus());
421 if (m_closingWindowsGuard) {
422 sendErrorReply(QDBusError::Failed, u
"Operation already in progress"_s);
426 m_closingWindowsGuard = std::make_unique<QObject>();
427 qCDebug(KWIN_CORE) <<
"Closing windows";
429 auto dbusMessage = message();
430 setDelayedReply(
true);
433 m_pendingWindows.clear();
434 m_pendingWindows.reserve(windows.size());
435 for (
const auto window : windows) {
436 if (
auto toplevelWindow = qobject_cast<XdgToplevelWindow *>(window)) {
438 m_pendingWindows.removeOne(toplevelWindow);
439 if (m_pendingWindows.empty()) {
441 m_closingWindowsGuard.reset();
442 QDBusConnection::sessionBus().send(dbusMessage.createReply(true));
445 m_pendingWindows.push_back(toplevelWindow);
446 toplevelWindow->closeWindow();
450 if (m_pendingWindows.empty()) {
451 m_closingWindowsGuard.reset();
452 QDBusConnection::sessionBus().send(dbusMessage.createReply(
true));
456 m_closeTimer.start(std::chrono::seconds(10));
457 m_closeTimer.setSingleShot(
true);
458 connect(&m_closeTimer, &QTimer::timeout, m_closingWindowsGuard.get(), [
this, dbusMessage] {
459#if KWIN_BUILD_NOTIFICATIONS
461 apps.reserve(m_pendingWindows.size());
462 std::transform(m_pendingWindows.cbegin(), m_pendingWindows.cend(), std::back_inserter(apps), [](const XdgToplevelWindow *window) -> QString {
463 const auto service = KService::serviceByDesktopName(window->desktopFileName());
464 return QChar(u
'•') + (service ? service->name() : window->caption());
466 apps.removeDuplicates();
467 qCDebug(KWIN_CORE) <<
"Not closed windows" << apps;
468 auto notification =
new KNotification(
"cancellogout", KNotification::DefaultEvent | KNotification::Persistent);
469 notification->setText(i18n(
"The following applications did not close:\n%1", apps.join(
'\n')));
470 auto cancel = notification->addAction(i18nc(
"@action:button",
"Cancel Logout"));
471 auto quit = notification->addAction(i18nc(
"@action::button",
"Log Out Anyway"));
472 connect(cancel, &KNotificationAction::activated, m_closingWindowsGuard.get(), [dbusMessage,
this] {
473 m_closingWindowsGuard.reset();
474 QDBusConnection::sessionBus().send(dbusMessage.createReply(false));
476 connect(quit, &KNotificationAction::activated, m_closingWindowsGuard.get(), [dbusMessage,
this] {
477 m_closingWindowsGuard.reset();
478 QDBusConnection::sessionBus().send(dbusMessage.createReply(true));
480 connect(notification, &KNotification::closed, m_closingWindowsGuard.get(), [dbusMessage,
this] {
481 m_closingWindowsGuard.reset();
482 QDBusConnection::sessionBus().send(dbusMessage.createReply(false));
484 notification->sendEvent();
486 m_closingWindowsGuard.reset();
487 QDBusConnection::sessionBus().send(dbusMessage.createReply(
false));
493void SessionManager::quit()
void setUpdatesDisabled(bool disable)
void loadSubSessionInfo(const QString &name)
~SessionManager() override
SessionInfo * takeSessionInfo(X11Window *)
SessionManager(QObject *parent)
void finishSessionSaveRequested(const QString &name)
void finishSaveSession(const QString &name)
void loadSession(const QString &name)
void aboutToSaveSession(const QString &name)
void setState(uint state)
void loadSessionRequested(const QString &name)
SessionState state() const
bool closeWaylandWindows()
void prepareSessionSaveRequested(const QString &name)
void storeSubSession(const QString &name, QSet< QByteArray > sessionIds)
bool isSpecialWindow() const
void setInitialDesktop(int desktop)
static Workspace * self()
RuleBook * rulebook() const
const QList< Window * > windows() const
void forEachClient(std::function< void(X11Window *)> func)
bool isUnmanaged() const override
NET::WindowType windowType() const override
QByteArray sessionId() const
QString windowRole() const override
WaylandServer * waylandServer()
NET::WindowType windowType