在 C 或 C++ 中从 X11 合成 WM 请求图像

Request image from X11 compositing WM in C or C++

我需要从 X 服务器或 WM 请求并检索 window 的图像(我相信 WM 进行实际合成)。我需要能够获取 window 的图像,即使它被另一个 window 遮挡或位于另一个工作区(但仍映射)。我需要使用 C 或 C++,并且想使用 XCB 或 Xlib。我想我也安装了 Gtk+ 开发工具,但是我已经有一段时间没有使用它们了。

我尝试使用 xcb_composite_name_window_pixmap() 但没有成功(XCB 错误)。

我尝试使用渲染扩展将 window 渲染为一张没有产生错误的图片,但我仍然需要将图像加载到我的程序内存中。

我尝试使用 xcb_get_image 来获取图片,但 IIRC 失败了(我认为是错误的资源)。

我试图将图片复制到像素图并在其上使用 xcb_get_image。我能够下载看似图像的内容,但它看起来更像是屏幕的随机区域,而不是实际的 window。

总之,我目前只是猜测

我很难找到有条理的完整文档。我能找到的关于 X11、XCB 或 Xlib 的最新出版物是 1994 年的(The O'Reilly book on Xlib and/or X11R6),我怀疑它对这些扩展是否有效(如果有的话)。大多数手册页和在线文档都有很多 "TODO: explain this" and/or 功能描述,例如 "deliver a request to the X server"。任何人可以提供的任何帮助都会对我有用。

我目前 运行 compiz 用于我的 WM 和 emerald 用于 window 装饰,就 "desktop environment" 而言没有其他标准。我正在为自定义桌面开发一些实用程序,我计划在它们准备就绪时发布。我希望我所做的任何事情都能与其他一些 WM 一起使用,但是,如果每个都需要特殊的代码路径,我可以添加其他的。

编辑:我最初在这里添加了一个非工作示例,然后是一个精简的工作示例,已按照评论中的建议将其全部移至答案。

我现在有一个工作示例,我将在此处post:

#include <xcb/xcb.h>
#include <xcb/xproto.h>
#include <xcb/composite.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
    if (argc < 2) {
        fprintf(stderr, "usage: %s windowId\n", argv[0]);
        return EXIT_FAILURE;
    }
    xcb_window_t req_win_id = strtoul(argv[1], NULL, 0);
    xcb_connection_t *connection = xcb_connect(NULL, NULL);
    xcb_generic_error_t *err = NULL, *err2 = NULL;

    xcb_composite_query_version_cookie_t comp_ver_cookie = xcb_composite_query_version(connection, 0, 2);
    xcb_composite_query_version_reply_t *comp_ver_reply = xcb_composite_query_version_reply(connection, comp_ver_cookie, &err);
    if (comp_ver_reply)
    {
        if (comp_ver_reply->minor_version < 2) {
            fprintf(stderr, "query composite failure: server returned v%d.%d\n", comp_ver_reply->major_version, comp_ver_reply->minor_version);
            free(comp_ver_reply);
            return EXIT_FAILURE;
        }
        free(comp_ver_reply);
    }
    else if (err)
    {
        fprintf(stderr, "xcb error: %d\n", err->error_code);
        free(err);
        return EXIT_FAILURE;
    }

    const xcb_setup_t *setup = xcb_get_setup(connection);
    xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup);
    xcb_screen_t *screen = screen_iter.data;
    // request redirection of window
    xcb_composite_redirect_window(connection, req_win_id, XCB_COMPOSITE_REDIRECT_AUTOMATIC);
    int win_h, win_w, win_d;

    xcb_get_geometry_cookie_t gg_cookie = xcb_get_geometry(connection, req_win_id);
    xcb_get_geometry_reply_t *gg_reply = xcb_get_geometry_reply(connection, gg_cookie, &err);
    if (gg_reply) {
        win_w = gg_reply->width;
        win_h = gg_reply->height;
        win_d = gg_reply->depth;
        free(gg_reply);
    } else {
        if (err) {
            fprintf(stderr, "get geometry: XCB error %d\n", err->error_code);
            free(err);
        }
        return EXIT_FAILURE;
    }

    // create a pixmap
    xcb_pixmap_t win_pixmap = xcb_generate_id(connection);
    xcb_composite_name_window_pixmap(connection, req_win_id, win_pixmap);

    // get the image
    xcb_get_image_cookie_t gi_cookie = xcb_get_image(connection, XCB_IMAGE_FORMAT_Z_PIXMAP, win_pixmap, 0, 0, win_w, win_h, (uint32_t)(~0UL));
    xcb_get_image_reply_t *gi_reply = xcb_get_image_reply(connection, gi_cookie, &err);
    if (gi_reply) {
        int data_len = xcb_get_image_data_length(gi_reply);
        fprintf(stderr, "data_len = %d\n", data_len);
        fprintf(stderr, "visual = %u\n", gi_reply->visual);
        fprintf(stderr, "depth = %u\n", gi_reply->depth);
        fprintf(stderr, "size = %dx%d\n", win_w, win_h);
        uint8_t *data = xcb_get_image_data(gi_reply);
        fwrite(data, data_len, 1, stdout);
        free(gi_reply);
    }
    return EXIT_SUCCESS;
}

这是一个过度的案例。我想到我应该从某个地方使用 Xrender,并开始从我并不真正需要的扩展中提取大量数据。但是我遇到的真正问题是不应该首先创建给 xcb_composite_name_window_pixmap 的像素图,而是我只需要它的新 ID。事实证明,它真的和我想象的一样简单。您只需要 Window ID 和大小,进行一些检查以确保复合材料存在并且版本正确(在本例中为 >=0.2),然后调用 xcb_composite_redirect_window、xcb_composite_name_window_pixmap (带有生成的像素图 ID)和 xcb_get_image(+回复,data_len,数据)的顺序,然后你就有了图像。