1 /********************************************************************
2 Copyright © 2020 Roman Gilg <subdiff@gmail.com>
3 
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) version 3, or any
8 later version accepted by the membership of KDE e.V. (or its
9 successor approved by the membership of KDE e.V.), which shall
10 act as a proxy defined in Section 6 of version 3 of the license.
11 
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library.  If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
20 #include "plasma_window_p.h"
21 
22 #include "display.h"
23 #include "plasma_virtual_desktop.h"
24 #include "surface.h"
25 
26 #include <QFile>
27 #include <QHash>
28 #include <QIcon>
29 #include <QList>
30 #include <QRect>
31 #include <QVector>
32 #include <QtConcurrentRun>
33 
34 #include <csignal>
35 #include <wayland-server.h>
36 
37 namespace Wrapland::Server
38 {
39 
40 const struct org_kde_plasma_window_management_interface PlasmaWindowManager::Private::s_interface
41     = {
42         showDesktopCallback,
43         getWindowCallback,
44 };
45 
Private(Display * display,PlasmaWindowManager * qptr)46 PlasmaWindowManager::Private::Private(Display* display, PlasmaWindowManager* qptr)
47     : PlasmaWindowManagerGlobal(qptr,
48                                 display,
49                                 &org_kde_plasma_window_management_interface,
50                                 &s_interface)
51 {
52     create();
53 }
54 
PlasmaWindowManager(Display * display,QObject * parent)55 PlasmaWindowManager::PlasmaWindowManager(Display* display, QObject* parent)
56     : QObject(parent)
57     , d_ptr(new Private(display, this))
58 {
59     // Needed because the icon is sent via a pipe and when it closes while being written to would
60     // kill off the compositor.
61     // TODO(romangg): Replace the pipe with a Unix domain socket and set on it to ignore the SIGPIPE
62     //                signal. See issue #7.
63     signal(SIGPIPE, SIG_IGN); // NOLINT
64 }
65 
66 PlasmaWindowManager::~PlasmaWindowManager() = default;
67 
bindInit(PlasmaWindowManagerBind * bind)68 void PlasmaWindowManager::Private::bindInit(PlasmaWindowManagerBind* bind)
69 {
70     for (auto it = windows.constBegin(); it != windows.constEnd(); ++it) {
71         send<org_kde_plasma_window_management_send_window>(bind, (*it)->d_ptr->windowId);
72     }
73 }
74 
sendShowingDesktopState()75 void PlasmaWindowManager::Private::sendShowingDesktopState()
76 {
77     uint32_t state = 0;
78     switch (desktopState) {
79     case ShowingDesktopState::Enabled:
80         state = ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED;
81         break;
82     case ShowingDesktopState::Disabled:
83         state = ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED;
84         break;
85     default:
86         Q_UNREACHABLE();
87         break;
88     }
89     send<org_kde_plasma_window_management_send_show_desktop_changed>(state);
90 }
91 
showDesktopCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t desktopState)92 void PlasmaWindowManager::Private::showDesktopCallback([[maybe_unused]] wl_client* wlClient,
93                                                        wl_resource* wlResource,
94                                                        uint32_t desktopState)
95 {
96     auto state = ShowingDesktopState::Disabled;
97 
98     switch (desktopState) {
99     case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED:
100         state = ShowingDesktopState::Enabled;
101         break;
102     case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED:
103     default:
104         state = ShowingDesktopState::Disabled;
105         break;
106     }
107 
108     Q_EMIT handle(wlResource)->requestChangeShowingDesktop(state);
109 }
110 
getWindowCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t id,uint32_t internalWindowId)111 void PlasmaWindowManager::Private::getWindowCallback([[maybe_unused]] wl_client* wlClient,
112                                                      wl_resource* wlResource,
113                                                      uint32_t id,
114                                                      uint32_t internalWindowId)
115 {
116     auto priv = handle(wlResource)->d_ptr.get();
117     auto bind = priv->getBind(wlResource);
118 
119     auto it = std::find_if(priv->windows.constBegin(),
120                            priv->windows.constEnd(),
121                            [internalWindowId](PlasmaWindow* window) {
122                                return window->d_ptr->windowId == internalWindowId;
123                            });
124 
125     if (it == priv->windows.constEnd()) {
126         // Create a temp window just for the resource and directly send unmapped.
127         auto window = std::unique_ptr<PlasmaWindow>(new PlasmaWindow(priv->handle()));
128         window->d_ptr->createResource(bind->version(), id, bind->client(), true);
129         return;
130     }
131     (*it)->d_ptr->createResource(bind->version(), id, bind->client(), false);
132 }
133 
setShowingDesktopState(ShowingDesktopState desktopState)134 void PlasmaWindowManager::setShowingDesktopState(ShowingDesktopState desktopState)
135 {
136     if (d_ptr->desktopState == desktopState) {
137         return;
138     }
139     d_ptr->desktopState = desktopState;
140     d_ptr->sendShowingDesktopState();
141 }
142 
createWindow(QObject * parent)143 PlasmaWindow* PlasmaWindowManager::createWindow(QObject* parent)
144 {
145     auto window = new PlasmaWindow(this, parent);
146 
147     // TODO(unknown author): improve window ids so that it cannot wrap around
148     window->d_ptr->windowId = ++d_ptr->windowIdCounter;
149 
150     d_ptr->send<org_kde_plasma_window_management_send_window>(window->d_ptr->windowId);
151 
152     d_ptr->windows << window;
153     connect(
154         window, &QObject::destroyed, this, [this, window] { d_ptr->windows.removeAll(window); });
155 
156     return window;
157 }
158 
windows() const159 QList<PlasmaWindow*> PlasmaWindowManager::windows() const
160 {
161     return d_ptr->windows;
162 }
163 
unmapWindow(PlasmaWindow * window)164 void PlasmaWindowManager::unmapWindow(PlasmaWindow* window)
165 {
166     if (!window) {
167         return;
168     }
169 
170     d_ptr->windows.removeOne(window);
171     Q_ASSERT(!d_ptr->windows.contains(window));
172 
173     window->d_ptr->unmap();
174     delete window;
175 }
176 
setVirtualDesktopManager(PlasmaVirtualDesktopManager * manager)177 void PlasmaWindowManager::setVirtualDesktopManager(PlasmaVirtualDesktopManager* manager)
178 {
179     d_ptr->virtualDesktopManager = manager;
180 }
181 
virtualDesktopManager() const182 PlasmaVirtualDesktopManager* PlasmaWindowManager::virtualDesktopManager() const
183 {
184     return d_ptr->virtualDesktopManager;
185 }
186 
187 /////////////////////////// Plasma Window ///////////////////////////
188 
Private(PlasmaWindowManager * manager,PlasmaWindow * q)189 PlasmaWindow::Private::Private(PlasmaWindowManager* manager, PlasmaWindow* q)
190     : manager(manager)
191     , q_ptr(q)
192 {
193 }
194 
~Private()195 PlasmaWindow::Private::~Private()
196 {
197     for (auto resource : resources) {
198         resource->unmap();
199     }
200 }
201 
createResource(uint32_t version,uint32_t id,Wayland::Client * client,bool temporary)202 void PlasmaWindow::Private::createResource(uint32_t version,
203                                            uint32_t id,
204                                            Wayland::Client* client,
205                                            bool temporary)
206 {
207     auto windowRes = new PlasmaWindowRes(client, version, id, temporary ? nullptr : q_ptr);
208     resources << windowRes;
209 
210     connect(windowRes, &PlasmaWindowRes::resourceDestroyed, q_ptr, [this, windowRes]() {
211         resources.removeOne(windowRes);
212     });
213 
214     windowRes->d_ptr->send<org_kde_plasma_window_send_virtual_desktop_changed>(m_virtualDesktop);
215     for (const auto& desk : plasmaVirtualDesktops) {
216         windowRes->d_ptr->send<org_kde_plasma_window_send_virtual_desktop_entered>(
217             desk.toUtf8().constData());
218     }
219     if (!m_appId.isEmpty()) {
220         windowRes->d_ptr->send<org_kde_plasma_window_send_app_id_changed>(
221             m_appId.toUtf8().constData());
222     }
223     if (m_pid != 0) {
224         windowRes->d_ptr->send<org_kde_plasma_window_send_pid_changed>(m_pid);
225     }
226     if (!m_title.isEmpty()) {
227         windowRes->d_ptr->send<org_kde_plasma_window_send_title_changed>(
228             m_title.toUtf8().constData());
229     }
230     if (!m_applicationMenu.serviceName.isEmpty() || !m_applicationMenu.objectPath.isEmpty()) {
231         windowRes->d_ptr->send<org_kde_plasma_window_send_application_menu>(
232             m_applicationMenu.serviceName.toLatin1().constData(),
233             m_applicationMenu.objectPath.toLatin1().constData());
234     }
235     windowRes->d_ptr->send<org_kde_plasma_window_send_state_changed>(m_desktopState);
236     if (!m_themedIconName.isEmpty()) {
237         windowRes->d_ptr->send<org_kde_plasma_window_send_themed_icon_name_changed>(
238             m_themedIconName.toUtf8().constData());
239     } else {
240         if (version >= ORG_KDE_PLASMA_WINDOW_ICON_CHANGED_SINCE_VERSION) {
241             windowRes->d_ptr->send<org_kde_plasma_window_send_icon_changed>();
242         }
243     }
244 
245     auto parentRes = getResourceOfParent(parentWindow, windowRes);
246     windowRes->d_ptr->send<org_kde_plasma_window_send_parent_window>(
247         parentRes ? parentRes->d_ptr->resource() : nullptr);
248 
249     if (temporary) {
250         windowRes->d_ptr->send<org_kde_plasma_window_send_unmapped>();
251     }
252 
253     if (geometry.isValid() && version >= ORG_KDE_PLASMA_WINDOW_GEOMETRY_SINCE_VERSION) {
254         windowRes->d_ptr->send<org_kde_plasma_window_send_geometry>(
255             geometry.x(), geometry.y(), geometry.width(), geometry.height());
256     }
257 
258     if (version >= ORG_KDE_PLASMA_WINDOW_INITIAL_STATE_SINCE_VERSION) {
259         windowRes->d_ptr->send<org_kde_plasma_window_send_initial_state>();
260     }
261     client->flush();
262 }
263 
setAppId(const QString & appId)264 void PlasmaWindow::Private::setAppId(const QString& appId)
265 {
266     if (m_appId == appId) {
267         return;
268     }
269     m_appId = appId;
270     const QByteArray utf8 = m_appId.toUtf8();
271     for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
272         (*it)->d_ptr->send<org_kde_plasma_window_send_app_id_changed>(utf8.constData());
273     }
274 }
275 
setPid(uint32_t pid)276 void PlasmaWindow::Private::setPid(uint32_t pid)
277 {
278     if (m_pid == pid) {
279         return;
280     }
281     m_pid = pid;
282     for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
283         (*it)->d_ptr->send<org_kde_plasma_window_send_pid_changed>(pid);
284     }
285 }
286 
setThemedIconName(const QString & iconName)287 void PlasmaWindow::Private::setThemedIconName(const QString& iconName)
288 {
289     if (m_themedIconName == iconName) {
290         return;
291     }
292     m_themedIconName = iconName;
293     const QByteArray utf8 = m_themedIconName.toUtf8();
294     for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
295         (*it)->d_ptr->send<org_kde_plasma_window_send_themed_icon_name_changed>(utf8.constData());
296     }
297 }
298 
setIcon(const QIcon & icon)299 void PlasmaWindow::Private::setIcon(const QIcon& icon)
300 {
301     m_icon = icon;
302     setThemedIconName(m_icon.name());
303     if (m_icon.name().isEmpty()) {
304         for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
305             if (wl_resource_get_version((*it)->d_ptr->resource())
306                 >= ORG_KDE_PLASMA_WINDOW_ICON_CHANGED_SINCE_VERSION) {
307                 (*it)->d_ptr->send<org_kde_plasma_window_send_icon_changed>();
308             }
309         }
310     }
311 }
312 
setTitle(const QString & title)313 void PlasmaWindow::Private::setTitle(const QString& title)
314 {
315     if (m_title == title) {
316         return;
317     }
318     m_title = title;
319     const QByteArray utf8 = m_title.toUtf8();
320     for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
321         (*it)->d_ptr->send<org_kde_plasma_window_send_title_changed>(utf8.constData());
322     }
323 }
324 
unmap() const325 void PlasmaWindow::Private::unmap() const
326 {
327     for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
328         (*it)->unmap();
329     }
330 }
331 
setState(org_kde_plasma_window_management_state flag,bool set)332 void PlasmaWindow::Private::setState(org_kde_plasma_window_management_state flag, bool set)
333 {
334     uint32_t newState = m_desktopState;
335     if (set) {
336         newState |= flag;
337     } else {
338         newState &= ~flag;
339     }
340     if (newState == m_desktopState) {
341         return;
342     }
343     m_desktopState = newState;
344     for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
345         (*it)->d_ptr->send<org_kde_plasma_window_send_state_changed>(m_desktopState);
346     }
347 }
348 
getResourceOfParent(PlasmaWindow * parent,PlasmaWindowRes * childRes)349 PlasmaWindowRes* PlasmaWindow::Private::getResourceOfParent(PlasmaWindow* parent,
350                                                             PlasmaWindowRes* childRes)
351 {
352     if (!parent) {
353         return nullptr;
354     }
355 
356     auto childClient = childRes->d_ptr->client();
357     auto it = std::find_if(parent->d_ptr->resources.begin(),
358                            parent->d_ptr->resources.end(),
359                            [childClient](PlasmaWindowRes* parentRes) {
360                                return parentRes->d_ptr->client() == childClient;
361                            });
362     return it != parent->d_ptr->resources.end() ? *it : nullptr;
363 }
364 
setParentWindow(PlasmaWindow * window)365 void PlasmaWindow::Private::setParentWindow(PlasmaWindow* window)
366 {
367     if (parentWindow == window) {
368         return;
369     }
370     QObject::disconnect(parentWindowDestroyConnection);
371     parentWindowDestroyConnection = QMetaObject::Connection();
372     parentWindow = window;
373     if (parentWindow) {
374         parentWindowDestroyConnection
375             = QObject::connect(window, &QObject::destroyed, q_ptr, [this] {
376                   parentWindow = nullptr;
377                   parentWindowDestroyConnection = QMetaObject::Connection();
378                   for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
379                       (*it)->d_ptr->send<org_kde_plasma_window_send_parent_window>(nullptr);
380                   }
381               });
382     }
383     for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
384         auto parentRes = getResourceOfParent(window, *it);
385         (*it)->d_ptr->send<org_kde_plasma_window_send_parent_window>(
386             parentRes ? parentRes->d_ptr->resource() : nullptr);
387     }
388 }
389 
setGeometry(const QRect & geo)390 void PlasmaWindow::Private::setGeometry(const QRect& geo)
391 {
392     if (geometry == geo) {
393         return;
394     }
395     geometry = geo;
396     if (!geometry.isValid()) {
397         return;
398     }
399     for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
400         auto resource = (*it)->d_ptr->resource();
401         if (wl_resource_get_version(resource) < ORG_KDE_PLASMA_WINDOW_GEOMETRY_SINCE_VERSION) {
402             continue;
403         }
404         (*it)->d_ptr->send<org_kde_plasma_window_send_geometry>(
405             geometry.x(), geometry.y(), geometry.width(), geometry.height());
406     }
407 }
408 
setApplicationMenuPaths(const QString & serviceName,const QString & objectPath)409 void PlasmaWindow::Private::setApplicationMenuPaths(const QString& serviceName,
410                                                     const QString& objectPath)
411 {
412     if (m_applicationMenu.serviceName == serviceName
413         && m_applicationMenu.objectPath == objectPath) {
414         return;
415     }
416     auto const service_name = serviceName.toLatin1();
417     auto const object_path = objectPath.toLatin1();
418 
419     m_applicationMenu.serviceName = serviceName;
420     m_applicationMenu.objectPath = objectPath;
421 
422     for (auto resource : qAsConst(resources)) {
423         resource->d_ptr->send<org_kde_plasma_window_send_application_menu,
424                               ORG_KDE_PLASMA_WINDOW_APPLICATION_MENU_SINCE_VERSION>(
425             service_name.data(), object_path.data());
426     }
427 }
428 
PlasmaWindow(PlasmaWindowManager * manager,QObject * parent)429 PlasmaWindow::PlasmaWindow(PlasmaWindowManager* manager, QObject* parent)
430     : QObject(parent)
431     , d_ptr(new Private(manager, this))
432 {
433 }
434 
435 PlasmaWindow::~PlasmaWindow() = default;
436 
setAppId(const QString & appId)437 void PlasmaWindow::setAppId(const QString& appId)
438 {
439     d_ptr->setAppId(appId);
440 }
441 
setPid(uint32_t pid)442 void PlasmaWindow::setPid(uint32_t pid)
443 {
444     d_ptr->setPid(pid);
445 }
446 
setTitle(const QString & title)447 void PlasmaWindow::setTitle(const QString& title)
448 {
449     d_ptr->setTitle(title);
450 }
451 
unmap()452 void PlasmaWindow::unmap()
453 {
454     d_ptr->manager->unmapWindow(this);
455 }
456 
minimizedGeometries() const457 QHash<Surface*, QRect> PlasmaWindow::minimizedGeometries() const
458 {
459     return d_ptr->minimizedGeometries;
460 }
461 
setActive(bool set)462 void PlasmaWindow::setActive(bool set)
463 {
464     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE, set);
465 }
466 
setFullscreen(bool set)467 void PlasmaWindow::setFullscreen(bool set)
468 {
469     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN, set);
470 }
471 
setKeepAbove(bool set)472 void PlasmaWindow::setKeepAbove(bool set)
473 {
474     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE, set);
475 }
476 
setKeepBelow(bool set)477 void PlasmaWindow::setKeepBelow(bool set)
478 {
479     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, set);
480 }
481 
setMaximized(bool set)482 void PlasmaWindow::setMaximized(bool set)
483 {
484     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, set);
485 }
486 
setMinimized(bool set)487 void PlasmaWindow::setMinimized(bool set)
488 {
489     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, set);
490 }
491 
setOnAllDesktops(bool set)492 void PlasmaWindow::setOnAllDesktops(bool set)
493 {
494     // the deprecated vd management
495     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS, set);
496 
497     if (!d_ptr->manager->virtualDesktopManager()) {
498         return;
499     }
500 
501     // the current vd management
502     if (set) {
503         if (d_ptr->plasmaVirtualDesktops.isEmpty()) {
504             return;
505         }
506         // leaving everything means on all desktops
507         auto const desktops = plasmaVirtualDesktops();
508         for (auto const& desk : desktops) {
509             for (auto it = d_ptr->resources.constBegin(); it != d_ptr->resources.constEnd(); ++it) {
510                 (*it)->d_ptr->send<org_kde_plasma_window_send_virtual_desktop_left>(
511                     desk.toUtf8().constData());
512             }
513         }
514         d_ptr->plasmaVirtualDesktops.clear();
515     } else {
516         if (!d_ptr->plasmaVirtualDesktops.isEmpty()) {
517             return;
518         }
519         // enters the desktops which are active (usually only one  but not a given)
520         for (auto desk : d_ptr->manager->virtualDesktopManager()->desktops()) {
521             if (desk->active() && !d_ptr->plasmaVirtualDesktops.contains(desk->id())) {
522                 d_ptr->plasmaVirtualDesktops << desk->id();
523                 for (auto it = d_ptr->resources.constBegin(); it != d_ptr->resources.constEnd();
524                      ++it) {
525                     (*it)->d_ptr->send<org_kde_plasma_window_send_virtual_desktop_entered>(
526                         desk->id().toUtf8().constData());
527                 }
528             }
529         }
530     }
531 }
532 
setDemandsAttention(bool set)533 void PlasmaWindow::setDemandsAttention(bool set)
534 {
535     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION, set);
536 }
537 
setCloseable(bool set)538 void PlasmaWindow::setCloseable(bool set)
539 {
540     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE, set);
541 }
542 
setFullscreenable(bool set)543 void PlasmaWindow::setFullscreenable(bool set)
544 {
545     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE, set);
546 }
547 
setMaximizeable(bool set)548 void PlasmaWindow::setMaximizeable(bool set)
549 {
550     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE, set);
551 }
552 
setMinimizeable(bool set)553 void PlasmaWindow::setMinimizeable(bool set)
554 {
555     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE, set);
556 }
557 
setSkipTaskbar(bool set)558 void PlasmaWindow::setSkipTaskbar(bool set)
559 {
560     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR, set);
561 }
562 
setSkipSwitcher(bool set)563 void PlasmaWindow::setSkipSwitcher(bool set)
564 {
565     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER, set);
566 }
567 
setIcon(const QIcon & icon)568 void PlasmaWindow::setIcon(const QIcon& icon)
569 {
570     d_ptr->setIcon(icon);
571 }
572 
addPlasmaVirtualDesktop(const QString & id)573 void PlasmaWindow::addPlasmaVirtualDesktop(const QString& id)
574 {
575     // don't add a desktop we're not sure it exists
576     if (!d_ptr->manager->virtualDesktopManager() || d_ptr->plasmaVirtualDesktops.contains(id)) {
577         return;
578     }
579 
580     PlasmaVirtualDesktop* desktop = d_ptr->manager->virtualDesktopManager()->desktop(id);
581 
582     if (!desktop) {
583         return;
584     }
585 
586     d_ptr->plasmaVirtualDesktops << id;
587 
588     // if the desktop dies, remove it from or list
589     connect(desktop, &QObject::destroyed, this, [this, id]() { removePlasmaVirtualDesktop(id); });
590 
591     for (auto it = d_ptr->resources.constBegin(); it != d_ptr->resources.constEnd(); ++it) {
592         (*it)->d_ptr->send<org_kde_plasma_window_send_virtual_desktop_entered>(
593             id.toUtf8().constData());
594     }
595 }
596 
removePlasmaVirtualDesktop(const QString & id)597 void PlasmaWindow::removePlasmaVirtualDesktop(const QString& id)
598 {
599     if (!d_ptr->plasmaVirtualDesktops.contains(id)) {
600         return;
601     }
602 
603     d_ptr->plasmaVirtualDesktops.removeAll(id);
604     for (auto it = d_ptr->resources.constBegin(); it != d_ptr->resources.constEnd(); ++it) {
605         (*it)->d_ptr->send<org_kde_plasma_window_send_virtual_desktop_left>(
606             id.toUtf8().constData());
607     }
608 
609     // we went on all desktops
610     if (d_ptr->plasmaVirtualDesktops.isEmpty()) {
611         setOnAllDesktops(true);
612     }
613 }
614 
plasmaVirtualDesktops() const615 QStringList PlasmaWindow::plasmaVirtualDesktops() const
616 {
617     return d_ptr->plasmaVirtualDesktops;
618 }
619 
setShadeable(bool set)620 void PlasmaWindow::setShadeable(bool set)
621 {
622     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE, set);
623 }
624 
setShaded(bool set)625 void PlasmaWindow::setShaded(bool set)
626 {
627     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, set);
628 }
629 
setMovable(bool set)630 void PlasmaWindow::setMovable(bool set)
631 {
632     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE, set);
633 }
634 
setResizable(bool set)635 void PlasmaWindow::setResizable(bool set)
636 {
637     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE, set);
638 }
639 
setApplicationMenuPaths(const QString & serviceName,const QString & objectPath) const640 void PlasmaWindow::setApplicationMenuPaths(const QString& serviceName,
641                                            const QString& objectPath) const
642 {
643     d_ptr->setApplicationMenuPaths(serviceName, objectPath);
644 }
645 
setVirtualDesktopChangeable(bool set)646 void PlasmaWindow::setVirtualDesktopChangeable(bool set)
647 {
648     d_ptr->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE, set);
649 }
650 
setParentWindow(PlasmaWindow * parentWindow)651 void PlasmaWindow::setParentWindow(PlasmaWindow* parentWindow)
652 {
653     d_ptr->setParentWindow(parentWindow);
654 }
655 
setGeometry(const QRect & geometry)656 void PlasmaWindow::setGeometry(const QRect& geometry)
657 {
658     d_ptr->setGeometry(geometry);
659 }
660 
661 /////////////////////////// Plasma Window Resource ///////////////////////////
662 
Private(Wayland::Client * client,uint32_t version,uint32_t id,PlasmaWindow * window,PlasmaWindowRes * q)663 PlasmaWindowRes::Private::Private(Wayland::Client* client,
664                                   uint32_t version,
665                                   uint32_t id,
666                                   PlasmaWindow* window,
667                                   PlasmaWindowRes* q)
668     : Wayland::Resource<PlasmaWindowRes>(client,
669                                          version,
670                                          id,
671                                          &org_kde_plasma_window_interface,
672                                          &s_interface,
673                                          q)
674     , window(window)
675 {
676 }
677 
678 const struct org_kde_plasma_window_interface PlasmaWindowRes::Private::s_interface = {
679     setStateCallback,
680     setVirtualDesktopCallback,
681     setMinimizedGeometryCallback,
682     unsetMinimizedGeometryCallback,
683     closeCallback,
684     requestMoveCallback,
685     requestResizeCallback,
686     destroyCallback,
687     getIconCallback,
688     requestEnterVirtualDesktopCallback,
689     requestEnterNewVirtualDesktopCallback,
690     requestLeaveVirtualDesktopCallback,
691 };
692 
getIconCallback(wl_client * wlClient,wl_resource * wlResource,int32_t fd)693 void PlasmaWindowRes::Private::getIconCallback([[maybe_unused]] wl_client* wlClient,
694                                                wl_resource* wlResource,
695                                                int32_t fd)
696 {
697     auto priv = handle(wlResource)->d_ptr;
698     if (!priv->window) {
699         return;
700     }
701     QtConcurrent::run(
702         [fd](const QIcon& icon) {
703             QFile file;
704             file.open(fd, QIODevice::WriteOnly, QFileDevice::AutoCloseHandle);
705             QDataStream ds(&file);
706             ds << icon;
707             file.close();
708         },
709         priv->window->d_ptr->m_icon);
710 }
711 
requestEnterVirtualDesktopCallback(wl_client * wlClient,wl_resource * wlResource,const char * id)712 void PlasmaWindowRes::Private::requestEnterVirtualDesktopCallback(
713     [[maybe_unused]] wl_client* wlClient,
714     wl_resource* wlResource,
715     const char* id)
716 {
717     auto priv = handle(wlResource)->d_ptr;
718     if (!priv->window) {
719         return;
720     }
721     Q_EMIT priv->window->enterPlasmaVirtualDesktopRequested(QString::fromUtf8(id));
722 }
723 
requestEnterNewVirtualDesktopCallback(wl_client * wlClient,wl_resource * wlResource)724 void PlasmaWindowRes::Private::requestEnterNewVirtualDesktopCallback(
725     [[maybe_unused]] wl_client* wlClient,
726     wl_resource* wlResource)
727 {
728     auto priv = handle(wlResource)->d_ptr;
729     if (!priv->window) {
730         return;
731     }
732     Q_EMIT priv->window->enterNewPlasmaVirtualDesktopRequested();
733 }
734 
requestLeaveVirtualDesktopCallback(wl_client * wlClient,wl_resource * wlResource,const char * id)735 void PlasmaWindowRes::Private::requestLeaveVirtualDesktopCallback(
736     [[maybe_unused]] wl_client* wlClient,
737     wl_resource* wlResource,
738     const char* id)
739 {
740     auto priv = handle(wlResource)->d_ptr;
741     if (!priv->window) {
742         return;
743     }
744     Q_EMIT priv->window->leavePlasmaVirtualDesktopRequested(QString::fromUtf8(id));
745 }
746 
closeCallback(wl_client * wlClient,wl_resource * wlResource)747 void PlasmaWindowRes::Private::closeCallback([[maybe_unused]] wl_client* wlClient,
748                                              wl_resource* wlResource)
749 {
750     auto priv = handle(wlResource)->d_ptr;
751     if (!priv->window) {
752         return;
753     }
754     Q_EMIT priv->window->closeRequested();
755 }
756 
requestMoveCallback(wl_client * wlClient,wl_resource * wlResource)757 void PlasmaWindowRes::Private::requestMoveCallback([[maybe_unused]] wl_client* wlClient,
758                                                    wl_resource* wlResource)
759 {
760     auto priv = handle(wlResource)->d_ptr;
761     if (!priv->window) {
762         return;
763     }
764     Q_EMIT priv->window->moveRequested();
765 }
766 
requestResizeCallback(wl_client * wlClient,wl_resource * wlResource)767 void PlasmaWindowRes::Private::requestResizeCallback([[maybe_unused]] wl_client* wlClient,
768                                                      wl_resource* wlResource)
769 {
770     auto priv = handle(wlResource)->d_ptr;
771     if (!priv->window) {
772         return;
773     }
774     Q_EMIT priv->window->resizeRequested();
775 }
776 
setVirtualDesktopCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t number)777 void PlasmaWindowRes::Private::setVirtualDesktopCallback([[maybe_unused]] wl_client* wlClient,
778                                                          [[maybe_unused]] wl_resource* wlResource,
779                                                          [[maybe_unused]] uint32_t number)
780 {
781 }
782 
setStateCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t flags,uint32_t desktopState)783 void PlasmaWindowRes::Private::setStateCallback([[maybe_unused]] wl_client* wlClient,
784                                                 wl_resource* wlResource,
785                                                 uint32_t flags,
786                                                 uint32_t desktopState)
787 {
788     auto window = handle(wlResource)->d_ptr->window;
789     if (!window) {
790         return;
791     }
792 
793     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE) {
794         Q_EMIT window->activeRequested(desktopState
795                                        & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE);
796     }
797 
798     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED) {
799         Q_EMIT window->minimizedRequested(desktopState
800                                           & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED);
801     }
802 
803     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED) {
804         Q_EMIT window->maximizedRequested(desktopState
805                                           & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED);
806     }
807 
808     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN) {
809         Q_EMIT window->fullscreenRequested(desktopState
810                                            & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN);
811     }
812 
813     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE) {
814         Q_EMIT window->keepAboveRequested(desktopState
815                                           & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE);
816     }
817 
818     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW) {
819         Q_EMIT window->keepBelowRequested(desktopState
820                                           & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW);
821     }
822 
823     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION) {
824         Q_EMIT window->demandsAttentionRequested(
825             desktopState & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION);
826     }
827 
828     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE) {
829         Q_EMIT window->closeableRequested(desktopState
830                                           & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE);
831     }
832 
833     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE) {
834         Q_EMIT window->minimizeableRequested(desktopState
835                                              & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE);
836     }
837 
838     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE) {
839         Q_EMIT window->maximizeableRequested(desktopState
840                                              & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE);
841     }
842 
843     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE) {
844         Q_EMIT window->fullscreenableRequested(
845             desktopState & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE);
846     }
847 
848     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR) {
849         Q_EMIT window->skipTaskbarRequested(desktopState
850                                             & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR);
851     }
852 
853     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER) {
854         Q_EMIT window->skipSwitcherRequested(desktopState
855                                              & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER);
856     }
857 
858     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE) {
859         Q_EMIT window->shadeableRequested(desktopState
860                                           & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE);
861     }
862 
863     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED) {
864         Q_EMIT window->shadedRequested(desktopState
865                                        & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED);
866     }
867 
868     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE) {
869         Q_EMIT window->movableRequested(desktopState
870                                         & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE);
871     }
872 
873     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE) {
874         Q_EMIT window->resizableRequested(desktopState
875                                           & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE);
876     }
877 
878     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE) {
879         Q_EMIT window->virtualDesktopChangeableRequested(
880             desktopState & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE);
881     }
882 }
883 
setMinimizedGeometryCallback(wl_client * wlClient,wl_resource * wlResource,wl_resource * wlPanel,uint32_t x,uint32_t y,uint32_t width,uint32_t height)884 void PlasmaWindowRes::Private::setMinimizedGeometryCallback([[maybe_unused]] wl_client* wlClient,
885                                                             wl_resource* wlResource,
886                                                             wl_resource* wlPanel,
887                                                             uint32_t x,
888                                                             uint32_t y,
889                                                             uint32_t width,
890                                                             uint32_t height)
891 {
892     auto priv = handle(wlResource)->d_ptr;
893     if (!priv->window) {
894         return;
895     }
896 
897     auto panel = Wayland::Resource<Surface>::handle(wlPanel);
898     auto const rect = QRect(static_cast<int>(x),
899                             static_cast<int>(y),
900                             static_cast<int>(width),
901                             static_cast<int>(height));
902 
903     if (priv->window->d_ptr->minimizedGeometries.value(panel) == rect) {
904         return;
905     }
906 
907     priv->window->d_ptr->minimizedGeometries[panel] = rect;
908     Q_EMIT priv->window->minimizedGeometriesChanged();
909     connect(panel, &Surface::resourceDestroyed, priv->handle(), [priv, panel]() {
910         if (priv->window && priv->window->d_ptr->minimizedGeometries.remove(panel)) {
911             Q_EMIT priv->window->minimizedGeometriesChanged();
912         }
913     });
914 }
915 
unsetMinimizedGeometryCallback(wl_client * wlClient,wl_resource * wlResource,wl_resource * wlPanel)916 void PlasmaWindowRes::Private::unsetMinimizedGeometryCallback([[maybe_unused]] wl_client* wlClient,
917                                                               wl_resource* wlResource,
918                                                               wl_resource* wlPanel)
919 {
920     auto priv = handle(wlResource)->d_ptr;
921     if (!priv->window) {
922         return;
923     }
924 
925     auto panel = Wayland::Resource<Surface>::handle(wlPanel);
926 
927     if (!panel) {
928         return;
929     }
930     if (!priv->window->d_ptr->minimizedGeometries.contains(panel)) {
931         return;
932     }
933     priv->window->d_ptr->minimizedGeometries.remove(panel);
934     Q_EMIT priv->window->minimizedGeometriesChanged();
935 }
936 
unmap()937 void PlasmaWindowRes::Private::unmap()
938 {
939     window = nullptr;
940     send<org_kde_plasma_window_send_unmapped>();
941     client()->flush();
942 }
943 
PlasmaWindowRes(Wayland::Client * client,uint32_t version,uint32_t id,PlasmaWindow * window)944 PlasmaWindowRes::PlasmaWindowRes(Wayland::Client* client,
945                                  uint32_t version,
946                                  uint32_t id,
947                                  PlasmaWindow* window)
948     : QObject(nullptr)
949     , d_ptr(new Private(client, version, id, window, this))
950 {
951 }
952 
unmap()953 void PlasmaWindowRes::unmap()
954 {
955     d_ptr->unmap();
956 }
957 
958 }
959