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 mPointerIdBits.clear();
39 mMultiTouchMotionAccumulator.reset(mDeviceContext);
40 return TouchInputMapper::reset(when);
41 }
42
process(const RawEvent & rawEvent)43 std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent& rawEvent) {
44 std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
45
46 mMultiTouchMotionAccumulator.process(rawEvent);
47 return out;
48 }
49
getActiveBitId(const MultiTouchMotionAccumulator::Slot & inSlot)50 std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
51 const MultiTouchMotionAccumulator::Slot& inSlot) {
52 if (mHavePointerIds) {
53 int32_t trackingId = inSlot.getTrackingId();
54 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
55 int32_t n = idBits.clearFirstMarkedBit();
56 if (mPointerTrackingIdMap[n] == trackingId) {
57 return std::make_optional(n);
58 }
59 }
60 }
61 return std::nullopt;
62 }
63
syncTouch(nsecs_t when,RawState * outState)64 void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
65 size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
66 size_t outCount = 0;
67 BitSet32 newPointerIdBits;
68 mHavePointerIds = true;
69
70 for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
71 const MultiTouchMotionAccumulator::Slot& inSlot =
72 mMultiTouchMotionAccumulator.getSlot(inIndex);
73 if (!inSlot.isInUse()) {
74 continue;
75 }
76
77 if (inSlot.getToolType() == ToolType::PALM) {
78 std::optional<int32_t> id = getActiveBitId(inSlot);
79 if (id) {
80 outState->rawPointerData.canceledIdBits.markBit(id.value());
81 }
82 ALOGI_IF(DEBUG_POINTERS,
83 "Stop processing slot %zu for it received a palm event from device %s",
84 inIndex, getDeviceName().c_str());
85 continue;
86 }
87
88 if (outCount >= MAX_POINTERS) {
89 ALOGD_IF(DEBUG_POINTERS,
90 "MultiTouch device %s emitted more than maximum of %zu pointers; ignoring the "
91 "rest.",
92 getDeviceName().c_str(), MAX_POINTERS);
93 break; // too many fingers!
94 }
95
96 RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
97 outPointer.x = inSlot.getX();
98 outPointer.y = inSlot.getY();
99 outPointer.pressure = inSlot.getPressure();
100 outPointer.touchMajor = inSlot.getTouchMajor();
101 outPointer.touchMinor = inSlot.getTouchMinor();
102 outPointer.toolMajor = inSlot.getToolMajor();
103 outPointer.toolMinor = inSlot.getToolMinor();
104 outPointer.orientation = inSlot.getOrientation();
105 outPointer.distance = inSlot.getDistance();
106 outPointer.tiltX = 0;
107 outPointer.tiltY = 0;
108
109 outPointer.toolType = inSlot.getToolType();
110 if (outPointer.toolType == ToolType::UNKNOWN) {
111 outPointer.toolType = mTouchButtonAccumulator.getToolType();
112 if (outPointer.toolType == ToolType::UNKNOWN) {
113 outPointer.toolType = ToolType::FINGER;
114 }
115 } else if (outPointer.toolType == ToolType::STYLUS && !mStylusMtToolSeen) {
116 mStylusMtToolSeen = true;
117 // The multi-touch device produced a stylus event with MT_TOOL_PEN. Dynamically
118 // re-configure this input device so that we add SOURCE_STYLUS if we haven't already.
119 // This is to cover the case where we cannot reliably detect whether a multi-touch
120 // device will ever produce stylus events when it is initially being configured.
121 if (!isFromSource(mSource, AINPUT_SOURCE_STYLUS)) {
122 // Add the stylus source immediately so that it is included in any events generated
123 // before we have a chance to re-configure the device.
124 mSource |= AINPUT_SOURCE_STYLUS;
125 bumpGeneration();
126 }
127 }
128 if (mShouldSimulateStylusWithTouch && outPointer.toolType == ToolType::FINGER) {
129 outPointer.toolType = ToolType::STYLUS;
130 }
131
132 bool isHovering = mTouchButtonAccumulator.getToolType() != ToolType::MOUSE &&
133 (mTouchButtonAccumulator.isHovering() ||
134 (mRawPointerAxes.pressure && inSlot.getPressure() <= 0));
135 outPointer.isHovering = isHovering;
136
137 // Assign pointer id using tracking id if available.
138 if (mHavePointerIds) {
139 const int32_t trackingId = inSlot.getTrackingId();
140 int32_t id = -1;
141 if (trackingId >= 0) {
142 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
143 uint32_t n = idBits.clearFirstMarkedBit();
144 if (mPointerTrackingIdMap[n] == trackingId) {
145 id = n;
146 break;
147 }
148 }
149
150 if (id < 0 && !mPointerIdBits.isFull()) {
151 id = mPointerIdBits.markFirstUnmarkedBit();
152 mPointerTrackingIdMap[id] = trackingId;
153 }
154 }
155 if (id < 0) {
156 mHavePointerIds = false;
157 outState->rawPointerData.clearIdBits();
158 newPointerIdBits.clear();
159 } else {
160 outPointer.id = id;
161 outState->rawPointerData.idToIndex[id] = outCount;
162 outState->rawPointerData.markIdBit(id, isHovering);
163 newPointerIdBits.markBit(id);
164 }
165 }
166 outCount += 1;
167 }
168
169 outState->rawPointerData.pointerCount = outCount;
170 mPointerIdBits = newPointerIdBits;
171
172 mMultiTouchMotionAccumulator.finishSync();
173 }
174
reconfigure(nsecs_t when,const InputReaderConfiguration & config,ConfigurationChanges changes)175 std::list<NotifyArgs> MultiTouchInputMapper::reconfigure(nsecs_t when,
176 const InputReaderConfiguration& config,
177 ConfigurationChanges changes) {
178 const bool simulateStylusWithTouch =
179 sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
180 if (simulateStylusWithTouch != mShouldSimulateStylusWithTouch) {
181 mShouldSimulateStylusWithTouch = simulateStylusWithTouch;
182 bumpGeneration();
183 }
184 return TouchInputMapper::reconfigure(when, config, changes);
185 }
186
configureRawPointerAxes()187 void MultiTouchInputMapper::configureRawPointerAxes() {
188 TouchInputMapper::configureRawPointerAxes();
189
190 // TODO(b/351870641): Investigate why we are sometime not getting valid axis infos for the x/y
191 // axes, even though those axes are required to be supported.
192 if (const auto xInfo = getAbsoluteAxisInfo(ABS_MT_POSITION_X); xInfo.has_value()) {
193 mRawPointerAxes.x = *xInfo;
194 }
195 if (const auto yInfo = getAbsoluteAxisInfo(ABS_MT_POSITION_Y); yInfo.has_value()) {
196 mRawPointerAxes.y = *yInfo;
197 }
198 mRawPointerAxes.touchMajor = getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR);
199 mRawPointerAxes.touchMinor = getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR);
200 mRawPointerAxes.toolMajor = getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR);
201 mRawPointerAxes.toolMinor = getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR);
202 mRawPointerAxes.orientation = getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
203 mRawPointerAxes.pressure = getAbsoluteAxisInfo(ABS_MT_PRESSURE);
204 mRawPointerAxes.distance = getAbsoluteAxisInfo(ABS_MT_DISTANCE);
205 mRawPointerAxes.trackingId = getAbsoluteAxisInfo(ABS_MT_TRACKING_ID);
206 mRawPointerAxes.slot = getAbsoluteAxisInfo(ABS_MT_SLOT);
207
208 if (mRawPointerAxes.trackingId && mRawPointerAxes.slot && mRawPointerAxes.slot->minValue == 0 &&
209 mRawPointerAxes.slot->maxValue > 0) {
210 size_t slotCount = mRawPointerAxes.slot->maxValue + 1;
211 if (slotCount > MAX_SLOTS) {
212 ALOGW("MultiTouch Device %s reported %zu slots but the framework "
213 "only supports a maximum of %zu slots at this time.",
214 getDeviceName().c_str(), slotCount, MAX_SLOTS);
215 slotCount = MAX_SLOTS;
216 }
217 mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
218 /*usingSlotsProtocol=*/true);
219 } else {
220 mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
221 /*usingSlotsProtocol=*/false);
222 }
223 }
224
hasStylus() const225 bool MultiTouchInputMapper::hasStylus() const {
226 return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() ||
227 mShouldSimulateStylusWithTouch;
228 }
229
230 } // namespace android
231