KWin
Loading...
Searching...
No Matches
dbus_interface_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: 2018 Martin Flöser <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
8*/
9#include "config-kwin.h"
10
11#include "kwin_wayland_test.h"
12
13#include "atoms.h"
14#include "rules.h"
15#include "virtualdesktops.h"
16#include "wayland_server.h"
17#include "window.h"
18#include "workspace.h"
19#include "x11window.h"
20
21#include <KWayland/Client/surface.h>
22
23#include <QDBusArgument>
24#include <QDBusConnection>
25#include <QDBusMessage>
26#include <QDBusPendingReply>
27#include <QUuid>
28
29#include <netwm.h>
30#include <xcb/xcb_icccm.h>
31
32using namespace KWin;
33
34static const QString s_socketName = QStringLiteral("wayland_test_kwin_dbus_interface-0");
35
36const QString s_destination{QStringLiteral("org.kde.KWin")};
37const QString s_path{QStringLiteral("/KWin")};
38const QString s_interface{QStringLiteral("org.kde.KWin")};
39
40class TestDbusInterface : public QObject
41{
42 Q_OBJECT
43private Q_SLOTS:
44 void initTestCase();
45 void init();
46 void cleanup();
47
48 void testGetWindowInfoInvalidUuid();
49 void testGetWindowInfoXdgShellClient();
50 void testGetWindowInfoX11Client();
51};
52
53void TestDbusInterface::initTestCase()
54{
55 qRegisterMetaType<KWin::Window *>();
56
57 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
58 QVERIFY(waylandServer()->init(s_socketName));
60 QRect(0, 0, 1280, 1024),
61 QRect(1280, 0, 1280, 1024),
62 });
63
64 kwinApp()->start();
65 QVERIFY(applicationStartedSpy.wait());
66 VirtualDesktopManager::self()->setCount(4);
67}
68
69void TestDbusInterface::init()
70{
72}
73
74void TestDbusInterface::cleanup()
75{
77}
78
79namespace
80{
81QDBusPendingCall getWindowInfo(const QUuid &uuid)
82{
83 auto msg = QDBusMessage::createMethodCall(s_destination, s_path, s_interface, QStringLiteral("getWindowInfo"));
84 msg.setArguments({uuid.toString()});
85 return QDBusConnection::sessionBus().asyncCall(msg);
86}
87}
88
89void TestDbusInterface::testGetWindowInfoInvalidUuid()
90{
91 QDBusPendingReply<QVariantMap> reply{getWindowInfo(QUuid::createUuid())};
92 reply.waitForFinished();
93 QVERIFY(reply.isValid());
94 QVERIFY(!reply.isError());
95 const auto windowData = reply.value();
96 QVERIFY(windowData.empty());
97}
98
99void TestDbusInterface::testGetWindowInfoXdgShellClient()
100{
101 QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
102
103 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
104 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
105 shellSurface->set_app_id(QStringLiteral("org.kde.foo"));
106 shellSurface->set_title(QStringLiteral("Test window"));
107
108 // now let's render
109 Test::render(surface.get(), QSize(100, 50), Qt::blue);
110 QVERIFY(windowAddedSpy.isEmpty());
111 QVERIFY(windowAddedSpy.wait());
112 auto window = windowAddedSpy.first().first().value<Window *>();
113 QVERIFY(window);
114
115 const QVariantMap expectedData = {
116 {QStringLiteral("type"), int(NET::Normal)},
117 {QStringLiteral("x"), window->x()},
118 {QStringLiteral("y"), window->y()},
119 {QStringLiteral("width"), window->width()},
120 {QStringLiteral("height"), window->height()},
121 {QStringLiteral("desktops"), window->desktopIds()},
122 {QStringLiteral("minimized"), false},
123 {QStringLiteral("shaded"), false},
124 {QStringLiteral("fullscreen"), false},
125 {QStringLiteral("keepAbove"), false},
126 {QStringLiteral("keepBelow"), false},
127 {QStringLiteral("skipTaskbar"), false},
128 {QStringLiteral("skipPager"), false},
129 {QStringLiteral("skipSwitcher"), false},
130 {QStringLiteral("maximizeHorizontal"), false},
131 {QStringLiteral("maximizeVertical"), false},
132 {QStringLiteral("noBorder"), false},
133 {QStringLiteral("clientMachine"), QString()},
134 {QStringLiteral("localhost"), true},
135 {QStringLiteral("role"), QString()},
136 {QStringLiteral("resourceName"), QStringLiteral("testDbusInterface")},
137 {QStringLiteral("resourceClass"), QStringLiteral("org.kde.foo")},
138 {QStringLiteral("desktopFile"), QStringLiteral("org.kde.foo")},
139 {QStringLiteral("caption"), QStringLiteral("Test window")},
140#if KWIN_BUILD_ACTIVITIES
141 {QStringLiteral("activities"), QStringList()},
142#endif
143 {QStringLiteral("layer"), NormalLayer},
144 };
145
146 // let's get the window info
147 QDBusPendingReply<QVariantMap> reply{getWindowInfo(window->internalId())};
148 reply.waitForFinished();
149 QVERIFY(reply.isValid());
150 QVERIFY(!reply.isError());
151 auto windowData = reply.value();
152 windowData.remove(QStringLiteral("uuid"));
153 QCOMPARE(windowData, expectedData);
154
155 auto verifyProperty = [window](const QString &name) {
156 QDBusPendingReply<QVariantMap> reply{getWindowInfo(window->internalId())};
157 reply.waitForFinished();
158 return reply.value().value(name).toBool();
159 };
160
161 QVERIFY(!window->isMinimized());
162 window->setMinimized(true);
163 QVERIFY(window->isMinimized());
164 QCOMPARE(verifyProperty(QStringLiteral("minimized")), true);
165
166 QVERIFY(!window->keepAbove());
167 window->setKeepAbove(true);
168 QVERIFY(window->keepAbove());
169 QCOMPARE(verifyProperty(QStringLiteral("keepAbove")), true);
170
171 QVERIFY(!window->keepBelow());
172 window->setKeepBelow(true);
173 QVERIFY(window->keepBelow());
174 QCOMPARE(verifyProperty(QStringLiteral("keepBelow")), true);
175
176 QVERIFY(!window->skipTaskbar());
177 window->setSkipTaskbar(true);
178 QVERIFY(window->skipTaskbar());
179 QCOMPARE(verifyProperty(QStringLiteral("skipTaskbar")), true);
180
181 QVERIFY(!window->skipPager());
182 window->setSkipPager(true);
183 QVERIFY(window->skipPager());
184 QCOMPARE(verifyProperty(QStringLiteral("skipPager")), true);
185
186 QVERIFY(!window->skipSwitcher());
187 window->setSkipSwitcher(true);
188 QVERIFY(window->skipSwitcher());
189 QCOMPARE(verifyProperty(QStringLiteral("skipSwitcher")), true);
190
191 // not testing shaded as that's X11
192 // not testing fullscreen, maximizeHorizontal, maximizeVertical and noBorder as those require window geometry changes
193
194 const QList<VirtualDesktop *> desktops = VirtualDesktopManager::self()->desktops();
195 QCOMPARE(window->desktops(), QList<VirtualDesktop *>{desktops[0]});
196 workspace()->sendWindowToDesktops(window, {desktops[1]}, false);
197 QCOMPARE(window->desktops(), QList<VirtualDesktop *>{desktops[1]});
198 reply = getWindowInfo(window->internalId());
199 reply.waitForFinished();
200 QCOMPARE(reply.value().value(QStringLiteral("desktops")).toStringList(), window->desktopIds());
201
202 window->move(QPoint(10, 20));
203 reply = getWindowInfo(window->internalId());
204 reply.waitForFinished();
205 QCOMPARE(reply.value().value(QStringLiteral("x")).toInt(), window->x());
206 QCOMPARE(reply.value().value(QStringLiteral("y")).toInt(), window->y());
207 // not testing width, height as that would require window geometry change
208
209 // finally close window
210 const auto id = window->internalId();
211 QSignalSpy windowClosedSpy(window, &Window::closed);
212 shellSurface.reset();
213 surface.reset();
214 QVERIFY(windowClosedSpy.wait());
215 QCOMPARE(windowClosedSpy.count(), 1);
216
217 reply = getWindowInfo(id);
218 reply.waitForFinished();
219 QVERIFY(reply.value().empty());
220}
221
222void TestDbusInterface::testGetWindowInfoX11Client()
223{
225 QVERIFY(!xcb_connection_has_error(c.get()));
226 const QRect windowGeometry(0, 0, 600, 400);
227 xcb_window_t windowId = xcb_generate_id(c.get());
228 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
229 windowGeometry.x(),
230 windowGeometry.y(),
231 windowGeometry.width(),
232 windowGeometry.height(),
233 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
234 xcb_size_hints_t hints;
235 memset(&hints, 0, sizeof(hints));
236 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
237 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
238 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
239 xcb_icccm_set_wm_class(c.get(), windowId, 7, "foo\0bar");
240 NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::Properties(), NET::Properties2());
241 winInfo.setName("Some caption");
242 winInfo.setDesktopFileName("org.kde.foo");
243 xcb_map_window(c.get(), windowId);
244 xcb_flush(c.get());
245
246 // we should get a window for it
247 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
248 QVERIFY(windowCreatedSpy.wait());
249 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
250 QVERIFY(window);
251 QCOMPARE(window->window(), windowId);
252 QCOMPARE(window->clientSize(), windowGeometry.size());
253
254 const QVariantMap expectedData = {
255 {QStringLiteral("type"), NET::Normal},
256 {QStringLiteral("x"), window->x()},
257 {QStringLiteral("y"), window->y()},
258 {QStringLiteral("width"), window->width()},
259 {QStringLiteral("height"), window->height()},
260 {QStringLiteral("desktops"), window->desktopIds()},
261 {QStringLiteral("minimized"), false},
262 {QStringLiteral("shaded"), false},
263 {QStringLiteral("fullscreen"), false},
264 {QStringLiteral("keepAbove"), false},
265 {QStringLiteral("keepBelow"), false},
266 {QStringLiteral("skipTaskbar"), false},
267 {QStringLiteral("skipPager"), false},
268 {QStringLiteral("skipSwitcher"), false},
269 {QStringLiteral("maximizeHorizontal"), false},
270 {QStringLiteral("maximizeVertical"), false},
271 {QStringLiteral("noBorder"), false},
272 {QStringLiteral("role"), QString()},
273 {QStringLiteral("resourceName"), QStringLiteral("foo")},
274 {QStringLiteral("resourceClass"), QStringLiteral("bar")},
275 {QStringLiteral("desktopFile"), QStringLiteral("org.kde.foo")},
276 {QStringLiteral("caption"), QStringLiteral("Some caption")},
277#if KWIN_BUILD_ACTIVITIES
278 {QStringLiteral("activities"), QStringList()},
279#endif
280 {QStringLiteral("layer"), NormalLayer},
281 };
282
283 // let's get the window info
284 QDBusPendingReply<QVariantMap> reply{getWindowInfo(window->internalId())};
285 reply.waitForFinished();
286 QVERIFY(reply.isValid());
287 QVERIFY(!reply.isError());
288 auto windowData = reply.value();
289 // not testing clientmachine as that is system dependent due to that also not testing localhost
290 windowData.remove(QStringLiteral("clientMachine"));
291 windowData.remove(QStringLiteral("localhost"));
292 windowData.remove(QStringLiteral("uuid"));
293 QCOMPARE(windowData, expectedData);
294
295 auto verifyProperty = [window](const QString &name) {
296 QDBusPendingReply<QVariantMap> reply{getWindowInfo(window->internalId())};
297 reply.waitForFinished();
298 return reply.value().value(name).toBool();
299 };
300
301 QVERIFY(!window->isMinimized());
302 window->setMinimized(true);
303 QVERIFY(window->isMinimized());
304 QCOMPARE(verifyProperty(QStringLiteral("minimized")), true);
305
306 QVERIFY(!window->keepAbove());
307 window->setKeepAbove(true);
308 QVERIFY(window->keepAbove());
309 QCOMPARE(verifyProperty(QStringLiteral("keepAbove")), true);
310
311 QVERIFY(!window->keepBelow());
312 window->setKeepBelow(true);
313 QVERIFY(window->keepBelow());
314 QCOMPARE(verifyProperty(QStringLiteral("keepBelow")), true);
315
316 QVERIFY(!window->skipTaskbar());
317 window->setSkipTaskbar(true);
318 QVERIFY(window->skipTaskbar());
319 QCOMPARE(verifyProperty(QStringLiteral("skipTaskbar")), true);
320
321 QVERIFY(!window->skipPager());
322 window->setSkipPager(true);
323 QVERIFY(window->skipPager());
324 QCOMPARE(verifyProperty(QStringLiteral("skipPager")), true);
325
326 QVERIFY(!window->skipSwitcher());
327 window->setSkipSwitcher(true);
328 QVERIFY(window->skipSwitcher());
329 QCOMPARE(verifyProperty(QStringLiteral("skipSwitcher")), true);
330
331 QVERIFY(!window->isShade());
332 window->setShade(ShadeNormal);
333 QVERIFY(window->isShade());
334 QCOMPARE(verifyProperty(QStringLiteral("shaded")), true);
335 window->setShade(ShadeNone);
336 QVERIFY(!window->isShade());
337
338 QVERIFY(!window->noBorder());
339 window->setNoBorder(true);
340 QVERIFY(window->noBorder());
341 QCOMPARE(verifyProperty(QStringLiteral("noBorder")), true);
342 window->setNoBorder(false);
343 QVERIFY(!window->noBorder());
344
345 QVERIFY(!window->isFullScreen());
346 window->setFullScreen(true);
347 QVERIFY(window->isFullScreen());
348 QVERIFY(window->clientSize() != windowGeometry.size());
349 QCOMPARE(verifyProperty(QStringLiteral("fullscreen")), true);
350 reply = getWindowInfo(window->internalId());
351 reply.waitForFinished();
352 QCOMPARE(reply.value().value(QStringLiteral("width")).toInt(), window->width());
353 QCOMPARE(reply.value().value(QStringLiteral("height")).toInt(), window->height());
354 window->setFullScreen(false);
355 QVERIFY(!window->isFullScreen());
356
357 // maximize
358 window->setMaximize(true, false);
359 QCOMPARE(verifyProperty(QStringLiteral("maximizeVertical")), true);
360 QCOMPARE(verifyProperty(QStringLiteral("maximizeHorizontal")), false);
361 window->setMaximize(false, true);
362 QCOMPARE(verifyProperty(QStringLiteral("maximizeVertical")), false);
363 QCOMPARE(verifyProperty(QStringLiteral("maximizeHorizontal")), true);
364
365 const auto id = window->internalId();
366 // destroy the window
367 xcb_unmap_window(c.get(), windowId);
368 xcb_flush(c.get());
369
370 QSignalSpy windowClosedSpy(window, &X11Window::closed);
371 QVERIFY(windowClosedSpy.wait());
372 xcb_destroy_window(c.get(), windowId);
373 c.reset();
374
375 reply = getWindowInfo(id);
376 reply.waitForFinished();
377 QVERIFY(reply.value().empty());
378}
379
381#include "dbus_interface_test.moc"
bool skipSwitcher
Definition window.h:320
void setMinimized(bool set)
Definition window.cpp:984
void setShade(bool set)
Definition window.cpp:863
qreal width
Definition window.h:99
bool isShade() const
Definition window.h:1034
void setSkipTaskbar(bool set)
Definition window.cpp:464
bool keepBelow
Definition window.h:337
void setKeepBelow(bool)
Definition window.cpp:649
QSizeF clientSize() const
Definition window.h:1872
QUuid internalId
Definition window.h:259
bool isMinimized() const
Definition window.h:988
qreal y
Definition window.h:94
qreal x
Definition window.h:89
qreal height
Definition window.h:104
void setSkipPager(bool set)
Definition window.cpp:448
QStringList desktopIds() const
Definition window.cpp:830
bool skipPager
Definition window.h:315
bool keepAbove
Definition window.h:332
void setKeepAbove(bool)
Definition window.cpp:628
void setSkipSwitcher(bool set)
Definition window.cpp:436
Q_INVOKABLE void setMaximize(bool vertically, bool horizontally)
Definition window.cpp:1172
bool skipTaskbar
Definition window.h:310
void windowAdded(KWin::Window *)
void sendWindowToDesktops(Window *window, const QList< VirtualDesktop * > &desktops, bool dont_activate)
void setFullScreen(bool set) override
bool noBorder() const override
void setNoBorder(bool set) override
xcb_window_t window() const
bool isFullScreen() const override
Definition x11window.h:580
const QString s_path
const QString s_destination
#define WAYLANDTEST_MAIN(TestObject)
void destroyWaylandConnection()
void setOutputConfig(const QList< QRect > &geometries)
bool setupWaylandConnection(AdditionalWaylandInterfaces flags=AdditionalWaylandInterfaces())
void render(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32_Premultiplied)
XcbConnectionPtr createX11Connection()
std::unique_ptr< KWayland::Client::Surface > createSurface()
XdgToplevel * createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent=nullptr)
std::unique_ptr< xcb_connection_t, XcbConnectionDeleter > XcbConnectionPtr
@ ShadeNone
Definition common.h:63
@ ShadeNormal
Definition common.h:64
KWIN_EXPORT xcb_window_t rootWindow()
Definition xcb.h:24
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
@ NormalLayer
Definition globals.h:167