Skip to content

X Events

In an X program, everything is driven by events. Event painting on the screen is sometimes done as a response to an event (an Expose event). If part of a program's window that was hidden, gets exposed (e.g. the window was raised above other widows), the X server will send an "expose" event to let the program know it should repaint that part of the window. User input (key presses, mouse movement, etc) is also received as a set of events.

  1. Registering for event types using event masks

    During the creation of a window, you should give it what kind of events it wishes to receive. Thus, you may register for various mouse (also called pointer) events, keyboard events, expose events, and so on. This is done for optimizing the server-to-client connection (i.e. why send a program (that might even be running at the other side of the globe) an event it is not interested in ?)

    In XCB, you use the "value_mask" and "value_list" data in the xcb_create_window() function to register for events. Here is how we register for Expose event when creating a window:

      mask = XCB_CW_EVENT_MASK;
      valwin[0] = XCB_EVENT_MASK_EXPOSURE;
      win = xcb_generate_id (c);
      xcb_create_window (c, depth, win, root->root,
                         0, 0, 150, 150, 10,
                         XCB_WINDOW_CLASS_INPUT_OUTPUT, root->root_visual,
                         mask, valwin);
    

    XCB_EVENT_MASK_EXPOSURE is a constant defined in the xcb_event_mask_t enumeration in the "xproto.h" header file. If we wanted to register for several event types, we can logically "or" them, as follows:

      mask = XCB_CW_EVENT_MASK;
      valwin[0] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS;
      win = xcb_generate_id (c);
      xcb_create_window (c, depth, win, root->root,
                         0, 0, 150, 150, 10,
                         XCB_WINDOW_CLASS_INPUT_OUTPUT, root->root_visual,
                         mask, valwin);
    

    This registers for Expose events as well as for mouse button presses inside the created window. You should note that a mask may represent several event sub-types.

    The values that a mask could take are given by the xcb_cw_t enumeration:

    typedef enum {
        XCB_CW_BACK_PIXMAP       = 1L<<0,
        XCB_CW_BACK_PIXEL        = 1L<<1,
        XCB_CW_BORDER_PIXMAP     = 1L<<2,
        XCB_CW_BORDER_PIXEL      = 1L<<3,
        XCB_CW_BIT_GRAVITY       = 1L<<4,
        XCB_CW_WIN_GRAVITY       = 1L<<5,
        XCB_CW_BACKING_STORE     = 1L<<6,
        XCB_CW_BACKING_PLANES    = 1L<<7,
        XCB_CW_BACKING_PIXEL     = 1L<<8,
        XCB_CW_OVERRIDE_REDIRECT = 1L<<9,
        XCB_CW_SAVE_UNDER        = 1L<<10,
        XCB_CW_EVENT_MASK        = 1L<<11,
        XCB_CW_DONT_PROPAGATE    = 1L<<12,
        XCB_CW_COLORMAP          = 1L<<13,
        XCB_CW_CURSOR            = 1L<<14
    } xcb_cw_t;
    

    Note: we must be careful when setting the values of the valwin parameter, as they have to follow the order the xcb_cw_t enumeration. Here is an example:

      mask = XCB_CW_EVENT_MASK | XCB_CW_BACK_PIXMAP;
      valwin[0] = XCB_NONE;                                              /* for XCB_CW_BACK_PIXMAP (whose value is 1)     */
      valwin[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS; /* for XCB_CW_EVENT_MASK, whose value (2048)     */
                                                                         /* is greater than the one of XCB_CW_BACK_PIXMAP */
    

    If the window has already been created, we can use the xcb_change_window_attributes() function to set the events that the window will receive. The subsection Configuring a window shows its prototype. As an example, here is a piece of code that configures the window to receive the Expose and ButtonPress events:

    const static uint32_t values[] = { XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS };
    
    /* The connection c and the window win are supposed to be defined */
    
    xcb_change_window_attributes (c, win, XCB_CW_EVENT_MASK, values);
    

    Note: A common bug programmers do is adding code to handle new event types in their program, while forgetting to add the masks for these events in the creation of the window. Such a programmer then should sit down for hours debugging his program, wondering "Why doesn't my program notice that I released the button?", only to find that they registered for button press events but not for button release events.

  2. Receiving events: writing the events loop

    After we have registered for the event types we are interested in, we need to enter a loop of receiving events and handling them. There are two ways to receive events: a blocking way and a non-blocking way:

    • xcb_wait_for_event (xcb_connection_t *c)
      xcb_wait_for_event (xcb_connection_t *c)
      is the blocking way. It waits (so blocks...) until an event is queued in the X server. Then it retrieves it into a newly allocated structure (it dequeues it from the queue) and returns it. This structure has to be freed. The function returns
      NULL
      NULL
      if an error occurs.
    • xcb_poll_for_event (xcb_connection_t *c, int
                *error)
      xcb_poll_for_event (xcb_connection_t *c, int
                *error)
      is the non-blocking way. It looks at the event queue and returns (and dequeues too) an existing event into a newly allocated structure. This structure has to be freed. It returns
      NULL
      NULL
      if there is no event. If an error occurs, the parameter
      error
      will be filled with the error status.

    There are various ways to write such a loop. We present two ways to write such a loop, with the two functions above. The first one uses xcb_wait_for_event_t, which is similar to an event Xlib loop using only XNextEvent:

      xcb_generic_event_t *e;
    
      while ((e = xcb_wait_for_event (c))) {
        switch (e->response_type & ~0x80) {
        case XCB_EXPOSE: {
          /* Handle the Expose event type */
          xcb_expose_event_t *ev = (xcb_expose_event_t *)e;
    
          /* ... */
    
          break;
        }
        case XCB_BUTTON_PRESS: {
          /* Handle the ButtonPress event type */
          xcb_button_press_event_t *ev = (xcb_button_press_event_t *)e;
    
          /* ... */
    
          break;
        }
        default: {
          /* Unknown event type, ignore it */
          break;
        }
        }
        /* Free the Generic Event */
        free (e);
      }
      xcb_generic_event_t *e;
    
      while ((e = xcb_wait_for_event (c))) {
        switch (e->response_type & ~0x80) {
        case XCB_EXPOSE: {
          /* Handle the Expose event type */
          xcb_expose_event_t *ev = (xcb_expose_event_t *)e;
    
          /* ... */
    
          break;
        }
        case XCB_BUTTON_PRESS: {
          /* Handle the ButtonPress event type */
          xcb_button_press_event_t *ev = (xcb_button_press_event_t *)e;
    
          /* ... */
    
          break;
        }
        default: {
          /* Unknown event type, ignore it */
          break;
        }
        }
        /* Free the Generic Event */
        free (e);
      }

    You will certainly want to use

    xcb_poll_for_event(xcb_connection_t *c, int
            *error)
    xcb_poll_for_event(xcb_connection_t *c, int
            *error)
    if, in Xlib, you use XPending or XCheckMaskEvent:

      while (XPending (display)) {
        XEvent ev;
    
        XNextEvent(d, &ev);
    
        /* Manage your event */
      }
      while (XPending (display)) {
        XEvent ev;
    
        XNextEvent(d, &ev);
    
        /* Manage your event */
      }

    Such a loop in XCB looks like:

      xcb_generic_event_t *ev;
    
      while ((ev = xcb_poll_for_event (conn, 0))) {
        /* Manage your event */
      }
      xcb_generic_event_t *ev;
    
      while ((ev = xcb_poll_for_event (conn, 0))) {
        /* Manage your event */
      }

    The events are managed in the same way as with xcb_wait_for_event_t. Obviously, we will need to give the user some way of terminating the program. This is usually done by handling a special "quit" event, as we will soon see.

    Comparison Xlib/XCB
    • XNextEvent ()
    • xcb_wait_for_event ()
    • XPending ()
    • XCheckMaskEvent ()
    • xcb_poll_for_event ()

  3. Expose events

    The Expose event is one of the most basic (and most used) events an application may receive. It will be sent to us in one of several cases:

    • A window that covered part of our window has moved away, exposing part (or all) of our window.
    • Our window was raised above other windows.
    • Our window mapped for the first time.
    • Our window was de-iconified.

    You should note the implicit assumption hidden here: the contents of our window is lost when it is being obscured (covered) by either windows. One may wonder why the X server does not save this contents. The answer is: to save memory. After all, the number of windows on a display at a given time may be very large, and storing the contents of all of them might require a lot of memory. Actually, there is a way to tell the X server to store the contents of a window in special cases, as we will see later.

    When we get an Expose event, we should take the event's data from the members of the following structure:

    typedef struct {
        uint8_t      response_type; /* The type of the event, here it is XCB_EXPOSE */
        uint8_t      pad0;
        uint16_t     sequence;
        xcb_window_t window;        /* The Id of the window that receives the event (in case */
                                    /* our application registered for events on several windows */
        uint16_t     x;             /* The x coordinate of the top-left part of the window that needs to be redrawn */
        uint16_t     y;             /* The y coordinate of the top-left part of the window that needs to be redrawn */
        uint16_t     width;         /* The width of the part of the window that needs to be redrawn */
        uint16_t     height;        /* The height of the part of the window that needs to be redrawn */
        uint16_t     count;
    } xcb_expose_event_t;
    typedef struct {
        uint8_t      response_type; /* The type of the event, here it is XCB_EXPOSE */
        uint8_t      pad0;
        uint16_t     sequence;
        xcb_window_t window;        /* The Id of the window that receives the event (in case */
                                    /* our application registered for events on several windows */
        uint16_t     x;             /* The x coordinate of the top-left part of the window that needs to be redrawn */
        uint16_t     y;             /* The y coordinate of the top-left part of the window that needs to be redrawn */
        uint16_t     width;         /* The width of the part of the window that needs to be redrawn */
        uint16_t     height;        /* The height of the part of the window that needs to be redrawn */
        uint16_t     count;
    } xcb_expose_event_t;
  4. Getting user input

    User input traditionally comes from two sources: the mouse and the keyboard. Various event types exist to notify us of user input (a key being presses on the keyboard, a key being released on the keyboard, the mouse moving over our window, the mouse entering (or leaving) our window, and so on.

    1. Mouse button press and release events

      The first event type we will deal with is a mouse button-press (or button-release) event in our window. In order to register to such an event type, we should add one (or more) of the following masks when we create our window:

      • XCB_EVENT_MASK_BUTTON_PRESS
        : notify us of any button that was pressed in one of our windows.
      • XCB_EVENT_MASK_BUTTON_RELEASE
        : notify us of any button that was released in one of our windows.

      The structure to be checked for in our events loop is the same for these two events, and is the following:

      typedef struct {
          uint8_t         response_type; /* The type of the event, here it is xcb_button_press_event_t or xcb_button_release_event_t */
          xcb_button_t    detail;
          uint16_t        sequence;
          xcb_timestamp_t time;          /* Time, in milliseconds the event took place in */
          xcb_window_t    root;
          xcb_window_t    event;
          xcb_window_t    child;
          int16_t         root_x;
          int16_t         root_y;
          int16_t         event_x;       /* The x coordinate where the mouse has been pressed in the window */
          int16_t         event_y;       /* The y coordinate where the mouse has been pressed in the window */
          uint16_t        state;         /* A mask of the buttons (or keys) during the event */
          uint8_t         same_screen;
      } xcb_button_press_event_t;
      
      typedef xcb_button_press_event_t xcb_button_release_event_t;
      typedef struct {
          uint8_t         response_type; /* The type of the event, here it is xcb_button_press_event_t or xcb_button_release_event_t */
          xcb_button_t    detail;
          uint16_t        sequence;
          xcb_timestamp_t time;          /* Time, in milliseconds the event took place in */
          xcb_window_t    root;
          xcb_window_t    event;
          xcb_window_t    child;
          int16_t         root_x;
          int16_t         root_y;
          int16_t         event_x;       /* The x coordinate where the mouse has been pressed in the window */
          int16_t         event_y;       /* The y coordinate where the mouse has been pressed in the window */
          uint16_t        state;         /* A mask of the buttons (or keys) during the event */
          uint8_t         same_screen;
      } xcb_button_press_event_t;
      
      typedef xcb_button_press_event_t xcb_button_release_event_t;

      The time field may be used to calculate "double-click" situations by an application (e.g. if the mouse button was clicked two times in a duration shorter than a given amount of time, assume this was a double click).

      The state field is a mask of the buttons held down during the event. It is a bitwise OR of any of the following (from the xcb_button_mask_t and xcb_mod_mask_t enumerations):

      • XCB_BUTTON_MASK_1
      • XCB_BUTTON_MASK_2
      • XCB_BUTTON_MASK_3
      • XCB_BUTTON_MASK_4
      • XCB_BUTTON_MASK_5
      • XCB_MOD_MASK_SHIFT
      • XCB_MOD_MASK_LOCK
      • XCB_MOD_MASK_CONTROL
      • XCB_MOD_MASK_1
      • XCB_MOD_MASK_2
      • XCB_MOD_MASK_3
      • XCB_MOD_MASK_4
      • XCB_MOD_MASK_5

      Their names are self explanatory, where the first 5 refer to the mouse buttons that are being pressed, while the rest refer to various "special keys" that are being pressed (Mod1 is usually the 'Alt' key or the 'Meta' key).

      TODO: Problem: it seems that the state does not change when clicking with various buttons.

    2. Mouse movement events

      Similar to mouse button press and release events, we also can be notified of various mouse movement events. These can be split into two families. One is of mouse pointer movement while no buttons are pressed, and the second is a mouse pointer motion while one (or more) of the buttons are pressed (this is sometimes called "a mouse drag operation", or just "dragging"). The following event masks may be added during the creation of our window:

      • XCB_EVENT_MASK_POINTER_MOTION
        : events of the pointer moving in one of the windows controlled by our application, while no mouse button is held pressed.
      • XCB_EVENT_MASK_BUTTON_MOTION
        : Events of the pointer moving while one or more of the mouse buttons is held pressed.
      • XCB_EVENT_MASK_BUTTON_1_MOTION
        : same as
        XCB_EVENT_MASK_BUTTON_MOTION
        , but only when the 1st mouse button is held pressed.
      • XCB_EVENT_MASK_BUTTON_2_MOTION
        ,
        XCB_EVENT_MASK_BUTTON_3_MOTION
        ,
        XCB_EVENT_MASK_BUTTON_4_MOTION
        ,
        XCB_EVENT_MASK_BUTTON_5_MOTION
        : same as
        XCB_EVENT_MASK_BUTTON_1_MOTION
        , but respectively for 2nd, 3rd, 4th and 5th mouse button.

      The structure to be checked for in our events loop is the same for these events, and is the following:

      typedef struct {
          uint8_t         response_type; /* The type of the event */
          uint8_t         detail;
          uint16_t        sequence;
          xcb_timestamp_t time;          /* Time, in milliseconds the event took place in */
          xcb_window_t    root;
          xcb_window_t    event;
          xcb_window_t    child;
          int16_t         root_x;
          int16_t         root_y;
          int16_t         event_x;       /* The x coordinate of the mouse when the  event was generated */
          int16_t         event_y;       /* The y coordinate of the mouse when the  event was generated */
          uint16_t        state;         /* A mask of the buttons (or keys) during the event */
          uint8_t         same_screen;
      } xcb_motion_notify_event_t;
      typedef struct {
          uint8_t         response_type; /* The type of the event */
          uint8_t         detail;
          uint16_t        sequence;
          xcb_timestamp_t time;          /* Time, in milliseconds the event took place in */
          xcb_window_t    root;
          xcb_window_t    event;
          xcb_window_t    child;
          int16_t         root_x;
          int16_t         root_y;
          int16_t         event_x;       /* The x coordinate of the mouse when the  event was generated */
          int16_t         event_y;       /* The y coordinate of the mouse when the  event was generated */
          uint16_t        state;         /* A mask of the buttons (or keys) during the event */
          uint8_t         same_screen;
      } xcb_motion_notify_event_t;
    3. Mouse pointer enter and leave events

      Another type of event that applications might be interested in, is a mouse pointer entering a window the program controls, or leaving such a window. Some programs use these events to show the user that the application is now in focus. In order to register for such an event type, we should add one (or more) of the following masks when we create our window:

      • xcb_event_enter_window_t
        : notify us when the mouse pointer enters any of our controlled windows.
      • xcb_event_leave_window_t
        : notify us when the mouse pointer leaves any of our controlled windows.

      The structure to be checked for in our events loop is the same for these two events, and is the following:

      typedef struct {
          uint8_t         response_type; /* The type of the event */
          uint8_t         detail;
          uint16_t        sequence;
          xcb_timestamp_t time;          /* Time, in milliseconds the event took place in */
          xcb_window_t    root;
          xcb_window_t    event;
          xcb_window_t    child;
          int16_t         root_x;
          int16_t         root_y;
          int16_t         event_x;       /* The x coordinate of the mouse when the  event was generated */
          int16_t         event_y;       /* The y coordinate of the mouse when the  event was generated */
          uint16_t        state;         /* A mask of the buttons (or keys) during the event */
          uint8_t         mode;          /* The number of mouse button that was clicked */
          uint8_t         same_screen_focus;
      } xcb_enter_notify_event_t;
      
      typedef xcb_enter_notify_event_t xcb_leave_notify_event_t;
      typedef struct {
          uint8_t         response_type; /* The type of the event */
          uint8_t         detail;
          uint16_t        sequence;
          xcb_timestamp_t time;          /* Time, in milliseconds the event took place in */
          xcb_window_t    root;
          xcb_window_t    event;
          xcb_window_t    child;
          int16_t         root_x;
          int16_t         root_y;
          int16_t         event_x;       /* The x coordinate of the mouse when the  event was generated */
          int16_t         event_y;       /* The y coordinate of the mouse when the  event was generated */
          uint16_t        state;         /* A mask of the buttons (or keys) during the event */
          uint8_t         mode;          /* The number of mouse button that was clicked */
          uint8_t         same_screen_focus;
      } xcb_enter_notify_event_t;
      
      typedef xcb_enter_notify_event_t xcb_leave_notify_event_t;
    4. The keyboard focus

      There may be many windows on a screen, but only a single keyboard attached to them. How does the X server then know which window should be sent a given keyboard input ? This is done using the keyboard focus. Only a single window on the screen may have the keyboard focus at a given time. There is a XCB function that allows a program to set the keyboard focus to a given window. The user can usually set the keyboard focus using the window manager (often by clicking on the title bar of the desired window). Once our window has the keyboard focus, every key press or key release will cause an event to be sent to our program (if it regsitered for these event types...).

    5. Keyboard press and release events

      If a window controlled by our program currently holds the keyboard focus, it can receive key press and key release events. So, we should add one (or more) of the following masks when we create our window:

      • XCB_EVENT_MASK_KEY_PRESS
        : notify us when a key was pressed while any of our controlled windows had the keyboard focus.
      • XCB_EVENT_MASK_KEY_RELEASE
        : notify us when a key was released while any of our controlled windows had the keyboard focus.

      The structure to be checked for in our events loop is the same for these two events, and is the following:

      typedef struct {
          uint8_t         response_type; /* The type of the event */
          xcb_keycode_t   detail;
          uint16_t        sequence;
          xcb_timestamp_t time;          /* Time, in milliseconds the event took place in */
          xcb_window_t    root;
          xcb_window_t    event;
          xcb_window_t    child;
          int16_t         root_x;
          int16_t         root_y;
          int16_t         event_x;
          int16_t         event_y;
          uint16_t        state;
          uint8_t         same_screen;
      } xcb_key_press_event_t;
      
      typedef xcb_key_press_event_t xcb_key_release_event_t;
      typedef struct {
          uint8_t         response_type; /* The type of the event */
          xcb_keycode_t   detail;
          uint16_t        sequence;
          xcb_timestamp_t time;          /* Time, in milliseconds the event took place in */
          xcb_window_t    root;
          xcb_window_t    event;
          xcb_window_t    child;
          int16_t         root_x;
          int16_t         root_y;
          int16_t         event_x;
          int16_t         event_y;
          uint16_t        state;
          uint8_t         same_screen;
      } xcb_key_press_event_t;
      
      typedef xcb_key_press_event_t xcb_key_release_event_t;

      The detail field refers to the physical key on the keyboard.

      TODO: Talk about getting the ASCII code from the key code.

  5. X events: a complete example

    As an example for handling events, we show a program that creates a window, enters an events loop and checks for all the events described above, and writes on the terminal the relevant characteristics of the event. With this code, it should be easy to add drawing operations, like those which have been described above.

    #include <stdlib.h>
    #include <stdio.h>
    
    #include <xcb/xcb.h>
    
    void
    print_modifiers (uint32_t mask)
    {
      const char **mod, *mods[] = {
        "Shift", "Lock", "Ctrl", "Alt",
        "Mod2", "Mod3", "Mod4", "Mod5",
        "Button1", "Button2", "Button3", "Button4", "Button5"
      };
      printf ("Modifier mask: ");
      for (mod = mods ; mask; mask >>= 1, mod++)
        if (mask & 1)
          printf(*mod);
      putchar ('\n');
    }
    
    int
    main ()
    {
      xcb_connection_t    *c;
      xcb_screen_t        *screen;
      xcb_window_t         win;
      xcb_generic_event_t *e;
      uint32_t             mask = 0;
      uint32_t             values[2];
    
      /* 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;
    
      /* 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_EVENT_MASK_BUTTON_PRESS   |
                  XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION |
                  XCB_EVENT_MASK_ENTER_WINDOW   | XCB_EVENT_MASK_LEAVE_WINDOW   |
                  XCB_EVENT_MASK_KEY_PRESS      | XCB_EVENT_MASK_KEY_RELEASE;
      xcb_create_window (c,                             /* Connection          */
                         0,                             /* 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);
    
      xcb_flush (c);
    
      while ((e = xcb_wait_for_event (c))) {
        switch (e->response_type & ~0x80) {
        case XCB_EXPOSE: {
          xcb_expose_event_t *ev = (xcb_expose_event_t *)e;
    
          printf ("Window %ld exposed. Region to be redrawn at location (%d,%d), with dimension (%d,%d)\n",
                  ev->window, ev->x, ev->y, ev->width, ev->height);
          break;
        }
        case XCB_BUTTON_PRESS: {
          xcb_button_press_event_t *ev = (xcb_button_press_event_t *)e;
          print_modifiers(ev->state);
    
          switch (ev->detail) {
          case 4:
            printf ("Wheel Button up in window %ld, at coordinates (%d,%d)\n",
                    ev->event, ev->event_x, ev->event_y);
            break;
          case 5:
            printf ("Wheel Button down in window %ld, at coordinates (%d,%d)\n",
                    ev->event, ev->event_x, ev->event_y);
            break;
          default:
            printf ("Button %d pressed in window %ld, at coordinates (%d,%d)\n",
                    ev->detail, ev->event, ev->event_x, ev->event_y);
          }
          break;
        }
        case XCB_BUTTON_RELEASE: {
          xcb_button_release_event_t *ev = (xcb_button_release_event_t *)e;
          print_modifiers(ev->state);
    
          printf ("Button %d released in window %ld, at coordinates (%d,%d)\n",
                  ev->detail, ev->event, ev->event_x, ev->event_y);
          break;
        }
        case XCB_MOTION_NOTIFY: {
          xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)e;
    
          printf ("Mouse moved in window %ld, at coordinates (%d,%d)\n",
                  ev->event, ev->event_x, ev->event_y);
          break;
        }
        case XCB_ENTER_NOTIFY: {
          xcb_enter_notify_event_t *ev = (xcb_enter_notify_event_t *)e;
    
          printf ("Mouse entered window %ld, at coordinates (%d,%d)\n",
                  ev->event, ev->event_x, ev->event_y);
          break;
        }
        case XCB_LEAVE_NOTIFY: {
          xcb_leave_notify_event_t *ev = (xcb_leave_notify_event_t *)e;
    
          printf ("Mouse left window %ld, at coordinates (%d,%d)\n",
                  ev->event, ev->event_x, ev->event_y);
          break;
        }
        case XCB_KEY_PRESS: {
          xcb_key_press_event_t *ev = (xcb_key_press_event_t *)e;
          print_modifiers(ev->state);
    
          printf ("Key pressed in window %ld\n",
                  ev->event);
          break;
        }
        case XCB_KEY_RELEASE: {
          xcb_key_release_event_t *ev = (xcb_key_release_event_t *)e;
          print_modifiers(ev->state);
    
          printf ("Key released in window %ld\n",
                  ev->event);
          break;
        }
        default:
          /* Unknown event type, ignore it */
          printf("Unknown event: %d\n", e->response_type);
          break;
        }
        /* Free the Generic Event */
        free (e);
      }
    
      return 0;
    }
    #include <stdlib.h>
    #include <stdio.h>
    
    #include <xcb/xcb.h>
    
    void
    print_modifiers (uint32_t mask)
    {
      const char **mod, *mods[] = {
        "Shift", "Lock", "Ctrl", "Alt",
        "Mod2", "Mod3", "Mod4", "Mod5",
        "Button1", "Button2", "Button3", "Button4", "Button5"
      };
      printf ("Modifier mask: ");
      for (mod = mods ; mask; mask >>= 1, mod++)
        if (mask & 1)
          printf(*mod);
      putchar ('\n');
    }
    
    int
    main ()
    {
      xcb_connection_t    *c;
      xcb_screen_t        *screen;
      xcb_window_t         win;
      xcb_generic_event_t *e;
      uint32_t             mask = 0;
      uint32_t             values[2];
    
      /* 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;
    
      /* 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_EVENT_MASK_BUTTON_PRESS   |
                  XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION |
                  XCB_EVENT_MASK_ENTER_WINDOW   | XCB_EVENT_MASK_LEAVE_WINDOW   |
                  XCB_EVENT_MASK_KEY_PRESS      | XCB_EVENT_MASK_KEY_RELEASE;
      xcb_create_window (c,                             /* Connection          */
                         0,                             /* 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);
    
      xcb_flush (c);
    
      while ((e = xcb_wait_for_event (c))) {
        switch (e->response_type & ~0x80) {
        case XCB_EXPOSE: {
          xcb_expose_event_t *ev = (xcb_expose_event_t *)e;
    
          printf ("Window %ld exposed. Region to be redrawn at location (%d,%d), with dimension (%d,%d)\n",
                  ev->window, ev->x, ev->y, ev->width, ev->height);
          break;
        }
        case XCB_BUTTON_PRESS: {
          xcb_button_press_event_t *ev = (xcb_button_press_event_t *)e;
          print_modifiers(ev->state);
    
          switch (ev->detail) {
          case 4:
            printf ("Wheel Button up in window %ld, at coordinates (%d,%d)\n",
                    ev->event, ev->event_x, ev->event_y);
            break;
          case 5:
            printf ("Wheel Button down in window %ld, at coordinates (%d,%d)\n",
                    ev->event, ev->event_x, ev->event_y);
            break;
          default:
            printf ("Button %d pressed in window %ld, at coordinates (%d,%d)\n",
                    ev->detail, ev->event, ev->event_x, ev->event_y);
          }
          break;
        }
        case XCB_BUTTON_RELEASE: {
          xcb_button_release_event_t *ev = (xcb_button_release_event_t *)e;
          print_modifiers(ev->state);
    
          printf ("Button %d released in window %ld, at coordinates (%d,%d)\n",
                  ev->detail, ev->event, ev->event_x, ev->event_y);
          break;
        }
        case XCB_MOTION_NOTIFY: {
          xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)e;
    
          printf ("Mouse moved in window %ld, at coordinates (%d,%d)\n",
                  ev->event, ev->event_x, ev->event_y);
          break;
        }
        case XCB_ENTER_NOTIFY: {
          xcb_enter_notify_event_t *ev = (xcb_enter_notify_event_t *)e;
    
          printf ("Mouse entered window %ld, at coordinates (%d,%d)\n",
                  ev->event, ev->event_x, ev->event_y);
          break;
        }
        case XCB_LEAVE_NOTIFY: {
          xcb_leave_notify_event_t *ev = (xcb_leave_notify_event_t *)e;
    
          printf ("Mouse left window %ld, at coordinates (%d,%d)\n",
                  ev->event, ev->event_x, ev->event_y);
          break;
        }
        case XCB_KEY_PRESS: {
          xcb_key_press_event_t *ev = (xcb_key_press_event_t *)e;
          print_modifiers(ev->state);
    
          printf ("Key pressed in window %ld\n",
                  ev->event);
          break;
        }
        case XCB_KEY_RELEASE: {
          xcb_key_release_event_t *ev = (xcb_key_release_event_t *)e;
          print_modifiers(ev->state);
    
          printf ("Key released in window %ld\n",
                  ev->event);
          break;
        }
        default:
          /* Unknown event type, ignore it */
          printf("Unknown event: %d\n", e->response_type);
          break;
        }
        /* Free the Generic Event */
        free (e);
      }
    
      return 0;
    }