Linux(X11)窗口动态预览--获取指定wid的窗口图像并通过qml窗口显示

1. 主要功能

实现X11环境下,在qml中显示指定winId窗口的窗口动态预览图像

2. 编译环境

系统:Linux系统,X11窗口环境
编译依赖:Qt5 XCB OpenGL

3. 实现部分

捕获窗口图像

其中m_winId代表X11窗口的winId ,winId怎么获取自行百度,本文不做阐述。

    xcb_composite_query_version_cookie_t comp_ver_cookie = xcb_composite_query_version(c, 0, 2);
    xcb_composite_query_version_reply_t *comp_ver_reply = xcb_composite_query_version_reply(c, comp_ver_cookie, NULL);
    if (!comp_ver_reply)
    {
        // 返回窗口图标
        return;
    }

    const xcb_setup_t *setup = xcb_get_setup(c);
    xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup);
    xcb_screen_t *screen = screen_iter.data;
    // request redirection of window
    xcb_void_cookie_t cookie = xcb_composite_redirect_window_checked(c, m_winId, XCB_COMPOSITE_REDIRECT_AUTOMATIC);
    int win_h, win_w, win_d;

    xcb_get_geometry_cookie_t gg_cookie = xcb_get_geometry(c, m_winId);
    xcb_get_geometry_reply_t *gg_reply = xcb_get_geometry_reply(c, gg_cookie, NULL);
    if (gg_reply) {
        win_w = gg_reply->width;
        win_h = gg_reply->height;
        win_d = gg_reply->depth;
        free(gg_reply);
    } else {
        return;
    }

    // create a pixmap
    xcb_pixmap_t win_pixmap = xcb_generate_id(c);
    xcb_composite_name_window_pixmap(c, m_winId, win_pixmap);

    // get the image
    xcb_get_image_cookie_t gi_cookie = xcb_get_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, win_pixmap, 0, 0, win_w, win_h, (uint32_t)(~0UL));
    QSharedPointer<xcb_get_image_reply_t> gi_reply(xcb_get_image_reply(c, gi_cookie, NULL));
    if (gi_reply) {
        int data_len = xcb_get_image_data_length(gi_reply.data());
        uint8_t *data = xcb_get_image_data(gi_reply.data());
        if(win_d == 32) {
            QImage img((uchar *)data, win_w, win_h, QImage::Format_ARGB32_Premultiplied);
            image_data = img.copy();
        } else if(win_d == 24) {
            QImage img((uchar *)data, win_w, win_h, QImage::Format_RGB32);
            image_data = img.copy();
        }
    }

借助qml显示QImage窗口预览图像

主要思路:

  1. 构建WindowPreview类继承自QQuickItem;
  2. 注册winId属性, 可以由qml前段赋值;
  3. 借助OpenGL实现图片渲染;
  4. 注册qml组件并使用。

完整代码

windowpreview.h

#pragma once
#define TEST
// Qt
#include <QX11Info>
#include <QVector>
#include <QMatrix4x4>
// Qt Quick
#include <QQuickItem>
#include <QQuickWindow>
// Qt OpenGL
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>
#include <QOpenGLShaderProgram>
// xcb
#include <xcb/composite.h>

class OpenGLRender;
class WindowPreview : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(uint winId READ winId WRITE setWinId NOTIFY winIdChanged)
    
public:
    explicit WindowPreview(QQuickItem* parent = nullptr);

    uint32_t winId() const;
    void setWinId(uint32_t winId);

Q_SIGNALS:
    void winIdChanged();

protected slots:
    void Render();
    void OnWindowChanged(QQuickWindow* w);
    void Release();

protected:
    uint32_t m_winId;
    xcb_connection_t* c;
    QVector<GLfloat> data;
    QOpenGLBuffer VBO;
    QOpenGLShaderProgram program;
    QOpenGLTexture *texture = nullptr;
    QImage image_data;
    QColor background;
    
    bool InitGL();
    void setImage(QImage &image);
    void updateWindowPreview(); // 刷新窗口预览图像
    // test only
    void setImage(const QString &filename);
};

windowpreview.cpp

#include "windowpreview.h"

WindowPreview::WindowPreview(QQuickItem* parent)
    : QQuickItem(parent),
      background(QColor(255, 255, 255, 255)), // white background
      c(QX11Info::connection())
{
    data.resize(24);
    data = {
    -1.0f, 1.0f,0.0f,0.0f,
     1.0f, 1.0f,1.0f,0.0f,
     1.0f,-1.0f,1.0f,1.0f,
    -1.0f, 1.0f,0.0f,0.0f,
    -1.0f,-1.0f,0.0f,1.0f,
     1.0f,-1.0f,1.0f,1.0f
    };

    connect(this, &QQuickItem::windowChanged, this, &WindowPreview::OnWindowChanged);
    // test only
    setImage("/home/kylin/1.png");
}

uint32_t WindowPreview::winId() const
{
    return m_winId;
}

void WindowPreview::setWinId(uint32_t winid)
{
    m_winId = winid;
}

