File indexing completed on 2025-02-16 11:23:01
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 "window.h" 0012 #include "workspace.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()->allClientList(); 0045 for (auto *const window : windows) { 0046 if (window->isDesktop()) { 0047 continue; 0048 } 0049 window->checkActivities(); 0050 } 0051 } 0052 0053 void Activities::setCurrent(const QString &activity) 0054 { 0055 m_controller->setCurrentActivity(activity); 0056 } 0057 0058 void Activities::slotCurrentChanged(const QString &newActivity) 0059 { 0060 if (m_current == newActivity) { 0061 return; 0062 } 0063 m_previous = m_current; 0064 m_current = newActivity; 0065 Q_EMIT currentChanged(newActivity); 0066 } 0067 0068 void Activities::slotRemoved(const QString &activity) 0069 { 0070 const auto windows = Workspace::self()->allClientList(); 0071 for (auto *const window : windows) { 0072 if (window->isDesktop()) { 0073 continue; 0074 } 0075 window->setOnActivity(activity, false); 0076 } 0077 // toss out any session data for it 0078 KConfigGroup cg(KSharedConfig::openConfig(), QByteArray("SubSession: ").append(activity.toUtf8()).constData()); 0079 cg.deleteGroup(); 0080 } 0081 0082 void Activities::toggleWindowOnActivity(Window *window, const QString &activity, bool dont_activate) 0083 { 0084 // int old_desktop = window->desktop(); 0085 bool was_on_activity = window->isOnActivity(activity); 0086 bool was_on_all = window->isOnAllActivities(); 0087 // note: all activities === no activities 0088 bool enable = was_on_all || !was_on_activity; 0089 window->setOnActivity(activity, enable); 0090 if (window->isOnActivity(activity) == was_on_activity && window->isOnAllActivities() == was_on_all) { // No change 0091 return; 0092 } 0093 0094 Workspace *ws = Workspace::self(); 0095 if (window->isOnCurrentActivity()) { 0096 if (window->wantsTabFocus() && options->focusPolicyIsReasonable() && !was_on_activity && // for stickyness changes 0097 // FIXME not sure if the line above refers to the correct activity 0098 !dont_activate) { 0099 ws->requestFocus(window); 0100 } else { 0101 ws->restackWindowUnderActive(window); 0102 } 0103 } else { 0104 ws->raiseWindow(window); 0105 } 0106 0107 // notifyWindowDesktopChanged( c, old_desktop ); 0108 0109 const auto transients_stacking_order = ws->ensureStackingOrder(window->transients()); 0110 for (auto *const window : transients_stacking_order) { 0111 if (!window) { 0112 continue; 0113 } 0114 toggleWindowOnActivity(window, activity, dont_activate); 0115 } 0116 ws->updateClientArea(); 0117 } 0118 0119 bool Activities::start(const QString &id) 0120 { 0121 Workspace *ws = Workspace::self(); 0122 if (ws->sessionManager()->state() == SessionState::Saving) { 0123 return false; // ksmserver doesn't queue requests (yet) 0124 } 0125 0126 if (!all().contains(id)) { 0127 return false; // bogus id 0128 } 0129 0130 ws->sessionManager()->loadSubSessionInfo(id); 0131 0132 QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface"); 0133 if (ksmserver.isValid()) { 0134 ksmserver.asyncCall("restoreSubSession", id); 0135 } else { 0136 qCDebug(KWIN_CORE) << "couldn't get ksmserver interface"; 0137 return false; 0138 } 0139 return true; 0140 } 0141 0142 bool Activities::stop(const QString &id) 0143 { 0144 if (Workspace::self()->sessionManager()->state() == SessionState::Saving) { 0145 return false; // ksmserver doesn't queue requests (yet) 0146 // FIXME what about session *loading*? 0147 } 0148 0149 // ugly hack to avoid dbus deadlocks 0150 QMetaObject::invokeMethod( 0151 this, [this, id] { 0152 reallyStop(id); 0153 }, 0154 Qt::QueuedConnection); 0155 // then lie and assume it worked. 0156 return true; 0157 } 0158 0159 void Activities::reallyStop(const QString &id) 0160 { 0161 Workspace *ws = Workspace::self(); 0162 if (ws->sessionManager()->state() == SessionState::Saving) { 0163 return; // ksmserver doesn't queue requests (yet) 0164 } 0165 0166 qCDebug(KWIN_CORE) << id; 0167 0168 QSet<QByteArray> saveSessionIds; 0169 QSet<QByteArray> dontCloseSessionIds; 0170 const auto windows = ws->allClientList(); 0171 for (auto *const window : windows) { 0172 if (window->isDesktop()) { 0173 continue; 0174 } 0175 const QByteArray sessionId = window->sessionId(); 0176 if (sessionId.isEmpty()) { 0177 continue; // TODO support old wm_command apps too? 0178 } 0179 0180 // qDebug() << sessionId; 0181 0182 // if it's on the activity that's closing, it needs saving 0183 // but if a process is on some other open activity, I don't wanna close it yet 0184 // this is, of course, complicated by a process having many windows. 0185 if (window->isOnAllActivities()) { 0186 dontCloseSessionIds << sessionId; 0187 continue; 0188 } 0189 0190 const QStringList activities = window->activities(); 0191 for (const QString &activityId : activities) { 0192 if (activityId == id) { 0193 saveSessionIds << sessionId; 0194 } else if (running().contains(activityId)) { 0195 dontCloseSessionIds << sessionId; 0196 } 0197 } 0198 } 0199 0200 ws->sessionManager()->storeSubSession(id, saveSessionIds); 0201 0202 QStringList saveAndClose; 0203 QStringList saveOnly; 0204 for (const QByteArray &sessionId : std::as_const(saveSessionIds)) { 0205 if (dontCloseSessionIds.contains(sessionId)) { 0206 saveOnly << sessionId; 0207 } else { 0208 saveAndClose << sessionId; 0209 } 0210 } 0211 0212 qCDebug(KWIN_CORE) << "saveActivity" << id << saveAndClose << saveOnly; 0213 0214 // pass off to ksmserver 0215 QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface"); 0216 if (ksmserver.isValid()) { 0217 ksmserver.asyncCall("saveSubSession", id, saveAndClose, saveOnly); 0218 } else { 0219 qCDebug(KWIN_CORE) << "couldn't get ksmserver interface"; 0220 } 0221 } 0222 0223 } // namespace