diff options
70 files changed, 4725 insertions, 207 deletions
diff --git a/.qmake.conf b/.qmake.conf index 2108ac6f..138038d5 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,3 @@ load(qt_build_config) -MODULE_VERSION = 5.9.3 +MODULE_VERSION = 5.10.0 diff --git a/examples/wayland/minimal-cpp/window.cpp b/examples/wayland/minimal-cpp/window.cpp index 89337183..a23bba3a 100644 --- a/examples/wayland/minimal-cpp/window.cpp +++ b/examples/wayland/minimal-cpp/window.cpp @@ -55,6 +55,7 @@ #include <QMatrix4x4> #include <QOpenGLFunctions> #include <QOpenGLTexture> +#include <QRandomGenerator> Window::Window() : m_compositor(0) @@ -74,7 +75,7 @@ static int sillyrandom(int range) { if (range <= 0) range = 200; - return qrand() % range; + return QRandomGenerator::bounded(range); } void Window::paintGL() @@ -90,8 +91,6 @@ void Window::paintGL() functions->glEnable(GL_BLEND); functions->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - qsrand(31337); - Q_FOREACH (View *view, m_compositor->views()) { if (view->isCursor()) continue; diff --git a/examples/wayland/multi-screen/qml/Screen.qml b/examples/wayland/multi-screen/qml/Screen.qml index 81514071..21f2d126 100644 --- a/examples/wayland/multi-screen/qml/Screen.qml +++ b/examples/wayland/multi-screen/qml/Screen.qml @@ -48,7 +48,7 @@ ** ****************************************************************************/ -import QtQuick 2.0 +import QtQuick 2.8 import QtWayland.Compositor 1.0 import QtQuick.Window 2.3 @@ -95,5 +95,9 @@ WaylandOutput { visible: mouseTracker.containsMouse } } + Shortcut { + sequence: "Ctrl+Alt+Backspace" + onActivated: Qt.quit() + } } } diff --git a/examples/wayland/pure-qml/qml/main.qml b/examples/wayland/pure-qml/qml/main.qml index 6ee450d4..9ed4acd2 100644 --- a/examples/wayland/pure-qml/qml/main.qml +++ b/examples/wayland/pure-qml/qml/main.qml @@ -49,7 +49,7 @@ ****************************************************************************/ import QtQuick 2.0 -import QtWayland.Compositor 1.0 +import QtWayland.Compositor 1.1 WaylandCompositor { id: comp @@ -83,8 +83,9 @@ WaylandCompositor { } } + property variant viewsBySurface: ({}) + XdgShellV5 { - property variant viewsBySurface: ({}) onXdgSurfaceCreated: { var item = chromeComponent.createObject(defaultOutput.surfaceArea, { "shellSurface": xdgSurface } ); viewsBySurface[xdgSurface.surface] = item; @@ -96,6 +97,19 @@ WaylandCompositor { } } + XdgShellV6 { + onToplevelCreated: { + var item = chromeComponent.createObject(defaultOutput.surfaceArea, { "shellSurface": xdgSurface } ); + viewsBySurface[xdgSurface.surface] = item; + } + onPopupCreated: { + var parentView = viewsBySurface[popup.parentXdgSurface.surface]; + var item = chromeComponent.createObject(defaultOutput.surfaceArea, { "shellSurface": xdgSurface } ); + viewsBySurface[xdgSurface.surface] = item; + //TODO: set popup position + } + } + TextInputManager { } diff --git a/examples/wayland/server-buffer/README b/examples/wayland/server-buffer/README new file mode 100644 index 00000000..4e5eebbb --- /dev/null +++ b/examples/wayland/server-buffer/README @@ -0,0 +1,29 @@ +This is the example to demonstrate the server buffer interfaces + +Compile up both compositor and client. + +If you have the drm-egl-server buffer integration (and you are running Mesa) +then start the compositor with: + +$ QT_WAYLAND_SERVER_BUFFER_INTEGRATION=drm-egl-server ./compositor + + +Note: if you are running a compositor on an X11 desktop, you also need to +set QT_XCB_GL_INTEGRATION=xcb_egl as usual. + +The compositor broadcasts the name of the server buffer integration to +all clients through the hardware integration extension. Therefore, +all you need to do is to start the client with + +$ ./cpp-client -platform wayland + +The client will show all the buffers shared by the compositor. + +For testing on desktop, there is also a shared memory based server buffer +integration that works with any graphics hardware: + +$ QT_WAYLAND_SERVER_BUFFER_INTEGRATION=shm-emulation-server QT_XCB_GL_INTEGRATION=xcb_egl ./compositor + +Note: the shm-emulation-server integration does not actually share graphics +buffers, so it will not give any graphics memory savings. It is intended solely +for testing during development and should never be used in production. diff --git a/examples/wayland/server-buffer/compositor/compositor.pro b/examples/wayland/server-buffer/compositor/compositor.pro new file mode 100644 index 00000000..8ff20ad2 --- /dev/null +++ b/examples/wayland/server-buffer/compositor/compositor.pro @@ -0,0 +1,27 @@ +QT += core gui qml + +QT += waylandcompositor-private + +CONFIG += wayland-scanner +CONFIG += c++11 +SOURCES += \ + main.cpp \ + sharebufferextension.cpp + +OTHER_FILES = \ + qml/main.qml \ + qml/Screen.qml \ + images/background.jpg + +WAYLANDSERVERSOURCES += \ + ../share-buffer.xml + +RESOURCES += compositor.qrc + +TARGET = compositor + +HEADERS += \ + sharebufferextension.h + +target.path = $$[QT_INSTALL_EXAMPLES]/wayland/server-buffer/compositor +INSTALLS += target diff --git a/examples/wayland/server-buffer/compositor/compositor.qrc b/examples/wayland/server-buffer/compositor/compositor.qrc new file mode 100644 index 00000000..b50594b5 --- /dev/null +++ b/examples/wayland/server-buffer/compositor/compositor.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/"> + <file>images/background.png</file> + <file>images/Siberischer_tiger_de_edit02.jpg</file> + <file>qml/main.qml</file> + </qresource> +</RCC> diff --git a/examples/wayland/server-buffer/compositor/images/Siberischer_tiger_de_edit02.jpg b/examples/wayland/server-buffer/compositor/images/Siberischer_tiger_de_edit02.jpg Binary files differnew file mode 100644 index 00000000..eb1b73f8 --- /dev/null +++ b/examples/wayland/server-buffer/compositor/images/Siberischer_tiger_de_edit02.jpg diff --git a/examples/wayland/server-buffer/compositor/images/Siberischer_tiger_de_edit02.txt b/examples/wayland/server-buffer/compositor/images/Siberischer_tiger_de_edit02.txt new file mode 100644 index 00000000..3a26c00d --- /dev/null +++ b/examples/wayland/server-buffer/compositor/images/Siberischer_tiger_de_edit02.txt @@ -0,0 +1,5 @@ +Image from https://en.wikipedia.org/wiki/File:Siberischer_tiger_de_edit02.jpg + +Author: S. Taheri, edited by Fir0002 + +License: Creative Commons Attribution-Share Alike 2.5 Generic diff --git a/examples/wayland/server-buffer/compositor/images/background.png b/examples/wayland/server-buffer/compositor/images/background.png Binary files differnew file mode 100644 index 00000000..292160cd --- /dev/null +++ b/examples/wayland/server-buffer/compositor/images/background.png diff --git a/examples/wayland/server-buffer/compositor/main.cpp b/examples/wayland/server-buffer/compositor/main.cpp new file mode 100644 index 00000000..689a3bbe --- /dev/null +++ b/examples/wayland/server-buffer/compositor/main.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Wayland module +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QUrl> +#include <QtCore/QDebug> +#include <QtGui/QGuiApplication> +#include <QtQml/QQmlApplicationEngine> + +#include <QtQml/qqml.h> +#include <QtQml/QQmlEngine> + +#include "sharebufferextension.h" + +static void registerTypes() +{ + qmlRegisterType<ShareBufferExtensionQuickExtension>("com.theqtcompany.sharebufferextension", 1, 0, "ShareBufferExtension"); +} + +int main(int argc, char *argv[]) +{ + // Make sure there is a valid OpenGL context in the main thread + qputenv("QSG_RENDER_LOOP", "basic"); + + QGuiApplication app(argc, argv); + registerTypes(); + QQmlApplicationEngine appEngine(QUrl("qrc:///qml/main.qml")); + + return app.exec(); +} diff --git a/examples/wayland/server-buffer/compositor/qml/main.qml b/examples/wayland/server-buffer/compositor/qml/main.qml new file mode 100644 index 00000000..7df8ab7b --- /dev/null +++ b/examples/wayland/server-buffer/compositor/qml/main.qml @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Wayland module +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtWayland.Compositor 1.0 +import QtQuick.Window 2.2 + +import com.theqtcompany.sharebufferextension 1.0 + +import QtQuick 2.6 +import QtQuick.Window 2.2 +import QtWayland.Compositor 1.0 + +WaylandCompositor { + WaylandOutput { + sizeFollowsWindow: true + window: Window { + width: 1024 + height: 768 + visible: true + Image { + id: surfaceArea + anchors.fill: parent + fillMode: Image.Tile + source: "qrc:/images/background.png" + smooth: false + } + } + } + Component { + id: chromeComponent + ShellSurfaceItem { + onSurfaceDestroyed: destroy() + } + } + + WlShell { + onWlShellSurfaceCreated: + chromeComponent.createObject(surfaceArea, { "shellSurface": shellSurface } ); + } + + ShareBufferExtension { + } + +} diff --git a/examples/wayland/server-buffer/compositor/sharebufferextension.cpp b/examples/wayland/server-buffer/compositor/sharebufferextension.cpp new file mode 100644 index 00000000..8277dad3 --- /dev/null +++ b/examples/wayland/server-buffer/compositor/sharebufferextension.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Wayland module +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "sharebufferextension.h" + +#include <QWaylandSurface> + +#include <QDebug> + +#include <QQuickWindow> + +#include <QPainter> +#include <QPen> + +ShareBufferExtension::ShareBufferExtension(QWaylandCompositor *compositor) + : QWaylandCompositorExtensionTemplate(compositor) + , m_server_buffer_integration(nullptr) + , m_server_buffers_created(false) +{ +} + +void ShareBufferExtension::initialize() +{ + QWaylandCompositorExtensionTemplate::initialize(); + QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer()); + init(compositor->display(), 1); +} + +QtWayland::ServerBuffer *ShareBufferExtension::addImage(const QImage &img) +{ + if (!m_server_buffer_integration) { + QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer()); + + m_server_buffer_integration = QWaylandCompositorPrivate::get(compositor)->serverBufferIntegration(); + if (!m_server_buffer_integration) { + qWarning("Could not find a server buffer integration"); + return nullptr; + } + } + + QImage image = img.convertToFormat(QImage::Format_RGBA8888); + + auto *buffer = m_server_buffer_integration->createServerBufferFromImage(image, QtWayland::ServerBuffer::RGBA32); + + m_server_buffers.append(buffer); + return buffer; +} + +void ShareBufferExtension::createServerBuffers() +{ + QImage image(100,100,QImage::Format_ARGB32_Premultiplied); + image.fill(QColor(0x55,0x0,0x55,0x01)); + { + QPainter p(&image); + QPen pen = p.pen(); + pen.setWidthF(3); + pen.setColor(Qt::red); + p.setPen(pen); + p.drawLine(0,0,100,100); + pen.setColor(Qt::green); + p.setPen(pen); + p.drawLine(100,0,0,100); + pen.setColor(Qt::blue); + p.setPen(pen); + p.drawLine(25,15,75,15); + } + + addImage(image); + + QImage image2(":/images/Siberischer_tiger_de_edit02.jpg"); + addImage(image2); + + m_server_buffers_created = true; +} + + +void ShareBufferExtension::share_buffer_bind_resource(Resource *resource) +{ + if (!m_server_buffers_created) + createServerBuffers(); + + for (auto *buffer : qAsConst(m_server_buffers)) { + qDebug() << "sending" << buffer << "to client"; + struct ::wl_client *client = resource->handle->client; + struct ::wl_resource *buffer_resource = buffer->resourceForClient(client); + send_cross_buffer(resource->handle, buffer_resource); + } +} diff --git a/examples/wayland/server-buffer/compositor/sharebufferextension.h b/examples/wayland/server-buffer/compositor/sharebufferextension.h new file mode 100644 index 00000000..f84d9c4d --- /dev/null +++ b/examples/wayland/server-buffer/compositor/sharebufferextension.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Wayland module +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHAREBUFFEREXTENSION_H +#define SHAREBUFFEREXTENSION_H + +#include "wayland-util.h" + +#include <QtCore/QMap> + +#include <QtWaylandCompositor/QWaylandCompositorExtensionTemplate> +#include <QtWaylandCompositor/QWaylandQuickExtension> +#include <QtWaylandCompositor/QWaylandCompositor> + +#include <QtWaylandCompositor/private/qwaylandcompositor_p.h> +#include <QtWaylandCompositor/private/qwlserverbufferintegration_p.h> + +#include "qwayland-server-share-buffer.h" + +QT_BEGIN_NAMESPACE + +namespace QtWayland +{ + class ServerBufferIntegration; +}; + + +class ShareBufferExtension : public QWaylandCompositorExtensionTemplate<ShareBufferExtension> + , public QtWaylandServer::qt_share_buffer +{ + Q_OBJECT +public: + ShareBufferExtension(QWaylandCompositor *compositor = nullptr); + void initialize() override; + +protected slots: + QtWayland::ServerBuffer *addImage(const QImage &image); + +protected: + void share_buffer_bind_resource(Resource *resource) override; + +private: + void createServerBuffers(); + QList<QtWayland::ServerBuffer *> m_server_buffers; + QtWayland::ServerBufferIntegration *m_server_buffer_integration; + bool m_server_buffers_created; +}; + +Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(ShareBufferExtension) + +QT_END_NAMESPACE + +#endif // SHAREBUFFEREXTENSION_H diff --git a/examples/wayland/server-buffer/cpp-client/cpp-client.pro b/examples/wayland/server-buffer/cpp-client/cpp-client.pro new file mode 100644 index 00000000..7f785f98 --- /dev/null +++ b/examples/wayland/server-buffer/cpp-client/cpp-client.pro @@ -0,0 +1,13 @@ +QT += waylandclient-private gui-private +CONFIG += wayland-scanner + +WAYLANDCLIENTSOURCES += ../share-buffer.xml + +SOURCES += main.cpp \ + sharebufferextension.cpp + +HEADERS += \ + sharebufferextension.h + +target.path = $$[QT_INSTALL_EXAMPLES]/wayland/server-buffer/cpp-client +INSTALLS += target diff --git a/examples/wayland/server-buffer/cpp-client/main.cpp b/examples/wayland/server-buffer/cpp-client/main.cpp new file mode 100644 index 00000000..b0b3712b --- /dev/null +++ b/examples/wayland/server-buffer/cpp-client/main.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** + ** + ** Copyright (C) 2017 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the examples of the Qt Wayland module + ** + ** $QT_BEGIN_LICENSE:BSD$ + ** 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 https://www.qt.io/terms-conditions. For further + ** information use the contact form at https://www.qt.io/contact-us. + ** + ** BSD License Usage + ** Alternatively, you may use this file under the terms of the BSD license + ** as follows: + ** + ** "Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions are + ** met: + ** * Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** * Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in + ** the documentation and/or other materials provided with the + ** distribution. + ** * Neither the name of The Qt Company Ltd nor the names of its + ** contributors may be used to endorse or promote products derived + ** from this software without specific prior written permission. + ** + ** + ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include <QGuiApplication> +#include <QtGui/private/qguiapplication_p.h> +#include <QOpenGLWindow> +#include <QOpenGLTexture> +#include <QOpenGLTextureBlitter> +#include <QPainter> +#include <QMouseEvent> +#include <QPlatformSurfaceEvent> + +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtWaylandClient/private/qwaylandintegration_p.h> +#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h> +#include "sharebufferextension.h" + +#include <QDebug> +#include <QtGui/qpa/qplatformnativeinterface.h> +#include <QTimer> +#include <QMap> + +class TestWindow : public QOpenGLWindow +{ + Q_OBJECT + +public: + TestWindow() + : m_extension(nullptr) + { + m_extension = new ShareBufferExtension; + connect(m_extension, SIGNAL(bufferReceived(QtWaylandClient::QWaylandServerBuffer*)), this, SLOT(receiveBuffer(QtWaylandClient::QWaylandServerBuffer*))); + } + +public slots: + void receiveBuffer(QtWaylandClient::QWaylandServerBuffer *buffer) + { + m_buffers.append(buffer); + update(); + } + +protected: + + void initializeGL() override + { + m_blitter = new QOpenGLTextureBlitter; + m_blitter->create(); + } + + void paintGL() override { + glClearColor(.5, .45, .42, 1.); + glClear(GL_COLOR_BUFFER_BIT); + int x = 0; + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + for (auto buffer: m_buffers) { + m_blitter->bind(); + QPointF pos(x,0); + QSize s(buffer->size()); + QRectF targetRect(pos, s); + QOpenGLTexture *texture = buffer->toOpenGlTexture(); + auto surfaceOrigin = QOpenGLTextureBlitter::OriginTopLeft; + QMatrix4x4 targetTransform = QOpenGLTextureBlitter::targetTransform(targetRect, QRect(QPoint(), size())); + m_blitter->blit(texture->textureId(), targetTransform, surfaceOrigin); + m_blitter->release(); + x += s.width() + 10; + } + } + +private: + + QOpenGLTextureBlitter *m_blitter; + ShareBufferExtension *m_extension; + QList<QtWaylandClient::QWaylandServerBuffer*> m_buffers; + +}; + +int main (int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + TestWindow window; + window.show(); + + return app.exec(); +} + +#include "main.moc" diff --git a/examples/wayland/server-buffer/cpp-client/sharebufferextension.cpp b/examples/wayland/server-buffer/cpp-client/sharebufferextension.cpp new file mode 100644 index 00000000..47b24822 --- /dev/null +++ b/examples/wayland/server-buffer/cpp-client/sharebufferextension.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Wayland module +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "sharebufferextension.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtWaylandClient/private/qwaylandintegration_p.h> +#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h> +#include <QtGui/QGuiApplication> +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/QWindow> +#include <QtGui/QPlatformSurfaceEvent> +#include <QtGui/qpa/qplatformnativeinterface.h> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +ShareBufferExtension::ShareBufferExtension() + : QWaylandClientExtensionTemplate(/* Supported protocol version */ 1 ) +{ + + auto *wayland_integration = static_cast<QtWaylandClient::QWaylandIntegration *>(QGuiApplicationPrivate::platformIntegration()); + m_server_buffer_integration = wayland_integration->serverBufferIntegration(); + if (!m_server_buffer_integration) { + qCritical() << "This application requires a working serverBufferIntegration"; + QGuiApplication::quit(); + } +} + +void ShareBufferExtension::share_buffer_cross_buffer(struct ::qt_server_buffer *buffer) +{ + QtWaylandClient::QWaylandServerBuffer *serverBuffer = m_server_buffer_integration->serverBuffer(buffer); + emit bufferReceived(serverBuffer); +} + + +QT_END_NAMESPACE diff --git a/examples/wayland/server-buffer/cpp-client/sharebufferextension.h b/examples/wayland/server-buffer/cpp-client/sharebufferextension.h new file mode 100644 index 00000000..6ea19c08 --- /dev/null +++ b/examples/wayland/server-buffer/cpp-client/sharebufferextension.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Wayland module +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHAREBUFFEREXTENSION_H +#define SHAREBUFFEREXTENSION_H + +#include <qpa/qwindowsysteminterface.h> +#include <QtWaylandClient/private/qwayland-wayland.h> +#include <QtWaylandClient/qwaylandclientextension.h> +#include "qwayland-share-buffer.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + class QWaylandServerBuffer; + class QWaylandServerBufferIntegration; +}; + +class ShareBufferExtension : public QWaylandClientExtensionTemplate<ShareBufferExtension> + , public QtWayland::qt_share_buffer +{ + Q_OBJECT +public: + ShareBufferExtension(); + +signals: + void bufferReceived(QtWaylandClient::QWaylandServerBuffer *buffer); + +private: + void share_buffer_cross_buffer(struct ::qt_server_buffer *buffer) override; + QtWaylandClient::QWaylandServerBufferIntegration *m_server_buffer_integration; +}; + +QT_END_NAMESPACE + +#endif // SHAREBUFFEREXTENSION_H diff --git a/examples/wayland/server-buffer/server-buffer.pro b/examples/wayland/server-buffer/server-buffer.pro new file mode 100644 index 00000000..0c737ea8 --- /dev/null +++ b/examples/wayland/server-buffer/server-buffer.pro @@ -0,0 +1,6 @@ +TEMPLATE=subdirs + +SUBDIRS += cpp-client compositor + +EXAMPLE_FILES += \ + share-buffer.xml diff --git a/examples/wayland/server-buffer/share-buffer.xml b/examples/wayland/server-buffer/share-buffer.xml new file mode 100644 index 00000000..ceb6b11f --- /dev/null +++ b/examples/wayland/server-buffer/share-buffer.xml @@ -0,0 +1,46 @@ +<protocol name="share_buffer"> + + <copyright> + Copyright (C) 2015 The Qt Company Ltd. + Contact: http://www.qt.io/licensing/ + + This file is part of the plugins of the Qt Toolkit. + + $QT_BEGIN_LICENSE:BSD$ + You may use this file under the terms of the BSD license as follows: + + "Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of The Qt Company Ltd nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + + $QT_END_LICENSE$ + </copyright> + + <interface name="qt_share_buffer" version="1"> + <event name="cross_buffer"> + <arg name="buffer" type="object" interface="qt_server_buffer"/> + </event> + </interface> +</protocol> diff --git a/examples/wayland/wayland.pro b/examples/wayland/wayland.pro index de3a3824..9da43fbd 100644 --- a/examples/wayland/wayland.pro +++ b/examples/wayland/wayland.pro @@ -15,6 +15,7 @@ qtHaveModule(quick) { SUBDIRS += ivi-compositor qtHaveModule(waylandclient) { SUBDIRS += \ - custom-extension + custom-extension \ + server-buffer } } diff --git a/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h b/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h index 67f857db..3b8b3a6d 100644 --- a/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h +++ b/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h @@ -59,6 +59,8 @@ QT_BEGIN_NAMESPACE +class QOpenGLTexture; + namespace QtWaylandClient { class QWaylandDisplay; @@ -74,7 +76,7 @@ public: QWaylandServerBuffer(); virtual ~QWaylandServerBuffer(); - virtual void bindTextureToBuffer() = 0; + virtual QOpenGLTexture *toOpenGlTexture() = 0; Format format() const; QSize size() const; diff --git a/src/client/qwaylanddnd.cpp b/src/client/qwaylanddnd.cpp index 54c075c4..fe620506 100644 --- a/src/client/qwaylanddnd.cpp +++ b/src/client/qwaylanddnd.cpp @@ -62,13 +62,6 @@ QWaylandDrag::~QWaylandDrag() { } -QMimeData * QWaylandDrag::platformDropData() -{ - if (drag()) - return drag()->mimeData(); - return 0; -} - void QWaylandDrag::startDrag() { QBasicDrag::startDrag(); diff --git a/src/client/qwaylanddnd_p.h b/src/client/qwaylanddnd_p.h index 2a99432c..8a1d7f1f 100644 --- a/src/client/qwaylanddnd_p.h +++ b/src/client/qwaylanddnd_p.h @@ -71,8 +71,6 @@ public: QWaylandDrag(QWaylandDisplay *display); ~QWaylandDrag(); - QMimeData *platformDropData() override; - void updateTarget(const QString &mimeType); void setResponse(const QPlatformDragQtResponse &response); void finishDrag(const QPlatformDropQtResponse &response); diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp index 334e0ec4..5b43428d 100644 --- a/src/client/qwaylandscreen.cpp +++ b/src/client/qwaylandscreen.cpp @@ -89,6 +89,16 @@ QWaylandDisplay * QWaylandScreen::display() const return mWaylandDisplay; } +QString QWaylandScreen::manufacturer() const +{ + return mManufacturer; +} + +QString QWaylandScreen::model() const +{ + return mModel; +} + QRect QWaylandScreen::geometry() const { // Scale geometry for QScreen. This makes window and screen @@ -203,12 +213,11 @@ void QWaylandScreen::output_geometry(int32_t x, int32_t y, int32_t transform) { Q_UNUSED(subpixel); - Q_UNUSED(make); - mTransform = transform; + mManufacturer = make; + mModel = model; - if (!model.isEmpty()) - mOutputName = model; + mTransform = transform; mPhysicalSize = QSize(width, height); mGeometry.moveTopLeft(QPoint(x, y)); diff --git a/src/client/qwaylandscreen_p.h b/src/client/qwaylandscreen_p.h index 3d4df6e6..674e1a91 100644 --- a/src/client/qwaylandscreen_p.h +++ b/src/client/qwaylandscreen_p.h @@ -72,6 +72,9 @@ public: void init(); QWaylandDisplay *display() const; + QString manufacturer() const override; + QString model() const override; + QRect geometry() const override; int depth() const override; QImage::Format format() const override; @@ -92,7 +95,7 @@ public: #if QT_CONFIG(cursor) QPlatformCursor *cursor() const override; - QWaylandCursor *waylandCursor() const { return mWaylandCursor; }; + QWaylandCursor *waylandCursor() const { return mWaylandCursor; } #endif uint32_t outputId() const { return m_outputId; } @@ -114,6 +117,8 @@ private: int m_outputId; QWaylandDisplay *mWaylandDisplay; + QString mManufacturer; + QString mModel; QRect mGeometry; int mScale; int mDepth; diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp index 045748a1..2085bc59 100644 --- a/src/client/qwaylandshmbackingstore.cpp +++ b/src/client/qwaylandshmbackingstore.cpp @@ -44,6 +44,8 @@ #include "qwaylandabstractdecoration_p.h" #include <QtCore/qdebug.h> +#include <QtCore/qstandardpaths.h> +#include <QtCore/qtemporaryfile.h> #include <QtGui/QPainter> #include <QMutexLocker> #include <QLoggingCategory> @@ -52,10 +54,14 @@ #include <wayland-client-protocol.h> #include <unistd.h> -#include <fcntl.h> -#include <errno.h> #include <sys/mman.h> +#ifdef Q_OS_LINUX +# include <sys/syscall.h> +// from linux/memfd.h: +# define MFD_CLOEXEC 0x0001U +#endif + QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -72,28 +78,36 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, { int stride = size.width() * 4; int alloc = stride * size.height(); - char filename[] = "/tmp/wayland-shm-XXXXXX"; - int fd = mkstemp(filename); - if (fd < 0) { - qWarning("mkstemp %s failed: %s", filename, strerror(errno)); - return; + int fd = -1; + +#ifdef SYS_memfd_create + fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC); +#endif + + QScopedPointer<QFile> filePointer; + + if (fd == -1) { + auto tmpFile = new QTemporaryFile (QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + + QLatin1String("/wayland-shm-XXXXXX")); + tmpFile->open(); + filePointer.reset(tmpFile); + } else { + auto file = new QFile; + file->open(fd, QIODevice::ReadWrite | QIODevice::Unbuffered, QFile::AutoCloseHandle); + filePointer.reset(file); } - int flags = fcntl(fd, F_GETFD); - if (flags != -1) - fcntl(fd, F_SETFD, flags | FD_CLOEXEC); - - if (ftruncate(fd, alloc) < 0) { - qWarning("ftruncate failed: %s", strerror(errno)); - close(fd); + if (!filePointer->isOpen() || !filePointer->resize(alloc)) { + qWarning("QWaylandShmBuffer: failed: %s", qUtf8Printable(filePointer->errorString())); return; } + fd = filePointer->handle(); + + // map ourselves: QFile::map() will unmap when the object is destroyed, + // but we want this mapping to persist (unmapping in destructor) uchar *data = (uchar *) mmap(NULL, alloc, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - unlink(filename); - if (data == (uchar *) MAP_FAILED) { - qWarning("mmap /dev/zero failed: %s", strerror(errno)); - close(fd); + qErrnoWarning("QWaylandShmBuffer: mmap failed"); return; } @@ -105,7 +119,6 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, mShmPool = wl_shm_create_pool(shm->object(), fd, alloc); init(wl_shm_pool_create_buffer(mShmPool,0, size.width(), size.height(), stride, wl_format)); - close(fd); } QWaylandShmBuffer::~QWaylandShmBuffer(void) diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp index 6d7c0885..cff380e8 100644 --- a/src/client/qwaylandwindow.cpp +++ b/src/client/qwaylandwindow.cpp @@ -111,7 +111,7 @@ QWaylandWindow::~QWaylandWindow() delete mWindowDecoration; if (isInitialized()) - reset(); + reset(false); QList<QWaylandInputDevice *> inputDevices = mDisplay->inputDevices(); for (int i = 0; i < inputDevices.size(); ++i) @@ -133,8 +133,11 @@ void QWaylandWindow::initWindow() if (window()->type() == Qt::Desktop) return; - if (!isInitialized()) + if (!isInitialized()) { initializeWlSurface(); + QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated); + QGuiApplication::sendEvent(window(), &e); + } if (shouldCreateSubSurface()) { Q_ASSERT(!mSubSurfaceWindow); @@ -212,7 +215,7 @@ void QWaylandWindow::initWindow() // but since we're creating the shellsurface only now we reset mState to // make sure the state gets sent out to the compositor mState = Qt::WindowNoState; - setWindowStateInternal(window()->windowState()); + setWindowStateInternal(window()->windowStates()); handleContentOrientationChange(window()->contentOrientation()); mFlags = window()->flags(); } @@ -241,8 +244,12 @@ bool QWaylandWindow::shouldCreateSubSurface() const return QPlatformWindow::parent() != Q_NULLPTR; } -void QWaylandWindow::reset() +void QWaylandWindow::reset(bool sendDestroyEvent) { + if (isInitialized() && sendDestroyEvent) { + QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed); + QGuiApplication::sendEvent(window(), &e); + } delete mShellSurface; mShellSurface = 0; delete mSubSurfaceWindow; @@ -677,7 +684,7 @@ void QWaylandWindow::setOrientationMask(Qt::ScreenOrientations mask) mShellSurface->setContentOrientationMask(mask); } -void QWaylandWindow::setWindowState(Qt::WindowState state) +void QWaylandWindow::setWindowState(Qt::WindowStates state) { if (setWindowStateInternal(state)) QWindowSystemInterface::flushWindowSystemEvents(); // Required for oldState to work on WindowStateChanged @@ -695,16 +702,16 @@ void QWaylandWindow::setWindowFlags(Qt::WindowFlags flags) bool QWaylandWindow::createDecoration() { // so far only xdg-shell support this "unminimize" trick, may be moved elsewhere - if (mState == Qt::WindowMinimized) { + if (mState & Qt::WindowMinimized) { QWaylandXdgSurface *xdgSurface = qobject_cast<QWaylandXdgSurface *>(mShellSurface); if ( xdgSurface ) { - if (xdgSurface->isFullscreen()) { - setWindowStateInternal(Qt::WindowFullScreen); - } else if (xdgSurface->isMaximized()) { - setWindowStateInternal(Qt::WindowMaximized); - } else { - setWindowStateInternal(Qt::WindowNoState); - } + Qt::WindowStates states; + if (xdgSurface->isFullscreen()) + states |= Qt::WindowFullScreen; + if (xdgSurface->isMaximized()) + states |= Qt::WindowMaximized; + + setWindowStateInternal(states); } } @@ -962,7 +969,7 @@ bool QWaylandWindow::setMouseGrabEnabled(bool grab) return true; } -bool QWaylandWindow::setWindowStateInternal(Qt::WindowState state) +bool QWaylandWindow::setWindowStateInternal(Qt::WindowStates state) { if (mState == state) { return false; @@ -975,19 +982,14 @@ bool QWaylandWindow::setWindowStateInternal(Qt::WindowState state) if (mShellSurface) { createDecoration(); - switch (state) { - case Qt::WindowFullScreen: - mShellSurface->setFullscreen(); - break; - case Qt::WindowMaximized: - mShellSurface->setMaximized(); - break; - case Qt::WindowMinimized: - mShellSurface->setMinimized(); - break; - default: - mShellSurface->setNormal(); - } + if (state & Qt::WindowMaximized) + mShellSurface->setMaximized(); + if (state & Qt::WindowFullScreen) + mShellSurface->setFullscreen(); + if (state & Qt::WindowMinimized) + mShellSurface->setMinimized(); + if (!state) + mShellSurface->setNormal(); } QWindowSystemInterface::handleWindowStateChanged(window(), mState); diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h index bd4a3590..961b7881 100644 --- a/src/client/qwaylandwindow_p.h +++ b/src/client/qwaylandwindow_p.h @@ -147,7 +147,7 @@ public: void handleContentOrientationChange(Qt::ScreenOrientation orientation) override; void setOrientationMask(Qt::ScreenOrientations mask); - void setWindowState(Qt::WindowState state) override; + void setWindowState(Qt::WindowStates state) override; void setWindowFlags(Qt::WindowFlags flags) override; void raise() override; @@ -172,8 +172,8 @@ public: bool createDecoration(); - inline bool isMaximized() const { return mState == Qt::WindowMaximized; } - inline bool isFullscreen() const { return mState == Qt::WindowFullScreen; } + inline bool isMaximized() const { return mState & Qt::WindowMaximized; } + inline bool isFullscreen() const { return mState & Qt::WindowFullScreen; } #if QT_CONFIG(cursor) void setMouseCursor(QWaylandInputDevice *device, const QCursor &cursor); @@ -241,7 +241,7 @@ protected: QIcon mWindowIcon; - Qt::WindowState mState; + Qt::WindowStates mState; Qt::WindowFlags mFlags; QRegion mMask; @@ -251,13 +251,13 @@ private slots: void handleScreenRemoved(QScreen *qScreen); private: - bool setWindowStateInternal(Qt::WindowState flags); + bool setWindowStateInternal(Qt::WindowStates flags); void setGeometry_helper(const QRect &rect); void initWindow(); void initializeWlSurface(); bool shouldCreateShellSurface() const; bool shouldCreateSubSurface() const; - void reset(); + void reset(bool sendDestroyEvent = true); void sendExposeEvent(const QRect &rect); static void closePopups(QWaylandWindow *parent); QWaylandScreen *calculateScreenFromSurfaceEvents() const; diff --git a/src/client/qwaylandwindowmanagerintegration.cpp b/src/client/qwaylandwindowmanagerintegration.cpp index 60825aec..c519126e 100644 --- a/src/client/qwaylandwindowmanagerintegration.cpp +++ b/src/client/qwaylandwindowmanagerintegration.cpp @@ -109,28 +109,6 @@ void QWaylandWindowManagerIntegration::windowmanager_quit() QGuiApplication::quit(); } -QByteArray QWaylandWindowManagerIntegration::desktopEnvironment() const -{ - const QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP"); - if (!xdgCurrentDesktop.isEmpty()) - return xdgCurrentDesktop.toUpper(); // KDE, GNOME, UNITY, LXDE, MATE, XFCE... - - // Classic fallbacks - if (!qEnvironmentVariableIsEmpty("KDE_FULL_SESSION")) - return QByteArrayLiteral("KDE"); - if (!qEnvironmentVariableIsEmpty("GNOME_DESKTOP_SESSION_ID")) - return QByteArrayLiteral("GNOME"); - - // Fallback to checking $DESKTOP_SESSION (unreliable) - const QByteArray desktopSession = qgetenv("DESKTOP_SESSION"); - if (desktopSession == "gnome") - return QByteArrayLiteral("GNOME"); - if (desktopSession == "xfce") - return QByteArrayLiteral("XFCE"); - - return QByteArrayLiteral("UNKNOWN"); -} - void QWaylandWindowManagerIntegration::openUrl_helper(const QUrl &url) { Q_ASSERT(isInitialized()); diff --git a/src/client/qwaylandwindowmanagerintegration_p.h b/src/client/qwaylandwindowmanagerintegration_p.h index cf6ca660..e399d4f0 100644 --- a/src/client/qwaylandwindowmanagerintegration_p.h +++ b/src/client/qwaylandwindowmanagerintegration_p.h @@ -77,8 +77,6 @@ public: explicit QWaylandWindowManagerIntegration(QWaylandDisplay *waylandDisplay); virtual ~QWaylandWindowManagerIntegration(); - QByteArray desktopEnvironment() const override; - bool openUrl(const QUrl &url) override; bool openDocument(const QUrl &url) override; diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp index f31bf39e..a993467a 100644 --- a/src/compositor/compositor_api/qwaylandquickitem.cpp +++ b/src/compositor/compositor_api/qwaylandquickitem.cpp @@ -222,9 +222,11 @@ void QWaylandBufferMaterial::bind() case 3: if (m_textures[2]) m_textures[2]->bind(GL_TEXTURE2); + Q_FALLTHROUGH(); case 2: if (m_textures[1]) m_textures[1]->bind(GL_TEXTURE1); + Q_FALLTHROUGH(); case 1: if (m_textures[0]) m_textures[0]->bind(GL_TEXTURE0); diff --git a/src/compositor/doc/src/qtwaylandcompositor-overview.qdoc b/src/compositor/doc/src/qtwaylandcompositor-overview.qdoc index 1c3cc13c..6be6444c 100644 --- a/src/compositor/doc/src/qtwaylandcompositor-overview.qdoc +++ b/src/compositor/doc/src/qtwaylandcompositor-overview.qdoc @@ -30,10 +30,58 @@ \title Qt Wayland Compositor \brief An API to develop display servers supporting the Wayland protocol - Qt Wayland Compositor is a module that provides QML and C++ APIs for - developing custom display servers based on the - \l{http://wayland.freedesktop.org}{Wayland protocol}. The server displays - content from client applications that support the Wayland protocol. + \l {https://wayland.freedesktop.org/} {Wayland} is a display server + protocol to help in creating multi-process systems, where multiple client + applications may render content on the same display, by going via + a compositor process. + + Compared to a system with a single-process design, a multi-process system + gives several benefits: + + \list + \li Easier resource management, through the regular operating system mechanisms. + \li Better security, as each application can run with its own permissions or + sandbox. + \li Clearer separation of application UI and compositor UI, so each + can be modified independently. + \endlist + + In a typical Wayland-based system, multiple client processes will render their + own contents to off-screen buffers. The information about these buffers will + then be passed to a display server process by using the Wayland protocol. + Finally, the display server process will composite and position the contents + on a physical display. + + Qt Wayland Compositor is a module that provides convenient and powerful + QML and C++ APIs for developing custom display servers based on this protocol. + The server displays content from client applications that support the Wayland + protocol. The design philosophy of Wayland is to keep the core protocol simple + and minimal, and to expand on this with use-case-specific extensions. Qt Wayland + Compositor supports many common extensions by default, and also has APIs that + enables the creation of new, custom extensions. + + In one typical use case, a display server written with Qt Wayland Compositor will + be a subsystem inside a larger application manager process. Qt Wayland Compositor + provides the APIs to communicate with clients and display their contents on screen, + using C++ for low-level access and the full set of Qt Quick effects, animations and + convenience when using the QML APIs. A typical application manager would, in addition + to this, implement features such as application life cycle, virtual keyboard input, + security and IPC. Qt provides APIs that can be used to develop the remaining parts + of an application manager in other modules. The \l {https://www.qt.io/qt-automotive-suite/} + {Qt Automotive Suite} provides a complete application manager which includes a + display server developed using Qt Wayland Compositor. + + \section1 Features of Qt Wayland Compositor + + The Qt Wayland Compositor API includes features needed to create a display server. + + \list + \li A QML API that can be used to display and manipulate client content, fully + integrated with all the features in Qt Quick. + \li A C++ API for low-level access and control. + \li Support for common extensions, including XDG Shell and IVI Application. + \li APIs to easily expand support to include custom extensions. + \endlist \section1 Environment Variables and Command-line Arguments diff --git a/src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc b/src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc index 19520438..1dadb710 100644 --- a/src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc +++ b/src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc @@ -26,7 +26,7 @@ ****************************************************************************/ /*! - \qmlmodule QtWayland.Compositor 1.0 + \qmlmodule QtWayland.Compositor 1.1 \title Qt Wayland Compositor QML Types \ingroup qmlmodules \brief Provides QML types for writing custom Wayland display servers. @@ -38,7 +38,7 @@ import statement: \code - import QtWayland.Compositor 1.0 + import QtWayland.Compositor 1.1 \endcode To link against the module, add this line to your \l qmake \c .pro file: diff --git a/src/compositor/extensions/extensions.pri b/src/compositor/extensions/extensions.pri index 9c5b37ea..9cb0bcfa 100644 --- a/src/compositor/extensions/extensions.pri +++ b/src/compositor/extensions/extensions.pri @@ -8,6 +8,7 @@ WAYLANDSERVERSOURCES += \ ../extensions/qt-windowmanager.xml \ ../3rdparty/protocol/text-input-unstable-v2.xml \ ../3rdparty/protocol/xdg-shell.xml \ + ../3rdparty/protocol/xdg-shell-unstable-v6.xml \ ../3rdparty/protocol/ivi-application.xml \ HEADERS += \ @@ -26,6 +27,8 @@ HEADERS += \ extensions/qwaylandqtwindowmanager_p.h \ extensions/qwaylandxdgshellv5.h \ extensions/qwaylandxdgshellv5_p.h \ + extensions/qwaylandxdgshellv6.h \ + extensions/qwaylandxdgshellv6_p.h \ extensions/qwaylandshellsurface.h \ extensions/qwaylandiviapplication.h \ extensions/qwaylandiviapplication_p.h \ @@ -42,6 +45,7 @@ SOURCES += \ extensions/qwaylandtextinputmanager.cpp \ extensions/qwaylandqtwindowmanager.cpp \ extensions/qwaylandxdgshellv5.cpp \ + extensions/qwaylandxdgshellv6.cpp \ extensions/qwaylandiviapplication.cpp \ extensions/qwaylandivisurface.cpp \ @@ -52,12 +56,14 @@ qtHaveModule(quick):contains(QT_CONFIG, opengl) { extensions/qwaylandivisurfaceintegration_p.h \ extensions/qwaylandwlshellintegration_p.h \ extensions/qwaylandxdgshellv5integration_p.h \ + extensions/qwaylandxdgshellv6integration_p.h \ SOURCES += \ extensions/qwaylandquickshellsurfaceitem.cpp \ extensions/qwaylandivisurfaceintegration.cpp \ extensions/qwaylandwlshellintegration.cpp \ extensions/qwaylandxdgshellv5integration.cpp \ + extensions/qwaylandxdgshellv6integration.cpp \ } diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp b/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp index 289f706c..2b603099 100644 --- a/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp +++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp @@ -78,6 +78,12 @@ QWaylandQuickShellSurfaceItem::QWaylandQuickShellSurfaceItem(QQuickItem *parent) { } +QWaylandQuickShellSurfaceItem::~QWaylandQuickShellSurfaceItem() +{ + Q_D(QWaylandQuickShellSurfaceItem); + delete d->m_shellIntegration; +} + /*! * \internal */ diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem.h b/src/compositor/extensions/qwaylandquickshellsurfaceitem.h index 549f4345..152d5c53 100644 --- a/src/compositor/extensions/qwaylandquickshellsurfaceitem.h +++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem.h @@ -56,6 +56,7 @@ class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickShellSurfaceItem : public QWaylan Q_PROPERTY(QQuickItem *moveItem READ moveItem WRITE setMoveItem NOTIFY moveItemChanged) public: QWaylandQuickShellSurfaceItem(QQuickItem *parent = nullptr); + ~QWaylandQuickShellSurfaceItem(); QWaylandShellSurface *shellSurface() const; void setShellSurface(QWaylandShellSurface *shellSurface); diff --git a/src/compositor/extensions/qwaylandxdgshellv6.cpp b/src/compositor/extensions/qwaylandxdgshellv6.cpp new file mode 100644 index 00000000..4b1d46b8 --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgshellv6.cpp @@ -0,0 +1,1901 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** 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 "qwaylandxdgshellv6.h" +#include "qwaylandxdgshellv6_p.h" + +#ifdef QT_WAYLAND_COMPOSITOR_QUICK +#include "qwaylandxdgshellv6integration_p.h" +#endif + +#include <QtWaylandCompositor/QWaylandCompositor> +#include <QtWaylandCompositor/QWaylandSeat> +#include <QtWaylandCompositor/QWaylandSurface> +#include <QtWaylandCompositor/QWaylandSurfaceRole> +#include <QtWaylandCompositor/QWaylandResource> + +#include <QtCore/QObject> + +#include <algorithm> + +QT_BEGIN_NAMESPACE + +QWaylandXdgShellV6Private::QWaylandXdgShellV6Private() + : QWaylandCompositorExtensionPrivate() + , zxdg_shell_v6() +{ +} + +void QWaylandXdgShellV6Private::ping(QtWaylandServer::zxdg_shell_v6::Resource *resource, uint32_t serial) +{ + m_pings.insert(serial); + send_ping(resource->handle, serial); +} + +void QWaylandXdgShellV6Private::registerXdgSurface(QWaylandXdgSurfaceV6 *xdgSurface) +{ + m_xdgSurfaces.insert(xdgSurface->surface()->client()->client(), xdgSurface); +} + +void QWaylandXdgShellV6Private::unregisterXdgSurface(QWaylandXdgSurfaceV6 *xdgSurface) +{ + auto xdgSurfacePrivate = QWaylandXdgSurfaceV6Private::get(xdgSurface); + if (!m_xdgSurfaces.remove(xdgSurfacePrivate->resource()->client(), xdgSurface)) + qWarning("%s Unexpected state. Can't find registered xdg surface\n", Q_FUNC_INFO); +} + +Qt::Edges QWaylandXdgShellV6Private::convertToEdges(uint xdgEdges) +{ + return Qt::Edges(((xdgEdges & 0b1100) >> 1) | ((xdgEdges & 0b0010) << 2) | (xdgEdges & 0b0001)); +} + +QWaylandXdgSurfaceV6 *QWaylandXdgShellV6Private::xdgSurfaceFromSurface(QWaylandSurface *surface) +{ + for (QWaylandXdgSurfaceV6 *xdgSurface : qAsConst(m_xdgSurfaces)) { + if (surface == xdgSurface->surface()) + return xdgSurface; + } + return nullptr; +} + +void QWaylandXdgShellV6Private::zxdg_shell_v6_destroy(Resource *resource) +{ + if (!m_xdgSurfaces.values(resource->client()).empty()) + wl_resource_post_error(resource->handle, ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES, + "xdg_shell was destroyed before children"); + + wl_resource_destroy(resource->handle); +} + +void QWaylandXdgShellV6Private::zxdg_shell_v6_create_positioner(QtWaylandServer::zxdg_shell_v6::Resource *resource, uint32_t id) +{ + QWaylandResource positionerResource(wl_resource_create(resource->client(), &zxdg_positioner_v6_interface, + wl_resource_get_version(resource->handle), id)); + + new QWaylandXdgPositionerV6(positionerResource); +} + +void QWaylandXdgShellV6Private::zxdg_shell_v6_get_xdg_surface(Resource *resource, uint32_t id, wl_resource *surfaceResource) +{ + Q_Q(QWaylandXdgShellV6); + QWaylandSurface *surface = QWaylandSurface::fromResource(surfaceResource); + + if (surface->role() != nullptr) { + wl_resource_post_error(resource->handle, ZXDG_SHELL_V6_ERROR_ROLE, + "wl_surface@%d, already has role %s\n", + wl_resource_get_id(surface->resource()), + surface->role()->name().constData()); + return; + } + + if (surface->hasContent()) { + //TODO: According to the spec, this is a client error, but there's no appropriate error code + qWarning() << "get_xdg_surface requested on a zxdg_surface_v6 with content"; + } + + QWaylandResource xdgSurfaceResource(wl_resource_create(resource->client(), &zxdg_surface_v6_interface, + wl_resource_get_version(resource->handle), id)); + + QWaylandXdgSurfaceV6 *xdgSurface = new QWaylandXdgSurfaceV6(q, surface, xdgSurfaceResource); + + registerXdgSurface(xdgSurface); + emit q->xdgSurfaceCreated(xdgSurface); +} + +void QWaylandXdgShellV6Private::zxdg_shell_v6_pong(Resource *resource, uint32_t serial) +{ + Q_UNUSED(resource); + Q_Q(QWaylandXdgShellV6); + if (m_pings.remove(serial)) + emit q->pong(serial); + else + qWarning("Received an unexpected pong!"); +} + +/*! + * \qmltype XdgShellV6 + * \inqmlmodule QtWayland.Compositor + * \since 5.10 + * \brief Provides an extension for desktop-style user interfaces. + * + * The XdgShellV6 extension provides a way to associate a XdgToplevelV6 or XdgPopupV6 + * with a regular Wayland surface. Using the XdgToplevelV6 interface, the client + * can request that the surface is resized, moved, and so on. + * + * XdgShellV6 corresponds to the Wayland interface, \c zxdg_shell_v6. + * + * To provide the functionality of the shell extension in a compositor, create + * an instance of the XdgShellV6 component and add it to the list of extensions + * supported by the compositor: + * \code + * import QtWayland.Compositor 1.1 + * + * WaylandCompositor { + * XdgShellV6 { + * // ... + * } + * } + * \endcode + */ + +/*! + * \class QWaylandXdgShellV6 + * \inmodule QtWaylandCompositor + * \since 5.10 + * \brief The QWaylandXdgShellV6 class is an extension for desktop-style user interfaces. + * + * The QWaylandXdgShellV6 extension provides a way to associate a QWaylandXdgToplevelV6 or + * QWaylandXdgPopupV6 with a regular Wayland surface. Using the QWaylandXdgToplevelV6 interface, + * the client can request that the surface is resized, moved, and so on. + * + * QWaylandXdgShellV6 corresponds to the Wayland interface, \c zxdg_shell_v6. + */ + +/*! + * Constructs a QWaylandXdgShellV6 object. + */ +QWaylandXdgShellV6::QWaylandXdgShellV6() + : QWaylandShellTemplate<QWaylandXdgShellV6>(*new QWaylandXdgShellV6Private()) +{ +} + +/*! + * Constructs a QWaylandXdgShellV6 object for the provided \a compositor. + */ +QWaylandXdgShellV6::QWaylandXdgShellV6(QWaylandCompositor *compositor) + : QWaylandShellTemplate<QWaylandXdgShellV6>(compositor, *new QWaylandXdgShellV6Private()) +{ +} + +/*! + * Initializes the shell extension. + */ +void QWaylandXdgShellV6::initialize() +{ + Q_D(QWaylandXdgShellV6); + QWaylandShellTemplate::initialize(); + QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer()); + if (!compositor) { + qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandXdgShellV6"; + return; + } + d->init(compositor->display(), 1); + + handleSeatChanged(compositor->defaultSeat(), nullptr); + + connect(compositor, &QWaylandCompositor::defaultSeatChanged, + this, &QWaylandXdgShellV6::handleSeatChanged); +} + +/*! + * Returns the Wayland interface for the QWaylandXdgShellV6. + */ +const struct wl_interface *QWaylandXdgShellV6::interface() +{ + return QWaylandXdgShellV6Private::interface(); +} + +QByteArray QWaylandXdgShellV6::interfaceName() +{ + return QWaylandXdgShellV6Private::interfaceName(); +} + +/*! + * \qmlmethod void QtWaylandCompositor::XdgShellV6::ping() + * + * Sends a ping event to the client. If the client replies to the event the + * \a pong signal will be emitted. + */ + +/*! + * Sends a ping event to the client. If the client replies to the event the + * \a pong signal will be emitted. + */ +uint QWaylandXdgShellV6::ping(QWaylandClient *client) +{ + Q_D(QWaylandXdgShellV6); + + QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer()); + Q_ASSERT(compositor); + + uint32_t serial = compositor->nextSerial(); + + QWaylandXdgShellV6Private::Resource *clientResource = d->resourceMap().value(client->client(), nullptr); + Q_ASSERT(clientResource); + + d->ping(clientResource, serial); + return serial; +} + +void QWaylandXdgShellV6::handleSeatChanged(QWaylandSeat *newSeat, QWaylandSeat *oldSeat) +{ + if (oldSeat != nullptr) { + disconnect(oldSeat, &QWaylandSeat::keyboardFocusChanged, + this, &QWaylandXdgShellV6::handleFocusChanged); + } + + if (newSeat != nullptr) { + connect(newSeat, &QWaylandSeat::keyboardFocusChanged, + this, &QWaylandXdgShellV6::handleFocusChanged); + } +} + +void QWaylandXdgShellV6::handleFocusChanged(QWaylandSurface *newSurface, QWaylandSurface *oldSurface) +{ + Q_D(QWaylandXdgShellV6); + + QWaylandXdgSurfaceV6 *newXdgSurface = d->xdgSurfaceFromSurface(newSurface); + QWaylandXdgSurfaceV6 *oldXdgSurface = d->xdgSurfaceFromSurface(oldSurface); + + if (newXdgSurface) + QWaylandXdgSurfaceV6Private::get(newXdgSurface)->handleFocusReceived(); + + if (oldXdgSurface) + QWaylandXdgSurfaceV6Private::get(oldXdgSurface)->handleFocusLost(); +} + +QWaylandXdgSurfaceV6Private::QWaylandXdgSurfaceV6Private() + : QWaylandCompositorExtensionPrivate() + , zxdg_surface_v6() + , m_xdgShell(nullptr) + , m_surface(nullptr) + , m_toplevel(nullptr) + , m_popup(nullptr) + , m_unsetWindowGeometry(true) + , m_windowType(Qt::WindowType::Window) +{ +} + +void QWaylandXdgSurfaceV6Private::setWindowType(Qt::WindowType windowType) +{ + if (m_windowType == windowType) + return; + + m_windowType = windowType; + + Q_Q(QWaylandXdgSurfaceV6); + emit q->windowTypeChanged(); +} + +void QWaylandXdgSurfaceV6Private::handleFocusLost() +{ + if (m_toplevel) + QWaylandXdgToplevelV6Private::get(m_toplevel)->handleFocusLost(); +} + +void QWaylandXdgSurfaceV6Private::handleFocusReceived() +{ + if (m_toplevel) + QWaylandXdgToplevelV6Private::get(m_toplevel)->handleFocusReceived(); +} + +QRect QWaylandXdgSurfaceV6Private::calculateFallbackWindowGeometry() const +{ + // TODO: The unset window geometry should include subsurfaces as well, so this solution + // won't work too well on those kinds of clients. + return QRect(QPoint(0, 0), m_surface->size() / m_surface->bufferScale()); +} + +void QWaylandXdgSurfaceV6Private::updateFallbackWindowGeometry() +{ + Q_Q(QWaylandXdgSurfaceV6); + if (!m_unsetWindowGeometry) + return; + + const QRect unsetGeometry = calculateFallbackWindowGeometry(); + if (unsetGeometry == m_windowGeometry) + return; + + m_windowGeometry = unsetGeometry; + emit q->windowGeometryChanged(); +} + +void QWaylandXdgSurfaceV6Private::zxdg_surface_v6_destroy_resource(QtWaylandServer::zxdg_surface_v6::Resource *resource) +{ + Q_UNUSED(resource); + Q_Q(QWaylandXdgSurfaceV6); + QWaylandXdgShellV6Private::get(m_xdgShell)->unregisterXdgSurface(q); + delete q; +} + +void QWaylandXdgSurfaceV6Private::zxdg_surface_v6_destroy(QtWaylandServer::zxdg_surface_v6::Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +void QWaylandXdgSurfaceV6Private::zxdg_surface_v6_get_toplevel(QtWaylandServer::zxdg_surface_v6::Resource *resource, uint32_t id) +{ + Q_Q(QWaylandXdgSurfaceV6); + + if (m_toplevel || m_popup) { + wl_resource_post_error(resource->handle, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED, + "zxdg_surface_v6 already has a role object"); + return; + } + + if (!m_surface->setRole(QWaylandXdgToplevelV6::role(), resource->handle, ZXDG_SHELL_V6_ERROR_ROLE)) + return; + + QWaylandResource topLevelResource(wl_resource_create(resource->client(), &zxdg_toplevel_v6_interface, + wl_resource_get_version(resource->handle), id)); + + m_toplevel = new QWaylandXdgToplevelV6(q, topLevelResource); + emit q->toplevelCreated(); + emit m_xdgShell->toplevelCreated(m_toplevel, q); +} + +void QWaylandXdgSurfaceV6Private::zxdg_surface_v6_get_popup(QtWaylandServer::zxdg_surface_v6::Resource *resource, uint32_t id, wl_resource *parentResource, wl_resource *positionerResource) +{ + Q_Q(QWaylandXdgSurfaceV6); + + if (m_toplevel || m_popup) { + wl_resource_post_error(resource->handle, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED, + "zxdg_surface_v6 already has a role object"); + return; + } + + QWaylandXdgSurfaceV6 *parent = QWaylandXdgSurfaceV6::fromResource(parentResource); + if (!parent) { + wl_resource_post_error(resource->handle, ZXDG_SHELL_V6_ERROR_INVALID_POPUP_PARENT, + "zxdg_surface_v6.get_popup with invalid popup parent"); + return; + } + + QWaylandXdgPositionerV6 *positioner = QWaylandXdgPositionerV6::fromResource(positionerResource); + if (!positioner) { + wl_resource_post_error(resource->handle, ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER, + "zxdg_surface_v6.get_popup without positioner"); + return; + } + if (!positioner->m_data.isComplete()) { + QWaylandXdgPositionerV6Data p = positioner->m_data; + wl_resource_post_error(resource->handle, ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER, + "zxdg_surface_v6.get_popup with invalid positioner (size: %dx%d, anchorRect: %dx%d)", + p.size.width(), p.size.height(), p.anchorRect.width(), p.anchorRect.height()); + return; + } + + if (!m_surface->setRole(QWaylandXdgPopupV6::role(), resource->handle, ZXDG_SHELL_V6_ERROR_ROLE)) + return; + + QWaylandResource popupResource(wl_resource_create(resource->client(), &zxdg_popup_v6_interface, + wl_resource_get_version(resource->handle), id)); + + m_popup = new QWaylandXdgPopupV6(q, parent, positioner, popupResource); + emit q->popupCreated(); + emit m_xdgShell->popupCreated(m_popup, q); +} + +void QWaylandXdgSurfaceV6Private::zxdg_surface_v6_ack_configure(QtWaylandServer::zxdg_surface_v6::Resource *resource, uint32_t serial) +{ + if (m_toplevel) { + QWaylandXdgToplevelV6Private::get(m_toplevel)->handleAckConfigure(serial); + } else if (m_popup) { + QWaylandXdgPopupV6Private::get(m_popup)->handleAckConfigure(serial); + } else { + wl_resource_post_error(resource->handle, ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "ack_configure requested on an unconstructed zxdg_surface_v6"); + } +} + +void QWaylandXdgSurfaceV6Private::zxdg_surface_v6_set_window_geometry(QtWaylandServer::zxdg_surface_v6::Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) +{ + Q_Q(QWaylandXdgSurfaceV6); + + if (!q->surface()->role()) { + wl_resource_post_error(resource->handle, ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "set_window_geometry requested on an unconstructed zxdg_surface_v6"); + return; + } + + if (width <= 0 || height <= 0) { + // The protocol spec says "setting an invalid size will raise an error". But doesn't tell + // which error to raise, and there's no fitting error in the zxdg_surface_v6_error enum. + // So until this is fixed, just output a warning and return. + qWarning() << "Invalid (non-positive) dimensions received in set_window_geometry"; + return; + } + + m_unsetWindowGeometry = false; + + QRect geometry(x, y, width, height); + + if (m_windowGeometry == geometry) + return; + + m_windowGeometry = geometry; + emit q->windowGeometryChanged(); +} + +/*! + * \qmltype XdgSurfaceV6 + * \inqmlmodule QtWayland.Compositor + * \since 5.10 + * \brief XdgSurfaceV6 provides desktop-style compositor-specific features to an xdg surface. + * + * This type is part of the \l{XdgShellV6} extension and provides a way to + * extend the functionality of an existing \l{WaylandSurface} with features + * specific to desktop-style compositors, such as resizing and moving the + * surface. + * + * It corresponds to the Wayland interface \c zxdg_surface_v6. + */ + +/*! + * \class QWaylandXdgSurfaceV6 + * \inmodule QtWaylandCompositor + * \since 5.8 + * \brief The QWaylandXdgSurfaceV6 class provides desktop-style compositor-specific features to an xdg surface. + * + * This class is part of the QWaylandXdgShellV6 extension and provides a way to + * extend the functionality of an existing QWaylandSurface with features + * specific to desktop-style compositors, such as resizing and moving the + * surface. + * + * It corresponds to the Wayland interface \c zxdg_surface_v6. + */ + +/*! + * Constructs a QWaylandXdgSurfaceV6. + */ +QWaylandXdgSurfaceV6::QWaylandXdgSurfaceV6() + : QWaylandShellSurfaceTemplate<QWaylandXdgSurfaceV6>(*new QWaylandXdgSurfaceV6Private) +{ +} + +/*! + * Constructs a QWaylandXdgSurfaceV6 for \a surface and initializes it with the + * given \a xdgShell, \a surface, and resource \a res. + */ +QWaylandXdgSurfaceV6::QWaylandXdgSurfaceV6(QWaylandXdgShellV6 *xdgShell, QWaylandSurface *surface, const QWaylandResource &res) + : QWaylandShellSurfaceTemplate<QWaylandXdgSurfaceV6>(*new QWaylandXdgSurfaceV6Private) +{ + initialize(xdgShell, surface, res); +} + +/*! + * \qmlmethod void QtWaylandCompositor::XdgSurfaceV6::initialize(object xdgShell, object surface, object client, int id) + * + * Initializes the XdgSurface, associating it with the given \a xdgShell, \a surface, + * \a client, and \a id. + */ + +/*! + * Initializes the QWaylandXdgSurfaceV6, associating it with the given \a xdgShell, \a surface + * and \a resource. + */ +void QWaylandXdgSurfaceV6::initialize(QWaylandXdgShellV6 *xdgShell, QWaylandSurface *surface, const QWaylandResource &resource) +{ + Q_D(QWaylandXdgSurfaceV6); + d->m_xdgShell = xdgShell; + d->m_surface = surface; + d->init(resource.resource()); + setExtensionContainer(surface); + d->m_windowGeometry = d->calculateFallbackWindowGeometry(); + connect(surface, &QWaylandSurface::sizeChanged, this, &QWaylandXdgSurfaceV6::handleSurfaceSizeChanged); + connect(surface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandXdgSurfaceV6::handleBufferScaleChanged); + emit shellChanged(); + emit surfaceChanged(); + QWaylandCompositorExtension::initialize(); +} + +/*! + * \qmlproperty enum QtWaylandCompositor::XdgSurfaceV6::windowType + * + * This property holds the window type of the XdgSurfaceV6. + */ + +/*! + * \property QWaylandXdgSurfaceV6::windowType + * + * This property holds the window type of the QWaylandXdgSurfaceV6. + */ +Qt::WindowType QWaylandXdgSurfaceV6::windowType() const +{ + Q_D(const QWaylandXdgSurfaceV6); + return d->m_windowType; +} + +/*! + * \qmlproperty rect QtWaylandCompositor::XdgSurfaceV6::windowGeometry + * + * This property holds the window geometry of the QWaylandXdgSurfaceV6. The window + * geometry describes the window's visible bounds from the user's perspective. + * The geometry includes title bars and borders if drawn by the client, but + * excludes drop shadows. It is meant to be used for aligning and tiling + * windows. + */ + +/*! + * \property QWaylandXdgSurfaceV6::windowGeometry + * + * This property holds the window geometry of the QWaylandXdgSurfaceV6. The window + * geometry describes the window's visible bounds from the user's perspective. + * The geometry includes title bars and borders if drawn by the client, but + * excludes drop shadows. It is meant to be used for aligning and tiling + * windows. + */ +QRect QWaylandXdgSurfaceV6::windowGeometry() const +{ + Q_D(const QWaylandXdgSurfaceV6); + return d->m_windowGeometry; +} + +/*! + * \internal + */ +void QWaylandXdgSurfaceV6::initialize() +{ + QWaylandCompositorExtension::initialize(); +} + +void QWaylandXdgSurfaceV6::handleSurfaceSizeChanged() +{ + Q_D(QWaylandXdgSurfaceV6); + d->updateFallbackWindowGeometry(); +} + +void QWaylandXdgSurfaceV6::handleBufferScaleChanged() +{ + Q_D(QWaylandXdgSurfaceV6); + d->updateFallbackWindowGeometry(); +} + +/*! + * \qmlproperty XdgShellV6 QtWaylandCompositor::XdgSurfaceV6::shell + * + * This property holds the shell associated with this XdgSurface. + */ + +/*! + * \property QWaylandXdgSurfaceV6::shell + * + * This property holds the shell associated with this QWaylandXdgSurfaceV6. + */ +QWaylandXdgShellV6 *QWaylandXdgSurfaceV6::shell() const +{ + Q_D(const QWaylandXdgSurfaceV6); + return d->m_xdgShell; +} + +/*! + * \qmlproperty WaylandSurface QtWaylandCompositor::XdgSurfaceV6::surface + * + * This property holds the surface associated with this XdgSurface. + */ + +/*! + * \property QWaylandXdgSurfaceV6::surface + * + * This property holds the surface associated with this QWaylandXdgSurfaceV6. + */ +QWaylandSurface *QWaylandXdgSurfaceV6::surface() const +{ + Q_D(const QWaylandXdgSurfaceV6); + return d->m_surface; +} + +QWaylandXdgToplevelV6 *QWaylandXdgSurfaceV6::toplevel() const +{ + Q_D(const QWaylandXdgSurfaceV6); + return d->m_toplevel; +} + +QWaylandXdgPopupV6 *QWaylandXdgSurfaceV6::popup() const +{ + Q_D(const QWaylandXdgSurfaceV6); + return d->m_popup; +} + +/*! + * Returns the Wayland interface for the QWaylandXdgSurfaceV6. + */ +const wl_interface *QWaylandXdgSurfaceV6::interface() +{ + return QWaylandXdgSurfaceV6Private::interface(); +} + +/*! + * \internal + */ +QByteArray QWaylandXdgSurfaceV6::interfaceName() +{ + return QWaylandXdgSurfaceV6Private::interfaceName(); +} + +/*! + * Returns the QWaylandXdgSurfaceV6 corresponding to the \a resource. + */ +QWaylandXdgSurfaceV6 *QWaylandXdgSurfaceV6::fromResource(wl_resource *resource) +{ + auto xsResource = QWaylandXdgSurfaceV6Private::Resource::fromResource(resource); + if (!xsResource) + return nullptr; + return static_cast<QWaylandXdgSurfaceV6Private *>(xsResource->zxdg_surface_v6_object)->q_func(); +} + +#ifdef QT_WAYLAND_COMPOSITOR_QUICK +QWaylandQuickShellIntegration *QWaylandXdgSurfaceV6::createIntegration(QWaylandQuickShellSurfaceItem *item) +{ + Q_D(const QWaylandXdgSurfaceV6); + + if (d->m_toplevel) + return new QtWayland::XdgToplevelV6Integration(item); + + if (d->m_popup) + return new QtWayland::XdgPopupV6Integration(item); + + return nullptr; +} +#endif + +/*! + * \qmltype XdgToplevelV6 + * \inqmlmodule QtWayland.Compositor + * \since 5.10 + * \brief XdgToplevelV6 represents the toplevel window specific parts of an xdg surface + * + * This type is part of the \l{XdgShellV6} extension and provides a way to + * extend the functionality of an QWaylandXdgSurfaceV6 with features + * specific to desktop-style windows. + * + * It corresponds to the Wayland interface \c zxdg_toplevel_v6. + */ + +/*! + * \class QWaylandXdgToplevelV6 + * \inmodule QtWaylandCompositor + * \since 5.10 + * \brief The QWaylandXdgToplevelV6 class represents the toplevel window specific parts of an xdg surface + * + * This class is part of the QWaylandXdgShellV6 extension and provides a way to + * extend the functionality of an QWaylandXdgSurfaceV6 with features + * specific to desktop-style windows. + * + * It corresponds to the Wayland interface \c zxdg_toplevel_v6. + */ + +/*! + * Constructs a QWaylandXdgToplevelV6. + */ +QWaylandXdgToplevelV6::QWaylandXdgToplevelV6(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandResource &resource) + : QObject(*new QWaylandXdgToplevelV6Private(xdgSurface, resource)) +{ + QVector<QWaylandXdgToplevelV6::State> states; + sendConfigure({0, 0}, states); +} + +/*! + * \qmlproperty XdgToplevelV6 QtWaylandCompositor::XdgToplevelV6::parentToplevel + * + * This property holds the XdgToplevelV6 parent of this XdgToplevelV6. + */ + +/*! + * \property QWaylandXdgSurfaceV6::parentToplevel + * + * This property holds the XdgToplevelV6 parent of this XdgToplevelV6. + * + */ +QWaylandXdgToplevelV6 *QWaylandXdgToplevelV6::parentToplevel() const +{ + Q_D(const QWaylandXdgToplevelV6); + return d->m_parentToplevel; +} + +/*! + * \qmlproperty string QtWaylandCompositor::XdgToplevelV6::title + * + * This property holds the title of the XdgToplevelV6. + */ + +/*! + * \property QWaylandXdgToplevelV6::title + * + * This property holds the title of the QWaylandXdgToplevelV6. + */ +QString QWaylandXdgToplevelV6::title() const +{ + Q_D(const QWaylandXdgToplevelV6); + return d->m_title; +} + +/*! + * \qmlproperty string QtWaylandCompositor::XdgToplevelV6::appId + * + * This property holds the app id of the XdgToplevelV6. + */ + +/*! + * \property QWaylandXdgToplevelV6::appId + * + * This property holds the app id of the QWaylandXdgToplevelV6. + */ +QString QWaylandXdgToplevelV6::appId() const +{ + Q_D(const QWaylandXdgToplevelV6); + return d->m_appId; +} + +/*! + * \qmlproperty size QtWaylandCompositor::XdgToplevelV6::maxSize + * + * This property holds the maximum size of the XdgToplevelV6 as requested by the client. + * + * The compositor is free to ignore this value and request a larger size. + */ + +/*! + * \property QWaylandXdgToplevelV6::maxSize + * + * This property holds the maximum size of the QWaylandXdgToplevelV6. + * + * The compositor is free to ignore this value and request a larger size. + */ +QSize QWaylandXdgToplevelV6::maxSize() const +{ + Q_D(const QWaylandXdgToplevelV6); + return d->m_maxSize; +} + +/*! + * \qmlproperty size QtWaylandCompositor::XdgToplevelV6::minSize + * + * This property holds the minimum size of the XdgToplevelV6 as requested by the client. + * + * The compositor is free to ignore this value and request a smaller size. + */ + +/*! + * \property QWaylandXdgToplevelV6::minSize + * + * This property holds the minimum size of the QWaylandXdgToplevelV6. + * + * The compositor is free to ignore this value and request a smaller size. + */ +QSize QWaylandXdgToplevelV6::minSize() const +{ + Q_D(const QWaylandXdgToplevelV6); + return d->m_minSize; +} + +/*! + * \property QWaylandXdgToplevelV6::states + * + * This property holds the last states the client acknowledged for this QWaylandToplevelV6. + */ +QVector<QWaylandXdgToplevelV6::State> QWaylandXdgToplevelV6::states() const +{ + Q_D(const QWaylandXdgToplevelV6); + return d->m_lastAckedConfigure.states; +} + +/*! + * \qmlproperty bool QtWaylandCompositor::XdgToplevelV6::maximized + * + * This property holds whether the client has acknowledged that it should be maximized. + */ + +/*! + * \property QWaylandXdgToplevelV6::maximized + * + * This property holds whether the client has acknowledged that it should be maximized. + */ +bool QWaylandXdgToplevelV6::maximized() const +{ + Q_D(const QWaylandXdgToplevelV6); + return d->m_lastAckedConfigure.states.contains(QWaylandXdgToplevelV6::State::MaximizedState); +} + +/*! + * \qmlproperty bool QtWaylandCompositor::XdgToplevelV6::fullscreen + * + * This property holds whether the client has acknowledged that it should be fullscreen. + */ + +/*! + * \property QWaylandXdgToplevelV6::fullscreen + * + * This property holds whether the client has acknowledged that it should be fullscreen. + */ +bool QWaylandXdgToplevelV6::fullscreen() const +{ + Q_D(const QWaylandXdgToplevelV6); + return d->m_lastAckedConfigure.states.contains(QWaylandXdgToplevelV6::State::FullscreenState); +} + +/*! + * \qmlproperty bool QtWaylandCompositor::XdgToplevelV6::resizing + * + * This property holds whether the client has acknowledged that it is being resized. + */ + +/*! + * \property QWaylandXdgToplevelV6::resizing + * + * This property holds whether the client has acknowledged that it is being resized. + */ +bool QWaylandXdgToplevelV6::resizing() const +{ + Q_D(const QWaylandXdgToplevelV6); + return d->m_lastAckedConfigure.states.contains(QWaylandXdgToplevelV6::State::ResizingState); +} + +/*! + * \qmlproperty bool QtWaylandCompositor::XdgToplevelV6::activated + * + * This property holds whether toplevel is drawing itself as having input focus. + */ + +/*! + * \property QWaylandXdgToplevelV6::activated + * + * This property holds whether toplevel is drawing itself as having input focus. + */ +bool QWaylandXdgToplevelV6::activated() const +{ + Q_D(const QWaylandXdgToplevelV6); + return d->m_lastAckedConfigure.states.contains(QWaylandXdgToplevelV6::State::ActivatedState); +} + +/*! + * \qmlmethod size QtWaylandCompositor::XdgToplevelV6::sizeForResize(size size, point delta, uint edges) + * + * Convenience for computing the new size given the current \a size, a \a delta, and + * the \a edges active in the drag. + */ + +/*! + * Convenience for computing the new size given the current \a size, a \a delta, and + * the \a edges active in the drag. + */ +QSize QWaylandXdgToplevelV6::sizeForResize(const QSizeF &size, const QPointF &delta, Qt::Edges edges) const +{ + qreal width = size.width(); + qreal height = size.height(); + if (edges & Qt::LeftEdge) + width -= delta.x(); + else if (edges & Qt::RightEdge) + width += delta.x(); + + if (edges & Qt::TopEdge) + height -= delta.y(); + else if (edges & Qt::BottomEdge) + height += delta.y(); + + return QSizeF(width, height).toSize(); +} + +/*! + * Sends a configure event to the client. Parameter \a size contains the pixel size + * of the surface. A size of zero means the client is free to decide the size. + * Known \a states are enumerated in QWaylandXdgToplevelV6::State. + */ +uint QWaylandXdgToplevelV6::sendConfigure(const QSize &size, const QVector<QWaylandXdgToplevelV6::State> &states) +{ + Q_D(QWaylandXdgToplevelV6); + auto statesBytes = QByteArray::fromRawData(reinterpret_cast<const char *>(states.data()), + states.size() * static_cast<int>(sizeof(State))); + uint32_t serial = d->m_xdgSurface->surface()->compositor()->nextSerial(); + d->m_pendingConfigures.append(QWaylandXdgToplevelV6Private::ConfigureEvent{states, size, serial}); + d->send_configure(size.width(), size.height(), statesBytes); + QWaylandXdgSurfaceV6Private::get(d->m_xdgSurface)->send_configure(serial); + return serial; +} + +/*! + * \qmlmethod int QtWaylandCompositor::XdgToplevelV6::sendConfigure(size size, list<int> states) + * + * Sends a configure event to the client. \a size contains the pixel size of the surface. + * A size of zero means the client is free to decide the size. + * Known \a states are enumerated in XdgToplevelV6::State. + */ +uint QWaylandXdgToplevelV6::sendConfigure(const QSize &size, const QVector<int> &states) +{ + QVector<State> s; + for (auto state : states) + s << State(state); + return sendConfigure(size, s); +} + +/*! + * \qmlmethod void QtWaylandCompositor::XdgToplevelV6::sendClose() + * + * Sends a close event to the client. The client may choose to ignore the event. + */ + +/*! + * Sends a close event to the client. The client may choose to ignore the event. + */ +void QWaylandXdgToplevelV6::sendClose() +{ + Q_D(QWaylandXdgToplevelV6); + d->send_close(); +} + +/*! + * \qmlmethod void QtWaylandCompositor::XdgToplevelV6::sendMaximized() + * + * Convenience for sending a configure event with the maximized state set, and + * fullscreen and resizing removed. The activated state is left in its current state. + */ + +/*! + * Convenience for sending a configure event with the maximized state set, and + * fullscreen and resizing removed. The activated state is left in its current state. + */ +uint QWaylandXdgToplevelV6::sendMaximized(const QSize &size) +{ + Q_D(QWaylandXdgToplevelV6); + QWaylandXdgToplevelV6Private::ConfigureEvent conf = d->lastSentConfigure(); + + if (!conf.states.contains(QWaylandXdgToplevelV6::State::MaximizedState)) + conf.states.append(QWaylandXdgToplevelV6::State::MaximizedState); + conf.states.removeOne(QWaylandXdgToplevelV6::State::FullscreenState); + conf.states.removeOne(QWaylandXdgToplevelV6::State::ResizingState); + + return sendConfigure(size, conf.states); +} + +/*! + * \qmlmethod void QtWaylandCompositor::XdgToplevelV6::sendUnmaximized() + * + * Convenience for sending a configure event with the maximized, fullscreen and + * resizing states removed, and fullscreen and resizing removed. The activated + * state is left in its current state. + * + * \a size is the new size of the window. If \a size is zero, the client decides the size. + */ + +/*! + * Convenience for sending a configure event with the maximized, fullscreen and + * resizing states removed, and fullscreen and resizing removed. The activated + * state is left in its current state. + * + * \a size is the new size of the window. If \a size is zero, the client decides the size. + */ +uint QWaylandXdgToplevelV6::sendUnmaximized(const QSize &size) +{ + Q_D(QWaylandXdgToplevelV6); + QWaylandXdgToplevelV6Private::ConfigureEvent conf = d->lastSentConfigure(); + + conf.states.removeOne(QWaylandXdgToplevelV6::State::MaximizedState); + conf.states.removeOne(QWaylandXdgToplevelV6::State::FullscreenState); + conf.states.removeOne(QWaylandXdgToplevelV6::State::ResizingState); + + return sendConfigure(size, conf.states); + +} + +/*! + * \qmlmethod void QtWaylandCompositor::XdgToplevelV6::sendFullscreen() + * + * Convenience for sending a configure event with the fullscreen state set, and + * maximized and resizing removed. The activated state is left in its current state. + * + * \a size is the new size of the window. + */ + +/*! + * Convenience for sending a configure event with the fullscreen state set, and + * maximized and resizing removed. The activated state is left in its current state. + * + * \a size is the new size of the window. + */ +uint QWaylandXdgToplevelV6::sendFullscreen(const QSize &size) +{ + Q_D(QWaylandXdgToplevelV6); + QWaylandXdgToplevelV6Private::ConfigureEvent conf = d->lastSentConfigure(); + + if (!conf.states.contains(QWaylandXdgToplevelV6::State::FullscreenState)) + conf.states.append(QWaylandXdgToplevelV6::State::FullscreenState); + conf.states.removeOne(QWaylandXdgToplevelV6::State::MaximizedState); + conf.states.removeOne(QWaylandXdgToplevelV6::State::ResizingState); + + return sendConfigure(size, conf.states); +} + +/*! + * \qmlmethod void QtWaylandCompositor::XdgToplevelV6::sendResizing() + * + * Convenience for sending a configure event with the resizing state set, and + * maximized and fullscreen removed. The activated state is left in its current state. + * + * \a size is the new size of the window. + */ + +/*! + * Convenience for sending a configure event with the resizing state set, and + * maximized and fullscreen removed. The activated state is left in its current state. + * + * \a size is the new size of the window. + */ +uint QWaylandXdgToplevelV6::sendResizing(const QSize &maxSize) +{ + Q_D(QWaylandXdgToplevelV6); + QWaylandXdgToplevelV6Private::ConfigureEvent conf = d->lastSentConfigure(); + + if (!conf.states.contains(QWaylandXdgToplevelV6::State::ResizingState)) + conf.states.append(QWaylandXdgToplevelV6::State::ResizingState); + conf.states.removeOne(QWaylandXdgToplevelV6::State::MaximizedState); + conf.states.removeOne(QWaylandXdgToplevelV6::State::FullscreenState); + + return sendConfigure(maxSize, conf.states); +} + +/*! + * Returns the surface role for the QWaylandToplevelV6. + */ +QWaylandSurfaceRole *QWaylandXdgToplevelV6::role() +{ + return &QWaylandXdgToplevelV6Private::s_role; +} + +QList<int> QWaylandXdgToplevelV6::statesAsInts() const +{ + QList<int> list; + Q_FOREACH (uint state, states()) { + list << static_cast<int>(state); + } + return list; +} + +QWaylandSurfaceRole QWaylandXdgToplevelV6Private::s_role("zxdg_toplevel_v6"); + +QWaylandXdgToplevelV6Private::QWaylandXdgToplevelV6Private(QWaylandXdgSurfaceV6 *xdgSurface, const QWaylandResource &resource) + : m_xdgSurface(xdgSurface) + , m_parentToplevel(nullptr) + , m_lastAckedConfigure({{}, QSize(0, 0), 0}) +{ + init(resource.resource()); +} + +void QWaylandXdgToplevelV6Private::handleAckConfigure(uint serial) +{ + Q_Q(QWaylandXdgToplevelV6); + ConfigureEvent config; + Q_FOREVER { + if (m_pendingConfigures.empty()) { + qWarning("Toplevel received an unexpected ack_configure!"); + return; + } + + // This won't work unless there always is a toplevel.configure for each xdgsurface.configure + config = m_pendingConfigures.takeFirst(); + + if (config.serial == serial) + break; + } + + QVector<uint> changedStates; + std::set_symmetric_difference( + m_lastAckedConfigure.states.begin(), m_lastAckedConfigure.states.end(), + config.states.begin(), config.states.end(), + std::back_inserter(changedStates)); + + m_lastAckedConfigure = config; + + for (uint state : changedStates) { + switch (state) { + case state_maximized: + emit q->maximizedChanged(); + break; + case state_fullscreen: + emit q->fullscreenChanged(); + break; + case state_resizing: + emit q->resizingChanged(); + break; + case state_activated: + emit q->activatedChanged(); + break; + } + } + + if (!changedStates.empty()) + emit q->statesChanged(); +} + +void QWaylandXdgToplevelV6Private::handleFocusLost() +{ + Q_Q(QWaylandXdgToplevelV6); + QWaylandXdgToplevelV6Private::ConfigureEvent current = lastSentConfigure(); + current.states.removeOne(QWaylandXdgToplevelV6::State::ActivatedState); + q->sendConfigure(current.size, current.states); +} + +void QWaylandXdgToplevelV6Private::handleFocusReceived() +{ + Q_Q(QWaylandXdgToplevelV6); + QWaylandXdgToplevelV6Private::ConfigureEvent current = lastSentConfigure(); + if (!current.states.contains(QWaylandXdgToplevelV6::State::ActivatedState)) { + current.states.push_back(QWaylandXdgToplevelV6::State::ActivatedState); + q->sendConfigure(current.size, current.states); + } +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_destroy_resource(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + Q_Q(QWaylandXdgToplevelV6); + delete q; +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_destroy(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + wl_resource_destroy(resource->handle); + //TODO: Should the xdg surface be desroyed as well? Or is it allowed to recreate a new toplevel for it? +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_set_parent(QtWaylandServer::zxdg_toplevel_v6::Resource *resource, wl_resource *parent) +{ + Q_UNUSED(resource); + QWaylandXdgToplevelV6 *parentToplevel = nullptr; + if (parent) { + parentToplevel = static_cast<QWaylandXdgToplevelV6Private *>( + QWaylandXdgToplevelV6Private::Resource::fromResource(parent)->zxdg_toplevel_v6_object)->q_func(); + } + + Q_Q(QWaylandXdgToplevelV6); + + if (m_parentToplevel != parentToplevel) { + m_parentToplevel = parentToplevel; + emit q->parentToplevelChanged(); + } + + if (m_parentToplevel && m_xdgSurface->windowType() != Qt::WindowType::SubWindow) { + // There's a parent now, which means the surface is transient + QWaylandXdgSurfaceV6Private::get(m_xdgSurface)->setWindowType(Qt::WindowType::SubWindow); + } else if (!m_parentToplevel && m_xdgSurface->windowType() != Qt::WindowType::Window) { + // When the surface has no parent it is toplevel + QWaylandXdgSurfaceV6Private::get(m_xdgSurface)->setWindowType(Qt::WindowType::Window); + } +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_set_title(QtWaylandServer::zxdg_toplevel_v6::Resource *resource, const QString &title) +{ + Q_UNUSED(resource); + if (title == m_title) + return; + Q_Q(QWaylandXdgToplevelV6); + m_title = title; + emit q->titleChanged(); +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_set_app_id(QtWaylandServer::zxdg_toplevel_v6::Resource *resource, const QString &app_id) +{ + Q_UNUSED(resource); + if (app_id == m_appId) + return; + Q_Q(QWaylandXdgToplevelV6); + m_appId = app_id; + emit q->appIdChanged(); +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_show_window_menu(QtWaylandServer::zxdg_toplevel_v6::Resource *resource, wl_resource *seatResource, uint32_t serial, int32_t x, int32_t y) +{ + Q_UNUSED(resource); + Q_UNUSED(serial); + QPoint position(x, y); + auto seat = QWaylandSeat::fromSeatResource(seatResource); + Q_Q(QWaylandXdgToplevelV6); + emit q->showWindowMenu(seat, position); +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_move(Resource *resource, wl_resource *seatResource, uint32_t serial) +{ + Q_UNUSED(resource); + Q_UNUSED(serial); + Q_Q(QWaylandXdgToplevelV6); + QWaylandSeat *seat = QWaylandSeat::fromSeatResource(seatResource); + emit q->startMove(seat); +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_resize(QtWaylandServer::zxdg_toplevel_v6::Resource *resource, wl_resource *seatResource, uint32_t serial, uint32_t edges) +{ + Q_UNUSED(resource); + Q_UNUSED(serial); + Q_Q(QWaylandXdgToplevelV6); + QWaylandSeat *seat = QWaylandSeat::fromSeatResource(seatResource); + emit q->startResize(seat, QWaylandXdgShellV6Private::convertToEdges(edges)); +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_set_max_size(QtWaylandServer::zxdg_toplevel_v6::Resource *resource, int32_t width, int32_t height) +{ + Q_UNUSED(resource); + + QSize maxSize(width, height); + if (width == 0 && height == 0) + maxSize = QSize(); // Wayland size of zero means unspecified which best translates to invalid + + if (m_maxSize == maxSize) + return; + + if (width < 0 || height < 0) { + // The spec says raise a protocol error, but there's no matching error defined + qWarning() << "Received a zxdg_toplevel_v6.set_max_size request with a negative size"; + return; + } + + if (m_minSize.isValid() && maxSize.isValid() && + (maxSize.width() < m_minSize.width() || maxSize.height() < m_minSize.height())) { + // The spec says raise a protocol error, but there's no matching error defined + qWarning() << "Received a zxdg_toplevel_v6.set_max_size request with a size smaller than the minimium size"; + return; + } + + else + m_maxSize = maxSize; + + Q_Q(QWaylandXdgToplevelV6); + emit q->maxSizeChanged(); +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_set_min_size(QtWaylandServer::zxdg_toplevel_v6::Resource *resource, int32_t width, int32_t height) +{ + Q_UNUSED(resource); + + QSize minSize(width, height); + if (width == 0 && height == 0) + minSize = QSize(); // Wayland size of zero means unspecified + + if (m_minSize == minSize) + return; + + if (width < 0 || height < 0) { + // The spec says raise a protocol error, but there's no matching error defined + qWarning() << "Received a zxdg_toplevel_v6.set_min_size request with a negative size"; + return; + } + + if (m_maxSize.isValid() && minSize.isValid() && + (minSize.width() > m_maxSize.width() || minSize.height() > m_maxSize.height())) { + // The spec says raise a protocol error, but there's no matching error defined + qWarning() << "Received a zxdg_toplevel_v6.set_min_size request with a size larger than the maximum size"; + return; + } + + else + m_minSize = minSize; + + Q_Q(QWaylandXdgToplevelV6); + emit q->minSizeChanged(); +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_set_maximized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + Q_Q(QWaylandXdgToplevelV6); + emit q->setMaximized(); +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_unset_maximized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + Q_Q(QWaylandXdgToplevelV6); + emit q->unsetMaximized(); +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_set_fullscreen(QtWaylandServer::zxdg_toplevel_v6::Resource *resource, wl_resource *output_res) +{ + Q_UNUSED(resource); + Q_Q(QWaylandXdgToplevelV6); + QWaylandOutput *output = output_res ? QWaylandOutput::fromResource(output_res) : nullptr; + emit q->setFullscreen(output); +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_unset_fullscreen(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + Q_Q(QWaylandXdgToplevelV6); + emit q->unsetFullscreen(); +} + +void QWaylandXdgToplevelV6Private::zxdg_toplevel_v6_set_minimized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + Q_Q(QWaylandXdgToplevelV6); + emit q->setMinimized(); +} + +/*! + * \qmltype XdgPopupV6 + * \inqmlmodule QtWayland.Compositor + * \since 5.10 + * \brief XdgPopupV6 represents the popup specific parts of and xdg surface + * + * This type is part of the \l{XdgShellV6} extension and provides a way to extend + * extend the functionality of an \l{XdgSurfaceV6} with features + * specific to desktop-style menus for an xdg surface. + * + * It corresponds to the Wayland interface \c zxdg_popup_v6. + */ + +/*! + * \class QWaylandXdgPopupV6 + * \inmodule QtWaylandCompositor + * \since 5.10 + * \brief The QWaylandXdgPopupV6 class represents the popup specific parts of an xdg surface + * + * This class is part of the QWaylandXdgShellV6 extension and provides a way to + * extend the functionality of a QWaylandXdgSurfaceV6 with features + * specific to desktop-style menus for an xdg surface. + * + * It corresponds to the Wayland interface \c zxdg_popup_v6. + */ + +/*! + * Constructs a QWaylandXdgPopupV6. + */ +QWaylandXdgPopupV6::QWaylandXdgPopupV6(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandXdgSurfaceV6 *parentXdgSurface, + QWaylandXdgPositionerV6 *positioner, QWaylandResource &resource) + : QObject(*new QWaylandXdgPopupV6Private(xdgSurface, parentXdgSurface, positioner, resource)) +{ +} + +/*! + * \qmlproperty XdgSurfaceV6 QtWaylandCompositor::XdgPopupV6::xdgSurface + * + * This property holds the XdgSurfaceV6 associated with this XdgPopupV6. + */ + +/*! + * \property QWaylandXdgPopupV6::xdgSurface + * + * This property holds the QWaylandXdgSurfaceV6 associated with this QWaylandXdgPopupV6. + */ +QWaylandXdgSurfaceV6 *QWaylandXdgPopupV6::xdgSurface() const +{ + Q_D(const QWaylandXdgPopupV6); + return d->m_xdgSurface; +} + +/*! + * \qmlproperty XdgSurfaceV6 QtWaylandCompositor::XdgPopupV6::parentXdgSurface + * + * This property holds the XdgSurfaceV6 associated with the parent of this XdgPopupV6. + */ + +/*! + * \property QWaylandXdgPopupV6::parentXdgSurface + * + * This property holds the QWaylandXdgSurfaceV6 associated with the parent of this + * QWaylandXdgPopupV6. + */ +QWaylandXdgSurfaceV6 *QWaylandXdgPopupV6::parentXdgSurface() const +{ + Q_D(const QWaylandXdgPopupV6); + return d->m_parentXdgSurface; +} + +/*! + * \qmlproperty rect QtWaylandCompositor::XdgPopupV6::configuredGeometry + * + * The window geometry the popup received in the configure event. Relative to the + * upper left corner of the parent surface. + */ + +/*! + * \property QWaylandXdgPopupV6::configuredGeometry + * + * The window geometry the popup received in the configure event. Relative to the + * upper left corner of the parent surface. + */ +QRect QWaylandXdgPopupV6::configuredGeometry() const +{ + Q_D(const QWaylandXdgPopupV6); + return d->m_geometry; +} + +/*! + * \qmlproperty rect QtWaylandCompositor::XdgPopupV6::anchorRect + * + * The anchor rectangle relative to the parent window geometry that the child + * surface should be placed relative to. + */ + +/*! + * \property QWaylandXdgPopupV6::anchorRect + * + * Returns the anchor rectangle relative to the parent window geometry that the child + * surface should be placed relative to. + */ +QRect QWaylandXdgPopupV6::anchorRect() const +{ + Q_D(const QWaylandXdgPopupV6); + return d->m_positionerData.anchorRect; +} + +/*! + * \qmlproperty enumeration QtWaylandCompositor::XdgPopupV6::anchorEdges + * + * This property holds the set of edges on the anchor rect that the child surface should be placed + * relative to. If no edges are specified in a direction, the anchor point should be + * centered between the edges. + * + * The possible values are: + * \value Qt.TopEdge The top edge of the rectangle. + * \value Qt.LeftEdge The left edge of the rectangle. + * \value Qt.RightEdge The right edge of the rectangle. + * \value Qt.BottomEdge The bottom edge of the rectangle. + */ + +/*! + * \property QWaylandXdgPopupV6::anchorEdges + * + * Returns the set of edges on the anchor rect that the child surface should be placed + * relative to. If no edges are specified in a direction, the anchor point should be + * centered between the edges. + */ +Qt::Edges QWaylandXdgPopupV6::anchorEdges() const +{ + Q_D(const QWaylandXdgPopupV6); + return d->m_positionerData.anchorEdges; +} + +/*! + * \qmlproperty rect QtWaylandCompositor::XdgPopupV6::gravityEdges + * + * Specifies in what direction the surface should be positioned, relative to the anchor + * point. + * + * The possible values are: + * \value Qt.TopEdge The surface should slide towards the top of the screen. + * \value Qt.LeftEdge The surface should slide towards the left of the screen. + * \value Qt.RightEdge The surface should slide towards the right of the screen. + * \value Qt.BottomEdge The surface should slide towards the bottom of the screen. + */ + +/*! + * \property QWaylandXdgPopupV6::gravityEdges + * + * Specifies in what direction the surface should be positioned, relative to the anchor + * point. + */ +Qt::Edges QWaylandXdgPopupV6::gravityEdges() const +{ + Q_D(const QWaylandXdgPopupV6); + return d->m_positionerData.gravityEdges; +} + +/*! + * \qmlproperty enumeration QtWaylandCompositor::XdgPopupV6::slideConstraints + * + * This property holds the orientations in which the child should slide to fit within the screen. + * + * Possible values: + * \value Qt.Horizontal Horizontal + * \value Qt.Vertical Vertical + */ + +/*! + * \property QWaylandXdgPopup::slideConstraints + * + * This property holds the orientations in which the child should slide to fit within the screen. + */ +Qt::Orientations QWaylandXdgPopupV6::slideConstraints() const +{ + Q_D(const QWaylandXdgPopupV6); + const uint flags = d->m_positionerData.constraintAdjustments; + + Qt::Orientations constraints = 0; + + if (flags & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X) + constraints |= Qt::Horizontal; + if (flags & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y) + constraints |= Qt::Vertical; + + return constraints; +} + +/*! + * \qmlproperty enumeration QtWaylandCompositor::XdgPopupV6::flipConstraints + * + * This property holds the orientations in which the child should flip to fit within the screen. + * + * Possible values: + * \value Qt.Horizontal Horizontal + * \value Qt.Vertical Vertical + */ + +/*! + * \property QWaylandXdgPopup::flipConstraints + * + * This property holds the orientations in which the child should flip to fit within the screen. + */ +Qt::Orientations QWaylandXdgPopupV6::flipConstraints() const +{ + Q_D(const QWaylandXdgPopupV6); + const uint flags = d->m_positionerData.constraintAdjustments; + + Qt::Orientations constraints = 0; + + if (flags & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X) + constraints |= Qt::Horizontal; + if (flags & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y) + constraints |= Qt::Vertical; + + return constraints; +} + +/*! + * \qmlproperty enumeration QtWaylandCompositor::XdgPopupV6::resizeConstraints + * + * This property holds the orientations in which the child should resize to fit within the screen. + * + * Possible values: + * \value Qt.Horizontal Horizontal + * \value Qt.Vertical Vertical + */ + +/*! + * \property QWaylandXdgPopup::resizeConstraints + * + * This property holds the orientations in which the child should resize to fit within the screen. + */ +Qt::Orientations QWaylandXdgPopupV6::resizeConstraints() const +{ + Q_D(const QWaylandXdgPopupV6); + const uint flags = d->m_positionerData.constraintAdjustments; + + Qt::Orientations constraints = 0; + + if (flags & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X) + constraints |= Qt::Horizontal; + if (flags & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y) + constraints |= Qt::Vertical; + + return constraints; +} + +/*! + * \qmlproperty point QtWaylandCompositor::XdgPopupV6::offset + * + * The position relative to the position of the anchor on the anchor rectangle and + * the anchor on the surface. + */ + +/*! + * \property QWaylandXdgPopup::offset + * + * Returns the surface position relative to the position of the anchor on the anchor + * rectangle and the anchor on the surface. + */ +QPoint QWaylandXdgPopupV6::offset() const +{ + Q_D(const QWaylandXdgPopupV6); + return d->m_positionerData.offset; +} + +/*! + * \qmlproperty size QtWaylandCompositor::XdgPopupV6::positionerSize + * + * The size requested for the window geometry by the positioner object. + */ + +/*! + * \property QWaylandXdgPopup::positionerSize + * + * Returns the size requested for the window geometry by the positioner object. + */ +QSize QWaylandXdgPopupV6::positionerSize() const +{ + Q_D(const QWaylandXdgPopupV6); + return d->m_positionerData.size; +} + +/*! + * \qmlproperty point QtWaylandCompositor::XdgPopupV6::unconstrainedPosition + * + * The position of the surface relative to the parent window geometry if the surface + * is not constrained. I.e. when not moved to fit inside the screen or similar. + */ + +/*! + * \property QWaylandXdgPopup::unconstrainedPosition + * + * The position of the surface relative to the parent window geometry if the surface + * is not constrained. I.e. when not moved to fit inside the screen or similar. + */ +QPoint QWaylandXdgPopupV6::unconstrainedPosition() const +{ + Q_D(const QWaylandXdgPopupV6); + return d->m_positionerData.unconstrainedPosition(); +} + +/*! + * \qmlmethod int QtWaylandCompositor::XdgPopupV6::sendConfigure(rect geometry) + * + * Sends a configure event to the client. \a geometry contains the window geometry + * relative to the upper left corner of the window geometry of the parent surface. + * + * This implicitly sends a configure event to the corresponding XdgSurfaceV6 as well. + */ + +/*! + * Sends a configure event to the client. \a geometry contains the window geometry + * relative to the upper left corner of the window geometry of the parent surface. + * + * This implicitly sends a configure event to the corresponding QWaylandXdgSurfaceV6 + * as well. + */ +uint QWaylandXdgPopupV6::sendConfigure(const QRect &geometry) +{ + Q_D(QWaylandXdgPopupV6); + return d->sendConfigure(geometry); +} + +/*! + * Returns the surface role for the QWaylandPopupV6. + */ +QWaylandSurfaceRole *QWaylandXdgPopupV6::role() +{ + return &QWaylandXdgPopupV6Private::s_role; +} + +QWaylandXdgPopupV6Private::QWaylandXdgPopupV6Private(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandXdgSurfaceV6 *parentXdgSurface, + QWaylandXdgPositionerV6 *positioner, const QWaylandResource &resource) + : m_xdgSurface(xdgSurface) + , m_parentXdgSurface(parentXdgSurface) +{ + init(resource.resource()); + m_positionerData = positioner->m_data; + + if (!m_positionerData.isComplete()) + qWarning() << "Trying to create xdg popup with incomplete positioner"; + + QWaylandXdgSurfaceV6Private::get(m_xdgSurface)->setWindowType(Qt::WindowType::Popup); + + //TODO: positioner rect may not extend parent's window geometry, enforce this? + //TODO: Need an API for sending a different initial configure + sendConfigure(QRect(m_positionerData.unconstrainedPosition(), m_positionerData.size)); +} + +void QWaylandXdgPopupV6Private::handleAckConfigure(uint serial) +{ + Q_Q(QWaylandXdgPopupV6); + ConfigureEvent config; + Q_FOREVER { + if (m_pendingConfigures.empty()) { + qWarning("Popup received an unexpected ack_configure!"); + return; + } + + // This won't work unless there always is a popup.configure for each xdgsurface.configure + config = m_pendingConfigures.takeFirst(); + + if (config.serial == serial) + break; + } + + if (m_geometry == config.geometry) + return; + + m_geometry = config.geometry; + emit q->configuredGeometryChanged(); +} + +uint QWaylandXdgPopupV6Private::sendConfigure(const QRect &geometry) +{ + uint32_t serial = m_xdgSurface->surface()->compositor()->nextSerial(); + m_pendingConfigures.append(QWaylandXdgPopupV6Private::ConfigureEvent{geometry, serial}); + send_configure(geometry.x(), geometry.y(), geometry.width(), geometry.height()); + QWaylandXdgSurfaceV6Private::get(m_xdgSurface)->send_configure(serial); + return serial; +} + +void QWaylandXdgPopupV6Private::zxdg_popup_v6_destroy(QtWaylandServer::zxdg_popup_v6::Resource *resource) +{ + Q_UNUSED(resource); + qWarning() << Q_FUNC_INFO << "Not implemented"; //TODO +} + +void QWaylandXdgPopupV6Private::zxdg_popup_v6_grab(QtWaylandServer::zxdg_popup_v6::Resource *resource, wl_resource *seat, uint32_t serial) +{ + Q_UNUSED(resource); + Q_UNUSED(serial); + Q_UNUSED(seat); + qWarning() << Q_FUNC_INFO << "Not implemented"; //TODO + //switch keyboard focus + //eventually send configure with activated. +} + +QWaylandSurfaceRole QWaylandXdgPopupV6Private::s_role("zxdg_popup_v6"); + +QWaylandXdgPositionerV6Data::QWaylandXdgPositionerV6Data() + : anchorEdges(0) + , gravityEdges(0) + , constraintAdjustments(ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) + , offset(0, 0) +{} + +bool QWaylandXdgPositionerV6Data::isComplete() const +{ + return size.width() > 0 && size.height() > 0 && anchorRect.size().width() > 0 && anchorRect.size().height() > 0; +} + +QPoint QWaylandXdgPositionerV6Data::anchorPoint() const +{ + int yPosition = 0; + if (anchorEdges & Qt::TopEdge) + yPosition = anchorRect.top(); + else if (anchorEdges & Qt::BottomEdge) + yPosition = anchorRect.bottom() + 1; + else + yPosition = anchorRect.top() + anchorRect.height() / 2; + + int xPosition = 0; + if (anchorEdges & Qt::LeftEdge) + xPosition = anchorRect.left(); + else if (anchorEdges & Qt::RightEdge) + xPosition = anchorRect.right() + 1; + else + xPosition = anchorRect.left() + anchorRect.width() / 2; + + return QPoint(xPosition, yPosition); +} + +QPoint QWaylandXdgPositionerV6Data::unconstrainedPosition() const +{ + int gravityOffsetY = 0; + if (gravityEdges & Qt::TopEdge) + gravityOffsetY = -size.height(); + else if (!(gravityEdges & Qt::BottomEdge)) + gravityOffsetY = -size.height() / 2; + + int gravityOffsetX = 0; + if (gravityEdges & Qt::LeftEdge) + gravityOffsetX = -size.width(); + else if (!(gravityEdges & Qt::RightEdge)) + gravityOffsetX = -size.width() / 2; + + QPoint gravityOffset(gravityOffsetX, gravityOffsetY); + return anchorPoint() + gravityOffset + offset; +} + +QWaylandXdgPositionerV6::QWaylandXdgPositionerV6(const QWaylandResource &resource) +{ + init(resource.resource()); +} + +void QWaylandXdgPositionerV6::zxdg_positioner_v6_destroy_resource(QtWaylandServer::zxdg_positioner_v6::Resource *resource) +{ + Q_UNUSED(resource); + delete this; +} + +void QWaylandXdgPositionerV6::zxdg_positioner_v6_destroy(QtWaylandServer::zxdg_positioner_v6::Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +void QWaylandXdgPositionerV6::zxdg_positioner_v6_set_size(QtWaylandServer::zxdg_positioner_v6::Resource *resource, int32_t width, int32_t height) +{ + Q_UNUSED(resource); + + if (width <= 0 || height <= 0) { + wl_resource_post_error(resource->handle, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "zxdg_positioner_v6.set_size requested with non-positive dimensions"); + return; + } + + QSize size(width, height); + m_data.size = size; +} + +void QWaylandXdgPositionerV6::zxdg_positioner_v6_set_anchor_rect(QtWaylandServer::zxdg_positioner_v6::Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) +{ + Q_UNUSED(resource); + + if (width <= 0 || height <= 0) { + wl_resource_post_error(resource->handle, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "zxdg_positioner_v6.set_anchor_rect requested with non-positive dimensions"); + return; + } + + QRect anchorRect(x, y, width, height); + m_data.anchorRect = anchorRect; +} + +void QWaylandXdgPositionerV6::zxdg_positioner_v6_set_anchor(QtWaylandServer::zxdg_positioner_v6::Resource *resource, uint32_t anchor) +{ + Q_UNUSED(resource); + + Qt::Edges anchorEdges = QWaylandXdgShellV6Private::convertToEdges(anchor); + + if ((anchorEdges & Qt::BottomEdge && anchorEdges & Qt::TopEdge) || + (anchorEdges & Qt::LeftEdge && anchorEdges & Qt::RightEdge)) { + wl_resource_post_error(resource->handle, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "zxdg_positioner_v6.set_anchor requested with parallel edges"); + return; + } + + m_data.anchorEdges = anchorEdges; +} + +void QWaylandXdgPositionerV6::zxdg_positioner_v6_set_gravity(QtWaylandServer::zxdg_positioner_v6::Resource *resource, uint32_t gravity) +{ + Q_UNUSED(resource); + Qt::Edges gravityEdges = QWaylandXdgShellV6Private::convertToEdges(gravity); + + if ((gravityEdges & Qt::BottomEdge && gravityEdges & Qt::TopEdge) || + (gravityEdges & Qt::LeftEdge && gravityEdges & Qt::RightEdge)) { + wl_resource_post_error(resource->handle, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "zxdg_positioner_v6.set_gravity requested with parallel edges"); + return; + } + + m_data.gravityEdges = gravityEdges; +} + +void QWaylandXdgPositionerV6::zxdg_positioner_v6_set_constraint_adjustment(QtWaylandServer::zxdg_positioner_v6::Resource *resource, uint32_t constraint_adjustment) +{ + Q_UNUSED(resource); + m_data.constraintAdjustments = constraint_adjustment; +} + +void QWaylandXdgPositionerV6::zxdg_positioner_v6_set_offset(QtWaylandServer::zxdg_positioner_v6::Resource *resource, int32_t x, int32_t y) +{ + Q_UNUSED(resource); + m_data.offset = QPoint(x, y); +} + +QWaylandXdgPositionerV6 *QWaylandXdgPositionerV6::fromResource(wl_resource *resource) +{ + return static_cast<QWaylandXdgPositionerV6 *>(Resource::fromResource(resource)->zxdg_positioner_v6_object); +} + +QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandxdgshellv6.h b/src/compositor/extensions/qwaylandxdgshellv6.h new file mode 100644 index 00000000..8c5eee8a --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgshellv6.h @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#ifndef QWAYLANDXDGSHELLV6_H +#define QWAYLANDXDGSHELLV6_H + +#include <QtWaylandCompositor/QWaylandCompositorExtension> +#include <QtWaylandCompositor/QWaylandResource> +#include <QtWaylandCompositor/QWaylandShell> +#include <QtWaylandCompositor/QWaylandShellSurface> + +#include <QtCore/QRect> + +struct wl_resource; + +QT_BEGIN_NAMESPACE + +class QWaylandClient; +class QWaylandOutput; +class QWaylandSeat; +class QWaylandSurface; +class QWaylandSurfaceRole; +class QWaylandXdgShellV6Private; +class QWaylandXdgSurfaceV6; +class QWaylandXdgSurfaceV6Private; +class QWaylandXdgToplevelV6; +class QWaylandXdgToplevelV6Private; +class QWaylandXdgPopupV6; +class QWaylandXdgPopupV6Private; +class QWaylandXdgPositionerV6; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgShellV6 : public QWaylandShellTemplate<QWaylandXdgShellV6> +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandXdgShellV6) +public: + QWaylandXdgShellV6(); + QWaylandXdgShellV6(QWaylandCompositor *compositor); + + void initialize() override; + + static const struct wl_interface *interface(); + static QByteArray interfaceName(); + +public Q_SLOTS: + uint ping(QWaylandClient *client); + +Q_SIGNALS: + void xdgSurfaceCreated(QWaylandXdgSurfaceV6 *xdgSurface); + void toplevelCreated(QWaylandXdgToplevelV6 *toplevel, QWaylandXdgSurfaceV6 *xdgSurface); + void popupCreated(QWaylandXdgPopupV6 *popup, QWaylandXdgSurfaceV6 *xdgSurface); + void pong(uint serial); + +private Q_SLOTS: + void handleSeatChanged(QWaylandSeat *newSeat, QWaylandSeat *oldSeat); + void handleFocusChanged(QWaylandSurface *newSurface, QWaylandSurface *oldSurface); +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgSurfaceV6 : public QWaylandShellSurfaceTemplate<QWaylandXdgSurfaceV6> +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandXdgSurfaceV6) + Q_PROPERTY(QWaylandXdgShellV6 *shell READ shell NOTIFY shellChanged) + Q_PROPERTY(QWaylandSurface *surface READ surface NOTIFY surfaceChanged) + Q_PROPERTY(QWaylandXdgToplevelV6 *toplevel READ toplevel NOTIFY toplevelCreated) + Q_PROPERTY(QWaylandXdgPopupV6 *popup READ popup NOTIFY popupCreated) + Q_PROPERTY(QRect windowGeometry READ windowGeometry NOTIFY windowGeometryChanged) + +public: + QWaylandXdgSurfaceV6(); + QWaylandXdgSurfaceV6(QWaylandXdgShellV6* xdgShell, QWaylandSurface *surface, const QWaylandResource &resource); + + Q_INVOKABLE void initialize(QWaylandXdgShellV6* xdgShell, QWaylandSurface *surface, const QWaylandResource &resource); + + Qt::WindowType windowType() const override; + + QWaylandXdgShellV6 *shell() const; + QWaylandSurface *surface() const; + QWaylandXdgToplevelV6 *toplevel() const; + QWaylandXdgPopupV6 *popup() const; + QRect windowGeometry() const; + + static const struct wl_interface *interface(); + static QByteArray interfaceName(); + static QWaylandXdgSurfaceV6 *fromResource(::wl_resource *resource); + +#ifdef QT_WAYLAND_COMPOSITOR_QUICK + QWaylandQuickShellIntegration *createIntegration(QWaylandQuickShellSurfaceItem *item) override; +#endif + +Q_SIGNALS: + void shellChanged(); + void surfaceChanged(); + void toplevelCreated(); + void popupCreated(); + void windowGeometryChanged(); + +private: + void initialize() override; + +private Q_SLOTS: + void handleSurfaceSizeChanged(); + void handleBufferScaleChanged(); +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgToplevelV6 : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandXdgToplevelV6) + Q_PROPERTY(QWaylandXdgToplevelV6 *parentToplevel READ parentToplevel NOTIFY parentToplevelChanged) + Q_PROPERTY(QString title READ title NOTIFY titleChanged) + Q_PROPERTY(QString appId READ appId NOTIFY appIdChanged) + Q_PROPERTY(QSize maxSize READ maxSize NOTIFY maxSizeChanged) + Q_PROPERTY(QSize minSize READ minSize NOTIFY minSizeChanged) + Q_PROPERTY(QList<int> states READ statesAsInts NOTIFY statesChanged) + Q_PROPERTY(bool maximized READ maximized NOTIFY maximizedChanged) + Q_PROPERTY(bool fullscreen READ fullscreen NOTIFY fullscreenChanged) + Q_PROPERTY(bool resizing READ resizing NOTIFY resizingChanged) + Q_PROPERTY(bool activated READ activated NOTIFY activatedChanged) +public: + enum State : uint { + MaximizedState = 1, + FullscreenState = 2, + ResizingState = 3, + ActivatedState = 4 + }; + Q_ENUM(State) + + QWaylandXdgToplevelV6(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandResource &resource); + + QWaylandXdgToplevelV6 *parentToplevel() const; + + QString title() const; + QString appId() const; + QSize maxSize() const; + QSize minSize() const; + QVector<QWaylandXdgToplevelV6::State> states() const; + bool maximized() const; + bool fullscreen() const; + bool resizing() const; + bool activated() const; + + Q_INVOKABLE QSize sizeForResize(const QSizeF &size, const QPointF &delta, Qt::Edges edges) const; + uint sendConfigure(const QSize &size, const QVector<State> &states); + Q_INVOKABLE uint sendConfigure(const QSize &size, const QVector<int> &states); + Q_INVOKABLE void sendClose(); + Q_INVOKABLE uint sendMaximized(const QSize &size); + Q_INVOKABLE uint sendUnmaximized(const QSize &size = QSize(0, 0)); + Q_INVOKABLE uint sendFullscreen(const QSize &size); + Q_INVOKABLE uint sendResizing(const QSize &maxSize); + + static QWaylandSurfaceRole *role(); + +Q_SIGNALS: + void parentToplevelChanged(); + void titleChanged(); + void appIdChanged(); + void maxSizeChanged(); + void minSizeChanged(); + void startMove(QWaylandSeat *seat); + void startResize(QWaylandSeat *seat, Qt::Edges edges); + void statesChanged(); + void maximizedChanged(); + void fullscreenChanged(); + void resizingChanged(); + void activatedChanged(); + + void showWindowMenu(QWaylandSeat *seat, const QPoint &localSurfacePosition); + void setMaximized(); + void unsetMaximized(); + void setFullscreen(QWaylandOutput *output); + void unsetFullscreen(); + void setMinimized(); + +private: + QList<int> statesAsInts() const; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgPopupV6 : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandXdgPopupV6) + Q_PROPERTY(QWaylandXdgSurfaceV6 *xdgSurface READ xdgSurface) + Q_PROPERTY(QWaylandXdgSurfaceV6 *parentXdgSurface READ parentXdgSurface) + Q_PROPERTY(QRect configuredGeometry READ configuredGeometry NOTIFY configuredGeometryChanged) + + // Positioner properties + Q_PROPERTY(QRect anchorRect READ anchorRect CONSTANT) + Q_PROPERTY(Qt::Edges anchorEdges READ anchorEdges CONSTANT) + Q_PROPERTY(Qt::Edges gravityEdges READ gravityEdges CONSTANT) + Q_PROPERTY(Qt::Orientations slideConstraints READ slideConstraints CONSTANT) + Q_PROPERTY(Qt::Orientations flipConstraints READ flipConstraints CONSTANT) + Q_PROPERTY(Qt::Orientations resizeConstraints READ resizeConstraints CONSTANT) + Q_PROPERTY(QPoint offset READ offset CONSTANT) + Q_PROPERTY(QSize positionerSize READ positionerSize CONSTANT) + Q_PROPERTY(QPoint unconstrainedPosition READ unconstrainedPosition CONSTANT) +public: + QWaylandXdgSurfaceV6 *xdgSurface() const; + QWaylandXdgSurfaceV6 *parentXdgSurface() const; + QRect configuredGeometry() const; + + // Positioner properties + QRect anchorRect() const; + Qt::Edges anchorEdges() const ; + Qt::Edges gravityEdges() const ; + Qt::Orientations slideConstraints() const; + Qt::Orientations flipConstraints() const; + Qt::Orientations resizeConstraints() const; + QPoint offset() const; + QSize positionerSize() const; + QPoint unconstrainedPosition() const; + + Q_INVOKABLE uint sendConfigure(const QRect &geometry); + + static QWaylandSurfaceRole *role(); + +Q_SIGNALS: + void configuredGeometryChanged(); + +private: + QWaylandXdgPopupV6(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandXdgSurfaceV6 *parentXdgSurface, + QWaylandXdgPositionerV6 *positioner, QWaylandResource &resource); + friend class QWaylandXdgSurfaceV6Private; +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDXDGSHELLV6_H diff --git a/src/compositor/extensions/qwaylandxdgshellv6_p.h b/src/compositor/extensions/qwaylandxdgshellv6_p.h new file mode 100644 index 00000000..98bcb70f --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgshellv6_p.h @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#ifndef QWAYLANDXDGSHELLV6_P_H +#define QWAYLANDXDGSHELLV6_P_H + +#include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h> +#include <QtWaylandCompositor/private/qwayland-server-xdg-shell-unstable-v6.h> + +#include <QtWaylandCompositor/QWaylandXdgShellV6> + +#include <QtCore/QSet> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +struct Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgPositionerV6Data { + QSize size; + QRect anchorRect; + Qt::Edges anchorEdges; + Qt::Edges gravityEdges; + uint constraintAdjustments; + QPoint offset; + QWaylandXdgPositionerV6Data(); + bool isComplete() const; + QPoint anchorPoint() const; + QPoint unconstrainedPosition() const; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgShellV6Private + : public QWaylandCompositorExtensionPrivate + , public QtWaylandServer::zxdg_shell_v6 +{ + Q_DECLARE_PUBLIC(QWaylandXdgShellV6) +public: + QWaylandXdgShellV6Private(); + void ping(Resource *resource, uint32_t serial); + void registerXdgSurface(QWaylandXdgSurfaceV6 *xdgSurface); + void unregisterXdgSurface(QWaylandXdgSurfaceV6 *xdgSurface); + static QWaylandXdgShellV6Private *get(QWaylandXdgShellV6 *xdgShell) { return xdgShell->d_func(); } + static Qt::Edges convertToEdges(uint xdgEdges); + + QSet<uint32_t> m_pings; + QMultiMap<struct wl_client *, QWaylandXdgSurfaceV6 *> m_xdgSurfaces; + + QWaylandXdgSurfaceV6 *xdgSurfaceFromSurface(QWaylandSurface *surface); + +protected: + void zxdg_shell_v6_destroy(Resource *resource) override; + void zxdg_shell_v6_create_positioner(Resource *resource, uint32_t id) override; + void zxdg_shell_v6_get_xdg_surface(Resource *resource, uint32_t id, + struct ::wl_resource *surface) override; + void zxdg_shell_v6_pong(Resource *resource, uint32_t serial) override; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgSurfaceV6Private + : public QWaylandCompositorExtensionPrivate + , public QtWaylandServer::zxdg_surface_v6 +{ + Q_DECLARE_PUBLIC(QWaylandXdgSurfaceV6) +public: + QWaylandXdgSurfaceV6Private(); + void setWindowType(Qt::WindowType windowType); + void handleFocusLost(); + void handleFocusReceived(); + static QWaylandXdgSurfaceV6Private *get(QWaylandXdgSurfaceV6 *xdgSurface) { return xdgSurface->d_func(); } + + QRect calculateFallbackWindowGeometry() const; + void updateFallbackWindowGeometry(); + +private: + QWaylandXdgShellV6 *m_xdgShell; + QWaylandSurface *m_surface; + + QWaylandXdgToplevelV6 *m_toplevel; + QWaylandXdgPopupV6 *m_popup; + QRect m_windowGeometry; + bool m_unsetWindowGeometry; + QMargins m_windowMargins; + Qt::WindowType m_windowType; + + void zxdg_surface_v6_destroy_resource(Resource *resource) override; + void zxdg_surface_v6_destroy(Resource *resource) override; + void zxdg_surface_v6_get_toplevel(Resource *resource, uint32_t id) override; + void zxdg_surface_v6_get_popup(Resource *resource, uint32_t id, struct ::wl_resource *parent, struct ::wl_resource *positioner) override; + void zxdg_surface_v6_ack_configure(Resource *resource, uint32_t serial) override; + void zxdg_surface_v6_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgToplevelV6Private : public QObjectPrivate, public QtWaylandServer::zxdg_toplevel_v6 +{ + Q_DECLARE_PUBLIC(QWaylandXdgToplevelV6) +public: + struct ConfigureEvent { + QVector<QWaylandXdgToplevelV6::State> states; + QSize size; + uint serial; + }; + + QWaylandXdgToplevelV6Private(QWaylandXdgSurfaceV6 *xdgSurface, const QWaylandResource& resource); + ConfigureEvent lastSentConfigure() const { return m_pendingConfigures.empty() ? m_lastAckedConfigure : m_pendingConfigures.first(); } + void handleAckConfigure(uint serial); //TODO: move? + void handleFocusLost(); + void handleFocusReceived(); + + static QWaylandXdgToplevelV6Private *get(QWaylandXdgToplevelV6 *toplevel) { return toplevel->d_func(); } + +protected: + + void zxdg_toplevel_v6_destroy_resource(Resource *resource) override; + + void zxdg_toplevel_v6_destroy(Resource *resource) override; + void zxdg_toplevel_v6_set_parent(Resource *resource, struct ::wl_resource *parent) override; + void zxdg_toplevel_v6_set_title(Resource *resource, const QString &title) override; + void zxdg_toplevel_v6_set_app_id(Resource *resource, const QString &app_id) override; + void zxdg_toplevel_v6_show_window_menu(Resource *resource, struct ::wl_resource *seat, uint32_t serial, int32_t x, int32_t y) override; + void zxdg_toplevel_v6_move(Resource *resource, struct ::wl_resource *seatResource, uint32_t serial) override; + void zxdg_toplevel_v6_resize(Resource *resource, struct ::wl_resource *seat, uint32_t serial, uint32_t edges) override; + void zxdg_toplevel_v6_set_max_size(Resource *resource, int32_t width, int32_t height) override; + void zxdg_toplevel_v6_set_min_size(Resource *resource, int32_t width, int32_t height) override; + void zxdg_toplevel_v6_set_maximized(Resource *resource) override; + void zxdg_toplevel_v6_unset_maximized(Resource *resource) override; + void zxdg_toplevel_v6_set_fullscreen(Resource *resource, struct ::wl_resource *output) override; + void zxdg_toplevel_v6_unset_fullscreen(Resource *resource) override; + void zxdg_toplevel_v6_set_minimized(Resource *resource) override; + +public: + QWaylandXdgSurfaceV6 *m_xdgSurface; + QWaylandXdgToplevelV6 *m_parentToplevel; + QList<ConfigureEvent> m_pendingConfigures; + ConfigureEvent m_lastAckedConfigure; + QString m_title; + QString m_appId; + QSize m_maxSize; + QSize m_minSize; + + static QWaylandSurfaceRole s_role; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgPopupV6Private : public QObjectPrivate, public QtWaylandServer::zxdg_popup_v6 +{ + Q_DECLARE_PUBLIC(QWaylandXdgPopupV6) +public: + struct ConfigureEvent { + QRect geometry; + uint serial; + }; + + QWaylandXdgPopupV6Private(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandXdgSurfaceV6 *parentXdgSurface, + QWaylandXdgPositionerV6 *positioner, const QWaylandResource& resource); + + void handleAckConfigure(uint serial); + + static QWaylandXdgPopupV6Private *get(QWaylandXdgPopupV6 *popup) { return popup->d_func(); } + + static QWaylandSurfaceRole s_role; + +private: + uint sendConfigure(const QRect &geometry); + +protected: + void zxdg_popup_v6_destroy(Resource *resource) override; + void zxdg_popup_v6_grab(Resource *resource, struct ::wl_resource *seat, uint32_t serial) override; + +private: + QWaylandXdgSurfaceV6 *m_xdgSurface; + QWaylandXdgSurfaceV6 *m_parentXdgSurface; + QWaylandXdgPositionerV6Data m_positionerData; + QRect m_geometry; + QList<ConfigureEvent> m_pendingConfigures; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgPositionerV6 : public QtWaylandServer::zxdg_positioner_v6 +{ +public: + QWaylandXdgPositionerV6(const QWaylandResource& resource); + static QWaylandXdgPositionerV6 *fromResource(wl_resource *resource); + +protected: + void zxdg_positioner_v6_destroy_resource(Resource *resource) override; //TODO: do something special here? + + void zxdg_positioner_v6_destroy(Resource *resource) override; + void zxdg_positioner_v6_set_size(Resource *resource, int32_t width, int32_t height) override; + void zxdg_positioner_v6_set_anchor_rect(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override; + void zxdg_positioner_v6_set_anchor(Resource *resource, uint32_t anchor) override; + void zxdg_positioner_v6_set_gravity(Resource *resource, uint32_t gravity) override; + void zxdg_positioner_v6_set_constraint_adjustment(Resource *resource, uint32_t constraint_adjustment) override; + void zxdg_positioner_v6_set_offset(Resource *resource, int32_t x, int32_t y) override; + +public: + QWaylandXdgPositionerV6Data m_data; +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDXDGSHELLV6_P_H diff --git a/src/compositor/extensions/qwaylandxdgshellv6integration.cpp b/src/compositor/extensions/qwaylandxdgshellv6integration.cpp new file mode 100644 index 00000000..17ee10db --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgshellv6integration.cpp @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** 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 "qwaylandxdgshellv6integration_p.h" + +#include <QtWaylandCompositor/QWaylandXdgSurfaceV6> +#include <QtWaylandCompositor/QWaylandCompositor> +#include <QtWaylandCompositor/QWaylandSeat> + +QT_BEGIN_NAMESPACE + +namespace QtWayland { + +XdgToplevelV6Integration::XdgToplevelV6Integration(QWaylandQuickShellSurfaceItem *item) + : QWaylandQuickShellIntegration(item) + , m_item(item) + , m_xdgSurface(qobject_cast<QWaylandXdgSurfaceV6 *>(item->shellSurface())) + , m_toplevel(m_xdgSurface->toplevel()) + , grabberState(GrabberState::Default) +{ + Q_ASSERT(m_toplevel); + + m_item->setSurface(m_xdgSurface->surface()); + connect(m_toplevel, &QWaylandXdgToplevelV6::startMove, this, &XdgToplevelV6Integration::handleStartMove); + connect(m_toplevel, &QWaylandXdgToplevelV6::startResize, this, &XdgToplevelV6Integration::handleStartResize); + connect(m_toplevel, &QWaylandXdgToplevelV6::setMaximized, this, &XdgToplevelV6Integration::handleSetMaximized); + connect(m_toplevel, &QWaylandXdgToplevelV6::unsetMaximized, this, &XdgToplevelV6Integration::handleUnsetMaximized); + connect(m_toplevel, &QWaylandXdgToplevelV6::maximizedChanged, this, &XdgToplevelV6Integration::handleMaximizedChanged); + connect(m_toplevel, &QWaylandXdgToplevelV6::activatedChanged, this, &XdgToplevelV6Integration::handleActivatedChanged); + connect(m_xdgSurface->surface(), &QWaylandSurface::sizeChanged, this, &XdgToplevelV6Integration::handleSurfaceSizeChanged); +} + +bool XdgToplevelV6Integration::mouseMoveEvent(QMouseEvent *event) +{ + if (grabberState == GrabberState::Resize) { + Q_ASSERT(resizeState.seat == m_item->compositor()->seatFor(event)); + if (!resizeState.initialized) { + resizeState.initialMousePos = event->windowPos(); + resizeState.initialized = true; + return true; + } + QPointF delta = m_item->mapToSurface(event->windowPos() - resizeState.initialMousePos); + QSize newSize = m_toplevel->sizeForResize(resizeState.initialWindowSize, delta, resizeState.resizeEdges); + m_toplevel->sendResizing(newSize); + } else if (grabberState == GrabberState::Move) { + Q_ASSERT(moveState.seat == m_item->compositor()->seatFor(event)); + QQuickItem *moveItem = m_item->moveItem(); + if (!moveState.initialized) { + moveState.initialOffset = moveItem->mapFromItem(nullptr, event->windowPos()); + moveState.initialized = true; + return true; + } + if (!moveItem->parentItem()) + return true; + QPointF parentPos = moveItem->parentItem()->mapFromItem(nullptr, event->windowPos()); + moveItem->setPosition(parentPos - moveState.initialOffset); + } + return false; +} + +bool XdgToplevelV6Integration::mouseReleaseEvent(QMouseEvent *event) +{ + Q_UNUSED(event); + + if (grabberState == GrabberState::Move) { + grabberState = GrabberState::Default; + return true; + } + return false; +} + +void XdgToplevelV6Integration::handleStartMove(QWaylandSeat *seat) +{ + grabberState = GrabberState::Move; + moveState.seat = seat; + moveState.initialized = false; +} + +void XdgToplevelV6Integration::handleStartResize(QWaylandSeat *seat, Qt::Edges edges) +{ + grabberState = GrabberState::Resize; + resizeState.seat = seat; + resizeState.resizeEdges = edges; + resizeState.initialWindowSize = m_xdgSurface->windowGeometry().size(); + resizeState.initialPosition = m_item->moveItem()->position(); + resizeState.initialSurfaceSize = m_item->surface()->size(); + resizeState.initialized = false; +} + +void XdgToplevelV6Integration::handleSetMaximized() +{ + if (!m_item->view()->isPrimary()) + return; + + maximizeState.initialWindowSize = m_xdgSurface->windowGeometry().size(); + maximizeState.initialPosition = m_item->moveItem()->position(); + + QWaylandOutput *output = m_item->view()->output(); + m_toplevel->sendMaximized(output->availableGeometry().size() / output->scaleFactor()); +} + +void XdgToplevelV6Integration::handleUnsetMaximized() +{ + if (!m_item->view()->isPrimary()) + return; + + m_toplevel->sendUnmaximized(maximizeState.initialWindowSize); +} + +void XdgToplevelV6Integration::handleMaximizedChanged() +{ + if (m_toplevel->maximized()) { + QWaylandOutput *output = m_item->view()->output(); + m_item->moveItem()->setPosition(output->position() + output->availableGeometry().topLeft()); + } else { + m_item->moveItem()->setPosition(maximizeState.initialPosition); + } +} + +void XdgToplevelV6Integration::handleActivatedChanged() +{ + if (m_toplevel->activated()) + m_item->raise(); +} + +void XdgToplevelV6Integration::handleSurfaceSizeChanged() +{ + if (grabberState == GrabberState::Resize) { + qreal x = resizeState.initialPosition.x(); + qreal y = resizeState.initialPosition.y(); + if (resizeState.resizeEdges & Qt::TopEdge) + y += resizeState.initialSurfaceSize.height() - m_item->surface()->size().height(); + + if (resizeState.resizeEdges & Qt::LeftEdge) + x += resizeState.initialSurfaceSize.width() - m_item->surface()->size().width(); + m_item->moveItem()->setPosition(QPointF(x, y)); + } +} + +XdgPopupV6Integration::XdgPopupV6Integration(QWaylandQuickShellSurfaceItem *item) + : m_item(item) + , m_xdgSurface(qobject_cast<QWaylandXdgSurfaceV6 *>(item->shellSurface())) + , m_popup(m_xdgSurface->popup()) +{ + Q_ASSERT(m_popup); + + m_item->setSurface(m_xdgSurface->surface()); + handleGeometryChanged(); + + connect(m_popup, &QWaylandXdgPopupV6::configuredGeometryChanged, this, &XdgPopupV6Integration::handleGeometryChanged); +} + +void XdgPopupV6Integration::handleGeometryChanged() +{ + if (m_item->view()->output()) { + const QPoint windowOffset = m_popup->parentXdgSurface()->windowGeometry().topLeft(); + const QPoint position = m_popup->unconstrainedPosition() + windowOffset; + //TODO: positioner size or other size...? + const float scaleFactor = m_item->view()->output()->scaleFactor(); + //TODO check positioner constraints etc... sliding, flipping + m_item->moveItem()->setPosition(position * scaleFactor); + } else { + qWarning() << "XdgPopupV6Integration popup item without output" << m_item; + } +} + +} + +QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandxdgshellv6integration_p.h b/src/compositor/extensions/qwaylandxdgshellv6integration_p.h new file mode 100644 index 00000000..36dd21b6 --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgshellv6integration_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#ifndef QWAYLANDXDGSHELLV6INTEGRATION_H +#define QWAYLANDXDGSHELLV6INTEGRATION_H + +#include <QtWaylandCompositor/private/qwaylandquickshellsurfaceitem_p.h> +#include <QtWaylandCompositor/QWaylandQuickShellSurfaceItem> +#include <QtWaylandCompositor/QWaylandXdgToplevelV6> + +QT_BEGIN_NAMESPACE + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +class QWaylandXdgSurfaceV6; + +namespace QtWayland { + +class XdgToplevelV6Integration : public QWaylandQuickShellIntegration +{ + Q_OBJECT +public: + XdgToplevelV6Integration(QWaylandQuickShellSurfaceItem *item); + bool mouseMoveEvent(QMouseEvent *event) override; + bool mouseReleaseEvent(QMouseEvent *event) override; + +private Q_SLOTS: + void handleStartMove(QWaylandSeat *seat); + void handleStartResize(QWaylandSeat *seat, Qt::Edges edges); + void handleSetMaximized(); + void handleUnsetMaximized(); + void handleMaximizedChanged(); + void handleActivatedChanged(); + void handleSurfaceSizeChanged(); + +private: + QWaylandQuickShellSurfaceItem *m_item; + QWaylandXdgSurfaceV6 *m_xdgSurface; + QWaylandXdgToplevelV6 *m_toplevel; + + enum class GrabberState { + Default, + Resize, + Move + }; + GrabberState grabberState; + + struct { + QWaylandSeat *seat; + QPointF initialOffset; + bool initialized; + } moveState; + + struct { + QWaylandSeat *seat; + Qt::Edges resizeEdges; + QSizeF initialWindowSize; + QPointF initialMousePos; + QPointF initialPosition; + QSize initialSurfaceSize; + bool initialized; + } resizeState; + + struct { + QSize initialWindowSize; + QPointF initialPosition; + } maximizeState; +}; + +class XdgPopupV6Integration : public QWaylandQuickShellIntegration +{ + Q_OBJECT +public: + XdgPopupV6Integration(QWaylandQuickShellSurfaceItem *item); + +private Q_SLOTS: + void handleGeometryChanged(); + +private: + QWaylandQuickShellSurfaceItem *m_item; + QWaylandXdgSurfaceV6 *m_xdgSurface; + QWaylandXdgPopupV6 *m_popup; +}; + +} + +QT_END_NAMESPACE + +#endif // QWAYLANDXDGSHELLV6INTEGRATION_H diff --git a/src/compositor/hardware_integration/qwlserverbufferintegration_p.h b/src/compositor/hardware_integration/qwlserverbufferintegration_p.h index 4754e751..c27fd4e4 100644 --- a/src/compositor/hardware_integration/qwlserverbufferintegration_p.h +++ b/src/compositor/hardware_integration/qwlserverbufferintegration_p.h @@ -64,6 +64,8 @@ QT_BEGIN_NAMESPACE class QWaylandCompositor; class QOpenGLContext; +class QOpenGLTexture; +class QImage; namespace QtWayland { class Display; @@ -81,7 +83,7 @@ public: virtual struct ::wl_resource *resourceForClient(struct ::wl_client *) = 0; - virtual void bindTextureToBuffer() = 0; + virtual QOpenGLTexture *toOpenGlTexture() = 0; virtual bool isYInverted() const; @@ -101,7 +103,7 @@ public: virtual void initializeHardware(QWaylandCompositor *); virtual bool supportsFormat(ServerBuffer::Format format) const = 0; - virtual ServerBuffer *createServerBuffer(const QSize &size, ServerBuffer::Format format) = 0; + virtual ServerBuffer *createServerBufferFromImage(const QImage &qimage, ServerBuffer::Format format) = 0; }; } diff --git a/src/extensions/shm-emulation-server-buffer.xml b/src/extensions/shm-emulation-server-buffer.xml new file mode 100644 index 00000000..7fb7069f --- /dev/null +++ b/src/extensions/shm-emulation-server-buffer.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="shm_emulation_server_buffer"> + <copyright> + Copyright (C) 2017 The Qt Company Ltd. + Contact: http://www.qt.io/licensing/ + + This file is part of the plugins of the Qt Toolkit. + + $QT_BEGIN_LICENSE:BSD$ + You may use this file under the terms of the BSD license as follows: + + "Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of The Qt Company Ltd nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + + $QT_END_LICENSE$ + </copyright> + <interface name="qt_shm_emulation_server_buffer" version="1"> + <description summary="shm-based server buffer for testing on desktop"> + This is software-based implementation of the qt_server_buffer extension. + It is intended for testing and debugging purposes only. + </description> + <enum name="format"> + <entry name="RGBA32" value="0"/> + <entry name="A8" value="1"/> + </enum> + <event name="server_buffer_created"> + <description summary="shm buffer information"> + Informs the client about a newly created server buffer. + The "key" argument contains a QSharedMemory key. + </description> + <arg name="id" type="new_id" interface="qt_server_buffer"/> + <arg name="key" type="string"/> + <arg name="width" type="int"/> + <arg name="height" type="int"/> + <arg name="bytes_per_line" type="int"/> + <arg name="format" type="int"/> + </event> + </interface> +</protocol> + diff --git a/src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.cpp b/src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.cpp index 5ded6ff0..fe2adbf5 100644 --- a/src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.cpp +++ b/src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.cpp @@ -41,6 +41,7 @@ #include <QtWaylandClient/private/qwaylanddisplay_p.h> #include <QDebug> #include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTexture> #include <EGL/egl.h> @@ -98,23 +99,35 @@ DrmServerBuffer::~DrmServerBuffer() m_integration->eglDestroyImageKHR(m_image); } -void DrmServerBuffer::bindTextureToBuffer() +QOpenGLTexture *DrmServerBuffer::toOpenGlTexture() { if (!QOpenGLContext::currentContext()) qWarning("DrmServerBuffer: creating texture with no current context"); - m_integration->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image); + if (!m_texture) { + m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + m_texture->create(); + } + m_texture->bind(); + m_integration->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_texture->release(); + m_texture->setSize(m_size.width(), m_size.height()); + return m_texture; } -void DrmEglServerBufferIntegration::initialize(QWaylandDisplay *display) +void DrmEglServerBufferIntegration::initializeEgl() { - m_egl_display = eglGetDisplay((EGLNativeDisplayType) display->wl_display()); - if (EGL_NO_DISPLAY) { + if (m_egl_initialized) + return; + m_egl_initialized = true; + + m_egl_display = eglGetDisplay((EGLNativeDisplayType) m_display->wl_display()); + if (m_egl_display == EGL_NO_DISPLAY) { qWarning("Failed to initialize drm egl server buffer integration. Could not get egl display from wl_display."); return; } @@ -136,8 +149,13 @@ void DrmEglServerBufferIntegration::initialize(QWaylandDisplay *display) qWarning("Failed to initialize drm egl server buffer integration. Could not resolve glEGLImageTargetTexture2DOES"); return; } + m_egl_initialized = true; +} - QtWayland::wl_registry::init(wl_display_get_registry(display->wl_display())); +void DrmEglServerBufferIntegration::initialize(QWaylandDisplay *display) +{ + m_display = display; + display->addRegistryListener(&wlDisplayHandleGlobal, this); } QWaylandServerBuffer *DrmEglServerBufferIntegration::serverBuffer(struct qt_server_buffer *buffer) @@ -145,12 +163,12 @@ QWaylandServerBuffer *DrmEglServerBufferIntegration::serverBuffer(struct qt_serv return static_cast<QWaylandServerBuffer *>(qt_server_buffer_get_user_data(buffer)); } -void DrmEglServerBufferIntegration::registry_global(uint32_t name, const QString &interface, uint32_t version) +void DrmEglServerBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) { Q_UNUSED(version); if (interface == QStringLiteral("qt_drm_egl_server_buffer")) { - struct ::wl_registry *registry = QtWayland::wl_registry::object(); - QtWayland::qt_drm_egl_server_buffer::init(registry, name, 1); + auto *integration = static_cast<DrmEglServerBufferIntegration *>(data); + integration->QtWayland::qt_drm_egl_server_buffer::init(registry, id, 1); } } diff --git a/src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.h b/src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.h index f848f190..f1b72299 100644 --- a/src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.h +++ b/src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.h @@ -71,15 +71,15 @@ class DrmServerBuffer : public QWaylandServerBuffer public: DrmServerBuffer(DrmEglServerBufferIntegration *integration, int32_t name, int32_t width, int32_t height, int32_t stride, int32_t format); ~DrmServerBuffer(); - void bindTextureToBuffer() override; + QOpenGLTexture* toOpenGlTexture() override; private: DrmEglServerBufferIntegration *m_integration; EGLImageKHR m_image; + QOpenGLTexture *m_texture = nullptr; }; class DrmEglServerBufferIntegration : public QWaylandServerBufferIntegration - , public QtWayland::wl_registry , public QtWayland::qt_drm_egl_server_buffer { public: @@ -91,17 +91,24 @@ public: inline EGLBoolean eglDestroyImageKHR (EGLImageKHR image); inline void glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image); protected: - void registry_global(uint32_t name, const QString &interface, uint32_t version) override; void drm_egl_server_buffer_server_buffer_created(struct ::qt_server_buffer *id, int32_t name, int32_t width, int32_t height, int32_t stride, int32_t format) override; private: + static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, + const QString &interface, uint32_t version); + void initializeEgl(); + PFNEGLCREATEIMAGEKHRPROC m_egl_create_image; PFNEGLDESTROYIMAGEKHRPROC m_egl_destroy_image; PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_gl_egl_image_target_texture; + QWaylandDisplay *m_display = nullptr; EGLDisplay m_egl_display; + bool m_egl_initialized = false; }; EGLImageKHR DrmEglServerBufferIntegration::eglCreateImageKHR(EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) { + if (!m_egl_initialized) + initializeEgl(); if (!m_egl_create_image) { qWarning("DrmEglServerBufferIntegration: Trying to used unresolved function eglCreateImageKHR"); return EGL_NO_IMAGE_KHR; diff --git a/src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.cpp b/src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.cpp index 21af476f..51b904e7 100644 --- a/src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.cpp +++ b/src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.cpp @@ -41,6 +41,7 @@ #include <QtWaylandClient/private/qwaylanddisplay_p.h> #include <QDebug> #include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTexture> #include <hybris/eglplatformcommon/hybris_nativebufferext.h> #include <EGL/egl.h> @@ -59,6 +60,7 @@ LibHybrisServerBuffer::LibHybrisServerBuffer(LibHybrisEglServerBufferIntegration , int32_t format) : QWaylandServerBuffer() , m_integration(integration) + , m_texture(nullptr) , m_stride(stride) , m_hybrisFormat(format) { @@ -76,18 +78,28 @@ LibHybrisServerBuffer::~LibHybrisServerBuffer() m_integration->eglDestroyImageKHR(m_image); } -void LibHybrisServerBuffer::bindTextureToBuffer() +QOpenGLTexture * LibHybrisServerBuffer::toOpenGlTexture() { if (!QOpenGLContext::currentContext()) { qWarning("LibHybrisServerBuffer: creating texture with no current context"); } + if (!m_texture) { + m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + m_texture->create(); + } + + m_texture->bind(); m_integration->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_texture->release(); + m_texture->setSize(m_size.width(), m_size.height()); + + return m_texture; } void LibHybrisServerBuffer::libhybris_buffer_add_fd(int32_t fd) @@ -114,10 +126,14 @@ void LibHybrisServerBuffer::libhybris_buffer_add_fd(int32_t fd) } } -void LibHybrisEglServerBufferIntegration::initialize(QWaylandDisplay *display) +void LibHybrisEglServerBufferIntegration::initializeEgl() { - m_egl_display = eglGetDisplay((EGLNativeDisplayType) display->wl_display()); - if (EGL_NO_DISPLAY) { + if (m_egl_initialized) + return; + m_egl_initialized = true; + + m_egl_display = eglGetDisplay((EGLNativeDisplayType) m_display->wl_display()); + if (m_egl_display == EGL_NO_DISPLAY) { qWarning("Failed to initialize libhybris egl server buffer integration. Could not get egl display from wl_display."); return; } @@ -145,8 +161,13 @@ void LibHybrisEglServerBufferIntegration::initialize(QWaylandDisplay *display) qWarning("Failed to initialize libhybris egl server buffer integration. Could not resolve eglHybrisCreateRemoteBuffer"); return; } + m_egl_initialized = true; +} - QtWayland::wl_registry::init(wl_display_get_registry(display->wl_display())); +void LibHybrisEglServerBufferIntegration::initialize(QWaylandDisplay *display) +{ + m_display = display; + display->addRegistryListener(&wlDisplayHandleGlobal, this); } QWaylandServerBuffer *LibHybrisEglServerBufferIntegration::serverBuffer(struct qt_server_buffer *buffer) @@ -154,12 +175,12 @@ QWaylandServerBuffer *LibHybrisEglServerBufferIntegration::serverBuffer(struct q return static_cast<QWaylandServerBuffer *>(qt_server_buffer_get_user_data(buffer)); } -void LibHybrisEglServerBufferIntegration::registry_global(uint32_t name, const QString &interface, uint32_t version) +void LibHybrisEglServerBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) { Q_UNUSED(version); if (interface == QStringLiteral("qt_libhybris_egl_server_buffer")) { - struct ::wl_registry *registry = QtWayland::wl_registry::object(); - QtWayland::qt_libhybris_egl_server_buffer::init(registry, name, 1); + auto *integration = static_cast<LibHybrisEglServerBufferIntegration *>(data); + integration->QtWayland::qt_libhybris_egl_server_buffer::init(registry, id, 1); } } diff --git a/src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.h b/src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.h index e9f0689a..b3c9c464 100644 --- a/src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.h +++ b/src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.h @@ -75,7 +75,7 @@ class LibHybrisServerBuffer : public QWaylandServerBuffer, public QtWayland::qt_ public: LibHybrisServerBuffer(LibHybrisEglServerBufferIntegration *integration, int32_t numFds, wl_array *ints, int32_t name, int32_t width, int32_t height, int32_t stride, int32_t format); ~LibHybrisServerBuffer(); - void bindTextureToBuffer() override; + QOpenGLTexture* toOpenGlTexture() override; protected: void libhybris_buffer_add_fd(int32_t fd) override; @@ -83,6 +83,7 @@ protected: private: LibHybrisEglServerBufferIntegration *m_integration; EGLImageKHR m_image; + QOpenGLTexture *m_texture; int m_numFds; QVector<int32_t> m_ints; QVector<int32_t> m_fds; @@ -92,7 +93,6 @@ private: class LibHybrisEglServerBufferIntegration : public QWaylandServerBufferIntegration - , public QtWayland::wl_registry , public QtWayland::qt_libhybris_egl_server_buffer { public: @@ -106,19 +106,27 @@ public: inline EGLBoolean eglHybrisCreateRemoteBuffer(EGLint width, EGLint height, EGLint usage, EGLint format, EGLint stride, int num_ints, int *ints, int num_fds, int *fds, EGLClientBuffer *buffer); protected: - void registry_global(uint32_t name, const QString &interface, uint32_t version) override; void libhybris_egl_server_buffer_server_buffer_created(struct ::qt_libhybris_buffer *id, struct ::qt_server_buffer *qid, int32_t numFds, wl_array *ints, int32_t name, int32_t width, int32_t height, int32_t stride, int32_t format) override; private: + static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, + const QString &interface, uint32_t version); + void initializeEgl(); + PFNEGLCREATEIMAGEKHRPROC m_egl_create_image; PFNEGLDESTROYIMAGEKHRPROC m_egl_destroy_image; PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_gl_egl_image_target_texture; PFNEGLHYBRISCREATEREMOTEBUFFERPROC m_egl_create_buffer; + QWaylandDisplay *m_display = nullptr; EGLDisplay m_egl_display; + bool m_egl_initialized = false; }; EGLImageKHR LibHybrisEglServerBufferIntegration::eglCreateImageKHR(EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) { + if (!m_egl_initialized) + initializeEgl(); + if (!m_egl_create_image) { qWarning("LibHybrisEglServerBufferIntegration: Trying to used unresolved function eglCreateImageKHR"); return EGL_NO_IMAGE_KHR; diff --git a/src/hardwareintegration/client/shm-emulation-server/shm-emulation-server.pri b/src/hardwareintegration/client/shm-emulation-server/shm-emulation-server.pri new file mode 100644 index 00000000..1a78290d --- /dev/null +++ b/src/hardwareintegration/client/shm-emulation-server/shm-emulation-server.pri @@ -0,0 +1,12 @@ +INCLUDEPATH += $$PWD + +QMAKE_USE += wayland-client + +SOURCES += \ + $$PWD/shmserverbufferintegration.cpp + +HEADERS += \ + $$PWD/shmserverbufferintegration.h + +CONFIG += wayland-scanner +WAYLANDCLIENTSOURCES += $$PWD/../../../extensions/shm-emulation-server-buffer.xml diff --git a/src/hardwareintegration/client/shm-emulation-server/shmserverbufferintegration.cpp b/src/hardwareintegration/client/shm-emulation-server/shmserverbufferintegration.cpp new file mode 100644 index 00000000..bc597ebf --- /dev/null +++ b/src/hardwareintegration/client/shm-emulation-server/shmserverbufferintegration.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shmserverbufferintegration.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QDebug> +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTexture> +#include <QtGui/QImage> +#include <QtCore/QSharedMemory> + +QT_BEGIN_NAMESPACE + +static QOpenGLTexture *createTextureFromShm(const QString &key, int w, int h, int bpl, int format) +{ + QSharedMemory shm(key); + bool ok; + ok = shm.attach(QSharedMemory::ReadOnly); + if (!ok) { + qWarning() << "Could not attach to" << key; + return nullptr; + } + ok = shm.lock(); + if (!ok) { + qWarning() << "Could not lock" << key << "for reading"; + return nullptr; + } + + QImage::Format imageFormat; + switch (format) { + case QtWayland::qt_shm_emulation_server_buffer::format_RGBA32: + imageFormat = QImage::Format_RGBA8888; + break; + case QtWayland::qt_shm_emulation_server_buffer::format_A8: + imageFormat = QImage::Format_Alpha8; + break; + default: + qWarning() << "ShmServerBuffer: unknown format" << format; + imageFormat = QImage::Format_RGBA8888; + break; + } + + QImage image(static_cast<const uchar*>(shm.constData()), w, h, bpl, imageFormat); + + if (!QOpenGLContext::currentContext()) + qWarning("ShmServerBuffer: creating texture with no current context"); + + auto *tex = new QOpenGLTexture(image, QOpenGLTexture::DontGenerateMipMaps); + shm.unlock(); + return tex; +} + + +namespace QtWaylandClient { +ShmServerBuffer::ShmServerBuffer(ShmServerBufferIntegration *integration, const QString &key, int32_t width, int32_t height, int32_t bytes_per_line, int32_t format) + : QWaylandServerBuffer() + , m_integration(integration) + , m_texture(nullptr) + , m_key(key) + , m_bpl(bytes_per_line) + , m_format(format) +{ + m_size = QSize(width, height); +} + +ShmServerBuffer::~ShmServerBuffer() +{ +} + +QOpenGLTexture *ShmServerBuffer::toOpenGlTexture() +{ + if (!m_texture) + m_texture = createTextureFromShm(m_key, m_size.width(), m_size.height(), m_bpl, m_format); + + return m_texture; +} + +void ShmServerBufferIntegration::initialize(QWaylandDisplay *display) +{ + m_display = display; + display->addRegistryListener(&wlDisplayHandleGlobal, this); +} + +QWaylandServerBuffer *ShmServerBufferIntegration::serverBuffer(struct qt_server_buffer *buffer) +{ + return static_cast<QWaylandServerBuffer *>(qt_server_buffer_get_user_data(buffer)); +} + +void ShmServerBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) +{ + Q_UNUSED(version); + if (interface == "qt_shm_emulation_server_buffer") { + auto *integration = static_cast<ShmServerBufferIntegration *>(data); + integration->QtWayland::qt_shm_emulation_server_buffer::init(registry, id, 1); + } +} + + +void QtWaylandClient::ShmServerBufferIntegration::shm_emulation_server_buffer_server_buffer_created(qt_server_buffer *id, const QString &key, int32_t width, int32_t height, int32_t bytes_per_line, int32_t format) +{ + auto *server_buffer = new ShmServerBuffer(this, key, width, height, bytes_per_line, format); + qt_server_buffer_set_user_data(id, server_buffer); +} + +} + +QT_END_NAMESPACE diff --git a/src/hardwareintegration/client/shm-emulation-server/shmserverbufferintegration.h b/src/hardwareintegration/client/shm-emulation-server/shmserverbufferintegration.h new file mode 100644 index 00000000..ce4032ba --- /dev/null +++ b/src/hardwareintegration/client/shm-emulation-server/shmserverbufferintegration.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHMSERVERBUFFERINTEGRATION_H +#define SHMSERVERBUFFERINTEGRATION_H + +#include <QtWaylandClient/private/qwayland-wayland.h> +#include "qwayland-shm-emulation-server-buffer.h" +#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h> + +#include "shmserverbufferintegration.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtCore/QTextStream> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class ShmServerBufferIntegration; + +class ShmServerBuffer : public QWaylandServerBuffer +{ +public: + ShmServerBuffer(ShmServerBufferIntegration *integration, const QString &key, int32_t width, int32_t height, int32_t bytes_per_line, int32_t format); + ~ShmServerBuffer(); + QOpenGLTexture* toOpenGlTexture() override; +private: + ShmServerBufferIntegration *m_integration; + QOpenGLTexture *m_texture; + QString m_key; + int m_bpl; + int m_format; +}; + +class ShmServerBufferIntegration + : public QWaylandServerBufferIntegration + , public QtWayland::qt_shm_emulation_server_buffer +{ +public: + void initialize(QWaylandDisplay *display) override; + + virtual QWaylandServerBuffer *serverBuffer(struct qt_server_buffer *buffer) override; + +protected: + void shm_emulation_server_buffer_server_buffer_created(qt_server_buffer *id, const QString &key, int32_t width, int32_t height, int32_t bytes_per_line, int32_t format) override; + +private: + static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, + const QString &interface, uint32_t version); + QWaylandDisplay *m_display = nullptr; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.cpp b/src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.cpp index fe7f12fd..35fdc827 100644 --- a/src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.cpp +++ b/src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.cpp @@ -40,12 +40,14 @@ #include "drmeglserverbufferintegration.h" #include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTexture> QT_BEGIN_NAMESPACE -DrmEglServerBuffer::DrmEglServerBuffer(DrmEglServerBufferIntegration *integration, const QSize &size, QtWayland::ServerBuffer::Format format) - : QtWayland::ServerBuffer(size,format) +DrmEglServerBuffer::DrmEglServerBuffer(DrmEglServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format) + : QtWayland::ServerBuffer(qimage.size(),format) , m_integration(integration) + , m_texture(nullptr) { m_format = format; @@ -82,38 +84,42 @@ DrmEglServerBuffer::DrmEglServerBuffer(DrmEglServerBufferIntegration *integratio qWarning("DrmEglServerBuffer: Failed to export egl image"); } + m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + m_texture->create(); + m_texture->bind(); + + m_integration->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image); + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, qimage.width(), qimage.height(), GL_RGBA, GL_UNSIGNED_BYTE, qimage.constBits()); + + m_texture->release(); + m_texture->setSize(m_size.width(), m_size.height()); } struct ::wl_resource *DrmEglServerBuffer::resourceForClient(struct ::wl_client *client) { - QMultiMap<struct ::wl_client *, Resource *>::iterator it = resourceMap().find(client); - if (it == resourceMap().end()) { - QMultiMap<struct ::wl_client *, QtWaylandServer::qt_drm_egl_server_buffer::Resource *>::iterator drm_egl_it = m_integration->resourceMap().find(client); - if (drm_egl_it == m_integration->resourceMap().end()) { + auto *bufferResource = resourceMap().value(client); + if (!bufferResource) { + auto integrationResource = m_integration->resourceMap().value(client); + if (!integrationResource) { qWarning("DrmEglServerBuffer::resourceForClient: Trying to get resource for ServerBuffer. But client is not bound to the drm_egl interface"); return 0; } - struct ::wl_resource *drm_egl_resource = (*drm_egl_it)->handle; - Resource *resource = add(client, 1, 1); - m_integration->send_server_buffer_created(drm_egl_resource, resource->handle, m_name, m_size.width(), m_size.height(), m_stride, m_drm_format); + struct ::wl_resource *drm_egl_integration_resource = integrationResource->handle; + Resource *resource = add(client, 1); + m_integration->send_server_buffer_created(drm_egl_integration_resource, resource->handle, m_name, m_size.width(), m_size.height(), m_stride, m_drm_format); return resource->handle; } - return (*it)->handle; + return bufferResource->handle; } -void DrmEglServerBuffer::bindTextureToBuffer() + +QOpenGLTexture *DrmEglServerBuffer::toOpenGlTexture() { - if (!QOpenGLContext::currentContext()) { - qWarning("DrmEglServerBuffer: No current context when creating buffer. Texture loading will fail"); - return; + if (!m_texture) { + qWarning("DrmEglServerBuffer::toOpenGlTexture: no texture defined"); } - - m_integration->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + return m_texture; } DrmEglServerBufferIntegration::DrmEglServerBufferIntegration() @@ -130,7 +136,7 @@ void DrmEglServerBufferIntegration::initializeHardware(QWaylandCompositor *compo m_egl_display = static_cast<EGLDisplay>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("egldisplay")); if (!m_egl_display) { - qWarning("Cant initialize drm egl server buffer integration. Missing egl display from platformplugin"); + qWarning("Can't initialize drm egl server buffer integration. Missing egl display from platform plugin"); return; } @@ -183,9 +189,9 @@ bool DrmEglServerBufferIntegration::supportsFormat(QtWayland::ServerBuffer::Form } } -QtWayland::ServerBuffer *DrmEglServerBufferIntegration::createServerBuffer(const QSize &size, QtWayland::ServerBuffer::Format format) +QtWayland::ServerBuffer *DrmEglServerBufferIntegration::createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format) { - return new DrmEglServerBuffer(this, size, format); + return new DrmEglServerBuffer(this, qimage, format); } QT_END_NAMESPACE diff --git a/src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.h b/src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.h index 239a1f7d..e1cc8768 100644 --- a/src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.h +++ b/src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.h @@ -72,14 +72,15 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLEXPORTDRMIMAGEMESAPROC) (EGLDisplay dpy, QT_BEGIN_NAMESPACE class DrmEglServerBufferIntegration; +class QImage; class DrmEglServerBuffer : public QtWayland::ServerBuffer, public QtWaylandServer::qt_server_buffer { public: - DrmEglServerBuffer(DrmEglServerBufferIntegration *integration, const QSize &size, QtWayland::ServerBuffer::Format format); + DrmEglServerBuffer(DrmEglServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format); struct ::wl_resource *resourceForClient(struct ::wl_client *) override; - void bindTextureToBuffer() override; + QOpenGLTexture *toOpenGlTexture() override; private: DrmEglServerBufferIntegration *m_integration; @@ -88,6 +89,7 @@ private: int32_t m_name; int32_t m_stride; + QOpenGLTexture *m_texture; QtWaylandServer::qt_drm_egl_server_buffer::format m_drm_format; }; @@ -102,7 +104,7 @@ public: void initializeHardware(QWaylandCompositor *) override; bool supportsFormat(QtWayland::ServerBuffer::Format format) const override; - QtWayland::ServerBuffer *createServerBuffer(const QSize &size, QtWayland::ServerBuffer::Format format) override; + QtWayland::ServerBuffer *createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format) override; EGLDisplay display() const { return m_egl_display; } diff --git a/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp b/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp index 2312a468..de9e1cf6 100644 --- a/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp +++ b/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp @@ -40,13 +40,15 @@ #include "libhybriseglserverbufferintegration.h" #include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTexture> #include <hybris/eglplatformcommon/hybris_nativebufferext.h> #include <wayland-server.h> QT_BEGIN_NAMESPACE -LibHybrisEglServerBuffer::LibHybrisEglServerBuffer(LibHybrisEglServerBufferIntegration *integration, const QSize &size, QtWayland::ServerBuffer::Format format) - : QtWayland::ServerBuffer(size,format) +LibHybrisEglServerBuffer::LibHybrisEglServerBuffer(LibHybrisEglServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format) + : QtWayland::ServerBuffer(qimage.size(),format) , m_integration(integration) + , m_texture(nullptr) { m_format = format; @@ -63,7 +65,7 @@ LibHybrisEglServerBuffer::LibHybrisEglServerBuffer(LibHybrisEglServerBufferInteg break; } - if (!m_integration->eglHybrisCreateNativeBuffer(size.width(), size.height(), HYBRIS_USAGE_HW_TEXTURE, egl_format, &m_stride, &m_buffer)) { + if (!m_integration->eglHybrisCreateNativeBuffer(m_size.width(), m_size.height(), HYBRIS_USAGE_HW_TEXTURE, egl_format, &m_stride, &m_buffer)) { qWarning("LibHybrisEglServerBuffer: Failed to create egl buffer"); return; } @@ -76,48 +78,53 @@ LibHybrisEglServerBuffer::LibHybrisEglServerBuffer(LibHybrisEglServerBufferInteg m_integration->eglHybrisSerializeNativeBuffer(m_buffer, m_ints.data(), m_fds.data()); m_image = m_integration->eglCreateImageKHR(EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, m_buffer, 0); + + if (!QOpenGLContext::currentContext()) { + qWarning("LibHybrisEglServerBuffer: No current context when creating buffer. Texture loading will fail"); + return; + } + + m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + m_texture->create(); + + m_texture->bind(); + + m_integration->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image); + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, qimage.width(), qimage.height(), GL_RGBA, GL_UNSIGNED_BYTE, qimage.constBits()); + + m_texture->release(); + m_texture->setSize(m_size.width(), m_size.height()); } struct ::wl_resource *LibHybrisEglServerBuffer::resourceForClient(struct ::wl_client *client) { - QMultiMap<struct ::wl_client *, Resource *>::iterator it = resourceMap().find(client); - if (it == resourceMap().end()) { - QMultiMap<struct ::wl_client *, QtWaylandServer::qt_libhybris_egl_server_buffer::Resource *>::iterator egl_it = m_integration->resourceMap().find(client); - if (egl_it == m_integration->resourceMap().end()) { + auto *bufferResource = resourceMap().value(client); + if (!bufferResource) { + auto integrationResource = m_integration->resourceMap().value(client); + if (!integrationResource) { qWarning("LibHybrisEglServerBuffer::resourceForClient: Trying to get resource for ServerBuffer. But client is not bound to the libhybris_egl interface"); return 0; } - struct ::wl_resource *egl_resource = (*egl_it)->handle; - Resource *resource = add(client, 1, 1); - wl_resource *bufRes = wl_client_new_object(client, &qt_libhybris_buffer_interface, 0, 0); + struct ::wl_resource *egl_integration_resource = integrationResource->handle; + Resource *resource = add(client, 1); + wl_resource *bufRes = wl_resource_create(client, &qt_libhybris_buffer_interface,-1, 0); - m_integration->send_server_buffer_created(egl_resource, resource->handle, bufRes, m_fds.size(), QByteArray((char *)m_ints.data(), m_ints.size() * sizeof(int32_t)), + m_integration->send_server_buffer_created(egl_integration_resource, resource->handle, bufRes, m_fds.size(), QByteArray((char *)m_ints.data(), m_ints.size() * sizeof(int32_t)), m_name, m_size.width(), m_size.height(), m_stride, m_format); - m_qtbuffers.insert(resource, bufRes); - for (int i = 0; i < m_fds.size(); ++i) { send_add_fd(resource->handle, m_fds.at(i)); } - return bufRes; + return resource->handle; } - return m_qtbuffers.value(*it); + return bufferResource->handle; } -void LibHybrisEglServerBuffer::bindTextureToBuffer() +QOpenGLTexture *LibHybrisEglServerBuffer::toOpenGlTexture() { - if (!QOpenGLContext::currentContext()) { - qWarning("LibHybrisEglServerBuffer: No current context when creating buffer. Texture loading will fail"); - return; - } - - m_integration->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + return m_texture; } LibHybrisEglServerBufferIntegration::LibHybrisEglServerBufferIntegration() @@ -134,7 +141,7 @@ void LibHybrisEglServerBufferIntegration::initializeHardware(QWaylandCompositor m_egl_display = static_cast<EGLDisplay>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("egldisplay")); if (!m_egl_display) { - qWarning("Cant initialize libhybris egl server buffer integration. Missing egl display from platformplugin"); + qWarning("Can't initialize libhybris egl server buffer integration. Missing egl display from platform plugin"); return; } @@ -187,9 +194,9 @@ bool LibHybrisEglServerBufferIntegration::supportsFormat(QtWayland::ServerBuffer } } -QtWayland::ServerBuffer *LibHybrisEglServerBufferIntegration::createServerBuffer(const QSize &size, QtWayland::ServerBuffer::Format format) +QtWayland::ServerBuffer *LibHybrisEglServerBufferIntegration::createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format) { - return new LibHybrisEglServerBuffer(this, size, format); + return new LibHybrisEglServerBuffer(this, qimage, format); } QT_END_NAMESPACE diff --git a/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.h b/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.h index e874e416..0e159344 100644 --- a/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.h +++ b/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.h @@ -78,10 +78,10 @@ class LibHybrisEglServerBufferIntegration; class LibHybrisEglServerBuffer : public QtWayland::ServerBuffer, public QtWaylandServer::qt_libhybris_buffer { public: - LibHybrisEglServerBuffer(LibHybrisEglServerBufferIntegration *integration, const QSize &size, QtWayland::ServerBuffer::Format format); + LibHybrisEglServerBuffer(LibHybrisEglServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format); struct ::wl_resource *resourceForClient(struct ::wl_client *) override; - void bindTextureToBuffer() override; + QOpenGLTexture *toOpenGlTexture() override; private: LibHybrisEglServerBufferIntegration *m_integration; @@ -91,10 +91,10 @@ private: int32_t m_name; int32_t m_stride; + QOpenGLTexture *m_texture; QtWaylandServer::qt_libhybris_egl_server_buffer::format m_hybris_format; QVector<int32_t> m_ints; QVector<int32_t> m_fds; - QHash<Resource *, wl_resource *> m_qtbuffers; }; class LibHybrisEglServerBufferIntegration : @@ -108,7 +108,7 @@ public: void initializeHardware(QWaylandCompositor *); bool supportsFormat(QtWayland::ServerBuffer::Format format) const override; - QtWayland::ServerBuffer *createServerBuffer(const QSize &size, QtWayland::ServerBuffer::Format format) override; + QtWayland::ServerBuffer *createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format) override; EGLDisplay display() const { return m_egl_display; } diff --git a/src/hardwareintegration/compositor/shm-emulation-server/shm-emulation-server.pri b/src/hardwareintegration/compositor/shm-emulation-server/shm-emulation-server.pri new file mode 100644 index 00000000..4d12024c --- /dev/null +++ b/src/hardwareintegration/compositor/shm-emulation-server/shm-emulation-server.pri @@ -0,0 +1,13 @@ +INCLUDEPATH += $$PWD + +QMAKE_USE_PRIVATE += wayland-server + +SOURCES += \ + $$PWD/shmserverbufferintegration.cpp + + +HEADERS += \ + $$PWD/shmserverbufferintegration.h + +CONFIG += wayland-scanner +WAYLANDSERVERSOURCES += $$PWD/../../../extensions/shm-emulation-server-buffer.xml diff --git a/src/hardwareintegration/compositor/shm-emulation-server/shmserverbufferintegration.cpp b/src/hardwareintegration/compositor/shm-emulation-server/shmserverbufferintegration.cpp new file mode 100644 index 00000000..7a86785b --- /dev/null +++ b/src/hardwareintegration/compositor/shm-emulation-server/shmserverbufferintegration.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shmserverbufferintegration.h" + +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTexture> +#include <QtCore/QSharedMemory> + +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +ShmServerBuffer::ShmServerBuffer(ShmServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format) + : QtWayland::ServerBuffer(qimage.size(),format) + , m_integration(integration) + , m_width(qimage.width()) + , m_height(qimage.height()) + , m_bpl(qimage.bytesPerLine()) +{ + m_format = format; + switch (m_format) { + case RGBA32: + m_shm_format = QtWaylandServer::qt_shm_emulation_server_buffer::format_RGBA32; + break; + case A8: + m_shm_format = QtWaylandServer::qt_shm_emulation_server_buffer::format_A8; + break; + default: + qWarning("ShmServerBuffer: unsupported format"); + m_shm_format = QtWaylandServer::qt_shm_emulation_server_buffer::format_RGBA32; + break; + } + + QString key = "qt_shm_emulation_" + QString::number(qimage.cacheKey()); + m_shm = new QSharedMemory(key); + int shm_size = qimage.byteCount(); + bool ok = m_shm->create(shm_size) && m_shm->lock(); + if (ok) { + memcpy(m_shm->data(), qimage.constBits(), shm_size); + m_shm->unlock(); + } else { + qWarning() << "Could not create shared memory" << key << shm_size; + } +} + +ShmServerBuffer::~ShmServerBuffer() +{ + delete m_shm; +} + +struct ::wl_resource *ShmServerBuffer::resourceForClient(struct ::wl_client *client) +{ + auto *bufferResource = resourceMap().value(client); + if (!bufferResource) { + auto integrationResource = m_integration->resourceMap().value(client); + if (!integrationResource) { + qWarning("ShmServerBuffer::resourceForClient: Trying to get resource for ServerBuffer. But client is not bound to the shm_emulation interface"); + return nullptr; + } + struct ::wl_resource *shm_integration_resource = integrationResource->handle; + Resource *resource = add(client, 1); + m_integration->send_server_buffer_created(shm_integration_resource, resource->handle, m_shm->key(), m_width, m_height, m_bpl, m_shm_format); + return resource->handle; + } + return bufferResource->handle; +} + + +QOpenGLTexture *ShmServerBuffer::toOpenGlTexture() +{ + if (!m_texture) { + qWarning("ShmServerBuffer::toOpenGlTexture: no texture defined"); + } + return m_texture; +} + +ShmServerBufferIntegration::ShmServerBufferIntegration() +{ +} + +ShmServerBufferIntegration::~ShmServerBufferIntegration() +{ +} + +void ShmServerBufferIntegration::initializeHardware(QWaylandCompositor *compositor) +{ + Q_ASSERT(QGuiApplication::platformNativeInterface()); + + QtWaylandServer::qt_shm_emulation_server_buffer::init(compositor->display(), 1); +} + +bool ShmServerBufferIntegration::supportsFormat(QtWayland::ServerBuffer::Format format) const +{ + switch (format) { + case QtWayland::ServerBuffer::RGBA32: + return true; + case QtWayland::ServerBuffer::A8: + return true; + default: + return false; + } +} + +QtWayland::ServerBuffer *ShmServerBufferIntegration::createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format) +{ + return new ShmServerBuffer(this, qimage, format); +} + +QT_END_NAMESPACE diff --git a/src/hardwareintegration/compositor/shm-emulation-server/shmserverbufferintegration.h b/src/hardwareintegration/compositor/shm-emulation-server/shmserverbufferintegration.h new file mode 100644 index 00000000..c303e5f1 --- /dev/null +++ b/src/hardwareintegration/compositor/shm-emulation-server/shmserverbufferintegration.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHMSERVERBUFFERINTEGRATION_H +#define SHMSERVERBUFFERINTEGRATION_H + +#include <QtWaylandCompositor/private/qwlserverbufferintegration_p.h> + +#include "qwayland-server-shm-emulation-server-buffer.h" + +#include <QtGui/QImage> +#include <QtGui/QWindow> +#include <QtGui/qpa/qplatformnativeinterface.h> +#include <QtGui/QGuiApplication> + +#include <QtWaylandCompositor/qwaylandcompositor.h> +#include <QtWaylandCompositor/private/qwayland-server-server-buffer-extension.h> + +QT_BEGIN_NAMESPACE + +class ShmServerBufferIntegration; +class QSharedMemory; + +class ShmServerBuffer : public QtWayland::ServerBuffer, public QtWaylandServer::qt_server_buffer +{ +public: + ShmServerBuffer(ShmServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format); + ~ShmServerBuffer(); + + struct ::wl_resource *resourceForClient(struct ::wl_client *) override; + QOpenGLTexture *toOpenGlTexture() override; + +private: + ShmServerBufferIntegration *m_integration; + + QSharedMemory *m_shm; + int m_width; + int m_height; + int m_bpl; + QOpenGLTexture *m_texture = nullptr; + QtWaylandServer::qt_shm_emulation_server_buffer::format m_shm_format; +}; + +class ShmServerBufferIntegration : + public QtWayland::ServerBufferIntegration, + public QtWaylandServer::qt_shm_emulation_server_buffer +{ +public: + ShmServerBufferIntegration(); + ~ShmServerBufferIntegration(); + + void initializeHardware(QWaylandCompositor *) override; + + bool supportsFormat(QtWayland::ServerBuffer::Format format) const override; + QtWayland::ServerBuffer *createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format) override; + + +private: +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/imports/compositor/qwaylandquickcompositorplugin.cpp b/src/imports/compositor/qwaylandquickcompositorplugin.cpp index 832ba04d..0d6dbf78 100644 --- a/src/imports/compositor/qwaylandquickcompositorplugin.cpp +++ b/src/imports/compositor/qwaylandquickcompositorplugin.cpp @@ -61,6 +61,7 @@ #include <QtWaylandCompositor/QWaylandWlShell> #include <QtWaylandCompositor/QWaylandTextInputManager> #include <QtWaylandCompositor/QWaylandXdgShellV5> +#include <QtWaylandCompositor/QWaylandXdgShellV6> #include <QtWaylandCompositor/QWaylandIviApplication> #include <QtWaylandCompositor/QWaylandIviSurface> @@ -74,6 +75,7 @@ Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandQtWindowManager) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandIviApplication) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandWlShell) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandXdgShellV5) +Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandXdgShellV6) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandTextInputManager) class QmlUrlResolver @@ -153,6 +155,11 @@ public: qmlRegisterType<QWaylandXdgSurfaceV5>(uri, 1, 0, "XdgSurfaceV5"); qmlRegisterType<QWaylandXdgPopupV5>(uri, 1, 0, "XdgPopupV5"); qmlRegisterType<QWaylandTextInputManagerQuickExtension>(uri, 1, 0, "TextInputManager"); + + qmlRegisterType<QWaylandXdgShellV6QuickExtension>(uri, 1, 1, "XdgShellV6"); + qmlRegisterType<QWaylandXdgSurfaceV6>(uri, 1, 1, "XdgSurfaceV6"); + qmlRegisterUncreatableType<QWaylandXdgToplevelV6>(uri, 1, 1, "XdgToplevelV6", QObject::tr("Cannot create instance of XdgShellToplevelV6")); + qmlRegisterUncreatableType<QWaylandXdgPopupV6>(uri, 1, 1, "XdgPopupV6", QObject::tr("Cannot create instance of XdgShellPopupV6")); } }; //![class decl] diff --git a/src/plugins/hardwareintegration/client/client.pro b/src/plugins/hardwareintegration/client/client.pro index a5967c71..f1c71f08 100644 --- a/src/plugins/hardwareintegration/client/client.pro +++ b/src/plugins/hardwareintegration/client/client.pro @@ -13,3 +13,6 @@ qtConfig(drm-egl-server): \ SUBDIRS += drm-egl-server qtConfig(libhybris-egl-server): \ SUBDIRS += libhybris-egl-server + +### TODO: make shm-emulation configurable +SUBDIRS += shm-emulation-server diff --git a/src/plugins/hardwareintegration/client/shm-emulation-server/main.cpp b/src/plugins/hardwareintegration/client/shm-emulation-server/main.cpp new file mode 100644 index 00000000..8c3ba842 --- /dev/null +++ b/src/plugins/hardwareintegration/client/shm-emulation-server/main.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtWaylandClient/private/qwaylandserverbufferintegrationplugin_p.h> +#include "shmserverbufferintegration.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class ShmServerBufferPlugin : public QWaylandServerBufferIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QWaylandServerBufferIntegrationFactoryInterface_iid FILE "shm-emulation-server.json") +public: + QWaylandServerBufferIntegration *create(const QString&, const QStringList&) override; +}; + +QWaylandServerBufferIntegration *ShmServerBufferPlugin::create(const QString& system, const QStringList& paramList) +{ + Q_UNUSED(paramList); + Q_UNUSED(system); + return new ShmServerBufferIntegration(); +} + +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/hardwareintegration/client/shm-emulation-server/shm-emulation-server.json b/src/plugins/hardwareintegration/client/shm-emulation-server/shm-emulation-server.json new file mode 100644 index 00000000..1bf8ded8 --- /dev/null +++ b/src/plugins/hardwareintegration/client/shm-emulation-server/shm-emulation-server.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "shm-emulation-server" ] +} diff --git a/src/plugins/hardwareintegration/client/shm-emulation-server/shm-emulation-server.pro b/src/plugins/hardwareintegration/client/shm-emulation-server/shm-emulation-server.pro new file mode 100644 index 00000000..8b1c6e5f --- /dev/null +++ b/src/plugins/hardwareintegration/client/shm-emulation-server/shm-emulation-server.pro @@ -0,0 +1,15 @@ +# We have a bunch of C code with casts, so we can't have this option +QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual + +QT += waylandclient-private + +include(../../../../hardwareintegration/client/shm-emulation-server/shm-emulation-server.pri) + +OTHER_FILES += \ + shm-emulation-server.json + +SOURCES += main.cpp + +PLUGIN_TYPE = wayland-graphics-integration-client +PLUGIN_CLASS_NAME = ShmServerBufferPlugin +load(qt_plugin) diff --git a/src/plugins/hardwareintegration/compositor/compositor.pro b/src/plugins/hardwareintegration/compositor/compositor.pro index 29911625..0b2a4292 100644 --- a/src/plugins/hardwareintegration/compositor/compositor.pro +++ b/src/plugins/hardwareintegration/compositor/compositor.pro @@ -13,3 +13,6 @@ qtConfig(drm-egl-server): \ SUBDIRS += drm-egl-server qtConfig(libhybris-egl-server): \ SUBDIRS += libhybris-egl-server + +### TODO: make shm-emulation configurable +SUBDIRS += shm-emulation-server diff --git a/src/plugins/hardwareintegration/compositor/shm-emulation-server/main.cpp b/src/plugins/hardwareintegration/compositor/shm-emulation-server/main.cpp new file mode 100644 index 00000000..9496361e --- /dev/null +++ b/src/plugins/hardwareintegration/compositor/shm-emulation-server/main.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtWaylandCompositor/private/qwlserverbufferintegrationplugin_p.h> +#include "shmserverbufferintegration.h" + +QT_BEGIN_NAMESPACE + +class ShmServerBufferIntegrationPlugin : public QtWayland::ServerBufferIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QtWaylandServerBufferIntegrationFactoryInterface_iid FILE "shm-emulation-server.json") +public: + QtWayland::ServerBufferIntegration *create(const QString&, const QStringList&) override; +}; + +QtWayland::ServerBufferIntegration *ShmServerBufferIntegrationPlugin::create(const QString& system, const QStringList& paramList) +{ + Q_UNUSED(paramList); + Q_UNUSED(system); + return new ShmServerBufferIntegration(); +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/hardwareintegration/compositor/shm-emulation-server/shm-emulation-server.json b/src/plugins/hardwareintegration/compositor/shm-emulation-server/shm-emulation-server.json new file mode 100644 index 00000000..1bf8ded8 --- /dev/null +++ b/src/plugins/hardwareintegration/compositor/shm-emulation-server/shm-emulation-server.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "shm-emulation-server" ] +} diff --git a/src/plugins/hardwareintegration/compositor/shm-emulation-server/shm-emulation-server.pro b/src/plugins/hardwareintegration/compositor/shm-emulation-server/shm-emulation-server.pro new file mode 100644 index 00000000..56a1bac2 --- /dev/null +++ b/src/plugins/hardwareintegration/compositor/shm-emulation-server/shm-emulation-server.pro @@ -0,0 +1,12 @@ +QT = waylandcompositor waylandcompositor-private core-private gui-private + +OTHER_FILES += shm-emulation-server.json + +SOURCES += \ + main.cpp + +include($PWD/../../../../../hardwareintegration/compositor/shm-emulation-server/shm-emulation-server.pri) + +PLUGIN_TYPE = wayland-graphics-integration-server +PLUGIN_CLASS_NAME = ShmServerBufferIntegrationPlugin +load(qt_plugin) diff --git a/src/qtwaylandscanner/qtwaylandscanner.cpp b/src/qtwaylandscanner/qtwaylandscanner.cpp index 01dfa921..ed36a138 100644 --- a/src/qtwaylandscanner/qtwaylandscanner.cpp +++ b/src/qtwaylandscanner/qtwaylandscanner.cpp @@ -432,7 +432,7 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr printf(" class Resource\n"); printf(" {\n"); printf(" public:\n"); - printf(" Resource() : %s_object(0), handle(0) {}\n", interfaceNameStripped); + printf(" Resource() : %s_object(nullptr), handle(nullptr) {}\n", interfaceNameStripped); printf(" virtual ~Resource() {}\n"); printf("\n"); printf(" %s *%s_object;\n", interfaceName, interfaceNameStripped); @@ -458,8 +458,8 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr printf(" QMultiMap<struct ::wl_client*, Resource*> resourceMap() { return m_resource_map; }\n"); printf(" const QMultiMap<struct ::wl_client*, Resource*> resourceMap() const { return m_resource_map; }\n"); printf("\n"); - printf(" bool isGlobal() const { return m_global != 0; }\n"); - printf(" bool isResource() const { return m_resource != 0; }\n"); + printf(" bool isGlobal() const { return m_global != nullptr; }\n"); + printf(" bool isResource() const { return m_resource != nullptr; }\n"); printf("\n"); printf(" static const struct ::wl_interface *interface();\n"); printf(" static QByteArray interfaceName() { return interface()->name; }\n"); @@ -572,8 +572,8 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr printf(" %s::%s(struct ::wl_client *client, int id, int version)\n", interfaceName, interfaceName); printf(" : m_resource_map()\n"); - printf(" , m_resource(0)\n"); - printf(" , m_global(0)\n"); + printf(" , m_resource(nullptr)\n"); + printf(" , m_global(nullptr)\n"); printf(" {\n"); printf(" init(client, id, version);\n"); printf(" }\n"); @@ -581,8 +581,8 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr printf(" %s::%s(struct ::wl_display *display, int version)\n", interfaceName, interfaceName); printf(" : m_resource_map()\n"); - printf(" , m_resource(0)\n"); - printf(" , m_global(0)\n"); + printf(" , m_resource(nullptr)\n"); + printf(" , m_global(nullptr)\n"); printf(" {\n"); printf(" init(display, version);\n"); printf(" }\n"); @@ -590,8 +590,8 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr printf(" %s::%s(struct ::wl_resource *resource)\n", interfaceName, interfaceName); printf(" : m_resource_map()\n"); - printf(" , m_resource(0)\n"); - printf(" , m_global(0)\n"); + printf(" , m_resource(nullptr)\n"); + printf(" , m_global(nullptr)\n"); printf(" {\n"); printf(" init(resource);\n"); printf(" }\n"); @@ -599,8 +599,8 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr printf(" %s::%s()\n", interfaceName, interfaceName); printf(" : m_resource_map()\n"); - printf(" , m_resource(0)\n"); - printf(" , m_global(0)\n"); + printf(" , m_resource(nullptr)\n"); + printf(" , m_global(nullptr)\n"); printf(" {\n"); printf(" }\n"); printf("\n"); @@ -689,7 +689,7 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr bool hasRequests = !interface.requests.isEmpty(); - QByteArray interfaceMember = hasRequests ? "&m_" + interface.name + "_interface" : QByteArray("0"); + QByteArray interfaceMember = hasRequests ? "&m_" + interface.name + "_interface" : QByteArray("nullptr"); //We should consider changing bind so that it doesn't special case id == 0 //and use function overloading instead. Jan do you have a lot of code dependent on this behavior? @@ -717,7 +717,7 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr printf(" {\n"); printf(" if (wl_resource_instance_of(resource, &::%s_interface, %s))\n", interfaceName, interfaceMember.constData()); printf(" return static_cast<Resource *>(resource->data);\n"); - printf(" return 0;\n"); + printf(" return nullptr;\n"); printf(" }\n"); if (hasRequests) { @@ -993,7 +993,7 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr printf("\n"); printf(" %s::%s()\n", interfaceName, interfaceName); - printf(" : m_%s(0)\n", interfaceName); + printf(" : m_%s(nullptr)\n", interfaceName); printf(" {\n"); printf(" }\n"); printf("\n"); @@ -1021,7 +1021,7 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr printf(" bool %s::isInitialized() const\n", interfaceName); printf(" {\n"); - printf(" return m_%s != 0;\n", interfaceName); + printf(" return m_%s != nullptr;\n", interfaceName); printf(" }\n"); printf("\n"); @@ -1087,7 +1087,7 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr } printf(");\n"); if (e.type == "destructor") - printf(" m_%s = 0;\n", interfaceName); + printf(" m_%s = nullptr;\n", interfaceName); printf(" }\n"); } diff --git a/tests/auto/client/client/mocksurface.h b/tests/auto/client/client/mocksurface.h index e34d6596..5155599b 100644 --- a/tests/auto/client/client/mocksurface.h +++ b/tests/auto/client/client/mocksurface.h @@ -26,6 +26,9 @@ ** ****************************************************************************/ +#ifndef MOCKSURFACE_H +#define MOCKSURFACE_H + #include <qglobal.h> #include "qwayland-server-wayland.h" @@ -69,3 +72,5 @@ private: }; } + +#endif // MOCKSURFACE_H diff --git a/tests/auto/compositor/compositor/tst_compositor.cpp b/tests/auto/compositor/compositor/tst_compositor.cpp index 6f00b6e6..c51c13bd 100644 --- a/tests/auto/compositor/compositor/tst_compositor.cpp +++ b/tests/auto/compositor/compositor/tst_compositor.cpp @@ -39,6 +39,7 @@ #include <QtGui/QScreen> #include <QtWaylandCompositor/QWaylandXdgShellV5> +#include <QtWaylandCompositor/private/qwaylandxdgshellv6_p.h> #include <QtWaylandCompositor/QWaylandIviApplication> #include <QtWaylandCompositor/QWaylandIviSurface> #include <QtWaylandCompositor/QWaylandSurface> @@ -78,6 +79,9 @@ private slots: void emitsErrorOnSameIviId(); void sendsIviConfigure(); void destroysIviSurfaces(); + + void convertsXdgEdgesToQtEdges(); + void xdgShellV6Positioner(); }; void tst_WaylandCompositor::init() { @@ -835,5 +839,49 @@ void tst_WaylandCompositor::destroysIviSurfaces() QTRY_VERIFY(destroySpy.count() == 1); } +void tst_WaylandCompositor::convertsXdgEdgesToQtEdges() +{ + const uint wlLeft = ZXDG_POSITIONER_V6_ANCHOR_LEFT; + QCOMPARE(QWaylandXdgShellV6Private::convertToEdges(wlLeft), Qt::LeftEdge); + + const uint wlRight = ZXDG_POSITIONER_V6_ANCHOR_RIGHT; + QCOMPARE(QWaylandXdgShellV6Private::convertToEdges(wlRight), Qt::RightEdge); + + const uint wlTop = ZXDG_POSITIONER_V6_ANCHOR_TOP; + QCOMPARE(QWaylandXdgShellV6Private::convertToEdges(wlTop), Qt::TopEdge); + + const uint wlBottom = ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; + QCOMPARE(QWaylandXdgShellV6Private::convertToEdges(wlBottom), Qt::BottomEdge); + + QCOMPARE(QWaylandXdgShellV6Private::convertToEdges(wlBottom | wlLeft), Qt::Edges(Qt::BottomEdge | Qt::LeftEdge)); + QCOMPARE(QWaylandXdgShellV6Private::convertToEdges(wlTop | wlRight), Qt::Edges(Qt::TopEdge | Qt::RightEdge)); +} + +void tst_WaylandCompositor::xdgShellV6Positioner() +{ + QWaylandXdgPositionerV6Data p; + QVERIFY(!p.isComplete()); + + p.size = QSize(100, 50); + p.anchorRect = QRect(QPoint(1, 2), QSize(800, 600)); + QVERIFY(p.isComplete()); + + p.anchorEdges = Qt::TopEdge | Qt::LeftEdge; + p.gravityEdges = Qt::BottomEdge | Qt::RightEdge; + QCOMPARE(p.unconstrainedPosition(), QPoint(1, 2)); + + p.anchorEdges = Qt::RightEdge; + QCOMPARE(p.unconstrainedPosition(), QPoint(1 + 800, 2 + 600 / 2)); + + p.gravityEdges = Qt::BottomEdge; + QCOMPARE(p.unconstrainedPosition(), QPoint(1 + 800 - 100 / 2, 2 + 600 / 2)); + + p.gravityEdges = Qt::TopEdge; + QCOMPARE(p.unconstrainedPosition(), QPoint(1 + 800 - 100 / 2, 2 + 600 / 2 - 50)); + + p.offset = QPoint(4, 8); + QCOMPARE(p.unconstrainedPosition(), QPoint(1 + 800 - 100 / 2 + 4, 2 + 600 / 2 - 50 + 8)); +} + #include <tst_compositor.moc> QTEST_MAIN(tst_WaylandCompositor); |