screencasting: Support regions from the v3 of the protocol

This commit is contained in:
Aleix Pol 2022-03-04 04:22:20 +01:00 committed by Aleix Pol Gonzalez
parent 2139e2abab
commit f0ce2a0f53
5 changed files with 209 additions and 0 deletions

View file

@ -4,6 +4,7 @@ target_sources(KWinScreencastPlugin PRIVATE
main.cpp main.cpp
outputscreencastsource.cpp outputscreencastsource.cpp
pipewirecore.cpp pipewirecore.cpp
regionscreencastsource.cpp
screencastmanager.cpp screencastmanager.cpp
screencastsource.cpp screencastsource.cpp
screencaststream.cpp screencaststream.cpp

View file

@ -0,0 +1,115 @@
/*
SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "regionscreencastsource.h"
#include "screencastutils.h"
#include <abstract_wayland_output.h>
#include <composite.h>
#include <kwingltexture.h>
#include <scene.h>
#include <kwinglutils.h>
#include <platform.h>
#include <QPainter>
namespace KWin
{
RegionScreenCastSource::RegionScreenCastSource(const QRect &region, qreal scale, QObject *parent)
: ScreenCastSource(parent)
, m_region(region)
, m_scale(scale)
{
Q_ASSERT(m_region.isValid());
Q_ASSERT(m_scale > 0);
}
QSize RegionScreenCastSource::textureSize() const
{
return m_region.size() * m_scale;
}
bool RegionScreenCastSource::hasAlphaChannel() const
{
return true;
}
void RegionScreenCastSource::updateOutput(AbstractWaylandOutput *output)
{
m_last = output->renderLoop()->lastPresentationTimestamp();
if (!m_renderedTexture.isNull()) {
const QSharedPointer<GLTexture> outputTexture = Compositor::self()->scene()->textureForOutput(output);
const auto outputGeometry = output->geometry();
if (!outputTexture || !m_region.intersects(output->geometry())) {
return;
}
GLRenderTarget::pushRenderTarget(m_target.data());
const QRect geometry({0, 0}, m_target->size());
ShaderBinder shaderBinder(ShaderTrait::MapTexture);
QMatrix4x4 projectionMatrix;
projectionMatrix.ortho(m_region);
const QPoint pos = outputGeometry.topLeft();
projectionMatrix.translate(pos.x(), pos.y());
shaderBinder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, projectionMatrix);
outputTexture->bind();
outputTexture->render(output->geometry());
outputTexture->unbind();
GLRenderTarget::popRenderTarget();
}
}
std::chrono::nanoseconds RegionScreenCastSource::clock() const
{
return m_last;
}
void RegionScreenCastSource::render(GLRenderTarget *target)
{
if (!m_renderedTexture) {
m_renderedTexture.reset(new GLTexture(hasAlphaChannel() ? GL_RGBA8 : GL_RGB8, textureSize()));
m_target.reset(new GLRenderTarget(m_renderedTexture.data()));
const auto allOutputs = kwinApp()->platform()->enabledOutputs();
for (auto output : allOutputs) {
AbstractWaylandOutput *streamOutput = qobject_cast<AbstractWaylandOutput *>(output);
if (streamOutput->geometry().intersects(m_region)) {
updateOutput(streamOutput);
}
}
}
GLRenderTarget::pushRenderTarget(target);
QRect r(QPoint(), target->size());
auto shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
QMatrix4x4 projectionMatrix;
projectionMatrix.ortho(r);
shader->setUniform(GLShader::ModelViewProjectionMatrix, projectionMatrix);
m_renderedTexture->bind();
m_renderedTexture->render(r);
m_renderedTexture->unbind();
ShaderManager::instance()->popShader();
GLRenderTarget::popRenderTarget();
}
void RegionScreenCastSource::render(QImage *image)
{
GLTexture offscreenTexture(hasAlphaChannel() ? GL_RGBA8 : GL_RGB8, textureSize());
GLRenderTarget offscreenTarget(&offscreenTexture);
render(&offscreenTarget);
grabTexture(&offscreenTexture, image);
}
}

View file

@ -0,0 +1,46 @@
/*
SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "screencastsource.h"
#include <QImage>
#include <kwingltexture.h>
#include <kwinglutils.h>
namespace KWin
{
class AbstractWaylandOutput;
class RegionScreenCastSource : public ScreenCastSource
{
Q_OBJECT
public:
explicit RegionScreenCastSource(const QRect &region, qreal scale, QObject *parent = nullptr);
bool hasAlphaChannel() const override;
QSize textureSize() const override;
void render(GLRenderTarget *target) override;
void render(QImage *image) override;
std::chrono::nanoseconds clock() const override;
QRect region() const {
return m_region;
}
void updateOutput(AbstractWaylandOutput *output);
private:
const QRect m_region;
const qreal m_scale;
QScopedPointer<GLRenderTarget> m_target;
QScopedPointer<GLTexture> m_renderedTexture;
std::chrono::nanoseconds m_last;
};
} // namespace KWin

View file

@ -14,6 +14,7 @@
#include "effects.h" #include "effects.h"
#include "kwingltexture.h" #include "kwingltexture.h"
#include "outputscreencastsource.h" #include "outputscreencastsource.h"
#include "regionscreencastsource.h"
#include "screencaststream.h" #include "screencaststream.h"
#include "platform.h" #include "platform.h"
#include "scene.h" #include "scene.h"
@ -37,6 +38,7 @@ ScreencastManager::ScreencastManager(QObject *parent)
this, &ScreencastManager::streamWindow); this, &ScreencastManager::streamWindow);
connect(m_screencast, &KWaylandServer::ScreencastV1Interface::outputScreencastRequested, this, &ScreencastManager::streamWaylandOutput); connect(m_screencast, &KWaylandServer::ScreencastV1Interface::outputScreencastRequested, this, &ScreencastManager::streamWaylandOutput);
connect(m_screencast, &KWaylandServer::ScreencastV1Interface::virtualOutputScreencastRequested, this, &ScreencastManager::streamVirtualOutput); connect(m_screencast, &KWaylandServer::ScreencastV1Interface::virtualOutputScreencastRequested, this, &ScreencastManager::streamVirtualOutput);
connect(m_screencast, &KWaylandServer::ScreencastV1Interface::regionScreencastRequested, this, &ScreencastManager::streamRegion);
} }
class WindowStream : public ScreenCastStream class WindowStream : public ScreenCastStream
@ -143,6 +145,47 @@ void ScreencastManager::streamOutput(KWaylandServer::ScreencastStreamV1Interface
integrateStreams(waylandStream, stream); integrateStreams(waylandStream, stream);
} }
static QString rectToString(const QRect &rect)
{
return QStringLiteral("%1,%2 %3x%4").arg(rect.x()).arg(rect.y()).arg(rect.width()).arg(rect.height());
}
void ScreencastManager::streamRegion(KWaylandServer::ScreencastStreamV1Interface *waylandStream, const QRect &geometry, qreal scale, KWaylandServer::ScreencastV1Interface::CursorMode mode)
{
if (!geometry.isValid()) {
waylandStream->sendFailed(i18n("Invalid region"));
return;
}
auto source = new RegionScreenCastSource(geometry, scale);
auto stream = new ScreenCastStream(source, this);
stream->setObjectName(rectToString(geometry));
stream->setCursorMode(mode, scale, geometry);
connect(stream, &ScreenCastStream::startStreaming, waylandStream, [geometry, stream, source] {
Compositor::self()->scene()->addRepaint(geometry);
const auto allOutputs = kwinApp()->platform()->enabledOutputs();
for (auto output : allOutputs) {
AbstractWaylandOutput *streamOutput = qobject_cast<AbstractWaylandOutput *>(output);
if (streamOutput->geometry().intersects(geometry)) {
auto bufferToStream = [streamOutput, stream, source] (const QRegion &damagedRegion) {
if (damagedRegion.isEmpty()) {
return;
}
const QRect streamRegion = source->region();
const QRegion region = streamOutput->pixelSize() != streamOutput->modeSize() ? streamOutput->geometry() : damagedRegion;
source->updateOutput(streamOutput);
stream->recordFrame(region.translated(-streamRegion.topLeft()).intersected(streamRegion));
};
connect(streamOutput, &AbstractWaylandOutput::outputChange, stream, bufferToStream);
}
}
});
integrateStreams(waylandStream, stream);
}
void ScreencastManager::integrateStreams(KWaylandServer::ScreencastStreamV1Interface *waylandStream, ScreenCastStream *stream) void ScreencastManager::integrateStreams(KWaylandServer::ScreencastStreamV1Interface *waylandStream, ScreenCastStream *stream)
{ {
connect(waylandStream, &KWaylandServer::ScreencastStreamV1Interface::finished, stream, &ScreenCastStream::stop); connect(waylandStream, &KWaylandServer::ScreencastStreamV1Interface::finished, stream, &ScreenCastStream::stop);

View file

@ -36,6 +36,10 @@ private:
const QSize &size, const QSize &size,
double scale, double scale,
KWaylandServer::ScreencastV1Interface::CursorMode mode); KWaylandServer::ScreencastV1Interface::CursorMode mode);
void streamRegion(KWaylandServer::ScreencastStreamV1Interface *stream,
const QRect &geometry,
qreal scale,
KWaylandServer::ScreencastV1Interface::CursorMode mode);
void integrateStreams(KWaylandServer::ScreencastStreamV1Interface *waylandStream, ScreenCastStream *stream); void integrateStreams(KWaylandServer::ScreencastStreamV1Interface *waylandStream, ScreenCastStream *stream);