问题末尾,我在X11中有一个非常简单的“ Hello World”。但是当它退出时,我得到下面的运行时错误消息:
$ ./xtest
XIO: fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
after 9 requests (7 known processed) with 0 events remaining.
所以我尝试自己处理wmDeleteMessage
,并且能够停止关闭窗口,所以我知道我正确地获取了该事件。比起我在事件处理中添加了XDestroyWindow()
,我得到了新的错误。
X Error of failed request: BadWindow (invalid Window parameter)
Major opcode of failed request: 4 (X_DestroyWindow)
Resource id in failed request: 0x130
Serial number of failed request: 12
Current serial number in output stream: 12
听起来好像我正在尝试销毁一个已经销毁的Window,但是如果我取出XDestroyWindow()
,它在屏幕上仍然有效。
下面是我的代码,尝试使用销毁窗口处理程序。如何退出而没有任何错误?
#include<X11/Xlib.h>
#include <iostream>
int main()
{
Display *display;
if(!(display=XOpenDisplay(NULL)))
{
std::cerr << "ERROR: could not open display\n";
return 1;
}
int screen = DefaultScreen(display);
Window rootwind = RootWindow(display, screen);
Colormap cmap = DefaultColormap(display, screen);
Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
int blackColor = BlackPixel(display, screen);
int whiteColor = WhitePixel(display, screen);
Window w = XCreateSimpleWindow(display, rootwind, 0, 0, 200, 100, 0, blackColor, blackColor);
XMapWindow(display, w);
XSetWMProtocols(display, w, &wmDeleteMessage, 1);
bool running = true;
while(running)
{
XEvent e;
XNextEvent(display, &e);
switch (e.type)
{
case ClientMessage:
if(e.xclient.data.l[0] == wmDeleteMessage)
{
std::cout << "Shutting down now!!!" << std::endl;
XDestroyWindow(display,e.xdestroywindow.window);
running=false;
break;
}
break;
}
}
XCloseDisplay(display);
return 0;
}
更新
将行更改为:
std::cout << "Shutting down now!!!" << std::endl;
XDestroyWindow(display,w);
我不喜欢,因为我计划拥有更多的窗户,但现在我回到我遇到的第一条错误消息:
XIO: fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
after 9 requests (7 known processed) with 0 events remaining.
更新
[试图改变许多事情,例如使循环从XPending()中消失。决定运行别人的hello world,我的代码也遇到了同样的问题。我的设置一定有问题。
更新显然很多人都有这个问题。 Google ftk遇到了此问题,他们将其修复为change log。他们调用FTK_QUIT(),我猜它就像Exit()一样。所以我把我的回报放在循环内,就解决了问题。不知道为什么,但是确实如此。固定代码:
case ClientMessage:
if(e.xclient.data.l[0] == wmDeleteMessage)
{
XDestroyWindow(display,e.xclient.window);
XCloseDisplay(display);
return 0;
}
仍然会给能够解释为什么以及是否有可能将return语句(连同XCloseDisplay
一起)移出循环的人提供正确答案。
事件循环应该看起来像这样才能正确退出:
XEvent e;
do
{
XNextEvent(display, &e);
if(e.type == ClientMessage && e.xclient.data.l[0] == wmDeleteMessage)
{
XDestroyWindow(display,e.xclient.window);
break;
}
//...
}while (XPending(display) > 0)
XCloseDisplay(display);
return 0;
在switch
语句中运行时,代码不起作用。即使它退出循环而没有调用另一个X函数。上面的if
语句之前的switch
语句解决了该问题,而无需从循环内的程序返回。
解决此问题的方法很简单:
您必须通过XDestroyWindow()函数使用正确的结构成员。
由于X11事件结构的实现标准,它们彼此非常相似。每个结构都以'type'成员开头,而第一个成员实际上总是相同的。
现在假设:
int = 4 bytes
Bool = 4 bytes
unsigned long = 8 bytes
Display* = 8 bytes
Window = 4 bytes
如果您使用e.xdestroywindow.window调用XDestroyWindow(),则距离事件结构的开头将有28个字节,而如果使用e.xclient.window ,那么您将有24个字节。
由于您将使用错误的Window参数调用XDestroyWindow(),因此它将失败。相反,如果您使用e.xdestroywindow.event调用它(距离事件结构的开头24个字节),则该地址将是正确的,并且该函数将正常运行。
如果您看一下Xlib.h文件,您会注意到两个结构的window元素位置不同。
指出这一点,请记住Xlib已经开发了很多年,并且每天都有许多程序员在使用它,因此,如果有一个神秘的错误,它可能不在Xlib中。作为最后的提示,我想告诉您:如果您想进一步了解Xlib编程,请始终将头文件作为主要参考,然后是系统手册,然后是所有其他文件。
最后,您的代码唯一的错误是:
XDestroyWindow(display,e.xdestroywindow.window);
必须更改为此:
XDestroyWindow(display,e.xclient.window);
相反,使用switch很好,并且使用最多,在X11代码上没有问题。
NOTE:我自己测试了您的代码,仅更改了该行,然后进行了各种测试,并打印了结果。 XDestroyWindow()行肯定是唯一的错误。
只需在XDestroyWindow()
之前呼叫XCloseDisplay()
。
编辑:
[抱歉,我不了解XSetWMProtocols。现在,我已经阅读了。我认为您访问的是事件联合的错误成员。
XDestroyWindow(display,e.xdestroywindow.window);
应该是:
XDestroyWindow(display,e.xclient.window);
我遇到了同样的问题,在仔细阅读了Xlib文档和大量实验之后,我认为我知道您问题的答案,并且可以向您解释。
[先调用XCreateWindow
或XCreateSimpleWindow
,然后再调用XMapWindow
,则指示X Server创建窗口并映射到屏幕上。在将这些命令从本地缓冲区发送到服务器后(通过调用XFlush
或向服务器请求一些数据的任何函数,因为它隐式刷新了命令缓冲区),X Server将显示您的窗口。然后,窗口管理器的工作就是将所有装饰附加到窗口上,例如一些边框,标题栏,窗口菜单以及用于最小化/最大化/关闭窗口的按钮。
现在显示窗口,过一会儿您可以决定用XDestroyWindow
销毁它,并通过调用XCloseDisplay
关闭与X Server的连接,一切都会好起来,没有错误。
问题是,当用户单击窗口标题栏上的X时,处理它不是X Server的工作,而是Window Manager的工作(X Server对这些装饰一无所知并且不在乎)。当用户关闭程序的顶级窗口时,Window Manager的通常反应是销毁该窗口并关闭与X Server的连接,因为这是大多数用户的期望。您的程序可能仍在屏幕外运行,但是顶层窗口通常通过窗口管理器与X Server连接关联。
因此,当窗口管理器销毁您的窗口时,您无法调用XDestroyWindow
,因为该窗口已被销毁并且其Window
句柄无效。您会收到关于BadWindow
的错误。您也不能调用XCloseDisplay
,因为与X Server的连接已经关闭,这将导致XIO: fatal IO error 11 (Resource temporarily unavailable) on X server
错误,许多用户从其作者不知道的应用程序中体验到。这是一个常见的错误,因为一方面鼓励您自己进行清理,但另一方面文档误导了如何正确执行此操作。
但是,关于X Server和Window Manager应该如何合作有一个约定,该约定还涉及响应用户的命令以关闭顶层窗口。处理它的X协议有一个扩展。 Xlib documentation的解释如下:
客户端,通常是那些具有多个顶级窗口的客户端,其服务器连接必须在删除某些顶级窗口后仍能幸免,因此应在每个此类窗口的
WM_DELETE_WINDOW
属性中包含原子WM_PROTOCOLS
。他们将收到如上所述的ClientMessage
事件,其data[0]
字段为WM_DELETE_WINDOW
。[...]如果用户要求删除客户端的顶级窗口之一,则选择不在WM_DELETE_WINDOW
属性中包含WM_PROTOCOLS
的客户端可能会与服务器断开连接。
因此,有两种解决方案:避免在窗口管理器关闭窗口而不是自己关闭窗口时调用XDestroyWindow
和XCloseDisplay
(实际上您不必清理顶层窗口因为X Server仍然会在程序结束时销毁它),或者您需要注册WM_DESTROY_WINDOW
扩展名并在用户指示关闭窗口时等待窗口管理器的通知(它将向您发送[然后将其ClientMessage
设置为data[0]
)。在收到它之后,只需销毁窗口并自己关闭与X Server的连接,然后结束程序即可。或者,如果需要,请保持与X Server的连接打开,以与其进行更多通信。当您处理WM_DELETE_WINDOW
时,窗口管理器将不会尝试破坏您的窗口,也不会关闭与X服务器的连接。