/* KWin - the KDE window manager This file is part of the KDE project. SPDX-FileCopyrightText: 2015 Martin Gräßlin SPDX-License-Identifier: GPL-2.0-or-later */ #include "testutils.h" // KWin #include "utils/xcbutils.h" // Qt #include #include #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include #else #include #endif #include // xcb #include #include using namespace KWin; using namespace KWin::Xcb; class TestXcbSizeHints : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testSizeHints_data(); void testSizeHints(); void testSizeHintsEmpty(); void testSizeHintsNotSet(); void geometryHintsBeforeInit(); void geometryHintsBeforeRead(); private: Window m_testWindow; }; void TestXcbSizeHints::initTestCase() { qApp->setProperty("x11RootWindow", QVariant::fromValue(QX11Info::appRootWindow())); qApp->setProperty("x11Connection", QVariant::fromValue(QX11Info::connection())); } void TestXcbSizeHints::init() { const uint32_t values[] = {true}; m_testWindow.create(QRect(0, 0, 10, 10), XCB_WINDOW_CLASS_INPUT_ONLY, XCB_CW_OVERRIDE_REDIRECT, values); QVERIFY(m_testWindow.isValid()); } void TestXcbSizeHints::cleanup() { m_testWindow.reset(); } void TestXcbSizeHints::testSizeHints_data() { // set QTest::addColumn("userPos"); QTest::addColumn("userSize"); QTest::addColumn("minSize"); QTest::addColumn("maxSize"); QTest::addColumn("resizeInc"); QTest::addColumn("minAspect"); QTest::addColumn("maxAspect"); QTest::addColumn("baseSize"); QTest::addColumn("gravity"); // read for SizeHints QTest::addColumn("expectedFlags"); QTest::addColumn("expectedPad0"); QTest::addColumn("expectedPad1"); QTest::addColumn("expectedPad2"); QTest::addColumn("expectedPad3"); QTest::addColumn("expectedMinWidth"); QTest::addColumn("expectedMinHeight"); QTest::addColumn("expectedMaxWidth"); QTest::addColumn("expectedMaxHeight"); QTest::addColumn("expectedWidthInc"); QTest::addColumn("expectedHeightInc"); QTest::addColumn("expectedMinAspectNum"); QTest::addColumn("expectedMinAspectDen"); QTest::addColumn("expectedMaxAspectNum"); QTest::addColumn("expectedMaxAspectDen"); QTest::addColumn("expectedBaseWidth"); QTest::addColumn("expectedBaseHeight"); // read for GeometryHints QTest::addColumn("expectedMinSize"); QTest::addColumn("expectedMaxSize"); QTest::addColumn("expectedResizeIncrements"); QTest::addColumn("expectedMinAspect"); QTest::addColumn("expectedMaxAspect"); QTest::addColumn("expectedBaseSize"); QTest::addColumn("expectedGravity"); QTest::newRow("userPos") << QPoint(1, 2) << QSize() << QSize() << QSize() << QSize() << QSize() << QSize() << QSize() << 0 << 1 << 1 << 2 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QSize(0, 0) << QSize(INT_MAX, INT_MAX) << QSize(1, 1) << QSize(1, INT_MAX) << QSize(INT_MAX, 1) << QSize(0, 0) << qint32(XCB_GRAVITY_NORTH_WEST); QTest::newRow("userSize") << QPoint() << QSize(1, 2) << QSize() << QSize() << QSize() << QSize() << QSize() << QSize() << 0 << 2 << 0 << 0 << 1 << 2 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QSize(0, 0) << QSize(INT_MAX, INT_MAX) << QSize(1, 1) << QSize(1, INT_MAX) << QSize(INT_MAX, 1) << QSize(0, 0) << qint32(XCB_GRAVITY_NORTH_WEST); QTest::newRow("minSize") << QPoint() << QSize() << QSize(1, 2) << QSize() << QSize() << QSize() << QSize() << QSize() << 0 << 16 << 0 << 0 << 0 << 0 << 1 << 2 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QSize(1, 2) << QSize(INT_MAX, INT_MAX) << QSize(1, 1) << QSize(1, INT_MAX) << QSize(INT_MAX, 1) << QSize(0, 0) << qint32(XCB_GRAVITY_NORTH_WEST); QTest::newRow("maxSize") << QPoint() << QSize() << QSize() << QSize(1, 2) << QSize() << QSize() << QSize() << QSize() << 0 << 32 << 0 << 0 << 0 << 0 << 0 << 0 << 1 << 2 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QSize(0, 0) << QSize(1, 2) << QSize(1, 1) << QSize(1, INT_MAX) << QSize(INT_MAX, 1) << QSize(0, 0) << qint32(XCB_GRAVITY_NORTH_WEST); QTest::newRow("maxSize0") << QPoint() << QSize() << QSize() << QSize(0, 0) << QSize() << QSize() << QSize() << QSize() << 0 << 32 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QSize(0, 0) << QSize(1, 1) << QSize(1, 1) << QSize(1, INT_MAX) << QSize(INT_MAX, 1) << QSize(0, 0) << qint32(XCB_GRAVITY_NORTH_WEST); QTest::newRow("min/maxSize") << QPoint() << QSize() << QSize(1, 2) << QSize(3, 4) << QSize() << QSize() << QSize() << QSize() << 0 << 48 << 0 << 0 << 0 << 0 << 1 << 2 << 3 << 4 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QSize(1, 2) << QSize(3, 4) << QSize(1, 1) << QSize(1, INT_MAX) << QSize(INT_MAX, 1) << QSize(0, 0) << qint32(XCB_GRAVITY_NORTH_WEST); QTest::newRow("resizeInc") << QPoint() << QSize() << QSize() << QSize() << QSize(1, 2) << QSize() << QSize() << QSize() << 0 << 64 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 1 << 2 << 0 << 0 << 0 << 0 << 0 << 0 << QSize(0, 0) << QSize(INT_MAX, INT_MAX) << QSize(1, 2) << QSize(1, INT_MAX) << QSize(INT_MAX, 1) << QSize(0, 0) << qint32(XCB_GRAVITY_NORTH_WEST); QTest::newRow("resizeInc0") << QPoint() << QSize() << QSize() << QSize() << QSize(0, 0) << QSize() << QSize() << QSize() << 0 << 64 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QSize(0, 0) << QSize(INT_MAX, INT_MAX) << QSize(1, 1) << QSize(1, INT_MAX) << QSize(INT_MAX, 1) << QSize(0, 0) << qint32(XCB_GRAVITY_NORTH_WEST); QTest::newRow("aspect") << QPoint() << QSize() << QSize() << QSize() << QSize() << QSize(1, 2) << QSize(3, 4) << QSize() << 0 << 128 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 1 << 2 << 3 << 4 << 0 << 0 << QSize(0, 0) << QSize(INT_MAX, INT_MAX) << QSize(1, 1) << QSize(1, 2) << QSize(3, 4) << QSize(0, 0) << qint32(XCB_GRAVITY_NORTH_WEST); QTest::newRow("aspectDivision0") << QPoint() << QSize() << QSize() << QSize() << QSize() << QSize(1, 0) << QSize(3, 0) << QSize() << 0 << 128 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 1 << 0 << 3 << 0 << 0 << 0 << QSize(0, 0) << QSize(INT_MAX, INT_MAX) << QSize(1, 1) << QSize(1, 1) << QSize(3, 1) << QSize(0, 0) << qint32(XCB_GRAVITY_NORTH_WEST); QTest::newRow("baseSize") << QPoint() << QSize() << QSize() << QSize() << QSize() << QSize() << QSize() << QSize(1, 2) << 0 << 256 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 1 << 2 << QSize(1, 2) << QSize(INT_MAX, INT_MAX) << QSize(1, 1) << QSize(1, INT_MAX) << QSize(INT_MAX, 1) << QSize(1, 2) << qint32(XCB_GRAVITY_NORTH_WEST); QTest::newRow("gravity") << QPoint() << QSize() << QSize() << QSize() << QSize() << QSize() << QSize() << QSize() << qint32(XCB_GRAVITY_STATIC) << 512 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QSize(0, 0) << QSize(INT_MAX, INT_MAX) << QSize(1, 1) << QSize(1, INT_MAX) << QSize(INT_MAX, 1) << QSize(0, 0) << qint32(XCB_GRAVITY_STATIC); QTest::newRow("all") << QPoint(1, 2) << QSize(3, 4) << QSize(5, 6) << QSize(7, 8) << QSize(9, 10) << QSize(11, 12) << QSize(13, 14) << QSize(15, 16) << 1 << 1011 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12 << 13 << 14 << 15 << 16 << QSize(5, 6) << QSize(7, 8) << QSize(9, 10) << QSize(11, 12) << QSize(13, 14) << QSize(15, 16) << qint32(XCB_GRAVITY_NORTH_WEST); } void TestXcbSizeHints::testSizeHints() { xcb_size_hints_t hints; memset(&hints, 0, sizeof(hints)); QFETCH(QPoint, userPos); if (!userPos.isNull()) { xcb_icccm_size_hints_set_position(&hints, 1, userPos.x(), userPos.y()); } QFETCH(QSize, userSize); if (userSize.isValid()) { xcb_icccm_size_hints_set_size(&hints, 1, userSize.width(), userSize.height()); } QFETCH(QSize, minSize); if (minSize.isValid()) { xcb_icccm_size_hints_set_min_size(&hints, minSize.width(), minSize.height()); } QFETCH(QSize, maxSize); if (maxSize.isValid()) { xcb_icccm_size_hints_set_max_size(&hints, maxSize.width(), maxSize.height()); } QFETCH(QSize, resizeInc); if (resizeInc.isValid()) { xcb_icccm_size_hints_set_resize_inc(&hints, resizeInc.width(), resizeInc.height()); } QFETCH(QSize, minAspect); QFETCH(QSize, maxAspect); if (minAspect.isValid() && maxAspect.isValid()) { xcb_icccm_size_hints_set_aspect(&hints, minAspect.width(), minAspect.height(), maxAspect.width(), maxAspect.height()); } QFETCH(QSize, baseSize); if (baseSize.isValid()) { xcb_icccm_size_hints_set_base_size(&hints, baseSize.width(), baseSize.height()); } QFETCH(qint32, gravity); if (gravity != 0) { xcb_icccm_size_hints_set_win_gravity(&hints, (xcb_gravity_t)gravity); } xcb_icccm_set_wm_normal_hints(QX11Info::connection(), m_testWindow, &hints); xcb_flush(QX11Info::connection()); GeometryHints geoHints; geoHints.init(m_testWindow); geoHints.read(); QCOMPARE(geoHints.hasAspect(), minAspect.isValid() && maxAspect.isValid()); QCOMPARE(geoHints.hasBaseSize(), baseSize.isValid()); QCOMPARE(geoHints.hasMaxSize(), maxSize.isValid()); QCOMPARE(geoHints.hasMinSize(), minSize.isValid()); QCOMPARE(geoHints.hasPosition(), !userPos.isNull()); QCOMPARE(geoHints.hasResizeIncrements(), resizeInc.isValid()); QCOMPARE(geoHints.hasSize(), userSize.isValid()); QCOMPARE(geoHints.hasWindowGravity(), gravity != 0); QTEST(geoHints.baseSize(), "expectedBaseSize"); QTEST(geoHints.maxAspect(), "expectedMaxAspect"); QTEST(geoHints.maxSize(), "expectedMaxSize"); QTEST(geoHints.minAspect(), "expectedMinAspect"); QTEST(geoHints.minSize(), "expectedMinSize"); QTEST(geoHints.resizeIncrements(), "expectedResizeIncrements"); QTEST(qint32(geoHints.windowGravity()), "expectedGravity"); auto sizeHints = geoHints.m_sizeHints; QVERIFY(sizeHints); QTEST(sizeHints->flags, "expectedFlags"); QTEST(sizeHints->pad[0], "expectedPad0"); QTEST(sizeHints->pad[1], "expectedPad1"); QTEST(sizeHints->pad[2], "expectedPad2"); QTEST(sizeHints->pad[3], "expectedPad3"); QTEST(sizeHints->minWidth, "expectedMinWidth"); QTEST(sizeHints->minHeight, "expectedMinHeight"); QTEST(sizeHints->maxWidth, "expectedMaxWidth"); QTEST(sizeHints->maxHeight, "expectedMaxHeight"); QTEST(sizeHints->widthInc, "expectedWidthInc"); QTEST(sizeHints->heightInc, "expectedHeightInc"); QTEST(sizeHints->minAspect[0], "expectedMinAspectNum"); QTEST(sizeHints->minAspect[1], "expectedMinAspectDen"); QTEST(sizeHints->maxAspect[0], "expectedMaxAspectNum"); QTEST(sizeHints->maxAspect[1], "expectedMaxAspectDen"); QTEST(sizeHints->baseWidth, "expectedBaseWidth"); QTEST(sizeHints->baseHeight, "expectedBaseHeight"); QCOMPARE(sizeHints->winGravity, gravity); // copy GeometryHints::NormalHints::SizeHints sizeHints2 = *sizeHints; QTEST(sizeHints2.flags, "expectedFlags"); QTEST(sizeHints2.pad[0], "expectedPad0"); QTEST(sizeHints2.pad[1], "expectedPad1"); QTEST(sizeHints2.pad[2], "expectedPad2"); QTEST(sizeHints2.pad[3], "expectedPad3"); QTEST(sizeHints2.minWidth, "expectedMinWidth"); QTEST(sizeHints2.minHeight, "expectedMinHeight"); QTEST(sizeHints2.maxWidth, "expectedMaxWidth"); QTEST(sizeHints2.maxHeight, "expectedMaxHeight"); QTEST(sizeHints2.widthInc, "expectedWidthInc"); QTEST(sizeHints2.heightInc, "expectedHeightInc"); QTEST(sizeHints2.minAspect[0], "expectedMinAspectNum"); QTEST(sizeHints2.minAspect[1], "expectedMinAspectDen"); QTEST(sizeHints2.maxAspect[0], "expectedMaxAspectNum"); QTEST(sizeHints2.maxAspect[1], "expectedMaxAspectDen"); QTEST(sizeHints2.baseWidth, "expectedBaseWidth"); QTEST(sizeHints2.baseHeight, "expectedBaseHeight"); QCOMPARE(sizeHints2.winGravity, gravity); } void TestXcbSizeHints::testSizeHintsEmpty() { xcb_size_hints_t xcbHints; memset(&xcbHints, 0, sizeof(xcbHints)); xcb_icccm_set_wm_normal_hints(QX11Info::connection(), m_testWindow, &xcbHints); xcb_flush(QX11Info::connection()); GeometryHints hints; hints.init(m_testWindow); hints.read(); QVERIFY(!hints.hasAspect()); QVERIFY(!hints.hasBaseSize()); QVERIFY(!hints.hasMaxSize()); QVERIFY(!hints.hasMinSize()); QVERIFY(!hints.hasPosition()); QVERIFY(!hints.hasResizeIncrements()); QVERIFY(!hints.hasSize()); QVERIFY(!hints.hasWindowGravity()); QCOMPARE(hints.baseSize(), QSize(0, 0)); QCOMPARE(hints.maxAspect(), QSize(INT_MAX, 1)); QCOMPARE(hints.maxSize(), QSize(INT_MAX, INT_MAX)); QCOMPARE(hints.minAspect(), QSize(1, INT_MAX)); QCOMPARE(hints.minSize(), QSize(0, 0)); QCOMPARE(hints.resizeIncrements(), QSize(1, 1)); QCOMPARE(hints.windowGravity(), XCB_GRAVITY_NORTH_WEST); auto sizeHints = hints.m_sizeHints; QVERIFY(sizeHints); QCOMPARE(sizeHints->flags, 0); QCOMPARE(sizeHints->pad[0], 0); QCOMPARE(sizeHints->pad[1], 0); QCOMPARE(sizeHints->pad[2], 0); QCOMPARE(sizeHints->pad[3], 0); QCOMPARE(sizeHints->minWidth, 0); QCOMPARE(sizeHints->minHeight, 0); QCOMPARE(sizeHints->maxWidth, 0); QCOMPARE(sizeHints->maxHeight, 0); QCOMPARE(sizeHints->widthInc, 0); QCOMPARE(sizeHints->heightInc, 0); QCOMPARE(sizeHints->minAspect[0], 0); QCOMPARE(sizeHints->minAspect[1], 0); QCOMPARE(sizeHints->maxAspect[0], 0); QCOMPARE(sizeHints->maxAspect[1], 0); QCOMPARE(sizeHints->baseWidth, 0); QCOMPARE(sizeHints->baseHeight, 0); QCOMPARE(sizeHints->winGravity, 0); } void TestXcbSizeHints::testSizeHintsNotSet() { GeometryHints hints; hints.init(m_testWindow); hints.read(); QVERIFY(!hints.m_sizeHints); QVERIFY(!hints.hasAspect()); QVERIFY(!hints.hasBaseSize()); QVERIFY(!hints.hasMaxSize()); QVERIFY(!hints.hasMinSize()); QVERIFY(!hints.hasPosition()); QVERIFY(!hints.hasResizeIncrements()); QVERIFY(!hints.hasSize()); QVERIFY(!hints.hasWindowGravity()); QCOMPARE(hints.baseSize(), QSize(0, 0)); QCOMPARE(hints.maxAspect(), QSize(INT_MAX, 1)); QCOMPARE(hints.maxSize(), QSize(INT_MAX, INT_MAX)); QCOMPARE(hints.minAspect(), QSize(1, INT_MAX)); QCOMPARE(hints.minSize(), QSize(0, 0)); QCOMPARE(hints.resizeIncrements(), QSize(1, 1)); QCOMPARE(hints.windowGravity(), XCB_GRAVITY_NORTH_WEST); } void TestXcbSizeHints::geometryHintsBeforeInit() { GeometryHints hints; QVERIFY(!hints.hasAspect()); QVERIFY(!hints.hasBaseSize()); QVERIFY(!hints.hasMaxSize()); QVERIFY(!hints.hasMinSize()); QVERIFY(!hints.hasPosition()); QVERIFY(!hints.hasResizeIncrements()); QVERIFY(!hints.hasSize()); QVERIFY(!hints.hasWindowGravity()); QCOMPARE(hints.baseSize(), QSize(0, 0)); QCOMPARE(hints.maxAspect(), QSize(INT_MAX, 1)); QCOMPARE(hints.maxSize(), QSize(INT_MAX, INT_MAX)); QCOMPARE(hints.minAspect(), QSize(1, INT_MAX)); QCOMPARE(hints.minSize(), QSize(0, 0)); QCOMPARE(hints.resizeIncrements(), QSize(1, 1)); QCOMPARE(hints.windowGravity(), XCB_GRAVITY_NORTH_WEST); } void TestXcbSizeHints::geometryHintsBeforeRead() { xcb_size_hints_t xcbHints; memset(&xcbHints, 0, sizeof(xcbHints)); xcb_icccm_size_hints_set_position(&xcbHints, 1, 1, 2); xcb_icccm_set_wm_normal_hints(QX11Info::connection(), m_testWindow, &xcbHints); xcb_flush(QX11Info::connection()); GeometryHints hints; hints.init(m_testWindow); QVERIFY(!hints.hasAspect()); QVERIFY(!hints.hasBaseSize()); QVERIFY(!hints.hasMaxSize()); QVERIFY(!hints.hasMinSize()); QVERIFY(!hints.hasPosition()); QVERIFY(!hints.hasResizeIncrements()); QVERIFY(!hints.hasSize()); QVERIFY(!hints.hasWindowGravity()); QCOMPARE(hints.baseSize(), QSize(0, 0)); QCOMPARE(hints.maxAspect(), QSize(INT_MAX, 1)); QCOMPARE(hints.maxSize(), QSize(INT_MAX, INT_MAX)); QCOMPARE(hints.minAspect(), QSize(1, INT_MAX)); QCOMPARE(hints.minSize(), QSize(0, 0)); QCOMPARE(hints.resizeIncrements(), QSize(1, 1)); QCOMPARE(hints.windowGravity(), XCB_GRAVITY_NORTH_WEST); } Q_CONSTRUCTOR_FUNCTION(forceXcb) QTEST_MAIN(TestXcbSizeHints) #include "test_xcb_size_hints.moc"