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

Commit 47db1c71 authored by Harry Cutts's avatar Harry Cutts
Browse files

Extract HardwareState conversion into a class and test it

Putting the conversion logic into its own class makes it more obvious
which variables are used for HardwareState conversion, and also provides
a clean public API to write tests against.

Bug: 251196347
Test: m inputflinger_tests && \
    $ANDROID_HOST_OUT/nativetest64/inputflinger_tests/inputflinger_tests \
    --gtest_filter='*HardwareStateConverterTest*'
Test: atest inputflinger_tests
Change-Id: I26771887b6b2eae46c9cec7190499da0016fdb1f
parent 15e72193
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ cc_library_headers {
        "include",
        "mapper",
        "mapper/accumulator",
        "mapper/gestures",
    ],
}

@@ -61,6 +62,7 @@ filegroup {
        "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
        "mapper/accumulator/TouchButtonAccumulator.cpp",
        "mapper/gestures/GesturesLogging.cpp",
        "mapper/gestures/HardwareStateConverter.cpp",
    ],
}

+11 −77
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

#include "../Macros.h"

#include <chrono>

#include <android/input.h>
#include <log/log_main.h>
#include "TouchCursorInputMapperCommon.h"
@@ -106,7 +104,7 @@ TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext)
      : InputMapper(deviceContext),
        mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
        mPointerController(getContext()->getPointerController(getDeviceId())),
        mTouchButtonAccumulator(deviceContext) {
        mStateConverter(deviceContext) {
    mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
    mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
    // Even though we don't explicitly delete copy/move semantics, it's safe to
@@ -116,16 +114,6 @@ TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext)
    mGestureInterpreter->SetCallback(gestureInterpreterCallback, this);
    // TODO(b/251196347): set a property provider, so we can change gesture properties.
    // TODO(b/251196347): set a timer provider, so the library can use timers.

    RawAbsoluteAxisInfo slotAxisInfo;
    getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
    if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
        ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
              "properly.",
              getDeviceName().c_str());
    }
    mMotionAccumulator.configure(getDeviceContext(), slotAxisInfo.maxValue + 1, true);
    mTouchButtonAccumulator.configure();
}

TouchpadInputMapper::~TouchpadInputMapper() {
@@ -139,82 +127,28 @@ uint32_t TouchpadInputMapper::getSources() const {
}

std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
    mCursorButtonAccumulator.reset(getDeviceContext());
    mTouchButtonAccumulator.reset();
    mMscTimestamp = 0;
    mStateConverter.reset();

    mButtonState = 0;
    return InputMapper::reset(when);
}

std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
    std::list<NotifyArgs> out = {};
    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        out = sync(rawEvent->when, rawEvent->readTime);
    }
    if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
        mMscTimestamp = rawEvent->value;
    }
    mCursorButtonAccumulator.process(rawEvent);
    mMotionAccumulator.process(rawEvent);
    mTouchButtonAccumulator.process(rawEvent);
    return out;
}

