首页 > 代码库 > 使用XCB编写X Window程序(02):在窗口中绘图

使用XCB编写X Window程序(02):在窗口中绘图

  在上一篇中,我展示了怎么连接X服务器以及怎么创建一个窗口。创建窗口是编写GUI程序的根本。在GUI编程中还有另外两个重点,其一是事件处理,其二是在窗口中绘图。这一篇中,将展示如何使用XCB在窗口中进行绘图。

  先看一个示例代码及其运行效果,代码如下:

  1     #include <stdlib.h>  2     #include <stdio.h>  3   4     #include <xcb/xcb.h>  5   6     int  7     main ()  8     {  9         /* geometric objects */ 10         xcb_point_t          points[] = { 11             {40, 40}, 12             {40, 80}, 13             {80, 40}, 14             {80, 80}}; 15  16         xcb_point_t          polyline[] = { 17             {200, 40}, 18             { 20, 80},     /* rest of points are relative */ 19             {100,-80}, 20             {40, 40}}; 21  22         xcb_segment_t        segments[] = { 23             {400, 40, 560, 120}, 24             {440, 100, 520, 240}}; 25  26         xcb_rectangle_t      rectangles[] = { 27             { 40, 200, 160, 80}, 28             { 320, 200, 40, 160}}; 29  30         xcb_arc_t            arcs[] = { 31             {40, 400, 240, 160, 0, 90 << 6}, 32             {360, 400, 220, 160, 0, 270 << 6}}; 33  34  35         /* Open the connection to the X server */ 36         xcb_connection_t *connection = xcb_connect (NULL, NULL); 37  38         /* Get the first screen */ 39         xcb_screen_t *screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data; 40  41         /* Create black (foreground) graphic context */ 42         xcb_drawable_t  window     = screen->root; 43         xcb_gcontext_t  gc = xcb_generate_id (connection); 44         uint32_t        mask       = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES; 45         uint32_t        values[2]  = {screen->black_pixel, 0}; 46  47         xcb_create_gc (connection, gc, window, mask, values); 48  49  50         /* Create a window */ 51         window = xcb_generate_id (connection); 52  53         mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; 54         values[0] = screen->white_pixel; 55         values[1] = XCB_EVENT_MASK_EXPOSURE; 56  57         xcb_create_window (connection,                    /* connection          */ 58                            XCB_COPY_FROM_PARENT,          /* depth               */ 59                            window,                        /* window Id           */ 60                            screen->root,                  /* parent window       */ 61                            0, 0,                          /* x, y                */ 62                            800, 600,                      /* width, height       */ 63                            10,                            /* border_width        */ 64                            XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class               */ 65                            screen->root_visual,           /* visual              */ 66                            mask, values );                /* masks */ 67  68  69         /* Map the window on the screen and flush*/ 70         xcb_map_window (connection, window); 71         xcb_flush (connection); 72  73  74         /* draw primitives */ 75         xcb_generic_event_t *event; 76         while ((event = xcb_wait_for_event (connection))) { 77             switch (event->response_type & ~0x80) { 78             case XCB_EXPOSE: 79                 /* We draw the points */ 80                 xcb_poly_point (connection, XCB_COORD_MODE_ORIGIN, window, gc, 4, points); 81  82                 /* We draw the polygonal line */ 83                 xcb_poly_line (connection, XCB_COORD_MODE_PREVIOUS, window, gc, 4, polyline); 84  85                 /* We draw the segments */ 86                 xcb_poly_segment (connection, window, gc, 2, segments); 87  88                 /* draw the rectangles */ 89                 xcb_poly_rectangle (connection, window, gc, 2, rectangles); 90  91                 /* draw the arcs */ 92                 xcb_poly_arc (connection, window, gc, 2, arcs); 93  94                 /* flush the request */ 95                 xcb_flush (connection); 96  97                 break; 98             default:  99                 /* Unknown event type, ignore it */100                 break;101             }102 103             free (event);104         }105 106         return 0;107     }

   运行效果如下图:

 

  下面,对以上GUI绘图的程序进行分析:

  1、任何绘图程序,无论使用的是Win32 API,还是Java Swing,都离不开GC和DC的概念,使用XCB编写窗口程序也是如此。这两个概念,一个代表了图怎么绘,一个代表了图绘在哪里。由于绘图时需要指定太多的信息,比如画笔的粗细、线型、前景色、背景色等等,如果把这些信息统统作为参数传递给绘图函数,就会产生两个问题:一是调用这些函数太复杂,二是效率太低。所以,目前所有主流的GUI库都采用了一致的做法,那就是把这些信息组织成一个context,我们称之为graphic context,可简称gcontext或GC。DC可以认为是绘图时所用的画布,我们可以直接把图绘到屏幕上,也可以把图绘到窗口中,当然,也可以绘到控件中(因为控件的本质也是窗口)。在面向对象的编程语言中,可以绘图的组件往往都继承自Drawable。在XCB中,从绘图函数的签名可以看出,它仍然将绘图的目标称为drawable,虽然它只是一个id。

  2、什么时候绘图。理论上讲,任何时候都可以调用绘图函数。但是从实践上来讲,所有的GUI程序都是在窗口重绘的事件中调用绘图函数。窗口重绘事件,有的叫onPaint,有的叫onDraw,XCB中比较奇怪,叫EXPOSE。不管叫什么,其道理是一样的。那就是当窗口初次显示、从隐藏到显示或窗口内容需要刷新时,都会触发该事件,所以在处理该事件的代码中调用绘图函数是最好的,既可以保证我们看到绘图的结果,又兼顾效率(窗口不可见时绘图函数不用执行,窗口不变化时绘图函数也不用执行)。MVC模式也是构建在这样的基础之上,将数据与显示分离,数据可以随时被操作,但是绘图只在窗口重绘时进行。

  3、调用绘图函数。这个不需要多讲,因为几乎所有的绘图函数都是自解释的,看到函数名,就知道它要进行什么操作。

 

  在XCB中,创建GC可以通过xcb_create_gc()函数进行,创建窗口可以通过xcb_create_window()函数进行。在这两个函数中,都有一个比较奇怪的模式,那就是通过一个mask和value数组来设定GC和窗口的具体信息。在上一篇中,创建的窗口没有背景,而这一篇中,窗口具有白色背景,就是因为这里调用xcb_create_window时,在其mask参数中指定了XCB_CW_BACK_PIXEL。

 

  借助于ctags和Vim的taglist.vim插件,可以非常方便地查看这些mask的取值及其意义,只需按下Ctrl+]就可以跳到相应的定义处。如下两图,是enum xcb_gc_t中定义的一系列XCB_GC_***:

 

  再下面两个图,是创建窗口是可以用到的mask值:

 

  这些枚举都有很详细的注释,所以我就不在这里啰嗦了。libxcb的头文件中的注释,本身就是一种很好的学习资料,不是吗?

 

(京山游侠于2014-07-05发布于博客园,转载请注明出处。)