summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dist/changes-5.12.140
-rw-r--r--src/3rdparty/protocol/qt_attribution.json17
-rw-r--r--src/3rdparty/protocol/wl-eglstream-controller.xml37
-rw-r--r--src/client/qwaylandintegration.cpp1
-rw-r--r--src/client/qwaylandwindow.cpp107
-rw-r--r--src/client/qwaylandwindow_p.h9
-rw-r--r--src/compositor/compositor_api/qwaylandcompositor.h2
-rw-r--r--src/compositor/compositor_api/qwaylandquickoutput.cpp2
-rw-r--r--src/compositor/compositor_api/qwaylandsurface.cpp1
-rw-r--r--src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp29
-rw-r--r--src/hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.pri16
-rw-r--r--src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamcontroller.cpp63
-rw-r--r--src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamcontroller.h79
-rw-r--r--src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.cpp439
-rw-r--r--src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.h93
-rw-r--r--src/plugins/hardwareintegration/compositor/compositor.pro4
-rw-r--r--src/plugins/hardwareintegration/compositor/wayland-eglstream-controller/main.cpp63
-rw-r--r--src/plugins/hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.json3
-rw-r--r--src/plugins/hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.pro12
-rw-r--r--tests/manual/wip-cpp-compositor/README.md6
-rw-r--r--tests/manual/wip-cpp-compositor/compositor.cpp290
-rw-r--r--tests/manual/wip-cpp-compositor/compositor.h160
-rw-r--r--tests/manual/wip-cpp-compositor/main.cpp67
-rw-r--r--tests/manual/wip-cpp-compositor/window.cpp145
-rw-r--r--tests/manual/wip-cpp-compositor/window.h87
-rw-r--r--tests/manual/wip-cpp-compositor/wip-cpp-compositor.pro12
26 files changed, 1675 insertions, 109 deletions
diff --git a/dist/changes-5.12.1 b/dist/changes-5.12.1
new file mode 100644
index 000000000..60ca27e91
--- /dev/null
+++ b/dist/changes-5.12.1
@@ -0,0 +1,40 @@
+Qt 5.12.1 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.12.0.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+http://doc.qt.io/qt-5/index.html
+
+The Qt version 5.12 series is binary compatible with the 5.11.x series.
+Applications compiled for 5.11 will continue to run with 5.12.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Compositor *
+****************************************************************************
+
+ - [QTBUG-71697] Added a new hardware-integration supporting eglstreams on
+ NVIDIA platforms. It can be enabled by setting
+ QT_WAYLAND_CLIENT_BUFFER_INTEGRATION=wayland-eglstream-controller in the
+ environment. This fixes showing a black frame when resizing client windows.
+ In addition some flickering problems got fixed, which happened when
+ the client repainted very often or running without window decorations.
+
+****************************************************************************
+* QPA plugin *
+****************************************************************************
+
+ - [QTBUG-71734] Fixed a protocol error that used to happen when closing a menu
+ with an active tooltip.
+ - [QTBUG-72235] Fixed a crash caused by compositors sending incorrect pointer
+ events.
+ - [QTBUG-72818] Fixed a bug where surface damage for window decorations was
+ outside the surface.
diff --git a/src/3rdparty/protocol/qt_attribution.json b/src/3rdparty/protocol/qt_attribution.json
index a9fddb840..657c03282 100644
--- a/src/3rdparty/protocol/qt_attribution.json
+++ b/src/3rdparty/protocol/qt_attribution.json
@@ -154,5 +154,20 @@ Copyright © 2015, 2016 Jan Arne Petersen"
"License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt",
"Copyright": "Copyright © 2014, 2015 Collabora, Ltd."
- }
+ },
+
+ {
+ "Id": "wayland-eglstream-controller",
+ "Name": "Wayland EGLStream Controller Protocol",
+ "QDocModule": "qtwaylandcompositor",
+ "QtUsage": "Used in the Qt Wayland Compositor",
+ "Files": "wayland-eglstream-controller.xml",
+
+ "Description": "Allows clients to request that the compositor creates its EGLStream.",
+ "Homepage": "https://github.com/NVIDIA/egl-wayland",
+ "LicenseId": "MIT",
+ "License": "MIT License",
+ "LicenseFile": "MIT_LICENSE.txt",
+ "Copyright": "Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved."
+ }
]
diff --git a/src/3rdparty/protocol/wl-eglstream-controller.xml b/src/3rdparty/protocol/wl-eglstream-controller.xml
new file mode 100644
index 000000000..dea072e64
--- /dev/null
+++ b/src/3rdparty/protocol/wl-eglstream-controller.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wl_eglstream_controller">
+ <copyright>
+ Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+
+ 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 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="wl_eglstream_controller" version="1">
+ <request name="attach_eglstream_consumer">
+ <description summary="Create server stream and attach consumer">
+ Creates the corresponding server side EGLStream from the given wl_buffer
+ and attaches a consumer to it.
+ </description>
+ <arg name="wl_surface" type="object" interface="wl_surface"
+ summary="wl_surface corresponds to the client surface associated with
+ newly created eglstream"/>
+ <arg name="wl_resource" type="object" interface="wl_buffer"
+ summary="wl_resource corresponding to an EGLStream"/>
+ </request>
+ </interface>
+</protocol>
diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp
index 1ffaf3c89..45957629f 100644
--- a/src/client/qwaylandintegration.cpp
+++ b/src/client/qwaylandintegration.cpp
@@ -343,6 +343,7 @@ void QWaylandIntegration::initializeClientBufferIntegration()
if (targetKey.isEmpty()) {
if (mDisplay->hardwareIntegration()
+ && mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("wayland-eglstream-controller")
&& mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("linux-dmabuf-unstable-v1")) {
targetKey = mDisplay->hardwareIntegration()->clientBufferIntegration();
} else {
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
index 2301875c9..e81221fb5 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -359,8 +359,6 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect)
{
if (!(mShellSurface && mShellSurface->handleExpose(rect)))
QWindowSystemInterface::handleExposeEvent(window(), rect);
- else
- qCDebug(lcQpaWayland) << "sendExposeEvent: intercepted by shell extension, not sending";
mLastExposeGeometry = rect;
}
@@ -549,11 +547,18 @@ void QWaylandWindow::handleScreenRemoved(QScreen *qScreen)
void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
{
Q_ASSERT(!buffer->committed());
+ if (mFrameCallback) {
+ wl_callback_destroy(mFrameCallback);
+ mFrameCallback = nullptr;
+ }
+
if (buffer) {
- handleUpdate();
+ mFrameCallback = frame();
+ wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
+ mWaitingForFrameSync = true;
buffer->setBusy();
- QtWayland::wl_surface::attach(buffer->buffer(), x, y);
+ attach(buffer->buffer(), x, y);
} else {
QtWayland::wl_surface::attach(nullptr, 0, 0);
}
@@ -623,9 +628,11 @@ void QWaylandWindow::frameCallback(void *data, struct wl_callback *callback, uin
Q_UNUSED(callback);
QWaylandWindow *self = static_cast<QWaylandWindow*>(data);
- self->mWaitingForFrameCallback = false;
- if (self->mUpdateRequested)
+ self->mWaitingForFrameSync = false;
+ if (self->mUpdateRequested) {
+ self->mUpdateRequested = false;
self->deliverUpdateRequest();
+ }
}
QMutex QWaylandWindow::mFrameSyncMutex;
@@ -633,10 +640,10 @@ QMutex QWaylandWindow::mFrameSyncMutex;
void QWaylandWindow::waitForFrameSync()
{
QMutexLocker locker(&mFrameSyncMutex);
- if (!mWaitingForFrameCallback)
+ if (!mWaitingForFrameSync)
return;
mDisplay->flushRequests();
- while (mWaitingForFrameCallback)
+ while (mWaitingForFrameSync)
mDisplay->blockingReadEvents();
}
@@ -1037,88 +1044,12 @@ QVariant QWaylandWindow::property(const QString &name, const QVariant &defaultVa
return m_properties.value(name, defaultValue);
}
-void QWaylandWindow::timerEvent(QTimerEvent *event)
-{
- if (event->timerId() == mFallbackUpdateTimerId) {
- killTimer(mFallbackUpdateTimerId);
- mFallbackUpdateTimerId = -1;
-
- if (!isExposed()) {
- qCDebug(lcWaylandBackingstore) << "Fallback update timer: Window not exposed,"
- << "not delivering update request.";
- return;
- }
-
- if (mWaitingForUpdate && mUpdateRequested && !mWaitingForFrameCallback) {
- qCWarning(lcWaylandBackingstore) << "Delivering update request through fallback timer,"
- << "may not be in sync with display";
- deliverUpdateRequest();
- }
- }
-}
-
void QWaylandWindow::requestUpdate()
{
- if (mUpdateRequested)
- return;
-
- mUpdateRequested = true;
-
- // If we have a frame callback all is good and will be taken care of there
- if (mWaitingForFrameCallback)
- return;
-
- // If we've already called deliverUpdateRequest(), but haven't seen any attach+commit/swap yet
- if (mWaitingForUpdate) {
- // Ideally, we should just have returned here, but we're not guaranteed that the client
- // will actually update, so start this timer to deliver another request update after a while
- // *IF* the client doesn't update.
- int fallbackTimeout = 100;
- mFallbackUpdateTimerId = startTimer(fallbackTimeout);
- return;
- }
-
- // Some applications (such as Qt Quick) depend on updates being delivered asynchronously,
- // so use invokeMethod to delay the delivery a bit.
- QMetaObject::invokeMethod(this, [this] {
- // Things might have changed in the meantime
- if (mUpdateRequested && !mWaitingForUpdate && !mWaitingForFrameCallback)
- deliverUpdateRequest();
- }, Qt::QueuedConnection);
-}
-
-// Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly
-// with eglSwapBuffers) to know when it's time to commit the next one.
-// Can be called from the render thread (without locking anything) so make sure to not make races in this method.
-void QWaylandWindow::handleUpdate()
-{
- // TODO: Should sync subsurfaces avoid requesting frame callbacks?
-
- if (mFrameCallback) {
- wl_callback_destroy(mFrameCallback);
- mFrameCallback = nullptr;
- }
-
- if (mFallbackUpdateTimerId != -1) {
- // Ideally, we would stop the fallback timer here, but since we're on another thread,
- // it's not allowed. Instead we set mFallbackUpdateTimer to -1 here, so we'll just
- // ignore it if it times out before it's cleaned up by the invokeMethod call.
- int id = mFallbackUpdateTimerId;
- mFallbackUpdateTimerId = -1;
- QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
- }
-
- mFrameCallback = frame();
- wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
- mWaitingForFrameCallback = true;
- mWaitingForUpdate = false;
-}
-
-void QWaylandWindow::deliverUpdateRequest()
-{
- mUpdateRequested = false;
- mWaitingForUpdate = true;
- QPlatformWindow::deliverUpdateRequest();
+ if (!mWaitingForFrameSync)
+ QPlatformWindow::requestUpdate();
+ else
+ mUpdateRequested = true;
}
void QWaylandWindow::addAttachOffset(const QPoint point)
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
index 52cbc3e59..146767a1e 100644
--- a/src/client/qwaylandwindow_p.h
+++ b/src/client/qwaylandwindow_p.h
@@ -193,10 +193,7 @@ public:
bool startSystemMove(const QPoint &pos) override;
- void timerEvent(QTimerEvent *event) override;
void requestUpdate() override;
- void handleUpdate();
- void deliverUpdateRequest() override;
public slots:
void applyConfigure();
@@ -216,14 +213,10 @@ protected:
Qt::MouseButtons mMousePressedInContentArea = Qt::NoButton;
WId mWindowId;
- bool mWaitingForFrameCallback = false;
+ bool mWaitingForFrameSync = false;
struct ::wl_callback *mFrameCallback = nullptr;
QWaitCondition mFrameSyncWait;
- // True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer
- bool mWaitingForUpdate = false;
- int mFallbackUpdateTimerId = -1;
-
QMutex mResizeLock;
bool mWaitingToApplyConfigure = false;
bool mCanResize = true;
diff --git a/src/compositor/compositor_api/qwaylandcompositor.h b/src/compositor/compositor_api/qwaylandcompositor.h
index 6bc3c1886..c343d6f84 100644
--- a/src/compositor/compositor_api/qwaylandcompositor.h
+++ b/src/compositor/compositor_api/qwaylandcompositor.h
@@ -121,8 +121,6 @@ public:
QWaylandSeat *defaultSeat() const;
- QWaylandView *createSurfaceView(QWaylandSurface *surface);
-
QWaylandSeat *seatFor(QInputEvent *inputEvent);
bool useHardwareIntegrationExtension() const;
diff --git a/src/compositor/compositor_api/qwaylandquickoutput.cpp b/src/compositor/compositor_api/qwaylandquickoutput.cpp
index 79e4fdec7..c6294eead 100644
--- a/src/compositor/compositor_api/qwaylandquickoutput.cpp
+++ b/src/compositor/compositor_api/qwaylandquickoutput.cpp
@@ -66,7 +66,7 @@ void QWaylandQuickOutput::initialize()
this, &QWaylandQuickOutput::updateStarted,
Qt::DirectConnection);
- connect(quickWindow, &QQuickWindow::beforeRendering,
+ connect(quickWindow, &QQuickWindow::afterRendering,
this, &QWaylandQuickOutput::doFrameCallbacks);
}
diff --git a/src/compositor/compositor_api/qwaylandsurface.cpp b/src/compositor/compositor_api/qwaylandsurface.cpp
index eef51283c..cc61fb17a 100644
--- a/src/compositor/compositor_api/qwaylandsurface.cpp
+++ b/src/compositor/compositor_api/qwaylandsurface.cpp
@@ -983,7 +983,6 @@ void QWaylandSurfacePrivate::Subsurface::subsurface_set_desync(wl_subsurface::Re
* This signal is emitted when a wl_subsurface, \a child, has been added to the surface.
*/
- void surfaceDestroyed();
/*!
* \qmlsignal QtWaylandCompositor::WaylandSurface::surfaceDestroyed()
*
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
index 0cbbe5389..a8ee9a43a 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
@@ -315,9 +315,7 @@ QWaylandGLContext::QWaylandGLContext(EGLDisplay eglDisplay, QWaylandDisplay *dis
mSupportNonBlockingSwap = false;
}
if (!mSupportNonBlockingSwap) {
- qWarning(lcQpaWayland) << "Non-blocking swap buffers not supported."
- << "Subsurface rendering can be affected."
- << "It may also cause the event loop to freeze in some situations";
+ qWarning() << "Non-blocking swap buffers not supported. Subsurface rendering can be affected.";
}
updateGLFormat();
@@ -401,8 +399,13 @@ bool QWaylandGLContext::makeCurrent(QPlatformSurface *surface)
QWaylandEglWindow *window = static_cast<QWaylandEglWindow *>(surface);
EGLSurface eglSurface = window->eglSurface();
- if (!window->needToUpdateContentFBO() && (eglSurface != EGL_NO_SURFACE && eglGetCurrentContext() == m_context && eglGetCurrentSurface(EGL_DRAW) == eglSurface))
+ if (!window->needToUpdateContentFBO() && (eglSurface != EGL_NO_SURFACE)) {
+ if (!eglMakeCurrent(m_eglDisplay, eglSurface, eglSurface, m_context)) {
+ qWarning("QWaylandGLContext::makeCurrent: eglError: %x, this: %p \n", eglGetError(), this);
+ return false;
+ }
return true;
+ }
if (window->isExposed())
window->setCanResize(false);
@@ -552,10 +555,20 @@ void QWaylandGLContext::swapBuffers(QPlatformSurface *surface)
m_blitter->blit(window);
}
- window->handleUpdate();
- int swapInterval = mSupportNonBlockingSwap ? 0 : m_format.swapInterval();
- eglSwapInterval(m_eglDisplay, swapInterval);
- eglSwapBuffers(m_eglDisplay, eglSurface);
+
+ QWaylandSubSurface *sub = window->subSurfaceWindow();
+ if (sub) {
+ QMutexLocker l(sub->syncMutex());
+
+ int si = (sub->isSync() && mSupportNonBlockingSwap) ? 0 : m_format.swapInterval();
+
+ eglSwapInterval(m_eglDisplay, si);
+ eglSwapBuffers(m_eglDisplay, eglSurface);
+ } else {
+ eglSwapInterval(m_eglDisplay, m_format.swapInterval());
+ eglSwapBuffers(m_eglDisplay, eglSurface);
+ }
+
window->setCanResize(true);
}
diff --git a/src/hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.pri b/src/hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.pri
new file mode 100644
index 000000000..931475ef6
--- /dev/null
+++ b/src/hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.pri
@@ -0,0 +1,16 @@
+INCLUDEPATH += $$PWD
+
+QMAKE_USE_PRIVATE += egl wayland-server wayland-egl
+
+CONFIG += wayland-scanner
+WAYLANDSERVERSOURCES += $$PWD/../../../3rdparty/protocol/wl-eglstream-controller.xml
+
+QT += egl_support-private
+
+SOURCES += \
+ $$PWD/waylandeglstreamintegration.cpp \
+ $$PWD/waylandeglstreamcontroller.cpp
+
+HEADERS += \
+ $$PWD/waylandeglstreamintegration.h \
+ $$PWD/waylandeglstreamcontroller.h
diff --git a/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamcontroller.cpp b/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamcontroller.cpp
new file mode 100644
index 000000000..09859d7e7
--- /dev/null
+++ b/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamcontroller.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "waylandeglstreamcontroller.h"
+#include "waylandeglstreamintegration.h"
+
+#include <QtWaylandCompositor/QWaylandCompositor>
+
+#include <unistd.h>
+
+QT_BEGIN_NAMESPACE
+
+
+
+WaylandEglStreamController::WaylandEglStreamController(wl_display *display, WaylandEglStreamClientBufferIntegration *clientBufferIntegration)
+ : wl_eglstream_controller(display, 1 /*version*/)
+ , m_clientBufferIntegration(clientBufferIntegration)
+{
+}
+
+void WaylandEglStreamController::eglstream_controller_attach_eglstream_consumer(Resource *resource, struct ::wl_resource *wl_surface, struct ::wl_resource *wl_buffer)
+{
+ Q_UNUSED(resource);
+ m_clientBufferIntegration->attachEglStreamConsumer(wl_surface, wl_buffer);
+}
+
+QT_END_NAMESPACE
diff --git a/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamcontroller.h b/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamcontroller.h
new file mode 100644
index 000000000..3a7fcee78
--- /dev/null
+++ b/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamcontroller.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** 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 WAYLANDEGLSTREAMCONTROLLER_H
+#define WAYLANDEGLSTREAMCONTROLLER_H
+
+#include "qwayland-server-wl-eglstream-controller.h"
+
+#include <QtWaylandCompositor/private/qwayland-server-wayland.h>
+#include <QtWaylandCompositor/private/qwlclientbufferintegration_p.h>
+
+#include <QtCore/QObject>
+#include <QtCore/QHash>
+#include <QtCore/QSize>
+#include <QtCore/QTextStream>
+#include <QtGui/QOpenGLTexture>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandCompositor;
+class QWaylandResource;
+class WaylandEglStreamClientBufferIntegration;
+
+class WaylandEglStreamController : public QtWaylandServer::wl_eglstream_controller
+{
+public:
+ explicit WaylandEglStreamController(wl_display *display, WaylandEglStreamClientBufferIntegration *clientBufferIntegration);
+
+protected:
+ void eglstream_controller_attach_eglstream_consumer(Resource *resource, struct ::wl_resource *wl_surface, struct ::wl_resource *wl_buffer) override;
+
+private:
+ WaylandEglStreamClientBufferIntegration *m_clientBufferIntegration;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // WAYLANDEGLSTREAMCONTROLLER_H
diff --git a/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.cpp b/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.cpp
new file mode 100644
index 000000000..1493e9fc0
--- /dev/null
+++ b/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.cpp
@@ -0,0 +1,439 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "waylandeglstreamintegration.h"
+#include "waylandeglstreamcontroller.h"
+
+#include <QtWaylandCompositor/QWaylandCompositor>
+#include <QtGui/QGuiApplication>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLTexture>
+#include <QtGui/QOffscreenSurface>
+
+#include <QtEglSupport/private/qeglstreamconvenience_p.h>
+#include <qpa/qplatformnativeinterface.h>
+
+#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
+#include <QtWaylandCompositor/private/qwlbuffermanager_p.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <unistd.h>
+
+#ifndef GL_TEXTURE_EXTERNAL_OES
+#define GL_TEXTURE_EXTERNAL_OES 0x8D65
+#endif
+
+#ifndef EGL_WAYLAND_BUFFER_WL
+#define EGL_WAYLAND_BUFFER_WL 0x31D5
+#endif
+
+#ifndef EGL_WAYLAND_EGLSTREAM_WL
+#define EGL_WAYLAND_EGLSTREAM_WL 0x334B
+#endif
+
+#ifndef EGL_WAYLAND_PLANE_WL
+#define EGL_WAYLAND_PLANE_WL 0x31D6
+#endif
+
+#ifndef EGL_WAYLAND_Y_INVERTED_WL
+#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
+#endif
+
+#ifndef EGL_TEXTURE_RGB
+#define EGL_TEXTURE_RGB 0x305D
+#endif
+
+#ifndef EGL_TEXTURE_RGBA
+#define EGL_TEXTURE_RGBA 0x305E
+#endif
+
+#ifndef EGL_TEXTURE_EXTERNAL_WL
+#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
+#endif
+
+#ifndef EGL_TEXTURE_Y_U_V_WL
+#define EGL_TEXTURE_Y_U_V_WL 0x31D7
+#endif
+
+#ifndef EGL_TEXTURE_Y_UV_WL
+#define EGL_TEXTURE_Y_UV_WL 0x31D8
+#endif
+
+#ifndef EGL_TEXTURE_Y_XUXV_WL
+#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
+#endif
+
+#ifndef EGL_PLATFORM_X11_KHR
+#define EGL_PLATFORM_X11_KHR 0x31D5
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/* Needed for compatibility with Mesa older than 10.0. */
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL_compat) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
+
+#ifndef EGL_WL_bind_wayland_display
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
+#endif
+
+static const char *
+egl_error_string(EGLint code)
+{
+#define MYERRCODE(x) case x: return #x;
+ switch (code) {
+ MYERRCODE(EGL_SUCCESS)
+ MYERRCODE(EGL_NOT_INITIALIZED)
+ MYERRCODE(EGL_BAD_ACCESS)
+ MYERRCODE(EGL_BAD_ALLOC)
+ MYERRCODE(EGL_BAD_ATTRIBUTE)
+ MYERRCODE(EGL_BAD_CONTEXT)
+ MYERRCODE(EGL_BAD_CONFIG)
+ MYERRCODE(EGL_BAD_CURRENT_SURFACE)
+ MYERRCODE(EGL_BAD_DISPLAY)
+ MYERRCODE(EGL_BAD_SURFACE)
+ MYERRCODE(EGL_BAD_MATCH)
+ MYERRCODE(EGL_BAD_PARAMETER)
+ MYERRCODE(EGL_BAD_NATIVE_PIXMAP)
+ MYERRCODE(EGL_BAD_NATIVE_WINDOW)
+ MYERRCODE(EGL_CONTEXT_LOST)
+ default:
+ return "unknown";
+ }
+#undef MYERRCODE
+}
+
+struct BufferState
+{
+ BufferState() = default;
+
+ EGLint egl_format = EGL_TEXTURE_EXTERNAL_WL;
+ QOpenGLTexture *textures[3] = {};
+ EGLStreamKHR egl_stream = EGL_NO_STREAM_KHR;
+
+ bool isYInverted = false;
+ QSize size;
+};
+
+class WaylandEglStreamClientBufferIntegrationPrivate
+{
+public:
+ WaylandEglStreamClientBufferIntegrationPrivate() = default;
+
+ bool ensureContext();
+ bool initEglStream(WaylandEglStreamClientBuffer *buffer, struct ::wl_resource *bufferHandle);
+ void handleEglstreamTexture(WaylandEglStreamClientBuffer *buffer);
+ void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { orphanedTextures << texture; }
+ void deleteOrphanedTextures();
+
+ EGLDisplay egl_display = EGL_NO_DISPLAY;
+ bool display_bound = false;
+ QOffscreenSurface *offscreenSurface = nullptr;
+ QOpenGLContext *localContext = nullptr;
+ QVector<QOpenGLTexture *> orphanedTextures;
+
+ WaylandEglStreamController *eglStreamController = nullptr;
+
+ PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display = nullptr;
+ PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display = nullptr;
+ PFNEGLQUERYWAYLANDBUFFERWL_compat egl_query_wayland_buffer = nullptr;
+
+ QEGLStreamConvenience *funcs = nullptr;
+ static WaylandEglStreamClientBufferIntegrationPrivate *get(WaylandEglStreamClientBufferIntegration *integration) {
+ return shuttingDown ? nullptr : integration->d_ptr.data();
+ }
+
+ static bool shuttingDown;
+};
+
+bool WaylandEglStreamClientBufferIntegrationPrivate::shuttingDown = false;
+
+void WaylandEglStreamClientBufferIntegrationPrivate::deleteOrphanedTextures()
+{
+ Q_ASSERT(QOpenGLContext::currentContext());
+ qDeleteAll(orphanedTextures);
+ orphanedTextures.clear();
+}
+
+bool WaylandEglStreamClientBufferIntegrationPrivate::ensureContext()
+{
+ bool localContextNeeded = false;
+ if (!QOpenGLContext::currentContext()) {
+ if (!localContext && QOpenGLContext::globalShareContext()) {
+ localContext = new QOpenGLContext;
+ localContext->setShareContext(QOpenGLContext::globalShareContext());
+ localContext->create();
+ }
+ if (localContext) {
+ if (!offscreenSurface) {
+ offscreenSurface = new QOffscreenSurface;
+ offscreenSurface->setFormat(localContext->format());
+ offscreenSurface->create();
+ }
+ localContext->makeCurrent(offscreenSurface);
+ localContextNeeded = true;
+ }
+ }
+ return localContextNeeded;
+}
+
+
+bool WaylandEglStreamClientBufferIntegrationPrivate::initEglStream(WaylandEglStreamClientBuffer *buffer, wl_resource *bufferHandle)
+{
+ BufferState &state = *buffer->d;
+ state.egl_format = EGL_TEXTURE_EXTERNAL_WL;
+ state.isYInverted = false;
+
+ EGLNativeFileDescriptorKHR streamFd = EGL_NO_FILE_DESCRIPTOR_KHR;
+
+ if (egl_query_wayland_buffer(egl_display, bufferHandle, EGL_WAYLAND_BUFFER_WL, &streamFd)) {
+ state.egl_stream = funcs->create_stream_from_file_descriptor(egl_display, streamFd);
+ close(streamFd);
+ } else {
+ EGLAttrib stream_attribs[] = {
+ EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib)bufferHandle,
+ EGL_NONE
+ };
+ state.egl_stream = funcs->create_stream_attrib_nv(egl_display, stream_attribs);
+ }
+
+ if (state.egl_stream == EGL_NO_STREAM_KHR) {
+ qWarning("%s:%d: eglCreateStreamFromFileDescriptorKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError());
+ return false;
+ }
+
+ bool usingLocalContext = ensureContext();
+
+ Q_ASSERT(QOpenGLContext::currentContext());
+
+ auto texture = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(GL_TEXTURE_EXTERNAL_OES));
+ texture->create();
+ state.textures[0] = texture; // TODO: support multiple planes
+
+ texture->bind();
+
+ auto newStream = funcs->stream_consumer_gltexture(egl_display, state.egl_stream);
+ if (usingLocalContext)
+ localContext->doneCurrent();
+
+ if (!newStream) {
+ EGLint code = eglGetError();
+ qWarning() << "Could not initialize EGLStream:" << egl_error_string(code) << hex << (long)code;
+ funcs->destroy_stream(egl_display, state.egl_stream);
+ state.egl_stream = EGL_NO_STREAM_KHR;
+ return false;
+ }
+ return true;
+}
+
+void WaylandEglStreamClientBufferIntegrationPrivate::handleEglstreamTexture(WaylandEglStreamClientBuffer *buffer)
+{
+ bool usingLocalContext = ensureContext();
+
+ BufferState &state = *buffer->d;
+ auto texture = state.textures[0];
+
+ // EGLStream requires calling acquire on every frame.
+ texture->bind();
+ EGLint stream_state;
+ funcs->query_stream(egl_display, state.egl_stream, EGL_STREAM_STATE_KHR, &stream_state);
+
+ if (stream_state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) {
+ if (funcs->stream_consumer_acquire(egl_display, state.egl_stream) != EGL_TRUE)
+ qWarning("%s:%d: eglStreamConsumerAcquireKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError());
+ }
+
+ if (usingLocalContext)
+ localContext->doneCurrent();
+}
+
+
+WaylandEglStreamClientBufferIntegration::WaylandEglStreamClientBufferIntegration()
+ : d_ptr(new WaylandEglStreamClientBufferIntegrationPrivate)
+{
+}
+
+WaylandEglStreamClientBufferIntegration::~WaylandEglStreamClientBufferIntegration()
+{
+ WaylandEglStreamClientBufferIntegrationPrivate::shuttingDown = true;
+}
+
+void WaylandEglStreamClientBufferIntegration::attachEglStreamConsumer(struct ::wl_resource *wl_surface, struct ::wl_resource *wl_buffer)
+{
+ Q_D(WaylandEglStreamClientBufferIntegration);
+ Q_UNUSED(wl_surface);
+
+ // NOTE: must use getBuffer to create the buffer here, so the buffer will end up in the buffer manager's hash
+
+ auto *bufferManager = QWaylandCompositorPrivate::get(m_compositor)->bufferManager();
+ auto *clientBuffer = static_cast<WaylandEglStreamClientBuffer*>(bufferManager->getBuffer(wl_buffer));
+
+ d->initEglStream(clientBuffer, wl_buffer);
+}
+
+void WaylandEglStreamClientBufferIntegration::initializeHardware(struct wl_display *display)
+{
+ Q_D(WaylandEglStreamClientBufferIntegration);
+
+ const bool ignoreBindDisplay = !qgetenv("QT_WAYLAND_IGNORE_BIND_DISPLAY").isEmpty();
+
+ QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
+ if (!nativeInterface) {
+ qWarning("QtCompositor: Failed to initialize EGL display. No native platform interface available.");
+ return;
+ }
+
+ d->egl_display = nativeInterface->nativeResourceForIntegration("EglDisplay");
+ if (!d->egl_display) {
+ qWarning("QtCompositor: Failed to initialize EGL display. Could not get EglDisplay for window.");
+ return;
+ }
+
+ const char *extensionString = eglQueryString(d->egl_display, EGL_EXTENSIONS);
+ if ((!extensionString || !strstr(extensionString, "EGL_WL_bind_wayland_display")) && !ignoreBindDisplay) {
+ qWarning("QtCompositor: Failed to initialize EGL display. There is no EGL_WL_bind_wayland_display extension.");
+ return;
+ }
+
+ d->egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL"));
+ d->egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglUnbindWaylandDisplayWL"));
+ if ((!d->egl_bind_wayland_display || !d->egl_unbind_wayland_display) && !ignoreBindDisplay) {
+ qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL.");
+ return;
+ }
+
+ d->egl_query_wayland_buffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL_compat>(eglGetProcAddress("eglQueryWaylandBufferWL"));
+ if (!d->egl_query_wayland_buffer) {
+ qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglQueryWaylandBufferWL.");
+ return;
+ }
+
+ if (d->egl_bind_wayland_display && d->egl_unbind_wayland_display) {
+ d->display_bound = d->egl_bind_wayland_display(d->egl_display, display);
+ if (!d->display_bound) {
+ if (!ignoreBindDisplay) {
+ qWarning("QtCompositor: Failed to initialize EGL display. Could not bind Wayland display.");
+ return;
+ } else {
+ qWarning("QtCompositor: Could not bind Wayland display. Ignoring.");
+ }
+ }
+ }
+
+ d->eglStreamController = new WaylandEglStreamController(display, this);
+
+ d->funcs = new QEGLStreamConvenience;
+ d->funcs->initialize(d->egl_display);
+}
+
+QtWayland::ClientBuffer *WaylandEglStreamClientBufferIntegration::createBufferFor(wl_resource *buffer)
+{
+ if (wl_shm_buffer_get(buffer))
+ return nullptr;
+
+ return new WaylandEglStreamClientBuffer(this, buffer);
+}
+
+
+WaylandEglStreamClientBuffer::WaylandEglStreamClientBuffer(WaylandEglStreamClientBufferIntegration *integration, wl_resource *buffer)
+ : ClientBuffer(buffer)
+ , m_integration(integration)
+{
+ auto *p = WaylandEglStreamClientBufferIntegrationPrivate::get(m_integration);
+ d = new BufferState;
+ if (buffer && !wl_shm_buffer_get(buffer)) {
+ EGLint width, height;
+ p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_WIDTH, &width);
+ p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_HEIGHT, &height);
+ d->size = QSize(width, height);
+ }
+}
+
+WaylandEglStreamClientBuffer::~WaylandEglStreamClientBuffer()
+{
+ auto *p = WaylandEglStreamClientBufferIntegrationPrivate::get(m_integration);
+
+ if (p) {
+ if (d->egl_stream)
+ p->funcs->destroy_stream(p->egl_display, d->egl_stream);
+
+ for (auto *texture : d->textures)
+ p->deleteGLTextureWhenPossible(texture);
+ }
+ delete d;
+}
+
+
+QWaylandBufferRef::BufferFormatEgl WaylandEglStreamClientBuffer::bufferFormatEgl() const
+{
+ return QWaylandBufferRef::BufferFormatEgl_EXTERNAL_OES;
+}
+
+
+QSize WaylandEglStreamClientBuffer::size() const
+{
+ return d->size;
+}
+
+QWaylandSurface::Origin WaylandEglStreamClientBuffer::origin() const
+{
+ return d->isYInverted ? QWaylandSurface::OriginTopLeft : QWaylandSurface::OriginBottomLeft;
+}
+
+QOpenGLTexture *WaylandEglStreamClientBuffer::toOpenGlTexture(int plane)
+{
+ auto *p = WaylandEglStreamClientBufferIntegrationPrivate::get(m_integration);
+ // At this point we should have a valid OpenGL context, so it's safe to destroy textures
+ p->deleteOrphanedTextures();
+
+ if (!m_buffer)
+ return nullptr;
+
+ return d->textures[plane];
+}
+
+void WaylandEglStreamClientBuffer::setCommitted(QRegion &damage)
+{
+ ClientBuffer::setCommitted(damage);
+ auto *p = WaylandEglStreamClientBufferIntegrationPrivate::get(m_integration);
+ p->handleEglstreamTexture(this);
+}
+
+QT_END_NAMESPACE
diff --git a/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.h b/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.h
new file mode 100644
index 000000000..d19302049
--- /dev/null
+++ b/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef WAYLANDEGLSTREAMINTEGRATION_H
+#define WAYLANDEGLSTREAMINTEGRATION_H
+
+#include <QtWaylandCompositor/private/qwlclientbufferintegration_p.h>
+#include <QtCore/QScopedPointer>
+#include <QtWaylandCompositor/private/qwlclientbuffer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class WaylandEglStreamClientBufferIntegrationPrivate;
+
+class WaylandEglStreamClientBufferIntegration : public QtWayland::ClientBufferIntegration
+{
+ Q_DECLARE_PRIVATE(WaylandEglStreamClientBufferIntegration)
+public:
+ WaylandEglStreamClientBufferIntegration();
+ ~WaylandEglStreamClientBufferIntegration() override;
+
+ void initializeHardware(struct ::wl_display *display) override;
+
+ QtWayland::ClientBuffer *createBufferFor(wl_resource *buffer) override;
+
+ void attachEglStreamConsumer(struct ::wl_resource *wl_surface, struct ::wl_resource *wl_buffer);
+
+private:
+ Q_DISABLE_COPY(WaylandEglStreamClientBufferIntegration)
+ QScopedPointer<WaylandEglStreamClientBufferIntegrationPrivate> d_ptr;
+};
+
+struct BufferState;
+
+class WaylandEglStreamClientBuffer : public QtWayland::ClientBuffer
+{
+public:
+ ~WaylandEglStreamClientBuffer() override;
+
+ QWaylandBufferRef::BufferFormatEgl bufferFormatEgl() const override;
+ QSize size() const override;
+ QWaylandSurface::Origin origin() const override;
+ QOpenGLTexture *toOpenGlTexture(int plane) override;
+ void setCommitted(QRegion &damage) override;
+
+private:
+ friend class WaylandEglStreamClientBufferIntegration;
+ friend class WaylandEglStreamClientBufferIntegrationPrivate;
+
+ WaylandEglStreamClientBuffer(WaylandEglStreamClientBufferIntegration* integration, wl_resource *bufferResource);
+
+ BufferState *d = nullptr;
+ WaylandEglStreamClientBufferIntegration *m_integration = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // WAYLANDEGLSTREAMINTEGRATION_H
diff --git a/src/plugins/hardwareintegration/compositor/compositor.pro b/src/plugins/hardwareintegration/compositor/compositor.pro
index 94e0f8bf1..59ea91414 100644
--- a/src/plugins/hardwareintegration/compositor/compositor.pro
+++ b/src/plugins/hardwareintegration/compositor/compositor.pro
@@ -21,4 +21,8 @@ qtConfig(wayland-shm-emulation-server-buffer): \
qtConfig(wayland-dmabuf-server-buffer): \
SUBDIRS += dmabuf-server
+qtConfig(wayland-egl): \
+ SUBDIRS += wayland-eglstream-controller
+
+
SUBDIRS += hardwarelayer
diff --git a/src/plugins/hardwareintegration/compositor/wayland-eglstream-controller/main.cpp b/src/plugins/hardwareintegration/compositor/wayland-eglstream-controller/main.cpp
new file mode 100644
index 000000000..8e1d50908
--- /dev/null
+++ b/src/plugins/hardwareintegration/compositor/wayland-eglstream-controller/main.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtWaylandCompositor/private/qwlclientbufferintegrationfactory_p.h>
+#include <QtWaylandCompositor/private/qwlclientbufferintegrationplugin_p.h>
+#include "waylandeglstreamintegration.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandEglStreamClientBufferIntegrationPlugin : public QtWayland::ClientBufferIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QtWaylandClientBufferIntegrationFactoryInterface_iid FILE "wayland-eglstream-controller.json")
+public:
+ QtWayland::ClientBufferIntegration *create(const QString& key, const QStringList& paramList) override;
+};
+
+QtWayland::ClientBufferIntegration *QWaylandEglStreamClientBufferIntegrationPlugin::create(const QString& key, const QStringList& paramList)
+{
+ Q_UNUSED(paramList);
+ Q_UNUSED(key);
+ return new WaylandEglStreamClientBufferIntegration();
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.json b/src/plugins/hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.json
new file mode 100644
index 000000000..0c94bb776
--- /dev/null
+++ b/src/plugins/hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "wayland-eglstream-controller" ]
+}
diff --git a/src/plugins/hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.pro b/src/plugins/hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.pro
new file mode 100644
index 000000000..f1ca7183a
--- /dev/null
+++ b/src/plugins/hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.pro
@@ -0,0 +1,12 @@
+QT = waylandcompositor waylandcompositor-private core-private gui-private
+
+OTHER_FILES += wayland-eglstream-controller.json
+
+SOURCES += \
+ main.cpp \
+
+include(../../../../hardwareintegration/compositor/wayland-eglstream-controller/wayland-eglstream-controller.pri)
+
+PLUGIN_TYPE = wayland-graphics-integration-server
+PLUGIN_CLASS_NAME = QWaylandEglStreamBufferIntegrationPlugin
+load(qt_plugin)
diff --git a/tests/manual/wip-cpp-compositor/README.md b/tests/manual/wip-cpp-compositor/README.md
new file mode 100644
index 000000000..f5af93a71
--- /dev/null
+++ b/tests/manual/wip-cpp-compositor/README.md
@@ -0,0 +1,6 @@
+# Reference C++ Compositor
+
+An example showing what is required to get a C++-based compositor up and
+running implementing basic functionality such as pointer and keyboard input as
+well as resizing and moving windows. This example uses the stable xdg-shell
+protocol.
diff --git a/tests/manual/wip-cpp-compositor/compositor.cpp b/tests/manual/wip-cpp-compositor/compositor.cpp
new file mode 100644
index 000000000..d65c5f0c3
--- /dev/null
+++ b/tests/manual/wip-cpp-compositor/compositor.cpp
@@ -0,0 +1,290 @@
+/****************************************************************************
+**
+** 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 "compositor.h"
+#include "window.h"
+
+#include <QtWaylandCompositor/QWaylandOutput>
+#include <QtWaylandCompositor/QWaylandXdgShell>
+#include <QtWaylandCompositor/QWaylandSeat>
+
+#include <QOpenGLFunctions>
+
+QOpenGLTexture *View::getTexture() {
+ if (advance())
+ m_texture = currentBuffer().toOpenGLTexture();
+ return m_texture;
+}
+
+void View::setGlobalPosition(const QPoint &position)
+{
+ if (m_globalPosition == position)
+ return;
+
+ m_globalPosition = position;
+ emit globalPositionChanged();
+}
+
+QPoint View::mapToLocal(const QPoint &globalPosition) const
+{
+ return globalPosition - this->globalPosition();
+}
+
+void View::updateAnchoredPosition()
+{
+ QPoint offset;
+ QSize size = surface()->size();
+ QSize delta = size - m_lastSize;
+ if (m_anchorEdges & Qt::RightEdge)
+ offset.setX(-delta.width());
+ if (m_anchorEdges & Qt::BottomEdge)
+ offset.setY(-delta.height());
+ setGlobalPosition(globalPosition() + offset);
+ m_lastSize = size;
+}
+
+void View::handleResizeMove(const QPoint &delta)
+{
+ Q_UNUSED(delta);
+ qWarning() << "Resize not implemented for this view";
+}
+
+ToplevelView::ToplevelView(QWaylandXdgToplevel *toplevel)
+ : m_toplevel(toplevel)
+{
+ QWaylandXdgSurface *xdgSurface = toplevel->xdgSurface();
+ setSurface(xdgSurface->surface());
+ connect(toplevel, &QWaylandXdgToplevel::startMove, this, &View::startMove);
+ connect(toplevel, &QWaylandXdgToplevel::startResize, this, [this](QWaylandSeat *, Qt::Edges edges) {
+ m_resize.edges = edges;
+ m_resize.initialSize = m_toplevel->xdgSurface()->windowGeometry().size();
+ Qt::Edges opposite = edges ^ Qt::Edges(0b1111);
+ setAnchorEdges(opposite);
+ emit startResize();
+ });
+ QVector<QWaylandXdgToplevel::State> states{QWaylandXdgToplevel::ActivatedState};
+ toplevel->sendConfigure(QSize(0, 0), states);
+}
+
+void ToplevelView::handleResizeMove(const QPoint &delta)
+{
+ QSize newSize = m_toplevel->sizeForResize(m_resize.initialSize, delta, m_resize.edges);
+ m_toplevel->sendResizing(newSize);
+}
+
+void ToplevelView::handleResizeRelease()
+{
+ setAnchorEdges({});
+ m_resize.edges = {};
+ m_resize.initialSize = {};
+}
+
+Compositor::~Compositor()
+{
+ delete m_xdgShell;
+}
+
+void Compositor::create()
+{
+ QWaylandCompositor::create();
+
+ m_xdgShell = new QWaylandXdgShell(this);
+ connect(m_xdgShell, &QWaylandXdgShell::toplevelCreated, this, &Compositor::handleXdgToplevelCreated);
+}
+
+View *Compositor::viewAt(const QPoint &position)
+{
+ // Since views are stored in painting order (back to front), we have to iterate backwards
+ // to find the topmost view at the given point
+ for (auto it = m_views.crbegin(); it != m_views.crend(); ++it) {
+ View *view = *it;
+ if (view->globalGeometry().contains(position))
+ return view;
+ }
+ return nullptr;
+}
+
+void Compositor::raise(View *view)
+{
+ m_views.removeAll(view);
+ m_views << view;
+ defaultSeat()->setKeyboardFocus(view->surface());
+ triggerRender();
+}
+
+void Compositor::handleMousePress(const QPoint &position, Qt::MouseButton button)
+{
+ if (m_grab.state == Grab::None) {
+ m_grab.view = viewAt(position);
+ if (m_grab.view) {
+ m_grab.state = Grab::Input;
+ m_grab.startGlobalPosition = position;
+ m_grab.startLocalPosition = m_grab.view->mapToLocal(position);
+ raise(m_grab.view);
+ }
+ }
+
+ switch (m_grab.state) {
+ case Grab::Input: {
+ auto *seat = defaultSeat();
+ seat->sendMouseMoveEvent(m_grab.view, m_grab.view->mapToLocal(position));
+ seat->sendMousePressEvent(button);
+ break;
+ }
+ case Grab::Move:
+ case Grab::Resize:
+ case Grab::None:
+ break;
+ }
+}
+
+void Compositor::handleMouseRelease(const QPoint &position, Qt::MouseButton button, Qt::MouseButtons buttons)
+{
+ auto *seat = defaultSeat();
+
+ switch (m_grab.state) {
+ case Grab::Input:
+ seat->sendMouseMoveEvent(m_grab.view, m_grab.view->mapToLocal(position));
+ seat->sendMouseReleaseEvent(button);
+ if (buttons == Qt::NoButton) {
+ View *newView = viewAt(position);
+ if (newView != m_grab.view) {
+ seat->setMouseFocus(newView);
+ if (newView)
+ seat->sendMouseMoveEvent(newView, newView->mapToLocal(position));
+ }
+ m_grab.view = nullptr;
+ m_grab.state = Grab::None;
+ }
+ break;
+ case Grab::Move:
+ case Grab::Resize:
+ m_grab.state = Grab::None;
+ m_grab.view = nullptr;
+ if (View *view = viewAt(position))
+ seat->sendMouseMoveEvent(view, view->mapToLocal(position));
+ break;
+ case Grab::None:
+ if (View *view = viewAt(position))
+ seat->sendMouseMoveEvent(view, view->mapToLocal(position));
+ break;
+ }
+}
+
+void Compositor::handleMouseMove(const QPoint &position)
+{
+ switch (m_grab.state) {
+ case Grab::Input:
+ defaultSeat()->sendMouseMoveEvent(m_grab.view, m_grab.view->mapToLocal(position));
+ break;
+ case Grab::None:
+ if (View *view = viewAt(position))
+ defaultSeat()->sendMouseMoveEvent(view, view->mapToLocal(position));
+ break;
+ case Grab::Resize:
+ m_grab.view->handleResizeMove(position - m_grab.startGlobalPosition);
+ break;
+ case Grab::Move:
+ m_grab.view->setGlobalPosition(position - m_grab.startLocalPosition);
+ break;
+ }
+}
+
+void Compositor::handleMouseWheel(Qt::Orientation orientation, int delta)
+{
+ defaultSeat()->sendMouseWheelEvent(orientation, delta);
+}
+
+void Compositor::handleKeyPress(quint32 nativeScanCode)
+{
+ defaultSeat()->sendKeyPressEvent(nativeScanCode);
+}
+
+void Compositor::handleKeyRelease(quint32 nativeScanCode)
+{
+ defaultSeat()->sendKeyReleaseEvent(nativeScanCode);
+}
+
+void Compositor::handleXdgToplevelCreated(QWaylandXdgToplevel *toplevel, QWaylandXdgSurface *xdgSurface)
+{
+ Q_UNUSED(xdgSurface);
+ auto *view = new ToplevelView(toplevel);
+ addView(view);
+}
+
+void Compositor::addView(View *view)
+{
+ view->setOutput(outputFor(m_window));
+ m_views << view;
+ connect(view, &QWaylandView::surfaceDestroyed, this, &Compositor::handleViewSurfaceDestroyed);
+ connect(view, &View::globalPositionChanged, this, &Compositor::triggerRender);
+ connect(view->surface(), &QWaylandSurface::redraw, this, &Compositor::triggerRender);
+ connect(view, &View::startMove, this, [this, view](){
+ m_grab.view = view;
+ m_grab.state = Grab::Move;
+ });
+ connect(view, &View::startResize, this, [this, view]() {
+ m_grab.view = view;
+ m_grab.state = Grab::Resize;
+ });
+}
+
+void Compositor::handleViewSurfaceDestroyed()
+{
+ auto *view = qobject_cast<ToplevelView*>(sender());
+ m_views.removeAll(view);
+ delete view;
+ triggerRender();
+}
+
+void Compositor::triggerRender()
+{
+ m_window->requestUpdate();
+}
diff --git a/tests/manual/wip-cpp-compositor/compositor.h b/tests/manual/wip-cpp-compositor/compositor.h
new file mode 100644
index 000000000..5c2d6a2f5
--- /dev/null
+++ b/tests/manual/wip-cpp-compositor/compositor.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef COMPOSITOR_H
+#define COMPOSITOR_H
+
+#include <QtCore/QPointer>
+
+#include <QtWaylandCompositor/QWaylandCompositor>
+#include <QtWaylandCompositor/QWaylandSurface>
+#include <QtWaylandCompositor/QWaylandView>
+
+QT_BEGIN_NAMESPACE
+
+class Window;
+class QOpenGLTexture;
+class QWaylandXdgShell;
+class QWaylandXdgSurface;
+class QWaylandXdgToplevel;
+
+class View : public QWaylandView
+{
+ Q_OBJECT
+public:
+ explicit View() = default;
+ QOpenGLTexture *getTexture();
+ QSize size() const { return surface() ? surface()->size() : QSize(); }
+ QRect globalGeometry() const { return {globalPosition(), surface()->size()}; }
+ QPoint globalPosition() const { return m_globalPosition; }
+ void setGlobalPosition(const QPoint &position);
+ QPoint mapToLocal(const QPoint &globalPosition) const;
+ void setAnchorEdges(Qt::Edges edges) { m_anchorEdges = edges; }
+ void updateAnchoredPosition();
+
+ virtual void handleResizeMove(const QPoint &delta);
+ virtual void handleResizeRelease() {}
+
+signals:
+ void globalPositionChanged();
+ void startResize();
+ void startMove();
+
+private:
+ QOpenGLTexture *m_texture = nullptr;
+ QPoint m_globalPosition;
+ Qt::Edges m_anchorEdges;
+ QSize m_lastSize;
+};
+
+class ToplevelView : public View
+{
+ Q_OBJECT
+public:
+ explicit ToplevelView(QWaylandXdgToplevel *toplevel);
+ void handleResizeMove(const QPoint &delta) override;
+ void handleResizeRelease() override;
+
+private:
+ QWaylandXdgToplevel *m_toplevel = nullptr;
+ struct Resize {
+ QSize initialSize;
+ Qt::Edges edges;
+ } m_resize;
+};
+
+class Compositor : public QWaylandCompositor
+{
+ Q_OBJECT
+public:
+ explicit Compositor() = default;
+ ~Compositor() override;
+ void create() override;
+ void setWindow(Window *window) { m_window = window; }
+
+ QList<View *> views() const { return m_views; }
+ View *viewAt(const QPoint &position);
+
+ void raise(View *view);
+
+ void handleGlInitialized() { create(); }
+ 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 handleKeyPress(quint32 nativeScanCode);
+ void handleKeyRelease(quint32 nativeScanCode);
+
+signals:
+ void startMove();
+
+private slots:
+ void handleXdgToplevelCreated(QWaylandXdgToplevel *toplevel, QWaylandXdgSurface *xdgSurface);
+ void addView(View *view);
+ void handleViewSurfaceDestroyed();
+ void triggerRender();
+
+private:
+ Window *m_window = nullptr;
+ QWaylandXdgShell *m_xdgShell = nullptr;
+ QList<View *> m_views; // Sorted by painters algorithm (back to front)
+ struct Grab {
+ QPointer<View> view;
+ enum State { None, Input, Move, Resize };
+ State state = None;
+ QPoint startLocalPosition; // in View's coordinate system
+ QPoint startGlobalPosition;
+ } m_grab;
+};
+
+QT_END_NAMESPACE
+
+#endif // COMPOSITOR_H
diff --git a/tests/manual/wip-cpp-compositor/main.cpp b/tests/manual/wip-cpp-compositor/main.cpp
new file mode 100644
index 000000000..8ca5a6f33
--- /dev/null
+++ b/tests/manual/wip-cpp-compositor/main.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** 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 "window.h"
+#include "compositor.h"
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ Compositor compositor;
+ Window window(&compositor);
+ compositor.setWindow(&window);
+
+ window.resize(800, 600);
+ window.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/wip-cpp-compositor/window.cpp b/tests/manual/wip-cpp-compositor/window.cpp
new file mode 100644
index 000000000..b5b2581e0
--- /dev/null
+++ b/tests/manual/wip-cpp-compositor/window.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** 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 "window.h"
+#include "compositor.h"
+
+#include <QPainter>
+#include <QMatrix4x4>
+#include <QOpenGLFunctions>
+#include <QOpenGLTexture>
+#include <QMouseEvent>
+
+Window::Window(Compositor *compositor)
+ : m_compositor(compositor)
+ , m_output(new QWaylandOutput(compositor, this))
+{
+ m_output->setSizeFollowsWindow(true);
+}
+
+void Window::initializeGL()
+{
+ m_textureBlitter.create();
+ m_compositor->handleGlInitialized();
+}
+
+void Window::paintGL()
+{
+ m_output->frameStarted();
+
+ QOpenGLFunctions *functions = context()->functions();
+ functions->glClearColor(.4f, .7f, .1f, 0.5f);
+ functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ GLenum currentTarget = GL_TEXTURE_2D;
+ m_textureBlitter.bind(currentTarget);
+ functions->glEnable(GL_BLEND);
+ functions->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ const auto views = m_compositor->views();
+
+ for (View *view : views) {
+ auto *texture = view->getTexture();
+ if (!texture)
+ continue;
+ if (texture->target() != currentTarget) {
+ currentTarget = texture->target();
+ m_textureBlitter.bind(currentTarget);
+ }
+ GLuint textureId = texture->textureId();
+
+ // Anchored position needs to be updated immediately before rendering or
+ // position and size might get out of sync.
+ view->updateAnchoredPosition();
+
+ QWaylandSurface *surface = view->surface();
+ if (surface && surface->hasContent()) {
+ QOpenGLTextureBlitter::Origin surfaceOrigin =
+ view->currentBuffer().origin() == QWaylandSurface::OriginTopLeft
+ ? QOpenGLTextureBlitter::OriginTopLeft
+ : QOpenGLTextureBlitter::OriginBottomLeft;
+ QMatrix4x4 targetTransform = QOpenGLTextureBlitter::targetTransform(view->globalGeometry(), QRect(QPoint(), size()));
+ m_textureBlitter.blit(textureId, targetTransform, surfaceOrigin);
+ }
+ }
+ m_textureBlitter.release();
+
+ m_output->sendFrameCallbacks();
+}
+
+void Window::mousePressEvent(QMouseEvent *event)
+{
+ m_compositor->handleMousePress(event->localPos().toPoint(), event->button());
+}
+
+void Window::mouseReleaseEvent(QMouseEvent *event)
+{
+ m_compositor->handleMouseRelease(event->localPos().toPoint(), event->button(), event->buttons());
+}
+
+void Window::mouseMoveEvent(QMouseEvent *event)
+{
+ m_compositor->handleMouseMove(event->localPos().toPoint());
+}
+
+void Window::wheelEvent(QWheelEvent *event)
+{
+ m_compositor->handleMouseWheel(event->orientation(), event->delta());
+}
+
+void Window::keyPressEvent(QKeyEvent *event)
+{
+ m_compositor->handleKeyPress(event->nativeScanCode());
+}
+
+void Window::keyReleaseEvent(QKeyEvent *event)
+{
+ m_compositor->handleKeyRelease(event->nativeScanCode());
+}
diff --git a/tests/manual/wip-cpp-compositor/window.h b/tests/manual/wip-cpp-compositor/window.h
new file mode 100644
index 000000000..bc71207ee
--- /dev/null
+++ b/tests/manual/wip-cpp-compositor/window.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <QOpenGLWindow>
+#include <QOpenGLTextureBlitter>
+
+QT_BEGIN_NAMESPACE
+
+class Compositor;
+class QWaylandOutput;
+
+class Window : public QOpenGLWindow
+{
+public:
+ explicit Window(Compositor *compositor);
+
+protected:
+ void initializeGL() override;
+ void paintGL() override;
+
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void wheelEvent(QWheelEvent *event) override;
+
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+
+private:
+ QOpenGLTextureBlitter m_textureBlitter;
+ Compositor *m_compositor = nullptr;
+ QWaylandOutput *m_output = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif //WINDOW_H
diff --git a/tests/manual/wip-cpp-compositor/wip-cpp-compositor.pro b/tests/manual/wip-cpp-compositor/wip-cpp-compositor.pro
new file mode 100644
index 000000000..546842acb
--- /dev/null
+++ b/tests/manual/wip-cpp-compositor/wip-cpp-compositor.pro
@@ -0,0 +1,12 @@
+QT += gui waylandcompositor
+
+HEADERS += \
+ compositor.h \
+ window.h
+
+SOURCES += main.cpp \
+ compositor.cpp \
+ window.cpp
+
+target.path = $$[QT_INSTALL_EXAMPLES]/wayland/reference-cpp
+INSTALLS += target