File indexing completed on 2025-03-23 13:47:57
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 "core/output.h" 0012 #include "core/outputbackend.h" 0013 #include "cursor.h" 0014 #include "deleted.h" 0015 #include "effects.h" 0016 #include "internalwindow.h" 0017 #include "wayland/surface_interface.h" 0018 #include "wayland_server.h" 0019 #include "workspace.h" 0020 0021 #include <QPainter> 0022 #include <QRasterWindow> 0023 0024 #include <KWayland/Client/keyboard.h> 0025 #include <KWayland/Client/seat.h> 0026 #include <KWayland/Client/surface.h> 0027 #include <KWindowSystem> 0028 0029 #include <linux/input.h> 0030 0031 Q_DECLARE_METATYPE(NET::WindowType); 0032 0033 namespace KWin 0034 { 0035 0036 static const QString s_socketName = QStringLiteral("wayland_test_kwin_internal_window-0"); 0037 0038 class InternalWindowTest : public QObject 0039 { 0040 Q_OBJECT 0041 private Q_SLOTS: 0042 void initTestCase(); 0043 void init(); 0044 void cleanup(); 0045 void testEnterLeave(); 0046 void testPointerPressRelease(); 0047 void testPointerAxis(); 0048 void testKeyboard_data(); 0049 void testKeyboard(); 0050 void testKeyboardShowWithoutActivating(); 0051 void testKeyboardTriggersLeave(); 0052 void testTouch(); 0053 void testOpacity(); 0054 void testMove(); 0055 void testSkipCloseAnimation_data(); 0056 void testSkipCloseAnimation(); 0057 void testModifierClickUnrestrictedMove(); 0058 void testModifierScroll(); 0059 void testPopup(); 0060 void testScale(); 0061 void testWindowType_data(); 0062 void testWindowType(); 0063 void testChangeWindowType_data(); 0064 void testChangeWindowType(); 0065 void testEffectWindow(); 0066 void testReentrantMoveResize(); 0067 void testDismissPopup(); 0068 }; 0069 0070 class HelperWindow : public QRasterWindow 0071 { 0072 Q_OBJECT 0073 public: 0074 HelperWindow(); 0075 ~HelperWindow() override; 0076 0077 QPoint latestGlobalMousePos() const 0078 { 0079 return m_latestGlobalMousePos; 0080 } 0081 Qt::MouseButtons pressedButtons() const 0082 { 0083 return m_pressedButtons; 0084 } 0085 0086 Q_SIGNALS: 0087 void entered(); 0088 void left(); 0089 void mouseMoved(const QPoint &global); 0090 void mousePressed(); 0091 void mouseReleased(); 0092 void wheel(); 0093 void keyPressed(); 0094 void keyReleased(); 0095 0096 protected: 0097 void paintEvent(QPaintEvent *event) override; 0098 bool event(QEvent *event) override; 0099 void mouseMoveEvent(QMouseEvent *event) override; 0100 void mousePressEvent(QMouseEvent *event) override; 0101 void mouseReleaseEvent(QMouseEvent *event) override; 0102 void wheelEvent(QWheelEvent *event) override; 0103 void keyPressEvent(QKeyEvent *event) override; 0104 void keyReleaseEvent(QKeyEvent *event) override; 0105 0106 private: 0107 QPoint m_latestGlobalMousePos; 0108 Qt::MouseButtons m_pressedButtons = Qt::MouseButtons(); 0109 }; 0110 0111 HelperWindow::HelperWindow() 0112 : QRasterWindow(nullptr) 0113 { 0114 setFlags(Qt::FramelessWindowHint); 0115 } 0116 0117 HelperWindow::~HelperWindow() = default; 0118 0119 void HelperWindow::paintEvent(QPaintEvent *event) 0120 { 0121 QPainter p(this); 0122 p.fillRect(0, 0, width(), height(), Qt::red); 0123 } 0124 0125 bool HelperWindow::event(QEvent *event) 0126 { 0127 if (event->type() == QEvent::Enter) { 0128 Q_EMIT entered(); 0129 } 0130 if (event->type() == QEvent::Leave) { 0131 Q_EMIT left(); 0132 } 0133 return QRasterWindow::event(event); 0134 } 0135 0136 void HelperWindow::mouseMoveEvent(QMouseEvent *event) 0137 { 0138 m_latestGlobalMousePos = event->globalPos(); 0139 Q_EMIT mouseMoved(event->globalPos()); 0140 } 0141 0142 void HelperWindow::mousePressEvent(QMouseEvent *event) 0143 { 0144 m_latestGlobalMousePos = event->globalPos(); 0145 m_pressedButtons = event->buttons(); 0146 Q_EMIT mousePressed(); 0147 } 0148 0149 void HelperWindow::mouseReleaseEvent(QMouseEvent *event) 0150 { 0151 m_latestGlobalMousePos = event->globalPos(); 0152 m_pressedButtons = event->buttons(); 0153 Q_EMIT mouseReleased(); 0154 } 0155 0156 void HelperWindow::wheelEvent(QWheelEvent *event) 0157 { 0158 Q_EMIT wheel(); 0159 } 0160 0161 void HelperWindow::keyPressEvent(QKeyEvent *event) 0162 { 0163 Q_EMIT keyPressed(); 0164 } 0165 0166 void HelperWindow::keyReleaseEvent(QKeyEvent *event) 0167 { 0168 Q_EMIT keyReleased(); 0169 } 0170 0171 void InternalWindowTest::initTestCase() 0172 { 0173 qRegisterMetaType<KWin::Window *>(); 0174 qRegisterMetaType<KWin::Deleted *>(); 0175 qRegisterMetaType<KWin::InternalWindow *>(); 0176 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); 0177 QVERIFY(waylandServer()->init(s_socketName)); 0178 QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024))); 0179 kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); 0180 0181 kwinApp()->start(); 0182 QVERIFY(applicationStartedSpy.wait()); 0183 const auto outputs = workspace()->outputs(); 0184 QCOMPARE(outputs.count(), 2); 0185 QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024)); 0186 QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024)); 0187 } 0188 0189 void InternalWindowTest::init() 0190 { 0191 Cursors::self()->mouse()->setPos(QPoint(512, 512)); 0192 QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat)); 0193 QVERIFY(Test::waitForWaylandKeyboard()); 0194 } 0195 0196 void InternalWindowTest::cleanup() 0197 { 0198 Test::destroyWaylandConnection(); 0199 } 0200 0201 void InternalWindowTest::testEnterLeave() 0202 { 0203 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0204 HelperWindow win; 0205 QVERIFY(!workspace()->findInternal(nullptr)); 0206 QVERIFY(!workspace()->findInternal(&win)); 0207 win.setGeometry(0, 0, 100, 100); 0208 win.show(); 0209 0210 QTRY_COMPARE(windowAddedSpy.count(), 1); 0211 QVERIFY(!workspace()->activeWindow()); 0212 InternalWindow *window = windowAddedSpy.first().first().value<InternalWindow *>(); 0213 QVERIFY(window); 0214 QVERIFY(window->isInternal()); 0215 QVERIFY(!window->isDecorated()); 0216 QCOMPARE(workspace()->findInternal(&win), window); 0217 QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 100)); 0218 QVERIFY(window->isShown()); 0219 QVERIFY(workspace()->stackingOrder().contains(window)); 0220 0221 QSignalSpy enterSpy(&win, &HelperWindow::entered); 0222 QSignalSpy leaveSpy(&win, &HelperWindow::left); 0223 QSignalSpy moveSpy(&win, &HelperWindow::mouseMoved); 0224 0225 quint32 timestamp = 1; 0226 Test::pointerMotion(QPoint(50, 50), timestamp++); 0227 QTRY_COMPARE(moveSpy.count(), 1); 0228 0229 Test::pointerMotion(QPoint(60, 50), timestamp++); 0230 QTRY_COMPARE(moveSpy.count(), 2); 0231 QCOMPARE(moveSpy[1].first().toPoint(), QPoint(60, 50)); 0232 0233 Test::pointerMotion(QPoint(101, 50), timestamp++); 0234 QTRY_COMPARE(leaveSpy.count(), 1); 0235 0236 // set a mask on the window 0237 win.setMask(QRegion(10, 20, 30, 40)); 0238 // outside the mask we should not get an enter 0239 Test::pointerMotion(QPoint(5, 5), timestamp++); 0240 QVERIFY(!enterSpy.wait(100)); 0241 QCOMPARE(enterSpy.count(), 1); 0242 // inside the mask we should still get an enter 0243 Test::pointerMotion(QPoint(25, 27), timestamp++); 0244 QTRY_COMPARE(enterSpy.count(), 2); 0245 } 0246 0247 void InternalWindowTest::testPointerPressRelease() 0248 { 0249 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0250 HelperWindow win; 0251 win.setGeometry(0, 0, 100, 100); 0252 win.show(); 0253 QSignalSpy pressSpy(&win, &HelperWindow::mousePressed); 0254 QSignalSpy releaseSpy(&win, &HelperWindow::mouseReleased); 0255 0256 QTRY_COMPARE(windowAddedSpy.count(), 1); 0257 0258 quint32 timestamp = 1; 0259 Test::pointerMotion(QPoint(50, 50), timestamp++); 0260 0261 Test::pointerButtonPressed(BTN_LEFT, timestamp++); 0262 QTRY_COMPARE(pressSpy.count(), 1); 0263 Test::pointerButtonReleased(BTN_LEFT, timestamp++); 0264 QTRY_COMPARE(releaseSpy.count(), 1); 0265 } 0266 0267 void InternalWindowTest::testPointerAxis() 0268 { 0269 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0270 HelperWindow win; 0271 win.setGeometry(0, 0, 100, 100); 0272 win.show(); 0273 QSignalSpy wheelSpy(&win, &HelperWindow::wheel); 0274 QTRY_COMPARE(windowAddedSpy.count(), 1); 0275 0276 quint32 timestamp = 1; 0277 Test::pointerMotion(QPoint(50, 50), timestamp++); 0278 0279 Test::pointerAxisVertical(5.0, timestamp++); 0280 QTRY_COMPARE(wheelSpy.count(), 1); 0281 Test::pointerAxisHorizontal(5.0, timestamp++); 0282 QTRY_COMPARE(wheelSpy.count(), 2); 0283 } 0284 0285 void InternalWindowTest::testKeyboard_data() 0286 { 0287 QTest::addColumn<QPoint>("cursorPos"); 0288 0289 QTest::newRow("on Window") << QPoint(50, 50); 0290 QTest::newRow("outside Window") << QPoint(250, 250); 0291 } 0292 0293 void InternalWindowTest::testKeyboard() 0294 { 0295 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0296 HelperWindow win; 0297 win.setGeometry(0, 0, 100, 100); 0298 win.show(); 0299 QSignalSpy pressSpy(&win, &HelperWindow::keyPressed); 0300 QSignalSpy releaseSpy(&win, &HelperWindow::keyReleased); 0301 QTRY_COMPARE(windowAddedSpy.count(), 1); 0302 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0303 QVERIFY(internalWindow); 0304 QVERIFY(internalWindow->isInternal()); 0305 QVERIFY(internalWindow->readyForPainting()); 0306 0307 quint32 timestamp = 1; 0308 QFETCH(QPoint, cursorPos); 0309 Test::pointerMotion(cursorPos, timestamp++); 0310 0311 Test::keyboardKeyPressed(KEY_A, timestamp++); 0312 QTRY_COMPARE(pressSpy.count(), 1); 0313 QCOMPARE(releaseSpy.count(), 0); 0314 Test::keyboardKeyReleased(KEY_A, timestamp++); 0315 QTRY_COMPARE(releaseSpy.count(), 1); 0316 QCOMPARE(pressSpy.count(), 1); 0317 } 0318 0319 void InternalWindowTest::testKeyboardShowWithoutActivating() 0320 { 0321 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0322 HelperWindow win; 0323 win.setProperty("_q_showWithoutActivating", true); 0324 win.setGeometry(0, 0, 100, 100); 0325 win.show(); 0326 QSignalSpy pressSpy(&win, &HelperWindow::keyPressed); 0327 QSignalSpy releaseSpy(&win, &HelperWindow::keyReleased); 0328 QTRY_COMPARE(windowAddedSpy.count(), 1); 0329 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0330 QVERIFY(internalWindow); 0331 QVERIFY(internalWindow->isInternal()); 0332 QVERIFY(internalWindow->readyForPainting()); 0333 0334 quint32 timestamp = 1; 0335 const QPoint cursorPos = QPoint(50, 50); 0336 Test::pointerMotion(cursorPos, timestamp++); 0337 0338 Test::keyboardKeyPressed(KEY_A, timestamp++); 0339 QCOMPARE(pressSpy.count(), 0); 0340 QVERIFY(!pressSpy.wait(100)); 0341 QCOMPARE(releaseSpy.count(), 0); 0342 Test::keyboardKeyReleased(KEY_A, timestamp++); 0343 QCOMPARE(releaseSpy.count(), 0); 0344 QVERIFY(!releaseSpy.wait(100)); 0345 QCOMPARE(pressSpy.count(), 0); 0346 } 0347 0348 void InternalWindowTest::testKeyboardTriggersLeave() 0349 { 0350 // this test verifies that a leave event is sent to a window when an internal window 0351 // gets a key event 0352 std::unique_ptr<KWayland::Client::Keyboard> keyboard(Test::waylandSeat()->createKeyboard()); 0353 QVERIFY(keyboard != nullptr); 0354 QVERIFY(keyboard->isValid()); 0355 QSignalSpy enteredSpy(keyboard.get(), &KWayland::Client::Keyboard::entered); 0356 QSignalSpy leftSpy(keyboard.get(), &KWayland::Client::Keyboard::left); 0357 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0358 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0359 0360 // now let's render 0361 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0362 QVERIFY(window); 0363 QVERIFY(window->isActive()); 0364 QVERIFY(!window->isInternal()); 0365 0366 if (enteredSpy.isEmpty()) { 0367 QVERIFY(enteredSpy.wait()); 0368 } 0369 QCOMPARE(enteredSpy.count(), 1); 0370 0371 // create internal window 0372 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0373 HelperWindow win; 0374 win.setGeometry(0, 0, 100, 100); 0375 win.show(); 0376 QSignalSpy pressSpy(&win, &HelperWindow::keyPressed); 0377 QSignalSpy releaseSpy(&win, &HelperWindow::keyReleased); 0378 QTRY_COMPARE(windowAddedSpy.count(), 1); 0379 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0380 QVERIFY(internalWindow); 0381 QVERIFY(internalWindow->isInternal()); 0382 QVERIFY(internalWindow->readyForPainting()); 0383 0384 QVERIFY(leftSpy.isEmpty()); 0385 QVERIFY(!leftSpy.wait(100)); 0386 0387 // now let's trigger a key, which should result in a leave 0388 quint32 timestamp = 1; 0389 Test::keyboardKeyPressed(KEY_A, timestamp++); 0390 QVERIFY(leftSpy.wait()); 0391 QCOMPARE(pressSpy.count(), 1); 0392 0393 Test::keyboardKeyReleased(KEY_A, timestamp++); 0394 QTRY_COMPARE(releaseSpy.count(), 1); 0395 0396 // after hiding the internal window, next key press should trigger an enter 0397 win.hide(); 0398 Test::keyboardKeyPressed(KEY_A, timestamp++); 0399 QVERIFY(enteredSpy.wait()); 0400 Test::keyboardKeyReleased(KEY_A, timestamp++); 0401 0402 // Destroy the test window. 0403 shellSurface.reset(); 0404 QVERIFY(Test::waitForWindowDestroyed(window)); 0405 } 0406 0407 void InternalWindowTest::testTouch() 0408 { 0409 // touch events for internal windows are emulated through mouse events 0410 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0411 HelperWindow win; 0412 win.setGeometry(0, 0, 100, 100); 0413 win.show(); 0414 QTRY_COMPARE(windowAddedSpy.count(), 1); 0415 0416 QSignalSpy pressSpy(&win, &HelperWindow::mousePressed); 0417 QSignalSpy releaseSpy(&win, &HelperWindow::mouseReleased); 0418 QSignalSpy moveSpy(&win, &HelperWindow::mouseMoved); 0419 0420 quint32 timestamp = 1; 0421 QCOMPARE(win.pressedButtons(), Qt::MouseButtons()); 0422 Test::touchDown(0, QPointF(50, 50), timestamp++); 0423 QCOMPARE(pressSpy.count(), 1); 0424 QCOMPARE(win.latestGlobalMousePos(), QPoint(50, 50)); 0425 QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton)); 0426 0427 // further touch down should not trigger 0428 Test::touchDown(1, QPointF(75, 75), timestamp++); 0429 QCOMPARE(pressSpy.count(), 1); 0430 Test::touchUp(1, timestamp++); 0431 QCOMPARE(releaseSpy.count(), 0); 0432 QCOMPARE(win.latestGlobalMousePos(), QPoint(50, 50)); 0433 QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton)); 0434 0435 // another press 0436 Test::touchDown(1, QPointF(10, 10), timestamp++); 0437 QCOMPARE(pressSpy.count(), 1); 0438 QCOMPARE(win.latestGlobalMousePos(), QPoint(50, 50)); 0439 QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton)); 0440 0441 // simulate the move 0442 QCOMPARE(moveSpy.count(), 0); 0443 Test::touchMotion(0, QPointF(80, 90), timestamp++); 0444 QCOMPARE(moveSpy.count(), 1); 0445 QCOMPARE(win.latestGlobalMousePos(), QPoint(80, 90)); 0446 QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton)); 0447 0448 // move on other ID should not do anything 0449 Test::touchMotion(1, QPointF(20, 30), timestamp++); 0450 QCOMPARE(moveSpy.count(), 1); 0451 QCOMPARE(win.latestGlobalMousePos(), QPoint(80, 90)); 0452 QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton)); 0453 0454 // now up our main point 0455 Test::touchUp(0, timestamp++); 0456 QCOMPARE(releaseSpy.count(), 1); 0457 QCOMPARE(win.latestGlobalMousePos(), QPoint(80, 90)); 0458 QCOMPARE(win.pressedButtons(), Qt::MouseButtons()); 0459 0460 // and up the additional point 0461 Test::touchUp(1, timestamp++); 0462 QCOMPARE(releaseSpy.count(), 1); 0463 QCOMPARE(moveSpy.count(), 1); 0464 QCOMPARE(win.latestGlobalMousePos(), QPoint(80, 90)); 0465 QCOMPARE(win.pressedButtons(), Qt::MouseButtons()); 0466 } 0467 0468 void InternalWindowTest::testOpacity() 0469 { 0470 // this test verifies that opacity is properly synced from QWindow to InternalClient 0471 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0472 HelperWindow win; 0473 win.setOpacity(0.5); 0474 win.setGeometry(0, 0, 100, 100); 0475 win.show(); 0476 QTRY_COMPARE(windowAddedSpy.count(), 1); 0477 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0478 QVERIFY(internalWindow); 0479 QVERIFY(internalWindow->isInternal()); 0480 QCOMPARE(internalWindow->opacity(), 0.5); 0481 0482 QSignalSpy opacityChangedSpy(internalWindow, &InternalWindow::opacityChanged); 0483 win.setOpacity(0.75); 0484 QCOMPARE(opacityChangedSpy.count(), 1); 0485 QCOMPARE(internalWindow->opacity(), 0.75); 0486 } 0487 0488 void InternalWindowTest::testMove() 0489 { 0490 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0491 HelperWindow win; 0492 win.setOpacity(0.5); 0493 win.setGeometry(0, 0, 100, 100); 0494 win.show(); 0495 QTRY_COMPARE(windowAddedSpy.count(), 1); 0496 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0497 QVERIFY(internalWindow); 0498 QCOMPARE(internalWindow->frameGeometry(), QRect(0, 0, 100, 100)); 0499 0500 // normal move should be synced 0501 internalWindow->move(QPoint(5, 10)); 0502 QCOMPARE(internalWindow->frameGeometry(), QRect(5, 10, 100, 100)); 0503 QTRY_COMPARE(win.geometry(), QRect(5, 10, 100, 100)); 0504 // another move should also be synced 0505 internalWindow->move(QPoint(10, 20)); 0506 QCOMPARE(internalWindow->frameGeometry(), QRect(10, 20, 100, 100)); 0507 QTRY_COMPARE(win.geometry(), QRect(10, 20, 100, 100)); 0508 0509 // now move with a Geometry update blocker 0510 { 0511 GeometryUpdatesBlocker blocker(internalWindow); 0512 internalWindow->move(QPoint(5, 10)); 0513 // not synced! 0514 QCOMPARE(win.geometry(), QRect(10, 20, 100, 100)); 0515 } 0516 // after destroying the blocker it should be synced 0517 QTRY_COMPARE(win.geometry(), QRect(5, 10, 100, 100)); 0518 } 0519 0520 void InternalWindowTest::testSkipCloseAnimation_data() 0521 { 0522 QTest::addColumn<bool>("initial"); 0523 0524 QTest::newRow("set") << true; 0525 QTest::newRow("not set") << false; 0526 } 0527 0528 void InternalWindowTest::testSkipCloseAnimation() 0529 { 0530 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0531 HelperWindow win; 0532 win.setOpacity(0.5); 0533 win.setGeometry(0, 0, 100, 100); 0534 QFETCH(bool, initial); 0535 win.setProperty("KWIN_SKIP_CLOSE_ANIMATION", initial); 0536 win.show(); 0537 QTRY_COMPARE(windowAddedSpy.count(), 1); 0538 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0539 QVERIFY(internalWindow); 0540 QCOMPARE(internalWindow->skipsCloseAnimation(), initial); 0541 QSignalSpy skipCloseChangedSpy(internalWindow, &Window::skipCloseAnimationChanged); 0542 win.setProperty("KWIN_SKIP_CLOSE_ANIMATION", !initial); 0543 QCOMPARE(skipCloseChangedSpy.count(), 1); 0544 QCOMPARE(internalWindow->skipsCloseAnimation(), !initial); 0545 win.setProperty("KWIN_SKIP_CLOSE_ANIMATION", initial); 0546 QCOMPARE(skipCloseChangedSpy.count(), 2); 0547 QCOMPARE(internalWindow->skipsCloseAnimation(), initial); 0548 } 0549 0550 void InternalWindowTest::testModifierClickUnrestrictedMove() 0551 { 0552 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0553 HelperWindow win; 0554 win.setGeometry(0, 0, 100, 100); 0555 win.setFlags(win.flags() & ~Qt::FramelessWindowHint); 0556 win.show(); 0557 QTRY_COMPARE(windowAddedSpy.count(), 1); 0558 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0559 QVERIFY(internalWindow); 0560 QVERIFY(internalWindow->isDecorated()); 0561 0562 KConfigGroup group = kwinApp()->config()->group("MouseBindings"); 0563 group.writeEntry("CommandAllKey", "Meta"); 0564 group.writeEntry("CommandAll1", "Move"); 0565 group.writeEntry("CommandAll2", "Move"); 0566 group.writeEntry("CommandAll3", "Move"); 0567 group.sync(); 0568 workspace()->slotReconfigure(); 0569 QCOMPARE(options->commandAllModifier(), Qt::MetaModifier); 0570 QCOMPARE(options->commandAll1(), Options::MouseUnrestrictedMove); 0571 QCOMPARE(options->commandAll2(), Options::MouseUnrestrictedMove); 0572 QCOMPARE(options->commandAll3(), Options::MouseUnrestrictedMove); 0573 0574 // move cursor on window 0575 Cursors::self()->mouse()->setPos(internalWindow->frameGeometry().center()); 0576 0577 // simulate modifier+click 0578 quint32 timestamp = 1; 0579 Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++); 0580 QVERIFY(!internalWindow->isInteractiveMove()); 0581 Test::pointerButtonPressed(BTN_LEFT, timestamp++); 0582 QVERIFY(internalWindow->isInteractiveMove()); 0583 // release modifier should not change it 0584 Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++); 0585 QVERIFY(internalWindow->isInteractiveMove()); 0586 // but releasing the key should end move/resize 0587 Test::pointerButtonReleased(BTN_LEFT, timestamp++); 0588 QVERIFY(!internalWindow->isInteractiveMove()); 0589 } 0590 0591 void InternalWindowTest::testModifierScroll() 0592 { 0593 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0594 HelperWindow win; 0595 win.setGeometry(0, 0, 100, 100); 0596 win.setFlags(win.flags() & ~Qt::FramelessWindowHint); 0597 win.show(); 0598 QTRY_COMPARE(windowAddedSpy.count(), 1); 0599 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0600 QVERIFY(internalWindow); 0601 QVERIFY(internalWindow->isDecorated()); 0602 0603 KConfigGroup group = kwinApp()->config()->group("MouseBindings"); 0604 group.writeEntry("CommandAllKey", "Meta"); 0605 group.writeEntry("CommandAllWheel", "change opacity"); 0606 group.sync(); 0607 workspace()->slotReconfigure(); 0608 0609 // move cursor on window 0610 Cursors::self()->mouse()->setPos(internalWindow->frameGeometry().center()); 0611 0612 // set the opacity to 0.5 0613 internalWindow->setOpacity(0.5); 0614 QCOMPARE(internalWindow->opacity(), 0.5); 0615 quint32 timestamp = 1; 0616 Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++); 0617 Test::pointerAxisVertical(-5, timestamp++); 0618 QCOMPARE(internalWindow->opacity(), 0.6); 0619 Test::pointerAxisVertical(5, timestamp++); 0620 QCOMPARE(internalWindow->opacity(), 0.5); 0621 Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++); 0622 } 0623 0624 void InternalWindowTest::testPopup() 0625 { 0626 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0627 HelperWindow win; 0628 win.setGeometry(0, 0, 100, 100); 0629 win.setFlags(win.flags() | Qt::Popup); 0630 win.show(); 0631 QTRY_COMPARE(windowAddedSpy.count(), 1); 0632 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0633 QVERIFY(internalWindow); 0634 QCOMPARE(internalWindow->isPopupWindow(), true); 0635 } 0636 0637 void InternalWindowTest::testScale() 0638 { 0639 QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, 0640 Q_ARG(QVector<QRect>, QVector<QRect>({QRect(0, 0, 1280, 1024), QRect(1280 / 2, 0, 1280, 1024)})), 0641 Q_ARG(QVector<qreal>, QVector<qreal>({2, 2}))); 0642 0643 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0644 HelperWindow win; 0645 win.setGeometry(0, 0, 100, 100); 0646 win.setFlags(win.flags() | Qt::Popup); 0647 win.show(); 0648 QCOMPARE(win.devicePixelRatio(), 2.0); 0649 QTRY_COMPARE(windowAddedSpy.count(), 1); 0650 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0651 QCOMPARE(internalWindow->bufferScale(), 2); 0652 } 0653 0654 void InternalWindowTest::testWindowType_data() 0655 { 0656 QTest::addColumn<NET::WindowType>("windowType"); 0657 0658 QTest::newRow("normal") << NET::Normal; 0659 QTest::newRow("desktop") << NET::Desktop; 0660 QTest::newRow("Dock") << NET::Dock; 0661 QTest::newRow("Toolbar") << NET::Toolbar; 0662 QTest::newRow("Menu") << NET::Menu; 0663 QTest::newRow("Dialog") << NET::Dialog; 0664 QTest::newRow("Utility") << NET::Utility; 0665 QTest::newRow("Splash") << NET::Splash; 0666 QTest::newRow("DropdownMenu") << NET::DropdownMenu; 0667 QTest::newRow("PopupMenu") << NET::PopupMenu; 0668 QTest::newRow("Tooltip") << NET::Tooltip; 0669 QTest::newRow("Notification") << NET::Notification; 0670 QTest::newRow("ComboBox") << NET::ComboBox; 0671 QTest::newRow("OnScreenDisplay") << NET::OnScreenDisplay; 0672 QTest::newRow("CriticalNotification") << NET::CriticalNotification; 0673 QTest::newRow("AppletPopup") << NET::AppletPopup; 0674 } 0675 0676 void InternalWindowTest::testWindowType() 0677 { 0678 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0679 HelperWindow win; 0680 win.setGeometry(0, 0, 100, 100); 0681 QFETCH(NET::WindowType, windowType); 0682 KWindowSystem::setType(win.winId(), windowType); 0683 win.show(); 0684 QTRY_COMPARE(windowAddedSpy.count(), 1); 0685 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0686 QVERIFY(internalWindow); 0687 QCOMPARE(internalWindow->windowType(), windowType); 0688 } 0689 0690 void InternalWindowTest::testChangeWindowType_data() 0691 { 0692 QTest::addColumn<NET::WindowType>("windowType"); 0693 0694 QTest::newRow("desktop") << NET::Desktop; 0695 QTest::newRow("Dock") << NET::Dock; 0696 QTest::newRow("Toolbar") << NET::Toolbar; 0697 QTest::newRow("Menu") << NET::Menu; 0698 QTest::newRow("Dialog") << NET::Dialog; 0699 QTest::newRow("Utility") << NET::Utility; 0700 QTest::newRow("Splash") << NET::Splash; 0701 QTest::newRow("DropdownMenu") << NET::DropdownMenu; 0702 QTest::newRow("PopupMenu") << NET::PopupMenu; 0703 QTest::newRow("Tooltip") << NET::Tooltip; 0704 QTest::newRow("Notification") << NET::Notification; 0705 QTest::newRow("ComboBox") << NET::ComboBox; 0706 QTest::newRow("OnScreenDisplay") << NET::OnScreenDisplay; 0707 QTest::newRow("CriticalNotification") << NET::CriticalNotification; 0708 QTest::newRow("AppletPopup") << NET::AppletPopup; 0709 } 0710 0711 void InternalWindowTest::testChangeWindowType() 0712 { 0713 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0714 HelperWindow win; 0715 win.setGeometry(0, 0, 100, 100); 0716 win.show(); 0717 QTRY_COMPARE(windowAddedSpy.count(), 1); 0718 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0719 QVERIFY(internalWindow); 0720 QCOMPARE(internalWindow->windowType(), NET::Normal); 0721 0722 QFETCH(NET::WindowType, windowType); 0723 KWindowSystem::setType(win.winId(), windowType); 0724 QTRY_COMPARE(internalWindow->windowType(), windowType); 0725 0726 KWindowSystem::setType(win.winId(), NET::Normal); 0727 QTRY_COMPARE(internalWindow->windowType(), NET::Normal); 0728 } 0729 0730 void InternalWindowTest::testEffectWindow() 0731 { 0732 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0733 HelperWindow win; 0734 win.setGeometry(0, 0, 100, 100); 0735 win.show(); 0736 QTRY_COMPARE(windowAddedSpy.count(), 1); 0737 auto internalWindow = windowAddedSpy.first().first().value<InternalWindow *>(); 0738 QVERIFY(internalWindow); 0739 QVERIFY(internalWindow->effectWindow()); 0740 QCOMPARE(internalWindow->effectWindow()->internalWindow(), &win); 0741 0742 QCOMPARE(effects->findWindow(&win), internalWindow->effectWindow()); 0743 QCOMPARE(effects->findWindow(&win)->internalWindow(), &win); 0744 } 0745 0746 void InternalWindowTest::testReentrantMoveResize() 0747 { 0748 // This test verifies that calling moveResize() from a slot connected directly 0749 // to the frameGeometryChanged() signal won't cause an infinite recursion. 0750 0751 // Create an internal window. 0752 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0753 HelperWindow win; 0754 win.setGeometry(0, 0, 100, 100); 0755 win.show(); 0756 QTRY_COMPARE(windowAddedSpy.count(), 1); 0757 auto window = windowAddedSpy.first().first().value<InternalWindow *>(); 0758 QVERIFY(window); 0759 QCOMPARE(window->pos(), QPoint(0, 0)); 0760 0761 // Let's pretend that there is a script that really wants the window to be at (100, 100). 0762 connect(window, &Window::frameGeometryChanged, this, [window]() { 0763 window->moveResize(QRectF(QPointF(100, 100), window->size())); 0764 }); 0765 0766 // Trigger the lambda above. 0767 window->move(QPoint(40, 50)); 0768 0769 // Eventually, the window will end up at (100, 100). 0770 QCOMPARE(window->pos(), QPoint(100, 100)); 0771 } 0772 0773 void InternalWindowTest::testDismissPopup() 0774 { 0775 // This test verifies that a popup window created by the compositor will be dismissed 0776 // when user clicks another window. 0777 0778 // Create a toplevel window. 0779 QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded); 0780 HelperWindow clientToplevel; 0781 clientToplevel.setGeometry(0, 0, 100, 100); 0782 clientToplevel.show(); 0783 QTRY_COMPARE(windowAddedSpy.count(), 1); 0784 auto serverToplevel = windowAddedSpy.last().first().value<InternalWindow *>(); 0785 QVERIFY(serverToplevel); 0786 0787 // Create a popup window. 0788 QRasterWindow clientPopup; 0789 clientPopup.setFlag(Qt::Popup); 0790 clientPopup.setTransientParent(&clientToplevel); 0791 clientPopup.setGeometry(0, 0, 50, 50); 0792 clientPopup.show(); 0793 QTRY_COMPARE(windowAddedSpy.count(), 2); 0794 auto serverPopup = windowAddedSpy.last().first().value<InternalWindow *>(); 0795 QVERIFY(serverPopup); 0796 0797 // Create the other window to click 0798 HelperWindow otherClientToplevel; 0799 otherClientToplevel.setGeometry(100, 100, 100, 100); 0800 otherClientToplevel.show(); 0801 QTRY_COMPARE(windowAddedSpy.count(), 3); 0802 auto serverOtherToplevel = windowAddedSpy.last().first().value<InternalWindow *>(); 0803 QVERIFY(serverOtherToplevel); 0804 0805 // Click somewhere outside the popup window. 0806 QSignalSpy popupClosedSpy(serverPopup, &InternalWindow::windowClosed); 0807 quint32 timestamp = 0; 0808 Test::pointerMotion(serverOtherToplevel->frameGeometry().center(), timestamp++); 0809 Test::pointerButtonPressed(BTN_LEFT, timestamp++); 0810 QTRY_COMPARE(popupClosedSpy.count(), 1); 0811 } 0812 0813 } 0814 0815 WAYLANDTEST_MAIN(KWin::InternalWindowTest) 0816 #include "internal_window.moc"