• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(InputDeviceContext & deviceContext,size_t slotCount,bool usingSlotsProtocol)41 void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
42                                             bool usingSlotsProtocol) {
43     mSlotCount = slotCount;
44     mUsingSlotsProtocol = usingSlotsProtocol;
45     mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
46 
47     delete[] mSlots;
48     mSlots = new Slot[slotCount];
49 }
50 
reset(InputDeviceContext & deviceContext)51 void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
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 = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
66         if (status) {
67             ALOGD("Could not retrieve current multitouch slot index.  status=%d", status);
68             initialSlot = -1;
69         }
70         clearSlots(initialSlot);
71     } else {
72         clearSlots(-1);
73     }
74 }
75 
clearSlots(int32_t initialSlot)76 void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
77     if (mSlots) {
78         for (size_t i = 0; i < mSlotCount; i++) {
79             mSlots[i].clear();
80         }
81     }
82     mCurrentSlot = initialSlot;
83 }
84 
process(const RawEvent * rawEvent)85 void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
86     if (rawEvent->type == EV_ABS) {
87         bool newSlot = false;
88         if (mUsingSlotsProtocol) {
89             if (rawEvent->code == ABS_MT_SLOT) {
90                 mCurrentSlot = rawEvent->value;
91                 newSlot = true;
92             }
93         } else if (mCurrentSlot < 0) {
94             mCurrentSlot = 0;
95         }
96 
97         if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
98 #if DEBUG_POINTERS
99             if (newSlot) {
100                 ALOGW("MultiTouch device emitted invalid slot index %d but it "
101                       "should be between 0 and %zd; ignoring this slot.",
102                       mCurrentSlot, mSlotCount - 1);
103             }
104 #endif
105         } else {
106             Slot* slot = &mSlots[mCurrentSlot];
107             // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
108             // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
109             // updating the slot.
110             if (!mUsingSlotsProtocol) {
111                 slot->mInUse = true;
112             }
113 
114             switch (rawEvent->code) {
115                 case ABS_MT_POSITION_X:
116                     slot->mAbsMTPositionX = rawEvent->value;
117                     warnIfNotInUse(*rawEvent, *slot);
118                     break;
119                 case ABS_MT_POSITION_Y:
120                     slot->mAbsMTPositionY = rawEvent->value;
121                     warnIfNotInUse(*rawEvent, *slot);
122                     break;
123                 case ABS_MT_TOUCH_MAJOR:
124                     slot->mAbsMTTouchMajor = rawEvent->value;
125                     break;
126                 case ABS_MT_TOUCH_MINOR:
127                     slot->mAbsMTTouchMinor = rawEvent->value;
128                     slot->mHaveAbsMTTouchMinor = true;
129                     break;
130                 case ABS_MT_WIDTH_MAJOR:
131                     slot->mAbsMTWidthMajor = rawEvent->value;
132                     break;
133                 case ABS_MT_WIDTH_MINOR:
134                     slot->mAbsMTWidthMinor = rawEvent->value;
135                     slot->mHaveAbsMTWidthMinor = true;
136                     break;
137                 case ABS_MT_ORIENTATION:
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->mAbsMTPressure = rawEvent->value;
152                     break;
153                 case ABS_MT_DISTANCE:
154                     slot->mAbsMTDistance = rawEvent->value;
155                     break;
156                 case ABS_MT_TOOL_TYPE:
157                     slot->mAbsMTToolType = rawEvent->value;
158                     slot->mHaveAbsMTToolType = true;
159                     break;
160             }
161         }
162     } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
163         // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
164         mCurrentSlot += 1;
165     }
166 }
167 
finishSync()168 void MultiTouchMotionAccumulator::finishSync() {
169     if (!mUsingSlotsProtocol) {
170         clearSlots(-1);
171     }
172 }
173 
hasStylus() const174 bool MultiTouchMotionAccumulator::hasStylus() const {
175     return mHaveStylus;
176 }
177 
warnIfNotInUse(const RawEvent & event,const Slot & slot)178 void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
179     if (!slot.mInUse) {
180         ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
181               event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId);
182     }
183 }
184 
185 // --- MultiTouchMotionAccumulator::Slot ---
186 
Slot()187 MultiTouchMotionAccumulator::Slot::Slot() {
188     clear();
189 }
190 
clear()191 void MultiTouchMotionAccumulator::Slot::clear() {
192     mInUse = false;
193     mHaveAbsMTTouchMinor = false;
194     mHaveAbsMTWidthMinor = false;
195     mHaveAbsMTToolType = false;
196     mAbsMTPositionX = 0;
197     mAbsMTPositionY = 0;
198     mAbsMTTouchMajor = 0;
199     mAbsMTTouchMinor = 0;
200     mAbsMTWidthMajor = 0;
201     mAbsMTWidthMinor = 0;
202     mAbsMTOrientation = 0;
203     mAbsMTTrackingId = -1;
204     mAbsMTPressure = 0;
205     mAbsMTDistance = 0;
206     mAbsMTToolType = 0;
207 }
208 
getToolType() const209 int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
210     if (mHaveAbsMTToolType) {
211         switch (mAbsMTToolType) {
212             case MT_TOOL_FINGER:
213                 return AMOTION_EVENT_TOOL_TYPE_FINGER;
214             case MT_TOOL_PEN:
215                 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
216             case MT_TOOL_PALM:
217                 return AMOTION_EVENT_TOOL_TYPE_PALM;
218         }
219     }
220     return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
221 }
222 
223 // --- MultiTouchInputMapper ---
224 
MultiTouchInputMapper(InputDeviceContext & deviceContext)225 MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
226       : TouchInputMapper(deviceContext) {}
227 
~MultiTouchInputMapper()228 MultiTouchInputMapper::~MultiTouchInputMapper() {}
229 
reset(nsecs_t when)230 void MultiTouchInputMapper::reset(nsecs_t when) {
231     mMultiTouchMotionAccumulator.reset(getDeviceContext());
232 
233     mPointerIdBits.clear();
234 
235     TouchInputMapper::reset(when);
236 }
237 
process(const RawEvent * rawEvent)238 void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
239     TouchInputMapper::process(rawEvent);
240 
241     mMultiTouchMotionAccumulator.process(rawEvent);
242 }
243 
getActiveBitId(const MultiTouchMotionAccumulator::Slot & inSlot)244 std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
245         const MultiTouchMotionAccumulator::Slot& inSlot) {
246     if (mHavePointerIds) {
247         int32_t trackingId = inSlot.getTrackingId();
248         for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
249             int32_t n = idBits.clearFirstMarkedBit();
250             if (mPointerTrackingIdMap[n] == trackingId) {
251                 return std::make_optional(n);
252             }
253         }
254     }
255     return std::nullopt;
256 }
257 
syncTouch(nsecs_t when,RawState * outState)258 void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
259     size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
260     size_t outCount = 0;
261     BitSet32 newPointerIdBits;
262     mHavePointerIds = true;
263 
264     for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
265         const MultiTouchMotionAccumulator::Slot* inSlot =
266                 mMultiTouchMotionAccumulator.getSlot(inIndex);
267         if (!inSlot->isInUse()) {
268             continue;
269         }
270 
271         if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
272             std::optional<int32_t> id = getActiveBitId(*inSlot);
273             if (id) {
274                 outState->rawPointerData.canceledIdBits.markBit(id.value());
275             }
276 #if DEBUG_POINTERS
277             ALOGI("Stop processing slot %zu for it received a palm event from device %s", inIndex,
278                   getDeviceName().c_str());
279 #endif
280             continue;
281         }
282 
283         if (outCount >= MAX_POINTERS) {
284 #if DEBUG_POINTERS
285             ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
286                   "ignoring the rest.",
287                   getDeviceName().c_str(), MAX_POINTERS);
288 #endif
289             break; // too many fingers!
290         }
291 
292         RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
293         outPointer.x = inSlot->getX();
294         outPointer.y = inSlot->getY();
295         outPointer.pressure = inSlot->getPressure();
296         outPointer.touchMajor = inSlot->getTouchMajor();
297         outPointer.touchMinor = inSlot->getTouchMinor();
298         outPointer.toolMajor = inSlot->getToolMajor();
299         outPointer.toolMinor = inSlot->getToolMinor();
300         outPointer.orientation = inSlot->getOrientation();
301         outPointer.distance = inSlot->getDistance();
302         outPointer.tiltX = 0;
303         outPointer.tiltY = 0;
304 
305         outPointer.toolType = inSlot->getToolType();
306         if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
307             outPointer.toolType = mTouchButtonAccumulator.getToolType();
308             if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
309                 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
310             }
311         }
312 
313         bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
314                 (mTouchButtonAccumulator.isHovering() ||
315                  (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
316         outPointer.isHovering = isHovering;
317 
318         // Assign pointer id using tracking id if available.
319         if (mHavePointerIds) {
320             int32_t trackingId = inSlot->getTrackingId();
321             int32_t id = -1;
322             if (trackingId >= 0) {
323                 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
324                     uint32_t n = idBits.clearFirstMarkedBit();
325                     if (mPointerTrackingIdMap[n] == trackingId) {
326                         id = n;
327                     }
328                 }
329 
330                 if (id < 0 && !mPointerIdBits.isFull()) {
331                     id = mPointerIdBits.markFirstUnmarkedBit();
332                     mPointerTrackingIdMap[id] = trackingId;
333                 }
334             }
335             if (id < 0) {
336                 mHavePointerIds = false;
337                 outState->rawPointerData.clearIdBits();
338                 newPointerIdBits.clear();
339             } else {
340                 outPointer.id = id;
341                 outState->rawPointerData.idToIndex[id] = outCount;
342                 outState->rawPointerData.markIdBit(id, isHovering);
343                 newPointerIdBits.markBit(id);
344             }
345         }
346         outCount += 1;
347     }
348 
349     outState->rawPointerData.pointerCount = outCount;
350     mPointerIdBits = newPointerIdBits;
351 
352     mMultiTouchMotionAccumulator.finishSync();
353 }
354 
configureRawPointerAxes()355 void MultiTouchInputMapper::configureRawPointerAxes() {
356     TouchInputMapper::configureRawPointerAxes();
357 
358     getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
359     getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
360     getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
361     getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
362     getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
363     getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
364     getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
365     getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
366     getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
367     getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
368     getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
369 
370     if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
371         mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
372         size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
373         if (slotCount > MAX_SLOTS) {
374             ALOGW("MultiTouch Device %s reported %zu slots but the framework "
375                   "only supports a maximum of %zu slots at this time.",
376                   getDeviceName().c_str(), slotCount, MAX_SLOTS);
377             slotCount = MAX_SLOTS;
378         }
379         mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
380                                                true /*usingSlotsProtocol*/);
381     } else {
382         mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
383                                                false /*usingSlotsProtocol*/);
384     }
385 }
386 
hasStylus() const387 bool MultiTouchInputMapper::hasStylus() const {
388     return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus();
389 }
390 
391 } // namespace android
392