File indexing completed on 2024-05-05 17:44:54
0001 /* 0002 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "waylandstartuptasksmodel.h" 0008 #include "libtaskmanager_debug.h" 0009 #include "tasktools.h" 0010 0011 #include <KConfigGroup> 0012 #include <KConfigWatcher> 0013 #include <KWayland/Client/connection_thread.h> 0014 #include <KWayland/Client/plasmawindowmanagement.h> 0015 #include <KWayland/Client/registry.h> 0016 0017 #include <QDebug> 0018 #include <QTimer> 0019 #include <QUrl> 0020 0021 namespace TaskManager 0022 { 0023 class Q_DECL_HIDDEN WaylandStartupTasksModel::Private 0024 { 0025 public: 0026 Private(WaylandStartupTasksModel *q); 0027 0028 void addActivation(KWayland::Client::PlasmaActivation *activation); 0029 void removeActivation(KWayland::Client::PlasmaActivation *activation); 0030 0031 void init(); 0032 void loadConfig(); 0033 0034 struct Startup { 0035 QString name; 0036 QIcon icon; 0037 QString applicationId; 0038 QUrl launcherUrl; 0039 KWayland::Client::PlasmaActivation *activation; 0040 }; 0041 0042 WaylandStartupTasksModel *q; 0043 KConfigWatcher::Ptr configWatcher = nullptr; 0044 KWayland::Client::PlasmaActivationFeedback *feedback = nullptr; 0045 KWayland::Client::Registry *registry = nullptr; 0046 QVector<Startup> startups; 0047 std::chrono::seconds startupTimeout = std::chrono::seconds::zero(); 0048 }; 0049 0050 WaylandStartupTasksModel::Private::Private(WaylandStartupTasksModel *q) 0051 : q(q) 0052 { 0053 } 0054 0055 void WaylandStartupTasksModel::Private::init() 0056 { 0057 configWatcher = KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("klaunchrc"), KConfig::NoGlobals)); 0058 QObject::connect(configWatcher.data(), &KConfigWatcher::configChanged, q, [this] { 0059 loadConfig(); 0060 }); 0061 0062 loadConfig(); 0063 } 0064 0065 void WaylandStartupTasksModel::Private::loadConfig() 0066 { 0067 KConfigGroup feedbackConfig(configWatcher->config(), "FeedbackStyle"); 0068 0069 if (!feedbackConfig.readEntry("TaskbarButton", true)) { 0070 delete feedback; 0071 feedback = nullptr; 0072 delete registry; 0073 registry = nullptr; 0074 0075 q->beginResetModel(); 0076 startups.clear(); 0077 q->endResetModel(); 0078 return; 0079 } 0080 0081 const KConfigGroup taskbarButtonConfig(configWatcher->config(), "TaskbarButtonSettings"); 0082 startupTimeout = std::chrono::seconds(taskbarButtonConfig.readEntry("Timeout", 5)); 0083 0084 if (!registry) { 0085 using namespace KWayland::Client; 0086 0087 ConnectionThread *connection = ConnectionThread::fromApplication(q); 0088 if (!connection) { 0089 return; 0090 } 0091 0092 registry = new Registry(q); 0093 registry->create(connection); 0094 0095 QObject::connect(registry, &Registry::plasmaActivationFeedbackAnnounced, q, [this](quint32 name, quint32 version) { 0096 feedback = registry->createPlasmaActivationFeedback(name, version, q); 0097 0098 QObject::connect(feedback, &PlasmaActivationFeedback::interfaceAboutToBeReleased, q, [this] { 0099 q->beginResetModel(); 0100 startups.clear(); 0101 q->endResetModel(); 0102 }); 0103 0104 QObject::connect(feedback, &PlasmaActivationFeedback::activation, q, [this](PlasmaActivation *activation) { 0105 addActivation(activation); 0106 }); 0107 }); 0108 0109 registry->setup(); 0110 } 0111 } 0112 0113 void WaylandStartupTasksModel::Private::addActivation(KWayland::Client::PlasmaActivation *activation) 0114 { 0115 QObject::connect(activation, &KWayland::Client::PlasmaActivation::applicationId, q, [this, activation](const QString &appId) { 0116 // The application id is guaranteed to be the desktop filename without ".desktop" 0117 const QString desktopFileName = appId + QLatin1String(".desktop"); 0118 const QString desktopFilePath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, desktopFileName); 0119 if (desktopFilePath.isEmpty()) { 0120 qCWarning(TASKMANAGER_DEBUG) << "Got invalid activation app_id:" << appId; 0121 return; 0122 } 0123 0124 const QUrl launcherUrl(QStringLiteral("applications:") + desktopFileName); 0125 const AppData appData = appDataFromUrl(QUrl::fromLocalFile(desktopFilePath)); 0126 0127 const int count = startups.count(); 0128 q->beginInsertRows(QModelIndex(), count, count); 0129 startups.append(Startup{ 0130 .name = appData.name, 0131 .icon = appData.icon, 0132 .applicationId = appId, 0133 .launcherUrl = launcherUrl, 0134 .activation = activation, 0135 }); 0136 q->endInsertRows(); 0137 0138 // Remove the activation if it doesn't finish within certain time interval. 0139 QTimer *timeoutTimer = new QTimer(activation); 0140 QObject::connect(timeoutTimer, &QTimer::timeout, q, [this, activation]() { 0141 removeActivation(activation); 0142 }); 0143 timeoutTimer->setSingleShot(true); 0144 timeoutTimer->start(startupTimeout); 0145 }); 0146 0147 QObject::connect(activation, &KWayland::Client::PlasmaActivation::finished, q, [this, activation]() { 0148 removeActivation(activation); 0149 }); 0150 } 0151 0152 void WaylandStartupTasksModel::Private::removeActivation(KWayland::Client::PlasmaActivation *activation) 0153 { 0154 int position = -1; 0155 for (int i = 0; i < startups.count(); ++i) { 0156 if (startups[i].activation == activation) { 0157 position = i; 0158 break; 0159 } 0160 } 0161 if (position != -1) { 0162 q->beginRemoveRows(QModelIndex(), position, position); 0163 startups.removeAt(position); 0164 q->endRemoveRows(); 0165 } 0166 } 0167 0168 WaylandStartupTasksModel::WaylandStartupTasksModel(QObject *parent) 0169 : AbstractTasksModel(parent) 0170 , d(new Private(this)) 0171 { 0172 d->init(); 0173 } 0174 0175 WaylandStartupTasksModel::~WaylandStartupTasksModel() 0176 { 0177 } 0178 0179 QVariant WaylandStartupTasksModel::data(const QModelIndex &index, int role) const 0180 { 0181 if (!index.isValid() || index.row() >= d->startups.count()) { 0182 return QVariant(); 0183 } 0184 0185 const auto &data = d->startups[index.row()]; 0186 if (role == Qt::DisplayRole) { 0187 return data.name; 0188 } else if (role == Qt::DecorationRole) { 0189 return data.icon; 0190 } else if (role == AppId) { 0191 return data.applicationId; 0192 } else if (role == AppName) { 0193 return data.name; 0194 } else if (role == LauncherUrl || role == LauncherUrlWithoutIcon) { 0195 return data.launcherUrl; 0196 } else if (role == IsStartup) { 0197 return true; 0198 } else if (role == CanLaunchNewInstance) { 0199 return false; 0200 } 0201 0202 return QVariant(); 0203 } 0204 0205 int WaylandStartupTasksModel::rowCount(const QModelIndex &parent) const 0206 { 0207 return parent.isValid() ? 0 : d->startups.count(); 0208 } 0209 0210 } // namespace TaskManager