IMS:InputDispatcher的焦点设置

Posted xhBruce

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IMS:InputDispatcher的焦点设置相关的知识,希望对你有一定的参考价值。

IMS:InputDispatcher的焦点设置

android11-release
IMS:InputDispatcher线程分发事件
ANR InputDispatching TimeOut超时判断


KeyEvent 和 MotionEvent中对应inputTargets


  • dispatchKeyLocked 中 focusedWindowHandle、focusedApplicationHandle判断
  • dispatchMotionLocked 中 findTouchedWindowAtLocked 查找 windowHandle
    在这里插入图片描述

焦点窗口和焦点app设置


findFocusedWindowTargetsLocked
findTouchedWindowTargetsLocked

dispatchKeyLocked 中 focusedWindowHandle、focusedApplicationHandle


findFocusedWindowTargetsLocked

    sp<InputWindowHandle> focusedWindowHandle =
            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
    sp<InputApplicationHandle> focusedApplicationHandle =
            getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
    // Focus tracking for keys, trackball, etc.
    std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
            GUARDED_BY(mLock);

    std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);

    // Focused applications.
    std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
            GUARDED_BY(mLock);

mFocusedWindowHandlesByDisplay设置

/**
 * Called from InputManagerService, update window handle list by displayId that can receive input.
 * A window handle contains information about InputChannel, Touch Region, Types, Focused,...
 * If set an empty list, remove all handles from the specific display.
 * For focused handle, check if need to change and send a cancel event to previous one.
 * For removed handle, check if need to send a cancel event if already in touch.
 */
void InputDispatcher::setInputWindowsLocked(
        const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) {
    if (DEBUG_FOCUS) {
        std::string windowList;
        for (const sp<InputWindowHandle>& iwh : inputWindowHandles) {
            windowList += iwh->getName() + " ";
        }
        ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
    }

    // Copy old handles for release if they are no longer present.
    const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);

    updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);

    sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
    bool foundHoveredWindow = false;
    for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
        // Set newFocusedWindowHandle to the top most focused window instead of the last one
        if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus &&
            windowHandle->getInfo()->visible) {
            newFocusedWindowHandle = windowHandle;
        }
        if (windowHandle == mLastHoverWindowHandle) {
            foundHoveredWindow = true;
        }
    }

    if (!foundHoveredWindow) {
        mLastHoverWindowHandle = nullptr;
    }

    sp<InputWindowHandle> oldFocusedWindowHandle =
            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);

    if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
        if (oldFocusedWindowHandle != nullptr) {
            if (DEBUG_FOCUS) {
                ALOGD("Focus left window: %s in display %" PRId32,
                      oldFocusedWindowHandle->getName().c_str(), displayId);
            }
            sp<InputChannel> focusedInputChannel =
                    getInputChannelLocked(oldFocusedWindowHandle->getToken());
            if (focusedInputChannel != nullptr) {
                CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                                           "focus left window");
                synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
                enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
            }
            mFocusedWindowHandlesByDisplay.erase(displayId);
        }
        if (newFocusedWindowHandle != nullptr) {
            if (DEBUG_FOCUS) {
                ALOGD("Focus entered window: %s in display %" PRId32,
                      newFocusedWindowHandle->getName().c_str(), displayId);
            }
            mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
            enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);
        }

        if (mFocusedDisplayId == displayId) {
            onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
        }
    }

    std::unordered_map<int32_t, TouchState>::iterator stateIt =
            mTouchStatesByDisplay.find(displayId);
    if (stateIt != mTouchStatesByDisplay.end()) {
        TouchState& state = stateIt->second;
        for (size_t i = 0; i < state.windows.size();) {
            TouchedWindow& touchedWindow = state.windows[i];
            if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
                if (DEBUG_FOCUS) {
                    ALOGD("Touched window was removed: %s in display %" PRId32,
                          touchedWindow.windowHandle->getName().c_str(), displayId);
                }
                sp<InputChannel> touchedInputChannel =
                        getInputChannelLocked(touchedWindow.windowHandle->getToken());
                if (touchedInputChannel != nullptr) {
                    CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                                               "touched window was removed");
                    synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
                }
                state.windows.erase(state.windows.begin() + i);
            } else {
                ++i;
            }
        }
    }

    // Release information for windows that are no longer present.
    // This ensures that unused input channels are released promptly.
    // Otherwise, they might stick around until the window handle is destroyed
    // which might not happen until the next GC.
    for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
        if (!hasWindowHandleLocked(oldWindowHandle)) {
            if (DEBUG_FOCUS) {
                ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
            }
            oldWindowHandle->releaseChannel();
        }
    }
}

