• 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 #include <android/sysprop/InputProperties.sysprop.h>
22 
23 namespace android {
24 
25 // --- Constants ---
26 
27 // Maximum number of slots supported when using the slot-based Multitouch Protocol B.
28 static constexpr size_t MAX_SLOTS = 32;
29 
30 // --- MultiTouchMotionAccumulator ---
31 
MultiTouchMotionAccumulator()32 MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
33       : mCurrentSlot(-1),
34         mSlots(nullptr),
35         mSlotCount(0),
36         mUsingSlotsProtocol(false),
37         mHaveStylus(false) {}
38 
~MultiTouchMotionAccumulator()39 MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
40     delete[] mSlots;
41 }
42 
configure(InputDeviceContext & deviceContext,size_t slotCount,bool usingSlotsProtocol)43 void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
44                                             bool usingSlotsProtocol) {
45     mSlotCount = slotCount;
46     mUsingSlotsProtocol = usingSlotsProtocol;
47     mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
48 
49     delete[] mSlots;
50     mSlots = new Slot[slotCount];
51 
52     mCurrentSlot = -1;
53     if (mUsingSlotsProtocol) {
54         // Query the driver for the current slot index and use it as the initial slot
55         // before we start reading events from the device.  It is possible that the
56         // current slot index will not be the same as it was when the first event was
57         // written into the evdev buffer, which means the input mapper could start
58         // out of sync with the initial state of the events in the evdev buffer.
59         // In the extremely unlikely case that this happens, the data from
60         // two slots will be confused until the next ABS_MT_SLOT event is received.
61         // This can cause the touch point to "jump", but at least there will be
62         // no stuck touches.
63         int32_t initialSlot;
64         if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
65             status == OK) {
66             mCurrentSlot = initialSlot;
67         } else {
68             ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
69         }
70     }
71 }
72 
resetSlots()73 void MultiTouchMotionAccumulator::resetSlots() {
74     if (mSlots) {
75         for (size_t i = 0; i < mSlotCount; i++) {
76             mSlots[i].clear();
77         }
78     }
79     mCurrentSlot = -1;
80 }
81 
process(const RawEvent * rawEvent)82 void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
83     if (rawEvent->type == EV_ABS) {
84         bool newSlot = false;
85         if (mUsingSlotsProtocol) {
86             if (rawEvent->code == ABS_MT_SLOT) {
87                 mCurrentSlot = rawEvent->value;
88                 newSlot = true;
89             }
90         } else if (mCurrentSlot < 0) {
91             mCurrentSlot = 0;
92         }
93 
94         if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
95             if (DEBUG_POINTERS) {
96                 if (newSlot) {
97                     ALOGW("MultiTouch device emitted invalid slot index %d but it "
98                           "should be between 0 and %zd; ignoring this slot.",
99                           mCurrentSlot, mSlotCount - 1);
100                 }
101             }
102         } else {
103             Slot* slot = &mSlots[mCurrentSlot];
104             // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
105             // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
106             // updating the slot.
107             if (!mUsingSlotsProtocol) {
108                 slot->mInUse = true;
109             }
110 
111             switch (rawEvent->code) {
112                 case ABS_MT_POSITION_X:
113                     slot->mAbsMTPositionX = rawEvent->value;
114                     warnIfNotInUse(*rawEvent, *slot);
115                     break;
116                 case ABS_MT_POSITION_Y:
117                     slot->mAbsMTPositionY = rawEvent->value;
118                     warnIfNotInUse(*rawEvent, *slot);
119                     break;
120                 case ABS_MT_TOUCH_MAJOR:
121                     slot->mAbsMTTouchMajor = rawEvent->value;
122                     break;
123                 case ABS_MT_TOUCH_MINOR:
124                     slot->mAbsMTTouchMinor = rawEvent->value;
125                     slot->mHaveAbsMTTouchMinor = true;
126                     break;
127                 case ABS_MT_WIDTH_MAJOR:
128                     slot->mAbsMTWidthMajor = rawEvent->value;
129                     break;
130                 case ABS_MT_WIDTH_MINOR:
131                     slot->mAbsMTWidthMinor = rawEvent->value;
132                     slot->mHaveAbsMTWidthMinor = true;
133                     break;
134                 case ABS_MT_ORIENTATION:
135                     slot->mAbsMTOrientation = rawEvent->value;
136                     break;
137                 case ABS_MT_TRACKING_ID:
138                     if (mUsingSlotsProtocol && rawEvent->value < 0) {
139                         // The slot is no longer in use but it retains its previous contents,
140                         // which may be reused for subsequent touches.
141                         slot->mInUse = false;
142                     } else {
143                         slot->mInUse = true;
144                         slot->mAbsMTTrackingId = rawEvent->value;
145                     }
146                     break;
147                 case ABS_MT_PRESSURE:
148                     slot->mAbsMTPressure = rawEvent->value;
149                     break;
150                 case ABS_MT_DISTANCE:
151                     slot->mAbsMTDistance = rawEvent->value;
152                     break;
153                 case ABS_MT_TOOL_TYPE:
154                     slot->mAbsMTToolType = rawEvent->value;
155                     slot->mHaveAbsMTToolType = true;
156                     break;
157             }
158         }
159     } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
160         // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
161         mCurrentSlot += 1;
162     }
163 }
164 
finishSync()165 void MultiTouchMotionAccumulator::finishSync() {
166     if (!mUsingSlotsProtocol) {
167         resetSlots();
168     }
169 }
170 
hasStylus() const171 bool MultiTouchMotionAccumulator::hasStylus() const {
172     return mHaveStylus;
173 }
174 
warnIfNotInUse(const RawEvent & event,const Slot & slot)175 void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
176     if (!slot.mInUse) {
177         ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
178               event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId);
179     }
180 }
181 
182 // --- MultiTouchMotionAccumulator::Slot ---
183 
Slot()184 MultiTouchMotionAccumulator::Slot::Slot() {
185     clear();
186 }
187 
clear()188 void MultiTouchMotionAccumulator::Slot::clear() {
189     mInUse = false;
190     mHaveAbsMTTouchMinor = false;
191     mHaveAbsMTWidthMinor = false;
192     mHaveAbsMTToolType = false;
193     mAbsMTPositionX = 0;
194     mAbsMTPositionY = 0;
195     mAbsMTTouchMajor = 0;
196     mAbsMTTouchMinor = 0;
197     mAbsMTWidthMajor = 0;
198     mAbsMTWidthMinor = 0;
199     mAbsMTOrientation = 0;
200     mAbsMTTrackingId = -1;
201     mAbsMTPressure = 0;
202     mAbsMTDistance = 0;
203     mAbsMTToolType = 0;
204 }
205 
getToolType() const206 int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
207     if (mHaveAbsMTToolType) {
208         switch (mAbsMTToolType) {
209             case MT_TOOL_FINGER:
210                 return AMOTION_EVENT_TOOL_TYPE_FINGER;
211             case MT_TOOL_PEN:
212                 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
213             case MT_TOOL_PALM:
214                 return AMOTION_EVENT_TOOL_TYPE_PALM;
215         }
216     }
217     return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
218 }
219 
220 // --- MultiTouchInputMapper ---
221 
MultiTouchInputMapper(InputDeviceContext & deviceContext)222 MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
223       : TouchInputMapper(deviceContext) {}
224 
~MultiTouchInputMapper()225 MultiTouchInputMapper::~MultiTouchInputMapper() {}
226 
reset(nsecs_t when)227 void MultiTouchInputMapper::reset(nsecs_t when) {
228     // The evdev multi-touch protocol does not allow userspace applications to query the initial or
229     // current state of the pointers at any time. This means if we clear our accumulated state when
230     // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
231     // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
232     // rebuilding the state from scratch, we work around this kernel API limitation by never
233     // fully clearing any state specific to the multi-touch protocol.
234     TouchInputMapper::reset(when);
235 }
236 
process(const RawEvent * rawEvent)237 void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
238     TouchInputMapper::process(rawEvent);
239 
240     mMultiTouchMotionAccumulator.process(rawEvent);
241 }
242 
getActiveBitId(const MultiTouchMotionAccumulator::Slot & inSlot)243 std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
244         const MultiTouchMotionAccumulator::Slot& inSlot) {
245     if (mHavePointerIds) {
246         int32_t trackingId = inSlot.getTrackingId();
247         for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
248             int32_t n = idBits.clearFirstMarkedBit();
249             if (mPointerTrackingIdMap[n] == trackingId) {
250                 return std::make_optional(n);
251             }
252         }
253     }
254     return std::nullopt;
255 }
256 
syncTouch(nsecs_t when,RawState * outState)257 void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
258     size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
259     size_t outCount = 0;
260     BitSet32 newPointerIdBits;
261     mHavePointerIds = true;
262 
263     for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
264         const MultiTouchMotionAccumulator::Slot* inSlot =
265                 mMultiTouchMotionAccumulator.getSlot(inIndex);
266         if (!inSlot->isInUse()) {
267             continue;
268         }
269 
270         if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
271             std::optional<int32_t> id = getActiveBitId(*inSlot);
272             if (id) {
273                 outState->rawPointerData.canceledIdBits.markBit(id.value());
274             }
275             if (DEBUG_POINTERS) {
276                 ALOGI("Stop processing slot %zu for it received a palm event from device %s",
277                       inIndex, getDeviceName().c_str());
278             }
279             continue;
280         }
281 
282         if (outCount >= MAX_POINTERS) {
283             if (DEBUG_POINTERS) {
284                 ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
285                       "ignoring the rest.",
286                       getDeviceName().c_str(), MAX_POINTERS);
287             }
288             break; // too many fingers!
289         }
290 
291         RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
292         outPointer.x = inSlot->getX();
293         outPointer.y = inSlot->getY();
294         outPointer.pressure = inSlot->getPressure();
295         outPointer.touchMajor = inSlot->getTouchMajor();
296         outPointer.touchMinor = inSlot->getTouchMinor();
297         outPointer.toolMajor = inSlot->getToolMajor();
298         outPointer.toolMinor = inSlot->getToolMinor();
299         outPointer.orientation = inSlot->getOrientation();
300         outPointer.distance = inSlot->getDistance();
301         outPointer.tiltX = 0;
302         outPointer.tiltY = 0;
303 
304         outPointer.toolType = inSlot->getToolType();
305         if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
306             outPointer.toolType = mTouchButtonAccumulator.getToolType();
307             if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
308                 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
309             }
310         }
311         if (shouldSimulateStylusWithTouch() &&
312             outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
313             outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
314         }
315 
316         bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
317                 (mTouchButtonAccumulator.isHovering() ||
318                  (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
319         outPointer.isHovering = isHovering;
320 
321         // Assign pointer id using tracking id if available.
322         if (mHavePointerIds) {
323             int32_t trackingId = inSlot->getTrackingId();
324             int32_t id = -1;
325             if (trackingId >= 0) {
326                 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
327                     uint32_t n = idBits.clearFirstMarkedBit();
328                     if (mPointerTrackingIdMap[n] == trackingId) {
329                         id = n;
330                     }
331                 }
332 
333                 if (id < 0 && !mPointerIdBits.isFull()) {
334                     id = mPointerIdBits.markFirstUnmarkedBit();
335                     mPointerTrackingIdMap[id] = trackingId;
336                 }
337             }
338             if (id < 0) {
339                 mHavePointerIds = false;
340                 outState->rawPointerData.clearIdBits();
341                 newPointerIdBits.clear();
342             } else {
343                 outPointer.id = id;
344                 outState->rawPointerData.idToIndex[id] = outCount;
345                 outState->rawPointerData.markIdBit(id, isHovering);
346                 newPointerIdBits.markBit(id);
347             }
348         }
349         outCount += 1;
350     }
351 
352     outState->rawPointerData.pointerCount = outCount;
353     mPointerIdBits = newPointerIdBits;
354 
355     mMultiTouchMotionAccumulator.finishSync();
356 }
357 
configureRawPointerAxes()358 void MultiTouchInputMapper::configureRawPointerAxes() {
359     TouchInputMapper::configureRawPointerAxes();
360 
361     getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
362     getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
363     getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
364     getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
365     getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
366     getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
367     getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
368     getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
369     getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
370     getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
371     getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
372 
373     if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
374         mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
375         size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
376         if (slotCount > MAX_SLOTS) {
377             ALOGW("MultiTouch Device %s reported %zu slots but the framework "
378                   "only supports a maximum of %zu slots at this time.",
379                   getDeviceName().c_str(), slotCount, MAX_SLOTS);
380             slotCount = MAX_SLOTS;
381         }
382         mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
383                                                true /*usingSlotsProtocol*/);
384     } else {
385         mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
386                                                false /*usingSlotsProtocol*/);
387     }
388 }
389 
hasStylus() const390 bool MultiTouchInputMapper::hasStylus() const {
391     return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus() ||
392             shouldSimulateStylusWithTouch();
393 }
394 
shouldSimulateStylusWithTouch() const395 bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
396     static const bool SIMULATE_STYLUS_WITH_TOUCH =
397             sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
398     return SIMULATE_STYLUS_WITH_TOUCH &&
399             mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
400 }
401 
402 } // namespace android
403