/**************************************************************************** ** ** Copyright (C) 2019 Pier Luigi Fiorini ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtWaylandCompositor module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPLv3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "qwaylandxdgoutputv1_p.h" #include "qwaylandoutput_p.h" #include QT_BEGIN_NAMESPACE /*! * \qmltype XdgOutputManagerV1 * \inqmlmodule QtWayland.Compositor * \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 \QtMinorVersion * import QtWayland.Compositor 1.\1 * * 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(*new QWaylandXdgOutputManagerV1Private()) { } /*! * Constructs a QWaylandXdgOutputManagerV1 object for the provided \a compositor. */ QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandCompositor *compositor) : QWaylandCompositorExtensionTemplate(compositor, *new QWaylandXdgOutputManagerV1Private()) { } // QWaylandXdgOutputManagerV1Private /*! * Initializes the extension. */ void QWaylandXdgOutputManagerV1::initialize() { Q_D(QWaylandXdgOutputManagerV1); QWaylandCompositorExtensionTemplate::initialize(); QWaylandCompositor *compositor = static_cast(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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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(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