File indexing completed on 2024-05-05 17:35:50

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "kwin_wayland_test.h"
0010 
0011 #include "composite.h"
0012 #include "core/output.h"
0013 #include "core/outputbackend.h"
0014 #include "core/renderbackend.h"
0015 #include "cursor.h"
0016 #include "screenedge.h"
0017 #include "wayland/keyboard_interface.h"
0018 #include "wayland/seat_interface.h"
0019 #include "wayland_server.h"
0020 #include "window.h"
0021 #include "workspace.h"
0022 #include <kwineffects.h>
0023 
0024 #include <KWayland/Client/compositor.h>
0025 #include <KWayland/Client/connection_thread.h>
0026 #include <KWayland/Client/keyboard.h>
0027 #include <KWayland/Client/pointer.h>
0028 #include <KWayland/Client/registry.h>
0029 #include <KWayland/Client/seat.h>
0030 #include <KWayland/Client/shm_pool.h>
0031 #include <KWayland/Client/surface.h>
0032 #include <KWayland/Client/touch.h>
0033 
0034 // screenlocker
0035 #include <KScreenLocker/KsldApp>
0036 
0037 #include <KGlobalAccel>
0038 
0039 #include <QAction>
0040 
0041 #include <linux/input.h>
0042 
0043 Q_DECLARE_METATYPE(Qt::Orientation)
0044 
0045 namespace KWin
0046 {
0047 
0048 static const QString s_socketName = QStringLiteral("wayland_test_kwin_lock_screen-0");
0049 
0050 class LockScreenTest : public QObject
0051 {
0052     Q_OBJECT
0053 private Q_SLOTS:
0054     void initTestCase();
0055     void init();
0056     void cleanup();
0057     void testStackingOrder();
0058     void testPointer();
0059     void testPointerButton();
0060     void testPointerAxis();
0061     void testKeyboard();
0062     void testScreenEdge();
0063     void testEffects();
0064     void testEffectsKeyboard();
0065     void testEffectsKeyboardAutorepeat();
0066     void testMoveWindow();
0067     void testPointerShortcut();
0068     void testAxisShortcut_data();
0069     void testAxisShortcut();
0070     void testKeyboardShortcut();
0071     void testTouch();
0072 
0073 private:
0074     void unlock();
0075     std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> showWindow();
0076     KWayland::Client::ConnectionThread *m_connection = nullptr;
0077     KWayland::Client::Compositor *m_compositor = nullptr;
0078     KWayland::Client::Seat *m_seat = nullptr;
0079     KWayland::Client::ShmPool *m_shm = nullptr;
0080 };
0081 
0082 class HelperEffect : public Effect
0083 {
0084     Q_OBJECT
0085 public:
0086     HelperEffect()
0087     {
0088     }
0089     ~HelperEffect() override
0090     {
0091     }
0092 
0093     void windowInputMouseEvent(QEvent *) override
0094     {
0095         Q_EMIT inputEvent();
0096     }
0097     void grabbedKeyboardEvent(QKeyEvent *e) override
0098     {
0099         Q_EMIT keyEvent(e->text());
0100     }
0101 
0102 Q_SIGNALS:
0103     void inputEvent();
0104     void keyEvent(const QString &);
0105 };
0106 
0107 #define LOCK                                                                                                     \
0108     do {                                                                                                         \
0109         QVERIFY(!waylandServer()->isScreenLocked());                                                             \
0110         QSignalSpy lockStateChangedSpy(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged); \
0111         ScreenLocker::KSldApp::self()->lock(ScreenLocker::EstablishLock::Immediate);                             \
0112         QTRY_COMPARE(ScreenLocker::KSldApp::self()->lockState(), ScreenLocker::KSldApp::Locked);                 \
0113         QVERIFY(waylandServer()->isScreenLocked());                                                              \
0114     } while (false)
0115 
0116 #define UNLOCK                                                                                                   \
0117     do {                                                                                                         \
0118         QSignalSpy lockStateChangedSpy(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged); \
0119         unlock();                                                                                                \
0120         if (lockStateChangedSpy.count() != 1) {                                                                  \
0121             QVERIFY(lockStateChangedSpy.wait());                                                                 \
0122         }                                                                                                        \
0123         QCOMPARE(lockStateChangedSpy.count(), 1);                                                                \
0124         QVERIFY(!waylandServer()->isScreenLocked());                                                             \
0125     } while (false)
0126 
0127 #define MOTION(target) Test::pointerMotion(target, timestamp++)
0128 
0129 #define PRESS Test::pointerButtonPressed(BTN_LEFT, timestamp++)
0130 
0131 #define RELEASE Test::pointerButtonReleased(BTN_LEFT, timestamp++)
0132 
0133 #define KEYPRESS(key) Test::keyboardKeyPressed(key, timestamp++)
0134 
0135 #define KEYRELEASE(key) Test::keyboardKeyReleased(key, timestamp++)
0136 
0137 void LockScreenTest::unlock()
0138 {
0139     using namespace ScreenLocker;
0140     const auto children = KSldApp::self()->children();
0141     for (auto it = children.begin(); it != children.end(); ++it) {
0142         if (qstrcmp((*it)->metaObject()->className(), "LogindIntegration") != 0) {
0143             continue;
0144         }
0145         QMetaObject::invokeMethod(*it, "requestUnlock");
0146         break;
0147     }
0148 }
0149 
0150 std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> LockScreenTest::showWindow()
0151 {
0152 #define VERIFY(statement)                                                 \
0153     if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) \
0154         return {nullptr, nullptr};
0155 #define COMPARE(actual, expected)                                                   \
0156     if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
0157         return {nullptr, nullptr};
0158 
0159     std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
0160     VERIFY(surface.get());
0161     Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
0162     VERIFY(shellSurface);
0163     // let's render
0164     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0165 
0166     VERIFY(window);
0167     COMPARE(workspace()->activeWindow(), window);
0168 
0169 #undef VERIFY
0170 #undef COMPARE
0171 
0172     return {window, std::move(surface)};
0173 }
0174 
0175 void LockScreenTest::initTestCase()
0176 {
0177     qRegisterMetaType<KWin::Window *>();
0178     qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
0179 
0180     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0181     QVERIFY(waylandServer()->init(s_socketName));
0182     QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024)));
0183 
0184     qputenv("KWIN_COMPOSE", QByteArrayLiteral("O2"));
0185     kwinApp()->start();
0186     QVERIFY(applicationStartedSpy.wait());
0187     const auto outputs = workspace()->outputs();
0188     QCOMPARE(outputs.count(), 2);
0189     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0190     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0191     setenv("QT_QPA_PLATFORM", "wayland", true);
0192 
0193     QCOMPARE(Compositor::self()->backend()->compositingType(), KWin::OpenGLCompositing);
0194 }
0195 
0196 void LockScreenTest::init()
0197 {
0198     QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat));
0199     QVERIFY(Test::waitForWaylandPointer());
0200     m_connection = Test::waylandConnection();
0201     m_compositor = Test::waylandCompositor();
0202     m_shm = Test::waylandShmPool();
0203     m_seat = Test::waylandSeat();
0204 
0205     workspace()->setActiveOutput(QPoint(640, 512));
0206     Cursors::self()->mouse()->setPos(QPoint(640, 512));
0207 }
0208 
0209 void LockScreenTest::cleanup()
0210 {
0211     Test::destroyWaylandConnection();
0212 }
0213 
0214 void LockScreenTest::testStackingOrder()
0215 {
0216     // This test verifies that the lockscreen greeter is placed above other windows.
0217 
0218     QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
0219 
0220     LOCK;
0221     QVERIFY(windowAddedSpy.wait());
0222 
0223     Window *window = windowAddedSpy.first().first().value<Window *>();
0224     QVERIFY(window);
0225     QVERIFY(window->isLockScreen());
0226     QCOMPARE(window->layer(), UnmanagedLayer);
0227 
0228     UNLOCK;
0229 }
0230 
0231 void LockScreenTest::testPointer()
0232 {
0233     std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
0234     QVERIFY(pointer != nullptr);
0235     QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
0236     QSignalSpy leftSpy(pointer.get(), &KWayland::Client::Pointer::left);
0237 
0238     auto [window, surface] = showWindow();
0239     QVERIFY(window);
0240 
0241     // first move cursor into the center of the window
0242     quint32 timestamp = 1;
0243     MOTION(window->frameGeometry().center());
0244     QVERIFY(enteredSpy.wait());
0245 
0246     LOCK;
0247 
0248     QVERIFY(leftSpy.wait());
0249     QCOMPARE(leftSpy.count(), 1);
0250 
0251     // simulate moving out in and out again
0252     MOTION(window->frameGeometry().center());
0253     MOTION(window->frameGeometry().bottomRight() + QPoint(100, 100));
0254     MOTION(window->frameGeometry().bottomRight() + QPoint(100, 100));
0255     QVERIFY(!leftSpy.wait(10));
0256     QCOMPARE(leftSpy.count(), 1);
0257     QCOMPARE(enteredSpy.count(), 1);
0258 
0259     // go back on the window
0260     MOTION(window->frameGeometry().center());
0261     // and unlock
0262     UNLOCK;
0263 
0264     QVERIFY(enteredSpy.wait());
0265     QCOMPARE(enteredSpy.count(), 2);
0266     // move on the window
0267     MOTION(window->frameGeometry().center() + QPoint(100, 100));
0268     QVERIFY(leftSpy.wait());
0269     MOTION(window->frameGeometry().center());
0270     QVERIFY(enteredSpy.wait());
0271     QCOMPARE(enteredSpy.count(), 3);
0272 }
0273 
0274 void LockScreenTest::testPointerButton()
0275 {
0276     std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
0277     QVERIFY(pointer != nullptr);
0278     QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
0279     QSignalSpy buttonChangedSpy(pointer.get(), &KWayland::Client::Pointer::buttonStateChanged);
0280 
0281     auto [window, surface] = showWindow();
0282     QVERIFY(window);
0283 
0284     // first move cursor into the center of the window
0285     quint32 timestamp = 1;
0286     MOTION(window->frameGeometry().center());
0287     QVERIFY(enteredSpy.wait());
0288     // and simulate a click
0289     PRESS;
0290     QVERIFY(buttonChangedSpy.wait());
0291     RELEASE;
0292     QVERIFY(buttonChangedSpy.wait());
0293 
0294     LOCK;
0295 
0296     // and simulate a click
0297     PRESS;
0298     QVERIFY(!buttonChangedSpy.wait(10));
0299     RELEASE;
0300     QVERIFY(!buttonChangedSpy.wait(10));
0301 
0302     UNLOCK;
0303     QVERIFY(enteredSpy.wait());
0304     QCOMPARE(enteredSpy.count(), 2);
0305 
0306     // and click again
0307     PRESS;
0308     QVERIFY(buttonChangedSpy.wait());
0309     RELEASE;
0310     QVERIFY(buttonChangedSpy.wait());
0311 }
0312 
0313 void LockScreenTest::testPointerAxis()
0314 {
0315     std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
0316     QVERIFY(pointer != nullptr);
0317     QSignalSpy axisChangedSpy(pointer.get(), &KWayland::Client::Pointer::axisChanged);
0318     QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
0319 
0320     auto [window, surface] = showWindow();
0321     QVERIFY(window);
0322 
0323     // first move cursor into the center of the window
0324     quint32 timestamp = 1;
0325     MOTION(window->frameGeometry().center());
0326     QVERIFY(enteredSpy.wait());
0327     // and simulate axis
0328     Test::pointerAxisHorizontal(5.0, timestamp++);
0329     QVERIFY(axisChangedSpy.wait());
0330 
0331     LOCK;
0332 
0333     // and simulate axis
0334     Test::pointerAxisHorizontal(5.0, timestamp++);
0335     QVERIFY(!axisChangedSpy.wait(10));
0336     Test::pointerAxisVertical(5.0, timestamp++);
0337     QVERIFY(!axisChangedSpy.wait(10));
0338 
0339     // and unlock
0340     UNLOCK;
0341     QVERIFY(enteredSpy.wait());
0342     QCOMPARE(enteredSpy.count(), 2);
0343 
0344     // and move axis again
0345     Test::pointerAxisHorizontal(5.0, timestamp++);
0346     QVERIFY(axisChangedSpy.wait());
0347     Test::pointerAxisVertical(5.0, timestamp++);
0348     QVERIFY(axisChangedSpy.wait());
0349 }
0350 
0351 void LockScreenTest::testKeyboard()
0352 {
0353     std::unique_ptr<KWayland::Client::Keyboard> keyboard(m_seat->createKeyboard());
0354     QVERIFY(keyboard != nullptr);
0355     QSignalSpy enteredSpy(keyboard.get(), &KWayland::Client::Keyboard::entered);
0356     QSignalSpy leftSpy(keyboard.get(), &KWayland::Client::Keyboard::left);
0357     QSignalSpy keyChangedSpy(keyboard.get(), &KWayland::Client::Keyboard::keyChanged);
0358 
0359     auto [window, surface] = showWindow();
0360     QVERIFY(window);
0361     QVERIFY(enteredSpy.wait());
0362     QTRY_COMPARE(enteredSpy.count(), 1);
0363 
0364     quint32 timestamp = 1;
0365     KEYPRESS(KEY_A);
0366     QVERIFY(keyChangedSpy.wait());
0367     QCOMPARE(keyChangedSpy.count(), 1);
0368     QCOMPARE(keyChangedSpy.at(0).at(0).value<quint32>(), quint32(KEY_A));
0369     QCOMPARE(keyChangedSpy.at(0).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Pressed);
0370     QCOMPARE(keyChangedSpy.at(0).at(2).value<quint32>(), quint32(1));
0371     KEYRELEASE(KEY_A);
0372     QVERIFY(keyChangedSpy.wait());
0373     QCOMPARE(keyChangedSpy.count(), 2);
0374     QCOMPARE(keyChangedSpy.at(1).at(0).value<quint32>(), quint32(KEY_A));
0375     QCOMPARE(keyChangedSpy.at(1).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Released);
0376     QCOMPARE(keyChangedSpy.at(1).at(2).value<quint32>(), quint32(2));
0377 
0378     LOCK;
0379     QVERIFY(leftSpy.wait());
0380     KEYPRESS(KEY_B);
0381     KEYRELEASE(KEY_B);
0382     QCOMPARE(leftSpy.count(), 1);
0383     QCOMPARE(keyChangedSpy.count(), 2);
0384 
0385     UNLOCK;
0386     QVERIFY(enteredSpy.wait());
0387     QCOMPARE(enteredSpy.count(), 2);
0388     KEYPRESS(KEY_C);
0389     QVERIFY(keyChangedSpy.wait());
0390     QCOMPARE(keyChangedSpy.count(), 3);
0391     KEYRELEASE(KEY_C);
0392     QVERIFY(keyChangedSpy.wait());
0393     QCOMPARE(keyChangedSpy.count(), 4);
0394     QCOMPARE(enteredSpy.count(), 2);
0395     QCOMPARE(keyChangedSpy.at(2).at(0).value<quint32>(), quint32(KEY_C));
0396     QCOMPARE(keyChangedSpy.at(3).at(0).value<quint32>(), quint32(KEY_C));
0397     QCOMPARE(keyChangedSpy.at(2).at(2).value<quint32>(), quint32(5));
0398     QCOMPARE(keyChangedSpy.at(3).at(2).value<quint32>(), quint32(6));
0399     QCOMPARE(keyChangedSpy.at(2).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Pressed);
0400     QCOMPARE(keyChangedSpy.at(3).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Released);
0401 }
0402 
0403 void LockScreenTest::testScreenEdge()
0404 {
0405     QSignalSpy screenEdgeSpy(workspace()->screenEdges(), &ScreenEdges::approaching);
0406     QCOMPARE(screenEdgeSpy.count(), 0);
0407 
0408     quint32 timestamp = 1;
0409     MOTION(QPoint(5, 5));
0410     QCOMPARE(screenEdgeSpy.count(), 1);
0411 
0412     LOCK;
0413 
0414     MOTION(QPoint(4, 4));
0415     QCOMPARE(screenEdgeSpy.count(), 1);
0416 
0417     // and unlock
0418     UNLOCK;
0419 
0420     MOTION(QPoint(5, 5));
0421     QCOMPARE(screenEdgeSpy.count(), 2);
0422 }
0423 
0424 void LockScreenTest::testEffects()
0425 {
0426     std::unique_ptr<HelperEffect> effect(new HelperEffect);
0427     QSignalSpy inputSpy(effect.get(), &HelperEffect::inputEvent);
0428     effects->startMouseInterception(effect.get(), Qt::ArrowCursor);
0429 
0430     quint32 timestamp = 1;
0431     QCOMPARE(inputSpy.count(), 0);
0432     MOTION(QPoint(5, 5));
0433     QCOMPARE(inputSpy.count(), 1);
0434     // simlate click
0435     PRESS;
0436     QCOMPARE(inputSpy.count(), 2);
0437     RELEASE;
0438     QCOMPARE(inputSpy.count(), 3);
0439 
0440     LOCK;
0441 
0442     MOTION(QPoint(6, 6));
0443     QCOMPARE(inputSpy.count(), 3);
0444     // simlate click
0445     PRESS;
0446     QCOMPARE(inputSpy.count(), 3);
0447     RELEASE;
0448     QCOMPARE(inputSpy.count(), 3);
0449 
0450     UNLOCK;
0451 
0452     MOTION(QPoint(5, 5));
0453     QCOMPARE(inputSpy.count(), 4);
0454     // simlate click
0455     PRESS;
0456     QCOMPARE(inputSpy.count(), 5);
0457     RELEASE;
0458     QCOMPARE(inputSpy.count(), 6);
0459 
0460     effects->stopMouseInterception(effect.get());
0461 }
0462 
0463 void LockScreenTest::testEffectsKeyboard()
0464 {
0465     std::unique_ptr<HelperEffect> effect(new HelperEffect);
0466     QSignalSpy inputSpy(effect.get(), &HelperEffect::keyEvent);
0467     effects->grabKeyboard(effect.get());
0468 
0469     quint32 timestamp = 1;
0470     KEYPRESS(KEY_A);
0471     QCOMPARE(inputSpy.count(), 1);
0472     QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
0473     KEYRELEASE(KEY_A);
0474     QCOMPARE(inputSpy.count(), 2);
0475     QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
0476     QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a"));
0477 
0478     LOCK;
0479     KEYPRESS(KEY_B);
0480     QCOMPARE(inputSpy.count(), 2);
0481     KEYRELEASE(KEY_B);
0482     QCOMPARE(inputSpy.count(), 2);
0483 
0484     UNLOCK;
0485     KEYPRESS(KEY_C);
0486     QCOMPARE(inputSpy.count(), 3);
0487     QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
0488     QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a"));
0489     QCOMPARE(inputSpy.at(2).first().toString(), QStringLiteral("c"));
0490     KEYRELEASE(KEY_C);
0491     QCOMPARE(inputSpy.count(), 4);
0492     QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
0493     QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a"));
0494     QCOMPARE(inputSpy.at(2).first().toString(), QStringLiteral("c"));
0495     QCOMPARE(inputSpy.at(3).first().toString(), QStringLiteral("c"));
0496 
0497     effects->ungrabKeyboard();
0498 }
0499 
0500 void LockScreenTest::testEffectsKeyboardAutorepeat()
0501 {
0502     // this test is just like testEffectsKeyboard, but tests auto repeat key events
0503     // while the key is pressed the Effect should get auto repeated events
0504     // but the lock screen should filter them out
0505     std::unique_ptr<HelperEffect> effect(new HelperEffect);
0506     QSignalSpy inputSpy(effect.get(), &HelperEffect::keyEvent);
0507     effects->grabKeyboard(effect.get());
0508 
0509     // we need to configure the key repeat first. It is only enabled on libinput
0510     waylandServer()->seat()->keyboard()->setRepeatInfo(25, 300);
0511 
0512     quint32 timestamp = 1;
0513     KEYPRESS(KEY_A);
0514     QCOMPARE(inputSpy.count(), 1);
0515     QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
0516     QVERIFY(inputSpy.wait());
0517     QVERIFY(inputSpy.count() > 1);
0518     // and still more events
0519     QVERIFY(inputSpy.wait());
0520     QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a"));
0521 
0522     // now release
0523     inputSpy.clear();
0524     KEYRELEASE(KEY_A);
0525     QCOMPARE(inputSpy.count(), 1);
0526 
0527     // while locked key repeat should not pass any events to the Effect
0528     LOCK;
0529     KEYPRESS(KEY_B);
0530     QVERIFY(!inputSpy.wait(10));
0531     KEYRELEASE(KEY_B);
0532     QVERIFY(!inputSpy.wait(10));
0533 
0534     UNLOCK;
0535     // don't test again, that's covered by testEffectsKeyboard
0536 
0537     effects->ungrabKeyboard();
0538 }
0539 
0540 void LockScreenTest::testMoveWindow()
0541 {
0542     auto [window, surface] = showWindow();
0543     QVERIFY(window);
0544     QSignalSpy clientStepUserMovedResizedSpy(window, &Window::clientStepUserMovedResized);
0545     quint32 timestamp = 1;
0546 
0547     workspace()->slotWindowMove();
0548     QCOMPARE(workspace()->moveResizeWindow(), window);
0549     QVERIFY(window->isInteractiveMove());
0550     Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
0551     Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
0552     QEXPECT_FAIL("", "First event is ignored", Continue);
0553     QCOMPARE(clientStepUserMovedResizedSpy.count(), 1);
0554 
0555     // TODO adjust once the expected fail is fixed
0556     Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
0557     Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
0558     QCOMPARE(clientStepUserMovedResizedSpy.count(), 1);
0559 
0560     // while locking our window should continue to be in move resize
0561     LOCK;
0562     QCOMPARE(workspace()->moveResizeWindow(), window);
0563     QVERIFY(window->isInteractiveMove());
0564     Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
0565     Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
0566     QCOMPARE(clientStepUserMovedResizedSpy.count(), 1);
0567 
0568     UNLOCK;
0569     QCOMPARE(workspace()->moveResizeWindow(), window);
0570     QVERIFY(window->isInteractiveMove());
0571     Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
0572     Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
0573     QCOMPARE(clientStepUserMovedResizedSpy.count(), 2);
0574     Test::keyboardKeyPressed(KEY_ESC, timestamp++);
0575     Test::keyboardKeyReleased(KEY_ESC, timestamp++);
0576     QVERIFY(!window->isInteractiveMove());
0577 }
0578 
0579 void LockScreenTest::testPointerShortcut()
0580 {
0581     std::unique_ptr<QAction> action(new QAction(nullptr));
0582     QSignalSpy actionSpy(action.get(), &QAction::triggered);
0583     input()->registerPointerShortcut(Qt::MetaModifier, Qt::LeftButton, action.get());
0584 
0585     // try to trigger the shortcut
0586     quint32 timestamp = 1;
0587 #define PERFORM(expectedCount)                                \
0588     do {                                                      \
0589         Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++);  \
0590         PRESS;                                                \
0591         QCoreApplication::instance()->processEvents();        \
0592         QCOMPARE(actionSpy.count(), expectedCount);           \
0593         RELEASE;                                              \
0594         Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++); \
0595         QCoreApplication::instance()->processEvents();        \
0596         QCOMPARE(actionSpy.count(), expectedCount);           \
0597     } while (false)
0598 
0599     PERFORM(1);
0600 
0601     // now the same thing with a locked screen
0602     LOCK;
0603     PERFORM(1);
0604 
0605     // and as unlocked
0606     UNLOCK;
0607     PERFORM(2);
0608 #undef PERFORM
0609 }
0610 
0611 void LockScreenTest::testAxisShortcut_data()
0612 {
0613     QTest::addColumn<Qt::Orientation>("direction");
0614     QTest::addColumn<int>("sign");
0615 
0616     QTest::newRow("up") << Qt::Vertical << 1;
0617     QTest::newRow("down") << Qt::Vertical << -1;
0618     QTest::newRow("left") << Qt::Horizontal << 1;
0619     QTest::newRow("right") << Qt::Horizontal << -1;
0620 }
0621 
0622 void LockScreenTest::testAxisShortcut()
0623 {
0624     std::unique_ptr<QAction> action(new QAction(nullptr));
0625     QSignalSpy actionSpy(action.get(), &QAction::triggered);
0626     QFETCH(Qt::Orientation, direction);
0627     QFETCH(int, sign);
0628     PointerAxisDirection axisDirection = PointerAxisUp;
0629     if (direction == Qt::Vertical) {
0630         axisDirection = sign > 0 ? PointerAxisUp : PointerAxisDown;
0631     } else {
0632         axisDirection = sign > 0 ? PointerAxisLeft : PointerAxisRight;
0633     }
0634     input()->registerAxisShortcut(Qt::MetaModifier, axisDirection, action.get());
0635 
0636     // try to trigger the shortcut
0637     quint32 timestamp = 1;
0638 #define PERFORM(expectedCount)                                    \
0639     do {                                                          \
0640         Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++);      \
0641         if (direction == Qt::Vertical)                            \
0642             Test::pointerAxisVertical(sign * 5.0, timestamp++);   \
0643         else                                                      \
0644             Test::pointerAxisHorizontal(sign * 5.0, timestamp++); \
0645         QCoreApplication::instance()->processEvents();            \
0646         QCOMPARE(actionSpy.count(), expectedCount);               \
0647         Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++);     \
0648         QCoreApplication::instance()->processEvents();            \
0649         QCOMPARE(actionSpy.count(), expectedCount);               \
0650     } while (false)
0651 
0652     PERFORM(1);
0653 
0654     // now the same thing with a locked screen
0655     LOCK;
0656     PERFORM(1);
0657 
0658     // and as unlocked
0659     UNLOCK;
0660     PERFORM(2);
0661 #undef PERFORM
0662 }
0663 
0664 void LockScreenTest::testKeyboardShortcut()
0665 {
0666     std::unique_ptr<QAction> action(new QAction(nullptr));
0667     QSignalSpy actionSpy(action.get(), &QAction::triggered);
0668     action->setProperty("componentName", QStringLiteral("kwin"));
0669     action->setObjectName("LockScreenTest::testKeyboardShortcut");
0670     KGlobalAccel::self()->setDefaultShortcut(action.get(), QList<QKeySequence>{Qt::CTRL | Qt::META | Qt::ALT | Qt::Key_Space});
0671     KGlobalAccel::self()->setShortcut(action.get(), QList<QKeySequence>{Qt::CTRL | Qt::META | Qt::ALT | Qt::Key_Space},
0672                                       KGlobalAccel::NoAutoloading);
0673 
0674     // try to trigger the shortcut
0675     quint32 timestamp = 1;
0676     KEYPRESS(KEY_LEFTCTRL);
0677     KEYPRESS(KEY_LEFTMETA);
0678     KEYPRESS(KEY_LEFTALT);
0679     KEYPRESS(KEY_SPACE);
0680     QVERIFY(actionSpy.wait());
0681     QCOMPARE(actionSpy.count(), 1);
0682     KEYRELEASE(KEY_SPACE);
0683     QVERIFY(!actionSpy.wait(10));
0684     QCOMPARE(actionSpy.count(), 1);
0685 
0686     LOCK;
0687     KEYPRESS(KEY_SPACE);
0688     QVERIFY(!actionSpy.wait(10));
0689     QCOMPARE(actionSpy.count(), 1);
0690     KEYRELEASE(KEY_SPACE);
0691     QVERIFY(!actionSpy.wait(10));
0692     QCOMPARE(actionSpy.count(), 1);
0693 
0694     UNLOCK;
0695     KEYPRESS(KEY_SPACE);
0696     QVERIFY(actionSpy.wait());
0697     QCOMPARE(actionSpy.count(), 2);
0698     KEYRELEASE(KEY_SPACE);
0699     QVERIFY(!actionSpy.wait(10));
0700     QCOMPARE(actionSpy.count(), 2);
0701     KEYRELEASE(KEY_LEFTCTRL);
0702     KEYRELEASE(KEY_LEFTMETA);
0703     KEYRELEASE(KEY_LEFTALT);
0704 }
0705 
0706 void LockScreenTest::testTouch()
0707 {
0708     auto touch = m_seat->createTouch(m_seat);
0709     QVERIFY(touch);
0710     QVERIFY(touch->isValid());
0711     auto [window, surface] = showWindow();
0712     QVERIFY(window);
0713     QSignalSpy sequenceStartedSpy(touch, &KWayland::Client::Touch::sequenceStarted);
0714     QSignalSpy cancelSpy(touch, &KWayland::Client::Touch::sequenceCanceled);
0715     QSignalSpy pointRemovedSpy(touch, &KWayland::Client::Touch::pointRemoved);
0716 
0717     quint32 timestamp = 1;
0718     Test::touchDown(1, QPointF(25, 25), timestamp++);
0719     QVERIFY(sequenceStartedSpy.wait());
0720     QCOMPARE(sequenceStartedSpy.count(), 1);
0721 
0722     LOCK;
0723     QVERIFY(cancelSpy.wait());
0724 
0725     Test::touchUp(1, timestamp++);
0726     QVERIFY(!pointRemovedSpy.wait(10));
0727     Test::touchDown(1, QPointF(25, 25), timestamp++);
0728     Test::touchMotion(1, QPointF(26, 26), timestamp++);
0729     Test::touchUp(1, timestamp++);
0730 
0731     UNLOCK;
0732     Test::touchDown(1, QPointF(25, 25), timestamp++);
0733     QVERIFY(sequenceStartedSpy.wait());
0734     QCOMPARE(sequenceStartedSpy.count(), 2);
0735     Test::touchUp(1, timestamp++);
0736     QVERIFY(pointRemovedSpy.wait());
0737     QCOMPARE(pointRemovedSpy.count(), 1);
0738 }
0739 
0740 }
0741 
0742 WAYLANDTEST_MAIN(KWin::LockScreenTest)
0743 #include "lockscreen.moc"