File indexing completed on 2024-11-10 04:55:58
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 "core/output.h" 0012 #include "pointer_input.h" 0013 #include "wayland/seat.h" 0014 #include "wayland_server.h" 0015 #include "window.h" 0016 #include "workspace.h" 0017 0018 #include <KWayland/Client/compositor.h> 0019 #include <KWayland/Client/connection_thread.h> 0020 #include <KWayland/Client/event_queue.h> 0021 #include <KWayland/Client/pointer.h> 0022 #include <KWayland/Client/registry.h> 0023 #include <KWayland/Client/seat.h> 0024 #include <KWayland/Client/shm_pool.h> 0025 #include <KWayland/Client/surface.h> 0026 0027 #include <QSignalSpy> 0028 0029 namespace KWin 0030 { 0031 0032 static const QString s_socketName = QStringLiteral("wayland_test_kwin_input_stacking_order-0"); 0033 0034 class InputStackingOrderTest : public QObject 0035 { 0036 Q_OBJECT 0037 private Q_SLOTS: 0038 void initTestCase(); 0039 void init(); 0040 void cleanup(); 0041 void testPointerFocusUpdatesOnStackingOrderChange(); 0042 0043 private: 0044 void render(KWayland::Client::Surface *surface); 0045 }; 0046 0047 void InputStackingOrderTest::initTestCase() 0048 { 0049 qRegisterMetaType<KWin::Window *>(); 0050 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); 0051 QVERIFY(waylandServer()->init(s_socketName)); 0052 Test::setOutputConfig({ 0053 QRect(0, 0, 1280, 1024), 0054 QRect(1280, 0, 1280, 1024), 0055 }); 0056 0057 kwinApp()->start(); 0058 QVERIFY(applicationStartedSpy.wait()); 0059 const auto outputs = workspace()->outputs(); 0060 QCOMPARE(outputs.count(), 2); 0061 QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024)); 0062 QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024)); 0063 setenv("QT_QPA_PLATFORM", "wayland", true); 0064 } 0065 0066 void InputStackingOrderTest::init() 0067 { 0068 QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat)); 0069 QVERIFY(Test::waitForWaylandPointer()); 0070 0071 workspace()->setActiveOutput(QPoint(640, 512)); 0072 input()->pointer()->warp(QPoint(640, 512)); 0073 } 0074 0075 void InputStackingOrderTest::cleanup() 0076 { 0077 Test::destroyWaylandConnection(); 0078 } 0079 0080 void InputStackingOrderTest::render(KWayland::Client::Surface *surface) 0081 { 0082 Test::render(surface, QSize(100, 50), Qt::blue); 0083 Test::flushWaylandConnection(); 0084 } 0085 0086 void InputStackingOrderTest::testPointerFocusUpdatesOnStackingOrderChange() 0087 { 0088 // this test creates two windows which overlap 0089 // the pointer is in the overlapping area which means the top most window has focus 0090 // as soon as the top most window gets lowered the window should lose focus and the 0091 // other window should gain focus without a mouse event in between 0092 0093 // create pointer and signal spy for enter and leave signals 0094 auto pointer = Test::waylandSeat()->createPointer(Test::waylandSeat()); 0095 QVERIFY(pointer); 0096 QVERIFY(pointer->isValid()); 0097 QSignalSpy enteredSpy(pointer, &KWayland::Client::Pointer::entered); 0098 QSignalSpy leftSpy(pointer, &KWayland::Client::Pointer::left); 0099 0100 // now create the two windows and make them overlap 0101 QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded); 0102 std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface(); 0103 QVERIFY(surface1); 0104 Test::XdgToplevel *shellSurface1 = Test::createXdgToplevelSurface(surface1.get(), surface1.get()); 0105 QVERIFY(shellSurface1); 0106 render(surface1.get()); 0107 QVERIFY(windowAddedSpy.wait()); 0108 Window *window1 = workspace()->activeWindow(); 0109 QVERIFY(window1); 0110 0111 std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface(); 0112 QVERIFY(surface2); 0113 Test::XdgToplevel *shellSurface2 = Test::createXdgToplevelSurface(surface2.get(), surface2.get()); 0114 QVERIFY(shellSurface2); 0115 render(surface2.get()); 0116 QVERIFY(windowAddedSpy.wait()); 0117 0118 Window *window2 = workspace()->activeWindow(); 0119 QVERIFY(window2); 0120 QVERIFY(window1 != window2); 0121 0122 // now make windows overlap 0123 window2->move(window1->pos()); 0124 QCOMPARE(window1->frameGeometry(), window2->frameGeometry()); 0125 0126 // enter 0127 Test::pointerMotion(QPointF(25, 25), 1); 0128 QVERIFY(enteredSpy.wait()); 0129 QCOMPARE(enteredSpy.count(), 1); 0130 // window 2 should have focus 0131 QCOMPARE(pointer->enteredSurface(), surface2.get()); 0132 // also on the server 0133 QCOMPARE(waylandServer()->seat()->focusedPointerSurface(), window2->surface()); 0134 0135 // raise window 1 above window 2 0136 QVERIFY(leftSpy.isEmpty()); 0137 workspace()->raiseWindow(window1); 0138 // should send leave to window2 0139 QVERIFY(leftSpy.wait()); 0140 QCOMPARE(leftSpy.count(), 1); 0141 // and an enter to window1 0142 QCOMPARE(enteredSpy.count(), 2); 0143 QCOMPARE(pointer->enteredSurface(), surface1.get()); 0144 QCOMPARE(waylandServer()->seat()->focusedPointerSurface(), window1->surface()); 0145 0146 // let's destroy window1, that should pass focus to window2 again 0147 QSignalSpy windowClosedSpy(window1, &Window::closed); 0148 surface1.reset(); 0149 QVERIFY(windowClosedSpy.wait()); 0150 QVERIFY(enteredSpy.wait()); 0151 QCOMPARE(enteredSpy.count(), 3); 0152 QCOMPARE(pointer->enteredSurface(), surface2.get()); 0153 QCOMPARE(waylandServer()->seat()->focusedPointerSurface(), window2->surface()); 0154 } 0155 0156 } 0157 0158 WAYLANDTEST_MAIN(KWin::InputStackingOrderTest) 0159 #include "input_stacking_order.moc"