blob: ae56bf6bf1e9fc11cf64542cf148c627d88155e8 [file] [log] [blame]
Kevin Schoedel89af70b2017-03-03 18:11:37 -05001#include "VirtualTouchpadEvdev.h"
Alex Vakulenkoe4eec202017-01-27 14:41:04 -08002
Kevin Schoedel43b5b062017-01-19 13:46:17 -05003#include <android/input.h>
Alex Vakulenkoe4eec202017-01-27 14:41:04 -08004#include <inttypes.h>
5#include <linux/input.h>
Alex Vakulenko4fe60582017-02-02 11:35:59 -08006#include <log/log.h>
Alex Vakulenkoe4eec202017-01-27 14:41:04 -08007
Kevin Schoedel43b5b062017-01-19 13:46:17 -05008// References:
9// [0] Multi-touch (MT) Protocol,
10// https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
11
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080012namespace android {
13namespace dvr {
14
15namespace {
16
Kevin Schoedel43b5b062017-01-19 13:46:17 -050017// Virtual evdev device properties. The name is arbitrary, but Android can
18// use it to look up device configuration, so it must be unique. Vendor and
19// product values must be 0 to indicate an internal device and prevent a
20// similar lookup that could conflict with a physical device.
Santos Cordonfa5cf462017-04-05 10:37:00 -070021static const char* const kDeviceNameFormat = "vr-virtual-touchpad-%d";
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080022static constexpr int16_t kDeviceBusType = BUS_VIRTUAL;
Kevin Schoedel43b5b062017-01-19 13:46:17 -050023static constexpr int16_t kDeviceVendor = 0;
24static constexpr int16_t kDeviceProduct = 0;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080025static constexpr int16_t kDeviceVersion = 0x0001;
Kevin Schoedel43b5b062017-01-19 13:46:17 -050026
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080027static constexpr int32_t kWidth = 0x10000;
28static constexpr int32_t kHeight = 0x10000;
29static constexpr int32_t kSlots = 2;
30
Kevin Schoedeld8fccf02017-06-05 11:13:20 -040031int32_t scale_relative_scroll(float x) {
32 // Guilty with an explanation, your honor.
33 // Ideally we should be able to communicate the full incoming precision
34 // to InputFlinger, through the evdev int32_t value, by scaling by a
35 // large factor, i.e. 2²³ for IEEE single precision floating point.
36 // However, although InputFlinger has |wheelVelocityControlParameters|,
37 // those parameters are currently hard coded, with a scale factor of 1.0.
38 // The observed evdev value for a physical mouse scroll wheel is usually
39 // ±1, with higher values up to ±4 for a very fast spin. So we imitate
40 // that. If the incoming value is not actually 0, the resulting magnitude
41 // should be at least 1, so that small movements are not lost.
42 // Adding IDC configurability of |VelocityControlParameters| may be
43 // desirable in the future.
44 return copysignf(ceilf(fabs(4.0f * x)), x);
45}
46
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080047} // anonymous namespace
48
Kevin Schoedelde1cdae2017-03-17 11:07:06 -040049std::unique_ptr<VirtualTouchpad> VirtualTouchpadEvdev::Create() {
50 std::unique_ptr<VirtualTouchpadEvdev> touchpad(new VirtualTouchpadEvdev());
Kevin Schoedel0108af72017-03-09 11:45:20 -050051 touchpad->Reset();
Kevin Schoedelde1cdae2017-03-17 11:07:06 -040052 return touchpad;
Kevin Schoedel89af70b2017-03-03 18:11:37 -050053}
54
Kevin Schoedel0108af72017-03-09 11:45:20 -050055void VirtualTouchpadEvdev::Reset() {
56 for (auto& touchpad : touchpad_) {
57 if (touchpad.injector) {
58 touchpad.injector->Close();
59 }
60 touchpad.injector = nullptr;
61 touchpad.owned_injector.reset();
62 touchpad.last_device_x = INT32_MIN;
63 touchpad.last_device_y = INT32_MIN;
64 touchpad.touches = 0;
65 touchpad.last_motion_event_buttons = 0;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080066 }
Kevin Schoedel0108af72017-03-09 11:45:20 -050067}
68
69status_t VirtualTouchpadEvdev::Attach() {
70 status_t status = OK;
71 for (int i = 0; i < kTouchpads; ++i) {
72 Touchpad& touchpad = touchpad_[i];
73 if (!touchpad.injector) {
74 touchpad.owned_injector.reset(new EvdevInjector());
75 touchpad.injector = touchpad.owned_injector.get();
76 }
77 String8 DeviceName;
78 DeviceName.appendFormat(kDeviceNameFormat, i);
79 touchpad.injector->ConfigureBegin(DeviceName, kDeviceBusType,
80 kDeviceVendor, kDeviceProduct,
81 kDeviceVersion);
82 touchpad.injector->ConfigureInputProperty(INPUT_PROP_DIRECT);
83 touchpad.injector->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1);
84 touchpad.injector->ConfigureAbsSlots(kSlots);
85 touchpad.injector->ConfigureKey(BTN_TOUCH);
86 touchpad.injector->ConfigureKey(BTN_BACK);
87 touchpad.injector->ConfigureEnd();
88 if (const status_t configuration_status = touchpad.injector->GetError()) {
89 status = configuration_status;
90 }
91 }
92 return status;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080093}
94
Kevin Schoedel4b64dd42017-03-07 13:06:25 -050095status_t VirtualTouchpadEvdev::Detach() {
Kevin Schoedel0108af72017-03-09 11:45:20 -050096 Reset();
Kevin Schoedel4b64dd42017-03-07 13:06:25 -050097 return OK;
98}
99
Kevin Schoedel0108af72017-03-09 11:45:20 -0500100int VirtualTouchpadEvdev::Touch(int touchpad_id, float x, float y,
Kevin Schoedel3002b8a2017-03-06 14:34:39 -0500101 float pressure) {
Kevin Schoedel0108af72017-03-09 11:45:20 -0500102 if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
103 return EINVAL;
104 }
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500105 if ((x < 0.0f) || (x >= 1.0f) || (y < 0.0f) || (y >= 1.0f)) {
106 return EINVAL;
107 }
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800108 int32_t device_x = x * kWidth;
109 int32_t device_y = y * kHeight;
Kevin Schoedel0108af72017-03-09 11:45:20 -0500110 Touchpad& touchpad = touchpad_[touchpad_id];
111 touchpad.touches = ((touchpad.touches & 1) << 1) | (pressure > 0);
Kevin Schoedel3002b8a2017-03-06 14:34:39 -0500112 ALOGV("(%f,%f) %f -> (%" PRId32 ",%" PRId32 ") %d", x, y, pressure, device_x,
Kevin Schoedel0108af72017-03-09 11:45:20 -0500113 device_y, touchpad.touches);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800114
Kevin Schoedel0108af72017-03-09 11:45:20 -0500115 if (!touchpad.injector) {
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500116 return EvdevInjector::ERROR_SEQUENCING;
117 }
Kevin Schoedel0108af72017-03-09 11:45:20 -0500118 touchpad.injector->ResetError();
119 switch (touchpad.touches) {
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800120 case 0b00: // Hover continues.
Kevin Schoedel0108af72017-03-09 11:45:20 -0500121 if (device_x != touchpad.last_device_x ||
122 device_y != touchpad.last_device_y) {
123 touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
124 touchpad.injector->SendSynReport();
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800125 }
126 break;
127 case 0b01: // Touch begins.
128 // Press.
Kevin Schoedel0108af72017-03-09 11:45:20 -0500129 touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
130 touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS);
131 touchpad.injector->SendSynReport();
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800132 break;
133 case 0b10: // Touch ends.
Kevin Schoedel0108af72017-03-09 11:45:20 -0500134 touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE);
135 touchpad.injector->SendMultiTouchLift(0);
136 touchpad.injector->SendSynReport();
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800137 break;
138 case 0b11: // Touch continues.
Kevin Schoedel0108af72017-03-09 11:45:20 -0500139 if (device_x != touchpad.last_device_x ||
140 device_y != touchpad.last_device_y) {
141 touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
142 touchpad.injector->SendSynReport();
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800143 }
144 break;
145 }
Kevin Schoedel0108af72017-03-09 11:45:20 -0500146 touchpad.last_device_x = device_x;
147 touchpad.last_device_y = device_y;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800148
Kevin Schoedel0108af72017-03-09 11:45:20 -0500149 return touchpad.injector->GetError();
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800150}
151
Kevin Schoedel0108af72017-03-09 11:45:20 -0500152int VirtualTouchpadEvdev::ButtonState(int touchpad_id, int buttons) {
153 if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
154 return EINVAL;
155 }
156 Touchpad& touchpad = touchpad_[touchpad_id];
157 const int changes = touchpad.last_motion_event_buttons ^ buttons;
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500158 if (!changes) {
159 return 0;
160 }
161 if (buttons & ~AMOTION_EVENT_BUTTON_BACK) {
162 return ENOTSUP;
163 }
Kevin Schoedel0108af72017-03-09 11:45:20 -0500164 ALOGV("change %X from %X to %X", changes, touchpad.last_motion_event_buttons,
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500165 buttons);
166
Kevin Schoedel0108af72017-03-09 11:45:20 -0500167 if (!touchpad.injector) {
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500168 return EvdevInjector::ERROR_SEQUENCING;
169 }
Kevin Schoedel0108af72017-03-09 11:45:20 -0500170 touchpad.injector->ResetError();
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500171 if (changes & AMOTION_EVENT_BUTTON_BACK) {
Kevin Schoedel0108af72017-03-09 11:45:20 -0500172 touchpad.injector->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK)
173 ? EvdevInjector::KEY_PRESS
174 : EvdevInjector::KEY_RELEASE);
175 touchpad.injector->SendSynReport();
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500176 }
Kevin Schoedel0108af72017-03-09 11:45:20 -0500177 touchpad.last_motion_event_buttons = buttons;
178 return touchpad.injector->GetError();
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500179}
180
Kevin Schoedeld8fccf02017-06-05 11:13:20 -0400181int VirtualTouchpadEvdev::Scroll(int touchpad_id, float x, float y) {
182 if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
183 return EINVAL;
184 }
185 if ((x < -1.0f) || (x > 1.0f) || (y < -1.0f) || (y > 1.0f)) {
186 return EINVAL;
187 }
188 Touchpad& touchpad = touchpad_[touchpad_id];
189 if (!touchpad.injector) {
190 return EvdevInjector::ERROR_SEQUENCING;
191 }
192 touchpad.injector->ResetError();
193 const int32_t scaled_x = scale_relative_scroll(x);
194 const int32_t scaled_y = scale_relative_scroll(y);
195 if (scaled_x) {
196 touchpad.injector->SendRel(REL_HWHEEL, scaled_x);
197 }
198 if (scaled_y) {
199 touchpad.injector->SendRel(REL_WHEEL, scaled_y);
200 }
201 if (scaled_x || scaled_y) {
202 touchpad.injector->SendSynReport();
203 }
204 return touchpad.injector->GetError();
205}
206
Kevin Schoedel4b64dd42017-03-07 13:06:25 -0500207void VirtualTouchpadEvdev::dumpInternal(String8& result) {
Kevin Schoedel0108af72017-03-09 11:45:20 -0500208 for (int i = 0; i < kTouchpads; ++i) {
209 const auto& touchpad = touchpad_[i];
210 result.appendFormat("[virtual touchpad %d]\n", i);
211 if (!touchpad.injector) {
212 result.append("injector = none\n");
213 return;
214 }
215 result.appendFormat("injector = %s\n",
216 touchpad.owned_injector ? "normal" : "test");
217 result.appendFormat("touches = %d\n", touchpad.touches);
218 result.appendFormat("last_position = (%" PRId32 ", %" PRId32 ")\n",
219 touchpad.last_device_x, touchpad.last_device_y);
220 result.appendFormat("last_buttons = 0x%" PRIX32 "\n",
221 touchpad.last_motion_event_buttons);
222 touchpad.injector->dumpInternal(result);
223 result.append("\n");
Kevin Schoedel4b64dd42017-03-07 13:06:25 -0500224 }
Kevin Schoedel4b64dd42017-03-07 13:06:25 -0500225}
226
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800227} // namespace dvr
228} // namespace android