blob: f98e3a79f6eedc99029f5a0029d9dff020bd17ed [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/input/mouse_injector.h"
#include "src/ui/scenic/lib/input/touch_injector.h"
#include "src/ui/scenic/lib/utils/helpers.h"
#include "src/ui/scenic/lib/utils/math.h"
namespace scenic_impl::input {
using fuchsia::ui::pointerinjector::DeviceType;
using fuchsia::ui::pointerinjector::DispatchPolicy;
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;
}
const auto device_type = config.device_type();
if (device_type != DeviceType::TOUCH && device_type != DeviceType::MOUSE) {
FX_LOGS(ERROR) << "InjectorRegistry::Register : Unknown DeviceType.";
return false;
}
const auto dispatch_policy = config.dispatch_policy();
if (device_type == DeviceType::MOUSE) {
if (dispatch_policy != DispatchPolicy::EXCLUSIVE_TARGET &&
dispatch_policy != DispatchPolicy::MOUSE_HOVER_AND_LATCH_IN_TARGET) {
FX_LOGS(ERROR)
<< "InjectorRegistry::Register : DeviceType::MOUSE with mismatched dispatch policy.";
return false;
}
} else if (device_type == DeviceType::TOUCH) {
if (dispatch_policy != DispatchPolicy::EXCLUSIVE_TARGET &&
dispatch_policy != DispatchPolicy::TOP_HIT_AND_ANCESTORS_IN_TARGET) {
FX_LOGS(ERROR)
<< "InjectorRegistry::Register : DeviceType::TOUCH with mismatched dispatch policy.";
return false;
}
} else {
FX_NOTREACHED();
}
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, TouchInjectFunc inject_touch_exclusive,
TouchInjectFunc inject_touch_hit_tested, MouseInjectFunc inject_mouse_exclusive,
MouseInjectFunc inject_mouse_hit_tested,
fit::function<void(StreamId stream_id)> cancel_mouse_stream, inspect::Node inspect_node)
: inject_touch_exclusive_(std::move(inject_touch_exclusive)),
inject_touch_hit_tested_(std::move(inject_touch_hit_tested)),
inject_mouse_exclusive_(std::move(inject_mouse_exclusive)),
inject_mouse_hit_tested_(std::move(inject_mouse_hit_tested)),
cancel_mouse_stream_(std::move(cancel_mouse_stream)),
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;
}
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 =
utils::ColumnMajorMat3ArrayToMat4(config.viewport().viewport_to_context_transform()),
};
fit::function<bool(/*descendant*/ zx_koid_t, /*ancestor*/ zx_koid_t)>
is_descendant_and_connected = [this](zx_koid_t descendant, zx_koid_t ancestor) {
return view_tree_snapshot_->IsDescendant(descendant, ancestor);
};
fit::function<void()> on_channel_closed = [this, id] { injectors_.erase(id); };
if (settings.device_type == fuchsia::ui::pointerinjector::DeviceType::TOUCH) {
const auto [_, success] = injectors_.emplace(
id,
std::make_unique<TouchInjector>(
inspect_node_.CreateChild(inspect_node_.UniqueName("touch-injector-")),
std::move(settings), std::move(viewport), std::move(injector),
std::move(is_descendant_and_connected),
/*inject=*/
[&inject_func = settings.dispatch_policy ==
fuchsia::ui::pointerinjector::DispatchPolicy::EXCLUSIVE_TARGET
? inject_touch_exclusive_
: inject_touch_hit_tested_](const InternalTouchEvent& event,
StreamId stream_id) {
inject_func(event, stream_id);
},
std::move(on_channel_closed)));
FX_CHECK(success) << "Injector already exists.";
} else if (settings.device_type == fuchsia::ui::pointerinjector::DeviceType::MOUSE) {
settings.button_identifiers = config.buttons();
if (config.has_scroll_v_range()) {
settings.scroll_v_range = config.scroll_v_range();
}
if (config.has_scroll_h_range()) {
settings.scroll_h_range = config.scroll_h_range();
}
const auto [_, success] = injectors_.emplace(
id,
std::make_unique<MouseInjector>(
inspect_node_.CreateChild(inspect_node_.UniqueName("mouse-injector-")),
std::move(settings), std::move(viewport), std::move(injector),
std::move(is_descendant_and_connected),
/*inject=*/
[&inject_func = settings.dispatch_policy ==
fuchsia::ui::pointerinjector::DispatchPolicy::EXCLUSIVE_TARGET
? inject_mouse_exclusive_
: inject_mouse_hit_tested_](const InternalMouseEvent& event,
StreamId stream_id) {
inject_func(event, stream_id);
},
/*cancel_stream=*/[this](StreamId stream_id) { cancel_mouse_stream_(stream_id); },
/*on_channel_closed=*/std::move(on_channel_closed)));
FX_CHECK(success) << "Injector already exists.";
} else {
FX_NOTREACHED();
}
callback();
}
} // namespace scenic_impl::input