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

MouseReadInput()

We discuss here a case study of a pInfo->read_input function. For the example we followed at section 5.1.1 this function is MouseReadInput(). This function is used to read the data sent to the X Server by the mouse.

The read_input function was called at section 5.1.1 as:


   pInfo->read_input(pInfo);

MouseReadInput() starts by setting the blocking to -1 with XisbBlockDuration().

    /*
     * Set blocking to -1 on the first call because we know there is data to
     * read. Xisb automatically clears it after one successful read so that
     * succeeding reads are preceeded by a select with a 0 timeout to prevent
     * read from blocking indefinitely.
     */
    XisbBlockDuration(pMse->buffer, -1);

    while ((c = XisbRead(pMse->buffer)) >= 0) {
	u = (unsigned char)c;

XisbRead() is implemented as:

_X_EXPORT int
XisbRead (XISBuffer *b)
{
	int ret;

	if (b->current >= b->end)
	{
		if (b->block_duration >= 0)
		{
			if (xf86WaitForInput (b->fd, b->block_duration) < 1)
				return (-1);
		}
		else
		{
			/*
			 * automatically clear it so if XisbRead is called in a loop
			 * the next call will make sure there is data with select and
			 * thus prevent a blocking read
			 */
			b->block_duration = 0;
		}
		
		ret = xf86ReadSerial (b->fd, b->buf, b->buffer_size);
		switch (ret)
		{
			case 0:
				return (-1); /* timeout */
			case -1:
				return (-2); /* error */
			default:
				b->end = ret;
				b->current = 0;
				break;
		}
	}
	if (b->trace)
		ErrorF ("read 0x%02x (%c)\n", b->buf[b->current], 
			isprint(b->buf[b->current])?b->buf[b->current]:'.');

	return (b->buf[b->current++]);
}

Since b->block_duration was initially set to -1 by XisbBlockDuration xf86WaitForInput() is not called the first time and we proceed to xf86ReadSerial(). The first time we know as the comment above notes that there are data since we came here right through a SIGIO signal. At the next iteration of the current 'while' loop XisbRead() goes first to xf86WaitForInput() with a zero timeout (the select returns immediately) and in this case is there are ready fds xf86ReadSerial() reads them.

xf86ReadSerial() is an encapsulation of the read() system call and is implemented as:

int
xf86ReadSerial (int fd, void *buf, int count)
{
	int r;
#ifdef DEBUG
	int i;
#endif
	SYSCALL (r = read (fd, buf, count));
#ifdef DEBUG
	ErrorF("ReadingSerial: 0x%x",
	       (unsigned char)*(((unsigned char *)buf)));
	for (i = 1; i < r; i++)
	    ErrorF(", 0x%x",(unsigned char)*(((unsigned char *)buf) + i));
	ErrorF("\n");
#endif
	return (r);
}

xf86WaitForInput() is implemented as:

int
xf86WaitForInput (int fd, int timeout)
{
	fd_set readfds;
	struct timeval to;
	int r;

	FD_ZERO(&readfds);

	if (fd >= 0) {
	    FD_SET(fd, &readfds);
	}
	
	to.tv_sec = timeout / 1000000;
	to.tv_usec = timeout % 1000000;

	if (fd >= 0) {
	    SYSCALL (r = select (FD_SETSIZE, &readfds, NULL, NULL, &to));
	}
	else {
	    SYSCALL (r = select (FD_SETSIZE, NULL, NULL, NULL, &to));
	}
	xf86ErrorFVerb (9,"select returned %d\n", r);
	return (r);
}

We continue with the MouseReadInput() code part that in the case of autoProbe the data read from the mouse device via XisbRead() is now placed in the next available element in the data[] field of the mousePrivRec struct:

	/* if we do autoprobing collect the data */
	if (pMse->collectData && pMse->autoProbe)
	    if (pMse->collectData(pMse,u))
		continue;

As seen at the example of section 3.2.3 pMse->collectData is the collectData() of mouse.c:

/*
 * collectData() -- collect data bytes sent by mouse.
 */
static Bool
collectData(MouseDevPtr pMse, unsigned char u)
{
    mousePrivPtr mPriv = (mousePrivPtr)pMse->mousePriv;
    if (mPriv->count < NUM_MSE_AUTOPROBE_TOTAL) {
	mPriv->data[mPriv->count++] = u;
	if (mPriv->count <= NUM_MSE_AUTOPROBE_BYTES) {
		return TRUE;
	} 
    }
    return FALSE;
}

A mouse mousePrivRec, pointed by mousePrivPtr (mPriv in our example) was previously allocated, as seen in section 3.2.3. This pointer is defined in mousePriv.h as:

typedef struct {
    int		current;
    Bool	inReset;
    CARD32	lastEvent;
    CARD32	expires;
    Bool	soft;
    int		goodCount;
    int		badCount;
    int		protocolID;
    int		count;
    char	data[NUM_MSE_AUTOPROBE_TOTAL];
    mseAutoProbeStates autoState;
    MouseProtocolID protoList[NUM_AUTOPROBE_PROTOS];
    int		serialDefaultsNum;
    int		prevDx, prevDy;
    int		accDx, accDy;
    int		acc;
    CARD32	pnpLast;
    Bool	disablePnPauto;
} mousePrivRec, *mousePrivPtr;

the following macros are defined also in mousePriv.h:

#define NUM_MSE_AUTOPROBE_BYTES 24  /* multiple of 3,4 and 6 byte packages */
#define NUM_MSE_AUTOPROBE_TOTAL 64

The data packet read previously with XisbRead() are now placed in the pBuf (a.k.a pMse->protoBuf), buffer:

	/*
	 * Append next byte to buffer (which is empty or contains an
	 * incomplete packet); iterate if packet (still) not complete.
	 */
	pBuf[pBufP++] = u;

The packet is then processed according to the protocol type. For instance for PS/2 we have:

	/*
	 * Packet complete and verified, now process it ...
	 */
    REDO_INTERPRET:
	dz = dw = 0;
	switch (pMse->protocolID) {
.  .  .
	case PROT_PS2:		/* PS/2 mouse */
	case PROT_GENPS2:	/* generic PS/2 mouse */
	    buttons = (pBuf[0] & 0x04) >> 1 |       /* Middle */
		      (pBuf[0] & 0x02) >> 1 |       /* Right */
		      (pBuf[0] & 0x01) << 2;        /* Left */
	    dx = (pBuf[0] & 0x10) ?    (int)pBuf[1]-256  :  (int)pBuf[1];
	    dy = (pBuf[0] & 0x20) ?  -((int)pBuf[2]-256) : -(int)pBuf[2];
	    break;

Case Study: the three bytes sent by the PS/2 mouse

Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0
Y overflowX overflowY sign bitX sign bitAlways 1Middle BtnRight BtnLeft Btn
BYTE 1
X Movement
BYTE 2
Y Movement
BYTE 3

The first byte, pBuf[0], from the mouse data packet provides the mouse buttons pressed, returned to variable buttons. If set bit 0 of the first byte indicates a left button pressed and similarly bit 1 the right button and bit 2 the middle button. Notice that buttons, the variable that holds that info stores the left byte at bit 2 the middle at bit 1 and the right at bit 0:

	    buttons = (pBuf[0] & 0x04) >> 1 |       /* Middle */
		      (pBuf[0] & 0x02) >> 1 |       /* Right */
		      (pBuf[0] & 0x01) << 2;        /* Left */

The second byte, pBuf[1], provides the X axis movement and the third, pBuf[2], the Y axis movement. They are returned to variables dx and dy respectively. If the X sign bit (0x10 hex or 10000 binary) is not set the X direction is the positive one. If the Y sign bit is not set (0x20 hex or 100000 binary) the Y direction is the negative one. Those are usually the default directions for graphics programming. We read from The PS/2 Mouse Interface:

"The movement counters are 9-bit 2's complement integers, where the most significant bit appears as a sign bit in Byte 1 of the movement data packet. These counters are updated when the mouse reads its input and finds movement has occurred. Their value is the amount of movement that has occurred since the last movement data packet was sent to the host (ie, after a packet is sent to the host, the movement counters are reset.) The range of values that can be expressed by the movement counters is -255 to +255. If this range is exceeded, the appropriate overflow bit is set."

For instance if pBuf[1] has a value of 10 using the 2's complement arithmetic the following instruction:

	    dx = (pBuf[0] & 0x10) ?    (int)pBuf[1]-256  :  (int)pBuf[1];
results to 10 if the X sign bit is not set and -10 if the it is set. More analytical:

(int)pBuf[1] results to 10 (binary 1010) and
(int)pBuf[1]-256 in binary is 000001010 + 100000000 = 100001010
Since the most significant bit of 100001010 holds the sign (minus for 1) the result is -10.

post_event:
#ifdef EXTMOUSEDEBUG
	ErrorF("dx=%i dy=%i dz=%i dw=%i buttons=%x\n",dx,dy,dz,dw,buttons);
#endif
	/* When auto-probing check if data makes sense */
	if (pMse->checkMovements && pMse->autoProbe)
	    pMse->checkMovements(pInfo,dx,dy);
	/* post an event */
	pMse->PostEvent(pInfo, buttons, dx, dy, dz, dw);

	/* 
	 * We don't reset pBufP here yet, as there may be an additional data
	 * byte in some protocols. See above.
	 */
    }
    pMse->protoBufTail = pBufP;
}

