Because AIXwindows can evolve by extensions to the core protocol, it is important that extensions not be perceived as second-class citizens.
To avoid initializing extensions explicitly in application programs, it is important that extensions perform "lazy evaluations" and automatically initialize themselves when called the first time.
The following sections describe techniques for writing extensions to the Xlib library that have the same performance rate as Core protocol requests.
Note: Because an AIXwindows extension is expected to consist of multiple requests, defining 10 new features as 10 separate extensions is not a good practice. Rather, package new features into a single extension and use minor opcodes to distinguish between the features.
The following extension protocols are included in this chapter:
The following are extensions function topics:
The basic protocol requests for extensions are the XQueryExtension extension function, the XListExtensions extension function, and the XFreeExtensionList extension function.
Hooking routines sink a connecting hook into the library. These routines normally are not used by application programmers but, instead, by programmers who need to extend the Core AIXwindows protocol and the AIXwindows library interface. Hooking routines, which generate protocol requests for AIXwindows, are called stubs.
In extensions, stubs first check to see if they have initialized themselves on a connection. If the stubs have not been initialized, they should call the XInitExtension function.
The wire-formatted structure XEvent is in the <X11/Xproto.h> header file, and the host-formatted structure XEvent is in the <X11/Xlib.h> header file.
The XExtCodes data structure returns the information from the XQueryExtension extension function and is defined in the <X11/Xlib.h> header file.
The XInitExtension function calls the XQueryExtension function to see if the extension exists. Then, it allocates storage for maintaining the information about the extension on the connection. It chains this to the extension list for the connection, and returns the information that the stub implementor needs to access the extension. If the extension does not exist, the XInitExtension function returns NULL.
The extension number returned in the XExtCodes data structure is used in the other calls that follow. This extension number is unique to a single connection only.
XExtCodes *XAddExtension(DisplayPtr) Display *DisplayPtr;
For local Xlib library extensions, the XAddExtension function allocates the XExtCodes data structure, bumps the extension number count, and chains the extension onto the extension list. (This allows extensions access to the Xlib library without requiring server extensions.)
The types of functions and associated functions that hook into the AIXwindows library include the following:
Use these routines to define procedures to be called under certain circumstances. All these routines return the previous routine defined for this extension.
Various Xlib library data structures have provisions for extension functions to chain extension-supplied data onto a list. Because the list pointer is always the first member of the structure, a single set of functions can be used to manipulate the data in these lists.
The XExtData data structure is used in these functions, and is defined in the <X11/Xlib.h> header file.
GCs are cached by the library so that independent change requests can be merged into a single protocol request. This cache is called a write back cache. Any extension function whose behavior depends on the contents of a GC must erase the GC cache to make sure the server has up-to-date contents in its GC.
If you extend the GC to add additional resource ID components, you should ensure that the library stub immediately sends the change request. Since a client can free a resource immediately after using it, storing the value in the cache without forcing a protocol request can destroy the resource before it is set into the GC.
The _XFlushGCCache procedure forces the cache to be erased.
If you extend AIXwindows to add more poly-graphics primitives, you might be able to take advantage of facilities in the library to allow back-to-back single calls to be transformed into poly-requests. The display structure has a pointer to an xReq called last_req, which is the last request being processed. By checking that the last request type, drawable, GC, and other options are the same as the new one, and that there is enough space left in the buffer, you might be able to extend the previous graphics request by extending the length field of the request and appending the data to the buffer.
For example, the following is the source for the XDrawPoint stub:
#include <X11/Xlibint.h> /* precompute the max size of batching request allowed */ static int size = sizeof(xPolyPointReq) + EPERBATCH * sizeof(xPoint); XDrawPoint(dpy, d, gc, x, y) register Display *dpy; Drawable d; GC gc; int x, y; /* INT16 */ { xPoint *point; LockDisplay(dpy); FlushGC(dpy, gc); { register xPolyPointReq *req = (xPolyPointReq *) dpy->last_req; /* if same as previous request, with same drawable, batch requests */ if ( (req->reqType == X_PolyPoint) && (req->drawable == d) && (req->gc == gc->gid) && (req->coordMode == CoordModeOrigin) && ((dpy->bufptr + sizeof (xPoint)) <=dpy->bufmax) && (((char *)dpy->bufptr - (char *)req) < size)) { point = (xPoint *) dpy->bufptr; req->length += sizeof (xPoint) >> 2; dpy->bufptr += sizeof (xPoint); } else { GetReqExtra(PolyPoint, 4, req ); /* 1 point = 4 bytes */ req->drawable = d; req->gc = gc->gid; req->coordMode = CoordModeOrigin; point = (xPoint *) (req + 1); } point->x = x; point->y = y; } UnlockDisplay(dpy:); SyncHandle(); }
To keep clients from generating long requests that might monopolize the server, there is a limit of EPERBATCH defined in <X11/Xlib.h> on the number of requests batched. Note that the FlushGC macro is called before picking up the value of the last_req field, since it may modify this field.
To lock the display structure for systems that want to support multithreaded access to a single display connection or to support asynchronous input, each stub needs to lock its critical section. Generally, this section is the point immediately prior to the appropriate GetReq call when all arguments to the call have been stored into the request. Two calls generally implemented as macros are:
LockDisplay(Display) Display *Display; UnlockDisplay(Display) Display *Display;
The Display parameter specifies a pointer to the display structure of the display to be locked or unlocked.
The <X11/Xproto.h> header file contains the following three sets of definitions:
X server requests contain the length, expressed as a 16-bit quantity of 32-bits, of the request. Therefore, a single request can be no more than 256k long. Some servers may not support single requests of such a length. The value of display -> max_request_size contains the maximum length as defined by the server implementation.
An Xlib library stub routine should start as follows:
#include <X11/Xlibint.h> XDoSomething (arguments, ...) /* argument declarations */ {
/* variable declarations, if any */
If the protocol request has a reply, then the variable declarations should include the reply structure for the request. The following is an example:
xDoSomethingReply rep;
Generate a file equivalent to the <X11/Xproto.h> header file for your extension and include it in your stub routine. Each stub routine also must include the <X11/Xlibint.h> header file.
The identifiers are deliberately chosen in such a way that if the request is called X_DoSomething , then its request structure is xDoSomethingReq and its reply is xDoSomethingReply . The GetReq family of macros, defined in the <X11/Xlibint.h> header file, takes advantage of this naming scheme.
For each X request, there is a definition in the <X11/Xproto.h> that looks similar to the following:
#define X_DoSomething 42
In your extension header file, this is a minor opcode instead of a major opcode.
Every request contains an 8-bit major opcode and a 16-bit length field expressed in units of 4 bytes. Every request consists of a 4-byte header (containing the major opcode, the length field, and a data byte) followed by a 0 (zero) or additional bytes of data. The length field defines the total length of the request, including the header. The length field in a request must equal the minimum length required to contain the request. If the specified length is smaller or larger than the required length, the extension should generate a BadLength error. Unused bytes in a request are not required to be the value of 0.
The XMaxRequestSize extension function returns the maximum request size (4-byte units) supported by the server.
Major opcodes 128 through 255 are reserved for extensions. Extensions are for holding multiple requests, so extension requests typically have an additional minor opcode encoded in the spare data byte in the request header. But the placement and interpretation of this minor opcode, as well as all other fields in extension requests, are not defined by the Core protocol. Every request is implicitly assigned a sequence number (starting with one) used in replies, errors, and events. The B16 and B32 macros have been defined so that they can become bit-field specifications on some machines.
Most protocol requests have a corresponding structure typedef in the <X11/Xproto.h> header file. The following is an example of the xResourceReq typedef structure:
typedef struct _ResourceReq{ CARD8 reqType; /* the request type, X_DoSomething */ BYTE pad; /* not used */ CARD16 length; /* 2 (= total number of bytes in request, divided by 4) */ CARD32 id; /* the window, drawable, font, or gcontext, for example */ } xResourceReq;
If a Core protocol request has a single 32-bit argument, you do not need to declare a request structure in your extension header file. Instead, such requests use the xResourceReq data structure in the <X11/Xproto.h> header file. This structure is used for any request whose single argument is Window, Pixmap, Drawable, GContext, Font, Cursor, Colormap, Atom, or VisualID.
typedef struct _DoSomethingReq{ CARD8 reqType; /* X_DoSomething */ CARD8 someDatum; /* used differently in different requests*/ CARD16 length; /* total number of bytes in request, divided by 4 */ .... /* request-specific data */ ... } xDoSomethingReq;
You can do something similar in your extension header file.
A few protocol requests take no arguments at all. Instead, they use the xReq data structure, which contains only a request type and a length (and a pad byte), in the <X11/Xproto.h> header file.
If the protocol request requires a reply, then the <Xproto.h> header file also contains a reply structure typedef.
typedef struct _DoSomethingReply { BYTE type; /* always X_Reply */ BYTE someDatum /* used differently in different requests */ CARD16 sequenceNumber; /* number of requests sent so far*/ CARD32 length; /* number of additional bytes, divided by 4 */ .... /* request-specific data */ .... } xDoSomethingReply;
Most of these reply structures are 32 bytes long. If the reply value is less than 32 bytes, the reply structure contains a sufficient number of pad fields to bring them up to 32 bytes.
The length is the total number of bytes in the request minus 32, divided by 4. This field is not the value of 0 if:
The only Core protocol exceptions that have reply structures longer than 32 bytes are as follows:
A few protocol requests return replies that contain no data. The <X11/Xproto.h> header file does not define reply structures for these. Instead, these protocol requests use the xGenericReply structure, which contains only a type, length, and sequence number (and sufficient padding to make it 32-bytes long).
After the variable declarations, a stub routine should call one of the following four macros defined in the Xlibint.h file:
These macros take the name of the protocol request as declared in the <X11/Xproto.h> header file without the X_ as their first argument. Each macro declares a Display structure pointer, called dpy and a pointer to a request structure, called req, which is of the appropriate type. The macro then appends the request structure to the output buffer, fills in the type and length field, and sets the req variable to point to it.
If the protocol request, such as GrabServer, has no arguments, use the GetEmptyReq protocol request as in the following example:
GetEmptyReq (DoSomething, req);
If the protocol request has a single 32-bit argument (such as a Pixmap, Window, Drawable, or Atom), use the GetResReq macro.
The second argument to this macro is the 32-bit object. The X_MapWindow request type is a good example of the GetResReq macro:
GetResReq (DoSomething, rid, req);
The rid argument is the Pixmap or Window value, or other resource ID.
If the protocol request takes any other argument list, then call the GetReq macro. After the GetReq macro, set all the other fields in the request structure, usually from arguments to the stub routine.
GetReq (DoSomething, req); /* fill in arguments here */ req->arg1 = arg1; req->arg2 = arg2;
A few stub routines, such as the XCreateGC function and the XCreatePixmap function, return a resource ID to the caller but pass a resource ID as an argument to the protocol request. These stub routines use the XAllocID macro to allocate a resource ID from the range of IDs that were assigned to this client when it opened the connection. The following is an example of the XAllocID macro:
rid = req->rid = XAllocID(); return (rid);
Finally, some stub routines transmit a fixed amount of variable-length data after the request. Typically, these routines, such as the XMoveWindow function and the XSetBackground function, are special cases of more general routines like the XMoveResizeWindow function and the XChangeGC function. In these cases, the GetReqExtra macro, which is like the GetReq macro with an additional argument, is used. The additional argument is the number of extra bytes (a multiple of 4) allocated in the output buffer after the request structure.
Some protocol requests take additional variable length data that follow the xDoSomethingReq structure. The format of this data varies from one request to another. Some require a sequence of 8-bit bytes, others a sequence of 16-bit or 32-bit entities, and still others a sequence of structures.
The length of any variable length data must be added to the length field of the request structure. The length field is in units of 32-bit long words. If the data is a string or other sequence of 8-bit bytes, then round up the length and shift it before adding. For example:
req->length += (nbytes+3)>>2;
To transmit the variable length data, use the Data macros. If the data fits into the output buffer, then this macro copies it to the buffer. If it does not fit, however, the Data macro calls the _XSend, which first transmits the contents of the buffer and then transmits your data. The Data macro takes three arguments: the display, a pointer to the beginning of the data, and the number of bytes to be sent.
Data, Data16, and Data32 are macros that can use their last argument more than once, so that argument should be a variable rather than an expression such as nitems*sizeof(item). You should do that kind of computation in a separate statement before calling them. Use the appropriate macro when sending byte, short, or long data.
If the protocol request requires a reply, then call the procedure _XSend instead of the Data macro. _XSend takes the same arguments, but because it sends your data immediately instead of copying it into the output buffer (which would later be erased anyway by the following call on _XReply ), it is faster.
The following is an example of the Data macro:
Data(display, (char *) data, nbytes);
If the protocol request has a reply, use the _XReply function after dealing with all the fixed and variable length arguments.
To ease debugging, each routine should have a call to a routine immediately prior to returning to the user. This routine is called SyncHandle() and is generally implemented as a macro. If the synchronous mode is enabled with the XSynchronize function, the request is sent immediately. The library, however, waits until any generated error has been handled.
To support the possible re-entry of these routines, several conventions should be observed when allocating and deallocating memory. This is appropriate especially when the user does not know the size of the data that is being returned. (The standard C language library routines on many systems are not protected against signals or other multithreaded use.) The analogies to standard I/O library routines are defined as follows:
Xmalloc() | Replaces the malloc() routine. |
Xfree() | Replaces the free() routine. |
Xcalloc() | Replaces the calloc() routine. |
These routines should be used in place of any calls made to the normal C language library routines. For example, if you need a single scratch buffer inside a critical section to pack and unpack data to and from wire protocol, the general memory allocators may be too expensive to use (particularly in output routines, which are performance critical). Use the _XAllocScratch function to return a scratch buffer. This storage must only be used inside the critical section of your stub.
When writing an extension stub routine map from the call to the proper major and minor opcodes, you can use the following suggested strategy:
Certain server extensions can be loaded by the X server during runtime when the server initializes. This can be done in two ways:
X -x mbxThis loads the Multi-Buffer Extension into the server. Multiple instances of the -x flag are allowed.
The format of both static_ext and dynamic_ext files is the same, it contains two fields, separated by white spaces. The first field is the extension's nickname used to identify the module to be loaded, the nickname cannot be longer than 27 characters; the second field is the path name of the extension's loadable module. The total length of one entry, that is, the two fields and white spaces, cannot be longer than 160 characters. The following is an example of the format:
mbx /usr/lpp/X11/bin/loadMbx
The server keeps track of nicknames of the extensions loaded, therefore, the same extension is only loaded once, even if it has multiple entries in files static_ext and dynamic_ext and command line flag -x.
If the X server cannot find or load the specified extension module, it is considered a fatal error. The X server uses the loadquery subroutine to query the error messages when the load system call fails. It then executes execerror to display these error messages, and exits the process.