18#include "virtualdesktops.h"
25#include <KWayland/Client/buffer.h>
26#include <KWayland/Client/compositor.h>
27#include <KWayland/Client/connection_thread.h>
28#include <KWayland/Client/keyboard.h>
29#include <KWayland/Client/pointer.h>
30#include <KWayland/Client/region.h>
31#include <KWayland/Client/seat.h>
32#include <KWayland/Client/shm_pool.h>
33#include <KWayland/Client/surface.h>
35#include <linux/input.h>
36#include <xcb/xcb_icccm.h>
41static PlatformCursorImage loadReferenceThemeCursor(
const QByteArray &name)
45 const KXcursorTheme theme(pointerCursor->themeName(), pointerCursor->themeSize(), kwinApp()->devicePixelRatio());
46 if (theme.isEmpty()) {
47 return PlatformCursorImage();
50 ShapeCursorSource source;
51 source.setShape(name);
52 source.setTheme(theme);
54 return PlatformCursorImage(source.image(), source.hotspot());
57static PlatformCursorImage loadReferenceThemeCursor(
const CursorShape &shape)
59 return loadReferenceThemeCursor(shape.name());
62static const QString s_socketName = QStringLiteral(
"wayland_test_kwin_pointer_input-0");
71 void testWarpingUpdatesFocus();
72 void testWarpingGeneratesPointerMotion();
73 void testWarpingBetweenWindows();
74 void testUpdateFocusAfterScreenChange();
75 void testUpdateFocusOnDecorationDestroy();
76 void testModifierClickUnrestrictedMove_data();
77 void testModifierClickUnrestrictedMove();
78 void testModifierClickUnrestrictedFullscreenMove();
79 void testModifierClickUnrestrictedMoveGlobalShortcutsDisabled();
80 void testModifierScrollOpacity_data();
81 void testModifierScrollOpacity();
82 void testModifierScrollOpacityGlobalShortcutsDisabled();
83 void testScrollAction();
84 void testFocusFollowsMouse();
85 void testMouseActionInactiveWindow_data();
86 void testMouseActionInactiveWindow();
87 void testMouseActionActiveWindow_data();
88 void testMouseActionActiveWindow();
89 void testCursorImage();
90 void testCursorShapeV1();
91 void testEffectOverrideCursorImage();
93 void testDecoCancelsPopup();
94 void testWindowUnderCursorWhileButtonPressed();
95 void testConfineToScreenGeometry_data();
96 void testConfineToScreenGeometry();
97 void testResizeCursor_data();
98 void testResizeCursor();
99 void testMoveCursor();
100 void testHideShowCursor();
101 void testDefaultInputRegion();
102 void testEmptyInputRegion();
103 void testUnfocusedModifiers();
106 void render(KWayland::Client::Surface *surface,
const QSize &size = QSize(100, 50));
107 KWayland::Client::Compositor *m_compositor =
nullptr;
108 KWayland::Client::Seat *m_seat =
nullptr;
111void PointerInputTest::initTestCase()
113 qRegisterMetaType<KWin::Window *>();
117 QRect(0, 0, 1280, 1024),
118 QRect(1280, 0, 1280, 1024),
121 kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
123 if (!QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral(
"icons/DMZ-White/index.theme")).isEmpty()) {
124 qputenv(
"XCURSOR_THEME", QByteArrayLiteral(
"DMZ-White"));
127 qputenv(
"XCURSOR_THEME", QByteArrayLiteral(
"Vanilla-DMZ"));
129 qputenv(
"XCURSOR_SIZE", QByteArrayLiteral(
"24"));
130 qputenv(
"XKB_DEFAULT_RULES",
"evdev");
133 QVERIFY(applicationStartedSpy.wait());
136 QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
137 QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
138 setenv(
"QT_QPA_PLATFORM",
"wayland",
true);
141void PointerInputTest::init()
152void PointerInputTest::cleanup()
157void PointerInputTest::render(KWayland::Client::Surface *surface,
const QSize &size)
163void PointerInputTest::testWarpingUpdatesFocus()
168 auto pointer = m_seat->createPointer(m_seat);
170 QVERIFY(pointer->isValid());
171 QSignalSpy enteredSpy(pointer, &KWayland::Client::Pointer::entered);
172 QSignalSpy leftSpy(pointer, &KWayland::Client::Pointer::left);
179 QVERIFY(shellSurface);
180 render(surface.get());
181 QVERIFY(windowAddedSpy.wait());
187 QVERIFY(!pointer->enteredSurface());
191 QVERIFY(enteredSpy.wait());
192 QCOMPARE(enteredSpy.count(), 1);
193 QCOMPARE(enteredSpy.first().at(1).toPointF(), QPointF(25, 25));
195 QCOMPARE(pointer->enteredSurface(), surface.get());
201 QVERIFY(leftSpy.wait());
202 QCOMPARE(leftSpy.count(), 1);
205 QVERIFY(!pointer->enteredSurface());
208void PointerInputTest::testWarpingGeneratesPointerMotion()
213 auto pointer = m_seat->createPointer(m_seat);
215 QVERIFY(pointer->isValid());
216 QSignalSpy enteredSpy(pointer, &KWayland::Client::Pointer::entered);
217 QSignalSpy movedSpy(pointer, &KWayland::Client::Pointer::motion);
224 QVERIFY(shellSurface);
225 render(surface.get());
226 QVERIFY(windowAddedSpy.wait());
232 QVERIFY(enteredSpy.wait());
233 QCOMPARE(enteredSpy.first().at(1).toPointF(), QPointF(25, 25));
237 QVERIFY(movedSpy.wait());
238 QCOMPARE(movedSpy.count(), 1);
239 QCOMPARE(movedSpy.last().first().toPointF(), QPointF(26, 26));
242void PointerInputTest::testWarpingBetweenWindows()
247 std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer(m_seat));
248 QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
249 QSignalSpy leftSpy(pointer.get(), &KWayland::Client::Pointer::left);
250 QSignalSpy motionSpy(pointer.get(), &KWayland::Client::Pointer::motion);
261 window1->move(QPoint(0, 0));
262 window2->move(QPoint(100, 0));
264 quint32 timestamp = 0;
268 QVERIFY(enteredSpy.wait());
269 QCOMPARE(enteredSpy.count(), 1);
270 QCOMPARE(enteredSpy.last().at(1).toPointF(), QPointF(50, 25));
271 QCOMPARE(leftSpy.count(), 0);
272 QCOMPARE(motionSpy.count(), 0);
273 QCOMPARE(pointer->enteredSurface(), surface1.get());
277 QVERIFY(enteredSpy.wait());
278 QCOMPARE(enteredSpy.count(), 2);
279 QCOMPARE(enteredSpy.last().at(1).toPointF(), QPointF(100, 50));
280 QCOMPARE(leftSpy.count(), 1);
281 QCOMPARE(motionSpy.count(), 0);
282 QCOMPARE(pointer->enteredSurface(), surface2.get());
285void PointerInputTest::testUpdateFocusAfterScreenChange()
291 auto pointer = m_seat->createPointer(m_seat);
293 QVERIFY(pointer->isValid());
294 QSignalSpy enteredSpy(pointer, &KWayland::Client::Pointer::entered);
295 QSignalSpy leftSpy(pointer, &KWayland::Client::Pointer::left);
302 QVERIFY(shellSurface);
303 render(surface.get(), QSize(1280, 1024));
304 QVERIFY(windowAddedSpy.wait());
308 QVERIFY(enteredSpy.wait());
309 QCOMPARE(enteredSpy.count(), 1);
314 QVERIFY(leftSpy.wait());
325 QVERIFY(enteredSpy.wait());
326 QCOMPARE(enteredSpy.count(), 2);
329void PointerInputTest::testUpdateFocusOnDecorationDestroy()
335 auto pointer = m_seat->createPointer(m_seat);
337 QVERIFY(pointer->isValid());
338 QSignalSpy buttonStateChangedSpy(pointer, &KWayland::Client::Pointer::buttonStateChanged);
341 auto group = kwinApp()->config()->group(QStringLiteral(
"Windows"));
342 group.writeEntry(
"BorderlessMaximizedWindows",
true);
355 decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
356 surface->commit(KWayland::Client::Surface::CommitFlag::None);
359 Test::XdgToplevel::States states;
360 QVERIFY(surfaceConfigureRequestedSpy.wait());
361 QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
362 QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(0, 0));
363 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
368 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
371 QVERIFY(window->isActive());
374 QCOMPARE(window->isDecorated(),
true);
377 QVERIFY(surfaceConfigureRequestedSpy.wait());
378 QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
379 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
384 quint32 timestamp = 0;
386 QVERIFY(
input()->pointer()->decoration());
390 QVERIFY(surfaceConfigureRequestedSpy.wait());
391 QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
392 QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024));
393 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
398 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
399 Test::render(surface.get(), QSize(1280, 1024), Qt::blue);
400 QVERIFY(frameGeometryChangedSpy.wait());
401 QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
403 QCOMPARE(window->requestedMaximizeMode(),
MaximizeFull);
404 QCOMPARE(window->isDecorated(),
false);
407 QVERIFY(!
input()->pointer()->decoration());
410 QVERIFY(buttonStateChangedSpy.wait());
411 QCOMPARE(pointer->enteredSurface(), surface.get());
414 shellSurface.reset();
418void PointerInputTest::testModifierClickUnrestrictedMove_data()
420 QTest::addColumn<int>(
"modifierKey");
421 QTest::addColumn<int>(
"mouseButton");
422 QTest::addColumn<QString>(
"modKey");
423 QTest::addColumn<bool>(
"capsLock");
425 const QString alt = QStringLiteral(
"Alt");
426 const QString meta = QStringLiteral(
"Meta");
428 QTest::newRow(
"Left Alt + Left Click") << KEY_LEFTALT << BTN_LEFT << alt <<
false;
429 QTest::newRow(
"Left Alt + Right Click") << KEY_LEFTALT << BTN_RIGHT << alt <<
false;
430 QTest::newRow(
"Left Alt + Middle Click") << KEY_LEFTALT << BTN_MIDDLE << alt <<
false;
431 QTest::newRow(
"Right Alt + Left Click") << KEY_RIGHTALT << BTN_LEFT << alt <<
false;
432 QTest::newRow(
"Right Alt + Right Click") << KEY_RIGHTALT << BTN_RIGHT << alt <<
false;
433 QTest::newRow(
"Right Alt + Middle Click") << KEY_RIGHTALT << BTN_MIDDLE << alt <<
false;
435 QTest::newRow(
"Left Meta + Left Click") << KEY_LEFTMETA << BTN_LEFT << meta <<
false;
436 QTest::newRow(
"Left Meta + Right Click") << KEY_LEFTMETA << BTN_RIGHT << meta <<
false;
437 QTest::newRow(
"Left Meta + Middle Click") << KEY_LEFTMETA << BTN_MIDDLE << meta <<
false;
438 QTest::newRow(
"Right Meta + Left Click") << KEY_RIGHTMETA << BTN_LEFT << meta <<
false;
439 QTest::newRow(
"Right Meta + Right Click") << KEY_RIGHTMETA << BTN_RIGHT << meta <<
false;
440 QTest::newRow(
"Right Meta + Middle Click") << KEY_RIGHTMETA << BTN_MIDDLE << meta <<
false;
443 QTest::newRow(
"Left Alt + Left Click/CapsLock") << KEY_LEFTALT << BTN_LEFT << alt <<
true;
444 QTest::newRow(
"Left Alt + Right Click/CapsLock") << KEY_LEFTALT << BTN_RIGHT << alt <<
true;
445 QTest::newRow(
"Left Alt + Middle Click/CapsLock") << KEY_LEFTALT << BTN_MIDDLE << alt <<
true;
446 QTest::newRow(
"Right Alt + Left Click/CapsLock") << KEY_RIGHTALT << BTN_LEFT << alt <<
true;
447 QTest::newRow(
"Right Alt + Right Click/CapsLock") << KEY_RIGHTALT << BTN_RIGHT << alt <<
true;
448 QTest::newRow(
"Right Alt + Middle Click/CapsLock") << KEY_RIGHTALT << BTN_MIDDLE << alt <<
true;
450 QTest::newRow(
"Left Meta + Left Click/CapsLock") << KEY_LEFTMETA << BTN_LEFT << meta <<
true;
451 QTest::newRow(
"Left Meta + Right Click/CapsLock") << KEY_LEFTMETA << BTN_RIGHT << meta <<
true;
452 QTest::newRow(
"Left Meta + Middle Click/CapsLock") << KEY_LEFTMETA << BTN_MIDDLE << meta <<
true;
453 QTest::newRow(
"Right Meta + Left Click/CapsLock") << KEY_RIGHTMETA << BTN_LEFT << meta <<
true;
454 QTest::newRow(
"Right Meta + Right Click/CapsLock") << KEY_RIGHTMETA << BTN_RIGHT << meta <<
true;
455 QTest::newRow(
"Right Meta + Middle Click/CapsLock") << KEY_RIGHTMETA << BTN_MIDDLE << meta <<
true;
458void PointerInputTest::testModifierClickUnrestrictedMove()
463 auto pointer = m_seat->createPointer(m_seat);
465 QVERIFY(pointer->isValid());
466 QSignalSpy buttonSpy(pointer, &KWayland::Client::Pointer::buttonStateChanged);
469 QFETCH(QString, modKey);
470 KConfigGroup group = kwinApp()->config()->group(QStringLiteral(
"MouseBindings"));
471 group.writeEntry(
"CommandAllKey", modKey);
472 group.writeEntry(
"CommandAll1",
"Move");
473 group.writeEntry(
"CommandAll2",
"Move");
474 group.writeEntry(
"CommandAll3",
"Move");
487 QVERIFY(shellSurface);
488 render(surface.get());
489 QVERIFY(windowAddedSpy.wait());
497 quint32 timestamp = 1;
498 QFETCH(
bool, capsLock);
502 QFETCH(
int, modifierKey);
503 QFETCH(
int, mouseButton);
505 QVERIFY(!window->isInteractiveMove());
507 QVERIFY(window->isInteractiveMove());
510 QVERIFY(window->isInteractiveMove());
513 QVERIFY(!window->isInteractiveMove());
519 QCOMPARE(buttonSpy.count(), 0);
522 QCOMPARE(buttonSpy.count(), 0);
525void PointerInputTest::testModifierClickUnrestrictedFullscreenMove()
530 QRect(0, 0, 1280, 1024),
531 QRect(1280, 0, 1280, 1024),
536 KConfigGroup group = kwinApp()->config()->group(QStringLiteral(
"MouseBindings"));
537 group.writeEntry(
"CommandAllKey",
"Meta");
538 group.writeEntry(
"CommandAll1",
"Move");
539 group.writeEntry(
"CommandAll2",
"Move");
540 group.writeEntry(
"CommandAll3",
"Move");
552 QVERIFY(shellSurface);
553 shellSurface->set_fullscreen(
nullptr);
556 QVERIFY(surfaceConfigureRequestedSpy.wait());
557 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
560 QVERIFY(window->isFullScreen());
566 quint32 timestamp = 1;
568 QVERIFY(!window->isInteractiveMove());
570 QVERIFY(window->isInteractiveMove());
573 QVERIFY(window->isInteractiveMove());
576 QVERIFY(!window->isInteractiveMove());
579void PointerInputTest::testModifierClickUnrestrictedMoveGlobalShortcutsDisabled()
584 auto pointer = m_seat->createPointer(m_seat);
586 QVERIFY(pointer->isValid());
587 QSignalSpy buttonSpy(pointer, &KWayland::Client::Pointer::buttonStateChanged);
590 KConfigGroup group = kwinApp()->config()->group(QStringLiteral(
"MouseBindings"));
591 group.writeEntry(
"CommandAllKey",
"Meta");
592 group.writeEntry(
"CommandAll1",
"Move");
593 group.writeEntry(
"CommandAll2",
"Move");
594 group.writeEntry(
"CommandAll3",
"Move");
607 QVERIFY(shellSurface);
608 render(surface.get());
609 QVERIFY(windowAddedSpy.wait());
614 QVERIFY(!
workspace()->globalShortcutsDisabled());
616 QVERIFY(
workspace()->globalShortcutsDisabled());
622 quint32 timestamp = 1;
624 QVERIFY(!window->isInteractiveMove());
626 QVERIFY(!window->isInteractiveMove());
629 QVERIFY(!window->isInteractiveMove());
635void PointerInputTest::testModifierScrollOpacity_data()
637 QTest::addColumn<int>(
"modifierKey");
638 QTest::addColumn<QString>(
"modKey");
639 QTest::addColumn<bool>(
"capsLock");
641 const QString alt = QStringLiteral(
"Alt");
642 const QString meta = QStringLiteral(
"Meta");
644 QTest::newRow(
"Left Alt") << KEY_LEFTALT << alt <<
false;
645 QTest::newRow(
"Right Alt") << KEY_RIGHTALT << alt <<
false;
646 QTest::newRow(
"Left Meta") << KEY_LEFTMETA << meta <<
false;
647 QTest::newRow(
"Right Meta") << KEY_RIGHTMETA << meta <<
false;
648 QTest::newRow(
"Left Alt/CapsLock") << KEY_LEFTALT << alt <<
true;
649 QTest::newRow(
"Right Alt/CapsLock") << KEY_RIGHTALT << alt <<
true;
650 QTest::newRow(
"Left Meta/CapsLock") << KEY_LEFTMETA << meta <<
true;
651 QTest::newRow(
"Right Meta/CapsLock") << KEY_RIGHTMETA << meta <<
true;
654void PointerInputTest::testModifierScrollOpacity()
660 auto pointer = m_seat->createPointer(m_seat);
662 QVERIFY(pointer->isValid());
663 QSignalSpy axisSpy(pointer, &KWayland::Client::Pointer::axisChanged);
666 QFETCH(QString, modKey);
667 KConfigGroup group = kwinApp()->config()->group(QStringLiteral(
"MouseBindings"));
668 group.writeEntry(
"CommandAllKey", modKey);
669 group.writeEntry(
"CommandAllWheel",
"change opacity");
678 QVERIFY(shellSurface);
679 render(surface.get());
680 QVERIFY(windowAddedSpy.wait());
684 window->setOpacity(0.5);
685 QCOMPARE(window->opacity(), 0.5);
691 quint32 timestamp = 1;
692 QFETCH(
bool, capsLock);
696 QFETCH(
int, modifierKey);
699 QCOMPARE(window->opacity(), 0.6);
701 QCOMPARE(window->opacity(), 0.5);
708 QCOMPARE(axisSpy.count(), 0);
710 QCOMPARE(axisSpy.count(), 0);
713void PointerInputTest::testModifierScrollOpacityGlobalShortcutsDisabled()
719 auto pointer = m_seat->createPointer(m_seat);
721 QVERIFY(pointer->isValid());
722 QSignalSpy axisSpy(pointer, &KWayland::Client::Pointer::axisChanged);
725 KConfigGroup group = kwinApp()->config()->group(QStringLiteral(
"MouseBindings"));
726 group.writeEntry(
"CommandAllKey",
"Meta");
727 group.writeEntry(
"CommandAllWheel",
"change opacity");
736 QVERIFY(shellSurface);
737 render(surface.get());
738 QVERIFY(windowAddedSpy.wait());
742 window->setOpacity(0.5);
743 QCOMPARE(window->opacity(), 0.5);
749 QVERIFY(!
workspace()->globalShortcutsDisabled());
751 QVERIFY(
workspace()->globalShortcutsDisabled());
754 quint32 timestamp = 1;
757 QCOMPARE(window->opacity(), 0.5);
759 QCOMPARE(window->opacity(), 0.5);
765void PointerInputTest::testScrollAction()
768 auto pointer = m_seat->createPointer(m_seat);
770 QVERIFY(pointer->isValid());
771 QSignalSpy axisSpy(pointer, &KWayland::Client::Pointer::axisChanged);
774 KConfigGroup group = kwinApp()->config()->group(QStringLiteral(
"MouseBindings"));
775 group.writeEntry(
"CommandWindowWheel",
"activate and scroll");
783 QVERIFY(shellSurface1);
784 render(surface1.get());
785 QVERIFY(windowAddedSpy.wait());
791 QVERIFY(shellSurface2);
792 render(surface2.get());
793 QVERIFY(windowAddedSpy.wait());
796 QVERIFY(window1 != window2);
801 quint32 timestamp = 1;
802 QVERIFY(!window1->isActive());
804 QVERIFY(window1->isActive());
807 QVERIFY(axisSpy.wait());
810void PointerInputTest::testFocusFollowsMouse()
813 auto pointer = m_seat->createPointer(m_seat);
815 QVERIFY(pointer->isValid());
820 KConfigGroup group = kwinApp()->config()->group(QStringLiteral(
"Windows"));
821 group.writeEntry(
"AutoRaise",
true);
822 group.writeEntry(
"AutoRaiseInterval", 20);
823 group.writeEntry(
"DelayFocusInterval", 200);
824 group.writeEntry(
"FocusPolicy",
"FocusFollowsMouse");
838 QVERIFY(shellSurface1);
839 render(surface1.get(), QSize(800, 800));
840 QVERIFY(windowAddedSpy.wait());
846 QVERIFY(shellSurface2);
847 render(surface2.get(), QSize(800, 800));
848 QVERIFY(windowAddedSpy.wait());
851 QVERIFY(window1 != window2);
852 QCOMPARE(
workspace()->topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop()), window2);
854 QVERIFY(window1->frameGeometry().intersects(window2->frameGeometry()));
860 QVERIFY(!window1->isActive());
861 QVERIFY(window2->isActive());
864 QVERIFY(exclusiveContains(window1->frameGeometry(), QPointF(10, 10)));
865 QVERIFY(!exclusiveContains(window2->frameGeometry(), QPointF(10, 10)));
867 QVERIFY(stackingOrderChangedSpy.wait());
868 QCOMPARE(stackingOrderChangedSpy.count(), 1);
869 QCOMPARE(
workspace()->topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop()), window1);
870 QTRY_VERIFY(window1->isActive());
874 QVERIFY(stackingOrderChangedSpy.wait());
875 QCOMPARE(stackingOrderChangedSpy.count(), 2);
876 QCOMPARE(
workspace()->topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop()), window2);
878 QVERIFY(!activeWindowChangedSpy.wait(250));
879 QVERIFY(window1->isActive());
880 QCOMPARE(
workspace()->topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop()), window1);
882 QCOMPARE(stackingOrderChangedSpy.count(), 3);
887 QVERIFY(!stackingOrderChangedSpy.wait(250));
890void PointerInputTest::testMouseActionInactiveWindow_data()
892 QTest::addColumn<quint32>(
"button");
894 QTest::newRow(
"Left") << quint32(BTN_LEFT);
895 QTest::newRow(
"Middle") << quint32(BTN_MIDDLE);
896 QTest::newRow(
"Right") << quint32(BTN_RIGHT);
899void PointerInputTest::testMouseActionInactiveWindow()
905 KConfigGroup group = kwinApp()->config()->group(QStringLiteral(
"Windows"));
906 group.writeEntry(
"FocusPolicy",
"ClickToFocus");
908 group = kwinApp()->config()->group(QStringLiteral(
"MouseBindings"));
909 group.writeEntry(
"CommandWindow1",
"Activate, raise and pass click");
910 group.writeEntry(
"CommandWindow2",
"Activate, raise and pass click");
911 group.writeEntry(
"CommandWindow3",
"Activate, raise and pass click");
920 QVERIFY(shellSurface1);
921 render(surface1.get(), QSize(800, 800));
922 QVERIFY(windowAddedSpy.wait());
928 QVERIFY(shellSurface2);
929 render(surface2.get(), QSize(800, 800));
930 QVERIFY(windowAddedSpy.wait());
933 QVERIFY(window1 != window2);
934 QCOMPARE(
workspace()->topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop()), window2);
936 QVERIFY(window1->frameGeometry().intersects(window2->frameGeometry()));
942 QVERIFY(!window1->isActive());
943 QVERIFY(window2->isActive());
946 QVERIFY(exclusiveContains(window1->frameGeometry(), QPointF(10, 10)));
947 QVERIFY(!exclusiveContains(window2->frameGeometry(), QPointF(10, 10)));
950 QVERIFY(stackingOrderChangedSpy.isEmpty());
951 QVERIFY(activeWindowChangedSpy.isEmpty());
952 QVERIFY(window2->isActive());
954 quint32 timestamp = 1;
955 QFETCH(quint32, button);
958 QCOMPARE(stackingOrderChangedSpy.count(), 1);
959 QVERIFY(!activeWindowChangedSpy.isEmpty());
960 QCOMPARE(
workspace()->topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop()), window1);
961 QVERIFY(window1->isActive());
962 QVERIFY(!window2->isActive());
968void PointerInputTest::testMouseActionActiveWindow_data()
970 QTest::addColumn<bool>(
"clickRaise");
971 QTest::addColumn<quint32>(
"button");
973 for (quint32 i = BTN_LEFT; i < BTN_JOYSTICK; i++) {
974 QByteArray number = QByteArray::number(i, 16);
975 QTest::newRow(QByteArrayLiteral(
"click raise/").append(number).constData()) <<
true << i;
976 QTest::newRow(QByteArrayLiteral(
"no click raise/").append(number).constData()) <<
false << i;
980void PointerInputTest::testMouseActionActiveWindow()
987 auto pointer = m_seat->createPointer(m_seat);
989 QVERIFY(pointer->isValid());
990 QSignalSpy buttonSpy(pointer, &KWayland::Client::Pointer::buttonStateChanged);
993 QFETCH(
bool, clickRaise);
994 KConfigGroup group = kwinApp()->config()->group(QStringLiteral(
"Windows"));
995 group.writeEntry(
"ClickRaise", clickRaise);
1005 QVERIFY(shellSurface1);
1006 render(surface1.get(), QSize(800, 800));
1007 QVERIFY(windowAddedSpy.wait());
1010 QSignalSpy window1DestroyedSpy(window1, &QObject::destroyed);
1014 QVERIFY(shellSurface2);
1015 render(surface2.get(), QSize(800, 800));
1016 QVERIFY(windowAddedSpy.wait());
1019 QVERIFY(window1 != window2);
1020 QSignalSpy window2DestroyedSpy(window2, &QObject::destroyed);
1021 QCOMPARE(
workspace()->topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop()), window2);
1023 QVERIFY(window1->frameGeometry().intersects(window2->frameGeometry()));
1026 QCOMPARE(
workspace()->topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop()), window1);
1032 QVERIFY(!exclusiveContains(window1->frameGeometry(), QPointF(900, 900)));
1033 QVERIFY(exclusiveContains(window2->frameGeometry(), QPointF(900, 900)));
1037 quint32 timestamp = 1;
1038 QFETCH(quint32, button);
1040 QVERIFY(buttonSpy.wait());
1042 QCOMPARE(stackingOrderChangedSpy.count(), 1);
1043 QCOMPARE(
workspace()->topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop()), window2);
1045 QCOMPARE(stackingOrderChangedSpy.count(), 0);
1046 QCOMPARE(
workspace()->topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop()), window1);
1053 QVERIFY(window1DestroyedSpy.wait());
1055 QVERIFY(window2DestroyedSpy.wait());
1058void PointerInputTest::testCursorImage()
1063 auto pointer = m_seat->createPointer(m_seat);
1065 QVERIFY(pointer->isValid());
1066 QSignalSpy enteredSpy(pointer, &KWayland::Client::Pointer::entered);
1073 const QImage fallbackCursor = kwinApp()->cursorImage().image();
1074 QVERIFY(!fallbackCursor.isNull());
1081 QVERIFY(shellSurface);
1082 render(surface.get());
1083 QVERIFY(windowAddedSpy.wait());
1089 QCOMPARE(p->focus(), window);
1090 QCOMPARE(kwinApp()->cursorImage().image(), fallbackCursor);
1091 QVERIFY(enteredSpy.wait());
1095 QVERIFY(cursorSurface);
1096 QSignalSpy cursorRenderedSpy(cursorSurface.get(), &KWayland::Client::Surface::frameRendered);
1097 QImage red = QImage(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
1100 cursorSurface->damage(QRect(0, 0, 10, 10));
1101 cursorSurface->commit();
1102 pointer->setCursor(cursorSurface.get(), QPoint(5, 5));
1103 QVERIFY(cursorRenderedSpy.wait());
1104 QCOMPARE(kwinApp()->cursorImage().image(), red);
1105 QCOMPARE(cursor->hotspot(), QPoint(5, 5));
1107 pointer->setCursor(cursorSurface.get(), QPoint(6, 6));
1109 QTRY_COMPARE(cursor->hotspot(), QPoint(6, 6));
1110 QCOMPARE(kwinApp()->cursorImage().image(), red);
1113 QImage blue = QImage(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
1114 blue.fill(Qt::blue);
1116 cursorSurface->attachBuffer(b);
1117 cursorSurface->damage(QRect(0, 0, 10, 10));
1118 cursorSurface->commit();
1119 QVERIFY(cursorRenderedSpy.wait());
1120 QTRY_COMPARE(kwinApp()->cursorImage().image(), blue);
1121 QCOMPARE(cursor->hotspot(), QPoint(6, 6));
1124 pointer->setCursor(
nullptr);
1126 QTRY_VERIFY(kwinApp()->cursorImage().image().isNull());
1129 input()->
pointer()->
warp(window->frameGeometry().bottomLeft() + QPoint(20, 20));
1130 QVERIFY(!p->focus());
1131 QVERIFY(!kwinApp()->cursorImage().image().isNull());
1132 QCOMPARE(kwinApp()->cursorImage().image(), fallbackCursor);
1135static QByteArray currentCursorShape()
1137 if (
auto source = qobject_cast<ShapeCursorSource *>(
Cursors::self()->currentCursor()->source())) {
1138 return source->shape();
1140 return QByteArray();
1143void PointerInputTest::testCursorShapeV1()
1148 std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
1153 QCOMPARE(currentCursorShape(), QByteArrayLiteral(
"default"));
1162 QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
1164 QVERIFY(enteredSpy.wait());
1168 cursorShapeDevice->set_shape(enteredSpy.last().at(0).value<quint32>(), Test::CursorShapeDeviceV1::shape_text);
1169 QVERIFY(cursorChanged.wait());
1170 QCOMPARE(currentCursorShape(), QByteArray(
"text"));
1174 QCOMPARE(currentCursorShape(), QByteArrayLiteral(
"default"));
1175 cursorShapeDevice->set_shape(enteredSpy.last().at(0).value<quint32>(), Test::CursorShapeDeviceV1::shape_grab);
1177 QCOMPARE(currentCursorShape(), QByteArrayLiteral(
"default"));
1180class HelperEffect :
public Effect
1192void PointerInputTest::testEffectOverrideCursorImage()
1197 std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
1199 QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
1200 QSignalSpy leftSpy(pointer.get(), &KWayland::Client::Pointer::left);
1211 QVERIFY(shellSurface);
1212 render(surface.get());
1213 QVERIFY(windowAddedSpy.wait());
1218 QVERIFY(!exclusiveContains(window->frameGeometry(), QPoint(800, 800)));
1220 QVERIFY(enteredSpy.wait());
1221 cursorShapeDevice->set_shape(enteredSpy.last().at(0).value<quint32>(), Test::CursorShapeDeviceV1::shape_wait);
1222 QVERIFY(cursorChanged.wait());
1223 QCOMPARE(currentCursorShape(), QByteArray(
"wait"));
1226 std::unique_ptr<HelperEffect> effect(
new HelperEffect);
1228 QCOMPARE(currentCursorShape(), QByteArrayLiteral(
"all-scroll"));
1232 QCOMPARE(currentCursorShape(), QByteArrayLiteral(
"default"));
1236 QCOMPARE(currentCursorShape(), QByteArrayLiteral(
"all-scroll"));
1240 QCOMPARE(currentCursorShape(), QByteArrayLiteral(
"all-scroll"));
1246 QCOMPARE(enteredSpy.count(), 1);
1250 QVERIFY(enteredSpy.wait());
1251 cursorShapeDevice->set_shape(enteredSpy.last().at(0).value<quint32>(), Test::CursorShapeDeviceV1::shape_crosshair);
1252 QVERIFY(cursorChanged.wait());
1253 QCOMPARE(currentCursorShape(), QByteArrayLiteral(
"crosshair"));
1256void PointerInputTest::testPopup()
1262 auto pointer = m_seat->createPointer(m_seat);
1264 QVERIFY(pointer->isValid());
1265 QSignalSpy enteredSpy(pointer, &KWayland::Client::Pointer::entered);
1266 QSignalSpy leftSpy(pointer, &KWayland::Client::Pointer::left);
1267 QSignalSpy buttonStateChangedSpy(pointer, &KWayland::Client::Pointer::buttonStateChanged);
1268 QSignalSpy motionSpy(pointer, &KWayland::Client::Pointer::motion);
1276 QVERIFY(shellSurface);
1277 render(surface.get());
1278 QVERIFY(windowAddedSpy.wait());
1281 QCOMPARE(window->hasPopupGrab(),
false);
1283 QVERIFY(!exclusiveContains(window->frameGeometry(), QPoint(800, 800)));
1285 QVERIFY(enteredSpy.wait());
1287 quint32 timestamp = 0;
1290 QVERIFY(buttonStateChangedSpy.wait());
1294 positioner->set_size(100, 50);
1295 positioner->set_anchor_rect(0, 0, 80, 20);
1296 positioner->set_anchor(Test::XdgPositioner::anchor_bottom_right);
1297 positioner->set_gravity(Test::XdgPositioner::gravity_bottom_right);
1299 QVERIFY(popupSurface);
1300 Test::XdgPopup *popupShellSurface =
Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
1301 QVERIFY(popupShellSurface);
1304 render(popupSurface.get(), QSize(100, 50));
1305 QVERIFY(windowAddedSpy.wait());
1306 auto popupWindow = windowAddedSpy.last().first().value<
Window *>();
1307 QVERIFY(popupWindow);
1308 QVERIFY(popupWindow != window);
1309 QCOMPARE(window,
workspace()->activeWindow());
1310 QCOMPARE(popupWindow->transientFor(), window);
1311 QCOMPARE(popupWindow->pos(), window->pos() + QPoint(80, 20));
1312 QCOMPARE(popupWindow->hasPopupGrab(),
true);
1316 QVERIFY(enteredSpy.wait());
1317 QCOMPARE(enteredSpy.count(), 2);
1318 QCOMPARE(leftSpy.count(), 1);
1319 QCOMPARE(pointer->enteredSurface(), popupSurface.get());
1323 input()->
pointer()->
warp(popupWindow->frameGeometry().bottomRight() + QPoint(2, 2));
1324 QVERIFY(leftSpy.wait());
1325 QCOMPARE(leftSpy.count(), 2);
1326 QVERIFY(doneReceivedSpy.isEmpty());
1329 QVERIFY(doneReceivedSpy.wait());
1333void PointerInputTest::testDecoCancelsPopup()
1339 auto pointer = m_seat->createPointer(m_seat);
1341 QVERIFY(pointer->isValid());
1342 QSignalSpy enteredSpy(pointer, &KWayland::Client::Pointer::entered);
1343 QSignalSpy leftSpy(pointer, &KWayland::Client::Pointer::left);
1344 QSignalSpy buttonStateChangedSpy(pointer, &KWayland::Client::Pointer::buttonStateChanged);
1345 QSignalSpy motionSpy(pointer, &KWayland::Client::Pointer::motion);
1354 decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
1355 surface->commit(KWayland::Client::Surface::CommitFlag::None);
1356 QVERIFY(surfaceConfigureRequestedSpy.wait());
1357 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1360 QCOMPARE(window->hasPopupGrab(),
false);
1361 QVERIFY(window->isDecorated());
1364 QVERIFY(!exclusiveContains(window->frameGeometry(), QPoint(800, 800)));
1366 QVERIFY(enteredSpy.wait());
1368 quint32 timestamp = 0;
1371 QVERIFY(buttonStateChangedSpy.wait());
1375 positioner->set_size(100, 50);
1376 positioner->set_anchor_rect(0, 0, 80, 20);
1377 positioner->set_anchor(Test::XdgPositioner::anchor_bottom_right);
1378 positioner->set_gravity(Test::XdgPositioner::gravity_bottom_right);
1380 QVERIFY(popupSurface);
1381 Test::XdgPopup *popupShellSurface =
Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
1382 QVERIFY(popupShellSurface);
1386 QVERIFY(popupWindow);
1387 QVERIFY(popupWindow != window);
1388 QCOMPARE(window,
workspace()->activeWindow());
1389 QCOMPARE(popupWindow->transientFor(), window);
1390 QCOMPARE(popupWindow->pos(), window->mapFromLocal(QPoint(80, 20)));
1391 QCOMPARE(popupWindow->hasPopupGrab(),
true);
1394 input()->
pointer()->
warp(QPointF(window->frameGeometry().center().x(), window->y() + (window->height() - window->clientSize().height()) / 2));
1397 QVERIFY(doneReceivedSpy.wait());
1401void PointerInputTest::testWindowUnderCursorWhileButtonPressed()
1408 auto pointer = m_seat->createPointer(m_seat);
1410 QVERIFY(pointer->isValid());
1411 QSignalSpy enteredSpy(pointer, &KWayland::Client::Pointer::entered);
1412 QSignalSpy leftSpy(pointer, &KWayland::Client::Pointer::left);
1419 QVERIFY(shellSurface);
1420 render(surface.get());
1421 QVERIFY(windowAddedSpy.wait());
1426 QVERIFY(!exclusiveContains(window->frameGeometry(), QPoint(800, 800)));
1428 QVERIFY(enteredSpy.wait());
1430 quint32 timestamp = 0;
1435 positioner->set_size(99, 49);
1436 positioner->set_anchor_rect(0, 0, 1, 1);
1437 positioner->set_anchor(Test::XdgPositioner::anchor_bottom_right);
1438 positioner->set_gravity(Test::XdgPositioner::gravity_bottom_right);
1440 QVERIFY(popupSurface);
1441 Test::XdgPopup *popupShellSurface =
Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
1442 QVERIFY(popupShellSurface);
1443 render(popupSurface.get(), QSize(99, 49));
1444 QVERIFY(windowAddedSpy.wait());
1445 auto popupWindow = windowAddedSpy.last().first().value<
Window *>();
1446 QVERIFY(popupWindow);
1447 QVERIFY(popupWindow != window);
1451 QCOMPARE(leftSpy.count(), 0);
1455 QVERIFY(leftSpy.wait());
1456 QCOMPARE(leftSpy.count(), 1);
1457 QCOMPARE(enteredSpy.count(), 2);
1460void PointerInputTest::testConfineToScreenGeometry_data()
1462 QTest::addColumn<QPoint>(
"startPos");
1463 QTest::addColumn<QPoint>(
"targetPos");
1464 QTest::addColumn<QPoint>(
"expectedPos");
1475 QTest::newRow(
"move top-left - left screen") << QPoint(640, 512) << QPoint(-100, -100) << QPoint(0, 0);
1476 QTest::newRow(
"move top - left screen") << QPoint(640, 512) << QPoint(640, -100) << QPoint(640, 0);
1477 QTest::newRow(
"move top-right - left screen") << QPoint(640, 512) << QPoint(1380, -100) << QPoint(1380, 0);
1478 QTest::newRow(
"move right - left screen") << QPoint(640, 512) << QPoint(1380, 512) << QPoint(1380, 512);
1479 QTest::newRow(
"move bottom-right - left screen") << QPoint(640, 512) << QPoint(1380, 1124) << QPoint(1380, 1124);
1480 QTest::newRow(
"move bottom - left screen") << QPoint(640, 512) << QPoint(640, 1124) << QPoint(640, 1023);
1481 QTest::newRow(
"move bottom-left - left screen") << QPoint(640, 512) << QPoint(-100, 1124) << QPoint(0, 1023);
1482 QTest::newRow(
"move left - left screen") << QPoint(640, 512) << QPoint(-100, 512) << QPoint(0, 512);
1484 QTest::newRow(
"move top-left - top screen") << QPoint(1920, 512) << QPoint(1180, -100) << QPoint(1180, 0);
1485 QTest::newRow(
"move top - top screen") << QPoint(1920, 512) << QPoint(1920, -100) << QPoint(1920, 0);
1486 QTest::newRow(
"move top-right - top screen") << QPoint(1920, 512) << QPoint(2660, -100) << QPoint(2660, 0);
1487 QTest::newRow(
"move right - top screen") << QPoint(1920, 512) << QPoint(2660, 512) << QPoint(2660, 512);
1488 QTest::newRow(
"move bottom-right - top screen") << QPoint(1920, 512) << QPoint(2660, 1124) << QPoint(2660, 1023);
1489 QTest::newRow(
"move bottom - top screen") << QPoint(1920, 512) << QPoint(1920, 1124) << QPoint(1920, 1124);
1490 QTest::newRow(
"move bottom-left - top screen") << QPoint(1920, 512) << QPoint(1180, 1124) << QPoint(1280, 1124);
1491 QTest::newRow(
"move left - top screen") << QPoint(1920, 512) << QPoint(1180, 512) << QPoint(1180, 512);
1493 QTest::newRow(
"move top-left - right screen") << QPoint(3200, 512) << QPoint(2460, -100) << QPoint(2460, 0);
1494 QTest::newRow(
"move top - right screen") << QPoint(3200, 512) << QPoint(3200, -100) << QPoint(3200, 0);
1495 QTest::newRow(
"move top-right - right screen") << QPoint(3200, 512) << QPoint(3940, -100) << QPoint(3839, 0);
1496 QTest::newRow(
"move right - right screen") << QPoint(3200, 512) << QPoint(3940, 512) << QPoint(3839, 512);
1497 QTest::newRow(
"move bottom-right - right screen") << QPoint(3200, 512) << QPoint(3940, 1124) << QPoint(3839, 1023);
1498 QTest::newRow(
"move bottom - right screen") << QPoint(3200, 512) << QPoint(3200, 1124) << QPoint(3200, 1023);
1499 QTest::newRow(
"move bottom-left - right screen") << QPoint(3200, 512) << QPoint(2460, 1124) << QPoint(2460, 1124);
1500 QTest::newRow(
"move left - right screen") << QPoint(3200, 512) << QPoint(2460, 512) << QPoint(2460, 512);
1502 QTest::newRow(
"move top-left - bottom screen") << QPoint(1920, 1536) << QPoint(1180, 924) << QPoint(1180, 924);
1503 QTest::newRow(
"move top - bottom screen") << QPoint(1920, 1536) << QPoint(1920, 924) << QPoint(1920, 924);
1504 QTest::newRow(
"move top-right - bottom screen") << QPoint(1920, 1536) << QPoint(2660, 924) << QPoint(2660, 924);
1505 QTest::newRow(
"move right - bottom screen") << QPoint(1920, 1536) << QPoint(2660, 1536) << QPoint(2559, 1536);
1506 QTest::newRow(
"move bottom-right - bottom screen") << QPoint(1920, 1536) << QPoint(2660, 2148) << QPoint(2559, 2047);
1507 QTest::newRow(
"move bottom - bottom screen") << QPoint(1920, 1536) << QPoint(1920, 2148) << QPoint(1920, 2047);
1508 QTest::newRow(
"move bottom-left - bottom screen") << QPoint(1920, 1536) << QPoint(1180, 2148) << QPoint(1280, 2047);
1509 QTest::newRow(
"move left - bottom screen") << QPoint(1920, 1536) << QPoint(1180, 1536) << QPoint(1280, 1536);
1512void PointerInputTest::testConfineToScreenGeometry()
1518 const QList<QRect> geometries{
1519 QRect(0, 0, 1280, 1024),
1520 QRect(1280, 0, 1280, 1024),
1521 QRect(2560, 0, 1280, 1024),
1522 QRect(1280, 1024, 1280, 1024)};
1526 QCOMPARE(
outputs.count(), geometries.count());
1527 QCOMPARE(outputs[0]->geometry(), geometries.at(0));
1528 QCOMPARE(outputs[1]->geometry(), geometries.at(1));
1529 QCOMPARE(outputs[2]->geometry(), geometries.at(2));
1530 QCOMPARE(outputs[3]->geometry(), geometries.at(3));
1533 QFETCH(QPoint, startPos);
1538 QFETCH(QPoint, targetPos);
1541 QFETCH(QPoint, expectedPos);
1545void PointerInputTest::testResizeCursor_data()
1547 QTest::addColumn<Qt::Edges>(
"edges");
1548 QTest::addColumn<KWin::CursorShape>(
"cursorShape");
1560void PointerInputTest::testResizeCursor()
1565 KConfigGroup group = kwinApp()->config()->group(QStringLiteral(
"MouseBindings"));
1566 group.writeEntry(
"CommandAllKey",
"Meta");
1567 group.writeEntry(
"CommandAll3",
"Resize");
1574 const PlatformCursorImage arrowCursor = loadReferenceThemeCursor(Qt::ArrowCursor);
1575 QVERIFY(!arrowCursor.isNull());
1576 QCOMPARE(kwinApp()->cursorImage().image(), arrowCursor.image());
1577 QCOMPARE(kwinApp()->cursorImage().hotSpot(), arrowCursor.hotSpot());
1580 auto pointer = m_seat->createPointer(m_seat);
1582 QVERIFY(pointer->isValid());
1583 QSignalSpy enteredSpy(pointer, &KWayland::Client::Pointer::entered);
1587 QVERIFY(surface !=
nullptr);
1589 QVERIFY(shellSurface !=
nullptr);
1595 QFETCH(Qt::Edges, edges);
1597 if (edges & Qt::LeftEdge) {
1598 cursorPos.setX(window->frameGeometry().left());
1599 }
else if (edges & Qt::RightEdge) {
1600 cursorPos.setX(window->frameGeometry().right() - 1);
1602 cursorPos.setX(window->frameGeometry().center().x());
1605 if (edges & Qt::TopEdge) {
1606 cursorPos.setY(window->frameGeometry().top());
1607 }
else if (edges & Qt::BottomEdge) {
1608 cursorPos.setY(window->frameGeometry().bottom() - 1);
1610 cursorPos.setY(window->frameGeometry().center().y());
1616 QVERIFY(enteredSpy.wait());
1618 QVERIFY(cursorSurface);
1619 QSignalSpy cursorRenderedSpy(cursorSurface.get(), &KWayland::Client::Surface::frameRendered);
1621 cursorSurface->damage(arrowCursor.image().rect());
1622 cursorSurface->commit();
1623 pointer->setCursor(cursorSurface.get(), arrowCursor.hotSpot().toPoint());
1624 QVERIFY(cursorRenderedSpy.wait());
1630 QVERIFY(window->isInteractiveResize());
1633 const PlatformCursorImage resizeCursor = loadReferenceThemeCursor(cursorShape);
1634 QVERIFY(!resizeCursor.isNull());
1635 QCOMPARE(kwinApp()->cursorImage().image(), resizeCursor.image());
1636 QCOMPARE(kwinApp()->cursorImage().hotSpot(), resizeCursor.hotSpot());
1641 QVERIFY(!window->isInteractiveResize());
1643 QCOMPARE(kwinApp()->cursorImage().image(), arrowCursor.image());
1644 QCOMPARE(kwinApp()->cursorImage().hotSpot(), arrowCursor.hotSpot());
1647void PointerInputTest::testMoveCursor()
1652 KConfigGroup group = kwinApp()->config()->group(QStringLiteral(
"MouseBindings"));
1653 group.writeEntry(
"CommandAllKey",
"Meta");
1654 group.writeEntry(
"CommandAll1",
"Move");
1661 const PlatformCursorImage arrowCursor = loadReferenceThemeCursor(Qt::ArrowCursor);
1662 QVERIFY(!arrowCursor.isNull());
1663 QCOMPARE(kwinApp()->cursorImage().image(), arrowCursor.image());
1664 QCOMPARE(kwinApp()->cursorImage().hotSpot(), arrowCursor.hotSpot());
1667 auto pointer = m_seat->createPointer(m_seat);
1669 QVERIFY(pointer->isValid());
1670 QSignalSpy enteredSpy(pointer, &KWayland::Client::Pointer::entered);
1674 QVERIFY(surface !=
nullptr);
1676 QVERIFY(shellSurface !=
nullptr);
1684 QVERIFY(enteredSpy.wait());
1686 QVERIFY(cursorSurface);
1687 QSignalSpy cursorRenderedSpy(cursorSurface.get(), &KWayland::Client::Surface::frameRendered);
1689 cursorSurface->damage(arrowCursor.image().rect());
1690 cursorSurface->commit();
1691 pointer->setCursor(cursorSurface.get(), arrowCursor.hotSpot().toPoint());
1692 QVERIFY(cursorRenderedSpy.wait());
1698 QVERIFY(window->isInteractiveMove());
1700 const PlatformCursorImage sizeAllCursor = loadReferenceThemeCursor(Qt::SizeAllCursor);
1701 QVERIFY(!sizeAllCursor.isNull());
1702 QCOMPARE(kwinApp()->cursorImage().image(), sizeAllCursor.image());
1703 QCOMPARE(kwinApp()->cursorImage().hotSpot(), sizeAllCursor.hotSpot());
1708 QVERIFY(!window->isInteractiveMove());
1710 QCOMPARE(kwinApp()->cursorImage().image(), arrowCursor.image());
1711 QCOMPARE(kwinApp()->cursorImage().hotSpot(), arrowCursor.hotSpot());
1714void PointerInputTest::testHideShowCursor()
1739void PointerInputTest::testDefaultInputRegion()
1745 QVERIFY(surface !=
nullptr);
1747 QVERIFY(shellSurface !=
nullptr);
1756 shellSurface.reset();
1760void PointerInputTest::testEmptyInputRegion()
1766 QVERIFY(surface !=
nullptr);
1767 std::unique_ptr<KWayland::Client::Region> inputRegion(m_compositor->createRegion(QRegion()));
1768 surface->setInputRegion(inputRegion.get());
1770 QVERIFY(shellSurface !=
nullptr);
1779 shellSurface.reset();
1783void PointerInputTest::testUnfocusedModifiers()
1789 std::unique_ptr<KWayland::Client::Keyboard> keyboard(
Test::waylandSeat()->createKeyboard());
1793 QVERIFY(surface !=
nullptr);
1795 QVERIFY(shellSurface !=
nullptr);
1797 QVERIFY(waylandWindow);
1798 waylandWindow->move(QPoint(0, 0));
1802 QVERIFY(!xcb_connection_has_error(c.get()));
1803 const QRect windowGeometry(0, 0, 10, 10);
1804 xcb_window_t windowId = xcb_generate_id(c.get());
1805 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId,
rootWindow(),
1808 windowGeometry.width(),
1809 windowGeometry.height(),
1810 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0,
nullptr);
1811 xcb_size_hints_t hints = {};
1812 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
1813 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
1814 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
1815 xcb_map_window(c.get(), windowId);
1818 QVERIFY(windowCreatedSpy.wait());
1819 X11Window *x11window = windowCreatedSpy.last().first().value<X11Window *>();
1820 QVERIFY(waylandWindow);
1821 x11window->move(QPoint(10, 10));
1827 QCOMPARE(
waylandServer()->
seat()->focusedPointerSurface(), waylandWindow->surface());
1829 QSignalSpy spy(keyboard.get(), &KWayland::Client::Keyboard::modifiersChanged);
1831 QVERIFY(spy.wait());
1832 QCOMPARE(spy.last().at(0).toInt(), XCB_MOD_MASK_CONTROL);
1838 xcb_unmap_window(c.get(), windowId);
1839 xcb_destroy_window(c.get(), windowId);
1844 shellSurface.reset();
1850#include "pointer_input.moc"
Wrapper round Qt::CursorShape with extensions enums into a single entity.
void currentCursorChanged(Cursor *cursor)
void stopMouseInterception(Effect *effect)
virtual void defineCursor(Qt::CursorShape shape)
void startMouseInterception(Effect *effect, Qt::CursorShape shape)
bool borderlessMaximizedWindows
@ MouseUnrestrictedResize
Qt::KeyboardModifier commandAllModifier() const
bool isClickRaise() const
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)
Window * activeWindow() const
void activateWindow(Window *window, bool force=false)
void stackingOrderChanged()
void slotWindowMaximize()
void windowActivated(KWin::Window *)
static Workspace * self()
void windowAdded(KWin::Window *)
void lowerWindow(Window *window, bool nogroup=false)
QList< Output * > outputs() const
void setActiveOutput(Output *output)
void disableGlobalShortcutsForClient(bool disable)
#define WAYLANDTEST_MAIN(TestObject)
XdgPositioner * createXdgPositioner()
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)
XdgPopup * createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface, XdgPositioner *positioner, CreationSetup configureMode=CreationSetup::CreateAndConfigure, QObject *parent=nullptr)
void destroyWaylandConnection()
void setOutputConfig(const QList< QRect > &geometries)
KWayland::Client::Seat * seat
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())
KWayland::Client::Compositor * waylandCompositor()
void render(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32_Premultiplied)
KWayland::Client::Seat * waylandSeat()
void pointerButtonPressed(quint32 button, quint32 time)
XcbConnectionPtr createX11Connection()
QList< KWayland::Client::Output * > outputs
void pointerMotion(const QPointF &position, quint32 time)
std::unique_ptr< KWayland::Client::Surface > createSurface()
KWayland::Client::ShmPool * waylandShmPool()
XdgToplevel * createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent=nullptr)
XdgToplevelDecorationV1 * createXdgToplevelDecorationV1(XdgToplevel *toplevel, QObject *parent=nullptr)
bool waitForWaylandPointer()
void pointerButtonReleased(quint32 button, quint32 time)
CursorShapeDeviceV1 * createCursorShapeDeviceV1(KWayland::Client::Pointer *pointer)
void flushWaylandConnection()
std::unique_ptr< xcb_connection_t, XcbConnectionDeleter > XcbConnectionPtr
bool waitForWindowClosed(Window *window)
KWIN_EXPORT xcb_window_t rootWindow()
@ MaximizeRestore
The window is not maximized in any direction.
@ MaximizeFull
Equal to MaximizeVertical | MaximizeHorizontal.
WaylandServer * waylandServer()
InputRedirection * input()