如何退出无错误的X11程序

问题描述 投票:17回答:4

问题末尾,我在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语句解决了该问题,而无需从循环内的程序返回。

c++ linux x11 xlib
4个回答
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);

相反,使用switch很好,并且使用最多,在X11代码上没有问题。

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


3
投票

只需在XDestroyWindow()之前呼叫XCloseDisplay()

编辑:

[抱歉,我不了解XSetWMProtocols。现在,我已经阅读了。我认为您访问的是事件联合的错误成员。

XDestroyWindow(display,e.xdestroywindow.window);

应该是:

XDestroyWindow(display,e.xclient.window);

3
投票

我遇到了同样的问题,在仔细阅读了Xlib文档和大量实验之后,我认为我知道您问题的答案,并且可以向您解释。

[先调用XCreateWindowXCreateSimpleWindow,然后再调用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的客户端可能会与服务器断开连接。

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


0
投票

//此代码来自:WM_DESTROY_WINDOW不显示错误...

https://en.wikibooks.org/wiki/X_Window_Programming/Xlib

}

© www.soinside.com 2019 - 2024. All rights reserved.