mFocusedApplicationHandlesByDisplay设置

void InputDispatcher::setFocusedApplication(
        int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) {
    if (DEBUG_FOCUS) {
        ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId,
              inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
    }
    { // acquire lock
        std::scoped_lock _l(mLock);

        sp<InputApplicationHandle> oldFocusedApplicationHandle =
                getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);

        if (oldFocusedApplicationHandle == mAwaitedFocusedApplication &&
            inputApplicationHandle != oldFocusedApplicationHandle) {
            resetNoFocusedWindowTimeoutLocked();
        }

        if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
            if (oldFocusedApplicationHandle != inputApplicationHandle) {
                mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
            }
        } else if (oldFocusedApplicationHandle != nullptr) {
            oldFocusedApplicationHandle.clear();
            mFocusedApplicationHandlesByDisplay.erase(displayId);
        }
    } // release lock

    // Wake up poll loop since it may need to make new input dispatching choices.
    mLooper->wake();
}

findTouchedWindowTargetsLocked->findTouchedWindowAtLocked 中 windowHandles


其实和上面一样windowHandles 从mWindowHandlesByDisplay获取
在这里插入图片描述

sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
                                                                 int32_t y, TouchState* touchState,
                                                                 bool addOutsideTargets,
                                                                 bool addPortalWindows) {
    if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
        LOG_ALWAYS_FATAL(
                "Must provide a valid touch state if adding portal windows or outside targets");
    }
    // Traverse windows from front to back to find touched window.
    const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
    for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
        const InputWindowInfo* windowInfo = windowHandle->getInfo();
        if (windowInfo->displayId == displayId) {
            int32_t flags = windowInfo->layoutParamsFlags;

            if (windowInfo->visible) {
                if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
                    bool isTouchModal = (flags &
                                         (InputWindowInfo::FLAG_NOT_FOCUSABLE |
                                          InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
                    if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                        int32_t portalToDisplayId = windowInfo->portalToDisplayId;
                        if (portalToDisplayId != ADISPLAY_ID_NONE &&
                            portalToDisplayId != displayId) {
                            if (addPortalWindows) {
                                // For the monitoring channels of the display.
                                touchState->addPortalWindow(windowHandle);
                            }
                            return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,
                                                             addOutsideTargets, addPortalWindows);
                        }
                        // Found window.
                        return windowHandle;
                    }
                }

                if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
                    touchState->addOrUpdateWindow(windowHandle,
                                                  InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
                                                  BitSet32(0));
                }
            }
        }
    }
    return nullptr;
}

InputMonitor.java桥梁


frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java

上面设置窗口是InputDispatcher内部方法,实质上窗口就是WMS设置下来的,通过WMS -> InputMonitor -> InputManagerService -> InputDispatcher桥梁,在updateInputWindowsLw方法中更新。
在这里插入图片描述
InputMonitor.java中InputDispatcher连接点:
在这里插入图片描述
在这里插入图片描述

上面代码明显日志:
setFocusedApplication displayId
setInputWindows displayId
Focus left window:
Focus entered window:

以上是关于IMS:InputDispatcher的焦点设置的主要内容,如果未能解决你的问题,请参考以下文章

IMS:Input事件可拦截位置

将焦点放在 WPF ComboBox 上并不总是有效

支持鼠标滚轮图片自适应的焦点图代码等

android 中如何设置焦点的位置。

去掉谷歌浏览器获取焦点时默认的inputtextarea的边框和背景

WPF 复合控件焦点设置