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