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

Commit 10c3f367 authored by Jeff Brown's avatar Jeff Brown Committed by Android (Google) Code Review
Browse files

Merge "Implement pointer acceleration." into honeycomb-mr2

parents a8629089 19c97d46
Loading
Loading
Loading
Loading
+81 −0
Original line number Diff line number Diff line
@@ -627,6 +627,87 @@ private:
    int32_t mActivePointerId;
};


/*
 * Specifies parameters that govern pointer or wheel acceleration.
 */
struct VelocityControlParameters {
    // A scale factor that is multiplied with the raw velocity deltas
    // prior to applying any other velocity control factors.  The scale
    // factor should be used to adapt the input device resolution
    // (eg. counts per inch) to the output device resolution (eg. pixels per inch).
    //
    // Must be a positive value.
    // Default is 1.0 (no scaling).
    float scale;

    // The scaled speed at which acceleration begins to be applied.
    // This value establishes the upper bound of a low speed regime for
    // small precise motions that are performed without any acceleration.
    //
    // Must be a non-negative value.
    // Default is 0.0 (no low threshold).
    float lowThreshold;

    // The scaled speed at which maximum acceleration is applied.
    // The difference between highThreshold and lowThreshold controls
    // the range of speeds over which the acceleration factor is interpolated.
    // The wider the range, the smoother the acceleration.
    //
    // Must be a non-negative value greater than or equal to lowThreshold.
    // Default is 0.0 (no high threshold).
    float highThreshold;

    // The acceleration factor.
    // When the speed is above the low speed threshold, the velocity will scaled
    // by an interpolated value between 1.0 and this amount.
    //
    // Must be a positive greater than or equal to 1.0.
    // Default is 1.0 (no acceleration).
    float acceleration;

    VelocityControlParameters() :
            scale(1.0f), lowThreshold(0.0f), highThreshold(0.0f), acceleration(1.0f) {
    }

    VelocityControlParameters(float scale, float lowThreshold,
            float highThreshold, float acceleration) :
            scale(scale), lowThreshold(lowThreshold),
            highThreshold(highThreshold), acceleration(acceleration) {
    }
};

/*
 * Implements mouse pointer and wheel speed control and acceleration.
 */
class VelocityControl {
public:
    VelocityControl();

    /* Sets the various parameters. */
    void setParameters(const VelocityControlParameters& parameters);

    /* Resets the current movement counters to zero.
     * This has the effect of nullifying any acceleration. */
    void reset();

    /* Translates a raw movement delta into an appropriately
     * scaled / accelerated delta based on the current velocity. */
    void move(nsecs_t eventTime, float* deltaX, float* deltaY);

private:
    // If no movements are received within this amount of time,
    // we assume the movement has stopped and reset the movement counters.
    static const nsecs_t STOP_TIME = 500 * 1000000; // 500 ms

    VelocityControlParameters mParameters;

    nsecs_t mLastMovementTime;
    VelocityTracker::Position mRawPosition;
    VelocityTracker mVelocityTracker;
};


/*
 * Describes the characteristics and capabilities of an input device.
 */
+89 −0
Original line number Diff line number Diff line
@@ -13,6 +13,10 @@
// Log debug messages about velocity tracking.
#define DEBUG_VELOCITY 0

// Log debug messages about acceleration.
#define DEBUG_ACCELERATION 0


#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
@@ -20,6 +24,7 @@
#include <ui/Input.h>

#include <math.h>
#include <limits.h>

#ifdef HAVE_ANDROID_OS
#include <binder/Parcel.h>
@@ -670,6 +675,11 @@ bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {

// --- VelocityTracker ---

const uint32_t VelocityTracker::HISTORY_SIZE;
const nsecs_t VelocityTracker::MAX_AGE;
const nsecs_t VelocityTracker::MIN_WINDOW;
const nsecs_t VelocityTracker::MIN_DURATION;

VelocityTracker::VelocityTracker() {
    clear();
}
@@ -879,6 +889,85 @@ bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const
}


// --- VelocityControl ---

const nsecs_t VelocityControl::STOP_TIME;

VelocityControl::VelocityControl() {
    reset();
}

void VelocityControl::setParameters(const VelocityControlParameters& parameters) {
    mParameters = parameters;
    reset();
}

void VelocityControl::reset() {
    mLastMovementTime = LLONG_MIN;
    mRawPosition.x = 0;
    mRawPosition.y = 0;
    mVelocityTracker.clear();
}

