diff options
Diffstat (limited to 'tests/auto/client/output/tst_output.cpp')
-rw-r--r-- | tests/auto/client/output/tst_output.cpp | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/tests/auto/client/output/tst_output.cpp b/tests/auto/client/output/tst_output.cpp new file mode 100644 index 000000000..2129e167b --- /dev/null +++ b/tests/auto/client/output/tst_output.cpp @@ -0,0 +1,249 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "mockcompositor.h" +#include <QtGui/QScreen> +#include <QtGui/QRasterWindow> + +using namespace MockCompositor; + +class tst_output : public QObject, private DefaultCompositor +{ + Q_OBJECT +private slots: + void initTestCase() + { + m_config.autoConfigure = true; + m_config.autoEnter = false; + } + void cleanup() + { + QCOMPOSITOR_COMPARE(getAll<Output>().size(), 1); // Only the default output should be left + QTRY_COMPARE(QGuiApplication::screens().size(), 1); + QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); + } + void primaryScreen(); + void secondaryHiDpiScreen(); + void addScreenWithGeometryChange(); + void windowScreens(); + void removePrimaryScreen(); + void screenOrder(); + void removeAllScreens(); +}; + +void tst_output::primaryScreen() +{ + // Verify that the client has bound to the output global + QCOMPOSITOR_TRY_COMPARE(output()->resourceMap().size(), 1); + QTRY_VERIFY(QGuiApplication::primaryScreen()); + QScreen *screen = QGuiApplication::primaryScreen(); + QCOMPARE(screen->manufacturer(), "Make"); + QCOMPARE(screen->model(), "Model"); + QCOMPARE(screen->size(), QSize(1920, 1080)); + QCOMPARE(screen->refreshRate(), 60); + QCOMPARE(qRound(screen->physicalDotsPerInch()), 96 / screen->devicePixelRatio()); + QCOMPARE(screen->devicePixelRatio(), 1); + QCOMPARE(screen->logicalDotsPerInch(), 96); +} + +void tst_output::secondaryHiDpiScreen() +{ + exec([&] { + OutputData d; + d.position = {1920, 0}; // in global compositor space (not pixels) + d.mode.resolution = {800, 640}; + d.physicalSize = d.mode.physicalSizeForDpi(200); + d.scale = 2; + add<Output>(d); + }); + + // Verify that the client has bound to the output global + QCOMPOSITOR_TRY_VERIFY(output(1) && output(1)->resourceMap().size() == 1); + + QTRY_COMPARE(QGuiApplication::screens().size(), 2); + QScreen *screen = QGuiApplication::screens()[1]; + QCOMPARE(screen->refreshRate(), 60); + QCOMPARE(screen->devicePixelRatio(), 2); + QCOMPARE(screen->logicalDotsPerInch(), 96); + + // Dots currently means device pixels, not actual pixels (see QTBUG-62649) + QCOMPARE(qRound(screen->physicalDotsPerInch() * screen->devicePixelRatio()), 200); + + // Size is in logical pixel coordinates + QCOMPARE(screen->size(), QSize(800, 640) / 2); + QCOMPARE(screen->geometry(), QRect(QPoint(1920, 0), QSize(400, 320))); + QCOMPARE(screen->virtualGeometry(), QRect(QPoint(0, 0), QSize(1920 + 800 / 2, 1080))); + + exec([&] { remove(output(1)); }); +} + +// QTBUG-62044 +void tst_output::addScreenWithGeometryChange() +{ + const QPoint initialPosition = exec([&] { return output(0)->m_data.position; }); + + exec([&] { + auto *oldOutput = output(0); + auto *newOutput = add<Output>(); + newOutput->m_data.mode.resolution = {1280, 720}; + // Move the primary output to the right + QPoint newPosition(newOutput->m_data.mode.resolution.width(), 0); + Q_ASSERT(newPosition != initialPosition); + oldOutput->m_data.position = newPosition; + oldOutput->sendGeometry(); + oldOutput->sendDone(); + }); + + QTRY_COMPARE(QGuiApplication::screens().size(), 2); + QTRY_COMPARE(QGuiApplication::primaryScreen()->geometry(), QRect(QPoint(1280, 0), QSize(1920, 1080))); + + // Remove the extra output and move the old one back + exec([&] { + remove(output(1)); + output()->m_data.position = initialPosition; + output()->sendGeometry(); + output()->sendDone(); + }); + QTRY_COMPARE(QGuiApplication::screens().size(), 1); + QTRY_COMPARE(QGuiApplication::primaryScreen()->geometry(), QRect(QPoint(0, 0), QSize(1920, 1080))); +} + +void tst_output::windowScreens() +{ + QRasterWindow window; + window.resize(400, 320); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + QTRY_COMPARE(QGuiApplication::screens().size(), 1); + QScreen *primaryScreen = QGuiApplication::screens().first(); + QCOMPARE(window.screen(), primaryScreen); + + exec([&] { add<Output>(); }); + + QTRY_COMPARE(QGuiApplication::screens().size(), 2); + QScreen *secondaryScreen = QGuiApplication::screens().at(1); + QVERIFY(secondaryScreen); + + window.setScreen(secondaryScreen); + QCOMPARE(window.screen(), secondaryScreen); + + exec([&] { + xdgToplevel()->surface()->sendEnter(output(0)); + xdgToplevel()->surface()->sendEnter(output(1)); + }); + + QTRY_COMPARE(window.screen(), primaryScreen); + + exec([&] { + xdgToplevel()->surface()->sendLeave(output(0)); + }); + QTRY_COMPARE(window.screen(), secondaryScreen); + + exec([&] { + remove(output(1)); + }); + QTRY_COMPARE(QGuiApplication::screens().size(), 1); + QCOMPARE(window.screen(), primaryScreen); +} + +void tst_output::removePrimaryScreen() +{ + QRasterWindow window; + window.resize(400, 320); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + QTRY_COMPARE(QGuiApplication::screens().size(), 1); + QScreen *primaryScreen = QGuiApplication::screens().first(); + QCOMPARE(window.screen(), primaryScreen); + + // Add a clone of the primary output + exec([&] { add<Output>(output()->m_data); }); + + QTRY_COMPARE(QGuiApplication::screens().size(), 2); + QTRY_COMPARE(QGuiApplication::primaryScreen()->virtualSiblings().size(), 2); + QScreen *secondaryScreen = QGuiApplication::screens().at(1); + QVERIFY(secondaryScreen); + + exec([&] { remove(output()); }); + QTRY_COMPARE(QGuiApplication::screens().size(), 1); + + exec([&] { + auto *surface = xdgToplevel()->surface(); + pointer()->sendEnter(surface, {32, 32}); + pointer()->sendFrame(client()); + pointer()->sendButton(client(), BTN_LEFT, 1); + pointer()->sendFrame(client()); + pointer()->sendButton(client(), BTN_LEFT, 0); + pointer()->sendFrame(client()); + }); + + // Wait to make sure mouse events dont't cause a crash now that the screen has changed + xdgPingAndWaitForPong(); +} + +// QTBUG-72828 +void tst_output::screenOrder() +{ + exec([&] { + add<Output>()->m_data.model = "Screen 1"; + add<Output>()->m_data.model = "Screen 2"; + }); + + QTRY_COMPARE(QGuiApplication::screens().size(), 3); + const auto screens = QGuiApplication::screens(); + + QCOMPARE(screens[1]->model(), "Screen 1"); + QCOMPARE(screens[2]->model(), "Screen 2"); + + exec([&] { + remove(output(2)); + remove(output(1)); + }); +} + +// This is different from tst_nooutput::noScreens because here we have a screen at platform +// integration initialization, which we then remove. +void tst_output::removeAllScreens() +{ + QRasterWindow window1; + window1.resize(400, 320); + window1.show(); + QCOMPOSITOR_TRY_VERIFY(xdgSurface(0) && xdgSurface(0)->m_committedConfigureSerial); + + const QString wlOutputPrimaryScreenModel = QGuiApplication::primaryScreen()->model(); + + // Get screen info so we can restore it after + auto screenInfo = exec([&] { return output()->m_data; }); + exec([&] { remove(output()); }); + + // Make sure the wl_output is actually removed before we continue + QTRY_VERIFY(!QGuiApplication::primaryScreen() || QGuiApplication::primaryScreen()->model() != wlOutputPrimaryScreenModel); + + // Adding a window while there are no screens should also work + QRasterWindow window2; + window2.resize(400, 320); + window2.show(); + + exec([&] { add<Output>(screenInfo); }); + + // Things should be back to normal + QTRY_VERIFY(QGuiApplication::primaryScreen()); + QTRY_COMPARE(QGuiApplication::primaryScreen()->model(), wlOutputPrimaryScreenModel); + + // Test that we don't leave any fake screens around after we get a wl_output back. + QTRY_COMPARE(QGuiApplication::screens().size(), 1); + + // Qt may choose to recreate/hide windows in response to changing screens, so give the client + // some time to potentially mess up before we verify that the windows are visible. + xdgPingAndWaitForPong(); + + // Windows should be visible after we've reconnected the screen + QCOMPOSITOR_TRY_VERIFY(xdgToplevel(0) && xdgToplevel(0)->m_xdgSurface->m_committedConfigureSerial); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel(1) && xdgToplevel(1)->m_xdgSurface->m_committedConfigureSerial); + +} + +QCOMPOSITOR_TEST_MAIN(tst_output) +#include "tst_output.moc" |