| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017-2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> |
| ** Copyright (C) 2017 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtWaylandCompositor module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** 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 https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) any later version |
| ** approved by the KDE Free Qt Foundation. The licenses are as published by |
| ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qwaylandsurface.h" |
| #include "qwaylandsurface_p.h" |
| |
| #include "wayland_wrapper/qwlbuffermanager_p.h" |
| #include "wayland_wrapper/qwlregion_p.h" |
| #include <QtWaylandCompositor/private/qtwaylandcompositorglobal_p.h> |
| #if QT_CONFIG(wayland_datadevice) |
| #include "wayland_wrapper/qwldatadevice_p.h" |
| #include "wayland_wrapper/qwldatadevicemanager_p.h" |
| #endif |
| |
| #include "qwaylandinputmethodcontrol_p.h" |
| |
| #include <QtWaylandCompositor/QWaylandCompositor> |
| #include <QtWaylandCompositor/QWaylandClient> |
| #include <QtWaylandCompositor/QWaylandView> |
| #include <QtWaylandCompositor/QWaylandBufferRef> |
| |
| #include <QtWaylandCompositor/private/qwaylandcompositor_p.h> |
| #include <QtWaylandCompositor/private/qwaylandview_p.h> |
| #include <QtWaylandCompositor/private/qwaylandseat_p.h> |
| #include <QtWaylandCompositor/private/qwaylandutils_p.h> |
| |
| #include <QtCore/private/qobject_p.h> |
| |
| #include <QtGui/QGuiApplication> |
| #include <QtGui/QScreen> |
| |
| #include <QtCore/QDebug> |
| #include <QtCore/QtMath> |
| |
| QT_BEGIN_NAMESPACE |
| |
| namespace QtWayland { |
| class FrameCallback { |
| public: |
| FrameCallback(QWaylandSurface *surf, wl_resource *res) |
| : surface(surf) |
| , resource(res) |
| { |
| wl_resource_set_implementation(res, nullptr, this, destroyCallback); |
| } |
| ~FrameCallback() |
| { |
| } |
| void destroy() |
| { |
| if (resource) |
| wl_resource_destroy(resource); |
| else |
| delete this; |
| } |
| void send(uint time) |
| { |
| wl_callback_send_done(resource, time); |
| wl_resource_destroy(resource); |
| } |
| static void destroyCallback(wl_resource *res) |
| { |
| FrameCallback *_this = static_cast<FrameCallback *>(wl_resource_get_user_data(res)); |
| if (_this->surface) |
| QWaylandSurfacePrivate::get(_this->surface)->removeFrameCallback(_this); |
| delete _this; |
| } |
| QWaylandSurface *surface = nullptr; |
| wl_resource *resource = nullptr; |
| bool canSend = false; |
| }; |
| } |
| static QRegion infiniteRegion() { |
| return QRegion(QRect(QPoint(std::numeric_limits<int>::min(), std::numeric_limits<int>::min()), |
| QPoint(std::numeric_limits<int>::max(), std::numeric_limits<int>::max()))); |
| } |
| |
| #ifndef QT_NO_DEBUG |
| QList<QWaylandSurfacePrivate *> QWaylandSurfacePrivate::uninitializedSurfaces; |
| #endif |
| |
| QWaylandSurfacePrivate::QWaylandSurfacePrivate() |
| : inputRegion(infiniteRegion()) |
| { |
| pending.buffer = QWaylandBufferRef(); |
| pending.newlyAttached = false; |
| pending.inputRegion = infiniteRegion(); |
| pending.bufferScale = 1; |
| #ifndef QT_NO_DEBUG |
| addUninitializedSurface(this); |
| #endif |
| } |
| |
| QWaylandSurfacePrivate::~QWaylandSurfacePrivate() |
| { |
| for (int i = 0; i < views.size(); i++) { |
| QWaylandViewPrivate::get(views.at(i))->markSurfaceAsDestroyed(q_func()); |
| } |
| views.clear(); |
| |
| bufferRef = QWaylandBufferRef(); |
| |
| for (QtWayland::FrameCallback *c : qAsConst(pendingFrameCallbacks)) |
| c->destroy(); |
| for (QtWayland::FrameCallback *c : qAsConst(frameCallbacks)) |
| c->destroy(); |
| } |
| |
| void QWaylandSurfacePrivate::removeFrameCallback(QtWayland::FrameCallback *callback) |
| { |
| pendingFrameCallbacks.removeOne(callback); |
| frameCallbacks.removeOne(callback); |
| } |
| |
| void QWaylandSurfacePrivate::notifyViewsAboutDestruction() |
| { |
| Q_Q(QWaylandSurface); |
| const auto viewsCopy = views; // Views will be removed from the list when marked as destroyed |
| for (QWaylandView *view : viewsCopy) { |
| QWaylandViewPrivate::get(view)->markSurfaceAsDestroyed(q); |
| } |
| if (hasContent) { |
| hasContent = false; |
| emit q->hasContentChanged(); |
| } |
| } |
| |
| #ifndef QT_NO_DEBUG |
| void QWaylandSurfacePrivate::addUninitializedSurface(QWaylandSurfacePrivate *surface) |
| { |
| Q_ASSERT(!surface->isInitialized); |
| Q_ASSERT(!uninitializedSurfaces.contains(surface)); |
| uninitializedSurfaces.append(surface); |
| } |
| |
| void QWaylandSurfacePrivate::removeUninitializedSurface(QWaylandSurfacePrivate *surface) |
| { |
| Q_ASSERT(surface->isInitialized); |
| bool removed = uninitializedSurfaces.removeOne(surface); |
| Q_ASSERT(removed); |
| } |
| |
| bool QWaylandSurfacePrivate::hasUninitializedSurface() |
| { |
| return uninitializedSurfaces.size(); |
| } |
| #endif |
| |
| void QWaylandSurfacePrivate::surface_destroy_resource(Resource *) |
| { |
| Q_Q(QWaylandSurface); |
| notifyViewsAboutDestruction(); |
| |
| destroyed = true; |
| emit q->surfaceDestroyed(); |
| q->destroy(); |
| } |
| |
| void QWaylandSurfacePrivate::surface_destroy(Resource *resource) |
| { |
| wl_resource_destroy(resource->handle); |
| } |
| |
| void QWaylandSurfacePrivate::surface_attach(Resource *, struct wl_resource *buffer, int x, int y) |
| { |
| pending.buffer = QWaylandBufferRef(getBuffer(buffer)); |
| pending.offset = QPoint(x, y); |
| pending.newlyAttached = true; |
| } |
| |
| void QWaylandSurfacePrivate::surface_damage(Resource *, int32_t x, int32_t y, int32_t width, int32_t height) |
| { |
| pending.damage = pending.damage.united(QRect(x, y, width, height)); |
| } |
| |
| void QWaylandSurfacePrivate::surface_frame(Resource *resource, uint32_t callback) |
| { |
| Q_Q(QWaylandSurface); |
| struct wl_resource *frame_callback = wl_resource_create(resource->client(), &wl_callback_interface, wl_callback_interface.version, callback); |
| pendingFrameCallbacks << new QtWayland::FrameCallback(q, frame_callback); |
| } |
| |
| void QWaylandSurfacePrivate::surface_set_opaque_region(Resource *, struct wl_resource *region) |
| { |
| pending.opaqueRegion = region ? QtWayland::Region::fromResource(region)->region() : QRegion(); |
| } |
| |
| void QWaylandSurfacePrivate::surface_set_input_region(Resource *, struct wl_resource *region) |
| { |
| if (region) { |
| pending.inputRegion = QtWayland::Region::fromResource(region)->region(); |
| } else { |
| pending.inputRegion = infiniteRegion(); |
| } |
| } |
| |
| void QWaylandSurfacePrivate::surface_commit(Resource *) |
| { |
| Q_Q(QWaylandSurface); |
| |
| // Needed in order to know whether we want to emit signals later |
| QSize oldBufferSize = bufferSize; |
| QRectF oldSourceGeometry = sourceGeometry; |
| QSize oldDestinationSize = destinationSize; |
| bool oldHasContent = hasContent; |
| int oldBufferScale = bufferScale; |
| |
| // Update all internal state |
| if (pending.buffer.hasBuffer() || pending.newlyAttached) |
| bufferRef = pending.buffer; |
| bufferScale = pending.bufferScale; |
| bufferSize = bufferRef.size(); |
| QSize surfaceSize = bufferSize / bufferScale; |
| sourceGeometry = !pending.sourceGeometry.isValid() ? QRect(QPoint(), surfaceSize) : pending.sourceGeometry; |
| destinationSize = pending.destinationSize.isEmpty() ? sourceGeometry.size().toSize() : pending.destinationSize; |
| damage = pending.damage.intersected(QRect(QPoint(), destinationSize)); |
| hasContent = bufferRef.hasContent(); |
| frameCallbacks << pendingFrameCallbacks; |
| inputRegion = pending.inputRegion.intersected(QRect(QPoint(), destinationSize)); |
| opaqueRegion = pending.opaqueRegion.intersected(QRect(QPoint(), destinationSize)); |
| QPoint offsetForNextFrame = pending.offset; |
| |
| if (viewport) |
| viewport->checkCommittedState(); |
| |
| // Clear per-commit state |
| pending.buffer = QWaylandBufferRef(); |
| pending.offset = QPoint(); |
| pending.newlyAttached = false; |
| pending.damage = QRegion(); |
| pendingFrameCallbacks.clear(); |
| |
| // Notify buffers and views |
| if (auto *buffer = bufferRef.buffer()) |
| buffer->setCommitted(damage); |
| for (auto *view : qAsConst(views)) |
| view->bufferCommitted(bufferRef, damage); |
| |
| // Now all double-buffered state has been applied so it's safe to emit general signals |
| // i.e. we won't have inconsistensies such as mismatched surface size and buffer scale in |
| // signal handlers. |
| |
| emit q->damaged(damage); |
| |
| if (oldBufferSize != bufferSize) { |
| emit q->bufferSizeChanged(); |
| #if QT_DEPRECATED_SINCE(5, 13) |
| emit q->sizeChanged(); |
| #endif |
| } |
| |
| if (oldBufferScale != bufferScale) |
| emit q->bufferScaleChanged(); |
| |
| if (oldDestinationSize != destinationSize) |
| emit q->destinationSizeChanged(); |
| |
| if (oldSourceGeometry != sourceGeometry) |
| emit q->sourceGeometryChanged(); |
| |
| if (oldHasContent != hasContent) |
| emit q->hasContentChanged(); |
| |
| if (!offsetForNextFrame.isNull()) |
| emit q->offsetForNextFrame(offsetForNextFrame); |
| |
| emit q->redraw(); |
| } |
| |
| void QWaylandSurfacePrivate::surface_set_buffer_transform(Resource *resource, int32_t orientation) |
| { |
| Q_UNUSED(resource); |
| Q_Q(QWaylandSurface); |
| QScreen *screen = QGuiApplication::primaryScreen(); |
| bool isPortrait = screen->primaryOrientation() == Qt::PortraitOrientation; |
| Qt::ScreenOrientation oldOrientation = contentOrientation; |
| switch (orientation) { |
| case WL_OUTPUT_TRANSFORM_90: |
| contentOrientation = isPortrait ? Qt::InvertedLandscapeOrientation : Qt::PortraitOrientation; |
| break; |
| case WL_OUTPUT_TRANSFORM_180: |
| contentOrientation = isPortrait ? Qt::InvertedPortraitOrientation : Qt::InvertedLandscapeOrientation; |
| break; |
| case WL_OUTPUT_TRANSFORM_270: |
| contentOrientation = isPortrait ? Qt::LandscapeOrientation : Qt::InvertedPortraitOrientation; |
| break; |
| default: |
| contentOrientation = Qt::PrimaryOrientation; |
| } |
| if (contentOrientation != oldOrientation) |
| emit q->contentOrientationChanged(); |
| } |
| |
| void QWaylandSurfacePrivate::surface_set_buffer_scale(QtWaylandServer::wl_surface::Resource *resource, int32_t scale) |
| { |
| Q_UNUSED(resource); |
| pending.bufferScale = scale; |
| } |
| |
| QtWayland::ClientBuffer *QWaylandSurfacePrivate::getBuffer(struct ::wl_resource *buffer) |
| { |
| QtWayland::BufferManager *bufMan = QWaylandCompositorPrivate::get(compositor)->bufferManager(); |
| return bufMan->getBuffer(buffer); |
| } |
| |
| /*! |
| * \qmltype WaylandSurface |
| * \inqmlmodule QtWayland.Compositor |
| * \since 5.8 |
| * \brief Represents a rectangular area on an output device. |
| * |
| * This type encapsulates a rectangular area of pixels that is displayed on an output device. It |
| * corresponds to the interface \c wl_surface in the Wayland protocol. |
| */ |
| |
| /*! |
| * \class QWaylandSurface |
| * \inmodule QtWaylandCompositor |
| * \since 5.8 |
| * \brief The QWaylandSurface class represents a rectangular area on an output device. |
| * |
| * This class encapsulates a rectangular area of pixels that is displayed on an output device. It |
| * corresponds to the interface \c wl_surface in the Wayland protocol. |
| */ |
| |
| /*! |
| * Constructs a an uninitialized QWaylandSurface. |
| */ |
| QWaylandSurface::QWaylandSurface() |
| : QWaylandObject(*new QWaylandSurfacePrivate()) |
| { |
| } |
| |
| /*! |
| * Constructs and initializes a QWaylandSurface for the given \a compositor and \a client, and with the given \a id |
| * and \a version. |
| */ |
| QWaylandSurface::QWaylandSurface(QWaylandCompositor *compositor, QWaylandClient *client, uint id, int version) |
| : QWaylandObject(*new QWaylandSurfacePrivate()) |
| { |
| initialize(compositor, client, id, version); |
| } |
| |
| /*! |
| * \internal |
| */ |
| QWaylandSurface::QWaylandSurface(QWaylandSurfacePrivate &dptr) |
| : QWaylandObject(dptr) |
| { |
| } |
| |
| /*! |
| * Destroys the QWaylandSurface. |
| */ |
| QWaylandSurface::~QWaylandSurface() |
| { |
| Q_D(QWaylandSurface); |
| if (d->compositor) |
| QWaylandCompositorPrivate::get(d->compositor)->unregisterSurface(this); |
| d->notifyViewsAboutDestruction(); |
| } |
| |
| /*! |
| * \qmlmethod void QtWaylandCompositor::WaylandSurface::initialize(WaylandCompositor compositor, WaylandClient client, int id, int version) |
| * |
| * Initializes the WaylandSurface with the given \a compositor and \a client, and with the given \a id |
| * and \a version. |
| */ |
| |
| /*! |
| * Initializes the QWaylandSurface with the given \a compositor and \a client, and with the given \a id |
| * and \a version. |
| */ |
| void QWaylandSurface::initialize(QWaylandCompositor *compositor, QWaylandClient *client, uint id, int version) |
| { |
| Q_D(QWaylandSurface); |
| d->compositor = compositor; |
| d->client = client; |
| d->init(client->client(), id, version); |
| d->isInitialized = true; |
| #if QT_CONFIG(im) |
| d->inputMethodControl = new QWaylandInputMethodControl(this); |
| #endif |
| #ifndef QT_NO_DEBUG |
| QWaylandSurfacePrivate::removeUninitializedSurface(d); |
| #endif |
| } |
| |
| /*! |
| * Returns true if the QWaylandSurface has been initialized. |
| */ |
| bool QWaylandSurface::isInitialized() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->isInitialized; |
| } |
| |
| /*! |
| * \qmlproperty WaylandClient QtWaylandCompositor::WaylandSurface::client |
| * |
| * This property holds the client using this WaylandSurface. |
| */ |
| |
| /*! |
| * \property QWaylandSurface::client |
| * |
| * This property holds the client using this QWaylandSurface. |
| */ |
| QWaylandClient *QWaylandSurface::client() const |
| { |
| Q_D(const QWaylandSurface); |
| if (isDestroyed() || !compositor() || !compositor()->clients().contains(d->client)) |
| return nullptr; |
| |
| return d->client; |
| } |
| |
| /*! |
| * Holds the \c wl_client using this QWaylandSurface. |
| */ |
| ::wl_client *QWaylandSurface::waylandClient() const |
| { |
| if (auto *c = client()) |
| return c->client(); |
| |
| return nullptr; |
| } |
| |
| /*! |
| * \qmlproperty bool QtWaylandCompositor::WaylandSurface::hasContent |
| * |
| * This property holds whether the WaylandSurface has content. |
| */ |
| |
| /*! |
| * \property QWaylandSurface::hasContent |
| * |
| * This property holds whether the QWaylandSurface has content. |
| */ |
| bool QWaylandSurface::hasContent() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->hasContent; |
| } |
| |
| /*! |
| * \qmlproperty rect QtWaylandCompositor::WaylandSurface::sourceGeometry |
| * \since 5.13 |
| * |
| * This property describes the portion of the attached Wayland buffer that should |
| * be drawn on the screen. The coordinates are from the corner of the buffer and are |
| * scaled by \l bufferScale. |
| * |
| * \sa bufferScale |
| * \sa bufferSize |
| * \sa destinationSize |
| */ |
| |
| /*! |
| * \property QWaylandSurface::sourceGeometry |
| * \since 5.13 |
| * |
| * This property describes the portion of the attached QWaylandBuffer that should |
| * be drawn on the screen. The coordinates are from the corner of the buffer and are |
| * scaled by \l bufferScale. |
| * |
| * \sa bufferScale |
| * \sa bufferSize |
| * \sa destinationSize |
| */ |
| QRectF QWaylandSurface::sourceGeometry() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->sourceGeometry; |
| } |
| |
| /*! |
| * \qmlproperty size QtWaylandCompositor::WaylandSurface::destinationSize |
| * \since 5.13 |
| * |
| * This property holds the size of this WaylandSurface in surface coordinates. |
| * |
| * \sa bufferScale |
| * \sa bufferSize |
| */ |
| |
| /*! |
| * \property QWaylandSurface::destinationSize |
| * \since 5.13 |
| * |
| * This property holds the size of this WaylandSurface in surface coordinates. |
| * |
| * \sa bufferScale |
| * \sa bufferSize |
| */ |
| QSize QWaylandSurface::destinationSize() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->destinationSize; |
| } |
| |
| /*! |
| * \qmlproperty size QtWaylandCompositor::WaylandSurface::bufferSize |
| * |
| * This property holds the size of the current buffer of this WaylandSurface in pixels, |
| * not in surface coordinates. |
| * |
| * For the size in surface coordinates, use \l destinationSize instead. |
| * |
| * \sa destinationSize |
| * \sa bufferScale |
| */ |
| |
| /*! |
| * \property QWaylandSurface::bufferSize |
| * |
| * This property holds the size of the current buffer of this QWaylandSurface in pixels, |
| * not in surface coordinates. |
| * |
| * For the size in surface coordinates, use \l destinationSize instead. |
| * |
| * \sa destinationSize |
| * \sa bufferScale |
| */ |
| QSize QWaylandSurface::bufferSize() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->bufferSize; |
| } |
| |
| #if QT_DEPRECATED_SINCE(5, 13) |
| /*! |
| * \qmlproperty size QtWaylandCompositor::WaylandSurface::size |
| * \obsolete use bufferSize or destinationSize instead |
| * |
| * This property has been deprecated, use \l bufferSize or \l destinationSize instead. |
| */ |
| |
| /*! |
| * \property QWaylandSurface::size |
| * \obsolete use bufferSize or destinationSize instead |
| * |
| * This property has been deprecated, use \l bufferSize or \l destinationSize instead. |
| */ |
| QSize QWaylandSurface::size() const |
| { |
| return bufferSize(); |
| } |
| #endif |
| |
| /*! |
| * \qmlproperty size QtWaylandCompositor::WaylandSurface::bufferScale |
| * |
| * This property holds the WaylandSurface's buffer scale. The buffer scale lets |
| * a client supply higher resolution buffer data for use on high resolution |
| * outputs. |
| */ |
| |
| /*! |
| * \property QWaylandSurface::bufferScale |
| * |
| * This property holds the QWaylandSurface's buffer scale. The buffer scale |
| * lets a client supply higher resolution buffer data for use on high |
| * resolution outputs. |
| */ |
| int QWaylandSurface::bufferScale() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->bufferScale; |
| } |
| |
| /*! |
| * \qmlproperty enum QtWaylandCompositor::WaylandSurface::contentOrientation |
| * |
| * This property holds the orientation of the WaylandSurface's contents. |
| * |
| * \sa {WaylandOutput::transform}{WaylandOutput.transform} |
| */ |
| |
| /*! |
| * \property QWaylandSurface::contentOrientation |
| * |
| * This property holds the orientation of the QWaylandSurface's contents. |
| * |
| * \sa QWaylandOutput::transform |
| */ |
| Qt::ScreenOrientation QWaylandSurface::contentOrientation() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->contentOrientation; |
| } |
| |
| /*! |
| * \enum QWaylandSurface::Origin |
| * |
| * This enum type is used to specify the origin of a QWaylandSurface's buffer. |
| * |
| * \value OriginTopLeft The origin is the top left corner of the buffer. |
| * \value OriginBottomLeft The origin is the bottom left corner of the buffer. |
| */ |
| |
| /*! |
| * \qmlproperty enum QtWaylandCompositor::WaylandSurface::origin |
| * |
| * This property holds the origin of the WaylandSurface's buffer, or |
| * WaylandSurface.OriginTopLeft if the surface has no buffer. |
| * |
| * It can have the following values: |
| * \list |
| * \li WaylandSurface.OriginTopLeft The origin is the top left corner of the buffer. |
| * \li WaylandSurface.OriginBottomLeft The origin is the bottom left corner of the buffer. |
| * \endlist |
| */ |
| |
| /*! |
| * \property QWaylandSurface::origin |
| * |
| * This property holds the origin of the QWaylandSurface's buffer, or |
| * QWaylandSurface::OriginTopLeft if the surface has no buffer. |
| */ |
| QWaylandSurface::Origin QWaylandSurface::origin() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->bufferRef.origin(); |
| } |
| |
| /*! |
| * Returns the compositor for this QWaylandSurface. |
| */ |
| QWaylandCompositor *QWaylandSurface::compositor() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->compositor; |
| } |
| |
| /*! |
| * Prepares all frame callbacks for sending. |
| */ |
| void QWaylandSurface::frameStarted() |
| { |
| Q_D(QWaylandSurface); |
| for (QtWayland::FrameCallback *c : qAsConst(d->frameCallbacks)) |
| c->canSend = true; |
| } |
| |
| /*! |
| * Sends pending frame callbacks. |
| */ |
| void QWaylandSurface::sendFrameCallbacks() |
| { |
| Q_D(QWaylandSurface); |
| uint time = d->compositor->currentTimeMsecs(); |
| int i = 0; |
| while (i < d->frameCallbacks.size()) { |
| if (d->frameCallbacks.at(i)->canSend) { |
| d->frameCallbacks.at(i)->surface = nullptr; |
| d->frameCallbacks.at(i)->send(time); |
| d->frameCallbacks.removeAt(i); |
| } else { |
| i++; |
| } |
| } |
| } |
| |
| /*! |
| * Returns \c true if the QWaylandSurface's input region contains the point \a p. |
| * Otherwise returns \c false. |
| */ |
| bool QWaylandSurface::inputRegionContains(const QPoint &p) const |
| { |
| Q_D(const QWaylandSurface); |
| return d->inputRegion.contains(p); |
| } |
| |
| /*! |
| * Returns \c true if the QWaylandSurface's input region contains the point \a position. |
| * Otherwise returns \c false. |
| * |
| * \since 5.14 |
| */ |
| bool QWaylandSurface::inputRegionContains(const QPointF &position) const |
| { |
| Q_D(const QWaylandSurface); |
| // QRegion::contains operates in integers. If a region has a rect (0,0,10,10), (0,0) is |
| // inside while (10,10) is outside. Therefore, we can't use QPoint::toPoint(), which will |
| // round upwards, meaning the point (-0.25,-0.25) would be rounded to (0,0) and count as |
| // being inside the region, and similarly, a point (9.75,9.75) inside the region would be |
| // rounded upwards and count as being outside the region. |
| const QPoint floored(qFloor(position.x()), qFloor(position.y())); |
| return d->inputRegion.contains(floored); |
| } |
| |
| /*! |
| * \qmlmethod void QtWaylandCompositor::WaylandSurface::destroy() |
| * |
| * Destroys the WaylandSurface. |
| */ |
| |
| /*! |
| * Destroys the QWaylandSurface. |
| */ |
| void QWaylandSurface::destroy() |
| { |
| Q_D(QWaylandSurface); |
| d->deref(); |
| } |
| |
| /*! |
| * \qmlmethod bool QtWaylandCompositor::WaylandSurface::isDestroyed() |
| * |
| * Returns \c true if the WaylandSurface has been destroyed. Otherwise returns \c false. |
| */ |
| |
| /*! |
| * Returns true if the QWaylandSurface has been destroyed. Otherwise returns false. |
| */ |
| bool QWaylandSurface::isDestroyed() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->destroyed; |
| } |
| |
| /*! |
| * \qmlproperty bool QtWaylandCompositor::WaylandSurface::cursorSurface |
| * |
| * This property holds whether the WaylandSurface is a cursor surface. |
| */ |
| |
| /*! |
| * \property QWaylandSurface::cursorSurface |
| * |
| * This property holds whether the QWaylandSurface is a cursor surface. |
| */ |
| void QWaylandSurface::markAsCursorSurface(bool cursorSurface) |
| { |
| Q_D(QWaylandSurface); |
| if (d->isCursorSurface == cursorSurface) |
| return; |
| |
| d->isCursorSurface = cursorSurface; |
| emit cursorSurfaceChanged(); |
| } |
| |
| bool QWaylandSurface::isCursorSurface() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->isCursorSurface; |
| } |
| |
| /*! |
| * \qmlproperty bool QtWaylandCompositor::WaylandSurface::inhibitsIdle |
| * \since 5.14 |
| * |
| * This property holds whether this surface is intended to inhibit the idle |
| * behavior of the compositor such as screen blanking, locking and screen saving. |
| * |
| * \sa IdleInhibitManagerV1 |
| */ |
| |
| /*! |
| * \property QWaylandSurface::inhibitsIdle |
| * \since 5.14 |
| * |
| * This property holds whether this surface is intended to inhibit the idle |
| * behavior of the compositor such as screen blanking, locking and screen saving. |
| * |
| * \sa QWaylandIdleInhibitManagerV1 |
| */ |
| bool QWaylandSurface::inhibitsIdle() const |
| { |
| Q_D(const QWaylandSurface); |
| return !d->idleInhibitors.isEmpty(); |
| } |
| |
| #if QT_CONFIG(im) |
| QWaylandInputMethodControl *QWaylandSurface::inputMethodControl() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->inputMethodControl; |
| } |
| #endif |
| |
| /*! |
| * Updates the surface with the compositor's retained clipboard selection. Although |
| * this is done automatically when the surface receives keyboard focus, this |
| * function is useful for updating clients which do not have keyboard focus. |
| */ |
| #if QT_CONFIG(clipboard) |
| void QWaylandSurface::updateSelection() |
| { |
| Q_D(QWaylandSurface); |
| QWaylandSeat *seat = d->compositor->defaultSeat(); |
| if (seat) { |
| const QtWayland::DataDevice *dataDevice = QWaylandSeatPrivate::get(seat)->dataDevice(); |
| if (dataDevice) { |
| QWaylandCompositorPrivate::get(d->compositor)->dataDeviceManager()->offerRetainedSelection( |
| dataDevice->resourceMap().value(d->resource()->client())->handle); |
| } |
| } |
| } |
| #endif |
| |
| /*! |
| * Returns this QWaylandSurface's primary view. |
| * |
| * \sa QWaylandView::advance(), QWaylandSurface::setPrimaryView() |
| */ |
| QWaylandView *QWaylandSurface::primaryView() const |
| { |
| Q_D(const QWaylandSurface); |
| if (d->views.isEmpty()) |
| return nullptr; |
| return d->views.first(); |
| } |
| |
| /*! |
| * Sets this QWaylandSurface's primary view to \a view, in case there are |
| * multiple views of this surface. The primary view is the view that |
| * governs the client's refresh rate. It takes care of discarding buffer |
| * references when QWaylandView::advance() is called. See the documentation |
| * for QWaylandView::advance() for more details. |
| * |
| * In shell surface integrations, such as QWaylandWlShellIntegration and |
| * QWaylandXdgShellV5Integration, maximize and fullscreen requests from the |
| * client will only have an effect if the integration has the primary view |
| * of the surface. |
| * |
| * \sa QWaylandView::advance() |
| */ |
| void QWaylandSurface::setPrimaryView(QWaylandView *view) |
| { |
| Q_D(QWaylandSurface); |
| |
| if (!view) |
| return; |
| |
| int index = d->views.indexOf(view); |
| |
| if (index < 0) { |
| view->setSurface(this); |
| index = d->views.indexOf(view); |
| } |
| |
| d->views.move(index, 0); |
| } |
| |
| /*! |
| * Returns the views for this QWaylandSurface. |
| */ |
| QList<QWaylandView *> QWaylandSurface::views() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->views; |
| } |
| |
| /*! |
| * Returns the QWaylandSurface corresponding to the Wayland resource \a resource. |
| */ |
| QWaylandSurface *QWaylandSurface::fromResource(::wl_resource *resource) |
| { |
| if (auto p = QtWayland::fromResource<QWaylandSurfacePrivate *>(resource)) |
| return p->q_func(); |
| return nullptr; |
| } |
| |
| /*! |
| * Returns the Wayland resource corresponding to this QWaylandSurface. |
| */ |
| struct wl_resource *QWaylandSurface::resource() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->resource()->handle; |
| } |
| |
| /*! |
| * Sets a \a role on the surface. A role defines how a surface will be mapped on screen; without a role |
| * a surface is supposed to be hidden. Only one role can be set on a surface, at all times. Although |
| * setting the same role many times is allowed, attempting to change the role of a surface will trigger |
| * a protocol error to the \a errorResource and send an \a errorCode to the client. |
| * |
| * Returns true if a role can be assigned; false otherwise. |
| */ |
| bool QWaylandSurface::setRole(QWaylandSurfaceRole *role, wl_resource *errorResource, uint32_t errorCode) |
| { |
| Q_D(QWaylandSurface); |
| |
| if (d->role && d->role != role) { |
| wl_resource_post_error(errorResource, errorCode, |
| "Cannot assign role %s to wl_surface@%d, already has role %s\n", |
| role->name().constData(), wl_resource_get_id(resource()), |
| d->role->name().constData()); |
| return false; |
| } |
| |
| d->role = role; |
| return true; |
| } |
| |
| QWaylandSurfaceRole *QWaylandSurface::role() const |
| { |
| Q_D(const QWaylandSurface); |
| return d->role; |
| } |
| |
| QWaylandSurfacePrivate *QWaylandSurfacePrivate::get(QWaylandSurface *surface) |
| { |
| return surface ? surface->d_func() : nullptr; |
| } |
| |
| void QWaylandSurfacePrivate::ref() |
| { |
| ++refCount; |
| } |
| |
| void QWaylandSurfacePrivate::deref() |
| { |
| if (--refCount == 0) |
| QWaylandCompositorPrivate::get(compositor)->destroySurface(q_func()); |
| } |
| |
| void QWaylandSurfacePrivate::refView(QWaylandView *view) |
| { |
| if (views.contains(view)) |
| return; |
| |
| views.append(view); |
| ref(); |
| view->bufferCommitted(bufferRef, QRect(QPoint(0,0), bufferRef.size())); |
| } |
| |
| void QWaylandSurfacePrivate::derefView(QWaylandView *view) |
| { |
| int nViews = views.removeAll(view); |
| |
| for (int i = 0; i < nViews && refCount > 0; i++) { |
| deref(); |
| } |
| } |
| |
| void QWaylandSurfacePrivate::initSubsurface(QWaylandSurface *parent, wl_client *client, int id, int version) |
| { |
| Q_Q(QWaylandSurface); |
| QWaylandSurface *oldParent = nullptr; // TODO: implement support for switching parents |
| |
| subsurface = new Subsurface(this); |
| subsurface->init(client, id, version); |
| subsurface->parentSurface = parent->d_func(); |
| emit q->parentChanged(parent, oldParent); |
| emit parent->childAdded(q); |
| } |
| |
| void QWaylandSurfacePrivate::Subsurface::subsurface_set_position(wl_subsurface::Resource *resource, int32_t x, int32_t y) |
| { |
| Q_UNUSED(resource); |
| position = QPoint(x,y); |
| emit surface->q_func()->subsurfacePositionChanged(position); |
| |
| } |
| |
| void QWaylandSurfacePrivate::Subsurface::subsurface_place_above(wl_subsurface::Resource *resource, struct wl_resource *sibling) |
| { |
| Q_UNUSED(resource); |
| emit surface->q_func()->subsurfacePlaceAbove(QWaylandSurface::fromResource(sibling)); |
| } |
| |
| void QWaylandSurfacePrivate::Subsurface::subsurface_place_below(wl_subsurface::Resource *resource, struct wl_resource *sibling) |
| { |
| Q_UNUSED(resource); |
| emit surface->q_func()->subsurfacePlaceBelow(QWaylandSurface::fromResource(sibling)); |
| } |
| |
| void QWaylandSurfacePrivate::Subsurface::subsurface_set_sync(wl_subsurface::Resource *resource) |
| { |
| Q_UNUSED(resource); |
| // TODO: sync/desync implementation |
| qDebug() << Q_FUNC_INFO; |
| } |
| |
| void QWaylandSurfacePrivate::Subsurface::subsurface_set_desync(wl_subsurface::Resource *resource) |
| { |
| Q_UNUSED(resource); |
| // TODO: sync/desync implementation |
| qDebug() << Q_FUNC_INFO; |
| } |
| |
| /*! |
| * \qmlsignal QtWaylandCompositor::WaylandSurface::childAdded(WaylandSurface child) |
| * |
| * This signal is emitted when a wl_subsurface, \a child, has been added to the surface. |
| */ |
| |
| /*! |
| * \fn void QWaylandSurface::childAdded(QWaylandSurface *child) |
| * |
| * This signal is emitted when a wl_subsurface, \a child, has been added to the surface. |
| */ |
| |
| /*! |
| * \qmlsignal QtWaylandCompositor::WaylandSurface::surfaceDestroyed() |
| * |
| * This signal is emitted when the corresponding wl_surface is destroyed. |
| */ |
| |
| /*! |
| * \fn void QWaylandSurface::surfaceDestroyed() |
| * |
| * This signal is emitted when the corresponing wl_surface is destroyed. |
| */ |
| |
| /*! |
| * \qmlsignal void QtWaylandCompositor::WaylandSurface::dragStarted(WaylandDrag drag) |
| * |
| * This signal is emitted when a \a drag has started from this surface. |
| */ |
| |
| /*! |
| * \fn void QWaylandSurface::dragStarted(QWaylandDrag *drag) |
| * |
| * This signal is emitted when a \a drag has started from this surface. |
| */ |
| |
| /*! |
| * \fn void damaged(const QRegion &rect) |
| * |
| * This signal is emitted when the client tells the compositor that a particular part of, or |
| * possibly the entire surface has been updated, so the compositor can redraw that part. |
| * |
| * While the compositor APIs take care of redrawing automatically, this function may be useful |
| * if you require a specific, custom behavior. |
| */ |
| |
| /*! |
| * \fn void parentChanged(QWaylandSurface *newParent, QWaylandSurface *oldParent) |
| * |
| * This signal is emitted when the client has requested that this surface should be a |
| * subsurface of \a newParent. |
| */ |
| |
| QT_END_NAMESPACE |