void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) {
    if ((deltaX && *deltaX) || (deltaY && *deltaY)) {
        if (eventTime >= mLastMovementTime + STOP_TIME) {
#if DEBUG_ACCELERATION
            LOGD("VelocityControl: stopped, last movement was %0.3fms ago",
                    (eventTime - mLastMovementTime) * 0.000001f);
#endif
            reset();
        }

        mLastMovementTime = eventTime;
        if (deltaX) {
            mRawPosition.x += *deltaX;
        }
        if (deltaY) {
            mRawPosition.y += *deltaY;
        }
        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition);

        float vx, vy;
        float scale = mParameters.scale;
        if (mVelocityTracker.getVelocity(0, &vx, &vy)) {
            float speed = hypotf(vx, vy) * scale;
            if (speed >= mParameters.highThreshold) {
                // Apply full acceleration above the high speed threshold.
                scale *= mParameters.acceleration;
            } else if (speed > mParameters.lowThreshold) {
                // Linearly interpolate the acceleration to apply between the low and high
                // speed thresholds.
                scale *= 1 + (speed - mParameters.lowThreshold)
                        / (mParameters.highThreshold - mParameters.lowThreshold)
                        * (mParameters.acceleration - 1);
            }

#if DEBUG_ACCELERATION
            LOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): "
                    "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
                    mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
                    mParameters.acceleration,
                    vx, vy, speed, scale / mParameters.scale);
#endif
        } else {
#if DEBUG_ACCELERATION
            LOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity",
                    mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
                    mParameters.acceleration);
#endif
        }

        if (deltaX) {
            *deltaX *= scale;
        }
        if (deltaY) {
            *deltaY *= scale;
        }
    }
}


// --- InputDeviceInfo ---

InputDeviceInfo::InputDeviceInfo() {
+110 −66
Original line number Diff line number Diff line
@@ -707,6 +707,20 @@ void InputReader::dump(String8& dump) {
    dump.appendFormat(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
            mConfig.virtualKeyQuietTime * 0.000001f);

    dump.appendFormat(INDENT2 "PointerVelocityControlParameters: "
            "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n",
            mConfig.pointerVelocityControlParameters.scale,
            mConfig.pointerVelocityControlParameters.lowThreshold,
            mConfig.pointerVelocityControlParameters.highThreshold,
            mConfig.pointerVelocityControlParameters.acceleration);

    dump.appendFormat(INDENT2 "WheelVelocityControlParameters: "
            "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n",
            mConfig.wheelVelocityControlParameters.scale,
            mConfig.wheelVelocityControlParameters.lowThreshold,
            mConfig.wheelVelocityControlParameters.highThreshold,
            mConfig.wheelVelocityControlParameters.acceleration);

    dump.appendFormat(INDENT2 "PointerGesture:\n");
    dump.appendFormat(INDENT3 "QuietInterval: %0.1fms\n",
            mConfig.pointerGestureQuietInterval * 0.000001f);
@@ -1371,6 +1385,10 @@ void CursorInputMapper::configure() {

    mHaveVWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_WHEEL);
    mHaveHWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_HWHEEL);

    mPointerVelocityControl.setParameters(getConfig()->pointerVelocityControlParameters);
    mWheelXVelocityControl.setParameters(getConfig()->wheelVelocityControlParameters);
    mWheelYVelocityControl.setParameters(getConfig()->wheelVelocityControlParameters);
}

void CursorInputMapper::configureParameters() {
@@ -1432,6 +1450,11 @@ void CursorInputMapper::reset() {
            }
        } // release lock

        // Reset velocity.
        mPointerVelocityControl.reset();
        mWheelXVelocityControl.reset();
        mWheelYVelocityControl.reset();

        // Synthesize button up event on reset.
        nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
        mAccumulator.clear();
