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

Commit b3e35952 authored by Jeff Brown's avatar Jeff Brown Committed by Android Git Automerger
Browse files

am 82e4373e: Merge "Use touch pad gestures to manipulate the pointer. (DO NOT...

am 82e4373e: Merge "Use touch pad gestures to manipulate the pointer. (DO NOT MERGE)" into honeycomb-mr2

* commit '82e4373ed3775395a23d161e58c003e82511921a':
  Use touch pad gestures to manipulate the pointer. (DO NOT MERGE)
parents cc5dee30 bf7493c5
Loading
Loading
Loading
Loading
+55 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/BitSet.h>

#ifdef HAVE_ANDROID_OS
class SkMatrix;
@@ -210,6 +211,13 @@ struct PointerCoords {
    status_t writeToParcel(Parcel* parcel) const;
#endif

    bool operator==(const PointerCoords& other) const;
    inline bool operator!=(const PointerCoords& other) const {
        return !(*this == other);
    }

    void copyFrom(const PointerCoords& other);

private:
    void tooManyAxes(int axis);
};
@@ -544,6 +552,53 @@ private:
    MotionEvent mMotionEvent;
};

/*
 * Calculates the velocity of pointer motions over time.
 * Uses essentially the same algorithm as android.view.VelocityTracker.
 */
class VelocityTracker {
public:
    struct Position {
        float x, y;
    };

    VelocityTracker();

    // Resets the velocity tracker state.
    void clear();

    // Adds movement information for a set of pointers.
    // The idBits bitfield specifies the pointer ids of the pointers whose positions
    // are included in the movement.
    // The positions array contains position information for each pointer in order by
    // increasing id.  Its size should be equal to the number of one bits in idBits.
    void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);

    // Gets the velocity of the specified pointer id in position units per second.
    // Returns false and sets the velocity components to zero if there is no movement
    // information for the pointer.
    bool getVelocity(uint32_t id, float* outVx, float* outVy) const;

private:
    // Number of samples to keep.
    static const uint32_t HISTORY_SIZE = 10;

    // Oldest sample to consider when calculating the velocity.
    static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms

    // The minimum duration between samples when estimating velocity.
    static const nsecs_t MIN_DURATION = 5 * 1000000; // 5 ms

    struct Movement {
        nsecs_t eventTime;
        BitSet32 idBits;
        Position positions[MAX_POINTERS];
    };

    uint32_t mIndex;
    Movement mMovements[HISTORY_SIZE];
};

/*
 * Describes the characteristics and capabilities of an input device.
 */
+6 −0
Original line number Diff line number Diff line
@@ -61,6 +61,12 @@ struct BitSet32 {
    // Result is undefined if all bits are marked.
    inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); }

    // Gets the index of the specified bit in the set, which is the number of
    // marked bits that appear before the specified bit.
    inline uint32_t getIndexOfBit(uint32_t n) const {
        return __builtin_popcount(value & ~(0xffffffffUL >> n));
    }

    inline bool operator== (const BitSet32& other) const { return value == other.value; }
    inline bool operator!= (const BitSet32& other) const { return value != other.value; }
};
+154 −0
Original line number Diff line number Diff line
@@ -7,8 +7,12 @@

//#define LOG_NDEBUG 0

// Log debug messages about keymap probing.
#define DEBUG_PROBE 0

// Log debug messages about velocity tracking.
#define DEBUG_VELOCITY 0

#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
@@ -347,6 +351,27 @@ void PointerCoords::tooManyAxes(int axis) {
            "cannot contain more than %d axis values.", axis, int(MAX_AXES));
}

bool PointerCoords::operator==(const PointerCoords& other) const {
    if (bits != other.bits) {
        return false;
    }
    uint32_t count = __builtin_popcountll(bits);
    for (uint32_t i = 0; i < count; i++) {
        if (values[i] != other.values[i]) {
            return false;
        }
    }
    return true;
}

void PointerCoords::copyFrom(const PointerCoords& other) {
    bits = other.bits;
    uint32_t count = __builtin_popcountll(bits);
    for (uint32_t i = 0; i < count; i++) {
        values[i] = other.values[i];
    }
}


// --- MotionEvent ---

@@ -633,6 +658,135 @@ bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
}


// --- VelocityTracker ---

VelocityTracker::VelocityTracker() {
    clear();
}

void VelocityTracker::clear() {
    mIndex = 0;
    mMovements[0].idBits.clear();
}

void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
    if (++mIndex == HISTORY_SIZE) {
        mIndex = 0;
    }
    Movement& movement = mMovements[mIndex];
    movement.eventTime = eventTime;
    movement.idBits = idBits;
    uint32_t count = idBits.count();
    for (uint32_t i = 0; i < count; i++) {
        movement.positions[i] = positions[i];
    }

