Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 7a7804b

Browse files
authored
Add "input shield" to capture pointer input for reinjection (#22067)
1 parent bb32446 commit 7a7804b

File tree

6 files changed

+130
-9
lines changed

6 files changed

+130
-9
lines changed

flow/scene_update_context.cc

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,16 @@ void SetMaterialColor(scenic::Material& material,
6868
SceneUpdateContext::SceneUpdateContext(std::string debug_label,
6969
fuchsia::ui::views::ViewToken view_token,
7070
scenic::ViewRefPair view_ref_pair,
71-
SessionWrapper& session)
71+
SessionWrapper& session,
72+
bool intercept_all_input)
7273
: session_(session),
7374
root_view_(session_.get(),
7475
std::move(view_token),
7576
std::move(view_ref_pair.control_ref),
7677
std::move(view_ref_pair.view_ref),
7778
debug_label),
78-
root_node_(session_.get()) {
79+
root_node_(session_.get()),
80+
intercept_all_input_(intercept_all_input) {
7981
root_view_.AddChild(root_node_);
8082
root_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
8183

@@ -317,6 +319,14 @@ SceneUpdateContext::Frame::Frame(std::shared_ptr<SceneUpdateContext> context,
317319
// with opacity != 1. For now, clamp to a infinitesimally smaller value than
318320
// 1, which does not cause visual problems in practice.
319321
opacity_node_.SetOpacity(std::min(kOneMinusEpsilon, opacity_ / 255.0f));
322+
323+
if (context->intercept_all_input_) {
324+
context->input_interceptor_.emplace(context->session_.get());
325+
context->input_interceptor_->UpdateDimensions(
326+
context->session_.get(), rrect.width(), rrect.height(),
327+
-(local_elevation + kScenicZElevationBetweenLayers * 0.5f));
328+
entity_node().AddChild(context->input_interceptor_->node());
329+
}
320330
}
321331

322332
SceneUpdateContext::Frame::~Frame() {

flow/scene_update_context.h

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder {
122122
SceneUpdateContext(std::string debug_label,
123123
fuchsia::ui::views::ViewToken view_token,
124124
scenic::ViewRefPair view_ref_pair,
125-
SessionWrapper& session);
125+
SessionWrapper& session,
126+
bool intercept_all_input = false);
126127
~SceneUpdateContext() = default;
127128

128129
scenic::ContainerNode& root_node() { return root_node_; }
@@ -177,6 +178,40 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder {
177178
std::optional<bool> override_hit_testable = std::nullopt);
178179

179180
private:
181+
// Helper class for setting up an invisible rectangle to catch all input.
182+
// Rejected input will then be re-injected into a suitable platform view
183+
// controlled by this Engine instance.
184+
class InputInterceptor {
185+
public:
186+
InputInterceptor(scenic::Session* session)
187+
: opacity_node_(session), shape_node_(session) {
188+
opacity_node_.SetLabel("Flutter::InputInterceptor");
189+
opacity_node_.SetOpacity(0.f);
190+
191+
// Set the shape node to capture all input. Any unwanted input will be
192+
// reinjected.
193+
shape_node_.SetHitTestBehavior(
194+
fuchsia::ui::gfx::HitTestBehavior::kDefault);
195+
shape_node_.SetSemanticVisibility(false);
196+
197+
opacity_node_.AddChild(shape_node_);
198+
}
199+
200+
void UpdateDimensions(scenic::Session* session,
201+
float width,
202+
float height,
203+
float elevation) {
204+
opacity_node_.SetTranslation(width * 0.5f, height * 0.5f, elevation);
205+
shape_node_.SetShape(scenic::Rectangle(session, width, height));
206+
}
207+
208+
const scenic::Node& node() { return opacity_node_; }
209+
210+
private:
211+
scenic::OpacityNodeHACK opacity_node_;
212+
scenic::ShapeNode shape_node_;
213+
};
214+
180215
void CreateFrame(scenic::EntityNode& entity_node,
181216
const SkRRect& rrect,
182217
SkColor color,
@@ -199,6 +234,9 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder {
199234
float next_elevation_ = 0.f;
200235
float alpha_ = 1.f;
201236

237+
std::optional<InputInterceptor> input_interceptor_;
238+
bool intercept_all_input_ = false;
239+
202240
FML_DISALLOW_COPY_AND_ASSIGN(SceneUpdateContext);
203241
};
204242

shell/platform/fuchsia/flutter/engine.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Engine::Engine(Delegate& delegate,
7575
#if defined(LEGACY_FUCHSIA_EMBEDDER)
7676
use_legacy_renderer_(product_config.use_legacy_renderer()),
7777
#endif
78+
intercept_all_input_(product_config.get_intercept_all_input()),
7879
weak_factory_(this) {
7980
if (zx::event::create(0, &vsync_event_) != ZX_OK) {
8081
FML_DLOG(ERROR) << "Could not create the vsync event.";
@@ -143,15 +144,16 @@ Engine::Engine(Delegate& delegate,
143144
legacy_external_view_embedder_ =
144145
std::make_shared<flutter::SceneUpdateContext>(
145146
thread_label_, std::move(view_token),
146-
std::move(view_ref_pair), session_connection_.value());
147+
std::move(view_ref_pair), session_connection_.value(),
148+
intercept_all_input_);
147149
} else
148150
#endif
149151
{
150152
external_view_embedder_ =
151153
std::make_shared<FuchsiaExternalViewEmbedder>(
152154
thread_label_, std::move(view_token),
153155
std::move(view_ref_pair), session_connection_.value(),
154-
surface_producer_.value());
156+
surface_producer_.value(), intercept_all_input_);
155157
}
156158
view_embedder_latch.Signal();
157159
}));

shell/platform/fuchsia/flutter/engine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class Engine final {
8787
#if defined(LEGACY_FUCHSIA_EMBEDDER)
8888
bool use_legacy_renderer_ = true;
8989
#endif
90+
bool intercept_all_input_ = false;
9091

9192
fml::WeakPtrFactory<Engine> weak_factory_;
9293

shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ FuchsiaExternalViewEmbedder::FuchsiaExternalViewEmbedder(
2929
fuchsia::ui::views::ViewToken view_token,
3030
scenic::ViewRefPair view_ref_pair,
3131
SessionConnection& session,
32-
VulkanSurfaceProducer& surface_producer)
32+
VulkanSurfaceProducer& surface_producer,
33+
bool intercept_all_input)
3334
: session_(session),
3435
surface_producer_(surface_producer),
3536
root_view_(session_.get(),
@@ -38,13 +39,20 @@ FuchsiaExternalViewEmbedder::FuchsiaExternalViewEmbedder(
3839
std::move(view_ref_pair.view_ref),
3940
debug_label),
4041
metrics_node_(session_.get()),
41-
root_node_(session_.get()) {
42+
root_node_(session_.get()),
43+
intercept_all_input_(intercept_all_input) {
4244
root_view_.AddChild(metrics_node_);
4345
metrics_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
4446
metrics_node_.SetLabel("Flutter::MetricsWatcher");
4547
metrics_node_.AddChild(root_node_);
4648
root_node_.SetLabel("Flutter::LayerTree");
4749

50+
// Set up the input interceptor at the top of the scene, if applicable.
51+
if (intercept_all_input_) {
52+
input_interceptor_.emplace(session_.get());
53+
metrics_node_.AddChild(input_interceptor_->node());
54+
}
55+
4856
session_.Present();
4957
}
5058

@@ -114,6 +122,15 @@ void FuchsiaExternalViewEmbedder::BeginFrame(
114122
frame_layers_.emplace(
115123
std::make_pair(kRootLayerId, EmbedderLayer(frame_size, std::nullopt)));
116124
frame_composition_order_.push_back(kRootLayerId);
125+
126+
// Set up the input interceptor at the top of the scene, if applicable.
127+
if (input_interceptor_.has_value()) {
128+
// TODO: Don't hardcode elevation.
129+
const float kMaximumElevation = -100.f;
130+
input_interceptor_->UpdateDimensions(session_.get(), frame_size.width(),
131+
frame_size.height(),
132+
kMaximumElevation);
133+
}
117134
}
118135

119136
void FuchsiaExternalViewEmbedder::EndFrame(
@@ -126,7 +143,6 @@ void FuchsiaExternalViewEmbedder::SubmitFrame(
126143
GrDirectContext* context,
127144
std::unique_ptr<flutter::SurfaceFrame> frame) {
128145
TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::SubmitFrame");
129-
130146
std::vector<std::unique_ptr<SurfaceProducerSurface>> frame_surfaces;
131147
std::unordered_map<EmbedderLayerId, size_t> frame_surface_indices;
132148

@@ -164,6 +180,7 @@ void FuchsiaExternalViewEmbedder::SubmitFrame(
164180
const float inv_dpr = 1.0f / frame_dpr_;
165181
root_node_.SetScale(inv_dpr, inv_dpr, 1.0f);
166182

183+
bool first_layer = true;
167184
for (const auto& layer_id : frame_composition_order_) {
168185
const auto& layer = frame_layers_.find(layer_id);
169186
FML_DCHECK(layer != frame_layers_.end());
@@ -324,6 +341,18 @@ void FuchsiaExternalViewEmbedder::SubmitFrame(
324341
SK_AlphaOPAQUE, SK_AlphaOPAQUE - 1);
325342
scenic_layer.material.SetTexture(*surface_image);
326343

344+
// Only the first (i.e. the bottom-most) layer should receive input.
345+
// TODO: Workaround for invisible overlays stealing input. Remove when
346+
// the underlying bug is fixed.
347+
if (first_layer) {
348+
scenic_layer.shape_node.SetHitTestBehavior(
349+
fuchsia::ui::gfx::HitTestBehavior::kDefault);
350+
} else {
351+
scenic_layer.shape_node.SetHitTestBehavior(
352+
fuchsia::ui::gfx::HitTestBehavior::kSuppress);
353+
}
354+
first_layer = false;
355+
327356
// Attach the ScenicLayer to the main scene graph.
328357
root_node_.AddChild(scenic_layer.shape_node);
329358

@@ -437,6 +466,8 @@ void FuchsiaExternalViewEmbedder::Reset() {
437466
for (auto& layer : scenic_layers_) {
438467
layer.material.SetTexture(0);
439468
}
469+
470+
input_interceptor_.reset();
440471
}
441472

442473
} // namespace flutter_runner

shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
3939
fuchsia::ui::views::ViewToken view_token,
4040
scenic::ViewRefPair view_ref_pair,
4141
SessionConnection& session,
42-
VulkanSurfaceProducer& surface_producer);
42+
VulkanSurfaceProducer& surface_producer,
43+
bool intercept_all_input = false);
4344
~FuchsiaExternalViewEmbedder();
4445

4546
// |ExternalViewEmbedder|
@@ -131,6 +132,40 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
131132
scenic::Material material;
132133
};
133134

135+
// Helper class for setting up an invisible rectangle to catch all input.
136+
// Rejected input will then be re-injected into a suitable platform view
137+
// controlled by this Engine instance.
138+
class InputInterceptor {
139+
public:
140+
InputInterceptor(scenic::Session* session)
141+
: opacity_node_(session), shape_node_(session) {
142+
opacity_node_.SetLabel("Flutter::InputInterceptor");
143+
opacity_node_.SetOpacity(0.5f);
144+
145+
// Set the shape node to capture all input. Any unwanted input will be
146+
// reinjected.
147+
shape_node_.SetHitTestBehavior(
148+
fuchsia::ui::gfx::HitTestBehavior::kDefault);
149+
shape_node_.SetSemanticVisibility(false);
150+
151+
opacity_node_.AddChild(shape_node_);
152+
}
153+
154+
void UpdateDimensions(scenic::Session* session,
155+
float width,
156+
float height,
157+
float elevation) {
158+
opacity_node_.SetTranslation(width * 0.5f, height * 0.5f, elevation);
159+
shape_node_.SetShape(scenic::Rectangle(session, width, height));
160+
}
161+
162+
const scenic::Node& node() { return opacity_node_; }
163+
164+
private:
165+
scenic::OpacityNodeHACK opacity_node_;
166+
scenic::ShapeNode shape_node_;
167+
};
168+
134169
using EmbedderLayerId = std::optional<uint32_t>;
135170
constexpr static EmbedderLayerId kRootLayerId = EmbedderLayerId{};
136171

@@ -145,11 +180,15 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
145180
std::unordered_map<int64_t, ScenicView> scenic_views_;
146181
std::vector<ScenicLayer> scenic_layers_;
147182

183+
std::optional<InputInterceptor> input_interceptor_;
184+
148185
std::unordered_map<EmbedderLayerId, EmbedderLayer> frame_layers_;
149186
std::vector<EmbedderLayerId> frame_composition_order_;
150187
SkISize frame_size_ = SkISize::Make(0, 0);
151188
float frame_dpr_ = 1.f;
152189

190+
bool intercept_all_input_ = false;
191+
153192
FML_DISALLOW_COPY_AND_ASSIGN(FuchsiaExternalViewEmbedder);
154193
};
155194

0 commit comments

Comments
 (0)