File indexing completed on 2024-11-10 04:56:05
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(®istry, &KWayland::Client::Registry::interfacesAnnounced); 0067 QSignalSpy outputAnnouncedSpy(®istry, &KWayland::Client::Registry::outputAnnounced); 0068 QSignalSpy outputRemovedSpy(®istry, &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"