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

Commit 4dac901f authored by Jeff Brown's avatar Jeff Brown
Browse files

Rewrite touch navigation dpad synthesis.

The new implementation more accurately tracks the velocity
of flings and takes care to avoid obvious discontinuities.
The main goal is for a fling to appear to be a linear
extension of the movement already in progress.  The minimum
fling velocity is set to ensure that flings appear to be
fairly smooth despite being discretized.

Use sequences of repeated key events instead of individual
down/up events to represent continuous motions in one
direction which can be helpful for stopping flings at boundaries
such as when flinging the cursor position within a text view.

Compute the movement thresholds based on the physical
size of the touch pad, if known.  If not known, we assume a
nominal size.

Support stopping flings with a tap just like we do for
normal touch events elsewhere in the framework.

Moved the detection of ASSIST swipes into the InputReader
where it belongs.  These swipes must be detected globally
to ensure consistent behavior across the all applications.

Added a custom protocol in EventHub to enable input device
drivers to override the timestamp of the following events
in a packet.  This change enables input device drivers
that have a better idea of when an input event was actually
generated to pass this information to the input system.
Particularly useful with uinput.

Bug: 8583760
Change-Id: I8ef4e827804786d549cfaa00793a2b9dd0fda465
parent a9574e33
Loading
Loading
Loading
Loading
+371 −245

File changed.

Preview size limit exceeded, changes collapsed.

+31 −5
Original line number Diff line number Diff line
@@ -40,7 +40,6 @@
#include <androidfw/KeyCharacterMap.h>
#include <androidfw/VirtualKeyMap.h>

#include <sha1.h>
#include <string.h>
#include <stdint.h>
#include <dirent.h>
@@ -49,6 +48,7 @@
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/limits.h>
#include <sys/sha1.h>

/* this macro is used to tell if "bit" is set in "array"
 * it selects a byte from the array, and does a boolean AND
@@ -162,7 +162,8 @@ EventHub::Device::Device(int fd, int32_t id, const String8& path,
        next(NULL),
        fd(fd), id(id), path(path), identifier(identifier),
        classes(0), configuration(NULL), virtualKeyMap(NULL),
        ffEffectPlaying(false), ffEffectId(-1) {
        ffEffectPlaying(false), ffEffectId(-1),
        timestampOverrideSec(0), timestampOverrideUsec(0) {
    memset(keyBitmask, 0, sizeof(keyBitmask));
    memset(absBitmask, 0, sizeof(absBitmask));
    memset(relBitmask, 0, sizeof(relBitmask));
@@ -766,12 +767,37 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz

                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    for (size_t i = 0; i < count; i++) {
                        const struct input_event& iev = readBuffer[i];
                        ALOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",
                        struct input_event& iev = readBuffer[i];
                        ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
                                device->path.string(),
                                (int) iev.time.tv_sec, (int) iev.time.tv_usec,
                                iev.type, iev.code, iev.value);

                        // Some input devices may have a better concept of the time
                        // when an input event was actually generated than the kernel
                        // which simply timestamps all events on entry to evdev.
                        // This is a custom Android extension of the input protocol
                        // mainly intended for use with uinput based device drivers.
                        if (iev.type == EV_MSC) {
                            if (iev.code == MSC_ANDROID_TIME_SEC) {
                                device->timestampOverrideSec = iev.value;
                                continue;
                            } else if (iev.code == MSC_ANDROID_TIME_USEC) {
                                device->timestampOverrideUsec = iev.value;
                                continue;
                            }
                        }
                        if (device->timestampOverrideSec || device->timestampOverrideUsec) {
                            iev.time.tv_sec = device->timestampOverrideSec;
                            iev.time.tv_usec = device->timestampOverrideUsec;
                            if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
                                device->timestampOverrideSec = 0;
                                device->timestampOverrideUsec = 0;
                            }
                            ALOGV("applied override time %d.%06d",
                                    int(iev.time.tv_sec), int(iev.time.tv_usec));
                        }

#ifdef HAVE_POSIX_CLOCKS
                        // Use the time specified in the event instead of the current time
                        // so that downstream code can get more accurate estimates of
@@ -829,8 +855,8 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                    capacity -= count;
                    if (capacity == 0) {
                        // The result buffer is full.  Reset the pending event index
                        // so we will try to read the device again on the next iteration.
+17 −0
Original line number Diff line number Diff line
@@ -42,6 +42,20 @@
#define BTN_FIRST 0x100  // first button code
#define BTN_LAST 0x15f   // last button code

/*
 * These constants are used privately in Android to pass raw timestamps
 * through evdev from uinput device drivers because there is currently no
 * other way to transfer this information.  The evdev driver automatically
 * timestamps all input events with the time they were posted and clobbers
 * whatever information was passed in.
 *
 * For the purposes of this hack, the timestamp is specified in the
 * CLOCK_MONOTONIC timebase and is split into two EV_MSC events specifying
 * seconds and microseconds.
 */
