首页 > *nix技术 > 通过Xcb截屏

通过Xcb截屏

2022年11月12日 发表评论 阅读评论 195 次浏览

一,测试环境
硬件:VirtualBox 7.0
操作系统:
$ cat /etc/issue
Ubuntu 20.04.3 LTS \n \l
$ uname -a
Linux lenky-VirtualBox 5.11.0-25-generic #27~20.04.1-Ubuntu SMP Tue Jul 13 17:41:23 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

二,测试
代码如下:

// filename: main.c
// sudo apt install gcc
// sudo apt install libpng-dev
// sudo apt install libxcb-xfixes0-dev
// gcc xcb_capture.c -o screenshot -lxcb -lxcb-xfixes -lpng -O2 -g

#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <string.h>

#include <xcb/xcb.h>
#include <xcb/xfixes.h>
#include <xcb/xproto.h>

#include <png.h>

#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
#define BLEND(target, source, alpha) \
    (target) + ((source) * (255 - (alpha)) + 255 / 2) / 255
    
typedef struct xcb_info
{
    xcb_connection_t *con;
    xcb_drawable_t   drawable;
    xcb_get_image_reply_t  *img;
}xcb_info;

xcb_info xcb_info_i;

#define LSBFirst 0

typedef struct xcb_png_info
{
    int width;
    int height;
    int depth;
    int bytes_per_line;

    unsigned char *data;
    int byte_order;
    
}xcb_png_info;

xcb_png_info xcb_png_info_i;


static char *g_display = ":0";

/* LSBFirst: BGRA -> RGBA */
static void convertrow_lsb(unsigned char *drow, unsigned char *srow, xcb_png_info *img)
{
    int sx, dx;
    for(sx = 0, dx = 0; dx < img->bytes_per_line; sx += 4) {
        drow[dx++] = srow[sx + 2]; /* B -> R */
        drow[dx++] = srow[sx + 1]; /* G -> G */
        drow[dx++] = srow[sx];     /* R -> B */
        if (img->depth == 32) drow[dx++] = srow[sx + 3]; /* A -> A */
        else                  drow[dx++] = 255;
    }
}

/* MSBFirst: ARGB -> RGBA */
static void convertrow_msb(unsigned char *drow, unsigned char *srow, xcb_png_info *img)
{
    int sx, dx;
    for(sx = 0, dx = 0; dx < img->bytes_per_line; sx += 4) {
        drow[dx++] = srow[sx + 1]; /* G -> R */
        drow[dx++] = srow[sx + 2]; /* B -> G */
        drow[dx++] = srow[sx + 3]; /* A -> B */
        if (img->depth == 32) drow[dx++] = srow[sx]; /* R -> A */
        else                  drow[dx++] = 255;
    }
}

static void writeaspng(xcb_png_info *img, FILE *fp)
{
    png_structp png_struct_p;
    png_infop png_info_p;
    void (*convert)(unsigned char *, unsigned char *, xcb_png_info *);
    unsigned char *drow = NULL, *srow;
    int h;
    png_struct_p = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    png_info_p = png_create_info_struct(png_struct_p);
    png_init_io(png_struct_p, fp);
    png_set_IHDR(
        png_struct_p, png_info_p, img->width, img->height, 8,
        PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
        PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE
    );          
    png_write_info(png_struct_p, png_info_p);
    srow = (unsigned char *)img->data;
    drow = calloc(1, img->width * 4);
    if(img->byte_order == LSBFirst) convert = convertrow_lsb;
    else                            convert = convertrow_msb;
    for(h = 0; h < img->height; ++h) {
        convert(drow, srow, img);
        srow += img->bytes_per_line;
        png_write_row(png_struct_p, drow);
    }
    png_write_end(png_struct_p, NULL);
    free(drow);
    png_free_data(png_struct_p, png_info_p, PNG_FREE_ALL, -1);
    png_destroy_write_struct(&png_struct_p, NULL);
}

