summaryrefslogtreecommitdiffstats
path: root/tests/auto/client/output/tst_output.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/client/output/tst_output.cpp')
-rw-r--r--tests/auto/client/output/tst_output.cpp249
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"