blob: 41a8426e4202c42c4550fb693dfb9880b1267d11 [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
Prabir Pradhanf4d65b12022-02-10 07:15:38 -080021#include <android/sysprop/InputProperties.sysprop.h>
22
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070023namespace android {
24
25// --- Constants ---
26
27// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
28static constexpr size_t MAX_SLOTS = 32;
29
30// --- MultiTouchMotionAccumulator ---
31
32MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
33 : mCurrentSlot(-1),
34 mSlots(nullptr),
35 mSlotCount(0),
36 mUsingSlotsProtocol(false),
37 mHaveStylus(false) {}
38
39MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
40 delete[] mSlots;
41}
42
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080043void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070044 bool usingSlotsProtocol) {
45 mSlotCount = slotCount;
46 mUsingSlotsProtocol = usingSlotsProtocol;
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080047 mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070048
49 delete[] mSlots;
50 mSlots = new Slot[slotCount];
51}
52
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080053void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070054 // Unfortunately there is no way to read the initial contents of the slots.
55 // So when we reset the accumulator, we must assume they are all zeroes.
56 if (mUsingSlotsProtocol) {
57 // Query the driver for the current slot index and use it as the initial slot
58 // before we start reading events from the device. It is possible that the
59 // current slot index will not be the same as it was when the first event was
60 // written into the evdev buffer, which means the input mapper could start
61 // out of sync with the initial state of the events in the evdev buffer.
62 // In the extremely unlikely case that this happens, the data from
63 // two slots will be confused until the next ABS_MT_SLOT event is received.
64 // This can cause the touch point to "jump", but at least there will be
65 // no stuck touches.
66 int32_t initialSlot;
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080067 status_t status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070068 if (status) {
69 ALOGD("Could not retrieve current multitouch slot index. status=%d", status);
70 initialSlot = -1;
71 }
72 clearSlots(initialSlot);
73 } else {
74 clearSlots(-1);
75 }
76}
77
78void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
79 if (mSlots) {
80 for (size_t i = 0; i < mSlotCount; i++) {
81 mSlots[i].clear();
82 }
83 }
84 mCurrentSlot = initialSlot;
85}
86
87void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
88 if (rawEvent->type == EV_ABS) {
89 bool newSlot = false;
90 if (mUsingSlotsProtocol) {
91 if (rawEvent->code == ABS_MT_SLOT) {
92 mCurrentSlot = rawEvent->value;
93 newSlot = true;
94 }
95 } else if (mCurrentSlot < 0) {
96 mCurrentSlot = 0;
97 }
98
99 if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500100 if (DEBUG_POINTERS) {
101 if (newSlot) {
102 ALOGW("MultiTouch device emitted invalid slot index %d but it "
103 "should be between 0 and %zd; ignoring this slot.",
104 mCurrentSlot, mSlotCount - 1);
105 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700106 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700107 } else {
108 Slot* slot = &mSlots[mCurrentSlot];
Arthur Hung9ad18942021-06-19 02:04:46 +0000109 // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
110 // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
111 // updating the slot.
112 if (!mUsingSlotsProtocol) {
113 slot->mInUse = true;
114 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700115
116 switch (rawEvent->code) {
117 case ABS_MT_POSITION_X:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700118 slot->mAbsMTPositionX = rawEvent->value;
Arthur Hung9ad18942021-06-19 02:04:46 +0000119 warnIfNotInUse(*rawEvent, *slot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700120 break;
121 case ABS_MT_POSITION_Y:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700122 slot->mAbsMTPositionY = rawEvent->value;
Arthur Hung9ad18942021-06-19 02:04:46 +0000123 warnIfNotInUse(*rawEvent, *slot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700124 break;
125 case ABS_MT_TOUCH_MAJOR:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700126 slot->mAbsMTTouchMajor = rawEvent->value;
127 break;
128 case ABS_MT_TOUCH_MINOR:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700129 slot->mAbsMTTouchMinor = rawEvent->value;
130 slot->mHaveAbsMTTouchMinor = true;
131 break;
132 case ABS_MT_WIDTH_MAJOR:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700133 slot->mAbsMTWidthMajor = rawEvent->value;
134 break;
135 case ABS_MT_WIDTH_MINOR:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700136 slot->mAbsMTWidthMinor = rawEvent->value;
137 slot->mHaveAbsMTWidthMinor = true;
138 break;
139 case ABS_MT_ORIENTATION:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700140 slot->mAbsMTOrientation = rawEvent->value;
141 break;
142 case ABS_MT_TRACKING_ID:
143 if (mUsingSlotsProtocol && rawEvent->value < 0) {
144 // The slot is no longer in use but it retains its previous contents,
145 // which may be reused for subsequent touches.
146 slot->mInUse = false;
147 } else {
148 slot->mInUse = true;
149 slot->mAbsMTTrackingId = rawEvent->value;
150 }
151 break;
152 case ABS_MT_PRESSURE:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700153 slot->mAbsMTPressure = rawEvent->value;
154 break;
155 case ABS_MT_DISTANCE:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700156 slot->mAbsMTDistance = rawEvent->value;
157 break;
158 case ABS_MT_TOOL_TYPE:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700159 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
170void MultiTouchMotionAccumulator::finishSync() {
171 if (!mUsingSlotsProtocol) {
172 clearSlots(-1);
173 }
174}
175
176bool MultiTouchMotionAccumulator::hasStylus() const {
177 return mHaveStylus;
178}
179
Arthur Hung9ad18942021-06-19 02:04:46 +0000180void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
181 if (!slot.mInUse) {
182 ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
183 event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId);
184 }
185}
186
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700187// --- MultiTouchMotionAccumulator::Slot ---
188
189MultiTouchMotionAccumulator::Slot::Slot() {
190 clear();
191}
192
193void MultiTouchMotionAccumulator::Slot::clear() {
194 mInUse = false;
195 mHaveAbsMTTouchMinor = false;
196 mHaveAbsMTWidthMinor = false;
197 mHaveAbsMTToolType = false;
198 mAbsMTPositionX = 0;
199 mAbsMTPositionY = 0;
200 mAbsMTTouchMajor = 0;
201 mAbsMTTouchMinor = 0;
202 mAbsMTWidthMajor = 0;
203 mAbsMTWidthMinor = 0;
204 mAbsMTOrientation = 0;
205 mAbsMTTrackingId = -1;
206 mAbsMTPressure = 0;
207 mAbsMTDistance = 0;
208 mAbsMTToolType = 0;
209}
210
211int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
212 if (mHaveAbsMTToolType) {
213 switch (mAbsMTToolType) {
214 case MT_TOOL_FINGER:
215 return AMOTION_EVENT_TOOL_TYPE_FINGER;
216 case MT_TOOL_PEN:
217 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
Arthur Hung421eb1c2020-01-16 00:09:42 +0800218 case MT_TOOL_PALM:
219 return AMOTION_EVENT_TOOL_TYPE_PALM;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700220 }
221 }
222 return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
223}
224
225// --- MultiTouchInputMapper ---
226
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800227MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
228 : TouchInputMapper(deviceContext) {}
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700229
230MultiTouchInputMapper::~MultiTouchInputMapper() {}
231
232void MultiTouchInputMapper::reset(nsecs_t when) {
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800233 mMultiTouchMotionAccumulator.reset(getDeviceContext());
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700234
235 mPointerIdBits.clear();
236
237 TouchInputMapper::reset(when);
238}
239
240void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
241 TouchInputMapper::process(rawEvent);
242
243 mMultiTouchMotionAccumulator.process(rawEvent);
244}
245
arthurhungcc7f9802020-04-30 17:55:40 +0800246std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
247 const MultiTouchMotionAccumulator::Slot& inSlot) {
248 if (mHavePointerIds) {
249 int32_t trackingId = inSlot.getTrackingId();
250 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
251 int32_t n = idBits.clearFirstMarkedBit();
252 if (mPointerTrackingIdMap[n] == trackingId) {
253 return std::make_optional(n);
254 }
255 }
256 }
257 return std::nullopt;
258}
259
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700260void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
261 size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
262 size_t outCount = 0;
263 BitSet32 newPointerIdBits;
264 mHavePointerIds = true;
265
266 for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
267 const MultiTouchMotionAccumulator::Slot* inSlot =
268 mMultiTouchMotionAccumulator.getSlot(inIndex);
269 if (!inSlot->isInUse()) {
270 continue;
271 }
272
Arthur Hung421eb1c2020-01-16 00:09:42 +0800273 if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
arthurhungcc7f9802020-04-30 17:55:40 +0800274 std::optional<int32_t> id = getActiveBitId(*inSlot);
275 if (id) {
276 outState->rawPointerData.canceledIdBits.markBit(id.value());
Arthur Hung421eb1c2020-01-16 00:09:42 +0800277 }
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500278 if (DEBUG_POINTERS) {
279 ALOGI("Stop processing slot %zu for it received a palm event from device %s",
280 inIndex, getDeviceName().c_str());
281 }
arthurhungbf89a482020-04-17 17:37:55 +0800282 continue;
Arthur Hung421eb1c2020-01-16 00:09:42 +0800283 }
284
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700285 if (outCount >= MAX_POINTERS) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500286 if (DEBUG_POINTERS) {
Siarhei Vishniakou01747382022-01-20 13:23:27 -0800287 ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500288 "ignoring the rest.",
289 getDeviceName().c_str(), MAX_POINTERS);
290 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700291 break; // too many fingers!
292 }
293
294 RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
295 outPointer.x = inSlot->getX();
296 outPointer.y = inSlot->getY();
297 outPointer.pressure = inSlot->getPressure();
298 outPointer.touchMajor = inSlot->getTouchMajor();
299 outPointer.touchMinor = inSlot->getTouchMinor();
300 outPointer.toolMajor = inSlot->getToolMajor();
301 outPointer.toolMinor = inSlot->getToolMinor();
302 outPointer.orientation = inSlot->getOrientation();
303 outPointer.distance = inSlot->getDistance();
304 outPointer.tiltX = 0;
305 outPointer.tiltY = 0;
306
307 outPointer.toolType = inSlot->getToolType();
308 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
309 outPointer.toolType = mTouchButtonAccumulator.getToolType();
310 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
311 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
312 }
313 }
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800314 if (shouldSimulateStylusWithTouch() &&
315 outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
316 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
317 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700318
319 bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
320 (mTouchButtonAccumulator.isHovering() ||
321 (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
322 outPointer.isHovering = isHovering;
323
324 // Assign pointer id using tracking id if available.
325 if (mHavePointerIds) {
326 int32_t trackingId = inSlot->getTrackingId();
327 int32_t id = -1;
328 if (trackingId >= 0) {
329 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
330 uint32_t n = idBits.clearFirstMarkedBit();
331 if (mPointerTrackingIdMap[n] == trackingId) {
332 id = n;
333 }
334 }
335
336 if (id < 0 && !mPointerIdBits.isFull()) {
337 id = mPointerIdBits.markFirstUnmarkedBit();
338 mPointerTrackingIdMap[id] = trackingId;
339 }
340 }
341 if (id < 0) {
342 mHavePointerIds = false;
343 outState->rawPointerData.clearIdBits();
344 newPointerIdBits.clear();
345 } else {
346 outPointer.id = id;
347 outState->rawPointerData.idToIndex[id] = outCount;
348 outState->rawPointerData.markIdBit(id, isHovering);
349 newPointerIdBits.markBit(id);
350 }
351 }
352 outCount += 1;
353 }
354
355 outState->rawPointerData.pointerCount = outCount;
356 mPointerIdBits = newPointerIdBits;
357
358 mMultiTouchMotionAccumulator.finishSync();
359}
360
361void MultiTouchInputMapper::configureRawPointerAxes() {
362 TouchInputMapper::configureRawPointerAxes();
363
364 getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
365 getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
366 getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
367 getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
368 getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
369 getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
370 getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
371 getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
372 getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
373 getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
374 getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
375
376 if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
377 mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
378 size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
379 if (slotCount > MAX_SLOTS) {
380 ALOGW("MultiTouch Device %s reported %zu slots but the framework "
381 "only supports a maximum of %zu slots at this time.",
382 getDeviceName().c_str(), slotCount, MAX_SLOTS);
383 slotCount = MAX_SLOTS;
384 }
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800385 mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
386 true /*usingSlotsProtocol*/);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700387 } else {
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800388 mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700389 false /*usingSlotsProtocol*/);
390 }
391}
392
393bool MultiTouchInputMapper::hasStylus() const {
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800394 return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus() ||
395 shouldSimulateStylusWithTouch();
396}
397
398bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
399 static const bool SIMULATE_STYLUS_WITH_TOUCH =
400 sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
401 return SIMULATE_STYLUS_WITH_TOUCH &&
402 mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700403}
404
405} // namespace android