blob: ff3a592c0fccfdf98197589568128afd24e26489 [file] [log] [blame]
Prabir Pradhanbaa5c822019-08-30 15:27:05 -07001/*
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
Prabir Pradhan9244aea2020-02-05 20:31:40 -080017#include "../Macros.h"
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070018
19#include "MultiTouchInputMapper.h"
20
21namespace android {
22
23// --- Constants ---
24
25// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
26static constexpr size_t MAX_SLOTS = 32;
27
28// --- MultiTouchMotionAccumulator ---
29
30MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
31 : mCurrentSlot(-1),
32 mSlots(nullptr),
33 mSlotCount(0),
34 mUsingSlotsProtocol(false),
35 mHaveStylus(false) {}
36
37MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
38 delete[] mSlots;
39}
40
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080041void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070042 bool usingSlotsProtocol) {
43 mSlotCount = slotCount;
44 mUsingSlotsProtocol = usingSlotsProtocol;
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080045 mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070046
47 delete[] mSlots;
48 mSlots = new Slot[slotCount];
49}
50
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080051void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070052 // 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;
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080065 status_t status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070066 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
76void 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
85void 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) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -050098 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 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700104 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700105 } else {
106 Slot* slot = &mSlots[mCurrentSlot];
Arthur Hung9ad18942021-06-19 02:04:46 +0000107 // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
108 // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
109 // updating the slot.
110 if (!mUsingSlotsProtocol) {
111 slot->mInUse = true;
112 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700113
114 switch (rawEvent->code) {
115 case ABS_MT_POSITION_X:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700116 slot->mAbsMTPositionX = rawEvent->value;
Arthur Hung9ad18942021-06-19 02:04:46 +0000117 warnIfNotInUse(*rawEvent, *slot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700118 break;
119 case ABS_MT_POSITION_Y:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700120 slot->mAbsMTPositionY = rawEvent->value;
Arthur Hung9ad18942021-06-19 02:04:46 +0000121 warnIfNotInUse(*rawEvent, *slot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700122 break;
123 case ABS_MT_TOUCH_MAJOR:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700124 slot->mAbsMTTouchMajor = rawEvent->value;
125 break;
126 case ABS_MT_TOUCH_MINOR:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700127 slot->mAbsMTTouchMinor = rawEvent->value;
128 slot->mHaveAbsMTTouchMinor = true;
129 break;
130 case ABS_MT_WIDTH_MAJOR:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700131 slot->mAbsMTWidthMajor = rawEvent->value;
132 break;
133 case ABS_MT_WIDTH_MINOR:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700134 slot->mAbsMTWidthMinor = rawEvent->value;
135 slot->mHaveAbsMTWidthMinor = true;
136 break;
137 case ABS_MT_ORIENTATION:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700138 slot->mAbsMTOrientation = rawEvent->value;
139 break;
140 case ABS_MT_TRACKING_ID:
141 if (mUsingSlotsProtocol && rawEvent->value < 0) {
142 // The slot is no longer in use but it retains its previous contents,
143 // which may be reused for subsequent touches.
144 slot->mInUse = false;
145 } else {
146 slot->mInUse = true;
147 slot->mAbsMTTrackingId = rawEvent->value;
148 }
149 break;
150 case ABS_MT_PRESSURE:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700151 slot->mAbsMTPressure = rawEvent->value;
152 break;
153 case ABS_MT_DISTANCE:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700154 slot->mAbsMTDistance = rawEvent->value;
155 break;
156 case ABS_MT_TOOL_TYPE:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700157 slot->mAbsMTToolType = rawEvent->value;
158 slot->mHaveAbsMTToolType = true;
159 break;
160 }
161 }
162 } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
163 // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
164 mCurrentSlot += 1;
165 }
166}
167
168void MultiTouchMotionAccumulator::finishSync() {
169 if (!mUsingSlotsProtocol) {
170 clearSlots(-1);
171 }
172}
173
174bool MultiTouchMotionAccumulator::hasStylus() const {
175 return mHaveStylus;
176}
177
Arthur Hung9ad18942021-06-19 02:04:46 +0000178void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
179 if (!slot.mInUse) {
180 ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
181 event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId);
182 }
183}
184
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700185// --- MultiTouchMotionAccumulator::Slot ---
186
187MultiTouchMotionAccumulator::Slot::Slot() {
188 clear();
189}
190
191void MultiTouchMotionAccumulator::Slot::clear() {
192 mInUse = false;
193 mHaveAbsMTTouchMinor = false;
194 mHaveAbsMTWidthMinor = false;
195 mHaveAbsMTToolType = false;
196 mAbsMTPositionX = 0;
197 mAbsMTPositionY = 0;
198 mAbsMTTouchMajor = 0;
199 mAbsMTTouchMinor = 0;
200 mAbsMTWidthMajor = 0;
201 mAbsMTWidthMinor = 0;
202 mAbsMTOrientation = 0;
203 mAbsMTTrackingId = -1;
204 mAbsMTPressure = 0;
205 mAbsMTDistance = 0;
206 mAbsMTToolType = 0;
207}
208
209int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
210 if (mHaveAbsMTToolType) {
211 switch (mAbsMTToolType) {
212 case MT_TOOL_FINGER:
213 return AMOTION_EVENT_TOOL_TYPE_FINGER;
214 case MT_TOOL_PEN:
215 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
Arthur Hung421eb1c2020-01-16 00:09:42 +0800216 case MT_TOOL_PALM:
217 return AMOTION_EVENT_TOOL_TYPE_PALM;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700218 }
219 }
220 return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
221}
222
223// --- MultiTouchInputMapper ---
224
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800225MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
226 : TouchInputMapper(deviceContext) {}
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700227
228MultiTouchInputMapper::~MultiTouchInputMapper() {}
229
230void MultiTouchInputMapper::reset(nsecs_t when) {
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800231 mMultiTouchMotionAccumulator.reset(getDeviceContext());
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700232
233 mPointerIdBits.clear();
234
235 TouchInputMapper::reset(when);
236}
237
238void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
239 TouchInputMapper::process(rawEvent);
240
241 mMultiTouchMotionAccumulator.process(rawEvent);
242}
243
arthurhungcc7f9802020-04-30 17:55:40 +0800244std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
245 const MultiTouchMotionAccumulator::Slot& inSlot) {
246 if (mHavePointerIds) {
247 int32_t trackingId = inSlot.getTrackingId();
248 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
249 int32_t n = idBits.clearFirstMarkedBit();
250 if (mPointerTrackingIdMap[n] == trackingId) {
251 return std::make_optional(n);
252 }
253 }
254 }
255 return std::nullopt;
256}
257
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700258void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
259 size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
260 size_t outCount = 0;
261 BitSet32 newPointerIdBits;
262 mHavePointerIds = true;
263
264 for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
265 const MultiTouchMotionAccumulator::Slot* inSlot =
266 mMultiTouchMotionAccumulator.getSlot(inIndex);
267 if (!inSlot->isInUse()) {
268 continue;
269 }
270
Arthur Hung421eb1c2020-01-16 00:09:42 +0800271 if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
arthurhungcc7f9802020-04-30 17:55:40 +0800272 std::optional<int32_t> id = getActiveBitId(*inSlot);
273 if (id) {
274 outState->rawPointerData.canceledIdBits.markBit(id.value());
Arthur Hung421eb1c2020-01-16 00:09:42 +0800275 }
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500276 if (DEBUG_POINTERS) {
277 ALOGI("Stop processing slot %zu for it received a palm event from device %s",
278 inIndex, getDeviceName().c_str());
279 }
arthurhungbf89a482020-04-17 17:37:55 +0800280 continue;
Arthur Hung421eb1c2020-01-16 00:09:42 +0800281 }
282
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700283 if (outCount >= MAX_POINTERS) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500284 if (DEBUG_POINTERS) {
Siarhei Vishniakou01747382022-01-20 13:23:27 -0800285 ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500286 "ignoring the rest.",
287 getDeviceName().c_str(), MAX_POINTERS);
288 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700289 break; // too many fingers!
290 }
291
292 RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
293 outPointer.x = inSlot->getX();
294 outPointer.y = inSlot->getY();
295 outPointer.pressure = inSlot->getPressure();
296 outPointer.touchMajor = inSlot->getTouchMajor();
297 outPointer.touchMinor = inSlot->getTouchMinor();
298 outPointer.toolMajor = inSlot->getToolMajor();
299 outPointer.toolMinor = inSlot->getToolMinor();
300 outPointer.orientation = inSlot->getOrientation();
301 outPointer.distance = inSlot->getDistance();
302 outPointer.tiltX = 0;
303 outPointer.tiltY = 0;
304
305 outPointer.toolType = inSlot->getToolType();
306 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
307 outPointer.toolType = mTouchButtonAccumulator.getToolType();
308 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
309 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
310 }
311 }
312
313 bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
314 (mTouchButtonAccumulator.isHovering() ||
315 (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
316 outPointer.isHovering = isHovering;
317
318 // Assign pointer id using tracking id if available.
319 if (mHavePointerIds) {
320 int32_t trackingId = inSlot->getTrackingId();
321 int32_t id = -1;
322 if (trackingId >= 0) {
323 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
324 uint32_t n = idBits.clearFirstMarkedBit();
325 if (mPointerTrackingIdMap[n] == trackingId) {
326 id = n;
327 }
328 }
329
330 if (id < 0 && !mPointerIdBits.isFull()) {
331 id = mPointerIdBits.markFirstUnmarkedBit();
332 mPointerTrackingIdMap[id] = trackingId;
333 }
334 }
335 if (id < 0) {
336 mHavePointerIds = false;
337 outState->rawPointerData.clearIdBits();
338 newPointerIdBits.clear();
339 } else {
340 outPointer.id = id;
341 outState->rawPointerData.idToIndex[id] = outCount;
342 outState->rawPointerData.markIdBit(id, isHovering);
343 newPointerIdBits.markBit(id);
344 }
345 }
346 outCount += 1;
347 }
348
349 outState->rawPointerData.pointerCount = outCount;
350 mPointerIdBits = newPointerIdBits;
351
352 mMultiTouchMotionAccumulator.finishSync();
353}
354
355void MultiTouchInputMapper::configureRawPointerAxes() {
356 TouchInputMapper::configureRawPointerAxes();
357
358 getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
359 getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
360 getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
361 getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
362 getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
363 getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
364 getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
365 getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
366 getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
367 getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
368 getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
369
370 if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
371 mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
372 size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
373 if (slotCount > MAX_SLOTS) {
374 ALOGW("MultiTouch Device %s reported %zu slots but the framework "
375 "only supports a maximum of %zu slots at this time.",
376 getDeviceName().c_str(), slotCount, MAX_SLOTS);
377 slotCount = MAX_SLOTS;
378 }
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800379 mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
380 true /*usingSlotsProtocol*/);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700381 } else {
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800382 mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700383 false /*usingSlotsProtocol*/);
384 }
385}
386
387bool MultiTouchInputMapper::hasStylus() const {
388 return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus();
389}
390
391} // namespace android