#if DEBUG_VELOCITY
    LOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x", eventTime, idBits.value);
    for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
        uint32_t id = iterBits.firstMarkedBit();
        uint32_t index = idBits.getIndexOfBit(id);
        iterBits.clearBit(id);
        float vx, vy;
        bool available = getVelocity(id, &vx, &vy);
        if (available) {
            LOGD("  %d: position (%0.3f, %0.3f), velocity (%0.3f, %0.3f), speed %0.3f",
                    id, positions[index].x, positions[index].y, vx, vy, sqrtf(vx * vx + vy * vy));
        } else {
            assert(vx == 0 && vy == 0);
            LOGD("  %d: position (%0.3f, %0.3f), velocity not available",
                    id, positions[index].x, positions[index].y);
        }
    }
#endif
}

bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
    const Movement& newestMovement = mMovements[mIndex];
    if (newestMovement.idBits.hasBit(id)) {
        // Find the oldest sample that contains the pointer and that is not older than MAX_AGE.
        nsecs_t minTime = newestMovement.eventTime - MAX_AGE;
        uint32_t oldestIndex = mIndex;
        uint32_t numTouches = 1;
        do {
            uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1;
            const Movement& nextOldestMovement = mMovements[nextOldestIndex];
            if (!nextOldestMovement.idBits.hasBit(id)
                    || nextOldestMovement.eventTime < minTime) {
                break;
            }
            oldestIndex = nextOldestIndex;
        } while (++numTouches < HISTORY_SIZE);

        // If we have a lot of samples, skip the last received sample since it is
        // probably pretty noisy compared to the sum of all of the traces already acquired.
        //
        // NOTE: This condition exists in the android.view.VelocityTracker and imposes a
        // bias against the most recent data.
        if (numTouches > 3) {
            numTouches -= 1;
        }

        // Calculate an exponentially weighted moving average of the velocity at different
        // points in time measured relative to the oldest samples.  This is essentially
        // an IIR filter.
        //
        // One problem with this algorithm is that the sample data may be poorly conditioned.
        // Sometimes samples arrive very close together in time which can cause us to
        // overestimate the velocity at that time point.  Most samples might be measured
        // 16ms apart but some consecutive samples could be only 0.5sm apart due to
        // the way they are reported by the hardware or driver (sometimes in bursts or with
        // significant jitter).  The instantaneous velocity for those samples 0.5ms apart will
        // be calculated to be 32 times what it should have been.
        // To work around this effect, we impose a minimum duration on the samples.
        //
        // FIXME: Samples close together in time can have an disproportionately large
        // impact on the result because all samples are equally weighted.  The average should
        // instead take the time factor into account.
        //
        // FIXME: The minimum duration condition does not exist in
        // android.view.VelocityTracker yet.  It is less important there because sample times
        // are truncated to the millisecond so back to back samples will often appear to be
        // zero milliseconds apart and will be ignored if they are the oldest ones.
        float accumVx = 0;
        float accumVy = 0;
        uint32_t index = oldestIndex;
        uint32_t samplesUsed = 0;
        const Movement& oldestMovement = mMovements[oldestIndex];
        const Position& oldestPosition =
                oldestMovement.positions[oldestMovement.idBits.getIndexOfBit(id)];
        while (numTouches-- > 1) {
            if (++index == HISTORY_SIZE) {
                index = 0;
            }
            const Movement& movement = mMovements[index];
            nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
            if (duration > MIN_DURATION) {
                const Position& position = movement.positions[movement.idBits.getIndexOfBit(id)];
                float scale = 1000000000.0f / duration; // one over time delta in seconds
                float vx = (position.x - oldestPosition.x) * scale;
                float vy = (position.y - oldestPosition.y) * scale;
                accumVx = accumVx == 0 ? vx : (accumVx + vx) * 0.5f;
                accumVy = accumVy == 0 ? vy : (accumVy + vy) * 0.5f;
                samplesUsed += 1;
            }
        }

        // Make sure we used at least one sample.
        if (samplesUsed != 0) {
            *outVx = accumVx;
            *outVy = accumVy;
            return true;
        }
    }

    // No data available for this pointer.
    *outVx = 0;
    *outVy = 0;
    return false;
}


// --- InputDeviceInfo ---

InputDeviceInfo::InputDeviceInfo() {
+2 −2
Original line number Diff line number Diff line
@@ -406,7 +406,7 @@ status_t InputPublisher::publishMotionEvent(

    for (size_t i = 0; i < pointerCount; i++) {
        mSharedMessage->motion.pointerIds[i] = pointerIds[i];
        mSharedMessage->motion.sampleData[0].coords[i] = pointerCoords[i];
        mSharedMessage->motion.sampleData[0].coords[i].copyFrom(pointerCoords[i]);
    }

    // Cache essential information about the motion event to ensure that a malicious consumer
@@ -475,7 +475,7 @@ status_t InputPublisher::appendMotionSample(

    mMotionEventSampleDataTail->eventTime = eventTime;
    for (size_t i = 0; i < mMotionEventPointerCount; i++) {
        mMotionEventSampleDataTail->coords[i] = pointerCoords[i];
        mMotionEventSampleDataTail->coords[i].copyFrom(pointerCoords[i]);
    }
    mMotionEventSampleDataTail = newTail;