summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Klokkhammer Helsing <johan.helsing@qt.io>2018-08-07 15:13:29 +0200
committerJohan Helsing <johan.helsing@qt.io>2019-01-16 09:50:35 +0000
commitdec178ad56f60a5508928147c06e1881c913d550 (patch)
tree409460c4413bed54e833a7c324353916f32cf7c5
parent701a7773e383816ccdfe37a48b766f4bb6c16851 (diff)
Add wip-cpp-compositor to manual tests
This is an example similar to minimal-cpp, but for xdg-shell instead of ivi-application. Eventually it should replace qwindow-compositor as the main c++-only example compositor. Task-number: QTBUG-69937 Change-Id: Icac70e4af8f97c5f628d7e30279249210772a1c1 Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
-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
7 files changed, 767 insertions, 0 deletions
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