The mouse PostEvent() is called as:

pMse->PostEvent(pInfo, buttons, dx, dy, dz, dw);
to deliver the mouse movement and button pressed events to the X Client application. MousePreInit() set MousePostEvent to pMse->PostEvent (see section 3.2.3).

MousePostEvent()

MousePostEvent() is implemented as:

static void
MousePostEvent(InputInfoPtr pInfo, int truebuttons,
	       int dx, int dy, int dz, int dw)
{
    MouseDevPtr pMse;
    int zbutton = 0;
    int i, b, buttons = 0;

    pMse = pInfo->private;
    if (pMse->protocolID == PROT_MMHIT)
	b = reverseBits(hitachMap, truebuttons);
    else
	b = reverseBits(reverseMap, truebuttons);

    /* Remap mouse buttons */
    b &= (1<buttonMap[i];
       b >>= 1;
    }

    /* Map the Z axis movement. */
    /* XXX Could this go in the conversion_proc? */
    switch (pMse->negativeZ) {
    case MSE_NOZMAP:	/* do nothing */
	break;
    case MSE_MAPTOX:
	if (dz != 0) {
	    dx = dz;
	    dz = 0;
	}
	break;
    case MSE_MAPTOY:
	if (dz != 0) {
	    dy = dz;
	    dz = 0;
	}
	break;
    default:	/* buttons */
	buttons &= ~(pMse->negativeZ | pMse->positiveZ
		   | pMse->negativeW | pMse->positiveW);
	if (dw < 0 || dz < -1)
	    zbutton = pMse->negativeW;
	else if (dz < 0)
	    zbutton = pMse->negativeZ;
	else if (dw > 0 || dz > 1)
	    zbutton = pMse->positiveW;
	else if (dz > 0)
	    zbutton = pMse->positiveZ;
	buttons |= zbutton;
	dz = 0;
	break;
    }

    /* Apply angle offset */
    if (pMse->angleOffset != 0) {
	double rad = 3.141592653 * pMse->angleOffset / 180.0;
	int ndx = dx;
	dx = (int)((dx * cos(rad)) + (dy * sin(rad)) + 0.5);
	dy = (int)((dy * cos(rad)) - (ndx * sin(rad)) + 0.5);
    }

    dx = pMse->invX * dx;
    dy = pMse->invY * dy;
    if (pMse->flipXY) {
	int tmp = dx;
	dx = dy;
	dy = tmp;
    }
    MouseDoPostEvent(pInfo, buttons, dx, dy);

    /*
     * If dz has been mapped to a button `down' event, we need to cook up
     * a corresponding button `up' event.
     */
    if (zbutton) {
	buttons &= ~zbutton;
	MouseDoPostEvent(pInfo, buttons, 0, 0);
    }

    pMse->lastButtons = truebuttons;
}

