如何在 XCB 上为软件图形制作动画?

How to animate software graphics on XCB?

我正在尝试在 XCB 上显示软件渲染的图形,但面临以下问题:

这是我的代码:

#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <xcb/xcb.h>
#include <xcb/shm.h>
#include <xcb/xcb_image.h>

int main()
{
    xcb_gcontext_t       gcontext;
    xcb_generic_event_t *e;
    uint32_t             value_mask = 0;
    uint32_t             value_list[ 2 ];
    const int width = 1200;
    const int height = 675;

    xcb_connection_t* c = xcb_connect( NULL, NULL );
    xcb_screen_t* screen = xcb_setup_roots_iterator( xcb_get_setup( c ) ).data;

    value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
    value_list[ 0 ] = screen->black_pixel;
    value_list[ 1 ] = XCB_EVENT_MASK_EXPOSURE;

    xcb_window_t win = xcb_generate_id( c );

    xcb_create_window( c,                             
                   XCB_COPY_FROM_PARENT,         
                   win,                          
                   screen->root,              
                   0, 0,                    
                   width, height,           
                   10,                      
                   XCB_WINDOW_CLASS_INPUT_OUTPUT, 
                   screen->root_visual,           
                   value_mask, value_list );      

    value_mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
    value_list[ 0 ] = screen->black_pixel;
    value_list[ 1 ] = 0;

    gcontext = xcb_generate_id( c );
    xcb_create_gc( c, gcontext, win, value_mask, value_list );

    xcb_map_window( c, win );
    xcb_flush( c );

    xcb_shm_query_version_reply_t* reply = xcb_shm_query_version_reply(
    c,
    xcb_shm_query_version( c ),
    NULL
    );

    xcb_shm_segment_info_t info;
    info.shmid   = shmget( IPC_PRIVATE, width * height * 4, IPC_CREAT | 0777 );
    info.shmaddr = shmat( info.shmid, 0, 0 );
    info.shmseg = xcb_generate_id( c );
    xcb_shm_attach( c, info.shmseg, info.shmid, 0 );
    shmctl( info.shmid, IPC_RMID, 0 );

    uint32_t* data = info.shmaddr;    

    xcb_pixmap_t pix = xcb_generate_id( c );
    xcb_shm_create_pixmap(
    c,
    pix,
    win,
    width, height,
    screen->root_depth,
    info.shmseg,
    0
    );

    int ry = 0;

    while ((e = xcb_wait_for_event( c )))
    {        
        switch (e->response_type & ~0x80)
        {
            case XCB_EXPOSE:
            {
                for (int y = 0; y < height; ++y)
                {
                    for (int x = 0; x < width; ++x)
                    {
                        data[ y * width + x ] = 0x0000FF00;
                    }
                }

                ++ry;
                if (ry > 400) ry = 0;

                for (int y = ry; y < ry + 50; ++y)
                {
                    for (int x = 0; x < 50; ++x)
                    {
                        data[ y * width + x ] = 0x00FF0000;
                    }
                }

                xcb_copy_area(
                          c,
                          pix,
                          win,
                          gcontext,
                          0, 0, 0, 0,
                          width, height
                          );
                xcb_flush( c );
            }

            break;
        }
        default: break;
    }

    free( e );
}

return 0;
}

代码垂直移动一个小矩形。

如何将此代码转换为无闪烁动画?

It doesn't animate (doesn't call EXPOSE event frequently)

EXPOSE 事件仅在 必须 重绘时发送到 window(例如:变得可见或调整为更大尺寸或 window 上面 window 已关闭)。

如果你的window的内容改变了,你就得自己重绘了。例如,您可以使用 select() 等待 X11 事件一段时间,然后绘制新版本的动画。

If I pull the drawing code out of Expose event handling, it animates, but flickers [...]

在使您的示例代码运行后,我看到此处 window 切换为黑色并返回时有些闪烁。发生这种情况是因为您将 window 的背景颜色设置为黑色。因此,在发送 EXPOSE 事件之前,X11 服务器用黑色填充暴露区域。如果我删除背景像素值,那么这里的闪烁就会消失。

[...] like there is no Vsync.

X11 中没有 Vsync。 X11 是即时模式绘图,无法告诉 X11 服务器您已完成帧生成。

但是,您在这里通过让 CopyArea 请求更新 window 来做正确的事情。这保证自动更新 window,但我不确定它是否同步到垂直刷新。

顺便说一句:您的动画只是向下移动了一个矩形。这里不能有撕裂。