Site hosted by Angelfire.com: Build your free website today!

Hands-on Projects for the Linux Graphics Subsystem

New book from Christos Karayiannis

Available in Amazon Kindle format at:

amazon.com   amazon.co.uk   amazon.de   amazon.es   amazon.fr   amazon.it

1.2 The Xlib routines

The X routines used in the previous example are analysed here. This example uses only a small fraction of the Xlib routines, however it is adequate to illustrate a simple session between the X client and the X server.

XOpenDisplay()
XCreateSimpleWindow()
XMapWindow()
XFlush()
XCloseDisplay()
XSelectInput()
XNextEvent()


1.2.1 XOpenDisplay()

The location of the Xlib source code in the Xorg tree is: xorg/xc/lib/X11. The first of the routines used in the example is XOpenDisplay(). There is also a man page for this routine.

XOpenDisplay() attempts to make a connection to the server, which is indicated by the only argument it takes. This argument is of type display_name which as we can see in ConnDis.c takes a value of the form:

[protocol/] [hostname] : [:] displaynumber [.screennumber]

Therefore XOpenDisplay() tries to get connected to the server found at the host 'hostname' with the server number 'servernumber' (if more than one X servers exist), using the screen with the number 'screenumber'. display_name is actually a field of the _XDisplay struct, defined in Xlibint.h.

If the value of the XOpenDisplay() argument is NULL the diplsay is assigned by the DISPLAY environmental variable. For example see this tutorial.

XOpenDisplay() allocates a Display struct, which as found in Xlib.h is the _XDisplay struct typedefed:

typedef struct _XDisplay Display;

XOpenDisplay() calls _X11TransOpenCOTSClient():

	if ( (trans_conn = _X11TransOpenCOTSClient(address)) == NULL )
Then XOpenDisplay() uses the return value trans_conn to call _X11TransConnectDisplay()

dpy->trans_conn = _X11TransConnectDisplay (
					 display_name, &fullname, &idisplay,
					 &iscreen, &conn_auth_name,
					 &conn_auth_namelen, &conn_auth_data,
					 &conn_auth_datalen)

Code commentary

_X11TransConnectDisplay() parses the display_name to find the hostname, the display number and the screen number. Then it makes the connection with the X server by calling:

trans_conn = _X11TransOpenCOTSClient(address)

This routine is called as _X11TransOpenCOTSClient(). According to Xtrans.h TRANS(OpenCOTSClient) is expanded to _X11TransOpenCOTSClient(), following the preprocessor directive:

#define TRANS(func) _XTrans##func

_X11TransOpenCOTSClient() takes as argument the X server address, which is this time has the form:

protocol/hostname:display (see this doc) and it mainly calls:

TRANS(Open) (XTRANS_OPEN_COTS_CLIENT, address)

TRANS(Open) mainly does the following:

1) Parses the address by calling:

TRANS(ParseAddress) (address, &protocol, &host, &port)

given the address which has the format "protocol/host:port" the values of protocol, host and port that were passed to this routine by reference are filled

2) Determines the transport type by calling:

thistrans = TRANS(SelectTransport) (protocol)

TRANS(SelectTransport), which is also implemented in Xtrans.c reads Xtransports[], also defined in Xtrans.c, which according to the system's #define varies and searches for a transport with a name identical to the protocol passed as argument. For instance if TRANS(SocketTCPFuncs) is selected then as found in Xtranssock.c the function
thistrans->OpenCOTSClient
which is used in the next step becomes TRANS(SocketOpenCOTSClient).

Xtransport	TRANS(SocketTCPFuncs) = {
	/* Socket Interface */
	"tcp",
        TRANS_ALIAS,
#ifdef TRANS_CLIENT
	TRANS(SocketOpenCOTSClient),
#endif /* TRANS_CLIENT */
#ifdef TRANS_SERVER
	tcp_nolisten,
	TRANS(SocketOpenCOTSServer),
#endif /* TRANS_SERVER */
#ifdef TRANS_CLIENT
	TRANS(SocketOpenCLTSClient),
#endif /* TRANS_CLIENT */
#ifdef TRANS_SERVER
	TRANS(SocketOpenCLTSServer),
#endif /* TRANS_SERVER */
#ifdef TRANS_REOPEN
	TRANS(SocketReopenCOTSServer),
	TRANS(SocketReopenCLTSServer),
#endif
	TRANS(SocketSetOption),
#ifdef TRANS_SERVER
	TRANS(SocketINETCreateListener),
	NULL,		       			/* ResetListener */
	TRANS(SocketINETAccept),
#endif /* TRANS_SERVER */
#ifdef TRANS_CLIENT
	TRANS(SocketINETConnect),
#endif /* TRANS_CLIENT */
	TRANS(SocketBytesReadable),
	TRANS(SocketRead),
	TRANS(SocketWrite),
	TRANS(SocketReadv),
	TRANS(SocketWritev),
	TRANS(SocketDisconnect),
	TRANS(SocketINETClose),
	TRANS(SocketINETClose),
	};

