You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 16:13:25