File indexing completed on 2024-11-10 04:57:06
0001 /* 0002 SPDX-FileCopyrightText: 2018-2020 Red Hat Inc 0003 SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org> 0004 SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "screencastmanager.h" 0010 #include "compositor.h" 0011 #include "core/output.h" 0012 #include "core/outputbackend.h" 0013 #include "opengl/gltexture.h" 0014 #include "outputscreencastsource.h" 0015 #include "pipewirecore.h" 0016 #include "regionscreencastsource.h" 0017 #include "scene/workspacescene.h" 0018 #include "screencaststream.h" 0019 #include "wayland/display.h" 0020 #include "wayland/output.h" 0021 #include "wayland_server.h" 0022 #include "window.h" 0023 #include "windowscreencastsource.h" 0024 #include "workspace.h" 0025 0026 #include <KLocalizedString> 0027 0028 namespace KWin 0029 { 0030 0031 ScreencastManager::ScreencastManager() 0032 : m_screencast(new ScreencastV1Interface(waylandServer()->display(), this)) 0033 , m_core(new PipeWireCore) 0034 { 0035 m_core->init(); 0036 connect(m_screencast, &ScreencastV1Interface::windowScreencastRequested, this, &ScreencastManager::streamWindow); 0037 connect(m_screencast, &ScreencastV1Interface::outputScreencastRequested, this, &ScreencastManager::streamWaylandOutput); 0038 connect(m_screencast, &ScreencastV1Interface::virtualOutputScreencastRequested, this, &ScreencastManager::streamVirtualOutput); 0039 connect(m_screencast, &ScreencastV1Interface::regionScreencastRequested, this, &ScreencastManager::streamRegion); 0040 } 0041 0042 static QRegion scaleRegion(const QRegion &_region, qreal scale) 0043 { 0044 if (scale == 1.) { 0045 return _region; 0046 } 0047 0048 QRegion region; 0049 for (auto it = _region.begin(), itEnd = _region.end(); it != itEnd; ++it) { 0050 region += QRect(std::floor(it->x() * scale), 0051 std::floor(it->y() * scale), 0052 std::ceil(it->width() * scale), 0053 std::ceil(it->height() * scale)); 0054 } 0055 0056 return region; 0057 } 0058 0059 class WindowStream : public ScreenCastStream 0060 { 0061 public: 0062 WindowStream(Window *window, std::shared_ptr<PipeWireCore> pwCore, QObject *parent) 0063 : ScreenCastStream(new WindowScreenCastSource(window), pwCore, parent) 0064 , m_window(window) 0065 { 0066 m_timer.setInterval(0); 0067 m_timer.setSingleShot(true); 0068 setObjectName(window->desktopFileName()); 0069 connect(&m_timer, &QTimer::timeout, this, &WindowStream::bufferToStream); 0070 connect(this, &ScreenCastStream::startStreaming, this, &WindowStream::startFeeding); 0071 connect(this, &ScreenCastStream::stopStreaming, this, &WindowStream::stopFeeding); 0072 } 0073 0074 private: 0075 void startFeeding() 0076 { 0077 connect(m_window, &Window::damaged, this, &WindowStream::markDirty); 0078 markDirty(); 0079 } 0080 0081 void stopFeeding() 0082 { 0083 disconnect(m_window, &Window::damaged, this, &WindowStream::markDirty); 0084 m_timer.stop(); 0085 } 0086 0087 void markDirty() 0088 { 0089 m_timer.start(); 0090 } 0091 0092 void bufferToStream() 0093 { 0094 recordFrame(QRegion(0, 0, m_window->width(), m_window->height())); 0095 } 0096 0097 Window *m_window; 0098 QTimer m_timer; 0099 }; 0100 0101 void ScreencastManager::streamWindow(ScreencastStreamV1Interface *waylandStream, 0102 const QString &winid, 0103 ScreencastV1Interface::CursorMode mode) 0104 { 0105 auto window = Workspace::self()->findWindow(QUuid(winid)); 0106 if (!window) { 0107 waylandStream->sendFailed(i18n("Could not find window id %1", winid)); 0108 return; 0109 } 0110 0111 auto stream = new WindowStream(window, m_core, this); 0112 stream->setCursorMode(mode, 1, window->clientGeometry()); 0113 if (mode != ScreencastV1Interface::CursorMode::Hidden) { 0114 connect(window, &Window::clientGeometryChanged, stream, [window, stream, mode]() { 0115 stream->setCursorMode(mode, 1, window->clientGeometry().toRect()); 0116 }); 0117 } 0118 0119 integrateStreams(waylandStream, stream); 0120 } 0121 0122 void ScreencastManager::streamVirtualOutput(ScreencastStreamV1Interface *stream, 0123 const QString &name, 0124 const QSize &size, 0125 double scale, 0126 ScreencastV1Interface::CursorMode mode) 0127 { 0128 auto output = kwinApp()->outputBackend()->createVirtualOutput(name, size, scale); 0129 streamOutput(stream, output, mode); 0130 connect(stream, &ScreencastStreamV1Interface::finished, output, [output] { 0131 kwinApp()->outputBackend()->removeVirtualOutput(output); 0132 }); 0133 } 0134 0135 void ScreencastManager::streamWaylandOutput(ScreencastStreamV1Interface *waylandStream, 0136 OutputInterface *output, 0137 ScreencastV1Interface::CursorMode mode) 0138 { 0139 streamOutput(waylandStream, output->handle(), mode); 0140 } 0141 0142 void ScreencastManager::streamOutput(ScreencastStreamV1Interface *waylandStream, 0143 Output *streamOutput, 0144 ScreencastV1Interface::CursorMode mode) 0145 { 0146 if (!streamOutput) { 0147 waylandStream->sendFailed(i18n("Could not find output")); 0148 return; 0149 } 0150 0151 auto stream = new ScreenCastStream(new OutputScreenCastSource(streamOutput), m_core, this); 0152 stream->setObjectName(streamOutput->name()); 0153 stream->setCursorMode(mode, streamOutput->scale(), streamOutput->geometry()); 0154 auto bufferToStream = [stream, streamOutput](const QRegion &damagedRegion) { 0155 if (!damagedRegion.isEmpty()) { 0156 stream->recordFrame(scaleRegion(damagedRegion, streamOutput->scale())); 0157 } 0158 }; 0159 connect(stream, &ScreenCastStream::startStreaming, waylandStream, [streamOutput, stream, bufferToStream] { 0160 Compositor::self()->scene()->addRepaint(streamOutput->geometry()); 0161 connect(streamOutput, &Output::outputChange, stream, bufferToStream); 0162 }); 0163 integrateStreams(waylandStream, stream); 0164 } 0165 0166 static QString rectToString(const QRect &rect) 0167 { 0168 return QStringLiteral("%1,%2 %3x%4").arg(rect.x()).arg(rect.y()).arg(rect.width()).arg(rect.height()); 0169 } 0170 0171 void ScreencastManager::streamRegion(ScreencastStreamV1Interface *waylandStream, const QRect &geometry, qreal scale, ScreencastV1Interface::CursorMode mode) 0172 { 0173 if (!geometry.isValid()) { 0174 waylandStream->sendFailed(i18n("Invalid region")); 0175 return; 0176 } 0177 0178 auto source = new RegionScreenCastSource(geometry, scale); 0179 auto stream = new ScreenCastStream(source, m_core, this); 0180 stream->setObjectName(rectToString(geometry)); 0181 stream->setCursorMode(mode, scale, geometry); 0182 0183 connect(stream, &ScreenCastStream::startStreaming, waylandStream, [geometry, stream, source, waylandStream] { 0184 Compositor::self()->scene()->addRepaint(geometry); 0185 0186 bool found = false; 0187 const auto allOutputs = workspace()->outputs(); 0188 for (auto output : allOutputs) { 0189 if (output->geometry().intersects(geometry)) { 0190 auto bufferToStream = [output, stream, source](const QRegion &damagedRegion) { 0191 if (damagedRegion.isEmpty()) { 0192 return; 0193 } 0194 0195 const QRect streamRegion = source->region(); 0196 const QRegion region = output->pixelSize() != output->modeSize() ? output->geometry() : damagedRegion; 0197 source->updateOutput(output); 0198 stream->recordFrame(scaleRegion(region.translated(-streamRegion.topLeft()).intersected(streamRegion), source->scale())); 0199 }; 0200 connect(output, &Output::outputChange, stream, bufferToStream); 0201 found |= true; 0202 } 0203 } 0204 if (!found) { 0205 waylandStream->sendFailed(i18n("Region outside the workspace")); 0206 } 0207 }); 0208 integrateStreams(waylandStream, stream); 0209 } 0210 0211 void ScreencastManager::integrateStreams(ScreencastStreamV1Interface *waylandStream, ScreenCastStream *stream) 0212 { 0213 connect(waylandStream, &ScreencastStreamV1Interface::finished, stream, &ScreenCastStream::stop); 0214 connect(stream, &ScreenCastStream::stopStreaming, waylandStream, [stream, waylandStream] { 0215 waylandStream->sendClosed(); 0216 stream->deleteLater(); 0217 }); 0218 connect(stream, &ScreenCastStream::streamReady, stream, [waylandStream](uint nodeid) { 0219 waylandStream->sendCreated(nodeid); 0220 }); 0221 if (!stream->init()) { 0222 waylandStream->sendFailed(stream->error()); 0223 delete stream; 0224 } 0225 } 0226 0227 } // namespace KWin 0228 0229 #include "moc_screencastmanager.cpp"