diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt index 4d59ab39ad..2dbf238575 100644 --- a/autotests/integration/CMakeLists.txt +++ b/autotests/integration/CMakeLists.txt @@ -119,8 +119,8 @@ integrationTest(WAYLAND_ONLY NAME testPlacement SRCS placement_test.cpp) integrationTest(WAYLAND_ONLY NAME testActivation SRCS activation_test.cpp) integrationTest(WAYLAND_ONLY NAME testInputMethod SRCS inputmethod_test.cpp) integrationTest(WAYLAND_ONLY NAME testScreens SRCS screens_test.cpp) -integrationTest(WAYLAND_ONLY NAME testOutputManagement SRCS outputmanagement_test.cpp) integrationTest(WAYLAND_ONLY NAME testScreenEdges SRCS screenedges_test.cpp) +integrationTest(WAYLAND_ONLY NAME testOutputChanges SRCS outputchanges_test.cpp) qt_add_dbus_interfaces(DBUS_SRCS ${CMAKE_BINARY_DIR}/src/org.kde.kwin.VirtualKeyboard.xml) integrationTest(WAYLAND_ONLY NAME testVirtualKeyboardDBus SRCS test_virtualkeyboard_dbus.cpp ${DBUS_SRCS}) diff --git a/autotests/integration/outputchanges_test.cpp b/autotests/integration/outputchanges_test.cpp new file mode 100644 index 0000000000..5d169c3ced --- /dev/null +++ b/autotests/integration/outputchanges_test.cpp @@ -0,0 +1,156 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "kwin_wayland_test.h" + +#include "abstract_client.h" +#include "abstract_output.h" +#include "cursor.h" +#include "platform.h" +#include "waylandoutputconfig.h" +#include "wayland_server.h" +#include "workspace.h" + +#include + +namespace KWin +{ + +static const QString s_socketName = QStringLiteral("wayland_test_output_changes-0"); + +class OutputChangesTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void init(); + void cleanup(); + + void testWindowSticksToOutputAfterOutputIsDisabled(); + void testWindowSticksToOutputAfterAnotherOutputIsDisabled(); + void testWindowSticksToOutputAfterOutputIsMoved(); +}; + +void OutputChangesTest::initTestCase() +{ + qRegisterMetaType(); + + QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); + QVERIFY(applicationStartedSpy.isValid()); + kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); + QVERIFY(waylandServer()->init(s_socketName)); + QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2)); + + kwinApp()->start(); + QVERIFY(applicationStartedSpy.wait()); + const auto outputs = kwinApp()->platform()->enabledOutputs(); + QCOMPARE(outputs.count(), 2); + QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024)); + QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024)); + Test::initWaylandWorkspace(); +} + +void OutputChangesTest::init() +{ + QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2)); + QVERIFY(Test::setupWaylandConnection()); + + workspace()->setActiveOutput(QPoint(640, 512)); + Cursors::self()->mouse()->setPos(QPoint(640, 512)); +} + +void OutputChangesTest::cleanup() +{ + Test::destroyWaylandConnection(); +} + +void OutputChangesTest::testWindowSticksToOutputAfterOutputIsDisabled() +{ + auto outputs = kwinApp()->platform()->outputs(); + + // Create a window. + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data())); + auto client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); + QVERIFY(client); + + // Move the window to some predefined position so the test is more robust. + client->move(QPoint(42, 67)); + QCOMPARE(client->frameGeometry(), QRect(42, 67, 100, 50)); + + // Disable the output where the window is on. + WaylandOutputConfig config; + { + auto changeSet = config.changeSet(static_cast(outputs[0])); + changeSet->enabled = false; + } + kwinApp()->platform()->applyOutputChanges(config); + + // The window will be sent to the second output, which is at (1280, 0). + QCOMPARE(client->frameGeometry(), QRect(1280 + 42, 0 + 67, 100, 50)); +} + +void OutputChangesTest::testWindowSticksToOutputAfterAnotherOutputIsDisabled() +{ + auto outputs = kwinApp()->platform()->outputs(); + + // Create a window. + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data())); + auto client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); + QVERIFY(client); + + // Move the window to the second output. + client->move(QPoint(1280 + 42, 67)); + QCOMPARE(client->frameGeometry(), QRect(1280 + 42, 67, 100, 50)); + + // Disable the first output. + WaylandOutputConfig config; + { + auto changeSet = config.changeSet(static_cast(outputs[0])); + changeSet->enabled = false; + } + { + auto changeSet = config.changeSet(static_cast(outputs[1])); + changeSet->pos = QPoint(0, 0); + } + kwinApp()->platform()->applyOutputChanges(config); + + // The position of the window relative to its output should remain the same. + QCOMPARE(client->frameGeometry(), QRect(42, 67, 100, 50)); +} + +void OutputChangesTest::testWindowSticksToOutputAfterOutputIsMoved() +{ + auto outputs = kwinApp()->platform()->outputs(); + + // Create a window. + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data())); + auto client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); + QVERIFY(client); + + // Move the window to some predefined position so the test is more robust. + client->move(QPoint(42, 67)); + QCOMPARE(client->frameGeometry(), QRect(42, 67, 100, 50)); + + // Disable the first output. + WaylandOutputConfig config; + { + auto changeSet = config.changeSet(static_cast(outputs[0])); + changeSet->pos = QPoint(-10, 20); + } + kwinApp()->platform()->applyOutputChanges(config); + + // The position of the window relative to its output should remain the same. + QCOMPARE(client->frameGeometry(), QRect(-10 + 42, 20 + 67, 100, 50)); +} + +} + +WAYLANDTEST_MAIN(KWin::OutputChangesTest) +#include "outputchanges_test.moc" diff --git a/autotests/integration/outputmanagement_test.cpp b/autotests/integration/outputmanagement_test.cpp deleted file mode 100644 index 2d7a18b12f..0000000000 --- a/autotests/integration/outputmanagement_test.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2020 Méven Car - - SPDX-License-Identifier: GPL-2.0-or-later -*/ -#include "kwin_wayland_test.h" -#include "abstract_client.h" -#include "abstract_wayland_output.h" -#include "deleted.h" -#include "platform.h" -#include "screens.h" -#include "wayland_server.h" -#include "workspace.h" - -#include -#include -#include - -#include -#include -#include - -#include - -using namespace KWin; -using namespace KWayland::Client; - -static const QString s_socketName = QStringLiteral("wayland_test_kwin_outputmanagement-0"); - -class TestOutputManagement : public QObject -{ - Q_OBJECT -private Q_SLOTS: - void initTestCase(); - void init(); - void cleanup(); - - void testOutputDeviceDisabled(); - void testOutputDeviceRemoved(); -}; - -void TestOutputManagement::initTestCase() -{ - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType("AbstractOutput *"); - qRegisterMetaType(); - - QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); - QVERIFY(applicationStartedSpy.isValid()); - kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); - QVERIFY(waylandServer()->init(s_socketName)); - QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2)); - - kwinApp()->start(); - QVERIFY(applicationStartedSpy.wait()); - const auto outputs = kwinApp()->platform()->enabledOutputs(); - QCOMPARE(outputs.count(), 2); - QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024)); - QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024)); - Test::initWaylandWorkspace(); -} - -void TestOutputManagement::init() -{ - QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::OutputManagementV2 | - Test::AdditionalWaylandInterface::OutputDeviceV2)); - - workspace()->setActiveOutput(QPoint(640, 512)); - //put mouse in the middle of screen one - KWin::Cursors::self()->mouse()->setPos(QPoint(640, 512)); -} - -void TestOutputManagement::cleanup() -{ - Test::destroyWaylandConnection(); -} - -void TestOutputManagement::testOutputDeviceDisabled() -{ - // This tests checks that OutputConfiguration::apply aka Platform::requestOutputsChange works as expected - // when disabling and enabling virtual OutputDevice - - QScopedPointer surface(Test::createSurface()); - QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data())); - auto size = QSize(200,200); - - QSignalSpy outputEnteredSpy(surface.data(), &KWayland::Client::Surface::outputEntered); - QSignalSpy outputLeftSpy(surface.data(), &KWayland::Client::Surface::outputLeft); - - QSignalSpy outputEnabledSpy(kwinApp()->platform(), &Platform::outputEnabled); - QSignalSpy outputDisabledSpy(kwinApp()->platform(), &Platform::outputDisabled); - - auto c = Test::renderAndWaitForShown(surface.data(), size, Qt::blue); - //move to be in the first screen - c->moveResize(QRect(QPoint(100,100), size)); - //we don't don't know where the compositor first placed this window, - //this might fire, it might not - outputEnteredSpy.wait(5); - outputEnteredSpy.clear(); - QCOMPARE(waylandServer()->display()->outputs().count(), 2); - - QCOMPARE(surface->outputs().count(), 1); - Output *firstOutput = surface->outputs().first(); - QCOMPARE(firstOutput->globalPosition(), QPoint(0,0)); - QSignalSpy modesChangedSpy(firstOutput, &Output::modeChanged); - - QSignalSpy screenChangedSpy(screens(), &KWin::Screens::changed); - - Test::WaylandOutputManagementV2 *outManagement = Test::waylandOutputManagementV2(); - - auto outputDevices = Test::waylandOutputDevicesV2(); - QCOMPARE(outputDevices.count(), 2); - - Test::WaylandOutputDeviceV2 *device = outputDevices.first(); - QCOMPARE(device->enabled(), true); - QSignalSpy outputDeviceEnabledChangedSpy(device, &Test::WaylandOutputDeviceV2::enabledChanged); - Test::WaylandOutputConfigurationV2 *config; - - // Disables an output - config = outManagement->createConfiguration(); - QSignalSpy configAppliedSpy(config, &Test::WaylandOutputConfigurationV2::applied); - config->enable(device->object(), false); - config->apply(); - QVERIFY(configAppliedSpy.wait()); - - QCOMPARE(outputDeviceEnabledChangedSpy.count(), 1); - QCOMPARE(device->enabled(), false); - QCOMPARE(screenChangedSpy.count(), 3); - QCOMPARE(outputLeftSpy.count(), 1); - QCOMPARE(outputEnteredSpy.count(), 1); // surface was moved to other screen - QCOMPARE(surface->outputs().count(), 1); - QCOMPARE(screens()->count(), 1); - QCOMPARE(modesChangedSpy.count(), 0); - QCOMPARE(outputEnabledSpy.count(), 0); - QCOMPARE(outputDisabledSpy.count(), 1); - - screenChangedSpy.clear(); - outputLeftSpy.clear(); - outputEnteredSpy.clear(); - outputDeviceEnabledChangedSpy.clear(); - outputEnabledSpy.clear(); - outputDisabledSpy.clear(); - - // Enable the disabled output - config = outManagement->createConfiguration(); - QSignalSpy configAppliedSpy2(config, &Test::WaylandOutputConfigurationV2::applied); - config->enable(device->object(), true); - config->apply(); - QVERIFY(configAppliedSpy2.wait()); - - QVERIFY(outputEnteredSpy.wait()); - - QCOMPARE(outputDeviceEnabledChangedSpy.count(), 1); - QCOMPARE(device->enabled(), true); - QCOMPARE(screenChangedSpy.count(), 3); - QCOMPARE(outputLeftSpy.count(), 1); - QCOMPARE(outputEnteredSpy.count(), 1); // surface moved back to first screen - QCOMPARE(surface->outputs().count(), 1); - QCOMPARE(screens()->count(), 2); - QCOMPARE(modesChangedSpy.count(), 0); - QCOMPARE(outputEnabledSpy.count(), 1); - QCOMPARE(outputDisabledSpy.count(), 0); -} - -void TestOutputManagement::testOutputDeviceRemoved() -{ - // This tests checks that OutputConfiguration::apply aka Platform::requestOutputsChange works as expected - // when removing a virtual OutputDevice - - QScopedPointer surface(Test::createSurface()); - QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data())); - auto size = QSize(200,200); - - QSignalSpy outputEnteredSpy(surface.data(), &KWayland::Client::Surface::outputEntered); - QSignalSpy outputLeftSpy(surface.data(), &KWayland::Client::Surface::outputLeft); - - QSignalSpy outputEnabledSpy(kwinApp()->platform(), &Platform::outputEnabled); - QSignalSpy outputDisabledSpy(kwinApp()->platform(), &Platform::outputDisabled); - QSignalSpy outputRemovedSpy(kwinApp()->platform(), &Platform::outputRemoved); - - auto c = Test::renderAndWaitForShown(surface.data(), size, Qt::blue); - //move to be in the first screen - c->move(QPoint(100,100)); - //we don't don't know where the compositor first placed this window, - //this might fire, it might not - outputEnteredSpy.wait(5); - outputEnteredSpy.clear(); - QCOMPARE(waylandServer()->display()->outputs().count(), 2); - - QCOMPARE(surface->outputs().count(), 1); - Output *firstOutput = surface->outputs().first(); - QCOMPARE(firstOutput->globalPosition(), QPoint(0,0)); - QSignalSpy modesChangedSpy(firstOutput, &Output::modeChanged); - - QSignalSpy screenChangedSpy(screens(), &KWin::Screens::changed); - - QCOMPARE(Test::waylandOutputDevicesV2().count(), 2); - - Test::WaylandOutputDeviceV2 *device = Test::waylandOutputDevicesV2().first(); - QCOMPARE(device->enabled(), true); - - QSignalSpy outputDeviceEnabledChangedSpy(device, &Test::WaylandOutputDeviceV2::enabledChanged); - - AbstractOutput *output = kwinApp()->platform()->outputs().first(); - // Removes an output - QMetaObject::invokeMethod(kwinApp()->platform(), "removeOutput", Qt::DirectConnection, Q_ARG(AbstractOutput *, output)); - - // Let the new state propagate - QVERIFY(outputEnteredSpy.wait()); - - QCOMPARE(waylandServer()->display()->outputs().count(), 1); - QCOMPARE(waylandServer()->display()->outputDevices().count(), 1); - - QCOMPARE(Test::waylandOutputDevicesV2().count(), 1); - QCOMPARE(outputDeviceEnabledChangedSpy.count(), 1); - QCOMPARE(device->enabled(), false); - QCOMPARE(outputLeftSpy.count(), 1); - QCOMPARE(outputEnteredSpy.count(), 1); // surface moved to the other screen - QCOMPARE(surface->outputs().count(), 1); - QCOMPARE(screens()->count(), 1); - QCOMPARE(screenChangedSpy.count(), 3); - QCOMPARE(modesChangedSpy.count(), 0); - QCOMPARE(outputEnabledSpy.count(), 0); - QCOMPARE(outputDisabledSpy.count(), 1); - QCOMPARE(outputRemovedSpy.count(), 1); -} - -WAYLANDTEST_MAIN(TestOutputManagement) -#include "outputmanagement_test.moc" diff --git a/src/platform.h b/src/platform.h index 078e7e02d9..e180d173f6 100644 --- a/src/platform.h +++ b/src/platform.h @@ -412,6 +412,11 @@ public: */ void setPrimaryOutput(AbstractOutput *primary); + /** + * Applies the output changes. Default implementation only sets values common between platforms + */ + virtual bool applyOutputChanges(const WaylandOutputConfig &config); + public Q_SLOTS: void pointerMotion(const QPointF &position, quint32 time); void pointerButtonPressed(quint32 button, quint32 time); @@ -523,11 +528,6 @@ protected: virtual void doShowCursor(); virtual void doSetSoftwareCursor(); - /** - * Applies the output changes. Default implementation only sets values common between platforms - */ - virtual bool applyOutputChanges(const WaylandOutputConfig &config); - private: void triggerCursorRepaint(); bool m_softwareCursor = false;