XQueryTree获取顶级窗口数量错误问题求助
问题原因与解决方案
问题原因
XQueryTree返回的是根窗口的全部直接子窗口,但GTK、Qt这类现代UI框架会为应用创建大量辅助窗口——比如托盘图标容器、工具提示窗口、客户端装饰组件、甚至隐藏的内部窗口——这些窗口都会直接作为根窗口的子节点存在,但它们并不是用户认知里的“顶级应用窗口”。以KeepassXC为例,它基于GTK开发,会在主窗口之外创建多个辅助窗口,导致XQueryTree返回的数量远多于实际可见的顶级窗口。
嵌套Xephyr环境本身可能会引入少量额外窗口节点,但这不是导致数值严重错误的核心原因。
解决方法
要得到真实的用户感知级顶级窗口数量,需要对XQueryTree返回的窗口列表进行过滤,基于EWMH(Extended Window Manager Hints)标准或X的基础属性筛选出符合条件的窗口。核心过滤逻辑包括:
- 检查
WM_STATE属性:标准顶级窗口都会设置该属性(用于标识窗口映射状态,如正常、最小化),辅助窗口通常不会设置。 - 过滤窗口类型:通过
_NET_WM_WINDOW_TYPE属性排除辅助窗口类型,比如托盘、提示框、通知类窗口。 - 检查窗口可见性:通过
XGetWindowAttributes确认窗口处于IsViewable状态(已映射且可见)。
代码示例
#include <X11/Xlib.h> #include <X11/Xutil.h> #include <stdio.h> #include <stdlib.h> // 判断窗口是否为真实顶级窗口 int is_real_top_level_window(Display *display, Window win) { Atom wm_state = XInternAtom(display, "WM_STATE", False); Atom net_wm_type = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); Atom net_wm_type_dock = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", False); Atom net_wm_type_tooltip = XInternAtom(display, "_NET_WM_WINDOW_TYPE_TOOLTIP", False); Atom net_wm_type_notification = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NOTIFICATION", False); // 检查WM_STATE属性是否存在 Atom actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *data = NULL; int result = XGetWindowProperty(display, win, wm_state, 0, 2, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &data); if (result != Success || data == NULL) { if (data) XFree(data); return 0; } XFree(data); // 排除辅助窗口类型 result = XGetWindowProperty(display, win, net_wm_type, 0, 10, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &data); if (result == Success && data != NULL) { Atom *types = (Atom *)data; for (unsigned long i = 0; i < nitems; i++) { if (types[i] == net_wm_type_dock || types[i] == net_wm_type_tooltip || types[i] == net_wm_type_notification) { XFree(data); return 0; } } XFree(data); } // 检查窗口是否可见 XWindowAttributes attrs; if (XGetWindowAttributes(display, win, &attrs) == 0) { return 0; } return attrs.map_state == IsViewable; } int main() { Display *display_ = XOpenDisplay(NULL); if (!display_) { fprintf(stderr, "无法打开显示器\n"); return 1; } Window root_ = DefaultRootWindow(display_); Window returned_root, returned_parent; Window* top_level_windows; unsigned int num_top_level_windows; XQueryTree( display_, root_, &returned_root, &returned_parent, &top_level_windows, &num_top_level_windows); // 过滤真实顶级窗口 unsigned int real_count = 0; for (unsigned int i = 0; i < num_top_level_windows; i++) { if (is_real_top_level_window(display_, top_level_windows[i])) { real_count++; } } printf("真实顶级窗口数量: %u\n", real_count); // 释放XQueryTree分配的内存 if (top_level_windows) { XFree(top_level_windows); } XCloseDisplay(display_); return 0; }
额外注意事项
- 编译时需链接X11库:
gcc -o top_win_count top_win_count.c -lX11 - 确保窗口管理器支持EWMH标准(绝大多数现代窗口管理器如Openbox、KWin、Mutter等均支持)
- 若需要统计包括最小化在内的所有顶级窗口,可去掉窗口可见性的检查逻辑。
内容的提问来源于stack exchange,提问作者Kuglatec
相关产品推荐
相关产品推荐

