Loading services/vr/virtual_touchpad/EvdevInjector.cpp +2 −3 Original line number Diff line number Diff line Loading @@ -308,9 +308,8 @@ int EvdevInjector::EnableEventType(uint16_t type) { } void EvdevInjector::dumpInternal(String8& result) { result.append("[injector]\n"); result.appendFormat("state = %d\n", static_cast<int>(state_)); result.appendFormat("error = %d\n\n", error_); result.appendFormat("injector_state = %d\n", static_cast<int>(state_)); result.appendFormat("injector_error = %d\n", error_); } } // namespace dvr Loading services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp +97 −65 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ namespace { // 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 kDeviceName = "vr window manager virtual touchpad"; 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; Loading @@ -32,122 +32,154 @@ static constexpr int32_t kSlots = 2; sp<VirtualTouchpad> VirtualTouchpadEvdev::Create() { VirtualTouchpadEvdev* const touchpad = new VirtualTouchpadEvdev(); touchpad->Reset(); return sp<VirtualTouchpad>(touchpad); } status_t VirtualTouchpadEvdev::Attach() { if (!injector_) { owned_injector_.reset(new EvdevInjector()); injector_ = owned_injector_.get(); 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; } } injector_->ConfigureBegin(kDeviceName, kDeviceBusType, kDeviceVendor, kDeviceProduct, kDeviceVersion); injector_->ConfigureInputProperty(INPUT_PROP_DIRECT); injector_->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1); injector_->ConfigureAbsSlots(kSlots); injector_->ConfigureKey(BTN_TOUCH); injector_->ConfigureKey(BTN_BACK); injector_->ConfigureEnd(); return injector_->GetError(); 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->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() { injector_->Close(); injector_ = nullptr; owned_injector_.reset(); last_device_x_ = INT32_MIN; last_device_y_ = INT32_MIN; touches_ = 0; last_motion_event_buttons_ = 0; Reset(); return OK; } int VirtualTouchpadEvdev::Touch(int touchpad, float x, float y, int VirtualTouchpadEvdev::Touch(int touchpad_id, float x, float y, float pressure) { (void)touchpad; // TODO(b/35992608) Support multiple touchpad devices. if (touchpad_id < 0 || touchpad_id >= kTouchpads) { return EINVAL; } if ((x < 0.0f) || (x >= 1.0f) || (y < 0.0f) || (y >= 1.0f)) { return EINVAL; } int32_t device_x = x * kWidth; int32_t device_y = y * kHeight; touches_ = ((touches_ & 1) << 1) | (pressure > 0); 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, touches_); device_y, touchpad.touches); if (!injector_) { if (!touchpad.injector) { return EvdevInjector::ERROR_SEQUENCING; } injector_->ResetError(); switch (touches_) { touchpad.injector->ResetError(); switch (touchpad.touches) { case 0b00: // Hover continues. if (device_x != last_device_x_ || device_y != last_device_y_) { injector_->SendMultiTouchXY(0, 0, device_x, device_y); injector_->SendSynReport(); 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. injector_->SendMultiTouchXY(0, 0, device_x, device_y); injector_->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS); injector_->SendSynReport(); 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. injector_->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE); injector_->SendMultiTouchLift(0); injector_->SendSynReport(); touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE); touchpad.injector->SendMultiTouchLift(0); touchpad.injector->SendSynReport(); break; case 0b11: // Touch continues. if (device_x != last_device_x_ || device_y != last_device_y_) { injector_->SendMultiTouchXY(0, 0, device_x, device_y); injector_->SendSynReport(); 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; } last_device_x_ = device_x; last_device_y_ = device_y; touchpad.last_device_x = device_x; touchpad.last_device_y = device_y; return injector_->GetError(); return touchpad.injector->GetError(); } int VirtualTouchpadEvdev::ButtonState(int touchpad, int buttons) { (void)touchpad; // TODO(b/35992608) Support multiple touchpad devices. const int changes = last_motion_event_buttons_ ^ buttons; 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, last_motion_event_buttons_, ALOGV("change %X from %X to %X", changes, touchpad.last_motion_event_buttons, buttons); if (!injector_) { if (!touchpad.injector) { return EvdevInjector::ERROR_SEQUENCING; } injector_->ResetError(); touchpad.injector->ResetError(); if (changes & AMOTION_EVENT_BUTTON_BACK) { injector_->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK) touchpad.injector->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK) ? EvdevInjector::KEY_PRESS : EvdevInjector::KEY_RELEASE); injector_->SendSynReport(); touchpad.injector->SendSynReport(); } last_motion_event_buttons_ = buttons; return injector_->GetError(); touchpad.last_motion_event_buttons = buttons; return touchpad.injector->GetError(); } void VirtualTouchpadEvdev::dumpInternal(String8& result) { result.append("[virtual touchpad]\n"); if (!injector_) { 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", owned_injector_ ? "normal" : "test"); result.appendFormat("touches = %d\n", touches_); result.appendFormat("injector = %s\n", touchpad.owned_injector ? "normal" : "test"); result.appendFormat("touches = %d\n", touchpad.touches); result.appendFormat("last_position = (%" PRId32 ", %" PRId32 ")\n", last_device_x_, last_device_y_); result.appendFormat("last_buttons = 0x%" PRIX32 "\n\n", last_motion_event_buttons_); injector_->dumpInternal(result); 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 Loading services/vr/virtual_touchpad/VirtualTouchpadEvdev.h +28 −20 Original line number Diff line number Diff line Loading @@ -3,8 +3,8 @@ #include <memory> #include "VirtualTouchpad.h" #include "EvdevInjector.h" #include "VirtualTouchpad.h" namespace android { namespace dvr { Loading @@ -25,31 +25,39 @@ class VirtualTouchpadEvdev : public VirtualTouchpad { void dumpInternal(String8& result) override; protected: static constexpr int kTouchpads = 2; VirtualTouchpadEvdev() {} ~VirtualTouchpadEvdev() override {} void Reset(); // Must be called only between construction and Attach(). inline void SetEvdevInjectorForTesting(EvdevInjector* injector) { injector_ = injector; // Must be called only between construction (or Detach()) and Attach(). inline void SetEvdevInjectorForTesting(int touchpad, EvdevInjector* injector) { touchpad_[touchpad].injector = injector; } private: // Per-touchpad state. struct Touchpad { // Except for testing, the |EvdevInjector| used to inject evdev events. std::unique_ptr<EvdevInjector> owned_injector_; std::unique_ptr<EvdevInjector> owned_injector; // Active pointer to |owned_injector_| or to a testing injector. EvdevInjector* injector_ = nullptr; EvdevInjector* injector = nullptr; // Previous (x, y) position in device space, to suppress redundant events. int32_t last_device_x_ = INT32_MIN; int32_t last_device_y_ = INT32_MIN; int32_t last_device_x; int32_t last_device_y; // Records current touch state (0=up 1=down) in bit 0, and previous state // in bit 1, to track transitions. int touches_ = 0; int touches; // Previous injected button state, to detect changes. int32_t last_motion_event_buttons_ = 0; int32_t last_motion_event_buttons; }; Touchpad touchpad_[kTouchpads]; VirtualTouchpadEvdev(const VirtualTouchpadEvdev&) = delete; void operator=(const VirtualTouchpadEvdev&) = delete; Loading services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp +119 −78 Original line number Diff line number Diff line Loading @@ -88,17 +88,24 @@ class UInputRecorder : public UInputForTesting { class EvdevInjectorForTesting : public EvdevInjector { public: EvdevInjectorForTesting(UInput& uinput) { SetUInputForTesting(&uinput); } EvdevInjectorForTesting() { SetUInputForTesting(&record); } const uinput_user_dev* GetUiDev() const { return GetUiDevForTesting(); } UInputRecorder record; }; class VirtualTouchpadForTesting : public VirtualTouchpadEvdev { public: static sp<VirtualTouchpad> Create(EvdevInjectorForTesting& injector) { static sp<VirtualTouchpad> Create() { return sp<VirtualTouchpad>(New()); } static VirtualTouchpadForTesting* New() { VirtualTouchpadForTesting* const touchpad = new VirtualTouchpadForTesting(); touchpad->SetEvdevInjectorForTesting(&injector); return sp<VirtualTouchpad>(touchpad); touchpad->Reset(); for (int t = 0; t < kTouchpads; ++t) { touchpad->SetEvdevInjectorForTesting(t, &touchpad->injector[t]); } return touchpad; } int GetTouchpadCount() const { return kTouchpads; } EvdevInjectorForTesting injector[kTouchpads]; }; void DumpDifference(const char* expect, const char* actual) { Loading @@ -117,29 +124,38 @@ void DumpDifference(const char* expect, const char* actual) { class VirtualTouchpadTest : public testing::Test {}; TEST_F(VirtualTouchpadTest, Goodness) { sp<VirtualTouchpadForTesting> touchpad(VirtualTouchpadForTesting::New()); UInputRecorder expect; UInputRecorder record; EvdevInjectorForTesting injector(record); sp<VirtualTouchpad> touchpad(VirtualTouchpadForTesting::Create(injector)); status_t touch_status = touchpad->Attach(); EXPECT_EQ(0, touch_status); // Check some aspects of uinput_user_dev. const uinput_user_dev* uidev = injector.GetUiDev(); const uinput_user_dev* uidev; for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); uidev = touchpad->injector[t].GetUiDev(); String8 name; name.appendFormat("vr virtual touchpad %d", t); EXPECT_EQ(name, uidev->name); for (int i = 0; i < ABS_CNT; ++i) { EXPECT_EQ(0, uidev->absmin[i]); EXPECT_EQ(0, uidev->absfuzz[i]); EXPECT_EQ(0, uidev->absflat[i]); if (i != ABS_MT_POSITION_X && i != ABS_MT_POSITION_Y && i != ABS_MT_SLOT) { if (i != ABS_MT_POSITION_X && i != ABS_MT_POSITION_Y && i != ABS_MT_SLOT) { EXPECT_EQ(0, uidev->absmax[i]); } } } const int32_t width = 1 + uidev->absmax[ABS_MT_POSITION_X]; const int32_t height = 1 + uidev->absmax[ABS_MT_POSITION_Y]; const int32_t slots = uidev->absmax[ABS_MT_SLOT]; for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); // Check the system calls performed by initialization. expect.Reset(); // From ConfigureBegin(): expect.Open(); // From ConfigureInputProperty(INPUT_PROP_DIRECT): Loading @@ -155,95 +171,122 @@ TEST_F(VirtualTouchpadTest, Goodness) { expect.IoctlSetInt(UI_SET_KEYBIT, BTN_TOUCH); expect.IoctlSetInt(UI_SET_KEYBIT, BTN_BACK); // From ConfigureEnd(): expect.Write(uidev, sizeof(uinput_user_dev)); expect.Write(touchpad->injector[t].GetUiDev(), sizeof(uinput_user_dev)); expect.IoctlVoid(UI_DEV_CREATE); EXPECT_EQ(expect.GetString(), record.GetString()); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0, 0, 0); EXPECT_EQ(0, touch_status); expect.WriteInputEvent(EV_ABS, ABS_MT_SLOT, 0); expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0); expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0); expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0); expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->Touch(t, 0, 0, 0); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 0.75f, 0.5f); EXPECT_EQ(0, touch_status); expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0); expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.25f * width); expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.75f * height); expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_PRESS); expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->Touch(t, 0.25f, 0.75f, 0.5f); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.99f, 0.99f, 0.99f); EXPECT_EQ(0, touch_status); expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0); expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.99f * width); expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.99f * height); expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->Touch(t, 0.99f, 0.99f, 0.99f); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.0f, 1.0f, 1.0f); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->Touch(t, 1.0f, 1.0f, 1.0f); EXPECT_EQ(EINVAL, touch_status); EXPECT_EQ(expect.GetString(), record.GetString()); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 0.75f, -0.01f); EXPECT_EQ(0, touch_status); expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_RELEASE); expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, -1); expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->Touch(t, 0.25f, 0.75f, -0.01f); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY, AMOTION_EVENT_BUTTON_BACK); EXPECT_EQ(0, touch_status); expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_PRESS); expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY, AMOTION_EVENT_BUTTON_BACK); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), record.GetString()); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY, 0); EXPECT_EQ(0, touch_status); expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_RELEASE); expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->ButtonState(t, 0); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); expect.Close(); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); } touch_status = touchpad->Detach(); EXPECT_EQ(0, touch_status); expect.Close(); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } } TEST_F(VirtualTouchpadTest, Badness) { sp<VirtualTouchpadForTesting> touchpad(VirtualTouchpadForTesting::New()); UInputRecorder expect; UInputRecorder record; EvdevInjectorForTesting injector(record); sp<VirtualTouchpad> touchpad(VirtualTouchpadForTesting::Create(injector)); UInputRecorder& record = touchpad->injector[VirtualTouchpad::PRIMARY].record; status_t touch_status = touchpad->Attach(); EXPECT_EQ(0, touch_status); Loading @@ -252,11 +295,9 @@ TEST_F(VirtualTouchpadTest, Badness) { // and should not result in any system calls. expect.Reset(); record.Reset(); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, -0.25f, 0.75f, 1.0f); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, -0.25f, 0.75f, 1.0f); EXPECT_NE(OK, touch_status); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, -0.75f, 1.0f); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, -0.75f, 1.0f); EXPECT_NE(OK, touch_status); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.25f, 0.75f, 1.0f); EXPECT_NE(OK, touch_status); Loading Loading
services/vr/virtual_touchpad/EvdevInjector.cpp +2 −3 Original line number Diff line number Diff line Loading @@ -308,9 +308,8 @@ int EvdevInjector::EnableEventType(uint16_t type) { } void EvdevInjector::dumpInternal(String8& result) { result.append("[injector]\n"); result.appendFormat("state = %d\n", static_cast<int>(state_)); result.appendFormat("error = %d\n\n", error_); result.appendFormat("injector_state = %d\n", static_cast<int>(state_)); result.appendFormat("injector_error = %d\n", error_); } } // namespace dvr Loading
services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp +97 −65 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ namespace { // 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 kDeviceName = "vr window manager virtual touchpad"; 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; Loading @@ -32,122 +32,154 @@ static constexpr int32_t kSlots = 2; sp<VirtualTouchpad> VirtualTouchpadEvdev::Create() { VirtualTouchpadEvdev* const touchpad = new VirtualTouchpadEvdev(); touchpad->Reset(); return sp<VirtualTouchpad>(touchpad); } status_t VirtualTouchpadEvdev::Attach() { if (!injector_) { owned_injector_.reset(new EvdevInjector()); injector_ = owned_injector_.get(); 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; } } injector_->ConfigureBegin(kDeviceName, kDeviceBusType, kDeviceVendor, kDeviceProduct, kDeviceVersion); injector_->ConfigureInputProperty(INPUT_PROP_DIRECT); injector_->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1); injector_->ConfigureAbsSlots(kSlots); injector_->ConfigureKey(BTN_TOUCH); injector_->ConfigureKey(BTN_BACK); injector_->ConfigureEnd(); return injector_->GetError(); 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->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() { injector_->Close(); injector_ = nullptr; owned_injector_.reset(); last_device_x_ = INT32_MIN; last_device_y_ = INT32_MIN; touches_ = 0; last_motion_event_buttons_ = 0; Reset(); return OK; } int VirtualTouchpadEvdev::Touch(int touchpad, float x, float y, int VirtualTouchpadEvdev::Touch(int touchpad_id, float x, float y, float pressure) { (void)touchpad; // TODO(b/35992608) Support multiple touchpad devices. if (touchpad_id < 0 || touchpad_id >= kTouchpads) { return EINVAL; } if ((x < 0.0f) || (x >= 1.0f) || (y < 0.0f) || (y >= 1.0f)) { return EINVAL; } int32_t device_x = x * kWidth; int32_t device_y = y * kHeight; touches_ = ((touches_ & 1) << 1) | (pressure > 0); 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, touches_); device_y, touchpad.touches); if (!injector_) { if (!touchpad.injector) { return EvdevInjector::ERROR_SEQUENCING; } injector_->ResetError(); switch (touches_) { touchpad.injector->ResetError(); switch (touchpad.touches) { case 0b00: // Hover continues. if (device_x != last_device_x_ || device_y != last_device_y_) { injector_->SendMultiTouchXY(0, 0, device_x, device_y); injector_->SendSynReport(); 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. injector_->SendMultiTouchXY(0, 0, device_x, device_y); injector_->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS); injector_->SendSynReport(); 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. injector_->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE); injector_->SendMultiTouchLift(0); injector_->SendSynReport(); touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE); touchpad.injector->SendMultiTouchLift(0); touchpad.injector->SendSynReport(); break; case 0b11: // Touch continues. if (device_x != last_device_x_ || device_y != last_device_y_) { injector_->SendMultiTouchXY(0, 0, device_x, device_y); injector_->SendSynReport(); 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; } last_device_x_ = device_x; last_device_y_ = device_y; touchpad.last_device_x = device_x; touchpad.last_device_y = device_y; return injector_->GetError(); return touchpad.injector->GetError(); } int VirtualTouchpadEvdev::ButtonState(int touchpad, int buttons) { (void)touchpad; // TODO(b/35992608) Support multiple touchpad devices. const int changes = last_motion_event_buttons_ ^ buttons; 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, last_motion_event_buttons_, ALOGV("change %X from %X to %X", changes, touchpad.last_motion_event_buttons, buttons); if (!injector_) { if (!touchpad.injector) { return EvdevInjector::ERROR_SEQUENCING; } injector_->ResetError(); touchpad.injector->ResetError(); if (changes & AMOTION_EVENT_BUTTON_BACK) { injector_->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK) touchpad.injector->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK) ? EvdevInjector::KEY_PRESS : EvdevInjector::KEY_RELEASE); injector_->SendSynReport(); touchpad.injector->SendSynReport(); } last_motion_event_buttons_ = buttons; return injector_->GetError(); touchpad.last_motion_event_buttons = buttons; return touchpad.injector->GetError(); } void VirtualTouchpadEvdev::dumpInternal(String8& result) { result.append("[virtual touchpad]\n"); if (!injector_) { 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", owned_injector_ ? "normal" : "test"); result.appendFormat("touches = %d\n", touches_); result.appendFormat("injector = %s\n", touchpad.owned_injector ? "normal" : "test"); result.appendFormat("touches = %d\n", touchpad.touches); result.appendFormat("last_position = (%" PRId32 ", %" PRId32 ")\n", last_device_x_, last_device_y_); result.appendFormat("last_buttons = 0x%" PRIX32 "\n\n", last_motion_event_buttons_); injector_->dumpInternal(result); 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 Loading
services/vr/virtual_touchpad/VirtualTouchpadEvdev.h +28 −20 Original line number Diff line number Diff line Loading @@ -3,8 +3,8 @@ #include <memory> #include "VirtualTouchpad.h" #include "EvdevInjector.h" #include "VirtualTouchpad.h" namespace android { namespace dvr { Loading @@ -25,31 +25,39 @@ class VirtualTouchpadEvdev : public VirtualTouchpad { void dumpInternal(String8& result) override; protected: static constexpr int kTouchpads = 2; VirtualTouchpadEvdev() {} ~VirtualTouchpadEvdev() override {} void Reset(); // Must be called only between construction and Attach(). inline void SetEvdevInjectorForTesting(EvdevInjector* injector) { injector_ = injector; // Must be called only between construction (or Detach()) and Attach(). inline void SetEvdevInjectorForTesting(int touchpad, EvdevInjector* injector) { touchpad_[touchpad].injector = injector; } private: // Per-touchpad state. struct Touchpad { // Except for testing, the |EvdevInjector| used to inject evdev events. std::unique_ptr<EvdevInjector> owned_injector_; std::unique_ptr<EvdevInjector> owned_injector; // Active pointer to |owned_injector_| or to a testing injector. EvdevInjector* injector_ = nullptr; EvdevInjector* injector = nullptr; // Previous (x, y) position in device space, to suppress redundant events. int32_t last_device_x_ = INT32_MIN; int32_t last_device_y_ = INT32_MIN; int32_t last_device_x; int32_t last_device_y; // Records current touch state (0=up 1=down) in bit 0, and previous state // in bit 1, to track transitions. int touches_ = 0; int touches; // Previous injected button state, to detect changes. int32_t last_motion_event_buttons_ = 0; int32_t last_motion_event_buttons; }; Touchpad touchpad_[kTouchpads]; VirtualTouchpadEvdev(const VirtualTouchpadEvdev&) = delete; void operator=(const VirtualTouchpadEvdev&) = delete; Loading
services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp +119 −78 Original line number Diff line number Diff line Loading @@ -88,17 +88,24 @@ class UInputRecorder : public UInputForTesting { class EvdevInjectorForTesting : public EvdevInjector { public: EvdevInjectorForTesting(UInput& uinput) { SetUInputForTesting(&uinput); } EvdevInjectorForTesting() { SetUInputForTesting(&record); } const uinput_user_dev* GetUiDev() const { return GetUiDevForTesting(); } UInputRecorder record; }; class VirtualTouchpadForTesting : public VirtualTouchpadEvdev { public: static sp<VirtualTouchpad> Create(EvdevInjectorForTesting& injector) { static sp<VirtualTouchpad> Create() { return sp<VirtualTouchpad>(New()); } static VirtualTouchpadForTesting* New() { VirtualTouchpadForTesting* const touchpad = new VirtualTouchpadForTesting(); touchpad->SetEvdevInjectorForTesting(&injector); return sp<VirtualTouchpad>(touchpad); touchpad->Reset(); for (int t = 0; t < kTouchpads; ++t) { touchpad->SetEvdevInjectorForTesting(t, &touchpad->injector[t]); } return touchpad; } int GetTouchpadCount() const { return kTouchpads; } EvdevInjectorForTesting injector[kTouchpads]; }; void DumpDifference(const char* expect, const char* actual) { Loading @@ -117,29 +124,38 @@ void DumpDifference(const char* expect, const char* actual) { class VirtualTouchpadTest : public testing::Test {}; TEST_F(VirtualTouchpadTest, Goodness) { sp<VirtualTouchpadForTesting> touchpad(VirtualTouchpadForTesting::New()); UInputRecorder expect; UInputRecorder record; EvdevInjectorForTesting injector(record); sp<VirtualTouchpad> touchpad(VirtualTouchpadForTesting::Create(injector)); status_t touch_status = touchpad->Attach(); EXPECT_EQ(0, touch_status); // Check some aspects of uinput_user_dev. const uinput_user_dev* uidev = injector.GetUiDev(); const uinput_user_dev* uidev; for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); uidev = touchpad->injector[t].GetUiDev(); String8 name; name.appendFormat("vr virtual touchpad %d", t); EXPECT_EQ(name, uidev->name); for (int i = 0; i < ABS_CNT; ++i) { EXPECT_EQ(0, uidev->absmin[i]); EXPECT_EQ(0, uidev->absfuzz[i]); EXPECT_EQ(0, uidev->absflat[i]); if (i != ABS_MT_POSITION_X && i != ABS_MT_POSITION_Y && i != ABS_MT_SLOT) { if (i != ABS_MT_POSITION_X && i != ABS_MT_POSITION_Y && i != ABS_MT_SLOT) { EXPECT_EQ(0, uidev->absmax[i]); } } } const int32_t width = 1 + uidev->absmax[ABS_MT_POSITION_X]; const int32_t height = 1 + uidev->absmax[ABS_MT_POSITION_Y]; const int32_t slots = uidev->absmax[ABS_MT_SLOT]; for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); // Check the system calls performed by initialization. expect.Reset(); // From ConfigureBegin(): expect.Open(); // From ConfigureInputProperty(INPUT_PROP_DIRECT): Loading @@ -155,95 +171,122 @@ TEST_F(VirtualTouchpadTest, Goodness) { expect.IoctlSetInt(UI_SET_KEYBIT, BTN_TOUCH); expect.IoctlSetInt(UI_SET_KEYBIT, BTN_BACK); // From ConfigureEnd(): expect.Write(uidev, sizeof(uinput_user_dev)); expect.Write(touchpad->injector[t].GetUiDev(), sizeof(uinput_user_dev)); expect.IoctlVoid(UI_DEV_CREATE); EXPECT_EQ(expect.GetString(), record.GetString()); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0, 0, 0); EXPECT_EQ(0, touch_status); expect.WriteInputEvent(EV_ABS, ABS_MT_SLOT, 0); expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0); expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0); expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0); expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->Touch(t, 0, 0, 0); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 0.75f, 0.5f); EXPECT_EQ(0, touch_status); expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0); expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.25f * width); expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.75f * height); expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_PRESS); expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->Touch(t, 0.25f, 0.75f, 0.5f); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.99f, 0.99f, 0.99f); EXPECT_EQ(0, touch_status); expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0); expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.99f * width); expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.99f * height); expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->Touch(t, 0.99f, 0.99f, 0.99f); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.0f, 1.0f, 1.0f); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->Touch(t, 1.0f, 1.0f, 1.0f); EXPECT_EQ(EINVAL, touch_status); EXPECT_EQ(expect.GetString(), record.GetString()); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 0.75f, -0.01f); EXPECT_EQ(0, touch_status); expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_RELEASE); expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, -1); expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->Touch(t, 0.25f, 0.75f, -0.01f); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY, AMOTION_EVENT_BUTTON_BACK); EXPECT_EQ(0, touch_status); expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_PRESS); expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY, AMOTION_EVENT_BUTTON_BACK); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), record.GetString()); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY, 0); EXPECT_EQ(0, touch_status); expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_RELEASE); expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); touch_status = touchpad->ButtonState(t, 0); EXPECT_EQ(0, touch_status); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } expect.Reset(); record.Reset(); expect.Close(); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); touchpad->injector[t].record.Reset(); } touch_status = touchpad->Detach(); EXPECT_EQ(0, touch_status); expect.Close(); EXPECT_EQ(expect.GetString(), record.GetString()); for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) { SCOPED_TRACE(t); EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString()); } } TEST_F(VirtualTouchpadTest, Badness) { sp<VirtualTouchpadForTesting> touchpad(VirtualTouchpadForTesting::New()); UInputRecorder expect; UInputRecorder record; EvdevInjectorForTesting injector(record); sp<VirtualTouchpad> touchpad(VirtualTouchpadForTesting::Create(injector)); UInputRecorder& record = touchpad->injector[VirtualTouchpad::PRIMARY].record; status_t touch_status = touchpad->Attach(); EXPECT_EQ(0, touch_status); Loading @@ -252,11 +295,9 @@ TEST_F(VirtualTouchpadTest, Badness) { // and should not result in any system calls. expect.Reset(); record.Reset(); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, -0.25f, 0.75f, 1.0f); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, -0.25f, 0.75f, 1.0f); EXPECT_NE(OK, touch_status); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, -0.75f, 1.0f); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, -0.75f, 1.0f); EXPECT_NE(OK, touch_status); touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.25f, 0.75f, 1.0f); EXPECT_NE(OK, touch_status); Loading