blob: 1d53eaba6eef1b25d8c2c86bd279ce127cff4220 [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);
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080041 mSlots = std::vector<Slot>(slotCount);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070042
Prabir Pradhanafabcde2022-09-27 19:32:43 +000043 mCurrentSlot = -1;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070044 if (mUsingSlotsProtocol) {
45 // Query the driver for the current slot index and use it as the initial slot
46 // before we start reading events from the device. It is possible that the
47 // current slot index will not be the same as it was when the first event was
48 // written into the evdev buffer, which means the input mapper could start
49 // out of sync with the initial state of the events in the evdev buffer.
50 // In the extremely unlikely case that this happens, the data from
51 // two slots will be confused until the next ABS_MT_SLOT event is received.
52 // This can cause the touch point to "jump", but at least there will be
53 // no stuck touches.
54 int32_t initialSlot;
Prabir Pradhanafabcde2022-09-27 19:32:43 +000055 if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
56 status == OK) {
57 mCurrentSlot = initialSlot;
58 } else {
59 ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070060 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070061 }
62}
63
Prabir Pradhanafabcde2022-09-27 19:32:43 +000064void MultiTouchMotionAccumulator::resetSlots() {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080065 for (Slot& slot : mSlots) {
66 slot.clear();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070067 }
Prabir Pradhanafabcde2022-09-27 19:32:43 +000068 mCurrentSlot = -1;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070069}
70
71void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
72 if (rawEvent->type == EV_ABS) {
73 bool newSlot = false;
74 if (mUsingSlotsProtocol) {
75 if (rawEvent->code == ABS_MT_SLOT) {
76 mCurrentSlot = rawEvent->value;
77 newSlot = true;
78 }
79 } else if (mCurrentSlot < 0) {
80 mCurrentSlot = 0;
81 }
82
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080083 if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -050084 if (DEBUG_POINTERS) {
85 if (newSlot) {
86 ALOGW("MultiTouch device emitted invalid slot index %d but it "
87 "should be between 0 and %zd; ignoring this slot.",
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080088 mCurrentSlot, mSlots.size() - 1);
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -050089 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070090 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070091 } else {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080092 Slot& slot = mSlots[mCurrentSlot];
Arthur Hung9ad18942021-06-19 02:04:46 +000093 // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
94 // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
95 // updating the slot.
96 if (!mUsingSlotsProtocol) {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080097 slot.mInUse = true;
Arthur Hung9ad18942021-06-19 02:04:46 +000098 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070099
100 switch (rawEvent->code) {
101 case ABS_MT_POSITION_X:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800102 slot.mAbsMTPositionX = rawEvent->value;
103 warnIfNotInUse(*rawEvent, slot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700104 break;
105 case ABS_MT_POSITION_Y:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800106 slot.mAbsMTPositionY = rawEvent->value;
107 warnIfNotInUse(*rawEvent, slot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700108 break;
109 case ABS_MT_TOUCH_MAJOR:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800110 slot.mAbsMTTouchMajor = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700111 break;
112 case ABS_MT_TOUCH_MINOR:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800113 slot.mAbsMTTouchMinor = rawEvent->value;
114 slot.mHaveAbsMTTouchMinor = true;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700115 break;
116 case ABS_MT_WIDTH_MAJOR:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800117 slot.mAbsMTWidthMajor = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700118 break;
119 case ABS_MT_WIDTH_MINOR:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800120 slot.mAbsMTWidthMinor = rawEvent->value;
121 slot.mHaveAbsMTWidthMinor = true;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700122 break;
123 case ABS_MT_ORIENTATION:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800124 slot.mAbsMTOrientation = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700125 break;
126 case ABS_MT_TRACKING_ID:
127 if (mUsingSlotsProtocol && rawEvent->value < 0) {
128 // The slot is no longer in use but it retains its previous contents,
129 // which may be reused for subsequent touches.
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800130 slot.mInUse = false;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700131 } else {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800132 slot.mInUse = true;
133 slot.mAbsMTTrackingId = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700134 }
135 break;
136 case ABS_MT_PRESSURE:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800137 slot.mAbsMTPressure = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700138 break;
139 case ABS_MT_DISTANCE:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800140 slot.mAbsMTDistance = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700141 break;
142 case ABS_MT_TOOL_TYPE:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800143 slot.mAbsMTToolType = rawEvent->value;
144 slot.mHaveAbsMTToolType = true;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700145 break;
146 }
147 }
148 } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
149 // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
150 mCurrentSlot += 1;
151 }
152}
153
154void MultiTouchMotionAccumulator::finishSync() {
155 if (!mUsingSlotsProtocol) {
Prabir Pradhanafabcde2022-09-27 19:32:43 +0000156 resetSlots();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700157 }
158}
159
160bool MultiTouchMotionAccumulator::hasStylus() const {
161 return mHaveStylus;
162}
163
Arthur Hung9ad18942021-06-19 02:04:46 +0000164void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
165 if (!slot.mInUse) {
166 ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
167 event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId);
168 }
169}
170
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700171// --- MultiTouchMotionAccumulator::Slot ---
172
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700173int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
174 if (mHaveAbsMTToolType) {
175 switch (mAbsMTToolType) {
176 case MT_TOOL_FINGER:
177 return AMOTION_EVENT_TOOL_TYPE_FINGER;
178 case MT_TOOL_PEN:
179 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
Arthur Hung421eb1c2020-01-16 00:09:42 +0800180 case MT_TOOL_PALM:
181 return AMOTION_EVENT_TOOL_TYPE_PALM;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700182 }
183 }
184 return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
185}
186
187// --- MultiTouchInputMapper ---
188
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800189MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
190 : TouchInputMapper(deviceContext) {}
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700191
192MultiTouchInputMapper::~MultiTouchInputMapper() {}
193
Siarhei Vishniakou2935db72022-09-22 13:35:22 -0700194std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
Prabir Pradhanafabcde2022-09-27 19:32:43 +0000195 // The evdev multi-touch protocol does not allow userspace applications to query the initial or
196 // current state of the pointers at any time. This means if we clear our accumulated state when
197 // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
198 // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
199 // rebuilding the state from scratch, we work around this kernel API limitation by never
200 // fully clearing any state specific to the multi-touch protocol.
Siarhei Vishniakou2935db72022-09-22 13:35:22 -0700201 return TouchInputMapper::reset(when);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700202}
203
Siarhei Vishniakou2935db72022-09-22 13:35:22 -0700204std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {
205 std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700206
207 mMultiTouchMotionAccumulator.process(rawEvent);
Siarhei Vishniakou2935db72022-09-22 13:35:22 -0700208 return out;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700209}
210
arthurhungcc7f9802020-04-30 17:55:40 +0800211std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
212 const MultiTouchMotionAccumulator::Slot& inSlot) {
213 if (mHavePointerIds) {
214 int32_t trackingId = inSlot.getTrackingId();
215 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
216 int32_t n = idBits.clearFirstMarkedBit();
217 if (mPointerTrackingIdMap[n] == trackingId) {
218 return std::make_optional(n);
219 }
220 }
221 }
222 return std::nullopt;
223}
224
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700225void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
226 size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
227 size_t outCount = 0;
228 BitSet32 newPointerIdBits;
229 mHavePointerIds = true;
230
231 for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800232 const MultiTouchMotionAccumulator::Slot& inSlot =
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700233 mMultiTouchMotionAccumulator.getSlot(inIndex);
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800234 if (!inSlot.isInUse()) {
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700235 continue;
236 }
237
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800238 if (inSlot.getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
239 std::optional<int32_t> id = getActiveBitId(inSlot);
arthurhungcc7f9802020-04-30 17:55:40 +0800240 if (id) {
241 outState->rawPointerData.canceledIdBits.markBit(id.value());
Arthur Hung421eb1c2020-01-16 00:09:42 +0800242 }
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500243 if (DEBUG_POINTERS) {
244 ALOGI("Stop processing slot %zu for it received a palm event from device %s",
245 inIndex, getDeviceName().c_str());
246 }
arthurhungbf89a482020-04-17 17:37:55 +0800247 continue;
Arthur Hung421eb1c2020-01-16 00:09:42 +0800248 }
249
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700250 if (outCount >= MAX_POINTERS) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500251 if (DEBUG_POINTERS) {
Siarhei Vishniakou01747382022-01-20 13:23:27 -0800252 ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500253 "ignoring the rest.",
254 getDeviceName().c_str(), MAX_POINTERS);
255 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700256 break; // too many fingers!
257 }
258
259 RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800260 outPointer.x = inSlot.getX();
261 outPointer.y = inSlot.getY();
262 outPointer.pressure = inSlot.getPressure();
263 outPointer.touchMajor = inSlot.getTouchMajor();
264 outPointer.touchMinor = inSlot.getTouchMinor();
265 outPointer.toolMajor = inSlot.getToolMajor();
266 outPointer.toolMinor = inSlot.getToolMinor();
267 outPointer.orientation = inSlot.getOrientation();
268 outPointer.distance = inSlot.getDistance();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700269 outPointer.tiltX = 0;
270 outPointer.tiltY = 0;
271
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800272 outPointer.toolType = inSlot.getToolType();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700273 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
274 outPointer.toolType = mTouchButtonAccumulator.getToolType();
275 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
276 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
277 }
278 }
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800279 if (shouldSimulateStylusWithTouch() &&
280 outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
281 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
282 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700283
284 bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
285 (mTouchButtonAccumulator.isHovering() ||
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800286 (mRawPointerAxes.pressure.valid && inSlot.getPressure() <= 0));
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700287 outPointer.isHovering = isHovering;
288
289 // Assign pointer id using tracking id if available.
290 if (mHavePointerIds) {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800291 int32_t trackingId = inSlot.getTrackingId();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700292 int32_t id = -1;
293 if (trackingId >= 0) {
294 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
295 uint32_t n = idBits.clearFirstMarkedBit();
296 if (mPointerTrackingIdMap[n] == trackingId) {
297 id = n;
298 }
299 }
300
301 if (id < 0 && !mPointerIdBits.isFull()) {
302 id = mPointerIdBits.markFirstUnmarkedBit();
303 mPointerTrackingIdMap[id] = trackingId;
304 }
305 }
306 if (id < 0) {
307 mHavePointerIds = false;
308 outState->rawPointerData.clearIdBits();
309 newPointerIdBits.clear();
310 } else {
311 outPointer.id = id;
312 outState->rawPointerData.idToIndex[id] = outCount;
313 outState->rawPointerData.markIdBit(id, isHovering);
314 newPointerIdBits.markBit(id);
315 }
316 }
317 outCount += 1;
318 }
319
320 outState->rawPointerData.pointerCount = outCount;
321 mPointerIdBits = newPointerIdBits;
322
323 mMultiTouchMotionAccumulator.finishSync();
324}
325
326void MultiTouchInputMapper::configureRawPointerAxes() {
327 TouchInputMapper::configureRawPointerAxes();
328
329 getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
330 getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
331 getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
332 getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
333 getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
334 getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
335 getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
336 getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
337 getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
338 getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
339 getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
340
341 if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
342 mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
343 size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
344 if (slotCount > MAX_SLOTS) {
345 ALOGW("MultiTouch Device %s reported %zu slots but the framework "
346 "only supports a maximum of %zu slots at this time.",
347 getDeviceName().c_str(), slotCount, MAX_SLOTS);
348 slotCount = MAX_SLOTS;
349 }
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800350 mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
351 true /*usingSlotsProtocol*/);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700352 } else {
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800353 mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700354 false /*usingSlotsProtocol*/);
355 }
356}
357
358bool MultiTouchInputMapper::hasStylus() const {
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800359 return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus() ||
360 shouldSimulateStylusWithTouch();
361}
362
363bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
364 static const bool SIMULATE_STYLUS_WITH_TOUCH =
365 sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
366 return SIMULATE_STYLUS_WITH_TOUCH &&
367 mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700368}
369
370} // namespace android