Xcb
Introduction
Porting Xlib calls to xcb is fairly straightfoward, since most of the time it's a matter of replacing a call to an Xlib function with a call to the equivalent xcb function. This page mainly deals with exceptions to that rule, and things that might not be obvious even if you are familiar with Xlib.
Porting can be done gradually, since you can mix xcb and Xlib calls. The only exception is event processing. The event queue is managed either by xcb or Xlib, not both at the same time.
Macros
RootWindow(), DisplayWidth(), DisplayHeight(), DefaultDepth() etc.
- Xlib version
Window root = RootWindow(dpy, screen);
int depth = DefaultDepth(dpy, screen);
int width = DisplayWidth(dpy, screen);
int height = DisplayHeight(dpy, screen);
- xcb equivalent
xcb_screen_t *screen = xcb_aux_get_screen(c, screen);
xcb_window_t root = screen->root;
int depth = screen->root_depth;
int width = screen->width;
int height = screen->height;
If you need a pointer to the xcb_screen_t for the default screen, call QPlatformNativeInterface::nativeResourceForWindow("screen", 0)) and cast the returned pointer to xcb_screen_t *.
DefaultRootWindow(), DefaultVisual(), DefaultDepth(), DefaultColormap()
See the previous section and the comment about obtaining a pointer to the xcb_screen_t for the default screen.
Functions
XSelectInput()
XSelectInput() is a convenience function that uses the ChangeWindowAttributes request to change the event mask for a window. The xcb equivalent of XSelectInput() is thus xcb_change_window_attributes().
// Xlib version
XSelectInput(dpy, window, PropertyChangeMask);
// xcb version
uint32_t mask = XCB_EVENT_MASK_PROPERTY_CHANGE;
xcb_change_window_attributes(c, window, XCB_CW_EVENT_MASK, &mask);
Note that this applies only to XSelectInput(), not other input selection functions added by extensions.
XSync()
The xcb equivalent of XSync() is xcb_aux_sync(), which is in xcb-utils.
The reason you won't find a sync function in libxcb is that there is no sync request in the X protocol. Calling XSync() or xcb_aux_sync() is equivalent to calling XGetInputFocus() and throwing away the reply.
XMoveWindow(), XResizeWindow(), XMoveResizeWindow(), XSetWindowBorderWidth(), XRaiseWindow(), XLowerWindow(), XRestackWindows()
These functions are Xlib convenience functions that translate to the same X request; ConfigureWindow. The xcb equivalent is thus xcb_configure_window().
- XMoveWindow() example
// Xlib version
XMoveWindow(dpy, window, x, y);
// xcb version
uint32_t values[] = { x, y };
xcb_configure_window(c, window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values);
- XRaiseWindow() example
// Xlib version
XRaiseWindow(dpy, window);
// xcb version
uint32_t value = XCB_STACK_MODE_ABOVE;
xcb_configure_window(c, window, XCB_CONFIG_STACK_MODE, &value);
Multiple calls to these functions can be combined into single call to xcb_configure_window():
// Xlib version
XMoveWindow(dpy, window, x, y);
XResizeWindow(dpy, window, width, height);
XRaiseWindow(dpy, window);
// xcb version
uint32_t values[] = { x, y, width, height, XCB_STACK_MODE_ABOVE };
xcb_configure_window(c, window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_STACK_MODE,
values);
Note that the order of the values in the values array must match the order in the xcb_config_window_t enum.
XGetWindowAttributes()
The xcb equivalent of this function is xcb_get_window_attributes().
However you may have noticed that xcb_get_window_attributes() doesn't give you all the information returned by XGetWindowAttributes(). More specifically, it doesn't return the position, size, border width and depth of the window. This is the reason why this function gets a special mention in this section.
It's not a bug that xcb_get_window_attributes() doesn't return the same information. It's because XGetWindowAttributes() actually generates and returns the replies from two requests; GetWindowAttributes and GetGeometry.
So if you need the geometry in addition to the other window attributes, you have to call both xcb_get_window_attributes() and xcb_get_geometry().
Creating Resources
In this example we're going to look at how to create a pixmap in Xlib and in xcb, and look at the differences.
// Xlib version
Pixmap pixmap = XCreatePixmap(dpy, root, width, height, depth);
// xcb version
xcb_pixmap_t pixmap = xcb_generate_id();
xcb_create_pixmap(c, depth, pixmap, root, width, height);
XCreatePixmap() creates a pixmap with the specified width, height and depth, and returns a handle to the newly created pixmap.
In the xcb version we call xcb_generate_id() to create the pixmap handle, and then we pass the handle to the xcb_create_pixmap() function.
When a client connects to the X server it is given a range of unique global ID's that can be assigned to new resources. XCreatePixmap() picks the first unused ID in this range, and tells the X server to assign that ID to the pixmap when it creates it. After sending the request, XCreatePixmap() returns the ID to the caller.
Unlike Xlib, xcb doesn't hide these details from us. Whenever you create a new resource, you start by calling xcb_generate_id() to get an ID that can be assigned to the resource. The ID is always passed as a parameter to the function that creates the resource.
The advantage of this design is that creating resources is asynchronous, since the client doesn't have to wait for the X server to send back the ID after creating the resource. The disadvantage is that if resource creation fails for any reason, the ID will never become valid and we won't find out about it until later.
Another example that creates a region:
xcb_rectangle_t rect = { 0, 0, 100, 100 };
xcb_xfixes_region_t region = xcb_generate_id();
xcb_xfixes_create_region(c, region, 1, &rect);
KXErrorHandler
The KXErrorHandler class is used when you need to check if an asychronous Xlib call succeeded or generated an an error. The following example shows how it is typically used.
KXerrorHandler handler;
XMapWindow(dpy, window);
if (handler.error(true)) {
kDebug() << "XMapWindow() failed!";
}
Unfortunately KXErrorHandler cannot be ported to xcb. The good news however is that with xcb you don't need KXErrorHandler to do this.
You may have noticed that xcb functions that don't generate a reply come in two versions; a regular version and a version with _checked() appended to the name:
xcb_void_cookie_t xcb_map_window(xcb_connection_t *c, xcb_window_t window);
xcb_void_cookie_t xcb_map_window_checked(xcb_connection_t *c, xcb_window_t window);
The xcb_void_cookie_t returned by the _checked() version of the function can be used to check if the function succeeded or not by passing the cookie to xcb_request_check().
The following example shows how this works:
xcb_void_cookie_t cookie = xcb_map_window_checked(c, window);
xcb_generic_error_t *error = xcb_request_check(c, cookie);
if (error) {
kDebug() << "xcb_map_window() failed!";
free(error);
}
Like KXErrorHandler, xcb_request_check() needs to synchronize with the X server to know that the X server has processed the request(s). For this reason you should only use xcb_request_check() when it is necessary to know that a request succeeded before proceeding with other calls.