Hands-on Projects for the Linux Graphics Subsystem
|
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
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
|
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() 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< |
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() 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