File indexing completed on 2024-11-10 04:56:09
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "kwin_wayland_test.h" 0010 0011 #include "atoms.h" 0012 #include "compositor.h" 0013 #include "cursor.h" 0014 #include "effect/effectloader.h" 0015 #include "wayland_server.h" 0016 #include "workspace.h" 0017 #include "x11window.h" 0018 0019 #include <KWayland/Client/surface.h> 0020 0021 #include <netwm.h> 0022 #include <xcb/xcb_icccm.h> 0023 0024 using namespace KWin; 0025 static const QString s_socketName = QStringLiteral("wayland_test_x11_window-0"); 0026 0027 class X11WindowTest : public QObject 0028 { 0029 Q_OBJECT 0030 private Q_SLOTS: 0031 void initTestCase_data(); 0032 void initTestCase(); 0033 void init(); 0034 void cleanup(); 0035 0036 void testMinimumSize(); 0037 void testMaximumSize(); 0038 void testResizeIncrements(); 0039 void testResizeIncrementsNoBaseSize(); 0040 void testTrimCaption_data(); 0041 void testTrimCaption(); 0042 void testFullscreenLayerWithActiveWaylandWindow(); 0043 void testFocusInWithWaylandLastActiveWindow(); 0044 void testCaptionChanges(); 0045 void testCaptionWmName(); 0046 void testFullscreenWindowGroups(); 0047 void testActivateFocusedWindow(); 0048 void testReentrantMoveResize(); 0049 }; 0050 0051 void X11WindowTest::initTestCase_data() 0052 { 0053 QTest::addColumn<qreal>("scale"); 0054 QTest::newRow("normal") << 1.0; 0055 QTest::newRow("scaled2x") << 2.0; 0056 } 0057 0058 void X11WindowTest::initTestCase() 0059 { 0060 qRegisterMetaType<KWin::Window *>(); 0061 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); 0062 QVERIFY(waylandServer()->init(s_socketName)); 0063 Test::setOutputConfig({QRect(0, 0, 1280, 1024)}); 0064 kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); 0065 0066 kwinApp()->start(); 0067 QVERIFY(applicationStartedSpy.wait()); 0068 QVERIFY(KWin::Compositor::self()); 0069 } 0070 0071 void X11WindowTest::init() 0072 { 0073 QVERIFY(Test::setupWaylandConnection()); 0074 } 0075 0076 void X11WindowTest::cleanup() 0077 { 0078 Test::destroyWaylandConnection(); 0079 } 0080 0081 void X11WindowTest::testMinimumSize() 0082 { 0083 // This test verifies that the minimum size constraint is correctly applied. 0084 0085 QFETCH_GLOBAL(qreal, scale); 0086 kwinApp()->setXwaylandScale(scale); 0087 0088 // Create an xcb window. 0089 Test::XcbConnectionPtr c = Test::createX11Connection(); 0090 QVERIFY(!xcb_connection_has_error(c.get())); 0091 const QRect windowGeometry(0, 0, 100, 200); 0092 xcb_window_t windowId = xcb_generate_id(c.get()); 0093 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0094 windowGeometry.x(), 0095 windowGeometry.y(), 0096 windowGeometry.width(), 0097 windowGeometry.height(), 0098 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0099 xcb_size_hints_t hints; 0100 memset(&hints, 0, sizeof(hints)); 0101 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0102 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0103 xcb_icccm_size_hints_set_min_size(&hints, windowGeometry.width(), windowGeometry.height()); 0104 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0105 xcb_map_window(c.get(), windowId); 0106 xcb_flush(c.get()); 0107 0108 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0109 QVERIFY(windowCreatedSpy.wait()); 0110 X11Window *window = windowCreatedSpy.last().first().value<X11Window *>(); 0111 QVERIFY(window); 0112 QVERIFY(window->isDecorated()); 0113 0114 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted); 0115 QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped); 0116 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished); 0117 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); 0118 0119 // Begin resize. 0120 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 0121 QVERIFY(!window->isInteractiveResize()); 0122 workspace()->slotWindowResize(); 0123 QCOMPARE(workspace()->moveResizeWindow(), window); 0124 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1); 0125 QVERIFY(window->isInteractiveResize()); 0126 0127 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos(); 0128 0129 window->keyPressEvent(Qt::Key_Left); 0130 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0131 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(-8, 0)); 0132 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0); 0133 QVERIFY(!frameGeometryChangedSpy.wait(10)); 0134 QCOMPARE(window->clientSize().width(), 100 / scale); 0135 0136 window->keyPressEvent(Qt::Key_Right); 0137 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0138 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos); 0139 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0); 0140 QVERIFY(!frameGeometryChangedSpy.wait(10)); 0141 QCOMPARE(window->clientSize().width(), 100 / scale); 0142 0143 window->keyPressEvent(Qt::Key_Right); 0144 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0145 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0)); 0146 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1); 0147 QVERIFY(frameGeometryChangedSpy.wait()); 0148 // whilst X11 window size goes through scale, the increment is a logical value kwin side 0149 QCOMPARE(window->clientSize().width(), 100 / scale + 8); 0150 0151 window->keyPressEvent(Qt::Key_Up); 0152 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0153 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, -8)); 0154 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1); 0155 QVERIFY(!frameGeometryChangedSpy.wait(10)); 0156 QCOMPARE(window->clientSize().height(), 200 / scale); 0157 0158 window->keyPressEvent(Qt::Key_Down); 0159 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0160 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0)); 0161 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1); 0162 QVERIFY(!frameGeometryChangedSpy.wait(10)); 0163 QCOMPARE(window->clientSize().height(), 200 / scale); 0164 0165 window->keyPressEvent(Qt::Key_Down); 0166 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0167 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 8)); 0168 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2); 0169 QVERIFY(frameGeometryChangedSpy.wait()); 0170 QCOMPARE(window->clientSize().height(), 200 / scale + 8); 0171 0172 // Finish the resize operation. 0173 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 0); 0174 window->keyPressEvent(Qt::Key_Enter); 0175 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1); 0176 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 0177 QVERIFY(!window->isInteractiveResize()); 0178 0179 // Destroy the window. 0180 QSignalSpy windowClosedSpy(window, &X11Window::closed); 0181 xcb_unmap_window(c.get(), windowId); 0182 xcb_destroy_window(c.get(), windowId); 0183 xcb_flush(c.get()); 0184 QVERIFY(windowClosedSpy.wait()); 0185 c.reset(); 0186 } 0187 0188 void X11WindowTest::testMaximumSize() 0189 { 0190 // This test verifies that the maximum size constraint is correctly applied. 0191 QFETCH_GLOBAL(qreal, scale); 0192 kwinApp()->setXwaylandScale(scale); 0193 0194 // Create an xcb window. 0195 Test::XcbConnectionPtr c = Test::createX11Connection(); 0196 QVERIFY(!xcb_connection_has_error(c.get())); 0197 const QRect windowGeometry(0, 0, 100, 200); 0198 xcb_window_t windowId = xcb_generate_id(c.get()); 0199 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0200 windowGeometry.x(), 0201 windowGeometry.y(), 0202 windowGeometry.width(), 0203 windowGeometry.height(), 0204 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0205 xcb_size_hints_t hints; 0206 memset(&hints, 0, sizeof(hints)); 0207 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0208 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0209 xcb_icccm_size_hints_set_max_size(&hints, windowGeometry.width(), windowGeometry.height()); 0210 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0211 xcb_map_window(c.get(), windowId); 0212 xcb_flush(c.get()); 0213 0214 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0215 QVERIFY(windowCreatedSpy.wait()); 0216 X11Window *window = windowCreatedSpy.last().first().value<X11Window *>(); 0217 QVERIFY(window); 0218 QVERIFY(window->isDecorated()); 0219 0220 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted); 0221 QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped); 0222 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished); 0223 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); 0224 0225 // Begin resize. 0226 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 0227 QVERIFY(!window->isInteractiveResize()); 0228 workspace()->slotWindowResize(); 0229 QCOMPARE(workspace()->moveResizeWindow(), window); 0230 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1); 0231 QVERIFY(window->isInteractiveResize()); 0232 0233 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos(); 0234 0235 window->keyPressEvent(Qt::Key_Right); 0236 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0237 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0)); 0238 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0); 0239 QVERIFY(!frameGeometryChangedSpy.wait(10)); 0240 QCOMPARE(window->clientSize().width(), 100 / scale); 0241 0242 window->keyPressEvent(Qt::Key_Left); 0243 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0244 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos); 0245 QVERIFY(!interactiveMoveResizeSteppedSpy.wait(10)); 0246 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0); 0247 QCOMPARE(window->clientSize().width(), 100 / scale); 0248 0249 window->keyPressEvent(Qt::Key_Left); 0250 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0251 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(-8, 0)); 0252 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1); 0253 QVERIFY(frameGeometryChangedSpy.wait()); 0254 QCOMPARE(window->clientSize().width(), 100 / scale - 8); 0255 0256 window->keyPressEvent(Qt::Key_Down); 0257 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0258 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(-8, 8)); 0259 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1); 0260 QVERIFY(!frameGeometryChangedSpy.wait(10)); 0261 QCOMPARE(window->clientSize().height(), 200 / scale); 0262 0263 window->keyPressEvent(Qt::Key_Up); 0264 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0265 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(-8, 0)); 0266 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1); 0267 QVERIFY(!frameGeometryChangedSpy.wait(10)); 0268 QCOMPARE(window->clientSize().height(), 200 / scale); 0269 0270 window->keyPressEvent(Qt::Key_Up); 0271 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0272 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(-8, -8)); 0273 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2); 0274 QVERIFY(frameGeometryChangedSpy.wait()); 0275 QCOMPARE(window->clientSize().height(), 200 / scale - 8); 0276 0277 // Finish the resize operation. 0278 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 0); 0279 window->keyPressEvent(Qt::Key_Enter); 0280 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1); 0281 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 0282 QVERIFY(!window->isInteractiveResize()); 0283 0284 // Destroy the window. 0285 QSignalSpy windowClosedSpy(window, &X11Window::closed); 0286 xcb_unmap_window(c.get(), windowId); 0287 xcb_destroy_window(c.get(), windowId); 0288 xcb_flush(c.get()); 0289 QVERIFY(windowClosedSpy.wait()); 0290 c.reset(); 0291 } 0292 0293 void X11WindowTest::testResizeIncrements() 0294 { 0295 // This test verifies that the resize increments constraint is correctly applied. 0296 QFETCH_GLOBAL(qreal, scale); 0297 kwinApp()->setXwaylandScale(scale); 0298 0299 // Create an xcb window. 0300 Test::XcbConnectionPtr c = Test::createX11Connection(); 0301 QVERIFY(!xcb_connection_has_error(c.get())); 0302 const QRect windowGeometry(0, 0, 100, 200); 0303 xcb_window_t windowId = xcb_generate_id(c.get()); 0304 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0305 windowGeometry.x(), 0306 windowGeometry.y(), 0307 windowGeometry.width(), 0308 windowGeometry.height(), 0309 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0310 xcb_size_hints_t hints; 0311 memset(&hints, 0, sizeof(hints)); 0312 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0313 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0314 xcb_icccm_size_hints_set_base_size(&hints, windowGeometry.width(), windowGeometry.height()); 0315 xcb_icccm_size_hints_set_resize_inc(&hints, 3, 5); 0316 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0317 xcb_map_window(c.get(), windowId); 0318 xcb_flush(c.get()); 0319 0320 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0321 QVERIFY(windowCreatedSpy.wait()); 0322 X11Window *window = windowCreatedSpy.last().first().value<X11Window *>(); 0323 QVERIFY(window); 0324 QVERIFY(window->isDecorated()); 0325 0326 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted); 0327 QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped); 0328 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished); 0329 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); 0330 0331 // Begin resize. 0332 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 0333 QVERIFY(!window->isInteractiveResize()); 0334 workspace()->slotWindowResize(); 0335 QCOMPARE(workspace()->moveResizeWindow(), window); 0336 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1); 0337 QVERIFY(window->isInteractiveResize()); 0338 0339 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos(); 0340 0341 window->keyPressEvent(Qt::Key_Right); 0342 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0343 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0)); 0344 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1); 0345 QVERIFY(frameGeometryChangedSpy.wait()); 0346 0347 // 100 + 8 logical pixels, rounded to resize increments. This will differ on scale 0348 const qreal horizontalResizeInc = 3 / scale; 0349 const qreal verticalResizeInc = 5 / scale; 0350 const qreal expectedHorizontalResizeInc = std::floor(8. / horizontalResizeInc) * horizontalResizeInc; 0351 const qreal expectedVerticalResizeInc = std::floor(8. / verticalResizeInc) * verticalResizeInc; 0352 0353 QCOMPARE(window->clientSize(), QSizeF(100, 200) / scale + QSizeF(expectedHorizontalResizeInc, 0)); 0354 0355 window->keyPressEvent(Qt::Key_Down); 0356 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0357 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 8)); 0358 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2); 0359 QVERIFY(frameGeometryChangedSpy.wait()); 0360 QCOMPARE(window->clientSize(), QSize(100, 200) / scale + QSizeF(expectedHorizontalResizeInc, expectedVerticalResizeInc)); 0361 0362 // Finish the resize operation. 0363 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 0); 0364 window->keyPressEvent(Qt::Key_Enter); 0365 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1); 0366 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 0367 QVERIFY(!window->isInteractiveResize()); 0368 0369 // Destroy the window. 0370 QSignalSpy windowClosedSpy(window, &X11Window::closed); 0371 xcb_unmap_window(c.get(), windowId); 0372 xcb_destroy_window(c.get(), windowId); 0373 xcb_flush(c.get()); 0374 QVERIFY(windowClosedSpy.wait()); 0375 c.reset(); 0376 } 0377 0378 void X11WindowTest::testResizeIncrementsNoBaseSize() 0379 { 0380 QFETCH_GLOBAL(qreal, scale); 0381 kwinApp()->setXwaylandScale(scale); 0382 0383 // Create an xcb window. 0384 Test::XcbConnectionPtr c = Test::createX11Connection(); 0385 QVERIFY(!xcb_connection_has_error(c.get())); 0386 const QRect windowGeometry(0, 0, 100, 200); 0387 xcb_window_t windowId = xcb_generate_id(c.get()); 0388 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0389 windowGeometry.x(), 0390 windowGeometry.y(), 0391 windowGeometry.width(), 0392 windowGeometry.height(), 0393 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0394 xcb_size_hints_t hints; 0395 memset(&hints, 0, sizeof(hints)); 0396 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0397 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0398 xcb_icccm_size_hints_set_min_size(&hints, windowGeometry.width(), windowGeometry.height()); 0399 xcb_icccm_size_hints_set_resize_inc(&hints, 3, 5); 0400 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0401 xcb_map_window(c.get(), windowId); 0402 xcb_flush(c.get()); 0403 0404 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0405 QVERIFY(windowCreatedSpy.wait()); 0406 X11Window *window = windowCreatedSpy.last().first().value<X11Window *>(); 0407 QVERIFY(window); 0408 QVERIFY(window->isDecorated()); 0409 0410 QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted); 0411 QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped); 0412 QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished); 0413 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); 0414 0415 // Begin resize. 0416 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 0417 QVERIFY(!window->isInteractiveResize()); 0418 workspace()->slotWindowResize(); 0419 QCOMPARE(workspace()->moveResizeWindow(), window); 0420 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1); 0421 QVERIFY(window->isInteractiveResize()); 0422 0423 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos(); 0424 0425 window->keyPressEvent(Qt::Key_Right); 0426 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0427 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0)); 0428 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1); 0429 QVERIFY(frameGeometryChangedSpy.wait()); 0430 0431 // 100 + 8 pixels, rounded to resize increments. This will differ on scale 0432 const qreal horizontalResizeInc = 3 / scale; 0433 const qreal verticalResizeInc = 5 / scale; 0434 const qreal expectedHorizontalResizeInc = std::floor(8. / horizontalResizeInc) * horizontalResizeInc; 0435 const qreal expectedVerticalResizeInc = std::floor(8. / verticalResizeInc) * verticalResizeInc; 0436 0437 QCOMPARE(window->clientSize(), QSizeF(100, 200) / scale + QSizeF(expectedHorizontalResizeInc, 0)); 0438 0439 window->keyPressEvent(Qt::Key_Down); 0440 window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos()); 0441 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 8)); 0442 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2); 0443 QVERIFY(frameGeometryChangedSpy.wait()); 0444 QCOMPARE(window->clientSize(), QSizeF(100, 200) / scale + QSizeF(expectedHorizontalResizeInc, expectedVerticalResizeInc)); 0445 0446 // Finish the resize operation. 0447 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 0); 0448 window->keyPressEvent(Qt::Key_Enter); 0449 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1); 0450 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 0451 QVERIFY(!window->isInteractiveResize()); 0452 0453 // Destroy the window. 0454 QSignalSpy windowClosedSpy(window, &X11Window::closed); 0455 xcb_unmap_window(c.get(), windowId); 0456 xcb_destroy_window(c.get(), windowId); 0457 xcb_flush(c.get()); 0458 QVERIFY(windowClosedSpy.wait()); 0459 c.reset(); 0460 } 0461 0462 void X11WindowTest::testTrimCaption_data() 0463 { 0464 QFETCH_GLOBAL(qreal, scale); 0465 kwinApp()->setXwaylandScale(scale); 0466 0467 QTest::addColumn<QByteArray>("originalTitle"); 0468 QTest::addColumn<QByteArray>("expectedTitle"); 0469 0470 QTest::newRow("simplified") 0471 << QByteArrayLiteral("Was tun, wenn Schüler Autismus haben?\342\200\250\342\200\250\342\200\250 – Marlies Hübner - Mozilla Firefox") 0472 << QByteArrayLiteral("Was tun, wenn Schüler Autismus haben? – Marlies Hübner - Mozilla Firefox"); 0473 0474 QTest::newRow("with emojis") 0475 << QByteArrayLiteral("\bTesting non\302\255printable:\177, emoij:\360\237\230\203, non-characters:\357\277\276") 0476 << QByteArrayLiteral("Testing nonprintable:, emoij:\360\237\230\203, non-characters:"); 0477 } 0478 0479 void X11WindowTest::testTrimCaption() 0480 { 0481 QFETCH_GLOBAL(qreal, scale); 0482 kwinApp()->setXwaylandScale(scale); 0483 0484 // this test verifies that caption is properly trimmed 0485 0486 // create an xcb window 0487 Test::XcbConnectionPtr c = Test::createX11Connection(); 0488 QVERIFY(!xcb_connection_has_error(c.get())); 0489 const QRect windowGeometry(0, 0, 100, 200); 0490 xcb_window_t windowId = xcb_generate_id(c.get()); 0491 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0492 windowGeometry.x(), 0493 windowGeometry.y(), 0494 windowGeometry.width(), 0495 windowGeometry.height(), 0496 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0497 xcb_size_hints_t hints; 0498 memset(&hints, 0, sizeof(hints)); 0499 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0500 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0501 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0502 NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::Properties(), NET::Properties2()); 0503 QFETCH(QByteArray, originalTitle); 0504 winInfo.setName(originalTitle); 0505 xcb_map_window(c.get(), windowId); 0506 xcb_flush(c.get()); 0507 0508 // we should get a window for it 0509 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0510 QVERIFY(windowCreatedSpy.wait()); 0511 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>(); 0512 QVERIFY(window); 0513 QCOMPARE(window->window(), windowId); 0514 QFETCH(QByteArray, expectedTitle); 0515 QCOMPARE(window->caption(), QString::fromUtf8(expectedTitle)); 0516 0517 // and destroy the window again 0518 xcb_unmap_window(c.get(), windowId); 0519 xcb_flush(c.get()); 0520 0521 QSignalSpy windowClosedSpy(window, &X11Window::closed); 0522 QVERIFY(windowClosedSpy.wait()); 0523 xcb_destroy_window(c.get(), windowId); 0524 c.reset(); 0525 } 0526 0527 void X11WindowTest::testFullscreenLayerWithActiveWaylandWindow() 0528 { 0529 QFETCH_GLOBAL(qreal, scale); 0530 kwinApp()->setXwaylandScale(scale); 0531 0532 // this test verifies that an X11 fullscreen window does not stay in the active layer 0533 // when a Wayland window is active, see BUG: 375759 0534 QCOMPARE(workspace()->outputs().count(), 1); 0535 0536 // first create an X11 window 0537 Test::XcbConnectionPtr c = Test::createX11Connection(); 0538 QVERIFY(!xcb_connection_has_error(c.get())); 0539 const QRect windowGeometry(0, 0, 100, 200); 0540 xcb_window_t windowId = xcb_generate_id(c.get()); 0541 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0542 windowGeometry.x(), 0543 windowGeometry.y(), 0544 windowGeometry.width(), 0545 windowGeometry.height(), 0546 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0547 xcb_size_hints_t hints; 0548 memset(&hints, 0, sizeof(hints)); 0549 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0550 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0551 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0552 xcb_map_window(c.get(), windowId); 0553 xcb_flush(c.get()); 0554 0555 // we should get a window for it 0556 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0557 QVERIFY(windowCreatedSpy.wait()); 0558 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>(); 0559 QVERIFY(window); 0560 QCOMPARE(window->window(), windowId); 0561 QVERIFY(!window->isFullScreen()); 0562 QVERIFY(window->isActive()); 0563 QCOMPARE(window->layer(), NormalLayer); 0564 0565 workspace()->slotWindowFullScreen(); 0566 QVERIFY(window->isFullScreen()); 0567 QCOMPARE(window->layer(), ActiveLayer); 0568 QCOMPARE(workspace()->stackingOrder().last(), window); 0569 0570 // now let's open a Wayland window 0571 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0572 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0573 auto waylandWindow = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0574 QVERIFY(waylandWindow); 0575 QVERIFY(waylandWindow->isActive()); 0576 QCOMPARE(waylandWindow->layer(), NormalLayer); 0577 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow); 0578 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow); 0579 QCOMPARE(window->layer(), NormalLayer); 0580 0581 // now activate fullscreen again 0582 workspace()->activateWindow(window); 0583 QTRY_VERIFY(window->isActive()); 0584 QCOMPARE(window->layer(), ActiveLayer); 0585 QCOMPARE(workspace()->stackingOrder().last(), window); 0586 QCOMPARE(workspace()->stackingOrder().last(), window); 0587 0588 // activate wayland window again 0589 workspace()->activateWindow(waylandWindow); 0590 QTRY_VERIFY(waylandWindow->isActive()); 0591 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow); 0592 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow); 0593 0594 // back to x window 0595 workspace()->activateWindow(window); 0596 QTRY_VERIFY(window->isActive()); 0597 // remove fullscreen 0598 QVERIFY(window->isFullScreen()); 0599 workspace()->slotWindowFullScreen(); 0600 QVERIFY(!window->isFullScreen()); 0601 // and fullscreen again 0602 workspace()->slotWindowFullScreen(); 0603 QVERIFY(window->isFullScreen()); 0604 QCOMPARE(workspace()->stackingOrder().last(), window); 0605 QCOMPARE(workspace()->stackingOrder().last(), window); 0606 0607 // activate wayland window again 0608 workspace()->activateWindow(waylandWindow); 0609 QTRY_VERIFY(waylandWindow->isActive()); 0610 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow); 0611 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow); 0612 0613 // back to X11 window 0614 workspace()->activateWindow(window); 0615 QTRY_VERIFY(window->isActive()); 0616 // remove fullscreen 0617 QVERIFY(window->isFullScreen()); 0618 workspace()->slotWindowFullScreen(); 0619 QVERIFY(!window->isFullScreen()); 0620 // and fullscreen through X API 0621 NETWinInfo info(c.get(), windowId, kwinApp()->x11RootWindow(), NET::Properties(), NET::Properties2()); 0622 info.setState(NET::FullScreen, NET::FullScreen); 0623 NETRootInfo rootInfo(c.get(), NET::Properties()); 0624 rootInfo.setActiveWindow(windowId, NET::FromApplication, XCB_CURRENT_TIME, XCB_WINDOW_NONE); 0625 xcb_flush(c.get()); 0626 QTRY_VERIFY(window->isFullScreen()); 0627 QCOMPARE(workspace()->stackingOrder().last(), window); 0628 QCOMPARE(workspace()->stackingOrder().last(), window); 0629 0630 // activate wayland window again 0631 workspace()->activateWindow(waylandWindow); 0632 QTRY_VERIFY(waylandWindow->isActive()); 0633 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow); 0634 QCOMPARE(workspace()->stackingOrder().last(), waylandWindow); 0635 QCOMPARE(window->layer(), NormalLayer); 0636 0637 // close the window 0638 shellSurface.reset(); 0639 surface.reset(); 0640 QVERIFY(Test::waitForWindowClosed(waylandWindow)); 0641 QTRY_VERIFY(window->isActive()); 0642 QCOMPARE(window->layer(), ActiveLayer); 0643 0644 // and destroy the window again 0645 xcb_unmap_window(c.get(), windowId); 0646 xcb_flush(c.get()); 0647 } 0648 0649 void X11WindowTest::testFocusInWithWaylandLastActiveWindow() 0650 { 0651 // this test verifies that Workspace::allowWindowActivation does not crash if last client was a Wayland client 0652 QFETCH_GLOBAL(qreal, scale); 0653 kwinApp()->setXwaylandScale(scale); 0654 0655 // create an X11 window 0656 Test::XcbConnectionPtr c = Test::createX11Connection(); 0657 QVERIFY(!xcb_connection_has_error(c.get())); 0658 const QRect windowGeometry(0, 0, 100, 200); 0659 xcb_window_t windowId = xcb_generate_id(c.get()); 0660 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0661 windowGeometry.x(), 0662 windowGeometry.y(), 0663 windowGeometry.width(), 0664 windowGeometry.height(), 0665 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0666 xcb_size_hints_t hints; 0667 memset(&hints, 0, sizeof(hints)); 0668 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0669 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0670 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0671 xcb_map_window(c.get(), windowId); 0672 xcb_flush(c.get()); 0673 0674 // we should get a window for it 0675 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0676 QVERIFY(windowCreatedSpy.wait()); 0677 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>(); 0678 QVERIFY(window); 0679 QCOMPARE(window->window(), windowId); 0680 QVERIFY(window->isActive()); 0681 0682 // create Wayland window 0683 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0684 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0685 auto waylandWindow = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0686 QVERIFY(waylandWindow); 0687 QVERIFY(waylandWindow->isActive()); 0688 // activate no window 0689 workspace()->setActiveWindow(nullptr); 0690 QVERIFY(!waylandWindow->isActive()); 0691 QVERIFY(!workspace()->activeWindow()); 0692 // and close Wayland window again 0693 shellSurface.reset(); 0694 surface.reset(); 0695 QVERIFY(Test::waitForWindowClosed(waylandWindow)); 0696 0697 // and try to activate the x11 window through X11 api 0698 const auto cookie = xcb_set_input_focus_checked(c.get(), XCB_INPUT_FOCUS_NONE, windowId, XCB_CURRENT_TIME); 0699 auto error = xcb_request_check(c.get(), cookie); 0700 QVERIFY(!error); 0701 // this accesses m_lastActiveWindow on trying to activate 0702 QTRY_VERIFY(window->isActive()); 0703 0704 // and destroy the window again 0705 xcb_unmap_window(c.get(), windowId); 0706 xcb_flush(c.get()); 0707 } 0708 0709 void X11WindowTest::testCaptionChanges() 0710 { 0711 QFETCH_GLOBAL(qreal, scale); 0712 kwinApp()->setXwaylandScale(scale); 0713 0714 // verifies that caption is updated correctly when the X11 window updates it 0715 // BUG: 383444 0716 Test::XcbConnectionPtr c = Test::createX11Connection(); 0717 QVERIFY(!xcb_connection_has_error(c.get())); 0718 const QRect windowGeometry(0, 0, 100, 200); 0719 xcb_window_t windowId = xcb_generate_id(c.get()); 0720 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0721 windowGeometry.x(), 0722 windowGeometry.y(), 0723 windowGeometry.width(), 0724 windowGeometry.height(), 0725 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0726 xcb_size_hints_t hints; 0727 memset(&hints, 0, sizeof(hints)); 0728 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0729 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0730 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0731 NETWinInfo info(c.get(), windowId, kwinApp()->x11RootWindow(), NET::Properties(), NET::Properties2()); 0732 info.setName("foo"); 0733 xcb_map_window(c.get(), windowId); 0734 xcb_flush(c.get()); 0735 0736 // we should get a window for it 0737 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0738 QVERIFY(windowCreatedSpy.wait()); 0739 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>(); 0740 QVERIFY(window); 0741 QCOMPARE(window->window(), windowId); 0742 QCOMPARE(window->caption(), QStringLiteral("foo")); 0743 0744 QSignalSpy captionChangedSpy(window, &X11Window::captionChanged); 0745 info.setName("bar"); 0746 xcb_flush(c.get()); 0747 QVERIFY(captionChangedSpy.wait()); 0748 QCOMPARE(window->caption(), QStringLiteral("bar")); 0749 0750 // and destroy the window again 0751 QSignalSpy windowClosedSpy(window, &X11Window::closed); 0752 xcb_unmap_window(c.get(), windowId); 0753 xcb_flush(c.get()); 0754 QVERIFY(windowClosedSpy.wait()); 0755 xcb_destroy_window(c.get(), windowId); 0756 c.reset(); 0757 } 0758 0759 void X11WindowTest::testCaptionWmName() 0760 { 0761 QFETCH_GLOBAL(qreal, scale); 0762 kwinApp()->setXwaylandScale(scale); 0763 0764 // this test verifies that a caption set through WM_NAME is read correctly 0765 0766 // open glxgears as that one only uses WM_NAME 0767 QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded); 0768 0769 QProcess glxgears; 0770 glxgears.setProgram(QStringLiteral("glxgears")); 0771 glxgears.start(); 0772 QVERIFY(glxgears.waitForStarted()); 0773 0774 QVERIFY(windowAddedSpy.wait()); 0775 QCOMPARE(windowAddedSpy.count(), 1); 0776 QCOMPARE(workspace()->windows().count(), 1); 0777 Window *glxgearsWindow = workspace()->windows().first(); 0778 QCOMPARE(glxgearsWindow->caption(), QStringLiteral("glxgears")); 0779 0780 glxgears.terminate(); 0781 QVERIFY(glxgears.waitForFinished()); 0782 } 0783 0784 void X11WindowTest::testFullscreenWindowGroups() 0785 { 0786 // this test creates an X11 window and puts it to full screen 0787 // then a second window is created which is in the same window group 0788 // BUG: 388310 0789 0790 QFETCH_GLOBAL(qreal, scale); 0791 kwinApp()->setXwaylandScale(scale); 0792 0793 Test::XcbConnectionPtr c = Test::createX11Connection(); 0794 QVERIFY(!xcb_connection_has_error(c.get())); 0795 const QRect windowGeometry(0, 0, 100, 200); 0796 xcb_window_t windowId = xcb_generate_id(c.get()); 0797 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0798 windowGeometry.x(), 0799 windowGeometry.y(), 0800 windowGeometry.width(), 0801 windowGeometry.height(), 0802 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0803 xcb_size_hints_t hints; 0804 memset(&hints, 0, sizeof(hints)); 0805 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0806 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0807 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0808 xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId); 0809 xcb_map_window(c.get(), windowId); 0810 xcb_flush(c.get()); 0811 0812 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0813 QVERIFY(windowCreatedSpy.wait()); 0814 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>(); 0815 QVERIFY(window); 0816 QCOMPARE(window->window(), windowId); 0817 QCOMPARE(window->isActive(), true); 0818 0819 QCOMPARE(window->isFullScreen(), false); 0820 QCOMPARE(window->layer(), NormalLayer); 0821 workspace()->slotWindowFullScreen(); 0822 QCOMPARE(window->isFullScreen(), true); 0823 QCOMPARE(window->layer(), ActiveLayer); 0824 0825 // now let's create a second window 0826 windowCreatedSpy.clear(); 0827 xcb_window_t w2 = xcb_generate_id(c.get()); 0828 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, w2, rootWindow(), 0829 windowGeometry.x(), 0830 windowGeometry.y(), 0831 windowGeometry.width(), 0832 windowGeometry.height(), 0833 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0834 xcb_size_hints_t hints2; 0835 memset(&hints2, 0, sizeof(hints2)); 0836 xcb_icccm_size_hints_set_position(&hints2, 1, windowGeometry.x(), windowGeometry.y()); 0837 xcb_icccm_size_hints_set_size(&hints2, 1, windowGeometry.width(), windowGeometry.height()); 0838 xcb_icccm_set_wm_normal_hints(c.get(), w2, &hints2); 0839 xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, w2, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId); 0840 xcb_map_window(c.get(), w2); 0841 xcb_flush(c.get()); 0842 0843 QVERIFY(windowCreatedSpy.wait()); 0844 X11Window *window2 = windowCreatedSpy.first().first().value<X11Window *>(); 0845 QVERIFY(window2); 0846 QVERIFY(window != window2); 0847 QCOMPARE(window2->window(), w2); 0848 QCOMPARE(window2->isActive(), true); 0849 QCOMPARE(window2->group(), window->group()); 0850 // first window should be moved back to normal layer 0851 QCOMPARE(window->isActive(), false); 0852 QCOMPARE(window->isFullScreen(), true); 0853 QCOMPARE(window->layer(), NormalLayer); 0854 0855 // activating the fullscreen window again, should move it to active layer 0856 workspace()->activateWindow(window); 0857 QTRY_COMPARE(window->layer(), ActiveLayer); 0858 } 0859 0860 void X11WindowTest::testActivateFocusedWindow() 0861 { 0862 // The window manager may call XSetInputFocus() on a window that already has focus, in which 0863 // case no FocusIn event will be generated and the window won't be marked as active. This test 0864 // verifies that we handle that subtle case properly. 0865 0866 QFETCH_GLOBAL(qreal, scale); 0867 kwinApp()->setXwaylandScale(scale); 0868 0869 Test::XcbConnectionPtr connection = Test::createX11Connection(); 0870 QVERIFY(!xcb_connection_has_error(connection.get())); 0871 0872 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0873 0874 const QRect windowGeometry(0, 0, 100, 200); 0875 xcb_size_hints_t hints; 0876 memset(&hints, 0, sizeof(hints)); 0877 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0878 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0879 0880 // Create the first test window. 0881 const xcb_window_t windowId1 = xcb_generate_id(connection.get()); 0882 xcb_create_window(connection.get(), XCB_COPY_FROM_PARENT, windowId1, rootWindow(), 0883 windowGeometry.x(), windowGeometry.y(), 0884 windowGeometry.width(), windowGeometry.height(), 0885 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0886 xcb_icccm_set_wm_normal_hints(connection.get(), windowId1, &hints); 0887 xcb_change_property(connection.get(), XCB_PROP_MODE_REPLACE, windowId1, 0888 atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId1); 0889 xcb_map_window(connection.get(), windowId1); 0890 xcb_flush(connection.get()); 0891 QVERIFY(windowCreatedSpy.wait()); 0892 X11Window *window1 = windowCreatedSpy.first().first().value<X11Window *>(); 0893 QVERIFY(window1); 0894 QCOMPARE(window1->window(), windowId1); 0895 QCOMPARE(window1->isActive(), true); 0896 0897 // Create the second test window. 0898 const xcb_window_t windowId2 = xcb_generate_id(connection.get()); 0899 xcb_create_window(connection.get(), XCB_COPY_FROM_PARENT, windowId2, rootWindow(), 0900 windowGeometry.x(), windowGeometry.y(), 0901 windowGeometry.width(), windowGeometry.height(), 0902 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0903 xcb_icccm_set_wm_normal_hints(connection.get(), windowId2, &hints); 0904 xcb_change_property(connection.get(), XCB_PROP_MODE_REPLACE, windowId2, 0905 atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId2); 0906 xcb_map_window(connection.get(), windowId2); 0907 xcb_flush(connection.get()); 0908 QVERIFY(windowCreatedSpy.wait()); 0909 X11Window *window2 = windowCreatedSpy.last().first().value<X11Window *>(); 0910 QVERIFY(window2); 0911 QCOMPARE(window2->window(), windowId2); 0912 QCOMPARE(window2->isActive(), true); 0913 0914 // When the second test window is destroyed, the window manager will attempt to activate the 0915 // next window in the focus chain, which is the first window. 0916 xcb_set_input_focus(connection.get(), XCB_INPUT_FOCUS_POINTER_ROOT, windowId1, XCB_CURRENT_TIME); 0917 xcb_destroy_window(connection.get(), windowId2); 0918 xcb_flush(connection.get()); 0919 QVERIFY(Test::waitForWindowClosed(window2)); 0920 QVERIFY(window1->isActive()); 0921 0922 // Destroy the first test window. 0923 xcb_destroy_window(connection.get(), windowId1); 0924 xcb_flush(connection.get()); 0925 QVERIFY(Test::waitForWindowClosed(window1)); 0926 } 0927 0928 void X11WindowTest::testReentrantMoveResize() 0929 { 0930 // This test verifies that calling moveResize() from a slot connected directly 0931 // to the frameGeometryChanged() signal won't cause an infinite recursion. 0932 0933 QFETCH_GLOBAL(qreal, scale); 0934 kwinApp()->setXwaylandScale(scale); 0935 0936 // Create a test window. 0937 Test::XcbConnectionPtr c = Test::createX11Connection(); 0938 QVERIFY(!xcb_connection_has_error(c.get())); 0939 const QRect windowGeometry(0, 0, 100, 200); 0940 xcb_window_t windowId = xcb_generate_id(c.get()); 0941 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0942 windowGeometry.x(), 0943 windowGeometry.y(), 0944 windowGeometry.width(), 0945 windowGeometry.height(), 0946 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0947 xcb_size_hints_t hints; 0948 memset(&hints, 0, sizeof(hints)); 0949 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0950 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0951 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0952 xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId); 0953 xcb_map_window(c.get(), windowId); 0954 xcb_flush(c.get()); 0955 0956 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0957 QVERIFY(windowCreatedSpy.wait()); 0958 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>(); 0959 QVERIFY(window); 0960 QCOMPARE(window->pos(), QPoint(0, 0)); 0961 0962 // Let's pretend that there is a script that really wants the window to be at (100, 100). 0963 connect(window, &Window::frameGeometryChanged, this, [window]() { 0964 window->moveResize(QRectF(QPointF(100, 100), window->size())); 0965 }); 0966 0967 // Trigger the lambda above. 0968 window->move(QPoint(40, 50)); 0969 0970 // Eventually, the window will end up at (100, 100). 0971 QCOMPARE(window->pos(), QPoint(100, 100)); 0972 0973 // Destroy the test window. 0974 xcb_destroy_window(c.get(), windowId); 0975 xcb_flush(c.get()); 0976 QVERIFY(Test::waitForWindowClosed(window)); 0977 } 0978 0979 WAYLANDTEST_MAIN(X11WindowTest) 0980 #include "x11_window_test.moc"