File indexing completed on 2024-11-10 04:56:05
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <aleixpol@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "compositor.h" 0010 #include "core/output.h" 0011 #include "generic_scene_opengl_test.h" 0012 #include "opengl/glplatform.h" 0013 #include "pointer_input.h" 0014 #include "scene/workspacescene.h" 0015 #include "wayland_server.h" 0016 #include "window.h" 0017 #include "workspace.h" 0018 0019 #include <KWayland/Client/output.h> 0020 #include <KWayland/Client/subsurface.h> 0021 #include <KWayland/Client/surface.h> 0022 #include <PipeWireSourceStream> 0023 #include <QPainter> 0024 #include <QScreen> 0025 0026 #define QCOMPAREIMG(actual, expected, id) \ 0027 { \ 0028 if ((actual) != (expected)) { \ 0029 const auto actualFile = QStringLiteral("appium_artifact_actual_%1.png").arg(id); \ 0030 const auto expectedFile = QStringLiteral("appium_artifact_expected_%1.png").arg(id); \ 0031 (actual).save(actualFile); \ 0032 (expected).save(expectedFile); \ 0033 qDebug() << "Generated failed file" << actualFile << expectedFile; \ 0034 } \ 0035 QCOMPARE(actual, expected); \ 0036 } 0037 0038 namespace KWin 0039 { 0040 0041 static const QString s_socketName = QStringLiteral("wayland_test_buffer_size_change-0"); 0042 0043 class ScreencastingTest : public GenericSceneOpenGLTest 0044 { 0045 Q_OBJECT 0046 public: 0047 ScreencastingTest() 0048 : GenericSceneOpenGLTest(QByteArrayLiteral("O2")) 0049 { 0050 auto wrap = [this](const QString &process, const QStringList &arguments = {}) { 0051 // Make sure PipeWire is running. If it's already running it will just exit 0052 QProcess *p = new QProcess(this); 0053 p->setProcessChannelMode(QProcess::MergedChannels); 0054 p->setArguments(arguments); 0055 connect(this, &QObject::destroyed, p, [p] { 0056 p->terminate(); 0057 p->waitForFinished(); 0058 p->kill(); 0059 }); 0060 connect(p, &QProcess::errorOccurred, p, [p](auto status) { 0061 qDebug() << "error" << status << p->program(); 0062 }); 0063 connect(p, &QProcess::finished, p, [p](int code, auto status) { 0064 if (code != 0) { 0065 qDebug() << p->readAll(); 0066 } 0067 qDebug() << "finished" << code << status << p->program(); 0068 }); 0069 p->setProgram(process); 0070 p->start(); 0071 }; 0072 0073 // If I run this outside the CI, it breaks the system's pipewire 0074 if (qgetenv("KDECI_BUILD") == "TRUE") { 0075 wrap("pipewire"); 0076 wrap("dbus-launch", {"wireplumber"}); 0077 } 0078 } 0079 private Q_SLOTS: 0080 void init(); 0081 void testWindowCasting(); 0082 void testOutputCasting(); 0083 0084 private: 0085 std::optional<QImage> oneFrameAndClose(Test::ScreencastingStreamV1 *stream); 0086 }; 0087 0088 void ScreencastingTest::init() 0089 { 0090 QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::ScreencastingV1)); 0091 QVERIFY(KWin::Test::screencasting()); 0092 Cursors::self()->hideCursor(); 0093 } 0094 0095 std::optional<QImage> ScreencastingTest::oneFrameAndClose(Test::ScreencastingStreamV1 *stream) 0096 { 0097 Q_ASSERT(stream); 0098 PipeWireSourceStream pwStream; 0099 qDebug() << "start" << stream; 0100 connect(stream, &Test::ScreencastingStreamV1::failed, qGuiApp, [](const QString &error) { 0101 qDebug() << "stream failed with error" << error; 0102 Q_ASSERT(false); 0103 }); 0104 connect(stream, &Test::ScreencastingStreamV1::closed, qGuiApp, [&pwStream] { 0105 pwStream.setActive(false); 0106 }); 0107 connect(stream, &Test::ScreencastingStreamV1::created, qGuiApp, [&pwStream](quint32 nodeId) { 0108 pwStream.createStream(nodeId, 0); 0109 }); 0110 0111 std::optional<QImage> img; 0112 connect(&pwStream, &PipeWireSourceStream::frameReceived, qGuiApp, [&img](const PipeWireFrame &frame) { 0113 if (frame.dataFrame) { 0114 img = frame.dataFrame->toImage(); 0115 } 0116 }); 0117 0118 QSignalSpy spy(&pwStream, &PipeWireSourceStream::frameReceived); 0119 if (!spy.wait()) { 0120 qDebug() << "Did not receive any frames"; 0121 } 0122 pwStream.stopStreaming(); 0123 return img; 0124 } 0125 0126 void ScreencastingTest::testWindowCasting() 0127 { 0128 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0129 QVERIFY(surface != nullptr); 0130 0131 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0132 QVERIFY(shellSurface != nullptr); 0133 0134 QImage sourceImage(QSize(30, 10), QImage::Format_RGBA8888_Premultiplied); 0135 sourceImage.fill(Qt::red); 0136 0137 Window *window = Test::renderAndWaitForShown(surface.get(), sourceImage); 0138 QVERIFY(window); 0139 0140 auto stream = KWin::Test::screencasting()->createWindowStream(window->internalId().toString(), QtWayland::zkde_screencast_unstable_v1::pointer_hidden); 0141 0142 std::optional<QImage> img = oneFrameAndClose(stream); 0143 QVERIFY(img); 0144 img->convertTo(sourceImage.format()); 0145 QCOMPAREIMG(*img, sourceImage, QLatin1String("window_cast")); 0146 } 0147 0148 void ScreencastingTest::testOutputCasting() 0149 { 0150 auto theOutput = KWin::Test::waylandOutputs().constFirst(); 0151 0152 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0153 QVERIFY(surface != nullptr); 0154 0155 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0156 QVERIFY(shellSurface != nullptr); 0157 0158 QImage sourceImage(theOutput->pixelSize(), QImage::Format_RGBA8888_Premultiplied); 0159 sourceImage.fill(Qt::green); 0160 { 0161 QPainter p(&sourceImage); 0162 p.drawRect(100, 100, 100, 100); 0163 } 0164 0165 Window *window = Test::renderAndWaitForShown(surface.get(), sourceImage); 0166 QVERIFY(window); 0167 QCOMPARE(window->frameGeometry(), window->output()->geometry()); 0168 0169 auto stream = KWin::Test::screencasting()->createOutputStream(theOutput->output(), QtWayland::zkde_screencast_unstable_v1::pointer_hidden); 0170 0171 std::optional<QImage> img = oneFrameAndClose(stream); 0172 QVERIFY(img); 0173 img->convertTo(sourceImage.format()); 0174 QCOMPAREIMG(*img, sourceImage, QLatin1String("output_cast")); 0175 } 0176 0177 } 0178 0179 WAYLANDTEST_MAIN(KWin::ScreencastingTest) 0180 #include "screencasting_test.moc"