blob: cc083d6ff9e0b24e1e9607bc18f3ac435b52af11 [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 "input_touch.h"
#include <lib/ddk/debug.h>
#include <cinttypes>
#include <fbl/algorithm.h>
namespace virtio {
namespace {
constexpr fuchsia_input_report::wire::Axis CreateNonNegativeAxis(int64_t max_micrometer) {
return {
.range = {.min = 0, .max = max_micrometer},
.unit =
{
.type = fuchsia_input_report::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));
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