Revision control
++ // Copyright (C) 2017-2016 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
++ // Copyright (C) 2017 Klarälvdalens Datakonsult AB (KDAB).
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:critical reason:network-protocol
++
++ #include "qwaylandoutput.h"
++ #include "qwaylandoutput_p.h"
++
++ #include <QtWaylandCompositor/QWaylandCompositor>
++ #include <QtWaylandCompositor/QWaylandView>
++
++ #include <QtWaylandCompositor/private/qwaylandsurface_p.h>
++ #include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
++ #include <QtWaylandCompositor/private/qwaylandview_p.h>
++ #include <QtWaylandCompositor/private/qwaylandutils_p.h>
++
++ #include <QtCore/QCoreApplication>
++ #include <QtCore/QtMath>
++ #include <QtGui/QWindow>
++ #include <QtGui/QExposeEvent>
++ #include <QtGui/QScreen>
++ #include <private/qobject_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ static QtWaylandServer::wl_output::subpixel toWlSubpixel(const QWaylandOutput::Subpixel &value)
++ {
++ switch (value) {
++ case QWaylandOutput::SubpixelUnknown:
++ return QtWaylandServer::wl_output::subpixel_unknown;
++ case QWaylandOutput::SubpixelNone:
++ return QtWaylandServer::wl_output::subpixel_none;
++ case QWaylandOutput::SubpixelHorizontalRgb:
++ return QtWaylandServer::wl_output::subpixel_horizontal_rgb;
++ case QWaylandOutput::SubpixelHorizontalBgr:
++ return QtWaylandServer::wl_output::subpixel_horizontal_bgr;
++ case QWaylandOutput::SubpixelVerticalRgb:
++ return QtWaylandServer::wl_output::subpixel_vertical_rgb;
++ case QWaylandOutput::SubpixelVerticalBgr:
++ return QtWaylandServer::wl_output::subpixel_vertical_bgr;
++ default:
++ break;
++ }
++
++ return QtWaylandServer::wl_output::subpixel_unknown;
++ }
++
++ static QtWaylandServer::wl_output::transform toWlTransform(const QWaylandOutput::Transform &value)
++ {
++ switch (value) {
++ case QWaylandOutput::Transform90:
++ return QtWaylandServer::wl_output::transform_90;
++ case QWaylandOutput::Transform180:
++ return QtWaylandServer::wl_output::transform_180;
++ case QWaylandOutput::Transform270:
++ return QtWaylandServer::wl_output::transform_270;
++ case QWaylandOutput::TransformFlipped:
++ return QtWaylandServer::wl_output::transform_flipped;
++ case QWaylandOutput::TransformFlipped90:
++ return QtWaylandServer::wl_output::transform_flipped_90;
++ case QWaylandOutput::TransformFlipped180:
++ return QtWaylandServer::wl_output::transform_flipped_180;
++ case QWaylandOutput::TransformFlipped270:
++ return QtWaylandServer::wl_output::transform_flipped_270;
++ default:
++ break;
++ }
++
++ return QtWaylandServer::wl_output::transform_normal;
++ }
++
++ QWaylandOutputPrivate::QWaylandOutputPrivate()
++ {
++ }
++
++ QWaylandOutputPrivate::~QWaylandOutputPrivate()
++ {
++ }
++
++ void QWaylandOutputPrivate::output_bind_resource(Resource *resource)
++ {
++ sendGeometry(resource);
++
++ for (const QWaylandOutputMode &mode : std::as_const(modes))
++ sendMode(resource, mode);
++
++ maybeSendScale(resource, scaleFactor);
++ maybeSendDone(resource);
++ }
++
++ void QWaylandOutputPrivate::_q_handleMaybeWindowPixelSizeChanged()
++ {
++ if (!window)
++ return;
++
++ const QSize pixelSize = window->size() * window->devicePixelRatio();
++
++ if (pixelSize != windowPixelSize) {
++ windowPixelSize = pixelSize;
++ handleWindowPixelSizeChanged();
++ }
++ }
++
++ void QWaylandOutputPrivate::_q_handleWindowDestroyed()
++ {
++ Q_Q(QWaylandOutput);
++ window = nullptr;
++ emit q->windowChanged();
++ emit q->windowDestroyed();
++ }
++
++ void QWaylandOutputPrivate::sendGeometry(const Resource *resource)
++ {
++ send_geometry(resource->handle,
++ position.x(), position.y(),
++ physicalSize.width(), physicalSize.height(),
++ toWlSubpixel(subpixel), manufacturer, model,
++ toWlTransform(transform));
++ }
++
++ void QWaylandOutputPrivate::sendGeometryInfo()
++ {
++ const auto resources = resourceMap().values();
++ for (const Resource *resource : resources) {
++ sendGeometry(resource);
++ maybeSendDone(resource);
++ }
++ }
++
++ void QWaylandOutputPrivate::sendMode(const Resource *resource, const QWaylandOutputMode &mode)
++ {
++ quint32 flags = 0;
++ if (currentMode == modes.indexOf(mode))
++ flags |= QtWaylandServer::wl_output::mode_current;
++ if (preferredMode == modes.indexOf(mode))
++ flags |= QtWaylandServer::wl_output::mode_preferred;
++
++ send_mode(resource->handle, flags,
++ mode.size().width(), mode.size().height(),
++ mode.refreshRate());
++ }
++
++ void QWaylandOutputPrivate::sendModesInfo()
++ {
++ const auto resources = resourceMap().values();
++ for (const Resource *resource : resources) {
++ for (const QWaylandOutputMode &mode : std::as_const(modes))
++ sendMode(resource, mode);
++ maybeSendDone(resource);
++ }
++ }
++
++ void QWaylandOutputPrivate::maybeSendDone(const Resource *resource)
++ {
++ if (resource->version() >= 2)
++ send_done(resource->handle);
++ }
++
++ void QWaylandOutputPrivate::sendDone()
++ {
++ const auto values = resourceMap().values();
++ for (auto *resource : values)
++ maybeSendDone(resource);
++ }
++
++ void QWaylandOutputPrivate::handleWindowPixelSizeChanged()
++ {
++ Q_Q(QWaylandOutput);
++ Q_ASSERT(window);
++ if (sizeFollowsWindow && currentMode < modes.size()) {
++ if (currentMode >= 0) {
++ QWaylandOutputMode &mode = modes[currentMode];
++ mode.setSize(windowPixelSize);
++ emit q->geometryChanged();
++ if (!availableGeometry.isValid())
++ emit q->availableGeometryChanged();
++ sendModesInfo();
++ } else {
++ // We didn't add a mode during the initialization because the window
++ // size was invalid, let's add it now
++ int mHzRefreshRate = qFloor(window->screen()->refreshRate() * 1000);
++ QWaylandOutputMode mode(windowPixelSize, mHzRefreshRate);
++ if (mode.isValid()) {
++ modes.clear();
++ q->addMode(mode, true);
++ q->setCurrentMode(mode);
++ }
++ }
++ }
++ }
++
++ void QWaylandOutputPrivate::addView(QWaylandView *view, QWaylandSurface *surface)
++ {
++ for (int i = 0; i < surfaceViews.size(); i++) {
++ if (surface == surfaceViews.at(i).surface) {
++ if (!surfaceViews.at(i).views.contains(view)) {
++ surfaceViews[i].views.append(view);
++ }
++ return;
++ }
++ }
++
++ surfaceViews.append(QWaylandSurfaceViewMapper(surface,view));
++ }
++
++ void QWaylandOutputPrivate::removeView(QWaylandView *view, QWaylandSurface *surface)
++ {
++ Q_Q(QWaylandOutput);
++ for (int i = 0; i < surfaceViews.size(); i++) {
++ if (surface == surfaceViews.at(i).surface) {
++ bool removed = surfaceViews[i].views.removeOne(view);
++ if (removed && surfaceViews.at(i).views.isEmpty()) {
++ if (surfaceViews.at(i).has_entered)
++ q->surfaceLeave(surface);
++ surfaceViews.remove(i);
++ }
++ return;
++ }
++ }
++ qWarning("%s Could not find view %p for surface %p to remove. Possible invalid state", Q_FUNC_INFO, view, surface);
++ }
++
++ QWaylandOutput::QWaylandOutput()
++ : QWaylandObject(*new QWaylandOutputPrivate())
++ {
++ }
++
++ /*!
++ \qmltype WaylandOutput
++ \nativetype QWaylandOutput
++ \inqmlmodule QtWayland.Compositor
++ \since 5.8
++ \brief Provides access to a displayable area managed by the compositor.
++
++ The WaylandOutput manages a rectangular area within bounds of the compositor's
++ geometry, to use it for displaying client content. This could, for instance, be
++ a screen managed by the WaylandCompositor.
++
++ The type corresponds to the \c wl_output interface in the Wayland protocol.
++
++ \note If the compositor has multiple Wayland outputs, the \l Qt::AA_ShareOpenGLContexts
++ attribute must be set before the \l QGuiApplication object is constructed.
++ */
++
++ /*!
++ \class QWaylandOutput
++ \inmodule QtWaylandCompositor
++ \since 5.8
++ \brief The QWaylandOutput class represents a displayable area managed by the compositor.
++
++ The QWaylandOutput manages a rectangular area within bounds of the compositor's
++ geometry, to use it for displaying client content. This could, for instance, be
++ a screen managed by the WaylandCompositor.
++
++ The class corresponds to the \c wl_output interface in the Wayland protocol.
++ */
++
++ /*!
++ * Constructs a QWaylandOutput in \a compositor and with the specified \a window. The
++ * \l{QWaylandCompositor::create()}{create()} function must be called on the
++ * \a compositor before constructing a QWaylandOutput for it.
++ *
++ * The QWaylandOutput object is initialized later, in reaction to an event.
++ * At this point it is added as an output for the \a compositor. If it is the
++ * first QWaylandOutput object created for this \a compositor, it becomes the
++ * \l{QWaylandCompositor::defaultOutput()}{default output}.
++ */
++ QWaylandOutput::QWaylandOutput(QWaylandCompositor *compositor, QWindow *window)
++ : QWaylandObject(*new QWaylandOutputPrivate())
++ {
++ Q_D(QWaylandOutput);
++ d->compositor = compositor;
++ d->window = window;
++ QWaylandCompositorPrivate::get(compositor)->addPolishObject(this);
++ }
++
++ /*!
++ * Destroys the QWaylandOutput.
++ */
++ QWaylandOutput::~QWaylandOutput()
++ {
++ Q_D(QWaylandOutput);
++
++ const auto surfaceViews = d->surfaceViews; // intentional copy
++ for (const QWaylandSurfaceViewMapper &surfacemapper : surfaceViews) {
++ for (QWaylandView *view : surfacemapper.views) {
++ if (view->output() == this)
++ view->setOutput(nullptr);
++ }
++ }
++
++ if (d->compositor)
++ QWaylandCompositorPrivate::get(d->compositor)->removeOutput(this);
++ }
++
++ /*!
++ * \internal
++ */
++ void QWaylandOutput::initialize()
++ {
++ Q_D(QWaylandOutput);
++
++ Q_ASSERT(!d->initialized);
++ Q_ASSERT(d->compositor);
++ Q_ASSERT(d->compositor->isCreated());
++
++ if (!d->window && d->sizeFollowsWindow) {
++ qWarning("Setting QWaylandOutput::sizeFollowsWindow without a window has no effect");
++ }
++
++ // Replace modes with one that follows the window size and refresh rate,
++ // but only if window size is valid
++ if (d->window && d->sizeFollowsWindow) {
++ QWaylandOutputMode mode(d->window->size() * d->window->devicePixelRatio(),
++ qFloor(d->window->screen()->refreshRate() * 1000));
++ if (mode.isValid()) {
++ d->modes.clear();
++ addMode(mode, true);
++ setCurrentMode(mode);
++ }
++ }
++
++ QWaylandCompositorPrivate::get(d->compositor)->addOutput(this);
++
++ if (d->window) {
++ QObjectPrivate::connect(d->window, &QWindow::widthChanged, d, &QWaylandOutputPrivate::_q_handleMaybeWindowPixelSizeChanged);
++ QObjectPrivate::connect(d->window, &QWindow::heightChanged, d, &QWaylandOutputPrivate::_q_handleMaybeWindowPixelSizeChanged);
++ QObjectPrivate::connect(d->window, &QWindow::screenChanged, d, &QWaylandOutputPrivate::_q_handleMaybeWindowPixelSizeChanged);
++ QObjectPrivate::connect(d->window, &QObject::destroyed, d, &QWaylandOutputPrivate::_q_handleWindowDestroyed);
++ }
++
++ d->init(d->compositor->display(), 2);
++
++ d->initialized = true;
++ }
++
++ /*!
++ * Returns the QWaylandOutput corresponding to \a resource.
++ */
++ QWaylandOutput *QWaylandOutput::fromResource(wl_resource *resource)
++ {
++ if (auto p = QtWayland::fromResource<QWaylandOutputPrivate *>(resource))
++ return p->q_func();
++ return nullptr;
++ }
++
++ /*!
++ * \internal
++ */
++ struct ::wl_resource *QWaylandOutput::resourceForClient(QWaylandClient *client) const
++ {
++ Q_D(const QWaylandOutput);
++ QWaylandOutputPrivate::Resource *r = d->resourceMap().value(client->client());
++ if (r)
++ return r->handle;
++
++ return nullptr;
++ }
++
++ /*!
++ * Schedules a QEvent::UpdateRequest to be delivered to the QWaylandOutput's \l{window()}{window}.
++ *
++ * \sa QWindow::requestUpdate()
++ */
++ void QWaylandOutput::update()
++ {
++ Q_D(QWaylandOutput);
++ if (!d->window)
++ return;
++ d->window->requestUpdate();
++ }
++
++ /*!
++ * \qmlproperty WaylandCompositor QtWayland.Compositor::WaylandOutput::compositor
++ *
++ * This property holds the compositor displaying content on this WaylandOutput.
++ *
++ * \note This property can be set only once, before the WaylandOutput component
++ * is completed.
++ */
++
++ /*!
++ * \property QWaylandOutput::compositor
++ *
++ * This property holds the compositor displaying content on this QWaylandOutput.
++ *
++ * \note This property can be set only once, before the QWaylandOutput has been
++ * initialized.
++ */
++
++ /*!
++ * Returns the compositor for this QWaylandOutput.
++ */
++ QWaylandCompositor *QWaylandOutput::compositor() const
++ {
++ return d_func()->compositor;
++ }
++
++ /*!
++ * \internal
++ */
++ void QWaylandOutput::setCompositor(QWaylandCompositor *compositor)
++ {
++ Q_D(QWaylandOutput);
++
++ if (d->compositor == compositor)
++ return;
++
++ if (d->initialized) {
++ qWarning("Setting QWaylandCompositor %p on QWaylandOutput %p is not supported after QWaylandOutput has been initialized\n", compositor, this);
++ return;
++ }
++ if (d->compositor && d->compositor != compositor) {
++ qWarning("Possible initialization error. Moving QWaylandOutput %p between compositor instances.\n", this);
++ }
++
++ d->compositor = compositor;
++
++ QWaylandCompositorPrivate::get(compositor)->addPolishObject(this);
++ }
++
++ /*!
++ * \qmlproperty string QtWayland.Compositor::WaylandOutput::manufacturer
++ *
++ * This property holds a textual description of the manufacturer of the display
++ * managed by this WaylandOutput.
++ */
++
++ /*!
++ * \property QWaylandOutput::manufacturer
++ *
++ * This property holds a textual description of the manufacturer of the display
++ * managed by this QWaylandOutput.
++ */
++ QString QWaylandOutput::manufacturer() const
++ {
++ return d_func()->manufacturer;
++ }
++
++ void QWaylandOutput::setManufacturer(const QString &manufacturer)
++ {
++ Q_D(QWaylandOutput);
++
++ if (d->manufacturer == manufacturer)
++ return;
++
++ d->manufacturer = manufacturer;
++ d->sendGeometryInfo();
++ Q_EMIT manufacturerChanged();
++ }
++
++ /*!
++ * \qmlproperty string QtWayland.Compositor::WaylandOutput::model
++ *
++ * This property holds a textual description of the model of the display
++ * managed by this WaylandOutput.
++ */
++
++ /*!
++ * \property QWaylandOutput::model
++ *
++ * This property holds a textual description of the model of the display
++ * managed by this QWaylandOutput.
++ */
++ QString QWaylandOutput::model() const
++ {
++ return d_func()->model;
++ }
++
++ void QWaylandOutput::setModel(const QString &model)
++ {
++ Q_D(QWaylandOutput);
++
++ if (d->model == model)
++ return;
++
++ d->model = model;
++ d->sendGeometryInfo();
++ Q_EMIT modelChanged();
++ }
++
++ /*!
++ * \qmlproperty point QtWayland.Compositor::WaylandOutput::position
++ *
++ * This property holds the position of this WaylandOutput in the compositor's coordinate system.
++ */
++
++ /*!
++ * \property QWaylandOutput::position
++ *
++ * This property holds the position of this QWaylandOutput in the compositor's coordinate system.
++ */
++ QPoint QWaylandOutput::position() const
++ {
++ return d_func()->position;
++ }
++
++ void QWaylandOutput::setPosition(const QPoint &pt)
++ {
++ Q_D(QWaylandOutput);
++ if (d->position == pt)
++ return;
++
++ d->position = pt;
++
++ d->sendGeometryInfo();
++
++ Q_EMIT positionChanged();
++ Q_EMIT geometryChanged();
++ }
++
++ /*!
++ * Returns the list of modes.
++ */
++ QList<QWaylandOutputMode> QWaylandOutput::modes() const
++ {
++ Q_D(const QWaylandOutput);
++ return d->modes.toList();
++ }
++
++ /*!
++ * Adds the mode \a mode to the output and mark it as preferred
++ * if \a preferred is \c true.
++ * Please note there can only be one preferred mode.
++ */
++ void QWaylandOutput::addMode(const QWaylandOutputMode &mode, bool preferred)
++ {
++ Q_D(QWaylandOutput);
++
++ if (!mode.isValid()) {
++ qWarning("Cannot add an invalid mode");
++ return;
++ }
++
++ int index = d->modes.indexOf(mode);
++ if (index < 0) {
++ d->modes.append(mode);
++ index = d->modes.size() - 1;
++ }
++
++ if (preferred)
++ d->preferredMode = index;
++
++ emit modeAdded();
++ }
++
++ /*!
++ * Returns the output's size in pixels and refresh rate in mHz.
++ * If the current mode is not set it will return an invalid mode.
++ *
++ * \sa QWaylandOutput::modes
++ * \sa QWaylandOutputMode
++ */
++ QWaylandOutputMode QWaylandOutput::currentMode() const
++ {
++ Q_D(const QWaylandOutput);
++
++ return d->modes.value(d->currentMode);
++ }
++
++ /*!
++ * Sets the current mode.
++ * The mode \a mode must have been previously added.
++ *
++ * \sa QWaylandOutput::modes
++ * \sa QWaylandOutputMode
++ */
++ void QWaylandOutput::setCurrentMode(const QWaylandOutputMode &mode)
++ {
++ Q_D(QWaylandOutput);
++
++ int index = d->modes.indexOf(mode);
++ if (index < 0) {
++ qWarning("Cannot set an unknown QWaylandOutput mode as current");
++ return;
++ }
++
++ d->currentMode = index;
++
++ Q_EMIT currentModeChanged();
++ Q_EMIT geometryChanged();
++ if (!d->availableGeometry.isValid())
++ emit availableGeometryChanged();
++
++ d->sendModesInfo();
++ }
++
++ /*!
++ * \qmlproperty rect QtWayland.Compositor::WaylandOutput::geometry
++ *
++ * This property holds the geometry of the WaylandOutput.
++ */
++
++ /*!
++ * \property QWaylandOutput::geometry
++ *
++ * This property holds the geometry of the QWaylandOutput.
++ *
++ * \sa QWaylandOutput::currentMode
++ */
++ QRect QWaylandOutput::geometry() const
++ {
++ Q_D(const QWaylandOutput);
++ return QRect(d->position, currentMode().size());
++ }
++
++ /*!
++ * \qmlproperty rect QtWayland.Compositor::WaylandOutput::availableGeometry
++ *
++ * This property holds the geometry of the WaylandOutput available for displaying content.
++ * The available geometry is in output coordinates space, starts from 0,0 and it's as big
++ * as the output by default.
++ *
++ * \sa QWaylandOutput::geometry
++ */
++
++ /*!
++ * \property QWaylandOutput::availableGeometry
++ *
++ * This property holds the geometry of the QWaylandOutput available for displaying content.
++ * The available geometry is in output coordinates space, starts from 0,0 and it's as big
++ * as the output by default.
++ *
++ * \sa QWaylandOutput::currentMode, QWaylandOutput::geometry
++ */
++ QRect QWaylandOutput::availableGeometry() const
++ {
++ Q_D(const QWaylandOutput);
++
++ if (!d->availableGeometry.isValid())
++ return QRect(QPoint(0, 0), currentMode().size());
++
++ return d->availableGeometry;
++ }
++
++ void QWaylandOutput::setAvailableGeometry(const QRect &availableGeometry)
++ {
++ Q_D(QWaylandOutput);
++ if (d->availableGeometry == availableGeometry)
++ return;
++
++ if (availableGeometry.topLeft().x() < 0 || availableGeometry.topLeft().y() < 0)
++ qWarning("Available geometry should be a portion of the output");
++
++ d->availableGeometry = availableGeometry;
++
++ Q_EMIT availableGeometryChanged();
++ }
++
++ /*!
++ * \qmlproperty size QtWayland.Compositor::WaylandOutput::physicalSize
++ *
++ * This property holds the physical size of the WaylandOutput in millimeters.
++ *
++ * \sa QWaylandOutput::geometry
++ */
++
++ /*!
++ * \property QWaylandOutput::physicalSize
++ *
++ * This property holds the physical size of the QWaylandOutput in millimeters.
++ *
++ * \sa QWaylandOutput::geometry, QWaylandOutput::currentMode
++ */
++ QSize QWaylandOutput::physicalSize() const
++ {
++ return d_func()->physicalSize;
++ }
++
++ void QWaylandOutput::setPhysicalSize(const QSize &size)
++ {
++ Q_D(QWaylandOutput);
++ if (d->physicalSize == size)
++ return;
++
++ d->physicalSize = size;
++
++ d->sendGeometryInfo();
++
++ Q_EMIT physicalSizeChanged();
++ }
++
++ /*!
++ * \enum QWaylandOutput::Subpixel
++ *
++ * This enum type is used to specify the subpixel arrangement of a QWaylandOutput.
++ *
++ * \value SubpixelUnknown The subpixel arrangement is not set.
++ * \value SubpixelNone There are no subpixels.
++ * \value SubpixelHorizontalRgb The subpixels are arranged horizontally in red, green, blue order.
++ * \value SubpixelHorizontalBgr The subpixels are arranged horizontally in blue, green, red order.
++ * \value SubpixelVerticalRgb The subpixels are arranged vertically in red, green, blue order.
++ * \value SubpixelVerticalBgr The subpixels are arranged vertically in blue, green, red order.
++ *
++ * \sa QWaylandOutput::subpixel
++ */
++
++ /*!
++ * \qmlproperty enum QtWayland.Compositor::WaylandOutput::subpixel
++ *
++ * This property holds the subpixel arrangement of this WaylandOutput.
++ *
++ * \list
++ * \li WaylandOutput.SubpixelUnknown The subpixel arrangement is not set.
++ * \li WaylandOutput.SubpixelNone There are no subpixels.
++ * \li WaylandOutput.SubpixelHorizontalRgb The subpixels are arranged horizontally in red, green, blue order.
++ * \li WaylandOutput.SubpixelHorizontalBgr The subpixels are arranged horizontally in blue, green, red order.
++ * \li WaylandOutput.SubpixelVerticalRgb The subpixels are arranged vertically in red, green, blue order.
++ * \li WaylandOutput.SubpixelVerticalBgr The subpixels are arranged vertically in blue, green, red order.
++ * \endlist
++ *
++ * The default is WaylandOutput.SubpixelUnknown.
++ */
++
++ /*!
++ * \property QWaylandOutput::subpixel
++ *
++ * This property holds the subpixel arrangement of this QWaylandOutput. The default is
++ * QWaylandOutput::SubpixelUnknown.
++ */
++ QWaylandOutput::Subpixel QWaylandOutput::subpixel() const
++ {
++ return d_func()->subpixel;
++ }
++
++ void QWaylandOutput::setSubpixel(const Subpixel &subpixel)
++ {
++ Q_D(QWaylandOutput);
++ if (d->subpixel == subpixel)
++ return;
++
++ d->subpixel = subpixel;
++
++ d->sendGeometryInfo();
++
++ Q_EMIT subpixelChanged();
++ }
++
++ /*! \enum QWaylandOutput::Transform
++ *
++ * This enum type is used to specify the orientation of a QWaylandOutput.
++ *
++ * \value TransformNormal The orientation is normal.
++ * \value Transform90 The orientation is rotated 90 degrees.
++ * \value Transform180 The orientation is rotated 180 degrees.
++ * \value Transform270 The orientation is rotated 270 degrees.
++ * \value TransformFlipped The orientation is mirrored.
++ * \value TransformFlipped90 The orientation is mirrored, and rotated 90 degrees.
++ * \value TransformFlipped180 The orientation is mirrored, and rotated 180 degrees.
++ * \value TransformFlipped270 The orientation is mirrored, and rotated 270 degrees.
++ *
++ * \sa QWaylandOutput::transform
++ */
++
++ /*!
++ * \qmlproperty enum QtWayland.Compositor::WaylandOutput::transform
++ *
++ * This property holds the transformation that the QWaylandCompositor applies to a surface
++ * to compensate for the orientation of the QWaylandOutput.
++ *
++ * \list
++ * \li WaylandOutput.TransformNormal The orientation is normal.
++ * \li WaylandOutput.Transform90 The orientation is rotated 90 degrees.
++ * \li WaylandOutput.Transform180 The orientation is rotated 180 degrees.
++ * \li WaylandOutput.Transform270 The orientation is rotated 270 degrees.
++ * \li WaylandOutput.TransformFlipped The orientation is mirrored.
++ * \li WaylandOutput.TransformFlipped90 The orientation is mirrored, then rotated 90 degrees.
++ * \li WaylandOutput.TransformFlipped180 The orientation is mirrored, then rotated 180 degrees.
++ * \li WaylandOutput.TransformFlipped270 The orientation is mirrored, then rotated 270 degrees.
++ * \endlist
++ *
++ * The default is WaylandOutput.TransformNormal.
++ */
++
++ /*!
++ * \property QWaylandOutput::transform
++ *
++ * This property holds the transformation that the QWaylandCompositor applies to a surface
++ * to compensate for the orientation of the QWaylandOutput.
++ *
++ * The default is QWaylandOutput::TransformNormal.
++ */
++ QWaylandOutput::Transform QWaylandOutput::transform() const
++ {
++ return d_func()->transform;
++ }
++
++ void QWaylandOutput::setTransform(const Transform &transform)
++ {
++ Q_D(QWaylandOutput);
++ if (d->transform == transform)
++ return;
++
++ d->transform = transform;
++
++ d->sendGeometryInfo();
++
++ Q_EMIT transformChanged();
++ }
++
++ /*!
++ * \qmlproperty int QtWayland.Compositor::WaylandOutput::scaleFactor
++ *
++ * This property holds the factor by which the WaylandCompositor scales surface buffers
++ * before they are displayed. It is used on high density output devices where unscaled content
++ * would be too small to be practical. The client can in turn set the scale factor of its
++ * buffer to match the output if it prefers to provide high resolution content that is
++ * suitable for the output device.
++ *
++ * The default is 1 (no scaling).
++ */
++
++ /*!
++ * \property QWaylandOutput::scaleFactor
++ *
++ * This property holds the factor by which the QWaylandCompositor scales surface buffers
++ * before they are displayed. This is used on high density output devices where unscaled content
++ * would be too small to be practical. The client can in turn set the scale factor of its
++ * buffer to match the output if it prefers to provide high resolution content that is
++ * suitable for the output device.
++ *
++ * The default is 1 (no scaling).
++ */
++ int QWaylandOutput::scaleFactor() const
++ {
++ return d_func()->scaleFactor;
++ }
++
++ void QWaylandOutputPrivate::maybeSendScale(const Resource *resource,
++ int scale)
++ {
++ if (resource->version() >= 2)
++ send_scale(resource->handle, scale);
++ }
++
++ void QWaylandOutput::setScaleFactor(int scale)
++ {
++ Q_D(QWaylandOutput);
++ if (d->scaleFactor == scale)
++ return;
++
++ d->scaleFactor = scale;
++
++ const auto resMap = d->resourceMap();
++ for (QWaylandOutputPrivate::Resource *resource : resMap) {
++ d->maybeSendScale(resource, scale);
++ d->maybeSendDone(resource);
++ }
++
++ Q_EMIT scaleFactorChanged();
++ }
++
++ /*!
++ * \qmlproperty bool QtWayland.Compositor::WaylandOutput::sizeFollowsWindow
++ * \default false
++ *
++ * This property controls whether the size of the WaylandOutput matches the
++ * size of its window.
++ *
++ * If this property is true, all modes previously added are replaced by a
++ * mode that matches window size and screen refresh rate. It is especially
++ * useful in a nested compositor setup during development.
++ */
++
++ /*!
++ * \property QWaylandOutput::sizeFollowsWindow
++ *
++ * This property controls whether the size of the QWaylandOutput matches the
++ * size of its window.
++ *
++ * If this property is true, all modes previously added are replaced by a
++ * mode that matches window size and screen refresh rate.
++ *
++ * The default is false.
++ */
++ bool QWaylandOutput::sizeFollowsWindow() const
++ {
++ return d_func()->sizeFollowsWindow;
++ }
++
++ void QWaylandOutput::setSizeFollowsWindow(bool follow)
++ {
++ Q_D(QWaylandOutput);
++
++ if (follow != d->sizeFollowsWindow) {
++ d->sizeFollowsWindow = follow;
++ Q_EMIT sizeFollowsWindowChanged();
++ }
++ }
++
++ /*!
++ * \qmlproperty Window QtWayland.Compositor::WaylandOutput::window
++ *
++ * This property holds the Window for this WaylandOutput.
++ *
++ * \note This property can be set only once, before the WaylandOutput
++ * component is completed.
++ */
++
++ /*!
++ * \property QWaylandOutput::window
++ *
++ * This property holds the QWindow for this QWaylandOutput.
++ */
++ QWindow *QWaylandOutput::window() const
++ {
++ return d_func()->window;
++ }
++
++ void QWaylandOutput::setWindow(QWindow *window)
++ {
++ Q_D(QWaylandOutput);
++ if (d->window == window)
++ return;
++ if (d->initialized) {
++ qWarning("Setting QWindow %p on QWaylandOutput %p is not supported after QWaylandOutput has been initialized\n", window, this);
++ return;
++ }
++ d->window = window;
++ emit windowChanged();
++ }
++
++ /*!
++ * Informs QWaylandOutput that a frame has started.
++ */
++ void QWaylandOutput::frameStarted()
++ {
++ Q_D(QWaylandOutput);
++
++ d->canSendFrameCallbacks = true;
++
++ for (const QWaylandSurfaceViewMapper &surfacemapper : std::as_const(d->surfaceViews)) {
++ if (QWaylandView *primaryView = surfacemapper.maybePrimaryView()) {
++ if (Q_LIKELY(!QWaylandViewPrivate::get(primaryView)->independentFrameCallback))
++ surfacemapper.surface->frameStarted();
++ }
++ }
++ }
++
++ /*!
++ * Sends pending frame callbacks.
++ */
++ void QWaylandOutput::sendFrameCallbacks()
++ {
++ Q_D(QWaylandOutput);
++
++ if (Q_UNLIKELY(!d->canSendFrameCallbacks))
++ return;
++
++ d->canSendFrameCallbacks = false;
++
++ for (int i = 0; i < d->surfaceViews.size(); i++) {
++ const QWaylandSurfaceViewMapper &surfacemapper = d->surfaceViews.at(i);
++ if (surfacemapper.surface && surfacemapper.surface->hasContent()) {
++ if (!surfacemapper.has_entered) {
++ surfaceEnter(surfacemapper.surface);
++ d->surfaceViews[i].has_entered = true;
++ }
++ if (auto primaryView = surfacemapper.maybePrimaryView()) {
++ if (!QWaylandViewPrivate::get(primaryView)->independentFrameCallback)
++ surfacemapper.surface->sendFrameCallbacks();
++ }
++ }
++ }
++ wl_display_flush_clients(d->compositor->display());
++ }
++
++ /*!
++ * \internal
++ */
++ void QWaylandOutput::surfaceEnter(QWaylandSurface *surface)
++ {
++ if (!surface)
++ return;
++
++ auto clientResource = resourceForClient(surface->client());
++ if (clientResource)
++ QWaylandSurfacePrivate::get(surface)->send_enter(clientResource);
++ }
++
++ /*!
++ * \internal
++ */
++ void QWaylandOutput::surfaceLeave(QWaylandSurface *surface)
++ {
++ if (!surface || !surface->client())
++ return;
++
++ auto *clientResource = resourceForClient(surface->client());
++ if (clientResource)
++ QWaylandSurfacePrivate::get(surface)->send_leave(clientResource);
++ }
++
++ /*!
++ * \internal
++ */
++ bool QWaylandOutput::event(QEvent *event)
++ {
++ if (event->type() == QEvent::Polish)
++ initialize();
++ return QObject::event(event);
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_qwaylandoutput.cpp"