summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--config.tests/vulkan_server_buffer/main.cpp60
-rw-r--r--config.tests/vulkan_server_buffer/vulkan_server_buffer.pro1
-rw-r--r--examples/wayland/server-buffer/README5
-rw-r--r--examples/wayland/texture-sharing/.gitignore2
-rw-r--r--examples/wayland/texture-sharing/README27
-rw-r--r--examples/wayland/texture-sharing/custom-compositor/compositor.qrc9
-rw-r--r--examples/wayland/texture-sharing/custom-compositor/custom-compositor.pro18
-rw-r--r--examples/wayland/texture-sharing/custom-compositor/images/background.pngbin0 -> 9287 bytes
-rw-r--r--examples/wayland/texture-sharing/custom-compositor/images/car.ktxbin0 -> 11908 bytes
-rw-r--r--examples/wayland/texture-sharing/custom-compositor/images/qt4.astcbin0 -> 12816 bytes
-rw-r--r--examples/wayland/texture-sharing/custom-compositor/images/qt_logo.pngbin0 -> 1528 bytes
-rw-r--r--examples/wayland/texture-sharing/custom-compositor/main.cpp143
-rw-r--r--examples/wayland/texture-sharing/custom-compositor/qml/main.qml122
-rw-r--r--examples/wayland/texture-sharing/minimal-compositor.qml93
-rw-r--r--examples/wayland/texture-sharing/qml-client/main.cpp68
-rw-r--r--examples/wayland/texture-sharing/qml-client/main.qml248
-rw-r--r--examples/wayland/texture-sharing/qml-client/qml-client.pro13
-rw-r--r--examples/wayland/texture-sharing/qml-client/qml-client.qrc5
-rw-r--r--examples/wayland/texture-sharing/texture-sharing.pro5
-rw-r--r--examples/wayland/wayland.pro18
-rw-r--r--src/3rdparty/protocol/qt_attribution.json17
-rw-r--r--src/3rdparty/protocol/wp-primary-selection-unstable-v1.xml225
-rw-r--r--src/client/client.pro8
-rw-r--r--src/client/configure.json17
-rw-r--r--src/client/hardwareintegration/qwaylandserverbufferintegration_p.h3
-rw-r--r--src/client/qwaylandclipboard.cpp71
-rw-r--r--src/client/qwaylandcursor.cpp7
-rw-r--r--src/client/qwaylandcursor_p.h2
-rw-r--r--src/client/qwaylanddatadevice.cpp16
-rw-r--r--src/client/qwaylanddataoffer.cpp15
-rw-r--r--src/client/qwaylanddataoffer_p.h24
-rw-r--r--src/client/qwaylanddisplay.cpp17
-rw-r--r--src/client/qwaylanddisplay_p.h12
-rw-r--r--src/client/qwaylandextendedsurface.cpp2
-rw-r--r--src/client/qwaylandinputcontext.cpp8
-rw-r--r--src/client/qwaylandinputdevice.cpp487
-rw-r--r--src/client/qwaylandinputdevice_p.h106
-rw-r--r--src/client/qwaylandnativeinterface.cpp2
-rw-r--r--src/client/qwaylandprimaryselectionv1.cpp162
-rw-r--r--src/client/qwaylandprimaryselectionv1_p.h148
-rw-r--r--src/client/qwaylandqtkey.cpp6
-rw-r--r--src/client/qwaylandscreen.cpp5
-rw-r--r--src/client/qwaylandshmbackingstore.cpp13
-rw-r--r--src/client/qwaylandsurface.cpp122
-rw-r--r--src/client/qwaylandsurface_p.h97
-rw-r--r--src/client/qwaylandtouch.cpp1
-rw-r--r--src/client/qwaylandwindow.cpp170
-rw-r--r--src/client/qwaylandwindow_p.h19
-rw-r--r--src/compositor/compositor_api/compositor_api.pri15
-rw-r--r--src/compositor/compositor_api/qwaylandcompositor.cpp2
-rw-r--r--src/compositor/compositor_api/qwaylandquickcompositor.cpp4
-rw-r--r--src/compositor/compositor_api/qwaylandquickitem.cpp76
-rw-r--r--src/compositor/compositor_api/qwaylandquickitem_p.h2
-rw-r--r--src/compositor/compositor_api/qwaylandseat.cpp33
-rw-r--r--src/compositor/compositor_api/qwaylandtouch.cpp19
-rw-r--r--src/compositor/compositor_api/qwaylandtouch_p.h2
-rw-r--r--src/compositor/configure.json10
-rw-r--r--src/compositor/extensions/extensions.pri18
-rw-r--r--src/compositor/extensions/qwaylandquickshellintegration.cpp256
-rw-r--r--src/compositor/extensions/qwaylandquickshellintegration.h76
-rw-r--r--src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp66
-rw-r--r--src/compositor/extensions/qwaylandquickshellsurfaceitem.h15
-rw-r--r--src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h17
-rw-r--r--src/compositor/extensions/qwaylandxdgshell.cpp20
-rw-r--r--src/compositor/extensions/qwaylandxdgshell.h1
-rw-r--r--src/compositor/extensions/qwaylandxdgshellv5.cpp4
-rw-r--r--src/compositor/extensions/qwaylandxdgshellv5_p.h2
-rw-r--r--src/compositor/extensions/qwaylandxdgshellv6.cpp20
-rw-r--r--src/compositor/extensions/qwaylandxdgshellv6.h1
-rw-r--r--src/compositor/extensions/qwltexturesharingextension.cpp497
-rw-r--r--src/compositor/extensions/qwltexturesharingextension_p.h159
-rw-r--r--src/compositor/hardware_integration/qwlserverbufferintegration_p.h11
-rw-r--r--src/extensions/README.md18
-rw-r--r--src/extensions/qt-texture-sharing-unstable-v1.xml57
-rw-r--r--src/extensions/qt-vulkan-server-buffer-unstable-v1.xml64
-rw-r--r--src/hardwareintegration/client/vulkan-server/vulkan-server.pri12
-rw-r--r--src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp213
-rw-r--r--src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h102
-rw-r--r--src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp5
-rw-r--r--src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp2
-rw-r--r--src/hardwareintegration/compositor/vulkan-server/vulkan-server.pri16
-rw-r--r--src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp322
-rw-r--r--src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.h111
-rw-r--r--src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp731
-rw-r--r--src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.h70
-rw-r--r--src/imports/compositor/qwaylandquickcompositorplugin.cpp2
-rw-r--r--src/imports/imports.pro11
-rw-r--r--src/imports/texture-sharing-extension/plugin.cpp109
-rw-r--r--src/imports/texture-sharing-extension/qmldir3
-rw-r--r--src/imports/texture-sharing-extension/texture-sharing-extension.pro11
-rw-r--r--src/imports/texture-sharing/plugin.cpp97
-rw-r--r--src/imports/texture-sharing/qmldir3
-rw-r--r--src/imports/texture-sharing/sharedtextureprovider.cpp322
-rw-r--r--src/imports/texture-sharing/sharedtextureprovider.h118
-rw-r--r--src/imports/texture-sharing/texture-sharing.pro21
-rw-r--r--src/imports/texture-sharing/texturesharingextension.cpp86
-rw-r--r--src/imports/texture-sharing/texturesharingextension.h77
-rw-r--r--src/plugins/hardwareintegration/client/client.pro2
-rw-r--r--src/plugins/hardwareintegration/client/vulkan-server/main.cpp66
-rw-r--r--src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.json3
-rw-r--r--src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.pro15
-rw-r--r--src/plugins/hardwareintegration/compositor/compositor.pro2
-rw-r--r--src/plugins/hardwareintegration/compositor/vulkan-server/main.cpp62
-rw-r--r--src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.json3
-rw-r--r--src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.pro12
-rw-r--r--src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp2
-rw-r--r--src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.cpp2
-rw-r--r--src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp2
-rw-r--r--src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp19
-rw-r--r--src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp4
-rw-r--r--src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp2
-rw-r--r--src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp2
-rw-r--r--src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp2
-rw-r--r--src/qtwaylandscanner/qtwaylandscanner.cpp11
-rw-r--r--src/src.pro2
-rw-r--r--sync.profile4
-rw-r--r--tests/auto/client/client.pro2
-rw-r--r--tests/auto/client/client/tst_client.cpp8
-rw-r--r--tests/auto/client/primaryselectionv1/primaryselectionv1.pro7
-rw-r--r--tests/auto/client/primaryselectionv1/tst_primaryselectionv1.cpp466
-rw-r--r--tests/auto/client/seatv4/tst_seatv4.cpp31
-rw-r--r--tests/auto/client/seatv5/seatv5.pro4
-rw-r--r--tests/auto/client/seatv5/tst_seatv5.cpp387
-rw-r--r--tests/auto/client/shared/corecompositor.h17
-rw-r--r--tests/auto/client/shared/coreprotocol.cpp35
-rw-r--r--tests/auto/client/shared/coreprotocol.h7
-rw-r--r--tests/auto/client/shared/mockcompositor.h8
-rw-r--r--tests/auto/client/surface/tst_surface.cpp6
-rw-r--r--tests/auto/client/xdgshell/tst_xdgshell.cpp30
-rw-r--r--tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp4
-rw-r--r--tests/manual/texture-sharing/cpp-client/cpp-client.pro15
-rw-r--r--tests/manual/texture-sharing/cpp-client/main.cpp229
133 files changed, 7623 insertions, 380 deletions
diff --git a/.qmake.conf b/.qmake.conf
index f8cda0e7d..db2e0b653 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,3 +1,3 @@
load(qt_build_config)
-MODULE_VERSION = 5.13.0
+MODULE_VERSION = 5.14.0
diff --git a/config.tests/vulkan_server_buffer/main.cpp b/config.tests/vulkan_server_buffer/main.cpp
new file mode 100644
index 000000000..5bd88d338
--- /dev/null
+++ b/config.tests/vulkan_server_buffer/main.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Compositor.
+**
+** $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 <vulkan/vulkan.h>
+
+int main()
+{
+ VkExportMemoryAllocateInfoKHR exportAllocInfo = {};
+ exportAllocInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;
+ exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+ return 0;
+}
diff --git a/config.tests/vulkan_server_buffer/vulkan_server_buffer.pro b/config.tests/vulkan_server_buffer/vulkan_server_buffer.pro
new file mode 100644
index 000000000..28dcadcbf
--- /dev/null
+++ b/config.tests/vulkan_server_buffer/vulkan_server_buffer.pro
@@ -0,0 +1 @@
+SOURCES += main.cpp
diff --git a/examples/wayland/server-buffer/README b/examples/wayland/server-buffer/README
index 5744a6baf..da20b0f50 100644
--- a/examples/wayland/server-buffer/README
+++ b/examples/wayland/server-buffer/README
@@ -1,4 +1,7 @@
-This is the example to demonstrate the server buffer interfaces
+This example shows how to use the low-level server buffer extension. This
+version of Qt also provides a texture sharing extension that provides more
+functionality and convenience for sharing graphical assets with Qt Quick
+clients: see the texture-sharing example.
Compile up both compositor and client.
diff --git a/examples/wayland/texture-sharing/.gitignore b/examples/wayland/texture-sharing/.gitignore
new file mode 100644
index 000000000..c684448d3
--- /dev/null
+++ b/examples/wayland/texture-sharing/.gitignore
@@ -0,0 +1,2 @@
+custom-compositor/custom-compositor
+qml-client/qml-client
diff --git a/examples/wayland/texture-sharing/README b/examples/wayland/texture-sharing/README
new file mode 100644
index 000000000..27ea76745
--- /dev/null
+++ b/examples/wayland/texture-sharing/README
@@ -0,0 +1,27 @@
+This example shows how to use the texture sharing extension, allowing
+multiple clients to share the same copy of an image in graphics memory.
+
+The texture sharing extension uses the server buffer extension to transport
+graphics buffers. There are different server buffer plugins for different
+graphics hardware. This is specified by setting an environment variable for
+the compositor process.
+
+-On a device with Mesa and Intel integrated graphics, set:
+
+ QT_WAYLAND_SERVER_BUFFER_INTEGRATION=dmabuf-server
+
+-On a device with NVIDIA graphics, set:
+
+ QT_WAYLAND_SERVER_BUFFER_INTEGRATION=vulkan-server
+
+'custom-compositor' shows how to write a server that creates shared textures
+programmatically.
+
+The file 'minimal-compositor.qml' shows how to add texture sharing to an
+existing compositor, using only QML. It is based on the minimal-qml example,
+and can be executed with qmlscene.
+
+'qml-client' shows how to use shared textures in a Qt Quick client.
+The compositor uses the hardware integration extension to broadcast
+the name of the server buffer integration to all clients, so qml-client
+can be started like any normal wayland client.
diff --git a/examples/wayland/texture-sharing/custom-compositor/compositor.qrc b/examples/wayland/texture-sharing/custom-compositor/compositor.qrc
new file mode 100644
index 000000000..86a8567f7
--- /dev/null
+++ b/examples/wayland/texture-sharing/custom-compositor/compositor.qrc
@@ -0,0 +1,9 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/background.png</file>
+ <file>images/qt_logo.png</file>
+ <file>images/qt4.astc</file>
+ <file>images/car.ktx</file>
+ <file>qml/main.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/wayland/texture-sharing/custom-compositor/custom-compositor.pro b/examples/wayland/texture-sharing/custom-compositor/custom-compositor.pro
new file mode 100644
index 000000000..e80e9e153
--- /dev/null
+++ b/examples/wayland/texture-sharing/custom-compositor/custom-compositor.pro
@@ -0,0 +1,18 @@
+QT += core gui qml
+
+QT += waylandcompositor-private
+
+SOURCES += \
+ main.cpp
+
+OTHER_FILES = \
+ qml/main.qml \
+ qml/Screen.qml \
+ images/background.jpg
+
+RESOURCES += compositor.qrc
+
+TARGET = custom-compositor
+
+target.path = $$[QT_INSTALL_EXAMPLES]/wayland/texture-sharing/custom-compositor
+INSTALLS += target
diff --git a/examples/wayland/texture-sharing/custom-compositor/images/background.png b/examples/wayland/texture-sharing/custom-compositor/images/background.png
new file mode 100644
index 000000000..845830c59
--- /dev/null
+++ b/examples/wayland/texture-sharing/custom-compositor/images/background.png
Binary files differ
diff --git a/examples/wayland/texture-sharing/custom-compositor/images/car.ktx b/examples/wayland/texture-sharing/custom-compositor/images/car.ktx
new file mode 100644
index 000000000..2aefdd306
--- /dev/null
+++ b/examples/wayland/texture-sharing/custom-compositor/images/car.ktx
Binary files differ
diff --git a/examples/wayland/texture-sharing/custom-compositor/images/qt4.astc b/examples/wayland/texture-sharing/custom-compositor/images/qt4.astc
new file mode 100644
index 000000000..7f7a3f473
--- /dev/null
+++ b/examples/wayland/texture-sharing/custom-compositor/images/qt4.astc
Binary files differ
diff --git a/examples/wayland/texture-sharing/custom-compositor/images/qt_logo.png b/examples/wayland/texture-sharing/custom-compositor/images/qt_logo.png
new file mode 100644
index 000000000..5e2b355ea
--- /dev/null
+++ b/examples/wayland/texture-sharing/custom-compositor/images/qt_logo.png
Binary files differ
diff --git a/examples/wayland/texture-sharing/custom-compositor/main.cpp b/examples/wayland/texture-sharing/custom-compositor/main.cpp
new file mode 100644
index 000000000..a39c8c381
--- /dev/null
+++ b/examples/wayland/texture-sharing/custom-compositor/main.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 <QtGui/QPainter>
+#include <QtGui/QImage>
+
+#include <QtCore/QDateTime>
+
+#include "QtWaylandCompositor/private/qwltexturesharingextension_p.h"
+
+class CustomSharingExtension : public QWaylandTextureSharingExtension
+{
+ Q_OBJECT
+public:
+ CustomSharingExtension() {qDebug("Instantiating custom texture sharing extension.");}
+protected:
+ bool customPixelData(const QString &key, QByteArray *data, QSize *size, uint *glInternalFormat) override
+ {
+ qDebug() << "CustomSharingExtension looking for local texture data for" << key;
+ if (key.startsWith("unreasonably large ")) {
+ int w = 10000;
+ int h = 10000;
+ int numBytes = w * h * 4;
+ *data = QByteArray(numBytes, 0);
+ quint32 *pixels = reinterpret_cast<quint32*>(data->data());
+ for (int i = 0; i < w*h; ++i)
+ pixels[i] = 0xff7f1fff;
+ *glInternalFormat = GL_RGBA8;
+ *size = QSize(w,h);
+ return true;
+ }
+
+ QImage img;
+
+ if (key == QLatin1String("test pattern 1")) {
+ img = QImage(128,128,QImage::Format_ARGB32_Premultiplied);
+ img.fill(QColor(0x55,0x0,0x55,0x01));
+ {
+ QPainter p(&img);
+ QPen pen = p.pen();
+ pen.setWidthF(3);
+ pen.setColor(Qt::red);
+ p.setPen(pen);
+ p.drawLine(0,0,128,128);
+ pen.setColor(Qt::green);
+ p.setPen(pen);
+ p.drawLine(128,0,0,128);
+ pen.setColor(Qt::blue);
+ p.setPen(pen);
+ p.drawLine(32,16,96,16);
+ pen.setColor(Qt::black);
+ p.setPen(pen);
+ p.translate(64, 64);
+ p.rotate(45);
+ p.drawText(QRect(-48, -32, 96, 64),
+ QDateTime::currentDateTime().toString(),
+ QTextOption(Qt::AlignHCenter));
+ }
+ }
+
+ if (!img.isNull()) {
+ img = img.convertToFormat(QImage::Format_RGBA8888);
+ *data = QByteArray(reinterpret_cast<const char*>(img.constBits()), img.sizeInBytes());
+ *size = img.size();
+ *glInternalFormat = GL_RGBA8;
+ return true;
+ }
+ return false;
+ }
+};
+
+Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(CustomSharingExtension);
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
+ QGuiApplication app(argc, argv);
+ QQmlApplicationEngine appEngine;
+
+ qmlRegisterType<CustomSharingExtensionQuickExtension>("com.theqtcompany.customsharingextension", 1, 0, "CustomSharingExtension");
+ appEngine.addImageProvider("wlshared", new QWaylandSharedTextureProvider);
+
+ appEngine.load(QUrl("qrc:///qml/main.qml"));
+
+ return app.exec();
+}
+
+#include "main.moc"
diff --git a/examples/wayland/texture-sharing/custom-compositor/qml/main.qml b/examples/wayland/texture-sharing/custom-compositor/qml/main.qml
new file mode 100644
index 000000000..16a412fcd
--- /dev/null
+++ b/examples/wayland/texture-sharing/custom-compositor/qml/main.qml
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QtQuick.Window 2.2
+import QtWayland.Compositor 1.3
+
+import com.theqtcompany.customsharingextension 1.0
+
+WaylandCompositor {
+ WaylandOutput {
+ sizeFollowsWindow: true
+ window: Window {
+ width: 1024
+ height: 768
+ visible: true
+ Image {
+ id: background
+ anchors.fill: parent
+ fillMode: Image.Tile
+ source: "qrc:/images/background.png"
+ smooth: true
+
+ Rectangle {
+ width: 100
+ height: 100
+ color: "red"
+ anchors.bottom: parent.bottom;
+ anchors.right: parent.right;
+ MouseArea {
+ anchors.fill: parent
+ onClicked: sharedTextureImage.source = "image://wlshared/car.ktx"
+ }
+ }
+ Image {
+ id: sharedTextureImage
+ anchors.bottom: parent.bottom;
+ anchors.right: parent.right;
+ source: ""
+ }
+ Image {
+ id: topRightImage
+ anchors.top: parent.top;
+ anchors.right: parent.right;
+ source: "image://wlshared/qt_logo.png"
+ }
+ }
+ Repeater {
+ model: shellSurfaces
+ ShellSurfaceItem {
+ autoCreatePopupItems: true
+ shellSurface: modelData
+ onSurfaceDestroyed: shellSurfaces.remove(index)
+ }
+ }
+ }
+ }
+ WlShell {
+ onWlShellSurfaceCreated:
+ shellSurfaces.append({shellSurface: shellSurface});
+ }
+ XdgShellV6 {
+ onToplevelCreated:
+ shellSurfaces.append({shellSurface: xdgSurface});
+ }
+ XdgShell {
+ onToplevelCreated:
+ shellSurfaces.append({shellSurface: xdgSurface});
+ }
+ ListModel { id: shellSurfaces }
+
+ CustomSharingExtension {
+ imageSearchPath: ":/images;."
+ }
+}
diff --git a/examples/wayland/texture-sharing/minimal-compositor.qml b/examples/wayland/texture-sharing/minimal-compositor.qml
new file mode 100644
index 000000000..3f714dc58
--- /dev/null
+++ b/examples/wayland/texture-sharing/minimal-compositor.qml
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $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 QtQuick.Window 2.2
+import QtWayland.Compositor 1.3
+
+// importing the texture sharing extension:
+import QtWayland.Compositor.TextureSharingExtension 1.0
+
+WaylandCompositor {
+ WaylandOutput {
+ sizeFollowsWindow: true
+ window: Window {
+ width: 1024
+ height: 768
+ visible: true
+ Repeater {
+ model: shellSurfaces
+ ShellSurfaceItem {
+ autoCreatePopupItems: true
+ shellSurface: modelData
+ onSurfaceDestroyed: shellSurfaces.remove(index)
+ }
+ }
+ }
+ }
+ WlShell {
+ onWlShellSurfaceCreated:
+ shellSurfaces.append({shellSurface: shellSurface});
+ }
+ XdgShellV6 {
+ onToplevelCreated:
+ shellSurfaces.append({shellSurface: xdgSurface});
+ }
+ XdgShell {
+ onToplevelCreated:
+ shellSurfaces.append({shellSurface: xdgSurface});
+ }
+ ListModel { id: shellSurfaces }
+
+ // instantiating the texture sharing extension:
+ TextureSharingExtension {
+ imageSearchPath: ".;/tmp;/usr/share/pixmaps"
+ }
+}
diff --git a/examples/wayland/texture-sharing/qml-client/main.cpp b/examples/wayland/texture-sharing/qml-client/main.cpp
new file mode 100644
index 000000000..618d6701d
--- /dev/null
+++ b/examples/wayland/texture-sharing/qml-client/main.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2019 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 <QtQuick/QQuickView>
+#include <QStandardPaths>
+#include <QFileInfo>
+#include <QQmlApplicationEngine>
+#include <QDebug>
+#include <QDir>
+#include <QTimer>
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+ QQmlApplicationEngine appEngine;
+
+ appEngine.load(QUrl("qrc:///main.qml"));
+
+ return app.exec();
+}
diff --git a/examples/wayland/texture-sharing/qml-client/main.qml b/examples/wayland/texture-sharing/qml-client/main.qml
new file mode 100644
index 000000000..371a97594
--- /dev/null
+++ b/examples/wayland/texture-sharing/qml-client/main.qml
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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.9
+import QtQuick.Window 2.2
+
+import QtWayland.Client.TextureSharing 1.0
+
+Window {
+ width: 800
+ height: 500
+ visible: true
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#C0FEFE"
+
+ Flickable {
+ anchors.fill: parent
+ contentHeight: imageGrid.height
+
+ Grid {
+ id: imageGrid
+ columns: 2
+ width: parent.width
+ spacing: 25
+ padding: 25
+
+
+ // loadedImage
+ Text {
+ width: 400
+ wrapMode: Text.Wrap
+ text: "An Image element using the shared buffer provider to load from a PNG image.<br>" +
+ "Source: '" + loadedImage.source + "'" +
+ (loadedImage.sourceSize.height <= 0 ? "<font color=\"#FF0000\"><br>[Image not loaded]</font>" : "")
+ }
+ Image {
+ id: loadedImage
+ fillMode: Image.PreserveAspectFit
+ source: "image://wlshared/qt_logo.png"
+ }
+ Rectangle {
+ visible: loadedImage.height <= 0
+ width:100; height: 100
+ color: "green"
+ }
+
+ // paintedImage
+ Text {
+ width: 400
+ wrapMode: Text.Wrap
+ text: "An Image element using the shared buffer provider.<br>" +
+ "This texture is created by the compositor using QPainter. <br>" +
+ "Source: '" + paintedImage.source + "'" +
+ (paintedImage.sourceSize.height <= 0 ? "<font color=\"#FF0000\"><br>[Image not loaded]</font>" : "")
+ }
+ Image {
+ id: paintedImage
+ fillMode: Image.PreserveAspectFit
+ source: "image://wlshared/test pattern 1"
+ }
+ Rectangle {
+ visible: paintedImage.height <= 0
+ width:100; height: 100
+ color: "green"
+ }
+
+ // ktxImage
+ Text {
+ width: 400
+ wrapMode: Text.Wrap
+ text: "An Image element using the shared buffer provider to load an ETC2 compressed texture." +
+ "<br>Source: '" + ktxImage.source + "'" +
+ (ktxImage.sourceSize.height <= 0 ? "<font color=\"#FF0000\"><br>[Image not loaded]</font>" : "")
+ }
+ Image {
+ id: ktxImage
+ source: "image://wlshared/car.ktx"
+ fillMode: Image.PreserveAspectFit
+ }
+ Rectangle {
+ visible: ktxImage.height <= 0
+ width:100; height: 100
+ color: "green"
+ }
+
+ //astcImage
+ Text {
+ width: 400
+ wrapMode: Text.Wrap
+ text: "An Image element using the shared buffer provider to load an ASTC compressed texture." +
+ "<br>Source: '" + astcImage.source + "'" +
+ (astcImage.sourceSize.height <= 0 ? "<font color=\"#FF0000\"><br>[Image not loaded]</font>" : "")
+ }
+
+ Image {
+ id: astcImage
+ source: "image://wlshared/qt4.astc"
+ fillMode: Image.PreserveAspectFit
+ }
+ Rectangle {
+ visible: astcImage.height <= 0
+ width:100; height: 100
+ color: "green"
+ }
+
+ // dynamicImage
+ Column {
+ Text {
+ width: 400
+ wrapMode: Text.Wrap
+ text: "An Image element using the shared buffer provider." +
+ "<br>Source: '" + dynamicImage.source + "'" +
+ (dynamicImage.sourceSize.height <= 0 ? "<font color=\"#FF0000\"><br>[Image not loaded]</font>" : "")
+ }
+ Row {
+ spacing: 10
+ Text {
+ text: "Enter filename:"
+ }
+ Rectangle {
+ color: "white"
+ width: sourceEdit.contentWidth + 30
+ height: sourceEdit.contentHeight
+ TextInput {
+ id: sourceEdit
+ anchors.fill: parent
+ horizontalAlignment: TextInput.AlignHCenter
+ onEditingFinished: dynamicImage.source = text ? "image://wlshared/" + text : ""
+ }
+ }
+ }
+ }
+ Image {
+ id: dynamicImage
+ fillMode: Image.PreserveAspectFit
+ }
+ Rectangle {
+ visible: dynamicImage.height <= 0
+ width:100; height: 100
+ color: "green"
+ }
+
+ // largeImage
+ Text {
+ width: 400
+ wrapMode: Text.Wrap
+ text: "An Image element using the shared buffer provider.<br>" +
+ "Left click to load a very large image. " +
+ "Right click to unload the image, potentially freeing graphics memory on the server-side " +
+ "if no other client is using the image." +
+ "<br>Source: '" + largeImage.source + "'" +
+ "<br>Size: " + largeImage.sourceSize +
+ (largeImage.sourceSize.height <= 0 ? "<font color=\"#FF0000\"><br>[Image not loaded]</font>" : "")
+ }
+
+ Rectangle {
+ width: 200
+ height: 200
+ border.color: "black"
+ border.width: 2
+ color: "transparent"
+ Image {
+ id: largeImage
+ anchors.fill: parent
+ fillMode: Image.PreserveAspectFit
+ }
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onClicked: {
+ if (mouse.button == Qt.LeftButton)
+ largeImage.source = "image://wlshared/unreasonably large image"
+ else
+ largeImage.source = ""
+
+ }
+ }
+ }
+
+ } // Grid
+ }
+
+ Rectangle {
+ color: "gray"
+ width: parent.width
+ height: 20
+ anchors.bottom: parent.bottom
+
+ Text {
+ color: "white"
+ anchors.fill: parent
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ text: "Scroll or drag for more"
+ }
+ }
+
+ }
+}
diff --git a/examples/wayland/texture-sharing/qml-client/qml-client.pro b/examples/wayland/texture-sharing/qml-client/qml-client.pro
new file mode 100644
index 000000000..67d5c7071
--- /dev/null
+++ b/examples/wayland/texture-sharing/qml-client/qml-client.pro
@@ -0,0 +1,13 @@
+QT += quick
+
+SOURCES += \
+ main.cpp
+
+RESOURCES += \
+ qml-client.qrc
+
+DISTFILES += \
+ main.qml
+
+target.path = $$[QT_INSTALL_EXAMPLES]/wayland/texture-sharing/qml-client
+INSTALLS += target
diff --git a/examples/wayland/texture-sharing/qml-client/qml-client.qrc b/examples/wayland/texture-sharing/qml-client/qml-client.qrc
new file mode 100644
index 000000000..5f6483ac3
--- /dev/null
+++ b/examples/wayland/texture-sharing/qml-client/qml-client.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/wayland/texture-sharing/texture-sharing.pro b/examples/wayland/texture-sharing/texture-sharing.pro
new file mode 100644
index 000000000..3f7792828
--- /dev/null
+++ b/examples/wayland/texture-sharing/texture-sharing.pro
@@ -0,0 +1,5 @@
+TEMPLATE=subdirs
+
+SUBDIRS += \
+ qml-client \
+ custom-compositor
diff --git a/examples/wayland/wayland.pro b/examples/wayland/wayland.pro
index b9e4263e7..eeb962eb2 100644
--- a/examples/wayland/wayland.pro
+++ b/examples/wayland/wayland.pro
@@ -1,10 +1,11 @@
requires(qtHaveModule(waylandcompositor))
-requires(qtConfig(opengl))
TEMPLATE=subdirs
-SUBDIRS += \
- qwindow-compositor \
- minimal-cpp
+qtHaveModule(opengl) {
+ SUBDIRS += \
+ qwindow-compositor \
+ minimal-cpp
+}
qtHaveModule(quick) {
SUBDIRS += minimal-qml
@@ -17,8 +18,13 @@ qtHaveModule(quick) {
SUBDIRS += server-side-decoration
qtHaveModule(waylandclient) {
SUBDIRS += \
- custom-extension \
- server-buffer
+ custom-extension
+
+ qtHaveModule(opengl) {
+ SUBDIRS += \
+ server-buffer \
+ texture-sharing
+ }
}
SUBDIRS += hwlayer-compositor
}
diff --git a/src/3rdparty/protocol/qt_attribution.json b/src/3rdparty/protocol/qt_attribution.json
index 7e068f755..e6f90698b 100644
--- a/src/3rdparty/protocol/qt_attribution.json
+++ b/src/3rdparty/protocol/qt_attribution.json
@@ -56,6 +56,23 @@ Copyright (c) 2013 BMW Car IT GmbH"
},
{
+ "Id": "wayland-primary-selection-protocol",
+ "Name": "Wayland Primary Selection Protocol",
+ "QDocModule": "qtwaylandcompositor",
+ "QtUsage": "Used in the Qt Wayland platform plugin",
+ "Files": "wp-primary-selection-unstable-v1.xml",
+
+ "Description": "The primary selection extension allows copying text by selecting it and pasting it with the middle mouse button.",
+ "Homepage": "https://wayland.freedesktop.org",
+ "Version": "1",
+ "DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland-protocols/plain/unstable/primary-selection/primary-selection-unstable-v1.xml",
+ "LicenseId": "MIT",
+ "License": "MIT License",
+ "LicenseFile": "MIT_LICENSE.txt",
+ "Copyright": "Copyright © 2015 2016 Red Hat"
+ },
+
+ {
"Id": "wayland-scaler-protocol",
"Name": "Wayland Scaler Protocol",
"QDocModule": "qtwaylandcompositor",
diff --git a/src/3rdparty/protocol/wp-primary-selection-unstable-v1.xml b/src/3rdparty/protocol/wp-primary-selection-unstable-v1.xml
new file mode 100644
index 000000000..e5a39e34c
--- /dev/null
+++ b/src/3rdparty/protocol/wp-primary-selection-unstable-v1.xml
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wp_primary_selection_unstable_v1">
+ <copyright>
+ Copyright © 2015, 2016 Red Hat
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <description summary="Primary selection protocol">
+ This protocol provides the ability to have a primary selection device to
+ match that of the X server. This primary selection is a shortcut to the
+ common clipboard selection, where text just needs to be selected in order
+ to allow copying it elsewhere. The de facto way to perform this action
+ is the middle mouse button, although it is not limited to this one.
+
+ Clients wishing to honor primary selection should create a primary
+ selection source and set it as the selection through
+ wp_primary_selection_device.set_selection whenever the text selection
+ changes. In order to minimize calls in pointer-driven text selection,
+ it should happen only once after the operation finished. Similarly,
+ a NULL source should be set when text is unselected.
+
+ wp_primary_selection_offer objects are first announced through the
+ wp_primary_selection_device.data_offer event. Immediately after this event,
+ the primary data offer will emit wp_primary_selection_offer.offer events
+ to let know of the mime types being offered.
+
+ When the primary selection changes, the client with the keyboard focus
+ will receive wp_primary_selection_device.selection events. Only the client
+ with the keyboard focus will receive such events with a non-NULL
+ wp_primary_selection_offer. Across keyboard focus changes, previously
+ focused clients will receive wp_primary_selection_device.events with a
+ NULL wp_primary_selection_offer.
+
+ In order to request the primary selection data, the client must pass
+ a recent serial pertaining to the press event that is triggering the
+ operation, if the compositor deems the serial valid and recent, the
+ wp_primary_selection_source.send event will happen in the other end
+ to let the transfer begin. The client owning the primary selection
+ should write the requested data, and close the file descriptor
+ immediately.
+
+ If the primary selection owner client disappeared during the transfer,
+ the client reading the data will receive a
+ wp_primary_selection_device.selection event with a NULL
+ wp_primary_selection_offer, the client should take this as a hint
+ to finish the reads related to the no longer existing offer.
+
+ The primary selection owner should be checking for errors during
+ writes, merely cancelling the ongoing transfer if any happened.
+ </description>
+
+ <interface name="zwp_primary_selection_device_manager_v1" version="1">
+ <description summary="X primary selection emulation">
+ The primary selection device manager is a singleton global object that
+ provides access to the primary selection. It allows to create
+ wp_primary_selection_source objects, as well as retrieving the per-seat
+ wp_primary_selection_device objects.
+ </description>
+
+ <request name="create_source">
+ <description summary="create a new primary selection source">
+ Create a new primary selection source.
+ </description>
+ <arg name="id" type="new_id" interface="zwp_primary_selection_source_v1"/>
+ </request>
+
+ <request name="get_device">
+ <description summary="create a new primary selection device">
+ Create a new data device for a given seat.
+ </description>
+ <arg name="id" type="new_id" interface="zwp_primary_selection_device_v1"/>
+ <arg name="seat" type="object" interface="wl_seat"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the primary selection device manager">
+ Destroy the primary selection device manager.
+ </description>
+ </request>
+ </interface>
+
+ <interface name="zwp_primary_selection_device_v1" version="1">
+ <request name="set_selection">
+ <description summary="set the primary selection">
+ Replaces the current selection. The previous owner of the primary
+ selection will receive a wp_primary_selection_source.cancelled event.
+
+ To unset the selection, set the source to NULL.
+ </description>
+ <arg name="source" type="object" interface="zwp_primary_selection_source_v1" allow-null="true"/>
+ <arg name="serial" type="uint" summary="serial of the event that triggered this request"/>
+ </request>
+
+ <event name="data_offer">
+ <description summary="introduce a new wp_primary_selection_offer">
+ Introduces a new wp_primary_selection_offer object that may be used
+ to receive the current primary selection. Immediately following this
+ event, the new wp_primary_selection_offer object will send
+ wp_primary_selection_offer.offer events to describe the offered mime
+ types.
+ </description>
+ <arg name="offer" type="new_id" interface="zwp_primary_selection_offer_v1"/>
+ </event>
+
+ <event name="selection">
+ <description summary="advertise a new primary selection">
+ The wp_primary_selection_device.selection event is sent to notify the
+ client of a new primary selection. This event is sent after the
+ wp_primary_selection.data_offer event introducing this object, and after
+ the offer has announced its mimetypes through
+ wp_primary_selection_offer.offer.
+
+ The data_offer is valid until a new offer or NULL is received
+ or until the client loses keyboard focus. The client must destroy the
+ previous selection data_offer, if any, upon receiving this event.
+ </description>
+ <arg name="id" type="object" interface="zwp_primary_selection_offer_v1" allow-null="true"/>
+ </event>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the primary selection device">
+ Destroy the primary selection device.
+ </description>
+ </request>
+ </interface>
+
+ <interface name="zwp_primary_selection_offer_v1" version="1">
+ <description summary="offer to transfer primary selection contents">
+ A wp_primary_selection_offer represents an offer to transfer the contents
+ of the primary selection clipboard to the client. Similar to
+ wl_data_offer, the offer also describes the mime types that the data can
+ be converted to and provides the mechanisms for transferring the data
+ directly to the client.
+ </description>
+
+ <request name="receive">
+ <description summary="request that the data is transferred">
+ To transfer the contents of the primary selection clipboard, the client
+ issues this request and indicates the mime type that it wants to
+ receive. The transfer happens through the passed file descriptor
+ (typically created with the pipe system call). The source client writes
+ the data in the mime type representation requested and then closes the
+ file descriptor.
+
+ The receiving client reads from the read end of the pipe until EOF and
+ closes its end, at which point the transfer is complete.
+ </description>
+ <arg name="mime_type" type="string"/>
+ <arg name="fd" type="fd"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the primary selection offer">
+ Destroy the primary selection offer.
+ </description>
+ </request>
+
+ <event name="offer">
+ <description summary="advertise offered mime type">
+ Sent immediately after creating announcing the
+ wp_primary_selection_offer through
+ wp_primary_selection_device.data_offer. One event is sent per offered
+ mime type.
+ </description>
+ <arg name="mime_type" type="string"/>
+ </event>
+ </interface>
+
+ <interface name="zwp_primary_selection_source_v1" version="1">
+ <description summary="offer to replace the contents of the primary selection">
+ The source side of a wp_primary_selection_offer, it provides a way to
+ describe the offered data and respond to requests to transfer the
+ requested contents of the primary selection clipboard.
+ </description>
+
+ <request name="offer">
+ <description summary="add an offered mime type">
+ This request adds a mime type to the set of mime types advertised to
+ targets. Can be called several times to offer multiple types.
+ </description>
+ <arg name="mime_type" type="string"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the primary selection source">
+ Destroy the primary selection source.
+ </description>
+ </request>
+
+ <event name="send">
+ <description summary="send the primary selection contents">
+ Request for the current primary selection contents from the client.
+ Send the specified mime type over the passed file descriptor, then
+ close it.
+ </description>
+ <arg name="mime_type" type="string"/>
+ <arg name="fd" type="fd"/>
+ </event>
+
+ <event name="cancelled">
+ <description summary="request for primary selection contents was canceled">
+ This primary selection source is no longer valid. The client should
+ clean up and destroy this primary selection source.
+ </description>
+ </event>
+ </interface>
+</protocol>
diff --git a/src/client/client.pro b/src/client/client.pro
index db91bd691..4f4c58328 100644
--- a/src/client/client.pro
+++ b/src/client/client.pro
@@ -31,6 +31,7 @@ WAYLANDCLIENTSOURCES += \
../extensions/touch-extension.xml \
../extensions/qt-key-unstable-v1.xml \
../extensions/qt-windowmanager.xml \
+ ../3rdparty/protocol/wp-primary-selection-unstable-v1.xml \
../3rdparty/protocol/text-input-unstable-v2.xml \
../3rdparty/protocol/xdg-output-unstable-v1.xml \
../3rdparty/protocol/wayland.xml
@@ -46,6 +47,7 @@ SOURCES += qwaylandintegration.cpp \
qwaylandshellsurface.cpp \
qwaylandextendedsurface.cpp \
qwaylandsubsurface.cpp \
+ qwaylandsurface.cpp \
qwaylandtouch.cpp \
qwaylandqtkey.cpp \
../shared/qwaylandmimehelper.cpp \
@@ -70,6 +72,7 @@ HEADERS += qwaylandintegration_p.h \
qwaylandshellsurface_p.h \
qwaylandextendedsurface_p.h \
qwaylandsubsurface_p.h \
+ qwaylandsurface_p.h \
qwaylandtouch_p.h \
qwaylandqtkey_p.h \
qwaylandabstractdecoration_p.h \
@@ -116,6 +119,11 @@ qtConfig(wayland-datadevice) {
qwaylanddatasource.cpp
}
+qtConfig(wayland-client-primary-selection) {
+ HEADERS += qwaylandprimaryselectionv1_p.h
+ SOURCES += qwaylandprimaryselectionv1.cpp
+}
+
qtConfig(draganddrop) {
HEADERS += \
qwaylanddnd_p.h
diff --git a/src/client/configure.json b/src/client/configure.json
index 93c5d4e49..403a2edcf 100644
--- a/src/client/configure.json
+++ b/src/client/configure.json
@@ -75,6 +75,11 @@
"type": "compile",
"test": "dmabuf_server_buffer",
"use": "egl"
+ },
+ "vulkan-server-buffer": {
+ "label": "Vulkan Buffer Sharing",
+ "type": "compile",
+ "test": "vulkan_server_buffer"
}
},
@@ -88,6 +93,11 @@
"condition": "features.draganddrop || features.clipboard",
"output": [ "privateFeature" ]
},
+ "wayland-client-primary-selection": {
+ "label": "primary-selection clipboard",
+ "condition": "features.clipboard",
+ "output": [ "privateFeature" ]
+ },
"wayland-client-fullscreen-shell-v1": {
"label": "fullscreen-shell-v1",
"condition": "features.wayland-client",
@@ -153,6 +163,11 @@
"condition": "features.wayland-client && features.opengl && features.egl && tests.dmabuf-server-buffer",
"output": [ "privateFeature" ]
},
+ "wayland-vulkan-server-buffer": {
+ "label": "Vulkan-based server buffer integration",
+ "condition": "features.wayland-client && features.opengl && features.egl && tests.vulkan-server-buffer",
+ "output": [ "privateFeature" ]
+ },
"wayland-shm-emulation-server-buffer": {
"label": "Shm emulation server buffer integration",
"condition": "features.wayland-client && features.opengl",
@@ -179,6 +194,8 @@
"xcomposite-glx",
"wayland-drm-egl-server-buffer",
"wayland-libhybris-egl-server-buffer",
+ "wayland-dmabuf-server-buffer",
+ "wayland-vulkan-server-buffer",
"wayland-shm-emulation-server-buffer"
]
},
diff --git a/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h b/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h
index 632429bef..6833efd0c 100644
--- a/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h
+++ b/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h
@@ -70,7 +70,8 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandServerBuffer
public:
enum Format {
RGBA32,
- A8
+ A8,
+ Custom
};
QWaylandServerBuffer();
diff --git a/src/client/qwaylandclipboard.cpp b/src/client/qwaylandclipboard.cpp
index 60820da92..369c6ec07 100644
--- a/src/client/qwaylandclipboard.cpp
+++ b/src/client/qwaylandclipboard.cpp
@@ -43,6 +43,9 @@
#include "qwaylanddataoffer_p.h"
#include "qwaylanddatasource_p.h"
#include "qwaylanddatadevice_p.h"
+#if QT_CONFIG(wayland_client_primary_selection)
+#include "qwaylandprimaryselectionv1_p.h"
+#endif
QT_BEGIN_NAMESPACE
@@ -59,44 +62,74 @@ QWaylandClipboard::~QWaylandClipboard()
QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode)
{
- if (mode != QClipboard::Clipboard)
+ auto *seat = mDisplay->currentInputDevice();
+ if (!seat)
return &m_emptyData;
- QWaylandInputDevice *inputDevice = mDisplay->currentInputDevice();
- if (!inputDevice || !inputDevice->dataDevice())
+ switch (mode) {
+ case QClipboard::Clipboard:
+ if (auto *dataDevice = seat->dataDevice()) {
+ if (auto *source = dataDevice->selectionSource())
+ return source->mimeData();
+ if (auto *offer = dataDevice->selectionOffer())
+ return offer->mimeData();
+ }
+ return &m_emptyData;
+ case QClipboard::Selection:
+#if QT_CONFIG(wayland_client_primary_selection)
+ if (auto *selectionDevice = seat->primarySelectionDevice()) {
+ if (auto *source = selectionDevice->selectionSource())
+ return source->mimeData();
+ if (auto *offer = selectionDevice->selectionOffer())
+ return offer->mimeData();
+ }
+#endif
+ return &m_emptyData;
+ default:
return &m_emptyData;
-
- QWaylandDataSource *source = inputDevice->dataDevice()->selectionSource();
- if (source) {
- return source->mimeData();
}
-
- if (inputDevice->dataDevice()->selectionOffer())
- return inputDevice->dataDevice()->selectionOffer()->mimeData();
-
- return &m_emptyData;
}
void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
{
- if (mode != QClipboard::Clipboard)
- return;
-
- QWaylandInputDevice *inputDevice = mDisplay->currentInputDevice();
- if (!inputDevice || !inputDevice->dataDevice())
+ auto *seat = mDisplay->currentInputDevice();
+ if (!seat)
return;
static const QString plain = QStringLiteral("text/plain");
static const QString utf8 = QStringLiteral("text/plain;charset=utf-8");
+
if (data && data->hasFormat(plain) && !data->hasFormat(utf8))
data->setData(utf8, data->data(plain));
- inputDevice->dataDevice()->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), data) : nullptr);
- emitChanged(mode);
+ switch (mode) {
+ case QClipboard::Clipboard:
+ if (auto *dataDevice = seat->dataDevice()) {
+ dataDevice->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), data) : nullptr);
+ emitChanged(mode);
+ }
+ break;
+ case QClipboard::Selection:
+#if QT_CONFIG(wayland_client_primary_selection)
+ if (auto *selectionDevice = seat->primarySelectionDevice()) {
+ selectionDevice->setSelectionSource(data ? new QWaylandPrimarySelectionSourceV1(mDisplay->primarySelectionManager(), data) : nullptr);
+ emitChanged(mode);
+ }
+#endif
+ break;
+ default:
+ break;
+ }
}
bool QWaylandClipboard::supportsMode(QClipboard::Mode mode) const
{
+#if QT_CONFIG(wayland_client_primary_selection)
+ if (mode == QClipboard::Selection) {
+ auto *seat = mDisplay->currentInputDevice();
+ return seat && seat->primarySelectionDevice();
+ }
+#endif
return mode == QClipboard::Clipboard;
}
diff --git a/src/client/qwaylandcursor.cpp b/src/client/qwaylandcursor.cpp
index 8b2ed036d..165df7762 100644
--- a/src/client/qwaylandcursor.cpp
+++ b/src/client/qwaylandcursor.cpp
@@ -209,7 +209,7 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape)
return nullptr;
}
-struct wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape)
+::wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape, uint millisecondsIntoAnimation)
{
struct wl_cursor *waylandCursor = nullptr;
@@ -227,8 +227,9 @@ struct wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape)
return nullptr;
}
- struct wl_cursor_image *image = waylandCursor->images[0];
- struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
+ int frame = wl_cursor_frame(waylandCursor, millisecondsIntoAnimation);
+ ::wl_cursor_image *image = waylandCursor->images[frame];
+ ::wl_buffer *buffer = wl_cursor_image_get_buffer(image);
if (!buffer) {
qCWarning(lcQpaWayland) << "Could not find buffer for cursor";
return nullptr;
diff --git a/src/client/qwaylandcursor_p.h b/src/client/qwaylandcursor_p.h
index 6c48fb628..a4605f3d2 100644
--- a/src/client/qwaylandcursor_p.h
+++ b/src/client/qwaylandcursor_p.h
@@ -75,7 +75,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandCursorTheme
public:
static QWaylandCursorTheme *create(QWaylandShm *shm, int size, const QString &themeName);
~QWaylandCursorTheme();
- struct wl_cursor_image *cursorImage(Qt::CursorShape shape);
+ ::wl_cursor_image *cursorImage(Qt::CursorShape shape, uint millisecondsIntoAnimation = 0);
private:
enum WaylandCursor {
diff --git a/src/client/qwaylanddatadevice.cpp b/src/client/qwaylanddatadevice.cpp
index 300c9de0a..9dbef8d5b 100644
--- a/src/client/qwaylanddatadevice.cpp
+++ b/src/client/qwaylanddatadevice.cpp
@@ -47,6 +47,7 @@
#include "qwaylandinputdevice_p.h"
#include "qwaylanddisplay_p.h"
#include "qwaylandabstractdecoration_p.h"
+#include "qwaylandsurface_p.h"
#include <QtCore/QMimeData>
#include <QtGui/QGuiApplication>
@@ -107,11 +108,12 @@ void QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon)
m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData));
connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled);
- QWaylandWindow *origin = m_display->currentInputDevice()->pointerFocus();
+ auto *seat = m_display->currentInputDevice();
+ auto *origin = seat->pointerFocus();
if (!origin)
- origin = m_display->currentInputDevice()->touchFocus();
+ origin = seat->touchFocus();
- start_drag(m_dragSource->object(), origin->object(), icon->object(), m_display->currentInputDevice()->serial());
+ start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial());
}
void QWaylandDataDevice::cancelDrag()
@@ -151,9 +153,13 @@ void QWaylandDataDevice::data_device_drop()
void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id)
{
- m_enterSerial = serial;
- m_dragWindow = QWaylandWindow::fromWlSurface(surface)->window();
+ auto *dragWaylandWindow = QWaylandWindow::fromWlSurface(surface);
+ if (!dragWaylandWindow)
+ return; // Ignore foreign surfaces
+
+ m_dragWindow = dragWaylandWindow->window();
m_dragPoint = calculateDragPosition(x, y, m_dragWindow);
+ m_enterSerial = serial;
QMimeData *dragData = nullptr;
Qt::DropActions supportedActions;
diff --git a/src/client/qwaylanddataoffer.cpp b/src/client/qwaylanddataoffer.cpp
index 0c732c020..e31e1220f 100644
--- a/src/client/qwaylanddataoffer.cpp
+++ b/src/client/qwaylanddataoffer.cpp
@@ -58,7 +58,8 @@ static QString utf8Text()
QWaylandDataOffer::QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer)
: QtWayland::wl_data_offer(offer)
- , m_mimeData(new QWaylandMimeData(this, display))
+ , m_display(display)
+ , m_mimeData(new QWaylandMimeData(this))
{
}
@@ -81,14 +82,19 @@ QMimeData *QWaylandDataOffer::mimeData()
return m_mimeData.data();
}
+void QWaylandDataOffer::startReceiving(const QString &mimeType, int fd)
+{
+ receive(mimeType, fd);
+ wl_display_flush(m_display->wl_display());
+}
+
void QWaylandDataOffer::data_offer_offer(const QString &mime_type)
{
m_mimeData->appendFormat(mime_type);
}
-QWaylandMimeData::QWaylandMimeData(QWaylandDataOffer *dataOffer, QWaylandDisplay *display)
+QWaylandMimeData::QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer)
: m_dataOffer(dataOffer)
- , m_display(display)
{
}
@@ -140,8 +146,7 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T
return QVariant();
}
- m_dataOffer->receive(mime, pipefd[1]);
- wl_display_flush(m_display->wl_display());
+ m_dataOffer->startReceiving(mime, pipefd[1]);
close(pipefd[1]);
diff --git a/src/client/qwaylanddataoffer_p.h b/src/client/qwaylanddataoffer_p.h
index 5412400a5..9cf1483ca 100644
--- a/src/client/qwaylanddataoffer_p.h
+++ b/src/client/qwaylanddataoffer_p.h
@@ -65,27 +65,40 @@ namespace QtWaylandClient {
class QWaylandDisplay;
class QWaylandMimeData;
-class Q_WAYLAND_CLIENT_EXPORT QWaylandDataOffer : public QtWayland::wl_data_offer
+class QWaylandAbstractDataOffer
+{
+public:
+ virtual void startReceiving(const QString &mimeType, int fd) = 0;
+ virtual QMimeData *mimeData() = 0;
+
+ virtual ~QWaylandAbstractDataOffer() = default;
+};
+
+class Q_WAYLAND_CLIENT_EXPORT QWaylandDataOffer
+ : public QtWayland::wl_data_offer // needs to be the first because we do static casts from the user pointer to the wrapper
+ , public QWaylandAbstractDataOffer
{
public:
explicit QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer);
~QWaylandDataOffer() override;
+ QMimeData *mimeData() override;
QString firstFormat() const;
- QMimeData *mimeData();
+ void startReceiving(const QString &mimeType, int fd) override;
protected:
void data_offer_offer(const QString &mime_type) override;
private:
+ QWaylandDisplay *m_display = nullptr;
QScopedPointer<QWaylandMimeData> m_mimeData;
};
class QWaylandMimeData : public QInternalMimeData {
public:
- explicit QWaylandMimeData(QWaylandDataOffer *dataOffer, QWaylandDisplay *display);
+ explicit QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer);
~QWaylandMimeData() override;
void appendFormat(const QString &mimeType);
@@ -98,13 +111,12 @@ protected:
private:
int readData(int fd, QByteArray &data) const;
- mutable QWaylandDataOffer *m_dataOffer = nullptr;
- QWaylandDisplay *m_display = nullptr;
+ QWaylandAbstractDataOffer *m_dataOffer = nullptr;
mutable QStringList m_types;
mutable QHash<QString, QByteArray> m_data;
};
-}
+} // namespace QtWaylandClient
QT_END_NAMESPACE
#endif
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
index 4a91c1c96..d0f981702 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -41,6 +41,7 @@
#include "qwaylandintegration_p.h"
#include "qwaylandwindow_p.h"
+#include "qwaylandsurface_p.h"
#include "qwaylandabstractdecoration_p.h"
#include "qwaylandscreen_p.h"
#include "qwaylandcursor_p.h"
@@ -51,7 +52,10 @@
#if QT_CONFIG(wayland_datadevice)
#include "qwaylanddatadevicemanager_p.h"
#include "qwaylanddatadevice_p.h"
-#endif
+#endif // QT_CONFIG(wayland_datadevice)
+#if QT_CONFIG(wayland_client_primary_selection)
+#include "qwaylandprimaryselectionv1_p.h"
+#endif // QT_CONFIG(wayland_client_primary_selection)
#if QT_CONFIG(cursor)
#include <wayland-cursor.h>
#endif
@@ -68,6 +72,7 @@
#include "qwaylandqtkey_p.h"
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
+#include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h>
#include <QtCore/private/qcore_unix_p.h>
@@ -109,7 +114,7 @@ struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
return nullptr;
}
- return mSubCompositor->get_subsurface(window->object(), parent->object());
+ return mSubCompositor->get_subsurface(window->wlSurface(), parent->wlSurface());
}
QWaylandShellIntegration *QWaylandDisplay::shellIntegration() const
@@ -317,6 +322,10 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
mTouchExtension.reset(new QWaylandTouchExtension(this, id));
} else if (interface == QStringLiteral("zqt_key_v1")) {
mQtKeyExtension.reset(new QWaylandQtKeyExtension(this, id));
+#if QT_CONFIG(wayland_client_primary_selection)
+ } else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) {
+ mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1));
+#endif
} else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) {
mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
@@ -377,9 +386,9 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
}
}
-bool QWaylandDisplay::hasRegistryGlobal(const QString &interfaceName)
+bool QWaylandDisplay::hasRegistryGlobal(QStringView interfaceName) const
{
- Q_FOREACH (const RegistryGlobal &global, mGlobals)
+ for (const RegistryGlobal &global : mGlobals)
if (global.interface == interfaceName)
return true;
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
index 558d8d9b5..1f085e67e 100644
--- a/src/client/qwaylanddisplay_p.h
+++ b/src/client/qwaylanddisplay_p.h
@@ -93,11 +93,15 @@ class QWaylandScreen;
class QWaylandClientBufferIntegration;
class QWaylandWindowManagerIntegration;
class QWaylandDataDeviceManager;
+#if QT_CONFIG(wayland_client_primary_selection)
+class QWaylandPrimarySelectionDeviceManagerV1;
+#endif
class QWaylandTouchExtension;
class QWaylandQtKeyExtension;
class QWaylandWindow;
class QWaylandIntegration;
class QWaylandHardwareIntegration;
+class QWaylandSurface;
class QWaylandShellIntegration;
class QWaylandCursor;
class QWaylandCursorTheme;
@@ -149,6 +153,9 @@ public:
#if QT_CONFIG(wayland_datadevice)
QWaylandDataDeviceManager *dndSelectionHandler() const { return mDndSelectionHandler.data(); }
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+ QWaylandPrimarySelectionDeviceManagerV1 *primarySelectionManager() const { return mPrimarySelectionManager.data(); }
+#endif
QtWayland::qt_surface_extension *windowExtension() const { return mWindowExtension.data(); }
QWaylandTouchExtension *touchExtension() const { return mTouchExtension.data(); }
QtWayland::zwp_text_input_manager_v2 *textInputManager() const { return mTextInputManager.data(); }
@@ -166,7 +173,7 @@ public:
: id(id_), interface(interface_), version(version_), registry(registry_) { }
};
QList<RegistryGlobal> globals() const { return mGlobals; }
- bool hasRegistryGlobal(const QString &interfaceName);
+ bool hasRegistryGlobal(QStringView interfaceName) const;
/* wl_registry_add_listener does not add but rather sets a listener, so this function is used
* to enable many listeners at once. */
@@ -237,6 +244,9 @@ private:
QScopedPointer<QWaylandTouchExtension> mTouchExtension;
QScopedPointer<QWaylandQtKeyExtension> mQtKeyExtension;
QScopedPointer<QWaylandWindowManagerIntegration> mWindowManagerIntegration;
+#if QT_CONFIG(wayland_client_primary_selection)
+ QScopedPointer<QWaylandPrimarySelectionDeviceManagerV1> mPrimarySelectionManager;
+#endif
QScopedPointer<QtWayland::zwp_text_input_manager_v2> mTextInputManager;
QScopedPointer<QWaylandHardwareIntegration> mHardwareIntegration;
QScopedPointer<QtWayland::zxdg_output_manager_v1> mXdgOutputManager;
diff --git a/src/client/qwaylandextendedsurface.cpp b/src/client/qwaylandextendedsurface.cpp
index c5db6d7ba..a7836e292 100644
--- a/src/client/qwaylandextendedsurface.cpp
+++ b/src/client/qwaylandextendedsurface.cpp
@@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
QWaylandExtendedSurface::QWaylandExtendedSurface(QWaylandWindow *window)
- : QtWayland::qt_extended_surface(window->display()->windowExtension()->get_extended_surface(window->object()))
+ : QtWayland::qt_extended_surface(window->display()->windowExtension()->get_extended_surface(window->wlSurface()))
, m_window(window)
{
}
diff --git a/src/client/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp
index c6f287dda..1d34f06cc 100644
--- a/src/client/qwaylandinputcontext.cpp
+++ b/src/client/qwaylandinputcontext.cpp
@@ -119,7 +119,7 @@ void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t fla
if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle())
return;
- struct ::wl_surface *surface = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle())->object();
+ auto *surface = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle())->wlSurface();
if (!surface || (surface != m_surface))
return;
@@ -428,7 +428,7 @@ static ::wl_surface *surfaceForWindow(QWindow *window)
return nullptr;
auto *waylandWindow = static_cast<QWaylandWindow *>(window->handle());
- return waylandWindow->wl_surface::object();
+ return waylandWindow->wlSurface();
}
void QWaylandInputContext::update(Qt::InputMethodQueries queries)
@@ -534,7 +534,7 @@ void QWaylandInputContext::setFocusObject(QObject *)
if (mCurrentWindow && mCurrentWindow->handle()) {
if (mCurrentWindow.data() != window || !inputMethodAccepted()) {
- struct ::wl_surface *surface = static_cast<QWaylandWindow *>(mCurrentWindow->handle())->object();
+ auto *surface = static_cast<QWaylandWindow *>(mCurrentWindow->handle())->wlSurface();
if (surface)
textInput()->disable(surface);
mCurrentWindow.clear();
@@ -543,7 +543,7 @@ void QWaylandInputContext::setFocusObject(QObject *)
if (window && window->handle() && inputMethodAccepted()) {
if (mCurrentWindow.data() != window) {
- struct ::wl_surface *surface = static_cast<QWaylandWindow *>(window->handle())->object();
+ auto *surface = static_cast<QWaylandWindow *>(window->handle())->wlSurface();
if (surface) {
textInput()->enable(surface);
mCurrentWindow = window;
diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp
index 39c02d962..8a580898d 100644
--- a/src/client/qwaylandinputdevice.cpp
+++ b/src/client/qwaylandinputdevice.cpp
@@ -41,11 +41,15 @@
#include "qwaylandintegration_p.h"
#include "qwaylandwindow_p.h"
+#include "qwaylandsurface_p.h"
#include "qwaylandbuffer_p.h"
#if QT_CONFIG(wayland_datadevice)
#include "qwaylanddatadevice_p.h"
#include "qwaylanddatadevicemanager_p.h"
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+#include "qwaylandprimaryselectionv1_p.h"
+#endif
#include "qwaylandtouch_p.h"
#include "qwaylandscreen_p.h"
#include "qwaylandcursor_p.h"
@@ -73,6 +77,8 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
+Q_LOGGING_CATEGORY(lcQpaWaylandInput, "qt.qpa.wayland.input");
+
QWaylandInputDevice::Keyboard::Keyboard(QWaylandInputDevice *p)
: mParent(p)
{
@@ -147,25 +153,45 @@ QWaylandInputDevice::Pointer::~Pointer()
wl_pointer_destroy(object());
}
+QWaylandWindow *QWaylandInputDevice::Pointer::focusWindow() const
+{
+ return mFocus ? mFocus->waylandWindow() : nullptr;
+}
+
#if QT_CONFIG(cursor)
-class CursorSurface : public QObject, public QtWayland::wl_surface
+class WlCallback : public QtWayland::wl_callback {
+public:
+ explicit WlCallback(::wl_callback *callback, std::function<void(uint32_t)> fn, bool autoDelete = false)
+ : QtWayland::wl_callback(callback)
+ , m_fn(fn)
+ , m_autoDelete(autoDelete)
+ {}
+ ~WlCallback() override { wl_callback_destroy(object()); }
+ bool done() const { return m_done; }
+ void callback_done(uint32_t callback_data) override {
+ m_done = true;
+ m_fn(callback_data);
+ if (m_autoDelete)
+ delete this;
+ }
+private:
+ bool m_done = false;
+ std::function<void(uint32_t)> m_fn;
+ bool m_autoDelete = false;
+};
+
+class CursorSurface : public QWaylandSurface
{
public:
explicit CursorSurface(QWaylandInputDevice::Pointer *pointer, QWaylandDisplay *display)
- : m_pointer(pointer)
+ : QWaylandSurface(display)
+ , m_pointer(pointer)
{
- init(display->createSurface(this));
//TODO: When we upgrade to libwayland 1.10, use wl_surface_get_version instead.
m_version = display->compositorVersion();
- connect(qApp, &QGuiApplication::screenRemoved, this, [this](QScreen *screen) {
- int oldScale = outputScale();
- if (!m_screens.removeOne(static_cast<QWaylandScreen *>(screen->handle())))
- return;
-
- if (outputScale() != oldScale)
- m_pointer->updateCursor();
- });
+ connect(this, &QWaylandSurface::screensChanged,
+ m_pointer, &QWaylandInputDevice::Pointer::updateCursor);
}
void hide()
@@ -177,7 +203,7 @@ public:
}
// Size and hotspot are in surface coordinates
- void update(wl_buffer *buffer, const QPoint &hotspot, const QSize &size, int bufferScale)
+ void update(wl_buffer *buffer, const QPoint &hotspot, const QSize &size, int bufferScale, bool animated = false)
{
// Calling code needs to ensure buffer scale is supported if != 1
Q_ASSERT(bufferScale == 1 || m_version >= 3);
@@ -194,6 +220,13 @@ public:
attach(buffer, 0, 0);
damage(0, 0, size.width(), size.height());
+ m_frameCallback.reset();
+ if (animated) {
+ m_frameCallback.reset(new WlCallback(frame(), [this](uint32_t time){
+ Q_UNUSED(time);
+ m_pointer->updateCursor();
+ }));
+ }
commit();
}
@@ -205,38 +238,12 @@ public:
return scale;
}
-protected:
- void surface_enter(struct ::wl_output *output) override
- {
- int oldScale = outputScale();
- auto *screen = QWaylandScreen::fromWlOutput(output);
- if (m_screens.contains(screen))
- return;
-
- m_screens.append(screen);
-
- if (outputScale() != oldScale)
- m_pointer->updateCursor();
- }
-
- void surface_leave(struct ::wl_output *output) override
- {
- int oldScale = outputScale();
- auto *screen = QWaylandScreen::fromWlOutput(output);
-
- if (!m_screens.removeOne(screen))
- return;
-
- if (outputScale() != oldScale)
- m_pointer->updateCursor();
- }
-
private:
+ QScopedPointer<WlCallback> m_frameCallback;
QWaylandInputDevice::Pointer *m_pointer = nullptr;
uint m_version = 0;
uint m_setSerial = 0;
QPoint m_hotspot;
- QVector<QWaylandScreen *> m_screens;
};
QString QWaylandInputDevice::Pointer::cursorThemeName() const
@@ -311,12 +318,14 @@ void QWaylandInputDevice::Pointer::updateCursor()
updateCursorTheme();
// Set from shape using theme
- if (struct ::wl_cursor_image *image = mCursor.theme->cursorImage(shape)) {
+ uint time = seat()->mCursor.animationTimer.elapsed();
+ if (struct ::wl_cursor_image *image = mCursor.theme->cursorImage(shape, time)) {
struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
int bufferScale = mCursor.themeBufferScale;
QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale;
QSize size = QSize(image->width, image->height) / bufferScale;
- getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale);
+ bool animated = image->delay > 0;
+ getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale, animated);
return;
}
@@ -346,10 +355,10 @@ QWaylandInputDevice::Touch::~Touch()
}
QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, uint32_t id)
- : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 4))
+ : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 5))
, mQDisplay(display)
, mDisplay(display->wl_display())
- , mVersion(qMin(version, 4))
+ , mVersion(qMin(version, 5))
{
#if QT_CONFIG(wayland_datadevice)
if (mQDisplay->dndSelectionHandler()) {
@@ -357,6 +366,12 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version,
}
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+ // TODO: Could probably decouple this more if there was a signal for new seat added
+ if (auto *psm = mQDisplay->primarySelectionManager())
+ setPrimarySelectionDevice(psm->createDevice(this));
+#endif
+
if (mQDisplay->textInputManager())
mTextInput.reset(new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())));
@@ -440,6 +455,18 @@ QWaylandDataDevice *QWaylandInputDevice::dataDevice() const
}
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+void QWaylandInputDevice::setPrimarySelectionDevice(QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice)
+{
+ mPrimarySelectionDevice.reset(primarySelectionDevice);
+}
+
+QWaylandPrimarySelectionDeviceV1 *QWaylandInputDevice::primarySelectionDevice() const
+{
+ return mPrimarySelectionDevice.data();
+}
+#endif
+
void QWaylandInputDevice::setTextInput(QWaylandTextInput *textInput)
{
mTextInput.reset(textInput);
@@ -458,7 +485,7 @@ void QWaylandInputDevice::removeMouseButtonFromState(Qt::MouseButton button)
QWaylandWindow *QWaylandInputDevice::pointerFocus() const
{
- return mPointer ? mPointer->mFocus : nullptr;
+ return mPointer ? mPointer->focusWindow() : nullptr;
}
QWaylandWindow *QWaylandInputDevice::keyboardFocus() const
@@ -517,6 +544,7 @@ void QWaylandInputDevice::setCursor(const QCursor *cursor, const QSharedPointer<
mCursor.shape = cursor ? cursor->shape() : Qt::ArrowCursor;
mCursor.hotspot = cursor ? cursor->hotSpot() : QPoint();
mCursor.fallbackOutputScale = fallbackOutputScale;
+ mCursor.animationTimer.start();
if (mCursor.shape == Qt::BitmapCursor) {
mCursor.bitmapBuffer = cachedBuffer ? cachedBuffer : QWaylandCursor::cursorBitmapBuffer(mQDisplay, cursor);
@@ -543,8 +571,9 @@ void QWaylandInputDevice::setCursor(const QCursor *cursor, const QSharedPointer<
class EnterEvent : public QWaylandPointerEvent
{
public:
- EnterEvent(const QPointF &l, const QPointF &g)
- : QWaylandPointerEvent(QWaylandPointerEvent::Enter, 0, l, g, nullptr, Qt::NoModifier)
+ EnterEvent(QWaylandWindow *surface, const QPointF &local, const QPointF &global)
+ : QWaylandPointerEvent(QWaylandPointerEvent::Enter, Qt::NoScrollPhase, surface, 0,
+ local, global, nullptr, Qt::NoModifier)
{}
};
@@ -556,14 +585,17 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
+ if (!window)
+ return; // Ignore foreign surfaces
+
if (mFocus) {
qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"
<< "leave event first, this is not allowed by the wayland protocol"
<< "attempting to work around it by invalidating the current focus";
invalidateFocus();
}
- mFocus = window;
- connect(mFocus, &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed);
+ mFocus = window->waylandSurface();
+ connect(mFocus, &QObject::destroyed, this, &Pointer::handleFocusDestroyed);
mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy));
mGlobalPos = window->window()->mapToGlobal(mSurfacePos.toPoint());
@@ -577,12 +609,19 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
#endif
QWaylandWindow *grab = QWaylandWindow::mouseGrab();
- if (!grab) {
- EnterEvent evt(mSurfacePos, mGlobalPos);
- window->handleMouse(mParent, evt);
- }
+ if (!grab)
+ setFrameEvent(new EnterEvent(window, mSurfacePos, mGlobalPos));
}
+class LeaveEvent : public QWaylandPointerEvent
+{
+public:
+ LeaveEvent(QWaylandWindow *surface, const QPointF &localPos, const QPointF &globalPos)
+ : QWaylandPointerEvent(QWaylandPointerEvent::Leave, Qt::NoScrollPhase, surface, 0,
+ localPos, globalPos, nullptr, Qt::NoModifier)
+ {}
+};
+
void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surface *surface)
{
// The event may arrive after destroying the window, indicated by
@@ -590,10 +629,12 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac
if (!surface)
return;
- if (!QWaylandWindow::mouseGrab()) {
- QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
- window->handleMouseLeave(mParent);
- }
+ auto *window = QWaylandWindow::fromWlSurface(surface);
+ if (!window)
+ return; // Ignore foreign surfaces
+
+ if (!QWaylandWindow::mouseGrab())
+ setFrameEvent(new LeaveEvent(window, mSurfacePos, mGlobalPos));
invalidateFocus();
mButtons = Qt::NoButton;
@@ -604,15 +645,17 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac
class MotionEvent : public QWaylandPointerEvent
{
public:
- MotionEvent(ulong t, const QPointF &l, const QPointF &g, Qt::MouseButtons b, Qt::KeyboardModifiers m)
- : QWaylandPointerEvent(QWaylandPointerEvent::Motion, t, l, g, b, m)
+ MotionEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
+ const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+ : QWaylandPointerEvent(QWaylandPointerEvent::Motion, Qt::NoScrollPhase, surface,
+ timestamp, localPos, globalPos, buttons, modifiers)
{
}
};
void QWaylandInputDevice::Pointer::pointer_motion(uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
{
- QWaylandWindow *window = mFocus;
+ QWaylandWindow *window = focusWindow();
if (!window) {
// We destroyed the pointer focus surface, but the server didn't get the message yet...
// or the server didn't send an enter event first. In either case, ignore the event.
@@ -634,18 +677,37 @@ void QWaylandInputDevice::Pointer::pointer_motion(uint32_t time, wl_fixed_t surf
// so we just set it outside of the window boundaries.
pos = QPointF(-1, -1);
global = grab->window()->mapToGlobal(pos.toPoint());
- MotionEvent e(time, pos, global, mButtons, mParent->modifiers());
- grab->handleMouse(mParent, e);
- } else {
- MotionEvent e(time, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers());
- window->handleMouse(mParent, e);
+ window = grab;
}
+ setFrameEvent(new MotionEvent(window, time, pos, global, mButtons, mParent->modifiers()));
}
+class PressEvent : public QWaylandPointerEvent
+{
+public:
+ PressEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
+ const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+ : QWaylandPointerEvent(QWaylandPointerEvent::Press, Qt::NoScrollPhase, surface,
+ timestamp, localPos, globalPos, buttons, modifiers)
+ {
+ }
+};
+
+class ReleaseEvent : public QWaylandPointerEvent
+{
+public:
+ ReleaseEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
+ const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+ : QWaylandPointerEvent(QWaylandPointerEvent::Release, Qt::NoScrollPhase, surface,
+ timestamp, localPos, globalPos, buttons, modifiers)
+ {
+ }
+};
+
void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time,
uint32_t button, uint32_t state)
{
- QWaylandWindow *window = mFocus;
+ QWaylandWindow *window = focusWindow();
if (!window) {
// We destroyed the pointer focus surface, but the server didn't get the message yet...
// or the server didn't send an enter event first. In either case, ignore the event.
@@ -687,20 +749,25 @@ void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time
mParent->mQDisplay->setLastInputDevice(mParent, serial, window);
QWaylandWindow *grab = QWaylandWindow::mouseGrab();
- if (grab && grab != mFocus) {
- QPointF pos = QPointF(-1, -1);
- QPointF global = grab->window()->mapToGlobal(pos.toPoint());
- MotionEvent e(time, pos, global, mButtons, mParent->modifiers());
- grab->handleMouse(mParent, e);
- } else if (window) {
- MotionEvent e(time, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers());
- window->handleMouse(mParent, e);
+
+ QPointF pos = mSurfacePos;
+ QPointF global = mGlobalPos;
+ if (grab && grab != focusWindow()) {
+ pos = QPointF(-1, -1);
+ global = grab->window()->mapToGlobal(pos.toPoint());
+
+ window = grab;
}
+
+ if (state)
+ setFrameEvent(new PressEvent(window, time, pos, global, mButtons, mParent->modifiers()));
+ else
+ setFrameEvent(new ReleaseEvent(window, time, pos, global, mButtons, mParent->modifiers()));
}
void QWaylandInputDevice::Pointer::invalidateFocus()
{
- disconnect(mFocus, &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed);
+ disconnect(mFocus, &QObject::destroyed, this, &Pointer::handleFocusDestroyed);
mFocus = nullptr;
mEnterSerial = 0;
}
@@ -708,45 +775,265 @@ void QWaylandInputDevice::Pointer::invalidateFocus()
void QWaylandInputDevice::Pointer::releaseButtons()
{
mButtons = Qt::NoButton;
- MotionEvent e(mParent->mTime, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers());
- if (mFocus)
- mFocus->handleMouse(mParent, e);
+
+ if (auto *window = focusWindow()) {
+ MotionEvent e(focusWindow(), mParent->mTime, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers());
+ window->handleMouse(mParent, e);
+ }
}
class WheelEvent : public QWaylandPointerEvent
{
public:
- WheelEvent(ulong t, const QPointF &l, const QPointF &g, const QPoint &pd, const QPoint &ad, Qt::KeyboardModifiers m)
- : QWaylandPointerEvent(QWaylandPointerEvent::Wheel, t, l, g, pd, ad, m)
+ WheelEvent(QWaylandWindow *surface, Qt::ScrollPhase phase, ulong timestamp, const QPointF &local,
+ const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta,
+ Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers)
+ : QWaylandPointerEvent(QWaylandPointerEvent::Wheel, phase, surface, timestamp,
+ local, global, pixelDelta, angleDelta, source, modifiers)
{
}
};
void QWaylandInputDevice::Pointer::pointer_axis(uint32_t time, uint32_t axis, int32_t value)
{
- QWaylandWindow *window = mFocus;
- if (!window) {
+ if (!focusWindow()) {
// We destroyed the pointer focus surface, but the server didn't get the message yet...
// or the server didn't send an enter event first. In either case, ignore the event.
return;
}
- QPoint pixelDelta;
- QPoint angleDelta;
+ // Get the delta and convert it into the expected range
+ switch (axis) {
+ case WL_POINTER_AXIS_VERTICAL_SCROLL:
+ mFrameData.delta.ry() += wl_fixed_to_double(value);
+ qCDebug(lcQpaWaylandInput) << "wl_pointer.axis vertical:" << mFrameData.delta.y();
+ break;
+ case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
+ mFrameData.delta.rx() += wl_fixed_to_double(value);
+ qCDebug(lcQpaWaylandInput) << "wl_pointer.axis horizontal:" << mFrameData.delta.x();
+ break;
+ default:
+ //TODO: is this really needed?
+ qCWarning(lcQpaWaylandInput) << "wl_pointer.axis: Unknown axis:" << axis;
+ return;
+ }
- //normalize value and inverse axis
- int valueDelta = wl_fixed_to_int(value) * -12;
+ mParent->mTime = time;
- if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
- pixelDelta = QPoint();
- angleDelta.setX(valueDelta);
- } else {
- pixelDelta = QPoint();
- angleDelta.setY(valueDelta);
+ if (mParent->mVersion < WL_POINTER_FRAME_SINCE_VERSION) {
+ qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
+ flushFrameEvent();
+ }
+}
+
+void QWaylandInputDevice::Pointer::pointer_frame()
+{
+ flushFrameEvent();
+}
+
+void QWaylandInputDevice::Pointer::pointer_axis_source(uint32_t source)
+{
+ switch (source) {
+ case axis_source_wheel:
+ qCDebug(lcQpaWaylandInput) << "Axis source wheel";
+ break;
+ case axis_source_finger:
+ qCDebug(lcQpaWaylandInput) << "Axis source finger";
+ break;
+ case axis_source_continuous:
+ qCDebug(lcQpaWaylandInput) << "Axis source continuous";
+ break;
+ }
+ mFrameData.axisSource = axis_source(source);
+}
+
+void QWaylandInputDevice::Pointer::pointer_axis_stop(uint32_t time, uint32_t axis)
+{
+ if (!focusWindow())
+ return;
+
+ mParent->mTime = time;
+ switch (axis) {
+ case axis_vertical_scroll:
+ qCDebug(lcQpaWaylandInput) << "Received vertical wl_pointer.axis_stop";
+ mFrameData.delta.setY(0); //TODO: what's the point of doing this?
+ break;
+ case axis_horizontal_scroll:
+ qCDebug(lcQpaWaylandInput) << "Received horizontal wl_pointer.axis_stop";
+ mFrameData.delta.setX(0);
+ break;
+ default:
+ qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_stop: Unknown axis: " << axis
+ << "This is most likely a compositor bug";
+ return;
+ }
+
+ // May receive axis_stop for events we haven't sent a ScrollBegin for because
+ // most axis_sources do not mandate an axis_stop event to be sent.
+ if (!mScrollBeginSent) {
+ // TODO: For now, we just ignore these events, but we could perhaps take this as an
+ // indication that this compositor will in fact send axis_stop events for these sources
+ // and send a ScrollBegin the next time an axis_source event with this type is encountered.
+ return;
+ }
+
+ QWaylandWindow *target = QWaylandWindow::mouseGrab();
+ if (!target)
+ target = focusWindow();
+ Qt::KeyboardModifiers mods = mParent->modifiers();
+ WheelEvent wheelEvent(focusWindow(), Qt::ScrollEnd, mParent->mTime, mSurfacePos, mGlobalPos,
+ QPoint(), QPoint(), Qt::MouseEventNotSynthesized, mods);
+ target->handleMouse(mParent, wheelEvent);
+ mScrollBeginSent = false;
+ mScrollDeltaRemainder = QPointF();
+}
+
+void QWaylandInputDevice::Pointer::pointer_axis_discrete(uint32_t axis, int32_t value)
+{
+ if (!focusWindow())
+ return;
+
+ switch (axis) {
+ case axis_vertical_scroll:
+ qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete vertical:" << value;
+ mFrameData.discreteDelta.ry() += value;
+ break;
+ case axis_horizontal_scroll:
+ qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete horizontal:" << value;
+ mFrameData.discreteDelta.rx() += value;
+ break;
+ default:
+ //TODO: is this really needed?
+ qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_discrete: Unknown axis:" << axis;
+ return;
+ }
+}
+
+void QWaylandInputDevice::Pointer::setFrameEvent(QWaylandPointerEvent *event)
+{
+ qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type;
+ if (mFrameData.event && mFrameData.event->type != event->type) {
+ qCDebug(lcQpaWaylandInput) << "Flushing; previous was " << mFrameData.event->type;
+ flushFrameEvent();
+ }
+
+ mFrameData.event = event;
+
+ if (mParent->mVersion < WL_POINTER_FRAME_SINCE_VERSION) {
+ qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
+ flushFrameEvent();
+ }
+}
+
+void QWaylandInputDevice::Pointer::FrameData::resetScrollData()
+{
+ discreteDelta = QPoint();
+ delta = QPointF();
+ axisSource = axis_source_wheel;
+}
+
+bool QWaylandInputDevice::Pointer::FrameData::hasPixelDelta() const
+{
+ switch (axisSource) {
+ case axis_source_wheel_tilt: // sideways tilt of the wheel
+ case axis_source_wheel:
+ // In the case of wheel events, a pixel delta doesn't really make sense,
+ // and will make Qt think this is a continuous scroll event when it isn't,
+ // so just ignore it.
+ return false;
+ case axis_source_finger:
+ case axis_source_continuous:
+ return !delta.isNull();
+ }
+}
+
+QPoint QWaylandInputDevice::Pointer::FrameData::pixelDeltaAndError(QPointF *accumulatedError) const
+{
+ if (!hasPixelDelta())
+ return QPoint();
+
+ Q_ASSERT(accumulatedError);
+ // Add accumulated rounding error before rounding again
+ QPoint pixelDelta = (delta + *accumulatedError).toPoint();
+ *accumulatedError += delta - pixelDelta;
+ Q_ASSERT(qAbs(accumulatedError->x()) < 1.0);
+ Q_ASSERT(qAbs(accumulatedError->y()) < 1.0);
+ return pixelDelta;
+}
+
+QPoint QWaylandInputDevice::Pointer::FrameData::angleDelta() const
+{
+ if (discreteDelta.isNull()) {
+ // If we didn't get any discrete events, then we need to fall back to
+ // the continuous information.
+ return (delta * -12).toPoint(); //TODO: why multiply by 12?
+ }
+
+ // The angle delta is in eights of degrees, and our docs says most mice have
+ // 1 click = 15 degrees. It's also in the opposite direction of surface space.
+ return -discreteDelta * 15 * 8;
+}
+
+Qt::MouseEventSource QWaylandInputDevice::Pointer::FrameData::wheelEventSource() const
+{
+ switch (axisSource) {
+ case axis_source_wheel_tilt: // sideways tilt of the wheel
+ case axis_source_wheel:
+ return Qt::MouseEventNotSynthesized;
+ case axis_source_finger:
+ case axis_source_continuous:
+ default: // Whatever other sources might be added are probably not mouse wheels
+ return Qt::MouseEventSynthesizedBySystem;
}
+}
+
+void QWaylandInputDevice::Pointer::flushScrollEvent()
+{
+ QPoint angleDelta = mFrameData.angleDelta();
+
+ // Angle delta is required for Qt wheel events, so don't try to send events if it's zero
+ if (!angleDelta.isNull()) {
+ QWaylandWindow *target = QWaylandWindow::mouseGrab();
+ if (!target)
+ target = focusWindow();
+
+ if (isDefinitelyTerminated(mFrameData.axisSource) && !mScrollBeginSent) {
+ qCDebug(lcQpaWaylandInput) << "Flushing scroll event sending ScrollBegin";
+ target->handleMouse(mParent, WheelEvent(focusWindow(), Qt::ScrollBegin, mParent->mTime,
+ mSurfacePos, mGlobalPos, QPoint(), QPoint(),
+ Qt::MouseEventNotSynthesized,
+ mParent->modifiers()));
+ mScrollBeginSent = true;
+ mScrollDeltaRemainder = QPointF();
+ }
+
+ Qt::ScrollPhase phase = mScrollBeginSent ? Qt::ScrollUpdate : Qt::NoScrollPhase;
+ QPoint pixelDelta = mFrameData.pixelDeltaAndError(&mScrollDeltaRemainder);
+ Qt::MouseEventSource source = mFrameData.wheelEventSource();
+
+ qCDebug(lcQpaWaylandInput) << "Flushing scroll event" << phase << pixelDelta << angleDelta;
+ target->handleMouse(mParent, WheelEvent(focusWindow(), phase, mParent->mTime, mSurfacePos, mGlobalPos,
+ pixelDelta, angleDelta, source, mParent->modifiers()));
+ }
+
+ mFrameData.resetScrollData();
+}
- WheelEvent e(time, mSurfacePos, mGlobalPos, pixelDelta, angleDelta, mParent->modifiers());
- window->handleMouse(mParent, e);
+void QWaylandInputDevice::Pointer::flushFrameEvent()
+{
+ if (mFrameData.event) {
+ mFrameData.event->surface->handleMouse(mParent, *mFrameData.event);
+ delete mFrameData.event;
+ mFrameData.event = nullptr;
+ }
+
+ //TODO: do modifiers get passed correctly here?
+ flushScrollEvent();
+}
+
+bool QWaylandInputDevice::Pointer::isDefinitelyTerminated(QtWayland::wl_pointer::axis_source source) const
+{
+ return source == axis_source_finger;
}
void QWaylandInputDevice::Keyboard::keyboard_keymap(uint32_t format, int32_t fd, uint32_t size)
@@ -913,7 +1200,7 @@ void QWaylandInputDevice::Keyboard::handleFocusDestroyed()
// surface, so we still need to disconnect the signal
auto *window = qobject_cast<QWaylandWindow *>(sender());
disconnect(window, &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed);
- Q_ASSERT(window->object() == mFocus);
+ Q_ASSERT(window->wlSurface() == mFocus);
handleFocusLost();
}
@@ -924,6 +1211,10 @@ void QWaylandInputDevice::Keyboard::handleFocusLost()
if (auto *dataDevice = mParent->dataDevice())
dataDevice->invalidateSelectionOffer();
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+ if (auto *device = mParent->primarySelectionDevice())
+ device->invalidateSelectionOffer();
+#endif
mParent->mQDisplay->handleKeyboardFocusChanged(mParent);
mRepeatTimer.stop();
}
@@ -965,9 +1256,13 @@ void QWaylandInputDevice::Touch::touch_down(uint32_t serial,
if (!surface)
return;
+ auto *window = QWaylandWindow::fromWlSurface(surface);
+ if (!window)
+ return; // Ignore foreign surfaces
+
mParent->mTime = time;
mParent->mSerial = serial;
- mFocus = QWaylandWindow::fromWlSurface(surface);
+ mFocus = window;
mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus);
mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointPressed);
}
@@ -1026,7 +1321,7 @@ void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::Touch
//is it possible that mTouchFocus is null;
if (!win && mPointer)
- win = mPointer->mFocus;
+ win = mPointer->focusWindow();
if (!win && mKeyboard)
win = mKeyboard->focusWindow();
if (!win || !win->window())
diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h
index 39ca9dca5..06ba5d566 100644
--- a/src/client/qwaylandinputdevice_p.h
+++ b/src/client/qwaylandinputdevice_p.h
@@ -69,7 +69,8 @@
#endif
#include <QtCore/QDebug>
-#include <QPointer>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QPointer>
#if QT_CONFIG(cursor)
struct wl_cursor_image;
@@ -77,11 +78,17 @@ struct wl_cursor_image;
QT_BEGIN_NAMESPACE
+namespace QtWayland {
+class zwp_primary_selection_device_v1;
+} //namespace QtWayland
+
namespace QtWaylandClient {
-class QWaylandWindow;
-class QWaylandDisplay;
class QWaylandDataDevice;
+class QWaylandDisplay;
+#if QT_CONFIG(wayland_client_primary_selection)
+class QWaylandPrimarySelectionDeviceV1;
+#endif
class QWaylandTextInput;
#if QT_CONFIG(cursor)
class QWaylandCursorTheme;
@@ -115,6 +122,11 @@ public:
QWaylandDataDevice *dataDevice() const;
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+ void setPrimarySelectionDevice(QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice);
+ QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice() const;
+#endif
+
void setTextInput(QWaylandTextInput *textInput);
QWaylandTextInput *textInput() const;
@@ -150,6 +162,7 @@ private:
Qt::CursorShape shape = Qt::ArrowCursor;
int fallbackOutputScale = 1;
QPoint hotspot;
+ QElapsedTimer animationTimer;
} mCursor;
#endif
@@ -157,6 +170,10 @@ private:
QWaylandDataDevice *mDataDevice = nullptr;
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+ QScopedPointer<QWaylandPrimarySelectionDeviceV1> mPrimarySelectionDevice;
+#endif
+
Keyboard *mKeyboard = nullptr;
Pointer *mPointer = nullptr;
Touch *mTouch = nullptr;
@@ -256,6 +273,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Pointer : public QObject, pub
public:
explicit Pointer(QWaylandInputDevice *seat);
~Pointer() override;
+ QWaylandWindow *focusWindow() const;
#if QT_CONFIG(cursor)
QString cursorThemeName() const;
int cursorSize() const; // in surface coordinates
@@ -277,6 +295,10 @@ protected:
void pointer_axis(uint32_t time,
uint32_t axis,
wl_fixed_t value) override;
+ void pointer_axis_source(uint32_t source) override;
+ void pointer_axis_stop(uint32_t time, uint32_t axis) override;
+ void pointer_axis_discrete(uint32_t axis, int32_t value) override;
+ void pointer_frame() override;
private slots:
void handleFocusDestroyed() { invalidateFocus(); }
@@ -288,7 +310,7 @@ public:
void releaseButtons();
QWaylandInputDevice *mParent = nullptr;
- QPointer<QWaylandWindow> mFocus;
+ QPointer<QWaylandSurface> mFocus;
uint32_t mEnterSerial = 0;
#if QT_CONFIG(cursor)
struct {
@@ -304,6 +326,30 @@ public:
wl_buffer *mCursorBuffer = nullptr;
Qt::CursorShape mCursorShape = Qt::BitmapCursor;
#endif
+
+ struct FrameData {
+ QWaylandPointerEvent *event = nullptr;
+
+ QPointF delta;
+ QPoint discreteDelta;
+ axis_source axisSource = axis_source_wheel;
+
+ void resetScrollData();
+ bool hasPixelDelta() const;
+ QPoint pixelDeltaAndError(QPointF *accumulatedError) const;
+ QPoint pixelDelta() const { return hasPixelDelta() ? delta.toPoint() : QPoint(); }
+ QPoint angleDelta() const;
+ Qt::MouseEventSource wheelEventSource() const;
+ } mFrameData;
+
+ bool mScrollBeginSent = false;
+ QPointF mScrollDeltaRemainder;
+
+ void setFrameEvent(QWaylandPointerEvent *event);
+ void flushScrollEvent();
+ void flushFrameEvent();
+private: //TODO: should other methods be private as well?
+ bool isDefinitelyTerminated(axis_source source) const;
};
class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Touch : public QtWayland::wl_touch
@@ -339,38 +385,58 @@ public:
class QWaylandPointerEvent
{
+ Q_GADGET
public:
enum Type {
Enter,
+ Leave,
Motion,
+ Press,
+ Release,
Wheel
};
- inline QWaylandPointerEvent(Type t, ulong ts, const QPointF &l, const QPointF &g, Qt::MouseButtons b, Qt::KeyboardModifiers m)
- : type(t)
- , timestamp(ts)
- , local(l)
- , global(g)
- , buttons(b)
- , modifiers(m)
+ Q_ENUM(Type)
+
+ inline QWaylandPointerEvent(Type type, Qt::ScrollPhase phase, QWaylandWindow *surface,
+ ulong timestamp, const QPointF &localPos, const QPointF &globalPos,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+ : type(type)
+ , phase(phase)
+ , timestamp(timestamp)
+ , local(localPos)
+ , global(globalPos)
+ , buttons(buttons)
+ , modifiers(modifiers)
+ , surface(surface)
{}
- inline QWaylandPointerEvent(Type t, ulong ts, const QPointF &l, const QPointF &g, const QPoint &pd, const QPoint &ad, Qt::KeyboardModifiers m)
- : type(t)
- , timestamp(ts)
- , local(l)
- , global(g)
- , modifiers(m)
- , pixelDelta(pd)
- , angleDelta(ad)
+ inline QWaylandPointerEvent(Type type, Qt::ScrollPhase phase, QWaylandWindow *surface,
+ ulong timestamp, const QPointF &local, const QPointF &global,
+ const QPoint &pixelDelta, const QPoint &angleDelta,
+ Qt::MouseEventSource source,
+ Qt::KeyboardModifiers modifiers)
+ : type(type)
+ , phase(phase)
+ , timestamp(timestamp)
+ , local(local)
+ , global(global)
+ , modifiers(modifiers)
+ , pixelDelta(pixelDelta)
+ , angleDelta(angleDelta)
+ , source(source)
+ , surface(surface)
{}
Type type;
- ulong timestamp;
+ Qt::ScrollPhase phase = Qt::NoScrollPhase;
+ ulong timestamp = 0;
QPointF local;
QPointF global;
Qt::MouseButtons buttons;
Qt::KeyboardModifiers modifiers;
QPoint pixelDelta;
QPoint angleDelta;
+ Qt::MouseEventSource source = Qt::MouseEventNotSynthesized;
+ QWaylandWindow *surface = nullptr;
};
}
diff --git a/src/client/qwaylandnativeinterface.cpp b/src/client/qwaylandnativeinterface.cpp
index 76acb526b..cf227d489 100644
--- a/src/client/qwaylandnativeinterface.cpp
+++ b/src/client/qwaylandnativeinterface.cpp
@@ -89,7 +89,7 @@ void *QWaylandNativeInterface::nativeResourceForWindow(const QByteArray &resourc
return const_cast<wl_compositor *>(m_integration->display()->wl_compositor());
if (lowerCaseResource == "surface") {
QWaylandWindow *w = static_cast<QWaylandWindow*>(window->handle());
- return w ? w->object() : nullptr;
+ return w ? w->wlSurface() : nullptr;
}
if (lowerCaseResource == "egldisplay" && m_integration->clientBufferIntegration())
diff --git a/src/client/qwaylandprimaryselectionv1.cpp b/src/client/qwaylandprimaryselectionv1.cpp
new file mode 100644
index 000000000..3ddf6dac3
--- /dev/null
+++ b/src/client/qwaylandprimaryselectionv1.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qwaylandprimaryselectionv1_p.h"
+#include "qwaylandinputdevice_p.h"
+#include "qwaylanddisplay_p.h"
+#include "qwaylandmimehelper_p.h"
+
+#include <QtGui/private/qguiapplication_p.h>
+
+#include <qpa/qplatformclipboard.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+QWaylandPrimarySelectionDeviceManagerV1::QWaylandPrimarySelectionDeviceManagerV1(QWaylandDisplay *display, uint id, uint version)
+ : zwp_primary_selection_device_manager_v1(display->wl_registry(), id, qMin(version, uint(1)))
+ , m_display(display)
+{
+ // Create devices for all seats.
+ // This only works if we get the global before all devices
+ const auto seats = m_display->inputDevices();
+ for (auto *seat : seats)
+ seat->setPrimarySelectionDevice(createDevice(seat));
+}
+
+QWaylandPrimarySelectionDeviceV1 *QWaylandPrimarySelectionDeviceManagerV1::createDevice(QWaylandInputDevice *seat)
+{
+ return new QWaylandPrimarySelectionDeviceV1(this, seat);
+}
+
+QWaylandPrimarySelectionOfferV1::QWaylandPrimarySelectionOfferV1(QWaylandDisplay *display, ::zwp_primary_selection_offer_v1 *offer)
+ : zwp_primary_selection_offer_v1(offer)
+ , m_display(display)
+ , m_mimeData(new QWaylandMimeData(this))
+{}
+
+void QWaylandPrimarySelectionOfferV1::startReceiving(const QString &mimeType, int fd)
+{
+ receive(mimeType, fd);
+ wl_display_flush(m_display->wl_display());
+}
+
+void QWaylandPrimarySelectionOfferV1::zwp_primary_selection_offer_v1_offer(const QString &mime_type)
+{
+ m_mimeData->appendFormat(mime_type);
+}
+
+QWaylandPrimarySelectionDeviceV1::QWaylandPrimarySelectionDeviceV1(
+ QWaylandPrimarySelectionDeviceManagerV1 *manager, QWaylandInputDevice *seat)
+ : QtWayland::zwp_primary_selection_device_v1(manager->get_device(seat->wl_seat()))
+ , m_display(manager->display())
+ , m_seat(seat)
+{
+}
+
+QWaylandPrimarySelectionDeviceV1::~QWaylandPrimarySelectionDeviceV1()
+{
+ destroy();
+}
+
+void QWaylandPrimarySelectionDeviceV1::setSelectionSource(QWaylandPrimarySelectionSourceV1 *source)
+{
+ if (source) {
+ connect(source, &QWaylandPrimarySelectionSourceV1::cancelled, this, [this]() {
+ m_selectionSource.reset();
+ QGuiApplicationPrivate::platformIntegration()->clipboard()->emitChanged(QClipboard::Selection);
+ });
+ }
+ set_selection(source ? source->object() : nullptr, m_seat->serial());
+ m_selectionSource.reset(source);
+}
+
+void QWaylandPrimarySelectionDeviceV1::zwp_primary_selection_device_v1_data_offer(zwp_primary_selection_offer_v1 *offer)
+{
+ new QWaylandPrimarySelectionOfferV1(m_display, offer);
+}
+
+void QWaylandPrimarySelectionDeviceV1::zwp_primary_selection_device_v1_selection(zwp_primary_selection_offer_v1 *id)
+{
+
+ if (id)
+ m_selectionOffer.reset(static_cast<QWaylandPrimarySelectionOfferV1 *>(zwp_primary_selection_offer_v1_get_user_data(id)));
+ else
+ m_selectionOffer.reset();
+
+ QGuiApplicationPrivate::platformIntegration()->clipboard()->emitChanged(QClipboard::Selection);
+}
+
+QWaylandPrimarySelectionSourceV1::QWaylandPrimarySelectionSourceV1(QWaylandPrimarySelectionDeviceManagerV1 *manager, QMimeData *mimeData)
+ : QtWayland::zwp_primary_selection_source_v1(manager->create_source())
+ , m_mimeData(mimeData)
+{
+ if (!mimeData)
+ return;
+ for (auto &format : mimeData->formats())
+ offer(format);
+}
+
+QWaylandPrimarySelectionSourceV1::~QWaylandPrimarySelectionSourceV1()
+{
+ destroy();
+}
+
+void QWaylandPrimarySelectionSourceV1::zwp_primary_selection_source_v1_send(const QString &mime_type, int32_t fd)
+{
+ QByteArray content = QWaylandMimeHelper::getByteArray(m_mimeData, mime_type);
+ if (!content.isEmpty()) {
+ // Create a sigpipe handler that does nothing, or clients may be forced to terminate
+ // if the pipe is closed in the other end.
+ struct sigaction action, oldAction;
+ action.sa_handler = SIG_IGN;
+ sigemptyset (&action.sa_mask);
+ action.sa_flags = 0;
+
+ sigaction(SIGPIPE, &action, &oldAction);
+ write(fd, content.constData(), size_t(content.size()));
+ sigaction(SIGPIPE, &oldAction, nullptr);
+ }
+ close(fd);
+}
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
diff --git a/src/client/qwaylandprimaryselectionv1_p.h b/src/client/qwaylandprimaryselectionv1_p.h
new file mode 100644
index 000000000..b165c51b8
--- /dev/null
+++ b/src/client/qwaylandprimaryselectionv1_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QWAYLANDPRIMARYSELECTIONV1_P_H
+#define QWAYLANDPRIMARYSELECTIONV1_P_H
+
+//
+// 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.
+//
+
+#include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h>
+
+#include <QtWaylandClient/private/qtwaylandclientglobal_p.h>
+#include <QtWaylandClient/private/qwaylanddataoffer_p.h>
+
+#include <QtCore/QObject>
+
+QT_REQUIRE_CONFIG(wayland_client_primary_selection);
+
+QT_BEGIN_NAMESPACE
+
+class QMimeData;
+
+namespace QtWaylandClient {
+
+class QWaylandInputDevice;
+class QWaylandPrimarySelectionDeviceV1;
+
+class QWaylandPrimarySelectionDeviceManagerV1 : public QtWayland::zwp_primary_selection_device_manager_v1
+{
+public:
+ explicit QWaylandPrimarySelectionDeviceManagerV1(QWaylandDisplay *display, uint id, uint version);
+ QWaylandPrimarySelectionDeviceV1 *createDevice(QWaylandInputDevice *seat);
+ QWaylandDisplay *display() const { return m_display; }
+
+private:
+ QWaylandDisplay *m_display = nullptr;
+};
+
+class QWaylandPrimarySelectionOfferV1 : public QtWayland::zwp_primary_selection_offer_v1, public QWaylandAbstractDataOffer
+{
+public:
+ explicit QWaylandPrimarySelectionOfferV1(QWaylandDisplay *display, ::zwp_primary_selection_offer_v1 *offer);
+ ~QWaylandPrimarySelectionOfferV1() override { destroy(); }
+ void startReceiving(const QString &mimeType, int fd) override;
+ QMimeData *mimeData() override { return m_mimeData.data(); }
+
+protected:
+ void zwp_primary_selection_offer_v1_offer(const QString &mime_type) override;
+
+private:
+ QWaylandDisplay *m_display = nullptr;
+ QScopedPointer<QWaylandMimeData> m_mimeData;
+};
+
+class Q_WAYLAND_CLIENT_EXPORT QWaylandPrimarySelectionSourceV1 : public QObject, public QtWayland::zwp_primary_selection_source_v1
+{
+ Q_OBJECT
+public:
+ explicit QWaylandPrimarySelectionSourceV1(QWaylandPrimarySelectionDeviceManagerV1 *manager, QMimeData *mimeData);
+ ~QWaylandPrimarySelectionSourceV1() override;
+
+ QMimeData *mimeData() const { return m_mimeData; }
+
+signals:
+ void cancelled();
+
+protected:
+ void zwp_primary_selection_source_v1_send(const QString &mime_type, int32_t fd) override;
+ void zwp_primary_selection_source_v1_cancelled() override { emit cancelled(); }
+
+private:
+ QWaylandDisplay *m_display = nullptr;
+ QMimeData *m_mimeData = nullptr;
+};
+
+class QWaylandPrimarySelectionDeviceV1 : public QObject, public QtWayland::zwp_primary_selection_device_v1
+{
+ Q_OBJECT
+ QWaylandPrimarySelectionDeviceV1(QWaylandPrimarySelectionDeviceManagerV1 *manager, QWaylandInputDevice *seat);
+
+public:
+ ~QWaylandPrimarySelectionDeviceV1() override;
+ QWaylandPrimarySelectionOfferV1 *selectionOffer() const { return m_selectionOffer.data(); }
+ void invalidateSelectionOffer() { m_selectionOffer.reset(); }
+ QWaylandPrimarySelectionSourceV1 *selectionSource() const { return m_selectionSource.data(); }
+ void setSelectionSource(QWaylandPrimarySelectionSourceV1 *source);
+
+protected:
+ void zwp_primary_selection_device_v1_data_offer(struct ::zwp_primary_selection_offer_v1 *offer) override;
+ void zwp_primary_selection_device_v1_selection(struct ::zwp_primary_selection_offer_v1 *id) override;
+
+private:
+ QWaylandDisplay *m_display = nullptr;
+ QWaylandInputDevice *m_seat = nullptr;
+ QScopedPointer<QWaylandPrimarySelectionOfferV1> m_selectionOffer;
+ QScopedPointer<QWaylandPrimarySelectionSourceV1> m_selectionSource;
+ friend class QWaylandPrimarySelectionDeviceManagerV1;
+};
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDPRIMARYSELECTIONV1_P_H
diff --git a/src/client/qwaylandqtkey.cpp b/src/client/qwaylandqtkey.cpp
index a60185bd6..192619738 100644
--- a/src/client/qwaylandqtkey.cpp
+++ b/src/client/qwaylandqtkey.cpp
@@ -70,7 +70,11 @@ void QWaylandQtKeyExtension::zqt_key_v1_key(struct wl_surface *surface,
}
QWaylandInputDevice *dev = inputDevices.first();
- QWaylandWindow *win = surface ? QWaylandWindow::fromWlSurface(surface) : dev->keyboardFocus();
+
+ auto *win = surface ? QWaylandWindow::fromWlSurface(surface) : nullptr;
+
+ if (!win)
+ win = dev->keyboardFocus();
if (!win || !win->window()) {
qWarning("qt_key_extension: handle_qtkey: No keyboard focus");
diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp
index 5b04ae609..d116a807b 100644
--- a/src/client/qwaylandscreen.cpp
+++ b/src/client/qwaylandscreen.cpp
@@ -217,8 +217,9 @@ QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window)
QWaylandScreen *QWaylandScreen::fromWlOutput(::wl_output *output)
{
- auto wlOutput = static_cast<QtWayland::wl_output *>(wl_output_get_user_data(output));
- return static_cast<QWaylandScreen *>(wlOutput);
+ if (auto *o = QtWayland::wl_output::fromObject(output))
+ return static_cast<QWaylandScreen *>(o);
+ return nullptr;
}
void QWaylandScreen::output_mode(uint32_t flags, int width, int height, int refresh)
diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp
index 34044ec9b..c16d346eb 100644
--- a/src/client/qwaylandshmbackingstore.cpp
+++ b/src/client/qwaylandshmbackingstore.cpp
@@ -288,12 +288,15 @@ void QWaylandShmBackingStore::resize(const QSize &size)
buffer = getBuffer(sizeWithMargins);
}
- qsizetype oldSize = mBackBuffer ? mBackBuffer->image()->sizeInBytes() : 0;
+ qsizetype oldSizeInBytes = mBackBuffer ? mBackBuffer->image()->sizeInBytes() : 0;
+ qsizetype newSizeInBytes = buffer->image()->sizeInBytes();
+
// mBackBuffer may have been deleted here but if so it means its size was different so we wouldn't copy it anyway
- if (mBackBuffer != buffer && oldSize == buffer->image()->sizeInBytes()) {
- memcpy(buffer->image()->bits(), mBackBuffer->image()->constBits(), buffer->image()->sizeInBytes());
- }
+ if (mBackBuffer != buffer && oldSizeInBytes == newSizeInBytes)
+ memcpy(buffer->image()->bits(), mBackBuffer->image()->constBits(), newSizeInBytes);
+
mBackBuffer = buffer;
+
// ensure the new buffer is at the beginning of the list so next time getBuffer() will pick
// it if possible
if (mBuffers.first() != buffer) {
@@ -301,7 +304,7 @@ void QWaylandShmBackingStore::resize(const QSize &size)
mBuffers.prepend(buffer);
}
- if (windowDecoration() && window()->isVisible())
+ if (windowDecoration() && window()->isVisible() && oldSizeInBytes != newSizeInBytes)
windowDecoration()->update();
}
diff --git a/src/client/qwaylandsurface.cpp b/src/client/qwaylandsurface.cpp
new file mode 100644
index 000000000..c35f01b56
--- /dev/null
+++ b/src/client/qwaylandsurface.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the config.tests 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 "qwaylandsurface_p.h"
+#include "qwaylanddisplay_p.h"
+#include "qwaylandscreen_p.h"
+
+#include <QtGui/QGuiApplication>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+QWaylandSurface::QWaylandSurface(QWaylandDisplay *display)
+ : wl_surface(display->createSurface(this))
+{
+ connect(qApp, &QGuiApplication::screenRemoved, this, &QWaylandSurface::handleScreenRemoved);
+}
+
+QWaylandSurface::~QWaylandSurface()
+{
+ destroy();
+}
+
+QWaylandScreen *QWaylandSurface::oldestEnteredScreen()
+{
+ return m_screens.value(0, nullptr);
+}
+
+QWaylandSurface *QWaylandSurface::fromWlSurface(::wl_surface *surface)
+{
+ if (auto *s = QtWayland::wl_surface::fromObject(surface))
+ return static_cast<QWaylandSurface *>(s);
+ return nullptr;
+}
+
+void QWaylandSurface::handleScreenRemoved(QScreen *qScreen)
+{
+ auto *screen = static_cast<QWaylandScreen *>(qScreen->handle());
+ if (m_screens.removeOne(screen))
+ emit screensChanged();
+}
+
+void QWaylandSurface::surface_enter(wl_output *output)
+{
+ auto addedScreen = QWaylandScreen::fromWlOutput(output);
+
+ if (!addedScreen)
+ return;
+
+ if (m_screens.contains(addedScreen)) {
+ qCWarning(lcQpaWayland)
+ << "Ignoring unexpected wl_surface.enter received for output with id:"
+ << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
+ << "screen name:" << addedScreen->name() << "screen model:" << addedScreen->model()
+ << "This is most likely a bug in the compositor.";
+ return;
+ }
+
+ m_screens.append(addedScreen);
+ emit screensChanged();
+}
+
+void QWaylandSurface::surface_leave(wl_output *output)
+{
+ auto *removedScreen = QWaylandScreen::fromWlOutput(output);
+
+ if (!removedScreen)
+ return;
+
+ bool wasRemoved = m_screens.removeOne(removedScreen);
+ if (!wasRemoved) {
+ qCWarning(lcQpaWayland)
+ << "Ignoring unexpected wl_surface.leave received for output with id:"
+ << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
+ << "screen name:" << removedScreen->name()
+ << "screen model:" << removedScreen->model()
+ << "This is most likely a bug in the compositor.";
+ return;
+ }
+ emit screensChanged();
+}
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
diff --git a/src/client/qwaylandsurface_p.h b/src/client/qwaylandsurface_p.h
new file mode 100644
index 000000000..541010934
--- /dev/null
+++ b/src/client/qwaylandsurface_p.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the config.tests 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 QWAYLANDSURFACE_P_H
+#define QWAYLANDSURFACE_P_H
+
+//
+// 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.
+//
+
+#include <QtGui/QScreen>
+
+#include <QtWaylandClient/private/qwayland-wayland.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+class QWaylandScreen;
+class QWaylandWindow;
+class QWaylandDisplay;
+
+class QWaylandSurface : public QObject, public QtWayland::wl_surface
+{
+ Q_OBJECT
+public:
+ explicit QWaylandSurface(QWaylandDisplay *display);
+ ~QWaylandSurface() override;
+ QWaylandScreen *oldestEnteredScreen();
+ QWaylandWindow *waylandWindow() const { return m_window; }
+
+ static QWaylandSurface *fromWlSurface(::wl_surface *surface);
+
+signals:
+ void screensChanged();
+
+private slots:
+ void handleScreenRemoved(QScreen *qScreen);
+
+protected:
+ void surface_enter(struct ::wl_output *output) override;
+ void surface_leave(struct ::wl_output *output) override;
+
+ QVector<QWaylandScreen *> m_screens; //As seen by wl_surface.enter/leave events. Chronological order.
+ QWaylandWindow *m_window = nullptr;
+
+ friend class QWaylandWindow; // TODO: shouldn't need to be friends
+};
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDSURFACE_P_H
diff --git a/src/client/qwaylandtouch.cpp b/src/client/qwaylandtouch.cpp
index 48c869a60..0394aef31 100644
--- a/src/client/qwaylandtouch.cpp
+++ b/src/client/qwaylandtouch.cpp
@@ -40,6 +40,7 @@
#include "qwaylandtouch_p.h"
#include "qwaylandinputdevice_p.h"
#include "qwaylanddisplay_p.h"
+#include "qwaylandsurface_p.h"
QT_BEGIN_NAMESPACE
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
index 4532bc236..9bc400f8a 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -41,6 +41,7 @@
#include "qwaylandbuffer_p.h"
#include "qwaylanddisplay_p.h"
+#include "qwaylandsurface_p.h"
#include "qwaylandinputdevice_p.h"
#include "qwaylandscreen_p.h"
#include "qwaylandshellsurface_p.h"
@@ -80,7 +81,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window)
{
static WId id = 1;
mWindowId = id++;
- connect(qApp, &QGuiApplication::screenRemoved, this, &QWaylandWindow::handleScreenRemoved);
initializeWlSurface();
}
@@ -90,7 +90,7 @@ QWaylandWindow::~QWaylandWindow()
delete mWindowDecoration;
- if (isInitialized())
+ if (mSurface)
reset(false);
const QWindow *parent = window();
@@ -115,7 +115,7 @@ void QWaylandWindow::initWindow()
if (window()->type() == Qt::Desktop)
return;
- if (!isInitialized()) {
+ if (!mSurface) {
initializeWlSurface();
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
QGuiApplication::sendEvent(window(), &e);
@@ -181,7 +181,7 @@ void QWaylandWindow::initWindow()
// typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
// to inform the compositor that high-resolution buffers will be provided.
if (mDisplay->compositorVersion() >= 3)
- set_buffer_scale(scale());
+ mSurface->set_buffer_scale(scale());
if (QScreen *s = window()->screen())
setOrientationMask(s->orientationUpdateMask());
@@ -199,7 +199,11 @@ void QWaylandWindow::initWindow()
void QWaylandWindow::initializeWlSurface()
{
- init(mDisplay->createSurface(static_cast<QtWayland::wl_surface *>(this)));
+ Q_ASSERT(!mSurface);
+ mSurface.reset(new QWaylandSurface(mDisplay));
+ connect(mSurface.data(), &QWaylandSurface::screensChanged,
+ this, &QWaylandWindow::handleScreensChanged);
+ mSurface->m_window = this;
}
bool QWaylandWindow::shouldCreateShellSurface() const
@@ -226,7 +230,7 @@ bool QWaylandWindow::shouldCreateSubSurface() const
void QWaylandWindow::reset(bool sendDestroyEvent)
{
- if (isInitialized() && sendDestroyEvent) {
+ if (mSurface && sendDestroyEvent) {
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
QGuiApplication::sendEvent(window(), &e);
}
@@ -234,11 +238,10 @@ void QWaylandWindow::reset(bool sendDestroyEvent)
mShellSurface = nullptr;
delete mSubSurfaceWindow;
mSubSurfaceWindow = nullptr;
- if (isInitialized()) {
+ if (mSurface) {
emit wlSurfaceDestroyed();
- destroy();
+ mSurface.reset();
}
- mScreens.clear();
if (mFrameCallback) {
wl_callback_destroy(mFrameCallback);
@@ -251,7 +254,9 @@ void QWaylandWindow::reset(bool sendDestroyEvent)
QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface)
{
- return static_cast<QWaylandWindow *>(static_cast<QtWayland::wl_surface *>(wl_surface_get_user_data(surface)));
+ if (auto *s = QWaylandSurface::fromWlSurface(surface))
+ return s->m_window;
+ return nullptr;
}
WId QWaylandWindow::winId() const
@@ -381,7 +386,12 @@ void QWaylandWindow::closePopups(QWaylandWindow *parent)
QWaylandScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
{
- return mScreens.isEmpty() ? waylandScreen() : mScreens.first();
+ if (mSurface) {
+ if (auto *screen = mSurface->oldestEnteredScreen())
+ return screen;
+ }
+
+ return waylandScreen();
}
void QWaylandWindow::setVisible(bool visible)
@@ -425,18 +435,18 @@ void QWaylandWindow::setMask(const QRegion &mask)
mMask = mask;
- if (!isInitialized())
+ if (!mSurface)
return;
if (mMask.isEmpty()) {
- set_input_region(nullptr);
+ mSurface->set_input_region(nullptr);
} else {
struct ::wl_region *region = mDisplay->createRegion(mMask);
- set_input_region(region);
+ mSurface->set_input_region(region);
wl_region_destroy(region);
}
- wl_surface::commit();
+ mSurface->commit();
}
void QWaylandWindow::applyConfigureWhenPossible()
@@ -490,58 +500,6 @@ void QWaylandWindow::applyConfigure()
QWindowSystemInterface::flushWindowSystemEvents();
}
-void QWaylandWindow::surface_enter(wl_output *output)
-{
- QWaylandScreen *oldScreen = calculateScreenFromSurfaceEvents();
- auto addedScreen = QWaylandScreen::fromWlOutput(output);
-
- if (mScreens.contains(addedScreen)) {
- qCWarning(lcQpaWayland)
- << "Ignoring unexpected wl_surface.enter received for output with id:"
- << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
- << "screen name:" << addedScreen->name() << "screen model:" << addedScreen->model()
- << "This is most likely a bug in the compositor.";
- return;
- }
-
- mScreens.append(addedScreen);
-
- QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
- if (oldScreen != newScreen) //currently this will only happen if the first wl_surface.enter is for a non-primary screen
- handleScreenChanged();
-}
-
-void QWaylandWindow::surface_leave(wl_output *output)
-{
- QWaylandScreen *oldScreen = calculateScreenFromSurfaceEvents();
- auto *removedScreen = QWaylandScreen::fromWlOutput(output);
- bool wasRemoved = mScreens.removeOne(removedScreen);
- if (!wasRemoved) {
- qCWarning(lcQpaWayland)
- << "Ignoring unexpected wl_surface.leave received for output with id:"
- << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
- << "screen name:" << removedScreen->name()
- << "screen model:" << removedScreen->model()
- << "This is most likely a bug in the compositor.";
- return;
- }
-
- QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
- if (oldScreen != newScreen)
- handleScreenChanged();
-}
-
-void QWaylandWindow::handleScreenRemoved(QScreen *qScreen)
-{
- QWaylandScreen *oldScreen = calculateScreenFromSurfaceEvents();
- bool wasRemoved = mScreens.removeOne(static_cast<QWaylandScreen *>(qScreen->handle()));
- if (wasRemoved) {
- QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
- if (oldScreen != newScreen)
- handleScreenChanged();
- }
-}
-
void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
{
Q_ASSERT(!buffer->committed());
@@ -549,9 +507,9 @@ void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
handleUpdate();
buffer->setBusy();
- QtWayland::wl_surface::attach(buffer->buffer(), x, y);
+ mSurface->attach(buffer->buffer(), x, y);
} else {
- QtWayland::wl_surface::attach(nullptr, 0, 0);
+ mSurface->attach(nullptr, 0, 0);
}
}
@@ -563,7 +521,7 @@ void QWaylandWindow::attachOffset(QWaylandBuffer *buffer)
void QWaylandWindow::damage(const QRect &rect)
{
- damage(rect.x(), rect.y(), rect.width(), rect.height());
+ mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
}
void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage)
@@ -593,20 +551,20 @@ void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage)
qCDebug(lcWaylandBackingstore) << "Buffer already committed, ignoring.";
return;
}
- if (!isInitialized())
+ if (!mSurface)
return;
attachOffset(buffer);
for (const QRect &rect: damage)
- wl_surface::damage(rect.x(), rect.y(), rect.width(), rect.height());
+ mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
Q_ASSERT(!buffer->committed());
buffer->setCommitted();
- wl_surface::commit();
+ mSurface->commit();
}
void QWaylandWindow::commit()
{
- wl_surface::commit();
+ mSurface->commit();
}
const wl_callback_listener QWaylandWindow::callbackListener = {
@@ -691,6 +649,11 @@ QRect QWaylandWindow::windowContentGeometry() const
return QRect(QPoint(), surfaceSize());
}
+wl_surface *QWaylandWindow::wlSurface()
+{
+ return mSurface ? mSurface->object() : nullptr;
+}
+
QWaylandShellSurface *QWaylandWindow::shellSurface() const
{
return mShellSurface;
@@ -732,9 +695,9 @@ void QWaylandWindow::handleContentOrientationChange(Qt::ScreenOrientation orient
default:
Q_UNREACHABLE();
}
- set_buffer_transform(transform);
+ mSurface->set_buffer_transform(transform);
// set_buffer_transform is double buffered, we need to commit.
- wl_surface::commit();
+ mSurface->commit();
}
void QWaylandWindow::setOrientationMask(Qt::ScreenOrientations mask)
@@ -866,6 +829,18 @@ QWaylandWindow *QWaylandWindow::transientParent() const
void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e)
{
+ if (e.type == QWaylandPointerEvent::Leave) {
+ if (mWindowDecoration) {
+ if (mMouseEventsInContentArea)
+ QWindowSystemInterface::handleLeaveEvent(window());
+ } else {
+ QWindowSystemInterface::handleLeaveEvent(window());
+ }
+#if QT_CONFIG(cursor)
+ restoreMouseCursor(inputDevice);
+#endif
+ return;
+ }
if (mWindowDecoration) {
handleMouseEventWithDecoration(inputDevice, e);
@@ -874,11 +849,15 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan
case QWaylandPointerEvent::Enter:
QWindowSystemInterface::handleEnterEvent(window(), e.local, e.global);
break;
+ case QWaylandPointerEvent::Press:
+ case QWaylandPointerEvent::Release:
case QWaylandPointerEvent::Motion:
QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, e.local, e.global, e.buttons, e.modifiers);
break;
case QWaylandPointerEvent::Wheel:
- QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, e.local, e.global, e.pixelDelta, e.angleDelta, e.modifiers);
+ QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, e.local, e.global,
+ e.pixelDelta, e.angleDelta, e.modifiers,
+ e.phase, e.source, false);
break;
}
}
@@ -892,20 +871,6 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan
#endif
}
-void QWaylandWindow::handleMouseLeave(QWaylandInputDevice *inputDevice)
-{
- if (mWindowDecoration) {
- if (mMouseEventsInContentArea) {
- QWindowSystemInterface::handleLeaveEvent(window());
- }
- } else {
- QWindowSystemInterface::handleLeaveEvent(window());
- }
-#if QT_CONFIG(cursor)
- restoreMouseCursor(inputDevice);
-#endif
-}
-
bool QWaylandWindow::touchDragDecoration(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::TouchPointState state, Qt::KeyboardModifiers mods)
{
if (!mWindowDecoration)
@@ -947,12 +912,18 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
case QWaylandPointerEvent::Enter:
QWindowSystemInterface::handleEnterEvent(window(), localTranslated, globalTranslated);
break;
+ case QWaylandPointerEvent::Press:
+ case QWaylandPointerEvent::Release:
case QWaylandPointerEvent::Motion:
QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, localTranslated, globalTranslated, e.buttons, e.modifiers);
break;
- case QWaylandPointerEvent::Wheel:
- QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, localTranslated, globalTranslated, e.pixelDelta, e.angleDelta, e.modifiers);
+ case QWaylandPointerEvent::Wheel: {
+ QWindowSystemInterface::handleWheelEvent(window(), e.timestamp,
+ localTranslated, globalTranslated,
+ e.pixelDelta, e.angleDelta, e.modifiers,
+ e.phase, e.source, false);
break;
+ }
}
mMouseEventsInContentArea = true;
@@ -965,16 +936,21 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
}
}
-void QWaylandWindow::handleScreenChanged()
+void QWaylandWindow::handleScreensChanged()
{
QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
+
+ if (newScreen == mLastReportedScreen)
+ return;
+
QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
+ mLastReportedScreen = newScreen;
int scale = newScreen->scale();
if (scale != mScale) {
mScale = scale;
- if (isInitialized() && mDisplay->compositorVersion() >= 3)
- set_buffer_scale(mScale);
+ if (mSurface && mDisplay->compositorVersion() >= 3)
+ mSurface->set_buffer_scale(mScale);
ensureSize();
}
}
@@ -1158,7 +1134,7 @@ void QWaylandWindow::handleUpdate()
QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
}
- mFrameCallback = frame();
+ mFrameCallback = mSurface->frame();
wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
mWaitingForFrameCallback = true;
mWaitingForUpdate = false;
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
index 8c1ebe167..5eadc02c8 100644
--- a/src/client/qwaylandwindow_p.h
+++ b/src/client/qwaylandwindow_p.h
@@ -79,8 +79,9 @@ class QWaylandInputDevice;
class QWaylandScreen;
class QWaylandShmBackingStore;
class QWaylandPointerEvent;
+class QWaylandSurface;
-class Q_WAYLAND_CLIENT_EXPORT QWaylandWindow : public QObject, public QPlatformWindow, public QtWayland::wl_surface
+class Q_WAYLAND_CLIENT_EXPORT QWaylandWindow : public QObject, public QPlatformWindow
{
Q_OBJECT
public:
@@ -108,12 +109,10 @@ public:
void applyConfigureWhenPossible(); //rename to possible?
- using QtWayland::wl_surface::attach;
void attach(QWaylandBuffer *buffer, int x, int y);
void attachOffset(QWaylandBuffer *buffer);
QPoint attachOffset() const;
- using QtWayland::wl_surface::damage;
void damage(const QRect &rect);
void safeCommit(QWaylandBuffer *buffer, const QRegion &damage);
@@ -128,6 +127,8 @@ public:
QSize surfaceSize() const;
QRect windowContentGeometry() const;
+ QWaylandSurface *waylandSurface() const { return mSurface.data(); }
+ ::wl_surface *wlSurface();
static QWaylandWindow *fromWlSurface(::wl_surface *surface);
QWaylandDisplay *display() const { return mDisplay; }
@@ -157,7 +158,6 @@ public:
QWaylandAbstractDecoration *decoration() const;
void handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);
- void handleMouseLeave(QWaylandInputDevice *inputDevice);
bool touchDragDecoration(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global,
Qt::TouchPointState state, Qt::KeyboardModifiers mods);
@@ -206,11 +206,8 @@ signals:
void wlSurfaceDestroyed();
protected:
- void surface_enter(struct ::wl_output *output) override;
- void surface_leave(struct ::wl_output *output) override;
-
- QVector<QWaylandScreen *> mScreens; //As seen by wl_surface.enter/leave events. Chronological order.
QWaylandDisplay *mDisplay = nullptr;
+ QScopedPointer<QWaylandSurface> mSurface;
QWaylandShellSurface *mShellSurface = nullptr;
QWaylandSubSurface *mSubSurfaceWindow = nullptr;
QVector<QWaylandSubSurface *> mChildren;
@@ -241,6 +238,7 @@ protected:
bool mSentInitialResize = false;
QPoint mOffset;
int mScale = 1;
+ QWaylandScreen *mLastReportedScreen = nullptr;
QIcon mWindowIcon;
@@ -252,9 +250,6 @@ protected:
QWaylandBuffer *mQueuedBuffer = nullptr;
QRegion mQueuedBufferDamage;
-private slots:
- void handleScreenRemoved(QScreen *qScreen);
-
private:
void setGeometry_helper(const QRect &rect);
void initWindow();
@@ -267,7 +262,7 @@ private:
QWaylandScreen *calculateScreenFromSurfaceEvents() const;
void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);
- void handleScreenChanged();
+ void handleScreensChanged();
QRect mLastExposeGeometry;
diff --git a/src/compositor/compositor_api/compositor_api.pri b/src/compositor/compositor_api/compositor_api.pri
index 8dbe12ac1..49d0de414 100644
--- a/src/compositor/compositor_api/compositor_api.pri
+++ b/src/compositor/compositor_api/compositor_api.pri
@@ -64,23 +64,28 @@ qtConfig(draganddrop) {
compositor_api/qwaylanddrag.cpp
}
-qtHaveModule(quick):qtConfig(opengl) {
+qtHaveModule(quick) {
DEFINES += QT_WAYLAND_COMPOSITOR_QUICK
SOURCES += \
compositor_api/qwaylandquickcompositor.cpp \
compositor_api/qwaylandquicksurface.cpp \
compositor_api/qwaylandquickoutput.cpp \
- compositor_api/qwaylandquickitem.cpp \
- compositor_api/qwaylandquickhardwarelayer.cpp
+ compositor_api/qwaylandquickitem.cpp
HEADERS += \
compositor_api/qwaylandquickcompositor.h \
compositor_api/qwaylandquicksurface.h \
compositor_api/qwaylandquickoutput.h \
compositor_api/qwaylandquickitem.h \
- compositor_api/qwaylandquickitem_p.h \
- compositor_api/qwaylandquickhardwarelayer_p.h
+ compositor_api/qwaylandquickitem_p.h
+
+ qtHaveModule(opengl) {
+ SOURCES += \
+ compositor_api/qwaylandquickhardwarelayer.cpp
+ HEADERS += \
+ compositor_api/qwaylandquickhardwarelayer_p.h
+ }
QT += qml qml-private quick quick-private
}
diff --git a/src/compositor/compositor_api/qwaylandcompositor.cpp b/src/compositor/compositor_api/qwaylandcompositor.cpp
index 8ff9e0905..530cf8ed6 100644
--- a/src/compositor/compositor_api/qwaylandcompositor.cpp
+++ b/src/compositor/compositor_api/qwaylandcompositor.cpp
@@ -254,8 +254,10 @@ QWaylandCompositorPrivate::~QWaylandCompositorPrivate()
delete data_device_manager;
#endif
+#if QT_CONFIG(opengl)
// Some client buffer integrations need to clean up before the destroying the wl_display
client_buffer_integration.reset();
+#endif
if (ownsDisplay)
wl_display_destroy(display);
diff --git a/src/compositor/compositor_api/qwaylandquickcompositor.cpp b/src/compositor/compositor_api/qwaylandquickcompositor.cpp
index 14b592efb..1b3f93e5c 100644
--- a/src/compositor/compositor_api/qwaylandquickcompositor.cpp
+++ b/src/compositor/compositor_api/qwaylandquickcompositor.cpp
@@ -128,6 +128,7 @@ void QWaylandQuickCompositor::grabSurface(QWaylandSurfaceGrabber *grabber, const
return;
}
+#if QT_CONFIG(opengl)
QWaylandQuickOutput *output = static_cast<QWaylandQuickOutput *>(defaultOutput());
if (!output) {
emit grabber->failed(QWaylandSurfaceGrabber::RendererNotReady);
@@ -169,6 +170,9 @@ void QWaylandQuickCompositor::grabSurface(QWaylandSurfaceGrabber *grabber, const
state->grabber = grabber;
state->buffer = buffer;
static_cast<QQuickWindow *>(output->window())->scheduleRenderJob(state, QQuickWindow::NoStage);
+#else
+ emit grabber->failed(QWaylandSurfaceGrabber::UnknownBufferType);
+#endif
}
QT_END_NAMESPACE
diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp
index 7b0d5c5d5..996177be4 100644
--- a/src/compositor/compositor_api/qwaylandquickitem.cpp
+++ b/src/compositor/compositor_api/qwaylandquickitem.cpp
@@ -73,6 +73,7 @@
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(opengl)
static const struct {
const char * const vertexShaderSourceFile;
const char * const fragmentShaderSourceFile;
@@ -259,6 +260,7 @@ void QWaylandBufferMaterial::ensureTextures(int count)
m_textures << nullptr;
}
}
+#endif // QT_CONFIG(opengl)
QMutex *QWaylandQuickItemPrivate::mutex = nullptr;
@@ -284,10 +286,12 @@ public:
if (m_ref.hasBuffer()) {
if (buffer.isSharedMemory()) {
m_sgTex = surfaceItem->window()->createTextureFromImage(buffer.image());
- if (m_sgTex) {
+#if QT_CONFIG(opengl)
+ if (m_sgTex)
m_sgTex->bind();
- }
+#endif
} else {
+#if QT_CONFIG(opengl)
QQuickWindow::CreateTextureOptions opt;
QWaylandQuickSurface *surface = qobject_cast<QWaylandQuickSurface *>(surfaceItem->surface());
if (surface && surface->useTextureAlpha()) {
@@ -297,6 +301,9 @@ public:
auto texture = buffer.toOpenGLTexture();
auto size = surface->bufferSize();
m_sgTex = surfaceItem->window()->createTextureFromId(texture->textureId(), size, opt);
+#else
+ qCWarning(qLcWaylandCompositor) << "Without OpenGL support only shared memory textures are supported";
+#endif
}
}
emit textureChanged();
@@ -1327,7 +1334,11 @@ QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
const QRectF rect = invertY ? QRectF(0, height(), width(), -height())
: QRectF(0, 0, width(), height());
- if (ref.isSharedMemory() || bufferTypes[ref.bufferFormatEgl()].canProvideTexture) {
+ if (ref.isSharedMemory()
+#if QT_CONFIG(opengl)
+ || bufferTypes[ref.bufferFormatEgl()].canProvideTexture
+#endif
+ ) {
// This case could covered by the more general path below, but this is more efficient (especially when using ShaderEffect items).
QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>(oldNode);
@@ -1353,45 +1364,48 @@ QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
node->setSourceRect(QRectF(source.topLeft() * scale, source.size() * scale));
return node;
- } else {
- Q_ASSERT(!d->provider);
+ }
- QSGGeometryNode *node = static_cast<QSGGeometryNode *>(oldNode);
+#if QT_CONFIG(opengl)
+ Q_ASSERT(!d->provider);
- if (!node) {
- node = new QSGGeometryNode;
- d->newTexture = true;
- }
+ QSGGeometryNode *node = static_cast<QSGGeometryNode *>(oldNode);
- QSGGeometry *geometry = node->geometry();
- QWaylandBufferMaterial *material = static_cast<QWaylandBufferMaterial *>(node->material());
+ if (!node) {
+ node = new QSGGeometryNode;
+ d->newTexture = true;
+ }
- if (!geometry)
- geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
+ QSGGeometry *geometry = node->geometry();
+ QWaylandBufferMaterial *material = static_cast<QWaylandBufferMaterial *>(node->material());
- if (!material)
- material = new QWaylandBufferMaterial(ref.bufferFormatEgl());
+ if (!geometry)
+ geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
- if (d->newTexture) {
- d->newTexture = false;
- for (int plane = 0; plane < bufferTypes[ref.bufferFormatEgl()].planeCount; plane++)
- if (auto texture = ref.toOpenGLTexture(plane))
- material->setTextureForPlane(plane, texture);
- material->bind();
- }
+ if (!material)
+ material = new QWaylandBufferMaterial(ref.bufferFormatEgl());
- QSGGeometry::updateTexturedRectGeometry(geometry, rect, QRectF(0, 0, 1, 1));
+ if (d->newTexture) {
+ d->newTexture = false;
+ for (int plane = 0; plane < bufferTypes[ref.bufferFormatEgl()].planeCount; plane++)
+ if (auto texture = ref.toOpenGLTexture(plane))
+ material->setTextureForPlane(plane, texture);
+ material->bind();
+ }
- node->setGeometry(geometry);
- node->setFlag(QSGNode::OwnsGeometry, true);
+ QSGGeometry::updateTexturedRectGeometry(geometry, rect, QRectF(0, 0, 1, 1));
- node->setMaterial(material);
- node->setFlag(QSGNode::OwnsMaterial, true);
+ node->setGeometry(geometry);
+ node->setFlag(QSGNode::OwnsGeometry, true);
- return node;
- }
+ node->setMaterial(material);
+ node->setFlag(QSGNode::OwnsMaterial, true);
- Q_UNREACHABLE();
+ return node;
+#else
+ qCWarning(qLcWaylandCompositor) << "Without OpenGL support only shared memory textures are supported";
+ return nullptr;
+#endif // QT_CONFIG(opengl)
}
void QWaylandQuickItem::setTouchEventsEnabled(bool enabled)
diff --git a/src/compositor/compositor_api/qwaylandquickitem_p.h b/src/compositor/compositor_api/qwaylandquickitem_p.h
index 3d710d71b..2ec02ca6d 100644
--- a/src/compositor/compositor_api/qwaylandquickitem_p.h
+++ b/src/compositor/compositor_api/qwaylandquickitem_p.h
@@ -64,6 +64,7 @@ class QWaylandSurfaceTextureProvider;
class QMutex;
class QOpenGLTexture;
+#if QT_CONFIG(opengl)
class QWaylandBufferMaterialShader : public QSGMaterialShader
{
public:
@@ -102,6 +103,7 @@ private:
const QWaylandBufferRef::BufferFormatEgl m_format;
QVarLengthArray<QOpenGLTexture*, 3> m_textures;
};
+#endif // QT_CONFIG(opengl)
class QWaylandQuickItemPrivate : public QQuickItemPrivate
{
diff --git a/src/compositor/compositor_api/qwaylandseat.cpp b/src/compositor/compositor_api/qwaylandseat.cpp
index bc5e1d8b2..0a6248d11 100644
--- a/src/compositor/compositor_api/qwaylandseat.cpp
+++ b/src/compositor/compositor_api/qwaylandseat.cpp
@@ -289,6 +289,10 @@ void QWaylandSeat::sendKeyReleaseEvent(uint code)
* Sends a touch point event to the \a surface on a touch device with the given
* \a id, \a point and \a state.
*
+ * \warning This API should not be used in combination with forwarding of touch
+ * events using \l QWaylandQuickItem::touchEventsEnabled or \l sendFullTouchEvent,
+ * as it might lead to conflicting touch ids.
+ *
* Returns the serial for the touch up or touch down event.
*/
uint QWaylandSeat::sendTouchPointEvent(QWaylandSurface *surface, int id, const QPointF &point, Qt::TouchPointState state)
@@ -310,6 +314,10 @@ uint QWaylandSeat::sendTouchPointEvent(QWaylandSurface *surface, int id, const Q
* \note You need to send a touch frame event when you are done sending touch
* events.
*
+ * \warning This API should not be used in combination with forwarding of touch
+ * events using \l WaylandQuickItem::touchEventsEnabled, as it might lead to
+ * conflicting touch ids.
+ *
* Returns the serial for the touch down event.
*/
@@ -320,6 +328,10 @@ uint QWaylandSeat::sendTouchPointEvent(QWaylandSurface *surface, int id, const Q
* \note You need to send a touch frame event when you are done sending touch
* events.
*
+ * \warning This API should not be used in combination with forwarding of touch
+ * events using \l QWaylandQuickItem::touchEventsEnabled or \l sendFullTouchEvent,
+ * as it might lead to conflicting touch ids.
+ *
* Returns the serial for the touch down event.
*/
uint QWaylandSeat::sendTouchPointPressed(QWaylandSurface *surface, int id, const QPointF &position)
@@ -336,6 +348,10 @@ uint QWaylandSeat::sendTouchPointPressed(QWaylandSurface *surface, int id, const
* \note You need to send a touch frame event when you are done sending touch
* events.
*
+ * \warning This API should not be used in combination with forwarding of touch
+ * events using \l WaylandQuickItem::touchEventsEnabled, as it might lead to
+ * conflicting touch ids.
+ *
* Returns the serial for the touch up event.
*/
@@ -346,6 +362,10 @@ uint QWaylandSeat::sendTouchPointPressed(QWaylandSurface *surface, int id, const
* \note You need to send a touch frame event when you are done sending touch
* events.
*
+ * \warning This API should not be used in combination with forwarding of touch
+ * events using \l QWaylandQuickItem::touchEventsEnabled or \l sendFullTouchEvent,
+ * as it might lead to conflicting touch ids.
+ *
* Returns the serial for the touch up event.
*/
uint QWaylandSeat::sendTouchPointReleased(QWaylandSurface *surface, int id, const QPointF &position)
@@ -362,6 +382,10 @@ uint QWaylandSeat::sendTouchPointReleased(QWaylandSurface *surface, int id, cons
* \note You need to send a touch frame event when you are done sending touch
* events.
*
+ * \warning This API should not be used in combination with forwarding of touch
+ * events using \l WaylandQuickItem::touchEventsEnabled, as it might lead to
+ * conflicting touch ids.
+ *
* Returns the serial for the touch motion event.
*/
@@ -372,6 +396,10 @@ uint QWaylandSeat::sendTouchPointReleased(QWaylandSurface *surface, int id, cons
* \note You need to send a touch frame event when you are done sending touch
* events.
*
+ * \warning This API should not be used in combination with forwarding of touch
+ * events using \l QWaylandQuickItem::touchEventsEnabled or \l sendFullTouchEvent,
+ * as it might lead to conflicting touch ids.
+ *
* Returns the serial for the touch motion event.
*/
uint QWaylandSeat::sendTouchPointMoved(QWaylandSurface *surface, int id, const QPointF &position)
@@ -415,6 +443,11 @@ void QWaylandSeat::sendTouchCancelEvent(QWaylandClient *client)
/*!
* Sends the \a event to the specified \a surface on the touch device.
+ *
+ * \warning This API will automatically map \l QTouchEvent::TouchPoint::id to a
+ * sequential id before sending it to the client. It should therefore not be
+ * used in combination with the other API using explicit ids, as collisions
+ * might occur.
*/
void QWaylandSeat::sendFullTouchEvent(QWaylandSurface *surface, QTouchEvent *event)
{
diff --git a/src/compositor/compositor_api/qwaylandtouch.cpp b/src/compositor/compositor_api/qwaylandtouch.cpp
index 3e7298001..15746cb5f 100644
--- a/src/compositor/compositor_api/qwaylandtouch.cpp
+++ b/src/compositor/compositor_api/qwaylandtouch.cpp
@@ -98,6 +98,20 @@ void QWaylandTouchPrivate::sendMotion(QWaylandClient *client, uint32_t time, int
wl_fixed_from_double(position.x()), wl_fixed_from_double(position.y()));
}
+int QWaylandTouchPrivate::toSequentialWaylandId(int touchId)
+{
+ const int waylandId = ids.indexOf(touchId);
+ if (waylandId != -1)
+ return waylandId;
+ const int availableId = ids.indexOf(-1);
+ if (availableId != -1) {
+ ids[availableId] = touchId;
+ return availableId;
+ }
+ ids.append(touchId);
+ return ids.length() - 1;
+}
+
/*!
* \class QWaylandTouch
* \inmodule QtWaylandCompositor
@@ -212,7 +226,10 @@ void QWaylandTouch::sendFullTouchEvent(QWaylandSurface *surface, QTouchEvent *ev
for (int i = 0; i < pointCount; ++i) {
const QTouchEvent::TouchPoint &tp(points.at(i));
// Convert the local pos in the compositor window to surface-relative.
- sendTouchPointEvent(surface, tp.id(), tp.pos(), tp.state());
+ const int id = d->toSequentialWaylandId(tp.id());
+ sendTouchPointEvent(surface, id, tp.pos(), tp.state());
+ if (tp.state() == Qt::TouchPointReleased)
+ d->ids[id] = -1;
}
sendFrameEvent(surface->client());
}
diff --git a/src/compositor/compositor_api/qwaylandtouch_p.h b/src/compositor/compositor_api/qwaylandtouch_p.h
index de1b748de..0b87f8475 100644
--- a/src/compositor/compositor_api/qwaylandtouch_p.h
+++ b/src/compositor/compositor_api/qwaylandtouch_p.h
@@ -80,8 +80,10 @@ public:
private:
void touch_release(Resource *resource) override;
+ int toSequentialWaylandId(int touchId);
QWaylandSeat *seat = nullptr;
+ QVarLengthArray<int, 10> ids;
};
QT_END_NAMESPACE
diff --git a/src/compositor/configure.json b/src/compositor/configure.json
index aabd2472f..db80543f7 100644
--- a/src/compositor/configure.json
+++ b/src/compositor/configure.json
@@ -86,6 +86,11 @@
"type": "compile",
"test": "dmabuf_client_buffer",
"use": "egl"
+ },
+ "vulkan-server-buffer": {
+ "label": "Vulkan Buffer Sharing",
+ "type": "compile",
+ "test": "vulkan_server_buffer"
}
},
@@ -139,6 +144,11 @@
"condition": "features.wayland-server && features.opengl && features.egl && tests.dmabuf-client-buffer",
"output": [ "privateFeature" ]
},
+ "wayland-vulkan-server-buffer": {
+ "label": "Vulkan-based server buffer integration",
+ "condition": "features.wayland-server && features.opengl && features.egl && tests.vulkan-server-buffer",
+ "output": [ "privateFeature" ]
+ },
"wayland-shm-emulation-server-buffer": {
"label": "Shm emulation server buffer",
"condition": "features.wayland-server && features.opengl",
diff --git a/src/compositor/extensions/extensions.pri b/src/compositor/extensions/extensions.pri
index 361f93984..206bde20d 100644
--- a/src/compositor/extensions/extensions.pri
+++ b/src/compositor/extensions/extensions.pri
@@ -65,24 +65,36 @@ SOURCES += \
extensions/qwaylandiviapplication.cpp \
extensions/qwaylandivisurface.cpp \
-qtHaveModule(quick):contains(QT_CONFIG, opengl) {
+qtHaveModule(quick) {
HEADERS += \
+ extensions/qwaylandquickshellintegration.h \
extensions/qwaylandquickshellsurfaceitem.h \
extensions/qwaylandquickshellsurfaceitem_p.h \
extensions/qwaylandivisurfaceintegration_p.h \
extensions/qwaylandwlshellintegration_p.h \
extensions/qwaylandxdgshellv5integration_p.h \
extensions/qwaylandxdgshellv6integration_p.h \
- extensions/qwaylandxdgshellintegration_p.h
+ extensions/qwaylandxdgshellintegration_p.h \
SOURCES += \
+ extensions/qwaylandquickshellintegration.cpp \
extensions/qwaylandquickshellsurfaceitem.cpp \
extensions/qwaylandivisurfaceintegration.cpp \
extensions/qwaylandwlshellintegration.cpp \
extensions/qwaylandxdgshellv5integration.cpp \
extensions/qwaylandxdgshellv6integration.cpp \
- extensions/qwaylandxdgshellintegration.cpp
+ extensions/qwaylandxdgshellintegration.cpp \
+ qtHaveModule(opengl) {
+ WAYLANDSERVERSOURCES += \
+ ../extensions/qt-texture-sharing-unstable-v1.xml
+
+ HEADERS += \
+ extensions/qwltexturesharingextension_p.h
+
+ SOURCES += \
+ extensions/qwltexturesharingextension.cpp
+ }
}
include ($$PWD/pregenerated/xdg-shell-v5.pri)
diff --git a/src/compositor/extensions/qwaylandquickshellintegration.cpp b/src/compositor/extensions/qwaylandquickshellintegration.cpp
new file mode 100644
index 000000000..961b7b111
--- /dev/null
+++ b/src/compositor/extensions/qwaylandquickshellintegration.cpp
@@ -0,0 +1,256 @@
+/****************************************************************************
+**
+** 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 "qwaylandquickshellintegration.h"
+
+/*!
+ * \class QWaylandQuickShellIntegration
+ * \inmodule QtWaylandCompositor
+ * \since 5.14
+ * \brief Provides support for shell surface integration with QtQuick
+ *
+ * Shell surface implementations should inherit from this class in order to provide
+ * an integration between the shell surface and QtQuick.
+ *
+ * \sa QWaylandShellSurface
+ * \sa QWaylandShellSurfaceItem
+ */
+
+QWaylandQuickShellIntegration::QWaylandQuickShellIntegration(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ * This method can be reimplemented in a subclass to receive touch events
+ * for a shell surface.
+ *
+ * The event information is provided by the \a event parameter.
+ *
+ * Return \a false if you want QWaylandQuickShellSurfaceItem to handle
+ * the event.
+ *
+ * \sa QWaylandQuickShellSurfaceItem::touchEvent()
+ */
+bool QWaylandQuickShellIntegration::touchEvent(QTouchEvent *event)
+{
+ Q_UNUSED(event);
+ return false;
+}
+
+/*!
+ * This method can be reimplemented in a subclass to receive hover-enter events
+ * for a shell surface.
+ *
+ * The event information is provided by the \a event parameter.
+ *
+ * Hover events are only provided if \l {QWaylandQuickShellSurfaceItem::} {acceptHoverEvents()}
+ * is \a true.
+ *
+ * Return \a false if you want QWaylandQuickShellSurfaceItem to handle
+ * the event.
+ *
+ * \sa QWaylandQuickShellSurfaceItem::hoverEnterEvent()
+ */
+bool QWaylandQuickShellIntegration::hoverEnterEvent(QHoverEvent *event)
+{
+ Q_UNUSED(event);
+ return false;
+}
+
+/*!
+ * This method can be reimplemented in a subclass to receive hover-leave events
+ * for a shell surface.
+ *
+ * The event information is provided by the \a event parameter.
+ *
+ * Hover events are only provided if \l {QWaylandQuickShellSurfaceItem::} {acceptHoverEvents()}
+ * is \a true.
+ *
+ * Return \a false if you want QWaylandQuickShellSurfaceItem to handle
+ * the event.
+ *
+ * \sa QWaylandQuickShellSurfaceItem::hoverLeaveEvent()
+ */
+bool QWaylandQuickShellIntegration::hoverLeaveEvent(QHoverEvent *event)
+{
+ Q_UNUSED(event);
+ return false;
+}
+
+/*!
+ * This method can be reimplemented in a subclass to receive hover-move events
+ * for a shell surface.
+ *
+ * The event information is provided by the \a event parameter.
+ *
+ * Hover events are only provided if \l {QWaylandQuickShellSurfaceItem::} {acceptHoverEvents()}
+ * is \a true.
+ *
+ * Return \a false if you want QWaylandQuickShellSurfaceItem to handle
+ * the event.
+ *
+ * \sa QWaylandQuickShellSurfaceItem::hoverMoveEvent()
+ */
+bool QWaylandQuickShellIntegration::hoverMoveEvent(QHoverEvent *event)
+{
+ Q_UNUSED(event);
+ return false;
+}
+
+/*!
+ * This method can be reimplemented in a subclass to receive key press events
+ * for a shell surface.
+ *
+ * The event information is provided by the \a event parameter.
+ *
+ * Return \a false if you want QWaylandQuickShellSurfaceItem to handle
+ * the event.
+ *
+ * \sa QWaylandQuickShellSurfaceItem::keyPressEvent()
+ */
+bool QWaylandQuickShellIntegration::keyPressEvent(QKeyEvent *event)
+{
+ Q_UNUSED(event);
+ return false;
+}
+
+/*!
+ * This method can be reimplemented in a subclass to receive key release events
+ * for a shell surface.
+ *
+ * The event information is provided by the \a event parameter.
+ *
+ * Return \a false if you want QWaylandQuickShellSurfaceItem to handle
+ * the event.
+ *
+ * \sa QWaylandQuickShellSurfaceItem::keyReleaseEvent()
+ */
+bool QWaylandQuickShellIntegration::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_UNUSED(event);
+ return false;
+}
+
+/*!
+ * This method can be reimplemented in a subclass to receive mouse double click events
+ * for a shell surface.
+ *
+ * The event information is provided by the \a event parameter.
+ *
+ * Return \a false if you want QWaylandQuickShellSurfaceItem to handle
+ * the event.
+ *
+ * \sa QWaylandQuickShellSurfaceItem::mouseDoubleClickEvent()
+ */
+bool QWaylandQuickShellIntegration::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_UNUSED(event);
+ return false;
+}
+
+/*!
+ * This method can be reimplemented in a subclass to receive mouse move events
+ * for a shell surface.
+ *
+ * The event information is provided by the \a event parameter.
+ *
+ * Return \a false if you want QWaylandQuickShellSurfaceItem to handle
+ * the event.
+ *
+ * \sa QWaylandQuickShellSurfaceItem::mouseMoveEvent()
+ */
+bool QWaylandQuickShellIntegration::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_UNUSED(event);
+ return false;
+}
+
+/*!
+ * This method can be reimplemented in a subclass to receive mouse press events
+ * for a shell surface.
+ *
+ * The event information is provided by the \a event parameter.
+ *
+ * Return \a false if you want QWaylandQuickShellSurfaceItem to handle
+ * the event.
+ *
+ * \sa QWaylandQuickShellSurfaceItem::mousePressEvent()
+ */
+bool QWaylandQuickShellIntegration::mousePressEvent(QMouseEvent *event)
+{
+ Q_UNUSED(event);
+ return false;
+}
+
+/*!
+ * This method can be reimplemented in a subclass to receive mouse release events
+ * for a shell surface.
+ *
+ * The event information is provided by the \a event parameter.
+ *
+ * Return \a false if you want QWaylandQuickShellSurfaceItem to handle
+ * the event.
+ *
+ * \sa QWaylandQuickShellSurfaceItem::mouseReleaseEvent()
+ */
+bool QWaylandQuickShellIntegration::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_UNUSED(event);
+ return false;
+}
+
+#if QT_CONFIG(wheelevent)
+/*!
+ * This method can be reimplemented in a subclass to receive wheel events
+ * for a shell surface.
+ *
+ * The event information is provided by the \a event parameter.
+ *
+ * Return \a false if you want QWaylandQuickShellSurfaceItem to handle
+ * the event.
+ *
+ * \sa QWaylandQuickShellSurfaceItem::wheelEvent()
+ */
+bool QWaylandQuickShellIntegration::wheelEvent(QWheelEvent *event)
+{
+ Q_UNUSED(event);
+ return false;
+}
+#endif
diff --git a/src/compositor/extensions/qwaylandquickshellintegration.h b/src/compositor/extensions/qwaylandquickshellintegration.h
new file mode 100644
index 000000000..e1a4c2384
--- /dev/null
+++ b/src/compositor/extensions/qwaylandquickshellintegration.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** 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 QWAYLANDQUICKSHELLINTEGRATION_H
+#define QWAYLANDQUICKSHELLINTEGRATION_H
+
+#include <QtCore/QObject>
+#include <QtGui/QMouseEvent>
+#include <QtWaylandCompositor/qtwaylandcompositorglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickShellIntegration : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QWaylandQuickShellIntegration(QObject *parent = nullptr);
+
+ virtual bool touchEvent(QTouchEvent *event);
+
+ virtual bool hoverEnterEvent(QHoverEvent *event);
+ virtual bool hoverLeaveEvent(QHoverEvent *event);
+ virtual bool hoverMoveEvent(QHoverEvent *event);
+
+ virtual bool keyPressEvent(QKeyEvent *event);
+ virtual bool keyReleaseEvent(QKeyEvent *event);
+
+ virtual bool mouseDoubleClickEvent(QMouseEvent *event);
+ virtual bool mouseMoveEvent(QMouseEvent *event);
+ virtual bool mousePressEvent(QMouseEvent *event);
+ virtual bool mouseReleaseEvent(QMouseEvent *event);
+
+#if QT_CONFIG(wheelevent)
+ virtual bool wheelEvent(QWheelEvent *event);
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDQUICKSHELLINTEGRATION_H
diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp b/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp
index 84e9c356d..110c3d0a3 100644
--- a/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp
+++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp
@@ -208,6 +208,56 @@ void QWaylandQuickShellSurfaceItem::setAutoCreatePopupItems(bool enabled)
emit autoCreatePopupItemsChanged();
}
+void QWaylandQuickShellSurfaceItem::touchEvent(QTouchEvent *event)
+{
+ Q_D(QWaylandQuickShellSurfaceItem);
+ if (!d->m_shellIntegration->touchEvent(event))
+ QWaylandQuickItem::touchEvent(event);
+}
+
+void QWaylandQuickShellSurfaceItem::hoverEnterEvent(QHoverEvent *event)
+{
+ Q_D(QWaylandQuickShellSurfaceItem);
+ if (!d->m_shellIntegration->hoverEnterEvent(event))
+ QWaylandQuickItem::hoverEnterEvent(event);
+}
+
+void QWaylandQuickShellSurfaceItem::hoverLeaveEvent(QHoverEvent *event)
+{
+ Q_D(QWaylandQuickShellSurfaceItem);
+ if (!d->m_shellIntegration->hoverLeaveEvent(event))
+ QWaylandQuickItem::hoverLeaveEvent(event);
+}
+
+void QWaylandQuickShellSurfaceItem::hoverMoveEvent(QHoverEvent *event)
+{
+ Q_D(QWaylandQuickShellSurfaceItem);
+ if (!d->m_shellIntegration->hoverMoveEvent(event))
+ QWaylandQuickItem::hoverMoveEvent(event);
+}
+
+
+void QWaylandQuickShellSurfaceItem::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QWaylandQuickShellSurfaceItem);
+ if (!d->m_shellIntegration->keyPressEvent(event))
+ QWaylandQuickItem::keyPressEvent(event);
+}
+
+void QWaylandQuickShellSurfaceItem::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_D(QWaylandQuickShellSurfaceItem);
+ if (!d->m_shellIntegration->keyReleaseEvent(event))
+ QWaylandQuickItem::keyReleaseEvent(event);
+}
+
+void QWaylandQuickShellSurfaceItem::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_D(QWaylandQuickShellSurfaceItem);
+ if (!d->m_shellIntegration->mouseDoubleClickEvent(event))
+ QWaylandQuickItem::mouseDoubleClickEvent(event);
+}
+
void QWaylandQuickShellSurfaceItem::mouseMoveEvent(QMouseEvent *event)
{
Q_D(QWaylandQuickShellSurfaceItem);
@@ -215,6 +265,13 @@ void QWaylandQuickShellSurfaceItem::mouseMoveEvent(QMouseEvent *event)
QWaylandQuickItem::mouseMoveEvent(event);
}
+void QWaylandQuickShellSurfaceItem::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QWaylandQuickShellSurfaceItem);
+ if (!d->m_shellIntegration->mousePressEvent(event))
+ QWaylandQuickItem::mousePressEvent(event);
+}
+
void QWaylandQuickShellSurfaceItem::mouseReleaseEvent(QMouseEvent *event)
{
Q_D(QWaylandQuickShellSurfaceItem);
@@ -222,6 +279,15 @@ void QWaylandQuickShellSurfaceItem::mouseReleaseEvent(QMouseEvent *event)
QWaylandQuickItem::mouseReleaseEvent(event);
}
+#if QT_CONFIG(wheelevent)
+void QWaylandQuickShellSurfaceItem::wheelEvent(QWheelEvent *event)
+{
+ Q_D(QWaylandQuickShellSurfaceItem);
+ if (!d->m_shellIntegration->wheelEvent(event))
+ QWaylandQuickItem::wheelEvent(event);
+}
+#endif
+
/*!
\class QWaylandQuickShellEventFilter
\brief QWaylandQuickShellEventFilter implements a Wayland popup grab
diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem.h b/src/compositor/extensions/qwaylandquickshellsurfaceitem.h
index d14fa3fce..73a4900bb 100644
--- a/src/compositor/extensions/qwaylandquickshellsurfaceitem.h
+++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem.h
@@ -76,8 +76,23 @@ Q_SIGNALS:
protected:
QWaylandQuickShellSurfaceItem(QWaylandQuickShellSurfaceItemPrivate &dd, QQuickItem *parent);
+ void touchEvent(QTouchEvent *event) override;
+
+ void hoverEnterEvent(QHoverEvent *event) override;
+ void hoverLeaveEvent(QHoverEvent *event) override;
+ void hoverMoveEvent(QHoverEvent *event) override;
+
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+
+ void mouseDoubleClickEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
+
+#if QT_CONFIG(wheelevent)
+ void wheelEvent(QWheelEvent *event) override;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h b/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h
index 7a458381e..f622368ab 100644
--- a/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h
+++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h
@@ -41,6 +41,7 @@
#define QWAYLANDQUICKSHELLSURFACEITEM_P_H
#include <QtWaylandCompositor/QWaylandQuickShellSurfaceItem>
+#include <QtWaylandCompositor/QWaylandQuickShellIntegration>
#include <QtWaylandCompositor/private/qwaylandquickitem_p.h>
#include <QtCore/QBasicTimer>
@@ -59,7 +60,6 @@ QT_BEGIN_NAMESPACE
// We mean it.
//
-class QWaylandQuickShellIntegration;
class QWaylandShellSurface;
class QWaylandQuickShellSurfaceItem;
@@ -74,16 +74,13 @@ public:
QWaylandQuickShellIntegration *m_shellIntegration = nullptr;
QWaylandShellSurface *m_shellSurface = nullptr;
QQuickItem *m_moveItem = nullptr;
- bool m_autoCreatePopupItems = false;
-};
+ bool m_autoCreatePopupItems =
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ true;
+#else
+ false;
+#endif
-class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickShellIntegration : public QObject
-{
- Q_OBJECT
-public:
- QWaylandQuickShellIntegration(QObject *parent = nullptr) : QObject(parent) {}
- virtual bool mouseMoveEvent(QMouseEvent *) { return false; }
- virtual bool mouseReleaseEvent(QMouseEvent *) { return false; }
};
class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickShellEventFilter : public QObject
diff --git a/src/compositor/extensions/qwaylandxdgshell.cpp b/src/compositor/extensions/qwaylandxdgshell.cpp
index eb7b958b4..f06fd7934 100644
--- a/src/compositor/extensions/qwaylandxdgshell.cpp
+++ b/src/compositor/extensions/qwaylandxdgshell.cpp
@@ -1871,6 +1871,26 @@ uint QWaylandXdgPopup::sendConfigure(const QRect &geometry)
}
/*!
+ * \qmlmethod void QtWaylandCompositor::XdgPopup::sendPopupDone()
+ * \since 5.14
+ *
+ * Dismiss the popup. According to the \c xdg-shell protocol this should make the
+ * client destroy the popup.
+ */
+
+/*!
+ * \since 5.14
+ *
+ * Dismiss the popup. According to the \c xdg-shell protocol this should make the
+ * client destroy the popup.
+ */
+void QWaylandXdgPopup::sendPopupDone()
+{
+ Q_D(QWaylandXdgPopup);
+ d->send_popup_done();
+}
+
+/*!
* Returns the surface role for the QWaylandPopup.
*/
QWaylandSurfaceRole *QWaylandXdgPopup::role()
diff --git a/src/compositor/extensions/qwaylandxdgshell.h b/src/compositor/extensions/qwaylandxdgshell.h
index 774dd2282..f45038eb9 100644
--- a/src/compositor/extensions/qwaylandxdgshell.h
+++ b/src/compositor/extensions/qwaylandxdgshell.h
@@ -260,6 +260,7 @@ public:
QPoint unconstrainedPosition() const;
Q_INVOKABLE uint sendConfigure(const QRect &geometry);
+ Q_REVISION(14) Q_INVOKABLE void sendPopupDone();
static QWaylandSurfaceRole *role();
diff --git a/src/compositor/extensions/qwaylandxdgshellv5.cpp b/src/compositor/extensions/qwaylandxdgshellv5.cpp
index 0628f55e7..a85efbc5f 100644
--- a/src/compositor/extensions/qwaylandxdgshellv5.cpp
+++ b/src/compositor/extensions/qwaylandxdgshellv5.cpp
@@ -115,9 +115,9 @@ QWaylandXdgPopupV5 *QWaylandXdgShellV5Private::topmostPopupForClient(wl_client *
return clientPopups.empty() ? nullptr : clientPopups.last();
}
-QWaylandXdgSurfaceV5 *QWaylandXdgShellV5Private::xdgSurfaceFromSurface(QWaylandSurface *surface)
+QWaylandXdgSurfaceV5 *QWaylandXdgShellV5Private::xdgSurfaceFromSurface(QWaylandSurface *surface) const
{
- Q_FOREACH (QWaylandXdgSurfaceV5 *xdgSurface, m_xdgSurfaces) {
+ for (QWaylandXdgSurfaceV5 *xdgSurface : m_xdgSurfaces) {
if (surface == xdgSurface->surface())
return xdgSurface;
}
diff --git a/src/compositor/extensions/qwaylandxdgshellv5_p.h b/src/compositor/extensions/qwaylandxdgshellv5_p.h
index 8f5af746b..d10359e68 100644
--- a/src/compositor/extensions/qwaylandxdgshellv5_p.h
+++ b/src/compositor/extensions/qwaylandxdgshellv5_p.h
@@ -81,7 +81,7 @@ public:
QMultiMap<struct wl_client *, QWaylandXdgSurfaceV5 *> m_xdgSurfaces;
QMultiMap<struct wl_client *, QWaylandXdgPopupV5 *> m_xdgPopups;
- QWaylandXdgSurfaceV5 *xdgSurfaceFromSurface(QWaylandSurface *surface);
+ QWaylandXdgSurfaceV5 *xdgSurfaceFromSurface(QWaylandSurface *surface) const;
protected:
void xdg_shell_destroy(Resource *resource) override;
diff --git a/src/compositor/extensions/qwaylandxdgshellv6.cpp b/src/compositor/extensions/qwaylandxdgshellv6.cpp
index f971fe5b4..d1ee45dab 100644
--- a/src/compositor/extensions/qwaylandxdgshellv6.cpp
+++ b/src/compositor/extensions/qwaylandxdgshellv6.cpp
@@ -1802,6 +1802,26 @@ uint QWaylandXdgPopupV6::sendConfigure(const QRect &geometry)
}
/*!
+ * \qmlmethod void QtWaylandCompositor::XdgPopupV6::sendPopupDone()
+ * \since 5.14
+ *
+ * Dismiss the popup. According to the \c xdg-shell-unstable-v6 protocol this should make the
+ * client destroy the popup.
+ */
+
+/*!
+ * \since 5.14
+ *
+ * Dismiss the popup. According to the \c xdg-shell-unstable-v6 protocol this should make the
+ * client destroy the popup.
+ */
+void QWaylandXdgPopupV6::sendPopupDone()
+{
+ Q_D(QWaylandXdgPopupV6);
+ d->send_popup_done();
+}
+
+/*!
* Returns the surface role for the QWaylandPopupV6.
*/
QWaylandSurfaceRole *QWaylandXdgPopupV6::role()
diff --git a/src/compositor/extensions/qwaylandxdgshellv6.h b/src/compositor/extensions/qwaylandxdgshellv6.h
index b9c47c578..64c82306c 100644
--- a/src/compositor/extensions/qwaylandxdgshellv6.h
+++ b/src/compositor/extensions/qwaylandxdgshellv6.h
@@ -246,6 +246,7 @@ public:
QPoint unconstrainedPosition() const;
Q_INVOKABLE uint sendConfigure(const QRect &geometry);
+ Q_REVISION(14) Q_INVOKABLE void sendPopupDone();
static QWaylandSurfaceRole *role();
diff --git a/src/compositor/extensions/qwltexturesharingextension.cpp b/src/compositor/extensions/qwltexturesharingextension.cpp
new file mode 100644
index 000000000..1c15bb49f
--- /dev/null
+++ b/src/compositor/extensions/qwltexturesharingextension.cpp
@@ -0,0 +1,497 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qwltexturesharingextension_p.h"
+
+#include <QWaylandSurface>
+
+#include <QDebug>
+
+#include <QQuickWindow>
+
+#include <QPainter>
+#include <QPen>
+#include <QTimer>
+
+#include <QtGui/private/qtexturefilereader_p.h>
+#include <QtGui/QOpenGLTexture>
+#include <QtGui/QImageReader>
+
+#include <QtQuick/QSGTexture>
+#include <QQmlContext>
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+class SharedTexture : public QSGTexture
+{
+ Q_OBJECT
+public:
+ SharedTexture(QtWayland::ServerBuffer *buffer);
+
+ int textureId() const override;
+ QSize textureSize() const override;
+ bool hasAlphaChannel() const override;
+ bool hasMipmaps() const override;
+
+ void bind() override;
+
+private:
+ void updateGLTexture() const;
+ QtWayland::ServerBuffer *m_buffer = nullptr;
+ mutable QOpenGLTexture *m_tex = nullptr;
+};
+
+SharedTexture::SharedTexture(QtWayland::ServerBuffer *buffer)
+ : m_buffer(buffer), m_tex(nullptr)
+{
+}
+
+int SharedTexture::textureId() const
+{
+ updateGLTexture();
+ return m_tex ? m_tex->textureId() : 0;
+}
+
+QSize SharedTexture::textureSize() const
+{
+ updateGLTexture();
+ return m_tex ? QSize(m_tex->width(), m_tex->height()) : QSize();
+}
+
+bool SharedTexture::hasAlphaChannel() const
+{
+ return true;
+}
+
+bool SharedTexture::hasMipmaps() const
+{
+ updateGLTexture();
+ return m_tex ? (m_tex->mipLevels() > 1) : false;
+}
+
+void SharedTexture::bind()
+{
+ updateGLTexture();
+ if (m_tex)
+ m_tex->bind();
+}
+
+inline void SharedTexture::updateGLTexture() const
+{
+ if (!m_tex && m_buffer)
+ m_tex = m_buffer->toOpenGlTexture();
+}
+
+class SharedTextureFactory : public QQuickTextureFactory
+{
+public:
+ SharedTextureFactory(const QtWayland::ServerBuffer *buffer)
+ : m_buffer(buffer)
+ {
+ }
+
+ ~SharedTextureFactory() override
+ {
+ if (m_buffer)
+ const_cast<QtWayland::ServerBuffer*>(m_buffer)->releaseOpenGlTexture();
+ }
+
+ QSize textureSize() const override
+ {
+ return m_buffer ? m_buffer->size() : QSize();
+ }
+
+ int textureByteCount() const override
+ {
+ return m_buffer ? (m_buffer->size().width() * m_buffer->size().height() * 4) : 0;
+ }
+
+ QSGTexture *createTexture(QQuickWindow *) const override
+ {
+ return new SharedTexture(const_cast<QtWayland::ServerBuffer *>(m_buffer));
+ }
+
+private:
+ const QtWayland::ServerBuffer *m_buffer = nullptr;
+};
+
+class SharedTextureImageResponse : public QQuickImageResponse
+{
+ Q_OBJECT
+public:
+ SharedTextureImageResponse(QWaylandTextureSharingExtension *extension, const QString &id)
+ : m_id(id)
+ {
+ if (extension)
+ doRequest(extension);
+ }
+
+ void doRequest(QWaylandTextureSharingExtension *extension)
+ {
+ m_extension = extension;
+ connect(extension, &QWaylandTextureSharingExtension::bufferResult, this, &SharedTextureImageResponse::doResponse);
+ QMetaObject::invokeMethod(extension, [this] { m_extension->requestBuffer(m_id); }, Qt::AutoConnection);
+ }
+
+ QQuickTextureFactory *textureFactory() const override
+ {
+ if (m_buffer) {
+// qDebug() << "Creating shared buffer texture for" << m_id;
+ return new SharedTextureFactory(m_buffer);
+ }
+// qDebug() << "Shared buffer NOT found for" << m_id;
+ m_errorString = QLatin1Literal("Shared buffer not found");
+ return nullptr;
+ }
+
+ QString errorString() const override
+ {
+ return m_errorString;
+ }
+
+public slots:
+ void doResponse(const QString &key, QtWayland::ServerBuffer *buffer)
+ {
+ if (key != m_id)
+ return; //somebody else's texture
+
+ m_buffer = buffer;
+
+ if (m_extension)
+ disconnect(m_extension, &QWaylandTextureSharingExtension::bufferResult, this, &SharedTextureImageResponse::doResponse);
+
+ emit finished();
+ }
+
+private:
+ QString m_id;
+ QWaylandTextureSharingExtension *m_extension = nullptr;
+ mutable QString m_errorString;
+ QtWayland::ServerBuffer *m_buffer = nullptr;
+};
+
+QWaylandSharedTextureProvider::QWaylandSharedTextureProvider()
+{
+}
+
+QWaylandSharedTextureProvider::~QWaylandSharedTextureProvider()
+{
+}
+
+QQuickImageResponse *QWaylandSharedTextureProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
+{
+ Q_UNUSED(requestedSize);
+
+// qDebug() << "Provider: got request for" << id;
+
+ auto *extension = QWaylandTextureSharingExtension::self();
+ auto *response = new SharedTextureImageResponse(extension, id);
+ if (!extension)
+ m_pendingResponses << response;
+
+ return response;
+}
+
+void QWaylandSharedTextureProvider::setExtensionReady(QWaylandTextureSharingExtension *extension)
+{
+ for (auto *response : qAsConst(m_pendingResponses))
+ response->doRequest(extension);
+ m_pendingResponses.clear();
+ m_pendingResponses.squeeze();
+}
+
+QWaylandTextureSharingExtension *QWaylandTextureSharingExtension::s_self = nullptr; // theoretical race conditions, but OK as long as we don't delete it while we are running
+
+QWaylandTextureSharingExtension::QWaylandTextureSharingExtension()
+{
+ s_self = this;
+}
+
+QWaylandTextureSharingExtension::QWaylandTextureSharingExtension(QWaylandCompositor *compositor)
+ :QWaylandCompositorExtensionTemplate(compositor)
+{
+ s_self = this;
+}
+
+QWaylandTextureSharingExtension::~QWaylandTextureSharingExtension()
+{
+ //qDebug() << Q_FUNC_INFO;
+ //dumpBufferInfo();
+
+ for (auto b : m_server_buffers)
+ delete b.buffer;
+
+ if (s_self == this)
+ s_self = nullptr;
+}
+
+void QWaylandTextureSharingExtension::setImageSearchPath(const QString &path)
+{
+ m_image_dirs = path.split(QLatin1Char(';'));
+
+ for (auto it = m_image_dirs.begin(); it != m_image_dirs.end(); ++it)
+ if (!(*it).endsWith(QLatin1Char('/')))
+ (*it) += QLatin1Char('/');
+}
+
+void QWaylandTextureSharingExtension::initialize()
+{
+ QWaylandCompositorExtensionTemplate::initialize();
+ QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
+ init(compositor->display(), 1);
+
+ QString image_search_path = qEnvironmentVariable("QT_WAYLAND_SHAREDTEXTURE_SEARCH_PATH");
+ if (!image_search_path.isEmpty())
+ setImageSearchPath(image_search_path);
+
+ if (m_image_dirs.isEmpty())
+ m_image_dirs << QLatin1Literal(":/") << QLatin1Literal("./");
+
+ auto suffixes = QTextureFileReader::supportedFileFormats();
+ suffixes.append(QImageReader::supportedImageFormats());
+ for (auto ext : qAsConst(suffixes))
+ m_image_suffixes << QLatin1Char('.') + QString::fromLatin1(ext);
+
+ //qDebug() << "m_image_suffixes" << m_image_suffixes << "m_image_dirs" << m_image_dirs;
+
+ auto *ctx = QQmlEngine::contextForObject(this);
+ if (ctx) {
+ QQmlEngine *engine = ctx->engine();
+ if (engine) {
+ auto *provider = static_cast<QWaylandSharedTextureProvider*>(engine->imageProvider(QLatin1Literal("wlshared")));
+ if (provider)
+ provider->setExtensionReady(this);
+ }
+ }
+}
+
+QString QWaylandTextureSharingExtension::getExistingFilePath(const QString &key) const
+{
+ // The default search path blocks absolute pathnames, but this does not prevent relative
+ // paths containing '../'. We handle that here, at the price of also blocking directory
+ // names ending with two or more dots.
+
+ if (key.contains(QLatin1Literal("../")))
+ return QString();
+
+ for (auto dir : m_image_dirs) {
+ QString path = dir + key;
+ if (QFileInfo::exists(path))
+ return path;
+ }
+
+ for (auto dir : m_image_dirs) {
+ for (auto ext : m_image_suffixes) {
+ QString fp = dir + key + ext;
+ //qDebug() << "trying" << fp;
+ if (QFileInfo::exists(fp))
+ return fp;
+ }
+ }
+ return QString();
+}
+
+QtWayland::ServerBuffer *QWaylandTextureSharingExtension::getBuffer(const QString &key)
+{
+ if (!initServerBufferIntegration())
+ return nullptr;
+
+//qDebug() << "getBuffer" << key;
+
+ QtWayland::ServerBuffer *buffer = nullptr;
+
+ if ((buffer = m_server_buffers.value(key).buffer))
+ return buffer;
+
+ QByteArray pixelData;
+ QSize size;
+ uint glInternalFormat = GL_NONE;
+
+ if (customPixelData(key, &pixelData, &size, &glInternalFormat)) {
+ if (!pixelData.isEmpty()) {
+ buffer = m_server_buffer_integration->createServerBufferFromData(pixelData, size, glInternalFormat);
+ if (!buffer)
+ qWarning() << "QWaylandTextureSharingExtension: could not create buffer from custom data for key:" << key;
+ }
+ } else {
+ QString pathName = getExistingFilePath(key);
+ //qDebug() << "pathName" << pathName;
+ if (pathName.isEmpty())
+ return nullptr;
+
+ buffer = getCompressedBuffer(pathName);
+ //qDebug() << "getCompressedBuffer" << buffer;
+
+ if (!buffer) {
+ QImage img(pathName);
+ if (!img.isNull()) {
+ img = img.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
+ buffer = m_server_buffer_integration->createServerBufferFromImage(img, QtWayland::ServerBuffer::RGBA32);
+ }
+ //qDebug() << "createServerBufferFromImage" << buffer;
+ }
+ }
+ if (buffer)
+ m_server_buffers.insert(key, BufferInfo(buffer));
+
+ //qDebug() << ">>>>" << key << buffer;
+
+ return buffer;
+}
+
+// Compositor requesting image for its own UI
+void QWaylandTextureSharingExtension::requestBuffer(const QString &key)
+{
+ //qDebug() << "requestBuffer" << key;
+
+ if (thread() != QThread::currentThread())
+ qWarning("QWaylandTextureSharingExtension::requestBuffer() called from outside main thread: possible race condition");
+
+ auto *buffer = getBuffer(key);
+
+ if (buffer)
+ m_server_buffers[key].usedLocally = true;
+
+ //dumpBufferInfo();
+
+ emit bufferResult(key, buffer);
+}
+
+void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_request_image(Resource *resource, const QString &key)
+{
+ //qDebug() << "texture_sharing_request_image" << key;
+ auto *buffer = getBuffer(key);
+ if (buffer) {
+ struct ::wl_client *client = resource->client();
+ struct ::wl_resource *buffer_resource = buffer->resourceForClient(client);
+ //qDebug() << " server_buffer resource" << buffer_resource;
+ if (buffer_resource)
+ send_provide_buffer(resource->handle, buffer_resource, key);
+ else
+ qWarning() << "QWaylandTextureSharingExtension: no buffer resource for client";
+ } else {
+ send_image_failed(resource->handle, key, QString());
+ }
+ //dumpBufferInfo();
+}
+
+void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_abandon_image(Resource *resource, const QString &key)
+{
+ Q_UNUSED(resource);
+ Q_UNUSED(key);
+// qDebug() << Q_FUNC_INFO << resource << key;
+ QTimer::singleShot(100, this, &QWaylandTextureSharingExtension::cleanupBuffers);
+}
+
+// A client has disconnected
+void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+// qDebug() << "texture_sharing_destroy_resource" << resource->handle << resource->handle->object.id << "client" << resource->client();
+// dumpBufferInfo();
+ QTimer::singleShot(1000, this, &QWaylandTextureSharingExtension::cleanupBuffers);
+}
+
+bool QWaylandTextureSharingExtension::initServerBufferIntegration()
+{
+ 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("QWaylandTextureSharingExtension initialization failed: No Server Buffer Integration");
+ if (qEnvironmentVariableIsEmpty("QT_WAYLAND_SERVER_BUFFER_INTEGRATION"))
+ qWarning("Set the environment variable 'QT_WAYLAND_SERVER_BUFFER_INTEGRATION' to specify.");
+ return false;
+ }
+ }
+ return true;
+}
+
+QtWayland::ServerBuffer *QWaylandTextureSharingExtension::getCompressedBuffer(const QString &pathName)
+{
+ QFile f(pathName);
+ if (!f.open(QIODevice::ReadOnly))
+ return nullptr;
+
+ QTextureFileReader r(&f, pathName);
+
+ if (!r.canRead())
+ return nullptr;
+
+ QTextureFileData td(r.read());
+
+ //qDebug() << "QWaylandTextureSharingExtension: reading compressed texture data" << td;
+
+ if (!td.isValid()) {
+ qWarning() << "VulkanServerBufferIntegration:" << pathName << "not valid compressed texture";
+ return nullptr;
+ }
+
+ QByteArray pixelData = QByteArray::fromRawData(td.data().constData() + td.dataOffset(), td.dataLength());
+
+ return m_server_buffer_integration->createServerBufferFromData(pixelData, td.size(), td.glInternalFormat());
+}
+
+void QWaylandTextureSharingExtension::cleanupBuffers()
+{
+ for (auto it = m_server_buffers.begin(); it != m_server_buffers.end(); ) {
+ auto *buffer = it.value().buffer;
+ if (!it.value().usedLocally && !buffer->bufferInUse()) {
+ //qDebug() << "deleting buffer for" << it.key();
+ it = m_server_buffers.erase(it);
+ delete buffer;
+ } else {
+ ++it;
+ }
+ }
+ //dumpBufferInfo();
+}
+
+void QWaylandTextureSharingExtension::dumpBufferInfo()
+{
+ qDebug() << "shared buffers:" << m_server_buffers.count();
+ for (auto it = m_server_buffers.cbegin(); it != m_server_buffers.cend(); ++it)
+ qDebug() << " " << it.key() << ":" << it.value().buffer << "in use" << it.value().buffer->bufferInUse() << "usedLocally" << it.value().usedLocally ;
+}
+
+QT_END_NAMESPACE
+
+#include "qwltexturesharingextension.moc"
diff --git a/src/compositor/extensions/qwltexturesharingextension_p.h b/src/compositor/extensions/qwltexturesharingextension_p.h
new file mode 100644
index 000000000..8f442a200
--- /dev/null
+++ b/src/compositor/extensions/qwltexturesharingextension_p.h
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QWLTEXTURESHARINGEXTENSION_P_H
+#define QWLTEXTURESHARINGEXTENSION_P_H
+
+//
+// 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.
+//
+
+#include "wayland-util.h"
+
+#include <QtCore/QMap>
+#include <QtCore/QHash>
+
+#include <QtWaylandCompositor/QWaylandCompositorExtensionTemplate>
+#include <QtWaylandCompositor/QWaylandQuickExtension>
+#include <QtWaylandCompositor/QWaylandCompositor>
+
+#include <QQuickImageProvider>
+
+#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
+#include <QtWaylandCompositor/private/qwlserverbufferintegration_p.h>
+
+#include <QtWaylandCompositor/private/qwayland-server-qt-texture-sharing-unstable-v1.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWayland
+{
+ class ServerBufferIntegration;
+}
+
+class QWaylandTextureSharingExtension;
+class SharedTextureImageResponse;
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandSharedTextureProvider : public QQuickAsyncImageProvider
+{
+public:
+ QWaylandSharedTextureProvider();
+ ~QWaylandSharedTextureProvider() override;
+
+ QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
+ void setExtensionReady(QWaylandTextureSharingExtension *extension);
+
+private:
+ QVector<SharedTextureImageResponse*> m_pendingResponses;
+};
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandTextureSharingExtension
+ : public QWaylandCompositorExtensionTemplate<QWaylandTextureSharingExtension>
+ , public QtWaylandServer::zqt_texture_sharing_v1
+{
+ Q_OBJECT
+ Q_PROPERTY(QString imageSearchPath WRITE setImageSearchPath)
+public:
+ QWaylandTextureSharingExtension();
+ QWaylandTextureSharingExtension(QWaylandCompositor *compositor);
+ ~QWaylandTextureSharingExtension() override;
+
+ void initialize() override;
+
+ void setImageSearchPath(const QString &path);
+
+ static QWaylandTextureSharingExtension *self() { return s_self; }
+
+public slots:
+ void requestBuffer(const QString &key);
+
+signals:
+ void bufferResult(const QString &key, QtWayland::ServerBuffer *buffer);
+
+protected slots:
+ void cleanupBuffers();
+
+protected:
+ void zqt_texture_sharing_v1_request_image(Resource *resource, const QString &key) override;
+ void zqt_texture_sharing_v1_abandon_image(Resource *resource, const QString &key) override;
+ void zqt_texture_sharing_v1_destroy_resource(Resource *resource) override;
+
+ virtual bool customPixelData(const QString &key, QByteArray *data, QSize *size, uint *glInternalFormat)
+ {
+ Q_UNUSED(key);
+ Q_UNUSED(data);
+ Q_UNUSED(size);
+ Q_UNUSED(glInternalFormat);
+ return false;
+ }
+
+private:
+ QtWayland::ServerBuffer *getBuffer(const QString &key);
+ bool initServerBufferIntegration();
+ QtWayland::ServerBuffer *getCompressedBuffer(const QString &key);
+ QString getExistingFilePath(const QString &key) const;
+ void dumpBufferInfo();
+
+ struct BufferInfo
+ {
+ BufferInfo(QtWayland::ServerBuffer *b = nullptr) : buffer(b) {}
+ QtWayland::ServerBuffer *buffer = nullptr;
+ bool usedLocally = false;
+ };
+
+ QStringList m_image_dirs;
+ QStringList m_image_suffixes;
+ QHash<QString, BufferInfo> m_server_buffers;
+ QtWayland::ServerBufferIntegration *m_server_buffer_integration = nullptr;
+
+ static QWaylandTextureSharingExtension *s_self;
+};
+
+Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandTextureSharingExtension)
+
+QT_END_NAMESPACE
+
+#endif // QWLTEXTURESHARINGEXTENSION_P_H
diff --git a/src/compositor/hardware_integration/qwlserverbufferintegration_p.h b/src/compositor/hardware_integration/qwlserverbufferintegration_p.h
index 7d8901d58..e8f774e60 100644
--- a/src/compositor/hardware_integration/qwlserverbufferintegration_p.h
+++ b/src/compositor/hardware_integration/qwlserverbufferintegration_p.h
@@ -75,7 +75,8 @@ class Q_WAYLAND_COMPOSITOR_EXPORT ServerBuffer
public:
enum Format {
RGBA32,
- A8
+ A8,
+ Custom
};
ServerBuffer(const QSize &size, ServerBuffer::Format format);
@@ -85,6 +86,7 @@ public:
virtual bool bufferInUse() { return true; }
virtual QOpenGLTexture *toOpenGlTexture() = 0;
+ virtual void releaseOpenGlTexture() {}
virtual bool isYInverted() const;
@@ -105,6 +107,13 @@ public:
virtual bool supportsFormat(ServerBuffer::Format format) const = 0;
virtual ServerBuffer *createServerBufferFromImage(const QImage &qimage, ServerBuffer::Format format) = 0;
+ virtual ServerBuffer *createServerBufferFromData(const QByteArray &data, const QSize &size, uint glInternalFormat)
+ {
+ Q_UNUSED(data);
+ Q_UNUSED(size);
+ Q_UNUSED(glInternalFormat);
+ return nullptr;
+ }
};
}
diff --git a/src/extensions/README.md b/src/extensions/README.md
index f75e0f236..8024ffa80 100644
--- a/src/extensions/README.md
+++ b/src/extensions/README.md
@@ -3,12 +3,10 @@
The protocol extensions in this folder are considered implementation details of
Qt. I.e. they may removed, renamed or changed without warning.
-## Suffixed protocols
-
-For protocols that have a version suffix, however, we will strive to not break
-backwards compatibility without bumping the suffix (renaming the protocol).
-E.g.: If your client sees a `zqt_key_v1` global, it can safely bind to it:
-the key event will always take the same number of arguments, regardless of
+However, starting with Qt 5.4, we promise not to break backwards compatibility
+without renaming (or removing) the protocol. I.e., if your client sees a global
+from one of these extensions, it can safely bind to it: the existing events
+and requests will always take the same number of arguments, regardless of
compositor version.
This is important also within a Qt-only scope if there are multiple versions of
@@ -16,3 +14,11 @@ Qt on the system. Consider for instance an application statically linked to Qt
(such as Qt Creator) running against a Qt compositor installed by the distro).
In such cases we don't want the compositor and client to disagree on the
protocol definition.
+
+## Protocol versioning.
+
+Protocol extensions in this folder should be versioned (e.g. `zqt_key_v1`).
+If it is necessary to break protocol compatibility, they will be renamed by
+incrementing the version number. For legacy reasons, there are also unversioned
+protocols in this folder. Those protocols should be renamed to be versioned
+if compatibility is broken.
diff --git a/src/extensions/qt-texture-sharing-unstable-v1.xml b/src/extensions/qt-texture-sharing-unstable-v1.xml
new file mode 100644
index 000000000..262ae487c
--- /dev/null
+++ b/src/extensions/qt-texture-sharing-unstable-v1.xml
@@ -0,0 +1,57 @@
+<protocol name="qt_texture_sharing_unstable_v1">
+
+ <copyright>
+ Copyright (C) 2019 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="zqt_texture_sharing_v1" version="1">
+ <request name="request_image">
+ <arg name="key" type="string"/>
+ </request>
+ <request name="abandon_image">
+ <arg name="key" type="string"/>
+ </request>
+ <event name="image_failed">
+ <arg name="key" type="string"/>
+ <arg name="error_message" type="string"/>
+ </event>
+ <event name="provide_buffer">
+ <arg name="buffer" type="object" interface="qt_server_buffer"/>
+ <arg name="key" type="string"/>
+ </event>
+ </interface>
+</protocol>
diff --git a/src/extensions/qt-vulkan-server-buffer-unstable-v1.xml b/src/extensions/qt-vulkan-server-buffer-unstable-v1.xml
new file mode 100644
index 000000000..211d0a7c7
--- /dev/null
+++ b/src/extensions/qt-vulkan-server-buffer-unstable-v1.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="qt_vulkan_server_buffer_unstable_v1">
+ <copyright>
+ Copyright (C) 2019 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="zqt_vulkan_server_buffer_v1" version="1">
+ <description summary="Internal protocol for buffer sharing using Vulkan external memory">
+ This protocol is used internally by Qt for implementing the
+ qt_server_buffer extension on hardware that supports Vulkan external memory .
+
+ This protocol is not part of the Qt API. It exists purely as an
+ implementation detail and may change from version to
+ version without notice, or even be removed.
+ </description>
+ <event name="server_buffer_created">
+ <description summary="vulkan buffer information">
+ Informs the client about a newly created server buffer.
+ The "fd" argument is a POSIX file descriptor representing the
+ underlying resources of a Vulkan device memory object as defined
+ in the GL_EXT_memory_object_fd extension.
+ </description>
+ <arg name="id" type="new_id" interface="qt_server_buffer"/>
+ <arg name="fd" type="fd" summary="GL_EXT_memory_object_fd"/>
+ <arg name="width" type="uint"/>
+ <arg name="height" type="uint"/>
+ <arg name="memory_size" type="uint" summary="size in bytes"/>
+ <arg name="format" type="uint" summary="GL internal format"/>
+ </event>
+ </interface>
+</protocol>
diff --git a/src/hardwareintegration/client/vulkan-server/vulkan-server.pri b/src/hardwareintegration/client/vulkan-server/vulkan-server.pri
new file mode 100644
index 000000000..2e13a87b7
--- /dev/null
+++ b/src/hardwareintegration/client/vulkan-server/vulkan-server.pri
@@ -0,0 +1,12 @@
+INCLUDEPATH += $$PWD
+
+QMAKE_USE += wayland-client
+
+SOURCES += \
+ $$PWD/vulkanserverbufferintegration.cpp
+
+HEADERS += \
+ $$PWD/vulkanserverbufferintegration.h
+
+CONFIG += wayland-scanner
+WAYLANDCLIENTSOURCES += $$PWD/../../../extensions/qt-vulkan-server-buffer-unstable-v1.xml
diff --git a/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp
new file mode 100644
index 000000000..4b2be50e6
--- /dev/null
+++ b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "vulkanserverbufferintegration.h"
+#include <QtWaylandClient/private/qwaylanddisplay_p.h>
+#include <QDebug>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLTexture>
+#include <QtGui/qopengl.h>
+#include <QtGui/QImage>
+#include <QtCore/QCoreApplication>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+static constexpr bool extraDebug =
+#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG
+ true;
+#else
+ false;
+#endif
+
+#define DECL_GL_FUNCTION(name, type) \
+ type name
+
+#define FIND_GL_FUNCTION(name, type) \
+ do { \
+ name = reinterpret_cast<type>(glContext->getProcAddress(#name)); \
+ if (!name) { \
+ qWarning() << "ERROR in GL proc lookup. Could not find " #name; \
+ return false; \
+ } \
+ } while (0)
+
+struct VulkanServerBufferGlFunctions
+{
+ DECL_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC);
+ DECL_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC);
+ DECL_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC);
+ DECL_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC);
+ DECL_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC);
+
+ bool init(QOpenGLContext *glContext)
+ {
+ FIND_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC);
+ FIND_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC);
+ FIND_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC);
+ FIND_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC);
+ FIND_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC);
+
+ return true;
+ }
+ static bool create(QOpenGLContext *glContext);
+};
+
+static VulkanServerBufferGlFunctions *funcs = nullptr;
+
+bool VulkanServerBufferGlFunctions::create(QOpenGLContext *glContext)
+{
+ if (funcs)
+ return true;
+ funcs = new VulkanServerBufferGlFunctions;
+ if (!funcs->init(glContext)) {
+ delete funcs;
+ funcs = nullptr;
+ return false;
+ }
+ return true;
+}
+
+VulkanServerBuffer::VulkanServerBuffer(VulkanServerBufferIntegration *integration, struct ::qt_server_buffer *id,
+ int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format)
+ : m_integration(integration)
+ , m_server_buffer(id)
+ , m_fd(fd)
+ , m_memorySize(memory_size)
+ , m_internalFormat(format)
+{
+ m_size = QSize(width, height);
+}
+
+VulkanServerBuffer::~VulkanServerBuffer()
+{
+ if (QCoreApplication::closingDown())
+ return; // can't trust anything at this point
+
+ if (m_texture) { //only do gl cleanup if import has been called
+ m_integration->deleteGLTextureWhenPossible(m_texture);
+
+ if (extraDebug) qDebug() << "glDeleteMemoryObjectsEXT" << m_memoryObject;
+ funcs->glDeleteMemoryObjectsEXT(1, &m_memoryObject);
+ }
+ qt_server_buffer_release(m_server_buffer);
+ qt_server_buffer_destroy(m_server_buffer);
+}
+
+void VulkanServerBuffer::import()
+{
+ if (m_texture)
+ return;
+
+ if (extraDebug) qDebug() << "importing" << m_fd << hex << glGetError();
+
+ auto *glContext = QOpenGLContext::currentContext();
+ if (!glContext)
+ return;
+
+ if (!funcs && !VulkanServerBufferGlFunctions::create(glContext))
+ return;
+
+ funcs->glCreateMemoryObjectsEXT(1, &m_memoryObject);
+ if (extraDebug) qDebug() << "glCreateMemoryObjectsEXT" << hex << glGetError();
+ funcs->glImportMemoryFdEXT(m_memoryObject, m_memorySize, GL_HANDLE_TYPE_OPAQUE_FD_EXT, m_fd);
+ if (extraDebug) qDebug() << "glImportMemoryFdEXT" << hex << glGetError();
+
+
+ m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+ m_texture->create();
+
+ if (extraDebug) qDebug() << "created texture" << m_texture->textureId() << hex << glGetError();
+
+ m_texture->bind();
+ if (extraDebug) qDebug() << "bound texture" << hex << glGetError();
+ funcs->glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, m_internalFormat, m_size.width(), m_size.height(), m_memoryObject, 0 );
+ if (extraDebug) qDebug() << "glTexStorageMem2DEXT" << hex << glGetError();
+ if (extraDebug) qDebug() << "format" << hex << m_internalFormat << GL_RGBA8;
+}
+
+QOpenGLTexture *VulkanServerBuffer::toOpenGlTexture()
+{
+ m_integration->deleteOrphanedTextures();
+ if (!m_texture)
+ import();
+ return m_texture;
+}
+
+void VulkanServerBufferIntegration::initialize(QWaylandDisplay *display)
+{
+ m_display = display;
+ display->addRegistryListener(&wlDisplayHandleGlobal, this);
+}
+
+QWaylandServerBuffer *VulkanServerBufferIntegration::serverBuffer(struct qt_server_buffer *buffer)
+{
+ return static_cast<QWaylandServerBuffer *>(qt_server_buffer_get_user_data(buffer));
+}
+
+void VulkanServerBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version)
+{
+ Q_UNUSED(version);
+ if (interface == "zqt_vulkan_server_buffer_v1") {
+ auto *integration = static_cast<VulkanServerBufferIntegration *>(data);
+ integration->QtWayland::zqt_vulkan_server_buffer_v1::init(registry, id, 1);
+ }
+}
+
+void VulkanServerBufferIntegration::zqt_vulkan_server_buffer_v1_server_buffer_created(qt_server_buffer *id, int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format)
+{
+ if (extraDebug) qDebug() << "vulkan_server_buffer_server_buffer_created" << fd;
+ auto *server_buffer = new VulkanServerBuffer(this, id, fd, width, height, memory_size, format);
+ qt_server_buffer_set_user_data(id, server_buffer);
+}
+
+void VulkanServerBufferIntegration::deleteOrphanedTextures()
+{
+ if (!QOpenGLContext::currentContext()) {
+ qWarning("VulkanServerBufferIntegration::deleteOrphanedTextures with no current context!");
+ return;
+ }
+ qDeleteAll(orphanedTextures);
+ orphanedTextures.clear();
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h
new file mode 100644
index 000000000..7add74269
--- /dev/null
+++ b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 VULKANSERVERBUFFERINTEGRATION_H
+#define VULKANSERVERBUFFERINTEGRATION_H
+
+#include <QtWaylandClient/private/qwayland-wayland.h>
+#include "qwayland-qt-vulkan-server-buffer-unstable-v1.h"
+#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h>
+
+#include "vulkanserverbufferintegration.h"
+#include <QtWaylandClient/private/qwaylanddisplay_p.h>
+#include <QtCore/QTextStream>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+class VulkanServerBufferIntegration;
+
+class VulkanServerBuffer : public QWaylandServerBuffer
+{
+public:
+ VulkanServerBuffer(VulkanServerBufferIntegration *integration, struct ::qt_server_buffer *id, int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format);
+ ~VulkanServerBuffer() override;
+ QOpenGLTexture* toOpenGlTexture() override;
+
+private:
+ void import();
+
+ VulkanServerBufferIntegration *m_integration = nullptr;
+ struct ::qt_server_buffer *m_server_buffer = nullptr;
+ QOpenGLTexture *m_texture = nullptr;
+ int m_fd = -1;
+ uint m_memorySize = 0;
+ uint m_internalFormat = 0;
+ GLuint m_memoryObject = 0;
+};
+
+class VulkanServerBufferIntegration
+ : public QWaylandServerBufferIntegration
+ , public QtWayland::zqt_vulkan_server_buffer_v1
+{
+public:
+ void initialize(QWaylandDisplay *display) override;
+
+ QWaylandServerBuffer *serverBuffer(struct qt_server_buffer *buffer) override;
+
+ void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { orphanedTextures << texture; }
+ void deleteOrphanedTextures();
+
+protected:
+ void zqt_vulkan_server_buffer_v1_server_buffer_created(qt_server_buffer *id, int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_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;
+ QVector<QOpenGLTexture *> orphanedTextures;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp
index 24dadff4d..030422c56 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp
@@ -137,9 +137,8 @@ void QWaylandEglWindow::updateSurface(bool create)
m_resize = true;
}
- } else if (create && wl_surface::isInitialized()) {
- ::wl_surface *wlSurface = wl_surface::object();
- m_waylandEglWindow = wl_egl_window_create(wlSurface, sizeWithMargins.width(), sizeWithMargins.height());
+ } else if (create && wlSurface()) {
+ m_waylandEglWindow = wl_egl_window_create(wlSurface(), sizeWithMargins.width(), sizeWithMargins.height());
}
if (!m_eglSurface && m_waylandEglWindow && create) {
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
index bc1f74af9..5bd2760d0 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
@@ -192,7 +192,7 @@ public:
}
void blit(QWaylandEglWindow *window)
{
- Q_ASSERT(window->wl_surface::isInitialized());
+ Q_ASSERT(window->wlSurface());
QOpenGLTextureCache *cache = QOpenGLTextureCache::cacheForContext(m_context->context());
QSize surfaceSize = window->surfaceSize();
diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkan-server.pri b/src/hardwareintegration/compositor/vulkan-server/vulkan-server.pri
new file mode 100644
index 000000000..63a96ad0f
--- /dev/null
+++ b/src/hardwareintegration/compositor/vulkan-server/vulkan-server.pri
@@ -0,0 +1,16 @@
+INCLUDEPATH += $$PWD $$PWD/../../../3rdparty/util
+
+QT += vulkan_support-private
+
+QMAKE_USE_PRIVATE += wayland-server
+
+SOURCES += \
+ $$PWD/vulkanserverbufferintegration.cpp \
+ $$PWD/vulkanwrapper.cpp
+
+HEADERS += \
+ $$PWD/vulkanserverbufferintegration.h \
+ $$PWD/vulkanwrapper.h
+
+CONFIG += wayland-scanner
+WAYLANDSERVERSOURCES += $$PWD/../../../extensions/qt-vulkan-server-buffer-unstable-v1.xml
diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp
new file mode 100644
index 000000000..df197ca23
--- /dev/null
+++ b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp
@@ -0,0 +1,322 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "vulkanserverbufferintegration.h"
+
+#include "vulkanwrapper.h"
+
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLTexture>
+#include <QtGui/QOffscreenSurface>
+#include <QtGui/qopengl.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+static constexpr bool extraDebug = false;
+
+#define DECL_GL_FUNCTION(name, type) \
+ type name
+
+#define FIND_GL_FUNCTION(name, type) \
+ do { \
+ name = reinterpret_cast<type>(glContext->getProcAddress(#name)); \
+ if (!name) { \
+ qWarning() << "ERROR in GL proc lookup. Could not find " #name; \
+ return false; \
+ } \
+ } while (0)
+
+struct VulkanServerBufferGlFunctions
+{
+ DECL_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC);
+ DECL_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC);
+ //DECL_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC);
+ DECL_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC);
+ DECL_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC);
+
+ bool init(QOpenGLContext *glContext)
+ {
+ FIND_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC);
+ FIND_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC);
+ //FIND_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC);
+ FIND_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC);
+ FIND_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC);
+
+ return true;
+ }
+ static bool create(QOpenGLContext *glContext);
+};
+
+static VulkanServerBufferGlFunctions *funcs = nullptr;
+
+//RAII
+class CurrentContext
+{
+public:
+ CurrentContext()
+ {
+ if (!QOpenGLContext::currentContext()) {
+ if (QOpenGLContext::globalShareContext()) {
+ if (!localContext) {
+ localContext = new QOpenGLContext;
+ localContext->setShareContext(QOpenGLContext::globalShareContext());
+ localContext->create();
+ }
+ if (!offscreenSurface) {
+ offscreenSurface = new QOffscreenSurface;
+ offscreenSurface->setFormat(localContext->format());
+ offscreenSurface->create();
+ }
+ localContext->makeCurrent(offscreenSurface);
+ localContextInUse = true;
+ } else {
+ qCritical("VulkanServerBufferIntegration: no globalShareContext");
+ }
+ }
+ }
+ ~CurrentContext()
+ {
+ if (localContextInUse)
+ localContext->doneCurrent();
+ }
+ QOpenGLContext *context() { return localContextInUse ? localContext : QOpenGLContext::currentContext(); }
+private:
+ static QOpenGLContext *localContext;
+ static QOffscreenSurface *offscreenSurface;
+ bool localContextInUse = false;
+};
+
+QOpenGLContext *CurrentContext::localContext = nullptr;
+QOffscreenSurface *CurrentContext::offscreenSurface = nullptr;
+
+bool VulkanServerBufferGlFunctions::create(QOpenGLContext *glContext)
+{
+ if (funcs)
+ return true;
+ funcs = new VulkanServerBufferGlFunctions;
+ if (!funcs->init(glContext)) {
+ delete funcs;
+ funcs = nullptr;
+ return false;
+ }
+ return true;
+}
+
+VulkanServerBuffer::VulkanServerBuffer(VulkanServerBufferIntegration *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_format = format;
+ switch (m_format) {
+ case RGBA32:
+ m_glInternalFormat = GL_RGBA8;
+ break;
+ // case A8:
+ // m_glInternalFormat = GL_R8;
+ // break;
+ default:
+ qWarning("VulkanServerBuffer: unsupported format");
+ m_glInternalFormat = GL_RGBA8;
+ break;
+ }
+
+ auto vulkanWrapper = m_integration->vulkanWrapper();
+ m_vImage = vulkanWrapper->createTextureImage(qimage);
+ if (m_vImage)
+ m_fd = vulkanWrapper->getImageInfo(m_vImage, &m_memorySize);
+}
+
+VulkanServerBuffer::VulkanServerBuffer(VulkanServerBufferIntegration *integration, VulkanImageWrapper *vImage, uint glInternalFormat, const QSize &size)
+ : QtWayland::ServerBuffer(size, QtWayland::ServerBuffer::Custom)
+ , m_integration(integration)
+ , m_width(size.width())
+ , m_height(size.height())
+ , m_vImage(vImage)
+ , m_glInternalFormat(glInternalFormat)
+{
+ auto vulkanWrapper = m_integration->vulkanWrapper();
+ m_fd = vulkanWrapper->getImageInfo(m_vImage, &m_memorySize);
+}
+
+VulkanServerBuffer::~VulkanServerBuffer()
+{
+ delete m_texture; //this is always nullptr for now
+ auto vulkanWrapper = m_integration->vulkanWrapper();
+ vulkanWrapper->freeTextureImage(m_vImage);
+}
+
+struct ::wl_resource *VulkanServerBuffer::resourceForClient(struct ::wl_client *client)
+{
+ auto *bufferResource = resourceMap().value(client);
+ if (!bufferResource) {
+ auto integrationResource = m_integration->resourceMap().value(client);
+ if (!integrationResource) {
+ qWarning("VulkanServerBuffer::resourceForClient: Trying to get resource for ServerBuffer. But client is not bound to the vulkan 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_fd, m_width, m_height, m_memorySize, m_glInternalFormat);
+ return resource->handle;
+ }
+ return bufferResource->handle;
+}
+
+QOpenGLTexture *VulkanServerBuffer::toOpenGlTexture()
+{
+ if (m_texture && m_texture->isCreated())
+ return m_texture;
+
+ CurrentContext current;
+
+ if (!funcs && !VulkanServerBufferGlFunctions::create(current.context()))
+ return nullptr;
+
+ funcs->glCreateMemoryObjectsEXT(1, &m_memoryObject);
+ if (extraDebug) qDebug() << "glCreateMemoryObjectsEXT" << hex << glGetError();
+
+
+ int dupfd = fcntl(m_fd, F_DUPFD_CLOEXEC, 0);
+ if (dupfd < 0) {
+ perror("VulkanServerBuffer::toOpenGlTexture() Could not dup fd:");
+ return nullptr;
+ }
+
+ funcs->glImportMemoryFdEXT(m_memoryObject, m_memorySize, GL_HANDLE_TYPE_OPAQUE_FD_EXT, dupfd);
+ if (extraDebug) qDebug() << "glImportMemoryFdEXT" << hex << glGetError();
+
+
+ if (!m_texture)
+ m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+ m_texture->create();
+
+ GLuint texId = m_texture->textureId();
+ if (extraDebug) qDebug() << "created texture" << texId << hex << glGetError();
+
+ m_texture->bind();
+ if (extraDebug) qDebug() << "bound texture" << texId << hex << glGetError();
+ funcs->glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, m_glInternalFormat, m_size.width(), m_size.height(), m_memoryObject, 0 );
+ if (extraDebug) qDebug() << "glTexStorageMem2DEXT" << hex << glGetError();
+ if (extraDebug) qDebug() << "format" << hex << m_glInternalFormat << GL_RGBA8;
+
+
+ return m_texture;
+}
+
+void VulkanServerBuffer::releaseOpenGlTexture()
+{
+ if (!m_texture || !m_texture->isCreated())
+ return;
+
+ CurrentContext current;
+ m_texture->destroy();
+ funcs->glDeleteMemoryObjectsEXT(1, &m_memoryObject);
+}
+
+
+bool VulkanServerBuffer::bufferInUse()
+{
+ return (m_texture && m_texture->isCreated()) || resourceMap().count() > 0;
+}
+
+void VulkanServerBuffer::server_buffer_release(Resource *resource)
+{
+ qCDebug(qLcWaylandCompositorHardwareIntegration) << "server_buffer RELEASE resource" << resource->handle << wl_resource_get_id(resource->handle) << "for client" << resource->client();
+ wl_resource_destroy(resource->handle);
+}
+
+VulkanServerBufferIntegration::VulkanServerBufferIntegration()
+{
+}
+
+VulkanServerBufferIntegration::~VulkanServerBufferIntegration()
+{
+}
+
+void VulkanServerBufferIntegration::initializeHardware(QWaylandCompositor *compositor)
+{
+ Q_ASSERT(QGuiApplication::platformNativeInterface());
+
+ QtWaylandServer::zqt_vulkan_server_buffer_v1::init(compositor->display(), 1);
+}
+
+bool VulkanServerBufferIntegration::supportsFormat(QtWayland::ServerBuffer::Format format) const
+{
+ switch (format) {
+ case QtWayland::ServerBuffer::RGBA32:
+ return true;
+ case QtWayland::ServerBuffer::A8:
+ return false;
+ default:
+ return false;
+ }
+}
+
+QtWayland::ServerBuffer *VulkanServerBufferIntegration::createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format)
+{
+ if (!m_vulkanWrapper) {
+ CurrentContext current;
+ m_vulkanWrapper = new VulkanWrapper(current.context());
+ }
+ return new VulkanServerBuffer(this, qimage, format);
+}
+
+QtWayland::ServerBuffer *VulkanServerBufferIntegration::createServerBufferFromData(const QByteArray &data, const QSize &size, uint glInternalFormat)
+{
+ if (!m_vulkanWrapper) {
+ CurrentContext current;
+ m_vulkanWrapper = new VulkanWrapper(current.context());
+ }
+
+ auto *vImage = m_vulkanWrapper->createTextureImageFromData(reinterpret_cast<const uchar*>(data.constData()), data.size(), size, glInternalFormat);
+
+ if (vImage)
+ return new VulkanServerBuffer(this, vImage, glInternalFormat, size);
+
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "could not load compressed texture";
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.h b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.h
new file mode 100644
index 000000000..7246e36df
--- /dev/null
+++ b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 VULKANSERVERBUFFERINTEGRATION_H
+#define VULKANSERVERBUFFERINTEGRATION_H
+
+#include <QtWaylandCompositor/private/qwlserverbufferintegration_p.h>
+
+#include "qwayland-server-qt-vulkan-server-buffer-unstable-v1.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 VulkanServerBufferIntegration;
+class VulkanWrapper;
+struct VulkanImageWrapper;
+
+class VulkanServerBuffer : public QtWayland::ServerBuffer, public QtWaylandServer::qt_server_buffer
+{
+public:
+ VulkanServerBuffer(VulkanServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format);
+ VulkanServerBuffer(VulkanServerBufferIntegration *integration, VulkanImageWrapper *vImage, uint glInternalFormat, const QSize &size);
+ ~VulkanServerBuffer() override;
+
+ struct ::wl_resource *resourceForClient(struct ::wl_client *) override;
+ bool bufferInUse() override;
+ QOpenGLTexture *toOpenGlTexture() override;
+ void releaseOpenGlTexture() override;
+
+protected:
+ void server_buffer_release(Resource *resource) override;
+
+private:
+ VulkanServerBufferIntegration *m_integration = nullptr;
+
+ int m_width;
+ int m_height;
+ int m_memorySize;
+ int m_fd = -1;
+ VulkanImageWrapper *m_vImage = nullptr;
+ QOpenGLTexture *m_texture = nullptr;
+ uint m_glInternalFormat;
+ GLuint m_memoryObject;
+};
+
+class VulkanServerBufferIntegration :
+ public QtWayland::ServerBufferIntegration,
+ public QtWaylandServer::zqt_vulkan_server_buffer_v1
+{
+public:
+ VulkanServerBufferIntegration();
+ ~VulkanServerBufferIntegration() override;
+
+ VulkanWrapper *vulkanWrapper() const { return m_vulkanWrapper; }
+
+ void initializeHardware(QWaylandCompositor *) override;
+
+ bool supportsFormat(QtWayland::ServerBuffer::Format format) const override;
+ QtWayland::ServerBuffer *createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format) override;
+ QtWayland::ServerBuffer *createServerBufferFromData(const QByteArray &data, const QSize &size, uint glInternalFormat) override;
+
+private:
+ VulkanWrapper *m_vulkanWrapper = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp
new file mode 100644
index 000000000..771e1015c
--- /dev/null
+++ b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp
@@ -0,0 +1,731 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+// NOTE: Some of the code below is adapted from the public domain code at https://vulkan-tutorial.com/
+
+#define GL_GLEXT_PROTOTYPES
+
+#include "vulkanwrapper.h"
+
+#include <QImage>
+#include <QOpenGLContext>
+#include <QtGui/qopengl.h>
+#include <QtVulkanSupport/private/qvkconvenience_p.h>
+
+#include <set>
+
+#include <unistd.h>
+
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+static constexpr bool extraDebug = false;
+
+#define DECL_VK_FUNCTION(name) \
+ PFN_ ## name name = nullptr;
+
+#define IMPL_VK_FUNCTION(name) \
+ name = reinterpret_cast<PFN_ ## name>(f_glGetVkProcAddrNV(#name)); \
+ if (!name) { \
+ qCritical() << "ERROR in Vulkan proc lookup. Could not find " #name; \
+ }
+
+struct QueueFamilyIndices {
+ int graphicsFamily = -1;
+ int presentFamily = -1;
+
+ bool isComplete() {
+ return graphicsFamily >= 0 && presentFamily >= 0;
+ }
+};
+
+class VulkanWrapperPrivate
+{
+public:
+ explicit VulkanWrapperPrivate(QOpenGLContext *glContext);
+
+ VulkanImageWrapper *createTextureImage(const QImage &img);
+ VulkanImageWrapper *createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, VkFormat vkFormat);
+
+ void freeTextureImage(VulkanImageWrapper *imageWrapper);
+
+private:
+ DECL_VK_FUNCTION(vkAllocateCommandBuffers);
+ DECL_VK_FUNCTION(vkAllocateMemory);
+ DECL_VK_FUNCTION(vkBeginCommandBuffer);
+ DECL_VK_FUNCTION(vkBindImageMemory);
+ DECL_VK_FUNCTION(vkCmdCopyBufferToImage);
+ DECL_VK_FUNCTION(vkCmdPipelineBarrier);
+ DECL_VK_FUNCTION(vkCreateImage);
+ DECL_VK_FUNCTION(vkDestroyImage);
+ DECL_VK_FUNCTION(vkDestroyBuffer);
+ DECL_VK_FUNCTION(vkEndCommandBuffer);
+ DECL_VK_FUNCTION(vkFreeCommandBuffers);
+ DECL_VK_FUNCTION(vkFreeMemory);
+ DECL_VK_FUNCTION(vkGetImageMemoryRequirements);
+ DECL_VK_FUNCTION(vkGetPhysicalDeviceMemoryProperties);
+ DECL_VK_FUNCTION(vkMapMemory);
+ DECL_VK_FUNCTION(vkQueueSubmit);
+ DECL_VK_FUNCTION(vkQueueWaitIdle);
+ DECL_VK_FUNCTION(vkUnmapMemory);
+ DECL_VK_FUNCTION(vkCreateBuffer);
+ DECL_VK_FUNCTION(vkGetBufferMemoryRequirements);
+ DECL_VK_FUNCTION(vkBindBufferMemory);
+
+ DECL_VK_FUNCTION(vkCreateInstance);
+ DECL_VK_FUNCTION(vkEnumeratePhysicalDevices);
+ DECL_VK_FUNCTION(vkGetPhysicalDeviceProperties);
+ DECL_VK_FUNCTION(vkCreateDevice);
+ DECL_VK_FUNCTION(vkGetPhysicalDeviceFormatProperties);
+
+ DECL_VK_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties);
+ DECL_VK_FUNCTION(vkCreateCommandPool);
+
+ DECL_VK_FUNCTION(vkGetDeviceQueue);
+ DECL_VK_FUNCTION(vkGetImageMemoryRequirements2KHR);
+ DECL_VK_FUNCTION(vkGetMemoryFdKHR);
+
+ //DECL_VK_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR);
+
+ void initFunctions(PFNGLGETVKPROCADDRNVPROC f_glGetVkProcAddrNV) {
+ IMPL_VK_FUNCTION(vkAllocateCommandBuffers);
+ IMPL_VK_FUNCTION(vkAllocateMemory);
+ IMPL_VK_FUNCTION(vkBeginCommandBuffer);
+ IMPL_VK_FUNCTION(vkBindImageMemory);
+ IMPL_VK_FUNCTION(vkCmdCopyBufferToImage);
+ IMPL_VK_FUNCTION(vkCmdPipelineBarrier);
+ IMPL_VK_FUNCTION(vkCreateImage);
+ IMPL_VK_FUNCTION(vkDestroyImage);
+ IMPL_VK_FUNCTION(vkDestroyBuffer);
+ IMPL_VK_FUNCTION(vkEndCommandBuffer);
+ IMPL_VK_FUNCTION(vkFreeCommandBuffers);
+ IMPL_VK_FUNCTION(vkFreeMemory);
+ IMPL_VK_FUNCTION(vkGetImageMemoryRequirements);
+ IMPL_VK_FUNCTION(vkGetPhysicalDeviceMemoryProperties);
+ IMPL_VK_FUNCTION(vkMapMemory);
+ IMPL_VK_FUNCTION(vkQueueSubmit);
+ IMPL_VK_FUNCTION(vkQueueWaitIdle);
+ IMPL_VK_FUNCTION(vkUnmapMemory);
+ IMPL_VK_FUNCTION(vkCreateBuffer);
+ IMPL_VK_FUNCTION(vkGetBufferMemoryRequirements);
+ IMPL_VK_FUNCTION(vkBindBufferMemory);
+
+ IMPL_VK_FUNCTION(vkCreateInstance);
+ IMPL_VK_FUNCTION(vkEnumeratePhysicalDevices);
+ IMPL_VK_FUNCTION(vkGetPhysicalDeviceProperties);
+ IMPL_VK_FUNCTION(vkCreateDevice);
+ IMPL_VK_FUNCTION(vkGetPhysicalDeviceFormatProperties);
+
+ IMPL_VK_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties);
+ IMPL_VK_FUNCTION(vkCreateCommandPool);
+
+ IMPL_VK_FUNCTION(vkGetDeviceQueue);
+ IMPL_VK_FUNCTION(vkGetImageMemoryRequirements2KHR);
+ IMPL_VK_FUNCTION(vkGetMemoryFdKHR);
+
+ //IMPL_VK_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR);
+ }
+
+ int findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
+
+ VulkanImageWrapper *createImage(VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, const QSize &size, int memSize);
+ bool transitionImageLayout(VkImage image, VkFormat /*format*/, VkImageLayout oldLayout, VkImageLayout newLayout);
+ bool createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
+ VkCommandBuffer beginSingleTimeCommands();
+ void endSingleTimeCommands(VkCommandBuffer commandBuffer);
+ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height);
+ void createCommandPool();
+ QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
+ bool createLogicalDevice();
+
+private:
+ VkInstance m_instance = VK_NULL_HANDLE;
+ VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
+ VkDevice m_device = VK_NULL_HANDLE;
+ VkCommandPool m_commandPool = VK_NULL_HANDLE;
+
+ VkQueue m_graphicsQueue = VK_NULL_HANDLE;
+
+ bool m_initFailed = false;
+};
+
+struct VulkanImageWrapper
+{
+ VkImage textureImage = VK_NULL_HANDLE;
+ int imgMemSize = -1;
+ QSize imgSize;
+ int imgFd = -1;
+ VkDeviceMemory textureImageMemory = VK_NULL_HANDLE;
+};
+
+int VulkanWrapperPrivate::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
+{
+ VkPhysicalDeviceMemoryProperties memProperties;
+ vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties);
+
+ for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
+ if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
+ return i;
+ }
+ }
+
+ qCritical("VulkanWrapper: failed to find suitable memory type!");
+ return -1;
+}
+
+
+VulkanImageWrapper *VulkanWrapperPrivate::createImage(VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, const QSize &size, int memSize)
+{
+ VkImageCreateInfo imageInfo = {};
+ imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.extent.width = size.width();
+ imageInfo.extent.height = size.height();
+ imageInfo.extent.depth = 1;
+ imageInfo.mipLevels = 1;
+ imageInfo.arrayLayers = 1;
+ imageInfo.format = format;
+ imageInfo.tiling = tiling;
+ imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ imageInfo.usage = usage;
+ imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+ VkImage image = VK_NULL_HANDLE;
+
+ if (vkCreateImage(m_device, &imageInfo, nullptr, &image) != VK_SUCCESS) {
+ qCritical("VulkanWrapper: failed to create image!");
+ return nullptr;
+ }
+
+ QScopedPointer<VulkanImageWrapper> imageWrapper(new VulkanImageWrapper);
+ imageWrapper->textureImage = image;
+ imageWrapper->imgMemSize = memSize;
+ imageWrapper->imgSize = size;
+
+ VkMemoryRequirements memRequirements;
+ vkGetImageMemoryRequirements(m_device, image, &memRequirements);
+
+ VkExportMemoryAllocateInfoKHR exportAllocInfo = {};
+ exportAllocInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;
+ exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+ VkMemoryAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.allocationSize = memRequirements.size;
+ int memoryType = findMemoryType(memRequirements.memoryTypeBits, properties);
+ if (memoryType < 0)
+ return nullptr;
+ allocInfo.memoryTypeIndex = memoryType;
+ allocInfo.pNext = &exportAllocInfo;
+
+ if (vkAllocateMemory(m_device, &allocInfo, nullptr, &imageWrapper->textureImageMemory) != VK_SUCCESS) {
+ qCritical("VulkanWrapper: failed to allocate image memory!");
+ return nullptr;
+ }
+
+ int res = vkBindImageMemory(m_device, image, imageWrapper->textureImageMemory, 0);
+ Q_UNUSED(res);
+ if (extraDebug) qDebug() << "vkBindImageMemory res" << res;
+
+ VkMemoryGetFdInfoKHR memoryFdInfo = {};
+ memoryFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;
+ memoryFdInfo.memory = imageWrapper->textureImageMemory;
+ memoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+ res = vkGetMemoryFdKHR(m_device, &memoryFdInfo, &imageWrapper->imgFd);
+ if (extraDebug) qDebug() << "vkGetMemoryFdKHR res" << res << "fd" << imageWrapper->imgFd;
+
+ return imageWrapper.take();
+}
+
+
+bool VulkanWrapperPrivate::transitionImageLayout(VkImage image, VkFormat /*format*/, VkImageLayout oldLayout, VkImageLayout newLayout)
+{
+ VkCommandBuffer commandBuffer = beginSingleTimeCommands();
+
+ VkImageMemoryBarrier barrier = {};
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.oldLayout = oldLayout;
+ barrier.newLayout = newLayout;
+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.image = image;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.levelCount = 1;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = 1;
+
+ VkPipelineStageFlags sourceStage;
+ VkPipelineStageFlags destinationStage;
+
+ if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
+ barrier.srcAccessMask = 0;
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+ sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+
+ sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ } else {
+ qCritical("VulkanWrapper: unsupported layout transition!");
+ return false;
+ }
+
+ vkCmdPipelineBarrier(
+ commandBuffer,
+ sourceStage, destinationStage,
+ 0,
+ 0, nullptr,
+ 0, nullptr,
+ 1, &barrier
+ );
+
+ endSingleTimeCommands(commandBuffer);
+ return true;
+}
+
+bool VulkanWrapperPrivate::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory)
+{
+ VkBufferCreateInfo bufferInfo = {};
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferInfo.size = size;
+ bufferInfo.usage = usage;
+ bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+ if (vkCreateBuffer(m_device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) {
+ qCritical("VulkanWrapper: failed to create buffer!");
+ return false;
+ }
+
+ VkMemoryRequirements memRequirements;
+ vkGetBufferMemoryRequirements(m_device, buffer, &memRequirements);
+
+ VkMemoryAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.allocationSize = memRequirements.size;
+ allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
+
+ if (vkAllocateMemory(m_device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) {
+ qCritical("VulkanWrapper: failed to allocate buffer memory!");
+ return false;
+ }
+
+ vkBindBufferMemory(m_device, buffer, bufferMemory, 0);
+ return true;
+}
+
+
+VkCommandBuffer VulkanWrapperPrivate::beginSingleTimeCommands()
+{
+ VkCommandBufferAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ allocInfo.commandPool = m_commandPool;
+ allocInfo.commandBufferCount = 1;
+
+ if (extraDebug) qDebug() << "allocating...";
+
+ VkCommandBuffer commandBuffer;
+ int res = vkAllocateCommandBuffers(m_device, &allocInfo, &commandBuffer);
+ Q_UNUSED(res);
+ if (extraDebug) qDebug() << "vkAllocateCommandBuffers res" << res;
+
+ VkCommandBufferBeginInfo beginInfo = {};
+ beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+
+ res = vkBeginCommandBuffer(commandBuffer, &beginInfo);
+ if (extraDebug) qDebug() << "BEGIN res" << res;
+
+ return commandBuffer;
+}
+
+void VulkanWrapperPrivate::endSingleTimeCommands(VkCommandBuffer commandBuffer)
+{
+ int res = vkEndCommandBuffer(commandBuffer);
+ Q_UNUSED(res);
+ if (extraDebug) qDebug() << "END res" << res;
+
+ VkSubmitInfo submitInfo = {};
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &commandBuffer;
+
+ vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
+ vkQueueWaitIdle(m_graphicsQueue);
+
+ vkFreeCommandBuffers(m_device, m_commandPool, 1, &commandBuffer);
+}
+
+void VulkanWrapperPrivate::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
+{
+ VkCommandBuffer commandBuffer = beginSingleTimeCommands();
+
+ VkBufferImageCopy region = {};
+ region.bufferOffset = 0;
+ region.bufferRowLength = 0;
+ region.bufferImageHeight = 0;
+ region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.imageSubresource.mipLevel = 0;
+ region.imageSubresource.baseArrayLayer = 0;
+ region.imageSubresource.layerCount = 1;
+ region.imageOffset = {0, 0, 0};
+ region.imageExtent = {
+ width,
+ height,
+ 1
+ };
+
+ vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
+
+ endSingleTimeCommands(commandBuffer);
+}
+
+void VulkanWrapperPrivate::createCommandPool()
+{
+ QueueFamilyIndices queueFamilyIndices = findQueueFamilies(m_physicalDevice);
+
+ VkCommandPoolCreateInfo poolInfo = {};
+ poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily;
+
+ if (vkCreateCommandPool(m_device, &poolInfo, nullptr, &m_commandPool) != VK_SUCCESS) {
+ m_initFailed = true;
+ qCritical("VulkanWrapperPrivate: could not create command pool");
+ }
+}
+
+QueueFamilyIndices VulkanWrapperPrivate::findQueueFamilies(VkPhysicalDevice device)
+{
+ QueueFamilyIndices indices;
+
+ uint32_t queueFamilyCount = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
+ if (extraDebug) qDebug() << "queueFamilyCount" << queueFamilyCount;
+
+
+ std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
+ vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
+
+#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG
+ for (const auto& queueFamily : queueFamilies) {
+ qDebug() << "....q" << "count" << queueFamily.queueCount << queueFamily.timestampValidBits << hex << queueFamily.queueFlags;
+ }
+#endif
+
+ int i = 0;
+ for (const auto& queueFamily : queueFamilies) {
+ if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ indices.graphicsFamily = i;
+ break;
+ }
+ i++;
+ }
+
+ return indices;
+}
+
+bool VulkanWrapperPrivate::createLogicalDevice()
+{
+ QueueFamilyIndices indices = findQueueFamilies(m_physicalDevice);
+
+ std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
+ std::set<int> uniqueQueueFamilies = {indices.graphicsFamily}; //////, indices.presentFamily};
+
+ float queuePriority = 1.0f;
+ for (int queueFamily : uniqueQueueFamilies) {
+ VkDeviceQueueCreateInfo queueCreateInfo = {};
+ queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queueCreateInfo.queueFamilyIndex = queueFamily;
+ queueCreateInfo.queueCount = 1;
+ queueCreateInfo.pQueuePriorities = &queuePriority;
+ queueCreateInfos.push_back(queueCreateInfo);
+ }
+
+ VkPhysicalDeviceFeatures deviceFeatures = {};
+
+ VkDeviceCreateInfo createInfo = {};
+ createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+
+ createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
+ createInfo.pQueueCreateInfos = queueCreateInfos.data();
+
+ createInfo.pEnabledFeatures = &deviceFeatures;
+
+ if (vkCreateDevice(m_physicalDevice, &createInfo, nullptr, &m_device) != VK_SUCCESS) {
+ qCritical("VulkanWrapper: failed to create logical device!");
+ return false;
+ }
+
+ vkGetDeviceQueue(m_device, indices.graphicsFamily, 0, &m_graphicsQueue);
+ return true;
+}
+
+VulkanImageWrapper *VulkanWrapperPrivate::createTextureImage(const QImage &img)
+{
+ return createTextureImageFromData(img.constBits(), img.sizeInBytes(), img.size(), VK_FORMAT_R8G8B8A8_UNORM);
+}
+
+VulkanImageWrapper *VulkanWrapperPrivate::createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, VkFormat vkFormat)
+{
+ if (m_initFailed)
+ return nullptr;
+
+ int texWidth = size.width();
+ int texHeight = size.height();
+ bool ok;
+ if (extraDebug) qDebug("image load %p %dx%d", pixels, texWidth, texHeight);
+ if (!pixels) {
+ qCritical("VulkanWrapper: failed to load texture image!");
+ return nullptr;
+ }
+
+ VkBuffer stagingBuffer;
+ VkDeviceMemory stagingBufferMemory;
+ ok = createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
+
+ if (!ok)
+ return nullptr;
+
+ void* data;
+ vkMapMemory(m_device, stagingBufferMemory, 0, bufferSize, 0, &data);
+ if (extraDebug) qDebug() << "mapped" << data << bufferSize;
+ memcpy(data, pixels, static_cast<size_t>(bufferSize));
+ vkUnmapMemory(m_device, stagingBufferMemory);
+
+ if (extraDebug) qDebug() << "creating image...";
+
+ QScopedPointer<VulkanImageWrapper> imageWrapper(createImage(vkFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, size, bufferSize));
+ if (imageWrapper.isNull())
+ return nullptr;
+
+ if (extraDebug) qDebug() << "transition...";
+
+ const VkImage textureImage = imageWrapper->textureImage;
+
+ ok = transitionImageLayout(textureImage, vkFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+
+ if (!ok)
+ return nullptr;
+
+ if (extraDebug) qDebug() << "copyBufferToImage...";
+ copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
+ transitionImageLayout(textureImage, vkFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+ vkDestroyBuffer(m_device, stagingBuffer, nullptr);
+ vkFreeMemory(m_device, stagingBufferMemory, nullptr);
+
+ return imageWrapper.take();
+}
+
+void VulkanWrapperPrivate::freeTextureImage(VulkanImageWrapper *imageWrapper)
+{
+ if (!imageWrapper)
+ return;
+
+ //"To avoid leaking resources, the application must release ownership of the file descriptor using the close system call"
+ ::close(imageWrapper->imgFd);
+
+ // clean up the image memory
+ vkDestroyImage(m_device, imageWrapper->textureImage, nullptr);
+ vkFreeMemory(m_device, imageWrapper->textureImageMemory, nullptr);
+}
+
+VulkanWrapperPrivate::VulkanWrapperPrivate(QOpenGLContext *glContext)
+{
+ if (extraDebug) qDebug("Creating Vulkan instance");
+ VkApplicationInfo applicationInfo = {};
+ applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+ applicationInfo.pNext = nullptr;
+ applicationInfo.pApplicationName = nullptr;
+ applicationInfo.applicationVersion = 0;
+ applicationInfo.pEngineName = nullptr;
+ applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
+ applicationInfo.apiVersion = VK_MAKE_VERSION(1, 0, 5);
+
+ VkInstanceCreateInfo instanceCreateInfo = {};
+ instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ instanceCreateInfo.pNext = nullptr;
+ instanceCreateInfo.flags = 0;
+ instanceCreateInfo.pApplicationInfo = &applicationInfo;
+ instanceCreateInfo.enabledLayerCount = 0;
+ instanceCreateInfo.ppEnabledLayerNames = nullptr;
+ instanceCreateInfo.enabledExtensionCount = 0;
+ instanceCreateInfo.ppEnabledExtensionNames = nullptr;
+
+ auto f_glGetVkProcAddrNV = reinterpret_cast<PFNGLGETVKPROCADDRNVPROC>(glContext->getProcAddress("glGetVkProcAddrNV"));
+
+ if (!f_glGetVkProcAddrNV) {
+ qCritical("VulkanWrapper: Could not find Vulkan/GL interop function glGetVkProcAddrNV");
+ m_initFailed = true;
+ return;
+ }
+
+ initFunctions(f_glGetVkProcAddrNV);
+
+ VkResult instanceCreationResult = vkCreateInstance(&instanceCreateInfo, nullptr, &m_instance);
+
+ if (extraDebug) qDebug() << "result" << instanceCreationResult;
+
+ if (instanceCreationResult != VK_SUCCESS) {
+ qCritical() << "VulkanWrapper: Failed to create Vulkan instance: Error "
+ << instanceCreationResult;
+ m_initFailed = true;
+ return;
+ }
+
+ uint32_t devCount;
+
+ auto res = vkEnumeratePhysicalDevices(m_instance, &devCount, nullptr);
+ if (extraDebug) qDebug() << "vkEnumeratePhysicalDevices res =" << res << "count =" << devCount;
+
+ QVarLengthArray<VkPhysicalDevice, 5> dev(devCount);
+
+ res = vkEnumeratePhysicalDevices(m_instance, &devCount, dev.data());
+ if (extraDebug) qDebug() << "...devs res =" << res << "count =" << devCount;
+
+#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG
+ VkPhysicalDeviceProperties props;
+
+ vkGetPhysicalDeviceProperties(dev[0], &props);
+
+ qDebug() << "Properties " << hex
+ << "apiVersion" << props.apiVersion
+ << "driverVersion" << props.driverVersion
+ << "vendorID" << props.vendorID
+ << "deviceID" << props.deviceID
+ << "deviceType" << props.deviceType
+ << "deviceName" << props.deviceName;
+#endif
+
+ m_physicalDevice = dev[0]; //TODO handle the case of multiple GPUs where only some support Vulkan
+
+ bool ok = createLogicalDevice();
+ if (!ok) {
+ qCritical("VulkanWrapperPrivate: could not create logical device");
+ m_initFailed = true;
+ return;
+ }
+
+ VkPhysicalDeviceMemoryProperties memProps;
+
+
+ vkGetPhysicalDeviceMemoryProperties(dev[0], &memProps);
+
+#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG
+ qDebug() << "Physical memory properties:\n" << "types:" << memProps.memoryTypeCount << "heaps:" << memProps.memoryHeapCount;
+ for (uint i = 0; i < memProps.memoryTypeCount; ++i)
+ qDebug() << " " << i << "heap" << memProps.memoryTypes[i].heapIndex << "flags" << hex << memProps.memoryTypes[i].propertyFlags;
+
+ for (uint i = 0; i < memProps.memoryHeapCount; ++i)
+ qDebug() << " " << i << "size" << memProps.memoryHeaps[i].size << "flags" << hex << memProps.memoryHeaps[i].flags;
+#endif
+
+ int gpuMemoryType = -1;
+
+ for (uint i = 0; i < memProps.memoryTypeCount; ++i) {
+ if (memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
+ gpuMemoryType = i;
+ break;
+ }
+ }
+
+ if (gpuMemoryType < 0) {
+ qCritical("VulkanWrapper: Could not find GPU memory!");
+ m_initFailed = true;
+ return;
+ }
+
+#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG
+ qDebug() << "GPU memory type:" << gpuMemoryType << "heap:" << memProps.memoryTypes[gpuMemoryType].heapIndex;
+
+ for (int f = 0; f <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK; f++)
+ {
+ VkFormatProperties formatProps;
+ vkGetPhysicalDeviceFormatProperties(dev[0], VkFormat(f), &formatProps);
+ qDebug() << "format" << f << "features" << hex << formatProps.linearTilingFeatures << formatProps.optimalTilingFeatures << formatProps.bufferFeatures;
+ }
+#endif
+ createCommandPool();
+}
+
+
+VulkanWrapper::VulkanWrapper(QOpenGLContext *glContext)
+ : d_ptr(new VulkanWrapperPrivate(glContext))
+{
+}
+
+VulkanImageWrapper *VulkanWrapper::createTextureImage(const QImage &img)
+{
+ return d_ptr->createTextureImage(img);
+}
+
+VulkanImageWrapper *VulkanWrapper::createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, uint glInternalFormat)
+{
+ VkFormat vkFormat = VkFormat(QVkConvenience::vkFormatFromGlFormat(glInternalFormat));
+ if (vkFormat == VK_FORMAT_UNDEFINED)
+ return nullptr;
+
+ return d_ptr->createTextureImageFromData(pixels, bufferSize, size, vkFormat);
+}
+
+int VulkanWrapper::getImageInfo(const VulkanImageWrapper *imgWrapper, int *memSize, int *w, int *h)
+{
+ if (memSize)
+ *memSize = imgWrapper->imgMemSize;
+ if (w)
+ *w = imgWrapper->imgSize.width();
+ if (h)
+ *h = imgWrapper->imgSize.height();
+ return imgWrapper->imgFd;
+}
+
+void VulkanWrapper::freeTextureImage(VulkanImageWrapper *imageWrapper)
+{
+ d_ptr->freeTextureImage(imageWrapper);
+}
+
+QT_END_NAMESPACE
diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.h b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.h
new file mode 100644
index 000000000..541618fb3
--- /dev/null
+++ b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 VULKANWRAPPER_H
+#define VULKANWRAPPER_H
+
+#include <QOpenGLContext>
+
+QT_BEGIN_NAMESPACE
+
+class VulkanWrapper;
+struct VulkanImageWrapper;
+class VulkanWrapperPrivate;
+
+class QOpenGLContext;
+class QImage;
+
+class VulkanWrapper
+{
+public:
+ VulkanWrapper(QOpenGLContext *glContext);
+
+ VulkanImageWrapper *createTextureImage(const QImage &img);
+ VulkanImageWrapper *createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, uint glInternalFormat);
+ int getImageInfo(const VulkanImageWrapper *imgWrapper, int *memSize, int *w = nullptr, int *h = nullptr);
+ void freeTextureImage(VulkanImageWrapper *imageWrapper);
+
+private:
+ VulkanWrapperPrivate *d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // VULKANWRAPPER_H
diff --git a/src/imports/compositor/qwaylandquickcompositorplugin.cpp b/src/imports/compositor/qwaylandquickcompositorplugin.cpp
index c7553d932..949d2cfaa 100644
--- a/src/imports/compositor/qwaylandquickcompositorplugin.cpp
+++ b/src/imports/compositor/qwaylandquickcompositorplugin.cpp
@@ -137,7 +137,9 @@ public:
qmlRegisterType<QWaylandQuickCompositorQuickExtensionContainer>(uri, 1, 0, "WaylandCompositor");
qmlRegisterType<QWaylandQuickItem>(uri, 1, 0, "WaylandQuickItem");
qmlRegisterType<QWaylandQuickItem, 13>(uri, 1, 13, "WaylandQuickItem");
+#if QT_CONFIG(opengl)
qmlRegisterType<QWaylandQuickHardwareLayer>(uri, 1, 2, "WaylandHardwareLayer");
+#endif
qmlRegisterType<QWaylandMouseTracker>(uri, 1, 0, "WaylandMouseTracker");
qmlRegisterType<QWaylandQuickOutput>(uri, 1, 0, "WaylandOutput");
qmlRegisterType<QWaylandQuickSurface>(uri, 1, 0, "WaylandSurface");
diff --git a/src/imports/imports.pro b/src/imports/imports.pro
index c57c95d20..1fced3dfd 100644
--- a/src/imports/imports.pro
+++ b/src/imports/imports.pro
@@ -1,3 +1,12 @@
TEMPLATE = subdirs
-qtHaveModule(quick): SUBDIRS += compositor
+qtHaveModule(quick): {
+ SUBDIRS += \
+ compositor
+
+ qtHaveModule(opengl): {
+ SUBDIRS += \
+ texture-sharing \
+ texture-sharing-extension
+ }
+}
diff --git a/src/imports/texture-sharing-extension/plugin.cpp b/src/imports/texture-sharing-extension/plugin.cpp
new file mode 100644
index 000000000..42dcd8e2d
--- /dev/null
+++ b/src/imports/texture-sharing-extension/plugin.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqmlengine.h>
+
+#include "QtWaylandCompositor/private/qwltexturesharingextension_p.h"
+
+/*!
+ \qmlmodule QtWayland.Compositor.TextureSharingExtension 1
+ \title Qt Wayland Shared Texture Provider
+ \ingroup qmlmodules
+ \brief Adds a mechanism to share GPU memory
+
+ \section2 Summary
+
+ This module lets the compositor export graphical resources that can be used by clients,
+ without allocating any graphics memory in the client.
+
+ \section2 Usage
+
+ This module is imported like this:
+
+ \code
+ import QtWayland.Compositor.TextureSharingExtension 1.0
+ \endcode
+
+ To use this module in a compositor, instantiate the extension object as a child of the compositor object, like this:
+
+
+ \code
+ WaylandCompositor {
+ //...
+ TextureSharingExtension {
+ }
+ }
+ \endcode
+
+ The sharing functionality is provided through a QQuickImageProvider. Use
+ the "image:" scheme for the URL source of the image, followed by the
+ identifier \e wlshared, followed by the image file path. For example:
+
+ \code
+ Image { source: "image://wlshared/wallpapers/mybackground.jpg" }
+ \endcode
+
+*/
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandTextureSharingExtensionPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+public:
+ QWaylandTextureSharingExtensionPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) {}
+
+ void registerTypes(const char *uri) override
+ {
+ Q_ASSERT(uri == QStringLiteral("QtWayland.Compositor.TextureSharingExtension"));
+ qmlRegisterType<QWaylandTextureSharingExtensionQuickExtension>("QtWayland.Compositor.TextureSharingExtension", 1, 0, "TextureSharingExtension");
+ }
+
+ void initializeEngine(QQmlEngine *engine, const char *uri) override
+ {
+ Q_UNUSED(uri);
+ engine->addImageProvider("wlshared", new QWaylandSharedTextureProvider);
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/imports/texture-sharing-extension/qmldir b/src/imports/texture-sharing-extension/qmldir
new file mode 100644
index 000000000..182e5c0ee
--- /dev/null
+++ b/src/imports/texture-sharing-extension/qmldir
@@ -0,0 +1,3 @@
+module QtWayland.Compositor.TextureSharingExtension
+plugin qwaylandtexturesharingextension
+classname QWaylandTextureSharingExtensionPlugin
diff --git a/src/imports/texture-sharing-extension/texture-sharing-extension.pro b/src/imports/texture-sharing-extension/texture-sharing-extension.pro
new file mode 100644
index 000000000..68a8cf757
--- /dev/null
+++ b/src/imports/texture-sharing-extension/texture-sharing-extension.pro
@@ -0,0 +1,11 @@
+CXX_MODULE = qml
+TARGET = qwaylandtexturesharingextension
+TARGETPATH = QtWayland/Compositor/TextureSharingExtension
+IMPORT_VERSION = 1.$$QT_MINOR_VERSION
+
+SOURCES += \
+ plugin.cpp
+
+QT += quick-private qml gui-private core-private waylandcompositor waylandcompositor-private
+
+load(qml_plugin)
diff --git a/src/imports/texture-sharing/plugin.cpp b/src/imports/texture-sharing/plugin.cpp
new file mode 100644
index 000000000..9cf6bbca1
--- /dev/null
+++ b/src/imports/texture-sharing/plugin.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandClient 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 <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqmlengine.h>
+
+#include "sharedtextureprovider.h"
+
+/*!
+ \qmlmodule QtWayland.Client.TextureSharing 1
+ \title Qt Wayland Shared Texture Provider
+ \ingroup qmlmodules
+ \brief Adds an image provider which utilizes shared GPU memory
+
+ \section2 Summary
+
+ This module allows Qt Wayland clients to use graphical resources exported
+ by the compositor, without allocating any graphics memory in the client.
+ \section2 Usage
+
+ To use this module, import it like this:
+ \code
+ import QtWayland.Client.TextureSharing 1.0
+ \endcode
+
+ The sharing functionality is provided through a QQuickImageProvider. Use
+ the "image:" scheme for the URL source of the image, followed by the
+ identifier \e wlshared, followed by the image file path. For example:
+
+ \code
+ Image { source: "image://wlshared/wallpapers/mybackground.jpg" }
+ \endcode
+
+ The shared texture module does not provide any directly usable QML types.
+*/
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandTextureSharingPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+public:
+ QWaylandTextureSharingPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) {}
+
+ void registerTypes(const char *uri) override
+ {
+ Q_ASSERT(uri == QStringLiteral("QtWayland.Client.TextureSharing"));
+ qmlRegisterModule(uri, 1, 0);
+ }
+
+ void initializeEngine(QQmlEngine *engine, const char *uri) override
+ {
+ Q_UNUSED(uri);
+ engine->addImageProvider("wlshared", new SharedTextureProvider);
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/imports/texture-sharing/qmldir b/src/imports/texture-sharing/qmldir
new file mode 100644
index 000000000..cf3b74c48
--- /dev/null
+++ b/src/imports/texture-sharing/qmldir
@@ -0,0 +1,3 @@
+module QtWayland.Client.TextureSharing
+plugin qwaylandtexturesharing
+classname QWaylandTextureSharingPlugin
diff --git a/src/imports/texture-sharing/sharedtextureprovider.cpp b/src/imports/texture-sharing/sharedtextureprovider.cpp
new file mode 100644
index 000000000..707e94ae6
--- /dev/null
+++ b/src/imports/texture-sharing/sharedtextureprovider.cpp
@@ -0,0 +1,322 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "sharedtextureprovider.h"
+
+#include <QFile>
+#include <QDebug>
+#include <qopenglfunctions.h>
+#include <QQuickWindow>
+
+#include <QtWaylandClient/private/qwaylandintegration_p.h>
+#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h>
+#include <QtGui/QGuiApplication>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformnativeinterface.h>
+#include <QtGui/QWindow>
+#include <QOpenGLTexture>
+#include <QImageReader>
+
+#include <QTimer>
+
+#include "texturesharingextension.h"
+
+QT_BEGIN_NAMESPACE
+
+SharedTexture::SharedTexture(QtWaylandClient::QWaylandServerBuffer *buffer)
+ : m_buffer(buffer), m_tex(nullptr)
+{
+}
+
+int SharedTexture::textureId() const
+{
+ updateGLTexture();
+ return m_tex ? m_tex->textureId() : 0;
+}
+
+QSize SharedTexture::textureSize() const
+{
+ updateGLTexture();
+ return m_tex ? QSize(m_tex->width(), m_tex->height()) : QSize();
+}
+
+bool SharedTexture::hasAlphaChannel() const
+{
+ return true;
+}
+
+bool SharedTexture::hasMipmaps() const
+{
+ updateGLTexture();
+ return m_tex ? (m_tex->mipLevels() > 1) : false;
+}
+
+void SharedTexture::bind()
+{
+ updateGLTexture();
+ if (m_tex)
+ m_tex->bind();
+}
+
+inline void SharedTexture::updateGLTexture() const
+{
+ if (!m_tex && m_buffer)
+ m_tex = m_buffer->toOpenGlTexture();
+}
+
+class SharedTextureFactory : public QQuickTextureFactory
+{
+public:
+ SharedTextureFactory(const QtWaylandClient::QWaylandServerBuffer *buffer, const QString &id, SharedTextureRegistry *registry)
+ : m_buffer(buffer), m_id(id), m_registry(registry)
+ {
+ }
+
+ ~SharedTextureFactory() override
+ {
+ //qDebug() << "====> DESTRUCTOR SharedTextureFactory" << this;
+ if (m_registry)
+ m_registry->abandonBuffer(m_id);
+ delete m_buffer; // TODO: make sure we are not keeping references to this elsewhere
+ //qDebug() << "buffer deleted";
+ }
+
+ QSize textureSize() const override
+ {
+ return m_buffer ? m_buffer->size() : QSize();
+ }
+
+ int textureByteCount() const override
+ {
+ return m_buffer ? (m_buffer->size().width() * m_buffer->size().height() * 4) : 0;
+ }
+
+ QSGTexture *createTexture(QQuickWindow *) const override
+ {
+ return new SharedTexture(const_cast<QtWaylandClient::QWaylandServerBuffer *>(m_buffer));
+ }
+
+private:
+ const QtWaylandClient::QWaylandServerBuffer *m_buffer = nullptr;
+ QString m_id;
+ QPointer<SharedTextureRegistry> m_registry;
+};
+
+
+SharedTextureRegistry::SharedTextureRegistry()
+ : m_extension(new TextureSharingExtension)
+{
+ connect(m_extension, &TextureSharingExtension::bufferReceived, this, &SharedTextureRegistry::receiveBuffer);
+ connect(m_extension, &TextureSharingExtension::activeChanged, this, &SharedTextureRegistry::handleExtensionActive);
+}
+
+SharedTextureRegistry::~SharedTextureRegistry()
+{
+ delete m_extension;
+}
+
+const QtWaylandClient::QWaylandServerBuffer *SharedTextureRegistry::bufferForId(const QString &id) const
+{
+ return m_buffers.value(id);
+}
+
+void SharedTextureRegistry::requestBuffer(const QString &id)
+{
+ if (!m_extension->isActive()) {
+ //qDebug() << "Extension not active, adding" << id << "to queue";
+ m_pendingBuffers << id;
+ return;
+ }
+ m_extension->requestImage(id);
+}
+
+void SharedTextureRegistry::abandonBuffer(const QString &id)
+{
+ m_buffers.remove(id);
+ m_extension->abandonImage(id);
+}
+
+
+void SharedTextureRegistry::handleExtensionActive()
+{
+ //qDebug() << "handleExtensionActive, queue:" << m_pendingBuffers;
+ if (m_extension->isActive())
+ while (!m_pendingBuffers.isEmpty())
+ requestBuffer(m_pendingBuffers.takeFirst());
+}
+
+bool SharedTextureRegistry::preinitialize()
+{
+ auto *serverBufferIntegration = QGuiApplicationPrivate::platformIntegration()->nativeInterface()->nativeResourceForIntegration("server_buffer_integration");
+
+ if (!serverBufferIntegration) {
+ qWarning() << "Wayland Server Buffer Integration not available.";
+ return false;
+ }
+
+ return true;
+}
+
+void SharedTextureRegistry::receiveBuffer(QtWaylandClient::QWaylandServerBuffer *buffer, const QString& id)
+{
+ //qDebug() << "ReceiveBuffer for id" << id;
+ if (buffer)
+ m_buffers.insert(id, buffer);
+ emit replyReceived(id);
+}
+
+class SharedTextureImageResponse : public QQuickImageResponse
+{
+ Q_OBJECT
+public:
+ SharedTextureImageResponse(SharedTextureRegistry *registry, const QString &id)
+ : m_id(id), m_registry(registry)
+ {
+ if (!m_registry || m_registry->bufferForId(id)) {
+ // Shortcut: no server roundtrip needed, just let the event loop call the slot
+ QMetaObject::invokeMethod(this, "doResponse", Qt::QueuedConnection, Q_ARG(QString, id));
+
+ } else {
+ // TBD: timeout?
+ connect(registry, &SharedTextureRegistry::replyReceived, this, &SharedTextureImageResponse::doResponse);
+ registry->requestBuffer(id);
+ }
+ }
+
+ QQuickTextureFactory *textureFactory() const override
+ {
+ if (m_registry) {
+ const QtWaylandClient::QWaylandServerBuffer *buffer = m_registry->bufferForId(m_id);
+ if (buffer) {
+ //qDebug() << "Creating shared buffer texture for" << m_id;
+ return new SharedTextureFactory(buffer, m_id, m_registry);
+ }
+ //qDebug() << "Shared buffer NOT found for" << m_id;
+ }
+
+ // No shared buffer, do fallback
+ QString fbPath = fallbackPath();
+ if (fbPath.isEmpty()) {
+ m_errorString = QStringLiteral("Shared buffer not found, and no fallback path set.");
+ return nullptr;
+ }
+
+ QImageReader reader(fbPath + m_id);
+ QImage img = reader.read();
+ if (img.isNull()) {
+ qWarning() << "Could not load local image from id/path" << reader.fileName();
+ m_errorString = QStringLiteral("Shared buffer not found, and fallback local file loading failed: ") + reader.errorString();
+ return nullptr;
+ }
+ return QQuickTextureFactory::textureFactoryForImage(img);
+ }
+
+ QString errorString() const override
+ {
+ return m_errorString;
+ }
+
+ static QString fallbackPath()
+ {
+ static QString fbPath;
+ static bool isInit = false;
+ if (!isInit) {
+ isInit = true;
+ QByteArray envVal = qgetenv("QT_SHAREDTEXTURE_FALLBACK_DIR");
+ if (!envVal.isEmpty()) {
+ fbPath = QString::fromLocal8Bit(envVal);
+ if (!fbPath.endsWith(QLatin1Char('/')))
+ fbPath.append(QLatin1Char('/'));
+ }
+ }
+ return fbPath;
+ }
+
+
+public slots:
+ void doResponse(const QString &key) {
+ if (key != m_id)
+ return; // not our buffer
+
+ // No need to be called again
+ if (m_registry)
+ disconnect(m_registry, &SharedTextureRegistry::replyReceived, this, &SharedTextureImageResponse::doResponse);
+
+ emit finished();
+ }
+
+private:
+ QString m_id;
+ SharedTextureRegistry *m_registry = nullptr;
+ mutable QString m_errorString;
+};
+
+
+SharedTextureProvider::SharedTextureProvider()
+{
+ m_sharingAvailable = SharedTextureRegistry::preinitialize();
+ if (!m_sharingAvailable) {
+ if (SharedTextureImageResponse::fallbackPath().isEmpty())
+ qWarning() << "Shared buffer images not available, and no fallback directory set.";
+ else
+ qWarning() << "Shared buffer images not available, will fallback to local image files from" << SharedTextureImageResponse::fallbackPath();
+ }
+}
+
+SharedTextureProvider::~SharedTextureProvider()
+{
+ delete m_registry;
+}
+
+QQuickImageResponse *SharedTextureProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
+{
+ Q_UNUSED(requestedSize);
+
+ //qDebug() << "Provider: got request for" << id;
+
+ if (m_sharingAvailable && !m_registry)
+ m_registry = new SharedTextureRegistry;
+
+ return new SharedTextureImageResponse(m_registry, id);
+}
+
+QT_END_NAMESPACE
+
+#include "sharedtextureprovider.moc"
diff --git a/src/imports/texture-sharing/sharedtextureprovider.h b/src/imports/texture-sharing/sharedtextureprovider.h
new file mode 100644
index 000000000..f25c7de9c
--- /dev/null
+++ b/src/imports/texture-sharing/sharedtextureprovider.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 SHAREDTEXTUREPROVIDER_H
+#define SHAREDTEXTUREPROVIDER_H
+
+#include <QOpenGLFunctions>
+#include <QQuickImageProvider>
+#include <QtQuick/QSGTexture>
+#include <QScopedPointer>
+#include <QHash>
+
+#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class TextureSharingExtension;
+
+class SharedTextureRegistry : public QObject
+{
+ Q_OBJECT
+public:
+ SharedTextureRegistry();
+ ~SharedTextureRegistry() override;
+
+ const QtWaylandClient::QWaylandServerBuffer *bufferForId(const QString &id) const;
+ void requestBuffer(const QString &id);
+ void abandonBuffer(const QString &id);
+
+ static bool preinitialize();
+
+public slots:
+ void receiveBuffer(QtWaylandClient::QWaylandServerBuffer *buffer, const QString &id);
+
+signals:
+ void replyReceived(const QString &id);
+
+private slots:
+ void handleExtensionActive();
+
+private:
+ TextureSharingExtension *m_extension = nullptr;
+ QHash<QString, QtWaylandClient::QWaylandServerBuffer *> m_buffers;
+ QStringList m_pendingBuffers;
+};
+
+class SharedTextureProvider : public QQuickAsyncImageProvider
+{
+public:
+ SharedTextureProvider();
+ ~SharedTextureProvider() override;
+
+ QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
+
+private:
+ SharedTextureRegistry *m_registry = nullptr;
+ bool m_sharingAvailable = false;
+};
+
+class SharedTexture : public QSGTexture
+{
+ Q_OBJECT
+public:
+ SharedTexture(QtWaylandClient::QWaylandServerBuffer *buffer);
+
+ int textureId() const override;
+ QSize textureSize() const override;
+ bool hasAlphaChannel() const override;
+ bool hasMipmaps() const override;
+
+ void bind() override;
+
+private:
+ void updateGLTexture() const;
+ QtWaylandClient::QWaylandServerBuffer *m_buffer = nullptr;
+ mutable QOpenGLTexture *m_tex = nullptr;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // SHAREDTEXTUREPROVIDER_H
diff --git a/src/imports/texture-sharing/texture-sharing.pro b/src/imports/texture-sharing/texture-sharing.pro
new file mode 100644
index 000000000..bec769ecb
--- /dev/null
+++ b/src/imports/texture-sharing/texture-sharing.pro
@@ -0,0 +1,21 @@
+CXX_MODULE = qml
+TARGET = qwaylandtexturesharing
+TARGETPATH = QtWayland/Client/TextureSharing
+IMPORT_VERSION = 1.$$QT_MINOR_VERSION
+
+HEADERS += \
+ sharedtextureprovider.h \
+ texturesharingextension.h
+
+SOURCES += \
+ plugin.cpp \
+ sharedtextureprovider.cpp \
+ texturesharingextension.cpp
+
+QT += quick-private qml gui-private core-private waylandclient waylandclient-private
+CONFIG += wayland-scanner
+
+WAYLANDCLIENTSOURCES += ../../extensions/qt-texture-sharing-unstable-v1.xml
+
+
+load(qml_plugin)
diff --git a/src/imports/texture-sharing/texturesharingextension.cpp b/src/imports/texture-sharing/texturesharingextension.cpp
new file mode 100644
index 000000000..31106d694
--- /dev/null
+++ b/src/imports/texture-sharing/texturesharingextension.cpp
@@ -0,0 +1,86 @@
+
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "texturesharingextension.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
+
+TextureSharingExtension::TextureSharingExtension()
+ : 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 TextureSharingExtension::zqt_texture_sharing_v1_provide_buffer(struct ::qt_server_buffer *buffer, const QString &key)
+{
+ QtWaylandClient::QWaylandServerBuffer *serverBuffer = m_server_buffer_integration->serverBuffer(buffer);
+ emit bufferReceived(serverBuffer, key);
+}
+
+void TextureSharingExtension::zqt_texture_sharing_v1_image_failed(const QString &key, const QString &message)
+{
+ qWarning() << "TextureSharingExtension" << key << "not found" << message;
+ emit bufferReceived(nullptr, key);
+}
+void TextureSharingExtension::requestImage(const QString &key)
+{
+ request_image(key);
+}
+
+void TextureSharingExtension::abandonImage(const QString &key)
+{
+ abandon_image(key);
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/texture-sharing/texturesharingextension.h b/src/imports/texture-sharing/texturesharingextension.h
new file mode 100644
index 000000000..7b864fbc8
--- /dev/null
+++ b/src/imports/texture-sharing/texturesharingextension.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 TEXTURESHARINGEXTENSION_H
+#define TEXTURESHARINGEXTENSION_H
+
+#include <qpa/qwindowsysteminterface.h>
+#include <QtWaylandClient/private/qwayland-wayland.h>
+#include <QtWaylandClient/qwaylandclientextension.h>
+#include "qwayland-qt-texture-sharing-unstable-v1.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+ class QWaylandServerBuffer;
+ class QWaylandServerBufferIntegration;
+};
+
+class TextureSharingExtension : public QWaylandClientExtensionTemplate<TextureSharingExtension>
+ , public QtWayland::zqt_texture_sharing_v1
+{
+ Q_OBJECT
+public:
+ TextureSharingExtension();
+
+public slots:
+ void requestImage(const QString &key);
+ void abandonImage(const QString &key);
+
+signals:
+ void bufferReceived(QtWaylandClient::QWaylandServerBuffer *buffer, const QString &key);
+
+private:
+ void zqt_texture_sharing_v1_provide_buffer(struct ::qt_server_buffer *buffer, const QString &key) override;
+ void zqt_texture_sharing_v1_image_failed(const QString &key, const QString &message) override;
+ QtWaylandClient::QWaylandServerBufferIntegration *m_server_buffer_integration = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // TEXTURESHARINGEXTENSION_H
diff --git a/src/plugins/hardwareintegration/client/client.pro b/src/plugins/hardwareintegration/client/client.pro
index 82e431ee8..7b7e8a49a 100644
--- a/src/plugins/hardwareintegration/client/client.pro
+++ b/src/plugins/hardwareintegration/client/client.pro
@@ -18,3 +18,5 @@ qtConfig(wayland-shm-emulation-server-buffer): \
SUBDIRS += shm-emulation-server
qtConfig(wayland-dmabuf-server-buffer): \
SUBDIRS += dmabuf-server
+qtConfig(wayland-vulkan-server-buffer): \
+ SUBDIRS += vulkan-server
diff --git a/src/plugins/hardwareintegration/client/vulkan-server/main.cpp b/src/plugins/hardwareintegration/client/vulkan-server/main.cpp
new file mode 100644
index 000000000..b8f64bf22
--- /dev/null
+++ b/src/plugins/hardwareintegration/client/vulkan-server/main.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "vulkanserverbufferintegration.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+class VulkanServerBufferPlugin : public QWaylandServerBufferIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QWaylandServerBufferIntegrationFactoryInterface_iid FILE "vulkan-server.json")
+public:
+ QWaylandServerBufferIntegration *create(const QString&, const QStringList&) override;
+};
+
+QWaylandServerBufferIntegration *VulkanServerBufferPlugin::create(const QString& key, const QStringList& paramList)
+{
+ Q_UNUSED(paramList);
+ Q_UNUSED(key);
+ return new VulkanServerBufferIntegration();
+}
+
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.json b/src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.json
new file mode 100644
index 000000000..baadd1529
--- /dev/null
+++ b/src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "vulkan-server" ]
+}
diff --git a/src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.pro b/src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.pro
new file mode 100644
index 000000000..1be60f7c1
--- /dev/null
+++ b/src/plugins/hardwareintegration/client/vulkan-server/vulkan-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/vulkan-server/vulkan-server.pri)
+
+OTHER_FILES += \
+ vulkan-server.json
+
+SOURCES += main.cpp
+
+PLUGIN_TYPE = wayland-graphics-integration-client
+PLUGIN_CLASS_NAME = VulkanServerBufferPlugin
+load(qt_plugin)
diff --git a/src/plugins/hardwareintegration/compositor/compositor.pro b/src/plugins/hardwareintegration/compositor/compositor.pro
index 59ea91414..32edf1f15 100644
--- a/src/plugins/hardwareintegration/compositor/compositor.pro
+++ b/src/plugins/hardwareintegration/compositor/compositor.pro
@@ -20,6 +20,8 @@ qtConfig(wayland-shm-emulation-server-buffer): \
SUBDIRS += shm-emulation-server
qtConfig(wayland-dmabuf-server-buffer): \
SUBDIRS += dmabuf-server
+qtConfig(wayland-vulkan-server-buffer): \
+ SUBDIRS += vulkan-server
qtConfig(wayland-egl): \
SUBDIRS += wayland-eglstream-controller
diff --git a/src/plugins/hardwareintegration/compositor/vulkan-server/main.cpp b/src/plugins/hardwareintegration/compositor/vulkan-server/main.cpp
new file mode 100644
index 000000000..d765dd389
--- /dev/null
+++ b/src/plugins/hardwareintegration/compositor/vulkan-server/main.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "vulkanserverbufferintegration.h"
+
+QT_BEGIN_NAMESPACE
+
+class VulkanServerBufferIntegrationPlugin : public QtWayland::ServerBufferIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QtWaylandServerBufferIntegrationFactoryInterface_iid FILE "vulkan-server.json")
+public:
+ QtWayland::ServerBufferIntegration *create(const QString&, const QStringList&) override;
+};
+
+QtWayland::ServerBufferIntegration *VulkanServerBufferIntegrationPlugin::create(const QString& key, const QStringList& paramList)
+{
+ Q_UNUSED(paramList);
+ Q_UNUSED(key);
+ return new VulkanServerBufferIntegration();
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.json b/src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.json
new file mode 100644
index 000000000..baadd1529
--- /dev/null
+++ b/src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "vulkan-server" ]
+}
diff --git a/src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.pro b/src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.pro
new file mode 100644
index 000000000..053654218
--- /dev/null
+++ b/src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.pro
@@ -0,0 +1,12 @@
+QT = waylandcompositor waylandcompositor-private core-private gui-private
+
+OTHER_FILES += vulkan-server.json
+
+SOURCES += \
+ main.cpp
+
+include($PWD/../../../../../hardwareintegration/compositor/vulkan-server/vulkan-server.pri)
+
+PLUGIN_TYPE = wayland-graphics-integration-server
+PLUGIN_CLASS_NAME = VulkanServerBufferIntegrationPlugin
+load(qt_plugin)
diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp
index 9a829f6e9..26f598895 100644
--- a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp
+++ b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp
@@ -51,7 +51,7 @@ QWaylandFullScreenShellV1Surface::QWaylandFullScreenShellV1Surface(QtWayland::zw
, m_window(window)
{
auto screen = static_cast<QWaylandScreen *>(m_window->screen());
- m_shell->present_surface(m_window->object(),
+ m_shell->present_surface(m_window->wlSurface(),
QtWayland::zwp_fullscreen_shell_v1::present_method_default,
screen->output());
}
diff --git a/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.cpp b/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.cpp
index efb61dc9f..9309faccd 100644
--- a/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.cpp
+++ b/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.cpp
@@ -132,7 +132,7 @@ QWaylandShellSurface *QWaylandIviShellIntegration::createShellSurface(QWaylandWi
if (surfaceId == 0)
return nullptr;
- struct ivi_surface *surface = m_iviApplication->surface_create(surfaceId, window->object());
+ struct ivi_surface *surface = m_iviApplication->surface_create(surfaceId, window->wlSurface());
if (!m_iviController)
return new QWaylandIviSurface(surface, window);
diff --git a/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp
index 1edb24b3c..f396e8402 100644
--- a/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp
+++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp
@@ -70,7 +70,7 @@ bool QWaylandWlShellIntegration::initialize(QWaylandDisplay *display)
QWaylandShellSurface *QWaylandWlShellIntegration::createShellSurface(QWaylandWindow *window)
{
- return new QWaylandWlShellSurface(m_wlShell->get_shell_surface(window->object()), window);
+ return new QWaylandWlShellSurface(m_wlShell->get_shell_surface(window->wlSurface()), window);
}
void *QWaylandWlShellIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window)
diff --git a/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp
index 4506c312a..48e14c753 100644
--- a/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp
+++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp
@@ -62,9 +62,9 @@ QWaylandWlShellSurface::QWaylandWlShellSurface(struct ::wl_shell_surface *shell_
Qt::WindowType type = window->window()->type();
auto *transientParent = window->transientParent();
- if (type == Qt::Popup && transientParent && transientParent->object())
+ if (type == Qt::Popup && transientParent && transientParent->wlSurface())
setPopup(transientParent, m_window->display()->lastInputDevice(), m_window->display()->lastInputSerial());
- else if (transientParent && transientParent->object())
+ else if (transientParent && transientParent->wlSurface())
updateTransientParent(transientParent->window());
else
setTopLevel();
@@ -234,11 +234,9 @@ void QWaylandWlShellSurface::updateTransientParent(QWindow *parent)
|| testShowWithoutActivating(m_window->window()))
flags |= WL_SHELL_SURFACE_TRANSIENT_INACTIVE;
- Q_ASSERT(parent_wayland_window->object());
- set_transient(parent_wayland_window->object(),
- transientPos.x(),
- transientPos.y(),
- flags);
+ auto *parentSurface = parent_wayland_window->wlSurface();
+ Q_ASSERT(parentSurface);
+ set_transient(parentSurface, transientPos.x(), transientPos.y(), flags);
}
void QWaylandWlShellSurface::setPopup(QWaylandWindow *parent, QWaylandInputDevice *device, uint serial)
@@ -261,9 +259,10 @@ void QWaylandWlShellSurface::setPopup(QWaylandWindow *parent, QWaylandInputDevic
transientPos.setY(transientPos.y() + parent_wayland_window->decoration()->margins().top());
}
- Q_ASSERT(parent_wayland_window->object());
- set_popup(device->wl_seat(), serial, parent_wayland_window->object(),
- transientPos.x(), transientPos.y(), 0);
+ auto *parentSurface = parent_wayland_window->wlSurface();
+ Q_ASSERT(parentSurface);
+ uint flags = 0;
+ set_popup(device->wl_seat(), serial, parentSurface, transientPos.x(), transientPos.y(), flags);
}
void QWaylandWlShellSurface::shell_surface_ping(uint32_t serial)
diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp
index 3eda43d7c..7e242c4a5 100644
--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp
+++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp
@@ -74,7 +74,7 @@ QWaylandXdgPopupV5 *QWaylandXdgShellV5::createXdgPopup(QWaylandWindow *window, Q
if (!parentWindow)
return nullptr;
- ::wl_surface *parentSurface = parentWindow->object();
+ ::wl_surface *parentSurface = parentWindow->wlSurface();
if (m_popupSerial == 0)
m_popupSerial = inputDevice->serial();
@@ -84,7 +84,7 @@ QWaylandXdgPopupV5 *QWaylandXdgShellV5::createXdgPopup(QWaylandWindow *window, Q
int x = position.x() + parentWindow->frameMargins().left();
int y = position.y() + parentWindow->frameMargins().top();
- auto popup = new QWaylandXdgPopupV5(get_xdg_popup(window->object(), parentSurface, seat, m_popupSerial, x, y), window);
+ auto popup = new QWaylandXdgPopupV5(get_xdg_popup(window->wlSurface(), parentSurface, seat, m_popupSerial, x, y), window);
m_popups.append(window);
QObject::connect(popup, &QWaylandXdgPopupV5::destroyed, [this, window](){
m_popups.removeOne(window);
diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp
index e9f64e2e6..e8bff9193 100644
--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp
+++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp
@@ -54,7 +54,7 @@ namespace QtWaylandClient {
QWaylandXdgSurfaceV5::QWaylandXdgSurfaceV5(QWaylandXdgShellV5 *shell, QWaylandWindow *window)
: QWaylandShellSurface(window)
- , QtWayland::xdg_surface_v5(shell->get_xdg_surface(window->object()))
+ , QtWayland::xdg_surface_v5(shell->get_xdg_surface(window->wlSurface()))
, m_window(window)
, m_shell(shell)
{
diff --git a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
index 980e4a601..e81aa9038 100644
--- a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
+++ b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
@@ -425,7 +425,7 @@ QWaylandXdgShellV6::~QWaylandXdgShellV6()
QWaylandXdgSurfaceV6 *QWaylandXdgShellV6::getXdgSurface(QWaylandWindow *window)
{
- return new QWaylandXdgSurfaceV6(this, get_xdg_surface(window->object()), window);
+ return new QWaylandXdgSurfaceV6(this, get_xdg_surface(window->wlSurface()), window);
}
void QWaylandXdgShellV6::zxdg_shell_v6_ping(uint32_t serial)
diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
index f55298134..ca8da80b7 100644
--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
+++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
@@ -463,7 +463,7 @@ QWaylandXdgShell::~QWaylandXdgShell()
QWaylandXdgSurface *QWaylandXdgShell::getXdgSurface(QWaylandWindow *window)
{
- return new QWaylandXdgSurface(this, get_xdg_surface(window->object()), window);
+ return new QWaylandXdgSurface(this, get_xdg_surface(window->wlSurface()), window);
}
void QWaylandXdgShell::xdg_wm_base_ping(uint32_t serial)
diff --git a/src/qtwaylandscanner/qtwaylandscanner.cpp b/src/qtwaylandscanner/qtwaylandscanner.cpp
index 56045e880..7f3dc5ad6 100644
--- a/src/qtwaylandscanner/qtwaylandscanner.cpp
+++ b/src/qtwaylandscanner/qtwaylandscanner.cpp
@@ -986,6 +986,7 @@ bool Scanner::process()
printf("\n");
printf(" struct ::%s *object() { return m_%s; }\n", interfaceName, interfaceName);
printf(" const struct ::%s *object() const { return m_%s; }\n", interfaceName, interfaceName);
+ printf(" static %s *fromObject(struct ::%s *object);\n", interfaceName, interfaceName);
printf("\n");
printf(" bool isInitialized() const;\n");
printf("\n");
@@ -1130,6 +1131,16 @@ bool Scanner::process()
printf(" }\n");
printf("\n");
+ printf(" %s *%s::fromObject(struct ::%s *object)\n", interfaceName, interfaceName, interfaceName);
+ printf(" {\n");
+ if (hasEvents) {
+ printf(" if (wl_proxy_get_listener((struct ::wl_proxy *)object) != (void *)&m_%s_listener)\n", interfaceName);
+ printf(" return nullptr;\n");
+ }
+ printf(" return static_cast<%s *>(%s_get_user_data(object));\n", interfaceName, interfaceName);
+ printf(" }\n");
+ printf("\n");
+
printf(" bool %s::isInitialized() const\n", interfaceName);
printf(" {\n");
printf(" return m_%s != nullptr;\n", interfaceName);
diff --git a/src/src.pro b/src/src.pro
index 031ff885c..d4244de33 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -28,7 +28,7 @@ qtConfig(wayland-client) {
SUBDIRS += sub_compositor
sub_imports.subdir = imports
- sub_imports.depends += sub-compositor
+ sub_imports.depends += sub-compositor sub-client
sub_imports.target = sub-imports
SUBDIRS += sub_imports
}
diff --git a/sync.profile b/sync.profile
index a06caa377..b754b4d16 100644
--- a/sync.profile
+++ b/sync.profile
@@ -27,6 +27,7 @@
"^qwayland-text-input-unstable-v2.h",
"^qwayland-touch-extension.h",
"^qwayland-wayland.h",
+ "^qwayland-wp-primary-selection-unstable-v1.h",
"^qwayland-xdg-output-unstable-v1.h",
"^wayland-hardware-integration-client-protocol.h",
"^wayland-qt-windowmanager-client-protocol.h",
@@ -36,6 +37,7 @@
"^wayland-text-input-unstable-v2-client-protocol.h",
"^wayland-touch-extension-client-protocol.h",
"^wayland-wayland-client-protocol.h",
+ "^wayland-wp-primary-selection-unstable-v1-client-protocol.h",
"^wayland-xdg-output-unstable-v1-client-protocol.h",
],
"$basedir/src/plugins/shellintegration/xdg-shell" => [
@@ -58,6 +60,7 @@
"^qwayland-server-ivi-application.h",
"^qwayland-server-qt-windowmanager.h",
"^qwayland-server-qt-key-unstable-v1.h",
+ "^qwayland-server-qt-texture-sharing-unstable-v1.h",
"^qwayland-server-scaler.h",
"^qwayland-server-server-buffer-extension.h",
"^qwayland-server-text-input-unstable-v2.h",
@@ -71,6 +74,7 @@
"^wayland-ivi-application-server-protocol.h",
"^wayland-qt-windowmanager-server-protocol.h",
"^wayland-qt-key-unstable-v1-server-protocol.h",
+ "^wayland-qt-texture-sharing-unstable-v1-server-protocol.h",
"^wayland-scaler-server-protocol.h",
"^wayland-server-buffer-extension-server-protocol.h",
"^wayland-text-input-unstable-v2-server-protocol.h",
diff --git a/tests/auto/client/client.pro b/tests/auto/client/client.pro
index 051cb4e3d..61bf67853 100644
--- a/tests/auto/client/client.pro
+++ b/tests/auto/client/client.pro
@@ -6,7 +6,9 @@ SUBDIRS += \
fullscreenshellv1 \
iviapplication \
output \
+ primaryselectionv1 \
seatv4 \
+ seatv5 \
surface \
wl_connect \
xdgoutput \
diff --git a/tests/auto/client/client/tst_client.cpp b/tests/auto/client/client/tst_client.cpp
index 08120c8c2..e9ae5e4b3 100644
--- a/tests/auto/client/client/tst_client.cpp
+++ b/tests/auto/client/client/tst_client.cpp
@@ -36,7 +36,9 @@
#include <QPixmap>
#include <QDrag>
#include <QWindow>
+#if QT_CONFIG(opengl)
#include <QOpenGLWindow>
+#endif
#include <QtTest/QtTest>
#include <QtWaylandClient/private/qwaylandintegration_p.h>
@@ -107,6 +109,7 @@ public:
QPoint mousePressPos;
};
+#if QT_CONFIG(opengl)
class TestGlWindow : public QOpenGLWindow
{
Q_OBJECT
@@ -136,6 +139,7 @@ void TestGlWindow::paintGL()
glClear(GL_COLOR_BUFFER_BIT);
++paintGLCalled;
}
+#endif // QT_CONFIG(opengl)
class tst_WaylandClient : public QObject
{
@@ -176,7 +180,9 @@ private slots:
void dontCrashOnMultipleCommits();
void hiddenTransientParent();
void hiddenPopupParent();
+#if QT_CONFIG(opengl)
void glWindow();
+#endif // QT_CONFIG(opengl)
void longWindowTitle();
private:
@@ -458,6 +464,7 @@ void tst_WaylandClient::hiddenPopupParent()
QTRY_VERIFY(compositor->surface());
}
+#if QT_CONFIG(opengl)
void tst_WaylandClient::glWindow()
{
QSKIP("Skipping GL tests, as not supported by all CI systems: See https://bugreports.qt.io/browse/QTBUG-65802");
@@ -483,6 +490,7 @@ void tst_WaylandClient::glWindow()
testWindow->setVisible(false);
QTRY_VERIFY(!compositor->surface());
}
+#endif // QT_CONFIG(opengl)
void tst_WaylandClient::longWindowTitle()
{
diff --git a/tests/auto/client/primaryselectionv1/primaryselectionv1.pro b/tests/auto/client/primaryselectionv1/primaryselectionv1.pro
new file mode 100644
index 000000000..9d00562df
--- /dev/null
+++ b/tests/auto/client/primaryselectionv1/primaryselectionv1.pro
@@ -0,0 +1,7 @@
+include (../shared/shared.pri)
+
+WAYLANDSERVERSOURCES += \
+ $$PWD/../../../../src/3rdparty/protocol/wp-primary-selection-unstable-v1.xml
+
+TARGET = tst_primaryselectionv1
+SOURCES += tst_primaryselectionv1.cpp
diff --git a/tests/auto/client/primaryselectionv1/tst_primaryselectionv1.cpp b/tests/auto/client/primaryselectionv1/tst_primaryselectionv1.cpp
new file mode 100644
index 000000000..281e4c5d1
--- /dev/null
+++ b/tests/auto/client/primaryselectionv1/tst_primaryselectionv1.cpp
@@ -0,0 +1,466 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+
+#include <qwayland-server-wp-primary-selection-unstable-v1.h>
+
+#include <QtGui/QRasterWindow>
+#include <QtGui/QOpenGLWindow>
+#include <QtGui/QClipboard>
+#include <QtCore/private/qcore_unix_p.h>
+
+#include <fcntl.h>
+
+using namespace MockCompositor;
+
+constexpr int primarySelectionVersion = 1; // protocol VERSION, not the name suffix (_v1)
+
+class PrimarySelectionDeviceV1;
+class PrimarySelectionDeviceManagerV1;
+
+class PrimarySelectionOfferV1 : public QObject, public QtWaylandServer::zwp_primary_selection_offer_v1
+{
+ Q_OBJECT
+public:
+ explicit PrimarySelectionOfferV1(PrimarySelectionDeviceV1 *device, wl_client *client, int version)
+ : zwp_primary_selection_offer_v1(client, 0, version)
+ , m_device(device)
+ {}
+ void send_offer() = delete;
+ void sendOffer(const QString &offer)
+ {
+ zwp_primary_selection_offer_v1::send_offer(offer);
+ m_mimeTypes << offer;
+ }
+
+ PrimarySelectionDeviceV1 *m_device = nullptr;
+ QStringList m_mimeTypes;
+
+signals:
+ void receive(QString mimeType, int fd);
+
+protected:
+ void zwp_primary_selection_offer_v1_destroy_resource(Resource *resource) override
+ {
+ Q_UNUSED(resource);
+ delete this;
+ }
+
+ void zwp_primary_selection_offer_v1_receive(Resource *resource, const QString &mime_type, int32_t fd) override
+ {
+ Q_UNUSED(resource);
+ QTRY_VERIFY(m_mimeTypes.contains(mime_type));
+ emit receive(mime_type, fd);
+ }
+
+ void zwp_primary_selection_offer_v1_destroy(Resource *resource) override;
+};
+
+class PrimarySelectionSourceV1 : public QObject, public QtWaylandServer::zwp_primary_selection_source_v1
+{
+ Q_OBJECT
+public:
+ explicit PrimarySelectionSourceV1(wl_client *client, int id, int version)
+ : zwp_primary_selection_source_v1(client, id, version)
+ {
+ }
+ QStringList m_offers;
+protected:
+ void zwp_primary_selection_source_v1_destroy_resource(Resource *resource) override
+ {
+ Q_UNUSED(resource);
+ delete this;
+ }
+ void zwp_primary_selection_source_v1_offer(Resource *resource, const QString &mime_type) override
+ {
+ Q_UNUSED(resource);
+ m_offers << mime_type;
+ }
+ void zwp_primary_selection_source_v1_destroy(Resource *resource) override
+ {
+ wl_resource_destroy(resource->handle);
+ }
+};
+
+class PrimarySelectionDeviceV1 : public QObject, public QtWaylandServer::zwp_primary_selection_device_v1
+{
+ Q_OBJECT
+public:
+ explicit PrimarySelectionDeviceV1(PrimarySelectionDeviceManagerV1 *manager, Seat *seat)
+ : m_manager(manager)
+ , m_seat(seat)
+ {}
+
+ void send_data_offer(::wl_resource *resource) = delete;
+
+ PrimarySelectionOfferV1 *sendDataOffer(::wl_client *client, const QStringList &mimeTypes = {});
+
+ PrimarySelectionOfferV1 *sendDataOffer(const QStringList &mimeTypes = {}) // creates a new offer for the focused surface and sends it
+ {
+ Q_ASSERT(m_seat->m_capabilities & Seat::capability_keyboard);
+ Q_ASSERT(m_seat->m_keyboard->m_enteredSurface);
+ auto *client = m_seat->m_keyboard->m_enteredSurface->resource()->client();
+ return sendDataOffer(client, mimeTypes);
+ }
+
+ void send_selection(::wl_resource *resource) = delete;
+ void sendSelection(PrimarySelectionOfferV1 *offer)
+ {
+ auto *client = offer->resource()->client();
+ for (auto *resource : resourceMap().values(client))
+ zwp_primary_selection_device_v1::send_selection(resource->handle, offer->resource()->handle);
+ m_sentSelectionOffers << offer;
+ }
+
+ PrimarySelectionDeviceManagerV1 *m_manager = nullptr;
+ Seat *m_seat = nullptr;
+ QVector<PrimarySelectionOfferV1 *> m_sentSelectionOffers;
+ PrimarySelectionSourceV1 *m_selectionSource = nullptr;
+ uint m_serial = 0;
+
+protected:
+ void zwp_primary_selection_device_v1_set_selection(Resource *resource, ::wl_resource *source, uint32_t serial) override
+ {
+ Q_UNUSED(resource);
+ m_selectionSource = fromResource<PrimarySelectionSourceV1>(source);
+ m_serial = serial;
+ }
+ void zwp_primary_selection_device_v1_destroy(Resource *resource) override
+ {
+ wl_resource_destroy(resource->handle);
+ }
+ void zwp_primary_selection_device_v1_destroy_resource(Resource *resource) override
+ {
+ Q_UNUSED(resource);
+ delete this;
+ }
+};
+
+class PrimarySelectionDeviceManagerV1 : public Global, public QtWaylandServer::zwp_primary_selection_device_manager_v1
+{
+ Q_OBJECT
+public:
+ explicit PrimarySelectionDeviceManagerV1(CoreCompositor *compositor, int version = 1)
+ : QtWaylandServer::zwp_primary_selection_device_manager_v1(compositor->m_display, version)
+ , m_version(version)
+ {}
+ bool isClean() override
+ {
+ for (auto *device : qAsConst(m_devices)) {
+ // The client should not leak selection offers, i.e. if this fails, there is a missing
+ // zwp_primary_selection_offer_v1.destroy request
+ if (!device->m_sentSelectionOffers.empty())
+ return false;
+ }
+ return true;
+ }
+
+ PrimarySelectionDeviceV1 *deviceFor(Seat *seat)
+ {
+ Q_ASSERT(seat);
+ if (auto *device = m_devices.value(seat, nullptr))
+ return device;
+
+ auto *device = new PrimarySelectionDeviceV1(this, seat);
+ m_devices[seat] = device;
+ return device;
+ }
+
+ int m_version = 1; // TODO: Remove on libwayland upgrade
+ QMap<Seat *, PrimarySelectionDeviceV1 *> m_devices;
+ QVector<PrimarySelectionSourceV1 *> m_sources;
+protected:
+ void zwp_primary_selection_device_manager_v1_destroy(Resource *resource) override
+ {
+ // The protocol doesn't say whether managed objects should be destroyed as well,
+ // so leave them alone, they'll be cleaned up in the destructor anyway
+ wl_resource_destroy(resource->handle);
+ }
+
+ void zwp_primary_selection_device_manager_v1_create_source(Resource *resource, uint32_t id) override
+ {
+ int version = m_version;
+ m_sources << new PrimarySelectionSourceV1(resource->client(), id, version);
+ }
+ void zwp_primary_selection_device_manager_v1_get_device(Resource *resource, uint32_t id, ::wl_resource *seatResource) override
+ {
+ auto *seat = fromResource<Seat>(seatResource);
+ QVERIFY(seat);
+ auto *device = deviceFor(seat);
+ device->add(resource->client(), id, resource->version());
+ }
+};
+
+PrimarySelectionOfferV1 *PrimarySelectionDeviceV1::sendDataOffer(wl_client *client, const QStringList &mimeTypes)
+{
+ Q_ASSERT(client);
+ auto *offer = new PrimarySelectionOfferV1(this, client, m_manager->m_version);
+ for (auto *resource : resourceMap().values(client))
+ zwp_primary_selection_device_v1::send_data_offer(resource->handle, offer->resource()->handle);
+ for (const auto &mimeType : mimeTypes)
+ offer->sendOffer(mimeType);
+ return offer;
+}
+
+void PrimarySelectionOfferV1::zwp_primary_selection_offer_v1_destroy(QtWaylandServer::zwp_primary_selection_offer_v1::Resource *resource)
+{
+ bool removed = m_device->m_sentSelectionOffers.removeOne(this);
+ QVERIFY(removed);
+ wl_resource_destroy(resource->handle);
+}
+
+class PrimarySelectionCompositor : public DefaultCompositor {
+public:
+ explicit PrimarySelectionCompositor()
+ {
+ exec([this] {
+ m_config.autoConfigure = true;
+ add<PrimarySelectionDeviceManagerV1>(primarySelectionVersion);
+ });
+ }
+ PrimarySelectionDeviceV1 *primarySelectionDevice(int i = 0) {
+ return get<PrimarySelectionDeviceManagerV1>()->deviceFor(get<Seat>(i));
+ }
+};
+
+class tst_primaryselectionv1 : public QObject, private PrimarySelectionCompositor
+{
+ Q_OBJECT
+private slots:
+ void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
+ void initTestCase();
+ void bindsToManager();
+ void createsPrimaryDevice();
+ void createsPrimaryDeviceForNewSeats();
+ void pasteAscii();
+ void pasteUtf8();
+ void destroysPreviousSelection();
+ void copy();
+};
+
+void tst_primaryselectionv1::initTestCase()
+{
+ QCOMPOSITOR_TRY_VERIFY(pointer());
+ QCOMPOSITOR_TRY_VERIFY(!pointer()->resourceMap().empty());
+ QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 4);
+
+ QCOMPOSITOR_TRY_VERIFY(keyboard());
+}
+
+void tst_primaryselectionv1::bindsToManager()
+{
+ QCOMPOSITOR_TRY_COMPARE(get<PrimarySelectionDeviceManagerV1>()->resourceMap().size(), 1);
+ QCOMPOSITOR_TRY_COMPARE(get<PrimarySelectionDeviceManagerV1>()->resourceMap().first()->version(), primarySelectionVersion);
+}
+
+void tst_primaryselectionv1::createsPrimaryDevice()
+{
+ QCOMPOSITOR_TRY_VERIFY(primarySelectionDevice());
+ QCOMPOSITOR_TRY_VERIFY(primarySelectionDevice()->resourceMap().contains(client()));
+ QCOMPOSITOR_TRY_COMPARE(primarySelectionDevice()->resourceMap().value(client())->version(), primarySelectionVersion);
+ QTRY_VERIFY(QGuiApplication::clipboard()->supportsSelection());
+}
+
+void tst_primaryselectionv1::createsPrimaryDeviceForNewSeats()
+{
+ exec([=] { add<Seat>(); });
+ QCOMPOSITOR_TRY_VERIFY(primarySelectionDevice(1));
+}
+
+void tst_primaryselectionv1::pasteAscii()
+{
+ class Window : public QRasterWindow {
+ public:
+ void mousePressEvent(QMouseEvent *event) override
+ {
+ Q_UNUSED(event);
+ auto *mimeData = QGuiApplication::clipboard()->mimeData(QClipboard::Selection);
+ m_formats = mimeData->formats();
+ m_text = QGuiApplication::clipboard()->text(QClipboard::Selection);
+ }
+ QStringList m_formats;
+ QString m_text;
+ };
+
+ Window window;
+ window.resize(64, 64);
+ window.show();
+
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+ exec([&] {
+ auto *surface = xdgSurface()->m_surface;
+ keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
+
+ auto *device = primarySelectionDevice();
+ auto *offer = device->sendDataOffer({"text/plain"});
+ connect(offer, &PrimarySelectionOfferV1::receive, [](QString mimeType, int fd) {
+ QFile file;
+ file.open(fd, QIODevice::WriteOnly, QFile::FileHandleFlag::AutoCloseHandle);
+ QCOMPARE(mimeType, "text/plain");
+ file.write(QByteArray("normal ascii"));
+ file.close();
+ });
+ device->sendSelection(offer);
+
+ pointer()->sendEnter(surface, {32, 32});
+ pointer()->sendButton(client(), BTN_MIDDLE, 1);
+ pointer()->sendButton(client(), BTN_MIDDLE, 0);
+ });
+ QTRY_COMPARE(window.m_formats, QStringList{"text/plain"});
+ QTRY_COMPARE(window.m_text, "normal ascii");
+}
+
+void tst_primaryselectionv1::pasteUtf8()
+{
+ class Window : public QRasterWindow {
+ public:
+ void mousePressEvent(QMouseEvent *event) override
+ {
+ Q_UNUSED(event);
+ auto *mimeData = QGuiApplication::clipboard()->mimeData(QClipboard::Selection);
+ m_formats = mimeData->formats();
+ m_text = QGuiApplication::clipboard()->text(QClipboard::Selection);
+ }
+ QStringList m_formats;
+ QString m_text;
+ };
+
+ Window window;
+ window.resize(64, 64);
+ window.show();
+
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+ exec([&] {
+ auto *surface = xdgSurface()->m_surface;
+ keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
+
+ auto *device = primarySelectionDevice();
+ auto *offer = device->sendDataOffer({"text/plain", "text/plain;charset=utf-8"});
+ connect(offer, &PrimarySelectionOfferV1::receive, [](QString mimeType, int fd) {
+ QFile file;
+ file.open(fd, QIODevice::WriteOnly, QFile::FileHandleFlag::AutoCloseHandle);
+ QCOMPARE(mimeType, "text/plain;charset=utf-8");
+ file.write(QByteArray("face with tears of joy: 😂"));
+ file.close();
+ });
+ device->sendSelection(offer);
+
+ pointer()->sendEnter(surface, {32, 32});
+ pointer()->sendButton(client(), BTN_MIDDLE, 1);
+ pointer()->sendButton(client(), BTN_MIDDLE, 0);
+ });
+ QTRY_COMPARE(window.m_formats, QStringList({"text/plain", "text/plain;charset=utf-8"}));
+ QTRY_COMPARE(window.m_text, "face with tears of joy: 😂");
+}
+
+void tst_primaryselectionv1::destroysPreviousSelection()
+{
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ // When the client receives a selection event, it is required to destroy the previous offer
+ exec([&] {
+ auto *surface = xdgSurface()->m_surface;
+ keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
+
+ auto *offer = primarySelectionDevice()->sendDataOffer({"text/plain"});
+ primarySelectionDevice()->sendSelection(offer);
+ });
+
+ exec([&] {
+ auto *offer = primarySelectionDevice()->sendDataOffer({"text/plain"});
+ primarySelectionDevice()->sendSelection(offer);
+ QCOMPARE(primarySelectionDevice()->m_sentSelectionOffers.size(), 2);
+ });
+
+ // Verify the first offer gets destroyed
+ QCOMPOSITOR_TRY_COMPARE(primarySelectionDevice()->m_sentSelectionOffers.size(), 1);
+}
+
+void tst_primaryselectionv1::copy()
+{
+ class Window : public QRasterWindow {
+ public:
+ void mousePressEvent(QMouseEvent *event) override
+ {
+ Q_UNUSED(event);
+ QGuiApplication::clipboard()->setText("face with tears of joy: 😂", QClipboard::Selection);
+ }
+ QStringList m_formats;
+ QString m_text;
+ };
+
+ Window window;
+ window.resize(64, 64);
+ window.show();
+
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+ QVector<uint> mouseSerials;
+ exec([&] {
+ auto *surface = xdgSurface()->m_surface;
+ keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
+ pointer()->sendEnter(surface, {32, 32});
+ mouseSerials << pointer()->sendButton(client(), BTN_MIDDLE, 1);
+ mouseSerials << pointer()->sendButton(client(), BTN_MIDDLE, 0);
+ });
+ QCOMPOSITOR_TRY_VERIFY(primarySelectionDevice()->m_selectionSource);
+ QCOMPOSITOR_TRY_VERIFY(mouseSerials.contains(primarySelectionDevice()->m_serial));
+ QByteArray pastedBuf;
+ exec([&](){
+ auto *source = primarySelectionDevice()->m_selectionSource;
+ QCOMPARE(source->m_offers, QStringList({"text/plain", "text/plain;charset=utf-8"}));
+ int fd[2];
+ if (pipe(fd) == -1)
+ QSKIP("Failed to create pipe");
+ fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL, 0) | O_NONBLOCK);
+ source->send_send("text/plain;charset=utf-8", fd[1]);
+ auto *notifier = new QSocketNotifier(fd[0], QSocketNotifier::Read, this);
+ connect(notifier, &QSocketNotifier::activated, this, [&](int fd) {
+ exec([&]{
+ static char buf[1024];
+ int n = QT_READ(fd, buf, sizeof buf);
+ if (n <= 0) {
+ delete notifier;
+ close(fd);
+ } else {
+ pastedBuf.append(buf, n);
+ }
+ });
+ });
+ });
+
+ QCOMPOSITOR_TRY_VERIFY(pastedBuf.size()); // this assumes we got everything in one read
+ auto pasted = QString::fromUtf8(pastedBuf);
+ QCOMPARE(pasted, "face with tears of joy: 😂");
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_primaryselectionv1)
+#include "tst_primaryselectionv1.moc"
diff --git a/tests/auto/client/seatv4/tst_seatv4.cpp b/tests/auto/client/seatv4/tst_seatv4.cpp
index 7dc2e727a..77304deaf 100644
--- a/tests/auto/client/seatv4/tst_seatv4.cpp
+++ b/tests/auto/client/seatv4/tst_seatv4.cpp
@@ -80,6 +80,7 @@ private slots:
void bitmapCursor();
void hidpiBitmapCursor();
void hidpiBitmapCursorNonInt();
+ void animatedCursor();
#endif
};
@@ -554,6 +555,36 @@ void tst_seatv4::hidpiBitmapCursorNonInt()
QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(25, 25));
}
+void tst_seatv4::animatedCursor()
+{
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.setCursor(Qt::WaitCursor); // TODO: verify that the theme has an animated wait cursor or skip test
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); });
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface());
+
+ // We should get the first buffer without waiting for a frame callback
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface()->m_committed.buffer);
+ QSignalSpy bufferSpy(exec([=] { return cursorSurface(); }), &Surface::bufferCommitted);
+
+ exec([&] {
+ // Make sure no extra buffers have arrived
+ QVERIFY(bufferSpy.empty());
+
+ // The client should send a frame request in order to time animations correctly
+ QVERIFY(!cursorSurface()->m_waitingFrameCallbacks.empty());
+
+ // Tell the client it's time to animate
+ cursorSurface()->sendFrameCallbacks();
+ });
+
+ // Verify that we get a new cursor buffer
+ QTRY_COMPARE(bufferSpy.count(), 1);
+}
+
#endif // QT_CONFIG(cursor)
QCOMPOSITOR_TEST_MAIN(tst_seatv4)
diff --git a/tests/auto/client/seatv5/seatv5.pro b/tests/auto/client/seatv5/seatv5.pro
new file mode 100644
index 000000000..2081845ef
--- /dev/null
+++ b/tests/auto/client/seatv5/seatv5.pro
@@ -0,0 +1,4 @@
+include (../shared/shared.pri)
+
+TARGET = tst_seatv5
+SOURCES += tst_seatv5.cpp
diff --git a/tests/auto/client/seatv5/tst_seatv5.cpp b/tests/auto/client/seatv5/tst_seatv5.cpp
new file mode 100644
index 000000000..5b9235d9a
--- /dev/null
+++ b/tests/auto/client/seatv5/tst_seatv5.cpp
@@ -0,0 +1,387 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+#include <QtGui/QRasterWindow>
+#include <QtGui/QOpenGLWindow>
+
+using namespace MockCompositor;
+
+class SeatV5Compositor : public DefaultCompositor {
+public:
+ explicit SeatV5Compositor()
+ {
+ exec([this] {
+ m_config.autoConfigure = true;
+
+ removeAll<Seat>();
+
+ uint capabilities = MockCompositor::Seat::capability_pointer;
+ int version = 5;
+ add<Seat>(capabilities, version);
+ });
+ }
+
+ Pointer *pointer()
+ {
+ auto *seat = get<Seat>();
+ Q_ASSERT(seat);
+ return seat->m_pointer;
+ }
+};
+
+class tst_seatv5 : public QObject, private SeatV5Compositor
+{
+ Q_OBJECT
+private slots:
+ void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
+ void bindsToSeat();
+ void createsPointer();
+ void setsCursorOnEnter();
+ void usesEnterSerial();
+ void simpleAxis_data();
+ void simpleAxis();
+ void fingerScroll();
+ void fingerScrollSlow();
+ void wheelDiscreteScroll();
+};
+
+void tst_seatv5::bindsToSeat()
+{
+ QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().size(), 1);
+ QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().first()->version(), 5);
+}
+
+void tst_seatv5::createsPointer()
+{
+ QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().size(), 1);
+ QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 5);
+}
+
+void tst_seatv5::setsCursorOnEnter()
+{
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] {
+ auto *surface = xdgSurface()->m_surface;
+ pointer()->sendEnter(surface, {0, 0});
+ pointer()->sendFrame(surface->resource()->client());
+ });
+
+ QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface());
+}
+
+void tst_seatv5::usesEnterSerial()
+{
+ QSignalSpy setCursorSpy(exec([=] { return pointer(); }), &Pointer::setCursor);
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ uint enterSerial = exec([=] {
+ return pointer()->sendEnter(xdgSurface()->m_surface, {0, 0});
+ });
+ QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface());
+
+ QTRY_COMPARE(setCursorSpy.count(), 1);
+ QCOMPARE(setCursorSpy.takeFirst().at(0).toUInt(), enterSerial);
+}
+
+class WheelWindow : QRasterWindow {
+public:
+ WheelWindow()
+ {
+ resize(64, 64);
+ show();
+ }
+ void wheelEvent(QWheelEvent *event) override
+ {
+ QRasterWindow::wheelEvent(event);
+// qDebug() << event << "angleDelta" << event->angleDelta() << "pixelDelta" << event->pixelDelta();
+
+ if (event->phase() == Qt::ScrollUpdate || event->phase() == Qt::NoScrollPhase) {
+ // Angle delta should always be provided (says docs, but QPA sends compatibility events
+ // for Qt4 with zero angleDelta, and with a delta)
+ QVERIFY(!event->angleDelta().isNull() || event->delta());
+ } else {
+ // Shouldn't have deltas in the other phases
+ QCOMPARE(event->angleDelta(), QPoint(0, 0));
+ QCOMPARE(event->pixelDelta(), QPoint(0, 0));
+ }
+
+ // The axis vector of the event is already in surface space, so there is now way to tell
+ // whether it is inverted or not.
+ QCOMPARE(event->inverted(), false);
+
+ // We didn't press any buttons
+ QCOMPARE(event->buttons(), Qt::NoButton);
+
+ if (!event->angleDelta().isNull()) {
+ if (event->orientation() == Qt::Horizontal)
+ QCOMPARE(event->delta(), event->angleDelta().x());
+ else
+ QCOMPARE(event->delta(), event->angleDelta().y());
+ }
+
+ m_events.append(Event{event});
+ }
+ struct Event // Because I didn't find a convenient way to copy it entirely
+ {
+ explicit Event() = default;
+ explicit Event(const QWheelEvent *event)
+ : phase(event->phase())
+ , pixelDelta(event->pixelDelta())
+ , angleDelta(event->angleDelta())
+ , orientation(event->orientation())
+ , source(event->source())
+ {
+ }
+ const Qt::ScrollPhase phase{};
+ const QPoint pixelDelta;
+ const QPoint angleDelta; // eights of a degree, positive is upwards, left
+ const Qt::Orientation orientation{};
+ const Qt::MouseEventSource source{};
+ };
+ QVector<Event> m_events;
+};
+
+void tst_seatv5::simpleAxis_data()
+{
+ QTest::addColumn<uint>("axis");
+ QTest::addColumn<qreal>("value");
+ QTest::addColumn<Qt::Orientation>("orientation");
+ QTest::addColumn<QPoint>("angleDelta");
+
+ // Directions in regular windows/linux terms (no "natural" scrolling)
+ QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << Qt::Vertical << QPoint{0, -12};
+ QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << Qt::Vertical << QPoint{0, 12};
+ QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << Qt::Horizontal << QPoint{-12, 0};
+ QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << Qt::Horizontal << QPoint{12, 0};
+ QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << Qt::Vertical << QPoint{0, 120};
+}
+
+void tst_seatv5::simpleAxis()
+{
+ QFETCH(uint, axis);
+ QFETCH(qreal, value);
+ QFETCH(Qt::Orientation, orientation);
+ QFETCH(QPoint, angleDelta);
+
+ WheelWindow window;
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] {
+ auto *p = pointer();
+ p->sendEnter(xdgToplevel()->surface(), {32, 32});
+ p->sendFrame(client());
+ p->sendAxis(
+ client(),
+ Pointer::axis(axis),
+ value // Length of vector in surface-local space. i.e. positive is downwards
+ );
+ p->sendFrame(client());
+ });
+
+ QTRY_VERIFY(!window.m_events.empty());
+ {
+ auto e = window.m_events.takeFirst();
+ QCOMPARE(e.phase, Qt::NoScrollPhase);
+ // Pixel delta should only be set if we know it's a high-res input device (which we don't)
+ QCOMPARE(e.pixelDelta, QPoint(0, 0));
+ // There has been no information about what created the event.
+ // Documentation says not synthesized is appropriate in such cases
+ QCOMPARE(e.source, Qt::MouseEventNotSynthesized);
+ QCOMPARE(e.orientation, orientation);
+ QCOMPARE(e.angleDelta, angleDelta);
+ }
+
+ // Sending axis_stop is not mandatory when axis source != finger
+}
+
+void tst_seatv5::fingerScroll()
+{
+ WheelWindow window;
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] {
+ auto *p = pointer();
+ auto *c = client();
+ p->sendEnter(xdgToplevel()->surface(), {32, 32});
+ p->sendFrame(c);
+ p->sendAxisSource(c, Pointer::axis_source_finger);
+ p->sendAxis(c, Pointer::axis_vertical_scroll, 10);
+ p->sendFrame(c);
+ });
+
+ QTRY_VERIFY(!window.m_events.empty());
+ {
+ auto e = window.m_events.takeFirst();
+ QCOMPARE(e.phase, Qt::ScrollBegin);
+ QCOMPARE(e.angleDelta, QPoint());
+ QCOMPARE(e.pixelDelta, QPoint());
+ }
+
+ QTRY_VERIFY(!window.m_events.empty());
+ // For some reason we send two ScrollBegins, one for each direction, not sure if this is really
+ // necessary, (could be removed from QtBase, hence the conditional below.
+ if (window.m_events.first().phase == Qt::ScrollBegin) {
+ auto e = window.m_events.takeFirst();
+ QCOMPARE(e.angleDelta, QPoint());
+ QCOMPARE(e.pixelDelta, QPoint());
+ }
+
+ QTRY_VERIFY(!window.m_events.empty());
+ {
+ auto e = window.m_events.takeFirst();
+ QCOMPARE(e.phase, Qt::ScrollUpdate);
+ QCOMPARE(e.orientation, Qt::Vertical);
+// QCOMPARE(e.angleDelta, angleDelta); // TODO: what should this be?
+ QCOMPARE(e.pixelDelta, QPoint(0, 10));
+ QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // A finger is not a wheel
+ }
+
+ QTRY_VERIFY(window.m_events.empty());
+
+ // Scroll horizontally as well
+ exec([=] {
+ pointer()->sendAxisSource(client(), Pointer::axis_source_finger);
+ pointer()->sendAxis(client(), Pointer::axis_horizontal_scroll, 10);
+ pointer()->sendFrame(client());
+ });
+ QTRY_VERIFY(!window.m_events.empty());
+ {
+ auto e = window.m_events.takeFirst();
+ QCOMPARE(e.phase, Qt::ScrollUpdate);
+ QCOMPARE(e.orientation, Qt::Horizontal);
+ QCOMPARE(e.pixelDelta, QPoint(10, 0));
+ QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // A finger is not a wheel
+ }
+
+ // Scroll diagonally
+ exec([=] {
+ pointer()->sendAxisSource(client(), Pointer::axis_source_finger);
+ pointer()->sendAxis(client(), Pointer::axis_horizontal_scroll, 10);
+ pointer()->sendAxis(client(), Pointer::axis_vertical_scroll, 10);
+ pointer()->sendFrame(client());
+ });
+ QTRY_VERIFY(!window.m_events.empty());
+ {
+ auto e = window.m_events.takeFirst();
+ QCOMPARE(e.phase, Qt::ScrollUpdate);
+ QCOMPARE(e.pixelDelta, QPoint(10, 10));
+ QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // A finger is not a wheel
+ }
+
+ // For diagonal events, Qt sends an additional compatibility ScrollUpdate event
+ if (window.m_events.first().phase == Qt::ScrollUpdate) {
+ auto e = window.m_events.takeFirst();
+ QCOMPARE(e.angleDelta, QPoint());
+ QCOMPARE(e.pixelDelta, QPoint());
+ }
+
+ QVERIFY(window.m_events.empty());
+
+ // Sending axis_stop is mandatory when axis source == finger
+ exec([=] {
+ pointer()->sendAxisStop(client(), Pointer::axis_vertical_scroll);
+ pointer()->sendFrame(client());
+ });
+
+ QTRY_VERIFY(!window.m_events.empty());
+ {
+ auto e = window.m_events.takeFirst();
+ QCOMPARE(e.phase, Qt::ScrollEnd);
+ }
+}
+
+
+void tst_seatv5::fingerScrollSlow()
+{
+ WheelWindow window;
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] {
+ auto *p = pointer();
+ auto *c = client();
+ p->sendEnter(xdgToplevel()->surface(), {32, 32});
+ p->sendFrame(c);
+ // Send 10 really small updates
+ for (int i = 0; i < 10; ++i) {
+ p->sendAxisSource(c, Pointer::axis_source_finger);
+ p->sendAxis(c, Pointer::axis_vertical_scroll, 0.1);
+ p->sendFrame(c);
+ }
+ p->sendAxisStop(c, Pointer::axis_vertical_scroll);
+ p->sendFrame(c);
+ });
+
+ QTRY_VERIFY(!window.m_events.empty());
+ QPoint accumulated;
+ while (window.m_events.first().phase != Qt::ScrollEnd) {
+ auto e = window.m_events.takeFirst();
+ accumulated += e.pixelDelta;
+ QTRY_VERIFY(!window.m_events.empty());
+ }
+ QCOMPARE(accumulated.y(), 1);
+}
+void tst_seatv5::wheelDiscreteScroll()
+{
+ WheelWindow window;
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] {
+ auto *p = pointer();
+ auto *c = client();
+ p->sendEnter(xdgToplevel()->surface(), {32, 32});
+ p->sendFrame(c);
+ p->sendAxisSource(c, Pointer::axis_source_wheel);
+ p->sendAxisDiscrete(c, Pointer::axis_vertical_scroll, 1); // 1 click downwards
+ p->sendAxis(c, Pointer::axis_vertical_scroll, 1.0);
+ p->sendFrame(c);
+ });
+
+ QTRY_VERIFY(!window.m_events.empty());
+ {
+ auto e = window.m_events.takeFirst();
+ QCOMPARE(e.phase, Qt::NoScrollPhase);
+ QCOMPARE(e.orientation, Qt::Vertical);
+ // According to the docs the angle delta is in eights of a degree and most mice have
+ // 1 click = 15 degrees. The angle delta should therefore be:
+ // 15 degrees / (1/8 eights per degrees) = 120 eights of degrees.
+ QCOMPARE(e.angleDelta, QPoint(0, -120));
+ // Click scrolls are not continuous and should not have a pixel delta
+ QCOMPARE(e.pixelDelta, QPoint(0, 0));
+ }
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_seatv5)
+#include "tst_seatv5.moc"
diff --git a/tests/auto/client/shared/corecompositor.h b/tests/auto/client/shared/corecompositor.h
index 875b7d050..254465ee6 100644
--- a/tests/auto/client/shared/corecompositor.h
+++ b/tests/auto/client/shared/corecompositor.h
@@ -125,6 +125,23 @@ public:
}
/*!
+ * \brief Returns the nth global with the given type, if any
+ */
+ template<typename global_type>
+ global_type *get(int index)
+ {
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ for (auto *global : qAsConst(m_globals)) {
+ if (auto *casted = qobject_cast<global_type *>(global)) {
+ if (index--)
+ continue;
+ return casted;
+ }
+ }
+ return nullptr;
+ }
+
+ /*!
* \brief Returns all globals with the given type, if any
*/
template<typename global_type>
diff --git a/tests/auto/client/shared/coreprotocol.cpp b/tests/auto/client/shared/coreprotocol.cpp
index 729d481f8..f9335e783 100644
--- a/tests/auto/client/shared/coreprotocol.cpp
+++ b/tests/auto/client/shared/coreprotocol.cpp
@@ -314,6 +314,39 @@ void Pointer::sendAxis(wl_client *client, axis axis, qreal value)
send_axis(r->handle, time, axis, val);
}
+void Pointer::sendAxisDiscrete(wl_client *client, QtWaylandServer::wl_pointer::axis axis, int discrete)
+{
+ // TODO: assert v5 or newer
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_axis_discrete(r->handle, axis, discrete);
+}
+
+void Pointer::sendAxisSource(wl_client *client, QtWaylandServer::wl_pointer::axis_source source)
+{
+ // TODO: assert v5 or newer
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_axis_source(r->handle, source);
+}
+
+void Pointer::sendAxisStop(wl_client *client, QtWaylandServer::wl_pointer::axis axis)
+{
+ // TODO: assert v5 or newer
+ auto time = m_seat->m_compositor->currentTimeMilliseconds();
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_axis_stop(r->handle, time, axis);
+}
+
+void Pointer::sendFrame(wl_client *client)
+{
+ //TODO: assert version 5 or newer?
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_frame(r->handle);
+}
+
void Pointer::pointer_set_cursor(Resource *resource, uint32_t serial, wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y)
{
Q_UNUSED(resource);
@@ -345,6 +378,7 @@ uint Keyboard::sendEnter(Surface *surface)
const auto pointerResources = resourceMap().values(client);
for (auto *r : pointerResources)
send_enter(r->handle, serial, surface->resource()->handle, QByteArray());
+ m_enteredSurface = surface;
return serial;
}
@@ -355,6 +389,7 @@ uint Keyboard::sendLeave(Surface *surface)
const auto pointerResources = resourceMap().values(client);
for (auto *r : pointerResources)
send_leave(r->handle, serial, surface->resource()->handle);
+ m_enteredSurface = nullptr;
return serial;
}
diff --git a/tests/auto/client/shared/coreprotocol.h b/tests/auto/client/shared/coreprotocol.h
index 5cef476c8..264c5f694 100644
--- a/tests/auto/client/shared/coreprotocol.h
+++ b/tests/auto/client/shared/coreprotocol.h
@@ -236,7 +236,7 @@ class Seat : public Global, public QtWaylandServer::wl_seat
{
Q_OBJECT
public:
- explicit Seat(CoreCompositor *compositor, uint capabilities, int version = 4);
+ explicit Seat(CoreCompositor *compositor, uint capabilities = Seat::capability_pointer | Seat::capability_keyboard, int version = 4);
~Seat() override;
void send_capabilities(Resource *resource, uint capabilities) = delete; // Use wrapper instead
void send_capabilities(uint capabilities) = delete; // Use wrapper instead
@@ -279,6 +279,10 @@ public:
void sendMotion(wl_client *client, const QPointF &position);
uint sendButton(wl_client *client, uint button, uint state);
void sendAxis(wl_client *client, axis axis, qreal value);
+ void sendAxisDiscrete(wl_client *client, axis axis, int discrete);
+ void sendAxisSource(wl_client *client, axis_source source);
+ void sendAxisStop(wl_client *client, axis axis);
+ void sendFrame(wl_client *client);
Seat *m_seat = nullptr;
QVector<uint> m_enterSerials;
@@ -313,6 +317,7 @@ public:
uint sendLeave(Surface *surface);
uint sendKey(wl_client *client, uint key, uint state);
Seat *m_seat = nullptr;
+ Surface *m_enteredSurface = nullptr;
};
class Shm : public Global, public QtWaylandServer::wl_shm
diff --git a/tests/auto/client/shared/mockcompositor.h b/tests/auto/client/shared/mockcompositor.h
index 75ef1eaea..aa85a4aea 100644
--- a/tests/auto/client/shared/mockcompositor.h
+++ b/tests/auto/client/shared/mockcompositor.h
@@ -36,10 +36,16 @@
#include <QtGui/QGuiApplication>
-#ifndef BTN_LEFT
// As defined in linux/input-event-codes.h
+#ifndef BTN_LEFT
#define BTN_LEFT 0x110
#endif
+#ifndef BTN_RIGHT
+#define BTN_RIGHT 0x111
+#endif
+#ifndef BTN_MIDDLE
+#define BTN_MIDDLE 0x112
+#endif
namespace MockCompositor {
diff --git a/tests/auto/client/surface/tst_surface.cpp b/tests/auto/client/surface/tst_surface.cpp
index dddff0866..9659235a0 100644
--- a/tests/auto/client/surface/tst_surface.cpp
+++ b/tests/auto/client/surface/tst_surface.cpp
@@ -28,7 +28,9 @@
#include "mockcompositor.h"
#include <QtGui/QRasterWindow>
+#if QT_CONFIG(opengl)
#include <QtGui/QOpenGLWindow>
+#endif
using namespace MockCompositor;
@@ -39,7 +41,9 @@ private slots:
void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
void createDestroySurface();
void waitForFrameCallbackRaster();
+#if QT_CONFIG(opengl)
void waitForFrameCallbackGl();
+#endif
void negotiateShmFormat();
};
@@ -89,6 +93,7 @@ void tst_surface::waitForFrameCallbackRaster()
}
}
+#if QT_CONFIG(opengl)
void tst_surface::waitForFrameCallbackGl()
{
QSKIP("TODO: This currently fails, needs a fix");
@@ -129,6 +134,7 @@ void tst_surface::waitForFrameCallbackGl()
bufferSpy.removeFirst();
}
}
+#endif // QT_CONFIG(opengl)
void tst_surface::negotiateShmFormat()
{
diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp
index 9b18abdc3..038ff6624 100644
--- a/tests/auto/client/xdgshell/tst_xdgshell.cpp
+++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp
@@ -29,6 +29,8 @@
#include "mockcompositor.h"
#include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow>
+#include <QtGui/qpa/qplatformnativeinterface.h>
+#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
using namespace MockCompositor;
@@ -47,6 +49,7 @@ private slots:
void pongs();
void minMaxSize();
void windowGeometry();
+ void foreignSurface();
};
void tst_xdgshell::showMinimized()
@@ -479,5 +482,32 @@ void tst_xdgshell::windowGeometry()
QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committed.windowGeometry, QRect(QPoint(0, 0), window.frameGeometry().size()));
}
+void tst_xdgshell::foreignSurface()
+{
+ auto *ni = QGuiApplication::platformNativeInterface();
+ auto *compositor = static_cast<::wl_compositor *>(ni->nativeResourceForIntegration("compositor"));
+ ::wl_surface *foreignSurface = wl_compositor_create_surface(compositor);
+
+ // There *could* be cursor surfaces lying around, we don't want to confuse those with
+ // the foreign surface we will be creating.
+ const int newSurfaceIndex = exec([&]{
+ return get<WlCompositor>()->m_surfaces.size();
+ });
+
+ QCOMPOSITOR_TRY_VERIFY(surface(newSurfaceIndex));
+ exec([&] {
+ pointer()->sendEnter(surface(newSurfaceIndex), {32, 32});
+ pointer()->sendLeave(surface(newSurfaceIndex));
+ });
+
+ // Just do something to make sure we don't destroy the surface before
+ // the pointer events above are handled.
+ QSignalSpy spy(exec([=] { return surface(newSurfaceIndex); }), &Surface::commit);
+ wl_surface_commit(foreignSurface);
+ QTRY_COMPARE(spy.count(), 1);
+
+ wl_surface_destroy(foreignSurface);
+}
+
QCOMPOSITOR_TEST_MAIN(tst_xdgshell)
#include "tst_xdgshell.moc"
diff --git a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp
index a397f60eb..027e1dfa8 100644
--- a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp
+++ b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp
@@ -413,11 +413,11 @@ void tst_WaylandClientXdgShellV6::dontSpamExposeEvents()
QSharedPointer<MockSurface> surface;
QTRY_VERIFY(surface = m_compositor->surface());
- QTRY_VERIFY(window.exposeEventCount == 0);
+ QTRY_COMPARE(window.exposeEventCount, 0);
m_compositor->sendShellSurfaceConfigure(surface);
QTRY_VERIFY(window.isExposed());
- QTRY_VERIFY(window.exposeEventCount == 1);
+ QTRY_COMPARE(window.exposeEventCount, 1);
}
int main(int argc, char **argv)
diff --git a/tests/manual/texture-sharing/cpp-client/cpp-client.pro b/tests/manual/texture-sharing/cpp-client/cpp-client.pro
new file mode 100644
index 000000000..d251791db
--- /dev/null
+++ b/tests/manual/texture-sharing/cpp-client/cpp-client.pro
@@ -0,0 +1,15 @@
+QT += waylandclient-private gui-private
+CONFIG += wayland-scanner
+
+WAYLANDCLIENTSOURCES += $$PWD/../../../../src/extensions/qt-texture-sharing-unstable-v1.xml
+
+SOURCES += main.cpp \
+ $$PWD/../../../../src/imports/texture-sharing/texturesharingextension.cpp
+
+HEADERS += \
+ $$PWD/../../../../src/imports/texture-sharing/texturesharingextension.h
+
+INCLUDEPATH += $$PWD/../../../../src/imports/texture-sharing/
+
+target.path = $$[QT_INSTALL_EXAMPLES]/wayland/texture-sharing/cpp-client
+INSTALLS += target
diff --git a/tests/manual/texture-sharing/cpp-client/main.cpp b/tests/manual/texture-sharing/cpp-client/main.cpp
new file mode 100644
index 000000000..e3f6d7025
--- /dev/null
+++ b/tests/manual/texture-sharing/cpp-client/main.cpp
@@ -0,0 +1,229 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2019 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 "texturesharingextension.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 TextureSharingExtension;
+ connect(m_extension, SIGNAL(bufferReceived(QtWaylandClient::QWaylandServerBuffer*, const QString&)), this, SLOT(receiveBuffer(QtWaylandClient::QWaylandServerBuffer*, const QString&)));
+ connect(m_extension, &TextureSharingExtension::activeChanged, this, &TestWindow::handleExtensionActive);
+ }
+
+public slots:
+ void receiveBuffer(QtWaylandClient::QWaylandServerBuffer *buffer, const QString &key)
+ {
+ if (!buffer) {
+ qWarning() << "Could not find image with key" << key;
+ return;
+ }
+ m_buffers.insert(key, buffer);
+ update();
+ }
+
+
+ void handleExtensionActive()
+ {
+ if (m_extension->isActive())
+ getImage("qt_logo");
+ }
+
+protected:
+
+ void mousePressEvent(QMouseEvent *ev) override {
+ QRect rect(10, height() - 10 - 50, 50, 50);
+ bool rectPressed = rect.contains(ev->pos());
+
+ static int c;
+
+ if (rectPressed && ev->button() == Qt::LeftButton)
+ getImage(QString("unreasonably large image %1").arg(c++));
+ else if (ev->button() == Qt::RightButton)
+ getImage("guitar.jpg");
+ else if (ev->button() == Qt::MiddleButton)
+ unloadImageAt(ev->pos());
+ }
+
+ void initializeGL() override
+ {
+ m_blitter = new QOpenGLTextureBlitter;
+ m_blitter->create();
+ }
+
+ void paintGL() override {
+ glClearColor(.5, .45, .42, 1.);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // draw a "button" to click in
+ glScissor(10,10,50,50);
+ glEnable(GL_SCISSOR_TEST);
+ glClearColor(0.4, 0.7, 0.9, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDisable(GL_SCISSOR_TEST);
+
+ int x = 0;
+ qDebug() << "*** paintGL ***";
+ showBuffers();
+ for (auto buffer: qAsConst(m_buffers)) {
+ m_blitter->bind();
+ QSize s(buffer->size());
+ qDebug() << "painting" << buffer << s;
+ if (s.width() > 1024) {
+ qDebug() << "showing large buffer at reduced size";
+ s = QSize(128,128);
+ }
+ QRectF targetRect(QPointF(x,0), s);
+ QOpenGLTexture *texture = buffer->toOpenGlTexture();
+ if (!texture) {
+ qWarning("Null texture");
+ continue;
+ }
+ 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:
+ void getImage(const QString &key) {
+ if (!m_buffers.contains(key))
+ m_extension->requestImage(key);
+ }
+
+ void showBuffers() const
+ {
+ auto end = m_buffers.cend();
+ for (auto it = m_buffers.cbegin(); it != end; ++it) {
+ qDebug() << " " << it.key() << it.value();
+ }
+ }
+
+ void unloadImageAt(const QPoint &pos) {
+ int x = 0;
+ QtWaylandClient::QWaylandServerBuffer *foundBuffer = nullptr;
+ QString name;
+ auto end = m_buffers.cend();
+ for (auto it = m_buffers.cbegin(); it != end; ++it) {
+ auto *buffer = it.value();
+ QSize s(buffer->size());
+ if (s.width() > 1024)
+ s = QSize(128,128);
+ QRectF targetRect(QPointF(x,0), s);
+ //qDebug() << " " << it.key() << it.value() << targetRect << pos;
+
+ if (targetRect.contains(pos)) {
+ foundBuffer = buffer;
+ name = it.key();
+ //qDebug() << "FOUND!!";
+ break;
+ }
+
+ x += s.width() + 10;
+ }
+ if (foundBuffer) {
+ qDebug() << "unloading image" << name << "found at" << pos;
+ unloadImage(name);
+ } else {
+ qDebug() << "no image at" << pos;
+ }
+ }
+
+ void unloadImage(const QString &key) {
+ auto *buf = m_buffers.take(key);
+ if (buf) {
+ qDebug() << "unloadImage deleting" << buf;
+ delete buf;
+ m_extension->abandonImage(key);
+ }
+ update();
+ }
+
+ QOpenGLTextureBlitter *m_blitter = nullptr;
+ TextureSharingExtension *m_extension = nullptr;
+ QMap<QString, QtWaylandClient::QWaylandServerBuffer*> m_buffers;
+
+};
+
+int main (int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ TestWindow window;
+ window.show();
+
+ return app.exec();
+}
+
+#include "main.moc"