summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/client/client.pro2
-rw-r--r--tests/auto/client/datadevicev1/tst_datadevicev1.cpp2
-rw-r--r--tests/auto/client/nooutput/nooutput.pro5
-rw-r--r--tests/auto/client/nooutput/tst_nooutput.cpp68
-rw-r--r--tests/auto/client/output/tst_output.cpp43
-rw-r--r--tests/auto/client/primaryselectionv1/tst_primaryselectionv1.cpp2
-rw-r--r--tests/auto/client/seatv4/tst_seatv4.cpp6
-rw-r--r--tests/auto/client/seatv5/tst_seatv5.cpp49
-rw-r--r--tests/auto/client/shared/coreprotocol.h25
-rw-r--r--tests/auto/client/shared/mockcompositor.cpp13
-rw-r--r--tests/auto/client/shared/mockcompositor.h1
-rw-r--r--tests/auto/client/shared/shared.pri2
-rw-r--r--tests/auto/client/shared/xdgshell.cpp2
-rw-r--r--tests/auto/client/shared_old/mockcompositor.cpp2
-rw-r--r--tests/auto/client/shared_old/shared_old.pri2
-rw-r--r--tests/auto/client/surface/tst_surface.cpp43
-rw-r--r--tests/auto/client/tabletv2/tabletv2.pro7
-rw-r--r--tests/auto/client/tabletv2/tst_tabletv2.cpp918
-rw-r--r--tests/auto/client/xdgdecorationv1/tst_xdgdecorationv1.cpp2
-rw-r--r--tests/auto/client/xdgoutput/tst_xdgoutput.cpp2
-rw-r--r--tests/auto/client/xdgshell/tst_xdgshell.cpp49
-rw-r--r--tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp2
-rw-r--r--tests/auto/compositor/compositor/tst_compositor.cpp8
23 files changed, 1215 insertions, 40 deletions
diff --git a/tests/auto/client/client.pro b/tests/auto/client/client.pro
index 4b1eb2458..46a3aa42e 100644
--- a/tests/auto/client/client.pro
+++ b/tests/auto/client/client.pro
@@ -5,11 +5,13 @@ SUBDIRS += \
datadevicev1 \
fullscreenshellv1 \
iviapplication \
+ nooutput \
output \
primaryselectionv1 \
seatv4 \
seatv5 \
surface \
+ tabletv2 \
wl_connect \
xdgdecorationv1 \
xdgoutput \
diff --git a/tests/auto/client/datadevicev1/tst_datadevicev1.cpp b/tests/auto/client/datadevicev1/tst_datadevicev1.cpp
index 1568b3b96..91a479e28 100644
--- a/tests/auto/client/datadevicev1/tst_datadevicev1.cpp
+++ b/tests/auto/client/datadevicev1/tst_datadevicev1.cpp
@@ -28,8 +28,8 @@
#include "mockcompositor.h"
+#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
-#include <QtGui/QOpenGLWindow>
#include <QtGui/QClipboard>
#include <QtGui/QDrag>
diff --git a/tests/auto/client/nooutput/nooutput.pro b/tests/auto/client/nooutput/nooutput.pro
new file mode 100644
index 000000000..1d8dc5626
--- /dev/null
+++ b/tests/auto/client/nooutput/nooutput.pro
@@ -0,0 +1,5 @@
+include (../shared/shared.pri)
+
+TARGET = tst_nooutput
+SOURCES += tst_nooutput.cpp
+
diff --git a/tests/auto/client/nooutput/tst_nooutput.cpp b/tests/auto/client/nooutput/tst_nooutput.cpp
new file mode 100644
index 000000000..098d88d99
--- /dev/null
+++ b/tests/auto/client/nooutput/tst_nooutput.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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/QScreen>
+#include <QtGui/QRasterWindow>
+
+using namespace MockCompositor;
+
+class NoOutputCompositor : public DefaultCompositor {
+public:
+ NoOutputCompositor()
+ {
+ exec([this] { removeAll<Output>(); });
+ m_config.autoConfigure = true;
+ }
+};
+
+class tst_nooutput : public QObject, private NoOutputCompositor
+{
+ Q_OBJECT
+private slots:
+ void cleanup()
+ {
+ // There should be no wl_outputs in this test
+ QCOMPOSITOR_COMPARE(getAll<Output>().size(), 0);
+ QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage()));
+ }
+ void noScreens();
+};
+
+void tst_nooutput::noScreens()
+{
+ QRasterWindow window;
+ window.resize(16, 16);
+ window.show();
+
+ // We have to handle showing a window when there are no real outputs
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_nooutput)
+#include "tst_nooutput.moc"
diff --git a/tests/auto/client/output/tst_output.cpp b/tests/auto/client/output/tst_output.cpp
index 29c773cf6..e9944f815 100644
--- a/tests/auto/client/output/tst_output.cpp
+++ b/tests/auto/client/output/tst_output.cpp
@@ -53,6 +53,7 @@ private slots:
void windowScreens();
void removePrimaryScreen();
void screenOrder();
+ void removeAllScreens();
};
void tst_output::primaryScreen()
@@ -227,5 +228,47 @@ void tst_output::screenOrder()
});
}
+// This is different from tst_nooutput::noScreens because here we have a screen at platform
+// integration initialization, which we then remove.
+void tst_output::removeAllScreens()
+{
+ QRasterWindow window1;
+ window1.resize(400, 320);
+ window1.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface(0) && xdgSurface(0)->m_committedConfigureSerial);
+
+ const QString wlOutputPrimaryScreenModel = QGuiApplication::primaryScreen()->model();
+
+ // Get screen info so we can restore it after
+ auto screenInfo = exec([=] { return output()->m_data; });
+ exec([=] { remove(output()); });
+
+ // Make sure the wl_output is actually removed before we continue
+ QTRY_VERIFY(!QGuiApplication::primaryScreen() || QGuiApplication::primaryScreen()->model() != wlOutputPrimaryScreenModel);
+
+ // Adding a window while there are no screens should also work
+ QRasterWindow window2;
+ window2.resize(400, 320);
+ window2.show();
+
+ exec([=] { add<Output>(screenInfo); });
+
+ // Things should be back to normal
+ QTRY_VERIFY(QGuiApplication::primaryScreen());
+ QTRY_COMPARE(QGuiApplication::primaryScreen()->model(), wlOutputPrimaryScreenModel);
+
+ // Test that we don't leave any fake screens around after we get a wl_output back.
+ QTRY_COMPARE(QGuiApplication::screens().size(), 1);
+
+ // Qt may choose to recreate/hide windows in response to changing screens, so give the client
+ // some time to potentially mess up before we verify that the windows are visible.
+ xdgPingAndWaitForPong();
+
+ // Windows should be visible after we've reconnected the screen
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel(0) && xdgToplevel(0)->m_xdgSurface->m_committedConfigureSerial);
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel(1) && xdgToplevel(1)->m_xdgSurface->m_committedConfigureSerial);
+
+}
+
QCOMPOSITOR_TEST_MAIN(tst_output)
#include "tst_output.moc"
diff --git a/tests/auto/client/primaryselectionv1/tst_primaryselectionv1.cpp b/tests/auto/client/primaryselectionv1/tst_primaryselectionv1.cpp
index ee9fa110e..660d1f83e 100644
--- a/tests/auto/client/primaryselectionv1/tst_primaryselectionv1.cpp
+++ b/tests/auto/client/primaryselectionv1/tst_primaryselectionv1.cpp
@@ -30,8 +30,8 @@
#include <qwayland-server-wp-primary-selection-unstable-v1.h>
+#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
-#include <QtGui/QOpenGLWindow>
#include <QtGui/QClipboard>
#include <QtCore/private/qcore_unix_p.h>
diff --git a/tests/auto/client/seatv4/tst_seatv4.cpp b/tests/auto/client/seatv4/tst_seatv4.cpp
index 2e17bef87..46d91e028 100644
--- a/tests/auto/client/seatv4/tst_seatv4.cpp
+++ b/tests/auto/client/seatv4/tst_seatv4.cpp
@@ -28,8 +28,8 @@
#include "mockcompositor.h"
+#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
-#include <QtGui/QOpenGLWindow>
#if QT_CONFIG(cursor)
#include <wayland-cursor.h>
#include <QtGui/private/qguiapplication_p.h>
@@ -289,8 +289,8 @@ void tst_seatv4::simpleAxis()
{
Event() = default;
- const QPoint pixelDelta;
- const QPoint angleDelta; // eights of a degree, positive is upwards, left
+ QPoint pixelDelta;
+ QPoint angleDelta; // eights of a degree, positive is upwards, left
};
QVector<Event> m_events;
};
diff --git a/tests/auto/client/seatv5/tst_seatv5.cpp b/tests/auto/client/seatv5/tst_seatv5.cpp
index 636f26081..081a0aa70 100644
--- a/tests/auto/client/seatv5/tst_seatv5.cpp
+++ b/tests/auto/client/seatv5/tst_seatv5.cpp
@@ -27,8 +27,8 @@
****************************************************************************/
#include "mockcompositor.h"
+#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
-#include <QtGui/QOpenGLWindow>
using namespace MockCompositor;
@@ -71,6 +71,7 @@ private slots:
void singleTapFloat();
void multiTouch();
void multiTouchUpAndMotionFrame();
+ void tapAndMoveInSameFrame();
};
void tst_seatv5::bindsToSeat()
@@ -155,10 +156,10 @@ public:
, source(event->source())
{
}
- const Qt::ScrollPhase phase{};
- const QPoint pixelDelta;
- const QPoint angleDelta; // eights of a degree, positive is upwards, left
- const Qt::MouseEventSource source{};
+ Qt::ScrollPhase phase{};
+ QPoint pixelDelta;
+ QPoint angleDelta; // eights of a degree, positive is upwards, left
+ Qt::MouseEventSource source{};
};
QVector<Event> m_events;
};
@@ -396,9 +397,9 @@ public:
, touchPoints(event->touchPoints())
{
}
- const QEvent::Type type{};
- const Qt::TouchPointStates touchPointStates{};
- const QList<QTouchEvent::TouchPoint> touchPoints;
+ QEvent::Type type{};
+ Qt::TouchPointStates touchPointStates{};
+ QList<QTouchEvent::TouchPoint> touchPoints;
};
QVector<Event> m_events;
};
@@ -586,5 +587,37 @@ void tst_seatv5::multiTouchUpAndMotionFrame()
QVERIFY(window.m_events.empty());
}
+void tst_seatv5::tapAndMoveInSameFrame()
+{
+ TouchWindow window;
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] {
+ auto *t = touch();
+ auto *c = client();
+
+ t->sendDown(xdgToplevel()->surface(), {32, 32}, 0);
+ t->sendMotion(c, {33, 33}, 0);
+ t->sendFrame(c);
+
+ // Don't leave touch in a weird state
+ t->sendUp(c, 0);
+ t->sendFrame(c);
+ });
+
+ QTRY_VERIFY(!window.m_events.empty());
+ {
+ auto e = window.m_events.takeFirst();
+ QCOMPARE(e.type, QEvent::TouchBegin);
+ QCOMPARE(e.touchPoints.size(), 1);
+ QCOMPARE(e.touchPoints[0].state(), Qt::TouchPointState::TouchPointPressed);
+ // Position isn't that important, we just want to make sure we actually get the pressed event
+ }
+
+ // Make sure we eventually release
+ QTRY_VERIFY(!window.m_events.empty());
+ QTRY_COMPARE(window.m_events.last().touchPoints.first().state(), Qt::TouchPointState::TouchPointReleased);
+}
+
QCOMPOSITOR_TEST_MAIN(tst_seatv5)
#include "tst_seatv5.moc"
diff --git a/tests/auto/client/shared/coreprotocol.h b/tests/auto/client/shared/coreprotocol.h
index fe8202ad1..a12d22d36 100644
--- a/tests/auto/client/shared/coreprotocol.h
+++ b/tests/auto/client/shared/coreprotocol.h
@@ -164,6 +164,16 @@ protected:
}
};
+class Subsurface : public QObject, public QtWaylandServer::wl_subsurface
+{
+ Q_OBJECT
+public:
+ explicit Subsurface(wl_client *client, int id, int version)
+ : QtWaylandServer::wl_subsurface(client, id, version)
+ {
+ }
+};
+
class SubCompositor : public Global, public QtWaylandServer::wl_subcompositor
{
Q_OBJECT
@@ -171,7 +181,20 @@ public:
explicit SubCompositor(CoreCompositor *compositor, int version = 1)
: QtWaylandServer::wl_subcompositor(compositor->m_display, version)
{}
- // TODO
+ QVector<Subsurface *> m_subsurfaces;
+
+signals:
+ void subsurfaceCreated(Subsurface *subsurface);
+
+protected:
+ void subcompositor_get_subsurface(Resource *resource, uint32_t id, ::wl_resource *surface, ::wl_resource *parent) override
+ {
+ QTRY_VERIFY(parent);
+ QTRY_VERIFY(surface);
+ auto *subsurface = new Subsurface(resource->client(), id, resource->version());
+ m_subsurfaces.append(subsurface); // TODO: clean up?
+ emit subsurfaceCreated(subsurface);
+ }
};
struct OutputMode {
diff --git a/tests/auto/client/shared/mockcompositor.cpp b/tests/auto/client/shared/mockcompositor.cpp
index 5f2d89078..dca9dac49 100644
--- a/tests/auto/client/shared/mockcompositor.cpp
+++ b/tests/auto/client/shared/mockcompositor.cpp
@@ -52,19 +52,16 @@ DefaultCompositor::DefaultCompositor()
// 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())
+ if (m_config.autoEnter && get<Output>() && 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();
- });
- });
+ QObject::connect(get<XdgWmBase>(), &XdgWmBase::toplevelCreated, get<XdgWmBase>(), [&] (XdgToplevel *toplevel) {
+ if (m_config.autoConfigure)
+ toplevel->sendCompleteConfigure();
+ }, Qt::DirectConnection);
}
Q_ASSERT(isClean());
}
diff --git a/tests/auto/client/shared/mockcompositor.h b/tests/auto/client/shared/mockcompositor.h
index c7d85b958..deef1f909 100644
--- a/tests/auto/client/shared/mockcompositor.h
+++ b/tests/auto/client/shared/mockcompositor.h
@@ -56,6 +56,7 @@ public:
// Convenience functions
Output *output(int i = 0) { return getAll<Output>().value(i, nullptr); }
Surface *surface(int i = 0) { return get<WlCompositor>()->m_surfaces.value(i, nullptr); }
+ Subsurface *subSurface(int i = 0) { return get<SubCompositor>()->m_subsurfaces.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); }
diff --git a/tests/auto/client/shared/shared.pri b/tests/auto/client/shared/shared.pri
index c86183b3d..b59cdc481 100644
--- a/tests/auto/client/shared/shared.pri
+++ b/tests/auto/client/shared/shared.pri
@@ -1,4 +1,4 @@
-QT += testlib waylandclient-private
+QT += testlib waylandclient-private opengl
CONFIG += testcase wayland-scanner
QMAKE_USE += wayland-server
diff --git a/tests/auto/client/shared/xdgshell.cpp b/tests/auto/client/shared/xdgshell.cpp
index 13acc01e2..72582f48d 100644
--- a/tests/auto/client/shared/xdgshell.cpp
+++ b/tests/auto/client/shared/xdgshell.cpp
@@ -83,7 +83,7 @@ XdgSurface::XdgSurface(XdgWmBase *xdgWmBase, Surface *surface, wl_client *client
{
QVERIFY(!surface->m_pending.buffer);
QVERIFY(!surface->m_committed.buffer);
- connect(this, &XdgSurface::toplevelCreated, xdgWmBase, &XdgWmBase::toplevelCreated);
+ connect(this, &XdgSurface::toplevelCreated, xdgWmBase, &XdgWmBase::toplevelCreated, Qt::DirectConnection);
connect(surface, &Surface::attach, this, &XdgSurface::verifyConfigured);
connect(surface, &Surface::commit, this, [this] {
m_committed = m_pending;
diff --git a/tests/auto/client/shared_old/mockcompositor.cpp b/tests/auto/client/shared_old/mockcompositor.cpp
index f71a78102..9553076dd 100644
--- a/tests/auto/client/shared_old/mockcompositor.cpp
+++ b/tests/auto/client/shared_old/mockcompositor.cpp
@@ -312,9 +312,9 @@ void *MockCompositor::run(void *data)
Impl::Compositor compositor(controller);
controller->m_compositor = &compositor;
- controller->m_waitCondition.wakeOne();
while (!controller->m_ready) {
+ controller->m_waitCondition.wakeOne();
controller->dispatchCommands();
compositor.dispatchEvents(20);
}
diff --git a/tests/auto/client/shared_old/shared_old.pri b/tests/auto/client/shared_old/shared_old.pri
index 467e98115..6989e613e 100644
--- a/tests/auto/client/shared_old/shared_old.pri
+++ b/tests/auto/client/shared_old/shared_old.pri
@@ -1,6 +1,6 @@
CONFIG += testcase link_pkgconfig
QT += testlib
-QT += core-private gui-private waylandclient-private
+QT += core-private gui-private waylandclient-private opengl
QMAKE_USE += wayland-client wayland-server
diff --git a/tests/auto/client/surface/tst_surface.cpp b/tests/auto/client/surface/tst_surface.cpp
index 9659235a0..289fbd621 100644
--- a/tests/auto/client/surface/tst_surface.cpp
+++ b/tests/auto/client/surface/tst_surface.cpp
@@ -29,7 +29,7 @@
#include "mockcompositor.h"
#include <QtGui/QRasterWindow>
#if QT_CONFIG(opengl)
-#include <QtGui/QOpenGLWindow>
+#include <QtOpenGL/QOpenGLWindow>
#endif
using namespace MockCompositor;
@@ -45,6 +45,10 @@ private slots:
void waitForFrameCallbackGl();
#endif
void negotiateShmFormat();
+
+ // Subsurfaces
+ void createSubsurface();
+ void createSubsurfaceForHiddenParent();
};
void tst_surface::createDestroySurface()
@@ -160,5 +164,42 @@ void tst_surface::negotiateShmFormat()
});
}
+void tst_surface::createSubsurface()
+{
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface()->m_committedConfigureSerial);
+
+ QRasterWindow subWindow;
+ subWindow.setParent(&window);
+ subWindow.resize(64, 64);
+ subWindow.show();
+ QCOMPOSITOR_TRY_VERIFY(subSurface());
+}
+
+// Used to cause a crash in libwayland (QTBUG-79674)
+void tst_surface::createSubsurfaceForHiddenParent()
+{
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface()->m_committedConfigureSerial);
+
+ window.hide();
+
+ QRasterWindow subWindow;
+ subWindow.setParent(&window);
+ subWindow.resize(64, 64);
+ subWindow.show();
+
+ // Make sure the client doesn't quit before it has a chance to crash
+ xdgPingAndWaitForPong();
+}
+
QCOMPOSITOR_TEST_MAIN(tst_surface)
#include "tst_surface.moc"
diff --git a/tests/auto/client/tabletv2/tabletv2.pro b/tests/auto/client/tabletv2/tabletv2.pro
new file mode 100644
index 000000000..9dc9636e9
--- /dev/null
+++ b/tests/auto/client/tabletv2/tabletv2.pro
@@ -0,0 +1,7 @@
+include (../shared/shared.pri)
+
+WAYLANDSERVERSOURCES += \
+ $$PWD/../../../../src/3rdparty/protocol/tablet-unstable-v2.xml
+
+TARGET = tst_tabletv2
+SOURCES += tst_tabletv2.cpp
diff --git a/tests/auto/client/tabletv2/tst_tabletv2.cpp b/tests/auto/client/tabletv2/tst_tabletv2.cpp
new file mode 100644
index 000000000..03fb2933d
--- /dev/null
+++ b/tests/auto/client/tabletv2/tst_tabletv2.cpp
@@ -0,0 +1,918 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+
+#include <qwayland-server-tablet-unstable-v2.h>
+
+#include <QtGui/QRasterWindow>
+
+using namespace MockCompositor;
+
+constexpr int tabletVersion = 1; // protocol VERSION, not the name suffix (_v2)
+
+class TabletManagerV2;
+class TabletSeatV2;
+
+class TabletV2 : public QObject, public QtWaylandServer::zwp_tablet_v2
+{
+ Q_OBJECT
+public:
+ explicit TabletV2(TabletSeatV2 *tabletSeat)
+ : m_tabletSeat(tabletSeat)
+ {
+ }
+
+ void send_removed() = delete;
+ void send_removed(struct ::wl_resource *resource) = delete;
+ void sendRemoved();
+
+ QPointer<TabletSeatV2> m_tabletSeat; // destroy order is not guaranteed
+protected:
+ void zwp_tablet_v2_destroy(Resource *resource) override;
+};
+
+class TabletToolV2 : public QObject, public QtWaylandServer::zwp_tablet_tool_v2
+{
+ Q_OBJECT
+public:
+ using ToolType = QtWaylandServer::zwp_tablet_tool_v2::type;
+ explicit TabletToolV2(TabletSeatV2 *tabletSeat, ToolType toolType, quint64 hardwareSerial)
+ : m_tabletSeat(tabletSeat)
+ , m_toolType(toolType)
+ , m_hardwareSerial(hardwareSerial)
+ {
+ }
+
+ wl_resource *toolResource() // for convenience
+ {
+ Q_ASSERT(resourceMap().size() == 1);
+ // Strictly speaking, there may be more than one resource for the tool, for intsance if
+ // if there are multiple clients, or a client has called get_tablet_seat multiple times.
+ // For now we'll pretend there can only be one resource.
+ return resourceMap().first()->handle;
+ }
+
+ void send_removed() = delete;
+ void send_removed(struct ::wl_resource *resource) = delete;
+ void sendRemoved();
+
+ uint sendProximityIn(TabletV2 *tablet, Surface *surface);
+ void sendProximityOut();
+ void sendMotion(QPointF position)
+ {
+ Q_ASSERT(m_proximitySurface);
+ for (auto *resource : resourceMap())
+ send_motion(resource->handle, wl_fixed_from_double(position.x()), wl_fixed_from_double(position.y()));
+ }
+ uint sendDown();
+ void sendUp() { send_up(toolResource()); }
+ void sendPressure(uint pressure);
+ void sendTilt(qreal tiltX, qreal tiltY) { send_tilt(toolResource(), wl_fixed_from_double(tiltX), wl_fixed_from_double(tiltY)); }
+ void sendRotation(qreal rotation) { send_rotation(toolResource(), wl_fixed_from_double(rotation)); }
+ uint sendButton(uint button, bool pressed);
+ uint sendFrame();
+
+ QPointer<TabletSeatV2> m_tabletSeat; // destruction order is not guaranteed
+ ToolType m_toolType = ToolType::type_pen;
+ quint64 m_hardwareSerial = 0;
+ QPointer<Surface> m_proximitySurface;
+protected:
+ void zwp_tablet_tool_v2_destroy(Resource *resource) override;
+};
+
+class TabletPadV2 : public QObject, public QtWaylandServer::zwp_tablet_pad_v2
+{
+ Q_OBJECT
+public:
+ explicit TabletPadV2(TabletSeatV2 *tabletSeat)
+ : m_tabletSeat(tabletSeat)
+ {
+ }
+
+ void send_removed() = delete;
+ void send_removed(struct ::wl_resource *resource) = delete;
+ void sendRemoved();
+
+ QPointer<TabletSeatV2> m_tabletSeat; // destroy order is not guaranteed
+protected:
+ void zwp_tablet_pad_v2_destroy(Resource *resource) override;
+};
+
+class TabletSeatV2 : public QObject, public QtWaylandServer::zwp_tablet_seat_v2
+{
+ Q_OBJECT
+public:
+ explicit TabletSeatV2(TabletManagerV2 *manager, Seat *seat)
+ : m_manager(manager)
+ , m_seat(seat)
+ {}
+ TabletV2 *addTablet()
+ {
+ auto *tablet = new TabletV2(this);
+ m_tablets.append(tablet);
+ for (auto *resource : resourceMap())
+ sendTabletAdded(resource, tablet);
+ return tablet;
+ }
+
+ void sendTabletAdded(Resource *resource, TabletV2 *tablet)
+ {
+ // Although, not necessarily correct, assuming just one tablet_seat per client
+ auto *tabletResource = tablet->add(resource->client(), resource->version());
+ zwp_tablet_seat_v2::send_tablet_added(resource->handle, tabletResource->handle);
+ // TODO: send extra stuff before done?
+ tablet->send_done(tabletResource->handle);
+ }
+
+ using ToolType = QtWaylandServer::zwp_tablet_tool_v2::type;
+ TabletToolV2 *addTool(ToolType toolType = ToolType::type_pen, quint64 hardwareSerial = 0)
+ {
+ auto *tool = new TabletToolV2(this, toolType, hardwareSerial);
+ m_tools.append(tool);
+ for (auto *resource : resourceMap())
+ sendToolAdded(resource, tool);
+ return tool;
+ }
+
+ void sendToolAdded(Resource *resource, TabletToolV2 *tool)
+ {
+ // Although, not necessarily correct, assuming just one tablet_seat per client
+ auto *toolResource = tool->add(resource->client(), resource->version())->handle;
+ zwp_tablet_seat_v2::send_tool_added(resource->handle, toolResource);
+ tool->send_type(toolResource, tool->m_toolType);
+ if (tool->m_hardwareSerial) {
+ const uint hi = tool->m_hardwareSerial >> 32;
+ const uint lo = tool->m_hardwareSerial & 0xffffffff;
+ tool->send_hardware_serial(toolResource, hi, lo);
+ }
+ tool->send_done(toolResource);
+ }
+
+ TabletPadV2 *addPad()
+ {
+ auto *pad = new TabletPadV2(this);
+ m_pads.append(pad);
+ for (auto *resource : resourceMap())
+ sendPadAdded(resource, pad);
+ return pad;
+ }
+
+ void sendPadAdded(Resource *resource, TabletPadV2 *pad)
+ {
+ // Although, not necessarily correct, assuming just one tablet_seat per client
+ auto *padResource = pad->add(resource->client(), resource->version())->handle;
+ zwp_tablet_seat_v2::send_pad_added(resource->handle, padResource);
+ pad->send_done(padResource);
+ }
+
+ void removeAll()
+ {
+ const auto tools = m_tools;
+ for (auto *tool : tools)
+ tool->sendRemoved();
+
+ const auto tablets = m_tablets;
+ for (auto *tablet : tablets)
+ tablet->sendRemoved();
+
+ const auto pads = m_pads;
+ for (auto *pad : pads)
+ pad->sendRemoved();
+ }
+
+ TabletManagerV2 *m_manager = nullptr;
+ Seat *m_seat = nullptr;
+ QVector<TabletV2 *> m_tablets;
+ QVector<TabletV2 *> m_tabletsWaitingForDestroy;
+ QVector<TabletToolV2 *> m_tools;
+ QVector<TabletToolV2 *> m_toolsWaitingForDestroy;
+ QVector<TabletPadV2 *> m_pads;
+ QVector<TabletPadV2 *> m_padsWaitingForDestroy;
+
+protected:
+ void zwp_tablet_seat_v2_bind_resource(Resource *resource)
+ {
+ for (auto *tablet : m_tablets)
+ sendTabletAdded(resource, tablet);
+ for (auto *tool : m_tools)
+ sendToolAdded(resource, tool);
+ for (auto *pad : m_pads)
+ sendPadAdded(resource, pad);
+ }
+};
+
+class TabletManagerV2 : public Global, public QtWaylandServer::zwp_tablet_manager_v2
+{
+ Q_OBJECT
+public:
+ explicit TabletManagerV2(CoreCompositor *compositor, int version = 1)
+ : QtWaylandServer::zwp_tablet_manager_v2(compositor->m_display, version)
+ , m_version(version)
+ {}
+ bool isClean() override
+ {
+ for (auto *seat : m_tabletSeats) {
+ if (!seat->m_tabletsWaitingForDestroy.empty())
+ return false;
+ if (!seat->m_toolsWaitingForDestroy.empty())
+ return false;
+ if (!seat->m_padsWaitingForDestroy.empty())
+ return false;
+ }
+ return true;
+ }
+
+ TabletSeatV2 *tabletSeatFor(Seat *seat)
+ {
+ Q_ASSERT(seat);
+ if (auto *tabletSeat = m_tabletSeats.value(seat, nullptr))
+ return tabletSeat;
+
+ auto *tabletSeat = new TabletSeatV2(this, seat);
+ m_tabletSeats[seat] = tabletSeat;
+ return tabletSeat;
+ }
+
+ int m_version = 1; // TODO: Remove on libwayland upgrade
+ QMap<Seat *, TabletSeatV2 *> m_tabletSeats;
+
+protected:
+ void zwp_tablet_manager_v2_destroy(Resource *resource) override
+ {
+ // tablet_seats created from this object are unaffected and should be destroyed separately.
+ wl_resource_destroy(resource->handle);
+ }
+
+ void zwp_tablet_manager_v2_get_tablet_seat(Resource *resource, uint32_t id, ::wl_resource *seatResource) override
+ {
+ auto *seat = fromResource<Seat>(seatResource);
+ QVERIFY(seat);
+ auto *tabletSeat = tabletSeatFor(seat);
+ tabletSeat->add(resource->client(), id, resource->version());
+ }
+};
+
+void TabletV2::sendRemoved()
+{
+ for (auto *resource : resourceMap())
+ zwp_tablet_v2_send_removed(resource->handle);
+ bool removed = m_tabletSeat->m_tablets.removeOne(this);
+ QVERIFY(removed);
+ m_tabletSeat->m_tabletsWaitingForDestroy.append(this);
+}
+
+void TabletV2::zwp_tablet_v2_destroy(QtWaylandServer::zwp_tablet_v2::Resource *resource)
+{
+ Q_UNUSED(resource)
+ if (m_tabletSeat) {
+ bool removed = m_tabletSeat->m_tabletsWaitingForDestroy.removeOne(this);
+ QVERIFY(removed);
+ }
+ wl_resource_destroy(resource->handle);
+}
+
+void TabletToolV2::sendRemoved()
+{
+ for (auto *resource : resourceMap())
+ zwp_tablet_tool_v2_send_removed(resource->handle);
+ bool removed = m_tabletSeat->m_tools.removeOne(this);
+ QVERIFY(removed);
+ m_tabletSeat->m_toolsWaitingForDestroy.append(this);
+}
+
+uint TabletToolV2::sendProximityIn(TabletV2 *tablet, Surface *surface)
+{
+ Q_ASSERT(!m_proximitySurface);
+ m_proximitySurface = surface;
+ uint serial = m_tabletSeat->m_seat->m_compositor->nextSerial();
+ auto *client = surface->resource()->client();
+ auto tabletResource = tablet->resourceMap().value(client)->handle;
+ send_proximity_in(toolResource(), serial, tabletResource, surface->resource()->handle);
+ return serial;
+}
+
+void TabletToolV2::sendProximityOut()
+{
+ Q_ASSERT(m_proximitySurface);
+ send_proximity_out(toolResource());
+ m_proximitySurface = nullptr;
+}
+
+uint TabletToolV2::sendDown()
+{
+ uint serial = m_tabletSeat->m_seat->m_compositor->nextSerial();
+ send_down(toolResource(), serial);
+ return serial;
+}
+
+void TabletToolV2::sendPressure(uint pressure)
+{
+ Q_ASSERT(m_proximitySurface);
+ auto *client = m_proximitySurface->resource()->client();
+ auto toolResource = resourceMap().value(client)->handle;
+ send_pressure(toolResource, pressure);
+}
+
+uint TabletToolV2::sendButton(uint button, bool pressed)
+{
+ button_state state = pressed ? button_state_pressed : button_state_released;
+ uint serial = m_tabletSeat->m_seat->m_compositor->nextSerial();
+ send_button(toolResource(), serial, button, state);
+ return serial;
+}
+
+uint TabletToolV2::sendFrame()
+{
+ uint time = m_tabletSeat->m_seat->m_compositor->currentTimeMilliseconds();
+ for (auto *resource : resourceMap())
+ send_frame(resource->handle, time);
+ return time;
+}
+
+void TabletToolV2::zwp_tablet_tool_v2_destroy(QtWaylandServer::zwp_tablet_tool_v2::Resource *resource)
+{
+ if (m_tabletSeat) {
+ bool removed = m_tabletSeat->m_toolsWaitingForDestroy.removeOne(this);
+ QVERIFY(removed);
+ }
+ wl_resource_destroy(resource->handle);
+}
+
+void TabletPadV2::sendRemoved()
+{
+ for (auto *resource : resourceMap())
+ zwp_tablet_pad_v2_send_removed(resource->handle);
+ bool removed = m_tabletSeat->m_pads.removeOne(this);
+ QVERIFY(removed);
+ m_tabletSeat->m_padsWaitingForDestroy.append(this);
+}
+
+void TabletPadV2::zwp_tablet_pad_v2_destroy(QtWaylandServer::zwp_tablet_pad_v2::Resource *resource)
+{
+ if (m_tabletSeat) {
+ bool removed = m_tabletSeat->m_padsWaitingForDestroy.removeOne(this);
+ QVERIFY(removed);
+ }
+ wl_resource_destroy(resource->handle);
+}
+
+class TabletCompositor : public DefaultCompositor {
+public:
+ explicit TabletCompositor()
+ {
+ exec([this] {
+ m_config.autoConfigure = true;
+ add<TabletManagerV2>(tabletVersion);
+ });
+ }
+ TabletSeatV2 *tabletSeat(int i = 0)
+ {
+ return get<TabletManagerV2>()->tabletSeatFor(get<Seat>(i));
+ }
+ TabletV2 *tablet(int i = 0, int iSeat = 0)
+ {
+ if (auto *ts = tabletSeat(iSeat))
+ return ts->m_tablets.value(i, nullptr);
+ return nullptr;
+ }
+ TabletToolV2 *tabletTool(int i = 0, int iSeat = 0)
+ {
+ if (auto *ts = tabletSeat(iSeat))
+ return ts->m_tools.value(i, nullptr);
+ return nullptr;
+ }
+ TabletPadV2 *tabletPad(int i = 0, int iSeat = 0)
+ {
+ if (auto *ts = tabletSeat(iSeat))
+ return ts->m_pads.value(i, nullptr);
+ return nullptr;
+ }
+};
+
+Q_DECLARE_METATYPE(QtWaylandServer::zwp_tablet_tool_v2::type);
+Q_DECLARE_METATYPE(QTabletEvent::PointerType);
+Q_DECLARE_METATYPE(Qt::MouseButton);
+
+class tst_tabletv2 : public QObject, private TabletCompositor
+{
+ using ToolType = QtWaylandServer::zwp_tablet_tool_v2::type;
+ Q_OBJECT
+private slots:
+ void cleanup();
+ void bindsToManager();
+ void createsTabletSeat();
+ void destroysTablet();
+ void destroysTool();
+ void destroysPad();
+ void proximityEvents();
+ void moveEvent();
+ void pointerType_data();
+ void pointerType();
+ void hardwareSerial();
+ void buttons_data();
+ void buttons();
+ void tabletEvents();
+};
+
+class ProximityFilter : public QObject {
+ Q_OBJECT
+public:
+ ProximityFilter() { qApp->installEventFilter(this); }
+ ~ProximityFilter() override { qDeleteAll(m_events); }
+ QVector<QTabletEvent *> m_events;
+
+ int nextEventIndex = 0;
+ int numEvents() const { return m_events.size() - nextEventIndex; }
+ QTabletEvent *popEvent()
+ {
+ auto *event = m_events.value(nextEventIndex, nullptr);
+ if (event)
+ ++nextEventIndex;
+ return event;
+ }
+
+protected:
+ bool eventFilter(QObject *object, QEvent *event) override
+ {
+ Q_UNUSED(object);
+ switch (event->type()) {
+ case QEvent::TabletEnterProximity:
+ case QEvent::TabletLeaveProximity: {
+ auto *e = static_cast<QTabletEvent *>(event);
+ auto *ev = new QTabletEvent(e->type(), e->posF(), e->globalPosF(), e->deviceType(),
+ e->pointerType(), e->pressure(), e->xTilt(), e->yTilt(),
+ e->tangentialPressure(), e->rotation(), e->z(),
+ Qt::KeyboardModifier::NoModifier, e->uniqueId(),
+ e->button(), e->buttons());
+ m_events << ev;
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
+ }
+};
+
+void tst_tabletv2::cleanup()
+{
+ exec([&] {
+ tabletSeat()->removeAll();
+ });
+ QCOMPOSITOR_COMPARE(get<TabletManagerV2>()->m_tabletSeats.size(), 1);
+ QCOMPOSITOR_COMPARE(tabletSeat()->m_tablets.size(), 0);
+ QCOMPOSITOR_COMPARE(tabletSeat()->m_tools.size(), 0);
+ QCOMPOSITOR_COMPARE(tabletSeat()->m_pads.size(), 0);
+
+ QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage()));
+}
+
+void tst_tabletv2::bindsToManager()
+{
+ QCOMPOSITOR_TRY_COMPARE(get<TabletManagerV2>()->resourceMap().size(), 1);
+ QCOMPOSITOR_TRY_COMPARE(get<TabletManagerV2>()->resourceMap().first()->version(), tabletVersion);
+}
+
+void tst_tabletv2::createsTabletSeat()
+{
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat());
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat()->resourceMap().contains(client()));
+ QCOMPOSITOR_TRY_COMPARE(tabletSeat()->resourceMap().value(client())->version(), tabletVersion);
+ //TODO: Maybe also assert some capability reported though qt APIs?
+}
+
+void tst_tabletv2::destroysTablet()
+{
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat());
+ exec([&] {
+ tabletSeat()->addTablet();
+ });
+ QCOMPOSITOR_TRY_VERIFY(tablet());
+
+ exec([&] {
+ tablet()->sendRemoved();
+ });
+
+ QCOMPOSITOR_TRY_VERIFY(!tablet());
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_tabletsWaitingForDestroy.empty());
+}
+
+void tst_tabletv2::destroysTool()
+{
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat());
+ exec([&] {
+ tabletSeat()->addTool();
+ });
+ QCOMPOSITOR_TRY_VERIFY(tabletTool());
+
+ exec([&] {
+ tabletTool()->sendRemoved();
+ });
+
+ QCOMPOSITOR_TRY_VERIFY(!tabletTool());
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_toolsWaitingForDestroy.empty());
+}
+
+void tst_tabletv2::destroysPad()
+{
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat());
+ exec([&] {
+ tabletSeat()->addPad();
+ });
+ QCOMPOSITOR_TRY_VERIFY(tabletPad());
+
+ exec([&] {
+ tabletPad()->sendRemoved();
+ });
+
+ QCOMPOSITOR_TRY_VERIFY(!tabletPad());
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_padsWaitingForDestroy.empty());
+}
+
+void tst_tabletv2::proximityEvents()
+{
+ ProximityFilter filter;
+
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat());
+ exec([&] {
+ tabletSeat()->addTablet();
+ tabletSeat()->addTool();
+ });
+
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ QCOMPOSITOR_TRY_VERIFY(tablet());
+ exec([&] {
+ auto *surface = xdgSurface()->m_surface;
+ auto *tool = tabletTool();
+ tool->sendProximityIn(tablet(), surface);
+ tool->sendFrame();
+ });
+
+ QTRY_COMPARE(filter.numEvents(), 1);
+ QTabletEvent *enterEvent = filter.popEvent();
+ QCOMPARE(enterEvent->type(), QEvent::TabletEnterProximity);
+
+ exec([&] {
+ auto *tool = tabletTool();
+ tool->sendProximityOut();
+ tool->sendFrame();
+ });
+
+ QTRY_COMPARE(filter.numEvents(), 1);
+ QTabletEvent *leaveEvent = filter.popEvent();
+ QCOMPARE(leaveEvent->type(), QEvent::TabletLeaveProximity);
+}
+
+class TabletWindow : public QRasterWindow {
+ Q_OBJECT
+public:
+ ~TabletWindow() override { qDeleteAll(m_events); }
+
+ void tabletEvent(QTabletEvent *e) override
+ {
+ m_events << new QTabletEvent(e->type(), e->posF(), e->globalPosF(), e->deviceType(),
+ e->pointerType(), e->pressure(), e->xTilt(), e->yTilt(),
+ e->tangentialPressure(), e->rotation(), e->z(),
+ Qt::KeyboardModifier::NoModifier, e->uniqueId(), e->button(),
+ e->buttons());
+ emit tabletEventReceived(m_events.last());
+ }
+ int nextEventIndex = 0;
+ int numEvents() const { return m_events.size() - nextEventIndex; }
+ QTabletEvent *popEvent()
+ {
+ auto *event = m_events.value(nextEventIndex, nullptr);
+ if (event)
+ ++nextEventIndex;
+ return event;
+ }
+
+signals:
+ void tabletEventReceived(QTabletEvent *event);
+
+private:
+ QVector<QTabletEvent *> m_events;
+};
+
+void tst_tabletv2::moveEvent()
+{
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat());
+ exec([&] {
+ tabletSeat()->addTablet();
+ tabletSeat()->addTool();
+ });
+
+ TabletWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ QCOMPOSITOR_TRY_VERIFY(tablet());
+ exec([&] {
+ auto *surface = xdgSurface()->m_surface;
+ auto *tool = tabletTool();
+ tool->sendProximityIn(tablet(), surface);
+ QMargins margins = window.frameMargins();
+ tool->sendMotion(QPointF(12 + margins.left(), 34 + margins.top()));
+ tool->sendFrame();
+ });
+ QTRY_VERIFY(window.numEvents());
+ QTabletEvent *event = window.popEvent();
+ QCOMPARE(event->type(), QEvent::TabletMove);
+ QCOMPARE(event->pressure(), 0);
+ QCOMPARE(event->posF(), QPointF(12, 34));
+}
+
+void tst_tabletv2::pointerType_data()
+{
+ QTest::addColumn<ToolType>("toolType");
+ QTest::addColumn<QTabletEvent::PointerType>("pointerType");
+ QTest::addColumn<QTabletEvent::TabletDevice>("tabletDevice");
+
+ QTest::newRow("pen") << ToolType::type_pen << QTabletEvent::PointerType::Pen << QTabletEvent::TabletDevice::Stylus;
+ QTest::newRow("eraser") << ToolType::type_eraser << QTabletEvent::PointerType::Eraser << QTabletEvent::TabletDevice::Stylus;
+ QTest::newRow("pencil") << ToolType::type_pencil << QTabletEvent::PointerType::Pen << QTabletEvent::TabletDevice::Stylus;
+ QTest::newRow("airbrush") << ToolType::type_airbrush << QTabletEvent::PointerType::Pen << QTabletEvent::TabletDevice::Airbrush;
+ QTest::newRow("brush") << ToolType::type_brush << QTabletEvent::PointerType::Pen << QTabletEvent::TabletDevice::Stylus; // TODO: is TabletDevice::Stylus the right thing?
+ QTest::newRow("lens") << ToolType::type_lens << QTabletEvent::PointerType::Cursor << QTabletEvent::TabletDevice::Puck;
+ // TODO: also add tests for FourDMouse and RotationStylus (also need to send capabilities)
+
+ // TODO: should these rather be mapped to touch/mouse events?
+ QTest::newRow("finger") << ToolType::type_finger << QTabletEvent::PointerType::UnknownPointer << QTabletEvent::TabletDevice::NoDevice;
+ QTest::newRow("mouse") << ToolType::type_mouse << QTabletEvent::PointerType::Cursor << QTabletEvent::TabletDevice::NoDevice;
+}
+
+void tst_tabletv2::pointerType()
+{
+ using ToolType = QtWaylandServer::zwp_tablet_tool_v2::type;
+ QFETCH(ToolType, toolType);
+ QFETCH(QTabletEvent::PointerType, pointerType);
+ QFETCH(QTabletEvent::TabletDevice, tabletDevice);
+
+ ProximityFilter filter;
+
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat());
+ exec([&] {
+ tabletSeat()->addTablet();
+ tabletSeat()->addTool(toolType);
+ });
+
+ TabletWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ QCOMPOSITOR_TRY_VERIFY(tablet());
+ exec([&] {
+ auto *surface = xdgSurface()->m_surface;
+ auto *tool = tabletTool();
+ tool->sendProximityIn(tablet(), surface);
+ QMargins margins = window.frameMargins();
+ tool->sendMotion(QPointF(12 + margins.left(), 34 + margins.top()));
+ tool->sendFrame();
+ });
+
+ QTRY_COMPARE(filter.numEvents(), 1);
+ QTabletEvent *event = filter.popEvent();
+ QCOMPARE(event->pointerType(), pointerType);
+ QCOMPARE(event->deviceType(), tabletDevice);
+
+ QTRY_VERIFY(window.numEvents());
+ event = window.popEvent();
+ QCOMPARE(event->pointerType(), pointerType);
+ QCOMPARE(event->deviceType(), tabletDevice);
+
+ exec([&] {
+ tabletTool()->sendProximityOut();
+ tabletTool()->sendFrame();
+ });
+
+ QTRY_VERIFY(filter.numEvents());
+ event = filter.popEvent();
+ QCOMPARE(event->pointerType(), pointerType);
+ QCOMPARE(event->deviceType(), tabletDevice);
+}
+
+void tst_tabletv2::hardwareSerial()
+{
+ ProximityFilter filter;
+ const quint64 uid = 0xbaba15dead15f00d;
+
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat());
+ exec([&] {
+ tabletSeat()->addTablet();
+ tabletSeat()->addTool(ToolType::type_pen, uid);
+ });
+
+ TabletWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ QCOMPOSITOR_TRY_VERIFY(tablet());
+ exec([&] {
+ auto *surface = xdgSurface()->m_surface;
+ auto *tool = tabletTool();
+ tool->sendProximityIn(tablet(), surface);
+ QMargins margins = window.frameMargins();
+ tool->sendMotion(QPointF(12 + margins.left(), 34 + margins.top()));
+ tool->sendFrame();
+ });
+
+ QTRY_COMPARE(filter.numEvents(), 1);
+ QTabletEvent *event = filter.popEvent();
+ QCOMPARE(event->uniqueId(), uid);
+
+ QTRY_VERIFY(window.numEvents());
+ event = window.popEvent();
+ QCOMPARE(event->uniqueId(), uid);
+
+ exec([&] {
+ tabletTool()->sendProximityOut();
+ tabletTool()->sendFrame();
+ });
+
+ QTRY_VERIFY(filter.numEvents());
+ event = filter.popEvent();
+ QCOMPARE(event->uniqueId(), uid);
+}
+
+// As defined in linux/input-event-codes.h
+#ifndef BTN_STYLUS
+#define BTN_STYLUS 0x14b
+#endif
+#ifndef BTN_STYLUS2
+#define BTN_STYLUS2 0x14c
+#endif
+
+void tst_tabletv2::buttons_data()
+{
+ QTest::addColumn<uint>("tabletButton");
+ QTest::addColumn<Qt::MouseButton>("mouseButton");
+
+ QTest::newRow("BTN_STYLUS2") << uint(BTN_STYLUS2) << Qt::MouseButton::RightButton;
+ QTest::newRow("BTN_STYLUS") << uint(BTN_STYLUS) << Qt::MouseButton::MiddleButton;
+}
+
+void tst_tabletv2::buttons()
+{
+ QFETCH(uint, tabletButton);
+ QFETCH(Qt::MouseButton, mouseButton);
+
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat());
+ exec([&] {
+ tabletSeat()->addTablet();
+ tabletSeat()->addTool();
+ });
+
+ TabletWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ QCOMPOSITOR_TRY_VERIFY(tablet());
+ exec([&] {
+ tabletTool()->sendProximityIn(tablet(), xdgSurface()->m_surface);
+ QMargins margins = window.frameMargins();
+ tabletTool()->sendMotion(QPointF(12 + margins.left(), 34 + margins.top()));
+ tabletTool()->sendFrame();
+ });
+
+ QTRY_VERIFY(window.numEvents());
+ window.popEvent();
+
+ QCOMPOSITOR_TRY_VERIFY(tablet());
+ exec([&] {
+ tabletTool()->sendButton(tabletButton, true);
+ tabletTool()->sendFrame();
+ tabletTool()->sendButton(tabletButton, false);
+ tabletTool()->sendFrame();
+ });
+
+ QTRY_VERIFY(window.numEvents());
+ QTabletEvent *event = window.popEvent();
+ QCOMPARE(event->buttons(), mouseButton);
+
+ exec([&] {
+ tabletTool()->sendProximityOut();
+ tabletTool()->sendFrame();
+ });
+}
+
+void tst_tabletv2::tabletEvents()
+{
+ QCOMPOSITOR_TRY_VERIFY(tabletSeat());
+ exec([&] {
+ tabletSeat()->addTablet();
+ tabletSeat()->addTool();
+ });
+
+ TabletWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ const QPointF insideDecorations(window.frameMargins().left(), window.frameMargins().top());
+
+ QCOMPOSITOR_TRY_VERIFY(tablet());
+ exec([&] {
+ auto *surface = xdgSurface()->m_surface;
+ auto *tool = tabletTool();
+ // TODO: encapsulate this into a helper function?
+ tool->sendProximityIn(tablet(), surface);
+ tool->sendMotion(QPointF(12, 34) + insideDecorations);
+ tool->sendDown();
+ tool->sendPressure(65535);
+ tool->sendFrame();
+ });
+
+ QTRY_VERIFY(window.numEvents());
+ QTabletEvent *event = window.popEvent();
+ QCOMPARE(event->type(), QEvent::TabletPress);
+ QCOMPARE(event->pressure(), 1.0);
+ QCOMPARE(event->posF(), QPointF(12, 34));
+
+ // Values we didn't send should be 0
+ QCOMPARE(event->rotation(), 0);
+ QCOMPARE(event->xTilt(), 0);
+ QCOMPARE(event->yTilt(), 0);
+
+ exec([&] {
+ tabletTool()->sendMotion(QPointF(45, 56) + insideDecorations);
+ tabletTool()->sendPressure(65535/2);
+ tabletTool()->sendRotation(90);
+ tabletTool()->sendTilt(13, 37);
+ tabletTool()->sendFrame();
+ });
+
+ QTRY_VERIFY(window.numEvents());
+ event = window.popEvent();
+ QCOMPARE(event->type(), QEvent::TabletMove);
+ QVERIFY(qAbs(event->pressure() - 0.5) < 0.01);
+ QVERIFY(qAbs(event->rotation() - 90) < 0.01);
+ QVERIFY(qAbs(event->xTilt() - 13) < 0.01);
+ QVERIFY(qAbs(event->yTilt() - 37) < 0.01);
+ QCOMPARE(event->posF(), QPointF(45, 56));
+
+ // Verify that the values stay the same if we don't update them
+ exec([&] {
+ tabletTool()->sendMotion(QPointF(10, 11) + insideDecorations); // Change position only
+ tabletTool()->sendFrame();
+ });
+ QTRY_VERIFY(window.numEvents());
+ event = window.popEvent();
+ QCOMPARE(event->type(), QEvent::TabletMove);
+ QVERIFY(qAbs(event->pressure() - 0.5) < 0.01);
+ QVERIFY(qAbs(event->rotation() - 90) < 0.01);
+ QVERIFY(qAbs(event->xTilt() - 13) < 0.01);
+ QVERIFY(qAbs(event->yTilt() - 37) < 0.01);
+ QCOMPARE(event->posF(), QPointF(10, 11));
+
+ exec([&] {
+ tabletTool()->sendPressure(0);
+ tabletTool()->sendUp();
+ tabletTool()->sendFrame();
+
+ tabletTool()->sendProximityOut();
+ tabletTool()->sendFrame();
+ });
+
+ QTRY_VERIFY(window.numEvents());
+ event = window.popEvent();
+ QCOMPARE(event->type(), QEvent::TabletRelease);
+ QCOMPARE(event->pressure(), 0);
+ QCOMPARE(event->posF(), QPointF(10, 11));
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_tabletv2)
+#include "tst_tabletv2.moc"
diff --git a/tests/auto/client/xdgdecorationv1/tst_xdgdecorationv1.cpp b/tests/auto/client/xdgdecorationv1/tst_xdgdecorationv1.cpp
index 386713cf5..cf20a1373 100644
--- a/tests/auto/client/xdgdecorationv1/tst_xdgdecorationv1.cpp
+++ b/tests/auto/client/xdgdecorationv1/tst_xdgdecorationv1.cpp
@@ -30,8 +30,8 @@
#include <qwayland-server-xdg-decoration-unstable-v1.h>
+#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
-#include <QtGui/QOpenGLWindow>
#include <QtGui/QClipboard>
#include <QtCore/private/qcore_unix_p.h>
diff --git a/tests/auto/client/xdgoutput/tst_xdgoutput.cpp b/tests/auto/client/xdgoutput/tst_xdgoutput.cpp
index a628810d1..16e29a3c6 100644
--- a/tests/auto/client/xdgoutput/tst_xdgoutput.cpp
+++ b/tests/auto/client/xdgoutput/tst_xdgoutput.cpp
@@ -27,8 +27,8 @@
****************************************************************************/
#include "mockcompositor.h"
+#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
-#include <QtGui/QOpenGLWindow>
#include <QtGui/QScreen>
#include <qwayland-server-xdg-output-unstable-v1.h>
diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp
index ac5c24988..eabf29b7e 100644
--- a/tests/auto/client/xdgshell/tst_xdgshell.cpp
+++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp
@@ -27,8 +27,8 @@
****************************************************************************/
#include "mockcompositor.h"
+#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
-#include <QtGui/QOpenGLWindow>
#include <QtGui/qpa/qplatformnativeinterface.h>
#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
@@ -46,6 +46,7 @@ private slots:
void popup();
void tooltipOnPopup();
void switchPopups();
+ void hidePopupParent();
void pongs();
void minMaxSize();
void windowGeometry();
@@ -219,8 +220,8 @@ void tst_xdgshell::popup()
p->sendFrame(c);
uint serial = p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
p->sendButton(c, BTN_LEFT, Pointer::button_state_released);
- return serial;
p->sendFrame(c);
+ return serial;
});
QTRY_VERIFY(window.m_popup);
@@ -429,6 +430,50 @@ void tst_xdgshell::switchPopups()
QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial);
}
+void tst_xdgshell::hidePopupParent()
+{
+ 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());
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial);
+
+ exec([=] {
+ auto *surface = xdgToplevel()->surface();
+ auto *p = pointer();
+ auto *c = client();
+ p->sendEnter(surface, {100, 100});
+ p->sendFrame(c);
+ p->sendButton(c, BTN_LEFT, Pointer::button_state_pressed);
+ p->sendButton(c, BTN_LEFT, Pointer::button_state_released);
+ p->sendFrame(c);
+ });
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup());
+ exec([=] {
+ xdgPopup()->sendConfigure(QRect(100, 100, 100, 100));
+ xdgPopup()->m_xdgSurface->sendConfigure();
+ });
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial);
+
+ window.hide();
+ QCOMPOSITOR_TRY_VERIFY(!xdgToplevel());
+}
+
void tst_xdgshell::pongs()
{
// Create and show a window to trigger shell integration initialzation,
diff --git a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp
index e44475de7..76df6eb58 100644
--- a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp
+++ b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp
@@ -403,7 +403,7 @@ void tst_WaylandClientXdgShellV6::flushUnconfiguredXdgSurface()
m_compositor->sendShellSurfaceConfigure(surface);
QTRY_COMPARE(surface->image.size(), window.frameGeometry().size());
QTRY_COMPARE(surface->image.pixel(window.frameMargins().left(), window.frameMargins().top()), color.rgba());
- QVERIFY(window.isExposed());
+ QTRY_VERIFY(window.isExposed());
}
void tst_WaylandClientXdgShellV6::dontSpamExposeEvents()
diff --git a/tests/auto/compositor/compositor/tst_compositor.cpp b/tests/auto/compositor/compositor/tst_compositor.cpp
index e87fab2a8..1397414af 100644
--- a/tests/auto/compositor/compositor/tst_compositor.cpp
+++ b/tests/auto/compositor/compositor/tst_compositor.cpp
@@ -545,11 +545,6 @@ void tst_WaylandCompositor::mapSurfaceHiDpi()
QObject::connect(waylandSurface, &QWaylandSurface::hasContentChanged, verifyComittedState);
QSignalSpy hasContentSpy(waylandSurface, SIGNAL(hasContentChanged()));
-#if QT_DEPRECATED_SINCE(5, 13)
- QObject::connect(waylandSurface, &QWaylandSurface::sizeChanged, verifyComittedState);
- QSignalSpy sizeSpy(waylandSurface, SIGNAL(sizeChanged()));
-#endif
-
QObject::connect(waylandSurface, &QWaylandSurface::bufferSizeChanged, verifyComittedState);
QSignalSpy bufferSizeSpy(waylandSurface, SIGNAL(bufferSizeChanged()));
@@ -575,9 +570,6 @@ void tst_WaylandCompositor::mapSurfaceHiDpi()
wl_surface_commit(surface);
QTRY_COMPARE(hasContentSpy.count(), 1);
-#if QT_DEPRECATED_SINCE(5, 13)
- QTRY_COMPARE(sizeSpy.count(), 1);
-#endif
QTRY_COMPARE(bufferSizeSpy.count(), 1);
QTRY_COMPARE(destinationSizeSpy.count(), 1);
QTRY_COMPARE(bufferScaleSpy.count(), 1);