blob: 8f5dc9bfd539c2b405e0db8a9980546785e607a3 [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),
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070034 mUsingSlotsProtocol(false),
35 mHaveStylus(false) {}
36
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080037void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070038 bool usingSlotsProtocol) {
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070039 mUsingSlotsProtocol = usingSlotsProtocol;
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080040 mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070041
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080042 mSlots = std::vector<Slot>(slotCount);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070043}
44
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080045void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070046 // Unfortunately there is no way to read the initial contents of the slots.
47 // So when we reset the accumulator, we must assume they are all zeroes.
48 if (mUsingSlotsProtocol) {
49 // Query the driver for the current slot index and use it as the initial slot
50 // before we start reading events from the device. It is possible that the
51 // current slot index will not be the same as it was when the first event was
52 // written into the evdev buffer, which means the input mapper could start
53 // out of sync with the initial state of the events in the evdev buffer.
54 // In the extremely unlikely case that this happens, the data from
55 // two slots will be confused until the next ABS_MT_SLOT event is received.
56 // This can cause the touch point to "jump", but at least there will be
57 // no stuck touches.
58 int32_t initialSlot;
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080059 status_t status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070060 if (status) {
61 ALOGD("Could not retrieve current multitouch slot index. status=%d", status);
62 initialSlot = -1;
63 }
64 clearSlots(initialSlot);
65 } else {
66 clearSlots(-1);
67 }
68}
69
70void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080071 for (Slot& slot : mSlots) {
72 slot.clear();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070073 }
74 mCurrentSlot = initialSlot;
75}
76
77void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
78 if (rawEvent->type == EV_ABS) {
79 bool newSlot = false;
80 if (mUsingSlotsProtocol) {
81 if (rawEvent->code == ABS_MT_SLOT) {
82 mCurrentSlot = rawEvent->value;
83 newSlot = true;
84 }
85 } else if (mCurrentSlot < 0) {
86 mCurrentSlot = 0;
87 }
88
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080089 if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -050090 if (DEBUG_POINTERS) {
91 if (newSlot) {
92 ALOGW("MultiTouch device emitted invalid slot index %d but it "
93 "should be between 0 and %zd; ignoring this slot.",
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080094 mCurrentSlot, mSlots.size() - 1);
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -050095 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070096 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070097 } else {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080098 Slot& slot = mSlots[mCurrentSlot];
Arthur Hung9ad18942021-06-19 02:04:46 +000099 // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
100 // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
101 // updating the slot.
102 if (!mUsingSlotsProtocol) {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800103 slot.mInUse = true;
Arthur Hung9ad18942021-06-19 02:04:46 +0000104 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700105
106 switch (rawEvent->code) {
107 case ABS_MT_POSITION_X:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800108 slot.mAbsMTPositionX = rawEvent->value;
109 warnIfNotInUse(*rawEvent, slot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700110 break;
111 case ABS_MT_POSITION_Y:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800112 slot.mAbsMTPositionY = rawEvent->value;
113 warnIfNotInUse(*rawEvent, slot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700114 break;
115 case ABS_MT_TOUCH_MAJOR:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800116 slot.mAbsMTTouchMajor = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700117 break;
118 case ABS_MT_TOUCH_MINOR:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800119 slot.mAbsMTTouchMinor = rawEvent->value;
120 slot.mHaveAbsMTTouchMinor = true;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700121 break;
122 case ABS_MT_WIDTH_MAJOR:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800123 slot.mAbsMTWidthMajor = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700124 break;
125 case ABS_MT_WIDTH_MINOR:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800126 slot.mAbsMTWidthMinor = rawEvent->value;
127 slot.mHaveAbsMTWidthMinor = true;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700128 break;
129 case ABS_MT_ORIENTATION:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800130 slot.mAbsMTOrientation = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700131 break;
132 case ABS_MT_TRACKING_ID:
133 if (mUsingSlotsProtocol && rawEvent->value < 0) {
134 // The slot is no longer in use but it retains its previous contents,
135 // which may be reused for subsequent touches.
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800136 slot.mInUse = false;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700137 } else {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800138 slot.mInUse = true;
139 slot.mAbsMTTrackingId = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700140 }
141 break;
142 case ABS_MT_PRESSURE:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800143 slot.mAbsMTPressure = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700144 break;
145 case ABS_MT_DISTANCE:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800146 slot.mAbsMTDistance = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700147 break;
148 case ABS_MT_TOOL_TYPE:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800149 slot.mAbsMTToolType = rawEvent->value;
150 slot.mHaveAbsMTToolType = true;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700151 break;
152 }
153 }
154 } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
155 // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
156 mCurrentSlot += 1;
157 }
158}
159
160void MultiTouchMotionAccumulator::finishSync() {
161 if (!mUsingSlotsProtocol) {
162 clearSlots(-1);
163 }
164}
165
166bool MultiTouchMotionAccumulator::hasStylus() const {
167 return mHaveStylus;
168}
169
Arthur Hung9ad18942021-06-19 02:04:46 +0000170void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
171 if (!slot.mInUse) {
172 ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
173 event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId);
174 }
175}
176
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700177// --- MultiTouchMotionAccumulator::Slot ---
178
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700179int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
180 if (mHaveAbsMTToolType) {
181 switch (mAbsMTToolType) {
182 case MT_TOOL_FINGER:
183 return AMOTION_EVENT_TOOL_TYPE_FINGER;
184 case MT_TOOL_PEN:
185 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
Arthur Hung421eb1c2020-01-16 00:09:42 +0800186 case MT_TOOL_PALM:
187 return AMOTION_EVENT_TOOL_TYPE_PALM;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700188 }
189 }
190 return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
191}
192
193// --- MultiTouchInputMapper ---
194
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800195MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
196 : TouchInputMapper(deviceContext) {}
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700197
198MultiTouchInputMapper::~MultiTouchInputMapper() {}
199
200void MultiTouchInputMapper::reset(nsecs_t when) {
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800201 mMultiTouchMotionAccumulator.reset(getDeviceContext());
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700202
203 mPointerIdBits.clear();
204
205 TouchInputMapper::reset(when);
206}
207
208void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
209 TouchInputMapper::process(rawEvent);
210
211 mMultiTouchMotionAccumulator.process(rawEvent);
212}
213
arthurhungcc7f9802020-04-30 17:55:40 +0800214std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
215 const MultiTouchMotionAccumulator::Slot& inSlot) {
216 if (mHavePointerIds) {
217 int32_t trackingId = inSlot.getTrackingId();
218 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
219 int32_t n = idBits.clearFirstMarkedBit();
220 if (mPointerTrackingIdMap[n] == trackingId) {
221 return std::make_optional(n);
222 }
223 }
224 }
225 return std::nullopt;
226}
227
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700228void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
229 size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
230 size_t outCount = 0;
231 BitSet32 newPointerIdBits;
232 mHavePointerIds = true;
233
234 for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800235 const MultiTouchMotionAccumulator::Slot& inSlot =
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700236 mMultiTouchMotionAccumulator.getSlot(inIndex);
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800237 if (!inSlot.isInUse()) {
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700238 continue;
239 }
240
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800241 if (inSlot.getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
242 std::optional<int32_t> id = getActiveBitId(inSlot);
arthurhungcc7f9802020-04-30 17:55:40 +0800243 if (id) {
244 outState->rawPointerData.canceledIdBits.markBit(id.value());
Arthur Hung421eb1c2020-01-16 00:09:42 +0800245 }
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500246 if (DEBUG_POINTERS) {
247 ALOGI("Stop processing slot %zu for it received a palm event from device %s",
248 inIndex, getDeviceName().c_str());
249 }
arthurhungbf89a482020-04-17 17:37:55 +0800250 continue;
Arthur Hung421eb1c2020-01-16 00:09:42 +0800251 }
252
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700253 if (outCount >= MAX_POINTERS) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500254 if (DEBUG_POINTERS) {
Siarhei Vishniakou01747382022-01-20 13:23:27 -0800255 ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500256 "ignoring the rest.",
257 getDeviceName().c_str(), MAX_POINTERS);
258 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700259 break; // too many fingers!
260 }
261
262 RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800263 outPointer.x = inSlot.getX();
264 outPointer.y = inSlot.getY();
265 outPointer.pressure = inSlot.getPressure();
266 outPointer.touchMajor = inSlot.getTouchMajor();
267 outPointer.touchMinor = inSlot.getTouchMinor();
268 outPointer.toolMajor = inSlot.getToolMajor();
269 outPointer.toolMinor = inSlot.getToolMinor();
270 outPointer.orientation = inSlot.getOrientation();
271 outPointer.distance = inSlot.getDistance();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700272 outPointer.tiltX = 0;
273 outPointer.tiltY = 0;
274
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800275 outPointer.toolType = inSlot.getToolType();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700276 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
277 outPointer.toolType = mTouchButtonAccumulator.getToolType();
278 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
279 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
280 }
281 }
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800282 if (shouldSimulateStylusWithTouch() &&
283 outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
284 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
285 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700286
287 bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
288 (mTouchButtonAccumulator.isHovering() ||
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800289 (mRawPointerAxes.pressure.valid && inSlot.getPressure() <= 0));
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700290 outPointer.isHovering = isHovering;
291
292 // Assign pointer id using tracking id if available.
293 if (mHavePointerIds) {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800294 int32_t trackingId = inSlot.getTrackingId();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700295 int32_t id = -1;
296 if (trackingId >= 0) {
297 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
298 uint32_t n = idBits.clearFirstMarkedBit();
299 if (mPointerTrackingIdMap[n] == trackingId) {
300 id = n;
301 }
302 }
303
304 if (id < 0 && !mPointerIdBits.isFull()) {
305 id = mPointerIdBits.markFirstUnmarkedBit();
306 mPointerTrackingIdMap[id] = trackingId;
307 }
308 }
309 if (id < 0) {
310 mHavePointerIds = false;
311 outState->rawPointerData.clearIdBits();
312 newPointerIdBits.clear();
313 } else {
314 outPointer.id = id;
315 outState->rawPointerData.idToIndex[id] = outCount;
316 outState->rawPointerData.markIdBit(id, isHovering);
317 newPointerIdBits.markBit(id);
318 }
319 }
320 outCount += 1;
321 }
322
323 outState->rawPointerData.pointerCount = outCount;
324 mPointerIdBits = newPointerIdBits;
325
326 mMultiTouchMotionAccumulator.finishSync();
327}
328
329void MultiTouchInputMapper::configureRawPointerAxes() {
330 TouchInputMapper::configureRawPointerAxes();
331
332 getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
333 getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
334 getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
335 getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
336 getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
337 getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
338 getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
339 getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
340 getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
341 getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
342 getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
343
344 if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
345 mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
346 size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
347 if (slotCount > MAX_SLOTS) {
348 ALOGW("MultiTouch Device %s reported %zu slots but the framework "
349 "only supports a maximum of %zu slots at this time.",
350 getDeviceName().c_str(), slotCount, MAX_SLOTS);
351 slotCount = MAX_SLOTS;
352 }
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800353 mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
354 true /*usingSlotsProtocol*/);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700355 } else {
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800356 mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700357 false /*usingSlotsProtocol*/);
358 }
359}
360
361bool MultiTouchInputMapper::hasStylus() const {
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800362 return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus() ||
363 shouldSimulateStylusWithTouch();
364}
365
366bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
367 static const bool SIMULATE_STYLUS_WITH_TOUCH =
368 sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
369 return SIMULATE_STYLUS_WITH_TOUCH &&
370 mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700371}
372
373} // namespace android