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

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

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

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

我已经有这个问题的解决scheme,但它是相当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,所以我把它放在这里辩论 – 以防万一有一个更干净的方式做到这一点。

在根窗口上选择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 ,不同的掩码。

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

您可能希望在X11事件队列上循环,因为我认为这应该是更有效的设计用于此目的。 此外,您可以配置事件过滤器,以便您只获取特定窗口感兴趣的事件。

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