VirtualTouchpadEvdev.cpp revision 0108af72a8d8d2ee2af127b4c099b340ad63e3f8
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
31}  // anonymous namespace
32
33sp<VirtualTouchpad> VirtualTouchpadEvdev::Create() {
34  VirtualTouchpadEvdev* const touchpad = new VirtualTouchpadEvdev();
35  touchpad->Reset();
36  return sp<VirtualTouchpad>(touchpad);
37}
38
39void VirtualTouchpadEvdev::Reset() {
40  for (auto& touchpad : touchpad_) {
41    if (touchpad.injector) {
42      touchpad.injector->Close();
43    }
44    touchpad.injector = nullptr;
45    touchpad.owned_injector.reset();
46    touchpad.last_device_x = INT32_MIN;
47    touchpad.last_device_y = INT32_MIN;
48    touchpad.touches = 0;
49    touchpad.last_motion_event_buttons = 0;
50  }
51}
52
53status_t VirtualTouchpadEvdev::Attach() {
54  status_t status = OK;
55  for (int i = 0; i < kTouchpads; ++i) {
56    Touchpad& touchpad = touchpad_[i];
57    if (!touchpad.injector) {
58      touchpad.owned_injector.reset(new EvdevInjector());
59      touchpad.injector = touchpad.owned_injector.get();
60    }
61    String8 DeviceName;
62    DeviceName.appendFormat(kDeviceNameFormat, i);
63    touchpad.injector->ConfigureBegin(DeviceName, kDeviceBusType,
64                                      kDeviceVendor, kDeviceProduct,
65                                      kDeviceVersion);
66    touchpad.injector->ConfigureInputProperty(INPUT_PROP_DIRECT);
67    touchpad.injector->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1);
68    touchpad.injector->ConfigureAbsSlots(kSlots);
69    touchpad.injector->ConfigureKey(BTN_TOUCH);
70    touchpad.injector->ConfigureKey(BTN_BACK);
71    touchpad.injector->ConfigureEnd();
72    if (const status_t configuration_status =  touchpad.injector->GetError()) {
73      status = configuration_status;
74    }
75  }
76  return status;
77}
78
79status_t VirtualTouchpadEvdev::Detach() {
80  Reset();
81  return OK;
82}
83
84int VirtualTouchpadEvdev::Touch(int touchpad_id, float x, float y,
85                                float pressure) {
86  if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
87    return EINVAL;
88  }
89  if ((x < 0.0f) || (x >= 1.0f) || (y < 0.0f) || (y >= 1.0f)) {
90    return EINVAL;
91  }
92  int32_t device_x = x * kWidth;
93  int32_t device_y = y * kHeight;
94  Touchpad& touchpad = touchpad_[touchpad_id];
95  touchpad.touches = ((touchpad.touches & 1) << 1) | (pressure > 0);
96  ALOGV("(%f,%f) %f -> (%" PRId32 ",%" PRId32 ") %d", x, y, pressure, device_x,
97        device_y, touchpad.touches);
98
99  if (!touchpad.injector) {
100    return EvdevInjector::ERROR_SEQUENCING;
101  }
102  touchpad.injector->ResetError();
103  switch (touchpad.touches) {
104    case 0b00:  // Hover continues.
105      if (device_x != touchpad.last_device_x ||
106          device_y != touchpad.last_device_y) {
107        touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
108        touchpad.injector->SendSynReport();
109      }
110      break;
111    case 0b01:  // Touch begins.
112      // Press.
113      touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
114      touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS);
115      touchpad.injector->SendSynReport();
116      break;
117    case 0b10:  // Touch ends.
118      touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE);
119      touchpad.injector->SendMultiTouchLift(0);
120      touchpad.injector->SendSynReport();
121      break;
122    case 0b11:  // Touch continues.
123      if (device_x != touchpad.last_device_x ||
124          device_y != touchpad.last_device_y) {
125        touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
126        touchpad.injector->SendSynReport();
127      }
128      break;
129  }
130  touchpad.last_device_x = device_x;
131  touchpad.last_device_y = device_y;
132
133  return touchpad.injector->GetError();
134}
135
136int VirtualTouchpadEvdev::ButtonState(int touchpad_id, int buttons) {
137  if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
138    return EINVAL;
139  }
140  Touchpad& touchpad = touchpad_[touchpad_id];
141  const int changes = touchpad.last_motion_event_buttons ^ buttons;
142  if (!changes) {
143    return 0;
144  }
145  if (buttons & ~AMOTION_EVENT_BUTTON_BACK) {
146    return ENOTSUP;
147  }
148  ALOGV("change %X from %X to %X", changes, touchpad.last_motion_event_buttons,
149        buttons);
150
151  if (!touchpad.injector) {
152    return EvdevInjector::ERROR_SEQUENCING;
153  }
154  touchpad.injector->ResetError();
155  if (changes & AMOTION_EVENT_BUTTON_BACK) {
156    touchpad.injector->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK)
157                                             ? EvdevInjector::KEY_PRESS
158                                             : EvdevInjector::KEY_RELEASE);
159    touchpad.injector->SendSynReport();
160  }
161  touchpad.last_motion_event_buttons = buttons;
162  return touchpad.injector->GetError();
163}
164
165void VirtualTouchpadEvdev::dumpInternal(String8& result) {
166  for (int i = 0; i < kTouchpads; ++i) {
167    const auto& touchpad = touchpad_[i];
168    result.appendFormat("[virtual touchpad %d]\n", i);
169    if (!touchpad.injector) {
170      result.append("injector = none\n");
171      return;
172    }
173    result.appendFormat("injector = %s\n",
174                        touchpad.owned_injector ? "normal" : "test");
175    result.appendFormat("touches = %d\n", touchpad.touches);
176    result.appendFormat("last_position = (%" PRId32 ", %" PRId32 ")\n",
177                        touchpad.last_device_x, touchpad.last_device_y);
178    result.appendFormat("last_buttons = 0x%" PRIX32 "\n",
179                        touchpad.last_motion_event_buttons);
180    touchpad.injector->dumpInternal(result);
181    result.append("\n");
182  }
183}
184
185}  // namespace dvr
186}  // namespace android
187