Xtransport if defined in Xtransint.h

3) After collecting the appropriate variable values from (1) and (2) it calls OpenCOTSClient as:

    case XTRANS_OPEN_COTS_CLIENT:
#ifdef TRANS_CLIENT
	ciptr = thistrans->OpenCOTSClient(thistrans, protocol, host, port);
#endif /* TRANS_CLIENT */ 

TRANS_CLIENT is defined in the X11 library Imakefile as:

XTRANS_X_DEFINES = -DX11_t -DTRANS_CLIENT $(XTRANS_FAILDEFINES)

From step (2) we saw how thistrans->OpenCOTSClient is expanded to TRANS(SocketOpenCOTSClient). This calls TRANS(SocketOpenCOTSClientBase), implemented in the same file, which calls TRANS(SocketOpen), which mainly makes a socket() call and returns a XtransConnInfo struct, which has as file descriptor field the one returned by the socket() call.

_X11TransConnectDisplay returns to XOpenDisplay() the trans_conn field of the Display which is a pointer to _XtransConnInfo. Next many fields of the Display are filled and this is assisted by the arguments that were previously passed by reference to _X11TransConnectDisplay() in order to obtain a value for instance the last four 'authorization' arguments.

A first message pair is exchanged between the X client and the X server with the client to ask for permission and the server to accept or to deny. Notice that this is the first message passing in the Application Layer, in our case the X Protocol, however in the Transport Layer previously other packet passing took place. The massage has the format indicated by xConnClientPrefix, which is defined in Xproto.h as:


typedef struct {
    CARD8	byteOrder;
    BYTE	pad;
    CARD16	majorVersion B16, minorVersion B16;
    CARD16	nbytesAuthProto B16;	/* Authorization protocol */
    CARD16	nbytesAuthString B16;	/* Authorization string */
    CARD16	pad2 B16;
} xConnClientPrefix;

_XSendClientPrefix() sends the information to the sever by calling _X11TransWritev(), implemented as TRANS(Writev) in Xtrans.c:

int
TRANS(Writev) (XtransConnInfo ciptr, struct iovec *buf, int size)

{
    return ciptr->transptr->Writev (ciptr, buf, size);
}

If thistrans points to TRANS(SocketTCPFuncs) and compare this with Xtransport the TRANS(Writev) becomes TRANS(SocketWritev), which for the UNIX system is resolved to macro WRITEV (Write Vector). WRITEV for UNIX systems is defined as the writev() system call:

#define WRITEV(ciptr, iov, iovcnt)	writev(ciptr->fd, iov, iovcnt)

The reply to the connection request sent to the X server is next readed with _XRead() in order to find if the connection is accepted and in this case to fill some more Display fields that are needed for the client server cooperation. _XRead() calls:

_X11TransRead(dpy->trans_conn, data, (int)size))

which is implemented as TRANS(Read) in Xtrans.c:

int
TRANS(Read) (XtransConnInfo ciptr, char *buf, int size)

{
    return ciptr->transptr->Read (ciptr, buf, size);
}

Again if thistrans points to TRANS(SocketTCPFuncs) and compare this with Xtransport the TRANS(Read) becomes TRANS(SocketRead), which for the UNIX system is resolved to a read() socket call (or to a recv call for some non-Unix imlpementations). If the reply indicates that the message was accepted by the X server the reply is used to fill some more Display fields and finally returns the Display opened with the client request.

XOpenDisplay() returns the Display struct that it created.

Note 1: about Xtransport see Xtrans.txt
Note 2: for Xtransports[] the TCPCONN flag is used. Where is it defined? As seen in Xtrans.txt at "vendor.cf or site.def config files". Looking for instance at Linux.cf we find:
# define ConnectionFlags -DUNIXCONN -DTCPCONN
Note 3: although so far it looks that the client with XOpenDisplay() do not send a request to the server, the server actually receives a fake request that is a request prepared by the server on behalf of the client (see Section 3).


1.2.2 XCreateSimpleWindow()

From the man page for this routine we read: "The XCreateSimpleWindow function creates an unmapped InputOutput subwindow for a specified parent window, returns the window ID of the created window, and causes the X server to generate a CreateNotify event."

XCreateSimpleWindow() is implemented in CrWindow.c:

