如何在XCB示例程序中订阅各类事件并调试?含实践问题
基于XCB实现简易窗口管理器的问题
我正在开发一个基于XCB库的简易应用,目标是模拟简单窗口管理器的行为。我知道需要在根窗口上设置Substructure Redirection,然后监听特定事件来管理顶层窗口,但不知道怎么测试现有方案。
实现代码
#include <stdlib.h> #include <stdio.h> #include <xcb/xcb.h> #include <xcb/xproto.h> int main() { xcb_connection_t *connection = xcb_connect(NULL, NULL); xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data; /* Subscribing to the desired events */ uint32_t mask = XCB_CW_EVENT_MASK | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; uint32_t values[3] = { XCB_KEY_PRESS, 1, 1 }; /* This applies the values and mask to the root window I think */ xcb_change_window_attributes(connection, screen->root, mask, values); xcb_flush(connection); xcb_generic_event_t *ev; while( (ev = xcb_wait_for_event(connection)) ) { switch(ev->response_type & ~0x80) { case XCB_KEY_PRESS: { xcb_key_press_event_t *ev = (xcb_key_press_event_t *)ev; if(ev->state & XCB_MOD_MASK_1) { printf("Pressed the Alt key modifier\n"); } printf("Key pressed: Keycode %d\n", ev->detail); break; } case XCB_CREATE_NOTIFY: { printf("CREATE NOTIFY\n"); break; } case XCB_MAP_NOTIFY: { printf("MAP NOTIFY\n"); break; } } free(ev); } xcb_disconnect(connection); }
测试方式
我使用Xephyr运行程序:
- 先执行
Xephyr -br -ac -noreset -screen 500x500 :1创建新窗口 - 再通过
DISPLAY=:1 ./myprogram测试
目前屏幕无任何反应,我想知道如何获取printf的输出内容。
补充说明
我添加XCB_CREATE_NOTIFY和XCB_MAP_NOTIFY事件的原因是,我认为开启子结构重定向后,当在窗口内启动新应用时,X服务器会向窗口管理器发送这些请求,这样我就能修改窗口的目标属性。如果我的理解有误,请指正。
问题分析与解决
1. 获取printf输出的方法
printf的输出默认会打印到启动程序的终端中,而非Xephyr窗口。你只需保留运行DISPLAY=:1 ./myprogram的终端,直接查看终端输出即可。若需要留存输出,可将内容重定向到文件:
DISPLAY=:1 ./myprogram > output.log 2>&1
之后查看output.log文件就能获取所有输出内容。
2. 代码核心错误修正
你的窗口属性设置逻辑存在错误:
- 掩码定义错误:
XCB_CW_EVENT_MASK是属性标识掩码,应该单独作为xcb_change_window_attributes的mask参数,而非和事件掩码并列。 - 事件掩码值错误:
values数组中应该传入事件掩码的组合,而非事件类型XCB_KEY_PRESS。
修正后的代码片段:
/* 正确设置事件掩码 */ uint32_t mask = XCB_CW_EVENT_MASK; uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_KEY_PRESS }; /* 应用到根窗口 */ xcb_change_window_attributes(connection, screen->root, mask, values);
3. 事件监听逻辑修正
开启SubstructureRedirect后,X服务器不会直接发送XCB_CREATE_NOTIFY和XCB_MAP_NOTIFY通知事件,而是会将对应的创建、映射请求拦截并发送给窗口管理器,对应的事件类型为XCB_CREATE_WINDOW和XCB_MAP_REQUEST。你需要在事件循环中处理这些请求:
switch(ev->response_type & ~0x80) { // ... 保留原有case case XCB_MAP_REQUEST: { printf("MAP REQUEST\n"); // 必须显式允许窗口映射,否则新窗口无法显示 xcb_map_window(connection, ((xcb_map_request_event_t*)ev)->window); xcb_flush(connection); break; } case XCB_CREATE_WINDOW: { printf("CREATE WINDOW REQUEST\n"); break; } }
只有当窗口管理器处理并允许这些请求后,X服务器才会发送对应的MAP_NOTIFY等通知事件。
内容的提问来源于stack exchange,提问作者gabriel_tiso
相关产品推荐
相关产品推荐