@@ -1585,11 +1608,16 @@ void CursorInputMapper::sync(nsecs_t when) {
        } else {
            vscroll = 0;
        }
        mWheelYVelocityControl.move(when, NULL, &vscroll);

        if (mHaveHWheel && (fields & Accumulator::FIELD_REL_HWHEEL)) {
            hscroll = mAccumulator.relHWheel;
        } else {
            hscroll = 0;
        }
        mWheelXVelocityControl.move(when, &hscroll, NULL);

        mPointerVelocityControl.move(when, &deltaX, &deltaY);

        if (mPointerController != NULL) {
            if (deltaX != 0 || deltaY != 0 || vscroll != 0 || hscroll != 0
@@ -1806,6 +1834,7 @@ void TouchInputMapper::initializeLocked() {
    mLocked.orientedRanges.haveOrientation = false;

    mPointerGesture.reset();
    mPointerGesture.pointerVelocityControl.setParameters(mConfig->pointerVelocityControlParameters);
}

void TouchInputMapper::configure() {
@@ -2239,11 +2268,10 @@ bool TouchInputMapper::configureSurfaceLocked() {
                    mLocked.associatedDisplayHeight);

            // Scale movements such that one whole swipe of the touch pad covers a
            // given area relative to the diagonal size of the display.
            // given area relative to the diagonal size of the display when no acceleration
            // is applied.
            // Assume that the touch pad has a square aspect ratio such that movements in
            // X and Y of the same number of raw units cover the same physical distance.
            const float scaleFactor = 0.8f;

            mLocked.pointerGestureXMovementScale = mConfig->pointerGestureMovementSpeedRatio
                    * displayDiagonal / rawDiagonal;
            mLocked.pointerGestureYMovementScale = mLocked.pointerGestureXMovementScale;
@@ -3247,6 +3275,9 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag
    if (!sendEvents) {
        return;
    }
    if (finishPreviousGesture) {
        cancelPreviousGesture = false;
    }

    // Switch pointer presentation.
    mPointerController->setPresentation(
@@ -3436,6 +3467,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
                mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
                mPointerGesture.currentGestureIdBits.clear();

                mPointerGesture.pointerVelocityControl.reset();

                if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
                    mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
                    mPointerGesture.spotIdBits.clear();
@@ -3530,6 +3563,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
        mPointerGesture.currentGestureMode = PointerGesture::QUIET;
        mPointerGesture.currentGestureIdBits.clear();

        mPointerGesture.pointerVelocityControl.reset();

        if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
            mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
            mPointerGesture.spotIdBits.clear();
@@ -3561,8 +3596,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,

        // Switch pointers if needed.
        // Find the fastest pointer and follow it.
        if (activeTouchId >= 0) {
            if (mCurrentTouch.pointerCount > 1) {
        if (activeTouchId >= 0 && mCurrentTouch.pointerCount > 1) {
            int32_t bestId = -1;
            float bestSpeed = mConfig->pointerGestureDragMinSwitchSpeed;
            for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
@@ -3586,7 +3620,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
            }
        }

            if (mLastTouch.idBits.hasBit(activeTouchId)) {
        if (activeTouchId >= 0 && mLastTouch.idBits.hasBit(activeTouchId)) {
            const PointerData& currentPointer =
                    mCurrentTouch.pointers[mCurrentTouch.idToIndex[activeTouchId]];
            const PointerData& lastPointer =
@@ -3596,11 +3630,14 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
            float deltaY = (currentPointer.y - lastPointer.y)
                    * mLocked.pointerGestureYMovementScale;

            mPointerGesture.pointerVelocityControl.move(when, &deltaX, &deltaY);

            // Move the pointer using a relative motion.
            // When using spots, the click will occur at the position of the anchor
            // spot and all other spots will move there.
            mPointerController->move(deltaX, deltaY);
            }
        } else {
            mPointerGesture.pointerVelocityControl.reset();
        }

        float x, y;
@@ -3700,6 +3737,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
            }
        }

        mPointerGesture.pointerVelocityControl.reset();

        if (!tapped) {
#if DEBUG_GESTURES
            LOGD("Gestures: NEUTRAL");
@@ -3756,9 +3795,13 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
            float deltaY = (currentPointer.y - lastPointer.y)
                    * mLocked.pointerGestureYMovementScale;

            mPointerGesture.pointerVelocityControl.move(when, &deltaX, &deltaY);

            // Move the pointer using a relative motion.
            // When using spots, the hover or drag will occur at the position of the anchor spot.
            mPointerController->move(deltaX, deltaY);
        } else {
            mPointerGesture.pointerVelocityControl.reset();
        }

        bool down;
@@ -3820,16 +3863,32 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
        // a decision to transition into SWIPE or FREEFORM mode accordingly.
        LOG_ASSERT(activeTouchId >= 0);

        bool needReference = false;
        bool settled = when >= mPointerGesture.firstTouchTime
                + mConfig->pointerGestureMultitouchSettleInterval;
        if (mPointerGesture.lastGestureMode != PointerGesture::PRESS
                && mPointerGesture.lastGestureMode != PointerGesture::SWIPE
                && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
            *outFinishPreviousGesture = true;
        } else if (!settled && mCurrentTouch.pointerCount > mLastTouch.pointerCount) {
            // Additional pointers have gone down but not yet settled.
            // Reset the gesture.
#if DEBUG_GESTURES
            LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
                    "settle time remaining %0.3fms",
                    (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
                            * 0.000001f);
#endif
            *outCancelPreviousGesture = true;
        } else {
            // Continue previous gesture.
            mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
        }

        if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
            mPointerGesture.currentGestureMode = PointerGesture::PRESS;
            mPointerGesture.activeGestureId = 0;
            mPointerGesture.referenceIdBits.clear();
            mPointerGesture.pointerVelocityControl.reset();

            if (settled && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
                    && mLastTouch.idBits.hasBit(mPointerGesture.activeTouchId)) {
@@ -3850,38 +3909,19 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,
                mPointerGesture.referenceGestureX = c.getAxisValue(AMOTION_EVENT_AXIS_X);
                mPointerGesture.referenceGestureY = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
            } else {
                // Use the centroid and pointer location as the reference points for the gesture.
#if DEBUG_GESTURES
                LOGD("Gestures: Using centroid as reference for MULTITOUCH, "
                        "settle time remaining %0.3fms",
                        (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
                                * 0.000001f);
#endif
                needReference = true;
            }
        } else if (!settled && mCurrentTouch.pointerCount > mLastTouch.pointerCount) {
            // Additional pointers have gone down but not yet settled.
            // Reset the gesture.
#if DEBUG_GESTURES
            LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
                    "settle time remaining %0.3fms",
                    (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
                            * 0.000001f);
#endif
            *outCancelPreviousGesture = true;
            mPointerGesture.currentGestureMode = PointerGesture::PRESS;
            mPointerGesture.activeGestureId = 0;
        } else {
            // Continue previous gesture.
            mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
        }

        if (needReference) {
            // Use the centroid and pointer location as the reference points for the gesture.
                mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
                        &mPointerGesture.referenceTouchY);
                mPointerController->getPosition(&mPointerGesture.referenceGestureX,
                        &mPointerGesture.referenceGestureY);
            }
        }

        if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
            float d;
@@ -4010,10 +4050,14 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when,

                mPointerGesture.referenceTouchX += commonDeltaX;
                mPointerGesture.referenceTouchY += commonDeltaY;
                mPointerGesture.referenceGestureX +=
                        commonDeltaX * mLocked.pointerGestureXMovementScale;
                mPointerGesture.referenceGestureY +=
                        commonDeltaY * mLocked.pointerGestureYMovementScale;

                commonDeltaX *= mLocked.pointerGestureXMovementScale;
                commonDeltaY *= mLocked.pointerGestureYMovementScale;
                mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);

                mPointerGesture.referenceGestureX += commonDeltaX;
                mPointerGesture.referenceGestureY += commonDeltaY;

                clampPositionUsingPointerBounds(mPointerController,
                        &mPointerGesture.referenceGestureX,
                        &mPointerGesture.referenceGestureY);
