File indexing completed on 2024-05-05 17:35:47

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