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