X11 DestroyNotify返回错误window参数 XCB reparenting窗口管理器开发问题
解决方案
核心问题排查
- 首先检查你是否取错了DestroyNotify的字段:
xcb_destroy_notify_event_t包含两个窗口相关字段,event是事件投递的目标窗口(即你设置了监听掩码的窗口),window才是实际被销毁的窗口,你需要匹配的是ev->window而非ev->event。 - 确认你在reparent客户端窗口时,给客户端原始窗口显式设置了
XCB_EVENT_MASK_STRUCTURE_NOTIFY掩码,只有设置了该掩码,你才能收到客户端顶层窗口本身的销毁事件,此时ev->window就会和你存储的client.window完全匹配。 - 你日志中出现的两次非预期DestroyNotify是客户端自行创建的子窗口销毁事件,这类窗口不属于你的管理列表,直接忽略即可,客户端顶层窗口销毁时你会收到对应事件。
实现修正
你需要单独实现DestroyNotify的处理逻辑,不要仅依赖UnmapNotify做取消管理,参考实现如下:
void handle_destroy_notify(xcb_destroy_notify_event_t *ev) { // 优先直接匹配管理的客户端窗口和框架窗口 for (auto it = clients.begin(); it != clients.end(); ++it) { if (it->window == ev->window) { xcb_unmap_window(conn, it->frame); xcb_destroy_window(conn, it->frame); // 销毁自行创建的框架窗口避免资源泄漏 clients.erase(it); return; } if (it->frame == ev->window) { clients.erase(it); return; } } // 兜底逻辑:遍历父窗口层级匹配,处理子窗口销毁触发的通知 xcb_window_t current = ev->window; while (current != root_window && current != XCB_WINDOW_NONE) { xcb_query_tree_cookie_t cookie = xcb_query_tree(conn, current); xcb_query_tree_reply_t *reply = xcb_query_tree_reply(conn, cookie, nullptr); if (!reply) break; xcb_window_t parent = reply->parent; free(reply); for (auto it = clients.begin(); it != clients.end(); ++it) { if (it->window == parent) { xcb_unmap_window(conn, it->frame); xcb_destroy_window(conn, it->frame); clients.erase(it); return; } } current = parent; } }
额外注意事项
- Reparent客户端窗口后,需要重新设置一次客户端窗口的事件掩码,避免客户端自身的掩码设置覆盖了你添加的StructureNotify监听
- 取消管理客户端时,务必销毁你自行创建的frame窗口,否则会产生僵尸窗口占用X服务器资源
内容的提问来源于stack exchange,提问作者gsb
相关产品推荐
相关产品推荐

