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

Commit 3f7545f4 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

Differentiate fused and unfused external styluses

An external stylus can be "fused" with touch data if it reports
pointer-specific data independently from the touch device. The only such
data we currently support is pressure. Thus, a fused external stylus
will reports pressure data.

If a fused stylus is connected, we withold all touches for up to 72
milliseconds to wait for pressure data from the stylus. If we get
pressure data within this time, we assume that the first pointer that
went down is actually the stylus, and fuse the pressure information from
the stylus to the pointer.

If an external stylus does not report pressure, it is an "unfused"
stylus.

When such an external stylus is connected, we don't withold any touches.
We still report button presses from these styluses through the touch
device.

DD: go/android-stylus-buttons

Bug: 246394583
Test: atest inputflinger_tests
Change-Id: I3d687a10630019756170e7e5e5f5d1902eb96e36
parent 9a561c20
Loading
Loading
Loading
Loading
+10 −18
Original line number Diff line number Diff line
@@ -24,27 +24,19 @@ namespace android {

struct StylusState {
    /* Time the stylus event was received. */
    nsecs_t when;
    /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */
    float pressure;
    nsecs_t when{};
    /*
     * Pressure as reported by the stylus if supported, normalized to the range [0, 1.0].
     * The presence of a pressure value indicates that the stylus is able to tell whether it is
     * touching the display.
     */
    std::optional<float> pressure{};
    /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */
    uint32_t buttons;
    uint32_t buttons{};
    /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */
    int32_t toolType;

    void copyFrom(const StylusState& other) {
        when = other.when;
        pressure = other.pressure;
        buttons = other.buttons;
        toolType = other.toolType;
    }
    int32_t toolType{AMOTION_EVENT_TOOL_TYPE_UNKNOWN};

    void clear() {
        when = LLONG_MAX;
        pressure = 0.f;
        buttons = 0;
        toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
    }
    void clear() { *this = StylusState{}; }
};

} // namespace android
+9 −8
Original line number Diff line number Diff line
@@ -32,8 +32,10 @@ uint32_t ExternalStylusInputMapper::getSources() const {

void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
    InputMapper::populateDeviceInfo(info);
    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f,
                         0.0f);
    if (mRawPressureAxis.valid) {
        info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f,
                             0.0f, 0.0f);
    }
}

void ExternalStylusInputMapper::dump(std::string& dump) {
@@ -79,13 +81,12 @@ std::list<NotifyArgs> ExternalStylusInputMapper::sync(nsecs_t when) {
        mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
    }

    int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
    if (mRawPressureAxis.valid) {
        mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue;
    } else if (mTouchButtonAccumulator.isToolActive()) {
        mStylusState.pressure = 1.0f;
    } else {
        mStylusState.pressure = 0.0f;
        auto rawPressure = static_cast<float>(mSingleTouchMotionAccumulator.getAbsolutePressure());
        mStylusState.pressure = (rawPressure - mRawPressureAxis.minValue) /
                static_cast<float>(mRawPressureAxis.maxValue - mRawPressureAxis.minValue);
    } else if (mTouchButtonAccumulator.hasButtonTouch()) {
        mStylusState.pressure = mTouchButtonAccumulator.isHovering() ? 0.0f : 1.0f;
    }

    mStylusState.buttons = mTouchButtonAccumulator.getButtonState();
+2 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include "InputMapper.h"

#include "InputDevice.h"
#include "input/PrintTools.h"

namespace android {

@@ -129,7 +130,7 @@ void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAx

void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) {
    dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when);
    dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure);
    dump += StringPrintf(INDENT4 "Pressure: %s\n", toString(state.pressure).c_str());
    dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons);
    dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType);
}
+50 −36
Original line number Diff line number Diff line
@@ -1705,41 +1705,66 @@ void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) {
    CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData;
    const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData;
    if (!mFusedStylusPointerId || !currentPointerData.isTouching(*mFusedStylusPointerId)) {
        return;
    }

    if (mFusedStylusPointerId && currentPointerData.isTouching(*mFusedStylusPointerId)) {
        float pressure = mExternalStylusState.pressure;
        if (pressure == 0.0f && lastPointerData.isTouching(*mFusedStylusPointerId)) {
            const PointerCoords& coords =
                    lastPointerData.pointerCoordsForId(*mFusedStylusPointerId);
            pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
    float pressure = lastPointerData.isTouching(*mFusedStylusPointerId)
            ? lastPointerData.pointerCoordsForId(*mFusedStylusPointerId)
                      .getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)
            : 0.f;
    if (mExternalStylusState.pressure && *mExternalStylusState.pressure > 0.f) {
        pressure = *mExternalStylusState.pressure;
    }
    PointerCoords& coords = currentPointerData.editPointerCoordsWithId(*mFusedStylusPointerId);
    coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);

    if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
        PointerProperties& properties =
                currentPointerData.editPointerPropertiesWithId(*mFusedStylusPointerId);
        if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
        properties.toolType = mExternalStylusState.toolType;
    }
}
}

bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) {
    if (mDeviceMode != DeviceMode::DIRECT || !hasExternalStylus()) {
        return false;
    }

    // Check if the stylus pointer has gone up.
    if (mFusedStylusPointerId &&
        !state.rawPointerData.touchingIdBits.hasBit(*mFusedStylusPointerId)) {
        ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus pointer is going up");
        mFusedStylusPointerId.reset();
        return false;
    }

    const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 &&
            state.rawPointerData.pointerCount != 0;
    if (initialDown) {
        if (mExternalStylusState.pressure != 0.0f) {
    if (!initialDown) {
        return false;
    }

    if (!mExternalStylusState.pressure) {
        ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus does not support pressure, no pointer fusion needed");
        return false;
    }

    if (*mExternalStylusState.pressure != 0.0f) {
        ALOGD_IF(DEBUG_STYLUS_FUSION, "Have both stylus and touch data, beginning fusion");
        mFusedStylusPointerId = state.rawPointerData.touchingIdBits.firstMarkedBit();
        } else if (timeout) {
        return false;
    }

    if (timeout) {
        ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, assuming touch is not a stylus.");
        mFusedStylusPointerId.reset();
        mExternalStylusFusionTimeout = LLONG_MAX;
        } else {
        return false;
    }

    // We are waiting for the external stylus to report a pressure value. Withhold touches from
    // being processed until we either get pressure data or timeout.
    if (mExternalStylusFusionTimeout == LLONG_MAX) {
        mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
    }
@@ -1749,17 +1774,6 @@ bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeou
    getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
    return true;
}
    }

    // Check if the stylus pointer has gone up.
    if (mFusedStylusPointerId &&
        !state.rawPointerData.touchingIdBits.hasBit(*mFusedStylusPointerId)) {
        ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus pointer is going up");
        mFusedStylusPointerId.reset();
    }

    return false;
}

std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) {
    std::list<NotifyArgs> out;
@@ -1782,7 +1796,7 @@ std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) {
std::list<NotifyArgs> TouchInputMapper::updateExternalStylusState(const StylusState& state) {
    std::list<NotifyArgs> out;
    const bool buttonsChanged = mExternalStylusState.buttons != state.buttons;
    mExternalStylusState.copyFrom(state);
    mExternalStylusState = state;
    if (mFusedStylusPointerId || mExternalStylusFusionTimeout != LLONG_MAX || buttonsChanged) {
        // The following three cases are handled here:
        // - We're in the middle of a fused stream of data;
+2 −0
Original line number Diff line number Diff line
@@ -795,6 +795,8 @@ private:
    [[nodiscard]] std::list<NotifyArgs> abortPointerSimple(nsecs_t when, nsecs_t readTime,
                                                           uint32_t policyFlags);

    // Attempts to assign a pointer id to the external stylus. Returns true if the state should be
    // withheld from further processing while waiting for data from the stylus.
    bool assignExternalStylusId(const RawState& state, bool timeout);
    void applyExternalStylusButtonState(nsecs_t when);
    void applyExternalStylusTouchState(nsecs_t when);
Loading