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

C++ 如何在没有错误的情况下退出X11程序,c++,linux,x11,xlib,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,并且能够阻止窗口关闭,因

在问题的最后,我在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;
}
更新

尝试改变很多事情,比如让循环运行XPending()。 我决定运行其他人的,他们的代码也有同样的问题。我的设置一定有问题

更新 显然很多人都有这个问题。谷歌ftk有这个问题,他们在自己的网站上修复了它。他们称之为FTK_QUIT(),我猜这类似于Exit()。所以我把我的回报放在循环中,这就解决了问题。不知道为什么,但它做到了。固定代码:

  case ClientMessage:
    if(e.xclient.data.l[0] == wmDeleteMessage) 
    {
      XDestroyWindow(display,e.xclient.window);
      XCloseDisplay(display);
      return 0;
    }
仍将向能够解释为什么以及如果可能的话将返回语句(连同
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;

开关
语句中运行时,代码不起作用。即使它退出循环而不调用另一个X函数。在
开关
语句之前的
if
语句修复了问题,而无需从循环中的程序返回

只需在
XCloseDisplay()之前调用
XDestroyWindow()

编辑:

对不起,我不明白XSetWMProtocols的事。现在我已经读过了。我想你找错活动联盟的成员了

XDestroyWindow(显示,例如XDestroyWindow.window)

应该是:

XDestroyWindow(display,e.xclient.window);

这个问题的解决方案很简单:

必须在XDestroyWindow()函数中使用正确的结构成员

由于X11事件结构的实现标准,它们彼此非常相似。每个结构都以“类型”构件开始,第一个构件实际上总是相同的

现在假设:

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个字节

由于要使用错误的窗口参数调用XDestroyWindow(),因此它将失败。相反,如果使用e.xdestroywindow.event(距离事件结构的开头有24个字节)调用它,则地址将正确,函数将正常工作

如果您亲自查看Xlib.h文件,您会注意到这两种结构的窗口元素的位置不同

说到这里,请记住Xlib已经开发了多年,每天都有许多程序员使用它,所以如果有一个神秘的错误,它可能不在Xlib中。作为最后一点提示,我想告诉您:如果您想进一步使用Xlib编程,请始终将头文件作为主要参考,然后是系统手册,然后是所有其他文件

最后,代码中唯一的错误是:

XDestroyWindow(display,e.xdestroywindow.window);
必须将其更改为:

XDestroyWindow(display,e.xclient.window);
相反,开关的使用是好的,并且是实现最多的,X11代码没有问题


注意:我自己测试了您的代码,只更改了那一行,然后进行各种测试,打印结果。XDestroyWindow()行肯定是唯一的错误。

我也遇到了同样的问题,在翻阅了Xlib文档并进行了大量实验后,我想我知道您问题的答案,我可以向您解释

当您调用
XCreateWindow
XCreateSimpleWindow
然后调用
XMapWindow
时,您指示X服务器创建窗口并映射到屏幕上。将这些命令从本地缓冲区发送到服务器后(通过调用
XFlush
或从服务器请求某些数据的任何函数,因为它会隐式刷新命令缓冲区),X服务器将显示您的窗口。然后,窗口管理器的任务是将所有装饰附加到窗口上,例如一些边框、标题栏、窗口菜单和用于最小化/最大化/关闭窗口的按钮

现在您的窗口正在显示,过了一会儿,您可以决定用
XDestroyWindow
销毁它,并通过调用
XCloseDisplay
关闭与X服务器的连接,一切都会好起来的,没有错误

问题是,当用户单击窗口标题栏上的X时,处理它不是X服务器的工作,而是窗口管理器的工作(X服务器对这些装饰一无所知,也不在乎)。当用户关闭程序的顶级窗口时,窗口管理器通常的反应是破坏窗口并关闭与X服务器的连接,因为这是大多数用户所期望的。您的程序可能仍在屏幕外运行,但顶层窗口通常由窗口管理器与X服务器连接相关联

因此,当窗口管理器销毁您的窗口时,您不能调用
XDestroyWindow
,因为该窗口已被销毁,其
窗口
句柄无效。您将得到一个关于
BadWindow
的错误。您也无法调用
XCloseDisplay
,因为与X服务器的连接已关闭,这将导致X服务器上出现
XIO:fatal IO错误11(资源暂时不可用)
错误
XDestroyWindow(display,e.xdestroywindow.window);
XDestroyWindow(display,e.xclient.window);
  /*
   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;