diff --git a/autotests/wayland/struts_test.cpp b/autotests/wayland/struts_test.cpp index f8f35e4489..f49ee3956c 100644 --- a/autotests/wayland/struts_test.cpp +++ b/autotests/wayland/struts_test.cpp @@ -32,6 +32,7 @@ along with this program. If not, see . #include #include #include +#include #include #include #include @@ -56,6 +57,8 @@ private Q_SLOTS: void initTestCase(); void init(); void cleanup(); + void testWaylandStruts_data(); + void testWaylandStruts(); void testX11Struts_data(); void testX11Struts(); void test363804(); @@ -68,6 +71,7 @@ private: KWayland::Client::ShmPool *m_shm = nullptr; KWayland::Client::Shell *m_shell = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; + KWayland::Client::PlasmaShell *m_plasmaShell = nullptr; QThread *m_thread = nullptr; }; @@ -145,6 +149,10 @@ void StrutsTest::init() QVERIFY(m_seat->isValid()); m_deco = registry.createServerSideDecorationManager(decorationSpy.first().first().value(), decorationSpy.first().last().value()); QVERIFY(m_deco->isValid()); + m_plasmaShell = registry.createPlasmaShell(registry.interface(Registry::Interface::PlasmaShell).name, + registry.interface(Registry::Interface::PlasmaShell).version, + this); + QVERIFY(m_plasmaShell); QSignalSpy hasPointerSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(hasPointerSpy.isValid()); QVERIFY(hasPointerSpy.wait()); @@ -165,6 +173,8 @@ void StrutsTest::cleanup() m_shm = nullptr; delete m_shell; m_shell = nullptr; + delete m_plasmaShell; + m_plasmaShell = nullptr; delete m_queue; m_queue = nullptr; if (m_thread) { @@ -175,6 +185,118 @@ void StrutsTest::cleanup() m_thread = nullptr; m_connection = nullptr; } + while (!waylandServer()->clients().isEmpty()) { + QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); + } + QVERIFY(waylandServer()->clients().isEmpty()); +} + +void StrutsTest::testWaylandStruts_data() +{ + QTest::addColumn>("windowGeometries"); + QTest::addColumn("screen0Maximized"); + QTest::addColumn("screen1Maximized"); + QTest::addColumn("workArea"); + + QTest::newRow("bottom/0") << QVector{QRect(0, 992, 1280, 32)} << QRect(0, 0, 1280, 992) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 992); + QTest::newRow("bottom/1") << QVector{QRect(1280, 992, 1280, 32)} << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 992) << QRect(0, 0, 2560, 992); + QTest::newRow("top/0") << QVector{QRect(0, 0, 1280, 32)} << QRect(0, 32, 1280, 992) << QRect(1280, 0, 1280, 1024) << QRect(0, 32, 2560, 992); + QTest::newRow("top/1") << QVector{QRect(1280, 0, 1280, 32)} << QRect(0, 0, 1280, 1024) << QRect(1280, 32, 1280, 992) << QRect(0, 32, 2560, 992); + QTest::newRow("left/0") << QVector{QRect(0, 0, 32, 1024)} << QRect(32, 0, 1248, 1024) << QRect(1280, 0, 1280, 1024) << QRect(32, 0, 2528, 1024); + QTest::newRow("left/1") << QVector{QRect(1280, 0, 32, 1024)} << QRect(0, 0, 1280, 1024) << QRect(1312, 0, 1248, 1024) << QRect(0, 0, 2560, 1024); + QTest::newRow("right/0") << QVector{QRect(1248, 0, 32, 1024)} << QRect(0, 0, 1248, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); + QTest::newRow("right/1") << QVector{QRect(2528, 0, 32, 1024)} << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1248, 1024) << QRect(0, 0, 2528, 1024); + + // same with partial panels not covering the whole area + QTest::newRow("part bottom/0") << QVector{QRect(100, 992, 1080, 32)} << QRect(0, 0, 1280, 992) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 992); + QTest::newRow("part bottom/1") << QVector{QRect(1380, 992, 1080, 32)} << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 992) << QRect(0, 0, 2560, 992); + QTest::newRow("part top/0") << QVector{QRect(100, 0, 1080, 32)} << QRect(0, 32, 1280, 992) << QRect(1280, 0, 1280, 1024) << QRect(0, 32, 2560, 992); + QTest::newRow("part top/1") << QVector{QRect(1380, 0, 1080, 32)} << QRect(0, 0, 1280, 1024) << QRect(1280, 32, 1280, 992) << QRect(0, 32, 2560, 992); + QTest::newRow("part left/0") << QVector{QRect(0, 100, 32, 824)} << QRect(32, 0, 1248, 1024) << QRect(1280, 0, 1280, 1024) << QRect(32, 0, 2528, 1024); + QTest::newRow("part left/1") << QVector{QRect(1280, 100, 32, 824)} << QRect(0, 0, 1280, 1024) << QRect(1312, 0, 1248, 1024) << QRect(0, 0, 2560, 1024); + QTest::newRow("part right/0") << QVector{QRect(1248, 100, 32, 824)} << QRect(0, 0, 1248, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); + QTest::newRow("part right/1") << QVector{QRect(2528, 100, 32, 824)} << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1248, 1024) << QRect(0, 0, 2528, 1024); + + // multiple panels + QTest::newRow("two bottom panels") << QVector{QRect(100, 992, 1080, 32), QRect(1380, 984, 1080, 40)} << QRect(0, 0, 1280, 992) << QRect(1280, 0, 1280, 984) << QRect(0, 0, 2560, 984); + QTest::newRow("two left panels") << QVector{QRect(0, 10, 32, 390), QRect(0, 450, 40, 100)} << QRect(40, 0, 1240, 1024) << QRect(1280, 0, 1280, 1024) << QRect(40, 0, 2520, 1024); +} + +void StrutsTest::testWaylandStruts() +{ + // this test verifies that struts on Wayland panels are handled correctly + using namespace KWayland::Client; + // no, struts yet + QVERIFY(waylandServer()->clients().isEmpty()); + // first screen + QCOMPARE(workspace()->clientArea(PlacementArea, 0, 1), QRect(0, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(MovementArea, 0, 1), QRect(0, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(MaximizeArea, 0, 1), QRect(0, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(MaximizeFullArea, 0, 1), QRect(0, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(FullScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(ScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); + // second screen + QCOMPARE(workspace()->clientArea(PlacementArea, 1, 1), QRect(1280, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(MovementArea, 1, 1), QRect(1280, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(MaximizeArea, 1, 1), QRect(1280, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(MaximizeFullArea, 1, 1), QRect(1280, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(FullScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(ScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); + // combined + QCOMPARE(workspace()->clientArea(WorkArea, 0, 1), QRect(0, 0, 2560, 1024)); + QCOMPARE(workspace()->clientArea(FullArea, 0, 1), QRect(0, 0, 2560, 1024)); + + QFETCH(QVector, windowGeometries); + // create the panels + QSignalSpy windowCreatedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QVERIFY(windowCreatedSpy.isValid()); + for (auto it = windowGeometries.constBegin(), end = windowGeometries.constEnd(); it != end; it++) { + const QRect windowGeometry = *it; + Surface *surface = m_compositor->createSurface(m_compositor); + ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + Q_UNUSED(shellSurface) + PlasmaShellSurface *plasmaSurface = m_plasmaShell->createSurface(surface, surface); + plasmaSurface->setPosition(windowGeometry.topLeft()); + plasmaSurface->setRole(PlasmaShellSurface::Role::Panel); + + // map the window + QImage img(windowGeometry.size(), QImage::Format_RGB32); + img.fill(Qt::red); + surface->attachBuffer(m_shm->createBuffer(img)); + surface->damage(QRect(QPoint(0, 0), windowGeometry.size())); + surface->commit(Surface::CommitFlag::None); + + QVERIFY(windowCreatedSpy.wait()); + QCOMPARE(windowCreatedSpy.count(), 1); + auto c = windowCreatedSpy.first().first().value(); + QVERIFY(c); + QVERIFY(!c->isActive()); + QCOMPARE(c->geometry(), windowGeometry); + QVERIFY(c->isDock()); + QVERIFY(c->hasStrut()); + windowCreatedSpy.clear(); + } + + // some props are independent of struts - those first + // screen 0 + QCOMPARE(workspace()->clientArea(MovementArea, 0, 1), QRect(0, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(MaximizeFullArea, 0, 1), QRect(0, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(FullScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(ScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); + // screen 1 + QCOMPARE(workspace()->clientArea(MovementArea, 1, 1), QRect(1280, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(MaximizeFullArea, 1, 1), QRect(1280, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(FullScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); + QCOMPARE(workspace()->clientArea(ScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); + // combined + QCOMPARE(workspace()->clientArea(FullArea, 0, 1), QRect(0, 0, 2560, 1024)); + + // now verify the actual updated client areas + QTEST(workspace()->clientArea(PlacementArea, 0, 1), "screen0Maximized"); + QTEST(workspace()->clientArea(MaximizeArea, 0, 1), "screen0Maximized"); + QTEST(workspace()->clientArea(PlacementArea, 1, 1), "screen1Maximized"); + QTEST(workspace()->clientArea(MaximizeArea, 1, 1), "screen1Maximized"); + QTEST(workspace()->clientArea(WorkArea, 0, 1), "workArea"); } void StrutsTest::testX11Struts_data() diff --git a/geometry.cpp b/geometry.cpp index 0eb289940a..aa33a8ddce 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -188,19 +188,44 @@ void Workspace::updateClientArea(bool force) if (!c->hasStrut()) { return; } + auto margins = [c] (const QRect &geometry) { + QMargins margins; + if (!geometry.intersects(c->geometry())) { + return margins; + } + // figure out which areas of the overall screen setup it borders + const bool left = c->geometry().left() == geometry.left(); + const bool right = c->geometry().right() == geometry.right(); + const bool top = c->geometry().top() == geometry.top(); + const bool bottom = c->geometry().bottom() == geometry.bottom(); + const bool horizontal = c->geometry().width() >= c->geometry().height(); + if (left && ((!top && !bottom) || !horizontal)) { + margins.setLeft(c->geometry().width()); + } + if (right && ((!top && !bottom) || !horizontal)) { + margins.setRight(c->geometry().width()); + } + if (top && ((!left && !right) || horizontal)) { + margins.setTop(c->geometry().height()); + } + if (bottom && ((!left && !right) || horizontal)) { + margins.setBottom(c->geometry().height()); + } + return margins; + }; // TODO: implement restrictedMoveArea adjustments - QRegion r = QRegion(desktopArea).subtracted(c->geometry()); + QRect r = desktopArea - margins(KWin::screens()->geometry()); if (c->isOnAllDesktops()) { for (int i = 1; i <= numberOfDesktops; ++i) { - new_wareas[ i ] = new_wareas[ i ].intersected(r.boundingRect()); + new_wareas[ i ] = new_wareas[ i ].intersected(r); for (int iS = 0; iS < nscreens; ++iS) { - new_sareas[ i ][ iS ] = new_sareas[ i ][ iS ].intersected(QRegion(screens[iS]).subtracted(c->geometry()).boundingRect()); + new_sareas[ i ][ iS ] = new_sareas[ i ][ iS ].intersected(screens[iS] - margins(screens[iS])); } } } else { - new_wareas[c->desktop()] = new_wareas[c->desktop()].intersected(r.boundingRect()); + new_wareas[c->desktop()] = new_wareas[c->desktop()].intersected(r); for (int iS = 0; iS < nscreens; iS++) { - new_sareas[c->desktop()][ iS ] = new_sareas[c->desktop()][ iS ].intersected(QRegion(screens[iS]).subtracted(c->geometry()).boundingRect()); + new_sareas[c->desktop()][ iS ] = new_sareas[c->desktop()][ iS ].intersected(screens[iS] - margins(screens[iS])); } } };