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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10792361/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 06:33:19  来源:igfitidea点击:

How do I gracefully exit an X11 event loop?

clinuxx11xlib

提问by TheBuzzSaw

Almost every tutorial I find tells me to do this for my event loop:

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

XEvent event;

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

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

        default:
            break;
    }
}

However, clicking the X to close the program results in this message.

但是,单击 X 关闭程序会导致出现此消息。

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

It is indeed strange to me that the examples suggest using an infinite loop. That doesn't sound natural, and my other X11 programs don't do that. So I searched around. I found out how to capture the window close event.

这些例子建议使用无限循环,这对我来说确实很奇怪。这听起来不自然,而且我的其他 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;
    }
}

That works. It exits without errors. ... But I refuse to believe this is the normal way to do things. I mean, is this the only way to properly exit an X11 app? It seems like a lot of work just to capture the close event. How do I make a 'proper' event loop? Why is the close event so deeply buried? What am I missing?

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

采纳答案by n. 'pronouns' m.

There are no such things as "exit button" or "application" or "close event" in X11. This is by design.

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

Window decorations, exit buttons and many the other things we depend upon are not built into X11. They are implemented on top of the core X11 instead. The name of the particular set of conventions responsible for wmDeleteMessageis ICCCM, look it up.

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

Xlib only deals with the core X11 protocol. No built-in close event there.

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

There are toolkits that make dealing with ICCCM and all other things that are not built into X11 easier (GTK, wxWindows, Qt, ...) You probably want to use one of those.

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

回答by SasQ

The problem lays in the communication between X Server and the Window Manager.

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

When you call XCreateWindowor XCreateSimpleWindow, the X Server creates your window (not showing it until you explicitly map it on the screen by calling XMapWindow), and then the Window Manager is responsible for attaching all the decorations and buttons and system menu around your window.

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

You can call XDestroyWindowon your own to remove the window, and this usually means it just disappears from the screen, but your program is still running and the connection to the X Server is still open, so you can send it some more requests.

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

The problem begins when the user clicks that little Xbutton attached to your window by the Window Manager, because it is not created by the X Server and it is not his business to decide what to do then. Now it's all in hands of Window Manager.

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

If the Window Manager simply called XDestroyWindowon your window, it would cause a problem if your application wanted to capture the closing event to do something before the window gets destroyed. So the convention has been established between the X Server and the Window Managers to handle this process.

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

The default behavior of most Window Managers is to destroy the window and closethe connection with the X server, because this is what most users of Window Managers would expect: that when they close the window, the program will end (and the connection to the X Server will close with the closed window). And then, when you try to call XCloseDisplay(display), it will cause the IO error you've mentioned, because the connection to the server is already closed and the displaystructure is invalid.

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

Here's an excerpt from the Xlib documentationwhich explains this:

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

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

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

Yeah, it would be great if they didn't hide it so deep in their docs, though :-P But when you already find it, fortunately it also hints for the solution.

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

If you want a different behavior (that is, to capture the closing event from the Window Manager), you need to use the WM_DESTROY_WINDOWprotocol.

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

Another excerpt from the docs:

文档的另一个摘录:

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_WINDOWin the WM_PROTOCOLSproperty on each such window. They will receive a ClientMessageevent as described above whose data[0]field is WM_DELETE_WINDOW.

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

I had the same error and I wanted to know exactly what causes it and why. It took me some time to figure it out and find the proper explanation in the doc, so I put my explanation here to save the time of others uninformed.

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