static xcb_screen_t *get_screen(const xcb_setup_t *setup, int screen_num)
{
    xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup);
    xcb_screen_t *screen = NULL;

    for(;it.rem > 0; xcb_screen_next(&it))
    {
        if(!screen_num)
        {
            screen = it.data;
            break;
        }
        screen_num--;
    }
    return screen;
}

//check draw mouse event
static int check_xfixes(xcb_connection_t *con)
{
    xcb_xfixes_query_version_cookie_t cookie;
    xcb_xfixes_query_version_reply_t *reply;
    
    cookie = xcb_xfixes_query_version(con, XCB_XFIXES_MAJOR_VERSION,
            XCB_XFIXES_MINOR_VERSION);
    reply  = xcb_xfixes_query_version_reply(con, cookie, NULL);

    if(reply)
    {
        free(reply);
        return 1;
    }
    return 0;
}

static void xcbgrab_draw_mouse(xcb_connection_t *con, unsigned char *data_buff,
        int pic_x, int pic_y, int pic_width, int pic_height)
{
    unsigned int *cursor;
    int length;
    xcb_xfixes_get_cursor_image_cookie_t cc;
    xcb_xfixes_get_cursor_image_reply_t *ci;
    int cx, cy, x, y, w, h, c_off, i_off;

    cc = xcb_xfixes_get_cursor_image(con);
    ci = xcb_xfixes_get_cursor_image_reply(con, cc, NULL);
    if(!ci)
        return;

    cursor = xcb_xfixes_get_cursor_image_cursor_image(ci);
    length = xcb_xfixes_get_cursor_image_cursor_image_length(ci);
    
    if(!cursor)
        return;

    cx = ci->x - ci->xhot;
    cy = ci->y - ci->yhot;

    x = FFMAX(cx, pic_x);
    y = FFMAX(cy, pic_y);

    w = FFMIN(cx + ci->width, pic_x + pic_width) - x;
    h = FFMIN(cy + ci->height, pic_y + pic_height) - y;

    c_off = x - cx;
    i_off = x - pic_x;
    
    cursor += (y - cy) * ci->width;
    data_buff += (y - pic_y) * pic_width * 4;           //4 represents the size of a pixel
    
    for(y = 0; y < h; y++)
    {
        cursor += c_off;
        data_buff += i_off * 4;
        for(x = 0; x < w; x++, cursor++, data_buff += 4)
        {
            int r, g, b, a;
            
            r = *cursor         & 0xff;
            g = (*cursor >> 8)  & 0xff;
            b = (*cursor >> 16) & 0xff;
            a = (*cursor >> 24) & 0xff;

            if(!a)
                continue;

            if(a == 255)
            {
                data_buff[0] = r;
                data_buff[1] = g;
                data_buff[2] = b;
            } else
            {
                data_buff[0] = BLEND(r, data_buff[0], a);
                data_buff[1] = BLEND(g, data_buff[1], a);
                data_buff[2] = BLEND(b, data_buff[2], a);
            }
        }
        cursor += ci->width - w - c_off;
        data_buff += (pic_width - w - i_off) * 4;
    }

    free(ci);

//    return length;
}

void init_capture(int *width, int *height, int *stride)
{
    xcb_screen_t       *screen;
    const xcb_setup_t  *setup;
    const xcb_format_t *fmt;
    int                screen_num;
    int                bpp = 0;
    int                length;
    /*
    char *display = getenv("DISPLAY");
    if (display == NULL) {
    	display = ":0";
    }
    printf("Info: display: %s\n", display);
    */

    xcb_info_i.con = xcb_connect(g_display, &screen_num);
    setup = xcb_get_setup(xcb_info_i.con);
    screen = get_screen(setup, screen_num);

    fmt = xcb_setup_pixmap_formats(setup);
    length = xcb_setup_pixmap_formats_length(setup);
    while (length--)
    {
        if (screen->root_depth == fmt->depth)
        {
            bpp = fmt->bits_per_pixel;
            break;
        }
        fmt++;
    }

    *width = screen->width_in_pixels;
    *height = screen->height_in_pixels;
    *stride = (*width) * (bpp >> 3);
    xcb_info_i.drawable = screen->root;

    xcb_png_info_i.width = (*width);
    xcb_png_info_i.height = (*height);
    xcb_png_info_i.depth = fmt->depth;
    xcb_png_info_i.bytes_per_line = *stride;
    xcb_png_info_i.byte_order = setup->image_byte_order;

}

