Skip to content

Basic XCB notions

XCB has been created to eliminate the need for programs to actually implement the X protocol layer. This library gives a program a very low-level access to any X server. Since the protocol is standardized, a client using any implementation of XCB may talk with any X server (the same occurs for Xlib, of course). We now give a brief description of the basic XCB notions. They will be detailed later.

  1. The X Connection

    The major notion of using XCB is the X Connection. This is a structure representing the connection we have open with a given X server. It hides a queue of messages coming from the server, and a queue of pending requests that our client intends to send to the server. In XCB, this structure is named 'xcb_connection_t'. It is analogous to the Xlib Display. When we open a connection to an X server, the library returns a pointer to such a structure. Later, we supply this pointer to any XCB function that should send messages to the X server or receive messages from this server.

  2. Requests and replies: the Xlib killers

    To ask for information from the X server, we have to make a request and ask for a reply. With Xlib, these two tasks are automatically done: Xlib locks the system, sends a request, waits for a reply from the X server and unlocks. This is annoying, especially if one makes a lot of requests to the X server. Indeed, Xlib has to wait for the end of a reply before asking for the next request (because of the locks that Xlib sends). For example, here is a time-line of N=4 requests/replies with Xlib, with a round-trip latency T_round_trip that is 5 times long as the time required to write or read a request/reply (T_write/T_read):

      W-----RW-----RW-----RW-----R
    
    • W: Writing request
    • -: Stalled, waiting for data
    • R: Reading reply

    The total time is N * (T_write + T_round_trip + T_read).

    With XCB, we can suppress most of the round-trips as the requests and the replies are not locked. We usually send a request, then XCB returns to us a cookie, which is an identifier. Then, later, we ask for a reply using this cookie and XCB returns a pointer to that reply. Hence, with XCB, we can send a lot of requests, and later in the program, ask for all the replies when we need them. Here is the time-line for 4 requests/replies when we use this property of XCB:

      WWWW--RRRR
    

    The total time is N * T_write + max (0, T_round_trip - (N-1) * T_write) + N * T_read. Which can be considerably faster than all those Xlib round-trips.

    Here is a program that computes the time to create 500 atoms with Xlib and XCB. It shows the Xlib way, the bad XCB way (which is similar to Xlib) and the good XCB way. On my computer, XCB is 25 times faster than Xlib.

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/time.h>
    
    #include <xcb/xcb.h>
    
    #include <X11/Xlib.h>
    
    double
    get_time(void)
    {
      struct timeval timev;
    
      gettimeofday(&timev, NULL);
    
      return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000);
    }
    
    int
    main ()
    {
      xcb_connection_t         *c;
      xcb_atom_t               *atoms;
      xcb_intern_atom_cookie_t *cs;
      char                    **names;
      int                       count;
      int                       i;
      double                    start;
      double                    end;
      double                    diff;
    
      /* Xlib */
      Display *disp;
      Atom    *atoms_x;
      double   diff_x;
    
      c = xcb_connect (NULL, NULL);
    
      count = 500;
      atoms = (xcb_atom_t *)malloc (count * sizeof (atoms));
      names = (char **)malloc (count * sizeof (char *));
    
      /* init names */
      for (i = 0; i < count; ++i) {
        char buf[100];
    
        sprintf (buf, "NAME%d", i);
        names[i] = strdup (buf);
      }
    
      /* bad use */
      start = get_time ();
    
      for (i = 0; i < count; ++i)
        atoms[i] = xcb_intern_atom_reply (c,
                                          xcb_intern_atom (c,
                                                           0,
                                                           strlen(names[i]),
                                                           names[i]),
                                          NULL)->atom;
    
      end = get_time ();
      diff = end - start;
      printf ("bad use time  : %f\n", diff);
    
      /* good use */
      start = get_time ();
    
      cs = (xcb_intern_atom_cookie_t *) malloc (count * sizeof(xcb_intern_atom_cookie_t));
      for(i = 0; i < count; ++i)
        cs[i] = xcb_intern_atom (c, 0, strlen(names[i]), names[i]);
    
      for(i = 0; i < count; ++i) {
        xcb_intern_atom_reply_t *r;
    
        r = xcb_intern_atom_reply(c, cs[i], 0);
        if(r)
          atoms[i] = r->atom;
        free(r);
      }
    
      end = get_time ();
      printf ("good use time : %f\n", end - start);
      printf ("ratio         : %f\n", diff / (end - start));
      diff = end - start;
    
      /* free var */
      free (atoms);
      free (cs);
    
      xcb_disconnect (c);
    
      /* Xlib */
      disp = XOpenDisplay (getenv("DISPLAY"));
    
      atoms_x = (Atom *)malloc (count * sizeof (atoms_x));
    
      start = get_time ();
    
      for (i = 0; i < count; ++i)
        atoms_x[i] = XInternAtom(disp, names[i], 0);
    
      end = get_time ();
      diff_x = end - start;
      printf ("Xlib use time : %f\n", diff_x);
      printf ("ratio         : %f\n", diff_x / diff);
    
      free (atoms_x);
      for (i = 0; i < count; ++i)
        free (names[i]);
      free (names);
    
      XCloseDisplay (disp);
    
      return 0;
    }
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/time.h>
    
    #include <xcb/xcb.h>
    
    #include <X11/Xlib.h>
    
    double
    get_time(void)
    {
      struct timeval timev;
    
      gettimeofday(&timev, NULL);
    
      return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000);
    }
    
    int
    main ()
    {
      xcb_connection_t         *c;
      xcb_atom_t               *atoms;
      xcb_intern_atom_cookie_t *cs;
      char                    **names;
      int                       count;
      int                       i;
      double                    start;
      double                    end;
      double                    diff;
    
      /* Xlib */
      Display *disp;
      Atom    *atoms_x;
      double   diff_x;
    
      c = xcb_connect (NULL, NULL);
    
      count = 500;
      atoms = (xcb_atom_t *)malloc (count * sizeof (atoms));
      names = (char **)malloc (count * sizeof (char *));
    
      /* init names */
      for (i = 0; i < count; ++i) {
        char buf[100];
    
        sprintf (buf, "NAME%d", i);
        names[i] = strdup (buf);
      }
    
      /* bad use */
      start = get_time ();
    
      for (i = 0; i < count; ++i)
        atoms[i] = xcb_intern_atom_reply (c,
                                          xcb_intern_atom (c,
                                                           0,
                                                           strlen(names[i]),
                                                           names[i]),
                                          NULL)->atom;
    
      end = get_time ();
      diff = end - start;
      printf ("bad use time  : %f\n", diff);
    
      /* good use */
      start = get_time ();
    
      cs = (xcb_intern_atom_cookie_t *) malloc (count * sizeof(xcb_intern_atom_cookie_t));
      for(i = 0; i < count; ++i)
        cs[i] = xcb_intern_atom (c, 0, strlen(names[i]), names[i]);
    
      for(i = 0; i < count; ++i) {
        xcb_intern_atom_reply_t *r;
    
        r = xcb_intern_atom_reply(c, cs[i], 0);
        if(r)
          atoms[i] = r->atom;
        free(r);
      }
    
      end = get_time ();
      printf ("good use time : %f\n", end - start);
      printf ("ratio         : %f\n", diff / (end - start));
      diff = end - start;
    
      /* free var */
      free (atoms);
      free (cs);
    
      xcb_disconnect (c);
    
      /* Xlib */
      disp = XOpenDisplay (getenv("DISPLAY"));
    
      atoms_x = (Atom *)malloc (count * sizeof (atoms_x));
    
      start = get_time ();
    
      for (i = 0; i < count; ++i)
        atoms_x[i] = XInternAtom(disp, names[i], 0);
    
      end = get_time ();
      diff_x = end - start;
      printf ("Xlib use time : %f\n", diff_x);
      printf ("ratio         : %f\n", diff_x / diff);
    
      free (atoms_x);
      for (i = 0; i < count; ++i)
        free (names[i]);
      free (names);
    
      XCloseDisplay (disp);
    
      return 0;
    }
  3. The Graphic Context

    When we perform various drawing operations (graphics, text, etc), we may specify various options for controlling how the data will be drawn (what foreground and background colors to use, how line edges will be connected, what font to use when drawing some text, etc). In order to avoid the need to supply hundreds of parameters to each drawing function, a graphical context structure is used. We set the various drawing options in this structure, and then we pass a pointer to this structure to any drawing routines. This is rather handy, as we often need to perform several drawing requests with the same options. Thus, we would initialize a graphical context, set the desired options, and pass this structure to all drawing functions.

    Note that graphic contexts have no client-side structure in XCB, they're just XIDs. Xlib has a client-side structure because it caches the GC contents so it can avoid making redundant requests, but of course XCB doesn't do that.

  4. Events

    A structure is used to pass events received from the X server. XCB supports exactly the events specified in the protocol (33 events). This structure contains the type of event received (including a bit for whether it came from the server or another client), as well as the data associated with the event (e.g. position on the screen where the event was generated, mouse button associated with the event, region of the screen associated with a "redraw" event, etc). The way to read the event's data depends on the event type.