功能机用上下键实现MoveEvent

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kc58236582/article/details/80687605

这个功能的用处功能手机(这里说的功能机只是没有触屏,单还是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处理,然后再去计算光标坐标等。

猜你喜欢

转载自blog.csdn.net/kc58236582/article/details/80687605