From 1e26d891c13521f7a59c5497a834f146114b88e0 Mon Sep 17 00:00:00 2001 From: Mikael Pessa Date: Fri, 23 Oct 2020 18:03:21 -0700 Subject: [PATCH 1/5] Add "input shield" to capture pointer input for reinjection --- flow/view_holder.cc | 11 ++++++ flow/view_holder.h | 34 +++++++++++++++++++ .../flutter/fuchsia_external_view_embedder.cc | 20 ++++++++++- .../flutter/fuchsia_external_view_embedder.h | 33 ++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/flow/view_holder.cc b/flow/view_holder.cc index c2011825c9474..22122793b12ce 100644 --- a/flow/view_holder.cc +++ b/flow/view_holder.cc @@ -114,6 +114,10 @@ void ViewHolder::UpdateScene(scenic::Session* session, opacity_node_->AddChild(*entity_node_); opacity_node_->SetLabel("flutter::ViewHolder"); entity_node_->Attach(*view_holder_); + + input_interceptor_=std::make_unique(session); + entity_node_->AddChild(input_interceptor_->node()); + if (ui_task_runner_ && pending_view_holder_token_.value) { ui_task_runner_->PostTask( [bind_callback = std::move(pending_bind_callback_), @@ -125,6 +129,7 @@ void ViewHolder::UpdateScene(scenic::Session* session, FML_DCHECK(entity_node_); FML_DCHECK(opacity_node_); FML_DCHECK(view_holder_); + FML_DCHECK(input_interceptor_); container_node.AddChild(*opacity_node_); opacity_node_->SetOpacity(opacity / 255.0f); @@ -133,6 +138,12 @@ void ViewHolder::UpdateScene(scenic::Session* session, hit_testable ? fuchsia::ui::gfx::HitTestBehavior::kDefault : fuchsia::ui::gfx::HitTestBehavior::kSuppress); if (has_pending_properties_) { + const auto width = pending_properties_.bounding_box.max.x + - pending_properties_.bounding_box.min.x; + const auto height = pending_properties_.bounding_box.max.y + - pending_properties_.bounding_box.min.y; + input_interceptor_->UpdateDimensions(session, width, height, /*elevation*/-0.2f); + // TODO(dworsham): This should be derived from size and elevation. We // should be able to Z-limit the view's box but otherwise it uses all of the // available airspace. diff --git a/flow/view_holder.h b/flow/view_holder.h index f25b205c7823c..6cb57fa2e260e 100644 --- a/flow/view_holder.h +++ b/flow/view_holder.h @@ -68,12 +68,46 @@ class ViewHolder { void set_focusable(bool value) { focusable_ = value; } private: + // Helper class for setting up an invisible rectangle to catch all input. Rejected input will then + // be re-injected into the scene below us. + class InputInterceptor { + public: + InputInterceptor(scenic::Session* session) : opacity_node_(session), shape_node_(session) { + opacity_node_.SetLabel("Flutter::InputInterceptor"); + opacity_node_.SetOpacity(0.f); + + // Set the shape node to capture all input. Any unwanted input will be reinjected. + shape_node_.SetHitTestBehavior(fuchsia::ui::gfx::HitTestBehavior::kDefault); + shape_node_.SetSemanticVisibility(false); + + + scenic::Material material(session); + material.SetColor(0xff, 0xff, 0xff, 0xff); // White + shape_node_.SetMaterial(material); + + opacity_node_.AddChild(shape_node_); + } + + void UpdateDimensions(scenic::Session* session, float width, float height, float elevation) { + opacity_node_.SetTranslation(width * 0.5f, height * 0.5f, elevation); + shape_node_.SetShape(scenic::Rectangle(session, width, height)); + } + + const scenic::Node& node() { return opacity_node_; } + + private: + scenic::OpacityNodeHACK opacity_node_; + scenic::ShapeNode shape_node_; + }; + fml::RefPtr ui_task_runner_; std::unique_ptr entity_node_; std::unique_ptr opacity_node_; std::unique_ptr view_holder_; + std::unique_ptr input_interceptor_; + fuchsia::ui::views::ViewHolderToken pending_view_holder_token_; BindCallback pending_bind_callback_; diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc index 96fd028d4dd99..ddb4097cacd98 100644 --- a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc +++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc @@ -114,6 +114,14 @@ void FuchsiaExternalViewEmbedder::BeginFrame( frame_layers_.emplace( std::make_pair(kRootLayerId, EmbedderLayer(frame_size, std::nullopt))); frame_composition_order_.push_back(kRootLayerId); + + // Set up the input interceptor at the top of the scene, if applicable. + // TODO: if (intercept_all_input_) + // TODO: Don't hardcode this. + const float kMaximumElevation = -100.f; + input_interceptor_.emplace(session_.get()); + input_interceptor_->UpdateDimensions(session_.get(), frame_size.width(), frame_size.height(), kMaximumElevation); + metrics_node_.AddChild(input_interceptor_->node()); } void FuchsiaExternalViewEmbedder::EndFrame( @@ -126,7 +134,7 @@ void FuchsiaExternalViewEmbedder::SubmitFrame( GrDirectContext* context, std::unique_ptr frame) { TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::SubmitFrame"); - +FML_LOG(WARNING) << "EmbeddedView::FuchsiaExternalViewEmbedder::SubmitFrame"; std::vector> frame_surfaces; std::unordered_map frame_surface_indices; @@ -290,6 +298,8 @@ void FuchsiaExternalViewEmbedder::SubmitFrame( if (found_rects == scenic_rects_.end()) { auto [emplaced_rects, success] = scenic_rects_.emplace( std::make_pair(rect_hash, std::vector())); + + FML_DCHECK(success); found_rects = std::move(emplaced_rects); @@ -324,6 +334,12 @@ void FuchsiaExternalViewEmbedder::SubmitFrame( SK_AlphaOPAQUE, SK_AlphaOPAQUE - 1); scenic_layer.material.SetTexture(*surface_image); + // Set the shape node to capture all input. Any unwanted input will be reinjected. + // TODO(): Reverse these properties when use of the full gesture disambiguation protocol is + // activated. + scenic_layer.shape_node.SetHitTestBehavior(fuchsia::ui::gfx::HitTestBehavior::kDefault); + scenic_layer.shape_node.SetSemanticVisibility(false); + // Attach the ScenicLayer to the main scene graph. root_node_.AddChild(scenic_layer.shape_node); @@ -437,6 +453,8 @@ void FuchsiaExternalViewEmbedder::Reset() { for (auto& layer : scenic_layers_) { layer.material.SetTexture(0); } + + input_interceptor_.reset(); } } // namespace flutter_runner diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h index f299a2f91f62f..7728595a558cc 100644 --- a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h +++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h @@ -131,6 +131,37 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder { scenic::Material material; }; + // Helper class for setting up an invisible rectangle to catch all input. Rejected input will then + // be re-injected into the scene below us. + class InputInterceptor { + public: + InputInterceptor(scenic::Session* session) : opacity_node_(session), shape_node_(session) { + opacity_node_.SetLabel("Flutter::InputInterceptor"); + opacity_node_.SetOpacity(0.f); + + // Set the shape node to capture all input. Any unwanted input will be reinjected. + shape_node_.SetHitTestBehavior(fuchsia::ui::gfx::HitTestBehavior::kDefault); + shape_node_.SetSemanticVisibility(false); + + scenic::Material material(session); + material.SetColor(0xff, 0xff, 0xff, 0xff); // White + shape_node_.SetMaterial(material); + + opacity_node_.AddChild(shape_node_); + } + + void UpdateDimensions(scenic::Session* session, float width, float height, float elevation) { + opacity_node_.SetTranslation(width * 0.5f, height * 0.5f, elevation); + shape_node_.SetShape(scenic::Rectangle(session, width, height)); + } + + const scenic::Node& node() { return opacity_node_; } + + private: + scenic::OpacityNodeHACK opacity_node_; + scenic::ShapeNode shape_node_; + }; + using EmbedderLayerId = std::optional; constexpr static EmbedderLayerId kRootLayerId = EmbedderLayerId{}; @@ -145,6 +176,8 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder { std::unordered_map scenic_views_; std::vector scenic_layers_; + std::optional input_interceptor_; + std::unordered_map frame_layers_; std::vector frame_composition_order_; SkISize frame_size_ = SkISize::Make(0, 0); From 85e6f61d1645d4692a5be8d99e33169d5643bb2b Mon Sep 17 00:00:00 2001 From: Mikael Pessa Date: Mon, 26 Oct 2020 11:44:44 -0700 Subject: [PATCH 2/5] Only activate the input interceptor if flags are set --- flow/scene_update_context.cc | 8 +++-- flow/scene_update_context.h | 5 ++- flow/view_holder.cc | 32 ++++++++++++------- flow/view_holder.h | 7 ++-- shell/platform/fuchsia/flutter/engine.cc | 5 +-- shell/platform/fuchsia/flutter/engine.h | 1 + .../flutter/fuchsia_external_view_embedder.cc | 19 ++++++----- .../flutter/fuchsia_external_view_embedder.h | 5 ++- 8 files changed, 53 insertions(+), 29 deletions(-) diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index 3edd9c4d9fe26..4ad2671fd8a36 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -68,14 +68,16 @@ void SetMaterialColor(scenic::Material& material, SceneUpdateContext::SceneUpdateContext(std::string debug_label, fuchsia::ui::views::ViewToken view_token, scenic::ViewRefPair view_ref_pair, - SessionWrapper& session) + SessionWrapper& session, + bool intercept_all_input) : session_(session), root_view_(session_.get(), std::move(view_token), std::move(view_ref_pair.control_ref), std::move(view_ref_pair.view_ref), debug_label), - root_node_(session_.get()) { + root_node_(session_.get()), + intercept_all_input_(intercept_all_input) { root_view_.AddChild(root_node_); root_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask); @@ -193,7 +195,7 @@ void SceneUpdateContext::CreateView(int64_t view_id, zx_handle_t handle = (zx_handle_t)view_id; flutter::ViewHolder::Create(handle, nullptr, scenic::ToViewHolderToken(zx::eventpair(handle)), - nullptr); + nullptr, intercept_all_input_); auto* view_holder = ViewHolder::FromId(view_id); FML_DCHECK(view_holder); diff --git a/flow/scene_update_context.h b/flow/scene_update_context.h index f15c59fa7f6ea..854b1be52cb18 100644 --- a/flow/scene_update_context.h +++ b/flow/scene_update_context.h @@ -120,7 +120,8 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder { SceneUpdateContext(std::string debug_label, fuchsia::ui::views::ViewToken view_token, scenic::ViewRefPair view_ref_pair, - SessionWrapper& session); + SessionWrapper& session, + bool intercept_all_input = false); ~SceneUpdateContext() = default; scenic::ContainerNode& root_node() { return root_node_; } @@ -197,6 +198,8 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder { float next_elevation_ = 0.f; float alpha_ = 1.f; + bool intercept_all_input_ = false; + FML_DISALLOW_COPY_AND_ASSIGN(SceneUpdateContext); }; diff --git a/flow/view_holder.cc b/flow/view_holder.cc index 22122793b12ce..7a1773124a837 100644 --- a/flow/view_holder.cc +++ b/flow/view_holder.cc @@ -53,7 +53,8 @@ namespace flutter { void ViewHolder::Create(zx_koid_t id, fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, - const BindCallback& on_bind_callback) { + const BindCallback& on_bind_callback, + bool intercept_all_input) { // This raster thread contains at least 1 ViewHolder. Initialize the // per-thread bindings. if (tls_view_holder_bindings.get() == nullptr) { @@ -66,7 +67,8 @@ void ViewHolder::Create(zx_koid_t id, auto view_holder = std::make_unique(std::move(ui_task_runner), std::move(view_holder_token), - on_bind_callback); + on_bind_callback, + intercept_all_input); bindings->emplace(id, std::move(view_holder)); } @@ -93,10 +95,12 @@ ViewHolder* ViewHolder::FromId(zx_koid_t id) { ViewHolder::ViewHolder(fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, - const BindCallback& on_bind_callback) + const BindCallback& on_bind_callback, + bool intercept_all_input) : ui_task_runner_(std::move(ui_task_runner)), pending_view_holder_token_(std::move(view_holder_token)), - pending_bind_callback_(on_bind_callback) { + pending_bind_callback_(on_bind_callback), + intercept_all_input_(intercept_all_input) { FML_DCHECK(pending_view_holder_token_.value); } @@ -115,8 +119,10 @@ void ViewHolder::UpdateScene(scenic::Session* session, opacity_node_->SetLabel("flutter::ViewHolder"); entity_node_->Attach(*view_holder_); - input_interceptor_=std::make_unique(session); - entity_node_->AddChild(input_interceptor_->node()); + if (intercept_all_input_) { + input_interceptor_=std::make_unique(session); + entity_node_->AddChild(input_interceptor_->node()); + } if (ui_task_runner_ && pending_view_holder_token_.value) { ui_task_runner_->PostTask( @@ -129,7 +135,7 @@ void ViewHolder::UpdateScene(scenic::Session* session, FML_DCHECK(entity_node_); FML_DCHECK(opacity_node_); FML_DCHECK(view_holder_); - FML_DCHECK(input_interceptor_); + FML_DCHECK(!intercept_all_input_ || input_interceptor_); container_node.AddChild(*opacity_node_); opacity_node_->SetOpacity(opacity / 255.0f); @@ -138,11 +144,13 @@ void ViewHolder::UpdateScene(scenic::Session* session, hit_testable ? fuchsia::ui::gfx::HitTestBehavior::kDefault : fuchsia::ui::gfx::HitTestBehavior::kSuppress); if (has_pending_properties_) { - const auto width = pending_properties_.bounding_box.max.x - - pending_properties_.bounding_box.min.x; - const auto height = pending_properties_.bounding_box.max.y - - pending_properties_.bounding_box.min.y; - input_interceptor_->UpdateDimensions(session, width, height, /*elevation*/-0.2f); + if (input_interceptor_) { + const auto width = pending_properties_.bounding_box.max.x + - pending_properties_.bounding_box.min.x; + const auto height = pending_properties_.bounding_box.max.y + - pending_properties_.bounding_box.min.y; + input_interceptor_->UpdateDimensions(session, width, height, /*elevation*/-0.2f); + } // TODO(dworsham): This should be derived from size and elevation. We // should be able to Z-limit the view's box but otherwise it uses all of the diff --git a/flow/view_holder.h b/flow/view_holder.h index 6cb57fa2e260e..f2d7dbee6b7e1 100644 --- a/flow/view_holder.h +++ b/flow/view_holder.h @@ -34,13 +34,15 @@ class ViewHolder { static void Create(zx_koid_t id, fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, - const BindCallback& on_bind_callback); + const BindCallback& on_bind_callback, + bool intercept_all_input = false); static void Destroy(zx_koid_t id); static ViewHolder* FromId(zx_koid_t id); ViewHolder(fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, - const BindCallback& on_bind_callback); + const BindCallback& on_bind_callback, + bool intercept_all_input); ~ViewHolder() = default; // Sets the properties/opacity of the child view by issuing a Scenic command. @@ -113,6 +115,7 @@ class ViewHolder { bool hit_testable_ = true; bool focusable_ = true; + bool intercept_all_input_ = false; fuchsia::ui::gfx::ViewProperties pending_properties_; bool has_pending_properties_ = false; diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index 3380726840ad6..6ac5199294d30 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -74,6 +74,7 @@ Engine::Engine(Delegate& delegate, #if defined(LEGACY_FUCHSIA_EMBEDDER) use_legacy_renderer_(product_config.use_legacy_renderer()), #endif + intercept_all_input_(product_config.get_intercept_all_input()), weak_factory_(this) { if (zx::event::create(0, &vsync_event_) != ZX_OK) { FML_DLOG(ERROR) << "Could not create the vsync event."; @@ -139,13 +140,13 @@ Engine::Engine(Delegate& delegate, if (use_legacy_renderer_) { legacy_external_view_embedder_.emplace( thread_label_, std::move(view_token), std::move(view_ref_pair), - session_connection_.value()); + session_connection_.value(), intercept_all_input_); } else #endif { external_view_embedder_.emplace( thread_label_, std::move(view_token), std::move(view_ref_pair), - session_connection_.value(), surface_producer_.value()); + session_connection_.value(), surface_producer_.value(), intercept_all_input_); } })); diff --git a/shell/platform/fuchsia/flutter/engine.h b/shell/platform/fuchsia/flutter/engine.h index 20e14e849fc17..e7289a1366e2d 100644 --- a/shell/platform/fuchsia/flutter/engine.h +++ b/shell/platform/fuchsia/flutter/engine.h @@ -84,6 +84,7 @@ class Engine final { #if defined(LEGACY_FUCHSIA_EMBEDDER) bool use_legacy_renderer_ = true; #endif + bool intercept_all_input_ = false; fml::WeakPtrFactory weak_factory_; diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc index ddb4097cacd98..085c9b677bbf1 100644 --- a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc +++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc @@ -29,7 +29,8 @@ FuchsiaExternalViewEmbedder::FuchsiaExternalViewEmbedder( fuchsia::ui::views::ViewToken view_token, scenic::ViewRefPair view_ref_pair, SessionConnection& session, - VulkanSurfaceProducer& surface_producer) + VulkanSurfaceProducer& surface_producer, + bool intercept_all_input) : session_(session), surface_producer_(surface_producer), root_view_(session_.get(), @@ -38,7 +39,8 @@ FuchsiaExternalViewEmbedder::FuchsiaExternalViewEmbedder( std::move(view_ref_pair.view_ref), debug_label), metrics_node_(session_.get()), - root_node_(session_.get()) { + root_node_(session_.get()), + intercept_all_input_(intercept_all_input) { root_view_.AddChild(metrics_node_); metrics_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask); metrics_node_.SetLabel("Flutter::MetricsWatcher"); @@ -116,12 +118,13 @@ void FuchsiaExternalViewEmbedder::BeginFrame( frame_composition_order_.push_back(kRootLayerId); // Set up the input interceptor at the top of the scene, if applicable. - // TODO: if (intercept_all_input_) - // TODO: Don't hardcode this. - const float kMaximumElevation = -100.f; - input_interceptor_.emplace(session_.get()); - input_interceptor_->UpdateDimensions(session_.get(), frame_size.width(), frame_size.height(), kMaximumElevation); - metrics_node_.AddChild(input_interceptor_->node()); + if (intercept_all_input_) { + // TODO: Don't hardcode elevation. + const float kMaximumElevation = -100.f; + input_interceptor_.emplace(session_.get()); + input_interceptor_->UpdateDimensions(session_.get(), frame_size.width(), frame_size.height(), kMaximumElevation); + metrics_node_.AddChild(input_interceptor_->node()); + } } void FuchsiaExternalViewEmbedder::EndFrame( diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h index 7728595a558cc..2a18e3890ae95 100644 --- a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h +++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h @@ -39,7 +39,8 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder { fuchsia::ui::views::ViewToken view_token, scenic::ViewRefPair view_ref_pair, SessionConnection& session, - VulkanSurfaceProducer& surface_producer); + VulkanSurfaceProducer& surface_producer, + bool intercept_all_input = false); ~FuchsiaExternalViewEmbedder(); // |ExternalViewEmbedder| @@ -183,6 +184,8 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder { SkISize frame_size_ = SkISize::Make(0, 0); float frame_dpr_ = 1.f; + bool intercept_all_input_ = false; + FML_DISALLOW_COPY_AND_ASSIGN(FuchsiaExternalViewEmbedder); }; From 0fa25706b18645840578f7aec1d551049baee99f Mon Sep 17 00:00:00 2001 From: Mikael Pessa Date: Mon, 26 Oct 2020 15:17:27 -0700 Subject: [PATCH 3/5] Remove leftover debug code --- flow/scene_update_context.cc | 10 ++++- flow/scene_update_context.h | 35 ++++++++++++++++ flow/view_holder.cc | 27 ++---------- flow/view_holder.h | 41 +------------------ shell/platform/fuchsia/flutter/engine.cc | 3 +- .../flutter/fuchsia_external_view_embedder.cc | 17 ++++---- .../flutter/fuchsia_external_view_embedder.h | 25 ++++++----- 7 files changed, 75 insertions(+), 83 deletions(-) diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index 4ad2671fd8a36..b4643258ddb7a 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -195,7 +195,7 @@ void SceneUpdateContext::CreateView(int64_t view_id, zx_handle_t handle = (zx_handle_t)view_id; flutter::ViewHolder::Create(handle, nullptr, scenic::ToViewHolderToken(zx::eventpair(handle)), - nullptr, intercept_all_input_); + nullptr); auto* view_holder = ViewHolder::FromId(view_id); FML_DCHECK(view_holder); @@ -317,6 +317,14 @@ SceneUpdateContext::Frame::Frame(SceneUpdateContext& context, // with opacity != 1. For now, clamp to a infinitesimally smaller value than // 1, which does not cause visual problems in practice. opacity_node_.SetOpacity(std::min(kOneMinusEpsilon, opacity_ / 255.0f)); + + if (context.intercept_all_input_) { + context.input_interceptor_.emplace(context.session_.get()); + context.input_interceptor_->UpdateDimensions( + context.session_.get(), rrect.width(), rrect.height(), + -(local_elevation + kScenicZElevationBetweenLayers * 0.5f)); + entity_node().AddChild(context.input_interceptor_->node()); + } } SceneUpdateContext::Frame::~Frame() { diff --git a/flow/scene_update_context.h b/flow/scene_update_context.h index 854b1be52cb18..ac2d7ddcfcbfb 100644 --- a/flow/scene_update_context.h +++ b/flow/scene_update_context.h @@ -176,6 +176,40 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder { std::optional override_hit_testable = std::nullopt); private: + // Helper class for setting up an invisible rectangle to catch all input. + // Rejected input will then be re-injected into a suitable platform view + // controlled by this Engine instance. + class InputInterceptor { + public: + InputInterceptor(scenic::Session* session) + : opacity_node_(session), shape_node_(session) { + opacity_node_.SetLabel("Flutter::InputInterceptor"); + opacity_node_.SetOpacity(0.f); + + // Set the shape node to capture all input. Any unwanted input will be + // reinjected. + shape_node_.SetHitTestBehavior( + fuchsia::ui::gfx::HitTestBehavior::kDefault); + shape_node_.SetSemanticVisibility(false); + + opacity_node_.AddChild(shape_node_); + } + + void UpdateDimensions(scenic::Session* session, + float width, + float height, + float elevation) { + opacity_node_.SetTranslation(width * 0.5f, height * 0.5f, elevation); + shape_node_.SetShape(scenic::Rectangle(session, width, height)); + } + + const scenic::Node& node() { return opacity_node_; } + + private: + scenic::OpacityNodeHACK opacity_node_; + scenic::ShapeNode shape_node_; + }; + void CreateFrame(scenic::EntityNode& entity_node, const SkRRect& rrect, SkColor color, @@ -198,6 +232,7 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder { float next_elevation_ = 0.f; float alpha_ = 1.f; + std::optional input_interceptor_; bool intercept_all_input_ = false; FML_DISALLOW_COPY_AND_ASSIGN(SceneUpdateContext); diff --git a/flow/view_holder.cc b/flow/view_holder.cc index 7a1773124a837..c2011825c9474 100644 --- a/flow/view_holder.cc +++ b/flow/view_holder.cc @@ -53,8 +53,7 @@ namespace flutter { void ViewHolder::Create(zx_koid_t id, fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, - const BindCallback& on_bind_callback, - bool intercept_all_input) { + const BindCallback& on_bind_callback) { // This raster thread contains at least 1 ViewHolder. Initialize the // per-thread bindings. if (tls_view_holder_bindings.get() == nullptr) { @@ -67,8 +66,7 @@ void ViewHolder::Create(zx_koid_t id, auto view_holder = std::make_unique(std::move(ui_task_runner), std::move(view_holder_token), - on_bind_callback, - intercept_all_input); + on_bind_callback); bindings->emplace(id, std::move(view_holder)); } @@ -95,12 +93,10 @@ ViewHolder* ViewHolder::FromId(zx_koid_t id) { ViewHolder::ViewHolder(fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, - const BindCallback& on_bind_callback, - bool intercept_all_input) + const BindCallback& on_bind_callback) : ui_task_runner_(std::move(ui_task_runner)), pending_view_holder_token_(std::move(view_holder_token)), - pending_bind_callback_(on_bind_callback), - intercept_all_input_(intercept_all_input) { + pending_bind_callback_(on_bind_callback) { FML_DCHECK(pending_view_holder_token_.value); } @@ -118,12 +114,6 @@ void ViewHolder::UpdateScene(scenic::Session* session, opacity_node_->AddChild(*entity_node_); opacity_node_->SetLabel("flutter::ViewHolder"); entity_node_->Attach(*view_holder_); - - if (intercept_all_input_) { - input_interceptor_=std::make_unique(session); - entity_node_->AddChild(input_interceptor_->node()); - } - if (ui_task_runner_ && pending_view_holder_token_.value) { ui_task_runner_->PostTask( [bind_callback = std::move(pending_bind_callback_), @@ -135,7 +125,6 @@ void ViewHolder::UpdateScene(scenic::Session* session, FML_DCHECK(entity_node_); FML_DCHECK(opacity_node_); FML_DCHECK(view_holder_); - FML_DCHECK(!intercept_all_input_ || input_interceptor_); container_node.AddChild(*opacity_node_); opacity_node_->SetOpacity(opacity / 255.0f); @@ -144,14 +133,6 @@ void ViewHolder::UpdateScene(scenic::Session* session, hit_testable ? fuchsia::ui::gfx::HitTestBehavior::kDefault : fuchsia::ui::gfx::HitTestBehavior::kSuppress); if (has_pending_properties_) { - if (input_interceptor_) { - const auto width = pending_properties_.bounding_box.max.x - - pending_properties_.bounding_box.min.x; - const auto height = pending_properties_.bounding_box.max.y - - pending_properties_.bounding_box.min.y; - input_interceptor_->UpdateDimensions(session, width, height, /*elevation*/-0.2f); - } - // TODO(dworsham): This should be derived from size and elevation. We // should be able to Z-limit the view's box but otherwise it uses all of the // available airspace. diff --git a/flow/view_holder.h b/flow/view_holder.h index f2d7dbee6b7e1..f25b205c7823c 100644 --- a/flow/view_holder.h +++ b/flow/view_holder.h @@ -34,15 +34,13 @@ class ViewHolder { static void Create(zx_koid_t id, fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, - const BindCallback& on_bind_callback, - bool intercept_all_input = false); + const BindCallback& on_bind_callback); static void Destroy(zx_koid_t id); static ViewHolder* FromId(zx_koid_t id); ViewHolder(fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, - const BindCallback& on_bind_callback, - bool intercept_all_input); + const BindCallback& on_bind_callback); ~ViewHolder() = default; // Sets the properties/opacity of the child view by issuing a Scenic command. @@ -70,52 +68,17 @@ class ViewHolder { void set_focusable(bool value) { focusable_ = value; } private: - // Helper class for setting up an invisible rectangle to catch all input. Rejected input will then - // be re-injected into the scene below us. - class InputInterceptor { - public: - InputInterceptor(scenic::Session* session) : opacity_node_(session), shape_node_(session) { - opacity_node_.SetLabel("Flutter::InputInterceptor"); - opacity_node_.SetOpacity(0.f); - - // Set the shape node to capture all input. Any unwanted input will be reinjected. - shape_node_.SetHitTestBehavior(fuchsia::ui::gfx::HitTestBehavior::kDefault); - shape_node_.SetSemanticVisibility(false); - - - scenic::Material material(session); - material.SetColor(0xff, 0xff, 0xff, 0xff); // White - shape_node_.SetMaterial(material); - - opacity_node_.AddChild(shape_node_); - } - - void UpdateDimensions(scenic::Session* session, float width, float height, float elevation) { - opacity_node_.SetTranslation(width * 0.5f, height * 0.5f, elevation); - shape_node_.SetShape(scenic::Rectangle(session, width, height)); - } - - const scenic::Node& node() { return opacity_node_; } - - private: - scenic::OpacityNodeHACK opacity_node_; - scenic::ShapeNode shape_node_; - }; - fml::RefPtr ui_task_runner_; std::unique_ptr entity_node_; std::unique_ptr opacity_node_; std::unique_ptr view_holder_; - std::unique_ptr input_interceptor_; - fuchsia::ui::views::ViewHolderToken pending_view_holder_token_; BindCallback pending_bind_callback_; bool hit_testable_ = true; bool focusable_ = true; - bool intercept_all_input_ = false; fuchsia::ui::gfx::ViewProperties pending_properties_; bool has_pending_properties_ = false; diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index 6ac5199294d30..d0dfa0f66463c 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -146,7 +146,8 @@ Engine::Engine(Delegate& delegate, { external_view_embedder_.emplace( thread_label_, std::move(view_token), std::move(view_ref_pair), - session_connection_.value(), surface_producer_.value(), intercept_all_input_); + session_connection_.value(), surface_producer_.value(), + intercept_all_input_); } })); diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc index 085c9b677bbf1..329562c922948 100644 --- a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc +++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc @@ -122,7 +122,9 @@ void FuchsiaExternalViewEmbedder::BeginFrame( // TODO: Don't hardcode elevation. const float kMaximumElevation = -100.f; input_interceptor_.emplace(session_.get()); - input_interceptor_->UpdateDimensions(session_.get(), frame_size.width(), frame_size.height(), kMaximumElevation); + input_interceptor_->UpdateDimensions(session_.get(), frame_size.width(), + frame_size.height(), + kMaximumElevation); metrics_node_.AddChild(input_interceptor_->node()); } } @@ -137,7 +139,6 @@ void FuchsiaExternalViewEmbedder::SubmitFrame( GrDirectContext* context, std::unique_ptr frame) { TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::SubmitFrame"); -FML_LOG(WARNING) << "EmbeddedView::FuchsiaExternalViewEmbedder::SubmitFrame"; std::vector> frame_surfaces; std::unordered_map frame_surface_indices; @@ -301,8 +302,6 @@ FML_LOG(WARNING) << "EmbeddedView::FuchsiaExternalViewEmbedder::SubmitFrame"; if (found_rects == scenic_rects_.end()) { auto [emplaced_rects, success] = scenic_rects_.emplace( std::make_pair(rect_hash, std::vector())); - - FML_DCHECK(success); found_rects = std::move(emplaced_rects); @@ -337,10 +336,12 @@ FML_LOG(WARNING) << "EmbeddedView::FuchsiaExternalViewEmbedder::SubmitFrame"; SK_AlphaOPAQUE, SK_AlphaOPAQUE - 1); scenic_layer.material.SetTexture(*surface_image); - // Set the shape node to capture all input. Any unwanted input will be reinjected. - // TODO(): Reverse these properties when use of the full gesture disambiguation protocol is - // activated. - scenic_layer.shape_node.SetHitTestBehavior(fuchsia::ui::gfx::HitTestBehavior::kDefault); + // Set the shape node to capture all input. Any unwanted input will be + // reinjected. + // TODO(): Reverse these properties when use of the full gesture + // disambiguation protocol is activated. + scenic_layer.shape_node.SetHitTestBehavior( + fuchsia::ui::gfx::HitTestBehavior::kDefault); scenic_layer.shape_node.SetSemanticVisibility(false); // Attach the ScenicLayer to the main scene graph. diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h index 2a18e3890ae95..c2aa386e220e3 100644 --- a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h +++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h @@ -132,26 +132,29 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder { scenic::Material material; }; - // Helper class for setting up an invisible rectangle to catch all input. Rejected input will then - // be re-injected into the scene below us. + // Helper class for setting up an invisible rectangle to catch all input. + // Rejected input will then be re-injected into a suitable platform view + // controlled by this Engine instance. class InputInterceptor { public: - InputInterceptor(scenic::Session* session) : opacity_node_(session), shape_node_(session) { + InputInterceptor(scenic::Session* session) + : opacity_node_(session), shape_node_(session) { opacity_node_.SetLabel("Flutter::InputInterceptor"); - opacity_node_.SetOpacity(0.f); + opacity_node_.SetOpacity(0.5f); - // Set the shape node to capture all input. Any unwanted input will be reinjected. - shape_node_.SetHitTestBehavior(fuchsia::ui::gfx::HitTestBehavior::kDefault); + // Set the shape node to capture all input. Any unwanted input will be + // reinjected. + shape_node_.SetHitTestBehavior( + fuchsia::ui::gfx::HitTestBehavior::kDefault); shape_node_.SetSemanticVisibility(false); - scenic::Material material(session); - material.SetColor(0xff, 0xff, 0xff, 0xff); // White - shape_node_.SetMaterial(material); - opacity_node_.AddChild(shape_node_); } - void UpdateDimensions(scenic::Session* session, float width, float height, float elevation) { + void UpdateDimensions(scenic::Session* session, + float width, + float height, + float elevation) { opacity_node_.SetTranslation(width * 0.5f, height * 0.5f, elevation); shape_node_.SetShape(scenic::Rectangle(session, width, height)); } From 615ad02a753e15471974078f629fb405ec69eaf6 Mon Sep 17 00:00:00 2001 From: Mikael Pessa Date: Tue, 27 Oct 2020 15:52:08 -0700 Subject: [PATCH 4/5] Fix crash --- flow/scene_update_context.cc | 10 +++++----- .../fuchsia/flutter/fuchsia_external_view_embedder.cc | 10 +++++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index f7152e1d3d4d7..461b0914433a2 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -320,12 +320,12 @@ SceneUpdateContext::Frame::Frame(std::shared_ptr context, // 1, which does not cause visual problems in practice. opacity_node_.SetOpacity(std::min(kOneMinusEpsilon, opacity_ / 255.0f)); - if (context.intercept_all_input_) { - context.input_interceptor_.emplace(context.session_.get()); - context.input_interceptor_->UpdateDimensions( - context.session_.get(), rrect.width(), rrect.height(), + if (context->intercept_all_input_) { + context->input_interceptor_.emplace(context->session_.get()); + context->input_interceptor_->UpdateDimensions( + context->session_.get(), rrect.width(), rrect.height(), -(local_elevation + kScenicZElevationBetweenLayers * 0.5f)); - entity_node().AddChild(context.input_interceptor_->node()); + entity_node().AddChild(context->input_interceptor_->node()); } } diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc index 329562c922948..48933c5585520 100644 --- a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc +++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc @@ -47,6 +47,12 @@ FuchsiaExternalViewEmbedder::FuchsiaExternalViewEmbedder( metrics_node_.AddChild(root_node_); root_node_.SetLabel("Flutter::LayerTree"); + // Set up the input interceptor at the top of the scene, if applicable. + if (intercept_all_input_) { + input_interceptor_.emplace(session_.get()); + metrics_node_.AddChild(input_interceptor_->node()); + } + session_.Present(); } @@ -118,14 +124,12 @@ void FuchsiaExternalViewEmbedder::BeginFrame( frame_composition_order_.push_back(kRootLayerId); // Set up the input interceptor at the top of the scene, if applicable. - if (intercept_all_input_) { + if (input_interceptor_.has_value()) { // TODO: Don't hardcode elevation. const float kMaximumElevation = -100.f; - input_interceptor_.emplace(session_.get()); input_interceptor_->UpdateDimensions(session_.get(), frame_size.width(), frame_size.height(), kMaximumElevation); - metrics_node_.AddChild(input_interceptor_->node()); } } From 4d7a8a10b16f53ad71f2638e958699db12ce9b80 Mon Sep 17 00:00:00 2001 From: Mikael Pessa Date: Wed, 28 Oct 2020 16:05:36 -0700 Subject: [PATCH 5/5] Only let the bottom-most layer interact with input --- .../flutter/fuchsia_external_view_embedder.cc | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc index 48933c5585520..987db137595e8 100644 --- a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc +++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc @@ -180,6 +180,7 @@ void FuchsiaExternalViewEmbedder::SubmitFrame( const float inv_dpr = 1.0f / frame_dpr_; root_node_.SetScale(inv_dpr, inv_dpr, 1.0f); + bool first_layer = true; for (const auto& layer_id : frame_composition_order_) { const auto& layer = frame_layers_.find(layer_id); FML_DCHECK(layer != frame_layers_.end()); @@ -340,13 +341,17 @@ void FuchsiaExternalViewEmbedder::SubmitFrame( SK_AlphaOPAQUE, SK_AlphaOPAQUE - 1); scenic_layer.material.SetTexture(*surface_image); - // Set the shape node to capture all input. Any unwanted input will be - // reinjected. - // TODO(): Reverse these properties when use of the full gesture - // disambiguation protocol is activated. - scenic_layer.shape_node.SetHitTestBehavior( - fuchsia::ui::gfx::HitTestBehavior::kDefault); - scenic_layer.shape_node.SetSemanticVisibility(false); + // Only the first (i.e. the bottom-most) layer should receive input. + // TODO: Workaround for invisible overlays stealing input. Remove when + // the underlying bug is fixed. + if (first_layer) { + scenic_layer.shape_node.SetHitTestBehavior( + fuchsia::ui::gfx::HitTestBehavior::kDefault); + } else { + scenic_layer.shape_node.SetHitTestBehavior( + fuchsia::ui::gfx::HitTestBehavior::kSuppress); + } + first_layer = false; // Attach the ScenicLayer to the main scene graph. root_node_.AddChild(scenic_layer.shape_node);