• 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 <android/sysprop/InputProperties.sysprop.h>
20 #include "MultiTouchInputMapper.h"
21 
22 namespace android {
23 
24 // --- Constants ---
25 
26 // Maximum number of slots supported when using the slot-based Multitouch Protocol B.
27 static constexpr size_t MAX_SLOTS = 32;
28 
29 // --- MultiTouchInputMapper ---
30 
MultiTouchInputMapper(InputDeviceContext & deviceContext,const InputReaderConfiguration & readerConfig)31 MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext,
32                                              const InputReaderConfiguration& readerConfig)
33       : TouchInputMapper(deviceContext, readerConfig) {}
34 
~MultiTouchInputMapper()35 MultiTouchInputMapper::~MultiTouchInputMapper() {}
36 
reset(nsecs_t when)37 std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
38     // The evdev multi-touch protocol does not allow userspace applications to query the initial or
39     // current state of the pointers at any time. This means if we clear our accumulated state when
40     // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
41     // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
42     // rebuilding the state from scratch, we work around this kernel API limitation by never
43     // fully clearing any state specific to the multi-touch protocol.
44     return TouchInputMapper::reset(when);
45 }
46 
process(const RawEvent * rawEvent)47 std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {
48     std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
49 
50     mMultiTouchMotionAccumulator.process(rawEvent);
51     return out;
52 }
53 
getActiveBitId(const MultiTouchMotionAccumulator::Slot & inSlot)54 std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
55         const MultiTouchMotionAccumulator::Slot& inSlot) {
56     if (mHavePointerIds) {
57         int32_t trackingId = inSlot.getTrackingId();
58         for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
59             int32_t n = idBits.clearFirstMarkedBit();
60             if (mPointerTrackingIdMap[n] == trackingId) {
61                 return std::make_optional(n);
62             }
63         }
64     }
65     return std::nullopt;
66 }
67 
syncTouch(nsecs_t when,RawState * outState)68 void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
69     size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
70     size_t outCount = 0;
71     BitSet32 newPointerIdBits;
72     mHavePointerIds = true;
73 
74     for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
75         const MultiTouchMotionAccumulator::Slot& inSlot =
76                 mMultiTouchMotionAccumulator.getSlot(inIndex);
77         if (!inSlot.isInUse()) {
78             continue;
79         }
80 
81         if (inSlot.getToolType() == ToolType::PALM) {
82             std::optional<int32_t> id = getActiveBitId(inSlot);
83             if (id) {
84                 outState->rawPointerData.canceledIdBits.markBit(id.value());
85             }
86             if (DEBUG_POINTERS) {
87                 ALOGI("Stop processing slot %zu for it received a palm event from device %s",
88                       inIndex, getDeviceName().c_str());
89             }
90             continue;
91         }
92 
93         if (outCount >= MAX_POINTERS) {
94             if (DEBUG_POINTERS) {
95                 ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
96                       "ignoring the rest.",
97                       getDeviceName().c_str(), MAX_POINTERS);
98             }
99             break; // too many fingers!
100         }
101 
102         RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
103         outPointer.x = inSlot.getX();
104         outPointer.y = inSlot.getY();
105         outPointer.pressure = inSlot.getPressure();
106         outPointer.touchMajor = inSlot.getTouchMajor();
107         outPointer.touchMinor = inSlot.getTouchMinor();
108         outPointer.toolMajor = inSlot.getToolMajor();
109         outPointer.toolMinor = inSlot.getToolMinor();
110         outPointer.orientation = inSlot.getOrientation();
111         outPointer.distance = inSlot.getDistance();
112         outPointer.tiltX = 0;
113         outPointer.tiltY = 0;
114 
115         outPointer.toolType = inSlot.getToolType();
116         if (outPointer.toolType == ToolType::UNKNOWN) {
117             outPointer.toolType = mTouchButtonAccumulator.getToolType();
118             if (outPointer.toolType == ToolType::UNKNOWN) {
119                 outPointer.toolType = ToolType::FINGER;
120             }
121         } else if (outPointer.toolType == ToolType::STYLUS && !mStylusMtToolSeen) {
122             mStylusMtToolSeen = true;
123             // The multi-touch device produced a stylus event with MT_TOOL_PEN. Dynamically
124             // re-configure this input device so that we add SOURCE_STYLUS if we haven't already.
125             // This is to cover the case where we cannot reliably detect whether a multi-touch
126             // device will ever produce stylus events when it is initially being configured.
127             if (!isFromSource(mSource, AINPUT_SOURCE_STYLUS)) {
128                 // Add the stylus source immediately so that it is included in any events generated
129                 // before we have a chance to re-configure the device.
130                 mSource |= AINPUT_SOURCE_STYLUS;
131                 bumpGeneration();
132             }
133         }
134         if (shouldSimulateStylusWithTouch() && outPointer.toolType == ToolType::FINGER) {
135             outPointer.toolType = ToolType::STYLUS;
136         }
137 
138         bool isHovering = mTouchButtonAccumulator.getToolType() != ToolType::MOUSE &&
139                 (mTouchButtonAccumulator.isHovering() ||
140                  (mRawPointerAxes.pressure.valid && inSlot.getPressure() <= 0));
141         outPointer.isHovering = isHovering;
142 
143         // Assign pointer id using tracking id if available.
144         if (mHavePointerIds) {
145             int32_t trackingId = inSlot.getTrackingId();
146             int32_t id = -1;
147             if (trackingId >= 0) {
148                 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
149                     uint32_t n = idBits.clearFirstMarkedBit();
150                     if (mPointerTrackingIdMap[n] == trackingId) {
151                         id = n;
152                     }
153                 }
154 
155                 if (id < 0 && !mPointerIdBits.isFull()) {
156                     id = mPointerIdBits.markFirstUnmarkedBit();
157                     mPointerTrackingIdMap[id] = trackingId;
158                 }
159             }
160             if (id < 0) {
161                 mHavePointerIds = false;
162                 outState->rawPointerData.clearIdBits();
163                 newPointerIdBits.clear();
164             } else {
165                 outPointer.id = id;
166                 outState->rawPointerData.idToIndex[id] = outCount;
167                 outState->rawPointerData.markIdBit(id, isHovering);
168                 newPointerIdBits.markBit(id);
169             }
170         }
171         outCount += 1;
172     }
173 
174     outState->rawPointerData.pointerCount = outCount;
175     mPointerIdBits = newPointerIdBits;
176 
177     mMultiTouchMotionAccumulator.finishSync();
178 }
179 
configureRawPointerAxes()180 void MultiTouchInputMapper::configureRawPointerAxes() {
181     TouchInputMapper::configureRawPointerAxes();
182 
183     getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
184     getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
185     getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
186     getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
187     getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
188     getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
189     getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
190     getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
191     getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
192     getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
193     getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
194 
195     if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
196         mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
197         size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
198         if (slotCount > MAX_SLOTS) {
199             ALOGW("MultiTouch Device %s reported %zu slots but the framework "
200                   "only supports a maximum of %zu slots at this time.",
201                   getDeviceName().c_str(), slotCount, MAX_SLOTS);
202             slotCount = MAX_SLOTS;
203         }
204         mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
205                                                /*usingSlotsProtocol=*/true);
206     } else {
207         mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
208                                                /*usingSlotsProtocol=*/false);
209     }
210 }
211 
hasStylus() const212 bool MultiTouchInputMapper::hasStylus() const {
213     return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() ||
214             shouldSimulateStylusWithTouch();
215 }
216 
shouldSimulateStylusWithTouch() const217 bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
218     static const bool SIMULATE_STYLUS_WITH_TOUCH =
219             sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
220     return SIMULATE_STYLUS_WITH_TOUCH &&
221             mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
222 }
223 
224 } // namespace android
225