標籤:style blog color 使用 os 資料
在前面的例子中,我們從來沒有關心過調用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_REPLACEXCB_PROP_MODE_PREPENDXCB_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發佈於部落格園,轉載請註明出處。)