blob: be500eafb8a174f0d20fff83f94ba0537dfc07dc [file] [log] [blame]
// Copyright 2020 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_report_reader/input_interpreter.h"
#include <fuchsia/ui/input/cpp/fidl.h>
#include <lib/fidl/cpp/clone.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/trace/event.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <zircon/errors.h>
#include <zircon/types.h>
namespace ui_input {
namespace {
fuchsia::ui::input::Axis ConvertAxis(fuchsia::input::report::Axis axis) {
fuchsia::ui::input::Axis a = {};
a.range.min = static_cast<int32_t>(axis.range.min);
a.range.max = static_cast<int32_t>(axis.range.max);
return a;
}
// Sets the fuchsia::ui::input Mouse button bit vector.
// prev_buttons represents the current button bit vector.
// button_id is the new button to set.
// The function returns the new button bit vector.
uint32_t SetMouseButton(uint32_t prev_buttons, uint8_t button_id) {
if (button_id == 0 || button_id > 32) {
return prev_buttons;
}
return prev_buttons | (1 << (button_id - 1));
}
} // namespace
InputInterpreter::InputInterpreter(InputReaderBase* base,
fuchsia::ui::input::InputDeviceRegistry* registry,
std::string name)
: base_(base), registry_(registry), name_(name) {}
InputInterpreter::~InputInterpreter() {}
void InputInterpreter::DispatchReport(const fuchsia::ui::input::InputDevicePtr& device,
fuchsia::ui::input::InputReport report) {
report.trace_id = TRACE_NONCE();
TRACE_FLOW_BEGIN("input", "hid_read_to_listener", report.trace_id);
device->DispatchReport(std::move(report));
}
std::unique_ptr<InputInterpreter> InputInterpreter::Create(
InputReaderBase* base, zx::channel channel, fuchsia::ui::input::InputDeviceRegistry* registry,
std::string name) {
// Using `new` to access a non-public constructor.
auto interpreter = std::unique_ptr<InputInterpreter>(new InputInterpreter(base, registry, name));
zx_status_t status = interpreter->device_.Bind(std::move(channel));
if (status != ZX_OK) {
FX_LOGS(ERROR) << "InputInterpreter::Create: Bind error: " << status;
return nullptr;
}
interpreter->Initialize();
return interpreter;
}
void InputInterpreter::Initialize() {
device_.set_error_handler([this](zx_status_t status) {
if (status != ZX_ERR_PEER_CLOSED) {
FX_LOGS(ERROR) << "InputInterpreter: Device error: " << status;
}
base_->RemoveDevice(this);
});
RegisterDevices();
}
void InputInterpreter::RegisterConsumerControl(
const fuchsia::input::report::DeviceDescriptor& descriptor) {
fuchsia::ui::input::DeviceDescriptor ui_descriptor;
if (descriptor.has_device_info()) {
auto info = std::make_unique<fuchsia::ui::input::DeviceInfo>();
info->vendor_id = descriptor.device_info().vendor_id;
info->product_id = descriptor.device_info().product_id;
info->version = descriptor.device_info().version;
ui_descriptor.device_info = std::move(info);
}
auto media_buttons = std::make_unique<fuchsia::ui::input::MediaButtonsDescriptor>();
if (descriptor.consumer_control().input().has_buttons()) {
for (auto button : descriptor.consumer_control().input().buttons()) {
switch (button) {
case fuchsia::input::report::ConsumerControlButton::VOLUME_UP:
media_buttons->buttons |= fuchsia::ui::input::kVolumeUp;
break;
case fuchsia::input::report::ConsumerControlButton::VOLUME_DOWN:
media_buttons->buttons |= fuchsia::ui::input::kVolumeDown;
break;
case fuchsia::input::report::ConsumerControlButton::MIC_MUTE:
media_buttons->buttons |= fuchsia::ui::input::kMicMute;
break;
case fuchsia::input::report::ConsumerControlButton::PAUSE:
media_buttons->buttons |= fuchsia::ui::input::kPause;
break;
case fuchsia::input::report::ConsumerControlButton::REBOOT:
media_buttons->buttons |= fuchsia::ui::input::kReset;
break;
case fuchsia::input::report::ConsumerControlButton::CAMERA_DISABLE:
media_buttons->buttons |= fuchsia::ui::input::kCameraDisable;
break;
default:
break;
}
}
}
ui_descriptor.media_buttons = std::move(media_buttons);
registry_->RegisterDevice(std::move(ui_descriptor), consumer_control_ptr_.NewRequest());
}
void InputInterpreter::RegisterMouse(const fuchsia::input::report::DeviceDescriptor& descriptor) {
fuchsia::ui::input::DeviceDescriptor ui_descriptor;
if (descriptor.has_device_info()) {
auto info = std::make_unique<fuchsia::ui::input::DeviceInfo>();
info->vendor_id = descriptor.device_info().vendor_id;
info->product_id = descriptor.device_info().product_id;
info->version = descriptor.device_info().version;
ui_descriptor.device_info = std::move(info);
}
auto mouse = std::make_unique<fuchsia::ui::input::MouseDescriptor>();
if (descriptor.mouse().input().has_movement_x()) {
mouse->rel_x = ConvertAxis(descriptor.mouse().input().movement_x());
}
if (descriptor.mouse().input().has_movement_y()) {
mouse->rel_y = ConvertAxis(descriptor.mouse().input().movement_y());
}
if (descriptor.mouse().input().has_buttons()) {
for (uint8_t button : descriptor.mouse().input().buttons()) {
mouse->buttons = SetMouseButton(mouse->buttons, button);
}
}
ui_descriptor.mouse = std::move(mouse);
registry_->RegisterDevice(std::move(ui_descriptor), mouse_ptr_.NewRequest());
}
void InputInterpreter::RegisterTouchscreen(
const fuchsia::input::report::DeviceDescriptor& descriptor) {
fuchsia::ui::input::DeviceDescriptor ui_descriptor;
if (descriptor.has_device_info()) {
auto info = std::make_unique<fuchsia::ui::input::DeviceInfo>();
info->vendor_id = descriptor.device_info().vendor_id;
info->product_id = descriptor.device_info().product_id;
info->version = descriptor.device_info().version;
ui_descriptor.device_info = std::move(info);
}
auto touch = std::make_unique<fuchsia::ui::input::TouchscreenDescriptor>();
if (descriptor.touch().input().has_contacts()) {
if (descriptor.touch().input().contacts()[0].has_position_x()) {
touch->x.range.min =
static_cast<int32_t>(descriptor.touch().input().contacts()[0].position_x().range.min);
touch->x.range.max =
static_cast<int32_t>(descriptor.touch().input().contacts()[0].position_x().range.max);
}
if (descriptor.touch().input().contacts()[0].has_position_y()) {
touch->y.range.min =
static_cast<int32_t>(descriptor.touch().input().contacts()[0].position_y().range.min);
touch->y.range.max =
static_cast<int32_t>(descriptor.touch().input().contacts()[0].position_y().range.max);
}
}
if (descriptor.touch().input().has_max_contacts()) {
touch->max_finger_id = descriptor.touch().input().max_contacts();
}
ui_descriptor.touchscreen = std::move(touch);
registry_->RegisterDevice(std::move(ui_descriptor), touch_ptr_.NewRequest());
}
void InputInterpreter::RegisterDevices() {
fuchsia::input::report::DeviceDescriptor descriptor;
device_->GetDescriptor([this](fuchsia::input::report::DeviceDescriptor descriptor) {
if (descriptor.has_touch() && descriptor.touch().has_input()) {
if (descriptor.touch().input().has_touch_type() &&
(descriptor.touch().input().touch_type() ==
fuchsia::input::report::TouchType::TOUCHSCREEN)) {
RegisterTouchscreen(descriptor);
}
}
if (descriptor.has_mouse() && descriptor.mouse().has_input()) {
RegisterMouse(descriptor);
}
if (descriptor.has_consumer_control() && descriptor.consumer_control().has_input()) {
RegisterConsumerControl(descriptor);
}
// Now that devices are registered we can start reading requests.
device_->GetInputReportsReader(reader_.NewRequest());
reader_.set_error_handler([this](zx_status_t status) {
if (status != ZX_ERR_PEER_CLOSED) {
FX_LOGS(ERROR) << "InputInterpreter: Reader error: " << status;
}
base_->RemoveDevice(this);
});
// Kick off the first Read, which will queue up the rest of the reads.
reader_->ReadInputReports([this](auto result) { ReadReports(std::move(result)); });
});
}
void InputInterpreter::DispatchTouchReport(const fuchsia::input::report::InputReport& report) {
fuchsia::ui::input::InputReport input_report;
if (report.has_event_time()) {
input_report.event_time = report.event_time();
}
auto input_touchscreen = std::make_unique<fuchsia::ui::input::TouchscreenReport>();
if (report.touch().has_contacts()) {
for (const fuchsia::input::report::ContactInputReport& contact : report.touch().contacts()) {
fuchsia::ui::input::Touch input_touch;
if (contact.has_contact_id()) {
input_touch.finger_id = contact.contact_id();
}
if (contact.has_position_x()) {
input_touch.x = static_cast<int32_t>(contact.position_x());
}
if (contact.has_position_y()) {
input_touch.y = static_cast<int32_t>(contact.position_y());
}
input_touchscreen->touches.push_back(std::move(input_touch));
}
}
input_report.touchscreen = std::move(input_touchscreen);
DispatchReport(touch_ptr_, std::move(input_report));
}
void InputInterpreter::DispatchMouseReport(const fuchsia::input::report::InputReport& report) {
fuchsia::ui::input::InputReport input_report;
if (report.has_event_time()) {
input_report.event_time = report.event_time();
}
auto input_mouse = std::make_unique<fuchsia::ui::input::MouseReport>();
if (report.mouse().has_movement_x()) {
input_mouse->rel_x = static_cast<uint32_t>(report.mouse().movement_x());
}
if (report.mouse().has_movement_y()) {
input_mouse->rel_y = static_cast<uint32_t>(report.mouse().movement_y());
}
if (report.mouse().has_pressed_buttons()) {
for (uint8_t button : report.mouse().pressed_buttons()) {
input_mouse->pressed_buttons = SetMouseButton(input_mouse->pressed_buttons, button);
}
}
input_report.mouse = std::move(input_mouse);
DispatchReport(mouse_ptr_, std::move(input_report));
}
void InputInterpreter::DispatchConsumerControlReport(
const fuchsia::input::report::InputReport& report) {
fuchsia::ui::input::InputReport input_report;
if (report.has_event_time()) {
input_report.event_time = report.event_time();
}
auto media_buttons = std::make_unique<fuchsia::ui::input::MediaButtonsReport>();
if (report.consumer_control().has_pressed_buttons()) {
for (const fuchsia::input::report::ConsumerControlButton& button :
report.consumer_control().pressed_buttons()) {
switch (button) {
case fuchsia::input::report::ConsumerControlButton::VOLUME_UP:
media_buttons->volume_up = true;
break;
case fuchsia::input::report::ConsumerControlButton::VOLUME_DOWN:
media_buttons->volume_down = true;
break;
case fuchsia::input::report::ConsumerControlButton::MIC_MUTE:
media_buttons->mic_mute = true;
break;
case fuchsia::input::report::ConsumerControlButton::PAUSE:
media_buttons->pause = true;
break;
case fuchsia::input::report::ConsumerControlButton::REBOOT:
media_buttons->reset = true;
break;
case fuchsia::input::report::ConsumerControlButton::CAMERA_DISABLE:
media_buttons->camera_disable = true;
break;
default:
break;
}
}
}
input_report.media_buttons = std::move(media_buttons);
DispatchReport(consumer_control_ptr_, std::move(input_report));
}
void InputInterpreter::ReadReports(
fuchsia::input::report::InputReportsReader_ReadInputReports_Result result) {
TRACE_DURATION("input", "input_report_reader Read");
if (result.is_err()) {
FX_LOGS(INFO) << "InputInterpreter: ReadInputReports received status code: " << result.err();
base_->RemoveDevice(this);
return;
}
if (base_->ActiveInput()) {
for (const auto& report : result.response().reports) {
if (report.has_trace_id()) {
TRACE_FLOW_END("input", "input_report", report.trace_id());
}
if (report.has_touch()) {
DispatchTouchReport(report);
}
if (report.has_consumer_control()) {
DispatchConsumerControlReport(report);
}
if (report.has_mouse()) {
DispatchMouseReport(report);
}
}
}
// Queue ourselves up again for another read.
reader_->ReadInputReports([this](auto result) { ReadReports(std::move(result)); });
}
} // namespace ui_input