screencasting: Support regions from the v3 of the protocol
This commit is contained in:
parent
2139e2abab
commit
f0ce2a0f53
5 changed files with 209 additions and 0 deletions
|
@ -4,6 +4,7 @@ target_sources(KWinScreencastPlugin PRIVATE
|
|||
main.cpp
|
||||
outputscreencastsource.cpp
|
||||
pipewirecore.cpp
|
||||
regionscreencastsource.cpp
|
||||
screencastmanager.cpp
|
||||
screencastsource.cpp
|
||||
screencaststream.cpp
|
||||
|
|
115
src/plugins/screencast/regionscreencastsource.cpp
Normal file
115
src/plugins/screencast/regionscreencastsource.cpp
Normal 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 ®ion, 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);
|
||||
}
|
||||
|
||||
}
|
46
src/plugins/screencast/regionscreencastsource.h
Normal file
46
src/plugins/screencast/regionscreencastsource.h
Normal 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 ®ion, 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
|
|
@ -14,6 +14,7 @@
|
|||
#include "effects.h"
|
||||
#include "kwingltexture.h"
|
||||
#include "outputscreencastsource.h"
|
||||
#include "regionscreencastsource.h"
|
||||
#include "screencaststream.h"
|
||||
#include "platform.h"
|
||||
#include "scene.h"
|
||||
|
@ -37,6 +38,7 @@ ScreencastManager::ScreencastManager(QObject *parent)
|
|||
this, &ScreencastManager::streamWindow);
|
||||
connect(m_screencast, &KWaylandServer::ScreencastV1Interface::outputScreencastRequested, this, &ScreencastManager::streamWaylandOutput);
|
||||
connect(m_screencast, &KWaylandServer::ScreencastV1Interface::virtualOutputScreencastRequested, this, &ScreencastManager::streamVirtualOutput);
|
||||
connect(m_screencast, &KWaylandServer::ScreencastV1Interface::regionScreencastRequested, this, &ScreencastManager::streamRegion);
|
||||
}
|
||||
|
||||
class WindowStream : public ScreenCastStream
|
||||
|
@ -143,6 +145,47 @@ void ScreencastManager::streamOutput(KWaylandServer::ScreencastStreamV1Interface
|
|||
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)
|
||||
{
|
||||
connect(waylandStream, &KWaylandServer::ScreencastStreamV1Interface::finished, stream, &ScreenCastStream::stop);
|
||||
|
|
|
@ -36,6 +36,10 @@ private:
|
|||
const QSize &size,
|
||||
double scale,
|
||||
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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue