Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
pointer_injector_delegate.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
8
9namespace flutter_runner {
10
11using fup_Config = fuchsia::ui::pointerinjector::Config;
12using fup_Context = fuchsia::ui::pointerinjector::Context;
13using fup_Data = fuchsia::ui::pointerinjector::Data;
14using fup_DeviceType = fuchsia::ui::pointerinjector::DeviceType;
15using fup_DispatchPolicy = fuchsia::ui::pointerinjector::DispatchPolicy;
16using fup_Event = fuchsia::ui::pointerinjector::Event;
17using fup_EventPhase = fuchsia::ui::pointerinjector::EventPhase;
18using fup_PointerSample = fuchsia::ui::pointerinjector::PointerSample;
19using fup_Target = fuchsia::ui::pointerinjector::Target;
20using fup_Viewport = fuchsia::ui::pointerinjector::Viewport;
21using fuv_ViewRef = fuchsia::ui::views::ViewRef;
22const auto fup_MAX_INJECT = fuchsia::ui::pointerinjector::MAX_INJECT;
23
24namespace {
25
26// clang-format off
27 static constexpr std::array<float, 9> kIdentityMatrix = {
28 1, 0, 0, // column one
29 0, 1, 0, // column two
30 0, 0, 1, // column three
31 };
32// clang-format on
33
34} // namespace
35
37 const rapidjson::Document& request,
39 if (!registry_->is_bound()) {
40 FML_LOG(WARNING)
41 << "Lost connection to fuchsia.ui.pointerinjector.Registry";
42 return false;
43 }
44
45 auto method = request.FindMember("method");
46 if (method == request.MemberEnd() || !method->value.IsString()) {
47 FML_LOG(ERROR) << "No method found in platform message.";
48 return false;
49 }
50
51 if (method->value != kPointerInjectorMethodPrefix) {
52 FML_LOG(ERROR) << "Unexpected platform message method, expected "
53 "View.pointerinjector.inject.";
54 return false;
55 }
56
57 auto args_it = request.FindMember("args");
58 if (args_it == request.MemberEnd() || !args_it->value.IsObject()) {
59 FML_LOG(ERROR) << "No arguments found in platform message's method";
60 return false;
61 }
62
63 const auto& args = args_it->value;
64
65 auto view_id = args.FindMember("viewId");
66 if (!view_id->value.IsUint64()) {
67 FML_LOG(ERROR) << "Argument 'viewId' is not a uint64";
68 return false;
69 }
70
71 auto id = view_id->value.GetUint64();
72 if (valid_views_.count(id) == 0) {
73 // A child view can get destroyed bottom-up, so the parent view may continue
74 // injecting until all view state processing "catches up". Until then, it's
75 // okay to accept a request to inject into a view that no longer exists.
76 // Doing so avoids log pollution regarding "MissingPluginException".
77 Complete(std::move(response), "[0]");
78 return true;
79 }
80
81 auto phase = args.FindMember("phase");
82 if (!phase->value.IsInt()) {
83 FML_LOG(ERROR) << "Argument 'phase' is not a int";
84 return false;
85 }
86
87 auto pointer_x = args.FindMember("x");
88 if (!pointer_x->value.IsFloat() && !pointer_x->value.IsInt()) {
89 FML_LOG(ERROR) << "Argument 'Pointer.X' is not a float";
90 return false;
91 }
92
93 auto pointer_y = args.FindMember("y");
94 if (!pointer_y->value.IsFloat() && !pointer_y->value.IsInt()) {
95 FML_LOG(ERROR) << "Argument 'Pointer.Y' is not a float";
96 return false;
97 }
98
99 auto pointer_id = args.FindMember("pointerId");
100 if (!pointer_id->value.IsUint()) {
101 FML_LOG(ERROR) << "Argument 'pointerId' is not a uint32";
102 return false;
103 }
104
105 auto trace_flow_id = args.FindMember("traceFlowId");
106 if (!trace_flow_id->value.IsInt()) {
107 FML_LOG(ERROR) << "Argument 'traceFlowId' is not a int";
108 return false;
109 }
110
111 auto width = args.FindMember("logicalWidth");
112 if (!width->value.IsFloat() && !width->value.IsInt()) {
113 FML_LOG(ERROR) << "Argument 'logicalWidth' is not a float";
114 return false;
115 }
116
117 auto height = args.FindMember("logicalHeight");
118 if (!height->value.IsFloat() && !height->value.IsInt()) {
119 FML_LOG(ERROR) << "Argument 'logicalHeight' is not a float";
120 return false;
121 }
122
123 auto timestamp = args.FindMember("timestamp");
124 if (!timestamp->value.IsInt() && !timestamp->value.IsUint64()) {
125 FML_LOG(ERROR) << "Argument 'timestamp' is not a int";
126 return false;
127 }
128
129 PointerInjectorRequest event = {
130 .x = pointer_x->value.GetFloat(),
131 .y = pointer_y->value.GetFloat(),
132 .pointer_id = pointer_id->value.GetUint(),
133 .phase = static_cast<fup_EventPhase>(phase->value.GetInt()),
134 .trace_flow_id = trace_flow_id->value.GetUint64(),
135 .logical_size = {width->value.GetFloat(), height->value.GetFloat()},
136 .timestamp = timestamp->value.GetInt()};
137
138 // Inject the pointer event if the view has been created.
139 valid_views_.at(id).InjectEvent(std::move(event));
140 Complete(std::move(response), "[0]");
141 return true;
142}
143
145 uint64_t view_id,
146 std::optional<fuv_ViewRef> view_ref) {
147 FML_CHECK(valid_views_.count(view_id) == 0);
148
149 auto [_, success] = valid_views_.try_emplace(
150 view_id, registry_, host_view_ref_, std::move(view_ref));
151
152 FML_CHECK(success);
153}
154
155fup_Event PointerInjectorDelegate::ExtractPointerEvent(
156 PointerInjectorRequest request) {
157 fup_Event event;
158 event.set_timestamp(request.timestamp);
159 event.set_trace_flow_id(request.trace_flow_id);
160
161 fup_PointerSample pointer_sample;
162 pointer_sample.set_pointer_id(request.pointer_id);
163 pointer_sample.set_phase(request.phase);
164 pointer_sample.set_position_in_viewport({request.x, request.y});
165
166 fup_Data data;
167 data.set_pointer_sample(std::move(pointer_sample));
168
169 event.set_data(std::move(data));
170 return event;
171}
172
173void PointerInjectorDelegate::Complete(
175 std::string value) {
176 if (response) {
177 response->Complete(std::make_unique<fml::DataMapping>(
178 std::vector<uint8_t>(value.begin(), value.end())));
179 }
180}
181
182void PointerInjectorDelegate::PointerInjectorEndpoint::InjectEvent(
183 PointerInjectorRequest request) {
184 if (!registered_) {
185 RegisterInjector(request);
186 }
187
188 auto event = ExtractPointerEvent(std::move(request));
189
190 // Add the event to |injector_events_| and dispatch it to the view.
191 EnqueueEvent(std::move(event));
192
193 DispatchPendingEvents();
194}
195
196void PointerInjectorDelegate::PointerInjectorEndpoint::DispatchPendingEvents() {
197 // Return if there is already a |fuchsia.ui.pointerinjector.Device.Inject|
198 // call in flight. The new pointer events will be dispatched once the
199 // in-progress call terminates.
200 if (injection_in_flight_) {
201 return;
202 }
203
204 // Dispatch the events present in |injector_events_|. Note that we recursively
205 // call |DispatchPendingEvents| in the callback passed to the
206 // |f.u.p.Device.Inject| call. This ensures that there is only one
207 // |f.u.p.Device.Inject| call at a time. If a new pointer event comes when
208 // there is a |f.u.p.Device.Inject| call in progress, it gets buffered in
209 // |injector_events_| and is picked up later.
210 if (!injector_events_.empty()) {
211 auto events = std::move(injector_events_.front());
212 injector_events_.pop();
213 injection_in_flight_ = true;
214
215 FML_CHECK(device_.is_bound());
216 FML_CHECK(events.size() <= fup_MAX_INJECT);
217
218 device_->Inject(std::move(events), [weak = weak_factory_.GetWeakPtr()] {
219 if (!weak) {
220 FML_LOG(WARNING) << "Use after free attempted.";
221 return;
222 }
223 weak->injection_in_flight_ = false;
224 weak->DispatchPendingEvents();
225 });
226 }
227}
228
229void PointerInjectorDelegate::PointerInjectorEndpoint::EnqueueEvent(
230 fup_Event event) {
231 // Add |event| in |injector_events_| keeping in mind that the vector size does
232 // not exceed |fup_MAX_INJECT|.
233 if (!injector_events_.empty() &&
234 injector_events_.back().size() < fup_MAX_INJECT) {
235 injector_events_.back().push_back(std::move(event));
236 } else {
237 std::vector<fup_Event> vec;
238 vec.reserve(fup_MAX_INJECT);
239 vec.push_back(std::move(event));
240 injector_events_.push(std::move(vec));
241 }
242}
243
244void PointerInjectorDelegate::PointerInjectorEndpoint::RegisterInjector(
245 const PointerInjectorRequest& request) {
246 if (registered_) {
247 return;
248 }
249
250 fup_Config config;
251 config.set_device_id(1);
252 config.set_device_type(fup_DeviceType::TOUCH);
253 config.set_dispatch_policy(fup_DispatchPolicy::EXCLUSIVE_TARGET);
254
255 fup_Context context;
256 fuv_ViewRef context_clone;
257 fidl::Clone(*host_view_ref_, &context_clone);
258 context.set_view(std::move(context_clone));
259 config.set_context(std::move(context));
260
261 FML_CHECK(view_ref_.has_value());
263 fuv_ViewRef target_clone;
264
265 fidl::Clone(*view_ref_, &target_clone);
266 target.set_view(std::move(target_clone));
267 config.set_target(std::move(target));
268
269 fup_Viewport viewport;
270 viewport.set_viewport_to_context_transform(kIdentityMatrix);
271 std::array<std::array<float, 2>, 2> extents{
272 {/*min*/ {0, 0},
273 /*max*/ {request.logical_size[0], request.logical_size[1]}}};
274 viewport.set_extents(std::move(extents));
275 config.set_viewport(std::move(viewport));
276
277 FML_CHECK(registry_->is_bound());
278
279 (*registry_)->Register(std::move(config), device_.NewRequest(), [] {});
280
281 registered_ = true;
282}
283
284void PointerInjectorDelegate::PointerInjectorEndpoint::Reset() {
285 injection_in_flight_ = false;
286 registered_ = false;
287 injector_events_ = {};
288}
289
290} // namespace flutter_runner
bool HandlePlatformMessage(const rapidjson::Document &request, fml::RefPtr< flutter::PlatformMessageResponse > response)
void OnCreateView(uint64_t view_id, std::optional< fuchsia::ui::views::ViewRef > view_ref=std::nullopt)
int32_t value
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint32_t * target
G_BEGIN_DECLS FlutterViewId view_id
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
fuchsia::ui::pointerinjector::Event fup_Event
fuchsia::ui::pointerinjector::Target fup_Target
fuchsia::ui::views::ViewRef fuv_ViewRef
fuchsia::ui::pointerinjector::DeviceType fup_DeviceType
fuchsia::ui::pointerinjector::Context fup_Context
fuchsia::ui::pointerinjector::DispatchPolicy fup_DispatchPolicy
fuchsia::ui::pointerinjector::Viewport fup_Viewport
fuchsia::ui::pointerinjector::Config fup_Config
fuchsia::ui::pointerinjector::PointerSample fup_PointerSample
fuchsia::ui::pointerinjector::Data fup_Data
fuchsia::ui::pointer::EventPhase fup_EventPhase
int32_t height
int32_t width