File indexing completed on 2024-04-14 15:33:31

0001 /*
0002     SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "screencasting.h"
0008 #include "logging.h"
0009 #include "qwayland-zkde-screencast-unstable-v1.h"
0010 #include <KWayland/Client/output.h>
0011 #include <KWayland/Client/plasmawindowmanagement.h>
0012 #include <KWayland/Client/registry.h>
0013 #include <QGuiApplication>
0014 #include <QRect>
0015 #include <QtWaylandClient/QWaylandClientExtensionTemplate>
0016 #include <qpa/qplatformnativeinterface.h>
0017 #include <qscreen.h>
0018 #include <qtwaylandclientversion.h>
0019 
0020 using namespace KWayland::Client;
0021 
0022 class ScreencastingStreamPrivate : public QtWayland::zkde_screencast_stream_unstable_v1
0023 {
0024 public:
0025     ScreencastingStreamPrivate(ScreencastingStream *q)
0026         : q(q)
0027     {
0028     }
0029     ~ScreencastingStreamPrivate()
0030     {
0031         if (isInitialized()) {
0032             close();
0033         }
0034         q->deleteLater();
0035     }
0036 
0037     void zkde_screencast_stream_unstable_v1_created(uint32_t node) override
0038     {
0039         m_nodeId = node;
0040         Q_EMIT q->created(node);
0041     }
0042 
0043     void zkde_screencast_stream_unstable_v1_closed() override
0044     {
0045         Q_EMIT q->closed();
0046     }
0047 
0048     void zkde_screencast_stream_unstable_v1_failed(const QString &error) override
0049     {
0050         Q_EMIT q->failed(error);
0051     }
0052 
0053     std::optional<uint> m_nodeId;
0054     QPointer<ScreencastingStream> q;
0055 };
0056 
0057 ScreencastingStream::ScreencastingStream(QObject *parent)
0058     : QObject(parent)
0059     , d(new ScreencastingStreamPrivate(this))
0060 {
0061 }
0062 
0063 ScreencastingStream::~ScreencastingStream() = default;
0064 
0065 quint32 ScreencastingStream::nodeId() const
0066 {
0067     Q_ASSERT(d->m_nodeId.has_value());
0068     return *d->m_nodeId;
0069 }
0070 
0071 class ScreencastingPrivate : public QWaylandClientExtensionTemplate<ScreencastingPrivate>, public QtWayland::zkde_screencast_unstable_v1
0072 {
0073 public:
0074     ScreencastingPrivate(Screencasting *q)
0075         : QWaylandClientExtensionTemplate<ScreencastingPrivate>(ZKDE_SCREENCAST_UNSTABLE_V1_STREAM_REGION_SINCE_VERSION)
0076         , q(q)
0077     {
0078 #if QTWAYLANDCLIENT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
0079         initialize();
0080 #else
0081         // QWaylandClientExtensionTemplate invokes this with a QueuedConnection but we want it called immediately
0082         QMetaObject::invokeMethod(this, "addRegistryListener", Qt::DirectConnection);
0083 #endif
0084 
0085         if (!isInitialized()) {
0086             qWarning() << "Remember requesting the interface on your desktop file: X-KDE-Wayland-Interfaces=zkde_screencast_unstable_v1";
0087         }
0088         Q_ASSERT(isInitialized());
0089     }
0090 
0091     ~ScreencastingPrivate()
0092     {
0093         destroy();
0094     }
0095 
0096     Screencasting *const q;
0097 };
0098 
0099 Screencasting::Screencasting(QObject *parent)
0100     : QObject(parent)
0101     , d(new ScreencastingPrivate(this))
0102 {
0103 }
0104 
0105 Screencasting::~Screencasting() = default;
0106 
0107 ScreencastingStream *Screencasting::createOutputStream(const QString &outputName, Screencasting::CursorMode mode)
0108 {
0109     wl_output *output = nullptr;
0110     for (auto screen : qGuiApp->screens()) {
0111         if (screen->name() == outputName) {
0112             output = (wl_output *)QGuiApplication::platformNativeInterface()->nativeResourceForScreen("output", screen);
0113         }
0114     }
0115 
0116     if (!output) {
0117         return nullptr;
0118     }
0119 
0120     auto stream = new ScreencastingStream(this);
0121     stream->setObjectName(outputName);
0122     stream->d->init(d->stream_output(output, mode));
0123     return stream;
0124 }
0125 
0126 ScreencastingStream *Screencasting::createOutputStream(Output *output, CursorMode mode)
0127 {
0128     auto stream = new ScreencastingStream(this);
0129     stream->setObjectName(output->model());
0130     stream->d->init(d->stream_output(*output, mode));
0131     return stream;
0132 }
0133 
0134 ScreencastingStream *Screencasting::createRegionStream(const QRect &geometry, qreal scaling, CursorMode mode)
0135 {
0136     Q_ASSERT(d->QWaylandClientExtension::version() >= ZKDE_SCREENCAST_UNSTABLE_V1_STREAM_REGION_SINCE_VERSION);
0137     auto stream = new ScreencastingStream(this);
0138     stream->setObjectName(QStringLiteral("region-%1,%2 (%3x%4)").arg(geometry.x()).arg(geometry.y()).arg(geometry.width()).arg(geometry.height()));
0139     stream->d->init(d->stream_region(geometry.x(), geometry.y(), geometry.width(), geometry.height(), wl_fixed_from_double(scaling), mode));
0140     return stream;
0141 }
0142 
0143 ScreencastingStream *Screencasting::createWindowStream(PlasmaWindow *window, CursorMode mode)
0144 {
0145     auto stream = createWindowStream(QString::fromUtf8(window->uuid()), mode);
0146     stream->setObjectName(window->appId());
0147     return stream;
0148 }
0149 
0150 ScreencastingStream *Screencasting::createWindowStream(const QString &uuid, CursorMode mode)
0151 {
0152     auto stream = new ScreencastingStream(this);
0153     stream->d->init(d->stream_window(uuid, mode));
0154     return stream;
0155 }
0156 
0157 ScreencastingStream *Screencasting::createVirtualMonitorStream(const QString &name, const QSize &size, qreal scale, CursorMode mode)
0158 {
0159     auto stream = new ScreencastingStream(this);
0160     stream->d->init(d->stream_virtual_output(name, size.width(), size.height(), wl_fixed_from_double(scale), mode));
0161     return stream;
0162 }
0163 
0164 void Screencasting::destroy()
0165 {
0166     d.reset(nullptr);
0167 }