Certainly the most important instruction here is MouseDoPostEvent().

Before we proceed with this routine let's have a look to the other parts of the MousePostEvent() code which takes actions when certain mouse private (pMse) fields have a non zero value:

consider for instance the next part:
    /* Apply angle offset */
    if (pMse->angleOffset != 0) {
	double rad = 3.141592653 * pMse->angleOffset / 180.0;
	int ndx = dx;
	dx = (int)((dx * cos(rad)) + (dy * sin(rad)) + 0.5);
	dy = (int)((dy * cos(rad)) - (ndx * sin(rad)) + 0.5);
    }
The "angle offset" is referred to the rotation that should be calculated when pMse->angleOffset != 0.

The following line:

double rad = 3.141592653 * pMse->angleOffset / 180.0;
calculates the number of radians from the number of degrees. The formula is
radians = degrees * PI / 180;

The following part

	dx = (int)((dx * cos(rad)) + (dy * sin(rad)) + 0.5);
	dy = (int)((dy * cos(rad)) - (ndx * sin(rad)) + 0.5);
is the transformation of dx, dy because of the rotation in the z axis. The 0.5 that is added in the end of each formula is needed for the type casting between the real and the integer (see this link).

MouseDoPostEvent()

MouseDoPostEvent() is covered in section 5.1.4.

REFERENCES

Writing Linux Mouse Drivers
More on Mouse Drivers
Moving the mouse handling code of the Xorg into a separate thread
InputEventProcessing
XorgInputHOWTO
The PS/2 Mouse Interface