blob: cc323ad42e1cf1b40a9d81354283b52ad2fbab51 [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];
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070051
Prabir Pradhan6c7fd132022-09-27 19:32:43 +000052 mCurrentSlot = -1;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070053 if (mUsingSlotsProtocol) {
54 // Query the driver for the current slot index and use it as the initial slot
55 // before we start reading events from the device. It is possible that the
56 // current slot index will not be the same as it was when the first event was
57 // written into the evdev buffer, which means the input mapper could start
58 // out of sync with the initial state of the events in the evdev buffer.
59 // In the extremely unlikely case that this happens, the data from
60 // two slots will be confused until the next ABS_MT_SLOT event is received.
61 // This can cause the touch point to "jump", but at least there will be
62 // no stuck touches.
63 int32_t initialSlot;
Prabir Pradhan6c7fd132022-09-27 19:32:43 +000064 if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
65 status == OK) {
66 mCurrentSlot = initialSlot;
67 } else {
68 ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070069 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070070 }
71}
72
Prabir Pradhan6c7fd132022-09-27 19:32:43 +000073void MultiTouchMotionAccumulator::resetSlots() {
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070074 if (mSlots) {
75 for (size_t i = 0; i < mSlotCount; i++) {
76 mSlots[i].clear();
77 }
78 }
Prabir Pradhan6c7fd132022-09-27 19:32:43 +000079 mCurrentSlot = -1;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070080}
81
82void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
83 if (rawEvent->type == EV_ABS) {
84 bool newSlot = false;
85 if (mUsingSlotsProtocol) {
86 if (rawEvent->code == ABS_MT_SLOT) {
87 mCurrentSlot = rawEvent->value;
88 newSlot = true;
89 }
90 } else if (mCurrentSlot < 0) {
91 mCurrentSlot = 0;
92 }
93
94 if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -050095 if (DEBUG_POINTERS) {
96 if (newSlot) {
97 ALOGW("MultiTouch device emitted invalid slot index %d but it "
98 "should be between 0 and %zd; ignoring this slot.",
99 mCurrentSlot, mSlotCount - 1);
100 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700101 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700102 } else {
103 Slot* slot = &mSlots[mCurrentSlot];
Arthur Hung9ad18942021-06-19 02:04:46 +0000104 // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
105 // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
106 // updating the slot.
107 if (!mUsingSlotsProtocol) {
108 slot->mInUse = true;
109 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700110
111 switch (rawEvent->code) {
112 case ABS_MT_POSITION_X:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700113 slot->mAbsMTPositionX = rawEvent->value;
Arthur Hung9ad18942021-06-19 02:04:46 +0000114 warnIfNotInUse(*rawEvent, *slot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700115 break;
116 case ABS_MT_POSITION_Y:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700117 slot->mAbsMTPositionY = rawEvent->value;
Arthur Hung9ad18942021-06-19 02:04:46 +0000118 warnIfNotInUse(*rawEvent, *slot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700119 break;
120 case ABS_MT_TOUCH_MAJOR:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700121 slot->mAbsMTTouchMajor = rawEvent->value;
122 break;
123 case ABS_MT_TOUCH_MINOR:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700124 slot->mAbsMTTouchMinor = rawEvent->value;
125 slot->mHaveAbsMTTouchMinor = true;
126 break;
127 case ABS_MT_WIDTH_MAJOR:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700128 slot->mAbsMTWidthMajor = rawEvent->value;
129 break;
130 case ABS_MT_WIDTH_MINOR:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700131 slot->mAbsMTWidthMinor = rawEvent->value;
132 slot->mHaveAbsMTWidthMinor = true;
133 break;
134 case ABS_MT_ORIENTATION:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700135 slot->mAbsMTOrientation = rawEvent->value;
136 break;
137 case ABS_MT_TRACKING_ID:
138 if (mUsingSlotsProtocol && rawEvent->value < 0) {
139 // The slot is no longer in use but it retains its previous contents,
140 // which may be reused for subsequent touches.
141 slot->mInUse = false;
142 } else {
143 slot->mInUse = true;
144 slot->mAbsMTTrackingId = rawEvent->value;
145 }
146 break;
147 case ABS_MT_PRESSURE:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700148 slot->mAbsMTPressure = rawEvent->value;
149 break;
150 case ABS_MT_DISTANCE:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700151 slot->mAbsMTDistance = rawEvent->value;
152 break;
153 case ABS_MT_TOOL_TYPE:
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700154 slot->mAbsMTToolType = rawEvent->value;
155 slot->mHaveAbsMTToolType = true;
156 break;
157 }
158 }
159 } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
160 // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
161 mCurrentSlot += 1;
162 }
163}
164
165void MultiTouchMotionAccumulator::finishSync() {
166 if (!mUsingSlotsProtocol) {
Prabir Pradhan6c7fd132022-09-27 19:32:43 +0000167 resetSlots();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700168 }
169}
170
171bool MultiTouchMotionAccumulator::hasStylus() const {
172 return mHaveStylus;
173}
174
Arthur Hung9ad18942021-06-19 02:04:46 +0000175void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
176 if (!slot.mInUse) {
177 ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
178 event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId);
179 }
180}
181
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700182// --- MultiTouchMotionAccumulator::Slot ---
183
184MultiTouchMotionAccumulator::Slot::Slot() {
185 clear();
186}
187
188void MultiTouchMotionAccumulator::Slot::clear() {
189 mInUse = false;
190 mHaveAbsMTTouchMinor = false;
191 mHaveAbsMTWidthMinor = false;
192 mHaveAbsMTToolType = false;
193 mAbsMTPositionX = 0;
194 mAbsMTPositionY = 0;
195 mAbsMTTouchMajor = 0;
196 mAbsMTTouchMinor = 0;
197 mAbsMTWidthMajor = 0;
198 mAbsMTWidthMinor = 0;
199 mAbsMTOrientation = 0;
200 mAbsMTTrackingId = -1;
201 mAbsMTPressure = 0;
202 mAbsMTDistance = 0;
203 mAbsMTToolType = 0;
204}
205
206int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
207 if (mHaveAbsMTToolType) {
208 switch (mAbsMTToolType) {
209 case MT_TOOL_FINGER:
210 return AMOTION_EVENT_TOOL_TYPE_FINGER;
211 case MT_TOOL_PEN:
212 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
Arthur Hung421eb1c2020-01-16 00:09:42 +0800213 case MT_TOOL_PALM:
214 return AMOTION_EVENT_TOOL_TYPE_PALM;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700215 }
216 }
217 return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
218}
219
220// --- MultiTouchInputMapper ---
221
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800222MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
223 : TouchInputMapper(deviceContext) {}
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700224
225MultiTouchInputMapper::~MultiTouchInputMapper() {}
226
227void MultiTouchInputMapper::reset(nsecs_t when) {
Prabir Pradhan6c7fd132022-09-27 19:32:43 +0000228 // The evdev multi-touch protocol does not allow userspace applications to query the initial or
229 // current state of the pointers at any time. This means if we clear our accumulated state when
230 // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
231 // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
232 // rebuilding the state from scratch, we work around this kernel API limitation by never
233 // fully clearing any state specific to the multi-touch protocol.
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700234 TouchInputMapper::reset(when);
235}
236
237void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
238 TouchInputMapper::process(rawEvent);
239
240 mMultiTouchMotionAccumulator.process(rawEvent);
241}
242
arthurhungcc7f9802020-04-30 17:55:40 +0800243std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
244 const MultiTouchMotionAccumulator::Slot& inSlot) {
245 if (mHavePointerIds) {
246 int32_t trackingId = inSlot.getTrackingId();
247 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
248 int32_t n = idBits.clearFirstMarkedBit();
249 if (mPointerTrackingIdMap[n] == trackingId) {
250 return std::make_optional(n);
251 }
252 }
253 }
254 return std::nullopt;
255}
256
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700257void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
258 size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
259 size_t outCount = 0;
260 BitSet32 newPointerIdBits;
261 mHavePointerIds = true;
262
263 for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
264 const MultiTouchMotionAccumulator::Slot* inSlot =
265 mMultiTouchMotionAccumulator.getSlot(inIndex);
266 if (!inSlot->isInUse()) {
267 continue;
268 }
269
Arthur Hung421eb1c2020-01-16 00:09:42 +0800270 if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
arthurhungcc7f9802020-04-30 17:55:40 +0800271 std::optional<int32_t> id = getActiveBitId(*inSlot);
272 if (id) {
273 outState->rawPointerData.canceledIdBits.markBit(id.value());
Arthur Hung421eb1c2020-01-16 00:09:42 +0800274 }
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500275 if (DEBUG_POINTERS) {
276 ALOGI("Stop processing slot %zu for it received a palm event from device %s",
277 inIndex, getDeviceName().c_str());
278 }
arthurhungbf89a482020-04-17 17:37:55 +0800279 continue;
Arthur Hung421eb1c2020-01-16 00:09:42 +0800280 }
281
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700282 if (outCount >= MAX_POINTERS) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500283 if (DEBUG_POINTERS) {
Siarhei Vishniakou01747382022-01-20 13:23:27 -0800284 ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500285 "ignoring the rest.",
286 getDeviceName().c_str(), MAX_POINTERS);
287 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700288 break; // too many fingers!
289 }
290
291 RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
292 outPointer.x = inSlot->getX();
293 outPointer.y = inSlot->getY();
294 outPointer.pressure = inSlot->getPressure();
295 outPointer.touchMajor = inSlot->getTouchMajor();
296 outPointer.touchMinor = inSlot->getTouchMinor();
297 outPointer.toolMajor = inSlot->getToolMajor();
298 outPointer.toolMinor = inSlot->getToolMinor();
299 outPointer.orientation = inSlot->getOrientation();
300 outPointer.distance = inSlot->getDistance();
301 outPointer.tiltX = 0;
302 outPointer.tiltY = 0;
303
304 outPointer.toolType = inSlot->getToolType();
305 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
306 outPointer.toolType = mTouchButtonAccumulator.getToolType();
307 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
308 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
309 }
310 }
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800311 if (shouldSimulateStylusWithTouch() &&
312 outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
313 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
314 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700315
316 bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
317 (mTouchButtonAccumulator.isHovering() ||
318 (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
319 outPointer.isHovering = isHovering;
320
321 // Assign pointer id using tracking id if available.
322 if (mHavePointerIds) {
323 int32_t trackingId = inSlot->getTrackingId();
324 int32_t id = -1;
325 if (trackingId >= 0) {
326 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
327 uint32_t n = idBits.clearFirstMarkedBit();
328 if (mPointerTrackingIdMap[n] == trackingId) {
329 id = n;
330 }
331 }
332
333 if (id < 0 && !mPointerIdBits.isFull()) {
334 id = mPointerIdBits.markFirstUnmarkedBit();
335 mPointerTrackingIdMap[id] = trackingId;
336 }
337 }
338 if (id < 0) {
339 mHavePointerIds = false;
340 outState->rawPointerData.clearIdBits();
341 newPointerIdBits.clear();
342 } else {
343 outPointer.id = id;
344 outState->rawPointerData.idToIndex[id] = outCount;
345 outState->rawPointerData.markIdBit(id, isHovering);
346 newPointerIdBits.markBit(id);
347 }
348 }
349 outCount += 1;
350 }
351
352 outState->rawPointerData.pointerCount = outCount;
353 mPointerIdBits = newPointerIdBits;
354
355 mMultiTouchMotionAccumulator.finishSync();
356}
357
358void MultiTouchInputMapper::configureRawPointerAxes() {
359 TouchInputMapper::configureRawPointerAxes();
360
361 getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
362 getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
363 getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
364 getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
365 getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
366 getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
367 getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
368 getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
369 getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
370 getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
371 getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
372
373 if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
374 mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
375 size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
376 if (slotCount > MAX_SLOTS) {
377 ALOGW("MultiTouch Device %s reported %zu slots but the framework "
378 "only supports a maximum of %zu slots at this time.",
379 getDeviceName().c_str(), slotCount, MAX_SLOTS);
380 slotCount = MAX_SLOTS;
381 }
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800382 mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
383 true /*usingSlotsProtocol*/);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700384 } else {
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800385 mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700386 false /*usingSlotsProtocol*/);
387 }
388}
389
390bool MultiTouchInputMapper::hasStylus() const {
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800391 return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus() ||
392 shouldSimulateStylusWithTouch();
393}
394
395bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
396 static const bool SIMULATE_STYLUS_WITH_TOUCH =
397 sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
398 return SIMULATE_STYLUS_WITH_TOUCH &&
399 mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700400}
401
402} // namespace android