我在问题末尾的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
听起来我正试图摧毁已经被摧毁的窗口,但是如果我取出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并且我的代码遇到同样的问题。我的设置一定有问题。
更新 显然很多人都有这个问题。谷歌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
语句之前修复问题而不从循环内的程序返回。
答案 0 :(得分:16)
这个问题的解决方案很简单:
您必须在 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);
相反,使用开关是好的,并且是实施最多的,在X11代码上没有任何问题。
注意:我自己测试了您的代码,只更改了该行,然后进行了各种测试,打印结果。 XDestroyWindow ()行肯定是唯一的错误。
答案 1 :(得分:3)
只需在XDestroyWindow()
之前致电XCloseDisplay()
。
修改强>
抱歉,我不明白XSetWMProtocols的事情。现在我已经阅读了它。我想你正在访问活动工会的错误成员。
XDestroyWindow(显示,e.xdestroywindow.window);
应该是:
XDestroyWindow(display,e.xclient.window);
答案 2 :(得分:3)
我遇到了同样的问题,在深入研究Xlib文档和大量实验后,我想我知道你问题的答案,我可以向你解释。
当您致电XCreateWindow
或XCreateSimpleWindow
然后XMapWindow
时,您会指示X服务器创建您的窗口并将其映射到屏幕上。将这些命令从本地缓冲区发送到服务器后(通过调用XFlush
或从服务器请求某些数据的任何函数,因为它隐式刷新命令缓冲区),X服务器显示您的窗口。然后它是窗口管理器的一项工作,将所有装饰附加到窗口,例如一些边框,标题栏,窗口菜单以及用于最小化/最大化/关闭窗口的按钮。
现在正在显示您的窗口,过了一段时间后您可以决定使用XDestroyWindow
销毁它并通过调用XCloseDisplay
关闭与X服务器的连接,一切都会好的,没有错误
问题在于,当用户点击窗口标题栏上的 X 时,X服务器不能处理它,而是窗口管理器&#39的工作(X服务器对这些装饰一无所知,也不关心)。当用户关闭程序的顶级窗口时,窗口管理器的通常反应是销毁窗口并关闭与X服务器的连接,因为&# 39;大多数用户会期望的。您的程序可能仍然在屏幕外运行,但顶级窗口通常与窗口管理器的X Server连接相关联。
因此,当Window Manager破坏您的窗口时,您无法调用XDestroyWindow
,因为该窗口已被销毁且其Window
句柄无效。您将收到有关BadWindow
的错误消息。您也无法调用XCloseDisplay
,因为与X Server的连接已经关闭,这将导致许多用户在作者不知道的应用程序中遇到XIO: fatal IO error 11 (Resource temporarily unavailable) on X server
错误。这是一个常见的错误,因为一方面鼓励你自己清理,但另一方面文档误导了如何正确地完成这项工作。
但是,有一个约定,关于X服务器和窗口管理器应该如何合作,这也包括响应用户关闭顶级窗口的命令。它是处理它的X协议的扩展。以下Xlib documentation解释了这一点:
客户端,通常是那些具有多个顶级窗口的客户端,其服务器连接必须在删除其某些顶级窗口后仍然存在,应该在每个这样的
WM_DELETE_WINDOW
属性中包含原子WM_PROTOCOLS
窗口。如上所述,他们会收到ClientMessage
字段为data[0]
的{{1}}事件 [...]
如果用户要求删除其中一个客户端的顶级窗口,那么选择不在WM_DELETE_WINDOW
属性中包含WM_DELETE_WINDOW
的客户端可能会与服务器断开连接。
因此,这个问题有两个解决方案:当窗口管理器关闭窗口时,要么避免调用WM_PROTOCOLS
和XDestroyWindow
,而不是自己(实际上你不需要)清理顶级窗口,因为当程序结束时X服务器将销毁它,或者您需要注册XCloseDisplay
扩展并在用户指示关闭时等待来自Window Manager的通知您的窗口(它会向您发送WM_DESTROY_WINDOW
个事件,然后将ClientMessage
设置为data[0]
)。收到它后,只需破坏窗口并自行关闭与X Server的连接,然后结束程序。或者,如果您愿意,可以打开与X Server的连接以与其进行更多通信。当您处理WM_DELETE_WINDOW
时,窗口管理器不会尝试销毁您的窗口,也不会关闭与X服务器的连接。
答案 3 :(得分:0)
//来自https://en.wikibooks.org/wiki/X_Window_Programming/Xlib的此代码不显示错误...
/*
Simple Xlib application drawing a box in a window.
To Compile: gcc -o test test.c -lX11 */
#include<X11/Xlib.h>
#include<stdio.h>
#include<stdlib.h> // prevents error for exit on line 18 when compiling with gcc
int main() {
Display *d;
int s;
Window w;
XEvent e;
/* open connection with the server */
d=XOpenDisplay(NULL);
if(d==NULL) {
printf("Cannot open display\n");
exit(1);
}
s=DefaultScreen(d);
/* create window */
w=XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
BlackPixel(d, s), WhitePixel(d, s));
// Process Window Close Event through event handler so XNextEvent does Not fail
Atom delWindow = XInternAtom( d, "WM_DELETE_WINDOW", 0 );
XSetWMProtocols(d , w, &delWindow, 1);
/* select kind of events we are interested in */
XSelectInput(d, w, ExposureMask | KeyPressMask);
/* map (show) the window */
XMapWindow(d, w);
/* event loop */
while(1) {
XNextEvent(d, &e);
/* draw or redraw the window */
if(e.type==Expose) {
XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10);
}
/* exit on key press */
if(e.type==KeyPress)
break;
// Handle Windows Close Event
if(e.type==ClientMessage)
break;
}
/* destroy our window */
XDestroyWindow(d, w);
/* close connection to server */
XCloseDisplay(d);
return 0;
}