X11:使用 xcb 获取 main windows 的列表
X11: Get the list of main windows using xcb
我正在尝试通过使用 xcb 库的 C 程序获取已启动 X 应用程序的主要 windows 列表。根据这个问题,这些 windows 似乎是 "top-level windows":X11: list top level windows
所以我的程序要求Openbox window manager 给出这些windows 的列表,然后询问每个window 的名称,但它不起作用。我正在使用 EWMH 原子,并且我读到 Openbox 是 EWMH 兼容的。
编辑:当我 运行 控制台命令时:xprop -root _NET_CLIENT_LIST
,它给出了几个 windows 的标识符。所以看起来 Openbox 支持这个原子。我看了xprop
的代码,但是是用Xlib写的,因为支持多线程所以需要用xcb
当我的程序收到来自Openbox的回复时,回复的长度为0。
这里是源代码:
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <xcb/xcb.h>
xcb_atom_t getatom(xcb_connection_t* c, char *atom_name)
{
xcb_intern_atom_cookie_t atom_cookie;
xcb_atom_t atom;
xcb_intern_atom_reply_t *rep;
atom_cookie = xcb_intern_atom(c, 0, strlen(atom_name), atom_name);
rep = xcb_intern_atom_reply(c, atom_cookie, NULL);
if (NULL != rep)
{
atom = rep->atom;
free(rep);
printf("\natom: %ld",atom);
fflush(stdout);
return atom;
}
printf("\nError getting atom.\n");
exit(1);
}
int main() {
xcb_generic_error_t *e;
int i,j,k;
xcb_connection_t* c = xcb_connect(NULL, NULL);
xcb_atom_t net_client_list = getatom(c,"_NET_CLIENT_LIST");
xcb_atom_t net_wm_visible_name = getatom(c,"_NET_WM_VISIBLE_NAME");
xcb_screen_t* screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
xcb_get_property_cookie_t prop_cookie_list,prop_cookie;
xcb_get_property_reply_t *reply_prop_list,*reply_prop;
prop_cookie_list = xcb_get_property(c, 0, screen->root, net_client_list, XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
reply_prop_list = xcb_get_property_reply(c, prop_cookie_list, &e);
if(e) {
printf("\nError: %d",e->error_code);
free(e);
}
if(reply_prop_list) {
int value_len = xcb_get_property_value_length(reply_prop_list);
printf("\nvalue_len: %d",value_len);
if(value_len) {
xcb_window_t* win = xcb_get_property_value(reply_prop_list);
for(i=0; i<value_len; i++) {
prop_cookie = xcb_get_property(c, 0, win[i], net_wm_visible_name, XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
reply_prop = xcb_get_property_reply(c, prop_cookie, &e);
if(e) {
printf("\nError: %d",e->error_code);
free(e);
}
if(reply_prop) {
int value_len2 = xcb_get_property_value_length(reply_prop);
printf("\nvalue_len2: %d",value_len2);
if(value_len2) {
char* name = xcb_get_property_value(reply_prop);
printf("\nName: %s",name);
fflush(stdout);
}
free(reply_prop);
}
}
}
free(reply_prop_list);
}
printf("\n\n");
fflush(stdout);
exit(0);
}
终于找到问题了,在网上看过的例子中,xcb_get_property()
的字段long_length
被设置为0
,但是好像要得到一个一致的答复,该字段的值必须大于或等于答复将具有的 32 位字的数量。所以我选择了 100
进行测试,但是如果指定根 window 的子树中的顶级 windows 超过 100 个,那么回复将被截断为第 100 个 windows.
我还不得不用value_len
除以4
来得到回复中的元素个数,因为value_len
是以字节为单位,而回复值是一个xcb_window_t 个元素,每个元素有 4 个字节长。
为了得到每个顶级 window 的名称,我选择将 1000
字段 long_length
设置为 xcb_get_property()
,以确保有完整的名字。但是好像在reply中分配的字符串的真实大小没有最后的[=21=]
字符,所以只好分配一个长度为value_len2 + 1
的内存块,然后使用strncpy()
将字符串复制到这个新位置。最后,添加最后一个 [=21=]
字符。
这是获取 属性 的正确代码:
prop_cookie_list = xcb_get_property(c, 0, screen->root, net_client_list, XCB_GET_PROPERTY_TYPE_ANY, 0, 100);
reply_prop_list = xcb_get_property_reply(c, prop_cookie_list, &e);
if(e) {
printf("\nError: %d",e->error_code);
free(e);
}
if(reply_prop_list) {
int value_len = xcb_get_property_value_length(reply_prop_list);
printf("\nvalue_len: %d",value_len);
if(value_len) {
xcb_window_t* win = xcb_get_property_value(reply_prop_list);
for(i=0; i<value_len/4; i++) {
printf("\n--------------------------------\nwin id: %d",win[i]);
prop_cookie = xcb_get_property(c, 0, win[i], net_wm_visible_name, XCB_GET_PROPERTY_TYPE_ANY, 0, 1000);
reply_prop = xcb_get_property_reply(c, prop_cookie, &e);
if(e) {
printf("\nError: %d",e->error_code);
free(e);
}
if(reply_prop) {
int value_len2 = xcb_get_property_value_length(reply_prop);
printf("\nvalue_len2: %d",value_len2);
if(value_len2) {
char* name = malloc(value_len2+1);
strncpy(name,xcb_get_property_value(reply_prop),value_len2);
name[value_len2] = '[=10=]';
printf("\nName: %s",name);
fflush(stdout);
free(name);
}
free(reply_prop);
}
}
}
free(reply_prop_list);
}
我正在尝试通过使用 xcb 库的 C 程序获取已启动 X 应用程序的主要 windows 列表。根据这个问题,这些 windows 似乎是 "top-level windows":X11: list top level windows
所以我的程序要求Openbox window manager 给出这些windows 的列表,然后询问每个window 的名称,但它不起作用。我正在使用 EWMH 原子,并且我读到 Openbox 是 EWMH 兼容的。
编辑:当我 运行 控制台命令时:xprop -root _NET_CLIENT_LIST
,它给出了几个 windows 的标识符。所以看起来 Openbox 支持这个原子。我看了xprop
的代码,但是是用Xlib写的,因为支持多线程所以需要用xcb
当我的程序收到来自Openbox的回复时,回复的长度为0。
这里是源代码:
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <xcb/xcb.h>
xcb_atom_t getatom(xcb_connection_t* c, char *atom_name)
{
xcb_intern_atom_cookie_t atom_cookie;
xcb_atom_t atom;
xcb_intern_atom_reply_t *rep;
atom_cookie = xcb_intern_atom(c, 0, strlen(atom_name), atom_name);
rep = xcb_intern_atom_reply(c, atom_cookie, NULL);
if (NULL != rep)
{
atom = rep->atom;
free(rep);
printf("\natom: %ld",atom);
fflush(stdout);
return atom;
}
printf("\nError getting atom.\n");
exit(1);
}
int main() {
xcb_generic_error_t *e;
int i,j,k;
xcb_connection_t* c = xcb_connect(NULL, NULL);
xcb_atom_t net_client_list = getatom(c,"_NET_CLIENT_LIST");
xcb_atom_t net_wm_visible_name = getatom(c,"_NET_WM_VISIBLE_NAME");
xcb_screen_t* screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
xcb_get_property_cookie_t prop_cookie_list,prop_cookie;
xcb_get_property_reply_t *reply_prop_list,*reply_prop;
prop_cookie_list = xcb_get_property(c, 0, screen->root, net_client_list, XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
reply_prop_list = xcb_get_property_reply(c, prop_cookie_list, &e);
if(e) {
printf("\nError: %d",e->error_code);
free(e);
}
if(reply_prop_list) {
int value_len = xcb_get_property_value_length(reply_prop_list);
printf("\nvalue_len: %d",value_len);
if(value_len) {
xcb_window_t* win = xcb_get_property_value(reply_prop_list);
for(i=0; i<value_len; i++) {
prop_cookie = xcb_get_property(c, 0, win[i], net_wm_visible_name, XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
reply_prop = xcb_get_property_reply(c, prop_cookie, &e);
if(e) {
printf("\nError: %d",e->error_code);
free(e);
}
if(reply_prop) {
int value_len2 = xcb_get_property_value_length(reply_prop);
printf("\nvalue_len2: %d",value_len2);
if(value_len2) {
char* name = xcb_get_property_value(reply_prop);
printf("\nName: %s",name);
fflush(stdout);
}
free(reply_prop);
}
}
}
free(reply_prop_list);
}
printf("\n\n");
fflush(stdout);
exit(0);
}
终于找到问题了,在网上看过的例子中,xcb_get_property()
的字段long_length
被设置为0
,但是好像要得到一个一致的答复,该字段的值必须大于或等于答复将具有的 32 位字的数量。所以我选择了 100
进行测试,但是如果指定根 window 的子树中的顶级 windows 超过 100 个,那么回复将被截断为第 100 个 windows.
我还不得不用value_len
除以4
来得到回复中的元素个数,因为value_len
是以字节为单位,而回复值是一个xcb_window_t 个元素,每个元素有 4 个字节长。
为了得到每个顶级 window 的名称,我选择将 1000
字段 long_length
设置为 xcb_get_property()
,以确保有完整的名字。但是好像在reply中分配的字符串的真实大小没有最后的[=21=]
字符,所以只好分配一个长度为value_len2 + 1
的内存块,然后使用strncpy()
将字符串复制到这个新位置。最后,添加最后一个 [=21=]
字符。
这是获取 属性 的正确代码:
prop_cookie_list = xcb_get_property(c, 0, screen->root, net_client_list, XCB_GET_PROPERTY_TYPE_ANY, 0, 100);
reply_prop_list = xcb_get_property_reply(c, prop_cookie_list, &e);
if(e) {
printf("\nError: %d",e->error_code);
free(e);
}
if(reply_prop_list) {
int value_len = xcb_get_property_value_length(reply_prop_list);
printf("\nvalue_len: %d",value_len);
if(value_len) {
xcb_window_t* win = xcb_get_property_value(reply_prop_list);
for(i=0; i<value_len/4; i++) {
printf("\n--------------------------------\nwin id: %d",win[i]);
prop_cookie = xcb_get_property(c, 0, win[i], net_wm_visible_name, XCB_GET_PROPERTY_TYPE_ANY, 0, 1000);
reply_prop = xcb_get_property_reply(c, prop_cookie, &e);
if(e) {
printf("\nError: %d",e->error_code);
free(e);
}
if(reply_prop) {
int value_len2 = xcb_get_property_value_length(reply_prop);
printf("\nvalue_len2: %d",value_len2);
if(value_len2) {
char* name = malloc(value_len2+1);
strncpy(name,xcb_get_property_value(reply_prop),value_len2);
name[value_len2] = '[=10=]';
printf("\nName: %s",name);
fflush(stdout);
free(name);
}
free(reply_prop);
}
}
}
free(reply_prop_list);
}