KWin
Loading...
Searching...
No Matches
screencastmanager.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2018-2020 Red Hat Inc
3 SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
4 SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "screencastmanager.h"
10#include "compositor.h"
11#include "core/output.h"
12#include "core/outputbackend.h"
13#include "opengl/gltexture.h"
15#include "pipewirecore.h"
18#include "screencaststream.h"
19#include "wayland/display.h"
20#include "wayland/output.h"
21#include "wayland_server.h"
22#include "window.h"
24#include "workspace.h"
25
26#include <KLocalizedString>
27
28namespace KWin
29{
30
32 : m_screencast(new ScreencastV1Interface(waylandServer()->display(), this))
33 , m_core(new PipeWireCore)
34{
35 m_core->init();
36 connect(m_screencast, &ScreencastV1Interface::windowScreencastRequested, this, &ScreencastManager::streamWindow);
37 connect(m_screencast, &ScreencastV1Interface::outputScreencastRequested, this, &ScreencastManager::streamWaylandOutput);
38 connect(m_screencast, &ScreencastV1Interface::virtualOutputScreencastRequested, this, &ScreencastManager::streamVirtualOutput);
39 connect(m_screencast, &ScreencastV1Interface::regionScreencastRequested, this, &ScreencastManager::streamRegion);
40}
41
42static QRegion scaleRegion(const QRegion &_region, qreal scale)
43{
44 if (scale == 1.) {
45 return _region;
46 }
47
48 QRegion region;
49 for (auto it = _region.begin(), itEnd = _region.end(); it != itEnd; ++it) {
50 region += QRect(std::floor(it->x() * scale),
51 std::floor(it->y() * scale),
52 std::ceil(it->width() * scale),
53 std::ceil(it->height() * scale));
54 }
55
56 return region;
57}
58
60{
61public:
62 WindowStream(Window *window, std::shared_ptr<PipeWireCore> pwCore, QObject *parent)
63 : ScreenCastStream(new WindowScreenCastSource(window), pwCore, parent)
64 , m_window(window)
65 {
66 m_timer.setInterval(0);
67 m_timer.setSingleShot(true);
68 setObjectName(window->desktopFileName());
69 connect(&m_timer, &QTimer::timeout, this, &WindowStream::bufferToStream);
70 connect(this, &ScreenCastStream::startStreaming, this, &WindowStream::startFeeding);
71 connect(this, &ScreenCastStream::stopStreaming, this, &WindowStream::stopFeeding);
72 }
73
74private:
75 void startFeeding()
76 {
77 connect(m_window, &Window::damaged, this, &WindowStream::markDirty);
78 markDirty();
79 }
80
81 void stopFeeding()
82 {
83 disconnect(m_window, &Window::damaged, this, &WindowStream::markDirty);
84 m_timer.stop();
85 }
86
87 void markDirty()
88 {
89 m_timer.start();
90 }
91
92 void bufferToStream()
93 {
94 recordFrame(QRegion(0, 0, m_window->width(), m_window->height()));
95 }
96
97 Window *m_window;
98 QTimer m_timer;
99};
100
101void ScreencastManager::streamWindow(ScreencastStreamV1Interface *waylandStream,
102 const QString &winid,
104{
105 auto window = Workspace::self()->findWindow(QUuid(winid));
106 if (!window) {
107 waylandStream->sendFailed(i18n("Could not find window id %1", winid));
108 return;
109 }
110
111 auto stream = new WindowStream(window, m_core, this);
112 stream->setCursorMode(mode, 1, window->clientGeometry());
114 connect(window, &Window::clientGeometryChanged, stream, [window, stream, mode]() {
115 stream->setCursorMode(mode, 1, window->clientGeometry().toRect());
116 });
117 }
118
119 integrateStreams(waylandStream, stream);
120}
121
122void ScreencastManager::streamVirtualOutput(ScreencastStreamV1Interface *stream,
123 const QString &name,
124 const QSize &size,
125 double scale,
127{
128 auto output = kwinApp()->outputBackend()->createVirtualOutput(name, size, scale);
129 streamOutput(stream, output, mode);
130 connect(stream, &ScreencastStreamV1Interface::finished, output, [output] {
131 kwinApp()->outputBackend()->removeVirtualOutput(output);
132 });
133}
134
135void ScreencastManager::streamWaylandOutput(ScreencastStreamV1Interface *waylandStream,
136 OutputInterface *output,
138{
139 streamOutput(waylandStream, output->handle(), mode);
140}
141
142void ScreencastManager::streamOutput(ScreencastStreamV1Interface *waylandStream,
143 Output *streamOutput,
145{
146 if (!streamOutput) {
147 waylandStream->sendFailed(i18n("Could not find output"));
148 return;
149 }
150
151 auto stream = new ScreenCastStream(new OutputScreenCastSource(streamOutput), m_core, this);
152 stream->setObjectName(streamOutput->name());
153 stream->setCursorMode(mode, streamOutput->scale(), streamOutput->geometry());
154 auto bufferToStream = [stream, streamOutput](const QRegion &damagedRegion) {
155 if (!damagedRegion.isEmpty()) {
156 stream->recordFrame(scaleRegion(damagedRegion, streamOutput->scale()));
157 }
158 };
159 connect(stream, &ScreenCastStream::startStreaming, waylandStream, [streamOutput, stream, bufferToStream] {
160 Compositor::self()->scene()->addRepaint(streamOutput->geometry());
161 connect(streamOutput, &Output::outputChange, stream, bufferToStream);
162 });
163 integrateStreams(waylandStream, stream);
164}
165
166static QString rectToString(const QRect &rect)
167{
168 return QStringLiteral("%1,%2 %3x%4").arg(rect.x()).arg(rect.y()).arg(rect.width()).arg(rect.height());
169}
170
171void ScreencastManager::streamRegion(ScreencastStreamV1Interface *waylandStream, const QRect &geometry, qreal scale, ScreencastV1Interface::CursorMode mode)
172{
173 if (!geometry.isValid()) {
174 waylandStream->sendFailed(i18n("Invalid region"));
175 return;
176 }
177
178 auto source = new RegionScreenCastSource(geometry, scale);
179 auto stream = new ScreenCastStream(source, m_core, this);
180 stream->setObjectName(rectToString(geometry));
181 stream->setCursorMode(mode, scale, geometry);
182
183 connect(stream, &ScreenCastStream::startStreaming, waylandStream, [geometry, stream, source, waylandStream] {
184 Compositor::self()->scene()->addRepaint(geometry);
185
186 bool found = false;
187 const auto allOutputs = workspace()->outputs();
188 for (auto output : allOutputs) {
189 if (output->geometry().intersects(geometry)) {
190 auto bufferToStream = [output, stream, source](const QRegion &damagedRegion) {
191 if (damagedRegion.isEmpty()) {
192 return;
193 }
194
195 const QRect streamRegion = source->region();
196 const QRegion region = output->pixelSize() != output->modeSize() ? output->geometry() : damagedRegion;
197 source->updateOutput(output);
198 stream->recordFrame(scaleRegion(region.translated(-streamRegion.topLeft()).intersected(streamRegion), source->scale()));
199 };
200 connect(output, &Output::outputChange, stream, bufferToStream);
201 found |= true;
202 }
203 }
204 if (!found) {
205 waylandStream->sendFailed(i18n("Region outside the workspace"));
206 }
207 });
208 integrateStreams(waylandStream, stream);
209}
210
211void ScreencastManager::integrateStreams(ScreencastStreamV1Interface *waylandStream, ScreenCastStream *stream)
212{
213 connect(waylandStream, &ScreencastStreamV1Interface::finished, stream, &ScreenCastStream::stop);
214 connect(stream, &ScreenCastStream::stopStreaming, waylandStream, [stream, waylandStream] {
215 waylandStream->sendClosed();
216 stream->deleteLater();
217 });
218 connect(stream, &ScreenCastStream::streamReady, stream, [waylandStream](uint nodeid) {
219 waylandStream->sendCreated(nodeid);
220 });
221 if (!stream->init()) {
222 waylandStream->sendFailed(stream->error());
223 delete stream;
224 }
225}
226
227} // namespace KWin
228
229#include "moc_screencastmanager.cpp"
WorkspaceScene * scene() const
Definition compositor.h:60
static Compositor * self()
void outputChange(const QRegion &damagedRegion)
void addRepaint(const QRegion &region)
Definition scene.cpp:91
void streamReady(quint32 nodeId)
void recordFrame(const QRegion &damagedRegion)
void virtualOutputScreencastRequested(ScreencastStreamV1Interface *stream, const QString &name, const QSize &size, double scaling, CursorMode mode)
void outputScreencastRequested(ScreencastStreamV1Interface *stream, OutputInterface *output, CursorMode mode)
void regionScreencastRequested(ScreencastStreamV1Interface *stream, const QRect &geometry, qreal scaling, CursorMode mode)
void windowScreencastRequested(ScreencastStreamV1Interface *stream, const QString &winid, CursorMode mode)
qreal width
Definition window.h:99
QString desktopFileName
Definition window.h:501
void clientGeometryChanged(const QRectF &oldGeometry)
qreal height
Definition window.h:104
void damaged(KWin::Window *window)
WindowStream(Window *window, std::shared_ptr< PipeWireCore > pwCore, QObject *parent)
Window * findWindow(const QUuid &internalId) const
static Workspace * self()
Definition workspace.h:91
QList< Output * > outputs() const
Definition workspace.h:762
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830