The X New Developer’s Guide: Using Extensions

Using Extensions

Alan Coopersmith

As described in the Communication Between Client and Server chapter, the X Window System has evolved over time via the X11 protocol's extension mechanism, which allows defining new protocol requests, events and errors for communication between clients and servers.

Clients who wish to use an extension must first check if the server supports the extension, and if so, which version of the extension the server supports. Some extensions provide a simple version negotiation mechanism where the client sends the server the protocol versions the client understands, and the server responds with the best version it can support in that range. Many extensions will not work properly if this version checking handshake is not performed, as key data structures in either the client libraries or server will not be initialized, and extensions with multiple versions may use the wrong version to communicate, causing parsing errors on either end.

Example: Using an extension from an xcb-based client

This example comes from the xwininfo client from X.Org a simple client that retrieves information about windows on the screen and prints them. If the server supports the SHAPE extension, it can print information about the shape of non-rectangular windows, such as the round eyeball windows of xeyes.

In the configure.ac script that is used with GNU autoconf to determine how to build xwininfo on a given platform, xwininfo declares that it requires the xcb library for the shape extension to build, and will not build without it. It also requires at least version 1.6 of the xcb library itself.

# Obtain compiler/linker options for xwininfo dependencies
PKG_CHECK_MODULES(XWININFO, [xcb >= 1.6] xcb-shape)

Some clients make extensions optional at build time, allowing builders to choose to omit support for them, and using mechanisms such as #ifdef statements to isolate the calls to them. This guide does not include an example of that.

The xwininfo program code then initializes the extension and checks the version before using it. The shape extension has had two versions over its lifetime, 1.0 and 1.1. The 1.1 version features simple additions to the 1.0 version that do not break the existing functions or protocol, nor the clients using it.

#include <xcb/shape.h>
[...]
static void
Display_Window_Shape (xcb_window_t window)
{
    xcb_query_extension_reply_t *shape_query;
        xcb_shape_query_extents_cookie_t extents_cookie;
        xcb_shape_query_extents_reply_t *extents;

        shape_query = xcb_get_extension_data (dpy, &xcb_shape_id);
        if (!shape_query->present)
        return;

    extents_cookie = xcb_shape_query_extents (dpy, window);
        extents = xcb_shape_query_extents_reply (dpy, extents_cookie, &err);
[...]

Since the shape window usage is isolated to this single function, it can simply return if the extension is not supported, otherwise it can continue on to make requests using the functions provided by the xcb-shape library to encapsulate the SHAPE extension protocol requests and replies.

Example: Using an extension from an Xlib-based client

This example comes from the xdpyinfo client from X.Org, a simple client that retrieves and prints information about the X server and the display. It prints a list of the extensions present, and if passed certain extension names via the -ext flag, prints more information about the given extension. One of the extensions it can do this for is the Xinerama extension to get information about how many monitors actually are displaying portions of a single logical screen, and which portion of the screen each monitor is displaying.

In the configure.ac script that is used with GNU autoconf to determine how to build xdpyinfo on a given platform, xdpyinfo gives builders a command line flag to enable or disable the build of xinerama support, and if enabled, checks for the required headers & library for libXinerama via pkg-config:

AC_ARG_WITH(xinerama, AS_HELP_STRING([--without-xinerama],[Disable xinerama support.]),
    [USE_XINERAMA="$withval"], [USE_XINERAMA="yes"])
if test "x$USE_XINERAMA" != "xno" ; then
    PKG_CHECK_MODULES(DPY_XINERAMA, xinerama)
else
    echo "without xinerama"
fi

In the code, the Xinerama support is #ifdef'ed so that it's only compiled when the extension library was found by the configure script. The code uses "PANORAMIX" for this, since that was the original proposed name for Xinerama, and the code didn't get updated when the extension name was finalized. As required, the first thing the client does is query for the presence of the extension and it's version, and then if it is available in a compatible version, goes on to make it's calls to the extension itself.

#ifdef PANORAMIX
#include <X11/extensions/Xinerama.h>
#endif
[...]
#ifdef PANORAMIX

static int
print_xinerama_info(Display *dpy, const char *extname)
{
  int              majorrev, minorrev;

  if (!XineramaQueryVersion (dpy, &majorrev, &minorrev))
    return 0;

  print_standard_extension_info(dpy, extname, majorrev, minorrev);

  if (!XineramaIsActive(dpy)) {
    printf("  Xinerama is inactive.\n");
  } else {
     int i, count = 0;
         XineramaScreenInfo *xineramaScreens = 
            XineramaQueryScreens(dpy, &count);
    [...]