| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <ddk/platform-defs.h> |
| #include <ddk/protocol/hidbus.h> |
| #include <fbl/auto_call.h> |
| #include <hid/boot.h> |
| #include <virtio/input.h> |
| #include <zxtest/zxtest.h> |
| |
| #include "input_kbd.h" |
| #include "input_mouse.h" |
| #include "input_touch.h" |
| #include "src/ui/input/lib/hid-input-report/keyboard.h" |
| #include "src/ui/input/lib/hid-input-report/mouse.h" |
| |
| namespace virtio { |
| |
| namespace { |
| |
| void SendTouchEvent(HidTouch& touch, uint16_t type, uint16_t code, uint32_t value) { |
| virtio_input_event_t event = {}; |
| event.type = type; |
| event.code = code; |
| event.value = value; |
| |
| touch.ReceiveEvent(&event); |
| } |
| |
| } // namespace |
| |
| class VirtioInputTest : public zxtest::Test {}; |
| |
| TEST_F(VirtioInputTest, MultiTouchReportDescriptor) { |
| virtio_input_absinfo_t x_info = {}; |
| virtio_input_absinfo_t y_info = {}; |
| HidTouch touch(x_info, y_info); |
| |
| // Assert that the report descriptor is correct. |
| // In this case correct means a copy of the paradise touch report descriptor. |
| uint8_t desc[HID_MAX_DESC_LEN]; |
| size_t desc_len; |
| zx_status_t status = |
| touch.GetDescriptor(HID_DESCRIPTION_TYPE_REPORT, desc, sizeof(desc), &desc_len); |
| ASSERT_OK(status); |
| |
| size_t paradise_size; |
| const uint8_t* paradise_desc = get_paradise_touch_report_desc(¶dise_size); |
| ASSERT_EQ(paradise_size, desc_len); |
| ASSERT_EQ(0, memcmp(paradise_desc, desc, desc_len)); |
| } |
| |
| TEST_F(VirtioInputTest, MultiTouchFingerEvents) { |
| int VAL_MAX = 100; |
| int X_VAL = 50; |
| int Y_VAL = 100; |
| virtio_input_absinfo_t x_info = {}; |
| x_info.min = 0; |
| x_info.max = static_cast<uint16_t>(VAL_MAX); |
| virtio_input_absinfo_t y_info = {}; |
| y_info.min = 0; |
| y_info.max = static_cast<uint16_t>(VAL_MAX); |
| HidTouch touch(x_info, y_info); |
| |
| // Assert that a single finger works. |
| SendTouchEvent(touch, VIRTIO_INPUT_EV_ABS, VIRTIO_INPUT_EV_MT_SLOT, 0); |
| SendTouchEvent(touch, VIRTIO_INPUT_EV_ABS, VIRTIO_INPUT_EV_MT_TRACKING_ID, 1); |
| SendTouchEvent(touch, VIRTIO_INPUT_EV_ABS, VIRTIO_INPUT_EV_MT_POSITION_X, |
| static_cast<uint16_t>(X_VAL)); |
| SendTouchEvent(touch, VIRTIO_INPUT_EV_ABS, VIRTIO_INPUT_EV_MT_POSITION_Y, |
| static_cast<uint16_t>(Y_VAL)); |
| |
| size_t paradise_size; |
| const void* report = touch.GetReport(¶dise_size); |
| const paradise_touch_t* paradise_touch = reinterpret_cast<const paradise_touch_t*>(report); |
| |
| ASSERT_EQ(sizeof(paradise_touch_t), paradise_size); |
| ASSERT_EQ(1, paradise_touch->contact_count); |
| ASSERT_EQ(PARADISE_FINGER_FLAGS_TSWITCH_MASK, |
| paradise_touch->fingers[0].flags & PARADISE_FINGER_FLAGS_TSWITCH_MASK); |
| ASSERT_EQ(X_VAL * PARADISE_X_MAX / VAL_MAX, paradise_touch->fingers[0].x); |
| ASSERT_EQ(Y_VAL * PARADISE_Y_MAX / VAL_MAX, paradise_touch->fingers[0].y); |
| |
| ASSERT_EQ(0, paradise_touch->fingers[1].flags); |
| ASSERT_EQ(0, paradise_touch->fingers[2].flags); |
| ASSERT_EQ(0, paradise_touch->fingers[3].flags); |
| ASSERT_EQ(0, paradise_touch->fingers[4].flags); |
| |
| // Assert that a second finger tracks. |
| SendTouchEvent(touch, VIRTIO_INPUT_EV_ABS, VIRTIO_INPUT_EV_MT_SLOT, 1); |
| SendTouchEvent(touch, VIRTIO_INPUT_EV_ABS, VIRTIO_INPUT_EV_MT_TRACKING_ID, 2); |
| |
| report = touch.GetReport(¶dise_size); |
| paradise_touch = reinterpret_cast<const paradise_touch_t*>(report); |
| |
| ASSERT_EQ(sizeof(paradise_touch_t), paradise_size); |
| ASSERT_EQ(2, paradise_touch->contact_count); |
| |
| ASSERT_EQ(PARADISE_FINGER_FLAGS_TSWITCH_MASK, |
| paradise_touch->fingers[0].flags & PARADISE_FINGER_FLAGS_TSWITCH_MASK); |
| ASSERT_EQ(PARADISE_FINGER_FLAGS_TSWITCH_MASK, |
| paradise_touch->fingers[1].flags & PARADISE_FINGER_FLAGS_TSWITCH_MASK); |
| ASSERT_EQ(0, paradise_touch->fingers[2].flags); |
| ASSERT_EQ(0, paradise_touch->fingers[3].flags); |
| ASSERT_EQ(0, paradise_touch->fingers[4].flags); |
| |
| // Pick up the second finger. |
| |
| // We don't send another SLOT event because we will rely on the slot already |
| // being 1. |
| SendTouchEvent(touch, VIRTIO_INPUT_EV_ABS, VIRTIO_INPUT_EV_MT_TRACKING_ID, -1); |
| |
| report = touch.GetReport(¶dise_size); |
| paradise_touch = reinterpret_cast<const paradise_touch_t*>(report); |
| |
| ASSERT_EQ(sizeof(paradise_touch_t), paradise_size); |
| ASSERT_EQ(1, paradise_touch->contact_count); |
| |
| ASSERT_EQ(PARADISE_FINGER_FLAGS_TSWITCH_MASK, |
| paradise_touch->fingers[0].flags & PARADISE_FINGER_FLAGS_TSWITCH_MASK); |
| ASSERT_EQ(0, paradise_touch->fingers[1].flags); |
| ASSERT_EQ(0, paradise_touch->fingers[2].flags); |
| ASSERT_EQ(0, paradise_touch->fingers[3].flags); |
| ASSERT_EQ(0, paradise_touch->fingers[4].flags); |
| } |
| |
| TEST_F(VirtioInputTest, MouseTest) { |
| // Get the HID descriptor. |
| HidMouse hid_mouse; |
| uint8_t report_descriptor[2048] = {}; |
| size_t report_descriptor_size = 0; |
| ASSERT_OK(hid_mouse.GetDescriptor(HID_DESCRIPTION_TYPE_REPORT, report_descriptor, |
| sizeof(report_descriptor), &report_descriptor_size)); |
| |
| // Parse the HID descriptor. |
| hid::DeviceDescriptor* dev_desc = nullptr; |
| auto parse_res = hid::ParseReportDescriptor(report_descriptor, report_descriptor_size, &dev_desc); |
| ASSERT_EQ(parse_res, hid::ParseResult::kParseOk); |
| ASSERT_EQ(1, dev_desc->rep_count); |
| fbl::AutoCall free_descriptor([dev_desc]() { hid::FreeDeviceDescriptor(dev_desc); }); |
| |
| hid_input_report::Mouse mouse; |
| ASSERT_EQ(hid_input_report::ParseResult::kOk, mouse.ParseReportDescriptor(dev_desc->report[0])); |
| |
| // Send the Virtio mouse keys. |
| virtio_input_event_t event = {}; |
| event.type = VIRTIO_INPUT_EV_KEY; |
| event.value = VIRTIO_INPUT_EV_KEY_PRESSED; |
| |
| event.code = 0x110; // BTN_LEFT. |
| hid_mouse.ReceiveEvent(&event); |
| |
| // Parse the HID report. |
| size_t report_size; |
| const uint8_t* report = hid_mouse.GetReport(&report_size); |
| |
| fidl::BufferThenHeapAllocator<2048> report_allocator; |
| auto report_builder = hid_input_report::fuchsia_input_report::InputReport::Builder( |
| report_allocator.make<hid_input_report::fuchsia_input_report::InputReport::Frame>()); |
| |
| EXPECT_EQ(hid_input_report::ParseResult::kOk, |
| mouse.ParseInputReport(report, report_size, &report_allocator, &report_builder)); |
| hid_input_report::fuchsia_input_report::InputReport input_report = report_builder.build(); |
| |
| ASSERT_EQ(input_report.mouse().pressed_buttons().count(), 1U); |
| EXPECT_EQ(input_report.mouse().pressed_buttons()[0], 1U); |
| |
| // ------------------------------------------------------------------ |
| |
| // Send another Virtio mouse key event. |
| event.type = VIRTIO_INPUT_EV_KEY; |
| event.value = VIRTIO_INPUT_EV_KEY_PRESSED; |
| |
| event.code = 0x111; // BTN_RIGHT. |
| hid_mouse.ReceiveEvent(&event); |
| |
| // Parse the HID report. |
| report = hid_mouse.GetReport(&report_size); |
| report_builder = hid_input_report::fuchsia_input_report::InputReport::Builder( |
| report_allocator.make<hid_input_report::fuchsia_input_report::InputReport::Frame>()); |
| EXPECT_EQ(hid_input_report::ParseResult::kOk, |
| mouse.ParseInputReport(report, report_size, &report_allocator, &report_builder)); |
| input_report = report_builder.build(); |
| |
| ASSERT_EQ(input_report.mouse().pressed_buttons().count(), 2U); |
| EXPECT_EQ(input_report.mouse().pressed_buttons()[0], 1U); |
| EXPECT_EQ(input_report.mouse().pressed_buttons()[1], 2U); |
| |
| // ------------------------------------------------------------------ |
| |
| // Release one Virtio mouse key. |
| event.type = VIRTIO_INPUT_EV_KEY; |
| event.value = VIRTIO_INPUT_EV_KEY_RELEASED; |
| |
| event.code = 0x110; // BTN_LEFT. |
| hid_mouse.ReceiveEvent(&event); |
| |
| // Parse the HID report. |
| report = hid_mouse.GetReport(&report_size); |
| report_builder = hid_input_report::fuchsia_input_report::InputReport::Builder( |
| report_allocator.make<hid_input_report::fuchsia_input_report::InputReport::Frame>()); |
| EXPECT_EQ(hid_input_report::ParseResult::kOk, |
| mouse.ParseInputReport(report, report_size, &report_allocator, &report_builder)); |
| input_report = report_builder.build(); |
| |
| ASSERT_EQ(input_report.mouse().pressed_buttons().count(), 1U); |
| EXPECT_EQ(input_report.mouse().pressed_buttons()[0], 2U); |
| |
| // ------------------------------------------------------------------ |
| |
| // Send another Virtio mouse key event. |
| event.type = VIRTIO_INPUT_EV_KEY; |
| event.value = VIRTIO_INPUT_EV_KEY_PRESSED; |
| event.code = 0x112; // BTN_MID. |
| hid_mouse.ReceiveEvent(&event); |
| |
| // Parse the HID report. |
| report = hid_mouse.GetReport(&report_size); |
| report_builder = hid_input_report::fuchsia_input_report::InputReport::Builder( |
| report_allocator.make<hid_input_report::fuchsia_input_report::InputReport::Frame>()); |
| EXPECT_EQ(hid_input_report::ParseResult::kOk, |
| mouse.ParseInputReport(report, report_size, &report_allocator, &report_builder)); |
| input_report = report_builder.build(); |
| |
| ASSERT_EQ(input_report.mouse().pressed_buttons().count(), 2U); |
| EXPECT_EQ(input_report.mouse().pressed_buttons()[0], 2U); |
| EXPECT_EQ(input_report.mouse().pressed_buttons()[1], 3U); |
| |
| // ------------------------------------------------------------------ |
| |
| // Send a Virtio mouse rel event on X axis. |
| event.type = VIRTIO_INPUT_EV_REL; |
| event.code = VIRTIO_INPUT_EV_REL_X; |
| event.value = 0x0abc; |
| hid_mouse.ReceiveEvent(&event); |
| |
| // Parse the HID report. |
| report = hid_mouse.GetReport(&report_size); |
| report_builder = hid_input_report::fuchsia_input_report::InputReport::Builder( |
| report_allocator.make<hid_input_report::fuchsia_input_report::InputReport::Frame>()); |
| EXPECT_EQ(hid_input_report::ParseResult::kOk, |
| mouse.ParseInputReport(report, report_size, &report_allocator, &report_builder)); |
| input_report = report_builder.build(); |
| |
| ASSERT_TRUE(input_report.mouse().has_movement_x()); |
| EXPECT_EQ(input_report.mouse().movement_x(), 0x0abc); |
| |
| // ------------------------------------------------------------------ |
| |
| // Send a Virtio mouse rel event on Y axis. |
| event.type = VIRTIO_INPUT_EV_REL; |
| event.code = VIRTIO_INPUT_EV_REL_Y; |
| event.value = 0x0123; |
| hid_mouse.ReceiveEvent(&event); |
| |
| // Parse the HID report. |
| report = hid_mouse.GetReport(&report_size); |
| report_builder = hid_input_report::fuchsia_input_report::InputReport::Builder( |
| report_allocator.make<hid_input_report::fuchsia_input_report::InputReport::Frame>()); |
| EXPECT_EQ(hid_input_report::ParseResult::kOk, |
| mouse.ParseInputReport(report, report_size, &report_allocator, &report_builder)); |
| input_report = report_builder.build(); |
| |
| ASSERT_TRUE(input_report.mouse().has_movement_y()); |
| EXPECT_EQ(input_report.mouse().movement_y(), 0x0123); |
| |
| // ------------------------------------------------------------------ |
| |
| // Send a Virtio mouse rel event on wheel. |
| event.type = VIRTIO_INPUT_EV_REL; |
| event.code = VIRTIO_INPUT_EV_REL_WHEEL; |
| event.value = 0x0345; |
| hid_mouse.ReceiveEvent(&event); |
| |
| // Parse the HID report. |
| report = hid_mouse.GetReport(&report_size); |
| report_builder = hid_input_report::fuchsia_input_report::InputReport::Builder( |
| report_allocator.make<hid_input_report::fuchsia_input_report::InputReport::Frame>()); |
| EXPECT_EQ(hid_input_report::ParseResult::kOk, |
| mouse.ParseInputReport(report, report_size, &report_allocator, &report_builder)); |
| input_report = report_builder.build(); |
| |
| ASSERT_TRUE(input_report.mouse().has_scroll_v()); |
| EXPECT_EQ(input_report.mouse().scroll_v(), 0x0345); |
| } |
| |
| TEST_F(VirtioInputTest, KeyboardTest) { |
| // Get the HID descriptor. |
| HidKeyboard hid_keyboard; |
| uint8_t report_descriptor[2048] = {}; |
| size_t report_descriptor_size = 0; |
| ASSERT_OK(hid_keyboard.GetDescriptor(HID_DESCRIPTION_TYPE_REPORT, report_descriptor, |
| sizeof(report_descriptor), &report_descriptor_size)); |
| |
| // Parse the HID descriptor. |
| hid::DeviceDescriptor* dev_desc = nullptr; |
| auto parse_res = hid::ParseReportDescriptor(report_descriptor, report_descriptor_size, &dev_desc); |
| ASSERT_EQ(parse_res, hid::ParseResult::kParseOk); |
| ASSERT_EQ(1, dev_desc->rep_count); |
| fbl::AutoCall free_descriptor([dev_desc]() { hid::FreeDeviceDescriptor(dev_desc); }); |
| |
| hid_input_report::Keyboard keyboard; |
| ASSERT_EQ(hid_input_report::ParseResult::kOk, |
| keyboard.ParseReportDescriptor(dev_desc->report[0])); |
| |
| // Send the Virtio keys. |
| virtio_input_event_t event = {}; |
| event.type = VIRTIO_INPUT_EV_KEY; |
| event.value = VIRTIO_INPUT_EV_KEY_PRESSED; |
| |
| event.code = 42; // LEFTSHIFT. |
| hid_keyboard.ReceiveEvent(&event); |
| |
| event.code = 30; // KEY_A |
| hid_keyboard.ReceiveEvent(&event); |
| |
| event.code = 100; // KEY_RIGHTALT |
| hid_keyboard.ReceiveEvent(&event); |
| |
| event.code = 108; // KEY_DOWN |
| hid_keyboard.ReceiveEvent(&event); |
| |
| // Parse the HID report. |
| size_t report_size; |
| const uint8_t* report = hid_keyboard.GetReport(&report_size); |
| |
| fidl::BufferThenHeapAllocator<2048> report_allocator; |
| auto report_builder = hid_input_report::fuchsia_input_report::InputReport::Builder( |
| report_allocator.make<hid_input_report::fuchsia_input_report::InputReport::Frame>()); |
| |
| EXPECT_EQ(hid_input_report::ParseResult::kOk, |
| keyboard.ParseInputReport(report, report_size, &report_allocator, &report_builder)); |
| hid_input_report::fuchsia_input_report::InputReport input_report = report_builder.build(); |
| |
| ASSERT_EQ(input_report.keyboard().pressed_keys().count(), 4U); |
| EXPECT_EQ(input_report.keyboard().pressed_keys()[0], llcpp::fuchsia::ui::input2::Key::LEFT_SHIFT); |
| EXPECT_EQ(input_report.keyboard().pressed_keys()[1], llcpp::fuchsia::ui::input2::Key::A); |
| EXPECT_EQ(input_report.keyboard().pressed_keys()[2], llcpp::fuchsia::ui::input2::Key::RIGHT_ALT); |
| EXPECT_EQ(input_report.keyboard().pressed_keys()[3], llcpp::fuchsia::ui::input2::Key::DOWN); |
| } |
| |
| } // namespace virtio |