Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b096ea0d authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Track hovering pointers explicitly -- try 2"

parents e3f93479 b581f7f0
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#pragma once

#include <bitset>
#include <map>
#include <optional>
#include <set>
@@ -23,6 +24,11 @@

namespace android {

template <size_t N>
std::string bitsetToString(const std::bitset<N>& bitset) {
    return bitset.to_string();
}

template <typename T>
inline std::string constToString(const T& v) {
    return std::to_string(v);
+103 −64
Original line number Diff line number Diff line
@@ -554,6 +554,68 @@ std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) {
    return std::nullopt;
}

/**
 * Compare the old touch state to the new touch state, and generate the corresponding touched
 * windows (== input targets).
 * If a window had the hovering pointer, but now it doesn't, produce HOVER_EXIT for that window.
 * If the pointer just entered the new window, produce HOVER_ENTER.
 * For pointers remaining in the window, produce HOVER_MOVE.
 */
std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState,
                                                    const TouchState& newTouchState,
                                                    const MotionEntry& entry) {
    std::vector<TouchedWindow> out;
    const int32_t maskedAction = MotionEvent::getActionMasked(entry.action);
    if (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER &&
        maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE &&
        maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
        // Not a hover event - don't need to do anything
        return out;
    }

    // We should consider all hovering pointers here. But for now, just use the first one
    const int32_t pointerId = entry.pointerProperties[0].id;

    std::set<sp<WindowInfoHandle>> oldWindows;
    if (oldState != nullptr) {
        oldWindows = oldState->getWindowsWithHoveringPointer(entry.deviceId, pointerId);
    }

    std::set<sp<WindowInfoHandle>> newWindows =
            newTouchState.getWindowsWithHoveringPointer(entry.deviceId, pointerId);

    // If the pointer is no longer in the new window set, send HOVER_EXIT.
    for (const sp<WindowInfoHandle>& oldWindow : oldWindows) {
        if (newWindows.find(oldWindow) == newWindows.end()) {
            TouchedWindow touchedWindow;
            touchedWindow.windowHandle = oldWindow;
            touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_HOVER_EXIT;
            touchedWindow.pointerIds.markBit(pointerId);
            out.push_back(touchedWindow);
        }
    }

    for (const sp<WindowInfoHandle>& newWindow : newWindows) {
        TouchedWindow touchedWindow;
        touchedWindow.windowHandle = newWindow;
        if (oldWindows.find(newWindow) == oldWindows.end()) {
            // Any windows that have this pointer now, and didn't have it before, should get
            // HOVER_ENTER
            touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_HOVER_ENTER;
        } else {
            // This pointer was already sent to the window. Use ACTION_HOVER_MOVE.
            LOG_ALWAYS_FATAL_IF(maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE);
            touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_IS;
        }
        touchedWindow.pointerIds.markBit(pointerId);
        if (canReceiveForegroundTouches(*newWindow->getInfo())) {
            touchedWindow.targetFlags |= InputTarget::Flags::FOREGROUND;
        }
        out.push_back(touchedWindow);
    }
    return out;
}

} // namespace

// --- InputDispatcher ---
@@ -2089,8 +2151,6 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(

    // Update the touch state as needed based on the properties of the touch event.
    outInjectionResult = InputEventInjectionResult::PENDING;
    sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle);
    sp<WindowInfoHandle> newTouchedWindowHandle;

    // Copy current touch state into tempTouchState.
    // This state will be used to update mTouchStatesByDisplay at the end of this function.
@@ -2123,7 +2183,7 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
            outInjectionResult = InputEventInjectionResult::FAILED;
            return touchedWindows; // wrong device
        }
        tempTouchState.reset();
        tempTouchState.clearWindowsWithoutPointers();
        tempTouchState.deviceId = entry.deviceId;
        tempTouchState.source = entry.source;
        isSplit = false;
@@ -2136,14 +2196,21 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
        return touchedWindows; // wrong device
    }

    if (isHoverAction) {
        // For hover actions, we will treat 'tempTouchState' as a new state, so let's erase
        // all of the existing hovering pointers and recompute.
        tempTouchState.clearHoveringPointers();
    }

    if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
        /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
        const auto [x, y] = resolveTouchedPosition(entry);
        const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
        const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
        const bool isStylus = isPointerFromStylus(entry, pointerIndex);
        newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
                                                           isStylus, isDown /*addOutsideTargets*/);
        sp<WindowInfoHandle> newTouchedWindowHandle =
                findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus,
                                          isDown /*addOutsideTargets*/);

        // Handle the case where we did not find a window.
        if (newTouchedWindowHandle == nullptr) {
@@ -2178,15 +2245,6 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
            isSplit = !isFromMouse;
        }

        // Update hover state.
        if (newTouchedWindowHandle != nullptr) {
            if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
                newHoverWindowHandle = nullptr;
            } else if (isHoverAction) {
                newHoverWindowHandle = newTouchedWindowHandle;
            }
        }

        std::vector<sp<WindowInfoHandle>> newTouchedWindows =
                findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus);
        if (newTouchedWindowHandle != nullptr) {
@@ -2206,6 +2264,18 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
                continue;
            }

            if (isHoverAction) {
                const int32_t pointerId = entry.pointerProperties[0].id;
                if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
                    // Pointer left. Remove it
                    tempTouchState.removeHoveringPointer(entry.deviceId, pointerId);
                } else {
                    // The "windowHandle" is the target of this hovering pointer.
                    tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId,
                                                              pointerId);
                }
            }

            // Set target flags.
            ftl::Flags<InputTarget::Flags> targetFlags = InputTarget::Flags::DISPATCH_AS_IS;

