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

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

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

最佳答案

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

当你调用 XCreateWindowXCreateSimpleWindow 时,X 服务器会创建你的窗口(直到你通过调用 XMapWindow),然后窗口管理器负责在窗口周围附加所有的装饰和按钮以及系统菜单。

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

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

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

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

这是 Xlib documentation 的摘录这解释了这一点:

Clients that choose not to include WM_DELETE_WINDOW in the WM_PROTOCOLS property may be disconnected from the server if the user asks for one of the client's top-level windows to be deleted.

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

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

文档的另一个摘录:

Clients, usually those with multiple top-level windows, whose server connection must survive the deletion of some of their top-level windows, should include the atom WM_DELETE_WINDOW in the WM_PROTOCOLS property on each such window. They will receive a ClientMessage event as described above whose data[0] field is WM_DELETE_WINDOW.

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

https://stackoverflow.com/questions/10792361/

相关文章:

linux - 在 PHP CLI 中设置 max_execution_time

linux - 如何使用 bash 在文件中间添加一行文本?

linux - 如何检测文本文件中的无效 utf8 unicode/binary

c - 在 Linux 中何时使用 pthread_exit() 以及何时使用 pthread_jo

linux - 为什么 Linux 不通过 TSS 使用硬件上下文切换?

linux - "zero copy networking"与 "kernel bypass"?

c - 为什么 pthread 互斥锁被认为是 "slower"而不是 futex?

c++ - 如何在不重新打印的情况下更新终端中的打印消息

python - 在 Ubuntu 中启动时运行 Python 脚本

c++ - 如何在 Linux 中命名线程?