【问题标题】:How to wait until window is mapped and viewable如何等到窗口被映射并可见
【发布时间】:2014-06-10 18:54:26
【问题描述】:

等到 X11 窗口被映射并可见的正确方法是什么?确切地说,我想等到我可以安全地调用 XSetInputFocus() 而不会遇到任何 X 服务器适得其反的风险并出现以下错误:

// X Error of failed request:  BadMatch (invalid parameter attributes)
// Major opcode of failed request:  42 (X_SetInputFocus)    

目前,此错误经常发生,尤其是在慢速 X 服务器上,或者在使用 libXrandr 更改显示器分辨率后立即尝试打开新窗口时。

我已经有了解决这个问题的方法,但它很hacky,因为它会轮询窗口属性,所以我想知道是否有更简洁的版本。

这是我目前的做法:

static Bool predicate(Display *display, XEvent *ev, XPointer arg)
{
    return(ev->type == MapNotify);
}

static void waitmapnotify(struct osdisplayinfo *osd)
{
    XEvent ev;
    XWindowAttributes xwa;

    XPeekIfEvent(osd->display, &ev, predicate, NULL);

    do {
        XGetWindowAttributes(osd->display, osd->window, &xwa);
        usleep(1);
    } while(xwa.map_state != IsViewable);   
}

这段代码运行良好,但它很老套,所以我在这里提出讨论——以防万一有更简洁的方法来做这件事。

【问题讨论】:

    标签: c linux x11 xlib


    【解决方案1】:

    在根窗口中选择SubstructureNotifyMask。每次映射、取消映射、移动、提升、调整大小等顶级窗口时,您都应该得到一个事件。这些事件可能会改变顶级窗口的可见性。每当发生此类事件时,此程序都会打印一条消息:

    #include <X11/Xlib.h>
    #include <stdio.h>
    int main ()
    {
      Display* d = XOpenDisplay(0);
      int cnt = 0;
      XEvent ev;    
      XSelectInput (d, RootWindow(d, DefaultScreen(d)), SubstructureNotifyMask);    
      while (1)
      {
        XNextEvent(d, &ev);
        printf ("Got an event %d!\n", cnt++);
        // <----- do your XGetWindowAttributes(...) check here
      }
    }
    

    请注意,您可能不会收到有关您自己的窗口被映射的事件。这是因为 WM 可能会将顶级窗口重新设置为不是根的子窗口,而是中间装饰窗口的子窗口。

    应对这种情况有两种方法:

    1. 检查您的窗口父级、父级的父级等是否是事件的映射窗口。
    2. XSelectInput (d, yourwindow, StructureNotifyMask); 添加到组合中。

    注意第一个选择有SubstructureNotifyMask,第二个选择有StructureNotifyMask,不同的掩码。

    【讨论】:

    • 谢谢,但这对我不起作用。我已经尝试过了,它只是在那里无限循环。此外,即使它有效,我想知道如果在调用 XNextEvent() 之前状态更改为 IsViewable 会发生什么?然后 XNextEvent() 会坐下来等到其他一些窗口被映射、取消映射、移动、提升、调整大小等,不是吗?所以我可能会遇到我的应用程序被阻止的情况。
    • 您可能有一个带有虚拟根目录的 WM。我对他们没有太多经验。您需要找到虚拟根目录并使用它。
    • IsViewable 不会自行更改,而是因为(因此之后)根子结构中的某些内容发生了更改。
    • 如果您在其他一些窗口系统(Mac/Windows)上实现了 X,则此方法将不起作用。
    • 这只是一个标准的 Mint 安装 (Maya)。我将在一个小型测试程序中再试一次,看看它在其中的表现如何。但是,考虑到 IsViewable 可以由 X 服务器在 XNextEvent() 被调用之前设置,因此您的解决方案是否可能会很危险,从而阻止我的应用程序直到另一个事件到达(这可能需要一些时间)。
    【解决方案2】:

    据我所知,X11 库没有公开任何用于 X11 事件处理的回调机制。 (了解事件过滤模型后,您可以轻松构建自己的)

    当我来宾时,您可能希望在 X11 事件队列上循环,这应该更有效地为此目的而设计。此外,您可以配置事件过滤器,以便仅获取特定窗口感兴趣的事件。

    一个有用的(虽然过时的)链接可能是: Linux Journal X11 Tutorial查看第二页以获取有关安装过滤器和从 X11 队列获取事件的示例。

    【讨论】:

    • 问题是 AFAICS 没有发送事件来表明窗口现在是可见的。有“MapNotify”,我已经在等待这个(见上文),但这还不够。我还需要等待在属性中设置“IsViewable”,但 AFAICS 没有事件会在设置/更改此属性时发出信号。
    • 当我在 Xlib 上“刷新”我的记忆时,我发现了以下事件:x.org/releases/X11R7.7/doc/libX11/libX11/… AFAIA,应该由窗口可见性状态更改触发。这对您的场景有意义吗?
    • 我尝试了 VisibilityNotify 但不幸的是,它仅在再次显示先前已最小化的窗口时才发送。创建并首次显示窗口时不会发送 VisibilityNotify。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-10
    • 2020-02-14
    • 2011-09-21
    相关资源
    最近更新 更多