Changeset View
Changeset View
Standalone View
Standalone View
autotests/integration/pointer_input.cpp
Show All 21 Lines | |||||
22 | #include "abstract_client.h" | 22 | #include "abstract_client.h" | ||
23 | #include "cursor.h" | 23 | #include "cursor.h" | ||
24 | #include "deleted.h" | 24 | #include "deleted.h" | ||
25 | #include "effects.h" | 25 | #include "effects.h" | ||
26 | #include "pointer_input.h" | 26 | #include "pointer_input.h" | ||
27 | #include "options.h" | 27 | #include "options.h" | ||
28 | #include "screenedge.h" | 28 | #include "screenedge.h" | ||
29 | #include "screens.h" | 29 | #include "screens.h" | ||
30 | #include "wayland_cursor_theme.h" | ||||
30 | #include "wayland_server.h" | 31 | #include "wayland_server.h" | ||
31 | #include "workspace.h" | 32 | #include "workspace.h" | ||
32 | #include "shell_client.h" | 33 | #include "shell_client.h" | ||
33 | #include <kwineffects.h> | 34 | #include <kwineffects.h> | ||
34 | 35 | | |||
36 | #include <KWayland/Client/buffer.h> | ||||
35 | #include <KWayland/Client/connection_thread.h> | 37 | #include <KWayland/Client/connection_thread.h> | ||
36 | #include <KWayland/Client/compositor.h> | 38 | #include <KWayland/Client/compositor.h> | ||
37 | #include <KWayland/Client/pointer.h> | 39 | #include <KWayland/Client/pointer.h> | ||
38 | #include <KWayland/Client/shell.h> | 40 | #include <KWayland/Client/shell.h> | ||
39 | #include <KWayland/Client/seat.h> | 41 | #include <KWayland/Client/seat.h> | ||
40 | #include <KWayland/Client/server_decoration.h> | 42 | #include <KWayland/Client/server_decoration.h> | ||
41 | #include <KWayland/Client/shm_pool.h> | 43 | #include <KWayland/Client/shm_pool.h> | ||
42 | #include <KWayland/Client/surface.h> | 44 | #include <KWayland/Client/surface.h> | ||
43 | 45 | | |||
46 | #include <KWayland/Server/buffer_interface.h> | ||||
47 | #include <KWayland/Server/clientconnection.h> | ||||
44 | #include <KWayland/Server/seat_interface.h> | 48 | #include <KWayland/Server/seat_interface.h> | ||
45 | 49 | | |||
50 | #include <wayland-cursor.h> | ||||
51 | | ||||
46 | #include <linux/input.h> | 52 | #include <linux/input.h> | ||
47 | 53 | | |||
48 | namespace KWin | 54 | namespace KWin | ||
49 | { | 55 | { | ||
50 | 56 | | |||
57 | template <typename T> | ||||
58 | PlatformCursorImage loadReferenceThemeCursor(const T &shape) | ||||
59 | { | ||||
60 | if (!waylandServer()->internalShmPool()) { | ||||
61 | return PlatformCursorImage(); | ||||
62 | } | ||||
63 | | ||||
64 | QScopedPointer<WaylandCursorTheme> cursorTheme; | ||||
65 | cursorTheme.reset(new WaylandCursorTheme(waylandServer()->internalShmPool())); | ||||
66 | | ||||
67 | wl_cursor_image *cursor = cursorTheme->get(shape); | ||||
68 | if (!cursor) { | ||||
69 | return PlatformCursorImage(); | ||||
70 | } | ||||
71 | | ||||
72 | wl_buffer *b = wl_cursor_image_get_buffer(cursor); | ||||
73 | if (!b) { | ||||
74 | return PlatformCursorImage(); | ||||
75 | } | ||||
76 | | ||||
77 | waylandServer()->internalClientConection()->flush(); | ||||
78 | waylandServer()->dispatch(); | ||||
79 | | ||||
80 | auto buffer = KWayland::Server::BufferInterface::get( | ||||
81 | waylandServer()->internalConnection()->getResource( | ||||
82 | KWayland::Client::Buffer::getId(b))); | ||||
83 | if (!buffer) { | ||||
84 | return PlatformCursorImage{}; | ||||
85 | } | ||||
86 | | ||||
87 | const qreal scale = screens()->maxScale(); | ||||
88 | QImage image = buffer->data().copy(); | ||||
89 | image.setDevicePixelRatio(scale); | ||||
90 | | ||||
91 | const QPoint hotSpot( | ||||
92 | qRound(cursor->hotspot_x / scale), | ||||
93 | qRound(cursor->hotspot_y / scale) | ||||
94 | ); | ||||
95 | | ||||
96 | return PlatformCursorImage(image, hotSpot); | ||||
97 | } | ||||
98 | | ||||
51 | static const QString s_socketName = QStringLiteral("wayland_test_kwin_pointer_input-0"); | 99 | static const QString s_socketName = QStringLiteral("wayland_test_kwin_pointer_input-0"); | ||
52 | 100 | | |||
53 | class PointerInputTest : public QObject | 101 | class PointerInputTest : public QObject | ||
54 | { | 102 | { | ||
55 | Q_OBJECT | 103 | Q_OBJECT | ||
56 | private Q_SLOTS: | 104 | private Q_SLOTS: | ||
57 | void initTestCase(); | 105 | void initTestCase(); | ||
58 | void init(); | 106 | void init(); | ||
Show All 16 Lines | |||||
75 | void testMouseActionActiveWindow(); | 123 | void testMouseActionActiveWindow(); | ||
76 | void testCursorImage(); | 124 | void testCursorImage(); | ||
77 | void testEffectOverrideCursorImage(); | 125 | void testEffectOverrideCursorImage(); | ||
78 | void testPopup(); | 126 | void testPopup(); | ||
79 | void testDecoCancelsPopup(); | 127 | void testDecoCancelsPopup(); | ||
80 | void testWindowUnderCursorWhileButtonPressed(); | 128 | void testWindowUnderCursorWhileButtonPressed(); | ||
81 | void testConfineToScreenGeometry_data(); | 129 | void testConfineToScreenGeometry_data(); | ||
82 | void testConfineToScreenGeometry(); | 130 | void testConfineToScreenGeometry(); | ||
131 | void testResizeCursor_data(); | ||||
132 | void testResizeCursor(); | ||||
133 | void testMoveCursor(); | ||||
83 | 134 | | |||
84 | private: | 135 | private: | ||
85 | void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50)); | 136 | void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50)); | ||
86 | KWayland::Client::Compositor *m_compositor = nullptr; | 137 | KWayland::Client::Compositor *m_compositor = nullptr; | ||
87 | KWayland::Client::Seat *m_seat = nullptr; | 138 | KWayland::Client::Seat *m_seat = nullptr; | ||
88 | KWayland::Client::Shell *m_shell = nullptr; | 139 | KWayland::Client::Shell *m_shell = nullptr; | ||
89 | }; | 140 | }; | ||
90 | 141 | | |||
▲ Show 20 Lines • Show All 1316 Lines • ▼ Show 20 Line(s) | 1428 | { | |||
1407 | // perform movement | 1458 | // perform movement | ||
1408 | QFETCH(QPoint, targetPos); | 1459 | QFETCH(QPoint, targetPos); | ||
1409 | kwinApp()->platform()->pointerMotion(targetPos, 1); | 1460 | kwinApp()->platform()->pointerMotion(targetPos, 1); | ||
1410 | 1461 | | |||
1411 | QFETCH(QPoint, expectedPos); | 1462 | QFETCH(QPoint, expectedPos); | ||
1412 | QCOMPARE(Cursor::pos(), expectedPos); | 1463 | QCOMPARE(Cursor::pos(), expectedPos); | ||
1413 | } | 1464 | } | ||
1414 | 1465 | | |||
1466 | void PointerInputTest::testResizeCursor_data() | ||||
1467 | { | ||||
1468 | QTest::addColumn<Qt::Edges>("edges"); | ||||
1469 | QTest::addColumn<KWin::CursorShape>("cursorShape"); | ||||
1470 | | ||||
1471 | QTest::newRow("top-left") << Qt::Edges(Qt::TopEdge | Qt::LeftEdge) << CursorShape(ExtendedCursor::SizeNorthWest); | ||||
1472 | QTest::newRow("top") << Qt::Edges(Qt::TopEdge) << CursorShape(ExtendedCursor::SizeNorth); | ||||
1473 | QTest::newRow("top-right") << Qt::Edges(Qt::TopEdge | Qt::RightEdge) << CursorShape(ExtendedCursor::SizeNorthEast); | ||||
1474 | QTest::newRow("right") << Qt::Edges(Qt::RightEdge) << CursorShape(ExtendedCursor::SizeEast); | ||||
1475 | QTest::newRow("bottom-right") << Qt::Edges(Qt::BottomEdge | Qt::RightEdge) << CursorShape(ExtendedCursor::SizeSouthEast); | ||||
1476 | QTest::newRow("bottom") << Qt::Edges(Qt::BottomEdge) << CursorShape(ExtendedCursor::SizeSouth); | ||||
1477 | QTest::newRow("bottom-left") << Qt::Edges(Qt::BottomEdge | Qt::LeftEdge) << CursorShape(ExtendedCursor::SizeSouthWest); | ||||
1478 | QTest::newRow("left") << Qt::Edges(Qt::LeftEdge) << CursorShape(ExtendedCursor::SizeWest); | ||||
1479 | } | ||||
1480 | | ||||
1481 | void PointerInputTest::testResizeCursor() | ||||
1482 | { | ||||
1483 | // this test verifies that the cursor has correct shape during resize operation | ||||
1484 | | ||||
1485 | // first modify the config for this run | ||||
1486 | KConfigGroup group = kwinApp()->config()->group("MouseBindings"); | ||||
1487 | group.writeEntry("CommandAllKey", "Alt"); | ||||
1488 | group.writeEntry("CommandAll3", "Resize"); | ||||
1489 | group.sync(); | ||||
1490 | workspace()->slotReconfigure(); | ||||
1491 | QCOMPARE(options->commandAllModifier(), Qt::AltModifier); | ||||
1492 | QCOMPARE(options->commandAll3(), Options::MouseUnrestrictedResize); | ||||
1493 | | ||||
1494 | // create a test client | ||||
1495 | using namespace KWayland::Client; | ||||
1496 | QScopedPointer<Surface> surface(Test::createSurface()); | ||||
1497 | QVERIFY(!surface.isNull()); | ||||
1498 | QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data())); | ||||
1499 | QVERIFY(!shellSurface.isNull()); | ||||
1500 | ShellClient *c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); | ||||
1501 | QVERIFY(c); | ||||
1502 | | ||||
1503 | // move the cursor to the test position | ||||
1504 | QPoint cursorPos; | ||||
1505 | QFETCH(Qt::Edges, edges); | ||||
1506 | | ||||
1507 | if (edges & Qt::LeftEdge) { | ||||
1508 | cursorPos.setX(c->geometry().left()); | ||||
1509 | } else if (edges & Qt::RightEdge) { | ||||
1510 | cursorPos.setX(c->geometry().right()); | ||||
1511 | } else { | ||||
1512 | cursorPos.setX(c->geometry().center().x()); | ||||
1513 | } | ||||
1514 | | ||||
1515 | if (edges & Qt::TopEdge) { | ||||
1516 | cursorPos.setY(c->geometry().top()); | ||||
1517 | } else if (edges & Qt::BottomEdge) { | ||||
1518 | cursorPos.setY(c->geometry().bottom()); | ||||
1519 | } else { | ||||
1520 | cursorPos.setY(c->geometry().center().y()); | ||||
1521 | } | ||||
1522 | | ||||
1523 | Cursor::setPos(cursorPos); | ||||
1524 | | ||||
1525 | const PlatformCursorImage arrowCursor = loadReferenceThemeCursor(Qt::ArrowCursor); | ||||
1526 | QVERIFY(!arrowCursor.image().isNull()); | ||||
1527 | QCOMPARE(kwinApp()->platform()->cursorImage().image(), arrowCursor.image()); | ||||
1528 | QCOMPARE(kwinApp()->platform()->cursorImage().hotSpot(), arrowCursor.hotSpot()); | ||||
1529 | | ||||
1530 | // start resizing the client | ||||
1531 | int timestamp = 1; | ||||
1532 | kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTALT, timestamp++); | ||||
1533 | kwinApp()->platform()->pointerButtonPressed(BTN_RIGHT, timestamp++); | ||||
1534 | QVERIFY(c->isResize()); | ||||
1535 | | ||||
1536 | QFETCH(KWin::CursorShape, cursorShape); | ||||
1537 | const PlatformCursorImage resizeCursor = loadReferenceThemeCursor(cursorShape); | ||||
1538 | QVERIFY(!resizeCursor.image().isNull()); | ||||
1539 | QCOMPARE(kwinApp()->platform()->cursorImage().image(), resizeCursor.image()); | ||||
1540 | QCOMPARE(kwinApp()->platform()->cursorImage().hotSpot(), resizeCursor.hotSpot()); | ||||
1541 | | ||||
1542 | // finish resizing the client | ||||
1543 | kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTALT, timestamp++); | ||||
1544 | kwinApp()->platform()->pointerButtonReleased(BTN_RIGHT, timestamp++); | ||||
1545 | QVERIFY(!c->isResize()); | ||||
1546 | | ||||
1547 | QCOMPARE(kwinApp()->platform()->cursorImage().image(), arrowCursor.image()); | ||||
1548 | QCOMPARE(kwinApp()->platform()->cursorImage().hotSpot(), arrowCursor.hotSpot()); | ||||
1549 | } | ||||
1550 | | ||||
1551 | void PointerInputTest::testMoveCursor() | ||||
1552 | { | ||||
1553 | // this test verifies that the cursor has correct shape during move operation | ||||
1554 | | ||||
1555 | // first modify the config for this run | ||||
1556 | KConfigGroup group = kwinApp()->config()->group("MouseBindings"); | ||||
1557 | group.writeEntry("CommandAllKey", "Alt"); | ||||
1558 | group.writeEntry("CommandAll1", "Move"); | ||||
1559 | group.sync(); | ||||
1560 | workspace()->slotReconfigure(); | ||||
1561 | QCOMPARE(options->commandAllModifier(), Qt::AltModifier); | ||||
1562 | QCOMPARE(options->commandAll1(), Options::MouseUnrestrictedMove); | ||||
1563 | | ||||
1564 | // create a test client | ||||
1565 | using namespace KWayland::Client; | ||||
1566 | QScopedPointer<Surface> surface(Test::createSurface()); | ||||
1567 | QVERIFY(!surface.isNull()); | ||||
1568 | QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data())); | ||||
1569 | QVERIFY(!shellSurface.isNull()); | ||||
1570 | ShellClient *c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); | ||||
1571 | QVERIFY(c); | ||||
1572 | | ||||
1573 | // move cursor to the test position | ||||
1574 | Cursor::setPos(c->geometry().center()); | ||||
1575 | | ||||
1576 | const PlatformCursorImage arrowCursor = loadReferenceThemeCursor(Qt::ArrowCursor); | ||||
1577 | QVERIFY(!arrowCursor.image().isNull()); | ||||
1578 | QCOMPARE(kwinApp()->platform()->cursorImage().image(), arrowCursor.image()); | ||||
1579 | QCOMPARE(kwinApp()->platform()->cursorImage().hotSpot(), arrowCursor.hotSpot()); | ||||
1580 | | ||||
1581 | // start moving the client | ||||
1582 | int timestamp = 1; | ||||
1583 | kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTALT, timestamp++); | ||||
1584 | kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++); | ||||
1585 | QVERIFY(c->isMove()); | ||||
1586 | | ||||
1587 | const PlatformCursorImage sizeAllCursor = loadReferenceThemeCursor(Qt::SizeAllCursor); | ||||
1588 | QVERIFY(!sizeAllCursor.image().isNull()); | ||||
1589 | QCOMPARE(kwinApp()->platform()->cursorImage().image(), sizeAllCursor.image()); | ||||
1590 | QCOMPARE(kwinApp()->platform()->cursorImage().hotSpot(), sizeAllCursor.hotSpot()); | ||||
1591 | | ||||
1592 | // finish moving the client | ||||
1593 | kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTALT, timestamp++); | ||||
1594 | kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++); | ||||
1595 | QVERIFY(!c->isMove()); | ||||
1596 | | ||||
1597 | QCOMPARE(kwinApp()->platform()->cursorImage().image(), arrowCursor.image()); | ||||
1598 | QCOMPARE(kwinApp()->platform()->cursorImage().hotSpot(), arrowCursor.hotSpot()); | ||||
1599 | } | ||||
1600 | | ||||
1415 | } | 1601 | } | ||
1416 | 1602 | | |||
1417 | WAYLANDTEST_MAIN(KWin::PointerInputTest) | 1603 | WAYLANDTEST_MAIN(KWin::PointerInputTest) | ||
1418 | #include "pointer_input.moc" | 1604 | #include "pointer_input.moc" |