如何等到窗口被映射和可见

时间:2014-06-10 18:54:26

标签: c linux x11 xlib

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

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

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

我已经有了这个问题的解决方案,但它非常hacky,因为它会轮询window属性,所以我想知道是否有更干净的版本。

这是我目前的做法:

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 个答案:

答案 0 :(得分: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
  }
}

请注意,您可能无法获得有关您自己的Windows映射的事件。这是因为WM很可能将顶级窗口重新设置为不是根目录的子窗口,而是中间装饰窗口的窗口。

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

  1. 检查您的窗口父级,父级的父级,...等是否是事件的映射窗口。
  2. XSelectInput (d, yourwindow, StructureNotifyMask);添加到混音中。
  3. 请注意,第一个选择包含SubstructureNotifyMask,第二个选择包含StructureNotifyMask,这是一个不同的掩码。

答案 1 :(得分:0)

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

您可能希望循环访问X11事件队列,因为我应该为此目的设计更高效。此外,您可以配置事件过滤器,以便仅获取特定窗口感兴趣的事件。

有用(但过时)链接可能是: Linux Journal X11 Tutorial检查第2页,了解有关安装过滤器和从X11队列获取事件的示例。