旧的 xlib 程序在 window 调整大小时挂起 Linux GUI。为什么?

Old xlib programs hang the Linux GUI on window resize. Why?

我注意到,对于较旧的 X 程序,当用户开始通过拖动其边缘来调整 window 大小时,OS 的整个 GUI 会冻结。

我正在使用 glxgears 进行测试 - 齿轮停止旋转。所有其他程序的内容更新也是如此 - 例如任务管理器、终端 windows 等等。

停止移动鼠标后,所有activity重新开始。

调整新程序的大小windows(我的意思是使用 GTK 或 Qt)不会冻结任何东西。

同时,旧程序的 GUI 比新程序的响应速度快得多。只有拖动调整大小是问题。

较旧的程序都使用标准的记录方式来处理消息队列。类似于以下内容(当然更复杂):

while (1) {
      XNextEvent(d, &e);
      if (e.type == Expose) {
         XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10);
         XDrawString(d, w, DefaultGC(d, s), 10, 50, msg, strlen(msg));
      }
}

我试图通过在主要 window 创建上设置 XSetWindowAttributes.event_mask = 0 来消除整个消息处理。事件完全停止流动,但在调整空 window 大小时,所有 GUI 仍然冻结。

所以,问题不(只)在客户端。虽然,它可以在客户端和服务器交互的方式。例如可能是因为客户端没有做某事。

那么,较新的工具包有哪些不同之处?为避免这种冻结,在旧程序中要更改什么。

好吧,经过一些研究我找到了答案。

问题是旧程序不使用 _NEW_WM_SYNC_REQUEST protocol 来同步重绘 window 内容的能力与来自 window 的调整大小事件的速率经理。

因此,window 管理器以过高的速度调整 window 的大小,应用程序无法如此快速地绘制。这样,window 管理器有效地加载了 X 服务器并向其他 运行 应用程序提供了 DoS 挂起。

当然在这种情况下,调整大小事件的速率取决于 window 管理器,但大多数只是在每次鼠标移动时调整 window 的大小。

_NET_WM_SYNC_REQUEST 协议的目的是在应用程序完成其 window 的绘制时向 window 管理器提供信息,并阻止它在上一个之前调整 window 的大小已处理调整大小。

实现非常简单。

首先,应用程序必须在 window 的 WM_PROTOCOLS 属性 中包含 _NET_WM_SYNC_REQUEST。

此外,应用程序应提供一个(可能两个)同步计数器(参见 SYNC X extension or xcb-sync library or the libX variant

然后协议如下所示:

  1. 在调整 window 大小之前,WM 向应用程序 ClientMessage 发送数据 [0] 设置为 _NEW_WM_SYNC_REQUEST 字符串的 Atom。在这个事件的数据[2] 和数据[3] 中有一个 64 位数字。应用程序必须将此数字存储在某处。

  2. 处理完以下 ConfigureNotify 和 Expose 事件并完全重绘 window 表面后,必须将同步计数器设置为此 64 位值。

  3. window 管理器检查计数器的值,在看到它的数字后,知道再次调整 window 的大小是安全的。

当然有一些超时机制,如果程序响应太慢或根本不响应,window管理器切换到fall-back模式并开始调整windows老办法。

这个协议还有另一个变体,有两个同步计数器,但是恕我直言,它旨在解决另一个程序。随着 window 大小的调整,协议的第一个版本运行良好。