1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // clang-format off
18 #include "../Macros.h"
19 // clang-format on
20 #include "MultiTouchMotionAccumulator.h"
21 
22 namespace android {
23 
24 // --- MultiTouchMotionAccumulator ---
25 
MultiTouchMotionAccumulator()26 MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
27       : mCurrentSlot(-1), mUsingSlotsProtocol(false) {}
28 
configure(const InputDeviceContext & deviceContext,size_t slotCount,bool usingSlotsProtocol)29 void MultiTouchMotionAccumulator::configure(const InputDeviceContext& deviceContext,
30                                             size_t slotCount, bool usingSlotsProtocol) {
31     mUsingSlotsProtocol = usingSlotsProtocol;
32     mSlots = std::vector<Slot>(slotCount);
33     populateCurrentSlot(deviceContext);
34 }
35 
reset(const InputDeviceContext & deviceContext)36 void MultiTouchMotionAccumulator::reset(const InputDeviceContext& deviceContext) {
37     resetSlots();
38     syncSlots(deviceContext);
39 }
40 
resetSlots()41 void MultiTouchMotionAccumulator::resetSlots() {
42     for (Slot& slot : mSlots) {
43         slot.clear();
44     }
45     mCurrentSlot = -1;
46 }
47 
process(const RawEvent & rawEvent)48 void MultiTouchMotionAccumulator::process(const RawEvent& rawEvent) {
49     if (rawEvent.type == EV_ABS) {
50         bool newSlot = false;
51         if (mUsingSlotsProtocol) {
52             if (rawEvent.code == ABS_MT_SLOT) {
53                 mCurrentSlot = rawEvent.value;
54                 newSlot = true;
55             }
56         } else if (mCurrentSlot < 0) {
57             mCurrentSlot = 0;
58         }
59 
60         if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) {
61             if (newSlot) {
62                 ALOGW_IF(DEBUG_POINTERS,
63                          "MultiTouch device emitted invalid slot index %d but it "
64                          "should be between 0 and %zd; ignoring this slot.",
65                          mCurrentSlot, mSlots.size() - 1);
66             }
67         } else {
68             Slot& slot = mSlots[mCurrentSlot];
69             // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
70             // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
71             // updating the slot.
72             if (!mUsingSlotsProtocol) {
73                 slot.mInUse = true;
74             }
75             if (rawEvent.code == ABS_MT_POSITION_X || rawEvent.code == ABS_MT_POSITION_Y) {
76                 warnIfNotInUse(rawEvent, slot);
77             }
78             slot.populateAxisValue(rawEvent.code, rawEvent.value);
79         }
80     } else if (rawEvent.type == EV_SYN && rawEvent.code == SYN_MT_REPORT) {
81         // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
82         mCurrentSlot += 1;
83     }
84 }
85 
syncSlots(const InputDeviceContext & deviceContext)86 void MultiTouchMotionAccumulator::syncSlots(const InputDeviceContext& deviceContext) {
87     if (!mUsingSlotsProtocol) {
88         return;
89     }
90     constexpr std::array<int32_t, 11> axisCodes = {ABS_MT_POSITION_X,  ABS_MT_POSITION_Y,
91                                                    ABS_MT_TOUCH_MAJOR, ABS_MT_TOUCH_MINOR,
92                                                    ABS_MT_WIDTH_MAJOR, ABS_MT_WIDTH_MINOR,
93                                                    ABS_MT_ORIENTATION, ABS_MT_TRACKING_ID,
94                                                    ABS_MT_PRESSURE,    ABS_MT_DISTANCE,
95                                                    ABS_MT_TOOL_TYPE};
96     const size_t numSlots = mSlots.size();
97     for (int32_t axisCode : axisCodes) {
98         if (!deviceContext.hasAbsoluteAxis(axisCode)) {
99             continue;
100         }
101         const auto result = deviceContext.getMtSlotValues(axisCode, numSlots);
102         if (result.ok()) {
103             const std::vector<int32_t>& mtSlotValues = result.value();
104             for (size_t i = 1; i <= numSlots; ++i) {
105                 // The returned slot values are in a 1-indexed vector of size numSlots + 1.
106                 mSlots[i - 1].populateAxisValue(axisCode, mtSlotValues[i]);
107             }
108         } else {
109             ALOGE("Could not retrieve multi-touch slot value for axis=%d error=%s status=%d",
110                   axisCode, result.error().message().c_str(), result.error().code().value());
111         }
112     }
113     populateCurrentSlot(deviceContext);
114 }
115 
finishSync()116 void MultiTouchMotionAccumulator::finishSync() {
117     if (!mUsingSlotsProtocol) {
118         resetSlots();
119     }
120 }
121 
warnIfNotInUse(const RawEvent & event,const Slot & slot)122 void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
123     if (!slot.mInUse) {
124         ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
125               event.code, event.value, mCurrentSlot, slot.mAbsMtTrackingId);
126     }
127 }
128 
getActiveSlotsCount() const129 size_t MultiTouchMotionAccumulator::getActiveSlotsCount() const {
130     if (!mUsingSlotsProtocol) {
131         return mCurrentSlot < 0 ? 0 : mCurrentSlot;
132     }
133     return std::count_if(mSlots.begin(), mSlots.end(),
134                          [](const Slot& slot) { return slot.mInUse; });
135 }
136 
populateCurrentSlot(const android::InputDeviceContext & deviceContext)137 void MultiTouchMotionAccumulator::populateCurrentSlot(
138         const android::InputDeviceContext& deviceContext) {
139     if (!mUsingSlotsProtocol) {
140         return;
141     }
142     int32_t initialSlot;
143     if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
144         status == OK) {
145         mCurrentSlot = initialSlot;
146     } else {
147         ALOGE("Could not retrieve current multi-touch slot index. status=%s",
148               statusToString(status).c_str());
149     }
150 }
151 
152 // --- MultiTouchMotionAccumulator::Slot ---
153 
getToolType() const154 ToolType MultiTouchMotionAccumulator::Slot::getToolType() const {
155     if (mHaveAbsMtToolType) {
156         switch (mAbsMtToolType) {
157             case MT_TOOL_FINGER:
158                 return ToolType::FINGER;
159             case MT_TOOL_PEN:
160                 return ToolType::STYLUS;
161             case MT_TOOL_PALM:
162                 return ToolType::PALM;
163         }
164     }
165     return ToolType::UNKNOWN;
166 }
167 
populateAxisValue(int32_t axisCode,int32_t value)168 void MultiTouchMotionAccumulator::Slot::populateAxisValue(int32_t axisCode, int32_t value) {
169     switch (axisCode) {
170         case ABS_MT_POSITION_X:
171             mAbsMtPositionX = value;
172             break;
173         case ABS_MT_POSITION_Y:
174             mAbsMtPositionY = value;
175             break;
176         case ABS_MT_TOUCH_MAJOR:
177             mAbsMtTouchMajor = value;
178             break;
179         case ABS_MT_TOUCH_MINOR:
180             mAbsMtTouchMinor = value;
181             mHaveAbsMtTouchMinor = true;
182             break;
183         case ABS_MT_WIDTH_MAJOR:
184             mAbsMtWidthMajor = value;
185             break;
186         case ABS_MT_WIDTH_MINOR:
187             mAbsMtWidthMinor = value;
188             mHaveAbsMtWidthMinor = true;
189             break;
190         case ABS_MT_ORIENTATION:
191             mAbsMtOrientation = value;
192             break;
193         case ABS_MT_TRACKING_ID:
194             if (value < 0) {
195                 // The slot is no longer in use but it retains its previous contents,
196                 // which may be reused for subsequent touches.
197                 mInUse = false;
198             } else {
199                 mInUse = true;
200                 mAbsMtTrackingId = value;
201             }
202             break;
203         case ABS_MT_PRESSURE:
204             mAbsMtPressure = value;
205             break;
206         case ABS_MT_DISTANCE:
207             mAbsMtDistance = value;
208             break;
209         case ABS_MT_TOOL_TYPE:
210             mAbsMtToolType = value;
211             mHaveAbsMtToolType = true;
212             break;
213     }
214 }
215 
216 } // namespace android
217