【问题标题】:How do you exit X11 program without Error如何无错误退出 X11 程序
【发布时间】:2011-12-31 20:48:42
【问题描述】:

问题结束时,我在 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,我的代码也遇到了同样的问题。我的设置一定有问题。

更新 显然很多人都有这个问题。 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 语句之前可以解决问题,而无需从循环内的程序返回。

【问题讨论】:

  • 添加了转义到循环,因为意识到它永远不会爆发。仍然是相同的错误消息。
  • 为什么要直接做X11编程?我强烈建议使用图形工具包,例如 GTK 或 Qt(但也有其他工具包:FLTK、Fox...)
  • @Starynkevitch 更多了解它的工作原理。不适用于工作或学校。

标签: c++ linux x11 xlib


【解决方案1】:

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

您必须在 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 代码上没有问题。

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

【讨论】:

  • +1 不仅是解决方案,而且是问题/解决方案的解释。谢谢。
【解决方案2】:

只需在XCloseDisplay() 之前致电XDestroyWindow()

编辑:

对不起,我不明白 XSetWMProtocols 的事情。现在我已经阅读了它。我认为您访问了错误的事件联盟成员。

XDestroyWindow(display,e.xdestroywindow.window);

应该是:

XDestroyWindow(display,e.xclient.window);

【讨论】:

  • @JoeMcGrath 改变了我的答案。抱歉误导了。
  • 谢谢,我学到了一些东西,尽管没有解决问题。至少我现在有了正确的窗口句柄。
  • 另一件事要尝试:XWithdrawWindow() 而不是 XDestroyWindow()
【解决方案3】:

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

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

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

问题在于,当用户点击窗口标题栏上的那个 X 时,处理它的不是 X 服务器的工作,而是窗口管理器的工作(X 服务器知道没有关于那些装饰品,它不在乎)。当用户关闭程序的顶级窗口时,窗口管理器的通常反应是销毁窗口并关闭与 X 服务器的连接,因为这就是大多数用户会期望。您的程序可能仍会在屏幕外运行,但顶层窗口通常通过窗口管理器与 X 服务器连接相关联。

所以当窗口管理器销毁你的窗口时,你不能调用XDestroyWindow,因为窗口已经被销毁并且它的Window句柄是无效的。您将收到有关BadWindow 的错误消息。您也不能调用XCloseDisplay,因为与X 服务器的连接已经关闭,这将导致许多用户在作者不知道的应用程序中遇到XIO: fatal IO error 11 (Resource temporarily unavailable) on X server 错误。这是一个常见的错误,因为一方面我们鼓励您自己清理,但另一方面,文档对如何正确执行此操作具有误导性。

但是,关于 X 服务器和窗口管理器应该如何合作,有一个约定,其中还包括响应用户关闭顶级窗口的命令。 X 协议有一个扩展来处理它。以下是Xlib documentation 的解释:

客户端,通常是具有多个顶级窗口的客户端,其服务器连接必须在删除一些顶级窗口后仍然存在,应该在每个此类窗口的WM_PROTOCOLS 属性中包含原子WM_DELETE_WINDOW。他们将收到如上所述的ClientMessage 事件,其data[0] 字段为WM_DELETE_WINDOW
[...]
如果用户要求删除客户端的顶级窗口之一,则选择不在WM_PROTOCOLS 属性中包含WM_DELETE_WINDOW 的客户端可能会与服务器断开连接。

所以这个问题有两种解决方案:当你的窗口被窗口管理器而不是你自己关闭时,要么避免调用XDestroyWindowXCloseDisplay(你实际上不必清理顶层窗口,因为 X 服务器会在您的程序结束时销毁它),或者您需要注册 WM_DESTROY_WINDOW 扩展名并在用户指示关闭窗口时等待窗口管理器的通知(它将向您发送一个然后ClientMessage 事件,其data[0] 设置为WM_DELETE_WINDOW)。收到它后,只需销毁窗口并自行关闭与 X 服务器的连接,然后结束您的程序。或者,如果您愿意,可以将与 X 服务器的连接保持打开状态,以便与它进行更多的通信。当您处理WM_DESTROY_WINDOW 时,窗口管理器不会尝试销毁您的窗口,也不会关闭与 X 服务器的连接。

【讨论】:

    【解决方案4】:

    // 此代码来自: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;
    

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-14
      • 2016-11-12
      • 1970-01-01
      • 2015-11-19
      相关资源
      最近更新 更多