Skip to content

Handling text and fonts

Besides drawing graphics on a window, we often want to draw text. Text strings have two major properties: the characters to be drawn and the font with which they are drawn. In order to draw text, we need to first request the X server to load a font. We then assign a font to a Graphic Context, and finally, we draw the text in a window, using the Graphic Context.

  1. The Font structure

    In order to support flexible fonts, a font type is defined. You know what ? It's an Id:

    typedef uint32_t xcb_font_t;
    typedef uint32_t xcb_font_t;

    It is used to contain information about a font, and is passed to several functions that handle fonts selection and text drawing. We ask the X server to attribute an Id to our font with the function:

    xcb_font_t xcb_generate_id (xcb_connection_t *c);
    xcb_font_t xcb_generate_id (xcb_connection_t *c);

  2. Opening a Font

    To open a font, we use the following function:

    xcb_void_cookie_t xcb_open_font (xcb_connection_t *c,
                                     xcb_font_t        fid,
                                     uint16_t          name_len,
                                     const char       *name);
    xcb_void_cookie_t xcb_open_font (xcb_connection_t *c,
                                     xcb_font_t        fid,
                                     uint16_t          name_len,
                                     const char       *name);

    The fid parameter is the font Id defined by xcb_generate_id() (see above). The name parameter is the name of the font you want to open. Use the command xlsfonts in a terminal to know which are the fonts available on your computer. The parameter name_len is the length of the name of the font (given by strlen()).

  3. Assigning a Font to a Graphic Context

    Once a font is opened, you have to create a Graphic Context that will contain the informations about the color of the foreground and the background used when you draw a text in a Drawable. Here is an exemple of a Graphic Context that will allow us to draw an opened font with a black foreground and a white background:

      /*
       * c is the connection
       * screen is the screen where the window is displayed
       * window is the window in which we will draw the text
       * font is the opened font
       */
    
      uint32_t             value_list[3];
      xcb_gcontext_t       gc;
      uint32_t             mask;
    
      gc = xcb_generate_id (c);
      mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
      value_list[0] = screen->black_pixel;
      value_list[1] = screen->white_pixel;
      value_list[2] = font;
      xcb_create_gc (c, gc, window, mask, value_list);
    
      /* The font is not needed anymore, so we close it */
      xcb_close_font (c, font);
      /*
       * c is the connection
       * screen is the screen where the window is displayed
       * window is the window in which we will draw the text
       * font is the opened font
       */
    
      uint32_t             value_list[3];
      xcb_gcontext_t       gc;
      uint32_t             mask;
    
      gc = xcb_generate_id (c);
      mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
      value_list[0] = screen->black_pixel;
      value_list[1] = screen->white_pixel;
      value_list[2] = font;
      xcb_create_gc (c, gc, window, mask, value_list);
    
      /* The font is not needed anymore, so we close it */
      xcb_close_font (c, font);
  4. Drawing text in a drawable

    To draw a text in a drawable, we use the following function:

    xcb_void_cookie_t xcb_image_text_8 (xcb_connection_t *c,
                                        uint8_t           string_len,
                                        xcb_drawable_t    drawable,
                                        xcb_gcontext_t    gc,
                                        int16_t           x,
                                        int16_t           y,
                                        const char       *string);
    xcb_void_cookie_t xcb_image_text_8 (xcb_connection_t *c,
                                        uint8_t           string_len,
                                        xcb_drawable_t    drawable,
                                        xcb_gcontext_t    gc,
                                        int16_t           x,
                                        int16_t           y,
                                        const char       *string);

    The string parameter is the text to draw. The location of the drawing is given by the parameters x and y. The base line of the text is exactly the parameter y.

  5. Complete example

    This example draw a text at 10 pixels (for the base line) of the bottom of a window. Pressing the Esc key exits the program.

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    #include <xcb/xcb.h>
    
    #define WIDTH 300
    #define HEIGHT 100
    
    
    
    static xcb_gc_t gc_font_get (xcb_connection_t *c,
                                 xcb_screen_t     *screen,
                                 xcb_window_t      window,
                                 const char       *font_name);
    
    static void text_draw (xcb_connection_t *c,
                           xcb_screen_t     *screen,
                           xcb_window_t      window,
                           int16_t           x1,
                           int16_t           y1,
                           const char       *label);
    
    static void
    text_draw (xcb_connection_t *c,
               xcb_screen_t     *screen,
               xcb_window_t      window,
               int16_t           x1,
               int16_t           y1,
               const char       *label)
    {
      xcb_void_cookie_t    cookie_gc;
      xcb_void_cookie_t    cookie_text;
      xcb_generic_error_t *error;
      xcb_gcontext_t       gc;
      uint8_t              length;
    
      length = strlen (label);
    
      gc = gc_font_get(c, screen, window, "7x13");
    
      cookie_text = xcb_image_text_8_checked (c, length, window, gc,
                                              x1,
                                              y1, label);
      error = xcb_request_check (c, cookie_text);
      if (error) {
        fprintf (stderr, "ERROR: can't paste text : %d\n", error->error_code);
        xcb_disconnect (c);
        exit (-1);
      }
    
      cookie_gc = xcb_free_gc (c, gc);
      error = xcb_request_check (c, cookie_gc);
      if (error) {
        fprintf (stderr, "ERROR: can't free gc : %d\n", error->error_code);
        xcb_disconnect (c);
        exit (-1);
      }
    }
    
    static xcb_gc_t
    gc_font_get (xcb_connection_t *c,
                 xcb_screen_t     *screen,
                 xcb_window_t      window,
                 const char       *font_name)
    {
      uint32_t             value_list[3];
      xcb_void_cookie_t    cookie_font;
      xcb_void_cookie_t    cookie_gc;
      xcb_generic_error_t *error;
      xcb_font_t           font;
      xcb_gcontext_t       gc;
      uint32_t             mask;
    
      font = xcb_generate_id (c);
      cookie_font = xcb_open_font_checked (c, font,
                                           strlen (font_name),
                                           font_name);
    
      error = xcb_request_check (c, cookie_font);
      if (error) {
        fprintf (stderr, "ERROR: can't open font : %d\n", error->error_code);
        xcb_disconnect (c);
        return -1;
      }
    
      gc = xcb_generate_id (c);
      mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
      value_list[0] = screen->black_pixel;
      value_list[1] = screen->white_pixel;
      value_list[2] = font;
      cookie_gc = xcb_create_gc_checked (c, gc, window, mask, value_list);
      error = xcb_request_check (c, cookie_gc);
      if (error) {
        fprintf (stderr, "ERROR: can't create gc : %d\n", error->error_code);
        xcb_disconnect (c);
        exit (-1);
      }
    
      cookie_font = xcb_close_font_checked (c, font);
      error = xcb_request_check (c, cookie_font);
      if (error) {
        fprintf (stderr, "ERROR: can't close font : %d\n", error->error_code);
        xcb_disconnect (c);
        exit (-1);
      }
    
      return gc;
    }
    
    int main ()
    {
      xcb_screen_iterator_t screen_iter;
      xcb_connection_t     *c;
      const xcb_setup_t    *setup;
      xcb_screen_t         *screen;
      xcb_generic_event_t  *e;
      xcb_generic_error_t  *error;
      xcb_void_cookie_t     cookie_window;
      xcb_void_cookie_t     cookie_map;
      xcb_window_t          window;
      uint32_t              mask;
      uint32_t              values[2];
      int                   screen_number;
    
      /* getting the connection */
      c = xcb_connect (NULL, &screen_number);
      if (!c) {
        fprintf (stderr, "ERROR: can't connect to an X server\n");
        return -1;
      }
    
      /* getting the current screen */
      setup = xcb_get_setup (c);
    
      screen = NULL;
      screen_iter = xcb_setup_roots_iterator (setup);
      for (; screen_iter.rem != 0; --screen_number, xcb_screen_next (&screen_iter))
        if (screen_number == 0)
          {
            screen = screen_iter.data;
            break;
          }
      if (!screen) {
        fprintf (stderr, "ERROR: can't get the current screen\n");
        xcb_disconnect (c);
        return -1;
      }
    
      /* creating the window */
      window = xcb_generate_id (c);
      mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
      values[0] = screen->white_pixel;
      values[1] =
        XCB_EVENT_MASK_KEY_RELEASE |
        XCB_EVENT_MASK_BUTTON_PRESS |
        XCB_EVENT_MASK_EXPOSURE |
        XCB_EVENT_MASK_POINTER_MOTION;
      cookie_window = xcb_create_window_checked (c,
                                                 screen->root_depth,
                                                 window, screen->root,
                                                 20, 200, WIDTH, HEIGHT,
                                                 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
                                                 screen->root_visual,
                                                 mask, values);
      cookie_map = xcb_map_window_checked (c, window);
    
      /* error managing */
      error = xcb_request_check (c, cookie_window);
      if (error) {
        fprintf (stderr, "ERROR: can't create window : %d\n", error->error_code);
        xcb_disconnect (c);
        return -1;
      }
      error = xcb_request_check (c, cookie_map);
      if (error) {
        fprintf (stderr, "ERROR: can't map window : %d\n", error->error_code);
        xcb_disconnect (c);
        return -1;
      }
    
      xcb_flush(c);
    
      while (1) {
        e = xcb_poll_for_event(c);
        if (e) {
          switch (e->response_type & ~0x80) {
          case XCB_EXPOSE: {
            char *text;
    
            text = "Press ESC key to exit...";
            text_draw (c, screen, window, 10, HEIGHT - 10, text);
            break;
          }
          case XCB_KEY_RELEASE: {
            xcb_key_release_event_t *ev;
    
            ev = (xcb_key_release_event_t *)e;
    
            switch (ev->detail) {
              /* ESC */
            case 9:
              free (e);
              xcb_disconnect (c);
              return 0;
            }
          }
          }
          free (e);
        }
      }
    
      return 0;
    }
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    #include <xcb/xcb.h>
    
    #define WIDTH 300
    #define HEIGHT 100
    
    
    
    static xcb_gc_t gc_font_get (xcb_connection_t *c,
                                 xcb_screen_t     *screen,
                                 xcb_window_t      window,
                                 const char       *font_name);
    
    static void text_draw (xcb_connection_t *c,
                           xcb_screen_t     *screen,
                           xcb_window_t      window,
                           int16_t           x1,
                           int16_t           y1,
                           const char       *label);
    
    static void
    text_draw (xcb_connection_t *c,
               xcb_screen_t     *screen,
               xcb_window_t      window,
               int16_t           x1,
               int16_t           y1,
               const char       *label)
    {
      xcb_void_cookie_t    cookie_gc;
      xcb_void_cookie_t    cookie_text;
      xcb_generic_error_t *error;
      xcb_gcontext_t       gc;
      uint8_t              length;
    
      length = strlen (label);
    
      gc = gc_font_get(c, screen, window, "7x13");
    
      cookie_text = xcb_image_text_8_checked (c, length, window, gc,
                                              x1,
                                              y1, label);
      error = xcb_request_check (c, cookie_text);
      if (error) {
        fprintf (stderr, "ERROR: can't paste text : %d\n", error->error_code);
        xcb_disconnect (c);
        exit (-1);
      }
    
      cookie_gc = xcb_free_gc (c, gc);
      error = xcb_request_check (c, cookie_gc);
      if (error) {
        fprintf (stderr, "ERROR: can't free gc : %d\n", error->error_code);
        xcb_disconnect (c);
        exit (-1);
      }
    }
    
    static xcb_gc_t
    gc_font_get (xcb_connection_t *c,
                 xcb_screen_t     *screen,
                 xcb_window_t      window,
                 const char       *font_name)
    {
      uint32_t             value_list[3];
      xcb_void_cookie_t    cookie_font;
      xcb_void_cookie_t    cookie_gc;
      xcb_generic_error_t *error;
      xcb_font_t           font;
      xcb_gcontext_t       gc;
      uint32_t             mask;
    
      font = xcb_generate_id (c);
      cookie_font = xcb_open_font_checked (c, font,
                                           strlen (font_name),
                                           font_name);
    
      error = xcb_request_check (c, cookie_font);
      if (error) {
        fprintf (stderr, "ERROR: can't open font : %d\n", error->error_code);
        xcb_disconnect (c);
        return -1;
      }
    
      gc = xcb_generate_id (c);
      mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
      value_list[0] = screen->black_pixel;
      value_list[1] = screen->white_pixel;
      value_list[2] = font;
      cookie_gc = xcb_create_gc_checked (c, gc, window, mask, value_list);
      error = xcb_request_check (c, cookie_gc);
      if (error) {
        fprintf (stderr, "ERROR: can't create gc : %d\n", error->error_code);
        xcb_disconnect (c);
        exit (-1);
      }
    
      cookie_font = xcb_close_font_checked (c, font);
      error = xcb_request_check (c, cookie_font);
      if (error) {
        fprintf (stderr, "ERROR: can't close font : %d\n", error->error_code);
        xcb_disconnect (c);
        exit (-1);
      }
    
      return gc;
    }
    
    int main ()
    {
      xcb_screen_iterator_t screen_iter;
      xcb_connection_t     *c;
      const xcb_setup_t    *setup;
      xcb_screen_t         *screen;
      xcb_generic_event_t  *e;
      xcb_generic_error_t  *error;
      xcb_void_cookie_t     cookie_window;
      xcb_void_cookie_t     cookie_map;
      xcb_window_t          window;
      uint32_t              mask;
      uint32_t              values[2];
      int                   screen_number;
    
      /* getting the connection */
      c = xcb_connect (NULL, &screen_number);
      if (!c) {
        fprintf (stderr, "ERROR: can't connect to an X server\n");
        return -1;
      }
    
      /* getting the current screen */
      setup = xcb_get_setup (c);
    
      screen = NULL;
      screen_iter = xcb_setup_roots_iterator (setup);
      for (; screen_iter.rem != 0; --screen_number, xcb_screen_next (&screen_iter))
        if (screen_number == 0)
          {
            screen = screen_iter.data;
            break;
          }
      if (!screen) {
        fprintf (stderr, "ERROR: can't get the current screen\n");
        xcb_disconnect (c);
        return -1;
      }
    
      /* creating the window */
      window = xcb_generate_id (c);
      mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
      values[0] = screen->white_pixel;
      values[1] =
        XCB_EVENT_MASK_KEY_RELEASE |
        XCB_EVENT_MASK_BUTTON_PRESS |
        XCB_EVENT_MASK_EXPOSURE |
        XCB_EVENT_MASK_POINTER_MOTION;
      cookie_window = xcb_create_window_checked (c,
                                                 screen->root_depth,
                                                 window, screen->root,
                                                 20, 200, WIDTH, HEIGHT,
                                                 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
                                                 screen->root_visual,
                                                 mask, values);
      cookie_map = xcb_map_window_checked (c, window);
    
      /* error managing */
      error = xcb_request_check (c, cookie_window);
      if (error) {
        fprintf (stderr, "ERROR: can't create window : %d\n", error->error_code);
        xcb_disconnect (c);
        return -1;
      }
      error = xcb_request_check (c, cookie_map);
      if (error) {
        fprintf (stderr, "ERROR: can't map window : %d\n", error->error_code);
        xcb_disconnect (c);
        return -1;
      }
    
      xcb_flush(c);
    
      while (1) {
        e = xcb_poll_for_event(c);
        if (e) {
          switch (e->response_type & ~0x80) {
          case XCB_EXPOSE: {
            char *text;
    
            text = "Press ESC key to exit...";
            text_draw (c, screen, window, 10, HEIGHT - 10, text);
            break;
          }
          case XCB_KEY_RELEASE: {
            xcb_key_release_event_t *ev;
    
            ev = (xcb_key_release_event_t *)e;
    
            switch (ev->detail) {
              /* ESC */
            case 9:
              free (e);
              xcb_disconnect (c);
              return 0;
            }
          }
          }
          free (e);
        }
      }
    
      return 0;
    }