summaryrefslogtreecommitdiffstats
path: root/tests/auto
diff options
context:
space:
mode:
authorJohan Klokkhammer Helsing <johan.helsing@qt.io>2018-10-24 08:46:06 +0200
committerJohan Helsing <johan.helsing@qt.io>2018-12-11 15:46:49 +0000
commitddef100d9fa7014b21280b1380e6fbcef80277b3 (patch)
treecd0e674010a585fd6bc28cf0c26523392d8fbb8b /tests/auto
parent32b6f5f41349d109f872caf42a44352744bd48e2 (diff)
Create a new type of mock compositor for client tests
There are a number of issues with the current client testing: - Adding new compositor functionality is cumbersome (need to add compositor send method, command, implementation, not to mention creating new wrapper objects. - Customizing available globals and their versions is not possible and would be hard to implement. I.e. how to test that functionality works with old and new versions of an interface? Handle globals being destroyed. We did this with wl_output, but it was painfully cumbersome. - Hard to verify that the compositor state is clean between tests. It is currently done in some tests, but requires boiler plate code which needs to be added and maintained for each test. - In general lots of boiler-plate for new tests. (We have to have separate tests as long as Qt has global/static state. I.e. if one shell extension has been initialized, we can't deinitialize and initialize another one, so tests have to be separate.) - Dispatching server events tied to the client event loop sometimes makes it hard to write tests without deadlocks. - Abstraction, encapsulation and automatic behavior that can't be disabled makes it hard to test low-level functionality like surface exposure. So, in an attempt to mitigate these issues, I wrote a new testing framework. - Compositor dispatch is running continuously in it's own thread, access to compositor state is guarded by a mutex on the compositor, locking this will make dispatching stop, so the test can safely access internals. Although a bit cumbersome at first this makes it much easier to directly use server protocol commands from the test itself, i.e. no need to create commands for every single thing we want to test. - The CoreCompositor::exec template method can accept a lambda that will be run with dispatching stopped. It can also return a value, conveniently letting us safely extract or modify compositor state from tests. - This framework also takes full advantage of the qtwaylandscanner, using wrapper classes for everything, reducing boiler plate considerably. - The compositor parts are designed to do as little as possible automatically, but still provide easy ways to enable common functionality, like releasing buffers automatically, configuring shell surfaces etc. - Compositor globals are pluggable, use add<GlobalClass>() and remove<GlobalClass>() to add new global interfaces. I.e. easy to create a compositor with or without data_device_manager for instance. - DefaultCompositor provides a sensible default set of functionality and convenience methods for most test-cases. Custom ones can still be made by inheriting from CoreCompositor directly instead or by removing or adding globals to DefaultCompositor. - Globals have an isClean() method. Implement it to verify that the client didn't leave any objects lying around from the previous test. CoreCompositor::isClean calls isClean on the globals so a single call is all that's needed. In short, we've traded mock compositor encapsulation and thread safety guarantees for less boiler-plate, easier and more convenient access to internals. Anything accessing compositor state should go into a exec() call, or through the wrapper macros QCOMPOSITOR_VERIFY and QCOMPOSITOR_COMPARE (or the TRY versions). I've also tried to make the compositor print warnings if compositor state is accessed in an unsafe way. The mock compositor is currently built once per test due to CI limitations (same thing as with the old tests). Change-Id: Ia3feb80ce175d3814292b7f4768a0cc719f8b0e8 Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
Diffstat (limited to 'tests/auto')
-rw-r--r--tests/auto/client/client.pro7
-rw-r--r--tests/auto/client/client/client.pro2
-rw-r--r--tests/auto/client/fullscreenshellv1/fullscreenshellv1.pro2
-rw-r--r--tests/auto/client/iviapplication/iviapplication.pro2
-rw-r--r--tests/auto/client/seatv4/seatv4.pro4
-rw-r--r--tests/auto/client/seatv4/tst_seatv4.cpp266
-rw-r--r--tests/auto/client/shared/corecompositor.cpp140
-rw-r--r--tests/auto/client/shared/corecompositor.h209
-rw-r--r--tests/auto/client/shared/coreprotocol.cpp322
-rw-r--r--tests/auto/client/shared/coreprotocol.h313
-rw-r--r--tests/auto/client/shared/mockcompositor.cpp534
-rw-r--r--tests/auto/client/shared/mockcompositor.h293
-rw-r--r--tests/auto/client/shared/shared.pri43
-rw-r--r--tests/auto/client/shared/xdgshell.cpp186
-rw-r--r--tests/auto/client/shared/xdgshell.h127
-rw-r--r--tests/auto/client/shared_old/mockcompositor.cpp520
-rw-r--r--tests/auto/client/shared_old/mockcompositor.h290
-rw-r--r--tests/auto/client/shared_old/mockfullscreenshellv1.cpp (renamed from tests/auto/client/shared/mockfullscreenshellv1.cpp)0
-rw-r--r--tests/auto/client/shared_old/mockfullscreenshellv1.h (renamed from tests/auto/client/shared/mockfullscreenshellv1.h)0
-rw-r--r--tests/auto/client/shared_old/mockinput.cpp (renamed from tests/auto/client/shared/mockinput.cpp)0
-rw-r--r--tests/auto/client/shared_old/mockinput.h (renamed from tests/auto/client/shared/mockinput.h)0
-rw-r--r--tests/auto/client/shared_old/mockiviapplication.cpp (renamed from tests/auto/client/shared/mockiviapplication.cpp)0
-rw-r--r--tests/auto/client/shared_old/mockiviapplication.h (renamed from tests/auto/client/shared/mockiviapplication.h)0
-rw-r--r--tests/auto/client/shared_old/mockoutput.cpp (renamed from tests/auto/client/shared/mockoutput.cpp)0
-rw-r--r--tests/auto/client/shared_old/mockoutput.h (renamed from tests/auto/client/shared/mockoutput.h)0
-rw-r--r--tests/auto/client/shared_old/mocksurface.cpp (renamed from tests/auto/client/shared/mocksurface.cpp)0
-rw-r--r--tests/auto/client/shared_old/mocksurface.h (renamed from tests/auto/client/shared/mocksurface.h)0
-rw-r--r--tests/auto/client/shared_old/mockwlshell.cpp (renamed from tests/auto/client/shared/mockwlshell.cpp)0
-rw-r--r--tests/auto/client/shared_old/mockwlshell.h (renamed from tests/auto/client/shared/mockwlshell.h)0
-rw-r--r--tests/auto/client/shared_old/mockxdgshellv6.cpp (renamed from tests/auto/client/shared/mockxdgshellv6.cpp)0
-rw-r--r--tests/auto/client/shared_old/mockxdgshellv6.h (renamed from tests/auto/client/shared/mockxdgshellv6.h)0
-rw-r--r--tests/auto/client/shared_old/shared_old.pri34
-rw-r--r--tests/auto/client/surface/surface.pro5
-rw-r--r--tests/auto/client/surface/tst_surface.cpp158
-rw-r--r--tests/auto/client/xdgshell/tst_xdgshell.cpp269
-rw-r--r--tests/auto/client/xdgshell/xdgshell.pro5
-rw-r--r--tests/auto/client/xdgshellv6/xdgshellv6.pro2
37 files changed, 2967 insertions, 766 deletions
diff --git a/tests/auto/client/client.pro b/tests/auto/client/client.pro
index 14ce4407d..af7889d5f 100644
--- a/tests/auto/client/client.pro
+++ b/tests/auto/client/client.pro
@@ -4,5 +4,8 @@ SUBDIRS += \
client \
fullscreenshellv1 \
iviapplication \
- xdgshellv6 \
- wl_connect
+ seatv4 \
+ surface \
+ wl_connect \
+ xdgshell \
+ xdgshellv6
diff --git a/tests/auto/client/client/client.pro b/tests/auto/client/client/client.pro
index f4ced252c..7c3a934d0 100644
--- a/tests/auto/client/client/client.pro
+++ b/tests/auto/client/client/client.pro
@@ -1,4 +1,4 @@
-include (../shared/shared.pri)
+include (../shared_old/shared_old.pri)
TARGET = tst_client
SOURCES += tst_client.cpp
diff --git a/tests/auto/client/fullscreenshellv1/fullscreenshellv1.pro b/tests/auto/client/fullscreenshellv1/fullscreenshellv1.pro
index 8ce6dfe5c..49d19d5c3 100644
--- a/tests/auto/client/fullscreenshellv1/fullscreenshellv1.pro
+++ b/tests/auto/client/fullscreenshellv1/fullscreenshellv1.pro
@@ -1,4 +1,4 @@
-include (../shared/shared.pri)
+include (../shared_old/shared_old.pri)
TARGET = tst_client_fullscreenshell1
SOURCES += tst_fullscreenshellv1.cpp
diff --git a/tests/auto/client/iviapplication/iviapplication.pro b/tests/auto/client/iviapplication/iviapplication.pro
index 326921373..f2d596e6c 100644
--- a/tests/auto/client/iviapplication/iviapplication.pro
+++ b/tests/auto/client/iviapplication/iviapplication.pro
@@ -1,4 +1,4 @@
-include (../shared/shared.pri)
+include (../shared_old/shared_old.pri)
TARGET = tst_client_iviapplication
SOURCES += tst_iviapplication.cpp
diff --git a/tests/auto/client/seatv4/seatv4.pro b/tests/auto/client/seatv4/seatv4.pro
new file mode 100644
index 000000000..c02db5855
--- /dev/null
+++ b/tests/auto/client/seatv4/seatv4.pro
@@ -0,0 +1,4 @@
+include (../shared/shared.pri)
+
+TARGET = tst_seatv4
+SOURCES += tst_seatv4.cpp
diff --git a/tests/auto/client/seatv4/tst_seatv4.cpp b/tests/auto/client/seatv4/tst_seatv4.cpp
new file mode 100644
index 000000000..a95dbfcc2
--- /dev/null
+++ b/tests/auto/client/seatv4/tst_seatv4.cpp
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+
+#include <QtGui/QRasterWindow>
+#include <QtGui/QOpenGLWindow>
+
+using namespace MockCompositor;
+
+// wl_seat version 5 was introduced in wayland 1.10, and although that's pretty old,
+// there are still compositors that have yet to update their implementation to support
+// the new version (most importantly our own QtWaylandCompositor).
+// As long as that's the case, this test makes sure input events still works on version 4.
+class SeatV4Compositor : public DefaultCompositor {
+public:
+ explicit SeatV4Compositor()
+ {
+ exec([this] {
+ m_config.autoConfigure = true;
+
+ removeAll<Seat>();
+
+ uint capabilities = MockCompositor::Seat::capability_pointer;
+ int version = 4;
+ add<Seat>(capabilities, version);
+ });
+ }
+};
+
+class tst_seatv4 : public QObject, private SeatV4Compositor
+{
+ Q_OBJECT
+private slots:
+ void cleanup();
+ void bindsToSeat();
+ void createsPointer();
+ void setsCursorOnEnter();
+ void usesEnterSerial();
+ void simpleAxis_data();
+ void simpleAxis();
+ void invalidPointerEvents();
+ void scaledCursor();
+};
+
+void tst_seatv4::cleanup()
+{
+ QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage()));
+ QCOMPOSITOR_COMPARE(getAll<Output>().size(), 1); // No extra outputs left
+}
+
+void tst_seatv4::bindsToSeat()
+{
+ QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().size(), 1);
+ QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().first()->version(), 4);
+}
+
+void tst_seatv4::createsPointer()
+{
+ QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().size(), 1);
+ QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 4);
+}
+
+void tst_seatv4::setsCursorOnEnter()
+{
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); });
+ QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface());
+}
+
+void tst_seatv4::usesEnterSerial()
+{
+ QSignalSpy setCursorSpy(exec([=] { return pointer(); }), &Pointer::setCursor);
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ uint enterSerial = exec([=] {
+ return pointer()->sendEnter(xdgSurface()->m_surface, {32, 32});
+ });
+ QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface());
+
+ QTRY_COMPARE(setCursorSpy.count(), 1);
+ QCOMPARE(setCursorSpy.takeFirst().at(0).toUInt(), enterSerial);
+}
+
+void tst_seatv4::simpleAxis_data()
+{
+ QTest::addColumn<uint>("axis");
+ QTest::addColumn<qreal>("value");
+ QTest::addColumn<Qt::Orientation>("orientation");
+ QTest::addColumn<QPoint>("angleDelta");
+
+ // Directions in regular windows/linux terms (no "natural" scrolling)
+ QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << Qt::Vertical << QPoint{0, -12};
+ QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << Qt::Vertical << QPoint{0, 12};
+ QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << Qt::Horizontal << QPoint{-12, 0};
+ QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << Qt::Horizontal << QPoint{12, 0};
+ QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << Qt::Vertical << QPoint{0, 120};
+}
+
+void tst_seatv4::simpleAxis()
+{
+ QFETCH(uint, axis);
+ QFETCH(qreal, value);
+ QFETCH(Qt::Orientation, orientation);
+ QFETCH(QPoint, angleDelta);
+
+ class WheelWindow : QRasterWindow {
+ public:
+ explicit WheelWindow()
+ {
+ resize(64, 64);
+ show();
+ }
+ void wheelEvent(QWheelEvent *event) override
+ {
+ QRasterWindow::wheelEvent(event);
+ // Angle delta should always be provided (says docs)
+ QVERIFY(!event->angleDelta().isNull());
+
+ // There are now scroll phases on Wayland prior to v5
+ QCOMPARE(event->phase(), Qt::NoScrollPhase);
+
+ // Pixel delta should only be set if we know it's a high-res input device (which we don't)
+ QCOMPARE(event->pixelDelta(), QPoint(0, 0));
+
+ // The axis vector of the event is already in surface space, so there is now way to tell
+ // whether it is inverted or not.
+ QCOMPARE(event->inverted(), false);
+
+ // We didn't press any buttons
+ QCOMPARE(event->buttons(), Qt::NoButton);
+
+ if (event->orientation() == Qt::Horizontal)
+ QCOMPARE(event->delta(), event->angleDelta().x());
+ else
+ QCOMPARE(event->delta(), event->angleDelta().y());
+
+ // There has been no information about what created the event.
+ // Documentation says not synthesized is appropriate in such cases
+ QCOMPARE(event->source(), Qt::MouseEventNotSynthesized);
+
+ m_events.append(Event(event->pixelDelta(), event->angleDelta(), event->orientation()));
+ }
+ struct Event // Because I didn't find a convenient way to copy it entirely
+ {
+ // TODO: Constructors can be removed when we start supporting brace-initializers
+ Event() = default;
+ Event(const QPoint &pixelDelta, const QPoint &angleDelta, Qt::Orientation orientation)
+ : pixelDelta(pixelDelta), angleDelta(angleDelta), orientation(orientation)
+ {}
+ const QPoint pixelDelta;
+ const QPoint angleDelta; // eights of a degree, positive is upwards, left
+ const Qt::Orientation orientation{};
+ };
+ QVector<Event> m_events;
+ };
+
+ WheelWindow window;
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] {
+ Surface *surface = xdgSurface()->m_surface;
+ pointer()->sendEnter(surface, {32, 32});
+ wl_client *client = surface->resource()->client();
+ // Length of vector in surface-local space. i.e. positive is downwards
+ pointer()->sendAxis(
+ client,
+ Pointer::axis(axis),
+ value // Length of vector in surface-local space. i.e. positive is downwards
+ );
+ });
+
+ QTRY_COMPARE(window.m_events.size(), 1);
+ auto event = window.m_events.takeFirst();
+ QCOMPARE(event.angleDelta, angleDelta);
+ QCOMPARE(event.orientation, orientation);
+}
+
+void tst_seatv4::invalidPointerEvents()
+{
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] {
+ auto *p = pointer();
+ auto *c = client();
+ // Purposefully send events without a wl_pointer.enter
+ p->sendMotion(c, {32, 32});
+ p->sendButton(c, BTN_LEFT, Pointer::button_state_pressed);
+ p->sendAxis(c, Pointer::axis_vertical_scroll, 1.0);
+ });
+
+ // Make sure we get here without crashing
+ xdgPingAndWaitForPong();
+}
+
+void tst_seatv4::scaledCursor()
+{
+ QSKIP("Currently broken and should be fixed");
+ // Add a highdpi output
+ exec([&] {
+ int scale = 2;
+ add<Output>(scale);
+ });
+
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); });
+ QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface());
+ QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface()->m_committed.buffer);
+ QCOMPOSITOR_TRY_COMPARE(pointer()->cursorSurface()->m_committed.bufferScale, 1);
+ QSize unscaledPixelSize = exec([=] {
+ return pointer()->cursorSurface()->m_committed.buffer->size();
+ });
+
+ exec([=] {
+ auto *surface = pointer()->cursorSurface();
+ surface->sendEnter(getAll<Output>()[1]);
+ surface->sendLeave(getAll<Output>()[0]);
+ });
+
+ QCOMPOSITOR_TRY_COMPARE(pointer()->cursorSurface()->m_committed.buffer->size(), unscaledPixelSize * 2);
+
+ // Remove the extra output to clean up for the next test
+ exec([&] { remove(getAll<Output>()[1]); });
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_seatv4)
+#include "tst_seatv4.moc"
diff --git a/tests/auto/client/shared/corecompositor.cpp b/tests/auto/client/shared/corecompositor.cpp
new file mode 100644
index 000000000..afa25e94c
--- /dev/null
+++ b/tests/auto/client/shared/corecompositor.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "corecompositor.h"
+
+namespace MockCompositor {
+
+CoreCompositor::CoreCompositor()
+ : m_display(wl_display_create())
+ , m_socketName(wl_display_add_socket_auto(m_display))
+ , m_eventLoop(wl_display_get_event_loop(m_display))
+
+ // Start dispatching
+ , m_dispatchThread([this](){
+ while (m_running) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(20));
+ dispatch();
+ }
+ })
+{
+ m_timer.start();
+ Q_ASSERT(isClean());
+}
+
+CoreCompositor::~CoreCompositor()
+{
+ m_running = false;
+ m_dispatchThread.join();
+ wl_display_destroy(m_display);
+}
+
+bool CoreCompositor::isClean()
+{
+ Lock lock(this);
+ for (auto *global : qAsConst(m_globals)) {
+ if (!global->isClean())
+ return false;
+ }
+ return true;
+}
+
+QString CoreCompositor::dirtyMessage()
+{
+ Lock lock(this);
+ QStringList messages;
+ for (auto *global : qAsConst(m_globals)) {
+ if (!global->isClean())
+ messages << (global->metaObject()->className() % QLatin1String(": ") % global->dirtyMessage());
+ }
+ return messages.join(", ");
+}
+
+void CoreCompositor::dispatch()
+{
+ Lock lock(this);
+ wl_display_flush_clients(m_display);
+ constexpr int timeout = 0; // immediate return
+ wl_event_loop_dispatch(m_eventLoop, timeout);
+}
+
+/*!
+ * \brief Adds a new global interface for the compositor
+ *
+ * Takes ownership of \a global
+ */
+void CoreCompositor::add(Global *global)
+{
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ m_globals.append(global);
+}
+
+void CoreCompositor::remove(Global *global)
+{
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ //TODO: Need to delete global as well!
+ m_globals.removeAll(global);
+}
+
+uint CoreCompositor::nextSerial()
+{
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ return wl_display_next_serial(m_display);
+}
+
+uint CoreCompositor::currentTimeMilliseconds()
+{
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ return uint(m_timer.elapsed());
+}
+
+wl_client *CoreCompositor::client(int index)
+{
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ wl_list *clients = wl_display_get_client_list(m_display);
+ wl_client *client = nullptr;
+ int i = 0;
+ wl_client_for_each(client, clients) {
+ if (i++ == index)
+ return client;
+ }
+ return nullptr;
+}
+
+void CoreCompositor::warnIfNotLockedByThread(const char *caller)
+{
+ if (!m_lock || !m_lock->isOwnedByCurrentThread()) {
+ qWarning() << caller << "called without locking the compositor to the current thread."
+ << "This means the compositor can start dispatching at any moment,"
+ << "potentially leading to threading issues."
+ << "Unless you know what you are doing you should probably fix the test"
+ << "by locking the compositor before accessing it (see mutex()).";
+ }
+}
+
+} // namespace MockCompositor
diff --git a/tests/auto/client/shared/corecompositor.h b/tests/auto/client/shared/corecompositor.h
new file mode 100644
index 000000000..875b7d050
--- /dev/null
+++ b/tests/auto/client/shared/corecompositor.h
@@ -0,0 +1,209 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MOCKCOMPOSITOR_CORECOMPOSITOR_H
+#define MOCKCOMPOSITOR_CORECOMPOSITOR_H
+
+#include <QtTest/QtTest>
+
+#include <wayland-server-core.h>
+
+struct wl_resource;
+
+namespace MockCompositor {
+
+class Global : public QObject
+{
+ Q_OBJECT
+public:
+ virtual bool isClean() { return true; }
+ virtual QString dirtyMessage() { return isClean() ? "clean" : "dirty"; }
+};
+
+class CoreCompositor
+{
+public:
+ explicit CoreCompositor();
+ ~CoreCompositor();
+ bool isClean();
+ QString dirtyMessage();
+ void dispatch();
+
+ template<typename function_type, typename... arg_types>
+ auto exec(function_type func, arg_types&&... args) -> decltype(func())
+ {
+ Lock lock(this);
+ return func(std::forward<arg_types>(args)...);
+ }
+
+ template<typename function_type, typename... arg_types>
+ auto call(function_type func, arg_types&&... args) -> decltype(func())
+ {
+ Lock lock(this);
+ auto boundFunc = std::bind(func, this);
+ return boundFunc(this, std::forward<arg_types>(args)...);
+ }
+
+ // Unsafe section below, YOU are responsible that the compositor is locked or
+ // this is run through the mutex() method!
+
+ void add(Global *global);
+ void remove(Global *global);
+
+ /*!
+ * \brief Constructs and adds a new global with the given parameters
+ *
+ * Convenience function. i.e.
+ *
+ * compositor->add(new MyGlobal(compositor, version);
+ *
+ * can be written as:
+ *
+ * compositor->add<MyGlobal>(version);
+ *
+ * Returns the new global
+ */
+ template<typename global_type, typename... arg_types>
+ global_type *add(arg_types&&... args)
+ {
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ auto *global = new global_type(this, std::forward<arg_types>(args)...);
+ m_globals.append(global);
+ return global;
+ }
+
+ /*!
+ * \brief Removes all globals of the given type
+ *
+ * Convenience function
+ */
+ template<typename global_type, typename... arg_types>
+ void removeAll()
+ {
+ const auto globals = getAll<global_type>();
+ for (auto global : globals)
+ remove(global);
+ }
+
+ /*!
+ * \brief Returns a global with the given type, if any
+ */
+ template<typename global_type>
+ global_type *get()
+ {
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ for (auto *global : qAsConst(m_globals)) {
+ if (auto *casted = qobject_cast<global_type *>(global))
+ return casted;
+ }
+ return nullptr;
+ }
+
+ /*!
+ * \brief Returns all globals with the given type, if any
+ */
+ template<typename global_type>
+ QVector<global_type *> getAll()
+ {
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ QVector<global_type *> matching;
+ for (auto *global : qAsConst(m_globals)) {
+ if (auto *casted = qobject_cast<global_type *>(global))
+ matching.append(casted);
+ }
+ return matching;
+ }
+
+ uint nextSerial();
+ uint currentTimeMilliseconds();
+ wl_client *client(int index = 0);
+ void warnIfNotLockedByThread(const char* caller = "warnIfNotLockedbyThread");
+
+public:
+ // Only use this carefully from the test thread (i.e. lock first)
+ wl_display *m_display = nullptr;
+protected:
+ class Lock {
+ public:
+ explicit Lock(CoreCompositor *compositor)
+ : m_compositor(compositor)
+ , m_threadId(std::this_thread::get_id())
+ {
+ // Can't use a QMutexLocker here, as it's not movable
+ compositor->m_mutex.lock();
+ Q_ASSERT(compositor->m_lock == nullptr);
+ compositor->m_lock = this;
+ }
+ ~Lock()
+ {
+ Q_ASSERT(m_compositor->m_lock == this);
+ m_compositor->m_lock = nullptr;
+ m_compositor->m_mutex.unlock();
+ }
+
+ // Move semantics
+ Lock(Lock &&) = default;
+ Lock &operator=(Lock &&) = default;
+
+ // Disable copying
+ Lock(const Lock &) = delete;
+ Lock &operator=(const Lock &) = delete;
+
+ bool isOwnedByCurrentThread() const { return m_threadId == std::this_thread::get_id(); }
+ private:
+ CoreCompositor *m_compositor = nullptr;
+ std::thread::id m_threadId;
+ };
+ QByteArray m_socketName;
+ wl_event_loop *m_eventLoop = nullptr;
+ bool m_running = true;
+ QVector<Global *> m_globals;
+ QElapsedTimer m_timer;
+
+private:
+ Lock *m_lock = nullptr;
+ QMutex m_mutex;
+ std::thread m_dispatchThread;
+};
+
+template<typename container_type>
+QByteArray toByteArray(container_type container)
+{
+ return QByteArray(reinterpret_cast<const char *>(container.data()), sizeof (container[0]) * container.size());
+}
+
+template<typename return_type>
+return_type *fromResource(::wl_resource *resource) {
+ if (auto *r = return_type::Resource::fromResource(resource))
+ return static_cast<return_type *>(r->object());
+ return nullptr;
+}
+
+} // namespace MockCompositor
+
+#endif // MOCKCOMPOSITOR_CORECOMPOSITOR_H
diff --git a/tests/auto/client/shared/coreprotocol.cpp b/tests/auto/client/shared/coreprotocol.cpp
new file mode 100644
index 000000000..46d46d980
--- /dev/null
+++ b/tests/auto/client/shared/coreprotocol.cpp
@@ -0,0 +1,322 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "coreprotocol.h"
+
+namespace MockCompositor {
+
+void Surface::sendFrameCallbacks()
+{
+ uint time = m_wlCompositor->m_compositor->currentTimeMilliseconds();
+ for (auto *callback : m_waitingFrameCallbacks)
+ callback->sendDone(time);
+ m_waitingFrameCallbacks.clear();
+}
+
+void Surface::sendEnter(Output *output)
+{
+ m_outputs.append(output);
+ const auto outputResources = output->resourceMap().values(resource()->client());
+ for (auto outputResource: outputResources)
+ wl_surface::send_enter(resource()->handle, outputResource->handle);
+}
+
+void Surface::sendLeave(Output *output)
+{
+ m_outputs.removeOne(output);
+ const auto outputResources = output->resourceMap().values(resource()->client());
+ for (auto outputResource: outputResources)
+ wl_surface::send_leave(resource()->handle, outputResource->handle);
+}
+
+void Surface::surface_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+ for (auto *commit : m_commits)
+ delete commit->commitSpecific.frame;
+ bool removed = m_wlCompositor->m_surfaces.removeOne(this);
+ Q_ASSERT(removed);
+ delete this;
+}
+
+void Surface::surface_attach(Resource *resource, wl_resource *buffer, int32_t x, int32_t y)
+{
+ Q_UNUSED(resource);
+ QPoint offset(x, y);
+ m_pending.buffer = fromResource<Buffer>(buffer);
+ m_pending.commitSpecific.attachOffset = offset;
+ m_pending.commitSpecific.attached = true;
+ emit attach(buffer, offset);
+}
+
+void Surface::surface_set_buffer_scale(QtWaylandServer::wl_surface::Resource *resource, int32_t scale)
+{
+ Q_UNUSED(resource);
+ m_pending.bufferScale = scale;
+}
+
+void Surface::surface_commit(Resource *resource)
+{
+ Q_UNUSED(resource);
+ m_committed = m_pending;
+ m_commits.append(new DoubleBufferedState(m_committed));
+
+ if (auto *frame = m_pending.commitSpecific.frame)
+ m_waitingFrameCallbacks.append(frame);
+
+ m_pending.commitSpecific = PerCommitData();
+ emit commit();
+ if (m_committed.commitSpecific.attached)
+ emit bufferCommitted();
+}
+
+void Surface::surface_frame(Resource *resource, uint32_t callback)
+{
+ // Although valid, there is really no point having multiple frame requests in the same commit.
+ // Make sure we don't do it
+ QCOMPARE(m_pending.commitSpecific.frame, nullptr);
+
+ auto *frame = new Callback(resource->client(), callback, 1);
+ m_pending.commitSpecific.frame = frame;
+}
+
+bool WlCompositor::isClean() {
+ for (auto *surface : qAsConst(m_surfaces)) {
+ if (!CursorRole::fromSurface(surface))
+ return false;
+ }
+ return true;
+}
+
+QString WlCompositor::dirtyMessage()
+{
+ if (isClean())
+ return "clean";
+ QStringList messages;
+ for (auto *s : qAsConst(m_surfaces)) {
+ QString role = s->m_role ? s->m_role->staticMetaObject.className(): "none/unknown";
+ messages << "Surface with role: " + role;
+ }
+ return "Dirty, surfaces left:\n\t" + messages.join("\n\t");
+}
+
+void Output::sendScale(int factor)
+{
+ Q_ASSERT(m_version >= WL_OUTPUT_SCALE_SINCE_VERSION);
+ m_scale = factor;
+ const auto resources = resourceMap().values();
+ for (auto r: resources)
+ wl_output::send_scale(r->handle, factor);
+}
+
+void Output::sendDone()
+{
+ Q_ASSERT(m_version >= WL_OUTPUT_DONE_SINCE_VERSION);
+ const auto resources = resourceMap().values();
+ for (auto r: resources)
+ wl_output::send_done(r->handle);
+}
+
+void Output::output_bind_resource(QtWaylandServer::wl_output::Resource *resource)
+{
+ if (m_version >= WL_OUTPUT_SCALE_SINCE_VERSION)
+ wl_output::send_scale(resource->handle, m_scale);
+ //TODO: send other required stuff as well
+ if (m_version >= WL_OUTPUT_DONE_SINCE_VERSION)
+ wl_output::send_done(resource->handle);
+}
+
+// Seat stuff
+Seat::Seat(CoreCompositor *compositor, uint capabilities, int version) //TODO: check version
+ : QtWaylandServer::wl_seat(compositor->m_display, version)
+ , m_compositor(compositor)
+{
+ setCapabilities(capabilities);
+}
+
+Seat::~Seat()
+{
+ qDeleteAll(m_oldPointers);
+ delete m_pointer;
+}
+
+void Seat::setCapabilities(uint capabilities) {
+ // TODO: Add support for touch and keyboard
+ Q_ASSERT(capabilities == 0 || capabilities == capability_pointer);
+
+ m_capabilities = capabilities;
+
+ if (m_capabilities & capability_pointer) {
+ if (!m_pointer)
+ m_pointer = (new Pointer(this));
+ } else if (m_pointer) {
+ m_oldPointers << m_pointer;
+ m_pointer = nullptr;
+ }
+
+ for (auto *resource : resourceMap())
+ wl_seat::send_capabilities(resource->handle, capabilities);
+}
+
+void Seat::seat_get_pointer(Resource *resource, uint32_t id)
+{
+ if (~m_capabilities & capability_pointer) {
+ qWarning() << "Client requested a wl_pointer without the capability being available."
+ << "This Could be a race condition when hotunplugging,"
+ << "but is most likely a client error";
+ Pointer *pointer = new Pointer(this);
+ pointer->add(resource->client(), id, resource->version());
+ // TODO: mark as destroyed
+ m_oldPointers << pointer;
+ return;
+ }
+ m_pointer->add(resource->client(), id, resource->version());
+}
+
+Surface *Pointer::cursorSurface()
+{
+ return m_cursorRole ? m_cursorRole->m_surface : nullptr;
+}
+
+uint Pointer::sendEnter(Surface *surface, const QPointF &position)
+{
+ wl_fixed_t x = wl_fixed_from_double(position.x());
+ wl_fixed_t y = wl_fixed_from_double(position.y());
+ m_enterSerial = m_seat->m_compositor->nextSerial();
+
+ wl_client *client = surface->resource()->client();
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_enter(r->handle, m_enterSerial, surface->resource()->handle, x ,y);
+ return m_enterSerial;
+}
+
+// Make sure you call enter, frame etc. first
+void Pointer::sendMotion(wl_client *client, const QPointF &position)
+{
+ wl_fixed_t x = wl_fixed_from_double(position.x());
+ wl_fixed_t y = wl_fixed_from_double(position.y());
+ auto time = m_seat->m_compositor->currentTimeMilliseconds();
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_motion(r->handle, time, x, y);
+}
+
+// Make sure you call enter, frame etc. first
+uint Pointer::sendButton(wl_client *client, uint button, uint state)
+{
+ Q_ASSERT(state == button_state_pressed || state == button_state_released);
+ auto time = m_seat->m_compositor->currentTimeMilliseconds();
+ uint serial = m_seat->m_compositor->nextSerial();
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_button(r->handle, serial, time, button, state);
+ return serial;
+}
+
+// Make sure you call enter, frame etc. first
+void Pointer::sendAxis(wl_client *client, axis axis, qreal value)
+{
+ auto time = m_seat->m_compositor->currentTimeMilliseconds();
+ wl_fixed_t val = wl_fixed_from_double(value);
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_axis(r->handle, time, axis, val);
+}
+
+void Pointer::pointer_set_cursor(Resource *resource, uint32_t serial, wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y)
+{
+ Q_UNUSED(resource);
+ Q_UNUSED(hotspot_x);
+ Q_UNUSED(hotspot_y);
+ auto *s = fromResource<Surface>(surface);
+ QVERIFY(s);
+
+ if (s->m_role) {
+ auto *cursorRole = CursorRole::fromSurface(s);
+ QVERIFY(cursorRole);
+ QVERIFY(cursorRole == m_cursorRole);
+ } else {
+ m_cursorRole = new CursorRole(s); //TODO: make sure we don't leak CursorRole
+ s->m_role = m_cursorRole;
+ }
+// QCOMPARE(serial, m_enterSerial); //TODO: uncomment when this bug is fixed
+ emit setCursor(serial);
+}
+
+// Shm implementation
+Shm::Shm(CoreCompositor *compositor, QVector<format> formats, int version)
+ : QtWaylandServer::wl_shm(compositor->m_display, version)
+ , m_compositor(compositor)
+ , m_formats(formats)
+{
+ // Some formats are specified as mandatory
+ Q_ASSERT(m_formats.contains(format_argb8888));
+ Q_ASSERT(m_formats.contains(format_xrgb8888));
+}
+
+bool Shm::isClean()
+{
+// for (ShmPool *pool : qAsConst(m_pools)) {
+// //TODO: return false if not cursor buffer
+// if (pool->m_buffers.isEmpty()) {
+// return false;
+// }
+// }
+ return true;
+}
+
+void Shm::shm_create_pool(Resource *resource, uint32_t id, int32_t fd, int32_t size)
+{
+ Q_UNUSED(fd);
+ Q_UNUSED(size);
+ auto *pool = new ShmPool(this, resource->client(), id, 1);
+ m_pools.append(pool);
+}
+
+ShmPool::ShmPool(Shm *shm, wl_client *client, int id, int version)
+ : QtWaylandServer::wl_shm_pool(client, id, version)
+ , m_shm(shm)
+{
+}
+
+void ShmPool::shm_pool_create_buffer(Resource *resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format)
+{
+ QSize size(width, height);
+ new ShmBuffer(offset, size, stride, Shm::format(format), resource->client(), id);
+}
+
+void ShmPool::shm_pool_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+ bool removed = m_shm->m_pools.removeOne(this);
+ Q_ASSERT(removed);
+ delete this;
+}
+
+} // namespace MockCompositor
diff --git a/tests/auto/client/shared/coreprotocol.h b/tests/auto/client/shared/coreprotocol.h
new file mode 100644
index 000000000..2fbe9b139
--- /dev/null
+++ b/tests/auto/client/shared/coreprotocol.h
@@ -0,0 +1,313 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MOCKCOMPOSITOR_COREPROTOCOL_H
+#define MOCKCOMPOSITOR_COREPROTOCOL_H
+
+#include "corecompositor.h"
+
+#include <qwayland-server-wayland.h>
+
+namespace MockCompositor {
+
+class WlCompositor;
+class Output;
+class Pointer;
+class CursorRole;
+class ShmPool;
+class ShmBuffer;
+
+class Buffer : public QObject, public QtWaylandServer::wl_buffer
+{
+ Q_OBJECT
+public:
+ explicit Buffer(wl_client *client, int id, int version)
+ : QtWaylandServer::wl_buffer(client, id, version)
+ {
+ }
+ virtual QSize size() const = 0;
+ bool m_destroyed = false;
+
+protected:
+ void buffer_destroy_resource(Resource *resource) override
+ {
+ Q_UNUSED(resource);
+ m_destroyed = true;
+ // The client side resource has been destroyed, but we keep this object because it may be
+ // be used as a reference by e.g. surface for the currently committed buffer so it's not
+ // yet safe to free it.
+
+ //TODO: The memory should be freed by its factory
+ }
+ void buffer_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); }
+};
+
+class Callback : public QObject, public QtWaylandServer::wl_callback
+{
+ Q_OBJECT
+public:
+ explicit Callback(wl_client *client, int id, int version = 1)
+ : QtWaylandServer::wl_callback(client, id, version)
+ {
+ }
+ ~Callback() override { if (!m_destroyed) wl_resource_destroy(resource()->handle); }
+ void send_done(uint32_t data) = delete; // use state-tracking method below instead
+ void sendDone(uint data) { Q_ASSERT(!m_done); QtWaylandServer::wl_callback::send_done(data); m_done = true; }
+ void sendDoneAndDestroy(uint data) { sendDone(data); wl_resource_destroy(resource()->handle); }
+ bool m_done = false;
+ bool m_destroyed = false;
+protected:
+ void callback_destroy_resource(Resource *resource) override { Q_UNUSED(resource); m_destroyed = true; }
+};
+
+class SurfaceRole : public QObject {
+ Q_OBJECT
+};
+
+class Surface : public QObject, public QtWaylandServer::wl_surface
+{
+ Q_OBJECT
+public:
+ explicit Surface(WlCompositor *wlCompositor, wl_client *client, int id, int version)
+ : QtWaylandServer::wl_surface(client, id, version)
+ , m_wlCompositor(wlCompositor)
+ {
+ }
+ ~Surface() override { qDeleteAll(m_commits); } // TODO: maybe make sure buffers are released?
+ void sendFrameCallbacks();
+ void sendEnter(Output *output);
+ void send_enter(::wl_resource *output) = delete;
+ void sendLeave(Output *output);
+ void send_leave(::wl_resource *output) = delete;
+
+ WlCompositor *m_wlCompositor;
+ struct PerCommitData {
+ Callback *frame = nullptr;
+ QPoint attachOffset;
+ bool attached = false;
+ };
+ struct DoubleBufferedState {
+ PerCommitData commitSpecific;
+ Buffer *buffer = nullptr;
+ uint configureSerial = 0;
+ int bufferScale = 1;
+ } m_pending, m_committed;
+ QVector<DoubleBufferedState *> m_commits;
+ QVector<Callback *> m_waitingFrameCallbacks;
+ QVector<Output *> m_outputs;
+ SurfaceRole *m_role = nullptr;
+
+signals:
+ void attach(void *buffer, QPoint offset);
+ void commit();
+ void bufferCommitted();
+
+protected:
+ void surface_destroy_resource(Resource *resource) override;
+ void surface_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); }
+ void surface_attach(Resource *resource, wl_resource *buffer, int32_t x, int32_t y) override;
+ void surface_set_buffer_scale(Resource *resource, int32_t scale) override;
+ void surface_commit(Resource *resource) override;
+ void surface_frame(Resource *resource, uint32_t callback) override;
+};
+
+class WlCompositor : public Global, public QtWaylandServer::wl_compositor
+{
+ Q_OBJECT
+public:
+ explicit WlCompositor(CoreCompositor *compositor, int version = 3)
+ : QtWaylandServer::wl_compositor(compositor->m_display, version)
+ , m_compositor(compositor)
+ {}
+ bool isClean() override;
+ QString dirtyMessage() override;
+ QVector<Surface *> m_surfaces;
+ CoreCompositor *m_compositor = nullptr;
+
+signals:
+ void surfaceCreated(Surface *surface);
+
+protected:
+ void compositor_create_surface(Resource *resource, uint32_t id) override
+ {
+ auto *surface = new Surface(this, resource->client(), id, resource->version());
+ m_surfaces.append(surface);
+ emit surfaceCreated(surface);
+ }
+};
+
+class SubCompositor : public Global, public QtWaylandServer::wl_subcompositor
+{
+ Q_OBJECT
+public:
+ explicit SubCompositor(CoreCompositor *compositor, int version = 1)
+ : QtWaylandServer::wl_subcompositor(compositor->m_display, version)
+ {}
+ // TODO
+};
+
+class Output : public Global, public QtWaylandServer::wl_output
+{
+ Q_OBJECT
+public:
+ explicit Output(CoreCompositor *compositor, int scale = 1, int version = 2)
+ : QtWaylandServer::wl_output(compositor->m_display, version)
+ , m_scale(scale)
+ , m_version(version)
+ {}
+ void sendScale(int factor);
+ void send_scale(int32_t factor) = delete;
+ void send_scale(struct ::wl_resource *resource, int32_t factor) = delete;
+ void sendDone();
+ int m_scale = 1;
+ int m_version = 1; // TODO: remove on libwayland upgrade
+
+protected:
+ void output_bind_resource(Resource *resource) override;
+};
+
+class Seat : public Global, public QtWaylandServer::wl_seat
+{
+ Q_OBJECT
+public:
+ explicit Seat(CoreCompositor *compositor, uint capabilities, int version = 4);
+ ~Seat() override;
+ void send_capabilities(Resource *resource, uint capabilities) = delete; // Use wrapper instead
+ void send_capabilities(uint capabilities) = delete; // Use wrapper instead
+ void setCapabilities(uint capabilities);
+
+ CoreCompositor *m_compositor = nullptr;
+
+ Pointer* m_pointer = nullptr;
+ QVector<Pointer *> m_oldPointers;
+
+ uint m_capabilities = 0;
+
+protected:
+ void seat_bind_resource(Resource *resource) override
+ {
+ wl_seat::send_capabilities(resource->handle, m_capabilities);
+ }
+
+ void seat_get_pointer(Resource *resource, uint32_t id) override;
+// void seat_get_keyboard(Resource *resource, uint32_t id) override;
+// void seat_get_touch(Resource *resource, uint32_t id) override;
+
+// void seat_release(Resource *resource) override;
+};
+
+class Pointer : public QObject, public QtWaylandServer::wl_pointer
+{
+ Q_OBJECT
+public:
+ explicit Pointer(Seat *seat) : m_seat(seat) {}
+ Surface *cursorSurface();
+ CursorRole* m_cursorRole = nullptr; //TODO: cleanup
+ uint sendEnter(Surface *surface, const QPointF &position);
+ void sendMotion(wl_client *client, const QPointF &position);
+ uint sendButton(wl_client *client, uint button, uint state);
+ void sendAxis(wl_client *client, axis axis, qreal value);
+
+ Seat *m_seat = nullptr;
+ uint m_enterSerial = 0;
+
+signals:
+ void setCursor(uint serial); //TODO: add arguments?
+
+protected:
+ void pointer_set_cursor(Resource *resource, uint32_t serial, ::wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y) override;
+ //TODO
+};
+
+class CursorRole : public SurfaceRole {
+ Q_OBJECT
+public:
+ explicit CursorRole(Surface *surface) // TODO: needs some more args
+ : m_surface(surface)
+ {
+ }
+ static CursorRole *fromSurface(Surface *surface) { return qobject_cast<CursorRole *>(surface->m_role); }
+ Surface *m_surface = nullptr;
+};
+
+class Shm : public Global, public QtWaylandServer::wl_shm
+{
+ Q_OBJECT
+public:
+ explicit Shm(CoreCompositor *compositor, QVector<format> formats = {format_argb8888, format_xrgb8888, format_rgb888}, int version = 1);
+ bool isClean() override;
+ CoreCompositor *m_compositor = nullptr;
+ QVector<ShmPool *> m_pools;
+ const QVector<format> m_formats;
+
+protected:
+ void shm_create_pool(Resource *resource, uint32_t id, int32_t fd, int32_t size) override;
+ void shm_bind_resource(Resource *resource) override
+ {
+ for (auto format : qAsConst(m_formats))
+ send_format(resource->handle, format);
+ }
+};
+
+class ShmPool : QObject, public QtWaylandServer::wl_shm_pool
+{
+ Q_OBJECT
+public:
+ explicit ShmPool(Shm *shm, wl_client *client, int id, int version = 1);
+ Shm *m_shm = nullptr;
+ QVector<ShmBuffer *> m_buffers;
+
+protected:
+ void shm_pool_create_buffer(Resource *resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format) override;
+ void shm_pool_destroy_resource(Resource *resource) override;
+ void shm_pool_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); }
+};
+
+class ShmBuffer : public Buffer
+{
+ Q_OBJECT
+public:
+ static ShmBuffer *fromBuffer(Buffer *buffer) { return qobject_cast<ShmBuffer *>(buffer); }
+ explicit ShmBuffer(int offset, const QSize &size, int stride, Shm::format format, wl_client *client, int id, int version = 1)
+ : Buffer(client, id, version)
+ , m_offset(offset)
+ , m_size(size)
+ , m_stride(stride)
+ , m_format(format)
+ {
+ }
+ QSize size() const override { return m_size; }
+ const int m_offset;
+ const QSize m_size;
+ const int m_stride;
+ const Shm::format m_format;
+};
+
+} // namespace MockCompositor
+
+#endif // MOCKCOMPOSITOR_COREPROTOCOL_H
diff --git a/tests/auto/client/shared/mockcompositor.cpp b/tests/auto/client/shared/mockcompositor.cpp
index df24b4091..45d62a153 100644
--- a/tests/auto/client/shared/mockcompositor.cpp
+++ b/tests/auto/client/shared/mockcompositor.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@@ -27,494 +27,64 @@
****************************************************************************/
#include "mockcompositor.h"
-#include "mockinput.h"
-#include "mockoutput.h"
-#include "mocksurface.h"
-#include "mockwlshell.h"
-#include "mockxdgshellv6.h"
-#include "mockiviapplication.h"
-#include <wayland-xdg-shell-unstable-v6-server-protocol.h>
-
-#include <stdio.h>
-MockCompositor::MockCompositor()
-{
- pthread_create(&m_thread, 0, run, this);
-
- m_mutex.lock();
- m_waitCondition.wait(&m_mutex);
- m_mutex.unlock();
-}
-
-MockCompositor::~MockCompositor()
-{
- m_alive = false;
- m_waitCondition.wakeOne();
- pthread_join(m_thread, 0);
-}
-
-void MockCompositor::lock()
-{
- m_mutex.lock();
-}
-
-void MockCompositor::unlock()
-{
- m_mutex.unlock();
-}
-
-void MockCompositor::applicationInitialized()
-{
- m_ready = true;
-}
-
-int MockCompositor::waylandFileDescriptor() const
-{
- return m_compositor->fileDescriptor();
-}
-
-void MockCompositor::processWaylandEvents()
-{
- m_waitCondition.wakeOne();
-}
-
-void MockCompositor::setOutputMode(const QSize &size)
-{
- Command command = makeCommand(Impl::Compositor::setOutputMode, m_compositor);
- command.parameters << size;
- processCommand(command);
-}
-
-void MockCompositor::setKeyboardFocus(const QSharedPointer<MockSurface> &surface)
-{
- Command command = makeCommand(Impl::Compositor::setKeyboardFocus, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- processCommand(command);
-}
-
-void MockCompositor::sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos)
-{
- Command command = makeCommand(Impl::Compositor::sendMousePress, m_compositor);
- command.parameters << QVariant::fromValue(surface) << pos;
- processCommand(command);
-}
-
-void MockCompositor::sendMouseRelease(const QSharedPointer<MockSurface> &surface)
-{
- Command command = makeCommand(Impl::Compositor::sendMouseRelease, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- processCommand(command);
-}
-
-void MockCompositor::sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code)
-{
- Command command = makeCommand(Impl::Compositor::sendKeyPress, m_compositor);
- command.parameters << QVariant::fromValue(surface) << code;
- processCommand(command);
-}
-
-void MockCompositor::sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code)
-{
- Command command = makeCommand(Impl::Compositor::sendKeyRelease, m_compositor);
- command.parameters << QVariant::fromValue(surface) << code;
- processCommand(command);
-}
-
-void MockCompositor::sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id)
-{
- Command command = makeCommand(Impl::Compositor::sendTouchDown, m_compositor);
- command.parameters << QVariant::fromValue(surface) << position << id;
- processCommand(command);
-}
-
-void MockCompositor::sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id)
-{
- Command command = makeCommand(Impl::Compositor::sendTouchMotion, m_compositor);
- command.parameters << QVariant::fromValue(surface) << position << id;
- processCommand(command);
-}
-
-void MockCompositor::sendTouchUp(const QSharedPointer<MockSurface> &surface, int id)
-{
- Command command = makeCommand(Impl::Compositor::sendTouchUp, m_compositor);
- command.parameters << QVariant::fromValue(surface) << id;
- processCommand(command);
-}
-
-void MockCompositor::sendTouchFrame(const QSharedPointer<MockSurface> &surface)
-{
- Command command = makeCommand(Impl::Compositor::sendTouchFrame, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- processCommand(command);
-}
-
-void MockCompositor::sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface)
-{
- Command command = makeCommand(Impl::Compositor::sendDataDeviceDataOffer, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- processCommand(command);
-}
-
-void MockCompositor::sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint& position)
-{
- Command command = makeCommand(Impl::Compositor::sendDataDeviceEnter, m_compositor);
- command.parameters << QVariant::fromValue(surface) << QVariant::fromValue(position);
- processCommand(command);
-}
-
-void MockCompositor::sendDataDeviceMotion(const QPoint &position)
-{
- Command command = makeCommand(Impl::Compositor::sendDataDeviceMotion, m_compositor);
- command.parameters << QVariant::fromValue(position);
- processCommand(command);
-}
-
-void MockCompositor::sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface)
-{
- Command command = makeCommand(Impl::Compositor::sendDataDeviceDrop, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- processCommand(command);
-}
-
-void MockCompositor::sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface)
-{
- Command command = makeCommand(Impl::Compositor::sendDataDeviceLeave, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- processCommand(command);
-}
-
-void MockCompositor::sendAddOutput()
-{
- Command command = makeCommand(Impl::Compositor::sendAddOutput, m_compositor);
- processCommand(command);
-}
-
-void MockCompositor::sendRemoveOutput(const QSharedPointer<MockOutput> &output)
-{
- Command command = makeCommand(Impl::Compositor::sendRemoveOutput, m_compositor);
- command.parameters << QVariant::fromValue(output);
- processCommand(command);
-}
-
-void MockCompositor::sendOutputGeometry(const QSharedPointer<MockOutput> &output, const QRect &geometry)
-{
- Command command = makeCommand(Impl::Compositor::sendOutputGeometry, m_compositor);
- command.parameters << QVariant::fromValue(output);
- command.parameters << QVariant::fromValue(geometry);
- processCommand(command);
-}
-
-void MockCompositor::sendSurfaceEnter(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output)
-{
- Command command = makeCommand(Impl::Compositor::sendSurfaceEnter, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- command.parameters << QVariant::fromValue(output);
- processCommand(command);
-}
-
-void MockCompositor::sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output)
-{
- Command command = makeCommand(Impl::Compositor::sendSurfaceLeave, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- command.parameters << QVariant::fromValue(output);
- processCommand(command);
-}
-
-void MockCompositor::sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size)
-{
- Command command = makeCommand(Impl::Compositor::sendShellSurfaceConfigure, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- command.parameters << QVariant::fromValue(size);
- processCommand(command);
-}
-
-void MockCompositor::sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size)
-{
- Command command = makeCommand(Impl::Compositor::sendIviSurfaceConfigure, m_compositor);
- command.parameters << QVariant::fromValue(iviSurface);
- command.parameters << QVariant::fromValue(size);
- processCommand(command);
-}
-
-void MockCompositor::sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size, const QVector<uint> &states)
-{
- Command command = makeCommand(Impl::Compositor::sendXdgToplevelV6Configure, m_compositor);
- command.parameters << QVariant::fromValue(toplevel);
- command.parameters << QVariant::fromValue(size);
- QByteArray statesBytes(reinterpret_cast<const char *>(states.data()),
- states.size() * static_cast<int>(sizeof(uint)));
- command.parameters << statesBytes;
- processCommand(command);
-}
-
-void MockCompositor::waitForStartDrag()
-{
- Command command = makeCommand(Impl::Compositor::waitForStartDrag, m_compositor);
- processCommand(command);
-}
-
-QSharedPointer<MockSurface> MockCompositor::surface()
-{
- QSharedPointer<MockSurface> result;
- lock();
- QVector<Impl::Surface *> surfaces = m_compositor->surfaces();
- foreach (Impl::Surface *surface, surfaces) {
- // we don't want to mistake the cursor surface for a window surface
- if (surface->isMapped()) {
- result = surface->mockSurface();
- break;
- }
- }
- unlock();
- return result;
-}
-
-QSharedPointer<MockOutput> MockCompositor::output(int index)
-{
- QSharedPointer<MockOutput> result;
- lock();
- if (Impl::Output *output = m_compositor->outputs().value(index, nullptr))
- result = output->mockOutput();
- unlock();
- return result;
-}
-
-QSharedPointer<MockIviSurface> MockCompositor::iviSurface(int index)
-{
- QSharedPointer<MockIviSurface> result;
- lock();
- if (Impl::IviSurface *toplevel = m_compositor->iviApplication()->iviSurfaces().value(index, nullptr))
- result = toplevel->mockIviSurface();
- unlock();
- return result;
-}
-
-QSharedPointer<MockXdgToplevelV6> MockCompositor::xdgToplevelV6(int index)
-{
- QSharedPointer<MockXdgToplevelV6> result;
- lock();
- if (Impl::XdgToplevelV6 *toplevel = m_compositor->xdgShellV6()->toplevels().value(index, nullptr))
- result = toplevel->mockToplevel();
- unlock();
- return result;
-}
-
-QSharedPointer<MockSurface> MockCompositor::fullScreenShellV1Surface(int index)
-{
- QSharedPointer<MockSurface> result;
- lock();
- if (Impl::Surface *surface = m_compositor->fullScreenShellV1()->surfaces().value(index, nullptr))
- result = surface->mockSurface();
- unlock();
- return result;
-}
-
-MockCompositor::Command MockCompositor::makeCommand(Command::Callback callback, void *target)
-{
- Command command;
- command.callback = callback;
- command.target = target;
- return command;
-}
-
-void MockCompositor::processCommand(const Command &command)
-{
- lock();
- m_commandQueue << command;
- unlock();
-
- m_waitCondition.wakeOne();
-}
-
-void MockCompositor::dispatchCommands()
-{
- lock();
- int count = m_commandQueue.length();
- unlock();
-
- for (int i = 0; i < count; ++i) {
- lock();
- const Command command = m_commandQueue.takeFirst();
- unlock();
- command.callback(command.target, command.parameters);
+namespace MockCompositor {
+
+DefaultCompositor::DefaultCompositor()
+{
+ {
+ Lock l(this);
+
+ // Globals: Should ideally always be at least the latest versions we support.
+ // Legacy versions can override in separate tests by removing and adding.
+ add<WlCompositor>();
+ add<SubCompositor>();
+ add<Output>();
+ add<Seat>(Seat::capability_pointer);
+ add<XdgWmBase>();
+ add<Shm>();
+ // TODO: other shells, viewporter, xdgoutput etc
+
+ QObject::connect(get<WlCompositor>(), &WlCompositor::surfaceCreated, [&] (Surface *surface){
+ QObject::connect(surface, &Surface::bufferCommitted, [=] {
+ if (m_config.autoRelease) {
+ // Pretend we made a copy of the buffer and just release it immediately
+ surface->m_committed.buffer->send_release();
+ }
+ if (m_config.autoEnter && surface->m_outputs.empty())
+ surface->sendEnter(get<Output>());
+ wl_display_flush_clients(m_display);
+ });
+ });
+
+ QObject::connect(get<XdgWmBase>(), &XdgWmBase::toplevelCreated, [&] (XdgToplevel *toplevel) {
+ // Needed because lambdas don't support Qt::DirectConnection
+ exec([&]{
+ if (m_config.autoConfigure)
+ toplevel->sendCompleteConfigure();
+ });
+ });
}
+ Q_ASSERT(isClean());
}
-void *MockCompositor::run(void *data)
-{
- MockCompositor *controller = static_cast<MockCompositor *>(data);
-
- Impl::Compositor compositor;
-
- controller->m_compositor = &compositor;
- controller->m_waitCondition.wakeOne();
-
- while (!controller->m_ready) {
- controller->dispatchCommands();
- compositor.dispatchEvents(20);
- }
-
- while (controller->m_alive) {
- {
- QMutexLocker locker(&controller->m_mutex);
- if (controller->m_commandQueue.isEmpty())
- controller->m_waitCondition.wait(&controller->m_mutex);
- }
- controller->dispatchCommands();
- compositor.dispatchEvents(20);
- }
-
- return 0;
-}
-
-namespace Impl {
-
-Compositor::Compositor()
- : m_display(wl_display_create())
-{
- if (wl_display_add_socket(m_display, 0)) {
- fprintf(stderr, "Fatal: Failed to open server socket\n");
- exit(EXIT_FAILURE);
- }
-
- wl_global_create(m_display, &wl_compositor_interface, 1, this, bindCompositor);
-
- m_data_device_manager.reset(new DataDeviceManager(this, m_display));
-
- wl_display_init_shm(m_display);
-
- m_seat.reset(new Seat(this, m_display));
- m_pointer = m_seat->pointer();
- m_keyboard = m_seat->keyboard();
- m_touch = m_seat->touch();
-
- m_outputs.append(new Output(m_display, QSize(1920, 1080), QPoint(0, 0)));
- m_iviApplication.reset(new IviApplication(m_display));
- m_wlShell.reset(new WlShell(m_display));
- m_xdgShellV6.reset(new XdgShellV6(m_display));
- m_fullScreenShellV1.reset(new FullScreenShellV1(m_display));
-
- m_loop = wl_display_get_event_loop(m_display);
- m_fd = wl_event_loop_get_fd(m_loop);
-}
-
-Compositor::~Compositor()
-{
- wl_display_destroy(m_display);
-}
-
-void Compositor::dispatchEvents(int timeout)
-{
- wl_display_flush_clients(m_display);
- wl_event_loop_dispatch(m_loop, timeout);
-}
-
-static void compositor_create_surface(wl_client *client, wl_resource *compositorResource, uint32_t id)
+uint DefaultCompositor::sendXdgShellPing()
{
- Compositor *compositor = static_cast<Compositor *>(wl_resource_get_user_data(compositorResource));
- compositor->addSurface(new Surface(client, id, wl_resource_get_version(compositorResource), compositor));
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ uint serial = nextSerial();
+ auto *base = get<XdgWmBase>();
+ const auto resourceMap = base->resourceMap();
+ Q_ASSERT(resourceMap.size() == 1); // binding more than once shouldn't be needed
+ base->send_ping(resourceMap.first()->handle, serial);
+ return serial;
}
-static void compositor_create_region(wl_client *client, wl_resource *compositorResource, uint32_t id)
+void DefaultCompositor::xdgPingAndWaitForPong()
{
- Q_UNUSED(client);
- Q_UNUSED(compositorResource);
- Q_UNUSED(id);
+ QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong);
+ uint serial = exec([=] { return sendXdgShellPing(); });
+ QTRY_COMPARE(pongSpy.count(), 1);
+ QTRY_COMPARE(pongSpy.first().at(0).toUInt(), serial);
}
-void Compositor::bindCompositor(wl_client *client, void *compositorData, uint32_t version, uint32_t id)
-{
- static const struct wl_compositor_interface compositorInterface = {
- compositor_create_surface,
- compositor_create_region
- };
-
- wl_resource *resource = wl_resource_create(client, &wl_compositor_interface, static_cast<int>(version), id);
- wl_resource_set_implementation(resource, &compositorInterface, compositorData, nullptr);
-}
-
-static void unregisterResourceCallback(wl_listener *listener, void *data)
-{
- struct wl_resource *resource = reinterpret_cast<struct wl_resource *>(data);
- wl_list_remove(wl_resource_get_link(resource));
- delete listener;
-}
-
-void registerResource(wl_list *list, wl_resource *resource)
-{
- wl_list_insert(list, wl_resource_get_link(resource));
-
- wl_listener *listener = new wl_listener;
- listener->notify = unregisterResourceCallback;
-
- wl_resource_add_destroy_listener(resource, listener);
-}
-
-QVector<Surface *> Compositor::surfaces() const
-{
- return m_surfaces;
-}
-
-QVector<Output *> Compositor::outputs() const
-{
- return m_outputs;
-}
-
-IviApplication *Compositor::iviApplication() const
-{
- return m_iviApplication.data();
-}
-
-XdgShellV6 *Compositor::xdgShellV6() const
-{
- return m_xdgShellV6.data();
-}
-
-FullScreenShellV1 *Compositor::fullScreenShellV1() const
-{
- return m_fullScreenShellV1.data();
-}
-
-uint32_t Compositor::nextSerial()
-{
- return wl_display_next_serial(m_display);
-}
-
-void Compositor::addSurface(Surface *surface)
-{
- m_surfaces << surface;
-}
-
-void Compositor::removeSurface(Surface *surface)
-{
- m_surfaces.removeOne(surface);
- m_keyboard->handleSurfaceDestroyed(surface);
- m_pointer->handleSurfaceDestroyed(surface);
- m_fullScreenShellV1->removeSurface(surface);
-}
-
-Surface *Compositor::resolveSurface(const QVariant &v)
-{
- QSharedPointer<MockSurface> mockSurface = v.value<QSharedPointer<MockSurface> >();
- return mockSurface ? mockSurface->handle() : nullptr;
-}
-
-Output *Compositor::resolveOutput(const QVariant &v)
-{
- QSharedPointer<MockOutput> mockOutput = v.value<QSharedPointer<MockOutput> >();
- return mockOutput ? mockOutput->handle() : nullptr;
-}
-
-IviSurface *Compositor::resolveIviSurface(const QVariant &v)
-{
- QSharedPointer<MockIviSurface> mockIviSurface = v.value<QSharedPointer<MockIviSurface>>();
- return mockIviSurface ? mockIviSurface->handle() : nullptr;
-}
-
-XdgToplevelV6 *Compositor::resolveToplevel(const QVariant &v)
-{
- QSharedPointer<MockXdgToplevelV6> mockToplevel = v.value<QSharedPointer<MockXdgToplevelV6>>();
- return mockToplevel ? mockToplevel->handle() : nullptr;
-}
-
-}
+} // namespace MockCompositor
diff --git a/tests/auto/client/shared/mockcompositor.h b/tests/auto/client/shared/mockcompositor.h
index 4bab1ed67..07366a493 100644
--- a/tests/auto/client/shared/mockcompositor.h
+++ b/tests/auto/client/shared/mockcompositor.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@@ -29,262 +29,55 @@
#ifndef MOCKCOMPOSITOR_H
#define MOCKCOMPOSITOR_H
-#include "mockxdgshellv6.h"
-#include "mockiviapplication.h"
-#include "mockfullscreenshellv1.h"
+#include "corecompositor.h"
+#include "coreprotocol.h"
+#include "xdgshell.h"
-#include <pthread.h>
-#include <qglobal.h>
-#include <wayland-server-core.h>
+#include <QtGui/QGuiApplication>
-#include <QImage>
-#include <QMutex>
-#include <QRect>
-#include <QSharedPointer>
-#include <QVariant>
-#include <QVector>
-#include <QWaitCondition>
-
-namespace Impl {
-
-typedef void (**Implementation)(void);
-
-class Keyboard;
-class Pointer;
-class Touch;
-class Seat;
-class DataDeviceManager;
-class Surface;
-class Output;
-class IviApplication;
-class WlShell;
-class XdgShellV6;
-
-class Compositor
-{
-public:
- Compositor();
- ~Compositor();
-
- int fileDescriptor() const { return m_fd; }
- void dispatchEvents(int timeout = 0);
-
- uint32_t nextSerial();
- uint32_t time() { return ++m_time; }
-
- QVector<Surface *> surfaces() const;
- QVector<Output *> outputs() const;
-
- IviApplication *iviApplication() const;
- XdgShellV6 *xdgShellV6() const;
- FullScreenShellV1 *fullScreenShellV1() const;
-
- void addSurface(Surface *surface);
- void removeSurface(Surface *surface);
-
- static void setKeyboardFocus(void *data, const QList<QVariant> &parameters);
- static void sendMousePress(void *data, const QList<QVariant> &parameters);
- static void sendMouseRelease(void *data, const QList<QVariant> &parameters);
- static void sendKeyPress(void *data, const QList<QVariant> &parameters);
- static void sendKeyRelease(void *data, const QList<QVariant> &parameters);
- static void sendTouchDown(void *data, const QList<QVariant> &parameters);
- static void sendTouchUp(void *data, const QList<QVariant> &parameters);
- static void sendTouchMotion(void *data, const QList<QVariant> &parameters);
- static void sendTouchFrame(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceDataOffer(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceEnter(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceMotion(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceDrop(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceLeave(void *data, const QList<QVariant> &parameters);
- static void waitForStartDrag(void *data, const QList<QVariant> &parameters);
- static void setOutputMode(void *compositor, const QList<QVariant> &parameters);
- static void sendAddOutput(void *data, const QList<QVariant> &parameters);
- static void sendRemoveOutput(void *data, const QList<QVariant> &parameters);
- static void sendOutputGeometry(void *data, const QList<QVariant> &parameters);
- static void sendSurfaceEnter(void *data, const QList<QVariant> &parameters);
- static void sendSurfaceLeave(void *data, const QList<QVariant> &parameters);
- static void sendShellSurfaceConfigure(void *data, const QList<QVariant> &parameters);
- static void sendIviSurfaceConfigure(void *data, const QList<QVariant> &parameters);
- static void sendXdgToplevelV6Configure(void *data, const QList<QVariant> &parameters);
-
-public:
- bool m_startDragSeen = false;
-
-private:
- static void bindCompositor(wl_client *client, void *data, uint32_t version, uint32_t id);
- static Surface *resolveSurface(const QVariant &v);
- static Output *resolveOutput(const QVariant &v);
- static IviSurface *resolveIviSurface(const QVariant &v);
- static XdgToplevelV6 *resolveToplevel(const QVariant &v);
-
- void initShm();
-
- QRect m_outputGeometry;
-
- wl_display *m_display = nullptr;
- wl_event_loop *m_loop = nullptr;
- int m_fd = -1;
-
- uint32_t m_time = 0;
-
- QScopedPointer<Seat> m_seat;
- Pointer *m_pointer = nullptr;
- Keyboard *m_keyboard = nullptr;
- Touch *m_touch = nullptr;
- QScopedPointer<DataDeviceManager> m_data_device_manager;
- QVector<Surface *> m_surfaces;
- QVector<Output *> m_outputs;
- QScopedPointer<IviApplication> m_iviApplication;
- QScopedPointer<WlShell> m_wlShell;
- QScopedPointer<XdgShellV6> m_xdgShellV6;
- QScopedPointer<FullScreenShellV1> m_fullScreenShellV1;
-};
-
-void registerResource(wl_list *list, wl_resource *resource);
-
-}
-
-class MockSurface
-{
-public:
- Impl::Surface *handle() const { return m_surface; }
-
- QImage image;
-
-private:
- MockSurface(Impl::Surface *surface);
- friend class Impl::Compositor;
- friend class Impl::Surface;
-
- Impl::Surface *m_surface = nullptr;
-};
-
-Q_DECLARE_METATYPE(QSharedPointer<MockSurface>)
-
-class MockIviSurface
-{
-public:
- Impl::IviSurface *handle() const { return m_iviSurface; }
- const uint iviId;
-
-private:
- MockIviSurface(Impl::IviSurface *iviSurface) : iviId(iviSurface->iviId()), m_iviSurface(iviSurface) {}
- friend class Impl::Compositor;
- friend class Impl::IviSurface;
-
- Impl::IviSurface *m_iviSurface;
-};
+#ifndef BTN_LEFT
+// As defined in linux/input-event-codes.h
+#define BTN_LEFT 0x110
+#endif
-Q_DECLARE_METATYPE(QSharedPointer<MockIviSurface>)
+namespace MockCompositor {
-class MockXdgToplevelV6 : public QObject
+class DefaultCompositor : public CoreCompositor
{
- Q_OBJECT
-public:
- Impl::XdgToplevelV6 *handle() const { return m_toplevel; }
-
- void sendConfigure(const QSharedPointer<MockXdgToplevelV6> toplevel);
-
-signals:
- uint setMinimizedRequested();
- uint setMaximizedRequested();
- uint unsetMaximizedRequested();
- uint setFullscreenRequested();
- uint unsetFullscreenRequested();
- void windowGeometryRequested(QRect geometry); // NOTE: This is really an xdg surface event
-
-private:
- MockXdgToplevelV6(Impl::XdgToplevelV6 *toplevel) : m_toplevel(toplevel) {}
- friend class Impl::Compositor;
- friend class Impl::XdgToplevelV6;
-
- Impl::XdgToplevelV6 *m_toplevel;
-};
-
-Q_DECLARE_METATYPE(QSharedPointer<MockXdgToplevelV6>)
-
-class MockOutput {
public:
- Impl::Output *handle() const { return m_output; }
- MockOutput(Impl::Output *output);
-private:
- Impl::Output *m_output = nullptr;
+ explicit DefaultCompositor();
+ // Convenience functions
+ Surface *surface(int i = 0) { return get<WlCompositor>()->m_surfaces.value(i, nullptr); }
+ XdgSurface *xdgSurface(int i = 0) { return get<XdgWmBase>()->m_xdgSurfaces.value(i, nullptr); }
+ XdgToplevel *xdgToplevel(int i = 0) { return get<XdgWmBase>()->toplevel(i); }
+ XdgPopup *xdgPopup(int i = 0) { return get<XdgWmBase>()->popup(i); }
+ Pointer *pointer() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_pointer; }
+ uint sendXdgShellPing();
+ void xdgPingAndWaitForPong();
+ // Things that can be changed run-time without confusing the client (i.e. don't require separate tests)
+ struct Config {
+ bool autoEnter = true;
+ bool autoRelease = true;
+ bool autoConfigure = false;
+ } m_config;
+ void resetConfig() { exec([&] { m_config = Config{}; }); }
};
-Q_DECLARE_METATYPE(QSharedPointer<MockOutput>)
-
-class MockCompositor
-{
-public:
- MockCompositor();
- ~MockCompositor();
-
- void applicationInitialized();
-
- int waylandFileDescriptor() const;
- void processWaylandEvents();
-
- void setOutputMode(const QSize &size);
- void setKeyboardFocus(const QSharedPointer<MockSurface> &surface);
- void sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos);
- void sendMouseRelease(const QSharedPointer<MockSurface> &surface);
- void sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code);
- void sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code);
- void sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id);
- void sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id);
- void sendTouchUp(const QSharedPointer<MockSurface> &surface, int id);
- void sendTouchFrame(const QSharedPointer<MockSurface> &surface);
- void sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface);
- void sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint &position);
- void sendDataDeviceMotion(const QPoint &position);
- void sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface);
- void sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface);
- void sendAddOutput();
- void sendRemoveOutput(const QSharedPointer<MockOutput> &output);
- void sendOutputGeometry(const QSharedPointer<MockOutput> &output, const QRect &geometry);
- void sendSurfaceEnter(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output);
- void sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output);
- void sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size = QSize(0, 0));
- void sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size);
- void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0),
- const QVector<uint> &states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED });
- void waitForStartDrag();
-
- QSharedPointer<MockSurface> surface();
- QSharedPointer<MockOutput> output(int index = 0);
- QSharedPointer<MockIviSurface> iviSurface(int index = 0);
- QSharedPointer<MockXdgToplevelV6> xdgToplevelV6(int index = 0);
- QSharedPointer<MockSurface> fullScreenShellV1Surface(int index = 0);
-
- void lock();
- void unlock();
-
-private:
- struct Command
- {
- typedef void (*Callback)(void *target, const QList<QVariant> &parameters);
-
- Callback callback;
- void *target = nullptr;
- QList<QVariant> parameters;
- };
-
- static Command makeCommand(Command::Callback callback, void *target);
-
- void processCommand(const Command &command);
- void dispatchCommands();
-
- static void *run(void *data);
-
- bool m_alive = true;
- bool m_ready = false;
- pthread_t m_thread;
- QMutex m_mutex;
- QWaitCondition m_waitCondition;
-
- Impl::Compositor *m_compositor = nullptr;
-
- QList<Command> m_commandQueue;
-};
+} // namespace MockCompositor
+
+#define QCOMPOSITOR_VERIFY(expr) QVERIFY(exec([&]{ return expr; }))
+#define QCOMPOSITOR_TRY_VERIFY(expr) QTRY_VERIFY(exec([&]{ return expr; }))
+#define QCOMPOSITOR_COMPARE(expr, expr2) QCOMPARE(exec([&]{ return expr; }), expr2)
+#define QCOMPOSITOR_TRY_COMPARE(expr, expr2) QTRY_COMPARE(exec([&]{ return expr; }), expr2)
+
+#define QCOMPOSITOR_TEST_MAIN(test) \
+int main(int argc, char **argv) \
+{ \
+ setenv("XDG_RUNTIME_DIR", ".", 1); \
+ setenv("QT_QPA_PLATFORM", "wayland", 1); \
+ test tc; \
+ QGuiApplication app(argc, argv); \
+ return QTest::qExec(&tc, argc, argv); \
+} \
#endif
diff --git a/tests/auto/client/shared/shared.pri b/tests/auto/client/shared/shared.pri
index db71de528..3376c73bc 100644
--- a/tests/auto/client/shared/shared.pri
+++ b/tests/auto/client/shared/shared.pri
@@ -1,34 +1,21 @@
-CONFIG += testcase link_pkgconfig
-QT += testlib
-QT += core-private gui-private waylandclient-private
+QT += testlib waylandclient-private
+CONFIG += testcase wayland-scanner
+QMAKE_USE += wayland-server
-QMAKE_USE += wayland-client wayland-server
-
-CONFIG += wayland-scanner
WAYLANDSERVERSOURCES += \
- ../../../../src/3rdparty/protocol/ivi-application.xml \
- ../../../../src/3rdparty/protocol/wayland.xml \
- ../../../../src/3rdparty/protocol/xdg-shell-unstable-v6.xml \
- ../../../../src/3rdparty/protocol/fullscreen-shell-unstable-v1.xml
+ $$PWD/../../../../src/3rdparty/protocol/wayland.xml \
+ $$PWD/../../../../src/3rdparty/protocol/xdg-shell.xml
INCLUDEPATH += ../shared
-SOURCES += \
- ../shared/mockcompositor.cpp \
- ../shared/mockfullscreenshellv1.cpp \
- ../shared/mockinput.cpp \
- ../shared/mockiviapplication.cpp \
- ../shared/mockwlshell.cpp \
- ../shared/mockxdgshellv6.cpp \
- ../shared/mocksurface.cpp \
- ../shared/mockoutput.cpp
-
HEADERS += \
- ../shared/mockcompositor.h \
- ../shared/mockfullscreenshellv1.h \
- ../shared/mockinput.h \
- ../shared/mockiviapplication.h \
- ../shared/mockwlshell.h \
- ../shared/mockxdgshellv6.h \
- ../shared/mocksurface.h \
- ../shared/mockoutput.h
+ $$PWD/corecompositor.h \
+ $$PWD/coreprotocol.h \
+ $$PWD/mockcompositor.h \
+ $$PWD/xdgshell.h
+
+SOURCES += \
+ $$PWD/corecompositor.cpp \
+ $$PWD/coreprotocol.cpp \
+ $$PWD/mockcompositor.cpp \
+ $$PWD/xdgshell.cpp
diff --git a/tests/auto/client/shared/xdgshell.cpp b/tests/auto/client/shared/xdgshell.cpp
new file mode 100644
index 000000000..ebbcc2942
--- /dev/null
+++ b/tests/auto/client/shared/xdgshell.cpp
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "xdgshell.h"
+
+namespace MockCompositor {
+
+XdgWmBase::XdgWmBase(CoreCompositor *compositor, int version)
+ : QtWaylandServer::xdg_wm_base(compositor->m_display, version)
+ , m_compositor(compositor)
+{
+}
+
+XdgToplevel *XdgWmBase::toplevel(int i)
+{
+ int j = 0;
+ for (auto *xdgSurface : qAsConst(m_xdgSurfaces)) {
+ if (auto *toplevel = xdgSurface->m_toplevel) {
+ if (j == i)
+ return toplevel;
+ ++j;
+ }
+ }
+ return nullptr;
+}
+
+XdgPopup *XdgWmBase::popup(int i)
+{
+ int j = 0;
+ for (auto *xdgSurface : qAsConst(m_xdgSurfaces)) {
+ if (auto *popup = xdgSurface->m_popup) {
+ if (j == i)
+ return popup;
+ ++j;
+ }
+ }
+ return nullptr;
+}
+
+void XdgWmBase::xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, wl_resource *surface)
+{
+ auto *s = fromResource<Surface>(surface);
+ auto *xdgSurface = new XdgSurface(this, s, resource->client(), id, resource->version());
+ m_xdgSurfaces << xdgSurface;
+ emit xdgSurfaceCreated(xdgSurface);
+}
+
+void XdgWmBase::xdg_wm_base_pong(Resource *resource, uint32_t serial)
+{
+ Q_UNUSED(resource);
+ emit pong(serial);
+}
+
+XdgSurface::XdgSurface(XdgWmBase *xdgWmBase, Surface *surface, wl_client *client, int id, int version)
+ : QtWaylandServer::xdg_surface(client, id, version)
+ , m_xdgWmBase(xdgWmBase)
+ , m_surface(surface)
+{
+ QVERIFY(!surface->m_pending.buffer);
+ QVERIFY(!surface->m_committed.buffer);
+ connect(this, &XdgSurface::toplevelCreated, xdgWmBase, &XdgWmBase::toplevelCreated);
+ connect(surface, &Surface::attach, this, &XdgSurface::verifyConfigured);
+ connect(surface, &Surface::commit, this, [this] {
+ if (m_ackedConfigureSerial != m_committedConfigureSerial) {
+ m_committedConfigureSerial = m_ackedConfigureSerial;
+ emit configureCommitted(m_committedConfigureSerial);
+ }
+ });
+}
+
+void XdgSurface::sendConfigure(uint serial)
+{
+ Q_ASSERT(serial);
+ m_pendingConfigureSerials.append(serial);
+ m_configureSent = true;
+ xdg_surface::send_configure(serial);
+}
+
+uint XdgSurface::sendConfigure()
+{
+ const uint serial = m_xdgWmBase->m_compositor->nextSerial();
+ sendConfigure(serial);
+ return serial;
+}
+
+void XdgSurface::xdg_surface_get_toplevel(Resource *resource, uint32_t id)
+{
+ QVERIFY(!m_toplevel);
+ QVERIFY(!m_popup);
+ m_toplevel = new XdgToplevel(this, id, resource->version());
+ emit toplevelCreated(m_toplevel);
+}
+
+void XdgSurface::xdg_surface_get_popup(Resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner)
+{
+ Q_UNUSED(parent);
+ Q_UNUSED(positioner);
+ QVERIFY(!m_toplevel);
+ QVERIFY(!m_popup);
+ m_popup = new XdgPopup(this, id, resource->version());
+}
+
+void XdgSurface::xdg_surface_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+ bool removed = m_xdgWmBase->m_xdgSurfaces.removeOne(this);
+ Q_ASSERT(removed);
+ delete this;
+}
+
+void XdgSurface::xdg_surface_ack_configure(Resource *resource, uint32_t serial)
+{
+ Q_UNUSED(resource);
+ QVERIFY2(m_pendingConfigureSerials.contains(serial), qPrintable(QString::number(serial)));
+ m_ackedConfigureSerial = serial;
+ while (!m_pendingConfigureSerials.empty()) {
+ uint s = m_pendingConfigureSerials.takeFirst();
+ if (s == serial)
+ return;
+ }
+}
+
+XdgToplevel::XdgToplevel(XdgSurface *xdgSurface, int id, int version)
+ : QtWaylandServer::xdg_toplevel(xdgSurface->resource()->client(), id, version)
+ , m_xdgSurface(xdgSurface)
+{
+}
+
+void XdgToplevel::sendConfigure(const QSize &size, const QVector<uint> &states)
+{
+ send_configure(size.width(), size.height(), toByteArray(states));
+}
+
+uint XdgToplevel::sendCompleteConfigure(const QSize &size, const QVector<uint> &states)
+{
+ sendConfigure(size, states);
+ return m_xdgSurface->sendConfigure();
+}
+
+XdgPopup::XdgPopup(XdgSurface *xdgSurface, int id, int version)
+ : QtWaylandServer::xdg_popup(xdgSurface->resource()->client(), id, version)
+ , m_xdgSurface(xdgSurface)
+{
+}
+
+void XdgPopup::sendConfigure(const QRect &geometry)
+{
+ send_configure(geometry.x(), geometry.y(), geometry.width(), geometry.height());
+}
+
+void XdgPopup::xdg_popup_grab(QtWaylandServer::xdg_popup::Resource *resource, wl_resource *seat, uint32_t serial)
+{
+ Q_UNUSED(resource);
+ Q_UNUSED(seat); // TODO: verify correct seat as well
+ //TODO: verify no other popup has grabbed
+ QVERIFY(!m_grabbed);
+ m_grabbed = true;
+ m_grabSerial = serial;
+}
+
+} // namespace MockCompositor
diff --git a/tests/auto/client/shared/xdgshell.h b/tests/auto/client/shared/xdgshell.h
new file mode 100644
index 000000000..3fcec7983
--- /dev/null
+++ b/tests/auto/client/shared/xdgshell.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MOCKCOMPOSITOR_XDGSHELL_H
+#define MOCKCOMPOSITOR_XDGSHELL_H
+
+#include "coreprotocol.h"
+#include <qwayland-server-xdg-shell.h>
+
+namespace MockCompositor {
+
+class XdgSurface;
+class XdgToplevel;
+class XdgPopup;
+using XdgPositioner = QtWaylandServer::xdg_positioner;
+
+class XdgWmBase : public Global, public QtWaylandServer::xdg_wm_base
+{
+ Q_OBJECT
+public:
+ explicit XdgWmBase(CoreCompositor *compositor, int version = 1);
+ using QtWaylandServer::xdg_wm_base::send_ping;
+ void send_ping(uint32_t) = delete; // It's a global, use resource specific instead
+ bool isClean() override { return m_xdgSurfaces.empty(); }
+ QString dirtyMessage() override { return m_xdgSurfaces.empty() ? "clean" : "remaining xdg surfaces"; }
+ QVector<XdgSurface *> m_xdgSurfaces;
+ XdgToplevel *toplevel(int i = 0);
+ XdgPopup *popup(int i = 0);
+ CoreCompositor *m_compositor = nullptr;
+
+signals:
+ void pong(uint serial);
+ void xdgSurfaceCreated(XdgSurface *xdgSurface);
+ void toplevelCreated(XdgToplevel *toplevel);
+
+protected:
+ void xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, ::wl_resource *surface) override;
+ void xdg_wm_base_pong(Resource *resource, uint32_t serial) override;
+ void xdg_wm_base_create_positioner(Resource *resource, uint32_t id) override
+ {
+ new XdgPositioner(resource->client(), id, resource->version());
+ }
+};
+
+class XdgSurface : public QObject, public QtWaylandServer::xdg_surface
+{
+ Q_OBJECT
+public:
+ explicit XdgSurface(XdgWmBase *xdgWmBase, Surface *surface, wl_client *client, int id, int version);
+ void send_configure(uint serial) = delete; // Use the one below instead, as it tracks state
+ void sendConfigure(uint serial);
+ uint sendConfigure();
+ XdgToplevel *m_toplevel = nullptr;
+ XdgPopup *m_popup = nullptr;
+ XdgWmBase *m_xdgWmBase = nullptr;
+ Surface *m_surface = nullptr;
+ bool m_configureSent = false;
+ QVector<uint> m_pendingConfigureSerials;
+ uint m_ackedConfigureSerial = 0;
+ uint m_committedConfigureSerial = 0;
+
+public slots:
+ void verifyConfigured() { QVERIFY(m_configureSent); }
+
+signals:
+ void configureCommitted(uint);
+ void toplevelCreated(XdgToplevel *toplevel);
+
+protected:
+ void xdg_surface_get_toplevel(Resource *resource, uint32_t id) override;
+ void xdg_surface_get_popup(Resource *resource, uint32_t id, ::wl_resource *parent, ::wl_resource *positioner) override;
+ void xdg_surface_destroy_resource(Resource *resource) override;
+ void xdg_surface_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); }
+ void xdg_surface_ack_configure(Resource *resource, uint32_t serial) override;
+};
+
+class XdgToplevel : public QtWaylandServer::xdg_toplevel
+{
+public:
+ explicit XdgToplevel(XdgSurface *xdgSurface, int id, int version = 1);
+ void sendConfigure(const QSize &size = {0, 0}, const QVector<uint> &states = {});
+ uint sendCompleteConfigure(const QSize &size = {0, 0}, const QVector<uint> &states = {});
+ Surface *surface() { return m_xdgSurface->m_surface; }
+ XdgSurface *m_xdgSurface = nullptr;
+};
+
+class XdgPopup : public QtWaylandServer::xdg_popup
+{
+public:
+ explicit XdgPopup(XdgSurface *xdgSurface, int id, int version = 1);
+ void sendConfigure(const QRect &geometry);
+ Surface *surface() { return m_xdgSurface->m_surface; }
+ XdgSurface *m_xdgSurface = nullptr;
+ bool m_grabbed = false;
+ uint m_grabSerial = 0;
+protected:
+ void xdg_popup_grab(Resource *resource, ::wl_resource *seat, uint32_t serial) override;
+};
+
+} // namespace MockCompositor
+
+#endif // MOCKCOMPOSITOR_XDGSHELL_H
diff --git a/tests/auto/client/shared_old/mockcompositor.cpp b/tests/auto/client/shared_old/mockcompositor.cpp
new file mode 100644
index 000000000..df24b4091
--- /dev/null
+++ b/tests/auto/client/shared_old/mockcompositor.cpp
@@ -0,0 +1,520 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+#include "mockinput.h"
+#include "mockoutput.h"
+#include "mocksurface.h"
+#include "mockwlshell.h"
+#include "mockxdgshellv6.h"
+#include "mockiviapplication.h"
+
+#include <wayland-xdg-shell-unstable-v6-server-protocol.h>
+
+#include <stdio.h>
+MockCompositor::MockCompositor()
+{
+ pthread_create(&m_thread, 0, run, this);
+
+ m_mutex.lock();
+ m_waitCondition.wait(&m_mutex);
+ m_mutex.unlock();
+}
+
+MockCompositor::~MockCompositor()
+{
+ m_alive = false;
+ m_waitCondition.wakeOne();
+ pthread_join(m_thread, 0);
+}
+
+void MockCompositor::lock()
+{
+ m_mutex.lock();
+}
+
+void MockCompositor::unlock()
+{
+ m_mutex.unlock();
+}
+
+void MockCompositor::applicationInitialized()
+{
+ m_ready = true;
+}
+
+int MockCompositor::waylandFileDescriptor() const
+{
+ return m_compositor->fileDescriptor();
+}
+
+void MockCompositor::processWaylandEvents()
+{
+ m_waitCondition.wakeOne();
+}
+
+void MockCompositor::setOutputMode(const QSize &size)
+{
+ Command command = makeCommand(Impl::Compositor::setOutputMode, m_compositor);
+ command.parameters << size;
+ processCommand(command);
+}
+
+void MockCompositor::setKeyboardFocus(const QSharedPointer<MockSurface> &surface)
+{
+ Command command = makeCommand(Impl::Compositor::setKeyboardFocus, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ processCommand(command);
+}
+
+void MockCompositor::sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos)
+{
+ Command command = makeCommand(Impl::Compositor::sendMousePress, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << pos;
+ processCommand(command);
+}
+
+void MockCompositor::sendMouseRelease(const QSharedPointer<MockSurface> &surface)
+{
+ Command command = makeCommand(Impl::Compositor::sendMouseRelease, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ processCommand(command);
+}
+
+void MockCompositor::sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code)
+{
+ Command command = makeCommand(Impl::Compositor::sendKeyPress, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << code;
+ processCommand(command);
+}
+
+void MockCompositor::sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code)
+{
+ Command command = makeCommand(Impl::Compositor::sendKeyRelease, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << code;
+ processCommand(command);
+}
+
+void MockCompositor::sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id)
+{
+ Command command = makeCommand(Impl::Compositor::sendTouchDown, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << position << id;
+ processCommand(command);
+}
+
+void MockCompositor::sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id)
+{
+ Command command = makeCommand(Impl::Compositor::sendTouchMotion, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << position << id;
+ processCommand(command);
+}
+
+void MockCompositor::sendTouchUp(const QSharedPointer<MockSurface> &surface, int id)
+{
+ Command command = makeCommand(Impl::Compositor::sendTouchUp, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << id;
+ processCommand(command);
+}
+
+void MockCompositor::sendTouchFrame(const QSharedPointer<MockSurface> &surface)
+{
+ Command command = makeCommand(Impl::Compositor::sendTouchFrame, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ processCommand(command);
+}
+
+void MockCompositor::sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface)
+{
+ Command command = makeCommand(Impl::Compositor::sendDataDeviceDataOffer, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ processCommand(command);
+}
+
+void MockCompositor::sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint& position)
+{
+ Command command = makeCommand(Impl::Compositor::sendDataDeviceEnter, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << QVariant::fromValue(position);
+ processCommand(command);
+}
+
+void MockCompositor::sendDataDeviceMotion(const QPoint &position)
+{
+ Command command = makeCommand(Impl::Compositor::sendDataDeviceMotion, m_compositor);
+ command.parameters << QVariant::fromValue(position);
+ processCommand(command);
+}
+
+void MockCompositor::sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface)
+{
+ Command command = makeCommand(Impl::Compositor::sendDataDeviceDrop, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ processCommand(command);
+}
+
+void MockCompositor::sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface)
+{
+ Command command = makeCommand(Impl::Compositor::sendDataDeviceLeave, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ processCommand(command);
+}
+
+void MockCompositor::sendAddOutput()
+{
+ Command command = makeCommand(Impl::Compositor::sendAddOutput, m_compositor);
+ processCommand(command);
+}
+
+void MockCompositor::sendRemoveOutput(const QSharedPointer<MockOutput> &output)
+{
+ Command command = makeCommand(Impl::Compositor::sendRemoveOutput, m_compositor);
+ command.parameters << QVariant::fromValue(output);
+ processCommand(command);
+}
+
+void MockCompositor::sendOutputGeometry(const QSharedPointer<MockOutput> &output, const QRect &geometry)
+{
+ Command command = makeCommand(Impl::Compositor::sendOutputGeometry, m_compositor);
+ command.parameters << QVariant::fromValue(output);
+ command.parameters << QVariant::fromValue(geometry);
+ processCommand(command);
+}
+
+void MockCompositor::sendSurfaceEnter(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output)
+{
+ Command command = makeCommand(Impl::Compositor::sendSurfaceEnter, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ command.parameters << QVariant::fromValue(output);
+ processCommand(command);
+}
+
+void MockCompositor::sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output)
+{
+ Command command = makeCommand(Impl::Compositor::sendSurfaceLeave, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ command.parameters << QVariant::fromValue(output);
+ processCommand(command);
+}
+
+void MockCompositor::sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size)
+{
+ Command command = makeCommand(Impl::Compositor::sendShellSurfaceConfigure, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ command.parameters << QVariant::fromValue(size);
+ processCommand(command);
+}
+
+void MockCompositor::sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size)
+{
+ Command command = makeCommand(Impl::Compositor::sendIviSurfaceConfigure, m_compositor);
+ command.parameters << QVariant::fromValue(iviSurface);
+ command.parameters << QVariant::fromValue(size);
+ processCommand(command);
+}
+
+void MockCompositor::sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size, const QVector<uint> &states)
+{
+ Command command = makeCommand(Impl::Compositor::sendXdgToplevelV6Configure, m_compositor);
+ command.parameters << QVariant::fromValue(toplevel);
+ command.parameters << QVariant::fromValue(size);
+ QByteArray statesBytes(reinterpret_cast<const char *>(states.data()),
+ states.size() * static_cast<int>(sizeof(uint)));
+ command.parameters << statesBytes;
+ processCommand(command);
+}
+
+void MockCompositor::waitForStartDrag()
+{
+ Command command = makeCommand(Impl::Compositor::waitForStartDrag, m_compositor);
+ processCommand(command);
+}
+
+QSharedPointer<MockSurface> MockCompositor::surface()
+{
+ QSharedPointer<MockSurface> result;
+ lock();
+ QVector<Impl::Surface *> surfaces = m_compositor->surfaces();
+ foreach (Impl::Surface *surface, surfaces) {
+ // we don't want to mistake the cursor surface for a window surface
+ if (surface->isMapped()) {
+ result = surface->mockSurface();
+ break;
+ }
+ }
+ unlock();
+ return result;
+}
+
+QSharedPointer<MockOutput> MockCompositor::output(int index)
+{
+ QSharedPointer<MockOutput> result;
+ lock();
+ if (Impl::Output *output = m_compositor->outputs().value(index, nullptr))
+ result = output->mockOutput();
+ unlock();
+ return result;
+}
+
+QSharedPointer<MockIviSurface> MockCompositor::iviSurface(int index)
+{
+ QSharedPointer<MockIviSurface> result;
+ lock();
+ if (Impl::IviSurface *toplevel = m_compositor->iviApplication()->iviSurfaces().value(index, nullptr))
+ result = toplevel->mockIviSurface();
+ unlock();
+ return result;
+}
+
+QSharedPointer<MockXdgToplevelV6> MockCompositor::xdgToplevelV6(int index)
+{
+ QSharedPointer<MockXdgToplevelV6> result;
+ lock();
+ if (Impl::XdgToplevelV6 *toplevel = m_compositor->xdgShellV6()->toplevels().value(index, nullptr))
+ result = toplevel->mockToplevel();
+ unlock();
+ return result;
+}
+
+QSharedPointer<MockSurface> MockCompositor::fullScreenShellV1Surface(int index)
+{
+ QSharedPointer<MockSurface> result;
+ lock();
+ if (Impl::Surface *surface = m_compositor->fullScreenShellV1()->surfaces().value(index, nullptr))
+ result = surface->mockSurface();
+ unlock();
+ return result;
+}
+
+MockCompositor::Command MockCompositor::makeCommand(Command::Callback callback, void *target)
+{
+ Command command;
+ command.callback = callback;
+ command.target = target;
+ return command;
+}
+
+void MockCompositor::processCommand(const Command &command)
+{
+ lock();
+ m_commandQueue << command;
+ unlock();
+
+ m_waitCondition.wakeOne();
+}
+
+void MockCompositor::dispatchCommands()
+{
+ lock();
+ int count = m_commandQueue.length();
+ unlock();
+
+ for (int i = 0; i < count; ++i) {
+ lock();
+ const Command command = m_commandQueue.takeFirst();
+ unlock();
+ command.callback(command.target, command.parameters);
+ }
+}
+
+void *MockCompositor::run(void *data)
+{
+ MockCompositor *controller = static_cast<MockCompositor *>(data);
+
+ Impl::Compositor compositor;
+
+ controller->m_compositor = &compositor;
+ controller->m_waitCondition.wakeOne();
+
+ while (!controller->m_ready) {
+ controller->dispatchCommands();
+ compositor.dispatchEvents(20);
+ }
+
+ while (controller->m_alive) {
+ {
+ QMutexLocker locker(&controller->m_mutex);
+ if (controller->m_commandQueue.isEmpty())
+ controller->m_waitCondition.wait(&controller->m_mutex);
+ }
+ controller->dispatchCommands();
+ compositor.dispatchEvents(20);
+ }
+
+ return 0;
+}
+
+namespace Impl {
+
+Compositor::Compositor()
+ : m_display(wl_display_create())
+{
+ if (wl_display_add_socket(m_display, 0)) {
+ fprintf(stderr, "Fatal: Failed to open server socket\n");
+ exit(EXIT_FAILURE);
+ }
+
+ wl_global_create(m_display, &wl_compositor_interface, 1, this, bindCompositor);
+
+ m_data_device_manager.reset(new DataDeviceManager(this, m_display));
+
+ wl_display_init_shm(m_display);
+
+ m_seat.reset(new Seat(this, m_display));
+ m_pointer = m_seat->pointer();
+ m_keyboard = m_seat->keyboard();
+ m_touch = m_seat->touch();
+
+ m_outputs.append(new Output(m_display, QSize(1920, 1080), QPoint(0, 0)));
+ m_iviApplication.reset(new IviApplication(m_display));
+ m_wlShell.reset(new WlShell(m_display));
+ m_xdgShellV6.reset(new XdgShellV6(m_display));
+ m_fullScreenShellV1.reset(new FullScreenShellV1(m_display));
+
+ m_loop = wl_display_get_event_loop(m_display);
+ m_fd = wl_event_loop_get_fd(m_loop);
+}
+
+Compositor::~Compositor()
+{
+ wl_display_destroy(m_display);
+}
+
+void Compositor::dispatchEvents(int timeout)
+{
+ wl_display_flush_clients(m_display);
+ wl_event_loop_dispatch(m_loop, timeout);
+}
+
+static void compositor_create_surface(wl_client *client, wl_resource *compositorResource, uint32_t id)
+{
+ Compositor *compositor = static_cast<Compositor *>(wl_resource_get_user_data(compositorResource));
+ compositor->addSurface(new Surface(client, id, wl_resource_get_version(compositorResource), compositor));
+}
+
+static void compositor_create_region(wl_client *client, wl_resource *compositorResource, uint32_t id)
+{
+ Q_UNUSED(client);
+ Q_UNUSED(compositorResource);
+ Q_UNUSED(id);
+}
+
+void Compositor::bindCompositor(wl_client *client, void *compositorData, uint32_t version, uint32_t id)
+{
+ static const struct wl_compositor_interface compositorInterface = {
+ compositor_create_surface,
+ compositor_create_region
+ };
+
+ wl_resource *resource = wl_resource_create(client, &wl_compositor_interface, static_cast<int>(version), id);
+ wl_resource_set_implementation(resource, &compositorInterface, compositorData, nullptr);
+}
+
+static void unregisterResourceCallback(wl_listener *listener, void *data)
+{
+ struct wl_resource *resource = reinterpret_cast<struct wl_resource *>(data);
+ wl_list_remove(wl_resource_get_link(resource));
+ delete listener;
+}
+
+void registerResource(wl_list *list, wl_resource *resource)
+{
+ wl_list_insert(list, wl_resource_get_link(resource));
+
+ wl_listener *listener = new wl_listener;
+ listener->notify = unregisterResourceCallback;
+
+ wl_resource_add_destroy_listener(resource, listener);
+}
+
+QVector<Surface *> Compositor::surfaces() const
+{
+ return m_surfaces;
+}
+
+QVector<Output *> Compositor::outputs() const
+{
+ return m_outputs;
+}
+
+IviApplication *Compositor::iviApplication() const
+{
+ return m_iviApplication.data();
+}
+
+XdgShellV6 *Compositor::xdgShellV6() const
+{
+ return m_xdgShellV6.data();
+}
+
+FullScreenShellV1 *Compositor::fullScreenShellV1() const
+{
+ return m_fullScreenShellV1.data();
+}
+
+uint32_t Compositor::nextSerial()
+{
+ return wl_display_next_serial(m_display);
+}
+
+void Compositor::addSurface(Surface *surface)
+{
+ m_surfaces << surface;
+}
+
+void Compositor::removeSurface(Surface *surface)
+{
+ m_surfaces.removeOne(surface);
+ m_keyboard->handleSurfaceDestroyed(surface);
+ m_pointer->handleSurfaceDestroyed(surface);
+ m_fullScreenShellV1->removeSurface(surface);
+}
+
+Surface *Compositor::resolveSurface(const QVariant &v)
+{
+ QSharedPointer<MockSurface> mockSurface = v.value<QSharedPointer<MockSurface> >();
+ return mockSurface ? mockSurface->handle() : nullptr;
+}
+
+Output *Compositor::resolveOutput(const QVariant &v)
+{
+ QSharedPointer<MockOutput> mockOutput = v.value<QSharedPointer<MockOutput> >();
+ return mockOutput ? mockOutput->handle() : nullptr;
+}
+
+IviSurface *Compositor::resolveIviSurface(const QVariant &v)
+{
+ QSharedPointer<MockIviSurface> mockIviSurface = v.value<QSharedPointer<MockIviSurface>>();
+ return mockIviSurface ? mockIviSurface->handle() : nullptr;
+}
+
+XdgToplevelV6 *Compositor::resolveToplevel(const QVariant &v)
+{
+ QSharedPointer<MockXdgToplevelV6> mockToplevel = v.value<QSharedPointer<MockXdgToplevelV6>>();
+ return mockToplevel ? mockToplevel->handle() : nullptr;
+}
+
+}
diff --git a/tests/auto/client/shared_old/mockcompositor.h b/tests/auto/client/shared_old/mockcompositor.h
new file mode 100644
index 000000000..4bab1ed67
--- /dev/null
+++ b/tests/auto/client/shared_old/mockcompositor.h
@@ -0,0 +1,290 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MOCKCOMPOSITOR_H
+#define MOCKCOMPOSITOR_H
+
+#include "mockxdgshellv6.h"
+#include "mockiviapplication.h"
+#include "mockfullscreenshellv1.h"
+
+#include <pthread.h>
+#include <qglobal.h>
+#include <wayland-server-core.h>
+
+#include <QImage>
+#include <QMutex>
+#include <QRect>
+#include <QSharedPointer>
+#include <QVariant>
+#include <QVector>
+#include <QWaitCondition>
+
+namespace Impl {
+
+typedef void (**Implementation)(void);
+
+class Keyboard;
+class Pointer;
+class Touch;
+class Seat;
+class DataDeviceManager;
+class Surface;
+class Output;
+class IviApplication;
+class WlShell;
+class XdgShellV6;
+
+class Compositor
+{
+public:
+ Compositor();
+ ~Compositor();
+
+ int fileDescriptor() const { return m_fd; }
+ void dispatchEvents(int timeout = 0);
+
+ uint32_t nextSerial();
+ uint32_t time() { return ++m_time; }
+
+ QVector<Surface *> surfaces() const;
+ QVector<Output *> outputs() const;
+
+ IviApplication *iviApplication() const;
+ XdgShellV6 *xdgShellV6() const;
+ FullScreenShellV1 *fullScreenShellV1() const;
+
+ void addSurface(Surface *surface);
+ void removeSurface(Surface *surface);
+
+ static void setKeyboardFocus(void *data, const QList<QVariant> &parameters);
+ static void sendMousePress(void *data, const QList<QVariant> &parameters);
+ static void sendMouseRelease(void *data, const QList<QVariant> &parameters);
+ static void sendKeyPress(void *data, const QList<QVariant> &parameters);
+ static void sendKeyRelease(void *data, const QList<QVariant> &parameters);
+ static void sendTouchDown(void *data, const QList<QVariant> &parameters);
+ static void sendTouchUp(void *data, const QList<QVariant> &parameters);
+ static void sendTouchMotion(void *data, const QList<QVariant> &parameters);
+ static void sendTouchFrame(void *data, const QList<QVariant> &parameters);
+ static void sendDataDeviceDataOffer(void *data, const QList<QVariant> &parameters);
+ static void sendDataDeviceEnter(void *data, const QList<QVariant> &parameters);
+ static void sendDataDeviceMotion(void *data, const QList<QVariant> &parameters);
+ static void sendDataDeviceDrop(void *data, const QList<QVariant> &parameters);
+ static void sendDataDeviceLeave(void *data, const QList<QVariant> &parameters);
+ static void waitForStartDrag(void *data, const QList<QVariant> &parameters);
+ static void setOutputMode(void *compositor, const QList<QVariant> &parameters);
+ static void sendAddOutput(void *data, const QList<QVariant> &parameters);
+ static void sendRemoveOutput(void *data, const QList<QVariant> &parameters);
+ static void sendOutputGeometry(void *data, const QList<QVariant> &parameters);
+ static void sendSurfaceEnter(void *data, const QList<QVariant> &parameters);
+ static void sendSurfaceLeave(void *data, const QList<QVariant> &parameters);
+ static void sendShellSurfaceConfigure(void *data, const QList<QVariant> &parameters);
+ static void sendIviSurfaceConfigure(void *data, const QList<QVariant> &parameters);
+ static void sendXdgToplevelV6Configure(void *data, const QList<QVariant> &parameters);
+
+public:
+ bool m_startDragSeen = false;
+
+private:
+ static void bindCompositor(wl_client *client, void *data, uint32_t version, uint32_t id);
+ static Surface *resolveSurface(const QVariant &v);
+ static Output *resolveOutput(const QVariant &v);
+ static IviSurface *resolveIviSurface(const QVariant &v);
+ static XdgToplevelV6 *resolveToplevel(const QVariant &v);
+
+ void initShm();
+
+ QRect m_outputGeometry;
+
+ wl_display *m_display = nullptr;
+ wl_event_loop *m_loop = nullptr;
+ int m_fd = -1;
+
+ uint32_t m_time = 0;
+
+ QScopedPointer<Seat> m_seat;
+ Pointer *m_pointer = nullptr;
+ Keyboard *m_keyboard = nullptr;
+ Touch *m_touch = nullptr;
+ QScopedPointer<DataDeviceManager> m_data_device_manager;
+ QVector<Surface *> m_surfaces;
+ QVector<Output *> m_outputs;
+ QScopedPointer<IviApplication> m_iviApplication;
+ QScopedPointer<WlShell> m_wlShell;
+ QScopedPointer<XdgShellV6> m_xdgShellV6;
+ QScopedPointer<FullScreenShellV1> m_fullScreenShellV1;
+};
+
+void registerResource(wl_list *list, wl_resource *resource);
+
+}
+
+class MockSurface
+{
+public:
+ Impl::Surface *handle() const { return m_surface; }
+
+ QImage image;
+
+private:
+ MockSurface(Impl::Surface *surface);
+ friend class Impl::Compositor;
+ friend class Impl::Surface;
+
+ Impl::Surface *m_surface = nullptr;
+};
+
+Q_DECLARE_METATYPE(QSharedPointer<MockSurface>)
+
+class MockIviSurface
+{
+public:
+ Impl::IviSurface *handle() const { return m_iviSurface; }
+ const uint iviId;
+
+private:
+ MockIviSurface(Impl::IviSurface *iviSurface) : iviId(iviSurface->iviId()), m_iviSurface(iviSurface) {}
+ friend class Impl::Compositor;
+ friend class Impl::IviSurface;
+
+ Impl::IviSurface *m_iviSurface;
+};
+
+Q_DECLARE_METATYPE(QSharedPointer<MockIviSurface>)
+
+class MockXdgToplevelV6 : public QObject
+{
+ Q_OBJECT
+public:
+ Impl::XdgToplevelV6 *handle() const { return m_toplevel; }
+
+ void sendConfigure(const QSharedPointer<MockXdgToplevelV6> toplevel);
+
+signals:
+ uint setMinimizedRequested();
+ uint setMaximizedRequested();
+ uint unsetMaximizedRequested();
+ uint setFullscreenRequested();
+ uint unsetFullscreenRequested();
+ void windowGeometryRequested(QRect geometry); // NOTE: This is really an xdg surface event
+
+private:
+ MockXdgToplevelV6(Impl::XdgToplevelV6 *toplevel) : m_toplevel(toplevel) {}
+ friend class Impl::Compositor;
+ friend class Impl::XdgToplevelV6;
+
+ Impl::XdgToplevelV6 *m_toplevel;
+};
+
+Q_DECLARE_METATYPE(QSharedPointer<MockXdgToplevelV6>)
+
+class MockOutput {
+public:
+ Impl::Output *handle() const { return m_output; }
+ MockOutput(Impl::Output *output);
+private:
+ Impl::Output *m_output = nullptr;
+};
+
+Q_DECLARE_METATYPE(QSharedPointer<MockOutput>)
+
+class MockCompositor
+{
+public:
+ MockCompositor();
+ ~MockCompositor();
+
+ void applicationInitialized();
+
+ int waylandFileDescriptor() const;
+ void processWaylandEvents();
+
+ void setOutputMode(const QSize &size);
+ void setKeyboardFocus(const QSharedPointer<MockSurface> &surface);
+ void sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos);
+ void sendMouseRelease(const QSharedPointer<MockSurface> &surface);
+ void sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code);
+ void sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code);
+ void sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id);
+ void sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id);
+ void sendTouchUp(const QSharedPointer<MockSurface> &surface, int id);
+ void sendTouchFrame(const QSharedPointer<MockSurface> &surface);
+ void sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface);
+ void sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint &position);
+ void sendDataDeviceMotion(const QPoint &position);
+ void sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface);
+ void sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface);
+ void sendAddOutput();
+ void sendRemoveOutput(const QSharedPointer<MockOutput> &output);
+ void sendOutputGeometry(const QSharedPointer<MockOutput> &output, const QRect &geometry);
+ void sendSurfaceEnter(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output);
+ void sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output);
+ void sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size = QSize(0, 0));
+ void sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size);
+ void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0),
+ const QVector<uint> &states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED });
+ void waitForStartDrag();
+
+ QSharedPointer<MockSurface> surface();
+ QSharedPointer<MockOutput> output(int index = 0);
+ QSharedPointer<MockIviSurface> iviSurface(int index = 0);
+ QSharedPointer<MockXdgToplevelV6> xdgToplevelV6(int index = 0);
+ QSharedPointer<MockSurface> fullScreenShellV1Surface(int index = 0);
+
+ void lock();
+ void unlock();
+
+private:
+ struct Command
+ {
+ typedef void (*Callback)(void *target, const QList<QVariant> &parameters);
+
+ Callback callback;
+ void *target = nullptr;
+ QList<QVariant> parameters;
+ };
+
+ static Command makeCommand(Command::Callback callback, void *target);
+
+ void processCommand(const Command &command);
+ void dispatchCommands();
+
+ static void *run(void *data);
+
+ bool m_alive = true;
+ bool m_ready = false;
+ pthread_t m_thread;
+ QMutex m_mutex;
+ QWaitCondition m_waitCondition;
+
+ Impl::Compositor *m_compositor = nullptr;
+
+ QList<Command> m_commandQueue;
+};
+
+#endif
diff --git a/tests/auto/client/shared/mockfullscreenshellv1.cpp b/tests/auto/client/shared_old/mockfullscreenshellv1.cpp
index 22c49cde6..22c49cde6 100644
--- a/tests/auto/client/shared/mockfullscreenshellv1.cpp
+++ b/tests/auto/client/shared_old/mockfullscreenshellv1.cpp
diff --git a/tests/auto/client/shared/mockfullscreenshellv1.h b/tests/auto/client/shared_old/mockfullscreenshellv1.h
index 819bbc186..819bbc186 100644
--- a/tests/auto/client/shared/mockfullscreenshellv1.h
+++ b/tests/auto/client/shared_old/mockfullscreenshellv1.h
diff --git a/tests/auto/client/shared/mockinput.cpp b/tests/auto/client/shared_old/mockinput.cpp
index 8b7592824..8b7592824 100644
--- a/tests/auto/client/shared/mockinput.cpp
+++ b/tests/auto/client/shared_old/mockinput.cpp
diff --git a/tests/auto/client/shared/mockinput.h b/tests/auto/client/shared_old/mockinput.h
index d9adb3621..d9adb3621 100644
--- a/tests/auto/client/shared/mockinput.h
+++ b/tests/auto/client/shared_old/mockinput.h
diff --git a/tests/auto/client/shared/mockiviapplication.cpp b/tests/auto/client/shared_old/mockiviapplication.cpp
index 29a308993..29a308993 100644
--- a/tests/auto/client/shared/mockiviapplication.cpp
+++ b/tests/auto/client/shared_old/mockiviapplication.cpp
diff --git a/tests/auto/client/shared/mockiviapplication.h b/tests/auto/client/shared_old/mockiviapplication.h
index 4d65eeaba..4d65eeaba 100644
--- a/tests/auto/client/shared/mockiviapplication.h
+++ b/tests/auto/client/shared_old/mockiviapplication.h
diff --git a/tests/auto/client/shared/mockoutput.cpp b/tests/auto/client/shared_old/mockoutput.cpp
index 13e0524ad..13e0524ad 100644
--- a/tests/auto/client/shared/mockoutput.cpp
+++ b/tests/auto/client/shared_old/mockoutput.cpp
diff --git a/tests/auto/client/shared/mockoutput.h b/tests/auto/client/shared_old/mockoutput.h
index 9f261d5d7..9f261d5d7 100644
--- a/tests/auto/client/shared/mockoutput.h
+++ b/tests/auto/client/shared_old/mockoutput.h
diff --git a/tests/auto/client/shared/mocksurface.cpp b/tests/auto/client/shared_old/mocksurface.cpp
index 84dcda6b0..84dcda6b0 100644
--- a/tests/auto/client/shared/mocksurface.cpp
+++ b/tests/auto/client/shared_old/mocksurface.cpp
diff --git a/tests/auto/client/shared/mocksurface.h b/tests/auto/client/shared_old/mocksurface.h
index 949dc23dd..949dc23dd 100644
--- a/tests/auto/client/shared/mocksurface.h
+++ b/tests/auto/client/shared_old/mocksurface.h
diff --git a/tests/auto/client/shared/mockwlshell.cpp b/tests/auto/client/shared_old/mockwlshell.cpp
index 50e539932..50e539932 100644
--- a/tests/auto/client/shared/mockwlshell.cpp
+++ b/tests/auto/client/shared_old/mockwlshell.cpp
diff --git a/tests/auto/client/shared/mockwlshell.h b/tests/auto/client/shared_old/mockwlshell.h
index 3da586ca8..3da586ca8 100644
--- a/tests/auto/client/shared/mockwlshell.h
+++ b/tests/auto/client/shared_old/mockwlshell.h
diff --git a/tests/auto/client/shared/mockxdgshellv6.cpp b/tests/auto/client/shared_old/mockxdgshellv6.cpp
index 05eff74ad..05eff74ad 100644
--- a/tests/auto/client/shared/mockxdgshellv6.cpp
+++ b/tests/auto/client/shared_old/mockxdgshellv6.cpp
diff --git a/tests/auto/client/shared/mockxdgshellv6.h b/tests/auto/client/shared_old/mockxdgshellv6.h
index a238fa562..a238fa562 100644
--- a/tests/auto/client/shared/mockxdgshellv6.h
+++ b/tests/auto/client/shared_old/mockxdgshellv6.h
diff --git a/tests/auto/client/shared_old/shared_old.pri b/tests/auto/client/shared_old/shared_old.pri
new file mode 100644
index 000000000..467e98115
--- /dev/null
+++ b/tests/auto/client/shared_old/shared_old.pri
@@ -0,0 +1,34 @@
+CONFIG += testcase link_pkgconfig
+QT += testlib
+QT += core-private gui-private waylandclient-private
+
+QMAKE_USE += wayland-client wayland-server
+
+CONFIG += wayland-scanner
+WAYLANDSERVERSOURCES += \
+ ../../../../src/3rdparty/protocol/ivi-application.xml \
+ ../../../../src/3rdparty/protocol/wayland.xml \
+ ../../../../src/3rdparty/protocol/xdg-shell-unstable-v6.xml \
+ ../../../../src/3rdparty/protocol/fullscreen-shell-unstable-v1.xml
+
+INCLUDEPATH += ../shared_old
+
+SOURCES += \
+ ../shared_old/mockcompositor.cpp \
+ ../shared_old/mockfullscreenshellv1.cpp \
+ ../shared_old/mockinput.cpp \
+ ../shared_old/mockiviapplication.cpp \
+ ../shared_old/mockwlshell.cpp \
+ ../shared_old/mockxdgshellv6.cpp \
+ ../shared_old/mocksurface.cpp \
+ ../shared_old/mockoutput.cpp
+
+HEADERS += \
+ ../shared_old/mockcompositor.h \
+ ../shared_old/mockfullscreenshellv1.h \
+ ../shared_old/mockinput.h \
+ ../shared_old/mockiviapplication.h \
+ ../shared_old/mockwlshell.h \
+ ../shared_old/mockxdgshellv6.h \
+ ../shared_old/mocksurface.h \
+ ../shared_old/mockoutput.h
diff --git a/tests/auto/client/surface/surface.pro b/tests/auto/client/surface/surface.pro
new file mode 100644
index 000000000..36882aa2d
--- /dev/null
+++ b/tests/auto/client/surface/surface.pro
@@ -0,0 +1,5 @@
+include (../shared/shared.pri)
+
+TARGET = tst_surface
+SOURCES += tst_surface.cpp
+
diff --git a/tests/auto/client/surface/tst_surface.cpp b/tests/auto/client/surface/tst_surface.cpp
new file mode 100644
index 000000000..dddff0866
--- /dev/null
+++ b/tests/auto/client/surface/tst_surface.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+#include <QtGui/QRasterWindow>
+#include <QtGui/QOpenGLWindow>
+
+using namespace MockCompositor;
+
+class tst_surface : public QObject, private DefaultCompositor
+{
+ Q_OBJECT
+private slots:
+ void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
+ void createDestroySurface();
+ void waitForFrameCallbackRaster();
+ void waitForFrameCallbackGl();
+ void negotiateShmFormat();
+};
+
+void tst_surface::createDestroySurface()
+{
+ QWindow window;
+ window.show();
+
+ QCOMPOSITOR_TRY_VERIFY(surface());
+
+ window.destroy();
+ QCOMPOSITOR_TRY_VERIFY(!surface());
+}
+
+void tst_surface::waitForFrameCallbackRaster()
+{
+ QSKIP("TODO: This currently fails, needs a fix");
+ class TestWindow : public QRasterWindow {
+ public:
+ explicit TestWindow() { resize(40, 40); }
+ void paintEvent(QPaintEvent *event) override
+ {
+ Q_UNUSED(event);
+ update();
+ }
+ };
+ TestWindow window;
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ QSignalSpy bufferSpy(exec([=] { return xdgSurface()->m_surface; }), &Surface::bufferCommitted);
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+
+ // We should get the first buffer without waiting for a frame callback
+ QTRY_COMPARE(bufferSpy.count(), 1);
+ bufferSpy.removeFirst();
+
+ // Make sure we follow frame callbacks for some frames
+ for (int i = 0; i < 5; ++i) {
+ xdgPingAndWaitForPong(); // Make sure things have happened on the client
+ exec([&] {
+ QVERIFY(bufferSpy.empty()); // Make sure no extra buffers have arrived
+ QVERIFY(!xdgToplevel()->surface()->m_waitingFrameCallbacks.empty());
+ xdgToplevel()->surface()->sendFrameCallbacks();
+ });
+ QTRY_COMPARE(bufferSpy.count(), 1);
+ bufferSpy.removeFirst();
+ }
+}
+
+void tst_surface::waitForFrameCallbackGl()
+{
+ QSKIP("TODO: This currently fails, needs a fix");
+ class TestWindow : public QOpenGLWindow {
+ public:
+ explicit TestWindow()
+ {
+ resize(40, 40);
+ connect(this, &QOpenGLWindow::frameSwapped,
+ this, QOverload<>::of(&QPaintDeviceWindow::update));
+ update();
+ }
+ void paintGL() override
+ {
+ glClearColor(1, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ };
+ TestWindow window;
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ QSignalSpy bufferSpy(exec([=] { return xdgSurface()->m_surface; }), &Surface::bufferCommitted);
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+
+ // We should get the first buffer without waiting for a frame callback
+ QTRY_COMPARE(bufferSpy.count(), 1);
+ bufferSpy.removeFirst();
+
+ // Make sure we follow frame callbacks for some frames
+ for (int i = 0; i < 5; ++i) {
+ xdgPingAndWaitForPong(); // Make sure things have happened on the client
+ exec([&] {
+ QVERIFY(bufferSpy.empty()); // Make sure no extra buffers have arrived
+ QVERIFY(!xdgToplevel()->surface()->m_waitingFrameCallbacks.empty());
+ xdgToplevel()->surface()->sendFrameCallbacks();
+ });
+ QTRY_COMPARE(bufferSpy.count(), 1);
+ bufferSpy.removeFirst();
+ }
+}
+
+void tst_surface::negotiateShmFormat()
+{
+ QSKIP("TODO: I'm not sure why we're choosing xrgb over argb in this case...");
+ QRasterWindow window;
+ window.setFlag(Qt::FramelessWindowHint); // decorations force alpha
+ QSurfaceFormat format;
+ format.setAlphaBufferSize(0);
+ window.setFormat(format);
+ window.resize(64, 48);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ QSignalSpy bufferSpy(exec([=] { return xdgSurface()->m_surface; }), &Surface::bufferCommitted);
+ const uint serial = exec([=] { return xdgToplevel()->sendCompleteConfigure(); });
+ QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, serial);
+ exec([&] {
+ Buffer *buffer = xdgToplevel()->surface()->m_committed.buffer;
+ QVERIFY(buffer);
+ auto *shmBuffer = ShmBuffer::fromBuffer(buffer);
+ QVERIFY(shmBuffer);
+ qDebug() << "shmBuffer->m_format" << shmBuffer->m_format;
+ QCOMPARE(shmBuffer->m_format, Shm::format_xrgb8888);
+ });
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_surface)
+#include "tst_surface.moc"
diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp
new file mode 100644
index 000000000..55e994b06
--- /dev/null
+++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp
@@ -0,0 +1,269 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+#include <QtGui/QRasterWindow>
+#include <QtGui/QOpenGLWindow>
+
+using namespace MockCompositor;
+
+class tst_xdgshell : public QObject, private DefaultCompositor
+{
+ Q_OBJECT
+private slots:
+ void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
+ void showMinimized();
+ void basicConfigure();
+ void configureSize();
+ void configureStates();
+ void popup();
+ void pongs();
+};
+
+void tst_xdgshell::showMinimized()
+{
+ QSKIP("TODO: This currently fails, needs a fix");
+ // On xdg-shell there's really no way for the compositor to tell the window if it's minimized
+ // There are wl_surface.enter events and so on, but there's really no way to differentiate
+ // between a window preview and an unminimized window.
+ QWindow window;
+ window.showMinimized();
+ QCOMPARE(window.windowStates(), Qt::WindowMinimized); // should return minimized until
+ QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); // rejected by handleWindowStateChanged
+
+ // Make sure the window on the compositor side is/was created here, and not after the test
+ // finishes, as that may mess up for later tests.
+ QCOMPOSITOR_TRY_VERIFY(surface());
+ QVERIFY(!window.isExposed());
+}
+
+void tst_xdgshell::basicConfigure()
+{
+ QRasterWindow window;
+ window.resize(64, 48);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+
+ QSignalSpy configureSpy(exec([=] { return xdgSurface(); }), &XdgSurface::configureCommitted);
+
+ QTRY_VERIFY(window.isVisible());
+ // The window should not be exposed before the first xdg_surface configure event
+ QTRY_VERIFY(!window.isExposed());
+
+ exec([=] {
+ xdgToplevel()->sendConfigure({0, 0}, {}); // Let the window decide the size
+ });
+
+ // Nothing should happen before the *xdg_surface* configure
+ QTRY_VERIFY(!window.isExposed()); //Window should not be exposed before the first configure event
+ QVERIFY(configureSpy.isEmpty());
+
+ const uint serial = exec([=] { return nextSerial(); });
+
+ exec([=] {
+ xdgSurface()->sendConfigure(serial);
+ });
+
+ // Finally, we're exposed
+ QTRY_VERIFY(window.isExposed());
+
+ // The client is now going to ack the configure
+ QTRY_COMPARE(configureSpy.count(), 1);
+ QCOMPARE(configureSpy.takeFirst().at(0).toUInt(), serial);
+
+ // And attach a buffer
+ exec([&] {
+ Buffer *buffer = xdgToplevel()->surface()->m_committed.buffer;
+ QVERIFY(buffer);
+ QCOMPARE(buffer->size(), window.frameGeometry().size());
+ });
+}
+
+void tst_xdgshell::configureSize()
+{
+ QRasterWindow window;
+ window.resize(64, 48);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+
+ QSignalSpy configureSpy(exec([=] { return xdgSurface(); }), &XdgSurface::configureCommitted);
+
+ const QSize configureSize(60, 40);
+
+ exec([=] {
+ xdgToplevel()->sendCompleteConfigure(configureSize);
+ });
+
+ QTRY_COMPARE(configureSpy.count(), 1);
+
+ exec([=] {
+ Buffer *buffer = xdgToplevel()->surface()->m_committed.buffer;
+ QVERIFY(buffer);
+ QCOMPARE(buffer->size(), configureSize);
+ });
+}
+
+void tst_xdgshell::configureStates()
+{
+ QRasterWindow window;
+ window.resize(64, 48);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+
+ const QSize windowedSize(320, 240);
+ const uint windowedSerial = exec([=] {
+ return xdgToplevel()->sendCompleteConfigure(windowedSize, { XdgToplevel::state_activated });
+ });
+ QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, windowedSerial);
+ QCOMPARE(window.visibility(), QWindow::Windowed);
+ QCOMPARE(window.windowStates(), Qt::WindowNoState);
+ QCOMPARE(window.frameGeometry().size(), windowedSize);
+ // Toplevel windows don't know their position on xdg-shell
+// QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled
+
+// QEXPECT_FAIL("", "configure has already been acked, we shouldn't have to wait for isActive", Continue);
+// QVERIFY(window.isActive());
+ QTRY_VERIFY(window.isActive()); // Just make sure it eventually get's set correctly
+
+ const QSize screenSize(640, 480);
+ const uint maximizedSerial = exec([=] {
+ return xdgToplevel()->sendCompleteConfigure(screenSize, { XdgToplevel::state_activated, XdgToplevel::state_maximized });
+ });
+ QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, maximizedSerial);
+ QCOMPARE(window.visibility(), QWindow::Maximized);
+ QCOMPARE(window.windowStates(), Qt::WindowMaximized);
+ QCOMPARE(window.frameGeometry().size(), screenSize);
+// QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled
+
+ const uint fullscreenSerial = exec([=] {
+ return xdgToplevel()->sendCompleteConfigure(screenSize, { XdgToplevel::state_activated, XdgToplevel::state_fullscreen });
+ });
+ QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, fullscreenSerial);
+ QCOMPARE(window.visibility(), QWindow::FullScreen);
+ QCOMPARE(window.windowStates(), Qt::WindowFullScreen);
+ QCOMPARE(window.frameGeometry().size(), screenSize);
+// QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled
+
+ // The window should remember its original size
+ const uint restoreSerial = exec([=] {
+ return xdgToplevel()->sendCompleteConfigure({0, 0}, { XdgToplevel::state_activated });
+ });
+ QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, restoreSerial);
+ QCOMPARE(window.visibility(), QWindow::Windowed);
+ QCOMPARE(window.windowStates(), Qt::WindowNoState);
+ QCOMPARE(window.frameGeometry().size(), windowedSize);
+// QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled
+}
+
+void tst_xdgshell::popup()
+{
+ class Window : public QRasterWindow {
+ public:
+ void mousePressEvent(QMouseEvent *event) override
+ {
+ QRasterWindow::mousePressEvent(event);
+ m_popup.reset(new QRasterWindow);
+ m_popup->setTransientParent(this);
+ m_popup->setFlags(Qt::Popup);
+ m_popup->resize(100, 100);
+ m_popup->show();
+ }
+ QScopedPointer<QRasterWindow> m_popup;
+ };
+ Window window;
+ window.resize(200, 200);
+ window.show();
+
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ QSignalSpy toplevelConfigureSpy(exec([=] { return xdgSurface(); }), &XdgSurface::configureCommitted);
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+ QTRY_COMPARE(toplevelConfigureSpy.count(), 1);
+
+ uint clickSerial = exec([=] {
+ auto *surface = xdgToplevel()->surface();
+ auto *p = pointer();
+ p->sendEnter(surface, {100, 100});
+// p->sendFrame(); //TODO: uncomment when we support seat v5
+ uint serial = p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_released);
+ return serial;
+// p->sendFrame(); //TODO: uncomment when we support seat v5
+ });
+
+ QTRY_VERIFY(window.m_popup);
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup());
+ QSignalSpy popupConfigureSpy(exec([=] { return xdgPopup()->m_xdgSurface; }), &XdgSurface::configureCommitted);
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_grabbed);
+ QCOMPOSITOR_TRY_COMPARE(xdgPopup()->m_grabSerial, clickSerial);
+
+ QRasterWindow *popup = window.m_popup.get();
+ QVERIFY(!popup->isExposed()); // wait for configure
+
+ //TODO: Verify it works with a different configure window geometry
+ exec([=] { xdgPopup()->sendConfigure(QRect(100, 100, 100, 100)); });
+
+ // Nothing should happen before the *xdg_surface* configure
+ QTRY_VERIFY(!popup->isExposed()); // Popup shouldn't be exposed before the first configure event
+ QVERIFY(popupConfigureSpy.isEmpty());
+
+ const uint configureSerial = exec([=] {
+ return xdgPopup()->m_xdgSurface->sendConfigure();
+ });
+
+ // Finally, we're exposed
+ QTRY_VERIFY(popup->isExposed());
+
+ // The client is now going to ack the configure
+ QTRY_COMPARE(popupConfigureSpy.count(), 1);
+ QCOMPARE(popupConfigureSpy.takeFirst().at(0).toUInt(), configureSerial);
+
+ // And attach a buffer
+ exec([&] {
+ Buffer *buffer = xdgPopup()->surface()->m_committed.buffer;
+ QVERIFY(buffer);
+ QCOMPARE(buffer->size(), popup->frameGeometry().size());
+ });
+}
+
+void tst_xdgshell::pongs()
+{
+ QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong);
+ // Verify that the client has bound to the global
+ QCOMPOSITOR_TRY_COMPARE(get<XdgWmBase>()->resourceMap().size(), 1);
+ const uint serial = exec([=] { return nextSerial(); });
+ exec([=] {
+ auto *base = get<XdgWmBase>();
+ wl_resource *resource = base->resourceMap().first()->handle;
+ base->send_ping(resource, serial);
+ });
+ QTRY_COMPARE(pongSpy.count(), 1);
+ QCOMPARE(pongSpy.first().at(0).toUInt(), serial);
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_xdgshell)
+#include "tst_xdgshell.moc"
diff --git a/tests/auto/client/xdgshell/xdgshell.pro b/tests/auto/client/xdgshell/xdgshell.pro
new file mode 100644
index 000000000..d7c3f9df6
--- /dev/null
+++ b/tests/auto/client/xdgshell/xdgshell.pro
@@ -0,0 +1,5 @@
+include (../shared/shared.pri)
+
+TARGET = tst_xdgshell
+SOURCES += tst_xdgshell.cpp
+
diff --git a/tests/auto/client/xdgshellv6/xdgshellv6.pro b/tests/auto/client/xdgshellv6/xdgshellv6.pro
index 4fec593df..cc8a22d83 100644
--- a/tests/auto/client/xdgshellv6/xdgshellv6.pro
+++ b/tests/auto/client/xdgshellv6/xdgshellv6.pro
@@ -1,4 +1,4 @@
-include (../shared/shared.pri)
+include (../shared_old/shared_old.pri)
TARGET = tst_client_xdgshellv6
SOURCES += tst_xdgshellv6.cpp