#define MSC_ANDROID_TIME_SEC 0x6
#define MSC_ANDROID_TIME_USEC 0x7

namespace android {

enum {
@@ -329,6 +343,9 @@ private:
        bool ffEffectPlaying;
        int16_t ffEffectId; // initially -1

        int32_t timestampOverrideSec;
        int32_t timestampOverrideUsec;

        Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
        ~Device();

+56 −5
Original line number Diff line number Diff line
@@ -2701,6 +2701,12 @@ void TouchInputMapper::dump(String8& dump) {
                mPointerYZoomScale);
        dump.appendFormat(INDENT4 "MaxSwipeWidth: %f\n",
                mPointerGestureMaxSwipeWidth);
    } else if (mDeviceMode == DEVICE_MODE_NAVIGATION) {
        dump.appendFormat(INDENT3 "Navigation Gesture Detector:\n");
        dump.appendFormat(INDENT4 "AssistStartY: %0.3f\n",
                mNavigationAssistStartY);
        dump.appendFormat(INDENT4 "AssistEndY: %0.3f\n",
                mNavigationAssistEndY);
    }
}

@@ -2895,7 +2901,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
        }
    } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
        mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
        mDeviceMode = DEVICE_MODE_UNSCALED;
        mDeviceMode = DEVICE_MODE_NAVIGATION;
    } else {
        mSource = AINPUT_SOURCE_TOUCHPAD;
        mDeviceMode = DEVICE_MODE_UNSCALED;
@@ -3243,8 +3249,8 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
            break;
        }

        // Compute pointer gesture detection parameters.
        if (mDeviceMode == DEVICE_MODE_POINTER) {
            // Compute pointer gesture detection parameters.
            float rawDiagonal = hypotf(rawWidth, rawHeight);
            float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);

@@ -3269,10 +3275,14 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
            // translated into freeform gestures.
            mPointerGestureMaxSwipeWidth =
                    mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
        }

            // Abort current pointer usages because the state has changed.
            abortPointerUsage(when, 0 /*policyFlags*/);
        } else if (mDeviceMode == DEVICE_MODE_NAVIGATION) {
            // Compute navigation parameters.
            mNavigationAssistStartY = mSurfaceHeight * 0.9f;
            mNavigationAssistEndY = mSurfaceHeight * 0.5f;
        }

        // Inform the dispatcher about the changes.
        *outResetNeeded = true;
