How to stop Cairo/X11 from flickering when resizing?
18:48 16 Apr 2026

I'm trying to make a GUI app with Cairo, but whenever I e.g. resize the main window, the background colour flickers in for a frame. There's a similar question about this, How to avoid cairo drawing xlib surface from flickering?, but the singular answer (just changing the background colour) doesn't actually solve anything.

Here's a simple example to illustrate my point:

#include 

#include 

#include 
#include 

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;
}

There's a lot of boilerplate to do with creating the window, but I sorted the most important code toward the bottom.

The flicker is annoying, so how do I stop it from happening?

c user-interface x11 cairo xlib