std::list<NotifyArgs> TouchpadInputMapper::sync(nsecs_t when, nsecs_t readTime) {
    HardwareState hwState;
    // The gestures library uses doubles to represent timestamps in seconds.
    hwState.timestamp = std::chrono::duration<stime_t>(std::chrono::nanoseconds(when)).count();
    hwState.msc_timestamp =
            std::chrono::duration<stime_t>(std::chrono::microseconds(mMscTimestamp)).count();

    hwState.buttons_down = 0;
    if (mCursorButtonAccumulator.isLeftPressed()) {
        hwState.buttons_down |= GESTURES_BUTTON_LEFT;
    }
    if (mCursorButtonAccumulator.isMiddlePressed()) {
        hwState.buttons_down |= GESTURES_BUTTON_MIDDLE;
    }
    if (mCursorButtonAccumulator.isRightPressed()) {
        hwState.buttons_down |= GESTURES_BUTTON_RIGHT;
    std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
    if (state) {
        return sendHardwareState(rawEvent->when, rawEvent->readTime, *state);
    } else {
        return {};
    }
    if (mCursorButtonAccumulator.isBackPressed() || mCursorButtonAccumulator.isSidePressed()) {
        hwState.buttons_down |= GESTURES_BUTTON_BACK;
    }
    if (mCursorButtonAccumulator.isForwardPressed() || mCursorButtonAccumulator.isExtraPressed()) {
        hwState.buttons_down |= GESTURES_BUTTON_FORWARD;
}

    std::vector<FingerState> fingers;
    for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
        MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
        if (slot.isInUse()) {
            FingerState& fingerState = fingers.emplace_back();
            fingerState = {};
            fingerState.touch_major = slot.getTouchMajor();
            fingerState.touch_minor = slot.getTouchMinor();
            fingerState.width_major = slot.getToolMajor();
            fingerState.width_minor = slot.getToolMinor();
            fingerState.pressure = slot.getPressure();
            fingerState.orientation = slot.getOrientation();
            fingerState.position_x = slot.getX();
            fingerState.position_y = slot.getY();
            fingerState.tracking_id = slot.getTrackingId();
        }
    }
    hwState.fingers = fingers.data();
    hwState.finger_cnt = fingers.size();
    hwState.touch_cnt = mTouchButtonAccumulator.getTouchCount();

std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime,
                                                             SelfContainedHardwareState schs) {
    mProcessing = true;
    mGestureInterpreter->PushHardwareState(&hwState);
    mGestureInterpreter->PushHardwareState(&schs.state);
    mProcessing = false;

    std::list<NotifyArgs> out = processGestures(when, readTime);

    mMotionAccumulator.finishSync();
    mMscTimestamp = 0;
    return out;
    return processGestures(when, readTime);
}

void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
+5 −8
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#pragma once

#include <memory>
#include <vector>

#include <PointerControllerInterface.h>

@@ -24,9 +25,7 @@
#include "InputDevice.h"
#include "InputMapper.h"
#include "NotifyArgs.h"
#include "accumulator/CursorButtonAccumulator.h"
#include "accumulator/MultiTouchMotionAccumulator.h"
#include "accumulator/TouchButtonAccumulator.h"
#include "gestures/HardwareStateConverter.h"

#include "include/gestures.h"

@@ -44,7 +43,8 @@ public:
    void consumeGesture(const Gesture* gesture);

private:
    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
    [[nodiscard]] std::list<NotifyArgs> sendHardwareState(nsecs_t when, nsecs_t readTime,
                                                          SelfContainedHardwareState schs);
    [[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime);
    NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
    [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
@@ -61,10 +61,7 @@ private:
            mGestureInterpreter;
    std::shared_ptr<PointerControllerInterface> mPointerController;

    CursorButtonAccumulator mCursorButtonAccumulator;
    MultiTouchMotionAccumulator mMotionAccumulator;
    TouchButtonAccumulator mTouchButtonAccumulator;
    int32_t mMscTimestamp = 0;
    HardwareStateConverter mStateConverter;

    bool mProcessing = false;
    std::vector<Gesture> mGesturesToProcess;
+109 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "gestures/HardwareStateConverter.h"

#include <chrono>
#include <vector>

#include <linux/input-event-codes.h>

namespace android {

HardwareStateConverter::HardwareStateConverter(InputDeviceContext& deviceContext)
      : mDeviceContext(deviceContext), mTouchButtonAccumulator(deviceContext) {
    RawAbsoluteAxisInfo slotAxisInfo;
    deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
    if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
        ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
              "properly.",
              deviceContext.getName().c_str());
    }
    mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true);
    mTouchButtonAccumulator.configure();
}

