如何使用xlib识别顶级X11窗口?

13

我正在尝试获取X11会话中所有顶层桌面窗口的列表。基本上,我想获取所有在窗口管理器应用程序切换UI中显示的窗口列表(通常在用户按下 ALT+TAB 时打开)。

我以前没有进行过任何 X11 编程,但到目前为止,我已经成功枚举了整个窗口列表,代码看起来类似于这样:

void CSoftwareInfoLinux::enumerateWindows(Display *display, Window rootWindow)
{
    Window parent;
    Window *children;
    Window *child;
    quint32 nNumChildren;

    XTextProperty wmName;
    XTextProperty wmCommand;

    int status = XGetWMName(display, rootWindow, &wmName);
    if (status && wmName.value && wmName.nitems)
    {
        int i;
        char **list;
        status = XmbTextPropertyToTextList(display, &wmName, &list, &i);
        if (status >= Success && i && *list)
        {
            qDebug() << "Found window with name:" << (char*) *list;
        }

        status = XGetCommand(display, rootWindow, &list, &i);
        if (status >= Success && i && *list)
        {
            qDebug() << "... and Command:" << i << (char*) *list;
        }

        Window tf;
        status = XGetTransientForHint(display, rootWindow, &tf);
        if (status >= Success && tf)
        {
            qDebug() << "TF set!";
        }

        XWMHints *pHints = XGetWMHints(display, rootWindow);
        if (pHints)
        {
            qDebug() << "Flags:" << pHints->flags
                    << "Window group:" << pHints->window_group;
        }
    }

    status = XQueryTree(display, rootWindow, &rootWindow, &parent, &children, &nNumChildren);
    if (status == 0)
    {
        // Could not query window tree further, aborting
        return;
    }

    if (nNumChildren == 0)
    {
        // No more children found. Aborting
        return;
    }

    for (int i = 0; i < nNumChildren; i++)
    {
        enumerateWindows(display, children[i]);
    }

    XFree((char*) children);
}

使用enumerateWindows(),最初用根窗口调用。

这个函数能够打印出数百个窗口的信息。我需要确定一个给定的Window是顶级桌面应用程序窗口(不确定官方术语是什么)还是其他类型的窗口。请问有人能够提供一些帮助吗?我找到的所有X11编程参考文献都过于枯燥难懂。或许有人可以指点我一个更好的资源?

3个回答

13

我有一个解决方案!

嗯,有点。

如果你的窗口管理器使用了扩展窗口管理器提示(EWMH),你可以使用“_NET_CLIENT_LIST”原子查询根窗口。这将返回窗口管理器正在管理的客户端窗口列表。有关更多信息,请参见这里

但是,这种方法存在一些问题。首先,使用的窗口管理器必须支持EWMH。KDE和GNOME支持,我相信其他一些窗口管理器也支持。但是,我相信有很多不支持的窗口管理器。此外,我注意到KDE存在一些问题。基本上,一些非KDE应用程序不会包含在列表中。例如,如果在KDE下运行xcalc,则不会显示在此列表中。

如果有人能提供任何改进此方法的建议,我会很高兴听取他们的意见。供参考,我使用的代码如下:

    Atom a = XInternAtom(m_pDisplay, "_NET_CLIENT_LIST" , true);
    Atom actualType;
    int format;
    unsigned long numItems, bytesAfter;
    unsigned char *data =0;
    int status = XGetWindowProperty(m_pDisplay,
                                rootWindow,
                                a,
                                0L,
                                (~0L),
                                false,
                                AnyPropertyType,
                                &actualType,
                                &format,
                                &numItems,
                                &bytesAfter,
                                &data);

    if (status >= Success && numItems)
    {
        // success - we have data: Format should always be 32:
        Q_ASSERT(format == 32);
        // cast to proper format, and iterate through values:
        quint32 *array = (quint32*) data;
        for (quint32 k = 0; k < numItems; k++)
        {
            // get window Id:
            Window w = (Window) array[k];

            qDebug() << "Scanned client window:" << w;
        }
        XFree(data);
    }

这段代码不正确,32位格式是指服务器上使用的比特数,而不是客户端。客户端始终使用“long”来表示XID值。您的数组必须是“long”类型的数组,而不是quint32。 - John Meacham
不错,但对我来说没有帮助,因为我仍在使用CDE(因此是dtwm)。对于大多数用户来说,这应该是有效的,但请记住,如果您正在阅读此内容,则并非所有地方都适用! - Wyatt Ward

6
为了进一步解决问题,如果您想获取窗口名称:
// get window Id:
Window w = (Window) array[k];

char* name = '\0';
status = XFetchName(display, w, &name);
if (status >= Success)
{
    if (name == NULL)
        printf("Found: %ul  NULL\n", w);
    else
        printf("Found: %ul  %s\n", w, name);
}
XFree(name);

1
请注意,XFetchName 可能会成功但设置 name=NULL。在这种情况下,您的示例将崩溃。此外,char* name='\0'; 是具有误导性的,常量应该看起来像 NULL,因为它是一个指针而不是一个字符。 - Ruslan

0
如果您不必使用Xlib,可以使用GDK的和来满足您的需求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接