File indexing completed on 2024-04-21 15:05:30

0001 /*
0002     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "nettesthelper.h"
0008 #include <netwm.h>
0009 
0010 #include <QProcess>
0011 #include <QStandardPaths>
0012 #include <qtest_widgets.h>
0013 
0014 // system
0015 #include <unistd.h>
0016 
0017 using Property = UniqueCPointer<xcb_get_property_reply_t>;
0018 
0019 // clang-format off
0020 #define INFO NETWinInfo info(m_connection, m_testWindow, m_rootWindow, NET::WMAllProperties, NET::WM2AllProperties, NET::Client);
0021 
0022 #define ATOM(name) \
0023     KXUtils::Atom atom(connection(), QByteArrayLiteral(#name));
0024 
0025 #define UTF8 KXUtils::Atom utf8String(connection(), QByteArrayLiteral("UTF8_STRING"));
0026 
0027 #define GETPROP(type, length, formatSize) \
0028     xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, m_testWindow, \
0029                                        atom, type, 0, length); \
0030     Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); \
0031     QVERIFY(reply); \
0032     QCOMPARE(reply->format, uint8_t(formatSize)); \
0033     QCOMPARE(reply->value_len, uint32_t(length));
0034 
0035 #define VERIFYDELETED(t) \
0036     xcb_get_property_cookie_t cookieDeleted = xcb_get_property_unchecked(connection(), false, m_testWindow, \
0037             atom, t, 0, 1); \
0038     Property replyDeleted(xcb_get_property_reply(connection(), cookieDeleted, nullptr)); \
0039     QVERIFY(replyDeleted); \
0040     QVERIFY(replyDeleted->type == XCB_ATOM_NONE);
0041 
0042 class NetWinInfoTestClient : public QObject
0043 {
0044     Q_OBJECT
0045 private Q_SLOTS:
0046     void initTestCase();
0047     void cleanupTestCase();
0048     void init();
0049     void cleanup();
0050 
0051     void testBlockCompositing();
0052     void testUserTime();
0053     void testStartupId();
0054     void testDesktopFileName();
0055     void testAppMenuObjectPath();
0056     void testAppMenuServiceName();
0057     void testHandledIcons_data();
0058     void testHandledIcons();
0059     void testPid();
0060     void testName();
0061     void testIconName();
0062 #if KWINDOWSYSTEM_ENABLE_DEPRECATED_SINCE(5, 0)
0063     void testStrut();
0064 #endif
0065     void testExtendedStrut();
0066     void testIconGeometry();
0067     void testWindowType_data();
0068     void testWindowType();
0069 
0070     void testActivities_data();
0071     void testActivities();
0072     void testWindowRole();
0073     void testWindowClass();
0074     void testClientMachine();
0075     void testGroupLeader();
0076     void testUrgency_data();
0077     void testUrgency();
0078     void testInput_data();
0079     void testInput();
0080     void testInitialMappingState_data();
0081     void testInitialMappingState();
0082     void testIconPixmap_data();
0083     void testIconPixmap();
0084     void testTransientFor();
0085     void testProtocols_data();
0086     void testProtocols();
0087     void testOpaqueRegion_data();
0088     void testOpaqueRegion();
0089 
0090 private:
0091     void performNameTest(xcb_atom_t atom, const char *(NETWinInfo:: *getter)(void)const, void (NETWinInfo:: *setter)(const char *), NET::Property property);
0092     void waitForPropertyChange(NETWinInfo *info, xcb_atom_t atom, NET::Property prop, NET::Property2 prop2 = NET::Property2(0));
0093     xcb_connection_t *connection()
0094     {
0095         return m_connection;
0096     }
0097     xcb_connection_t *m_connection;
0098     QVector<xcb_connection_t*> m_connections;
0099     std::unique_ptr<QProcess> m_xvfb;
0100     xcb_window_t m_rootWindow;
0101     xcb_window_t m_testWindow;
0102 };
0103 
0104 void NetWinInfoTestClient::initTestCase()
0105 {
0106 }
0107 
0108 void NetWinInfoTestClient::cleanupTestCase()
0109 {
0110     // close connection
0111     while (!m_connections.isEmpty()) {
0112         xcb_disconnect(m_connections.takeFirst());
0113     }
0114 }
0115 
0116 void NetWinInfoTestClient::init()
0117 {
0118     // first reset just to be sure
0119     m_connection = nullptr;
0120     m_rootWindow = XCB_WINDOW_NONE;
0121     m_testWindow = XCB_WINDOW_NONE;
0122 
0123     const QString xfvbExec = QStandardPaths::findExecutable(QStringLiteral("Xvfb"));
0124     QVERIFY(!xfvbExec.isEmpty());
0125 
0126     // start Xvfb
0127     m_xvfb.reset(new QProcess);
0128     // use pipe to pass fd to Xvfb to get back the display id
0129     int pipeFds[2];
0130     QVERIFY(pipe(pipeFds) == 0);
0131     m_xvfb->start(QStringLiteral("Xvfb"), QStringList{ QStringLiteral("-displayfd"), QString::number(pipeFds[1]) });
0132     QVERIFY(m_xvfb->waitForStarted());
0133     QCOMPARE(m_xvfb->state(), QProcess::Running);
0134 
0135     // reads from pipe, closes write side
0136     close(pipeFds[1]);
0137 
0138     QFile readPipe;
0139     QVERIFY(readPipe.open(pipeFds[0], QIODevice::ReadOnly, QFileDevice::AutoCloseHandle));
0140     QByteArray displayNumber = readPipe.readLine();
0141     readPipe.close();
0142 
0143     displayNumber.prepend(QByteArray(":"));
0144     displayNumber.remove(displayNumber.size() -1, 1);
0145 
0146     // create X connection
0147     int screen = 0;
0148     m_connection = xcb_connect(displayNumber.constData(), &screen);
0149     QVERIFY(m_connection);
0150     QVERIFY(!xcb_connection_has_error(m_connection));
0151     m_rootWindow = KXUtils::rootWindow(m_connection, screen);
0152 
0153     // create test window
0154     m_testWindow = xcb_generate_id(m_connection);
0155     uint32_t values[] = {XCB_EVENT_MASK_PROPERTY_CHANGE};
0156     xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_testWindow,
0157                       m_rootWindow,
0158                       0, 0, 100, 100, 0, XCB_COPY_FROM_PARENT,
0159                       XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);
0160     // and map it
0161     xcb_map_window(m_connection, m_testWindow);
0162 }
0163 
0164 void NetWinInfoTestClient::cleanup()
0165 {
0166     // destroy test window
0167     xcb_unmap_window(m_connection, m_testWindow);
0168     xcb_destroy_window(m_connection, m_testWindow);
0169     m_testWindow = XCB_WINDOW_NONE;
0170 
0171     // delay till clenupTestCase as otherwise xcb reuses the same memory address
0172     m_connections << connection();
0173     // kill Xvfb
0174     m_xvfb->terminate();
0175     m_xvfb->waitForFinished();
0176 }
0177 
0178 void NetWinInfoTestClient::waitForPropertyChange(NETWinInfo *info, xcb_atom_t atom, NET::Property prop, NET::Property2 prop2)
0179 {
0180     while (true) {
0181         UniqueCPointer<xcb_generic_event_t> event(xcb_wait_for_event(connection()));
0182         if (!event) {
0183             break;
0184         }
0185         if ((event->response_type & ~0x80) != XCB_PROPERTY_NOTIFY) {
0186             continue;
0187         }
0188         xcb_property_notify_event_t *pe = reinterpret_cast<xcb_property_notify_event_t *>(event.get());
0189         if (pe->window != m_testWindow) {
0190             continue;
0191         }
0192         if (pe->atom != atom) {
0193             continue;
0194         }
0195         NET::Properties dirty;
0196         NET::Properties2 dirty2;
0197         info->event(event.get(), &dirty, &dirty2);
0198         if (prop != 0) {
0199             QVERIFY(dirty & prop);
0200         }
0201         if (prop2 != 0) {
0202             QVERIFY(dirty2 & prop2);
0203         }
0204         if (!prop) {
0205             QCOMPARE(dirty, NET::Properties());
0206         }
0207         if (!prop2) {
0208             QCOMPARE(dirty2, NET::Properties2());
0209         }
0210         break;
0211     }
0212 }
0213 
0214 void NetWinInfoTestClient::testBlockCompositing()
0215 {
0216     QVERIFY(connection());
0217     ATOM(_KDE_NET_WM_BLOCK_COMPOSITING)
0218     INFO
0219 
0220     QVERIFY(!info.isBlockingCompositing());
0221     info.setBlockingCompositing(true);
0222     QVERIFY(info.isBlockingCompositing());
0223 
0224     // compare with the X property
0225     QVERIFY(atom != XCB_ATOM_NONE);
0226     GETPROP(XCB_ATOM_CARDINAL, 1, 32)
0227     QCOMPARE(reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()))[0], uint32_t(1));
0228 
0229     // and wait for our event
0230     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2BlockCompositing);
0231     QVERIFY(info.isBlockingCompositing());
0232 
0233     // setting false should delete the property again
0234     info.setBlockingCompositing(false);
0235     QVERIFY(!info.isBlockingCompositing());
0236     VERIFYDELETED(XCB_ATOM_CARDINAL)
0237 
0238     // and wait for our event
0239     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2BlockCompositing);
0240     QVERIFY(!info.isBlockingCompositing());
0241 }
0242 
0243 void NetWinInfoTestClient::testUserTime()
0244 {
0245     QVERIFY(connection());
0246     ATOM(_NET_WM_USER_TIME)
0247     INFO
0248 
0249     QCOMPARE(info.userTime(), uint32_t(-1));
0250     info.setUserTime(500);
0251     QCOMPARE(info.userTime(), uint32_t(500));
0252 
0253     // compare with the X property
0254     QVERIFY(atom != XCB_ATOM_NONE);
0255     GETPROP(XCB_ATOM_CARDINAL, 1, 32)
0256     QCOMPARE(reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()))[0], uint32_t(500));
0257 
0258     // and wait for our event
0259     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2UserTime);
0260     QCOMPARE(info.userTime(), uint32_t(500));
0261 }
0262 
0263 void NetWinInfoTestClient::testStartupId()
0264 {
0265     QVERIFY(connection());
0266     ATOM(_NET_STARTUP_ID)
0267     UTF8
0268     INFO
0269 
0270     QVERIFY(!info.startupId());
0271     info.setStartupId("foo");
0272     QCOMPARE(info.startupId(), "foo");
0273 
0274     // compare with the X property
0275     QVERIFY(atom != XCB_ATOM_NONE);
0276     QVERIFY(utf8String != XCB_ATOM_NONE);
0277     GETPROP(utf8String, 3, 8)
0278     QCOMPARE(reinterpret_cast<const char *>(xcb_get_property_value(reply.get())), "foo");
0279 
0280     // and wait for our event
0281     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2StartupId);
0282     QCOMPARE(info.startupId(), "foo");
0283 }
0284 
0285 void NetWinInfoTestClient::testAppMenuObjectPath()
0286 {
0287     ATOM(_KDE_NET_WM_APPMENU_OBJECT_PATH)
0288     INFO
0289 
0290     QVERIFY(!info.appMenuObjectPath());
0291 
0292     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow,
0293                         atom, XCB_ATOM_STRING, 8, 3, "foo");
0294     xcb_flush(connection());
0295 
0296     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2AppMenuObjectPath);
0297     QCOMPARE(info.appMenuObjectPath(), "foo");
0298 }
0299 
0300 void NetWinInfoTestClient::testAppMenuServiceName()
0301 {
0302     ATOM(_KDE_NET_WM_APPMENU_SERVICE_NAME)
0303     INFO
0304 
0305     QVERIFY(!info.appMenuServiceName());
0306 
0307     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow,
0308                         atom, XCB_ATOM_STRING, 8, 3, "foo");
0309     xcb_flush(connection());
0310 
0311     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2AppMenuServiceName);
0312     QCOMPARE(info.appMenuServiceName(), "foo");
0313 }
0314 
0315 void NetWinInfoTestClient::testDesktopFileName()
0316 {
0317     QVERIFY(connection());
0318     ATOM(_KDE_NET_WM_DESKTOP_FILE)
0319     UTF8
0320     INFO
0321 
0322     QVERIFY(!info.desktopFileName());
0323     info.setDesktopFileName("foo");
0324     QCOMPARE(info.desktopFileName(), "foo");
0325 
0326     // compare with the X property
0327     QVERIFY(atom != XCB_ATOM_NONE);
0328     QVERIFY(utf8String != XCB_ATOM_NONE);
0329     GETPROP(utf8String, 3, 8)
0330     QCOMPARE(reinterpret_cast<const char *>(xcb_get_property_value(reply.get())), "foo");
0331 
0332     // and wait for our event
0333     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2DesktopFileName);
0334     QCOMPARE(info.desktopFileName(), "foo");
0335 }
0336 
0337 void NetWinInfoTestClient::testHandledIcons_data()
0338 {
0339     QTest::addColumn<bool>("handled");
0340     QTest::addColumn<uint32_t>("value");
0341 
0342     QTest::newRow("enabled") << true << uint32_t(1);
0343     QTest::newRow("disabled") << false << uint32_t(0);
0344 }
0345 
0346 void NetWinInfoTestClient::testHandledIcons()
0347 {
0348     QVERIFY(connection());
0349     ATOM(_NET_WM_HANDLED_ICONS)
0350     INFO
0351 
0352     QVERIFY(!info.handledIcons());
0353     QFETCH(bool, handled);
0354     info.setHandledIcons(handled);
0355     QCOMPARE(info.handledIcons(), handled);
0356 
0357     // compare with the X property
0358     QVERIFY(atom != XCB_ATOM_NONE);
0359     GETPROP(XCB_ATOM_CARDINAL, 1, 32)
0360     QTEST(reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()))[0], "value");
0361 
0362     // and wait for our event
0363     waitForPropertyChange(&info, atom, NET::WMHandledIcons);
0364     QCOMPARE(info.handledIcons(), handled);
0365 }
0366 
0367 void NetWinInfoTestClient::testPid()
0368 {
0369     QVERIFY(connection());
0370     ATOM(_NET_WM_PID)
0371     INFO
0372 
0373     QCOMPARE(info.pid(), 0);
0374     info.setPid(m_xvfb->processId());
0375     QCOMPARE(info.pid(), m_xvfb->processId());
0376 
0377     // compare with the X property
0378     QVERIFY(atom != XCB_ATOM_NONE);
0379     GETPROP(XCB_ATOM_CARDINAL, 1, 32)
0380     QCOMPARE(reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()))[0], uint32_t(m_xvfb->processId()));
0381 
0382     // and wait for our event
0383     waitForPropertyChange(&info, atom, NET::WMPid);
0384     QCOMPARE(info.pid(), m_xvfb->processId());
0385 }
0386 
0387 void NetWinInfoTestClient::performNameTest(xcb_atom_t atom, const char *(NETWinInfo:: *getter)(void)const, void (NETWinInfo:: *setter)(const char *), NET::Property property)
0388 {
0389     UTF8
0390     INFO
0391 
0392     QVERIFY(!(info.*getter)());
0393     (info.*setter)("foo");
0394     QCOMPARE((info.*getter)(), "foo");
0395 
0396     // compare with the X property
0397     QVERIFY(atom != XCB_ATOM_NONE);
0398     QVERIFY(utf8String != XCB_ATOM_NONE);
0399     GETPROP(utf8String, 3, 8)
0400     QCOMPARE(reinterpret_cast<const char *>(xcb_get_property_value(reply.get())), "foo");
0401 
0402     // and wait for our event
0403     waitForPropertyChange(&info, atom, property);
0404     QCOMPARE((info.*getter)(), "foo");
0405 
0406     // delete the string
0407     (info.*setter)("");
0408     QCOMPARE((info.*getter)(), "");
0409     VERIFYDELETED(utf8String)
0410 
0411     // and wait for our event
0412     waitForPropertyChange(&info, atom, property);
0413     QVERIFY(!(info.*getter)());
0414 
0415     // set it again, to ensure that we don't leak on tear-down
0416     (info.*setter)("bar");
0417     QCOMPARE((info.*getter)(), "bar");
0418     xcb_flush(connection());
0419     waitForPropertyChange(&info, atom, property);
0420     QCOMPARE((info.*getter)(), "bar");
0421 }
0422 
0423 void NetWinInfoTestClient::testIconName()
0424 {
0425     QVERIFY(connection());
0426     ATOM(_NET_WM_ICON_NAME)
0427     performNameTest(atom, &NETWinInfo::iconName, &NETWinInfo::setIconName, NET::WMIconName);
0428 }
0429 
0430 void NetWinInfoTestClient::testName()
0431 {
0432     QVERIFY(connection());
0433     ATOM(_NET_WM_NAME)
0434     performNameTest(atom, &NETWinInfo::name, &NETWinInfo::setName, NET::WMName);
0435 }
0436 
0437 #if KWINDOWSYSTEM_ENABLE_DEPRECATED_SINCE(5, 0)
0438 void NetWinInfoTestClient::testStrut()
0439 {
0440     QVERIFY(connection());
0441     ATOM(_NET_WM_STRUT)
0442     INFO
0443 
0444     NETStrut extents = info.strut();
0445     QCOMPARE(extents.bottom, 0);
0446     QCOMPARE(extents.left, 0);
0447     QCOMPARE(extents.right, 0);
0448     QCOMPARE(extents.top, 0);
0449 
0450     NETStrut newExtents;
0451     newExtents.bottom = 10;
0452     newExtents.left   = 20;
0453     newExtents.right  = 30;
0454     newExtents.top    = 40;
0455     info.setStrut(newExtents);
0456     extents = info.strut();
0457     QCOMPARE(extents.bottom, newExtents.bottom);
0458     QCOMPARE(extents.left,   newExtents.left);
0459     QCOMPARE(extents.right,  newExtents.right);
0460     QCOMPARE(extents.top,    newExtents.top);
0461 
0462     // compare with the X property
0463     QVERIFY(atom != XCB_ATOM_NONE);
0464     GETPROP(XCB_ATOM_CARDINAL, 4, 32)
0465     uint32_t *data = reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()));
0466     QCOMPARE(data[0], uint32_t(newExtents.left));
0467     QCOMPARE(data[1], uint32_t(newExtents.right));
0468     QCOMPARE(data[2], uint32_t(newExtents.top));
0469     QCOMPARE(data[3], uint32_t(newExtents.bottom));
0470 
0471     // and wait for our event
0472     waitForPropertyChange(&info, atom, NET::WMStrut);
0473     extents = info.strut();
0474     QCOMPARE(extents.bottom, newExtents.bottom);
0475     QCOMPARE(extents.left,   newExtents.left);
0476     QCOMPARE(extents.right,  newExtents.right);
0477     QCOMPARE(extents.top,    newExtents.top);
0478 }
0479 #endif
0480 
0481 void NetWinInfoTestClient::testExtendedStrut()
0482 {
0483     QVERIFY(connection());
0484     ATOM(_NET_WM_STRUT_PARTIAL)
0485     INFO
0486 
0487     NETExtendedStrut extents = info.extendedStrut();
0488     QCOMPARE(extents.left_width, 0);
0489     QCOMPARE(extents.right_width, 0);
0490     QCOMPARE(extents.top_width, 0);
0491     QCOMPARE(extents.bottom_width, 0);
0492     QCOMPARE(extents.left_start, 0);
0493     QCOMPARE(extents.left_end, 0);
0494     QCOMPARE(extents.right_start, 0);
0495     QCOMPARE(extents.right_end, 0);
0496     QCOMPARE(extents.top_start, 0);
0497     QCOMPARE(extents.top_end, 0);
0498     QCOMPARE(extents.bottom_start, 0);
0499     QCOMPARE(extents.bottom_end, 0);
0500 
0501     NETExtendedStrut newExtents;
0502     newExtents.left_width   = 10;
0503     newExtents.right_width  = 20;
0504     newExtents.top_width    = 30;
0505     newExtents.bottom_width = 40;
0506     newExtents.left_start   = 50;
0507     newExtents.left_end     = 60;
0508     newExtents.right_start  = 70;
0509     newExtents.right_end    = 80;
0510     newExtents.top_start    = 90;
0511     newExtents.top_end      = 91;
0512     newExtents.bottom_start = 92;
0513     newExtents.bottom_end   = 93;
0514     info.setExtendedStrut(newExtents);
0515     extents = info.extendedStrut();
0516     QCOMPARE(extents.left_width,   newExtents.left_width);
0517     QCOMPARE(extents.right_width,  newExtents.right_width);
0518     QCOMPARE(extents.top_width,    newExtents.top_width);
0519     QCOMPARE(extents.bottom_width, newExtents.bottom_width);
0520     QCOMPARE(extents.left_start,   newExtents.left_start);
0521     QCOMPARE(extents.left_end,     newExtents.left_end);
0522     QCOMPARE(extents.right_start,  newExtents.right_start);
0523     QCOMPARE(extents.right_end,    newExtents.right_end);
0524     QCOMPARE(extents.top_start,    newExtents.top_start);
0525     QCOMPARE(extents.top_end,      newExtents.top_end);
0526     QCOMPARE(extents.bottom_start, newExtents.bottom_start);
0527     QCOMPARE(extents.bottom_end,   newExtents.bottom_end);
0528 
0529     // compare with the X property
0530     QVERIFY(atom != XCB_ATOM_NONE);
0531     GETPROP(XCB_ATOM_CARDINAL, 12, 32)
0532     uint32_t *data = reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()));
0533     QCOMPARE(data[ 0], uint32_t(newExtents.left_width));
0534     QCOMPARE(data[ 1], uint32_t(newExtents.right_width));
0535     QCOMPARE(data[ 2], uint32_t(newExtents.top_width));
0536     QCOMPARE(data[ 3], uint32_t(newExtents.bottom_width));
0537     QCOMPARE(data[ 4], uint32_t(newExtents.left_start));
0538     QCOMPARE(data[ 5], uint32_t(newExtents.left_end));
0539     QCOMPARE(data[ 6], uint32_t(newExtents.right_start));
0540     QCOMPARE(data[ 7], uint32_t(newExtents.right_end));
0541     QCOMPARE(data[ 8], uint32_t(newExtents.top_start));
0542     QCOMPARE(data[ 9], uint32_t(newExtents.top_end));
0543     QCOMPARE(data[10], uint32_t(newExtents.bottom_start));
0544     QCOMPARE(data[11], uint32_t(newExtents.bottom_end));
0545 
0546     // and wait for our event
0547     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2ExtendedStrut);
0548     extents = info.extendedStrut();
0549     QCOMPARE(extents.left_width,   newExtents.left_width);
0550     QCOMPARE(extents.right_width,  newExtents.right_width);
0551     QCOMPARE(extents.top_width,    newExtents.top_width);
0552     QCOMPARE(extents.bottom_width, newExtents.bottom_width);
0553     QCOMPARE(extents.left_start,   newExtents.left_start);
0554     QCOMPARE(extents.left_end,     newExtents.left_end);
0555     QCOMPARE(extents.right_start,  newExtents.right_start);
0556     QCOMPARE(extents.right_end,    newExtents.right_end);
0557     QCOMPARE(extents.top_start,    newExtents.top_start);
0558     QCOMPARE(extents.top_end,      newExtents.top_end);
0559     QCOMPARE(extents.bottom_start, newExtents.bottom_start);
0560     QCOMPARE(extents.bottom_end,   newExtents.bottom_end);
0561 }
0562 
0563 void NetWinInfoTestClient::testIconGeometry()
0564 {
0565     QVERIFY(connection());
0566     ATOM(_NET_WM_ICON_GEOMETRY)
0567     INFO
0568 
0569     NETRect geo = info.iconGeometry();
0570     QCOMPARE(geo.pos.x, 0);
0571     QCOMPARE(geo.pos.y, 0);
0572     QCOMPARE(geo.size.width, 0);
0573     QCOMPARE(geo.size.height, 0);
0574 
0575     NETRect newGeo;
0576     newGeo.pos.x       = 10;
0577     newGeo.pos.y       = 20;
0578     newGeo.size.width  = 30;
0579     newGeo.size.height = 40;
0580     info.setIconGeometry(newGeo);
0581     geo = info.iconGeometry();
0582     QCOMPARE(geo.pos.x,       newGeo.pos.x);
0583     QCOMPARE(geo.pos.y,       newGeo.pos.y);
0584     QCOMPARE(geo.size.width,  newGeo.size.width);
0585     QCOMPARE(geo.size.height, newGeo.size.height);
0586 
0587     // compare with the X property
0588     QVERIFY(atom != XCB_ATOM_NONE);
0589     GETPROP(XCB_ATOM_CARDINAL, 4, 32)
0590     uint32_t *data = reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()));
0591     QCOMPARE(data[0], uint32_t(newGeo.pos.x));
0592     QCOMPARE(data[1], uint32_t(newGeo.pos.y));
0593     QCOMPARE(data[2], uint32_t(newGeo.size.width));
0594     QCOMPARE(data[3], uint32_t(newGeo.size.height));
0595 
0596     // and wait for our event
0597     waitForPropertyChange(&info, atom, NET::WMIconGeometry);
0598     geo = info.iconGeometry();
0599     QCOMPARE(geo.pos.x,       newGeo.pos.x);
0600     QCOMPARE(geo.pos.y,       newGeo.pos.y);
0601     QCOMPARE(geo.size.width,  newGeo.size.width);
0602     QCOMPARE(geo.size.height, newGeo.size.height);
0603 }
0604 
0605 Q_DECLARE_METATYPE(NET::WindowType)
0606 void NetWinInfoTestClient::testWindowType_data()
0607 {
0608     QTest::addColumn<NET::WindowType>("type");
0609     QTest::addColumn<int>("length");
0610     QTest::addColumn<QByteArray>("typeAtom");
0611     QTest::addColumn<QByteArray>("secondaryTypeAtom");
0612 
0613     QTest::newRow("override")     << NET::Override     << 2 << QByteArrayLiteral("_KDE_NET_WM_WINDOW_TYPE_OVERRIDE")  << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_NORMAL");
0614     QTest::newRow("TopMenu")      << NET::TopMenu      << 2 << QByteArrayLiteral("_KDE_NET_WM_WINDOW_TYPE_TOPMENU")   << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DOCK");
0615     QTest::newRow("Utility")      << NET::Utility      << 2 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_UTILITY")       << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DIALOG");
0616     QTest::newRow("Splash")       << NET::Splash       << 2 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_SPLASH")        << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DOCK");
0617     // TODO: this should be 2
0618     QTest::newRow("DropdownMenu") << NET::DropdownMenu << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU") << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_MENU");
0619     // TODO: this should be 2
0620     QTest::newRow("PopupMenu")    << NET::PopupMenu    << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_POPUP_MENU")    << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_MENU");
0621     // TODO: this should be 2
0622     QTest::newRow("Notification") << NET::Notification << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_NOTIFICATION")  << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_UTILITY");
0623     QTest::newRow("Dialog")       << NET::Dialog       << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DIALOG")   << QByteArray();
0624     QTest::newRow("Menu")         << NET::Menu         << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_MENU")     << QByteArray();
0625     QTest::newRow("Toolbar")      << NET::Toolbar      << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_TOOLBAR")  << QByteArray();
0626     QTest::newRow("Dock")         << NET::Dock         << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DOCK")     << QByteArray();
0627     QTest::newRow("Desktop")      << NET::Desktop      << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DESKTOP")  << QByteArray();
0628     QTest::newRow("Tooltip")      << NET::Tooltip      << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_TOOLTIP")  << QByteArray();
0629     QTest::newRow("ComboBox")     << NET::ComboBox     << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_COMBO")    << QByteArray();
0630     QTest::newRow("DNDIcon")      << NET::DNDIcon      << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DND")      << QByteArray();
0631     QTest::newRow("Normal")       << NET::Normal       << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_NORMAL")   << QByteArray();
0632     QTest::newRow("OnScreenDisplay") << NET::OnScreenDisplay << 1 << QByteArrayLiteral("_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY") << QByteArray();
0633     QTest::newRow("CriticalNotification") << NET::CriticalNotification << 1 << QByteArrayLiteral("_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION") << QByteArray();
0634     QTest::newRow("AppletPopup")  << NET::AppletPopup  << 1 << QByteArrayLiteral("_KDE_NET_WM_WINDOW_TYPE_APPLET_POPUP") << QByteArray();
0635 }
0636 
0637 void NetWinInfoTestClient::testWindowType()
0638 {
0639     QVERIFY(connection());
0640     ATOM(_NET_WM_WINDOW_TYPE)
0641     INFO
0642 
0643     QVERIFY(info.hasWindowType());
0644     QVERIFY(!info.hasNETSupport());
0645     QCOMPARE(info.windowType(NET::AllTypesMask), NET::Unknown);
0646     QFETCH(NET::WindowType, type);
0647     info.setWindowType(type);
0648     // it does not update the internal type!
0649     QCOMPARE(info.windowType(NET::AllTypesMask), NET::Unknown);
0650     QFETCH(int, length);
0651     QFETCH(QByteArray, typeAtom);
0652 
0653     // compare the X property
0654     KXUtils::Atom type1(connection(), typeAtom);
0655     QVERIFY(atom != XCB_ATOM_NONE);
0656     GETPROP(XCB_ATOM_ATOM, length, 32)
0657     xcb_atom_t *atoms = reinterpret_cast<xcb_atom_t *>(xcb_get_property_value(reply.get()));
0658     QCOMPARE(atoms[0], xcb_atom_t(type1));
0659     if (reply->value_len > 1) {
0660         QFETCH(QByteArray, secondaryTypeAtom);
0661         KXUtils::Atom type2(connection(), secondaryTypeAtom);
0662         QVERIFY(type2 != XCB_ATOM_NONE);
0663         QCOMPARE(atoms[1], xcb_atom_t(type2));
0664     }
0665 
0666     waitForPropertyChange(&info, atom, NET::WMWindowType);
0667     QCOMPARE(info.windowType(NET::AllTypesMask), type);
0668     QVERIFY(info.hasNETSupport());
0669 }
0670 
0671 void NetWinInfoTestClient::testClientMachine()
0672 {
0673     QVERIFY(connection());
0674     INFO
0675 
0676     QVERIFY(!info.clientMachine());
0677 
0678     // client machine needs to be changed using xcb
0679     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow,
0680                         XCB_ATOM_WM_CLIENT_MACHINE, XCB_ATOM_STRING, 8, 9, "localhost");
0681     xcb_flush(connection());
0682 
0683     // only updated after event
0684     waitForPropertyChange(&info, XCB_ATOM_WM_CLIENT_MACHINE, NET::Property(0), NET::WM2ClientMachine);
0685     QCOMPARE(info.clientMachine(), "localhost");
0686 }
0687 
0688 void NetWinInfoTestClient::testGroupLeader()
0689 {
0690     QVERIFY(connection());
0691     INFO
0692 
0693     QVERIFY(info.groupLeader() == XCB_WINDOW_NONE);
0694 
0695     // group leader needs to be changed through wm hints
0696     uint32_t values[] = {
0697         1 << 6, /* WindowGroupHint*/
0698         1, /* Input */
0699         1, /* Normal State */
0700         XCB_NONE, /* icon pixmap */
0701         XCB_NONE, /* icon window */
0702         XCB_NONE, /* icon x */
0703         XCB_NONE, /* icon y */
0704         XCB_NONE, /* icon mask */
0705         m_rootWindow /* group leader */
0706     };
0707     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow,
0708                         XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, 9, values);
0709     xcb_flush(connection());
0710 
0711     // only updated after event
0712     waitForPropertyChange(&info, XCB_ATOM_WM_HINTS, NET::Property(0), NET::WM2GroupLeader);
0713     QCOMPARE(info.groupLeader(), m_rootWindow);
0714 }
0715 
0716 void NetWinInfoTestClient::testUrgency_data()
0717 {
0718     QTest::addColumn<quint32>("flags");
0719     QTest::addColumn<bool>("expected");
0720 
0721     QTest::newRow("urgency") << quint32(1 << 8) << true;
0722     QTest::newRow("none") << quint32(0) << false;
0723     QTest::newRow("group_urgency") << quint32((1 << 6) | (1 << 8)) << true;
0724     QTest::newRow("input") << quint32(1) << false;
0725 }
0726 
0727 void NetWinInfoTestClient::testUrgency()
0728 {
0729     QVERIFY(connection());
0730     INFO
0731 
0732     QVERIFY(!info.urgency());
0733     QFETCH(quint32, flags);
0734 
0735     // group leader needs to be changed through wm hints
0736     uint32_t values[] = {
0737         flags,
0738         1, /* Input */
0739         1, /* Normal State */
0740         XCB_NONE, /* icon pixmap */
0741         XCB_NONE, /* icon window */
0742         XCB_NONE, /* icon x */
0743         XCB_NONE, /* icon y */
0744         XCB_NONE, /* icon mask */
0745         XCB_NONE /* group leader */
0746     };
0747     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow,
0748                         XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, 9, values);
0749     xcb_flush(connection());
0750 
0751     // only updated after event
0752     waitForPropertyChange(&info, XCB_ATOM_WM_HINTS, NET::Property(0), NET::WM2Urgency);
0753     QTEST(info.urgency(), "expected");
0754 }
0755 
0756 void NetWinInfoTestClient::testInput_data()
0757 {
0758     QTest::addColumn<quint32>("flags");
0759     QTest::addColumn<quint32>("input");
0760     QTest::addColumn<bool>("expected");
0761 
0762     QTest::newRow("flag_input")      << quint32(1) << quint32(1) << true;
0763     QTest::newRow("flag_noinput")    << quint32(1) << quint32(0) << false;
0764     QTest::newRow("noflag_input")    << quint32(0) << quint32(1) << true;
0765     QTest::newRow("noflag_noinput")  << quint32(0) << quint32(0) << true;
0766     QTest::newRow("flag_with_other_input")   << quint32(1 | 1 << 8) << quint32(1) << true;
0767     QTest::newRow("flag_with_other_noinput") << quint32(1 | 1 << 8) << quint32(0) << false;
0768 }
0769 
0770 void NetWinInfoTestClient::testInput()
0771 {
0772     QVERIFY(connection());
0773     INFO
0774 
0775     QVERIFY(info.input());
0776     QFETCH(quint32, flags);
0777     QFETCH(quint32, input);
0778 
0779     // group leader needs to be changed through wm hints
0780     uint32_t values[] = {
0781         flags,
0782         input, /* Input */
0783         1, /* Normal State */
0784         XCB_NONE, /* icon pixmap */
0785         XCB_NONE, /* icon window */
0786         XCB_NONE, /* icon x */
0787         XCB_NONE, /* icon y */
0788         XCB_NONE, /* icon mask */
0789         XCB_NONE /* group leader */
0790     };
0791     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow,
0792                         XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, 9, values);
0793     xcb_flush(connection());
0794 
0795     // only updated after event
0796     waitForPropertyChange(&info, XCB_ATOM_WM_HINTS, NET::Property(0), NET::WM2Urgency);
0797     QTEST(info.input(), "expected");
0798 }
0799 
0800 Q_DECLARE_METATYPE(NET::MappingState)
0801 
0802 void NetWinInfoTestClient::testInitialMappingState_data()
0803 {
0804     QTest::addColumn<quint32>("flags");
0805     QTest::addColumn<quint32>("state");
0806     QTest::addColumn<NET::MappingState>("expected");
0807 
0808     QTest::newRow("flag-iconic") << quint32(2) << quint32(3) << NET::Iconic;
0809     QTest::newRow("flag-normal") << quint32(2) << quint32(1) << NET::Visible;
0810     QTest::newRow("flag-invalid") << quint32(2) << quint32(8) << NET::Withdrawn;
0811     QTest::newRow("noflag-iconic") << quint32(256) << quint32(3) << NET::Withdrawn;
0812     QTest::newRow("noflag-normal") << quint32(256) << quint32(1) << NET::Withdrawn;
0813 }
0814 
0815 void NetWinInfoTestClient::testInitialMappingState()
0816 {
0817     QVERIFY(connection());
0818     INFO
0819 
0820     QCOMPARE(info.initialMappingState(), NET::Withdrawn);
0821     QFETCH(quint32, flags);
0822     QFETCH(quint32, state);
0823 
0824     // group leader needs to be changed through wm hints
0825     uint32_t values[] = {
0826         flags,
0827         1, /* Input */
0828         state, /* Normal State */
0829         XCB_NONE, /* icon pixmap */
0830         XCB_NONE, /* icon window */
0831         XCB_NONE, /* icon x */
0832         XCB_NONE, /* icon y */
0833         XCB_NONE, /* icon mask */
0834         XCB_NONE /* group leader */
0835     };
0836     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow,
0837                         XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, 9, values);
0838     xcb_flush(connection());
0839 
0840     // only updated after event
0841     waitForPropertyChange(&info, XCB_ATOM_WM_HINTS, NET::Property(0), NET::WM2InitialMappingState);
0842     QTEST(info.initialMappingState(), "expected");
0843 }
0844 
0845 void NetWinInfoTestClient::testIconPixmap_data()
0846 {
0847     QTest::addColumn<quint32>("flags");
0848     QTest::addColumn<quint32>("icon");
0849     QTest::addColumn<quint32>("mask");
0850     QTest::addColumn<quint32>("expectedPixmap");
0851     QTest::addColumn<quint32>("expectedMask");
0852 
0853     QTest::newRow("invalid-flags") << 1u << 2u << 3u << 0u << 0u;
0854     QTest::newRow("pixmap-flags") << 4u << 2u << 3u << 2u << 0u;
0855     QTest::newRow("mask-flags") << 32u << 2u << 3u << 0u << 3u;
0856     QTest::newRow("pixmap-mask-flags") << 36u << 2u << 3u << 2u << 3u;
0857 }
0858 
0859 void NetWinInfoTestClient::testIconPixmap()
0860 {
0861     QVERIFY(connection());
0862     INFO
0863 
0864     QCOMPARE(info.icccmIconPixmap(), 0u);
0865     QCOMPARE(info.icccmIconPixmapMask(), 0u);
0866     QFETCH(quint32, flags);
0867     QFETCH(quint32, icon);
0868     QFETCH(quint32, mask);
0869 
0870     // icon pixmap needs to be changed through wm hints
0871     uint32_t values[] = {
0872         flags,
0873         1, /* Input */
0874         XCB_NONE, /* Normal State */
0875         icon,     /* icon pixmap */
0876         XCB_NONE, /* icon window */
0877         XCB_NONE, /* icon x */
0878         XCB_NONE, /* icon y */
0879         mask,     /* icon mask */
0880         XCB_NONE  /* group leader */
0881     };
0882     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow,
0883                         XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, 9, values);
0884     xcb_flush(connection());
0885     // only updated after event
0886     waitForPropertyChange(&info, XCB_ATOM_WM_HINTS, NET::Property(0), NET::WM2IconPixmap);
0887     QTEST(info.icccmIconPixmap(), "expectedPixmap");
0888     QTEST(info.icccmIconPixmapMask(), "expectedMask");
0889 }
0890 
0891 void NetWinInfoTestClient::testTransientFor()
0892 {
0893     QVERIFY(connection());
0894     INFO
0895 
0896     QVERIFY(info.transientFor() == XCB_WINDOW_NONE);
0897     // transient for needs to be changed using xcb
0898     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow,
0899                         XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32, 1, &m_rootWindow);
0900     xcb_flush(connection());
0901 
0902     // only updated after event
0903     waitForPropertyChange(&info, XCB_ATOM_WM_TRANSIENT_FOR, NET::Property(0), NET::WM2TransientFor);
0904     QCOMPARE(info.transientFor(), m_rootWindow);
0905 }
0906 
0907 void NetWinInfoTestClient::testWindowClass()
0908 {
0909     QVERIFY(connection());
0910     INFO
0911 
0912     QVERIFY(!info.windowClassClass());
0913     QVERIFY(!info.windowClassName());
0914 
0915     // window class needs to be changed using xcb
0916     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow,
0917                         XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, 7, "foo\0bar");
0918     xcb_flush(connection());
0919 
0920     // only updated after event
0921     waitForPropertyChange(&info, XCB_ATOM_WM_CLASS, NET::Property(0), NET::WM2WindowClass);
0922     QCOMPARE(info.windowClassName(), "foo");
0923     QCOMPARE(info.windowClassClass(), "bar");
0924 }
0925 
0926 void NetWinInfoTestClient::testWindowRole()
0927 {
0928     QVERIFY(connection());
0929     ATOM(WM_WINDOW_ROLE)
0930     INFO
0931 
0932     QVERIFY(!info.windowRole());
0933 
0934     // window role needs to be changed using xcb
0935     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow,
0936                         atom, XCB_ATOM_STRING, 8, 3, "bar");
0937     xcb_flush(connection());
0938 
0939     // only updated after event
0940     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2WindowRole);
0941     QCOMPARE(info.windowRole(), "bar");
0942 }
0943 
0944 void NetWinInfoTestClient::testActivities_data()
0945 {
0946     QTest::addColumn<QByteArray>("activities");
0947     QTest::addColumn<QByteArray>("expectedActivities");
0948 
0949     const QByteArray testActivities = QByteArrayLiteral("foo,bar");
0950     const QByteArray allActivities = QByteArrayLiteral(KDE_ALL_ACTIVITIES_UUID);
0951 
0952     QTest::newRow("activities") << testActivities << testActivities;
0953     QTest::newRow("empty") << QByteArray() << allActivities;
0954     QTest::newRow("\\0") << QByteArrayLiteral("\0") << allActivities;
0955 }
0956 
0957 void NetWinInfoTestClient::testActivities()
0958 {
0959     QVERIFY(connection());
0960     ATOM(_KDE_NET_WM_ACTIVITIES)
0961     INFO
0962 
0963     QVERIFY(!info.activities());
0964     QFETCH(QByteArray, activities);
0965 
0966     // activities needs to be changed using xcb
0967     info.setActivities(activities.isNull() ? nullptr : activities.constData());
0968     xcb_flush(connection());
0969 
0970     // only updated after event
0971     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2Activities);
0972     QTEST(QByteArray(info.activities()), "expectedActivities");
0973 }
0974 
0975 Q_DECLARE_METATYPE(NET::Protocols)
0976 void NetWinInfoTestClient::testProtocols_data()
0977 {
0978     QTest::addColumn<NET::Protocols>("protocols");
0979     QTest::addColumn<bool>("takeFocus");
0980     QTest::addColumn<bool>("deleteWindow");
0981     QTest::addColumn<bool>("ping");
0982     QTest::addColumn<bool>("sync");
0983     QTest::addColumn<bool>("context");
0984 
0985     const NET::Protocol t = NET::TakeFocusProtocol;
0986     const NET::Protocol d = NET::DeleteWindowProtocol;
0987     const NET::Protocol p = NET::PingProtocol;
0988     const NET::Protocol s = NET::SyncRequestProtocol;
0989     const NET::Protocol c = NET::ContextHelpProtocol;
0990 
0991     QTest::newRow("none") << NET::Protocols(NET::NoProtocol) << false << false << false << false << false;
0992 
0993     QTest::newRow("t") << NET::Protocols(t) << true << false << false << false << false;
0994     QTest::newRow("d") << NET::Protocols(d) << false << true << false << false << false;
0995     QTest::newRow("p") << NET::Protocols(p) << false << false << true << false << false;
0996     QTest::newRow("s") << NET::Protocols(s) << false << false << false << true << false;
0997     QTest::newRow("c") << NET::Protocols(c) << false << false << false << false << true;
0998 
0999     // all two combinations with t
1000     QTest::newRow("t/d") << NET::Protocols(t | d) << true << true  << false << false << false;
1001     QTest::newRow("t/p") << NET::Protocols(t | p) << true << false << true  << false << false;
1002     QTest::newRow("t/s") << NET::Protocols(t | s) << true << false << false << true  << false;
1003     QTest::newRow("t/c") << NET::Protocols(t | c) << true << false << false << false << true;
1004     // all two combinations with d
1005     QTest::newRow("d/p") << NET::Protocols(d | p) << false << true << true  << false << false;
1006     QTest::newRow("d/s") << NET::Protocols(d | s) << false << true << false << true  << false;
1007     QTest::newRow("d/c") << NET::Protocols(d | c) << false << true << false << false << true;
1008     // all two combinations with p
1009     QTest::newRow("p/s") << NET::Protocols(p | s) << false << false << true << true  << false;
1010     QTest::newRow("p/c") << NET::Protocols(p | c) << false << false << true << false << true;
1011     // and remaining two combination
1012     QTest::newRow("s/c") << NET::Protocols(s | c) << false << false << false << true << true;
1013 
1014     // all three combinations with t
1015     QTest::newRow("t/d/p") << NET::Protocols(t | d | p) << true << true  << true  << false << false;
1016     QTest::newRow("t/d/s") << NET::Protocols(t | d | s) << true << true  << false << true  << false;
1017     QTest::newRow("t/d/c") << NET::Protocols(t | d | c) << true << true  << false << false << true;
1018     QTest::newRow("t/p/s") << NET::Protocols(t | p | s) << true << false << true  << true  << false;
1019     QTest::newRow("t/p/c") << NET::Protocols(t | p | c) << true << false << true  << false << true;
1020     QTest::newRow("t/s/c") << NET::Protocols(t | s | c) << true << false << false << true  << true;
1021     // all three combinations with d
1022     QTest::newRow("d/p/s") << NET::Protocols(d | p | s) << false << true << true  << true  << false;
1023     QTest::newRow("d/p/c") << NET::Protocols(d | p | c) << false << true << true  << false << true;
1024     QTest::newRow("d/s/c") << NET::Protocols(d | s | c) << false << true << false << true  << true;
1025     // and remaining
1026     QTest::newRow("p/s/c") << NET::Protocols(p | s | c) << false << false << true << true << true;
1027 
1028     QTest::newRow("t/d/p/s") << NET::Protocols(t | d | p | s) << true  << true  << true  << true  << false;
1029     QTest::newRow("t/d/p/c") << NET::Protocols(t | d | p | c) << true  << true  << true  << false << true;
1030     QTest::newRow("t/d/s/c") << NET::Protocols(t | d | s | c) << true  << true  << false << true  << true;
1031     QTest::newRow("t/p/s/c") << NET::Protocols(t | p | s | c) << true  << false << true  << true  << true;
1032     QTest::newRow("d/p/s/c") << NET::Protocols(d | p | s | c) << false << true  << true  << true  << true;
1033 
1034     QTest::newRow("all") << NET::Protocols(t | d | p | s | c) << true << true << true << true << true;
1035 }
1036 
1037 void NetWinInfoTestClient::testProtocols()
1038 {
1039     QVERIFY(connection());
1040     ATOM(WM_PROTOCOLS)
1041     KXUtils::Atom takeFocus(connection(), QByteArrayLiteral("WM_TAKE_FOCUS"));
1042     KXUtils::Atom deleteWindow(connection(), QByteArrayLiteral("WM_DELETE_WINDOW"));
1043     KXUtils::Atom ping(connection(), QByteArrayLiteral("_NET_WM_PING"));
1044     KXUtils::Atom syncRequest(connection(), QByteArrayLiteral("_NET_WM_SYNC_REQUEST"));
1045     KXUtils::Atom contextHelp(connection(), QByteArrayLiteral("_NET_WM_CONTEXT_HELP"));
1046     INFO
1047 
1048     QVERIFY(!info.supportsProtocol(NET::TakeFocusProtocol));
1049     QVERIFY(!info.supportsProtocol(NET::DeleteWindowProtocol));
1050     QVERIFY(!info.supportsProtocol(NET::PingProtocol));
1051     QVERIFY(!info.supportsProtocol(NET::SyncRequestProtocol));
1052     QVERIFY(!info.supportsProtocol(NET::ContextHelpProtocol));
1053     QCOMPARE(info.protocols(), NET::Protocols(NET::NoProtocol));
1054 
1055     QVector<xcb_atom_t> props;
1056     QFETCH(NET::Protocols, protocols);
1057     if (protocols.testFlag(NET::TakeFocusProtocol)) {
1058         props << takeFocus;
1059     }
1060     if (protocols.testFlag(NET::DeleteWindowProtocol)) {
1061         props << deleteWindow;
1062     }
1063     if (protocols.testFlag(NET::PingProtocol)) {
1064         props << ping;
1065     }
1066     if (protocols.testFlag(NET::SyncRequestProtocol)) {
1067         props << syncRequest;
1068     }
1069     if (protocols.testFlag(NET::ContextHelpProtocol)) {
1070         props << contextHelp;
1071     }
1072 
1073     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, atom, XCB_ATOM_ATOM, 32, props.size(), props.constData());
1074     xcb_flush(connection());
1075 
1076     // only updated after event
1077     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2Protocols);
1078     QCOMPARE(info.protocols(), protocols);
1079     QTEST(info.supportsProtocol(NET::TakeFocusProtocol), "takeFocus");
1080     QTEST(info.supportsProtocol(NET::DeleteWindowProtocol), "deleteWindow");
1081     QTEST(info.supportsProtocol(NET::PingProtocol), "ping");
1082     QTEST(info.supportsProtocol(NET::SyncRequestProtocol), "sync");
1083     QTEST(info.supportsProtocol(NET::ContextHelpProtocol), "context");
1084 
1085     xcb_delete_property(connection(), m_testWindow, atom);
1086     xcb_flush(connection());
1087     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2Protocols);
1088     QVERIFY(!info.supportsProtocol(NET::TakeFocusProtocol));
1089     QVERIFY(!info.supportsProtocol(NET::DeleteWindowProtocol));
1090     QVERIFY(!info.supportsProtocol(NET::PingProtocol));
1091     QVERIFY(!info.supportsProtocol(NET::SyncRequestProtocol));
1092     QVERIFY(!info.supportsProtocol(NET::ContextHelpProtocol));
1093     QCOMPARE(info.protocols(), NET::Protocols(NET::NoProtocol));
1094 }
1095 
1096 void NetWinInfoTestClient::testOpaqueRegion_data()
1097 {
1098     QTest::addColumn<QVector<QRect> >("geometries");
1099 
1100     QTest::newRow("none") << QVector<QRect>();
1101     QTest::newRow("empty") << QVector<QRect>({QRect(0, 0, 0, 0)});
1102     QTest::newRow("one rect") << QVector<QRect>({QRect(10, 20, 30, 40)});
1103     QTest::newRow("two rect") << QVector<QRect>({QRect(10, 20, 30, 40), QRect(1, 2, 4, 5)});
1104     QTest::newRow("multiple") << QVector<QRect>({QRect(10, 20, 30, 40),
1105                                                  QRect(1, 2, 4, 5),
1106                                                  QRect(100, 0, 200, 400),
1107                                                  QRect(1, 2, 4, 5)});
1108 }
1109 
1110 void NetWinInfoTestClient::testOpaqueRegion()
1111 {
1112     QVERIFY(connection());
1113     ATOM(_NET_WM_OPAQUE_REGION)
1114     INFO
1115 
1116     QCOMPARE(info.opaqueRegion().size(), std::size_t(0));
1117 
1118     QFETCH(QVector<QRect>, geometries);
1119     QVector<qint32> data;
1120     for (auto it = geometries.constBegin(); it != geometries.constEnd(); ++it) {
1121         const QRect &r = *it;
1122         data << r.x();
1123         data << r.y();
1124         data << r.width();
1125         data << r.height();
1126     }
1127 
1128     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, atom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData());
1129     xcb_flush(connection());
1130 
1131     // only updated after event
1132     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2OpaqueRegion);
1133     const auto opaqueRegion = info.opaqueRegion();
1134     QCOMPARE(opaqueRegion.size(), std::size_t(geometries.size()));
1135 
1136     for (std::size_t i = 0; i < opaqueRegion.size(); ++i) {
1137         auto r1 = opaqueRegion.at(i);
1138         auto r2 = geometries.at(i);
1139         QCOMPARE(r1.pos.x, r2.x());
1140         QCOMPARE(r1.pos.y, r2.y());
1141         QCOMPARE(r1.size.width, r2.width());
1142         QCOMPARE(r1.size.height, r2.height());
1143     }
1144 
1145     xcb_delete_property(connection(), m_testWindow, atom);
1146     xcb_flush(connection());
1147     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2OpaqueRegion);
1148     QCOMPARE(info.opaqueRegion().size(), std::size_t(0));
1149 }
1150 
1151 QTEST_GUILESS_MAIN(NetWinInfoTestClient)
1152 
1153 #include "netwininfotestclient.moc"