X11 Extension tutorial

This tutorial briefly explains what is X11 extension and how to implement one. Extensibility is one of the requirements for a windowing system. X extension interface can be used to add new functionality to X server, it also requires additional functionality from the DDX (driver) itself.

Programming extensions
X extension code can be logically divided in 3 parts :
 * protocol definition
 * server side functional implementation
 * library offering API for client programs

(+ additions to driver code to use extension)

Extension file structure in Kdrive
Directory 'mi' contains the machine-independent library which is a set of common DDX routines. Initialization of server extensions is made by 'InitExtensions' function found at 'xserver_root/mi/miinitext.c'. This file has to be changed in order to initialize extension on server startup. Other files that have to be changed in order to build and link the extension with X server are 'xserver_root/Makefile.am' and 'xserver_root/configure.ac'.

Extension itself should be located in a directory of it's own, for example 'xserver_root/sample'. This directory should be added to 'xserver_root/Makefile.am' SUBDIRS section. Figure below shows an example of extension directory structure. The client interface can be compiled anywhere else. Client interface is usually put in it's own directory under libs directory, which contains all other libraries X uses.

 |        |_ Sampleext (protocol header) |        |_ Xsample (client interface)

 (Makefile.am, configure.ac) |          |_ mi (miinitext.c)           | |_ sample (extension files)

Protocol
Protocol consists of requests, events, replies and error messages. These are sent between client and server. Example : client sends a request for server using the library provided by extension, extension-code on serverside processes this request and performs required action + sends a reply to client.

The basic structure of request packet is predefined and every request must contain this 4-byte header information. After the header data, request can contain any amount of optional data limited only by the max packet size of the transport protocol.

typedef struct _xReq { CARD8 reqType;           /* major opcode of request, allocated by server on extension initialization          */ CARD8 data;              /* undefined, usually used as 'minor opcode' by the extension to differiate requests */ CARD16 length;           /* total length of request (including header) in 4 byte quantities                   */ ... } xReq;

The structure skeleton of a request packet. All request structures must be multiple of 4 bytes long.

typedef struct _xRep { CARD8 type;                 /* X_Reply                                */ CARD8 pad1;                 /* can be anything, used for padding here */ CARD16 sequenceNumber B16;  /* last sequence number                   */ CARD32 length B32;          /* length of the reply                    */

/* now the actual content ... */

CARD16 data B16;            /* data                                   */ CARD16 moredata B16;        /* some more data                         */

CARD32 pad2 B32;            /* padding just to have 32B               */ CARD32 pad3 B32; CARD32 pad4 B32; CARD32 pad5 B32; CARD32 pad6 B32;

} sample_data_reply;
 * 1) define sz_sample_data_reply 32

An example structure for reply packet returning data in form of 2 CARD16 variables. All reply packages must be at least 32 bytes. You also have to define a macro for returning size of the structure as 'sz_'. Server will use this macro to check package sizes (using macro REQUEST_SIZE_MATCH).

Every X extension has a Dispatch function, which works as a request-handler. When X-server receives a request package, it will check the major opcode. If major opcode belongs to some extension it calls extension's Dispatch routine. Dispatch checks the minor opcode of request and returns a pointer to appropriate function to call or BadRequest in error situation. If protocol requires a reply packet, function will send a reply packet.

Function wrapping
When adding and modifying functionality of normal DDX functions, extension has to wrap those functions. This means that extension will replace existing function with it's own version containing needed new functionality. X server calls all DDX functions through a function pointer and wrapping means switching of these pointers. Different extensions can wrap same functions, and depending on the order of extension initialization, they wrap each others functions. This will create a function chain. Wrapper function can call down to this function chain (it is recommended way, so that other extensions can do their work aswell unless you plan to replace larger part of the functionality but in this case you really have to know what you are doing).

to be continued ... will contain more examples etc. (please send comments to [mailto:tapani.palli@nokia.com] Tapani Palli)