【问题标题】:X11: How to delay repainting until all events are processed?X11:如何延迟重绘,直到处理完所有事件?
【发布时间】:2012-10-13 07:44:27
【问题描述】:

我正在编写一个具有 X11/Xlib 接口的程序,我的事件处理循环如下所示:

while (XNextEvent(display, &ev) >= 0) {
    switch (ev.type) {
        // Process events
    }
}

问题是当窗口调整大小时,我收到一堆Expose 事件告诉我要重绘窗口的哪些部分。如果我直接响应事件重绘它们,重绘操作会非常滞后,因为它太慢了(调整大小后,我会看到所有新失效的矩形一个一个刷新。)

我想做的是在更新的窗口大小发生变化时记录它,并且当没有更多事件需要处理时,只在整个窗口(或至少只有两个矩形)上运行一次重绘操作。

很遗憾,我看不到这样做的方法。我试过这个:

do {
    XPeekEvent(display, &ev);
    while (XCheckMaskEvent(display, ExposureMask | StructureNotifyMask, &ev)) {
        switch (ev.type) {
            // Process events, record but don't process redraw events
        }
    }
    // No more events, do combined redraw here
}

这确实有效,但效率有点低,如果一个我对XCheckMaskEvent 调用不感兴趣的事件到达,它不会从队列中删除它,所以它会停留在那里阻止XPeekEvent 阻塞,导致 100% 的 CPU 使用率。

我只是想知道是否有一种标准方法可以实现我所追求的延迟/组合重绘?许多 Xlib 事件处理函数似乎会阻塞,因此如果您想在它们阻塞之前进行一些处理,它们并不适合使用,但前提是它们会阻塞!


编辑:为了记录,这是我使用的解决方案。这是 n.m. 的简化版:

while (XNextEvent(display, &ev) >= 0) {
    switch (ev.type) {
        // Process events, remember any redraws needed later
    }
    if (!XPending(display)) {
        // No more events, redraw if needed
    }
}

【问题讨论】:

    标签: x11 repaint xlib


    【解决方案1】:

    FWIW 诸如 GTK+ 之类的 UI 工具包就是这样做的:

    • 对于每个窗口,维护一个“损坏区域”(所有暴露事件的联合)
    • 当损坏区域变为非空时,添加一个“空闲处理程序”,这是事件循环在没有其他事情可做时运行的函数
    • 空闲处理程序将在事件队列为空且 X 套接字没有可读取的内容时运行(根据 ConnectionNumber(dpy) 上的 poll()
    • 空闲处理程序当然会重新绘制损坏区域

    在 GTK+ 中,他们将在未来版本中将其更改为更现代的面向 3D 引擎的方式(清理垂直同步上的损坏区域),但多年来一直以上述相当简单的方式工作。

    当翻译成原始 Xlib 时,这看起来就像 n.m. 的答案:当你有损坏区域和 !XPending() 时重新绘制。所以请随意接受这个答案,我只是想添加一些额外的信息。

    如果您想要诸如计时器和闲置之类的东西,您可以考虑 lke libev http://software.schmorp.de/pkg/libev.html 它旨在仅在您的应用程序中删除几个源文件(它没有设置为外部依赖项)。您可以将显示器的文件描述符添加到事件循环中。

    为了跟踪损坏区域,人们经常从 X 服务器中的“机器无关”代码中剪切和粘贴文件“miregion.c”。只需在谷歌上搜索 miregion.c 或下载 X 服务器源并寻找它。这里的“区域”只是一个支持联合和相交等操作的矩形列表。要增加伤害,将它与旧区域合并,修复伤害,减去它,等等。

    【讨论】:

      【解决方案2】:

      尝试以下方法(未经实际测试):

      while (TRUE) {
        if (XPending(display) || !pendingRedraws) {
          // if an event is pending, fetch it and process it
          // otherwise, we have neither events nor pending redraws, so we can
          // safely block on the event queue
          XNextEvent (display, &ev);
          if (isExposeEvent(&ev)) {
            pendingRedraws = TRUE;
          }
          else {
            processEvent(&ev);
          }
        }
        else {
          // we must have a pending redraw
          redraw();
          pendingRedraws = FALSE;
        }
      }
      

      在进行重绘之前等待 10 毫秒左右可能会有所帮助。不幸的是,原始 Xlib 没有计时器接口。为此,您需要一个更高级别的工具包(包括 Xt 在内的所有工具包都有某种计时器接口),或者直接使用 X11 连接的底层套接字。

      【讨论】:

      • 啊,XPending! 这就是我要找的。​​span>
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-30
      • 1970-01-01
      相关资源
      最近更新 更多