/* 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 cairo-xcb-bug.c `pkg-config --cflags --libs cairo-xcb x11-xcb` -O0 -g3 */ #include #include #include #include #include #include #include const unsigned int width = 100; const unsigned int height = 100; 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); } int main () { Display *display; xcb_connection_t *connection; xcb_screen_t *screen; xcb_visualtype_t *visual_type; cairo_surface_t *surface; //cairo_device_t *device_ref; // part of the ritual needed to avoid a crash cairo_t *context; printf ("Press any key or mouse button at the X Window to destroy it and recreate it again.\n\n"); printf ("Press C-c here to exit.\n"); while (1) { /* Reset our variables */ display = NULL; connection = NULL; screen = NULL; visual_type = NULL; surface = NULL; //device_ref = NULL; context = NULL; 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; /* Create a window */ xcb_drawable_t window = xcb_generate_id (connection); uint32_t mask = XCB_CW_BACK_PIXMAP | XCB_CW_EVENT_MASK; uint32_t values[2] = {screen->black_pixel, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_BUTTON_PRESS}; xcb_create_window (connection, /* connection */ XCB_COPY_FROM_PARENT, /* depth */ window, /* window Id */ screen->root, /* parent window */ 0, 0, /* x, y */ width, height, /* width, height */ 10, /* border_width */ XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */ XCB_COPY_FROM_PARENT, /* visual */ mask, values ); /* masks */ /* Map the window on the screen and flush */ xcb_map_window (connection, window); xcb_flush (connection); /* Boilerplate to get xcb visual for cairo */ if (screen) { xcb_depth_iterator_t depth_iter; depth_iter = xcb_screen_allowed_depths_iterator (screen); for (; depth_iter.rem; xcb_depth_next (&depth_iter)) { xcb_visualtype_iterator_t visual_iter; visual_iter = xcb_depth_visuals_iterator (depth_iter.data); for (; visual_iter.rem; xcb_visualtype_next (&visual_iter)) { if (screen->root_visual == visual_iter.data->visual_id) { visual_type = visual_iter.data; break; } } } } if (!visual_type) { perror ("Bad visual type"); exit (1); } surface = cairo_xcb_surface_create (connection, window, visual_type, width, height); if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) { perror ("Bad cairo surface"); exit (1); } //device_ref = cairo_device_reference (device); // part of the ritual to make this program crash-free context = cairo_create (surface); if (cairo_status (context) != CAIRO_STATUS_SUCCESS) { perror ("Bad cairo context"); exit (1); } int loop = 1; while (loop) { xcb_generic_event_t *event; event = xcb_wait_for_event (connection); switch (event->response_type & ~0x80) { case XCB_BUTTON_PRESS: case XCB_KEY_PRESS: loop = 0; break; case XCB_EXPOSE: draw (context); cairo_surface_flush (surface); // not needed here, apparently? xcb_flush (connection); break; default: /* Unknown event type, ignore it */ break; } free (event); } usleep (100000); cairo_destroy (context); cairo_surface_destroy (surface); /* Must destroy the device before closing the display to avoid bugs with Cairo-XCB */ //cairo_device_finish (device_ref); //cairo_device_destroy (device_ref); XCloseDisplay (display); usleep (100000); } return 0; }