void capture(unsigned char **buf, int *size_buf, int width, int height)
{
    xcb_generic_error_t    *err = NULL;
    xcb_get_image_cookie_t iq;
    int ret_mouse;

    if (xcb_info_i.img)
    {
        free(xcb_info_i.img);
        xcb_info_i.img = NULL;
    }

    iq = xcb_get_image(
        xcb_info_i.con, XCB_IMAGE_FORMAT_Z_PIXMAP,
        xcb_info_i.drawable, 0, 0,
        width, height, ~0);
    xcb_info_i.img = xcb_get_image_reply(xcb_info_i.con, iq, &err);
    if (err)
    {
        printf("get image failed\n");
        return;
    }

    *buf = xcb_get_image_data(xcb_info_i.img);
    *size_buf = xcb_get_image_data_length(xcb_info_i.img);
    ret_mouse = check_xfixes(xcb_info_i.con);
    if (!ret_mouse)
    {
        printf("connot draw the mouse\n");
        return;
    }
/*
    xcbgrab_draw_mouse(xcb_info_i.con, *buf,
        0, 0, width, height);
*/
    xcb_png_info_i.data = *buf;
    
}

void destory_capture()
{
    if (xcb_info_i.img)
    {
        free(xcb_info_i.img);
        xcb_info_i.img = NULL;
    }
    if(xcb_info_i.con)
    {
        xcb_disconnect(xcb_info_i.con);
        xcb_info_i.con = NULL;
    }
}

#define CLOCK_NSEC (1000000000.0)
double get_time_clock() 
{
    struct timespec now_time;
    clock_gettime(CLOCK_REALTIME, &now_time);
    return (double) (now_time.tv_sec * CLOCK_NSEC) + now_time.tv_nsec;
}

void sleep_time_clock(double t)
{
    struct timespec ts;
    ts.tv_sec = (int)(t/CLOCK_NSEC);
    ts.tv_nsec = (t - ts.tv_sec * CLOCK_NSEC);

    while (nanosleep(&ts, &ts) == -1 && errno == EINTR) {}
}

unsigned char *save_buf[1000];
unsigned char save_buf_name[1000][64];
    
int main(int argc, char **argv) 
{
    if (argc != 4) {
        printf("Usage: app display(:0) fps(60, max:-1) write_number(0, max:1000)\n");
        exit(-1);
    }
    
    g_display = argv[1];
    int fps = atoi(argv[2]);
    if (fps <= 0) {
        fps = 999999;
    }
    printf("Info: target fps: %d\n", fps);

    int write_number = atoi(argv[3]);
    if (write_number <= 0) {
        write_number = -1;
    }
    printf("Info: capture png file number: %d\n", write_number);
    
    int width;
    int height;
    int stride;
    init_capture(&width, &height, &stride);

    int capture_count = 0;
    double capture_time_now = get_time_clock();
    double capture_time_last = capture_time_now;

    double capture_time_gap = (CLOCK_NSEC/fps);
    double capture_time_gap_last = capture_time_now;
    double time_gap_surplus = 0;
    
    int save_buf_count = 0;
    int save_buf_flag = 0;
    if (write_number > 0 && write_number <= 1000) {
        printf("Info: capture png file number(%d) will save to file!\n", write_number);
        save_buf_flag = 1;
    } else if (write_number > 1000) {
        printf("Info: capture png file number larger than 1000, cannot save to file!\n");
    }
    
    while (write_number--) {
        unsigned char *buf;
        int size_buf;
        capture(&buf, &size_buf, width, height);

        if(save_buf_flag == 1) {
            save_buf[save_buf_count] = (unsigned char *)malloc(size_buf);
            memcpy(save_buf[save_buf_count], buf, size_buf);
            
            sprintf(save_buf_name[save_buf_count], "%f.png", capture_time_now);
            save_buf_count++;
        }

/*
        char filename[64];
        sprintf(filename, "%f.png", capture_time_now);
        //printf("Info: filename is %s\n", filename);
        
        FILE *fp;
        fp = fopen(filename, "wb");
        writeaspng(&xcb_png_info_i, fp);
        fclose(fp);
*/
        capture_time_now = get_time_clock();
        time_gap_surplus = capture_time_gap - (capture_time_now - capture_time_gap_last);
        if (time_gap_surplus > 0) {
            sleep_time_clock(time_gap_surplus);
        }
        capture_time_now = get_time_clock();
        capture_time_gap_last = capture_time_now;

        if (capture_time_now - capture_time_last >= CLOCK_NSEC) {
            printf("Info: target fps: %d, current fps: %d\n", fps, capture_count);
            capture_time_last = capture_time_now;
            capture_count = 0;
        }

        capture_count ++;

//        break;
    }
    
    if(save_buf_flag == 1) {
        printf("capture png file start save to file!\n");
        for (int i = 0; i < save_buf_count; i++) {
        
            FILE *fp;
            fp = fopen(save_buf_name[i], "wb");

            xcb_png_info_i.data = save_buf[i];

            writeaspng(&xcb_png_info_i, fp);
            fclose(fp);
            
            free(save_buf[i]);
        }
    }
    

    destory_capture();
}

