blob: f535ab4bbbdf67733828fa2a31470eab7140e38d [file] [log] [blame]
Harry Cutts79cc9fa2022-10-28 15:32:39 +00001/*
2 * Copyright 2022 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
Harry Cutts74235542022-11-24 15:52:53 +000019#include <android/input.h>
20#include <log/log_main.h>
21#include "TouchCursorInputMapperCommon.h"
Harry Cutts79cc9fa2022-10-28 15:32:39 +000022#include "TouchpadInputMapper.h"
23
24namespace android {
25
Harry Cutts1f48a442022-11-15 17:38:36 +000026namespace {
27
28short getMaxTouchCount(const InputDeviceContext& context) {
Harry Cuttsb2552152022-12-13 17:18:09 +000029 if (context.hasScanCode(BTN_TOOL_QUINTTAP)) return 5;
30 if (context.hasScanCode(BTN_TOOL_QUADTAP)) return 4;
31 if (context.hasScanCode(BTN_TOOL_TRIPLETAP)) return 3;
32 if (context.hasScanCode(BTN_TOOL_DOUBLETAP)) return 2;
33 if (context.hasScanCode(BTN_TOOL_FINGER)) return 1;
Harry Cutts1f48a442022-11-15 17:38:36 +000034 return 0;
35}
36
37HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
38 HardwareProperties props;
39 RawAbsoluteAxisInfo absMtPositionX;
40 context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
41 props.left = absMtPositionX.minValue;
42 props.right = absMtPositionX.maxValue;
43 props.res_x = absMtPositionX.resolution;
44
45 RawAbsoluteAxisInfo absMtPositionY;
46 context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
47 props.top = absMtPositionY.minValue;
48 props.bottom = absMtPositionY.maxValue;
49 props.res_y = absMtPositionY.resolution;
50
51 RawAbsoluteAxisInfo absMtOrientation;
52 context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
53 props.orientation_minimum = absMtOrientation.minValue;
54 props.orientation_maximum = absMtOrientation.maxValue;
55
56 RawAbsoluteAxisInfo absMtSlot;
57 context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
58 props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
59 props.max_touch_cnt = getMaxTouchCount(context);
60
61 // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
62 // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads
63 // that did this, so assume false.
64 props.supports_t5r2 = false;
65
66 props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT);
67 props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD);
68
69 // Mouse-only properties, which will always be false.
70 props.has_wheel = false;
71 props.wheel_is_hi_res = false;
72
73 // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads
74 // are haptic.
75 props.is_haptic_pad = false;
76 return props;
77}
78
Harry Cutts74235542022-11-24 15:52:53 +000079void gestureInterpreterCallback(void* clientData, const Gesture* gesture) {
80 TouchpadInputMapper* mapper = static_cast<TouchpadInputMapper*>(clientData);
81 mapper->consumeGesture(gesture);
82}
83
84uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) {
85 switch (gesturesButton) {
86 case GESTURES_BUTTON_LEFT:
87 return AMOTION_EVENT_BUTTON_PRIMARY;
88 case GESTURES_BUTTON_MIDDLE:
89 return AMOTION_EVENT_BUTTON_TERTIARY;
90 case GESTURES_BUTTON_RIGHT:
91 return AMOTION_EVENT_BUTTON_SECONDARY;
92 case GESTURES_BUTTON_BACK:
93 return AMOTION_EVENT_BUTTON_BACK;
94 case GESTURES_BUTTON_FORWARD:
95 return AMOTION_EVENT_BUTTON_FORWARD;
96 default:
97 return 0;
98 }
Harry Cutts1f48a442022-11-15 17:38:36 +000099}
100
101} // namespace
102
Harry Cutts79cc9fa2022-10-28 15:32:39 +0000103TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext)
Harry Cutts1f48a442022-11-15 17:38:36 +0000104 : InputMapper(deviceContext),
105 mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
Harry Cutts74235542022-11-24 15:52:53 +0000106 mPointerController(getContext()->getPointerController(getDeviceId())),
Harry Cutts47db1c72022-12-13 19:20:47 +0000107 mStateConverter(deviceContext) {
Harry Cutts1f48a442022-11-15 17:38:36 +0000108 mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
109 mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
Harry Cutts74235542022-11-24 15:52:53 +0000110 // Even though we don't explicitly delete copy/move semantics, it's safe to
111 // give away a pointer to TouchpadInputMapper here because
112 // 1) mGestureInterpreter's lifecycle is determined by TouchpadInputMapper, and
113 // 2) TouchpadInputMapper is stored as a unique_ptr and not moved.
114 mGestureInterpreter->SetCallback(gestureInterpreterCallback, this);
Harry Cutts1f48a442022-11-15 17:38:36 +0000115 // TODO(b/251196347): set a property provider, so we can change gesture properties.
116 // TODO(b/251196347): set a timer provider, so the library can use timers.
Harry Cutts1f48a442022-11-15 17:38:36 +0000117}
Harry Cutts79cc9fa2022-10-28 15:32:39 +0000118
Harry Cutts74235542022-11-24 15:52:53 +0000119TouchpadInputMapper::~TouchpadInputMapper() {
120 if (mPointerController != nullptr) {
121 mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
122 }
123}
124
Harry Cutts79cc9fa2022-10-28 15:32:39 +0000125uint32_t TouchpadInputMapper::getSources() const {
126 return AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD;
127}
128
Harry Cutts1f48a442022-11-15 17:38:36 +0000129std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
Harry Cutts47db1c72022-12-13 19:20:47 +0000130 mStateConverter.reset();
Harry Cutts74235542022-11-24 15:52:53 +0000131
132 mButtonState = 0;
Harry Cutts1f48a442022-11-15 17:38:36 +0000133 return InputMapper::reset(when);
134}
135
Harry Cutts79cc9fa2022-10-28 15:32:39 +0000136std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
Harry Cutts47db1c72022-12-13 19:20:47 +0000137 std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
138 if (state) {
139 return sendHardwareState(rawEvent->when, rawEvent->readTime, *state);
140 } else {
141 return {};
Harry Cutts1f48a442022-11-15 17:38:36 +0000142 }
Harry Cutts79cc9fa2022-10-28 15:32:39 +0000143}
144
Harry Cutts47db1c72022-12-13 19:20:47 +0000145std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime,
146 SelfContainedHardwareState schs) {
Harry Cutts74235542022-11-24 15:52:53 +0000147 mProcessing = true;
Harry Cutts47db1c72022-12-13 19:20:47 +0000148 mGestureInterpreter->PushHardwareState(&schs.state);
Harry Cutts74235542022-11-24 15:52:53 +0000149 mProcessing = false;
150
Harry Cutts47db1c72022-12-13 19:20:47 +0000151 return processGestures(when, readTime);
Harry Cutts74235542022-11-24 15:52:53 +0000152}
153
154void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
155 ALOGD("Gesture ready: %s", gesture->String().c_str());
156 if (!mProcessing) {
157 ALOGE("Received gesture outside of the normal processing flow; ignoring it.");
158 return;
159 }
160 mGesturesToProcess.push_back(*gesture);
161}
162
163std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) {
164 std::list<NotifyArgs> out = {};
165 for (Gesture& gesture : mGesturesToProcess) {
166 switch (gesture.type) {
167 case kGestureTypeMove:
168 out.push_back(handleMove(when, readTime, gesture));
169 break;
170 case kGestureTypeButtonsChange:
171 out += handleButtonsChange(when, readTime, gesture);
172 break;
173 default:
174 // TODO(b/251196347): handle more gesture types.
175 break;
176 }
177 }
178 mGesturesToProcess.clear();
179 return out;
180}
181
182NotifyArgs TouchpadInputMapper::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
183 PointerProperties props;
184 props.clear();
185 props.id = 0;
186 props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
187
188 mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
189 mPointerController->move(gesture.details.move.dx, gesture.details.move.dy);
190 mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
191 float xCursorPosition, yCursorPosition;
192 mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
193
194 PointerCoords coords;
195 coords.clear();
196 coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
197 coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
198 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, gesture.details.move.dx);
199 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, gesture.details.move.dy);
200 const bool down = isPointerDown(mButtonState);
201 coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
202
203 const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
204 return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState,
205 /* pointerCount= */ 1, &props, &coords, xCursorPosition, yCursorPosition);
206}
207
208std::list<NotifyArgs> TouchpadInputMapper::handleButtonsChange(nsecs_t when, nsecs_t readTime,
209 const Gesture& gesture) {
210 std::list<NotifyArgs> out = {};
211
212 mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
213 mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
214
215 PointerProperties props;
216 props.clear();
217 props.id = 0;
218 props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
219
220 float xCursorPosition, yCursorPosition;
221 mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
222
223 PointerCoords coords;
224 coords.clear();
225 coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
226 coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
227 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
228 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
229 const uint32_t buttonsPressed = gesture.details.buttons.down;
230 bool pointerDown = isPointerDown(mButtonState) ||
231 buttonsPressed &
232 (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT);
233 coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
234
235 uint32_t newButtonState = mButtonState;
236 std::list<NotifyArgs> pressEvents = {};
237 for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
238 if (buttonsPressed & button) {
239 uint32_t actionButton = gesturesButtonToMotionEventButton(button);
240 newButtonState |= actionButton;
241 pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
242 actionButton, newButtonState,
243 /* pointerCount= */ 1, &props, &coords,
244 xCursorPosition, yCursorPosition));
245 }
246 }
247 if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
248 mDownTime = when;
249 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
250 /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
251 &props, &coords, xCursorPosition, yCursorPosition));
252 }
253 out.splice(out.end(), pressEvents);
254
255 // The same button may be in both down and up in the same gesture, in which case we should treat
256 // it as having gone down and then up. So, we treat a single button change gesture as two state
257 // changes: a set of buttons going down, followed by a set of buttons going up.
258 mButtonState = newButtonState;
259
260 const uint32_t buttonsReleased = gesture.details.buttons.up;
261 for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
262 if (buttonsReleased & button) {
263 uint32_t actionButton = gesturesButtonToMotionEventButton(button);
264 newButtonState &= ~actionButton;
265 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
266 actionButton, newButtonState, /* pointerCount= */ 1,
267 &props, &coords, xCursorPosition, yCursorPosition));
268 }
269 }
270 if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
271 coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
272 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
273 newButtonState, /* pointerCount= */ 1, &props, &coords,
274 xCursorPosition, yCursorPosition));
275 }
276 mButtonState = newButtonState;
277 return out;
278}
279
280NotifyMotionArgs TouchpadInputMapper::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
281 int32_t actionButton, int32_t buttonState,
282 uint32_t pointerCount,
283 const PointerProperties* pointerProperties,
284 const PointerCoords* pointerCoords,
285 float xCursorPosition, float yCursorPosition) {
286 // TODO(b/260226362): consider what the appropriate source for these events is.
287 const uint32_t source = AINPUT_SOURCE_MOUSE;
288
289 return NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), source,
290 mPointerController->getDisplayId(), /* policyFlags= */ 0, action,
291 /* actionButton= */ actionButton, /* flags= */ 0,
292 getContext()->getGlobalMetaState(), buttonState,
293 MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
294 pointerProperties, pointerCoords,
295 /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, xCursorPosition,
296 yCursorPosition, /* downTime= */ mDownTime, /* videoFrames= */ {});
Harry Cutts1f48a442022-11-15 17:38:36 +0000297}
298
Harry Cutts79cc9fa2022-10-28 15:32:39 +0000299} // namespace android