1 /*
2 * Copyright (C) 2019 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 #include "../Macros.h"
18
19 #include "MultiTouchInputMapper.h"
20
21 namespace android {
22
23 // --- Constants ---
24
25 // Maximum number of slots supported when using the slot-based Multitouch Protocol B.
26 static constexpr size_t MAX_SLOTS = 32;
27
28 // --- MultiTouchMotionAccumulator ---
29
MultiTouchMotionAccumulator()30 MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
31 : mCurrentSlot(-1),
32 mSlots(nullptr),
33 mSlotCount(0),
34 mUsingSlotsProtocol(false),
35 mHaveStylus(false) {}
36
~MultiTouchMotionAccumulator()37 MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
38 delete[] mSlots;
39 }
40
configure(InputDeviceContext & deviceContext,size_t slotCount,bool usingSlotsProtocol)41 void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
42 bool usingSlotsProtocol) {
43 mSlotCount = slotCount;
44 mUsingSlotsProtocol = usingSlotsProtocol;
45 mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
46
47 delete[] mSlots;
48 mSlots = new Slot[slotCount];
49 }
50
reset(InputDeviceContext & deviceContext)51 void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
52 // Unfortunately there is no way to read the initial contents of the slots.
53 // So when we reset the accumulator, we must assume they are all zeroes.
54 if (mUsingSlotsProtocol) {
55 // Query the driver for the current slot index and use it as the initial slot
56 // before we start reading events from the device. It is possible that the
57 // current slot index will not be the same as it was when the first event was
58 // written into the evdev buffer, which means the input mapper could start
59 // out of sync with the initial state of the events in the evdev buffer.
60 // In the extremely unlikely case that this happens, the data from
61 // two slots will be confused until the next ABS_MT_SLOT event is received.
62 // This can cause the touch point to "jump", but at least there will be
63 // no stuck touches.
64 int32_t initialSlot;
65 status_t status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
66 if (status) {
67 ALOGD("Could not retrieve current multitouch slot index. status=%d", status);
68 initialSlot = -1;
69 }
70 clearSlots(initialSlot);
71 } else {
72 clearSlots(-1);
73 }
74 }
75
clearSlots(int32_t initialSlot)76 void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
77 if (mSlots) {
78 for (size_t i = 0; i < mSlotCount; i++) {
79 mSlots[i].clear();
80 }
81 }
82 mCurrentSlot = initialSlot;
83 }
84
process(const RawEvent * rawEvent)85 void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
86 if (rawEvent->type == EV_ABS) {
87 bool newSlot = false;
88 if (mUsingSlotsProtocol) {
89 if (rawEvent->code == ABS_MT_SLOT) {
90 mCurrentSlot = rawEvent->value;
91 newSlot = true;
92 }
93 } else if (mCurrentSlot < 0) {
94 mCurrentSlot = 0;
95 }
96
97 if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
98 #if DEBUG_POINTERS
99 if (newSlot) {
100 ALOGW("MultiTouch device emitted invalid slot index %d but it "
101 "should be between 0 and %zd; ignoring this slot.",
102 mCurrentSlot, mSlotCount - 1);
103 }
104 #endif
105 } else {
106 Slot* slot = &mSlots[mCurrentSlot];
107
108 switch (rawEvent->code) {
109 case ABS_MT_POSITION_X:
110 slot->mInUse = true;
111 slot->mAbsMTPositionX = rawEvent->value;
112 break;
113 case ABS_MT_POSITION_Y:
114 slot->mInUse = true;
115 slot->mAbsMTPositionY = rawEvent->value;
116 break;
117 case ABS_MT_TOUCH_MAJOR:
118 slot->mInUse = true;
119 slot->mAbsMTTouchMajor = rawEvent->value;
120 break;
121 case ABS_MT_TOUCH_MINOR:
122 slot->mInUse = true;
123 slot->mAbsMTTouchMinor = rawEvent->value;
124 slot->mHaveAbsMTTouchMinor = true;
125 break;
126 case ABS_MT_WIDTH_MAJOR:
127 slot->mInUse = true;
128 slot->mAbsMTWidthMajor = rawEvent->value;
129 break;
130 case ABS_MT_WIDTH_MINOR:
131 slot->mInUse = true;
132 slot->mAbsMTWidthMinor = rawEvent->value;
133 slot->mHaveAbsMTWidthMinor = true;
134 break;
135 case ABS_MT_ORIENTATION:
136 slot->mInUse = true;
137 slot->mAbsMTOrientation = rawEvent->value;
138 break;
139 case ABS_MT_TRACKING_ID:
140 if (mUsingSlotsProtocol && rawEvent->value < 0) {
141 // The slot is no longer in use but it retains its previous contents,
142 // which may be reused for subsequent touches.
143 slot->mInUse = false;
144 } else {
145 slot->mInUse = true;
146 slot->mAbsMTTrackingId = rawEvent->value;
147 }
148 break;
149 case ABS_MT_PRESSURE:
150 slot->mInUse = true;
151 slot->mAbsMTPressure = rawEvent->value;
152 break;
153 case ABS_MT_DISTANCE:
154 slot->mInUse = true;
155 slot->mAbsMTDistance = rawEvent->value;
156 break;
157 case ABS_MT_TOOL_TYPE:
158 slot->mInUse = true;
159 slot->mAbsMTToolType = rawEvent->value;
160 slot->mHaveAbsMTToolType = true;
161 break;
162 }
163 }
164 } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
165 // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
166 mCurrentSlot += 1;
167 }
168 }
169
finishSync()170 void MultiTouchMotionAccumulator::finishSync() {
171 if (!mUsingSlotsProtocol) {
172 clearSlots(-1);
173 }
174 }
175
hasStylus() const176 bool MultiTouchMotionAccumulator::hasStylus() const {
177 return mHaveStylus;
178 }
179
180 // --- MultiTouchMotionAccumulator::Slot ---
181
Slot()182 MultiTouchMotionAccumulator::Slot::Slot() {
183 clear();
184 }
185
clear()186 void MultiTouchMotionAccumulator::Slot::clear() {
187 mInUse = false;
188 mHaveAbsMTTouchMinor = false;
189 mHaveAbsMTWidthMinor = false;
190 mHaveAbsMTToolType = false;
191 mAbsMTPositionX = 0;
192 mAbsMTPositionY = 0;
193 mAbsMTTouchMajor = 0;
194 mAbsMTTouchMinor = 0;
195 mAbsMTWidthMajor = 0;
196 mAbsMTWidthMinor = 0;
197 mAbsMTOrientation = 0;
198 mAbsMTTrackingId = -1;
199 mAbsMTPressure = 0;
200 mAbsMTDistance = 0;
201 mAbsMTToolType = 0;
202 }
203
getToolType() const204 int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
205 if (mHaveAbsMTToolType) {
206 switch (mAbsMTToolType) {
207 case MT_TOOL_FINGER:
208 return AMOTION_EVENT_TOOL_TYPE_FINGER;
209 case MT_TOOL_PEN:
210 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
211 case MT_TOOL_PALM:
212 return AMOTION_EVENT_TOOL_TYPE_PALM;
213 }
214 }
215 return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
216 }
217
218 // --- MultiTouchInputMapper ---
219
MultiTouchInputMapper(InputDeviceContext & deviceContext)220 MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
221 : TouchInputMapper(deviceContext) {}
222
~MultiTouchInputMapper()223 MultiTouchInputMapper::~MultiTouchInputMapper() {}
224
reset(nsecs_t when)225 void MultiTouchInputMapper::reset(nsecs_t when) {
226 mMultiTouchMotionAccumulator.reset(getDeviceContext());
227
228 mPointerIdBits.clear();
229
230 TouchInputMapper::reset(when);
231 }
232
process(const RawEvent * rawEvent)233 void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
234 TouchInputMapper::process(rawEvent);
235
236 mMultiTouchMotionAccumulator.process(rawEvent);
237 }
238
syncTouch(nsecs_t when,RawState * outState)239 void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
240 size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
241 size_t outCount = 0;
242 BitSet32 newPointerIdBits;
243 mHavePointerIds = true;
244
245 for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
246 const MultiTouchMotionAccumulator::Slot* inSlot =
247 mMultiTouchMotionAccumulator.getSlot(inIndex);
248 if (!inSlot->isInUse()) {
249 continue;
250 }
251
252 if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
253 if (!mCurrentMotionAborted) {
254 ALOGI("Canceling touch gesture from device %s because the palm event was detected",
255 getDeviceName().c_str());
256 cancelTouch(when);
257 }
258 continue;
259 }
260
261 if (outCount >= MAX_POINTERS) {
262 #if DEBUG_POINTERS
263 ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
264 "ignoring the rest.",
265 getDeviceName().c_str(), MAX_POINTERS);
266 #endif
267 break; // too many fingers!
268 }
269
270 RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
271 outPointer.x = inSlot->getX();
272 outPointer.y = inSlot->getY();
273 outPointer.pressure = inSlot->getPressure();
274 outPointer.touchMajor = inSlot->getTouchMajor();
275 outPointer.touchMinor = inSlot->getTouchMinor();
276 outPointer.toolMajor = inSlot->getToolMajor();
277 outPointer.toolMinor = inSlot->getToolMinor();
278 outPointer.orientation = inSlot->getOrientation();
279 outPointer.distance = inSlot->getDistance();
280 outPointer.tiltX = 0;
281 outPointer.tiltY = 0;
282
283 outPointer.toolType = inSlot->getToolType();
284 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
285 outPointer.toolType = mTouchButtonAccumulator.getToolType();
286 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
287 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
288 }
289 }
290
291 bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
292 (mTouchButtonAccumulator.isHovering() ||
293 (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
294 outPointer.isHovering = isHovering;
295
296 // Assign pointer id using tracking id if available.
297 if (mHavePointerIds) {
298 int32_t trackingId = inSlot->getTrackingId();
299 int32_t id = -1;
300 if (trackingId >= 0) {
301 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
302 uint32_t n = idBits.clearFirstMarkedBit();
303 if (mPointerTrackingIdMap[n] == trackingId) {
304 id = n;
305 }
306 }
307
308 if (id < 0 && !mPointerIdBits.isFull()) {
309 id = mPointerIdBits.markFirstUnmarkedBit();
310 mPointerTrackingIdMap[id] = trackingId;
311 }
312 }
313 if (id < 0) {
314 mHavePointerIds = false;
315 outState->rawPointerData.clearIdBits();
316 newPointerIdBits.clear();
317 } else {
318 outPointer.id = id;
319 outState->rawPointerData.idToIndex[id] = outCount;
320 outState->rawPointerData.markIdBit(id, isHovering);
321 newPointerIdBits.markBit(id);
322 }
323 }
324 outCount += 1;
325 }
326
327 outState->rawPointerData.pointerCount = outCount;
328 mPointerIdBits = newPointerIdBits;
329
330 mMultiTouchMotionAccumulator.finishSync();
331 }
332
configureRawPointerAxes()333 void MultiTouchInputMapper::configureRawPointerAxes() {
334 TouchInputMapper::configureRawPointerAxes();
335
336 getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
337 getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
338 getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
339 getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
340 getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
341 getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
342 getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
343 getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
344 getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
345 getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
346 getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
347
348 if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
349 mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
350 size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
351 if (slotCount > MAX_SLOTS) {
352 ALOGW("MultiTouch Device %s reported %zu slots but the framework "
353 "only supports a maximum of %zu slots at this time.",
354 getDeviceName().c_str(), slotCount, MAX_SLOTS);
355 slotCount = MAX_SLOTS;
356 }
357 mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
358 true /*usingSlotsProtocol*/);
359 } else {
360 mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
361 false /*usingSlotsProtocol*/);
362 }
363 }
364
hasStylus() const365 bool MultiTouchInputMapper::hasStylus() const {
366 return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus();
367 }
368
369 } // namespace android
370