| // 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::FACTORY_RESET: |
| 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::FACTORY_RESET: |
| 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 |