mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add "input shield" to capture pointer input for reinjection (flutter/engine#22067)
This commit is contained in:
parent
30b8a62f7f
commit
92beaad3ea
@ -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<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() {
|
||||
|
||||
@ -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<bool> 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<InputInterceptor> input_interceptor_;
|
||||
bool intercept_all_input_ = false;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(SceneUpdateContext);
|
||||
};
|
||||
|
||||
|
||||
@ -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<flutter::SceneUpdateContext>(
|
||||
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<FuchsiaExternalViewEmbedder>(
|
||||
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();
|
||||
}));
|
||||
|
||||
@ -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<Engine> weak_factory_;
|
||||
|
||||
|
||||
@ -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<flutter::SurfaceFrame> frame) {
|
||||
TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::SubmitFrame");
|
||||
|
||||
std::vector<std::unique_ptr<SurfaceProducerSurface>> frame_surfaces;
|
||||
std::unordered_map<EmbedderLayerId, size_t> 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
|
||||
|
||||
@ -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<uint32_t>;
|
||||
constexpr static EmbedderLayerId kRootLayerId = EmbedderLayerId{};
|
||||
|
||||
@ -145,11 +180,15 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
|
||||
std::unordered_map<int64_t, ScenicView> scenic_views_;
|
||||
std::vector<ScenicLayer> scenic_layers_;
|
||||
|
||||
std::optional<InputInterceptor> input_interceptor_;
|
||||
|
||||
std::unordered_map<EmbedderLayerId, EmbedderLayer> frame_layers_;
|
||||
std::vector<EmbedderLayerId> 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);
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user