Xlib Programming Manual (O'Reilly & Associates, Inc.) |
Extensions can be added to Xlib only with no protocol addition or to both Xlib and the server with a protocol addition. Several extensions are already available that support nonrectangular windows, 3-D graphics, and alternate input devices and multibuffering. See the code for these for examples of writing extensions.
Because X can only evolve by extension to the core protocol, it is important that extensions not be perceivable as second-class citizens. At some point, some extensions may be adopted as parts of the "X Standard."
Therefore, there should be little to distinguish the use of an extension from that of the core protocol. To avoid having to initialize extensions explicitly in application programs, extensions should perform "lazy evaluations" and automatically initialize themselves when called for the first time.
Extensions written according to these instructions will run at essentially the same performance as the core protocol requests.
It is expected that a given extension to X will consist of multiple requests. Defining ten new features as ten separate extensions is a bad practice. Rather, they should be packaged into a single extension and should use minor opcodes to distinguish the requests.
The symbols and macros used for writing stubs to Xlib
are listed in <Xlibint.h>.
Bool XQueryExtension(display, name, major_opcode_return, \ first_event_return, first_error_return) Display *display; char *name; int *major_opcode_return; int *first_event_return; int *first_error_return;
If the extension name is not in the Host Portable Character Encoding the result is implementation dependent. Case matters; the strings thing, Thing, and thinG are all considered different names.
char **XListExtensions(display, nextensions_return) Display *display; int *nextensions_return;
XFreeExtensionList(list) char **list;
In extensions, stubs first should check to see if they have initialized themselves on a connection. If they have not, they then should call XInitExtension() to attempt to initialize themselves on the connection.
If the extension needs to be informed of GC/font allocation or deallocation or if the extension defines new event types, the functions described here allow the extension to be called when these events occur.
typedef struct _XExtCodes { /* public to extension, cannot be changed */
int extension; /* extension number */
int major_opcode; /* major op-code assigned by server */
int first_event; /* first event number for the extension */
int first_error; /* first error number for the extension */
} XExtCodes;
XExtCodes *XInitExtension(display, name) Display *display; char *name;
If the extension name is not in the Host Portable Character Encoding the result is implementation dependent. Case matters; the strings thing, Thing, and thinG are all considered different names.
The extension number in the XExtCodes structure is needed in the other calls that follow. This extension number is unique only to a single connection.
XExtCodes *XAddExtension(display) Display *display;
All of these functions return the previous routine defined for this extension.
int (*XESetCloseDisplay(display, extension, proc))() Display *display; int extension; int (*proc)();
When XCloseDisplay() is called, your routine is called with these arguments:
(*proc)(display, codes) Display *display; XExtCodes *codes;
int (*XESetCreateGC(display, extension, proc))() Display *display; int extension; int (*proc)();
When a GC is created, your routine is called with these arguments:
(*proc)(display, gc, codes) Display *display; GC gc; XExtCodes *codes;
int (*XESetCopyGC(display, extension, proc))() Display *display; int extension; int (*proc)();
When a GC is copied, your routine is called with these arguments:
(*proc)(display, gc, codes) Display *display; GC gc; XExtCodes *codes;
int (*XESetFreeGC(display, extension, proc))() Display *display; int extension; int (*proc)();
When a GC is freed, your routine is called with these arguments:
(*proc)(display, gc, codes) Display *display; GC gc; XExtCodes *codes;
int (*XESetCreateFont(display, extension, proc))() Display *display; int extension; int (*proc)();
When XLoadQueryFont() or XQueryFont() is called, your routine is called with these arguments:
(*proc)(display, fs, codes) Display *display; XFontStruct *fs; XExtCodes *codes;
int (*XESetFreeFont(display, extension, proc))() Display *display; int extension; int (*proc)();
When XFreeFont() is called, your routine is called with these arguments:
(*proc)(display, fs, codes) Display *display; XFontStruct *fs; XExtCodes *codes;
There is an implementation limit such that your host event structure size cannot be bigger than the size of the XEvent union of structures. There also is no way to guarantee that more than 24 elements or 96 characters in the structure will be fully portable between machines.
int (*XESetWireToEvent()(display, event_number, proc))() Display *display; int event_number; Status (*proc)();
You can replace a core event conversion routine with one of your own, although this is not encouraged. It would, however, allow you to intercept a core event and modify it before being placed in the queue or otherwise examined.
When Xlib needs to convert an event from wire format to host format, your routine is called with these arguments:
Your routine must return status to indicate if the conversion succeeded. The re argument is a pointer to where the host format event should be stored, and the event argument is the 32-byte wire event structure. In the XEvent structure you are creating, you must fill in the five required members of the event structure. You should fill in the type member with the type specified for the xEvent structure. You should copy all other members from the xEvent structure (wire format) to the XEvent structure (host format). Your conversion routine should return True if the event should be placed in the queue or False if it should not be placed in the queue.Status (*proc)(display, re, event) Display *display; XEvent *re; xEvent *event;
unsigned long _XSetLastRequestRead(display, rep) Display *display; xGenericReply *rep;
Status (*XESetEventToWire()(display, event_number, proc))() Display *display; int event_number; int (*proc)();
You can replace a core event conversion routine with one of your own, although this is not encouraged. It would, however, allow you to intercept a core event and modify it before being sent to another client.
When Xlib needs to convert an event from host format to wire format, your routine is called with these arguments:
The re argument is a pointer to the host format event, and the event argument is a pointer to where the 32-byte wire event structure should be stored. You should fill in the type with the type from the XEvent structure. All other members then should be copied from the host format to the xEvent structure.(*proc)(display, re, event) Display *display; XEvent *re; xEvent *event;
Bool (*XESetWireToError()(display, error_number, proc)() Display *display; int error_number; Bool (*proc)();
Use this function for extension errors that contain additional error values beyond those in a core X error, when multiple wire errors must be combined into a single Xlib error, or when it is necessary to intercept an X error before it is otherwise examined.
When Xlib needs to convert an error from wire format to host format, the routine is called with these arguments:
The he argument is a pointer to where the host format error should be stored. The structure pointed at by he is guaranteed to be as large as an XEvent structure, and so can be cast to a type larger than an XErrorEvent(), in order to store additional values. If the error is to be completely ignored by Xlib (for example, several protocol error structures will be combined into one Xlib error), then the function should return False; otherwise it should return True.Bool (*proc)(display, he, we) Display *display; XErrorEvent *he; xError *we;
int (*XESetError()(display, extension, proc))() Display *display; int extension; int (*proc)();
When Xlib detects a protocol error in _XReply(), it calls your procedure with these arguments:
The err argument is a pointer to the 32-byte wire format error. The codes argument is a pointer to the extension codes structure. The ret_code argument is the return code you may want _XReply returned to.int (*proc)(display, err, codes, ret_code) Display *display; xError *err; XExtCodes *codes; int *ret_code;
If your routine returns a zero value, the error is not suppressed, and the client's error handler is called. (For further information, see Section 11.8.2.) If your routine returns nonzero, the error is suppressed, and _XReply returns the value of ret_code.
char *(*XESetErrorString()(display, extension, proc))() Display *display; int extension; char *(*proc)();
Your procedure is called with the error code for every error detected. You should copy nbytes of a null-terminated string containing the error message into buffer.(*proc)(display, code, codes, buffer, nbytes) Display *display; int code; XExtCodes *codes; char *buffer; int nbytes;
void (*XESetPrintErrorValues()(display, extension, proc))() Display *display; int extension; void (*proc)();
When Xlib needs to print an error, the routine is called with these arguments:
The structure pointed at by ev is guaranteed to be as large as an XEvent structure, and so can be cast to a type larger than an XErrorEvent(), in order to obtain additional values set by using XESetWireToError(). The underlying type of the fp argument is system dependent; on a POSIX-compliant fp should be cast to type FILE*.void (*proc)(display, ev, fp) Display *display; XErrorEvent *ev; void *fp;
int (*XESetFlushGC()(display, extension, proc))() Display *display; int extension; int *(*proc)();
The following structure is used in the routines in this section and is defined in <Xlib.h>:
typedef struct _XExtData {
int number; /* number returned by XInitExtension() */
struct _XExtData *next; /* next item on list of data for structure */
int (*free_private)(); /* if defined, called to free private */
XPointer private_data; /* data private to this extension. */
} XExtData;
When any of the data structures listed above are freed, the
list is walked, and the structure's free routine (if any) is called. If
free is NULL, then the library frees both the data pointed to by the private_data
member and the structure itself.
union { Display *display;
GC gc;
Visual *visual;
Screen *screen;
ScreenFormat *pixmap_format;
XFontStruct *font } XEDataObject;
XExtData **XEHeadOfExtensionList(object) XEDataObject object;
XAddToExtensionList(structure, ext_data) XExtData **structure; XExtData *ext_data;
XExtData *XFindOnExtensionList()(structure, number) struct _XExtData **structure; int number;
XAllocID(display) Display *display;
The FlushGC() macro checks the dirty bits in the library's GC structure and calls _XFlushGCCache if any elements have changed. The FlushGC() macro is defined as follows:
FlushGC(display, gc) Display *display; GC gc;
_XFlushGC()Cache(display, gc) Display *display; GC gc;
#include "copyright.h"
#include "Xlibint.h"
/* precompute the maximum 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 very long requests that may
monopolize the server, there is a symbol defined in <Xlibint.h>
of EPERBATCH on the number of requests batched. Most of the performance
benefit occurs in the first few merged requests. Note that FlushGC()
is called before picking up the value of last_req, because
it may modify this field.
You need to generate a file equivalent to <Xproto.h> for your extension and need to include it in your stub routine. Each stub routine also must include <Xlibint.h>.
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 <Xlibint.h>, takes advantage of this naming scheme.
For each X request, there is a definition in <Xproto.h> that looks similar to this:
In your extension header file this will be a minor opcode, rather than a major opcode.#define X_DoSomething 42
Major opcodes 128 through 255 are reserved for extensions. Extensions are intended to contain 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.
To help but not cure portability problems to certain machines, the B16 and B32 macros have been defined so that they can become bitfield specifications on some machines. For example, on a Cray, these should be used for all 16-bit and 32-bit quantities, as discussed below.
Most protocol requests have a corresponding structure typedef in <Xproto.h>, which looks like:
typedef struct _DoSomethingReq {
CARD8 reqType; /* X_DoSomething */
CARD8 someDatum; /* used differently in different requests */
CARD16 length B16; /* total # of bytes in request, divided by 4 */
...
/* request-specific data */
...
} xDoSomethingReq;
If a core protocol request has a single 32-bit argument,
you need not declare a request structure in your extension header file.
Instead, such requests use <Xproto.h>'s xResourceReq structure.
This structure is used for any request whose single argument is a Window,
Pixmap, Drawable, GContext, Font, Cursor,
Colormap, Atom, or VisualID.
typedef struct _ResourceReq {
CARD8 reqType; /* the request type, e.g., X_DoSomething */
BYTE pad; /* not used */
CARD16 length B16; /* 2 (= total # of bytes in request, divided by 4) */
CARD32 id B32; /* the Window, Drawable, Font, GContext, etc. */
} xResourceReq;
If convenient, you can do something similar in your extension
header file.
In both of these structures, the reqType field identifies the type of the request (for example, X_MapWindow or X_CreatePixmap). The length field tells how long the request is in units of 4-byte longwords. This length includes both the request structure itself and any variable length data, such as strings or lists, that follow the request structure. Request structures come in different sizes, but all requests are padded to be multiples of four bytes long.
A few protocol requests take no arguments at all. Instead, they use <Xproto.h>'s xReq structure, which contains only a reqType and a length (and a pad byte).
If the protocol request requires a reply, then <Xproto.h> also contains a reply structure typedef:
typedef struct _DoSomethingReply {
BYTE type; /* always X_Reply */
BYTE someDatum; /* used differently in different requests */
CARD16 sequenceNumber B16; /* # of requests sent so far */
CARD32 length B32; /* # of additional bytes, divided by 4 */
...
/* request-specific data */
...
} xDoSomethingReply;
Most of these reply structures are 32 bytes long. If there
are not that many reply values, then they contain a sufficient number of
pad fields to bring them up to 32 bytes. The length field is the total
number of bytes in the request minus 32, divided by 4. This length will
be nonzero only if:
A few protocol requests return replies that contain no data. <Xproto.h> does not define reply structures for these. Instead, they use the xGenericReply structure, which contains only a type, length, and sequence number (and sufficient padding to make it 32 bytes long).
#include "Xlibint.h"
XDoSomething (arguments, ... )
/* argument declarations */
{
register XDoSomethingReq *req;
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;
LockDisplay(display) Display *display;
UnlockDisplay(display) Display *display;
If the protocol request has no arguments (for instance, X_GrabServer), then use GetEmptyReq().
If the protocol request has a single 32-bit argument (such as a Pixmap, Window, Drawable, Atom, and so on), then use GetResReq(). The second argument to the macro is the 32-bit object. X_MapWindow is a good example.GetEmptyReq (DoSomething, req);
The rid argument is the Pixmap, Window, or other resource ID.GetResReq (DoSomething, rid, req);
If the protocol request takes any other argument list, then call GetReq(). After the GetReq(), you need to set all the other fields in the request structure, usually from arguments to the stub routine.
A few stub routines (such as XCreateGC() and XCreatePixmap()) return a resource ID to the caller but pass a resource ID as an argument to the protocol request. Such routines use the macro XAllocID to allocate a resource ID from the range of IDs that were assigned to this client when it opened the connection.GetReq (DoSomething, req); /* fill in arguments here */ req->arg1 = arg1; req->arg2 = arg2;
Finally, some stub routines transmit a fixed amount of variable length data after the request. Typically, these routines (such as XMoveWindow() and XSetBackground()) are special cases of more general functions like XMoveResizeWindow() and XChangeGC(). These special case routines use GetReqExtra(), which is the same as GetReq except that it takes an additional argument (the number of extra bytes to allocate in the output buffer after the request structure). This number should always be a multiple of four.rid = req->rid = XAllocID(); return (rid);
It is necessary to add the length of any variable length data to the length field of the request structure. That length field is in units of 32-bit longwords. If the data is a string or other sequence of 8-bit bytes, then you must round the length up and shift it before adding:
To transmit 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 _XSend(), which transmits first the contents of the buffer and then your data. The Data macros take three arguments: the Display, a pointer to the beginning of the data, and the number of bytes to be sent.req->length += (nbytes+3)>>2;
Data(), Data16(), and Data32() are macros that may 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.Data(display, (char *) data, nbytes); Data16(display, (short *) data, nbytes); Data32(display, (long *) data, nbytes);
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 flushed anyway by the following call on _XReply()), it is faster.
Status _XReply(display, rep, extra, discard) Display *display; xReply *rep; int extra; Bool discard;
The last argument should be False if the reply structure is followed by additional variable length data (such as a list or string). It should be True if there is not any variable length data.
This last argument is provided for upward-compatibility reasons to allow a client to communicate properly with a hypothetical later version of the server that sends more data than the client expected. For example, some later version of XGetWindowAttributes might use a larger, but compatible, xGetWindowAttributesReply that contains additional attribute data at the end.
_XReplyreturns True if it received a reply successfully or False if it received any sort of error.
For a request with a reply that is not followed by variable length data, you write something like:
If there is variable length data after the reply, change the True to False, and use the appropriate _XRead function to read the variable length data._XReply(display, (xReply *)&rep, 0, True); *ret1 = rep.ret1; *ret2 = rep.ret2; *ret3 = rep.ret3; UnlockDisplay()(dpy); SyncHandle(); return (rep.ret4); }
_XRead(display, data_return, nbytes) Display *display; char *data_return; long nbytes;
_XRead16(display, data_return, nbytes) Display *display; short *data_return; long nbytes;
_XRead32(display, data_return, nbytes) Display *display; long *data_return; long nbytes;
_XRead16Pad(display, data_return, nbytes) Display *display; short *data_return; long nbytes;
_XReadPad(display, data_return, nbytes) Display *display; char *data_return; long nbytes;
Each protocol request is a little different. For further information, see the Xlib sources for examples.
If you need a single scratch buffer inside a critical section (for example, to pack and unpack data to and from the wire protocol), the general memory allocators may be too expensive to use (particularly in output routines, which are performance critical). The routine below returns a scratch buffer for your use:
char *_XAllocScratch(display, nbytes) Display *display; unsigned long nbytes;
This code may run on machines with 16-bit ints. So, if any integer argument, variable, or return value either can take only nonnegative values or is declared as a CARD16 in the protocol, be sure to declare it as unsigned int and not as int. (This, of course, does not apply to Booleans or enumerations.)
Similarly, if any integer argument or return value is declared CARD32 in the protocol, declare it as an unsigned long and not as int or long. This also goes for any internal variables that may take on values larger than the maximum 16-bit unsigned int.
The library currently assumes that a char is 8 bits, a short is 16 bits, an int is 16 or 32 bits, and a long is 32 bits. The PackData macro is a half-hearted attempt to deal with the possibility of 32 bit shorts. However, much more work is needed to make this work properly.
Xlib Programming Manual (O'Reilly & Associates, Inc.) |