blob: de2f5df3bb4b173dc53967db32935195e917c7b0 [file] [log] [blame]
// 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);