当前位置:首页 >> 编程开发 >> Visual C++ >> 内容

使用XCB编写X Window程序(三) 捕获并响应事件(Event)

时间:2015/5/13 作者:平凡之路 来源:xuhantao.com 浏览:

GUI程序都是事件驱动的,目前这已经是大家的共识,X Window也不例外。在这一篇中,将展示X核心协议中有哪些事件,以及怎么使用XCB来捕获并响应事件。首先还是先给出一份完整的代码及其运行效果,然后再做详细的解释。代码如下:

1     #include <stdlib.h>
2 #include <stdio.h>
3 #include <inttypes.h>
4
5 #include <xcb/xcb.h>
6
7 /* print names of modifiers present in mask */
8 void
9 print_modifiers (uint32_t mask)
10 {
11 const char *MODIFIERS[] = {
12 "Shift", "Lock", "Ctrl", "Alt",
13 "Mod2", "Mod3", "Mod4", "Mod5",
14 "Button1", "Button2", "Button3", "Button4", "Button5"
15 };
16
17 printf ("Modifier mask: ");
18 for (const char **modifier = MODIFIERS ; mask; mask >>= 1, ++modifier) {
19 if (mask & 1) {
20 printf (*modifier);
21 }
22 }
23 printf ("\n");
24 }
25
26 int
27 main ()
28 {
29 /* Open the connection to the X server */
30 xcb_connection_t *connection = xcb_connect (NULL, NULL);
31
32 /* Get the first screen */
33 xcb_screen_t *screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;
34
35
36 /* Create the window */
37 xcb_window_t window = xcb_generate_id (connection);
38
39 uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
40 uint32_t values[2] = {screen->white_pixel,
41 XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS |
42 XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION |
43 XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW |
44 XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE };
45
46 xcb_create_window (connection,
47 0, /* depth */
48 window,
49 screen->root, /* parent window */
50 0, 0, /* x, y */
51 600, 400, /* width, height */
52 10, /* border_width */
53 XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */
54 screen->root_visual, /* visual */
55 mask, values ); /* masks */
56
57 /* Map the window on the screen */
58 xcb_map_window (connection, window);
59
60 xcb_flush (connection);
61
62 xcb_generic_event_t *event;
63 while ( (event = xcb_wait_for_event (connection)) ) {
64 switch (event->response_type & ~0x80) {
65 case XCB_EXPOSE: {
66 xcb_expose_event_t *expose = (xcb_expose_event_t *)event;
67
68 printf ("Window %"PRIu32" exposed. Region to be redrawn at location (%"PRIu16",%"PRIu16"), with dimension (%"PRIu16",%"PRIu16")\n",
69 expose->window, expose->x, expose->y, expose->width, expose->height );
70 break;
71 }
72 case XCB_BUTTON_PRESS: {
73 xcb_button_press_event_t *bp = (xcb_button_press_event_t *)event;
74 print_modifiers (bp->state);
75
76 switch (bp->detail) {
77 case 4:
78 printf ("Wheel Button up in window %"PRIu32", at coordinates (%"PRIi16",%"PRIi16")\n",
79 bp->event, bp->event_x, bp->event_y );
80 break;
81 case 5:
82 printf ("Wheel Button down in window %"PRIu32", at coordinates (%"PRIi16",%"PRIi16")\n",
83 bp->event, bp->event_x, bp->event_y );
84 break;
85 default:
86 printf ("Button %"PRIu8" pressed in window %"PRIu32", at coordinates (%"PRIi16",%"PRIi16")\n",
87 bp->detail, bp->event, bp->event_x, bp->event_y );
88 break;
89 }
90 break;
91 }
92 case XCB_BUTTON_RELEASE: {
93 xcb_button_release_event_t *br = (xcb_button_release_event_t *)event;
94 print_modifiers(br->state);
95
96 printf ("Button %"PRIu8" released in window %"PRIu32", at coordinates (%"PRIi16",%"PRIi16")\n",
97 br->detail, br->event, br->event_x, br->event_y );
98 break;
99 }
100 case XCB_MOTION_NOTIFY: {
101 xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)event;
102
103 printf ("Mouse moved in window %"PRIu32", at coordinates (%"PRIi16",%"PRIi16")\n",
104 motion->event, motion->event_x, motion->event_y );
105 break;
106 }
107 case XCB_ENTER_NOTIFY: {
108 xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *)event;
109
110 printf ("Mouse entered window %"PRIu32", at coordinates (%"PRIi16",%"PRIi16")\n",
111 enter->event, enter->event_x, enter->event_y );
112 break;
113 }
114 case XCB_LEAVE_NOTIFY: {
115 xcb_leave_notify_event_t *leave = (xcb_leave_notify_event_t *)event;
116
117 printf ("Mouse left window %"PRIu32", at coordinates (%"PRIi16",%"PRIi16")\n",
118 leave->event, leave->event_x, leave->event_y );
119 break;
120 }
121 case XCB_KEY_PRESS: {
122 xcb_key_press_event_t *kp = (xcb_key_press_event_t *)event;
123 print_modifiers(kp->state);
124
125 printf ("Key pressed in window %"PRIu32"\n",
126 kp->event);
127 break;
128 }
129 case XCB_KEY_RELEASE: {
130 xcb_key_release_event_t *kr = (xcb_key_release_event_t *)event;
131 print_modifiers(kr->state);
132
133 printf ("Key released in window %"PRIu32"\n",
134 kr->event);
135 break;
136 }
137 default:
138 /* Unknown event type, ignore it */
139 printf ("Unknown event: %"PRIu8"\n",
140 event->response_type);
141 break;
142 }
143
144 free (event);
145 }
146
147 return 0;
148 }

