File indexing completed on 2024-05-19 16:35:24

0001 /*
0002     SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 #include "plasmawindowmanagement_interface.h"
0007 #include "display.h"
0008 #include "plasmavirtualdesktop_interface.h"
0009 #include "surface_interface.h"
0010 #include "utils/common.h"
0011 
0012 #include <QFile>
0013 #include <QHash>
0014 #include <QIcon>
0015 #include <QList>
0016 #include <QRect>
0017 #include <QUuid>
0018 #include <QVector>
0019 #include <QtConcurrentRun>
0020 
0021 #include <qwayland-server-plasma-window-management.h>
0022 
0023 namespace KWaylandServer
0024 {
0025 static const quint32 s_version = 16;
0026 static const quint32 s_activationVersion = 1;
0027 
0028 // any strings that come from user-defined sources
0029 // that could exceed the maximum wayland length (i.e xwayland clients)
0030 // need to be truncated to the maximumWaylandBufferSize
0031 static QString truncate(const QString &stringIn)
0032 {
0033     const int libwaylandMaxBufferSize = 4096;
0034     // Some parts of the buffer is used for metadata, so subtract 100 to be on the safe side.
0035     // Also, QString is in utf-16, which means that in the worst case each character will be
0036     // three bytes when converted to utf-8 (which is what libwayland uses), so divide by three.
0037     const int maxLength = libwaylandMaxBufferSize / 3 - 100;
0038     return stringIn.left(maxLength);
0039 }
0040 
0041 class PlasmaWindowManagementInterfacePrivate : public QtWaylandServer::org_kde_plasma_window_management
0042 {
0043 public:
0044     PlasmaWindowManagementInterfacePrivate(PlasmaWindowManagementInterface *_q, Display *display);
0045     void sendShowingDesktopState();
0046     void sendShowingDesktopState(wl_resource *resource);
0047     void sendStackingOrderChanged();
0048     void sendStackingOrderChanged(wl_resource *resource);
0049     void sendStackingOrderUuidsChanged();
0050     void sendStackingOrderUuidsChanged(wl_resource *resource);
0051 
0052     PlasmaWindowManagementInterface::ShowingDesktopState state = PlasmaWindowManagementInterface::ShowingDesktopState::Disabled;
0053     QList<PlasmaWindowInterface *> windows;
0054     QPointer<PlasmaVirtualDesktopManagementInterface> plasmaVirtualDesktopManagementInterface = nullptr;
0055     quint32 windowIdCounter = 0;
0056     QVector<quint32> stackingOrder;
0057     QVector<QString> stackingOrderUuids;
0058     PlasmaWindowManagementInterface *q;
0059 
0060 protected:
0061     void org_kde_plasma_window_management_bind_resource(Resource *resource) override;
0062     void org_kde_plasma_window_management_show_desktop(Resource *resource, uint32_t state) override;
0063     void org_kde_plasma_window_management_get_window(Resource *resource, uint32_t id, uint32_t internal_window_id) override;
0064     void org_kde_plasma_window_management_get_window_by_uuid(Resource *resource, uint32_t id, const QString &internal_window_uuid) override;
0065 };
0066 
0067 class PlasmaWindowInterfacePrivate : public QtWaylandServer::org_kde_plasma_window
0068 {
0069 public:
0070     PlasmaWindowInterfacePrivate(PlasmaWindowManagementInterface *wm, PlasmaWindowInterface *q);
0071     ~PlasmaWindowInterfacePrivate();
0072 
0073     void setTitle(const QString &title);
0074     void setAppId(const QString &appId);
0075     void setPid(quint32 pid);
0076     void setThemedIconName(const QString &iconName);
0077     void setIcon(const QIcon &icon);
0078     void unmap();
0079     void setState(org_kde_plasma_window_management_state flag, bool set);
0080     void setParentWindow(PlasmaWindowInterface *parent);
0081     void setGeometry(const QRect &geometry);
0082     void setApplicationMenuPaths(const QString &service, const QString &object);
0083     void setResourceName(const QString &resourceName);
0084     wl_resource *resourceForParent(PlasmaWindowInterface *parent, Resource *child) const;
0085 
0086     quint32 windowId = 0;
0087     QHash<SurfaceInterface *, QRect> minimizedGeometries;
0088     PlasmaWindowManagementInterface *wm;
0089 
0090     bool unmapped = false;
0091     PlasmaWindowInterface *parentWindow = nullptr;
0092     QMetaObject::Connection parentWindowDestroyConnection;
0093     QStringList plasmaVirtualDesktops;
0094     QStringList plasmaActivities;
0095     QRect geometry;
0096     PlasmaWindowInterface *q;
0097     QString m_title;
0098     QString m_appId;
0099     quint32 m_pid = 0;
0100     QString m_themedIconName;
0101     QString m_appServiceName;
0102     QString m_appObjectPath;
0103     QIcon m_icon;
0104     quint32 m_state = 0;
0105     QString uuid;
0106     QString m_resourceName;
0107 
0108 protected:
0109     void org_kde_plasma_window_bind_resource(Resource *resource) override;
0110     void org_kde_plasma_window_set_state(Resource *resource, uint32_t flags, uint32_t state) override;
0111     void org_kde_plasma_window_set_virtual_desktop(Resource *resource, uint32_t number) override;
0112     void org_kde_plasma_window_set_minimized_geometry(Resource *resource, wl_resource *panel, uint32_t x, uint32_t y, uint32_t width, uint32_t height) override;
0113     void org_kde_plasma_window_unset_minimized_geometry(Resource *resource, wl_resource *panel) override;
0114     void org_kde_plasma_window_close(Resource *resource) override;
0115     void org_kde_plasma_window_request_move(Resource *resource) override;
0116     void org_kde_plasma_window_request_resize(Resource *resource) override;
0117     void org_kde_plasma_window_destroy(Resource *resource) override;
0118     void org_kde_plasma_window_get_icon(Resource *resource, int32_t fd) override;
0119     void org_kde_plasma_window_request_enter_virtual_desktop(Resource *resource, const QString &id) override;
0120     void org_kde_plasma_window_request_enter_new_virtual_desktop(Resource *resource) override;
0121     void org_kde_plasma_window_request_leave_virtual_desktop(Resource *resource, const QString &id) override;
0122     void org_kde_plasma_window_request_enter_activity(Resource *resource, const QString &id) override;
0123     void org_kde_plasma_window_request_leave_activity(Resource *resource, const QString &id) override;
0124     void org_kde_plasma_window_send_to_output(Resource *resource, struct wl_resource *output) override;
0125 };
0126 
0127 PlasmaWindowManagementInterfacePrivate::PlasmaWindowManagementInterfacePrivate(PlasmaWindowManagementInterface *_q, Display *display)
0128     : QtWaylandServer::org_kde_plasma_window_management(*display, s_version)
0129     , q(_q)
0130 {
0131 }
0132 
0133 void PlasmaWindowManagementInterfacePrivate::sendShowingDesktopState()
0134 {
0135     const auto clientResources = resourceMap();
0136     for (auto resource : clientResources) {
0137         sendShowingDesktopState(resource->handle);
0138     }
0139 }
0140 
0141 void PlasmaWindowManagementInterfacePrivate::sendShowingDesktopState(wl_resource *r)
0142 {
0143     uint32_t s = 0;
0144     switch (state) {
0145     case PlasmaWindowManagementInterface::ShowingDesktopState::Enabled:
0146         s = QtWaylandServer::org_kde_plasma_window_management::show_desktop_enabled;
0147         break;
0148     case PlasmaWindowManagementInterface::ShowingDesktopState::Disabled:
0149         s = QtWaylandServer::org_kde_plasma_window_management::show_desktop_disabled;
0150         break;
0151     default:
0152         Q_UNREACHABLE();
0153         break;
0154     }
0155     send_show_desktop_changed(r, s);
0156 }
0157 
0158 void PlasmaWindowManagementInterfacePrivate::sendStackingOrderChanged()
0159 {
0160     const auto clientResources = resourceMap();
0161     for (auto resource : clientResources) {
0162         sendStackingOrderChanged(resource->handle);
0163     }
0164 }
0165 
0166 void PlasmaWindowManagementInterfacePrivate::sendStackingOrderChanged(wl_resource *r)
0167 {
0168     if (wl_resource_get_version(r) < ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STACKING_ORDER_CHANGED_SINCE_VERSION) {
0169         return;
0170     }
0171 
0172     send_stacking_order_changed(r, QByteArray::fromRawData(reinterpret_cast<const char *>(stackingOrder.constData()), sizeof(uint32_t) * stackingOrder.size()));
0173 }
0174 
0175 void PlasmaWindowManagementInterfacePrivate::sendStackingOrderUuidsChanged()
0176 {
0177     const auto clientResources = resourceMap();
0178     for (auto resource : clientResources) {
0179         sendStackingOrderUuidsChanged(resource->handle);
0180     }
0181 }
0182 
0183 void PlasmaWindowManagementInterfacePrivate::sendStackingOrderUuidsChanged(wl_resource *r)
0184 {
0185     if (wl_resource_get_version(r) < ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STACKING_ORDER_UUID_CHANGED_SINCE_VERSION) {
0186         return;
0187     }
0188 
0189     QString uuids;
0190     for (const auto &uuid : std::as_const(stackingOrderUuids)) {
0191         uuids += uuid;
0192         uuids += QLatin1Char(';');
0193     }
0194     // Remove the trailing ';', on the receiving side this is interpreted as an empty uuid.
0195     if (stackingOrderUuids.size() > 0) {
0196         uuids.remove(uuids.length() - 1, 1);
0197     }
0198     send_stacking_order_uuid_changed(r, uuids);
0199 }
0200 
0201 void PlasmaWindowManagementInterfacePrivate::org_kde_plasma_window_management_bind_resource(Resource *resource)
0202 {
0203     for (const auto window : std::as_const(windows)) {
0204         if (resource->version() >= ORG_KDE_PLASMA_WINDOW_MANAGEMENT_WINDOW_WITH_UUID_SINCE_VERSION) {
0205             send_window_with_uuid(resource->handle, window->d->windowId, window->d->uuid);
0206         } else {
0207             send_window(resource->handle, window->d->windowId);
0208         }
0209     }
0210     sendStackingOrderChanged(resource->handle);
0211     sendStackingOrderUuidsChanged(resource->handle);
0212 }
0213 
0214 void PlasmaWindowManagementInterfacePrivate::org_kde_plasma_window_management_show_desktop(Resource *resource, uint32_t state)
0215 {
0216     PlasmaWindowManagementInterface::ShowingDesktopState s = PlasmaWindowManagementInterface::ShowingDesktopState::Disabled;
0217     switch (state) {
0218     case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED:
0219         s = PlasmaWindowManagementInterface::ShowingDesktopState::Enabled;
0220         break;
0221     case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED:
0222     default:
0223         s = PlasmaWindowManagementInterface::ShowingDesktopState::Disabled;
0224         break;
0225     }
0226     Q_EMIT q->requestChangeShowingDesktop(s);
0227 }
0228 
0229 void PlasmaWindowManagementInterfacePrivate::org_kde_plasma_window_management_get_window(Resource *resource, uint32_t id, uint32_t internal_window_id)
0230 {
0231     for (const auto window : std::as_const(windows)) {
0232         if (window->d->windowId == internal_window_id) {
0233             window->d->add(resource->client(), id, resource->version());
0234             return;
0235         }
0236     }
0237     // create a temp window just for the resource, bind then immediately delete it, sending an unmap event
0238     PlasmaWindowInterface window(q, q);
0239     window.d->add(resource->client(), id, resource->version());
0240 }
0241 
0242 void PlasmaWindowManagementInterfacePrivate::org_kde_plasma_window_management_get_window_by_uuid(Resource *resource,
0243                                                                                                  uint32_t id,
0244                                                                                                  const QString &internal_window_uuid)
0245 {
0246     auto it = std::find_if(windows.constBegin(), windows.constEnd(), [internal_window_uuid](PlasmaWindowInterface *window) {
0247         return window->d->uuid == internal_window_uuid;
0248     });
0249     if (it == windows.constEnd()) {
0250         qCWarning(KWIN_CORE) << "Could not find window with uuid" << internal_window_uuid;
0251         // create a temp window just for the resource, bind then immediately delete it, sending an unmap event
0252         PlasmaWindowInterface window(q, q);
0253         window.d->add(resource->client(), id, resource->version());
0254         return;
0255     }
0256     (*it)->d->add(resource->client(), id, resource->version());
0257 }
0258 
0259 PlasmaWindowManagementInterface::PlasmaWindowManagementInterface(Display *display, QObject *parent)
0260     : QObject(parent)
0261     , d(new PlasmaWindowManagementInterfacePrivate(this, display))
0262 {
0263 }
0264 
0265 PlasmaWindowManagementInterface::~PlasmaWindowManagementInterface() = default;
0266 
0267 void PlasmaWindowManagementInterface::setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState state)
0268 {
0269     if (d->state == state) {
0270         return;
0271     }
0272     d->state = state;
0273     d->sendShowingDesktopState();
0274 }
0275 
0276 PlasmaWindowInterface *PlasmaWindowManagementInterface::createWindow(QObject *parent, const QUuid &uuid)
0277 {
0278     PlasmaWindowInterface *window = new PlasmaWindowInterface(this, parent);
0279 
0280     window->d->uuid = uuid.toString();
0281     window->d->windowId = ++d->windowIdCounter; // NOTE the window id is deprecated
0282 
0283     const auto clientResources = d->resourceMap();
0284     for (auto resource : clientResources) {
0285         if (resource->version() >= ORG_KDE_PLASMA_WINDOW_MANAGEMENT_WINDOW_WITH_UUID_SINCE_VERSION) {
0286             d->send_window_with_uuid(resource->handle, window->d->windowId, window->d->uuid);
0287         } else {
0288             d->send_window(resource->handle, window->d->windowId);
0289         }
0290     }
0291     d->windows << window;
0292     connect(window, &QObject::destroyed, this, [this, window] {
0293         d->windows.removeAll(window);
0294     });
0295     return window;
0296 }
0297 
0298 QList<PlasmaWindowInterface *> PlasmaWindowManagementInterface::windows() const
0299 {
0300     return d->windows;
0301 }
0302 
0303 void PlasmaWindowManagementInterface::setStackingOrder(const QVector<quint32> &stackingOrder)
0304 {
0305     if (d->stackingOrder == stackingOrder) {
0306         return;
0307     }
0308     d->stackingOrder = stackingOrder;
0309     d->sendStackingOrderChanged();
0310 }
0311 
0312 void PlasmaWindowManagementInterface::setStackingOrderUuids(const QVector<QString> &stackingOrderUuids)
0313 {
0314     if (d->stackingOrderUuids == stackingOrderUuids) {
0315         return;
0316     }
0317     d->stackingOrderUuids = stackingOrderUuids;
0318     d->sendStackingOrderUuidsChanged();
0319 }
0320 
0321 void PlasmaWindowManagementInterface::setPlasmaVirtualDesktopManagementInterface(PlasmaVirtualDesktopManagementInterface *manager)
0322 {
0323     if (d->plasmaVirtualDesktopManagementInterface == manager) {
0324         return;
0325     }
0326     d->plasmaVirtualDesktopManagementInterface = manager;
0327 }
0328 
0329 PlasmaVirtualDesktopManagementInterface *PlasmaWindowManagementInterface::plasmaVirtualDesktopManagementInterface() const
0330 {
0331     return d->plasmaVirtualDesktopManagementInterface;
0332 }
0333 
0334 //////PlasmaWindow
0335 PlasmaWindowInterfacePrivate::PlasmaWindowInterfacePrivate(PlasmaWindowManagementInterface *wm, PlasmaWindowInterface *q)
0336     : QtWaylandServer::org_kde_plasma_window()
0337     , wm(wm)
0338     , q(q)
0339 {
0340 }
0341 
0342 PlasmaWindowInterfacePrivate::~PlasmaWindowInterfacePrivate()
0343 {
0344     unmap();
0345 }
0346 
0347 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_destroy(Resource *resource)
0348 {
0349     wl_resource_destroy(resource->handle);
0350 }
0351 
0352 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_bind_resource(Resource *resource)
0353 {
0354     for (const auto &desk : std::as_const(plasmaVirtualDesktops)) {
0355         send_virtual_desktop_entered(resource->handle, desk);
0356     }
0357     for (const auto &activity : std::as_const(plasmaActivities)) {
0358         if (resource->version() >= ORG_KDE_PLASMA_WINDOW_ACTIVITY_ENTERED_SINCE_VERSION) {
0359             send_activity_entered(resource->handle, activity);
0360         }
0361     }
0362     if (!m_appId.isEmpty()) {
0363         send_app_id_changed(resource->handle, truncate(m_appId));
0364     }
0365     if (m_pid != 0) {
0366         send_pid_changed(resource->handle, m_pid);
0367     }
0368     if (!m_title.isEmpty()) {
0369         send_title_changed(resource->handle, truncate(m_title));
0370     }
0371     if (!m_appObjectPath.isEmpty() || !m_appServiceName.isEmpty()) {
0372         send_application_menu(resource->handle, m_appServiceName, m_appObjectPath);
0373     }
0374     send_state_changed(resource->handle, m_state);
0375     if (!m_themedIconName.isEmpty()) {
0376         send_themed_icon_name_changed(resource->handle, m_themedIconName);
0377     } else if (!m_icon.isNull()) {
0378         if (resource->version() >= ORG_KDE_PLASMA_WINDOW_ICON_CHANGED_SINCE_VERSION) {
0379             send_icon_changed(resource->handle);
0380         }
0381     }
0382 
0383     send_parent_window(resource->handle, resourceForParent(parentWindow, resource));
0384 
0385     if (geometry.isValid() && resource->version() >= ORG_KDE_PLASMA_WINDOW_GEOMETRY_SINCE_VERSION) {
0386         send_geometry(resource->handle, geometry.x(), geometry.y(), geometry.width(), geometry.height());
0387     }
0388 
0389     if (resource->version() >= ORG_KDE_PLASMA_WINDOW_INITIAL_STATE_SINCE_VERSION) {
0390         send_initial_state(resource->handle);
0391     }
0392     if (!m_resourceName.isEmpty()) {
0393         if (resource->version() >= ORG_KDE_PLASMA_WINDOW_RESOURCE_NAME_CHANGED_SINCE_VERSION) {
0394             send_resource_name_changed(resource->handle, m_resourceName);
0395         }
0396     }
0397 }
0398 
0399 void PlasmaWindowInterfacePrivate::setAppId(const QString &appId)
0400 {
0401     if (m_appId == appId) {
0402         return;
0403     }
0404 
0405     m_appId = appId;
0406     const auto clientResources = resourceMap();
0407 
0408     for (auto resource : clientResources) {
0409         send_app_id_changed(resource->handle, truncate(m_appId));
0410     }
0411 }
0412 
0413 void PlasmaWindowInterfacePrivate::setPid(quint32 pid)
0414 {
0415     if (m_pid == pid) {
0416         return;
0417     }
0418     m_pid = pid;
0419     const auto clientResources = resourceMap();
0420 
0421     for (auto resource : clientResources) {
0422         send_pid_changed(resource->handle, pid);
0423     }
0424 }
0425 
0426 void PlasmaWindowInterfacePrivate::setThemedIconName(const QString &iconName)
0427 {
0428     if (m_themedIconName == iconName) {
0429         return;
0430     }
0431     m_themedIconName = iconName;
0432     const auto clientResources = resourceMap();
0433 
0434     for (auto resource : clientResources) {
0435         send_themed_icon_name_changed(resource->handle, m_themedIconName);
0436     }
0437 }
0438 
0439 void PlasmaWindowInterfacePrivate::setIcon(const QIcon &icon)
0440 {
0441     m_icon = icon;
0442     setThemedIconName(m_icon.name());
0443 
0444     const auto clientResources = resourceMap();
0445     for (auto resource : clientResources) {
0446         if (resource->version() >= ORG_KDE_PLASMA_WINDOW_ICON_CHANGED_SINCE_VERSION) {
0447             send_icon_changed(resource->handle);
0448         }
0449     }
0450 }
0451 
0452 void PlasmaWindowInterfacePrivate::setResourceName(const QString &resourceName)
0453 {
0454     if (m_resourceName == resourceName) {
0455         return;
0456     }
0457     m_resourceName = resourceName;
0458 
0459     const auto clientResources = resourceMap();
0460     for (auto resource : clientResources) {
0461         if (resource->version() >= ORG_KDE_PLASMA_WINDOW_RESOURCE_NAME_CHANGED_SINCE_VERSION) {
0462             send_resource_name_changed(resource->handle, resourceName);
0463         }
0464     }
0465 }
0466 
0467 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_get_icon(Resource *resource, int32_t fd)
0468 {
0469     QtConcurrent::run(
0470         [fd](const QIcon &icon) {
0471             QFile file;
0472             file.open(fd, QIODevice::WriteOnly, QFileDevice::AutoCloseHandle);
0473             QDataStream ds(&file);
0474             ds << icon;
0475             file.close();
0476         },
0477         m_icon);
0478 }
0479 
0480 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_request_enter_virtual_desktop(Resource *resource, const QString &id)
0481 {
0482     Q_EMIT q->enterPlasmaVirtualDesktopRequested(id);
0483 }
0484 
0485 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_request_enter_new_virtual_desktop(Resource *resource)
0486 {
0487     Q_EMIT q->enterNewPlasmaVirtualDesktopRequested();
0488 }
0489 
0490 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_request_leave_virtual_desktop(Resource *resource, const QString &id)
0491 {
0492     Q_EMIT q->leavePlasmaVirtualDesktopRequested(id);
0493 }
0494 
0495 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_request_enter_activity(Resource *resource, const QString &id)
0496 {
0497     Q_EMIT q->enterPlasmaActivityRequested(id);
0498 }
0499 
0500 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_request_leave_activity(Resource *resource, const QString &id)
0501 {
0502     Q_EMIT q->leavePlasmaActivityRequested(id);
0503 }
0504 
0505 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_send_to_output(Resource *resource, struct wl_resource *output)
0506 {
0507     Q_EMIT q->sendToOutput(KWaylandServer::OutputInterface::get(output));
0508 }
0509 
0510 void PlasmaWindowInterfacePrivate::setTitle(const QString &title)
0511 {
0512     if (m_title == title) {
0513         return;
0514     }
0515     m_title = title;
0516     const auto clientResources = resourceMap();
0517 
0518     for (auto resource : clientResources) {
0519         send_title_changed(resource->handle, truncate(m_title));
0520     }
0521 }
0522 
0523 void PlasmaWindowInterfacePrivate::unmap()
0524 {
0525     if (unmapped) {
0526         return;
0527     }
0528     unmapped = true;
0529     const auto clientResources = resourceMap();
0530 
0531     for (auto resource : clientResources) {
0532         send_unmapped(resource->handle);
0533     }
0534 }
0535 
0536 void PlasmaWindowInterfacePrivate::setState(org_kde_plasma_window_management_state flag, bool set)
0537 {
0538     quint32 newState = m_state;
0539     if (set) {
0540         newState |= flag;
0541     } else {
0542         newState &= ~flag;
0543     }
0544     if (newState == m_state) {
0545         return;
0546     }
0547     m_state = newState;
0548     const auto clientResources = resourceMap();
0549 
0550     for (auto resource : clientResources) {
0551         send_state_changed(resource->handle, m_state);
0552     }
0553 }
0554 
0555 wl_resource *PlasmaWindowInterfacePrivate::resourceForParent(PlasmaWindowInterface *parent, Resource *child) const
0556 {
0557     if (!parent) {
0558         return nullptr;
0559     }
0560 
0561     const auto parentResource = parent->d->resourceMap();
0562 
0563     for (auto resource : parentResource) {
0564         if (child->client() == resource->client()) {
0565             return resource->handle;
0566         }
0567     }
0568     return nullptr;
0569 }
0570 
0571 void PlasmaWindowInterfacePrivate::setParentWindow(PlasmaWindowInterface *window)
0572 {
0573     if (parentWindow == window) {
0574         return;
0575     }
0576     QObject::disconnect(parentWindowDestroyConnection);
0577     parentWindowDestroyConnection = QMetaObject::Connection();
0578     parentWindow = window;
0579     if (parentWindow) {
0580         parentWindowDestroyConnection = QObject::connect(window, &QObject::destroyed, q, [this] {
0581             parentWindow = nullptr;
0582             parentWindowDestroyConnection = QMetaObject::Connection();
0583             const auto clientResources = resourceMap();
0584             for (auto resource : clientResources) {
0585                 send_parent_window(resource->handle, nullptr);
0586             }
0587         });
0588     }
0589     const auto clientResources = resourceMap();
0590     for (auto resource : clientResources) {
0591         send_parent_window(resource->handle, resourceForParent(window, resource));
0592     }
0593 }
0594 
0595 void PlasmaWindowInterfacePrivate::setGeometry(const QRect &geo)
0596 {
0597     if (geometry == geo) {
0598         return;
0599     }
0600     geometry = geo;
0601     if (!geometry.isValid()) {
0602         return;
0603     }
0604 
0605     const auto clientResources = resourceMap();
0606     for (auto resource : clientResources) {
0607         if (resource->version() < ORG_KDE_PLASMA_WINDOW_GEOMETRY_SINCE_VERSION) {
0608             continue;
0609         }
0610         send_geometry(resource->handle, geometry.x(), geometry.y(), geometry.width(), geometry.height());
0611     }
0612 }
0613 
0614 void PlasmaWindowInterfacePrivate::setApplicationMenuPaths(const QString &service, const QString &object)
0615 {
0616     if (m_appServiceName == service && m_appObjectPath == object) {
0617         return;
0618     }
0619     m_appServiceName = service;
0620     m_appObjectPath = object;
0621     const auto clientResources = resourceMap();
0622     for (auto resource : clientResources) {
0623         if (resource->version() < ORG_KDE_PLASMA_WINDOW_APPLICATION_MENU_SINCE_VERSION) {
0624             continue;
0625         }
0626         send_application_menu(resource->handle, service, object);
0627     }
0628 }
0629 
0630 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_close(Resource *resource)
0631 {
0632     Q_EMIT q->closeRequested();
0633 }
0634 
0635 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_request_move(Resource *resource)
0636 {
0637     Q_EMIT q->moveRequested();
0638 }
0639 
0640 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_request_resize(Resource *resource)
0641 {
0642     Q_EMIT q->resizeRequested();
0643 }
0644 
0645 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_set_virtual_desktop(Resource *resource, uint32_t number)
0646 {
0647     // This method is intentionally left blank.
0648 }
0649 
0650 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_set_state(Resource *resource, uint32_t flags, uint32_t state)
0651 {
0652     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE) {
0653         Q_EMIT q->activeRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE);
0654     }
0655     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED) {
0656         Q_EMIT q->minimizedRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED);
0657     }
0658     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED) {
0659         Q_EMIT q->maximizedRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED);
0660     }
0661     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN) {
0662         Q_EMIT q->fullscreenRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN);
0663     }
0664     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE) {
0665         Q_EMIT q->keepAboveRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE);
0666     }
0667     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW) {
0668         Q_EMIT q->keepBelowRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW);
0669     }
0670     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION) {
0671         Q_EMIT q->demandsAttentionRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION);
0672     }
0673     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE) {
0674         Q_EMIT q->closeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE);
0675     }
0676     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE) {
0677         Q_EMIT q->minimizeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE);
0678     }
0679     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE) {
0680         Q_EMIT q->maximizeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE);
0681     }
0682     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE) {
0683         Q_EMIT q->fullscreenableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE);
0684     }
0685     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR) {
0686         Q_EMIT q->skipTaskbarRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR);
0687     }
0688     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER) {
0689         Q_EMIT q->skipSwitcherRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER);
0690     }
0691     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE) {
0692         Q_EMIT q->shadeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE);
0693     }
0694     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED) {
0695         Q_EMIT q->shadedRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED);
0696     }
0697     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE) {
0698         Q_EMIT q->movableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE);
0699     }
0700     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE) {
0701         Q_EMIT q->resizableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE);
0702     }
0703     if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE) {
0704         Q_EMIT q->virtualDesktopChangeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE);
0705     }
0706 }
0707 
0708 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_set_minimized_geometry(Resource *resource,
0709                                                                                 wl_resource *panel,
0710                                                                                 uint32_t x,
0711                                                                                 uint32_t y,
0712                                                                                 uint32_t width,
0713                                                                                 uint32_t height)
0714 {
0715     SurfaceInterface *panelSurface = SurfaceInterface::get(panel);
0716 
0717     if (!panelSurface) {
0718         return;
0719     }
0720 
0721     if (minimizedGeometries.value(panelSurface) == QRect(x, y, width, height)) {
0722         return;
0723     }
0724 
0725     minimizedGeometries[panelSurface] = QRect(x, y, width, height);
0726     Q_EMIT q->minimizedGeometriesChanged();
0727     QObject::connect(panelSurface, &QObject::destroyed, q, [this, panelSurface]() {
0728         if (minimizedGeometries.remove(panelSurface)) {
0729             Q_EMIT q->minimizedGeometriesChanged();
0730         }
0731     });
0732 }
0733 
0734 void PlasmaWindowInterfacePrivate::org_kde_plasma_window_unset_minimized_geometry(Resource *resource, wl_resource *panel)
0735 {
0736     SurfaceInterface *panelSurface = SurfaceInterface::get(panel);
0737 
0738     if (!panelSurface) {
0739         return;
0740     }
0741     if (!minimizedGeometries.contains(panelSurface)) {
0742         return;
0743     }
0744     minimizedGeometries.remove(panelSurface);
0745     Q_EMIT q->minimizedGeometriesChanged();
0746 }
0747 
0748 PlasmaWindowInterface::PlasmaWindowInterface(PlasmaWindowManagementInterface *wm, QObject *parent)
0749     : QObject(parent)
0750     , d(new PlasmaWindowInterfacePrivate(wm, this))
0751 {
0752 }
0753 
0754 PlasmaWindowInterface::~PlasmaWindowInterface() = default;
0755 
0756 void PlasmaWindowInterface::setAppId(const QString &appId)
0757 {
0758     d->setAppId(appId);
0759 }
0760 
0761 void PlasmaWindowInterface::setPid(quint32 pid)
0762 {
0763     d->setPid(pid);
0764 }
0765 
0766 void PlasmaWindowInterface::setTitle(const QString &title)
0767 {
0768     d->setTitle(title);
0769 }
0770 
0771 void PlasmaWindowInterface::unmap()
0772 {
0773     d->unmap();
0774 }
0775 
0776 QHash<SurfaceInterface *, QRect> PlasmaWindowInterface::minimizedGeometries() const
0777 {
0778     return d->minimizedGeometries;
0779 }
0780 
0781 void PlasmaWindowInterface::setActive(bool set)
0782 {
0783     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE, set);
0784 }
0785 
0786 void PlasmaWindowInterface::setFullscreen(bool set)
0787 {
0788     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN, set);
0789 }
0790 
0791 void PlasmaWindowInterface::setKeepAbove(bool set)
0792 {
0793     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE, set);
0794 }
0795 
0796 void PlasmaWindowInterface::setKeepBelow(bool set)
0797 {
0798     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, set);
0799 }
0800 
0801 void PlasmaWindowInterface::setMaximized(bool set)
0802 {
0803     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, set);
0804 }
0805 
0806 void PlasmaWindowInterface::setMinimized(bool set)
0807 {
0808     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, set);
0809 }
0810 
0811 void PlasmaWindowInterface::setOnAllDesktops(bool set)
0812 {
0813     // the deprecated vd management
0814     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS, set);
0815 
0816     if (!d->wm->plasmaVirtualDesktopManagementInterface()) {
0817         return;
0818     }
0819     const auto clientResources = d->resourceMap();
0820     // the current vd management
0821     if (set) {
0822         if (d->plasmaVirtualDesktops.isEmpty()) {
0823             return;
0824         }
0825         // leaving everything means on all desktops
0826         for (auto desk : plasmaVirtualDesktops()) {
0827             for (auto resource : clientResources) {
0828                 d->send_virtual_desktop_left(resource->handle, desk);
0829             }
0830         }
0831         d->plasmaVirtualDesktops.clear();
0832     } else {
0833         if (!d->plasmaVirtualDesktops.isEmpty()) {
0834             return;
0835         }
0836         // enters the desktops which are active (usually only one  but not a given)
0837         const auto desktops = d->wm->plasmaVirtualDesktopManagementInterface()->desktops();
0838         for (const auto desktop : desktops) {
0839             if (desktop->isActive() && !d->plasmaVirtualDesktops.contains(desktop->id())) {
0840                 d->plasmaVirtualDesktops << desktop->id();
0841                 for (auto resource : clientResources) {
0842                     d->send_virtual_desktop_entered(resource->handle, desktop->id());
0843                 }
0844             }
0845         }
0846     }
0847 }
0848 
0849 void PlasmaWindowInterface::setDemandsAttention(bool set)
0850 {
0851     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION, set);
0852 }
0853 
0854 void PlasmaWindowInterface::setCloseable(bool set)
0855 {
0856     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE, set);
0857 }
0858 
0859 void PlasmaWindowInterface::setFullscreenable(bool set)
0860 {
0861     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE, set);
0862 }
0863 
0864 void PlasmaWindowInterface::setMaximizeable(bool set)
0865 {
0866     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE, set);
0867 }
0868 
0869 void PlasmaWindowInterface::setMinimizeable(bool set)
0870 {
0871     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE, set);
0872 }
0873 
0874 void PlasmaWindowInterface::setSkipTaskbar(bool set)
0875 {
0876     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR, set);
0877 }
0878 
0879 void PlasmaWindowInterface::setSkipSwitcher(bool skip)
0880 {
0881     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER, skip);
0882 }
0883 
0884 void PlasmaWindowInterface::setIcon(const QIcon &icon)
0885 {
0886     d->setIcon(icon);
0887 }
0888 
0889 void PlasmaWindowInterface::setResourceName(const QString &resourceName)
0890 {
0891     d->setResourceName(resourceName);
0892 }
0893 
0894 void PlasmaWindowInterface::addPlasmaVirtualDesktop(const QString &id)
0895 {
0896     // don't add a desktop we're not sure it exists
0897     if (!d->wm->plasmaVirtualDesktopManagementInterface() || d->plasmaVirtualDesktops.contains(id)) {
0898         return;
0899     }
0900 
0901     PlasmaVirtualDesktopInterface *desktop = d->wm->plasmaVirtualDesktopManagementInterface()->desktop(id);
0902 
0903     if (!desktop) {
0904         return;
0905     }
0906 
0907     d->plasmaVirtualDesktops << id;
0908 
0909     // if the desktop dies, remove it from or list
0910     connect(desktop, &QObject::destroyed, this, [this, id]() {
0911         removePlasmaVirtualDesktop(id);
0912     });
0913 
0914     const auto clientResources = d->resourceMap();
0915     for (auto resource : clientResources) {
0916         d->send_virtual_desktop_entered(resource->handle, id);
0917     }
0918 }
0919 
0920 void PlasmaWindowInterface::removePlasmaVirtualDesktop(const QString &id)
0921 {
0922     if (!d->plasmaVirtualDesktops.contains(id)) {
0923         return;
0924     }
0925 
0926     d->plasmaVirtualDesktops.removeAll(id);
0927     const auto clientResources = d->resourceMap();
0928     for (auto resource : clientResources) {
0929         d->send_virtual_desktop_left(resource->handle, id);
0930     }
0931 
0932     // we went on all desktops
0933     if (d->plasmaVirtualDesktops.isEmpty()) {
0934         setOnAllDesktops(true);
0935     }
0936 }
0937 
0938 QStringList PlasmaWindowInterface::plasmaVirtualDesktops() const
0939 {
0940     return d->plasmaVirtualDesktops;
0941 }
0942 
0943 void PlasmaWindowInterface::addPlasmaActivity(const QString &id)
0944 {
0945     if (d->plasmaActivities.contains(id)) {
0946         return;
0947     }
0948 
0949     d->plasmaActivities << id;
0950 
0951     const auto clientResources = d->resourceMap();
0952     for (auto resource : clientResources) {
0953         if (resource->version() >= ORG_KDE_PLASMA_WINDOW_ACTIVITY_ENTERED_SINCE_VERSION) {
0954             d->send_activity_entered(resource->handle, id);
0955         }
0956     }
0957 }
0958 
0959 void PlasmaWindowInterface::removePlasmaActivity(const QString &id)
0960 {
0961     if (!d->plasmaActivities.removeOne(id)) {
0962         return;
0963     }
0964 
0965     const auto clientResources = d->resourceMap();
0966     for (auto resource : clientResources) {
0967         if (resource->version() >= ORG_KDE_PLASMA_WINDOW_ACTIVITY_LEFT_SINCE_VERSION) {
0968             d->send_activity_left(resource->handle, id);
0969         }
0970     }
0971 }
0972 
0973 QStringList PlasmaWindowInterface::plasmaActivities() const
0974 {
0975     return d->plasmaActivities;
0976 }
0977 
0978 void PlasmaWindowInterface::setShadeable(bool set)
0979 {
0980     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE, set);
0981 }
0982 
0983 void PlasmaWindowInterface::setShaded(bool set)
0984 {
0985     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, set);
0986 }
0987 
0988 void PlasmaWindowInterface::setMovable(bool set)
0989 {
0990     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE, set);
0991 }
0992 
0993 void PlasmaWindowInterface::setResizable(bool set)
0994 {
0995     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE, set);
0996 }
0997 
0998 void PlasmaWindowInterface::setVirtualDesktopChangeable(bool set)
0999 {
1000     d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE, set);
1001 }
1002 
1003 void PlasmaWindowInterface::setParentWindow(PlasmaWindowInterface *parentWindow)
1004 {
1005     d->setParentWindow(parentWindow);
1006 }
1007 
1008 void PlasmaWindowInterface::setGeometry(const QRect &geometry)
1009 {
1010     d->setGeometry(geometry);
1011 }
1012 
1013 void PlasmaWindowInterface::setApplicationMenuPaths(const QString &serviceName, const QString &objectPath)
1014 {
1015     d->setApplicationMenuPaths(serviceName, objectPath);
1016 }
1017 
1018 quint32 PlasmaWindowInterface::internalId() const
1019 {
1020     return d->windowId;
1021 }
1022 
1023 QString PlasmaWindowInterface::uuid() const
1024 {
1025     return d->uuid;
1026 }
1027 
1028 class PlasmaWindowActivationFeedbackInterfacePrivate : public QtWaylandServer::org_kde_plasma_activation_feedback
1029 {
1030 public:
1031     explicit PlasmaWindowActivationFeedbackInterfacePrivate(Display *display);
1032 
1033 protected:
1034     void org_kde_plasma_activation_feedback_destroy(Resource *resource) override;
1035 };
1036 
1037 class PlasmaWindowActivationInterfacePrivate : public QtWaylandServer::org_kde_plasma_activation
1038 {
1039 public:
1040     explicit PlasmaWindowActivationInterfacePrivate(PlasmaWindowActivationInterface *q)
1041         : QtWaylandServer::org_kde_plasma_activation()
1042         , q(q)
1043     {
1044     }
1045 
1046     PlasmaWindowActivationInterface *const q;
1047 };
1048 
1049 PlasmaWindowActivationFeedbackInterfacePrivate::PlasmaWindowActivationFeedbackInterfacePrivate(Display *display)
1050     : QtWaylandServer::org_kde_plasma_activation_feedback(*display, s_activationVersion)
1051 {
1052 }
1053 
1054 void PlasmaWindowActivationFeedbackInterfacePrivate::org_kde_plasma_activation_feedback_destroy(Resource *resource)
1055 {
1056     wl_resource_destroy(resource->handle);
1057 }
1058 
1059 PlasmaWindowActivationFeedbackInterface::PlasmaWindowActivationFeedbackInterface(Display *display, QObject *parent)
1060     : QObject(parent)
1061     , d(new PlasmaWindowActivationFeedbackInterfacePrivate(display))
1062 {
1063 }
1064 
1065 PlasmaWindowActivationFeedbackInterface::~PlasmaWindowActivationFeedbackInterface()
1066 {
1067 }
1068 
1069 std::unique_ptr<PlasmaWindowActivationInterface> PlasmaWindowActivationFeedbackInterface::createActivation(const QString &appid)
1070 {
1071     std::unique_ptr<PlasmaWindowActivationInterface> activation(new PlasmaWindowActivationInterface());
1072     const auto resources = d->resourceMap();
1073     for (auto resource : resources) {
1074         auto activationResource = activation->d->add(resource->client(), resource->version());
1075         d->send_activation(resource->handle, activationResource->handle);
1076     }
1077     activation->sendAppId(appid);
1078     return activation;
1079 }
1080 
1081 PlasmaWindowActivationInterface::PlasmaWindowActivationInterface()
1082     : d(new PlasmaWindowActivationInterfacePrivate(this))
1083 {
1084 }
1085 
1086 PlasmaWindowActivationInterface::~PlasmaWindowActivationInterface()
1087 {
1088     const auto clientResources = d->resourceMap();
1089     for (auto resource : clientResources) {
1090         d->send_finished(resource->handle);
1091     }
1092 }
1093 
1094 void PlasmaWindowActivationInterface::sendAppId(const QString &appid)
1095 {
1096     const auto clientResources = d->resourceMap();
1097     for (auto resource : clientResources) {
1098         d->send_app_id(resource->handle, appid);
1099     }
1100 }
1101 
1102 }