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

KDE's Doxygen guidelines are available online.