KWin
Loading...
Searching...
No Matches
test_xcb_wrapper.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: 2013 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "testutils.h"
10// KWin
11#include "utils/xcbutils.h"
12// Qt
13#include <QApplication>
14#include <QTest>
15#include <netwm.h>
16#include <private/qtx11extras_p.h>
17// xcb
18#include <xcb/xcb.h>
19
20using namespace KWin;
21
22class TestXcbWrapper : public QObject
23{
24 Q_OBJECT
25private Q_SLOTS:
26 void initTestCase();
27 void init();
28 void cleanup();
29 void defaultCtor();
30 void normalCtor();
31 void copyCtorEmpty();
32 void copyCtorBeforeRetrieve();
33 void copyCtorAfterRetrieve();
34 void assignementEmpty();
35 void assignmentBeforeRetrieve();
36 void assignmentAfterRetrieve();
37 void discard();
38 void testQueryTree();
39 void testCurrentInput();
40 void testTransientFor();
41 void testPropertyByteArray();
42 void testPropertyBool();
43 void testAtom();
44 void testMotifEmpty();
45 void testMotif_data();
46 void testMotif();
47
48private:
49 void testEmpty(Xcb::WindowGeometry &geometry);
50 void testGeometry(Xcb::WindowGeometry &geometry, const QRect &rect);
51 Xcb::Window m_testWindow;
52};
53
54void TestXcbWrapper::initTestCase()
55{
56 qApp->setProperty("x11RootWindow", QVariant::fromValue<quint32>(QX11Info::appRootWindow()));
57 qApp->setProperty("x11Connection", QVariant::fromValue<void *>(QX11Info::connection()));
58}
59
60void TestXcbWrapper::init()
61{
62 const uint32_t values[] = {true};
63 m_testWindow.create(QRect(0, 0, 10, 10), XCB_WINDOW_CLASS_INPUT_ONLY, XCB_CW_OVERRIDE_REDIRECT, values);
64 QVERIFY(m_testWindow.isValid());
65}
66
67void TestXcbWrapper::cleanup()
68{
69 m_testWindow.reset();
70}
71
72void TestXcbWrapper::testEmpty(Xcb::WindowGeometry &geometry)
73{
74 QCOMPARE(geometry.window(), KWin::noneWindow());
75 QVERIFY(!geometry.data());
76 QCOMPARE(geometry.isNull(), true);
77 QCOMPARE(geometry.rect(), QRect());
78 QVERIFY(!geometry);
79}
80
81void TestXcbWrapper::testGeometry(Xcb::WindowGeometry &geometry, const QRect &rect)
82{
83 QCOMPARE(geometry.window(), (xcb_window_t)m_testWindow);
84 // now lets retrieve some data
85 QCOMPARE(geometry.rect(), rect);
86 QVERIFY(geometry.isRetrieved());
87 QCOMPARE(geometry.isNull(), false);
88 QVERIFY(geometry);
89 QVERIFY(geometry.data());
90 QCOMPARE(geometry.data()->x, int16_t(rect.x()));
91 QCOMPARE(geometry.data()->y, int16_t(rect.y()));
92 QCOMPARE(geometry.data()->width, uint16_t(rect.width()));
93 QCOMPARE(geometry.data()->height, uint16_t(rect.height()));
94}
95
96void TestXcbWrapper::defaultCtor()
97{
98 Xcb::WindowGeometry geometry;
99 testEmpty(geometry);
100 QVERIFY(!geometry.isRetrieved());
101}
102
103void TestXcbWrapper::normalCtor()
104{
105 Xcb::WindowGeometry geometry(m_testWindow);
106 QVERIFY(!geometry.isRetrieved());
107 testGeometry(geometry, QRect(0, 0, 10, 10));
108}
109
110void TestXcbWrapper::copyCtorEmpty()
111{
112 Xcb::WindowGeometry geometry;
113 Xcb::WindowGeometry other(geometry);
114 testEmpty(geometry);
115 QVERIFY(geometry.isRetrieved());
116 testEmpty(other);
117 QVERIFY(!other.isRetrieved());
118}
119
120void TestXcbWrapper::copyCtorBeforeRetrieve()
121{
122 Xcb::WindowGeometry geometry(m_testWindow);
123 QVERIFY(!geometry.isRetrieved());
124 Xcb::WindowGeometry other(geometry);
125 testEmpty(geometry);
126 QVERIFY(geometry.isRetrieved());
127
128 QVERIFY(!other.isRetrieved());
129 testGeometry(other, QRect(0, 0, 10, 10));
130}
131
132void TestXcbWrapper::copyCtorAfterRetrieve()
133{
134 Xcb::WindowGeometry geometry(m_testWindow);
135 QVERIFY(geometry);
136 QVERIFY(geometry.isRetrieved());
137 QCOMPARE(geometry.rect(), QRect(0, 0, 10, 10));
138 Xcb::WindowGeometry other(geometry);
139 testEmpty(geometry);
140 QVERIFY(geometry.isRetrieved());
141
142 QVERIFY(other.isRetrieved());
143 testGeometry(other, QRect(0, 0, 10, 10));
144}
145
146void TestXcbWrapper::assignementEmpty()
147{
148 Xcb::WindowGeometry geometry;
150 testEmpty(geometry);
151 testEmpty(other);
152
153 other = geometry;
154 QVERIFY(geometry.isRetrieved());
155 testEmpty(geometry);
156 testEmpty(other);
157 QVERIFY(!other.isRetrieved());
158
159 QT_WARNING_PUSH
160 QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")
161 // test assignment to self
162 geometry = geometry;
163 other = other;
164 testEmpty(geometry);
165 testEmpty(other);
166 QT_WARNING_POP
167}
168
169void TestXcbWrapper::assignmentBeforeRetrieve()
170{
171 Xcb::WindowGeometry geometry(m_testWindow);
172 Xcb::WindowGeometry other = geometry;
173 QVERIFY(geometry.isRetrieved());
174 testEmpty(geometry);
175
176 QVERIFY(!other.isRetrieved());
177 testGeometry(other, QRect(0, 0, 10, 10));
178
179 other = Xcb::WindowGeometry(m_testWindow);
180 QVERIFY(!other.isRetrieved());
181 QCOMPARE(other.window(), (xcb_window_t)m_testWindow);
182 other = Xcb::WindowGeometry();
183 testEmpty(geometry);
184
185 QT_WARNING_PUSH
186 QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")
187 // test assignment to self
188 geometry = geometry;
189 other = other;
190 testEmpty(geometry);
191 QT_WARNING_POP
192}
193
194void TestXcbWrapper::assignmentAfterRetrieve()
195{
196 Xcb::WindowGeometry geometry(m_testWindow);
197 QVERIFY(geometry);
198 QVERIFY(geometry.isRetrieved());
199 Xcb::WindowGeometry other = geometry;
200 testEmpty(geometry);
201
202 QVERIFY(other.isRetrieved());
203 testGeometry(other, QRect(0, 0, 10, 10));
204
205 QT_WARNING_PUSH
206 QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")
207 // test assignment to self
208 geometry = geometry;
209 other = other;
210 testEmpty(geometry);
211 testGeometry(other, QRect(0, 0, 10, 10));
212 QT_WARNING_POP
213
214 // set to empty again
215 other = Xcb::WindowGeometry();
216 testEmpty(other);
217}
218
219void TestXcbWrapper::discard()
220{
221 // discard of reply cannot be tested properly as we cannot check whether the reply has been discarded
222 // therefore it's more or less just a test to ensure that it doesn't crash and the code paths
223 // are taken.
225 delete geometry;
226
227 geometry = new Xcb::WindowGeometry(m_testWindow);
228 delete geometry;
229
230 geometry = new Xcb::WindowGeometry(m_testWindow);
231 QVERIFY(geometry->data());
232 delete geometry;
233}
234
235void TestXcbWrapper::testQueryTree()
236{
237 Xcb::Tree tree(m_testWindow);
238 // should have root as parent
239 QCOMPARE(tree.parent(), static_cast<xcb_window_t>(QX11Info::appRootWindow()));
240 // shouldn't have any children
241 QCOMPARE(tree->children_len, uint16_t(0));
242 QVERIFY(!tree.children());
243
244 // query for root
245 Xcb::Tree root(QX11Info::appRootWindow());
246 // shouldn't have a parent
247 QCOMPARE(root.parent(), xcb_window_t(XCB_WINDOW_NONE));
248 QVERIFY(root->children_len > 0);
249 xcb_window_t *children = root.children();
250 bool found = false;
251 for (int i = 0; i < xcb_query_tree_children_length(root.data()); ++i) {
252 if (children[i] == tree.window()) {
253 found = true;
254 break;
255 }
256 }
257 QVERIFY(found);
258
259 // query for not existing window
260 Xcb::Tree doesntExist(XCB_WINDOW_NONE);
261 QCOMPARE(doesntExist.parent(), xcb_window_t(XCB_WINDOW_NONE));
262 QVERIFY(doesntExist.isNull());
263 QVERIFY(doesntExist.isRetrieved());
264}
265
266void TestXcbWrapper::testCurrentInput()
267{
268 xcb_connection_t *c = QX11Info::connection();
269 m_testWindow.map();
270 QX11Info::setAppTime(QX11Info::getTimestamp());
271
272 // let's set the input focus
273 m_testWindow.focus(XCB_INPUT_FOCUS_PARENT, QX11Info::appTime());
274 xcb_flush(c);
275
277 QCOMPARE(input.window(), (xcb_window_t)m_testWindow);
278
279 // creating a copy should make the input object have no window any more
280 Xcb::CurrentInput input2(input);
281 QCOMPARE(input2.window(), (xcb_window_t)m_testWindow);
282 QCOMPARE(input.window(), xcb_window_t(XCB_WINDOW_NONE));
283}
284
285void TestXcbWrapper::testTransientFor()
286{
287 Xcb::TransientFor transient(m_testWindow);
288 QCOMPARE(transient.window(), (xcb_window_t)m_testWindow);
289 // our m_testWindow doesn't have a transient for hint
290 xcb_window_t compareWindow = XCB_WINDOW_NONE;
291 QVERIFY(!transient.getTransientFor(&compareWindow));
292 QCOMPARE(compareWindow, xcb_window_t(XCB_WINDOW_NONE));
293 bool ok = true;
294 QCOMPARE(transient.value<xcb_window_t>(32, XCB_ATOM_WINDOW, XCB_WINDOW_NONE, &ok), xcb_window_t(XCB_WINDOW_NONE));
295 QVERIFY(!ok);
296 ok = true;
297 QCOMPARE(transient.value<xcb_window_t>(XCB_WINDOW_NONE, &ok), xcb_window_t(XCB_WINDOW_NONE));
298 QVERIFY(!ok);
299
300 // Create a Window with a transient for hint
301 Xcb::Window transientWindow(KWin::createWindow());
302 xcb_window_t testWindowId = m_testWindow;
303 transientWindow.changeProperty(XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32, 1, &testWindowId);
304
305 // let's get another transient object
306 Xcb::TransientFor realTransient(transientWindow);
307 QVERIFY(realTransient.getTransientFor(&compareWindow));
308 QCOMPARE(compareWindow, (xcb_window_t)m_testWindow);
309 ok = false;
310 QCOMPARE(realTransient.value<xcb_window_t>(32, XCB_ATOM_WINDOW, XCB_WINDOW_NONE, &ok), (xcb_window_t)m_testWindow);
311 QVERIFY(ok);
312 ok = false;
313 QCOMPARE(realTransient.value<xcb_window_t>(XCB_WINDOW_NONE, &ok), (xcb_window_t)m_testWindow);
314 QVERIFY(ok);
315 ok = false;
316 QCOMPARE(realTransient.value<xcb_window_t>(), (xcb_window_t)m_testWindow);
317 QCOMPARE(realTransient.value<xcb_window_t *>(nullptr, &ok)[0], (xcb_window_t)m_testWindow);
318 QVERIFY(ok);
319 QCOMPARE(realTransient.value<xcb_window_t *>()[0], (xcb_window_t)m_testWindow);
320
321 // test for a not existing window
322 Xcb::TransientFor doesntExist(XCB_WINDOW_NONE);
323 QVERIFY(!doesntExist.getTransientFor(&compareWindow));
324}
325
326void TestXcbWrapper::testPropertyByteArray()
327{
328 Xcb::Window testWindow(KWin::createWindow());
329 Xcb::Property prop(false, testWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 100000);
330 QCOMPARE(prop.toByteArray(), QByteArray());
331 bool ok = true;
332 QCOMPARE(prop.toByteArray(&ok), QByteArray());
333 QVERIFY(!ok);
334 ok = true;
335 QVERIFY(!prop.value<const char *>());
336 QCOMPARE(prop.value<const char *>("bar", &ok), "bar");
337 QVERIFY(!ok);
338 QCOMPARE(QByteArray(Xcb::StringProperty(testWindow, XCB_ATOM_WM_NAME)), QByteArray());
339
340 testWindow.changeProperty(XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, 3, "foo");
341 prop = Xcb::Property(false, testWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 100000);
342 QCOMPARE(prop.toByteArray(), QByteArrayLiteral("foo"));
343 QCOMPARE(prop.toByteArray(&ok), QByteArrayLiteral("foo"));
344 QVERIFY(ok);
345 QCOMPARE(prop.value<const char *>(nullptr, &ok), "foo");
346 QVERIFY(ok);
347 QCOMPARE(QByteArray(Xcb::StringProperty(testWindow, XCB_ATOM_WM_NAME)), QByteArrayLiteral("foo"));
348
349 // verify incorrect format and type
350 QCOMPARE(prop.toByteArray(32), QByteArray());
351 QCOMPARE(prop.toByteArray(8, XCB_ATOM_CARDINAL), QByteArray());
352
353 // verify empty property
354 testWindow.changeProperty(XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, 0, nullptr);
355 prop = Xcb::Property(false, testWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 100000);
356 QCOMPARE(prop.toByteArray(), QByteArray());
357 QCOMPARE(prop.toByteArray(&ok), QByteArray());
358 // valid bytearray
359 QVERIFY(ok);
360 // The bytearray should be empty
361 QVERIFY(prop.toByteArray().isEmpty());
362 // The bytearray should be not null
363 QVERIFY(!prop.toByteArray().isNull());
364 QVERIFY(!prop.value<const char *>());
365 QCOMPARE(QByteArray(Xcb::StringProperty(testWindow, XCB_ATOM_WM_NAME)), QByteArray());
366
367 // verify non existing property
368 Xcb::Atom invalid(QByteArrayLiteral("INVALID_ATOM"));
369 prop = Xcb::Property(false, testWindow, invalid, XCB_ATOM_STRING, 0, 100000);
370 QCOMPARE(prop.toByteArray(), QByteArray());
371 QCOMPARE(prop.toByteArray(&ok), QByteArray());
372 // invalid bytearray
373 QVERIFY(!ok);
374 // The bytearray should be empty
375 QVERIFY(prop.toByteArray().isEmpty());
376 // The bytearray should be not null
377 QVERIFY(prop.toByteArray().isNull());
378 QVERIFY(!prop.value<const char *>());
379 QCOMPARE(QByteArray(Xcb::StringProperty(testWindow, XCB_ATOM_WM_NAME)), QByteArray());
380}
381
382void TestXcbWrapper::testPropertyBool()
383{
384 Xcb::Window testWindow(KWin::createWindow());
385 Xcb::Atom blockCompositing(QByteArrayLiteral("_KDE_NET_WM_BLOCK_COMPOSITING"));
386 QVERIFY(blockCompositing != XCB_ATOM_NONE);
387 NETWinInfo info(QX11Info::connection(), testWindow, QX11Info::appRootWindow(), NET::Properties(), NET::WM2BlockCompositing);
388
389 Xcb::Property prop(false, testWindow, blockCompositing, XCB_ATOM_CARDINAL, 0, 100000);
390 bool ok = true;
391 QVERIFY(!prop.toBool());
392 QVERIFY(!prop.toBool(&ok));
393 QVERIFY(!ok);
394
395 info.setBlockingCompositing(true);
396 xcb_flush(QX11Info::connection());
397 prop = Xcb::Property(false, testWindow, blockCompositing, XCB_ATOM_CARDINAL, 0, 100000);
398 QVERIFY(prop.toBool());
399 QVERIFY(prop.toBool(&ok));
400 QVERIFY(ok);
401
402 // incorrect type and format
403 QVERIFY(!prop.toBool(8));
404 QVERIFY(!prop.toBool(32, blockCompositing));
405 QVERIFY(!prop.toBool(32, blockCompositing, &ok));
406 QVERIFY(!ok);
407
408 // incorrect value:
409 uint32_t d[] = {1, 0};
410 testWindow.changeProperty(blockCompositing, XCB_ATOM_CARDINAL, 32, 2, d);
411 prop = Xcb::Property(false, testWindow, blockCompositing, XCB_ATOM_CARDINAL, 0, 100000);
412 QVERIFY(!prop.toBool());
413 ok = true;
414 QVERIFY(!prop.toBool(&ok));
415 QVERIFY(!ok);
416}
417
418void TestXcbWrapper::testAtom()
419{
420 Xcb::Atom atom(QByteArrayLiteral("WM_CLIENT_MACHINE"));
421 QCOMPARE(atom.name(), QByteArrayLiteral("WM_CLIENT_MACHINE"));
422 QVERIFY(atom == XCB_ATOM_WM_CLIENT_MACHINE);
423 QVERIFY(atom.isValid());
424
425 // test the const paths
426 const Xcb::Atom &atom2(atom);
427 QVERIFY(atom2.isValid());
428 QVERIFY(atom2 == XCB_ATOM_WM_CLIENT_MACHINE);
429 QCOMPARE(atom2.name(), QByteArrayLiteral("WM_CLIENT_MACHINE"));
430
431 // destroy before retrieved
432 Xcb::Atom atom3(QByteArrayLiteral("WM_CLIENT_MACHINE"));
433 QCOMPARE(atom3.name(), QByteArrayLiteral("WM_CLIENT_MACHINE"));
434}
435
436void TestXcbWrapper::testMotifEmpty()
437{
438 Xcb::Atom atom(QByteArrayLiteral("_MOTIF_WM_HINTS"));
439 Xcb::MotifHints hints(atom);
440 // pre init
441 QCOMPARE(hints.hasDecoration(), false);
442 QCOMPARE(hints.noBorder(), false);
443 QCOMPARE(hints.resize(), true);
444 QCOMPARE(hints.move(), true);
445 QCOMPARE(hints.minimize(), true);
446 QCOMPARE(hints.maximize(), true);
447 QCOMPARE(hints.close(), true);
448 // post init, pre read
449 hints.init(m_testWindow);
450 QCOMPARE(hints.hasDecoration(), false);
451 QCOMPARE(hints.noBorder(), false);
452 QCOMPARE(hints.resize(), true);
453 QCOMPARE(hints.move(), true);
454 QCOMPARE(hints.minimize(), true);
455 QCOMPARE(hints.maximize(), true);
456 QCOMPARE(hints.close(), true);
457 // post read
458 hints.read();
459 QCOMPARE(hints.hasDecoration(), false);
460 QCOMPARE(hints.noBorder(), false);
461 QCOMPARE(hints.resize(), true);
462 QCOMPARE(hints.move(), true);
463 QCOMPARE(hints.minimize(), true);
464 QCOMPARE(hints.maximize(), true);
465 QCOMPARE(hints.close(), true);
466}
467
468void TestXcbWrapper::testMotif_data()
469{
470 QTest::addColumn<quint32>("flags");
471 QTest::addColumn<quint32>("functions");
472 QTest::addColumn<quint32>("decorations");
473
474 QTest::addColumn<bool>("expectedHasDecoration");
475 QTest::addColumn<bool>("expectedNoBorder");
476 QTest::addColumn<bool>("expectedResize");
477 QTest::addColumn<bool>("expectedMove");
478 QTest::addColumn<bool>("expectedMinimize");
479 QTest::addColumn<bool>("expectedMaximize");
480 QTest::addColumn<bool>("expectedClose");
481
482 QTest::newRow("none") << 0u << 0u << 0u << false << false << true << true << true << true << true;
483 QTest::newRow("noborder") << 2u << 5u << 0u << true << true << true << true << true << true << true;
484 QTest::newRow("border") << 2u << 5u << 1u << true << false << true << true << true << true << true;
485 QTest::newRow("resize") << 1u << 2u << 1u << false << false << true << false << false << false << false;
486 QTest::newRow("move") << 1u << 4u << 1u << false << false << false << true << false << false << false;
487 QTest::newRow("minimize") << 1u << 8u << 1u << false << false << false << false << true << false << false;
488 QTest::newRow("maximize") << 1u << 16u << 1u << false << false << false << false << false << true << false;
489 QTest::newRow("close") << 1u << 32u << 1u << false << false << false << false << false << false << true;
490
491 QTest::newRow("resize/all") << 1u << 3u << 1u << false << false << false << true << true << true << true;
492 QTest::newRow("move/all") << 1u << 5u << 1u << false << false << true << false << true << true << true;
493 QTest::newRow("minimize/all") << 1u << 9u << 1u << false << false << true << true << false << true << true;
494 QTest::newRow("maximize/all") << 1u << 17u << 1u << false << false << true << true << true << false << true;
495 QTest::newRow("close/all") << 1u << 33u << 1u << false << false << true << true << true << true << false;
496
497 QTest::newRow("all") << 1u << 62u << 1u << false << false << true << true << true << true << true;
498 QTest::newRow("all/all") << 1u << 63u << 1u << false << false << false << false << false << false << false;
499 QTest::newRow("all/all/deco") << 3u << 63u << 1u << true << false << false << false << false << false << false;
500}
501
502void TestXcbWrapper::testMotif()
503{
504 Xcb::Atom atom(QByteArrayLiteral("_MOTIF_WM_HINTS"));
505 QFETCH(quint32, flags);
506 QFETCH(quint32, functions);
507 QFETCH(quint32, decorations);
508 quint32 data[] = {
509 flags,
510 functions,
511 decorations,
512 0,
513 0};
514 xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, m_testWindow, atom, atom, 32, 5, data);
515 xcb_flush(QX11Info::connection());
516 Xcb::MotifHints hints(atom);
517 hints.init(m_testWindow);
518 hints.read();
519 QTEST(hints.hasDecoration(), "expectedHasDecoration");
520 QTEST(hints.noBorder(), "expectedNoBorder");
521 QTEST(hints.resize(), "expectedResize");
522 QTEST(hints.move(), "expectedMove");
523 QTEST(hints.minimize(), "expectedMinimize");
524 QTEST(hints.maximize(), "expectedMaximize");
525 QTEST(hints.close(), "expectedClose");
526}
527
528Q_CONSTRUCTOR_FUNCTION(forceXcb)
530#include "test_xcb_wrapper.moc"
const Reply * data()
Definition xcbutils.h:287
WindowId window() const
Definition xcbutils.h:297
void changeProperty(xcb_atom_t property, xcb_atom_t type, uint8_t format, uint32_t length, const void *data, uint8_t mode=XCB_PROP_MODE_REPLACE)
Definition xcbutils.h:1829
void create(const QRectF &geometry, uint32_t mask=0, const uint32_t *values=nullptr, xcb_window_t parent=rootWindow())
Definition xcbutils.h:1726
void reset(xcb_window_t window=XCB_WINDOW_NONE, bool destroy=true)
Definition xcbutils.h:1741
void focus(uint8_t revertTo=XCB_INPUT_FOCUS_POINTER_ROOT, xcb_timestamp_t time=XCB_TIME_CURRENT_TIME)
Definition xcbutils.h:1895
bool isValid() const
Definition xcbutils.h:1710
InputRedirection * input()
Definition input.h:549
QTEST_MAIN(OnScreenNotificationTest)