| /**************************************************************************** |
| ** |
| ** Copyright (C) 2018 The Qt Company Ltd. |
| ** Contact: http://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtWaylandCompositor module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL3$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see http://www.qt.io/terms-conditions. For further |
| ** information use the contact form at http://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or later as published by the Free |
| ** Software Foundation and appearing in the file LICENSE.GPL included in |
| ** the packaging of this file. Please review the following information to |
| ** ensure the GNU General Public License version 2.0 requirements will be |
| ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qwaylandxdgshell.h" |
| #include "qwaylandxdgshell_p.h" |
| |
| #if QT_CONFIG(wayland_compositor_quick) |
| #include "qwaylandxdgshellintegration_p.h" |
| #endif |
| #include <QtWaylandCompositor/private/qwaylandutils_p.h> |
| |
| #include <QtWaylandCompositor/QWaylandCompositor> |
| #include <QtWaylandCompositor/QWaylandSeat> |
| #include <QtWaylandCompositor/QWaylandSurface> |
| #include <QtWaylandCompositor/QWaylandSurfaceRole> |
| #include <QtWaylandCompositor/QWaylandResource> |
| |
| #include <QtCore/QObject> |
| |
| #include <algorithm> |
| |
| QT_BEGIN_NAMESPACE |
| |
| QWaylandXdgShellPrivate::QWaylandXdgShellPrivate() |
| { |
| } |
| |
| void QWaylandXdgShellPrivate::ping(QtWaylandServer::xdg_wm_base::Resource *resource, uint32_t serial) |
| { |
| m_pings.insert(serial); |
| send_ping(resource->handle, serial); |
| } |
| |
| void QWaylandXdgShellPrivate::registerXdgSurface(QWaylandXdgSurface *xdgSurface) |
| { |
| m_xdgSurfaces.insert(xdgSurface->surface()->client()->client(), xdgSurface); |
| } |
| |
| void QWaylandXdgShellPrivate::unregisterXdgSurface(QWaylandXdgSurface *xdgSurface) |
| { |
| auto xdgSurfacePrivate = QWaylandXdgSurfacePrivate::get(xdgSurface); |
| if (!m_xdgSurfaces.remove(xdgSurfacePrivate->resource()->client(), xdgSurface)) |
| qWarning("%s Unexpected state. Can't find registered xdg surface\n", Q_FUNC_INFO); |
| } |
| |
| QWaylandXdgSurface *QWaylandXdgShellPrivate::xdgSurfaceFromSurface(QWaylandSurface *surface) |
| { |
| for (QWaylandXdgSurface *xdgSurface : qAsConst(m_xdgSurfaces)) { |
| if (surface == xdgSurface->surface()) |
| return xdgSurface; |
| } |
| return nullptr; |
| } |
| |
| void QWaylandXdgShellPrivate::xdg_wm_base_destroy(Resource *resource) |
| { |
| if (!m_xdgSurfaces.values(resource->client()).empty()) |
| wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES, |
| "xdg_shell was destroyed before children"); |
| |
| wl_resource_destroy(resource->handle); |
| } |
| |
| void QWaylandXdgShellPrivate::xdg_wm_base_create_positioner(QtWaylandServer::xdg_wm_base::Resource *resource, uint32_t id) |
| { |
| QWaylandResource positionerResource(wl_resource_create(resource->client(), &xdg_positioner_interface, |
| wl_resource_get_version(resource->handle), id)); |
| |
| new QWaylandXdgPositioner(positionerResource); |
| } |
| |
| void QWaylandXdgShellPrivate::xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, wl_resource *surfaceResource) |
| { |
| Q_Q(QWaylandXdgShell); |
| QWaylandSurface *surface = QWaylandSurface::fromResource(surfaceResource); |
| |
| if (surface->role() != nullptr) { |
| wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_ROLE, |
| "wl_surface@%d, already has role %s\n", |
| wl_resource_get_id(surface->resource()), |
| surface->role()->name().constData()); |
| return; |
| } |
| |
| if (surface->hasContent()) { |
| //TODO: According to the spec, this is a client error, but there's no appropriate error code |
| qWarning() << "get_xdg_surface requested on a xdg_surface with content"; |
| } |
| |
| QWaylandResource xdgSurfaceResource(wl_resource_create(resource->client(), &xdg_surface_interface, |
| wl_resource_get_version(resource->handle), id)); |
| |
| QWaylandXdgSurface *xdgSurface = new QWaylandXdgSurface(q, surface, xdgSurfaceResource); |
| |
| registerXdgSurface(xdgSurface); |
| emit q->xdgSurfaceCreated(xdgSurface); |
| } |
| |
| void QWaylandXdgShellPrivate::xdg_wm_base_pong(Resource *resource, uint32_t serial) |
| { |
| Q_UNUSED(resource); |
| Q_Q(QWaylandXdgShell); |
| if (m_pings.remove(serial)) |
| emit q->pong(serial); |
| else |
| qWarning("Received an unexpected pong!"); |
| } |
| |
| /*! |
| * \qmltype XdgShell |
| * \inqmlmodule QtWayland.Compositor |
| * \since 5.12 |
| * \brief Provides an extension for desktop-style user interfaces. |
| * |
| * The XdgShell extension provides a way to associate a XdgToplevel or XdgPopup |
| * with a regular Wayland surface. Using the XdgToplevel interface, the client |
| * can request that the surface is resized, moved, and so on. |
| * |
| * XdgShell corresponds to the Wayland interface, \c xdg_shell. |
| * |
| * To provide the functionality of the shell extension in a compositor, create |
| * an instance of the XdgShell component and add it to the list of extensions |
| * supported by the compositor: |
| * |
| * \qml \QtMinorVersion |
| * import QtWayland.Compositor 1.\1 |
| * |
| * WaylandCompositor { |
| * XdgShell { |
| * // ... |
| * } |
| * } |
| * \endqml |
| */ |
| |
| /*! |
| * \class QWaylandXdgShell |
| * \inmodule QtWaylandCompositor |
| * \since 5.12 |
| * \brief The QWaylandXdgShell class is an extension for desktop-style user interfaces. |
| * |
| * The QWaylandXdgShell extension provides a way to associate a QWaylandXdgToplevel or |
| * QWaylandXdgPopup with a regular Wayland surface. Using the QWaylandXdgToplevel interface, |
| * the client can request that the surface is resized, moved, and so on. |
| * |
| * QWaylandXdgShell corresponds to the Wayland interface, \c xdg_shell. |
| */ |
| |
| /*! |
| * Constructs a QWaylandXdgShell object. |
| */ |
| QWaylandXdgShell::QWaylandXdgShell() |
| : QWaylandShellTemplate<QWaylandXdgShell>(*new QWaylandXdgShellPrivate()) |
| { |
| } |
| |
| /*! |
| * Constructs a QWaylandXdgShell object for the provided \a compositor. |
| */ |
| QWaylandXdgShell::QWaylandXdgShell(QWaylandCompositor *compositor) |
| : QWaylandShellTemplate<QWaylandXdgShell>(compositor, *new QWaylandXdgShellPrivate()) |
| { |
| } |
| |
| /*! |
| * Initializes the shell extension. |
| */ |
| void QWaylandXdgShell::initialize() |
| { |
| Q_D(QWaylandXdgShell); |
| QWaylandShellTemplate::initialize(); |
| QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer()); |
| if (!compositor) { |
| qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandXdgShell"; |
| return; |
| } |
| d->init(compositor->display(), 1); |
| |
| handleSeatChanged(compositor->defaultSeat(), nullptr); |
| |
| connect(compositor, &QWaylandCompositor::defaultSeatChanged, |
| this, &QWaylandXdgShell::handleSeatChanged); |
| } |
| |
| /*! |
| * Returns the Wayland interface for the QWaylandXdgShell. |
| */ |
| const struct wl_interface *QWaylandXdgShell::interface() |
| { |
| return QWaylandXdgShellPrivate::interface(); |
| } |
| |
| QByteArray QWaylandXdgShell::interfaceName() |
| { |
| return QWaylandXdgShellPrivate::interfaceName(); |
| } |
| |
| /*! |
| * \qmlmethod void QtWaylandCompositor::XdgShell::ping(WaylandClient client) |
| * |
| * Sends a ping event to \a client. If the client replies to the event the |
| * \l pong signal will be emitted. |
| */ |
| |
| /*! |
| * Sends a ping event to \a client. If the client replies to the event the |
| * \l pong signal will be emitted. |
| */ |
| uint QWaylandXdgShell::ping(QWaylandClient *client) |
| { |
| Q_D(QWaylandXdgShell); |
| |
| QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer()); |
| Q_ASSERT(compositor); |
| |
| uint32_t serial = compositor->nextSerial(); |
| |
| QWaylandXdgShellPrivate::Resource *clientResource = d->resourceMap().value(client->client(), nullptr); |
| Q_ASSERT(clientResource); |
| |
| d->ping(clientResource, serial); |
| return serial; |
| } |
| |
| void QWaylandXdgShell::handleSeatChanged(QWaylandSeat *newSeat, QWaylandSeat *oldSeat) |
| { |
| if (oldSeat != nullptr) { |
| disconnect(oldSeat, &QWaylandSeat::keyboardFocusChanged, |
| this, &QWaylandXdgShell::handleFocusChanged); |
| } |
| |
| if (newSeat != nullptr) { |
| connect(newSeat, &QWaylandSeat::keyboardFocusChanged, |
| this, &QWaylandXdgShell::handleFocusChanged); |
| } |
| } |
| |
| void QWaylandXdgShell::handleFocusChanged(QWaylandSurface *newSurface, QWaylandSurface *oldSurface) |
| { |
| Q_D(QWaylandXdgShell); |
| |
| QWaylandXdgSurface *newXdgSurface = d->xdgSurfaceFromSurface(newSurface); |
| QWaylandXdgSurface *oldXdgSurface = d->xdgSurfaceFromSurface(oldSurface); |
| |
| if (newXdgSurface) |
| QWaylandXdgSurfacePrivate::get(newXdgSurface)->handleFocusReceived(); |
| |
| if (oldXdgSurface) |
| QWaylandXdgSurfacePrivate::get(oldXdgSurface)->handleFocusLost(); |
| } |
| |
| QWaylandXdgSurfacePrivate::QWaylandXdgSurfacePrivate() |
| { |
| } |
| |
| void QWaylandXdgSurfacePrivate::setWindowType(Qt::WindowType windowType) |
| { |
| if (m_windowType == windowType) |
| return; |
| |
| m_windowType = windowType; |
| |
| Q_Q(QWaylandXdgSurface); |
| emit q->windowTypeChanged(); |
| } |
| |
| void QWaylandXdgSurfacePrivate::handleFocusLost() |
| { |
| if (m_toplevel) |
| QWaylandXdgToplevelPrivate::get(m_toplevel)->handleFocusLost(); |
| } |
| |
| void QWaylandXdgSurfacePrivate::handleFocusReceived() |
| { |
| if (m_toplevel) |
| QWaylandXdgToplevelPrivate::get(m_toplevel)->handleFocusReceived(); |
| } |
| |
| QRect QWaylandXdgSurfacePrivate::calculateFallbackWindowGeometry() const |
| { |
| // TODO: The unset window geometry should include subsurfaces as well, so this solution |
| // won't work too well on those kinds of clients. |
| return QRect(QPoint(), m_surface->destinationSize()); |
| } |
| |
| void QWaylandXdgSurfacePrivate::updateFallbackWindowGeometry() |
| { |
| Q_Q(QWaylandXdgSurface); |
| if (!m_unsetWindowGeometry) |
| return; |
| |
| const QRect unsetGeometry = calculateFallbackWindowGeometry(); |
| if (unsetGeometry == m_windowGeometry) |
| return; |
| |
| m_windowGeometry = unsetGeometry; |
| emit q->windowGeometryChanged(); |
| } |
| |
| void QWaylandXdgSurfacePrivate::xdg_surface_destroy_resource(QtWaylandServer::xdg_surface::Resource *resource) |
| { |
| Q_UNUSED(resource); |
| Q_Q(QWaylandXdgSurface); |
| QWaylandXdgShellPrivate::get(m_xdgShell)->unregisterXdgSurface(q); |
| delete q; |
| } |
| |
| void QWaylandXdgSurfacePrivate::xdg_surface_destroy(QtWaylandServer::xdg_surface::Resource *resource) |
| { |
| wl_resource_destroy(resource->handle); |
| } |
| |
| void QWaylandXdgSurfacePrivate::xdg_surface_get_toplevel(QtWaylandServer::xdg_surface::Resource *resource, uint32_t id) |
| { |
| Q_Q(QWaylandXdgSurface); |
| |
| if (m_toplevel || m_popup) { |
| wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, |
| "xdg_surface already has a role object"); |
| return; |
| } |
| |
| if (!m_surface->setRole(QWaylandXdgToplevel::role(), resource->handle, XDG_WM_BASE_ERROR_ROLE)) |
| return; |
| |
| QWaylandResource topLevelResource(wl_resource_create(resource->client(), &xdg_toplevel_interface, |
| wl_resource_get_version(resource->handle), id)); |
| |
| m_toplevel = new QWaylandXdgToplevel(q, topLevelResource); |
| emit q->toplevelCreated(); |
| emit m_xdgShell->toplevelCreated(m_toplevel, q); |
| } |
| |
| void QWaylandXdgSurfacePrivate::xdg_surface_get_popup(QtWaylandServer::xdg_surface::Resource *resource, uint32_t id, wl_resource *parentResource, wl_resource *positionerResource) |
| { |
| Q_Q(QWaylandXdgSurface); |
| |
| if (m_toplevel || m_popup) { |
| wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, |
| "xdg_surface already has a role object"); |
| return; |
| } |
| |
| QWaylandXdgSurface *parent = QWaylandXdgSurface::fromResource(parentResource); |
| if (!parent) { |
| wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT, |
| "xdg_surface.get_popup with invalid popup parent"); |
| return; |
| } |
| |
| QWaylandXdgPositioner *positioner = QWaylandXdgPositioner::fromResource(positionerResource); |
| if (!positioner) { |
| wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER, |
| "xdg_surface.get_popup without positioner"); |
| return; |
| } |
| |
| if (!positioner->m_data.isComplete()) { |
| QWaylandXdgPositionerData p = positioner->m_data; |
| wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER, |
| "xdg_surface.get_popup with invalid positioner (size: %dx%d, anchorRect: %dx%d)", |
| p.size.width(), p.size.height(), p.anchorRect.width(), p.anchorRect.height()); |
| return; |
| } |
| |
| QRect anchorBounds(QPoint(0, 0), parent->windowGeometry().size()); |
| if (!anchorBounds.contains(positioner->m_data.anchorRect)) { |
| // TODO: this is a protocol error and should ideally be handled like this: |
| //wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER, |
| // "xdg_positioner anchor rect extends beyound its parent's window geometry"); |
| //return; |
| // However, our own clients currently do this, so we'll settle for a gentle warning instead. |
| qCWarning(qLcWaylandCompositor) << "Ignoring client protocol error: xdg_positioner anchor" |
| << "rect extends beyond its parent's window geometry"; |
| } |
| |
| if (!m_surface->setRole(QWaylandXdgPopup::role(), resource->handle, XDG_WM_BASE_ERROR_ROLE)) |
| return; |
| |
| QWaylandResource popupResource(wl_resource_create(resource->client(), &xdg_popup_interface, |
| wl_resource_get_version(resource->handle), id)); |
| |
| m_popup = new QWaylandXdgPopup(q, parent, positioner, popupResource); |
| emit q->popupCreated(); |
| emit m_xdgShell->popupCreated(m_popup, q); |
| } |
| |
| void QWaylandXdgSurfacePrivate::xdg_surface_ack_configure(QtWaylandServer::xdg_surface::Resource *resource, uint32_t serial) |
| { |
| if (m_toplevel) { |
| QWaylandXdgToplevelPrivate::get(m_toplevel)->handleAckConfigure(serial); |
| } else if (m_popup) { |
| QWaylandXdgPopupPrivate::get(m_popup)->handleAckConfigure(serial); |
| } else { |
| wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, |
| "ack_configure requested on an unconstructed xdg_surface"); |
| } |
| } |
| |
| void QWaylandXdgSurfacePrivate::xdg_surface_set_window_geometry(QtWaylandServer::xdg_surface::Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) |
| { |
| Q_Q(QWaylandXdgSurface); |
| |
| if (!q->surface()->role()) { |
| wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, |
| "set_window_geometry requested on an unconstructed xdg_surface"); |
| return; |
| } |
| |
| if (width <= 0 || height <= 0) { |
| // The protocol spec says "setting an invalid size will raise an error". But doesn't tell |
| // which error to raise, and there's no fitting error in the xdg_surface_error enum. |
| // So until this is fixed, just output a warning and return. |
| qWarning() << "Invalid (non-positive) dimensions received in set_window_geometry"; |
| return; |
| } |
| |
| m_unsetWindowGeometry = false; |
| |
| QRect geometry(x, y, width, height); |
| |
| if (m_windowGeometry == geometry) |
| return; |
| |
| m_windowGeometry = geometry; |
| emit q->windowGeometryChanged(); |
| } |
| |
| /*! |
| * \qmltype XdgSurface |
| * \inqmlmodule QtWayland.Compositor |
| * \since 5.12 |
| * \brief XdgSurface provides desktop-style compositor-specific features to an xdg surface. |
| * |
| * This type is part of the \l{XdgShell} extension and provides a way to |
| * extend the functionality of an existing \l{WaylandSurface} with features |
| * specific to desktop-style compositors, such as resizing and moving the |
| * surface. |
| * |
| * It corresponds to the Wayland interface \c xdg_surface. |
| */ |
| |
| /*! |
| * \class QWaylandXdgSurface |
| * \inmodule QtWaylandCompositor |
| * \since 5.12 |
| * \brief The QWaylandXdgSurface class provides desktop-style compositor-specific features to an xdg surface. |
| * |
| * This class is part of the QWaylandXdgShell extension and provides a way to |
| * extend the functionality of an existing QWaylandSurface with features |
| * specific to desktop-style compositors, such as resizing and moving the |
| * surface. |
| * |
| * It corresponds to the Wayland interface \c xdg_surface. |
| */ |
| |
| /*! |
| * Constructs a QWaylandXdgSurface. |
| */ |
| QWaylandXdgSurface::QWaylandXdgSurface() |
| : QWaylandShellSurfaceTemplate<QWaylandXdgSurface>(*new QWaylandXdgSurfacePrivate) |
| { |
| } |
| |
| /*! |
| * Constructs a QWaylandXdgSurface for \a surface and initializes it with the |
| * given \a xdgShell, \a surface, and resource \a res. |
| */ |
| QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *xdgShell, QWaylandSurface *surface, const QWaylandResource &res) |
| : QWaylandShellSurfaceTemplate<QWaylandXdgSurface>(*new QWaylandXdgSurfacePrivate) |
| { |
| initialize(xdgShell, surface, res); |
| } |
| |
| /*! |
| * \qmlmethod void QtWaylandCompositor::XdgSurface::initialize(object xdgShell, object surface, object client, int id) |
| * |
| * Initializes the XdgSurface, associating it with the given \a xdgShell, \a surface, |
| * \a client, and \a id. |
| */ |
| |
| /*! |
| * Initializes the QWaylandXdgSurface, associating it with the given \a xdgShell, \a surface |
| * and \a resource. |
| */ |
| void QWaylandXdgSurface::initialize(QWaylandXdgShell *xdgShell, QWaylandSurface *surface, const QWaylandResource &resource) |
| { |
| Q_D(QWaylandXdgSurface); |
| d->m_xdgShell = xdgShell; |
| d->m_surface = surface; |
| d->init(resource.resource()); |
| setExtensionContainer(surface); |
| d->m_windowGeometry = d->calculateFallbackWindowGeometry(); |
| connect(surface, &QWaylandSurface::destinationSizeChanged, this, &QWaylandXdgSurface::handleSurfaceSizeChanged); |
| connect(surface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandXdgSurface::handleBufferScaleChanged); |
| emit shellChanged(); |
| emit surfaceChanged(); |
| QWaylandCompositorExtension::initialize(); |
| } |
| |
| /*! |
| * \qmlproperty enum QtWaylandCompositor::XdgSurface::windowType |
| * |
| * This property holds the window type of the XdgSurface. |
| */ |
| Qt::WindowType QWaylandXdgSurface::windowType() const |
| { |
| Q_D(const QWaylandXdgSurface); |
| return d->m_windowType; |
| } |
| |
| /*! |
| * \qmlproperty rect QtWaylandCompositor::XdgSurface::windowGeometry |
| * |
| * This property holds the window geometry of the QWaylandXdgSurface. The window |
| * geometry describes the window's visible bounds from the user's perspective. |
| * The geometry includes title bars and borders if drawn by the client, but |
| * excludes drop shadows. It is meant to be used for aligning and tiling |
| * windows. |
| */ |
| |
| /*! |
| * \property QWaylandXdgSurface::windowGeometry |
| * |
| * This property holds the window geometry of the QWaylandXdgSurface. The window |
| * geometry describes the window's visible bounds from the user's perspective. |
| * The geometry includes title bars and borders if drawn by the client, but |
| * excludes drop shadows. It is meant to be used for aligning and tiling |
| * windows. |
| */ |
| QRect QWaylandXdgSurface::windowGeometry() const |
| { |
| Q_D(const QWaylandXdgSurface); |
| return d->m_windowGeometry; |
| } |
| |
| /*! |
| * \internal |
| */ |
| void QWaylandXdgSurface::initialize() |
| { |
| QWaylandCompositorExtension::initialize(); |
| } |
| |
| void QWaylandXdgSurface::handleSurfaceSizeChanged() |
| { |
| Q_D(QWaylandXdgSurface); |
| d->updateFallbackWindowGeometry(); |
| } |
| |
| void QWaylandXdgSurface::handleBufferScaleChanged() |
| { |
| Q_D(QWaylandXdgSurface); |
| d->updateFallbackWindowGeometry(); |
| } |
| |
| /*! |
| * \qmlproperty XdgShell QtWaylandCompositor::XdgSurface::shell |
| * |
| * This property holds the shell associated with this XdgSurface. |
| */ |
| |
| /*! |
| * \property QWaylandXdgSurface::shell |
| * |
| * This property holds the shell associated with this QWaylandXdgSurface. |
| */ |
| QWaylandXdgShell *QWaylandXdgSurface::shell() const |
| { |
| Q_D(const QWaylandXdgSurface); |
| return d->m_xdgShell; |
| } |
| |
| /*! |
| * \qmlproperty WaylandSurface QtWaylandCompositor::XdgSurface::surface |
| * |
| * This property holds the surface associated with this XdgSurface. |
| */ |
| |
| /*! |
| * \property QWaylandXdgSurface::surface |
| * |
| * This property holds the surface associated with this QWaylandXdgSurface. |
| */ |
| QWaylandSurface *QWaylandXdgSurface::surface() const |
| { |
| Q_D(const QWaylandXdgSurface); |
| return d->m_surface; |
| } |
| |
| /*! |
| * \qmlproperty XdgToplevel QtWaylandCompositor::XdgSurface::toplevel |
| * |
| * This property holds the properties and methods that are specific to the |
| * toplevel XdgSurface. |
| * |
| * \sa popup, XdgShell::toplevelCreated |
| */ |
| |
| /*! |
| * \property QWaylandXdgSurface::toplevel |
| * |
| * This property holds the properties and methods that are specific to the |
| * toplevel QWaylandXdgSurface. |
| * |
| * \sa QWaylandXdgSurface::popup, QWaylandXdgShell::toplevelCreated |
| */ |
| QWaylandXdgToplevel *QWaylandXdgSurface::toplevel() const |
| { |
| Q_D(const QWaylandXdgSurface); |
| return d->m_toplevel; |
| } |
| |
| /*! |
| * \qmlproperty XdgPopup QtWaylandCompositor::XdgSurface::popup |
| * |
| * This property holds the properties and methods that are specific to the |
| * popup XdgSurface. |
| * |
| * \sa toplevel, XdgShell::popupCreated |
| */ |
| |
| /*! |
| * \property QWaylandXdgSurface::popup |
| * |
| * This property holds the properties and methods that are specific to the |
| * popup QWaylandXdgSurface. |
| * |
| * \sa QWaylandXdgSurface::toplevel, QWaylandXdgShell::popupCreated |
| */ |
| QWaylandXdgPopup *QWaylandXdgSurface::popup() const |
| { |
| Q_D(const QWaylandXdgSurface); |
| return d->m_popup; |
| } |
| |
| /*! |
| * Returns the Wayland interface for the QWaylandXdgSurface. |
| */ |
| const wl_interface *QWaylandXdgSurface::interface() |
| { |
| return QWaylandXdgSurfacePrivate::interface(); |
| } |
| |
| /*! |
| * \internal |
| */ |
| QByteArray QWaylandXdgSurface::interfaceName() |
| { |
| return QWaylandXdgSurfacePrivate::interfaceName(); |
| } |
| |
| /*! |
| * Returns the QWaylandXdgSurface corresponding to the \a resource. |
| */ |
| QWaylandXdgSurface *QWaylandXdgSurface::fromResource(wl_resource *resource) |
| { |
| if (auto p = QtWayland::fromResource<QWaylandXdgSurfacePrivate *>(resource)) |
| return p->q_func(); |
| return nullptr; |
| } |
| |
| #if QT_CONFIG(wayland_compositor_quick) |
| QWaylandQuickShellIntegration *QWaylandXdgSurface::createIntegration(QWaylandQuickShellSurfaceItem *item) |
| { |
| Q_D(const QWaylandXdgSurface); |
| |
| if (d->m_toplevel) |
| return new QtWayland::XdgToplevelIntegration(item); |
| |
| if (d->m_popup) |
| return new QtWayland::XdgPopupIntegration(item); |
| |
| return nullptr; |
| } |
| #endif |
| |
| /*! |
| * \qmltype XdgToplevel |
| * \inqmlmodule QtWayland.Compositor |
| * \since 5.12 |
| * \brief XdgToplevel represents the toplevel window specific parts of an xdg surface. |
| * |
| * This type is part of the \l{XdgShell} extension and provides a way to |
| * extend the functionality of an XdgSurface with features |
| * specific to desktop-style windows. |
| * |
| * It corresponds to the Wayland interface \c xdg_toplevel. |
| */ |
| |
| /*! |
| * \class QWaylandXdgToplevel |
| * \inmodule QtWaylandCompositor |
| * \since 5.12 |
| * \brief The QWaylandXdgToplevel class represents the toplevel window specific parts of an xdg surface. |
| * |
| * This class is part of the QWaylandXdgShell extension and provides a way to |
| * extend the functionality of an QWaylandXdgSurface with features |
| * specific to desktop-style windows. |
| * |
| * It corresponds to the Wayland interface \c xdg_toplevel. |
| */ |
| |
| /*! |
| * Constructs a QWaylandXdgToplevel for the given \a xdgSurface and \a resource. |
| */ |
| QWaylandXdgToplevel::QWaylandXdgToplevel(QWaylandXdgSurface *xdgSurface, QWaylandResource &resource) |
| : QObject(*new QWaylandXdgToplevelPrivate(xdgSurface, resource)) |
| { |
| QVector<QWaylandXdgToplevel::State> states; |
| sendConfigure({0, 0}, states); |
| } |
| |
| QWaylandXdgToplevel::~QWaylandXdgToplevel() |
| { |
| Q_D(QWaylandXdgToplevel); |
| // Usually, the decoration is destroyed by the client (according to the protocol), |
| // but if the client misbehaves, or is shut down, we need to clean up here. |
| if (Q_UNLIKELY(d->m_decoration)) |
| wl_resource_destroy(d->m_decoration->resource()->handle); |
| Q_ASSERT(!d->m_decoration); |
| } |
| |
| /*! |
| * \qmlproperty XdgSurface QtWaylandCompositor::XdgToplevel::xdgSurface |
| * |
| * This property holds the XdgSurface for this XdgToplevel. |
| */ |
| |
| /*! |
| * \property QWaylandXdgToplevel::xdgSurface |
| * |
| * This property holds the QWaylandXdgSurface for this QWaylandXdgToplevel. |
| */ |
| QWaylandXdgSurface *QWaylandXdgToplevel::xdgSurface() const |
| { |
| Q_D(const QWaylandXdgToplevel); |
| return d->m_xdgSurface; |
| } |
| |
| /*! |
| * \qmlproperty XdgToplevel QtWaylandCompositor::XdgToplevel::parentToplevel |
| * |
| * This property holds the XdgToplevel parent of this XdgToplevel. |
| */ |
| |
| /*! |
| * \property QWaylandXdgToplevel::parentToplevel |
| * |
| * This property holds the XdgToplevel parent of this XdgToplevel. |
| * |
| */ |
| QWaylandXdgToplevel *QWaylandXdgToplevel::parentToplevel() const |
| { |
| Q_D(const QWaylandXdgToplevel); |
| return d->m_parentToplevel; |
| } |
| |
| /*! |
| * \qmlproperty string QtWaylandCompositor::XdgToplevel::title |
| * |
| * This property holds the title of the XdgToplevel. |
| */ |
| |
| /*! |
| * \property QWaylandXdgToplevel::title |
| * |
| * This property holds the title of the QWaylandXdgToplevel. |
| */ |
| QString QWaylandXdgToplevel::title() const |
| { |
| Q_D(const QWaylandXdgToplevel); |
| return d->m_title; |
| } |
| |
| /*! |
| * \qmlproperty string QtWaylandCompositor::XdgToplevel::appId |
| * |
| * This property holds the app id of the XdgToplevel. |
| */ |
| |
| /*! |
| * \property QWaylandXdgToplevel::appId |
| * |
| * This property holds the app id of the QWaylandXdgToplevel. |
| */ |
| QString QWaylandXdgToplevel::appId() const |
| { |
| Q_D(const QWaylandXdgToplevel); |
| return d->m_appId; |
| } |
| |
| /*! |
| * \qmlproperty size QtWaylandCompositor::XdgToplevel::maxSize |
| * |
| * This property holds the maximum size of the XdgToplevel as requested by the client. |
| * |
| * The compositor is free to ignore this value and request a larger size. |
| */ |
| |
| /*! |
| * \property QWaylandXdgToplevel::maxSize |
| * |
| * This property holds the maximum size of the QWaylandXdgToplevel. |
| * |
| * The compositor is free to ignore this value and request a larger size. |
| */ |
| QSize QWaylandXdgToplevel::maxSize() const |
| { |
| Q_D(const QWaylandXdgToplevel); |
| return d->m_maxSize; |
| } |
| |
| /*! |
| * \qmlproperty size QtWaylandCompositor::XdgToplevel::minSize |
| * |
| * This property holds the minimum size of the XdgToplevel as requested by the client. |
| * |
| * The compositor is free to ignore this value and request a smaller size. |
| */ |
| |
| /*! |
| * \property QWaylandXdgToplevel::minSize |
| * |
| * This property holds the minimum size of the QWaylandXdgToplevel. |
| * |
| * The compositor is free to ignore this value and request a smaller size. |
| */ |
| QSize QWaylandXdgToplevel::minSize() const |
| { |
| Q_D(const QWaylandXdgToplevel); |
| return d->m_minSize; |
| } |
| |
| /*! |
| * \property QWaylandXdgToplevel::states |
| * |
| * This property holds the last states the client acknowledged for this QWaylandToplevel. |
| */ |
| QVector<QWaylandXdgToplevel::State> QWaylandXdgToplevel::states() const |
| { |
| Q_D(const QWaylandXdgToplevel); |
| return d->m_lastAckedConfigure.states; |
| } |
| |
| /*! |
| * \qmlproperty bool QtWaylandCompositor::XdgToplevel::maximized |
| * |
| * This property holds whether the client has acknowledged that it should be maximized. |
| */ |
| |
| /*! |
| * \property QWaylandXdgToplevel::maximized |
| * |
| * This property holds whether the client has acknowledged that it should be maximized. |
| */ |
| bool QWaylandXdgToplevel::maximized() const |
| { |
| Q_D(const QWaylandXdgToplevel); |
| return d->m_lastAckedConfigure.states.contains(QWaylandXdgToplevel::State::MaximizedState); |
| } |
| |
| /*! |
| * \qmlproperty bool QtWaylandCompositor::XdgToplevel::fullscreen |
| * |
| * This property holds whether the client has acknowledged that it should be fullscreen. |
| */ |
| |
| /*! |
| * \property QWaylandXdgToplevel::fullscreen |
| * |
| * This property holds whether the client has acknowledged that it should be fullscreen. |
| */ |
| bool QWaylandXdgToplevel::fullscreen() const |
| { |
| Q_D(const QWaylandXdgToplevel); |
| return d->m_lastAckedConfigure.states.contains(QWaylandXdgToplevel::State::FullscreenState); |
| } |
| |
| /*! |
| * \qmlproperty bool QtWaylandCompositor::XdgToplevel::resizing |
| * |
| * This property holds whether the client has acknowledged that it is being resized. |
| */ |
| |
| /*! |
| * \property QWaylandXdgToplevel::resizing |
| * |
| * This property holds whether the client has acknowledged that it is being resized. |
| */ |
| bool QWaylandXdgToplevel::resizing() const |
| { |
| Q_D(const QWaylandXdgToplevel); |
| return d->m_lastAckedConfigure.states.contains(QWaylandXdgToplevel::State::ResizingState); |
| } |
| |
| /*! |
| * \qmlproperty bool QtWaylandCompositor::XdgToplevel::activated |
| * |
| * This property holds whether toplevel is drawing itself as having input focus. |
| */ |
| |
| /*! |
| * \property QWaylandXdgToplevel::activated |
| * |
| * This property holds whether toplevel is drawing itself as having input focus. |
| */ |
| bool QWaylandXdgToplevel::activated() const |
| { |
| Q_D(const QWaylandXdgToplevel); |
| return d->m_lastAckedConfigure.states.contains(QWaylandXdgToplevel::State::ActivatedState); |
| } |
| |
| /*! |
| * \enum QWaylandXdgToplevel::DecorationMode |
| * |
| * This enum type is used to specify the window decoration mode for toplevel windows. |
| * |
| * \value ServerSideDecoration The compositor should draw window decorations. |
| * \value ClientSideDecoration The client should draw window decorations. |
| */ |
| |
| /*! |
| * \qmlproperty enumeration QtWaylandCompositor::XdgToplevel::decorationMode |
| * |
| * This property holds the current window decoration mode for this toplevel. |
| * |
| * The possible values are: |
| * \value XdgToplevel.ServerSideDecoration The compositor should draw window decorations. |
| * \value XdgToplevel.ClientSideDecoration The client should draw window decorations. |
| * |
| * \sa XdgDecorationManagerV1 |
| */ |
| |
| /*! |
| * \property QWaylandXdgToplevel::decorationMode |
| * |
| * This property holds the current window decoration mode for this toplevel. |
| * |
| * \sa QWaylandXdgDecorationManagerV1 |
| */ |
| QWaylandXdgToplevel::DecorationMode QWaylandXdgToplevel::decorationMode() const |
| { |
| Q_D(const QWaylandXdgToplevel); |
| return d->m_decoration ? d->m_decoration->configuredMode() : DecorationMode::ClientSideDecoration; |
| } |
| |
| /*! |
| * \qmlmethod size QtWaylandCompositor::XdgToplevel::sizeForResize(size size, point delta, uint edges) |
| * |
| * Convenience for computing the new size given the current \a size, a \a delta, and |
| * the \a edges active in the drag. |
| */ |
| |
| /*! |
| * Convenience for computing the new size given the current \a size, a \a delta, and |
| * the \a edges active in the drag. |
| */ |
| QSize QWaylandXdgToplevel::sizeForResize(const QSizeF &size, const QPointF &delta, Qt::Edges edges) const |
| { |
| qreal width = size.width(); |
| qreal height = size.height(); |
| if (edges & Qt::LeftEdge) |
| width -= delta.x(); |
| else if (edges & Qt::RightEdge) |
| width += delta.x(); |
| |
| if (edges & Qt::TopEdge) |
| height -= delta.y(); |
| else if (edges & Qt::BottomEdge) |
| height += delta.y(); |
| |
| QSize newSize = QSize(width, height) |
| .expandedTo(minSize()) |
| .expandedTo({1, 1}); // We don't want to send a size of (0,0) as that means that the client decides |
| |
| if (maxSize().isValid()) |
| newSize = newSize.boundedTo(maxSize()); |
| |
| return newSize; |
| } |
| |
| /*! |
| * Sends a configure event to the client. Parameter \a size contains the pixel size |
| * of the surface. A size of zero means the client is free to decide the size. |
| * Known \a states are enumerated in QWaylandXdgToplevel::State. |
| */ |
| uint QWaylandXdgToplevel::sendConfigure(const QSize &size, const QVector<QWaylandXdgToplevel::State> &states) |
| { |
| if (!size.isValid()) { |
| qWarning() << "Can't configure xdg_toplevel with an invalid size" << size; |
| return 0; |
| } |
| Q_D(QWaylandXdgToplevel); |
| auto statesBytes = QByteArray::fromRawData(reinterpret_cast<const char *>(states.data()), |
| states.size() * static_cast<int>(sizeof(State))); |
| uint32_t serial = d->m_xdgSurface->surface()->compositor()->nextSerial(); |
| d->m_pendingConfigures.append(QWaylandXdgToplevelPrivate::ConfigureEvent{states, size, serial}); |
| d->send_configure(size.width(), size.height(), statesBytes); |
| QWaylandXdgSurfacePrivate::get(d->m_xdgSurface)->send_configure(serial); |
| return serial; |
| } |
| |
| /*! |
| * \qmlmethod int QtWaylandCompositor::XdgToplevel::sendConfigure(size size, list<int> states) |
| * |
| * Sends a configure event to the client. \a size contains the pixel size of the surface. |
| * A size of zero means the client is free to decide the size. |
| * Known \a states are enumerated in XdgToplevel::State. |
| */ |
| uint QWaylandXdgToplevel::sendConfigure(const QSize &size, const QVector<int> &states) |
| { |
| QVector<State> s; |
| for (auto state : states) |
| s << State(state); |
| return sendConfigure(size, s); |
| } |
| |
| /*! |
| * \qmlmethod void QtWaylandCompositor::XdgToplevel::sendClose() |
| * |
| * Sends a close event to the client. The client may choose to ignore the event. |
| */ |
| |
| /*! |
| * Sends a close event to the client. The client may choose to ignore the event. |
| */ |
| void QWaylandXdgToplevel::sendClose() |
| { |
| Q_D(QWaylandXdgToplevel); |
| d->send_close(); |
| } |
| |
| /*! |
| * \qmlmethod void QtWaylandCompositor::XdgToplevel::sendMaximized(size size) |
| * |
| * Convenience for sending a configure event with the maximized state set, and |
| * fullscreen and resizing removed. The activated state is left in its current state. |
| * |
| * \a size is the new size of the window. |
| */ |
| |
| /*! |
| * Convenience for sending a configure event with the maximized state set, and |
| * fullscreen and resizing removed. The activated state is left in its current state. |
| * |
| * \a size is the new size of the window. |
| */ |
| uint QWaylandXdgToplevel::sendMaximized(const QSize &size) |
| { |
| Q_D(QWaylandXdgToplevel); |
| QWaylandXdgToplevelPrivate::ConfigureEvent conf = d->lastSentConfigure(); |
| |
| if (!conf.states.contains(QWaylandXdgToplevel::State::MaximizedState)) |
| conf.states.append(QWaylandXdgToplevel::State::MaximizedState); |
| conf.states.removeOne(QWaylandXdgToplevel::State::FullscreenState); |
| conf.states.removeOne(QWaylandXdgToplevel::State::ResizingState); |
| |
| return sendConfigure(size, conf.states); |
| } |
| |
| /*! |
| * \qmlmethod void QtWaylandCompositor::XdgToplevel::sendUnmaximized(size size) |
| * |
| * Convenience for sending a configure event with the maximized, fullscreen and |
| * resizing states removed, and fullscreen and resizing removed. The activated |
| * state is left in its current state. |
| * |
| * \a size is the new size of the window. If \a size is zero, the client decides the size. |
| */ |
| |
| /*! |
| * Convenience for sending a configure event with the maximized, fullscreen and |
| * resizing states removed, and fullscreen and resizing removed. The activated |
| * state is left in its current state. |
| * |
| * \a size is the new size of the window. If \a size is zero, the client decides the size. |
| */ |
| uint QWaylandXdgToplevel::sendUnmaximized(const QSize &size) |
| { |
| Q_D(QWaylandXdgToplevel); |
| QWaylandXdgToplevelPrivate::ConfigureEvent conf = d->lastSentConfigure(); |
| |
| conf.states.removeOne(QWaylandXdgToplevel::State::MaximizedState); |
| conf.states.removeOne(QWaylandXdgToplevel::State::FullscreenState); |
| conf.states.removeOne(QWaylandXdgToplevel::State::ResizingState); |
| |
| return sendConfigure(size, conf.states); |
| |
| } |
| |
| /*! |
| * \qmlmethod void QtWaylandCompositor::XdgToplevel::sendFullscreen(size size) |
| * |
| * Convenience for sending a configure event with the fullscreen state set, and |
| * maximized and resizing removed. The activated state is left in its current state. |
| * |
| * \sa sendUnmaximized |
| * |
| * \a size is the new size of the window. |
| */ |
| |
| /*! |
| * Convenience for sending a configure event with the fullscreen state set, and |
| * maximized and resizing removed. The activated state is left in its current state. |
| * |
| * \sa sendUnmaximized |
| * |
| * \a size is the new size of the window. |
| */ |
| uint QWaylandXdgToplevel::sendFullscreen(const QSize &size) |
| { |
| Q_D(QWaylandXdgToplevel); |
| QWaylandXdgToplevelPrivate::ConfigureEvent conf = d->lastSentConfigure(); |
| |
| if (!conf.states.contains(QWaylandXdgToplevel::State::FullscreenState)) |
| conf.states.append(QWaylandXdgToplevel::State::FullscreenState); |
| conf.states.removeOne(QWaylandXdgToplevel::State::MaximizedState); |
| conf.states.removeOne(QWaylandXdgToplevel::State::ResizingState); |
| |
| return sendConfigure(size, conf.states); |
| } |
| |
| /*! |
| * \qmlmethod void QtWaylandCompositor::XdgToplevel::sendResizing(size maxSize) |
| * |
| * Convenience for sending a configure event with the resizing state set, and |
| * maximized and fullscreen removed. The activated state is left in its current state. |
| * |
| * \a maxSize is the new size of the window. |
| */ |
| |
| /*! |
| * Convenience for sending a configure event with the resizing state set, and |
| * maximized and fullscreen removed. The activated state is left in its current state. |
| * |
| * \a maxSize is the new size of the window. |
| */ |
| uint QWaylandXdgToplevel::sendResizing(const QSize &maxSize) |
| { |
| Q_D(QWaylandXdgToplevel); |
| QWaylandXdgToplevelPrivate::ConfigureEvent conf = d->lastSentConfigure(); |
| |
| if (!conf.states.contains(QWaylandXdgToplevel::State::ResizingState)) |
| conf.states.append(QWaylandXdgToplevel::State::ResizingState); |
| conf.states.removeOne(QWaylandXdgToplevel::State::MaximizedState); |
| conf.states.removeOne(QWaylandXdgToplevel::State::FullscreenState); |
| |
| return sendConfigure(maxSize, conf.states); |
| } |
| |
| /*! |
| * Returns the surface role for the QWaylandToplevel. |
| */ |
| QWaylandSurfaceRole *QWaylandXdgToplevel::role() |
| { |
| return &QWaylandXdgToplevelPrivate::s_role; |
| } |
| |
| /*! |
| * Returns the QWaylandXdgToplevel corresponding to the \a resource. |
| */ |
| QWaylandXdgToplevel *QWaylandXdgToplevel::fromResource(wl_resource *resource) |
| { |
| if (auto p = QtWayland::fromResource<QWaylandXdgToplevelPrivate *>(resource)) |
| return p->q_func(); |
| return nullptr; |
| } |
| |
| /*! |
| * \qmlsignal QtWaylandCompositor::XdgShell::xdgSurfaceCreated(XdgSurface xdgSurface) |
| * |
| * This signal is emitted when the client has created a \c xdg_surface. |
| * Note that \a xdgSurface is not mapped, i.e. according to the \c xdg-shell |
| * protocol it should not be displayed, until it has received a role object. |
| * |
| * \sa toplevelCreated(), popupCreated() |
| */ |
| |
| /*! |
| * \fn void QWaylandXdgShell::xdgSurfaceCreated(QWaylandXdgSurface *xdgSurface) |
| * |
| * This signal is emitted when the client has created a \c xdg_surface. |
| * Note that \a xdgSurface is not mapped, i.e. according to the \c xdg-shell |
| * protocol it should not be displayed, until it has received a role object. |
| * |
| * \sa toplevelCreated(), popupCreated() |
| */ |
| |
| /*! |
| * \qmlsignal QtWaylandCompositor::XdgShell::toplevelCreated(XdgToplevel toplevel, XdgSurface xdgSurface) |
| * |
| * This signal is emitted when the client has created a \c xdg_toplevel. |
| * A common use case is to let the handler of this signal instantiate a ShellSurfaceItem or |
| * WaylandQuickItem for displaying \a toplevel in a QtQuick scene. |
| * |
| * \a xdgSurface is the XdgSurface \a toplevel is the role object for. |
| */ |
| |
| /*! |
| * \fn void QWaylandXdgShell::toplevelCreated(QWaylandXdgToplevel *toplevel, QWaylandXdgSurface *xdgSurface) |
| * |
| * This signal is emitted when the client has created a \c xdg_toplevel. |
| * A common use case is to let the handler of this signal instantiate a QWaylandShellSurfaceItem or |
| * QWaylandQuickItem for displaying \a toplevel in a QtQuick scene. |
| * |
| * \a xdgSurface is the XdgSurface \a toplevel is the role object for. |
| */ |
| |
| /*! |
| * \qmlsignal QtWaylandCompositor::XdgShell::popupCreated(XdgPopup popup, XdgSurface xdgSurface) |
| * |
| * This signal is emitted when the client has created a \c xdg_popup. |
| * A common use case is to let the handler of this signal instantiate a ShellSurfaceItem or |
| * WaylandQuickItem for displaying \a popup in a QtQuick scene. |
| * |
| * \a xdgSurface is the XdgSurface \a popup is the role object for. |
| */ |
| |
| /*! |
| * \fn void QWaylandXdgShell::popupCreated(QWaylandXdgPopup *popup, QWaylandXdgSurface *xdgSurface) |
| * |
| * This signal is emitted when the client has created a \c xdg_popup. |
| * A common use case is to let the handler of this signal instantiate a QWaylandShellSurfaceItem or |
| * QWaylandQuickItem for displaying \a popup in a QtQuick scene. |
| * |
| * \a xdgSurface is the XdgSurface \a popup is the role object for. |
| */ |
| |
| /*! |
| * \qmlsignal QtWaylandCompositor::XdgShell::pong(int serial) |
| * |
| * This signal is emitted when the client has responded to a ping event with serial, \a serial. |
| * |
| * \sa ping() |
| */ |
| |
| /*! |
| * \fn void QWaylandXdgShell::pong(uint serial) |
| * |
| * This signal is emitted when the client has responded to a ping event with serial, \a serial. |
| * |
| * \sa QWaylandXdgShell::ping() |
| */ |
| |
| QList<int> QWaylandXdgToplevel::statesAsInts() const |
| { |
| QList<int> list; |
| const auto s = states(); |
| list.reserve(s.size()); |
| for (auto state : s) { |
| list << static_cast<int>(state); |
| } |
| return list; |
| } |
| |
| QWaylandSurfaceRole QWaylandXdgToplevelPrivate::s_role("xdg_toplevel"); |
| |
| QWaylandXdgToplevelPrivate::QWaylandXdgToplevelPrivate(QWaylandXdgSurface *xdgSurface, const QWaylandResource &resource) |
| : m_xdgSurface(xdgSurface) |
| { |
| init(resource.resource()); |
| } |
| |
| void QWaylandXdgToplevelPrivate::handleAckConfigure(uint serial) |
| { |
| Q_Q(QWaylandXdgToplevel); |
| ConfigureEvent config; |
| Q_FOREVER { |
| if (m_pendingConfigures.empty()) { |
| qWarning("Toplevel received an unexpected ack_configure!"); |
| return; |
| } |
| |
| // This won't work unless there always is a toplevel.configure for each xdgsurface.configure |
| config = m_pendingConfigures.takeFirst(); |
| |
| if (config.serial == serial) |
| break; |
| } |
| |
| QVector<uint> changedStates; |
| std::set_symmetric_difference( |
| m_lastAckedConfigure.states.begin(), m_lastAckedConfigure.states.end(), |
| config.states.begin(), config.states.end(), |
| std::back_inserter(changedStates)); |
| |
| m_lastAckedConfigure = config; |
| |
| for (uint state : changedStates) { |
| switch (state) { |
| case state_maximized: |
| emit q->maximizedChanged(); |
| break; |
| case state_fullscreen: |
| emit q->fullscreenChanged(); |
| break; |
| case state_resizing: |
| emit q->resizingChanged(); |
| break; |
| case state_activated: |
| emit q->activatedChanged(); |
| break; |
| } |
| } |
| |
| if (!changedStates.empty()) |
| emit q->statesChanged(); |
| } |
| |
| void QWaylandXdgToplevelPrivate::handleFocusLost() |
| { |
| Q_Q(QWaylandXdgToplevel); |
| QWaylandXdgToplevelPrivate::ConfigureEvent current = lastSentConfigure(); |
| current.states.removeOne(QWaylandXdgToplevel::State::ActivatedState); |
| q->sendConfigure(current.size, current.states); |
| } |
| |
| void QWaylandXdgToplevelPrivate::handleFocusReceived() |
| { |
| Q_Q(QWaylandXdgToplevel); |
| QWaylandXdgToplevelPrivate::ConfigureEvent current = lastSentConfigure(); |
| if (!current.states.contains(QWaylandXdgToplevel::State::ActivatedState)) { |
| current.states.push_back(QWaylandXdgToplevel::State::ActivatedState); |
| q->sendConfigure(current.size, current.states); |
| } |
| } |
| |
| Qt::Edges QWaylandXdgToplevelPrivate::convertToEdges(resize_edge edge) |
| { |
| return Qt::Edges(((edge & 0b1100) >> 1) | ((edge & 0b0010) << 2) | (edge & 0b0001)); |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_destroy_resource(QtWaylandServer::xdg_toplevel::Resource *resource) |
| { |
| Q_UNUSED(resource); |
| Q_Q(QWaylandXdgToplevel); |
| delete q; |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_destroy(QtWaylandServer::xdg_toplevel::Resource *resource) |
| { |
| if (Q_UNLIKELY(m_decoration)) |
| qWarning() << "Client error: xdg_toplevel destroyed before its decoration object"; |
| |
| wl_resource_destroy(resource->handle); |
| //TODO: Should the xdg surface be desroyed as well? Or is it allowed to recreate a new toplevel for it? |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_set_parent(QtWaylandServer::xdg_toplevel::Resource *resource, wl_resource *parent) |
| { |
| Q_UNUSED(resource); |
| QWaylandXdgToplevel *parentToplevel = QWaylandXdgToplevel::fromResource(parent); |
| |
| Q_Q(QWaylandXdgToplevel); |
| |
| if (m_parentToplevel != parentToplevel) { |
| m_parentToplevel = parentToplevel; |
| emit q->parentToplevelChanged(); |
| } |
| |
| if (m_parentToplevel && m_xdgSurface->windowType() != Qt::WindowType::SubWindow) { |
| // There's a parent now, which means the surface is transient |
| QWaylandXdgSurfacePrivate::get(m_xdgSurface)->setWindowType(Qt::WindowType::SubWindow); |
| } else if (!m_parentToplevel && m_xdgSurface->windowType() != Qt::WindowType::Window) { |
| // When the surface has no parent it is toplevel |
| QWaylandXdgSurfacePrivate::get(m_xdgSurface)->setWindowType(Qt::WindowType::Window); |
| } |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_set_title(QtWaylandServer::xdg_toplevel::Resource *resource, const QString &title) |
| { |
| Q_UNUSED(resource); |
| if (title == m_title) |
| return; |
| Q_Q(QWaylandXdgToplevel); |
| m_title = title; |
| emit q->titleChanged(); |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_set_app_id(QtWaylandServer::xdg_toplevel::Resource *resource, const QString &app_id) |
| { |
| Q_UNUSED(resource); |
| if (app_id == m_appId) |
| return; |
| Q_Q(QWaylandXdgToplevel); |
| m_appId = app_id; |
| emit q->appIdChanged(); |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_show_window_menu(QtWaylandServer::xdg_toplevel::Resource *resource, wl_resource *seatResource, uint32_t serial, int32_t x, int32_t y) |
| { |
| Q_UNUSED(resource); |
| Q_UNUSED(serial); |
| QPoint position(x, y); |
| auto seat = QWaylandSeat::fromSeatResource(seatResource); |
| Q_Q(QWaylandXdgToplevel); |
| emit q->showWindowMenu(seat, position); |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_move(Resource *resource, wl_resource *seatResource, uint32_t serial) |
| { |
| Q_UNUSED(resource); |
| Q_UNUSED(serial); |
| Q_Q(QWaylandXdgToplevel); |
| QWaylandSeat *seat = QWaylandSeat::fromSeatResource(seatResource); |
| emit q->startMove(seat); |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_resize(QtWaylandServer::xdg_toplevel::Resource *resource, wl_resource *seatResource, uint32_t serial, uint32_t edges) |
| { |
| Q_UNUSED(resource); |
| Q_UNUSED(serial); |
| Q_Q(QWaylandXdgToplevel); |
| QWaylandSeat *seat = QWaylandSeat::fromSeatResource(seatResource); |
| emit q->startResize(seat, convertToEdges(resize_edge(edges))); |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_set_max_size(QtWaylandServer::xdg_toplevel::Resource *resource, int32_t width, int32_t height) |
| { |
| Q_UNUSED(resource); |
| |
| QSize maxSize(width, height); |
| if (width == 0 && height == 0) |
| maxSize = QSize(); // Wayland size of zero means unspecified which best translates to invalid |
| |
| if (m_maxSize == maxSize) |
| return; |
| |
| if (width < 0 || height < 0) { |
| // The spec says raise a protocol error, but there's no matching error defined |
| qWarning() << "Received a xdg_toplevel.set_max_size request with a negative size"; |
| return; |
| } |
| |
| if (m_minSize.isValid() && maxSize.isValid() && |
| (maxSize.width() < m_minSize.width() || maxSize.height() < m_minSize.height())) { |
| // The spec says raise a protocol error, but there's no matching error defined |
| qWarning() << "Received a xdg_toplevel.set_max_size request with a size smaller than the minimium size"; |
| return; |
| } |
| |
| m_maxSize = maxSize; |
| |
| Q_Q(QWaylandXdgToplevel); |
| emit q->maxSizeChanged(); |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_set_min_size(QtWaylandServer::xdg_toplevel::Resource *resource, int32_t width, int32_t height) |
| { |
| Q_UNUSED(resource); |
| |
| QSize minSize(width, height); |
| if (width == 0 && height == 0) |
| minSize = QSize(); // Wayland size of zero means unspecified |
| |
| if (m_minSize == minSize) |
| return; |
| |
| if (width < 0 || height < 0) { |
| // The spec says raise a protocol error, but there's no matching error defined |
| qWarning() << "Received a xdg_toplevel.set_min_size request with a negative size"; |
| return; |
| } |
| |
| if (m_maxSize.isValid() && minSize.isValid() && |
| (minSize.width() > m_maxSize.width() || minSize.height() > m_maxSize.height())) { |
| // The spec says raise a protocol error, but there's no matching error defined |
| qWarning() << "Received a xdg_toplevel.set_min_size request with a size larger than the maximum size"; |
| return; |
| } |
| |
| m_minSize = minSize; |
| |
| Q_Q(QWaylandXdgToplevel); |
| emit q->minSizeChanged(); |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_set_maximized(QtWaylandServer::xdg_toplevel::Resource *resource) |
| { |
| Q_UNUSED(resource); |
| Q_Q(QWaylandXdgToplevel); |
| emit q->setMaximized(); |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_unset_maximized(QtWaylandServer::xdg_toplevel::Resource *resource) |
| { |
| Q_UNUSED(resource); |
| Q_Q(QWaylandXdgToplevel); |
| emit q->unsetMaximized(); |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_set_fullscreen(QtWaylandServer::xdg_toplevel::Resource *resource, wl_resource *output_res) |
| { |
| Q_UNUSED(resource); |
| Q_Q(QWaylandXdgToplevel); |
| QWaylandOutput *output = output_res ? QWaylandOutput::fromResource(output_res) : nullptr; |
| emit q->setFullscreen(output); |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_unset_fullscreen(QtWaylandServer::xdg_toplevel::Resource *resource) |
| { |
| Q_UNUSED(resource); |
| Q_Q(QWaylandXdgToplevel); |
| emit q->unsetFullscreen(); |
| } |
| |
| void QWaylandXdgToplevelPrivate::xdg_toplevel_set_minimized(QtWaylandServer::xdg_toplevel::Resource *resource) |
| { |
| Q_UNUSED(resource); |
| Q_Q(QWaylandXdgToplevel); |
| emit q->setMinimized(); |
| } |
| |
| /*! |
| * \qmltype XdgPopup |
| * \inqmlmodule QtWayland.Compositor |
| * \since 5.12 |
| * \brief XdgPopup represents the popup specific parts of and xdg surface. |
| * |
| * This type is part of the \l{XdgShell} extension and provides a way to extend |
| * extend the functionality of an \l{XdgSurface} with features |
| * specific to desktop-style menus for an xdg surface. |
| * |
| * It corresponds to the Wayland interface \c xdg_popup. |
| */ |
| |
| /*! |
| * \class QWaylandXdgPopup |
| * \inmodule QtWaylandCompositor |
| * \since 5.12 |
| * \brief The QWaylandXdgPopup class represents the popup specific parts of an xdg surface. |
| * |
| * This class is part of the QWaylandXdgShell extension and provides a way to |
| * extend the functionality of a QWaylandXdgSurface with features |
| * specific to desktop-style menus for an xdg surface. |
| * |
| * It corresponds to the Wayland interface \c xdg_popup. |
| */ |
| |
| /*! |
| * Constructs a QWaylandXdgPopup. |
| */ |
| QWaylandXdgPopup::QWaylandXdgPopup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parentXdgSurface, |
| QWaylandXdgPositioner *positioner, QWaylandResource &resource) |
| : QObject(*new QWaylandXdgPopupPrivate(xdgSurface, parentXdgSurface, positioner, resource)) |
| { |
| } |
| |
| /*! |
| * \qmlproperty XdgSurface QtWaylandCompositor::XdgPopup::xdgSurface |
| * |
| * This property holds the XdgSurface associated with this XdgPopup. |
| */ |
| |
| /*! |
| * \property QWaylandXdgPopup::xdgSurface |
| * |
| * This property holds the QWaylandXdgSurface associated with this QWaylandXdgPopup. |
| */ |
| QWaylandXdgSurface *QWaylandXdgPopup::xdgSurface() const |
| { |
| Q_D(const QWaylandXdgPopup); |
| return d->m_xdgSurface; |
| } |
| |
| /*! |
| * \qmlproperty XdgSurface QtWaylandCompositor::XdgPopup::parentXdgSurface |
| * |
| * This property holds the XdgSurface associated with the parent of this XdgPopup. |
| */ |
| |
| /*! |
| * \property QWaylandXdgPopup::parentXdgSurface |
| * |
| * This property holds the QWaylandXdgSurface associated with the parent of this |
| * QWaylandXdgPopup. |
| */ |
| QWaylandXdgSurface *QWaylandXdgPopup::parentXdgSurface() const |
| { |
| Q_D(const QWaylandXdgPopup); |
| return d->m_parentXdgSurface; |
| } |
| |
| /*! |
| * \qmlproperty rect QtWaylandCompositor::XdgPopup::configuredGeometry |
| * |
| * The window geometry the popup received in the configure event. Relative to the |
| * upper left corner of the parent surface. |
| */ |
| |
| /*! |
| * \property QWaylandXdgPopup::configuredGeometry |
| * |
| * The window geometry the popup received in the configure event. Relative to the |
| * upper left corner of the parent surface. |
| */ |
| QRect QWaylandXdgPopup::configuredGeometry() const |
| { |
| Q_D(const QWaylandXdgPopup); |
| return d->m_geometry; |
| } |
| |
| /*! |
| * \qmlproperty rect QtWaylandCompositor::XdgPopup::anchorRect |
| * |
| * The anchor rectangle relative to the parent window geometry that the child |
| * surface should be placed relative to. |
| */ |
| |
| /*! |
| * \property QWaylandXdgPopup::anchorRect |
| * |
| * Returns the anchor rectangle relative to the parent window geometry that the child |
| * surface should be placed relative to. |
| */ |
| QRect QWaylandXdgPopup::anchorRect() const |
| { |
| Q_D(const QWaylandXdgPopup); |
| return d->m_positionerData.anchorRect; |
| } |
| |
| /*! |
| * \qmlproperty enumeration QtWaylandCompositor::XdgPopup::anchorEdges |
| * |
| * This property holds the set of edges on the anchor rect that the child surface should be placed |
| * relative to. If no edges are specified in a direction, the anchor point should be |
| * centered between the edges. |
| * |
| * The possible values are: |
| * \value Qt.TopEdge The top edge of the rectangle. |
| * \value Qt.LeftEdge The left edge of the rectangle. |
| * \value Qt.RightEdge The right edge of the rectangle. |
| * \value Qt.BottomEdge The bottom edge of the rectangle. |
| */ |
| |
| /*! |
| * \property QWaylandXdgPopup::anchorEdges |
| * |
| * Returns the set of edges on the anchor rect that the child surface should be placed |
| * relative to. If no edges are specified in a direction, the anchor point should be |
| * centered between the edges. |
| */ |
| Qt::Edges QWaylandXdgPopup::anchorEdges() const |
| { |
| Q_D(const QWaylandXdgPopup); |
| return d->m_positionerData.anchorEdges; |
| } |
| |
| /*! |
| * \qmlproperty rect QtWaylandCompositor::XdgPopup::gravityEdges |
| * |
| * Specifies in what direction the surface should be positioned, relative to the anchor |
| * point. |
| * |
| * The possible values are: |
| * \value Qt.TopEdge The surface should slide towards the top of the screen. |
| * \value Qt.LeftEdge The surface should slide towards the left of the screen. |
| * \value Qt.RightEdge The surface should slide towards the right of the screen. |
| * \value Qt.BottomEdge The surface should slide towards the bottom of the screen. |
| */ |
| |
| /*! |
| * \property QWaylandXdgPopup::gravityEdges |
| * |
| * Specifies in what direction the surface should be positioned, relative to the anchor |
| * point. |
| */ |
| Qt::Edges QWaylandXdgPopup::gravityEdges() const |
| { |
| Q_D(const QWaylandXdgPopup); |
| return d->m_positionerData.gravityEdges; |
| } |
| |
| /*! |
| * \qmlproperty enumeration QtWaylandCompositor::XdgPopup::slideConstraints |
| * |
| * This property holds the orientations in which the child should slide to fit within the screen. |
| * |
| * Possible values: |
| * \value Qt.Horizontal Horizontal |
| * \value Qt.Vertical Vertical |
| */ |
| |
| /*! |
| * \property QWaylandXdgPopup::slideConstraints |
| * |
| * This property holds the orientations in which the child should slide to fit within the screen. |
| */ |
| Qt::Orientations QWaylandXdgPopup::slideConstraints() const |
| { |
| Q_D(const QWaylandXdgPopup); |
| const uint flags = d->m_positionerData.constraintAdjustments; |
| |
| Qt::Orientations constraints = {}; |
| |
| if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X) |
| constraints |= Qt::Horizontal; |
| if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) |
| constraints |= Qt::Vertical; |
| |
| return constraints; |
| } |
| |
| /*! |
| * \qmlproperty enumeration QtWaylandCompositor::XdgPopup::flipConstraints |
| * |
| * This property holds the orientations in which the child should flip to fit within the screen. |
| * |
| * Possible values: |
| * \value Qt.Horizontal Horizontal |
| * \value Qt.Vertical Vertical |
| */ |
| |
| /*! |
| * \property QWaylandXdgPopup::flipConstraints |
| * |
| * This property holds the orientations in which the child should flip to fit within the screen. |
| */ |
| Qt::Orientations QWaylandXdgPopup::flipConstraints() const |
| { |
| Q_D(const QWaylandXdgPopup); |
| const uint flags = d->m_positionerData.constraintAdjustments; |
| |
| Qt::Orientations constraints = {}; |
| |
| if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X) |
| constraints |= Qt::Horizontal; |
| if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y) |
| constraints |= Qt::Vertical; |
| |
| return constraints; |
| } |
| |
| /*! |
| * \qmlproperty enumeration QtWaylandCompositor::XdgPopup::resizeConstraints |
| * |
| * This property holds the orientations in which the child should resize to fit within the screen. |
| * |
| * Possible values: |
| * \value Qt.Horizontal Horizontal |
| * \value Qt.Vertical Vertical |
| */ |
| |
| /*! |
| * \property QWaylandXdgPopup::resizeConstraints |
| * |
| * This property holds the orientations in which the child should resize to fit within the screen. |
| */ |
| Qt::Orientations QWaylandXdgPopup::resizeConstraints() const |
| { |
| Q_D(const QWaylandXdgPopup); |
| const uint flags = d->m_positionerData.constraintAdjustments; |
| |
| Qt::Orientations constraints = {}; |
| |
| if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X) |
| constraints |= Qt::Horizontal; |
| if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y) |
| constraints |= Qt::Vertical; |
| |
| return constraints; |
| } |
| |
| /*! |
| * \qmlproperty point QtWaylandCompositor::XdgPopup::offset |
| * |
| * The position relative to the position of the anchor on the anchor rectangle and |
| * the anchor on the surface. |
| */ |
| |
| /*! |
| * \property QWaylandXdgPopup::offset |
| * |
| * Returns the surface position relative to the position of the anchor on the anchor |
| * rectangle and the anchor on the surface. |
| */ |
| QPoint QWaylandXdgPopup::offset() const |
| { |
| Q_D(const QWaylandXdgPopup); |
| return d->m_positionerData.offset; |
| } |
| |
| /*! |
| * \qmlproperty size QtWaylandCompositor::XdgPopup::positionerSize |
| * |
| * The size requested for the window geometry by the positioner object. |
| */ |
| |
| /*! |
| * \property QWaylandXdgPopup::positionerSize |
| * |
| * Returns the size requested for the window geometry by the positioner object. |
| */ |
| QSize QWaylandXdgPopup::positionerSize() const |
| { |
| Q_D(const QWaylandXdgPopup); |
| return d->m_positionerData.size; |
| } |
| |
| /*! |
| * \qmlproperty point QtWaylandCompositor::XdgPopup::unconstrainedPosition |
| * |
| * The position of the surface relative to the parent window geometry if the surface |
| * is not constrained. I.e. when not moved to fit inside the screen or similar. |
| */ |
| |
| /*! |
| * \property QWaylandXdgPopup::unconstrainedPosition |
| * |
| * The position of the surface relative to the parent window geometry if the surface |
| * is not constrained. I.e. when not moved to fit inside the screen or similar. |
| */ |
| QPoint QWaylandXdgPopup::unconstrainedPosition() const |
| { |
| Q_D(const QWaylandXdgPopup); |
| return d->m_positionerData.unconstrainedPosition(); |
| } |
| |
| /*! |
| * \qmlmethod int QtWaylandCompositor::XdgPopup::sendConfigure(rect geometry) |
| * |
| * Sends a configure event to the client. \a geometry contains the window geometry |
| * relative to the upper left corner of the window geometry of the parent surface. |
| * |
| * This implicitly sends a configure event to the corresponding XdgSurface as well. |
| */ |
| |
| /*! |
| * Sends a configure event to the client. \a geometry contains the window geometry |
| * relative to the upper left corner of the window geometry of the parent surface. |
| * |
| * This implicitly sends a configure event to the corresponding QWaylandXdgSurface |
| * as well. |
| */ |
| uint QWaylandXdgPopup::sendConfigure(const QRect &geometry) |
| { |
| Q_D(QWaylandXdgPopup); |
| return d->sendConfigure(geometry); |
| } |
| |
| /*! |
| * \qmlmethod void QtWaylandCompositor::XdgPopup::sendPopupDone() |
| * \since 5.14 |
| * |
| * Dismiss the popup. According to the \c xdg-shell protocol this should make the |
| * client destroy the popup. |
| */ |
| |
| /*! |
| * \since 5.14 |
| * |
| * Dismiss the popup. According to the \c xdg-shell protocol this should make the |
| * client destroy the popup. |
| */ |
| void QWaylandXdgPopup::sendPopupDone() |
| { |
| Q_D(QWaylandXdgPopup); |
| d->send_popup_done(); |
| } |
| |
| /*! |
| * Returns the surface role for the QWaylandPopup. |
| */ |
| QWaylandSurfaceRole *QWaylandXdgPopup::role() |
| { |
| return &QWaylandXdgPopupPrivate::s_role; |
| } |
| |
| QWaylandXdgPopupPrivate::QWaylandXdgPopupPrivate(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parentXdgSurface, |
| QWaylandXdgPositioner *positioner, const QWaylandResource &resource) |
| : m_xdgSurface(xdgSurface) |
| , m_parentXdgSurface(parentXdgSurface) |
| , m_positionerData(positioner->m_data) |
| { |
| Q_ASSERT(m_positionerData.isComplete()); |
| init(resource.resource()); |
| |
| QWaylandXdgSurfacePrivate::get(m_xdgSurface)->setWindowType(Qt::WindowType::Popup); |
| |
| //TODO: Need an API for sending a different initial configure |
| sendConfigure(QRect(m_positionerData.unconstrainedPosition(), m_positionerData.size)); |
| } |
| |
| void QWaylandXdgPopupPrivate::handleAckConfigure(uint serial) |
| { |
| Q_Q(QWaylandXdgPopup); |
| ConfigureEvent config; |
| Q_FOREVER { |
| if (m_pendingConfigures.empty()) { |
| qWarning("Popup received an unexpected ack_configure!"); |
| return; |
| } |
| |
| // This won't work unless there always is a popup.configure for each xdgsurface.configure |
| config = m_pendingConfigures.takeFirst(); |
| |
| if (config.serial == serial) |
| break; |
| } |
| |
| if (m_geometry == config.geometry) |
| return; |
| |
| m_geometry = config.geometry; |
| emit q->configuredGeometryChanged(); |
| } |
| |
| uint QWaylandXdgPopupPrivate::sendConfigure(const QRect &geometry) |
| { |
| uint32_t serial = m_xdgSurface->surface()->compositor()->nextSerial(); |
| m_pendingConfigures.append(QWaylandXdgPopupPrivate::ConfigureEvent{geometry, serial}); |
| send_configure(geometry.x(), geometry.y(), geometry.width(), geometry.height()); |
| QWaylandXdgSurfacePrivate::get(m_xdgSurface)->send_configure(serial); |
| return serial; |
| } |
| |
| void QWaylandXdgPopupPrivate::xdg_popup_destroy(QtWaylandServer::xdg_popup::Resource *resource) |
| { |
| Q_UNUSED(resource); |
| qWarning() << Q_FUNC_INFO << "Not implemented"; //TODO |
| } |
| |
| void QWaylandXdgPopupPrivate::xdg_popup_grab(QtWaylandServer::xdg_popup::Resource *resource, wl_resource *seat, uint32_t serial) |
| { |
| Q_UNUSED(resource); |
| Q_UNUSED(serial); |
| Q_UNUSED(seat); |
| qWarning() << Q_FUNC_INFO << "Not implemented"; //TODO |
| //switch keyboard focus |
| //eventually send configure with activated. |
| } |
| |
| QWaylandSurfaceRole QWaylandXdgPopupPrivate::s_role("xdg_popup"); |
| |
| QWaylandXdgPositionerData::QWaylandXdgPositionerData() |
| : offset(0, 0) |
| {} |
| |
| bool QWaylandXdgPositionerData::isComplete() const |
| { |
| return size.width() > 0 && size.height() > 0 && anchorRect.size().width() > 0 && anchorRect.size().height() > 0; |
| } |
| |
| QPoint QWaylandXdgPositionerData::anchorPoint() const |
| { |
| int yPosition = 0; |
| if (anchorEdges & Qt::TopEdge) |
| yPosition = anchorRect.top(); |
| else if (anchorEdges & Qt::BottomEdge) |
| yPosition = anchorRect.bottom() + 1; |
| else |
| yPosition = anchorRect.top() + anchorRect.height() / 2; |
| |
| int xPosition = 0; |
| if (anchorEdges & Qt::LeftEdge) |
| xPosition = anchorRect.left(); |
| else if (anchorEdges & Qt::RightEdge) |
| xPosition = anchorRect.right() + 1; |
| else |
| xPosition = anchorRect.left() + anchorRect.width() / 2; |
| |
| return QPoint(xPosition, yPosition); |
| } |
| |
| QPoint QWaylandXdgPositionerData::unconstrainedPosition() const |
| { |
| int gravityOffsetY = 0; |
| if (gravityEdges & Qt::TopEdge) |
| gravityOffsetY = -size.height(); |
| else if (!(gravityEdges & Qt::BottomEdge)) |
| gravityOffsetY = -size.height() / 2; |
| |
| int gravityOffsetX = 0; |
| if (gravityEdges & Qt::LeftEdge) |
| gravityOffsetX = -size.width(); |
| else if (!(gravityEdges & Qt::RightEdge)) |
| gravityOffsetX = -size.width() / 2; |
| |
| QPoint gravityOffset(gravityOffsetX, gravityOffsetY); |
| return anchorPoint() + gravityOffset + offset; |
| } |
| |
| QWaylandXdgPositioner::QWaylandXdgPositioner(const QWaylandResource &resource) |
| { |
| init(resource.resource()); |
| } |
| |
| void QWaylandXdgPositioner::xdg_positioner_destroy_resource(QtWaylandServer::xdg_positioner::Resource *resource) |
| { |
| Q_UNUSED(resource); |
| delete this; |
| } |
| |
| void QWaylandXdgPositioner::xdg_positioner_destroy(QtWaylandServer::xdg_positioner::Resource *resource) |
| { |
| wl_resource_destroy(resource->handle); |
| } |
| |
| void QWaylandXdgPositioner::xdg_positioner_set_size(QtWaylandServer::xdg_positioner::Resource *resource, int32_t width, int32_t height) |
| { |
| if (width <= 0 || height <= 0) { |
| wl_resource_post_error(resource->handle, XDG_POSITIONER_ERROR_INVALID_INPUT, |
| "xdg_positioner.set_size requested with non-positive dimensions"); |
| return; |
| } |
| |
| QSize size(width, height); |
| m_data.size = size; |
| } |
| |
| void QWaylandXdgPositioner::xdg_positioner_set_anchor_rect(QtWaylandServer::xdg_positioner::Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) |
| { |
| if (width <= 0 || height <= 0) { |
| wl_resource_post_error(resource->handle, XDG_POSITIONER_ERROR_INVALID_INPUT, |
| "xdg_positioner.set_anchor_rect requested with non-positive dimensions"); |
| return; |
| } |
| |
| QRect anchorRect(x, y, width, height); |
| m_data.anchorRect = anchorRect; |
| } |
| |
| void QWaylandXdgPositioner::xdg_positioner_set_anchor(QtWaylandServer::xdg_positioner::Resource *resource, uint32_t anchor) |
| { |
| Qt::Edges anchorEdges = convertToEdges(xdg_positioner::anchor(anchor)); |
| |
| if ((anchorEdges & Qt::BottomEdge && anchorEdges & Qt::TopEdge) || |
| (anchorEdges & Qt::LeftEdge && anchorEdges & Qt::RightEdge)) { |
| wl_resource_post_error(resource->handle, XDG_POSITIONER_ERROR_INVALID_INPUT, |
| "xdg_positioner.set_anchor requested with parallel edges"); |
| return; |
| } |
| |
| m_data.anchorEdges = anchorEdges; |
| } |
| |
| void QWaylandXdgPositioner::xdg_positioner_set_gravity(QtWaylandServer::xdg_positioner::Resource *resource, uint32_t gravity) |
| { |
| Qt::Edges gravityEdges = convertToEdges(xdg_positioner::gravity(gravity)); |
| |
| if ((gravityEdges & Qt::BottomEdge && gravityEdges & Qt::TopEdge) || |
| (gravityEdges & Qt::LeftEdge && gravityEdges & Qt::RightEdge)) { |
| wl_resource_post_error(resource->handle, XDG_POSITIONER_ERROR_INVALID_INPUT, |
| "xdg_positioner.set_gravity requested with parallel edges"); |
| return; |
| } |
| |
| m_data.gravityEdges = gravityEdges; |
| } |
| |
| void QWaylandXdgPositioner::xdg_positioner_set_constraint_adjustment(QtWaylandServer::xdg_positioner::Resource *resource, uint32_t constraint_adjustment) |
| { |
| Q_UNUSED(resource); |
| m_data.constraintAdjustments = constraint_adjustment; |
| } |
| |
| void QWaylandXdgPositioner::xdg_positioner_set_offset(QtWaylandServer::xdg_positioner::Resource *resource, int32_t x, int32_t y) |
| { |
| Q_UNUSED(resource); |
| m_data.offset = QPoint(x, y); |
| } |
| |
| QWaylandXdgPositioner *QWaylandXdgPositioner::fromResource(wl_resource *resource) |
| { |
| return QtWayland::fromResource<QWaylandXdgPositioner *>(resource); |
| } |
| |
| Qt::Edges QWaylandXdgPositioner::convertToEdges(anchor anchor) |
| { |
| switch (anchor) { |
| case anchor_none: |
| return Qt::Edges(); |
| case anchor_top: |
| return Qt::TopEdge; |
| case anchor_bottom: |
| return Qt::BottomEdge; |
| case anchor_left: |
| return Qt::LeftEdge; |
| case anchor_right: |
| return Qt::RightEdge; |
| case anchor_top_left: |
| return Qt::TopEdge | Qt::LeftEdge; |
| case anchor_bottom_left: |
| return Qt::BottomEdge | Qt::LeftEdge; |
| case anchor_top_right: |
| return Qt::TopEdge | Qt::RightEdge; |
| case anchor_bottom_right: |
| return Qt::BottomEdge | Qt::RightEdge; |
| default: |
| qWarning() << "Unknown Wayland xdg edge" << anchor; |
| return Qt::Edges(); |
| } |
| } |
| |
| Qt::Edges QWaylandXdgPositioner::convertToEdges(QWaylandXdgPositioner::gravity gravity) |
| { |
| return convertToEdges(anchor(gravity)); |
| } |
| |
| |
| QT_END_NAMESPACE |