Window XCreateSimpleWindow(dpy, parent, x, y, width, height, 
                      borderWidth, border, background)
    register Display *dpy;
    Window parent;
    int x, y;
    unsigned int width, height, borderWidth;
    unsigned long border;
    unsigned long background;
{
    Window wid;
    register xCreateWindowReq *req;

    LockDisplay(dpy);
    GetReqExtra(CreateWindow, 8, req);
    req->parent = parent;
    req->x = x;
    req->y = y;
    req->width = width;
    req->height = height;
    req->borderWidth = borderWidth;
    req->depth = 0;
    req->class = CopyFromParent;
    req->visual = CopyFromParent;
    wid = req->wid = XAllocID(dpy);
    req->mask = CWBackPixel | CWBorderPixel;

#ifdef MUSTCOPY
    {
	unsigned long lbackground = background, lborder = border;
	dpy->bufptr -= 8;
	Data32 (dpy, (long *) &lbackground, 4);
	Data32 (dpy, (long *) &lborder, 4);
    }
#else
    {
	register CARD32 *valuePtr = (CARD32 *) NEXTPTR(req,xCreateWindowReq);
	*valuePtr++ = background;
	*valuePtr = border;
    }
#endif /* MUSTCOPY */

    UnlockDisplay(dpy);
    SyncHandle();
    return (wid);
    }

It mainly utilises macro GetReqExtra , which does the following:

/* GetReqExtra is the same as GetReq, but allocates "n" additional
   bytes after the request. "n" must be a multiple of 4!  */

