40

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

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 应用程序的唯一方法吗?仅仅为了捕捉关闭事件似乎需要做很多工作。如何制作“正确”的事件循环?为什么近距离事件被埋得如此之深?我错过了什么?

4

2 回答 2

66

问题在于 X 服务器和窗口管理器之间的通信。

当您调用XCreateWindoworXCreateSimpleWindow时,X 服务器会创建您的窗口(直到您通过调用 将其显式映射到屏幕上才显示它XMapWindow),然后窗口管理器负责在您的窗口周围附加所有装饰和按钮以及系统菜单。

您可以自己调用XDestroyWindow来移除窗口,这通常意味着它只是从屏幕上消失,但您的程序仍在运行并且与 X 服务器的连接仍然打开,因此您可以向它发送更多请求。

当用户单击X窗口管理器附加到您的窗口的那个小按钮时,问题就开始了,因为它不是由 X 服务器创建的,决定接下来做什么不是他的事。现在一切都在窗口管理器手中。

如果窗口管理器只是XDestroyWindow在您的窗口上调用,如果您的应用程序想要在窗口被销毁之前捕获关闭事件以执行某些操作,则会导致问题。所以在 X 服务器和窗口管理器之间建立了约定来处理这个过程。

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

这是Xlib 文档的摘录,它解释了这一点:

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

是的,如果他们不在他们的文档中隐藏得那么深,那就太好了:-P 但是当你已经找到它时,幸运的是它也暗示了解决方案。

如果您想要不同的行为(即从窗口管理器捕获关闭事件),则需要使用WM_DESTROY_WINDOW协议。

文档的另一个摘录:

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

我有同样的错误,我想知道究竟是什么原因造成的以及为什么。我花了一些时间才弄清楚并在文档中找到了正确的解释,所以我把我的解释放在这里是为了节省其他不知情的人的时间。

于 2014-03-24T14:55:28.687 回答
23

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

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

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

有一些工具包可以更轻松地处理 ICCCM 和所有其他未内置到 X11 中的东西(GTK、wxWindows、Qt,...)您可能想要使用其中之一。

于 2012-05-29T10:48:48.667 回答