【问题标题】:How do I obtain, and synchronize, a complete list of all X11 windows?如何获取和同步所有 X11 窗口的完整列表?
【发布时间】:2009-06-19 13:47:43
【问题描述】:

我想监控 X11 下所有打开的窗口。目前,我这样做如下:

  1. 通过从根窗口递归调用 XQueryTree 来初步遍历整个树
  2. 监听整个桌面的子结构变化:XSelectInput( display, root_window, SubstructureNotifyMask | PropertyChangeMask )
  3. 处理所有 MapNotify、UnmapNotify 和 DestroyNotify 事件,在此过程中更新我自己的窗口列表

我主要担心第1点。在递归过程中,XQueryTree会被多次调用。有什么办法可以确保树在此期间不会改变?换句话说,要在某个时间点获取整棵树的“快照”?

另外,我注意到在某些 X11 系统下,并非所有事件都正确到达。例如,当在桌面上打开一个新窗口时,该窗口的 MapNotify 可能永远不会到达我的监控应用程序。怎么会这样?有没有可能还没到就扔掉了?

更新:

我编写了一个小程序,它将监视根窗口上的 X 事件(见下文)。现在,当我运行这个程序并启动和退出 xcalc 时,我得到以下输出:

Reparented: 0x4a0005b to 0x1001e40
Mapped    : 0x1001e40
Destroyed : 0x1001e40

就是这样。 我从来没有收到关于真实窗口 (0x4a0005b) 被破坏的通知。甚至没有被映射!谁能告诉我为什么不呢? SubStructureNotifyMask 是否仅导致发送直接子窗口的事件而不是整个子树?

顺便说一句,这显然不会在 Compiz 运行时发生。然后不进行任何重养:

Mapped    : 0x4a0005b
Mapped    : 0x4e00233
Destroyed : 0x4a0005b
Destroyed : 0x4e00233

监控程序来源:

#include <X11/Xlib.h>
#include <cstdio>

int main()
{
    Display *display;
    Window rootwin;

    display = XOpenDisplay( NULL );
    rootwin = DefaultRootWindow( display );
    XSelectInput( display, rootwin, SubstructureNotifyMask );

    XEvent event;

    while ( 1 ) {
        XNextEvent( display, &event );
        if ( event.type == MapNotify ) {
            XMapEvent *mapevent = (XMapEvent *)&event;
            printf( "Mapped    : 0x%x\n", (unsigned int)(mapevent->window) );
        }
        if ( event.type == DestroyNotify ) {
            XDestroyWindowEvent *destroywindowevent = (XDestroyWindowEvent *)&event;
            printf( "Destroyed : 0x%x\n", (unsigned int)(destroywindowevent->window) );
        }
        if ( event.type == ReparentNotify ) {
            XReparentEvent *reparentevent = (XReparentEvent *)&event;
            printf( "Reparented: 0x%x to 0x%x\n", (unsigned int)(reparentevent->window), (unsigned int)(reparentevent->parent) );
        }
    }

    return 0;
}

【问题讨论】:

    标签: c linux x11 race-condition xorg


    【解决方案1】:

    看看xwininfo

    您可能还喜欢xpropxspy 以获取更多信息。

    更新: 是的。尝试将xwininfo-root-tree-children 一起使用,以使所有窗口都参与其中。

    可以使用xprop -spy 跟踪更改。

    【讨论】:

    • 谢谢!我查看了 xwininfo 的源代码,它似乎以与我相同的方式进行树遍历:没有保护它周围的构造。因此,如果在 XQueryTree 调用之间存在树更改的可能性,xwininfo 也会受到影响,并且我猜不会给出正确的结果......
    • @Marten,是的。 xwininfo 是一个快照,但它会给你层次结构。然后您可以打开几个窗口并使用它们运行 xprop -spy 以检查更新。这些工具有很多。查看x.org/archive/X11R6.9.0/doc/html/manindex1.html 的手册页列表
    【解决方案2】:

    我相信抓取 X 服务器 (XGrabServer(3)) 将阻止对窗口层次结构的更改。虽然它有点笨重,所以你应该只在你真的需要它时才这样做。

    有关遍历窗口层次结构、构建树、使用窗口事件使其保持最新以及忽略由于竞争而不可避免的 X 协议错误的代码示例,请参见源代码中的文件 src/VBox/Additions/x11/VBoxClient/seamless-x11.cpp VirtualBox 的代码。

    【讨论】:

    • XGrabServer 会给你全神贯注,但它也会断开所有其他客户端,这使得它对此毫无用处
    • XGrabServer 不会关闭其他客户端连接,它只是阻止 X 服务器处理它们上的任何请求,将它们全部冻结,直到您释放抓取。但是,如果您的代码在抓取过程中挂起,那么您基本上已经冻结了用户会话,让他们对您非常不满。
    【解决方案3】:

    X11 是一个远程协议。这意味着当您向 X 服务器查询任何信息时,您总是会得到自己的副本。当 X 服务器更新其内部数据结构时,您的副本永远不会改变。

    这意味着当您遍历树时,树不会突然改变,但是当您使用其中的信息(例如检查窗口)时,该信息可能已过时(可能有人关闭了窗口)。这就是为什么您需要进行适当的错误处理。

    【讨论】:

      猜你喜欢
      • 2019-03-29
      • 2014-08-27
      • 1970-01-01
      • 2012-04-06
      • 1970-01-01
      • 2013-01-25
      • 1970-01-01
      • 2013-12-31
      • 1970-01-01
      相关资源
      最近更新 更多