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