KWin
Loading...
Searching...
No Matches
transient_placement.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "kwin_wayland_test.h"
10
11#include "core/output.h"
12#include "pointer_input.h"
13#include "wayland/seat.h"
14#include "wayland/surface.h"
15#include "wayland_server.h"
16#include "window.h"
17#include "workspace.h"
18
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>
29
30#include <QSignalSpy>
31
33{
35 QSize size;
36 quint32 anchor = 0;
37 quint32 gravity = 0;
38 quint32 constraint = 0;
39};
41
42namespace KWin
43{
44
45static const QString s_socketName = QStringLiteral("wayland_test_kwin_transient_placement-0");
46
47class TransientPlacementTest : public QObject
48{
49 Q_OBJECT
50private Q_SLOTS:
51 void initTestCase();
52 void init();
53 void cleanup();
54 void testXdgPopup_data();
55 void testXdgPopup();
56 void testXdgPopupWithPanel();
57};
58
59void TransientPlacementTest::initTestCase()
60{
61 qRegisterMetaType<KWin::Window *>();
62 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
63 QVERIFY(waylandServer()->init(s_socketName));
65 QRect(0, 0, 1280, 1024),
66 QRect(1280, 0, 1280, 1024),
67 });
68
69 kwinApp()->start();
70 QVERIFY(applicationStartedSpy.wait());
71 const auto outputs = workspace()->outputs();
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);
76}
77
78void TransientPlacementTest::init()
79{
81
82 workspace()->setActiveOutput(QPoint(640, 512));
83 input()->pointer()->warp(QPoint(640, 512));
84}
85
86void TransientPlacementTest::cleanup()
87{
89}
90
91void TransientPlacementTest::testXdgPopup_data()
92{
93 QTest::addColumn<QSize>("parentSize");
94 QTest::addColumn<QPoint>("parentPosition");
95 QTest::addColumn<PopupLayout>("layout");
96 QTest::addColumn<QRect>("expectedGeometry");
97
98 // parent window is 500,500, starting at 300,300, anchorRect is therefore between 350->750 in both dirs
99
100 // ----------------------------------------------------------------
101 // window in the middle, plenty of room either side: Changing anchor
102
103 const PopupLayout layoutNone{
104 .anchorRect = QRect(50, 50, 400, 400),
105 .size = QSize(200, 200),
106 .anchor = Test::XdgPositioner::anchor_top_left,
107 .gravity = Test::XdgPositioner::gravity_top_left,
108 };
109 QTest::newRow("no constraint adjustments") << QSize(500, 500) << QPoint(0, 0) << layoutNone << QRect(50 - 200, 50 - 200, 200, 200);
110
111 const PopupLayout layoutAnchorCenter{
112 .anchorRect = QRect(50, 50, 400, 400),
113 .size = QSize(200, 200),
114 .anchor = Test::XdgPositioner::anchor_none,
115 .gravity = Test::XdgPositioner::gravity_bottom_right,
116 };
117 QTest::newRow("anchorCentre") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorCenter << QRect(550, 550, 200, 200);
118
119 const PopupLayout layoutAnchorTopLeft{
120 .anchorRect = QRect(50, 50, 400, 400),
121 .size = QSize(200, 200),
122 .anchor = Test::XdgPositioner::anchor_top_left,
123 .gravity = Test::XdgPositioner::gravity_bottom_right,
124 };
125 QTest::newRow("anchorTopLeft") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorTopLeft << QRect(350, 350, 200, 200);
126
127 const PopupLayout layoutAnchorTop{
128 .anchorRect = QRect(50, 50, 400, 400),
129 .size = QSize(200, 200),
130 .anchor = Test::XdgPositioner::anchor_top,
131 .gravity = Test::XdgPositioner::gravity_bottom_right,
132 };
133 QTest::newRow("anchorTop") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorTop << QRect(550, 350, 200, 200);
134
135 const PopupLayout layoutAnchorTopRight{
136 .anchorRect = QRect(50, 50, 400, 400),
137 .size = QSize(200, 200),
138 .anchor = Test::XdgPositioner::anchor_top_right,
139 .gravity = Test::XdgPositioner::gravity_bottom_right,
140 };
141 QTest::newRow("anchorTopRight") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorTopRight << QRect(750, 350, 200, 200);
142
143 const PopupLayout layoutAnchorRight{
144 .anchorRect = QRect(50, 50, 400, 400),
145 .size = QSize(200, 200),
146 .anchor = Test::XdgPositioner::anchor_right,
147 .gravity = Test::XdgPositioner::gravity_bottom_right,
148 };
149 QTest::newRow("anchorRight") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorRight << QRect(750, 550, 200, 200);
150
151 const PopupLayout layoutAnchorBottomRight{
152 .anchorRect = QRect(50, 50, 400, 400),
153 .size = QSize(200, 200),
154 .anchor = Test::XdgPositioner::anchor_bottom_right,
155 .gravity = Test::XdgPositioner::gravity_bottom_right,
156 };
157 QTest::newRow("anchorBottomRight") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorBottomRight << QRect(750, 750, 200, 200);
158
159 const PopupLayout layoutAnchorBottom{
160 .anchorRect = QRect(50, 50, 400, 400),
161 .size = QSize(200, 200),
162 .anchor = Test::XdgPositioner::anchor_bottom,
163 .gravity = Test::XdgPositioner::gravity_bottom_right,
164 };
165 QTest::newRow("anchorBottom") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorBottom << QRect(550, 750, 200, 200);
166
167 const PopupLayout layoutAnchorBottomLeft{
168 .anchorRect = QRect(50, 50, 400, 400),
169 .size = QSize(200, 200),
170 .anchor = Test::XdgPositioner::anchor_bottom_left,
171 .gravity = Test::XdgPositioner::gravity_bottom_right,
172 };
173 QTest::newRow("anchorBottomLeft") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorBottomLeft << QRect(350, 750, 200, 200);
174
175 const PopupLayout layoutAnchorLeft{
176 .anchorRect = QRect(50, 50, 400, 400),
177 .size = QSize(200, 200),
178 .anchor = Test::XdgPositioner::anchor_left,
179 .gravity = Test::XdgPositioner::gravity_bottom_right,
180 };
181 QTest::newRow("anchorLeft") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorLeft << QRect(350, 550, 200, 200);
182
183 // ----------------------------------------------------------------
184 // window in the middle, plenty of room either side: Changing gravity around the bottom right anchor
185
186 const PopupLayout layoutGravityCenter{
187 .anchorRect = QRect(50, 50, 400, 400),
188 .size = QSize(200, 200),
189 .anchor = Test::XdgPositioner::anchor_bottom_right,
190 .gravity = Test::XdgPositioner::gravity_none,
191 };
192 QTest::newRow("gravityCentre") << QSize(500, 500) << QPoint(300, 300) << layoutGravityCenter << QRect(650, 650, 200, 200);
193
194 const PopupLayout layoutGravityTopLeft{
195 .anchorRect = QRect(50, 50, 400, 400),
196 .size = QSize(200, 200),
197 .anchor = Test::XdgPositioner::anchor_bottom_right,
198 .gravity = Test::XdgPositioner::gravity_top_left,
199 };
200 QTest::newRow("gravityTopLeft") << QSize(500, 500) << QPoint(300, 300) << layoutGravityTopLeft << QRect(550, 550, 200, 200);
201
202 const PopupLayout layoutGravityTop{
203 .anchorRect = QRect(50, 50, 400, 400),
204 .size = QSize(200, 200),
205 .anchor = Test::XdgPositioner::anchor_bottom_right,
206 .gravity = Test::XdgPositioner::gravity_top,
207 };
208 QTest::newRow("gravityTop") << QSize(500, 500) << QPoint(300, 300) << layoutGravityTop << QRect(650, 550, 200, 200);
209
210 const PopupLayout layoutGravityTopRight{
211 .anchorRect = QRect(50, 50, 400, 400),
212 .size = QSize(200, 200),
213 .anchor = Test::XdgPositioner::anchor_bottom_right,
214 .gravity = Test::XdgPositioner::gravity_top_right,
215 };
216 QTest::newRow("gravityTopRight") << QSize(500, 500) << QPoint(300, 300) << layoutGravityTopRight << QRect(750, 550, 200, 200);
217
218 const PopupLayout layoutGravityRight{
219 .anchorRect = QRect(50, 50, 400, 400),
220 .size = QSize(200, 200),
221 .anchor = Test::XdgPositioner::anchor_bottom_right,
222 .gravity = Test::XdgPositioner::gravity_right,
223 };
224 QTest::newRow("gravityRight") << QSize(500, 500) << QPoint(300, 300) << layoutGravityRight << QRect(750, 650, 200, 200);
225
226 const PopupLayout layoutGravityBottomRight{
227 .anchorRect = QRect(50, 50, 400, 400),
228 .size = QSize(200, 200),
229 .anchor = Test::XdgPositioner::anchor_bottom_right,
230 .gravity = Test::XdgPositioner::gravity_bottom_right,
231 };
232 QTest::newRow("gravityBottomRight") << QSize(500, 500) << QPoint(300, 300) << layoutGravityBottomRight << QRect(750, 750, 200, 200);
233
234 const PopupLayout layoutGravityBottom{
235 .anchorRect = QRect(50, 50, 400, 400),
236 .size = QSize(200, 200),
237 .anchor = Test::XdgPositioner::anchor_bottom_right,
238 .gravity = Test::XdgPositioner::gravity_bottom,
239 };
240 QTest::newRow("gravityBottom") << QSize(500, 500) << QPoint(300, 300) << layoutGravityBottom << QRect(650, 750, 200, 200);
241
242 const PopupLayout layoutGravityBottomLeft{
243 .anchorRect = QRect(50, 50, 400, 400),
244 .size = QSize(200, 200),
245 .anchor = Test::XdgPositioner::anchor_bottom_right,
246 .gravity = Test::XdgPositioner::gravity_bottom_left,
247 };
248 QTest::newRow("gravityBottomLeft") << QSize(500, 500) << QPoint(300, 300) << layoutGravityBottomLeft << QRect(550, 750, 200, 200);
249
250 const PopupLayout layoutGravityLeft{
251 .anchorRect = QRect(50, 50, 400, 400),
252 .size = QSize(200, 200),
253 .anchor = Test::XdgPositioner::anchor_bottom_right,
254 .gravity = Test::XdgPositioner::gravity_left,
255 };
256 QTest::newRow("gravityLeft") << QSize(500, 500) << QPoint(300, 300) << layoutGravityLeft << QRect(550, 650, 200, 200);
257
258 // ----------------------------------------------------------------
259 // constrain and slide
260 // popup is still 200,200. window moved near edge of screen, popup always comes out towards the screen edge
261
262 const PopupLayout layoutSlideTop{
263 .anchorRect = QRect(50, 50, 400, 400),
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,
268 };
269 QTest::newRow("constraintSlideTop") << QSize(500, 500) << QPoint(80, 80) << layoutSlideTop << QRect(80 + 250 - 100, 0, 200, 200);
270
271 const PopupLayout layoutSlideLeft{
272 .anchorRect = QRect(50, 50, 400, 400),
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,
277 };
278 QTest::newRow("constraintSlideLeft") << QSize(500, 500) << QPoint(80, 80) << layoutSlideLeft << QRect(0, 80 + 250 - 100, 200, 200);
279
280 const PopupLayout layoutSlideRight{
281 .anchorRect = QRect(50, 50, 400, 400),
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,
286 };
287 QTest::newRow("constraintSlideRight") << QSize(500, 500) << QPoint(700, 80) << layoutSlideRight << QRect(1280 - 200, 80 + 250 - 100, 200, 200);
288
289 const PopupLayout layoutSlideBottom{
290 .anchorRect = QRect(50, 50, 400, 400),
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,
295 };
296 QTest::newRow("constraintSlideBottom") << QSize(500, 500) << QPoint(80, 500) << layoutSlideBottom << QRect(80 + 250 - 100, 1024 - 200, 200, 200);
297
298 const PopupLayout layoutSlideBottomRight{
299 .anchorRect = QRect(50, 50, 400, 400),
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,
304 };
305 QTest::newRow("constraintSlideBottomRight") << QSize(500, 500) << QPoint(700, 1000) << layoutSlideBottomRight << QRect(1280 - 200, 1024 - 200, 200, 200);
306
307 // ----------------------------------------------------------------
308 // constrain and flip
309
310 const PopupLayout layoutFlipTop{
311 .anchorRect = QRect(50, 50, 400, 400),
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,
316 };
317 QTest::newRow("constraintFlipTop") << QSize(500, 500) << QPoint(80, 80) << layoutFlipTop << QRect(230, 80 + 500 - 50, 200, 200);
318
319 const PopupLayout layoutFlipLeft{
320 .anchorRect = QRect(50, 50, 400, 400),
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,
325 };
326 QTest::newRow("constraintFlipLeft") << QSize(500, 500) << QPoint(80, 80) << layoutFlipLeft << QRect(80 + 500 - 50, 230, 200, 200);
327
328 const PopupLayout layoutFlipRight{
329 .anchorRect = QRect(50, 50, 400, 400),
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,
334 };
335 QTest::newRow("constraintFlipRight") << QSize(500, 500) << QPoint(700, 80) << layoutFlipRight << QRect(700 + 50 - 200, 230, 200, 200);
336
337 const PopupLayout layoutFlipBottom{
338 .anchorRect = QRect(50, 50, 400, 400),
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,
343 };
344 QTest::newRow("constraintFlipBottom") << QSize(500, 500) << QPoint(80, 500) << layoutFlipBottom << QRect(230, 500 + 50 - 200, 200, 200);
345
346 const PopupLayout layoutFlipBottomRight{
347 .anchorRect = QRect(50, 50, 400, 400),
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,
352 };
353 QTest::newRow("constraintFlipBottomRight") << QSize(500, 500) << QPoint(700, 500) << layoutFlipBottomRight << QRect(700 + 50 - 200, 500 + 50 - 200, 200, 200);
354
355 const PopupLayout layoutFlipRightNoAnchor{
356 .anchorRect = QRect(50, 50, 400, 400),
357 // as popup is positioned in the middle of the parent we need a massive popup to be able to overflow
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,
362 };
363 QTest::newRow("constraintFlipRightNoAnchor") << QSize(500, 500) << QPoint(700, 80) << layoutFlipRightNoAnchor << QRect(700 + 250 - 400, 330, 400, 400);
364
365 const PopupLayout layoutFlipRightNoGravity{
366 .anchorRect = QRect(50, 50, 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,
371 };
372 QTest::newRow("constraintFlipRightNoGravity") << QSize(500, 500) << QPoint(700, 80) << layoutFlipRightNoGravity << QRect(700 + 50 - 150, 130, 300, 200);
373
374 // ----------------------------------------------------------------
375 // resize
376
377 const PopupLayout layoutResizeTop{
378 .anchorRect = QRect(50, 50, 400, 400),
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,
383 };
384 QTest::newRow("resizeTop") << QSize(500, 500) << QPoint(80, 80) << layoutResizeTop << QRect(80 + 250 - 100, 0, 200, 130);
385
386 const PopupLayout layoutResizeLeft{
387 .anchorRect = QRect(50, 50, 400, 400),
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,
392 };
393 QTest::newRow("resizeLeft") << QSize(500, 500) << QPoint(80, 80) << layoutResizeLeft << QRect(0, 80 + 250 - 100, 130, 200);
394
395 const PopupLayout layoutResizeRight{
396 .anchorRect = QRect(50, 50, 400, 400),
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,
401 };
402 QTest::newRow("resizeRight") << QSize(500, 500) << QPoint(700, 80) << layoutResizeRight << QRect(700 + 50 + 400, 80 + 250 - 100, 130, 200);
403
404 const PopupLayout layoutResizeBottom{
405 .anchorRect = QRect(50, 50, 400, 400),
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,
410 };
411 QTest::newRow("resizeBottom") << QSize(500, 500) << QPoint(80, 500) << layoutResizeBottom << QRect(80 + 250 - 100, 500 + 50 + 400, 200, 74);
412}
413
414void TransientPlacementTest::testXdgPopup()
415{
416 // this test verifies that the position of a transient window is taken from the passed position
417 // there are no further constraints like window too large to fit screen, cascading transients, etc
418 // some test cases also verify that the transient fits on the screen
419 QFETCH(QSize, parentSize);
420 QFETCH(QPoint, parentPosition);
421 QFETCH(QRect, expectedGeometry);
422 const QRect expectedRelativeGeometry = expectedGeometry.translated(-parentPosition);
423
424 std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
425 QVERIFY(surface);
426 auto parentShellSurface = Test::createXdgToplevelSurface(surface.get(), Test::waylandCompositor());
427 QVERIFY(parentShellSurface);
428 auto parent = Test::renderAndWaitForShown(surface.get(), parentSize, Qt::blue);
429 QVERIFY(parent);
430
431 QVERIFY(!parent->isDecorated());
432 parent->move(parentPosition);
433 QCOMPARE(parent->frameGeometry(), QRect(parentPosition, parentSize));
434
435 // create popup
436 QFETCH(PopupLayout, layout);
437
438 std::unique_ptr<KWayland::Client::Surface> transientSurface = Test::createSurface();
439 QVERIFY(transientSurface);
440
441 std::unique_ptr<Test::XdgPositioner> positioner(Test::createXdgPositioner());
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);
447 std::unique_ptr<Test::XdgPopup> popup(Test::createXdgPopupSurface(transientSurface.get(), parentShellSurface->xdgSurface(), positioner.get(), Test::CreationSetup::CreateOnly));
448 QSignalSpy popupConfigureRequestedSpy(popup.get(), &Test::XdgPopup::configureRequested);
449 QSignalSpy surfaceConfigureRequestedSpy(popup->xdgSurface(), &Test::XdgSurface::configureRequested);
450 transientSurface->commit(KWayland::Client::Surface::CommitFlag::None);
451
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());
456
457 auto transient = Test::renderAndWaitForShown(transientSurface.get(), expectedRelativeGeometry.size(), Qt::red);
458 QVERIFY(transient);
459
460 QVERIFY(!transient->isDecorated());
461 QVERIFY(transient->hasTransientPlacementHint());
462 QCOMPARE(transient->frameGeometry(), expectedGeometry);
463
464 QCOMPARE(surfaceConfigureRequestedSpy.count(), 1); // check that we did not get reconfigured
465}
466
467void TransientPlacementTest::testXdgPopupWithPanel()
468{
469 const Output *output = workspace()->activeOutput();
470
471 std::unique_ptr<KWayland::Client::Surface> dockSurface{Test::createSurface()};
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);
477
478 // now render and map the window
479 QSignalSpy dockConfigureRequestedSpy(dockShellSurface.get(), &Test::LayerSurfaceV1::configureRequested);
480 QVERIFY(dockConfigureRequestedSpy.wait());
481 auto dock = Test::renderAndWaitForShown(dockSurface.get(), dockConfigureRequestedSpy.last().at(1).toSize(), Qt::blue);
482 QVERIFY(dock);
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);
487 QCOMPARE(workspace()->clientArea(PlacementArea, dock), QRect(0, 0, 1280, 1024 - 50));
488 QCOMPARE(workspace()->clientArea(FullScreenArea, dock), QRect(0, 0, 1280, 1024));
489
490 // create parent
491 std::unique_ptr<KWayland::Client::Surface> parentSurface(Test::createSurface());
492 QVERIFY(parentSurface);
493 auto parentShellSurface = Test::createXdgToplevelSurface(parentSurface.get());
494 QVERIFY(parentShellSurface);
495 auto parent = Test::renderAndWaitForShown(parentSurface.get(), {800, 600}, Qt::blue);
496 QVERIFY(parent);
497
498 QVERIFY(!parent->isDecorated());
499 parent->move(QPointF(0, output->geometry().height() - 600));
500 parent->keepInArea(workspace()->clientArea(PlacementArea, parent));
501 QCOMPARE(parent->frameGeometry(), QRect(0, output->geometry().height() - 600 - 50, 800, 600));
502
503 std::unique_ptr<KWayland::Client::Surface> transientSurface(Test::createSurface());
504 QVERIFY(transientSurface);
505
506 std::unique_ptr<Test::XdgPositioner> positioner(Test::createXdgPositioner());
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);
510
511 std::unique_ptr<Test::XdgPopup> transientShellSurface(Test::createXdgPopupSurface(transientSurface.get(), parentShellSurface->xdgSurface(), positioner.get()));
512 auto transient = Test::renderAndWaitForShown(transientSurface.get(), QSize(200, 200), Qt::red);
513 QVERIFY(transient);
514
515 QVERIFY(!transient->isDecorated());
516 QVERIFY(transient->hasTransientPlacementHint());
517
518 QCOMPARE(transient->frameGeometry(), QRect(50, output->geometry().height() - 200 - 50, 200, 200));
519
520 transientShellSurface.reset();
521 transientSurface.reset();
522 QVERIFY(Test::waitForWindowClosed(transient));
523
524 // now parent to fullscreen - on fullscreen the panel is ignored
525 QSignalSpy toplevelConfigureRequestedSpy(parentShellSurface, &Test::XdgToplevel::configureRequested);
526 QSignalSpy surfaceConfigureRequestedSpy(parentShellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
527 parent->setFullScreen(true);
528 QVERIFY(surfaceConfigureRequestedSpy.wait());
529 parentShellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
530 QSignalSpy frameGeometryChangedSpy{parent, &Window::frameGeometryChanged};
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());
535
536 // another transient, with same hints as before from bottom of window
537 transientSurface = Test::createSurface();
538 QVERIFY(transientSurface);
539
540 const QRect anchorRect2(50, output->geometry().height() - 100, 200, 200);
541 std::unique_ptr<Test::XdgPositioner> positioner2(Test::createXdgPositioner());
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()));
546 transient = Test::renderAndWaitForShown(transientSurface.get(), QSize(200, 200), Qt::red);
547 QVERIFY(transient);
548
549 QVERIFY(!transient->isDecorated());
550 QVERIFY(transient->hasTransientPlacementHint());
551
552 QCOMPARE(transient->frameGeometry(), QRect(50, output->geometry().height() - 200, 200, 200));
553}
554
555}
556
558#include "transient_placement.moc"
PointerInputRedirection * pointer() const
Definition input.h:220
void warp(const QPointF &pos)
void configureRequested(quint32 serial, const QSize &size)
void configureRequested(const QRect &rect)
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
Definition workspace.h:762
void setActiveOutput(Output *output)
Q_DECLARE_METATYPE(KWin::SwitchEvent::State)
#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)
@ FullScreenArea
Definition globals.h:53
@ PlacementArea
Definition globals.h:49
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
InputRedirection * input()
Definition input.h:549