[Task Manager] Add support for StartupTasksModel on Wayland

BUG: 402903
wilder-5.24
Vlad Zahorodnii 5 years ago
parent 7059ef56ab
commit 73c378832b
  1. 2
      libtaskmanager/CMakeLists.txt
  2. 5
      libtaskmanager/startuptasksmodel.cpp
  3. 207
      libtaskmanager/waylandstartuptasksmodel.cpp
  4. 29
      libtaskmanager/waylandstartuptasksmodel.h
  5. 2
      shell/org.kde.plasmashell.desktop.cmake

@ -17,6 +17,7 @@ set(taskmanager_LIB_SRCS
tasksmodel.cpp
tasktools.cpp
virtualdesktopinfo.cpp
waylandstartuptasksmodel.cpp
waylandtasksmodel.cpp
windowtasksmodel.cpp
)
@ -84,6 +85,7 @@ install(FILES
tasksmodel.h
tasktools.h
virtualdesktopinfo.h
waylandstartuptasksmodel.h
waylandtasksmodel.h
windowtasksmodel.h
${CMAKE_CURRENT_BINARY_DIR}/taskmanager_export.h

@ -9,6 +9,7 @@
#include <config-X11.h>
#include "waylandstartuptasksmodel.h"
#if HAVE_X11
#include "xstartuptasksmodel.h"
#endif
@ -53,6 +54,10 @@ StartupTasksModel::Private::~Private()
void StartupTasksModel::Private::initSourceTasksModel()
{
if (!sourceTasksModel && KWindowSystem::isPlatformWayland()) {
sourceTasksModel = new WaylandStartupTasksModel();
}
#if HAVE_X11
if (!sourceTasksModel && KWindowSystem::isPlatformX11()) {
sourceTasksModel = new XStartupTasksModel();

@ -0,0 +1,207 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "waylandstartuptasksmodel.h"
#include "tasktools.h"
#include <KConfigGroup>
#include <KConfigWatcher>
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/plasmawindowmanagement.h>
#include <KWayland/Client/registry.h>
#include <QDebug>
#include <QTimer>
#include <QUrl>
namespace TaskManager
{
class Q_DECL_HIDDEN WaylandStartupTasksModel::Private
{
public:
Private(WaylandStartupTasksModel *q);
void addActivation(KWayland::Client::PlasmaActivation *activation);
void removeActivation(KWayland::Client::PlasmaActivation *activation);
void init();
void loadConfig();
struct Startup {
QString name;
QIcon icon;
QString applicationId;
QUrl launcherUrl;
KWayland::Client::PlasmaActivation *activation;
};
WaylandStartupTasksModel *q;
KConfigWatcher::Ptr configWatcher = nullptr;
KWayland::Client::PlasmaActivationFeedback *feedback = nullptr;
KWayland::Client::Registry *registry = nullptr;
QVector<Startup> startups;
std::chrono::seconds startupTimeout = std::chrono::seconds::zero();
};
WaylandStartupTasksModel::Private::Private(WaylandStartupTasksModel *q)
: q(q)
{
}
void WaylandStartupTasksModel::Private::init()
{
configWatcher = KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("klaunchrc")));
QObject::connect(configWatcher.data(), &KConfigWatcher::configChanged, q, [this]() {
loadConfig();
});
loadConfig();
}
void WaylandStartupTasksModel::Private::loadConfig()
{
KConfigGroup feedbackConfig(configWatcher->config(), "FeedbackStyle");
if (!feedbackConfig.readEntry("TaskbarButton", true)) {
delete feedback;
feedback = nullptr;
delete registry;
registry = nullptr;
q->beginResetModel();
startups.clear();
q->endResetModel();
return;
}
const KConfigGroup taskbarButtonConfig(configWatcher->config(), "TaskbarButtonSettings");
startupTimeout = std::chrono::seconds(taskbarButtonConfig.readEntry("Timeout", 5));
if (!registry) {
using namespace KWayland::Client;
ConnectionThread *connection = ConnectionThread::fromApplication(q);
if (!connection) {
return;
}
registry = new Registry(q);
registry->create(connection);
QObject::connect(registry, &Registry::plasmaActivationFeedbackAnnounced, q, [this](quint32 name, quint32 version) {
feedback = registry->createPlasmaActivationFeedback(name, version, q);
QObject::connect(feedback, &PlasmaActivationFeedback::interfaceAboutToBeReleased, q, [this] {
q->beginResetModel();
startups.clear();
q->endResetModel();
});
QObject::connect(feedback, &PlasmaActivationFeedback::activation, q, [this](PlasmaActivation *activation) {
addActivation(activation);
});
});
registry->setup();
}
}
void WaylandStartupTasksModel::Private::addActivation(KWayland::Client::PlasmaActivation *activation)
{
QObject::connect(activation, &KWayland::Client::PlasmaActivation::applicationId, q, [this, activation](const QString &appId) {
// The application id is guaranteed to be the desktop filename without ".desktop"
const QString desktopFileName = appId + QLatin1String(".desktop");
const QString desktopFilePath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, desktopFileName);
if (desktopFilePath.isEmpty()) {
qWarning() << "Got invalid activation app_id:" << appId;
return;
}
const QUrl launcherUrl(QStringLiteral("applications:") + desktopFileName);
const AppData appData = appDataFromUrl(QUrl::fromLocalFile(desktopFilePath));
const int count = startups.count();
q->beginInsertRows(QModelIndex(), count, count);
startups.append(Startup{
.name = appData.name,
.icon = appData.icon,
.applicationId = appId,
.launcherUrl = launcherUrl,
.activation = activation,
});
q->endInsertRows();
// Remove the activation if it doesn't finish within certain time interval.
QTimer *timeoutTimer = new QTimer(activation);
QObject::connect(timeoutTimer, &QTimer::timeout, q, [this, activation]() {
removeActivation(activation);
});
timeoutTimer->setSingleShot(true);
timeoutTimer->start(startupTimeout);
});
QObject::connect(activation, &KWayland::Client::PlasmaActivation::finished, q, [this, activation]() {
removeActivation(activation);
});
}
void WaylandStartupTasksModel::Private::removeActivation(KWayland::Client::PlasmaActivation *activation)
{
int position = -1;
for (int i = 0; i < startups.count(); ++i) {
if (startups[i].activation == activation) {
position = i;
break;
}
}
if (position != -1) {
q->beginRemoveRows(QModelIndex(), position, position);
startups.removeAt(position);
q->endRemoveRows();
}
}
WaylandStartupTasksModel::WaylandStartupTasksModel(QObject *parent)
: AbstractTasksModel(parent)
, d(new Private(this))
{
d->init();
}
WaylandStartupTasksModel::~WaylandStartupTasksModel()
{
}
QVariant WaylandStartupTasksModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() >= d->startups.count()) {
return QVariant();
}
const auto &data = d->startups[index.row()];
if (role == Qt::DisplayRole) {
return data.name;
} else if (role == Qt::DecorationRole) {
return data.icon;
} else if (role == AppId) {
return data.applicationId;
} else if (role == AppName) {
return data.name;
} else if (role == LauncherUrl || role == LauncherUrlWithoutIcon) {
return data.launcherUrl;
} else if (role == IsStartup) {
return true;
}
return QVariant();
}
int WaylandStartupTasksModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : d->startups.count();
}
} // namespace TaskManager

@ -0,0 +1,29 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "abstracttasksmodel.h"
namespace TaskManager
{
class TASKMANAGER_EXPORT WaylandStartupTasksModel : public AbstractTasksModel
{
Q_OBJECT
public:
explicit WaylandStartupTasksModel(QObject *parent = nullptr);
~WaylandStartupTasksModel() override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
private:
class Private;
QScopedPointer<Private> d;
};
} // namespace TaskManager

@ -64,5 +64,5 @@ Icon=plasmashell
NoDisplay=true
X-systemd-skip=true
X-KDE-Wayland-Interfaces=org_kde_plasma_window_management,org_kde_kwin_keystate,zkde_screencast_unstable_v1
X-KDE-Wayland-Interfaces=org_kde_plasma_window_management,org_kde_kwin_keystate,zkde_screencast_unstable_v1,org_kde_plasma_activation_feedback
X-KDE-DBUS-Restricted-Interfaces=org.kde.kwin.Screenshot,org.kde.KWin.ScreenShot2

Loading…
Cancel
Save