@@ -3611,6 +3621,7 @@ void TouchInputMapper::reset(nsecs_t when) {

    mPointerGesture.reset();
    mPointerSimple.reset();
    mNavigation.reset();

    if (mPointerController != NULL) {
        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
@@ -3761,6 +3772,8 @@ void TouchInputMapper::sync(nsecs_t when) {
                mPointerController->setSpots(mCurrentCookedPointerData.pointerCoords,
                        mCurrentCookedPointerData.idToIndex,
                        mCurrentCookedPointerData.touchingIdBits);
            } else if (mDeviceMode == DEVICE_MODE_NAVIGATION) {
                dispatchNavigationAssist(when, policyFlags);
            }

            dispatchHoverExit(when, policyFlags);
@@ -5482,6 +5495,44 @@ void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) {
    dispatchPointerSimple(when, policyFlags, false, false);
}

void TouchInputMapper::dispatchNavigationAssist(nsecs_t when, uint32_t policyFlags) {
    if (mCurrentCookedPointerData.touchingIdBits.count() == 1) {
        if (mLastCookedPointerData.touchingIdBits.isEmpty()) {
            // First pointer down.
            uint32_t id = mCurrentCookedPointerData.touchingIdBits.firstMarkedBit();
            const PointerCoords& coords = mCurrentCookedPointerData.pointerCoordsForId(id);
            if (coords.getY() >= mNavigationAssistStartY) {
                // Start tracking the possible assist swipe.
                mNavigation.activeAssistId = id;
                return;
            }
        } else if (mNavigation.activeAssistId >= 0
                && mCurrentCookedPointerData.touchingIdBits.hasBit(mNavigation.activeAssistId)) {
            const PointerCoords& coords = mCurrentCookedPointerData.pointerCoordsForId(
                    mNavigation.activeAssistId);
            if (coords.getY() > mNavigationAssistEndY) {
                // Swipe is still in progress.
                return;
            }

            // Detected assist swipe.
            int32_t metaState = mContext->getGlobalMetaState();
            NotifyKeyArgs downArgs(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
                    policyFlags | POLICY_FLAG_VIRTUAL,
                    AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_ASSIST, 0, metaState, when);
            getListener()->notifyKey(&downArgs);

            NotifyKeyArgs upArgs(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
                    policyFlags | POLICY_FLAG_VIRTUAL,
                    AKEY_EVENT_ACTION_UP, 0, AKEYCODE_ASSIST, 0, metaState, when);
            getListener()->notifyKey(&upArgs);
        }
    }

    // Cancel the assist swipe.
    mNavigation.activeAssistId = -1;
}

void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
        int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
        const PointerProperties* properties, const PointerCoords* coords,
+20 −0
Original line number Diff line number Diff line
@@ -791,6 +791,10 @@ struct CookedPointerData {
    void clear();
    void copyFrom(const CookedPointerData& other);

    inline const PointerCoords& pointerCoordsForId(uint32_t id) const {
        return pointerCoords[idToIndex[id]];
    }

    inline bool isHovering(uint32_t pointerIndex) {
        return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id);
    }
@@ -1180,6 +1184,7 @@ protected:
        DEVICE_MODE_DISABLED, // input is disabled
        DEVICE_MODE_DIRECT, // direct mapping (touchscreen)
        DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad)
        DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
        DEVICE_MODE_POINTER, // pointer mapping (pointer)
    };
    DeviceMode mDeviceMode;
@@ -1432,6 +1437,10 @@ private:
    // The maximum swipe width.
    float mPointerGestureMaxSwipeWidth;

    // The start and end Y thresholds for invoking the assist navigation swipe.
    float mNavigationAssistStartY;
    float mNavigationAssistEndY;

    struct PointerDistanceHeapElement {
        uint32_t currentPointerIndex : 8;
        uint32_t lastPointerIndex : 8;
@@ -1606,6 +1615,15 @@ private:
        }
    } mPointerSimple;

    struct Navigation {
        // The id of a pointer that is tracking a possible assist swipe.
        int32_t activeAssistId; // -1 if none

        void reset() {
            activeAssistId = -1;
        }
    } mNavigation;

    // The pointer and scroll velocity controls.
    VelocityControl mPointerVelocityControl;
    VelocityControl mWheelXVelocityControl;
@@ -1641,6 +1659,8 @@ private:
            bool down, bool hovering);
    void abortPointerSimple(nsecs_t when, uint32_t policyFlags);

    void dispatchNavigationAssist(nsecs_t when, uint32_t policyFlags);

    // Dispatches a motion event.
    // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
    // method will take care of setting the index and transmuting the action to DOWN or UP