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