KWin
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
screen_changes_test.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "kwin_wayland_test.h"
10
11#include "core/output.h"
12#include "core/outputbackend.h"
13#include "pointer_input.h"
14#include "wayland_server.h"
15#include "workspace.h"
16
17#include <KWayland/Client/output.h>
18#include <KWayland/Client/registry.h>
19#include <KWayland/Client/xdgoutput.h>
20
21using namespace KWin;
22
23static const QString s_socketName = QStringLiteral("wayland_test_kwin_screen_changes-0");
24
25class ScreenChangesTest : public QObject
26{
27 Q_OBJECT
28private Q_SLOTS:
29 void initTestCase();
30 void init();
31 void cleanup();
32
33 void testScreenAddRemove();
34};
35
36void ScreenChangesTest::initTestCase()
37{
38 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
39 QVERIFY(waylandServer()->init(s_socketName));
40 Test::setOutputConfig({QRect(0, 0, 1280, 1024)});
41
42 kwinApp()->start();
43 QVERIFY(applicationStartedSpy.wait());
44 setenv("QT_QPA_PLATFORM", "wayland", true);
45}
46
47void ScreenChangesTest::init()
48{
50
51 workspace()->setActiveOutput(QPoint(640, 512));
52 KWin::input()->pointer()->warp(QPoint(640, 512));
53}
54
55void ScreenChangesTest::cleanup()
56{
58}
59
60void ScreenChangesTest::testScreenAddRemove()
61{
62 // this test verifies that when a new screen is added it gets synced to Wayland
63
64 // first create a registry to get signals about Outputs announced/removed
65 KWayland::Client::Registry registry;
66 QSignalSpy allAnnounced(&registry, &KWayland::Client::Registry::interfacesAnnounced);
67 QSignalSpy outputAnnouncedSpy(&registry, &KWayland::Client::Registry::outputAnnounced);
68 QSignalSpy outputRemovedSpy(&registry, &KWayland::Client::Registry::outputRemoved);
70 QVERIFY(registry.isValid());
71 registry.setup();
72 QVERIFY(allAnnounced.wait());
73 const auto xdgOMData = registry.interface(KWayland::Client::Registry::Interface::XdgOutputUnstableV1);
74 auto xdgOutputManager = registry.createXdgOutputManager(xdgOMData.name, xdgOMData.version);
75
76 // should be one output
77 QCOMPARE(workspace()->outputs().count(), 1);
78 QCOMPARE(outputAnnouncedSpy.count(), 1);
79 const quint32 firstOutputId = outputAnnouncedSpy.first().first().value<quint32>();
80 QVERIFY(firstOutputId != 0u);
81 outputAnnouncedSpy.clear();
82
83 // let's announce a new output
84 const QList<QRect> geometries{QRect(0, 0, 1280, 1024), QRect(1280, 0, 1280, 1024)};
85 Test::setOutputConfig(geometries);
86 auto outputs = workspace()->outputs();
87 QCOMPARE(outputs.count(), 2);
88 QCOMPARE(outputs[0]->geometry(), geometries[0]);
89 QCOMPARE(outputs[1]->geometry(), geometries[1]);
90
91 // this should result in it getting announced, two new outputs are added...
92 QVERIFY(outputAnnouncedSpy.wait());
93 if (outputAnnouncedSpy.count() < 2) {
94 QVERIFY(outputAnnouncedSpy.wait());
95 }
96 QCOMPARE(outputAnnouncedSpy.count(), 2);
97 // ... and afterward the previous output gets removed
98 if (outputRemovedSpy.isEmpty()) {
99 QVERIFY(outputRemovedSpy.wait());
100 }
101 QCOMPARE(outputRemovedSpy.count(), 1);
102 QCOMPARE(outputRemovedSpy.first().first().value<quint32>(), firstOutputId);
103
104 // let's wait a little bit to ensure we don't get more events
105 QTest::qWait(100);
106 QCOMPARE(outputAnnouncedSpy.count(), 2);
107 QCOMPARE(outputRemovedSpy.count(), 1);
108
109 // let's create the output objects to ensure they are correct
110 std::unique_ptr<KWayland::Client::Output> o1(registry.createOutput(outputAnnouncedSpy.first().first().value<quint32>(), outputAnnouncedSpy.first().last().value<quint32>()));
111 QVERIFY(o1->isValid());
112 QSignalSpy o1ChangedSpy(o1.get(), &KWayland::Client::Output::changed);
113 QVERIFY(o1ChangedSpy.wait());
114 KWin::Output *serverOutput1 = kwinApp()->outputBackend()->findOutput(o1->name()); // use wl_output.name to find the compositor side output
115 QCOMPARE(o1->globalPosition(), serverOutput1->geometry().topLeft());
116 QCOMPARE(o1->pixelSize(), serverOutput1->modeSize());
117 std::unique_ptr<KWayland::Client::Output> o2(registry.createOutput(outputAnnouncedSpy.last().first().value<quint32>(), outputAnnouncedSpy.last().last().value<quint32>()));
118 QVERIFY(o2->isValid());
119 QSignalSpy o2ChangedSpy(o2.get(), &KWayland::Client::Output::changed);
120 QVERIFY(o2ChangedSpy.wait());
121 KWin::Output *serverOutput2 = kwinApp()->outputBackend()->findOutput(o2->name()); // use wl_output.name to find the compositor side output
122 QCOMPARE(o2->globalPosition(), serverOutput2->geometry().topLeft());
123 QCOMPARE(o2->pixelSize(), serverOutput2->modeSize());
124
125 // and check XDGOutput is synced
126 std::unique_ptr<KWayland::Client::XdgOutput> xdgO1(xdgOutputManager->getXdgOutput(o1.get()));
127 QSignalSpy xdgO1ChangedSpy(xdgO1.get(), &KWayland::Client::XdgOutput::changed);
128 QVERIFY(xdgO1ChangedSpy.wait());
129 QCOMPARE(xdgO1->logicalPosition(), serverOutput1->geometry().topLeft());
130 QCOMPARE(xdgO1->logicalSize(), serverOutput1->geometry().size());
131 std::unique_ptr<KWayland::Client::XdgOutput> xdgO2(xdgOutputManager->getXdgOutput(o2.get()));
132 QSignalSpy xdgO2ChangedSpy(xdgO2.get(), &KWayland::Client::XdgOutput::changed);
133 QVERIFY(xdgO2ChangedSpy.wait());
134 QCOMPARE(xdgO2->logicalPosition(), serverOutput2->geometry().topLeft());
135 QCOMPARE(xdgO2->logicalSize(), serverOutput2->geometry().size());
136
137 QVERIFY(xdgO1->name().startsWith("Virtual-"));
138 QVERIFY(xdgO1->name() != xdgO2->name());
139 QVERIFY(!xdgO1->description().isEmpty());
140
141 // now let's try to remove one output again
142 outputAnnouncedSpy.clear();
143 outputRemovedSpy.clear();
144
145 QSignalSpy o1RemovedSpy(o1.get(), &KWayland::Client::Output::removed);
146 QSignalSpy o2RemovedSpy(o2.get(), &KWayland::Client::Output::removed);
147
148 const QList<QRect> geometries2{QRect(0, 0, 1280, 1024)};
149 Test::setOutputConfig(geometries2);
151 QCOMPARE(outputs.count(), 1);
152 QCOMPARE(outputs[0]->geometry(), geometries2.at(0));
153
154 QVERIFY(outputAnnouncedSpy.wait());
155 QCOMPARE(outputAnnouncedSpy.count(), 1);
156 if (o1RemovedSpy.isEmpty()) {
157 QVERIFY(o1RemovedSpy.wait());
158 }
159 if (o2RemovedSpy.isEmpty()) {
160 QVERIFY(o2RemovedSpy.wait());
161 }
162 // now wait a bit to ensure we don't get more events
163 QTest::qWait(100);
164 QCOMPARE(outputAnnouncedSpy.count(), 1);
165 QCOMPARE(o1RemovedSpy.count(), 1);
166 QCOMPARE(o2RemovedSpy.count(), 1);
167 QCOMPARE(outputRemovedSpy.count(), 2);
168}
169
171#include "screen_changes_test.moc"
PointerInputRedirection * pointer() const
Definition input.h:220
QSize modeSize() const
Definition output.cpp:480
QRect geometry
Definition output.h:134
void warp(const QPointF &pos)
QList< Output * > outputs() const
Definition workspace.h:762
void setActiveOutput(Output *output)
#define WAYLANDTEST_MAIN(TestObject)
void destroyWaylandConnection()
void setOutputConfig(const QList< QRect > &geometries)
bool setupWaylandConnection(AdditionalWaylandInterfaces flags=AdditionalWaylandInterfaces())
KWayland::Client::Registry * registry
QList< KWayland::Client::Output * > outputs
KWayland::Client::ConnectionThread * waylandConnection()
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
InputRedirection * input()
Definition input.h:549