File indexing completed on 2024-05-05 17:36:00

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "kwin_wayland_test.h"
0010 
0011 #include "core/output.h"
0012 #include "core/outputbackend.h"
0013 #include "cursor.h"
0014 #include "deleted.h"
0015 #include "screenedge.h"
0016 #include "virtualdesktops.h"
0017 #include "wayland_server.h"
0018 #include "workspace.h"
0019 #include "x11window.h"
0020 #include <kwineffects.h>
0021 
0022 #include <KWayland/Client/compositor.h>
0023 #include <KWayland/Client/plasmashell.h>
0024 #include <KWayland/Client/surface.h>
0025 
0026 #include <KDecoration2/Decoration>
0027 
0028 #include <netwm.h>
0029 #include <xcb/xcb_icccm.h>
0030 
0031 Q_DECLARE_METATYPE(KWin::StrutRects)
0032 
0033 namespace KWin
0034 {
0035 
0036 static const QString s_socketName = QStringLiteral("wayland_test_kwin_struts-0");
0037 
0038 class StrutsTest : public QObject
0039 {
0040     Q_OBJECT
0041 private Q_SLOTS:
0042     void initTestCase();
0043     void init();
0044     void cleanup();
0045     void testWaylandStruts_data();
0046     void testWaylandStruts();
0047     void testMoveWaylandPanel();
0048     void testWaylandMobilePanel();
0049     void testX11Struts_data();
0050     void testX11Struts();
0051     void test363804();
0052     void testLeftScreenSmallerBottomAligned();
0053     void testWindowMoveWithPanelBetweenScreens();
0054 
0055 private:
0056     KWayland::Client::Compositor *m_compositor = nullptr;
0057     KWayland::Client::PlasmaShell *m_plasmaShell = nullptr;
0058 };
0059 
0060 void StrutsTest::initTestCase()
0061 {
0062     qRegisterMetaType<KWin::Window *>();
0063     qRegisterMetaType<KWin::Deleted *>();
0064     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0065     QVERIFY(waylandServer()->init(s_socketName));
0066     QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024)));
0067 
0068     // set custom config which disables the Outline
0069     KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
0070     KConfigGroup group = config->group("Outline");
0071     group.writeEntry(QStringLiteral("QmlPath"), QString("/does/not/exist.qml"));
0072     group.sync();
0073 
0074     kwinApp()->setConfig(config);
0075 
0076     kwinApp()->start();
0077     QVERIFY(applicationStartedSpy.wait());
0078     const auto outputs = workspace()->outputs();
0079     QCOMPARE(outputs.count(), 2);
0080     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0081     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0082     setenv("QT_QPA_PLATFORM", "wayland", true);
0083 }
0084 
0085 void StrutsTest::init()
0086 {
0087     QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::PlasmaShell));
0088     m_compositor = Test::waylandCompositor();
0089     m_plasmaShell = Test::waylandPlasmaShell();
0090 
0091     workspace()->setActiveOutput(QPoint(640, 512));
0092     Cursors::self()->mouse()->setPos(QPoint(640, 512));
0093     QVERIFY(waylandServer()->windows().isEmpty());
0094 }
0095 
0096 void StrutsTest::cleanup()
0097 {
0098     Test::destroyWaylandConnection();
0099 }
0100 
0101 void StrutsTest::testWaylandStruts_data()
0102 {
0103     QTest::addColumn<QVector<QRect>>("windowGeometries");
0104     QTest::addColumn<QRectF>("screen0Maximized");
0105     QTest::addColumn<QRectF>("screen1Maximized");
0106     QTest::addColumn<QRectF>("workArea");
0107     QTest::addColumn<StrutRects>("restrictedMoveArea");
0108 
0109     QTest::newRow("bottom/0") << QVector<QRect>{QRect(0, 992, 1280, 32)} << QRectF(0, 0, 1280, 992) << QRectF(1280, 0, 1280, 1024) << QRectF(0, 0, 2560, 992) << StrutRects{StrutRect(0, 992, 1280, 32)};
0110     QTest::newRow("bottom/1") << QVector<QRect>{QRect(1280, 992, 1280, 32)} << QRectF(0, 0, 1280, 1024) << QRectF(1280, 0, 1280, 992) << QRectF(0, 0, 2560, 992) << StrutRects{StrutRect(1280, 992, 1280, 32)};
0111     QTest::newRow("top/0") << QVector<QRect>{QRect(0, 0, 1280, 32)} << QRectF(0, 32, 1280, 992) << QRectF(1280, 0, 1280, 1024) << QRectF(0, 32, 2560, 992) << StrutRects{StrutRect(0, 0, 1280, 32)};
0112     QTest::newRow("top/1") << QVector<QRect>{QRect(1280, 0, 1280, 32)} << QRectF(0, 0, 1280, 1024) << QRectF(1280, 32, 1280, 992) << QRectF(0, 32, 2560, 992) << StrutRects{StrutRect(1280, 0, 1280, 32)};
0113     QTest::newRow("left/0") << QVector<QRect>{QRect(0, 0, 32, 1024)} << QRectF(32, 0, 1248, 1024) << QRectF(1280, 0, 1280, 1024) << QRectF(32, 0, 2528, 1024) << StrutRects{StrutRect(0, 0, 32, 1024)};
0114     QTest::newRow("left/1") << QVector<QRect>{QRect(1280, 0, 32, 1024)} << QRectF(0, 0, 1280, 1024) << QRectF(1312, 0, 1248, 1024) << QRectF(0, 0, 2560, 1024) << StrutRects{StrutRect(1280, 0, 32, 1024)};
0115     QTest::newRow("right/0") << QVector<QRect>{QRect(1248, 0, 32, 1024)} << QRectF(0, 0, 1248, 1024) << QRectF(1280, 0, 1280, 1024) << QRectF(0, 0, 2560, 1024) << StrutRects{StrutRect(1248, 0, 32, 1024)};
0116     QTest::newRow("right/1") << QVector<QRect>{QRect(2528, 0, 32, 1024)} << QRectF(0, 0, 1280, 1024) << QRectF(1280, 0, 1248, 1024) << QRectF(0, 0, 2528, 1024) << StrutRects{StrutRect(2528, 0, 32, 1024)};
0117 
0118     // same with partial panels not covering the whole area
0119     QTest::newRow("part bottom/0") << QVector<QRect>{QRect(100, 992, 1080, 32)} << QRectF(0, 0, 1280, 992) << QRectF(1280, 0, 1280, 1024) << QRectF(0, 0, 2560, 992) << StrutRects{StrutRect(100, 992, 1080, 32)};
0120     QTest::newRow("part bottom/1") << QVector<QRect>{QRect(1380, 992, 1080, 32)} << QRectF(0, 0, 1280, 1024) << QRectF(1280, 0, 1280, 992) << QRectF(0, 0, 2560, 992) << StrutRects{StrutRect(1380, 992, 1080, 32)};
0121     QTest::newRow("part top/0") << QVector<QRect>{QRect(100, 0, 1080, 32)} << QRectF(0, 32, 1280, 992) << QRectF(1280, 0, 1280, 1024) << QRectF(0, 32, 2560, 992) << StrutRects{StrutRect(100, 0, 1080, 32)};
0122     QTest::newRow("part top/1") << QVector<QRect>{QRect(1380, 0, 1080, 32)} << QRectF(0, 0, 1280, 1024) << QRectF(1280, 32, 1280, 992) << QRectF(0, 32, 2560, 992) << StrutRects{StrutRect(1380, 0, 1080, 32)};
0123     QTest::newRow("part left/0") << QVector<QRect>{QRect(0, 100, 32, 824)} << QRectF(32, 0, 1248, 1024) << QRectF(1280, 0, 1280, 1024) << QRectF(32, 0, 2528, 1024) << StrutRects{StrutRect(0, 100, 32, 824)};
0124     QTest::newRow("part left/1") << QVector<QRect>{QRect(1280, 100, 32, 824)} << QRectF(0, 0, 1280, 1024) << QRectF(1312, 0, 1248, 1024) << QRectF(0, 0, 2560, 1024) << StrutRects{StrutRect(1280, 100, 32, 824)};
0125     QTest::newRow("part right/0") << QVector<QRect>{QRect(1248, 100, 32, 824)} << QRectF(0, 0, 1248, 1024) << QRectF(1280, 0, 1280, 1024) << QRectF(0, 0, 2560, 1024) << StrutRects{StrutRect(1248, 100, 32, 824)};
0126     QTest::newRow("part right/1") << QVector<QRect>{QRect(2528, 100, 32, 824)} << QRectF(0, 0, 1280, 1024) << QRectF(1280, 0, 1248, 1024) << QRectF(0, 0, 2528, 1024) << StrutRects{StrutRect(2528, 100, 32, 824)};
0127 
0128     // multiple panels
0129     QTest::newRow("two bottom panels") << QVector<QRect>{QRect(100, 992, 1080, 32), QRect(1380, 984, 1080, 40)} << QRectF(0, 0, 1280, 992) << QRectF(1280, 0, 1280, 984) << QRectF(0, 0, 2560, 984) << StrutRects{StrutRect(100, 992, 1080, 32), StrutRect(1380, 984, 1080, 40)};
0130     QTest::newRow("two left panels") << QVector<QRect>{QRect(0, 10, 32, 390), QRect(0, 450, 40, 100)} << QRectF(40, 0, 1240, 1024) << QRectF(1280, 0, 1280, 1024) << QRectF(40, 0, 2520, 1024) << StrutRects{StrutRect(0, 10, 32, 390), StrutRect(0, 450, 40, 100)};
0131 }
0132 
0133 void StrutsTest::testWaylandStruts()
0134 {
0135     // this test verifies that struts on Wayland panels are handled correctly
0136 
0137     VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop();
0138     const QList<Output *> outputs = workspace()->outputs();
0139 
0140     // no, struts yet
0141     QVERIFY(waylandServer()->windows().isEmpty());
0142     // first screen
0143     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0144     QCOMPARE(workspace()->clientArea(MovementArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0145     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0146     QCOMPARE(workspace()->clientArea(MaximizeFullArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0147     QCOMPARE(workspace()->clientArea(FullScreenArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0148     QCOMPARE(workspace()->clientArea(ScreenArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0149     // second screen
0150     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0151     QCOMPARE(workspace()->clientArea(MovementArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0152     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0153     QCOMPARE(workspace()->clientArea(MaximizeFullArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0154     QCOMPARE(workspace()->clientArea(FullScreenArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0155     QCOMPARE(workspace()->clientArea(ScreenArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0156     // combined
0157     QCOMPARE(workspace()->clientArea(WorkArea, outputs[0], desktop), QRect(0, 0, 2560, 1024));
0158     QCOMPARE(workspace()->clientArea(FullArea, outputs[0], desktop), QRect(0, 0, 2560, 1024));
0159     QCOMPARE(workspace()->restrictedMoveArea(desktop), StrutRects());
0160 
0161     QFETCH(QVector<QRect>, windowGeometries);
0162     // create the panels
0163     std::map<Window *, std::unique_ptr<KWayland::Client::Surface>> windows;
0164     for (auto it = windowGeometries.constBegin(), end = windowGeometries.constEnd(); it != end; it++) {
0165         const QRect windowGeometry = *it;
0166         std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
0167         Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly, surface.get());
0168         KWayland::Client::PlasmaShellSurface *plasmaSurface = m_plasmaShell->createSurface(surface.get(), surface.get());
0169         plasmaSurface->setPosition(windowGeometry.topLeft());
0170         plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::Panel);
0171 
0172         QSignalSpy configureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0173         surface->commit(KWayland::Client::Surface::CommitFlag::None);
0174         QVERIFY(configureRequestedSpy.wait());
0175 
0176         // map the window
0177         shellSurface->xdgSurface()->ack_configure(configureRequestedSpy.last().first().toUInt());
0178         auto window = Test::renderAndWaitForShown(surface.get(), windowGeometry.size(), Qt::red, QImage::Format_RGB32);
0179 
0180         QVERIFY(window);
0181         QVERIFY(!window->isActive());
0182         QCOMPARE(window->frameGeometry(), windowGeometry);
0183         QVERIFY(window->isDock());
0184         QVERIFY(window->hasStrut());
0185         windows[window] = std::move(surface);
0186     }
0187 
0188     // some props are independent of struts - those first
0189     // screen 0
0190     QCOMPARE(workspace()->clientArea(MovementArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0191     QCOMPARE(workspace()->clientArea(MaximizeFullArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0192     QCOMPARE(workspace()->clientArea(FullScreenArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0193     QCOMPARE(workspace()->clientArea(ScreenArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0194     // screen 1
0195     QCOMPARE(workspace()->clientArea(MovementArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0196     QCOMPARE(workspace()->clientArea(MaximizeFullArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0197     QCOMPARE(workspace()->clientArea(FullScreenArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0198     QCOMPARE(workspace()->clientArea(ScreenArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0199     // combined
0200     QCOMPARE(workspace()->clientArea(FullArea, outputs[0], desktop), QRect(0, 0, 2560, 1024));
0201 
0202     // now verify the actual updated client areas
0203     QTEST(workspace()->clientArea(PlacementArea, outputs[0], desktop), "screen0Maximized");
0204     QTEST(workspace()->clientArea(MaximizeArea, outputs[0], desktop), "screen0Maximized");
0205     QTEST(workspace()->clientArea(PlacementArea, outputs[1], desktop), "screen1Maximized");
0206     QTEST(workspace()->clientArea(MaximizeArea, outputs[1], desktop), "screen1Maximized");
0207     QTEST(workspace()->clientArea(WorkArea, outputs[0], desktop), "workArea");
0208     QTEST(workspace()->restrictedMoveArea(desktop), "restrictedMoveArea");
0209 
0210     // delete all surfaces
0211     for (auto it = windows.begin(); it != windows.end();) {
0212         auto &[window, surface] = *it;
0213         QSignalSpy destroyedSpy(window, &QObject::destroyed);
0214         it = windows.erase(it);
0215         QVERIFY(destroyedSpy.wait());
0216     }
0217     QCOMPARE(workspace()->restrictedMoveArea(desktop), StrutRects());
0218 }
0219 
0220 void StrutsTest::testMoveWaylandPanel()
0221 {
0222     VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop();
0223     const QList<Output *> outputs = workspace()->outputs();
0224 
0225     // this test verifies that repositioning a Wayland panel updates the client area
0226     const QRect windowGeometry(0, 1000, 1280, 24);
0227     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0228     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
0229     std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get()));
0230     plasmaSurface->setPosition(windowGeometry.topLeft());
0231     plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::Panel);
0232 
0233     QSignalSpy configureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0234     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0235     QVERIFY(configureRequestedSpy.wait());
0236 
0237     // map the window
0238     shellSurface->xdgSurface()->ack_configure(configureRequestedSpy.last().first().toUInt());
0239     auto window = Test::renderAndWaitForShown(surface.get(), windowGeometry.size(), Qt::red, QImage::Format_RGB32);
0240     QVERIFY(window);
0241     QVERIFY(!window->isActive());
0242     QCOMPARE(window->frameGeometry(), windowGeometry);
0243     QVERIFY(window->isDock());
0244     QVERIFY(window->hasStrut());
0245     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[0], desktop), QRect(0, 0, 1280, 1000));
0246     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[0], desktop), QRect(0, 0, 1280, 1000));
0247     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0248     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0249     QCOMPARE(workspace()->clientArea(WorkArea, outputs[0], desktop), QRect(0, 0, 2560, 1000));
0250 
0251     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0252     plasmaSurface->setPosition(QPoint(1280, 1000));
0253     QVERIFY(frameGeometryChangedSpy.wait());
0254     QCOMPARE(window->frameGeometry(), QRect(1280, 1000, 1280, 24));
0255     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0256     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0257     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[1], desktop), QRect(1280, 0, 1280, 1000));
0258     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[1], desktop), QRect(1280, 0, 1280, 1000));
0259     QCOMPARE(workspace()->clientArea(WorkArea, outputs[0], desktop), QRect(0, 0, 2560, 1000));
0260 }
0261 
0262 void StrutsTest::testWaylandMobilePanel()
0263 {
0264     VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop();
0265     const QList<Output *> outputs = workspace()->outputs();
0266 
0267     // First enable maxmizing policy
0268     KConfigGroup group = kwinApp()->config()->group("Windows");
0269     group.writeEntry("Placement", "Maximizing");
0270     group.sync();
0271     workspace()->slotReconfigure();
0272 
0273     // create first top panel
0274     const QRect windowGeometry(0, 0, 1280, 60);
0275     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0276     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
0277     std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get()));
0278     plasmaSurface->setPosition(windowGeometry.topLeft());
0279     plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::Panel);
0280 
0281     QSignalSpy configureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0282     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0283     QVERIFY(configureRequestedSpy.wait());
0284 
0285     // map the window
0286     shellSurface->xdgSurface()->ack_configure(configureRequestedSpy.last().first().toUInt());
0287     auto window = Test::renderAndWaitForShown(surface.get(), windowGeometry.size(), Qt::red, QImage::Format_RGB32);
0288     QVERIFY(window);
0289     QVERIFY(!window->isActive());
0290     QCOMPARE(window->frameGeometry(), windowGeometry);
0291     QVERIFY(window->isDock());
0292     QVERIFY(window->hasStrut());
0293 
0294     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[0], desktop), QRect(0, 60, 1280, 964));
0295     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[0], desktop), QRect(0, 60, 1280, 964));
0296     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0297     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0298     QCOMPARE(workspace()->clientArea(WorkArea, outputs[0], desktop), QRect(0, 60, 2560, 964));
0299 
0300     // create another bottom panel
0301     const QRect windowGeometry2(0, 874, 1280, 150);
0302     std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
0303     std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get(), Test::CreationSetup::CreateOnly));
0304     std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface2(m_plasmaShell->createSurface(surface2.get()));
0305     plasmaSurface2->setPosition(windowGeometry2.topLeft());
0306     plasmaSurface2->setRole(KWayland::Client::PlasmaShellSurface::Role::Panel);
0307 
0308     QSignalSpy configureRequestedSpy2(shellSurface2->xdgSurface(), &Test::XdgSurface::configureRequested);
0309     surface2->commit(KWayland::Client::Surface::CommitFlag::None);
0310     QVERIFY(configureRequestedSpy2.wait());
0311 
0312     // map the window
0313     shellSurface2->xdgSurface()->ack_configure(configureRequestedSpy2.last().first().toUInt());
0314     auto c1 = Test::renderAndWaitForShown(surface2.get(), windowGeometry2.size(), Qt::blue, QImage::Format_RGB32);
0315 
0316     QVERIFY(c1);
0317     QVERIFY(!c1->isActive());
0318     QCOMPARE(c1->frameGeometry(), windowGeometry2);
0319     QVERIFY(c1->isDock());
0320     QVERIFY(c1->hasStrut());
0321 
0322     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[0], desktop), QRect(0, 60, 1280, 814));
0323     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[0], desktop), QRect(0, 60, 1280, 814));
0324     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0325     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0326     QCOMPARE(workspace()->clientArea(WorkArea, outputs[0], desktop), QRect(0, 60, 2560, 814));
0327 
0328     // Destroy test windows.
0329     shellSurface.reset();
0330     QVERIFY(Test::waitForWindowDestroyed(window));
0331     shellSurface2.reset();
0332     QVERIFY(Test::waitForWindowDestroyed(c1));
0333 }
0334 
0335 void StrutsTest::testX11Struts_data()
0336 {
0337     QTest::addColumn<QRect>("windowGeometry");
0338     QTest::addColumn<int>("leftStrut");
0339     QTest::addColumn<int>("rightStrut");
0340     QTest::addColumn<int>("topStrut");
0341     QTest::addColumn<int>("bottomStrut");
0342     QTest::addColumn<int>("leftStrutStart");
0343     QTest::addColumn<int>("leftStrutEnd");
0344     QTest::addColumn<int>("rightStrutStart");
0345     QTest::addColumn<int>("rightStrutEnd");
0346     QTest::addColumn<int>("topStrutStart");
0347     QTest::addColumn<int>("topStrutEnd");
0348     QTest::addColumn<int>("bottomStrutStart");
0349     QTest::addColumn<int>("bottomStrutEnd");
0350     QTest::addColumn<QRectF>("screen0Maximized");
0351     QTest::addColumn<QRectF>("screen1Maximized");
0352     QTest::addColumn<QRectF>("workArea");
0353     QTest::addColumn<StrutRects>("restrictedMoveArea");
0354 
0355     QTest::newRow("bottom panel/no strut") << QRect(0, 980, 1280, 44)
0356                                            << 0 << 0 << 0 << 0
0357                                            << 0 << 0
0358                                            << 0 << 0
0359                                            << 0 << 0
0360                                            << 0 << 0
0361                                            << QRectF(0, 0, 1280, 1024)
0362                                            << QRectF(1280, 0, 1280, 1024)
0363                                            << QRectF(0, 0, 2560, 1024)
0364                                            << StrutRects();
0365     QTest::newRow("bottom panel/strut") << QRect(0, 980, 1280, 44)
0366                                         << 0 << 0 << 0 << 44
0367                                         << 0 << 0
0368                                         << 0 << 0
0369                                         << 0 << 0
0370                                         << 0 << 1279
0371                                         << QRectF(0, 0, 1280, 980)
0372                                         << QRectF(1280, 0, 1280, 1024)
0373                                         << QRectF(0, 0, 2560, 980)
0374                                         << StrutRects{StrutRect(0, 980, 1279, 44)};
0375     QTest::newRow("top panel/no strut") << QRect(0, 0, 1280, 44)
0376                                         << 0 << 0 << 0 << 0
0377                                         << 0 << 0
0378                                         << 0 << 0
0379                                         << 0 << 0
0380                                         << 0 << 0
0381                                         << QRectF(0, 0, 1280, 1024)
0382                                         << QRectF(1280, 0, 1280, 1024)
0383                                         << QRectF(0, 0, 2560, 1024)
0384                                         << StrutRects();
0385     QTest::newRow("top panel/strut") << QRect(0, 0, 1280, 44)
0386                                      << 0 << 0 << 44 << 0
0387                                      << 0 << 0
0388                                      << 0 << 0
0389                                      << 0 << 1279
0390                                      << 0 << 0
0391                                      << QRectF(0, 44, 1280, 980)
0392                                      << QRectF(1280, 0, 1280, 1024)
0393                                      << QRectF(0, 44, 2560, 980)
0394                                      << StrutRects{StrutRect(0, 0, 1279, 44)};
0395     QTest::newRow("left panel/no strut") << QRect(0, 0, 60, 1024)
0396                                          << 0 << 0 << 0 << 0
0397                                          << 0 << 0
0398                                          << 0 << 0
0399                                          << 0 << 0
0400                                          << 0 << 0
0401                                          << QRectF(0, 0, 1280, 1024)
0402                                          << QRectF(1280, 0, 1280, 1024)
0403                                          << QRectF(0, 0, 2560, 1024)
0404                                          << StrutRects();
0405     QTest::newRow("left panel/strut") << QRect(0, 0, 60, 1024)
0406                                       << 60 << 0 << 0 << 0
0407                                       << 0 << 1023
0408                                       << 0 << 0
0409                                       << 0 << 0
0410                                       << 0 << 0
0411                                       << QRectF(60, 0, 1220, 1024)
0412                                       << QRectF(1280, 0, 1280, 1024)
0413                                       << QRectF(60, 0, 2500, 1024)
0414                                       << StrutRects{StrutRect(0, 0, 60, 1023)};
0415     QTest::newRow("right panel/no strut") << QRect(1220, 0, 60, 1024)
0416                                           << 0 << 0 << 0 << 0
0417                                           << 0 << 0
0418                                           << 0 << 0
0419                                           << 0 << 0
0420                                           << 0 << 0
0421                                           << QRectF(0, 0, 1280, 1024)
0422                                           << QRectF(1280, 0, 1280, 1024)
0423                                           << QRectF(0, 0, 2560, 1024)
0424                                           << StrutRects();
0425     QTest::newRow("right panel/strut") << QRect(1220, 0, 60, 1024)
0426                                        << 0 << 1340 << 0 << 0
0427                                        << 0 << 0
0428                                        << 0 << 1023
0429                                        << 0 << 0
0430                                        << 0 << 0
0431                                        << QRectF(0, 0, 1220, 1024)
0432                                        << QRectF(1280, 0, 1280, 1024)
0433                                        << QRectF(0, 0, 2560, 1024)
0434                                        << StrutRects{StrutRect(1220, 0, 60, 1023)};
0435     // second screen
0436     QTest::newRow("bottom panel 1/no strut") << QRect(1280, 980, 1280, 44)
0437                                              << 0 << 0 << 0 << 0
0438                                              << 0 << 0
0439                                              << 0 << 0
0440                                              << 0 << 0
0441                                              << 0 << 0
0442                                              << QRectF(0, 0, 1280, 1024)
0443                                              << QRectF(1280, 0, 1280, 1024)
0444                                              << QRectF(0, 0, 2560, 1024)
0445                                              << StrutRects();
0446     QTest::newRow("bottom panel 1/strut") << QRect(1280, 980, 1280, 44)
0447                                           << 0 << 0 << 0 << 44
0448                                           << 0 << 0
0449                                           << 0 << 0
0450                                           << 0 << 0
0451                                           << 1280 << 2559
0452                                           << QRectF(0, 0, 1280, 1024)
0453                                           << QRectF(1280, 0, 1280, 980)
0454                                           << QRectF(0, 0, 2560, 980)
0455                                           << StrutRects{StrutRect(1280, 980, 1279, 44)};
0456     QTest::newRow("top panel 1/no strut") << QRect(1280, 0, 1280, 44)
0457                                           << 0 << 0 << 0 << 0
0458                                           << 0 << 0
0459                                           << 0 << 0
0460                                           << 0 << 0
0461                                           << 0 << 0
0462                                           << QRectF(0, 0, 1280, 1024)
0463                                           << QRectF(1280, 0, 1280, 1024)
0464                                           << QRectF(0, 0, 2560, 1024)
0465                                           << StrutRects();
0466     QTest::newRow("top panel 1 /strut") << QRect(1280, 0, 1280, 44)
0467                                         << 0 << 0 << 44 << 0
0468                                         << 0 << 0
0469                                         << 0 << 0
0470                                         << 1280 << 2559
0471                                         << 0 << 0
0472                                         << QRectF(0, 0, 1280, 1024)
0473                                         << QRectF(1280, 44, 1280, 980)
0474                                         << QRectF(0, 44, 2560, 980)
0475                                         << StrutRects{StrutRect(1280, 0, 1279, 44)};
0476     QTest::newRow("left panel 1/no strut") << QRect(1280, 0, 60, 1024)
0477                                            << 0 << 0 << 0 << 0
0478                                            << 0 << 0
0479                                            << 0 << 0
0480                                            << 0 << 0
0481                                            << 0 << 0
0482                                            << QRectF(0, 0, 1280, 1024)
0483                                            << QRectF(1280, 0, 1280, 1024)
0484                                            << QRectF(0, 0, 2560, 1024)
0485                                            << StrutRects();
0486     QTest::newRow("left panel 1/strut") << QRect(1280, 0, 60, 1024)
0487                                         << 1340 << 0 << 0 << 0
0488                                         << 0 << 1023
0489                                         << 0 << 0
0490                                         << 0 << 0
0491                                         << 0 << 0
0492                                         << QRectF(0, 0, 1280, 1024)
0493                                         << QRectF(1340, 0, 1220, 1024)
0494                                         << QRectF(0, 0, 2560, 1024)
0495                                         << StrutRects{StrutRect(1280, 0, 60, 1023)};
0496     // invalid struts
0497     QTest::newRow("bottom panel/ invalid strut") << QRect(0, 980, 1280, 44)
0498                                                  << 1280 << 0 << 0 << 44
0499                                                  << 980 << 1024
0500                                                  << 0 << 0
0501                                                  << 0 << 0
0502                                                  << 0 << 1279
0503                                                  << QRectF(0, 0, 1280, 1024)
0504                                                  << QRectF(1280, 0, 1280, 1024)
0505                                                  << QRectF(0, 0, 2560, 1024)
0506                                                  << StrutRects{StrutRect(0, 980, 1279, 44), StrutRect(0, 980, 1280, 44)};
0507     QTest::newRow("top panel/ invalid strut") << QRect(0, 0, 1280, 44)
0508                                               << 1280 << 0 << 44 << 0
0509                                               << 0 << 44
0510                                               << 0 << 0
0511                                               << 0 << 1279
0512                                               << 0 << 0
0513                                               << QRectF(0, 0, 1280, 1024)
0514                                               << QRectF(1280, 0, 1280, 1024)
0515                                               << QRectF(0, 0, 2560, 1024)
0516                                               << StrutRects{StrutRect(0, 0, 1279, 44), StrutRect(0, 0, 1280, 44)};
0517     QTest::newRow("top panel/invalid strut 2") << QRect(0, 0, 1280, 44)
0518                                                << 0 << 0 << 1024 << 0
0519                                                << 0 << 0
0520                                                << 0 << 0
0521                                                << 0 << 1279
0522                                                << 0 << 0
0523                                                << QRectF(0, 0, 1280, 1024)
0524                                                << QRectF(1280, 0, 1280, 1024)
0525                                                << QRectF(0, 0, 2560, 1024)
0526                                                << StrutRects();
0527 }
0528 
0529 struct XcbConnectionDeleter
0530 {
0531     void operator()(xcb_connection_t *pointer)
0532     {
0533         xcb_disconnect(pointer);
0534     }
0535 };
0536 
0537 void StrutsTest::testX11Struts()
0538 {
0539     // this test verifies that struts are applied correctly for X11 windows
0540 
0541     VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop();
0542     const QList<Output *> outputs = workspace()->outputs();
0543 
0544     // no, struts yet
0545     // first screen
0546     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0547     QCOMPARE(workspace()->clientArea(MovementArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0548     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0549     QCOMPARE(workspace()->clientArea(MaximizeFullArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0550     QCOMPARE(workspace()->clientArea(FullScreenArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0551     QCOMPARE(workspace()->clientArea(ScreenArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0552     // second screen
0553     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0554     QCOMPARE(workspace()->clientArea(MovementArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0555     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0556     QCOMPARE(workspace()->clientArea(MaximizeFullArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0557     QCOMPARE(workspace()->clientArea(FullScreenArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0558     QCOMPARE(workspace()->clientArea(ScreenArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0559     // combined
0560     QCOMPARE(workspace()->clientArea(WorkArea, outputs[0], desktop), QRect(0, 0, 2560, 1024));
0561     QCOMPARE(workspace()->clientArea(FullArea, outputs[0], desktop), QRect(0, 0, 2560, 1024));
0562     QCOMPARE(workspace()->restrictedMoveArea(desktop), StrutRects());
0563 
0564     // create an xcb window
0565     std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr));
0566     QVERIFY(!xcb_connection_has_error(c.get()));
0567 
0568     xcb_window_t windowId = xcb_generate_id(c.get());
0569     QFETCH(QRect, windowGeometry);
0570     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
0571                       windowGeometry.x(),
0572                       windowGeometry.y(),
0573                       windowGeometry.width(),
0574                       windowGeometry.height(),
0575                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0576     xcb_size_hints_t hints;
0577     memset(&hints, 0, sizeof(hints));
0578     xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
0579     xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
0580     xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
0581     NETWinInfo info(c.get(), windowId, rootWindow(), NET::WMAllProperties, NET::WM2AllProperties);
0582     info.setWindowType(NET::Dock);
0583     // set the extended strut
0584     QFETCH(int, leftStrut);
0585     QFETCH(int, rightStrut);
0586     QFETCH(int, topStrut);
0587     QFETCH(int, bottomStrut);
0588     QFETCH(int, leftStrutStart);
0589     QFETCH(int, leftStrutEnd);
0590     QFETCH(int, rightStrutStart);
0591     QFETCH(int, rightStrutEnd);
0592     QFETCH(int, topStrutStart);
0593     QFETCH(int, topStrutEnd);
0594     QFETCH(int, bottomStrutStart);
0595     QFETCH(int, bottomStrutEnd);
0596     NETExtendedStrut strut;
0597     strut.left_start = leftStrutStart;
0598     strut.left_end = leftStrutEnd;
0599     strut.left_width = leftStrut;
0600     strut.right_start = rightStrutStart;
0601     strut.right_end = rightStrutEnd;
0602     strut.right_width = rightStrut;
0603     strut.top_start = topStrutStart;
0604     strut.top_end = topStrutEnd;
0605     strut.top_width = topStrut;
0606     strut.bottom_start = bottomStrutStart;
0607     strut.bottom_end = bottomStrutEnd;
0608     strut.bottom_width = bottomStrut;
0609     info.setExtendedStrut(strut);
0610     xcb_map_window(c.get(), windowId);
0611     xcb_flush(c.get());
0612 
0613     // we should get a window for it
0614     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0615     QVERIFY(windowCreatedSpy.wait());
0616     X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
0617     QVERIFY(window);
0618     QCOMPARE(window->window(), windowId);
0619     QVERIFY(!window->isDecorated());
0620     QCOMPARE(window->windowType(), NET::Dock);
0621     QCOMPARE(window->frameGeometry(), windowGeometry);
0622 
0623     // this should have affected the client area
0624     // some props are independent of struts - those first
0625     // screen 0
0626     QCOMPARE(workspace()->clientArea(MovementArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0627     QCOMPARE(workspace()->clientArea(MaximizeFullArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0628     QCOMPARE(workspace()->clientArea(FullScreenArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0629     QCOMPARE(workspace()->clientArea(ScreenArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0630     // screen 1
0631     QCOMPARE(workspace()->clientArea(MovementArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0632     QCOMPARE(workspace()->clientArea(MaximizeFullArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0633     QCOMPARE(workspace()->clientArea(FullScreenArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0634     QCOMPARE(workspace()->clientArea(ScreenArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0635     // combined
0636     QCOMPARE(workspace()->clientArea(FullArea, outputs[0], desktop), QRect(0, 0, 2560, 1024));
0637 
0638     // now verify the actual updated client areas
0639     QTEST(workspace()->clientArea(PlacementArea, outputs[0], desktop), "screen0Maximized");
0640     QTEST(workspace()->clientArea(MaximizeArea, outputs[0], desktop), "screen0Maximized");
0641     QTEST(workspace()->clientArea(PlacementArea, outputs[1], desktop), "screen1Maximized");
0642     QTEST(workspace()->clientArea(MaximizeArea, outputs[1], desktop), "screen1Maximized");
0643     QTEST(workspace()->clientArea(WorkArea, outputs[0], desktop), "workArea");
0644     QTEST(workspace()->restrictedMoveArea(desktop), "restrictedMoveArea");
0645 
0646     // and destroy the window again
0647     xcb_unmap_window(c.get(), windowId);
0648     xcb_destroy_window(c.get(), windowId);
0649     xcb_flush(c.get());
0650     c.reset();
0651 
0652     QSignalSpy windowClosedSpy(window, &X11Window::windowClosed);
0653     QVERIFY(windowClosedSpy.wait());
0654 
0655     // now struts should be removed again
0656     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0657     QCOMPARE(workspace()->clientArea(MovementArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0658     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0659     QCOMPARE(workspace()->clientArea(MaximizeFullArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0660     QCOMPARE(workspace()->clientArea(FullScreenArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0661     QCOMPARE(workspace()->clientArea(ScreenArea, outputs[0], desktop), QRect(0, 0, 1280, 1024));
0662     // second screen
0663     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0664     QCOMPARE(workspace()->clientArea(MovementArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0665     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0666     QCOMPARE(workspace()->clientArea(MaximizeFullArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0667     QCOMPARE(workspace()->clientArea(FullScreenArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0668     QCOMPARE(workspace()->clientArea(ScreenArea, outputs[1], desktop), QRect(1280, 0, 1280, 1024));
0669     // combined
0670     QCOMPARE(workspace()->clientArea(WorkArea, outputs[0], desktop), QRect(0, 0, 2560, 1024));
0671     QCOMPARE(workspace()->clientArea(FullArea, outputs[0], desktop), QRect(0, 0, 2560, 1024));
0672     QCOMPARE(workspace()->restrictedMoveArea(desktop), StrutRects());
0673 }
0674 
0675 void StrutsTest::test363804()
0676 {
0677     // this test verifies the condition described in BUG 363804
0678     // two screens in a vertical setup, aligned to right border with panel on the bottom screen
0679     const QVector<QRect> geometries{QRect(0, 0, 1920, 1080), QRect(554, 1080, 1366, 768)};
0680     QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs",
0681                               Qt::DirectConnection,
0682                               Q_ARG(QVector<QRect>, geometries));
0683     QCOMPARE(workspace()->geometry(), QRect(0, 0, 1920, 1848));
0684 
0685     VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop();
0686     const QList<Output *> outputs = workspace()->outputs();
0687     QCOMPARE(outputs.count(), 2);
0688     QCOMPARE(outputs[0]->geometry(), geometries[0]);
0689     QCOMPARE(outputs[1]->geometry(), geometries[1]);
0690 
0691     // create an xcb window
0692     std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr));
0693     QVERIFY(!xcb_connection_has_error(c.get()));
0694 
0695     xcb_window_t windowId = xcb_generate_id(c.get());
0696     const QRect windowGeometry(554, 1812, 1366, 36);
0697     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
0698                       windowGeometry.x(),
0699                       windowGeometry.y(),
0700                       windowGeometry.width(),
0701                       windowGeometry.height(),
0702                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0703     xcb_size_hints_t hints;
0704     memset(&hints, 0, sizeof(hints));
0705     xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
0706     xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
0707     xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
0708     NETWinInfo info(c.get(), windowId, rootWindow(), NET::WMAllProperties, NET::WM2AllProperties);
0709     info.setWindowType(NET::Dock);
0710     NETExtendedStrut strut;
0711     strut.left_start = 0;
0712     strut.left_end = 0;
0713     strut.left_width = 0;
0714     strut.right_start = 0;
0715     strut.right_end = 0;
0716     strut.right_width = 0;
0717     strut.top_start = 0;
0718     strut.top_end = 0;
0719     strut.top_width = 0;
0720     strut.bottom_start = 554;
0721     strut.bottom_end = 1919;
0722     strut.bottom_width = 36;
0723     info.setExtendedStrut(strut);
0724     xcb_map_window(c.get(), windowId);
0725     xcb_flush(c.get());
0726 
0727     // we should get a window for it
0728     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0729     QVERIFY(windowCreatedSpy.wait());
0730     X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
0731     QVERIFY(window);
0732     QCOMPARE(window->window(), windowId);
0733     QVERIFY(!window->isDecorated());
0734     QCOMPARE(window->windowType(), NET::Dock);
0735     QCOMPARE(window->frameGeometry(), windowGeometry);
0736 
0737     // now verify the actual updated client areas
0738     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[0], desktop), geometries.at(0));
0739     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[0], desktop), geometries.at(0));
0740     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[1], desktop), QRect(554, 1080, 1366, 732));
0741     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[1], desktop), QRect(554, 1080, 1366, 732));
0742     QCOMPARE(workspace()->clientArea(WorkArea, outputs[0], desktop), QRect(0, 0, 1920, 1812));
0743 
0744     // and destroy the window again
0745     xcb_unmap_window(c.get(), windowId);
0746     xcb_destroy_window(c.get(), windowId);
0747     xcb_flush(c.get());
0748     c.reset();
0749 
0750     QSignalSpy windowClosedSpy(window, &X11Window::windowClosed);
0751     QVERIFY(windowClosedSpy.wait());
0752 }
0753 
0754 void StrutsTest::testLeftScreenSmallerBottomAligned()
0755 {
0756     // this test verifies a two screen setup with the left screen smaller than the right and bottom aligned
0757     // the panel is on the top of the left screen, thus not at 0/0
0758     // what this test in addition tests is whether a window larger than the left screen is not placed into
0759     // the dead area
0760     const QVector<QRect> geometries{QRect(0, 282, 1366, 768), QRect(1366, 0, 1680, 1050)};
0761     QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs",
0762                               Qt::DirectConnection,
0763                               Q_ARG(QVector<QRect>, geometries));
0764     QCOMPARE(workspace()->geometry(), QRect(0, 0, 3046, 1050));
0765 
0766     const QList<Output *> outputs = workspace()->outputs();
0767     QCOMPARE(outputs[0]->geometry(), geometries.at(0));
0768     QCOMPARE(outputs[1]->geometry(), geometries.at(1));
0769 
0770     // the test window will be on the current desktop
0771     VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop();
0772 
0773     // create the panel
0774     std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr));
0775     QVERIFY(!xcb_connection_has_error(c.get()));
0776 
0777     xcb_window_t windowId = xcb_generate_id(c.get());
0778     const QRect windowGeometry(0, 282, 1366, 24);
0779     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
0780                       windowGeometry.x(),
0781                       windowGeometry.y(),
0782                       windowGeometry.width(),
0783                       windowGeometry.height(),
0784                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0785     xcb_size_hints_t hints;
0786     memset(&hints, 0, sizeof(hints));
0787     xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
0788     xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
0789     xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
0790     NETWinInfo info(c.get(), windowId, rootWindow(), NET::WMAllProperties, NET::WM2AllProperties);
0791     info.setWindowType(NET::Dock);
0792     NETExtendedStrut strut;
0793     strut.left_start = 0;
0794     strut.left_end = 0;
0795     strut.left_width = 0;
0796     strut.right_start = 0;
0797     strut.right_end = 0;
0798     strut.right_width = 0;
0799     strut.top_start = 0;
0800     strut.top_end = 1365;
0801     strut.top_width = 306;
0802     strut.bottom_start = 0;
0803     strut.bottom_end = 0;
0804     strut.bottom_width = 0;
0805     info.setExtendedStrut(strut);
0806     xcb_map_window(c.get(), windowId);
0807     xcb_flush(c.get());
0808 
0809     // we should get a window for it
0810     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0811     QVERIFY(windowCreatedSpy.wait());
0812     X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
0813     QVERIFY(window);
0814     QCOMPARE(window->window(), windowId);
0815     QVERIFY(!window->isDecorated());
0816     QCOMPARE(window->windowType(), NET::Dock);
0817     QCOMPARE(window->frameGeometry(), windowGeometry);
0818 
0819     // now verify the actual updated client areas
0820     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[0], desktop), QRect(0, 306, 1366, 744));
0821     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[0], desktop), QRect(0, 306, 1366, 744));
0822     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[1], desktop), geometries.at(1));
0823     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[1], desktop), geometries.at(1));
0824     QCOMPARE(workspace()->clientArea(WorkArea, outputs[0], desktop), QRect(0, 0, 3046, 1050));
0825 
0826     // now create a window which is larger than screen 0
0827 
0828     xcb_window_t w2 = xcb_generate_id(c.get());
0829     const QRect windowGeometry2(0, 26, 1280, 774);
0830     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, w2, rootWindow(),
0831                       windowGeometry2.x(),
0832                       windowGeometry2.y(),
0833                       windowGeometry2.width(),
0834                       windowGeometry2.height(),
0835                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0836     xcb_size_hints_t hints2;
0837     memset(&hints2, 0, sizeof(hints2));
0838     xcb_icccm_size_hints_set_min_size(&hints2, 868, 431);
0839     xcb_icccm_set_wm_normal_hints(c.get(), w2, &hints2);
0840     xcb_map_window(c.get(), w2);
0841     xcb_flush(c.get());
0842     QVERIFY(windowCreatedSpy.wait());
0843     X11Window *window2 = windowCreatedSpy.last().first().value<X11Window *>();
0844     QVERIFY(window2);
0845     QVERIFY(window2 != window);
0846     QVERIFY(window2->isDecorated());
0847     QCOMPARE(window2->frameGeometry(), QRect(0, 306, 1366, 744));
0848     QCOMPARE(window2->maximizeMode(), KWin::MaximizeFull);
0849     // destroy window again
0850     QSignalSpy normalWindowClosedSpy(window2, &X11Window::windowClosed);
0851     xcb_unmap_window(c.get(), w2);
0852     xcb_destroy_window(c.get(), w2);
0853     xcb_flush(c.get());
0854     QVERIFY(normalWindowClosedSpy.wait());
0855 
0856     // and destroy the window again
0857     xcb_unmap_window(c.get(), windowId);
0858     xcb_destroy_window(c.get(), windowId);
0859     xcb_flush(c.get());
0860     c.reset();
0861 
0862     QSignalSpy windowClosedSpy(window, &X11Window::windowClosed);
0863     QVERIFY(windowClosedSpy.wait());
0864 }
0865 
0866 void StrutsTest::testWindowMoveWithPanelBetweenScreens()
0867 {
0868     // this test verifies the condition of BUG
0869     // when moving a window with decorations in a restricted way it should pass from one screen
0870     // to the other even if there is a panel in between.
0871 
0872     // left screen must be smaller than right screen
0873     const QVector<QRect> geometries{QRect(0, 282, 1366, 768), QRect(1366, 0, 1680, 1050)};
0874     QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs",
0875                               Qt::DirectConnection,
0876                               Q_ARG(QVector<QRect>, geometries));
0877     QCOMPARE(workspace()->geometry(), QRect(0, 0, 3046, 1050));
0878 
0879     const QList<Output *> outputs = workspace()->outputs();
0880     QCOMPARE(outputs[0]->geometry(), geometries.at(0));
0881     QCOMPARE(outputs[1]->geometry(), geometries.at(1));
0882 
0883     // all windows will be placed on the current desktop
0884     VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop();
0885 
0886     // create the panel on the right screen, left edge
0887     std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr));
0888     QVERIFY(!xcb_connection_has_error(c.get()));
0889 
0890     xcb_window_t windowId = xcb_generate_id(c.get());
0891     const QRect windowGeometry(1366, 0, 24, 1050);
0892     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
0893                       windowGeometry.x(),
0894                       windowGeometry.y(),
0895                       windowGeometry.width(),
0896                       windowGeometry.height(),
0897                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0898     xcb_size_hints_t hints;
0899     memset(&hints, 0, sizeof(hints));
0900     xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
0901     xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
0902     xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
0903     NETWinInfo info(c.get(), windowId, rootWindow(), NET::WMAllProperties, NET::WM2AllProperties);
0904     info.setWindowType(NET::Dock);
0905     NETExtendedStrut strut;
0906     strut.left_start = 0;
0907     strut.left_end = 1050;
0908     strut.left_width = 1366 + 24;
0909     strut.right_start = 0;
0910     strut.right_end = 0;
0911     strut.right_width = 0;
0912     strut.top_start = 0;
0913     strut.top_end = 0;
0914     strut.top_width = 0;
0915     strut.bottom_start = 0;
0916     strut.bottom_end = 0;
0917     strut.bottom_width = 0;
0918     info.setExtendedStrut(strut);
0919     xcb_map_window(c.get(), windowId);
0920     xcb_flush(c.get());
0921 
0922     // we should get a window for it
0923     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0924     QVERIFY(windowCreatedSpy.wait());
0925     X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
0926     QVERIFY(window);
0927     QCOMPARE(window->window(), windowId);
0928     QVERIFY(!window->isDecorated());
0929     QCOMPARE(window->windowType(), NET::Dock);
0930     QCOMPARE(window->frameGeometry(), windowGeometry);
0931 
0932     // now verify the actual updated client areas
0933     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[0], desktop), QRect(0, 282, 1366, 768));
0934     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[0], desktop), QRect(0, 282, 1366, 768));
0935     QCOMPARE(workspace()->clientArea(PlacementArea, outputs[1], desktop), QRect(1390, 0, 1656, 1050));
0936     QCOMPARE(workspace()->clientArea(MaximizeArea, outputs[1], desktop), QRect(1390, 0, 1656, 1050));
0937     QCOMPARE(workspace()->clientArea(WorkArea, outputs[0], desktop), QRect(0, 0, 3046, 1050));
0938     QCOMPARE(workspace()->restrictedMoveArea(desktop), StrutRects{StrutRect(1366, 0, 24, 1050)});
0939 
0940     // create another window and try to move it
0941 
0942     xcb_window_t w2 = xcb_generate_id(c.get());
0943     const QRect windowGeometry2(1500, 400, 200, 300);
0944     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, w2, rootWindow(),
0945                       windowGeometry2.x(),
0946                       windowGeometry2.y(),
0947                       windowGeometry2.width(),
0948                       windowGeometry2.height(),
0949                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0950     xcb_size_hints_t hints2;
0951     memset(&hints2, 0, sizeof(hints2));
0952     xcb_icccm_size_hints_set_position(&hints2, 1, windowGeometry2.x(), windowGeometry2.y());
0953     xcb_icccm_size_hints_set_min_size(&hints2, 200, 300);
0954     xcb_icccm_set_wm_normal_hints(c.get(), w2, &hints2);
0955     xcb_map_window(c.get(), w2);
0956     xcb_flush(c.get());
0957     QVERIFY(windowCreatedSpy.wait());
0958     X11Window *window2 = windowCreatedSpy.last().first().value<X11Window *>();
0959     QVERIFY(window2);
0960     QVERIFY(window2 != window);
0961     QVERIFY(window2->isDecorated());
0962     QCOMPARE(window2->clientSize(), QSize(200, 300));
0963     QCOMPARE(window2->pos(), QPoint(1500, 400));
0964 
0965     const QRectF origGeo = window2->frameGeometry();
0966     Cursors::self()->mouse()->setPos(origGeo.center());
0967     workspace()->performWindowOperation(window2, Options::MoveOp);
0968     QTRY_COMPARE(workspace()->moveResizeWindow(), window2);
0969     QVERIFY(window2->isInteractiveMove());
0970     // move to next screen - step is 8 pixel, so 800 pixel
0971     for (int i = 0; i < 100; i++) {
0972         window2->keyPressEvent(Qt::Key_Left);
0973     }
0974     window2->keyPressEvent(Qt::Key_Enter);
0975     QCOMPARE(window2->isInteractiveMove(), false);
0976     QVERIFY(workspace()->moveResizeWindow() == nullptr);
0977     QCOMPARE(window2->frameGeometry(), QRectF(origGeo.translated(-800, 0)));
0978 }
0979 
0980 }
0981 
0982 WAYLANDTEST_MAIN(KWin::StrutsTest)
0983 #include "struts_test.moc"