从使用C或C ++的X11合成WM请求图像

时间:2019-02-03 17:57:27

标签: c++ c x11 xlib xcb

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

我尝试使用xcb_composite_name_window_pixmap()失败(XCB错误)。

我尝试使用render扩展名将窗口渲染为没有错误的图片,但是我仍然需要将图像加载到程序的内存中。

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

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

简而言之,我现在只是在猜测。

我很难找到井井有条的完整文档。我可以在X11,XCB或Xlib上找到的最新出版物是1994年(关于Xlib和/或X11R6的O'Reilly书),我怀疑这些扩展是否有效。大多数手册页和在线文档都有很多“待办事项:解释这一点”和/或功能描述,如“向X服务器发送请求”。任何人都可以提供的帮助将对我有用。

我目前正在为我的WM运行compiz,为窗口装饰运行翡翠,而在“桌面环境”方面没有其他标准。我正在为自定义桌面开发一些实用程序应用程序,这些应用程序计划在它们准备就绪时发布。我希望与其他WM一起工作,但是,如果每个WM都需要特殊的代码路径,则可以将其他WM添加到此行。

编辑:我最初在此处添加了一个不起作用的示例,然后添加了一个简化的示例,所有示例均已移至注释中建议的答案。

1 个答案:

答案 0 :(得分:0)

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

#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。事实证明,它确实像我期望的那样简单。您所需要做的就是窗口ID和大小,进行一些检查以确保其中有Composite和一个好的版本(在这种情况下,> = 0.2),然后调用xcb_composite_redirect_window,xcb_composite_name_window_pixmap(具有生成的pixmap ID)和xcb_get_image(+依次按顺序回复,data_len,数据),您就会看到图片。