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

X11:如何延迟重新绘制直到处理所有事件?,x11,repaint,xlib,X11,Repaint,Xlib,我正在编写一个具有X11/Xlib接口的程序,我的事件处理循环如下所示: while (XNextEvent(display, &ev) >= 0) { switch (ev.type) { // Process events } } 问题是,当窗口调整大小时,我会收到一堆Expose事件,告诉我要重画窗口的哪些部分。如果我直接响应事件重新绘制它们,则重新绘制操作会严重滞后,因为速度太慢(调整大小后,我会看到所有新失效的矩形逐个刷新) 我想做的是记

我正在编写一个具有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
    }
}

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


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

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


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

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

  • 对于每个窗口,维护一个“损坏区域”(所有暴露事件的联合)
  • 当损坏区域变为非空时,添加一个“空闲处理程序”,这是事件循环在没有其他事情可做时将运行的一个函数
  • 空闲处理程序将在事件队列为空且X套接字无需读取时运行(根据
    ConnectionNumber(dpy)
    上的
    poll()
  • 空闲处理程序当然会重新绘制损坏区域
在GTK+中,他们将在未来的版本中将其改为更现代的面向3D引擎的方式(清理垂直同步上的损坏区域),但多年来它一直以上述相当简单的方式工作

当转换为原始Xlib时,这看起来像n.m.的答案:当你有一个损坏区域和
时重新喷漆!XPending()
。所以请随意接受这个答案,我只是想补充一点额外的信息

如果你想要定时器和IDLE之类的东西,你可以考虑一些LKYBEV,它的目的是在你的应用程序中删除两个源文件(它不是被设置为外部依赖项)。您可以将显示器的文件描述符添加到事件循环中


为了跟踪损坏区域,人们通常剪切并粘贴文件“miregion.c”,该文件来自X服务器中的“独立于机器”代码。只需在谷歌上搜索miregion.c,或者下载X服务器源代码并查找它。这里的“区域”只是一个支持并集和相交等操作的矩形列表。要添加损伤,请将其与旧区域合并,修复损伤,减去损伤,等等。

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

  • 对于每个窗口,维护一个“损坏区域”(所有暴露事件的联合)
  • 当损坏区域变为非空时,添加一个“空闲处理程序”,这是事件循环在没有其他事情可做时将运行的一个函数
  • 空闲处理程序将在事件队列为空且X套接字无需读取时运行(根据
    ConnectionNumber(dpy)
    上的
    poll()
  • 空闲处理程序当然会重新绘制损坏区域
在GTK+中,他们将在未来的版本中将其改为更现代的面向3D引擎的方式(清理垂直同步上的损坏区域),但多年来它一直以上述相当简单的方式工作

当转换为原始Xlib时,这看起来像n.m.的答案:当你有一个损坏区域和
时重新喷漆!XPending()
。所以请随意接受这个答案,我只是想补充一点额外的信息

如果你想要定时器和IDLE之类的东西,你可以考虑一些LKYBEV,它的目的是在你的应用程序中删除两个源文件(它不是被设置为外部依赖项)。您可以将显示器的文件描述符添加到事件循环中

为了跟踪损坏区域,人们通常剪切并粘贴文件“miregion.c”,该文件来自X服务器中的“独立于机器”代码。只需在谷歌上搜索miregion.c,或者下载X服务器源代码并查找它。这里的“区域”只是一个支持并集和相交等操作的矩形列表。若要增加伤害,请将其与旧区域合并;若要修复伤害,请将其减去,等等。

Ah,XPending!这就是我要找的。啊,是的!这就是我要找的。
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;
  }
}