如何等待窗口被映射并可见

7

如何正确等待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);   
}

这段代码可以正常工作,但是它有些hacky,所以我在这里提出讨论 - 以防有更干净的方法来完成这个任务。

2个回答

2

在根窗口上选择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()将坐等直到其他窗口被映射、取消映射、移动、提高、调整大小等,不是吗?所以我可能会遇到我的应用程序被阻塞的问题。 - Andreas
你可能有一个带有虚拟根的WM。我对它们没有太多经验。你需要找到虚拟根并使用它。 - n. m.
这只是一个标准的Mint安装(Maya)。我会在一个小的测试程序中再试一次,看看它在那里的表现如何。但是考虑到IsViewable可能会在调用XNextEvent()之前被X服务器设置,从而阻止我的应用程序直到另一个事件到达(这可能需要一些时间),你的解决方案可能是危险的。 - Andreas
嗯,如果在事件之前正确设置IsViewable,那就不是问题了。事实上,这是期望的顺序。在事件之后设置会有问题。 - n. m.
对我来说可以运行,不知道问题可能出在哪里。 - n. m.
显示剩余5条评论

0
据我所知,X11库没有公开任何回调机制来处理X11事件。(一旦您理解了事件过滤模型,您可以轻松地构建自己的回调机制)
您可能希望循环遍历X11事件队列,因为它是为此目的而设计的,所以应该更有效率。此外,您可以配置事件过滤器,以便仅获取您特定窗口感兴趣的事件。
一个有用的(虽然有点过时)链接可能是: Linux Journal X11教程 在第二页中查看有关安装过滤器和从X11队列获取事件的示例。

问题在于据我所知,没有事件被发送来表示窗口现在是可见的。有"MapNotify",我已经在等待它了(见上文),但这还不够。我还需要等待属性中的"IsViewable"被设置,但据我所知,没有事件来表示何时设置/更改此属性。 - Andreas
当我在“刷新”我的Xlib记忆时,我发现了以下事件:http://www.x.org/releases/X11R7.7/doc/libX11/libX11/libX11.html#VisibilityNotify_Events ,据我所知,它应该由窗口可见性状态更改触发。对于您的情况有意义吗? - sergico
我尝试了VisibilityNotify,但不幸的是它只在再次显示先前被最小化的窗口时发送。当创建并首次显示窗口时,不会发送VisibilityNotify。 - Andreas

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