File indexing completed on 2024-04-21 03:59:20

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 Q_DECLARE_METATYPE(NET::State)
0018 Q_DECLARE_METATYPE(NET::States)
0019 Q_DECLARE_METATYPE(NET::Actions)
0020 
0021 using Property = UniqueCPointer<xcb_get_property_reply_t>;
0022 
0023 // clang-format off
0024 #define INFO NETWinInfo info(m_connection, m_testWindow, m_rootWindow, NET::WMAllProperties, NET::WM2AllProperties, NET::WindowManager);
0025 
0026 #define ATOM(name) \
0027     KXUtils::Atom atom(connection(), QByteArrayLiteral(#name));
0028 
0029 #define UTF8 KXUtils::Atom utf8String(connection(), QByteArrayLiteral("UTF8_STRING"));
0030 
0031 #define GETPROP(type, length, formatSize) \
0032     xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, m_testWindow, \
0033                                        atom, type, 0, length); \
0034     Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); \
0035     QVERIFY(reply); \
0036     QCOMPARE(reply->format, uint8_t(formatSize)); \
0037     QCOMPARE(reply->value_len, uint32_t(length));
0038 
0039 #define VERIFYDELETED(t) \
0040     xcb_get_property_cookie_t cookieDeleted = xcb_get_property_unchecked(connection(), false, m_testWindow, \
0041             atom, t, 0, 1); \
0042     Property replyDeleted(xcb_get_property_reply(connection(), cookieDeleted, nullptr)); \
0043     QVERIFY(replyDeleted); \
0044     QVERIFY(replyDeleted->type == XCB_ATOM_NONE);
0045 
0046 class NetWinInfoTestWM : public QObject
0047 {
0048     Q_OBJECT
0049 private Q_SLOTS:
0050     void initTestCase();
0051     void cleanupTestCase();
0052     void init();
0053     void cleanup();
0054 
0055     void testState_data();
0056     void testState();
0057     void testVisibleName();
0058     void testVisibleIconName();
0059     void testDesktop_data();
0060     void testDesktop();
0061     void testOpacity_data();
0062     void testOpacity();
0063     void testAllowedActions_data();
0064     void testAllowedActions();
0065     void testFrameExtents();
0066     void testFrameExtentsKDE();
0067     void testFrameOverlap();
0068     void testFullscreenMonitors();
0069 
0070 private:
0071     bool hasAtomFlag(const xcb_atom_t *atoms, int atomsLenght, const QByteArray &actionName);
0072     void testStrut(xcb_atom_t atom, NETStrut(NETWinInfo:: *getter)(void)const, void (NETWinInfo:: *setter)(NETStrut), NET::Property property, NET::Property2 property2 = NET::Property2(0));
0073     void waitForPropertyChange(NETWinInfo *info, xcb_atom_t atom, NET::Property prop, NET::Property2 prop2 = NET::Property2(0));
0074     xcb_connection_t *connection()
0075     {
0076         return m_connection;
0077     }
0078     xcb_connection_t *m_connection;
0079     QList<xcb_connection_t*> m_connections;
0080     std::unique_ptr<QProcess> m_xvfb;
0081     xcb_window_t m_rootWindow;
0082     xcb_window_t m_testWindow;
0083     QByteArray m_displayNumber;
0084 };
0085 
0086 void NetWinInfoTestWM::initTestCase()
0087 {
0088 }
0089 
0090 void NetWinInfoTestWM::cleanupTestCase()
0091 {
0092     // close connection
0093     while (!m_connections.isEmpty()) {
0094         xcb_disconnect(m_connections.takeFirst());
0095     }
0096 }
0097 
0098 void NetWinInfoTestWM::init()
0099 {
0100     // first reset just to be sure
0101     m_connection = nullptr;
0102     m_rootWindow = XCB_WINDOW_NONE;
0103     m_testWindow = XCB_WINDOW_NONE;
0104 
0105     const QString xfvbExec = QStandardPaths::findExecutable(QStringLiteral("Xvfb"));
0106     QVERIFY(!xfvbExec.isEmpty());
0107 
0108     // start Xvfb
0109     m_xvfb.reset(new QProcess);
0110 
0111     // use pipe to pass fd to Xvfb to get back the display id
0112     int pipeFds[2];
0113     QVERIFY(pipe(pipeFds) == 0);
0114     m_xvfb->start(QStringLiteral("Xvfb"), QStringList{ QStringLiteral("-displayfd"), QString::number(pipeFds[1]) });
0115     QVERIFY(m_xvfb->waitForStarted());
0116     QCOMPARE(m_xvfb->state(), QProcess::Running);
0117 
0118     // reads from pipe, closes write side
0119     close(pipeFds[1]);
0120 
0121     QFile readPipe;
0122     QVERIFY(readPipe.open(pipeFds[0], QIODevice::ReadOnly, QFileDevice::AutoCloseHandle));
0123     QByteArray displayNumber = readPipe.readLine();
0124     readPipe.close();
0125 
0126     displayNumber.prepend(QByteArray(":"));
0127     displayNumber.remove(displayNumber.size() -1, 1);
0128     m_displayNumber = displayNumber;
0129 
0130     // create X connection
0131     int screen = 0;
0132     m_connection = xcb_connect(displayNumber.constData(), &screen);
0133     QVERIFY(m_connection);
0134     QVERIFY(!xcb_connection_has_error(m_connection));
0135     m_rootWindow = KXUtils::rootWindow(m_connection, screen);
0136 
0137     // create test window
0138     m_testWindow = xcb_generate_id(m_connection);
0139     uint32_t values[] = {XCB_EVENT_MASK_PROPERTY_CHANGE};
0140     xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_testWindow,
0141                       m_rootWindow,
0142                       0, 0, 100, 100, 0, XCB_COPY_FROM_PARENT,
0143                       XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);
0144     // and map it
0145     xcb_map_window(m_connection, m_testWindow);
0146 }
0147 
0148 void NetWinInfoTestWM::cleanup()
0149 {
0150     // destroy test window
0151     xcb_unmap_window(m_connection, m_testWindow);
0152     xcb_destroy_window(m_connection, m_testWindow);
0153     m_testWindow = XCB_WINDOW_NONE;
0154 
0155     // delay till clenupTestCase as otherwise xcb reuses the same memory address
0156     m_connections << connection();
0157     // kill Xvfb
0158     m_xvfb->terminate();
0159     m_xvfb->waitForFinished();
0160 }
0161 
0162 void NetWinInfoTestWM::waitForPropertyChange(NETWinInfo *info, xcb_atom_t atom, NET::Property prop, NET::Property2 prop2)
0163 {
0164     while (true) {
0165         UniqueCPointer<xcb_generic_event_t> event(xcb_wait_for_event(connection()));
0166         if (!event) {
0167             break;
0168         }
0169         if ((event->response_type & ~0x80) != XCB_PROPERTY_NOTIFY) {
0170             continue;
0171         }
0172         xcb_property_notify_event_t *pe = reinterpret_cast<xcb_property_notify_event_t *>(event.get());
0173         if (pe->window != m_testWindow) {
0174             continue;
0175         }
0176         if (pe->atom != atom) {
0177             continue;
0178         }
0179         NET::Properties dirty;
0180         NET::Properties2 dirty2;
0181         info->event(event.get(), &dirty, &dirty2);
0182         if (prop != 0) {
0183             QVERIFY(dirty & prop);
0184         }
0185         if (prop2 != 0) {
0186             QVERIFY(dirty2 & prop2);
0187         }
0188         if (!prop) {
0189             QCOMPARE(dirty, NET::Properties());
0190         }
0191         if (!prop2) {
0192             QCOMPARE(dirty2, NET::Properties2());
0193         }
0194         break;
0195     }
0196 }
0197 
0198 bool NetWinInfoTestWM::hasAtomFlag(const xcb_atom_t *atoms, int atomsLength, const QByteArray &actionName)
0199 {
0200     KXUtils::Atom atom(connection(), actionName);
0201     if (atom == XCB_ATOM_NONE) {
0202         qDebug() << "get atom failed";
0203         return false;
0204     }
0205     for (int i = 0; i < atomsLength; ++i) {
0206         if (atoms[i] == atom) {
0207             return true;
0208         }
0209     }
0210     return false;
0211 }
0212 
0213 void NetWinInfoTestWM::testAllowedActions_data()
0214 {
0215     QTest::addColumn<NET::Actions>("actions");
0216     QTest::addColumn<QList<QByteArray> >("names");
0217 
0218     const QByteArray move       = QByteArrayLiteral("_NET_WM_ACTION_MOVE");
0219     const QByteArray resize     = QByteArrayLiteral("_NET_WM_ACTION_RESIZE");
0220     const QByteArray minimize   = QByteArrayLiteral("_NET_WM_ACTION_MINIMIZE");
0221     const QByteArray shade      = QByteArrayLiteral("_NET_WM_ACTION_SHADE");
0222     const QByteArray stick      = QByteArrayLiteral("_NET_WM_ACTION_STICK");
0223     const QByteArray maxVert    = QByteArrayLiteral("_NET_WM_ACTION_MAXIMIZE_VERT");
0224     const QByteArray maxHoriz   = QByteArrayLiteral("_NET_WM_ACTION_MAXIMIZE_HORZ");
0225     const QByteArray fullscreen = QByteArrayLiteral("_NET_WM_ACTION_FULLSCREEN");
0226     const QByteArray desktop    = QByteArrayLiteral("_NET_WM_ACTION_CHANGE_DESKTOP");
0227     const QByteArray close      = QByteArrayLiteral("_NET_WM_ACTION_CLOSE");
0228 
0229     QTest::newRow("move")       << NET::Actions(NET::ActionMove)          << (QList<QByteArray>() << move);
0230     QTest::newRow("resize")     << NET::Actions(NET::ActionResize)        << (QList<QByteArray>() << resize);
0231     QTest::newRow("minimize")   << NET::Actions(NET::ActionMinimize)      << (QList<QByteArray>() << minimize);
0232     QTest::newRow("shade")      << NET::Actions(NET::ActionShade)         << (QList<QByteArray>() << shade);
0233     QTest::newRow("stick")      << NET::Actions(NET::ActionStick)         << (QList<QByteArray>() << stick);
0234     QTest::newRow("maxVert")    << NET::Actions(NET::ActionMaxVert)       << (QList<QByteArray>() << maxVert);
0235     QTest::newRow("maxHoriz")   << NET::Actions(NET::ActionMaxHoriz)      << (QList<QByteArray>() << maxHoriz);
0236     QTest::newRow("fullscreen") << NET::Actions(NET::ActionFullScreen)    << (QList<QByteArray>() << fullscreen);
0237     QTest::newRow("desktop")    << NET::Actions(NET::ActionChangeDesktop) << (QList<QByteArray>() << desktop);
0238     QTest::newRow("close")      << NET::Actions(NET::ActionClose)         << (QList<QByteArray>() << close);
0239 
0240     QTest::newRow("none") << NET::Actions() << QList<QByteArray>();
0241 
0242     QTest::newRow("all") << NET::Actions(NET::ActionMove |
0243                                     NET::ActionResize |
0244                                     NET::ActionMinimize |
0245                                     NET::ActionShade |
0246                                     NET::ActionStick |
0247                                     NET::ActionMaxVert |
0248                                     NET::ActionMaxHoriz |
0249                                     NET::ActionFullScreen |
0250                                     NET::ActionChangeDesktop |
0251                                     NET::ActionClose)
0252                          << (QList<QByteArray>() << move << resize << minimize << shade <<
0253                              stick << maxVert << maxHoriz <<
0254                              fullscreen << desktop << close);
0255 }
0256 
0257 void NetWinInfoTestWM::testAllowedActions()
0258 {
0259     QVERIFY(connection());
0260     ATOM(_NET_WM_ALLOWED_ACTIONS)
0261     INFO
0262 
0263     QCOMPARE(info.allowedActions(), NET::Actions());
0264     QFETCH(NET::Actions, actions);
0265     info.setAllowedActions(actions);
0266     QCOMPARE(info.allowedActions(), actions);
0267 
0268     // compare with the X property
0269     QFETCH(QList<QByteArray>, names);
0270     QVERIFY(atom != XCB_ATOM_NONE);
0271     GETPROP(XCB_ATOM_ATOM, names.size(), 32)
0272     xcb_atom_t *atoms = reinterpret_cast<xcb_atom_t *>(xcb_get_property_value(reply.get()));
0273     for (int i = 0; i < names.size(); ++i) {
0274         QVERIFY(hasAtomFlag(atoms, names.size(), names.at(i)));
0275     }
0276 
0277     // and wait for our event
0278     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2AllowedActions);
0279     QCOMPARE(info.allowedActions(), actions);
0280 }
0281 
0282 void NetWinInfoTestWM::testDesktop_data()
0283 {
0284     QTest::addColumn<int>("desktop");
0285     QTest::addColumn<uint32_t>("propertyDesktop");
0286 
0287     QTest::newRow("1") << 1 << uint32_t(0);
0288     QTest::newRow("4") << 4 << uint32_t(3);
0289     QTest::newRow("on all") << int(NET::OnAllDesktops) << uint32_t(~0);
0290 }
0291 
0292 void NetWinInfoTestWM::testDesktop()
0293 {
0294     QVERIFY(connection());
0295     ATOM(_NET_WM_DESKTOP)
0296     INFO
0297 
0298     QCOMPARE(info.desktop(), 0);
0299     QFETCH(int, desktop);
0300     info.setDesktop(desktop);
0301     QCOMPARE(info.desktop(), desktop);
0302 
0303     // compare with the X property
0304     QVERIFY(atom != XCB_ATOM_NONE);
0305     GETPROP(XCB_ATOM_CARDINAL, 1, 32)
0306     QTEST(reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()))[0], "propertyDesktop");
0307 
0308     // and wait for our event
0309     waitForPropertyChange(&info, atom, NET::WMDesktop);
0310     QCOMPARE(info.desktop(), desktop);
0311 
0312     // delete it
0313     info.setDesktop(0);
0314     QCOMPARE(info.desktop(), 0);
0315     VERIFYDELETED(XCB_ATOM_CARDINAL)
0316 
0317     // and wait for our event
0318     waitForPropertyChange(&info, atom, NET::WMDesktop);
0319     QCOMPARE(info.desktop(), 0);
0320 }
0321 
0322 void NetWinInfoTestWM::testStrut(xcb_atom_t atom, NETStrut(NETWinInfo:: *getter)(void)const, void (NETWinInfo:: *setter)(NETStrut), NET::Property property, NET::Property2 property2)
0323 {
0324     INFO
0325 
0326     NETStrut extents = (info.*getter)();
0327     QCOMPARE(extents.bottom, 0);
0328     QCOMPARE(extents.left, 0);
0329     QCOMPARE(extents.right, 0);
0330     QCOMPARE(extents.top, 0);
0331 
0332     NETStrut newExtents;
0333     newExtents.bottom = 10;
0334     newExtents.left   = 20;
0335     newExtents.right  = 30;
0336     newExtents.top    = 40;
0337     (info.*setter)(newExtents);
0338     extents = (info.*getter)();
0339     QCOMPARE(extents.bottom, newExtents.bottom);
0340     QCOMPARE(extents.left,   newExtents.left);
0341     QCOMPARE(extents.right,  newExtents.right);
0342     QCOMPARE(extents.top,    newExtents.top);
0343 
0344     // compare with the X property
0345     QVERIFY(atom != XCB_ATOM_NONE);
0346     GETPROP(XCB_ATOM_CARDINAL, 4, 32)
0347     uint32_t *data = reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()));
0348     QCOMPARE(data[0], uint32_t(newExtents.left));
0349     QCOMPARE(data[1], uint32_t(newExtents.right));
0350     QCOMPARE(data[2], uint32_t(newExtents.top));
0351     QCOMPARE(data[3], uint32_t(newExtents.bottom));
0352 
0353     // and wait for our event
0354     waitForPropertyChange(&info, atom, property, property2);
0355     extents = (info.*getter)();
0356     QCOMPARE(extents.bottom, newExtents.bottom);
0357     QCOMPARE(extents.left,   newExtents.left);
0358     QCOMPARE(extents.right,  newExtents.right);
0359     QCOMPARE(extents.top,    newExtents.top);
0360 }
0361 
0362 void NetWinInfoTestWM::testFrameExtents()
0363 {
0364     QVERIFY(connection());
0365     ATOM(_NET_FRAME_EXTENTS)
0366     testStrut(atom, &NETWinInfo::frameExtents, &NETWinInfo::setFrameExtents, NET::WMFrameExtents);
0367 }
0368 
0369 void NetWinInfoTestWM::testFrameExtentsKDE()
0370 {
0371     // same as testFrameExtents just with a different atom name
0372     QVERIFY(connection());
0373     ATOM(_KDE_NET_WM_FRAME_STRUT)
0374     testStrut(atom, &NETWinInfo::frameExtents, &NETWinInfo::setFrameExtents, NET::WMFrameExtents);
0375 }
0376 
0377 void NetWinInfoTestWM::testFrameOverlap()
0378 {
0379     QVERIFY(connection());
0380     ATOM(_NET_WM_FRAME_OVERLAP)
0381     testStrut(atom, &NETWinInfo::frameOverlap, &NETWinInfo::setFrameOverlap, NET::Property(0), NET::WM2FrameOverlap);
0382 }
0383 
0384 void NetWinInfoTestWM::testOpacity_data()
0385 {
0386     QTest::addColumn<uint32_t>("opacity");
0387 
0388     QTest::newRow("0 %")   << uint32_t(0);
0389     QTest::newRow("50 %")  << uint32_t(0x0000ffff);
0390     QTest::newRow("100 %") << uint32_t(0xffffffff);
0391 }
0392 
0393 void NetWinInfoTestWM::testOpacity()
0394 {
0395     QVERIFY(connection());
0396     ATOM(_NET_WM_WINDOW_OPACITY)
0397     INFO
0398 
0399     QCOMPARE(info.opacity(), static_cast<unsigned long>(0xffffffffU));
0400     QFETCH(uint32_t, opacity);
0401     info.setOpacity(opacity);
0402     QCOMPARE(info.opacity(), static_cast<unsigned long>(opacity));
0403 
0404     // compare with the X property
0405     QVERIFY(atom != XCB_ATOM_NONE);
0406     GETPROP(XCB_ATOM_CARDINAL, 1, 32)
0407     QCOMPARE(reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()))[0], opacity);
0408 
0409     // and wait for our event
0410     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2Opacity);
0411     QCOMPARE(info.opacity(), static_cast<unsigned long>(opacity));
0412 }
0413 
0414 void NetWinInfoTestWM::testState_data()
0415 {
0416     QTest::addColumn<NET::States>("states");
0417     QTest::addColumn<QList<QByteArray> >("names");
0418 
0419     const QByteArray modal            = QByteArrayLiteral("_NET_WM_STATE_MODAL");
0420     const QByteArray sticky           = QByteArrayLiteral("_NET_WM_STATE_STICKY");
0421     const QByteArray maxVert          = QByteArrayLiteral("_NET_WM_STATE_MAXIMIZED_VERT");
0422     const QByteArray maxHoriz         = QByteArrayLiteral("_NET_WM_STATE_MAXIMIZED_HORZ");
0423     const QByteArray shaded           = QByteArrayLiteral("_NET_WM_STATE_SHADED");
0424     const QByteArray skipTaskbar      = QByteArrayLiteral("_NET_WM_STATE_SKIP_TASKBAR");
0425     const QByteArray skipSwitcher     = QByteArrayLiteral("_KDE_NET_WM_STATE_SKIP_SWITCHER");
0426     const QByteArray keepAbove        = QByteArrayLiteral("_NET_WM_STATE_ABOVE");
0427     const QByteArray staysOnTop       = QByteArrayLiteral("_NET_WM_STATE_STAYS_ON_TOP");
0428     const QByteArray skipPager        = QByteArrayLiteral("_NET_WM_STATE_SKIP_PAGER");
0429     const QByteArray hidden           = QByteArrayLiteral("_NET_WM_STATE_HIDDEN");
0430     const QByteArray fullScreen       = QByteArrayLiteral("_NET_WM_STATE_FULLSCREEN");
0431     const QByteArray keepBelow        = QByteArrayLiteral("_NET_WM_STATE_BELOW");
0432     const QByteArray demandsAttention = QByteArrayLiteral("_NET_WM_STATE_DEMANDS_ATTENTION");
0433     const QByteArray focused          = QByteArrayLiteral("_NET_WM_STATE_FOCUSED");
0434 
0435     QTest::newRow("modal")            << NET::States(NET::Modal)            << (QList<QByteArray>() << modal);
0436     QTest::newRow("sticky")           << NET::States(NET::Sticky)           << (QList<QByteArray>() << sticky);
0437     QTest::newRow("maxVert")          << NET::States(NET::MaxVert)          << (QList<QByteArray>() << maxVert);
0438     QTest::newRow("maxHoriz")         << NET::States(NET::MaxHoriz)         << (QList<QByteArray>() << maxHoriz);
0439     QTest::newRow("shaded")           << NET::States(NET::Shaded)           << (QList<QByteArray>() << shaded);
0440     QTest::newRow("skipTaskbar")      << NET::States(NET::SkipTaskbar)      << (QList<QByteArray>() << skipTaskbar);
0441     QTest::newRow("keepAbove")        << NET::States(NET::KeepAbove)        << (QList<QByteArray>() << keepAbove << staysOnTop);
0442     QTest::newRow("skipPager")        << NET::States(NET::SkipPager)        << (QList<QByteArray>() << skipPager);
0443     QTest::newRow("hidden")           << NET::States(NET::Hidden)           << (QList<QByteArray>() << hidden);
0444     QTest::newRow("fullScreen")       << NET::States(NET::FullScreen)       << (QList<QByteArray>() << fullScreen);
0445     QTest::newRow("keepBelow")        << NET::States(NET::KeepBelow)        << (QList<QByteArray>() << keepBelow);
0446     QTest::newRow("demandsAttention") << NET::States(NET::DemandsAttention) << (QList<QByteArray>() << demandsAttention);
0447     QTest::newRow("skipSwitcher")     << NET::States(NET::SkipSwitcher)     << (QList<QByteArray>() << skipSwitcher);
0448     QTest::newRow("focused")          << NET::States(NET::Focused)          << (QList<QByteArray>() << focused);
0449 
0450     // TODO: it's possible to be keep above and below at the same time?!?
0451     QTest::newRow("all") << NET::States(NET::Modal |
0452                                    NET::Sticky |
0453                                    NET::Max |
0454                                    NET::Shaded |
0455                                    NET::SkipTaskbar |
0456                                    NET::SkipPager |
0457                                    NET::KeepAbove |
0458                                    NET::KeepBelow |
0459                                    NET::Hidden |
0460                                    NET::FullScreen |
0461                                    NET::DemandsAttention |
0462                                    NET::SkipSwitcher  |
0463                                    NET::Focused)
0464                          << (QList<QByteArray>() << modal << sticky << maxVert << maxHoriz
0465                              << shaded << skipTaskbar << keepAbove
0466                              << skipPager << hidden << fullScreen
0467                              << keepBelow << demandsAttention << staysOnTop << skipSwitcher << focused);
0468 }
0469 
0470 void NetWinInfoTestWM::testState()
0471 {
0472     QVERIFY(connection());
0473     ATOM(_NET_WM_STATE)
0474     INFO
0475 
0476     QCOMPARE(info.state(), NET::States());
0477     QFETCH(NET::States, states);
0478     info.setState(states, NET::States());
0479     QCOMPARE(info.state(), states);
0480 
0481     // compare with the X property
0482     QFETCH(QList<QByteArray>, names);
0483     QVERIFY(atom != XCB_ATOM_NONE);
0484     GETPROP(XCB_ATOM_ATOM, names.size(), 32)
0485     xcb_atom_t *atoms = reinterpret_cast<xcb_atom_t *>(xcb_get_property_value(reply.get()));
0486     for (int i = 0; i < names.size(); ++i) {
0487         QVERIFY(hasAtomFlag(atoms, names.size(), names.at(i)));
0488     }
0489 
0490     // and wait for our event
0491     waitForPropertyChange(&info, atom, NET::WMState);
0492     QCOMPARE(info.state(), states);
0493 }
0494 
0495 void NetWinInfoTestWM::testVisibleIconName()
0496 {
0497     QVERIFY(connection());
0498     ATOM(_NET_WM_VISIBLE_ICON_NAME)
0499     UTF8
0500     INFO
0501 
0502     QVERIFY(!info.visibleIconName());
0503     info.setVisibleIconName("foo");
0504     QCOMPARE(info.visibleIconName(), "foo");
0505 
0506     // compare with the X property
0507     QVERIFY(atom != XCB_ATOM_NONE);
0508     QVERIFY(utf8String != XCB_ATOM_NONE);
0509     GETPROP(utf8String, 3, 8)
0510     QCOMPARE(reinterpret_cast<const char *>(xcb_get_property_value(reply.get())), "foo");
0511 
0512     // and wait for our event
0513     waitForPropertyChange(&info, atom, NET::WMVisibleIconName);
0514     QCOMPARE(info.visibleIconName(), "foo");
0515 
0516     // delete the string
0517     info.setVisibleIconName("");
0518     QCOMPARE(info.visibleIconName(), "");
0519     VERIFYDELETED(utf8String)
0520     // and wait for our event
0521     waitForPropertyChange(&info, atom, NET::WMVisibleIconName);
0522     QVERIFY(!info.visibleIconName());
0523 
0524     // set again, to ensure we don't leak on tear down
0525     info.setVisibleIconName("bar");
0526     xcb_flush(connection());
0527     waitForPropertyChange(&info, atom, NET::WMVisibleIconName);
0528     QCOMPARE(info.visibleIconName(), "bar");
0529 }
0530 
0531 void NetWinInfoTestWM::testVisibleName()
0532 {
0533     QVERIFY(connection());
0534     ATOM(_NET_WM_VISIBLE_NAME)
0535     UTF8
0536     INFO
0537 
0538     QVERIFY(!info.visibleName());
0539     info.setVisibleName("foo");
0540     QCOMPARE(info.visibleName(), "foo");
0541 
0542     // compare with the X property
0543     QVERIFY(atom != XCB_ATOM_NONE);
0544     QVERIFY(utf8String != XCB_ATOM_NONE);
0545     GETPROP(utf8String, 3, 8)
0546     QCOMPARE(reinterpret_cast<const char *>(xcb_get_property_value(reply.get())), "foo");
0547 
0548     // and wait for our event
0549     waitForPropertyChange(&info, atom, NET::WMVisibleName);
0550     QCOMPARE(info.visibleName(), "foo");
0551 
0552     // delete the string
0553     info.setVisibleName("");
0554     QCOMPARE(info.visibleName(), "");
0555     VERIFYDELETED(utf8String)
0556 
0557     // and wait for our event
0558     waitForPropertyChange(&info, atom, NET::WMVisibleName);
0559     QVERIFY(!info.visibleName());
0560 
0561     // set again, to ensure we don't leak on tear down
0562     info.setVisibleName("bar");
0563     xcb_flush(connection());
0564     waitForPropertyChange(&info, atom, NET::WMVisibleName);
0565     QCOMPARE(info.visibleName(), "bar");
0566 }
0567 
0568 class MockWinInfo : public NETWinInfo
0569 {
0570 public:
0571     MockWinInfo(xcb_connection_t *connection, xcb_window_t window, xcb_window_t rootWindow)
0572         : NETWinInfo(connection, window, rootWindow, NET::WMAllProperties, NET::WM2AllProperties, NET::WindowManager)
0573     {
0574     }
0575 
0576 protected:
0577     void changeFullscreenMonitors(NETFullscreenMonitors topology) override
0578     {
0579         setFullscreenMonitors(topology);
0580     }
0581 };
0582 
0583 void NetWinInfoTestWM::testFullscreenMonitors()
0584 {
0585     // test case for BUG 391960
0586     QVERIFY(connection());
0587     const uint32_t maskValues[] = {
0588                      XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
0589                      XCB_EVENT_MASK_KEY_PRESS |
0590                      XCB_EVENT_MASK_PROPERTY_CHANGE |
0591                      XCB_EVENT_MASK_COLOR_MAP_CHANGE |
0592                      XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
0593                      XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
0594                      XCB_EVENT_MASK_FOCUS_CHANGE | // For NotifyDetailNone
0595                      XCB_EVENT_MASK_EXPOSURE
0596     };
0597     UniqueCPointer<xcb_generic_error_t> redirectCheck(xcb_request_check(connection(), xcb_change_window_attributes_checked(connection(),
0598                                                       m_rootWindow, XCB_CW_EVENT_MASK, maskValues)));
0599     QVERIFY(!redirectCheck);
0600 
0601     KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_WM_FULLSCREEN_MONITORS"));
0602 
0603     // create client connection
0604     auto clientConnection = xcb_connect(m_displayNumber.constData(), nullptr);
0605     QVERIFY(clientConnection);
0606     QVERIFY(!xcb_connection_has_error(clientConnection));
0607 
0608     NETWinInfo clientInfo(clientConnection, m_testWindow, m_rootWindow, NET::WMAllProperties, NET::WM2AllProperties);
0609     NETFullscreenMonitors topology;
0610     topology.top = 1;
0611     topology.bottom = 2;
0612     topology.left = 3;
0613     topology.right = 4;
0614     clientInfo.setFullscreenMonitors(topology);
0615     xcb_flush(clientConnection);
0616 
0617     MockWinInfo info(connection(), m_testWindow, m_rootWindow);
0618 
0619     while (true) {
0620         UniqueCPointer<xcb_generic_event_t> event(xcb_wait_for_event(connection()));
0621         if (!event) {
0622             break;
0623         }
0624         if ((event->response_type & ~0x80) != XCB_CLIENT_MESSAGE) {
0625             continue;
0626         }
0627 
0628         NET::Properties dirtyProtocols;
0629         NET::Properties2 dirtyProtocols2;
0630         QCOMPARE(info.fullscreenMonitors().isSet(), false);
0631         info.event(event.get(), &dirtyProtocols, &dirtyProtocols2);
0632         QCOMPARE(info.fullscreenMonitors().isSet(), true);
0633         break;
0634     }
0635     xcb_flush(connection());
0636     // now the property should be updated
0637     waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2FullscreenMonitors);
0638 
0639     QCOMPARE(info.fullscreenMonitors().top, 1);
0640     QCOMPARE(info.fullscreenMonitors().bottom, 2);
0641     QCOMPARE(info.fullscreenMonitors().left, 3);
0642     QCOMPARE(info.fullscreenMonitors().right, 4);
0643 
0644     xcb_disconnect(clientConnection);
0645 }
0646 
0647 QTEST_GUILESS_MAIN(NetWinInfoTestWM)
0648 
0649 #include "netwininfotestwm.moc"