blob: eea026720c7f791c2a17f52867524aae43e9680e [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/input_reader/stylus.h"
#include <stdint.h>
#include <stdio.h>
#include <vector>
#include <hid-parser/parser.h>
#include <hid-parser/report.h>
#include <hid-parser/units.h>
#include <hid-parser/usages.h>
#include "src/lib/fxl/logging.h"
#include "src/ui/lib/input_reader/device.h"
namespace ui_input {
bool Stylus::ParseReportDescriptor(const hid::ReportDescriptor& report_descriptor,
Descriptor* device_descriptor) {
hid::Attributes x = {};
hid::Attributes y = {};
hid::Attributes pressure = {};
hid::Attributes tip_switch = {};
hid::Attributes barrel_switch = {};
hid::Attributes invert = {};
hid::Attributes eraser = {};
hid::Attributes in_range = {};
uint32_t caps = 0;
for (size_t i = 0; i < report_descriptor.input_count; i++) {
const hid::ReportField& field = report_descriptor.input_fields[i];
if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kGenericDesktop, hid::usage::GenericDesktop::kX)) {
x = field.attr;
caps |= Capabilities::X;
} else if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kGenericDesktop, hid::usage::GenericDesktop::kY)) {
y = field.attr;
caps |= Capabilities::Y;
} else if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kDigitizer, hid::usage::Digitizer::kTipPressure)) {
pressure = field.attr;
caps |= Capabilities::PRESSURE;
} else if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kDigitizer, hid::usage::Digitizer::kBarrelSwitch)) {
barrel_switch = field.attr;
caps |= Capabilities::BARREL_SWITCH;
} else if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kDigitizer, hid::usage::Digitizer::kInvert)) {
invert = field.attr;
caps |= Capabilities::INVERT;
} else if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kDigitizer, hid::usage::Digitizer::kEraser)) {
eraser = field.attr;
caps |= Capabilities::ERASER;
} else if (field.attr.usage ==
hid::USAGE(hid::usage::Page::kDigitizer, hid::usage::Digitizer::kInRange)) {
in_range = field.attr;
caps |= Capabilities::IN_RANGE;
}
}
// No error, write to class members.
x_ = x;
y_ = y;
pressure_ = pressure;
tip_switch_ = tip_switch;
barrel_switch_ = barrel_switch;
invert_ = invert;
eraser_ = eraser;
in_range_ = in_range;
capabilities_ = caps;
report_size_ = report_descriptor.input_byte_sz;
report_id_ = report_descriptor.report_id;
device_descriptor->protocol = Protocol::Stylus;
device_descriptor->has_stylus = true;
device_descriptor->stylus_descriptor = fuchsia::ui::input::StylusDescriptor::New();
device_descriptor->stylus_descriptor->x.range.min = x_.phys_mm.min;
device_descriptor->stylus_descriptor->x.range.max = x_.phys_mm.max;
device_descriptor->stylus_descriptor->x.resolution = 1;
device_descriptor->stylus_descriptor->y.range.min = y_.phys_mm.min;
device_descriptor->stylus_descriptor->y.range.max = y_.phys_mm.max;
device_descriptor->stylus_descriptor->y.resolution = 1;
device_descriptor->stylus_descriptor->is_invertible =
((capabilities_ & Capabilities::INVERT) != 0);
device_descriptor->stylus_descriptor->buttons = 0;
if (capabilities_ & Capabilities::BARREL_SWITCH) {
device_descriptor->stylus_descriptor->buttons |= fuchsia::ui::input::kStylusBarrel;
}
return true;
}
bool Stylus::ParseReport(const uint8_t* data, size_t len, fuchsia::ui::input::InputReport* report) {
FXL_CHECK(report);
FXL_CHECK(report->stylus);
double x = 0;
double y = 0;
double pressure = 0;
bool tip_switch = false;
bool barrel_switch = false;
bool invert = false;
bool eraser = false;
bool in_range = false;
uint8_t tmp_val;
if (len != report_size_) {
FXL_LOG(INFO) << "Stylus HID Report is not correct size, (" << len << " != " << report_size_
<< ")";
return false;
}
// rel_x and rel_y will have units of 10^-5 meters if the report defines units
hid::Unit length_unit = {};
length_unit.exp = -5;
hid::unit::SetSystem(length_unit, hid::unit::System::si_linear);
hid::unit::SetLengthExp(length_unit, 1);
if (capabilities_ & Capabilities::X) {
if (!hid::ExtractAsUnit(data, len, x_, &x)) {
FXL_LOG(INFO) << "Stylus report: Failed to parse X";
return false;
}
// If this returns true, scan_time was converted. If it returns false,
// scan_time is unchanged. Either way we return successfully.
hid::unit::ConvertUnits(x_.unit, x, length_unit, &x);
}
if (capabilities_ & Capabilities::Y) {
if (!hid::ExtractAsUnit(data, len, y_, &y)) {
FXL_LOG(INFO) << "Stylus report: Failed to parse Y";
return false;
}
// If this returns true, scan_time was converted. If it returns false,
// scan_time is unchanged. Either way we return successfully.
hid::unit::ConvertUnits(y_.unit, y, length_unit, &y);
}
if (capabilities_ & Capabilities::PRESSURE) {
if (!hid::ExtractAsUnit(data, len, pressure_, &pressure)) {
FXL_LOG(INFO) << "Stylus report: Failed to parse PRESSURE";
return false;
}
}
if (capabilities_ & Capabilities::TIP_SWITCH) {
if (!hid::ExtractUint(data, len, tip_switch_, &tmp_val)) {
FXL_LOG(INFO) << "Stylus report: Failed to parse TIP_SWITCH";
return false;
}
tip_switch = (tmp_val == 1);
}
if (capabilities_ & Capabilities::BARREL_SWITCH) {
if (!hid::ExtractUint(data, len, barrel_switch_, &tmp_val)) {
FXL_LOG(INFO) << "Stylus report: Failed to parse BARREL_SWITCH";
return false;
}
barrel_switch = (tmp_val == 1);
}
if (capabilities_ & Capabilities::INVERT) {
if (!hid::ExtractUint(data, len, invert_, &tmp_val)) {
FXL_LOG(INFO) << "Stylus report: Failed to parse INVERT";
return false;
}
invert = (tmp_val == 1);
}
if (capabilities_ & Capabilities::ERASER) {
if (!hid::ExtractUint(data, len, eraser_, &tmp_val)) {
FXL_LOG(INFO) << "Stylus report: Failed to parse ERASER";
return false;
}
eraser = (tmp_val == 1);
}
if (capabilities_ & Capabilities::IN_RANGE) {
if (!hid::ExtractUint(data, len, in_range_, &tmp_val)) {
FXL_LOG(INFO) << "Stylus report: Failed to parse IN_RANGE";
return false;
}
in_range = (tmp_val == 1);
}
// Now that we can't fail, set the real report.
report->stylus->x = x;
report->stylus->y = y;
report->stylus->pressure = pressure;
report->stylus->is_in_contact = in_range && (tip_switch || eraser);
report->stylus->is_inverted = invert;
if (barrel_switch) {
report->stylus->pressed_buttons |= fuchsia::ui::input::kStylusBarrel;
}
return true;
}
} // namespace ui_input