编译:
$ sudo apt-get install libxcb-xfixes0-dev
$ sudo apt-get install libpng-dev
$ gcc main.c -o screenshot -lxcb -lxcb-xfixes -lpng -O0 -g
$ ./screenshot
Info: filename is 1667881128.png

一切ok。

over~

—-
三,更新20221119
xcb_get_image vs xcb_shm_get_image

xcb_get_image

https://www.linuxhowtos.org/manpages/3/xcb_get_image.htm

#include
Request function
xcb_get_image_cookie_t xcb_get_image(xcb_connection_t *conn, uint8_t format, xcb_drawable_t drawable, int16_t x, int16_t y, uint16_t width, uint16_t height, uint32_t plane_mask);

xcb_shm_get_image

https://www.linuxhowtos.org/manpages/3/xcb_shm_get_image.htm

#include
Request function
xcb_shm_get_image_cookie_t xcb_shm_get_image(xcb_connection_t *conn, xcb_drawable_t drawable, int16_t x, int16_t y, uint16_t width, uint16_t height, uint32_t plane_mask, uint8_t format, xcb_shm_seg_t shmseg, uint32_t offset);

关于这两个函数的对比使用,可以参考这个:

https://github.com/borzunov/remoteink/blob/master/server/screen.c

关于shm介绍,参考这个:

https://x.org/releases/X11R7.7/doc/xextproto/shm.html

https://x.org/releases/X11R7.7/doc/xextproto/

https://x.org/releases/X11R7.7/doc/

参考:

https://github.com/SollyBunny/screenshot

C windows和linux截屏:https://blog.csdn.net/weixin_46840784/article/details/125424113
在C或C++中合成WM时从X11请求图像:https://cloud.tencent.com/developer/ask/sof/714425
2021-08-21窗口管理器杂谈:https://www.jianshu.com/p/c49fc6c1b03e

xcb和pipewire截屏:

https://github.com/gnif/LookingGlass/tree/master/host/platform/Linux/capture

https://itcodet.com/cpp/cpp-xcb_get_image_data_length-function-examples.html

https://gitlab.freedesktop.org/xorg/lib/libxcb-image

https://tronche.com/gui/x/xlib/graphics/images.html

https://www.x.org/releases/X11R7.7/doc/libxcb/tutorial/index.html

转载请保留地址:http://www.lenky.info/archives/2022/11/3225http://lenky.info/?p=3225


备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。

法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。

分类: *nix技术 标签: ,
  1. 本文目前尚无任何评论.
您必须在 登录 后才能发布评论.