KWin
Loading...
Searching...
No Matches
slidingpopups_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*/
10#include "effect/effectloader.h"
11#include "kwin_wayland_test.h"
12#include "wayland_server.h"
13#include "workspace.h"
14#include "x11window.h"
15
16#include <KConfigGroup>
17
18#include <KWayland/Client/connection_thread.h>
19#include <KWayland/Client/registry.h>
20#include <KWayland/Client/slide.h>
21#include <KWayland/Client/surface.h>
22
23#include <netwm.h>
24#include <xcb/xcb_icccm.h>
25
26using namespace KWin;
27static const QString s_socketName = QStringLiteral("wayland_test_effects_slidingpopups-0");
28
29class SlidingPopupsTest : public QObject
30{
31 Q_OBJECT
32private Q_SLOTS:
33 void initTestCase();
34 void init();
35 void cleanup();
36
37 void testWithOtherEffect_data();
38 void testWithOtherEffect();
39 void testWithOtherEffectWayland_data();
40 void testWithOtherEffectWayland();
41};
42
43void SlidingPopupsTest::initTestCase()
44{
46 QSKIP("no render node available");
47 return;
48 }
49 qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
50 qRegisterMetaType<KWin::Window *>();
51 qRegisterMetaType<KWin::Effect *>();
52 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
53 QVERIFY(waylandServer()->init(s_socketName));
55 QRect(0, 0, 1280, 1024),
56 QRect(1280, 0, 1280, 1024),
57 });
58
59 // disable all effects - we don't want to have it interact with the rendering
60 auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
61 KConfigGroup plugins(config, QStringLiteral("Plugins"));
62 const auto builtinNames = EffectLoader().listOfKnownEffects();
63 for (QString name : builtinNames) {
64 plugins.writeEntry(name + QStringLiteral("Enabled"), false);
65 }
66 KConfigGroup wobblyGroup = config->group(QStringLiteral("Effect-Wobbly"));
67 wobblyGroup.writeEntry(QStringLiteral("Settings"), QStringLiteral("Custom"));
68 wobblyGroup.writeEntry(QStringLiteral("OpenEffect"), true);
69 wobblyGroup.writeEntry(QStringLiteral("CloseEffect"), true);
70
71 config->sync();
72 kwinApp()->setConfig(config);
73
74 qputenv("KWIN_COMPOSE", QByteArrayLiteral("O2"));
75 qputenv("KWIN_EFFECTS_FORCE_ANIMATIONS", "1");
76 kwinApp()->start();
77 QVERIFY(applicationStartedSpy.wait());
78}
79
80void SlidingPopupsTest::init()
81{
83}
84
85void SlidingPopupsTest::cleanup()
86{
88 while (!effects->loadedEffects().isEmpty()) {
89 const QString effect = effects->loadedEffects().first();
90 effects->unloadEffect(effect);
91 QVERIFY(!effects->isEffectLoaded(effect));
92 }
93}
94
95void SlidingPopupsTest::testWithOtherEffect_data()
96{
97 QTest::addColumn<QStringList>("effectsToLoad");
98
99 QTest::newRow("fade, slide") << QStringList{QStringLiteral("fade"), QStringLiteral("slidingpopups")};
100 QTest::newRow("slide, fade") << QStringList{QStringLiteral("slidingpopups"), QStringLiteral("fade")};
101 QTest::newRow("scale, slide") << QStringList{QStringLiteral("scale"), QStringLiteral("slidingpopups")};
102 QTest::newRow("slide, scale") << QStringList{QStringLiteral("slidingpopups"), QStringLiteral("scale")};
103
105 QTest::newRow("glide, slide") << QStringList{QStringLiteral("glide"), QStringLiteral("slidingpopups")};
106 QTest::newRow("slide, glide") << QStringList{QStringLiteral("slidingpopups"), QStringLiteral("glide")};
107 QTest::newRow("wobblywindows, slide") << QStringList{QStringLiteral("wobblywindows"), QStringLiteral("slidingpopups")};
108 QTest::newRow("slide, wobblywindows") << QStringList{QStringLiteral("slidingpopups"), QStringLiteral("wobblywindows")};
109 QTest::newRow("fallapart, slide") << QStringList{QStringLiteral("fallapart"), QStringLiteral("slidingpopups")};
110 QTest::newRow("slide, fallapart") << QStringList{QStringLiteral("slidingpopups"), QStringLiteral("fallapart")};
111 }
112}
113
114void SlidingPopupsTest::testWithOtherEffect()
115{
116 // this test verifies that slidingpopups effect grabs the window added role
117 // independently of the sequence how the effects are loaded.
118 // see BUG 336866
119 // find the effectsloader
120 auto effectloader = effects->findChild<AbstractEffectLoader *>();
121 QVERIFY(effectloader);
122 QSignalSpy effectLoadedSpy(effectloader, &AbstractEffectLoader::effectLoaded);
123
124 Effect *slidingPoupus = nullptr;
125 Effect *otherEffect = nullptr;
126 QFETCH(QStringList, effectsToLoad);
127 for (const QString &effectName : effectsToLoad) {
128 QVERIFY(!effects->isEffectLoaded(effectName));
129 QVERIFY(effects->loadEffect(effectName));
130 QVERIFY(effects->isEffectLoaded(effectName));
131
132 QCOMPARE(effectLoadedSpy.count(), 1);
133 Effect *effect = effectLoadedSpy.first().first().value<Effect *>();
134 if (effectName == QStringLiteral("slidingpopups")) {
135 slidingPoupus = effect;
136 } else {
137 otherEffect = effect;
138 }
139 effectLoadedSpy.clear();
140 }
141 QVERIFY(slidingPoupus);
142 QVERIFY(otherEffect);
143
144 QVERIFY(!slidingPoupus->isActive());
145 QVERIFY(!otherEffect->isActive());
146
147 QSignalSpy windowAddedSpy(effects, &EffectsHandler::windowAdded);
148
149 // create an xcb window
151 QVERIFY(!xcb_connection_has_error(c.get()));
152 const QRect windowGeometry(0, 0, 100, 200);
153 xcb_window_t windowId = xcb_generate_id(c.get());
154 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
155 windowGeometry.x(),
156 windowGeometry.y(),
157 windowGeometry.width(),
158 windowGeometry.height(),
159 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
160 xcb_size_hints_t hints;
161 memset(&hints, 0, sizeof(hints));
162 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
163 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
164 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
165 NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::Properties(), NET::Properties2());
166 winInfo.setWindowType(NET::Normal);
167
168 // and get the slide atom
169 const QByteArray effectAtomName = QByteArrayLiteral("_KDE_SLIDE");
170 xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c.get(), false, effectAtomName.length(), effectAtomName.constData());
171 const int size = 2;
172 int32_t data[size];
173 data[0] = 0;
174 data[1] = 0;
175 UniqueCPtr<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c.get(), atomCookie, nullptr));
176 QVERIFY(atom != nullptr);
177 xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atom->atom, atom->atom, 32, size, data);
178
179 xcb_map_window(c.get(), windowId);
180 xcb_flush(c.get());
181
182 // we should get a window for it
183 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
184 QVERIFY(windowCreatedSpy.wait());
185 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
186 QVERIFY(window);
187 QCOMPARE(window->window(), windowId);
188 QVERIFY(window->isNormalWindow());
189
190 // sliding popups should be active
191 QCOMPARE(windowAddedSpy.count(), 1);
192 QTRY_VERIFY(slidingPoupus->isActive());
193 QVERIFY(!otherEffect->isActive());
194
195 // wait till effect ends
196 QTRY_VERIFY(!slidingPoupus->isActive());
197 QTRY_VERIFY(!otherEffect->isActive());
198
199 // and destroy the window again
200 xcb_unmap_window(c.get(), windowId);
201 xcb_flush(c.get());
202
203 QSignalSpy closedSpy(window, &X11Window::closed);
204
205 QSignalSpy windowDeletedSpy(effects, &EffectsHandler::windowDeleted);
206 QVERIFY(closedSpy.wait());
207
208 // again we should have the sliding popups active
209 QVERIFY(slidingPoupus->isActive());
210 QVERIFY(!otherEffect->isActive());
211
212 QVERIFY(windowDeletedSpy.wait());
213
214 QCOMPARE(windowDeletedSpy.count(), 1);
215 QVERIFY(!slidingPoupus->isActive());
216 QVERIFY(!otherEffect->isActive());
217 xcb_destroy_window(c.get(), windowId);
218 c.reset();
219}
220
221void SlidingPopupsTest::testWithOtherEffectWayland_data()
222{
223 QTest::addColumn<QStringList>("effectsToLoad");
224
225 QTest::newRow("fade, slide") << QStringList{QStringLiteral("fade"), QStringLiteral("slidingpopups")};
226 QTest::newRow("slide, fade") << QStringList{QStringLiteral("slidingpopups"), QStringLiteral("fade")};
227 QTest::newRow("scale, slide") << QStringList{QStringLiteral("scale"), QStringLiteral("slidingpopups")};
228 QTest::newRow("slide, scale") << QStringList{QStringLiteral("slidingpopups"), QStringLiteral("scale")};
229
231 QTest::newRow("glide, slide") << QStringList{QStringLiteral("glide"), QStringLiteral("slidingpopups")};
232 QTest::newRow("slide, glide") << QStringList{QStringLiteral("slidingpopups"), QStringLiteral("glide")};
233 QTest::newRow("wobblywindows, slide") << QStringList{QStringLiteral("wobblywindows"), QStringLiteral("slidingpopups")};
234 QTest::newRow("slide, wobblywindows") << QStringList{QStringLiteral("slidingpopups"), QStringLiteral("wobblywindows")};
235 QTest::newRow("fallapart, slide") << QStringList{QStringLiteral("fallapart"), QStringLiteral("slidingpopups")};
236 QTest::newRow("slide, fallapart") << QStringList{QStringLiteral("slidingpopups"), QStringLiteral("fallapart")};
237 }
238}
239
240void SlidingPopupsTest::testWithOtherEffectWayland()
241{
242 // this test verifies that slidingpopups effect grabs the window added role
243 // independently of the sequence how the effects are loaded.
244 // see BUG 336866
245 // the test is like testWithOtherEffect, but simulates using a Wayland window
246 // find the effectsloader
247 auto effectloader = effects->findChild<AbstractEffectLoader *>();
248 QVERIFY(effectloader);
249 QSignalSpy effectLoadedSpy(effectloader, &AbstractEffectLoader::effectLoaded);
250
251 Effect *slidingPoupus = nullptr;
252 Effect *otherEffect = nullptr;
253 QFETCH(QStringList, effectsToLoad);
254 for (const QString &effectName : effectsToLoad) {
255 QVERIFY(!effects->isEffectLoaded(effectName));
256 QVERIFY(effects->loadEffect(effectName));
257 QVERIFY(effects->isEffectLoaded(effectName));
258
259 QCOMPARE(effectLoadedSpy.count(), 1);
260 Effect *effect = effectLoadedSpy.first().first().value<Effect *>();
261 if (effectName == QStringLiteral("slidingpopups")) {
262 slidingPoupus = effect;
263 } else {
264 otherEffect = effect;
265 }
266 effectLoadedSpy.clear();
267 }
268 QVERIFY(slidingPoupus);
269 QVERIFY(otherEffect);
270
271 QVERIFY(!slidingPoupus->isActive());
272 QVERIFY(!otherEffect->isActive());
273 QSignalSpy windowAddedSpy(effects, &EffectsHandler::windowAdded);
274
275 // the test created the slide protocol, let's create a Registry and listen for it
276 std::unique_ptr<KWayland::Client::Registry> registry(new KWayland::Client::Registry);
278
279 QSignalSpy interfacesAnnouncedSpy(registry.get(), &KWayland::Client::Registry::interfacesAnnounced);
280 registry->setup();
281 QVERIFY(interfacesAnnouncedSpy.wait());
282 auto slideInterface = registry->interface(KWayland::Client::Registry::Interface::Slide);
283 QVERIFY(slideInterface.name != 0);
284 std::unique_ptr<KWayland::Client::SlideManager> slideManager(registry->createSlideManager(slideInterface.name, slideInterface.version));
285 QVERIFY(slideManager);
286
287 // create Wayland window
288 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
289 QVERIFY(surface);
290 std::unique_ptr<KWayland::Client::Slide> slide(slideManager->createSlide(surface.get()));
291 slide->setLocation(KWayland::Client::Slide::Location::Left);
292 slide->commit();
293 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
294 QVERIFY(shellSurface);
295 QCOMPARE(windowAddedSpy.count(), 0);
296 auto window = Test::renderAndWaitForShown(surface.get(), QSize(10, 20), Qt::blue);
297 QVERIFY(window);
298 QVERIFY(window->isNormalWindow());
299
300 // sliding popups should be active
301 QCOMPARE(windowAddedSpy.count(), 1);
302 QTRY_VERIFY(slidingPoupus->isActive());
303 QVERIFY(!otherEffect->isActive());
304
305 // wait till effect ends
306 QTRY_VERIFY(!slidingPoupus->isActive());
307 QTRY_VERIFY(!otherEffect->isActive());
308
309 // and destroy the window again
310 shellSurface.reset();
311 surface.reset();
312
313 QSignalSpy windowClosedSpy(window, &X11Window::closed);
314
315 QSignalSpy windowDeletedSpy(effects, &EffectsHandler::windowDeleted);
316 QVERIFY(windowClosedSpy.wait());
317
318 // again we should have the sliding popups active
319 QVERIFY(slidingPoupus->isActive());
320 QVERIFY(!otherEffect->isActive());
321
322 QVERIFY(windowDeletedSpy.wait());
323
324 QCOMPARE(windowDeletedSpy.count(), 1);
325 QVERIFY(!slidingPoupus->isActive());
326 QVERIFY(!otherEffect->isActive());
327}
328
330#include "slidingpopups_test.moc"
Interface to describe how an effect loader has to function.
void effectLoaded(KWin::Effect *effect, const QString &name)
The loader emits this signal when it successfully loaded an effect.
Base class for all KWin effects.
Definition effect.h:535
QStringList listOfKnownEffects() const override
All the Effects this loader knows of.
Q_SCRIPTABLE void unloadEffect(const QString &name)
void windowDeleted(KWin::EffectWindow *w)
QStringList loadedEffects
Q_SCRIPTABLE bool loadEffect(const QString &name)
CompositingType compositingType
Q_SCRIPTABLE bool isEffectLoaded(const QString &name) const
void windowAdded(KWin::EffectWindow *w)
bool isNormalWindow() const
Definition window.h:1957
void windowAdded(KWin::Window *)
xcb_window_t window() const
virtual bool isActive() const
Definition effect.cpp:447
#define WAYLANDTEST_MAIN(TestObject)
Window * renderAndWaitForShown(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32, int timeout=5000)
void destroyWaylandConnection()
void setOutputConfig(const QList< QRect > &geometries)
bool setupWaylandConnection(AdditionalWaylandInterfaces flags=AdditionalWaylandInterfaces())
KWayland::Client::Registry * registry
XcbConnectionPtr createX11Connection()
bool renderNodeAvailable()
std::unique_ptr< KWayland::Client::Surface > createSurface()
XdgToplevel * createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent=nullptr)
KWayland::Client::ConnectionThread * waylandConnection()
std::unique_ptr< xcb_connection_t, XcbConnectionDeleter > XcbConnectionPtr
KWIN_EXPORT xcb_window_t rootWindow()
Definition xcb.h:24
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
@ OpenGLCompositing
Definition globals.h:37
EffectsHandler * effects
std::unique_ptr< T, CDeleter > UniqueCPtr
Definition c_ptr.h:28