File indexing completed on 2024-11-10 04:57:41
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"