blob: df8c747720de26089f8743feef0c8e384cff0ce8 [file] [log] [blame]
// Copyright 2021 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/scenic/lib/input/pointerinjector_registry.h"
#include <lib/syslog/cpp/macros.h>
#include "src/ui/scenic/lib/utils/helpers.h"
namespace scenic_impl::input {
namespace {
bool IsValidConfig(const fuchsia::ui::pointerinjector::Config& config) {
if (!config.has_device_id() || !config.has_device_type() || !config.has_context() ||
!config.has_target() || !config.has_viewport() || !config.has_dispatch_policy()) {
FX_LOGS(ERROR) << "InjectorRegistry::Register : Argument |config| is incomplete.";
return false;
}
if (config.dispatch_policy() != fuchsia::ui::pointerinjector::DispatchPolicy::EXCLUSIVE_TARGET &&
config.dispatch_policy() !=
fuchsia::ui::pointerinjector::DispatchPolicy::TOP_HIT_AND_ANCESTORS_IN_TARGET) {
FX_LOGS(ERROR) << "InjectorRegistry::Register : Only EXCLUSIVE_TARGET and "
"TOP_HIT_AND_ANCESTORS_IN_TARGET DispatchPolicy is supported.";
return false;
}
if (config.device_type() != fuchsia::ui::pointerinjector::DeviceType::TOUCH) {
FX_LOGS(ERROR) << "InjectorRegistry::Register : Only DeviceType TOUCH is supported.";
return false;
}
if (!config.context().is_view() || !config.target().is_view()) {
FX_LOGS(ERROR) << "InjectorRegistry::Register : Argument |config.context| or |config.target| "
"is not a view. Only views are supported.";
return false;
}
if (Injector::IsValidViewport(config.viewport()) != ZX_OK) {
// Errors printed in IsValidViewport. Just return result here.
return false;
}
return true;
}
} // namespace
PointerinjectorRegistry::PointerinjectorRegistry(sys::ComponentContext* context,
InjectFunc inject_touch_exclusive,
InjectFunc inject_touch_hit_tested,
inspect::Node inspect_node)
: inject_touch_exclusive_(std::move(inject_touch_exclusive)),
inject_touch_hit_tested_(std::move(inject_touch_hit_tested)),
inspect_node_(std::move(inspect_node)) {
if (context) {
// Adding the service here is safe since the PointerinjectorRegistry instance in InputSystem is
// created at construction time..
context->outgoing()->AddPublicService(injector_registry_.GetHandler(this));
}
}
void PointerinjectorRegistry::Register(
fuchsia::ui::pointerinjector::Config config,
fidl::InterfaceRequest<fuchsia::ui::pointerinjector::Device> injector,
RegisterCallback callback) {
if (!IsValidConfig(config)) {
// Errors printed inside IsValidConfig. Just return here.
return;
}
// Check connectivity here, since injector doesn't have access to it.
const zx_koid_t context_koid = utils::ExtractKoid(config.context().view());
const zx_koid_t target_koid = utils::ExtractKoid(config.target().view());
if (context_koid == ZX_KOID_INVALID || target_koid == ZX_KOID_INVALID) {
FX_LOGS(ERROR) << "InjectorRegistry::Register : Argument |config.context| or |config.target| "
"was invalid.";
return;
}
if (!view_tree_snapshot_->IsDescendant(target_koid, context_koid)) {
FX_LOGS(ERROR) << "InjectorRegistry::Register : Argument |config.context| must be connected to "
"the Scene, and |config.target| must be a descendant of |config.context|";
return;
}
// TODO(fxbug.dev/50348): Add a callback to kill the channel immediately if connectivity breaks.
const InjectorId id = ++last_injector_id_;
InjectorSettings settings{.dispatch_policy = config.dispatch_policy(),
.device_id = config.device_id(),
.device_type = config.device_type(),
.context_koid = context_koid,
.target_koid = target_koid};
Viewport viewport{
.extents = {config.viewport().extents()},
.context_from_viewport_transform =
ColumnMajorMat3VectorToMat4(config.viewport().viewport_to_context_transform()),
};
fit::function<void(const InternalPointerEvent&, StreamId)> inject_func;
switch (settings.dispatch_policy) {
case fuchsia::ui::pointerinjector::DispatchPolicy::EXCLUSIVE_TARGET:
inject_func = [this](const InternalPointerEvent& event, StreamId stream_id) {
inject_touch_exclusive_(event, stream_id);
};
break;
case fuchsia::ui::pointerinjector::DispatchPolicy::TOP_HIT_AND_ANCESTORS_IN_TARGET:
inject_func = [this](const InternalPointerEvent& event, StreamId stream_id) {
inject_touch_hit_tested_(event, stream_id);
};
break;
default:
FX_CHECK(false) << "Should never be reached.";
break;
}
const auto [it, success] = injectors_.try_emplace(
id, inspect_node_.CreateChild(inspect_node_.UniqueName("injector-")), std::move(settings),
std::move(viewport), std::move(injector),
/*is_descendant_and_connected*/
[this](zx_koid_t descendant, zx_koid_t ancestor) {
return view_tree_snapshot_->IsDescendant(descendant, ancestor);
},
std::move(inject_func),
/*on_channel_closed*/
[this, id] { injectors_.erase(id); });
FX_CHECK(success) << "Injector already exists.";
callback();
}
} // namespace scenic_impl::input