KWin
Loading...
Searching...
No Matches
x11_window_test.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 "atoms.h"
12#include "compositor.h"
13#include "cursor.h"
14#include "effect/effectloader.h"
15#include "wayland_server.h"
16#include "workspace.h"
17#include "x11window.h"
18
19#include <KWayland/Client/surface.h>
20
21#include <netwm.h>
22#include <xcb/xcb_icccm.h>
23
24using namespace KWin;
25static const QString s_socketName = QStringLiteral("wayland_test_x11_window-0");
26
27class X11WindowTest : public QObject
28{
29 Q_OBJECT
30private Q_SLOTS:
31 void initTestCase_data();
32 void initTestCase();
33 void init();
34 void cleanup();
35
36 void testMinimumSize();
37 void testMaximumSize();
38 void testResizeIncrements();
39 void testResizeIncrementsNoBaseSize();
40 void testTrimCaption_data();
41 void testTrimCaption();
42 void testFullscreenLayerWithActiveWaylandWindow();
43 void testFocusInWithWaylandLastActiveWindow();
44 void testCaptionChanges();
45 void testCaptionWmName();
46 void testFullscreenWindowGroups();
47 void testActivateFocusedWindow();
48 void testReentrantMoveResize();
49};
50
51void X11WindowTest::initTestCase_data()
52{
53 QTest::addColumn<qreal>("scale");
54 QTest::newRow("normal") << 1.0;
55 QTest::newRow("scaled2x") << 2.0;
56}
57
58void X11WindowTest::initTestCase()
59{
60 qRegisterMetaType<KWin::Window *>();
61 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
62 QVERIFY(waylandServer()->init(s_socketName));
63 Test::setOutputConfig({QRect(0, 0, 1280, 1024)});
64 kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
65
66 kwinApp()->start();
67 QVERIFY(applicationStartedSpy.wait());
68 QVERIFY(KWin::Compositor::self());
69}
70
71void X11WindowTest::init()
72{
74}
75
76void X11WindowTest::cleanup()
77{
79}
80
81void X11WindowTest::testMinimumSize()
82{
83 // This test verifies that the minimum size constraint is correctly applied.
84
85 QFETCH_GLOBAL(qreal, scale);
86 kwinApp()->setXwaylandScale(scale);
87
88 // Create an xcb window.
90 QVERIFY(!xcb_connection_has_error(c.get()));
91 const QRect windowGeometry(0, 0, 100, 200);
92 xcb_window_t windowId = xcb_generate_id(c.get());
93 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
94 windowGeometry.x(),
95 windowGeometry.y(),
96 windowGeometry.width(),
97 windowGeometry.height(),
98 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
99 xcb_size_hints_t hints;
100 memset(&hints, 0, sizeof(hints));
101 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
102 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
103 xcb_icccm_size_hints_set_min_size(&hints, windowGeometry.width(), windowGeometry.height());
104 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
105 xcb_map_window(c.get(), windowId);
106 xcb_flush(c.get());
107
108 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
109 QVERIFY(windowCreatedSpy.wait());
110 X11Window *window = windowCreatedSpy.last().first().value<X11Window *>();
111 QVERIFY(window);
112 QVERIFY(window->isDecorated());
113
114 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
115 QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
116 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
117 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
118
119 // Begin resize.
120 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
121 QVERIFY(!window->isInteractiveResize());
123 QCOMPARE(workspace()->moveResizeWindow(), window);
124 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
125 QVERIFY(window->isInteractiveResize());
126
127 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
128
129 window->keyPressEvent(Qt::Key_Left);
130 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
131 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(-8, 0));
132 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
133 QVERIFY(!frameGeometryChangedSpy.wait(10));
134 QCOMPARE(window->clientSize().width(), 100 / scale);
135
136 window->keyPressEvent(Qt::Key_Right);
137 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
138 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos);
139 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
140 QVERIFY(!frameGeometryChangedSpy.wait(10));
141 QCOMPARE(window->clientSize().width(), 100 / scale);
142
143 window->keyPressEvent(Qt::Key_Right);
144 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
145 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
146 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
147 QVERIFY(frameGeometryChangedSpy.wait());
148 // whilst X11 window size goes through scale, the increment is a logical value kwin side
149 QCOMPARE(window->clientSize().width(), 100 / scale + 8);
150
151 window->keyPressEvent(Qt::Key_Up);
152 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
153 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, -8));
154 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
155 QVERIFY(!frameGeometryChangedSpy.wait(10));
156 QCOMPARE(window->clientSize().height(), 200 / scale);
157
158 window->keyPressEvent(Qt::Key_Down);
159 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
160 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
161 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
162 QVERIFY(!frameGeometryChangedSpy.wait(10));
163 QCOMPARE(window->clientSize().height(), 200 / scale);
164
165 window->keyPressEvent(Qt::Key_Down);
166 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
167 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 8));
168 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
169 QVERIFY(frameGeometryChangedSpy.wait());
170 QCOMPARE(window->clientSize().height(), 200 / scale + 8);
171
172 // Finish the resize operation.
173 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 0);
174 window->keyPressEvent(Qt::Key_Enter);
175 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
176 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
177 QVERIFY(!window->isInteractiveResize());
178
179 // Destroy the window.
180 QSignalSpy windowClosedSpy(window, &X11Window::closed);
181 xcb_unmap_window(c.get(), windowId);
182 xcb_destroy_window(c.get(), windowId);
183 xcb_flush(c.get());
184 QVERIFY(windowClosedSpy.wait());
185 c.reset();
186}
187
188void X11WindowTest::testMaximumSize()
189{
190 // This test verifies that the maximum size constraint is correctly applied.
191 QFETCH_GLOBAL(qreal, scale);
192 kwinApp()->setXwaylandScale(scale);
193
194 // Create an xcb window.
196 QVERIFY(!xcb_connection_has_error(c.get()));
197 const QRect windowGeometry(0, 0, 100, 200);
198 xcb_window_t windowId = xcb_generate_id(c.get());
199 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
200 windowGeometry.x(),
201 windowGeometry.y(),
202 windowGeometry.width(),
203 windowGeometry.height(),
204 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
205 xcb_size_hints_t hints;
206 memset(&hints, 0, sizeof(hints));
207 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
208 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
209 xcb_icccm_size_hints_set_max_size(&hints, windowGeometry.width(), windowGeometry.height());
210 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
211 xcb_map_window(c.get(), windowId);
212 xcb_flush(c.get());
213
214 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
215 QVERIFY(windowCreatedSpy.wait());
216 X11Window *window = windowCreatedSpy.last().first().value<X11Window *>();
217 QVERIFY(window);
218 QVERIFY(window->isDecorated());
219
220 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
221 QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
222 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
223 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
224
225 // Begin resize.
226 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
227 QVERIFY(!window->isInteractiveResize());
229 QCOMPARE(workspace()->moveResizeWindow(), window);
230 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
231 QVERIFY(window->isInteractiveResize());
232
233 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
234
235 window->keyPressEvent(Qt::Key_Right);
236 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
237 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
238 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
239 QVERIFY(!frameGeometryChangedSpy.wait(10));
240 QCOMPARE(window->clientSize().width(), 100 / scale);
241
242 window->keyPressEvent(Qt::Key_Left);
243 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
244 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos);
245 QVERIFY(!interactiveMoveResizeSteppedSpy.wait(10));
246 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
247 QCOMPARE(window->clientSize().width(), 100 / scale);
248
249 window->keyPressEvent(Qt::Key_Left);
250 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
251 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(-8, 0));
252 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
253 QVERIFY(frameGeometryChangedSpy.wait());
254 QCOMPARE(window->clientSize().width(), 100 / scale - 8);
255
256 window->keyPressEvent(Qt::Key_Down);
257 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
258 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(-8, 8));
259 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
260 QVERIFY(!frameGeometryChangedSpy.wait(10));
261 QCOMPARE(window->clientSize().height(), 200 / scale);
262
263 window->keyPressEvent(Qt::Key_Up);
264 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
265 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(-8, 0));
266 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
267 QVERIFY(!frameGeometryChangedSpy.wait(10));
268 QCOMPARE(window->clientSize().height(), 200 / scale);
269
270 window->keyPressEvent(Qt::Key_Up);
271 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
272 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(-8, -8));
273 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
274 QVERIFY(frameGeometryChangedSpy.wait());
275 QCOMPARE(window->clientSize().height(), 200 / scale - 8);
276
277 // Finish the resize operation.
278 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 0);
279 window->keyPressEvent(Qt::Key_Enter);
280 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
281 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
282 QVERIFY(!window->isInteractiveResize());
283
284 // Destroy the window.
285 QSignalSpy windowClosedSpy(window, &X11Window::closed);
286 xcb_unmap_window(c.get(), windowId);
287 xcb_destroy_window(c.get(), windowId);
288 xcb_flush(c.get());
289 QVERIFY(windowClosedSpy.wait());
290 c.reset();
291}
292
293void X11WindowTest::testResizeIncrements()
294{
295 // This test verifies that the resize increments constraint is correctly applied.
296 QFETCH_GLOBAL(qreal, scale);
297 kwinApp()->setXwaylandScale(scale);
298
299 // Create an xcb window.
301 QVERIFY(!xcb_connection_has_error(c.get()));
302 const QRect windowGeometry(0, 0, 100, 200);
303 xcb_window_t windowId = xcb_generate_id(c.get());
304 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
305 windowGeometry.x(),
306 windowGeometry.y(),
307 windowGeometry.width(),
308 windowGeometry.height(),
309 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
310 xcb_size_hints_t hints;
311 memset(&hints, 0, sizeof(hints));
312 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
313 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
314 xcb_icccm_size_hints_set_base_size(&hints, windowGeometry.width(), windowGeometry.height());
315 xcb_icccm_size_hints_set_resize_inc(&hints, 3, 5);
316 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
317 xcb_map_window(c.get(), windowId);
318 xcb_flush(c.get());
319
320 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
321 QVERIFY(windowCreatedSpy.wait());
322 X11Window *window = windowCreatedSpy.last().first().value<X11Window *>();
323 QVERIFY(window);
324 QVERIFY(window->isDecorated());
325
326 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
327 QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
328 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
329 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
330
331 // Begin resize.
332 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
333 QVERIFY(!window->isInteractiveResize());
335 QCOMPARE(workspace()->moveResizeWindow(), window);
336 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
337 QVERIFY(window->isInteractiveResize());
338
339 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
340
341 window->keyPressEvent(Qt::Key_Right);
342 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
343 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
344 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
345 QVERIFY(frameGeometryChangedSpy.wait());
346
347 // 100 + 8 logical pixels, rounded to resize increments. This will differ on scale
348 const qreal horizontalResizeInc = 3 / scale;
349 const qreal verticalResizeInc = 5 / scale;
350 const qreal expectedHorizontalResizeInc = std::floor(8. / horizontalResizeInc) * horizontalResizeInc;
351 const qreal expectedVerticalResizeInc = std::floor(8. / verticalResizeInc) * verticalResizeInc;
352
353 QCOMPARE(window->clientSize(), QSizeF(100, 200) / scale + QSizeF(expectedHorizontalResizeInc, 0));
354
355 window->keyPressEvent(Qt::Key_Down);
356 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
357 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 8));
358 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
359 QVERIFY(frameGeometryChangedSpy.wait());
360 QCOMPARE(window->clientSize(), QSize(100, 200) / scale + QSizeF(expectedHorizontalResizeInc, expectedVerticalResizeInc));
361
362 // Finish the resize operation.
363 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 0);
364 window->keyPressEvent(Qt::Key_Enter);
365 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
366 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
367 QVERIFY(!window->isInteractiveResize());
368
369 // Destroy the window.
370 QSignalSpy windowClosedSpy(window, &X11Window::closed);
371 xcb_unmap_window(c.get(), windowId);
372 xcb_destroy_window(c.get(), windowId);
373 xcb_flush(c.get());
374 QVERIFY(windowClosedSpy.wait());
375 c.reset();
376}
377
378void X11WindowTest::testResizeIncrementsNoBaseSize()
379{
380 QFETCH_GLOBAL(qreal, scale);
381 kwinApp()->setXwaylandScale(scale);
382
383 // Create an xcb window.
385 QVERIFY(!xcb_connection_has_error(c.get()));
386 const QRect windowGeometry(0, 0, 100, 200);
387 xcb_window_t windowId = xcb_generate_id(c.get());
388 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
389 windowGeometry.x(),
390 windowGeometry.y(),
391 windowGeometry.width(),
392 windowGeometry.height(),
393 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
394 xcb_size_hints_t hints;
395 memset(&hints, 0, sizeof(hints));
396 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
397 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
398 xcb_icccm_size_hints_set_min_size(&hints, windowGeometry.width(), windowGeometry.height());
399 xcb_icccm_size_hints_set_resize_inc(&hints, 3, 5);
400 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
401 xcb_map_window(c.get(), windowId);
402 xcb_flush(c.get());
403
404 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
405 QVERIFY(windowCreatedSpy.wait());
406 X11Window *window = windowCreatedSpy.last().first().value<X11Window *>();
407 QVERIFY(window);
408 QVERIFY(window->isDecorated());
409
410 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
411 QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
412 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
413 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
414
415 // Begin resize.
416 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
417 QVERIFY(!window->isInteractiveResize());
419 QCOMPARE(workspace()->moveResizeWindow(), window);
420 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
421 QVERIFY(window->isInteractiveResize());
422
423 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
424
425 window->keyPressEvent(Qt::Key_Right);
426 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
427 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
428 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
429 QVERIFY(frameGeometryChangedSpy.wait());
430
431 // 100 + 8 pixels, rounded to resize increments. This will differ on scale
432 const qreal horizontalResizeInc = 3 / scale;
433 const qreal verticalResizeInc = 5 / scale;
434 const qreal expectedHorizontalResizeInc = std::floor(8. / horizontalResizeInc) * horizontalResizeInc;
435 const qreal expectedVerticalResizeInc = std::floor(8. / verticalResizeInc) * verticalResizeInc;
436
437 QCOMPARE(window->clientSize(), QSizeF(100, 200) / scale + QSizeF(expectedHorizontalResizeInc, 0));
438
439 window->keyPressEvent(Qt::Key_Down);
440 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
441 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 8));
442 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
443 QVERIFY(frameGeometryChangedSpy.wait());
444 QCOMPARE(window->clientSize(), QSizeF(100, 200) / scale + QSizeF(expectedHorizontalResizeInc, expectedVerticalResizeInc));
445
446 // Finish the resize operation.
447 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 0);
448 window->keyPressEvent(Qt::Key_Enter);
449 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
450 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
451 QVERIFY(!window->isInteractiveResize());
452
453 // Destroy the window.
454 QSignalSpy windowClosedSpy(window, &X11Window::closed);
455 xcb_unmap_window(c.get(), windowId);
456 xcb_destroy_window(c.get(), windowId);
457 xcb_flush(c.get());
458 QVERIFY(windowClosedSpy.wait());
459 c.reset();
460}
461
462void X11WindowTest::testTrimCaption_data()
463{
464 QFETCH_GLOBAL(qreal, scale);
465 kwinApp()->setXwaylandScale(scale);
466
467 QTest::addColumn<QByteArray>("originalTitle");
468 QTest::addColumn<QByteArray>("expectedTitle");
469
470 QTest::newRow("simplified")
471 << QByteArrayLiteral("Was tun, wenn Schüler Autismus haben?\342\200\250\342\200\250\342\200\250 – Marlies Hübner - Mozilla Firefox")
472 << QByteArrayLiteral("Was tun, wenn Schüler Autismus haben? – Marlies Hübner - Mozilla Firefox");
473
474 QTest::newRow("with emojis")
475 << QByteArrayLiteral("\bTesting non\302\255printable:\177, emoij:\360\237\230\203, non-characters:\357\277\276")
476 << QByteArrayLiteral("Testing nonprintable:, emoij:\360\237\230\203, non-characters:");
477}
478
479void X11WindowTest::testTrimCaption()
480{
481 QFETCH_GLOBAL(qreal, scale);
482 kwinApp()->setXwaylandScale(scale);
483
484 // this test verifies that caption is properly trimmed
485
486 // create an xcb window
488 QVERIFY(!xcb_connection_has_error(c.get()));
489 const QRect windowGeometry(0, 0, 100, 200);
490 xcb_window_t windowId = xcb_generate_id(c.get());
491 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
492 windowGeometry.x(),
493 windowGeometry.y(),
494 windowGeometry.width(),
495 windowGeometry.height(),
496 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
497 xcb_size_hints_t hints;
498 memset(&hints, 0, sizeof(hints));
499 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
500 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
501 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
502 NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::Properties(), NET::Properties2());
503 QFETCH(QByteArray, originalTitle);
504 winInfo.setName(originalTitle);
505 xcb_map_window(c.get(), windowId);
506 xcb_flush(c.get());
507
508 // we should get a window for it
509 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
510 QVERIFY(windowCreatedSpy.wait());
511 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
512 QVERIFY(window);
513 QCOMPARE(window->window(), windowId);
514 QFETCH(QByteArray, expectedTitle);
515 QCOMPARE(window->caption(), QString::fromUtf8(expectedTitle));
516
517 // and destroy the window again
518 xcb_unmap_window(c.get(), windowId);
519 xcb_flush(c.get());
520
521 QSignalSpy windowClosedSpy(window, &X11Window::closed);
522 QVERIFY(windowClosedSpy.wait());
523 xcb_destroy_window(c.get(), windowId);
524 c.reset();
525}
526
527void X11WindowTest::testFullscreenLayerWithActiveWaylandWindow()
528{
529 QFETCH_GLOBAL(qreal, scale);
530 kwinApp()->setXwaylandScale(scale);
531
532 // this test verifies that an X11 fullscreen window does not stay in the active layer
533 // when a Wayland window is active, see BUG: 375759
534 QCOMPARE(workspace()->outputs().count(), 1);
535
536 // first create an X11 window
538 QVERIFY(!xcb_connection_has_error(c.get()));
539 const QRect windowGeometry(0, 0, 100, 200);
540 xcb_window_t windowId = xcb_generate_id(c.get());
541 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
542 windowGeometry.x(),
543 windowGeometry.y(),
544 windowGeometry.width(),
545 windowGeometry.height(),
546 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
547 xcb_size_hints_t hints;
548 memset(&hints, 0, sizeof(hints));
549 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
550 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
551 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
552 xcb_map_window(c.get(), windowId);
553 xcb_flush(c.get());
554
555 // we should get a window for it
556 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
557 QVERIFY(windowCreatedSpy.wait());
558 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
559 QVERIFY(window);
560 QCOMPARE(window->window(), windowId);
561 QVERIFY(!window->isFullScreen());
562 QVERIFY(window->isActive());
563 QCOMPARE(window->layer(), NormalLayer);
564
566 QVERIFY(window->isFullScreen());
567 QCOMPARE(window->layer(), ActiveLayer);
568 QCOMPARE(workspace()->stackingOrder().last(), window);
569
570 // now let's open a Wayland window
571 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
572 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
573 auto waylandWindow = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
574 QVERIFY(waylandWindow);
575 QVERIFY(waylandWindow->isActive());
576 QCOMPARE(waylandWindow->layer(), NormalLayer);
577 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow);
578 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow);
579 QCOMPARE(window->layer(), NormalLayer);
580
581 // now activate fullscreen again
582 workspace()->activateWindow(window);
583 QTRY_VERIFY(window->isActive());
584 QCOMPARE(window->layer(), ActiveLayer);
585 QCOMPARE(workspace()->stackingOrder().last(), window);
586 QCOMPARE(workspace()->stackingOrder().last(), window);
587
588 // activate wayland window again
589 workspace()->activateWindow(waylandWindow);
590 QTRY_VERIFY(waylandWindow->isActive());
591 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow);
592 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow);
593
594 // back to x window
595 workspace()->activateWindow(window);
596 QTRY_VERIFY(window->isActive());
597 // remove fullscreen
598 QVERIFY(window->isFullScreen());
600 QVERIFY(!window->isFullScreen());
601 // and fullscreen again
603 QVERIFY(window->isFullScreen());
604 QCOMPARE(workspace()->stackingOrder().last(), window);
605 QCOMPARE(workspace()->stackingOrder().last(), window);
606
607 // activate wayland window again
608 workspace()->activateWindow(waylandWindow);
609 QTRY_VERIFY(waylandWindow->isActive());
610 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow);
611 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow);
612
613 // back to X11 window
614 workspace()->activateWindow(window);
615 QTRY_VERIFY(window->isActive());
616 // remove fullscreen
617 QVERIFY(window->isFullScreen());
619 QVERIFY(!window->isFullScreen());
620 // and fullscreen through X API
621 NETWinInfo info(c.get(), windowId, kwinApp()->x11RootWindow(), NET::Properties(), NET::Properties2());
622 info.setState(NET::FullScreen, NET::FullScreen);
623 NETRootInfo rootInfo(c.get(), NET::Properties());
624 rootInfo.setActiveWindow(windowId, NET::FromApplication, XCB_CURRENT_TIME, XCB_WINDOW_NONE);
625 xcb_flush(c.get());
626 QTRY_VERIFY(window->isFullScreen());
627 QCOMPARE(workspace()->stackingOrder().last(), window);
628 QCOMPARE(workspace()->stackingOrder().last(), window);
629
630 // activate wayland window again
631 workspace()->activateWindow(waylandWindow);
632 QTRY_VERIFY(waylandWindow->isActive());
633 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow);
634 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow);
635 QCOMPARE(window->layer(), NormalLayer);
636
637 // close the window
638 shellSurface.reset();
639 surface.reset();
640 QVERIFY(Test::waitForWindowClosed(waylandWindow));
641 QTRY_VERIFY(window->isActive());
642 QCOMPARE(window->layer(), ActiveLayer);
643
644 // and destroy the window again
645 xcb_unmap_window(c.get(), windowId);
646 xcb_flush(c.get());
647}
648
649void X11WindowTest::testFocusInWithWaylandLastActiveWindow()
650{
651 // this test verifies that Workspace::allowWindowActivation does not crash if last client was a Wayland client
652 QFETCH_GLOBAL(qreal, scale);
653 kwinApp()->setXwaylandScale(scale);
654
655 // create an X11 window
657 QVERIFY(!xcb_connection_has_error(c.get()));
658 const QRect windowGeometry(0, 0, 100, 200);
659 xcb_window_t windowId = xcb_generate_id(c.get());
660 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
661 windowGeometry.x(),
662 windowGeometry.y(),
663 windowGeometry.width(),
664 windowGeometry.height(),
665 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
666 xcb_size_hints_t hints;
667 memset(&hints, 0, sizeof(hints));
668 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
669 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
670 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
671 xcb_map_window(c.get(), windowId);
672 xcb_flush(c.get());
673
674 // we should get a window for it
675 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
676 QVERIFY(windowCreatedSpy.wait());
677 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
678 QVERIFY(window);
679 QCOMPARE(window->window(), windowId);
680 QVERIFY(window->isActive());
681
682 // create Wayland window
683 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
684 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
685 auto waylandWindow = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
686 QVERIFY(waylandWindow);
687 QVERIFY(waylandWindow->isActive());
688 // activate no window
689 workspace()->setActiveWindow(nullptr);
690 QVERIFY(!waylandWindow->isActive());
691 QVERIFY(!workspace()->activeWindow());
692 // and close Wayland window again
693 shellSurface.reset();
694 surface.reset();
695 QVERIFY(Test::waitForWindowClosed(waylandWindow));
696
697 // and try to activate the x11 window through X11 api
698 const auto cookie = xcb_set_input_focus_checked(c.get(), XCB_INPUT_FOCUS_NONE, windowId, XCB_CURRENT_TIME);
699 auto error = xcb_request_check(c.get(), cookie);
700 QVERIFY(!error);
701 // this accesses m_lastActiveWindow on trying to activate
702 QTRY_VERIFY(window->isActive());
703
704 // and destroy the window again
705 xcb_unmap_window(c.get(), windowId);
706 xcb_flush(c.get());
707}
708
709void X11WindowTest::testCaptionChanges()
710{
711 QFETCH_GLOBAL(qreal, scale);
712 kwinApp()->setXwaylandScale(scale);
713
714 // verifies that caption is updated correctly when the X11 window updates it
715 // BUG: 383444
717 QVERIFY(!xcb_connection_has_error(c.get()));
718 const QRect windowGeometry(0, 0, 100, 200);
719 xcb_window_t windowId = xcb_generate_id(c.get());
720 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
721 windowGeometry.x(),
722 windowGeometry.y(),
723 windowGeometry.width(),
724 windowGeometry.height(),
725 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
726 xcb_size_hints_t hints;
727 memset(&hints, 0, sizeof(hints));
728 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
729 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
730 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
731 NETWinInfo info(c.get(), windowId, kwinApp()->x11RootWindow(), NET::Properties(), NET::Properties2());
732 info.setName("foo");
733 xcb_map_window(c.get(), windowId);
734 xcb_flush(c.get());
735
736 // we should get a window for it
737 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
738 QVERIFY(windowCreatedSpy.wait());
739 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
740 QVERIFY(window);
741 QCOMPARE(window->window(), windowId);
742 QCOMPARE(window->caption(), QStringLiteral("foo"));
743
744 QSignalSpy captionChangedSpy(window, &X11Window::captionChanged);
745 info.setName("bar");
746 xcb_flush(c.get());
747 QVERIFY(captionChangedSpy.wait());
748 QCOMPARE(window->caption(), QStringLiteral("bar"));
749
750 // and destroy the window again
751 QSignalSpy windowClosedSpy(window, &X11Window::closed);
752 xcb_unmap_window(c.get(), windowId);
753 xcb_flush(c.get());
754 QVERIFY(windowClosedSpy.wait());
755 xcb_destroy_window(c.get(), windowId);
756 c.reset();
757}
758
759void X11WindowTest::testCaptionWmName()
760{
761 QFETCH_GLOBAL(qreal, scale);
762 kwinApp()->setXwaylandScale(scale);
763
764 // this test verifies that a caption set through WM_NAME is read correctly
765
766 // open glxgears as that one only uses WM_NAME
767 QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
768
769 QProcess glxgears;
770 glxgears.setProgram(QStringLiteral("glxgears"));
771 glxgears.start();
772 QVERIFY(glxgears.waitForStarted());
773
774 QVERIFY(windowAddedSpy.wait());
775 QCOMPARE(windowAddedSpy.count(), 1);
776 QCOMPARE(workspace()->windows().count(), 1);
777 Window *glxgearsWindow = workspace()->windows().first();
778 QCOMPARE(glxgearsWindow->caption(), QStringLiteral("glxgears"));
779
780 glxgears.terminate();
781 QVERIFY(glxgears.waitForFinished());
782}
783
784void X11WindowTest::testFullscreenWindowGroups()
785{
786 // this test creates an X11 window and puts it to full screen
787 // then a second window is created which is in the same window group
788 // BUG: 388310
789
790 QFETCH_GLOBAL(qreal, scale);
791 kwinApp()->setXwaylandScale(scale);
792
794 QVERIFY(!xcb_connection_has_error(c.get()));
795 const QRect windowGeometry(0, 0, 100, 200);
796 xcb_window_t windowId = xcb_generate_id(c.get());
797 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
798 windowGeometry.x(),
799 windowGeometry.y(),
800 windowGeometry.width(),
801 windowGeometry.height(),
802 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
803 xcb_size_hints_t hints;
804 memset(&hints, 0, sizeof(hints));
805 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
806 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
807 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
808 xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId);
809 xcb_map_window(c.get(), windowId);
810 xcb_flush(c.get());
811
812 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
813 QVERIFY(windowCreatedSpy.wait());
814 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
815 QVERIFY(window);
816 QCOMPARE(window->window(), windowId);
817 QCOMPARE(window->isActive(), true);
818
819 QCOMPARE(window->isFullScreen(), false);
820 QCOMPARE(window->layer(), NormalLayer);
822 QCOMPARE(window->isFullScreen(), true);
823 QCOMPARE(window->layer(), ActiveLayer);
824
825 // now let's create a second window
826 windowCreatedSpy.clear();
827 xcb_window_t w2 = xcb_generate_id(c.get());
828 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, w2, rootWindow(),
829 windowGeometry.x(),
830 windowGeometry.y(),
831 windowGeometry.width(),
832 windowGeometry.height(),
833 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
834 xcb_size_hints_t hints2;
835 memset(&hints2, 0, sizeof(hints2));
836 xcb_icccm_size_hints_set_position(&hints2, 1, windowGeometry.x(), windowGeometry.y());
837 xcb_icccm_size_hints_set_size(&hints2, 1, windowGeometry.width(), windowGeometry.height());
838 xcb_icccm_set_wm_normal_hints(c.get(), w2, &hints2);
839 xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, w2, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId);
840 xcb_map_window(c.get(), w2);
841 xcb_flush(c.get());
842
843 QVERIFY(windowCreatedSpy.wait());
844 X11Window *window2 = windowCreatedSpy.first().first().value<X11Window *>();
845 QVERIFY(window2);
846 QVERIFY(window != window2);
847 QCOMPARE(window2->window(), w2);
848 QCOMPARE(window2->isActive(), true);
849 QCOMPARE(window2->group(), window->group());
850 // first window should be moved back to normal layer
851 QCOMPARE(window->isActive(), false);
852 QCOMPARE(window->isFullScreen(), true);
853 QCOMPARE(window->layer(), NormalLayer);
854
855 // activating the fullscreen window again, should move it to active layer
856 workspace()->activateWindow(window);
857 QTRY_COMPARE(window->layer(), ActiveLayer);
858}
859
860void X11WindowTest::testActivateFocusedWindow()
861{
862 // The window manager may call XSetInputFocus() on a window that already has focus, in which
863 // case no FocusIn event will be generated and the window won't be marked as active. This test
864 // verifies that we handle that subtle case properly.
865
866 QFETCH_GLOBAL(qreal, scale);
867 kwinApp()->setXwaylandScale(scale);
868
870 QVERIFY(!xcb_connection_has_error(connection.get()));
871
872 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
873
874 const QRect windowGeometry(0, 0, 100, 200);
875 xcb_size_hints_t hints;
876 memset(&hints, 0, sizeof(hints));
877 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
878 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
879
880 // Create the first test window.
881 const xcb_window_t windowId1 = xcb_generate_id(connection.get());
882 xcb_create_window(connection.get(), XCB_COPY_FROM_PARENT, windowId1, rootWindow(),
883 windowGeometry.x(), windowGeometry.y(),
884 windowGeometry.width(), windowGeometry.height(),
885 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
886 xcb_icccm_set_wm_normal_hints(connection.get(), windowId1, &hints);
887 xcb_change_property(connection.get(), XCB_PROP_MODE_REPLACE, windowId1,
888 atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId1);
889 xcb_map_window(connection.get(), windowId1);
890 xcb_flush(connection.get());
891 QVERIFY(windowCreatedSpy.wait());
892 X11Window *window1 = windowCreatedSpy.first().first().value<X11Window *>();
893 QVERIFY(window1);
894 QCOMPARE(window1->window(), windowId1);
895 QCOMPARE(window1->isActive(), true);
896
897 // Create the second test window.
898 const xcb_window_t windowId2 = xcb_generate_id(connection.get());
899 xcb_create_window(connection.get(), XCB_COPY_FROM_PARENT, windowId2, rootWindow(),
900 windowGeometry.x(), windowGeometry.y(),
901 windowGeometry.width(), windowGeometry.height(),
902 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
903 xcb_icccm_set_wm_normal_hints(connection.get(), windowId2, &hints);
904 xcb_change_property(connection.get(), XCB_PROP_MODE_REPLACE, windowId2,
905 atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId2);
906 xcb_map_window(connection.get(), windowId2);
907 xcb_flush(connection.get());
908 QVERIFY(windowCreatedSpy.wait());
909 X11Window *window2 = windowCreatedSpy.last().first().value<X11Window *>();
910 QVERIFY(window2);
911 QCOMPARE(window2->window(), windowId2);
912 QCOMPARE(window2->isActive(), true);
913
914 // When the second test window is destroyed, the window manager will attempt to activate the
915 // next window in the focus chain, which is the first window.
916 xcb_set_input_focus(connection.get(), XCB_INPUT_FOCUS_POINTER_ROOT, windowId1, XCB_CURRENT_TIME);
917 xcb_destroy_window(connection.get(), windowId2);
918 xcb_flush(connection.get());
919 QVERIFY(Test::waitForWindowClosed(window2));
920 QVERIFY(window1->isActive());
921
922 // Destroy the first test window.
923 xcb_destroy_window(connection.get(), windowId1);
924 xcb_flush(connection.get());
925 QVERIFY(Test::waitForWindowClosed(window1));
926}
927
928void X11WindowTest::testReentrantMoveResize()
929{
930 // This test verifies that calling moveResize() from a slot connected directly
931 // to the frameGeometryChanged() signal won't cause an infinite recursion.
932
933 QFETCH_GLOBAL(qreal, scale);
934 kwinApp()->setXwaylandScale(scale);
935
936 // Create a test window.
938 QVERIFY(!xcb_connection_has_error(c.get()));
939 const QRect windowGeometry(0, 0, 100, 200);
940 xcb_window_t windowId = xcb_generate_id(c.get());
941 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
942 windowGeometry.x(),
943 windowGeometry.y(),
944 windowGeometry.width(),
945 windowGeometry.height(),
946 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
947 xcb_size_hints_t hints;
948 memset(&hints, 0, sizeof(hints));
949 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
950 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
951 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
952 xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId);
953 xcb_map_window(c.get(), windowId);
954 xcb_flush(c.get());
955
956 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
957 QVERIFY(windowCreatedSpy.wait());
958 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
959 QVERIFY(window);
960 QCOMPARE(window->pos(), QPoint(0, 0));
961
962 // Let's pretend that there is a script that really wants the window to be at (100, 100).
963 connect(window, &Window::frameGeometryChanged, this, [window]() {
964 window->moveResize(QRectF(QPointF(100, 100), window->size()));
965 });
966
967 // Trigger the lambda above.
968 window->move(QPoint(40, 50));
969
970 // Eventually, the window will end up at (100, 100).
971 QCOMPARE(window->pos(), QPoint(100, 100));
972
973 // Destroy the test window.
974 xcb_destroy_window(c.get(), windowId);
975 xcb_flush(c.get());
976 QVERIFY(Test::waitForWindowClosed(window));
977}
978
980#include "x11_window_test.moc"
Xcb::Atom wm_client_leader
Definition atoms.h:30
static Compositor * self()
QPointF pos()
Definition cursor.cpp:204
static Cursors * self()
Definition cursor.cpp:35
Cursor * mouse() const
Definition cursor.h:266
QPointF pos
Definition window.h:79
void moveResize(const QRectF &rect)
Definition window.cpp:3323
void updateInteractiveMoveResize(const QPointF &currentGlobalCursor)
Definition window.cpp:1378
QSizeF size
Definition window.h:84
QSizeF clientSize() const
Definition window.h:1872
bool isInteractiveResize() const
Definition window.h:1114
bool move
Definition window.h:437
bool isActive() const
Definition window.h:882
bool isDecorated() const
Definition window.h:1162
QString caption
Definition window.h:392
KWin::Layer layer
Definition window.h:529
void activateWindow(Window *window, bool force=false)
void windowAdded(KWin::Window *)
void slotWindowFullScreen()
void setActiveWindow(Window *window)
const QList< Window * > windows() const
Definition workspace.h:248
void keyPressEvent(uint key_code, xcb_timestamp_t time)
Definition events.cpp:1321
xcb_window_t window() const
const Group * group() const override
Definition x11window.h:565
bool isFullScreen() const override
Definition x11window.h:580
#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())
XcbConnectionPtr createX11Connection()
QList< KWayland::Client::Output * > outputs
std::unique_ptr< KWayland::Client::Surface > createSurface()
XdgToplevel * createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent=nullptr)
std::unique_ptr< xcb_connection_t, XcbConnectionDeleter > XcbConnectionPtr
bool waitForWindowClosed(Window *window)
RootInfo * rootInfo()
Definition netinfo.h:59
KWIN_EXPORT xcb_window_t rootWindow()
Definition xcb.h:24
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
KWIN_EXPORT xcb_connection_t * connection()
Definition xcb.h:19
@ ActiveLayer
Definition globals.h:170
@ NormalLayer
Definition globals.h:167
KWIN_EXPORT Atoms * atoms
Definition main.cpp:74