diff options
165 files changed, 6316 insertions, 763 deletions
diff --git a/.qmake.conf b/.qmake.conf index 1ed62c5a..2c01c5d2 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,7 @@ load(qt_build_config) -MODULE_VERSION = 5.13.1 +DEFINES += QT_NO_FOREACH +DEFINES += QT_NO_JAVA_STYLE_ITERATORS +DEFINES += QT_NO_LINKED_LIST + +MODULE_VERSION = 5.14.0 diff --git a/examples/wayland/minimal-cpp/compositor.cpp b/examples/wayland/minimal-cpp/compositor.cpp index fa9ae2da..a4b98997 100644 --- a/examples/wayland/minimal-cpp/compositor.cpp +++ b/examples/wayland/minimal-cpp/compositor.cpp @@ -163,9 +163,13 @@ void Compositor::handleMouseMove(const QPoint &position) defaultSeat()->sendMouseMoveEvent(view, mapToView(view, position)); } -void Compositor::handleMouseWheel(Qt::Orientation orientation, int delta) +void Compositor::handleMouseWheel(const QPoint &angleDelta) { - defaultSeat()->sendMouseWheelEvent(orientation, delta); + // TODO: fix this to send a single event, when diagonal scrolling is supported + if (angleDelta.x() != 0) + defaultSeat()->sendMouseWheelEvent(Qt::Horizontal, angleDelta.x()); + if (angleDelta.y() != 0) + defaultSeat()->sendMouseWheelEvent(Qt::Vertical, angleDelta.y()); } void Compositor::handleKeyPress(quint32 nativeScanCode) diff --git a/examples/wayland/minimal-cpp/compositor.h b/examples/wayland/minimal-cpp/compositor.h index 3c0c80e0..e32442dd 100644 --- a/examples/wayland/minimal-cpp/compositor.h +++ b/examples/wayland/minimal-cpp/compositor.h @@ -102,7 +102,7 @@ public: void handleMousePress(const QPoint &position, Qt::MouseButton button); void handleMouseRelease(const QPoint &position, Qt::MouseButton button, Qt::MouseButtons buttons); void handleMouseMove(const QPoint &position); - void handleMouseWheel(Qt::Orientation orientation, int delta); + void handleMouseWheel(const QPoint &angleDelta); void handleKeyPress(quint32 nativeScanCode); void handleKeyRelease(quint32 nativeScanCode); diff --git a/examples/wayland/minimal-cpp/window.cpp b/examples/wayland/minimal-cpp/window.cpp index f32fb515..9f22cc68 100644 --- a/examples/wayland/minimal-cpp/window.cpp +++ b/examples/wayland/minimal-cpp/window.cpp @@ -129,7 +129,7 @@ void Window::mouseMoveEvent(QMouseEvent *event) void Window::wheelEvent(QWheelEvent *event) { - m_compositor->handleMouseWheel(event->orientation(), event->delta()); + m_compositor->handleMouseWheel(event->angleDelta()); } void Window::keyPressEvent(QKeyEvent *e) diff --git a/examples/wayland/multi-output/qml/GridScreen.qml b/examples/wayland/multi-output/qml/GridScreen.qml index 340291af..a59cb8fb 100644 --- a/examples/wayland/multi-output/qml/GridScreen.qml +++ b/examples/wayland/multi-output/qml/GridScreen.qml @@ -50,7 +50,7 @@ import QtQuick 2.0 import QtQuick.Window 2.2 -import QtWayland.Compositor 1.0 +import QtWayland.Compositor 1.14 WaylandOutput { id: output @@ -93,4 +93,12 @@ WaylandOutput { } } } + + XdgOutputV1 { + name: "WL-2" + description: "Overview screen" + logicalPosition: output.position + logicalSize: Qt.size(output.geometry.width / output.scaleFactor, + output.geometry.height / output.scaleFactor) + } } diff --git a/examples/wayland/multi-output/qml/ShellScreen.qml b/examples/wayland/multi-output/qml/ShellScreen.qml index 4482e6e8..aa9b4b29 100644 --- a/examples/wayland/multi-output/qml/ShellScreen.qml +++ b/examples/wayland/multi-output/qml/ShellScreen.qml @@ -50,7 +50,7 @@ import QtQuick 2.0 import QtQuick.Window 2.2 -import QtWayland.Compositor 1.0 +import QtWayland.Compositor 1.14 WaylandOutput { id: output @@ -84,4 +84,12 @@ WaylandOutput { } } } + + XdgOutputV1 { + name: "WL-1" + description: "Screen with window management" + logicalPosition: output.position + logicalSize: Qt.size(output.geometry.width / output.scaleFactor, + output.geometry.height / output.scaleFactor) + } } diff --git a/examples/wayland/multi-output/qml/main.qml b/examples/wayland/multi-output/qml/main.qml index d01def55..45f91fd6 100644 --- a/examples/wayland/multi-output/qml/main.qml +++ b/examples/wayland/multi-output/qml/main.qml @@ -49,7 +49,7 @@ ****************************************************************************/ import QtQuick 2.0 -import QtWayland.Compositor 1.0 +import QtWayland.Compositor 1.14 WaylandCompositor { id: comp @@ -91,6 +91,8 @@ WaylandCompositor { } } + XdgOutputManagerV1 {} + WlShell { id: defaultShell diff --git a/examples/wayland/qwindow-compositor/compositor.cpp b/examples/wayland/qwindow-compositor/compositor.cpp index 199de22e..220ea3d7 100644 --- a/examples/wayland/qwindow-compositor/compositor.cpp +++ b/examples/wayland/qwindow-compositor/compositor.cpp @@ -266,7 +266,7 @@ void Compositor::viewAnimationDone() View * Compositor::findView(const QWaylandSurface *s) const { - Q_FOREACH (View* view, m_views) { + for (View* view : m_views) { if (view->surface() == s) return view; } @@ -545,6 +545,6 @@ void Compositor::raise(View *view) int source = endPos + 1 + i; int dest = startPos + i; for (int j = source; j > dest; j--) - m_views.swap(j, j-1); + m_views.swapItemsAt(j, j-1); } } diff --git a/examples/wayland/qwindow-compositor/window.cpp b/examples/wayland/qwindow-compositor/window.cpp index 9a8ffc2d..c439d20a 100644 --- a/examples/wayland/qwindow-compositor/window.cpp +++ b/examples/wayland/qwindow-compositor/window.cpp @@ -125,7 +125,8 @@ void Window::paintGL() functions->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GLenum currentTarget = GL_TEXTURE_2D; - Q_FOREACH (View *view, m_compositor->views()) { + const auto views = m_compositor->views(); + for (View *view : views) { if (view->isCursor()) continue; auto texture = view->getTexture(); @@ -160,7 +161,8 @@ void Window::paintGL() View *Window::viewAt(const QPointF &point) { View *ret = nullptr; - Q_FOREACH (View *view, m_compositor->views()) { + const auto views = m_compositor->views(); + for (View *view : views) { if (view == m_dragIconView) continue; QRectF geom(view->position(), view->size()); diff --git a/examples/wayland/server-buffer/README b/examples/wayland/server-buffer/README index 5744a6ba..da20b0f5 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 00000000..c684448d --- /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 00000000..27ea7674 --- /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 00000000..86a8567f --- /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 00000000..e80e9e15 --- /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 Binary files differnew file mode 100644 index 00000000..845830c5 --- /dev/null +++ b/examples/wayland/texture-sharing/custom-compositor/images/background.png diff --git a/examples/wayland/texture-sharing/custom-compositor/images/car.ktx b/examples/wayland/texture-sharing/custom-compositor/images/car.ktx Binary files differnew file mode 100644 index 00000000..2aefdd30 --- /dev/null +++ b/examples/wayland/texture-sharing/custom-compositor/images/car.ktx diff --git a/examples/wayland/texture-sharing/custom-compositor/images/qt4.astc b/examples/wayland/texture-sharing/custom-compositor/images/qt4.astc Binary files differnew file mode 100644 index 00000000..7f7a3f47 --- /dev/null +++ b/examples/wayland/texture-sharing/custom-compositor/images/qt4.astc diff --git a/examples/wayland/texture-sharing/custom-compositor/images/qt_logo.png b/examples/wayland/texture-sharing/custom-compositor/images/qt_logo.png Binary files differnew file mode 100644 index 00000000..5e2b355e --- /dev/null +++ b/examples/wayland/texture-sharing/custom-compositor/images/qt_logo.png 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 00000000..a39c8c38 --- /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 00000000..16a412fc --- /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 00000000..3f714dc5 --- /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 00000000..618d6701 --- /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 00000000..371a9759 --- /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 00000000..67d5c707 --- /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 00000000..5f6483ac --- /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 00000000..3f779282 --- /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 5551b570..f9287481 100644 --- a/examples/wayland/wayland.pro +++ b/examples/wayland/wayland.pro @@ -3,12 +3,11 @@ TEMPLATE=subdirs !qtHaveModule(waylandcompositor): \ return() -!qtConfig(opengl): \ - return() - -SUBDIRS += \ - qwindow-compositor \ - minimal-cpp +qtConfig(opengl) { + SUBDIRS += \ + qwindow-compositor \ + minimal-cpp +} qtHaveModule(quick) { SUBDIRS += minimal-qml @@ -21,8 +20,13 @@ qtHaveModule(quick) { SUBDIRS += server-side-decoration qtHaveModule(waylandclient) { SUBDIRS += \ - custom-extension \ - server-buffer + custom-extension + + qtConfig(opengl) { + SUBDIRS += \ + server-buffer \ + texture-sharing + } } SUBDIRS += hwlayer-compositor } diff --git a/src/3rdparty/protocol/idle-inhibit-unstable-v1.xml b/src/3rdparty/protocol/idle-inhibit-unstable-v1.xml new file mode 100644 index 00000000..9c06cdcb --- /dev/null +++ b/src/3rdparty/protocol/idle-inhibit-unstable-v1.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="idle_inhibit_unstable_v1"> + + <copyright> + Copyright © 2015 Samsung Electronics Co., Ltd + + 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> + + <interface name="zwp_idle_inhibit_manager_v1" version="1"> + <description summary="control behavior when display idles"> + This interface permits inhibiting the idle behavior such as screen + blanking, locking, and screensaving. The client binds the idle manager + globally, then creates idle-inhibitor objects for each surface. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + </description> + + <request name="destroy" type="destructor"> + <description summary="destroy the idle inhibitor object"> + Destroy the inhibit manager. + </description> + </request> + + <request name="create_inhibitor"> + <description summary="create a new inhibitor object"> + Create a new inhibitor object associated with the given surface. + </description> + <arg name="id" type="new_id" interface="zwp_idle_inhibitor_v1"/> + <arg name="surface" type="object" interface="wl_surface" + summary="the surface that inhibits the idle behavior"/> + </request> + + </interface> + + <interface name="zwp_idle_inhibitor_v1" version="1"> + <description summary="context object for inhibiting idle behavior"> + An idle inhibitor prevents the output that the associated surface is + visible on from being set to a state where it is not visually usable due + to lack of user interaction (e.g. blanked, dimmed, locked, set to power + save, etc.) Any screensaver processes are also blocked from displaying. + + If the surface is destroyed, unmapped, becomes occluded, loses + visibility, or otherwise becomes not visually relevant for the user, the + idle inhibitor will not be honored by the compositor; if the surface + subsequently regains visibility the inhibitor takes effect once again. + Likewise, the inhibitor isn't honored if the system was already idled at + the time the inhibitor was established, although if the system later + de-idles and re-idles the inhibitor will take effect. + </description> + + <request name="destroy" type="destructor"> + <description summary="destroy the idle inhibitor object"> + Remove the inhibitor effect from the associated wl_surface. + </description> + </request> + + </interface> +</protocol> diff --git a/src/3rdparty/protocol/qt_attribution.json b/src/3rdparty/protocol/qt_attribution.json index 7e068f75..c49ead4f 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", @@ -110,8 +127,9 @@ Copyright (c) 2013 BMW Car IT GmbH" "Id": "wayland-xdg-output-protocol", "Name": "Wayland XDG Output Protocol", "QDocModule": "qtwaylandcompositor", - "QtUsage": "Used in the Qt Wayland platform plugin.", + "QtUsage": "Used in the Qt Wayland Compositor API, and the Qt Wayland platform plugin.", "Files": "xdg-output-unstable-v1.xml", + "Description": "The XDG Output protocol is an extended way to describe output regions under Wayland", "Homepage": "https://wayland.freedesktop.org", "Version": "unstable v1, version 2", 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 00000000..e5a39e34 --- /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 4233ac95..d0ae9009 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 91024c9d..e9e16324 100644 --- a/src/client/configure.json +++ b/src/client/configure.json @@ -93,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", @@ -158,14 +163,9 @@ "condition": "features.wayland-client && features.opengl && features.egl && tests.dmabuf-server-buffer", "output": [ "privateFeature" ] }, - "wayland-client-texture-sharing-experimental" : { - "label": "Texture sharing (experimental)", - "autoDetect": "false", - "output": [ "privateFeature" ] - }, "wayland-vulkan-server-buffer": { "label": "Vulkan-based server buffer integration", - "condition": "features.wayland-client && features.opengl && features.egl && tests.vulkan-server-buffer && features.wayland-client-texture-sharing-experimental", + "condition": "features.wayland-client && features.opengl && features.egl && tests.vulkan-server-buffer", "output": [ "privateFeature" ] }, "wayland-shm-emulation-server-buffer": { diff --git a/src/client/qwaylandclipboard.cpp b/src/client/qwaylandclipboard.cpp index 60820da9..369c6ec0 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 8b2ed036..4356b23a 100644 --- a/src/client/qwaylandcursor.cpp +++ b/src/client/qwaylandcursor.cpp @@ -48,6 +48,8 @@ #include <wayland-cursor.h> +#include <algorithm> + QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -75,7 +77,10 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape) if (struct wl_cursor *cursor = m_cursors.value(shape, nullptr)) return cursor; - static const QMultiMap<WaylandCursor, QByteArray>cursorNamesMap { + static Q_CONSTEXPR struct ShapeAndName { + WaylandCursor shape; + const char name[33]; + } cursorNamesMap[] = { {ArrowCursor, "left_ptr"}, {ArrowCursor, "default"}, {ArrowCursor, "top_left_arrow"}, @@ -193,9 +198,14 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape) {ResizeSouthWestCursor, "bottom_left_corner"}, }; - QList<QByteArray> cursorNames = cursorNamesMap.values(shape); - for (auto &name : qAsConst(cursorNames)) { - if (wl_cursor *cursor = wl_cursor_theme_get_cursor(m_theme, name.constData())) { + const auto byShape = [](ShapeAndName lhs, ShapeAndName rhs) { + return lhs.shape < rhs.shape; + }; + Q_ASSERT(std::is_sorted(std::begin(cursorNamesMap), std::end(cursorNamesMap), byShape)); + const auto p = std::equal_range(std::begin(cursorNamesMap), std::end(cursorNamesMap), + ShapeAndName{shape, ""}, byShape); + for (auto it = p.first; it != p.second; ++it) { + if (wl_cursor *cursor = wl_cursor_theme_get_cursor(m_theme, it->name)) { m_cursors.insert(shape, cursor); return cursor; } @@ -209,7 +219,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 +237,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 6c48fb62..a4605f3d 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 990f92ba..fc3c7077 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> @@ -104,9 +105,10 @@ QWaylandDataOffer *QWaylandDataDevice::dragOffer() const bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) { - 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(); if (!origin) { qCDebug(lcQpaWayland) << "Couldn't start a drag because the origin window could not be found."; @@ -116,7 +118,7 @@ bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData)); connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled); - 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()); return true; } diff --git a/src/client/qwaylanddataoffer.cpp b/src/client/qwaylanddataoffer.cpp index 3da16ed0..4c06277f 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 5412400a..9cf1483c 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/qwaylanddatasource.cpp b/src/client/qwaylanddatasource.cpp index 0c6ad50e..ea76943a 100644 --- a/src/client/qwaylanddatasource.cpp +++ b/src/client/qwaylanddatasource.cpp @@ -60,7 +60,8 @@ QWaylandDataSource::QWaylandDataSource(QWaylandDataDeviceManager *dataDeviceMana { if (!mimeData) return; - Q_FOREACH (const QString &format, mimeData->formats()) { + const auto formats = mimeData->formats(); + for (const QString &format : formats) { offer(format); } } diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp index 78524f6f..a17e8917 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 @@ -162,13 +167,11 @@ QWaylandDisplay::~QWaylandDisplay(void) if (mSyncCallback) wl_callback_destroy(mSyncCallback); - qDeleteAll(mInputDevices); - mInputDevices.clear(); + qDeleteAll(qExchange(mInputDevices, {})); - foreach (QWaylandScreen *screen, mScreens) { + for (QWaylandScreen *screen : qExchange(mScreens, {})) { QWindowSystemInterface::handleScreenRemoved(screen); } - mScreens.clear(); qDeleteAll(mWaitingScreens); #if QT_CONFIG(wayland_datadevice) @@ -307,6 +310,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)) @@ -329,7 +336,8 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin mGlobals.append(RegistryGlobal(id, interface, version, registry)); - foreach (Listener l, mRegistryListeners) + const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification + for (Listener l : copy) (*l.listener)(l.data, registry, id, interface, version); } @@ -347,7 +355,7 @@ void QWaylandDisplay::registry_global_remove(uint32_t id) } } - foreach (QWaylandScreen *screen, mScreens) { + for (QWaylandScreen *screen : qAsConst(mScreens)) { if (screen->outputId() == id) { mScreens.removeOne(screen); QWindowSystemInterface::handleScreenRemoved(screen); @@ -367,9 +375,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 7cfbc19b..14bb7719 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. */ @@ -236,6 +243,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 c5db6d7b..a7836e29 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 0f27f551..e9afe05e 100644 --- a/src/client/qwaylandinputcontext.cpp +++ b/src/client/qwaylandinputcontext.cpp @@ -120,7 +120,7 @@ void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t fla return; auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle()); - auto *surface = window->object(); + auto *surface = window->wlSurface(); if (!surface || (surface != m_surface)) return; @@ -224,11 +224,11 @@ void QWaylandTextInput::zwp_text_input_v2_leave(uint32_t serial, ::wl_surface *s void QWaylandTextInput::zwp_text_input_v2_modifiers_map(wl_array *map) { - QList<QByteArray> modifiersMap = QByteArray::fromRawData(static_cast<const char*>(map->data), map->size).split('\0'); + const QList<QByteArray> modifiersMap = QByteArray::fromRawData(static_cast<const char*>(map->data), map->size).split('\0'); m_modifiersMap.clear(); - Q_FOREACH (const QByteArray &modifier, modifiersMap) { + for (const QByteArray &modifier : modifiersMap) { if (modifier == "Shift") m_modifiersMap.append(Qt::ShiftModifier); else if (modifier == "Control") @@ -431,7 +431,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) @@ -537,7 +537,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(); @@ -546,7 +546,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 8f3df8e4..e39ccf25 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 @@ -318,12 +325,14 @@ void QWaylandInputDevice::Pointer::updateCursor() return; // 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; } @@ -353,10 +362,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()) { @@ -364,6 +373,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()))); @@ -427,6 +442,21 @@ QWaylandInputDevice::Touch *QWaylandInputDevice::createTouch(QWaylandInputDevice return new Touch(device); } +QWaylandInputDevice::Keyboard *QWaylandInputDevice::keyboard() const +{ + return mKeyboard; +} + +QWaylandInputDevice::Pointer *QWaylandInputDevice::pointer() const +{ + return mPointer; +} + +QWaylandInputDevice::Touch *QWaylandInputDevice::touch() const +{ + return mTouch; +} + void QWaylandInputDevice::handleEndDrag() { if (mTouch) @@ -447,6 +477,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); @@ -465,7 +507,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 @@ -524,6 +566,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); @@ -550,8 +593,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) {} }; @@ -562,6 +606,7 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf return; QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); + if (!window) return; // Ignore foreign surfaces @@ -571,9 +616,8 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf << "attempting to work around it by invalidating the current focus"; invalidateFocus(); } - - mFocus = window; - connect(mFocus.data(), &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed); + mFocus = window->waylandSurface(); + connect(mFocus.data(), &QObject::destroyed, this, &Pointer::handleFocusDestroyed); mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy)); mGlobalPos = window->window()->mapToGlobal(mSurfacePos.toPoint()); @@ -587,12 +631,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 @@ -604,10 +655,8 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac if (!window) return; // Ignore foreign surfaces - if (!QWaylandWindow::mouseGrab()) { - QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); - window->handleMouseLeave(mParent); - } + if (!QWaylandWindow::mouseGrab()) + setFrameEvent(new LeaveEvent(window, mSurfacePos, mGlobalPos)); invalidateFocus(); mButtons = Qt::NoButton; @@ -618,15 +667,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. @@ -648,18 +699,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. @@ -701,66 +771,300 @@ 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.data(), &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed); - mFocus = nullptr; + if (mFocus) { + disconnect(mFocus.data(), &QObject::destroyed, this, &Pointer::handleFocusDestroyed); + mFocus = nullptr; + } mEnterSerial = 0; } 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? } - WheelEvent e(time, mSurfacePos, mGlobalPos, pixelDelta, angleDelta, mParent->modifiers()); - window->handleMouse(mParent, e); + // 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(); +} + +void QWaylandInputDevice::Pointer::flushFrameEvent() +{ + if (auto *event = mFrameData.event) { + if (auto window = event->surface) { + window->handleMouse(mParent, *event); + } else if (mFrameData.event->type == QWaylandPointerEvent::Type::Release) { + // If the window has been destroyed, we still need to report an up event, but it can't + // be handled by the destroyed window (obviously), so send the event here instead. + QWindowSystemInterface::handleMouseEvent(nullptr, event->timestamp, event->local, + event->global, event->buttons, event->modifiers); + } + 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) @@ -927,7 +1231,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(); } @@ -938,6 +1242,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(); } @@ -1046,7 +1354,7 @@ void QWaylandInputDevice::handleTouchPoint(int id, Qt::TouchPointState state, co //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 143e1122..60d6f2c1 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; @@ -136,6 +148,10 @@ public: virtual Pointer *createPointer(QWaylandInputDevice *device); virtual Touch *createTouch(QWaylandInputDevice *device); + Keyboard *keyboard() const; + Pointer *pointer() const; + Touch *touch() const; + private: QWaylandDisplay *mQDisplay = nullptr; struct wl_display *mDisplay = nullptr; @@ -150,6 +166,7 @@ private: Qt::CursorShape shape = Qt::ArrowCursor; int fallbackOutputScale = 1; QPoint hotspot; + QElapsedTimer animationTimer; } mCursor; #endif @@ -157,6 +174,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; @@ -231,6 +252,8 @@ public: Qt::KeyboardModifiers modifiers() const; + struct ::wl_keyboard *wl_keyboard() { return QtWayland::wl_keyboard::object(); } + private slots: void handleFocusDestroyed(); void handleFocusLost(); @@ -256,6 +279,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 @@ -266,6 +290,8 @@ public: #endif QWaylandInputDevice *seat() const { return mParent; } + struct ::wl_pointer *wl_pointer() { return QtWayland::wl_pointer::object(); } + protected: void pointer_enter(uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) override; @@ -277,6 +303,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 +318,7 @@ public: void releaseButtons(); QWaylandInputDevice *mParent = nullptr; - QPointer<QWaylandWindow> mFocus; + QPointer<QWaylandSurface> mFocus; uint32_t mEnterSerial = 0; #if QT_CONFIG(cursor) struct { @@ -304,6 +334,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 @@ -331,6 +385,8 @@ public: bool allTouchPointsReleased(); void releasePoints(); + struct ::wl_touch *wl_touch() { return QtWayland::wl_touch::object(); } + QWaylandInputDevice *mParent = nullptr; QPointer<QWaylandWindow> mFocus; QList<QWindowSystemInterface::TouchPoint> mPendingTouchPoints; @@ -338,38 +394,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; + QPointer<QWaylandWindow> surface; }; } diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp index 85fcef43..f6a80e18 100644 --- a/src/client/qwaylandintegration.cpp +++ b/src/client/qwaylandintegration.cpp @@ -272,7 +272,7 @@ QPlatformAccessibility *QWaylandIntegration::accessibility() const { if (!mAccessibility) { #ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE - Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QXcbIntegration", + Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QWaylandIntegration", "Initializing accessibility without event-dispatcher!"); mAccessibility.reset(new QSpiAccessibleBridge()); #else @@ -352,7 +352,7 @@ void QWaylandIntegration::initializeClientBufferIntegration() && mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("linux-dmabuf-unstable-v1")) { targetKey = mDisplay->hardwareIntegration()->clientBufferIntegration(); } else { - targetKey = QLatin1Literal("wayland-egl"); + targetKey = QLatin1String("wayland-egl"); } } @@ -430,7 +430,7 @@ void QWaylandIntegration::initializeShellIntegration() preferredShells << QLatin1String("wl-shell") << QLatin1String("ivi-shell"); } - Q_FOREACH (QString preferredShell, preferredShells) { + for (const QString &preferredShell : qAsConst(preferredShells)) { mShellIntegration.reset(createShellIntegration(preferredShell)); if (mShellIntegration) { qCDebug(lcQpaWayland, "Using the '%s' shell integration", qPrintable(preferredShell)); diff --git a/src/client/qwaylandnativeinterface.cpp b/src/client/qwaylandnativeinterface.cpp index 76acb526..b4ecc009 100644 --- a/src/client/qwaylandnativeinterface.cpp +++ b/src/client/qwaylandnativeinterface.cpp @@ -47,6 +47,7 @@ #include "qwaylanddisplay_p.h" #include "qwaylandwindowmanagerintegration_p.h" #include "qwaylandscreen_p.h" +#include "qwaylandinputdevice_p.h" #include <QtGui/private/qguiapplication_p.h> #include <QtGui/QScreen> #include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h> @@ -76,6 +77,27 @@ void *QWaylandNativeInterface::nativeResourceForIntegration(const QByteArray &re if (lowerCaseResource == "egldisplay" && m_integration->clientBufferIntegration()) return m_integration->clientBufferIntegration()->nativeResource(QWaylandClientBufferIntegration::EglDisplay); + if (lowerCaseResource == "wl_seat") + return m_integration->display()->defaultInputDevice()->wl_seat(); + if (lowerCaseResource == "wl_keyboard") { + auto *keyboard = m_integration->display()->defaultInputDevice()->keyboard(); + if (keyboard) + return keyboard->wl_keyboard(); + return nullptr; + } + if (lowerCaseResource == "wl_pointer") { + auto *pointer = m_integration->display()->defaultInputDevice()->pointer(); + if (pointer) + return pointer->wl_pointer(); + return nullptr; + } + if (lowerCaseResource == "wl_touch") { + auto *touch = m_integration->display()->defaultInputDevice()->touch(); + if (touch) + return touch->wl_touch(); + return nullptr; + } + return nullptr; } @@ -89,7 +111,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 00000000..3ddf6dac --- /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 00000000..b165c51b --- /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/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp index d116a807..e7079683 100644 --- a/src/client/qwaylandscreen.cpp +++ b/src/client/qwaylandscreen.cpp @@ -175,7 +175,8 @@ QList<QPlatformScreen *> QWaylandScreen::virtualSiblings() const void QWaylandScreen::setOrientationUpdateMask(Qt::ScreenOrientations mask) { - foreach (QWindow *window, QGuiApplication::allWindows()) { + const auto allWindows = QGuiApplication::allWindows(); + for (QWindow *window : allWindows) { QWaylandWindow *w = static_cast<QWaylandWindow *>(window->handle()); if (w && w->waylandScreen() == this) w->setOrientationMask(mask); diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp index 34044ec9..9b5971a2 100644 --- a/src/client/qwaylandshmbackingstore.cpp +++ b/src/client/qwaylandshmbackingstore.cpp @@ -243,12 +243,13 @@ void QWaylandShmBackingStore::resize(const QSize &size, const QRegion &) QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size) { - foreach (QWaylandShmBuffer *b, mBuffers) { + const auto copy = mBuffers; // remove when ported to vector<unique_ptr> + remove_if + for (QWaylandShmBuffer *b : copy) { if (!b->busy()) { if (b->size() == size) { return b; } else { - mBuffers.removeOne(b); + mBuffers.remove(b); if (mBackBuffer == b) mBackBuffer = nullptr; delete b; @@ -256,11 +257,11 @@ QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size) } } - static const int MAX_BUFFERS = 5; - if (mBuffers.count() < MAX_BUFFERS) { + static const size_t MAX_BUFFERS = 5; + if (mBuffers.size() < MAX_BUFFERS) { QImage::Format format = QPlatformScreen::platformScreenForWindow(window())->format(); QWaylandShmBuffer *b = new QWaylandShmBuffer(mDisplay, size, format, waylandWindow()->scale()); - mBuffers.prepend(b); + mBuffers.push_front(b); return b; } return nullptr; @@ -288,20 +289,23 @@ 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) { - mBuffers.removeOne(buffer); - mBuffers.prepend(buffer); + if (mBuffers.front() != buffer) { + mBuffers.remove(buffer); + mBuffers.push_front(buffer); } - if (windowDecoration() && window()->isVisible()) + if (windowDecoration() && window()->isVisible() && oldSizeInBytes != newSizeInBytes) windowDecoration()->update(); } diff --git a/src/client/qwaylandshmbackingstore_p.h b/src/client/qwaylandshmbackingstore_p.h index 88ecfc5e..8a85cd7f 100644 --- a/src/client/qwaylandshmbackingstore_p.h +++ b/src/client/qwaylandshmbackingstore_p.h @@ -57,7 +57,8 @@ #include <QtGui/QImage> #include <qpa/qplatformwindow.h> #include <QMutex> -#include <QLinkedList> + +#include <list> QT_BEGIN_NAMESPACE @@ -116,7 +117,7 @@ private: QWaylandShmBuffer *getBuffer(const QSize &size); QWaylandDisplay *mDisplay = nullptr; - QLinkedList<QWaylandShmBuffer *> mBuffers; + std::list<QWaylandShmBuffer *> mBuffers; QWaylandShmBuffer *mFrontBuffer = nullptr; QWaylandShmBuffer *mBackBuffer = nullptr; bool mPainting = false; diff --git a/src/client/qwaylandsurface.cpp b/src/client/qwaylandsurface.cpp new file mode 100644 index 00000000..c35f01b5 --- /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 00000000..54101093 --- /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 48c869a6..0394aef3 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 ccfcaf93..ccf4f8da 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,11 +90,12 @@ QWaylandWindow::~QWaylandWindow() delete mWindowDecoration; - if (isInitialized()) + if (mSurface) reset(false); const QWindow *parent = window(); - foreach (QWindow *w, QGuiApplication::topLevelWindows()) { + const auto tlw = QGuiApplication::topLevelWindows(); + for (QWindow *w : tlw) { if (w->transientParent() == parent) QWindowSystemInterface::handleCloseEvent(w); } @@ -115,7 +116,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 +182,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,10 +200,13 @@ void QWaylandWindow::initWindow() void QWaylandWindow::initializeWlSurface() { - Q_ASSERT(!isInitialized()); + Q_ASSERT(!mSurface); { QWriteLocker lock(&mSurfaceLock); - init(mDisplay->createSurface(static_cast<QtWayland::wl_surface *>(this))); + mSurface.reset(new QWaylandSurface(mDisplay)); + connect(mSurface.data(), &QWaylandSurface::screensChanged, + this, &QWaylandWindow::handleScreensChanged); + mSurface->m_window = this; } emit wlSurfaceCreated(); } @@ -231,7 +235,7 @@ bool QWaylandWindow::shouldCreateSubSurface() const void QWaylandWindow::reset(bool sendDestroyEvent) { - if (isInitialized() && sendDestroyEvent) { + if (mSurface && sendDestroyEvent) { QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed); QGuiApplication::sendEvent(window(), &e); } @@ -239,12 +243,11 @@ void QWaylandWindow::reset(bool sendDestroyEvent) mShellSurface = nullptr; delete mSubSurfaceWindow; mSubSurfaceWindow = nullptr; - if (isInitialized()) { + if (mSurface) { emit wlSurfaceDestroyed(); QWriteLocker lock(&mSurfaceLock); - destroy(); + mSurface.reset(); } - mScreens.clear(); if (mFrameCallback) { wl_callback_destroy(mFrameCallback); @@ -264,7 +267,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 @@ -396,7 +401,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) @@ -440,18 +450,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() @@ -505,58 +515,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()); @@ -564,9 +522,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); } } @@ -578,7 +536,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) @@ -608,20 +566,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 = { @@ -712,6 +670,11 @@ QRect QWaylandWindow::windowContentGeometry() const return QRect(QPoint(), surfaceSize()); } +wl_surface *QWaylandWindow::wlSurface() +{ + return mSurface ? mSurface->object() : nullptr; +} + QWaylandShellSurface *QWaylandWindow::shellSurface() const { return mShellSurface; @@ -753,9 +716,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) @@ -844,7 +807,7 @@ bool QWaylandWindow::createDecoration() } if (hadDecoration != (bool)mWindowDecoration) { - foreach (QWaylandSubSurface *subsurf, mChildren) { + for (QWaylandSubSurface *subsurf : qAsConst(mChildren)) { QPoint pos = subsurf->window()->geometry().topLeft(); QMargins m = frameMargins(); subsurf->set_position(pos.x() + m.left(), pos.y() + m.top()); @@ -887,6 +850,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); @@ -895,11 +870,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; } } @@ -913,20 +892,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) @@ -968,12 +933,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; @@ -986,16 +957,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(); } } @@ -1165,7 +1141,7 @@ void QWaylandWindow::handleUpdate() { // TODO: Should sync subsurfaces avoid requesting frame callbacks? QReadLocker lock(&mSurfaceLock); - if (!isInitialized()) + if (!mSurface) return; if (mFrameCallback) { @@ -1182,7 +1158,7 @@ void QWaylandWindow::handleUpdate() QMetaObject::invokeMethod(this, [this, id] { 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 b03d92e5..52e57c72 100644 --- a/src/client/qwaylandwindow_p.h +++ b/src/client/qwaylandwindow_p.h @@ -81,8 +81,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: @@ -110,12 +111,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); @@ -130,6 +129,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; } @@ -159,7 +160,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); @@ -209,11 +209,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; @@ -244,6 +241,7 @@ protected: bool mSentInitialResize = false; QPoint mOffset; int mScale = 1; + QWaylandScreen *mLastReportedScreen = nullptr; QIcon mWindowIcon; @@ -255,9 +253,6 @@ protected: QWaylandBuffer *mQueuedBuffer = nullptr; QRegion mQueuedBufferDamage; -private slots: - void handleScreenRemoved(QScreen *qScreen); - private: void setGeometry_helper(const QRect &rect); void initWindow(); @@ -270,7 +265,7 @@ private: QWaylandScreen *calculateScreenFromSurfaceEvents() const; void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e); - void handleScreenChanged(); + void handleScreensChanged(); bool mInResizeFromApplyConfigure = false; QRect mLastExposeGeometry; diff --git a/src/compositor/compositor_api/compositor_api.pri b/src/compositor/compositor_api/compositor_api.pri index c8f1c98b..bbc3a03e 100644 --- a/src/compositor/compositor_api/compositor_api.pri +++ b/src/compositor/compositor_api/compositor_api.pri @@ -69,16 +69,21 @@ qtConfig(wayland-compositor-quick) { 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 + + qtConfig(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 530cf8ed..e2d617c3 100644 --- a/src/compositor/compositor_api/qwaylandcompositor.cpp +++ b/src/compositor/compositor_api/qwaylandcompositor.cpp @@ -200,8 +200,8 @@ void QWaylandCompositorPrivate::init() buffer_manager = new QtWayland::BufferManager(q); wl_display_init_shm(display); - QVector<wl_shm_format> formats = QWaylandSharedMemoryFormatHelper::supportedWaylandFormats(); - foreach (wl_shm_format format, formats) + const QVector<wl_shm_format> formats = QWaylandSharedMemoryFormatHelper::supportedWaylandFormats(); + for (wl_shm_format format : formats) wl_display_add_shm_format(display, format); if (!socket_name.isEmpty()) { @@ -234,7 +234,7 @@ void QWaylandCompositorPrivate::init() initialized = true; - Q_FOREACH (QPointer<QObject> object, polish_objects) { + for (const QPointer<QObject> &object : qExchange(polish_objects, {})) { if (object) { QEvent polishEvent(QEvent::Polish); QCoreApplication::sendEvent(object.data(), &polishEvent); @@ -302,7 +302,7 @@ void QWaylandCompositorPrivate::addPolishObject(QObject *object) if (initialized) { QCoreApplication::postEvent(object, new QEvent(QEvent::Polish)); } else { - polish_objects.append(object); + polish_objects.push_back(object); } } @@ -706,7 +706,7 @@ QList<QWaylandSurface *> QWaylandCompositor::surfacesForClient(QWaylandClient* c { Q_D(const QWaylandCompositor); QList<QWaylandSurface *> surfs; - foreach (QWaylandSurface *surface, d->all_surfaces) { + for (QWaylandSurface *surface : d->all_surfaces) { if (surface->client() == client) surfs.append(surface); } @@ -728,7 +728,7 @@ QList<QWaylandSurface *> QWaylandCompositor::surfaces() const QWaylandOutput *QWaylandCompositor::outputFor(QWindow *window) const { Q_D(const QWaylandCompositor); - foreach (QWaylandOutput *output, d->outputs) { + for (QWaylandOutput *output : d->outputs) { if (output->window() == window) return output; } diff --git a/src/compositor/compositor_api/qwaylandcompositor_p.h b/src/compositor/compositor_api/qwaylandcompositor_p.h index 2c962421..2437533d 100644 --- a/src/compositor/compositor_api/qwaylandcompositor_p.h +++ b/src/compositor/compositor_api/qwaylandcompositor_p.h @@ -60,6 +60,8 @@ #include <QtWaylandCompositor/private/qwayland-server-wayland.h> +#include <vector> + #if QT_CONFIG(xkbcommon) #include <QtXkbCommonSupport/private/qxkbcommon_p.h> #endif @@ -175,7 +177,7 @@ protected: bool retainSelection = false; bool preInitialized = false; bool initialized = false; - QList<QPointer<QObject> > polish_objects; + std::vector<QPointer<QObject> > polish_objects; #if QT_CONFIG(xkbcommon) QXkbCommon::ScopedXKBContext mXkbContext; diff --git a/src/compositor/compositor_api/qwaylandkeyboard.cpp b/src/compositor/compositor_api/qwaylandkeyboard.cpp index 2302c0b6..c5ec008d 100644 --- a/src/compositor/compositor_api/qwaylandkeyboard.cpp +++ b/src/compositor/compositor_api/qwaylandkeyboard.cpp @@ -256,7 +256,8 @@ void QWaylandKeyboardPrivate::maybeUpdateKeymap() return; createXKBKeymap(); - foreach (Resource *res, resourceMap()) { + const auto resMap = resourceMap(); + for (Resource *res : resMap) { send_keymap(res->handle, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, keymap_size); } @@ -365,6 +366,13 @@ void QWaylandKeyboardPrivate::createXKBKeymap() QByteArray variant = keymap->variant().toLocal8Bit(); QByteArray options = keymap->options().toLocal8Bit(); + if (!layout.isEmpty() && !layout.contains("us")) { + // This is needed for shortucts like "ctrl+c" to function even when + // user has selected only non-latin keyboard layouts, e.g. 'ru'. + layout.append(",us"); + variant.append(","); + } + struct xkb_rule_names rule_names = { rules.constData(), model.constData(), @@ -386,7 +394,8 @@ void QWaylandKeyboardPrivate::createXKBKeymap() void QWaylandKeyboardPrivate::sendRepeatInfo() { - Q_FOREACH (Resource *resource, resourceMap()) { + const auto resMap = resourceMap(); + for (Resource *resource : resMap) { if (resource->version() >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) send_repeat_info(resource->handle, repeatRate, repeatDelay); } diff --git a/src/compositor/compositor_api/qwaylandoutput.cpp b/src/compositor/compositor_api/qwaylandoutput.cpp index 006edbe6..601f692a 100644 --- a/src/compositor/compositor_api/qwaylandoutput.cpp +++ b/src/compositor/compositor_api/qwaylandoutput.cpp @@ -48,6 +48,7 @@ #include <QtWaylandCompositor/private/qwaylandcompositor_p.h> #include <QtWaylandCompositor/private/qwaylandview_p.h> #include <QtWaylandCompositor/private/qwaylandutils_p.h> +#include <QtWaylandCompositor/private/qwaylandxdgoutputv1_p.h> #include <QtCore/QCoreApplication> #include <QtCore/QtMath> @@ -162,6 +163,9 @@ void QWaylandOutputPrivate::sendGeometryInfo() if (resource->version() >= 2) send_done(resource->handle); } + + if (xdgOutput) + QWaylandXdgOutputV1Private::get(xdgOutput)->sendDone(); } void QWaylandOutputPrivate::sendMode(const Resource *resource, const QWaylandOutputMode &mode) @@ -185,6 +189,9 @@ void QWaylandOutputPrivate::sendModesInfo() if (resource->version() >= 2) send_done(resource->handle); } + + if (xdgOutput) + QWaylandXdgOutputV1Private::get(xdgOutput)->sendDone(); } void QWaylandOutputPrivate::handleWindowPixelSizeChanged() @@ -831,7 +838,8 @@ void QWaylandOutput::setScaleFactor(int scale) d->scaleFactor = scale; - Q_FOREACH (QWaylandOutputPrivate::Resource *resource, d->resourceMap().values()) { + const auto resMap = d->resourceMap(); + for (QWaylandOutputPrivate::Resource *resource : resMap) { if (resource->version() >= 2) { d->send_scale(resource->handle, scale); d->send_done(resource->handle); @@ -839,6 +847,9 @@ void QWaylandOutput::setScaleFactor(int scale) } Q_EMIT scaleFactorChanged(); + + if (d->xdgOutput) + QWaylandXdgOutputV1Private::get(d->xdgOutput)->sendDone(); } /*! diff --git a/src/compositor/compositor_api/qwaylandoutput_p.h b/src/compositor/compositor_api/qwaylandoutput_p.h index 4badd379..58188ac3 100644 --- a/src/compositor/compositor_api/qwaylandoutput_p.h +++ b/src/compositor/compositor_api/qwaylandoutput_p.h @@ -57,6 +57,7 @@ #include <QtWaylandCompositor/QWaylandOutput> #include <QtWaylandCompositor/QWaylandClient> #include <QtWaylandCompositor/QWaylandSurface> +#include <QtWaylandCompositor/QWaylandXdgOutputV1> #include <QtWaylandCompositor/private/qwayland-server-wayland.h> @@ -110,6 +111,8 @@ public: void handleWindowPixelSizeChanged(); + QPointer<QWaylandXdgOutputV1> xdgOutput; + protected: void output_bind_resource(Resource *resource) override; @@ -137,6 +140,8 @@ private: Q_DECLARE_PUBLIC(QWaylandOutput) Q_DISABLE_COPY(QWaylandOutputPrivate) + + friend class QWaylandXdgOutputManagerV1Private; }; diff --git a/src/compositor/compositor_api/qwaylandquickcompositor.cpp b/src/compositor/compositor_api/qwaylandquickcompositor.cpp index 14b592ef..1b3f93e5 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 648993fc..ecea8125 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(); @@ -603,13 +610,17 @@ void QWaylandQuickItem::wheelEvent(QWheelEvent *event) { Q_D(QWaylandQuickItem); if (d->shouldSendInputEvents()) { - if (!inputRegionContains(event->posF())) { + if (!inputRegionContains(event->position())) { event->ignore(); return; } QWaylandSeat *seat = compositor()->seatFor(event); - seat->sendMouseWheelEvent(event->orientation(), event->delta()); + // TODO: fix this to send a single event, when diagonal scrolling is supported + if (event->angleDelta().x() != 0) + seat->sendMouseWheelEvent(Qt::Horizontal, event->angleDelta().x()); + if (event->angleDelta().y() != 0) + seat->sendMouseWheelEvent(Qt::Vertical, event->angleDelta().y()); } else { event->ignore(); } @@ -1327,7 +1338,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 +1368,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 352a130d..832272a3 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 bc5e1d8b..0a6248d1 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/qwaylandsurface.cpp b/src/compositor/compositor_api/qwaylandsurface.cpp index a82c93f7..c5462839 100644 --- a/src/compositor/compositor_api/qwaylandsurface.cpp +++ b/src/compositor/compositor_api/qwaylandsurface.cpp @@ -137,9 +137,9 @@ QWaylandSurfacePrivate::~QWaylandSurfacePrivate() bufferRef = QWaylandBufferRef(); - foreach (QtWayland::FrameCallback *c, pendingFrameCallbacks) + for (QtWayland::FrameCallback *c : qAsConst(pendingFrameCallbacks)) c->destroy(); - foreach (QtWayland::FrameCallback *c, frameCallbacks) + for (QtWayland::FrameCallback *c : qAsConst(frameCallbacks)) c->destroy(); } @@ -152,7 +152,8 @@ void QWaylandSurfacePrivate::removeFrameCallback(QtWayland::FrameCallback *callb void QWaylandSurfacePrivate::notifyViewsAboutDestruction() { Q_Q(QWaylandSurface); - foreach (QWaylandView *view, views) { + const auto viewsCopy = views; // Views will be removed from the list when marked as destroyed + for (QWaylandView *view : viewsCopy) { QWaylandViewPrivate::get(view)->markSurfaceAsDestroyed(q); } if (hasContent) { @@ -674,7 +675,7 @@ QWaylandCompositor *QWaylandSurface::compositor() const void QWaylandSurface::frameStarted() { Q_D(QWaylandSurface); - foreach (QtWayland::FrameCallback *c, d->frameCallbacks) + for (QtWayland::FrameCallback *c : qAsConst(d->frameCallbacks)) c->canSend = true; } @@ -781,6 +782,31 @@ bool QWaylandSurface::isCursorSurface() const return d->isCursorSurface; } +/*! + * \qmlproperty bool QtWaylandCompositor::WaylandSurface::inhibitsIdle + * \since 5.14 + * + * This property holds whether this surface is intended to inhibit the idle + * behavior of the compositor such as screen blanking, locking and screen saving. + * + * \sa IdleInhibitManagerV1 + */ + +/*! + * \property QWaylandSurface::inhibitsIdle + * \since 5.14 + * + * This property holds whether this surface is intended to inhibit the idle + * behavior of the compositor such as screen blanking, locking and screen saving. + * + * \sa QWaylandIdleInhibitManagerV1 + */ +bool QWaylandSurface::inhibitsIdle() const +{ + Q_D(const QWaylandSurface); + return !d->idleInhibitors.isEmpty(); +} + #if QT_CONFIG(im) QWaylandInputMethodControl *QWaylandSurface::inputMethodControl() const { diff --git a/src/compositor/compositor_api/qwaylandsurface.h b/src/compositor/compositor_api/qwaylandsurface.h index 48b69fe2..f1a8bad2 100644 --- a/src/compositor/compositor_api/qwaylandsurface.h +++ b/src/compositor/compositor_api/qwaylandsurface.h @@ -92,6 +92,7 @@ class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandSurface : public QWaylandObject Q_PROPERTY(QWaylandSurface::Origin origin READ origin NOTIFY originChanged) Q_PROPERTY(bool hasContent READ hasContent NOTIFY hasContentChanged) Q_PROPERTY(bool cursorSurface READ isCursorSurface WRITE markAsCursorSurface NOTIFY cursorSurfaceChanged) + Q_PROPERTY(bool inhibitsIdle READ inhibitsIdle NOTIFY inhibitsIdleChanged REVISION 14) public: enum Origin { @@ -155,6 +156,8 @@ public: void markAsCursorSurface(bool cursorSurface); bool isCursorSurface() const; + bool inhibitsIdle() const; + #if QT_CONFIG(im) QWaylandInputMethodControl *inputMethodControl() const; #endif @@ -188,6 +191,7 @@ Q_SIGNALS: void subsurfacePlaceBelow(QWaylandSurface *sibling); void dragStarted(QWaylandDrag *drag); void cursorSurfaceChanged(); + Q_REVISION(14) void inhibitsIdleChanged(); void configure(bool hasBuffer); void redraw(); diff --git a/src/compositor/compositor_api/qwaylandsurface_p.h b/src/compositor/compositor_api/qwaylandsurface_p.h index 1637d870..97cb19d9 100644 --- a/src/compositor/compositor_api/qwaylandsurface_p.h +++ b/src/compositor/compositor_api/qwaylandsurface_p.h @@ -74,6 +74,7 @@ #include <QtWaylandCompositor/private/qwayland-server-wayland.h> #include <QtWaylandCompositor/private/qwaylandviewporter_p.h> +#include <QtWaylandCompositor/private/qwaylandidleinhibitv1_p.h> QT_BEGIN_NAMESPACE @@ -167,6 +168,8 @@ public: //member variables QList<QPointer<QWaylandSurface>> subsurfaceChildren; + QVector<QWaylandIdleInhibitManagerV1Private::Inhibitor *> idleInhibitors; + QRegion inputRegion; QRegion opaqueRegion; diff --git a/src/compositor/compositor_api/qwaylandview.cpp b/src/compositor/compositor_api/qwaylandview.cpp index 1a6bf1a6..12889bdf 100644 --- a/src/compositor/compositor_api/qwaylandview.cpp +++ b/src/compositor/compositor_api/qwaylandview.cpp @@ -246,7 +246,8 @@ bool QWaylandView::advance() return false; if (d->surface && d->surface->primaryView() == this) { - Q_FOREACH (QWaylandView *view, d->surface->views()) { + const auto views = d->surface->views(); + for (QWaylandView *view : views) { if (view != this && view->allowDiscardFrontBuffer() && view->d_func()->currentBuffer == d->currentBuffer) view->discardCurrentBuffer(); } diff --git a/src/compositor/configure.json b/src/compositor/configure.json index 5d8fe8dd..46caceff 100644 --- a/src/compositor/configure.json +++ b/src/compositor/configure.json @@ -145,14 +145,9 @@ "condition": "features.wayland-server && features.opengl && features.egl && tests.dmabuf-client-buffer", "output": [ "privateFeature" ] }, - "wayland-compositor-texture-sharing-experimental" : { - "label": "Texture sharing (experimental)", - "autoDetect": "false", - "output": [ "privateFeature" ] - }, "wayland-vulkan-server-buffer": { "label": "Vulkan-based server buffer integration", - "condition": "features.wayland-server && features.opengl && features.egl && tests.vulkan-server-buffer && features.wayland-compositor-texture-sharing-experimental", + "condition": "features.wayland-server && features.opengl && features.egl && tests.vulkan-server-buffer", "output": [ "privateFeature" ] }, "wayland-shm-emulation-server-buffer": { diff --git a/src/compositor/extensions/extensions.pri b/src/compositor/extensions/extensions.pri index d8f0069d..61ab043e 100644 --- a/src/compositor/extensions/extensions.pri +++ b/src/compositor/extensions/extensions.pri @@ -14,7 +14,9 @@ WAYLANDSERVERSOURCES += \ ../3rdparty/protocol/xdg-shell-unstable-v6.xml \ ../3rdparty/protocol/xdg-shell.xml \ ../3rdparty/protocol/xdg-decoration-unstable-v1.xml \ + ../3rdparty/protocol/xdg-output-unstable-v1.xml \ ../3rdparty/protocol/ivi-application.xml \ + ../3rdparty/protocol/idle-inhibit-unstable-v1.xml \ HEADERS += \ extensions/qwlqttouch_p.h \ @@ -41,7 +43,11 @@ HEADERS += \ extensions/qwaylandxdgshell_p.h \ extensions/qwaylandxdgdecorationv1.h \ extensions/qwaylandxdgdecorationv1_p.h \ + extensions/qwaylandxdgoutputv1.h \ + extensions/qwaylandxdgoutputv1_p.h \ extensions/qwaylandshellsurface.h \ + extensions/qwaylandidleinhibitv1.h \ + extensions/qwaylandidleinhibitv1_p.h \ extensions/qwaylandiviapplication.h \ extensions/qwaylandiviapplication_p.h \ extensions/qwaylandivisurface.h \ @@ -61,32 +67,43 @@ SOURCES += \ extensions/qwaylandxdgshellv6.cpp \ extensions/qwaylandxdgshell.cpp \ extensions/qwaylandxdgdecorationv1.cpp \ + extensions/qwaylandxdgoutputv1.cpp \ extensions/qwaylandshellsurface.cpp \ + extensions/qwaylandidleinhibitv1.cpp \ 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/qwaylandquickxdgoutputv1.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/qwaylandquickxdgoutputv1.cpp \ extensions/qwaylandxdgshellv5integration.cpp \ extensions/qwaylandxdgshellv6integration.cpp \ - extensions/qwaylandxdgshellintegration.cpp + extensions/qwaylandxdgshellintegration.cpp \ - qtConfig(wayland-compositor-texture-sharing-experimental) { - HEADERS += extensions/qwltexturesharingextension_p.h - SOURCES += extensions/qwltexturesharingextension.cpp - WAYLANDSERVERSOURCES += ../extensions/qt-texture-sharing-unstable-v1.xml + qtConfig(opengl) { + WAYLANDSERVERSOURCES += \ + ../extensions/qt-texture-sharing-unstable-v1.xml + + HEADERS += \ + extensions/qwltexturesharingextension_p.h + + SOURCES += \ + extensions/qwltexturesharingextension.cpp } } diff --git a/src/compositor/extensions/qwaylandidleinhibitv1.cpp b/src/compositor/extensions/qwaylandidleinhibitv1.cpp new file mode 100644 index 00000000..b97f5813 --- /dev/null +++ b/src/compositor/extensions/qwaylandidleinhibitv1.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtWaylandCompositor/QWaylandCompositor> +#include <QtWaylandCompositor/private/qwaylandsurface_p.h> + +#include "qwaylandidleinhibitv1_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QWaylandIdleInhibitManagerV1 + \inmodule QtWaylandCompositor + \since 5.14 + \brief Provides an extension that allows to inhibit the idle behavior of the compositor + \sa QWaylandSurface::inhibitsIdle + + The QWaylandIdleInhibitV1 extension provides a way for a client to inhibit the idle behavior of + the compositor when a specific surface is visually relevant to the user. + + QWaylandIdleInhibitManagerV1 corresponds to the Wayland interface, \c zwp_idle_inhibit_manager_v1. + + Inhibited surfaces have the QWaylandSurface::inhibitsIdle property set to \c true. +*/ + +/*! + \qmltype IdleInhibitManagerV1 + \inqmlmodule QtWayland.Compositor + \since 5.14 + \brief Provides an extension that allows to inhibit the idle behavior of the compositor + \sa WaylandSurface::inhibitsIdle + + The IdleInhibitManagerV1 extension provides a way for a client to inhibit the idle behavior of + the compositor when a specific surface is visually relevant to the user. + + IdleInhibitManagerV1 corresponds to the Wayland interface, \c zwp_idle_inhibit_manager_v1. + + To provide the functionality of the extension in a compositor, create an instance of the + IdleInhibitManagerV1 component and add it to the list of extensions supported by the compositor: + + \qml \QtMinorVersion + import QtWayland.Compositor 1.\1 + + WaylandCompositor { + IdleInhibitManagerV1 { + // ... + } + } + \endqml + + Inhibited surfaces have the WaylandSurface::inhibitsIdle property set to \c true. +*/ + +/*! + Constructs a QWaylandIdleInhibitManagerV1 object. +*/ +QWaylandIdleInhibitManagerV1::QWaylandIdleInhibitManagerV1() + : QWaylandCompositorExtensionTemplate<QWaylandIdleInhibitManagerV1>(*new QWaylandIdleInhibitManagerV1Private()) +{ +} + +/*! + Constructs a QWaylandIdleInhibitManagerV1 object for the provided \a compositor. +*/ +QWaylandIdleInhibitManagerV1::QWaylandIdleInhibitManagerV1(QWaylandCompositor *compositor) + : QWaylandCompositorExtensionTemplate<QWaylandIdleInhibitManagerV1>(compositor, *new QWaylandIdleInhibitManagerV1Private()) +{ +} + +/*! + Destructs a QWaylandIdleInhibitManagerV1 object. +*/ +QWaylandIdleInhibitManagerV1::~QWaylandIdleInhibitManagerV1() = default; + +/*! + Initializes the extension. +*/ +void QWaylandIdleInhibitManagerV1::initialize() +{ + Q_D(QWaylandIdleInhibitManagerV1); + + QWaylandCompositorExtensionTemplate::initialize(); + QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer()); + if (!compositor) { + qCWarning(qLcWaylandCompositor) << "Failed to find QWaylandCompositor when initializing QWaylandIdleInhibitManagerV1"; + return; + } + d->init(compositor->display(), d->interfaceVersion()); +} + +/*! + Returns the Wayland interface for the QWaylandIdleInhibitManagerV1. +*/ +const wl_interface *QWaylandIdleInhibitManagerV1::interface() +{ + return QWaylandIdleInhibitManagerV1Private::interface(); +} + + +void QWaylandIdleInhibitManagerV1Private::zwp_idle_inhibit_manager_v1_create_inhibitor(Resource *resource, uint id, wl_resource *surfaceResource) +{ + auto *surface = QWaylandSurface::fromResource(surfaceResource); + if (!surface) { + qCWarning(qLcWaylandCompositor) << "Couldn't find surface requested for creating an inhibitor"; + wl_resource_post_error(resource->handle, WL_DISPLAY_ERROR_INVALID_OBJECT, + "invalid wl_surface@%d", wl_resource_get_id(surfaceResource)); + return; + } + + auto *surfacePrivate = QWaylandSurfacePrivate::get(surface); + if (!surfacePrivate) { + wl_resource_post_no_memory(resource->handle); + return; + } + + auto *inhibitor = new Inhibitor(surface, resource->client(), id, resource->version()); + if (!inhibitor) { + wl_resource_post_no_memory(resource->handle); + return; + } + surfacePrivate->idleInhibitors.append(inhibitor); + + if (surfacePrivate->idleInhibitors.size() == 1) + Q_EMIT surface->inhibitsIdleChanged(); +} + + +QWaylandIdleInhibitManagerV1Private::Inhibitor::Inhibitor(QWaylandSurface *surface, + wl_client *client, + quint32 id, quint32 version) + : QtWaylandServer::zwp_idle_inhibitor_v1(client, id, qMin<quint32>(version, interfaceVersion())) + , m_surface(surface) +{ + Q_ASSERT(surface); +} + +void QWaylandIdleInhibitManagerV1Private::Inhibitor::zwp_idle_inhibitor_v1_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource); + delete this; +} + +void QWaylandIdleInhibitManagerV1Private::Inhibitor::zwp_idle_inhibitor_v1_destroy(Resource *resource) +{ + if (m_surface) { + auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface.data()); + Q_ASSERT(surfacePrivate->idleInhibitors.contains(this)); + surfacePrivate->idleInhibitors.removeOne(this); + + if (surfacePrivate->idleInhibitors.isEmpty()) + Q_EMIT m_surface.data()->inhibitsIdleChanged(); + } + + wl_resource_destroy(resource->handle); +} + +QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandidleinhibitv1.h b/src/compositor/extensions/qwaylandidleinhibitv1.h new file mode 100644 index 00000000..53c09d08 --- /dev/null +++ b/src/compositor/extensions/qwaylandidleinhibitv1.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDIDLEINHIBITV1_H +#define QWAYLANDIDLEINHIBITV1_H + +#include <QtWaylandCompositor/QWaylandCompositorExtension> + +QT_BEGIN_NAMESPACE + +class QWaylandIdleInhibitManagerV1Private; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandIdleInhibitManagerV1 : public QWaylandCompositorExtensionTemplate<QWaylandIdleInhibitManagerV1> +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandIdleInhibitManagerV1) +public: + QWaylandIdleInhibitManagerV1(); + explicit QWaylandIdleInhibitManagerV1(QWaylandCompositor *compositor); + ~QWaylandIdleInhibitManagerV1(); + + void initialize() override; + + static const struct wl_interface *interface(); +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDIDLEINHIBITV1_H diff --git a/src/compositor/extensions/qwaylandidleinhibitv1_p.h b/src/compositor/extensions/qwaylandidleinhibitv1_p.h new file mode 100644 index 00000000..38055180 --- /dev/null +++ b/src/compositor/extensions/qwaylandidleinhibitv1_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDIDLEINHIBITV1_P_H +#define QWAYLANDIDLEINHIBITV1_P_H + +#include <QtWaylandCompositor/QWaylandSurface> +#include <QtWaylandCompositor/QWaylandIdleInhibitManagerV1> +#include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h> +#include <QtWaylandCompositor/private/qwayland-server-idle-inhibit-unstable-v1.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. +// + +QT_BEGIN_NAMESPACE + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandIdleInhibitManagerV1Private + : public QWaylandCompositorExtensionPrivate + , public QtWaylandServer::zwp_idle_inhibit_manager_v1 +{ + Q_DECLARE_PUBLIC(QWaylandIdleInhibitManagerV1) +public: + explicit QWaylandIdleInhibitManagerV1Private() = default; + + class Q_WAYLAND_COMPOSITOR_EXPORT Inhibitor + : public QtWaylandServer::zwp_idle_inhibitor_v1 + { + public: + explicit Inhibitor(QWaylandSurface *surface, wl_client *client, quint32 id, quint32 version); + + protected: + void zwp_idle_inhibitor_v1_destroy_resource(Resource *resource) override; + void zwp_idle_inhibitor_v1_destroy(Resource *resource) override; + + private: + QPointer<QWaylandSurface> m_surface; + }; + + static QWaylandIdleInhibitManagerV1Private *get(QWaylandIdleInhibitManagerV1 *manager) { return manager ? manager->d_func() : nullptr; } + +protected: + void zwp_idle_inhibit_manager_v1_create_inhibitor(Resource *resource, uint32_t id, wl_resource *surfaceResource) override; +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDIDLEINHIBITV1_P_H diff --git a/src/compositor/extensions/qwaylandqtwindowmanager.cpp b/src/compositor/extensions/qwaylandqtwindowmanager.cpp index 86678b2e..c3cf3488 100644 --- a/src/compositor/extensions/qwaylandqtwindowmanager.cpp +++ b/src/compositor/extensions/qwaylandqtwindowmanager.cpp @@ -109,7 +109,8 @@ void QWaylandQtWindowManager::setShowIsFullScreen(bool value) return; d->showIsFullScreen = value; - Q_FOREACH (QWaylandQtWindowManagerPrivate::Resource *resource, d->resourceMap().values()) { + const auto resMap = d->resourceMap(); + for (QWaylandQtWindowManagerPrivate::Resource *resource : resMap) { d->send_hints(resource->handle, static_cast<int32_t>(d->showIsFullScreen)); } Q_EMIT showIsFullScreenChanged(); diff --git a/src/compositor/extensions/qwaylandquickshellintegration.cpp b/src/compositor/extensions/qwaylandquickshellintegration.cpp new file mode 100644 index 00000000..d56d6c22 --- /dev/null +++ b/src/compositor/extensions/qwaylandquickshellintegration.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** 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. + * + * Shell integration is installed as an event filter for a QWaylandQuickShellSurfaceItem. + * Reimplement the event filter method and return \c true when you want to filter the + * event out, otherwise return \c false. + * + * Example: + * + * \code + * class MyShellIntegration : public QWaylandQuickShellIntegration + * { + * Q_OBJECT + * public: + * MyShellIntegration(QObject *parent = nullptr); + * + * protected: + * bool eventFilter(QObject *object, QEvent *event) override; + * }; + * + * MyShellIntegration::MyShellIntegration(QObject *parent) + * : QWaylandQuickShellIntegration(parent) + * { + * } + * + * bool MyShellIntegration::eventFilter(QObject *object, QEvent *event) + * { + * QWaylandQuickShellSurfaceItem *shellSurfaceItem = qobject_cast<QWaylandQuickShellSurfaceItem *>(object); + * if (!shellSurfaceItem) + * return QWaylandQuickShellIntegration::eventFilter(object, event); + * + * if (event->type() == QEvent::MouseMove) { + * QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + * qDebug() << "Mouse moved on" << shellSurfaceItem << "pos:" << mouseEvent->pos(); + * return true; + * } + * + * return QWaylandQuickShellIntegration::eventFilter(object, event); + * } + * \endcode + * + * \sa QWaylandQuickShellSurfaceItem + * \sa QObject::eventFilter() + */ + +QWaylandQuickShellIntegration::QWaylandQuickShellIntegration(QObject *parent) + : QObject(parent) +{ +} + +QWaylandQuickShellIntegration::~QWaylandQuickShellIntegration() +{ +} diff --git a/src/compositor/extensions/qwaylandquickshellintegration.h b/src/compositor/extensions/qwaylandquickshellintegration.h new file mode 100644 index 00000000..00696681 --- /dev/null +++ b/src/compositor/extensions/qwaylandquickshellintegration.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** 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 <QtWaylandCompositor/qtwaylandcompositorglobal.h> + +QT_BEGIN_NAMESPACE + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickShellIntegration : public QObject +{ + Q_OBJECT +public: + QWaylandQuickShellIntegration(QObject *parent = nullptr); + ~QWaylandQuickShellIntegration() override; +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDQUICKSHELLINTEGRATION_H diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp b/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp index 84e9c356..bda53601 100644 --- a/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp +++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp @@ -96,7 +96,11 @@ QWaylandQuickShellSurfaceItem::QWaylandQuickShellSurfaceItem(QQuickItem *parent) QWaylandQuickShellSurfaceItem::~QWaylandQuickShellSurfaceItem() { Q_D(QWaylandQuickShellSurfaceItem); - delete d->m_shellIntegration; + + if (d->m_shellIntegration) { + removeEventFilter(d->m_shellIntegration); + delete d->m_shellIntegration; + } } /*! @@ -137,12 +141,15 @@ void QWaylandQuickShellSurfaceItem::setShellSurface(QWaylandShellSurface *shellS d->m_shellSurface = shellSurface; if (d->m_shellIntegration) { + removeEventFilter(d->m_shellIntegration); delete d->m_shellIntegration; d->m_shellIntegration = nullptr; } - if (shellSurface) + if (shellSurface) { d->m_shellIntegration = shellSurface->createIntegration(this); + installEventFilter(d->m_shellIntegration); + } emit shellSurfaceChanged(); } @@ -208,20 +215,6 @@ void QWaylandQuickShellSurfaceItem::setAutoCreatePopupItems(bool enabled) emit autoCreatePopupItemsChanged(); } -void QWaylandQuickShellSurfaceItem::mouseMoveEvent(QMouseEvent *event) -{ - Q_D(QWaylandQuickShellSurfaceItem); - if (!d->m_shellIntegration->mouseMoveEvent(event)) - QWaylandQuickItem::mouseMoveEvent(event); -} - -void QWaylandQuickShellSurfaceItem::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QWaylandQuickShellSurfaceItem); - if (!d->m_shellIntegration->mouseReleaseEvent(event)) - QWaylandQuickItem::mouseReleaseEvent(event); -} - /*! \class QWaylandQuickShellEventFilter \brief QWaylandQuickShellEventFilter implements a Wayland popup grab diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem.h b/src/compositor/extensions/qwaylandquickshellsurfaceitem.h index d14fa3fc..51c0425e 100644 --- a/src/compositor/extensions/qwaylandquickshellsurfaceitem.h +++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem.h @@ -75,9 +75,6 @@ Q_SIGNALS: protected: QWaylandQuickShellSurfaceItem(QWaylandQuickShellSurfaceItemPrivate &dd, QQuickItem *parent); - - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; }; QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h b/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h index 7a458381..f622368a 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/qwaylandquickxdgoutputv1.cpp b/src/compositor/extensions/qwaylandquickxdgoutputv1.cpp new file mode 100644 index 00000000..eb6717a7 --- /dev/null +++ b/src/compositor/extensions/qwaylandquickxdgoutputv1.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QWaylandCompositor> +#include <QWaylandOutput> + +#include "qwaylandquickxdgoutputv1.h" +#include "qwaylandxdgoutputv1_p.h" + +QWaylandQuickXdgOutputV1::QWaylandQuickXdgOutputV1() + : QWaylandXdgOutputV1() +{ +} + +void QWaylandQuickXdgOutputV1::componentComplete() +{ + // Try to find the manager from the compositor extensions + if (!manager()) { + for (auto *p = parent(); p != nullptr; p = p->parent()) { + if (auto *c = qobject_cast<QWaylandCompositor *>(p)) { + for (auto *extension : c->extensions()) { + if (auto *m = qobject_cast<QWaylandXdgOutputManagerV1 *>(extension)) { + QWaylandXdgOutputV1Private::get(this)->setManager(m); + break; + } + } + } + } + } + + // Try to find the output from the parents + if (!output()) { + for (auto *p = parent(); p != nullptr; p = p->parent()) { + if (auto *o = qobject_cast<QWaylandOutput *>(p)) { + QWaylandXdgOutputV1Private::get(this)->setOutput(o); + break; + } + } + } +} diff --git a/src/compositor/extensions/qwaylandquickxdgoutputv1.h b/src/compositor/extensions/qwaylandquickxdgoutputv1.h new file mode 100644 index 00000000..c8b16ab8 --- /dev/null +++ b/src/compositor/extensions/qwaylandquickxdgoutputv1.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDQUICKXDGOUTPUT_V1 +#define QWAYLANDQUICKXDGOUTPUT_V1 + +#include <QtQml/QQmlListProperty> +#include <QtQml/QQmlParserStatus> +#include <QtWaylandCompositor/QWaylandXdgOutputV1> + +QT_BEGIN_NAMESPACE + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickXdgOutputV1 + : public QWaylandXdgOutputV1 + , public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) +public: + explicit QWaylandQuickXdgOutputV1(); + +protected: + void classBegin() override {} + void componentComplete() override; +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDQUICKXDGOUTPUT_V1 diff --git a/src/compositor/extensions/qwaylandtextinput.cpp b/src/compositor/extensions/qwaylandtextinput.cpp index f60a32a1..b46caa64 100644 --- a/src/compositor/extensions/qwaylandtextinput.cpp +++ b/src/compositor/extensions/qwaylandtextinput.cpp @@ -162,7 +162,7 @@ void QWaylandTextInputPrivate::sendInputMethodEvent(QInputMethodEvent *event) afterCommit.cursorPosition += event->commitString().length(); afterCommit.anchorPosition = afterCommit.cursorPosition; - foreach (const QInputMethodEvent::Attribute &attribute, event->attributes()) { + for (const QInputMethodEvent::Attribute &attribute : event->attributes()) { if (attribute.type == QInputMethodEvent::Selection) { afterCommit.cursorPosition = attribute.start; afterCommit.anchorPosition = attribute.length; @@ -174,7 +174,7 @@ void QWaylandTextInputPrivate::sendInputMethodEvent(QInputMethodEvent *event) } } send_commit_string(focusResource->handle, event->commitString()); - foreach (const QInputMethodEvent::Attribute &attribute, event->attributes()) { + for (const QInputMethodEvent::Attribute &attribute : event->attributes()) { if (attribute.type == QInputMethodEvent::Cursor) { int index = QWaylandInputMethodEventBuilder::indexToWayland(event->preeditString(), attribute.start); send_preedit_cursor(focusResource->handle, index); diff --git a/src/compositor/extensions/qwaylandwlscaler_p.h b/src/compositor/extensions/qwaylandwlscaler_p.h index d3c2edd7..10a66f88 100644 --- a/src/compositor/extensions/qwaylandwlscaler_p.h +++ b/src/compositor/extensions/qwaylandwlscaler_p.h @@ -55,6 +55,7 @@ QT_BEGIN_NAMESPACE +#if QT_DEPRECATED_SINCE(5, 13) class QWaylandSurface; class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandWlScalerPrivate @@ -87,6 +88,7 @@ private: QPointer<QWaylandSurface> m_surface = nullptr; }; }; +#endif QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandwlshell.cpp b/src/compositor/extensions/qwaylandwlshell.cpp index 66aeb64b..ea228cab 100644 --- a/src/compositor/extensions/qwaylandwlshell.cpp +++ b/src/compositor/extensions/qwaylandwlshell.cpp @@ -332,7 +332,7 @@ QList<QWaylandWlShellSurface *> QWaylandWlShell::shellSurfacesForClient(QWayland { Q_D(const QWaylandWlShell); QList<QWaylandWlShellSurface *> surfsForClient; - Q_FOREACH (QWaylandWlShellSurface *shellSurface, d->m_shellSurfaces) { + for (QWaylandWlShellSurface *shellSurface : d->m_shellSurfaces) { if (shellSurface->surface() && shellSurface->surface()->client() == client) surfsForClient.append(shellSurface); } @@ -343,7 +343,7 @@ QList<QWaylandWlShellSurface *> QWaylandWlShell::mappedPopups() const { Q_D(const QWaylandWlShell); QList<QWaylandWlShellSurface *> popupSurfaces; - Q_FOREACH (QWaylandWlShellSurface *shellSurface, d->m_shellSurfaces) { + for (QWaylandWlShellSurface *shellSurface : d->m_shellSurfaces) { if (shellSurface->windowType() == Qt::WindowType::Popup && shellSurface->surface() && shellSurface->surface()->hasContent()) { popupSurfaces.append(shellSurface); @@ -355,7 +355,7 @@ QList<QWaylandWlShellSurface *> QWaylandWlShell::mappedPopups() const QWaylandClient *QWaylandWlShell::popupClient() const { Q_D(const QWaylandWlShell); - Q_FOREACH (QWaylandWlShellSurface *shellSurface, d->m_shellSurfaces) { + for (QWaylandWlShellSurface *shellSurface : d->m_shellSurfaces) { if (shellSurface->windowType() == Qt::WindowType::Popup && shellSurface->surface() && shellSurface->surface()->hasContent()) { return shellSurface->surface()->client(); @@ -366,7 +366,8 @@ QWaylandClient *QWaylandWlShell::popupClient() const void QWaylandWlShell::closeAllPopups() { - Q_FOREACH (QWaylandWlShellSurface* shellSurface, mappedPopups()) + const auto mapped = mappedPopups(); + for (QWaylandWlShellSurface *shellSurface : mapped) shellSurface->sendPopupDone(); } diff --git a/src/compositor/extensions/qwaylandwlshellintegration.cpp b/src/compositor/extensions/qwaylandwlshellintegration.cpp index 99a2e765..724580e2 100644 --- a/src/compositor/extensions/qwaylandwlshellintegration.cpp +++ b/src/compositor/extensions/qwaylandwlshellintegration.cpp @@ -198,7 +198,8 @@ void WlShellIntegration::handleSetPopup(QWaylandSeat *seat, QWaylandSurface *par // Find the parent item on the same output QWaylandQuickShellSurfaceItem *parentItem = nullptr; - Q_FOREACH (QWaylandView *view, parent->views()) { + const auto views = parent->views(); + for (QWaylandView *view : views) { if (view->output() == m_item->view()->output()) { QWaylandQuickShellSurfaceItem *item = qobject_cast<QWaylandQuickShellSurfaceItem*>(view->renderObject()); if (item) { @@ -288,7 +289,19 @@ void WlShellIntegration::adjustOffsetForNextFrame(const QPointF &offset) moveItem->setPosition(moveItem->position() + m_item->mapFromSurface(offset)); } -bool WlShellIntegration::mouseMoveEvent(QMouseEvent *event) +bool WlShellIntegration::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::MouseMove) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + return filterMouseMoveEvent(mouseEvent); + } else if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + return filterMouseReleaseEvent(mouseEvent); + } + return QWaylandQuickShellIntegration::eventFilter(object, event); +} + +bool WlShellIntegration::filterMouseMoveEvent(QMouseEvent *event) { if (grabberState == GrabberState::Resize) { Q_ASSERT(resizeState.seat == m_item->compositor()->seatFor(event)); @@ -317,7 +330,7 @@ bool WlShellIntegration::mouseMoveEvent(QMouseEvent *event) return false; } -bool WlShellIntegration::mouseReleaseEvent(QMouseEvent *event) +bool WlShellIntegration::filterMouseReleaseEvent(QMouseEvent *event) { Q_UNUSED(event); if (grabberState != GrabberState::Default) { diff --git a/src/compositor/extensions/qwaylandwlshellintegration_p.h b/src/compositor/extensions/qwaylandwlshellintegration_p.h index 8af54dfc..eb23a62f 100644 --- a/src/compositor/extensions/qwaylandwlshellintegration_p.h +++ b/src/compositor/extensions/qwaylandwlshellintegration_p.h @@ -65,8 +65,9 @@ class WlShellIntegration : public QWaylandQuickShellIntegration public: WlShellIntegration(QWaylandQuickShellSurfaceItem *item); ~WlShellIntegration() override; - bool mouseMoveEvent(QMouseEvent *event) override; - bool mouseReleaseEvent(QMouseEvent *event) override; + +protected: + bool eventFilter(QObject *object, QEvent *event) override; private Q_SLOTS: void handleStartMove(QWaylandSeat *seat); @@ -130,6 +131,9 @@ private: QPointF normalPosition; QPointF finalPosition; + + bool filterMouseMoveEvent(QMouseEvent *event); + bool filterMouseReleaseEvent(QMouseEvent *event); }; } diff --git a/src/compositor/extensions/qwaylandxdgoutputv1.cpp b/src/compositor/extensions/qwaylandxdgoutputv1.cpp new file mode 100644 index 00000000..2ab26c16 --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgoutputv1.cpp @@ -0,0 +1,595 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QWaylandCompositor> + +#include "qwaylandxdgoutputv1_p.h" +#include "qwaylandoutput_p.h" + +#include <wayland-server.h> + +QT_BEGIN_NAMESPACE + +/*! + * \qmltype XdgOutputManagerV1 + * \inqmlmodule QtWayland.Compositor + * \since 5.14 + * \brief Provides an extension for describing outputs in a desktop oriented fashion + * + * The XdgOutputManagerV1 extension provides a way for a compositor to describe outputs in a way + * that is more in line with the concept of an output on desktop oriented systems. + * + * Some information may not make sense in other applications such as IVI systems. + * + * Typically the global compositor space on a desktop system is made of a + * contiguous or overlapping set of rectangular regions. + * + * XdgOutputManagerV1 corresponds to the Wayland interface, \c zxdg_output_manager_v1. + * + * To provide the functionality of the extension in a compositor, create an instance of the + * XdgOutputManagerV1 component and add it to the list of extensions supported by the compositor, + * and associated each XdgOutputV1 with its WaylandOutput: + * + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 + * + * WaylandCompositor { + * XdgOutputManagerV1 { + * WaylandOutput { + * id: output1 + * + * position: Qt.point(0, 0) + * window: Window {} + * + * XdgOutputV1 { + * name: "WL-1" + * logicalPosition: output1.position + * logicalSize: Qt.size(output1.geometry.width / output1.scaleFactor, + * output1.geometry.height / output1.scaleFactor) + * } + * } + * + * WaylandOutput { + * id: output2 + * + * position: Qt.point(800, 0) + * window: Window {} + * + * XdgOutputV1 { + * name: "WL-2" + * logicalPosition: output2.position + * logicalSize: Qt.size(output2.geometry.width / output2.scaleFactor, + * output2.geometry.height / output2.scaleFactor) + * } + * } + * } + * } + * \endqml + */ + +/*! + * \class QWaylandXdgOutputManagerV1 + * \inmodule QtWaylandCompositor + * \since 5.14 + * \brief Provides an extension for describing outputs in a desktop oriented fashion + * + * The QWaylandXdgOutputManagerV1 extension provides a way for a compositor to describe outputs in a way + * that is more in line with the concept of an output on desktop oriented systems. + * + * Some information may not make sense in other applications such as IVI systems. + * + * QWaylandXdgOutputManagerV1 corresponds to the Wayland interface, \c zxdg_output_manager_v1. + */ + +/*! + * Constructs a QWaylandXdgOutputManagerV1 object. + */ +QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1() + : QWaylandCompositorExtensionTemplate<QWaylandXdgOutputManagerV1>(*new QWaylandXdgOutputManagerV1Private()) +{ +} + +/*! + * Constructs a QWaylandXdgOutputManagerV1 object for the provided \a compositor. + */ +QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandCompositor *compositor) + : QWaylandCompositorExtensionTemplate<QWaylandXdgOutputManagerV1>(compositor, *new QWaylandXdgOutputManagerV1Private()) +{ +} + +// QWaylandXdgOutputManagerV1Private + +/*! + * Initializes the extension. + */ +void QWaylandXdgOutputManagerV1::initialize() +{ + Q_D(QWaylandXdgOutputManagerV1); + + QWaylandCompositorExtensionTemplate::initialize(); + QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer()); + if (!compositor) { + qCWarning(qLcWaylandCompositor) << "Failed to find QWaylandCompositor when initializing QWaylandXdgOutputManagerV1"; + return; + } + d->init(compositor->display(), d->interfaceVersion()); +} + +/*! + * Returns the Wayland interface for QWaylandXdgOutputManagerV1. + */ +const wl_interface *QWaylandXdgOutputManagerV1::interface() +{ + return QWaylandXdgOutputManagerV1Private::interface(); +} + +// QWaylandXdgOutputManagerV1Private + +void QWaylandXdgOutputManagerV1Private::registerXdgOutput(QWaylandOutput *output, QWaylandXdgOutputV1 *xdgOutput) +{ + if (!xdgOutputs.contains(output)) { + xdgOutputs[output] = xdgOutput; + QWaylandOutputPrivate::get(output)->xdgOutput = xdgOutput; + } +} + +void QWaylandXdgOutputManagerV1Private::unregisterXdgOutput(QWaylandOutput *output) +{ + xdgOutputs.remove(output); +} + +void QWaylandXdgOutputManagerV1Private::zxdg_output_manager_v1_get_xdg_output(Resource *resource, + uint32_t id, + wl_resource *outputResource) +{ + Q_Q(QWaylandXdgOutputManagerV1); + + // Verify if the associated output exist + auto *output = QWaylandOutput::fromResource(outputResource); + if (!output) { + qCWarning(qLcWaylandCompositor, + "The client is requesting a QWaylandXdgOutputV1 for a " + "QWaylandOutput that doesn't exist"); + wl_resource_post_error(resource->handle, WL_DISPLAY_ERROR_INVALID_OBJECT, "output not found"); + return; + } + + // Do we have a QWaylandXdgOutputV1 for this output? + if (!xdgOutputs.contains(output)) { + qCWarning(qLcWaylandCompositor, + "The client is requesting a QWaylandXdgOutputV1 that the compositor " + "didn't create before"); + wl_resource_post_error(resource->handle, WL_DISPLAY_ERROR_INVALID_OBJECT, + "compositor didn't create a QWaylandXdgOutputV1 for this zxdg_output_v1 object"); + return; + } + + // Bind QWaylandXdgOutputV1 and initialize + auto *xdgOutput = xdgOutputs[output]; + auto *xdgOutputPrivate = QWaylandXdgOutputV1Private::get(xdgOutput); + Q_ASSERT(xdgOutputPrivate); + xdgOutputPrivate->setManager(q); + xdgOutputPrivate->setOutput(output); + xdgOutputPrivate->add(resource->client(), id, qMin(resource->version(), QWaylandXdgOutputV1Private::interfaceVersion())); +} + +// QWaylandXdgOutputV1 + +QWaylandXdgOutputV1::QWaylandXdgOutputV1() + : QObject(*new QWaylandXdgOutputV1Private) +{ +} + +QWaylandXdgOutputV1::QWaylandXdgOutputV1(QWaylandOutput *output, QWaylandXdgOutputManagerV1 *manager) + : QObject(*new QWaylandXdgOutputV1Private) +{ + Q_D(QWaylandXdgOutputV1); + + // Set members before emitting changed signals so that handlers will + // see both already set and not nullptr, avoiding potential crashes + d->manager = manager; + d->output = output; + + QWaylandXdgOutputManagerV1Private::get(d->manager)->registerXdgOutput(output, this); + + emit managerChanged(); + emit outputChanged(); +} + +QWaylandXdgOutputV1::~QWaylandXdgOutputV1() +{ + Q_D(QWaylandXdgOutputV1); + + if (d->manager) + QWaylandXdgOutputManagerV1Private::get(d->manager)->unregisterXdgOutput(d->output); +} + +/*! + * \qmlproperty XdgOutputManagerV1 QtWaylandCompositor::XdgOutputV1::manager + * \readonly + * + * This property holds the object that manages this XdgOutputV1. + */ +/*! + * \property QWaylandXdgOutputV1::manager + * \readonly + * + * This property holds the object that manages this QWaylandXdgOutputV1. + */ +QWaylandXdgOutputManagerV1 *QWaylandXdgOutputV1::manager() const +{ + Q_D(const QWaylandXdgOutputV1); + return d->manager; +} + +/*! + * \qmlproperty WaylandOutput QtWaylandCompositor::XdgOutputV1::output + * \readonly + * + * This property holds the WaylandOutput associated with this XdgOutputV1. + */ +/*! + * \property QWaylandXdgOutputV1::output + * \readonly + * + * This property holds the QWaylandOutput associated with this QWaylandXdgOutputV1. + */ +QWaylandOutput *QWaylandXdgOutputV1::output() const +{ + Q_D(const QWaylandXdgOutputV1); + return d->output; +} + +/*! + * \qmlproperty string QtWaylandCompositor::XdgOutputV1::name + * + * This property holds the name of this output. + * + * The naming convention is compositor defined, but limited to alphanumeric + * characters and dashes ("-"). Each name is unique and will also remain + * consistent across sessions with the same hardware and software configuration. + * + * Examples of names include "HDMI-A-1", "WL-1", "X11-1" etc... + * However don't assume the name reflects the underlying technology. + * + * Changing this property after initialization doesn't take effect. + */ +/*! + * \property QWaylandXdgOutputV1::name + * + * This property holds the name of this output. + * + * The naming convention is compositor defined, but limited to alphanumeric + * characters and dashes ("-"). Each name is unique and will also remain + * consistent across sessions with the same hardware and software configuration. + * + * Examples of names include "HDMI-A-1", "WL-1", "X11-1" etc... + * However don't assume the name reflects the underlying technology. + * + * Changing this property after initialization doesn't take effect. + */ +QString QWaylandXdgOutputV1::name() const +{ + Q_D(const QWaylandXdgOutputV1); + return d->name; +} + +void QWaylandXdgOutputV1::setName(const QString &name) +{ + Q_D(QWaylandXdgOutputV1); + + if (d->name == name) + return; + + // Can't change after clients bound to xdg-output + if (d->initialized) { + qCWarning(qLcWaylandCompositor, "QWaylandXdgOutputV1::name cannot be changed after initialization"); + return; + } + + d->name = name; + emit nameChanged(); +} + +/*! + * \qmlproperty string QtWaylandCompositor::XdgOutputV1::description + * + * This property holds the description of this output. + * + * No convention is defined for the description. + * + * Changing this property after initialization doesn't take effect. + */ +/*! + * \property QWaylandXdgOutputV1::description + * + * This property holds the description of this output. + * + * No convention is defined for the description. + * + * Changing this property after initialization doesn't take effect. + */ +QString QWaylandXdgOutputV1::description() const +{ + Q_D(const QWaylandXdgOutputV1); + return d->description; +} + +void QWaylandXdgOutputV1::setDescription(const QString &description) +{ + Q_D(QWaylandXdgOutputV1); + + if (d->description == description) + return; + + // Can't change after clients bound to xdg-output + if (d->initialized) { + qCWarning(qLcWaylandCompositor, "QWaylandXdgOutputV1::description cannot be changed after initialization"); + return; + } + + d->description = description; + emit descriptionChanged(); +} + +/*! + * \qmlproperty point QtWaylandCompositor::XdgOutputV1::logicalPosition + * + * This property holds the coordinates of the output within the global compositor space. + * + * The default value is 0,0. + */ +/*! + * \property QWaylandXdgOutputV1::logicalPosition + * + * This property holds the coordinates of the output within the global compositor space. + * + * The default value is 0,0. + */ +QPoint QWaylandXdgOutputV1::logicalPosition() const +{ + Q_D(const QWaylandXdgOutputV1); + return d->logicalPos; +} + +void QWaylandXdgOutputV1::setLogicalPosition(const QPoint &position) +{ + Q_D(QWaylandXdgOutputV1); + + if (d->logicalPos == position) + return; + + d->logicalPos = position; + if (d->initialized) { + d->sendLogicalPosition(position); + d->sendDone(); + } + emit logicalPositionChanged(); + emit logicalGeometryChanged(); +} + +/*! + * \qmlproperty size QtWaylandCompositor::XdgOutputV1::logicalSize + * + * This property holds the size of the output in the global compositor space. + * + * The default value is -1,-1 which is invalid. + * + * Please remember that this is the logical size, not the physical size. + * For example, for a WaylandOutput mode 3840x2160 and a scale factor 2: + * \list + * \li A compositor not scaling the surface buffers, will report a logical size of 3840x2160. + * \li A compositor automatically scaling the surface buffers, will report a logical size of 1920x1080. + * \li A compositor using a fractional scale of 1.5, will report a logical size of 2560x1620. + * \endlist + */ +/*! + * \property QWaylandXdgOutputV1::logicalSize + * + * This property holds the size of the output in the global compositor space. + * + * The default value is -1,-1 which is invalid. + * + * Please remember that this is the logical size, not the physical size. + * For example, for a WaylandOutput mode 3840x2160 and a scale factor 2: + * \list + * \li A compositor not scaling the surface buffers, will report a logical size of 3840x2160. + * \li A compositor automatically scaling the surface buffers, will report a logical size of 1920x1080. + * \li A compositor using a fractional scale of 1.5, will report a logical size of 2560x1620. + * \endlist + */ +QSize QWaylandXdgOutputV1::logicalSize() const +{ + Q_D(const QWaylandXdgOutputV1); + return d->logicalSize; +} + +void QWaylandXdgOutputV1::setLogicalSize(const QSize &size) +{ + Q_D(QWaylandXdgOutputV1); + + if (d->logicalSize == size) + return; + + d->logicalSize = size; + if (d->initialized) { + d->sendLogicalSize(size); + d->sendDone(); + } + emit logicalSizeChanged(); + emit logicalGeometryChanged(); +} + +/*! + * \qmlproperty rect QtWaylandCompositor::XdgOutputV1::logicalGeometry + * \readonly + * + * This property holds the position and size of the output in the global compositor space. + * It's the combination of the logical position and logical size. + * + * \sa XdgOutputV1::logicalPosition + * \sa XdgOutputV1::logicalSize + */ +/*! + * \property QWaylandXdgOutputV1::logicalGeometry + * \readonly + * + * This property holds the position and size of the output in the global compositor space. + * It's the combination of the logical position and logical size. + * + * \sa QWaylandXdgOutputV1::logicalPosition + * \sa QWaylandXdgOutputV1::logicalSize + */ +QRect QWaylandXdgOutputV1::logicalGeometry() const +{ + Q_D(const QWaylandXdgOutputV1); + return QRect(d->logicalPos, d->logicalSize); +} + +// QWaylandXdgOutputV1Private + +void QWaylandXdgOutputV1Private::sendLogicalPosition(const QPoint &position) +{ + const auto values = resourceMap().values(); + for (auto *resource : values) + send_logical_position(resource->handle, position.x(), position.y()); + needToSendDone = true; +} + +void QWaylandXdgOutputV1Private::sendLogicalSize(const QSize &size) +{ + const auto values = resourceMap().values(); + for (auto *resource : values) + send_logical_size(resource->handle, size.width(), size.height()); + needToSendDone = true; +} + +void QWaylandXdgOutputV1Private::sendDone() +{ + if (needToSendDone) { + const auto values = resourceMap().values(); + for (auto *resource : values) { + if (resource->version() < 3) + send_done(resource->handle); + } + needToSendDone = false; + } +} + +void QWaylandXdgOutputV1Private::setManager(QWaylandXdgOutputManagerV1 *_manager) +{ + Q_Q(QWaylandXdgOutputV1); + + if (!_manager) { + qCWarning(qLcWaylandCompositor, + "Cannot associate a null QWaylandXdgOutputManagerV1 to QWaylandXdgOutputV1 %p", this); + return; + } + + if (manager == _manager) + return; + + if (manager) { + qCWarning(qLcWaylandCompositor, + "Cannot associate a different QWaylandXdgOutputManagerV1 to QWaylandXdgOutputV1 %p " + "after initialization", this); + return; + } + + manager = _manager; + emit q->managerChanged(); +} + +void QWaylandXdgOutputV1Private::setOutput(QWaylandOutput *_output) +{ + Q_Q(QWaylandXdgOutputV1); + + if (!_output) { + qCWarning(qLcWaylandCompositor, + "Cannot associate a null QWaylandOutput to QWaylandXdgOutputV1 %p", this); + return; + } + + if (output == _output) + return; + + if (output) { + qCWarning(qLcWaylandCompositor, + "Cannot associate a different QWaylandOutput to QWaylandXdgOutputV1 %p " + "after initialization", this); + return; + } + + // Assign output above manager, to make both values valid in handlers + output = _output; + + if (!manager) { + // Try to find the manager from the output parents + for (auto *p = output->parent(); p != nullptr; p = p->parent()) { + if (auto *m = qobject_cast<QWaylandXdgOutputManagerV1 *>(p)) { + manager = m; + emit q->managerChanged(); + break; + } + } + } + + emit q->outputChanged(); + + // Register the output + if (manager) + QWaylandXdgOutputManagerV1Private::get(manager)->registerXdgOutput(output, q); +} + +void QWaylandXdgOutputV1Private::zxdg_output_v1_bind_resource(Resource *resource) +{ + send_logical_position(resource->handle, logicalPos.x(), logicalPos.y()); + send_logical_size(resource->handle, logicalSize.width(), logicalSize.height()); + if (resource->version() >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) + send_name(resource->handle, name); + if (resource->version() >= ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION) + send_description(resource->handle, description); + send_done(resource->handle); + + initialized = true; +} + +void QWaylandXdgOutputV1Private::zxdg_output_v1_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandxdgoutputv1.h b/src/compositor/extensions/qwaylandxdgoutputv1.h new file mode 100644 index 00000000..957ac3be --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgoutputv1.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDXDGOUTPUTV1_H +#define QWAYLANDXDGOUTPUTV1_H + +#include <QRect> +#include <QtWaylandCompositor/QWaylandCompositorExtension> +#include <QtWaylandCompositor/qwaylandquickchildren.h> + +QT_BEGIN_NAMESPACE + +class QWaylandOutput; + +class QWaylandXdgOutputManagerV1Private; +class QWaylandXdgOutputV1Private; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgOutputManagerV1 + : public QWaylandCompositorExtensionTemplate<QWaylandXdgOutputManagerV1> +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandXdgOutputManagerV1) +public: + explicit QWaylandXdgOutputManagerV1(); + QWaylandXdgOutputManagerV1(QWaylandCompositor *compositor); + + void initialize() override; + + static const wl_interface *interface(); +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgOutputV1 : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandXdgOutputV1) + Q_WAYLAND_COMPOSITOR_DECLARE_QUICK_CHILDREN(QWaylandXdgOutputV1) + Q_PROPERTY(QWaylandXdgOutputManagerV1 *manager READ manager NOTIFY managerChanged) + Q_PROPERTY(QWaylandOutput *output READ output NOTIFY outputChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged) + Q_PROPERTY(QPoint logicalPosition READ logicalPosition WRITE setLogicalPosition NOTIFY logicalPositionChanged) + Q_PROPERTY(QSize logicalSize READ logicalSize WRITE setLogicalSize NOTIFY logicalSizeChanged) + Q_PROPERTY(QRect logicalGeometry READ logicalGeometry NOTIFY logicalGeometryChanged) +public: + QWaylandXdgOutputV1(); + QWaylandXdgOutputV1(QWaylandOutput *output, QWaylandXdgOutputManagerV1 *manager); + ~QWaylandXdgOutputV1() override; + + QWaylandXdgOutputManagerV1 *manager() const; + QWaylandOutput *output() const; + + QString name() const; + void setName(const QString &name); + + QString description() const; + void setDescription(const QString &name); + + QPoint logicalPosition() const; + void setLogicalPosition(const QPoint &position); + + QSize logicalSize() const; + void setLogicalSize(const QSize &size); + + QRect logicalGeometry() const; + +Q_SIGNALS: + void managerChanged(); + void outputChanged(); + void logicalPositionChanged(); + void logicalSizeChanged(); + void logicalGeometryChanged(); + void nameChanged(); + void descriptionChanged(); +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDXDGOUTPUTV1_H diff --git a/src/compositor/extensions/qwaylandxdgoutputv1_p.h b/src/compositor/extensions/qwaylandxdgoutputv1_p.h new file mode 100644 index 00000000..2e8a6fff --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgoutputv1_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDXDGOUTPUTV1_P_H +#define QWAYLANDXDGOUTPUTV1_P_H + +#include <QWaylandOutput> +#include <QWaylandXdgOutputV1> +#include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h> +#include <QtWaylandCompositor/private/qwayland-server-xdg-output-unstable-v1.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. +// + +QT_BEGIN_NAMESPACE + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgOutputManagerV1Private + : public QWaylandCompositorExtensionPrivate + , public QtWaylandServer::zxdg_output_manager_v1 +{ + Q_DECLARE_PUBLIC(QWaylandXdgOutputManagerV1) +public: + explicit QWaylandXdgOutputManagerV1Private() = default; + + void registerXdgOutput(QWaylandOutput *output, QWaylandXdgOutputV1 *xdgOutput); + void unregisterXdgOutput(QWaylandOutput *output); + + static QWaylandXdgOutputManagerV1Private *get(QWaylandXdgOutputManagerV1 *manager) { return manager ? manager->d_func() : nullptr; } + +protected: + void zxdg_output_manager_v1_get_xdg_output(Resource *resource, uint32_t id, + wl_resource *outputResource) override; + +private: + QHash<QWaylandOutput *, QWaylandXdgOutputV1 *> xdgOutputs; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgOutputV1Private + : public QObjectPrivate + , public QtWaylandServer::zxdg_output_v1 +{ + Q_DECLARE_PUBLIC(QWaylandXdgOutputV1) +public: + explicit QWaylandXdgOutputV1Private() = default; + + void sendLogicalPosition(const QPoint &position); + void sendLogicalSize(const QSize &size); + void sendDone(); + + void setManager(QWaylandXdgOutputManagerV1 *manager); + void setOutput(QWaylandOutput *output); + + static QWaylandXdgOutputV1Private *get(QWaylandXdgOutputV1 *xdgOutput) { return xdgOutput ? xdgOutput->d_func() : nullptr; } + + bool initialized = false; + QWaylandOutput *output = nullptr; + QWaylandXdgOutputManagerV1 *manager = nullptr; + QPoint logicalPos; + QSize logicalSize; + QString name; + QString description; + bool needToSendDone = false; + +protected: + void zxdg_output_v1_bind_resource(Resource *resource) override; + void zxdg_output_v1_destroy(Resource *resource) override; +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDXDGOUTPUTV1_P_H diff --git a/src/compositor/extensions/qwaylandxdgshell.cpp b/src/compositor/extensions/qwaylandxdgshell.cpp index 710385c0..1b8a3c2e 100644 --- a/src/compositor/extensions/qwaylandxdgshell.cpp +++ b/src/compositor/extensions/qwaylandxdgshell.cpp @@ -1279,7 +1279,9 @@ QWaylandXdgToplevel *QWaylandXdgToplevel::fromResource(wl_resource *resource) QList<int> QWaylandXdgToplevel::statesAsInts() const { QList<int> list; - Q_FOREACH (uint state, states()) { + const auto s = states(); + list.reserve(s.size()); + for (auto state : s) { list << static_cast<int>(state); } return list; @@ -1871,6 +1873,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 c7834ab9..2e3e2818 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/qwaylandxdgshellintegration.cpp b/src/compositor/extensions/qwaylandxdgshellintegration.cpp index 3de52944..336ede3c 100644 --- a/src/compositor/extensions/qwaylandxdgshellintegration.cpp +++ b/src/compositor/extensions/qwaylandxdgshellintegration.cpp @@ -77,7 +77,19 @@ XdgToplevelIntegration::XdgToplevelIntegration(QWaylandQuickShellSurfaceItem *it connect(m_toplevel, &QObject::destroyed, this, &XdgToplevelIntegration::handleToplevelDestroyed); } -bool XdgToplevelIntegration::mouseMoveEvent(QMouseEvent *event) +bool XdgToplevelIntegration::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::MouseMove) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + return filterMouseMoveEvent(mouseEvent); + } else if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + return filterMouseReleaseEvent(mouseEvent); + } + return QWaylandQuickShellIntegration::eventFilter(object, event); +} + +bool XdgToplevelIntegration::filterMouseMoveEvent(QMouseEvent *event) { if (grabberState == GrabberState::Resize) { Q_ASSERT(resizeState.seat == m_item->compositor()->seatFor(event)); @@ -105,7 +117,7 @@ bool XdgToplevelIntegration::mouseMoveEvent(QMouseEvent *event) return false; } -bool XdgToplevelIntegration::mouseReleaseEvent(QMouseEvent *event) +bool XdgToplevelIntegration::filterMouseReleaseEvent(QMouseEvent *event) { Q_UNUSED(event); diff --git a/src/compositor/extensions/qwaylandxdgshellintegration_p.h b/src/compositor/extensions/qwaylandxdgshellintegration_p.h index 34e3873d..cd6bad57 100644 --- a/src/compositor/extensions/qwaylandxdgshellintegration_p.h +++ b/src/compositor/extensions/qwaylandxdgshellintegration_p.h @@ -63,8 +63,9 @@ class XdgToplevelIntegration : public QWaylandQuickShellIntegration Q_OBJECT public: XdgToplevelIntegration(QWaylandQuickShellSurfaceItem *item); - bool mouseMoveEvent(QMouseEvent *event) override; - bool mouseReleaseEvent(QMouseEvent *event) override; + +protected: + bool eventFilter(QObject *object, QEvent *event) override; private Q_SLOTS: void handleStartMove(QWaylandSeat *seat); @@ -120,6 +121,9 @@ private: // will be hooked to geometry-changed or available- // geometry-changed. } nonwindowedState; + + bool filterMouseMoveEvent(QMouseEvent *event); + bool filterMouseReleaseEvent(QMouseEvent *event); }; class XdgPopupIntegration : public QWaylandQuickShellIntegration diff --git a/src/compositor/extensions/qwaylandxdgshellv5.cpp b/src/compositor/extensions/qwaylandxdgshellv5.cpp index ffd1ef90..e38e68eb 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; } @@ -414,7 +414,7 @@ void QWaylandXdgSurfaceV5Private::xdg_surface_ack_configure(Resource *resource, break; } - QVector<uint> changedStates; + std::vector<uint> changedStates; std::set_symmetric_difference( m_lastAckedConfigure.states.begin(), m_lastAckedConfigure.states.end(), config.states.begin(), config.states.end(), @@ -423,7 +423,7 @@ void QWaylandXdgSurfaceV5Private::xdg_surface_ack_configure(Resource *resource, m_lastAckedConfigure = config; if (!changedStates.empty()) { - Q_FOREACH (uint state, changedStates) { + for (uint state : changedStates) { switch (state) { case QWaylandXdgSurfaceV5::State::MaximizedState: emit q->maximizedChanged(); @@ -580,7 +580,7 @@ void QWaylandXdgShellV5::initialize() QWaylandClient *QWaylandXdgShellV5::popupClient() const { Q_D(const QWaylandXdgShellV5); - Q_FOREACH (QWaylandXdgPopupV5 *popup, d->m_xdgPopups) { + for (QWaylandXdgPopupV5 *popup : d->m_xdgPopups) { if (popup->surface()->hasContent()) return popup->surface()->client(); } @@ -627,15 +627,19 @@ uint QWaylandXdgShellV5::ping(QWaylandClient *client) return serial; } +// ### remove once QMap has rbegin()/rend() +template <typename Iterator> +std::reverse_iterator<Iterator> make_reverse(Iterator it) +{ + return std::reverse_iterator<Iterator>(std::move(it)); +} + void QWaylandXdgShellV5::closeAllPopups() { Q_D(QWaylandXdgShellV5); - Q_FOREACH (struct wl_client *client, d->m_xdgPopups.keys()) { - QList<QWaylandXdgPopupV5 *> popups = d->m_xdgPopups.values(client); - std::reverse(popups.begin(), popups.end()); - Q_FOREACH (QWaylandXdgPopupV5 *currentTopmostPopup, popups) { - currentTopmostPopup->sendPopupDone(); - } + // Close pop-ups from top-most to bottom-most, lest we get protocol errors: + for (auto rit = make_reverse(d->m_xdgPopups.end()), rend = make_reverse(d->m_xdgPopups.begin()); rit != rend; ++rit) { + (*rit)->sendPopupDone(); } } @@ -987,7 +991,9 @@ void QWaylandXdgSurfaceV5::initialize() QList<int> QWaylandXdgSurfaceV5::statesAsInts() const { QList<int> list; - Q_FOREACH (uint state, states()) { + const auto s = states(); + list.reserve(s.size()); + for (auto state : s) { list << static_cast<int>(state); } return list; @@ -1238,7 +1244,8 @@ uint QWaylandXdgSurfaceV5::sendConfigure(const QSize &size, const QVector<uint> uint QWaylandXdgSurfaceV5::sendConfigure(const QSize &size, const QVector<QWaylandXdgSurfaceV5::State> &states) { QVector<uint> asUints; - Q_FOREACH (QWaylandXdgSurfaceV5::State state, states) { + asUints.reserve(states.size()); + for (QWaylandXdgSurfaceV5::State state : states) { asUints << state; } return sendConfigure(size, asUints); diff --git a/src/compositor/extensions/qwaylandxdgshellv5_p.h b/src/compositor/extensions/qwaylandxdgshellv5_p.h index 8f5af746..d10359e6 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/qwaylandxdgshellv5integration.cpp b/src/compositor/extensions/qwaylandxdgshellv5integration.cpp index 1d63632a..4907a061 100644 --- a/src/compositor/extensions/qwaylandxdgshellv5integration.cpp +++ b/src/compositor/extensions/qwaylandxdgshellv5integration.cpp @@ -82,7 +82,19 @@ XdgShellV5Integration::~XdgShellV5Integration() m_item->setSurface(nullptr); } -bool XdgShellV5Integration::mouseMoveEvent(QMouseEvent *event) +bool XdgShellV5Integration::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::MouseMove) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + return filterMouseMoveEvent(mouseEvent); + } else if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + return filterMouseReleaseEvent(mouseEvent); + } + return QWaylandQuickShellIntegration::eventFilter(object, event); +} + +bool XdgShellV5Integration::filterMouseMoveEvent(QMouseEvent *event) { if (grabberState == GrabberState::Resize) { Q_ASSERT(resizeState.seat == m_item->compositor()->seatFor(event)); @@ -110,7 +122,7 @@ bool XdgShellV5Integration::mouseMoveEvent(QMouseEvent *event) return false; } -bool XdgShellV5Integration::mouseReleaseEvent(QMouseEvent *event) +bool XdgShellV5Integration::filterMouseReleaseEvent(QMouseEvent *event) { Q_UNUSED(event); diff --git a/src/compositor/extensions/qwaylandxdgshellv5integration_p.h b/src/compositor/extensions/qwaylandxdgshellv5integration_p.h index 5d0e1e14..99983c86 100644 --- a/src/compositor/extensions/qwaylandxdgshellv5integration_p.h +++ b/src/compositor/extensions/qwaylandxdgshellv5integration_p.h @@ -64,8 +64,9 @@ class XdgShellV5Integration : public QWaylandQuickShellIntegration public: XdgShellV5Integration(QWaylandQuickShellSurfaceItem *item); ~XdgShellV5Integration() override; - bool mouseMoveEvent(QMouseEvent *event) override; - bool mouseReleaseEvent(QMouseEvent *event) override; + +protected: + bool eventFilter(QObject *object, QEvent *event) override; private Q_SLOTS: void handleStartMove(QWaylandSeat *seat); @@ -108,6 +109,9 @@ private: QSize initialWindowSize; QPointF initialPosition; } maximizeState; + + bool filterMouseMoveEvent(QMouseEvent *event); + bool filterMouseReleaseEvent(QMouseEvent *event); }; class XdgPopupV5Integration : public QWaylandQuickShellIntegration diff --git a/src/compositor/extensions/qwaylandxdgshellv6.cpp b/src/compositor/extensions/qwaylandxdgshellv6.cpp index 396506cf..934dccb0 100644 --- a/src/compositor/extensions/qwaylandxdgshellv6.cpp +++ b/src/compositor/extensions/qwaylandxdgshellv6.cpp @@ -1214,7 +1214,9 @@ QWaylandSurfaceRole *QWaylandXdgToplevelV6::role() QList<int> QWaylandXdgToplevelV6::statesAsInts() const { QList<int> list; - Q_FOREACH (uint state, states()) { + const auto s = states(); + list.reserve(s.size()); + for (auto state : s) { list << static_cast<int>(state); } return list; @@ -1802,6 +1804,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 710482ac..71f82521 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/qwaylandxdgshellv6integration.cpp b/src/compositor/extensions/qwaylandxdgshellv6integration.cpp index 66dbc684..e424af19 100644 --- a/src/compositor/extensions/qwaylandxdgshellv6integration.cpp +++ b/src/compositor/extensions/qwaylandxdgshellv6integration.cpp @@ -77,7 +77,19 @@ XdgToplevelV6Integration::XdgToplevelV6Integration(QWaylandQuickShellSurfaceItem connect(m_toplevel, &QObject::destroyed, this, &XdgToplevelV6Integration::handleToplevelDestroyed); } -bool XdgToplevelV6Integration::mouseMoveEvent(QMouseEvent *event) +bool XdgToplevelV6Integration::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::MouseMove) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + return filterMouseMoveEvent(mouseEvent); + } else if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + return filterMouseReleaseEvent(mouseEvent); + } + return QWaylandQuickShellIntegration::eventFilter(object, event); +} + +bool XdgToplevelV6Integration::filterMouseMoveEvent(QMouseEvent *event) { if (grabberState == GrabberState::Resize) { Q_ASSERT(resizeState.seat == m_item->compositor()->seatFor(event)); @@ -105,7 +117,7 @@ bool XdgToplevelV6Integration::mouseMoveEvent(QMouseEvent *event) return false; } -bool XdgToplevelV6Integration::mouseReleaseEvent(QMouseEvent *event) +bool XdgToplevelV6Integration::filterMouseReleaseEvent(QMouseEvent *event) { Q_UNUSED(event); diff --git a/src/compositor/extensions/qwaylandxdgshellv6integration_p.h b/src/compositor/extensions/qwaylandxdgshellv6integration_p.h index 049b901c..9df2885f 100644 --- a/src/compositor/extensions/qwaylandxdgshellv6integration_p.h +++ b/src/compositor/extensions/qwaylandxdgshellv6integration_p.h @@ -63,8 +63,9 @@ class XdgToplevelV6Integration : public QWaylandQuickShellIntegration Q_OBJECT public: XdgToplevelV6Integration(QWaylandQuickShellSurfaceItem *item); - bool mouseMoveEvent(QMouseEvent *event) override; - bool mouseReleaseEvent(QMouseEvent *event) override; + +protected: + bool eventFilter(QObject *object, QEvent *event) override; private Q_SLOTS: void handleStartMove(QWaylandSeat *seat); @@ -120,6 +121,9 @@ private: // will be hooked to geometry-changed or available- // geometry-changed. } nonwindowedState; + + bool filterMouseMoveEvent(QMouseEvent *event); + bool filterMouseReleaseEvent(QMouseEvent *event); }; class XdgPopupV6Integration : public QWaylandQuickShellIntegration diff --git a/src/compositor/extensions/qwltexturesharingextension.cpp b/src/compositor/extensions/qwltexturesharingextension.cpp index 251c5fec..cf9fd011 100644 --- a/src/compositor/extensions/qwltexturesharingextension.cpp +++ b/src/compositor/extensions/qwltexturesharingextension.cpp @@ -177,7 +177,7 @@ public: return new SharedTextureFactory(m_buffer); } // qDebug() << "Shared buffer NOT found for" << m_id; - m_errorString = QLatin1Literal("Shared buffer not found"); + m_errorString = QLatin1String("Shared buffer not found"); return nullptr; } @@ -282,7 +282,7 @@ void QWaylandTextureSharingExtension::initialize() setImageSearchPath(image_search_path); if (m_image_dirs.isEmpty()) - m_image_dirs << QLatin1Literal(":/") << QLatin1Literal("./"); + m_image_dirs << QLatin1String(":/") << QLatin1String("./"); auto suffixes = QTextureFileReader::supportedFileFormats(); suffixes.append(QImageReader::supportedImageFormats()); @@ -295,7 +295,7 @@ void QWaylandTextureSharingExtension::initialize() if (ctx) { QQmlEngine *engine = ctx->engine(); if (engine) { - auto *provider = static_cast<QWaylandSharedTextureProvider*>(engine->imageProvider(QLatin1Literal("wlshared"))); + auto *provider = static_cast<QWaylandSharedTextureProvider*>(engine->imageProvider(QLatin1String("wlshared"))); if (provider) provider->setExtensionReady(this); } @@ -308,7 +308,7 @@ QString QWaylandTextureSharingExtension::getExistingFilePath(const QString &key) // paths containing '../'. We handle that here, at the price of also blocking directory // names ending with two or more dots. - if (key.contains(QLatin1Literal("../"))) + if (key.contains(QLatin1String("../"))) return QString(); for (auto dir : m_image_dirs) { diff --git a/src/compositor/global/qwaylandcompositorextension.cpp b/src/compositor/global/qwaylandcompositorextension.cpp index 91298539..36daebce 100644 --- a/src/compositor/global/qwaylandcompositorextension.cpp +++ b/src/compositor/global/qwaylandcompositorextension.cpp @@ -146,7 +146,7 @@ QWaylandObject::QWaylandObject(QObjectPrivate &d, QObject *parent) QWaylandObject::~QWaylandObject() { - foreach (QWaylandCompositorExtension *extension, extension_vector) + for (QWaylandCompositorExtension *extension : qAsConst(extension_vector)) QWaylandCompositorExtensionPrivate::get(extension)->extension_container = nullptr; } diff --git a/src/compositor/hardware_integration/qwlserverbufferintegration_p.h b/src/compositor/hardware_integration/qwlserverbufferintegration_p.h index b8d9fa99..e8f774e6 100644 --- a/src/compositor/hardware_integration/qwlserverbufferintegration_p.h +++ b/src/compositor/hardware_integration/qwlserverbufferintegration_p.h @@ -55,7 +55,8 @@ #include <QtCore/QSize> #include <QtGui/qopengl.h> -#include <QtWaylandCompositor/private/qtwaylandcompositorglobal_p.h> +#include <QtWaylandCompositor/qtwaylandcompositorglobal.h> + struct wl_client; struct wl_resource; @@ -85,9 +86,8 @@ public: virtual bool bufferInUse() { return true; } virtual QOpenGLTexture *toOpenGlTexture() = 0; -#if QT_CONFIG(wayland_compositor_texture_sharing_experimental) virtual void releaseOpenGlTexture() {} -#endif + virtual bool isYInverted() const; QSize size() const; @@ -107,7 +107,6 @@ public: virtual bool supportsFormat(ServerBuffer::Format format) const = 0; virtual ServerBuffer *createServerBufferFromImage(const QImage &qimage, ServerBuffer::Format format) = 0; -#if QT_CONFIG(wayland_compositor_texture_sharing_experimental) virtual ServerBuffer *createServerBufferFromData(const QByteArray &data, const QSize &size, uint glInternalFormat) { Q_UNUSED(data); @@ -115,7 +114,6 @@ public: Q_UNUSED(glInternalFormat); return nullptr; } -#endif }; } diff --git a/src/compositor/wayland_wrapper/qwlclientbuffer.cpp b/src/compositor/wayland_wrapper/qwlclientbuffer.cpp index cb1ee3da..d3f8df9f 100644 --- a/src/compositor/wayland_wrapper/qwlclientbuffer.cpp +++ b/src/compositor/wayland_wrapper/qwlclientbuffer.cpp @@ -81,7 +81,7 @@ void ClientBuffer::setDestroyed() m_committed = false; m_buffer = nullptr; - if (!m_refCount) + if (!m_refCount.loadAcquire()) delete this; } @@ -133,17 +133,19 @@ QWaylandSurface::Origin SharedMemoryBuffer::origin() const return QWaylandSurface::OriginTopLeft; } - -// TODO: support different color formats, and try to avoid QImage::convertToFormat() - QImage SharedMemoryBuffer::image() const { if (wl_shm_buffer *shmBuffer = wl_shm_buffer_get(m_buffer)) { int width = wl_shm_buffer_get_width(shmBuffer); int height = wl_shm_buffer_get_height(shmBuffer); int bytesPerLine = wl_shm_buffer_get_stride(shmBuffer); + + // TODO: try to avoid QImage::convertToFormat() + wl_shm_format shmFormat = wl_shm_format(wl_shm_buffer_get_format(shmBuffer)); + QImage::Format format = QWaylandSharedMemoryFormatHelper::fromWaylandShmFormat(shmFormat); + uchar *data = static_cast<uchar *>(wl_shm_buffer_get_data(shmBuffer)); - return QImage(data, width, height, bytesPerLine, QImage::Format_ARGB32_Premultiplied); + return QImage(data, width, height, bytesPerLine, format); } return QImage(); diff --git a/src/compositor/wayland_wrapper/qwldatadevicemanager.cpp b/src/compositor/wayland_wrapper/qwldatadevicemanager.cpp index e30bc84f..624a24b0 100644 --- a/src/compositor/wayland_wrapper/qwldatadevicemanager.cpp +++ b/src/compositor/wayland_wrapper/qwldatadevicemanager.cpp @@ -186,12 +186,12 @@ struct wl_display *DataDeviceManager::display() const void DataDeviceManager::overrideSelection(const QMimeData &mimeData) { - QStringList formats = mimeData.formats(); + const QStringList formats = mimeData.formats(); if (formats.isEmpty()) return; m_retainedData.clear(); - foreach (const QString &format, formats) + for (const QString &format : formats) m_retainedData.setData(format, mimeData.data(format)); QWaylandCompositorPrivate::get(m_compositor)->feedRetainedSelectionData(&m_retainedData); @@ -217,7 +217,8 @@ bool DataDeviceManager::offerFromCompositorToClient(wl_resource *clientDataDevic wl_resource_create(client, &wl_data_offer_interface, -1, 0); wl_resource_set_implementation(selectionOffer, &compositor_offer_interface, this, nullptr); wl_data_device_send_data_offer(clientDataDeviceResource, selectionOffer); - foreach (const QString &format, m_retainedData.formats()) { + const auto formats = m_retainedData.formats(); + for (const QString &format : formats) { QByteArray ba = format.toLatin1(); wl_data_offer_send_offer(selectionOffer, ba.constData()); } diff --git a/src/compositor/wayland_wrapper/qwldataoffer.cpp b/src/compositor/wayland_wrapper/qwldataoffer.cpp index 9fea664a..ddc5545e 100644 --- a/src/compositor/wayland_wrapper/qwldataoffer.cpp +++ b/src/compositor/wayland_wrapper/qwldataoffer.cpp @@ -55,7 +55,8 @@ DataOffer::DataOffer(DataSource *dataSource, QtWaylandServer::wl_data_device::Re { // FIXME: connect to dataSource and reset m_dataSource on destroy target->data_device_object->send_data_offer(target->handle, resource()->handle); - Q_FOREACH (const QString &mimeType, dataSource->mimeTypes()) { + const auto mimeTypes = dataSource->mimeTypes(); + for (const QString &mimeType : mimeTypes) { send_offer(mimeType); } } diff --git a/src/extensions/README.md b/src/extensions/README.md index f75e0f23..8024ffa8 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/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp index 7e1d5966..4b2be50e 100644 --- a/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp +++ b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp @@ -37,15 +37,12 @@ ** ****************************************************************************/ -#define GL_GLEXT_PROTOTYPES -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> - #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> diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp index 24dadff4..030422c5 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 bc1f74af..5bd2760d 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/vulkanserverbufferintegration.cpp b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp index 7f9f8a15..df197ca2 100644 --- a/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp +++ b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp @@ -37,10 +37,6 @@ ** ****************************************************************************/ -#define GL_GLEXT_PROTOTYPES -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> - #include "vulkanserverbufferintegration.h" #include "vulkanwrapper.h" @@ -48,6 +44,7 @@ #include <QtGui/QOpenGLContext> #include <QtGui/QOpenGLTexture> #include <QtGui/QOffscreenSurface> +#include <QtGui/qopengl.h> #include <unistd.h> #include <fcntl.h> diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp index fe66adf5..771e1015 100644 --- a/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp +++ b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp @@ -40,14 +40,13 @@ // NOTE: Some of the code below is adapted from the public domain code at https://vulkan-tutorial.com/ #define GL_GLEXT_PROTOTYPES -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include <QtVulkanSupport/private/qvkconvenience_p.h> #include "vulkanwrapper.h" #include <QImage> #include <QOpenGLContext> +#include <QtGui/qopengl.h> +#include <QtVulkanSupport/private/qvkconvenience_p.h> #include <set> @@ -707,7 +706,6 @@ VulkanImageWrapper *VulkanWrapper::createTextureImage(const QImage &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; diff --git a/src/imports/compositor/qwaylandquickcompositorplugin.cpp b/src/imports/compositor/qwaylandquickcompositorplugin.cpp index c7553d93..223ca0bc 100644 --- a/src/imports/compositor/qwaylandquickcompositorplugin.cpp +++ b/src/imports/compositor/qwaylandquickcompositorplugin.cpp @@ -66,6 +66,8 @@ #include <QtWaylandCompositor/QWaylandXdgShellV6> #include <QtWaylandCompositor/QWaylandXdgShell> #include <QtWaylandCompositor/QWaylandXdgDecorationManagerV1> +#include <QtWaylandCompositor/QWaylandIdleInhibitManagerV1> +#include <QtWaylandCompositor/QWaylandQuickXdgOutputV1> #include <QtWaylandCompositor/QWaylandIviApplication> #include <QtWaylandCompositor/QWaylandIviSurface> @@ -76,13 +78,17 @@ QT_BEGIN_NAMESPACE Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CONTAINER_CLASS(QWaylandQuickCompositor) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandQtWindowManager) +Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandIdleInhibitManagerV1) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandIviApplication) +#if QT_DEPRECATED_SINCE(5, 13) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandWlScaler) +#endif Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandWlShell) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandXdgShellV5) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandXdgShellV6) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandXdgShell) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandXdgDecorationManagerV1) +Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandXdgOutputManagerV1) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandTextInputManager) class QmlUrlResolver @@ -137,7 +143,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"); @@ -182,7 +190,14 @@ public: qmlRegisterType<QWaylandXdgDecorationManagerV1QuickExtension>(uri, 1, 3, "XdgDecorationManagerV1"); +#if QT_DEPRECATED_SINCE(5, 13) qmlRegisterType<QWaylandWlScalerQuickExtension>(uri, 1, 13, "WlScaler"); +#endif + + qmlRegisterType<QWaylandIdleInhibitManagerV1QuickExtension>(uri, 1, 14, "IdleInhibitManagerV1"); + + qmlRegisterType<QWaylandXdgOutputManagerV1QuickExtension>(uri, 1, 14, "XdgOutputManagerV1"); + qmlRegisterType<QWaylandQuickXdgOutputV1>(uri, 1, 14, "XdgOutputV1"); } }; //![class decl] diff --git a/src/imports/imports.pro b/src/imports/imports.pro index c8394f0c..7452a928 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -1,8 +1,12 @@ TEMPLATE = subdirs -qtHaveModule(quick): { +qtHaveModule(quick):qtHaveModule(waylandcompositor) { SUBDIRS += \ - compositor \ - texture-sharing \ - texture-sharing-extension + compositor + + qtConfig(opengl):qtHaveModule(waylandclient) { + SUBDIRS += \ + texture-sharing \ + texture-sharing-extension + } } diff --git a/src/imports/texture-sharing-extension/texture-sharing-extension.pro b/src/imports/texture-sharing-extension/texture-sharing-extension.pro index 577ab58e..68a8cf75 100644 --- a/src/imports/texture-sharing-extension/texture-sharing-extension.pro +++ b/src/imports/texture-sharing-extension/texture-sharing-extension.pro @@ -8,6 +8,4 @@ SOURCES += \ QT += quick-private qml gui-private core-private waylandcompositor waylandcompositor-private -requires(qtConfig(wayland-compositor-texture-sharing-experimental)) - load(qml_plugin) diff --git a/src/imports/texture-sharing/texture-sharing.pro b/src/imports/texture-sharing/texture-sharing.pro index eb5c0a9a..bec769ec 100644 --- a/src/imports/texture-sharing/texture-sharing.pro +++ b/src/imports/texture-sharing/texture-sharing.pro @@ -15,8 +15,6 @@ SOURCES += \ QT += quick-private qml gui-private core-private waylandclient waylandclient-private CONFIG += wayland-scanner -requires(qtConfig(wayland-client-texture-sharing-experimental)) - WAYLANDCLIENTSOURCES += ../../extensions/qt-texture-sharing-unstable-v1.xml diff --git a/src/plugins/hardwareintegration/hardwareintegration.pro b/src/plugins/hardwareintegration/hardwareintegration.pro index cb7a4b26..12658adf 100644 --- a/src/plugins/hardwareintegration/hardwareintegration.pro +++ b/src/plugins/hardwareintegration/hardwareintegration.pro @@ -1,4 +1,4 @@ TEMPLATE=subdirs -SUBDIRS += client +qtHaveModule(waylandclient): SUBDIRS += client qtHaveModule(waylandcompositor): SUBDIRS += compositor diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 9b66b851..e121d92d 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -1,6 +1,11 @@ TEMPLATE=subdirs + +qtHaveModule(waylandclient) { + SUBDIRS += \ + platforms \ + decorations \ + shellintegration +} + SUBDIRS += \ - platforms \ - hardwareintegration \ - decorations \ - shellintegration + hardwareintegration diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp index 9a829f6e..26f59889 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 efb61dc9..ea725ac3 100644 --- a/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.cpp +++ b/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.cpp @@ -48,6 +48,8 @@ #include "qwaylandivisurface_p.h" +#include <mutex> + #include <unistd.h> QT_BEGIN_NAMESPACE @@ -95,7 +97,7 @@ uint32_t QWaylandIviShellIntegration::getNextUniqueSurfaceId() { const uint32_t PID_MAX_EXPONENTIATION = 22; // 22 bit shift operation const uint32_t ID_LIMIT = 1 << (32 - PID_MAX_EXPONENTIATION); // 10 bit is unique id - QMutexLocker locker(&m_mutex); + const std::lock_guard<QRecursiveMutex> locker(m_mutex); if (m_lastSurfaceId == 0) { QByteArray env = qgetenv("QT_IVI_SURFACE_ID"); @@ -132,7 +134,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/ivi-shell/qwaylandivishellintegration.h b/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.h index fc16d2f6..13282e43 100644 --- a/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.h +++ b/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.h @@ -71,7 +71,7 @@ private: uint32_t m_lastSurfaceId = 0; uint32_t m_surfaceNumber = 0; bool m_useEnvSurfaceId = false; - QMutex m_mutex{QMutex::Recursive}; + QRecursiveMutex m_mutex; }; } diff --git a/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp index 1edb24b3..ab809806 100644 --- a/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp +++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp @@ -49,7 +49,8 @@ namespace QtWaylandClient { bool QWaylandWlShellIntegration::initialize(QWaylandDisplay *display) { - Q_FOREACH (QWaylandDisplay::RegistryGlobal global, display->globals()) { + const auto globals = display->globals(); + for (QWaylandDisplay::RegistryGlobal global : globals) { if (global.interface == QLatin1String("wl_shell")) { m_wlShell = new QtWayland::wl_shell(display->wl_registry(), global.id, 1); break; @@ -70,7 +71,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 4506c312..48e14c75 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 3eda43d7..7e242c4a 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/qwaylandxdgshellv5integration.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp index 410f2700..4e25949f 100644 --- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp @@ -51,7 +51,8 @@ namespace QtWaylandClient { bool QWaylandXdgShellV5Integration::initialize(QWaylandDisplay *display) { - Q_FOREACH (QWaylandDisplay::RegistryGlobal global, display->globals()) { + const auto globals = display->globals(); + for (QWaylandDisplay::RegistryGlobal global : globals) { if (global.interface == QLatin1String("xdg_shell")) { m_xdgShell.reset(new QWaylandXdgShellV5(display->wl_registry(), global.id)); break; diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp index e9f64e2e..e8bff919 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 99b478d9..dc3cfdbf 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 026bb56a..bd1f5a21 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 7f3dc5ad..9691b857 100644 --- a/src/qtwaylandscanner/qtwaylandscanner.cpp +++ b/src/qtwaylandscanner/qtwaylandscanner.cpp @@ -40,7 +40,8 @@ #include <QCoreApplication> #include <QFile> #include <QXmlStreamReader> -#include <QtCore/QList> + +#include <vector> class Scanner { @@ -63,7 +64,7 @@ private: struct WaylandEnum { QByteArray name; - QList<WaylandEnumEntry> entries; + std::vector<WaylandEnumEntry> entries; }; struct WaylandArgument { @@ -78,16 +79,16 @@ private: bool request; QByteArray name; QByteArray type; - QList<WaylandArgument> arguments; + std::vector<WaylandArgument> arguments; }; struct WaylandInterface { QByteArray name; int version; - QList<WaylandEnum> enums; - QList<WaylandEvent> events; - QList<WaylandEvent> requests; + std::vector<WaylandEnum> enums; + std::vector<WaylandEvent> events; + std::vector<WaylandEvent> requests; }; bool isServerSide(); @@ -101,11 +102,11 @@ private: Scanner::WaylandInterface readInterface(QXmlStreamReader &xml); QByteArray waylandToCType(const QByteArray &waylandType, const QByteArray &interface); QByteArray waylandToQtType(const QByteArray &waylandType, const QByteArray &interface, bool cStyleArray); - const Scanner::WaylandArgument *newIdArgument(const QList<WaylandArgument> &arguments); + const Scanner::WaylandArgument *newIdArgument(const std::vector<WaylandArgument> &arguments); void printEvent(const WaylandEvent &e, bool omitNames = false, bool withResource = false); void printEventHandlerSignature(const WaylandEvent &e, const char *interfaceName, bool deepIndent = true); - void printEnums(const QList<WaylandEnum> &enums); + void printEnums(const std::vector<WaylandEnum> &enums); QByteArray stripInterfaceName(const QByteArray &name); bool ignoreInterface(const QByteArray &name); @@ -189,19 +190,22 @@ bool Scanner::boolValue(const QXmlStreamReader &xml, const char *name) Scanner::WaylandEvent Scanner::readEvent(QXmlStreamReader &xml, bool request) { - WaylandEvent event; - event.request = request; - event.name = byteArrayValue(xml, "name"); - event.type = byteArrayValue(xml, "type"); + WaylandEvent event = { + .request = request, + .name = byteArrayValue(xml, "name"), + .type = byteArrayValue(xml, "type"), + .arguments = {}, + }; while (xml.readNextStartElement()) { if (xml.name() == "arg") { - WaylandArgument argument; - argument.name = byteArrayValue(xml, "name"); - argument.type = byteArrayValue(xml, "type"); - argument.interface = byteArrayValue(xml, "interface"); - argument.summary = byteArrayValue(xml, "summary"); - argument.allowNull = boolValue(xml, "allowNull"); - event.arguments << argument; + WaylandArgument argument = { + .name = byteArrayValue(xml, "name"), + .type = byteArrayValue(xml, "type"), + .interface = byteArrayValue(xml, "interface"), + .summary = byteArrayValue(xml, "summary"), + .allowNull = boolValue(xml, "allowNull"), + }; + event.arguments.push_back(std::move(argument)); } xml.skipCurrentElement(); @@ -211,16 +215,19 @@ Scanner::WaylandEvent Scanner::readEvent(QXmlStreamReader &xml, bool request) Scanner::WaylandEnum Scanner::readEnum(QXmlStreamReader &xml) { - WaylandEnum result; - result.name = byteArrayValue(xml, "name"); + WaylandEnum result = { + .name = byteArrayValue(xml, "name"), + .entries = {}, + }; while (xml.readNextStartElement()) { if (xml.name() == "entry") { - WaylandEnumEntry entry; - entry.name = byteArrayValue(xml, "name"); - entry.value = byteArrayValue(xml, "value"); - entry.summary = byteArrayValue(xml, "summary"); - result.entries << entry; + WaylandEnumEntry entry = { + .name = byteArrayValue(xml, "name"), + .value = byteArrayValue(xml, "value"), + .summary = byteArrayValue(xml, "summary"), + }; + result.entries.push_back(std::move(entry)); } xml.skipCurrentElement(); @@ -231,17 +238,21 @@ Scanner::WaylandEnum Scanner::readEnum(QXmlStreamReader &xml) Scanner::WaylandInterface Scanner::readInterface(QXmlStreamReader &xml) { - WaylandInterface interface; - interface.name = byteArrayValue(xml, "name"); - interface.version = intValue(xml, "version", 1); + WaylandInterface interface = { + .name = byteArrayValue(xml, "name"), + .version = intValue(xml, "version", 1), + .enums = {}, + .events = {}, + .requests = {}, + }; while (xml.readNextStartElement()) { if (xml.name() == "event") - interface.events << readEvent(xml, false); + interface.events.push_back(readEvent(xml, false)); else if (xml.name() == "request") - interface.requests << readEvent(xml, true); + interface.requests.push_back(readEvent(xml, true)); else if (xml.name() == "enum") - interface.enums << readEnum(xml); + interface.enums.push_back(readEnum(xml)); else xml.skipCurrentElement(); } @@ -283,11 +294,11 @@ QByteArray Scanner::waylandToQtType(const QByteArray &waylandType, const QByteAr return waylandToCType(waylandType, interface); } -const Scanner::WaylandArgument *Scanner::newIdArgument(const QList<WaylandArgument> &arguments) +const Scanner::WaylandArgument *Scanner::newIdArgument(const std::vector<WaylandArgument> &arguments) { - for (int i = 0; i < arguments.size(); ++i) { - if (arguments.at(i).type == "new_id") - return &arguments.at(i); + for (const WaylandArgument &a : arguments) { + if (a.type == "new_id") + return &a; } return nullptr; } @@ -305,8 +316,7 @@ void Scanner::printEvent(const WaylandEvent &e, bool omitNames, bool withResourc needsComma = true; } } - for (int i = 0; i < e.arguments.size(); ++i) { - const WaylandArgument &a = e.arguments.at(i); + for (const WaylandArgument &a : e.arguments) { bool isNewId = a.type == "new_id"; if (isNewId && !isServerSide() && (a.interface.isEmpty() != e.request)) continue; @@ -346,9 +356,8 @@ void Scanner::printEventHandlerSignature(const WaylandEvent &e, const char *inte printf(" %svoid *data,\n", indent); printf(" %sstruct ::%s *object", indent, interfaceName); } - for (int i = 0; i < e.arguments.size(); ++i) { + for (const WaylandArgument &a : e.arguments) { printf(",\n"); - const WaylandArgument &a = e.arguments.at(i); bool isNewId = a.type == "new_id"; if (isServerSide() && isNewId) { printf(" %suint32_t %s", indent, a.name.constData()); @@ -360,17 +369,13 @@ void Scanner::printEventHandlerSignature(const WaylandEvent &e, const char *inte printf(")"); } -void Scanner::printEnums(const QList<WaylandEnum> &enums) +void Scanner::printEnums(const std::vector<WaylandEnum> &enums) { - for (int i = 0; i < enums.size(); ++i) { + for (const WaylandEnum &e : enums) { printf("\n"); - const WaylandEnum &e = enums.at(i); printf(" enum %s {\n", e.name.constData()); - for (int i = 0; i < e.entries.size(); ++i) { - const WaylandEnumEntry &entry = e.entries.at(i); - printf(" %s_%s = %s", e.name.constData(), entry.name.constData(), entry.value.constData()); - if (i < e.entries.size() - 1) - printf(","); + for (const WaylandEnumEntry &entry : e.entries) { + printf(" %s_%s = %s,", e.name.constData(), entry.name.constData(), entry.value.constData()); if (!entry.summary.isNull()) printf(" // %s", entry.summary.constData()); printf("\n"); @@ -424,11 +429,11 @@ bool Scanner::process() //QByteArray preProcessorProtocolName = QByteArray(m_protocolName).replace('-', '_').toUpper(); QByteArray preProcessorProtocolName = QByteArray(m_protocolName).toUpper(); - QList<WaylandInterface> interfaces; + std::vector<WaylandInterface> interfaces; while (m_xml->readNextStartElement()) { if (m_xml->name() == "interface") - interfaces << readInterface(*m_xml); + interfaces.push_back(readInterface(*m_xml)); else m_xml->skipCurrentElement(); } @@ -478,12 +483,16 @@ bool Scanner::process() printf("\n"); printf("namespace QtWaylandServer {\n"); - for (int j = 0; j < interfaces.size(); ++j) { - const WaylandInterface &interface = interfaces.at(j); + bool needsNewLine = false; + for (const WaylandInterface &interface : interfaces) { if (ignoreInterface(interface.name)) continue; + if (needsNewLine) + printf("\n"); + needsNewLine = true; + const char *interfaceName = interface.name.constData(); QByteArray stripped = stripInterfaceName(interface.name); @@ -538,11 +547,11 @@ bool Scanner::process() printEnums(interface.enums); - bool hasEvents = !interface.events.isEmpty(); + bool hasEvents = !interface.events.empty(); if (hasEvents) { printf("\n"); - foreach (const WaylandEvent &e, interface.events) { + for (const WaylandEvent &e : interface.events) { printf(" void send_"); printEvent(e); printf(";\n"); @@ -559,11 +568,11 @@ bool Scanner::process() printf(" virtual void %s_bind_resource(Resource *resource);\n", interfaceNameStripped); printf(" virtual void %s_destroy_resource(Resource *resource);\n", interfaceNameStripped); - bool hasRequests = !interface.requests.isEmpty(); + bool hasRequests = !interface.requests.empty(); if (hasRequests) { printf("\n"); - foreach (const WaylandEvent &e, interface.requests) { + for (const WaylandEvent &e : interface.requests) { printf(" virtual void %s_", interfaceNameStripped); printEvent(e); printf(";\n"); @@ -584,8 +593,7 @@ bool Scanner::process() printf(" static const struct ::%s_interface m_%s_interface;\n", interfaceName, interfaceName); printf("\n"); - for (int i = 0; i < interface.requests.size(); ++i) { - const WaylandEvent &e = interface.requests.at(i); + for (const WaylandEvent &e : interface.requests) { printf(" static void "); printEventHandlerSignature(e, interfaceName); @@ -603,9 +611,6 @@ bool Scanner::process() printf(" };\n"); printf(" DisplayDestroyedListener m_displayDestroyedListener;\n"); printf(" };\n"); - - if (j < interfaces.size() - 1) - printf("\n"); } printf("}\n"); @@ -629,8 +634,7 @@ bool Scanner::process() printf("namespace QtWaylandServer {\n"); bool needsNewLine = false; - for (int j = 0; j < interfaces.size(); ++j) { - const WaylandInterface &interface = interfaces.at(j); + for (const WaylandInterface &interface : interfaces) { if (ignoreInterface(interface.name)) continue; @@ -778,7 +782,7 @@ bool Scanner::process() printf(" }\n"); printf("\n"); - bool hasRequests = !interface.requests.isEmpty(); + bool hasRequests = !interface.requests.empty(); QByteArray interfaceMember = hasRequests ? "&m_" + interface.name + "_interface" : QByteArray("nullptr"); @@ -816,17 +820,18 @@ bool Scanner::process() if (hasRequests) { printf("\n"); printf(" const struct ::%s_interface %s::m_%s_interface = {", interfaceName, interfaceName, interfaceName); - for (int i = 0; i < interface.requests.size(); ++i) { - if (i > 0) + bool needsComma = false; + for (const WaylandEvent &e : interface.requests) { + if (needsComma) printf(","); + needsComma = true; printf("\n"); - const WaylandEvent &e = interface.requests.at(i); printf(" %s::handle_%s", interfaceName, e.name.constData()); } printf("\n"); printf(" };\n"); - foreach (const WaylandEvent &e, interface.requests) { + for (const WaylandEvent &e : interface.requests) { printf("\n"); printf(" void %s::%s_", interfaceName, interfaceNameStripped); printEvent(e, true); @@ -836,11 +841,10 @@ bool Scanner::process() } printf("\n"); - for (int i = 0; i < interface.requests.size(); ++i) { + for (const WaylandEvent &e : interface.requests) { printf("\n"); printf(" void %s::", interfaceName); - const WaylandEvent &e = interface.requests.at(i); printEventHandlerSignature(e, interfaceName, false); printf("\n"); @@ -849,9 +853,8 @@ bool Scanner::process() printf(" Resource *r = Resource::fromResource(resource);\n"); printf(" static_cast<%s *>(r->%s_object)->%s_%s(\n", interfaceName, interfaceNameStripped, interfaceNameStripped, e.name.constData()); printf(" r"); - for (int i = 0; i < e.arguments.size(); ++i) { + for (const WaylandArgument &a : e.arguments) { printf(",\n"); - const WaylandArgument &a = e.arguments.at(i); QByteArray cType = waylandToCType(a.type, a.interface); QByteArray qtType = waylandToQtType(a.type, a.interface, e.request); const char *argumentName = a.name.constData(); @@ -865,17 +868,15 @@ bool Scanner::process() } } - for (int i = 0; i < interface.events.size(); ++i) { + for (const WaylandEvent &e : interface.events) { printf("\n"); - const WaylandEvent &e = interface.events.at(i); printf(" void %s::send_", interfaceName); printEvent(e); printf("\n"); printf(" {\n"); printf(" send_%s(\n", e.name.constData()); printf(" m_resource->handle"); - for (int i = 0; i < e.arguments.size(); ++i) { - const WaylandArgument &a = e.arguments.at(i); + for (const WaylandArgument &a : e.arguments) { printf(",\n"); printf(" %s", a.name.constData()); } @@ -888,8 +889,7 @@ bool Scanner::process() printf("\n"); printf(" {\n"); - for (int i = 0; i < e.arguments.size(); ++i) { - const WaylandArgument &a = e.arguments.at(i); + for (const WaylandArgument &a : e.arguments) { if (a.type != "array") continue; QByteArray array = a.name + "_data"; @@ -905,8 +905,7 @@ bool Scanner::process() printf(" %s_send_%s(\n", interfaceName, e.name.constData()); printf(" resource"); - for (int i = 0; i < e.arguments.size(); ++i) { - const WaylandArgument &a = e.arguments.at(i); + for (const WaylandArgument &a : e.arguments) { printf(",\n"); QByteArray cType = waylandToCType(a.type, a.interface); QByteArray qtType = waylandToQtType(a.type, a.interface, e.request); @@ -962,12 +961,17 @@ bool Scanner::process() } printf("\n"); printf("namespace QtWayland {\n"); - for (int j = 0; j < interfaces.size(); ++j) { - const WaylandInterface &interface = interfaces.at(j); + + bool needsNewLine = false; + for (const WaylandInterface &interface : interfaces) { if (ignoreInterface(interface.name)) continue; + if (needsNewLine) + printf("\n"); + needsNewLine = true; + const char *interfaceName = interface.name.constData(); QByteArray stripped = stripInterfaceName(interface.name); @@ -994,9 +998,9 @@ bool Scanner::process() printEnums(interface.enums); - if (!interface.requests.isEmpty()) { + if (!interface.requests.empty()) { printf("\n"); - foreach (const WaylandEvent &e, interface.requests) { + for (const WaylandEvent &e : interface.requests) { const WaylandArgument *new_id = newIdArgument(e.arguments); QByteArray new_id_str = "void "; if (new_id) { @@ -1011,12 +1015,12 @@ bool Scanner::process() } } - bool hasEvents = !interface.events.isEmpty(); + bool hasEvents = !interface.events.empty(); if (hasEvents) { printf("\n"); printf(" protected:\n"); - foreach (const WaylandEvent &e, interface.events) { + for (const WaylandEvent &e : interface.events) { printf(" virtual void %s_", interfaceNameStripped); printEvent(e); printf(";\n"); @@ -1028,8 +1032,7 @@ bool Scanner::process() if (hasEvents) { printf(" void init_listener();\n"); printf(" static const struct %s_listener m_%s_listener;\n", interfaceName, interfaceName); - for (int i = 0; i < interface.events.size(); ++i) { - const WaylandEvent &e = interface.events.at(i); + for (const WaylandEvent &e : interface.events) { printf(" static void "); printEventHandlerSignature(e, interfaceName); @@ -1038,9 +1041,6 @@ bool Scanner::process() } printf(" struct ::%s *m_%s;\n", interfaceName, interfaceName); printf(" };\n"); - - if (j < interfaces.size() - 1) - printf("\n"); } printf("}\n"); printf("\n"); @@ -1077,18 +1077,23 @@ bool Scanner::process() printf("#endif\n"); printf("}\n"); printf("\n"); - for (int j = 0; j < interfaces.size(); ++j) { - const WaylandInterface &interface = interfaces.at(j); + + bool needsNewLine = false; + for (const WaylandInterface &interface : interfaces) { if (ignoreInterface(interface.name)) continue; + if (needsNewLine) + printf("\n"); + needsNewLine = true; + const char *interfaceName = interface.name.constData(); QByteArray stripped = stripInterfaceName(interface.name); const char *interfaceNameStripped = stripped.constData(); - bool hasEvents = !interface.events.isEmpty(); + bool hasEvents = !interface.events.empty(); printf(" %s::%s(struct ::wl_registry *registry, int id, int version)\n", interfaceName, interfaceName); printf(" {\n"); @@ -1152,9 +1157,8 @@ bool Scanner::process() printf(" return &::%s_interface;\n", interfaceName); printf(" }\n"); - for (int i = 0; i < interface.requests.size(); ++i) { + for (const WaylandEvent &e : interface.requests) { printf("\n"); - const WaylandEvent &e = interface.requests.at(i); const WaylandArgument *new_id = newIdArgument(e.arguments); QByteArray new_id_str = "void "; if (new_id) { @@ -1167,8 +1171,7 @@ bool Scanner::process() printEvent(e); printf("\n"); printf(" {\n"); - for (int i = 0; i < e.arguments.size(); ++i) { - const WaylandArgument &a = e.arguments.at(i); + for (const WaylandArgument &a : e.arguments) { if (a.type != "array") continue; QByteArray array = a.name + "_data"; @@ -1180,12 +1183,11 @@ bool Scanner::process() printf(" %s.alloc = 0;\n", arrayName); printf("\n"); } - int actualArgumentCount = new_id ? e.arguments.size() - 1 : e.arguments.size(); + int actualArgumentCount = new_id ? int(e.arguments.size()) - 1 : int(e.arguments.size()); printf(" %s%s_%s(\n", new_id ? "return " : "", interfaceName, e.name.constData()); printf(" m_%s%s", interfaceName, actualArgumentCount > 0 ? "," : ""); bool needsComma = false; - for (int i = 0; i < e.arguments.size(); ++i) { - const WaylandArgument &a = e.arguments.at(i); + for (const WaylandArgument &a : e.arguments) { bool isNewId = a.type == "new_id"; if (isNewId && !a.interface.isEmpty()) continue; @@ -1215,8 +1217,7 @@ bool Scanner::process() if (hasEvents) { printf("\n"); - for (int i = 0; i < interface.events.size(); ++i) { - const WaylandEvent &e = interface.events.at(i); + for (const WaylandEvent &e : interface.events) { printf(" void %s::%s_", interfaceName, interfaceNameStripped); printEvent(e, true); printf("\n"); @@ -1229,17 +1230,17 @@ bool Scanner::process() printf(" {\n"); printf(" Q_UNUSED(object);\n"); printf(" static_cast<%s *>(data)->%s_%s(", interfaceName, interfaceNameStripped, e.name.constData()); - for (int i = 0; i < e.arguments.size(); ++i) { + bool needsComma = false; + for (const WaylandArgument &a : e.arguments) { + if (needsComma) + printf(","); + needsComma = true; printf("\n"); - const WaylandArgument &a = e.arguments.at(i); const char *argumentName = a.name.constData(); if (a.type == "string") printf(" QString::fromUtf8(%s)", argumentName); else printf(" %s", argumentName); - - if (i < e.arguments.size() - 1) - printf(","); } printf(");\n"); @@ -1247,9 +1248,8 @@ bool Scanner::process() printf("\n"); } printf(" const struct %s_listener %s::m_%s_listener = {\n", interfaceName, interfaceName, interfaceName); - for (int i = 0; i < interface.events.size(); ++i) { - const WaylandEvent &e = interface.events.at(i); - printf(" %s::handle_%s%s\n", interfaceName, e.name.constData(), i < interface.events.size() - 1 ? "," : ""); + for (const WaylandEvent &e : interface.events) { + printf(" %s::handle_%s,\n", interfaceName, e.name.constData()); } printf(" };\n"); printf("\n"); @@ -1259,9 +1259,6 @@ bool Scanner::process() printf(" %s_add_listener(m_%s, &m_%s_listener, this);\n", interfaceName, interfaceName, interfaceName); printf(" }\n"); } - - if (j < interfaces.size() - 1) - printf("\n"); } printf("}\n"); printf("\n"); diff --git a/src/shared/qwaylandinputmethodeventbuilder.cpp b/src/shared/qwaylandinputmethodeventbuilder.cpp index 88056637..526d0ef4 100644 --- a/src/shared/qwaylandinputmethodeventbuilder.cpp +++ b/src/shared/qwaylandinputmethodeventbuilder.cpp @@ -157,7 +157,7 @@ QInputMethodEvent QWaylandInputMethodEventBuilder::buildPreedit(const QString &t attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, indexFromWayland(text, m_preeditCursor), 1, QVariant())); } - Q_FOREACH (const QInputMethodEvent::Attribute &attr, m_preeditStyles) { + for (const QInputMethodEvent::Attribute &attr : qAsConst(m_preeditStyles)) { int start = indexFromWayland(text, attr.start); int length = indexFromWayland(text, attr.start + attr.length) - start; attributes.append(QInputMethodEvent::Attribute(attr.type, start, length, attr.value)); diff --git a/src/src.pro b/src/src.pro index d4244de3..3d68c69f 100644 --- a/src/src.pro +++ b/src/src.pro @@ -3,21 +3,22 @@ include($$OUT_PWD/client/qtwaylandclient-config.pri) include($$OUT_PWD/compositor/qtwaylandcompositor-config.pri) QT_FOR_CONFIG += waylandclient-private waylandcompositor-private -qtConfig(wayland-client) { +qtConfig(wayland-client)|qtConfig(wayland-server) { sub_qtwaylandscanner.subdir = qtwaylandscanner sub_qtwaylandscanner.target = sub-qtwaylandscanner SUBDIRS += sub_qtwaylandscanner - sub_client.subdir = client - sub_client.depends = sub-qtwaylandscanner - sub_client.target = sub-client - SUBDIRS += sub_client + qtConfig(wayland-client) { + sub_client.subdir = client + sub_client.depends = sub-qtwaylandscanner + sub_client.target = sub-client + SUBDIRS += sub_client + } sub_plugins.subdir = plugins - sub_plugins.depends += sub-qtwaylandscanner sub-client - qtConfig(wayland-server) { - sub_plugins.depends += sub-compositor - } + sub_plugins.depends += sub-qtwaylandscanner + qtConfig(wayland-client):sub_plugins.depends += sub-client + qtConfig(wayland-server):sub_plugins.depends += sub-compositor sub_plugins.target = sub-plugins SUBDIRS += sub_plugins @@ -26,10 +27,11 @@ qtConfig(wayland-client) { sub_compositor.depends = sub-qtwaylandscanner sub_compositor.target = sub-compositor SUBDIRS += sub_compositor - - sub_imports.subdir = imports - sub_imports.depends += sub-compositor sub-client - sub_imports.target = sub-imports - SUBDIRS += sub_imports } + + sub_imports.subdir = imports + qtConfig(wayland-client):sub_imports.depends += sub-client + qtConfig(wayland-server):sub_imports.depends += sub-compositor + sub_imports.target = sub-imports + SUBDIRS += sub_imports } diff --git a/sync.profile b/sync.profile index 1aa935c3..5c7ada91 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" => [ @@ -55,33 +57,35 @@ "$basedir/src/compositor" => [ "^qwayland-server-wayland.h", "^qwayland-server-hardware-integration.h", + "^qwayland-server-idle-inhibit-unstable-v1.h", "^qwayland-server-ivi-application.h", "^qwayland-server-qt-windowmanager.h", "^qwayland-server-qt-key-unstable-v1.h", - "^qwayland-server-scaler.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", "^qwayland-server-touch-extension.h", "^qwayland-server-viewporter.h", "^qwayland-server-xdg-decoration-unstable-v1.h", - "^qwayland-server-xdg-shell-unstable-v5.h", + "^qwayland-server-xdg-output-unstable-v1.h", "^qwayland-server-xdg-shell-unstable-v6.h", "^qwayland-server-xdg-shell.h", "^wayland-hardware-integration-server-protocol.h", + "^wayland-idle-inhibit-unstable-v1-server-protocol.h", "^wayland-ivi-application-server-protocol.h", "^wayland-qt-windowmanager-server-protocol.h", "^wayland-qt-key-unstable-v1-server-protocol.h", - "^wayland-scaler-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", "^wayland-viewporter-server-protocol.h", "^wayland-touch-extension-server-protocol.h", "^wayland-wayland-server-protocol.h", "^wayland-xdg-decoration-unstable-v1-server-protocol.h", + "^wayland-xdg-output-unstable-v1-server-protocol.h", "^wayland-xdg-shell-server-protocol.h", - "^wayland-xdg-shell-unstable-v5-server-protocol.h", "^wayland-xdg-shell-unstable-v6-server-protocol.h", ], "$basedir/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1" => [ diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index d81339e8..032ae47a 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -1,10 +1,11 @@ TEMPLATE=subdirs QT_FOR_CONFIG += waylandclient-private -!qtHaveModule(waylandcompositor): \ - return() +qtHaveModule(waylandclient): \ + SUBDIRS += client -SUBDIRS += compositor +qtHaveModule(waylandclient):qtHaveModule(waylandcompositor): \ + SUBDIRS += cmake -qtConfig(wayland-client): \ - SUBDIRS += client cmake +qtHaveModule(waylandcompositor): \ + SUBDIRS += compositor diff --git a/tests/auto/client/client.pro b/tests/auto/client/client.pro index 06c1cb87..4b1eb245 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 \ xdgdecorationv1 \ diff --git a/tests/auto/client/client/tst_client.cpp b/tests/auto/client/client/tst_client.cpp index 08120c8c..e9ae5e4b 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/datadevicev1/tst_datadevicev1.cpp b/tests/auto/client/datadevicev1/tst_datadevicev1.cpp index 7368829d..e3babceb 100644 --- a/tests/auto/client/datadevicev1/tst_datadevicev1.cpp +++ b/tests/auto/client/datadevicev1/tst_datadevicev1.cpp @@ -66,7 +66,7 @@ void tst_datadevicev1::initTestCase() { QCOMPOSITOR_TRY_VERIFY(pointer()); QCOMPOSITOR_TRY_VERIFY(!pointer()->resourceMap().empty()); - QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 4); + QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 5); QCOMPOSITOR_TRY_VERIFY(keyboard()); @@ -104,8 +104,11 @@ void tst_datadevicev1::pasteAscii() keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol pointer()->sendEnter(surface, {32, 32}); + pointer()->sendFrame(client); pointer()->sendButton(client, BTN_LEFT, 1); + pointer()->sendFrame(client); pointer()->sendButton(client, BTN_LEFT, 0); + pointer()->sendFrame(client); }); QTRY_COMPARE(window.m_text, "normal ascii"); } @@ -139,8 +142,11 @@ void tst_datadevicev1::pasteUtf8() keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol pointer()->sendEnter(surface, {32, 32}); + pointer()->sendFrame(client); pointer()->sendButton(client, BTN_LEFT, 1); + pointer()->sendFrame(client); pointer()->sendButton(client, BTN_LEFT, 0); + pointer()->sendFrame(client); }); QTRY_COMPARE(window.m_text, "face with tears of joy: 😂"); } diff --git a/tests/auto/client/inputcontext/inputcontext.pro b/tests/auto/client/inputcontext/inputcontext.pro index 4419b3e7..1971d455 100644 --- a/tests/auto/client/inputcontext/inputcontext.pro +++ b/tests/auto/client/inputcontext/inputcontext.pro @@ -1,6 +1,4 @@ include (../shared/shared.pri) -QT += waylandcompositor - TARGET = tst_inputcontext SOURCES += tst_inputcontext.cpp diff --git a/tests/auto/client/output/tst_output.cpp b/tests/auto/client/output/tst_output.cpp index 2d2c8efd..29c773cf 100644 --- a/tests/auto/client/output/tst_output.cpp +++ b/tests/auto/client/output/tst_output.cpp @@ -196,8 +196,11 @@ void tst_output::removePrimaryScreen() exec([&] { auto *surface = xdgToplevel()->surface(); pointer()->sendEnter(surface, {32, 32}); + pointer()->sendFrame(client()); pointer()->sendButton(client(), BTN_LEFT, 1); + pointer()->sendFrame(client()); pointer()->sendButton(client(), BTN_LEFT, 0); + pointer()->sendFrame(client()); }); // Wait to make sure mouse events dont't cause a crash now that the screen has changed diff --git a/tests/auto/client/primaryselectionv1/primaryselectionv1.pro b/tests/auto/client/primaryselectionv1/primaryselectionv1.pro new file mode 100644 index 00000000..9d00562d --- /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 00000000..216db85c --- /dev/null +++ b/tests/auto/client/primaryselectionv1/tst_primaryselectionv1.cpp @@ -0,0 +1,475 @@ +/**************************************************************************** +** +** 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(), 5); + + 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()->sendFrame(client()); + pointer()->sendButton(client(), BTN_MIDDLE, 1); + pointer()->sendFrame(client()); + pointer()->sendButton(client(), BTN_MIDDLE, 0); + pointer()->sendFrame(client()); + }); + 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()->sendFrame(client()); + pointer()->sendButton(client(), BTN_MIDDLE, 1); + pointer()->sendFrame(client()); + pointer()->sendButton(client(), BTN_MIDDLE, 0); + pointer()->sendFrame(client()); + }); + 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}); + pointer()->sendFrame(client()); + mouseSerials << pointer()->sendButton(client(), BTN_MIDDLE, 1); + pointer()->sendFrame(client()); + mouseSerials << pointer()->sendButton(client(), BTN_MIDDLE, 0); + pointer()->sendFrame(client()); + }); + 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 1d6fb6b9..2e17bef8 100644 --- a/tests/auto/client/seatv4/tst_seatv4.cpp +++ b/tests/auto/client/seatv4/tst_seatv4.cpp @@ -81,6 +81,7 @@ private slots: void bitmapCursor(); void hidpiBitmapCursor(); void hidpiBitmapCursorNonInt(); + void animatedCursor(); #endif }; @@ -236,22 +237,20 @@ void tst_seatv4::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}; + QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << QPoint{0, -12}; + QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << QPoint{0, 12}; + QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << QPoint{-12, 0}; + QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << QPoint{12, 0}; + QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << QPoint{0, 120}; } void tst_seatv4::simpleAxis() { QFETCH(uint, axis); QFETCH(qreal, value); - QFETCH(Qt::Orientation, orientation); QFETCH(QPoint, angleDelta); class WheelWindow : QRasterWindow { @@ -280,27 +279,18 @@ void tst_seatv4::simpleAxis() // We didn't press any buttons QCOMPARE(event->buttons(), Qt::NoButton); - if (event->orientation() == Qt::Horizontal) - QCOMPARE(event->delta(), event->angleDelta().x()); - else - QCOMPARE(event->delta(), event->angleDelta().y()); - // There has been no information about what created the event. // Documentation says not synthesized is appropriate in such cases QCOMPARE(event->source(), Qt::MouseEventNotSynthesized); - m_events.append(Event(event->pixelDelta(), event->angleDelta(), event->orientation())); + m_events.append(Event{event->pixelDelta(), event->angleDelta()}); } struct Event // Because I didn't find a convenient way to copy it entirely { - // TODO: Constructors can be removed when we start supporting brace-initializers Event() = default; - Event(const QPoint &pixelDelta, const QPoint &angleDelta, Qt::Orientation orientation) - : pixelDelta(pixelDelta), angleDelta(angleDelta), orientation(orientation) - {} + const QPoint pixelDelta; const QPoint angleDelta; // eights of a degree, positive is upwards, left - const Qt::Orientation orientation{}; }; QVector<Event> m_events; }; @@ -323,7 +313,6 @@ void tst_seatv4::simpleAxis() QTRY_COMPARE(window.m_events.size(), 1); auto event = window.m_events.takeFirst(); QCOMPARE(event.angleDelta, angleDelta); - QCOMPARE(event.orientation, orientation); } void tst_seatv4::invalidPointerEvents() @@ -579,6 +568,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 00000000..2081845e --- /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 00000000..ca8de31a --- /dev/null +++ b/tests/auto/client/seatv5/tst_seatv5.cpp @@ -0,0 +1,435 @@ +/**************************************************************************** +** +** 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 | MockCompositor::Seat::capability_touch; + int version = 5; + add<Seat>(capabilities, version); + }); + } +}; + +class tst_seatv5 : public QObject, private SeatV5Compositor +{ + Q_OBJECT +private slots: + void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); } + void bindsToSeat(); + + // Pointer tests + void createsPointer(); + void setsCursorOnEnter(); + void usesEnterSerial(); + void simpleAxis_data(); + void simpleAxis(); + void fingerScroll(); + void fingerScrollSlow(); + void wheelDiscreteScroll(); + + // Touch tests + void createsTouch(); + void singleTap(); +}; + +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) { + // Shouldn't have deltas in the these 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); + + 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()) + , source(event->source()) + { + } + const Qt::ScrollPhase phase{}; + const QPoint pixelDelta; + const QPoint angleDelta; // eights of a degree, positive is upwards, left + const Qt::MouseEventSource source{}; + }; + QVector<Event> m_events; +}; + +void tst_seatv5::simpleAxis_data() +{ + QTest::addColumn<uint>("axis"); + QTest::addColumn<qreal>("value"); + QTest::addColumn<QPoint>("angleDelta"); + + // Directions in regular windows/linux terms (no "natural" scrolling) + QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << QPoint{0, -12}; + QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << QPoint{0, 12}; + QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << QPoint{-12, 0}; + QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << QPoint{12, 0}; + QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << QPoint{0, 120}; +} + +void tst_seatv5::simpleAxis() +{ + QFETCH(uint, axis); + QFETCH(qreal, value); + 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.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); + QVERIFY(qAbs(e.angleDelta.x()) <= qAbs(e.angleDelta.y())); // Vertical scroll +// 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); + QVERIFY(qAbs(e.angleDelta.x()) > qAbs(e.angleDelta.y())); // Horizontal scroll + 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); + QVERIFY(qAbs(e.angleDelta.x()) <= qAbs(e.angleDelta.y())); // Vertical scroll + // 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)); + } +} + +void tst_seatv5::createsTouch() +{ + QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().size(), 1); + QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().first()->version(), 5); +} + +class TouchWindow : public QRasterWindow { +public: + TouchWindow() + { + resize(64, 64); + show(); + } + void touchEvent(QTouchEvent *event) override + { + QRasterWindow::touchEvent(event); + 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 QTouchEvent *event) + : type(event->type()) + , touchPointStates(event->touchPointStates()) + , touchPoints(event->touchPoints()) + { + } + const QEvent::Type type{}; + const Qt::TouchPointStates touchPointStates{}; + const QList<QTouchEvent::TouchPoint> touchPoints; + }; + QVector<Event> m_events; +}; + +void tst_seatv5::singleTap() +{ + TouchWindow window; + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + exec([=] { + auto *t = touch(); + auto *c = client(); + t->sendDown(xdgToplevel()->surface(), {32, 32}, 1); + t->sendFrame(c); + t->sendUp(c, 1); + t->sendFrame(c); + }); + + QTRY_VERIFY(!window.m_events.empty()); + { + auto e = window.m_events.takeFirst(); + QCOMPARE(e.type, QEvent::TouchBegin); + QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointPressed); + QCOMPARE(e.touchPoints.length(), 1); + QCOMPARE(e.touchPoints.first().pos(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top())); + } + { + auto e = window.m_events.takeFirst(); + QCOMPARE(e.type, QEvent::TouchEnd); + QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointReleased); + QCOMPARE(e.touchPoints.length(), 1); + QCOMPARE(e.touchPoints.first().pos(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top())); + } +} + +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 875b7d05..254465ee 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 729d481f..120c256c 100644 --- a/tests/auto/client/shared/coreprotocol.cpp +++ b/tests/auto/client/shared/coreprotocol.cpp @@ -191,12 +191,15 @@ Seat::~Seat() { qDeleteAll(m_oldPointers); delete m_pointer; + + qDeleteAll(m_oldTouchs); + delete m_touch; + + qDeleteAll(m_oldKeyboards); + delete m_keyboard; } void Seat::setCapabilities(uint capabilities) { - // TODO: Add support for touch - Q_ASSERT(~capabilities & capability_touch); - m_capabilities = capabilities; if (m_capabilities & capability_pointer) { @@ -207,6 +210,14 @@ void Seat::setCapabilities(uint capabilities) { m_pointer = nullptr; } + if (m_capabilities & capability_touch) { + if (!m_touch) + m_touch = (new Touch(this)); + } else if (m_touch) { + m_oldTouchs << m_touch; + m_touch = nullptr; + } + if (m_capabilities & capability_keyboard) { if (!m_keyboard) m_keyboard = (new Keyboard(this)); @@ -234,9 +245,24 @@ void Seat::seat_get_pointer(Resource *resource, uint32_t id) m_pointer->add(resource->client(), id, resource->version()); } +void Seat::seat_get_touch(QtWaylandServer::wl_seat::Resource *resource, uint32_t id) +{ + if (~m_capabilities & capability_touch) { + qWarning() << "Client requested a wl_touch without the capability being available." + << "This Could be a race condition when hotunplugging," + << "but is most likely a client error"; + Touch *touch = new Touch(this); + touch->add(resource->client(), id, resource->version()); + // TODO: mark as destroyed + m_oldTouchs << touch; + return; + } + m_touch->add(resource->client(), id, resource->version()); +} + void Seat::seat_get_keyboard(QtWaylandServer::wl_seat::Resource *resource, uint32_t id) { - if (~m_capabilities & capability_pointer) { + if (~m_capabilities & capability_keyboard) { qWarning() << "Client requested a wl_keyboard without the capability being available." << "This Could be a race condition when hotunplugging," << "but is most likely a client error"; @@ -314,6 +340,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); @@ -338,6 +397,40 @@ void Pointer::pointer_set_cursor(Resource *resource, uint32_t serial, wl_resourc emit setCursor(serial); } +uint Touch::sendDown(Surface *surface, const QPointF &position, int id) +{ + wl_fixed_t x = wl_fixed_from_double(position.x()); + wl_fixed_t y = wl_fixed_from_double(position.y()); + uint serial = m_seat->m_compositor->nextSerial(); + auto time = m_seat->m_compositor->currentTimeMilliseconds(); + wl_client *client = surface->resource()->client(); + + const auto touchResources = resourceMap().values(client); + for (auto *r : touchResources) + wl_touch::send_down(r->handle, serial, time, surface->resource()->handle, id, x, y); + + return serial; +} + +uint Touch::sendUp(wl_client *client, int id) +{ + uint serial = m_seat->m_compositor->nextSerial(); + auto time = m_seat->m_compositor->currentTimeMilliseconds(); + + const auto touchResources = resourceMap().values(client); + for (auto *r : touchResources) + wl_touch::send_up(r->handle, serial, time, id); + + return serial; +} + +void Touch::sendFrame(wl_client *client) +{ + const auto touchResources = resourceMap().values(client); + for (auto *r : touchResources) + send_frame(r->handle); +} + uint Keyboard::sendEnter(Surface *surface) { auto serial = m_seat->m_compositor->nextSerial(); @@ -345,6 +438,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 +449,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 5cef476c..50812c29 100644 --- a/tests/auto/client/shared/coreprotocol.h +++ b/tests/auto/client/shared/coreprotocol.h @@ -38,6 +38,7 @@ namespace MockCompositor { class WlCompositor; class Output; class Pointer; +class Touch; class Keyboard; class CursorRole; class ShmPool; @@ -236,7 +237,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 | Seat::capability_touch, int version = 5); ~Seat() override; void send_capabilities(Resource *resource, uint capabilities) = delete; // Use wrapper instead void send_capabilities(uint capabilities) = delete; // Use wrapper instead @@ -247,6 +248,9 @@ public: Pointer* m_pointer = nullptr; QVector<Pointer *> m_oldPointers; + Touch* m_touch = nullptr; + QVector<Touch *> m_oldTouchs; + Keyboard* m_keyboard = nullptr; QVector<Keyboard *> m_oldKeyboards; @@ -259,8 +263,8 @@ protected: } void seat_get_pointer(Resource *resource, uint32_t id) override; + void seat_get_touch(Resource *resource, uint32_t id) override; void seat_get_keyboard(Resource *resource, uint32_t id) override; -// void seat_get_touch(Resource *resource, uint32_t id) override; // void seat_release(Resource *resource) override; }; @@ -279,6 +283,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; @@ -303,6 +311,18 @@ public: Surface *m_surface = nullptr; }; +class Touch : public QObject, public QtWaylandServer::wl_touch +{ + Q_OBJECT +public: + explicit Touch(Seat *seat) : m_seat(seat) {} + uint sendDown(Surface *surface, const QPointF &position, int id); + uint sendUp(wl_client *client, int id); + void sendFrame(wl_client *client); + + Seat *m_seat = nullptr; +}; + class Keyboard : public QObject, public QtWaylandServer::wl_keyboard { Q_OBJECT @@ -313,6 +333,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.cpp b/tests/auto/client/shared/mockcompositor.cpp index 6b9af429..5f2d8907 100644 --- a/tests/auto/client/shared/mockcompositor.cpp +++ b/tests/auto/client/shared/mockcompositor.cpp @@ -41,7 +41,7 @@ DefaultCompositor::DefaultCompositor() add<SubCompositor>(); auto *output = add<Output>(); output->m_data.physicalSize = output->m_data.mode.physicalSizeForDpi(96); - add<Seat>(Seat::capability_pointer | Seat::capability_keyboard); + add<Seat>(Seat::capability_pointer | Seat::capability_keyboard | Seat::capability_touch); add<XdgWmBase>(); add<Shm>(); // TODO: other shells, viewporter, xdgoutput etc diff --git a/tests/auto/client/shared/mockcompositor.h b/tests/auto/client/shared/mockcompositor.h index 05bf32c8..fc4d7cc4 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 { @@ -54,6 +60,7 @@ public: XdgToplevel *xdgToplevel(int i = 0) { return get<XdgWmBase>()->toplevel(i); } XdgPopup *xdgPopup(int i = 0) { return get<XdgWmBase>()->popup(i); } Pointer *pointer() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_pointer; } + Touch *touch() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_touch; } Surface *cursorSurface() { auto *p = pointer(); return p ? p->cursorSurface() : nullptr; } Keyboard *keyboard() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_keyboard; } uint sendXdgShellPing(); diff --git a/tests/auto/client/shared_old/mockcompositor.cpp b/tests/auto/client/shared_old/mockcompositor.cpp index 0dfaef5e..f71a7810 100644 --- a/tests/auto/client/shared_old/mockcompositor.cpp +++ b/tests/auto/client/shared_old/mockcompositor.cpp @@ -221,8 +221,8 @@ QSharedPointer<MockSurface> MockCompositor::surface() QSharedPointer<MockSurface> result; lock(); { - QVector<Impl::Surface *> surfaces = m_compositor->surfaces(); - foreach (Impl::Surface *surface, surfaces) { + const QVector<Impl::Surface *> surfaces = m_compositor->surfaces(); + for (Impl::Surface *surface : surfaces) { // we don't want to mistake the cursor surface for a window surface if (surface->isMapped()) { result = surface->mockSurface(); diff --git a/tests/auto/client/shared_old/mocksurface.cpp b/tests/auto/client/shared_old/mocksurface.cpp index 81a5edbd..e9df5f90 100644 --- a/tests/auto/client/shared_old/mocksurface.cpp +++ b/tests/auto/client/shared_old/mocksurface.cpp @@ -149,11 +149,10 @@ void Surface::surface_commit(Resource *resource) } } - foreach (wl_resource *frameCallback, m_frameCallbackList) { + for (wl_resource *frameCallback : qExchange(m_frameCallbackList, {})) { wl_callback_send_done(frameCallback, m_compositor->time()); wl_resource_destroy(frameCallback); } - m_frameCallbackList.clear(); } } diff --git a/tests/auto/client/surface/tst_surface.cpp b/tests/auto/client/surface/tst_surface.cpp index dddff086..9659235a 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 9b18abdc..ac5c2498 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() @@ -211,12 +214,13 @@ void tst_xdgshell::popup() uint clickSerial = exec([=] { auto *surface = xdgToplevel()->surface(); auto *p = pointer(); + auto *c = client(); p->sendEnter(surface, {100, 100}); -// p->sendFrame(); //TODO: uncomment when we support seat v5 + p->sendFrame(c); uint serial = p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); - p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); + p->sendButton(c, BTN_LEFT, Pointer::button_state_released); return serial; -// p->sendFrame(); //TODO: uncomment when we support seat v5 + p->sendFrame(c); }); QTRY_VERIFY(window.m_popup); @@ -295,13 +299,14 @@ void tst_xdgshell::tooltipOnPopup() exec([=] { auto *surface = xdgToplevel()->surface(); auto *p = pointer(); + auto *c = client(); p->sendEnter(surface, {100, 100}); -// p->sendFrame(); //TODO: uncomment when we support seat v5 + p->sendFrame(c); p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); -// p->sendFrame(); + p->sendFrame(c); p->sendLeave(surface); -// p->sendFrame(); + p->sendFrame(c); }); QCOMPOSITOR_TRY_VERIFY(xdgPopup()); @@ -312,11 +317,12 @@ void tst_xdgshell::tooltipOnPopup() exec([=] { auto *surface = xdgPopup()->surface(); auto *p = pointer(); + auto *c = client(); p->sendEnter(surface, {100, 100}); -// p->sendFrame(); + p->sendFrame(c); p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); -// p->sendFrame(); + p->sendFrame(c); }); QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)); @@ -377,13 +383,14 @@ void tst_xdgshell::switchPopups() exec([=] { auto *surface = xdgToplevel()->surface(); auto *p = pointer(); + auto *c = client(); p->sendEnter(surface, {100, 100}); -// p->sendFrame(); //TODO: uncomment when we support seat v5 - p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); - p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); -// p->sendFrame(); + p->sendFrame(c); + p->sendButton(c, BTN_LEFT, Pointer::button_state_pressed); + p->sendButton(c, BTN_LEFT, Pointer::button_state_released); + p->sendFrame(c); p->sendLeave(surface); -// p->sendFrame(); + p->sendFrame(c); }); QCOMPOSITOR_TRY_VERIFY(xdgPopup()); @@ -396,11 +403,12 @@ void tst_xdgshell::switchPopups() exec([=] { auto *surface = xdgToplevel()->surface(); auto *p = pointer(); + auto *c = client(); p->sendEnter(surface, {100, 100}); -// p->sendFrame(); - p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); - p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); -// p->sendFrame(); + p->sendFrame(c); + p->sendButton(c, BTN_LEFT, Pointer::button_state_pressed); + p->sendButton(c, BTN_LEFT, Pointer::button_state_released); + p->sendFrame(c); }); // The client will now hide one popup and then show another @@ -473,10 +481,41 @@ void tst_xdgshell::windowGeometry() exec([=] { xdgToplevel()->sendCompleteConfigure(); }); - QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committed.windowGeometry, QRect(QPoint(0, 0), window.frameGeometry().size())); + QSize marginsSize; + marginsSize.setWidth(window.frameMargins().left() + window.frameMargins().right()); + marginsSize.setHeight(window.frameMargins().top() + window.frameMargins().bottom()); + + QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committed.windowGeometry, QRect(QPoint(0, 0), QSize(400, 320) + marginsSize)); window.resize(800, 600); - QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committed.windowGeometry, QRect(QPoint(0, 0), window.frameGeometry().size())); + QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committed.windowGeometry, QRect(QPoint(0, 0), QSize(800, 600) + marginsSize)); +} + +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) diff --git a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp index a397f60e..027e1dfa 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/auto/compositor/compositor/compositor.pro b/tests/auto/compositor/compositor/compositor.pro index 500a92c6..27001659 100644 --- a/tests/auto/compositor/compositor/compositor.pro +++ b/tests/auto/compositor/compositor/compositor.pro @@ -14,7 +14,9 @@ WAYLANDCLIENTSOURCES += \ ../../../../src/3rdparty/protocol/ivi-application.xml \ ../../../../src/3rdparty/protocol/wayland.xml \ ../../../../src/3rdparty/protocol/xdg-shell.xml \ - ../../../../src/3rdparty/protocol/viewporter.xml + ../../../../src/3rdparty/protocol/viewporter.xml \ + ../../../../src/3rdparty/protocol/idle-inhibit-unstable-v1.xml \ + ../../../../src/3rdparty/protocol/xdg-output-unstable-v1.xml SOURCES += \ tst_compositor.cpp \ @@ -24,7 +26,8 @@ SOURCES += \ mockseat.cpp \ testseat.cpp \ mockkeyboard.cpp \ - mockpointer.cpp + mockpointer.cpp \ + mockxdgoutputv1.cpp HEADERS += \ testcompositor.h \ @@ -33,4 +36,5 @@ HEADERS += \ mockseat.h \ testseat.h \ mockkeyboard.h \ - mockpointer.h + mockpointer.h \ + mockxdgoutputv1.h diff --git a/tests/auto/compositor/compositor/mockclient.cpp b/tests/auto/compositor/compositor/mockclient.cpp index fa4d4c6b..27d1eed8 100644 --- a/tests/auto/compositor/compositor/mockclient.cpp +++ b/tests/auto/compositor/compositor/mockclient.cpp @@ -184,11 +184,19 @@ void MockClient::handleGlobal(uint32_t id, const QByteArray &interface) } else if (interface == "wl_seat") { wl_seat *s = static_cast<wl_seat *>(wl_registry_bind(registry, id, &wl_seat_interface, 1)); m_seats << new MockSeat(s); + } else if (interface == "zwp_idle_inhibit_manager_v1") { + idleInhibitManager = static_cast<zwp_idle_inhibit_manager_v1 *>(wl_registry_bind(registry, id, &zwp_idle_inhibit_manager_v1_interface, 1)); + } else if (interface == "zxdg_output_manager_v1") { + xdgOutputManager = new QtWayland::zxdg_output_manager_v1(registry, id, 2); } } void MockClient::handleGlobalRemove(uint32_t id) { + auto *output = m_outputs[id]; + if (m_xdgOutputs.contains(output)) + delete m_xdgOutputs.take(output); + m_outputs.remove(id); } @@ -222,6 +230,23 @@ ivi_surface *MockClient::createIviSurface(wl_surface *surface, uint iviId) return ivi_application_surface_create(iviApplication, iviId, surface); } +zwp_idle_inhibitor_v1 *MockClient::createIdleInhibitor(wl_surface *surface) +{ + flushDisplay(); + + auto *idleInhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor( + idleInhibitManager, surface); + zwp_idle_inhibitor_v1_set_user_data(idleInhibitor, this); + return idleInhibitor; +} + +MockXdgOutputV1 *MockClient::createXdgOutput(wl_output *output) +{ + auto *xdgOutput = new MockXdgOutputV1(xdgOutputManager->get_xdg_output(output)); + m_xdgOutputs[output] = xdgOutput; + return xdgOutput; +} + ShmBuffer::ShmBuffer(const QSize &size, wl_shm *shm) { int stride = size.width() * 4; diff --git a/tests/auto/compositor/compositor/mockclient.h b/tests/auto/compositor/compositor/mockclient.h index b537e9df..69a0e47c 100644 --- a/tests/auto/compositor/compositor/mockclient.h +++ b/tests/auto/compositor/compositor/mockclient.h @@ -30,6 +30,7 @@ #include <qwayland-xdg-shell.h> #include <wayland-ivi-application-client-protocol.h> #include "wayland-viewporter-client-protocol.h" +#include "wayland-idle-inhibit-unstable-v1-client-protocol.h" #include <QObject> #include <QImage> @@ -37,6 +38,8 @@ #include <QList> #include <QWaylandOutputMode> +#include "mockxdgoutputv1.h" + class MockSeat; class ShmBuffer @@ -63,16 +66,21 @@ public: xdg_surface *createXdgSurface(wl_surface *surface); xdg_toplevel *createXdgToplevel(xdg_surface *xdgSurface); ivi_surface *createIviSurface(wl_surface *surface, uint iviId); + zwp_idle_inhibitor_v1 *createIdleInhibitor(wl_surface *surface); + MockXdgOutputV1 *createXdgOutput(wl_output *output); wl_display *display = nullptr; wl_compositor *compositor = nullptr; QMap<uint, wl_output *> m_outputs; + QMap<wl_output *, MockXdgOutputV1 *> m_xdgOutputs; wl_shm *shm = nullptr; wl_registry *registry = nullptr; wl_shell *wlshell = nullptr; xdg_wm_base *xdgWmBase = nullptr; wp_viewporter *viewporter = nullptr; ivi_application *iviApplication = nullptr; + zwp_idle_inhibit_manager_v1 *idleInhibitManager = nullptr; + QtWayland::zxdg_output_manager_v1 *xdgOutputManager = nullptr; QList<MockSeat *> m_seats; diff --git a/tests/auto/compositor/compositor/mockxdgoutputv1.cpp b/tests/auto/compositor/compositor/mockxdgoutputv1.cpp new file mode 100644 index 00000000..eebc55bb --- /dev/null +++ b/tests/auto/compositor/compositor/mockxdgoutputv1.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** 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 "mockxdgoutputv1.h" + +MockXdgOutputV1::MockXdgOutputV1(struct ::zxdg_output_v1 *object) + : QtWayland::zxdg_output_v1(object) +{ +} + +MockXdgOutputV1::~MockXdgOutputV1() +{ + destroy(); +} + +void MockXdgOutputV1::zxdg_output_v1_logical_position(int32_t x, int32_t y) +{ + pending.logicalPosition = QPoint(x, y); +} + +void MockXdgOutputV1::zxdg_output_v1_logical_size(int32_t width, int32_t height) +{ + pending.logicalSize = QSize(width, height); +} + +void MockXdgOutputV1::zxdg_output_v1_done() +{ + // In version 3 we'll have to do this for wl_output.done as well + name = pending.name; + description = pending.description; + logicalPosition = pending.logicalPosition; + logicalSize = pending.logicalSize; +} + +void MockXdgOutputV1::zxdg_output_v1_name(const QString &name) +{ + pending.name = name; +} + +void MockXdgOutputV1::zxdg_output_v1_description(const QString &description) +{ + pending.description = description; +} diff --git a/tests/auto/compositor/compositor/mockxdgoutputv1.h b/tests/auto/compositor/compositor/mockxdgoutputv1.h new file mode 100644 index 00000000..db582069 --- /dev/null +++ b/tests/auto/compositor/compositor/mockxdgoutputv1.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** 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$ +** +****************************************************************************/ + +#ifndef MOCKXDGOUTPUTV1_H +#define MOCKXDGOUTPUTV1_H + +#include <QPoint> +#include <QSize> +#include <QString> + +#include "qwayland-xdg-output-unstable-v1.h" + +class MockXdgOutputV1 : public QtWayland::zxdg_output_v1 +{ +public: + explicit MockXdgOutputV1(struct ::zxdg_output_v1 *object); + ~MockXdgOutputV1(); + + QString name; + QString description; + QPoint logicalPosition; + QSize logicalSize; + + struct { + QString name; + QString description; + QPoint logicalPosition; + QSize logicalSize; + } pending; + +protected: + void zxdg_output_v1_logical_position(int32_t x, int32_t y) override; + void zxdg_output_v1_logical_size(int32_t width, int32_t height) override; + void zxdg_output_v1_done() override; + void zxdg_output_v1_name(const QString &name) override; + void zxdg_output_v1_description(const QString &description) override; +}; + +#endif // MOCKXDGOUTPUTV1_H diff --git a/tests/auto/compositor/compositor/tst_compositor.cpp b/tests/auto/compositor/compositor/tst_compositor.cpp index c425f2ba..f4f63228 100644 --- a/tests/auto/compositor/compositor/tst_compositor.cpp +++ b/tests/auto/compositor/compositor/tst_compositor.cpp @@ -29,6 +29,7 @@ #include "mockclient.h" #include "mockseat.h" #include "mockpointer.h" +#include "mockxdgoutputv1.h" #include "testcompositor.h" #include "testkeyboardgrabber.h" #include "testseat.h" @@ -47,8 +48,12 @@ #include <QtWaylandCompositor/QWaylandResource> #include <QtWaylandCompositor/QWaylandKeymap> #include <QtWaylandCompositor/QWaylandViewporter> +#include <QtWaylandCompositor/QWaylandIdleInhibitManagerV1> +#include <QtWaylandCompositor/QWaylandXdgOutputManagerV1> #include <qwayland-xdg-shell.h> #include <qwayland-ivi-application.h> +#include <QtWaylandCompositor/private/qwaylandoutput_p.h> +#include <QtWaylandCompositor/private/qwaylandsurface_p.h> #include <QtTest/QtTest> @@ -73,12 +78,14 @@ private slots: void singleClient(); void multipleClients(); void geometry(); + void availableGeometry(); void modes(); void comparingModes(); void sizeFollowsWindow(); void mapSurface(); void mapSurfaceHiDpi(); void frameCallback(); + void pixelFormats(); void outputs(); void customSurface(); @@ -110,6 +117,10 @@ private slots: void viewportDestinationNoSurfaceError(); void viewportSourceNoSurfaceError(); void viewportHiDpi(); + + void idleInhibit(); + + void xdgOutput(); }; void tst_WaylandCompositor::init() { @@ -367,6 +378,22 @@ void tst_WaylandCompositor::geometry() QTRY_COMPARE(client.refreshRate, 60000); } +void tst_WaylandCompositor::availableGeometry() +{ + TestCompositor compositor; + compositor.create(); + + QWaylandOutputMode mode(QSize(1024, 768), 60000); + compositor.defaultOutput()->addMode(mode, true); + compositor.defaultOutput()->setCurrentMode(mode); + + MockClient client; + + QRect availableGeometry(50, 100, 850, 600); + compositor.defaultOutput()->setAvailableGeometry(availableGeometry); + QCOMPARE(compositor.defaultOutput()->availableGeometry(), availableGeometry); +} + void tst_WaylandCompositor::modes() { TestCompositor compositor; @@ -513,8 +540,13 @@ void tst_WaylandCompositor::mapSurfaceHiDpi() QObject::connect(waylandSurface, &QWaylandSurface::hasContentChanged, verifyComittedState); QSignalSpy hasContentSpy(waylandSurface, SIGNAL(hasContentChanged())); +#if QT_DEPRECATED_SINCE(5, 13) QObject::connect(waylandSurface, &QWaylandSurface::sizeChanged, verifyComittedState); QSignalSpy sizeSpy(waylandSurface, SIGNAL(sizeChanged())); +#endif + + QObject::connect(waylandSurface, &QWaylandSurface::bufferSizeChanged, verifyComittedState); + QSignalSpy bufferSizeSpy(waylandSurface, SIGNAL(bufferSizeChanged())); QObject::connect(waylandSurface, &QWaylandSurface::destinationSizeChanged, verifyComittedState); QSignalSpy destinationSizeSpy(waylandSurface, SIGNAL(destinationSizeChanged())); @@ -538,7 +570,10 @@ void tst_WaylandCompositor::mapSurfaceHiDpi() wl_surface_commit(surface); QTRY_COMPARE(hasContentSpy.count(), 1); +#if QT_DEPRECATED_SINCE(5, 13) QTRY_COMPARE(sizeSpy.count(), 1); +#endif + QTRY_COMPARE(bufferSizeSpy.count(), 1); QTRY_COMPARE(destinationSizeSpy.count(), 1); QTRY_COMPARE(bufferScaleSpy.count(), 1); QTRY_COMPARE(offsetSpy.count(), 1); @@ -561,27 +596,27 @@ static void registerFrameCallback(wl_surface *surface, int *counter) wl_callback_add_listener(wl_surface_frame(surface), &frameCallbackListener, counter); } -void tst_WaylandCompositor::frameCallback() +class BufferView : public QWaylandView { - class BufferView : public QWaylandView +public: + void bufferCommitted(const QWaylandBufferRef &ref, const QRegion &damage) override { - public: - void bufferCommitted(const QWaylandBufferRef &ref, const QRegion &damage) override - { - Q_UNUSED(damage); - bufferRef = ref; - } + Q_UNUSED(damage); + bufferRef = ref; + } - QImage image() const - { - if (bufferRef.isNull() || !bufferRef.isSharedMemory()) - return QImage(); - return bufferRef.image(); - } + QImage image() const + { + if (bufferRef.isNull() || !bufferRef.isSharedMemory()) + return QImage(); + return bufferRef.image(); + } - QWaylandBufferRef bufferRef; - }; + QWaylandBufferRef bufferRef; +}; +void tst_WaylandCompositor::frameCallback() +{ TestCompositor compositor; compositor.create(); @@ -622,6 +657,35 @@ void tst_WaylandCompositor::frameCallback() wl_surface_destroy(surface); } +void tst_WaylandCompositor::pixelFormats() +{ + TestCompositor compositor; + compositor.create(); + + MockClient client; + + wl_surface *surface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + BufferView* view = new BufferView; + view->setSurface(waylandSurface); + view->setOutput(compositor.defaultOutput()); + + QSize size(32, 32); + ShmBuffer buffer(size, client.shm); // Will be WL_SHM_FORMAT_ARGB8888; + wl_surface_attach(surface, buffer.handle, 0, 0); + wl_surface_damage(surface, 0, 0, size.width(), size.height()); + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->hasContent(), true); + + // According to https://lists.freedesktop.org/archives/wayland-devel/2017-August/034791.html + // all RGB formats with alpha are premultiplied. Verify it here: + QCOMPARE(view->image().format(), QImage::Format_ARGB32_Premultiplied); + + wl_surface_destroy(surface); +} + void tst_WaylandCompositor::outputs() { TestCompositor compositor; @@ -715,9 +779,8 @@ void tst_WaylandCompositor::seatCreation() // The compositor will create the default input device QTRY_VERIFY(seat->isInitialized()); - QList<QMouseEvent *> allEvents; - allEvents += seat->createMouseEvents(5); - foreach (QMouseEvent *me, allEvents) { + const QList<QMouseEvent *> allEvents = seat->createMouseEvents(5); + for (QMouseEvent *me : allEvents) { compositor.seatFor(me); } @@ -1700,5 +1763,103 @@ void tst_WaylandCompositor::viewportHiDpi() wl_surface_destroy(surface); } +class IdleInhibitCompositor : public TestCompositor +{ + Q_OBJECT +public: + IdleInhibitCompositor() : idleInhibitManager(this) {} + QWaylandIdleInhibitManagerV1 idleInhibitManager; +}; + +void tst_WaylandCompositor::idleInhibit() +{ + IdleInhibitCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.idleInhibitManager); + + auto *surface = client.createSurface(); + QVERIFY(surface); + QTRY_COMPARE(compositor.surfaces.size(), 1); + + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + auto *waylandSurfacePrivate = + QWaylandSurfacePrivate::get(waylandSurface); + QVERIFY(waylandSurfacePrivate); + + QSignalSpy changedSpy(waylandSurface, SIGNAL(inhibitsIdleChanged())); + + QCOMPARE(waylandSurface->inhibitsIdle(), false); + + auto *idleInhibitor = client.createIdleInhibitor(surface); + QVERIFY(idleInhibitor); + QTRY_COMPARE(waylandSurfacePrivate->idleInhibitors.size(), 1); + QCOMPARE(waylandSurface->inhibitsIdle(), true); + QTRY_COMPARE(changedSpy.count(), 1); +} + +class XdgOutputCompositor : public TestCompositor +{ + Q_OBJECT +public: + XdgOutputCompositor() : xdgOutputManager(this) {} + QWaylandXdgOutputManagerV1 xdgOutputManager; +}; + +void tst_WaylandCompositor::xdgOutput() +{ + XdgOutputCompositor compositor; + compositor.create(); + + QWaylandOutputMode mode(QSize(1024, 768), 60000); + compositor.defaultOutput()->addMode(mode, true); + compositor.defaultOutput()->setCurrentMode(mode); + + MockClient client; + QTRY_VERIFY(client.xdgOutputManager); + QTRY_COMPARE(client.m_outputs.size(), 1); + + auto *wlOutput = client.m_outputs.first(); + QVERIFY(wlOutput); + + // Output is not associated yet + QCOMPARE(QWaylandOutputPrivate::get(compositor.defaultOutput())->xdgOutput.isNull(), true); + + // Create xdg-output on the server + auto *xdgOutputServer = new QWaylandXdgOutputV1(compositor.defaultOutput(), &compositor.xdgOutputManager); + QVERIFY(xdgOutputServer); + xdgOutputServer->setName(QStringLiteral("OUTPUT1")); + xdgOutputServer->setDescription(QStringLiteral("This is a test output")); + + // Create it on the client + auto *xdgOutput = client.createXdgOutput(wlOutput); + QVERIFY(xdgOutput); + QVERIFY(client.m_xdgOutputs.contains(wlOutput)); + + // Now it should be associated + QCOMPARE(QWaylandOutputPrivate::get(compositor.defaultOutput())->xdgOutput.isNull(), false); + + // Verify initial values + QTRY_COMPARE(xdgOutput->name, "OUTPUT1"); + QTRY_COMPARE(xdgOutput->logicalPosition, QPoint()); + QTRY_COMPARE(xdgOutput->logicalSize, QSize()); + + // Change properties + xdgOutputServer->setName(QStringLiteral("OUTPUT2")); + xdgOutputServer->setDescription(QStringLiteral("New description")); + xdgOutputServer->setLogicalPosition(QPoint(100, 100)); + xdgOutputServer->setLogicalSize(QSize(1000, 1000)); + compositor.flushClients(); + + // Name and description can't be changed after initialization, + // so we expect them to be the same + // TODO: With protocol version 3 the description will be allowed to change, + // but we implement version 2 now + QTRY_COMPARE(xdgOutput->name, "OUTPUT1"); + QTRY_COMPARE(xdgOutput->description, "This is a test output"); + QTRY_COMPARE(xdgOutput->logicalPosition, QPoint(100, 100)); + QTRY_COMPARE(xdgOutput->logicalSize, QSize(1000, 1000)); +} + #include <tst_compositor.moc> QTEST_MAIN(tst_WaylandCompositor); 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 00000000..d251791d --- /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 00000000..e3f6d702 --- /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" |