• 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     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