Revision control

++ // Copyright (C) 2023 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include "tst_qwindowcapturebackend_widget.h"
++
++ #include <QtTest/qtest.h>
++ #include <QtWidgets/qapplication.h>
++ #include <QtGui/qwindow.h>
++ #include <QtCore/qsystemsemaphore.h>
++
++ #include "tst_qwindowcapturebackend_fixture.h"
++
++ using QtMultimediaTest::surfaceCaptureTestTimeout;
++
++ TestWidget::TestWidget(const QString &uuid, QScreen *screen)
++ {
++ // Give each window a unique title so that we can uniquely identify it
++ setWindowTitle(uuid);
++
++ setScreen(screen ? screen : QApplication::primaryScreen());
++
++ // Use frameless hint because on Windows UWP platform, the window titlebar is captured,
++ // but the reference image acquired by 'QWindow::grab()' does not include titlebar.
++ // This allows us to do pixel-perfect matching of captured content.
++ setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
++ setFixedSize(60, 40);
++
++ // Detect when the test application loses the foreground while a capture
++ // window is visible (e.g. the user clicks another app during a local run).
++ // This can silently affect capture results, so make it visible in the test
++ // output.
++ connect(
++ qApp,
++ &QGuiApplication::applicationStateChanged,
++ this,
++ [this](Qt::ApplicationState state) {
++ if (state != Qt::ApplicationActive && isVisible() && !isMinimized()) {
++ qCritical() << "Test application lost the foreground while test window"
++ << windowTitle()
++ << "was visible. This can affect test results.";
++ }
++ });
++ }
++
++ void TestWidget::setDisplayPattern(Pattern p)
++ {
++ m_pattern = p;
++
++ // The animated pattern needs continuous repaints to keep the content
++ // changing. The other patterns are static.
++ m_animated = (p == Pattern::Animated);
++
++ repaint();
++ }
++
++ void TestWidget::setSize(QSize size)
++ {
++ if (size == QApplication::primaryScreen()->size())
++ setWindowState(Qt::WindowMaximized);
++ else
++ setFixedSize(size);
++ }
++
++ QImage TestWidget::grabImage()
++ {
++ return grab().toImage();
++ }
++
++ void TestWidget::paintEvent(QPaintEvent *)
++ {
++ QPainter p(this);
++ p.setPen(Qt::NoPen);
++ p.setBrush(Qt::black);
++ p.drawRect(rect());
++
++ switch (m_pattern) {
++ case Pattern::ColoredSquares:
++ drawColoredSquares(p);
++ break;
++ case Pattern::Grid:
++ drawGrid(p);
++ break;
++ case Pattern::Animated:
++ drawAnimationFrame(p);
++ break;
++ }
++
++ p.end();
++
++ if (m_animated) {
++ ++m_animationTick;
++ if (QWindow *window = windowHandle())
++ window->requestUpdate();
++ }
++ }
++
++ void TestWidget::changeEvent(QEvent *event)
++ {
++ switch (event->type()) {
++ case QEvent::WindowStateChange:
++ if (windowState() & Qt::WindowMinimized) {
++ qCritical() << "Test window" << windowTitle()
++ << "was minimized during the test. This can affect window capture results";
++ }
++ break;
++ default:
++ break;
++ }
++
++ QWidget::changeEvent(event);
++ }
++
++ void TestWidget::drawColoredSquares(QPainter &p) const
++ {
++ const std::vector<std::vector<Qt::GlobalColor>> colors = { { Qt::red, Qt::green, Qt::blue },
++ { Qt::white, Qt::white, Qt::white },
++ { Qt::blue, Qt::green, Qt::red } };
++
++ const QSize squareSize = size() / 3;
++ QRect rect{ QPoint{ 0, 0 }, squareSize };
++
++ for (const auto &row : colors) {
++ for (const auto &color : row) {
++ p.setBrush(color);
++ p.drawRect(rect);
++ rect.moveLeft(rect.left() + rect.width());
++ }
++ rect.moveTo({ 0, rect.bottom() });
++ }
++ }
++
++ void TestWidget::drawAnimationFrame(QPainter &p) const
++ {
++ // Alternate between the colored squares and the grid pattern on every tick
++ // so that the captured content changes from frame to frame.
++ if (m_animationTick % 2 == 0)
++ drawColoredSquares(p);
++ else
++ drawGrid(p);
++ }
++
++ void TestWidget::drawGrid(QPainter &p) const
++ {
++ const QSize winSize = size();
++
++ p.setPen(Qt::white);
++
++ QLine vertical{ QPoint{ 5, 0 }, QPoint{ 5, winSize.height() } };
++ while (vertical.x1() < winSize.width()) {
++ p.drawLine(vertical);
++ vertical.translate(10, 0);
++ }
++ QLine horizontal{ QPoint{ 0, 5 }, QPoint{ winSize.width(), 5 } };
++ while (horizontal.y1() < winSize.height()) {
++ p.drawLine(horizontal);
++ horizontal.translate(0, 10);
++ }
++ }
++
++ bool showCaptureWindow(const QString &windowTitle)
++ {
++ const QNativeIpcKey key{ windowTitle };
++ QSystemSemaphore windowVisible(key);
++
++ TestWidget widget{ windowTitle };
++ widget.show();
++
++ // Wait for window to be visible and suitable for window capturing
++ const bool result = QTest::qWaitForWindowExposed(&widget, surfaceCaptureTestTimeout());
++ if (!result)
++ qDebug() << "Failed to show window";
++
++ // Signal to host process that the window is visible
++ windowVisible.release();
++
++ // Keep window visible until a termination signal is received
++ QApplication::exec();
++
++ return result;
++ }
++
++ #include "moc_tst_qwindowcapturebackend_widget.cpp"