blob: 51edbf15331ce1a063f0a0d54b693f0d24f075ea [file] [log] [blame]
Zixuan Qudd0635d2023-02-06 04:52:38 +00001/*
Biswarup Pal590eb732023-11-30 17:59:57 +00002 * Copyright 2023 The Android Open Source Project
Zixuan Qudd0635d2023-02-06 04:52:38 +00003 *
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#define LOG_TAG "VirtualInputDevice"
18
Siarhei Vishniakou8508b662024-08-08 17:04:37 -070019#include <android-base/logging.h>
Zixuan Qudd0635d2023-02-06 04:52:38 +000020#include <android/input.h>
21#include <android/keycodes.h>
Biswarup Pal8ff5e5e2024-06-15 12:58:20 +000022#include <android_companion_virtualdevice_flags.h>
Zixuan Qudd0635d2023-02-06 04:52:38 +000023#include <fcntl.h>
24#include <input/Input.h>
25#include <input/VirtualInputDevice.h>
26#include <linux/uinput.h>
Zixuan Qudd0635d2023-02-06 04:52:38 +000027
Zixuan Qudd0635d2023-02-06 04:52:38 +000028#include <string>
29
30using android::base::unique_fd;
31
Siarhei Vishniakou8508b662024-08-08 17:04:37 -070032namespace {
33
Zixuan Qudd0635d2023-02-06 04:52:38 +000034/**
35 * Log debug messages about native virtual input devices.
36 * Enable this via "adb shell setprop log.tag.VirtualInputDevice DEBUG"
37 */
Siarhei Vishniakou8508b662024-08-08 17:04:37 -070038bool isDebug() {
Zixuan Qudd0635d2023-02-06 04:52:38 +000039 return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
40}
41
Siarhei Vishniakou8508b662024-08-08 17:04:37 -070042unique_fd invalidFd() {
43 return unique_fd(-1);
44}
45
46} // namespace
47
Zixuan Qudd0635d2023-02-06 04:52:38 +000048namespace android {
Biswarup Pal590eb732023-11-30 17:59:57 +000049
Biswarup Pal8ff5e5e2024-06-15 12:58:20 +000050namespace vd_flags = android::companion::virtualdevice::flags;
51
Siarhei Vishniakou8508b662024-08-08 17:04:37 -070052/** Creates a new uinput device and assigns a file descriptor. */
53unique_fd openUinput(const char* readableName, int32_t vendorId, int32_t productId,
54 const char* phys, DeviceType deviceType, int32_t screenHeight,
55 int32_t screenWidth) {
56 unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
57 if (fd < 0) {
58 ALOGE("Error creating uinput device: %s", strerror(errno));
59 return invalidFd();
60 }
61
62 ioctl(fd, UI_SET_PHYS, phys);
63
64 ioctl(fd, UI_SET_EVBIT, EV_KEY);
65 ioctl(fd, UI_SET_EVBIT, EV_SYN);
66 switch (deviceType) {
67 case DeviceType::DPAD:
68 for (const auto& [_, keyCode] : VirtualDpad::DPAD_KEY_CODE_MAPPING) {
69 ioctl(fd, UI_SET_KEYBIT, keyCode);
70 }
71 break;
72 case DeviceType::KEYBOARD:
73 for (const auto& [_, keyCode] : VirtualKeyboard::KEY_CODE_MAPPING) {
74 ioctl(fd, UI_SET_KEYBIT, keyCode);
75 }
76 break;
77 case DeviceType::MOUSE:
78 ioctl(fd, UI_SET_EVBIT, EV_REL);
79 ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
80 ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
81 ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
82 ioctl(fd, UI_SET_KEYBIT, BTN_BACK);
83 ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD);
84 ioctl(fd, UI_SET_RELBIT, REL_X);
85 ioctl(fd, UI_SET_RELBIT, REL_Y);
86 ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
87 ioctl(fd, UI_SET_RELBIT, REL_HWHEEL);
88 if (vd_flags::high_resolution_scroll()) {
89 ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
90 ioctl(fd, UI_SET_RELBIT, REL_HWHEEL_HI_RES);
91 }
92 break;
93 case DeviceType::TOUCHSCREEN:
94 ioctl(fd, UI_SET_EVBIT, EV_ABS);
95 ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
96 ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
97 ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
98 ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
99 ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
100 ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
101 ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
102 ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
103 ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
104 break;
105 case DeviceType::STYLUS:
106 ioctl(fd, UI_SET_EVBIT, EV_ABS);
107 ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
108 ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS);
109 ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2);
110 ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_PEN);
111 ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_RUBBER);
112 ioctl(fd, UI_SET_ABSBIT, ABS_X);
113 ioctl(fd, UI_SET_ABSBIT, ABS_Y);
114 ioctl(fd, UI_SET_ABSBIT, ABS_TILT_X);
115 ioctl(fd, UI_SET_ABSBIT, ABS_TILT_Y);
116 ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE);
117 ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
118 break;
119 case DeviceType::ROTARY_ENCODER:
120 ioctl(fd, UI_SET_EVBIT, EV_REL);
121 ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
122 if (vd_flags::high_resolution_scroll()) {
123 ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
124 }
125 break;
126 default:
127 ALOGE("Invalid input device type %d", static_cast<int32_t>(deviceType));
128 return invalidFd();
129 }
130
131 int version;
132 if (ioctl(fd, UI_GET_VERSION, &version) == 0 && version >= 5) {
133 uinput_setup setup;
134 memset(&setup, 0, sizeof(setup));
135 std::strncpy(setup.name, readableName, UINPUT_MAX_NAME_SIZE);
136 setup.id.version = 1;
137 setup.id.bustype = BUS_VIRTUAL;
138 setup.id.vendor = vendorId;
139 setup.id.product = productId;
140 if (deviceType == DeviceType::TOUCHSCREEN) {
141 uinput_abs_setup xAbsSetup;
142 xAbsSetup.code = ABS_MT_POSITION_X;
143 xAbsSetup.absinfo.maximum = screenWidth - 1;
144 xAbsSetup.absinfo.minimum = 0;
145 if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
146 ALOGE("Error creating touchscreen uinput x axis: %s", strerror(errno));
147 return invalidFd();
148 }
149 uinput_abs_setup yAbsSetup;
150 yAbsSetup.code = ABS_MT_POSITION_Y;
151 yAbsSetup.absinfo.maximum = screenHeight - 1;
152 yAbsSetup.absinfo.minimum = 0;
153 if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
154 ALOGE("Error creating touchscreen uinput y axis: %s", strerror(errno));
155 return invalidFd();
156 }
157 uinput_abs_setup majorAbsSetup;
158 majorAbsSetup.code = ABS_MT_TOUCH_MAJOR;
159 majorAbsSetup.absinfo.maximum = screenWidth - 1;
160 majorAbsSetup.absinfo.minimum = 0;
161 if (ioctl(fd, UI_ABS_SETUP, &majorAbsSetup) != 0) {
162 ALOGE("Error creating touchscreen uinput major axis: %s", strerror(errno));
163 return invalidFd();
164 }
165 uinput_abs_setup pressureAbsSetup;
166 pressureAbsSetup.code = ABS_MT_PRESSURE;
167 pressureAbsSetup.absinfo.maximum = 255;
168 pressureAbsSetup.absinfo.minimum = 0;
169 if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
170 ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
171 return invalidFd();
172 }
173 uinput_abs_setup slotAbsSetup;
174 slotAbsSetup.code = ABS_MT_SLOT;
175 slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
176 slotAbsSetup.absinfo.minimum = 0;
177 if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) {
178 ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno));
179 return invalidFd();
180 }
181 uinput_abs_setup trackingIdAbsSetup;
182 trackingIdAbsSetup.code = ABS_MT_TRACKING_ID;
183 trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
184 trackingIdAbsSetup.absinfo.minimum = 0;
185 if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) {
186 ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno));
187 return invalidFd();
188 }
189 } else if (deviceType == DeviceType::STYLUS) {
190 uinput_abs_setup xAbsSetup;
191 xAbsSetup.code = ABS_X;
192 xAbsSetup.absinfo.maximum = screenWidth - 1;
193 xAbsSetup.absinfo.minimum = 0;
194 if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
195 ALOGE("Error creating stylus uinput x axis: %s", strerror(errno));
196 return invalidFd();
197 }
198 uinput_abs_setup yAbsSetup;
199 yAbsSetup.code = ABS_Y;
200 yAbsSetup.absinfo.maximum = screenHeight - 1;
201 yAbsSetup.absinfo.minimum = 0;
202 if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
203 ALOGE("Error creating stylus uinput y axis: %s", strerror(errno));
204 return invalidFd();
205 }
206 uinput_abs_setup tiltXAbsSetup;
207 tiltXAbsSetup.code = ABS_TILT_X;
208 tiltXAbsSetup.absinfo.maximum = 90;
209 tiltXAbsSetup.absinfo.minimum = -90;
210 if (ioctl(fd, UI_ABS_SETUP, &tiltXAbsSetup) != 0) {
211 ALOGE("Error creating stylus uinput tilt x axis: %s", strerror(errno));
212 return invalidFd();
213 }
214 uinput_abs_setup tiltYAbsSetup;
215 tiltYAbsSetup.code = ABS_TILT_Y;
216 tiltYAbsSetup.absinfo.maximum = 90;
217 tiltYAbsSetup.absinfo.minimum = -90;
218 if (ioctl(fd, UI_ABS_SETUP, &tiltYAbsSetup) != 0) {
219 ALOGE("Error creating stylus uinput tilt y axis: %s", strerror(errno));
220 return invalidFd();
221 }
222 uinput_abs_setup pressureAbsSetup;
223 pressureAbsSetup.code = ABS_PRESSURE;
224 pressureAbsSetup.absinfo.maximum = 255;
225 pressureAbsSetup.absinfo.minimum = 0;
226 if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
227 ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
228 return invalidFd();
229 }
230 }
231 if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
232 ALOGE("Error creating uinput device: %s", strerror(errno));
233 return invalidFd();
234 }
235 } else {
236 // UI_DEV_SETUP was not introduced until version 5. Try setting up manually.
237 ALOGI("Falling back to version %d manual setup", version);
238 uinput_user_dev fallback;
239 memset(&fallback, 0, sizeof(fallback));
240 std::strncpy(fallback.name, readableName, UINPUT_MAX_NAME_SIZE);
241 fallback.id.version = 1;
242 fallback.id.bustype = BUS_VIRTUAL;
243 fallback.id.vendor = vendorId;
244 fallback.id.product = productId;
245 if (deviceType == DeviceType::TOUCHSCREEN) {
246 fallback.absmin[ABS_MT_POSITION_X] = 0;
247 fallback.absmax[ABS_MT_POSITION_X] = screenWidth - 1;
248 fallback.absmin[ABS_MT_POSITION_Y] = 0;
249 fallback.absmax[ABS_MT_POSITION_Y] = screenHeight - 1;
250 fallback.absmin[ABS_MT_TOUCH_MAJOR] = 0;
251 fallback.absmax[ABS_MT_TOUCH_MAJOR] = screenWidth - 1;
252 fallback.absmin[ABS_MT_PRESSURE] = 0;
253 fallback.absmax[ABS_MT_PRESSURE] = 255;
254 } else if (deviceType == DeviceType::STYLUS) {
255 fallback.absmin[ABS_X] = 0;
256 fallback.absmax[ABS_X] = screenWidth - 1;
257 fallback.absmin[ABS_Y] = 0;
258 fallback.absmax[ABS_Y] = screenHeight - 1;
259 fallback.absmin[ABS_TILT_X] = -90;
260 fallback.absmax[ABS_TILT_X] = 90;
261 fallback.absmin[ABS_TILT_Y] = -90;
262 fallback.absmax[ABS_TILT_Y] = 90;
263 fallback.absmin[ABS_PRESSURE] = 0;
264 fallback.absmax[ABS_PRESSURE] = 255;
265 }
266 if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) {
267 ALOGE("Error creating uinput device: %s", strerror(errno));
268 return invalidFd();
269 }
270 }
271
272 if (ioctl(fd, UI_DEV_CREATE) != 0) {
273 ALOGE("Error creating uinput device: %s", strerror(errno));
274 return invalidFd();
275 }
276
277 return fd;
278}
279
Zixuan Qudd0635d2023-02-06 04:52:38 +0000280VirtualInputDevice::VirtualInputDevice(unique_fd fd) : mFd(std::move(fd)) {}
Biswarup Pal590eb732023-11-30 17:59:57 +0000281
Zixuan Qudd0635d2023-02-06 04:52:38 +0000282VirtualInputDevice::~VirtualInputDevice() {
283 ioctl(mFd, UI_DEV_DESTROY);
284}
285
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000286bool VirtualInputDevice::writeInputEvent(uint16_t type, uint16_t code, int32_t value,
287 std::chrono::nanoseconds eventTime) {
288 std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(eventTime);
289 std::chrono::microseconds microseconds =
290 std::chrono::duration_cast<std::chrono::microseconds>(eventTime - seconds);
Colin Cross983cae02023-05-09 22:45:35 -0700291 struct input_event ev = {.type = type, .code = code, .value = value};
292 ev.input_event_sec = static_cast<decltype(ev.input_event_sec)>(seconds.count());
293 ev.input_event_usec = static_cast<decltype(ev.input_event_usec)>(microseconds.count());
294
Zixuan Qudd0635d2023-02-06 04:52:38 +0000295 return TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(struct input_event))) == sizeof(ev);
296}
297
Biswarup Pal590eb732023-11-30 17:59:57 +0000298/** Utility method to write keyboard key events or mouse/stylus button events. */
Zixuan Qudd0635d2023-02-06 04:52:38 +0000299bool VirtualInputDevice::writeEvKeyEvent(int32_t androidCode, int32_t androidAction,
300 const std::map<int, int>& evKeyCodeMapping,
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000301 const std::map<int, UinputAction>& actionMapping,
302 std::chrono::nanoseconds eventTime) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000303 auto evKeyCodeIterator = evKeyCodeMapping.find(androidCode);
304 if (evKeyCodeIterator == evKeyCodeMapping.end()) {
305 ALOGE("Unsupported native EV keycode for android code %d", androidCode);
306 return false;
307 }
308 auto actionIterator = actionMapping.find(androidAction);
309 if (actionIterator == actionMapping.end()) {
Biswarup Pal590eb732023-11-30 17:59:57 +0000310 ALOGE("Unsupported native action for android action %d", androidAction);
Zixuan Qudd0635d2023-02-06 04:52:38 +0000311 return false;
312 }
Biswarup Pal590eb732023-11-30 17:59:57 +0000313 int32_t action = static_cast<int32_t>(actionIterator->second);
314 uint16_t evKeyCode = static_cast<uint16_t>(evKeyCodeIterator->second);
315 if (!writeInputEvent(EV_KEY, evKeyCode, action, eventTime)) {
316 ALOGE("Failed to write native action %d and EV keycode %u.", action, evKeyCode);
Zixuan Qudd0635d2023-02-06 04:52:38 +0000317 return false;
318 }
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000319 if (!writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime)) {
Biswarup Pal590eb732023-11-30 17:59:57 +0000320 ALOGE("Failed to write SYN_REPORT for EV_KEY event.");
Zixuan Qudd0635d2023-02-06 04:52:38 +0000321 return false;
322 }
323 return true;
324}
325
326// --- VirtualKeyboard ---
327const std::map<int, UinputAction> VirtualKeyboard::KEY_ACTION_MAPPING = {
328 {AKEY_EVENT_ACTION_DOWN, UinputAction::PRESS},
329 {AKEY_EVENT_ACTION_UP, UinputAction::RELEASE},
330};
Biswarup Pal590eb732023-11-30 17:59:57 +0000331
Zixuan Qudd0635d2023-02-06 04:52:38 +0000332// Keycode mapping from https://source.android.com/devices/input/keyboard-devices
333const std::map<int, int> VirtualKeyboard::KEY_CODE_MAPPING = {
334 {AKEYCODE_0, KEY_0},
335 {AKEYCODE_1, KEY_1},
336 {AKEYCODE_2, KEY_2},
337 {AKEYCODE_3, KEY_3},
338 {AKEYCODE_4, KEY_4},
339 {AKEYCODE_5, KEY_5},
340 {AKEYCODE_6, KEY_6},
341 {AKEYCODE_7, KEY_7},
342 {AKEYCODE_8, KEY_8},
343 {AKEYCODE_9, KEY_9},
344 {AKEYCODE_A, KEY_A},
345 {AKEYCODE_B, KEY_B},
346 {AKEYCODE_C, KEY_C},
347 {AKEYCODE_D, KEY_D},
348 {AKEYCODE_E, KEY_E},
349 {AKEYCODE_F, KEY_F},
350 {AKEYCODE_G, KEY_G},
351 {AKEYCODE_H, KEY_H},
352 {AKEYCODE_I, KEY_I},
353 {AKEYCODE_J, KEY_J},
354 {AKEYCODE_K, KEY_K},
355 {AKEYCODE_L, KEY_L},
356 {AKEYCODE_M, KEY_M},
357 {AKEYCODE_N, KEY_N},
358 {AKEYCODE_O, KEY_O},
359 {AKEYCODE_P, KEY_P},
360 {AKEYCODE_Q, KEY_Q},
361 {AKEYCODE_R, KEY_R},
362 {AKEYCODE_S, KEY_S},
363 {AKEYCODE_T, KEY_T},
364 {AKEYCODE_U, KEY_U},
365 {AKEYCODE_V, KEY_V},
366 {AKEYCODE_W, KEY_W},
367 {AKEYCODE_X, KEY_X},
368 {AKEYCODE_Y, KEY_Y},
369 {AKEYCODE_Z, KEY_Z},
370 {AKEYCODE_GRAVE, KEY_GRAVE},
371 {AKEYCODE_MINUS, KEY_MINUS},
372 {AKEYCODE_EQUALS, KEY_EQUAL},
373 {AKEYCODE_LEFT_BRACKET, KEY_LEFTBRACE},
374 {AKEYCODE_RIGHT_BRACKET, KEY_RIGHTBRACE},
375 {AKEYCODE_BACKSLASH, KEY_BACKSLASH},
376 {AKEYCODE_SEMICOLON, KEY_SEMICOLON},
377 {AKEYCODE_APOSTROPHE, KEY_APOSTROPHE},
378 {AKEYCODE_COMMA, KEY_COMMA},
379 {AKEYCODE_PERIOD, KEY_DOT},
380 {AKEYCODE_SLASH, KEY_SLASH},
381 {AKEYCODE_ALT_LEFT, KEY_LEFTALT},
382 {AKEYCODE_ALT_RIGHT, KEY_RIGHTALT},
383 {AKEYCODE_CTRL_LEFT, KEY_LEFTCTRL},
384 {AKEYCODE_CTRL_RIGHT, KEY_RIGHTCTRL},
385 {AKEYCODE_SHIFT_LEFT, KEY_LEFTSHIFT},
386 {AKEYCODE_SHIFT_RIGHT, KEY_RIGHTSHIFT},
387 {AKEYCODE_META_LEFT, KEY_LEFTMETA},
388 {AKEYCODE_META_RIGHT, KEY_RIGHTMETA},
389 {AKEYCODE_CAPS_LOCK, KEY_CAPSLOCK},
390 {AKEYCODE_SCROLL_LOCK, KEY_SCROLLLOCK},
391 {AKEYCODE_NUM_LOCK, KEY_NUMLOCK},
392 {AKEYCODE_ENTER, KEY_ENTER},
393 {AKEYCODE_TAB, KEY_TAB},
394 {AKEYCODE_SPACE, KEY_SPACE},
395 {AKEYCODE_DPAD_DOWN, KEY_DOWN},
396 {AKEYCODE_DPAD_UP, KEY_UP},
397 {AKEYCODE_DPAD_LEFT, KEY_LEFT},
398 {AKEYCODE_DPAD_RIGHT, KEY_RIGHT},
399 {AKEYCODE_MOVE_END, KEY_END},
400 {AKEYCODE_MOVE_HOME, KEY_HOME},
401 {AKEYCODE_PAGE_DOWN, KEY_PAGEDOWN},
402 {AKEYCODE_PAGE_UP, KEY_PAGEUP},
403 {AKEYCODE_DEL, KEY_BACKSPACE},
404 {AKEYCODE_FORWARD_DEL, KEY_DELETE},
405 {AKEYCODE_INSERT, KEY_INSERT},
406 {AKEYCODE_ESCAPE, KEY_ESC},
407 {AKEYCODE_BREAK, KEY_PAUSE},
408 {AKEYCODE_F1, KEY_F1},
409 {AKEYCODE_F2, KEY_F2},
410 {AKEYCODE_F3, KEY_F3},
411 {AKEYCODE_F4, KEY_F4},
412 {AKEYCODE_F5, KEY_F5},
413 {AKEYCODE_F6, KEY_F6},
414 {AKEYCODE_F7, KEY_F7},
415 {AKEYCODE_F8, KEY_F8},
416 {AKEYCODE_F9, KEY_F9},
417 {AKEYCODE_F10, KEY_F10},
418 {AKEYCODE_F11, KEY_F11},
419 {AKEYCODE_F12, KEY_F12},
420 {AKEYCODE_BACK, KEY_BACK},
421 {AKEYCODE_FORWARD, KEY_FORWARD},
422 {AKEYCODE_NUMPAD_1, KEY_KP1},
423 {AKEYCODE_NUMPAD_2, KEY_KP2},
424 {AKEYCODE_NUMPAD_3, KEY_KP3},
425 {AKEYCODE_NUMPAD_4, KEY_KP4},
426 {AKEYCODE_NUMPAD_5, KEY_KP5},
427 {AKEYCODE_NUMPAD_6, KEY_KP6},
428 {AKEYCODE_NUMPAD_7, KEY_KP7},
429 {AKEYCODE_NUMPAD_8, KEY_KP8},
430 {AKEYCODE_NUMPAD_9, KEY_KP9},
431 {AKEYCODE_NUMPAD_0, KEY_KP0},
432 {AKEYCODE_NUMPAD_ADD, KEY_KPPLUS},
433 {AKEYCODE_NUMPAD_SUBTRACT, KEY_KPMINUS},
434 {AKEYCODE_NUMPAD_MULTIPLY, KEY_KPASTERISK},
435 {AKEYCODE_NUMPAD_DIVIDE, KEY_KPSLASH},
436 {AKEYCODE_NUMPAD_DOT, KEY_KPDOT},
437 {AKEYCODE_NUMPAD_ENTER, KEY_KPENTER},
438 {AKEYCODE_NUMPAD_EQUALS, KEY_KPEQUAL},
439 {AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA},
Vladimir Komsiyski65efbaa2023-11-14 09:30:12 +0100440 {AKEYCODE_LANGUAGE_SWITCH, KEY_LANGUAGE},
Zixuan Qudd0635d2023-02-06 04:52:38 +0000441};
Biswarup Pal590eb732023-11-30 17:59:57 +0000442
Zixuan Qudd0635d2023-02-06 04:52:38 +0000443VirtualKeyboard::VirtualKeyboard(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
Biswarup Pal590eb732023-11-30 17:59:57 +0000444
Zixuan Qudd0635d2023-02-06 04:52:38 +0000445VirtualKeyboard::~VirtualKeyboard() {}
446
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000447bool VirtualKeyboard::writeKeyEvent(int32_t androidKeyCode, int32_t androidAction,
448 std::chrono::nanoseconds eventTime) {
449 return writeEvKeyEvent(androidKeyCode, androidAction, KEY_CODE_MAPPING, KEY_ACTION_MAPPING,
450 eventTime);
Zixuan Qudd0635d2023-02-06 04:52:38 +0000451}
452
453// --- VirtualDpad ---
454// Dpad keycode mapping from https://source.android.com/devices/input/keyboard-devices
455const std::map<int, int> VirtualDpad::DPAD_KEY_CODE_MAPPING = {
456 // clang-format off
457 {AKEYCODE_DPAD_DOWN, KEY_DOWN},
458 {AKEYCODE_DPAD_UP, KEY_UP},
459 {AKEYCODE_DPAD_LEFT, KEY_LEFT},
460 {AKEYCODE_DPAD_RIGHT, KEY_RIGHT},
461 {AKEYCODE_DPAD_CENTER, KEY_SELECT},
462 {AKEYCODE_BACK, KEY_BACK},
463 // clang-format on
464};
465
466VirtualDpad::VirtualDpad(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
467
468VirtualDpad::~VirtualDpad() {}
469
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000470bool VirtualDpad::writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction,
471 std::chrono::nanoseconds eventTime) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000472 return writeEvKeyEvent(androidKeyCode, androidAction, DPAD_KEY_CODE_MAPPING,
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000473 VirtualKeyboard::KEY_ACTION_MAPPING, eventTime);
Zixuan Qudd0635d2023-02-06 04:52:38 +0000474}
475
476// --- VirtualMouse ---
477const std::map<int, UinputAction> VirtualMouse::BUTTON_ACTION_MAPPING = {
478 {AMOTION_EVENT_ACTION_BUTTON_PRESS, UinputAction::PRESS},
479 {AMOTION_EVENT_ACTION_BUTTON_RELEASE, UinputAction::RELEASE},
480};
481
482// Button code mapping from https://source.android.com/devices/input/touch-devices
483const std::map<int, int> VirtualMouse::BUTTON_CODE_MAPPING = {
484 // clang-format off
485 {AMOTION_EVENT_BUTTON_PRIMARY, BTN_LEFT},
486 {AMOTION_EVENT_BUTTON_SECONDARY, BTN_RIGHT},
487 {AMOTION_EVENT_BUTTON_TERTIARY, BTN_MIDDLE},
488 {AMOTION_EVENT_BUTTON_BACK, BTN_BACK},
489 {AMOTION_EVENT_BUTTON_FORWARD, BTN_FORWARD},
490 // clang-format on
491};
492
Biswarup Pal8ff5e5e2024-06-15 12:58:20 +0000493VirtualMouse::VirtualMouse(unique_fd fd)
494 : VirtualInputDevice(std::move(fd)),
495 mAccumulatedHighResScrollX(0),
496 mAccumulatedHighResScrollY(0) {}
Zixuan Qudd0635d2023-02-06 04:52:38 +0000497
498VirtualMouse::~VirtualMouse() {}
499
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000500bool VirtualMouse::writeButtonEvent(int32_t androidButtonCode, int32_t androidAction,
501 std::chrono::nanoseconds eventTime) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000502 return writeEvKeyEvent(androidButtonCode, androidAction, BUTTON_CODE_MAPPING,
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000503 BUTTON_ACTION_MAPPING, eventTime);
Zixuan Qudd0635d2023-02-06 04:52:38 +0000504}
505
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000506bool VirtualMouse::writeRelativeEvent(float relativeX, float relativeY,
507 std::chrono::nanoseconds eventTime) {
508 return writeInputEvent(EV_REL, REL_X, relativeX, eventTime) &&
509 writeInputEvent(EV_REL, REL_Y, relativeY, eventTime) &&
510 writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
Zixuan Qudd0635d2023-02-06 04:52:38 +0000511}
512
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000513bool VirtualMouse::writeScrollEvent(float xAxisMovement, float yAxisMovement,
514 std::chrono::nanoseconds eventTime) {
Biswarup Pal8ff5e5e2024-06-15 12:58:20 +0000515 if (!vd_flags::high_resolution_scroll()) {
Biswarup Palba27d1d2024-07-09 19:57:33 +0000516 return writeInputEvent(EV_REL, REL_HWHEEL, static_cast<int32_t>(xAxisMovement),
517 eventTime) &&
518 writeInputEvent(EV_REL, REL_WHEEL, static_cast<int32_t>(yAxisMovement),
519 eventTime) &&
Biswarup Pal8ff5e5e2024-06-15 12:58:20 +0000520 writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
521 }
522
Biswarup Palba27d1d2024-07-09 19:57:33 +0000523 const auto highResScrollX =
524 static_cast<int32_t>(xAxisMovement * kEvdevHighResScrollUnitsPerDetent);
525 const auto highResScrollY =
526 static_cast<int32_t>(yAxisMovement * kEvdevHighResScrollUnitsPerDetent);
Biswarup Pal8ff5e5e2024-06-15 12:58:20 +0000527 bool highResScrollResult =
528 writeInputEvent(EV_REL, REL_HWHEEL_HI_RES, highResScrollX, eventTime) &&
529 writeInputEvent(EV_REL, REL_WHEEL_HI_RES, highResScrollY, eventTime);
530 if (!highResScrollResult) {
531 return false;
532 }
533
534 // According to evdev spec, a high-resolution mouse needs to emit REL_WHEEL / REL_HWHEEL events
535 // in addition to high-res scroll events. Regular scroll events can approximate high-res scroll
536 // events, so we send a regular scroll event when the accumulated scroll motion reaches a detent
537 // (single mouse wheel click).
538 mAccumulatedHighResScrollX += highResScrollX;
539 mAccumulatedHighResScrollY += highResScrollY;
Biswarup Palba27d1d2024-07-09 19:57:33 +0000540 const int32_t scrollX = mAccumulatedHighResScrollX / kEvdevHighResScrollUnitsPerDetent;
541 const int32_t scrollY = mAccumulatedHighResScrollY / kEvdevHighResScrollUnitsPerDetent;
Biswarup Pal8ff5e5e2024-06-15 12:58:20 +0000542 if (scrollX != 0) {
543 if (!writeInputEvent(EV_REL, REL_HWHEEL, scrollX, eventTime)) {
544 return false;
545 }
Biswarup Palba27d1d2024-07-09 19:57:33 +0000546 mAccumulatedHighResScrollX %= kEvdevHighResScrollUnitsPerDetent;
Biswarup Pal8ff5e5e2024-06-15 12:58:20 +0000547 }
548 if (scrollY != 0) {
549 if (!writeInputEvent(EV_REL, REL_WHEEL, scrollY, eventTime)) {
550 return false;
551 }
Biswarup Palba27d1d2024-07-09 19:57:33 +0000552 mAccumulatedHighResScrollY %= kEvdevHighResScrollUnitsPerDetent;
Biswarup Pal8ff5e5e2024-06-15 12:58:20 +0000553 }
554
555 return writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
Zixuan Qudd0635d2023-02-06 04:52:38 +0000556}
557
558// --- VirtualTouchscreen ---
559const std::map<int, UinputAction> VirtualTouchscreen::TOUCH_ACTION_MAPPING = {
560 {AMOTION_EVENT_ACTION_DOWN, UinputAction::PRESS},
561 {AMOTION_EVENT_ACTION_UP, UinputAction::RELEASE},
562 {AMOTION_EVENT_ACTION_MOVE, UinputAction::MOVE},
563 {AMOTION_EVENT_ACTION_CANCEL, UinputAction::CANCEL},
564};
Biswarup Pal590eb732023-11-30 17:59:57 +0000565
Zixuan Qudd0635d2023-02-06 04:52:38 +0000566// Tool type mapping from https://source.android.com/devices/input/touch-devices
567const std::map<int, int> VirtualTouchscreen::TOOL_TYPE_MAPPING = {
568 {AMOTION_EVENT_TOOL_TYPE_FINGER, MT_TOOL_FINGER},
569 {AMOTION_EVENT_TOOL_TYPE_PALM, MT_TOOL_PALM},
570};
571
572VirtualTouchscreen::VirtualTouchscreen(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
573
574VirtualTouchscreen::~VirtualTouchscreen() {}
575
576bool VirtualTouchscreen::isValidPointerId(int32_t pointerId, UinputAction uinputAction) {
577 if (pointerId < -1 || pointerId >= (int)MAX_POINTERS) {
578 ALOGE("Virtual touch event has invalid pointer id %d; value must be between -1 and %zu",
579 pointerId, MAX_POINTERS - 0);
580 return false;
581 }
582
583 if (uinputAction == UinputAction::PRESS && mActivePointers.test(pointerId)) {
584 ALOGE("Repetitive action DOWN event received on a pointer %d that is already down.",
585 pointerId);
586 return false;
587 }
588 if (uinputAction == UinputAction::RELEASE && !mActivePointers.test(pointerId)) {
589 ALOGE("PointerId %d action UP received with no prior action DOWN on touchscreen %d.",
590 pointerId, mFd.get());
591 return false;
592 }
593 return true;
594}
595
596bool VirtualTouchscreen::writeTouchEvent(int32_t pointerId, int32_t toolType, int32_t action,
597 float locationX, float locationY, float pressure,
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000598 float majorAxisSize, std::chrono::nanoseconds eventTime) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000599 auto actionIterator = TOUCH_ACTION_MAPPING.find(action);
600 if (actionIterator == TOUCH_ACTION_MAPPING.end()) {
601 return false;
602 }
603 UinputAction uinputAction = actionIterator->second;
604 if (!isValidPointerId(pointerId, uinputAction)) {
605 return false;
606 }
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000607 if (!writeInputEvent(EV_ABS, ABS_MT_SLOT, pointerId, eventTime)) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000608 return false;
609 }
610 auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType);
611 if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) {
612 return false;
613 }
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000614 if (!writeInputEvent(EV_ABS, ABS_MT_TOOL_TYPE, static_cast<int32_t>(toolTypeIterator->second),
615 eventTime)) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000616 return false;
617 }
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000618 if (uinputAction == UinputAction::PRESS && !handleTouchDown(pointerId, eventTime)) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000619 return false;
620 }
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000621 if (uinputAction == UinputAction::RELEASE && !handleTouchUp(pointerId, eventTime)) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000622 return false;
623 }
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000624 if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_X, locationX, eventTime)) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000625 return false;
626 }
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000627 if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_Y, locationY, eventTime)) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000628 return false;
629 }
630 if (!isnan(pressure)) {
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000631 if (!writeInputEvent(EV_ABS, ABS_MT_PRESSURE, pressure, eventTime)) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000632 return false;
633 }
634 }
635 if (!isnan(majorAxisSize)) {
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000636 if (!writeInputEvent(EV_ABS, ABS_MT_TOUCH_MAJOR, majorAxisSize, eventTime)) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000637 return false;
638 }
639 }
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000640 return writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
Zixuan Qudd0635d2023-02-06 04:52:38 +0000641}
642
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000643bool VirtualTouchscreen::handleTouchUp(int32_t pointerId, std::chrono::nanoseconds eventTime) {
644 if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(-1), eventTime)) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000645 return false;
646 }
647 // When a pointer is no longer in touch, remove the pointer id from the corresponding
648 // entry in the unreleased touches map.
649 mActivePointers.reset(pointerId);
650 ALOGD_IF(isDebug(), "Pointer %d erased from the touchscreen %d", pointerId, mFd.get());
651
652 // Only sends the BTN UP event when there's no pointers on the touchscreen.
653 if (mActivePointers.none()) {
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000654 if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE),
655 eventTime)) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000656 return false;
657 }
658 ALOGD_IF(isDebug(), "No pointers on touchscreen %d, BTN UP event sent.", mFd.get());
659 }
660 return true;
661}
662
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000663bool VirtualTouchscreen::handleTouchDown(int32_t pointerId, std::chrono::nanoseconds eventTime) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000664 // When a new pointer is down on the touchscreen, add the pointer id in the corresponding
665 // entry in the unreleased touches map.
666 if (mActivePointers.none()) {
667 // Only sends the BTN Down event when the first pointer on the touchscreen is down.
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000668 if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS),
669 eventTime)) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000670 return false;
671 }
672 ALOGD_IF(isDebug(), "First pointer %d down under touchscreen %d, BTN DOWN event sent",
673 pointerId, mFd.get());
674 }
675
676 mActivePointers.set(pointerId);
677 ALOGD_IF(isDebug(), "Added pointer %d under touchscreen %d in the map", pointerId, mFd.get());
Biswarup Pale9fe2df2023-04-05 18:20:54 +0000678 if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(pointerId), eventTime)) {
Zixuan Qudd0635d2023-02-06 04:52:38 +0000679 return false;
680 }
681 return true;
682}
683
Biswarup Pal590eb732023-11-30 17:59:57 +0000684// --- VirtualStylus ---
685const std::map<int, int> VirtualStylus::TOOL_TYPE_MAPPING = {
686 {AMOTION_EVENT_TOOL_TYPE_STYLUS, BTN_TOOL_PEN},
687 {AMOTION_EVENT_TOOL_TYPE_ERASER, BTN_TOOL_RUBBER},
688};
689
690// Button code mapping from https://source.android.com/devices/input/touch-devices
691const std::map<int, int> VirtualStylus::BUTTON_CODE_MAPPING = {
692 {AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, BTN_STYLUS},
693 {AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, BTN_STYLUS2},
694};
695
696VirtualStylus::VirtualStylus(unique_fd fd)
697 : VirtualInputDevice(std::move(fd)), mIsStylusDown(false) {}
698
699VirtualStylus::~VirtualStylus() {}
700
701bool VirtualStylus::writeMotionEvent(int32_t toolType, int32_t action, int32_t locationX,
702 int32_t locationY, int32_t pressure, int32_t tiltX,
703 int32_t tiltY, std::chrono::nanoseconds eventTime) {
704 auto actionIterator = VirtualTouchscreen::TOUCH_ACTION_MAPPING.find(action);
705 if (actionIterator == VirtualTouchscreen::TOUCH_ACTION_MAPPING.end()) {
706 ALOGE("Unsupported action passed for stylus: %d.", action);
707 return false;
708 }
709 UinputAction uinputAction = actionIterator->second;
710 auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType);
711 if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) {
712 ALOGE("Unsupported tool type passed for stylus: %d.", toolType);
713 return false;
714 }
715 uint16_t tool = static_cast<uint16_t>(toolTypeIterator->second);
716 if (uinputAction == UinputAction::PRESS && !handleStylusDown(tool, eventTime)) {
717 return false;
718 }
719 if (!mIsStylusDown) {
720 ALOGE("Action UP or MOVE received with no prior action DOWN for stylus %d.", mFd.get());
721 return false;
722 }
723 if (uinputAction == UinputAction::RELEASE && !handleStylusUp(tool, eventTime)) {
724 return false;
725 }
726 if (!writeInputEvent(EV_ABS, ABS_X, locationX, eventTime)) {
727 ALOGE("Unsupported x-axis location passed for stylus: %d.", locationX);
728 return false;
729 }
730 if (!writeInputEvent(EV_ABS, ABS_Y, locationY, eventTime)) {
731 ALOGE("Unsupported y-axis location passed for stylus: %d.", locationY);
732 return false;
733 }
734 if (!writeInputEvent(EV_ABS, ABS_TILT_X, tiltX, eventTime)) {
735 ALOGE("Unsupported x-axis tilt passed for stylus: %d.", tiltX);
736 return false;
737 }
738 if (!writeInputEvent(EV_ABS, ABS_TILT_Y, tiltY, eventTime)) {
739 ALOGE("Unsupported y-axis tilt passed for stylus: %d.", tiltY);
740 return false;
741 }
742 if (!writeInputEvent(EV_ABS, ABS_PRESSURE, pressure, eventTime)) {
743 ALOGE("Unsupported pressure passed for stylus: %d.", pressure);
744 return false;
745 }
746 if (!writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime)) {
747 ALOGE("Failed to write SYN_REPORT for stylus motion event.");
748 return false;
749 }
750 return true;
751}
752
753bool VirtualStylus::writeButtonEvent(int32_t androidButtonCode, int32_t androidAction,
754 std::chrono::nanoseconds eventTime) {
755 return writeEvKeyEvent(androidButtonCode, androidAction, BUTTON_CODE_MAPPING,
756 VirtualMouse::BUTTON_ACTION_MAPPING, eventTime);
757}
758
759bool VirtualStylus::handleStylusDown(uint16_t tool, std::chrono::nanoseconds eventTime) {
760 if (mIsStylusDown) {
761 ALOGE("Repetitive action DOWN event received for a stylus that is already down.");
762 return false;
763 }
764 if (!writeInputEvent(EV_KEY, tool, static_cast<int32_t>(UinputAction::PRESS), eventTime)) {
765 ALOGE("Failed to write EV_KEY for stylus tool type: %u.", tool);
766 return false;
767 }
768 if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS), eventTime)) {
769 ALOGE("Failed to write BTN_TOUCH for stylus press.");
770 return false;
771 }
772 mIsStylusDown = true;
773 return true;
774}
775
776bool VirtualStylus::handleStylusUp(uint16_t tool, std::chrono::nanoseconds eventTime) {
777 if (!writeInputEvent(EV_KEY, tool, static_cast<int32_t>(UinputAction::RELEASE), eventTime)) {
778 ALOGE("Failed to write EV_KEY for stylus tool type: %u.", tool);
779 return false;
780 }
781 if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE),
782 eventTime)) {
783 ALOGE("Failed to write BTN_TOUCH for stylus release.");
784 return false;
785 }
786 mIsStylusDown = false;
787 return true;
788}
789
Vladimir Komsiyskidd438e22024-02-13 11:47:54 +0100790// --- VirtualRotaryEncoder ---
Biswarup Palba27d1d2024-07-09 19:57:33 +0000791VirtualRotaryEncoder::VirtualRotaryEncoder(unique_fd fd)
792 : VirtualInputDevice(std::move(fd)), mAccumulatedHighResScrollAmount(0) {}
Vladimir Komsiyskidd438e22024-02-13 11:47:54 +0100793
794VirtualRotaryEncoder::~VirtualRotaryEncoder() {}
795
796bool VirtualRotaryEncoder::writeScrollEvent(float scrollAmount,
797 std::chrono::nanoseconds eventTime) {
Biswarup Palba27d1d2024-07-09 19:57:33 +0000798 if (!vd_flags::high_resolution_scroll()) {
799 return writeInputEvent(EV_REL, REL_WHEEL, static_cast<int32_t>(scrollAmount), eventTime) &&
800 writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
801 }
802
803 const auto highResScrollAmount =
804 static_cast<int32_t>(scrollAmount * kEvdevHighResScrollUnitsPerDetent);
805 if (!writeInputEvent(EV_REL, REL_WHEEL_HI_RES, highResScrollAmount, eventTime)) {
806 return false;
807 }
808
809 // According to evdev spec, a high-resolution scroll device needs to emit REL_WHEEL / REL_HWHEEL
810 // events in addition to high-res scroll events. Regular scroll events can approximate high-res
811 // scroll events, so we send a regular scroll event when the accumulated scroll motion reaches a
812 // detent (single wheel click).
813 mAccumulatedHighResScrollAmount += highResScrollAmount;
814 const int32_t scroll = mAccumulatedHighResScrollAmount / kEvdevHighResScrollUnitsPerDetent;
815 if (scroll != 0) {
816 if (!writeInputEvent(EV_REL, REL_WHEEL, scroll, eventTime)) {
817 return false;
818 }
819 mAccumulatedHighResScrollAmount %= kEvdevHighResScrollUnitsPerDetent;
820 }
821
822 return writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
Vladimir Komsiyskidd438e22024-02-13 11:47:54 +0100823}
824
Zixuan Qudd0635d2023-02-06 04:52:38 +0000825} // namespace android