X11:如何延迟重新绘制直到处理完所有事件?

时间:2012-10-13 07:44:27

标签: x11 repaint xlib

我正在编写一个具有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调用不感兴趣,就不会将它从队列中删除,所以它停在那里{{1}阻止,导致100%的CPU使用。

我只是想知道是否有一种标准方法来实现我追求的延迟/组合重绘?许多Xlib事件处理函数似乎都被阻塞了,所以如果你想在它们阻塞之前做一些处理它们就不适合使用它们,但只有当它们阻塞时才会使用它们!


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

XPeekEvent

2 个答案:

答案 0 :(得分:4)

FWIW像GTK +这样的UI工具包就是这样做的:

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

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

当翻译为原始Xlib时,这看起来像是n.m.的答案:当你有一个伤害区域和!XPending()时重绘。所以我可以随意接受这个答案,我只是想补充一些额外的信息。

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

为了跟踪损坏区域,人们经常剪切并粘贴来自X服务器中“机器无关”代码的文件“miregion.c”。只需google for miregion.c或下载X服务器源并查找它。这里的“区域”只是一个矩形列表,它支持诸如并集和交叉之类的操作。要增加伤害,请将它与旧区域结合,修复伤害,减去伤害等等。

答案 1 :(得分: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 ms左右可能会有所帮助。不幸的是,原始Xlib没有定时器接口。您需要一个更高级别的工具包(包括Xt在内的所有工具包都有某种定时器接口),或者直接使用X11连接的底层套接字。