Expose a method to allow closing windows on shutdown.
This allows Plasma to gracefully close windows on shutdown by sending xdg_toplevel.close. If after 10 seconds windows are still open because they prompt for unsaved changes or similar cases, a notification is shown to either prompt or logout regardless. CCBUG: 461176
This commit is contained in:
parent
40aa5aceb8
commit
4cdf27e74c
3 changed files with 94 additions and 2 deletions
|
@ -22,6 +22,9 @@
|
||||||
<!-- Shutdown kwin at the end of the session -->
|
<!-- Shutdown kwin at the end of the session -->
|
||||||
<method name="quit">
|
<method name="quit">
|
||||||
</method>
|
</method>
|
||||||
|
<method name="closeWaylandWindows">
|
||||||
|
<arg type="b" direction="out" />
|
||||||
|
</method>
|
||||||
</interface>
|
</interface>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
|
|
84
src/sm.cpp
84
src/sm.cpp
|
@ -16,13 +16,22 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "virtualdesktops.h"
|
#include "virtualdesktops.h"
|
||||||
|
#include "wayland_server.h"
|
||||||
#include "workspace.h"
|
#include "workspace.h"
|
||||||
#include "x11window.h"
|
#include "x11window.h"
|
||||||
|
#include "xdgshellwindow.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include <QSessionManager>
|
#include <QSessionManager>
|
||||||
|
#if KWIN_BUILD_NOTIFICATIONS
|
||||||
|
#include <KLocalizedString>
|
||||||
|
#include <KNotification>
|
||||||
|
#include <KService>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "sessionadaptor.h"
|
#include "sessionadaptor.h"
|
||||||
#include <QDBusConnection>
|
|
||||||
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
@ -385,6 +394,7 @@ void SessionManager::setState(SessionState state)
|
||||||
client->setSessionActivityOverride(false);
|
client->setSessionActivityOverride(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sessionState = state;
|
m_sessionState = state;
|
||||||
Q_EMIT stateChanged();
|
Q_EMIT stateChanged();
|
||||||
}
|
}
|
||||||
|
@ -401,6 +411,78 @@ void SessionManager::finishSaveSession(const QString &name)
|
||||||
storeSession(name, SMSavePhase2);
|
storeSession(name, SMSavePhase2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SessionManager::closeWaylandWindows()
|
||||||
|
{
|
||||||
|
Q_ASSERT(calledFromDBus());
|
||||||
|
if (!waylandServer()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_closingWindowsGuard) {
|
||||||
|
sendErrorReply(QDBusError::Failed, u"Operation already in progress"_s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_closingWindowsGuard = std::make_unique<QObject>();
|
||||||
|
qCDebug(KWIN_CORE) << "Closing windows";
|
||||||
|
|
||||||
|
auto dbusMessage = message();
|
||||||
|
setDelayedReply(true);
|
||||||
|
|
||||||
|
const auto windows = workspace()->windows();
|
||||||
|
m_pendingWindows.clear();
|
||||||
|
m_pendingWindows.reserve(windows.size());
|
||||||
|
for (const auto window : windows) {
|
||||||
|
if (auto toplevelWindow = qobject_cast<XdgToplevelWindow *>(window)) {
|
||||||
|
connect(toplevelWindow, &XdgToplevelWindow::closed, m_closingWindowsGuard.get(), [this, toplevelWindow, dbusMessage] {
|
||||||
|
m_pendingWindows.removeOne(toplevelWindow);
|
||||||
|
if (m_pendingWindows.empty()) {
|
||||||
|
m_closeTimer.stop();
|
||||||
|
m_closingWindowsGuard.reset();
|
||||||
|
QDBusConnection::sessionBus().send(dbusMessage.createReply(true));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_pendingWindows.push_back(toplevelWindow);
|
||||||
|
toplevelWindow->closeWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_closeTimer.start(std::chrono::seconds(10));
|
||||||
|
m_closeTimer.setSingleShot(true);
|
||||||
|
connect(&m_closeTimer, &QTimer::timeout, m_closingWindowsGuard.get(), [this, dbusMessage] {
|
||||||
|
#if KWIN_BUILD_NOTIFICATIONS
|
||||||
|
QStringList apps;
|
||||||
|
apps.reserve(m_pendingWindows.size());
|
||||||
|
std::transform(m_pendingWindows.cbegin(), m_pendingWindows.cend(), std::back_inserter(apps), [](const XdgToplevelWindow *window) -> QString {
|
||||||
|
const auto service = KService::serviceByDesktopName(window->desktopFileName());
|
||||||
|
return QChar(u'•') + (service ? service->name() : window->caption());
|
||||||
|
});
|
||||||
|
apps.removeDuplicates();
|
||||||
|
qCDebug(KWIN_CORE) << "Not closed windows" << apps;
|
||||||
|
auto notification = new KNotification("cancellogout", KNotification::DefaultEvent | KNotification::Persistent);
|
||||||
|
notification->setText(i18n("The following applications did not close:\n%1", apps.join('\n')));
|
||||||
|
auto cancel = notification->addAction(i18nc("@action:button", "Cancel Logout"));
|
||||||
|
auto quit = notification->addAction(i18nc("@action::button", "Log Out Anyway"));
|
||||||
|
connect(cancel, &KNotificationAction::activated, m_closingWindowsGuard.get(), [dbusMessage, this] {
|
||||||
|
m_closingWindowsGuard.reset();
|
||||||
|
QDBusConnection::sessionBus().send(dbusMessage.createReply(false));
|
||||||
|
});
|
||||||
|
connect(quit, &KNotificationAction::activated, m_closingWindowsGuard.get(), [dbusMessage, this] {
|
||||||
|
m_closingWindowsGuard.reset();
|
||||||
|
QDBusConnection::sessionBus().send(dbusMessage.createReply(true));
|
||||||
|
});
|
||||||
|
connect(notification, &KNotification::closed, m_closingWindowsGuard.get(), [dbusMessage, this] {
|
||||||
|
m_closingWindowsGuard.reset();
|
||||||
|
QDBusConnection::sessionBus().send(dbusMessage.createReply(false));
|
||||||
|
});
|
||||||
|
notification->sendEvent();
|
||||||
|
#else
|
||||||
|
m_closingWindowsGuard.reset();
|
||||||
|
QDBusConnection::sessionBus().send(dbusMessage.createReply(false));
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void SessionManager::quit()
|
void SessionManager::quit()
|
||||||
{
|
{
|
||||||
qApp->quit();
|
qApp->quit();
|
||||||
|
|
9
src/sm.h
9
src/sm.h
|
@ -10,9 +10,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDBusContext>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QRect>
|
#include <QRect>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <KConfigGroup>
|
#include <KConfigGroup>
|
||||||
|
|
||||||
|
@ -24,8 +26,9 @@ namespace KWin
|
||||||
|
|
||||||
class X11Window;
|
class X11Window;
|
||||||
struct SessionInfo;
|
struct SessionInfo;
|
||||||
|
class XdgToplevelWindow;
|
||||||
|
|
||||||
class SessionManager : public QObject
|
class SessionManager : public QObject, public QDBusContext
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
@ -57,6 +60,7 @@ public Q_SLOTS: // DBus API
|
||||||
void loadSession(const QString &name);
|
void loadSession(const QString &name);
|
||||||
void aboutToSaveSession(const QString &name);
|
void aboutToSaveSession(const QString &name);
|
||||||
void finishSaveSession(const QString &name);
|
void finishSaveSession(const QString &name);
|
||||||
|
bool closeWaylandWindows();
|
||||||
void quit();
|
void quit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -73,6 +77,9 @@ private:
|
||||||
int m_sessionDesktop;
|
int m_sessionDesktop;
|
||||||
|
|
||||||
QList<SessionInfo *> session;
|
QList<SessionInfo *> session;
|
||||||
|
QList<XdgToplevelWindow *> m_pendingWindows;
|
||||||
|
QTimer m_closeTimer;
|
||||||
|
std::unique_ptr<QObject> m_closingWindowsGuard;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SessionInfo
|
struct SessionInfo
|
||||||
|
|
Loading…
Reference in a new issue