程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 使用XCB編寫X Window程序(三) 捕獲並響應事件(Event)

使用XCB編寫X Window程序(三) 捕獲並響應事件(Event)

編輯:關於C++

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 京山游俠

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved