File indexing completed on 2024-05-12 05:30:45

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