18#include <KWayland/Client/compositor.h>
19#include <KWayland/Client/connection_thread.h>
20#include <KWayland/Client/seat.h>
21#include <KWayland/Client/surface.h>
22#include <KWayland/Client/touch.h>
30static const QString s_socketName = QStringLiteral(
"wayland_test_kwin_touch_input-0");
39 void testTouchHidesCursor();
40 void testMultipleTouchPoints_data();
41 void testMultipleTouchPoints();
43 void testTouchMouseAction();
44 void testTouchPointCount();
45 void testUpdateFocusOnDecorationDestroy();
46 void testGestureDetection();
49 std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> showWindow(
bool decorated =
false);
50 KWayland::Client::Touch *m_touch =
nullptr;
53void TouchInputTest::initTestCase()
55 qRegisterMetaType<KWin::Window *>();
59 QRect(0, 0, 1280, 1024),
60 QRect(1280, 0, 1280, 1024),
64 QVERIFY(applicationStartedSpy.wait());
66 QCOMPARE(outputs.count(), 2);
67 QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
68 QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
71void TouchInputTest::init()
77 QVERIFY(m_touch->isValid());
83void TouchInputTest::cleanup()
90std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> TouchInputTest::showWindow(
bool decorated)
92#define VERIFY(statement) \
93 if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) \
94 return {nullptr, nullptr};
95#define COMPARE(actual, expected) \
96 if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
97 return {nullptr, nullptr};
105 decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
108 surface->commit(KWayland::Client::Surface::CommitFlag::None);
109 VERIFY(surfaceConfigureRequestedSpy.wait());
111 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
120 return {window, std::move(surface)};
123void TouchInputTest::testTouchHidesCursor()
126 quint32 timestamp = 1;
147void TouchInputTest::testMultipleTouchPoints_data()
149 QTest::addColumn<bool>(
"decorated");
151 QTest::newRow(
"undecorated") <<
false;
152 QTest::newRow(
"decorated") <<
true;
155void TouchInputTest::testMultipleTouchPoints()
157 QFETCH(
bool, decorated);
158 auto [window, surface] = showWindow(decorated);
159 QCOMPARE(window->isDecorated(), decorated);
160 window->move(QPoint(100, 100));
162 QSignalSpy sequenceStartedSpy(m_touch, &KWayland::Client::Touch::sequenceStarted);
163 QSignalSpy pointAddedSpy(m_touch, &KWayland::Client::Touch::pointAdded);
164 QSignalSpy pointMovedSpy(m_touch, &KWayland::Client::Touch::pointMoved);
165 QSignalSpy pointRemovedSpy(m_touch, &KWayland::Client::Touch::pointRemoved);
166 QSignalSpy endedSpy(m_touch, &KWayland::Client::Touch::sequenceEnded);
168 quint32 timestamp = 1;
169 Test::touchDown(1, window->mapFromLocal(QPointF(25, 25)), timestamp++);
170 QVERIFY(sequenceStartedSpy.wait());
171 QCOMPARE(sequenceStartedSpy.count(), 1);
172 QCOMPARE(m_touch->sequence().count(), 1);
173 QCOMPARE(m_touch->sequence().first()->isDown(),
true);
174 QCOMPARE(m_touch->sequence().first()->position(), QPointF(25, 25));
175 QCOMPARE(pointAddedSpy.count(), 0);
176 QCOMPARE(pointMovedSpy.count(), 0);
179 Test::touchDown(2, window->mapFromLocal(QPointF(-100, -100)), timestamp++);
180 QVERIFY(pointAddedSpy.wait());
181 QCOMPARE(pointAddedSpy.count(), 1);
182 QCOMPARE(m_touch->sequence().count(), 2);
183 QCOMPARE(m_touch->sequence().at(1)->isDown(),
true);
184 QCOMPARE(m_touch->sequence().at(1)->position(), QPointF(-100, -100));
185 QCOMPARE(pointMovedSpy.count(), 0);
189 QVERIFY(pointMovedSpy.wait());
190 QCOMPARE(pointMovedSpy.count(), 1);
191 QCOMPARE(m_touch->sequence().count(), 2);
192 QCOMPARE(m_touch->sequence().at(1)->isDown(),
true);
193 QCOMPARE(m_touch->sequence().at(1)->position(), QPointF(0, 0));
196 QVERIFY(pointRemovedSpy.wait());
197 QCOMPARE(pointRemovedSpy.count(), 1);
198 QCOMPARE(m_touch->sequence().count(), 2);
199 QCOMPARE(m_touch->sequence().first()->isDown(),
false);
200 QCOMPARE(endedSpy.count(), 0);
203 QVERIFY(pointRemovedSpy.wait());
204 QCOMPARE(pointRemovedSpy.count(), 2);
205 QCOMPARE(m_touch->sequence().count(), 2);
206 QCOMPARE(m_touch->sequence().first()->isDown(),
false);
207 QCOMPARE(m_touch->sequence().at(1)->isDown(),
false);
208 QCOMPARE(endedSpy.count(), 1);
211void TouchInputTest::testCancel()
213 auto [window, surface] = showWindow();
214 window->move(QPoint(100, 100));
216 QSignalSpy sequenceStartedSpy(m_touch, &KWayland::Client::Touch::sequenceStarted);
217 QSignalSpy cancelSpy(m_touch, &KWayland::Client::Touch::sequenceCanceled);
218 QSignalSpy pointRemovedSpy(m_touch, &KWayland::Client::Touch::pointRemoved);
220 quint32 timestamp = 1;
222 QVERIFY(sequenceStartedSpy.wait());
223 QCOMPARE(sequenceStartedSpy.count(), 1);
227 QVERIFY(cancelSpy.wait());
228 QCOMPARE(cancelSpy.count(), 1);
231void TouchInputTest::testTouchMouseAction()
236 auto [c1, surface] = showWindow();
238 auto [c2, surface2] = showWindow();
241 QVERIFY(!c1->isActive());
242 QVERIFY(c2->isActive());
245 QSignalSpy sequenceStartedSpy(m_touch, &KWayland::Client::Touch::sequenceStarted);
247 quint32 timestamp = 1;
249 QVERIFY(c1->isActive());
251 QVERIFY(sequenceStartedSpy.wait());
252 QCOMPARE(sequenceStartedSpy.count(), 1);
258void TouchInputTest::testTouchPointCount()
260 QCOMPARE(
input()->touch()->touchPointCount(), 0);
261 quint32 timestamp = 1;
265 QCOMPARE(
input()->touch()->touchPointCount(), 3);
268 QCOMPARE(
input()->touch()->touchPointCount(), 2);
271 QCOMPARE(
input()->touch()->touchPointCount(), 0);
274void TouchInputTest::testUpdateFocusOnDecorationDestroy()
279 QSignalSpy sequenceEndedSpy(m_touch, &KWayland::Client::Touch::sequenceEnded);
282 auto group = kwinApp()->config()->group(QStringLiteral(
"Windows"));
283 group.writeEntry(
"BorderlessMaximizedWindows",
true);
296 decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
297 surface->commit(KWayland::Client::Surface::CommitFlag::None);
300 Test::XdgToplevel::States states;
301 QVERIFY(surfaceConfigureRequestedSpy.wait());
302 QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
303 QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(0, 0));
304 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
309 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
312 QVERIFY(window->isActive());
315 QCOMPARE(window->isDecorated(),
true);
318 QVERIFY(surfaceConfigureRequestedSpy.wait());
319 QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
320 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
325 quint32 timestamp = 0;
327 QVERIFY(
input()->touch()->decoration());
331 QVERIFY(surfaceConfigureRequestedSpy.wait());
332 QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
333 QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024));
334 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
339 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
340 Test::render(surface.get(), QSize(1280, 1024), Qt::blue);
341 QVERIFY(frameGeometryChangedSpy.wait());
342 QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
344 QCOMPARE(window->requestedMaximizeMode(),
MaximizeFull);
345 QCOMPARE(window->isDecorated(),
false);
348 QVERIFY(!
input()->touch()->decoration());
350 QVERIFY(!sequenceEndedSpy.wait(100));
353 QVERIFY(sequenceEndedSpy.wait());
356 shellSurface.reset();
360void TouchInputTest::testGestureDetection()
362#if !KWIN_BUILD_GLOBALSHORTCUTS
363 QSKIP(
"Can't test shortcuts without shortcuts");
367 bool callbackTriggered =
false;
368 const auto callback = [&callbackTriggered](
float progress) {
369 callbackTriggered =
true;
370 qWarning() <<
"progress callback!" << progress;
377 quint32 timestamp = 1;
383 QVERIFY(callbackTriggered);
386 QSignalSpy gestureCancelled(&action, &QAction::triggered);
388 QVERIFY(gestureCancelled.wait());
393 callbackTriggered =
false;
399 QVERIFY(!callbackTriggered);
410 QVERIFY(!callbackTriggered);
424 QVERIFY(callbackTriggered);
433#include "touch_input_test.moc"
bool borderlessMaximizedWindows
void configureRequested(quint32 serial)
void configureRequested(QtWayland::zxdg_toplevel_decoration_v1::mode mode)
void configureRequested(const QSize &size, KWin::Test::XdgToplevel::States states)
void frameGeometryChanged(const QRectF &oldGeometry)
void slotWindowMaximize()
static Workspace * self()
QList< Output * > outputs() const
void setActiveOutput(Output *output)
#define WAYLANDTEST_MAIN(TestObject)
Window * renderAndWaitForShown(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32, int timeout=5000)
void destroyWaylandConnection()
void setOutputConfig(const QList< QRect > &geometries)
void touchDown(qint32 id, const QPointF &pos, quint32 time)
void pointerAxisVertical(qreal delta, quint32 time, qint32 discreteDelta=0, InputRedirection::PointerAxisSource source=InputRedirection::PointerAxisSourceUnknown)
bool setupWaylandConnection(AdditionalWaylandInterfaces flags=AdditionalWaylandInterfaces())
void touchMotion(qint32 id, const QPointF &pos, quint32 time)
void render(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32_Premultiplied)
KWayland::Client::Seat * waylandSeat()
bool waitForWaylandTouch()
void pointerMotion(const QPointF &position, quint32 time)
std::unique_ptr< KWayland::Client::Surface > createSurface()
XdgToplevel * createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent=nullptr)
XdgToplevelDecorationV1 * createXdgToplevelDecorationV1(XdgToplevel *toplevel, QObject *parent=nullptr)
void touchUp(qint32 id, quint32 time)
bool waitForWindowClosed(Window *window)
@ MaximizeRestore
The window is not maximized in any direction.
@ MaximizeFull
Equal to MaximizeVertical | MaximizeHorizontal.
WaylandServer * waylandServer()
InputRedirection * input()