1#include "VirtualTouchpadEvdev.h"
2
3#include <android/input.h>
4#include <inttypes.h>
5#include <linux/input.h>
6#include <log/log.h>
7
8// References:
9//  [0] Multi-touch (MT) Protocol,
10//      https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
11
12namespace android {
13namespace dvr {
14
15namespace {
16
17// 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.
21static const char* const kDeviceNameFormat = "vr-virtual-touchpad-%d";
22static constexpr int16_t kDeviceBusType = BUS_VIRTUAL;
23static constexpr int16_t kDeviceVendor = 0;
24static constexpr int16_t kDeviceProduct = 0;
25static constexpr int16_t kDeviceVersion = 0x0001;
26
27static constexpr int32_t kWidth = 0x10000;
28static constexpr int32_t kHeight = 0x10000;
29static constexpr int32_t kSlots = 2;
30
31static constexpr float kScrollScale = 100.0f;
32
33int32_t scale_relative_scroll(float x) {
34  return kScrollScale * x;
35}
36
37}  // anonymous namespace
38
39std::unique_ptr<VirtualTouchpad> VirtualTouchpadEvdev::Create() {
40  std::unique_ptr<VirtualTouchpadEvdev> touchpad(new VirtualTouchpadEvdev());
41  touchpad->Reset();
42  return touchpad;
43}
44
45void 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;
56  }
57}
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);
75    touchpad.injector->ConfigureRel(REL_WHEEL);
76    touchpad.injector->ConfigureRel(REL_HWHEEL);
77    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;
85}
86
87status_t VirtualTouchpadEvdev::Detach() {
88  Reset();
89  return OK;
90}
91
92int VirtualTouchpadEvdev::Touch(int touchpad_id, float x, float y,
93                                float pressure) {
94  if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
95    return EINVAL;
96  }
97  int32_t device_x = x * kWidth;
98  int32_t device_y = y * kHeight;
99  Touchpad& touchpad = touchpad_[touchpad_id];
100  touchpad.touches = ((touchpad.touches & 1) << 1) | (pressure > 0);
101  ALOGV("(%f,%f) %f -> (%" PRId32 ",%" PRId32 ") %d", x, y, pressure, device_x,
102        device_y, touchpad.touches);
103
104  if (!touchpad.injector) {
105    return EvdevInjector::ERROR_SEQUENCING;
106  }
107  touchpad.injector->ResetError();
108  switch (touchpad.touches) {
109    case 0b00:  // Hover continues.
110      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();
114      }
115      break;
116    case 0b01:  // Touch begins.
117      // Press.
118      touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
119      touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS);
120      touchpad.injector->SendSynReport();
121      break;
122    case 0b10:  // Touch ends.
123      touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE);
124      touchpad.injector->SendMultiTouchLift(0);
125      touchpad.injector->SendSynReport();
126      break;
127    case 0b11:  // Touch continues.
128      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();
132      }
133      break;
134  }
135  touchpad.last_device_x = device_x;
136  touchpad.last_device_y = device_y;
137
138  return touchpad.injector->GetError();
139}
140
141int 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;
147  if (!changes) {
148    return 0;
149  }
150  if (buttons & ~AMOTION_EVENT_BUTTON_BACK) {
151    return ENOTSUP;
152  }
153  ALOGV("change %X from %X to %X", changes, touchpad.last_motion_event_buttons,
154        buttons);
155
156  if (!touchpad.injector) {
157    return EvdevInjector::ERROR_SEQUENCING;
158  }
159  touchpad.injector->ResetError();
160  if (changes & AMOTION_EVENT_BUTTON_BACK) {
161    touchpad.injector->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK)
162                                             ? EvdevInjector::KEY_PRESS
163                                             : EvdevInjector::KEY_RELEASE);
164    touchpad.injector->SendSynReport();
165  }
166  touchpad.last_motion_event_buttons = buttons;
167  return touchpad.injector->GetError();
168}
169
170int 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);
184  ALOGV("(%f,%f) -> (%" PRId32 ",%" PRId32 ")", x, y, scaled_x, scaled_y);
185  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
197void VirtualTouchpadEvdev::dumpInternal(String8& result) {
198  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");
214  }
215}
216
217}  // namespace dvr
218}  // namespace android
219