blob: a8713c20136a07da46633fb14b4d3dfff641e613 [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 <lib/hid-parser/parser.h>
#include <lib/hid-parser/report.h>
#include <lib/hid-parser/units.h>
#include <lib/hid-parser/usages.h>
#include <lib/hid/usages.h>
#include <lib/stdcompat/span.h>
#include <stdint.h>
#include "src/ui/input/lib/hid-input-report/device.h"
namespace hid_input_report {
namespace {
std::optional<fuchsia::input::Key> hid_key_to_fuchsia_key3(hid::Usage usage) {
if (usage.page == hid::usage::Page::kKeyboardKeypad) {
auto code = (((uint8_t)(hid::usage::Page::kKeyboardKeypad)) & 0xFF) << 16 | (usage.usage);
if (code == ((uint32_t)fuchsia::input::Key::KEYPAD_EQUALS) ||
code == ((uint32_t)fuchsia::input::Key::MENU) ||
(code >= ((uint32_t)fuchsia::input::Key::A) &&
code <= ((uint32_t)fuchsia::input::Key::NON_US_BACKSLASH)) ||
(code >= ((uint32_t)fuchsia::input::Key::LEFT_CTRL) &&
(code <= ((uint32_t)fuchsia::input::Key::RIGHT_META)))) {
return static_cast<fuchsia::input::Key>(code);
}
}
return {};
}
void InsertFuchsiaKey3(uint32_t hid_usage, uint32_t hid_key,
std::set<fuchsia_input::wire::Key>* key_values) {
std::optional<fuchsia::input::Key> fuchsia_key3 =
hid_key_to_fuchsia_key3(hid::USAGE(hid_usage, hid_key));
if (fuchsia_key3) {
// Cast the key enum from HLCPP to LLCPP. We are guaranteed that this will be
// equivalent.
key_values->insert(static_cast<fuchsia_input::wire::Key>(*fuchsia_key3));
}
}
} // namespace
ParseResult Keyboard::ParseInputReportDescriptor(
const hid::ReportDescriptor& hid_report_descriptor) {
// Use a set to make it easy to create a list of sorted and unique keys.
std::set<fuchsia_input::wire::Key> key_3_values;
std::array<hid::ReportField, fuchsia_input_report::wire::kKeyboardMaxNumKeys> key_fields;
size_t num_key_fields = 0;
for (size_t i = 0; i < hid_report_descriptor.input_count; i++) {
const hid::ReportField& field = hid_report_descriptor.input_fields[i];
if (field.attr.usage.page == hid::usage::Page::kKeyboardKeypad) {
if (field.flags & hid::FieldTypeFlags::kArray) {
for (uint8_t key = static_cast<uint8_t>(field.attr.logc_mm.min);
key < static_cast<uint8_t>(field.attr.logc_mm.max); key++) {
InsertFuchsiaKey3(field.attr.usage.page, key, &key_3_values);
}
} else {
InsertFuchsiaKey3(field.attr.usage.page, field.attr.usage.usage, &key_3_values);
}
key_fields[num_key_fields++] = field;
if (num_key_fields == fuchsia_input_report::wire::kKeyboardMaxNumKeys) {
return ParseResult::kTooManyItems;
}
}
}
if (key_3_values_.size() >= fuchsia_input_report::wire::kKeyboardMaxNumKeys) {
return ParseResult::kTooManyItems;
}
// No error, write to class members.
key_3_values_ = std::move(key_3_values);
num_key_fields_ = num_key_fields;
key_fields_ = key_fields;
input_report_size_ = hid_report_descriptor.input_byte_sz;
input_report_id_ = hid_report_descriptor.report_id;
return ParseResult::kOk;
}
ParseResult Keyboard::ParseOutputReportDescriptor(
const hid::ReportDescriptor& hid_report_descriptor) {
std::array<hid::ReportField, fuchsia_input_report::wire::kKeyboardMaxNumLeds> led_fields;
size_t num_leds = 0;
for (size_t i = 0; i < hid_report_descriptor.output_count; i++) {
const hid::ReportField& field = hid_report_descriptor.output_fields[i];
if (field.attr.usage.page == hid::usage::Page::kLEDs) {
if (num_leds == fuchsia_input_report::wire::kKeyboardMaxNumLeds) {
return ParseResult::kTooManyItems;
}
led_fields[num_leds++] = field;
}
}
if (num_leds == 0) {
return ParseResult::kOk;
}
// No errors, write to class members.
num_leds_ = num_leds;
led_fields_ = led_fields;
output_report_id_ = hid_report_descriptor.report_id;
output_report_size_ = hid_report_descriptor.output_byte_sz;
return ParseResult::kOk;
}
ParseResult Keyboard::ParseReportDescriptor(const hid::ReportDescriptor& hid_report_descriptor) {
ParseResult res = ParseInputReportDescriptor(hid_report_descriptor);
if (res != ParseResult::kOk) {
return res;
}
return ParseOutputReportDescriptor(hid_report_descriptor);
}
ParseResult Keyboard::CreateDescriptor(fidl::AnyArena& allocator,
fuchsia_input_report::wire::DeviceDescriptor& descriptor) {
fuchsia_input_report::wire::KeyboardDescriptor keyboard(allocator);
// Input Descriptor parsing.
if (input_report_size_ > 0) {
fuchsia_input_report::wire::KeyboardInputDescriptor keyboard_input(allocator);
size_t keys_3_index = 0;
fidl::VectorView<fuchsia_input::wire::Key> keys_3(allocator, key_3_values_.size());
for (auto& key : key_3_values_) {
keys_3[keys_3_index++] = key;
}
keyboard_input.set_keys3(allocator, std::move(keys_3));
keyboard.set_input(allocator, std::move(keyboard_input));
}
// Output Descriptor parsing.
if (output_report_size_ > 0) {
fuchsia_input_report::wire::KeyboardOutputDescriptor keyboard_output(allocator);
size_t leds_index = 0;
fidl::VectorView<fuchsia_input_report::wire::LedType> leds(allocator, num_leds_);
for (hid::ReportField& field : cpp20::span(led_fields_.data(), num_leds_)) {
zx_status_t status = HidLedUsageToLlcppLedType(
static_cast<hid::usage::LEDs>(field.attr.usage.usage), &leds[leds_index++]);
if (status != ZX_OK) {
return ParseResult::kBadReport;
}
}
keyboard_output.set_leds(allocator, std::move(leds));
keyboard.set_output(allocator, std::move(keyboard_output));
}
descriptor.set_keyboard(allocator, std::move(keyboard));
return ParseResult::kOk;
}
ParseResult Keyboard::ParseInputReportInternal(
const uint8_t* data, size_t len, fidl::AnyArena& allocator,
fuchsia_input_report::wire::InputReport& input_report) {
if (len != input_report_size_) {
return ParseResult::kReportSizeMismatch;
}
fuchsia_input_report::wire::KeyboardInputReport keyboard_report(allocator);
size_t num_pressed_keys_3 = 0;
std::array<fuchsia_input::wire::Key, fuchsia_input_report::wire::kKeyboardMaxNumKeys>
pressed_keys_3;
for (hid::ReportField& field : cpp20::span(key_fields_.data(), num_key_fields_)) {
double val_out_double;
if (!ExtractAsUnitType(data, len, field.attr, &val_out_double)) {
continue;
}
uint32_t val_out = static_cast<uint32_t>(val_out_double);
if (val_out == 0) {
continue;
}
// Get the HID key.
uint32_t hid_key;
if (field.flags & hid::FieldTypeFlags::kArray) {
if (val_out == HID_USAGE_KEY_ERROR_ROLLOVER) {
return ParseResult::kBadReport;
}
hid_key = val_out;
} else {
hid_key = field.attr.usage.usage;
}
// Convert to fuchsia key.
auto fuchsia_key_3 =
hid_key_to_fuchsia_key3(hid::USAGE(hid::usage::Page::kKeyboardKeypad, hid_key));
if (fuchsia_key_3) {
// Cast the key enum from HLCPP to LLCPP. We are guaranteed that this will be equivalent.
pressed_keys_3[num_pressed_keys_3++] = static_cast<fuchsia_input::wire::Key>(*fuchsia_key_3);
}
}
fidl::VectorView<fuchsia_input::wire::Key> fidl_pressed_keys_3(allocator, num_pressed_keys_3);
for (size_t i = 0; i < num_pressed_keys_3; i++) {
fidl_pressed_keys_3[i] = pressed_keys_3[i];
}
keyboard_report.set_pressed_keys3(allocator, std::move(fidl_pressed_keys_3));
input_report.set_keyboard(allocator, std::move(keyboard_report));
return ParseResult::kOk;
}
ParseResult Keyboard::SetOutputReportInternal(
const fuchsia_input_report::wire::OutputReport* report, uint8_t* data, size_t data_size,
size_t* data_out_size) {
if (!report->has_keyboard()) {
return ParseResult::kNotImplemented;
}
if (!report->keyboard().has_enabled_leds()) {
return ParseResult::kNotImplemented;
}
if (data_size < output_report_size_) {
return ParseResult::kNoMemory;
}
for (size_t i = 0; i < data_size; i++) {
data[i] = 0;
}
// Go through each enabled LED and set its report field to enabled.
for (fuchsia_input_report::wire::LedType led : report->keyboard().enabled_leds()) {
bool found = false;
for (size_t i = 0; i < num_leds_; i++) {
hid::ReportField& hid_led = led_fields_[i];
// Convert the usage to LedType.
fuchsia_input_report::wire::LedType hid_led_type;
zx_status_t status = HidLedUsageToLlcppLedType(
static_cast<hid::usage::LEDs>(hid_led.attr.usage.usage), &hid_led_type);
if (status != ZX_OK) {
return ParseResult::kBadReport;
}
if (hid_led_type == led) {
found = true;
if (!InsertAsUnitType(data, data_size, hid_led.attr, 1)) {
return ParseResult::kBadReport;
}
break;
}
}
if (!found) {
return ParseResult::kItemNotFound;
}
}
*data_out_size = output_report_size_;
return ParseResult::kOk;
}
} // namespace hid_input_report