通过Xcb截屏
一,测试环境
硬件: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/3225 或 http://lenky.info/?p=3225
备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来信讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。
法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。