Revision control

++ // Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:critical reason:network-protocol
++
++ #include <QWaylandCompositor>
++
++ #include "qwaylandxdgoutputv1_p.h"
++ #include "qwaylandoutput_p.h"
++
++ #include <wayland-server.h>
++
++ QT_BEGIN_NAMESPACE
++ /*!
++ * \qmltype XdgOutputV1
++ * \nativetype QWaylandXdgOutputV1
++ * \inqmlmodule QtWayland.Compositor.XdgShell
++ * \brief Represents a display in a compositor.
++ */
++
++ /*!
++ * \class QWaylandXdgOutputV1
++ * \inmodule QtWaylandCompositor
++ * \brief Represents a display in a compositor.
++ */
++
++ /*!
++ * \qmltype XdgOutputManagerV1
++ * \nativetype QWaylandXdgOutputManagerV1
++ * \inqmlmodule QtWayland.Compositor.XdgShell
++ * \since 5.14
++ * \brief Provides an extension for describing outputs in a desktop oriented fashion.
++ *
++ * The XdgOutputManagerV1 extension provides a way for a compositor to describe outputs in a way
++ * that is more in line with the concept of an output on desktop oriented systems.
++ *
++ * Some information may not make sense in other applications such as IVI systems.
++ *
++ * Typically the global compositor space on a desktop system is made of a
++ * contiguous or overlapping set of rectangular regions.
++ *
++ * XdgOutputManagerV1 corresponds to the Wayland interface, \c zxdg_output_manager_v1.
++ *
++ * To provide the functionality of the extension in a compositor, create an instance of the
++ * XdgOutputManagerV1 component and add it to the list of extensions supported by the compositor,
++ * and associated each XdgOutputV1 with its WaylandOutput:
++ *
++ * \qml
++ * import QtWayland.Compositor
++ *
++ * WaylandCompositor {
++ * XdgOutputManagerV1 {
++ * WaylandOutput {
++ * id: output1
++ *
++ * position: Qt.point(0, 0)
++ * window: Window {}
++ *
++ * XdgOutputV1 {
++ * name: "WL-1"
++ * logicalPosition: output1.position
++ * logicalSize: Qt.size(output1.geometry.width / output1.scaleFactor,
++ * output1.geometry.height / output1.scaleFactor)
++ * }
++ * }
++ *
++ * WaylandOutput {
++ * id: output2
++ *
++ * position: Qt.point(800, 0)
++ * window: Window {}
++ *
++ * XdgOutputV1 {
++ * name: "WL-2"
++ * logicalPosition: output2.position
++ * logicalSize: Qt.size(output2.geometry.width / output2.scaleFactor,
++ * output2.geometry.height / output2.scaleFactor)
++ * }
++ * }
++ * }
++ * }
++ * \endqml
++ */
++
++ /*!
++ * \class QWaylandXdgOutputManagerV1
++ * \inmodule QtWaylandCompositor
++ * \since 5.14
++ * \brief Provides an extension for describing outputs in a desktop oriented fashion.
++ *
++ * The QWaylandXdgOutputManagerV1 extension provides a way for a compositor to describe outputs in a way
++ * that is more in line with the concept of an output on desktop oriented systems.
++ *
++ * Some information may not make sense in other applications such as IVI systems.
++ *
++ * QWaylandXdgOutputManagerV1 corresponds to the Wayland interface, \c zxdg_output_manager_v1.
++ */
++
++ /*!
++ * Constructs a QWaylandXdgOutputManagerV1 object.
++ */
++ QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1()
++ : QWaylandCompositorExtensionTemplate<QWaylandXdgOutputManagerV1>(*new QWaylandXdgOutputManagerV1Private())
++ {
++ }
++
++ /*!
++ * Constructs a QWaylandXdgOutputManagerV1 object for the provided \a compositor.
++ */
++ QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandCompositor *compositor)
++ : QWaylandCompositorExtensionTemplate<QWaylandXdgOutputManagerV1>(compositor, *new QWaylandXdgOutputManagerV1Private())
++ {
++ }
++
++ // QWaylandXdgOutputManagerV1Private
++
++ /*!
++ * Initializes the extension.
++ */
++ void QWaylandXdgOutputManagerV1::initialize()
++ {
++ Q_D(QWaylandXdgOutputManagerV1);
++
++ QWaylandCompositorExtensionTemplate::initialize();
++ QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
++ if (!compositor) {
++ qCWarning(qLcWaylandCompositor) << "Failed to find QWaylandCompositor when initializing QWaylandXdgOutputManagerV1";
++ return;
++ }
++ d->init(compositor->display(), d->interfaceVersion());
++ }
++
++ /*!
++ * Returns the Wayland interface for QWaylandXdgOutputManagerV1.
++ */
++ const wl_interface *QWaylandXdgOutputManagerV1::interface()
++ {
++ return QWaylandXdgOutputManagerV1Private::interface();
++ }
++
++ // QWaylandXdgOutputManagerV1Private
++
++ void QWaylandXdgOutputManagerV1Private::registerXdgOutput(QWaylandOutput *output, QWaylandXdgOutputV1 *xdgOutput)
++ {
++ if (!xdgOutputs.contains(output)) {
++ xdgOutputs[output] = xdgOutput;
++ }
++ }
++
++ void QWaylandXdgOutputManagerV1Private::unregisterXdgOutput(QWaylandOutput *output)
++ {
++ xdgOutputs.remove(output);
++ }
++
++ QWaylandXdgOutputV1 *QWaylandXdgOutputManagerV1Private::xdgOutput(QWaylandOutput *output) const
++ {
++ return xdgOutputs.value(output);
++ }
++
++ void QWaylandXdgOutputManagerV1Private::zxdg_output_manager_v1_get_xdg_output(Resource *resource,
++ uint32_t id,
++ wl_resource *outputResource)
++ {
++ Q_Q(QWaylandXdgOutputManagerV1);
++
++ // Verify if the associated output exist
++ auto *output = QWaylandOutput::fromResource(outputResource);
++ if (!output) {
++ qCWarning(qLcWaylandCompositor,
++ "The client is requesting a QWaylandXdgOutputV1 for a "
++ "QWaylandOutput that doesn't exist");
++ wl_resource_post_error(resource->handle, WL_DISPLAY_ERROR_INVALID_OBJECT, "output not found");
++ return;
++ }
++
++ // Do we have a QWaylandXdgOutputV1 for this output?
++ if (!xdgOutputs.contains(output)) {
++ qCWarning(qLcWaylandCompositor,
++ "The client is requesting a QWaylandXdgOutputV1 that the compositor "
++ "didn't create before");
++ wl_resource_post_error(resource->handle, WL_DISPLAY_ERROR_INVALID_OBJECT,
++ "compositor didn't create a QWaylandXdgOutputV1 for this zxdg_output_v1 object");
++ return;
++ }
++
++ // Bind QWaylandXdgOutputV1 and initialize
++ auto *xdgOutput = xdgOutputs[output];
++ auto *xdgOutputPrivate = QWaylandXdgOutputV1Private::get(xdgOutput);
++ Q_ASSERT(xdgOutputPrivate);
++ xdgOutputPrivate->setManager(q);
++ xdgOutputPrivate->setOutput(output);
++ xdgOutputPrivate->add(resource->client(), id, qMin(resource->version(), QWaylandXdgOutputV1Private::interfaceVersion()));
++ }
++
++ // QWaylandXdgOutputV1
++
++ QWaylandXdgOutputV1::QWaylandXdgOutputV1()
++ : QObject(*new QWaylandXdgOutputV1Private)
++ {
++ }
++
++ QWaylandXdgOutputV1::QWaylandXdgOutputV1(QWaylandOutput *output, QWaylandXdgOutputManagerV1 *manager)
++ : QObject(*new QWaylandXdgOutputV1Private)
++ {
++ Q_D(QWaylandXdgOutputV1);
++
++ // Set members before emitting changed signals so that handlers will
++ // see both already set and not nullptr, avoiding potential crashes
++ d->manager = manager;
++ d->output = output;
++
++ QWaylandXdgOutputManagerV1Private::get(d->manager)->registerXdgOutput(output, this);
++
++ emit managerChanged();
++ emit outputChanged();
++ }
++
++ QWaylandXdgOutputV1::~QWaylandXdgOutputV1()
++ {
++ Q_D(QWaylandXdgOutputV1);
++
++ if (d->manager)
++ QWaylandXdgOutputManagerV1Private::get(d->manager)->unregisterXdgOutput(d->output);
++ }
++
++ /*!
++ * \qmlproperty XdgOutputManagerV1 XdgOutputV1::manager
++ * \readonly
++ *
++ * This property holds the object that manages this XdgOutputV1.
++ */
++ /*!
++ * \property QWaylandXdgOutputV1::manager
++ * \readonly
++ * \internal
++ *
++ * This property holds the object that manages this QWaylandXdgOutputV1.
++ */
++ QWaylandXdgOutputManagerV1 *QWaylandXdgOutputV1::manager() const
++ {
++ Q_D(const QWaylandXdgOutputV1);
++ return d->manager;
++ }
++
++ /*!
++ * \qmlproperty WaylandOutput XdgOutputV1::output
++ * \readonly
++ *
++ * This property holds the WaylandOutput associated with this XdgOutputV1.
++ */
++ /*!
++ * \property QWaylandXdgOutputV1::output
++ * \readonly
++ *
++ * This property holds the QWaylandOutput associated with this QWaylandXdgOutputV1.
++ */
++ QWaylandOutput *QWaylandXdgOutputV1::output() const
++ {
++ Q_D(const QWaylandXdgOutputV1);
++ return d->output;
++ }
++
++ /*!
++ * \qmlproperty string XdgOutputV1::name
++ *
++ * This property holds the name of this output.
++ *
++ * The naming convention is compositor defined, but limited to alphanumeric
++ * characters and dashes ("-"). Each name is unique and will also remain
++ * consistent across sessions with the same hardware and software configuration.
++ *
++ * Examples of names include "HDMI-A-1", "WL-1", "X11-1" etc...
++ * However don't assume the name reflects the underlying technology.
++ *
++ * Changing this property after initialization doesn't take effect.
++ */
++ /*!
++ * \property QWaylandXdgOutputV1::name
++ *
++ * This property holds the name of this output.
++ *
++ * The naming convention is compositor defined, but limited to alphanumeric
++ * characters and dashes ("-"). Each name is unique and will also remain
++ * consistent across sessions with the same hardware and software configuration.
++ *
++ * Examples of names include "HDMI-A-1", "WL-1", "X11-1" etc...
++ * However don't assume the name reflects the underlying technology.
++ *
++ * Changing this property after initialization doesn't take effect.
++ */
++ QString QWaylandXdgOutputV1::name() const
++ {
++ Q_D(const QWaylandXdgOutputV1);
++ return d->name;
++ }
++
++ void QWaylandXdgOutputV1::setName(const QString &name)
++ {
++ Q_D(QWaylandXdgOutputV1);
++
++ if (d->name == name)
++ return;
++
++ // Can't change after clients bound to xdg-output
++ if (d->initialized) {
++ qCWarning(qLcWaylandCompositor, "QWaylandXdgOutputV1::name cannot be changed after initialization");
++ return;
++ }
++
++ d->name = name;
++ emit nameChanged();
++ }
++
++ /*!
++ * \qmlproperty string XdgOutputV1::description
++ *
++ * This property holds the description of this output.
++ *
++ * No convention is defined for the description.
++ *
++ * Changing this property after initialization doesn't take effect.
++ */
++ /*!
++ * \property QWaylandXdgOutputV1::description
++ *
++ * This property holds the description of this output.
++ *
++ * No convention is defined for the description.
++ *
++ * Changing this property after initialization doesn't take effect.
++ */
++ QString QWaylandXdgOutputV1::description() const
++ {
++ Q_D(const QWaylandXdgOutputV1);
++ return d->description;
++ }
++
++ void QWaylandXdgOutputV1::setDescription(const QString &description)
++ {
++ Q_D(QWaylandXdgOutputV1);
++
++ if (d->description == description)
++ return;
++
++ // Can't change after clients bound to xdg-output
++ if (d->initialized) {
++ qCWarning(qLcWaylandCompositor, "QWaylandXdgOutputV1::description cannot be changed after initialization");
++ return;
++ }
++
++ d->description = description;
++ emit descriptionChanged();
++ }
++
++ /*!
++ * \qmlproperty point XdgOutputV1::logicalPosition
++ *
++ * This property holds the coordinates of the output within the global compositor space.
++ *
++ * The default value is 0,0.
++ */
++ /*!
++ * \property QWaylandXdgOutputV1::logicalPosition
++ *
++ * This property holds the coordinates of the output within the global compositor space.
++ *
++ * The default value is 0,0.
++ */
++ QPoint QWaylandXdgOutputV1::logicalPosition() const
++ {
++ Q_D(const QWaylandXdgOutputV1);
++ return d->logicalPos;
++ }
++
++ void QWaylandXdgOutputV1::setLogicalPosition(const QPoint &position)
++ {
++ Q_D(QWaylandXdgOutputV1);
++
++ if (d->logicalPos == position)
++ return;
++
++ d->logicalPos = position;
++ if (d->initialized) {
++ d->sendLogicalPosition(position);
++ d->sendDone();
++ }
++ emit logicalPositionChanged();
++ emit logicalGeometryChanged();
++ }
++
++ /*!
++ * \qmlproperty size XdgOutputV1::logicalSize
++ *
++ * This property holds the size of the output in the global compositor space.
++ *
++ * The default value is -1,-1 which is invalid.
++ *
++ * Please remember that this is the logical size, not the physical size.
++ * For example, for a WaylandOutput mode 3840x2160 and a scale factor 2:
++ * \list
++ * \li A compositor not scaling the surface buffers, will report a logical size of 3840x2160.
++ * \li A compositor automatically scaling the surface buffers, will report a logical size of 1920x1080.
++ * \li A compositor using a fractional scale of 1.5, will report a logical size of 2560x1620.
++ * \endlist
++ */
++ /*!
++ * \property QWaylandXdgOutputV1::logicalSize
++ *
++ * This property holds the size of the output in the global compositor space.
++ *
++ * The default value is -1,-1 which is invalid.
++ *
++ * Please remember that this is the logical size, not the physical size.
++ * For example, for a WaylandOutput mode 3840x2160 and a scale factor 2:
++ * \list
++ * \li A compositor not scaling the surface buffers, will report a logical size of 3840x2160.
++ * \li A compositor automatically scaling the surface buffers, will report a logical size of 1920x1080.
++ * \li A compositor using a fractional scale of 1.5, will report a logical size of 2560x1620.
++ * \endlist
++ */
++ QSize QWaylandXdgOutputV1::logicalSize() const
++ {
++ Q_D(const QWaylandXdgOutputV1);
++ return d->logicalSize;
++ }
++
++ void QWaylandXdgOutputV1::setLogicalSize(const QSize &size)
++ {
++ Q_D(QWaylandXdgOutputV1);
++
++ if (d->logicalSize == size)
++ return;
++
++ d->logicalSize = size;
++ if (d->initialized) {
++ d->sendLogicalSize(size);
++ d->sendDone();
++ }
++ emit logicalSizeChanged();
++ emit logicalGeometryChanged();
++ }
++
++ /*!
++ * \qmlproperty rect XdgOutputV1::logicalGeometry
++ * \readonly
++ *
++ * This property holds the position and size of the output in the global compositor space.
++ * It's the combination of the logical position and logical size.
++ *
++ * \sa XdgOutputV1::logicalPosition
++ * \sa XdgOutputV1::logicalSize
++ */
++ /*!
++ * \property QWaylandXdgOutputV1::logicalGeometry
++ * \readonly
++ *
++ * This property holds the position and size of the output in the global compositor space.
++ * It's the combination of the logical position and logical size.
++ *
++ * \sa QWaylandXdgOutputV1::logicalPosition
++ * \sa QWaylandXdgOutputV1::logicalSize
++ */
++ QRect QWaylandXdgOutputV1::logicalGeometry() const
++ {
++ Q_D(const QWaylandXdgOutputV1);
++ return QRect(d->logicalPos, d->logicalSize);
++ }
++
++ // QWaylandXdgOutputV1Private
++
++ void QWaylandXdgOutputV1Private::sendLogicalPosition(const QPoint &position)
++ {
++ const auto values = resourceMap().values();
++ for (auto *resource : values)
++ send_logical_position(resource->handle, position.x(), position.y());
++ }
++
++ void QWaylandXdgOutputV1Private::sendLogicalSize(const QSize &size)
++ {
++ const auto values = resourceMap().values();
++ for (auto *resource : values)
++ send_logical_size(resource->handle, size.width(), size.height());
++ }
++
++ void QWaylandXdgOutputV1Private::maybeSendDone(const Resource *resource)
++ {
++ if (resource->version() < 3)
++ send_done(resource->handle);
++ }
++
++ void QWaylandXdgOutputV1Private::sendDone()
++ {
++ const auto values = resourceMap().values();
++ for (auto *resource : values)
++ maybeSendDone(resource);
++
++ QWaylandOutputPrivate::get(output)->sendDone();
++ }
++
++ void QWaylandXdgOutputV1Private::setManager(QWaylandXdgOutputManagerV1 *_manager)
++ {
++ Q_Q(QWaylandXdgOutputV1);
++
++ if (!_manager) {
++ qCWarning(qLcWaylandCompositor,
++ "Cannot associate a null QWaylandXdgOutputManagerV1 to QWaylandXdgOutputV1 %p", this);
++ return;
++ }
++
++ if (manager == _manager)
++ return;
++
++ if (manager) {
++ qCWarning(qLcWaylandCompositor,
++ "Cannot associate a different QWaylandXdgOutputManagerV1 to QWaylandXdgOutputV1 %p "
++ "after initialization", this);
++ return;
++ }
++
++ manager = _manager;
++ emit q->managerChanged();
++ }
++
++ void QWaylandXdgOutputV1Private::setOutput(QWaylandOutput *_output)
++ {
++ Q_Q(QWaylandXdgOutputV1);
++
++ if (!_output) {
++ qCWarning(qLcWaylandCompositor,
++ "Cannot associate a null QWaylandOutput to QWaylandXdgOutputV1 %p", this);
++ return;
++ }
++
++ if (output == _output)
++ return;
++
++ if (output) {
++ qCWarning(qLcWaylandCompositor,
++ "Cannot associate a different QWaylandOutput to QWaylandXdgOutputV1 %p "
++ "after initialization", this);
++ return;
++ }
++
++ // Assign output above manager, to make both values valid in handlers
++ output = _output;
++
++ if (!manager) {
++ // Try to find the manager from the output parents
++ for (auto *p = output->parent(); p != nullptr; p = p->parent()) {
++ if (auto *m = qobject_cast<QWaylandXdgOutputManagerV1 *>(p)) {
++ manager = m;
++ emit q->managerChanged();
++ break;
++ }
++ }
++ }
++
++ emit q->outputChanged();
++
++ // Register the output
++ if (manager)
++ QWaylandXdgOutputManagerV1Private::get(manager)->registerXdgOutput(output, q);
++ }
++
++ void QWaylandXdgOutputV1Private::zxdg_output_v1_bind_resource(Resource *resource)
++ {
++ send_logical_position(resource->handle, logicalPos.x(), logicalPos.y());
++ send_logical_size(resource->handle, logicalSize.width(), logicalSize.height());
++ if (resource->version() >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION)
++ send_name(resource->handle, name);
++ if (resource->version() >= ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION)
++ send_description(resource->handle, description);
++ maybeSendDone(resource);
++
++ QWaylandOutputPrivate *output_d = QWaylandOutputPrivate::get(output);
++ auto *outputResource = output_d->resourceMap().value(resource->client());
++ if (outputResource)
++ output_d->maybeSendDone(outputResource);
++
++ initialized = true;
++ }
++
++ void QWaylandXdgOutputV1Private::zxdg_output_v1_destroy(Resource *resource)
++ {
++ wl_resource_destroy(resource->handle);
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_qwaylandxdgoutputv1.cpp"