blob: 0f290250cc616e026645f3d33575478e2f64c862 [file] [log] [blame]
// 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(&paradise_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(&paradise_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(&paradise_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(&paradise_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