From 58db4777966e380aede6c2f00b744ef792a49351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Thu, 9 Jun 2016 10:01:05 +0200 Subject: [PATCH] Fix the strut handling for wayland clients Summary: The implementation was broken as it transformed the QRects into QRegions, subtracted the geometries and took the bounding rect again. In several setups this could result in the strut getting ignored. This change improves the calculation of the struts by creating a QMargin which describes the area which needs to be subtracted from a screen rect. The QMargin is only adjusted for the edge the window borders. We can assume that a window with a strut needs to border a screen on Wayland. With this change we are also able to support panels between screens. On Wayland a panel placed on the right of a left screen affects the maximization area of the left screen, but does not affect the overall workarea. CCBUG: 167852 Reviewers: #plasma_on_wayland, #kwin Subscribers: plasma-devel, kwin Tags: #plasma_on_wayland, #kwin Differential Revision: https://phabricator.kde.org/D1803 --- autotests/wayland/struts_test.cpp | 122 ++++++++++++++++++++++++++++++ geometry.cpp | 35 +++++++-- 2 files changed, 152 insertions(+), 5 deletions(-) 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])); } } };