KWin
Loading...
Searching...
No Matches
move_resize_window_test.cpp
Go to the documentation of this file.
1
2/*
3 KWin - the KDE window manager
4 This file is part of the KDE project.
5
6 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10#include "kwin_wayland_test.h"
11
12#include "atoms.h"
13#include "core/output.h"
14#include "cursor.h"
15#include "placement.h"
16#include "pointer_input.h"
17#include "wayland_server.h"
18#include "window.h"
19#include "workspace.h"
20#include "x11window.h"
21
22#include <KWayland/Client/compositor.h>
23#include <KWayland/Client/connection_thread.h>
24#include <KWayland/Client/pointer.h>
25#include <KWayland/Client/seat.h>
26#include <KWayland/Client/surface.h>
27
28#include <linux/input.h>
29#include <xcb/xcb_icccm.h>
30
31Q_DECLARE_METATYPE(KWin::QuickTileMode)
33
34namespace KWin
35{
36
37static const QString s_socketName = QStringLiteral("wayland_test_kwin_quick_tiling-0");
38
39class MoveResizeWindowTest : public QObject
40{
41 Q_OBJECT
42private Q_SLOTS:
43 void initTestCase();
44 void init();
45 void cleanup();
46 void testMove();
47 void testResize();
48 void testPackTo_data();
49 void testPackTo();
50 void testPackAgainstClient_data();
51 void testPackAgainstClient();
52 void testGrowShrink_data();
53 void testGrowShrink();
54 void testPointerMoveEnd_data();
55 void testPointerMoveEnd();
56 void testClientSideMove();
57 void testNetMove();
58 void testAdjustClientGeometryOfHiddenX11Panel_data();
59 void testAdjustClientGeometryOfHiddenX11Panel();
60 void testAdjustClientGeometryOfHiddenWaylandPanel_data();
61 void testAdjustClientGeometryOfHiddenWaylandPanel();
62 void testResizeForVirtualKeyboard_data();
63 void testResizeForVirtualKeyboard();
64 void testResizeForVirtualKeyboardWithMaximize();
65 void testResizeForVirtualKeyboardWithFullScreen();
66 void testDestroyMoveClient();
67 void testDestroyResizeClient();
68 void testCancelInteractiveMoveResize_data();
69 void testCancelInteractiveMoveResize();
70
71private:
72 KWayland::Client::ConnectionThread *m_connection = nullptr;
73 KWayland::Client::Compositor *m_compositor = nullptr;
74};
75
76void MoveResizeWindowTest::initTestCase()
77{
78 qRegisterMetaType<KWin::Window *>();
79 qRegisterMetaType<KWin::MaximizeMode>("MaximizeMode");
80 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
81 QVERIFY(waylandServer()->init(s_socketName));
82 Test::setOutputConfig({QRect(0, 0, 1280, 1024)});
83 kwinApp()->start();
84 QVERIFY(applicationStartedSpy.wait());
85 const auto outputs = workspace()->outputs();
86 QCOMPARE(outputs.count(), 1);
87 QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
88}
89
90void MoveResizeWindowTest::init()
91{
94 m_connection = Test::waylandConnection();
95 m_compositor = Test::waylandCompositor();
96
97 workspace()->setActiveOutput(QPoint(640, 512));
98}
99
100void MoveResizeWindowTest::cleanup()
101{
103}
104
105void MoveResizeWindowTest::testMove()
106{
107 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
108 QVERIFY(surface != nullptr);
109
110 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
111 QVERIFY(shellSurface != nullptr);
112 // let's render
113 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
114
115 QVERIFY(window);
116 QCOMPARE(workspace()->activeWindow(), window);
117 QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
118 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
119 QSignalSpy moveResizedChangedSpy(window, &Window::moveResizedChanged);
120 QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
121 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
122
123 // begin move
124 QVERIFY(workspace()->moveResizeWindow() == nullptr);
125 QCOMPARE(window->isInteractiveMove(), false);
127 QCOMPARE(workspace()->moveResizeWindow(), window);
128 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
129 QCOMPARE(moveResizedChangedSpy.count(), 1);
130 QCOMPARE(window->isInteractiveMove(), true);
131 QCOMPARE(window->geometryRestore(), QRect());
132
133 // send some key events, not going through input redirection
134 const QPointF cursorPos = Cursors::self()->mouse()->pos();
135 window->keyPressEvent(Qt::Key_Right);
136 window->updateInteractiveMoveResize(Cursors::self()->mouse()->pos());
137 QCOMPARE(Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
138 QEXPECT_FAIL("", "First event is ignored", Continue);
139 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
140 interactiveMoveResizeSteppedSpy.clear();
141
142 window->keyPressEvent(Qt::Key_Right);
143 window->updateInteractiveMoveResize(Cursors::self()->mouse()->pos());
144 QCOMPARE(Cursors::self()->mouse()->pos(), cursorPos + QPoint(16, 0));
145 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
146
147 window->keyPressEvent(Qt::Key_Down | Qt::ALT);
148 window->updateInteractiveMoveResize(Cursors::self()->mouse()->pos());
149 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
150 QCOMPARE(window->frameGeometry(), QRect(16, 32, 100, 50));
151 QCOMPARE(Cursors::self()->mouse()->pos(), cursorPos + QPoint(16, 32));
152
153 // let's end
154 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 0);
155 window->keyPressEvent(Qt::Key_Enter);
156 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
157 QCOMPARE(moveResizedChangedSpy.count(), 2);
158 QCOMPARE(window->frameGeometry(), QRect(16, 32, 100, 50));
159 QCOMPARE(window->isInteractiveMove(), false);
160 QVERIFY(workspace()->moveResizeWindow() == nullptr);
161 surface.reset();
162 QVERIFY(Test::waitForWindowClosed(window));
163}
164
165void MoveResizeWindowTest::testResize()
166{
167 // a test case which manually resizes a window
168
169 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
170 QVERIFY(surface != nullptr);
171
172 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
173 QVERIFY(shellSurface != nullptr);
174
175 // Wait for the initial configure event.
176 Test::XdgToplevel::States states;
177 QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
178 QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
179 surface->commit(KWayland::Client::Surface::CommitFlag::None);
180 QVERIFY(surfaceConfigureRequestedSpy.wait());
181 QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
182 QCOMPARE(toplevelConfigureRequestedSpy.count(), 1);
183 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
184 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
185 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing));
186
187 // Let's render.
188 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
189 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
190
191 // We have to receive a configure event when the client becomes active.
192 QVERIFY(surfaceConfigureRequestedSpy.wait());
193 QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
194 QCOMPARE(toplevelConfigureRequestedSpy.count(), 2);
195 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
196 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
197 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing));
198
199 QVERIFY(window);
200 QCOMPARE(workspace()->activeWindow(), window);
201 QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
202 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
203 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
204 QSignalSpy moveResizedChangedSpy(window, &Window::moveResizedChanged);
205 QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
206 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
207
208 // begin resize
209 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
210 QCOMPARE(window->isInteractiveMove(), false);
211 QCOMPARE(window->isInteractiveResize(), false);
213 QCOMPARE(workspace()->moveResizeWindow(), window);
214 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
215 QCOMPARE(moveResizedChangedSpy.count(), 1);
216 QCOMPARE(window->isInteractiveResize(), true);
217 QCOMPARE(window->geometryRestore(), QRect());
218 QVERIFY(surfaceConfigureRequestedSpy.wait());
219 QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
220 QCOMPARE(toplevelConfigureRequestedSpy.count(), 3);
221 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
222 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
223 QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
224
225 // Trigger a change.
226 const QPointF cursorPos = Cursors::self()->mouse()->pos();
227 window->keyPressEvent(Qt::Key_Right);
228 window->updateInteractiveMoveResize(Cursors::self()->mouse()->pos());
229 QCOMPARE(Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
230
231 // The client should receive a configure event with the new size.
232 QVERIFY(surfaceConfigureRequestedSpy.wait());
233 QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
234 QCOMPARE(toplevelConfigureRequestedSpy.count(), 4);
235 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
236 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
237 QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
238 QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(108, 50));
239 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
240
241 // Now render new size.
242 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
243 Test::render(surface.get(), QSize(108, 50), Qt::blue);
244 QVERIFY(frameGeometryChangedSpy.wait());
245 QCOMPARE(window->frameGeometry(), QRect(0, 0, 108, 50));
246 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
247
248 // Go down.
249 window->keyPressEvent(Qt::Key_Down);
250 window->updateInteractiveMoveResize(Cursors::self()->mouse()->pos());
251 QCOMPARE(Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 8));
252
253 // The client should receive another configure event.
254 QVERIFY(surfaceConfigureRequestedSpy.wait());
255 QCOMPARE(surfaceConfigureRequestedSpy.count(), 5);
256 QCOMPARE(toplevelConfigureRequestedSpy.count(), 5);
257 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
258 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
259 QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
260 QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(108, 58));
261
262 // Now render new size.
263 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
264 Test::render(surface.get(), QSize(108, 58), Qt::blue);
265 QVERIFY(frameGeometryChangedSpy.wait());
266 QCOMPARE(window->frameGeometry(), QRect(0, 0, 108, 58));
267 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
268
269 // Let's finalize the resize operation.
270 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 0);
271 window->keyPressEvent(Qt::Key_Enter);
272 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
273 QCOMPARE(moveResizedChangedSpy.count(), 2);
274 QCOMPARE(window->isInteractiveResize(), false);
275 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
276 QVERIFY(surfaceConfigureRequestedSpy.wait());
277 QCOMPARE(surfaceConfigureRequestedSpy.count(), 6);
278 QCOMPARE(toplevelConfigureRequestedSpy.count(), 6);
279 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
280 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
281 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing));
282
283 // Destroy the client.
284 shellSurface.reset();
285 QVERIFY(Test::waitForWindowClosed(window));
286}
287
288void MoveResizeWindowTest::testPackTo_data()
289{
290 QTest::addColumn<QString>("methodCall");
291 QTest::addColumn<QRectF>("expectedGeometry");
292
293 QTest::newRow("left") << QStringLiteral("slotWindowMoveLeft") << QRectF(0, 487, 100, 50);
294 QTest::newRow("up") << QStringLiteral("slotWindowMoveUp") << QRectF(590, 0, 100, 50);
295 QTest::newRow("right") << QStringLiteral("slotWindowMoveRight") << QRectF(1180, 487, 100, 50);
296 QTest::newRow("down") << QStringLiteral("slotWindowMoveDown") << QRectF(590, 974, 100, 50);
297}
298
299void MoveResizeWindowTest::testPackTo()
300{
301 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
302 QVERIFY(surface != nullptr);
303
304 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
305 QVERIFY(shellSurface != nullptr);
306 // let's render
307 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
308
309 QVERIFY(window);
310 QCOMPARE(workspace()->activeWindow(), window);
311 QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
312
313 // let's place it centered
314 workspace()->placement()->placeCentered(window, QRect(0, 0, 1280, 1024));
315 QCOMPARE(window->frameGeometry(), QRect(590, 487, 100, 50));
316
317 QFETCH(QString, methodCall);
318 QMetaObject::invokeMethod(workspace(), methodCall.toLocal8Bit().constData());
319 QTEST(window->frameGeometry(), "expectedGeometry");
320 surface.reset();
321 QVERIFY(Test::waitForWindowClosed(window));
322}
323
324void MoveResizeWindowTest::testPackAgainstClient_data()
325{
326 QTest::addColumn<QString>("methodCall");
327 QTest::addColumn<QRectF>("expectedGeometry");
328
329 QTest::newRow("left") << QStringLiteral("slotWindowMoveLeft") << QRectF(10, 487, 100, 50);
330 QTest::newRow("up") << QStringLiteral("slotWindowMoveUp") << QRectF(590, 10, 100, 50);
331 QTest::newRow("right") << QStringLiteral("slotWindowMoveRight") << QRectF(1170, 487, 100, 50);
332 QTest::newRow("down") << QStringLiteral("slotWindowMoveDown") << QRectF(590, 964, 100, 50);
333}
334
335void MoveResizeWindowTest::testPackAgainstClient()
336{
337 std::unique_ptr<KWayland::Client::Surface> surface1(Test::createSurface());
338 QVERIFY(surface1 != nullptr);
339 std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
340 QVERIFY(surface2 != nullptr);
341 std::unique_ptr<KWayland::Client::Surface> surface3(Test::createSurface());
342 QVERIFY(surface3 != nullptr);
343 std::unique_ptr<KWayland::Client::Surface> surface4(Test::createSurface());
344 QVERIFY(surface4 != nullptr);
345
346 std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get()));
347 QVERIFY(shellSurface1 != nullptr);
348 std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
349 QVERIFY(shellSurface2 != nullptr);
350 std::unique_ptr<Test::XdgToplevel> shellSurface3(Test::createXdgToplevelSurface(surface3.get()));
351 QVERIFY(shellSurface3 != nullptr);
352 std::unique_ptr<Test::XdgToplevel> shellSurface4(Test::createXdgToplevelSurface(surface4.get()));
353 QVERIFY(shellSurface4 != nullptr);
354 auto renderWindow = [](KWayland::Client::Surface *surface, const QString &methodCall, const QRect &expectedGeometry) {
355 // let's render
356 auto window = Test::renderAndWaitForShown(surface, QSize(10, 10), Qt::blue);
357
358 QVERIFY(window);
359 QCOMPARE(workspace()->activeWindow(), window);
360 QCOMPARE(window->frameGeometry().size(), QSize(10, 10));
361 // let's place it centered
362 workspace()->placement()->placeCentered(window, QRect(0, 0, 1280, 1024));
363 QCOMPARE(window->frameGeometry(), QRect(635, 507, 10, 10));
364 QMetaObject::invokeMethod(workspace(), methodCall.toLocal8Bit().constData());
365 QCOMPARE(window->frameGeometry(), expectedGeometry);
366 };
367 renderWindow(surface1.get(), QStringLiteral("slotWindowMoveLeft"), QRect(0, 507, 10, 10));
368 renderWindow(surface2.get(), QStringLiteral("slotWindowMoveUp"), QRect(635, 0, 10, 10));
369 renderWindow(surface3.get(), QStringLiteral("slotWindowMoveRight"), QRect(1270, 507, 10, 10));
370 renderWindow(surface4.get(), QStringLiteral("slotWindowMoveDown"), QRect(635, 1014, 10, 10));
371
372 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
373 QVERIFY(surface != nullptr);
374 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
375 QVERIFY(shellSurface != nullptr);
376 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
377
378 QVERIFY(window);
379 QCOMPARE(workspace()->activeWindow(), window);
380 // let's place it centered
381 workspace()->placement()->placeCentered(window, QRect(0, 0, 1280, 1024));
382 QCOMPARE(window->frameGeometry(), QRect(590, 487, 100, 50));
383
384 QFETCH(QString, methodCall);
385 QMetaObject::invokeMethod(workspace(), methodCall.toLocal8Bit().constData());
386 QTEST(window->frameGeometry(), "expectedGeometry");
387}
388
389void MoveResizeWindowTest::testGrowShrink_data()
390{
391 QTest::addColumn<QString>("methodCall");
392 QTest::addColumn<QRectF>("expectedGeometry");
393
394 QTest::newRow("grow vertical") << QStringLiteral("slotWindowExpandVertical") << QRectF(590, 487, 100, 537);
395 QTest::newRow("grow horizontal") << QStringLiteral("slotWindowExpandHorizontal") << QRectF(590, 487, 690, 50);
396 QTest::newRow("shrink vertical") << QStringLiteral("slotWindowShrinkVertical") << QRectF(590, 487, 100, 23);
397 QTest::newRow("shrink horizontal") << QStringLiteral("slotWindowShrinkHorizontal") << QRectF(590, 487, 40, 50);
398}
399
400void MoveResizeWindowTest::testGrowShrink()
401{
402 // block geometry helper
403 std::unique_ptr<KWayland::Client::Surface> surface1(Test::createSurface());
404 QVERIFY(surface1 != nullptr);
405 std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get()));
406 QVERIFY(shellSurface1 != nullptr);
407 Test::render(surface1.get(), QSize(650, 514), Qt::blue);
411
412 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
413 QVERIFY(surface != nullptr);
414
415 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
416 QVERIFY(shellSurface != nullptr);
417 QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
418 QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
419 // let's render
420 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
421 QVERIFY(toplevelConfigureRequestedSpy.wait());
422
423 QVERIFY(window);
424 QCOMPARE(workspace()->activeWindow(), window);
425
426 // let's place it centered
427 workspace()->placement()->placeCentered(window, QRect(0, 0, 1280, 1024));
428 QCOMPARE(window->frameGeometry(), QRect(590, 487, 100, 50));
429
430 QFETCH(QString, methodCall);
431 QMetaObject::invokeMethod(workspace(), methodCall.toLocal8Bit().constData());
432 QVERIFY(surfaceConfigureRequestedSpy.wait());
433 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::red);
434
435 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
436 m_connection->flush();
437 QVERIFY(frameGeometryChangedSpy.wait());
438 QTEST(window->frameGeometry(), "expectedGeometry");
439}
440
441void MoveResizeWindowTest::testPointerMoveEnd_data()
442{
443 QTest::addColumn<int>("additionalButton");
444
445 QTest::newRow("BTN_RIGHT") << BTN_RIGHT;
446 QTest::newRow("BTN_MIDDLE") << BTN_MIDDLE;
447 QTest::newRow("BTN_SIDE") << BTN_SIDE;
448 QTest::newRow("BTN_EXTRA") << BTN_EXTRA;
449 QTest::newRow("BTN_FORWARD") << BTN_FORWARD;
450 QTest::newRow("BTN_BACK") << BTN_BACK;
451 QTest::newRow("BTN_TASK") << BTN_TASK;
452 for (int i = BTN_TASK + 1; i < BTN_JOYSTICK; i++) {
453 QTest::newRow(QByteArray::number(i, 16).constData()) << i;
454 }
455}
456
457void MoveResizeWindowTest::testPointerMoveEnd()
458{
459 // this test verifies that moving a window through pointer only ends if all buttons are released
460
461 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
462 QVERIFY(surface != nullptr);
463
464 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
465 QVERIFY(shellSurface != nullptr);
466 // let's render
467 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
468
469 QVERIFY(window);
470 QCOMPARE(window, workspace()->activeWindow());
471 QVERIFY(!window->isInteractiveMove());
472
473 // let's trigger the left button
474 quint32 timestamp = 1;
475 Test::pointerButtonPressed(BTN_LEFT, timestamp++);
476 QVERIFY(!window->isInteractiveMove());
478 QVERIFY(window->isInteractiveMove());
479
480 // let's press another button
481 QFETCH(int, additionalButton);
482 Test::pointerButtonPressed(additionalButton, timestamp++);
483 QVERIFY(window->isInteractiveMove());
484
485 // release the left button, should still have the window moving
486 Test::pointerButtonReleased(BTN_LEFT, timestamp++);
487 QVERIFY(window->isInteractiveMove());
488
489 // but releasing the other button should now end moving
490 Test::pointerButtonReleased(additionalButton, timestamp++);
491 QVERIFY(!window->isInteractiveMove());
492 surface.reset();
493 QVERIFY(Test::waitForWindowClosed(window));
494}
495void MoveResizeWindowTest::testClientSideMove()
496{
497 input()->pointer()->warp(QPointF(640, 512));
498 std::unique_ptr<KWayland::Client::Pointer> pointer(Test::waylandSeat()->createPointer());
499 QSignalSpy pointerEnteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
500 QSignalSpy pointerLeftSpy(pointer.get(), &KWayland::Client::Pointer::left);
501 QSignalSpy buttonSpy(pointer.get(), &KWayland::Client::Pointer::buttonStateChanged);
502
503 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
504 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
505 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
506 QVERIFY(window);
507
508 // move pointer into center of geometry
509 const QRectF startGeometry = window->frameGeometry();
510 input()->pointer()->warp(startGeometry.center());
511 QVERIFY(pointerEnteredSpy.wait());
512 QCOMPARE(pointerEnteredSpy.first().last().toPoint(), QPoint(50, 25));
513 // simulate press
514 quint32 timestamp = 1;
515 Test::pointerButtonPressed(BTN_LEFT, timestamp++);
516 QVERIFY(buttonSpy.wait());
517 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
518 shellSurface->move(*Test::waylandSeat(), buttonSpy.first().first().value<quint32>());
519 QVERIFY(interactiveMoveResizeStartedSpy.wait());
520 QCOMPARE(window->isInteractiveMove(), true);
521 QVERIFY(pointerLeftSpy.wait());
522
523 // move a bit
524 QSignalSpy clientMoveStepSpy(window, &Window::interactiveMoveResizeStepped);
525 const QPointF startPoint = startGeometry.center();
526 const int dragDistance = QApplication::startDragDistance();
527 // Why?
528 Test::pointerMotion(startPoint + QPoint(dragDistance, dragDistance) + QPoint(6, 6), timestamp++);
529 QCOMPARE(clientMoveStepSpy.count(), 1);
530
531 // and release again
532 Test::pointerButtonReleased(BTN_LEFT, timestamp++);
533 QVERIFY(pointerEnteredSpy.wait());
534 QCOMPARE(window->isInteractiveMove(), false);
535 QCOMPARE(window->frameGeometry(), startGeometry.translated(QPoint(dragDistance, dragDistance) + QPoint(6, 6)));
536 QCOMPARE(pointerEnteredSpy.last().last().toPoint(), QPoint(50, 25));
537}
538
539void MoveResizeWindowTest::testNetMove()
540{
541 // this test verifies that a move request for an X11 window through NET API works
542 // create an xcb window
544 QVERIFY(!xcb_connection_has_error(c.get()));
545
546 xcb_window_t windowId = xcb_generate_id(c.get());
547 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
548 0, 0, 100, 100,
549 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
550 xcb_size_hints_t hints;
551 memset(&hints, 0, sizeof(hints));
552 xcb_icccm_size_hints_set_position(&hints, 1, 0, 0);
553 xcb_icccm_size_hints_set_size(&hints, 1, 100, 100);
554 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
555 // let's set a no-border
556 NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::WMWindowType, NET::Properties2());
557 winInfo.setWindowType(NET::Override);
558 xcb_map_window(c.get(), windowId);
559 xcb_flush(c.get());
560
561 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
562 QVERIFY(windowCreatedSpy.wait());
563 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
564 QVERIFY(window);
565 QCOMPARE(window->window(), windowId);
566 const QRectF origGeo = window->frameGeometry();
567
568 // let's move the cursor outside the window
569 input()->pointer()->warp(workspace()->activeOutput()->geometry().center());
570 QVERIFY(!exclusiveContains(origGeo, Cursors::self()->mouse()->pos()));
571
572 QSignalSpy interactiveMoveResizeStartedSpy(window, &X11Window::interactiveMoveResizeStarted);
573 QSignalSpy interactiveMoveResizeFinishedSpy(window, &X11Window::interactiveMoveResizeFinished);
574 QSignalSpy interactiveMoveResizeSteppedSpy(window, &X11Window::interactiveMoveResizeStepped);
575 QVERIFY(!workspace()->moveResizeWindow());
576
577 // use NETRootInfo to trigger a move request
578 NETRootInfo root(c.get(), NET::Properties());
579 root.moveResizeRequest(windowId, origGeo.center().x(), origGeo.center().y(), NET::Move, XCB_BUTTON_INDEX_1);
580 xcb_flush(c.get());
581
582 QVERIFY(interactiveMoveResizeStartedSpy.wait());
583 QCOMPARE(workspace()->moveResizeWindow(), window);
584 QVERIFY(window->isInteractiveMove());
585 QCOMPARE(window->geometryRestore(), origGeo);
586 QCOMPARE(Cursors::self()->mouse()->pos(), origGeo.center());
587
588 // let's move a step
589 input()->pointer()->warp(Cursors::self()->mouse()->pos() + QPoint(10, 10));
590 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
591 QCOMPARE(interactiveMoveResizeSteppedSpy.first().last(), origGeo.translated(10, 10));
592
593 // let's cancel the move resize again through the net API
594 root.moveResizeRequest(windowId, window->frameGeometry().center().x(), window->frameGeometry().center().y(), NET::MoveResizeCancel, XCB_BUTTON_INDEX_1);
595 xcb_flush(c.get());
596 QVERIFY(interactiveMoveResizeFinishedSpy.wait());
597
598 // and destroy the window again
599 xcb_unmap_window(c.get(), windowId);
600 xcb_destroy_window(c.get(), windowId);
601 xcb_flush(c.get());
602 c.reset();
603
604 QSignalSpy windowClosedSpy(window, &X11Window::closed);
605 QVERIFY(windowClosedSpy.wait());
606}
607
608void MoveResizeWindowTest::testAdjustClientGeometryOfHiddenX11Panel_data()
609{
610 QTest::addColumn<QRect>("panelGeometry");
611 QTest::addColumn<QPoint>("targetPoint");
612 QTest::addColumn<QPoint>("expectedAdjustedPoint");
613 QTest::addColumn<quint32>("hideLocation");
614
615 QTest::newRow("top") << QRect(0, 0, 100, 20) << QPoint(50, 25) << QPoint(50, 20) << 0u;
616 QTest::newRow("bottom") << QRect(0, 1024 - 20, 100, 20) << QPoint(50, 1024 - 25 - 50) << QPoint(50, 1024 - 20 - 50) << 2u;
617 QTest::newRow("left") << QRect(0, 0, 20, 100) << QPoint(25, 50) << QPoint(20, 50) << 3u;
618 QTest::newRow("right") << QRect(1280 - 20, 0, 20, 100) << QPoint(1280 - 25 - 100, 50) << QPoint(1280 - 20 - 100, 50) << 1u;
619}
620
621void MoveResizeWindowTest::testAdjustClientGeometryOfHiddenX11Panel()
622{
623 // this test verifies that auto hiding panels are ignored when adjusting client geometry
624 // see BUG 365892
625
626 // first create our panel
628 QVERIFY(!xcb_connection_has_error(c.get()));
629
630 xcb_window_t windowId = xcb_generate_id(c.get());
631 QFETCH(QRect, panelGeometry);
632 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
633 panelGeometry.x(), panelGeometry.y(), panelGeometry.width(), panelGeometry.height(),
634 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
635 xcb_size_hints_t hints;
636 memset(&hints, 0, sizeof(hints));
637 xcb_icccm_size_hints_set_position(&hints, 1, panelGeometry.x(), panelGeometry.y());
638 xcb_icccm_size_hints_set_size(&hints, 1, panelGeometry.width(), panelGeometry.height());
639 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
640 NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::WMWindowType, NET::Properties2());
641 winInfo.setWindowType(NET::Dock);
642 xcb_map_window(c.get(), windowId);
643 xcb_flush(c.get());
644
645 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
646 QVERIFY(windowCreatedSpy.wait());
647 X11Window *panel = windowCreatedSpy.first().first().value<X11Window *>();
648 QVERIFY(panel);
649 QCOMPARE(panel->window(), windowId);
650 QCOMPARE(panel->frameGeometry(), panelGeometry);
651 QVERIFY(panel->isDock());
652
653 // let's create a window
654 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
655 QVERIFY(surface != nullptr);
656
657 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
658 QVERIFY(shellSurface != nullptr);
659 auto testWindow = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
660
661 QVERIFY(testWindow);
662 QVERIFY(testWindow->isMovable());
663 // panel is not yet hidden, we should snap against it
664 QFETCH(QPoint, targetPoint);
665 QTEST(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false).toPoint(), "expectedAdjustedPoint");
666
667 // now let's hide the panel
668 QSignalSpy panelHiddenSpy(panel, &Window::windowHidden);
669 QFETCH(quint32, hideLocation);
670 xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->kde_screen_edge_show, XCB_ATOM_CARDINAL, 32, 1, &hideLocation);
671 xcb_flush(c.get());
672 QVERIFY(panelHiddenSpy.wait());
673
674 // now try to snap again
675 QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint);
676
677 // and destroy the panel again
678 xcb_unmap_window(c.get(), windowId);
679 xcb_destroy_window(c.get(), windowId);
680 xcb_flush(c.get());
681 c.reset();
682
683 QSignalSpy panelClosedSpy(panel, &X11Window::closed);
684 QVERIFY(panelClosedSpy.wait());
685
686 // snap once more
687 QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint);
688
689 // and close
690 QSignalSpy windowClosedSpy(testWindow, &Window::closed);
691 shellSurface.reset();
692 surface.reset();
693 QVERIFY(windowClosedSpy.wait());
694}
695
696void MoveResizeWindowTest::testAdjustClientGeometryOfHiddenWaylandPanel_data()
697{
698 QTest::addColumn<uint32_t>("anchor");
699 QTest::addColumn<QRect>("panelGeometry");
700 QTest::addColumn<QPoint>("targetPoint");
701 QTest::addColumn<QPoint>("expectedAdjustedPoint");
702
703 QTest::newRow("top") << uint32_t(Test::LayerSurfaceV1::anchor_top) << QRect(0, 0, 1280, 20) << QPoint(50, 25) << QPoint(50, 20);
704 QTest::newRow("bottom") << uint32_t(Test::LayerSurfaceV1::anchor_bottom) << QRect(0, 1024 - 20, 1280, 20) << QPoint(50, 1024 - 25 - 50) << QPoint(50, 1024 - 20 - 50);
705 QTest::newRow("left") << uint32_t(Test::LayerSurfaceV1::anchor_left) << QRect(0, 0, 20, 1024) << QPoint(25, 50) << QPoint(20, 50);
706 QTest::newRow("right") << uint32_t(Test::LayerSurfaceV1::anchor_right) << QRect(1280 - 20, 0, 20, 1024) << QPoint(1280 - 25 - 100, 50) << QPoint(1280 - 20 - 100, 50);
707}
708
709void MoveResizeWindowTest::testAdjustClientGeometryOfHiddenWaylandPanel()
710{
711 // this test verifies that hidden panels are ignored when adjusting client geometry
712 // see BUG 365892
713
714 // first create our panel
715 std::unique_ptr<KWayland::Client::Surface> panelSurface(Test::createSurface());
716 std::unique_ptr<Test::LayerSurfaceV1> panelShellSurface(Test::createLayerSurfaceV1(panelSurface.get(), QStringLiteral("dock")));
717 QFETCH(QRect, panelGeometry);
718 QFETCH(uint32_t, anchor);
719 panelShellSurface->set_anchor(anchor);
720 panelShellSurface->set_size(panelGeometry.width(), panelGeometry.height());
721 panelSurface->commit(KWayland::Client::Surface::CommitFlag::None);
722
723 // let's render
724 QSignalSpy panelConfigureRequestedSpy(panelShellSurface.get(), &Test::LayerSurfaceV1::configureRequested);
725 QVERIFY(panelConfigureRequestedSpy.wait());
726 auto panel = Test::renderAndWaitForShown(panelSurface.get(), panelConfigureRequestedSpy.last().at(1).toSize(), Qt::blue);
727 QVERIFY(panel);
728 QCOMPARE(panel->frameGeometry(), panelGeometry);
729 QVERIFY(panel->isDock());
730
731 // let's create a window
732 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
733 QVERIFY(surface != nullptr);
734
735 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
736 QVERIFY(shellSurface != nullptr);
737 auto testWindow = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
738
739 QVERIFY(testWindow);
740 QVERIFY(testWindow->isMovable());
741 // panel is not yet hidden, we should snap against it
742 QFETCH(QPoint, targetPoint);
743 QTEST(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false).toPoint(), "expectedAdjustedPoint");
744
745 // now let's hide the panel
746 panel->setHidden(true);
747
748 // now try to snap again
749 QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint);
750
751 // and destroy the panel again
752 QSignalSpy panelClosedSpy(panel, &Window::closed);
753 panelShellSurface.reset();
754 panelSurface.reset();
755 QVERIFY(panelClosedSpy.wait());
756
757 // snap once more
758 QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint);
759
760 // and close
761 QSignalSpy windowClosedSpy(testWindow, &Window::closed);
762 shellSurface.reset();
763 surface.reset();
764 QVERIFY(windowClosedSpy.wait());
765}
766
767void MoveResizeWindowTest::testResizeForVirtualKeyboard_data()
768{
769 QTest::addColumn<QRect>("windowRect");
770 QTest::addColumn<QRect>("keyboardRect");
771 QTest::addColumn<QRect>("resizedWindowRect");
772
773 QTest::newRow("standard") << QRect(100, 300, 500, 800) << QRect(0, 100, 1280, 500) << QRect(100, 0, 500, 100);
774 QTest::newRow("same size") << QRect(100, 300, 500, 500) << QRect(0, 600, 1280, 400) << QRect(100, 100, 500, 500);
775 QTest::newRow("smaller width") << QRect(100, 300, 500, 800) << QRect(300, 100, 100, 500) << QRect(100, 0, 500, 100);
776 QTest::newRow("no height change") << QRect(100, 300, 500, 500) << QRect(0, 900, 1280, 124) << QRect(100, 300, 500, 500);
777 QTest::newRow("no width change") << QRect(100, 300, 500, 500) << QRect(0, 400, 100, 500) << QRect(100, 300, 500, 500);
778}
779
780void MoveResizeWindowTest::testResizeForVirtualKeyboard()
781{
782 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
783 QVERIFY(surface != nullptr);
784
785 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
786 QVERIFY(shellSurface != nullptr);
787
788 QFETCH(QRect, windowRect);
789 QFETCH(QRect, keyboardRect);
790 QFETCH(QRect, resizedWindowRect);
791
792 // There are three things that may happen when the virtual keyboard geometry
793 // is set: We move the window to the top and resize it, we move the window
794 // but don't change its size (if the window is already small enough) or we
795 // do not change anything because the virtual keyboard does not overlap the
796 // window. We should verify that, for the first, we get both a position and
797 // a size change, for the second we only get a position change and for the
798 // last we get no changes.
799 bool sizeChange = windowRect.size() != resizedWindowRect.size();
800 bool positionChange = windowRect.topLeft() != resizedWindowRect.topLeft();
801
802 // let's render
803 auto window = Test::renderAndWaitForShown(surface.get(), windowRect.size(), Qt::blue);
804 QVERIFY(window);
805
806 // The client should receive a configure event upon becoming active.
807 QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
808 QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
809 QVERIFY(surfaceConfigureRequestedSpy.wait());
810 surfaceConfigureRequestedSpy.clear();
811
812 window->move(windowRect.topLeft());
813 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
814
815 QCOMPARE(window->frameGeometry(), windowRect);
816 window->setVirtualKeyboardGeometry(keyboardRect);
817
818 if (sizeChange) {
819 QVERIFY(surfaceConfigureRequestedSpy.wait());
820 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt());
821 } else {
822 QVERIFY(surfaceConfigureRequestedSpy.count() == 0);
823 }
824 // render at the new size
825 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
826
827 if (positionChange || sizeChange) {
828 QVERIFY(frameGeometryChangedSpy.count() > 0 || frameGeometryChangedSpy.wait());
829 frameGeometryChangedSpy.clear();
830 } else {
831 QVERIFY(frameGeometryChangedSpy.count() == 0);
832 }
833
834 QCOMPARE(window->frameGeometry(), resizedWindowRect);
835 window->setVirtualKeyboardGeometry(QRect());
836
837 if (sizeChange) {
838 QVERIFY(surfaceConfigureRequestedSpy.wait());
839 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt());
840 }
841 // render at the new size
842 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
843
844 if (positionChange || sizeChange) {
845 QVERIFY(frameGeometryChangedSpy.count() > 0 || frameGeometryChangedSpy.wait());
846 } else {
847 QVERIFY(frameGeometryChangedSpy.count() == 0);
848 }
849
850 QCOMPARE(window->frameGeometry(), windowRect);
851}
852
853void MoveResizeWindowTest::testResizeForVirtualKeyboardWithMaximize()
854{
855 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
856 QVERIFY(surface != nullptr);
857
858 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
859 QVERIFY(shellSurface != nullptr);
860
861 // let's render
862 auto window = Test::renderAndWaitForShown(surface.get(), QSize(500, 800), Qt::blue);
863 QVERIFY(window);
864
865 // The client should receive a configure event upon becoming active.
866 QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
867 QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
868 QVERIFY(surfaceConfigureRequestedSpy.wait());
869
870 window->move(QPoint(100, 300));
871 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
872
873 QCOMPARE(window->frameGeometry(), QRect(100, 300, 500, 800));
874 window->setVirtualKeyboardGeometry(QRect(0, 100, 1280, 500));
875 QVERIFY(surfaceConfigureRequestedSpy.wait());
876
877 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt());
878 // render at the new size
879 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
880 QVERIFY(frameGeometryChangedSpy.wait());
881 QCOMPARE(window->frameGeometry(), QRect(100, 0, 500, 100));
882
883 window->setMaximize(true, true);
884 QVERIFY(surfaceConfigureRequestedSpy.wait());
885 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt());
886 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
887 QVERIFY(frameGeometryChangedSpy.wait());
888 QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
889
890 window->setVirtualKeyboardGeometry(QRect());
891 QVERIFY(!surfaceConfigureRequestedSpy.wait(10));
892
893 // render at the size of the configureRequested.. it won't have changed
894 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
895 QVERIFY(!frameGeometryChangedSpy.wait(10));
896
897 // Size will NOT be restored
898 QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
899}
900
901void MoveResizeWindowTest::testResizeForVirtualKeyboardWithFullScreen()
902{
903 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
904 QVERIFY(surface != nullptr);
905
906 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
907 QVERIFY(shellSurface != nullptr);
908
909 // let's render
910 auto window = Test::renderAndWaitForShown(surface.get(), QSize(500, 800), Qt::blue);
911 QVERIFY(window);
912
913 // The client should receive a configure event upon becoming active.
914 QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
915 QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
916 QVERIFY(surfaceConfigureRequestedSpy.wait());
917
918 window->move(QPoint(100, 300));
919 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
920
921 QCOMPARE(window->frameGeometry(), QRect(100, 300, 500, 800));
922 window->setVirtualKeyboardGeometry(QRect(0, 100, 1280, 500));
923 QVERIFY(surfaceConfigureRequestedSpy.wait());
924
925 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt());
926 // render at the new size
927 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
928 QVERIFY(frameGeometryChangedSpy.wait());
929 QCOMPARE(window->frameGeometry(), QRect(100, 0, 500, 100));
930
931 window->setFullScreen(true);
932 QVERIFY(surfaceConfigureRequestedSpy.wait());
933 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt());
934 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
935 QVERIFY(frameGeometryChangedSpy.wait());
936 QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
937
938 window->setVirtualKeyboardGeometry(QRect());
939 QVERIFY(!surfaceConfigureRequestedSpy.wait(10));
940
941 // render at the size of the configureRequested.. it won't have changed
942 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
943 QVERIFY(!frameGeometryChangedSpy.wait(10));
944 // Size will NOT be restored
945 QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
946}
947
948void MoveResizeWindowTest::testDestroyMoveClient()
949{
950 // This test verifies that active move operation gets finished when
951 // the associated client is destroyed.
952
953 // Create the test client.
954 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
955 QVERIFY(surface != nullptr);
956 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
957 QVERIFY(shellSurface != nullptr);
958 Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
959 QVERIFY(window);
960
961 // Start moving the client.
962 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
963 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
964
965 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
966 QCOMPARE(window->isInteractiveMove(), false);
967 QCOMPARE(window->isInteractiveResize(), false);
969 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
970 QCOMPARE(workspace()->moveResizeWindow(), window);
971 QCOMPARE(window->isInteractiveMove(), true);
972 QCOMPARE(window->isInteractiveResize(), false);
973
974 // Let's pretend that the client crashed.
975 shellSurface.reset();
976 surface.reset();
977 QVERIFY(Test::waitForWindowClosed(window));
978 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
979 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
980}
981
982void MoveResizeWindowTest::testDestroyResizeClient()
983{
984 // This test verifies that active resize operation gets finished when
985 // the associated client is destroyed.
986
987 // Create the test client.
988 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
989 QVERIFY(surface != nullptr);
990 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
991 QVERIFY(shellSurface != nullptr);
992 Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
993 QVERIFY(window);
994
995 // Start resizing the client.
996 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
997 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
998
999 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
1000 QCOMPARE(window->isInteractiveMove(), false);
1001 QCOMPARE(window->isInteractiveResize(), false);
1003 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
1004 QCOMPARE(workspace()->moveResizeWindow(), window);
1005 QCOMPARE(window->isInteractiveMove(), false);
1006 QCOMPARE(window->isInteractiveResize(), true);
1007
1008 // Let's pretend that the client crashed.
1009 shellSurface.reset();
1010 surface.reset();
1011 QVERIFY(Test::waitForWindowClosed(window));
1012 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
1013 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
1014}
1015
1016void MoveResizeWindowTest::testCancelInteractiveMoveResize_data()
1017{
1018 QTest::addColumn<QuickTileMode>("quickTileMode");
1019 QTest::addColumn<MaximizeMode>("maximizeMode");
1020
1021 QTest::newRow("quicktile_bottom") << QuickTileMode(QuickTileFlag::Bottom) << MaximizeMode::MaximizeRestore;
1022 QTest::newRow("quicktile_top") << QuickTileMode(QuickTileFlag::Top) << MaximizeMode::MaximizeRestore;
1023 QTest::newRow("quicktile_left") << QuickTileMode(QuickTileFlag::Left) << MaximizeMode::MaximizeRestore;
1024 QTest::newRow("quicktile_right") << QuickTileMode(QuickTileFlag::Right) << MaximizeMode::MaximizeRestore;
1025 QTest::newRow("maximize_vertical") << QuickTileMode(QuickTileFlag::None) << MaximizeMode::MaximizeVertical;
1026 QTest::newRow("maximize_horizontal") << QuickTileMode(QuickTileFlag::None) << MaximizeMode::MaximizeHorizontal;
1027 QTest::newRow("maximize_full") << QuickTileMode(QuickTileFlag::Maximize) << MaximizeMode::MaximizeFull;
1028}
1029
1030void MoveResizeWindowTest::testCancelInteractiveMoveResize()
1031{
1032 // This test verifies that after moveresize is cancelled, all relevant window states are restored
1033 // to what they were before moveresize began
1034
1035 // Create the test client.
1036 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1037 QVERIFY(surface != nullptr);
1038 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
1039 QVERIFY(shellSurface != nullptr);
1040 Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
1041 QVERIFY(window);
1042
1043 // tile / maximize window
1044 QFETCH(QuickTileMode, quickTileMode);
1045 QFETCH(MaximizeMode, maximizeMode);
1046 if (maximizeMode) {
1047 window->setMaximize(maximizeMode & MaximizeMode::MaximizeVertical, maximizeMode & MaximizeMode::MaximizeHorizontal);
1048 } else {
1049 window->setQuickTileMode(quickTileMode, true);
1050 }
1051 QCOMPARE(window->quickTileMode(), quickTileMode);
1052 QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
1053
1054 QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1055 QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1056 QVERIFY(surfaceConfigureRequestedSpy.wait());
1057 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
1058
1059 const QRectF geometry = window->moveResizeGeometry();
1060 const QRectF geometryRestore = window->geometryRestore();
1061
1062 // Start resizing the client.
1063 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
1064 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
1065
1066 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
1067 QCOMPARE(window->isInteractiveMove(), false);
1068 QCOMPARE(window->isInteractiveResize(), false);
1070 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
1071 QCOMPARE(workspace()->moveResizeWindow(), window);
1072 QCOMPARE(window->isInteractiveMove(), false);
1073 QCOMPARE(window->isInteractiveResize(), true);
1074
1075 Test::pointerMotionRelative(QPoint(1, 1), 1);
1076 QCOMPARE(window->quickTileMode(), QuickTileMode());
1077 QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1078
1079 // cancel moveresize, all state from before should be restored
1080 window->keyPressEvent(Qt::Key::Key_Escape);
1081 QCOMPARE(window->moveResizeGeometry(), geometry);
1082 QCOMPARE(window->quickTileMode(), quickTileMode);
1083 QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
1084 QCOMPARE(window->geometryRestore(), geometryRestore);
1085}
1086}
1087
1089#include "move_resize_window_test.moc"
Xcb::Atom kde_screen_edge_show
Definition atoms.h:61
QPointF pos()
Definition cursor.cpp:204
static Cursors * self()
Definition cursor.cpp:35
Cursor * mouse() const
Definition cursor.h:266
PointerInputRedirection * pointer() const
Definition input.h:220
void placeCentered(Window *c, const QRectF &area, PlacementPolicy next=PlacementUnknown)
void warp(const QPointF &pos)
void configureRequested(quint32 serial, const QSize &size)
void configureRequested(quint32 serial)
void configureRequested(const QSize &size, KWin::Test::XdgToplevel::States states)
void interactiveMoveResizeStarted()
void interactiveMoveResizeStepped(const QRectF &geometry)
void interactiveMoveResizeFinished()
void moveResizedChanged()
void frameGeometryChanged(const QRectF &oldGeometry)
void windowHidden(KWin::Window *window)
Placement * placement() const
static Workspace * self()
Definition workspace.h:91
void windowAdded(KWin::Window *)
void slotWindowMoveDown()
void slotWindowMoveRight()
QList< Output * > outputs() const
Definition workspace.h:762
void setActiveOutput(Output *output)
Q_DECLARE_METATYPE(KWin::SwitchEvent::State)
#define WAYLANDTEST_MAIN(TestObject)
Window * renderAndWaitForShown(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32, int timeout=5000)
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)
KWayland::Client::Seat * waylandSeat()
LayerSurfaceV1 * createLayerSurfaceV1(KWayland::Client::Surface *surface, const QString &scope, KWayland::Client::Output *output=nullptr, LayerShellV1::layer layer=LayerShellV1::layer_top)
Window * waitForWaylandWindowShown(int timeout=5000)
void pointerButtonPressed(quint32 button, quint32 time)
XcbConnectionPtr createX11Connection()
void pointerMotion(const QPointF &position, quint32 time)
std::unique_ptr< KWayland::Client::Surface > createSurface()
void pointerMotionRelative(const QPointF &delta, quint32 time)
XdgToplevel * createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent=nullptr)
bool waitForWaylandPointer()
KWayland::Client::ConnectionThread * waylandConnection()
void pointerButtonReleased(quint32 button, quint32 time)
std::unique_ptr< xcb_connection_t, XcbConnectionDeleter > XcbConnectionPtr
bool waitForWindowClosed(Window *window)
KWIN_EXPORT xcb_window_t rootWindow()
Definition xcb.h:24
MaximizeMode
Definition common.h:74
@ MaximizeVertical
The window is maximized vertically.
Definition common.h:76
@ MaximizeRestore
The window is not maximized in any direction.
Definition common.h:75
@ MaximizeHorizontal
Definition common.h:77
@ MaximizeFull
Equal to MaximizeVertical | MaximizeHorizontal.
Definition common.h:79
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
InputRedirection * input()
Definition input.h:549
KWIN_EXPORT Atoms * atoms
Definition main.cpp:74