19#include <KWayland/Client/compositor.h>
20#include <KWayland/Client/connection_thread.h>
21#include <KWayland/Client/event_queue.h>
22#include <KWayland/Client/keyboard.h>
23#include <KWayland/Client/pointer.h>
24#include <KWayland/Client/registry.h>
25#include <KWayland/Client/seat.h>
26#include <KWayland/Client/shm_pool.h>
27#include <KWayland/Client/surface.h>
28#include <KWayland/Client/touch.h>
45static const QString s_socketName = QStringLiteral(
"wayland_test_kwin_transient_placement-0");
54 void testXdgPopup_data();
56 void testXdgPopupWithPanel();
59void TransientPlacementTest::initTestCase()
61 qRegisterMetaType<KWin::Window *>();
65 QRect(0, 0, 1280, 1024),
66 QRect(1280, 0, 1280, 1024),
70 QVERIFY(applicationStartedSpy.wait());
72 QCOMPARE(outputs.count(), 2);
73 QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
74 QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
75 setenv(
"QT_QPA_PLATFORM",
"wayland",
true);
78void TransientPlacementTest::init()
86void TransientPlacementTest::cleanup()
91void TransientPlacementTest::testXdgPopup_data()
93 QTest::addColumn<QSize>(
"parentSize");
94 QTest::addColumn<QPoint>(
"parentPosition");
95 QTest::addColumn<PopupLayout>(
"layout");
96 QTest::addColumn<QRect>(
"expectedGeometry");
105 .size = QSize(200, 200),
106 .anchor = Test::XdgPositioner::anchor_top_left,
107 .gravity = Test::XdgPositioner::gravity_top_left,
109 QTest::newRow(
"no constraint adjustments") << QSize(500, 500) << QPoint(0, 0) << layoutNone << QRect(50 - 200, 50 - 200, 200, 200);
113 .size = QSize(200, 200),
114 .anchor = Test::XdgPositioner::anchor_none,
115 .gravity = Test::XdgPositioner::gravity_bottom_right,
117 QTest::newRow(
"anchorCentre") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorCenter << QRect(550, 550, 200, 200);
121 .size = QSize(200, 200),
122 .anchor = Test::XdgPositioner::anchor_top_left,
123 .gravity = Test::XdgPositioner::gravity_bottom_right,
125 QTest::newRow(
"anchorTopLeft") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorTopLeft << QRect(350, 350, 200, 200);
129 .size = QSize(200, 200),
130 .anchor = Test::XdgPositioner::anchor_top,
131 .gravity = Test::XdgPositioner::gravity_bottom_right,
133 QTest::newRow(
"anchorTop") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorTop << QRect(550, 350, 200, 200);
137 .size = QSize(200, 200),
138 .anchor = Test::XdgPositioner::anchor_top_right,
139 .gravity = Test::XdgPositioner::gravity_bottom_right,
141 QTest::newRow(
"anchorTopRight") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorTopRight << QRect(750, 350, 200, 200);
145 .size = QSize(200, 200),
146 .anchor = Test::XdgPositioner::anchor_right,
147 .gravity = Test::XdgPositioner::gravity_bottom_right,
149 QTest::newRow(
"anchorRight") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorRight << QRect(750, 550, 200, 200);
153 .size = QSize(200, 200),
154 .anchor = Test::XdgPositioner::anchor_bottom_right,
155 .gravity = Test::XdgPositioner::gravity_bottom_right,
157 QTest::newRow(
"anchorBottomRight") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorBottomRight << QRect(750, 750, 200, 200);
161 .size = QSize(200, 200),
162 .anchor = Test::XdgPositioner::anchor_bottom,
163 .gravity = Test::XdgPositioner::gravity_bottom_right,
165 QTest::newRow(
"anchorBottom") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorBottom << QRect(550, 750, 200, 200);
169 .size = QSize(200, 200),
170 .anchor = Test::XdgPositioner::anchor_bottom_left,
171 .gravity = Test::XdgPositioner::gravity_bottom_right,
173 QTest::newRow(
"anchorBottomLeft") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorBottomLeft << QRect(350, 750, 200, 200);
177 .size = QSize(200, 200),
178 .anchor = Test::XdgPositioner::anchor_left,
179 .gravity = Test::XdgPositioner::gravity_bottom_right,
181 QTest::newRow(
"anchorLeft") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorLeft << QRect(350, 550, 200, 200);
188 .size = QSize(200, 200),
189 .anchor = Test::XdgPositioner::anchor_bottom_right,
190 .gravity = Test::XdgPositioner::gravity_none,
192 QTest::newRow(
"gravityCentre") << QSize(500, 500) << QPoint(300, 300) << layoutGravityCenter << QRect(650, 650, 200, 200);
196 .size = QSize(200, 200),
197 .anchor = Test::XdgPositioner::anchor_bottom_right,
198 .gravity = Test::XdgPositioner::gravity_top_left,
200 QTest::newRow(
"gravityTopLeft") << QSize(500, 500) << QPoint(300, 300) << layoutGravityTopLeft << QRect(550, 550, 200, 200);
204 .size = QSize(200, 200),
205 .anchor = Test::XdgPositioner::anchor_bottom_right,
206 .gravity = Test::XdgPositioner::gravity_top,
208 QTest::newRow(
"gravityTop") << QSize(500, 500) << QPoint(300, 300) << layoutGravityTop << QRect(650, 550, 200, 200);
212 .size = QSize(200, 200),
213 .anchor = Test::XdgPositioner::anchor_bottom_right,
214 .gravity = Test::XdgPositioner::gravity_top_right,
216 QTest::newRow(
"gravityTopRight") << QSize(500, 500) << QPoint(300, 300) << layoutGravityTopRight << QRect(750, 550, 200, 200);
220 .size = QSize(200, 200),
221 .anchor = Test::XdgPositioner::anchor_bottom_right,
222 .gravity = Test::XdgPositioner::gravity_right,
224 QTest::newRow(
"gravityRight") << QSize(500, 500) << QPoint(300, 300) << layoutGravityRight << QRect(750, 650, 200, 200);
228 .size = QSize(200, 200),
229 .anchor = Test::XdgPositioner::anchor_bottom_right,
230 .gravity = Test::XdgPositioner::gravity_bottom_right,
232 QTest::newRow(
"gravityBottomRight") << QSize(500, 500) << QPoint(300, 300) << layoutGravityBottomRight << QRect(750, 750, 200, 200);
236 .size = QSize(200, 200),
237 .anchor = Test::XdgPositioner::anchor_bottom_right,
238 .gravity = Test::XdgPositioner::gravity_bottom,
240 QTest::newRow(
"gravityBottom") << QSize(500, 500) << QPoint(300, 300) << layoutGravityBottom << QRect(650, 750, 200, 200);
244 .size = QSize(200, 200),
245 .anchor = Test::XdgPositioner::anchor_bottom_right,
246 .gravity = Test::XdgPositioner::gravity_bottom_left,
248 QTest::newRow(
"gravityBottomLeft") << QSize(500, 500) << QPoint(300, 300) << layoutGravityBottomLeft << QRect(550, 750, 200, 200);
252 .size = QSize(200, 200),
253 .anchor = Test::XdgPositioner::anchor_bottom_right,
254 .gravity = Test::XdgPositioner::gravity_left,
256 QTest::newRow(
"gravityLeft") << QSize(500, 500) << QPoint(300, 300) << layoutGravityLeft << QRect(550, 650, 200, 200);
264 .size = QSize(200, 200),
265 .anchor = Test::XdgPositioner::anchor_top,
266 .gravity = Test::XdgPositioner::gravity_top,
267 .constraint = Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y,
269 QTest::newRow(
"constraintSlideTop") << QSize(500, 500) << QPoint(80, 80) << layoutSlideTop << QRect(80 + 250 - 100, 0, 200, 200);
273 .size = QSize(200, 200),
274 .anchor = Test::XdgPositioner::anchor_left,
275 .gravity = Test::XdgPositioner::gravity_left,
276 .constraint = Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y,
278 QTest::newRow(
"constraintSlideLeft") << QSize(500, 500) << QPoint(80, 80) << layoutSlideLeft << QRect(0, 80 + 250 - 100, 200, 200);
282 .size = QSize(200, 200),
283 .anchor = Test::XdgPositioner::anchor_right,
284 .gravity = Test::XdgPositioner::gravity_right,
285 .constraint = Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y,
287 QTest::newRow(
"constraintSlideRight") << QSize(500, 500) << QPoint(700, 80) << layoutSlideRight << QRect(1280 - 200, 80 + 250 - 100, 200, 200);
291 .size = QSize(200, 200),
292 .anchor = Test::XdgPositioner::anchor_bottom,
293 .gravity = Test::XdgPositioner::gravity_bottom,
294 .constraint = Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y,
296 QTest::newRow(
"constraintSlideBottom") << QSize(500, 500) << QPoint(80, 500) << layoutSlideBottom << QRect(80 + 250 - 100, 1024 - 200, 200, 200);
300 .size = QSize(200, 200),
301 .anchor = Test::XdgPositioner::anchor_bottom_right,
302 .gravity = Test::XdgPositioner::gravity_bottom_right,
303 .constraint = Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y,
305 QTest::newRow(
"constraintSlideBottomRight") << QSize(500, 500) << QPoint(700, 1000) << layoutSlideBottomRight << QRect(1280 - 200, 1024 - 200, 200, 200);
312 .size = QSize(200, 200),
313 .anchor = Test::XdgPositioner::anchor_top,
314 .gravity = Test::XdgPositioner::gravity_top,
315 .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
317 QTest::newRow(
"constraintFlipTop") << QSize(500, 500) << QPoint(80, 80) << layoutFlipTop << QRect(230, 80 + 500 - 50, 200, 200);
321 .size = QSize(200, 200),
322 .anchor = Test::XdgPositioner::anchor_left,
323 .gravity = Test::XdgPositioner::gravity_left,
324 .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
326 QTest::newRow(
"constraintFlipLeft") << QSize(500, 500) << QPoint(80, 80) << layoutFlipLeft << QRect(80 + 500 - 50, 230, 200, 200);
330 .size = QSize(200, 200),
331 .anchor = Test::XdgPositioner::anchor_right,
332 .gravity = Test::XdgPositioner::gravity_right,
333 .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
335 QTest::newRow(
"constraintFlipRight") << QSize(500, 500) << QPoint(700, 80) << layoutFlipRight << QRect(700 + 50 - 200, 230, 200, 200);
339 .size = QSize(200, 200),
340 .anchor = Test::XdgPositioner::anchor_bottom,
341 .gravity = Test::XdgPositioner::gravity_bottom,
342 .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
344 QTest::newRow(
"constraintFlipBottom") << QSize(500, 500) << QPoint(80, 500) << layoutFlipBottom << QRect(230, 500 + 50 - 200, 200, 200);
348 .size = QSize(200, 200),
349 .anchor = Test::XdgPositioner::anchor_bottom_right,
350 .gravity = Test::XdgPositioner::gravity_bottom_right,
351 .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
353 QTest::newRow(
"constraintFlipBottomRight") << QSize(500, 500) << QPoint(700, 500) << layoutFlipBottomRight << QRect(700 + 50 - 200, 500 + 50 - 200, 200, 200);
358 .size = QSize(400, 400),
359 .anchor = Test::XdgPositioner::anchor_top,
360 .gravity = Test::XdgPositioner::gravity_right,
361 .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
363 QTest::newRow(
"constraintFlipRightNoAnchor") << QSize(500, 500) << QPoint(700, 80) << layoutFlipRightNoAnchor << QRect(700 + 250 - 400, 330, 400, 400);
367 .size = QSize(300, 200),
368 .anchor = Test::XdgPositioner::anchor_right,
369 .gravity = Test::XdgPositioner::gravity_top,
370 .constraint = Test::XdgPositioner::constraint_adjustment_flip_x | Test::XdgPositioner::constraint_adjustment_flip_y,
372 QTest::newRow(
"constraintFlipRightNoGravity") << QSize(500, 500) << QPoint(700, 80) << layoutFlipRightNoGravity << QRect(700 + 50 - 150, 130, 300, 200);
379 .size = QSize(200, 200),
380 .anchor = Test::XdgPositioner::anchor_top,
381 .gravity = Test::XdgPositioner::gravity_top,
382 .constraint = Test::XdgPositioner::constraint_adjustment_resize_x | Test::XdgPositioner::constraint_adjustment_resize_y,
384 QTest::newRow(
"resizeTop") << QSize(500, 500) << QPoint(80, 80) << layoutResizeTop << QRect(80 + 250 - 100, 0, 200, 130);
388 .size = QSize(200, 200),
389 .anchor = Test::XdgPositioner::anchor_left,
390 .gravity = Test::XdgPositioner::gravity_left,
391 .constraint = Test::XdgPositioner::constraint_adjustment_resize_x | Test::XdgPositioner::constraint_adjustment_resize_y,
393 QTest::newRow(
"resizeLeft") << QSize(500, 500) << QPoint(80, 80) << layoutResizeLeft << QRect(0, 80 + 250 - 100, 130, 200);
397 .size = QSize(200, 200),
398 .anchor = Test::XdgPositioner::anchor_right,
399 .gravity = Test::XdgPositioner::gravity_right,
400 .constraint = Test::XdgPositioner::constraint_adjustment_resize_x | Test::XdgPositioner::constraint_adjustment_resize_y,
402 QTest::newRow(
"resizeRight") << QSize(500, 500) << QPoint(700, 80) << layoutResizeRight << QRect(700 + 50 + 400, 80 + 250 - 100, 130, 200);
406 .size = QSize(200, 200),
407 .anchor = Test::XdgPositioner::anchor_bottom,
408 .gravity = Test::XdgPositioner::gravity_bottom,
409 .constraint = Test::XdgPositioner::constraint_adjustment_resize_x | Test::XdgPositioner::constraint_adjustment_resize_y,
411 QTest::newRow(
"resizeBottom") << QSize(500, 500) << QPoint(80, 500) << layoutResizeBottom << QRect(80 + 250 - 100, 500 + 50 + 400, 200, 74);
414void TransientPlacementTest::testXdgPopup()
419 QFETCH(QSize, parentSize);
420 QFETCH(QPoint, parentPosition);
421 QFETCH(QRect, expectedGeometry);
422 const QRect expectedRelativeGeometry = expectedGeometry.translated(-parentPosition);
427 QVERIFY(parentShellSurface);
431 QVERIFY(!parent->isDecorated());
432 parent->move(parentPosition);
433 QCOMPARE(parent->frameGeometry(), QRect(parentPosition, parentSize));
439 QVERIFY(transientSurface);
442 positioner->set_anchor_rect(layout.anchorRect.x(), layout.anchorRect.y(), layout.anchorRect.width(), layout.anchorRect.height());
443 positioner->set_size(layout.size.width(), layout.size.height());
444 positioner->set_anchor(layout.anchor);
445 positioner->set_gravity(layout.gravity);
446 positioner->set_constraint_adjustment(layout.constraint);
450 transientSurface->commit(KWayland::Client::Surface::CommitFlag::None);
452 QVERIFY(surfaceConfigureRequestedSpy.wait());
453 QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
454 QCOMPARE(popupConfigureRequestedSpy.last()[0].value<QRect>(), expectedRelativeGeometry);
455 popup->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toUInt());
460 QVERIFY(!transient->isDecorated());
461 QVERIFY(transient->hasTransientPlacementHint());
462 QCOMPARE(transient->frameGeometry(), expectedGeometry);
464 QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
467void TransientPlacementTest::testXdgPopupWithPanel()
472 std::unique_ptr<Test::LayerSurfaceV1> dockShellSurface{
Test::createLayerSurfaceV1(dockSurface.get(), QStringLiteral(
"dock"))};
473 dockShellSurface->set_size(1280, 50);
474 dockShellSurface->set_anchor(Test::LayerSurfaceV1::anchor_bottom);
475 dockShellSurface->set_exclusive_zone(50);
476 dockSurface->commit(KWayland::Client::Surface::CommitFlag::None);
480 QVERIFY(dockConfigureRequestedSpy.wait());
483 QCOMPARE(dock->windowType(), NET::Dock);
484 QVERIFY(dock->isDock());
485 QCOMPARE(dock->frameGeometry(), QRect(0, output->geometry().height() - 50, 1280, 50));
486 QCOMPARE(dock->hasStrut(),
true);
492 QVERIFY(parentSurface);
494 QVERIFY(parentShellSurface);
498 QVERIFY(!parent->isDecorated());
499 parent->move(QPointF(0, output->geometry().height() - 600));
501 QCOMPARE(parent->frameGeometry(), QRect(0, output->geometry().height() - 600 - 50, 800, 600));
504 QVERIFY(transientSurface);
507 positioner->set_size(200, 200);
508 positioner->set_anchor_rect(50, 500, 200, 200);
509 positioner->set_constraint_adjustment(Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y);
511 std::unique_ptr<Test::XdgPopup> transientShellSurface(
Test::createXdgPopupSurface(transientSurface.get(), parentShellSurface->xdgSurface(), positioner.get()));
515 QVERIFY(!transient->isDecorated());
516 QVERIFY(transient->hasTransientPlacementHint());
518 QCOMPARE(transient->frameGeometry(), QRect(50, output->geometry().height() - 200 - 50, 200, 200));
520 transientShellSurface.reset();
521 transientSurface.reset();
527 parent->setFullScreen(
true);
528 QVERIFY(surfaceConfigureRequestedSpy.wait());
529 parentShellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
531 Test::render(parentSurface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
532 QVERIFY(frameGeometryChangedSpy.wait());
533 QCOMPARE(parent->frameGeometry(), output->geometry());
534 QVERIFY(parent->isFullScreen());
538 QVERIFY(transientSurface);
540 const QRect anchorRect2(50, output->geometry().height() - 100, 200, 200);
542 positioner2->set_size(200, 200);
543 positioner2->set_anchor_rect(anchorRect2.x(), anchorRect2.y(), anchorRect2.width(), anchorRect2.height());
544 positioner2->set_constraint_adjustment(Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y);
545 transientShellSurface.reset(
Test::createXdgPopupSurface(transientSurface.get(), parentShellSurface->xdgSurface(), positioner2.get()));
549 QVERIFY(!transient->isDecorated());
550 QVERIFY(transient->hasTransientPlacementHint());
552 QCOMPARE(transient->frameGeometry(), QRect(50, output->geometry().height() - 200, 200, 200));
558#include "transient_placement.moc"
void configureRequested(quint32 serial, const QSize &size)
void configureRequested(quint32 serial)
void configureRequested(const QSize &size, KWin::Test::XdgToplevel::States states)
void frameGeometryChanged(const QRectF &oldGeometry)
Output * activeOutput() const
QList< Output * > outputs() const
void setActiveOutput(Output *output)
#define WAYLANDTEST_MAIN(TestObject)
XdgPositioner * createXdgPositioner()
Window * renderAndWaitForShown(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32, int timeout=5000)
XdgPopup * createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface, XdgPositioner *positioner, CreationSetup configureMode=CreationSetup::CreateAndConfigure, QObject *parent=nullptr)
void destroyWaylandConnection()
void setOutputConfig(const QList< QRect > &geometries)
bool setupWaylandConnection(AdditionalWaylandInterfaces flags=AdditionalWaylandInterfaces())
KWayland::Client::Compositor * waylandCompositor()
void render(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32_Premultiplied)
LayerSurfaceV1 * createLayerSurfaceV1(KWayland::Client::Surface *surface, const QString &scope, KWayland::Client::Output *output=nullptr, LayerShellV1::layer layer=LayerShellV1::layer_top)
std::unique_ptr< KWayland::Client::Surface > createSurface()
XdgToplevel * createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent=nullptr)
bool waitForWindowClosed(Window *window)
WaylandServer * waylandServer()
InputRedirection * input()