/* Example program to trigger reference bugs in cairo-xcb when reopening the display. This program should crash with the following error message: ``` cairo-xcb-screen.c:219: _get_screen_index: Assertion `!"reached"' failed. ``` Compile with: gcc -Wall -o cairo-xcb-bug-2 cairo-xcb-bug-2.c `pkg-config --cflags --libs cairo-xcb x11-xcb` -O0 -g3 */ #include #include #include #include #include #include #include const unsigned int max_iter = 100000; const unsigned int width = 2; const unsigned int height = 2; /* Uncomment the line below to avoid triggering the error */ //#define USE_CAIRO_DEVICE void draw (cairo_t *cr) { cairo_push_group (cr); cairo_set_source_rgb (cr, 0, 0, 0); cairo_paint (cr); cairo_move_to (cr, 0, 0); cairo_line_to (cr, width, height); cairo_move_to (cr, 0, width); cairo_line_to (cr, height, 0); cairo_set_source_rgb (cr, 1, 1, 1); cairo_stroke (cr); cairo_pop_group_to_source (cr); cairo_paint (cr); } /* Boilerplate to get xcb visual for cairo */ xcb_visualtype_t * find_visual (xcb_screen_t *screen, xcb_visualid_t visual) { xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator (screen); for (; depth_iter.rem; xcb_depth_next (&depth_iter)) { xcb_visualtype_iterator_t visual_iter = xcb_depth_visuals_iterator (depth_iter.data); for (; visual_iter.rem; xcb_visualtype_next (&visual_iter)) if (visual == visual_iter.data->visual_id) return visual_iter.data; } return NULL; } int main () { Display *display; xcb_connection_t *connection; xcb_screen_t *screen; xcb_visualtype_t *visual_type; Pixmap pixmap; cairo_surface_t *surface; cairo_t *context; #ifdef USE_CAIRO_DEVICE cairo_device_t *device_ref; #endif printf ("Press C-c to exit.\n"); for (unsigned int k = 1; k <= max_iter; ++k) { printf("\rIteration: %d/%d", k, max_iter); fflush(stdout); /* Reset our variables */ display = NULL; connection = NULL; screen = NULL; visual_type = NULL; pixmap = 0; surface = NULL; context = NULL; #ifdef USE_CAIRO_DEVICE device_ref = NULL; #endif /* Open display */ display = XOpenDisplay (getenv ("DISPLAY")); if (!display) { perror ("Cannot open display"); exit (1); } /* Open the connection to the X server */ connection = XGetXCBConnection (display); if (!connection) { perror ("Cannot open connection"); exit (1); } /* Get the first screen */ screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data; if (!screen) { perror ("Cannot get screen"); exit (1); } /* Create pixmap */ pixmap = XCreatePixmap (display, screen->root, width, height, screen->root_depth); if (!pixmap) { perror ("Cannot create pixmap"); exit (1); } visual_type = find_visual(screen, screen->root_visual); if (!visual_type) { perror ("Bad visual type"); exit (1); } surface = cairo_xcb_surface_create (connection, pixmap, visual_type, width, height); if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) { perror ("Bad cairo surface"); exit (1); } #ifdef USE_CAIRO_DEVICE device_ref = cairo_device_reference (cairo_surface_get_device (surface)); #endif context = cairo_create (surface); if (cairo_status (context) != CAIRO_STATUS_SUCCESS) { perror ("Bad cairo context"); exit (1); } draw (context); cairo_surface_flush (surface); // not needed here, apparently? xcb_flush (connection); // is it needed in this headless scenario? cairo_destroy (context); cairo_surface_destroy (surface); XFreePixmap (display, pixmap); #ifdef USE_CAIRO_DEVICE /* Must destroy the device before closing the display to avoid bugs with Cairo-XCB */ cairo_device_finish (device_ref); cairo_device_destroy (device_ref); #endif XCloseDisplay (display); } printf("\nEnd\n"); return 0; }