std::optional<SelfContainedHardwareState> HardwareStateConverter::processRawEvent(
        const RawEvent* rawEvent) {
    std::optional<SelfContainedHardwareState> out;
    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        out = produceHardwareState(rawEvent->when);
        mMotionAccumulator.finishSync();
        mMscTimestamp = 0;
    }
    if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
        mMscTimestamp = rawEvent->value;
    }
    mCursorButtonAccumulator.process(rawEvent);
    mMotionAccumulator.process(rawEvent);
    mTouchButtonAccumulator.process(rawEvent);
    return out;
}

SelfContainedHardwareState HardwareStateConverter::produceHardwareState(nsecs_t when) {
    SelfContainedHardwareState schs;
    // The gestures library uses doubles to represent timestamps in seconds.
    schs.state.timestamp = std::chrono::duration<stime_t>(std::chrono::nanoseconds(when)).count();
    schs.state.msc_timestamp =
            std::chrono::duration<stime_t>(std::chrono::microseconds(mMscTimestamp)).count();

    schs.state.buttons_down = 0;
    if (mCursorButtonAccumulator.isLeftPressed()) {
        schs.state.buttons_down |= GESTURES_BUTTON_LEFT;
    }
    if (mCursorButtonAccumulator.isMiddlePressed()) {
        schs.state.buttons_down |= GESTURES_BUTTON_MIDDLE;
    }
    if (mCursorButtonAccumulator.isRightPressed()) {
        schs.state.buttons_down |= GESTURES_BUTTON_RIGHT;
    }
    if (mCursorButtonAccumulator.isBackPressed() || mCursorButtonAccumulator.isSidePressed()) {
        schs.state.buttons_down |= GESTURES_BUTTON_BACK;
    }
    if (mCursorButtonAccumulator.isForwardPressed() || mCursorButtonAccumulator.isExtraPressed()) {
        schs.state.buttons_down |= GESTURES_BUTTON_FORWARD;
    }

    schs.fingers.clear();
    for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
        MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
        if (slot.isInUse()) {
            FingerState& fingerState = schs.fingers.emplace_back();
            fingerState = {};
            fingerState.touch_major = slot.getTouchMajor();
            fingerState.touch_minor = slot.getTouchMinor();
            fingerState.width_major = slot.getToolMajor();
            fingerState.width_minor = slot.getToolMinor();
            fingerState.pressure = slot.getPressure();
            fingerState.orientation = slot.getOrientation();
            fingerState.position_x = slot.getX();
            fingerState.position_y = slot.getY();
            fingerState.tracking_id = slot.getTrackingId();
        }
    }
    schs.state.fingers = schs.fingers.data();
    schs.state.finger_cnt = schs.fingers.size();
    schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount();
    return schs;
}

void HardwareStateConverter::reset() {
    mCursorButtonAccumulator.reset(mDeviceContext);
    mTouchButtonAccumulator.reset();
    mMscTimestamp = 0;
}

} // namespace android
+58 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <optional>

#include <utils/Timers.h>

#include "EventHub.h"
#include "InputDevice.h"
#include "accumulator/CursorButtonAccumulator.h"
#include "accumulator/MultiTouchMotionAccumulator.h"
#include "accumulator/TouchButtonAccumulator.h"

#include "include/gestures.h"

namespace android {

// A HardwareState struct, but bundled with a vector to contain its FingerStates, so you don't have
// to worry about where that memory is allocated.
struct SelfContainedHardwareState {
    HardwareState state;
    std::vector<FingerState> fingers;
};

// Converts RawEvents into the HardwareState structs used by the gestures library.
class HardwareStateConverter {
public:
    HardwareStateConverter(InputDeviceContext& deviceContext);

    std::optional<SelfContainedHardwareState> processRawEvent(const RawEvent* event);
    void reset();

private:
    SelfContainedHardwareState produceHardwareState(nsecs_t when);

    InputDeviceContext& mDeviceContext;
    CursorButtonAccumulator mCursorButtonAccumulator;
    MultiTouchMotionAccumulator mMotionAccumulator;
    TouchButtonAccumulator mTouchButtonAccumulator;
    int32_t mMscTimestamp = 0;
};

} // namespace android
Loading