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?