如何正确处理 xcb 客户端消息
How to handle xcb client messages properly
以下代码使用 opengl 渲染上下文创建 window。
一切正常,但是 window 关闭事件与 wm_delete_window 原子不相关。
static int
hnd_connect_to_xcb
(
hnd_linux_window_t *_window
)
{
_window->renderer.display = XOpenDisplay((char *)NULL);
if (!hnd_assert(_window->renderer.display != NULL, "Could not open display"))
return HND_NK;
_window->renderer.default_screen = DefaultScreen(_window->renderer.display);
_window->connection = XGetXCBConnection(_window->renderer.display);
if (!hnd_assert(_window->connection != NULL, "Could not connect to X display"))
return HND_NK;
XSetEventQueueOwner(_window->renderer.display, XCBOwnsEventQueue);
/* Get screen data */
xcb_screen_iterator_t screen_iterator = xcb_setup_roots_iterator(xcb_get_setup(_window->connection));
for (int i = _window->renderer.default_screen; i > 0; --i)
xcb_screen_next(&screen_iterator);
_window->screen_data = screen_iterator.data;
if (!hnd_set_fb_configs(&_window->renderer))
return HND_NK;
return HND_OK;
}
static void
hnd_set_window_properties
(
hnd_linux_window_t *_window
)
{
/* Set title */
xcb_intern_atom_cookie_t utf8_string_cookie =
hnd_create_intern_atom_cookie(_window->connection, HND_NK, "UTF8_STRING");
_window->utf8_string = hnd_create_intern_atom_reply(_window->connection, utf8_string_cookie);
xcb_intern_atom_cookie_t wm_name_cookie =
hnd_create_intern_atom_cookie(_window->connection, HND_NK, "WM_NAME");
_window->wm_name = hnd_create_intern_atom_reply(_window->connection, wm_name_cookie);
xcb_change_property(_window->connection,
XCB_PROP_MODE_REPLACE,
_window->id,
_window->wm_name->atom,
_window->utf8_string->atom,
8,
strlen(_window->title),
_window->title);
/* Add listener for close event */
xcb_intern_atom_cookie_t wm_protocols_cookie =
hnd_create_intern_atom_cookie(_window->connection, HND_OK, "WM_PROTOCOLS");
_window->wm_protocols = hnd_create_intern_atom_reply(_window->connection, wm_protocols_cookie);
xcb_intern_atom_cookie_t wm_delete_cookie =
hnd_create_intern_atom_cookie(_window->connection, HND_NK, "WM_DELETE_WINDOW");
_window->wm_delete_window = hnd_create_intern_atom_reply(_window->connection, wm_delete_cookie);
xcb_change_property(_window->connection,
XCB_PROP_MODE_REPLACE,
_window->id,
_window->wm_protocols->atom,
XCB_ATOM_ATOM,
32,
1,
&_window->wm_delete_window->atom);
}
hnd_linux_window_t *
hnd_create_window
(
const char *_title,
unsigned int _left,
unsigned int _top,
unsigned int _width,
unsigned int _height,
unsigned int _decoration
)
{
hnd_linux_window_t *new_window = malloc(sizeof(hnd_linux_window_t));
if (!hnd_assert(new_window != NULL, NULL))
return NULL;
if (!hnd_connect_to_xcb(new_window))
return NULL;
new_window->title = (char *)_title;
new_window->left = _left;
new_window->top = _top;
new_window->width = _width;
new_window->height = _height;
new_window->running = HND_OK;
if (!hnd_init_renderer(&new_window->renderer))
{
hnd_destroy_window(new_window);
return NULL;
}
new_window->colormap_id = xcb_generate_id(new_window->connection);
xcb_create_colormap(new_window->connection,
XCB_COLORMAP_ALLOC_NONE,
new_window->colormap_id,
new_window->screen_data->root,
new_window->renderer.visual_id);
new_window->value_mask = XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
new_window->event_mask = XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_BUTTON_MOTION |
XCB_EVENT_MASK_ENTER_WINDOW |
XCB_EVENT_MASK_LEAVE_WINDOW |
XCB_EVENT_MASK_KEY_PRESS |
XCB_EVENT_MASK_KEY_RELEASE;
new_window->value_list[0] = new_window->event_mask;
new_window->value_list[1] = new_window->colormap_id;
/* Create window */
new_window->id = xcb_generate_id(new_window->connection);
xcb_create_window(new_window->connection,
XCB_COPY_FROM_PARENT,
new_window->id,
new_window->screen_data->root,
new_window->left,
new_window->top,
new_window->width,
new_window->height,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
new_window->renderer.visual_id,
new_window->value_mask,
new_window->value_list);
hnd_set_window_properties(new_window);
hnd_set_window_decoration(new_window, _decoration);
xcb_map_window(new_window->connection, new_window->id);
xcb_flush(new_window->connection);
/* Finish opengl binding */
new_window->renderer.gl_window = glXCreateWindow(new_window->renderer.display,
new_window->renderer.current_fb_config,
new_window->id,
0);
if (!hnd_assert(new_window->renderer.gl_window, "Could not create OpenGL window"))
{
hnd_destroy_window(new_window);
return NULL;
}
if (!hnd_assert(glXMakeContextCurrent(new_window->renderer.display,
new_window->renderer.gl_window,
new_window->renderer.gl_window,
new_window->renderer.gl_context),
"Could not set OpenGL current rendering context"))
{
hnd_destroy_window(new_window);
return NULL;
}
hnd_print_debug(HND_LOG, HND_CREATED("OpenGL context"), HND_SUCCESS);
hnd_print_debug(HND_LOG, HND_CREATED("window"), HND_SUCCESS);
return new_window;
}
void
hnd_destroy_window
(
hnd_linux_window_t *_window
)
{
if (!hnd_assert(_window != NULL, HND_SYNTAX))
return;
hnd_end_renderer(&_window->renderer);
xcb_free_colormap(_window->connection, _window->colormap_id);
hnd_print_debug(HND_LOG, HND_ENDED("window"), HND_SUCCESS);
}
void
hnd_poll_events
(
hnd_linux_window_t *_window,
hnd_event_t *_event
)
{
if (!hnd_assert(_window != NULL, HND_SYNTAX))
return;
if (!hnd_assert(_event != NULL, HND_SYNTAX))
return;
_event->xcb_event = xcb_poll_for_event(_window->connection);
if (!_event->xcb_event)
return;
switch (_event->xcb_event->response_type & 0x7f)
{
case XCB_EXPOSE:
xcb_flush(_window->connection);
break;
case XCB_KEY_PRESS:
hnd_queue_key_event(_event->pressed_keys, _event->xcb_event);
break;
case XCB_KEY_RELEASE:
hnd_queue_key_event(_event->released_keys, _event->xcb_event);
break;
case XCB_CLIENT_MESSAGE:
{
xcb_client_message_event_t *temp_client_message_event = (xcb_client_message_event_t *)_event;
if (temp_client_message_event->data.data32[0] == _window->wm_delete_window->atom)
_window->running = HND_NK;
} break;
default:
break;
}
}
除了这两个创建原子的助手之外,大多数渲染器函数都没有太大帮助:
xcb_intern_atom_cookie_t
hnd_create_intern_atom_cookie
(
xcb_connection_t *_connection,
unsigned int _exists,
const char *_string
)
{
return xcb_intern_atom(_connection, _exists, strlen(_string), _string);
}
xcb_intern_atom_reply_t *
hnd_create_intern_atom_reply
(
xcb_connection_t *_connection,
xcb_intern_atom_cookie_t _cookie
)
{
xcb_intern_atom_reply_t *new_atom_reply = xcb_intern_atom_reply(_connection, _cookie, NULL);
if (!hnd_assert(new_atom_reply != NULL, "Could not create atom"))
return NULL;
return new_atom_reply;
}
是的,我包括了外部文件。一切都正确编译。
问题基本上是错别字:
_event->xcb_event = xcb_poll_for_event(_window->connection);
[...]
switch (_event->xcb_event->response_type & 0x7f)
{
[...]
case XCB_CLIENT_MESSAGE:
{
xcb_client_message_event_t *temp_client_message_event = (xcb_client_message_event_t *)_event;
您正在 _event->xcb_event
中保存活动。但是您正在将 _event
转换为 xcb_client_message_event_t*
。你应该投射 _event->xcb_event
.
所以,最后一行应该是:
xcb_client_message_event_t *temp_client_message_event = (xcb_client_message_event_t *)_event->xcb_event;
以下代码使用 opengl 渲染上下文创建 window。 一切正常,但是 window 关闭事件与 wm_delete_window 原子不相关。
static int
hnd_connect_to_xcb
(
hnd_linux_window_t *_window
)
{
_window->renderer.display = XOpenDisplay((char *)NULL);
if (!hnd_assert(_window->renderer.display != NULL, "Could not open display"))
return HND_NK;
_window->renderer.default_screen = DefaultScreen(_window->renderer.display);
_window->connection = XGetXCBConnection(_window->renderer.display);
if (!hnd_assert(_window->connection != NULL, "Could not connect to X display"))
return HND_NK;
XSetEventQueueOwner(_window->renderer.display, XCBOwnsEventQueue);
/* Get screen data */
xcb_screen_iterator_t screen_iterator = xcb_setup_roots_iterator(xcb_get_setup(_window->connection));
for (int i = _window->renderer.default_screen; i > 0; --i)
xcb_screen_next(&screen_iterator);
_window->screen_data = screen_iterator.data;
if (!hnd_set_fb_configs(&_window->renderer))
return HND_NK;
return HND_OK;
}
static void
hnd_set_window_properties
(
hnd_linux_window_t *_window
)
{
/* Set title */
xcb_intern_atom_cookie_t utf8_string_cookie =
hnd_create_intern_atom_cookie(_window->connection, HND_NK, "UTF8_STRING");
_window->utf8_string = hnd_create_intern_atom_reply(_window->connection, utf8_string_cookie);
xcb_intern_atom_cookie_t wm_name_cookie =
hnd_create_intern_atom_cookie(_window->connection, HND_NK, "WM_NAME");
_window->wm_name = hnd_create_intern_atom_reply(_window->connection, wm_name_cookie);
xcb_change_property(_window->connection,
XCB_PROP_MODE_REPLACE,
_window->id,
_window->wm_name->atom,
_window->utf8_string->atom,
8,
strlen(_window->title),
_window->title);
/* Add listener for close event */
xcb_intern_atom_cookie_t wm_protocols_cookie =
hnd_create_intern_atom_cookie(_window->connection, HND_OK, "WM_PROTOCOLS");
_window->wm_protocols = hnd_create_intern_atom_reply(_window->connection, wm_protocols_cookie);
xcb_intern_atom_cookie_t wm_delete_cookie =
hnd_create_intern_atom_cookie(_window->connection, HND_NK, "WM_DELETE_WINDOW");
_window->wm_delete_window = hnd_create_intern_atom_reply(_window->connection, wm_delete_cookie);
xcb_change_property(_window->connection,
XCB_PROP_MODE_REPLACE,
_window->id,
_window->wm_protocols->atom,
XCB_ATOM_ATOM,
32,
1,
&_window->wm_delete_window->atom);
}
hnd_linux_window_t *
hnd_create_window
(
const char *_title,
unsigned int _left,
unsigned int _top,
unsigned int _width,
unsigned int _height,
unsigned int _decoration
)
{
hnd_linux_window_t *new_window = malloc(sizeof(hnd_linux_window_t));
if (!hnd_assert(new_window != NULL, NULL))
return NULL;
if (!hnd_connect_to_xcb(new_window))
return NULL;
new_window->title = (char *)_title;
new_window->left = _left;
new_window->top = _top;
new_window->width = _width;
new_window->height = _height;
new_window->running = HND_OK;
if (!hnd_init_renderer(&new_window->renderer))
{
hnd_destroy_window(new_window);
return NULL;
}
new_window->colormap_id = xcb_generate_id(new_window->connection);
xcb_create_colormap(new_window->connection,
XCB_COLORMAP_ALLOC_NONE,
new_window->colormap_id,
new_window->screen_data->root,
new_window->renderer.visual_id);
new_window->value_mask = XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
new_window->event_mask = XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_BUTTON_MOTION |
XCB_EVENT_MASK_ENTER_WINDOW |
XCB_EVENT_MASK_LEAVE_WINDOW |
XCB_EVENT_MASK_KEY_PRESS |
XCB_EVENT_MASK_KEY_RELEASE;
new_window->value_list[0] = new_window->event_mask;
new_window->value_list[1] = new_window->colormap_id;
/* Create window */
new_window->id = xcb_generate_id(new_window->connection);
xcb_create_window(new_window->connection,
XCB_COPY_FROM_PARENT,
new_window->id,
new_window->screen_data->root,
new_window->left,
new_window->top,
new_window->width,
new_window->height,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
new_window->renderer.visual_id,
new_window->value_mask,
new_window->value_list);
hnd_set_window_properties(new_window);
hnd_set_window_decoration(new_window, _decoration);
xcb_map_window(new_window->connection, new_window->id);
xcb_flush(new_window->connection);
/* Finish opengl binding */
new_window->renderer.gl_window = glXCreateWindow(new_window->renderer.display,
new_window->renderer.current_fb_config,
new_window->id,
0);
if (!hnd_assert(new_window->renderer.gl_window, "Could not create OpenGL window"))
{
hnd_destroy_window(new_window);
return NULL;
}
if (!hnd_assert(glXMakeContextCurrent(new_window->renderer.display,
new_window->renderer.gl_window,
new_window->renderer.gl_window,
new_window->renderer.gl_context),
"Could not set OpenGL current rendering context"))
{
hnd_destroy_window(new_window);
return NULL;
}
hnd_print_debug(HND_LOG, HND_CREATED("OpenGL context"), HND_SUCCESS);
hnd_print_debug(HND_LOG, HND_CREATED("window"), HND_SUCCESS);
return new_window;
}
void
hnd_destroy_window
(
hnd_linux_window_t *_window
)
{
if (!hnd_assert(_window != NULL, HND_SYNTAX))
return;
hnd_end_renderer(&_window->renderer);
xcb_free_colormap(_window->connection, _window->colormap_id);
hnd_print_debug(HND_LOG, HND_ENDED("window"), HND_SUCCESS);
}
void
hnd_poll_events
(
hnd_linux_window_t *_window,
hnd_event_t *_event
)
{
if (!hnd_assert(_window != NULL, HND_SYNTAX))
return;
if (!hnd_assert(_event != NULL, HND_SYNTAX))
return;
_event->xcb_event = xcb_poll_for_event(_window->connection);
if (!_event->xcb_event)
return;
switch (_event->xcb_event->response_type & 0x7f)
{
case XCB_EXPOSE:
xcb_flush(_window->connection);
break;
case XCB_KEY_PRESS:
hnd_queue_key_event(_event->pressed_keys, _event->xcb_event);
break;
case XCB_KEY_RELEASE:
hnd_queue_key_event(_event->released_keys, _event->xcb_event);
break;
case XCB_CLIENT_MESSAGE:
{
xcb_client_message_event_t *temp_client_message_event = (xcb_client_message_event_t *)_event;
if (temp_client_message_event->data.data32[0] == _window->wm_delete_window->atom)
_window->running = HND_NK;
} break;
default:
break;
}
}
除了这两个创建原子的助手之外,大多数渲染器函数都没有太大帮助:
xcb_intern_atom_cookie_t
hnd_create_intern_atom_cookie
(
xcb_connection_t *_connection,
unsigned int _exists,
const char *_string
)
{
return xcb_intern_atom(_connection, _exists, strlen(_string), _string);
}
xcb_intern_atom_reply_t *
hnd_create_intern_atom_reply
(
xcb_connection_t *_connection,
xcb_intern_atom_cookie_t _cookie
)
{
xcb_intern_atom_reply_t *new_atom_reply = xcb_intern_atom_reply(_connection, _cookie, NULL);
if (!hnd_assert(new_atom_reply != NULL, "Could not create atom"))
return NULL;
return new_atom_reply;
}
是的,我包括了外部文件。一切都正确编译。
问题基本上是错别字:
_event->xcb_event = xcb_poll_for_event(_window->connection);
[...]
switch (_event->xcb_event->response_type & 0x7f)
{
[...]
case XCB_CLIENT_MESSAGE:
{
xcb_client_message_event_t *temp_client_message_event = (xcb_client_message_event_t *)_event;
您正在 _event->xcb_event
中保存活动。但是您正在将 _event
转换为 xcb_client_message_event_t*
。你应该投射 _event->xcb_event
.
所以,最后一行应该是:
xcb_client_message_event_t *temp_client_message_event = (xcb_client_message_event_t *)_event->xcb_event;