File indexing completed on 2024-05-12 05:30:36

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 "compositor.h"
0012 #include "core/output.h"
0013 #include "core/renderbackend.h"
0014 #include "effect/effecthandler.h"
0015 #include "pointer_input.h"
0016 #include "screenedge.h"
0017 #include "wayland/keyboard.h"
0018 #include "wayland/seat.h"
0019 #include "wayland_server.h"
0020 #include "window.h"
0021 #include "workspace.h"
0022 
0023 #include <KWayland/Client/compositor.h>
0024 #include <KWayland/Client/connection_thread.h>
0025 #include <KWayland/Client/keyboard.h>
0026 #include <KWayland/Client/pointer.h>
0027 #include <KWayland/Client/registry.h>
0028 #include <KWayland/Client/seat.h>
0029 #include <KWayland/Client/shm_pool.h>
0030 #include <KWayland/Client/surface.h>
0031 #include <KWayland/Client/touch.h>
0032 
0033 // screenlocker
0034 #include <KScreenLocker/KsldApp>
0035 
0036 #include <KGlobalAccel>
0037 
0038 #include <QAction>
0039 #include <QSignalSpy>
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     Test::setOutputConfig({
0183         QRect(0, 0, 1280, 1024),
0184         QRect(1280, 0, 1280, 1024),
0185     });
0186 
0187     kwinApp()->start();
0188     QVERIFY(applicationStartedSpy.wait());
0189     const auto outputs = workspace()->outputs();
0190     QCOMPARE(outputs.count(), 2);
0191     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0192     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0193     setenv("QT_QPA_PLATFORM", "wayland", true);
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     input()->pointer()->warp(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(), OverlayLayer);
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 class TestObject : public QObject
0404 {
0405     Q_OBJECT
0406 
0407 public Q_SLOTS:
0408     bool callback(ElectricBorder border)
0409     {
0410         return true;
0411     }
0412 };
0413 
0414 void LockScreenTest::testScreenEdge()
0415 {
0416     QSignalSpy screenEdgeSpy(workspace()->screenEdges(), &ScreenEdges::approaching);
0417     QCOMPARE(screenEdgeSpy.count(), 0);
0418 
0419     TestObject callback;
0420     workspace()->screenEdges()->reserve(ElectricTopLeft, &callback, "callback");
0421 
0422     quint32 timestamp = 1;
0423     MOTION(QPoint(5, 5));
0424     QCOMPARE(screenEdgeSpy.count(), 1);
0425 
0426     LOCK;
0427 
0428     MOTION(QPoint(4, 4));
0429     QCOMPARE(screenEdgeSpy.count(), 1);
0430 
0431     // and unlock
0432     UNLOCK;
0433 
0434     MOTION(QPoint(5, 5));
0435     QCOMPARE(screenEdgeSpy.count(), 2);
0436 }
0437 
0438 void LockScreenTest::testEffects()
0439 {
0440     std::unique_ptr<HelperEffect> effect(new HelperEffect);
0441     QSignalSpy inputSpy(effect.get(), &HelperEffect::inputEvent);
0442     effects->startMouseInterception(effect.get(), Qt::ArrowCursor);
0443 
0444     quint32 timestamp = 1;
0445     QCOMPARE(inputSpy.count(), 0);
0446     MOTION(QPoint(5, 5));
0447     QCOMPARE(inputSpy.count(), 1);
0448     // simlate click
0449     PRESS;
0450     QCOMPARE(inputSpy.count(), 2);
0451     RELEASE;
0452     QCOMPARE(inputSpy.count(), 3);
0453 
0454     LOCK;
0455 
0456     MOTION(QPoint(6, 6));
0457     QCOMPARE(inputSpy.count(), 3);
0458     // simlate click
0459     PRESS;
0460     QCOMPARE(inputSpy.count(), 3);
0461     RELEASE;
0462     QCOMPARE(inputSpy.count(), 3);
0463 
0464     UNLOCK;
0465 
0466     MOTION(QPoint(5, 5));
0467     QCOMPARE(inputSpy.count(), 4);
0468     // simlate click
0469     PRESS;
0470     QCOMPARE(inputSpy.count(), 5);
0471     RELEASE;
0472     QCOMPARE(inputSpy.count(), 6);
0473 
0474     effects->stopMouseInterception(effect.get());
0475 }
0476 
0477 void LockScreenTest::testEffectsKeyboard()
0478 {
0479     std::unique_ptr<HelperEffect> effect(new HelperEffect);
0480     QSignalSpy inputSpy(effect.get(), &HelperEffect::keyEvent);
0481     effects->grabKeyboard(effect.get());
0482 
0483     quint32 timestamp = 1;
0484     KEYPRESS(KEY_A);
0485     QCOMPARE(inputSpy.count(), 1);
0486     QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
0487     KEYRELEASE(KEY_A);
0488     QCOMPARE(inputSpy.count(), 2);
0489     QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
0490     QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a"));
0491 
0492     LOCK;
0493     KEYPRESS(KEY_B);
0494     QCOMPARE(inputSpy.count(), 2);
0495     KEYRELEASE(KEY_B);
0496     QCOMPARE(inputSpy.count(), 2);
0497 
0498     UNLOCK;
0499     KEYPRESS(KEY_C);
0500     QCOMPARE(inputSpy.count(), 3);
0501     QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
0502     QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a"));
0503     QCOMPARE(inputSpy.at(2).first().toString(), QStringLiteral("c"));
0504     KEYRELEASE(KEY_C);
0505     QCOMPARE(inputSpy.count(), 4);
0506     QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
0507     QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a"));
0508     QCOMPARE(inputSpy.at(2).first().toString(), QStringLiteral("c"));
0509     QCOMPARE(inputSpy.at(3).first().toString(), QStringLiteral("c"));
0510 
0511     effects->ungrabKeyboard();
0512 }
0513 
0514 void LockScreenTest::testEffectsKeyboardAutorepeat()
0515 {
0516     // this test is just like testEffectsKeyboard, but tests auto repeat key events
0517     // while the key is pressed the Effect should get auto repeated events
0518     // but the lock screen should filter them out
0519     std::unique_ptr<HelperEffect> effect(new HelperEffect);
0520     QSignalSpy inputSpy(effect.get(), &HelperEffect::keyEvent);
0521     effects->grabKeyboard(effect.get());
0522 
0523     // we need to configure the key repeat first. It is only enabled on libinput
0524     waylandServer()->seat()->keyboard()->setRepeatInfo(25, 300);
0525 
0526     quint32 timestamp = 1;
0527     KEYPRESS(KEY_A);
0528     QCOMPARE(inputSpy.count(), 1);
0529     QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
0530     QVERIFY(inputSpy.wait());
0531     QVERIFY(inputSpy.count() > 1);
0532     // and still more events
0533     QVERIFY(inputSpy.wait());
0534     QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a"));
0535 
0536     // now release
0537     inputSpy.clear();
0538     KEYRELEASE(KEY_A);
0539     QCOMPARE(inputSpy.count(), 1);
0540 
0541     // while locked key repeat should not pass any events to the Effect
0542     LOCK;
0543     KEYPRESS(KEY_B);
0544     QVERIFY(!inputSpy.wait(10));
0545     KEYRELEASE(KEY_B);
0546     QVERIFY(!inputSpy.wait(10));
0547 
0548     UNLOCK;
0549     // don't test again, that's covered by testEffectsKeyboard
0550 
0551     effects->ungrabKeyboard();
0552 }
0553 
0554 void LockScreenTest::testMoveWindow()
0555 {
0556     auto [window, surface] = showWindow();
0557     QVERIFY(window);
0558     QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
0559     quint32 timestamp = 1;
0560 
0561     workspace()->slotWindowMove();
0562     QCOMPARE(workspace()->moveResizeWindow(), window);
0563     QVERIFY(window->isInteractiveMove());
0564     Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
0565     Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
0566     QEXPECT_FAIL("", "First event is ignored", Continue);
0567     QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
0568 
0569     // TODO adjust once the expected fail is fixed
0570     Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
0571     Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
0572     QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
0573 
0574     // while locking our window should continue to be in move resize
0575     LOCK;
0576     QCOMPARE(workspace()->moveResizeWindow(), window);
0577     QVERIFY(window->isInteractiveMove());
0578     Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
0579     Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
0580     QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
0581 
0582     UNLOCK;
0583     QCOMPARE(workspace()->moveResizeWindow(), window);
0584     QVERIFY(window->isInteractiveMove());
0585     Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
0586     Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
0587     QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
0588     Test::keyboardKeyPressed(KEY_ESC, timestamp++);
0589     Test::keyboardKeyReleased(KEY_ESC, timestamp++);
0590     QVERIFY(!window->isInteractiveMove());
0591 }
0592 
0593 void LockScreenTest::testPointerShortcut()
0594 {
0595 #if !KWIN_BUILD_GLOBALSHORTCUTS
0596     QSKIP("Can't test shortcuts without shortcuts");
0597     return;
0598 #endif
0599 
0600     std::unique_ptr<QAction> action(new QAction(nullptr));
0601     QSignalSpy actionSpy(action.get(), &QAction::triggered);
0602     input()->registerPointerShortcut(Qt::MetaModifier, Qt::LeftButton, action.get());
0603 
0604     // try to trigger the shortcut
0605     quint32 timestamp = 1;
0606 #define PERFORM(expectedCount)                                \
0607     do {                                                      \
0608         Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++);  \
0609         PRESS;                                                \
0610         QCoreApplication::instance()->processEvents();        \
0611         QCOMPARE(actionSpy.count(), expectedCount);           \
0612         RELEASE;                                              \
0613         Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++); \
0614         QCoreApplication::instance()->processEvents();        \
0615         QCOMPARE(actionSpy.count(), expectedCount);           \
0616     } while (false)
0617 
0618     PERFORM(1);
0619 
0620     // now the same thing with a locked screen
0621     LOCK;
0622     PERFORM(1);
0623 
0624     // and as unlocked
0625     UNLOCK;
0626     PERFORM(2);
0627 #undef PERFORM
0628 }
0629 
0630 void LockScreenTest::testAxisShortcut_data()
0631 {
0632 #if !KWIN_BUILD_GLOBALSHORTCUTS
0633     QSKIP("Can't test shortcuts without shortcuts");
0634     return;
0635 #endif
0636 
0637     QTest::addColumn<Qt::Orientation>("direction");
0638     QTest::addColumn<int>("sign");
0639 
0640     QTest::newRow("up") << Qt::Vertical << 1;
0641     QTest::newRow("down") << Qt::Vertical << -1;
0642     QTest::newRow("left") << Qt::Horizontal << 1;
0643     QTest::newRow("right") << Qt::Horizontal << -1;
0644 }
0645 
0646 void LockScreenTest::testAxisShortcut()
0647 {
0648     std::unique_ptr<QAction> action(new QAction(nullptr));
0649     QSignalSpy actionSpy(action.get(), &QAction::triggered);
0650     QFETCH(Qt::Orientation, direction);
0651     QFETCH(int, sign);
0652     PointerAxisDirection axisDirection = PointerAxisUp;
0653     if (direction == Qt::Vertical) {
0654         axisDirection = sign > 0 ? PointerAxisUp : PointerAxisDown;
0655     } else {
0656         axisDirection = sign > 0 ? PointerAxisLeft : PointerAxisRight;
0657     }
0658     input()->registerAxisShortcut(Qt::MetaModifier, axisDirection, action.get());
0659 
0660     // try to trigger the shortcut
0661     quint32 timestamp = 1;
0662 #define PERFORM(expectedCount)                                    \
0663     do {                                                          \
0664         Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++);      \
0665         if (direction == Qt::Vertical)                            \
0666             Test::pointerAxisVertical(sign * 5.0, timestamp++);   \
0667         else                                                      \
0668             Test::pointerAxisHorizontal(sign * 5.0, timestamp++); \
0669         QCoreApplication::instance()->processEvents();            \
0670         QCOMPARE(actionSpy.count(), expectedCount);               \
0671         Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++);     \
0672         QCoreApplication::instance()->processEvents();            \
0673         QCOMPARE(actionSpy.count(), expectedCount);               \
0674     } while (false)
0675 
0676     PERFORM(1);
0677 
0678     // now the same thing with a locked screen
0679     LOCK;
0680     PERFORM(1);
0681 
0682     // and as unlocked
0683     UNLOCK;
0684     PERFORM(2);
0685 #undef PERFORM
0686 }
0687 
0688 void LockScreenTest::testKeyboardShortcut()
0689 {
0690 #if !KWIN_BUILD_GLOBALSHORTCUTS
0691     QSKIP("Can't test shortcuts without shortcuts");
0692     return;
0693 #endif
0694 
0695     std::unique_ptr<QAction> action(new QAction(nullptr));
0696     QSignalSpy actionSpy(action.get(), &QAction::triggered);
0697     action->setProperty("componentName", QStringLiteral("kwin"));
0698     action->setObjectName("LockScreenTest::testKeyboardShortcut");
0699     KGlobalAccel::self()->setDefaultShortcut(action.get(), QList<QKeySequence>{Qt::CTRL | Qt::META | Qt::ALT | Qt::Key_Space});
0700     KGlobalAccel::self()->setShortcut(action.get(), QList<QKeySequence>{Qt::CTRL | Qt::META | Qt::ALT | Qt::Key_Space},
0701                                       KGlobalAccel::NoAutoloading);
0702 
0703     // try to trigger the shortcut
0704     quint32 timestamp = 1;
0705     KEYPRESS(KEY_LEFTCTRL);
0706     KEYPRESS(KEY_LEFTMETA);
0707     KEYPRESS(KEY_LEFTALT);
0708     KEYPRESS(KEY_SPACE);
0709     QVERIFY(actionSpy.wait());
0710     QCOMPARE(actionSpy.count(), 1);
0711     KEYRELEASE(KEY_SPACE);
0712     QVERIFY(!actionSpy.wait(10));
0713     QCOMPARE(actionSpy.count(), 1);
0714 
0715     LOCK;
0716     KEYPRESS(KEY_SPACE);
0717     QVERIFY(!actionSpy.wait(10));
0718     QCOMPARE(actionSpy.count(), 1);
0719     KEYRELEASE(KEY_SPACE);
0720     QVERIFY(!actionSpy.wait(10));
0721     QCOMPARE(actionSpy.count(), 1);
0722 
0723     UNLOCK;
0724     KEYPRESS(KEY_SPACE);
0725     QVERIFY(actionSpy.wait());
0726     QCOMPARE(actionSpy.count(), 2);
0727     KEYRELEASE(KEY_SPACE);
0728     QVERIFY(!actionSpy.wait(10));
0729     QCOMPARE(actionSpy.count(), 2);
0730     KEYRELEASE(KEY_LEFTCTRL);
0731     KEYRELEASE(KEY_LEFTMETA);
0732     KEYRELEASE(KEY_LEFTALT);
0733 }
0734 
0735 void LockScreenTest::testTouch()
0736 {
0737     auto touch = m_seat->createTouch(m_seat);
0738     QVERIFY(touch);
0739     QVERIFY(touch->isValid());
0740     auto [window, surface] = showWindow();
0741     QVERIFY(window);
0742     QSignalSpy sequenceStartedSpy(touch, &KWayland::Client::Touch::sequenceStarted);
0743     QSignalSpy cancelSpy(touch, &KWayland::Client::Touch::sequenceCanceled);
0744     QSignalSpy pointRemovedSpy(touch, &KWayland::Client::Touch::pointRemoved);
0745 
0746     quint32 timestamp = 1;
0747     Test::touchDown(1, QPointF(25, 25), timestamp++);
0748     QVERIFY(sequenceStartedSpy.wait());
0749     QCOMPARE(sequenceStartedSpy.count(), 1);
0750 
0751     LOCK;
0752     QVERIFY(cancelSpy.wait());
0753 
0754     Test::touchUp(1, timestamp++);
0755     QVERIFY(!pointRemovedSpy.wait(10));
0756     Test::touchDown(1, QPointF(25, 25), timestamp++);
0757     Test::touchMotion(1, QPointF(26, 26), timestamp++);
0758     Test::touchUp(1, timestamp++);
0759 
0760     UNLOCK;
0761     Test::touchDown(1, QPointF(25, 25), timestamp++);
0762     QVERIFY(sequenceStartedSpy.wait());
0763     QCOMPARE(sequenceStartedSpy.count(), 2);
0764     Test::touchUp(1, timestamp++);
0765     QVERIFY(pointRemovedSpy.wait());
0766     QCOMPARE(pointRemovedSpy.count(), 1);
0767 }
0768 
0769 }
0770 
0771 WAYLANDTEST_MAIN(KWin::LockScreenTest)
0772 #include "lockscreen.moc"