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