Plasma-workspace

waylandtasksmodel.cpp
1/*
2 SPDX-FileCopyrightText: 2016 Eike Hein <hein@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "waylandtasksmodel.h"
8#include "libtaskmanager_debug.h"
9#include "tasktools.h"
10#include "virtualdesktopinfo.h"
11
12#include <KDirWatch>
13#include <KSharedConfig>
14#include <KWindowSystem>
15
16#include <qwayland-plasma-window-management.h>
17
18#include <QDateTime>
19#include <QFuture>
20#include <QFutureWatcher>
21#include <QGuiApplication>
22#include <QMimeData>
23#include <QQuickItem>
24#include <QQuickWindow>
25#include <QSet>
26#include <QUrl>
27#include <QUuid>
28#include <QWaylandClientExtension>
29#include <QWindow>
30#include <QtConcurrentRun>
31#include <qpa/qplatformwindow_p.h>
32
33#include <fcntl.h>
34#include <sys/poll.h>
35#include <unistd.h>
36
37namespace TaskManager
38{
39
40class PlasmaWindow : public QObject, public QtWayland::org_kde_plasma_window
41{
43public:
44 PlasmaWindow(const QString &uuid, ::org_kde_plasma_window *id)
45 : org_kde_plasma_window(id)
46 , uuid(uuid)
47 {
48 }
49 ~PlasmaWindow()
50 {
51 destroy();
52 }
53 using state = QtWayland::org_kde_plasma_window_management::state;
54 const QString uuid;
55 QString title;
56 QString appId;
57 QIcon icon;
58 QFlags<state> windowState;
59 QList<QString> virtualDesktops;
60 QRect geometry;
61 QString applicationMenuService;
62 QString applicationMenuObjectPath;
63 QList<QString> activities;
64 quint32 pid;
65 QString resourceName;
66 QPointer<PlasmaWindow> parentWindow;
67 bool wasUnmapped = false;
68
70 void unmapped();
71 void titleChanged();
72 void appIdChanged();
73 void iconChanged();
74 void activeChanged();
75 void minimizedChanged();
76 void maximizedChanged();
77 void fullscreenChanged();
78 void keepAboveChanged();
79 void keepBelowChanged();
80 void onAllDesktopsChanged();
81 void demandsAttentionChanged();
82 void closeableChanged();
83 void minimizeableChanged();
84 void maximizeableChanged();
85 void fullscreenableChanged();
86 void skiptaskbarChanged();
87 void shadeableChanged();
88 void shadedChanged();
89 void canSetNoBorderChanged();
90 void hasNoBorderChanged();
91 void movableChanged();
92 void resizableChanged();
93 void virtualDesktopChangeableChanged();
94 void skipSwitcherChanged();
95 void virtualDesktopEntered();
96 void virtualDesktopLeft();
97 void geometryChanged();
98 void skipTaskbarChanged();
99 void applicationMenuChanged();
100 void activitiesChanged();
101 void parentWindowChanged();
102 void initialStateDone();
103
104protected:
105 void org_kde_plasma_window_unmapped() override
106 {
107 wasUnmapped = true;
108 Q_EMIT unmapped();
109 }
110 void org_kde_plasma_window_title_changed(const QString &title) override
111 {
112 this->title = title;
113 Q_EMIT titleChanged();
114 }
115 void org_kde_plasma_window_app_id_changed(const QString &app_id) override
116 {
117 appId = app_id;
118 Q_EMIT appIdChanged();
119 }
120 void org_kde_plasma_window_icon_changed() override
121 {
122 int pipeFds[2];
123 if (pipe2(pipeFds, O_CLOEXEC) != 0) {
124 qCWarning(TASKMANAGER_DEBUG) << "failed creating pipe";
125 return;
126 }
127 get_icon(pipeFds[1]);
128 ::close(pipeFds[1]);
129 auto readIcon = [uuid = uuid](int fd) {
130 auto closeGuard = qScopeGuard([fd]() {
131 ::close(fd);
132 });
133 pollfd pollFd;
134 pollFd.fd = fd;
135 pollFd.events = POLLIN;
136 QByteArray data;
137 while (true) {
138 int ready = poll(&pollFd, 1, 1000);
139 if (ready < 0 && errno != EINTR) {
140 qCWarning(TASKMANAGER_DEBUG) << "polling for icon of window" << uuid << "failed";
141 return QIcon();
142 } else if (ready == 0) {
143 qCWarning(TASKMANAGER_DEBUG) << "time out polling for icon of window" << uuid;
144 return QIcon();
145 } else {
146 char buffer[4096];
147 int n = read(fd, buffer, sizeof(buffer));
148 if (n < 0) {
149 qCWarning(TASKMANAGER_DEBUG) << "error reading icon of window" << uuid;
150 return QIcon();
151 } else if (n > 0) {
152 data.append(buffer, n);
153 } else {
154 QIcon icon;
155 QDataStream ds(data);
156 ds >> icon;
157 return icon;
158 }
159 }
160 }
161 };
162 QFuture<QIcon> future = QtConcurrent::run(readIcon, pipeFds[0]);
163 auto watcher = new QFutureWatcher<QIcon>();
164 connect(watcher, &QFutureWatcher<QIcon>::finished, this, [this, watcher] {
165 icon = watcher->future().result();
166 Q_EMIT iconChanged();
167 });
169 watcher->setFuture(future);
170 }
171 void org_kde_plasma_window_themed_icon_name_changed(const QString &name) override
172 {
173 icon = QIcon::fromTheme(name);
174 Q_EMIT iconChanged();
175 }
176 void org_kde_plasma_window_state_changed(uint32_t flags) override
177 {
178 auto diff = windowState ^ flags;
179 if (diff & state::state_active) {
180 windowState.setFlag(state::state_active, flags & state::state_active);
181 Q_EMIT activeChanged();
182 }
183 if (diff & state::state_minimized) {
184 windowState.setFlag(state::state_minimized, flags & state::state_minimized);
185 Q_EMIT minimizedChanged();
186 }
187 if (diff & state::state_maximized) {
188 windowState.setFlag(state::state_maximized, flags & state::state_maximized);
189 Q_EMIT maximizedChanged();
190 }
191 if (diff & state::state_fullscreen) {
192 windowState.setFlag(state::state_fullscreen, flags & state::state_fullscreen);
193 Q_EMIT fullscreenChanged();
194 }
195 if (diff & state::state_keep_above) {
196 windowState.setFlag(state::state_keep_above, flags & state::state_keep_above);
197 Q_EMIT keepAboveChanged();
198 }
199 if (diff & state::state_keep_below) {
200 windowState.setFlag(state::state_keep_below, flags & state::state_keep_below);
201 Q_EMIT keepBelowChanged();
202 }
203 if (diff & state::state_on_all_desktops) {
204 windowState.setFlag(state::state_on_all_desktops, flags & state::state_on_all_desktops);
205 Q_EMIT onAllDesktopsChanged();
206 }
207 if (diff & state::state_demands_attention) {
208 windowState.setFlag(state::state_demands_attention, flags & state::state_demands_attention);
209 Q_EMIT demandsAttentionChanged();
210 }
211 if (diff & state::state_closeable) {
212 windowState.setFlag(state::state_closeable, flags & state::state_closeable);
213 Q_EMIT closeableChanged();
214 }
215 if (diff & state::state_minimizable) {
216 windowState.setFlag(state::state_minimizable, flags & state::state_minimizable);
217 Q_EMIT minimizeableChanged();
218 }
219 if (diff & state::state_maximizable) {
220 windowState.setFlag(state::state_maximizable, flags & state::state_maximizable);
221 Q_EMIT maximizeableChanged();
222 }
223 if (diff & state::state_fullscreenable) {
224 windowState.setFlag(state::state_fullscreenable, flags & state::state_fullscreenable);
225 Q_EMIT fullscreenableChanged();
226 }
227 if (diff & state::state_skiptaskbar) {
228 windowState.setFlag(state::state_skiptaskbar, flags & state::state_skiptaskbar);
229 Q_EMIT skipTaskbarChanged();
230 }
231 if (diff & state::state_shadeable) {
232 windowState.setFlag(state::state_shadeable, flags & state::state_shadeable);
233 Q_EMIT shadeableChanged();
234 }
235 if (diff & state::state_shaded) {
236 windowState.setFlag(state::state_shaded, flags & state::state_shaded);
237 Q_EMIT shadedChanged();
238 }
239 if (diff & state::state_movable) {
240 windowState.setFlag(state::state_movable, flags & state::state_movable);
241 Q_EMIT movableChanged();
242 }
243 if (diff & state::state_resizable) {
244 windowState.setFlag(state::state_resizable, flags & state::state_resizable);
245 Q_EMIT resizableChanged();
246 }
247 if (diff & state::state_virtual_desktop_changeable) {
248 windowState.setFlag(state::state_virtual_desktop_changeable, flags & state::state_virtual_desktop_changeable);
249 Q_EMIT virtualDesktopChangeableChanged();
250 }
251 if (diff & state::state_skipswitcher) {
252 windowState.setFlag(state::state_skipswitcher, flags & state::state_skipswitcher);
253 Q_EMIT skipSwitcherChanged();
254 }
255 if (diff & state::state_can_set_no_border) {
256 windowState.setFlag(state::state_can_set_no_border, flags & state::state_can_set_no_border);
257 Q_EMIT canSetNoBorderChanged();
258 }
259 if (diff & state::state_no_border) {
260 windowState.setFlag(state::state_no_border, flags & state::state_no_border);
261 Q_EMIT hasNoBorderChanged();
262 }
263 }
264 void org_kde_plasma_window_virtual_desktop_entered(const QString &id) override
265 {
266 virtualDesktops.push_back(id);
267 Q_EMIT virtualDesktopEntered();
268 }
269
270 void org_kde_plasma_window_virtual_desktop_left(const QString &id) override
271 {
272 virtualDesktops.removeAll(id);
273 Q_EMIT virtualDesktopLeft();
274 }
275 void org_kde_plasma_window_geometry(int32_t x, int32_t y, uint32_t width, uint32_t height) override
276 {
277 geometry = QRect(x, y, width, height);
278 Q_EMIT geometryChanged();
279 }
280 void org_kde_plasma_window_application_menu(const QString &service_name, const QString &object_path) override
281
282 {
283 applicationMenuService = service_name;
284 applicationMenuObjectPath = object_path;
285 Q_EMIT applicationMenuChanged();
286 }
287 void org_kde_plasma_window_activity_entered(const QString &id) override
288 {
289 activities.push_back(id);
290 Q_EMIT activitiesChanged();
291 }
292 void org_kde_plasma_window_activity_left(const QString &id) override
293 {
294 activities.removeAll(id);
295 Q_EMIT activitiesChanged();
296 }
297 void org_kde_plasma_window_pid_changed(uint32_t pid) override
298 {
299 this->pid = pid;
300 }
301 void org_kde_plasma_window_resource_name_changed(const QString &resource_name) override
302 {
303 resourceName = resource_name;
304 }
305 void org_kde_plasma_window_parent_window(::org_kde_plasma_window *parent) override
306 {
307 PlasmaWindow *parentWindow = nullptr;
308 if (parent) {
309 parentWindow = dynamic_cast<PlasmaWindow *>(PlasmaWindow::fromObject(parent));
310 }
311 setParentWindow(parentWindow);
312 }
313 void org_kde_plasma_window_initial_state() override
314 {
315 Q_EMIT initialStateDone();
316 }
317
318private:
319 void setParentWindow(PlasmaWindow *parent)
320 {
321 const auto old = parentWindow;
322 QObject::disconnect(parentWindowUnmappedConnection);
323 QObject::disconnect(parentWindowDestroyedConnection);
324
325 if (parent && !parent->wasUnmapped) {
326 parentWindow = QPointer<PlasmaWindow>(parent);
327 parentWindowUnmappedConnection = QObject::connect(parent, &PlasmaWindow::unmapped, this, [this] {
328 setParentWindow(nullptr);
329 });
330 // QPointer nulling itself wouldn't cause the change signal to be emitted.
331 parentWindowDestroyedConnection = QObject::connect(parent, &QObject::destroyed, this, &PlasmaWindow::parentWindowChanged);
332 } else {
333 parentWindow = QPointer<PlasmaWindow>();
334 parentWindowUnmappedConnection = QMetaObject::Connection();
335 parentWindowDestroyedConnection = QMetaObject::Connection();
336 }
337
338 if (parentWindow.data() != old.data()) {
339 Q_EMIT parentWindowChanged();
340 }
341 }
342
343 QMetaObject::Connection parentWindowUnmappedConnection;
344 QMetaObject::Connection parentWindowDestroyedConnection;
345};
346
347class PlasmaStackingOrder;
348
349class PlasmaWindowManagement : public QWaylandClientExtensionTemplate<PlasmaWindowManagement>, public QtWayland::org_kde_plasma_window_management
350{
351 Q_OBJECT
352public:
353 static constexpr int s_version = 17;
354 PlasmaWindowManagement()
355 : QWaylandClientExtensionTemplate(s_version)
356 {
357 connect(this, &QWaylandClientExtension::activeChanged, this, [this] {
358 if (!isActive()) {
359 wl_proxy_destroy(reinterpret_cast<wl_proxy *>(object()));
360 } else if (QtWayland::org_kde_plasma_window_management::version() >= ORG_KDE_PLASMA_WINDOW_MANAGEMENT_GET_STACKING_ORDER_SINCE_VERSION) {
361 // fetch the stacking order
362 org_kde_plasma_window_management_stacking_order_changed_2();
363 }
364 });
365 initialize();
366 if (!isActive()) {
367 qCWarning(TASKMANAGER_DEBUG)
368 << "The PlasmaWindowManagement protocol hasn't activated in time. The client possibly got denied by kwin? Check kwin output.";
369 }
370 }
371 ~PlasmaWindowManagement()
372 {
373 if (isActive()) {
374 wl_proxy_destroy(reinterpret_cast<wl_proxy *>(object()));
375 }
376 }
377 void org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) override
378 {
379 Q_UNUSED(id)
380 Q_EMIT windowCreated(new PlasmaWindow(uuid, get_window_by_uuid(uuid)));
381 }
382 void org_kde_plasma_window_management_stacking_order_uuid_changed(const QString &uuids) override
383 {
384 Q_EMIT stackingOrderChanged(uuids.split(QLatin1Char(';')));
385 }
386 void org_kde_plasma_window_management_stacking_order_changed_2() override;
387 void stackingOrderDone(const QList<QString> &uuids)
388 {
389 Q_EMIT stackingOrderChanged(uuids);
390 m_pendingStackingOrder.reset();
391 }
392Q_SIGNALS:
393 void windowCreated(PlasmaWindow *window);
394 void stackingOrderChanged(const QList<QString> &uuids);
395
396private:
397 std::unique_ptr<PlasmaStackingOrder> m_pendingStackingOrder;
398};
399
400class PlasmaStackingOrder : public QtWayland::org_kde_plasma_stacking_order
401{
402public:
403 explicit PlasmaStackingOrder(PlasmaWindowManagement *windowManagement, ::org_kde_plasma_stacking_order *id)
404 : QtWayland::org_kde_plasma_stacking_order(id)
405 , m_windowManagement(windowManagement)
406 {
407 }
408 ~PlasmaStackingOrder()
409 {
410 org_kde_plasma_stacking_order_destroy(object());
411 }
412
413 void org_kde_plasma_stacking_order_window(const QString &uuid) override
414 {
415 m_uuids.push_back(uuid);
416 }
417
418 void org_kde_plasma_stacking_order_done() override
419 {
420 m_windowManagement->stackingOrderDone(m_uuids);
421 }
422
423 PlasmaWindowManagement *const m_windowManagement;
424 QList<QString> m_uuids;
425};
426
427void PlasmaWindowManagement::org_kde_plasma_window_management_stacking_order_changed_2()
428{
429 m_pendingStackingOrder.reset(new PlasmaStackingOrder(this, org_kde_plasma_window_management_get_stacking_order(object())));
430}
431
432class Q_DECL_HIDDEN WaylandTasksModel::Private
433{
434public:
435 Private(WaylandTasksModel *q);
436 QHash<PlasmaWindow *, AppData> appDataCache;
437 QHash<PlasmaWindow *, QDateTime> lastActivated;
438 PlasmaWindow *activeWindow = nullptr;
439 std::vector<std::unique_ptr<PlasmaWindow>> windows;
440 // key=transient child, value=leader
441 QHash<PlasmaWindow *, PlasmaWindow *> transients;
442 // key=leader, values=transient children
443 QMultiHash<PlasmaWindow *, PlasmaWindow *> transientsDemandingAttention;
444 std::unique_ptr<PlasmaWindowManagement> windowManagement;
445 KSharedConfig::Ptr rulesConfig;
446 KDirWatch *configWatcher = nullptr;
447 VirtualDesktopInfo *virtualDesktopInfo = nullptr;
448 static QUuid uuid;
449 QList<QString> stackingOrder;
450
451 void init();
452 void initWayland();
453 auto findWindow(PlasmaWindow *window) const;
454 void addWindow(PlasmaWindow *window);
455
456 const AppData &appData(PlasmaWindow *window);
457
458 QIcon icon(PlasmaWindow *window);
459
460 static QString mimeType();
461 static QString groupMimeType();
462
463 void dataChanged(PlasmaWindow *window, int role);
464 void dataChanged(PlasmaWindow *window, const QList<int> &roles);
465
466private:
467 WaylandTasksModel *const q;
468};
469
470QUuid WaylandTasksModel::Private::uuid = QUuid::createUuid();
471
472WaylandTasksModel::Private::Private(WaylandTasksModel *q)
473 : q(q)
474{
475}
476
477void WaylandTasksModel::Private::init()
478{
479 auto clearCacheAndRefresh = [this] {
480 if (windows.empty()) {
481 return;
482 }
483
484 appDataCache.clear();
485
486 // Emit changes of all roles satisfied from app data cache.
487 Q_EMIT q->dataChanged(q->index(0, 0),
488 q->index(windows.size() - 1, 0),
489 QList<int>{Qt::DecorationRole,
490 AbstractTasksModel::AppId,
491 AbstractTasksModel::AppName,
492 AbstractTasksModel::GenericName,
493 AbstractTasksModel::LauncherUrl,
494 AbstractTasksModel::LauncherUrlWithoutIcon,
495 AbstractTasksModel::CanLaunchNewInstance,
496 AbstractTasksModel::SkipTaskbar});
497 };
498
499 rulesConfig = KSharedConfig::openConfig(QStringLiteral("taskmanagerrulesrc"));
500 configWatcher = new KDirWatch(q);
501
503 configWatcher->addFile(location + QLatin1String("/taskmanagerrulesrc"));
504 }
505
506 auto rulesConfigChange = [this, clearCacheAndRefresh] {
507 rulesConfig->reparseConfiguration();
508 clearCacheAndRefresh();
509 };
510
511 QObject::connect(configWatcher, &KDirWatch::dirty, rulesConfigChange);
512 QObject::connect(configWatcher, &KDirWatch::created, rulesConfigChange);
513 QObject::connect(configWatcher, &KDirWatch::deleted, rulesConfigChange);
514
515 virtualDesktopInfo = new VirtualDesktopInfo(q);
516
517 initWayland();
518}
519
520void WaylandTasksModel::Private::initWayland()
521{
523 return;
524 }
525
526 windowManagement = std::make_unique<PlasmaWindowManagement>();
527
528 QObject::connect(windowManagement.get(), &PlasmaWindowManagement::activeChanged, q, [this] {
529 q->beginResetModel();
530 windows.clear();
531 q->endResetModel();
532 });
533
534 QObject::connect(windowManagement.get(), &PlasmaWindowManagement::windowCreated, q, [this](PlasmaWindow *window) {
535 connect(window, &PlasmaWindow::initialStateDone, q, [this, window] {
536 addWindow(window);
537 });
538 });
539
540 QObject::connect(windowManagement.get(), &PlasmaWindowManagement::stackingOrderChanged, q, [this](const QList<QString> &order) {
541 stackingOrder = order;
542 for (const auto &window : std::as_const(windows)) {
543 this->dataChanged(window.get(), StackingOrder);
544 }
545 });
546}
547
548auto WaylandTasksModel::Private::findWindow(PlasmaWindow *window) const
549{
550 return std::find_if(windows.begin(), windows.end(), [window](const std::unique_ptr<PlasmaWindow> &candidate) {
551 return candidate.get() == window;
552 });
553}
554
555void WaylandTasksModel::Private::addWindow(PlasmaWindow *window)
556{
557 if (findWindow(window) != windows.end()) {
558 return;
559 }
560
561 auto removeWindow = [window, this] {
562 // findWindow() is const, we need a non-const iterator for the "take" below.
563 auto it = std::find_if(windows.begin(), windows.end(), [window](const std::unique_ptr<PlasmaWindow> &candidate) {
564 return candidate.get() == window;
565 });
566
567 if (it != windows.end()) {
568 const int row = it - windows.begin();
569 q->beginRemoveRows(QModelIndex(), row, row);
570
571 // "take"... don't use just erase() here as it will destroy the unique_ptr and thus
572 // the window, which will trigger QObject::destroyed handlers which might
573 // add/remove items from the model whilst we're removing rows ourselves.
574 const std::unique_ptr<PlasmaWindow> removedWindow = std::move(*it);
575 windows.erase(it);
576
577 transientsDemandingAttention.remove(window);
578 appDataCache.remove(window);
579 lastActivated.remove(window);
580 q->endRemoveRows();
581 // Removing a transient might change the demands attention state of the leader.
582 if (transients.remove(window)) {
583 if (PlasmaWindow *leader = transientsDemandingAttention.key(window)) {
584 transientsDemandingAttention.remove(leader, window);
585 dataChanged(leader, QList<int>{IsDemandingAttention});
586 }
587 }
588 }
589
590 if (activeWindow == window) {
591 activeWindow = nullptr;
592 }
593 };
594
595 QObject::connect(window, &PlasmaWindow::unmapped, q, removeWindow);
596
597 QObject::connect(window, &PlasmaWindow::titleChanged, q, [window, this] {
598 this->dataChanged(window, Qt::DisplayRole);
599 });
600
601 QObject::connect(window, &PlasmaWindow::iconChanged, q, [window, this] {
602 // The icon in the AppData struct might come from PlasmaWindow if it wasn't
603 // filled in by windowUrlFromMetadata+appDataFromUrl.
604 // TODO: Don't evict the cache unnecessarily if this isn't the case. As icons
605 // are currently very static on Wayland, this eviction is unlikely to happen
606 // frequently as of now.
607 appDataCache.remove(window);
608 this->dataChanged(window, Qt::DecorationRole);
609 });
610
611 QObject::connect(window, &PlasmaWindow::appIdChanged, q, [window, this] {
612 // The AppData struct in the cache is derived from this and needs
613 // to be evicted in favor of a fresh struct based on the changed
614 // window metadata.
615 appDataCache.remove(window);
616
617 // Refresh roles satisfied from the app data cache.
618 this->dataChanged(window,
619 QList<int>{Qt::DecorationRole, AppId, AppName, GenericName, LauncherUrl, LauncherUrlWithoutIcon, SkipTaskbar, CanLaunchNewInstance});
620 });
621
622 if (window->windowState & PlasmaWindow::state::state_active) {
623 PlasmaWindow *effectiveActive = window;
624 while (effectiveActive->parentWindow) {
625 effectiveActive = effectiveActive->parentWindow;
626 }
627
628 lastActivated[effectiveActive] = QDateTime::currentDateTime();
629 activeWindow = effectiveActive;
630 }
631
632 QObject::connect(window, &PlasmaWindow::activeChanged, q, [window, this] {
633 const bool active = window->windowState & PlasmaWindow::state::state_active;
634
635 PlasmaWindow *effectiveWindow = window;
636
637 while (effectiveWindow->parentWindow) {
638 effectiveWindow = effectiveWindow->parentWindow;
639 }
640
641 if (active) {
642 lastActivated[effectiveWindow] = QDateTime::currentDateTime();
643
644 if (activeWindow != effectiveWindow) {
645 activeWindow = effectiveWindow;
646 this->dataChanged(effectiveWindow, IsActive);
647 }
648 } else {
649 if (activeWindow == effectiveWindow) {
650 activeWindow = nullptr;
651 this->dataChanged(effectiveWindow, IsActive);
652 }
653 }
654 });
655
656 QObject::connect(window, &PlasmaWindow::parentWindowChanged, q, [window, this] {
657 PlasmaWindow *leader = window->parentWindow.data();
658
659 // Migrate demanding attention to new leader.
660 if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) {
661 if (auto *oldLeader = transientsDemandingAttention.key(window)) {
662 if (window->parentWindow != oldLeader) {
663 transientsDemandingAttention.remove(oldLeader, window);
664 transientsDemandingAttention.insert(leader, window);
665 dataChanged(oldLeader, QList<int>{IsDemandingAttention});
666 dataChanged(leader, QList<int>{IsDemandingAttention});
667 }
668 }
669 }
670
671 if (transients.remove(window)) {
672 if (leader) { // leader change.
673 transients.insert(window, leader);
674 } else { // lost a leader, add to regular windows list.
675 dataChanged(window, SkipTaskbar);
676 }
677 } else if (leader) { // gained a leader, remove from regular windows list.
678 transients.insert(window, leader);
679 dataChanged(window, SkipTaskbar);
680 }
681 });
682
683 QObject::connect(window, &PlasmaWindow::closeableChanged, q, [window, this] {
684 this->dataChanged(window, IsClosable);
685 });
686
687 QObject::connect(window, &PlasmaWindow::movableChanged, q, [window, this] {
688 this->dataChanged(window, IsMovable);
689 });
690
691 QObject::connect(window, &PlasmaWindow::resizableChanged, q, [window, this] {
692 this->dataChanged(window, IsResizable);
693 });
694
695 QObject::connect(window, &PlasmaWindow::fullscreenableChanged, q, [window, this] {
696 this->dataChanged(window, IsFullScreenable);
697 });
698
699 QObject::connect(window, &PlasmaWindow::fullscreenChanged, q, [window, this] {
700 this->dataChanged(window, IsFullScreen);
701 });
702
703 QObject::connect(window, &PlasmaWindow::maximizeableChanged, q, [window, this] {
704 this->dataChanged(window, IsMaximizable);
705 });
706
707 QObject::connect(window, &PlasmaWindow::maximizedChanged, q, [window, this] {
708 this->dataChanged(window, IsMaximized);
709 });
710
711 QObject::connect(window, &PlasmaWindow::minimizeableChanged, q, [window, this] {
712 this->dataChanged(window, IsMinimizable);
713 });
714
715 QObject::connect(window, &PlasmaWindow::minimizedChanged, q, [window, this] {
716 this->dataChanged(window, IsMinimized);
717 });
718
719 QObject::connect(window, &PlasmaWindow::keepAboveChanged, q, [window, this] {
720 this->dataChanged(window, IsKeepAbove);
721 });
722
723 QObject::connect(window, &PlasmaWindow::keepBelowChanged, q, [window, this] {
724 this->dataChanged(window, IsKeepBelow);
725 });
726
727 QObject::connect(window, &PlasmaWindow::shadeableChanged, q, [window, this] {
728 this->dataChanged(window, IsShadeable);
729 });
730
731 QObject::connect(window, &PlasmaWindow::canSetNoBorderChanged, q, [window, this] {
732 this->dataChanged(window, CanSetNoBorder);
733 });
734
735 QObject::connect(window, &PlasmaWindow::hasNoBorderChanged, q, [window, this] {
736 this->dataChanged(window, HasNoBorder);
737 });
738
739 QObject::connect(window, &PlasmaWindow::virtualDesktopChangeableChanged, q, [window, this] {
740 this->dataChanged(window, IsVirtualDesktopsChangeable);
741 });
742
743 QObject::connect(window, &PlasmaWindow::virtualDesktopEntered, q, [window, this] {
744 this->dataChanged(window, VirtualDesktops);
745
746 // If the count has changed from 0, the window may no longer be on all virtual
747 // desktops.
748 if (!window->virtualDesktops.isEmpty()) {
749 this->dataChanged(window, IsOnAllVirtualDesktops);
750 }
751 });
752
753 QObject::connect(window, &PlasmaWindow::virtualDesktopLeft, q, [window, this] {
754 this->dataChanged(window, VirtualDesktops);
755
756 // If the count has changed to 0, the window is now on all virtual desktops.
757 if (window->virtualDesktops.isEmpty()) {
758 this->dataChanged(window, IsOnAllVirtualDesktops);
759 }
760 });
761
762 QObject::connect(window, &PlasmaWindow::geometryChanged, q, [window, this] {
763 this->dataChanged(window, QList<int>{Geometry, ScreenGeometry});
764 });
765
766 QObject::connect(window, &PlasmaWindow::demandsAttentionChanged, q, [window, this] {
767 // Changes to a transient's state might change demands attention state for leader.
768 if (auto *leader = transients.value(window)) {
769 if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) {
770 if (!transientsDemandingAttention.values(leader).contains(window)) {
771 transientsDemandingAttention.insert(leader, window);
772 this->dataChanged(leader, QList<int>{IsDemandingAttention});
773 }
774 } else if (transientsDemandingAttention.remove(leader, window)) {
775 this->dataChanged(leader, QList<int>{IsDemandingAttention});
776 }
777 } else {
778 this->dataChanged(window, QList<int>{IsDemandingAttention});
779 }
780 });
781
782 QObject::connect(window, &PlasmaWindow::skipTaskbarChanged, q, [window, this] {
783 this->dataChanged(window, SkipTaskbar);
784 });
785
786 QObject::connect(window, &PlasmaWindow::applicationMenuChanged, q, [window, this] {
787 this->dataChanged(window, QList<int>{ApplicationMenuServiceName, ApplicationMenuObjectPath});
788 });
789
790 QObject::connect(window, &PlasmaWindow::activitiesChanged, q, [window, this] {
791 this->dataChanged(window, Activities);
792 });
793
794 // Handle transient.
795 if (PlasmaWindow *leader = window->parentWindow.data()) {
796 transients.insert(window, leader);
797
798 // Update demands attention state for leader.
799 if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) {
800 transientsDemandingAttention.insert(leader, window);
801 dataChanged(leader, QList<int>{IsDemandingAttention});
802 }
803 }
804
805 const int count = windows.size();
806
807 q->beginInsertRows(QModelIndex(), count, count);
808
809 windows.emplace_back(window);
810
811 q->endInsertRows();
812}
813
814const AppData &WaylandTasksModel::Private::appData(PlasmaWindow *window)
815{
816 static_assert(!std::is_trivially_copy_assignable_v<AppData>);
817 if (auto it = appDataCache.constFind(window); it != appDataCache.constEnd()) {
818 return *it;
819 }
820
821 return *appDataCache.emplace(window, appDataFromUrl(windowUrlFromMetadata(window->appId, window->pid, rulesConfig, window->resourceName)));
822}
823
824QIcon WaylandTasksModel::Private::icon(PlasmaWindow *window)
825{
826 const AppData &app = appData(window);
827
828 if (!app.icon.isNull()) {
829 return app.icon;
830 }
831
832 appDataCache[window].icon = window->icon;
833
834 return window->icon;
835}
836
837QString WaylandTasksModel::Private::mimeType()
838{
839 // Use a unique format id to make this intentionally useless for
840 // cross-process DND.
841 return u"windowsystem/winid+" + uuid.toString();
842}
843
844QString WaylandTasksModel::Private::groupMimeType()
845{
846 // Use a unique format id to make this intentionally useless for
847 // cross-process DND.
848 return u"windowsystem/multiple-winids+" + uuid.toString();
849}
850
851void WaylandTasksModel::Private::dataChanged(PlasmaWindow *window, int role)
852{
853 auto it = findWindow(window);
854 if (it == windows.end()) {
855 return;
856 }
857 QModelIndex idx = q->index(it - windows.begin());
858 Q_EMIT q->dataChanged(idx, idx, QList<int>{role});
859}
860
861void WaylandTasksModel::Private::dataChanged(PlasmaWindow *window, const QList<int> &roles)
862{
863 auto it = findWindow(window);
864 if (it == windows.end()) {
865 return;
866 }
867 QModelIndex idx = q->index(it - windows.begin());
868 Q_EMIT q->dataChanged(idx, idx, roles);
869}
870
871WaylandTasksModel::WaylandTasksModel(QObject *parent)
872 : AbstractWindowTasksModel(parent)
873 , d(new Private(this))
874{
875 d->init();
876}
877
878WaylandTasksModel::~WaylandTasksModel()
879{
880 for (auto &window : d->windows) {
881 QObject::disconnect(window.get(), &PlasmaWindow::parentWindowChanged, this, nullptr);
882 }
883}
884
885QVariant WaylandTasksModel::data(const QModelIndex &index, int role) const
886{
887 // Note: when index is valid, its row >= 0, so casting to unsigned is safe
888 if (!index.isValid() || static_cast<size_t>(index.row()) >= d->windows.size()) {
889 return QVariant();
890 }
891
892 PlasmaWindow *window = d->windows.at(index.row()).get();
893
894 if (role == Qt::DisplayRole) {
895 return window->title;
896 } else if (role == Qt::DecorationRole) {
897 return d->icon(window);
898 } else if (role == AppId) {
899 const QString &id = d->appData(window).id;
900
901 if (id.isEmpty()) {
902 return window->appId;
903 } else {
904 return id;
905 }
906 } else if (role == AppName) {
907 return d->appData(window).name;
908 } else if (role == GenericName) {
909 return d->appData(window).genericName;
910 } else if (role == LauncherUrl || role == LauncherUrlWithoutIcon) {
911 return d->appData(window).url;
912 } else if (role == WinIdList) {
913 return QVariantList{window->uuid};
914 } else if (role == MimeType) {
915 return d->mimeType();
916 } else if (role == MimeData) {
917 return window->uuid;
918 } else if (role == IsWindow) {
919 return true;
920 } else if (role == IsActive) {
921 return (window == d->activeWindow);
922 } else if (role == IsClosable) {
923 return window->windowState.testFlag(PlasmaWindow::state::state_closeable);
924 } else if (role == IsMovable) {
925 return window->windowState.testFlag(PlasmaWindow::state::state_movable);
926 } else if (role == IsResizable) {
927 return window->windowState.testFlag(PlasmaWindow::state::state_resizable);
928 } else if (role == IsMaximizable) {
929 return window->windowState.testFlag(PlasmaWindow::state::state_maximizable);
930 } else if (role == IsMaximized) {
931 return window->windowState.testFlag(PlasmaWindow::state::state_maximized);
932 } else if (role == IsMinimizable) {
933 return window->windowState.testFlag(PlasmaWindow::state::state_minimizable);
934 } else if (role == IsMinimized || role == IsHidden) {
935 return window->windowState.testFlag(PlasmaWindow::state::state_minimized);
936 } else if (role == IsKeepAbove) {
937 return window->windowState.testFlag(PlasmaWindow::state::state_keep_above);
938 } else if (role == IsKeepBelow) {
939 return window->windowState.testFlag(PlasmaWindow::state::state_keep_below);
940 } else if (role == IsFullScreenable) {
941 return window->windowState.testFlag(PlasmaWindow::state::state_fullscreenable);
942 } else if (role == IsFullScreen) {
943 return window->windowState.testFlag(PlasmaWindow::state::state_fullscreen);
944 } else if (role == IsShadeable) {
945 return window->windowState.testFlag(PlasmaWindow::state::state_shadeable);
946 } else if (role == IsShaded) {
947 return window->windowState.testFlag(PlasmaWindow::state::state_shaded);
948 } else if (role == CanSetNoBorder) {
949 return window->windowState.testFlag(PlasmaWindow::state::state_can_set_no_border);
950 } else if (role == HasNoBorder) {
951 return window->windowState.testFlag(PlasmaWindow::state::state_no_border);
952 } else if (role == IsVirtualDesktopsChangeable) {
953 return window->windowState.testFlag(PlasmaWindow::state::state_virtual_desktop_changeable);
954 } else if (role == VirtualDesktops) {
955 return window->virtualDesktops;
956 } else if (role == IsOnAllVirtualDesktops) {
957 return window->virtualDesktops.isEmpty();
958 } else if (role == Geometry) {
959 return window->geometry;
960 } else if (role == ScreenGeometry) {
961 return screenGeometry(window->geometry.center());
962 } else if (role == Activities) {
963 return window->activities;
964 } else if (role == IsDemandingAttention) {
965 return window->windowState.testFlag(PlasmaWindow::state::state_demands_attention) || d->transientsDemandingAttention.contains(window);
966 } else if (role == SkipTaskbar) {
967 return window->windowState.testFlag(PlasmaWindow::state::state_skiptaskbar) || d->appData(window).skipTaskbar || d->transients.contains(window);
968 } else if (role == SkipPager) {
969 // FIXME Implement.
970 } else if (role == AppPid) {
971 return window->pid;
972 } else if (role == StackingOrder) {
973 return d->stackingOrder.indexOf(window->uuid);
974 } else if (role == LastActivated) {
975 if (d->lastActivated.contains(window)) {
976 return d->lastActivated.value(window);
977 }
978 } else if (role == ApplicationMenuObjectPath) {
979 return window->applicationMenuObjectPath;
980 } else if (role == ApplicationMenuServiceName) {
981 return window->applicationMenuService;
982 } else if (role == CanLaunchNewInstance) {
983 return canLauchNewInstance(d->appData(window));
984 }
985
986 return AbstractTasksModel::data(index, role);
987}
988
989int WaylandTasksModel::rowCount(const QModelIndex &parent) const
990{
991 return parent.isValid() ? 0 : d->windows.size();
992}
993
994QModelIndex WaylandTasksModel::index(int row, int column, const QModelIndex &parent) const
995{
996 return hasIndex(row, column, parent) ? createIndex(row, column, d->windows.at(row).get()) : QModelIndex();
997}
998
1000{
1001 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1002 return;
1003 }
1004
1005 PlasmaWindow *window = d->windows.at(index.row()).get();
1006
1007 // Pull forward any transient demanding attention.
1008 if (auto *transientDemandingAttention = d->transientsDemandingAttention.value(window)) {
1009 window = transientDemandingAttention;
1010 } else {
1011 // TODO Shouldn't KWin take care of that?
1012 // Bringing a transient to the front usually brings its parent with it
1013 // but focus is not handled properly.
1014 // TODO take into account d->lastActivation instead
1015 // of just taking the first one.
1016 while (d->transients.key(window)) {
1017 window = d->transients.key(window);
1018 }
1019 }
1020
1021 window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active);
1022}
1023
1025{
1026 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1027 return;
1028 }
1029
1030 runApp(d->appData(d->windows.at(index.row()).get()));
1031}
1032
1034{
1035 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent) || urls.isEmpty()) {
1036 return;
1037 }
1038
1039 runApp(d->appData(d->windows.at(index.row()).get()), urls);
1040}
1041
1043{
1044 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1045 return;
1046 }
1047
1048 d->windows.at(index.row())->close();
1049}
1050
1052{
1053 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1054 return;
1055 }
1056
1057 auto &window = d->windows.at(index.row());
1058
1059 window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active);
1060 window->request_move();
1061}
1062
1064{
1065 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1066 return;
1067 }
1068
1069 auto &window = d->windows.at(index.row());
1070
1071 window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active);
1072 window->request_resize();
1073}
1074
1076{
1077 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1078 return;
1079 }
1080
1081 auto &window = d->windows.at(index.row());
1082
1083 if (window->windowState & PlasmaWindow::state::state_minimized) {
1084 window->set_state(PlasmaWindow::state::state_minimized, 0);
1085 } else {
1086 window->set_state(PlasmaWindow::state::state_minimized, PlasmaWindow::state::state_minimized);
1087 }
1088}
1089
1091{
1092 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1093 return;
1094 }
1095
1096 auto &window = d->windows.at(index.row());
1097
1098 if (window->windowState & PlasmaWindow::state::state_maximized) {
1099 window->set_state(PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active, PlasmaWindow::state::state_active);
1100 } else {
1101 window->set_state(PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active,
1102 PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active);
1103 }
1104}
1105
1107{
1108 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1109 return;
1110 }
1111
1112 auto &window = d->windows.at(index.row());
1113
1114 if (window->windowState & PlasmaWindow::state::state_keep_above) {
1115 window->set_state(PlasmaWindow::state::state_keep_above, 0);
1116 } else {
1117 window->set_state(PlasmaWindow::state::state_keep_above, PlasmaWindow::state::state_keep_above);
1118 }
1119}
1120
1122{
1123 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1124 return;
1125 }
1126 auto &window = d->windows.at(index.row());
1127
1128 if (window->windowState & PlasmaWindow::state::state_keep_below) {
1129 window->set_state(PlasmaWindow::state::state_keep_below, 0);
1130 } else {
1131 window->set_state(PlasmaWindow::state::state_keep_below, PlasmaWindow::state::state_keep_below);
1132 }
1133}
1134
1136{
1137 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1138 return;
1139 }
1140
1141 auto &window = d->windows.at(index.row());
1142
1143 if (window->windowState & PlasmaWindow::state::state_fullscreen) {
1144 window->set_state(PlasmaWindow::state::state_fullscreen, 0);
1145 } else {
1146 window->set_state(PlasmaWindow::state::state_fullscreen, PlasmaWindow::state::state_fullscreen);
1147 }
1148}
1149
1151{
1152 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1153 return;
1154 }
1155
1156 auto &window = d->windows.at(index.row());
1157
1158 if (window->windowState & PlasmaWindow::state::state_shaded) {
1159 window->set_state(PlasmaWindow::state::state_shaded, 0);
1160 } else {
1161 window->set_state(PlasmaWindow::state::state_shaded, PlasmaWindow::state::state_shaded);
1162 };
1163}
1164
1166{
1167 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1168 return;
1169 }
1170
1171 auto &window = d->windows.at(index.row());
1172
1173 if (window->windowState & PlasmaWindow::state::state_no_border) {
1174 window->set_state(PlasmaWindow::state::state_no_border, 0);
1175 } else {
1176 window->set_state(PlasmaWindow::state::state_no_border, PlasmaWindow::state::state_no_border);
1177 };
1178}
1179
1180void WaylandTasksModel::requestVirtualDesktops(const QModelIndex &index, const QVariantList &desktops)
1181{
1182 // FIXME TODO: Lacks the "if we've requested the current desktop, force-activate
1183 // the window" logic from X11 version. This behavior should be in KWin rather than
1184 // libtm however.
1185
1186 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1187 return;
1188 }
1189
1190 auto &window = d->windows.at(index.row());
1191
1192 if (desktops.isEmpty()) {
1193 const QStringList virtualDesktops = window->virtualDesktops;
1194 for (const QString &desktop : virtualDesktops) {
1195 window->request_leave_virtual_desktop(desktop);
1196 }
1197 } else {
1198 const QStringList &now = window->virtualDesktops;
1199 QStringList next;
1200
1201 for (const QVariant &desktop : desktops) {
1202 const QString &desktopId = desktop.toString();
1203
1204 if (!desktopId.isEmpty()) {
1205 next << desktopId;
1206
1207 if (!now.contains(desktopId)) {
1208 window->request_enter_virtual_desktop(desktopId);
1209 }
1210 }
1211 }
1212
1213 for (const QString &desktop : now) {
1214 if (!next.contains(desktop)) {
1215 window->request_leave_virtual_desktop(desktop);
1216 }
1217 }
1218 }
1219}
1220
1222{
1223 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1224 return;
1225 }
1226
1227 d->windows.at(index.row())->request_enter_new_virtual_desktop();
1228}
1229
1231{
1232 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1233 return;
1234 }
1235
1236 auto &window = d->windows.at(index.row());
1237 const auto newActivities = QSet(activities.begin(), activities.end());
1238 const auto plasmaActivities = window->activities;
1239 const auto oldActivities = QSet(plasmaActivities.begin(), plasmaActivities.end());
1240
1241 const auto activitiesToAdd = newActivities - oldActivities;
1242 for (const auto &activity : activitiesToAdd) {
1243 window->request_enter_activity(activity);
1244 }
1245
1246 const auto activitiesToRemove = oldActivities - newActivities;
1247 for (const auto &activity : activitiesToRemove) {
1248 window->request_leave_activity(activity);
1249 }
1250}
1251
1253{
1254 /*
1255 FIXME: This introduces the dependency on Qt::Quick. I might prefer
1256 reversing this and publishing the window pointer through the model,
1257 then calling PlasmaWindow::setMinimizeGeometry in the applet backend,
1258 rather than hand delegate items into the lib, keeping the lib more UI-
1259 agnostic.
1260 */
1261
1262 Q_UNUSED(geometry)
1263
1264 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1265 return;
1266 }
1267
1268 const QQuickItem *item = qobject_cast<const QQuickItem *>(delegate);
1269
1270 if (!item || !item->parentItem()) {
1271 return;
1272 }
1273
1274 QWindow *itemWindow = item->window();
1275
1276 if (!itemWindow) {
1277 return;
1278 }
1279
1280 auto waylandWindow = itemWindow->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
1281
1282 if (!waylandWindow || !waylandWindow->surface()) {
1283 return;
1284 }
1285
1286 QRect rect(item->x(), item->y(), item->width(), item->height());
1287 rect.moveTopLeft(item->parentItem()->mapToScene(rect.topLeft()).toPoint());
1288
1289 auto &window = d->windows.at(index.row());
1290
1291 window->set_minimized_geometry(waylandWindow->surface(), rect.x(), rect.y(), rect.width(), rect.height());
1292}
1293
1295{
1296 Q_ASSERT(mimeData);
1297
1298 if (ok) {
1299 *ok = false;
1300 }
1301
1302 if (!mimeData->hasFormat(Private::mimeType())) {
1303 return {};
1304 }
1305
1306 QUuid id(mimeData->data(Private::mimeType()));
1307 *ok = !id.isNull();
1308
1309 return id;
1310}
1311
1313{
1314 Q_ASSERT(mimeData);
1315 QList<QUuid> ids;
1316
1317 if (ok) {
1318 *ok = false;
1319 }
1320
1321 if (!mimeData->hasFormat(Private::groupMimeType())) {
1322 // Try to extract single window id.
1323 bool singularOk;
1324 QUuid id = winIdFromMimeData(mimeData, &singularOk);
1325
1326 if (ok) {
1327 *ok = singularOk;
1328 }
1329
1330 if (singularOk) {
1331 ids << id;
1332 }
1333
1334 return ids;
1335 }
1336
1337 // FIXME: Extracting multiple winids is still unimplemented;
1338 // TaskGroupingProxy::data(..., ::MimeData) can't produce
1339 // a payload with them anyways.
1340
1341 return ids;
1342}
1343
1344}
1345
1346#include "waylandtasksmodel.moc"
1347
1348#include "moc_waylandtasksmodel.cpp"
void deleted(const QString &path)
void dirty(const QString &path)
void created(const QString &path)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
static bool isPlatformWayland()
A tasks model for Wayland windows.
void requestActivities(const QModelIndex &index, const QStringList &activities) override
Request moving the window at the given index to the specified activities.
void requestToggleKeepBelow(const QModelIndex &index) override
Request toggling the keep-below state of the task at the given index.
void requestToggleMinimized(const QModelIndex &index) override
Request toggling the minimized state of the window at the given index.
void requestClose(const QModelIndex &index) override
Request the window at the given index be closed.
void requestActivate(const QModelIndex &index) override
Request activation of the window at the given index.
void requestNewInstance(const QModelIndex &index) override
Request an additional instance of the application owning the window at the given index.
void requestOpenUrls(const QModelIndex &index, const QList< QUrl > &urls) override
Runs the application backing the launcher at the given index with the given URLs.
void requestMove(const QModelIndex &index) override
Request starting an interactive move for the window at the given index.
void requestToggleKeepAbove(const QModelIndex &index) override
Request toggling the keep-above state of the task at the given index.
void requestToggleMaximized(const QModelIndex &index) override
Request toggling the maximized state of the task at the given index.
static QList< QUuid > winIdsFromMimeData(const QMimeData *mimeData, bool *ok=nullptr)
Tries to extract process-internal Wayland window ids from supplied mime data.
void requestToggleShaded(const QModelIndex &index) override
Request toggling the shaded state of the task at the given index.
void requestToggleFullScreen(const QModelIndex &index) override
Request toggling the fullscreen state of the task at the given index.
static QUuid winIdFromMimeData(const QMimeData *mimeData, bool *ok=nullptr)
Tries to extract a process-internal Wayland window id from supplied mime data.
void requestResize(const QModelIndex &index) override
Request starting an interactive move for the window at the given index.
void requestNewVirtualDesktop(const QModelIndex &index) override
Request entering the window at the given index on a new virtual desktop, which is created in response...
void requestVirtualDesktops(const QModelIndex &index, const QVariantList &desktops) override
Request entering the window at the given index on the specified virtual desktops, leaving any other d...
void requestPublishDelegateGeometry(const QModelIndex &index, const QRect &geometry, QObject *delegate=nullptr) override
Request informing the window manager of new geometry for a visual delegate for the window at the give...
void requestToggleNoBorder(const QModelIndex &index) override
Request toggling the no border state of the task at given index.
KCALUTILS_EXPORT QString mimeType()
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
QVariant read(const QByteArray &data, int versionOverride=0)
QWidget * window(QObject *job)
void initialize(StandardShortcut id)
bool checkIndex(const QModelIndex &index, CheckIndexOptions options) const const
virtual QMimeData * mimeData(const QModelIndexList &indexes) const const
QByteArray & append(QByteArrayView data)
QDateTime currentDateTime()
QIcon fromTheme(const QString &name)
iterator begin()
iterator end()
bool isEmpty() const const
bool isValid() const const
int row() const const
Q_EMITQ_EMIT
Q_OBJECTQ_OBJECT
Q_SIGNALSQ_SIGNALS
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
void destroyed(QObject *obj)
bool disconnect(const QMetaObject::Connection &connection)
QObject * parent() const const
T qobject_cast(QObject *object)
QPoint toPoint() const const
QPointF mapToScene(const QPointF &point) const const
QQuickItem * parentItem() const const
QQuickWindow * window() const const
int height() const const
void moveTopLeft(const QPoint &position)
QPoint topLeft() const const
int width() const const
int x() const const
int y() const const
QStringList standardLocations(StandardLocation type)
bool isEmpty() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
DisplayRole
QFuture< T > run(Function function,...)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QUuid createUuid()
Qt::WindowStates windowState() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Mar 7 2025 11:52:22 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.