| // 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 "input_touch.h" | |
| #include <lib/ddk/debug.h> | |
| #include <cinttypes> | |
| #include <fbl/algorithm.h> | |
| namespace virtio { | |
| namespace { | |
| constexpr fuchsia_input::wire::Axis CreateNonNegativeAxis(int64_t max_micrometer) { | |
| return { | |
| .range = {.min = 0, .max = max_micrometer}, | |
| .unit = | |
| { | |
| .type = fuchsia_input::wire::UnitType::kMeters, | |
| .exponent = -6, | |
| }, | |
| }; | |
| } | |
| } // namespace | |
| void TouchReport::ToFidlInputReport( | |
| fidl::WireTableBuilder<::fuchsia_input_report::wire::InputReport>& input_report, | |
| fidl::AnyArena& allocator) const { | |
| size_t count = 0; | |
| fidl::VectorView<fuchsia_input_report::wire::ContactInputReport> contact_rpt(allocator, | |
| kMaxTouchPoints); | |
| for (uint32_t i = 0; i < kMaxTouchPoints; i++) { | |
| if (!contacts[i].exists) { | |
| continue; | |
| } | |
| contact_rpt[count++] = fuchsia_input_report::wire::ContactInputReport::Builder(allocator) | |
| .contact_id(i) | |
| .position_x(contacts[i].x) | |
| .position_y(contacts[i].y) | |
| .Build(); | |
| } | |
| contact_rpt.set_size(count); | |
| input_report.event_time(event_time.get()) | |
| .touch(fuchsia_input_report::wire::TouchInputReport::Builder(allocator) | |
| .contacts(contact_rpt) | |
| .Build()); | |
| } | |
| fuchsia_input_report::wire::DeviceDescriptor HidTouch::GetDescriptor(fidl::AnyArena& allocator) { | |
| auto device_info = fuchsia_input_report::wire::DeviceInformation::Builder(allocator); | |
| device_info.vendor_id(static_cast<uint32_t>(fuchsia_input_report::wire::VendorId::kGoogle)); | |
| device_info.product_id( | |
| static_cast<uint32_t>(fuchsia_input_report::wire::VendorGoogleProductId::kVirtioTouchscreen)); | |
| if (!product_name_.empty()) { | |
| device_info.product_name(allocator, std::string_view(product_name_)); | |
| } | |
| if (!serial_number_.empty()) { | |
| device_info.serial_number(allocator, std::string_view(serial_number_)); | |
| } | |
| fidl::VectorView<fuchsia_input_report::wire::ContactInputDescriptor> contacts(allocator, | |
| kMaxTouchPoints); | |
| for (auto& c : contacts) { | |
| c = fuchsia_input_report::wire::ContactInputDescriptor::Builder(allocator) | |
| .position_x(CreateNonNegativeAxis(kXPhysicalMaxMicrometer)) | |
| .position_y(CreateNonNegativeAxis(kYPhysicalMaxMicrometer)) | |
| .Build(); | |
| } | |
| const auto input = fuchsia_input_report::wire::TouchInputDescriptor::Builder(allocator) | |
| .touch_type(fuchsia_input_report::wire::TouchType::kTouchscreen) | |
| .max_contacts(kMaxTouchPoints) | |
| .contacts(contacts) | |
| .Build(); | |
| const auto touch = | |
| fuchsia_input_report::wire::TouchDescriptor::Builder(allocator).input(input).Build(); | |
| return fuchsia_input_report::wire::DeviceDescriptor::Builder(allocator) | |
| .device_information(device_info.Build()) | |
| .touch(touch) | |
| .Build(); | |
| } | |
| void HidTouch::ReceiveEvent(virtio_input_event_t* event) { | |
| if (event->type == VIRTIO_INPUT_EV_ABS) { | |
| if (event->code == VIRTIO_INPUT_EV_MT_SLOT) { | |
| if (event->value >= kMaxTouchPoints) { | |
| zxlogf(ERROR, | |
| "Touch input finger ID (%" PRIu32 ") exceeds the maximum finger ID supported %d", | |
| event->value, kMaxTouchPoints - 1); | |
| active_finger_index_ = -1; | |
| return; | |
| } | |
| active_finger_index_ = event->value; | |
| } | |
| if (active_finger_index_ < 0 || active_finger_index_ >= kMaxTouchPoints) { | |
| return; | |
| } | |
| if (event->code == VIRTIO_INPUT_EV_MT_TRACKING_ID) { | |
| // If tracking id is -1 we have to remove the finger from being tracked. | |
| report_.contacts[active_finger_index_].exists = static_cast<int32_t>(event->value) != -1; | |
| } else if (event->code == VIRTIO_INPUT_EV_MT_POSITION_X) { | |
| // By guaranteeing `event->value` <= max, the product will be <= kPhysicalMax, where both | |
| // kPhysicalMax and the product are int64_t, so we are guaranteed to not overflow. | |
| ZX_DEBUG_ASSERT(event->value <= x_info_.max); | |
| report_.contacts[active_finger_index_].x = | |
| event->value * kXPhysicalMaxMicrometer / x_info_.max; | |
| } else if (event->code == VIRTIO_INPUT_EV_MT_POSITION_Y) { | |
| // By guaranteeing `event->value` <= max, the product will be <= kPhysicalMax, where both | |
| // kPhysicalMax and the product are int64_t, so we are guaranteed to not overflow. | |
| ZX_DEBUG_ASSERT(event->value <= y_info_.max); | |
| report_.contacts[active_finger_index_].y = | |
| event->value * kYPhysicalMaxMicrometer / y_info_.max; | |
| } | |
| } | |
| } | |
| } // namespace virtio |