void WindowPreview::Render()
{
    static bool initGL = InitGL();
    Q_UNUSED(initGL);

    updateWindowPreview();
    QMatrix4x4 trans_;
    float scale_im = (float)image_data.width()/(float)image_data.height();
    float scale_form = (float)width()/(float)height();
    if(scale_im>scale_form)
        trans_.ortho(-1.0f,1.0f,-scale_im/scale_form,scale_im/scale_form,-1.0f,1.0f);
    else
        trans_.ortho(-scale_form/scale_im,scale_form/scale_im,-1.0f,1.0f,-1.0f,1.0f);
    program.setUniformValue("transform",trans_);
    if(!image_data.isNull() && texture) {
        delete texture;
        texture = new QOpenGLTexture(image_data);
        texture->bind();
    }
    glClear(GL_COLOR_BUFFER_BIT);
    glClearColor((GLfloat)background.red()/255.0,(GLfloat)background.green()/255.0,(GLfloat)background.blue()/255.0,(GLfloat)background.alpha()/255.0);
    glDrawArrays(GL_TRIANGLES,0,data.count());
    // only test
    window()->update();
}

void WindowPreview::OnWindowChanged(QQuickWindow* w)
{
    if (w == nullptr) return;
    connect(w, &QQuickWindow::beforeRendering, this, &WindowPreview::Render, Qt::DirectConnection);
    w->setClearBeforeRendering(false);
}

void WindowPreview::Release()
{
    VBO.destroy();
}

bool WindowPreview::InitGL()
{
    QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex,this);
    const char *vertexShaderSource =
    "#version 330 core\n"
    "layout(location=0)in vec2 aPos;\n"
    "layout(location=1)in vec2 aTexCoord;\n"
    "out vec2 TexCoord;\n"
    "uniform mat4 transform;\n"
    "void main()\n"
    "{\n"
    "gl_Position = transform * vec4(aPos,0.0f,1.0f);\n"
    "TexCoord = aTexCoord;\n"
    "}";
    vshader->compileSourceCode(vertexShaderSource);

    QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    const char *fsrc =
    "#version 330 core\n"
    "out vec4 FragColor;\n"
    "in vec2 TexCoord;\n"
    "uniform sampler2D ourTexture;\n"
    "void main()\n"
    "{FragColor = texture(ourTexture, TexCoord);}";
    fshader->compileSourceCode(fsrc);


    program.addShader(vshader);
    program.addShader(fshader);
    program.bindAttributeLocation("ourTexture",0);
    program.bindAttributeLocation("transform",1);
    program.link();
    program.bind();

    VBO.create();

    VBO.bind();
    VBO.allocate(data.data(),data.count()*sizeof (GLfloat));

    program.enableAttributeArray(0);
    program.setAttributeBuffer(0,GL_FLOAT,0,2,4*sizeof (GLfloat));

    program.enableAttributeArray(1);
    program.setAttributeBuffer(1,GL_FLOAT,2*sizeof (GLfloat),2,4*sizeof (GLfloat));
    // set texture filter
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    connect(window()->openglContext(), &QOpenGLContext::aboutToBeDestroyed, this, &WindowPreview::Release, Qt::DirectConnection);
    return true;
}

void WindowPreview::setImage(QImage &image)
{
    image_data = image.mirrored();
}

void WindowPreview::updateWindowPreview()
{
    xcb_composite_query_version_cookie_t comp_ver_cookie = xcb_composite_query_version(c, 0, 2);
    xcb_composite_query_version_reply_t *comp_ver_reply = xcb_composite_query_version_reply(c, comp_ver_cookie, NULL);
    if (!comp_ver_reply)
    {
        // 返回窗口图标
        return;
    }

    const xcb_setup_t *setup = xcb_get_setup(c);
    xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup);
    xcb_screen_t *screen = screen_iter.data;
    // request redirection of window
    xcb_void_cookie_t cookie = xcb_composite_redirect_window_checked(c, m_winId, XCB_COMPOSITE_REDIRECT_AUTOMATIC);
    int win_h, win_w, win_d;

    xcb_get_geometry_cookie_t gg_cookie = xcb_get_geometry(c, m_winId);
    xcb_get_geometry_reply_t *gg_reply = xcb_get_geometry_reply(c, gg_cookie, NULL);
    if (gg_reply) {
        win_w = gg_reply->width;
        win_h = gg_reply->height;
        win_d = gg_reply->depth;
        free(gg_reply);
    } else {
        return;
    }

    // create a pixmap
    xcb_pixmap_t win_pixmap = xcb_generate_id(c);
    xcb_composite_name_window_pixmap(c, m_winId, win_pixmap);

    // get the image
    xcb_get_image_cookie_t gi_cookie = xcb_get_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, win_pixmap, 0, 0, win_w, win_h, (uint32_t)(~0UL));
    QSharedPointer<xcb_get_image_reply_t> gi_reply(xcb_get_image_reply(c, gi_cookie, NULL));
    if (gi_reply) {
        int data_len = xcb_get_image_data_length(gi_reply.data());
        uint8_t *data = xcb_get_image_data(gi_reply.data());
        if(win_d == 32) {
            QImage img((uchar *)data, win_w, win_h, QImage::Format_ARGB32_Premultiplied);
            image_data = img.copy();
        } else if(win_d == 24) {
            QImage img((uchar *)data, win_w, win_h, QImage::Format_RGB32);
            image_data = img.copy();
        }
    }
    window()->update();
}

// test only
void WindowPreview::setImage(const QString &filename)
{
    if(texture) delete texture;
    image_data = QImage(filename).mirrored();
    texture = new QOpenGLTexture(image_data);
}

4. 使用方法

利用qmlRegisterType函数注册组件

qmlRegisterType<WindowPreview>("MyControls", 1, 0, "WindowPreview");

在qml文件中导入组件

import MyControls 1.0

使用组件

WindowPreview {
	winId: 8388613
}

5. 完整代码与使用示例

代码链接:https://gitee.com/upcliujie/windowpreview

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只双鱼儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值