8#include "pipewiresourceitem.h" 
   11#include "pipewiresourcestream.h" 
   14#include <QGuiApplication> 
   15#include <QOpenGLContext> 
   16#include <QOpenGLTexture> 
   18#include <QQuickWindow> 
   20#include <QSGImageNode> 
   21#include <QSocketNotifier> 
   23#include <qpa/qplatformnativeinterface.h> 
   25#include <EGL/eglext.h> 
   27#include <libdrm/drm_fourcc.h> 
   32    pw_init(
nullptr, 
nullptr);
 
   34Q_COREAPP_STARTUP_FUNCTION(pwInit);
 
   36class PipeWireSourceItemPrivate
 
   40    std::optional<uint> m_fd;
 
   41    std::function<QSGTexture *()> m_createNextTexture;
 
   42    std::unique_ptr<PipeWireSourceStream> m_stream;
 
   43    std::unique_ptr<QOpenGLTexture> m_texture;
 
   45    EGLImage m_image = 
nullptr;
 
   46    bool m_needsRecreateTexture = 
false;
 
   47    bool m_allowDmaBuf = 
true;
 
   52        std::optional<QPoint> position;
 
   56    std::optional<QRegion> m_damage;
 
   59class DiscardEglPixmapRunnable : 
public QRunnable 
   62    DiscardEglPixmapRunnable(EGLImageKHR image, QOpenGLTexture *texture)
 
   70        if (m_image != EGL_NO_IMAGE_KHR) {
 
   71            eglDestroyImageKHR(eglGetCurrentDisplay(), m_image);
 
   78    const EGLImageKHR m_image;
 
   79    QOpenGLTexture *m_texture;
 
   82PipeWireSourceItem::PipeWireSourceItem(
QQuickItem *parent)
 
   84    , d(new PipeWireSourceItemPrivate)
 
   86    setFlag(ItemHasContents, 
true);
 
   89PipeWireSourceItem::~PipeWireSourceItem()
 
  108        d->m_needsRecreateTexture = 
true;
 
  118void PipeWireSourceItem::releaseResources()
 
  120    if (
window() && (d->m_image || d->m_texture)) {
 
  122        d->m_image = EGL_NO_IMAGE_KHR;
 
  126void PipeWireSourceItem::setFd(uint fd)
 
  139void PipeWireSourceItem::resetFd()
 
  141    if (!d->m_fd.has_value()) {
 
  148    d->m_stream.reset(
nullptr);
 
  149    d->m_createNextTexture = [] {
 
  152    Q_EMIT streamSizeChanged();
 
  155void PipeWireSourceItem::refresh()
 
  163    if (d->m_nodeId == 0) {
 
  165        d->m_stream.reset(
nullptr);
 
  166        Q_EMIT streamSizeChanged();
 
  168        d->m_createNextTexture = [] {
 
  172        d->m_stream.reset(
new PipeWireSourceStream(
this));
 
  173        d->m_stream->setAllowDmaBuf(d->m_allowDmaBuf);
 
  174        Q_EMIT streamSizeChanged();
 
  175        connect(d->m_stream.get(), &PipeWireSourceStream::streamParametersChanged, 
this, &PipeWireSourceItem::streamSizeChanged);
 
  176        connect(d->m_stream.get(), &PipeWireSourceStream::streamParametersChanged, 
this, &PipeWireSourceItem::usingDmaBufChanged);
 
  178        const bool created = d->m_stream->createStream(d->m_nodeId, d->m_fd.value_or(0));
 
  179        if (!created || !d->m_stream->error().isEmpty()) {
 
  180            d->m_stream.reset(
nullptr);
 
  186        connect(d->m_stream.get(), &PipeWireSourceStream::frameReceived, 
this, &PipeWireSourceItem::processFrame);
 
  187        connect(d->m_stream.get(), &PipeWireSourceStream::stateChanged, 
this, &PipeWireSourceItem::stateChanged);
 
  192void PipeWireSourceItem::setNodeId(uint nodeId)
 
  194    if (nodeId == d->m_nodeId)
 
  197    d->m_nodeId = nodeId;
 
  199    Q_EMIT nodeIdChanged(nodeId);
 
  202class PipeWireRenderNode : 
public QSGNode 
  205    QSGImageNode *screenNode(QQuickWindow *window)
 
  208            m_screenNode = 
window->createImageNode();
 
  213    QSGImageNode *cursorNode(QQuickWindow *window)
 
  216            m_cursorNode = 
window->createImageNode();
 
  222    QSGImageNode *damageNode(QQuickWindow *window)
 
  225            m_damageNode = 
window->createImageNode();
 
  236            m_cursorNode = 
nullptr;
 
  245            m_damageNode = 
nullptr;
 
  250    QSGImageNode *m_screenNode = 
nullptr;
 
  251    QSGImageNode *m_cursorNode = 
nullptr;
 
  252    QSGImageNode *m_damageNode = 
nullptr;
 
  255QSGNode *PipeWireSourceItem::updatePaintNode(
QSGNode *node, QQuickItem::UpdatePaintNodeData *)
 
  257    if (Q_UNLIKELY(!d->m_createNextTexture)) {
 
  261    auto texture = d->m_createNextTexture();
 
  267    auto pwNode = 
static_cast<PipeWireRenderNode *
>(node);
 
  269        pwNode = 
new PipeWireRenderNode;
 
  272    QSGImageNode *screenNode = pwNode->screenNode(
window());
 
  278    rect.moveCenter(br.center());
 
  281    if (!d->m_cursor.position.has_value() || d->m_cursor.texture.isNull()) {
 
  282        pwNode->discardCursor();
 
  284        QSGImageNode *cursorNode = pwNode->cursorNode(
window());
 
  285        if (d->m_cursor.dirty || !cursorNode->
texture()) {
 
  286            cursorNode->
setTexture(
window()->createTextureFromImage(d->m_cursor.texture));
 
  288            d->m_cursor.dirty = 
false;
 
  290        const qreal 
scale = qreal(rect.width()) / texture->textureSize().width();
 
  291        cursorNode->
setRect(QRectF{rect.topLeft() + (d->m_cursor.position.value() * 
scale), d->m_cursor.texture.size() * 
scale});
 
  292        Q_ASSERT(cursorNode->
texture());
 
  295    if (!d->m_damage || d->m_damage->isEmpty()) {
 
  296        pwNode->discardDamage();
 
  298        auto *damageNode = pwNode->damageNode(
window());
 
  301        QPainter p(&damageImage);
 
  303        for (
auto rect : *d->m_damage) {
 
  306        damageNode->setTexture(
window()->createTextureFromImage(damageImage));
 
  307        damageNode->setOwnsTexture(
true);
 
  308        damageNode->setRect(rect);
 
  309        Q_ASSERT(damageNode->texture());
 
  314QString PipeWireSourceItem::error()
 const 
  316    return d->m_stream->error();
 
  319void PipeWireSourceItem::processFrame(
const PipeWireFrame &frame)
 
  321    d->m_damage = frame.damage;
 
  324        d->m_cursor.position = frame.cursor->position;
 
  325        d->m_cursor.hotspot = frame.cursor->hotspot;
 
  326        if (!frame.cursor->texture.isNull()) {
 
  327            d->m_cursor.dirty = 
true;
 
  328            d->m_cursor.texture = frame.cursor->texture;
 
  331        d->m_cursor.position = std::nullopt;
 
  332        d->m_cursor.hotspot = {};
 
  336        updateTextureDmaBuf(*frame.dmabuf, frame.format);
 
  337    } 
else if (frame.dataFrame) {
 
  338        updateTextureImage(frame.dataFrame);
 
  346void PipeWireSourceItem::updateTextureDmaBuf(
const DmaBufAttributes &attribs, spa_video_format format)
 
  349        qCWarning(PIPEWIRE_LOGGING) << 
"Window not available" << 
this;
 
  354    if (!openglContext || !d->m_stream) {
 
  355        qCWarning(PIPEWIRE_LOGGING) << 
"need a window and a context" << 
window();
 
  359    d->m_createNextTexture = [
this, format, attribs]() -> QSGTexture * {
 
  360        const EGLDisplay display = 
static_cast<EGLDisplay
>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration(
"egldisplay"));
 
  362            eglDestroyImageKHR(display, d->m_image);
 
  364        const auto size = d->m_stream->size();
 
  365        d->m_image = GLHelpers::createImage(display, attribs, PipeWireSourceStream::spaVideoFormatToDrmFormat(format), 
size, 
nullptr);
 
  366        if (d->m_image == EGL_NO_IMAGE_KHR) {
 
  369                [
this, format, attribs]() {
 
  370                    d->m_stream->renegotiateModifierFailed(format, attribs.modifier);
 
  377            bool created = d->m_texture->create();
 
  381        GLHelpers::initDebugOutput();
 
  382        d->m_texture->bind();
 
  384        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)d->m_image);
 
  388        d->m_texture->release();
 
  389        d->m_texture->setSize(
size.width(), 
size.height());
 
  391        int textureId = d->m_texture->textureId();
 
  400void PipeWireSourceItem::updateTextureImage(
const std::shared_ptr<PipeWireFrameData> &data)
 
  403        qCWarning(PIPEWIRE_LOGGING) << 
"pass";
 
  407    d->m_createNextTexture = [
this, data] {
 
  414void PipeWireSourceItem::componentComplete()
 
  417    if (d->m_nodeId != 0) {
 
  422PipeWireSourceItem::StreamState PipeWireSourceItem::state()
 const 
  425        return StreamState::Unconnected;
 
  427    switch (d->m_stream->state()) {
 
  428    case PW_STREAM_STATE_ERROR:
 
  429        return StreamState::Error;
 
  430    case PW_STREAM_STATE_UNCONNECTED:
 
  431        return StreamState::Unconnected;
 
  432    case PW_STREAM_STATE_CONNECTING:
 
  433        return StreamState::Connecting;
 
  434    case PW_STREAM_STATE_PAUSED:
 
  435        return StreamState::Paused;
 
  436    case PW_STREAM_STATE_STREAMING:
 
  437        return StreamState::Streaming;
 
  439        return StreamState::Error;
 
  443uint PipeWireSourceItem::fd()
 const 
  445    return d->m_fd.value_or(0);
 
  448uint PipeWireSourceItem::nodeId()
 const 
  453QSize PipeWireSourceItem::streamSize()
 const 
  458    return d->m_stream->size();
 
  461bool PipeWireSourceItem::usingDmaBuf()
 const 
  463    return d->m_stream && d->m_stream->usingDmaBuf();
 
  466bool PipeWireSourceItem::allowDmaBuf()
 const 
  468    return d->m_stream && d->m_stream->allowDmaBuf();
 
  471void PipeWireSourceItem::setAllowDmaBuf(
bool allowed)
 
  473    d->m_allowDmaBuf = allowed;
 
  475        d->m_stream->setAllowDmaBuf(allowed);
 
  479void PipeWireSourceItem::setReady(
bool ready)
 
  481    if (d->m_ready != ready) {
 
  487bool PipeWireSourceItem::isReady()
 const 
  492#include "moc_pipewiresourceitem.cpp" 
const QList< QKeySequence > & close()
 
QSGTexture * fromNative(GLuint textureId, QQuickWindow *window, const QSize &size, QQuickWindow::CreateTextureOptions options)
 
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
 
virtual QRectF boundingRect() const const
 
virtual void componentComplete() override
 
bool isComponentComplete() const const
 
virtual void itemChange(ItemChange change, const ItemChangeData &value)
 
QSizeF size() const const
 
bool isVisible() const const
 
QQuickWindow * window() const const
 
QSGTexture * createTextureFromImage(const QImage &image) const const
 
QSGRendererInterface * rendererInterface() const const
 
void scheduleRenderJob(QRunnable *job, RenderStage stage)
 
QRect toRect() const const
 
virtual void setOwnsTexture(bool owns)=0
 
virtual void setRect(const QRectF &rect)=0
 
virtual void setTexture(QSGTexture *texture)=0
 
virtual QSGTexture * texture() const const=0
 
void appendChildNode(QSGNode *node)
 
void removeChildNode(QSGNode *node)
 
virtual void * getResource(QQuickWindow *window, Resource resource) const const