如何在没有错误的情况下退出X11程序

时间:2011-11-18 00:53:43

标签: c++ linux x11 xlib

我在问题末尾的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语句之前修复问题而不从循环内的程序返回。

4 个答案:

答案 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文档和大量实验后,我想我知道你问题的答案,我可以向你解释。

当您致电XCreateWindowXCreateSimpleWindow然后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_PROTOCOLSXDestroyWindow,而不是自己(实际上你不需要)清理顶级窗口,因为当程序结束时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;

}