You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于XLib的C程序窗口Resize时黑屏闪烁问题求助

解决XLib窗口Resize时的闪烁问题

核心原因

X服务器在窗口Resize时,默认会使用窗口的背景色(你创建窗口时设为黑色)填充新区域或整个窗口,这会在你的60fps渲染周期重绘前产生黑屏闪烁。仅在ConfigureNotify中重绘无法完全避免,因为X的异步特性可能导致清屏操作先于你的重绘命令执行。

彻底解决方案

1. 禁止X服务器自动清屏窗口

创建窗口时,不要使用XCreateSimpleWindow(它会默认设置背景色),改用XCreateWindow并配置窗口属性,让服务器不自动填充背景:

修改初始化代码如下:

displayPt = XOpenDisplay(NULL);
s32 screenId = DefaultScreen(displayPt);
Visual* visual = DefaultVisual(displayPt, screenId);
int depth = DefaultDepth(displayPt, screenId);

// 设置窗口属性,禁止自动清屏
XSetWindowAttributes attr;
attr.back_pixmap = None; // 不使用背景像素图,避免服务器自动填充
attr.backing_store = NotUseful; // 不需要服务器保存窗口内容
attr.border_pixel = 0;
attr.event_mask = StructureNotifyMask | ExposureMask; // 确保接收ConfigureNotify和Expose事件

u64 rootWindow = XRootWindow(displayPt, screenId);
// 使用XCreateWindow替代XCreateSimpleWindow
window = XCreateWindow(displayPt, rootWindow,
    0, 0, pxWidth, pxHeight, 0, depth, InputOutput,
    visual, CWBackPixmap | CWBackingStore | CWBorderPixel | CWEventMask, &attr);

gc = XCreateGC(displayPt, window, 0, 0);

// 初始化XImage
ximg = *XCreateImage(displayPt, visual, depth, ZPixmap, 0, RenderGetMemory(),
    pxWidth, pxHeight, depth, RenderGetBytesPerRow());

2. 处理Expose事件

Resize操作可能会触发Expose事件(告知窗口部分区域需要重绘),需要在事件循环中处理该事件,确保及时重绘:

case Expose:
    // 如果是完整窗口重绘请求,直接刷新缓冲区到窗口
    if (ev.xexpose.count == 0) {
        PlatformPasteBuffer(displayPt, window, gc, &ximg);
        XFlush(displayPt); // 立即发送绘制命令到服务器
    }
    break;

3. 优化ConfigureNotify事件处理

在处理Resize时,确保修改缓冲区后立即重绘,并强制XLib刷新命令队列,避免绘制命令延迟:

case ConfigureNotify:
    if (ev.xconfigure.width != ximg.width 
        || ev.xconfigure.height != ximg.height) {
        ximg.width = ev.xconfigure.width;
        ximg.height = ev.xconfigure.height;
        RenderResize(ximg.width, ximg.height);
        ximg.bytes_per_line = RenderGetBytesPerRow();
        ximg.data = RenderGetMemory();
        
        // 重绘窗口
        PlatformPasteBuffer(displayPt, window, gc, &ximg);
        XFlush(displayPt); // 关键:立即发送命令,避免XLib缓存导致延迟
    }
    break;

4. 可选:使用离屏Pixmap实现双缓冲

如果上述方法仍有轻微闪烁,可以用离屏Pixmap作为中间缓冲区,先渲染到Pixmap,再一次性拷贝到窗口,减少绘制过程中的视觉断层:

// 初始化时创建离屏Pixmap
Pixmap offscreen_pixmap = XCreatePixmap(displayPt, window, pxWidth, pxHeight, depth);

// 渲染时先绘制到offscreen_pixmap,再拷贝到窗口
// 替换PlatformPasteBuffer中的逻辑,改为:
XCopyArea(displayPt, offscreen_pixmap, window, gc, 0, 0, ximg.width, ximg.height, 0, 0);
XFlush(displayPt);

关键说明

  • XFlush的作用是强制XLib将所有待处理的命令发送到X服务器,避免异步队列导致的绘制延迟,这是消除闪烁的关键一步。
  • 设置back_pixmap = None会让X服务器在窗口Resize时不自动填充背景,而是等待客户端重绘,从根源上避免黑屏。

内容的提问来源于stack exchange,提问作者user3640830

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 17:47:26