单线程服务多个X11窗口时点击关闭按钮导致所有窗口消失问题咨询
核心误区澄清
你对XSetWMProtocols的功能存在误解:注册WM_DELETE_WINDOW协议完全不需要你自行实现窗口装饰、resize等逻辑,这些功能仍然由窗口管理器全权处理,你只是接管了关闭按钮的触发逻辑,属于X11编程的常规操作,没有额外开发成本。
你之前多线程架构下没有触发全窗口关闭的核心原因是:每个窗口对应独立线程,每个线程都创建了独立的Display连接,单个窗口销毁触发的连接错误只会影响对应线程的连接,其他线程的独立连接不受影响。切换到单线程架构后所有窗口复用同一个Display连接,默认逻辑下点关闭按钮会触发窗口管理器直接销毁对应窗口,后续你对已销毁窗口XID的操作会触发X服务端断开整个Display连接,自然所有窗口全部消失。
具体解决步骤
你只需要给每个窗口注册WM_DELETE_WINDOW协议即可,无需修改其他窗口管理器相关逻辑:
- 全局获取关闭事件的原子值:
Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
- 每个窗口创建完成后,给窗口绑定该协议:
XSetWMProtocols(display, window, &wm_delete_window, 1);
- 在你的事件循环中处理
ClientMessage事件:
XEvent event; XNextEvent(display, &event); if (event.type == ClientMessage) { if (event.xclient.data.l[0] == wm_delete_window) { // 在这里单独销毁当前触发关闭的窗口、清理对应资源即可 XDestroyWindow(display, event.xclient.window); // 其他窗口不受影响,连接保持正常 } }
你当前现象的解释
你看到的_XIOError回调触发时窗口已经全部消失,是因为该回调是X连接已经被服务端断开后才触发的后置错误处理,此时整个连接已经失效,所有和该连接绑定的窗口资源都已经被X服务端回收。你之前设置的普通错误处理回调没有触发,是因为X服务端遇到无效XID操作时直接返回了致命错误断开连接,没有走普通错误响应流程。
关于调试符号缺失的问题,你安装的libx11-dev libx11-xcb-dev是开发头文件包,不含调试符号,Debian/Ubuntu系发行版需要额外安装libx11-6-dbg libx11-xcb1-dbg包才能拿到libX11的调试符号。
内容的提问来源于stack exchange,提问作者Bernd Elkemann

