File indexing completed on 2024-05-12 05:30:41

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 "pointer_input.h"
0014 #include "wayland_server.h"
0015 #include "workspace.h"
0016 
0017 #include <KWayland/Client/output.h>
0018 #include <KWayland/Client/registry.h>
0019 #include <KWayland/Client/xdgoutput.h>
0020 
0021 using namespace KWin;
0022 
0023 static const QString s_socketName = QStringLiteral("wayland_test_kwin_screen_changes-0");
0024 
0025 class ScreenChangesTest : public QObject
0026 {
0027     Q_OBJECT
0028 private Q_SLOTS:
0029     void initTestCase();
0030     void init();
0031     void cleanup();
0032 
0033     void testScreenAddRemove();
0034 };
0035 
0036 void ScreenChangesTest::initTestCase()
0037 {
0038     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0039     QVERIFY(waylandServer()->init(s_socketName));
0040     Test::setOutputConfig({QRect(0, 0, 1280, 1024)});
0041 
0042     kwinApp()->start();
0043     QVERIFY(applicationStartedSpy.wait());
0044     setenv("QT_QPA_PLATFORM", "wayland", true);
0045 }
0046 
0047 void ScreenChangesTest::init()
0048 {
0049     QVERIFY(Test::setupWaylandConnection());
0050 
0051     workspace()->setActiveOutput(QPoint(640, 512));
0052     KWin::input()->pointer()->warp(QPoint(640, 512));
0053 }
0054 
0055 void ScreenChangesTest::cleanup()
0056 {
0057     Test::destroyWaylandConnection();
0058 }
0059 
0060 void ScreenChangesTest::testScreenAddRemove()
0061 {
0062     // this test verifies that when a new screen is added it gets synced to Wayland
0063 
0064     // first create a registry to get signals about Outputs announced/removed
0065     KWayland::Client::Registry registry;
0066     QSignalSpy allAnnounced(&registry, &KWayland::Client::Registry::interfacesAnnounced);
0067     QSignalSpy outputAnnouncedSpy(&registry, &KWayland::Client::Registry::outputAnnounced);
0068     QSignalSpy outputRemovedSpy(&registry, &KWayland::Client::Registry::outputRemoved);
0069     registry.create(Test::waylandConnection());
0070     QVERIFY(registry.isValid());
0071     registry.setup();
0072     QVERIFY(allAnnounced.wait());
0073     const auto xdgOMData = registry.interface(KWayland::Client::Registry::Interface::XdgOutputUnstableV1);
0074     auto xdgOutputManager = registry.createXdgOutputManager(xdgOMData.name, xdgOMData.version);
0075 
0076     // should be one output
0077     QCOMPARE(workspace()->outputs().count(), 1);
0078     QCOMPARE(outputAnnouncedSpy.count(), 1);
0079     const quint32 firstOutputId = outputAnnouncedSpy.first().first().value<quint32>();
0080     QVERIFY(firstOutputId != 0u);
0081     outputAnnouncedSpy.clear();
0082 
0083     // let's announce a new output
0084     const QList<QRect> geometries{QRect(0, 0, 1280, 1024), QRect(1280, 0, 1280, 1024)};
0085     Test::setOutputConfig(geometries);
0086     auto outputs = workspace()->outputs();
0087     QCOMPARE(outputs.count(), 2);
0088     QCOMPARE(outputs[0]->geometry(), geometries[0]);
0089     QCOMPARE(outputs[1]->geometry(), geometries[1]);
0090 
0091     // this should result in it getting announced, two new outputs are added...
0092     QVERIFY(outputAnnouncedSpy.wait());
0093     if (outputAnnouncedSpy.count() < 2) {
0094         QVERIFY(outputAnnouncedSpy.wait());
0095     }
0096     QCOMPARE(outputAnnouncedSpy.count(), 2);
0097     // ... and afterward the previous output gets removed
0098     if (outputRemovedSpy.isEmpty()) {
0099         QVERIFY(outputRemovedSpy.wait());
0100     }
0101     QCOMPARE(outputRemovedSpy.count(), 1);
0102     QCOMPARE(outputRemovedSpy.first().first().value<quint32>(), firstOutputId);
0103 
0104     // let's wait a little bit to ensure we don't get more events
0105     QTest::qWait(100);
0106     QCOMPARE(outputAnnouncedSpy.count(), 2);
0107     QCOMPARE(outputRemovedSpy.count(), 1);
0108 
0109     // let's create the output objects to ensure they are correct
0110     std::unique_ptr<KWayland::Client::Output> o1(registry.createOutput(outputAnnouncedSpy.first().first().value<quint32>(), outputAnnouncedSpy.first().last().value<quint32>()));
0111     QVERIFY(o1->isValid());
0112     QSignalSpy o1ChangedSpy(o1.get(), &KWayland::Client::Output::changed);
0113     QVERIFY(o1ChangedSpy.wait());
0114     KWin::Output *serverOutput1 = kwinApp()->outputBackend()->findOutput(o1->name()); // use wl_output.name to find the compositor side output
0115     QCOMPARE(o1->globalPosition(), serverOutput1->geometry().topLeft());
0116     QCOMPARE(o1->pixelSize(), serverOutput1->modeSize());
0117     std::unique_ptr<KWayland::Client::Output> o2(registry.createOutput(outputAnnouncedSpy.last().first().value<quint32>(), outputAnnouncedSpy.last().last().value<quint32>()));
0118     QVERIFY(o2->isValid());
0119     QSignalSpy o2ChangedSpy(o2.get(), &KWayland::Client::Output::changed);
0120     QVERIFY(o2ChangedSpy.wait());
0121     KWin::Output *serverOutput2 = kwinApp()->outputBackend()->findOutput(o2->name()); // use wl_output.name to find the compositor side output
0122     QCOMPARE(o2->globalPosition(), serverOutput2->geometry().topLeft());
0123     QCOMPARE(o2->pixelSize(), serverOutput2->modeSize());
0124 
0125     // and check XDGOutput is synced
0126     std::unique_ptr<KWayland::Client::XdgOutput> xdgO1(xdgOutputManager->getXdgOutput(o1.get()));
0127     QSignalSpy xdgO1ChangedSpy(xdgO1.get(), &KWayland::Client::XdgOutput::changed);
0128     QVERIFY(xdgO1ChangedSpy.wait());
0129     QCOMPARE(xdgO1->logicalPosition(), serverOutput1->geometry().topLeft());
0130     QCOMPARE(xdgO1->logicalSize(), serverOutput1->geometry().size());
0131     std::unique_ptr<KWayland::Client::XdgOutput> xdgO2(xdgOutputManager->getXdgOutput(o2.get()));
0132     QSignalSpy xdgO2ChangedSpy(xdgO2.get(), &KWayland::Client::XdgOutput::changed);
0133     QVERIFY(xdgO2ChangedSpy.wait());
0134     QCOMPARE(xdgO2->logicalPosition(), serverOutput2->geometry().topLeft());
0135     QCOMPARE(xdgO2->logicalSize(), serverOutput2->geometry().size());
0136 
0137     QVERIFY(xdgO1->name().startsWith("Virtual-"));
0138     QVERIFY(xdgO1->name() != xdgO2->name());
0139     QVERIFY(!xdgO1->description().isEmpty());
0140 
0141     // now let's try to remove one output again
0142     outputAnnouncedSpy.clear();
0143     outputRemovedSpy.clear();
0144 
0145     QSignalSpy o1RemovedSpy(o1.get(), &KWayland::Client::Output::removed);
0146     QSignalSpy o2RemovedSpy(o2.get(), &KWayland::Client::Output::removed);
0147 
0148     const QList<QRect> geometries2{QRect(0, 0, 1280, 1024)};
0149     Test::setOutputConfig(geometries2);
0150     outputs = workspace()->outputs();
0151     QCOMPARE(outputs.count(), 1);
0152     QCOMPARE(outputs[0]->geometry(), geometries2.at(0));
0153 
0154     QVERIFY(outputAnnouncedSpy.wait());
0155     QCOMPARE(outputAnnouncedSpy.count(), 1);
0156     if (o1RemovedSpy.isEmpty()) {
0157         QVERIFY(o1RemovedSpy.wait());
0158     }
0159     if (o2RemovedSpy.isEmpty()) {
0160         QVERIFY(o2RemovedSpy.wait());
0161     }
0162     // now wait a bit to ensure we don't get more events
0163     QTest::qWait(100);
0164     QCOMPARE(outputAnnouncedSpy.count(), 1);
0165     QCOMPARE(o1RemovedSpy.count(), 1);
0166     QCOMPARE(o2RemovedSpy.count(), 1);
0167     QCOMPARE(outputRemovedSpy.count(), 2);
0168 }
0169 
0170 WAYLANDTEST_MAIN(ScreenChangesTest)
0171 #include "screen_changes_test.moc"