#if !defined(UNIXCPP) || defined(ANSICPP)
#define GetReqExtra(name, n, req) \
        WORD64ALIGN\
	if ((dpy->bufptr + SIZEOF(x##name##Req) + n) > dpy->bufmax)\
		_XFlush(dpy);\
	req = (x##name##Req *)(dpy->last_req = dpy->bufptr);\
	req->reqType = X_##name;\
	req->length = (SIZEOF(x##name##Req) + n)>>2;\
	dpy->bufptr += SIZEOF(x##name##Req) + n;\
	dpy->request++

At the macro the request becomes of type X_CreateWindow which is defined in Xproto.h as:

#define X_CreateWindow                  1     
the request with the additional 8 bytes are placed in the output buffer (bufptr) of the current Display and the bufptr is updated to be ready for the next request. From the macro the request gets the format of struct xCreateWindowReq, which is also defined in Xproto.h as:

typedef struct {
    CARD8 reqType;
    CARD8 depth;
    CARD16 length B16;
    Window wid B32, parent B32;
    INT16 x B16, y B16;
    CARD16 width B16, height B16, borderWidth B16;  
#if defined(__cplusplus) || defined(c_plusplus)
    CARD16 c_class B16;
#else
    CARD16 class B16;
#endif
    VisualID visual B32;
    CARD32 mask B32;
} xCreateWindowReq;

The additional bytes are needed because as seen in the man page XCreateSimpleWindow() extends XCreateWindow(), although it does not use all of the fields in XCreateWindow(), and two more unsigned longs are added. Those are 'background' and 'border'. XCreateSimpleWindow() fills the values of these two variables in the output buffer.

XCreateSimpleWindow() returns the wid (widow id):

wid = req->wid = XAllocID(dpy);

Macro XAllocID is defined in Xlib.h. It calls the allocator function for the current display, which was set by XOpenDisplay() to _XAllocID.

The saved request keeps waiting, probably with other older or newer requests, at the output buffer until a XFlush() call is issued.


1.2.3 XMapWindow()

This routine is implemented in MapWindow.c and is described by its man page. This time macro GetResReq is used implemented in the same file GetReqExtra was implemented. We read there for GetResReq:
"GetResReq is for those requests that have a resource ID (Window, Pixmap, GContext, etc.) as their single argument."

#if !defined(UNIXCPP) || defined(ANSICPP)
#define GetResReq(name, rid, req) \
        WORD64ALIGN\
	if ((dpy->bufptr + SIZEOF(xResourceReq)) > dpy->bufmax)\
	    _XFlush(dpy);\
	req = (xResourceReq *) (dpy->last_req = dpy->bufptr);\
	req->reqType = X_##name;\
	req->length = 2;\
	req->id = (rid);\
	dpy->bufptr += SIZEOF(xResourceReq);\
	dpy->request++

Since GetResReq was called as:

GetResReq(MapWindow, w, req);

the request type becomes X_MapWindow. This is defined in Xproto.h as:

#define X_MapWindow                     8
Struct xResourceReq is defined in Xproto.h as:

/* ResourceReq is used for any request which has a resource ID 
   (or Atom or Time) as its one and only argument.  */
typedef struct {
    CARD8 reqType;
    BYTE pad;
    CARD16 length B16;
    CARD32 id B32;  /* a Window, Drawable, Font, GContext, Pixmap, etc. */
    } xResourceReq;

Again the saved request keeps waiting at the output buffer until a XFlush() call is issued.


1.2.4 XFlush()

At last the XFlush is called.

This Xlib routine calls _XFlush() , which actually calls _XFlushInt.
The latter calls _X11TransWrite(), implemented as TRANS(Write) in Xtrans.c to 'flush' all output buffer requests to the X server.


int
TRANS(Write) (XtransConnInfo ciptr, char *buf, int size)

{
    return ciptr->transptr->Write (ciptr, buf, size);
}

For TRANS(SocketTCPFuncs) the ciptr->transptr->Write is resolved to TRANS(SocketWrite), which is actually a write() socket call.

Xflush is similar to Xsync described latter, see this man page The XFlush function flushes the output buffer. The XSync function flushes the output buffer and then waits until all requests have been received and processed by the X server.


1.2.5 XCloseDisplay()

This routine is implemented in ClDisplay.c This routine mainly calls XSync(), which is similar to XFlush() to throw away events sent from the X server. XSync() uses macro GetEmptyReq as:

GetEmptyReq(GetInputFocus, req);

which as we read in Xlibint.h:
"GetEmptyReq is for those requests that have no arguments at all."

#if !defined(UNIXCPP) || defined(ANSICPP)
#define GetEmptyReq(name, req) \
        WORD64ALIGN\
	if ((dpy->bufptr + SIZEOF(xReq)) > dpy->bufmax)\
	    _XFlush(dpy);\
	req = (xReq *) (dpy->last_req = dpy->bufptr);\
	req->reqType = X_##name;\
	req->length = 1;\
	dpy->bufptr += SIZEOF(xReq);\
	dpy->request++

The request name is GetInputFocus and from this the request type becomes inside the macro X_GetInputFocus. This is defined to Xproto.h as:

#define X_GetInputFocus                43
The request header is represented by struct Xreq is also defined in Xproto.h as:

/* Request structure */

typedef struct _xReq {
	CARD8 reqType;
	CARD8 data;            /* meaning depends on request type */
	CARD16 length B16;         /* length in 4 bytes quantities 
				  of whole request, including this header */
} xReq;

CARD8 and CARD16 are defined in Xmd.h. xReq is used for requests that take no arguments at all. xReq contains the request type, the length and data mainly used as a pad byte.

Also XCloseDisplay() calls two other routines: _XDisconnectDisplay() and _XFreeDisplayStructure()


1.2.6 XSelectInput()

This routine is implemented in SelInput.c. From the man page we read:
"The XSelectInput function requests that the X server report the events associated with the specified event mask."

Like XCreateSimpleWindow() it uses macro GetReqExtra to place a request of type X_ChangeWindowAttributes along with 4 more bytes. X_ChangeWindowAttributes is defined in Xproto.h as:

#define X_ChangeWindowAttributes        2      
The request header is represented by struct xChangeWindowAttributesReq:

typedef struct {
    CARD8 reqType;
    BYTE pad;
    CARD16 length B16;
    Window window B32;
    CARD32 valueMask B32; 
} xChangeWindowAttributesReq;

XSelectInput() uses xChangeWindowAttributesReq, used mainly by XChangeWindowAttributes(). XChangeWindowAttributes() has the following arguments:

dpy, w, valuemask, attributes

whereas XSelectInput:

dpy, w, mask

Therefore the additional 4 bytes are for the mask which is of type long.


1.2.7 XNextEvent()

XNextEvent() is implemented in NextEvent.c. It reads the event found at the event queue of the current display and uses _XDeq to remove the event packet from the display's queue.

From the XFlush man page we read:

       The XFlush function flushes the output buffer.  Most client applica-
       tions need not use this function because the output buffer is automati-
       cally flushed as needed by calls to XPending, XNextEvent, and XWindow-
       Event.  Events generated by the server may be enqueued into the
       library's event queue.


Conclusion

From the previous routines we see that we roughly have the following categories of Xlib routines:

1) Transportation, which cary tasks like connecting/disconnecting to the X server and sending receiving messages to and from. Those encapsulate the system calls of transport APIs like sockets, TLI, UNIX local, etc. _X11TransConnectDisplay() for instance in the case of sockets includes a socket() call. XFlush() can also encapsulate a send() socket call. Certainly their functionality includes more than simple transport routines encapsulation. As seen in Transport Interface text Xlib can be based in other transport interfaces as well. Certainly more functionality is included in those calls since the Display struct that represents the connection to the server has many fields to be filled.

2) X Protocol messaging, like XCreateSimpleWindow() and XMapWindow(), that do not much, since they prepare the X Protocol messages, store them to the output queue and wait for a XFlush or a XSync call

3) Event handling, like XNextEvent()


References:

https://www.msu.edu/~huntharo/xwin/docs/xwindows/XLIB.pdf