Skip to content

Drawing in a window

Drawing in a window can be done using various graphical functions (drawing pixels, lines, rectangles, etc). In order to draw in a window, we first need to define various general drawing parameters (what line width to use, which color to draw with, etc). This is done using a graphical context.

  1. Allocating a Graphics Context

    As we said, a graphical context defines several attributes to be used with the various drawing functions. For this, we define a graphical context. We can use more than one graphical context with a single window, in order to draw in multiple styles (different colors, different line widths, etc). In XCB, a Graphics Context is, as a window, characterized by an Id:

    typedef uint32_t xcb_gcontext_t;
    typedef uint32_t xcb_gcontext_t;

    We first ask the X server to attribute an Id to our graphic context with this function:

    xcb_gcontext_t xcb_generate_id (xcb_connection_t *c);
    xcb_gcontext_t xcb_generate_id (xcb_connection_t *c);

    Then, we set the attributes of the graphic context with this function:

    xcb_void_cookie_t xcb_create_gc (xcb_connection_t *c,
                                     xcb_gcontext_t    cid,
                                     xcb_drawable_t    drawable,
                                     uint32_t          value_mask,
                                     const uint32_t   *value_list);
    xcb_void_cookie_t xcb_create_gc (xcb_connection_t *c,
                                     xcb_gcontext_t    cid,
                                     xcb_drawable_t    drawable,
                                     uint32_t          value_mask,
                                     const uint32_t   *value_list);

    We give now an example on how to allocate a graphic context that specifies that each drawing function that uses it will draw in foreground with a black color.

    #include <xcb/xcb.h>
    
    int
    main ()
    {
      xcb_connection_t *c;
      xcb_screen_t     *screen;
      xcb_drawable_t    win;
      xcb_gcontext_t    black;
      uint32_t          mask;
      uint32_t          value[1];
    
      /* Open the connection to the X server and get the first screen */
      c = xcb_connect (NULL, NULL);
      screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
    
      /* Create a black graphic context for drawing in the foreground */
      win = screen->root;
      black = xcb_generate_id (c);
      mask = XCB_GC_FOREGROUND;
      value[0] = screen->black_pixel;
      xcb_create_gc (c, black, win, mask, value);
    
      return 0;
    }
    #include <xcb/xcb.h>
    
    int
    main ()
    {
      xcb_connection_t *c;
      xcb_screen_t     *screen;
      xcb_drawable_t    win;
      xcb_gcontext_t    black;
      uint32_t          mask;
      uint32_t          value[1];
    
      /* Open the connection to the X server and get the first screen */
      c = xcb_connect (NULL, NULL);
      screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
    
      /* Create a black graphic context for drawing in the foreground */
      win = screen->root;
      black = xcb_generate_id (c);
      mask = XCB_GC_FOREGROUND;
      value[0] = screen->black_pixel;
      xcb_create_gc (c, black, win, mask, value);
    
      return 0;
    }

    Note should be taken regarding the role of "value_mask" and "value_list" in the prototype of xcb_create_gc(). Since a graphic context has many attributes, and since we often just want to define a few of them, we need to be able to tell the xcb_create_gc() which attributes we want to set. This is what the "value_mask" parameter is for. We then use the "value_list" parameter to specify actual values for the attribute we defined in "value_mask". Thus, for each constant used in "value_list", we will use the matching constant in "value_mask". In this case, we define a graphic context with one attribute: when drawing (a point, a line, etc), the foreground color will be black. The rest of the attributes of this graphic context will be set to their default values.

    See the next Subsection for more details.

    Comparison Xlib/XCB
    • XCreateGC ()
    • xcb_generate_id ()
    • xcb_create_gc ()

  2. Changing the attributes of a Graphics Context

    Once we have allocated a Graphic Context, we may need to change its attributes (for example, changing the foreground color we use to draw a line, or changing the attributes of the font we use to display strings. See Subsections Drawing with a color and Assigning a Font to a Graphic Context). This is done by using this function:

    xcb_void_cookie_t xcb_change_gc (xcb_connection_t *c,           /* The XCB Connection */
                                     xcb_gcontext_t    gc,          /* The Graphic Context */
                                     uint32_t          value_mask,  /* Components of the Graphic Context that have to be set */
                                     const uint32_t   *value_list); /* Value as specified by value_mask */
    xcb_void_cookie_t xcb_change_gc (xcb_connection_t *c,           /* The XCB Connection */
                                     xcb_gcontext_t    gc,          /* The Graphic Context */
                                     uint32_t          value_mask,  /* Components of the Graphic Context that have to be set */
                                     const uint32_t   *value_list); /* Value as specified by value_mask */

    The value_mask parameter could take any combination of these masks from the xcb_gc_t enumeration:

    • XCB_GC_FUNCTION
    • XCB_GC_PLANE_MASK
    • XCB_GC_FOREGROUND
    • XCB_GC_BACKGROUND
    • XCB_GC_LINE_WIDTH
    • XCB_GC_LINE_STYLE
    • XCB_GC_CAP_STYLE
    • XCB_GC_JOIN_STYLE
    • XCB_GC_FILL_STYLE
    • XCB_GC_FILL_RULE
    • XCB_GC_TILE
    • XCB_GC_STIPPLE
    • XCB_GC_TILE_STIPPLE_ORIGIN_X
    • XCB_GC_TILE_STIPPLE_ORIGIN_Y
    • XCB_GC_FONT
    • XCB_GC_SUBWINDOW_MODE
    • XCB_GC_GRAPHICS_EXPOSURES
    • XCB_GC_CLIP_ORIGIN_X
    • XCB_GC_CLIP_ORIGIN_Y
    • XCB_GC_CLIP_MASK
    • XCB_GC_DASH_OFFSET
    • XCB_GC_DASH_LIST
    • XCB_GC_ARC_MODE

    It is possible to set several attributes at the same time (for example setting the attributes of a font and the color which will be used to display a string), by OR'ing these values in value_mask. Then value_list has to be an array which lists the value for the respective attributes. These values must be in the same order as masks listed above. See Subsection Drawing with a color to have an example.

    TODO: set the links of the 3 subsections, once they will be written :)

    TODO: give an example which sets several attributes.

  3. Drawing primitives: point, line, box, circle,...

    After we have created a Graphic Context, we can draw on a window using this Graphic Context, with a set of XCB functions, collectively called "drawing primitives". Let see how they are used.

    To draw a point, or several points, we use

    xcb_void_cookie_t xcb_poly_point (xcb_connection_t  *c,               /* The connection to the X server */
                                      uint8_t            coordinate_mode, /* Coordinate mode, usually set to XCB_COORD_MODE_ORIGIN */
                                      xcb_drawable_t     drawable,        /* The drawable on which we want to draw the point(s) */
                                      xcb_gcontext_t     gc,              /* The Graphic Context we use to draw the point(s) */
                                      uint32_t           points_len,      /* The number of points */
                                      const xcb_point_t *points);         /* An array of points */
    xcb_void_cookie_t xcb_poly_point (xcb_connection_t  *c,               /* The connection to the X server */
                                      uint8_t            coordinate_mode, /* Coordinate mode, usually set to XCB_COORD_MODE_ORIGIN */
                                      xcb_drawable_t     drawable,        /* The drawable on which we want to draw the point(s) */
                                      xcb_gcontext_t     gc,              /* The Graphic Context we use to draw the point(s) */
                                      uint32_t           points_len,      /* The number of points */
                                      const xcb_point_t *points);         /* An array of points */

    The coordinate_mode parameter specifies the coordinate mode. Available values are

    • XCB_COORD_MODE_ORIGIN
    • XCB_COORD_MODE_PREVIOUS

    If XCB_COORD_MODE_PREVIOUS is used, then all points but the first one are relative to the immediately previous point.

    The xcb_point_t type is just a structure with two fields (the coordinates of the point):

    typedef struct {
        int16_t x;
        int16_t y;
    } xcb_point_t;
    typedef struct {
        int16_t x;
        int16_t y;
    } xcb_point_t;

    You could see an example in xpoints.c. TODO Set the link.

    To draw a line, or a polygonal line, we use

    xcb_void_cookie_t xcb_poly_line (xcb_connection_t  *c,               /* The connection to the X server */
                                     uint8_t            coordinate_mode, /* Coordinate mode, usually set to XCB_COORD_MODE_ORIGIN */
                                     xcb_drawable_t     drawable,        /* The drawable on which we want to draw the line(s) */
                                     xcb_gcontext_t     gc,              /* The Graphic Context we use to draw the line(s) */
                                     uint32_t           points_len,      /* The number of points in the polygonal line */
                                     const xcb_point_t *points);         /* An array of points */
    xcb_void_cookie_t xcb_poly_line (xcb_connection_t  *c,               /* The connection to the X server */
                                     uint8_t            coordinate_mode, /* Coordinate mode, usually set to XCB_COORD_MODE_ORIGIN */
                                     xcb_drawable_t     drawable,        /* The drawable on which we want to draw the line(s) */
                                     xcb_gcontext_t     gc,              /* The Graphic Context we use to draw the line(s) */
                                     uint32_t           points_len,      /* The number of points in the polygonal line */
                                     const xcb_point_t *points);         /* An array of points */

    This function will draw the line between the first and the second points, then the line between the second and the third points, and so on.

    To draw a segment, or several segments, we use

    xcb_void_cookie_t xcb_poly_segment (xcb_connection_t    *c,              /* The connection to the X server */
                                        xcb_drawable_t       drawable,       /* The drawable on which we want to draw the segment(s) */
                                        xcb_gcontext_t       gc,             /* The Graphic Context we use to draw the segment(s) */
                                        uint32_t             segments_len,   /* The number of segments */
                                        const xcb_segment_t *segments);      /* An array of segments */
    xcb_void_cookie_t xcb_poly_segment (xcb_connection_t    *c,              /* The connection to the X server */
                                        xcb_drawable_t       drawable,       /* The drawable on which we want to draw the segment(s) */
                                        xcb_gcontext_t       gc,             /* The Graphic Context we use to draw the segment(s) */
                                        uint32_t             segments_len,   /* The number of segments */
                                        const xcb_segment_t *segments);      /* An array of segments */

    The xcb_segment_t type is just a structure with four fields (the coordinates of the two points that define the segment):

    typedef struct {
        int16_t x1;
        int16_t y1;
        int16_t x2;
        int16_t y2;
    } xcb_segment_t;
    typedef struct {
        int16_t x1;
        int16_t y1;
        int16_t x2;
        int16_t y2;
    } xcb_segment_t;

    To draw a rectangle, or several rectangles, we use

    xcb_void_cookie_t xcb_poly_rectangle (xcb_connection_t      *c,              /* The connection to the X server */
                                          xcb_drawable_t         drawable,       /* The drawable on which we want to draw the rectangle(s) */
                                          xcb_gcontext_t         gc,             /* The Graphic Context we use to draw the rectangle(s) */
                                          uint32_t               rectangles_len, /* The number of rectangles */
                                          const xcb_rectangle_t *rectangles);    /* An array of rectangles */
    xcb_void_cookie_t xcb_poly_rectangle (xcb_connection_t      *c,              /* The connection to the X server */
                                          xcb_drawable_t         drawable,       /* The drawable on which we want to draw the rectangle(s) */
                                          xcb_gcontext_t         gc,             /* The Graphic Context we use to draw the rectangle(s) */
                                          uint32_t               rectangles_len, /* The number of rectangles */
                                          const xcb_rectangle_t *rectangles);    /* An array of rectangles */

    The xcb_rectangle_t type is just a structure with four fields (the coordinates of the top-left corner of the rectangle, and its width and height):

    typedef struct {
        int16_t  x;
        int16_t  y;
        uint16_t width;
        uint16_t height;
    } xcb_rectangle_t;
    typedef struct {
        int16_t  x;
        int16_t  y;
        uint16_t width;
        uint16_t height;
    } xcb_rectangle_t;

    To draw an elliptical arc, or several elliptical arcs, we use

    xcb_void_cookie_t xcb_poly_arc (xcb_connection_t *c,          /* The connection to the X server */
                                    xcb_drawable_t    drawable,   /* The drawable on which we want to draw the arc(s) */
                                    xcb_gcontext_t    gc,         /* The Graphic Context we use to draw the arc(s) */
                                    uint32_t          arcs_len,   /* The number of arcs */
                                    const xcb_arc_t  *arcs);      /* An array of arcs */
    xcb_void_cookie_t xcb_poly_arc (xcb_connection_t *c,          /* The connection to the X server */
                                    xcb_drawable_t    drawable,   /* The drawable on which we want to draw the arc(s) */
                                    xcb_gcontext_t    gc,         /* The Graphic Context we use to draw the arc(s) */
                                    uint32_t          arcs_len,   /* The number of arcs */
                                    const xcb_arc_t  *arcs);      /* An array of arcs */

    The xcb_arc_t type is a structure with six fields:

    typedef struct {
        int16_t  x;       /* Top left x coordinate of the rectangle surrounding the ellipse */
        int16_t  y;       /* Top left y coordinate of the rectangle surrounding the ellipse */
        uint16_t width;   /* Width of the rectangle surrounding the ellipse */
        uint16_t height;  /* Height of the rectangle surrounding the ellipse */
        int16_t  angle1;  /* Angle at which the arc begins */
        int16_t  angle2;  /* Angle at which the arc ends */
    } xcb_arc_t;
    typedef struct {
        int16_t  x;       /* Top left x coordinate of the rectangle surrounding the ellipse */
        int16_t  y;       /* Top left y coordinate of the rectangle surrounding the ellipse */
        uint16_t width;   /* Width of the rectangle surrounding the ellipse */
        uint16_t height;  /* Height of the rectangle surrounding the ellipse */
        int16_t  angle1;  /* Angle at which the arc begins */
        int16_t  angle2;  /* Angle at which the arc ends */
    } xcb_arc_t;

    Note: the angles are expressed in units of 1/64 of a degree, so to have an angle of 90 degrees, starting at 0, angle1 = 0 and angle2 = 90 << 6. Positive angles indicate counterclockwise motion, while negative angles indicate clockwise motion.

    The corresponding function which fill inside the geometrical object are listed below, without further explanation, as they are used as the above functions.

    To Fill a polygon defined by the points given as arguments , we use

    xcb_void_cookie_t xcb_fill_poly (xcb_connection_t  *c,
                                     xcb_drawable_t     drawable,
                                     xcb_gcontext_t     gc,
                                     uint8_t            shape,
                                     uint8_t            coordinate_mode,
                                     uint32_t           points_len,
                                     const xcb_point_t *points);
    xcb_void_cookie_t xcb_fill_poly (xcb_connection_t  *c,
                                     xcb_drawable_t     drawable,
                                     xcb_gcontext_t     gc,
                                     uint8_t            shape,
                                     uint8_t            coordinate_mode,
                                     uint32_t           points_len,
                                     const xcb_point_t *points);

    The shape parameter specifies a shape that helps the server to improve performance. Available values are

    • XCB_POLY_SHAPE_COMPLEX
    • XCB_POLY_SHAPE_NONCONVEX
    • XCB_POLY_SHAPE_CONVEX

    To fill one or several rectangles, we use

    xcb_void_cookie_t xcb_poly_fill_rectangle (xcb_connection_t      *c,
                                               xcb_drawable_t         drawable,
                                               xcb_gcontext_t         gc,
                                               uint32_t               rectangles_len,
                                               const xcb_rectangle_t *rectangles);
    xcb_void_cookie_t xcb_poly_fill_rectangle (xcb_connection_t      *c,
                                               xcb_drawable_t         drawable,
                                               xcb_gcontext_t         gc,
                                               uint32_t               rectangles_len,
                                               const xcb_rectangle_t *rectangles);

    To fill one or several arcs, we use

    xcb_void_cookie_t xcb_poly_fill_arc (xcb_connection_t *c,
                                         xcb_drawable_t    drawable,
                                         xcb_gcontext_t    gc,
                                         uint32_t          arcs_len,
                                         const xcb_arc_t  *arcs);
    xcb_void_cookie_t xcb_poly_fill_arc (xcb_connection_t *c,
                                         xcb_drawable_t    drawable,
                                         xcb_gcontext_t    gc,
                                         uint32_t          arcs_len,
                                         const xcb_arc_t  *arcs);

    To illustrate these functions, here is an example that draws four points, a polygonal line, two segments, two rectangles and two arcs. Remark that we use events for the first time, as an introduction to the next section.

    TODO: Use screen->root_depth for depth parameter.

    #include <stdlib.h>
    #include <stdio.h>
    
    #include <xcb/xcb.h>
    
    int
    main ()
    {
      xcb_connection_t    *c;
      xcb_screen_t        *screen;
      xcb_drawable_t       win;
      xcb_gcontext_t       foreground;
      xcb_generic_event_t *e;
      uint32_t             mask = 0;
      uint32_t             values[2];
    
      /* geometric objects */
      xcb_point_t          points[] = {
        {10, 10},
        {10, 20},
        {20, 10},
        {20, 20}};
    
      xcb_point_t          polyline[] = {
        {50, 10},
        { 5, 20},     /* rest of points are relative */
        {25,-20},
        {10, 10}};
    
      xcb_segment_t        segments[] = {
        {100, 10, 140, 30},
        {110, 25, 130, 60}};
    
      xcb_rectangle_t      rectangles[] = {
        { 10, 50, 40, 20},
        { 80, 50, 10, 40}};
    
      xcb_arc_t            arcs[] = {
        {10, 100, 60, 40, 0, 90 << 6},
        {90, 100, 55, 40, 0, 270 << 6}};
    
      /* Open the connection to the X server */
      c = xcb_connect (NULL, NULL);
    
      /* Get the first screen */
      screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
    
      /* Create black (foreground) graphic context */
      win = screen->root;
    
      foreground = xcb_generate_id (c);
      mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
      values[0] = screen->black_pixel;
      values[1] = 0;
      xcb_create_gc (c, foreground, win, mask, values);
    
      /* Ask for our window's Id */
      win = xcb_generate_id(c);
    
      /* Create the window */
      mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
      values[0] = screen->white_pixel;
      values[1] = XCB_EVENT_MASK_EXPOSURE;
      xcb_create_window (c,                             /* Connection          */
                         XCB_COPY_FROM_PARENT,          /* depth               */
                         win,                           /* window Id           */
                         screen->root,                  /* parent window       */
                         0, 0,                          /* x, y                */
                         150, 150,                      /* width, height       */
                         10,                            /* border_width        */
                         XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class               */
                         screen->root_visual,           /* visual              */
                         mask, values);                 /* masks */
    
      /* Map the window on the screen */
      xcb_map_window (c, win);
    
    
      /* We flush the request */
      xcb_flush (c);
    
      while ((e = xcb_wait_for_event (c))) {
        switch (e->response_type & ~0x80) {
        case XCB_EXPOSE: {
          /* We draw the points */
          xcb_poly_point (c, XCB_COORD_MODE_ORIGIN, win, foreground, 4, points);
    
          /* We draw the polygonal line */
          xcb_poly_line (c, XCB_COORD_MODE_PREVIOUS, win, foreground, 4, polyline);
    
          /* We draw the segements */
          xcb_poly_segment (c, win, foreground, 2, segments);
    
          /* We draw the rectangles */
          xcb_poly_rectangle (c, win, foreground, 2, rectangles);
    
          /* We draw the arcs */
          xcb_poly_arc (c, win, foreground, 2, arcs);
    
          /* We flush the request */
          xcb_flush (c);
    
          break;
        }
        default: {
          /* Unknown event type, ignore it */
          break;
        }
        }
        /* Free the Generic Event */
        free (e);
      }
    
      return 0;
    }
    #include <stdlib.h>
    #include <stdio.h>
    
    #include <xcb/xcb.h>
    
    int
    main ()
    {
      xcb_connection_t    *c;
      xcb_screen_t        *screen;
      xcb_drawable_t       win;
      xcb_gcontext_t       foreground;
      xcb_generic_event_t *e;
      uint32_t             mask = 0;
      uint32_t             values[2];
    
      /* geometric objects */
      xcb_point_t          points[] = {
        {10, 10},
        {10, 20},
        {20, 10},
        {20, 20}};
    
      xcb_point_t          polyline[] = {
        {50, 10},
        { 5, 20},     /* rest of points are relative */
        {25,-20},
        {10, 10}};
    
      xcb_segment_t        segments[] = {
        {100, 10, 140, 30},
        {110, 25, 130, 60}};
    
      xcb_rectangle_t      rectangles[] = {
        { 10, 50, 40, 20},
        { 80, 50, 10, 40}};
    
      xcb_arc_t            arcs[] = {
        {10, 100, 60, 40, 0, 90 << 6},
        {90, 100, 55, 40, 0, 270 << 6}};
    
      /* Open the connection to the X server */
      c = xcb_connect (NULL, NULL);
    
      /* Get the first screen */
      screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
    
      /* Create black (foreground) graphic context */
      win = screen->root;
    
      foreground = xcb_generate_id (c);
      mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
      values[0] = screen->black_pixel;
      values[1] = 0;
      xcb_create_gc (c, foreground, win, mask, values);
    
      /* Ask for our window's Id */
      win = xcb_generate_id(c);
    
      /* Create the window */
      mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
      values[0] = screen->white_pixel;
      values[1] = XCB_EVENT_MASK_EXPOSURE;
      xcb_create_window (c,                             /* Connection          */
                         XCB_COPY_FROM_PARENT,          /* depth               */
                         win,                           /* window Id           */
                         screen->root,                  /* parent window       */
                         0, 0,                          /* x, y                */
                         150, 150,                      /* width, height       */
                         10,                            /* border_width        */
                         XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class               */
                         screen->root_visual,           /* visual              */
                         mask, values);                 /* masks */
    
      /* Map the window on the screen */
      xcb_map_window (c, win);
    
    
      /* We flush the request */
      xcb_flush (c);
    
      while ((e = xcb_wait_for_event (c))) {
        switch (e->response_type & ~0x80) {
        case XCB_EXPOSE: {
          /* We draw the points */
          xcb_poly_point (c, XCB_COORD_MODE_ORIGIN, win, foreground, 4, points);
    
          /* We draw the polygonal line */
          xcb_poly_line (c, XCB_COORD_MODE_PREVIOUS, win, foreground, 4, polyline);
    
          /* We draw the segements */
          xcb_poly_segment (c, win, foreground, 2, segments);
    
          /* We draw the rectangles */
          xcb_poly_rectangle (c, win, foreground, 2, rectangles);
    
          /* We draw the arcs */
          xcb_poly_arc (c, win, foreground, 2, arcs);
    
          /* We flush the request */
          xcb_flush (c);
    
          break;
        }
        default: {
          /* Unknown event type, ignore it */
          break;
        }
        }
        /* Free the Generic Event */
        free (e);
      }
    
      return 0;
    }