+19 −1
Original line number Diff line number Diff line
@@ -62,6 +62,12 @@ struct InputReaderConfiguration {
    // Devices with these names will be ignored.
    Vector<String8> excludedDeviceNames;

    // Velocity control parameters for mouse pointer movements.
    VelocityControlParameters pointerVelocityControlParameters;

    // Velocity control parameters for mouse wheel movements.
    VelocityControlParameters wheelVelocityControlParameters;

    // Quiet time between certain pointer gesture transitions.
    // Time to allow for all fingers or buttons to settle into a stable state before
    // starting a new gesture.
@@ -128,6 +134,8 @@ struct InputReaderConfiguration {
            filterTouchEvents(false),
            filterJumpyTouchEvents(false),
            virtualKeyQuietTime(0),
            pointerVelocityControlParameters(1.0f, 80.0f, 400.0f, 4.0f),
            wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
            pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
            pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
            pointerGestureTapInterval(150 * 1000000LL), // 150 ms
@@ -137,7 +145,7 @@ struct InputReaderConfiguration {
            pointerGestureMultitouchMinSpeed(150.0f), // 150 pixels per second
            pointerGestureSwipeTransitionAngleCosine(0.5f), // cosine of 45degrees
            pointerGestureSwipeMaxWidthRatio(0.333f),
            pointerGestureMovementSpeedRatio(0.8f),
            pointerGestureMovementSpeedRatio(0.5f),
            pointerGestureZoomSpeedRatio(0.3f) { }
};

@@ -629,6 +637,12 @@ private:
    float mVWheelScale;
    float mHWheelScale;

    // Velocity controls for mouse pointer and wheel movements.
    // The controls for X and Y wheel movements are separate to keep them decoupled.
    VelocityControl mPointerVelocityControl;
    VelocityControl mWheelXVelocityControl;
    VelocityControl mWheelYVelocityControl;

    sp<PointerControllerInterface> mPointerController;

    struct LockedState {
@@ -1133,6 +1147,9 @@ private:
        // A velocity tracker for determining whether to switch active pointers during drags.
        VelocityTracker velocityTracker;

        // Velocity control for pointer movements.
        VelocityControl pointerVelocityControl;

        void reset() {
            firstTouchTime = LLONG_MIN;
            activeTouchId = -1;
@@ -1147,6 +1164,7 @@ private:
            velocityTracker.clear();
            resetTap();
            resetQuietTime();
            pointerVelocityControl.reset();
        }

        void resetTap() {