在前面的例子中,我们从来没有关心过调用XCB函数时,该函数和X Server的通讯是同步的还是异步的。因为在前面的例子中,我们基本上不关心XCB函数的返回值,只有在上一篇中,由于某些操作需要关心它们是否成功(比如是否成功打开字体、是否成功创建GC等),才涉及到XCB函数的返回值。
在这一篇中会更进一步,因为是获取窗口的属性,所以肯定要关注从X Server获取的数据,这时,将会涉及到XCB函数同X Server的通讯是同步的还是异步的。什么是同步?就是说调用一个函数向X Server发出一个请求后,该函数要一直等到X Server回复的数据函数才返回。什么是异步?就是说调用一个XCB函数向X Server发出一个请求后,不用等X Server回复的数据,该函数直接返回。传统的Xlib使用的是同步的方式,而XCB则是使用的异步方式。很显然,使用异步的方式可以获得更好的效率。这也是XCB取代Xlib的一个主要原因。
那么既然XCB使用的是异步的方式,调用一个XCB函数后没有等到X Server的回复该函数就已经返回了,那么我们真的需要X Server回复的数据怎么办呢?对于这个问题,在XCB中使用的是这样一种解决方式:XCB函数先返回一个cookie,数据传输在后台进行,由XCB自动处理,当需要使用从X Server获取的数据时,再调用另外一个函数通过该cookie获取数据。后面将会看到具体的示例。
操控窗口的属性
前面展示了怎么创建窗口以及怎么在窗口中画图。其实对于窗口,还可以有更多的操作。可以认为这些操作都是通过获取和设置窗口的属性来进行。窗口可以有很多的属性,而且这些属性的设置方式还不一样。下面来回顾一下创建窗口的函数:
xcb_void_cookie_t xcb_create_window ( xcb_connection_t *connection, /* Pointer to the xcb_connection_t structure */ uint8_t depth, /* Depth of the screen */ xcb_window_t wid, /* Id of the window */ xcb_window_t parent, /* Id of an existing window that should be the parent of the new window */ int16_t x, /* X position of the top-left corner of the window (in pixels) */ int16_t y, /* Y position of the top-left corner of the window (in pixels) */ uint16_t width, /* Width of the window (in pixels) */ uint16_t height, /* Height of the window (in pixels) */ uint16_t border_width, /* Width of the window‘s border (in pixels) */ uint16_t _class, xcb_visualid_t visual, uint32_t value_mask, const uint32_t *value_list );
可以看到:1、有一部分属性是直接通过xcb_create_window的参数设置的,比如窗口的颜色深度、位置、大小、边框宽度等;2、一部分窗口的属性通过mask、values的方式设置,比如前景色、背景色、需要关注的事件等;3、还有一部分属性需要通过另外一种方式来设置,这种方式我之前没有展示过,那就是通过xcb_change_property函数来设置,这些属性比较特别,每一个都称之为atom。从xcb_create_window的函数签名还可以看出,xcb_create_window确实是返回一个xcb_void_cookie_t类型的值,也就是返回一个cookie。这验证了我之前关于XCB函数是异步的这个说法。
xcb_change_property的函数签名如下:
xcb_void_cookie_t xcb_change_property ( xcb_connection_t *c, /* Connection to the X server */ uint8_t mode, /* Property mode */ xcb_window_t window, /* Window */ xcb_atom_t property, /* Property to change */ xcb_atom_t type, /* Type of the property */ uint8_t format, /* Format of the property (8, 16, 32) */ uint32_t data_len, /* Length of the data parameter */ const void *data /* Data */ );
可以看出,该函数要同时关注atom的id、atom的类型及atom的值,还要关注该值的格式和长度。也就是说,每一个atom由一个id标识,它可以有自己的类型和值。比如一个atom可能是一个数字,而另一atom可能是一个字符串。该函数的mode参数可以取下面值中的一个:
XCB_PROP_MODE_REPLACE
XCB_PROP_MODE_PREPEND
XCB_PROP_MODE_APPEND
使用atom来表示窗口的属性主要是为了和别的程序之间进行沟通,比如和窗口管理器沟通。下面是一个设置窗口标题以及窗口最小化后任务栏标题的示例:
#include <string.h> #include <xcb/xcb.h> #include <xcb/xcb_atom.h> int main () { /* open the connection to the X server */ xcb_connection_t *connection = xcb_connect (NULL, NULL); /* get the first screen */ xcb_screen_t *screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data; /* create the window */ xcb_window_t window = xcb_generate_id (connection); xcb_create_window (connection, 0, /* depth */ window, screen->root, /* parent window */ 0, 0, /* x, y */ 250, 150, /* width, height */ 10, /* border_width */ XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */ screen->root_visual, /* visual */ 0, NULL ); /* masks, not used */ /* set the title of the window */ char *title = "Hello World !"; xcb_change_property (connection, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, strlen (title), title ); /* set the title of the window icon */ char *iconTitle = "Hello World ! (iconified)"; xcb_change_property (connection, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_ICON_NAME, XCB_ATOM_STRING, 8, strlen(iconTitle), iconTitle); /* map the window on the screen */ xcb_map_window (connection, window); xcb_flush (connection); /* event loop (in this case, no events to handle) */ while (1) {} return 0; }
如果想更改窗口的大小、位置、边框宽度等属性的话,可以通过如下函数进行:
xcb_void_cookie_t xcb_configure_window ( xcb_connection_t *c, /* The connection to the X server*/ xcb_window_t window, /* The window to configure */ uint16_t value_mask, /* The mask */ const uint32_t *value_list /* The values to set */ );
可以看到,该函数再次用到了mask、valuelist这种模式。该函数可操作的属性有如下这些:
XCB_CONFIG_WINDOW_X // new x coordinate of the window‘s top left corner XCB_CONFIG_WINDOW_Y // new y coordinate of the window‘s top left corner XCB_CONFIG_WINDOW_WIDTH // new width of the window XCB_CONFIG_WINDOW_HEIGHT // new height of the window XCB_CONFIG_WINDOW_BORDER_WIDTH // new width of the border of the window XCB_CONFIG_WINDOW_SIBLING XCB_CONFIG_WINDOW_STACK_MODE // the new stacking order
写了半天也还没写到XCB函数和X Server异步通讯的用法。为了不把这篇随笔写得又臭又长,xcb_configure_window具体怎么用我就不举例了。大家可以自行阅读XCB的官方教程。下面来看什么是异步。
获取窗口的属性
有很多函数可以获取窗口的属性,比如最常见的 xcb_get_geometry 和 xcb_get_geometry_reply ,看到geometry肯定会想到长宽高、横坐标纵坐标什么的。确实,这两个函数就是用来获取窗口的位置、大小这样一些信息的。而且这两个函数总是成对出现。因为xcb_get_geometry被调用后,它是马上返回的,这时X Server的数据还没传回来呢,所以,等数据传回来了,还得调用xcb_get_geometry_reply把这些数据读出来。这两个函数的签名如下:
xcb_get_geometry_cookie_t xcb_get_geometry ( xcb_connection_t *connection, xcb_drawable_t drawable ); xcb_get_geometry_reply_t *xcb_get_geometry_reply ( xcb_connection_t *connection, xcb_get_geometry_cookie_t cookie, xcb_generic_error_t **error );
可以看到,第一个函数返回一个类型为xcb_get_geometry_cookie_t的cookie,第二个函数再以该cookie为参数,返回具体的geometry信息,这就是异步。该信息是一个类型为xcb_get_geometry_reply_t的指针,该类型定义如下:
typedef struct { uint8_t response_type; uint8_t depth; /* depth of the window */ uint16_t sequence; uint32_t length; xcb_window_t root; /* Id of the root window *> int16_t x; /* X coordinate of the window‘s location */ int16_t y; /* Y coordinate of the window‘s location */ uint16_t width; /* Width of the window */ uint16_t height; /* Height of the window */ uint16_t border_width; /* Width of the window‘s border */ } xcb_get_geometry_reply_t;
前面说过,窗口有很多属性。geometry属性指示窗口属性的一部分,所以,还有一对更猛的获取窗口属性的函数及其使用到的数据结构,如下:
typedef struct { uint8_t response_type; uint8_t backing_store; uint16_t sequence; uint32_t length; xcb_visualid_t visual; /* Visual of the window */ uint16_t _class; uint8_t bit_gravity; uint8_t win_gravity; uint32_t backing_planes; uint32_t backing_pixel; uint8_t save_under; uint8_t map_is_installed; uint8_t map_state; /* Map state of the window */ uint8_t override_redirect; xcb_colormap_t colormap; /* Colormap of the window */ uint32_t all_event_masks; uint32_t your_event_mask; uint16_t do_not_propagate_mask; } xcb_get_window_attributes_reply_t; xcb_get_window_attributes_cookie_t xcb_get_window_attributes ( xcb_connection_t *connection, xcb_window_t window ); xcb_get_window_attributes_reply_t *xcb_get_window_attributes_reply ( xcb_connection_t *connection, xcb_get_window_attributes_cookie_t cookie, xcb_generic_error_t **e );
这些都还是基本的。编写简单的窗口程序,了解这些基本的方法就够了。但是GUI编程,总还有一些高深的需求,比如枚举系统中所有的窗口。我这里用到了“枚举”这个词,是因为Win32 API用了这个词。而在X Window中,这样的操作叫做“遍历”。很显然,在GUI系统中,所有的窗口都是通过parent、child这样的方式呈树形组织起来的,所以通过“遍历”窗口树来“枚举”系统中的所有窗口,其实也不难。这个任务需要用到这样的函数和数据结构:
typedef struct { uint8_t response_type; uint8_t pad0; uint16_t sequence; uint32_t length; xcb_window_t root; xcb_window_t parent; /* Id of the parent window */ uint16_t children_len; uint8_t pad1[14]; } xcb_query_tree_reply_t; xcb_query_tree_cookie_t xcb_query_tree ( xcb_connection_t *connection, xcb_window_t window ); xcb_query_tree_reply_t *xcb_query_tree_reply ( xcb_connection_t *connection, xcb_query_tree_cookie_t cookie, xcb_generic_error_t **error );
不过该函数好像只能从子窗口查找父窗口,很显然要完成遍历,还必须要能从父窗口查找子窗口。X Window中有通过父窗口查找子窗口的方法吗?大家慢慢研究去吧。
(京山游侠于2014-07-21发布于博客园,转载请注明出处。)
使用XCB编写X Window程序(05):使用异步的方式和X Server通讯及获取和设置窗口的属性,布布扣,bubuko.com
使用XCB编写X Window程序(05):使用异步的方式和X Server通讯及获取和设置窗口的属性
原文地址:http://www.cnblogs.com/youxia/p/gui005.html