这个功能的用处功能手机(这里说的功能机只是没有触屏,单还是Android系统)能在浏览器中使用上下键实现移动光标的目的,这里我们大致分析流程。

我们知道普通按键,会在KeyboardInputMapper的process执行,比如这里我们要关注的上下左右按键。

void KeyboardInputMapper::process(const RawEvent* rawEvent) {
#if CURSOR_LOGSLOGE("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);
#endifswitch (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_LOGSLOGE("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 pointerif(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);// 如果不是浏览器,正常流程处理}
#elseprocessKey(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_CURSORif(g_bIsBrowserAppForeground) {// handling sync event for cursor pointerHandleCursorPointerForDpad(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;}}
#endifif (rawEvent->code == SYN_REPORT) {mCurrentHidUsage = 0;}}}
}

函数HandleCursorPointerForDpad就是对长按键进行处理,然后调用了DispatchMouseEventForDPAD函数。

void KeyboardInputMapper::HandleCursorPointerForDpad(const RawEvent* KrawEvent, int32_t keyCode)
{
#if CURSOR_LOGSLOGE("KeyboardInputMapper::HandleCursorPointerForDpad keyCode = [%d]",keyCode);
#endifif(keyCode != AKEYCODE_ENTER && keyCode != AKEYCODE_UNKNOWN) {// check if key pressedif(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_LOGSLOGE("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 pressgetContext()->requestTimeoutAtTime(G_LPData.longPressTimeOut);} else {
#if CURSOR_LOGSLOGE("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_LOGSLOGE("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 constructedCursorMapper->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_CURSORif(g_bIsBrowserAppForeground && mPointerController == NULL) {// create and configure the pointer controllermSource = AINPUT_SOURCE_MOUSE;mXPrecision = 1.0f;mYPrecision = 1.0f;mXScale = 1.0f;mYScale = 1.0f;mPointerController = getPolicy()->obtainPointerController(0);//这个用来保存光标的位置信息}
#endifif (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;//记录DownTimedownChanged = 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_CURSORfloat deltaX = mCursorMotionAccumulator.getRelativeX();//这里保存的每一次移动的步长float deltaY = mCursorMotionAccumulator.getRelativeY();//上下左右
#elsefloat deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
#endifbool 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 neededif (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_SPOTmLocked.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处理,然后再去计算光标坐标等。

功能机用上下键实现MoveEvent相关推荐

  1. 功能机也不放过,谷歌或为 Chrome 提供非触控模式

    百度智能云 云生态狂欢季 热门云产品1折起>>>   目前 Chrome 浏览器几乎已支持所有的操作系统,例如 Windows.macOS.Linux.Android.Fuchsia ...

  2. 展讯功能机平台MMK消息流转

    展讯功能机平台是一种消息驱动机制.各窗口(控件)向MMI kernel发送消息.然后MMI kernel通过消息分发,分发到对应的handle处理.下面将以按键消息为例,大致的了解一下展讯平台的消息机 ...

  3. MTK 功能机 MMI绘画

    MMI架构及基础知识 MMI全称Man Macheine Interface(人机接口或人机界面).人机界面分为文件界面(如DOS)和图形界面(如Windows)两种类型,功能机的平台属于简单的图形界 ...

  4. delphi trackbar 判断停止拖动_飞智八爪鱼2首发评测,可拖动轮盘,全机高达27键,这波太秀了...

    说到手游外设,相信大家第一时间想到肯定是手游外设领跑者"飞智科技".自从手游火爆以来,推出了不少手游外设,几乎经常能看到他们推出新品,特别是今年更是新品不断,继上次银狐T1真无线蓝 ...

  5. 富士施乐推出“智能工作平台”旗舰产品 10款智能型彩色数码多功能机上市

    2019年1月9日,富士施乐(中国)有限公司在京召开了"'智能工作平台'旗舰新品发布会",推出智能型彩色数码多功能机ApeosPort-VII/DocuCentre-VII系列新品 ...

  6. Electron Cash发布功能机BCH钱包,低端市场利好来袭

    Electron Cash,是比特币现金(BCH)坚定支持者CoinGeek所投资开发的BCH区块链项目,每年投资最高达30万美元. Electron Cash是专门为BCH制造的一个轻便钱包,与多数 ...

  7. 小米功能机支持java吗_小米竟然卖功能机了!2.8吋/15天超长待机

    [手机中国 新闻]众多周知,小米是从智能手机起家的,对于功能机从未涉足.但自从有了强大的小米生态链,制造各种科技产品那都不是事儿了.8月2日上午10点,小米有品众筹频道上线了一款功能手机--QIN多亲 ...

  8. 计算机卡住了怎样恢复,电脑死机按什么键恢复

    描述 如何判断电脑是否死机 判断机器处于死机状态.状态的方法是按键的NumLock键,看Num灯是不是会随着键的按下改变状态.如果不改变,那么就是处于死机状态. 电脑死机按什么键恢复 1.按CTRL+ ...

  9. MacBook Pro死机强制重启键

    MacBook Pro死机强制重启键 背景 之前开发项目一直在用MacBook,有时候项目一起运行一整天,连续一周不关机,感觉电脑很容易死机 重启键 按住control + command健 再点击右 ...

  10. 飞利浦e570有JAVA吗_功能机怎么了?飞利浦E570的待机长达170天

    原标题:功能机怎么了?飞利浦E570的待机长达170天 虽然现如今智能手机已经成为了手机市场中的绝对主流,但是这并不意味着当年的功能机就已经被市场与用户彻底淘汰.其实现在回头来看功能机与智能手机的话, ...

最新文章

  1. 2022-2028年中国新能源环卫车行业深度调研与投资战略规划分析报告
  2. iPhone 隐私新规下的“大地震”:四大平台损失近百亿美元,“连用户是男是女都分不清……”
  3. python输入函数格式_python如何提取.c文件中的指定函数的输入参数
  4. VSCODE常见问题(设置为4个空格)
  5. android源码编译 简书,android学习笔记之源码编译
  6. Special Judge Ⅲ(这道题考的就是出栈序列判定_关键代码不差什么)
  7. Redis的启动和关闭(前台启动和后台启动)
  8. 8086汇编-实验8-jmp指令的理解
  9. WIN7做无线路由供手机上网
  10. Find The Multiple POJ - 1426 (BFS)
  11. python---之[::-1]
  12. sed 、awk用法
  13. python 灰度图像_Python灰度图像到3个通道
  14. 图像扭曲(仿射变换)
  15. Mysql 杀死进程 | 解决Lock wait timeout exceeded
  16. swf格式视频播放器、免费绿色免安装
  17. 苹果太狠了:升级iOS 8小心变砖
  18. Python 可视化神器--Plotly
  19. 《爬虫爬 wallhaven.cc壁纸》
  20. 尾波冲浪流行起来了,水上运动“圣地”三亚火出圈

热门文章

  1. 如何查找专栏(知乎专栏汇总)
  2. ALSA音频框架理解:machine
  3. i3wm中Chrome不保存密码
  4. 高等数学张宇18讲 第五讲 中值定理
  5. C语言使用信号量(Linux)
  6. node2vec 包安装
  7. svn server 搭建
  8. 非晶金属模型建模:Ovito方法
  9. 永磁同步电机矢量控制(四)——simulink仿真搭建
  10. iOS下Safari自动化测试