blob: b193dffcfca6403e1940daf35efbd6e498ef123f [file] [log] [blame]
Prabir Pradhanbaa5c822019-08-30 15:27:05 -07001/*
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
Prabir Pradhan9244aea2020-02-05 20:31:40 -080017#include "../Macros.h"
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070018
19#include "MultiTouchInputMapper.h"
Siarhei Vishniakou31977182022-09-30 08:51:23 -070020#if defined(__ANDROID__)
Prabir Pradhanf4d65b12022-02-10 07:15:38 -080021#include <android/sysprop/InputProperties.sysprop.h>
Siarhei Vishniakou31977182022-09-30 08:51:23 -070022#endif
Prabir Pradhanf4d65b12022-02-10 07:15:38 -080023
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070024namespace android {
25
26// --- Constants ---
27
28// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
29static constexpr size_t MAX_SLOTS = 32;
30
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070031// --- MultiTouchInputMapper ---
32
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080033MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
34 : TouchInputMapper(deviceContext) {}
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070035
36MultiTouchInputMapper::~MultiTouchInputMapper() {}
37
Siarhei Vishniakou2935db72022-09-22 13:35:22 -070038std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
Prabir Pradhanafabcde2022-09-27 19:32:43 +000039 // The evdev multi-touch protocol does not allow userspace applications to query the initial or
40 // current state of the pointers at any time. This means if we clear our accumulated state when
41 // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
42 // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
43 // rebuilding the state from scratch, we work around this kernel API limitation by never
44 // fully clearing any state specific to the multi-touch protocol.
Siarhei Vishniakou2935db72022-09-22 13:35:22 -070045 return TouchInputMapper::reset(when);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070046}
47
Siarhei Vishniakou2935db72022-09-22 13:35:22 -070048std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {
49 std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070050
51 mMultiTouchMotionAccumulator.process(rawEvent);
Siarhei Vishniakou2935db72022-09-22 13:35:22 -070052 return out;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070053}
54
arthurhungcc7f9802020-04-30 17:55:40 +080055std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
56 const MultiTouchMotionAccumulator::Slot& inSlot) {
57 if (mHavePointerIds) {
58 int32_t trackingId = inSlot.getTrackingId();
59 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
60 int32_t n = idBits.clearFirstMarkedBit();
61 if (mPointerTrackingIdMap[n] == trackingId) {
62 return std::make_optional(n);
63 }
64 }
65 }
66 return std::nullopt;
67}
68
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070069void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
70 size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
71 size_t outCount = 0;
72 BitSet32 newPointerIdBits;
73 mHavePointerIds = true;
74
75 for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080076 const MultiTouchMotionAccumulator::Slot& inSlot =
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070077 mMultiTouchMotionAccumulator.getSlot(inIndex);
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080078 if (!inSlot.isInUse()) {
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070079 continue;
80 }
81
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080082 if (inSlot.getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
83 std::optional<int32_t> id = getActiveBitId(inSlot);
arthurhungcc7f9802020-04-30 17:55:40 +080084 if (id) {
85 outState->rawPointerData.canceledIdBits.markBit(id.value());
Arthur Hung421eb1c2020-01-16 00:09:42 +080086 }
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -050087 if (DEBUG_POINTERS) {
88 ALOGI("Stop processing slot %zu for it received a palm event from device %s",
89 inIndex, getDeviceName().c_str());
90 }
arthurhungbf89a482020-04-17 17:37:55 +080091 continue;
Arthur Hung421eb1c2020-01-16 00:09:42 +080092 }
93
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070094 if (outCount >= MAX_POINTERS) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -050095 if (DEBUG_POINTERS) {
Siarhei Vishniakou01747382022-01-20 13:23:27 -080096 ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -050097 "ignoring the rest.",
98 getDeviceName().c_str(), MAX_POINTERS);
99 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700100 break; // too many fingers!
101 }
102
103 RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800104 outPointer.x = inSlot.getX();
105 outPointer.y = inSlot.getY();
106 outPointer.pressure = inSlot.getPressure();
107 outPointer.touchMajor = inSlot.getTouchMajor();
108 outPointer.touchMinor = inSlot.getTouchMinor();
109 outPointer.toolMajor = inSlot.getToolMajor();
110 outPointer.toolMinor = inSlot.getToolMinor();
111 outPointer.orientation = inSlot.getOrientation();
112 outPointer.distance = inSlot.getDistance();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700113 outPointer.tiltX = 0;
114 outPointer.tiltY = 0;
115
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800116 outPointer.toolType = inSlot.getToolType();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700117 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
118 outPointer.toolType = mTouchButtonAccumulator.getToolType();
119 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
120 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
121 }
Prabir Pradhan5d0d97d2022-11-17 01:06:01 +0000122 } else if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS && !mStylusMtToolSeen) {
123 mStylusMtToolSeen = true;
124 // The multi-touch device produced a stylus event with MT_TOOL_PEN. Dynamically
125 // re-configure this input device so that we add SOURCE_STYLUS if we haven't already.
126 // This is to cover the case where we cannot reliably detect whether a multi-touch
127 // device will ever produce stylus events when it is initially being configured.
128 if (!isFromSource(mSource, AINPUT_SOURCE_STYLUS)) {
129 // Add the stylus source immediately so that it is included in any events generated
130 // before we have a chance to re-configure the device.
131 mSource |= AINPUT_SOURCE_STYLUS;
132 bumpGeneration();
133 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700134 }
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800135 if (shouldSimulateStylusWithTouch() &&
136 outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
137 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
138 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700139
140 bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
141 (mTouchButtonAccumulator.isHovering() ||
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800142 (mRawPointerAxes.pressure.valid && inSlot.getPressure() <= 0));
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700143 outPointer.isHovering = isHovering;
144
145 // Assign pointer id using tracking id if available.
146 if (mHavePointerIds) {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800147 int32_t trackingId = inSlot.getTrackingId();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700148 int32_t id = -1;
149 if (trackingId >= 0) {
150 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
151 uint32_t n = idBits.clearFirstMarkedBit();
152 if (mPointerTrackingIdMap[n] == trackingId) {
153 id = n;
154 }
155 }
156
157 if (id < 0 && !mPointerIdBits.isFull()) {
158 id = mPointerIdBits.markFirstUnmarkedBit();
159 mPointerTrackingIdMap[id] = trackingId;
160 }
161 }
162 if (id < 0) {
163 mHavePointerIds = false;
164 outState->rawPointerData.clearIdBits();
165 newPointerIdBits.clear();
166 } else {
167 outPointer.id = id;
168 outState->rawPointerData.idToIndex[id] = outCount;
169 outState->rawPointerData.markIdBit(id, isHovering);
170 newPointerIdBits.markBit(id);
171 }
172 }
173 outCount += 1;
174 }
175
176 outState->rawPointerData.pointerCount = outCount;
177 mPointerIdBits = newPointerIdBits;
178
179 mMultiTouchMotionAccumulator.finishSync();
180}
181
182void MultiTouchInputMapper::configureRawPointerAxes() {
183 TouchInputMapper::configureRawPointerAxes();
184
185 getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
186 getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
187 getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
188 getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
189 getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
190 getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
191 getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
192 getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
193 getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
194 getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
195 getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
196
197 if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
198 mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
199 size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
200 if (slotCount > MAX_SLOTS) {
201 ALOGW("MultiTouch Device %s reported %zu slots but the framework "
202 "only supports a maximum of %zu slots at this time.",
203 getDeviceName().c_str(), slotCount, MAX_SLOTS);
204 slotCount = MAX_SLOTS;
205 }
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800206 mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
207 true /*usingSlotsProtocol*/);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700208 } else {
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800209 mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700210 false /*usingSlotsProtocol*/);
211 }
212}
213
214bool MultiTouchInputMapper::hasStylus() const {
Prabir Pradhan5d0d97d2022-11-17 01:06:01 +0000215 return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() ||
216 shouldSimulateStylusWithTouch();
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800217}
218
219bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
220 static const bool SIMULATE_STYLUS_WITH_TOUCH =
Siarhei Vishniakou31977182022-09-30 08:51:23 -0700221#if defined(__ANDROID__)
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800222 sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
Siarhei Vishniakou31977182022-09-30 08:51:23 -0700223#else
224 // Disable this developer feature where sysproperties are not available
225 false;
226#endif
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800227 return SIMULATE_STYLUS_WITH_TOUCH &&
228 mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700229}
230
231} // namespace android