如何优雅地退出X11事件循环?

时间:2012-05-29 02:12:20

标签: c linux x11 xlib

我发现的几乎每个教程都告诉我为我的事件循环执行此操作:

XEvent event;

while (true)
{
    XNextEvent(display, &event);

    switch (event.type)
    {
        case Expose:
            printf("Expose\n");
            break;

        default:
            break;
    }
}

但是,单击X关闭程序会产生此消息。

XIO:  fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
after 10 requests (10 known processed) with 0 events remaining.

我确实很奇怪,这些例子建议使用无限循环。这听起来并不自然,我的其他X11程序也不这样做。所以我四处搜寻。我发现了如何捕捉窗口关闭事件。

Atom wmDeleteMessage = XInternAtom(mDisplay, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window, &wmDeleteMessage, 1);

XEvent event;
bool running = true;

while (running)
{
    XNextEvent(display, &event);

    switch (event.type)
    {
        case Expose:
            printf("Expose\n");
            break;

        case ClientMessage:
            if (event.xclient.data.l[0] == wmDeleteMessage)
                running = false;
            break;

        default:
            break;
    }
}

有效。它退出没有错误。 ......但我拒绝相信这是做事的正常方式。我的意思是,这是正确退出X11应用程序的唯一方法吗?为了捕捉近距离事件,似乎需要做很多工作。如何制作“正确”的事件循环?为什么近距离事件如此深深地被埋葬?我错过了什么?

2 个答案:

答案 0 :(得分:51)

问题在于X Server和Window Manager之间的通信。

当您致电XCreateWindowXCreateSimpleWindow时,X服务器会创建您的窗口(在您通过调用XMapWindow在屏幕上明确映射它之前不显示它),然后是窗口管理器负责将所有装饰,按钮和系统菜单附在窗户周围。

你可以自己调用XDestroyWindow删除窗口,这通常意味着它只是从屏幕上消失,但你的程序仍在运行,并且与X Server的连接仍然是打开的,所以你可以发送更多请求。

当用户点击窗口管理器附加到窗口的那个小 X 按钮时问题就开始了,因为它不是由X服务器创建的,因此决定做什么不是他的事。然后。现在,它掌握在Window Manager手中。

如果窗口管理器只是在窗口上调用了XDestroyWindow,那么如果你的应用程序想要在窗口被破坏之前捕获关闭事件以执行某些操作,则会导致问题。因此,已在X Server和Window Manager之间建立了约定来处理此过程。

大多数窗口管理器的默认行为是销毁窗口并关闭与X服务器的连接,因为这是Window Managers的大多数用户所期望的:当它们关闭窗口时,程序将结束(并且与关闭窗口的X服务器连接将关闭)。然后,当您尝试调用XCloseDisplay(display)时,它将导致您提到的IO错误,因为与服务器的连接已关闭且display结构无效。

以下是Xlib documentation的摘录,其中解释了这一点:

  

如果用户要求删除其中一个客户端的顶级窗口,那么选择不在WM_DELETE_WINDOW属性中包含WM_PROTOCOLS的客户端可能会与服务器断开连接。

是的,如果他们没有在文档中如此深入地隐藏它,那将是很棒的,但是:-P 但是当你已经找到它时,幸运的是它也暗示了解决方案。

如果您想要一个不同的行为(即从Window Manager捕获结束事件),您需要使用WM_DESTROY_WINDOW协议。

文档的另一段摘录:

  

客户端,通常是那些具有多个顶级窗口的客户端,其服务器连接必须在删除其某些顶级窗口后仍然存在,应该在每个这样的WM_DELETE_WINDOW属性中包含原子WM_PROTOCOLS窗口。如上所述,他们会收到ClientMessage字段为data[0]的{​​{1}}事件。

我遇到了同样的错误,我想确切地知道是什么导致它和原因。我花了一些时间来弄清楚并在文档中找到正确的解释,所以我在这里做了解释,以节省其他人不知情的时间。

答案 1 :(得分:21)

X11中没有“退出按钮”或“应用程序”或“关闭事件”之类的内容。这是设计的。

窗口装饰,退出按钮以及我们所依赖的许多其他东西都没有内置到X11中。它们是在核心X11之上实现的。负责wmDeleteMessage的特定约定集的名称是ICCCM,请查阅。

Xlib仅处理核心X11协议。那里没有内置的关闭事件。

有些工具包可以处理ICCCM以及所有其他没有内置到X11中的东西(GTK,wxWindows,Qt,...)你可能想要使用其中一种。