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

The X Protocol Requests

This section theoretically examines the process of filling the output buffer of the X Client program with the X Protocol messages. At the next section we make an experiment to confirm the following conclusions.

_XDisplay includes the following fields to store the X Protocol requests:

	char *buffer;		/* Output buffer starting address. */
	char *bufptr;		/* Output buffer index pointer. */
	char *bufmax;		/* Output buffer maximum+1 address. */

In this example XOpenDisplay() is initially called, and then XCreateSimpleWindow() places a struct XCreateWindowReq at the memory address pointed by buffer. Also 8 additional bytes are reserved for two more arguments of XCreateSimpleWindow() that are not found in XCreateWindowReq. The latter is mainly used for XCreateWindow(). XMapWindow() is called next to place a xResourceReq struct at bufptr. This pointer was previously updated to be ready for another request struct.

The following figure illustrates the buffer, bufptr and bufmax pointers for the XCreateSimpleWindow() and XMapWindow() requests, for instance of the X Client program of section 1.5. Between the request structs created for XCreateSimpleWindow() and XMapWindow() other requests in this X Client program create some other request structs, denoted in the figure with three dots. Notice that this figure illustrates the X Protocol messages, from the one created by XCreateSimpleWindow() to the one created by XMapWindow() at the time that all these messages are stored in 'buffer'. This is a rather theoretical assumption and as we see in section 2.3 in the practice these messages are stored in 'buffer' and then flushed to the X Server in two groups.

XFlush() is called next to sent to the X Server with the given Display all requests stored in the output buffer. The size of the message to be sent is certainly:

size = todo = dpy->bufptr - dpy->buffer;

Flushing the client's requests to the X Server

XFlush() is implemented as:

int
XFlush (dpy)
    register Display *dpy;
    {
    LockDisplay(dpy);
    _XFlush (dpy);
    UnlockDisplay(dpy);
    return 1;
    }

_XFlush() is implemented in XlibInt.c, which stands for Internal Xlib routines, as:

 */
void _XFlush(
	register Display *dpy)
{
. . .
	size = todo = dpy->bufptr - dpy->buffer;
	if (!size) return;
. . .
	for (ext = dpy->flushes; ext; ext = ext->next_flush)
	    (*ext->before_flush)(dpy, &ext->codes, dpy->buffer, size);
	bufindex = dpy->buffer;
	 * While write has not written the entire buffer, keep looping
	 * until the entire buffer is written.  bufindex will be
	 * incremented and size decremented as buffer is written out.
	 */
	while (size) {
	    ESET(0);
	    write_stat = _X11TransWrite(dpy->trans_conn,
					bufindex, (int) todo);
	    if (write_stat >= 0) {
		size -= write_stat;
		todo = size;
		bufindex += write_stat;
. . .

As we see _XFlush used the _X11TransWrite() call, which as we recall from section 1.2 this is resolved (for most Unix systems) to a write() system call.

For the current example XNextEvent() is called instead of XFlush(). From the Xlib Programming Manual we read about XNextEvent():

"The XNextEvent() function copies the first event from the event queue into the specified XEvent structure and then removes it from the queue. If the event queue is empty, XNextEvent() flushes the output buffer and blocks until an event is received."

For the moment lets keep the last sentence "XNextEvent() flushes the output buffer" and ignore the events, that are discussed in section 6.

XNextEvent() is implemented in NextEvent.c as:

int
XNextEvent (dpy, event)
	register Display *dpy;
	register XEvent *event;
{
	register _XQEvent *qelt;
	
	LockDisplay(dpy);
	
	if (dpy->head == NULL)
	    _XReadEvents(dpy);
	qelt = dpy->head;
	*event = qelt->event;
	_XDeq(dpy, NULL, qelt);
	UnlockDisplay(dpy);
	return 0;
}

As we read from the _XReadEvents comment:

/* _XReadEvents - Flush the output queue,
 * then read as many events as possible (but at least 1) and enqueue them
 */

_XReadEvents() calls _XFlush() to flush the output buffer of the client.

It seemed strange by the first look that by performing strace to the sample X Client ( see section 1.5) of the Ubuntu Linux system I used, no write() call was found as expected. As we explain in section 1.6 strace analyses the system calls of the X Client process. As we previously see XFlush() is eventually an encapsulation of the write() system call. Instead only writev() calls appeared in the strace output.

The answer to this was that the libX11 of my system was compiled with the XCB extension (see Wikipedia).

As we see in XlibInt.c _XFlush() is implemented when XCB is not defined (!USE_XCB):

#if !USE_XCB

. . .

/*
 * _XFlush - Flush the X request buffer.  If the buffer is empty, no
 * action is taken.  This routine correctly handles incremental writes.
 * This routine may have to be reworked if int < long.
 */
void _XFlush(
	register Display *dpy)

In the case that symbol USE_XCB is defined (case of my Ubuntu system) _XFlush() is implemented in xcb_io.c as:

/*
 * _XFlush - Flush the X request buffer.  If the buffer is empty, no
 * action is taken.
 */
void _XFlush(Display *dpy)
{
	require_socket(dpy);
	_XSend(dpy, NULL, 0);

	_XEventsQueued(dpy, QueuedAfterReading);
}

_XFlush() calls therefore _XSend(), which flushes the output buffer with a writev() call.