diff options
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 00000000..60ca27e9 --- /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 a9fddb84..657c0328 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 00000000..dea072e6 --- /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 1ffaf3c8..45957629 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 2301875c..e81221fb 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 52cbc3e5..146767a1 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 6bc3c188..c343d6f8 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 79e4fdec..c6294eea 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 eef51283..cc61fb17 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 0cbbe538..a8ee9a43 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 00000000..931475ef --- /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 00000000..09859d7e --- /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 00000000..3a7fcee7 --- /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 00000000..1493e9fc --- /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 00000000..d1930204 --- /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 94e0f8bf..59ea9141 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 00000000..8e1d5090 --- /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 00000000..0c94bb77 --- /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 00000000..f1ca7183 --- /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 00000000..f5af93a7 --- /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 00000000..d65c5f0c --- /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 00000000..5c2d6a2f --- /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 00000000..8ca5a6f3 --- /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 00000000..b5b2581e --- /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 00000000..bc71207e --- /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 00000000..546842ac --- /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 |