1/*
2 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6#include "plasmawindowmanagement.h"
7#include "event_queue.h"
8#include "output.h"
9#include "plasmavirtualdesktop.h"
10#include "plasmawindowmodel.h"
11#include "surface.h"
12#include "wayland_pointer_p.h"
13// Wayland
14#include <wayland-plasma-window-management-client-protocol.h>
15
16#include <QFutureWatcher>
17#include <QTimer>
18#include <QtConcurrentRun>
19#include <qplatformdefs.h>
20
21#include <cerrno>
22
23namespace KWayland
24{
25namespace Client
26{
27class Q_DECL_HIDDEN PlasmaWindowManagement::Private
28{
29public:
30 Private(PlasmaWindowManagement *q);
31 WaylandPointer<org_kde_plasma_window_management, org_kde_plasma_window_management_destroy> wm;
32 EventQueue *queue = nullptr;
33 bool showingDesktop = false;
34 QList<PlasmaWindow *> windows;
35 PlasmaWindow *activeWindow = nullptr;
36 QList<quint32> stackingOrder;
37 QList<QByteArray> stackingOrderUuids;
38
39 void setup(org_kde_plasma_window_management *wm);
40
41private:
42 static void showDesktopCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t state);
43 static void windowCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t id);
44 static void windowWithUuidCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t id, const char *uuid);
45 static void stackingOrderCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, wl_array *ids);
46 static void stackingOrderUuidsCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, const char *uuids);
47 void setShowDesktop(bool set);
48 void windowCreated(org_kde_plasma_window *id, quint32 internalId, const char *uuid);
49 void setStackingOrder(const QList<quint32> &ids);
50 void setStackingOrder(const QList<QByteArray> &uuids);
51
52 static struct org_kde_plasma_window_management_listener s_listener;
53 PlasmaWindowManagement *q;
54};
55
56class Q_DECL_HIDDEN PlasmaWindow::Private
57{
58public:
59 Private(org_kde_plasma_window *window, quint32 internalId, const char *uuid, PlasmaWindow *q);
60 WaylandPointer<org_kde_plasma_window, org_kde_plasma_window_destroy> window;
61 quint32 internalId; ///< @deprecated
62 QByteArray uuid;
63 QString title;
64 QString appId;
65 quint32 desktop = 0;
66 bool active = false;
67 bool minimized = false;
68 bool maximized = false;
69 bool fullscreen = false;
70 bool keepAbove = false;
71 bool keepBelow = false;
72 bool onAllDesktops = false;
73 bool demandsAttention = false;
74 bool closeable = false;
75 bool minimizeable = false;
76 bool maximizeable = false;
77 bool fullscreenable = false;
78 bool skipTaskbar = false;
79 bool skipSwitcher = false;
80 bool shadeable = false;
81 bool shaded = false;
82 bool movable = false;
83 bool resizable = false;
84 bool virtualDesktopChangeable = false;
85 QIcon icon;
86 PlasmaWindowManagement *wm = nullptr;
87 bool unmapped = false;
88 QPointer<PlasmaWindow> parentWindow;
89 QMetaObject::Connection parentWindowUnmappedConnection;
90 QStringList plasmaVirtualDesktops;
91 QStringList plasmaActivities;
92 QRect geometry;
93 quint32 pid = 0;
94 QString resourceName;
95 QString applicationMenuServiceName;
96 QString applicationMenuObjectPath;
97
98private:
99 static void titleChangedCallback(void *data, org_kde_plasma_window *window, const char *title);
100 static void appIdChangedCallback(void *data, org_kde_plasma_window *window, const char *app_id);
101 static void pidChangedCallback(void *data, org_kde_plasma_window *window, uint32_t pid);
102 static void resourceNameChangedCallback(void *data, org_kde_plasma_window *window, const char *resourceName);
103 static void stateChangedCallback(void *data, org_kde_plasma_window *window, uint32_t state);
104 static void virtualDesktopChangedCallback(void *data, org_kde_plasma_window *window, int32_t number);
105 static void themedIconNameChangedCallback(void *data, org_kde_plasma_window *window, const char *name);
106 static void unmappedCallback(void *data, org_kde_plasma_window *window);
107 static void initialStateCallback(void *data, org_kde_plasma_window *window);
108 static void parentWindowCallback(void *data, org_kde_plasma_window *window, org_kde_plasma_window *parent);
109 static void windowGeometryCallback(void *data, org_kde_plasma_window *window, int32_t x, int32_t y, uint32_t width, uint32_t height);
110 static void iconChangedCallback(void *data, org_kde_plasma_window *org_kde_plasma_window);
111 static void virtualDesktopEnteredCallback(void *data, org_kde_plasma_window *org_kde_plasma_window, const char *id);
112 static void virtualDesktopLeftCallback(void *data, org_kde_plasma_window *org_kde_plasma_window, const char *id);
113 static void appmenuChangedCallback(void *data, org_kde_plasma_window *org_kde_plasma_window, const char *service_name, const char *object_path);
114 static void activityEnteredCallback(void *data, org_kde_plasma_window *org_kde_plasma_window, const char *id);
115 static void activityLeftCallback(void *data, org_kde_plasma_window *org_kde_plasma_window, const char *id);
116 void setActive(bool set);
117 void setMinimized(bool set);
118 void setMaximized(bool set);
119 void setFullscreen(bool set);
120 void setKeepAbove(bool set);
121 void setKeepBelow(bool set);
122 void setOnAllDesktops(bool set);
123 void setDemandsAttention(bool set);
124 void setCloseable(bool set);
125 void setMinimizeable(bool set);
126 void setMaximizeable(bool set);
127 void setFullscreenable(bool set);
128 void setSkipTaskbar(bool skip);
129 void setSkipSwitcher(bool skip);
130 void setShadeable(bool set);
131 void setShaded(bool set);
132 void setMovable(bool set);
133 void setResizable(bool set);
134 void setVirtualDesktopChangeable(bool set);
135 void setParentWindow(PlasmaWindow *parentWindow);
136 void setPid(const quint32 pid);
137
138 static Private *cast(void *data)
139 {
140 return reinterpret_cast<Private *>(data);
141 }
142
143 PlasmaWindow *q;
144
145 static struct org_kde_plasma_window_listener s_listener;
146};
147
148PlasmaWindowManagement::Private::Private(PlasmaWindowManagement *q)
149 : q(q)
150{
151}
152
153org_kde_plasma_window_management_listener PlasmaWindowManagement::Private::s_listener = {
154 .show_desktop_changed: showDesktopCallback,
155 .window: windowCallback,
156 .stacking_order_changed: stackingOrderCallback,
157 .stacking_order_uuid_changed: stackingOrderUuidsCallback,
158 .window_with_uuid: windowWithUuidCallback,
159};
160
161void PlasmaWindowManagement::Private::setup(org_kde_plasma_window_management *windowManagement)
162{
163 Q_ASSERT(!wm);
164 Q_ASSERT(windowManagement);
165 wm.setup(pointer: windowManagement);
166 org_kde_plasma_window_management_add_listener(org_kde_plasma_window_management: windowManagement, listener: &s_listener, data: this);
167}
168
169void PlasmaWindowManagement::Private::showDesktopCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t state)
170{
171 auto wm = reinterpret_cast<PlasmaWindowManagement::Private *>(data);
172 Q_ASSERT(wm->wm == org_kde_plasma_window_management);
173 switch (state) {
174 case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED:
175 wm->setShowDesktop(true);
176 break;
177 case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED:
178 wm->setShowDesktop(false);
179 break;
180 default:
181 Q_UNREACHABLE();
182 break;
183 }
184}
185
186void PlasmaWindowManagement::Private::setShowDesktop(bool set)
187{
188 if (showingDesktop == set) {
189 return;
190 }
191 showingDesktop = set;
192 Q_EMIT q->showingDesktopChanged(showingDesktop);
193}
194
195void PlasmaWindowManagement::Private::windowCallback(void *data, org_kde_plasma_window_management *interface, uint32_t id)
196{
197 auto wm = reinterpret_cast<PlasmaWindowManagement::Private *>(data);
198 Q_ASSERT(wm->wm == interface);
199 QTimer *timer = new QTimer();
200 timer->setSingleShot(true);
201 timer->setInterval(0);
202 QObject::connect(
203 sender: timer,
204 signal: &QTimer::timeout,
205 context: wm->q,
206 slot: [timer, wm, id] {
207 wm->windowCreated(id: org_kde_plasma_window_management_get_window(org_kde_plasma_window_management: wm->wm, internal_window_id: id), internalId: id, uuid: "unavailable");
208 timer->deleteLater();
209 },
210 type: Qt::QueuedConnection);
211 timer->start();
212}
213
214void PlasmaWindowManagement::Private::windowWithUuidCallback(void *data, org_kde_plasma_window_management *interface, uint32_t id, const char *_uuid)
215{
216 QByteArray uuid(_uuid);
217 auto wm = reinterpret_cast<PlasmaWindowManagement::Private *>(data);
218 Q_ASSERT(wm->wm == interface);
219 QTimer *timer = new QTimer();
220 timer->setSingleShot(true);
221 timer->setInterval(0);
222 QObject::connect(
223 sender: timer,
224 signal: &QTimer::timeout,
225 context: wm->q,
226 slot: [timer, wm, id, uuid] {
227 wm->windowCreated(id: org_kde_plasma_window_management_get_window_by_uuid(org_kde_plasma_window_management: wm->wm, internal_window_uuid: uuid), internalId: id, uuid);
228 timer->deleteLater();
229 },
230 type: Qt::QueuedConnection);
231 timer->start();
232}
233
234void PlasmaWindowManagement::Private::windowCreated(org_kde_plasma_window *id, quint32 internalId, const char *uuid)
235{
236 if (queue) {
237 queue->addProxy(proxy: id);
238 }
239 PlasmaWindow *window = new PlasmaWindow(q, id, internalId, uuid);
240 window->d->wm = q;
241 windows << window;
242
243 const auto windowRemoved = [this, window] {
244 windows.removeAll(t: window);
245 if (activeWindow == window) {
246 activeWindow = nullptr;
247 Q_EMIT q->activeWindowChanged();
248 }
249 };
250
251 QObject::connect(sender: window, signal: &QObject::destroyed, context: q, slot: windowRemoved);
252 // unmapped is emitted earlier than QObject::destroyed. We want to update windows earlier to ensure other slot will see the up to date value of
253 // PlasmaWindowManagement::windows().
254 QObject::connect(sender: window, signal: &PlasmaWindow::unmapped, context: q, slot: windowRemoved);
255 QObject::connect(sender: window, signal: &PlasmaWindow::activeChanged, context: q, slot: [this, window] {
256 if (window->d->unmapped) {
257 return;
258 }
259 if (window->isActive()) {
260 if (activeWindow == window) {
261 return;
262 }
263 activeWindow = window;
264 Q_EMIT q->activeWindowChanged();
265 } else {
266 if (activeWindow == window) {
267 activeWindow = nullptr;
268 Q_EMIT q->activeWindowChanged();
269 }
270 }
271 });
272}
273
274void PlasmaWindowManagement::Private::stackingOrderCallback(void *data, org_kde_plasma_window_management *interface, wl_array *ids)
275{
276 // This is no-op since setStackingOrder(const QList<quint32> &ids) is deprecated since 5.73,
277 // but we can't remove this method because it's needed in
278 // PlasmaWindowManagement::Private::s_listener struct
279}
280
281void PlasmaWindowManagement::Private::stackingOrderUuidsCallback(void *data, org_kde_plasma_window_management *interface, const char *uuids)
282{
283 auto wm = reinterpret_cast<PlasmaWindowManagement::Private *>(data);
284 Q_ASSERT(wm->wm == interface);
285 wm->setStackingOrder(QByteArray(uuids).split(sep: ';').toVector());
286}
287
288void PlasmaWindowManagement::Private::setStackingOrder(const QList<QByteArray> &uuids)
289{
290 if (stackingOrderUuids == uuids) {
291 return;
292 }
293 stackingOrderUuids = uuids;
294 Q_EMIT q->stackingOrderUuidsChanged();
295}
296
297PlasmaWindowManagement::PlasmaWindowManagement(QObject *parent)
298 : QObject(parent)
299 , d(new Private(this))
300{
301}
302
303PlasmaWindowManagement::~PlasmaWindowManagement()
304{
305 release();
306}
307
308void PlasmaWindowManagement::destroy()
309{
310 if (!d->wm) {
311 return;
312 }
313 Q_EMIT interfaceAboutToBeDestroyed();
314 d->wm.destroy();
315}
316
317void PlasmaWindowManagement::release()
318{
319 if (!d->wm) {
320 return;
321 }
322 Q_EMIT interfaceAboutToBeReleased();
323 d->wm.release();
324}
325
326void PlasmaWindowManagement::setup(org_kde_plasma_window_management *wm)
327{
328 d->setup(wm);
329}
330
331void PlasmaWindowManagement::setEventQueue(EventQueue *queue)
332{
333 d->queue = queue;
334}
335
336EventQueue *PlasmaWindowManagement::eventQueue()
337{
338 return d->queue;
339}
340
341bool PlasmaWindowManagement::isValid() const
342{
343 return d->wm.isValid();
344}
345
346PlasmaWindowManagement::operator org_kde_plasma_window_management *()
347{
348 return d->wm;
349}
350
351PlasmaWindowManagement::operator org_kde_plasma_window_management *() const
352{
353 return d->wm;
354}
355
356void PlasmaWindowManagement::hideDesktop()
357{
358 setShowingDesktop(false);
359}
360
361void PlasmaWindowManagement::showDesktop()
362{
363 setShowingDesktop(true);
364}
365
366void PlasmaWindowManagement::setShowingDesktop(bool show)
367{
368 org_kde_plasma_window_management_show_desktop(org_kde_plasma_window_management: d->wm,
369 state: show ? ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED
370 : ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED);
371}
372
373bool PlasmaWindowManagement::isShowingDesktop() const
374{
375 return d->showingDesktop;
376}
377
378QList<PlasmaWindow *> PlasmaWindowManagement::windows() const
379{
380 return d->windows;
381}
382
383PlasmaWindow *PlasmaWindowManagement::activeWindow() const
384{
385 return d->activeWindow;
386}
387
388PlasmaWindowModel *PlasmaWindowManagement::createWindowModel()
389{
390 return new PlasmaWindowModel(this);
391}
392
393QList<QByteArray> PlasmaWindowManagement::stackingOrderUuids() const
394{
395 return d->stackingOrderUuids;
396}
397
398org_kde_plasma_window_listener PlasmaWindow::Private::s_listener = {
399 .title_changed: titleChangedCallback,
400 .app_id_changed: appIdChangedCallback,
401 .state_changed: stateChangedCallback,
402 .virtual_desktop_changed: virtualDesktopChangedCallback,
403 .themed_icon_name_changed: themedIconNameChangedCallback,
404 .unmapped: unmappedCallback,
405 .initial_state: initialStateCallback,
406 .parent_window: parentWindowCallback,
407 .geometry: windowGeometryCallback,
408 .icon_changed: iconChangedCallback,
409 .pid_changed: pidChangedCallback,
410 .virtual_desktop_entered: virtualDesktopEnteredCallback,
411 .virtual_desktop_left: virtualDesktopLeftCallback,
412 .application_menu: appmenuChangedCallback,
413 .activity_entered: activityEnteredCallback,
414 .activity_left: activityLeftCallback,
415 .resource_name_changed: resourceNameChangedCallback,
416};
417
418void PlasmaWindow::Private::appmenuChangedCallback(void *data, org_kde_plasma_window *window, const char *service_name, const char *object_path)
419{
420 Q_UNUSED(window)
421
422 Private *p = cast(data);
423
424 p->applicationMenuServiceName = QString::fromUtf8(utf8: service_name);
425 p->applicationMenuObjectPath = QString::fromUtf8(utf8: object_path);
426
427 Q_EMIT p->q->applicationMenuChanged();
428}
429
430void PlasmaWindow::Private::parentWindowCallback(void *data, org_kde_plasma_window *window, org_kde_plasma_window *parent)
431{
432 Q_UNUSED(window)
433 Private *p = cast(data);
434 const auto windows = p->wm->windows();
435 auto it = std::find_if(first: windows.constBegin(), last: windows.constEnd(), pred: [parent](const PlasmaWindow *w) {
436 return *w == parent;
437 });
438 p->setParentWindow(it != windows.constEnd() ? *it : nullptr);
439}
440
441void PlasmaWindow::Private::windowGeometryCallback(void *data, org_kde_plasma_window *window, int32_t x, int32_t y, uint32_t width, uint32_t height)
442{
443 Q_UNUSED(window)
444 Private *p = cast(data);
445 QRect geo(x, y, width, height);
446 if (geo == p->geometry) {
447 return;
448 }
449 p->geometry = geo;
450 Q_EMIT p->q->geometryChanged();
451}
452
453void PlasmaWindow::Private::setParentWindow(PlasmaWindow *parent)
454{
455 const auto old = parentWindow;
456 QObject::disconnect(parentWindowUnmappedConnection);
457 if (parent && !parent->d->unmapped) {
458 parentWindow = QPointer<PlasmaWindow>(parent);
459 parentWindowUnmappedConnection = QObject::connect(sender: parent, signal: &PlasmaWindow::unmapped, context: q, slot: [this] {
460 setParentWindow(nullptr);
461 });
462 } else {
463 parentWindow = QPointer<PlasmaWindow>();
464 parentWindowUnmappedConnection = QMetaObject::Connection();
465 }
466 if (parentWindow.data() != old.data()) {
467 Q_EMIT q->parentWindowChanged();
468 }
469}
470
471void PlasmaWindow::Private::initialStateCallback(void *data, org_kde_plasma_window *window)
472{
473 Q_UNUSED(window)
474 Private *p = cast(data);
475 if (!p->unmapped) {
476 Q_EMIT p->wm->windowCreated(window: p->q);
477 }
478}
479
480void PlasmaWindow::Private::titleChangedCallback(void *data, org_kde_plasma_window *window, const char *title)
481{
482 Q_UNUSED(window)
483 Private *p = cast(data);
484 const QString t = QString::fromUtf8(utf8: title);
485 if (p->title == t) {
486 return;
487 }
488 p->title = t;
489 Q_EMIT p->q->titleChanged();
490}
491
492void PlasmaWindow::Private::appIdChangedCallback(void *data, org_kde_plasma_window *window, const char *appId)
493{
494 Q_UNUSED(window)
495 Private *p = cast(data);
496 const QString s = QString::fromUtf8(utf8: appId);
497 if (s == p->appId) {
498 return;
499 }
500 p->appId = s;
501 Q_EMIT p->q->appIdChanged();
502}
503
504void PlasmaWindow::Private::pidChangedCallback(void *data, org_kde_plasma_window *window, uint32_t pid)
505{
506 Q_UNUSED(window)
507 Private *p = cast(data);
508 if (p->pid == static_cast<quint32>(pid)) {
509 return;
510 }
511 p->pid = pid;
512}
513
514void PlasmaWindow::Private::resourceNameChangedCallback(void *data, org_kde_plasma_window *window, const char *resourceName)
515{
516 Q_UNUSED(window)
517 Private *p = cast(data);
518 const QString s = QString::fromUtf8(utf8: resourceName);
519 if (s == p->resourceName) {
520 return;
521 }
522 p->resourceName = s;
523 Q_EMIT p->q->resourceNameChanged();
524}
525
526void PlasmaWindow::Private::virtualDesktopChangedCallback([[maybe_unused]] void *data,
527 [[maybe_unused]] org_kde_plasma_window *window,
528 [[maybe_unused]] int32_t number)
529{
530 // Can't remove this method as it's used in PlasmaWindow::Private::s_listener struct
531}
532
533void PlasmaWindow::Private::unmappedCallback(void *data, org_kde_plasma_window *window)
534{
535 auto p = cast(data);
536 Q_UNUSED(window);
537 p->unmapped = true;
538 Q_EMIT p->q->unmapped();
539 p->q->deleteLater();
540}
541
542void PlasmaWindow::Private::virtualDesktopEnteredCallback(void *data, org_kde_plasma_window *window, const char *id)
543{
544 auto p = cast(data);
545 Q_UNUSED(window);
546 const QString stringId(QString::fromUtf8(utf8: id));
547 p->plasmaVirtualDesktops << stringId;
548 Q_EMIT p->q->plasmaVirtualDesktopEntered(id: stringId);
549 if (p->plasmaVirtualDesktops.count() == 1) {
550 Q_EMIT p->q->onAllDesktopsChanged();
551 }
552}
553
554void PlasmaWindow::Private::virtualDesktopLeftCallback(void *data, org_kde_plasma_window *window, const char *id)
555{
556 auto p = cast(data);
557 Q_UNUSED(window);
558 const QString stringId(QString::fromUtf8(utf8: id));
559 p->plasmaVirtualDesktops.removeAll(t: stringId);
560 Q_EMIT p->q->plasmaVirtualDesktopLeft(id: stringId);
561 if (p->plasmaVirtualDesktops.isEmpty()) {
562 Q_EMIT p->q->onAllDesktopsChanged();
563 }
564}
565
566void PlasmaWindow::Private::activityEnteredCallback(void *data, org_kde_plasma_window *window, const char *id)
567{
568 auto p = cast(data);
569 Q_UNUSED(window);
570 const QString stringId(QString::fromUtf8(utf8: id));
571 p->plasmaActivities << stringId;
572 Q_EMIT p->q->plasmaActivityEntered(id: stringId);
573}
574
575void PlasmaWindow::Private::activityLeftCallback(void *data, org_kde_plasma_window *window, const char *id)
576{
577 auto p = cast(data);
578 Q_UNUSED(window);
579 const QString stringId(QString::fromUtf8(utf8: id));
580 p->plasmaActivities.removeAll(t: stringId);
581 Q_EMIT p->q->plasmaActivityLeft(id: stringId);
582}
583
584void PlasmaWindow::Private::stateChangedCallback(void *data, org_kde_plasma_window *window, uint32_t state)
585{
586 auto p = cast(data);
587 Q_UNUSED(window);
588 p->setActive(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE);
589 p->setMinimized(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED);
590 p->setMaximized(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED);
591 p->setFullscreen(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN);
592 p->setKeepAbove(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE);
593 p->setKeepBelow(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW);
594 p->setOnAllDesktops(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS);
595 p->setDemandsAttention(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION);
596 p->setCloseable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE);
597 p->setFullscreenable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE);
598 p->setMaximizeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE);
599 p->setMinimizeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE);
600 p->setSkipTaskbar(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR);
601 p->setSkipSwitcher(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER);
602 p->setShadeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE);
603 p->setShaded(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED);
604 p->setMovable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE);
605 p->setResizable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE);
606 p->setVirtualDesktopChangeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE);
607}
608
609void PlasmaWindow::Private::themedIconNameChangedCallback(void *data, org_kde_plasma_window *window, const char *name)
610{
611 auto p = cast(data);
612 Q_UNUSED(window);
613 const QString themedName = QString::fromUtf8(utf8: name);
614 if (!themedName.isEmpty()) {
615 QIcon icon = QIcon::fromTheme(name: themedName);
616 p->icon = icon;
617 } else {
618 p->icon = QIcon();
619 }
620 Q_EMIT p->q->iconChanged();
621}
622
623static int readData(int fd, QByteArray &data)
624{
625 // implementation based on QtWayland file qwaylanddataoffer.cpp
626 char buf[4096];
627 int retryCount = 0;
628 int n;
629 while (true) {
630 n = QT_READ(fd: fd, buf: buf, nbytes: sizeof buf);
631 if (n > 0) {
632 data.append(s: buf, len: n);
633 } else if (n == -1 && (errno == EAGAIN) && ++retryCount < 1000) {
634 usleep(useconds: 1000);
635 } else {
636 break;
637 }
638 }
639 return n;
640}
641
642void PlasmaWindow::Private::iconChangedCallback(void *data, org_kde_plasma_window *window)
643{
644 auto p = cast(data);
645 Q_UNUSED(window);
646 int pipeFds[2];
647 if (pipe2(pipedes: pipeFds, O_CLOEXEC | O_NONBLOCK) != 0) {
648 return;
649 }
650 org_kde_plasma_window_get_icon(org_kde_plasma_window: p->window, fd: pipeFds[1]);
651 close(fd: pipeFds[1]);
652 const int pipeFd = pipeFds[0];
653 auto readIcon = [pipeFd]() -> QIcon {
654 QByteArray content;
655 if (readData(fd: pipeFd, data&: content) != 0) {
656 close(fd: pipeFd);
657 return QIcon();
658 }
659 close(fd: pipeFd);
660 QDataStream ds(content);
661 QIcon icon;
662 ds >> icon;
663 return icon;
664 };
665 QFutureWatcher<QIcon> *watcher = new QFutureWatcher<QIcon>(p->q);
666 QObject::connect(sender: watcher, signal: &QFutureWatcher<QIcon>::finished, context: p->q, slot: [p, watcher] {
667 watcher->deleteLater();
668 QIcon icon = watcher->result();
669 if (!icon.isNull()) {
670 p->icon = icon;
671 } else {
672 p->icon = QIcon::fromTheme(QStringLiteral("wayland"));
673 }
674 Q_EMIT p->q->iconChanged();
675 });
676 watcher->setFuture(QtConcurrent::run(f&: readIcon));
677}
678
679void PlasmaWindow::Private::setActive(bool set)
680{
681 if (active == set) {
682 return;
683 }
684 active = set;
685 Q_EMIT q->activeChanged();
686}
687
688void PlasmaWindow::Private::setFullscreen(bool set)
689{
690 if (fullscreen == set) {
691 return;
692 }
693 fullscreen = set;
694 Q_EMIT q->fullscreenChanged();
695}
696
697void PlasmaWindow::Private::setKeepAbove(bool set)
698{
699 if (keepAbove == set) {
700 return;
701 }
702 keepAbove = set;
703 Q_EMIT q->keepAboveChanged();
704}
705
706void PlasmaWindow::Private::setKeepBelow(bool set)
707{
708 if (keepBelow == set) {
709 return;
710 }
711 keepBelow = set;
712 Q_EMIT q->keepBelowChanged();
713}
714
715void PlasmaWindow::Private::setMaximized(bool set)
716{
717 if (maximized == set) {
718 return;
719 }
720 maximized = set;
721 Q_EMIT q->maximizedChanged();
722}
723
724void PlasmaWindow::Private::setMinimized(bool set)
725{
726 if (minimized == set) {
727 return;
728 }
729 minimized = set;
730 Q_EMIT q->minimizedChanged();
731}
732
733void PlasmaWindow::Private::setOnAllDesktops(bool set)
734{
735 if (onAllDesktops == set) {
736 return;
737 }
738 onAllDesktops = set;
739 Q_EMIT q->onAllDesktopsChanged();
740}
741
742void PlasmaWindow::Private::setDemandsAttention(bool set)
743{
744 if (demandsAttention == set) {
745 return;
746 }
747 demandsAttention = set;
748 Q_EMIT q->demandsAttentionChanged();
749}
750
751void PlasmaWindow::Private::setCloseable(bool set)
752{
753 if (closeable == set) {
754 return;
755 }
756 closeable = set;
757 Q_EMIT q->closeableChanged();
758}
759
760void PlasmaWindow::Private::setFullscreenable(bool set)
761{
762 if (fullscreenable == set) {
763 return;
764 }
765 fullscreenable = set;
766 Q_EMIT q->fullscreenableChanged();
767}
768
769void PlasmaWindow::Private::setMaximizeable(bool set)
770{
771 if (maximizeable == set) {
772 return;
773 }
774 maximizeable = set;
775 Q_EMIT q->maximizeableChanged();
776}
777
778void PlasmaWindow::Private::setMinimizeable(bool set)
779{
780 if (minimizeable == set) {
781 return;
782 }
783 minimizeable = set;
784 Q_EMIT q->minimizeableChanged();
785}
786
787void PlasmaWindow::Private::setSkipTaskbar(bool skip)
788{
789 if (skipTaskbar == skip) {
790 return;
791 }
792 skipTaskbar = skip;
793 Q_EMIT q->skipTaskbarChanged();
794}
795
796void PlasmaWindow::Private::setSkipSwitcher(bool skip)
797{
798 if (skipSwitcher == skip) {
799 return;
800 }
801 skipSwitcher = skip;
802 Q_EMIT q->skipSwitcherChanged();
803}
804
805void PlasmaWindow::Private::setShadeable(bool set)
806{
807 if (shadeable == set) {
808 return;
809 }
810 shadeable = set;
811 Q_EMIT q->shadeableChanged();
812}
813
814void PlasmaWindow::Private::setShaded(bool set)
815{
816 if (shaded == set) {
817 return;
818 }
819 shaded = set;
820 Q_EMIT q->shadedChanged();
821}
822
823void PlasmaWindow::Private::setMovable(bool set)
824{
825 if (movable == set) {
826 return;
827 }
828 movable = set;
829 Q_EMIT q->movableChanged();
830}
831
832void PlasmaWindow::Private::setResizable(bool set)
833{
834 if (resizable == set) {
835 return;
836 }
837 resizable = set;
838 Q_EMIT q->resizableChanged();
839}
840
841void PlasmaWindow::Private::setVirtualDesktopChangeable(bool set)
842{
843 if (virtualDesktopChangeable == set) {
844 return;
845 }
846 virtualDesktopChangeable = set;
847 Q_EMIT q->virtualDesktopChangeableChanged();
848}
849
850PlasmaWindow::Private::Private(org_kde_plasma_window *w, quint32 internalId, const char *uuid, PlasmaWindow *q)
851 : internalId(internalId)
852 , uuid(uuid)
853 , q(q)
854{
855 Q_ASSERT(!this->uuid.isEmpty());
856 window.setup(pointer: w);
857 org_kde_plasma_window_add_listener(org_kde_plasma_window: w, listener: &s_listener, data: this);
858}
859
860PlasmaWindow::PlasmaWindow(PlasmaWindowManagement *parent, org_kde_plasma_window *window, quint32 internalId, const char *uuid)
861 : QObject(parent)
862 , d(new Private(window, internalId, uuid, this))
863{
864}
865
866PlasmaWindow::~PlasmaWindow()
867{
868 release();
869}
870
871void PlasmaWindow::destroy()
872{
873 d->window.destroy();
874}
875
876void PlasmaWindow::release()
877{
878 d->window.release();
879}
880
881bool PlasmaWindow::isValid() const
882{
883 return d->window.isValid();
884}
885
886PlasmaWindow::operator org_kde_plasma_window *() const
887{
888 return d->window;
889}
890
891PlasmaWindow::operator org_kde_plasma_window *()
892{
893 return d->window;
894}
895
896QString PlasmaWindow::appId() const
897{
898 return d->appId;
899}
900
901quint32 PlasmaWindow::pid() const
902{
903 return d->pid;
904}
905
906QString PlasmaWindow::resourceName() const
907{
908 return d->resourceName;
909}
910
911QString PlasmaWindow::title() const
912{
913 return d->title;
914}
915
916bool PlasmaWindow::isActive() const
917{
918 return d->active;
919}
920
921bool PlasmaWindow::isFullscreen() const
922{
923 return d->fullscreen;
924}
925
926bool PlasmaWindow::isKeepAbove() const
927{
928 return d->keepAbove;
929}
930
931bool PlasmaWindow::isKeepBelow() const
932{
933 return d->keepBelow;
934}
935
936bool PlasmaWindow::isMaximized() const
937{
938 return d->maximized;
939}
940
941bool PlasmaWindow::isMinimized() const
942{
943 return d->minimized;
944}
945
946bool PlasmaWindow::isOnAllDesktops() const
947{
948 // from protocol version 8 virtual desktops are managed by plasmaVirtualDesktops
949 if (org_kde_plasma_window_get_version(org_kde_plasma_window: d->window) < 8) {
950 return d->onAllDesktops;
951 } else {
952 return d->plasmaVirtualDesktops.isEmpty();
953 }
954}
955
956bool PlasmaWindow::isDemandingAttention() const
957{
958 return d->demandsAttention;
959}
960
961bool PlasmaWindow::isCloseable() const
962{
963 return d->closeable;
964}
965
966bool PlasmaWindow::isFullscreenable() const
967{
968 return d->fullscreenable;
969}
970
971bool PlasmaWindow::isMaximizeable() const
972{
973 return d->maximizeable;
974}
975
976bool PlasmaWindow::isMinimizeable() const
977{
978 return d->minimizeable;
979}
980
981bool PlasmaWindow::skipTaskbar() const
982{
983 return d->skipTaskbar;
984}
985
986bool PlasmaWindow::skipSwitcher() const
987{
988 return d->skipSwitcher;
989}
990
991QIcon PlasmaWindow::icon() const
992{
993 return d->icon;
994}
995
996bool PlasmaWindow::isShadeable() const
997{
998 return d->shadeable;
999}
1000
1001bool PlasmaWindow::isShaded() const
1002{
1003 return d->shaded;
1004}
1005
1006bool PlasmaWindow::isResizable() const
1007{
1008 return d->resizable;
1009}
1010
1011bool PlasmaWindow::isMovable() const
1012{
1013 return d->movable;
1014}
1015
1016bool PlasmaWindow::isVirtualDesktopChangeable() const
1017{
1018 return d->virtualDesktopChangeable;
1019}
1020
1021QString PlasmaWindow::applicationMenuObjectPath() const
1022{
1023 return d->applicationMenuObjectPath;
1024}
1025
1026QString PlasmaWindow::applicationMenuServiceName() const
1027{
1028 return d->applicationMenuServiceName;
1029}
1030
1031void PlasmaWindow::requestActivate()
1032{
1033 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE, state: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE);
1034}
1035
1036void PlasmaWindow::requestClose()
1037{
1038 org_kde_plasma_window_close(org_kde_plasma_window: d->window);
1039}
1040
1041void PlasmaWindow::requestMove()
1042{
1043 org_kde_plasma_window_request_move(org_kde_plasma_window: d->window);
1044}
1045
1046void PlasmaWindow::requestResize()
1047{
1048 org_kde_plasma_window_request_resize(org_kde_plasma_window: d->window);
1049}
1050
1051void PlasmaWindow::requestToggleKeepAbove()
1052{
1053 if (d->keepAbove) {
1054 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE, state: 0);
1055 } else {
1056 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE, state: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE);
1057 }
1058}
1059
1060void PlasmaWindow::requestToggleKeepBelow()
1061{
1062 if (d->keepBelow) {
1063 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, state: 0);
1064 } else {
1065 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, state: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW);
1066 }
1067}
1068
1069void PlasmaWindow::requestToggleMinimized()
1070{
1071 if (d->minimized) {
1072 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, state: 0);
1073 } else {
1074 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, state: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED);
1075 }
1076}
1077
1078void PlasmaWindow::requestToggleMaximized()
1079{
1080 if (d->maximized) {
1081 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, state: 0);
1082 } else {
1083 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, state: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED);
1084 }
1085}
1086
1087void PlasmaWindow::requestToggleFullscreen()
1088{
1089 if (d->fullscreen) {
1090 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN, state: 0);
1091 } else {
1092 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN, state: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN);
1093 }
1094}
1095
1096void PlasmaWindow::setMinimizedGeometry(Surface *panel, const QRect &geom)
1097{
1098 org_kde_plasma_window_set_minimized_geometry(org_kde_plasma_window: d->window, panel: *panel, x: geom.x(), y: geom.y(), width: geom.width(), height: geom.height());
1099}
1100
1101void PlasmaWindow::unsetMinimizedGeometry(Surface *panel)
1102{
1103 org_kde_plasma_window_unset_minimized_geometry(org_kde_plasma_window: d->window, panel: *panel);
1104}
1105
1106void PlasmaWindow::requestToggleShaded()
1107{
1108 if (d->shaded) {
1109 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, state: 0);
1110 } else {
1111 org_kde_plasma_window_set_state(org_kde_plasma_window: d->window, flags: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, state: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED);
1112 }
1113}
1114
1115QByteArray PlasmaWindow::uuid() const
1116{
1117 return d->uuid;
1118}
1119
1120QPointer<PlasmaWindow> PlasmaWindow::parentWindow() const
1121{
1122 return d->parentWindow;
1123}
1124
1125QRect PlasmaWindow::geometry() const
1126{
1127 return d->geometry;
1128}
1129
1130void PlasmaWindow::requestEnterVirtualDesktop(const QString &id)
1131{
1132 org_kde_plasma_window_request_enter_virtual_desktop(org_kde_plasma_window: d->window, id: id.toUtf8());
1133}
1134
1135void PlasmaWindow::requestEnterNewVirtualDesktop()
1136{
1137 org_kde_plasma_window_request_enter_new_virtual_desktop(org_kde_plasma_window: d->window);
1138}
1139
1140void PlasmaWindow::requestLeaveVirtualDesktop(const QString &id)
1141{
1142 org_kde_plasma_window_request_leave_virtual_desktop(org_kde_plasma_window: d->window, id: id.toUtf8());
1143}
1144
1145QStringList PlasmaWindow::plasmaVirtualDesktops() const
1146{
1147 return d->plasmaVirtualDesktops;
1148}
1149
1150void PlasmaWindow::requestEnterActivity(const QString &id)
1151{
1152 org_kde_plasma_window_request_enter_activity(org_kde_plasma_window: d->window, id: id.toUtf8());
1153}
1154
1155void PlasmaWindow::requestLeaveActivity(const QString &id)
1156{
1157 org_kde_plasma_window_request_leave_activity(org_kde_plasma_window: d->window, id: id.toUtf8());
1158}
1159
1160QStringList PlasmaWindow::plasmaActivities() const
1161{
1162 return d->plasmaActivities;
1163}
1164
1165void PlasmaWindow::sendToOutput(KWayland::Client::Output *output) const
1166{
1167 if (org_kde_plasma_window_get_version(org_kde_plasma_window: d->window) >= ORG_KDE_PLASMA_WINDOW_SEND_TO_OUTPUT_SINCE_VERSION) {
1168 org_kde_plasma_window_send_to_output(org_kde_plasma_window: d->window, output: *output);
1169 }
1170}
1171
1172class Q_DECL_HIDDEN PlasmaActivationFeedback::Private
1173{
1174public:
1175 Private(PlasmaActivationFeedback *q);
1176 WaylandPointer<org_kde_plasma_activation_feedback, org_kde_plasma_activation_feedback_destroy> feedback;
1177 EventQueue *queue = nullptr;
1178
1179 void setup(org_kde_plasma_activation_feedback *feedback);
1180
1181private:
1182 static void activationCallback(void *data, struct org_kde_plasma_activation_feedback *feedback, struct org_kde_plasma_activation *id);
1183
1184 static struct org_kde_plasma_activation_feedback_listener s_listener;
1185 PlasmaActivationFeedback *q;
1186};
1187
1188PlasmaActivationFeedback::Private::Private(PlasmaActivationFeedback *q)
1189 : q(q)
1190{
1191}
1192
1193org_kde_plasma_activation_feedback_listener PlasmaActivationFeedback::Private::s_listener = {
1194 .activation: activationCallback,
1195};
1196
1197void PlasmaActivationFeedback::Private::activationCallback(void *data, org_kde_plasma_activation_feedback *interface, struct org_kde_plasma_activation *id)
1198{
1199 auto feedbackPrivate = reinterpret_cast<PlasmaActivationFeedback::Private *>(data);
1200 Q_ASSERT(feedbackPrivate->feedback == interface);
1201 auto activation = new PlasmaActivation(feedbackPrivate->q, id);
1202 Q_EMIT feedbackPrivate->q->activation(activation);
1203}
1204
1205void PlasmaActivationFeedback::Private::setup(org_kde_plasma_activation_feedback *m)
1206{
1207 Q_ASSERT(!feedback);
1208 Q_ASSERT(m);
1209 feedback.setup(pointer: m);
1210 org_kde_plasma_activation_feedback_add_listener(org_kde_plasma_activation_feedback: m, listener: &s_listener, data: this);
1211}
1212
1213PlasmaActivationFeedback::PlasmaActivationFeedback(QObject *parent)
1214 : QObject(parent)
1215 , d(new Private(this))
1216{
1217}
1218
1219PlasmaActivationFeedback::~PlasmaActivationFeedback()
1220{
1221 release();
1222}
1223
1224void PlasmaActivationFeedback::destroy()
1225{
1226 if (!d->feedback) {
1227 return;
1228 }
1229 Q_EMIT interfaceAboutToBeDestroyed();
1230 d->feedback.destroy();
1231}
1232
1233void PlasmaActivationFeedback::release()
1234{
1235 if (!d->feedback) {
1236 return;
1237 }
1238 Q_EMIT interfaceAboutToBeReleased();
1239 d->feedback.release();
1240}
1241
1242void PlasmaActivationFeedback::setup(org_kde_plasma_activation_feedback *wm)
1243{
1244 d->setup(wm);
1245}
1246
1247void PlasmaActivationFeedback::setEventQueue(EventQueue *queue)
1248{
1249 d->queue = queue;
1250}
1251
1252EventQueue *PlasmaActivationFeedback::eventQueue()
1253{
1254 return d->queue;
1255}
1256
1257bool PlasmaActivationFeedback::isValid() const
1258{
1259 return d->feedback.isValid();
1260}
1261
1262PlasmaActivationFeedback::operator org_kde_plasma_activation_feedback *()
1263{
1264 return d->feedback;
1265}
1266
1267PlasmaActivationFeedback::operator org_kde_plasma_activation_feedback *() const
1268{
1269 return d->feedback;
1270}
1271
1272class Q_DECL_HIDDEN PlasmaActivation::Private
1273{
1274public:
1275 Private(org_kde_plasma_activation *activation, PlasmaActivation *q)
1276 : activation(activation)
1277 {
1278 org_kde_plasma_activation_add_listener(org_kde_plasma_activation: activation, listener: &s_listener, data: q);
1279 }
1280
1281 static PlasmaActivation *cast(void *data)
1282 {
1283 return reinterpret_cast<PlasmaActivation *>(data);
1284 }
1285 WaylandPointer<org_kde_plasma_activation, org_kde_plasma_activation_destroy> activation;
1286
1287 static org_kde_plasma_activation_listener s_listener;
1288 static void app_idCallback(void *data, struct org_kde_plasma_activation *org_kde_plasma_activation, const char *app_id);
1289 static void finishedCallback(void *data, struct org_kde_plasma_activation *org_kde_plasma_activation);
1290};
1291
1292org_kde_plasma_activation_listener PlasmaActivation::Private::s_listener = {
1293 .app_id: app_idCallback,
1294 .finished: finishedCallback,
1295};
1296
1297void PlasmaActivation::Private::app_idCallback(void *data, org_kde_plasma_activation *activation, const char *appId)
1298{
1299 Q_UNUSED(activation)
1300 Q_EMIT cast(data)->applicationId(appId: QString::fromUtf8(utf8: appId));
1301}
1302
1303void PlasmaActivation::Private::finishedCallback(void *data, org_kde_plasma_activation *)
1304{
1305 auto q = cast(data);
1306 Q_EMIT q->finished();
1307 Q_EMIT q->deleteLater();
1308 q->d->activation.release();
1309}
1310
1311PlasmaActivation::PlasmaActivation(PlasmaActivationFeedback *parent, org_kde_plasma_activation *activation)
1312 : QObject(parent)
1313 , d(new PlasmaActivation::Private(activation, this))
1314{
1315}
1316
1317PlasmaActivation::~PlasmaActivation() = default;
1318}
1319}
1320
1321#include "moc_plasmawindowmanagement.cpp"
1322

source code of kwayland/src/client/plasmawindowmanagement.cpp