blob: 0119f28e8a2b0d149e620b14e474956003fd6fd0 [file] [log] [blame]
// Copyright 2013 The Flutter 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 "pointer_injector_delegate.h"
#include "flutter/fml/logging.h"
namespace flutter_runner {
using fup_Config = fuchsia::ui::pointerinjector::Config;
using fup_Context = fuchsia::ui::pointerinjector::Context;
using fup_Data = fuchsia::ui::pointerinjector::Data;
using fup_DeviceType = fuchsia::ui::pointerinjector::DeviceType;
using fup_DispatchPolicy = fuchsia::ui::pointerinjector::DispatchPolicy;
using fup_Event = fuchsia::ui::pointerinjector::Event;
using fup_EventPhase = fuchsia::ui::pointerinjector::EventPhase;
using fup_PointerSample = fuchsia::ui::pointerinjector::PointerSample;
using fup_Target = fuchsia::ui::pointerinjector::Target;
using fup_Viewport = fuchsia::ui::pointerinjector::Viewport;
using fuv_ViewRef = fuchsia::ui::views::ViewRef;
const auto fup_MAX_INJECT = fuchsia::ui::pointerinjector::MAX_INJECT;
namespace {
// clang-format off
static constexpr std::array<float, 9> kIdentityMatrix = {
1, 0, 0, // column one
0, 1, 0, // column two
0, 0, 1, // column three
};
// clang-format on
} // namespace
bool PointerInjectorDelegate::HandlePlatformMessage(
rapidjson::Value request,
fml::RefPtr<flutter::PlatformMessageResponse> response) {
if (!registry_->is_bound()) {
FML_LOG(WARNING)
<< "Lost connection to fuchsia.ui.pointerinjector.Registry";
return false;
}
auto method = request.FindMember("method");
if (method == request.MemberEnd() || !method->value.IsString()) {
return false;
}
if (method->value == kPointerInjectorMethodPrefix) {
auto args_it = request.FindMember("args");
if (args_it == request.MemberEnd() || !args_it->value.IsObject()) {
FML_LOG(ERROR) << "No arguments found.";
return false;
}
const auto& args = args_it->value;
auto view_id = args.FindMember("viewId");
if (!view_id->value.IsUint64()) {
FML_LOG(ERROR) << "Argument 'viewId' is not a uint64";
return false;
}
auto id = view_id->value.GetUint64();
auto phase = args.FindMember("phase");
if (!phase->value.IsInt()) {
FML_LOG(ERROR) << "Argument 'phase' is not a int";
return false;
}
auto pointer_x = args.FindMember("x");
if (!pointer_x->value.IsFloat() && !pointer_x->value.IsInt()) {
FML_LOG(ERROR) << "Argument 'Pointer.X' is not a float";
return false;
}
auto pointer_y = args.FindMember("y");
if (!pointer_y->value.IsFloat() && !pointer_y->value.IsInt()) {
FML_LOG(ERROR) << "Argument 'Pointer.Y' is not a float";
return false;
}
auto pointer_id = args.FindMember("pointerId");
if (!pointer_id->value.IsUint()) {
FML_LOG(ERROR) << "Argument 'pointerId' is not a uint32";
return false;
}
auto trace_flow_id = args.FindMember("traceFlowId");
if (!trace_flow_id->value.IsInt()) {
FML_LOG(ERROR) << "Argument 'traceFlowId' is not a int";
return false;
}
// For GFX, the viewRef for the view is provided through the platform
// message. For flatland, the viewRef is provided through |OnCreateView|.
std::optional<fuv_ViewRef> view_ref;
if (!is_flatland_) {
auto view_ref_arg = args.FindMember("viewRef");
if (!view_ref_arg->value.IsUint64()) {
FML_LOG(ERROR) << "Argument 'viewRef' is not a uint64";
return false;
}
zx_handle_t handle = view_ref_arg->value.GetUint64();
zx_handle_t out_handle;
zx_status_t status =
zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, &out_handle);
if (status != ZX_OK) {
FML_LOG(ERROR) << "Argument 'viewRef' is not valid";
return false;
}
auto ref = fuv_ViewRef({
.reference = zx::eventpair(out_handle),
});
view_ref = std::move(ref);
}
auto width = args.FindMember("logicalWidth");
if (!width->value.IsFloat() && !width->value.IsInt()) {
FML_LOG(ERROR) << "Argument 'logicalWidth' is not a float";
return false;
}
auto height = args.FindMember("logicalHeight");
if (!height->value.IsFloat() && !height->value.IsInt()) {
FML_LOG(ERROR) << "Argument 'logicalHeight' is not a float";
return false;
}
auto timestamp = args.FindMember("timestamp");
if (!timestamp->value.IsInt() && !timestamp->value.IsUint64()) {
FML_LOG(ERROR) << "Argument 'timestamp' is not a int";
return false;
}
PointerInjectorRequest request = {
.x = pointer_x->value.GetFloat(),
.y = pointer_y->value.GetFloat(),
.pointer_id = pointer_id->value.GetUint(),
.phase = static_cast<fup_EventPhase>(phase->value.GetInt()),
.trace_flow_id = trace_flow_id->value.GetUint64(),
.view_ref = std::move(view_ref),
.logical_size = {width->value.GetFloat(), height->value.GetFloat()},
.timestamp = timestamp->value.GetInt()};
// Inject the pointer event if the view has been created.
if (valid_views_.count(id) > 0) {
valid_views_.at(id).InjectEvent(std::move(request));
Complete(std::move(response), "[0]");
} else {
return false;
}
} else {
return false;
}
// All of our methods complete the platform message response.
return true;
}
void PointerInjectorDelegate::OnCreateView(
uint64_t view_id,
std::optional<fuv_ViewRef> view_ref) {
FML_CHECK(valid_views_.count(view_id) == 0);
auto [_, success] = valid_views_.try_emplace(
view_id, registry_, host_view_ref_, std::move(view_ref));
FML_CHECK(success);
}
fup_Event PointerInjectorDelegate::ExtractPointerEvent(
PointerInjectorRequest request) {
fup_Event event;
event.set_timestamp(request.timestamp);
event.set_trace_flow_id(request.trace_flow_id);
fup_PointerSample pointer_sample;
pointer_sample.set_pointer_id(request.pointer_id);
pointer_sample.set_phase(request.phase);
pointer_sample.set_position_in_viewport({request.x, request.y});
fup_Data data;
data.set_pointer_sample(std::move(pointer_sample));
event.set_data(std::move(data));
return event;
}
void PointerInjectorDelegate::Complete(
fml::RefPtr<flutter::PlatformMessageResponse> response,
std::string value) {
if (response) {
response->Complete(std::make_unique<fml::DataMapping>(
std::vector<uint8_t>(value.begin(), value.end())));
}
}
void PointerInjectorDelegate::PointerInjectorEndpoint::InjectEvent(
PointerInjectorRequest request) {
if (!registered_) {
RegisterInjector(request);
}
auto event = ExtractPointerEvent(std::move(request));
// Add the event to |injector_events_| and dispatch it to the view.
EnqueueEvent(std::move(event));
DispatchPendingEvents();
}
void PointerInjectorDelegate::PointerInjectorEndpoint::DispatchPendingEvents() {
// Return if there is already a |fuchsia.ui.pointerinjector.Device.Inject|
// call in flight. The new pointer events will be dispatched once the
// in-progress call terminates.
if (injection_in_flight_) {
return;
}
// Dispatch the events present in |injector_events_|. Note that we recursively
// call |DispatchPendingEvents| in the callback passed to the
// |f.u.p.Device.Inject| call. This ensures that there is only one
// |f.u.p.Device.Inject| call at a time. If a new pointer event comes when
// there is a |f.u.p.Device.Inject| call in progress, it gets buffered in
// |injector_events_| and is picked up later.
if (!injector_events_.empty()) {
auto events = std::move(injector_events_.front());
injector_events_.pop();
injection_in_flight_ = true;
FML_CHECK(device_.is_bound());
FML_CHECK(events.size() <= fup_MAX_INJECT);
device_->Inject(std::move(events), [weak = weak_factory_.GetWeakPtr()] {
if (!weak) {
FML_LOG(WARNING) << "Use after free attempted.";
return;
}
weak->injection_in_flight_ = false;
weak->DispatchPendingEvents();
});
}
}
void PointerInjectorDelegate::PointerInjectorEndpoint::EnqueueEvent(
fup_Event event) {
// Add |event| in |injector_events_| keeping in mind that the vector size does
// not exceed |fup_MAX_INJECT|.
if (!injector_events_.empty() &&
injector_events_.back().size() < fup_MAX_INJECT) {
injector_events_.back().push_back(std::move(event));
} else {
std::vector<fup_Event> vec;
vec.reserve(fup_MAX_INJECT);
vec.push_back(std::move(event));
injector_events_.push(std::move(vec));
}
}
void PointerInjectorDelegate::PointerInjectorEndpoint::RegisterInjector(
const PointerInjectorRequest& request) {
if (registered_) {
return;
}
fup_Config config;
config.set_device_id(1);
config.set_device_type(fup_DeviceType::TOUCH);
config.set_dispatch_policy(fup_DispatchPolicy::EXCLUSIVE_TARGET);
fup_Context context;
fuv_ViewRef context_clone;
fidl::Clone(*host_view_ref_, &context_clone);
context.set_view(std::move(context_clone));
config.set_context(std::move(context));
FML_CHECK(request.view_ref.has_value() || view_ref_.has_value());
fup_Target target;
fuv_ViewRef target_clone;
// GFX.
if (request.view_ref.has_value()) {
fidl::Clone(*request.view_ref, &target_clone);
}
// Flatland.
else {
fidl::Clone(*view_ref_, &target_clone);
}
target.set_view(std::move(target_clone));
config.set_target(std::move(target));
fup_Viewport viewport;
viewport.set_viewport_to_context_transform(kIdentityMatrix);
std::array<std::array<float, 2>, 2> extents{
{/*min*/ {0, 0},
/*max*/ {request.logical_size[0], request.logical_size[1]}}};
viewport.set_extents(std::move(extents));
config.set_viewport(std::move(viewport));
FML_CHECK(registry_->is_bound());
(*registry_)->Register(std::move(config), device_.NewRequest(), [] {});
registered_ = true;
}
} // namespace flutter_runner