KWin
Loading...
Searching...
No Matches
lockscreen.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "kwin_wayland_test.h"
10
11#include "compositor.h"
12#include "core/output.h"
13#include "core/renderbackend.h"
15#include "pointer_input.h"
16#include "screenedge.h"
17#include "wayland/keyboard.h"
18#include "wayland/seat.h"
19#include "wayland_server.h"
20#include "window.h"
21#include "workspace.h"
22
23#include <KWayland/Client/compositor.h>
24#include <KWayland/Client/connection_thread.h>
25#include <KWayland/Client/keyboard.h>
26#include <KWayland/Client/pointer.h>
27#include <KWayland/Client/registry.h>
28#include <KWayland/Client/seat.h>
29#include <KWayland/Client/shm_pool.h>
30#include <KWayland/Client/surface.h>
31#include <KWayland/Client/touch.h>
32
33// screenlocker
34#include <KScreenLocker/KsldApp>
35
36#include <KGlobalAccel>
37
38#include <QAction>
39#include <QSignalSpy>
40
41#include <linux/input.h>
42
43Q_DECLARE_METATYPE(Qt::Orientation)
44
45namespace KWin
46{
47
48static const QString s_socketName = QStringLiteral("wayland_test_kwin_lock_screen-0");
49
50class LockScreenTest : public QObject
51{
52 Q_OBJECT
53private Q_SLOTS:
54 void initTestCase();
55 void init();
56 void cleanup();
57 void testStackingOrder();
58 void testPointer();
59 void testPointerButton();
60 void testPointerAxis();
61 void testKeyboard();
62 void testScreenEdge();
63 void testEffects();
64 void testEffectsKeyboard();
65 void testEffectsKeyboardAutorepeat();
66 void testMoveWindow();
67 void testPointerShortcut();
68 void testAxisShortcut_data();
69 void testAxisShortcut();
70 void testKeyboardShortcut();
71 void testTouch();
72
73private:
74 void unlock();
75 std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> showWindow();
76 KWayland::Client::ConnectionThread *m_connection = nullptr;
77 KWayland::Client::Compositor *m_compositor = nullptr;
78 KWayland::Client::Seat *m_seat = nullptr;
79 KWayland::Client::ShmPool *m_shm = nullptr;
80};
81
82class HelperEffect : public Effect
83{
84 Q_OBJECT
85public:
87 {
88 }
89 ~HelperEffect() override
90 {
91 }
92
93 void windowInputMouseEvent(QEvent *) override
94 {
95 Q_EMIT inputEvent();
96 }
97 void grabbedKeyboardEvent(QKeyEvent *e) override
98 {
99 Q_EMIT keyEvent(e->text());
100 }
101
102Q_SIGNALS:
104 void keyEvent(const QString &);
105};
106
107#define LOCK \
108 do { \
109 QVERIFY(!waylandServer()->isScreenLocked()); \
110 QSignalSpy lockStateChangedSpy(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged); \
111 ScreenLocker::KSldApp::self()->lock(ScreenLocker::EstablishLock::Immediate); \
112 QTRY_COMPARE(ScreenLocker::KSldApp::self()->lockState(), ScreenLocker::KSldApp::Locked); \
113 QVERIFY(waylandServer()->isScreenLocked()); \
114 } while (false)
115
116#define UNLOCK \
117 do { \
118 QSignalSpy lockStateChangedSpy(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged); \
119 unlock(); \
120 if (lockStateChangedSpy.count() != 1) { \
121 QVERIFY(lockStateChangedSpy.wait()); \
122 } \
123 QCOMPARE(lockStateChangedSpy.count(), 1); \
124 QVERIFY(!waylandServer()->isScreenLocked()); \
125 } while (false)
126
127#define MOTION(target) Test::pointerMotion(target, timestamp++)
128
129#define PRESS Test::pointerButtonPressed(BTN_LEFT, timestamp++)
130
131#define RELEASE Test::pointerButtonReleased(BTN_LEFT, timestamp++)
132
133#define KEYPRESS(key) Test::keyboardKeyPressed(key, timestamp++)
134
135#define KEYRELEASE(key) Test::keyboardKeyReleased(key, timestamp++)
136
137void LockScreenTest::unlock()
138{
139 using namespace ScreenLocker;
140 const auto children = KSldApp::self()->children();
141 for (auto it = children.begin(); it != children.end(); ++it) {
142 if (qstrcmp((*it)->metaObject()->className(), "LogindIntegration") != 0) {
143 continue;
144 }
145 QMetaObject::invokeMethod(*it, "requestUnlock");
146 break;
147 }
148}
149
150std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> LockScreenTest::showWindow()
151{
152#define VERIFY(statement) \
153 if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) \
154 return {nullptr, nullptr};
155#define COMPARE(actual, expected) \
156 if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
157 return {nullptr, nullptr};
158
159 std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
160 VERIFY(surface.get());
161 Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
162 VERIFY(shellSurface);
163 // let's render
164 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
165
166 VERIFY(window);
167 COMPARE(workspace()->activeWindow(), window);
168
169#undef VERIFY
170#undef COMPARE
171
172 return {window, std::move(surface)};
173}
174
175void LockScreenTest::initTestCase()
176{
177 qRegisterMetaType<KWin::Window *>();
178 qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
179
180 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
181 QVERIFY(waylandServer()->init(s_socketName));
183 QRect(0, 0, 1280, 1024),
184 QRect(1280, 0, 1280, 1024),
185 });
186
187 kwinApp()->start();
188 QVERIFY(applicationStartedSpy.wait());
189 const auto outputs = workspace()->outputs();
190 QCOMPARE(outputs.count(), 2);
191 QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
192 QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
193 setenv("QT_QPA_PLATFORM", "wayland", true);
194}
195
196void LockScreenTest::init()
197{
200 m_connection = Test::waylandConnection();
201 m_compositor = Test::waylandCompositor();
202 m_shm = Test::waylandShmPool();
203 m_seat = Test::waylandSeat();
204
205 workspace()->setActiveOutput(QPoint(640, 512));
206 input()->pointer()->warp(QPoint(640, 512));
207}
208
209void LockScreenTest::cleanup()
210{
212}
213
214void LockScreenTest::testStackingOrder()
215{
216 // This test verifies that the lockscreen greeter is placed above other windows.
217
218 QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
219
220 LOCK;
221 QVERIFY(windowAddedSpy.wait());
222
223 Window *window = windowAddedSpy.first().first().value<Window *>();
224 QVERIFY(window);
225 QVERIFY(window->isLockScreen());
226 QCOMPARE(window->layer(), OverlayLayer);
227
228 UNLOCK;
229}
230
231void LockScreenTest::testPointer()
232{
233 std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
234 QVERIFY(pointer != nullptr);
235 QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
236 QSignalSpy leftSpy(pointer.get(), &KWayland::Client::Pointer::left);
237
238 auto [window, surface] = showWindow();
239 QVERIFY(window);
240
241 // first move cursor into the center of the window
242 quint32 timestamp = 1;
243 MOTION(window->frameGeometry().center());
244 QVERIFY(enteredSpy.wait());
245
246 LOCK;
247
248 QVERIFY(leftSpy.wait());
249 QCOMPARE(leftSpy.count(), 1);
250
251 // simulate moving out in and out again
252 MOTION(window->frameGeometry().center());
253 MOTION(window->frameGeometry().bottomRight() + QPoint(100, 100));
254 MOTION(window->frameGeometry().bottomRight() + QPoint(100, 100));
255 QVERIFY(!leftSpy.wait(10));
256 QCOMPARE(leftSpy.count(), 1);
257 QCOMPARE(enteredSpy.count(), 1);
258
259 // go back on the window
260 MOTION(window->frameGeometry().center());
261 // and unlock
262 UNLOCK;
263
264 QVERIFY(enteredSpy.wait());
265 QCOMPARE(enteredSpy.count(), 2);
266 // move on the window
267 MOTION(window->frameGeometry().center() + QPoint(100, 100));
268 QVERIFY(leftSpy.wait());
269 MOTION(window->frameGeometry().center());
270 QVERIFY(enteredSpy.wait());
271 QCOMPARE(enteredSpy.count(), 3);
272}
273
274void LockScreenTest::testPointerButton()
275{
276 std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
277 QVERIFY(pointer != nullptr);
278 QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
279 QSignalSpy buttonChangedSpy(pointer.get(), &KWayland::Client::Pointer::buttonStateChanged);
280
281 auto [window, surface] = showWindow();
282 QVERIFY(window);
283
284 // first move cursor into the center of the window
285 quint32 timestamp = 1;
286 MOTION(window->frameGeometry().center());
287 QVERIFY(enteredSpy.wait());
288 // and simulate a click
289 PRESS;
290 QVERIFY(buttonChangedSpy.wait());
291 RELEASE;
292 QVERIFY(buttonChangedSpy.wait());
293
294 LOCK;
295
296 // and simulate a click
297 PRESS;
298 QVERIFY(!buttonChangedSpy.wait(10));
299 RELEASE;
300 QVERIFY(!buttonChangedSpy.wait(10));
301
302 UNLOCK;
303 QVERIFY(enteredSpy.wait());
304 QCOMPARE(enteredSpy.count(), 2);
305
306 // and click again
307 PRESS;
308 QVERIFY(buttonChangedSpy.wait());
309 RELEASE;
310 QVERIFY(buttonChangedSpy.wait());
311}
312
313void LockScreenTest::testPointerAxis()
314{
315 std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
316 QVERIFY(pointer != nullptr);
317 QSignalSpy axisChangedSpy(pointer.get(), &KWayland::Client::Pointer::axisChanged);
318 QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
319
320 auto [window, surface] = showWindow();
321 QVERIFY(window);
322
323 // first move cursor into the center of the window
324 quint32 timestamp = 1;
325 MOTION(window->frameGeometry().center());
326 QVERIFY(enteredSpy.wait());
327 // and simulate axis
328 Test::pointerAxisHorizontal(5.0, timestamp++);
329 QVERIFY(axisChangedSpy.wait());
330
331 LOCK;
332
333 // and simulate axis
334 Test::pointerAxisHorizontal(5.0, timestamp++);
335 QVERIFY(!axisChangedSpy.wait(10));
336 Test::pointerAxisVertical(5.0, timestamp++);
337 QVERIFY(!axisChangedSpy.wait(10));
338
339 // and unlock
340 UNLOCK;
341 QVERIFY(enteredSpy.wait());
342 QCOMPARE(enteredSpy.count(), 2);
343
344 // and move axis again
345 Test::pointerAxisHorizontal(5.0, timestamp++);
346 QVERIFY(axisChangedSpy.wait());
347 Test::pointerAxisVertical(5.0, timestamp++);
348 QVERIFY(axisChangedSpy.wait());
349}
350
351void LockScreenTest::testKeyboard()
352{
353 std::unique_ptr<KWayland::Client::Keyboard> keyboard(m_seat->createKeyboard());
354 QVERIFY(keyboard != nullptr);
355 QSignalSpy enteredSpy(keyboard.get(), &KWayland::Client::Keyboard::entered);
356 QSignalSpy leftSpy(keyboard.get(), &KWayland::Client::Keyboard::left);
357 QSignalSpy keyChangedSpy(keyboard.get(), &KWayland::Client::Keyboard::keyChanged);
358
359 auto [window, surface] = showWindow();
360 QVERIFY(window);
361 QVERIFY(enteredSpy.wait());
362 QTRY_COMPARE(enteredSpy.count(), 1);
363
364 quint32 timestamp = 1;
365 KEYPRESS(KEY_A);
366 QVERIFY(keyChangedSpy.wait());
367 QCOMPARE(keyChangedSpy.count(), 1);
368 QCOMPARE(keyChangedSpy.at(0).at(0).value<quint32>(), quint32(KEY_A));
369 QCOMPARE(keyChangedSpy.at(0).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Pressed);
370 QCOMPARE(keyChangedSpy.at(0).at(2).value<quint32>(), quint32(1));
371 KEYRELEASE(KEY_A);
372 QVERIFY(keyChangedSpy.wait());
373 QCOMPARE(keyChangedSpy.count(), 2);
374 QCOMPARE(keyChangedSpy.at(1).at(0).value<quint32>(), quint32(KEY_A));
375 QCOMPARE(keyChangedSpy.at(1).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Released);
376 QCOMPARE(keyChangedSpy.at(1).at(2).value<quint32>(), quint32(2));
377
378 LOCK;
379 QVERIFY(leftSpy.wait());
380 KEYPRESS(KEY_B);
381 KEYRELEASE(KEY_B);
382 QCOMPARE(leftSpy.count(), 1);
383 QCOMPARE(keyChangedSpy.count(), 2);
384
385 UNLOCK;
386 QVERIFY(enteredSpy.wait());
387 QCOMPARE(enteredSpy.count(), 2);
388 KEYPRESS(KEY_C);
389 QVERIFY(keyChangedSpy.wait());
390 QCOMPARE(keyChangedSpy.count(), 3);
391 KEYRELEASE(KEY_C);
392 QVERIFY(keyChangedSpy.wait());
393 QCOMPARE(keyChangedSpy.count(), 4);
394 QCOMPARE(enteredSpy.count(), 2);
395 QCOMPARE(keyChangedSpy.at(2).at(0).value<quint32>(), quint32(KEY_C));
396 QCOMPARE(keyChangedSpy.at(3).at(0).value<quint32>(), quint32(KEY_C));
397 QCOMPARE(keyChangedSpy.at(2).at(2).value<quint32>(), quint32(5));
398 QCOMPARE(keyChangedSpy.at(3).at(2).value<quint32>(), quint32(6));
399 QCOMPARE(keyChangedSpy.at(2).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Pressed);
400 QCOMPARE(keyChangedSpy.at(3).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Released);
401}
402
403class TestObject : public QObject
404{
405 Q_OBJECT
406
407public Q_SLOTS:
409 {
410 return true;
411 }
412};
413
414void LockScreenTest::testScreenEdge()
415{
416 QSignalSpy screenEdgeSpy(workspace()->screenEdges(), &ScreenEdges::approaching);
417 QCOMPARE(screenEdgeSpy.count(), 0);
418
419 TestObject callback;
420 workspace()->screenEdges()->reserve(ElectricTopLeft, &callback, "callback");
421
422 quint32 timestamp = 1;
423 MOTION(QPoint(5, 5));
424 QCOMPARE(screenEdgeSpy.count(), 1);
425
426 LOCK;
427
428 MOTION(QPoint(4, 4));
429 QCOMPARE(screenEdgeSpy.count(), 1);
430
431 // and unlock
432 UNLOCK;
433
434 MOTION(QPoint(5, 5));
435 QCOMPARE(screenEdgeSpy.count(), 2);
436}
437
438void LockScreenTest::testEffects()
439{
440 std::unique_ptr<HelperEffect> effect(new HelperEffect);
441 QSignalSpy inputSpy(effect.get(), &HelperEffect::inputEvent);
442 effects->startMouseInterception(effect.get(), Qt::ArrowCursor);
443
444 quint32 timestamp = 1;
445 QCOMPARE(inputSpy.count(), 0);
446 MOTION(QPoint(5, 5));
447 QCOMPARE(inputSpy.count(), 1);
448 // simlate click
449 PRESS;
450 QCOMPARE(inputSpy.count(), 2);
451 RELEASE;
452 QCOMPARE(inputSpy.count(), 3);
453
454 LOCK;
455
456 MOTION(QPoint(6, 6));
457 QCOMPARE(inputSpy.count(), 3);
458 // simlate click
459 PRESS;
460 QCOMPARE(inputSpy.count(), 3);
461 RELEASE;
462 QCOMPARE(inputSpy.count(), 3);
463
464 UNLOCK;
465
466 MOTION(QPoint(5, 5));
467 QCOMPARE(inputSpy.count(), 4);
468 // simlate click
469 PRESS;
470 QCOMPARE(inputSpy.count(), 5);
471 RELEASE;
472 QCOMPARE(inputSpy.count(), 6);
473
474 effects->stopMouseInterception(effect.get());
475}
476
477void LockScreenTest::testEffectsKeyboard()
478{
479 std::unique_ptr<HelperEffect> effect(new HelperEffect);
480 QSignalSpy inputSpy(effect.get(), &HelperEffect::keyEvent);
481 effects->grabKeyboard(effect.get());
482
483 quint32 timestamp = 1;
484 KEYPRESS(KEY_A);
485 QCOMPARE(inputSpy.count(), 1);
486 QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
487 KEYRELEASE(KEY_A);
488 QCOMPARE(inputSpy.count(), 2);
489 QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
490 QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a"));
491
492 LOCK;
493 KEYPRESS(KEY_B);
494 QCOMPARE(inputSpy.count(), 2);
495 KEYRELEASE(KEY_B);
496 QCOMPARE(inputSpy.count(), 2);
497
498 UNLOCK;
499 KEYPRESS(KEY_C);
500 QCOMPARE(inputSpy.count(), 3);
501 QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
502 QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a"));
503 QCOMPARE(inputSpy.at(2).first().toString(), QStringLiteral("c"));
504 KEYRELEASE(KEY_C);
505 QCOMPARE(inputSpy.count(), 4);
506 QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
507 QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a"));
508 QCOMPARE(inputSpy.at(2).first().toString(), QStringLiteral("c"));
509 QCOMPARE(inputSpy.at(3).first().toString(), QStringLiteral("c"));
510
512}
513
514void LockScreenTest::testEffectsKeyboardAutorepeat()
515{
516 // this test is just like testEffectsKeyboard, but tests auto repeat key events
517 // while the key is pressed the Effect should get auto repeated events
518 // but the lock screen should filter them out
519 std::unique_ptr<HelperEffect> effect(new HelperEffect);
520 QSignalSpy inputSpy(effect.get(), &HelperEffect::keyEvent);
521 effects->grabKeyboard(effect.get());
522
523 // we need to configure the key repeat first. It is only enabled on libinput
524 waylandServer()->seat()->keyboard()->setRepeatInfo(25, 300);
525
526 quint32 timestamp = 1;
527 KEYPRESS(KEY_A);
528 QCOMPARE(inputSpy.count(), 1);
529 QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a"));
530 QVERIFY(inputSpy.wait());
531 QVERIFY(inputSpy.count() > 1);
532 // and still more events
533 QVERIFY(inputSpy.wait());
534 QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a"));
535
536 // now release
537 inputSpy.clear();
538 KEYRELEASE(KEY_A);
539 QCOMPARE(inputSpy.count(), 1);
540
541 // while locked key repeat should not pass any events to the Effect
542 LOCK;
543 KEYPRESS(KEY_B);
544 QVERIFY(!inputSpy.wait(10));
545 KEYRELEASE(KEY_B);
546 QVERIFY(!inputSpy.wait(10));
547
548 UNLOCK;
549 // don't test again, that's covered by testEffectsKeyboard
550
552}
553
554void LockScreenTest::testMoveWindow()
555{
556 auto [window, surface] = showWindow();
557 QVERIFY(window);
558 QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
559 quint32 timestamp = 1;
560
562 QCOMPARE(workspace()->moveResizeWindow(), window);
563 QVERIFY(window->isInteractiveMove());
564 Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
565 Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
566 QEXPECT_FAIL("", "First event is ignored", Continue);
567 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
568
569 // TODO adjust once the expected fail is fixed
570 Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
571 Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
572 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
573
574 // while locking our window should continue to be in move resize
575 LOCK;
576 QCOMPARE(workspace()->moveResizeWindow(), window);
577 QVERIFY(window->isInteractiveMove());
578 Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
579 Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
580 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
581
582 UNLOCK;
583 QCOMPARE(workspace()->moveResizeWindow(), window);
584 QVERIFY(window->isInteractiveMove());
585 Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
586 Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
587 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
588 Test::keyboardKeyPressed(KEY_ESC, timestamp++);
589 Test::keyboardKeyReleased(KEY_ESC, timestamp++);
590 QVERIFY(!window->isInteractiveMove());
591}
592
593void LockScreenTest::testPointerShortcut()
594{
595#if !KWIN_BUILD_GLOBALSHORTCUTS
596 QSKIP("Can't test shortcuts without shortcuts");
597 return;
598#endif
599
600 std::unique_ptr<QAction> action(new QAction(nullptr));
601 QSignalSpy actionSpy(action.get(), &QAction::triggered);
602 input()->registerPointerShortcut(Qt::MetaModifier, Qt::LeftButton, action.get());
603
604 // try to trigger the shortcut
605 quint32 timestamp = 1;
606#define PERFORM(expectedCount) \
607 do { \
608 Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++); \
609 PRESS; \
610 QCoreApplication::instance()->processEvents(); \
611 QCOMPARE(actionSpy.count(), expectedCount); \
612 RELEASE; \
613 Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++); \
614 QCoreApplication::instance()->processEvents(); \
615 QCOMPARE(actionSpy.count(), expectedCount); \
616 } while (false)
617
618 PERFORM(1);
619
620 // now the same thing with a locked screen
621 LOCK;
622 PERFORM(1);
623
624 // and as unlocked
625 UNLOCK;
626 PERFORM(2);
627#undef PERFORM
628}
629
630void LockScreenTest::testAxisShortcut_data()
631{
632#if !KWIN_BUILD_GLOBALSHORTCUTS
633 QSKIP("Can't test shortcuts without shortcuts");
634 return;
635#endif
636
637 QTest::addColumn<Qt::Orientation>("direction");
638 QTest::addColumn<int>("sign");
639
640 QTest::newRow("up") << Qt::Vertical << 1;
641 QTest::newRow("down") << Qt::Vertical << -1;
642 QTest::newRow("left") << Qt::Horizontal << 1;
643 QTest::newRow("right") << Qt::Horizontal << -1;
644}
645
646void LockScreenTest::testAxisShortcut()
647{
648 std::unique_ptr<QAction> action(new QAction(nullptr));
649 QSignalSpy actionSpy(action.get(), &QAction::triggered);
650 QFETCH(Qt::Orientation, direction);
651 QFETCH(int, sign);
652 PointerAxisDirection axisDirection = PointerAxisUp;
653 if (direction == Qt::Vertical) {
654 axisDirection = sign > 0 ? PointerAxisUp : PointerAxisDown;
655 } else {
656 axisDirection = sign > 0 ? PointerAxisLeft : PointerAxisRight;
657 }
658 input()->registerAxisShortcut(Qt::MetaModifier, axisDirection, action.get());
659
660 // try to trigger the shortcut
661 quint32 timestamp = 1;
662#define PERFORM(expectedCount) \
663 do { \
664 Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++); \
665 if (direction == Qt::Vertical) \
666 Test::pointerAxisVertical(sign * 5.0, timestamp++); \
667 else \
668 Test::pointerAxisHorizontal(sign * 5.0, timestamp++); \
669 QCoreApplication::instance()->processEvents(); \
670 QCOMPARE(actionSpy.count(), expectedCount); \
671 Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++); \
672 QCoreApplication::instance()->processEvents(); \
673 QCOMPARE(actionSpy.count(), expectedCount); \
674 } while (false)
675
676 PERFORM(1);
677
678 // now the same thing with a locked screen
679 LOCK;
680 PERFORM(1);
681
682 // and as unlocked
683 UNLOCK;
684 PERFORM(2);
685#undef PERFORM
686}
687
688void LockScreenTest::testKeyboardShortcut()
689{
690#if !KWIN_BUILD_GLOBALSHORTCUTS
691 QSKIP("Can't test shortcuts without shortcuts");
692 return;
693#endif
694
695 std::unique_ptr<QAction> action(new QAction(nullptr));
696 QSignalSpy actionSpy(action.get(), &QAction::triggered);
697 action->setProperty("componentName", QStringLiteral("kwin"));
698 action->setObjectName("LockScreenTest::testKeyboardShortcut");
699 KGlobalAccel::self()->setDefaultShortcut(action.get(), QList<QKeySequence>{Qt::CTRL | Qt::META | Qt::ALT | Qt::Key_Space});
700 KGlobalAccel::self()->setShortcut(action.get(), QList<QKeySequence>{Qt::CTRL | Qt::META | Qt::ALT | Qt::Key_Space},
701 KGlobalAccel::NoAutoloading);
702
703 // try to trigger the shortcut
704 quint32 timestamp = 1;
705 KEYPRESS(KEY_LEFTCTRL);
706 KEYPRESS(KEY_LEFTMETA);
707 KEYPRESS(KEY_LEFTALT);
708 KEYPRESS(KEY_SPACE);
709 QVERIFY(actionSpy.wait());
710 QCOMPARE(actionSpy.count(), 1);
711 KEYRELEASE(KEY_SPACE);
712 QVERIFY(!actionSpy.wait(10));
713 QCOMPARE(actionSpy.count(), 1);
714
715 LOCK;
716 KEYPRESS(KEY_SPACE);
717 QVERIFY(!actionSpy.wait(10));
718 QCOMPARE(actionSpy.count(), 1);
719 KEYRELEASE(KEY_SPACE);
720 QVERIFY(!actionSpy.wait(10));
721 QCOMPARE(actionSpy.count(), 1);
722
723 UNLOCK;
724 KEYPRESS(KEY_SPACE);
725 QVERIFY(actionSpy.wait());
726 QCOMPARE(actionSpy.count(), 2);
727 KEYRELEASE(KEY_SPACE);
728 QVERIFY(!actionSpy.wait(10));
729 QCOMPARE(actionSpy.count(), 2);
730 KEYRELEASE(KEY_LEFTCTRL);
731 KEYRELEASE(KEY_LEFTMETA);
732 KEYRELEASE(KEY_LEFTALT);
733}
734
735void LockScreenTest::testTouch()
736{
737 auto touch = m_seat->createTouch(m_seat);
738 QVERIFY(touch);
739 QVERIFY(touch->isValid());
740 auto [window, surface] = showWindow();
741 QVERIFY(window);
742 QSignalSpy sequenceStartedSpy(touch, &KWayland::Client::Touch::sequenceStarted);
743 QSignalSpy cancelSpy(touch, &KWayland::Client::Touch::sequenceCanceled);
744 QSignalSpy pointRemovedSpy(touch, &KWayland::Client::Touch::pointRemoved);
745
746 quint32 timestamp = 1;
747 Test::touchDown(1, QPointF(25, 25), timestamp++);
748 QVERIFY(sequenceStartedSpy.wait());
749 QCOMPARE(sequenceStartedSpy.count(), 1);
750
751 LOCK;
752 QVERIFY(cancelSpy.wait());
753
754 Test::touchUp(1, timestamp++);
755 QVERIFY(!pointRemovedSpy.wait(10));
756 Test::touchDown(1, QPointF(25, 25), timestamp++);
757 Test::touchMotion(1, QPointF(26, 26), timestamp++);
758 Test::touchUp(1, timestamp++);
759
760 UNLOCK;
761 Test::touchDown(1, QPointF(25, 25), timestamp++);
762 QVERIFY(sequenceStartedSpy.wait());
763 QCOMPARE(sequenceStartedSpy.count(), 2);
764 Test::touchUp(1, timestamp++);
765 QVERIFY(pointRemovedSpy.wait());
766 QCOMPARE(pointRemovedSpy.count(), 1);
767}
768
769}
770
772#include "lockscreen.moc"
Base class for all KWin effects.
Definition effect.h:535
void stopMouseInterception(Effect *effect)
void startMouseInterception(Effect *effect, Qt::CursorShape shape)
bool grabKeyboard(Effect *effect)
~HelperEffect() override
void grabbedKeyboardEvent(QKeyEvent *e) override
void keyEvent(const QString &)
void windowInputMouseEvent(QEvent *) override
void registerPointerShortcut(Qt::KeyboardModifiers modifiers, Qt::MouseButton pointerButtons, QAction *action)
Definition input.cpp:3314
void registerAxisShortcut(Qt::KeyboardModifiers modifiers, PointerAxisDirection axis, QAction *action)
PointerInputRedirection * pointer() const
Definition input.h:220
void setRepeatInfo(qint32 charactersPerSecond, qint32 delay)
Definition keyboard.cpp:240
void warp(const QPointF &pos)
void approaching(ElectricBorder border, qreal factor, const QRect &geometry)
void reserve(ElectricBorder border, QObject *object, const char *callback)
KeyboardInterface * keyboard() const
Definition seat.cpp:963
bool callback(ElectricBorder border)
SeatInterface * seat() const
void interactiveMoveResizeStepped(const QRectF &geometry)
ScreenEdges * screenEdges() const
void windowAdded(KWin::Window *)
QList< Output * > outputs() const
Definition workspace.h:762
void setActiveOutput(Output *output)
#define VERIFY(statement)
#define PRESS
#define RELEASE
#define MOTION(target)
#define COMPARE(actual, expected)
Q_DECLARE_METATYPE(KWin::SwitchEvent::State)
#define WAYLANDTEST_MAIN(TestObject)
#define LOCK
#define PERFORM(expectedCount)
#define KEYRELEASE(key)
#define UNLOCK
#define KEYPRESS(key)
Window * renderAndWaitForShown(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32, int timeout=5000)
void keyboardKeyReleased(quint32 key, quint32 time)
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)
void keyboardKeyPressed(quint32 key, quint32 time)
bool setupWaylandConnection(AdditionalWaylandInterfaces flags=AdditionalWaylandInterfaces())
void touchMotion(qint32 id, const QPointF &pos, quint32 time)
KWayland::Client::Compositor * waylandCompositor()
void pointerAxisHorizontal(qreal delta, quint32 time, qint32 discreteDelta=0, InputRedirection::PointerAxisSource source=InputRedirection::PointerAxisSourceUnknown)
KWayland::Client::Seat * waylandSeat()
QList< KWayland::Client::Output * > outputs
std::unique_ptr< KWayland::Client::Surface > createSurface()
KWayland::Client::ShmPool * waylandShmPool()
XdgToplevel * createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent=nullptr)
bool waitForWaylandPointer()
KWayland::Client::ConnectionThread * waylandConnection()
void touchUp(qint32 id, quint32 time)
PointerAxisDirection
The direction in which a pointer axis is moved.
Definition globals.h:104
@ PointerAxisUp
Definition globals.h:105
@ PointerAxisLeft
Definition globals.h:107
@ PointerAxisDown
Definition globals.h:106
@ PointerAxisRight
Definition globals.h:108
ElectricBorder
Definition globals.h:60
@ ElectricTopLeft
Definition globals.h:68
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
InputRedirection * input()
Definition input.h:549
@ OverlayLayer
Definition globals.h:174
EffectsHandler * effects