在调整大小时,如何防止Cairo/X11闪烁?

编程语言 2026-07-10

我想用Cairo做一个GUI应用,但每次把主窗口往内缩小时,背景颜色会闪现一帧。有一个类似的问题,关于这个的 如何避免Cairo绘制的Xlib表面闪烁?,但那个唯一的答案(只是改变背景颜色)其实并不能解决问题。

下面有一个简单的例子来说明我的观点:

#include <threads.h>

#include <X11/Xlib.h>

#include <cairo/cairo.h>
#include <cairo/cairo-xlib.h>

constexpr int start_width = 400;
constexpr int start_height = 400;

_Atomic bool have_been_killed = false;
Display *_Atomic display;
cairo_surface_t *_Atomic main_surface;

int guiThread(void *const data);

int main(int, char *[]) {
    display = XOpenDisplay(nullptr);
    if (!display) {
        return 1;
    }
    const Window root_window = XDefaultRootWindow(display);
    if (!root_window) {
        return 1;
    }

    const Window main_window = XCreateWindow(
        display, root_window,
        100, 100, start_width, start_height, 0,
        CopyFromParent, CopyFromParent, CopyFromParent,
        CWBackPixel | CWEventMask,
        &(XSetWindowAttributes){
            .background_pixel = 0xFF1E1E1E,
            .event_mask = StructureNotifyMask,
        }
    );
    XWindowAttributes main_window_attributes;
    if (!XGetWindowAttributes(display, main_window, &main_window_attributes)) {
        return 1;
    }

    Atom WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", true);
    if(!XSetWMProtocols(display, main_window, &WM_DELETE_WINDOW, 1)) {
        return 1;
    }

    main_surface = cairo_xlib_surface_create(display, main_window, main_window_attributes.visual, start_width, start_height);
    cairo_t *cairo = cairo_create(main_surface);
    if (!cairo) {
        return 1;
    }
    thrd_t gui_thread;
    if (thrd_create(&gui_thread, guiThread, cairo) != thrd_success) {
        return 1;
    }

    XMapWindow(display, main_window);
    XFlush(display);
    while (!have_been_killed) {
        XEvent event;
        XNextEvent(display, &event);
        switch (event.type) {
            case ConfigureNotify: {
                XConfigureEvent e = event.xconfigure;
                if (e.window != main_window) {
                    break;
                }
                cairo_xlib_surface_set_size(main_surface, e.width, e.height);
                break;
            }
            case ClientMessage: {
                XClientMessageEvent e = event.xclient;
                if (e.window == main_window && (Atom)e.data.l[0] == WM_DELETE_WINDOW) {
                    have_been_killed = true;
                }
                break;
            }
            default:
        }
    }
    thrd_join(gui_thread, nullptr);
    XCloseDisplay(display);
    return 0;
}

int guiThread(void *const data) {
    cairo_t *const ctx = data;
    while (!have_been_killed) {
        // Paint a solid colour to simulate an actual GUI
        cairo_set_source_rgb(ctx, 70.0/255, 130.0/255, 180.0/255);
        cairo_paint(ctx);
        cairo_surface_flush(main_surface);
        XFlush(display);
        thrd_sleep(&(struct timespec){.tv_nsec = 10'000'000}, nullptr);
    }
    return 0;
}

在创建窗口时需要大量的样板代码,不过我把最关键的代码放在了底部。

对我来说,闪烁只在向内调整大小时才会发生,但有些其他用户表示在任意方向调整大小时都会出现。无论如何,这很让人恼火,所以我该如何让它不再发生呢?

解决方案

这似乎是X11的一个问题。不过,可以很容易地缓解。

由于你自己绘制了整个窗口区域,因此窗口不需要背景色。因此,你可以不用设置一个背景色—从 XCreateWindow 调用中移除 CWBackPixel.background_pixel = 0xFF1E1E1E

这会把窗口背景设为无背景,因此X11就不会在你的GUI上方绘制实色背景,而是直接不在上面绘制任何东西。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章