blob: 96f16c399e89d25dcac702335ca39a6fdd649eb6 [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 "src/ui/input/lib/hid-input-report/keyboard.h"
#include <fuchsia/input/cpp/fidl.h>
#include <fuchsia/ui/input2/cpp/fidl.h>
#include <variant>
#include <fbl/auto_call.h>
#include <hid/boot.h>
#include <hid/usages.h>
#include <zxtest/zxtest.h>
#include "src/ui/input/lib/hid-input-report/device.h"
#include "src/ui/input/lib/hid-input-report/test/test.h"
#include "src/ui/lib/key_util/key_util.h"
namespace fuchsia_input_report = ::llcpp::fuchsia::input::report;
namespace {
// This is a keyboard with multiple keys of the same usage.
const uint8_t double_keys_keyboard[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x02, // Report Count (2)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0x00, // Usage Minimum (0x00)
0x29, 0x65, // Usage Maximum (0x65)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
};
// This keyboard declares keys up to 0xFF (256 keys).
const uint8_t full_keys_keyboard[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Page (LEDs)
0x19, 0x01, // Usage Minimum (Num Lock)
0x29, 0x05, // Usage Maximum (Kana)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null
// Position,Non-volatile)
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null
// Position,Non-volatile)
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0x00, // Usage Minimum (0x00)
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
};
} // namespace
// Each test parses the report descriptor for the mouse and then sends one
// report to ensure that it has been parsed correctly.
TEST(KeyboardTest, BootKeyboard) {
size_t descriptor_size;
const uint8_t* boot_keyboard_descriptor = get_boot_kbd_report_desc(&descriptor_size);
hid::DeviceDescriptor* dev_desc = nullptr;
auto parse_res = hid::ParseReportDescriptor(boot_keyboard_descriptor, descriptor_size, &dev_desc);
ASSERT_EQ(hid::ParseResult::kParseOk, parse_res);
fbl::AutoCall free_descriptor([dev_desc]() { hid::FreeDeviceDescriptor(dev_desc); });
hid_input_report::Keyboard keyboard;
EXPECT_EQ(hid_input_report::ParseResult::kOk,
keyboard.ParseReportDescriptor(dev_desc->report[0]));
hid_input_report::TestDescriptorAllocator descriptor_allocator;
auto descriptor_builder = fuchsia_input_report::DeviceDescriptor::Builder(
descriptor_allocator.make<fuchsia_input_report::DeviceDescriptor::Frame>());
EXPECT_EQ(hid_input_report::ParseResult::kOk,
keyboard.CreateDescriptor(&descriptor_allocator, &descriptor_builder));
fuchsia_input_report::DeviceDescriptor descriptor = descriptor_builder.build();
EXPECT_TRUE(descriptor.has_keyboard());
EXPECT_TRUE(descriptor.keyboard().has_input());
EXPECT_TRUE(descriptor.keyboard().input().has_keys());
EXPECT_EQ(105, descriptor.keyboard().input().keys().count());
EXPECT_EQ(105, descriptor.keyboard().input().keys3().count());
// Test a report parses correctly.
hid_boot_kbd_report kbd_report = {};
kbd_report.modifier = HID_KBD_MODIFIER_LEFT_SHIFT | HID_KBD_MODIFIER_RIGHT_GUI;
kbd_report.usage[0] = HID_USAGE_KEY_A;
kbd_report.usage[1] = HID_USAGE_KEY_NON_US_BACKSLASH;
kbd_report.usage[2] = HID_USAGE_KEY_UP;
hid_input_report::TestReportAllocator report_allocator;
auto report_builder = fuchsia_input_report::InputReport::Builder(
report_allocator.make<fuchsia_input_report::InputReport::Frame>());
EXPECT_EQ(hid_input_report::ParseResult::kOk,
keyboard.ParseInputReport(reinterpret_cast<uint8_t*>(&kbd_report), sizeof(kbd_report),
&report_allocator, &report_builder));
fuchsia_input_report::InputReport input_report = report_builder.build();
ASSERT_TRUE(input_report.has_keyboard());
ASSERT_EQ(input_report.keyboard().pressed_keys().count(), 5U);
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::RIGHT_META);
EXPECT_EQ(input_report.keyboard().pressed_keys()[2], llcpp::fuchsia::ui::input2::Key::A);
EXPECT_EQ(input_report.keyboard().pressed_keys()[3],
llcpp::fuchsia::ui::input2::Key::NON_US_BACKSLASH);
EXPECT_EQ(input_report.keyboard().pressed_keys()[4], llcpp::fuchsia::ui::input2::Key::UP);
EXPECT_EQ(input_report.keyboard().pressed_keys3()[0], llcpp::fuchsia::input::Key::LEFT_SHIFT);
EXPECT_EQ(input_report.keyboard().pressed_keys3()[1], llcpp::fuchsia::input::Key::RIGHT_META);
EXPECT_EQ(input_report.keyboard().pressed_keys3()[2], llcpp::fuchsia::input::Key::A);
EXPECT_EQ(input_report.keyboard().pressed_keys3()[3],
llcpp::fuchsia::input::Key::NON_US_BACKSLASH);
EXPECT_EQ(input_report.keyboard().pressed_keys3()[4], llcpp::fuchsia::input::Key::UP);
}
TEST(KeyboardTest, OutputDescriptor) {
size_t descriptor_size;
const uint8_t* boot_keyboard_descriptor = get_boot_kbd_report_desc(&descriptor_size);
hid::DeviceDescriptor* dev_desc = nullptr;
auto parse_res = hid::ParseReportDescriptor(boot_keyboard_descriptor, descriptor_size, &dev_desc);
ASSERT_EQ(hid::ParseResult::kParseOk, parse_res);
fbl::AutoCall free_descriptor([dev_desc]() { hid::FreeDeviceDescriptor(dev_desc); });
hid_input_report::Keyboard keyboard;
EXPECT_EQ(hid_input_report::ParseResult::kOk,
keyboard.ParseReportDescriptor(dev_desc->report[0]));
hid_input_report::TestDescriptorAllocator descriptor_allocator;
auto descriptor_builder = fuchsia_input_report::DeviceDescriptor::Builder(
descriptor_allocator.make<fuchsia_input_report::DeviceDescriptor::Frame>());
EXPECT_EQ(hid_input_report::ParseResult::kOk,
keyboard.CreateDescriptor(&descriptor_allocator, &descriptor_builder));
fuchsia_input_report::DeviceDescriptor descriptor = descriptor_builder.build();
ASSERT_EQ(descriptor.keyboard().output().leds().count(), 5);
EXPECT_EQ(descriptor.keyboard().output().leds()[0],
hid_input_report::fuchsia_input_report::LedType::NUM_LOCK);
EXPECT_EQ(descriptor.keyboard().output().leds()[1],
hid_input_report::fuchsia_input_report::LedType::CAPS_LOCK);
EXPECT_EQ(descriptor.keyboard().output().leds()[2],
hid_input_report::fuchsia_input_report::LedType::SCROLL_LOCK);
EXPECT_EQ(descriptor.keyboard().output().leds()[3],
hid_input_report::fuchsia_input_report::LedType::COMPOSE);
EXPECT_EQ(descriptor.keyboard().output().leds()[4],
hid_input_report::fuchsia_input_report::LedType::KANA);
}
// This test double checks that we don't double count keys that are included twice.
TEST(KeyboardTest, DoubleCountingKeys) {
hid::DeviceDescriptor* dev_desc = nullptr;
auto parse_res =
hid::ParseReportDescriptor(double_keys_keyboard, sizeof(double_keys_keyboard), &dev_desc);
ASSERT_EQ(hid::ParseResult::kParseOk, parse_res);
fbl::AutoCall free_descriptor([dev_desc]() { hid::FreeDeviceDescriptor(dev_desc); });
// Test the descriptor parses correctly.
hid_input_report::Keyboard keyboard;
EXPECT_EQ(hid_input_report::ParseResult::kOk,
keyboard.ParseReportDescriptor(dev_desc->report[0]));
hid_input_report::TestDescriptorAllocator descriptor_allocator;
auto descriptor_builder = fuchsia_input_report::DeviceDescriptor::Builder(
descriptor_allocator.make<fuchsia_input_report::DeviceDescriptor::Frame>());
EXPECT_EQ(hid_input_report::ParseResult::kOk,
keyboard.CreateDescriptor(&descriptor_allocator, &descriptor_builder));
fuchsia_input_report::DeviceDescriptor descriptor = descriptor_builder.build();
EXPECT_EQ(descriptor.keyboard().input().keys().count(), 105U);
}
TEST(KeyboardTest, BootKeyboardOutputReport) {
size_t descriptor_size;
const uint8_t* boot_keyboard_descriptor = get_boot_kbd_report_desc(&descriptor_size);
hid::DeviceDescriptor* dev_desc = nullptr;
auto parse_res = hid::ParseReportDescriptor(boot_keyboard_descriptor, descriptor_size, &dev_desc);
ASSERT_EQ(hid::ParseResult::kParseOk, parse_res);
fbl::AutoCall free_descriptor([dev_desc]() { hid::FreeDeviceDescriptor(dev_desc); });
hid_input_report::Keyboard keyboard;
EXPECT_EQ(hid_input_report::ParseResult::kOk,
keyboard.ParseReportDescriptor(dev_desc->report[0]));
std::array<hid_input_report::fuchsia_input_report::LedType, 2> led_array;
led_array[0] = hid_input_report::fuchsia_input_report::LedType::NUM_LOCK;
led_array[1] = hid_input_report::fuchsia_input_report::LedType::SCROLL_LOCK;
// Build the FIDL table.
auto led_view = fidl::unowned_vec(led_array);
hid_input_report::fuchsia_input_report::KeyboardOutputReport::UnownedBuilder keyboard_builder;
keyboard_builder.set_enabled_leds(fidl::unowned_ptr(&led_view));
hid_input_report::fuchsia_input_report::KeyboardOutputReport fidl_keyboard =
keyboard_builder.build();
hid_input_report::fuchsia_input_report::OutputReport::UnownedBuilder builder;
builder.set_keyboard(fidl::unowned_ptr(&fidl_keyboard));
hid_input_report::fuchsia_input_report::OutputReport fidl_report = builder.build();
uint8_t report_data;
size_t out_report_size;
auto result =
keyboard.SetOutputReport(&fidl_report, &report_data, sizeof(report_data), &out_report_size);
ASSERT_EQ(result, hid_input_report::ParseResult::kOk);
ASSERT_EQ(1, out_report_size);
ASSERT_EQ(0b101, report_data);
}
TEST(KeyboardTest, FullKeysKeyboard) {
hid::DeviceDescriptor* dev_desc = nullptr;
auto parse_res =
hid::ParseReportDescriptor(full_keys_keyboard, sizeof(full_keys_keyboard), &dev_desc);
ASSERT_EQ(hid::ParseResult::kParseOk, parse_res);
fbl::AutoCall free_descriptor([dev_desc]() { hid::FreeDeviceDescriptor(dev_desc); });
hid_input_report::Keyboard keyboard;
EXPECT_EQ(hid_input_report::ParseResult::kOk,
keyboard.ParseReportDescriptor(dev_desc->report[0]));
hid_input_report::TestDescriptorAllocator descriptor_allocator;
auto descriptor_builder = fuchsia_input_report::DeviceDescriptor::Builder(
descriptor_allocator.make<fuchsia_input_report::DeviceDescriptor::Frame>());
EXPECT_EQ(hid_input_report::ParseResult::kOk,
keyboard.CreateDescriptor(&descriptor_allocator, &descriptor_builder));
fuchsia_input_report::DeviceDescriptor descriptor = descriptor_builder.build();
EXPECT_EQ(descriptor.keyboard().input().keys().count(), 107);
EXPECT_EQ(descriptor.keyboard().input().keys3().count(), 107);
// Test a report parses correctly.
hid_boot_kbd_report kbd_report = {};
kbd_report.modifier = HID_KBD_MODIFIER_LEFT_SHIFT | HID_KBD_MODIFIER_RIGHT_GUI;
kbd_report.usage[0] = HID_USAGE_KEY_A;
kbd_report.usage[1] = HID_USAGE_KEY_NON_US_BACKSLASH;
kbd_report.usage[2] = HID_USAGE_KEY_UP;
hid_input_report::TestReportAllocator report_allocator;
auto report_builder = fuchsia_input_report::InputReport::Builder(
report_allocator.make<fuchsia_input_report::InputReport::Frame>());
EXPECT_EQ(hid_input_report::ParseResult::kOk,
keyboard.ParseInputReport(reinterpret_cast<uint8_t*>(&kbd_report), sizeof(kbd_report),
&report_allocator, &report_builder));
fuchsia_input_report::InputReport input_report = report_builder.build();
ASSERT_EQ(input_report.keyboard().pressed_keys().count(), 5U);
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::RIGHT_META);
EXPECT_EQ(input_report.keyboard().pressed_keys()[2], llcpp::fuchsia::ui::input2::Key::A);
EXPECT_EQ(input_report.keyboard().pressed_keys()[3],
llcpp::fuchsia::ui::input2::Key::NON_US_BACKSLASH);
EXPECT_EQ(input_report.keyboard().pressed_keys()[4], llcpp::fuchsia::ui::input2::Key::UP);
ASSERT_EQ(input_report.keyboard().pressed_keys3().count(), 5U);
EXPECT_EQ(input_report.keyboard().pressed_keys3()[0], llcpp::fuchsia::input::Key::LEFT_SHIFT);
EXPECT_EQ(input_report.keyboard().pressed_keys3()[1], llcpp::fuchsia::input::Key::RIGHT_META);
EXPECT_EQ(input_report.keyboard().pressed_keys3()[2], llcpp::fuchsia::input::Key::A);
EXPECT_EQ(input_report.keyboard().pressed_keys3()[3],
llcpp::fuchsia::input::Key::NON_US_BACKSLASH);
EXPECT_EQ(input_report.keyboard().pressed_keys3()[4], llcpp::fuchsia::input::Key::UP);
}
TEST(KeyboardTest, DeviceType) {
hid_input_report::Keyboard keyboard;
ASSERT_EQ(hid_input_report::DeviceType::kKeyboard, keyboard.GetDeviceType());
}