@@ -2225,7 +2295,9 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(

            // Update the temporary touch state.
            BitSet32 pointerIds;
            if (!isHoverAction) {
                pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
            }

            tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds,
                                             entry.eventTime);
@@ -2287,7 +2359,7 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
            const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/);
            sp<WindowInfoHandle> oldTouchedWindowHandle =
                    tempTouchState.getFirstForegroundWindowHandle();
            newTouchedWindowHandle =
            sp<WindowInfoHandle> newTouchedWindowHandle =
                    findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus);

            // Verify targeted injection.
@@ -2362,36 +2434,11 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
    }

    // Update dispatching for hover enter and exit.
    if (newHoverWindowHandle != mLastHoverWindowHandle) {
        // Let the previous window know that the hover sequence is over, unless we already did
        // it when dispatching it as is to newTouchedWindowHandle.
        if (mLastHoverWindowHandle != nullptr &&
            (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT ||
             mLastHoverWindowHandle != newTouchedWindowHandle)) {
            if (DEBUG_HOVER) {
                ALOGD("Sending hover exit event to window %s.",
                      mLastHoverWindowHandle->getName().c_str());
            }
            tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
                                             InputTarget::Flags::DISPATCH_AS_HOVER_EXIT,
                                             BitSet32(0));
        }

        // Let the new window know that the hover sequence is starting, unless we already did it
        // when dispatching it as is to newTouchedWindowHandle.
        if (newHoverWindowHandle != nullptr &&
            (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER ||
             newHoverWindowHandle != newTouchedWindowHandle)) {
            if (DEBUG_HOVER) {
                ALOGD("Sending hover enter event to window %s.",
                      newHoverWindowHandle->getName().c_str());
            }
            tempTouchState.addOrUpdateWindow(newHoverWindowHandle,
                                             InputTarget::Flags::DISPATCH_AS_HOVER_ENTER,
                                             BitSet32(0));
        }
    {
        std::vector<TouchedWindow> hoveringWindows =
                getHoveringWindowsLocked(oldState, tempTouchState, entry);
        touchedWindows.insert(touchedWindows.end(), hoveringWindows.begin(), hoveringWindows.end());
    }

    // Ensure that we have at least one foreground window or at least one window that cannot be a
    // foreground target. If we only have windows that are not receiving foreground touches (e.g. we
    // only have windows getting ACTION_OUTSIDE), then drop the event, because there is no window
@@ -2449,10 +2496,13 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
        }
    }

    // Success!  Output targets.
    touchedWindows = tempTouchState.windows;
    outInjectionResult = InputEventInjectionResult::SUCCEEDED;
    // Success!  Output targets for everything except hovers.
    if (!isHoverAction) {
        touchedWindows.insert(touchedWindows.end(), tempTouchState.windows.begin(),
                              tempTouchState.windows.end());
    }

    outInjectionResult = InputEventInjectionResult::SUCCEEDED;
    // Drop the outside or hover touch windows since we will not care about them
    // in the next iteration.
    tempTouchState.filterNonAsIsTouchWindows();
@@ -2473,14 +2523,16 @@ Failed:
                     "Conflicting pointer actions: Hover received while pointer was down.");
            *outConflictingPointerActions = true;
        }
        tempTouchState.reset();
        if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
            maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
            tempTouchState.deviceId = entry.deviceId;
            tempTouchState.source = entry.source;
        }
    } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
               maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
    } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
        // Pointer went up.
        tempTouchState.removeTouchedPointer(entry.pointerProperties[0].id);
        tempTouchState.clearWindowsWithoutPointers();
    } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
        // All pointers up or canceled.
        tempTouchState.reset();
    } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
@@ -2519,9 +2571,6 @@ Failed:
        mTouchStatesByDisplay.erase(displayId);
    }

    // Update hover state.
    mLastHoverWindowHandle = newHoverWindowHandle;

    return touchedWindows;
}

@@ -4824,14 +4873,6 @@ void InputDispatcher::setInputWindowsLocked(
    updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);

    const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
    if (mLastHoverWindowHandle) {
        const WindowInfo* lastHoverWindowInfo = mLastHoverWindowHandle->getInfo();
        if (lastHoverWindowInfo->displayId == displayId &&
            std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
                    windowHandles.end()) {
            mLastHoverWindowHandle = nullptr;
        }
    }

    std::optional<FocusResolver::FocusChanges> changes =
            mFocusResolver.setInputWindows(displayId, windowHandles);
