1 /*
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
17 #include "Macros.h"
18
19 #include "MultiTouchInputMapper.h"
20
21 namespace android {
22
23 // --- Constants ---
24
25 // Maximum number of slots supported when using the slot-based Multitouch Protocol B.
26 static constexpr size_t MAX_SLOTS = 32;
27
28 // --- MultiTouchMotionAccumulator ---
29
MultiTouchMotionAccumulator()30 MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
31 : mCurrentSlot(-1),
32 mSlots(nullptr),
33 mSlotCount(0),
34 mUsingSlotsProtocol(false),
35 mHaveStylus(false) {}
36
~MultiTouchMotionAccumulator()37 MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
38 delete[] mSlots;
39 }
40
configure(InputDevice * device,size_t slotCount,bool usingSlotsProtocol)41 void MultiTouchMotionAccumulator::configure(InputDevice* device, size_t slotCount,
42 bool usingSlotsProtocol) {
43 mSlotCount = slotCount;
44 mUsingSlotsProtocol = usingSlotsProtocol;
45 mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
46
47 delete[] mSlots;
48 mSlots = new Slot[slotCount];
49 }
50
reset(InputDevice * device)51 void MultiTouchMotionAccumulator::reset(InputDevice* device) {
52 // 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;
65 status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), ABS_MT_SLOT,
66 &initialSlot);
67 if (status) {
68 ALOGD("Could not retrieve current multitouch slot index. status=%d", status);
69 initialSlot = -1;
70 }
71 clearSlots(initialSlot);
72 } else {
73 clearSlots(-1);
74 }
75 mDeviceTimestamp = 0;
76 }
77
clearSlots(int32_t initialSlot)78 void 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
process(const RawEvent * rawEvent)87 void 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) {
100 #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 }
106 #endif
107 } else {
108 Slot* slot = &mSlots[mCurrentSlot];
109
110 switch (rawEvent->code) {
111 case ABS_MT_POSITION_X:
112 slot->mInUse = true;
113 slot->mAbsMTPositionX = rawEvent->value;
114 break;
115 case ABS_MT_POSITION_Y:
116 slot->mInUse = true;
117 slot->mAbsMTPositionY = rawEvent->value;
118 break;
119 case ABS_MT_TOUCH_MAJOR:
120 slot->mInUse = true;
121 slot->mAbsMTTouchMajor = rawEvent->value;
122 break;
123 case ABS_MT_TOUCH_MINOR:
124 slot->mInUse = true;
125 slot->mAbsMTTouchMinor = rawEvent->value;
126 slot->mHaveAbsMTTouchMinor = true;
127 break;
128 case ABS_MT_WIDTH_MAJOR:
129 slot->mInUse = true;
130 slot->mAbsMTWidthMajor = rawEvent->value;
131 break;
132 case ABS_MT_WIDTH_MINOR:
133 slot->mInUse = true;
134 slot->mAbsMTWidthMinor = rawEvent->value;
135 slot->mHaveAbsMTWidthMinor = true;
136 break;
137 case ABS_MT_ORIENTATION:
138 slot->mInUse = true;
139 slot->mAbsMTOrientation = rawEvent->value;
140 break;
141 case ABS_MT_TRACKING_ID:
142 if (mUsingSlotsProtocol && rawEvent->value < 0) {
143 // The slot is no longer in use but it retains its previous contents,
144 // which may be reused for subsequent touches.
145 slot->mInUse = false;
146 } else {
147 slot->mInUse = true;
148 slot->mAbsMTTrackingId = rawEvent->value;
149 }
150 break;
151 case ABS_MT_PRESSURE:
152 slot->mInUse = true;
153 slot->mAbsMTPressure = rawEvent->value;
154 break;
155 case ABS_MT_DISTANCE:
156 slot->mInUse = true;
157 slot->mAbsMTDistance = rawEvent->value;
158 break;
159 case ABS_MT_TOOL_TYPE:
160 slot->mInUse = true;
161 slot->mAbsMTToolType = rawEvent->value;
162 slot->mHaveAbsMTToolType = true;
163 break;
164 }
165 }
166 } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
167 // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
168 mCurrentSlot += 1;
169 } else if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
170 mDeviceTimestamp = rawEvent->value;
171 }
172 }
173
finishSync()174 void MultiTouchMotionAccumulator::finishSync() {
175 if (!mUsingSlotsProtocol) {
176 clearSlots(-1);
177 }
178 }
179
hasStylus() const180 bool MultiTouchMotionAccumulator::hasStylus() const {
181 return mHaveStylus;
182 }
183
184 // --- MultiTouchMotionAccumulator::Slot ---
185
Slot()186 MultiTouchMotionAccumulator::Slot::Slot() {
187 clear();
188 }
189
clear()190 void MultiTouchMotionAccumulator::Slot::clear() {
191 mInUse = false;
192 mHaveAbsMTTouchMinor = false;
193 mHaveAbsMTWidthMinor = false;
194 mHaveAbsMTToolType = false;
195 mAbsMTPositionX = 0;
196 mAbsMTPositionY = 0;
197 mAbsMTTouchMajor = 0;
198 mAbsMTTouchMinor = 0;
199 mAbsMTWidthMajor = 0;
200 mAbsMTWidthMinor = 0;
201 mAbsMTOrientation = 0;
202 mAbsMTTrackingId = -1;
203 mAbsMTPressure = 0;
204 mAbsMTDistance = 0;
205 mAbsMTToolType = 0;
206 }
207
getToolType() const208 int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
209 if (mHaveAbsMTToolType) {
210 switch (mAbsMTToolType) {
211 case MT_TOOL_FINGER:
212 return AMOTION_EVENT_TOOL_TYPE_FINGER;
213 case MT_TOOL_PEN:
214 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
215 }
216 }
217 return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
218 }
219
220 // --- MultiTouchInputMapper ---
221
MultiTouchInputMapper(InputDevice * device)222 MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {}
223
~MultiTouchInputMapper()224 MultiTouchInputMapper::~MultiTouchInputMapper() {}
225
reset(nsecs_t when)226 void MultiTouchInputMapper::reset(nsecs_t when) {
227 mMultiTouchMotionAccumulator.reset(getDevice());
228
229 mPointerIdBits.clear();
230
231 TouchInputMapper::reset(when);
232 }
233
process(const RawEvent * rawEvent)234 void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
235 TouchInputMapper::process(rawEvent);
236
237 mMultiTouchMotionAccumulator.process(rawEvent);
238 }
239
syncTouch(nsecs_t when,RawState * outState)240 void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
241 size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
242 size_t outCount = 0;
243 BitSet32 newPointerIdBits;
244 mHavePointerIds = true;
245
246 for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
247 const MultiTouchMotionAccumulator::Slot* inSlot =
248 mMultiTouchMotionAccumulator.getSlot(inIndex);
249 if (!inSlot->isInUse()) {
250 continue;
251 }
252
253 if (outCount >= MAX_POINTERS) {
254 #if DEBUG_POINTERS
255 ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
256 "ignoring the rest.",
257 getDeviceName().c_str(), MAX_POINTERS);
258 #endif
259 break; // too many fingers!
260 }
261
262 RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
263 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();
272 outPointer.tiltX = 0;
273 outPointer.tiltY = 0;
274
275 outPointer.toolType = inSlot->getToolType();
276 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 }
282
283 bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
284 (mTouchButtonAccumulator.isHovering() ||
285 (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
286 outPointer.isHovering = isHovering;
287
288 // Assign pointer id using tracking id if available.
289 if (mHavePointerIds) {
290 int32_t trackingId = inSlot->getTrackingId();
291 int32_t id = -1;
292 if (trackingId >= 0) {
293 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
294 uint32_t n = idBits.clearFirstMarkedBit();
295 if (mPointerTrackingIdMap[n] == trackingId) {
296 id = n;
297 }
298 }
299
300 if (id < 0 && !mPointerIdBits.isFull()) {
301 id = mPointerIdBits.markFirstUnmarkedBit();
302 mPointerTrackingIdMap[id] = trackingId;
303 }
304 }
305 if (id < 0) {
306 mHavePointerIds = false;
307 outState->rawPointerData.clearIdBits();
308 newPointerIdBits.clear();
309 } else {
310 outPointer.id = id;
311 outState->rawPointerData.idToIndex[id] = outCount;
312 outState->rawPointerData.markIdBit(id, isHovering);
313 newPointerIdBits.markBit(id);
314 }
315 }
316 outCount += 1;
317 }
318
319 outState->deviceTimestamp = mMultiTouchMotionAccumulator.getDeviceTimestamp();
320 outState->rawPointerData.pointerCount = outCount;
321 mPointerIdBits = newPointerIdBits;
322
323 mMultiTouchMotionAccumulator.finishSync();
324 }
325
configureRawPointerAxes()326 void 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 }
350 mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/);
351 } else {
352 mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS,
353 false /*usingSlotsProtocol*/);
354 }
355 }
356
hasStylus() const357 bool MultiTouchInputMapper::hasStylus() const {
358 return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus();
359 }
360
361 } // namespace android
362