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

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