diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt index e168cad80..31c49dc39 100644 --- a/autotests/integration/CMakeLists.txt +++ b/autotests/integration/CMakeLists.txt @@ -1,45 +1,45 @@ add_definitions(-DKWINBACKENDPATH="${CMAKE_BINARY_DIR}/plugins/platforms/virtual/KWinWaylandVirtualBackend.so") add_definitions(-DKWINQPAPATH="${CMAKE_BINARY_DIR}/plugins/qpa/") add_subdirectory(helper) -add_library(KWinIntegrationTestFramework STATIC kwin_wayland_test.cpp) +add_library(KWinIntegrationTestFramework STATIC kwin_wayland_test.cpp test_helpers.cpp) target_link_libraries(KWinIntegrationTestFramework kwin Qt5::Test) function(integrationTest) set(oneValueArgs NAME) set(multiValueArgs SRCS LIBS) cmake_parse_arguments(ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) add_executable(${ARGS_NAME} ${ARGS_SRCS}) target_link_libraries(${ARGS_NAME} KWinIntegrationTestFramework kwin Qt5::Test ${ARGS_LIBS}) add_test(kwin-${ARGS_NAME} ${ARGS_NAME}) endfunction() integrationTest(NAME testStart SRCS start_test.cpp) integrationTest(NAME testTransientNoInput SRCS transient_no_input_test.cpp) integrationTest(NAME testQuickTiling SRCS quick_tiling_test.cpp) integrationTest(NAME testDontCrashGlxgears SRCS dont_crash_glxgears.cpp) integrationTest(NAME testLockScreen SRCS lockscreen.cpp) integrationTest(NAME testDecorationInput SRCS decoration_input_test.cpp) integrationTest(NAME testInternalWindow SRCS internal_window.cpp) integrationTest(NAME testTouchInput SRCS touch_input_test.cpp) integrationTest(NAME testInputStackingOrder SRCS input_stacking_order.cpp) integrationTest(NAME testPointerInput SRCS pointer_input.cpp) integrationTest(NAME testPlatformCursor SRCS platformcursor.cpp) integrationTest(NAME testDontCrashCancelAnimation SRCS dont_crash_cancel_animation.cpp) integrationTest(NAME testTransientPlacmenet SRCS transient_placement.cpp) integrationTest(NAME testDebugConsole SRCS debug_console_test.cpp) integrationTest(NAME testDontCrashEmptyDeco SRCS dont_crash_empty_deco.cpp) integrationTest(NAME testPlasmaSurface SRCS plasma_surface_test.cpp) integrationTest(NAME testMaximized SRCS maximize_test.cpp) integrationTest(NAME testShellClient SRCS shell_client_test.cpp) integrationTest(NAME testDontCrashNoBorder SRCS dont_crash_no_border.cpp) integrationTest(NAME testXClipboardSync SRCS xclipboardsync_test.cpp) integrationTest(NAME testSceneQPainter SRCS scene_qpainter_test.cpp) if (XCB_ICCCM_FOUND) integrationTest(NAME testMoveResize SRCS move_resize_window_test.cpp LIBS XCB::ICCCM) integrationTest(NAME testStruts SRCS struts_test.cpp LIBS XCB::ICCCM) integrationTest(NAME testShade SRCS shade_test.cpp LIBS XCB::ICCCM) integrationTest(NAME testDontCrashAuroraeDestroyDeco SRCS dont_crash_aurorae_destroy_deco.cpp LIBS XCB::ICCCM) integrationTest(NAME testPlasmaWindow SRCS plasmawindow_test.cpp LIBS XCB::ICCCM) endif() diff --git a/autotests/integration/debug_console_test.cpp b/autotests/integration/debug_console_test.cpp index 56c844d4c..2d42e40f0 100644 --- a/autotests/integration/debug_console_test.cpp +++ b/autotests/integration/debug_console_test.cpp @@ -1,535 +1,496 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "debug_console.h" #include "screens.h" #include "shell_client.h" #include "wayland_server.h" #include "xcbutils.h" #include #include -#include -#include #include #include #include #include #include namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_debug_console-0"); class DebugConsoleTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); + void cleanup(); void topLevelTest_data(); void topLevelTest(); void testX11Client(); void testX11Unmanaged(); void testWaylandClient(); void testInternalWindow(); }; void DebugConsoleTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); setenv("QT_QPA_PLATFORM", "wayland", true); waylandServer()->initWorkspace(); } +void DebugConsoleTest::cleanup() +{ + Test::destroyWaylandConnection(); +} + void DebugConsoleTest::topLevelTest_data() { QTest::addColumn("row"); QTest::addColumn("column"); QTest::addColumn("expectedValid"); // this tests various combinations of row/column on the top level whether they are valid // valid are rows 0-4 with column 0, everything else is invalid QTest::newRow("0/0") << 0 << 0 << true; QTest::newRow("0/1") << 0 << 1 << false; QTest::newRow("0/3") << 0 << 3 << false; QTest::newRow("1/0") << 1 << 0 << true; QTest::newRow("1/1") << 1 << 1 << false; QTest::newRow("1/3") << 1 << 3 << false; QTest::newRow("2/0") << 2 << 0 << true; QTest::newRow("3/0") << 3 << 0 << true; QTest::newRow("4/0") << 4 << 0 << false; QTest::newRow("100/0") << 4 << 0 << false; } void DebugConsoleTest::topLevelTest() { DebugConsoleModel model; QCOMPARE(model.rowCount(QModelIndex()), 4); QCOMPARE(model.columnCount(QModelIndex()), 2); QFETCH(int, row); QFETCH(int, column); const QModelIndex index = model.index(row, column, QModelIndex()); QTEST(index.isValid(), "expectedValid"); if (index.isValid()) { QVERIFY(!model.parent(index).isValid()); QVERIFY(model.data(index, Qt::DisplayRole).isValid()); QCOMPARE(model.data(index, Qt::DisplayRole).userType(), int(QMetaType::QString)); for (int i = Qt::DecorationRole; i <= Qt::UserRole; i++) { QVERIFY(!model.data(index, i).isValid()); } } } void DebugConsoleTest::testX11Client() { DebugConsoleModel model; QModelIndex x11TopLevelIndex = model.index(0, 0, QModelIndex()); QVERIFY(x11TopLevelIndex.isValid()); // we don't have any windows yet QCOMPARE(model.rowCount(x11TopLevelIndex), 0); QVERIFY(!model.hasChildren(x11TopLevelIndex)); // child index must be invalid QVERIFY(!model.index(0, 0, x11TopLevelIndex).isValid()); QVERIFY(!model.index(0, 1, x11TopLevelIndex).isValid()); QVERIFY(!model.index(0, 2, x11TopLevelIndex).isValid()); QVERIFY(!model.index(1, 0, x11TopLevelIndex).isValid()); // start glxgears, to get a window, which should be added to the model QSignalSpy rowsInsertedSpy(&model, &QAbstractItemModel::rowsInserted); QVERIFY(rowsInsertedSpy.isValid()); QProcess glxgears; glxgears.start(QStringLiteral("glxgears")); QVERIFY(glxgears.waitForStarted()); QVERIFY(rowsInsertedSpy.wait()); QCOMPARE(rowsInsertedSpy.count(), 1); QVERIFY(model.hasChildren(x11TopLevelIndex)); QCOMPARE(model.rowCount(x11TopLevelIndex), 1); QCOMPARE(rowsInsertedSpy.first().at(0).value(), x11TopLevelIndex); QCOMPARE(rowsInsertedSpy.first().at(1).value(), 0); QCOMPARE(rowsInsertedSpy.first().at(2).value(), 0); QModelIndex clientIndex = model.index(0, 0, x11TopLevelIndex); QVERIFY(clientIndex.isValid()); QCOMPARE(model.parent(clientIndex), x11TopLevelIndex); QVERIFY(model.hasChildren(clientIndex)); QVERIFY(model.rowCount(clientIndex) != 0); QCOMPARE(model.columnCount(clientIndex), 2); // other indexes are still invalid QVERIFY(!model.index(0, 1, x11TopLevelIndex).isValid()); QVERIFY(!model.index(0, 2, x11TopLevelIndex).isValid()); QVERIFY(!model.index(1, 0, x11TopLevelIndex).isValid()); // the clientIndex has children and those are properties for (int i = 0; i < model.rowCount(clientIndex); i++) { const QModelIndex propNameIndex = model.index(i, 0, clientIndex); QVERIFY(propNameIndex.isValid()); QCOMPARE(model.parent(propNameIndex), clientIndex); QVERIFY(!model.hasChildren(propNameIndex)); QVERIFY(!model.index(0, 0, propNameIndex).isValid()); QVERIFY(model.data(propNameIndex, Qt::DisplayRole).isValid()); QCOMPARE(model.data(propNameIndex, Qt::DisplayRole).userType(), int(QMetaType::QString)); // and the value const QModelIndex propValueIndex = model.index(i, 1, clientIndex); QVERIFY(propValueIndex.isValid()); QCOMPARE(model.parent(propValueIndex), clientIndex); QVERIFY(!model.index(0, 0, propValueIndex).isValid()); QVERIFY(!model.hasChildren(propValueIndex)); // TODO: how to test whether the values actually work? // and on third column we should not get an index any more QVERIFY(!model.index(i, 2, clientIndex).isValid()); } // row after count should be invalid QVERIFY(!model.index(model.rowCount(clientIndex), 0, clientIndex).isValid()); // creating a second model should be initialized directly with the X11 child DebugConsoleModel model2; QVERIFY(model2.hasChildren(model2.index(0, 0, QModelIndex()))); // now close the window again, it should be removed from the model QSignalSpy rowsRemovedSpy(&model, &QAbstractItemModel::rowsRemoved); QVERIFY(rowsRemovedSpy.isValid()); glxgears.terminate(); QVERIFY(glxgears.waitForFinished()); QVERIFY(rowsRemovedSpy.wait()); QCOMPARE(rowsRemovedSpy.count(), 1); QCOMPARE(rowsRemovedSpy.first().first().value(), x11TopLevelIndex); QCOMPARE(rowsRemovedSpy.first().at(1).value(), 0); QCOMPARE(rowsRemovedSpy.first().at(2).value(), 0); // the child should be gone again QVERIFY(!model.hasChildren(x11TopLevelIndex)); QVERIFY(!model2.hasChildren(model2.index(0, 0, QModelIndex()))); } void DebugConsoleTest::testX11Unmanaged() { DebugConsoleModel model; QModelIndex unmanagedTopLevelIndex = model.index(1, 0, QModelIndex()); QVERIFY(unmanagedTopLevelIndex.isValid()); // we don't have any windows yet QCOMPARE(model.rowCount(unmanagedTopLevelIndex), 0); QVERIFY(!model.hasChildren(unmanagedTopLevelIndex)); // child index must be invalid QVERIFY(!model.index(0, 0, unmanagedTopLevelIndex).isValid()); QVERIFY(!model.index(0, 1, unmanagedTopLevelIndex).isValid()); QVERIFY(!model.index(0, 2, unmanagedTopLevelIndex).isValid()); QVERIFY(!model.index(1, 0, unmanagedTopLevelIndex).isValid()); // we need to create an unmanaged window QSignalSpy rowsInsertedSpy(&model, &QAbstractItemModel::rowsInserted); QVERIFY(rowsInsertedSpy.isValid()); // let's create an override redirect window const uint32_t values[] = {true}; Xcb::Window window(QRect(0, 0, 10, 10), XCB_CW_OVERRIDE_REDIRECT, values); window.map(); QVERIFY(rowsInsertedSpy.wait()); QCOMPARE(rowsInsertedSpy.count(), 1); QVERIFY(model.hasChildren(unmanagedTopLevelIndex)); QCOMPARE(model.rowCount(unmanagedTopLevelIndex), 1); QCOMPARE(rowsInsertedSpy.first().at(0).value(), unmanagedTopLevelIndex); QCOMPARE(rowsInsertedSpy.first().at(1).value(), 0); QCOMPARE(rowsInsertedSpy.first().at(2).value(), 0); QModelIndex clientIndex = model.index(0, 0, unmanagedTopLevelIndex); QVERIFY(clientIndex.isValid()); QCOMPARE(model.parent(clientIndex), unmanagedTopLevelIndex); QVERIFY(model.hasChildren(clientIndex)); QVERIFY(model.rowCount(clientIndex) != 0); QCOMPARE(model.columnCount(clientIndex), 2); // other indexes are still invalid QVERIFY(!model.index(0, 1, unmanagedTopLevelIndex).isValid()); QVERIFY(!model.index(0, 2, unmanagedTopLevelIndex).isValid()); QVERIFY(!model.index(1, 0, unmanagedTopLevelIndex).isValid()); QCOMPARE(model.data(clientIndex, Qt::DisplayRole).toString(), QString::number(window)); // the clientIndex has children and those are properties for (int i = 0; i < model.rowCount(clientIndex); i++) { const QModelIndex propNameIndex = model.index(i, 0, clientIndex); QVERIFY(propNameIndex.isValid()); QCOMPARE(model.parent(propNameIndex), clientIndex); QVERIFY(!model.hasChildren(propNameIndex)); QVERIFY(!model.index(0, 0, propNameIndex).isValid()); QVERIFY(model.data(propNameIndex, Qt::DisplayRole).isValid()); QCOMPARE(model.data(propNameIndex, Qt::DisplayRole).userType(), int(QMetaType::QString)); // and the value const QModelIndex propValueIndex = model.index(i, 1, clientIndex); QVERIFY(propValueIndex.isValid()); QCOMPARE(model.parent(propValueIndex), clientIndex); QVERIFY(!model.index(0, 0, propValueIndex).isValid()); QVERIFY(!model.hasChildren(propValueIndex)); // TODO: how to test whether the values actually work? // and on third column we should not get an index any more QVERIFY(!model.index(i, 2, clientIndex).isValid()); } // row after count should be invalid QVERIFY(!model.index(model.rowCount(clientIndex), 0, clientIndex).isValid()); // creating a second model should be initialized directly with the X11 child DebugConsoleModel model2; QVERIFY(model2.hasChildren(model2.index(1, 0, QModelIndex()))); // now close the window again, it should be removed from the model QSignalSpy rowsRemovedSpy(&model, &QAbstractItemModel::rowsRemoved); QVERIFY(rowsRemovedSpy.isValid()); window.unmap(); QVERIFY(rowsRemovedSpy.wait()); QCOMPARE(rowsRemovedSpy.count(), 1); QCOMPARE(rowsRemovedSpy.first().first().value(), unmanagedTopLevelIndex); QCOMPARE(rowsRemovedSpy.first().at(1).value(), 0); QCOMPARE(rowsRemovedSpy.first().at(2).value(), 0); // the child should be gone again QVERIFY(!model.hasChildren(unmanagedTopLevelIndex)); QVERIFY(!model2.hasChildren(model2.index(1, 0, QModelIndex()))); } void DebugConsoleTest::testWaylandClient() { DebugConsoleModel model; QModelIndex waylandTopLevelIndex = model.index(2, 0, QModelIndex()); QVERIFY(waylandTopLevelIndex.isValid()); // we don't have any windows yet QCOMPARE(model.rowCount(waylandTopLevelIndex), 0); QVERIFY(!model.hasChildren(waylandTopLevelIndex)); // child index must be invalid QVERIFY(!model.index(0, 0, waylandTopLevelIndex).isValid()); QVERIFY(!model.index(0, 1, waylandTopLevelIndex).isValid()); QVERIFY(!model.index(0, 2, waylandTopLevelIndex).isValid()); QVERIFY(!model.index(1, 0, waylandTopLevelIndex).isValid()); // we need to create a wayland window QSignalSpy rowsInsertedSpy(&model, &QAbstractItemModel::rowsInserted); QVERIFY(rowsInsertedSpy.isValid()); // create our connection - using namespace KWayland::Client; - // setup connection - QScopedPointer connection(new ConnectionThread); - QSignalSpy connectedSpy(connection.data(), &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - connection->setSocketName(s_socketName); - - QScopedPointer thread(new QThread); - connection->moveToThread(thread.data()); - thread->start(); - - connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - QScopedPointer queue(new EventQueue); - QVERIFY(!queue->isValid()); - queue->setup(connection.data()); - QVERIFY(queue->isValid()); - - Registry registry; - registry.setEventQueue(queue.data()); - QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); - QVERIFY(interfacesAnnouncedSpy.isValid()); - registry.create(connection.data()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(interfacesAnnouncedSpy.wait()); - - QScopedPointer compositor(registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version)); - QVERIFY(compositor->isValid()); - QScopedPointer shm(registry.createShmPool(registry.interface(Registry::Interface::Shm).name, registry.interface(Registry::Interface::Shm).version)); - QVERIFY(shm->isValid()); - QScopedPointer shell(registry.createShell(registry.interface(Registry::Interface::Shell).name, registry.interface(Registry::Interface::Shell).version)); - QVERIFY(shell->isValid()); + QVERIFY(Test::setupWaylandConnection(s_socketName)); // create the Surface and ShellSurface - QScopedPointer surface(compositor->createSurface()); + using namespace KWayland::Client; + QScopedPointer surface(Test::createSurface()); QVERIFY(surface->isValid()); - QScopedPointer shellSurface(shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(shellSurface->isValid()); - QImage img(10, 10, QImage::Format_ARGB32); - img.fill(Qt::red); - surface->attachBuffer(shm->createBuffer(img)); - surface->damage(QRect(0, 0, 10, 10)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(10, 10), Qt::red); // now we have the window, it should be added to our model QVERIFY(rowsInsertedSpy.wait()); QCOMPARE(rowsInsertedSpy.count(), 1); QVERIFY(model.hasChildren(waylandTopLevelIndex)); QCOMPARE(model.rowCount(waylandTopLevelIndex), 1); QCOMPARE(rowsInsertedSpy.first().at(0).value(), waylandTopLevelIndex); QCOMPARE(rowsInsertedSpy.first().at(1).value(), 0); QCOMPARE(rowsInsertedSpy.first().at(2).value(), 0); QModelIndex clientIndex = model.index(0, 0, waylandTopLevelIndex); QVERIFY(clientIndex.isValid()); QCOMPARE(model.parent(clientIndex), waylandTopLevelIndex); QVERIFY(model.hasChildren(clientIndex)); QVERIFY(model.rowCount(clientIndex) != 0); QCOMPARE(model.columnCount(clientIndex), 2); // other indexes are still invalid QVERIFY(!model.index(0, 1, waylandTopLevelIndex).isValid()); QVERIFY(!model.index(0, 2, waylandTopLevelIndex).isValid()); QVERIFY(!model.index(1, 0, waylandTopLevelIndex).isValid()); // the clientIndex has children and those are properties for (int i = 0; i < model.rowCount(clientIndex); i++) { const QModelIndex propNameIndex = model.index(i, 0, clientIndex); QVERIFY(propNameIndex.isValid()); QCOMPARE(model.parent(propNameIndex), clientIndex); QVERIFY(!model.hasChildren(propNameIndex)); QVERIFY(!model.index(0, 0, propNameIndex).isValid()); QVERIFY(model.data(propNameIndex, Qt::DisplayRole).isValid()); QCOMPARE(model.data(propNameIndex, Qt::DisplayRole).userType(), int(QMetaType::QString)); // and the value const QModelIndex propValueIndex = model.index(i, 1, clientIndex); QVERIFY(propValueIndex.isValid()); QCOMPARE(model.parent(propValueIndex), clientIndex); QVERIFY(!model.index(0, 0, propValueIndex).isValid()); QVERIFY(!model.hasChildren(propValueIndex)); // TODO: how to test whether the values actually work? // and on third column we should not get an index any more QVERIFY(!model.index(i, 2, clientIndex).isValid()); } // row after count should be invalid QVERIFY(!model.index(model.rowCount(clientIndex), 0, clientIndex).isValid()); // creating a second model should be initialized directly with the X11 child DebugConsoleModel model2; QVERIFY(model2.hasChildren(model2.index(2, 0, QModelIndex()))); // now close the window again, it should be removed from the model QSignalSpy rowsRemovedSpy(&model, &QAbstractItemModel::rowsRemoved); QVERIFY(rowsRemovedSpy.isValid()); surface->attachBuffer(Buffer::Ptr()); surface->commit(Surface::CommitFlag::None); shellSurface.reset(); - connection->flush(); + Test::flushWaylandConnection(); qDebug() << rowsRemovedSpy.count(); QEXPECT_FAIL("", "Deleting a ShellSurface does not result in the server removing the ShellClient", Continue); QVERIFY(rowsRemovedSpy.wait()); - // also destroy the connection to ensure the ShellSurface goes away surface.reset(); - shell.reset(); - shm.reset(); - compositor.reset(); - connection.take()->deleteLater(); - thread->quit(); - QVERIFY(thread->wait()); QVERIFY(rowsRemovedSpy.wait()); QCOMPARE(rowsRemovedSpy.count(), 1); QCOMPARE(rowsRemovedSpy.first().first().value(), waylandTopLevelIndex); QCOMPARE(rowsRemovedSpy.first().at(1).value(), 0); QCOMPARE(rowsRemovedSpy.first().at(2).value(), 0); // the child should be gone again QVERIFY(!model.hasChildren(waylandTopLevelIndex)); QVERIFY(!model2.hasChildren(model2.index(2, 0, QModelIndex()))); } class HelperWindow : public QRasterWindow { Q_OBJECT public: HelperWindow() : QRasterWindow(nullptr) {} ~HelperWindow() = default; Q_SIGNALS: void entered(); void left(); void mouseMoved(const QPoint &global); void mousePressed(); void mouseReleased(); void wheel(); void keyPressed(); void keyReleased(); protected: void paintEvent(QPaintEvent *event) override { Q_UNUSED(event) QPainter p(this); p.fillRect(0, 0, width(), height(), Qt::red); } }; void DebugConsoleTest::testInternalWindow() { DebugConsoleModel model; QModelIndex internalTopLevelIndex = model.index(3, 0, QModelIndex()); QVERIFY(internalTopLevelIndex.isValid()); // there might already be some internal windows, so we cannot reliable test whether there are children // given that we just test whether adding a window works. QSignalSpy rowsInsertedSpy(&model, &QAbstractItemModel::rowsInserted); QVERIFY(rowsInsertedSpy.isValid()); QScopedPointer w(new HelperWindow); w->setGeometry(0, 0, 100, 100); w->show(); QVERIFY(rowsInsertedSpy.wait()); QCOMPARE(rowsInsertedSpy.count(), 1); QCOMPARE(rowsInsertedSpy.first().first().value(), internalTopLevelIndex); QModelIndex clientIndex = model.index(rowsInsertedSpy.first().last().toInt(), 0, internalTopLevelIndex); QVERIFY(clientIndex.isValid()); QCOMPARE(model.parent(clientIndex), internalTopLevelIndex); QVERIFY(model.hasChildren(clientIndex)); QVERIFY(model.rowCount(clientIndex) != 0); QCOMPARE(model.columnCount(clientIndex), 2); // other indexes are still invalid QVERIFY(!model.index(rowsInsertedSpy.first().last().toInt(), 1, internalTopLevelIndex).isValid()); QVERIFY(!model.index(rowsInsertedSpy.first().last().toInt(), 2, internalTopLevelIndex).isValid()); QVERIFY(!model.index(rowsInsertedSpy.first().last().toInt() + 1, 0, internalTopLevelIndex).isValid()); // the wayland shell client top level should not have gained this window QVERIFY(!model.hasChildren(model.index(2, 0, QModelIndex()))); // the clientIndex has children and those are properties for (int i = 0; i < model.rowCount(clientIndex); i++) { const QModelIndex propNameIndex = model.index(i, 0, clientIndex); QVERIFY(propNameIndex.isValid()); QCOMPARE(model.parent(propNameIndex), clientIndex); QVERIFY(!model.hasChildren(propNameIndex)); QVERIFY(!model.index(0, 0, propNameIndex).isValid()); QVERIFY(model.data(propNameIndex, Qt::DisplayRole).isValid()); QCOMPARE(model.data(propNameIndex, Qt::DisplayRole).userType(), int(QMetaType::QString)); // and the value const QModelIndex propValueIndex = model.index(i, 1, clientIndex); QVERIFY(propValueIndex.isValid()); QCOMPARE(model.parent(propValueIndex), clientIndex); QVERIFY(!model.index(0, 0, propValueIndex).isValid()); QVERIFY(!model.hasChildren(propValueIndex)); // TODO: how to test whether the values actually work? // and on third column we should not get an index any more QVERIFY(!model.index(i, 2, clientIndex).isValid()); } // row after count should be invalid QVERIFY(!model.index(model.rowCount(clientIndex), 0, clientIndex).isValid()); // now close the window again, it should be removed from the model QSignalSpy rowsRemovedSpy(&model, &QAbstractItemModel::rowsRemoved); QVERIFY(rowsRemovedSpy.isValid()); w->hide(); w.reset(); QVERIFY(rowsRemovedSpy.wait()); QCOMPARE(rowsRemovedSpy.count(), 1); QCOMPARE(rowsRemovedSpy.first().first().value(), internalTopLevelIndex); } } WAYLANDTEST_MAIN(KWin::DebugConsoleTest) #include "debug_console_test.moc" diff --git a/autotests/integration/decoration_input_test.cpp b/autotests/integration/decoration_input_test.cpp index b8b1dd8e3..56939db35 100644 --- a/autotests/integration/decoration_input_test.cpp +++ b/autotests/integration/decoration_input_test.cpp @@ -1,559 +1,473 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "abstract_client.h" #include "cursor.h" #include "pointer_input.h" #include "touch_input.h" #include "screenedge.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include #include "decorations/decoratedclient.h" #include #include -#include -#include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(Qt::WindowFrameSection) namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_decoration_input-0"); class DecorationInputTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testAxis_data(); void testAxis(); void testDoubleClick_data(); void testDoubleClick(); void testDoubleTap_data(); void testDoubleTap(); void testHover(); void testPressToMove_data(); void testPressToMove(); void testTapToMove_data(); void testTapToMove(); private: AbstractClient *showWindow(); - KWayland::Client::ConnectionThread *m_connection = nullptr; - KWayland::Client::Compositor *m_compositor = nullptr; - KWayland::Client::ServerSideDecorationManager *m_deco = nullptr; - KWayland::Client::Seat *m_seat = nullptr; - KWayland::Client::ShmPool *m_shm = nullptr; - KWayland::Client::Shell *m_shell = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; #define MOTION(target) \ kwinApp()->platform()->pointerMotion(target, timestamp++) #define PRESS \ kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++) #define RELEASE \ kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++) AbstractClient *DecorationInputTest::showWindow() { using namespace KWayland::Client; #define VERIFY(statement) \ if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\ return nullptr; #define COMPARE(actual, expected) \ if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ return nullptr; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); VERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); + Surface *surface = Test::createSurface(Test::waylandCompositor()); VERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); VERIFY(shellSurface); - auto deco = m_deco->create(surface, surface); + auto deco = Test::waylandServerSideDecoration()->create(surface, surface); QSignalSpy decoSpy(deco, &ServerSideDecoration::modeChanged); VERIFY(decoSpy.isValid()); VERIFY(decoSpy.wait()); deco->requestMode(ServerSideDecoration::Mode::Server); VERIFY(decoSpy.wait()); COMPARE(deco->mode(), ServerSideDecoration::Mode::Server); // let's render - QImage img(QSize(500, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 500, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface, QSize(500, 50), Qt::blue); - m_connection->flush(); + Test::flushWaylandConnection(); VERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); VERIFY(c); COMPARE(clientAddedSpy.first().first().value(), c); #undef VERIFY #undef COMPARE return c; } void DecorationInputTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); // change some options KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); config->group(QStringLiteral("MouseBindings")).writeEntry("CommandTitlebarWheel", QStringLiteral("above/below")); config->group(QStringLiteral("Windows")).writeEntry("TitlebarDoubleClickCommand", QStringLiteral("OnAllDesktops")); config->group(QStringLiteral("Desktops")).writeEntry("Number", 2); config->sync(); kwinApp()->setConfig(config); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); setenv("QT_QPA_PLATFORM", "wayland", true); waylandServer()->initWorkspace(); } void DecorationInputTest::init() { using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy seatSpy(®istry, &Registry::seatAnnounced); - QSignalSpy decorationSpy(®istry, &Registry::serverSideDecorationManagerAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - QVERIFY(seatSpy.isValid()); - QVERIFY(decorationSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - QVERIFY(!seatSpy.isEmpty()); - QVERIFY(!decorationSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); - m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); - QVERIFY(m_seat->isValid()); - m_deco = registry.createServerSideDecorationManager(decorationSpy.first().first().value(), decorationSpy.first().last().value()); - QVERIFY(m_deco->isValid()); - QSignalSpy hasPointerSpy(m_seat, &Seat::hasPointerChanged); - QVERIFY(hasPointerSpy.isValid()); - QVERIFY(hasPointerSpy.wait()); + QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::Seat | Test::AdditionalWaylandInterface::Decoration)); + QVERIFY(Test::waitForWaylandPointer()); screens()->setCurrent(0); Cursor::setPos(QPoint(640, 512)); } void DecorationInputTest::cleanup() { - delete m_compositor; - m_compositor = nullptr; - delete m_deco; - m_deco = nullptr; - delete m_seat; - m_seat = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_connection->deleteLater(); - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - m_connection = nullptr; - } + Test::destroyWaylandConnection(); } void DecorationInputTest::testAxis_data() { QTest::addColumn("decoPoint"); QTest::addColumn("expectedSection"); QTest::newRow("topLeft") << QPoint(0, 0) << Qt::TopLeftSection; QTest::newRow("top") << QPoint(250, 0) << Qt::TopSection; QTest::newRow("topRight") << QPoint(499, 0) << Qt::TopRightSection; } void DecorationInputTest::testAxis() { AbstractClient *c = showWindow(); QVERIFY(c); QVERIFY(c->isDecorated()); QVERIFY(!c->noBorder()); QCOMPARE(c->titlebarPosition(), AbstractClient::PositionTop); QVERIFY(!c->keepAbove()); QVERIFY(!c->keepBelow()); quint32 timestamp = 1; MOTION(QPoint(c->geometry().center().x(), c->clientPos().y() / 2)); QVERIFY(!input()->pointer()->decoration().isNull()); QCOMPARE(input()->pointer()->decoration()->decoration()->sectionUnderMouse(), Qt::TitleBarArea); // TODO: mouse wheel direction looks wrong to me // simulate wheel kwinApp()->platform()->pointerAxisVertical(5.0, timestamp++); QVERIFY(c->keepBelow()); QVERIFY(!c->keepAbove()); kwinApp()->platform()->pointerAxisVertical(-5.0, timestamp++); QVERIFY(!c->keepBelow()); QVERIFY(!c->keepAbove()); kwinApp()->platform()->pointerAxisVertical(-5.0, timestamp++); QVERIFY(!c->keepBelow()); QVERIFY(c->keepAbove()); // test top most deco pixel, BUG: 362860 c->move(0, 0); QFETCH(QPoint, decoPoint); MOTION(decoPoint); QVERIFY(!input()->pointer()->decoration().isNull()); QCOMPARE(input()->pointer()->decoration()->client(), c); QTEST(input()->pointer()->decoration()->decoration()->sectionUnderMouse(), "expectedSection"); kwinApp()->platform()->pointerAxisVertical(5.0, timestamp++); QVERIFY(!c->keepBelow()); QVERIFY(!c->keepAbove()); } void DecorationInputTest::testDoubleClick_data() { QTest::addColumn("decoPoint"); QTest::addColumn("expectedSection"); QTest::newRow("topLeft") << QPoint(0, 0) << Qt::TopLeftSection; QTest::newRow("top") << QPoint(250, 0) << Qt::TopSection; QTest::newRow("topRight") << QPoint(499, 0) << Qt::TopRightSection; } void KWin::DecorationInputTest::testDoubleClick() { AbstractClient *c = showWindow(); QVERIFY(c); QVERIFY(c->isDecorated()); QVERIFY(!c->noBorder()); QVERIFY(!c->isOnAllDesktops()); quint32 timestamp = 1; MOTION(QPoint(c->geometry().center().x(), c->clientPos().y() / 2)); // double click PRESS; RELEASE; PRESS; RELEASE; QVERIFY(c->isOnAllDesktops()); // double click again PRESS; RELEASE; QVERIFY(c->isOnAllDesktops()); PRESS; RELEASE; QVERIFY(!c->isOnAllDesktops()); // test top most deco pixel, BUG: 362860 c->move(0, 0); QFETCH(QPoint, decoPoint); MOTION(decoPoint); QVERIFY(!input()->pointer()->decoration().isNull()); QCOMPARE(input()->pointer()->decoration()->client(), c); QTEST(input()->pointer()->decoration()->decoration()->sectionUnderMouse(), "expectedSection"); // double click PRESS; RELEASE; QVERIFY(!c->isOnAllDesktops()); PRESS; RELEASE; QVERIFY(c->isOnAllDesktops()); } void DecorationInputTest::testDoubleTap_data() { QTest::addColumn("decoPoint"); QTest::addColumn("expectedSection"); QTest::newRow("topLeft") << QPoint(0, 0) << Qt::TopLeftSection; QTest::newRow("top") << QPoint(250, 0) << Qt::TopSection; QTest::newRow("topRight") << QPoint(499, 0) << Qt::TopRightSection; } void KWin::DecorationInputTest::testDoubleTap() { AbstractClient *c = showWindow(); QVERIFY(c); QVERIFY(c->isDecorated()); QVERIFY(!c->noBorder()); QVERIFY(!c->isOnAllDesktops()); quint32 timestamp = 1; const QPoint tapPoint(c->geometry().center().x(), c->clientPos().y() / 2); // double tap kwinApp()->platform()->touchDown(0, tapPoint, timestamp++); kwinApp()->platform()->touchUp(0, timestamp++); kwinApp()->platform()->touchDown(0, tapPoint, timestamp++); kwinApp()->platform()->touchUp(0, timestamp++); QVERIFY(c->isOnAllDesktops()); // double tap again kwinApp()->platform()->touchDown(0, tapPoint, timestamp++); kwinApp()->platform()->touchUp(0, timestamp++); QVERIFY(c->isOnAllDesktops()); kwinApp()->platform()->touchDown(0, tapPoint, timestamp++); kwinApp()->platform()->touchUp(0, timestamp++); QVERIFY(!c->isOnAllDesktops()); // test top most deco pixel, BUG: 362860 c->move(0, 0); QFETCH(QPoint, decoPoint); // double click kwinApp()->platform()->touchDown(0, decoPoint, timestamp++); QVERIFY(!input()->touch()->decoration().isNull()); QCOMPARE(input()->touch()->decoration()->client(), c); QTEST(input()->touch()->decoration()->decoration()->sectionUnderMouse(), "expectedSection"); kwinApp()->platform()->touchUp(0, timestamp++); QVERIFY(!c->isOnAllDesktops()); kwinApp()->platform()->touchDown(0, decoPoint, timestamp++); kwinApp()->platform()->touchUp(0, timestamp++); QVERIFY(c->isOnAllDesktops()); } void DecorationInputTest::testHover() { AbstractClient *c = showWindow(); QVERIFY(c); QVERIFY(c->isDecorated()); QVERIFY(!c->noBorder()); // our left border is moved out of the visible area, so move the window to a better place c->move(QPoint(20, 0)); quint32 timestamp = 1; MOTION(QPoint(c->geometry().center().x(), c->clientPos().y() / 2)); QCOMPARE(c->cursor(), Qt::ArrowCursor); MOTION(QPoint(20, 0)); QCOMPARE(c->cursor(), Qt::SizeFDiagCursor); MOTION(QPoint(c->geometry().x() + c->geometry().width() / 2, 0)); QCOMPARE(c->cursor(), Qt::SizeVerCursor); MOTION(QPoint(c->geometry().x() + c->geometry().width() - 1, 0)); QCOMPARE(c->cursor(), Qt::SizeBDiagCursor); MOTION(QPoint(c->geometry().x() + c->geometry().width() - 1, c->height() / 2)); QCOMPARE(c->cursor(), Qt::SizeHorCursor); MOTION(QPoint(c->geometry().x() + c->geometry().width() - 1, c->height() - 1)); QCOMPARE(c->cursor(), Qt::SizeFDiagCursor); MOTION(QPoint(c->geometry().x() + c->geometry().width() / 2, c->height() - 1)); QCOMPARE(c->cursor(), Qt::SizeVerCursor); MOTION(QPoint(c->geometry().x(), c->height() - 1)); QCOMPARE(c->cursor(), Qt::SizeBDiagCursor); MOTION(QPoint(c->geometry().x(), c->height() / 2)); QCOMPARE(c->cursor(), Qt::SizeHorCursor); MOTION(c->geometry().center()); QEXPECT_FAIL("", "Cursor not set back on leave", Continue); QCOMPARE(c->cursor(), Qt::ArrowCursor); } void DecorationInputTest::testPressToMove_data() { QTest::addColumn("offset"); QTest::addColumn("offset2"); QTest::addColumn("offset3"); QTest::newRow("To right") << QPoint(10, 0) << QPoint(20, 0) << QPoint(30, 0); QTest::newRow("To left") << QPoint(-10, 0) << QPoint(-20, 0) << QPoint(-30, 0); QTest::newRow("To bottom") << QPoint(0, 10) << QPoint(0, 20) << QPoint(0, 30); QTest::newRow("To top") << QPoint(0, -10) << QPoint(0, -20) << QPoint(0, -30); } void DecorationInputTest::testPressToMove() { AbstractClient *c = showWindow(); QVERIFY(c); QVERIFY(c->isDecorated()); QVERIFY(!c->noBorder()); c->move(screens()->geometry(0).center() - QPoint(c->width()/2, c->height()/2)); QSignalSpy startMoveResizedSpy(c, &AbstractClient::clientStartUserMovedResized); QVERIFY(startMoveResizedSpy.isValid()); QSignalSpy clientFinishUserMovedResizedSpy(c, &AbstractClient::clientFinishUserMovedResized); QVERIFY(clientFinishUserMovedResizedSpy.isValid()); quint32 timestamp = 1; MOTION(QPoint(c->geometry().center().x(), c->y() + c->clientPos().y() / 2)); QCOMPARE(c->cursor(), Qt::ArrowCursor); PRESS; QVERIFY(!c->isMove()); QFETCH(QPoint, offset); MOTION(QPoint(c->geometry().center().x(), c->y() + c->clientPos().y() / 2) + offset); const QPoint oldPos = c->pos(); QVERIFY(c->isMove()); QCOMPARE(startMoveResizedSpy.count(), 1); RELEASE; QTRY_VERIFY(!c->isMove()); QCOMPARE(clientFinishUserMovedResizedSpy.count(), 1); QEXPECT_FAIL("", "Just trigger move doesn't move the window", Continue); QCOMPARE(c->pos(), oldPos + offset); // again PRESS; QVERIFY(!c->isMove()); QFETCH(QPoint, offset2); MOTION(QPoint(c->geometry().center().x(), c->y() + c->clientPos().y() / 2) + offset2); QVERIFY(c->isMove()); QCOMPARE(startMoveResizedSpy.count(), 2); QFETCH(QPoint, offset3); MOTION(QPoint(c->geometry().center().x(), c->y() + c->clientPos().y() / 2) + offset3); RELEASE; QTRY_VERIFY(!c->isMove()); QCOMPARE(clientFinishUserMovedResizedSpy.count(), 2); // TODO: the offset should also be included QCOMPARE(c->pos(), oldPos + offset2 + offset3); } void DecorationInputTest::testTapToMove_data() { QTest::addColumn("offset"); QTest::addColumn("offset2"); QTest::addColumn("offset3"); QTest::newRow("To right") << QPoint(10, 0) << QPoint(20, 0) << QPoint(30, 0); QTest::newRow("To left") << QPoint(-10, 0) << QPoint(-20, 0) << QPoint(-30, 0); QTest::newRow("To bottom") << QPoint(0, 10) << QPoint(0, 20) << QPoint(0, 30); QTest::newRow("To top") << QPoint(0, -10) << QPoint(0, -20) << QPoint(0, -30); } void DecorationInputTest::testTapToMove() { AbstractClient *c = showWindow(); QVERIFY(c); QVERIFY(c->isDecorated()); QVERIFY(!c->noBorder()); c->move(screens()->geometry(0).center() - QPoint(c->width()/2, c->height()/2)); QSignalSpy startMoveResizedSpy(c, &AbstractClient::clientStartUserMovedResized); QVERIFY(startMoveResizedSpy.isValid()); QSignalSpy clientFinishUserMovedResizedSpy(c, &AbstractClient::clientFinishUserMovedResized); QVERIFY(clientFinishUserMovedResizedSpy.isValid()); quint32 timestamp = 1; QPoint p = QPoint(c->geometry().center().x(), c->y() + c->clientPos().y() / 2); kwinApp()->platform()->touchDown(0, p, timestamp++); QVERIFY(!c->isMove()); QFETCH(QPoint, offset); QCOMPARE(input()->touch()->decorationPressId(), 0); kwinApp()->platform()->touchMotion(0, p + offset, timestamp++); const QPoint oldPos = c->pos(); QVERIFY(c->isMove()); QCOMPARE(startMoveResizedSpy.count(), 1); kwinApp()->platform()->touchUp(0, timestamp++); QTRY_VERIFY(!c->isMove()); QCOMPARE(clientFinishUserMovedResizedSpy.count(), 1); QEXPECT_FAIL("", "Just trigger move doesn't move the window", Continue); QCOMPARE(c->pos(), oldPos + offset); // again kwinApp()->platform()->touchDown(1, p + offset, timestamp++); QCOMPARE(input()->touch()->decorationPressId(), 1); QVERIFY(!c->isMove()); QFETCH(QPoint, offset2); kwinApp()->platform()->touchMotion(1, QPoint(c->geometry().center().x(), c->y() + c->clientPos().y() / 2) + offset2, timestamp++); QVERIFY(c->isMove()); QCOMPARE(startMoveResizedSpy.count(), 2); QFETCH(QPoint, offset3); kwinApp()->platform()->touchMotion(1, QPoint(c->geometry().center().x(), c->y() + c->clientPos().y() / 2) + offset3, timestamp++); kwinApp()->platform()->touchUp(1, timestamp++); QTRY_VERIFY(!c->isMove()); QCOMPARE(clientFinishUserMovedResizedSpy.count(), 2); // TODO: the offset should also be included QCOMPARE(c->pos(), oldPos + offset2 + offset3); } } WAYLANDTEST_MAIN(KWin::DecorationInputTest) #include "decoration_input_test.moc" diff --git a/autotests/integration/dont_crash_cancel_animation.cpp b/autotests/integration/dont_crash_cancel_animation.cpp index 6106b9e24..09342201e 100644 --- a/autotests/integration/dont_crash_cancel_animation.cpp +++ b/autotests/integration/dont_crash_cancel_animation.cpp @@ -1,206 +1,137 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "abstract_client.h" #include "client.h" #include "composite.h" #include "deleted.h" #include "effects.h" #include "effectloader.h" #include "screens.h" #include "shell_client.h" #include "wayland_server.h" #include "workspace.h" #include "scripting/scriptedeffect.h" #include -#include #include #include #include #include #include -#include namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_dont_crash_cancel_animation-0"); class DontCrashCancelAnimationFromAnimationEndedTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testScript(); private: void unlock(); AbstractClient *showWindow(); - KWayland::Client::ConnectionThread *m_connection = nullptr; - KWayland::Client::Compositor *m_compositor = nullptr; - KWayland::Client::ShmPool *m_shm = nullptr; - KWayland::Client::Shell *m_shell = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; void DontCrashCancelAnimationFromAnimationEndedTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(Compositor::self()); QSignalSpy compositorToggledSpy(Compositor::self(), &Compositor::compositingToggled); QVERIFY(compositorToggledSpy.isValid()); QVERIFY(compositorToggledSpy.wait()); QVERIFY(effects); } void DontCrashCancelAnimationFromAnimationEndedTest::init() { - using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); + QVERIFY(Test::setupWaylandConnection(s_socketName)); } void DontCrashCancelAnimationFromAnimationEndedTest::cleanup() { - delete m_compositor; - m_compositor = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_connection->deleteLater(); - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - m_connection = nullptr; - } + Test::destroyWaylandConnection(); } void DontCrashCancelAnimationFromAnimationEndedTest::testScript() { // load a scripted effect which deletes animation data ScriptedEffect *effect = ScriptedEffect::create(QStringLiteral("crashy"), QFINDTESTDATA("data/anim-data-delete-effect/effect.js"), 10); QVERIFY(effect); const auto children = effects->children(); for (auto it = children.begin(); it != children.end(); ++it) { if (qstrcmp((*it)->metaObject()->className(), "KWin::EffectLoader") != 0) { continue; } QVERIFY(QMetaObject::invokeMethod(*it, "effectLoaded", Q_ARG(KWin::Effect*, effect), Q_ARG(QString, QStringLiteral("crashy")))); break; } QVERIFY(static_cast(effects)->isEffectLoaded(QStringLiteral("crashy"))); using namespace KWayland::Client; // create a window QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); + Surface *surface = Test::createSurface(Test::waylandCompositor()); QVERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); QVERIFY(shellSurface); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface, QSize(100, 50), Qt::blue); - m_connection->flush(); + Test::flushWaylandConnection(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); // make sure we animate QTest::qWait(200); // wait for the window to be passed to Deleted QSignalSpy windowDeletedSpy(c, &AbstractClient::windowClosed); QVERIFY(windowDeletedSpy.isValid()); surface->deleteLater(); QVERIFY(windowDeletedSpy.wait()); // make sure we animate QTest::qWait(200); } } WAYLANDTEST_MAIN(KWin::DontCrashCancelAnimationFromAnimationEndedTest) #include "dont_crash_cancel_animation.moc" diff --git a/autotests/integration/dont_crash_empty_deco.cpp b/autotests/integration/dont_crash_empty_deco.cpp index 8ca2f2ed9..bdd3c4813 100644 --- a/autotests/integration/dont_crash_empty_deco.cpp +++ b/autotests/integration/dont_crash_empty_deco.cpp @@ -1,224 +1,121 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "client.h" #include "cursor.h" #include "screenedge.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_dont_crash_empty_decoration-0"); class DontCrashEmptyDecorationTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); - void cleanup(); void testBug361551(); - -private: - KWayland::Client::ConnectionThread *m_connection = nullptr; - KWayland::Client::Compositor *m_compositor = nullptr; - KWayland::Client::ServerSideDecorationManager *m_deco = nullptr; - KWayland::Client::Seat *m_seat = nullptr; - KWayland::Client::ShmPool *m_shm = nullptr; - KWayland::Client::Shell *m_shell = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; void DontCrashEmptyDecorationTest::initTestCase() { if (!QFile::exists(QStringLiteral("/dev/dri/card0"))) { QSKIP("Needs a dri device"); } qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); // this test needs to enforce OpenGL compositing to get into the crashy condition qputenv("KWIN_COMPOSE", QByteArrayLiteral("O2")); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); setenv("QT_QPA_PLATFORM", "wayland", true); waylandServer()->initWorkspace(); } void DontCrashEmptyDecorationTest::init() { - using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy seatSpy(®istry, &Registry::seatAnnounced); - QSignalSpy decorationSpy(®istry, &Registry::serverSideDecorationManagerAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - QVERIFY(seatSpy.isValid()); - QVERIFY(decorationSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - QVERIFY(!seatSpy.isEmpty()); - QVERIFY(!decorationSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); - m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); - QVERIFY(m_seat->isValid()); - m_deco = registry.createServerSideDecorationManager(decorationSpy.first().first().value(), decorationSpy.first().last().value()); - QVERIFY(m_deco->isValid()); - QSignalSpy hasPointerSpy(m_seat, &Seat::hasPointerChanged); - QVERIFY(hasPointerSpy.isValid()); - QVERIFY(hasPointerSpy.wait()); - screens()->setCurrent(0); Cursor::setPos(QPoint(640, 512)); } -void DontCrashEmptyDecorationTest::cleanup() -{ - delete m_compositor; - m_compositor = nullptr; - delete m_deco; - m_deco = nullptr; - delete m_seat; - m_seat = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_connection->deleteLater(); - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - m_connection = nullptr; - } -} - void DontCrashEmptyDecorationTest::testBug361551() { // this test verifies that resizing an X11 window to an invalid size does not result in crash on unmap // when the DecorationRenderer gets copied to the Deleted // there a repaint is scheduled and the resulting texture is invalid if the window size is invalid // create an xcb window xcb_connection_t *c = xcb_connect(nullptr, nullptr); QVERIFY(!xcb_connection_has_error(c)); xcb_window_t w = xcb_generate_id(c); xcb_create_window(c, XCB_COPY_FROM_PARENT, w, rootWindow(), 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); xcb_map_window(c, w); xcb_flush(c); // we should get a client for it QSignalSpy windowCreatedSpy(workspace(), &Workspace::clientAdded); QVERIFY(windowCreatedSpy.isValid()); QVERIFY(windowCreatedSpy.wait()); Client *client = windowCreatedSpy.first().first().value(); QVERIFY(client); QCOMPARE(client->window(), w); QVERIFY(client->isDecorated()); // let's set a stupid geometry client->setGeometry(0, 0, 0, 0); QCOMPARE(client->geometry(), QRect(0, 0, 0, 0)); // and destroy the window again xcb_unmap_window(c, w); xcb_destroy_window(c, w); xcb_flush(c); xcb_disconnect(c); QSignalSpy windowClosedSpy(client, &Client::windowClosed); QVERIFY(windowClosedSpy.isValid()); QVERIFY(windowClosedSpy.wait()); } } WAYLANDTEST_MAIN(KWin::DontCrashEmptyDecorationTest) #include "dont_crash_empty_deco.moc" diff --git a/autotests/integration/dont_crash_no_border.cpp b/autotests/integration/dont_crash_no_border.cpp index 47b5f4eda..653cf0c21 100644 --- a/autotests/integration/dont_crash_no_border.cpp +++ b/autotests/integration/dont_crash_no_border.cpp @@ -1,224 +1,129 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "client.h" #include "cursor.h" #include "screenedge.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include -#include -#include -#include -#include -#include #include #include -#include -#include #include #include #include namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_dont_crash_no_border-0"); class DontCrashNoBorder : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testCreateWindow(); - -private: - KWayland::Client::ConnectionThread *m_connection = nullptr; - KWayland::Client::Compositor *m_compositor = nullptr; - KWayland::Client::ServerSideDecorationManager *m_deco = nullptr; - KWayland::Client::Seat *m_seat = nullptr; - KWayland::Client::ShmPool *m_shm = nullptr; - KWayland::Client::Shell *m_shell = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; void DontCrashNoBorder::initTestCase() { if (!QFile::exists(QStringLiteral("/dev/dri/card0"))) { QSKIP("Needs a dri device"); } qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); config->group("org.kde.kdecoration2").writeEntry("NoPlugin", true); config->sync(); kwinApp()->setConfig(config); // this test needs to enforce OpenGL compositing to get into the crashy condition qputenv("KWIN_COMPOSE", QByteArrayLiteral("O2")); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); setenv("QT_QPA_PLATFORM", "wayland", true); waylandServer()->initWorkspace(); } void DontCrashNoBorder::init() { - using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy seatSpy(®istry, &Registry::seatAnnounced); - QSignalSpy decorationSpy(®istry, &Registry::serverSideDecorationManagerAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - QVERIFY(seatSpy.isValid()); - QVERIFY(decorationSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - QVERIFY(!seatSpy.isEmpty()); - QVERIFY(!decorationSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); - m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); - QVERIFY(m_seat->isValid()); - m_deco = registry.createServerSideDecorationManager(decorationSpy.first().first().value(), decorationSpy.first().last().value()); - QVERIFY(m_deco->isValid()); - QSignalSpy hasPointerSpy(m_seat, &Seat::hasPointerChanged); - QVERIFY(hasPointerSpy.isValid()); - QVERIFY(hasPointerSpy.wait()); + QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::Decoration)); screens()->setCurrent(0); Cursor::setPos(QPoint(640, 512)); } void DontCrashNoBorder::cleanup() { - delete m_compositor; - m_compositor = nullptr; - delete m_deco; - m_deco = nullptr; - delete m_seat; - m_seat = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_connection->deleteLater(); - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - m_connection = nullptr; - } + Test::destroyWaylandConnection(); } void DontCrashNoBorder::testCreateWindow() { // create a window and ensure that this doesn't crash using namespace KWayland::Client; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); - QVERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + QScopedPointer surface(Test::createSurface()); + QVERIFY(!surface.isNull()); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(shellSurface); - auto deco = m_deco->create(surface, surface); - QSignalSpy decoSpy(deco, &ServerSideDecoration::modeChanged); + QScopedPointer deco(Test::waylandServerSideDecoration()->create(surface.data())); + QSignalSpy decoSpy(deco.data(), &ServerSideDecoration::modeChanged); QVERIFY(decoSpy.isValid()); QVERIFY(decoSpy.wait()); deco->requestMode(ServerSideDecoration::Mode::Server); QVERIFY(decoSpy.wait()); QCOMPARE(deco->mode(), ServerSideDecoration::Mode::Server); // let's render - QImage img(QSize(500, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 500, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(500, 50), Qt::blue); - m_connection->flush(); + Test::flushWaylandConnection(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); QVERIFY(!c->isDecorated()); } } WAYLANDTEST_MAIN(KWin::DontCrashNoBorder) #include "dont_crash_no_border.moc" diff --git a/autotests/integration/input_stacking_order.cpp b/autotests/integration/input_stacking_order.cpp index 507d1d749..de63f2d41 100644 --- a/autotests/integration/input_stacking_order.cpp +++ b/autotests/integration/input_stacking_order.cpp @@ -1,256 +1,180 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "abstract_client.h" #include "cursor.h" #include "deleted.h" #include "screenedge.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include #include #include #include #include #include #include #include #include #include #include namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_input_stacking_order-0"); class InputStackingOrderTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testPointerFocusUpdatesOnStackingOrderChange(); private: void render(KWayland::Client::Surface *surface); - KWayland::Client::ConnectionThread *m_connection = nullptr; - KWayland::Client::Compositor *m_compositor = nullptr; - KWayland::Client::Seat *m_seat = nullptr; - KWayland::Client::ShmPool *m_shm = nullptr; - KWayland::Client::Shell *m_shell = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; void InputStackingOrderTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); setenv("QT_QPA_PLATFORM", "wayland", true); waylandServer()->initWorkspace(); } void InputStackingOrderTest::init() { using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy seatSpy(®istry, &Registry::seatAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - QVERIFY(seatSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - QVERIFY(!seatSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); - m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); - QVERIFY(m_seat->isValid()); - QSignalSpy hasPointerSpy(m_seat, &Seat::hasPointerChanged); - QVERIFY(hasPointerSpy.isValid()); - QVERIFY(hasPointerSpy.wait()); + QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::Seat)); + QVERIFY(Test::waitForWaylandPointer()); screens()->setCurrent(0); Cursor::setPos(QPoint(640, 512)); } void InputStackingOrderTest::cleanup() { - delete m_compositor; - m_compositor = nullptr; - delete m_seat; - m_seat = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_connection->deleteLater(); - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - m_connection = nullptr; - } + Test::destroyWaylandConnection(); } void InputStackingOrderTest::render(KWayland::Client::Surface *surface) { - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(KWayland::Client::Surface::CommitFlag::None); - m_connection->flush(); + Test::render(surface, QSize(100, 50), Qt::blue); + Test::flushWaylandConnection(); } void InputStackingOrderTest::testPointerFocusUpdatesOnStackingOrderChange() { // this test creates two windows which overlap // the pointer is in the overlapping area which means the top most window has focus // as soon as the top most window gets lowered the window should lose focus and the // other window should gain focus without a mouse event in between using namespace KWayland::Client; // create pointer and signal spy for enter and leave signals - auto pointer = m_seat->createPointer(m_seat); + auto pointer = Test::waylandSeat()->createPointer(Test::waylandSeat()); QVERIFY(pointer); QVERIFY(pointer->isValid()); QSignalSpy enteredSpy(pointer, &Pointer::entered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(pointer, &Pointer::left); QVERIFY(leftSpy.isValid()); // now create the two windows and make them overlap QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface1 = m_compositor->createSurface(m_compositor); + Surface *surface1 = Test::createSurface(Test::waylandCompositor()); QVERIFY(surface1); - ShellSurface *shellSurface1 = m_shell->createSurface(surface1, surface1); + ShellSurface *shellSurface1 = Test::createShellSurface(surface1, surface1); QVERIFY(shellSurface1); render(surface1); QVERIFY(clientAddedSpy.wait()); AbstractClient *window1 = workspace()->activeClient(); QVERIFY(window1); - Surface *surface2 = m_compositor->createSurface(m_compositor); + Surface *surface2 = Test::createSurface(Test::waylandCompositor()); QVERIFY(surface2); - ShellSurface *shellSurface2 = m_shell->createSurface(surface2, surface2); + ShellSurface *shellSurface2 = Test::createShellSurface(surface2, surface2); QVERIFY(shellSurface2); render(surface2); QVERIFY(clientAddedSpy.wait()); AbstractClient *window2 = workspace()->activeClient(); QVERIFY(window2); QVERIFY(window1 != window2); // now make windows overlap window2->move(window1->pos()); QCOMPARE(window1->geometry(), window2->geometry()); // enter kwinApp()->platform()->pointerMotion(QPointF(25, 25), 1); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 1); // window 2 should have focus QCOMPARE(pointer->enteredSurface(), surface2); // also on the server QCOMPARE(waylandServer()->seat()->focusedPointerSurface(), window2->surface()); // raise window 1 above window 2 QVERIFY(leftSpy.isEmpty()); workspace()->raiseClient(window1); // should send leave to window2 QVERIFY(leftSpy.wait()); QCOMPARE(leftSpy.count(), 1); // and an enter to window1 QCOMPARE(enteredSpy.count(), 2); QCOMPARE(pointer->enteredSurface(), surface1); QCOMPARE(waylandServer()->seat()->focusedPointerSurface(), window1->surface()); // let's destroy window1, that should pass focus to window2 again QSignalSpy windowClosedSpy(window1, &Toplevel::windowClosed); QVERIFY(windowClosedSpy.isValid()); surface1->deleteLater(); QVERIFY(windowClosedSpy.wait()); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 3); QCOMPARE(pointer->enteredSurface(), surface2); QCOMPARE(waylandServer()->seat()->focusedPointerSurface(), window2->surface()); } } WAYLANDTEST_MAIN(KWin::InputStackingOrderTest) #include "input_stacking_order.moc" diff --git a/autotests/integration/kwin_wayland_test.h b/autotests/integration/kwin_wayland_test.h index c35d74273..2dda31941 100644 --- a/autotests/integration/kwin_wayland_test.h +++ b/autotests/integration/kwin_wayland_test.h @@ -1,74 +1,144 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_WAYLAND_TEST_H #define KWIN_WAYLAND_TEST_H #include "../../main.h" // Qt #include +namespace KWayland +{ +namespace Client +{ +class ConnectionThread; +class Compositor; +class PlasmaShell; +class PlasmaWindowManagement; +class Seat; +class ServerSideDecorationManager; +class Shell; +class ShellSurface; +class ShmPool; +class Surface; +} +} + namespace KWin { class WaylandTestApplication : public Application { Q_OBJECT public: WaylandTestApplication(int &argc, char **argv); virtual ~WaylandTestApplication(); protected: void performStartup() override; private: void createBackend(); void createX11Connection(); void continueStartupWithScreens(); void continueStartupWithX(); void startXwaylandServer(); int m_xcbConnectionFd = -1; QProcess *m_xwaylandProcess = nullptr; QMetaObject::Connection m_xwaylandFailConnection; }; +namespace Test +{ + +enum class AdditionalWaylandInterface { + Seat = 1 << 0, + Decoration = 1 << 1, + PlasmaShell = 1 << 2, + WindowManagement = 1 << 3 +}; +Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface) +/** + * Creates a Wayland Connection in a dedicated thread and creates various + * client side objects which can be used to create windows. + * @param socketName The name of the Wayland socket to connect to. + * @returns @c true if created successfully, @c false if there was an error + * @see destroyWaylandConnection + **/ +bool setupWaylandConnection(const QString &socketName, AdditionalWaylandInterfaces flags = AdditionalWaylandInterfaces()); + +/** + * Destroys the Wayland Connection created with @link{setupWaylandConnection}. + * This can be called from cleanup in order to ensure that no Wayland Connection + * leaks into the next test method. + * @see setupWaylandConnection + */ +void destroyWaylandConnection(); + +KWayland::Client::ConnectionThread *waylandConnection(); +KWayland::Client::Compositor *waylandCompositor(); +KWayland::Client::Shell *waylandShell(); +KWayland::Client::ShmPool *waylandShmPool(); +KWayland::Client::Seat *waylandSeat(); +KWayland::Client::ServerSideDecorationManager *waylandServerSideDecoration(); +KWayland::Client::PlasmaShell *waylandPlasmaShell(); +KWayland::Client::PlasmaWindowManagement *waylandWindowManagement(); + +bool waitForWaylandPointer(); +bool waitForWaylandTouch(); + +void flushWaylandConnection(); + +KWayland::Client::Surface *createSurface(QObject *parent = nullptr); +KWayland::Client::ShellSurface *createShellSurface(KWayland::Client::Surface *surface, QObject *parent = nullptr); + +/** + * Creates a shared memory buffer of @p size in @p color and attaches it to the @p surface. + * The @p surface gets damaged and committed, thus it's rendered. + **/ +void render(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format = QImage::Format_ARGB32); +} + } +Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::Test::AdditionalWaylandInterfaces) + #define WAYLANDTEST_MAIN_HELPER(TestObject, DPI) \ int main(int argc, char *argv[]) \ { \ setenv("QT_QPA_PLATFORM", "wayland-org.kde.kwin.qpa", true); \ setenv("QT_QPA_PLATFORM_PLUGIN_PATH", KWINQPAPATH, true); \ setenv("KWIN_FORCE_OWN_QPA", "1", true); \ DPI; \ KWin::WaylandTestApplication app(argc, argv); \ app.setAttribute(Qt::AA_Use96Dpi, true); \ TestObject tc; \ return QTest::qExec(&tc, argc, argv); \ } #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) #define WAYLANDTEST_MAIN(TestObject) WAYLANDTEST_MAIN_HELPER(TestObject, QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling) ) #else #define WAYLANDTEST_MAIN(TestObject) WAYLANDTEST_MAIN_HELPER(TestObject,) #endif #endif diff --git a/autotests/integration/lockscreen.cpp b/autotests/integration/lockscreen.cpp index 170c27f67..0fff8b388 100644 --- a/autotests/integration/lockscreen.cpp +++ b/autotests/integration/lockscreen.cpp @@ -1,821 +1,753 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "abstract_client.h" #include "cursor.h" #include "screenedge.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include #include #include -#include #include #include #include #include #include #include #include #include #include //screenlocker #include #include Q_DECLARE_METATYPE(Qt::Orientation) namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_lock_screen-0"); class LockScreenTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testPointer(); void testPointerButton(); void testPointerAxis(); void testKeyboard(); void testScreenEdge(); void testEffects(); void testEffectsKeyboard(); void testEffectsKeyboardAutorepeat(); void testMoveWindow(); void testPointerShortcut(); void testAxisShortcut_data(); void testAxisShortcut(); void testKeyboardShortcut(); void testTouch(); private: void unlock(); AbstractClient *showWindow(); KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; KWayland::Client::Seat *m_seat = nullptr; KWayland::Client::ShmPool *m_shm = nullptr; KWayland::Client::Shell *m_shell = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; class HelperEffect : public Effect { Q_OBJECT public: HelperEffect() {} ~HelperEffect() {} void windowInputMouseEvent(QEvent*) override { emit inputEvent(); } void grabbedKeyboardEvent(QKeyEvent *e) override { emit keyEvent(e->text()); } Q_SIGNALS: void inputEvent(); void keyEvent(const QString&); }; #define LOCK \ QVERIFY(!waylandServer()->isScreenLocked()); \ QSignalSpy lockStateChangedSpy(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged); \ QVERIFY(lockStateChangedSpy.isValid()); \ ScreenLocker::KSldApp::self()->lock(ScreenLocker::EstablishLock::Immediate); \ QCOMPARE(lockStateChangedSpy.count(), 1); \ QVERIFY(waylandServer()->isScreenLocked()); #define UNLOCK \ int expectedLockCount = 1; \ if (ScreenLocker::KSldApp::self()->lockState() == ScreenLocker::KSldApp::Locked) { \ expectedLockCount = 2; \ } \ QCOMPARE(lockStateChangedSpy.count(), expectedLockCount); \ unlock(); \ if (lockStateChangedSpy.count() < expectedLockCount + 1) { \ QVERIFY(lockStateChangedSpy.wait()); \ } \ QCOMPARE(lockStateChangedSpy.count(), expectedLockCount + 1); \ QVERIFY(!waylandServer()->isScreenLocked()); #define MOTION(target) \ kwinApp()->platform()->pointerMotion(target, timestamp++) #define PRESS \ kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++) #define RELEASE \ kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++) #define KEYPRESS( key ) \ kwinApp()->platform()->keyboardKeyPressed(key, timestamp++) #define KEYRELEASE( key ) \ kwinApp()->platform()->keyboardKeyReleased(key, timestamp++) void LockScreenTest::unlock() { using namespace ScreenLocker; const auto children = KSldApp::self()->children(); for (auto it = children.begin(); it != children.end(); ++it) { if (qstrcmp((*it)->metaObject()->className(), "LogindIntegration") != 0) { continue; } QMetaObject::invokeMethod(*it, "requestUnlock"); break; } } AbstractClient *LockScreenTest::showWindow() { using namespace KWayland::Client; #define VERIFY(statement) \ if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\ return nullptr; #define COMPARE(actual, expected) \ if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ return nullptr; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); VERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); + Surface *surface = Test::createSurface(m_compositor); VERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); VERIFY(shellSurface); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface, QSize(100, 50), Qt::blue); m_connection->flush(); VERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); VERIFY(c); COMPARE(clientAddedSpy.first().first().value(), c); #undef VERIFY #undef COMPARE return c; } void LockScreenTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); setenv("QT_QPA_PLATFORM", "wayland", true); setenv("QMLSCENE_DEVICE", "softwarecontext", true); waylandServer()->initWorkspace(); } void LockScreenTest::init() { - using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy seatSpy(®istry, &Registry::seatAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - QVERIFY(seatSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - QVERIFY(!seatSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); - m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); - QVERIFY(m_seat->isValid()); - QSignalSpy hasPointerSpy(m_seat, &Seat::hasPointerChanged); - QVERIFY(hasPointerSpy.isValid()); - QVERIFY(hasPointerSpy.wait()); + QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::Seat)); + QVERIFY(Test::waitForWaylandPointer()); + m_connection = Test::waylandConnection(); + m_compositor = Test::waylandCompositor(); + m_shell = Test::waylandShell(); + m_shm = Test::waylandShmPool(); + m_seat = Test::waylandSeat(); screens()->setCurrent(0); Cursor::setPos(QPoint(640, 512)); } void LockScreenTest::cleanup() { - delete m_compositor; - m_compositor = nullptr; - delete m_seat; - m_seat = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_connection->deleteLater(); - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - m_connection = nullptr; - } + Test::destroyWaylandConnection(); } void LockScreenTest::testPointer() { using namespace KWayland::Client; QScopedPointer pointer(m_seat->createPointer()); QVERIFY(!pointer.isNull()); QSignalSpy enteredSpy(pointer.data(), &Pointer::entered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(pointer.data(), &Pointer::left); QVERIFY(leftSpy.isValid()); AbstractClient *c = showWindow(); QVERIFY(c); // first move cursor into the center of the window quint32 timestamp = 1; MOTION(c->geometry().center()); QVERIFY(enteredSpy.wait()); LOCK QVERIFY(leftSpy.wait(100)); QCOMPARE(leftSpy.count(), 1); // simulate moving out in and out again MOTION(c->geometry().center()); MOTION(c->geometry().bottomRight() + QPoint(100, 100)); MOTION(c->geometry().bottomRight() + QPoint(100, 100)); QVERIFY(!leftSpy.wait(100)); QCOMPARE(leftSpy.count(), 1); QCOMPARE(enteredSpy.count(), 1); // go back on the window MOTION(c->geometry().center()); // and unlock UNLOCK QVERIFY(enteredSpy.wait(100)); QCOMPARE(enteredSpy.count(), 2); // move on the window MOTION(c->geometry().center() + QPoint(100, 100)); QVERIFY(leftSpy.wait()); MOTION(c->geometry().center()); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 3); } void LockScreenTest::testPointerButton() { using namespace KWayland::Client; QScopedPointer pointer(m_seat->createPointer()); QVERIFY(!pointer.isNull()); QSignalSpy buttonChangedSpy(pointer.data(), &Pointer::buttonStateChanged); QVERIFY(buttonChangedSpy.isValid()); AbstractClient *c = showWindow(); QVERIFY(c); // first move cursor into the center of the window quint32 timestamp = 1; MOTION(c->geometry().center()); // and simulate a click PRESS; QVERIFY(buttonChangedSpy.wait()); RELEASE; QVERIFY(buttonChangedSpy.wait()); LOCK // and simulate a click PRESS; QVERIFY(!buttonChangedSpy.wait(100)); RELEASE; QVERIFY(!buttonChangedSpy.wait(100)); UNLOCK // and click again PRESS; QVERIFY(buttonChangedSpy.wait()); RELEASE; QVERIFY(buttonChangedSpy.wait()); } void LockScreenTest::testPointerAxis() { using namespace KWayland::Client; QScopedPointer pointer(m_seat->createPointer()); QVERIFY(!pointer.isNull()); QSignalSpy axisChangedSpy(pointer.data(), &Pointer::axisChanged); QVERIFY(axisChangedSpy.isValid()); AbstractClient *c = showWindow(); QVERIFY(c); // first move cursor into the center of the window quint32 timestamp = 1; MOTION(c->geometry().center()); // and simulate axis kwinApp()->platform()->pointerAxisHorizontal(5.0, timestamp++); QVERIFY(axisChangedSpy.wait()); LOCK // and simulate axis kwinApp()->platform()->pointerAxisHorizontal(5.0, timestamp++); QVERIFY(!axisChangedSpy.wait(100)); kwinApp()->platform()->pointerAxisVertical(5.0, timestamp++); QVERIFY(!axisChangedSpy.wait(100)); // and unlock UNLOCK // and move axis again kwinApp()->platform()->pointerAxisHorizontal(5.0, timestamp++); QVERIFY(axisChangedSpy.wait()); kwinApp()->platform()->pointerAxisVertical(5.0, timestamp++); QVERIFY(axisChangedSpy.wait()); } void LockScreenTest::testKeyboard() { using namespace KWayland::Client; QScopedPointer keyboard(m_seat->createKeyboard()); QVERIFY(!keyboard.isNull()); QSignalSpy enteredSpy(keyboard.data(), &Keyboard::entered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(keyboard.data(), &Keyboard::left); QVERIFY(leftSpy.isValid()); QSignalSpy keyChangedSpy(keyboard.data(), &Keyboard::keyChanged); QVERIFY(keyChangedSpy.isValid()); AbstractClient *c = showWindow(); QVERIFY(c); QVERIFY(enteredSpy.wait()); QTRY_COMPARE(enteredSpy.count(), 1); quint32 timestamp = 1; KEYPRESS(KEY_A); QVERIFY(keyChangedSpy.wait()); QCOMPARE(keyChangedSpy.count(), 1); QCOMPARE(keyChangedSpy.at(0).at(0).value(), quint32(KEY_A)); QCOMPARE(keyChangedSpy.at(0).at(1).value(), Keyboard::KeyState::Pressed); QCOMPARE(keyChangedSpy.at(0).at(2).value(), quint32(1)); KEYRELEASE(KEY_A); QVERIFY(keyChangedSpy.wait()); QCOMPARE(keyChangedSpy.count(), 2); QCOMPARE(keyChangedSpy.at(1).at(0).value(), quint32(KEY_A)); QCOMPARE(keyChangedSpy.at(1).at(1).value(), Keyboard::KeyState::Released); QCOMPARE(keyChangedSpy.at(1).at(2).value(), quint32(2)); LOCK QVERIFY(leftSpy.wait()); KEYPRESS(KEY_B); KEYRELEASE(KEY_B); QCOMPARE(leftSpy.count(), 1); QCOMPARE(keyChangedSpy.count(), 2); UNLOCK QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 2); KEYPRESS(KEY_C); QVERIFY(keyChangedSpy.wait()); QCOMPARE(keyChangedSpy.count(), 3); KEYRELEASE(KEY_C); QVERIFY(keyChangedSpy.wait()); QCOMPARE(keyChangedSpy.count(), 4); QCOMPARE(enteredSpy.count(), 2); QCOMPARE(keyChangedSpy.at(2).at(0).value(), quint32(KEY_C)); QCOMPARE(keyChangedSpy.at(3).at(0).value(), quint32(KEY_C)); QCOMPARE(keyChangedSpy.at(2).at(2).value(), quint32(5)); QCOMPARE(keyChangedSpy.at(3).at(2).value(), quint32(6)); QCOMPARE(keyChangedSpy.at(2).at(1).value(), Keyboard::KeyState::Pressed); QCOMPARE(keyChangedSpy.at(3).at(1).value(), Keyboard::KeyState::Released); } void LockScreenTest::testScreenEdge() { QSignalSpy screenEdgeSpy(ScreenEdges::self(), &ScreenEdges::approaching); QVERIFY(screenEdgeSpy.isValid()); QCOMPARE(screenEdgeSpy.count(), 0); quint32 timestamp = 1; MOTION(QPoint(5, 5)); QCOMPARE(screenEdgeSpy.count(), 1); LOCK MOTION(QPoint(4, 4)); QCOMPARE(screenEdgeSpy.count(), 1); // and unlock UNLOCK MOTION(QPoint(5, 5)); QCOMPARE(screenEdgeSpy.count(), 2); } void LockScreenTest::testEffects() { QScopedPointer effect(new HelperEffect); QSignalSpy inputSpy(effect.data(), &HelperEffect::inputEvent); QVERIFY(inputSpy.isValid()); effects->startMouseInterception(effect.data(), Qt::ArrowCursor); quint32 timestamp = 1; QCOMPARE(inputSpy.count(), 0); MOTION(QPoint(5, 5)); QCOMPARE(inputSpy.count(), 1); // simlate click PRESS; QCOMPARE(inputSpy.count(), 2); RELEASE; QCOMPARE(inputSpy.count(), 3); LOCK MOTION(QPoint(6, 6)); QCOMPARE(inputSpy.count(), 3); // simlate click PRESS; QCOMPARE(inputSpy.count(), 3); RELEASE; QCOMPARE(inputSpy.count(), 3); UNLOCK MOTION(QPoint(5, 5)); QCOMPARE(inputSpy.count(), 4); // simlate click PRESS; QCOMPARE(inputSpy.count(), 5); RELEASE; QCOMPARE(inputSpy.count(), 6); effects->stopMouseInterception(effect.data()); } void LockScreenTest::testEffectsKeyboard() { QScopedPointer effect(new HelperEffect); QSignalSpy inputSpy(effect.data(), &HelperEffect::keyEvent); QVERIFY(inputSpy.isValid()); effects->grabKeyboard(effect.data()); quint32 timestamp = 1; KEYPRESS(KEY_A); QCOMPARE(inputSpy.count(), 1); QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a")); KEYRELEASE(KEY_A); QCOMPARE(inputSpy.count(), 2); QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a")); QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a")); LOCK KEYPRESS(KEY_B); QCOMPARE(inputSpy.count(), 2); KEYRELEASE(KEY_B); QCOMPARE(inputSpy.count(), 2); UNLOCK KEYPRESS(KEY_C); QCOMPARE(inputSpy.count(), 3); QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a")); QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a")); QCOMPARE(inputSpy.at(2).first().toString(), QStringLiteral("c")); KEYRELEASE(KEY_C); QCOMPARE(inputSpy.count(), 4); QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a")); QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a")); QCOMPARE(inputSpy.at(2).first().toString(), QStringLiteral("c")); QCOMPARE(inputSpy.at(3).first().toString(), QStringLiteral("c")); effects->ungrabKeyboard(); } void LockScreenTest::testEffectsKeyboardAutorepeat() { // this test is just like testEffectsKeyboard, but tests auto repeat key events // while the key is pressed the Effect should get auto repeated events // but the lock screen should filter them out QScopedPointer effect(new HelperEffect); QSignalSpy inputSpy(effect.data(), &HelperEffect::keyEvent); QVERIFY(inputSpy.isValid()); effects->grabKeyboard(effect.data()); // we need to configure the key repeat first. It is only enabled on libinput waylandServer()->seat()->setKeyRepeatInfo(25, 300); quint32 timestamp = 1; KEYPRESS(KEY_A); QCOMPARE(inputSpy.count(), 1); QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a")); QVERIFY(inputSpy.wait()); QVERIFY(inputSpy.count() > 1); // and still more events QVERIFY(inputSpy.wait()); QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a")); // now release inputSpy.clear(); KEYRELEASE(KEY_A); QCOMPARE(inputSpy.count(), 1); // while locked key repeat should not pass any events to the Effect LOCK KEYPRESS(KEY_B); QVERIFY(!inputSpy.wait(200)); KEYRELEASE(KEY_B); QVERIFY(!inputSpy.wait(200)); UNLOCK // don't test again, that's covered by testEffectsKeyboard effects->ungrabKeyboard(); } void LockScreenTest::testMoveWindow() { using namespace KWayland::Client; AbstractClient *c = showWindow(); QVERIFY(c); QSignalSpy clientStepUserMovedResizedSpy(c, &AbstractClient::clientStepUserMovedResized); QVERIFY(clientStepUserMovedResizedSpy.isValid()); quint32 timestamp = 1; workspace()->slotWindowMove(); QCOMPARE(workspace()->getMovingClient(), c); QVERIFY(c->isMove()); kwinApp()->platform()->keyboardKeyPressed(KEY_RIGHT, timestamp++); kwinApp()->platform()->keyboardKeyReleased(KEY_RIGHT, timestamp++); QEXPECT_FAIL("", "First event is ignored", Continue); QCOMPARE(clientStepUserMovedResizedSpy.count(), 1); // TODO adjust once the expected fail is fixed kwinApp()->platform()->keyboardKeyPressed(KEY_RIGHT, timestamp++); kwinApp()->platform()->keyboardKeyReleased(KEY_RIGHT, timestamp++); QCOMPARE(clientStepUserMovedResizedSpy.count(), 1); // while locking our window should continue to be in move resize LOCK QCOMPARE(workspace()->getMovingClient(), c); QVERIFY(c->isMove()); kwinApp()->platform()->keyboardKeyPressed(KEY_RIGHT, timestamp++); kwinApp()->platform()->keyboardKeyReleased(KEY_RIGHT, timestamp++); QCOMPARE(clientStepUserMovedResizedSpy.count(), 1); UNLOCK QCOMPARE(workspace()->getMovingClient(), c); QVERIFY(c->isMove()); kwinApp()->platform()->keyboardKeyPressed(KEY_RIGHT, timestamp++); kwinApp()->platform()->keyboardKeyReleased(KEY_RIGHT, timestamp++); QCOMPARE(clientStepUserMovedResizedSpy.count(), 2); kwinApp()->platform()->keyboardKeyPressed(KEY_ESC, timestamp++); kwinApp()->platform()->keyboardKeyReleased(KEY_ESC, timestamp++); QVERIFY(!c->isMove()); } void LockScreenTest::testPointerShortcut() { using namespace KWayland::Client; QScopedPointer action(new QAction(nullptr)); QSignalSpy actionSpy(action.data(), &QAction::triggered); QVERIFY(actionSpy.isValid()); input()->registerPointerShortcut(Qt::MetaModifier, Qt::LeftButton, action.data()); // try to trigger the shortcut quint32 timestamp = 1; #define PERFORM(expectedCount) \ kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTMETA, timestamp++); \ PRESS; \ QCoreApplication::instance()->processEvents(); \ QCOMPARE(actionSpy.count(), expectedCount); \ RELEASE; \ kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTMETA, timestamp++); \ QCoreApplication::instance()->processEvents(); \ QCOMPARE(actionSpy.count(), expectedCount); PERFORM(1) // now the same thing with a locked screen LOCK PERFORM(1) // and as unlocked UNLOCK PERFORM(2) #undef PERFORM } void LockScreenTest::testAxisShortcut_data() { QTest::addColumn("direction"); QTest::addColumn("sign"); QTest::newRow("up") << Qt::Vertical << 1; QTest::newRow("down") << Qt::Vertical << -1; QTest::newRow("left") << Qt::Horizontal << 1; QTest::newRow("right") << Qt::Horizontal << -1; } void LockScreenTest::testAxisShortcut() { using namespace KWayland::Client; QScopedPointer action(new QAction(nullptr)); QSignalSpy actionSpy(action.data(), &QAction::triggered); QVERIFY(actionSpy.isValid()); QFETCH(Qt::Orientation, direction); QFETCH(int, sign); PointerAxisDirection axisDirection = PointerAxisUp; if (direction == Qt::Vertical) { axisDirection = sign > 0 ? PointerAxisUp : PointerAxisDown; } else { axisDirection = sign > 0 ? PointerAxisLeft : PointerAxisRight; } input()->registerAxisShortcut(Qt::MetaModifier, axisDirection, action.data()); // try to trigger the shortcut quint32 timestamp = 1; #define PERFORM(expectedCount) \ kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTMETA, timestamp++); \ if (direction == Qt::Vertical) \ kwinApp()->platform()->pointerAxisVertical(sign * 5.0, timestamp++); \ else \ kwinApp()->platform()->pointerAxisHorizontal(sign * 5.0, timestamp++); \ QCoreApplication::instance()->processEvents(); \ QCOMPARE(actionSpy.count(), expectedCount); \ kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTMETA, timestamp++); \ QCoreApplication::instance()->processEvents(); \ QCOMPARE(actionSpy.count(), expectedCount); PERFORM(1) // now the same thing with a locked screen LOCK PERFORM(1) // and as unlocked UNLOCK PERFORM(2) #undef PERFORM } void LockScreenTest::testKeyboardShortcut() { using namespace KWayland::Client; QScopedPointer action(new QAction(nullptr)); QSignalSpy actionSpy(action.data(), &QAction::triggered); QVERIFY(actionSpy.isValid()); input()->registerShortcut(Qt::CTRL + Qt::META + Qt::ALT + Qt::Key_Space, action.data()); // try to trigger the shortcut quint32 timestamp = 1; KEYPRESS(KEY_LEFTCTRL); KEYPRESS(KEY_LEFTMETA); KEYPRESS(KEY_LEFTALT); KEYPRESS(KEY_SPACE); QCoreApplication::instance()->processEvents(); QCOMPARE(actionSpy.count(), 1); KEYRELEASE(KEY_SPACE); QCoreApplication::instance()->processEvents(); QCOMPARE(actionSpy.count(), 1); LOCK KEYPRESS(KEY_SPACE); QCoreApplication::instance()->processEvents(); QCOMPARE(actionSpy.count(), 1); KEYRELEASE(KEY_SPACE); QCoreApplication::instance()->processEvents(); QCOMPARE(actionSpy.count(), 1); UNLOCK KEYPRESS(KEY_SPACE); QCoreApplication::instance()->processEvents(); QCOMPARE(actionSpy.count(), 2); KEYRELEASE(KEY_SPACE); QCoreApplication::instance()->processEvents(); QCOMPARE(actionSpy.count(), 2); KEYRELEASE(KEY_LEFTCTRL); KEYRELEASE(KEY_LEFTMETA); KEYRELEASE(KEY_LEFTALT); } void LockScreenTest::testTouch() { using namespace KWayland::Client; auto touch = m_seat->createTouch(m_seat); QVERIFY(touch); QVERIFY(touch->isValid()); AbstractClient *c = showWindow(); QVERIFY(c); QSignalSpy sequenceStartedSpy(touch, &Touch::sequenceStarted); QVERIFY(sequenceStartedSpy.isValid()); QSignalSpy cancelSpy(touch, &Touch::sequenceCanceled); QVERIFY(cancelSpy.isValid()); QSignalSpy pointRemovedSpy(touch, &Touch::pointRemoved); QVERIFY(pointRemovedSpy.isValid()); quint32 timestamp = 1; kwinApp()->platform()->touchDown(1, QPointF(25, 25), timestamp++); QVERIFY(sequenceStartedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); LOCK QVERIFY(cancelSpy.wait()); kwinApp()->platform()->touchUp(1, timestamp++); QVERIFY(!pointRemovedSpy.wait(100)); kwinApp()->platform()->touchDown(1, QPointF(25, 25), timestamp++); kwinApp()->platform()->touchMotion(1, QPointF(26, 26), timestamp++); kwinApp()->platform()->touchUp(1, timestamp++); UNLOCK kwinApp()->platform()->touchDown(1, QPointF(25, 25), timestamp++); QVERIFY(sequenceStartedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 2); kwinApp()->platform()->touchUp(1, timestamp++); QVERIFY(pointRemovedSpy.wait()); QCOMPARE(pointRemovedSpy.count(), 1); } } WAYLANDTEST_MAIN(KWin::LockScreenTest) #include "lockscreen.moc" diff --git a/autotests/integration/maximize_test.cpp b/autotests/integration/maximize_test.cpp index 057940929..b375318c8 100644 --- a/autotests/integration/maximize_test.cpp +++ b/autotests/integration/maximize_test.cpp @@ -1,262 +1,177 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "cursor.h" #include "platform.h" #include "shell_client.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" -#include #include -#include -#include #include #include #include #include #include #include #include -#include - using namespace KWin; using namespace KWayland::Client; static const QString s_socketName = QStringLiteral("wayland_test_kwin_maximized-0"); class TestMaximized : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testMaximizedPassedToDeco(); void testInitiallyMaximized(); - -private: - KWayland::Client::Compositor *m_compositor = nullptr; - Shell *m_shell = nullptr; - ShmPool *m_shm = nullptr; - EventQueue *m_queue = nullptr; - ServerSideDecorationManager *m_ssd = nullptr; - ConnectionThread *m_connection = nullptr; - QThread *m_thread = nullptr; }; void TestMaximized::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); waylandServer()->initWorkspace(); } void TestMaximized::init() { - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - - m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, - registry.interface(Registry::Interface::Compositor).version, - this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(registry.interface(Registry::Interface::Shm).name, - registry.interface(Registry::Interface::Shm).version, - this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(registry.interface(Registry::Interface::Shell).name, - registry.interface(Registry::Interface::Shell).version, - this); - QVERIFY(m_shell->isValid()); - m_ssd = registry.createServerSideDecorationManager(registry.interface(Registry::Interface::ServerSideDecorationManager).name, - registry.interface(Registry::Interface::ServerSideDecorationManager).version, - this); - QVERIFY(m_ssd); + QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::Decoration)); screens()->setCurrent(0); KWin::Cursor::setPos(QPoint(1280, 512)); } void TestMaximized::cleanup() { -#define CLEANUP(name) \ - if (name) { \ - delete name; \ - name = nullptr; \ - } - CLEANUP(m_compositor) - CLEANUP(m_shm) - CLEANUP(m_shell) - CLEANUP(m_ssd) - CLEANUP(m_queue) - - if (m_connection) { - m_connection->deleteLater(); - m_connection = nullptr; - } - if (m_thread) { - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - } -#undef CLEANUP + Test::destroyWaylandConnection(); } void TestMaximized::testMaximizedPassedToDeco() { // this test verifies that when a ShellClient gets maximized the Decoration receives the signal QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface(m_compositor->createSurface()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); - QScopedPointer ssd(m_ssd->create(surface.data())); + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); + QScopedPointer ssd(Test::waylandServerSideDecoration()->create(surface.data())); - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); QSignalSpy sizeChangedSpy(shellSurface.data(), &ShellSurface::sizeChanged); QVERIFY(sizeChangedSpy.isValid()); QVERIFY(clientAddedSpy.isEmpty()); QVERIFY(clientAddedSpy.wait()); auto client = clientAddedSpy.first().first().value(); QVERIFY(client); QVERIFY(client->isDecorated()); auto decoration = client->decoration(); QVERIFY(decoration); QCOMPARE(client->maximizeMode(), MaximizeMode::MaximizeRestore); // now maximize QVERIFY(sizeChangedSpy.isEmpty()); QSignalSpy bordersChangedSpy(decoration, &KDecoration2::Decoration::bordersChanged); QVERIFY(bordersChangedSpy.isValid()); QSignalSpy maximizedChangedSpy(decoration->client().data(), &KDecoration2::DecoratedClient::maximizedChanged); QVERIFY(maximizedChangedSpy.isValid()); workspace()->slotWindowMaximize(); QCOMPARE(client->maximizeMode(), MaximizeMode::MaximizeFull); QCOMPARE(maximizedChangedSpy.count(), 1); QCOMPARE(maximizedChangedSpy.last().first().toBool(), true); QCOMPARE(bordersChangedSpy.count(), 1); QCOMPARE(decoration->borderLeft(), 0); QCOMPARE(decoration->borderBottom(), 0); QCOMPARE(decoration->borderRight(), 0); QVERIFY(decoration->borderTop() != 0); QVERIFY(sizeChangedSpy.isEmpty()); QVERIFY(sizeChangedSpy.wait()); QCOMPARE(sizeChangedSpy.count(), 1); QCOMPARE(sizeChangedSpy.first().first().toSize(), QSize(1280, 1024 - decoration->borderTop())); // now unmaximize again workspace()->slotWindowMaximize(); QCOMPARE(client->maximizeMode(), MaximizeMode::MaximizeRestore); QCOMPARE(maximizedChangedSpy.count(), 2); QCOMPARE(maximizedChangedSpy.last().first().toBool(), false); QCOMPARE(bordersChangedSpy.count(), 2); QVERIFY(decoration->borderTop() != 0); QVERIFY(decoration->borderLeft() != 0); QVERIFY(decoration->borderRight() != 0); QVERIFY(decoration->borderBottom() != 0); QVERIFY(sizeChangedSpy.wait()); QCOMPARE(sizeChangedSpy.count(), 2); QCOMPARE(sizeChangedSpy.last().first().toSize(), QSize(100, 50)); } void TestMaximized::testInitiallyMaximized() { // this test verifies that a window created as maximized, will be maximized QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface(m_compositor->createSurface()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QSignalSpy sizeChangedSpy(shellSurface.data(), &ShellSurface::sizeChanged); QVERIFY(sizeChangedSpy.isValid()); shellSurface->setMaximized(); QVERIFY(sizeChangedSpy.wait()); QCOMPARE(shellSurface->size(), QSize(1280, 1024)); // now let's render in an incorrect size - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); QVERIFY(clientAddedSpy.isEmpty()); QVERIFY(clientAddedSpy.wait()); auto client = clientAddedSpy.first().first().value(); QVERIFY(client); QCOMPARE(client->geometry(), QRect(0, 0, 100, 50)); QEXPECT_FAIL("", "Should go out of maximzied", Continue); QCOMPARE(client->maximizeMode(), MaximizeMode::MaximizeRestore); QVERIFY(client->shellSurface()->isMaximized()); } WAYLANDTEST_MAIN(TestMaximized) #include "maximize_test.moc" diff --git a/autotests/integration/move_resize_window_test.cpp b/autotests/integration/move_resize_window_test.cpp index a22f7d60a..ed27a4154 100644 --- a/autotests/integration/move_resize_window_test.cpp +++ b/autotests/integration/move_resize_window_test.cpp @@ -1,659 +1,559 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "abstract_client.h" #include "cursor.h" #include "effects.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include #include -#include -#include #include #include -#include #include #include #include Q_DECLARE_METATYPE(KWin::AbstractClient::QuickTileMode) Q_DECLARE_METATYPE(KWin::MaximizeMode) namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_quick_tiling-0"); class MoveResizeWindowTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testMove(); void testPackTo_data(); void testPackTo(); void testPackAgainstClient_data(); void testPackAgainstClient(); void testGrowShrink_data(); void testGrowShrink(); void testPointerMoveEnd_data(); void testPointerMoveEnd(); void testPlasmaShellSurfaceMovable_data(); void testPlasmaShellSurfaceMovable(); void testNetMove(); private: KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; - KWayland::Client::ShmPool *m_shm = nullptr; KWayland::Client::Shell *m_shell = nullptr; - KWayland::Client::PlasmaShell *m_plasmaShell = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; void MoveResizeWindowTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType("MaximizeMode"); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 1); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); } void MoveResizeWindowTest::init() { - using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); - m_plasmaShell = registry.createPlasmaShell(registry.interface(Registry::Interface::PlasmaShell).name, registry.interface(Registry::Interface::PlasmaShell).version, this); - QVERIFY(m_plasmaShell->isValid()); + QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::PlasmaShell)); + m_connection = Test::waylandConnection(); + m_compositor = Test::waylandCompositor(); + m_shell = Test::waylandShell(); screens()->setCurrent(0); } void MoveResizeWindowTest::cleanup() { - delete m_compositor; - m_compositor = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_plasmaShell; - m_plasmaShell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - } - + Test::destroyWaylandConnection(); } void MoveResizeWindowTest::testMove() { using namespace KWayland::Client; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); QSignalSpy sizeChangeSpy(shellSurface.data(), &ShellSurface::sizeChanged); QVERIFY(sizeChangeSpy.isValid()); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); m_connection->flush(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); QCOMPARE(c->geometry(), QRect(0, 0, 100, 50)); QSignalSpy geometryChangedSpy(c, &AbstractClient::geometryChanged); QVERIFY(geometryChangedSpy.isValid()); QSignalSpy startMoveResizedSpy(c, &AbstractClient::clientStartUserMovedResized); QVERIFY(startMoveResizedSpy.isValid()); QSignalSpy moveResizedChangedSpy(c, &AbstractClient::moveResizedChanged); QVERIFY(moveResizedChangedSpy.isValid()); QSignalSpy clientStepUserMovedResizedSpy(c, &AbstractClient::clientStepUserMovedResized); QVERIFY(clientStepUserMovedResizedSpy.isValid()); QSignalSpy clientFinishUserMovedResizedSpy(c, &AbstractClient::clientFinishUserMovedResized); QVERIFY(clientFinishUserMovedResizedSpy.isValid()); // effects signal handlers QSignalSpy windowStartUserMovedResizedSpy(effects, &EffectsHandler::windowStartUserMovedResized); QVERIFY(windowStartUserMovedResizedSpy.isValid()); QSignalSpy windowStepUserMovedResizedSpy(effects, &EffectsHandler::windowStepUserMovedResized); QVERIFY(windowStepUserMovedResizedSpy.isValid()); QSignalSpy windowFinishUserMovedResizedSpy(effects, &EffectsHandler::windowFinishUserMovedResized); QVERIFY(windowFinishUserMovedResizedSpy.isValid()); // begin move QVERIFY(workspace()->getMovingClient() == nullptr); QCOMPARE(c->isMove(), false); workspace()->slotWindowMove(); QCOMPARE(workspace()->getMovingClient(), c); QCOMPARE(startMoveResizedSpy.count(), 1); QCOMPARE(moveResizedChangedSpy.count(), 1); QCOMPARE(windowStartUserMovedResizedSpy.count(), 1); QCOMPARE(c->isMove(), true); QCOMPARE(c->geometryRestore(), QRect(0, 0, 100, 50)); // send some key events, not going through input redirection const QPoint cursorPos = Cursor::pos(); c->keyPressEvent(Qt::Key_Right); c->updateMoveResize(Cursor::pos()); QCOMPARE(Cursor::pos(), cursorPos + QPoint(8, 0)); QEXPECT_FAIL("", "First event is ignored", Continue); QCOMPARE(clientStepUserMovedResizedSpy.count(), 1); c->keyPressEvent(Qt::Key_Right); c->updateMoveResize(Cursor::pos()); QCOMPARE(Cursor::pos(), cursorPos + QPoint(16, 0)); QEXPECT_FAIL("", "First event is ignored", Continue); QCOMPARE(clientStepUserMovedResizedSpy.count(), 2); QEXPECT_FAIL("", "First event is ignored", Continue); QCOMPARE(windowStepUserMovedResizedSpy.count(), 2); c->keyPressEvent(Qt::Key_Down | Qt::ALT); c->updateMoveResize(Cursor::pos()); QEXPECT_FAIL("", "First event is ignored", Continue); QCOMPARE(clientStepUserMovedResizedSpy.count(), 3); QCOMPARE(c->geometry(), QRect(16, 32, 100, 50)); QCOMPARE(Cursor::pos(), cursorPos + QPoint(16, 32)); // let's end QCOMPARE(clientFinishUserMovedResizedSpy.count(), 0); c->keyPressEvent(Qt::Key_Enter); QCOMPARE(clientFinishUserMovedResizedSpy.count(), 1); QCOMPARE(moveResizedChangedSpy.count(), 2); QCOMPARE(windowFinishUserMovedResizedSpy.count(), 1); QCOMPARE(c->geometry(), QRect(16, 32, 100, 50)); QCOMPARE(c->isMove(), false); QVERIFY(workspace()->getMovingClient() == nullptr); } void MoveResizeWindowTest::testPackTo_data() { QTest::addColumn("methodCall"); QTest::addColumn("expectedGeometry"); QTest::newRow("left") << QStringLiteral("slotWindowPackLeft") << QRect(0, 487, 100, 50); QTest::newRow("up") << QStringLiteral("slotWindowPackUp") << QRect(590, 0, 100, 50); QTest::newRow("right") << QStringLiteral("slotWindowPackRight") << QRect(1180, 487, 100, 50); QTest::newRow("down") << QStringLiteral("slotWindowPackDown") << QRect(590, 974, 100, 50); } void MoveResizeWindowTest::testPackTo() { using namespace KWayland::Client; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); QSignalSpy sizeChangeSpy(shellSurface.data(), &ShellSurface::sizeChanged); QVERIFY(sizeChangeSpy.isValid()); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); m_connection->flush(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); QCOMPARE(c->geometry(), QRect(0, 0, 100, 50)); // let's place it centered Placement::self()->placeCentered(c, QRect(0, 0, 1280, 1024)); QCOMPARE(c->geometry(), QRect(590, 487, 100, 50)); QFETCH(QString, methodCall); QMetaObject::invokeMethod(workspace(), methodCall.toLocal8Bit().constData()); QTEST(c->geometry(), "expectedGeometry"); } void MoveResizeWindowTest::testPackAgainstClient_data() { QTest::addColumn("methodCall"); QTest::addColumn("expectedGeometry"); QTest::newRow("left") << QStringLiteral("slotWindowPackLeft") << QRect(10, 487, 100, 50); QTest::newRow("up") << QStringLiteral("slotWindowPackUp") << QRect(590, 10, 100, 50); QTest::newRow("right") << QStringLiteral("slotWindowPackRight") << QRect(1170, 487, 100, 50); QTest::newRow("down") << QStringLiteral("slotWindowPackDown") << QRect(590, 964, 100, 50); } void MoveResizeWindowTest::testPackAgainstClient() { using namespace KWayland::Client; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface1(m_compositor->createSurface()); + QScopedPointer surface1(Test::createSurface()); QVERIFY(!surface1.isNull()); - QScopedPointer surface2(m_compositor->createSurface()); + QScopedPointer surface2(Test::createSurface()); QVERIFY(!surface2.isNull()); - QScopedPointer surface3(m_compositor->createSurface()); + QScopedPointer surface3(Test::createSurface()); QVERIFY(!surface3.isNull()); - QScopedPointer surface4(m_compositor->createSurface()); + QScopedPointer surface4(Test::createSurface()); QVERIFY(!surface4.isNull()); - QScopedPointer shellSurface1(m_shell->createSurface(surface1.data())); + QScopedPointer shellSurface1(Test::createShellSurface(surface1.data())); QVERIFY(!shellSurface1.isNull()); - QScopedPointer shellSurface2(m_shell->createSurface(surface2.data())); + QScopedPointer shellSurface2(Test::createShellSurface(surface2.data())); QVERIFY(!shellSurface2.isNull()); - QScopedPointer shellSurface3(m_shell->createSurface(surface3.data())); + QScopedPointer shellSurface3(Test::createShellSurface(surface3.data())); QVERIFY(!shellSurface3.isNull()); - QScopedPointer shellSurface4(m_shell->createSurface(surface4.data())); + QScopedPointer shellSurface4(Test::createShellSurface(surface4.data())); QVERIFY(!shellSurface4.isNull()); auto renderWindow = [this, &clientAddedSpy] (Surface *surface, const QString &methodCall, const QRect &expectedGeometry) { // let's render - QImage img(QSize(10, 10), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 10, 10)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface, QSize(10, 10), Qt::blue); m_connection->flush(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); QCOMPARE(c->geometry().size(), QSize(10, 10)); clientAddedSpy.clear(); // let's place it centered Placement::self()->placeCentered(c, QRect(0, 0, 1280, 1024)); QCOMPARE(c->geometry(), QRect(635, 507, 10, 10)); QMetaObject::invokeMethod(workspace(), methodCall.toLocal8Bit().constData()); QCOMPARE(c->geometry(), expectedGeometry); }; renderWindow(surface1.data(), QStringLiteral("slotWindowPackLeft"), QRect(0, 507, 10, 10)); renderWindow(surface2.data(), QStringLiteral("slotWindowPackUp"), QRect(635, 0, 10, 10)); renderWindow(surface3.data(), QStringLiteral("slotWindowPackRight"), QRect(1270, 507, 10, 10)); renderWindow(surface4.data(), QStringLiteral("slotWindowPackDown"), QRect(635, 1014, 10, 10)); - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); m_connection->flush(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); // let's place it centered Placement::self()->placeCentered(c, QRect(0, 0, 1280, 1024)); QCOMPARE(c->geometry(), QRect(590, 487, 100, 50)); QFETCH(QString, methodCall); QMetaObject::invokeMethod(workspace(), methodCall.toLocal8Bit().constData()); QTEST(c->geometry(), "expectedGeometry"); } void MoveResizeWindowTest::testGrowShrink_data() { QTest::addColumn("methodCall"); QTest::addColumn("expectedGeometry"); QTest::newRow("grow vertical") << QStringLiteral("slotWindowGrowVertical") << QRect(590, 487, 100, 537); QTest::newRow("grow horizontal") << QStringLiteral("slotWindowGrowHorizontal") << QRect(590, 487, 690, 50); QTest::newRow("shrink vertical") << QStringLiteral("slotWindowShrinkVertical") << QRect(590, 487, 100, 23); QTest::newRow("shrink horizontal") << QStringLiteral("slotWindowShrinkHorizontal") << QRect(590, 487, 40, 50); } void MoveResizeWindowTest::testGrowShrink() { using namespace KWayland::Client; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); // block geometry helper - QScopedPointer surface1(m_compositor->createSurface()); + QScopedPointer surface1(Test::createSurface()); QVERIFY(!surface1.isNull()); - QScopedPointer shellSurface1(m_shell->createSurface(surface1.data())); + QScopedPointer shellSurface1(Test::createShellSurface(surface1.data())); QVERIFY(!shellSurface1.isNull()); - QImage img1(QSize(650, 514), QImage::Format_ARGB32); - img1.fill(Qt::blue); - surface1->attachBuffer(m_shm->createBuffer(img1)); - surface1->damage(QRect(0, 0, 650, 514)); - surface1->commit(Surface::CommitFlag::None); + Test::render(surface1.data(), QSize(650, 514), Qt::blue); m_connection->flush(); QVERIFY(clientAddedSpy.wait()); clientAddedSpy.clear(); workspace()->slotWindowPackRight(); workspace()->slotWindowPackDown(); - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); QSignalSpy sizeChangeSpy(shellSurface.data(), &ShellSurface::sizeChanged); QVERIFY(sizeChangeSpy.isValid()); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); m_connection->flush(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); // let's place it centered Placement::self()->placeCentered(c, QRect(0, 0, 1280, 1024)); QCOMPARE(c->geometry(), QRect(590, 487, 100, 50)); QFETCH(QString, methodCall); QMetaObject::invokeMethod(workspace(), methodCall.toLocal8Bit().constData()); QVERIFY(sizeChangeSpy.wait()); - QImage img2(shellSurface->size(), QImage::Format_ARGB32); - img2.fill(Qt::red); - surface->attachBuffer(m_shm->createBuffer(img2)); - surface->damage(QRect(QPoint(0, 0), shellSurface->size())); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), shellSurface->size(), Qt::red); QSignalSpy geometryChangedSpy(c, &AbstractClient::geometryChanged); QVERIFY(geometryChangedSpy.isValid()); m_connection->flush(); QVERIFY(geometryChangedSpy.wait()); QTEST(c->geometry(), "expectedGeometry"); } void MoveResizeWindowTest::testPointerMoveEnd_data() { QTest::addColumn("additionalButton"); QTest::newRow("BTN_RIGHT") << BTN_RIGHT; QTest::newRow("BTN_MIDDLE") << BTN_MIDDLE; QTest::newRow("BTN_SIDE") << BTN_SIDE; QTest::newRow("BTN_EXTRA") << BTN_EXTRA; QTest::newRow("BTN_FORWARD") << BTN_FORWARD; QTest::newRow("BTN_BACK") << BTN_BACK; QTest::newRow("BTN_TASK") << BTN_TASK; for (int i=BTN_TASK + 1; i < BTN_JOYSTICK; i++) { QTest::newRow(QByteArray::number(i, 16).constData()) << i; } } void MoveResizeWindowTest::testPointerMoveEnd() { // this test verifies that moving a window through pointer only ends if all buttons are released using namespace KWayland::Client; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); QSignalSpy sizeChangeSpy(shellSurface.data(), &ShellSurface::sizeChanged); QVERIFY(sizeChangeSpy.isValid()); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); m_connection->flush(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QVERIFY(!c->isMove()); // let's trigger the left button quint32 timestamp = 1; kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++); QVERIFY(!c->isMove()); workspace()->slotWindowMove(); QVERIFY(c->isMove()); // let's press another button QFETCH(int, additionalButton); kwinApp()->platform()->pointerButtonPressed(additionalButton, timestamp++); QVERIFY(c->isMove()); // release the left button, should still have the window moving kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++); QVERIFY(c->isMove()); // but releasing the other button should now end moving kwinApp()->platform()->pointerButtonReleased(additionalButton, timestamp++); QVERIFY(!c->isMove()); } void MoveResizeWindowTest::testPlasmaShellSurfaceMovable_data() { QTest::addColumn("role"); QTest::addColumn("movable"); QTest::addColumn("movableAcrossScreens"); QTest::addColumn("resizable"); QTest::newRow("normal") << KWayland::Client::PlasmaShellSurface::Role::Normal << true << true << true; QTest::newRow("desktop") << KWayland::Client::PlasmaShellSurface::Role::Desktop << false << false << false; QTest::newRow("panel") << KWayland::Client::PlasmaShellSurface::Role::Panel << false << false << false; QTest::newRow("osd") << KWayland::Client::PlasmaShellSurface::Role::OnScreenDisplay << false << false << false; } void MoveResizeWindowTest::testPlasmaShellSurfaceMovable() { // this test verifies that certain window types from PlasmaShellSurface are not moveable or resizable using namespace KWayland::Client; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); // and a PlasmaShellSurface - QScopedPointer plasmaSurface(m_plasmaShell->createSurface(surface.data())); + QScopedPointer plasmaSurface(Test::waylandPlasmaShell()->createSurface(surface.data())); QVERIFY(!plasmaSurface.isNull()); QFETCH(KWayland::Client::PlasmaShellSurface::Role, role); plasmaSurface->setRole(role); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); m_connection->flush(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = clientAddedSpy.first().first().value(); QVERIFY(c); QTEST(c->isMovable(), "movable"); QTEST(c->isMovableAcrossScreens(), "movableAcrossScreens"); QTEST(c->isResizable(), "resizable"); } void MoveResizeWindowTest::testNetMove() { // this test verifies that a move request for an X11 window through NET API works // create an xcb window struct XcbConnectionDeleter { static inline void cleanup(xcb_connection_t *pointer) { xcb_disconnect(pointer); } }; QScopedPointer c(xcb_connect(nullptr, nullptr)); QVERIFY(!xcb_connection_has_error(c.data())); xcb_window_t w = xcb_generate_id(c.data()); xcb_create_window(c.data(), XCB_COPY_FROM_PARENT, w, rootWindow(), 0, 0, 100, 100, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); xcb_size_hints_t hints; memset(&hints, 0, sizeof(hints)); xcb_icccm_size_hints_set_position(&hints, 1, 0, 0); xcb_icccm_size_hints_set_size(&hints, 1, 100, 100); xcb_icccm_set_wm_normal_hints(c.data(), w, &hints); // let's set a no-border NETWinInfo winInfo(c.data(), w, rootWindow(), NET::WMWindowType, NET::Properties2()); winInfo.setWindowType(NET::Override); xcb_map_window(c.data(), w); xcb_flush(c.data()); QSignalSpy windowCreatedSpy(workspace(), &Workspace::clientAdded); QVERIFY(windowCreatedSpy.isValid()); QVERIFY(windowCreatedSpy.wait()); Client *client = windowCreatedSpy.first().first().value(); QVERIFY(client); QCOMPARE(client->window(), w); const QRect origGeo = client->geometry(); // let's move the cursor outside the window Cursor::setPos(screens()->geometry(0).center()); QVERIFY(!origGeo.contains(Cursor::pos())); QSignalSpy moveStartSpy(client, &Client::clientStartUserMovedResized); QVERIFY(moveStartSpy.isValid()); QSignalSpy moveEndSpy(client, &Client::clientFinishUserMovedResized); QVERIFY(moveEndSpy.isValid()); QSignalSpy moveStepSpy(client, &Client::clientStepUserMovedResized); QVERIFY(moveStepSpy.isValid()); // use NETRootInfo to trigger a move request NETRootInfo root(c.data(), NET::Properties()); root.moveResizeRequest(w, origGeo.center().x(), origGeo.center().y(), NET::Move); xcb_flush(c.data()); QVERIFY(moveStartSpy.wait()); QCOMPARE(workspace()->getMovingClient(), client); QVERIFY(client->isMove()); QCOMPARE(client->geometryRestore(), origGeo); QCOMPARE(Cursor::pos(), origGeo.center()); // let's move a step Cursor::setPos(Cursor::pos() + QPoint(10, 10)); QCOMPARE(moveStepSpy.count(), 1); QCOMPARE(moveStepSpy.first().last().toRect(), origGeo.translated(10, 10)); // let's cancel the move resize again through the net API root.moveResizeRequest(w, client->geometry().center().x(), client->geometry().center().y(), NET::MoveResizeCancel); xcb_flush(c.data()); QVERIFY(moveEndSpy.wait()); // and destroy the window again xcb_unmap_window(c.data(), w); xcb_destroy_window(c.data(), w); xcb_flush(c.data()); c.reset(); QSignalSpy windowClosedSpy(client, &Client::windowClosed); QVERIFY(windowClosedSpy.isValid()); QVERIFY(windowClosedSpy.wait()); } } WAYLANDTEST_MAIN(KWin::MoveResizeWindowTest) #include "move_resize_window_test.moc" diff --git a/autotests/integration/plasma_surface_test.cpp b/autotests/integration/plasma_surface_test.cpp index d14fb425e..e2554f972 100644 --- a/autotests/integration/plasma_surface_test.cpp +++ b/autotests/integration/plasma_surface_test.cpp @@ -1,258 +1,190 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "shell_client.h" #include "wayland_server.h" #include "workspace.h" #include #include #include #include #include #include #include #include using namespace KWin; using namespace KWayland::Client; static const QString s_socketName = QStringLiteral("wayland_test_kwin_plasma_surface-0"); class PlasmaSurfaceTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testRoleOnAllDesktops_data(); void testRoleOnAllDesktops(); void testAcceptsFocus_data(); void testAcceptsFocus(); private: ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; - ShmPool *m_shm = nullptr; Shell *m_shell = nullptr; PlasmaShell *m_plasmaShell = nullptr; - EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; void PlasmaSurfaceTest::initTestCase() { qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); } void PlasmaSurfaceTest::init() { - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - - m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, - registry.interface(Registry::Interface::Compositor).version, - this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(registry.interface(Registry::Interface::Shm).name, - registry.interface(Registry::Interface::Shm).version, - this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(registry.interface(Registry::Interface::Shell).name, - registry.interface(Registry::Interface::Shell).version, - this); - QVERIFY(m_shell->isValid()); - m_plasmaShell = registry.createPlasmaShell(registry.interface(Registry::Interface::PlasmaShell).name, - registry.interface(Registry::Interface::PlasmaShell).version, - this); - QVERIFY(m_plasmaShell->isValid()); + QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::PlasmaShell)); + m_compositor = Test::waylandCompositor(); + m_shell = Test::waylandShell(); + m_plasmaShell = Test::waylandPlasmaShell(); } void PlasmaSurfaceTest::cleanup() { -#define CLEANUP(name) delete name; name = nullptr; - CLEANUP(m_plasmaShell) - CLEANUP(m_shell) - CLEANUP(m_shm) - CLEANUP(m_compositor) - CLEANUP(m_queue) - if (m_connection) { - m_connection->deleteLater(); - m_connection = nullptr; - } - if (m_thread) { - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - } -#undef CLEANUP + Test::destroyWaylandConnection(); } void PlasmaSurfaceTest::testRoleOnAllDesktops_data() { QTest::addColumn("role"); QTest::addColumn("expectedOnAllDesktops"); QTest::newRow("Desktop") << PlasmaShellSurface::Role::Desktop << true; QTest::newRow("Panel") << PlasmaShellSurface::Role::Panel << true; QTest::newRow("OSD") << PlasmaShellSurface::Role::OnScreenDisplay << true; QTest::newRow("Normal") << PlasmaShellSurface::Role::Normal << false; QTest::newRow("Notification") << PlasmaShellSurface::Role::Notification << true; QTest::newRow("ToolTip") << PlasmaShellSurface::Role::ToolTip << true; } void PlasmaSurfaceTest::testRoleOnAllDesktops() { // this test verifies that a ShellClient is set on all desktops when the role changes - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); QScopedPointer plasmaSurface(m_plasmaShell->createSurface(surface.data())); QVERIFY(!plasmaSurface.isNull()); QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); // now render to map the window - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(); + Test::render(surface.data(), QSize(100, 50), Qt::blue); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); // currently the role is not yet set, so the window should not be on all desktops QCOMPARE(c->isOnAllDesktops(), false); // now let's try to change that QSignalSpy onAllDesktopsSpy(c, &AbstractClient::desktopChanged); QVERIFY(onAllDesktopsSpy.isValid()); QFETCH(PlasmaShellSurface::Role, role); plasmaSurface->setRole(role); QFETCH(bool, expectedOnAllDesktops); QCOMPARE(onAllDesktopsSpy.wait(), expectedOnAllDesktops); QCOMPARE(c->isOnAllDesktops(), expectedOnAllDesktops); // let's create a second window where we init a little bit different // first creating the PlasmaSurface then the Shell Surface - QScopedPointer surface2(m_compositor->createSurface()); + QScopedPointer surface2(Test::createSurface()); QVERIFY(!surface2.isNull()); QScopedPointer plasmaSurface2(m_plasmaShell->createSurface(surface2.data())); QVERIFY(!plasmaSurface2.isNull()); plasmaSurface2->setRole(role); - QScopedPointer shellSurface2(m_shell->createSurface(surface2.data())); + QScopedPointer shellSurface2(Test::createShellSurface(surface2.data())); QVERIFY(!shellSurface2.isNull()); - surface2->attachBuffer(m_shm->createBuffer(img)); - surface2->damage(QRect(0, 0, 100, 50)); - surface2->commit(); + Test::render(surface2.data(), QSize(100, 50), Qt::blue); QVERIFY(clientAddedSpy.wait()); QVERIFY(workspace()->activeClient() != c); c = workspace()->activeClient(); QEXPECT_FAIL("Desktop", "PS before WS not supported", Continue); QEXPECT_FAIL("Panel", "PS before WS not supported", Continue); QEXPECT_FAIL("OSD", "PS before WS not supported", Continue); QEXPECT_FAIL("Notification", "PS before WS not supported", Continue); QEXPECT_FAIL("ToolTip", "PS before WS not supported", Continue); QCOMPARE(c->isOnAllDesktops(), expectedOnAllDesktops); } void PlasmaSurfaceTest::testAcceptsFocus_data() { QTest::addColumn("role"); QTest::addColumn("wantsInput"); QTest::addColumn("active"); QTest::newRow("Desktop") << PlasmaShellSurface::Role::Desktop << true << true; QTest::newRow("Panel") << PlasmaShellSurface::Role::Panel << true << false; QTest::newRow("OSD") << PlasmaShellSurface::Role::OnScreenDisplay << false << false; QTest::newRow("Normal") << PlasmaShellSurface::Role::Normal << true << true; QTest::newRow("Notification") << PlasmaShellSurface::Role::Notification << false << false; QTest::newRow("ToolTip") << PlasmaShellSurface::Role::ToolTip << false << false; } void PlasmaSurfaceTest::testAcceptsFocus() { // this test verifies that some surface roles don't get focus - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); QScopedPointer plasmaSurface(m_plasmaShell->createSurface(surface.data())); QVERIFY(!plasmaSurface.isNull()); QFETCH(PlasmaShellSurface::Role, role); plasmaSurface->setRole(role); QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); // now render to map the window - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(); + Test::render(surface.data(), QSize(100, 50), Qt::blue); QVERIFY(clientAddedSpy.wait()); auto c = clientAddedSpy.first().first().value(); QVERIFY(c); QTEST(c->wantsInput(), "wantsInput"); QTEST(c->isActive(), "active"); } WAYLANDTEST_MAIN(PlasmaSurfaceTest) #include "plasma_surface_test.moc" diff --git a/autotests/integration/plasmawindow_test.cpp b/autotests/integration/plasmawindow_test.cpp index 45f02d7ee..abceb706c 100644 --- a/autotests/integration/plasmawindow_test.cpp +++ b/autotests/integration/plasmawindow_test.cpp @@ -1,437 +1,358 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "client.h" #include "cursor.h" #include "screenedge.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include #include -#include -#include #include -#include #include -#include #include #include //screenlocker #include #include #include #include #include using namespace KWayland::Client; namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_plasma-window-0"); class PlasmaWindowTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testCreateDestroyX11PlasmaWindow(); void testInternalWindowNoPlasmaWindow(); void testPopupWindowNoPlasmaWindow(); void testLockScreenNoPlasmaWindow(); void testDestroyedButNotUnmapped(); private: - ConnectionThread *m_connection = nullptr; PlasmaWindowManagement *m_windowManagement = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; Shell *m_shell = nullptr; - ShmPool *m_shm = nullptr; - EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; void PlasmaWindowTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); setenv("QT_QPA_PLATFORM", "wayland", true); setenv("QMLSCENE_DEVICE", "softwarecontext", true); waylandServer()->initWorkspace(); } void PlasmaWindowTest::init() { - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - - m_windowManagement = registry.createPlasmaWindowManagement(registry.interface(Registry::Interface::PlasmaWindowManagement).name, - registry.interface(Registry::Interface::PlasmaWindowManagement).version, - this); - QVERIFY(m_windowManagement); - m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, - registry.interface(Registry::Interface::Compositor).version, - this); - QVERIFY(m_compositor); - m_shm = registry.createShmPool(registry.interface(Registry::Interface::Shm).name, - registry.interface(Registry::Interface::Shm).version, - this); - QVERIFY(m_shm); - m_shell = registry.createShell(registry.interface(Registry::Interface::Shell).name, - registry.interface(Registry::Interface::Shell).version, - this); - QVERIFY(m_shell); + QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::WindowManagement)); + m_windowManagement = Test::waylandWindowManagement(); + m_compositor = Test::waylandCompositor(); + m_shell = Test::waylandShell(); screens()->setCurrent(0); Cursor::setPos(QPoint(640, 512)); } void PlasmaWindowTest::cleanup() { -#define CLEANUP(name) \ - if (name) { \ - delete name; \ - name = nullptr; \ - } - CLEANUP(m_windowManagement) - CLEANUP(m_shm) - CLEANUP(m_shell) - CLEANUP(m_compositor) - CLEANUP(m_queue) -#undef CLEANUP - if (m_connection) { - m_connection->deleteLater(); - m_connection = nullptr; - } - if (m_thread) { - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - } + Test::destroyWaylandConnection(); } void PlasmaWindowTest::testCreateDestroyX11PlasmaWindow() { // this test verifies that a PlasmaWindow gets unmapped on Client side when an X11 client is destroyed QSignalSpy plasmaWindowCreatedSpy(m_windowManagement, &PlasmaWindowManagement::windowCreated); QVERIFY(plasmaWindowCreatedSpy.isValid()); // create an xcb window struct XcbConnectionDeleter { static inline void cleanup(xcb_connection_t *pointer) { xcb_disconnect(pointer); } }; QScopedPointer c(xcb_connect(nullptr, nullptr)); QVERIFY(!xcb_connection_has_error(c.data())); const QRect windowGeometry(0, 0, 100, 200); xcb_window_t w = xcb_generate_id(c.data()); xcb_create_window(c.data(), XCB_COPY_FROM_PARENT, w, rootWindow(), windowGeometry.x(), windowGeometry.y(), windowGeometry.width(), windowGeometry.height(), 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); xcb_size_hints_t hints; memset(&hints, 0, sizeof(hints)); xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); xcb_icccm_set_wm_normal_hints(c.data(), w, &hints); xcb_map_window(c.data(), w); xcb_flush(c.data()); // we should get a client for it QSignalSpy windowCreatedSpy(workspace(), &Workspace::clientAdded); QVERIFY(windowCreatedSpy.isValid()); QVERIFY(windowCreatedSpy.wait()); Client *client = windowCreatedSpy.first().first().value(); QVERIFY(client); QCOMPARE(client->window(), w); QVERIFY(client->isDecorated()); QVERIFY(client->isActive()); // verify that it gets the keyboard focus QVERIFY(!client->surface()); // we don't have a surface yet, so focused keyboard surface if set is not ours QVERIFY(!waylandServer()->seat()->focusedKeyboardSurface()); QSignalSpy surfaceChangedSpy(client, &Toplevel::surfaceChanged); QVERIFY(surfaceChangedSpy.isValid()); QVERIFY(surfaceChangedSpy.wait()); QVERIFY(client->surface()); QCOMPARE(waylandServer()->seat()->focusedKeyboardSurface(), client->surface()); // now that should also give it to us on client side QVERIFY(plasmaWindowCreatedSpy.wait()); QCOMPARE(plasmaWindowCreatedSpy.count(), 1); QCOMPARE(m_windowManagement->windows().count(), 1); QSignalSpy unmappedSpy(m_windowManagement->windows().first(), &PlasmaWindow::unmapped); QVERIFY(unmappedSpy.isValid()); QSignalSpy destroyedSpy(m_windowManagement->windows().first(), &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); // now shade the window const QRect geoBeforeShade = client->geometry(); QVERIFY(geoBeforeShade.isValid()); QVERIFY(!geoBeforeShade.isEmpty()); workspace()->slotWindowShade(); QVERIFY(client->isShade()); QVERIFY(client->geometry() != geoBeforeShade); // and unshade again workspace()->slotWindowShade(); QVERIFY(!client->isShade()); QCOMPARE(client->geometry(), geoBeforeShade); // and destroy the window again xcb_unmap_window(c.data(), w); xcb_flush(c.data()); QSignalSpy windowClosedSpy(client, &Client::windowClosed); QVERIFY(windowClosedSpy.isValid()); QVERIFY(windowClosedSpy.wait()); xcb_destroy_window(c.data(), w); c.reset(); QVERIFY(unmappedSpy.wait()); QCOMPARE(unmappedSpy.count(), 1); QVERIFY(destroyedSpy.wait()); } class HelperWindow : public QRasterWindow { Q_OBJECT public: HelperWindow(); ~HelperWindow(); protected: void paintEvent(QPaintEvent *event) override; }; HelperWindow::HelperWindow() : QRasterWindow(nullptr) { } HelperWindow::~HelperWindow() = default; void HelperWindow::paintEvent(QPaintEvent *event) { Q_UNUSED(event) QPainter p(this); p.fillRect(0, 0, width(), height(), Qt::red); } void PlasmaWindowTest::testInternalWindowNoPlasmaWindow() { // this test verifies that an internal window is not added as a PlasmaWindow to the client QSignalSpy plasmaWindowCreatedSpy(m_windowManagement, &PlasmaWindowManagement::windowCreated); QVERIFY(plasmaWindowCreatedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); win.show(); QVERIFY(!plasmaWindowCreatedSpy.wait()); } void PlasmaWindowTest::testPopupWindowNoPlasmaWindow() { // this test verifies that for a popup window no PlasmaWindow is sent to the client QSignalSpy plasmaWindowCreatedSpy(m_windowManagement, &PlasmaWindowManagement::windowCreated); QVERIFY(plasmaWindowCreatedSpy.isValid()); // first create the parent window - QScopedPointer parentSurface(m_compositor->createSurface()); - QScopedPointer parentShellSurface(m_shell->createSurface(parentSurface.data())); + QScopedPointer parentSurface(Test::createSurface()); + QScopedPointer parentShellSurface(Test::createShellSurface(parentSurface.data())); // map that window - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - parentSurface->attachBuffer(m_shm->createBuffer(img)); - parentSurface->damage(QRect(0, 0, 100, 50)); - parentSurface->commit(); + Test::render(parentSurface.data(), QSize(100, 50), Qt::blue); // this should create a plasma window QVERIFY(plasmaWindowCreatedSpy.wait()); // now let's create a popup window for it - QScopedPointer popupSurface(m_compositor->createSurface()); - QScopedPointer popupShellSurface(m_shell->createSurface(popupSurface.data())); + QScopedPointer popupSurface(Test::createSurface()); + QScopedPointer popupShellSurface(Test::createShellSurface(popupSurface.data())); popupShellSurface->setTransient(parentSurface.data(), QPoint(0, 0), ShellSurface::TransientFlag::NoFocus); // let's map it - popupSurface->attachBuffer(m_shm->createBuffer(img)); - popupSurface->damage(QRect(0, 0, 100, 50)); - popupSurface->commit(); + Test::render(popupSurface.data(), QSize(100, 50), Qt::blue); // this should not create a plasma window QVERIFY(!plasmaWindowCreatedSpy.wait()); // now the same with an already mapped surface when we create the shell surface - QScopedPointer popup2Surface(m_compositor->createSurface()); - popup2Surface->attachBuffer(m_shm->createBuffer(img)); - popup2Surface->damage(QRect(0, 0, 100, 50)); - popup2Surface->commit(); - QScopedPointer popup2ShellSurface(m_shell->createSurface(popup2Surface.data())); + QScopedPointer popup2Surface(Test::createSurface()); + Test::render(popup2Surface.data(), QSize(100, 50), Qt::blue); + QScopedPointer popup2ShellSurface(Test::createShellSurface(popup2Surface.data())); popup2ShellSurface->setTransient(popupSurface.data(), QPoint(0, 0), ShellSurface::TransientFlag::NoFocus); // this should not create a plasma window QEXPECT_FAIL("", "The call to setTransient comes to late the window is already mapped then", Continue); QVERIFY(!plasmaWindowCreatedSpy.wait()); // let's destroy the windows QCOMPARE(waylandServer()->clients().count(), 3); QSignalSpy destroyed1Spy(waylandServer()->clients().last(), &QObject::destroyed); QVERIFY(destroyed1Spy.isValid()); popup2Surface->attachBuffer(Buffer::Ptr()); popup2Surface->commit(Surface::CommitFlag::None); popup2ShellSurface.reset(); popup2Surface.reset(); QVERIFY(destroyed1Spy.wait()); QCOMPARE(waylandServer()->clients().count(), 2); QSignalSpy destroyed2Spy(waylandServer()->clients().last(), &QObject::destroyed); QVERIFY(destroyed2Spy.isValid()); popupSurface->attachBuffer(Buffer::Ptr()); popupSurface->commit(Surface::CommitFlag::None); popupShellSurface.reset(); popupSurface.reset(); QVERIFY(destroyed2Spy.wait()); QCOMPARE(waylandServer()->clients().count(), 1); QSignalSpy destroyed3Spy(waylandServer()->clients().last(), &QObject::destroyed); QVERIFY(destroyed3Spy.isValid()); parentSurface->attachBuffer(Buffer::Ptr()); parentSurface->commit(Surface::CommitFlag::None); parentShellSurface.reset(); parentSurface.reset(); QVERIFY(destroyed3Spy.wait()); } void PlasmaWindowTest::testLockScreenNoPlasmaWindow() { if (!QFile::exists(QStringLiteral("/dev/dri/card0"))) { QSKIP("Needs a dri device"); } // this test verifies that lock screen windows are not exposed to PlasmaWindow QSignalSpy plasmaWindowCreatedSpy(m_windowManagement, &PlasmaWindowManagement::windowCreated); QVERIFY(plasmaWindowCreatedSpy.isValid()); // this time we use a QSignalSpy on ShellClient as it'a a little bit more complex setup QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); // lock ScreenLocker::KSldApp::self()->lock(ScreenLocker::EstablishLock::Immediate); QVERIFY(clientAddedSpy.wait()); QVERIFY(clientAddedSpy.first().first().value()->isLockScreen()); // should not be sent to the client QVERIFY(plasmaWindowCreatedSpy.isEmpty()); QVERIFY(!plasmaWindowCreatedSpy.wait()); // fake unlock QSignalSpy lockStateChangedSpy(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged); QVERIFY(lockStateChangedSpy.isValid()); const auto children = ScreenLocker::KSldApp::self()->children(); for (auto it = children.begin(); it != children.end(); ++it) { if (qstrcmp((*it)->metaObject()->className(), "LogindIntegration") != 0) { continue; } QMetaObject::invokeMethod(*it, "requestUnlock"); break; } QVERIFY(lockStateChangedSpy.wait()); QVERIFY(!waylandServer()->isScreenLocked()); } void PlasmaWindowTest::testDestroyedButNotUnmapped() { // this test verifies that also when a ShellSurface gets destroyed without a prior unmap // the PlasmaWindow gets destroyed on Client side QSignalSpy plasmaWindowCreatedSpy(m_windowManagement, &PlasmaWindowManagement::windowCreated); QVERIFY(plasmaWindowCreatedSpy.isValid()); // first create the parent window - QScopedPointer parentSurface(m_compositor->createSurface()); - QScopedPointer parentShellSurface(m_shell->createSurface(parentSurface.data())); + QScopedPointer parentSurface(Test::createSurface()); + QScopedPointer parentShellSurface(Test::createShellSurface(parentSurface.data())); // map that window - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - parentSurface->attachBuffer(m_shm->createBuffer(img)); - parentSurface->damage(QRect(0, 0, 100, 50)); - parentSurface->commit(); + Test::render(parentSurface.data(), QSize(100, 50), Qt::blue); // this should create a plasma window QVERIFY(plasmaWindowCreatedSpy.wait()); QCOMPARE(plasmaWindowCreatedSpy.count(), 1); auto window = plasmaWindowCreatedSpy.first().first().value(); QVERIFY(window); QSignalSpy destroyedSpy(window, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); // now destroy without an unmap parentShellSurface.reset(); parentSurface.reset(); QVERIFY(destroyedSpy.wait()); } } WAYLANDTEST_MAIN(KWin::PlasmaWindowTest) #include "plasmawindow_test.moc" diff --git a/autotests/integration/pointer_input.cpp b/autotests/integration/pointer_input.cpp index bd2ede8ce..3d99daf26 100644 --- a/autotests/integration/pointer_input.cpp +++ b/autotests/integration/pointer_input.cpp @@ -1,963 +1,890 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "abstract_client.h" #include "cursor.h" #include "deleted.h" #include "pointer_input.h" #include "options.h" #include "screenedge.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include #include #include -#include -#include #include #include #include #include #include #include #include namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_pointer_input-0"); class PointerInputTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testWarpingUpdatesFocus(); void testWarpingGeneratesPointerMotion(); void testUpdateFocusAfterScreenChange(); void testModifierClickUnrestrictedMove_data(); void testModifierClickUnrestrictedMove(); void testModifierScrollOpacity_data(); void testModifierScrollOpacity(); void testScrollAction(); void testFocusFollowsMouse(); void testMouseActionInactiveWindow_data(); void testMouseActionInactiveWindow(); void testMouseActionActiveWindow_data(); void testMouseActionActiveWindow(); void testCursorImage(); void testEffectOverrideCursorImage(); private: void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50)); - KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; KWayland::Client::Seat *m_seat = nullptr; - KWayland::Client::ShmPool *m_shm = nullptr; KWayland::Client::Shell *m_shell = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; void PointerInputTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); qputenv("XCURSOR_THEME", QByteArrayLiteral("DMZ-White")); qputenv("XCURSOR_SIZE", QByteArrayLiteral("24")); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); setenv("QT_QPA_PLATFORM", "wayland", true); waylandServer()->initWorkspace(); } void PointerInputTest::init() { - using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy seatSpy(®istry, &Registry::seatAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - QVERIFY(seatSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - QVERIFY(!seatSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); - m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); - QVERIFY(m_seat->isValid()); - QSignalSpy hasPointerSpy(m_seat, &Seat::hasPointerChanged); - QVERIFY(hasPointerSpy.isValid()); - QVERIFY(hasPointerSpy.wait()); + QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::Seat)); + QVERIFY(Test::waitForWaylandPointer()); + m_compositor = Test::waylandCompositor(); + m_shell = Test::waylandShell(); + m_seat = Test::waylandSeat(); screens()->setCurrent(0); Cursor::setPos(QPoint(640, 512)); } void PointerInputTest::cleanup() { - delete m_compositor; - m_compositor = nullptr; - delete m_seat; - m_seat = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_connection->deleteLater(); - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - m_connection = nullptr; - } + Test::destroyWaylandConnection(); } void PointerInputTest::render(KWayland::Client::Surface *surface, const QSize &size) { - QImage img(size, QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(QPoint(0, 0), size)); - surface->commit(KWayland::Client::Surface::CommitFlag::None); - m_connection->flush(); + Test::render(surface, size, Qt::blue); + Test::flushWaylandConnection(); } void PointerInputTest::testWarpingUpdatesFocus() { // this test verifies that warping the pointer creates pointer enter and leave events using namespace KWayland::Client; // create pointer and signal spy for enter and leave signals auto pointer = m_seat->createPointer(m_seat); QVERIFY(pointer); QVERIFY(pointer->isValid()); QSignalSpy enteredSpy(pointer, &Pointer::entered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(pointer, &Pointer::left); QVERIFY(leftSpy.isValid()); // create a window QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); + Surface *surface = Test::createSurface(m_compositor); QVERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); QVERIFY(shellSurface); render(surface); QVERIFY(clientAddedSpy.wait()); AbstractClient *window = workspace()->activeClient(); QVERIFY(window); // currently there should not be a focused pointer surface QVERIFY(!waylandServer()->seat()->focusedPointerSurface()); QVERIFY(!pointer->enteredSurface()); // enter Cursor::setPos(QPoint(25, 25)); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 1); // window should have focus QCOMPARE(pointer->enteredSurface(), surface); // also on the server QCOMPARE(waylandServer()->seat()->focusedPointerSurface(), window->surface()); // and out again Cursor::setPos(QPoint(250, 250));; QVERIFY(leftSpy.wait()); QCOMPARE(leftSpy.count(), 1); // there should not be a focused pointer surface anymore QVERIFY(!waylandServer()->seat()->focusedPointerSurface()); QVERIFY(!pointer->enteredSurface()); } void PointerInputTest::testWarpingGeneratesPointerMotion() { // this test verifies that warping the pointer creates pointer motion events using namespace KWayland::Client; // create pointer and signal spy for enter and motion auto pointer = m_seat->createPointer(m_seat); QVERIFY(pointer); QVERIFY(pointer->isValid()); QSignalSpy enteredSpy(pointer, &Pointer::entered); QVERIFY(enteredSpy.isValid()); QSignalSpy movedSpy(pointer, &Pointer::motion); QVERIFY(movedSpy.isValid()); // create a window QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); + Surface *surface = Test::createSurface(m_compositor); QVERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); QVERIFY(shellSurface); render(surface); QVERIFY(clientAddedSpy.wait()); AbstractClient *window = workspace()->activeClient(); QVERIFY(window); // enter kwinApp()->platform()->pointerMotion(QPointF(25, 25), 1); QVERIFY(enteredSpy.wait()); // we get a move event together with the enter, that's actually wrong but also shouldn't harm QVERIFY(movedSpy.wait()); QCOMPARE(movedSpy.count(), 1); QCOMPARE(movedSpy.first().first().toPointF(), QPointF(25, 25)); // now warp Cursor::setPos(QPoint(26, 26)); QVERIFY(movedSpy.wait()); QCOMPARE(movedSpy.count(), 2); QCOMPARE(movedSpy.last().first().toPointF(), QPointF(26, 26)); } void PointerInputTest::testUpdateFocusAfterScreenChange() { // this test verifies that a pointer enter event is generated when the cursor changes to another // screen due to removal of screen using namespace KWayland::Client; // ensure cursor is on second screen Cursor::setPos(1500, 300); // create pointer and signal spy for enter and motion auto pointer = m_seat->createPointer(m_seat); QVERIFY(pointer); QVERIFY(pointer->isValid()); QSignalSpy enteredSpy(pointer, &Pointer::entered); QVERIFY(enteredSpy.isValid()); // create a window QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); + Surface *surface = Test::createSurface(m_compositor); QVERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); QVERIFY(shellSurface); render(surface, QSize(1280, 1024)); QVERIFY(clientAddedSpy.wait()); AbstractClient *window = workspace()->activeClient(); QVERIFY(window); QVERIFY(!window->geometry().contains(Cursor::pos())); QSignalSpy screensChangedSpy(screens(), &Screens::changed); QVERIFY(screensChangedSpy.isValid()); // now let's remove the screen containing the cursor QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 1)); QMetaObject::invokeMethod(kwinApp()->platform(), "sizeChanged", Qt::QueuedConnection); QVERIFY(screensChangedSpy.wait()); QCOMPARE(screens()->count(), 1); // this should have warped the cursor QCOMPARE(Cursor::pos(), QPoint(639, 511)); QVERIFY(window->geometry().contains(Cursor::pos())); // and we should get an enter event QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 1); } void PointerInputTest::testModifierClickUnrestrictedMove_data() { QTest::addColumn("modifierKey"); QTest::addColumn("mouseButton"); QTest::addColumn("modKey"); const QString alt = QStringLiteral("Alt"); const QString meta = QStringLiteral("Meta"); QTest::newRow("Left Alt + Left Click") << KEY_LEFTALT << BTN_LEFT << alt; QTest::newRow("Left Alt + Right Click") << KEY_LEFTALT << BTN_RIGHT << alt; QTest::newRow("Left Alt + Middle Click") << KEY_LEFTALT << BTN_MIDDLE << alt; QTest::newRow("Right Alt + Left Click") << KEY_RIGHTALT << BTN_LEFT << alt; QTest::newRow("Right Alt + Right Click") << KEY_RIGHTALT << BTN_RIGHT << alt; QTest::newRow("Right Alt + Middle Click") << KEY_RIGHTALT << BTN_MIDDLE << alt; // now everything with meta QTest::newRow("Left Meta + Left Click") << KEY_LEFTMETA << BTN_LEFT << meta; QTest::newRow("Left Meta + Right Click") << KEY_LEFTMETA << BTN_RIGHT << meta; QTest::newRow("Left Meta + Middle Click") << KEY_LEFTMETA << BTN_MIDDLE << meta; QTest::newRow("Right Meta + Left Click") << KEY_RIGHTMETA << BTN_LEFT << meta; QTest::newRow("Right Meta + Right Click") << KEY_RIGHTMETA << BTN_RIGHT << meta; QTest::newRow("Right Meta + Middle Click") << KEY_RIGHTMETA << BTN_MIDDLE << meta; } void PointerInputTest::testModifierClickUnrestrictedMove() { // this test ensures that Alt+mouse button press triggers unrestricted move using namespace KWayland::Client; // create pointer and signal spy for button events auto pointer = m_seat->createPointer(m_seat); QVERIFY(pointer); QVERIFY(pointer->isValid()); QSignalSpy buttonSpy(pointer, &Pointer::buttonStateChanged); QVERIFY(buttonSpy.isValid()); // first modify the config for this run QFETCH(QString, modKey); KConfigGroup group = kwinApp()->config()->group("MouseBindings"); group.writeEntry("CommandAllKey", modKey); group.writeEntry("CommandAll1", "Move"); group.writeEntry("CommandAll2", "Move"); group.writeEntry("CommandAll3", "Move"); group.sync(); workspace()->slotReconfigure(); QCOMPARE(options->commandAllModifier(), modKey == QStringLiteral("Alt") ? Qt::AltModifier : Qt::MetaModifier); QCOMPARE(options->commandAll1(), Options::MouseUnrestrictedMove); QCOMPARE(options->commandAll2(), Options::MouseUnrestrictedMove); QCOMPARE(options->commandAll3(), Options::MouseUnrestrictedMove); // create a window QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); + Surface *surface = Test::createSurface(m_compositor); QVERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); QVERIFY(shellSurface); render(surface); QVERIFY(clientAddedSpy.wait()); AbstractClient *window = workspace()->activeClient(); QVERIFY(window); // move cursor on window Cursor::setPos(window->geometry().center()); // simulate modifier+click quint32 timestamp = 1; QFETCH(int, modifierKey); QFETCH(int, mouseButton); kwinApp()->platform()->keyboardKeyPressed(modifierKey, timestamp++); QVERIFY(!window->isMove()); kwinApp()->platform()->pointerButtonPressed(mouseButton, timestamp++); QVERIFY(window->isMove()); // release modifier should not change it kwinApp()->platform()->keyboardKeyReleased(modifierKey, timestamp++); QVERIFY(window->isMove()); // but releasing the key should end move/resize kwinApp()->platform()->pointerButtonReleased(mouseButton, timestamp++); QVERIFY(!window->isMove()); // all of that should not have triggered button events on the surface QCOMPARE(buttonSpy.count(), 0); // also waiting shouldn't give us the event QVERIFY(!buttonSpy.wait(100)); } void PointerInputTest::testModifierScrollOpacity_data() { QTest::addColumn("modifierKey"); QTest::addColumn("modKey"); const QString alt = QStringLiteral("Alt"); const QString meta = QStringLiteral("Meta"); QTest::newRow("Left Alt") << KEY_LEFTALT << alt; QTest::newRow("Right Alt") << KEY_RIGHTALT << alt; QTest::newRow("Left Meta") << KEY_LEFTMETA << meta; QTest::newRow("Right Meta") << KEY_RIGHTMETA << meta; } void PointerInputTest::testModifierScrollOpacity() { // this test verifies that mod+wheel performs a window operation and does not // pass the wheel to the window using namespace KWayland::Client; // create pointer and signal spy for button events auto pointer = m_seat->createPointer(m_seat); QVERIFY(pointer); QVERIFY(pointer->isValid()); QSignalSpy axisSpy(pointer, &Pointer::axisChanged); QVERIFY(axisSpy.isValid()); // first modify the config for this run QFETCH(QString, modKey); KConfigGroup group = kwinApp()->config()->group("MouseBindings"); group.writeEntry("CommandAllKey", modKey); group.writeEntry("CommandAllWheel", "change opacity"); group.sync(); workspace()->slotReconfigure(); // create a window QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); + Surface *surface = Test::createSurface(m_compositor); QVERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); QVERIFY(shellSurface); render(surface); QVERIFY(clientAddedSpy.wait()); AbstractClient *window = workspace()->activeClient(); QVERIFY(window); // set the opacity to 0.5 window->setOpacity(0.5); QCOMPARE(window->opacity(), 0.5); // move cursor on window Cursor::setPos(window->geometry().center()); // simulate modifier+wheel quint32 timestamp = 1; QFETCH(int, modifierKey); kwinApp()->platform()->keyboardKeyPressed(modifierKey, timestamp++); kwinApp()->platform()->pointerAxisVertical(-5, timestamp++); QCOMPARE(window->opacity(), 0.6); kwinApp()->platform()->pointerAxisVertical(5, timestamp++); QCOMPARE(window->opacity(), 0.5); kwinApp()->platform()->keyboardKeyReleased(modifierKey, timestamp++); // axis should have been filtered out QCOMPARE(axisSpy.count(), 0); QVERIFY(!axisSpy.wait(100)); } void PointerInputTest::testScrollAction() { // this test verifies that scroll on inactive window performs a mouse action using namespace KWayland::Client; auto pointer = m_seat->createPointer(m_seat); QVERIFY(pointer); QVERIFY(pointer->isValid()); QSignalSpy axisSpy(pointer, &Pointer::axisChanged); QVERIFY(axisSpy.isValid()); // first modify the config for this run KConfigGroup group = kwinApp()->config()->group("MouseBindings"); group.writeEntry("CommandWindowWheel", "activate and scroll"); group.sync(); workspace()->slotReconfigure(); // create two windows QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface1 = m_compositor->createSurface(m_compositor); + Surface *surface1 = Test::createSurface(m_compositor); QVERIFY(surface1); - ShellSurface *shellSurface1 = m_shell->createSurface(surface1, surface1); + ShellSurface *shellSurface1 = Test::createShellSurface(surface1, surface1); QVERIFY(shellSurface1); render(surface1); QVERIFY(clientAddedSpy.wait()); AbstractClient *window1 = workspace()->activeClient(); QVERIFY(window1); - Surface *surface2 = m_compositor->createSurface(m_compositor); + Surface *surface2 = Test::createSurface(m_compositor); QVERIFY(surface2); - ShellSurface *shellSurface2 = m_shell->createSurface(surface2, surface2); + ShellSurface *shellSurface2 = Test::createShellSurface(surface2, surface2); QVERIFY(shellSurface2); render(surface2); QVERIFY(clientAddedSpy.wait()); AbstractClient *window2 = workspace()->activeClient(); QVERIFY(window2); QVERIFY(window1 != window2); // move cursor to the inactive window Cursor::setPos(window1->geometry().center()); quint32 timestamp = 1; QVERIFY(!window1->isActive()); kwinApp()->platform()->pointerAxisVertical(5, timestamp++); QVERIFY(window1->isActive()); // but also the wheel event should be passed to the window QVERIFY(axisSpy.wait()); // we need to wait a little bit, otherwise the test crashes in effectshandler, needs fixing QTest::qWait(100); } void PointerInputTest::testFocusFollowsMouse() { using namespace KWayland::Client; // need to create a pointer, otherwise it doesn't accept focus auto pointer = m_seat->createPointer(m_seat); QVERIFY(pointer); QVERIFY(pointer->isValid()); // move cursor out of the way of first window to be created Cursor::setPos(900, 900); // first modify the config for this run KConfigGroup group = kwinApp()->config()->group("Windows"); group.writeEntry("AutoRaise", true); group.writeEntry("AutoRaiseInterval", 20); group.writeEntry("DelayFocusInterval", 200); group.writeEntry("FocusPolicy", "FocusFollowsMouse"); group.sync(); workspace()->slotReconfigure(); // verify the settings QCOMPARE(options->focusPolicy(), Options::FocusFollowsMouse); QVERIFY(options->isAutoRaise()); QCOMPARE(options->autoRaiseInterval(), 20); QCOMPARE(options->delayFocusInterval(), 200); // create two windows QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface1 = m_compositor->createSurface(m_compositor); + Surface *surface1 = Test::createSurface(m_compositor); QVERIFY(surface1); - ShellSurface *shellSurface1 = m_shell->createSurface(surface1, surface1); + ShellSurface *shellSurface1 = Test::createShellSurface(surface1, surface1); QVERIFY(shellSurface1); render(surface1, QSize(800, 800)); QVERIFY(clientAddedSpy.wait()); AbstractClient *window1 = workspace()->activeClient(); QVERIFY(window1); - Surface *surface2 = m_compositor->createSurface(m_compositor); + Surface *surface2 = Test::createSurface(m_compositor); QVERIFY(surface2); - ShellSurface *shellSurface2 = m_shell->createSurface(surface2, surface2); + ShellSurface *shellSurface2 = Test::createShellSurface(surface2, surface2); QVERIFY(shellSurface2); render(surface2, QSize(800, 800)); QVERIFY(clientAddedSpy.wait()); AbstractClient *window2 = workspace()->activeClient(); QVERIFY(window2); QVERIFY(window1 != window2); QCOMPARE(workspace()->topClientOnDesktop(1, -1), window2); // geometry of the two windows should be overlapping QVERIFY(window1->geometry().intersects(window2->geometry())); // signal spies for active window changed and stacking order changed QSignalSpy activeWindowChangedSpy(workspace(), &Workspace::clientActivated); QVERIFY(activeWindowChangedSpy.isValid()); QSignalSpy stackingOrderChangedSpy(workspace(), &Workspace::stackingOrderChanged); QVERIFY(stackingOrderChangedSpy.isValid()); QVERIFY(!window1->isActive()); QVERIFY(window2->isActive()); // move on top of first window QVERIFY(window1->geometry().contains(10, 10)); QVERIFY(!window2->geometry().contains(10, 10)); Cursor::setPos(10, 10); QVERIFY(stackingOrderChangedSpy.wait()); QCOMPARE(stackingOrderChangedSpy.count(), 1); QCOMPARE(workspace()->topClientOnDesktop(1, -1), window1); QTRY_VERIFY(window1->isActive()); // move on second window, but move away before active window change delay hits Cursor::setPos(810, 810); QVERIFY(stackingOrderChangedSpy.wait()); QCOMPARE(stackingOrderChangedSpy.count(), 2); QCOMPARE(workspace()->topClientOnDesktop(1, -1), window2); Cursor::setPos(10, 10); QVERIFY(!activeWindowChangedSpy.wait(250)); QVERIFY(window1->isActive()); QCOMPARE(workspace()->topClientOnDesktop(1, -1), window1); // as we moved back on window 1 that should been raised in the mean time QCOMPARE(stackingOrderChangedSpy.count(), 3); // quickly move on window 2 and back on window 1 should not raise window 2 Cursor::setPos(810, 810); Cursor::setPos(10, 10); QVERIFY(!stackingOrderChangedSpy.wait(250)); } void PointerInputTest::testMouseActionInactiveWindow_data() { QTest::addColumn("button"); QTest::newRow("Left") << quint32(BTN_LEFT); QTest::newRow("Middle") << quint32(BTN_MIDDLE); QTest::newRow("Right") << quint32(BTN_RIGHT); } void PointerInputTest::testMouseActionInactiveWindow() { // this test performs the mouse button window action on an inactive window // it should activate the window and raise it using namespace KWayland::Client; // first modify the config for this run - disable FocusFollowsMouse KConfigGroup group = kwinApp()->config()->group("Windows"); group.writeEntry("FocusPolicy", "ClickToFocus"); group.sync(); group = kwinApp()->config()->group("MouseBindings"); group.writeEntry("CommandWindow1", "Activate, raise and pass click"); group.writeEntry("CommandWindow2", "Activate, raise and pass click"); group.writeEntry("CommandWindow3", "Activate, raise and pass click"); group.sync(); workspace()->slotReconfigure(); // create two windows QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface1 = m_compositor->createSurface(m_compositor); + Surface *surface1 = Test::createSurface(m_compositor); QVERIFY(surface1); - ShellSurface *shellSurface1 = m_shell->createSurface(surface1, surface1); + ShellSurface *shellSurface1 = Test::createShellSurface(surface1, surface1); QVERIFY(shellSurface1); render(surface1, QSize(800, 800)); QVERIFY(clientAddedSpy.wait()); AbstractClient *window1 = workspace()->activeClient(); QVERIFY(window1); - Surface *surface2 = m_compositor->createSurface(m_compositor); + Surface *surface2 = Test::createSurface(m_compositor); QVERIFY(surface2); - ShellSurface *shellSurface2 = m_shell->createSurface(surface2, surface2); + ShellSurface *shellSurface2 = Test::createShellSurface(surface2, surface2); QVERIFY(shellSurface2); render(surface2, QSize(800, 800)); QVERIFY(clientAddedSpy.wait()); AbstractClient *window2 = workspace()->activeClient(); QVERIFY(window2); QVERIFY(window1 != window2); QCOMPARE(workspace()->topClientOnDesktop(1, -1), window2); // geometry of the two windows should be overlapping QVERIFY(window1->geometry().intersects(window2->geometry())); // signal spies for active window changed and stacking order changed QSignalSpy activeWindowChangedSpy(workspace(), &Workspace::clientActivated); QVERIFY(activeWindowChangedSpy.isValid()); QSignalSpy stackingOrderChangedSpy(workspace(), &Workspace::stackingOrderChanged); QVERIFY(stackingOrderChangedSpy.isValid()); QVERIFY(!window1->isActive()); QVERIFY(window2->isActive()); // move on top of first window QVERIFY(window1->geometry().contains(10, 10)); QVERIFY(!window2->geometry().contains(10, 10)); Cursor::setPos(10, 10); // no focus follows mouse QVERIFY(!stackingOrderChangedSpy.wait(200)); QVERIFY(stackingOrderChangedSpy.isEmpty()); QVERIFY(activeWindowChangedSpy.isEmpty()); QVERIFY(window2->isActive()); // and click quint32 timestamp = 1; QFETCH(quint32, button); kwinApp()->platform()->pointerButtonPressed(button, timestamp++); // should raise window1 and activate it QCOMPARE(stackingOrderChangedSpy.count(), 1); QVERIFY(!activeWindowChangedSpy.isEmpty()); QCOMPARE(workspace()->topClientOnDesktop(1, -1), window1); QVERIFY(window1->isActive()); QVERIFY(!window2->isActive()); // release again kwinApp()->platform()->pointerButtonReleased(button, timestamp++); } void PointerInputTest::testMouseActionActiveWindow_data() { QTest::addColumn("clickRaise"); QTest::addColumn("button"); for (quint32 i=BTN_LEFT; i < BTN_JOYSTICK; i++) { QByteArray number = QByteArray::number(i, 16); QTest::newRow(QByteArrayLiteral("click raise/").append(number).constData()) << true << i; QTest::newRow(QByteArrayLiteral("no click raise/").append(number).constData()) << false << i; } } void PointerInputTest::testMouseActionActiveWindow() { // this test verifies the mouse action performed on an active window // for all buttons it should trigger a window raise depending on the // click raise option using namespace KWayland::Client; // create a button spy - all clicks should be passed through auto pointer = m_seat->createPointer(m_seat); QVERIFY(pointer); QVERIFY(pointer->isValid()); QSignalSpy buttonSpy(pointer, &Pointer::buttonStateChanged); QVERIFY(buttonSpy.isValid()); // adjust config for this run QFETCH(bool, clickRaise); KConfigGroup group = kwinApp()->config()->group("Windows"); group.writeEntry("ClickRaise", clickRaise); group.sync(); workspace()->slotReconfigure(); QCOMPARE(options->isClickRaise(), clickRaise); // create two windows QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface1 = m_compositor->createSurface(m_compositor); + Surface *surface1 = Test::createSurface(m_compositor); QVERIFY(surface1); - ShellSurface *shellSurface1 = m_shell->createSurface(surface1, surface1); + ShellSurface *shellSurface1 = Test::createShellSurface(surface1, surface1); QVERIFY(shellSurface1); render(surface1, QSize(800, 800)); QVERIFY(clientAddedSpy.wait()); AbstractClient *window1 = workspace()->activeClient(); QVERIFY(window1); QSignalSpy window1DestroyedSpy(window1, &QObject::destroyed); QVERIFY(window1DestroyedSpy.isValid()); - Surface *surface2 = m_compositor->createSurface(m_compositor); + Surface *surface2 = Test::createSurface(m_compositor); QVERIFY(surface2); - ShellSurface *shellSurface2 = m_shell->createSurface(surface2, surface2); + ShellSurface *shellSurface2 = Test::createShellSurface(surface2, surface2); QVERIFY(shellSurface2); render(surface2, QSize(800, 800)); QVERIFY(clientAddedSpy.wait()); AbstractClient *window2 = workspace()->activeClient(); QVERIFY(window2); QVERIFY(window1 != window2); QSignalSpy window2DestroyedSpy(window2, &QObject::destroyed); QVERIFY(window2DestroyedSpy.isValid()); QCOMPARE(workspace()->topClientOnDesktop(1, -1), window2); // geometry of the two windows should be overlapping QVERIFY(window1->geometry().intersects(window2->geometry())); // lower the currently active window workspace()->lowerClient(window2); QCOMPARE(workspace()->topClientOnDesktop(1, -1), window1); // signal spy for stacking order spy QSignalSpy stackingOrderChangedSpy(workspace(), &Workspace::stackingOrderChanged); QVERIFY(stackingOrderChangedSpy.isValid()); // move on top of second window QVERIFY(!window1->geometry().contains(900, 900)); QVERIFY(window2->geometry().contains(900, 900)); Cursor::setPos(900, 900); // and click quint32 timestamp = 1; QFETCH(quint32, button); kwinApp()->platform()->pointerButtonPressed(button, timestamp++); QVERIFY(buttonSpy.wait()); if (clickRaise) { QCOMPARE(stackingOrderChangedSpy.count(), 1); QTRY_COMPARE_WITH_TIMEOUT(workspace()->topClientOnDesktop(1, -1), window2, 200); } else { QCOMPARE(stackingOrderChangedSpy.count(), 0); QVERIFY(!stackingOrderChangedSpy.wait(100)); QCOMPARE(workspace()->topClientOnDesktop(1, -1), window1); } // release again kwinApp()->platform()->pointerButtonReleased(button, timestamp++); delete surface1; QVERIFY(window1DestroyedSpy.wait()); delete surface2; QVERIFY(window2DestroyedSpy.wait()); } void PointerInputTest::testCursorImage() { // this test verifies that the pointer image gets updated correctly from the client provided data using namespace KWayland::Client; // we need a pointer to get the enter event auto pointer = m_seat->createPointer(m_seat); QVERIFY(pointer); QVERIFY(pointer->isValid()); QSignalSpy enteredSpy(pointer, &Pointer::entered); QVERIFY(enteredSpy.isValid()); // move cursor somewhere the new window won't open Cursor::setPos(800, 800); auto p = input()->pointer(); // at the moment it should be the fallback cursor QVERIFY(!p->cursorImage().isNull()); // create a window QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); + Surface *surface = Test::createSurface(m_compositor); QVERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); QVERIFY(shellSurface); render(surface); QVERIFY(clientAddedSpy.wait()); AbstractClient *window = workspace()->activeClient(); QVERIFY(window); // move cursor to center of window, this should first set a null pointer Cursor::setPos(window->geometry().center()); QCOMPARE(p->window().data(), window); QVERIFY(p->cursorImage().isNull()); QVERIFY(enteredSpy.wait()); // create a cursor on the pointer - Surface *cursorSurface = m_compositor->createSurface(m_compositor); + Surface *cursorSurface = Test::createSurface(m_compositor); QVERIFY(cursorSurface); QSignalSpy cursorRenderedSpy(cursorSurface, &Surface::frameRendered); QVERIFY(cursorRenderedSpy.isValid()); QImage red = QImage(QSize(10, 10), QImage::Format_ARGB32); red.fill(Qt::red); - cursorSurface->attachBuffer(m_shm->createBuffer(red)); + cursorSurface->attachBuffer(Test::waylandShmPool()->createBuffer(red)); cursorSurface->damage(QRect(0, 0, 10, 10)); cursorSurface->commit(); pointer->setCursor(cursorSurface, QPoint(5, 5)); QVERIFY(cursorRenderedSpy.wait()); QCOMPARE(p->cursorImage(), red); QCOMPARE(p->cursorHotSpot(), QPoint(5, 5)); // change hotspot pointer->setCursor(cursorSurface, QPoint(6, 6)); - m_connection->flush(); + Test::flushWaylandConnection(); QTRY_COMPARE(p->cursorHotSpot(), QPoint(6, 6)); QCOMPARE(p->cursorImage(), red); // change the buffer QImage blue = QImage(QSize(10, 10), QImage::Format_ARGB32); blue.fill(Qt::blue); - auto b = m_shm->createBuffer(blue); + auto b = Test::waylandShmPool()->createBuffer(blue); cursorSurface->attachBuffer(b); cursorSurface->damage(QRect(0, 0, 10, 10)); cursorSurface->commit(); QVERIFY(cursorRenderedSpy.wait()); QTRY_COMPARE(p->cursorImage(), blue); QCOMPARE(p->cursorHotSpot(), QPoint(6, 6)); // hide the cursor pointer->setCursor(nullptr); - m_connection->flush(); + Test::flushWaylandConnection(); QTRY_VERIFY(p->cursorImage().isNull()); // move cursor somewhere else, should reset to fallback cursor Cursor::setPos(window->geometry().bottomLeft() + QPoint(20, 20)); QVERIFY(p->window().isNull()); QVERIFY(!p->cursorImage().isNull()); } class HelperEffect : public Effect { Q_OBJECT public: HelperEffect() {} ~HelperEffect() {} }; void PointerInputTest::testEffectOverrideCursorImage() { // this test verifies the effect cursor override handling using namespace KWayland::Client; // we need a pointer to get the enter event and set a cursor auto pointer = m_seat->createPointer(m_seat); QVERIFY(pointer); QVERIFY(pointer->isValid()); QSignalSpy enteredSpy(pointer, &Pointer::entered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(pointer, &Pointer::left); QVERIFY(leftSpy.isValid()); // move cursor somewhere the new window won't open Cursor::setPos(800, 800); auto p = input()->pointer(); // here we should have the fallback cursor const QImage fallback = p->cursorImage(); QVERIFY(!fallback.isNull()); // now let's create a window QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); + Surface *surface = Test::createSurface(m_compositor); QVERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); QVERIFY(shellSurface); render(surface); QVERIFY(clientAddedSpy.wait()); AbstractClient *window = workspace()->activeClient(); QVERIFY(window); // and move cursor to the window QVERIFY(!window->geometry().contains(QPoint(800, 800))); Cursor::setPos(window->geometry().center()); QVERIFY(enteredSpy.wait()); // cursor image should be null QVERIFY(p->cursorImage().isNull()); // now create an effect and set an override cursor QScopedPointer effect(new HelperEffect); effects->startMouseInterception(effect.data(), Qt::SizeAllCursor); const QImage sizeAll = p->cursorImage(); QVERIFY(!sizeAll.isNull()); QVERIFY(sizeAll != fallback); QVERIFY(leftSpy.wait()); // let's change to arrow cursor, this should be our fallback effects->defineCursor(Qt::ArrowCursor); QCOMPARE(p->cursorImage(), fallback); // back to size all effects->defineCursor(Qt::SizeAllCursor); QCOMPARE(p->cursorImage(), sizeAll); // move cursor outside the window area Cursor::setPos(800, 800); // and end the override, which should switch to fallback effects->stopMouseInterception(effect.data()); QCOMPARE(p->cursorImage(), fallback); // start mouse interception again effects->startMouseInterception(effect.data(), Qt::SizeAllCursor); QCOMPARE(p->cursorImage(), sizeAll); // move cursor to area of window Cursor::setPos(window->geometry().center()); // this should not result in an enter event QVERIFY(!enteredSpy.wait(100)); // after ending the interception we should get an enter event effects->stopMouseInterception(effect.data()); QVERIFY(enteredSpy.wait()); QVERIFY(p->cursorImage().isNull()); } } WAYLANDTEST_MAIN(KWin::PointerInputTest) #include "pointer_input.moc" diff --git a/autotests/integration/quick_tiling_test.cpp b/autotests/integration/quick_tiling_test.cpp index 1cb2fd200..7e8e51fbf 100644 --- a/autotests/integration/quick_tiling_test.cpp +++ b/autotests/integration/quick_tiling_test.cpp @@ -1,525 +1,437 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "abstract_client.h" #include "cursor.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include #include -#include -#include #include -#include #include #include Q_DECLARE_METATYPE(KWin::AbstractClient::QuickTileMode) Q_DECLARE_METATYPE(KWin::MaximizeMode) namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_quick_tiling-0"); class QuickTilingTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testQuickTiling_data(); void testQuickTiling(); void testQuickMaximizing_data(); void testQuickMaximizing(); void testQuickTilingKeyboardMove_data(); void testQuickTilingKeyboardMove(); void testQuickTilingPointerMove_data(); void testQuickTilingPointerMove(); private: KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; - KWayland::Client::ShmPool *m_shm = nullptr; KWayland::Client::Shell *m_shell = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; void QuickTilingTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType("MaximizeMode"); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); // set custom config which disables the Outline KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); KConfigGroup group = config->group("Outline"); group.writeEntry(QStringLiteral("QmlPath"), QString("/does/not/exist.qml")); group.sync(); kwinApp()->setConfig(config); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); } void QuickTilingTest::init() { - using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); + QVERIFY(Test::setupWaylandConnection(s_socketName)); + m_connection = Test::waylandConnection(); + m_compositor = Test::waylandCompositor(); + m_shell = Test::waylandShell(); screens()->setCurrent(0); } void QuickTilingTest::cleanup() { - delete m_compositor; - m_compositor = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_connection->deleteLater(); - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - m_connection = nullptr; - } + Test::destroyWaylandConnection(); } void QuickTilingTest::testQuickTiling_data() { QTest::addColumn("mode"); QTest::addColumn("expectedGeometry"); QTest::addColumn("secondScreen"); #define FLAG(name) AbstractClient::QuickTileMode(AbstractClient::QuickTile##name) QTest::newRow("left") << FLAG(Left) << QRect(0, 0, 640, 1024) << QRect(1280, 0, 640, 1024); QTest::newRow("top") << FLAG(Top) << QRect(0, 0, 1280, 512) << QRect(1280, 0, 1280, 512); QTest::newRow("right") << FLAG(Right) << QRect(640, 0, 640, 1024) << QRect(1920, 0, 640, 1024); QTest::newRow("bottom") << FLAG(Bottom) << QRect(0, 512, 1280, 512) << QRect(1280, 512, 1280, 512); QTest::newRow("top left") << (FLAG(Left) | FLAG(Top)) << QRect(0, 0, 640, 512) << QRect(1280, 0, 640, 512); QTest::newRow("top right") << (FLAG(Right) | FLAG(Top)) << QRect(640, 0, 640, 512) << QRect(1920, 0, 640, 512); QTest::newRow("bottom left") << (FLAG(Left) | FLAG(Bottom)) << QRect(0, 512, 640, 512) << QRect(1280, 512, 640, 512); QTest::newRow("bottom right") << (FLAG(Right) | FLAG(Bottom)) << QRect(640, 512, 640, 512) << QRect(1920, 512, 640, 512); QTest::newRow("maximize") << FLAG(Maximize) << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024); #undef FLAG } void QuickTilingTest::testQuickTiling() { using namespace KWayland::Client; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); QSignalSpy sizeChangeSpy(shellSurface.data(), &ShellSurface::sizeChanged); QVERIFY(sizeChangeSpy.isValid()); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); m_connection->flush(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); QCOMPARE(c->geometry(), QRect(0, 0, 100, 50)); QCOMPARE(c->quickTileMode(), AbstractClient::QuickTileNone); QSignalSpy quickTileChangedSpy(c, &AbstractClient::quickTileModeChanged); QVERIFY(quickTileChangedSpy.isValid()); QSignalSpy geometryChangedSpy(c, &AbstractClient::geometryChanged); QVERIFY(geometryChangedSpy.isValid()); QFETCH(AbstractClient::QuickTileMode, mode); QFETCH(QRect, expectedGeometry); c->setQuickTileMode(mode, true); QCOMPARE(quickTileChangedSpy.count(), 1); // at this point the geometry did not yet change QCOMPARE(c->geometry(), QRect(0, 0, 100, 50)); // but quick tile mode already changed QCOMPARE(c->quickTileMode(), mode); // but we got requested a new geometry QVERIFY(sizeChangeSpy.wait()); QCOMPARE(sizeChangeSpy.count(), 1); QCOMPARE(sizeChangeSpy.first().first().toSize(), expectedGeometry.size()); // attach a new image - img = QImage(expectedGeometry.size(), QImage::Format_ARGB32); - img.fill(Qt::red); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(QPoint(0, 0), expectedGeometry.size())); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), expectedGeometry.size(), Qt::red); m_connection->flush(); QVERIFY(geometryChangedSpy.wait()); QEXPECT_FAIL("maximize", "Geometry changed called twice for maximize", Continue); QCOMPARE(geometryChangedSpy.count(), 1); QCOMPARE(c->geometry(), expectedGeometry); // send window to other screen QCOMPARE(c->screen(), 0); c->sendToScreen(1); QCOMPARE(c->screen(), 1); // quick tile should not be changed QEXPECT_FAIL("maximize", "Maximize loses the state", Continue); QCOMPARE(c->quickTileMode(), mode); QTEST(c->geometry(), "secondScreen"); } void QuickTilingTest::testQuickMaximizing_data() { QTest::addColumn("mode"); #define FLAG(name) AbstractClient::QuickTileMode(AbstractClient::QuickTile##name) QTest::newRow("maximize") << FLAG(Maximize); QTest::newRow("none") << FLAG(None); #undef FLAG } void QuickTilingTest::testQuickMaximizing() { using namespace KWayland::Client; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); QSignalSpy sizeChangeSpy(shellSurface.data(), &ShellSurface::sizeChanged); QVERIFY(sizeChangeSpy.isValid()); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); m_connection->flush(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); QCOMPARE(c->geometry(), QRect(0, 0, 100, 50)); QCOMPARE(c->quickTileMode(), AbstractClient::QuickTileNone); QCOMPARE(c->maximizeMode(), MaximizeRestore); QSignalSpy quickTileChangedSpy(c, &AbstractClient::quickTileModeChanged); QVERIFY(quickTileChangedSpy.isValid()); QSignalSpy geometryChangedSpy(c, &AbstractClient::geometryChanged); QVERIFY(geometryChangedSpy.isValid()); QSignalSpy maximizeChangedSpy1(c, SIGNAL(clientMaximizedStateChanged(KWin::AbstractClient*,MaximizeMode))); QVERIFY(maximizeChangedSpy1.isValid()); QSignalSpy maximizeChangedSpy2(c, SIGNAL(clientMaximizedStateChanged(KWin::AbstractClient*,bool,bool))); QVERIFY(maximizeChangedSpy2.isValid()); c->setQuickTileMode(AbstractClient::QuickTileMaximize, true); QCOMPARE(quickTileChangedSpy.count(), 1); QCOMPARE(maximizeChangedSpy1.count(), 1); QCOMPARE(maximizeChangedSpy1.first().first().value(), c); QCOMPARE(maximizeChangedSpy1.first().last().value(), MaximizeFull); QCOMPARE(maximizeChangedSpy2.count(), 1); QCOMPARE(maximizeChangedSpy2.first().first().value(), c); QCOMPARE(maximizeChangedSpy2.first().at(1).toBool(), true); QCOMPARE(maximizeChangedSpy2.first().at(2).toBool(), true); // at this point the geometry did not yet change QCOMPARE(c->geometry(), QRect(0, 0, 100, 50)); // but quick tile mode already changed QCOMPARE(c->quickTileMode(), AbstractClient::QuickTileMaximize); QCOMPARE(c->maximizeMode(), MaximizeFull); QCOMPARE(c->geometryRestore(), QRect(0, 0, 100, 50)); // but we got requested a new geometry QVERIFY(sizeChangeSpy.wait()); QCOMPARE(sizeChangeSpy.count(), 1); QCOMPARE(sizeChangeSpy.first().first().toSize(), QSize(1280, 1024)); // attach a new image - img = QImage(QSize(1280, 1024), QImage::Format_ARGB32); - img.fill(Qt::red); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 1280, 1024)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(1280, 1024), Qt::red); m_connection->flush(); QVERIFY(geometryChangedSpy.wait()); QCOMPARE(geometryChangedSpy.count(), 2); QCOMPARE(c->geometry(), QRect(0, 0, 1280, 1024)); QCOMPARE(c->geometryRestore(), QRect(0, 0, 100, 50)); // go back to quick tile none QFETCH(AbstractClient::QuickTileMode, mode); c->setQuickTileMode(mode, true); QCOMPARE(quickTileChangedSpy.count(), 2); QCOMPARE(maximizeChangedSpy1.count(), 2); QCOMPARE(maximizeChangedSpy1.last().first().value(), c); QCOMPARE(maximizeChangedSpy1.last().last().value(), MaximizeRestore); QCOMPARE(maximizeChangedSpy2.count(), 2); QCOMPARE(maximizeChangedSpy2.last().first().value(), c); QCOMPARE(maximizeChangedSpy2.last().at(1).toBool(), false); QCOMPARE(maximizeChangedSpy2.last().at(2).toBool(), false); QCOMPARE(c->quickTileMode(), AbstractClient::QuickTileNone); QCOMPARE(c->maximizeMode(), MaximizeRestore); // geometry not yet changed QCOMPARE(c->geometry(), QRect(0, 0, 1280, 1024)); QCOMPARE(c->geometryRestore(), QRect(0, 0, 100, 50)); // we got requested a new geometry QVERIFY(sizeChangeSpy.wait()); QCOMPARE(sizeChangeSpy.count(), 2); QCOMPARE(sizeChangeSpy.last().first().toSize(), QSize(100, 50)); // render again - img = QImage(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::yellow); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::yellow); m_connection->flush(); QVERIFY(geometryChangedSpy.wait()); QCOMPARE(geometryChangedSpy.count(), 4); QCOMPARE(c->geometry(), QRect(0, 0, 100, 50)); QCOMPARE(c->geometryRestore(), QRect(0, 0, 100, 50)); } void QuickTilingTest::testQuickTilingKeyboardMove_data() { QTest::addColumn("targetPos"); QTest::addColumn("expectedMode"); QTest::newRow("topRight") << QPoint(2559, 24) << AbstractClient::QuickTileMode(AbstractClient::QuickTileTop | AbstractClient::QuickTileRight); QTest::newRow("right") << QPoint(2559, 512) << AbstractClient::QuickTileMode(AbstractClient::QuickTileRight); QTest::newRow("bottomRight") << QPoint(2559, 1023) << AbstractClient::QuickTileMode(AbstractClient::QuickTileBottom | AbstractClient::QuickTileRight); QTest::newRow("bottomLeft") << QPoint(0, 1023) << AbstractClient::QuickTileMode(AbstractClient::QuickTileBottom | AbstractClient::QuickTileLeft); QTest::newRow("Left") << QPoint(0, 512) << AbstractClient::QuickTileMode(AbstractClient::QuickTileLeft); QTest::newRow("topLeft") << QPoint(0, 24) << AbstractClient::QuickTileMode(AbstractClient::QuickTileTop | AbstractClient::QuickTileLeft); } void QuickTilingTest::testQuickTilingKeyboardMove() { using namespace KWayland::Client; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); QSignalSpy sizeChangeSpy(shellSurface.data(), &ShellSurface::sizeChanged); QVERIFY(sizeChangeSpy.isValid()); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); m_connection->flush(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); QCOMPARE(c->geometry(), QRect(0, 0, 100, 50)); QCOMPARE(c->quickTileMode(), AbstractClient::QuickTileNone); QCOMPARE(c->maximizeMode(), MaximizeRestore); QSignalSpy quickTileChangedSpy(c, &AbstractClient::quickTileModeChanged); QVERIFY(quickTileChangedSpy.isValid()); workspace()->performWindowOperation(c, Options::UnrestrictedMoveOp); QCOMPARE(c, workspace()->getMovingClient()); QCOMPARE(Cursor::pos(), QPoint(49, 24)); QFETCH(QPoint, targetPos); quint32 timestamp = 1; kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTCTRL, timestamp++); while (Cursor::pos().x() > targetPos.x()) { kwinApp()->platform()->keyboardKeyPressed(KEY_LEFT, timestamp++); kwinApp()->platform()->keyboardKeyReleased(KEY_LEFT, timestamp++); } while (Cursor::pos().x() < targetPos.x()) { kwinApp()->platform()->keyboardKeyPressed(KEY_RIGHT, timestamp++); kwinApp()->platform()->keyboardKeyReleased(KEY_RIGHT, timestamp++); } while (Cursor::pos().y() < targetPos.y()) { kwinApp()->platform()->keyboardKeyPressed(KEY_DOWN, timestamp++); kwinApp()->platform()->keyboardKeyReleased(KEY_DOWN, timestamp++); } while (Cursor::pos().y() > targetPos.y()) { kwinApp()->platform()->keyboardKeyPressed(KEY_UP, timestamp++); kwinApp()->platform()->keyboardKeyReleased(KEY_UP, timestamp++); } kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTCTRL, timestamp++); kwinApp()->platform()->keyboardKeyPressed(KEY_ENTER, timestamp++); kwinApp()->platform()->keyboardKeyReleased(KEY_ENTER, timestamp++); QCOMPARE(Cursor::pos(), targetPos); QVERIFY(!workspace()->getMovingClient()); QCOMPARE(quickTileChangedSpy.count(), 1); QTEST(c->quickTileMode(), "expectedMode"); } void QuickTilingTest::testQuickTilingPointerMove_data() { QTest::addColumn("targetPos"); QTest::addColumn("expectedMode"); QTest::newRow("topRight") << QPoint(2559, 24) << AbstractClient::QuickTileMode(AbstractClient::QuickTileTop | AbstractClient::QuickTileRight); QTest::newRow("right") << QPoint(2559, 512) << AbstractClient::QuickTileMode(AbstractClient::QuickTileRight); QTest::newRow("bottomRight") << QPoint(2559, 1023) << AbstractClient::QuickTileMode(AbstractClient::QuickTileBottom | AbstractClient::QuickTileRight); QTest::newRow("bottomLeft") << QPoint(0, 1023) << AbstractClient::QuickTileMode(AbstractClient::QuickTileBottom | AbstractClient::QuickTileLeft); QTest::newRow("Left") << QPoint(0, 512) << AbstractClient::QuickTileMode(AbstractClient::QuickTileLeft); QTest::newRow("topLeft") << QPoint(0, 24) << AbstractClient::QuickTileMode(AbstractClient::QuickTileTop | AbstractClient::QuickTileLeft); } void QuickTilingTest::testQuickTilingPointerMove() { using namespace KWayland::Client; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); QSignalSpy sizeChangeSpy(shellSurface.data(), &ShellSurface::sizeChanged); QVERIFY(sizeChangeSpy.isValid()); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); m_connection->flush(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); QCOMPARE(c->geometry(), QRect(0, 0, 100, 50)); QCOMPARE(c->quickTileMode(), AbstractClient::QuickTileNone); QCOMPARE(c->maximizeMode(), MaximizeRestore); QSignalSpy quickTileChangedSpy(c, &AbstractClient::quickTileModeChanged); QVERIFY(quickTileChangedSpy.isValid()); workspace()->performWindowOperation(c, Options::UnrestrictedMoveOp); QCOMPARE(c, workspace()->getMovingClient()); QCOMPARE(Cursor::pos(), QPoint(49, 24)); QFETCH(QPoint, targetPos); quint32 timestamp = 1; kwinApp()->platform()->pointerMotion(targetPos, timestamp++); kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++); kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++); QCOMPARE(Cursor::pos(), targetPos); QVERIFY(!workspace()->getMovingClient()); QCOMPARE(quickTileChangedSpy.count(), 1); QTEST(c->quickTileMode(), "expectedMode"); } } WAYLANDTEST_MAIN(KWin::QuickTilingTest) #include "quick_tiling_test.moc" diff --git a/autotests/integration/shell_client_test.cpp b/autotests/integration/shell_client_test.cpp index 525eb8a52..6b603ce9f 100644 --- a/autotests/integration/shell_client_test.cpp +++ b/autotests/integration/shell_client_test.cpp @@ -1,212 +1,135 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "cursor.h" #include "platform.h" #include "shell_client.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include #include -#include -#include #include -#include #include #include -#include - using namespace KWin; using namespace KWayland::Client; static const QString s_socketName = QStringLiteral("wayland_test_kwin_shell_client-0"); class TestShellClient : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testMapUnmapMap(); - -private: - KWayland::Client::Compositor *m_compositor = nullptr; - Shell *m_shell = nullptr; - ShmPool *m_shm = nullptr; - EventQueue *m_queue = nullptr; - ConnectionThread *m_connection = nullptr; - QThread *m_thread = nullptr; }; void TestShellClient::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); waylandServer()->initWorkspace(); } void TestShellClient::init() { - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - - m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, - registry.interface(Registry::Interface::Compositor).version, - this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(registry.interface(Registry::Interface::Shm).name, - registry.interface(Registry::Interface::Shm).version, - this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(registry.interface(Registry::Interface::Shell).name, - registry.interface(Registry::Interface::Shell).version, - this); - QVERIFY(m_shell->isValid()); + QVERIFY(Test::setupWaylandConnection(s_socketName)); screens()->setCurrent(0); KWin::Cursor::setPos(QPoint(1280, 512)); } void TestShellClient::cleanup() { -#define CLEANUP(name) \ - if (name) { \ - delete name; \ - name = nullptr; \ - } - CLEANUP(m_compositor) - CLEANUP(m_shm) - CLEANUP(m_shell) - CLEANUP(m_queue) - - if (m_connection) { - m_connection->deleteLater(); - m_connection = nullptr; - } - if (m_thread) { - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - } -#undef CLEANUP + Test::destroyWaylandConnection(); } void TestShellClient::testMapUnmapMap() { // this test verifies that mapping a previously mapped window works correctly QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface(m_compositor->createSurface()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); // now let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); QVERIFY(clientAddedSpy.isEmpty()); QVERIFY(clientAddedSpy.wait()); auto client = clientAddedSpy.first().first().value(); QVERIFY(client); QVERIFY(client->isShown(true)); QCOMPARE(workspace()->activeClient(), client); // now unmap QSignalSpy hiddenSpy(client, &ShellClient::windowHidden); QVERIFY(hiddenSpy.isValid()); QSignalSpy windowClosedSpy(client, &ShellClient::windowClosed); QVERIFY(windowClosedSpy.isValid()); surface->attachBuffer(Buffer::Ptr()); surface->commit(Surface::CommitFlag::None); QVERIFY(hiddenSpy.wait()); QVERIFY(windowClosedSpy.isEmpty()); QVERIFY(!workspace()->activeClient()); QSignalSpy windowShownSpy(client, &ShellClient::windowShown); QVERIFY(windowShownSpy.isValid()); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), QSize(100, 50), Qt::blue); QCOMPARE(clientAddedSpy.count(), 1); QVERIFY(windowShownSpy.wait()); QCOMPARE(windowShownSpy.count(), 1); QCOMPARE(clientAddedSpy.count(), 1); QCOMPARE(workspace()->activeClient(), client); // let's unmap again surface->attachBuffer(Buffer::Ptr()); surface->commit(Surface::CommitFlag::None); QVERIFY(hiddenSpy.wait()); QCOMPARE(hiddenSpy.count(), 2); QVERIFY(windowClosedSpy.isEmpty()); shellSurface.reset(); surface.reset(); QVERIFY(windowClosedSpy.wait()); QCOMPARE(windowClosedSpy.count(), 1); } WAYLANDTEST_MAIN(TestShellClient) #include "shell_client_test.moc" diff --git a/autotests/integration/start_test.cpp b/autotests/integration/start_test.cpp index 8feb17861..f414ce3f3 100644 --- a/autotests/integration/start_test.cpp +++ b/autotests/integration/start_test.cpp @@ -1,168 +1,135 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include #include -#include -#include #include -#include #include namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_start_test-0"); class StartTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); + void cleanup(); void testScreens(); void testNoWindowsAtStart(); void testCreateWindow(); }; void StartTest::initTestCase() { QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); } +void StartTest::cleanup() +{ + Test::destroyWaylandConnection(); +} + void StartTest::testScreens() { QCOMPARE(screens()->count(), 1); QCOMPARE(screens()->size(), QSize(1280, 1024)); QCOMPARE(screens()->geometry(), QRect(0, 0, 1280, 1024)); } void StartTest::testNoWindowsAtStart() { QVERIFY(workspace()->clientList().isEmpty()); QVERIFY(workspace()->desktopList().isEmpty()); QVERIFY(workspace()->allClientList().isEmpty()); QVERIFY(workspace()->deletedList().isEmpty()); QVERIFY(workspace()->unmanagedList().isEmpty()); QVERIFY(waylandServer()->clients().isEmpty()); } void StartTest::testCreateWindow() { // first we need to connect to the server using namespace KWayland::Client; - auto connection = new ConnectionThread; - QSignalSpy connectedSpy(connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - connection->setSocketName(s_socketName); - QThread *thread = new QThread(this); - connection->moveToThread(thread); - thread->start(); - connection->initConnection(); - QVERIFY(connectedSpy.wait()); + QVERIFY(Test::setupWaylandConnection(s_socketName)); QSignalSpy shellClientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(shellClientAddedSpy.isValid()); QSignalSpy shellClientRemovedSpy(waylandServer(), &WaylandServer::shellClientRemoved); QVERIFY(shellClientRemovedSpy.isValid()); { - EventQueue queue; - queue.setup(connection); - Registry registry; - registry.setEventQueue(&queue); - registry.create(connection); - QSignalSpy registryAnnouncedSpy(®istry, &Registry::interfacesAnnounced); - QVERIFY(registryAnnouncedSpy.isValid()); - registry.setup(); - QVERIFY(registryAnnouncedSpy.wait()); - - const auto compositorData = registry.interface(Registry::Interface::Compositor); - const auto shellData = registry.interface(Registry::Interface::Shell); - const auto shmData = registry.interface(Registry::Interface::Shm); - QScopedPointer compositor(registry.createCompositor(compositorData.name, compositorData.version)); - QVERIFY(!compositor.isNull()); - QScopedPointer shell(registry.createShell(shellData.name, shellData.version)); - QVERIFY(!shell.isNull()); - - QScopedPointer surface(compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); QSignalSpy surfaceRenderedSpy(surface.data(), &Surface::frameRendered); QVERIFY(surfaceRenderedSpy.isValid()); - QScopedPointer shellSurface(shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); - connection->flush(); + Test::flushWaylandConnection(); QVERIFY(waylandServer()->clients().isEmpty()); // now dispatch should give us the client waylandServer()->dispatch(); QCOMPARE(waylandServer()->clients().count(), 1); // but still not yet in workspace QVERIFY(workspace()->allClientList().isEmpty()); // icon geometry accesses windowManagementInterface which only exists after window became visible // verify that accessing doesnt't crash QVERIFY(waylandServer()->clients().first()->iconGeometry().isNull()); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - QScopedPointer shm(registry.createShmPool(shmData.name, shmData.version)); - QVERIFY(!shm.isNull()); - surface->attachBuffer(shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); + Test::render(surface.data(), QSize(100, 50), Qt::blue); surface->commit(); - connection->flush(); + Test::flushWaylandConnection(); QVERIFY(shellClientAddedSpy.wait()); QCOMPARE(workspace()->allClientList().count(), 1); QCOMPARE(workspace()->allClientList().first(), waylandServer()->clients().first()); QVERIFY(workspace()->activeClient()); QCOMPARE(workspace()->activeClient()->pos(), QPoint(0, 0)); QCOMPARE(workspace()->activeClient()->size(), QSize(100, 50)); QCOMPARE(workspace()->activeClient()->geometry(), QRect(0, 0, 100, 50)); // and kwin will render it QVERIFY(surfaceRenderedSpy.wait()); } // this should tear down everything again QVERIFY(shellClientRemovedSpy.wait()); QVERIFY(waylandServer()->clients().isEmpty()); - - // cleanup - connection->deleteLater(); - thread->quit(); - thread->wait(); } } WAYLANDTEST_MAIN(KWin::StartTest) #include "start_test.moc" diff --git a/autotests/integration/struts_test.cpp b/autotests/integration/struts_test.cpp index ec6a98749..2dd4f588d 100644 --- a/autotests/integration/struts_test.cpp +++ b/autotests/integration/struts_test.cpp @@ -1,830 +1,729 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "client.h" #include "cursor.h" #include "screenedge.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include -#include #include -#include -#include #include -#include -#include #include -#include -#include #include #include #include #include namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_struts-0"); class StrutsTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testWaylandStruts_data(); void testWaylandStruts(); void testMoveWaylandPanel(); void testWaylandMobilePanel(); void testX11Struts_data(); void testX11Struts(); void test363804(); private: - KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; - KWayland::Client::ServerSideDecorationManager *m_deco = nullptr; - KWayland::Client::Seat *m_seat = nullptr; - KWayland::Client::ShmPool *m_shm = nullptr; - KWayland::Client::Shell *m_shell = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; KWayland::Client::PlasmaShell *m_plasmaShell = nullptr; - QThread *m_thread = nullptr; }; void StrutsTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); setenv("QT_QPA_PLATFORM", "wayland", true); waylandServer()->initWorkspace(); } void StrutsTest::init() { - using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy seatSpy(®istry, &Registry::seatAnnounced); - QSignalSpy decorationSpy(®istry, &Registry::serverSideDecorationManagerAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - QVERIFY(seatSpy.isValid()); - QVERIFY(decorationSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - QVERIFY(!seatSpy.isEmpty()); - QVERIFY(!decorationSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); - m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); - QVERIFY(m_seat->isValid()); - m_deco = registry.createServerSideDecorationManager(decorationSpy.first().first().value(), decorationSpy.first().last().value()); - QVERIFY(m_deco->isValid()); - m_plasmaShell = registry.createPlasmaShell(registry.interface(Registry::Interface::PlasmaShell).name, - registry.interface(Registry::Interface::PlasmaShell).version, - this); - QVERIFY(m_plasmaShell); - QSignalSpy hasPointerSpy(m_seat, &Seat::hasPointerChanged); - QVERIFY(hasPointerSpy.isValid()); - QVERIFY(hasPointerSpy.wait()); + QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::PlasmaShell)); + m_compositor = Test::waylandCompositor(); + m_plasmaShell = Test::waylandPlasmaShell(); screens()->setCurrent(0); Cursor::setPos(QPoint(640, 512)); + QVERIFY(waylandServer()->clients().isEmpty()); } void StrutsTest::cleanup() { - delete m_compositor; - m_compositor = nullptr; - delete m_deco; - m_deco = nullptr; - delete m_seat; - m_seat = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_plasmaShell; - m_plasmaShell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_connection->deleteLater(); - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - m_connection = nullptr; - } - while (!waylandServer()->clients().isEmpty()) { - QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); - } - QVERIFY(waylandServer()->clients().isEmpty()); + Test::destroyWaylandConnection(); } void StrutsTest::testWaylandStruts_data() { QTest::addColumn>("windowGeometries"); QTest::addColumn("screen0Maximized"); QTest::addColumn("screen1Maximized"); QTest::addColumn("workArea"); QTest::newRow("bottom/0") << QVector{QRect(0, 992, 1280, 32)} << QRect(0, 0, 1280, 992) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 992); QTest::newRow("bottom/1") << QVector{QRect(1280, 992, 1280, 32)} << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 992) << QRect(0, 0, 2560, 992); QTest::newRow("top/0") << QVector{QRect(0, 0, 1280, 32)} << QRect(0, 32, 1280, 992) << QRect(1280, 0, 1280, 1024) << QRect(0, 32, 2560, 992); QTest::newRow("top/1") << QVector{QRect(1280, 0, 1280, 32)} << QRect(0, 0, 1280, 1024) << QRect(1280, 32, 1280, 992) << QRect(0, 32, 2560, 992); QTest::newRow("left/0") << QVector{QRect(0, 0, 32, 1024)} << QRect(32, 0, 1248, 1024) << QRect(1280, 0, 1280, 1024) << QRect(32, 0, 2528, 1024); QTest::newRow("left/1") << QVector{QRect(1280, 0, 32, 1024)} << QRect(0, 0, 1280, 1024) << QRect(1312, 0, 1248, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("right/0") << QVector{QRect(1248, 0, 32, 1024)} << QRect(0, 0, 1248, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("right/1") << QVector{QRect(2528, 0, 32, 1024)} << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1248, 1024) << QRect(0, 0, 2528, 1024); // same with partial panels not covering the whole area QTest::newRow("part bottom/0") << QVector{QRect(100, 992, 1080, 32)} << QRect(0, 0, 1280, 992) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 992); QTest::newRow("part bottom/1") << QVector{QRect(1380, 992, 1080, 32)} << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 992) << QRect(0, 0, 2560, 992); QTest::newRow("part top/0") << QVector{QRect(100, 0, 1080, 32)} << QRect(0, 32, 1280, 992) << QRect(1280, 0, 1280, 1024) << QRect(0, 32, 2560, 992); QTest::newRow("part top/1") << QVector{QRect(1380, 0, 1080, 32)} << QRect(0, 0, 1280, 1024) << QRect(1280, 32, 1280, 992) << QRect(0, 32, 2560, 992); QTest::newRow("part left/0") << QVector{QRect(0, 100, 32, 824)} << QRect(32, 0, 1248, 1024) << QRect(1280, 0, 1280, 1024) << QRect(32, 0, 2528, 1024); QTest::newRow("part left/1") << QVector{QRect(1280, 100, 32, 824)} << QRect(0, 0, 1280, 1024) << QRect(1312, 0, 1248, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("part right/0") << QVector{QRect(1248, 100, 32, 824)} << QRect(0, 0, 1248, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("part right/1") << QVector{QRect(2528, 100, 32, 824)} << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1248, 1024) << QRect(0, 0, 2528, 1024); // multiple panels QTest::newRow("two bottom panels") << QVector{QRect(100, 992, 1080, 32), QRect(1380, 984, 1080, 40)} << QRect(0, 0, 1280, 992) << QRect(1280, 0, 1280, 984) << QRect(0, 0, 2560, 984); QTest::newRow("two left panels") << QVector{QRect(0, 10, 32, 390), QRect(0, 450, 40, 100)} << QRect(40, 0, 1240, 1024) << QRect(1280, 0, 1280, 1024) << QRect(40, 0, 2520, 1024); } void StrutsTest::testWaylandStruts() { // this test verifies that struts on Wayland panels are handled correctly using namespace KWayland::Client; // no, struts yet QVERIFY(waylandServer()->clients().isEmpty()); // first screen QCOMPARE(workspace()->clientArea(PlacementArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MovementArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeFullArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(FullScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(ScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); // second screen QCOMPARE(workspace()->clientArea(PlacementArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MovementArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeFullArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(FullScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(ScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); // combined QCOMPARE(workspace()->clientArea(WorkArea, 0, 1), QRect(0, 0, 2560, 1024)); QCOMPARE(workspace()->clientArea(FullArea, 0, 1), QRect(0, 0, 2560, 1024)); QFETCH(QVector, windowGeometries); // create the panels QSignalSpy windowCreatedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(windowCreatedSpy.isValid()); + QHash clients; for (auto it = windowGeometries.constBegin(), end = windowGeometries.constEnd(); it != end; it++) { const QRect windowGeometry = *it; - Surface *surface = m_compositor->createSurface(m_compositor); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + Surface *surface = Test::createSurface(m_compositor); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); Q_UNUSED(shellSurface) PlasmaShellSurface *plasmaSurface = m_plasmaShell->createSurface(surface, surface); plasmaSurface->setPosition(windowGeometry.topLeft()); plasmaSurface->setRole(PlasmaShellSurface::Role::Panel); // map the window - QImage img(windowGeometry.size(), QImage::Format_RGB32); - img.fill(Qt::red); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(QPoint(0, 0), windowGeometry.size())); - surface->commit(Surface::CommitFlag::None); + Test::render(surface, windowGeometry.size(), Qt::red, QImage::Format_RGB32); QVERIFY(windowCreatedSpy.wait()); QCOMPARE(windowCreatedSpy.count(), 1); auto c = windowCreatedSpy.first().first().value(); QVERIFY(c); QVERIFY(!c->isActive()); QCOMPARE(c->geometry(), windowGeometry); QVERIFY(c->isDock()); QVERIFY(c->hasStrut()); windowCreatedSpy.clear(); + clients.insert(surface, c); } // some props are independent of struts - those first // screen 0 QCOMPARE(workspace()->clientArea(MovementArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeFullArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(FullScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(ScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); // screen 1 QCOMPARE(workspace()->clientArea(MovementArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeFullArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(FullScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(ScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); // combined QCOMPARE(workspace()->clientArea(FullArea, 0, 1), QRect(0, 0, 2560, 1024)); // now verify the actual updated client areas QTEST(workspace()->clientArea(PlacementArea, 0, 1), "screen0Maximized"); QTEST(workspace()->clientArea(MaximizeArea, 0, 1), "screen0Maximized"); QTEST(workspace()->clientArea(PlacementArea, 1, 1), "screen1Maximized"); QTEST(workspace()->clientArea(MaximizeArea, 1, 1), "screen1Maximized"); QTEST(workspace()->clientArea(WorkArea, 0, 1), "workArea"); + + // delete all surfaces + for (auto it = clients.begin(); it != clients.end(); it++) { + QSignalSpy destroyedSpy(it.value(), &QObject::destroyed); + QVERIFY(destroyedSpy.isValid()); + delete it.key(); + QVERIFY(destroyedSpy.wait()); + } } void StrutsTest::testMoveWaylandPanel() { // this test verifies that repositioning a Wayland panel updates the client area using namespace KWayland::Client; const QRect windowGeometry(0, 1000, 1280, 24); - QScopedPointer surface(m_compositor->createSurface()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); Q_UNUSED(shellSurface) QScopedPointer plasmaSurface(m_plasmaShell->createSurface(surface.data())); plasmaSurface->setPosition(windowGeometry.topLeft()); plasmaSurface->setRole(PlasmaShellSurface::Role::Panel); QSignalSpy windowCreatedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(windowCreatedSpy.isValid()); // map the window - QImage img(windowGeometry.size(), QImage::Format_RGB32); - img.fill(Qt::red); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(QPoint(0, 0), windowGeometry.size())); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), windowGeometry.size(), Qt::red, QImage::Format_RGB32); QVERIFY(windowCreatedSpy.wait()); QCOMPARE(windowCreatedSpy.count(), 1); auto c = windowCreatedSpy.first().first().value(); QVERIFY(c); QVERIFY(!c->isActive()); QCOMPARE(c->geometry(), windowGeometry); QVERIFY(c->isDock()); QVERIFY(c->hasStrut()); windowCreatedSpy.clear(); QCOMPARE(workspace()->clientArea(PlacementArea, 0, 1), QRect(0, 0, 1280, 1000)); QCOMPARE(workspace()->clientArea(MaximizeArea, 0, 1), QRect(0, 0, 1280, 1000)); QCOMPARE(workspace()->clientArea(PlacementArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(WorkArea, 0, 1), QRect(0, 0, 2560, 1000)); QSignalSpy geometryChangedSpy(c, &ShellClient::geometryShapeChanged); QVERIFY(geometryChangedSpy.isValid()); plasmaSurface->setPosition(QPoint(1280, 1000)); QVERIFY(geometryChangedSpy.wait()); QCOMPARE(c->geometry(), QRect(1280, 1000, 1280, 24)); QCOMPARE(workspace()->clientArea(PlacementArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(PlacementArea, 1, 1), QRect(1280, 0, 1280, 1000)); QCOMPARE(workspace()->clientArea(MaximizeArea, 1, 1), QRect(1280, 0, 1280, 1000)); QCOMPARE(workspace()->clientArea(WorkArea, 0, 1), QRect(0, 0, 2560, 1000)); } void StrutsTest::testWaylandMobilePanel() { using namespace KWayland::Client; //First enable maxmizing policy KConfigGroup group = kwinApp()->config()->group("Windows"); group.writeEntry("Placement", "Maximizing"); group.sync(); workspace()->slotReconfigure(); // create first top panel const QRect windowGeometry(0, 0, 1280, 60); - QScopedPointer surface(m_compositor->createSurface()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); Q_UNUSED(shellSurface) QScopedPointer plasmaSurface(m_plasmaShell->createSurface(surface.data())); plasmaSurface->setPosition(windowGeometry.topLeft()); plasmaSurface->setRole(PlasmaShellSurface::Role::Panel); QSignalSpy windowCreatedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(windowCreatedSpy.isValid()); // map the first panel - QImage img(windowGeometry.size(), QImage::Format_RGB32); - img.fill(Qt::red); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(QPoint(0, 0), windowGeometry.size())); - surface->commit(Surface::CommitFlag::None); + Test::render(surface.data(), windowGeometry.size(), Qt::red, QImage::Format_RGB32); QVERIFY(windowCreatedSpy.wait()); QCOMPARE(windowCreatedSpy.count(), 1); auto c = windowCreatedSpy.first().first().value(); QVERIFY(c); QVERIFY(!c->isActive()); QCOMPARE(c->geometry(), windowGeometry); QVERIFY(c->isDock()); QVERIFY(c->hasStrut()); windowCreatedSpy.clear(); QCOMPARE(workspace()->clientArea(PlacementArea, 0, 1), QRect(0, 60, 1280, 964)); QCOMPARE(workspace()->clientArea(MaximizeArea, 0, 1), QRect(0, 60, 1280, 964)); QCOMPARE(workspace()->clientArea(PlacementArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(WorkArea, 0, 1), QRect(0, 60, 2560, 964)); // create another bottom panel const QRect windowGeometry2(0, 874, 1280, 150); - QScopedPointer surface2(m_compositor->createSurface()); - QScopedPointer shellSurface2(m_shell->createSurface(surface2.data())); + QScopedPointer surface2(Test::createSurface()); + QScopedPointer shellSurface2(Test::createShellSurface(surface2.data())); Q_UNUSED(shellSurface2) QScopedPointer plasmaSurface2(m_plasmaShell->createSurface(surface2.data())); plasmaSurface2->setPosition(windowGeometry2.topLeft()); plasmaSurface2->setRole(PlasmaShellSurface::Role::Panel); - QImage img2(windowGeometry2.size(), QImage::Format_RGB32); - img2.fill(Qt::blue); - surface2->attachBuffer(m_shm->createBuffer(img2)); - surface2->damage(QRect(QPoint(0, 0), windowGeometry2.size())); - surface2->commit(Surface::CommitFlag::None); + Test::render(surface2.data(), windowGeometry2.size(), Qt::blue, QImage::Format_RGB32); QVERIFY(windowCreatedSpy.wait()); QCOMPARE(windowCreatedSpy.count(), 1); auto c1 = windowCreatedSpy.first().first().value(); QVERIFY(c1); QVERIFY(!c1->isActive()); QCOMPARE(c1->geometry(), windowGeometry2); QVERIFY(c1->isDock()); QVERIFY(c1->hasStrut()); windowCreatedSpy.clear(); QCOMPARE(workspace()->clientArea(PlacementArea, 0, 1), QRect(0, 60, 1280, 814)); QCOMPARE(workspace()->clientArea(MaximizeArea, 0, 1), QRect(0, 60, 1280, 814)); QCOMPARE(workspace()->clientArea(PlacementArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(WorkArea, 0, 1), QRect(0, 60, 2560, 814)); } void StrutsTest::testX11Struts_data() { QTest::addColumn("windowGeometry"); QTest::addColumn("leftStrut"); QTest::addColumn("rightStrut"); QTest::addColumn("topStrut"); QTest::addColumn("bottomStrut"); QTest::addColumn("leftStrutStart"); QTest::addColumn("leftStrutEnd"); QTest::addColumn("rightStrutStart"); QTest::addColumn("rightStrutEnd"); QTest::addColumn("topStrutStart"); QTest::addColumn("topStrutEnd"); QTest::addColumn("bottomStrutStart"); QTest::addColumn("bottomStrutEnd"); QTest::addColumn("screen0Maximized"); QTest::addColumn("screen1Maximized"); QTest::addColumn("workArea"); QTest::newRow("bottom panel/no strut") << QRect(0, 980, 1280, 44) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("bottom panel/strut") << QRect(0, 980, 1280, 44) << 0 << 0 << 0 << 44 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 1279 << QRect(0, 0, 1280, 980) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 980); QTest::newRow("top panel/no strut") << QRect(0, 0, 1280, 44) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("top panel/strut") << QRect(0, 0, 1280, 44) << 0 << 0 << 44 << 0 << 0 << 0 << 0 << 0 << 0 << 1279 << 0 << 0 << QRect(0, 44, 1280, 980) << QRect(1280, 0, 1280, 1024) << QRect(0, 44, 2560, 980); QTest::newRow("left panel/no strut") << QRect(0, 0, 60, 1024) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("left panel/strut") << QRect(0, 0, 60, 1024) << 60 << 0 << 0 << 0 << 0 << 1023 << 0 << 0 << 0 << 0 << 0 << 0 << QRect(60, 0, 1220, 1024) << QRect(1280, 0, 1280, 1024) << QRect(60, 0, 2500, 1024); QTest::newRow("right panel/no strut") << QRect(0, 1220, 60, 1024) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("right panel/strut") << QRect(0, 1220, 60, 1024) << 0 << 1340 << 0 << 0 << 0 << 0 << 0 << 1023 << 0 << 0 << 0 << 0 << QRect(0, 0, 1220, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); // second screen QTest::newRow("bottom panel 1/no strut") << QRect(1280, 980, 1280, 44) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("bottom panel 1/strut") << QRect(1280, 980, 1280, 44) << 0 << 0 << 0 << 44 << 0 << 0 << 0 << 0 << 0 << 0 << 1280 << 2559 << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 980) << QRect(0, 0, 2560, 980); QTest::newRow("top panel 1/no strut") << QRect(1280, 0, 1280, 44) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("top panel 1 /strut") << QRect(1280, 0, 1280, 44) << 0 << 0 << 44 << 0 << 0 << 0 << 0 << 0 << 1280 << 2559 << 0 << 0 << QRect(0, 0, 1280, 1024) << QRect(1280, 44, 1280, 980) << QRect(0, 44, 2560, 980); QTest::newRow("left panel 1/no strut") << QRect(1280, 0, 60, 1024) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("left panel 1/strut") << QRect(1280, 0, 60, 1024) << 1340 << 0 << 0 << 0 << 0 << 1023 << 0 << 0 << 0 << 0 << 0 << 0 << QRect(0, 0, 1280, 1024) << QRect(1340, 0, 1220, 1024) << QRect(0, 0, 2560, 1024); // invalid struts QTest::newRow("bottom panel/ invalid strut") << QRect(0, 980, 1280, 44) << 1280 << 0 << 0 << 44 << 980 << 1024 << 0 << 0 << 0 << 0 << 0 << 1279 << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("top panel/ invalid strut") << QRect(0, 0, 1280, 44) << 1280 << 0 << 44 << 0 << 0 << 44 << 0 << 0 << 0 << 1279 << 0 << 0 << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); QTest::newRow("top panel/invalid strut 2") << QRect(0, 0, 1280, 44) << 0 << 0 << 1024 << 0 << 0 << 0 << 0 << 0 << 0 << 1279 << 0 << 0 << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024) << QRect(0, 0, 2560, 1024); } struct XcbConnectionDeleter { static inline void cleanup(xcb_connection_t *pointer) { xcb_disconnect(pointer); } }; void StrutsTest::testX11Struts() { // this test verifies that struts are applied correctly for X11 windows // no, struts yet // first screen QCOMPARE(workspace()->clientArea(PlacementArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MovementArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeFullArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(FullScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(ScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); // second screen QCOMPARE(workspace()->clientArea(PlacementArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MovementArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeFullArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(FullScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(ScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); // combined QCOMPARE(workspace()->clientArea(WorkArea, 0, 1), QRect(0, 0, 2560, 1024)); QCOMPARE(workspace()->clientArea(FullArea, 0, 1), QRect(0, 0, 2560, 1024)); // create an xcb window QScopedPointer c(xcb_connect(nullptr, nullptr)); QVERIFY(!xcb_connection_has_error(c.data())); xcb_window_t w = xcb_generate_id(c.data()); QFETCH(QRect, windowGeometry); xcb_create_window(c.data(), XCB_COPY_FROM_PARENT, w, rootWindow(), windowGeometry.x(), windowGeometry.y(), windowGeometry.width(), windowGeometry.height(), 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); xcb_size_hints_t hints; memset(&hints, 0, sizeof(hints)); xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); xcb_icccm_set_wm_normal_hints(c.data(), w, &hints); NETWinInfo info(c.data(), w, rootWindow(), NET::WMAllProperties, NET::WM2AllProperties); info.setWindowType(NET::Dock); // set the extended strut QFETCH(int, leftStrut); QFETCH(int, rightStrut); QFETCH(int, topStrut); QFETCH(int, bottomStrut); QFETCH(int, leftStrutStart); QFETCH(int, leftStrutEnd); QFETCH(int, rightStrutStart); QFETCH(int, rightStrutEnd); QFETCH(int, topStrutStart); QFETCH(int, topStrutEnd); QFETCH(int, bottomStrutStart); QFETCH(int, bottomStrutEnd); NETExtendedStrut strut; strut.left_start = leftStrutStart; strut.left_end = leftStrutEnd; strut.left_width = leftStrut; strut.right_start = rightStrutStart; strut.right_end = rightStrutEnd; strut.right_width = rightStrut; strut.top_start = topStrutStart; strut.top_end = topStrutEnd; strut.top_width = topStrut; strut.bottom_start = bottomStrutStart; strut.bottom_end = bottomStrutEnd; strut.bottom_width = bottomStrut; info.setExtendedStrut(strut); xcb_map_window(c.data(), w); xcb_flush(c.data()); // we should get a client for it QSignalSpy windowCreatedSpy(workspace(), &Workspace::clientAdded); QVERIFY(windowCreatedSpy.isValid()); QVERIFY(windowCreatedSpy.wait()); Client *client = windowCreatedSpy.first().first().value(); QVERIFY(client); QCOMPARE(client->window(), w); QVERIFY(!client->isDecorated()); QCOMPARE(client->windowType(), NET::Dock); QCOMPARE(client->geometry(), windowGeometry); // this should have affected the client area // some props are independent of struts - those first // screen 0 QCOMPARE(workspace()->clientArea(MovementArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeFullArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(FullScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(ScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); // screen 1 QCOMPARE(workspace()->clientArea(MovementArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeFullArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(FullScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(ScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); // combined QCOMPARE(workspace()->clientArea(FullArea, 0, 1), QRect(0, 0, 2560, 1024)); // now verify the actual updated client areas QTEST(workspace()->clientArea(PlacementArea, 0, 1), "screen0Maximized"); QTEST(workspace()->clientArea(MaximizeArea, 0, 1), "screen0Maximized"); QTEST(workspace()->clientArea(PlacementArea, 1, 1), "screen1Maximized"); QTEST(workspace()->clientArea(MaximizeArea, 1, 1), "screen1Maximized"); QTEST(workspace()->clientArea(WorkArea, 0, 1), "workArea"); // and destroy the window again xcb_unmap_window(c.data(), w); xcb_destroy_window(c.data(), w); xcb_flush(c.data()); c.reset(); QSignalSpy windowClosedSpy(client, &Client::windowClosed); QVERIFY(windowClosedSpy.isValid()); QVERIFY(windowClosedSpy.wait()); // now struts should be removed again QCOMPARE(workspace()->clientArea(PlacementArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MovementArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeFullArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(FullScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(ScreenArea, 0, 1), QRect(0, 0, 1280, 1024)); // second screen QCOMPARE(workspace()->clientArea(PlacementArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MovementArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(MaximizeFullArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(FullScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); QCOMPARE(workspace()->clientArea(ScreenArea, 1, 1), QRect(1280, 0, 1280, 1024)); // combined QCOMPARE(workspace()->clientArea(WorkArea, 0, 1), QRect(0, 0, 2560, 1024)); QCOMPARE(workspace()->clientArea(FullArea, 0, 1), QRect(0, 0, 2560, 1024)); } void StrutsTest::test363804() { // this test verifies the condition described in BUG 363804 // two screens in a vertical setup, aligned to right border with panel on the bottom screen const QVector geometries{QRect(0, 0, 1920, 1080), QRect(554, 1080, 1366, 768)}; QMetaObject::invokeMethod(kwinApp()->platform(), "outputGeometriesChanged", Qt::DirectConnection, Q_ARG(QVector, geometries)); QCOMPARE(screens()->geometry(0), geometries.at(0)); QCOMPARE(screens()->geometry(1), geometries.at(1)); QCOMPARE(screens()->geometry(), QRect(0, 0, 1920, 1848)); // create an xcb window QScopedPointer c(xcb_connect(nullptr, nullptr)); QVERIFY(!xcb_connection_has_error(c.data())); xcb_window_t w = xcb_generate_id(c.data()); const QRect windowGeometry(554, 1812, 1366, 36); xcb_create_window(c.data(), XCB_COPY_FROM_PARENT, w, rootWindow(), windowGeometry.x(), windowGeometry.y(), windowGeometry.width(), windowGeometry.height(), 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); xcb_size_hints_t hints; memset(&hints, 0, sizeof(hints)); xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); xcb_icccm_set_wm_normal_hints(c.data(), w, &hints); NETWinInfo info(c.data(), w, rootWindow(), NET::WMAllProperties, NET::WM2AllProperties); info.setWindowType(NET::Dock); NETExtendedStrut strut; strut.left_start = 0; strut.left_end = 0; strut.left_width = 0; strut.right_start = 0; strut.right_end = 0; strut.right_width = 0; strut.top_start = 0; strut.top_end = 0; strut.top_width = 0; strut.bottom_start = 554; strut.bottom_end = 1919; strut.bottom_width = 36; info.setExtendedStrut(strut); xcb_map_window(c.data(), w); xcb_flush(c.data()); // we should get a client for it QSignalSpy windowCreatedSpy(workspace(), &Workspace::clientAdded); QVERIFY(windowCreatedSpy.isValid()); QVERIFY(windowCreatedSpy.wait()); Client *client = windowCreatedSpy.first().first().value(); QVERIFY(client); QCOMPARE(client->window(), w); QVERIFY(!client->isDecorated()); QCOMPARE(client->windowType(), NET::Dock); QCOMPARE(client->geometry(), windowGeometry); // now verify the actual updated client areas QCOMPARE(workspace()->clientArea(PlacementArea, 0, 1), geometries.at(0)); QCOMPARE(workspace()->clientArea(MaximizeArea, 0, 1), geometries.at(0)); QCOMPARE(workspace()->clientArea(PlacementArea, 1, 1), QRect(554, 1080, 1366, 732)); QCOMPARE(workspace()->clientArea(MaximizeArea, 1, 1), QRect(554, 1080, 1366, 732)); QCOMPARE(workspace()->clientArea(WorkArea, 0, 1), QRect(0, 0, 1920, 1812)); // and destroy the window again xcb_unmap_window(c.data(), w); xcb_destroy_window(c.data(), w); xcb_flush(c.data()); c.reset(); QSignalSpy windowClosedSpy(client, &Client::windowClosed); QVERIFY(windowClosedSpy.isValid()); QVERIFY(windowClosedSpy.wait()); } } WAYLANDTEST_MAIN(KWin::StrutsTest) #include "struts_test.moc" diff --git a/autotests/integration/test_helpers.cpp b/autotests/integration/test_helpers.cpp new file mode 100644 index 000000000..465f150ba --- /dev/null +++ b/autotests/integration/test_helpers.cpp @@ -0,0 +1,281 @@ +/******************************************************************** +KWin - the KDE window manager +This file is part of the KDE project. + +Copyright (C) 2015 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "kwin_wayland_test.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace KWayland::Client; + +namespace KWin +{ +namespace Test +{ + +static struct { + ConnectionThread *connection = nullptr; + EventQueue *queue = nullptr; + Compositor *compositor = nullptr; + ServerSideDecorationManager *decoration = nullptr; + Shell *shell = nullptr; + ShmPool *shm = nullptr; + Seat *seat = nullptr; + PlasmaShell *plasmaShell = nullptr; + PlasmaWindowManagement *windowManagement = nullptr; + QThread *thread = nullptr; +} s_waylandConnection; + +bool setupWaylandConnection(const QString &socketName, AdditionalWaylandInterfaces flags) +{ + if (s_waylandConnection.connection) { + return false; + } + // setup connection + s_waylandConnection.connection = new ConnectionThread; + QSignalSpy connectedSpy(s_waylandConnection.connection, &ConnectionThread::connected); + if (!connectedSpy.isValid()) { + return false; + } + s_waylandConnection.connection->setSocketName(socketName); + + s_waylandConnection.thread = new QThread(kwinApp()); + s_waylandConnection.connection->moveToThread(s_waylandConnection.thread); + s_waylandConnection.thread->start(); + + s_waylandConnection.connection->initConnection(); + if (!connectedSpy.wait()) { + return false; + } + + s_waylandConnection.queue = new EventQueue; + s_waylandConnection.queue->setup(s_waylandConnection.connection); + if (!s_waylandConnection.queue->isValid()) { + return false; + } + + Registry registry; + registry.setEventQueue(s_waylandConnection.queue); + QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); + if (!allAnnounced.isValid()) { + return false; + } + registry.create(s_waylandConnection.connection); + if (!registry.isValid()) { + return false; + } + registry.setup(); + if (!allAnnounced.wait()) { + return false; + } + + s_waylandConnection.compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version); + if (!s_waylandConnection.compositor->isValid()) { + return false; + } + s_waylandConnection.shm = registry.createShmPool(registry.interface(Registry::Interface::Shm).name, registry.interface(Registry::Interface::Shm).version); + if (!s_waylandConnection.shm->isValid()) { + return false; + } + s_waylandConnection.shell = registry.createShell(registry.interface(Registry::Interface::Shell).name, registry.interface(Registry::Interface::Shell).version); + if (!s_waylandConnection.shell->isValid()) { + return false; + } + if (flags.testFlag(AdditionalWaylandInterface::Seat)) { + s_waylandConnection.seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version); + if (!s_waylandConnection.seat->isValid()) { + return false; + } + } + if (flags.testFlag(AdditionalWaylandInterface::Decoration)) { + s_waylandConnection.decoration = registry.createServerSideDecorationManager(registry.interface(Registry::Interface::ServerSideDecorationManager).name, + registry.interface(Registry::Interface::ServerSideDecorationManager).version); + if (!s_waylandConnection.decoration->isValid()) { + return false; + } + } + if (flags.testFlag(AdditionalWaylandInterface::PlasmaShell)) { + s_waylandConnection.plasmaShell = registry.createPlasmaShell(registry.interface(Registry::Interface::PlasmaShell).name, + registry.interface(Registry::Interface::PlasmaShell).version); + if (!s_waylandConnection.plasmaShell->isValid()) { + return false; + } + } + if (flags.testFlag(AdditionalWaylandInterface::WindowManagement)) { + s_waylandConnection.windowManagement = registry.createPlasmaWindowManagement(registry.interface(Registry::Interface::PlasmaWindowManagement).name, + registry.interface(Registry::Interface::PlasmaWindowManagement).version); + if (!s_waylandConnection.windowManagement->isValid()) { + return false; + } + } + + return true; +} + +void destroyWaylandConnection() +{ + delete s_waylandConnection.compositor; + s_waylandConnection.compositor = nullptr; + delete s_waylandConnection.windowManagement; + s_waylandConnection.windowManagement = nullptr; + delete s_waylandConnection.plasmaShell; + s_waylandConnection.plasmaShell = nullptr; + delete s_waylandConnection.decoration; + s_waylandConnection.decoration = nullptr; + delete s_waylandConnection.decoration; + s_waylandConnection.decoration = nullptr; + delete s_waylandConnection.seat; + s_waylandConnection.seat = nullptr; + delete s_waylandConnection.shell; + s_waylandConnection.shell = nullptr; + delete s_waylandConnection.shm; + s_waylandConnection.shm = nullptr; + delete s_waylandConnection.queue; + s_waylandConnection.queue = nullptr; + if (s_waylandConnection.thread) { + QSignalSpy spy(s_waylandConnection.connection, &QObject::destroyed); + s_waylandConnection.connection->deleteLater(); + QVERIFY(spy.wait()); + s_waylandConnection.thread->quit(); + s_waylandConnection.thread->wait(); + delete s_waylandConnection.thread; + s_waylandConnection.thread = nullptr; + s_waylandConnection.connection = nullptr; + } +} + +ConnectionThread *waylandConnection() +{ + return s_waylandConnection.connection; +} + +Compositor *waylandCompositor() +{ + return s_waylandConnection.compositor; +} + +Shell *waylandShell() +{ + return s_waylandConnection.shell; +} + +ShmPool *waylandShmPool() +{ + return s_waylandConnection.shm; +} + +Seat *waylandSeat() +{ + return s_waylandConnection.seat; +} + +ServerSideDecorationManager *waylandServerSideDecoration() +{ + return s_waylandConnection.decoration; +} + +PlasmaShell *waylandPlasmaShell() +{ + return s_waylandConnection.plasmaShell; +} + +PlasmaWindowManagement *waylandWindowManagement() +{ + return s_waylandConnection.windowManagement; +} + +bool waitForWaylandPointer() +{ + if (!s_waylandConnection.seat) { + return false; + } + QSignalSpy hasPointerSpy(s_waylandConnection.seat, &Seat::hasPointerChanged); + if (!hasPointerSpy.isValid()) { + return false; + } + return hasPointerSpy.wait(); +} + +bool waitForWaylandTouch() +{ + if (!s_waylandConnection.seat) { + return false; + } + QSignalSpy hasTouchSpy(s_waylandConnection.seat, &Seat::hasTouchChanged); + if (!hasTouchSpy.isValid()) { + return false; + } + return hasTouchSpy.wait(); +} + +void render(Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format) +{ + QImage img(size, format); + img.fill(color); + surface->attachBuffer(s_waylandConnection.shm->createBuffer(img)); + surface->damage(QRect(QPoint(0, 0), size)); + surface->commit(Surface::CommitFlag::None); +} + +void flushWaylandConnection() +{ + if (s_waylandConnection.connection) { + s_waylandConnection.connection->flush(); + } +} + +Surface *createSurface(QObject *parent) +{ + if (!s_waylandConnection.compositor) { + return nullptr; + } + auto s = s_waylandConnection.compositor->createSurface(parent); + if (!s->isValid()) { + delete s; + return nullptr; + } + return s; +} + +ShellSurface *createShellSurface(Surface *surface, QObject *parent) +{ + if (!s_waylandConnection.shell) { + return nullptr; + } + auto s = s_waylandConnection.shell->createSurface(surface, parent); + if (!s->isValid()) { + delete s; + return nullptr; + } + return s; +} + +} +} diff --git a/autotests/integration/touch_input_test.cpp b/autotests/integration/touch_input_test.cpp index f8d20751a..66ead44bf 100644 --- a/autotests/integration/touch_input_test.cpp +++ b/autotests/integration/touch_input_test.cpp @@ -1,323 +1,244 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "cursor.h" #include "shell_client.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include #include -#include -#include #include #include -#include #include #include namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_touch_input-0"); class TouchInputTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testMultipleTouchPoints(); void testCancel(); void testTouchMouseAction(); private: AbstractClient *showWindow(); - KWayland::Client::ConnectionThread *m_connection = nullptr; - KWayland::Client::Compositor *m_compositor = nullptr; - KWayland::Client::Seat *m_seat = nullptr; - KWayland::Client::ShmPool *m_shm = nullptr; - KWayland::Client::Shell *m_shell = nullptr; KWayland::Client::Touch *m_touch = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; void TouchInputTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); waylandServer()->initWorkspace(); } void TouchInputTest::init() { using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy seatSpy(®istry, &Registry::seatAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - QVERIFY(seatSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - QVERIFY(!seatSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); - m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); - QVERIFY(m_seat->isValid()); - QSignalSpy hasTouchSpy(m_seat, &Seat::hasTouchChanged); - QVERIFY(hasTouchSpy.isValid()); - QVERIFY(hasTouchSpy.wait()); - m_touch = m_seat->createTouch(m_seat); + QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::Seat)); + QVERIFY(Test::waitForWaylandTouch()); + m_touch = Test::waylandSeat()->createTouch(Test::waylandSeat()); QVERIFY(m_touch); QVERIFY(m_touch->isValid()); screens()->setCurrent(0); Cursor::setPos(QPoint(1280, 512)); } void TouchInputTest::cleanup() { - delete m_compositor; - m_compositor = nullptr; delete m_touch; m_touch = nullptr; - delete m_seat; - m_seat = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_connection->deleteLater(); - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - m_connection = nullptr; - } + Test::destroyWaylandConnection(); } AbstractClient *TouchInputTest::showWindow() { using namespace KWayland::Client; #define VERIFY(statement) \ if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\ return nullptr; #define COMPARE(actual, expected) \ if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ return nullptr; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); VERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); + Surface *surface = Test::createSurface(Test::waylandCompositor()); VERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); VERIFY(shellSurface); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface, QSize(100, 50), Qt::blue); - m_connection->flush(); + Test::flushWaylandConnection(); VERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); VERIFY(c); COMPARE(clientAddedSpy.first().first().value(), c); #undef VERIFY #undef COMPARE return c; } void TouchInputTest::testMultipleTouchPoints() { using namespace KWayland::Client; AbstractClient *c = showWindow(); c->move(100, 100); QVERIFY(c); QSignalSpy sequenceStartedSpy(m_touch, &Touch::sequenceStarted); QVERIFY(sequenceStartedSpy.isValid()); QSignalSpy pointAddedSpy(m_touch, &Touch::pointAdded); QVERIFY(pointAddedSpy.isValid()); QSignalSpy pointMovedSpy(m_touch, &Touch::pointMoved); QVERIFY(pointMovedSpy.isValid()); QSignalSpy pointRemovedSpy(m_touch, &Touch::pointRemoved); QVERIFY(pointRemovedSpy.isValid()); QSignalSpy endedSpy(m_touch, &Touch::sequenceEnded); QVERIFY(endedSpy.isValid()); quint32 timestamp = 1; kwinApp()->platform()->touchDown(1, QPointF(125, 125), timestamp++); QVERIFY(sequenceStartedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(m_touch->sequence().count(), 1); QCOMPARE(m_touch->sequence().first()->isDown(), true); QCOMPARE(m_touch->sequence().first()->position(), QPointF(25, 25)); QCOMPARE(pointAddedSpy.count(), 0); QCOMPARE(pointMovedSpy.count(), 0); // a point outside the window kwinApp()->platform()->touchDown(2, QPointF(0, 0), timestamp++); QVERIFY(pointAddedSpy.wait()); QCOMPARE(pointAddedSpy.count(), 1); QCOMPARE(m_touch->sequence().count(), 2); QCOMPARE(m_touch->sequence().at(1)->isDown(), true); QCOMPARE(m_touch->sequence().at(1)->position(), QPointF(-100, -100)); QCOMPARE(pointMovedSpy.count(), 0); // let's move that one kwinApp()->platform()->touchMotion(2, QPointF(100, 100), timestamp++); QVERIFY(pointMovedSpy.wait()); QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(m_touch->sequence().count(), 2); QCOMPARE(m_touch->sequence().at(1)->isDown(), true); QCOMPARE(m_touch->sequence().at(1)->position(), QPointF(0, 0)); kwinApp()->platform()->touchUp(1, timestamp++); QVERIFY(pointRemovedSpy.wait()); QCOMPARE(pointRemovedSpy.count(), 1); QCOMPARE(m_touch->sequence().count(), 2); QCOMPARE(m_touch->sequence().first()->isDown(), false); QCOMPARE(endedSpy.count(), 0); kwinApp()->platform()->touchUp(2, timestamp++); QVERIFY(pointRemovedSpy.wait()); QCOMPARE(pointRemovedSpy.count(), 2); QCOMPARE(m_touch->sequence().count(), 2); QCOMPARE(m_touch->sequence().first()->isDown(), false); QCOMPARE(m_touch->sequence().at(1)->isDown(), false); QCOMPARE(endedSpy.count(), 1); } void TouchInputTest::testCancel() { using namespace KWayland::Client; AbstractClient *c = showWindow(); c->move(100, 100); QVERIFY(c); QSignalSpy sequenceStartedSpy(m_touch, &Touch::sequenceStarted); QVERIFY(sequenceStartedSpy.isValid()); QSignalSpy cancelSpy(m_touch, &Touch::sequenceCanceled); QVERIFY(cancelSpy.isValid()); QSignalSpy pointRemovedSpy(m_touch, &Touch::pointRemoved); QVERIFY(pointRemovedSpy.isValid()); quint32 timestamp = 1; kwinApp()->platform()->touchDown(1, QPointF(125, 125), timestamp++); QVERIFY(sequenceStartedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); // cancel kwinApp()->platform()->touchCancel(); QVERIFY(cancelSpy.wait()); QCOMPARE(cancelSpy.count(), 1); kwinApp()->platform()->touchUp(1, timestamp++); QVERIFY(!pointRemovedSpy.wait(100)); QCOMPARE(pointRemovedSpy.count(), 0); } void TouchInputTest::testTouchMouseAction() { // this test verifies that a touch down on an inactive client will activate it using namespace KWayland::Client; // create two windows AbstractClient *c1 = showWindow(); QVERIFY(c1); AbstractClient *c2 = showWindow(); QVERIFY(c2); QVERIFY(!c1->isActive()); QVERIFY(c2->isActive()); // also create a sequence started spy as the touch event should be passed through QSignalSpy sequenceStartedSpy(m_touch, &Touch::sequenceStarted); QVERIFY(sequenceStartedSpy.isValid()); quint32 timestamp = 1; kwinApp()->platform()->touchDown(1, c1->geometry().center(), timestamp++); QVERIFY(c1->isActive()); QVERIFY(sequenceStartedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); // cleanup kwinApp()->platform()->touchCancel(); } } WAYLANDTEST_MAIN(KWin::TouchInputTest) #include "touch_input_test.moc" diff --git a/autotests/integration/transient_no_input_test.cpp b/autotests/integration/transient_no_input_test.cpp index d6636058c..9760e4e63 100644 --- a/autotests/integration/transient_no_input_test.cpp +++ b/autotests/integration/transient_no_input_test.cpp @@ -1,191 +1,116 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "abstract_client.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include #include -#include -#include #include -#include #include namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_transient_no_input-0"); class TransientNoInputTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testTransientNoFocus(); - -private: - KWayland::Client::ConnectionThread *m_connection = nullptr; - KWayland::Client::Compositor *m_compositor = nullptr; - KWayland::Client::ShmPool *m_shm = nullptr; - KWayland::Client::Shell *m_shell = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; void TransientNoInputTest::initTestCase() { qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); } void TransientNoInputTest::init() { - using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); + QVERIFY(Test::setupWaylandConnection(s_socketName)); } void TransientNoInputTest::cleanup() { - delete m_compositor; - m_compositor = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - } - + Test::destroyWaylandConnection(); } void TransientNoInputTest::testTransientNoFocus() { using namespace KWayland::Client; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); QVERIFY(clientAddedSpy.isValid()); - QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); QVERIFY(!shellSurface.isNull()); // let's render - QImage img(QSize(100, 50), QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(0, 0, 100, 50)); - surface->commit(); + Test::render(surface.data(), QSize(100, 50), Qt::blue); - m_connection->flush(); + Test::flushWaylandConnection(); QVERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); QVERIFY(c); QCOMPARE(clientAddedSpy.first().first().value(), c); // let's create a transient with no input - QScopedPointer transientSurface(m_compositor->createSurface()); + QScopedPointer transientSurface(Test::createSurface()); QVERIFY(!transientSurface.isNull()); - QScopedPointer transientShellSurface(m_shell->createSurface(transientSurface.data())); + QScopedPointer transientShellSurface(Test::createShellSurface(transientSurface.data())); QVERIFY(!transientShellSurface.isNull()); transientShellSurface->setTransient(surface.data(), QPoint(10, 20), ShellSurface::TransientFlag::NoFocus); - m_connection->flush(); + Test::flushWaylandConnection(); // let's render - QImage img2(QSize(200, 20), QImage::Format_ARGB32); - img2.fill(Qt::red); - transientSurface->attachBuffer(m_shm->createBuffer(img2)); - transientSurface->damage(QRect(0, 0, 200, 20)); - transientSurface->commit(); - m_connection->flush(); + Test::render(transientSurface.data(), QSize(200, 20), Qt::red); + Test::flushWaylandConnection(); QVERIFY(clientAddedSpy.wait()); // get the latest ShellClient auto transientClient = clientAddedSpy.last().first().value(); QVERIFY(transientClient != c); QCOMPARE(transientClient->geometry(), QRect(c->x() + 10, c->y() + 20, 200, 20)); QVERIFY(transientClient->isTransient()); QCOMPARE(transientClient->transientPlacementHint(), QPoint(10, 20)); QVERIFY(!transientClient->wantsInput()); // workspace's active window should not have changed QCOMPARE(workspace()->activeClient(), c); } } WAYLANDTEST_MAIN(KWin::TransientNoInputTest) #include "transient_no_input_test.moc" diff --git a/autotests/integration/transient_placement.cpp b/autotests/integration/transient_placement.cpp index 0879a0ee1..79a2a7b88 100644 --- a/autotests/integration/transient_placement.cpp +++ b/autotests/integration/transient_placement.cpp @@ -1,314 +1,230 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" #include "platform.h" #include "abstract_client.h" #include "cursor.h" #include "screenedge.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "shell_client.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KWin { static const QString s_socketName = QStringLiteral("wayland_test_kwin_transient_placement-0"); class TransientPlacementTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testSimplePosition_data(); void testSimplePosition(); void testDecorationPosition_data(); void testDecorationPosition(); private: AbstractClient *showWindow(const QSize &size, bool decorated = false, KWayland::Client::Surface *parent = nullptr, const QPoint &offset = QPoint()); KWayland::Client::Surface *surfaceForClient(AbstractClient *c) const; - KWayland::Client::ConnectionThread *m_connection = nullptr; - KWayland::Client::Compositor *m_compositor = nullptr; - KWayland::Client::ServerSideDecorationManager *m_deco = nullptr; - KWayland::Client::Seat *m_seat = nullptr; - KWayland::Client::ShmPool *m_shm = nullptr; - KWayland::Client::Shell *m_shell = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread = nullptr; }; void TransientPlacementTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); setenv("QT_QPA_PLATFORM", "wayland", true); waylandServer()->initWorkspace(); } void TransientPlacementTest::init() { - using namespace KWayland::Client; - // setup connection - m_connection = new ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - Registry registry; - registry.setEventQueue(m_queue); - QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); - QSignalSpy shmSpy(®istry, &Registry::shmAnnounced); - QSignalSpy shellSpy(®istry, &Registry::shellAnnounced); - QSignalSpy seatSpy(®istry, &Registry::seatAnnounced); - QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); - QVERIFY(allAnnounced.isValid()); - QVERIFY(shmSpy.isValid()); - QVERIFY(shellSpy.isValid()); - QVERIFY(compositorSpy.isValid()); - QVERIFY(seatSpy.isValid()); - registry.create(m_connection->display()); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(allAnnounced.wait()); - QVERIFY(!compositorSpy.isEmpty()); - QVERIFY(!shmSpy.isEmpty()); - QVERIFY(!shellSpy.isEmpty()); - QVERIFY(!seatSpy.isEmpty()); - - m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); - QVERIFY(m_compositor->isValid()); - m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); - QVERIFY(m_shm->isValid()); - m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); - QVERIFY(m_shell->isValid()); - m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); - QVERIFY(m_seat->isValid()); - QSignalSpy hasPointerSpy(m_seat, &Seat::hasPointerChanged); - QVERIFY(hasPointerSpy.isValid()); - QVERIFY(hasPointerSpy.wait()); - - m_deco = registry.createServerSideDecorationManager(registry.interface(Registry::Interface::ServerSideDecorationManager).name, registry.interface(Registry::Interface::ServerSideDecorationManager).version, this); - QVERIFY(m_deco->isValid()); + QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::Decoration)); screens()->setCurrent(0); Cursor::setPos(QPoint(640, 512)); } void TransientPlacementTest::cleanup() { - delete m_deco; - m_deco = nullptr; - delete m_compositor; - m_compositor = nullptr; - delete m_seat; - m_seat = nullptr; - delete m_shm; - m_shm = nullptr; - delete m_shell; - m_shell = nullptr; - delete m_queue; - m_queue = nullptr; - if (m_thread) { - m_connection->deleteLater(); - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - m_connection = nullptr; - } + Test::destroyWaylandConnection(); } AbstractClient *TransientPlacementTest::showWindow(const QSize &size, bool decorated, KWayland::Client::Surface *parent, const QPoint &offset) { using namespace KWayland::Client; #define VERIFY(statement) \ if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\ return nullptr; #define COMPARE(actual, expected) \ if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ return nullptr; QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); VERIFY(clientAddedSpy.isValid()); - Surface *surface = m_compositor->createSurface(m_compositor); + Surface *surface = Test::createSurface(Test::waylandCompositor()); VERIFY(surface); - ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); VERIFY(shellSurface); if (parent) { shellSurface->setTransient(parent, offset); } if (decorated) { - auto deco = m_deco->create(surface, surface); + auto deco = Test::waylandServerSideDecoration()->create(surface, surface); QSignalSpy decoSpy(deco, &ServerSideDecoration::modeChanged); VERIFY(decoSpy.isValid()); VERIFY(decoSpy.wait()); deco->requestMode(ServerSideDecoration::Mode::Server); VERIFY(decoSpy.wait()); COMPARE(deco->mode(), ServerSideDecoration::Mode::Server); } // let's render - QImage img(size, QImage::Format_ARGB32); - img.fill(Qt::blue); - surface->attachBuffer(m_shm->createBuffer(img)); - surface->damage(QRect(QPoint(0, 0), size)); - surface->commit(Surface::CommitFlag::None); + Test::render(surface, size, Qt::blue); - m_connection->flush(); + Test::flushWaylandConnection(); VERIFY(clientAddedSpy.wait()); AbstractClient *c = workspace()->activeClient(); VERIFY(c); COMPARE(clientAddedSpy.first().first().value(), c); #undef VERIFY #undef COMPARE return c; } KWayland::Client::Surface *TransientPlacementTest::surfaceForClient(AbstractClient *c) const { const auto &surfaces = KWayland::Client::Surface::all(); auto it = std::find_if(surfaces.begin(), surfaces.end(), [c] (KWayland::Client::Surface *s) { return s->id() == c->surface()->id(); }); if (it != surfaces.end()) { return *it; } return nullptr; } void TransientPlacementTest::testSimplePosition_data() { QTest::addColumn("parentSize"); QTest::addColumn("parentPosition"); QTest::addColumn("transientSize"); QTest::addColumn("transientOffset"); QTest::addColumn("expectedGeometry"); QTest::newRow("0/0") << QSize(640, 512) << QPoint(0, 0) << QSize(10, 100) << QPoint(0, 0) << QRect(0, 0, 10, 100); QTest::newRow("bottomRight") << QSize(640, 512) << QPoint(0, 0) << QSize(10, 100) << QPoint(639, 511) << QRect(639, 511, 10, 100); QTest::newRow("offset") << QSize(640, 512) << QPoint(200, 300) << QSize(100, 10) << QPoint(320, 256) << QRect(520, 556, 100, 10); QTest::newRow("right border") << QSize(1280, 1024) << QPoint(0, 0) << QSize(10, 100) << QPoint(1279, 50) << QRect(1269, 50, 10, 100); QTest::newRow("bottom border") << QSize(1280, 1024) << QPoint(0, 0) << QSize(10, 100) << QPoint(512, 1020) << QRect(512, 920, 10, 100); QTest::newRow("bottom right") << QSize(1280, 1024) << QPoint(0, 0) << QSize(10, 100) << QPoint(1279, 1020) << QRect(1269, 920, 10, 100); QTest::newRow("top border") << QSize(1280, 1024) << QPoint(0, -100) << QSize(10, 100) << QPoint(512, 50) << QRect(512, 0, 10, 100); QTest::newRow("left border") << QSize(1280, 1024) << QPoint(-100, 0) << QSize(100, 10) << QPoint(50, 512) << QRect(0, 512, 100, 10); QTest::newRow("top left") << QSize(1280, 1024) << QPoint(-100, -100) << QSize(100, 100) << QPoint(50, 50) << QRect(0, 0, 100, 100); QTest::newRow("bottom left") << QSize(1280, 1024) << QPoint(-100, 0) << QSize(100, 100) << QPoint(50, 1000) << QRect(0, 900, 100, 100); } void TransientPlacementTest::testSimplePosition() { // this test verifies that the position of a transient window is taken from the passed position // there are no further constraints like window too large to fit screen, cascading transients, etc // some test cases also verify that the transient fits on the screen QFETCH(QSize, parentSize); AbstractClient *parent = showWindow(parentSize); QVERIFY(parent->clientPos().isNull()); QVERIFY(!parent->isDecorated()); QFETCH(QPoint, parentPosition); parent->move(parentPosition); QFETCH(QSize, transientSize); QFETCH(QPoint, transientOffset); AbstractClient *transient = showWindow(transientSize, false, surfaceForClient(parent), transientOffset); QVERIFY(transient); QVERIFY(!transient->isDecorated()); QVERIFY(transient->hasTransientPlacementHint()); QTEST(transient->geometry(), "expectedGeometry"); } void TransientPlacementTest::testDecorationPosition_data() { QTest::addColumn("parentSize"); QTest::addColumn("parentPosition"); QTest::addColumn("transientSize"); QTest::addColumn("transientOffset"); QTest::addColumn("expectedGeometry"); QTest::newRow("0/0") << QSize(640, 512) << QPoint(0, 0) << QSize(10, 100) << QPoint(0, 0) << QRect(0, 0, 10, 100); QTest::newRow("bottomRight") << QSize(640, 512) << QPoint(0, 0) << QSize(10, 100) << QPoint(639, 511) << QRect(639, 511, 10, 100); QTest::newRow("offset") << QSize(640, 512) << QPoint(200, 300) << QSize(100, 10) << QPoint(320, 256) << QRect(520, 556, 100, 10); } void TransientPlacementTest::testDecorationPosition() { // this test verifies that a transient window is correctly placed if the parent window has a // server side decoration QFETCH(QSize, parentSize); AbstractClient *parent = showWindow(parentSize, true); QVERIFY(!parent->clientPos().isNull()); QVERIFY(parent->isDecorated()); QFETCH(QPoint, parentPosition); parent->move(parentPosition); QFETCH(QSize, transientSize); QFETCH(QPoint, transientOffset); AbstractClient *transient = showWindow(transientSize, false, surfaceForClient(parent), transientOffset); QVERIFY(transient); QVERIFY(!transient->isDecorated()); QVERIFY(transient->hasTransientPlacementHint()); QFETCH(QRect, expectedGeometry); expectedGeometry.translate(parent->clientPos()); QCOMPARE(transient->geometry(), expectedGeometry); } } WAYLANDTEST_MAIN(KWin::TransientPlacementTest) #include "transient_placement.moc"