程序运行效果如下图:

返回栏目页:http://www.bianceng.cn/Programming/cplus/

X Window程序处理事件遵循以下流程:

1、创建窗口的时候向X服务器注册该程序需要处理哪些事件。这是为了效率方面的考虑,对于本程序不关心的事件,X Server就不用发过来了。在前面一篇创建gcontext的时候提到XCB使用一种特殊的mask、value数组的方式来向X Server指定gcontext的属性,创建window的时候也是使用这样一个mask、value数组的机制,只是mask和value的取值和gcontext不一样而已;

2、在程序中使用一个while循环,在循环中调用xcb_wait_for_event()函数来接受事件,也可以使用xcb_poll_for_event()函数来主动向X Server查询事件,接受到事件后,使用一个switch...case...来根据事件的类型处理相应的事件。如果接受到quit事件,则结束while循环,退出程序。

流程就是这么简单,主要是一些细节。比如,创建窗口时,mask的取值可以是下面这些值的组合:

typedef enum {
        XCB_CW_BACK_PIXMAP       = 1L<<0,
        XCB_CW_BACK_PIXEL        = 1L<<1,
        XCB_CW_BORDER_PIXMAP     = 1L<<2,
        XCB_CW_BORDER_PIXEL      = 1L<<3,
        XCB_CW_BIT_GRAVITY       = 1L<<4,
        XCB_CW_WIN_GRAVITY       = 1L<<5,
        XCB_CW_BACKING_STORE     = 1L<<6,
        XCB_CW_BACKING_PLANES    = 1L<<7,
        XCB_CW_BACKING_PIXEL     = 1L<<8,
        XCB_CW_OVERRIDE_REDIRECT = 1L<<9,
        XCB_CW_SAVE_UNDER        = 1L<<10,
        XCB_CW_EVENT_MASK        = 1L<<11,
        XCB_CW_DONT_PROPAGATE    = 1L<<12,
        XCB_CW_COLORMAP          = 1L<<13,
        XCB_CW_CURSOR            = 1L<<14
    } xcb_cw_t;

当在mask中指定了XCB_CW_EVENT_MASK后,就需要在value数组中对应的项填写程序关心的事件。程序关注的事件可以是以下值的组合:

这些枚举值的定义都是很明确的,所以这里不多做解释。

对于事件对应的数据结构。如果抽空到X.org翻看一下X11协议,会发现核心协议中对事件的定义很简单。所有事件的长度都是32字节,而且其第1个字节代表了事件的类型。所以,在程序中可以使用xcb_generic_event_t指针来保存从xcb_wait_for_event()函数返回的事件,然后根据event->response_type来决定事件的类型,然后再将这个指针强制转型为其它事件的指针。

如果想了解各个事件的具体的数据结构,在Vim中只需要一个Ctrl+]就能跳到相应的定义处,如下图:

最后,还有一个地方需要说明,那就是程序中的 switch (event->response_type & ~0x80) 这一句,为什么response_type要和~0x80按位与呢?同样,翻一下X11协议就可以获得答案。因为如果response_type的最高位为1,也就是事件号128~255代表该事件是其它程序使用SendEvent发送过来的。如果response_type的最高位为0,也就是事件号0~127代表该事件是X Server发送过来的。很显然,我们并不关心事件的来源,只关心事件的类型,所以,只需要知道response_type的低7位即可。另外,对于0~127这些数字,X核心协议最多只会使用0~63,而64~127是为其它扩展保留的。再另外,XCB教程中说目前只有33个事件需要处理,是不是很简单呢?

作者:cnblogs 京山游侠

相关文章
  • 没有相关文章
共有评论 0相关评论
发表我的评论
  • 大名:
  • 内容:
  • 徐汉涛(www.xuhantao.com) © 2024 版权所有 All Rights Reserved.
  • 部分内容来自网络,如有侵权请联系站长尽快处理 站长QQ:965898558(广告及站内业务受理) 网站备案号:蒙ICP备15000590号-1