这个功能的用处功能手机(这里说的功能机只是没有触屏,单还是Android系统)能在浏览器中使用上下键实现移动光标的目的,这里我们大致分析流程。
我们知道普通按键,会在KeyboardInputMapper的process执行,比如这里我们要关注的上下左右按键。
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
#if CURSOR_LOGS
LOGE("KeyboardInputMapper::process rawEvent->code =[%d], rawEvent->deviceId=[%d], "
"rawEvent->type=[%d], rawEvent->value=[%d], rawEvent->when=[%f] \n", rawEvent->code,
rawEvent->deviceId, rawEvent->type, rawEvent->value, rawEvent->when);
#endif
switch (rawEvent->type) {
case EV_KEY: {
int32_t scanCode = rawEvent->code;
int32_t usageCode = mCurrentHidUsage;
mCurrentHidUsage = 0;
if (isKeyboardOrGamepadKey(scanCode)) {
int32_t keyCode;
uint32_t flags;
if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {//通过kl将扫描码转换成按键码
keyCode = AKEYCODE_UNKNOWN;
flags = 0;
}
#ifdef SUPPORT_BROWSER_VIRTUAL_CURSOR//是否支持浏览器虚拟键
#if CURSOR_LOGS
LOGE("KeyboardInputMapper::process keyCode =[%d]", keyCode);
LOGE("KeyboardInputMapper::process down time =[%lf] and down time =[%le] \n",
rawEvent->when, rawEvent->when);
#endif
/* Reset state & allow release event only if Browser has gone to background.
& If Press event is fired already. */
if(!g_bIsBrowserAppForeground && g_bNeedsEventCompletion && AKEYCODE_ENTER == keyCode){
/* Store Browser g_bIsBrowserAppForeground state. */
g_bCursorState = g_bIsBrowserAppForeground;
/* Reset g_bIsBrowserAppForeground state to allow one last release event. */
g_bIsBrowserAppForeground = true;
}
//checking four way navigation keys to enable and control mouse pointer
if(g_bIsBrowserAppForeground && (AKEYCODE_DPAD_UP == keyCode//当前是浏览器,上下左右 确定按键过滤
|| AKEYCODE_DPAD_DOWN == keyCode
|| AKEYCODE_DPAD_LEFT == keyCode
|| AKEYCODE_DPAD_RIGHT == keyCode
|| AKEYCODE_ENTER == keyCode)) {
/* Set NeedsEventCompletion to true on Press event. */
if(AKEYCODE_ENTER == keyCode && rawEvent->value == 1) {
g_bNeedsEventCompletion = true;//确定事件,而且是down事件
}
/* Set NeedsEventCompletion to false on release event. */
if(AKEYCODE_ENTER == keyCode && rawEvent->value == 0) {
g_bNeedsEventCompletion = false;//确定键 up事件
}
HandleCursorPointerForDpad(rawEvent, keyCode);
}
else {
processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);// 如果不是浏览器,正常流程处理
}
#else
processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
#endif
}
break;
}
case EV_MSC: {
if (rawEvent->code == MSC_SCAN) {
mCurrentHidUsage = rawEvent->value;
}
break;
}
case EV_SYN: {
#ifdef SUPPORT_BROWSER_VIRTUAL_CURSOR
if(g_bIsBrowserAppForeground) {
// handling sync event for cursor pointer
HandleCursorPointerForDpad(rawEvent, AKEYCODE_UNKNOWN);
/* Hide the cursor after handling the release event for long Press */
if(!g_bCursorState && !g_bNeedsEventCompletion) {
InputMapper* mKeyboardCursorInputMapper = KeyboardInputMapper::getKeyboardCursorPointer();
if(mKeyboardCursorInputMapper) {
// fade the cursor pointer when browser app is paused.
mKeyboardCursorInputMapper->fadePointer();
}
/* Restore the state after hiding cursor*/
g_bIsBrowserAppForeground = g_bCursorState;
g_bCursorState = true;
}
}
#endif
if (rawEvent->code == SYN_REPORT) {
mCurrentHidUsage = 0;
}
}
}
}
函数HandleCursorPointerForDpad就是对长按键进行处理,然后调用了DispatchMouseEventForDPAD函数。
void KeyboardInputMapper::HandleCursorPointerForDpad(const RawEvent* KrawEvent, int32_t keyCode)
{
#if CURSOR_LOGS
LOGE("KeyboardInputMapper::HandleCursorPointerForDpad keyCode = [%d]",
keyCode);
#endif
if(keyCode != AKEYCODE_ENTER && keyCode != AKEYCODE_UNKNOWN) {
// check if key pressed
if(KrawEvent->value != 0) {
G_LPData.rawEvent = *KrawEvent ;
G_LPData.keyCode = keyCode ;
// Increase initial timeout for long press.
G_LPData.longPressTimeOut = KrawEvent->when + INITIAL_MAX_LONG_PRESS_NSECS;
G_LPData.IsOn = true ;
#if CURSOR_LOGS
LOGE("KeyboardInputMapper::HandleCursorPointerForDpad calling "
"getContext()->requestTimeoutAtTime keyCode = [%d]",keyCode);
#endif
//when key is pressed, mentaion the key state and request for timeout to handle key long press
getContext()->requestTimeoutAtTime(G_LPData.longPressTimeOut);
} else {
#if CURSOR_LOGS
LOGE("KeyboardInputMapper::HandleCursorPointerForDpad KrawEvent->when = [%le]",
KrawEvent->when);
#endif
// Reset the scroll count if key up is received.
g_bScrollCount = 0;
// here reseting the key state which was preserved when key pressed.
if(keyCode == G_LPData.keyCode && G_LPData.IsOn == true) {
G_LPData.IsOn = false;
G_LPData.keyCode = 0 ;
#if CURSOR_LOGS
LOGE("KeyboardInputMapper::HandleCursorPointerForDpad Resetting global "
"event event keyCode = [%d]",keyCode);
#endif
}
}
}
DispatchMouseEventForDPAD(mCursorInputMapper, KrawEvent, keyCode, CURSOR_MOVE_PIXELS);//最后一个参数就是每一次的步长
}
函数DispatchMouseEventForDPAD,对RawEvent重新进行封装,然后调用了CursorInputMapper::process,CursorInputMapper是专门处理光标的。
void DispatchMouseEventForDPAD(InputMapper* CursorMapper, const RawEvent* KrawEvent, int32_t keyCode, int iMovePixels)
{
/* Move the cursor only when key is pressed. On the key release,
* pass the event but do not move the cursor. */
if(KrawEvent->value == 0) {
iMovePixels = 0;
}
RawEvent rawEvent ;
rawEvent.when = KrawEvent->when;
rawEvent.deviceId = KrawEvent->deviceId;
switch(keyCode) {
case AKEYCODE_DPAD_UP:
rawEvent.type = EV_REL;
rawEvent.code = REL_Y;
rawEvent.value = -iMovePixels;//往上应该是y的坐标减步长
break;
case AKEYCODE_DPAD_DOWN:
rawEvent.type = EV_REL;
rawEvent.code = REL_Y;
rawEvent.value = iMovePixels;
break;
case AKEYCODE_DPAD_LEFT:
rawEvent.type = EV_REL;
rawEvent.code = REL_X;
rawEvent.value = -iMovePixels;
break;
case AKEYCODE_DPAD_RIGHT:
rawEvent.type = EV_REL;
rawEvent.code = REL_X;
rawEvent.value = iMovePixels;
break;
case AKEYCODE_ENTER:
rawEvent.type = EV_KEY ;
rawEvent.code = BTN_LEFT ;
rawEvent.value = KrawEvent->value;
break;
case AKEYCODE_UNKNOWN:
rawEvent.type = EV_SYN ;
rawEvent.code = KrawEvent->code ;
}
// call process of cursorinputmapper to process the mouse event constructed
CursorMapper->process(&rawEvent);
}
这个函数先调用三个Accumulator的process来保存rawEvent的值,最后调用了sync函数。
void CursorInputMapper::process(const RawEvent* rawEvent) {
mCursorButtonAccumulator.process(rawEvent);//把rawEvent的值进行保存
mCursorMotionAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
#ifdef SUPPORT_BROWSER_VIRTUAL_CURSOR
if(g_bIsBrowserAppForeground && mPointerController == NULL) {
// create and configure the pointer controller
mSource = AINPUT_SOURCE_MOUSE;
mXPrecision = 1.0f;
mYPrecision = 1.0f;
mXScale = 1.0f;
mYScale = 1.0f;
mPointerController = getPolicy()->obtainPointerController(0);//这个用来保存光标的位置信息
}
#endif
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
sync(rawEvent->when);//调用sync
}
}
前面三个Accumulator主要是对RawEvent的各个值进行保存
void CursorButtonAccumulator::process(const RawEvent* rawEvent) {
if (rawEvent->type == EV_KEY) {
switch (rawEvent->code) {
case BTN_LEFT:
mBtnLeft = rawEvent->value;
break;
case BTN_RIGHT:
mBtnRight = rawEvent->value;
break;
case BTN_MIDDLE:
mBtnMiddle = rawEvent->value;
break;
case BTN_BACK:
mBtnBack = rawEvent->value;
break;
case BTN_SIDE:
mBtnSide = rawEvent->value;
break;
case BTN_FORWARD:
mBtnForward = rawEvent->value;
break;
case BTN_EXTRA:
mBtnExtra = rawEvent->value;
break;
case BTN_TASK:
mBtnTask = rawEvent->value;
break;
}
}
}
void CursorMotionAccumulator::process(const RawEvent* rawEvent) {
if (rawEvent->type == EV_REL) {
switch (rawEvent->code) {
case REL_X:
mRelX = rawEvent->value;
break;
case REL_Y:
mRelY = rawEvent->value;
break;
}
}
}
void CursorScrollAccumulator::process(const RawEvent* rawEvent) {
if (rawEvent->type == EV_REL) {
switch (rawEvent->code) {
case REL_WHEEL:
mRelWheel = rawEvent->value;
break;
case REL_HWHEEL:
mRelHWheel = rawEvent->value;
break;
}
}
}
我们再来看下sync函数,这里主要是用PointerController来设置光标位置,最后再去notifyMotion发送MotionEvent事件。
void CursorInputMapper::sync(nsecs_t when) {
int32_t lastButtonState = mButtonState;
int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
mButtonState = currentButtonState;
bool wasDown = isPointerDown(lastButtonState);
bool down = isPointerDown(currentButtonState);
bool downChanged;
if (!wasDown && down) {
mDownTime = when;//记录DownTime
downChanged = true;
} else if (wasDown && !down) {
downChanged = true;
} else {
downChanged = false;
}
nsecs_t downTime = mDownTime;
bool buttonsChanged = currentButtonState != lastButtonState;
bool buttonsPressed = currentButtonState & ~lastButtonState;
#ifdef SUPPORT_BROWSER_VIRTUAL_CURSOR
float deltaX = mCursorMotionAccumulator.getRelativeX();//这里保存的每一次移动的步长
float deltaY = mCursorMotionAccumulator.getRelativeY();//上下左右
#else
float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
#endif
bool moved = deltaX != 0 || deltaY != 0;
#ifdef SUPPORT_BROWSER_VIRTUAL_CURSOR
// Remove orientation support as device does not support.
// if (mParameters.orientationAware && mParameters.hasAssociatedDisplay
// && (deltaX != 0.0f || deltaY != 0.0f)) {
// rotateDelta(mOrientation, &deltaX, &deltaY);
// }
#else
// Rotate delta according to orientation if needed
if (mParameters.orientationAware && mParameters.hasAssociatedDisplay
&& (deltaX != 0.0f || deltaY != 0.0f)) {
rotateDelta(mOrientation, &deltaX, &deltaY);
}
#endif
// Move the pointer.
PointerProperties pointerProperties;
pointerProperties.clear();
pointerProperties.id = 0;
pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
PointerCoords pointerCoords;
pointerCoords.clear();
float vscroll = mCursorScrollAccumulator.getRelativeVWheel();
float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
bool scrolled = vscroll != 0 || hscroll != 0;
mWheelYVelocityControl.move(when, NULL, &vscroll);
mWheelXVelocityControl.move(when, &hscroll, NULL);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
int32_t displayId;
if (mPointerController != NULL) {
if (moved || scrolled || buttonsChanged) {
mPointerController->setPresentation(
PointerControllerInterface::PRESENTATION_POINTER);
if (moved) {
mPointerController->move(deltaX, deltaY);//PointerController设置每一次移动的一个相对坐标
}
if (buttonsChanged) {
mPointerController->setButtonState(currentButtonState);
}
mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
}
float x, y;
mPointerController->getPosition(&x, &y);//获取现在光标坐标位置
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
displayId = ADISPLAY_ID_DEFAULT;
} else {
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
displayId = ADISPLAY_ID_NONE;
}
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
// Moving an external trackball or mouse should wake the device.
// We don't do this for internal cursor devices to prevent them from waking up
// the device in your pocket.
// TODO: Use the input device configuration to control this behavior more finely.
uint32_t policyFlags = 0;
if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) {
policyFlags |= POLICY_FLAG_WAKE_DROPPED;
}
// Synthesize key down from buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
policyFlags, lastButtonState, currentButtonState);
// Send motion event.
if (downChanged || moved || scrolled || buttonsChanged) {
int32_t metaState = mContext->getGlobalMetaState();
int32_t motionEventAction;
if (downChanged) {
motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
} else if (down || mPointerController == NULL) {
motionEventAction = AMOTION_EVENT_ACTION_MOVE;
} else {
motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
}
NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
motionEventAction, 0, metaState, currentButtonState, 0,
displayId, 1, &pointerProperties, &pointerCoords,
mXPrecision, mYPrecision, downTime);
getListener()->notifyMotion(&args);//发送MotionEvent
// Send hover move after UP to tell the application that the mouse is hovering now.
if (motionEventAction == AMOTION_EVENT_ACTION_UP
&& mPointerController != NULL) {
NotifyMotionArgs hoverArgs(when, getDeviceId(), mSource, policyFlags,
AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE,
displayId, 1, &pointerProperties, &pointerCoords,
mXPrecision, mYPrecision, downTime);
getListener()->notifyMotion(&hoverArgs);
}
// Send scroll events.
if (scrolled) {
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags,
AMOTION_EVENT_ACTION_SCROLL, 0, metaState, currentButtonState,
AMOTION_EVENT_EDGE_FLAG_NONE,
displayId, 1, &pointerProperties, &pointerCoords,
mXPrecision, mYPrecision, downTime);
getListener()->notifyMotion(&scrollArgs);
}
}
......
}
我们来看下这个PointerController的move函数就是将之前的坐标加上这个新的相对坐标。
void PointerController::move(float deltaX, float deltaY) {
if (deltaX == 0.0f && deltaY == 0.0f) {
return;
}
AutoMutex _l(mLock);
setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
}
setPositionLocked函数原理很简单,就是算现在的值有没有超过边界。
void PointerController::setPositionLocked(float x, float y) {
float minX, minY, maxX, maxY;
if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
if (x <= minX) {
mLocked.pointerX = minX;
} else if (x >= maxX) {
mLocked.pointerX = maxX;
} else {
mLocked.pointerX = x;
}
if (y <= minY) {
mLocked.pointerY = minY;
} else if (y >= maxY) {
mLocked.pointerY = maxY;
} else {
mLocked.pointerY = y;
}
updatePointerLocked();
}
}
updatePointerLocked函数会去绘制光标,已经更新光标在layer的位置等等。
void PointerController::updatePointerLocked() {
mSpriteController->openTransaction();
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
if (mLocked.pointerAlpha > 0) {
mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
mLocked.pointerSprite->setVisible(true);
} else {
mLocked.pointerSprite->setVisible(false);
}
if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
if (mLocked.presentation == PRESENTATION_POINTER) {
mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
} else if(mLocked.presentation == PRESENTATION_STYLUS_HOVER) {
mLocked.pointerSprite->setIcon(mLocked.hoverIcon.isValid()
? mLocked.hoverIcon : mResources.stylusHover);
} else { // PRESENTATION_SPOT
mLocked.pointerSprite->setIcon(mResources.spotAnchor);
}
mLocked.pointerIconChanged = false;
mLocked.presentationChanged = false;
}
mSpriteController->closeTransaction();
}
我们再来看getBoundsLocked函数就是获取边界,这里原先android原生就是分辨率的长和宽。也就是outMinY是0,outMaxY是mLocked.displayHeight - 1,这里我们需要适应浏览器,上面和下面(下面是状态栏,上面是导航栏吧)需要减去别的view的值,所以会根据density做适应。
bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const {
if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
return false;
}
char propBuf[PROPERTY_VALUE_MAX];
property_get("ro.sf.lcd_density", propBuf, "160");
int density = atoi(propBuf);
*outMinX = 0;
//*outMinY = 0;
/* Restricting the cursor from floating on status bar, this
* behavior is generic for all the devices that uses cursor like OTG mouse. */
*outMinY = 1 + ((float)density/160)*24;
switch (mLocked.displayOrientation) {
case DISPLAY_ORIENTATION_90:
case DISPLAY_ORIENTATION_270:
*outMaxX = mLocked.displayHeight - 1;
*outMaxY = mLocked.displayWidth - 1;
break;
default:
*outMaxX = mLocked.displayWidth - 1;
// Restricting mouse cursor from floating on the softkey menu in Browser and
// HTMLViewer app.
*outMaxY = mLocked.displayHeight - 1 - ((float)density/160)*32;
break;
}
return true;
}
其实原因就是在普通按键处理KeyboardInputMapper::process的函数中重新封装下发给CursorInputMapper处理,然后再去计算光标坐标等。