#include "VirtualTouchpadEvdev.h" #include #include #include #include // References: // [0] Multi-touch (MT) Protocol, // https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt namespace android { namespace dvr { namespace { // Virtual evdev device properties. The name is arbitrary, but Android can // use it to look up device configuration, so it must be unique. Vendor and // product values must be 0 to indicate an internal device and prevent a // similar lookup that could conflict with a physical device. static const char* const kDeviceNameFormat = "vr-virtual-touchpad-%d"; static constexpr int16_t kDeviceBusType = BUS_VIRTUAL; static constexpr int16_t kDeviceVendor = 0; static constexpr int16_t kDeviceProduct = 0; static constexpr int16_t kDeviceVersion = 0x0001; static constexpr int32_t kWidth = 0x10000; static constexpr int32_t kHeight = 0x10000; static constexpr int32_t kSlots = 2; static constexpr float kScrollScale = 100.0f; int32_t scale_relative_scroll(float x) { return kScrollScale * x; } } // anonymous namespace std::unique_ptr VirtualTouchpadEvdev::Create() { std::unique_ptr touchpad(new VirtualTouchpadEvdev()); touchpad->Reset(); return touchpad; } void VirtualTouchpadEvdev::Reset() { for (auto& touchpad : touchpad_) { if (touchpad.injector) { touchpad.injector->Close(); } touchpad.injector = nullptr; touchpad.owned_injector.reset(); touchpad.last_device_x = INT32_MIN; touchpad.last_device_y = INT32_MIN; touchpad.touches = 0; touchpad.last_motion_event_buttons = 0; } } status_t VirtualTouchpadEvdev::Attach() { status_t status = OK; for (int i = 0; i < kTouchpads; ++i) { Touchpad& touchpad = touchpad_[i]; if (!touchpad.injector) { touchpad.owned_injector.reset(new EvdevInjector()); touchpad.injector = touchpad.owned_injector.get(); } String8 DeviceName; DeviceName.appendFormat(kDeviceNameFormat, i); touchpad.injector->ConfigureBegin(DeviceName, kDeviceBusType, kDeviceVendor, kDeviceProduct, kDeviceVersion); touchpad.injector->ConfigureInputProperty(INPUT_PROP_DIRECT); touchpad.injector->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1); touchpad.injector->ConfigureAbsSlots(kSlots); touchpad.injector->ConfigureRel(REL_WHEEL); touchpad.injector->ConfigureRel(REL_HWHEEL); touchpad.injector->ConfigureKey(BTN_TOUCH); touchpad.injector->ConfigureKey(BTN_BACK); touchpad.injector->ConfigureEnd(); if (const status_t configuration_status = touchpad.injector->GetError()) { status = configuration_status; } } return status; } status_t VirtualTouchpadEvdev::Detach() { Reset(); return OK; } int VirtualTouchpadEvdev::Touch(int touchpad_id, float x, float y, float pressure) { if (touchpad_id < 0 || touchpad_id >= kTouchpads) { return EINVAL; } int32_t device_x = x * kWidth; int32_t device_y = y * kHeight; Touchpad& touchpad = touchpad_[touchpad_id]; touchpad.touches = ((touchpad.touches & 1) << 1) | (pressure > 0); ALOGV("(%f,%f) %f -> (%" PRId32 ",%" PRId32 ") %d", x, y, pressure, device_x, device_y, touchpad.touches); if (!touchpad.injector) { return EvdevInjector::ERROR_SEQUENCING; } touchpad.injector->ResetError(); switch (touchpad.touches) { case 0b00: // Hover continues. if (device_x != touchpad.last_device_x || device_y != touchpad.last_device_y) { touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y); touchpad.injector->SendSynReport(); } break; case 0b01: // Touch begins. // Press. touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y); touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS); touchpad.injector->SendSynReport(); break; case 0b10: // Touch ends. touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE); touchpad.injector->SendMultiTouchLift(0); touchpad.injector->SendSynReport(); break; case 0b11: // Touch continues. if (device_x != touchpad.last_device_x || device_y != touchpad.last_device_y) { touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y); touchpad.injector->SendSynReport(); } break; } touchpad.last_device_x = device_x; touchpad.last_device_y = device_y; return touchpad.injector->GetError(); } int VirtualTouchpadEvdev::ButtonState(int touchpad_id, int buttons) { if (touchpad_id < 0 || touchpad_id >= kTouchpads) { return EINVAL; } Touchpad& touchpad = touchpad_[touchpad_id]; const int changes = touchpad.last_motion_event_buttons ^ buttons; if (!changes) { return 0; } if (buttons & ~AMOTION_EVENT_BUTTON_BACK) { return ENOTSUP; } ALOGV("change %X from %X to %X", changes, touchpad.last_motion_event_buttons, buttons); if (!touchpad.injector) { return EvdevInjector::ERROR_SEQUENCING; } touchpad.injector->ResetError(); if (changes & AMOTION_EVENT_BUTTON_BACK) { touchpad.injector->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK) ? EvdevInjector::KEY_PRESS : EvdevInjector::KEY_RELEASE); touchpad.injector->SendSynReport(); } touchpad.last_motion_event_buttons = buttons; return touchpad.injector->GetError(); } int VirtualTouchpadEvdev::Scroll(int touchpad_id, float x, float y) { if (touchpad_id < 0 || touchpad_id >= kTouchpads) { return EINVAL; } if ((x < -1.0f) || (x > 1.0f) || (y < -1.0f) || (y > 1.0f)) { return EINVAL; } Touchpad& touchpad = touchpad_[touchpad_id]; if (!touchpad.injector) { return EvdevInjector::ERROR_SEQUENCING; } touchpad.injector->ResetError(); const int32_t scaled_x = scale_relative_scroll(x); const int32_t scaled_y = scale_relative_scroll(y); ALOGV("(%f,%f) -> (%" PRId32 ",%" PRId32 ")", x, y, scaled_x, scaled_y); if (scaled_x) { touchpad.injector->SendRel(REL_HWHEEL, scaled_x); } if (scaled_y) { touchpad.injector->SendRel(REL_WHEEL, scaled_y); } if (scaled_x || scaled_y) { touchpad.injector->SendSynReport(); } return touchpad.injector->GetError(); } void VirtualTouchpadEvdev::dumpInternal(String8& result) { for (int i = 0; i < kTouchpads; ++i) { const auto& touchpad = touchpad_[i]; result.appendFormat("[virtual touchpad %d]\n", i); if (!touchpad.injector) { result.append("injector = none\n"); return; } result.appendFormat("injector = %s\n", touchpad.owned_injector ? "normal" : "test"); result.appendFormat("touches = %d\n", touchpad.touches); result.appendFormat("last_position = (%" PRId32 ", %" PRId32 ")\n", touchpad.last_device_x, touchpad.last_device_y); result.appendFormat("last_buttons = 0x%" PRIX32 "\n", touchpad.last_motion_event_buttons); touchpad.injector->dumpInternal(result); result.append("\n"); } } } // namespace dvr } // namespace android