我发现几乎每个教程都告诉我为我的事件循环执行此操作:
XEvent event; while (true) { XNextEvent(display, &event); switch (event.type) { case Expose: printf("Expose\n"); break; default: break; } }
但是,单击Xclosures程序将导致此消息。
XIO: fatal IO error 11 (Resource temporarily unavailable) on X server ":0" after 10 requests (10 known processed) with 0 events remaining.
这些例子表明使用无限循环对我来说确实很奇怪。 这听起来不自然,我的其他X11程序不这样做。 所以我search了一下。 我发现如何捕捉窗口closures事件。
Atom wmDeleteMessage = XInternAtom(mDisplay, "WM_DELETE_WINDOW", False); XSetWMProtocols(display, window, &wmDeleteMessage, 1); XEvent event; bool running = true; while (running) { XNextEvent(display, &event); switch (event.type) { case Expose: printf("Expose\n"); break; case ClientMessage: if (event.xclient.data.l[0] == wmDeleteMessage) running = false; break; default: break; } }
这样可行。 它没有错误地退出。 …但我拒绝相信这是做事的正常方式。 我的意思是,这是正确退出X11应用程序的唯一方法吗? 捕捉closures事件似乎有很多工作要做。 我如何做一个“正确的”事件循环? 为什么近距离事件如此深埋? 我错过了什么?
在X11中没有“退出按钮”或“应用程序”或“关闭事件”。 这是设计。
窗口装饰,退出按钮和许多我们所依赖的其他东西都没有内置到X11中。 它们是在核心X11上实现的。 负责wmDeleteMessage
组特定的约定的wmDeleteMessage
是ICCCM,查找它。
Xlib只处理核心X11协议。 没有内置的关闭事件。
有一些工具包可以处理ICCCM和所有其他没有内置到X11中的东西(GTK,wxWindows,Qt,…)。您可能想要使用其中的一种。
问题在于X server和Window Manager之间的通信。
当您调用XCreateWindow
或XCreateSimpleWindow
,X服务器会创建您的窗口(直到您通过调用XMapWindow
将其明确映射到屏幕上之前,才会显示该窗口),然后窗口管理器负责将窗口周围的所有装饰,按钮和系统菜单。
你可以自己调用XDestroyWindow
来移除窗口,这通常意味着它只是从屏幕上消失,但是你的程序仍在运行,并且连接到X服务器仍然是打开的,所以你可以发送更多的请求。
当用户点击窗口管理器附加到窗口的那个小X按钮时,问题就开始了,因为它不是由X服务器创建的,而是决定该怎么做的不是他的业务。 现在这一切都在窗口管理器手中。
如果窗口管理器在你的窗口上简单地调用了XDestroyWindow
,那么如果你的应用程序想要捕获关闭事件来在窗口被破坏之前做一些事情的话,会导致一个问题。 所以X服务器和窗口管理器之间已经建立了这个约定来处理这个过程。
大多数窗口管理器的默认行为是销毁窗口并关闭与X服务器的连接 ,因为这是大多数窗口管理器用户所期望的:当他们关闭窗口时,程序将结束(连接到X server将关闭并关闭窗口)。 然后,当您尝试调用XCloseDisplay(display)
,将会导致您提到的IO错误,因为与服务器的连接已经关闭,并且display
结构无效。
下面是Xlib文档的一个摘录,它解释了这一点:
如果用户要求删除其中一个客户端的顶级窗口,
WM_DELETE_WINDOW
在WM_PROTOCOLS
属性中选择不包括WM_DELETE_WINDOW
客户端可能会与服务器断开连接。
是的,如果他们没有在文档中隐藏太深的话,这将是非常棒的,但是: – 但是当你已经找到它的时候,幸运的是它也提示了解决方案。
如果您需要不同的行为(即从窗口管理器捕获关闭事件),则需要使用WM_DESTROY_WINDOW
协议。
文档的另一个摘录:
客户端(通常是具有多个顶级窗口的客户端,其服务器连接必须在删除某些顶级窗口后才能存活)应在每个此类窗口的
WM_PROTOCOLS
属性中包含原子WM_DELETE_WINDOW
。 他们将收到一个ClientMessage
事件,其data[0]
字段为WM_DELETE_WINDOW
。
我有同样的错误,我想知道究竟是什么原因,为什么。 我花了一些时间弄清楚,在文档中找到正确的解释,所以我把这个解释放在这里,以节省别人不知情的时间。