/* * Copyright (C) 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. */ // clang-format off #include "../Macros.h" // clang-format on #include "MultiTouchMotionAccumulator.h" namespace android { // --- MultiTouchMotionAccumulator --- MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() : mCurrentSlot(-1), mUsingSlotsProtocol(false) {} void MultiTouchMotionAccumulator::configure(const InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol) { mUsingSlotsProtocol = usingSlotsProtocol; mSlots = std::vector(slotCount); populateCurrentSlot(deviceContext); } void MultiTouchMotionAccumulator::reset(const InputDeviceContext& deviceContext) { resetSlots(); syncSlots(deviceContext); } void MultiTouchMotionAccumulator::resetSlots() { for (Slot& slot : mSlots) { slot.clear(); } mCurrentSlot = -1; } void MultiTouchMotionAccumulator::process(const RawEvent& rawEvent) { if (rawEvent.type == EV_ABS) { bool newSlot = false; if (mUsingSlotsProtocol) { if (rawEvent.code == ABS_MT_SLOT) { mCurrentSlot = rawEvent.value; newSlot = true; } } else if (mCurrentSlot < 0) { mCurrentSlot = 0; } if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) { if (newSlot) { ALOGW_IF(DEBUG_POINTERS, "MultiTouch device emitted invalid slot index %d but it " "should be between 0 and %zd; ignoring this slot.", mCurrentSlot, mSlots.size() - 1); } } else { Slot& slot = mSlots[mCurrentSlot]; // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while // updating the slot. if (!mUsingSlotsProtocol) { slot.mInUse = true; } if (rawEvent.code == ABS_MT_POSITION_X || rawEvent.code == ABS_MT_POSITION_Y) { warnIfNotInUse(rawEvent, slot); } slot.populateAxisValue(rawEvent.code, rawEvent.value); } } else if (rawEvent.type == EV_SYN && rawEvent.code == SYN_MT_REPORT) { // MultiTouch Sync: The driver has returned all data for *one* of the pointers. mCurrentSlot += 1; } } void MultiTouchMotionAccumulator::syncSlots(const InputDeviceContext& deviceContext) { if (!mUsingSlotsProtocol) { return; } constexpr std::array axisCodes = {ABS_MT_POSITION_X, ABS_MT_POSITION_Y, ABS_MT_TOUCH_MAJOR, ABS_MT_TOUCH_MINOR, ABS_MT_WIDTH_MAJOR, ABS_MT_WIDTH_MINOR, ABS_MT_ORIENTATION, ABS_MT_TRACKING_ID, ABS_MT_PRESSURE, ABS_MT_DISTANCE, ABS_MT_TOOL_TYPE}; const size_t numSlots = mSlots.size(); for (int32_t axisCode : axisCodes) { if (!deviceContext.hasAbsoluteAxis(axisCode)) { continue; } const auto result = deviceContext.getMtSlotValues(axisCode, numSlots); if (result.ok()) { const std::vector& mtSlotValues = result.value(); for (size_t i = 1; i <= numSlots; ++i) { // The returned slot values are in a 1-indexed vector of size numSlots + 1. mSlots[i - 1].populateAxisValue(axisCode, mtSlotValues[i]); } } else { ALOGE("Could not retrieve multi-touch slot value for axis=%d error=%s status=%d", axisCode, result.error().message().c_str(), result.error().code().value()); } } populateCurrentSlot(deviceContext); } void MultiTouchMotionAccumulator::finishSync() { if (!mUsingSlotsProtocol) { resetSlots(); } } void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) { if (!slot.mInUse) { ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i", event.code, event.value, mCurrentSlot, slot.mAbsMtTrackingId); } } size_t MultiTouchMotionAccumulator::getActiveSlotsCount() const { if (!mUsingSlotsProtocol) { return mCurrentSlot < 0 ? 0 : mCurrentSlot; } return std::count_if(mSlots.begin(), mSlots.end(), [](const Slot& slot) { return slot.mInUse; }); } void MultiTouchMotionAccumulator::populateCurrentSlot( const android::InputDeviceContext& deviceContext) { if (!mUsingSlotsProtocol) { return; } int32_t initialSlot; if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); status == OK) { mCurrentSlot = initialSlot; } else { ALOGE("Could not retrieve current multi-touch slot index. status=%s", statusToString(status).c_str()); } } // --- MultiTouchMotionAccumulator::Slot --- ToolType MultiTouchMotionAccumulator::Slot::getToolType() const { if (mHaveAbsMtToolType) { switch (mAbsMtToolType) { case MT_TOOL_FINGER: return ToolType::FINGER; case MT_TOOL_PEN: return ToolType::STYLUS; case MT_TOOL_PALM: return ToolType::PALM; } } return ToolType::UNKNOWN; } void MultiTouchMotionAccumulator::Slot::populateAxisValue(int32_t axisCode, int32_t value) { switch (axisCode) { case ABS_MT_POSITION_X: mAbsMtPositionX = value; break; case ABS_MT_POSITION_Y: mAbsMtPositionY = value; break; case ABS_MT_TOUCH_MAJOR: mAbsMtTouchMajor = value; break; case ABS_MT_TOUCH_MINOR: mAbsMtTouchMinor = value; mHaveAbsMtTouchMinor = true; break; case ABS_MT_WIDTH_MAJOR: mAbsMtWidthMajor = value; break; case ABS_MT_WIDTH_MINOR: mAbsMtWidthMinor = value; mHaveAbsMtWidthMinor = true; break; case ABS_MT_ORIENTATION: mAbsMtOrientation = value; break; case ABS_MT_TRACKING_ID: if (value < 0) { // The slot is no longer in use but it retains its previous contents, // which may be reused for subsequent touches. mInUse = false; } else { mInUse = true; mAbsMtTrackingId = value; } break; case ABS_MT_PRESSURE: mAbsMtPressure = value; break; case ABS_MT_DISTANCE: mAbsMtDistance = value; break; case ABS_MT_TOOL_TYPE: mAbsMtToolType = value; mHaveAbsMtToolType = true; break; } } } // namespace android