blob: 817cb2e4a1215bda4aae087ca5d8c0c1c8da4e6e [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
** 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 <QWaylandCompositor>
#include "qwaylandxdgoutputv1_p.h"
#include "qwaylandoutput_p.h"
#include <wayland-server.h>
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<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 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<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