blob: bcfdad3ef932c70a989a7bcd170f0d603fb0cef7 [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 Schoedelb2990032017-06-15 11:21:13 -040031static constexpr float kScrollScale = 100.0f;
32
Kevin Schoedeld8fccf02017-06-05 11:13:20 -040033int32_t scale_relative_scroll(float x) {
Kevin Schoedelb2990032017-06-15 11:21:13 -040034 return kScrollScale * x;
Kevin Schoedeld8fccf02017-06-05 11:13:20 -040035}
36
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080037} // anonymous namespace
38
Kevin Schoedelde1cdae2017-03-17 11:07:06 -040039std::unique_ptr<VirtualTouchpad> VirtualTouchpadEvdev::Create() {
40 std::unique_ptr<VirtualTouchpadEvdev> touchpad(new VirtualTouchpadEvdev());
Kevin Schoedel0108af72017-03-09 11:45:20 -050041 touchpad->Reset();
Kevin Schoedelde1cdae2017-03-17 11:07:06 -040042 return touchpad;
Kevin Schoedel89af70b2017-03-03 18:11:37 -050043}
44
Kevin Schoedel0108af72017-03-09 11:45:20 -050045void VirtualTouchpadEvdev::Reset() {
46 for (auto& touchpad : touchpad_) {
47 if (touchpad.injector) {
48 touchpad.injector->Close();
49 }
50 touchpad.injector = nullptr;
51 touchpad.owned_injector.reset();
52 touchpad.last_device_x = INT32_MIN;
53 touchpad.last_device_y = INT32_MIN;
54 touchpad.touches = 0;
55 touchpad.last_motion_event_buttons = 0;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080056 }
Kevin Schoedel0108af72017-03-09 11:45:20 -050057}
58
59status_t VirtualTouchpadEvdev::Attach() {
60 status_t status = OK;
61 for (int i = 0; i < kTouchpads; ++i) {
62 Touchpad& touchpad = touchpad_[i];
63 if (!touchpad.injector) {
64 touchpad.owned_injector.reset(new EvdevInjector());
65 touchpad.injector = touchpad.owned_injector.get();
66 }
67 String8 DeviceName;
68 DeviceName.appendFormat(kDeviceNameFormat, i);
69 touchpad.injector->ConfigureBegin(DeviceName, kDeviceBusType,
70 kDeviceVendor, kDeviceProduct,
71 kDeviceVersion);
72 touchpad.injector->ConfigureInputProperty(INPUT_PROP_DIRECT);
73 touchpad.injector->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1);
74 touchpad.injector->ConfigureAbsSlots(kSlots);
Kevin Schoedelb2990032017-06-15 11:21:13 -040075 touchpad.injector->ConfigureRel(REL_WHEEL);
76 touchpad.injector->ConfigureRel(REL_HWHEEL);
Kevin Schoedel0108af72017-03-09 11:45:20 -050077 touchpad.injector->ConfigureKey(BTN_TOUCH);
78 touchpad.injector->ConfigureKey(BTN_BACK);
79 touchpad.injector->ConfigureEnd();
80 if (const status_t configuration_status = touchpad.injector->GetError()) {
81 status = configuration_status;
82 }
83 }
84 return status;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080085}
86
Kevin Schoedel4b64dd42017-03-07 13:06:25 -050087status_t VirtualTouchpadEvdev::Detach() {
Kevin Schoedel0108af72017-03-09 11:45:20 -050088 Reset();
Kevin Schoedel4b64dd42017-03-07 13:06:25 -050089 return OK;
90}
91
Kevin Schoedel0108af72017-03-09 11:45:20 -050092int VirtualTouchpadEvdev::Touch(int touchpad_id, float x, float y,
Kevin Schoedel3002b8a2017-03-06 14:34:39 -050093 float pressure) {
Kevin Schoedel0108af72017-03-09 11:45:20 -050094 if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
95 return EINVAL;
96 }
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080097 int32_t device_x = x * kWidth;
98 int32_t device_y = y * kHeight;
Kevin Schoedel0108af72017-03-09 11:45:20 -050099 Touchpad& touchpad = touchpad_[touchpad_id];
100 touchpad.touches = ((touchpad.touches & 1) << 1) | (pressure > 0);
Kevin Schoedel3002b8a2017-03-06 14:34:39 -0500101 ALOGV("(%f,%f) %f -> (%" PRId32 ",%" PRId32 ") %d", x, y, pressure, device_x,
Kevin Schoedel0108af72017-03-09 11:45:20 -0500102 device_y, touchpad.touches);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800103
Kevin Schoedel0108af72017-03-09 11:45:20 -0500104 if (!touchpad.injector) {
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500105 return EvdevInjector::ERROR_SEQUENCING;
106 }
Kevin Schoedel0108af72017-03-09 11:45:20 -0500107 touchpad.injector->ResetError();
108 switch (touchpad.touches) {
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800109 case 0b00: // Hover continues.
Kevin Schoedel0108af72017-03-09 11:45:20 -0500110 if (device_x != touchpad.last_device_x ||
111 device_y != touchpad.last_device_y) {
112 touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
113 touchpad.injector->SendSynReport();
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800114 }
115 break;
116 case 0b01: // Touch begins.
117 // Press.
Kevin Schoedel0108af72017-03-09 11:45:20 -0500118 touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
119 touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS);
120 touchpad.injector->SendSynReport();
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800121 break;
122 case 0b10: // Touch ends.
Kevin Schoedel0108af72017-03-09 11:45:20 -0500123 touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE);
124 touchpad.injector->SendMultiTouchLift(0);
125 touchpad.injector->SendSynReport();
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800126 break;
127 case 0b11: // Touch continues.
Kevin Schoedel0108af72017-03-09 11:45:20 -0500128 if (device_x != touchpad.last_device_x ||
129 device_y != touchpad.last_device_y) {
130 touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
131 touchpad.injector->SendSynReport();
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800132 }
133 break;
134 }
Kevin Schoedel0108af72017-03-09 11:45:20 -0500135 touchpad.last_device_x = device_x;
136 touchpad.last_device_y = device_y;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800137
Kevin Schoedel0108af72017-03-09 11:45:20 -0500138 return touchpad.injector->GetError();
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800139}
140
Kevin Schoedel0108af72017-03-09 11:45:20 -0500141int VirtualTouchpadEvdev::ButtonState(int touchpad_id, int buttons) {
142 if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
143 return EINVAL;
144 }
145 Touchpad& touchpad = touchpad_[touchpad_id];
146 const int changes = touchpad.last_motion_event_buttons ^ buttons;
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500147 if (!changes) {
148 return 0;
149 }
150 if (buttons & ~AMOTION_EVENT_BUTTON_BACK) {
151 return ENOTSUP;
152 }
Kevin Schoedel0108af72017-03-09 11:45:20 -0500153 ALOGV("change %X from %X to %X", changes, touchpad.last_motion_event_buttons,
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500154 buttons);
155
Kevin Schoedel0108af72017-03-09 11:45:20 -0500156 if (!touchpad.injector) {
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500157 return EvdevInjector::ERROR_SEQUENCING;
158 }
Kevin Schoedel0108af72017-03-09 11:45:20 -0500159 touchpad.injector->ResetError();
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500160 if (changes & AMOTION_EVENT_BUTTON_BACK) {
Kevin Schoedel0108af72017-03-09 11:45:20 -0500161 touchpad.injector->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK)
162 ? EvdevInjector::KEY_PRESS
163 : EvdevInjector::KEY_RELEASE);
164 touchpad.injector->SendSynReport();
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500165 }
Kevin Schoedel0108af72017-03-09 11:45:20 -0500166 touchpad.last_motion_event_buttons = buttons;
167 return touchpad.injector->GetError();
Kevin Schoedel43b5b062017-01-19 13:46:17 -0500168}
169
Kevin Schoedeld8fccf02017-06-05 11:13:20 -0400170int VirtualTouchpadEvdev::Scroll(int touchpad_id, float x, float y) {
171 if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
172 return EINVAL;
173 }
174 if ((x < -1.0f) || (x > 1.0f) || (y < -1.0f) || (y > 1.0f)) {
175 return EINVAL;
176 }
177 Touchpad& touchpad = touchpad_[touchpad_id];
178 if (!touchpad.injector) {
179 return EvdevInjector::ERROR_SEQUENCING;
180 }
181 touchpad.injector->ResetError();
182 const int32_t scaled_x = scale_relative_scroll(x);
183 const int32_t scaled_y = scale_relative_scroll(y);
Kevin Schoedelb2990032017-06-15 11:21:13 -0400184 ALOGV("(%f,%f) -> (%" PRId32 ",%" PRId32 ")", x, y, scaled_x, scaled_y);
Kevin Schoedeld8fccf02017-06-05 11:13:20 -0400185 if (scaled_x) {
186 touchpad.injector->SendRel(REL_HWHEEL, scaled_x);
187 }
188 if (scaled_y) {
189 touchpad.injector->SendRel(REL_WHEEL, scaled_y);
190 }
191 if (scaled_x || scaled_y) {
192 touchpad.injector->SendSynReport();
193 }
194 return touchpad.injector->GetError();
195}
196
Kevin Schoedel4b64dd42017-03-07 13:06:25 -0500197void VirtualTouchpadEvdev::dumpInternal(String8& result) {
Kevin Schoedel0108af72017-03-09 11:45:20 -0500198 for (int i = 0; i < kTouchpads; ++i) {
199 const auto& touchpad = touchpad_[i];
200 result.appendFormat("[virtual touchpad %d]\n", i);
201 if (!touchpad.injector) {
202 result.append("injector = none\n");
203 return;
204 }
205 result.appendFormat("injector = %s\n",
206 touchpad.owned_injector ? "normal" : "test");
207 result.appendFormat("touches = %d\n", touchpad.touches);
208 result.appendFormat("last_position = (%" PRId32 ", %" PRId32 ")\n",
209 touchpad.last_device_x, touchpad.last_device_y);
210 result.appendFormat("last_buttons = 0x%" PRIX32 "\n",
211 touchpad.last_motion_event_buttons);
212 touchpad.injector->dumpInternal(result);
213 result.append("\n");
Kevin Schoedel4b64dd42017-03-07 13:06:25 -0500214 }
Kevin Schoedel4b64dd42017-03-07 13:06:25 -0500215}
216
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800217} // namespace dvr
218} // namespace android