@@ -5278,7 +5319,6 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {

    mAnrTracker.clear();
    mTouchStatesByDisplay.clear();
    mLastHoverWindowHandle.clear();
    mReplacedKeys.clear();
}

@@ -6468,7 +6508,6 @@ void InputDispatcher::cancelCurrentTouch() {
        synthesizeCancelationEventsForAllConnectionsLocked(options);

        mTouchStatesByDisplay.clear();
        mLastHoverWindowHandle.clear();
    }
    // Wake up poll loop since there might be work to do.
    mLooper->wake();
+0 −3
Original line number Diff line number Diff line
@@ -530,9 +530,6 @@ private:
    // prevent unneeded wakeups.
    AnrTracker mAnrTracker GUARDED_BY(mLock);

    // Contains the last window which received a hover event.
    sp<android::gui::WindowInfoHandle> mLastHoverWindowHandle GUARDED_BY(mLock);

    void cancelEventsForAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
    // If a focused application changes, we should stop counting down the "no focused window" time,
    // because we will have no way of knowing when the previous application actually added a window.
+55 −0
Original line number Diff line number Diff line
@@ -31,10 +31,30 @@ void TouchState::reset() {
    *this = TouchState();
}

void TouchState::removeTouchedPointer(int32_t pointerId) {
    for (TouchedWindow& touchedWindow : windows) {
        touchedWindow.pointerIds.clearBit(pointerId);
    }
}

void TouchState::clearHoveringPointers() {
    for (TouchedWindow& touchedWindow : windows) {
        touchedWindow.clearHoveringPointers();
    }
}

void TouchState::clearWindowsWithoutPointers() {
    std::erase_if(windows, [](const TouchedWindow& w) {
        return w.pointerIds.isEmpty() && !w.hasHoveringPointers();
    });
}

void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
                                   ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
                                   std::optional<nsecs_t> eventTime) {
    for (TouchedWindow& touchedWindow : windows) {
        // We do not compare windows by token here because two windows that share the same token
        // may have a different transform
        if (touchedWindow.windowHandle == windowHandle) {
            touchedWindow.targetFlags |= targetFlags;
            if (targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
@@ -59,6 +79,21 @@ void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
    windows.push_back(touchedWindow);
}

void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
                                            int32_t hoveringDeviceId, int32_t hoveringPointerId) {
    for (TouchedWindow& touchedWindow : windows) {
        if (touchedWindow.windowHandle == windowHandle) {
            touchedWindow.addHoveringPointer(hoveringDeviceId, hoveringPointerId);
            return;
        }
    }

    TouchedWindow touchedWindow;
    touchedWindow.windowHandle = windowHandle;
    touchedWindow.addHoveringPointer(hoveringDeviceId, hoveringPointerId);
    windows.push_back(touchedWindow);
}

void TouchState::removeWindowByToken(const sp<IBinder>& token) {
    for (size_t i = 0; i < windows.size(); i++) {
        if (windows[i].windowHandle->getToken() == token) {
@@ -145,6 +180,26 @@ bool TouchState::isDown() const {
                       [](const TouchedWindow& window) { return !window.pointerIds.isEmpty(); });
}

std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(int32_t hoveringDeviceId,
                                                                         int32_t pointerId) const {
    std::set<sp<WindowInfoHandle>> out;
    for (const TouchedWindow& window : windows) {
        if (window.hasHoveringPointer(hoveringDeviceId, pointerId)) {
            out.insert(window.windowHandle);
        }
    }
    return out;
}

void TouchState::removeHoveringPointer(int32_t hoveringDeviceId, int32_t hoveringPointerId) {
    for (TouchedWindow& window : windows) {
        window.removeHoveringPointer(hoveringDeviceId, hoveringPointerId);
    }
    std::erase_if(windows, [](const TouchedWindow& w) {
        return w.pointerIds.isEmpty() && !w.hasHoveringPointers();
    });
}

std::string TouchState::dump() const {
    std::string out;
    out += StringPrintf("deviceId=%d, source=%s\n", deviceId,
+11 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#pragma once

#include <set>
#include "TouchedWindow.h"

namespace android {
@@ -39,9 +40,16 @@ struct TouchState {
    TouchState& operator=(const TouchState&) = default;

    void reset();
    void clearWindowsWithoutPointers();

    void removeTouchedPointer(int32_t pointerId);
    void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
                           ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
                           std::optional<nsecs_t> eventTime = std::nullopt);
    void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
                                    int32_t deviceId, int32_t hoveringPointerId);
    void removeHoveringPointer(int32_t deviceId, int32_t hoveringPointerId);
    void clearHoveringPointers();
    void removeWindowByToken(const sp<IBinder>& token);
    void filterNonAsIsTouchWindows();

@@ -56,6 +64,9 @@ struct TouchState {
    sp<android::gui::WindowInfoHandle> getWallpaperWindow() const;
    // Whether any of the windows are currently being touched
    bool isDown() const;

    std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer(
            int32_t deviceId, int32_t pointerId) const;
    std::string dump() const;
};

Loading