File indexing completed on 2025-03-16 13:55:59
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"