File indexing completed on 2024-11-10 04:57:37

0001 /*
0002     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "xdgshell.h"
0008 #include "xdgshell_p.h"
0009 
0010 #include "display.h"
0011 #include "output.h"
0012 #include "seat.h"
0013 #include "utils/common.h"
0014 #include "utils/resource.h"
0015 
0016 #include <QTimer>
0017 
0018 namespace KWin
0019 {
0020 static const int s_version = 6;
0021 
0022 XdgShellInterfacePrivate::XdgShellInterfacePrivate(XdgShellInterface *shell)
0023     : q(shell)
0024 {
0025 }
0026 
0027 XdgShellInterfacePrivate::Resource *XdgShellInterfacePrivate::resourceForXdgSurface(XdgSurfaceInterface *surface) const
0028 {
0029     return xdgSurfaces.value(surface);
0030 }
0031 
0032 void XdgShellInterfacePrivate::unregisterXdgSurface(XdgSurfaceInterface *surface)
0033 {
0034     xdgSurfaces.remove(surface);
0035 }
0036 
0037 /**
0038  * @todo Whether the ping is delayed or has timed out is out of domain of the XdgShellInterface.
0039  * Such matter must be handled somewhere else, e.g. XdgToplevelWindow, not here!
0040  */
0041 void XdgShellInterfacePrivate::registerPing(quint32 serial)
0042 {
0043     QTimer *timer = new QTimer(q);
0044     // we'll run the timer twice.
0045     timer->setInterval(pingTimeout / 2);
0046     QObject::connect(timer, &QTimer::timeout, q, [this, serial, attempt = 0]() mutable {
0047         ++attempt;
0048         if (attempt == 1) {
0049             Q_EMIT q->pingDelayed(serial);
0050             return;
0051         }
0052         Q_EMIT q->pingTimeout(serial);
0053         delete pings.take(serial);
0054     });
0055     pings.insert(serial, timer);
0056     timer->start();
0057 }
0058 
0059 XdgShellInterfacePrivate *XdgShellInterfacePrivate::get(XdgShellInterface *shell)
0060 {
0061     return shell->d.get();
0062 }
0063 
0064 void XdgShellInterfacePrivate::xdg_wm_base_destroy_resource(Resource *resource)
0065 {
0066     const QList<XdgSurfaceInterface *> surfaces = xdgSurfaces.keys(resource);
0067     qDeleteAll(surfaces);
0068 }
0069 
0070 void XdgShellInterfacePrivate::xdg_wm_base_destroy(Resource *resource)
0071 {
0072     if (xdgSurfaces.key(resource)) {
0073         wl_resource_post_error(resource->handle, error_defunct_surfaces, "xdg_wm_base was destroyed before children");
0074         return;
0075     }
0076     wl_resource_destroy(resource->handle);
0077 }
0078 
0079 void XdgShellInterfacePrivate::xdg_wm_base_create_positioner(Resource *resource, uint32_t id)
0080 {
0081     wl_resource *positionerResource = wl_resource_create(resource->client(), &xdg_positioner_interface, resource->version(), id);
0082     new XdgPositionerPrivate(positionerResource);
0083 }
0084 
0085 void XdgShellInterfacePrivate::xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, ::wl_resource *surfaceResource)
0086 {
0087     SurfaceInterface *surface = SurfaceInterface::get(surfaceResource);
0088 
0089     if (surface->buffer()) {
0090         wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface must not have a buffer at creation");
0091         return;
0092     }
0093 
0094     wl_resource *xdgSurfaceResource = wl_resource_create(resource->client(), &xdg_surface_interface, resource->version(), id);
0095 
0096     XdgSurfaceInterface *xdgSurface = new XdgSurfaceInterface(q, surface, xdgSurfaceResource);
0097     xdgSurfaces.insert(xdgSurface, resource);
0098 }
0099 
0100 void XdgShellInterfacePrivate::xdg_wm_base_pong(Resource *resource, uint32_t serial)
0101 {
0102     if (QTimer *timer = pings.take(serial)) {
0103         delete timer;
0104     }
0105     Q_EMIT q->pongReceived(serial);
0106 }
0107 
0108 XdgShellInterface::XdgShellInterface(Display *display, QObject *parent)
0109     : QObject(parent)
0110     , d(new XdgShellInterfacePrivate(this))
0111 {
0112     d->display = display;
0113     d->init(*display, s_version);
0114 }
0115 
0116 XdgShellInterface::~XdgShellInterface()
0117 {
0118 }
0119 
0120 Display *XdgShellInterface::display() const
0121 {
0122     return d->display;
0123 }
0124 
0125 quint32 XdgShellInterface::ping(XdgSurfaceInterface *surface)
0126 {
0127     XdgShellInterfacePrivate::Resource *clientResource = d->resourceForXdgSurface(surface);
0128     if (!clientResource)
0129         return 0;
0130 
0131     quint32 serial = d->display->nextSerial();
0132     d->send_ping(clientResource->handle, serial);
0133     d->registerPing(serial);
0134 
0135     return serial;
0136 }
0137 
0138 std::chrono::milliseconds XdgShellInterface::pingTimeoutInterval() const
0139 {
0140     return d->pingTimeout;
0141 }
0142 
0143 void XdgShellInterface::setPingTimeoutInterval(std::chrono::milliseconds pingTimeout)
0144 {
0145     d->pingTimeout = pingTimeout;
0146 }
0147 
0148 XdgSurfaceInterfacePrivate::XdgSurfaceInterfacePrivate(XdgSurfaceInterface *xdgSurface)
0149     : q(xdgSurface)
0150 {
0151 }
0152 
0153 void XdgSurfaceInterfacePrivate::apply(XdgSurfaceCommit *commit)
0154 {
0155     if (surface->buffer()) {
0156         firstBufferAttached = true;
0157     }
0158 
0159     if (commit->acknowledgedConfigure.has_value()) {
0160         Q_EMIT q->configureAcknowledged(commit->acknowledgedConfigure.value());
0161     }
0162 
0163     if (commit->windowGeometry.has_value()) {
0164         windowGeometry = commit->windowGeometry.value();
0165         Q_EMIT q->windowGeometryChanged(windowGeometry);
0166     }
0167 }
0168 
0169 void XdgSurfaceInterfacePrivate::reset()
0170 {
0171     firstBufferAttached = false;
0172     isConfigured = false;
0173     isInitialized = false;
0174     windowGeometry = QRect();
0175     Q_EMIT q->resetOccurred();
0176 }
0177 
0178 XdgSurfaceInterfacePrivate *XdgSurfaceInterfacePrivate::get(XdgSurfaceInterface *surface)
0179 {
0180     return surface->d.get();
0181 }
0182 
0183 void XdgSurfaceInterfacePrivate::xdg_surface_destroy_resource(Resource *resource)
0184 {
0185     delete q;
0186 }
0187 
0188 void XdgSurfaceInterfacePrivate::xdg_surface_destroy(Resource *resource)
0189 {
0190     if (toplevel || popup) {
0191         qWarning() << "Tried to destroy xdg_surface before its role object";
0192     }
0193     wl_resource_destroy(resource->handle);
0194 }
0195 
0196 void XdgSurfaceInterfacePrivate::xdg_surface_get_toplevel(Resource *resource, uint32_t id)
0197 {
0198     if (const SurfaceRole *role = surface->role()) {
0199         if (role != XdgToplevelInterface::role()) {
0200             wl_resource_post_error(resource->handle, error_already_constructed, "the surface already has a role assigned %s", role->name().constData());
0201             return;
0202         }
0203     } else {
0204         surface->setRole(XdgToplevelInterface::role());
0205     }
0206 
0207     wl_resource *toplevelResource = wl_resource_create(resource->client(), &xdg_toplevel_interface, resource->version(), id);
0208 
0209     auto toplevel = new XdgToplevelInterface(q, toplevelResource);
0210     Q_EMIT shell->toplevelCreated(toplevel);
0211 }
0212 
0213 void XdgSurfaceInterfacePrivate::xdg_surface_get_popup(Resource *resource, uint32_t id, ::wl_resource *parentResource, ::wl_resource *positionerResource)
0214 {
0215     if (const SurfaceRole *role = surface->role()) {
0216         if (role != XdgPopupInterface::role()) {
0217             wl_resource_post_error(resource->handle, error_already_constructed, "the surface already has a role assigned %s", role->name().constData());
0218             return;
0219         }
0220     } else {
0221         surface->setRole(XdgPopupInterface::role());
0222     }
0223 
0224     XdgPositioner positioner = XdgPositioner::get(positionerResource);
0225     if (!positioner.isComplete()) {
0226         auto shellPrivate = XdgShellInterfacePrivate::get(shell);
0227         wl_resource_post_error(shellPrivate->resourceForXdgSurface(q)->handle,
0228                                QtWaylandServer::xdg_wm_base::error_invalid_positioner,
0229                                "xdg_positioner is incomplete");
0230         return;
0231     }
0232 
0233     XdgSurfaceInterface *parentXdgSurface = XdgSurfaceInterface::get(parentResource);
0234     SurfaceInterface *parentSurface = nullptr;
0235     if (parentXdgSurface) {
0236         if (!parentXdgSurface->surface()->role()) {
0237             auto shellPrivate = XdgShellInterfacePrivate::get(shell);
0238             wl_resource_post_error(shellPrivate->resourceForXdgSurface(q)->handle,
0239                                    QtWaylandServer::xdg_wm_base::error_invalid_popup_parent,
0240                                    "parent surface has no surface role");
0241             return;
0242         }
0243         parentSurface = parentXdgSurface->surface();
0244     }
0245 
0246     wl_resource *popupResource = wl_resource_create(resource->client(), &xdg_popup_interface, resource->version(), id);
0247 
0248     auto popup = new XdgPopupInterface(q, parentSurface, positioner, popupResource);
0249     Q_EMIT shell->popupCreated(popup);
0250 }
0251 
0252 void XdgSurfaceInterfacePrivate::xdg_surface_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
0253 {
0254     if (Q_UNLIKELY(!pending)) {
0255         wl_resource_post_error(resource->handle, error_not_constructed, "xdg_surface must have a role");
0256         return;
0257     }
0258 
0259     if (width < 1 || height < 1) {
0260         wl_resource_post_error(resource->handle, -1, "invalid window geometry size (%dx%d)", width, height);
0261         return;
0262     }
0263 
0264     pending->windowGeometry = QRect(x, y, width, height);
0265 }
0266 
0267 void XdgSurfaceInterfacePrivate::xdg_surface_ack_configure(Resource *resource, uint32_t serial)
0268 {
0269     if (Q_UNLIKELY(!pending)) {
0270         wl_resource_post_error(resource->handle, error_not_constructed, "xdg_surface must have a role");
0271         return;
0272     }
0273 
0274     pending->acknowledgedConfigure = serial;
0275 }
0276 
0277 XdgSurfaceInterface::XdgSurfaceInterface(XdgShellInterface *shell, SurfaceInterface *surface, ::wl_resource *resource)
0278     : d(new XdgSurfaceInterfacePrivate(this))
0279 {
0280     d->shell = shell;
0281     d->surface = surface;
0282     d->init(resource);
0283 }
0284 
0285 XdgSurfaceInterface::~XdgSurfaceInterface()
0286 {
0287     delete d->toplevel;
0288     delete d->popup;
0289 
0290     Q_EMIT aboutToBeDestroyed();
0291     XdgShellInterfacePrivate::get(d->shell)->unregisterXdgSurface(this);
0292 }
0293 
0294 XdgToplevelInterface *XdgSurfaceInterface::toplevel() const
0295 {
0296     return d->toplevel;
0297 }
0298 
0299 XdgPopupInterface *XdgSurfaceInterface::popup() const
0300 {
0301     return d->popup;
0302 }
0303 
0304 XdgShellInterface *XdgSurfaceInterface::shell() const
0305 {
0306     return d->shell;
0307 }
0308 
0309 SurfaceInterface *XdgSurfaceInterface::surface() const
0310 {
0311     return d->surface;
0312 }
0313 
0314 bool XdgSurfaceInterface::isConfigured() const
0315 {
0316     return d->isConfigured;
0317 }
0318 
0319 QRect XdgSurfaceInterface::windowGeometry() const
0320 {
0321     return d->windowGeometry;
0322 }
0323 
0324 XdgSurfaceInterface *XdgSurfaceInterface::get(::wl_resource *resource)
0325 {
0326     if (auto surfacePrivate = resource_cast<XdgSurfaceInterfacePrivate *>(resource)) {
0327         return surfacePrivate->q;
0328     }
0329     return nullptr;
0330 }
0331 
0332 XdgToplevelInterfacePrivate::XdgToplevelInterfacePrivate(XdgToplevelInterface *toplevel, XdgSurfaceInterface *xdgSurface)
0333     : SurfaceExtension(xdgSurface->surface())
0334     , q(toplevel)
0335     , xdgSurface(xdgSurface)
0336 {
0337 }
0338 
0339 void XdgToplevelInterfacePrivate::apply(XdgToplevelCommit *commit)
0340 {
0341     auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
0342     if (xdgSurfacePrivate->firstBufferAttached && !xdgSurfacePrivate->surface->buffer()) {
0343         reset();
0344         return;
0345     }
0346 
0347     xdgSurfacePrivate->apply(commit);
0348 
0349     const auto minSize = commit->minimumSize.value_or(minimumSize);
0350     const auto maxSize = commit->maximumSize.value_or(maximumSize);
0351     if (!minSize.isEmpty() && !maxSize.isEmpty() && (minSize.width() > maxSize.width() || minSize.height() > maxSize.height())) {
0352         wl_resource_post_error(resource()->handle, error_invalid_size, "minimum size can't be bigger than the maximum");
0353         return;
0354     }
0355 
0356     if (commit->minimumSize && commit->minimumSize != minimumSize) {
0357         minimumSize = commit->minimumSize.value();
0358         Q_EMIT q->minimumSizeChanged(minimumSize);
0359     }
0360     if (commit->maximumSize && commit->maximumSize != maximumSize) {
0361         maximumSize = commit->maximumSize.value();
0362         Q_EMIT q->maximumSizeChanged(maximumSize);
0363     }
0364 
0365     if (!xdgSurfacePrivate->isInitialized) {
0366         Q_EMIT q->initializeRequested();
0367         xdgSurfacePrivate->isInitialized = true;
0368     }
0369 }
0370 
0371 void XdgToplevelInterfacePrivate::reset()
0372 {
0373     auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
0374     xdgSurfacePrivate->reset();
0375 
0376     windowTitle = QString();
0377     windowClass = QString();
0378     minimumSize = QSize();
0379     maximumSize = QSize();
0380     pending = XdgToplevelCommit{};
0381     stashed.clear();
0382 
0383     Q_EMIT q->resetOccurred();
0384 }
0385 
0386 void XdgToplevelInterfacePrivate::xdg_toplevel_destroy_resource(Resource *resource)
0387 {
0388     delete q;
0389 }
0390 
0391 void XdgToplevelInterfacePrivate::xdg_toplevel_destroy(Resource *resource)
0392 {
0393     wl_resource_destroy(resource->handle);
0394 }
0395 
0396 void XdgToplevelInterfacePrivate::xdg_toplevel_set_parent(Resource *resource, ::wl_resource *parentResource)
0397 {
0398     XdgToplevelInterface *parent = XdgToplevelInterface::get(parentResource);
0399     if (parentXdgToplevel == parent) {
0400         return;
0401     }
0402     parentXdgToplevel = parent;
0403     Q_EMIT q->parentXdgToplevelChanged();
0404 }
0405 
0406 void XdgToplevelInterfacePrivate::xdg_toplevel_set_title(Resource *resource, const QString &title)
0407 {
0408     if (windowTitle == title) {
0409         return;
0410     }
0411     windowTitle = title;
0412     Q_EMIT q->windowTitleChanged(title);
0413 }
0414 
0415 void XdgToplevelInterfacePrivate::xdg_toplevel_set_app_id(Resource *resource, const QString &app_id)
0416 {
0417     if (windowClass == app_id) {
0418         return;
0419     }
0420     windowClass = app_id;
0421     Q_EMIT q->windowClassChanged(app_id);
0422 }
0423 
0424 void XdgToplevelInterfacePrivate::xdg_toplevel_show_window_menu(Resource *resource, ::wl_resource *seatResource, uint32_t serial, int32_t x, int32_t y)
0425 {
0426     auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
0427 
0428     if (!xdgSurfacePrivate->isConfigured) {
0429         wl_resource_post_error(resource->handle, QtWaylandServer::xdg_surface::error_not_constructed, "surface has not been configured yet");
0430         return;
0431     }
0432 
0433     SeatInterface *seat = SeatInterface::get(seatResource);
0434     Q_EMIT q->windowMenuRequested(seat, QPoint(x, y), serial);
0435 }
0436 
0437 void XdgToplevelInterfacePrivate::xdg_toplevel_move(Resource *resource, ::wl_resource *seatResource, uint32_t serial)
0438 {
0439     auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
0440 
0441     if (!xdgSurfacePrivate->isConfigured) {
0442         wl_resource_post_error(resource->handle, QtWaylandServer::xdg_surface::error_not_constructed, "surface has not been configured yet");
0443         return;
0444     }
0445 
0446     SeatInterface *seat = SeatInterface::get(seatResource);
0447     Q_EMIT q->moveRequested(seat, serial);
0448 }
0449 
0450 void XdgToplevelInterfacePrivate::xdg_toplevel_resize(Resource *resource, ::wl_resource *seatResource, uint32_t serial, uint32_t xdgEdges)
0451 {
0452     auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
0453 
0454     if (!xdgSurfacePrivate->isConfigured) {
0455         wl_resource_post_error(resource->handle, QtWaylandServer::xdg_surface::error_not_constructed, "surface has not been configured yet");
0456         return;
0457     }
0458 
0459     SeatInterface *seat = SeatInterface::get(seatResource);
0460     Q_EMIT q->resizeRequested(seat, XdgToplevelInterface::ResizeAnchor(xdgEdges), serial);
0461 }
0462 
0463 void XdgToplevelInterfacePrivate::xdg_toplevel_set_max_size(Resource *resource, int32_t width, int32_t height)
0464 {
0465     if (width < 0 || height < 0) {
0466         wl_resource_post_error(resource->handle, error_invalid_size, "width and height must be positive or zero");
0467         return;
0468     }
0469     pending.maximumSize = QSize(width, height);
0470 }
0471 
0472 void XdgToplevelInterfacePrivate::xdg_toplevel_set_min_size(Resource *resource, int32_t width, int32_t height)
0473 {
0474     if (width < 0 || height < 0) {
0475         wl_resource_post_error(resource->handle, error_invalid_size, "width and height must be positive or zero");
0476         return;
0477     }
0478     pending.minimumSize = QSize(width, height);
0479 }
0480 
0481 void XdgToplevelInterfacePrivate::xdg_toplevel_set_maximized(Resource *resource)
0482 {
0483     Q_EMIT q->maximizeRequested();
0484 }
0485 
0486 void XdgToplevelInterfacePrivate::xdg_toplevel_unset_maximized(Resource *resource)
0487 {
0488     Q_EMIT q->unmaximizeRequested();
0489 }
0490 
0491 void XdgToplevelInterfacePrivate::xdg_toplevel_set_fullscreen(Resource *resource, ::wl_resource *outputResource)
0492 {
0493     OutputInterface *output = OutputInterface::get(outputResource);
0494     Q_EMIT q->fullscreenRequested(output);
0495 }
0496 
0497 void XdgToplevelInterfacePrivate::xdg_toplevel_unset_fullscreen(Resource *resource)
0498 {
0499     Q_EMIT q->unfullscreenRequested();
0500 }
0501 
0502 void XdgToplevelInterfacePrivate::xdg_toplevel_set_minimized(Resource *resource)
0503 {
0504     Q_EMIT q->minimizeRequested();
0505 }
0506 
0507 XdgToplevelInterfacePrivate *XdgToplevelInterfacePrivate::get(XdgToplevelInterface *toplevel)
0508 {
0509     return toplevel->d.get();
0510 }
0511 
0512 XdgToplevelInterfacePrivate *XdgToplevelInterfacePrivate::get(wl_resource *resource)
0513 {
0514     return resource_cast<XdgToplevelInterfacePrivate *>(resource);
0515 }
0516 
0517 XdgToplevelInterface::XdgToplevelInterface(XdgSurfaceInterface *xdgSurface, ::wl_resource *resource)
0518     : d(new XdgToplevelInterfacePrivate(this, xdgSurface))
0519 {
0520     XdgSurfaceInterfacePrivate *surfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
0521     surfacePrivate->toplevel = this;
0522     surfacePrivate->pending = &d->pending;
0523 
0524     d->init(resource);
0525 }
0526 
0527 XdgToplevelInterface::~XdgToplevelInterface()
0528 {
0529     Q_EMIT aboutToBeDestroyed();
0530 
0531     XdgSurfaceInterfacePrivate *surfacePrivate = XdgSurfaceInterfacePrivate::get(d->xdgSurface);
0532     surfacePrivate->toplevel = nullptr;
0533     surfacePrivate->pending = nullptr;
0534 }
0535 
0536 SurfaceRole *XdgToplevelInterface::role()
0537 {
0538     static SurfaceRole role(QByteArrayLiteral("xdg_toplevel"));
0539     return &role;
0540 }
0541 
0542 XdgShellInterface *XdgToplevelInterface::shell() const
0543 {
0544     return d->xdgSurface->shell();
0545 }
0546 
0547 XdgSurfaceInterface *XdgToplevelInterface::xdgSurface() const
0548 {
0549     return d->xdgSurface;
0550 }
0551 
0552 SurfaceInterface *XdgToplevelInterface::surface() const
0553 {
0554     return d->xdgSurface->surface();
0555 }
0556 
0557 bool XdgToplevelInterface::isConfigured() const
0558 {
0559     return d->xdgSurface->isConfigured();
0560 }
0561 
0562 XdgToplevelInterface *XdgToplevelInterface::parentXdgToplevel() const
0563 {
0564     return d->parentXdgToplevel;
0565 }
0566 
0567 QString XdgToplevelInterface::windowTitle() const
0568 {
0569     return d->windowTitle;
0570 }
0571 
0572 QString XdgToplevelInterface::windowClass() const
0573 {
0574     return d->windowClass;
0575 }
0576 
0577 QSize XdgToplevelInterface::minimumSize() const
0578 {
0579     return d->minimumSize.isEmpty() ? QSize(0, 0) : d->minimumSize;
0580 }
0581 
0582 QSize XdgToplevelInterface::maximumSize() const
0583 {
0584     return d->maximumSize.isEmpty() ? QSize(INT_MAX, INT_MAX) : d->maximumSize;
0585 }
0586 
0587 quint32 XdgToplevelInterface::sendConfigure(const QSize &size, const States &states)
0588 {
0589     // Note that the states listed in the configure event must be an array of uint32_t.
0590 
0591     uint32_t statesData[9] = {0};
0592     int i = 0;
0593 
0594     if (states & State::MaximizedHorizontal && states & State::MaximizedVertical) {
0595         statesData[i++] = QtWaylandServer::xdg_toplevel::state_maximized;
0596     }
0597     if (states & State::FullScreen) {
0598         statesData[i++] = QtWaylandServer::xdg_toplevel::state_fullscreen;
0599     }
0600     if (states & State::Resizing) {
0601         statesData[i++] = QtWaylandServer::xdg_toplevel::state_resizing;
0602     }
0603     if (states & State::Activated) {
0604         statesData[i++] = QtWaylandServer::xdg_toplevel::state_activated;
0605     }
0606 
0607     if (d->resource()->version() >= XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) {
0608         if (states & State::TiledLeft) {
0609             statesData[i++] = QtWaylandServer::xdg_toplevel::state_tiled_left;
0610         }
0611         if (states & State::TiledTop) {
0612             statesData[i++] = QtWaylandServer::xdg_toplevel::state_tiled_top;
0613         }
0614         if (states & State::TiledRight) {
0615             statesData[i++] = QtWaylandServer::xdg_toplevel::state_tiled_right;
0616         }
0617         if (states & State::TiledBottom) {
0618             statesData[i++] = QtWaylandServer::xdg_toplevel::state_tiled_bottom;
0619         }
0620     }
0621 
0622     if (d->resource()->version() >= XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION) {
0623         if (states & State::Suspended) {
0624             statesData[i++] = QtWaylandServer::xdg_toplevel::state_suspended;
0625         }
0626     }
0627 
0628     const QByteArray xdgStates = QByteArray::fromRawData(reinterpret_cast<char *>(statesData), sizeof(uint32_t) * i);
0629     const quint32 serial = xdgSurface()->shell()->display()->nextSerial();
0630 
0631     d->send_configure(size.width(), size.height(), xdgStates);
0632 
0633     auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface());
0634     xdgSurfacePrivate->send_configure(serial);
0635     xdgSurfacePrivate->isConfigured = true;
0636 
0637     return serial;
0638 }
0639 
0640 void XdgToplevelInterface::sendClose()
0641 {
0642     d->send_close();
0643 }
0644 
0645 void XdgToplevelInterface::sendConfigureBounds(const QSize &size)
0646 {
0647     if (d->resource()->version() >= XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION) {
0648         d->send_configure_bounds(size.width(), size.height());
0649     }
0650 }
0651 
0652 void XdgToplevelInterface::sendWmCapabilities(Capabilities capabilities)
0653 {
0654     if (d->resource()->version() < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) {
0655         return;
0656     }
0657     // Note that the capabilities listed in the event must be an array of uint32_t.
0658 
0659     uint32_t capabilitiesData[4] = {0};
0660     int i = 0;
0661 
0662     if (capabilities & Capability::WindowMenu) {
0663         capabilitiesData[i++] = QtWaylandServer::xdg_toplevel::wm_capabilities_window_menu;
0664     }
0665     if (capabilities & Capability::Maximize) {
0666         capabilitiesData[i++] = QtWaylandServer::xdg_toplevel::wm_capabilities_maximize;
0667     }
0668     if (capabilities & Capability::FullScreen) {
0669         capabilitiesData[i++] = QtWaylandServer::xdg_toplevel::wm_capabilities_fullscreen;
0670     }
0671     if (capabilities & Capability::Minimize) {
0672         capabilitiesData[i++] = QtWaylandServer::xdg_toplevel::wm_capabilities_minimize;
0673     }
0674 
0675     d->send_wm_capabilities(QByteArray::fromRawData(reinterpret_cast<char *>(capabilitiesData), sizeof(uint32_t) * i));
0676 }
0677 
0678 XdgToplevelInterface *XdgToplevelInterface::get(::wl_resource *resource)
0679 {
0680     if (auto toplevelPrivate = resource_cast<XdgToplevelInterfacePrivate *>(resource)) {
0681         return toplevelPrivate->q;
0682     }
0683     return nullptr;
0684 }
0685 
0686 XdgPopupInterfacePrivate *XdgPopupInterfacePrivate::get(XdgPopupInterface *popup)
0687 {
0688     return popup->d.get();
0689 }
0690 
0691 XdgPopupInterfacePrivate::XdgPopupInterfacePrivate(XdgPopupInterface *popup, XdgSurfaceInterface *xdgSurface)
0692     : SurfaceExtension(xdgSurface->surface())
0693     , q(popup)
0694     , xdgSurface(xdgSurface)
0695 {
0696 }
0697 
0698 void XdgPopupInterfacePrivate::apply(XdgPopupCommit *commit)
0699 {
0700     if (!parentSurface) {
0701         auto shellPrivate = XdgShellInterfacePrivate::get(xdgSurface->shell());
0702         wl_resource_post_error(shellPrivate->resourceForXdgSurface(xdgSurface)->handle,
0703                                QtWaylandServer::xdg_wm_base::error_invalid_popup_parent,
0704                                "no xdg_popup parent surface has been specified");
0705         return;
0706     }
0707 
0708     auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
0709     if (xdgSurfacePrivate->firstBufferAttached && !xdgSurfacePrivate->surface->buffer()) {
0710         reset();
0711         return;
0712     }
0713 
0714     xdgSurfacePrivate->apply(commit);
0715 
0716     if (!xdgSurfacePrivate->isInitialized) {
0717         Q_EMIT q->initializeRequested();
0718         xdgSurfacePrivate->isInitialized = true;
0719     }
0720 }
0721 
0722 void XdgPopupInterfacePrivate::reset()
0723 {
0724     auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
0725     pending = XdgPopupCommit{};
0726     stashed.clear();
0727     xdgSurfacePrivate->reset();
0728 }
0729 
0730 void XdgPopupInterfacePrivate::xdg_popup_destroy_resource(Resource *resource)
0731 {
0732     delete q;
0733 }
0734 
0735 void XdgPopupInterfacePrivate::xdg_popup_destroy(Resource *resource)
0736 {
0737     // TODO: We need to post an error with the code XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP if
0738     // this popup is not the topmost grabbing popup. We most likely need a grab abstraction or
0739     // something to determine whether the given popup has an explicit grab.
0740     wl_resource_destroy(resource->handle);
0741 }
0742 
0743 void XdgPopupInterfacePrivate::xdg_popup_grab(Resource *resource, ::wl_resource *seatHandle, uint32_t serial)
0744 {
0745     if (xdgSurface->surface()->buffer()) {
0746         wl_resource_post_error(resource->handle, error_invalid_grab, "xdg_surface is already mapped");
0747         return;
0748     }
0749     SeatInterface *seat = SeatInterface::get(seatHandle);
0750     Q_EMIT q->grabRequested(seat, serial);
0751 }
0752 
0753 void XdgPopupInterfacePrivate::xdg_popup_reposition(Resource *resource, ::wl_resource *positionerResource, uint32_t token)
0754 {
0755     positioner = XdgPositioner::get(positionerResource);
0756     Q_EMIT q->repositionRequested(token);
0757 }
0758 
0759 XdgPopupInterface::XdgPopupInterface(XdgSurfaceInterface *xdgSurface, SurfaceInterface *parentSurface, const XdgPositioner &positioner, ::wl_resource *resource)
0760     : d(new XdgPopupInterfacePrivate(this, xdgSurface))
0761 {
0762     XdgSurfaceInterfacePrivate *surfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
0763     surfacePrivate->popup = this;
0764     surfacePrivate->pending = &d->pending;
0765 
0766     d->parentSurface = parentSurface;
0767     d->positioner = positioner;
0768     d->init(resource);
0769 }
0770 
0771 XdgPopupInterface::~XdgPopupInterface()
0772 {
0773     Q_EMIT aboutToBeDestroyed();
0774 
0775     XdgSurfaceInterfacePrivate *surfacePrivate = XdgSurfaceInterfacePrivate::get(d->xdgSurface);
0776     surfacePrivate->popup = nullptr;
0777     surfacePrivate->pending = nullptr;
0778 }
0779 
0780 SurfaceRole *XdgPopupInterface::role()
0781 {
0782     static SurfaceRole role(QByteArrayLiteral("xdg_popup"));
0783     return &role;
0784 }
0785 
0786 SurfaceInterface *XdgPopupInterface::parentSurface() const
0787 {
0788     return d->parentSurface;
0789 }
0790 
0791 XdgSurfaceInterface *XdgPopupInterface::xdgSurface() const
0792 {
0793     return d->xdgSurface;
0794 }
0795 
0796 SurfaceInterface *XdgPopupInterface::surface() const
0797 {
0798     return d->xdgSurface->surface();
0799 }
0800 
0801 bool XdgPopupInterface::isConfigured() const
0802 {
0803     return d->xdgSurface->isConfigured();
0804 }
0805 
0806 XdgPositioner XdgPopupInterface::positioner() const
0807 {
0808     return d->positioner;
0809 }
0810 
0811 quint32 XdgPopupInterface::sendConfigure(const QRect &rect)
0812 {
0813     const quint32 serial = xdgSurface()->shell()->display()->nextSerial();
0814 
0815     d->send_configure(rect.x(), rect.y(), rect.width(), rect.height());
0816 
0817     auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface());
0818     xdgSurfacePrivate->send_configure(serial);
0819     xdgSurfacePrivate->isConfigured = true;
0820 
0821     return serial;
0822 }
0823 
0824 void XdgPopupInterface::sendPopupDone()
0825 {
0826     d->send_popup_done();
0827 }
0828 
0829 void XdgPopupInterface::sendRepositioned(quint32 token)
0830 {
0831     Q_ASSERT(d->resource()->version() >= XDG_POPUP_REPOSITIONED_SINCE_VERSION);
0832     d->send_repositioned(token);
0833 }
0834 
0835 XdgPopupInterface *XdgPopupInterface::get(::wl_resource *resource)
0836 {
0837     if (auto popupPrivate = resource_cast<XdgPopupInterfacePrivate *>(resource)) {
0838         return popupPrivate->q;
0839     }
0840     return nullptr;
0841 }
0842 
0843 XdgPositionerPrivate::XdgPositionerPrivate(::wl_resource *resource)
0844     : data(new XdgPositionerData)
0845 {
0846     init(resource);
0847 }
0848 
0849 XdgPositionerPrivate *XdgPositionerPrivate::get(wl_resource *resource)
0850 {
0851     return resource_cast<XdgPositionerPrivate *>(resource);
0852 }
0853 
0854 void XdgPositionerPrivate::xdg_positioner_destroy_resource(Resource *resource)
0855 {
0856     delete this;
0857 }
0858 
0859 void XdgPositionerPrivate::xdg_positioner_destroy(Resource *resource)
0860 {
0861     wl_resource_destroy(resource->handle);
0862 }
0863 
0864 void XdgPositionerPrivate::xdg_positioner_set_size(Resource *resource, int32_t width, int32_t height)
0865 {
0866     if (width < 1 || height < 1) {
0867         wl_resource_post_error(resource->handle, error_invalid_input, "width and height must be positive and non-zero");
0868         return;
0869     }
0870     data->size = QSize(width, height);
0871 }
0872 
0873 void XdgPositionerPrivate::xdg_positioner_set_anchor_rect(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
0874 {
0875     if (width < 1 || height < 1) {
0876         wl_resource_post_error(resource->handle, error_invalid_input, "width and height must be positive and non-zero");
0877         return;
0878     }
0879     data->anchorRect = QRect(x, y, width, height);
0880 }
0881 
0882 void XdgPositionerPrivate::xdg_positioner_set_anchor(Resource *resource, uint32_t anchor)
0883 {
0884     if (anchor > anchor_bottom_right) {
0885         wl_resource_post_error(resource->handle, error_invalid_input, "unknown anchor point");
0886         return;
0887     }
0888 
0889     switch (anchor) {
0890     case anchor_top:
0891         data->anchorEdges = Qt::TopEdge;
0892         break;
0893     case anchor_top_right:
0894         data->anchorEdges = Qt::TopEdge | Qt::RightEdge;
0895         break;
0896     case anchor_right:
0897         data->anchorEdges = Qt::RightEdge;
0898         break;
0899     case anchor_bottom_right:
0900         data->anchorEdges = Qt::BottomEdge | Qt::RightEdge;
0901         break;
0902     case anchor_bottom:
0903         data->anchorEdges = Qt::BottomEdge;
0904         break;
0905     case anchor_bottom_left:
0906         data->anchorEdges = Qt::BottomEdge | Qt::LeftEdge;
0907         break;
0908     case anchor_left:
0909         data->anchorEdges = Qt::LeftEdge;
0910         break;
0911     case anchor_top_left:
0912         data->anchorEdges = Qt::TopEdge | Qt::LeftEdge;
0913         break;
0914     default:
0915         data->anchorEdges = Qt::Edges();
0916         break;
0917     }
0918 }
0919 
0920 void XdgPositionerPrivate::xdg_positioner_set_parent_size(Resource *resource, int32_t width, int32_t height)
0921 {
0922     data->parentSize = QSize(width, height);
0923 }
0924 
0925 void XdgPositionerPrivate::xdg_positioner_set_reactive(Resource *resource)
0926 {
0927     data->isReactive = true;
0928 }
0929 
0930 void XdgPositionerPrivate::xdg_positioner_set_parent_configure(Resource *resource, uint32_t serial)
0931 {
0932     data->parentConfigure = serial;
0933 }
0934 
0935 void XdgPositionerPrivate::xdg_positioner_set_gravity(Resource *resource, uint32_t gravity)
0936 {
0937     if (gravity > gravity_bottom_right) {
0938         wl_resource_post_error(resource->handle, error_invalid_input, "unknown gravity direction");
0939         return;
0940     }
0941 
0942     switch (gravity) {
0943     case gravity_top:
0944         data->gravityEdges = Qt::TopEdge;
0945         break;
0946     case gravity_top_right:
0947         data->gravityEdges = Qt::TopEdge | Qt::RightEdge;
0948         break;
0949     case gravity_right:
0950         data->gravityEdges = Qt::RightEdge;
0951         break;
0952     case gravity_bottom_right:
0953         data->gravityEdges = Qt::BottomEdge | Qt::RightEdge;
0954         break;
0955     case gravity_bottom:
0956         data->gravityEdges = Qt::BottomEdge;
0957         break;
0958     case gravity_bottom_left:
0959         data->gravityEdges = Qt::BottomEdge | Qt::LeftEdge;
0960         break;
0961     case gravity_left:
0962         data->gravityEdges = Qt::LeftEdge;
0963         break;
0964     case gravity_top_left:
0965         data->gravityEdges = Qt::TopEdge | Qt::LeftEdge;
0966         break;
0967     default:
0968         data->gravityEdges = Qt::Edges();
0969         break;
0970     }
0971 }
0972 
0973 void XdgPositionerPrivate::xdg_positioner_set_constraint_adjustment(Resource *resource, uint32_t constraint_adjustment)
0974 {
0975     if (constraint_adjustment & constraint_adjustment_flip_x) {
0976         data->flipConstraintAdjustments |= Qt::Horizontal;
0977     } else {
0978         data->flipConstraintAdjustments &= ~Qt::Horizontal;
0979     }
0980 
0981     if (constraint_adjustment & constraint_adjustment_flip_y) {
0982         data->flipConstraintAdjustments |= Qt::Vertical;
0983     } else {
0984         data->flipConstraintAdjustments &= ~Qt::Vertical;
0985     }
0986 
0987     if (constraint_adjustment & constraint_adjustment_slide_x) {
0988         data->slideConstraintAdjustments |= Qt::Horizontal;
0989     } else {
0990         data->slideConstraintAdjustments &= ~Qt::Horizontal;
0991     }
0992 
0993     if (constraint_adjustment & constraint_adjustment_slide_y) {
0994         data->slideConstraintAdjustments |= Qt::Vertical;
0995     } else {
0996         data->slideConstraintAdjustments &= ~Qt::Vertical;
0997     }
0998 
0999     if (constraint_adjustment & constraint_adjustment_resize_x) {
1000         data->resizeConstraintAdjustments |= Qt::Horizontal;
1001     } else {
1002         data->resizeConstraintAdjustments &= ~Qt::Horizontal;
1003     }
1004 
1005     if (constraint_adjustment & constraint_adjustment_resize_y) {
1006         data->resizeConstraintAdjustments |= Qt::Vertical;
1007     } else {
1008         data->resizeConstraintAdjustments &= ~Qt::Vertical;
1009     }
1010 }
1011 
1012 void XdgPositionerPrivate::xdg_positioner_set_offset(Resource *resource, int32_t x, int32_t y)
1013 {
1014     data->offset = QPoint(x, y);
1015 }
1016 
1017 XdgPositioner::XdgPositioner()
1018     : d(new XdgPositionerData)
1019 {
1020 }
1021 
1022 XdgPositioner::XdgPositioner(const XdgPositioner &other)
1023     : d(other.d)
1024 {
1025 }
1026 
1027 XdgPositioner::~XdgPositioner()
1028 {
1029 }
1030 
1031 XdgPositioner &XdgPositioner::operator=(const XdgPositioner &other)
1032 {
1033     d = other.d;
1034     return *this;
1035 }
1036 
1037 bool XdgPositioner::isComplete() const
1038 {
1039     return d->size.isValid() && d->anchorRect.isValid();
1040 }
1041 
1042 QSize XdgPositioner::size() const
1043 {
1044     return d->size;
1045 }
1046 
1047 bool XdgPositioner::isReactive() const
1048 {
1049     return d->isReactive;
1050 }
1051 
1052 QRectF XdgPositioner::placement(const QRectF &bounds) const
1053 {
1054     // returns if a target is within the supplied bounds, optional edges argument states which side to check
1055     auto inBounds = [bounds](const QRectF &target, Qt::Edges edges = Qt::LeftEdge | Qt::RightEdge | Qt::TopEdge | Qt::BottomEdge) -> bool {
1056         if (edges & Qt::LeftEdge && target.left() < bounds.left()) {
1057             return false;
1058         }
1059         if (edges & Qt::TopEdge && target.top() < bounds.top()) {
1060             return false;
1061         }
1062         if (edges & Qt::RightEdge && target.right() > bounds.right()) {
1063             // normal QRect::right issue cancels out
1064             return false;
1065         }
1066         if (edges & Qt::BottomEdge && target.bottom() > bounds.bottom()) {
1067             return false;
1068         }
1069         return true;
1070     };
1071 
1072     QRectF popupRect(popupOffset(d->anchorRect, d->anchorEdges, d->gravityEdges, d->size) + d->offset, d->size);
1073 
1074     // if that fits, we don't need to do anything
1075     if (inBounds(popupRect)) {
1076         return popupRect;
1077     }
1078     // otherwise apply constraint adjustment per axis in order XDG Shell Popup states
1079 
1080     if (d->flipConstraintAdjustments & Qt::Horizontal) {
1081         if (!inBounds(popupRect, Qt::LeftEdge | Qt::RightEdge)) {
1082             // flip both edges (if either bit is set, XOR both)
1083             auto flippedAnchorEdge = d->anchorEdges;
1084             if (flippedAnchorEdge & (Qt::LeftEdge | Qt::RightEdge)) {
1085                 flippedAnchorEdge ^= (Qt::LeftEdge | Qt::RightEdge);
1086             }
1087             auto flippedGravity = d->gravityEdges;
1088             if (flippedGravity & (Qt::LeftEdge | Qt::RightEdge)) {
1089                 flippedGravity ^= (Qt::LeftEdge | Qt::RightEdge);
1090             }
1091             auto flippedPopupRect = QRectF(popupOffset(d->anchorRect, flippedAnchorEdge, flippedGravity, d->size) + d->offset, d->size);
1092 
1093             // if it still doesn't fit we should continue with the unflipped version
1094             if (inBounds(flippedPopupRect, Qt::LeftEdge | Qt::RightEdge)) {
1095                 popupRect.moveLeft(flippedPopupRect.left());
1096             }
1097         }
1098     }
1099     if (d->slideConstraintAdjustments & Qt::Horizontal) {
1100         if (!inBounds(popupRect, Qt::LeftEdge)) {
1101             popupRect.moveLeft(bounds.left());
1102         }
1103         if (!inBounds(popupRect, Qt::RightEdge)) {
1104             popupRect.moveRight(bounds.right());
1105         }
1106     }
1107     if (d->resizeConstraintAdjustments & Qt::Horizontal) {
1108         QRectF unconstrainedRect = popupRect;
1109 
1110         if (!inBounds(unconstrainedRect, Qt::LeftEdge)) {
1111             unconstrainedRect.setLeft(bounds.left());
1112         }
1113         if (!inBounds(unconstrainedRect, Qt::RightEdge)) {
1114             unconstrainedRect.setRight(bounds.right());
1115         }
1116 
1117         if (unconstrainedRect.isValid()) {
1118             popupRect = unconstrainedRect;
1119         }
1120     }
1121 
1122     if (d->flipConstraintAdjustments & Qt::Vertical) {
1123         if (!inBounds(popupRect, Qt::TopEdge | Qt::BottomEdge)) {
1124             // flip both edges (if either bit is set, XOR both)
1125             auto flippedAnchorEdge = d->anchorEdges;
1126             if (flippedAnchorEdge & (Qt::TopEdge | Qt::BottomEdge)) {
1127                 flippedAnchorEdge ^= (Qt::TopEdge | Qt::BottomEdge);
1128             }
1129             auto flippedGravity = d->gravityEdges;
1130             if (flippedGravity & (Qt::TopEdge | Qt::BottomEdge)) {
1131                 flippedGravity ^= (Qt::TopEdge | Qt::BottomEdge);
1132             }
1133             auto flippedPopupRect = QRectF(popupOffset(d->anchorRect, flippedAnchorEdge, flippedGravity, d->size) + d->offset, d->size);
1134 
1135             // if it still doesn't fit we should continue with the unflipped version
1136             if (inBounds(flippedPopupRect, Qt::TopEdge | Qt::BottomEdge)) {
1137                 popupRect.moveTop(flippedPopupRect.top());
1138             }
1139         }
1140     }
1141     if (d->slideConstraintAdjustments & Qt::Vertical) {
1142         if (!inBounds(popupRect, Qt::TopEdge)) {
1143             popupRect.moveTop(bounds.top());
1144         }
1145         if (!inBounds(popupRect, Qt::BottomEdge)) {
1146             popupRect.moveBottom(bounds.bottom());
1147         }
1148     }
1149     if (d->resizeConstraintAdjustments & Qt::Vertical) {
1150         QRectF unconstrainedRect = popupRect;
1151 
1152         if (!inBounds(unconstrainedRect, Qt::TopEdge)) {
1153             unconstrainedRect.setTop(bounds.top());
1154         }
1155         if (!inBounds(unconstrainedRect, Qt::BottomEdge)) {
1156             unconstrainedRect.setBottom(bounds.bottom());
1157         }
1158 
1159         if (unconstrainedRect.isValid()) {
1160             popupRect = unconstrainedRect;
1161         }
1162     }
1163 
1164     return popupRect;
1165 }
1166 
1167 XdgPositioner XdgPositioner::get(::wl_resource *resource)
1168 {
1169     XdgPositionerPrivate *xdgPositionerPrivate = XdgPositionerPrivate::get(resource);
1170     if (xdgPositionerPrivate)
1171         return XdgPositioner(xdgPositionerPrivate->data);
1172     return XdgPositioner();
1173 }
1174 
1175 XdgPositioner::XdgPositioner(const QSharedDataPointer<XdgPositionerData> &data)
1176     : d(data)
1177 {
1178 }
1179 
1180 } // namespace KWin
1181 
1182 #include "moc_xdgshell.cpp"