diff options
Diffstat (limited to 'src/compositor/extensions/qwaylandxdgoutputv1.cpp')
-rw-r--r-- | src/compositor/extensions/qwaylandxdgoutputv1.cpp | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/src/compositor/extensions/qwaylandxdgoutputv1.cpp b/src/compositor/extensions/qwaylandxdgoutputv1.cpp new file mode 100644 index 000000000..035d8dd3d --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgoutputv1.cpp @@ -0,0 +1,565 @@ +// Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QWaylandCompositor> + +#include "qwaylandxdgoutputv1_p.h" +#include "qwaylandoutput_p.h" + +#include <wayland-server.h> + +QT_BEGIN_NAMESPACE + +/*! + * \qmltype XdgOutputManagerV1 + * \instantiates 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; + QWaylandOutputPrivate::get(output)->xdgOutput = xdgOutput; + } +} + +void QWaylandXdgOutputManagerV1Private::unregisterXdgOutput(QWaylandOutput *output) +{ + xdgOutputs.remove(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 + * + * 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()); + needToSendDone = true; +} + +void QWaylandXdgOutputV1Private::sendLogicalSize(const QSize &size) +{ + const auto values = resourceMap().values(); + for (auto *resource : values) + send_logical_size(resource->handle, size.width(), size.height()); + needToSendDone = true; +} + +void QWaylandXdgOutputV1Private::sendDone() +{ + if (needToSendDone) { + const auto values = resourceMap().values(); + for (auto *resource : values) { + if (resource->version() < 3) + send_done(resource->handle); + } + needToSendDone = false; + } +} + +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); + send_done(resource->handle); + + initialized = true; +} + +void QWaylandXdgOutputV1Private::zxdg_output_v1_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +QT_END_NAMESPACE + +#include "moc_qwaylandxdgoutputv1.cpp" |