blob: c567c8bf80c7e4231c57b5909c7d33f6901f53a2 [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
17#include "Macros.h"
18
19#include "MultiTouchInputMapper.h"
20
21namespace android {
22
23// --- Constants ---
24
25// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
26static constexpr size_t MAX_SLOTS = 32;
27
28// --- MultiTouchMotionAccumulator ---
29
30MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
31 : mCurrentSlot(-1),
32 mSlots(nullptr),
33 mSlotCount(0),
34 mUsingSlotsProtocol(false),
35 mHaveStylus(false) {}
36
37MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
38 delete[] mSlots;
39}
40
41void 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
51void 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}
76
77void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
78 if (mSlots) {
79 for (size_t i = 0; i < mSlotCount; i++) {
80 mSlots[i].clear();
81 }
82 }
83 mCurrentSlot = initialSlot;
84}
85
86void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
87 if (rawEvent->type == EV_ABS) {
88 bool newSlot = false;
89 if (mUsingSlotsProtocol) {
90 if (rawEvent->code == ABS_MT_SLOT) {
91 mCurrentSlot = rawEvent->value;
92 newSlot = true;
93 }
94 } else if (mCurrentSlot < 0) {
95 mCurrentSlot = 0;
96 }
97
98 if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
99#if DEBUG_POINTERS
100 if (newSlot) {
101 ALOGW("MultiTouch device emitted invalid slot index %d but it "
102 "should be between 0 and %zd; ignoring this slot.",
103 mCurrentSlot, mSlotCount - 1);
104 }
105#endif
106 } else {
107 Slot* slot = &mSlots[mCurrentSlot];
108
109 switch (rawEvent->code) {
110 case ABS_MT_POSITION_X:
111 slot->mInUse = true;
112 slot->mAbsMTPositionX = rawEvent->value;
113 break;
114 case ABS_MT_POSITION_Y:
115 slot->mInUse = true;
116 slot->mAbsMTPositionY = rawEvent->value;
117 break;
118 case ABS_MT_TOUCH_MAJOR:
119 slot->mInUse = true;
120 slot->mAbsMTTouchMajor = rawEvent->value;
121 break;
122 case ABS_MT_TOUCH_MINOR:
123 slot->mInUse = true;
124 slot->mAbsMTTouchMinor = rawEvent->value;
125 slot->mHaveAbsMTTouchMinor = true;
126 break;
127 case ABS_MT_WIDTH_MAJOR:
128 slot->mInUse = true;
129 slot->mAbsMTWidthMajor = rawEvent->value;
130 break;
131 case ABS_MT_WIDTH_MINOR:
132 slot->mInUse = true;
133 slot->mAbsMTWidthMinor = rawEvent->value;
134 slot->mHaveAbsMTWidthMinor = true;
135 break;
136 case ABS_MT_ORIENTATION:
137 slot->mInUse = true;
138 slot->mAbsMTOrientation = rawEvent->value;
139 break;
140 case ABS_MT_TRACKING_ID:
141 if (mUsingSlotsProtocol && rawEvent->value < 0) {
142 // The slot is no longer in use but it retains its previous contents,
143 // which may be reused for subsequent touches.
144 slot->mInUse = false;
145 } else {
146 slot->mInUse = true;
147 slot->mAbsMTTrackingId = rawEvent->value;
148 }
149 break;
150 case ABS_MT_PRESSURE:
151 slot->mInUse = true;
152 slot->mAbsMTPressure = rawEvent->value;
153 break;
154 case ABS_MT_DISTANCE:
155 slot->mInUse = true;
156 slot->mAbsMTDistance = rawEvent->value;
157 break;
158 case ABS_MT_TOOL_TYPE:
159 slot->mInUse = true;
160 slot->mAbsMTToolType = rawEvent->value;
161 slot->mHaveAbsMTToolType = true;
162 break;
163 }
164 }
165 } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
166 // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
167 mCurrentSlot += 1;
168 }
169}
170
171void MultiTouchMotionAccumulator::finishSync() {
172 if (!mUsingSlotsProtocol) {
173 clearSlots(-1);
174 }
175}
176
177bool MultiTouchMotionAccumulator::hasStylus() const {
178 return mHaveStylus;
179}
180
181// --- MultiTouchMotionAccumulator::Slot ---
182
183MultiTouchMotionAccumulator::Slot::Slot() {
184 clear();
185}
186
187void MultiTouchMotionAccumulator::Slot::clear() {
188 mInUse = false;
189 mHaveAbsMTTouchMinor = false;
190 mHaveAbsMTWidthMinor = false;
191 mHaveAbsMTToolType = false;
192 mAbsMTPositionX = 0;
193 mAbsMTPositionY = 0;
194 mAbsMTTouchMajor = 0;
195 mAbsMTTouchMinor = 0;
196 mAbsMTWidthMajor = 0;
197 mAbsMTWidthMinor = 0;
198 mAbsMTOrientation = 0;
199 mAbsMTTrackingId = -1;
200 mAbsMTPressure = 0;
201 mAbsMTDistance = 0;
202 mAbsMTToolType = 0;
203}
204
205int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
206 if (mHaveAbsMTToolType) {
207 switch (mAbsMTToolType) {
208 case MT_TOOL_FINGER:
209 return AMOTION_EVENT_TOOL_TYPE_FINGER;
210 case MT_TOOL_PEN:
211 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
212 }
213 }
214 return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
215}
216
217// --- MultiTouchInputMapper ---
218
219MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {}
220
221MultiTouchInputMapper::~MultiTouchInputMapper() {}
222
223void MultiTouchInputMapper::reset(nsecs_t when) {
224 mMultiTouchMotionAccumulator.reset(getDevice());
225
226 mPointerIdBits.clear();
227
228 TouchInputMapper::reset(when);
229}
230
231void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
232 TouchInputMapper::process(rawEvent);
233
234 mMultiTouchMotionAccumulator.process(rawEvent);
235}
236
237void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
238 size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
239 size_t outCount = 0;
240 BitSet32 newPointerIdBits;
241 mHavePointerIds = true;
242
243 for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
244 const MultiTouchMotionAccumulator::Slot* inSlot =
245 mMultiTouchMotionAccumulator.getSlot(inIndex);
246 if (!inSlot->isInUse()) {
247 continue;
248 }
249
250 if (outCount >= MAX_POINTERS) {
251#if DEBUG_POINTERS
252 ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
253 "ignoring the rest.",
254 getDeviceName().c_str(), MAX_POINTERS);
255#endif
256 break; // too many fingers!
257 }
258
259 RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
260 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();
269 outPointer.tiltX = 0;
270 outPointer.tiltY = 0;
271
272 outPointer.toolType = inSlot->getToolType();
273 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 }
279
280 bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
281 (mTouchButtonAccumulator.isHovering() ||
282 (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
283 outPointer.isHovering = isHovering;
284
285 // Assign pointer id using tracking id if available.
286 if (mHavePointerIds) {
287 int32_t trackingId = inSlot->getTrackingId();
288 int32_t id = -1;
289 if (trackingId >= 0) {
290 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
291 uint32_t n = idBits.clearFirstMarkedBit();
292 if (mPointerTrackingIdMap[n] == trackingId) {
293 id = n;
294 }
295 }
296
297 if (id < 0 && !mPointerIdBits.isFull()) {
298 id = mPointerIdBits.markFirstUnmarkedBit();
299 mPointerTrackingIdMap[id] = trackingId;
300 }
301 }
302 if (id < 0) {
303 mHavePointerIds = false;
304 outState->rawPointerData.clearIdBits();
305 newPointerIdBits.clear();
306 } else {
307 outPointer.id = id;
308 outState->rawPointerData.idToIndex[id] = outCount;
309 outState->rawPointerData.markIdBit(id, isHovering);
310 newPointerIdBits.markBit(id);
311 }
312 }
313 outCount += 1;
314 }
315
316 outState->rawPointerData.pointerCount = outCount;
317 mPointerIdBits = newPointerIdBits;
318
319 mMultiTouchMotionAccumulator.finishSync();
320}
321
322void MultiTouchInputMapper::configureRawPointerAxes() {
323 TouchInputMapper::configureRawPointerAxes();
324
325 getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
326 getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
327 getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
328 getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
329 getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
330 getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
331 getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
332 getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
333 getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
334 getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
335 getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
336
337 if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
338 mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
339 size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
340 if (slotCount > MAX_SLOTS) {
341 ALOGW("MultiTouch Device %s reported %zu slots but the framework "
342 "only supports a maximum of %zu slots at this time.",
343 getDeviceName().c_str(), slotCount, MAX_SLOTS);
344 slotCount = MAX_SLOTS;
345 }
346 mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/);
347 } else {
348 mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS,
349 false /*usingSlotsProtocol*/);
350 }
351}
352
353bool MultiTouchInputMapper::hasStylus() const {
354 return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus();
355}
356
357} // namespace android