【问题标题】:How do I gracefully exit an X11 event loop?如何优雅地退出 X11 事件循环?
【发布时间】:2012-06-03 06:37:29
【问题描述】:

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

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 中做任何有用的事情... :-)
  • 嗯,我强调的并不是“工作”。我想它更公正:为什么那个特定的功能如此模糊?如何授予退出按钮“默认行为”?
  • 整个 X11 库是默默无闻之上的,多层次的深度。就像有人拿了但丁的地狱并将其重写为 C API。我想,当它完成后,它的易用性仍然胜过它的前辈。
  • 不得不评论但丁的地狱笑话。我第一次在同一个句子中阅读该参考资料和 C API

标签: c linux x11 xlib


【解决方案1】:

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

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

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

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

【讨论】:

  • 这就是关闭窗口如此晦涩难懂的原因。 X 不是为关心这些事情而设计的——它是窗口管理器的工作。这就是大多数人使用 GTK 或 Qt 等工具包的原因。
【解决方案2】:

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

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

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

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

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

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

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

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

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

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

文档的另一个摘录:

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

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

【讨论】:

  • 很好的答案。感谢您的宝贵时间!
猜你喜欢
  • 1970-01-01
  • 2011-10-20
  • 2011-01-21
  • 1970-01-01
  • 2014-04-30
  • 1970-01-01
  • 1970-01-01
  • 2019-04-14
相关资源
最近更新 更多