如何使用xcb在纯C语言中实现截图?
嘿,我之前也为了追求截图速度放弃过Cairo,转而用XCB实现,完全懂你找不到示例的痛苦!下面直接上纯C的实现方案,从核心逻辑到完整代码都给你理清楚:
纯XCB截图的核心逻辑
XCB的截图本质是借助X服务器的像素复制能力,流程非常直接:
- 建立与X服务器的连接
- 获取目标屏幕的根窗口和尺寸信息
- 创建一个Pixmap(X服务器端的像素缓冲区)
- 将根窗口的像素数据复制到Pixmap中
- 从Pixmap读取原始像素数据到客户端内存
- 清理资源
这个流程全程是X服务器端操作+直接内存读取,比Cairo的客户端渲染快不少,完全能满足高性能需求。
完整代码示例
下面是可直接运行的纯C代码,包含基础错误处理和可选的PNG保存逻辑:
#include <stdio.h> #include <stdlib.h> #include <xcb/xcb.h> // 如果需要保存PNG,取消下面的注释并链接libpng // #include <png.h> int main() { // 1. 连接到X服务器 xcb_connection_t *conn = xcb_connect(NULL, NULL); if (xcb_connection_has_error(conn)) { fprintf(stderr, "无法连接到X服务器\n"); return 1; } // 2. 获取默认屏幕、根窗口和屏幕尺寸 const xcb_setup_t *setup = xcb_get_setup(conn); xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup); xcb_screen_t *screen = screen_iter.data; xcb_window_t root_window = screen->root; uint16_t screen_width = screen->width_in_pixels; uint16_t screen_height = screen->height_in_pixels; // 3. 创建用于存储截图的Pixmap xcb_pixmap_t screenshot_pixmap = xcb_create_pixmap( conn, screen->root_depth, root_window, screen_width, screen_height ); // 4. 将根窗口的像素复制到Pixmap(X服务器端操作,速度极快) xcb_copy_area( conn, root_window, // 源窗口 screenshot_pixmap, // 目标Pixmap screen->root_visual, // 视觉格式 0, 0, // 源窗口起始坐标 0, 0, // 目标Pixmap起始坐标 screen_width, screen_height // 复制区域尺寸 ); // 强制发送请求并等待X服务器处理完成 xcb_flush(conn); // 5. 从Pixmap读取像素数据到客户端内存(ZPixmap格式,像素连续存储) xcb_get_image_cookie_t image_cookie = xcb_get_image( conn, XCB_IMAGE_FORMAT_Z_PIXMAP, screenshot_pixmap, 0, 0, screen_width, screen_height, ~0 // 获取所有颜色位 ); xcb_get_image_reply_t *image_reply = xcb_get_image_reply(conn, image_cookie, NULL); if (!image_reply) { fprintf(stderr, "获取图像数据失败\n"); xcb_free_pixmap(conn, screenshot_pixmap); xcb_disconnect(conn); return 1; } // 原始像素数据指针(紧跟在image_reply结构体之后) uint8_t *pixel_data = xcb_get_image_data(image_reply); int bytes_per_pixel = image_reply->depth / 8; printf("截图成功:%dx%d,每个像素占用%d字节\n", screen_width, screen_height, bytes_per_pixel); // --- 可选:将像素数据保存为PNG --- // 注意:XCB的ZPixmap默认是BGR/BGRA顺序,需要转换为RGB/RGBA才能正确显示 /* FILE *png_file = fopen("screenshot.png", "wb"); if (png_file) { png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_infop info_ptr = png_create_info_struct(png_ptr); png_init_io(png_ptr, png_file); // 设置PNG图像信息 png_set_IHDR(png_ptr, info_ptr, screen_width, screen_height, 8, bytes_per_pixel == 4 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png_ptr, info_ptr); // 逐行转换并写入PNG for (int y = 0; y < screen_height; y++) { uint8_t *row = pixel_data + y * image_reply->stride; // 转换BGR到RGB,BGRA到RGBA for (int x = 0; x < screen_width; x++) { uint8_t temp = row[x * bytes_per_pixel]; row[x * bytes_per_pixel] = row[x * bytes_per_pixel + 2]; row[x * bytes_per_pixel + 2] = temp; } png_write_row(png_ptr, row); } // 完成PNG写入并清理 png_write_end(png_ptr, NULL); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(png_file); printf("截图已保存为screenshot.png\n"); } */ // 6. 清理所有资源 free(image_reply); xcb_free_pixmap(conn, screenshot_pixmap); xcb_disconnect(conn); return 0; }
编译与运行
- 如果不需要保存PNG,编译命令:
gcc xcb_screenshot.c -o xcb_screenshot -lxcb - 如果需要保存PNG,先确保安装了libpng开发库,然后编译:
gcc xcb_screenshot.c -o xcb_screenshot -lxcb -lpng
运行后会在控制台输出截图信息,若启用PNG保存则会生成screenshot.png。
关键注意点
- 像素格式转换:XCB的ZPixmap默认采用BGR/BGRA顺序(与大多数图像库的RGB/RGBA相反),如果需要和其他工具兼容,必须手动转换颜色通道,代码中已经给出示例。
- 性能优化:如果只需要截取屏幕的一部分,只需修改
xcb_copy_area和xcb_get_image中的坐标与尺寸参数,不用截取整个屏幕,能进一步提升速度。 - 多屏幕支持:如果是多显示器环境,需要通过Xinerama或RandR扩展获取每个屏幕的独立根窗口和尺寸,基础代码仅支持单屏幕,扩展逻辑可以基于现有代码修改。
- 错误处理:代码中加入了基础的错误检查,实际项目中可以用
xcb_request_check来验证每个XCB请求的执行结果,避免潜在的崩溃。
内容的提问来源于stack exchange,提问作者Alessandro Sangiuliano
相关产品推荐
相关产品推荐

