2

我正在编写一个具有 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 事件处理函数似乎是阻塞的,所以如果你想在它们阻塞之前进行一些处理,它们并不适合使用,但前提是它们会阻塞!


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

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

2 回答 2

4

FWIW 一个 UI 工具包,比如 GTK+ 就是这样做的:

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

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

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

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

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

于 2012-10-14T02:21:20.303 回答
3

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

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 连接的底层套接字。

于 2012-10-13T09:02:57.483 回答