估計現在已經沒有誰使用XCB這麼底層的庫寫應用程序了,要用也是用經過精心封裝的Motif, LessTiff, GTK, Qt, EWL, ETK或者Cairo等高層次的庫。我之所以這麼費心地去折騰XCB,其實主要也是為了學習。畢竟,使用最接近底層的UI庫寫代碼是學習X協議及GUI編程原理的最好方法。
XCB的主要教程可以參考這裡:http://xcb.freedesktop.org/tutorial/
和X協議有關的文檔,在這裡:http://www.x.org/releases/X11R7.7/doc/
在這裡要繼續吐槽freedesktop.org。沒錯,XCB的官網又是在freedesktop.org,而且正如我前幾篇隨筆中提到的Xft、Freetype一樣,文檔極其不完善。不過在其XcbApi頁面有這樣的提示“Refactoring this page...please be patient...”,那就耐心等待吧。好在代碼是最好的文檔,在Fedora 20中安裝libxcb-devel軟件包後,可以直接到/usr/include/xcb目錄下查看XCB庫的頭文件,所以真要學習XCB也不是很難。如下圖,我系統中的XCB庫的頭文件:
下面是一個最簡單的XCB程序,它的功能是創建一個窗口。由於沒有任何事件處理的機制,所以使用了pause()讓程序暫停,要退出程序,必須得按Ctrl+C。
#include <stdlib.h> #include <sys/time.h> #include <unistd.h> #include <xcb/xcb.h> #include <stdio.h> double get_time(){ struct timeval timeval; gettimeofday(&timeval, NULL); return (double)timeval.tv_sec + (((double)timeval.tv_usec)/1000000); } int main(){ double start_time = get_time(); xcb_connection_t *connection = xcb_connect(NULL, NULL); const xcb_setup_t *setup = xcb_get_setup(connection); xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup); xcb_screen_t *screen = iter.data; xcb_window_t window = xcb_generate_id(connection); xcb_create_window( connection, XCB_COPY_FROM_PARENT, window, screen->root, 100,100, 400, 300, 10, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, 0, NULL); xcb_map_window(connection, window); xcb_flush(connection); printf("花費時間:%f秒。",get_time()-start_time); fflush(stdout); pause(); xcb_disconnect(connection); return 0; }
從這段代碼可以看出,要創建一個簡單的窗口程序,必須經過如下步驟:
1、連接到XServer的Display,通過xcb_connect()函數進行,返回一個xcb_connection_t的指針,在這一步中,同時可以獲得當前屏幕的Screen number;
2、獲得xcb_setup,通過xcb_get_setup()函數進行。xcb_setup裡面保存的是應用程序和XServer之間通訊時需要用到的信息,包括協議的版本、字節的順序等。一般情況下,我們不需要關注這些細節;
3、獲得Screen對象,這一步很重要,也很復雜。重要是因為只有獲得一個Screen後,才能在屏幕上創建窗口,創建窗口時需要用到Screen中的一些信息。復雜是因為一個Display可以有多個Screen,所以通過xcb_setup_roots_iterator()函數返回的是一個迭代器,可以通過該迭代器對所有的Screen進行遍歷。如果只有一個Screen,則返回的第一個迭代器中的data就指向該Screen。XCB中使用xcb_screen_t結構來保存Screen的信息;
4、創建窗口並顯示窗口,這需要三步,第一步先使用xcb_generate_id()函數生成一個ID,第二步使用xcb_create_window()函數創建一個窗口,第三步使用xcb_map_window讓窗口顯示出來。
通過以上的代碼,我還學到了一個技巧,那就是使用gettimeofday()函數來獲取一個精確到微秒的時間,用來查看應用程序的耗時。
程序運行如下圖:
這個新創建的窗口自己沒有背景,所以它創建的時候屏幕上有什麼,它窗口裡面就有什麼。對於程序中用到的數據結構和枚舉的含義,可以直接查看xcb的頭文件,配合ctags和taglist.vim插件使用的話,只需要按Ctrl+]鍵,就可以自動跳轉到這些數據結構的定義處(在Vim中使用taglist的方法見這裡Linux江湖02:打造屬於自己的Vim),如下兩圖:
返回欄目頁:http://www.bianceng.cn/Programming/cplus/
最後,我對第一個簡單的程序進行適當的擴展,看看怎麼獲取Display中有幾個Screen以及怎麼遍歷Screen,最後顯示Screen的一些信息。程序如下:
1 #include <stdlib.h>
2 #include <sys/time.h>
3 #include <unistd.h>
4 #include <xcb/xcb.h>
5 #include <stdio.h>
6
7 double get_time(){
8 struct timeval timeval;
9 gettimeofday(&timeval, NULL);
10 return (double)timeval.tv_sec + (((double)timeval.tv_usec)/1000000);
11 }
12
13 int main(){
14 double start_time = get_time();
15 int screen_number;
16
17 xcb_connection_t *connection = xcb_connect(NULL, &screen_number);
18 const xcb_setup_t *setup = xcb_get_setup(connection);
19 xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
20 printf("當前的Screen Number為:%d\n",screen_number);
21 printf("iter.rem = %d,下面開始遍歷:\n",iter.rem);
22
23 xcb_screen_t *screen;
24 for(; iter.rem!=0; xcb_screen_next(&iter)){
25 screen = iter.data;
26 printf("*****看到多少行這個提示,就說明有多少個Screen。*****\n");
27 printf(" Screen->root:%d\n",screen->root);
28 printf(" Screen->root_depth:%d\n",screen->root_depth);
29 printf(" Screen->white_pixel:%d\n",screen->white_pixel);
30 printf(" Screen->black_pixel:%d\n",screen->black_pixel);
31 printf(" Screen->width_in_pixels:%d\n",screen->width_in_pixels);
32 printf(" Screen->height_in_pixels:%d\n",screen->height_in_pixels);
33 printf(" Screen->width_in_millimeters:%d\n",screen->width_in_millimeters);
34 printf(" Screen->height_in_millimeters:%d\n",screen->height_in_millimeters);
35 }
36
37
38 xcb_window_t window = xcb_generate_id(connection);
39 xcb_create_window(
40 connection,
41 XCB_COPY_FROM_PARENT,
42 window,
43 screen->root,
44 100,100,
45 400, 300,
46 10,
47 XCB_WINDOW_CLASS_INPUT_OUTPUT,
48 screen->root_visual,
49 0, NULL);
50 xcb_map_window(connection, window);
51 xcb_flush(connection);
52
53 printf("花費時間:%f秒。",get_time()-start_time);
54 fflush(stdout);
55
56 pause();
57 xcb_disconnect(connection);
58 return 0;
59 }
最後運行效果如下圖:
作者:cnblogs 京山游俠