kwin/src/activities.cpp
Vlad Zahorodnii 8f21d444c6 Make Workspace::allClientList() return all windows
Currently windows are scattered in a few separate lists. If you need to
go through the windows, you have to do it piece by piece. On the other
hand, with the overhaul of window types, we've started converging
towards one universal type: Window. Keeping windows in the separate
buckets goes against this design.

Workspace::stackingOrder() already contains all windows. This change
repurposes Workspace::allClientList() from a list of "normal" windows to
all windows, i.e. Workspace::windows(), to be consistent.

There's one API change though. Scripting API will expose other window
types too. This is an intentional change so scripted effects could
operate with all windows. It also matches the current behavior observed
in libkwineffects, which exposes all windows as well.
2023-03-15 11:52:01 +00:00

232 lines
7 KiB
C++

/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "activities.h"
// KWin
#include "window.h"
#include "workspace.h"
// KDE
#include <KConfigGroup>
// Qt
#include <QDBusInterface>
#include <QDBusPendingCall>
#include <QFutureWatcher>
#include <QtConcurrentRun>
namespace KWin
{
Activities::Activities()
: m_controller(new KActivities::Controller(this))
{
connect(m_controller, &KActivities::Controller::activityRemoved, this, &Activities::slotRemoved);
connect(m_controller, &KActivities::Controller::activityRemoved, this, &Activities::removed);
connect(m_controller, &KActivities::Controller::activityAdded, this, &Activities::added);
connect(m_controller, &KActivities::Controller::currentActivityChanged, this, &Activities::slotCurrentChanged);
connect(m_controller, &KActivities::Controller::serviceStatusChanged, this, &Activities::slotServiceStatusChanged);
}
KActivities::Consumer::ServiceStatus Activities::serviceStatus() const
{
return m_controller->serviceStatus();
}
void Activities::slotServiceStatusChanged()
{
if (m_controller->serviceStatus() != KActivities::Consumer::Running) {
return;
}
const auto windows = Workspace::self()->windows();
for (auto *const window : windows) {
if (!window->isClient()) {
continue;
}
if (window->isDesktop()) {
continue;
}
window->checkActivities();
}
}
void Activities::setCurrent(const QString &activity)
{
m_controller->setCurrentActivity(activity);
}
void Activities::slotCurrentChanged(const QString &newActivity)
{
if (m_current == newActivity) {
return;
}
m_previous = m_current;
m_current = newActivity;
Q_EMIT currentChanged(newActivity);
}
void Activities::slotRemoved(const QString &activity)
{
const auto windows = Workspace::self()->windows();
for (auto *const window : windows) {
if (!window->isClient()) {
continue;
}
if (window->isDesktop()) {
continue;
}
window->setOnActivity(activity, false);
}
// toss out any session data for it
KConfigGroup cg(KSharedConfig::openConfig(), QByteArray("SubSession: ").append(activity.toUtf8()).constData());
cg.deleteGroup();
}
void Activities::toggleWindowOnActivity(Window *window, const QString &activity, bool dont_activate)
{
// int old_desktop = window->desktop();
bool was_on_activity = window->isOnActivity(activity);
bool was_on_all = window->isOnAllActivities();
// note: all activities === no activities
bool enable = was_on_all || !was_on_activity;
window->setOnActivity(activity, enable);
if (window->isOnActivity(activity) == was_on_activity && window->isOnAllActivities() == was_on_all) { // No change
return;
}
Workspace *ws = Workspace::self();
if (window->isOnCurrentActivity()) {
if (window->wantsTabFocus() && options->focusPolicyIsReasonable() && !was_on_activity && // for stickyness changes
// FIXME not sure if the line above refers to the correct activity
!dont_activate) {
ws->requestFocus(window);
} else {
ws->restackWindowUnderActive(window);
}
} else {
ws->raiseWindow(window);
}
// notifyWindowDesktopChanged( c, old_desktop );
const auto transients_stacking_order = ws->ensureStackingOrder(window->transients());
for (auto *const window : transients_stacking_order) {
if (!window) {
continue;
}
toggleWindowOnActivity(window, activity, dont_activate);
}
ws->updateClientArea();
}
bool Activities::start(const QString &id)
{
Workspace *ws = Workspace::self();
if (ws->sessionManager()->state() == SessionState::Saving) {
return false; // ksmserver doesn't queue requests (yet)
}
if (!all().contains(id)) {
return false; // bogus id
}
ws->sessionManager()->loadSubSessionInfo(id);
QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface");
if (ksmserver.isValid()) {
ksmserver.asyncCall("restoreSubSession", id);
} else {
qCDebug(KWIN_CORE) << "couldn't get ksmserver interface";
return false;
}
return true;
}
bool Activities::stop(const QString &id)
{
if (Workspace::self()->sessionManager()->state() == SessionState::Saving) {
return false; // ksmserver doesn't queue requests (yet)
// FIXME what about session *loading*?
}
// ugly hack to avoid dbus deadlocks
QMetaObject::invokeMethod(
this, [this, id] {
reallyStop(id);
},
Qt::QueuedConnection);
// then lie and assume it worked.
return true;
}
void Activities::reallyStop(const QString &id)
{
Workspace *ws = Workspace::self();
if (ws->sessionManager()->state() == SessionState::Saving) {
return; // ksmserver doesn't queue requests (yet)
}
qCDebug(KWIN_CORE) << id;
QSet<QByteArray> saveSessionIds;
QSet<QByteArray> dontCloseSessionIds;
const auto windows = ws->windows();
for (auto *const window : windows) {
if (!window->isClient()) {
continue;
}
if (window->isDesktop()) {
continue;
}
const QByteArray sessionId = window->sessionId();
if (sessionId.isEmpty()) {
continue; // TODO support old wm_command apps too?
}
// qDebug() << sessionId;
// if it's on the activity that's closing, it needs saving
// but if a process is on some other open activity, I don't wanna close it yet
// this is, of course, complicated by a process having many windows.
if (window->isOnAllActivities()) {
dontCloseSessionIds << sessionId;
continue;
}
const QStringList activities = window->activities();
for (const QString &activityId : activities) {
if (activityId == id) {
saveSessionIds << sessionId;
} else if (running().contains(activityId)) {
dontCloseSessionIds << sessionId;
}
}
}
ws->sessionManager()->storeSubSession(id, saveSessionIds);
QStringList saveAndClose;
QStringList saveOnly;
for (const QByteArray &sessionId : std::as_const(saveSessionIds)) {
if (dontCloseSessionIds.contains(sessionId)) {
saveOnly << sessionId;
} else {
saveAndClose << sessionId;
}
}
qCDebug(KWIN_CORE) << "saveActivity" << id << saveAndClose << saveOnly;
// pass off to ksmserver
QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface");
if (ksmserver.isValid()) {
ksmserver.asyncCall("saveSubSession", id, saveAndClose, saveOnly);
} else {
qCDebug(KWIN_CORE) << "couldn't get ksmserver interface";
}
}
} // namespace