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

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 "wayland/seat_interface.h"
0015 #include "wayland/surface_interface.h"
0016 #include "wayland_server.h"
0017 #include "window.h"
0018 #include "workspace.h"
0019 #include <kwineffects.h>
0020 
0021 #include <KWayland/Client/compositor.h>
0022 #include <KWayland/Client/connection_thread.h>
0023 #include <KWayland/Client/event_queue.h>
0024 #include <KWayland/Client/keyboard.h>
0025 #include <KWayland/Client/plasmashell.h>
0026 #include <KWayland/Client/pointer.h>
0027 #include <KWayland/Client/registry.h>
0028 #include <KWayland/Client/seat.h>
0029 #include <KWayland/Client/server_decoration.h>
0030 #include <KWayland/Client/shm_pool.h>
0031 #include <KWayland/Client/surface.h>
0032 #include <KWayland/Client/touch.h>
0033 
0034 struct PopupLayout
0035 {
0036     QRect anchorRect;
0037     QSize size;
0038     quint32 anchor = 0;
0039     quint32 gravity = 0;
0040     quint32 constraint = 0;
0041 };
0042 Q_DECLARE_METATYPE(PopupLayout)
0043 
0044 namespace KWin
0045 {
0046 
0047 static const QString s_socketName = QStringLiteral("wayland_test_kwin_transient_placement-0");
0048 
0049 class TransientPlacementTest : public QObject
0050 {
0051     Q_OBJECT
0052 private Q_SLOTS:
0053     void initTestCase();
0054     void init();
0055     void cleanup();
0056     void testXdgPopup_data();
0057     void testXdgPopup();
0058     void testXdgPopupWithPanel();
0059 };
0060 
0061 void TransientPlacementTest::initTestCase()
0062 {
0063     qRegisterMetaType<KWin::Window *>();
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     kwinApp()->start();
0069     QVERIFY(applicationStartedSpy.wait());
0070     const auto outputs = workspace()->outputs();
0071     QCOMPARE(outputs.count(), 2);
0072     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0073     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0074     setenv("QT_QPA_PLATFORM", "wayland", true);
0075 }
0076 
0077 void TransientPlacementTest::init()
0078 {
0079     QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration | Test::AdditionalWaylandInterface::PlasmaShell));
0080 
0081     workspace()->setActiveOutput(QPoint(640, 512));
0082     Cursors::self()->mouse()->setPos(QPoint(640, 512));
0083 }
0084 
0085 void TransientPlacementTest::cleanup()
0086 {
0087     Test::destroyWaylandConnection();
0088 }
0089 
0090 void TransientPlacementTest::testXdgPopup_data()
0091 {
0092     QTest::addColumn<QSize>("parentSize");
0093     QTest::addColumn<QPoint>("parentPosition");
0094     QTest::addColumn<PopupLayout>("layout");
0095     QTest::addColumn<QRect>("expectedGeometry");
0096 
0097     // parent window is 500,500, starting at 300,300, anchorRect is therefore between 350->750 in both dirs
0098 
0099     // ----------------------------------------------------------------
0100     // window in the middle, plenty of room either side: Changing anchor
0101 
0102     const PopupLayout layoutAnchorCenter{
0103         .anchorRect = QRect(50, 50, 400, 400),
0104         .size = QSize(200, 200),
0105         .anchor = Test::XdgPositioner::anchor_none,
0106         .gravity = Test::XdgPositioner::gravity_bottom_right,
0107     };
0108     QTest::newRow("anchorCentre") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorCenter << QRect(550, 550, 200, 200);
0109 
0110     const PopupLayout layoutAnchorTopLeft{
0111         .anchorRect = QRect(50, 50, 400, 400),
0112         .size = QSize(200, 200),
0113         .anchor = Test::XdgPositioner::anchor_top_left,
0114         .gravity = Test::XdgPositioner::gravity_bottom_right,
0115     };
0116     QTest::newRow("anchorTopLeft") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorTopLeft << QRect(350, 350, 200, 200);
0117 
0118     const PopupLayout layoutAnchorTop{
0119         .anchorRect = QRect(50, 50, 400, 400),
0120         .size = QSize(200, 200),
0121         .anchor = Test::XdgPositioner::anchor_top,
0122         .gravity = Test::XdgPositioner::gravity_bottom_right,
0123     };
0124     QTest::newRow("anchorTop") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorTop << QRect(550, 350, 200, 200);
0125 
0126     const PopupLayout layoutAnchorTopRight{
0127         .anchorRect = QRect(50, 50, 400, 400),
0128         .size = QSize(200, 200),
0129         .anchor = Test::XdgPositioner::anchor_top_right,
0130         .gravity = Test::XdgPositioner::gravity_bottom_right,
0131     };
0132     QTest::newRow("anchorTopRight") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorTopRight << QRect(750, 350, 200, 200);
0133 
0134     const PopupLayout layoutAnchorRight{
0135         .anchorRect = QRect(50, 50, 400, 400),
0136         .size = QSize(200, 200),
0137         .anchor = Test::XdgPositioner::anchor_right,
0138         .gravity = Test::XdgPositioner::gravity_bottom_right,
0139     };
0140     QTest::newRow("anchorRight") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorRight << QRect(750, 550, 200, 200);
0141 
0142     const PopupLayout layoutAnchorBottomRight{
0143         .anchorRect = QRect(50, 50, 400, 400),
0144         .size = QSize(200, 200),
0145         .anchor = Test::XdgPositioner::anchor_bottom_right,
0146         .gravity = Test::XdgPositioner::gravity_bottom_right,
0147     };
0148     QTest::newRow("anchorBottomRight") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorBottomRight << QRect(750, 750, 200, 200);
0149 
0150     const PopupLayout layoutAnchorBottom{
0151         .anchorRect = QRect(50, 50, 400, 400),
0152         .size = QSize(200, 200),
0153         .anchor = Test::XdgPositioner::anchor_bottom,
0154         .gravity = Test::XdgPositioner::gravity_bottom_right,
0155     };
0156     QTest::newRow("anchorBottom") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorBottom << QRect(550, 750, 200, 200);
0157 
0158     const PopupLayout layoutAnchorBottomLeft{
0159         .anchorRect = QRect(50, 50, 400, 400),
0160         .size = QSize(200, 200),
0161         .anchor = Test::XdgPositioner::anchor_bottom_left,
0162         .gravity = Test::XdgPositioner::gravity_bottom_right,
0163     };
0164     QTest::newRow("anchorBottomLeft") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorBottomLeft << QRect(350, 750, 200, 200);
0165 
0166     const PopupLayout layoutAnchorLeft{
0167         .anchorRect = QRect(50, 50, 400, 400),
0168         .size = QSize(200, 200),
0169         .anchor = Test::XdgPositioner::anchor_left,
0170         .gravity = Test::XdgPositioner::gravity_bottom_right,
0171     };
0172     QTest::newRow("anchorLeft") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorLeft << QRect(350, 550, 200, 200);
0173 
0174     // ----------------------------------------------------------------
0175     // window in the middle, plenty of room either side: Changing gravity around the bottom right anchor
0176 
0177     const PopupLayout layoutGravityCenter{
0178         .anchorRect = QRect(50, 50, 400, 400),
0179         .size = QSize(200, 200),
0180         .anchor = Test::XdgPositioner::anchor_bottom_right,
0181         .gravity = Test::XdgPositioner::gravity_none,
0182     };
0183     QTest::newRow("gravityCentre") << QSize(500, 500) << QPoint(300, 300) << layoutGravityCenter << QRect(650, 650, 200, 200);
0184 
0185     const PopupLayout layoutGravityTopLeft{
0186         .anchorRect = QRect(50, 50, 400, 400),
0187         .size = QSize(200, 200),
0188         .anchor = Test::XdgPositioner::anchor_bottom_right,
0189         .gravity = Test::XdgPositioner::gravity_top_left,
0190     };
0191     QTest::newRow("gravityTopLeft") << QSize(500, 500) << QPoint(300, 300) << layoutGravityTopLeft << QRect(550, 550, 200, 200);
0192 
0193     const PopupLayout layoutGravityTop{
0194         .anchorRect = QRect(50, 50, 400, 400),
0195         .size = QSize(200, 200),
0196         .anchor = Test::XdgPositioner::anchor_bottom_right,
0197         .gravity = Test::XdgPositioner::gravity_top,
0198     };
0199     QTest::newRow("gravityTop") << QSize(500, 500) << QPoint(300, 300) << layoutGravityTop << QRect(650, 550, 200, 200);
0200 
0201     const PopupLayout layoutGravityTopRight{
0202         .anchorRect = QRect(50, 50, 400, 400),
0203         .size = QSize(200, 200),
0204         .anchor = Test::XdgPositioner::anchor_bottom_right,
0205         .gravity = Test::XdgPositioner::gravity_top_right,
0206     };
0207     QTest::newRow("gravityTopRight") << QSize(500, 500) << QPoint(300, 300) << layoutGravityTopRight << QRect(750, 550, 200, 200);
0208 
0209     const PopupLayout layoutGravityRight{
0210         .anchorRect = QRect(50, 50, 400, 400),
0211         .size = QSize(200, 200),
0212         .anchor = Test::XdgPositioner::anchor_bottom_right,
0213         .gravity = Test::XdgPositioner::gravity_right,
0214     };
0215     QTest::newRow("gravityRight") << QSize(500, 500) << QPoint(300, 300) << layoutGravityRight << QRect(750, 650, 200, 200);
0216 
0217     const PopupLayout layoutGravityBottomRight{
0218         .anchorRect = QRect(50, 50, 400, 400),
0219         .size = QSize(200, 200),
0220         .anchor = Test::XdgPositioner::anchor_bottom_right,
0221         .gravity = Test::XdgPositioner::gravity_bottom_right,
0222     };
0223     QTest::newRow("gravityBottomRight") << QSize(500, 500) << QPoint(300, 300) << layoutGravityBottomRight << QRect(750, 750, 200, 200);
0224 
0225     const PopupLayout layoutGravityBottom{
0226         .anchorRect = QRect(50, 50, 400, 400),
0227         .size = QSize(200, 200),
0228         .anchor = Test::XdgPositioner::anchor_bottom_right,
0229         .gravity = Test::XdgPositioner::gravity_bottom,
0230     };
0231     QTest::newRow("gravityBottom") << QSize(500, 500) << QPoint(300, 300) << layoutGravityBottom << QRect(650, 750, 200, 200);
0232 
0233     const PopupLayout layoutGravityBottomLeft{
0234         .anchorRect = QRect(50, 50, 400, 400),
0235         .size = QSize(200, 200),
0236         .anchor = Test::XdgPositioner::anchor_bottom_right,
0237         .gravity = Test::XdgPositioner::gravity_bottom_left,
0238     };
0239     QTest::newRow("gravityBottomLeft") << QSize(500, 500) << QPoint(300, 300) << layoutGravityBottomLeft << QRect(550, 750, 200, 200);
0240 
0241     const PopupLayout layoutGravityLeft{
0242         .anchorRect = QRect(50, 50, 400, 400),
0243         .size = QSize(200, 200),
0244         .anchor = Test::XdgPositioner::anchor_bottom_right,
0245         .gravity = Test::XdgPositioner::gravity_left,
0246     };
0247     QTest::newRow("gravityLeft") << QSize(500, 500) << QPoint(300, 300) << layoutGravityLeft << QRect(550, 650, 200, 200);
0248 
0249     // ----------------------------------------------------------------
0250     // constrain and slide
0251     // popup is still 200,200. window moved near edge of screen, popup always comes out towards the screen edge
0252 
0253     const PopupLayout layoutSlideTop{
0254         .anchorRect = QRect(50, 50, 400, 400),
0255         .size = QSize(200, 200),
0256         .anchor = Test::XdgPositioner::anchor_top,
0257         .gravity = Test::XdgPositioner::gravity_top,
0258         .constraint = Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y,
0259     };
0260     QTest::newRow("constraintSlideTop") << QSize(500, 500) << QPoint(80, 80) << layoutSlideTop << QRect(80 + 250 - 100, 0, 200, 200);
0261 
0262     const PopupLayout layoutSlideLeft{
0263         .anchorRect = QRect(50, 50, 400, 400),
0264         .size = QSize(200, 200),
0265         .anchor = Test::XdgPositioner::anchor_left,
0266         .gravity = Test::XdgPositioner::gravity_left,
0267         .constraint = Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y,
0268     };
0269     QTest::newRow("constraintSlideLeft") << QSize(500, 500) << QPoint(80, 80) << layoutSlideLeft << QRect(0, 80 + 250 - 100, 200, 200);
0270 
0271     const PopupLayout layoutSlideRight{
0272         .anchorRect = QRect(50, 50, 400, 400),
0273         .size = QSize(200, 200),
0274         .anchor = Test::XdgPositioner::anchor_right,
0275         .gravity = Test::XdgPositioner::gravity_right,
0276         .constraint = Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y,
0277     };
0278     QTest::newRow("constraintSlideRight") << QSize(500, 500) << QPoint(700, 80) << layoutSlideRight << QRect(1280 - 200, 80 + 250 - 100, 200, 200);
0279 
0280     const PopupLayout layoutSlideBottom{
0281         .anchorRect = QRect(50, 50, 400, 400),
0282         .size = QSize(200, 200),
0283         .anchor = Test::XdgPositioner::anchor_bottom,
0284         .gravity = Test::XdgPositioner::gravity_bottom,
0285         .constraint = Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y,
0286     };
0287     QTest::newRow("constraintSlideBottom") << QSize(500, 500) << QPoint(80, 500) << layoutSlideBottom << QRect(80 + 250 - 100, 1024 - 200, 200, 200);
0288 
0289     const PopupLayout layoutSlideBottomRight{
0290         .anchorRect = QRect(50, 50, 400, 400),
0291         .size = QSize(200, 200),
0292         .anchor = Test::XdgPositioner::anchor_bottom_right,
0293         .gravity = Test::XdgPositioner::gravity_bottom_right,
0294         .constraint = Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y,
0295     };
0296     QTest::newRow("constraintSlideBottomRight") << QSize(500, 500) << QPoint(700, 1000) << layoutSlideBottomRight << QRect(1280 - 200, 1024 - 200, 200, 200);
0297 
0298     // ----------------------------------------------------------------
0299     // constrain and flip
0300 
0301     const PopupLayout layoutFlipTop{
0302         .anchorRect = QRect(50, 50, 400, 400),
0303         .size = QSize(200, 200),
0304         .anchor = Test::XdgPositioner::anchor_top,
0305         .gravity = Test::XdgPositioner::gravity_top,
0306         .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
0307     };
0308     QTest::newRow("constraintFlipTop") << QSize(500, 500) << QPoint(80, 80) << layoutFlipTop << QRect(230, 80 + 500 - 50, 200, 200);
0309 
0310     const PopupLayout layoutFlipLeft{
0311         .anchorRect = QRect(50, 50, 400, 400),
0312         .size = QSize(200, 200),
0313         .anchor = Test::XdgPositioner::anchor_left,
0314         .gravity = Test::XdgPositioner::gravity_left,
0315         .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
0316     };
0317     QTest::newRow("constraintFlipLeft") << QSize(500, 500) << QPoint(80, 80) << layoutFlipLeft << QRect(80 + 500 - 50, 230, 200, 200);
0318 
0319     const PopupLayout layoutFlipRight{
0320         .anchorRect = QRect(50, 50, 400, 400),
0321         .size = QSize(200, 200),
0322         .anchor = Test::XdgPositioner::anchor_right,
0323         .gravity = Test::XdgPositioner::gravity_right,
0324         .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
0325     };
0326     QTest::newRow("constraintFlipRight") << QSize(500, 500) << QPoint(700, 80) << layoutFlipRight << QRect(700 + 50 - 200, 230, 200, 200);
0327 
0328     const PopupLayout layoutFlipBottom{
0329         .anchorRect = QRect(50, 50, 400, 400),
0330         .size = QSize(200, 200),
0331         .anchor = Test::XdgPositioner::anchor_bottom,
0332         .gravity = Test::XdgPositioner::gravity_bottom,
0333         .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
0334     };
0335     QTest::newRow("constraintFlipBottom") << QSize(500, 500) << QPoint(80, 500) << layoutFlipBottom << QRect(230, 500 + 50 - 200, 200, 200);
0336 
0337     const PopupLayout layoutFlipBottomRight{
0338         .anchorRect = QRect(50, 50, 400, 400),
0339         .size = QSize(200, 200),
0340         .anchor = Test::XdgPositioner::anchor_bottom_right,
0341         .gravity = Test::XdgPositioner::gravity_bottom_right,
0342         .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
0343     };
0344     QTest::newRow("constraintFlipBottomRight") << QSize(500, 500) << QPoint(700, 500) << layoutFlipBottomRight << QRect(700 + 50 - 200, 500 + 50 - 200, 200, 200);
0345 
0346     const PopupLayout layoutFlipRightNoAnchor{
0347         .anchorRect = QRect(50, 50, 400, 400),
0348         // as popup is positioned in the middle of the parent we need a massive popup to be able to overflow
0349         .size = QSize(400, 400),
0350         .anchor = Test::XdgPositioner::anchor_top,
0351         .gravity = Test::XdgPositioner::gravity_right,
0352         .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
0353     };
0354     QTest::newRow("constraintFlipRightNoAnchor") << QSize(500, 500) << QPoint(700, 80) << layoutFlipRightNoAnchor << QRect(700 + 250 - 400, 330, 400, 400);
0355 
0356     const PopupLayout layoutFlipRightNoGravity{
0357         .anchorRect = QRect(50, 50, 400, 400),
0358         .size = QSize(300, 200),
0359         .anchor = Test::XdgPositioner::anchor_right,
0360         .gravity = Test::XdgPositioner::gravity_top,
0361         .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
0362     };
0363     QTest::newRow("constraintFlipRightNoGravity") << QSize(500, 500) << QPoint(700, 80) << layoutFlipRightNoGravity << QRect(700 + 50 - 150, 130, 300, 200);
0364 
0365     // ----------------------------------------------------------------
0366     // resize
0367 
0368     const PopupLayout layoutResizeTop{
0369         .anchorRect = QRect(50, 50, 400, 400),
0370         .size = QSize(200, 200),
0371         .anchor = Test::XdgPositioner::anchor_top,
0372         .gravity = Test::XdgPositioner::gravity_top,
0373         .constraint = Test::XdgPositioner::constraint_adjustment_resize_x | Test::XdgPositioner::constraint_adjustment_resize_y,
0374     };
0375     QTest::newRow("resizeTop") << QSize(500, 500) << QPoint(80, 80) << layoutResizeTop << QRect(80 + 250 - 100, 0, 200, 130);
0376 
0377     const PopupLayout layoutResizeLeft{
0378         .anchorRect = QRect(50, 50, 400, 400),
0379         .size = QSize(200, 200),
0380         .anchor = Test::XdgPositioner::anchor_left,
0381         .gravity = Test::XdgPositioner::gravity_left,
0382         .constraint = Test::XdgPositioner::constraint_adjustment_resize_x | Test::XdgPositioner::constraint_adjustment_resize_y,
0383     };
0384     QTest::newRow("resizeLeft") << QSize(500, 500) << QPoint(80, 80) << layoutResizeLeft << QRect(0, 80 + 250 - 100, 130, 200);
0385 
0386     const PopupLayout layoutResizeRight{
0387         .anchorRect = QRect(50, 50, 400, 400),
0388         .size = QSize(200, 200),
0389         .anchor = Test::XdgPositioner::anchor_right,
0390         .gravity = Test::XdgPositioner::gravity_right,
0391         .constraint = Test::XdgPositioner::constraint_adjustment_resize_x | Test::XdgPositioner::constraint_adjustment_resize_y,
0392     };
0393     QTest::newRow("resizeRight") << QSize(500, 500) << QPoint(700, 80) << layoutResizeRight << QRect(700 + 50 + 400, 80 + 250 - 100, 130, 200);
0394 
0395     const PopupLayout layoutResizeBottom{
0396         .anchorRect = QRect(50, 50, 400, 400),
0397         .size = QSize(200, 200),
0398         .anchor = Test::XdgPositioner::anchor_bottom,
0399         .gravity = Test::XdgPositioner::gravity_bottom,
0400         .constraint = Test::XdgPositioner::constraint_adjustment_resize_x | Test::XdgPositioner::constraint_adjustment_resize_y,
0401     };
0402     QTest::newRow("resizeBottom") << QSize(500, 500) << QPoint(80, 500) << layoutResizeBottom << QRect(80 + 250 - 100, 500 + 50 + 400, 200, 74);
0403 }
0404 
0405 void TransientPlacementTest::testXdgPopup()
0406 {
0407     // this test verifies that the position of a transient window is taken from the passed position
0408     // there are no further constraints like window too large to fit screen, cascading transients, etc
0409     // some test cases also verify that the transient fits on the screen
0410     QFETCH(QSize, parentSize);
0411     QFETCH(QPoint, parentPosition);
0412     QFETCH(QRect, expectedGeometry);
0413     const QRect expectedRelativeGeometry = expectedGeometry.translated(-parentPosition);
0414 
0415     std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
0416     QVERIFY(surface);
0417     auto parentShellSurface = Test::createXdgToplevelSurface(surface.get(), Test::waylandCompositor());
0418     QVERIFY(parentShellSurface);
0419     auto parent = Test::renderAndWaitForShown(surface.get(), parentSize, Qt::blue);
0420     QVERIFY(parent);
0421 
0422     QVERIFY(!parent->isDecorated());
0423     parent->move(parentPosition);
0424     QCOMPARE(parent->frameGeometry(), QRect(parentPosition, parentSize));
0425 
0426     // create popup
0427     QFETCH(PopupLayout, layout);
0428 
0429     std::unique_ptr<KWayland::Client::Surface> transientSurface = Test::createSurface();
0430     QVERIFY(transientSurface);
0431 
0432     std::unique_ptr<Test::XdgPositioner> positioner(Test::createXdgPositioner());
0433     positioner->set_anchor_rect(layout.anchorRect.x(), layout.anchorRect.y(), layout.anchorRect.width(), layout.anchorRect.height());
0434     positioner->set_size(layout.size.width(), layout.size.height());
0435     positioner->set_anchor(layout.anchor);
0436     positioner->set_gravity(layout.gravity);
0437     positioner->set_constraint_adjustment(layout.constraint);
0438     std::unique_ptr<Test::XdgPopup> popup(Test::createXdgPopupSurface(transientSurface.get(), parentShellSurface->xdgSurface(), positioner.get(), Test::CreationSetup::CreateOnly));
0439     QSignalSpy popupConfigureRequestedSpy(popup.get(), &Test::XdgPopup::configureRequested);
0440     QSignalSpy surfaceConfigureRequestedSpy(popup->xdgSurface(), &Test::XdgSurface::configureRequested);
0441     transientSurface->commit(KWayland::Client::Surface::CommitFlag::None);
0442 
0443     QVERIFY(surfaceConfigureRequestedSpy.wait());
0444     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
0445     QCOMPARE(popupConfigureRequestedSpy.last()[0].value<QRect>(), expectedRelativeGeometry);
0446     popup->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toUInt());
0447 
0448     auto transient = Test::renderAndWaitForShown(transientSurface.get(), expectedRelativeGeometry.size(), Qt::red);
0449     QVERIFY(transient);
0450 
0451     QVERIFY(!transient->isDecorated());
0452     QVERIFY(transient->hasTransientPlacementHint());
0453     QCOMPARE(transient->frameGeometry(), expectedGeometry);
0454 
0455     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1); // check that we did not get reconfigured
0456 }
0457 
0458 void TransientPlacementTest::testXdgPopupWithPanel()
0459 {
0460     const Output *output = workspace()->activeOutput();
0461 
0462     std::unique_ptr<KWayland::Client::Surface> surface{Test::createSurface()};
0463     QVERIFY(surface != nullptr);
0464     std::unique_ptr<Test::XdgToplevel> dockShellSurface{Test::createXdgToplevelSurface(surface.get())};
0465     QVERIFY(dockShellSurface != nullptr);
0466     std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(Test::waylandPlasmaShell()->createSurface(surface.get()));
0467     QVERIFY(plasmaSurface != nullptr);
0468     plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::Panel);
0469     plasmaSurface->setPosition(QPoint(0, output->geometry().height() - 50));
0470     plasmaSurface->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::AlwaysVisible);
0471 
0472     // now render and map the window
0473     auto dock = Test::renderAndWaitForShown(surface.get(), QSize(1280, 50), Qt::blue);
0474     QVERIFY(dock);
0475     QCOMPARE(dock->windowType(), NET::Dock);
0476     QVERIFY(dock->isDock());
0477     QCOMPARE(dock->frameGeometry(), QRect(0, output->geometry().height() - 50, 1280, 50));
0478     QCOMPARE(dock->hasStrut(), true);
0479     QCOMPARE(workspace()->clientArea(PlacementArea, dock), QRect(0, 0, 1280, 1024 - 50));
0480     QCOMPARE(workspace()->clientArea(FullScreenArea, dock), QRect(0, 0, 1280, 1024));
0481 
0482     // create parent
0483     std::unique_ptr<KWayland::Client::Surface> parentSurface(Test::createSurface());
0484     QVERIFY(parentSurface);
0485     auto parentShellSurface = Test::createXdgToplevelSurface(parentSurface.get());
0486     QVERIFY(parentShellSurface);
0487     auto parent = Test::renderAndWaitForShown(parentSurface.get(), {800, 600}, Qt::blue);
0488     QVERIFY(parent);
0489 
0490     QVERIFY(!parent->isDecorated());
0491     parent->move(QPointF(0, output->geometry().height() - 600));
0492     parent->keepInArea(workspace()->clientArea(PlacementArea, parent));
0493     QCOMPARE(parent->frameGeometry(), QRect(0, output->geometry().height() - 600 - 50, 800, 600));
0494 
0495     std::unique_ptr<KWayland::Client::Surface> transientSurface(Test::createSurface());
0496     QVERIFY(transientSurface);
0497 
0498     std::unique_ptr<Test::XdgPositioner> positioner(Test::createXdgPositioner());
0499     positioner->set_size(200, 200);
0500     positioner->set_anchor_rect(50, 500, 200, 200);
0501 
0502     std::unique_ptr<Test::XdgPopup> transientShellSurface(Test::createXdgPopupSurface(transientSurface.get(), parentShellSurface->xdgSurface(), positioner.get()));
0503     auto transient = Test::renderAndWaitForShown(transientSurface.get(), QSize(200, 200), Qt::red);
0504     QVERIFY(transient);
0505 
0506     QVERIFY(!transient->isDecorated());
0507     QVERIFY(transient->hasTransientPlacementHint());
0508 
0509     QCOMPARE(transient->frameGeometry(), QRect(50, output->geometry().height() - 200 - 50, 200, 200));
0510 
0511     transientShellSurface.reset();
0512     transientSurface.reset();
0513     QVERIFY(Test::waitForWindowDestroyed(transient));
0514 
0515     // now parent to fullscreen - on fullscreen the panel is ignored
0516     QSignalSpy toplevelConfigureRequestedSpy(parentShellSurface, &Test::XdgToplevel::configureRequested);
0517     QSignalSpy surfaceConfigureRequestedSpy(parentShellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0518     parent->setFullScreen(true);
0519     QVERIFY(surfaceConfigureRequestedSpy.wait());
0520     parentShellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0521     QSignalSpy frameGeometryChangedSpy{parent, &Window::frameGeometryChanged};
0522     Test::render(parentSurface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
0523     QVERIFY(frameGeometryChangedSpy.wait());
0524     QCOMPARE(parent->frameGeometry(), output->geometry());
0525     QVERIFY(parent->isFullScreen());
0526 
0527     // another transient, with same hints as before from bottom of window
0528     transientSurface = Test::createSurface();
0529     QVERIFY(transientSurface);
0530 
0531     const QRect anchorRect2(50, output->geometry().height() - 100, 200, 200);
0532     std::unique_ptr<Test::XdgPositioner> positioner2(Test::createXdgPositioner());
0533     positioner2->set_size(200, 200);
0534     positioner2->set_anchor_rect(anchorRect2.x(), anchorRect2.y(), anchorRect2.width(), anchorRect2.height());
0535     transientShellSurface.reset(Test::createXdgPopupSurface(transientSurface.get(), parentShellSurface->xdgSurface(), positioner2.get()));
0536     transient = Test::renderAndWaitForShown(transientSurface.get(), QSize(200, 200), Qt::red);
0537     QVERIFY(transient);
0538 
0539     QVERIFY(!transient->isDecorated());
0540     QVERIFY(transient->hasTransientPlacementHint());
0541 
0542     QCOMPARE(transient->frameGeometry(), QRect(50, output->geometry().height() - 200, 200, 200));
0543 }
0544 
0545 }
0546 
0547 WAYLANDTEST_MAIN(KWin::TransientPlacementTest)
0548 #include "transient_placement.moc"