diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index 141e2caafeb35..461b0914433a2 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); @@ -317,6 +319,14 @@ SceneUpdateContext::Frame::Frame(std::shared_ptr 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 d95513bc259b5..23edb35a1c1e9 100644 --- a/flow/scene_update_context.h +++ b/flow/scene_update_context.h @@ -122,7 +122,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_; } @@ -177,6 +178,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, @@ -199,6 +234,9 @@ 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/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index 822caa9ddb4c6..20bf34531582c 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -75,6 +75,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."; @@ -143,7 +144,8 @@ Engine::Engine(Delegate& delegate, legacy_external_view_embedder_ = std::make_shared( thread_label_, std::move(view_token), - std::move(view_ref_pair), session_connection_.value()); + std::move(view_ref_pair), session_connection_.value(), + intercept_all_input_); } else #endif { @@ -151,7 +153,7 @@ Engine::Engine(Delegate& delegate, std::make_shared( thread_label_, std::move(view_token), std::move(view_ref_pair), session_connection_.value(), - surface_producer_.value()); + surface_producer_.value(), intercept_all_input_); } view_embedder_latch.Signal(); })); diff --git a/shell/platform/fuchsia/flutter/engine.h b/shell/platform/fuchsia/flutter/engine.h index a1335c59c78e5..377868666f94c 100644 --- a/shell/platform/fuchsia/flutter/engine.h +++ b/shell/platform/fuchsia/flutter/engine.h @@ -87,6 +87,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 96fd028d4dd99..987db137595e8 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,13 +39,20 @@ 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"); 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(); } @@ -114,6 +122,15 @@ 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. + if (input_interceptor_.has_value()) { + // TODO: Don't hardcode elevation. + const float kMaximumElevation = -100.f; + input_interceptor_->UpdateDimensions(session_.get(), frame_size.width(), + frame_size.height(), + kMaximumElevation); + } } void FuchsiaExternalViewEmbedder::EndFrame( @@ -126,7 +143,6 @@ void FuchsiaExternalViewEmbedder::SubmitFrame( GrDirectContext* context, std::unique_ptr frame) { TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::SubmitFrame"); - std::vector> frame_surfaces; std::unordered_map frame_surface_indices; @@ -164,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()); @@ -324,6 +341,18 @@ void FuchsiaExternalViewEmbedder::SubmitFrame( SK_AlphaOPAQUE, SK_AlphaOPAQUE - 1); scenic_layer.material.SetTexture(*surface_image); + // 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); @@ -437,6 +466,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..c2aa386e220e3 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| @@ -131,6 +132,40 @@ 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 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.5f); + + // 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_; + }; + using EmbedderLayerId = std::optional; constexpr static EmbedderLayerId kRootLayerId = EmbedderLayerId{}; @@ -145,11 +180,15 @@ 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); float frame_dpr_ = 1.f; + bool intercept_all_input_ = false; + FML_DISALLOW_COPY_AND_ASSIGN(FuchsiaExternalViewEmbedder); };