File indexing completed on 2024-04-28 05:30:12

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "activities.h"
0010 // KWin
0011 #include "workspace.h"
0012 #include "x11window.h"
0013 // KDE
0014 #include <KConfigGroup>
0015 // Qt
0016 #include <QDBusInterface>
0017 #include <QDBusPendingCall>
0018 #include <QFutureWatcher>
0019 #include <QtConcurrentRun>
0020 
0021 namespace KWin
0022 {
0023 
0024 Activities::Activities()
0025     : m_controller(new KActivities::Controller(this))
0026 {
0027     connect(m_controller, &KActivities::Controller::activityRemoved, this, &Activities::slotRemoved);
0028     connect(m_controller, &KActivities::Controller::activityRemoved, this, &Activities::removed);
0029     connect(m_controller, &KActivities::Controller::activityAdded, this, &Activities::added);
0030     connect(m_controller, &KActivities::Controller::currentActivityChanged, this, &Activities::slotCurrentChanged);
0031     connect(m_controller, &KActivities::Controller::serviceStatusChanged, this, &Activities::slotServiceStatusChanged);
0032 }
0033 
0034 KActivities::Consumer::ServiceStatus Activities::serviceStatus() const
0035 {
0036     return m_controller->serviceStatus();
0037 }
0038 
0039 void Activities::slotServiceStatusChanged()
0040 {
0041     if (m_controller->serviceStatus() != KActivities::Consumer::Running) {
0042         return;
0043     }
0044     const auto windows = Workspace::self()->windows();
0045     for (auto *const window : windows) {
0046         if (!window->isClient()) {
0047             continue;
0048         }
0049         if (window->isDesktop()) {
0050             continue;
0051         }
0052         window->checkActivities();
0053     }
0054 }
0055 
0056 void Activities::setCurrent(const QString &activity)
0057 {
0058     m_controller->setCurrentActivity(activity);
0059 }
0060 
0061 void Activities::slotCurrentChanged(const QString &newActivity)
0062 {
0063     if (m_current == newActivity) {
0064         return;
0065     }
0066     m_previous = m_current;
0067     m_current = newActivity;
0068     Q_EMIT currentChanged(newActivity);
0069 }
0070 
0071 void Activities::slotRemoved(const QString &activity)
0072 {
0073     const auto windows = Workspace::self()->windows();
0074     for (auto *const window : windows) {
0075         if (!window->isClient()) {
0076             continue;
0077         }
0078         if (window->isDesktop()) {
0079             continue;
0080         }
0081         window->setOnActivity(activity, false);
0082     }
0083     // toss out any session data for it
0084     KConfigGroup cg(KSharedConfig::openConfig(), QLatin1String("SubSession: ") + activity);
0085     cg.deleteGroup();
0086 }
0087 
0088 void Activities::toggleWindowOnActivity(Window *window, const QString &activity, bool dont_activate)
0089 {
0090     // int old_desktop = window->desktop();
0091     bool was_on_activity = window->isOnActivity(activity);
0092     bool was_on_all = window->isOnAllActivities();
0093     // note: all activities === no activities
0094     bool enable = was_on_all || !was_on_activity;
0095     window->setOnActivity(activity, enable);
0096     if (window->isOnActivity(activity) == was_on_activity && window->isOnAllActivities() == was_on_all) { // No change
0097         return;
0098     }
0099 
0100     Workspace *ws = Workspace::self();
0101     if (window->isOnCurrentActivity()) {
0102         if (window->wantsTabFocus() && options->focusPolicyIsReasonable() && !was_on_activity && // for stickyness changes
0103                                                                                                  // FIXME not sure if the line above refers to the correct activity
0104             !dont_activate) {
0105             ws->requestFocus(window);
0106         } else {
0107             ws->restackWindowUnderActive(window);
0108         }
0109     } else {
0110         ws->raiseWindow(window);
0111     }
0112 
0113     // notifyWindowDesktopChanged( c, old_desktop );
0114 
0115     const auto transients_stacking_order = ws->ensureStackingOrder(window->transients());
0116     for (auto *const window : transients_stacking_order) {
0117         if (!window) {
0118             continue;
0119         }
0120         toggleWindowOnActivity(window, activity, dont_activate);
0121     }
0122     ws->updateClientArea();
0123 }
0124 
0125 bool Activities::start(const QString &id)
0126 {
0127     Workspace *ws = Workspace::self();
0128     if (ws->sessionManager()->state() == SessionState::Saving) {
0129         return false; // ksmserver doesn't queue requests (yet)
0130     }
0131 
0132     if (!all().contains(id)) {
0133         return false; // bogus id
0134     }
0135 
0136     ws->sessionManager()->loadSubSessionInfo(id);
0137 
0138     QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface");
0139     if (ksmserver.isValid()) {
0140         ksmserver.asyncCall("restoreSubSession", id);
0141     } else {
0142         qCDebug(KWIN_CORE) << "couldn't get ksmserver interface";
0143         return false;
0144     }
0145     return true;
0146 }
0147 
0148 bool Activities::stop(const QString &id)
0149 {
0150     if (Workspace::self()->sessionManager()->state() == SessionState::Saving) {
0151         return false; // ksmserver doesn't queue requests (yet)
0152         // FIXME what about session *loading*?
0153     }
0154 
0155     // ugly hack to avoid dbus deadlocks
0156     QMetaObject::invokeMethod(
0157         this, [this, id] {
0158             reallyStop(id);
0159         },
0160         Qt::QueuedConnection);
0161     // then lie and assume it worked.
0162     return true;
0163 }
0164 
0165 void Activities::reallyStop(const QString &id)
0166 {
0167     Workspace *ws = Workspace::self();
0168     if (ws->sessionManager()->state() == SessionState::Saving) {
0169         return; // ksmserver doesn't queue requests (yet)
0170     }
0171 
0172     qCDebug(KWIN_CORE) << id;
0173 
0174     QSet<QByteArray> saveSessionIds;
0175     QSet<QByteArray> dontCloseSessionIds;
0176     const auto windows = ws->windows();
0177     for (auto *const window : windows) {
0178         auto x11Window = qobject_cast<X11Window *>(window);
0179         if (!x11Window || window->isUnmanaged()) {
0180             continue;
0181         }
0182         if (window->isDesktop()) {
0183             continue;
0184         }
0185         const QByteArray sessionId = x11Window->sessionId();
0186         if (sessionId.isEmpty()) {
0187             continue; // TODO support old wm_command apps too?
0188         }
0189 
0190         // qDebug() << sessionId;
0191 
0192         // if it's on the activity that's closing, it needs saving
0193         // but if a process is on some other open activity, I don't wanna close it yet
0194         // this is, of course, complicated by a process having many windows.
0195         if (window->isOnAllActivities()) {
0196             dontCloseSessionIds << sessionId;
0197             continue;
0198         }
0199 
0200         const QStringList activities = window->activities();
0201         for (const QString &activityId : activities) {
0202             if (activityId == id) {
0203                 saveSessionIds << sessionId;
0204             } else if (running().contains(activityId)) {
0205                 dontCloseSessionIds << sessionId;
0206             }
0207         }
0208     }
0209 
0210     ws->sessionManager()->storeSubSession(id, saveSessionIds);
0211 
0212     QStringList saveAndClose;
0213     QStringList saveOnly;
0214     for (const QByteArray &sessionId : std::as_const(saveSessionIds)) {
0215         if (dontCloseSessionIds.contains(sessionId)) {
0216             saveOnly << sessionId;
0217         } else {
0218             saveAndClose << sessionId;
0219         }
0220     }
0221 
0222     qCDebug(KWIN_CORE) << "saveActivity" << id << saveAndClose << saveOnly;
0223 
0224     // pass off to ksmserver
0225     QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface");
0226     if (ksmserver.isValid()) {
0227         ksmserver.asyncCall("saveSubSession", id, saveAndClose, saveOnly);
0228     } else {
0229         qCDebug(KWIN_CORE) << "couldn't get ksmserver interface";
0230     }
0231 }
0232 
0233 } // namespace
0234 
0235 #include "moc_activities.cpp"