Hands-on Projects for the Linux Graphics Subsystem
|
MouseDoPostEvent() is implemented as:
/******************************************************************* * * Post mouse events * *******************************************************************/ static void MouseDoPostEvent(InputInfoPtr pInfo, int buttons, int dx, int dy) { MouseDevPtr pMse; int emulateButtons; int id, change; int emuWheelDelta, emuWheelButton, emuWheelButtonMask; int wheelButtonMask; int ms; pMse = pInfo->private; /* Do single button double click */ if (pMse->doubleClickSourceButtonMask) { if (buttons & pMse->doubleClickSourceButtonMask) { if (!(pMse->doubleClickOldSourceState)) { /* double-click button has just been pressed. Ignore it if target button * is already down. */ if (!(buttons & pMse->doubleClickTargetButtonMask)) { /* Target button isn't down, so send a double-click */ xf86PostButtonEvent(pInfo->dev, 0, pMse->doubleClickTargetButton, 1, 0, 0); xf86PostButtonEvent(pInfo->dev, 0, pMse->doubleClickTargetButton, 0, 0, 0); xf86PostButtonEvent(pInfo->dev, 0, pMse->doubleClickTargetButton, 1, 0, 0); xf86PostButtonEvent(pInfo->dev, 0, pMse->doubleClickTargetButton, 0, 0, 0); } } pMse->doubleClickOldSourceState = 1; } else pMse->doubleClickOldSourceState = 0; /* Whatever happened, mask the double-click button so it doesn't get * processed as a normal button as well. */ buttons &= ~(pMse->doubleClickSourceButtonMask); } if (pMse->emulateWheel) { /* Emulate wheel button handling */ wheelButtonMask = 1 << (pMse->wheelButton - 1); change = buttons ^ pMse->lastMappedButtons; if (change & wheelButtonMask) { if (buttons & wheelButtonMask) { /* Start timeout handling */ pMse->wheelButtonExpires = GetTimeInMillis () + pMse->wheelButtonTimeout; ms = - pMse->wheelButtonTimeout; } else { ms = pMse->wheelButtonExpires - GetTimeInMillis (); if (0 < ms) { /* * If the button is released early enough emit the button * press/release events */ xf86PostButtonEvent(pInfo->dev, 0, pMse->wheelButton, 1, 0, 0); xf86PostButtonEvent(pInfo->dev, 0, pMse->wheelButton, 0, 0, 0); } } } else ms = pMse->wheelButtonExpires - GetTimeInMillis (); /* Intercept wheel emulation. */ if (buttons & wheelButtonMask) { if (ms <= 0) { /* Y axis movement */ if (pMse->negativeY != MSE_NOAXISMAP) { pMse->wheelYDistance += dy; if (pMse->wheelYDistance < 0) { emuWheelDelta = -pMse->wheelInertia; emuWheelButton = pMse->negativeY; } else { emuWheelDelta = pMse->wheelInertia; emuWheelButton = pMse->positiveY; } emuWheelButtonMask = 1 << (emuWheelButton - 1); while (abs(pMse->wheelYDistance) > pMse->wheelInertia) { pMse->wheelYDistance -= emuWheelDelta; /* * Synthesize the press and release, but not when * the button to be synthesized is already pressed * "for real". */ if (!(emuWheelButtonMask & buttons) || (emuWheelButtonMask & wheelButtonMask)) { xf86PostButtonEvent(pInfo->dev, 0, emuWheelButton, 1, 0, 0); xf86PostButtonEvent(pInfo->dev, 0, emuWheelButton, 0, 0, 0); } } } /* X axis movement */ if (pMse->negativeX != MSE_NOAXISMAP) { pMse->wheelXDistance += dx; if (pMse->wheelXDistance < 0) { emuWheelDelta = -pMse->wheelInertia; emuWheelButton = pMse->negativeX; } else { emuWheelDelta = pMse->wheelInertia; emuWheelButton = pMse->positiveX; } emuWheelButtonMask = 1 << (emuWheelButton - 1); while (abs(pMse->wheelXDistance) > pMse->wheelInertia) { pMse->wheelXDistance -= emuWheelDelta; /* * Synthesize the press and release, but not when * the button to be synthesized is already pressed * "for real". */ if (!(emuWheelButtonMask & buttons) || (emuWheelButtonMask & wheelButtonMask)) { xf86PostButtonEvent(pInfo->dev, 0, emuWheelButton, 1, 0, 0); xf86PostButtonEvent(pInfo->dev, 0, emuWheelButton, 0, 0, 0); } } } } /* Absorb the mouse movement while the wheel button is pressed. */ dx = 0; dy = 0; } /* * Button events for the wheel button are only emitted through * the timeout code. */ buttons &= ~wheelButtonMask; } if (pMse->emulate3ButtonsSoft && pMse->emulate3Pending && (dx || dy)) buttonTimer(pInfo); if (dx || dy) xf86PostMotionEvent(pInfo->dev, 0, 0, 2, dx, dy); if (buttons != pMse->lastMappedButtons) { change = buttons ^ pMse->lastMappedButtons; /* * adjust buttons state for drag locks! * if there is drag locks */ if (pMse->pDragLock) { DragLockPtr pLock; int tarOfGoingDown, tarOfDown; int realbuttons; /* get drag lock block */ pLock = pMse->pDragLock; /* save real buttons */ realbuttons = buttons; /* if drag lock used */ /* state of drag lock buttons not seen always up */ buttons &= ~pLock->lockButtonsM; /* * if lock buttons being depressed changes state of * targets simulatedDown. */ tarOfGoingDown = lock2targetMap(pLock, realbuttons & change & pLock->lockButtonsM); pLock->simulatedDown ^= tarOfGoingDown; /* targets of drag locks down */ tarOfDown = lock2targetMap(pLock, realbuttons & pLock->lockButtonsM); /* * when simulatedDown set and target pressed, * simulatedDown goes false */ pLock->simulatedDown &= ~(realbuttons & change); /* * if master drag lock released * then master drag lock state on */ pLock->masterTS |= (~realbuttons & change) & pLock->masterLockM; /* if master state, buttons going down are simulatedDown */ if (pLock->masterTS) pLock->simulatedDown |= (realbuttons & change); /* if any button pressed, no longer in master drag lock state */ if (realbuttons & change) pLock->masterTS = 0; /* if simulatedDown or drag lock down, simulate down */ buttons |= (pLock->simulatedDown | tarOfDown); /* master button not seen */ buttons &= ~(pLock->masterLockM); /* buttons changed since last time */ change = buttons ^ pLock->lockLastButtons; /* save this time for next last time. */ pLock->lockLastButtons = buttons; } if (pMse->emulate3Buttons && (!(buttons & 0x02) || Emulate3ButtonsSoft(pInfo))) { /* handle all but buttons 1 & 3 normally */ change &= ~05; /* emulate the third button by the other two */ emulateButtons = (buttons & 01) | ((buttons &04) >> 1); if ((id = stateTab[pMse->emulateState][emulateButtons][0]) != 0) xf86PostButtonEvent(pInfo->dev, 0, abs(id), (id >= 0), 0, 0); if ((id = stateTab[pMse->emulateState][emulateButtons][1]) != 0) xf86PostButtonEvent(pInfo->dev, 0, abs(id), (id >= 0), 0, 0); pMse->emulateState = stateTab[pMse->emulateState][emulateButtons][2]; if (stateTab[pMse->emulateState][4][0] != 0) { pMse->emulate3Expires = GetTimeInMillis () + pMse->emulate3Timeout; pMse->emulate3Pending = TRUE; } else { pMse->emulate3Pending = FALSE; } } while (change) { id = ffs(change); change &= ~(1 << (id - 1)); xf86PostButtonEvent(pInfo->dev, 0, id, (buttons & (1 << (id - 1))), 0, 0); } pMse->lastMappedButtons = buttons; } } |
The main parts of the previous function are:
if (dx || dy) xf86PostMotionEvent(pInfo->dev, 0, 0, 2, dx, dy);This is straightforward. If there is either movement to the X axis (dx), or to the Y axis (dy) xf86PostMotionEvent() is called. This function is examined in the paragraphs that follow.
The other part is:
if (buttons != pMse->lastMappedButtons) { change = buttons ^ pMse->lastMappedButtons; . . . while (change) { id = ffs(change); change &= ~(1 << (id - 1)); xf86PostButtonEvent(pInfo->dev, 0, id, (buttons & (1 << (id - 1))), 0, 0); } pMse->lastMappedButtons = buttons;
The XOR operator (^) is used to find the buttons changed state. Recall from section 5.1.3 that variable 'buttons' holds a pressed left, middle and right button at bit position 2, 1 and 0 respectively.
The ffs() function returns the position of the first (least significant) bit set. It considers that the least significant bit is at position 1 (instead of 0 we count it). Then with the following instruction:
change &= ~(1 << (id - 1));change is updated to clear that bit, since xf86PostButtonEvent() that follows right next informs the X client process for the certain button. The whole process continues until the whole 'change' is cleared.
The two routines, used to post motion and buttons pressed are as we previously saw xf86PostMotionEvent() and xf86PostButtonEvent(). They are discussed next:
xf86PostMotionEvent() is implemented as:
void xf86PostMotionEvent(DeviceIntPtr device, int is_absolute, int first_valuator, int num_valuators, ...) { va_list var; int loop; xEvent xE[2]; deviceKeyButtonPointer *xev = (deviceKeyButtonPointer*) xE; deviceValuator *xv = (deviceValuator*) xev+1; LocalDevicePtr local = (LocalDevicePtr) device->public.devicePrivate; char *buff = 0; Time current; Bool is_core = xf86IsCorePointer(device); Bool is_shared = xf86ShareCorePointer(device); Bool drag = xf86SendDragEvents(device); ValuatorClassPtr val = device->valuator; int valuator[6]; int oldaxis[6]; int *axisvals; int dx = 0, dy = 0; float mult; int x, y; int loop_start; int i; int num; DBG(5, ErrorF("xf86PostMotionEvent BEGIN 0x%x(%s) is_core=%s is_shared=%s is_absolute=%s\n", device, device->name, is_core ? "True" : "False", is_shared ? "True" : "False", is_absolute ? "True" : "False")); xf86Info.lastEventTime = xev->time = current = GetTimeInMillis(); if (!is_core) { if (HAS_MOTION_HISTORY(local)) { buff = ((char *)local->motion_history + (sizeof(INT32) * local->dev->valuator->numAxes + sizeof(Time)) * local->last); } } if (num_valuators && (!val || (first_valuator + num_valuators > val->numAxes))) { ErrorF("Bad valuators reported for device \"%s\"\n", device->name); return; } axisvals = val->axisVal; va_start(var, num_valuators); loop_start = first_valuator; for(loop=0; loop < num_valuators; loop++) { valuator[loop%6] = va_arg(var,int); if (loop % 6 == 5 || loop == num_valuators - 1) { num = loop % 6 + 1; /* * Adjust first two relative valuators */ if (!is_absolute && num_valuators >= 2 && loop_start == 0) { dx = valuator[0]; dy = valuator[1]; /* * Accelerate */ if (device->ptrfeed && device->ptrfeed->ctrl.num) { /* modeled from xf86Events.c */ if (device->ptrfeed->ctrl.threshold) { if ((abs(dx) + abs(dy)) >= device->ptrfeed->ctrl.threshold) { local->dxremaind = ((float)dx * (float)(device->ptrfeed->ctrl.num)) / (float)(device->ptrfeed->ctrl.den) + local->dxremaind; valuator[0] = (int)local->dxremaind; local->dxremaind = local->dxremaind - (float)valuator[0]; local->dyremaind = ((float)dy * (float)(device->ptrfeed->ctrl.num)) / (float)(device->ptrfeed->ctrl.den) + local->dyremaind; valuator[1] = (int)local->dyremaind; local->dyremaind = local->dyremaind - (float)valuator[1]; } } else if (dx || dy) { mult = pow((float)(dx*dx+dy*dy), ((float)(device->ptrfeed->ctrl.num) / (float)(device->ptrfeed->ctrl.den) - 1.0) / 2.0) / 2.0; if (dx) { local->dxremaind = mult * (float)dx + local->dxremaind; valuator[0] = (int)local->dxremaind; local->dxremaind = local->dxremaind - (float)valuator[0]; } if (dy) { local->dyremaind = mult * (float)dy + local->dyremaind; valuator[1] = (int)local->dyremaind; local->dyremaind = local->dyremaind - (float)valuator[1]; } } DBG(6, ErrorF("xf86PostMotionEvent acceleration v0=%d v1=%d\n", valuator[0], valuator[1])); } /* * Map current position back to device space in case * the cursor was warped */ if (is_core || is_shared) { miPointerPosition (&x, &y); if (local->reverse_conversion_proc) (*local->reverse_conversion_proc)(local, x, y, axisvals); else { axisvals[0] = x; axisvals[1] = y; } } } /* * Update axes */ for (i = 0; i < num; i++) { oldaxis[i] = axisvals[loop_start + i]; if (is_absolute) axisvals[loop_start + i] = valuator[i]; else axisvals[loop_start + i] += valuator[i]; } /* * Deliver extension event */ if (!is_core) { xev->type = DeviceMotionNotify; xev->detail = 0; xev->deviceid = device->id | MORE_EVENTS; xv->type = DeviceValuator; xv->deviceid = device->id; xv->device_state = 0; xv->num_valuators = num; xv->first_valuator = loop_start; memcpy (&xv->valuator0, &axisvals[loop_start], sizeof(INT32)*xv->num_valuators); if (HAS_MOTION_HISTORY(local)) { *(Time*)buff = current; memcpy(buff+sizeof(Time)+sizeof(INT32)*xv->first_valuator, &axisvals[loop_start], sizeof(INT32)*xv->num_valuators); } ENQUEUE(xE); } /* * Deliver core event */ if (is_core || (is_shared && num_valuators >= 2 && loop_start == 0)) { #ifdef XFreeXDGA /* * Let DGA peek at the event and steal it */ xev->type = MotionNotify; xev->detail = 0; if (is_absolute) { dx = axisvals[0] - oldaxis[0]; dy = axisvals[1] - oldaxis[1]; } if (DGAStealMouseEvent(xf86EventQueue.pEnqueueScreen->myNum, xE, dx, dy)) continue; #endif if (!(*local->conversion_proc)(local, loop_start, num, axisvals[0], axisvals[1], axisvals[2], axisvals[3], axisvals[4], axisvals[5], &x, &y)) continue; if (drag) miPointerAbsoluteCursor (x, y, current); /* * Retrieve the position */ miPointerPosition (&x, &y); if (local->reverse_conversion_proc) (*local->reverse_conversion_proc)(local, x, y, axisvals); else { axisvals[0] = x; axisvals[1] = y; } } loop_start += 6; } } va_end(var); if (HAS_MOTION_HISTORY(local)) { local->last = (local->last + 1) % device->valuator->numMotionEvents; if (local->last == local->first) local->first = (local->first + 1) % device->valuator->numMotionEvents; } DBG(5, ErrorF("xf86PostMotionEvent END 0x%x(%s) is_core=%s is_shared=%s\n", device, device->name, is_core ? "True" : "False", is_shared ? "True" : "False")); } |
With xf86PostMotionEvent() we cover only the core events, events that come from the core mouse, and not the extension (Xinput) events (see X Server Input Event generation and processing). Core events are sent if 'drag' is true. 'Drag' is occurred when the mouse cursor is moved and at the same time a mouse button is pressed. The first 'for' loop runs once for our case (core devices) and runs more than one in the case of devices that use more than 6 valuators.
Does this mean that in order to find the mouse coordinates in the X Window system a mouse button should be pressed?
Many programs e.g. image manipulating applications require that mouse coordinates should be updated at the screen all the time. Which is the solution then? At this link XTestFakeButtonEvent() is used to simulate fake mouse press and release button events. XTestFakeButtonEvent() has its own man page |
Variable drag derives from:
Bool drag = xf86SendDragEvents(device);
xf86SendDragEvents() is implemented as:
static Bool xf86SendDragEvents(DeviceIntPtr device) { LocalDevicePtr local = (LocalDevicePtr) device->public.devicePrivate; if (inputInfo.pointer->button->buttonsDown > 0) return (local->flags & XI86_SEND_DRAG_EVENTS); else return (TRUE); } |
where inputInfo.pointer->button->buttonsDown is set to 1 by ProcessPointerEvent() (see section 3.2.5.1 at the 'case ButtonPress'. Also XI86_SEND_DRAG_EVENTS is set by xf86ProcessCommonOptions() , called by MousePreInit() (see section 3.2.3) if the "SendDragEvents" option is set in xorg.conf.
MouseConvert() is implemented as:
/* *************************************************************************** * * MouseConvert -- * Convert valuators to X and Y. * *************************************************************************** */ static Bool MouseConvert(InputInfoPtr pInfo, int first, int num, int v0, int v1, int v2, int v3, int v4, int v5, int *x, int *y) { if (first != 0 || num != 2) return FALSE; *x = v0; *y = v1; return TRUE; } |
miPointerAbsoluteCursor() is implemented as:
/* * miPointerAbsoluteCursor. The pointer has moved to x,y */ void miPointerAbsoluteCursor (x, y, time) int x, y; unsigned long time; { miPointerScreenPtr pScreenPriv; ScreenPtr pScreen; ScreenPtr newScreen; pScreen = miPointer.pScreen; if (!pScreen) return; /* called before ready */ if (x < 0 || x >= pScreen->width || y < 0 || y >= pScreen->height) { pScreenPriv = GetScreenPrivate (pScreen); if (!miPointer.confined) { newScreen = pScreen; (*pScreenPriv->screenFuncs->CursorOffScreen) (&newScreen, &x, &y); if (newScreen != pScreen) { pScreen = newScreen; (*pScreenPriv->screenFuncs->NewEventScreen) (pScreen, FALSE); pScreenPriv = GetScreenPrivate (pScreen); /* Smash the confine to the new screen */ miPointer.limits.x2 = pScreen->width; miPointer.limits.y2 = pScreen->height; } } } /* * constrain the hot-spot to the current * limits */ if (x < miPointer.limits.x1) x = miPointer.limits.x1; if (x >= miPointer.limits.x2) x = miPointer.limits.x2 - 1; if (y < miPointer.limits.y1) y = miPointer.limits.y1; if (y >= miPointer.limits.y2) y = miPointer.limits.y2 - 1; if (miPointer.x == x && miPointer.y == y && miPointer.pScreen == pScreen) return; miPointerMove (pScreen, x, y, time); } |
miPointerPosition() is implemented as;
void miPointerPosition (x, y) int *x, *y; { *x = miPointer.x; *y = miPointer.y; } |
miPointerMove() is implemented as:
/* * miPointerMove. The pointer has moved to x,y on current screen */ static void miPointerMove (pScreen, x, y, time) ScreenPtr pScreen; int x, y; unsigned long time; { SetupScreen(pScreen); xEvent xE; miHistoryPtr history; int prev, end, start; if (!pScreenPriv->waitForUpdate && pScreen == miPointer.pSpriteScreen) { miPointer.devx = x; miPointer.devy = y; if(!miPointer.pCursor->bits->emptyMask) (*pScreenPriv->spriteFuncs->MoveCursor) (pScreen, x, y); } miPointer.x = x; miPointer.y = y; miPointer.pScreen = pScreen; xE.u.u.type = MotionNotify; xE.u.keyButtonPointer.rootX = x; xE.u.keyButtonPointer.rootY = y; xE.u.keyButtonPointer.time = time; (*pScreenPriv->screenFuncs->EnqueueEvent) (&xE); end = miPointer.history_end; start = miPointer.history_start; prev = end - 1; if (end == 0) prev = MOTION_SIZE - 1; history = &miPointer.history[prev]; if (end == start || history->event.time != time) { history = &miPointer.history[end]; if (++end == MOTION_SIZE) end = 0; if (end == start) { start = end + 1; if (start == MOTION_SIZE) start = 0; miPointer.history_start = start; } miPointer.history_end = end; } history->event.x = x; history->event.y = y; history->event.time = time; history->pScreen = pScreen; } |
xf86PostButtonEvent() is implemented as:
void xf86PostButtonEvent(DeviceIntPtr device, int is_absolute, int button, int is_down, int first_valuator, int num_valuators, ...) { va_list var; int loop; xEvent xE[2]; deviceKeyButtonPointer *xev = (deviceKeyButtonPointer*) xE; deviceValuator *xv = (deviceValuator*) xev+1; ValuatorClassPtr val = device->valuator; Bool is_core = xf86IsCorePointer(device); Bool is_shared = xf86ShareCorePointer(device); DBG(5, ErrorF("xf86PostButtonEvent BEGIN 0x%x(%s) button=%d down=%s |
In xf86PostButtonEvent()we also cover only the core event part of the code (ommiting the Xinput event part).
xf86CheckButton() for the specific button (bit in button) adds it to static int variable xf86CoreButtonState. This is initial zero and also the first time the button is used it is pressed (contrary to released). Therefore the first time the check:
check = xf86CoreButtonState & bit;is zero and the first part of the condition (therefore all the condition):
if ((check && down) || (!check && !down)) {becomes true. The next time with the instruction:
xf86CoreButtonState ^= bitthe xf86CoreButtonState bit becomes 1 and if all work right the next operation in the button is to be released and the second part of the condition becomes true and so on.
xf86CheckButton() is implemented as:
static int xf86CoreButtonState; /*********************************************************************** * * xf86CheckButton -- * * Test if the core pointer button state is coherent with * the button event to send. * *********************************************************************** */ Bool xf86CheckButton(int button, int down) { int check; int bit = (1 << (button - 1)); check = xf86CoreButtonState & bit; DBG(5, ErrorF("xf86CheckButton " "button=%d down=%d state=%d check=%d returns ", button, down, xf86CoreButtonState, check)); if ((check && down) || (!check && !down)) { DBG(5, ErrorF("FALSE\n")); return FALSE; } xf86CoreButtonState ^= bit; DBG(5, ErrorF("TRUE\n")); return TRUE; } |
The ButtonPress or ButtonRelease event is then constructed and enters the miEventQueue via mieqEnqueue().
Macro ENQUEUE is defined in xf86Events.c as:
#define ENQUEUE(ev, code, direction, dev_type) \ (ev)->u.u.detail = (code); \ (ev)->u.u.type = (direction); \ EqEnqueue((ev)) |
where __EqEnqueue() is defined in the same file as:
#ifdef XINPUT #define __EqEnqueue(ev) xf86eqEnqueue(ev) #else #define __EqEnqueue(ev) mieqEnqueue(ev) #endif |
REFERENCES
Input Event Processing
X Server Input Event generation and processing
X.Org Manual pages: Section 4
Information about the XInput extension in XFree86
AIXwindows Programming Guide