blob: 82b42100a7cbbec4fd5dce568ec5df783b1cce2f [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/lib/hid-input-report/touch.h"
#include <stdint.h>
#include <hid-parser/parser.h>
#include <hid-parser/report.h>
#include <hid-parser/units.h>
#include <hid-parser/usages.h>
#include "src/ui/lib/hid-input-report/device.h"
namespace hid_input_report {
ParseResult Touch::ParseReportDescriptor(const hid::ReportDescriptor& hid_report_descriptor) {
ContactConfig contacts[kTouchMaxContacts];
size_t num_contacts = 0;
TouchDescriptor descriptor = {};
// Traverse up the nested collections to the Application collection.
hid::Collection* main_collection = hid_report_descriptor.input_fields[0].col;
while (main_collection != nullptr) {
if (main_collection->type == hid::CollectionType::kApplication) {
break;
}
main_collection = main_collection->parent;
}
if (!main_collection) {
return kParseNoCollection;
}
if (main_collection->usage ==
hid::USAGE(hid::usage::Page::kDigitizer, hid::usage::Digitizer::kTouchScreen)) {
descriptor.touch_type = ::llcpp::fuchsia::input::report::TouchType::TOUCHSCREEN;
} else {
return ParseResult::kParseNoCollection;
}
hid::Collection* finger_collection = nullptr;
for (size_t i = 0; i < hid_report_descriptor.input_count; i++) {
const hid::ReportField field = hid_report_descriptor.input_fields[i];
// Process touch points. Don't process the item if it's not part of a touch point collection.
if (field.col->usage !=
hid::USAGE(hid::usage::Page::kDigitizer, hid::usage::Digitizer::kFinger)) {
continue;
}
// If our collection pointer is different than the previous collection
// pointer, we have started a new collection and are on a new touch point
if (field.col != finger_collection) {
finger_collection = field.col;
num_contacts++;
}
if (num_contacts < 1) {
return ParseResult::kParseNoCollection;
}
if (num_contacts > kTouchMaxContacts) {
return kParseTooManyItems;
}
ContactConfig* contact = &contacts[num_contacts - 1];
if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kDigitizer, hid::usage::Digitizer::kContactID)) {
contact->contact_id = field.attr;
SetAxisFromAttribute(contact->contact_id, &descriptor.contacts[num_contacts - 1].contact_id);
}
if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kDigitizer, hid::usage::Digitizer::kTipSwitch)) {
contact->tip_switch = field.attr;
SetAxisFromAttribute(contact->tip_switch, &descriptor.contacts[num_contacts - 1].is_pressed);
}
if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kGenericDesktop, hid::usage::GenericDesktop::kX)) {
contact->position_x = field.attr;
SetAxisFromAttribute(contact->position_x, &descriptor.contacts[num_contacts - 1].position_x);
}
if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kGenericDesktop, hid::usage::GenericDesktop::kY)) {
contact->position_y = field.attr;
SetAxisFromAttribute(contact->position_y, &descriptor.contacts[num_contacts - 1].position_y);
}
if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kDigitizer, hid::usage::Digitizer::kTipPressure)) {
contact->pressure = field.attr;
SetAxisFromAttribute(contact->pressure, &descriptor.contacts[num_contacts - 1].pressure);
}
if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kDigitizer, hid::usage::Digitizer::kWidth)) {
contact->contact_width = field.attr;
SetAxisFromAttribute(contact->contact_width,
&descriptor.contacts[num_contacts - 1].contact_width);
}
if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kDigitizer, hid::usage::Digitizer::kHeight)) {
contact->contact_height = field.attr;
SetAxisFromAttribute(contact->contact_height,
&descriptor.contacts[num_contacts - 1].contact_height);
}
}
// No error, write to class members.
for (size_t i = 0; i < num_contacts; i++) {
contacts_[i] = contacts[i];
}
descriptor.max_contacts = static_cast<uint32_t>(num_contacts);
descriptor.num_contacts = num_contacts;
descriptor_ = descriptor;
report_size_ = hid_report_descriptor.input_byte_sz;
report_id_ = hid_report_descriptor.report_id;
return kParseOk;
}
ReportDescriptor Touch::GetDescriptor() {
ReportDescriptor report_descriptor = {};
report_descriptor.descriptor = descriptor_;
return report_descriptor;
}
ParseResult Touch::ParseReport(const uint8_t* data, size_t len, Report* report) {
TouchReport touch_report = {};
if (len != report_size_) {
return kParseReportSizeMismatch;
}
double value_out;
// Extract each touch item.
size_t contact_num = 0;
for (size_t i = 0; i < descriptor_.num_contacts; i++) {
ContactReport& contact = touch_report.contacts[contact_num];
if (descriptor_.contacts[i].is_pressed.enabled) {
if (hid::ExtractAsUnitType(data, len, contacts_[i].tip_switch, &value_out)) {
contact.is_pressed = static_cast<bool>(value_out);
contact.has_is_pressed = true;
if (!contact.is_pressed) {
continue;
}
}
}
contact_num++;
if (descriptor_.contacts[i].contact_id.enabled) {
// Some touchscreens we support mistakenly set the logical range to 0-1 for the
// tip switch and then never reset the range for the contact id. For this reason,
// we have to do an "unconverted" extraction.
if (hid::ExtractUint(data, len, contacts_[i].contact_id, &contact.contact_id)) {
contact.has_contact_id = true;
}
}
if (descriptor_.contacts[i].position_x.enabled) {
if (hid::ExtractAsUnitType(data, len, contacts_[i].position_x, &value_out)) {
contact.position_x = static_cast<int64_t>(value_out);
contact.has_position_x = true;
}
}
if (descriptor_.contacts[i].position_y.enabled) {
if (hid::ExtractAsUnitType(data, len, contacts_[i].position_y, &value_out)) {
contact.position_y = static_cast<int64_t>(value_out);
contact.has_position_y = true;
}
}
if (descriptor_.contacts[i].pressure.enabled) {
if (hid::ExtractAsUnitType(data, len, contacts_[i].pressure, &value_out)) {
contact.pressure = static_cast<int64_t>(value_out);
contact.has_pressure = true;
}
}
if (descriptor_.contacts[i].contact_width.enabled) {
if (hid::ExtractAsUnitType(data, len, contacts_[i].contact_width, &value_out)) {
contact.contact_width = static_cast<int64_t>(value_out);
contact.has_contact_width = true;
}
}
if (descriptor_.contacts[i].contact_height.enabled) {
if (hid::ExtractAsUnitType(data, len, contacts_[i].contact_height, &value_out)) {
contact.contact_height = static_cast<int64_t>(value_out);
contact.has_contact_height = true;
}
}
}
touch_report.num_contacts = contact_num;
// Now that we can't fail, set the real report.
report->report = touch_report;
return kParseOk;
}
} // namespace hid_input_report