| // Copyright 2022 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/input_sample/input_sample.h" |
| |
| #include <fidl/fuchsia.component.decl/cpp/wire.h> |
| #include <fidl/fuchsia.driver.compat/cpp/wire.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/driver2/record_cpp.h> |
| |
| #include <unistd.h> |
| |
| namespace fdf { |
| using namespace fuchsia_driver_framework; |
| } // namespace fdf |
| |
| namespace input_sample { |
| |
| namespace { |
| |
| namespace fcd = fuchsia_component_decl; |
| namespace fio = fuchsia_io; |
| |
| zx::status<fidl::ClientEnd<fuchsia_driver_compat::Device>> ConnectToParentDevice( |
| const driver::Namespace* ns, std::string_view name) { |
| auto result = ns->OpenService<fuchsia_driver_compat::Service>(name); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| return result.value().connect_device(); |
| } |
| |
| constexpr uint8_t kMouseButtonCount = 3; |
| |
| } // namespace |
| |
| void SampleInputReport::ToFidlInputReport( |
| fidl::WireTableBuilder<::fuchsia_input_report::wire::InputReport>& input_report, |
| fidl::AnyArena& arena) { |
| std::vector<uint8_t> pressed_buttons; |
| for (uint8_t i = 0; i < kMouseButtonCount; i++) { |
| if (buttons & (1 << i)) { |
| pressed_buttons.push_back(i + 1); |
| } |
| } |
| fidl::VectorView<uint8_t> buttons_vec(arena, pressed_buttons.size()); |
| size_t idx = 0; |
| for (const auto& button : pressed_buttons) { |
| buttons_vec[idx++] = button; |
| } |
| |
| auto mouse_input_rpt = fuchsia_input_report::wire::MouseInputReport::Builder(arena); |
| mouse_input_rpt.pressed_buttons(buttons_vec); |
| mouse_input_rpt.movement_x(rel_x); |
| mouse_input_rpt.movement_y(rel_y); |
| |
| input_report.mouse(mouse_input_rpt.Build()); |
| input_report.event_time(event_time.get()); |
| } |
| |
| void InputSampleDriver::SendFakeReport() { |
| // Update fake mouse report |
| report_.event_time = zx::time(zx_clock_get_monotonic()); |
| report_.buttons++; |
| report_.rel_x++; |
| report_.rel_y++; |
| |
| // Send fake mouse report |
| input_report_readers_.SendReportToAllReaders(report_); |
| |
| // Run again in 1 second |
| async::PostDelayedTask( |
| dispatcher_, |
| [this]() mutable { |
| SendFakeReport(); |
| }, |
| zx::sec(1)); |
| } |
| |
| // static |
| zx::status<std::unique_ptr<InputSampleDriver>> InputSampleDriver::Start( |
| fdf::wire::DriverStartArgs& start_args, fdf::UnownedDispatcher dispatcher, |
| fidl::WireSharedClient<fdf::Node> node, driver::Namespace ns, driver::Logger logger) { |
| auto driver = std::make_unique<InputSampleDriver>(dispatcher->async_dispatcher(), std::move(node), |
| std::move(ns), std::move(logger)); |
| auto result = driver->Run(std::move(start_args.outgoing_dir())); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| return zx::ok(std::move(driver)); |
| } |
| |
| zx::status<> InputSampleDriver::Run(fidl::ServerEnd<fio::Directory> outgoing_dir) { |
| // Connect to DevfsExporter. |
| auto exporter = ns_.Connect<fuchsia_device_fs::Exporter>(); |
| if (exporter.status_value() != ZX_OK) { |
| return exporter.take_error(); |
| } |
| exporter_ = fidl::WireClient(std::move(*exporter), dispatcher_); |
| |
| // Connect to parent. |
| auto parent = ConnectToParentDevice(&ns_, "default"); |
| if (parent.status_value() != ZX_OK) { |
| FDF_SLOG(ERROR, "Failed to connect to parent", KV("status", parent.status_string())); |
| return parent.take_error(); |
| } |
| |
| auto result = fidl::WireCall(*parent)->GetTopologicalPath(); |
| if (!result.ok()) { |
| return zx::error(result.status()); |
| } |
| |
| std::string path(result->path.data(), result->path.size()); |
| |
| // TODO (fxbug.dev/103199): migrate off this when fuchsia.input.report InputDevice is discoverable |
| auto status = outgoing_.AddProtocol<fuchsia_input_report::InputDevice>(this, Name()); |
| if (status.status_value() != ZX_OK) { |
| return status; |
| } |
| |
| path.append("/"); |
| path.append(Name()); |
| |
| FDF_LOG(INFO, "Exporting device to: %s", path.data()); |
| |
| // Serve a connection to outgoing. |
| auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>(); |
| if (endpoints.is_error()) { |
| return endpoints.take_error(); |
| } |
| { |
| auto status = outgoing_.Serve(std::move(endpoints->server)); |
| if (status.is_error()) { |
| return status.take_error(); |
| } |
| } |
| |
| // Export our protocol. |
| exporter_ |
| ->Export(std::move(endpoints->client), |
| // TODO (fxbug.dev/103199): migrate off this when fuchsia.input.report |
| // InputDevice is discoverable |
| fidl::StringView::FromExternal(std::string("svc/").append(Name())), |
| fidl::StringView::FromExternal(path), |
| /* INPUT_REPORT */ 26) // TODO (fxbug.dev/98831): Non-hardcoded proto_id |
| .Then([this](fidl::WireUnownedResult<fuchsia_device_fs::Exporter::Export>& result) { |
| if (!result.ok()) { |
| FDF_LOG(ERROR, "Exporting failed with: %s", result.status_string()); |
| } |
| }); |
| |
| status = outgoing_.Serve(std::move(outgoing_dir)); |
| if (status.is_error()) { |
| FDF_LOG(ERROR, "Could not serve: %d", status.error_value()); |
| return status; |
| } |
| |
| // Start sending fake reports |
| SendFakeReport(); |
| |
| return zx::ok(); |
| } |
| |
| void InputSampleDriver::GetInputReportsReader(GetInputReportsReaderRequestView request, |
| GetInputReportsReaderCompleter::Sync& completer) { |
| zx_status_t status = input_report_readers_.CreateReader(dispatcher_, std::move(request->reader)); |
| if (status != ZX_OK) { |
| FDF_LOG(ERROR, "Could not create input report reader: %d", status); |
| } |
| } |
| |
| void InputSampleDriver::GetDescriptor(GetDescriptorRequestView request, |
| GetDescriptorCompleter::Sync& completer) { |
| fidl::Arena allocator; |
| auto descriptor = fuchsia_input_report::wire::DeviceDescriptor::Builder(allocator); |
| |
| fuchsia_input_report::wire::DeviceInfo device_info; |
| // We pick random VID/PIDs here, but these should be taken from the HID device, |
| // or defined in fuchsia.input.report/device_ids.fidl |
| device_info.vendor_id = 0x12345678; |
| device_info.product_id = 0x87654321; |
| |
| fidl::VectorView<uint8_t> buttons(allocator, kMouseButtonCount); |
| buttons[0] = 0x01; |
| buttons[1] = 0x02; |
| buttons[2] = 0x03; |
| |
| constexpr fuchsia_input_report::wire::Axis movement_x{ |
| .range = {.min = -127, .max = 127}, |
| .unit = {.type = fuchsia_input_report::wire::UnitType::kNone, .exponent = 0}, |
| }; |
| constexpr fuchsia_input_report::wire::Axis movement_y{ |
| .range = {.min = -127, .max = 127}, |
| .unit = {.type = fuchsia_input_report::wire::UnitType::kNone, .exponent = 0}, |
| }; |
| |
| auto mouse_in_desc = fuchsia_input_report::wire::MouseInputDescriptor::Builder(allocator); |
| mouse_in_desc.buttons(buttons); |
| mouse_in_desc.movement_x(movement_x); |
| mouse_in_desc.movement_y(movement_y); |
| |
| auto mouse_descriptor = fuchsia_input_report::wire::MouseDescriptor::Builder(allocator); |
| mouse_descriptor.input(mouse_in_desc.Build()); |
| descriptor.mouse(mouse_descriptor.Build()); |
| descriptor.device_info(device_info); |
| |
| completer.Reply(descriptor.Build()); |
| } |
| |
| } // namespace input_sample |
| |
| FUCHSIA_DRIVER_RECORD_CPP_V1(input_sample::InputSampleDriver); |