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 | |
5 | #define FML_USED_ON_EMBEDDER |
6 | #define RAPIDJSON_HAS_STDSTRING 1 |
7 | |
8 | #include <cstring> |
9 | #include <iostream> |
10 | #include <memory> |
11 | #include <set> |
12 | #include <string> |
13 | #include <vector> |
14 | |
15 | #include "flutter/fml/build_config.h" |
16 | #include "flutter/fml/closure.h" |
17 | #include "flutter/fml/make_copyable.h" |
18 | #include "flutter/fml/native_library.h" |
19 | #include "flutter/fml/thread.h" |
20 | #include "third_party/dart/runtime/bin/elf_loader.h" |
21 | #include "third_party/dart/runtime/include/dart_native_api.h" |
22 | #include "third_party/skia/include/core/SkSurface.h" |
23 | #include "third_party/skia/include/gpu/GrBackendSurface.h" |
24 | #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" |
25 | |
26 | #if !defined(FLUTTER_NO_EXPORT) |
27 | #if FML_OS_WIN |
28 | #define FLUTTER_EXPORT __declspec(dllexport) |
29 | #else // FML_OS_WIN |
30 | #define FLUTTER_EXPORT __attribute__((visibility("default"))) |
31 | #endif // FML_OS_WIN |
32 | #endif // !FLUTTER_NO_EXPORT |
33 | |
34 | extern "C"{ |
35 | #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG |
36 | // Used for debugging dart:* sources. |
37 | extern const uint8_t kPlatformStrongDill[]; |
38 | extern const intptr_t kPlatformStrongDillSize; |
39 | #endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG |
40 | } |
41 | |
42 | #include "flutter/assets/directory_asset_bundle.h" |
43 | #include "flutter/common/graphics/persistent_cache.h" |
44 | #include "flutter/common/task_runners.h" |
45 | #include "flutter/fml/command_line.h" |
46 | #include "flutter/fml/file.h" |
47 | #include "flutter/fml/make_copyable.h" |
48 | #include "flutter/fml/message_loop.h" |
49 | #include "flutter/fml/paths.h" |
50 | #include "flutter/fml/trace_event.h" |
51 | #include "flutter/shell/common/rasterizer.h" |
52 | #include "flutter/shell/common/switches.h" |
53 | #include "flutter/shell/platform/embedder/embedder.h" |
54 | #include "flutter/shell/platform/embedder/embedder_engine.h" |
55 | #include "flutter/shell/platform/embedder/embedder_external_texture_resolver.h" |
56 | #include "flutter/shell/platform/embedder/embedder_platform_message_response.h" |
57 | #include "flutter/shell/platform/embedder/embedder_render_target.h" |
58 | #include "flutter/shell/platform/embedder/embedder_render_target_skia.h" |
59 | #include "flutter/shell/platform/embedder/embedder_struct_macros.h" |
60 | #include "flutter/shell/platform/embedder/embedder_task_runner.h" |
61 | #include "flutter/shell/platform/embedder/embedder_thread_host.h" |
62 | #include "flutter/shell/platform/embedder/pixel_formats.h" |
63 | #include "flutter/shell/platform/embedder/platform_view_embedder.h" |
64 | #include "rapidjson/rapidjson.h" |
65 | #include "rapidjson/writer.h" |
66 | |
67 | // Note: the IMPELLER_SUPPORTS_RENDERING may be defined even when the |
68 | // embedder/BUILD.gn variable impeller_supports_rendering is disabled. |
69 | #ifdef SHELL_ENABLE_GL |
70 | #include "flutter/shell/platform/embedder/embedder_external_texture_gl.h" |
71 | #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h" |
72 | #include "third_party/skia/include/gpu/gl/GrGLTypes.h" |
73 | #ifdef IMPELLER_SUPPORTS_RENDERING |
74 | #include "flutter/shell/platform/embedder/embedder_render_target_impeller.h" // nogncheck |
75 | #include "flutter/shell/platform/embedder/embedder_surface_gl_impeller.h" // nogncheck |
76 | #include "impeller/core/texture.h" // nogncheck |
77 | #include "impeller/renderer/backend/gles/context_gles.h" // nogncheck |
78 | #include "impeller/renderer/backend/gles/texture_gles.h" // nogncheck |
79 | #include "impeller/renderer/context.h" // nogncheck |
80 | #include "impeller/renderer/render_target.h" // nogncheck |
81 | #endif // IMPELLER_SUPPORTS_RENDERING |
82 | #endif // SHELL_ENABLE_GL |
83 | |
84 | #ifdef SHELL_ENABLE_METAL |
85 | #include "flutter/shell/platform/embedder/embedder_surface_metal.h" |
86 | #include "third_party/skia/include/ports/SkCFObject.h" |
87 | #ifdef IMPELLER_SUPPORTS_RENDERING |
88 | #include "flutter/shell/platform/embedder/embedder_render_target_impeller.h" // nogncheck |
89 | #include "flutter/shell/platform/embedder/embedder_surface_metal_impeller.h" // nogncheck |
90 | #include "impeller/core/texture.h" // nogncheck |
91 | #include "impeller/renderer/backend/metal/texture_wrapper_mtl.h" // nogncheck |
92 | #include "impeller/renderer/render_target.h" // nogncheck |
93 | #endif // IMPELLER_SUPPORTS_RENDERING |
94 | #endif // SHELL_ENABLE_METAL |
95 | |
96 | const int32_t kFlutterSemanticsNodeIdBatchEnd = -1; |
97 | const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1; |
98 | |
99 | static constexpr int64_t kFlutterImplicitViewId = 0; |
100 | |
101 | // A message channel to send platform-independent FlutterKeyData to the |
102 | // framework. |
103 | // |
104 | // This should be kept in sync with the following variables: |
105 | // |
106 | // - lib/ui/platform_dispatcher.dart, _kFlutterKeyDataChannel |
107 | // - shell/platform/darwin/ios/framework/Source/FlutterEngine.mm, |
108 | // FlutterKeyDataChannel |
109 | // - io/flutter/embedding/android/KeyData.java, |
110 | // CHANNEL |
111 | // |
112 | // Not to be confused with "flutter/keyevent", which is used to send raw |
113 | // key event data in a platform-dependent format. |
114 | // |
115 | // ## Format |
116 | // |
117 | // Send: KeyDataPacket.data(). |
118 | // |
119 | // Expected reply: Whether the event is handled. Exactly 1 byte long, with value |
120 | // 1 for handled, and 0 for not. Malformed value is considered false. |
121 | const char* kFlutterKeyDataChannel = "flutter/keydata"; |
122 | |
123 | static FlutterEngineResult LogEmbedderError(FlutterEngineResult code, |
124 | const char* reason, |
125 | const char* code_name, |
126 | const char* function, |
127 | const char* file, |
128 | int line) { |
129 | #if FML_OS_WIN |
130 | constexpr char kSeparator = '\\'; |
131 | #else |
132 | constexpr char kSeparator = '/'; |
133 | #endif |
134 | const auto file_base = |
135 | (::strrchr(s: file, c: kSeparator) ? strrchr(s: file, c: kSeparator) + 1 : file); |
136 | char error[256] = {}; |
137 | snprintf(s: error, maxlen: (sizeof(error) / sizeof(char)), |
138 | format: "%s (%d): '%s' returned '%s'. %s", file_base, line, function, |
139 | code_name, reason); |
140 | std::cerr << error << std::endl; |
141 | return code; |
142 | } |
143 | |
144 | #define LOG_EMBEDDER_ERROR(code, reason) \ |
145 | LogEmbedderError(code, reason, #code, __FUNCTION__, __FILE__, __LINE__) |
146 | |
147 | static bool IsOpenGLRendererConfigValid(const FlutterRendererConfig* config) { |
148 | if (config->type != kOpenGL) { |
149 | return false; |
150 | } |
151 | |
152 | const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl; |
153 | |
154 | if (!SAFE_EXISTS(open_gl_config, make_current) || |
155 | !SAFE_EXISTS(open_gl_config, clear_current) || |
156 | !SAFE_EXISTS_ONE_OF(open_gl_config, fbo_callback, |
157 | fbo_with_frame_info_callback) || |
158 | !SAFE_EXISTS_ONE_OF(open_gl_config, present, present_with_info)) { |
159 | return false; |
160 | } |
161 | |
162 | return true; |
163 | } |
164 | |
165 | static bool IsSoftwareRendererConfigValid(const FlutterRendererConfig* config) { |
166 | if (config->type != kSoftware) { |
167 | return false; |
168 | } |
169 | |
170 | const FlutterSoftwareRendererConfig* software_config = &config->software; |
171 | |
172 | if (SAFE_ACCESS(software_config, surface_present_callback, nullptr) == |
173 | nullptr) { |
174 | return false; |
175 | } |
176 | |
177 | return true; |
178 | } |
179 | |
180 | static bool IsMetalRendererConfigValid(const FlutterRendererConfig* config) { |
181 | if (config->type != kMetal) { |
182 | return false; |
183 | } |
184 | |
185 | const FlutterMetalRendererConfig* metal_config = &config->metal; |
186 | |
187 | bool device = SAFE_ACCESS(metal_config, device, nullptr); |
188 | bool command_queue = |
189 | SAFE_ACCESS(metal_config, present_command_queue, nullptr); |
190 | |
191 | bool present = SAFE_ACCESS(metal_config, present_drawable_callback, nullptr); |
192 | bool get_texture = |
193 | SAFE_ACCESS(metal_config, get_next_drawable_callback, nullptr); |
194 | |
195 | return device && command_queue && present && get_texture; |
196 | } |
197 | |
198 | static bool IsVulkanRendererConfigValid(const FlutterRendererConfig* config) { |
199 | if (config->type != kVulkan) { |
200 | return false; |
201 | } |
202 | |
203 | const FlutterVulkanRendererConfig* vulkan_config = &config->vulkan; |
204 | |
205 | if (!SAFE_EXISTS(vulkan_config, instance) || |
206 | !SAFE_EXISTS(vulkan_config, physical_device) || |
207 | !SAFE_EXISTS(vulkan_config, device) || |
208 | !SAFE_EXISTS(vulkan_config, queue) || |
209 | !SAFE_EXISTS(vulkan_config, get_instance_proc_address_callback) || |
210 | !SAFE_EXISTS(vulkan_config, get_next_image_callback) || |
211 | !SAFE_EXISTS(vulkan_config, present_image_callback)) { |
212 | return false; |
213 | } |
214 | |
215 | return true; |
216 | } |
217 | |
218 | static bool IsRendererValid(const FlutterRendererConfig* config) { |
219 | if (config == nullptr) { |
220 | return false; |
221 | } |
222 | |
223 | switch (config->type) { |
224 | case kOpenGL: |
225 | return IsOpenGLRendererConfigValid(config); |
226 | case kSoftware: |
227 | return IsSoftwareRendererConfigValid(config); |
228 | case kMetal: |
229 | return IsMetalRendererConfigValid(config); |
230 | case kVulkan: |
231 | return IsVulkanRendererConfigValid(config); |
232 | default: |
233 | return false; |
234 | } |
235 | |
236 | return false; |
237 | } |
238 | |
239 | #if FML_OS_LINUX || FML_OS_WIN |
240 | static void* DefaultGLProcResolver(const char* name) { |
241 | static fml::RefPtr<fml::NativeLibrary> proc_library = |
242 | #if FML_OS_LINUX |
243 | fml::NativeLibrary::CreateForCurrentProcess(); |
244 | #elif FML_OS_WIN // FML_OS_LINUX |
245 | fml::NativeLibrary::Create("opengl32.dll"); |
246 | #endif // FML_OS_WIN |
247 | return static_cast<void*>( |
248 | const_cast<uint8_t*>(proc_library->ResolveSymbol(symbol: name))); |
249 | } |
250 | #endif // FML_OS_LINUX || FML_OS_WIN |
251 | |
252 | #ifdef SHELL_ENABLE_GL |
253 | // Auxiliary function used to translate rectangles of type SkIRect to |
254 | // FlutterRect. |
255 | static FlutterRect SkIRectToFlutterRect(const SkIRect sk_rect) { |
256 | FlutterRect flutter_rect = {.left: static_cast<double>(sk_rect.fLeft), |
257 | .top: static_cast<double>(sk_rect.fTop), |
258 | .right: static_cast<double>(sk_rect.fRight), |
259 | .bottom: static_cast<double>(sk_rect.fBottom)}; |
260 | return flutter_rect; |
261 | } |
262 | |
263 | // Auxiliary function used to translate rectangles of type FlutterRect to |
264 | // SkIRect. |
265 | static const SkIRect FlutterRectToSkIRect(FlutterRect flutter_rect) { |
266 | SkIRect rect = {.fLeft: static_cast<int32_t>(flutter_rect.left), |
267 | .fTop: static_cast<int32_t>(flutter_rect.top), |
268 | .fRight: static_cast<int32_t>(flutter_rect.right), |
269 | .fBottom: static_cast<int32_t>(flutter_rect.bottom)}; |
270 | return rect; |
271 | } |
272 | #endif |
273 | |
274 | static inline flutter::Shell::CreateCallback<flutter::PlatformView> |
275 | InferOpenGLPlatformViewCreationCallback( |
276 | const FlutterRendererConfig* config, |
277 | void* user_data, |
278 | const flutter::PlatformViewEmbedder::PlatformDispatchTable& |
279 | platform_dispatch_table, |
280 | std::unique_ptr<flutter::EmbedderExternalViewEmbedder> |
281 | external_view_embedder, |
282 | bool enable_impeller) { |
283 | #ifdef SHELL_ENABLE_GL |
284 | if (config->type != kOpenGL) { |
285 | return nullptr; |
286 | } |
287 | |
288 | auto gl_make_current = [ptr = config->open_gl.make_current, |
289 | user_data]() -> bool { return ptr(user_data); }; |
290 | |
291 | auto gl_clear_current = [ptr = config->open_gl.clear_current, |
292 | user_data]() -> bool { return ptr(user_data); }; |
293 | |
294 | auto gl_present = |
295 | [present = config->open_gl.present, |
296 | present_with_info = config->open_gl.present_with_info, |
297 | user_data](flutter::GLPresentInfo gl_present_info) -> bool { |
298 | if (present) { |
299 | return present(user_data); |
300 | } else { |
301 | // Format the frame and buffer damages accordingly. Note that, since the |
302 | // current compute damage algorithm only returns one rectangle for damage |
303 | // we are assuming the number of rectangles provided in frame and buffer |
304 | // damage are always 1. Once the function that computes damage implements |
305 | // support for multiple damage rectangles, GLPresentInfo should also |
306 | // contain the number of damage rectangles. |
307 | const size_t num_rects = 1; |
308 | |
309 | std::array<FlutterRect, num_rects> frame_damage_rect = { |
310 | SkIRectToFlutterRect(sk_rect: *(gl_present_info.frame_damage))}; |
311 | std::array<FlutterRect, num_rects> buffer_damage_rect = { |
312 | SkIRectToFlutterRect(sk_rect: *(gl_present_info.buffer_damage))}; |
313 | |
314 | FlutterDamage frame_damage{ |
315 | .struct_size = sizeof(FlutterDamage), |
316 | .num_rects = frame_damage_rect.size(), |
317 | .damage = frame_damage_rect.data(), |
318 | }; |
319 | FlutterDamage buffer_damage{ |
320 | .struct_size = sizeof(FlutterDamage), |
321 | .num_rects = buffer_damage_rect.size(), |
322 | .damage = buffer_damage_rect.data(), |
323 | }; |
324 | |
325 | // Construct the present information concerning the frame being rendered. |
326 | FlutterPresentInfo present_info = { |
327 | .struct_size = sizeof(FlutterPresentInfo), |
328 | .fbo_id = gl_present_info.fbo_id, |
329 | .frame_damage = frame_damage, |
330 | .buffer_damage = buffer_damage, |
331 | }; |
332 | |
333 | return present_with_info(user_data, &present_info); |
334 | } |
335 | }; |
336 | |
337 | auto gl_fbo_callback = |
338 | [fbo_callback = config->open_gl.fbo_callback, |
339 | fbo_with_frame_info_callback = |
340 | config->open_gl.fbo_with_frame_info_callback, |
341 | user_data](flutter::GLFrameInfo gl_frame_info) -> intptr_t { |
342 | if (fbo_callback) { |
343 | return fbo_callback(user_data); |
344 | } else { |
345 | FlutterFrameInfo frame_info = {}; |
346 | frame_info.struct_size = sizeof(FlutterFrameInfo); |
347 | frame_info.size = {.width: gl_frame_info.width, .height: gl_frame_info.height}; |
348 | return fbo_with_frame_info_callback(user_data, &frame_info); |
349 | } |
350 | }; |
351 | |
352 | auto gl_populate_existing_damage = |
353 | [populate_existing_damage = config->open_gl.populate_existing_damage, |
354 | user_data](intptr_t id) -> flutter::GLFBOInfo { |
355 | // If no populate_existing_damage was provided, disable partial |
356 | // repaint. |
357 | if (!populate_existing_damage) { |
358 | return flutter::GLFBOInfo{ |
359 | .fbo_id = static_cast<uint32_t>(id), |
360 | .partial_repaint_enabled = false, |
361 | .existing_damage = SkIRect::MakeEmpty(), |
362 | }; |
363 | } |
364 | |
365 | // Given the FBO's ID, get its existing damage. |
366 | FlutterDamage existing_damage; |
367 | populate_existing_damage(user_data, id, &existing_damage); |
368 | |
369 | bool partial_repaint_enabled = true; |
370 | SkIRect existing_damage_rect; |
371 | |
372 | // Verify that at least one damage rectangle was provided. |
373 | if (existing_damage.num_rects <= 0 || existing_damage.damage == nullptr) { |
374 | FML_LOG(INFO) << "No damage was provided. Forcing full repaint."; |
375 | existing_damage_rect = SkIRect::MakeEmpty(); |
376 | partial_repaint_enabled = false; |
377 | } else if (existing_damage.num_rects > 1) { |
378 | // Log message notifying users that multi-damage is not yet available in |
379 | // case they try to make use of it. |
380 | FML_LOG(INFO) << "Damage with multiple rectangles not yet supported. " |
381 | "Repainting the whole frame."; |
382 | existing_damage_rect = SkIRect::MakeEmpty(); |
383 | partial_repaint_enabled = false; |
384 | } else { |
385 | existing_damage_rect = FlutterRectToSkIRect(flutter_rect: *(existing_damage.damage)); |
386 | } |
387 | |
388 | // Pass the information about this FBO to the rendering backend. |
389 | return flutter::GLFBOInfo{ |
390 | .fbo_id = static_cast<uint32_t>(id), |
391 | .partial_repaint_enabled = partial_repaint_enabled, |
392 | .existing_damage = existing_damage_rect, |
393 | }; |
394 | }; |
395 | |
396 | const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl; |
397 | std::function<bool()> gl_make_resource_current_callback = nullptr; |
398 | if (SAFE_ACCESS(open_gl_config, make_resource_current, nullptr) != nullptr) { |
399 | gl_make_resource_current_callback = |
400 | [ptr = config->open_gl.make_resource_current, user_data]() { |
401 | return ptr(user_data); |
402 | }; |
403 | } |
404 | |
405 | std::function<SkMatrix(void)> gl_surface_transformation_callback = nullptr; |
406 | if (SAFE_ACCESS(open_gl_config, surface_transformation, nullptr) != nullptr) { |
407 | gl_surface_transformation_callback = |
408 | [ptr = config->open_gl.surface_transformation, user_data]() { |
409 | FlutterTransformation transformation = ptr(user_data); |
410 | return SkMatrix::MakeAll(scaleX: transformation.scaleX, // |
411 | skewX: transformation.skewX, // |
412 | transX: transformation.transX, // |
413 | skewY: transformation.skewY, // |
414 | scaleY: transformation.scaleY, // |
415 | transY: transformation.transY, // |
416 | pers0: transformation.pers0, // |
417 | pers1: transformation.pers1, // |
418 | pers2: transformation.pers2 // |
419 | ); |
420 | }; |
421 | |
422 | // If there is an external view embedder, ask it to apply the surface |
423 | // transformation to its surfaces as well. |
424 | if (external_view_embedder) { |
425 | external_view_embedder->SetSurfaceTransformationCallback( |
426 | gl_surface_transformation_callback); |
427 | } |
428 | } |
429 | |
430 | flutter::GPUSurfaceGLDelegate::GLProcResolver gl_proc_resolver = nullptr; |
431 | if (SAFE_ACCESS(open_gl_config, gl_proc_resolver, nullptr) != nullptr) { |
432 | gl_proc_resolver = [ptr = config->open_gl.gl_proc_resolver, |
433 | user_data](const char* gl_proc_name) { |
434 | return ptr(user_data, gl_proc_name); |
435 | }; |
436 | } else { |
437 | #if FML_OS_LINUX || FML_OS_WIN |
438 | gl_proc_resolver = DefaultGLProcResolver; |
439 | #endif |
440 | } |
441 | |
442 | bool fbo_reset_after_present = |
443 | SAFE_ACCESS(open_gl_config, fbo_reset_after_present, false); |
444 | |
445 | flutter::EmbedderSurfaceGL::GLDispatchTable gl_dispatch_table = { |
446 | .gl_make_current_callback: gl_make_current, // gl_make_current_callback |
447 | .gl_clear_current_callback: gl_clear_current, // gl_clear_current_callback |
448 | .gl_present_callback: gl_present, // gl_present_callback |
449 | .gl_fbo_callback: gl_fbo_callback, // gl_fbo_callback |
450 | .gl_make_resource_current_callback: gl_make_resource_current_callback, // gl_make_resource_current_callback |
451 | .gl_surface_transformation_callback: gl_surface_transformation_callback, // gl_surface_transformation_callback |
452 | .gl_proc_resolver: gl_proc_resolver, // gl_proc_resolver |
453 | .gl_populate_existing_damage: gl_populate_existing_damage, // gl_populate_existing_damage |
454 | }; |
455 | |
456 | return fml::MakeCopyable( |
457 | lambda: [gl_dispatch_table, fbo_reset_after_present, platform_dispatch_table, |
458 | enable_impeller, |
459 | external_view_embedder = |
460 | std::move(external_view_embedder)](flutter::Shell& shell) mutable { |
461 | std::shared_ptr<flutter::EmbedderExternalViewEmbedder> view_embedder = |
462 | std::move(external_view_embedder); |
463 | if (enable_impeller) { |
464 | return std::make_unique<flutter::PlatformViewEmbedder>( |
465 | args&: shell, // delegate |
466 | args: shell.GetTaskRunners(), // task runners |
467 | args: std::make_unique<flutter::EmbedderSurfaceGLImpeller>( |
468 | args&: gl_dispatch_table, args&: fbo_reset_after_present, |
469 | args&: view_embedder), // embedder_surface |
470 | args: platform_dispatch_table, // embedder platform dispatch table |
471 | args&: view_embedder // external view embedder |
472 | ); |
473 | } |
474 | return std::make_unique<flutter::PlatformViewEmbedder>( |
475 | args&: shell, // delegate |
476 | args: shell.GetTaskRunners(), // task runners |
477 | args: std::make_unique<flutter::EmbedderSurfaceGL>( |
478 | args&: gl_dispatch_table, args&: fbo_reset_after_present, |
479 | args&: view_embedder), // embedder_surface |
480 | args: platform_dispatch_table, // embedder platform dispatch table |
481 | args&: view_embedder // external view embedder |
482 | ); |
483 | }); |
484 | #else |
485 | return nullptr; |
486 | #endif |
487 | } |
488 | |
489 | static flutter::Shell::CreateCallback<flutter::PlatformView> |
490 | InferMetalPlatformViewCreationCallback( |
491 | const FlutterRendererConfig* config, |
492 | void* user_data, |
493 | const flutter::PlatformViewEmbedder::PlatformDispatchTable& |
494 | platform_dispatch_table, |
495 | std::unique_ptr<flutter::EmbedderExternalViewEmbedder> |
496 | external_view_embedder, |
497 | bool enable_impeller) { |
498 | if (config->type != kMetal) { |
499 | return nullptr; |
500 | } |
501 | |
502 | #ifdef SHELL_ENABLE_METAL |
503 | std::function<bool(flutter::GPUMTLTextureInfo texture)> metal_present = |
504 | [ptr = config->metal.present_drawable_callback, |
505 | user_data](flutter::GPUMTLTextureInfo texture) { |
506 | FlutterMetalTexture embedder_texture; |
507 | embedder_texture.struct_size = sizeof(FlutterMetalTexture); |
508 | embedder_texture.texture = texture.texture; |
509 | embedder_texture.texture_id = texture.texture_id; |
510 | embedder_texture.user_data = texture.destruction_context; |
511 | embedder_texture.destruction_callback = texture.destruction_callback; |
512 | return ptr(user_data, &embedder_texture); |
513 | }; |
514 | auto metal_get_texture = |
515 | [ptr = config->metal.get_next_drawable_callback, |
516 | user_data](const SkISize& frame_size) -> flutter::GPUMTLTextureInfo { |
517 | FlutterFrameInfo frame_info = {}; |
518 | frame_info.struct_size = sizeof(FlutterFrameInfo); |
519 | frame_info.size = {static_cast<uint32_t>(frame_size.width()), |
520 | static_cast<uint32_t>(frame_size.height())}; |
521 | flutter::GPUMTLTextureInfo texture_info; |
522 | |
523 | FlutterMetalTexture metal_texture = ptr(user_data, &frame_info); |
524 | texture_info.texture_id = metal_texture.texture_id; |
525 | texture_info.texture = metal_texture.texture; |
526 | texture_info.destruction_callback = metal_texture.destruction_callback; |
527 | texture_info.destruction_context = metal_texture.user_data; |
528 | return texture_info; |
529 | }; |
530 | |
531 | std::shared_ptr<flutter::EmbedderExternalViewEmbedder> view_embedder = |
532 | std::move(external_view_embedder); |
533 | |
534 | std::unique_ptr<flutter::EmbedderSurface> embedder_surface; |
535 | |
536 | if (enable_impeller) { |
537 | flutter::EmbedderSurfaceMetalImpeller::MetalDispatchTable |
538 | metal_dispatch_table = { |
539 | .present = metal_present, |
540 | .get_texture = metal_get_texture, |
541 | }; |
542 | embedder_surface = std::make_unique<flutter::EmbedderSurfaceMetalImpeller>( |
543 | const_cast<flutter::GPUMTLDeviceHandle>(config->metal.device), |
544 | const_cast<flutter::GPUMTLCommandQueueHandle>( |
545 | config->metal.present_command_queue), |
546 | metal_dispatch_table, view_embedder); |
547 | } else { |
548 | flutter::EmbedderSurfaceMetal::MetalDispatchTable metal_dispatch_table = { |
549 | .present = metal_present, |
550 | .get_texture = metal_get_texture, |
551 | }; |
552 | embedder_surface = std::make_unique<flutter::EmbedderSurfaceMetal>( |
553 | const_cast<flutter::GPUMTLDeviceHandle>(config->metal.device), |
554 | const_cast<flutter::GPUMTLCommandQueueHandle>( |
555 | config->metal.present_command_queue), |
556 | metal_dispatch_table, view_embedder); |
557 | } |
558 | |
559 | // The static leak checker gets confused by the use of fml::MakeCopyable. |
560 | // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) |
561 | return fml::MakeCopyable( |
562 | [embedder_surface = std::move(embedder_surface), platform_dispatch_table, |
563 | external_view_embedder = view_embedder](flutter::Shell& shell) mutable { |
564 | return std::make_unique<flutter::PlatformViewEmbedder>( |
565 | shell, // delegate |
566 | shell.GetTaskRunners(), // task runners |
567 | std::move(embedder_surface), // embedder surface |
568 | platform_dispatch_table, // platform dispatch table |
569 | std::move(external_view_embedder) // external view embedder |
570 | ); |
571 | }); |
572 | #else |
573 | return nullptr; |
574 | #endif |
575 | } |
576 | |
577 | static flutter::Shell::CreateCallback<flutter::PlatformView> |
578 | InferVulkanPlatformViewCreationCallback( |
579 | const FlutterRendererConfig* config, |
580 | void* user_data, |
581 | const flutter::PlatformViewEmbedder::PlatformDispatchTable& |
582 | platform_dispatch_table, |
583 | std::unique_ptr<flutter::EmbedderExternalViewEmbedder> |
584 | external_view_embedder) { |
585 | if (config->type != kVulkan) { |
586 | return nullptr; |
587 | } |
588 | |
589 | #ifdef SHELL_ENABLE_VULKAN |
590 | std::function<void*(VkInstance, const char*)> |
591 | vulkan_get_instance_proc_address = |
592 | [ptr = config->vulkan.get_instance_proc_address_callback, user_data]( |
593 | VkInstance instance, const char* proc_name) -> void* { |
594 | return ptr(user_data, instance, proc_name); |
595 | }; |
596 | |
597 | auto vulkan_get_next_image = |
598 | [ptr = config->vulkan.get_next_image_callback, |
599 | user_data](const SkISize& frame_size) -> FlutterVulkanImage { |
600 | FlutterFrameInfo frame_info = { |
601 | .struct_size = sizeof(FlutterFrameInfo), |
602 | .size = {.width: static_cast<uint32_t>(frame_size.width()), |
603 | .height: static_cast<uint32_t>(frame_size.height())}, |
604 | }; |
605 | |
606 | return ptr(user_data, &frame_info); |
607 | }; |
608 | |
609 | auto vulkan_present_image_callback = |
610 | [ptr = config->vulkan.present_image_callback, user_data]( |
611 | VkImage image, VkFormat format) -> bool { |
612 | FlutterVulkanImage image_desc = { |
613 | .struct_size = sizeof(FlutterVulkanImage), |
614 | .image = reinterpret_cast<uint64_t>(image), |
615 | .format = static_cast<uint32_t>(format), |
616 | }; |
617 | return ptr(user_data, &image_desc); |
618 | }; |
619 | |
620 | auto vk_instance = static_cast<VkInstance>(config->vulkan.instance); |
621 | auto proc_addr = |
622 | vulkan_get_instance_proc_address(vk_instance, "vkGetInstanceProcAddr"); |
623 | |
624 | flutter::EmbedderSurfaceVulkan::VulkanDispatchTable vulkan_dispatch_table = { |
625 | .get_instance_proc_address = |
626 | reinterpret_cast<PFN_vkGetInstanceProcAddr>(proc_addr), |
627 | .get_next_image = vulkan_get_next_image, |
628 | .present_image = vulkan_present_image_callback, |
629 | }; |
630 | |
631 | std::shared_ptr<flutter::EmbedderExternalViewEmbedder> view_embedder = |
632 | std::move(external_view_embedder); |
633 | |
634 | std::unique_ptr<flutter::EmbedderSurfaceVulkan> embedder_surface = |
635 | std::make_unique<flutter::EmbedderSurfaceVulkan>( |
636 | args: config->vulkan.version, args&: vk_instance, |
637 | args: config->vulkan.enabled_instance_extension_count, |
638 | args: config->vulkan.enabled_instance_extensions, |
639 | args: config->vulkan.enabled_device_extension_count, |
640 | args: config->vulkan.enabled_device_extensions, |
641 | args: static_cast<VkPhysicalDevice>(config->vulkan.physical_device), |
642 | args: static_cast<VkDevice>(config->vulkan.device), |
643 | args: config->vulkan.queue_family_index, |
644 | args: static_cast<VkQueue>(config->vulkan.queue), args&: vulkan_dispatch_table, |
645 | args&: view_embedder); |
646 | |
647 | return fml::MakeCopyable( |
648 | lambda: [embedder_surface = std::move(embedder_surface), platform_dispatch_table, |
649 | external_view_embedder = |
650 | std::move(view_embedder)](flutter::Shell& shell) mutable { |
651 | return std::make_unique<flutter::PlatformViewEmbedder>( |
652 | args&: shell, // delegate |
653 | args: shell.GetTaskRunners(), // task runners |
654 | args: std::move(embedder_surface), // embedder surface |
655 | args: platform_dispatch_table, // platform dispatch table |
656 | args: std::move(external_view_embedder) // external view embedder |
657 | ); |
658 | }); |
659 | #else |
660 | return nullptr; |
661 | #endif |
662 | } |
663 | |
664 | static flutter::Shell::CreateCallback<flutter::PlatformView> |
665 | InferSoftwarePlatformViewCreationCallback( |
666 | const FlutterRendererConfig* config, |
667 | void* user_data, |
668 | const flutter::PlatformViewEmbedder::PlatformDispatchTable& |
669 | platform_dispatch_table, |
670 | std::unique_ptr<flutter::EmbedderExternalViewEmbedder> |
671 | external_view_embedder) { |
672 | if (config->type != kSoftware) { |
673 | return nullptr; |
674 | } |
675 | |
676 | auto software_present_backing_store = |
677 | [ptr = config->software.surface_present_callback, user_data]( |
678 | const void* allocation, size_t row_bytes, size_t height) -> bool { |
679 | return ptr(user_data, allocation, row_bytes, height); |
680 | }; |
681 | |
682 | flutter::EmbedderSurfaceSoftware::SoftwareDispatchTable |
683 | software_dispatch_table = { |
684 | .software_present_backing_store: software_present_backing_store, // required |
685 | }; |
686 | |
687 | return fml::MakeCopyable( |
688 | lambda: [software_dispatch_table, platform_dispatch_table, |
689 | external_view_embedder = |
690 | std::move(external_view_embedder)](flutter::Shell& shell) mutable { |
691 | return std::make_unique<flutter::PlatformViewEmbedder>( |
692 | args&: shell, // delegate |
693 | args: shell.GetTaskRunners(), // task runners |
694 | args&: software_dispatch_table, // software dispatch table |
695 | args: platform_dispatch_table, // platform dispatch table |
696 | args: std::move(external_view_embedder) // external view embedder |
697 | ); |
698 | }); |
699 | } |
700 | |
701 | static flutter::Shell::CreateCallback<flutter::PlatformView> |
702 | InferPlatformViewCreationCallback( |
703 | const FlutterRendererConfig* config, |
704 | void* user_data, |
705 | const flutter::PlatformViewEmbedder::PlatformDispatchTable& |
706 | platform_dispatch_table, |
707 | std::unique_ptr<flutter::EmbedderExternalViewEmbedder> |
708 | external_view_embedder, |
709 | bool enable_impeller) { |
710 | if (config == nullptr) { |
711 | return nullptr; |
712 | } |
713 | |
714 | switch (config->type) { |
715 | case kOpenGL: |
716 | return InferOpenGLPlatformViewCreationCallback( |
717 | config, user_data, platform_dispatch_table, |
718 | external_view_embedder: std::move(external_view_embedder), enable_impeller); |
719 | case kSoftware: |
720 | return InferSoftwarePlatformViewCreationCallback( |
721 | config, user_data, platform_dispatch_table, |
722 | external_view_embedder: std::move(external_view_embedder)); |
723 | case kMetal: |
724 | return InferMetalPlatformViewCreationCallback( |
725 | config, user_data, platform_dispatch_table, |
726 | external_view_embedder: std::move(external_view_embedder), enable_impeller); |
727 | case kVulkan: |
728 | return InferVulkanPlatformViewCreationCallback( |
729 | config, user_data, platform_dispatch_table, |
730 | external_view_embedder: std::move(external_view_embedder)); |
731 | default: |
732 | return nullptr; |
733 | } |
734 | return nullptr; |
735 | } |
736 | |
737 | static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore( |
738 | GrDirectContext* context, |
739 | const FlutterBackingStoreConfig& config, |
740 | const FlutterOpenGLTexture* texture) { |
741 | #ifdef SHELL_ENABLE_GL |
742 | GrGLTextureInfo texture_info; |
743 | texture_info.fTarget = texture->target; |
744 | texture_info.fID = texture->name; |
745 | texture_info.fFormat = texture->format; |
746 | |
747 | auto backend_texture = GrBackendTextures::MakeGL(width: config.size.width, // |
748 | height: config.size.height, // |
749 | skgpu::Mipmapped::kNo, // |
750 | glInfo: texture_info // |
751 | ); |
752 | |
753 | SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry); |
754 | |
755 | auto surface = SkSurfaces::WrapBackendTexture( |
756 | context, // context |
757 | backendTexture: backend_texture, // back-end texture |
758 | origin: kBottomLeft_GrSurfaceOrigin, // surface origin |
759 | sampleCnt: 1, // sample count |
760 | colorType: kN32_SkColorType, // color type |
761 | colorSpace: SkColorSpace::MakeSRGB(), // color space |
762 | surfaceProps: &surface_properties, // surface properties |
763 | textureReleaseProc: static_cast<SkSurfaces::TextureReleaseProc>( |
764 | texture->destruction_callback), // release proc |
765 | releaseContext: texture->user_data // release context |
766 | ); |
767 | |
768 | if (!surface) { |
769 | FML_LOG(ERROR) << "Could not wrap embedder supplied render texture."; |
770 | return nullptr; |
771 | } |
772 | |
773 | return surface; |
774 | #else |
775 | return nullptr; |
776 | #endif |
777 | } |
778 | |
779 | static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore( |
780 | GrDirectContext* context, |
781 | const FlutterBackingStoreConfig& config, |
782 | const FlutterOpenGLFramebuffer* framebuffer) { |
783 | #ifdef SHELL_ENABLE_GL |
784 | GrGLFramebufferInfo framebuffer_info = {}; |
785 | framebuffer_info.fFormat = framebuffer->target; |
786 | framebuffer_info.fFBOID = framebuffer->name; |
787 | |
788 | auto backend_render_target = |
789 | GrBackendRenderTargets::MakeGL(width: config.size.width, // width |
790 | height: config.size.height, // height |
791 | sampleCnt: 1, // sample count |
792 | stencilBits: 0, // stencil bits |
793 | glInfo: framebuffer_info // framebuffer info |
794 | ); |
795 | |
796 | SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry); |
797 | |
798 | auto surface = SkSurfaces::WrapBackendRenderTarget( |
799 | context, // context |
800 | backendRenderTarget: backend_render_target, // backend render target |
801 | origin: kBottomLeft_GrSurfaceOrigin, // surface origin |
802 | colorType: kN32_SkColorType, // color type |
803 | colorSpace: SkColorSpace::MakeSRGB(), // color space |
804 | surfaceProps: &surface_properties, // surface properties |
805 | releaseProc: static_cast<SkSurfaces::RenderTargetReleaseProc>( |
806 | framebuffer->destruction_callback), // release proc |
807 | releaseContext: framebuffer->user_data // release context |
808 | ); |
809 | |
810 | if (!surface) { |
811 | FML_LOG(ERROR) << "Could not wrap embedder supplied frame-buffer."; |
812 | return nullptr; |
813 | } |
814 | return surface; |
815 | #else |
816 | return nullptr; |
817 | #endif |
818 | } |
819 | |
820 | static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore( |
821 | GrDirectContext* context, |
822 | const FlutterBackingStoreConfig& config, |
823 | const FlutterSoftwareBackingStore* software) { |
824 | const auto image_info = |
825 | SkImageInfo::MakeN32Premul(width: config.size.width, height: config.size.height); |
826 | |
827 | struct Captures { |
828 | VoidCallback destruction_callback; |
829 | void* user_data; |
830 | }; |
831 | auto captures = std::make_unique<Captures>(); |
832 | captures->destruction_callback = software->destruction_callback; |
833 | captures->user_data = software->user_data; |
834 | auto release_proc = [](void* pixels, void* context) { |
835 | auto captures = reinterpret_cast<Captures*>(context); |
836 | if (captures->destruction_callback) { |
837 | captures->destruction_callback(captures->user_data); |
838 | } |
839 | delete captures; |
840 | }; |
841 | |
842 | auto surface = |
843 | SkSurfaces::WrapPixels(imageInfo: image_info, // image info |
844 | pixels: const_cast<void*>(software->allocation), // pixels |
845 | rowBytes: software->row_bytes, // row bytes |
846 | release_proc, // release proc |
847 | context: captures.get() // get context |
848 | ); |
849 | |
850 | if (!surface) { |
851 | FML_LOG(ERROR) |
852 | << "Could not wrap embedder supplied software render buffer."; |
853 | if (software->destruction_callback) { |
854 | software->destruction_callback(software->user_data); |
855 | } |
856 | return nullptr; |
857 | } |
858 | if (surface) { |
859 | captures.release(); // Skia has assumed ownership of the struct. |
860 | } |
861 | return surface; |
862 | } |
863 | |
864 | static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore( |
865 | GrDirectContext* context, |
866 | const FlutterBackingStoreConfig& config, |
867 | const FlutterSoftwareBackingStore2* software) { |
868 | const auto color_info = getSkColorInfo(pixfmt: software->pixel_format); |
869 | if (!color_info) { |
870 | return nullptr; |
871 | } |
872 | |
873 | const auto image_info = SkImageInfo::Make( |
874 | dimensions: SkISize::Make(w: config.size.width, h: config.size.height), colorInfo: *color_info); |
875 | |
876 | struct Captures { |
877 | VoidCallback destruction_callback; |
878 | void* user_data; |
879 | }; |
880 | auto captures = std::make_unique<Captures>(); |
881 | captures->destruction_callback = software->destruction_callback; |
882 | captures->user_data = software->user_data; |
883 | auto release_proc = [](void* pixels, void* context) { |
884 | auto captures = reinterpret_cast<Captures*>(context); |
885 | if (captures->destruction_callback) { |
886 | captures->destruction_callback(captures->user_data); |
887 | } |
888 | }; |
889 | |
890 | auto surface = |
891 | SkSurfaces::WrapPixels(imageInfo: image_info, // image info |
892 | pixels: const_cast<void*>(software->allocation), // pixels |
893 | rowBytes: software->row_bytes, // row bytes |
894 | release_proc, // release proc |
895 | context: captures.release() // release context |
896 | ); |
897 | |
898 | if (!surface) { |
899 | FML_LOG(ERROR) |
900 | << "Could not wrap embedder supplied software render buffer."; |
901 | if (software->destruction_callback) { |
902 | software->destruction_callback(software->user_data); |
903 | } |
904 | return nullptr; |
905 | } |
906 | return surface; |
907 | } |
908 | |
909 | static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore( |
910 | GrDirectContext* context, |
911 | const FlutterBackingStoreConfig& config, |
912 | const FlutterMetalBackingStore* metal) { |
913 | #ifdef SHELL_ENABLE_METAL |
914 | GrMtlTextureInfo texture_info; |
915 | if (!metal->texture.texture) { |
916 | FML_LOG(ERROR) << "Embedder supplied null Metal texture."; |
917 | return nullptr; |
918 | } |
919 | sk_cfp<FlutterMetalTextureHandle> mtl_texture; |
920 | mtl_texture.retain(metal->texture.texture); |
921 | texture_info.fTexture = mtl_texture; |
922 | GrBackendTexture backend_texture(config.size.width, // |
923 | config.size.height, // |
924 | GrMipMapped::kNo, // |
925 | texture_info // |
926 | ); |
927 | |
928 | SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry); |
929 | |
930 | auto surface = SkSurfaces::WrapBackendTexture( |
931 | context, // context |
932 | backend_texture, // back-end texture |
933 | kTopLeft_GrSurfaceOrigin, // surface origin |
934 | // TODO(dnfield): Update this when embedders support MSAA, see |
935 | // https://github.com/flutter/flutter/issues/100392 |
936 | 1, // sample count |
937 | kBGRA_8888_SkColorType, // color type |
938 | nullptr, // color space |
939 | &surface_properties, // surface properties |
940 | static_cast<SkSurfaces::TextureReleaseProc>( |
941 | metal->texture.destruction_callback), // release proc |
942 | metal->texture.user_data // release context |
943 | ); |
944 | |
945 | if (!surface) { |
946 | FML_LOG(ERROR) << "Could not wrap embedder supplied Metal render texture."; |
947 | return nullptr; |
948 | } |
949 | |
950 | return surface; |
951 | #else |
952 | return nullptr; |
953 | #endif |
954 | } |
955 | |
956 | static std::unique_ptr<flutter::EmbedderRenderTarget> |
957 | MakeRenderTargetFromBackingStoreImpeller( |
958 | FlutterBackingStore backing_store, |
959 | const fml::closure& on_release, |
960 | const std::shared_ptr<impeller::AiksContext>& aiks_context, |
961 | const FlutterBackingStoreConfig& config, |
962 | const FlutterOpenGLFramebuffer* framebuffer) { |
963 | #if defined(SHELL_ENABLE_GL) && defined(IMPELLER_SUPPORTS_RENDERING) |
964 | |
965 | const auto& gl_context = |
966 | impeller::ContextGLES::Cast(base&: *aiks_context->GetContext()); |
967 | const auto size = impeller::ISize(config.size.width, config.size.height); |
968 | |
969 | impeller::TextureDescriptor color0_tex; |
970 | color0_tex.type = impeller::TextureType::kTexture2D; |
971 | color0_tex.format = impeller::PixelFormat::kR8G8B8A8UNormInt; |
972 | color0_tex.size = size; |
973 | color0_tex.usage = static_cast<impeller::TextureUsageMask>( |
974 | impeller::TextureUsage::kRenderTarget); |
975 | color0_tex.sample_count = impeller::SampleCount::kCount1; |
976 | color0_tex.storage_mode = impeller::StorageMode::kDevicePrivate; |
977 | |
978 | impeller::ColorAttachment color0; |
979 | color0.texture = std::make_shared<impeller::TextureGLES>( |
980 | args: gl_context.GetReactor(), args&: color0_tex, |
981 | args: impeller::TextureGLES::IsWrapped::kWrapped); |
982 | color0.clear_color = impeller::Color::DarkSlateGray(); |
983 | color0.load_action = impeller::LoadAction::kClear; |
984 | color0.store_action = impeller::StoreAction::kStore; |
985 | |
986 | impeller::TextureDescriptor stencil0_tex; |
987 | stencil0_tex.type = impeller::TextureType::kTexture2D; |
988 | stencil0_tex.format = impeller::PixelFormat::kR8G8B8A8UNormInt; |
989 | stencil0_tex.size = size; |
990 | stencil0_tex.usage = static_cast<impeller::TextureUsageMask>( |
991 | impeller::TextureUsage::kRenderTarget); |
992 | stencil0_tex.sample_count = impeller::SampleCount::kCount1; |
993 | |
994 | impeller::StencilAttachment stencil0; |
995 | stencil0.clear_stencil = 0; |
996 | stencil0.texture = std::make_shared<impeller::TextureGLES>( |
997 | args: gl_context.GetReactor(), args&: stencil0_tex, |
998 | args: impeller::TextureGLES::IsWrapped::kWrapped); |
999 | stencil0.load_action = impeller::LoadAction::kClear; |
1000 | stencil0.store_action = impeller::StoreAction::kDontCare; |
1001 | |
1002 | impeller::RenderTarget render_target_desc; |
1003 | |
1004 | render_target_desc.SetColorAttachment(attachment: color0, index: 0u); |
1005 | render_target_desc.SetStencilAttachment(stencil0); |
1006 | |
1007 | return std::make_unique<flutter::EmbedderRenderTargetImpeller>( |
1008 | args&: backing_store, args: aiks_context, |
1009 | args: std::make_unique<impeller::RenderTarget>(args: std::move(render_target_desc)), |
1010 | args: on_release); |
1011 | #else |
1012 | return nullptr; |
1013 | #endif |
1014 | } |
1015 | |
1016 | static std::unique_ptr<flutter::EmbedderRenderTarget> |
1017 | MakeRenderTargetFromBackingStoreImpeller( |
1018 | FlutterBackingStore backing_store, |
1019 | const fml::closure& on_release, |
1020 | const std::shared_ptr<impeller::AiksContext>& aiks_context, |
1021 | const FlutterBackingStoreConfig& config, |
1022 | const FlutterMetalBackingStore* metal) { |
1023 | #if defined(SHELL_ENABLE_METAL) && defined(IMPELLER_SUPPORTS_RENDERING) |
1024 | if (!metal->texture.texture) { |
1025 | FML_LOG(ERROR) << "Embedder supplied null Metal texture."; |
1026 | return nullptr; |
1027 | } |
1028 | |
1029 | const auto size = impeller::ISize(config.size.width, config.size.height); |
1030 | |
1031 | impeller::TextureDescriptor resolve_tex_desc; |
1032 | resolve_tex_desc.size = size; |
1033 | resolve_tex_desc.sample_count = impeller::SampleCount::kCount1; |
1034 | resolve_tex_desc.storage_mode = impeller::StorageMode::kDevicePrivate; |
1035 | resolve_tex_desc.usage = |
1036 | static_cast<uint64_t>(impeller::TextureUsage::kRenderTarget) | |
1037 | static_cast<uint64_t>(impeller::TextureUsage::kShaderRead); |
1038 | |
1039 | auto resolve_tex = impeller::WrapTextureMTL( |
1040 | resolve_tex_desc, metal->texture.texture, |
1041 | [callback = metal->texture.destruction_callback, |
1042 | user_data = metal->texture.user_data]() { callback(user_data); }); |
1043 | if (!resolve_tex) { |
1044 | FML_LOG(ERROR) << "Could not wrap embedder supplied Metal render texture."; |
1045 | return nullptr; |
1046 | } |
1047 | resolve_tex->SetLabel("ImpellerBackingStoreResolve"); |
1048 | |
1049 | impeller::TextureDescriptor msaa_tex_desc; |
1050 | msaa_tex_desc.storage_mode = impeller::StorageMode::kDeviceTransient; |
1051 | msaa_tex_desc.type = impeller::TextureType::kTexture2DMultisample; |
1052 | msaa_tex_desc.sample_count = impeller::SampleCount::kCount4; |
1053 | msaa_tex_desc.format = resolve_tex->GetTextureDescriptor().format; |
1054 | msaa_tex_desc.size = size; |
1055 | msaa_tex_desc.usage = |
1056 | static_cast<uint64_t>(impeller::TextureUsage::kRenderTarget); |
1057 | |
1058 | auto msaa_tex = |
1059 | aiks_context->GetContext()->GetResourceAllocator()->CreateTexture( |
1060 | msaa_tex_desc); |
1061 | if (!msaa_tex) { |
1062 | FML_LOG(ERROR) << "Could not allocate MSAA color texture."; |
1063 | return nullptr; |
1064 | } |
1065 | msaa_tex->SetLabel("ImpellerBackingStoreColorMSAA"); |
1066 | |
1067 | impeller::ColorAttachment color0; |
1068 | color0.texture = msaa_tex; |
1069 | color0.clear_color = impeller::Color::DarkSlateGray(); |
1070 | color0.load_action = impeller::LoadAction::kClear; |
1071 | color0.store_action = impeller::StoreAction::kMultisampleResolve; |
1072 | color0.resolve_texture = resolve_tex; |
1073 | |
1074 | impeller::RenderTarget render_target_desc; |
1075 | render_target_desc.SetColorAttachment(color0, 0u); |
1076 | |
1077 | return std::make_unique<flutter::EmbedderRenderTargetImpeller>( |
1078 | backing_store, aiks_context, |
1079 | std::make_unique<impeller::RenderTarget>(std::move(render_target_desc)), |
1080 | on_release); |
1081 | #else |
1082 | return nullptr; |
1083 | #endif |
1084 | } |
1085 | |
1086 | static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore( |
1087 | GrDirectContext* context, |
1088 | const FlutterBackingStoreConfig& config, |
1089 | const FlutterVulkanBackingStore* vulkan) { |
1090 | #ifdef SHELL_ENABLE_VULKAN |
1091 | if (!vulkan->image) { |
1092 | FML_LOG(ERROR) << "Embedder supplied null Vulkan image."; |
1093 | return nullptr; |
1094 | } |
1095 | GrVkImageInfo image_info = { |
1096 | .fImage = reinterpret_cast<VkImage>(vulkan->image->image), |
1097 | .fImageTiling = VK_IMAGE_TILING_OPTIMAL, |
1098 | .fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, |
1099 | .fFormat = static_cast<VkFormat>(vulkan->image->format), |
1100 | .fImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | |
1101 | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | |
1102 | VK_IMAGE_USAGE_TRANSFER_DST_BIT | |
1103 | VK_IMAGE_USAGE_SAMPLED_BIT, |
1104 | .fSampleCount = 1, |
1105 | .fLevelCount = 1, |
1106 | }; |
1107 | GrBackendTexture backend_texture(config.size.width, // |
1108 | config.size.height, // |
1109 | image_info // |
1110 | ); |
1111 | |
1112 | SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry); |
1113 | |
1114 | auto surface = SkSurfaces::WrapBackendTexture( |
1115 | context, // context |
1116 | backendTexture: backend_texture, // back-end texture |
1117 | origin: kTopLeft_GrSurfaceOrigin, // surface origin |
1118 | sampleCnt: 1, // sample count |
1119 | colorType: flutter::GPUSurfaceVulkan::ColorTypeFromFormat( |
1120 | format: static_cast<VkFormat>(vulkan->image->format)), // color type |
1121 | colorSpace: SkColorSpace::MakeSRGB(), // color space |
1122 | surfaceProps: &surface_properties, // surface properties |
1123 | textureReleaseProc: static_cast<SkSurfaces::TextureReleaseProc>( |
1124 | vulkan->destruction_callback), // release proc |
1125 | releaseContext: vulkan->user_data // release context |
1126 | ); |
1127 | |
1128 | if (!surface) { |
1129 | FML_LOG(ERROR) << "Could not wrap embedder supplied Vulkan render texture."; |
1130 | return nullptr; |
1131 | } |
1132 | |
1133 | return surface; |
1134 | #else |
1135 | return nullptr; |
1136 | #endif |
1137 | } |
1138 | |
1139 | static std::unique_ptr<flutter::EmbedderRenderTarget> |
1140 | MakeRenderTargetFromSkSurface(FlutterBackingStore backing_store, |
1141 | sk_sp<SkSurface> skia_surface, |
1142 | fml::closure on_release) { |
1143 | if (!skia_surface) { |
1144 | return nullptr; |
1145 | } |
1146 | return std::make_unique<flutter::EmbedderRenderTargetSkia>( |
1147 | args&: backing_store, args: std::move(skia_surface), args: std::move(on_release)); |
1148 | } |
1149 | |
1150 | static std::unique_ptr<flutter::EmbedderRenderTarget> |
1151 | CreateEmbedderRenderTarget( |
1152 | const FlutterCompositor* compositor, |
1153 | const FlutterBackingStoreConfig& config, |
1154 | GrDirectContext* context, |
1155 | const std::shared_ptr<impeller::AiksContext>& aiks_context, |
1156 | bool enable_impeller) { |
1157 | FlutterBackingStore backing_store = {}; |
1158 | backing_store.struct_size = sizeof(backing_store); |
1159 | |
1160 | // Safe access checks on the compositor struct have been performed in |
1161 | // InferExternalViewEmbedderFromArgs and are not necessary here. |
1162 | auto c_create_callback = compositor->create_backing_store_callback; |
1163 | auto c_collect_callback = compositor->collect_backing_store_callback; |
1164 | |
1165 | { |
1166 | TRACE_EVENT0("flutter", "FlutterCompositorCreateBackingStore"); |
1167 | if (!c_create_callback(&config, &backing_store, compositor->user_data)) { |
1168 | FML_LOG(ERROR) << "Could not create the embedder backing store."; |
1169 | return nullptr; |
1170 | } |
1171 | } |
1172 | |
1173 | if (backing_store.struct_size != sizeof(backing_store)) { |
1174 | FML_LOG(ERROR) << "Embedder modified the backing store struct size."; |
1175 | return nullptr; |
1176 | } |
1177 | |
1178 | // In case we return early without creating an embedder render target, the |
1179 | // embedder has still given us ownership of its baton which we must return |
1180 | // back to it. If this method is successful, the closure is released when the |
1181 | // render target is eventually released. |
1182 | fml::ScopedCleanupClosure collect_callback( |
1183 | [c_collect_callback, backing_store, user_data = compositor->user_data]() { |
1184 | TRACE_EVENT0("flutter", "FlutterCompositorCollectBackingStore"); |
1185 | c_collect_callback(&backing_store, user_data); |
1186 | }); |
1187 | |
1188 | // No safe access checks on the renderer are necessary since we allocated |
1189 | // the struct. |
1190 | |
1191 | std::unique_ptr<flutter::EmbedderRenderTarget> render_target; |
1192 | |
1193 | switch (backing_store.type) { |
1194 | case kFlutterBackingStoreTypeOpenGL: { |
1195 | switch (backing_store.open_gl.type) { |
1196 | case kFlutterOpenGLTargetTypeTexture: { |
1197 | auto skia_surface = MakeSkSurfaceFromBackingStore( |
1198 | context, config, texture: &backing_store.open_gl.texture); |
1199 | render_target = MakeRenderTargetFromSkSurface( |
1200 | backing_store, skia_surface: std::move(skia_surface), |
1201 | on_release: collect_callback.Release()); |
1202 | break; |
1203 | } |
1204 | case kFlutterOpenGLTargetTypeFramebuffer: { |
1205 | if (enable_impeller) { |
1206 | render_target = MakeRenderTargetFromBackingStoreImpeller( |
1207 | backing_store, on_release: collect_callback.Release(), aiks_context, config, |
1208 | framebuffer: &backing_store.open_gl.framebuffer); |
1209 | break; |
1210 | } else { |
1211 | auto skia_surface = MakeSkSurfaceFromBackingStore( |
1212 | context, config, framebuffer: &backing_store.open_gl.framebuffer); |
1213 | render_target = MakeRenderTargetFromSkSurface( |
1214 | backing_store, skia_surface: std::move(skia_surface), |
1215 | on_release: collect_callback.Release()); |
1216 | break; |
1217 | } |
1218 | } |
1219 | } |
1220 | break; |
1221 | } |
1222 | case kFlutterBackingStoreTypeSoftware: { |
1223 | auto skia_surface = MakeSkSurfaceFromBackingStore( |
1224 | context, config, software: &backing_store.software); |
1225 | render_target = MakeRenderTargetFromSkSurface( |
1226 | backing_store, skia_surface: std::move(skia_surface), on_release: collect_callback.Release()); |
1227 | break; |
1228 | } |
1229 | case kFlutterBackingStoreTypeSoftware2: { |
1230 | auto skia_surface = MakeSkSurfaceFromBackingStore( |
1231 | context, config, software: &backing_store.software2); |
1232 | render_target = MakeRenderTargetFromSkSurface( |
1233 | backing_store, skia_surface: std::move(skia_surface), on_release: collect_callback.Release()); |
1234 | break; |
1235 | } |
1236 | case kFlutterBackingStoreTypeMetal: { |
1237 | if (enable_impeller) { |
1238 | render_target = MakeRenderTargetFromBackingStoreImpeller( |
1239 | backing_store, on_release: collect_callback.Release(), aiks_context, config, |
1240 | metal: &backing_store.metal); |
1241 | } else { |
1242 | auto skia_surface = MakeSkSurfaceFromBackingStore(context, config, |
1243 | metal: &backing_store.metal); |
1244 | render_target = MakeRenderTargetFromSkSurface( |
1245 | backing_store, skia_surface: std::move(skia_surface), on_release: collect_callback.Release()); |
1246 | } |
1247 | break; |
1248 | } |
1249 | case kFlutterBackingStoreTypeVulkan: { |
1250 | auto skia_surface = |
1251 | MakeSkSurfaceFromBackingStore(context, config, vulkan: &backing_store.vulkan); |
1252 | render_target = MakeRenderTargetFromSkSurface( |
1253 | backing_store, skia_surface: std::move(skia_surface), on_release: collect_callback.Release()); |
1254 | break; |
1255 | } |
1256 | }; |
1257 | |
1258 | if (!render_target) { |
1259 | FML_LOG(ERROR) << "Could not create a surface from an embedder provided " |
1260 | "render target."; |
1261 | } |
1262 | return render_target; |
1263 | } |
1264 | |
1265 | static std::pair<std::unique_ptr<flutter::EmbedderExternalViewEmbedder>, |
1266 | bool /* halt engine launch if true */> |
1267 | InferExternalViewEmbedderFromArgs(const FlutterCompositor* compositor, |
1268 | bool enable_impeller) { |
1269 | if (compositor == nullptr) { |
1270 | return {nullptr, false}; |
1271 | } |
1272 | |
1273 | auto c_create_callback = |
1274 | SAFE_ACCESS(compositor, create_backing_store_callback, nullptr); |
1275 | auto c_collect_callback = |
1276 | SAFE_ACCESS(compositor, collect_backing_store_callback, nullptr); |
1277 | auto c_present_callback = |
1278 | SAFE_ACCESS(compositor, present_layers_callback, nullptr); |
1279 | bool avoid_backing_store_cache = |
1280 | SAFE_ACCESS(compositor, avoid_backing_store_cache, false); |
1281 | |
1282 | // Make sure the required callbacks are present |
1283 | if (!c_create_callback || !c_collect_callback || !c_present_callback) { |
1284 | FML_LOG(ERROR) << "Required compositor callbacks absent."; |
1285 | return {nullptr, true}; |
1286 | } |
1287 | |
1288 | FlutterCompositor captured_compositor = *compositor; |
1289 | |
1290 | flutter::EmbedderExternalViewEmbedder::CreateRenderTargetCallback |
1291 | create_render_target_callback = |
1292 | [captured_compositor, enable_impeller]( |
1293 | GrDirectContext* context, |
1294 | const std::shared_ptr<impeller::AiksContext>& aiks_context, |
1295 | const auto& config) { |
1296 | return CreateEmbedderRenderTarget(&captured_compositor, config, |
1297 | context, aiks_context, |
1298 | enable_impeller); |
1299 | }; |
1300 | |
1301 | flutter::EmbedderExternalViewEmbedder::PresentCallback present_callback = |
1302 | [c_present_callback, |
1303 | user_data = compositor->user_data](const auto& layers) { |
1304 | TRACE_EVENT0("flutter", "FlutterCompositorPresentLayers"); |
1305 | return c_present_callback( |
1306 | const_cast<const FlutterLayer**>(layers.data()), layers.size(), |
1307 | user_data); |
1308 | }; |
1309 | |
1310 | return {std::make_unique<flutter::EmbedderExternalViewEmbedder>( |
1311 | args&: avoid_backing_store_cache, args&: create_render_target_callback, |
1312 | args&: present_callback), |
1313 | false}; |
1314 | } |
1315 | |
1316 | struct _FlutterPlatformMessageResponseHandle { |
1317 | std::unique_ptr<flutter::PlatformMessage> message; |
1318 | }; |
1319 | |
1320 | struct LoadedElfDeleter { |
1321 | void operator()(Dart_LoadedElf* elf) { |
1322 | if (elf) { |
1323 | ::Dart_UnloadELF(loaded: elf); |
1324 | } |
1325 | } |
1326 | }; |
1327 | |
1328 | using UniqueLoadedElf = std::unique_ptr<Dart_LoadedElf, LoadedElfDeleter>; |
1329 | |
1330 | struct _FlutterEngineAOTData { |
1331 | UniqueLoadedElf loaded_elf = nullptr; |
1332 | const uint8_t* vm_snapshot_data = nullptr; |
1333 | const uint8_t* vm_snapshot_instrs = nullptr; |
1334 | const uint8_t* vm_isolate_data = nullptr; |
1335 | const uint8_t* vm_isolate_instrs = nullptr; |
1336 | }; |
1337 | |
1338 | FlutterEngineResult FlutterEngineCreateAOTData( |
1339 | const FlutterEngineAOTDataSource* source, |
1340 | FlutterEngineAOTData* data_out) { |
1341 | if (!flutter::DartVM::IsRunningPrecompiledCode()) { |
1342 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
1343 | "AOT data can only be created in AOT mode."); |
1344 | } else if (!source) { |
1345 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Null source specified."); |
1346 | } else if (!data_out) { |
1347 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Null data_out specified."); |
1348 | } |
1349 | |
1350 | switch (source->type) { |
1351 | case kFlutterEngineAOTDataSourceTypeElfPath: { |
1352 | if (!source->elf_path || !fml::IsFile(path: source->elf_path)) { |
1353 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
1354 | "Invalid ELF path specified."); |
1355 | } |
1356 | |
1357 | auto aot_data = std::make_unique<_FlutterEngineAOTData>(); |
1358 | const char* error = nullptr; |
1359 | |
1360 | #if OS_FUCHSIA |
1361 | // TODO(gw280): https://github.com/flutter/flutter/issues/50285 |
1362 | // Dart doesn't implement Dart_LoadELF on Fuchsia |
1363 | Dart_LoadedElf* loaded_elf = nullptr; |
1364 | #else |
1365 | Dart_LoadedElf* loaded_elf = Dart_LoadELF( |
1366 | filename: source->elf_path, // file path |
1367 | file_offset: 0, // file offset |
1368 | error: &error, // error (out) |
1369 | vm_snapshot_data: &aot_data->vm_snapshot_data, // vm snapshot data (out) |
1370 | vm_snapshot_instrs: &aot_data->vm_snapshot_instrs, // vm snapshot instr (out) |
1371 | vm_isolate_data: &aot_data->vm_isolate_data, // vm isolate data (out) |
1372 | vm_isolate_instrs: &aot_data->vm_isolate_instrs // vm isolate instr (out) |
1373 | ); |
1374 | #endif |
1375 | |
1376 | if (loaded_elf == nullptr) { |
1377 | return LOG_EMBEDDER_ERROR(kInvalidArguments, error); |
1378 | } |
1379 | |
1380 | aot_data->loaded_elf.reset(p: loaded_elf); |
1381 | |
1382 | *data_out = aot_data.release(); |
1383 | return kSuccess; |
1384 | } |
1385 | } |
1386 | |
1387 | return LOG_EMBEDDER_ERROR( |
1388 | kInvalidArguments, |
1389 | "Invalid FlutterEngineAOTDataSourceType type specified."); |
1390 | } |
1391 | |
1392 | FlutterEngineResult FlutterEngineCollectAOTData(FlutterEngineAOTData data) { |
1393 | if (!data) { |
1394 | // Deleting a null object should be a no-op. |
1395 | return kSuccess; |
1396 | } |
1397 | |
1398 | // Created in a unique pointer in `FlutterEngineCreateAOTData`. |
1399 | delete data; |
1400 | return kSuccess; |
1401 | } |
1402 | |
1403 | // Constructs appropriate mapping callbacks if JIT snapshot locations have been |
1404 | // explictly specified. |
1405 | void PopulateJITSnapshotMappingCallbacks(const FlutterProjectArgs* args, |
1406 | flutter::Settings& settings) { |
1407 | auto make_mapping_callback = [](const char* path, bool executable) { |
1408 | return [path, executable]() { |
1409 | if (executable) { |
1410 | return fml::FileMapping::CreateReadExecute(path); |
1411 | } else { |
1412 | return fml::FileMapping::CreateReadOnly(path); |
1413 | } |
1414 | }; |
1415 | }; |
1416 | |
1417 | // Users are allowed to specify only certain snapshots if they so desire. |
1418 | if (SAFE_ACCESS(args, vm_snapshot_data, nullptr) != nullptr) { |
1419 | settings.vm_snapshot_data = make_mapping_callback( |
1420 | reinterpret_cast<const char*>(args->vm_snapshot_data), false); |
1421 | } |
1422 | |
1423 | if (SAFE_ACCESS(args, vm_snapshot_instructions, nullptr) != nullptr) { |
1424 | settings.vm_snapshot_instr = make_mapping_callback( |
1425 | reinterpret_cast<const char*>(args->vm_snapshot_instructions), true); |
1426 | } |
1427 | |
1428 | if (SAFE_ACCESS(args, isolate_snapshot_data, nullptr) != nullptr) { |
1429 | settings.isolate_snapshot_data = make_mapping_callback( |
1430 | reinterpret_cast<const char*>(args->isolate_snapshot_data), false); |
1431 | } |
1432 | |
1433 | if (SAFE_ACCESS(args, isolate_snapshot_instructions, nullptr) != nullptr) { |
1434 | settings.isolate_snapshot_instr = make_mapping_callback( |
1435 | reinterpret_cast<const char*>(args->isolate_snapshot_instructions), |
1436 | true); |
1437 | } |
1438 | |
1439 | #if !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) |
1440 | settings.dart_library_sources_kernel = []() { |
1441 | return std::make_unique<fml::NonOwnedMapping>(args: kPlatformStrongDill, |
1442 | args: kPlatformStrongDillSize); |
1443 | }; |
1444 | #endif // !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE == |
1445 | // FLUTTER_RUNTIME_MODE_DEBUG) |
1446 | } |
1447 | |
1448 | void PopulateAOTSnapshotMappingCallbacks( |
1449 | const FlutterProjectArgs* args, |
1450 | flutter::Settings& settings) { // NOLINT(google-runtime-references) |
1451 | // There are no ownership concerns here as all mappings are owned by the |
1452 | // embedder and not the engine. |
1453 | auto make_mapping_callback = [](const uint8_t* mapping, size_t size) { |
1454 | return [mapping, size]() { |
1455 | return std::make_unique<fml::NonOwnedMapping>(args: mapping, args: size); |
1456 | }; |
1457 | }; |
1458 | |
1459 | if (SAFE_ACCESS(args, aot_data, nullptr) != nullptr) { |
1460 | settings.vm_snapshot_data = |
1461 | make_mapping_callback(args->aot_data->vm_snapshot_data, 0); |
1462 | |
1463 | settings.vm_snapshot_instr = |
1464 | make_mapping_callback(args->aot_data->vm_snapshot_instrs, 0); |
1465 | |
1466 | settings.isolate_snapshot_data = |
1467 | make_mapping_callback(args->aot_data->vm_isolate_data, 0); |
1468 | |
1469 | settings.isolate_snapshot_instr = |
1470 | make_mapping_callback(args->aot_data->vm_isolate_instrs, 0); |
1471 | } |
1472 | |
1473 | if (SAFE_ACCESS(args, vm_snapshot_data, nullptr) != nullptr) { |
1474 | settings.vm_snapshot_data = make_mapping_callback( |
1475 | args->vm_snapshot_data, SAFE_ACCESS(args, vm_snapshot_data_size, 0)); |
1476 | } |
1477 | |
1478 | if (SAFE_ACCESS(args, vm_snapshot_instructions, nullptr) != nullptr) { |
1479 | settings.vm_snapshot_instr = make_mapping_callback( |
1480 | args->vm_snapshot_instructions, |
1481 | SAFE_ACCESS(args, vm_snapshot_instructions_size, 0)); |
1482 | } |
1483 | |
1484 | if (SAFE_ACCESS(args, isolate_snapshot_data, nullptr) != nullptr) { |
1485 | settings.isolate_snapshot_data = |
1486 | make_mapping_callback(args->isolate_snapshot_data, |
1487 | SAFE_ACCESS(args, isolate_snapshot_data_size, 0)); |
1488 | } |
1489 | |
1490 | if (SAFE_ACCESS(args, isolate_snapshot_instructions, nullptr) != nullptr) { |
1491 | settings.isolate_snapshot_instr = make_mapping_callback( |
1492 | args->isolate_snapshot_instructions, |
1493 | SAFE_ACCESS(args, isolate_snapshot_instructions_size, 0)); |
1494 | } |
1495 | } |
1496 | |
1497 | // Translates engine semantic nodes to embedder semantic nodes. |
1498 | FlutterSemanticsNode CreateEmbedderSemanticsNode( |
1499 | const flutter::SemanticsNode& node) { |
1500 | SkMatrix transform = node.transform.asM33(); |
1501 | FlutterTransformation flutter_transform{ |
1502 | .scaleX: transform.get(index: SkMatrix::kMScaleX), .skewX: transform.get(index: SkMatrix::kMSkewX), |
1503 | .transX: transform.get(index: SkMatrix::kMTransX), .skewY: transform.get(index: SkMatrix::kMSkewY), |
1504 | .scaleY: transform.get(index: SkMatrix::kMScaleY), .transY: transform.get(index: SkMatrix::kMTransY), |
1505 | .pers0: transform.get(index: SkMatrix::kMPersp0), .pers1: transform.get(index: SkMatrix::kMPersp1), |
1506 | .pers2: transform.get(index: SkMatrix::kMPersp2)}; |
1507 | |
1508 | // Do not add new members to FlutterSemanticsNode. |
1509 | // This would break the forward compatibility of FlutterSemanticsUpdate. |
1510 | // All new members must be added to FlutterSemanticsNode2 instead. |
1511 | return { |
1512 | .struct_size: sizeof(FlutterSemanticsNode), |
1513 | .id: node.id, |
1514 | .flags: static_cast<FlutterSemanticsFlag>(node.flags), |
1515 | .actions: static_cast<FlutterSemanticsAction>(node.actions), |
1516 | .text_selection_base: node.textSelectionBase, |
1517 | .text_selection_extent: node.textSelectionExtent, |
1518 | .scroll_child_count: node.scrollChildren, |
1519 | .scroll_index: node.scrollIndex, |
1520 | .scroll_position: node.scrollPosition, |
1521 | .scroll_extent_max: node.scrollExtentMax, |
1522 | .scroll_extent_min: node.scrollExtentMin, |
1523 | .elevation: node.elevation, |
1524 | .thickness: node.thickness, |
1525 | .label: node.label.c_str(), |
1526 | .hint: node.hint.c_str(), |
1527 | .value: node.value.c_str(), |
1528 | .increased_value: node.increasedValue.c_str(), |
1529 | .decreased_value: node.decreasedValue.c_str(), |
1530 | .text_direction: static_cast<FlutterTextDirection>(node.textDirection), |
1531 | .rect: FlutterRect{.left: node.rect.fLeft, .top: node.rect.fTop, .right: node.rect.fRight, |
1532 | .bottom: node.rect.fBottom}, |
1533 | .transform: flutter_transform, |
1534 | .child_count: node.childrenInTraversalOrder.size(), |
1535 | .children_in_traversal_order: node.childrenInTraversalOrder.data(), |
1536 | .children_in_hit_test_order: node.childrenInHitTestOrder.data(), |
1537 | .custom_accessibility_actions_count: node.customAccessibilityActions.size(), |
1538 | .custom_accessibility_actions: node.customAccessibilityActions.data(), |
1539 | .platform_view_id: node.platformViewId, |
1540 | .tooltip: node.tooltip.c_str(), |
1541 | }; |
1542 | } |
1543 | |
1544 | // Translates engine semantic nodes to embedder semantic nodes. |
1545 | FlutterSemanticsNode2 CreateEmbedderSemanticsNode2( |
1546 | const flutter::SemanticsNode& node) { |
1547 | SkMatrix transform = node.transform.asM33(); |
1548 | FlutterTransformation flutter_transform{ |
1549 | .scaleX: transform.get(index: SkMatrix::kMScaleX), .skewX: transform.get(index: SkMatrix::kMSkewX), |
1550 | .transX: transform.get(index: SkMatrix::kMTransX), .skewY: transform.get(index: SkMatrix::kMSkewY), |
1551 | .scaleY: transform.get(index: SkMatrix::kMScaleY), .transY: transform.get(index: SkMatrix::kMTransY), |
1552 | .pers0: transform.get(index: SkMatrix::kMPersp0), .pers1: transform.get(index: SkMatrix::kMPersp1), |
1553 | .pers2: transform.get(index: SkMatrix::kMPersp2)}; |
1554 | return { |
1555 | .struct_size: sizeof(FlutterSemanticsNode2), |
1556 | .id: node.id, |
1557 | .flags: static_cast<FlutterSemanticsFlag>(node.flags), |
1558 | .actions: static_cast<FlutterSemanticsAction>(node.actions), |
1559 | .text_selection_base: node.textSelectionBase, |
1560 | .text_selection_extent: node.textSelectionExtent, |
1561 | .scroll_child_count: node.scrollChildren, |
1562 | .scroll_index: node.scrollIndex, |
1563 | .scroll_position: node.scrollPosition, |
1564 | .scroll_extent_max: node.scrollExtentMax, |
1565 | .scroll_extent_min: node.scrollExtentMin, |
1566 | .elevation: node.elevation, |
1567 | .thickness: node.thickness, |
1568 | .label: node.label.c_str(), |
1569 | .hint: node.hint.c_str(), |
1570 | .value: node.value.c_str(), |
1571 | .increased_value: node.increasedValue.c_str(), |
1572 | .decreased_value: node.decreasedValue.c_str(), |
1573 | .text_direction: static_cast<FlutterTextDirection>(node.textDirection), |
1574 | .rect: FlutterRect{.left: node.rect.fLeft, .top: node.rect.fTop, .right: node.rect.fRight, |
1575 | .bottom: node.rect.fBottom}, |
1576 | .transform: flutter_transform, |
1577 | .child_count: node.childrenInTraversalOrder.size(), |
1578 | .children_in_traversal_order: node.childrenInTraversalOrder.data(), |
1579 | .children_in_hit_test_order: node.childrenInHitTestOrder.data(), |
1580 | .custom_accessibility_actions_count: node.customAccessibilityActions.size(), |
1581 | .custom_accessibility_actions: node.customAccessibilityActions.data(), |
1582 | .platform_view_id: node.platformViewId, |
1583 | .tooltip: node.tooltip.c_str(), |
1584 | }; |
1585 | } |
1586 | |
1587 | // Translates engine semantic custom actions to embedder semantic custom |
1588 | // actions. |
1589 | FlutterSemanticsCustomAction CreateEmbedderSemanticsCustomAction( |
1590 | const flutter::CustomAccessibilityAction& action) { |
1591 | // Do not add new members to FlutterSemanticsCustomAction. |
1592 | // This would break the forward compatibility of FlutterSemanticsUpdate. |
1593 | // All new members must be added to FlutterSemanticsCustomAction2 instead. |
1594 | return { |
1595 | .struct_size: sizeof(FlutterSemanticsCustomAction), |
1596 | .id: action.id, |
1597 | .override_action: static_cast<FlutterSemanticsAction>(action.overrideId), |
1598 | .label: action.label.c_str(), |
1599 | .hint: action.hint.c_str(), |
1600 | }; |
1601 | } |
1602 | |
1603 | // Translates engine semantic custom actions to embedder semantic custom |
1604 | // actions. |
1605 | FlutterSemanticsCustomAction2 CreateEmbedderSemanticsCustomAction2( |
1606 | const flutter::CustomAccessibilityAction& action) { |
1607 | return { |
1608 | .struct_size: sizeof(FlutterSemanticsCustomAction2), |
1609 | .id: action.id, |
1610 | .override_action: static_cast<FlutterSemanticsAction>(action.overrideId), |
1611 | .label: action.label.c_str(), |
1612 | .hint: action.hint.c_str(), |
1613 | }; |
1614 | } |
1615 | |
1616 | // Create a callback to notify the embedder of semantic updates |
1617 | // using the deprecated embedder callback 'update_semantics_callback'. |
1618 | flutter::PlatformViewEmbedder::UpdateSemanticsCallback |
1619 | CreateNewEmbedderSemanticsUpdateCallback( |
1620 | FlutterUpdateSemanticsCallback update_semantics_callback, |
1621 | void* user_data) { |
1622 | return [update_semantics_callback, user_data]( |
1623 | const flutter::SemanticsNodeUpdates& nodes, |
1624 | const flutter::CustomAccessibilityActionUpdates& actions) { |
1625 | std::vector<FlutterSemanticsNode> embedder_nodes; |
1626 | for (const auto& value : nodes) { |
1627 | embedder_nodes.push_back(x: CreateEmbedderSemanticsNode(node: value.second)); |
1628 | } |
1629 | |
1630 | std::vector<FlutterSemanticsCustomAction> embedder_custom_actions; |
1631 | for (const auto& value : actions) { |
1632 | embedder_custom_actions.push_back( |
1633 | x: CreateEmbedderSemanticsCustomAction(action: value.second)); |
1634 | } |
1635 | |
1636 | FlutterSemanticsUpdate update{ |
1637 | .struct_size = sizeof(FlutterSemanticsUpdate), |
1638 | .nodes_count = embedder_nodes.size(), |
1639 | .nodes = embedder_nodes.data(), |
1640 | .custom_actions_count = embedder_custom_actions.size(), |
1641 | .custom_actions = embedder_custom_actions.data(), |
1642 | }; |
1643 | |
1644 | update_semantics_callback(&update, user_data); |
1645 | }; |
1646 | } |
1647 | |
1648 | // Create a callback to notify the embedder of semantic updates |
1649 | // using the new embedder callback 'update_semantics_callback2'. |
1650 | flutter::PlatformViewEmbedder::UpdateSemanticsCallback |
1651 | CreateNewEmbedderSemanticsUpdateCallback2( |
1652 | FlutterUpdateSemanticsCallback2 update_semantics_callback, |
1653 | void* user_data) { |
1654 | return [update_semantics_callback, user_data]( |
1655 | const flutter::SemanticsNodeUpdates& nodes, |
1656 | const flutter::CustomAccessibilityActionUpdates& actions) { |
1657 | std::vector<FlutterSemanticsNode2> embedder_nodes; |
1658 | std::vector<FlutterSemanticsCustomAction2> embedder_custom_actions; |
1659 | |
1660 | embedder_nodes.reserve(n: nodes.size()); |
1661 | embedder_custom_actions.reserve(n: actions.size()); |
1662 | |
1663 | for (const auto& value : nodes) { |
1664 | embedder_nodes.push_back(x: CreateEmbedderSemanticsNode2(node: value.second)); |
1665 | } |
1666 | |
1667 | for (const auto& value : actions) { |
1668 | embedder_custom_actions.push_back( |
1669 | x: CreateEmbedderSemanticsCustomAction2(action: value.second)); |
1670 | } |
1671 | |
1672 | // Provide the embedder an array of pointers to maintain full forward and |
1673 | // backward compatibility even if new members are added to semantic structs. |
1674 | std::vector<FlutterSemanticsNode2*> embedder_node_pointers; |
1675 | std::vector<FlutterSemanticsCustomAction2*> embedder_custom_action_pointers; |
1676 | |
1677 | embedder_node_pointers.reserve(n: embedder_nodes.size()); |
1678 | embedder_custom_action_pointers.reserve(n: embedder_custom_actions.size()); |
1679 | |
1680 | for (auto& node : embedder_nodes) { |
1681 | embedder_node_pointers.push_back(x: &node); |
1682 | } |
1683 | |
1684 | for (auto& action : embedder_custom_actions) { |
1685 | embedder_custom_action_pointers.push_back(x: &action); |
1686 | } |
1687 | |
1688 | FlutterSemanticsUpdate2 update{ |
1689 | .struct_size = sizeof(FlutterSemanticsUpdate2), |
1690 | .node_count = embedder_node_pointers.size(), |
1691 | .nodes = embedder_node_pointers.data(), |
1692 | .custom_action_count = embedder_custom_action_pointers.size(), |
1693 | .custom_actions = embedder_custom_action_pointers.data(), |
1694 | }; |
1695 | |
1696 | update_semantics_callback(&update, user_data); |
1697 | }; |
1698 | } |
1699 | |
1700 | // Create a callback to notify the embedder of semantic updates |
1701 | // using the legacy embedder callbacks 'update_semantics_node_callback' and |
1702 | // 'update_semantics_custom_action_callback'. |
1703 | flutter::PlatformViewEmbedder::UpdateSemanticsCallback |
1704 | CreateLegacyEmbedderSemanticsUpdateCallback( |
1705 | FlutterUpdateSemanticsNodeCallback update_semantics_node_callback, |
1706 | FlutterUpdateSemanticsCustomActionCallback |
1707 | update_semantics_custom_action_callback, |
1708 | void* user_data) { |
1709 | return [update_semantics_node_callback, |
1710 | update_semantics_custom_action_callback, |
1711 | user_data](const flutter::SemanticsNodeUpdates& nodes, |
1712 | const flutter::CustomAccessibilityActionUpdates& actions) { |
1713 | // First, queue all node and custom action updates. |
1714 | if (update_semantics_node_callback != nullptr) { |
1715 | for (const auto& value : nodes) { |
1716 | const FlutterSemanticsNode embedder_node = |
1717 | CreateEmbedderSemanticsNode(node: value.second); |
1718 | update_semantics_node_callback(&embedder_node, user_data); |
1719 | } |
1720 | } |
1721 | |
1722 | if (update_semantics_custom_action_callback != nullptr) { |
1723 | for (const auto& value : actions) { |
1724 | const FlutterSemanticsCustomAction embedder_action = |
1725 | CreateEmbedderSemanticsCustomAction(action: value.second); |
1726 | update_semantics_custom_action_callback(&embedder_action, user_data); |
1727 | } |
1728 | } |
1729 | |
1730 | // Second, mark node and action batches completed now that all |
1731 | // updates are queued. |
1732 | if (update_semantics_node_callback != nullptr) { |
1733 | const FlutterSemanticsNode batch_end_sentinel = { |
1734 | .struct_size: sizeof(FlutterSemanticsNode), |
1735 | .id: kFlutterSemanticsNodeIdBatchEnd, |
1736 | }; |
1737 | update_semantics_node_callback(&batch_end_sentinel, user_data); |
1738 | } |
1739 | |
1740 | if (update_semantics_custom_action_callback != nullptr) { |
1741 | const FlutterSemanticsCustomAction batch_end_sentinel = { |
1742 | .struct_size: sizeof(FlutterSemanticsCustomAction), |
1743 | .id: kFlutterSemanticsCustomActionIdBatchEnd, |
1744 | }; |
1745 | update_semantics_custom_action_callback(&batch_end_sentinel, user_data); |
1746 | } |
1747 | }; |
1748 | } |
1749 | |
1750 | // Creates a callback that receives semantic updates from the engine |
1751 | // and notifies the embedder's callback(s). Returns null if the embedder |
1752 | // did not register any callbacks. |
1753 | flutter::PlatformViewEmbedder::UpdateSemanticsCallback |
1754 | CreateEmbedderSemanticsUpdateCallback(const FlutterProjectArgs* args, |
1755 | void* user_data) { |
1756 | // The embedder can register the new callback, or the legacy callbacks, or |
1757 | // nothing at all. Handle the case where the embedder registered the 'new' |
1758 | // callback. |
1759 | if (SAFE_ACCESS(args, update_semantics_callback2, nullptr) != nullptr) { |
1760 | return CreateNewEmbedderSemanticsUpdateCallback2( |
1761 | update_semantics_callback: args->update_semantics_callback2, user_data); |
1762 | } |
1763 | |
1764 | if (SAFE_ACCESS(args, update_semantics_callback, nullptr) != nullptr) { |
1765 | return CreateNewEmbedderSemanticsUpdateCallback( |
1766 | update_semantics_callback: args->update_semantics_callback, user_data); |
1767 | } |
1768 | |
1769 | // Handle the case where the embedder registered 'legacy' callbacks. |
1770 | FlutterUpdateSemanticsNodeCallback update_semantics_node_callback = nullptr; |
1771 | if (SAFE_ACCESS(args, update_semantics_node_callback, nullptr) != nullptr) { |
1772 | update_semantics_node_callback = args->update_semantics_node_callback; |
1773 | } |
1774 | |
1775 | FlutterUpdateSemanticsCustomActionCallback |
1776 | update_semantics_custom_action_callback = nullptr; |
1777 | if (SAFE_ACCESS(args, update_semantics_custom_action_callback, nullptr) != |
1778 | nullptr) { |
1779 | update_semantics_custom_action_callback = |
1780 | args->update_semantics_custom_action_callback; |
1781 | } |
1782 | |
1783 | if (update_semantics_node_callback != nullptr || |
1784 | update_semantics_custom_action_callback != nullptr) { |
1785 | return CreateLegacyEmbedderSemanticsUpdateCallback( |
1786 | update_semantics_node_callback, update_semantics_custom_action_callback, |
1787 | user_data); |
1788 | } |
1789 | |
1790 | // Handle the case where the embedder registered no callbacks. |
1791 | return nullptr; |
1792 | } |
1793 | |
1794 | FlutterEngineResult FlutterEngineRun(size_t version, |
1795 | const FlutterRendererConfig* config, |
1796 | const FlutterProjectArgs* args, |
1797 | void* user_data, |
1798 | FLUTTER_API_SYMBOL(FlutterEngine) * |
1799 | engine_out) { |
1800 | auto result = |
1801 | FlutterEngineInitialize(version, config, args, user_data, engine_out); |
1802 | |
1803 | if (result != kSuccess) { |
1804 | return result; |
1805 | } |
1806 | |
1807 | return FlutterEngineRunInitialized(engine: *engine_out); |
1808 | } |
1809 | |
1810 | FlutterEngineResult FlutterEngineInitialize(size_t version, |
1811 | const FlutterRendererConfig* config, |
1812 | const FlutterProjectArgs* args, |
1813 | void* user_data, |
1814 | FLUTTER_API_SYMBOL(FlutterEngine) * |
1815 | engine_out) { |
1816 | // Step 0: Figure out arguments for shell creation. |
1817 | if (version != FLUTTER_ENGINE_VERSION) { |
1818 | return LOG_EMBEDDER_ERROR( |
1819 | kInvalidLibraryVersion, |
1820 | "Flutter embedder version mismatch. There has been a breaking change. " |
1821 | "Please consult the changelog and update the embedder."); |
1822 | } |
1823 | |
1824 | if (engine_out == nullptr) { |
1825 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
1826 | "The engine out parameter was missing."); |
1827 | } |
1828 | |
1829 | if (args == nullptr) { |
1830 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
1831 | "The Flutter project arguments were missing."); |
1832 | } |
1833 | |
1834 | if (SAFE_ACCESS(args, assets_path, nullptr) == nullptr) { |
1835 | return LOG_EMBEDDER_ERROR( |
1836 | kInvalidArguments, |
1837 | "The assets path in the Flutter project arguments was missing."); |
1838 | } |
1839 | |
1840 | if (SAFE_ACCESS(args, main_path__unused__, nullptr) != nullptr) { |
1841 | FML_LOG(WARNING) |
1842 | << "FlutterProjectArgs.main_path is deprecated and should be set null."; |
1843 | } |
1844 | |
1845 | if (SAFE_ACCESS(args, packages_path__unused__, nullptr) != nullptr) { |
1846 | FML_LOG(WARNING) << "FlutterProjectArgs.packages_path is deprecated and " |
1847 | "should be set null."; |
1848 | } |
1849 | |
1850 | if (!IsRendererValid(config)) { |
1851 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
1852 | "The renderer configuration was invalid."); |
1853 | } |
1854 | |
1855 | std::string icu_data_path; |
1856 | if (SAFE_ACCESS(args, icu_data_path, nullptr) != nullptr) { |
1857 | icu_data_path = SAFE_ACCESS(args, icu_data_path, nullptr); |
1858 | } |
1859 | |
1860 | if (SAFE_ACCESS(args, persistent_cache_path, nullptr) != nullptr) { |
1861 | std::string persistent_cache_path = |
1862 | SAFE_ACCESS(args, persistent_cache_path, nullptr); |
1863 | flutter::PersistentCache::SetCacheDirectoryPath(persistent_cache_path); |
1864 | } |
1865 | |
1866 | if (SAFE_ACCESS(args, is_persistent_cache_read_only, false)) { |
1867 | flutter::PersistentCache::gIsReadOnly = true; |
1868 | } |
1869 | |
1870 | fml::CommandLine command_line; |
1871 | if (SAFE_ACCESS(args, command_line_argc, 0) != 0 && |
1872 | SAFE_ACCESS(args, command_line_argv, nullptr) != nullptr) { |
1873 | command_line = fml::CommandLineFromArgcArgv( |
1874 | SAFE_ACCESS(args, command_line_argc, 0), |
1875 | SAFE_ACCESS(args, command_line_argv, nullptr)); |
1876 | } |
1877 | |
1878 | flutter::Settings settings = flutter::SettingsFromCommandLine(command_line); |
1879 | |
1880 | if (SAFE_ACCESS(args, aot_data, nullptr)) { |
1881 | if (SAFE_ACCESS(args, vm_snapshot_data, nullptr) || |
1882 | SAFE_ACCESS(args, vm_snapshot_instructions, nullptr) || |
1883 | SAFE_ACCESS(args, isolate_snapshot_data, nullptr) || |
1884 | SAFE_ACCESS(args, isolate_snapshot_instructions, nullptr)) { |
1885 | return LOG_EMBEDDER_ERROR( |
1886 | kInvalidArguments, |
1887 | "Multiple AOT sources specified. Embedders should provide either " |
1888 | "*_snapshot_* buffers or aot_data, not both."); |
1889 | } |
1890 | } |
1891 | |
1892 | if (flutter::DartVM::IsRunningPrecompiledCode()) { |
1893 | PopulateAOTSnapshotMappingCallbacks(args, settings); |
1894 | } else { |
1895 | PopulateJITSnapshotMappingCallbacks(args, settings); |
1896 | } |
1897 | |
1898 | settings.icu_data_path = icu_data_path; |
1899 | settings.assets_path = args->assets_path; |
1900 | settings.leak_vm = !SAFE_ACCESS(args, shutdown_dart_vm_when_done, false); |
1901 | settings.old_gen_heap_size = SAFE_ACCESS(args, dart_old_gen_heap_size, -1); |
1902 | |
1903 | if (!flutter::DartVM::IsRunningPrecompiledCode()) { |
1904 | // Verify the assets path contains Dart 2 kernel assets. |
1905 | const std::string kApplicationKernelSnapshotFileName = "kernel_blob.bin"; |
1906 | std::string application_kernel_path = fml::paths::JoinPaths( |
1907 | components: {settings.assets_path, kApplicationKernelSnapshotFileName}); |
1908 | if (!fml::IsFile(path: application_kernel_path)) { |
1909 | return LOG_EMBEDDER_ERROR( |
1910 | kInvalidArguments, |
1911 | "Not running in AOT mode but could not resolve the kernel binary."); |
1912 | } |
1913 | settings.application_kernel_asset = kApplicationKernelSnapshotFileName; |
1914 | } |
1915 | |
1916 | settings.task_observer_add = [](intptr_t key, const fml::closure& callback) { |
1917 | fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback); |
1918 | }; |
1919 | settings.task_observer_remove = [](intptr_t key) { |
1920 | fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); |
1921 | }; |
1922 | if (SAFE_ACCESS(args, root_isolate_create_callback, nullptr) != nullptr) { |
1923 | VoidCallback callback = |
1924 | SAFE_ACCESS(args, root_isolate_create_callback, nullptr); |
1925 | settings.root_isolate_create_callback = |
1926 | [callback, user_data](const auto& isolate) { callback(user_data); }; |
1927 | } |
1928 | if (SAFE_ACCESS(args, log_message_callback, nullptr) != nullptr) { |
1929 | FlutterLogMessageCallback callback = |
1930 | SAFE_ACCESS(args, log_message_callback, nullptr); |
1931 | settings.log_message_callback = [callback, user_data]( |
1932 | const std::string& tag, |
1933 | const std::string& message) { |
1934 | callback(tag.c_str(), message.c_str(), user_data); |
1935 | }; |
1936 | } |
1937 | if (SAFE_ACCESS(args, log_tag, nullptr) != nullptr) { |
1938 | settings.log_tag = SAFE_ACCESS(args, log_tag, nullptr); |
1939 | } |
1940 | |
1941 | bool has_update_semantics_2_callback = |
1942 | SAFE_ACCESS(args, update_semantics_callback2, nullptr) != nullptr; |
1943 | bool has_update_semantics_callback = |
1944 | SAFE_ACCESS(args, update_semantics_callback, nullptr) != nullptr; |
1945 | bool has_legacy_update_semantics_callback = |
1946 | SAFE_ACCESS(args, update_semantics_node_callback, nullptr) != nullptr || |
1947 | SAFE_ACCESS(args, update_semantics_custom_action_callback, nullptr) != |
1948 | nullptr; |
1949 | |
1950 | int semantic_callback_count = (has_update_semantics_2_callback ? 1 : 0) + |
1951 | (has_update_semantics_callback ? 1 : 0) + |
1952 | (has_legacy_update_semantics_callback ? 1 : 0); |
1953 | |
1954 | if (semantic_callback_count > 1) { |
1955 | return LOG_EMBEDDER_ERROR( |
1956 | kInvalidArguments, |
1957 | "Multiple semantics update callbacks provided. " |
1958 | "Embedders should provide either `update_semantics_callback2`, " |
1959 | "`update_semantics_callback`, or both " |
1960 | "`update_semantics_node_callback` and " |
1961 | "`update_semantics_custom_action_callback`."); |
1962 | } |
1963 | |
1964 | flutter::PlatformViewEmbedder::UpdateSemanticsCallback |
1965 | update_semantics_callback = |
1966 | CreateEmbedderSemanticsUpdateCallback(args, user_data); |
1967 | |
1968 | flutter::PlatformViewEmbedder::PlatformMessageResponseCallback |
1969 | platform_message_response_callback = nullptr; |
1970 | if (SAFE_ACCESS(args, platform_message_callback, nullptr) != nullptr) { |
1971 | platform_message_response_callback = |
1972 | [ptr = args->platform_message_callback, |
1973 | user_data](std::unique_ptr<flutter::PlatformMessage> message) { |
1974 | auto handle = new FlutterPlatformMessageResponseHandle(); |
1975 | const FlutterPlatformMessage incoming_message = { |
1976 | .struct_size: sizeof(FlutterPlatformMessage), // struct_size |
1977 | .channel: message->channel().c_str(), // channel |
1978 | .message: message->data().GetMapping(), // message |
1979 | .message_size: message->data().GetSize(), // message_size |
1980 | .response_handle: handle, // response_handle |
1981 | }; |
1982 | handle->message = std::move(message); |
1983 | return ptr(&incoming_message, user_data); |
1984 | }; |
1985 | } |
1986 | |
1987 | flutter::VsyncWaiterEmbedder::VsyncCallback vsync_callback = nullptr; |
1988 | if (SAFE_ACCESS(args, vsync_callback, nullptr) != nullptr) { |
1989 | vsync_callback = [ptr = args->vsync_callback, user_data](intptr_t baton) { |
1990 | return ptr(user_data, baton); |
1991 | }; |
1992 | } |
1993 | |
1994 | flutter::PlatformViewEmbedder::ComputePlatformResolvedLocaleCallback |
1995 | compute_platform_resolved_locale_callback = nullptr; |
1996 | if (SAFE_ACCESS(args, compute_platform_resolved_locale_callback, nullptr) != |
1997 | nullptr) { |
1998 | compute_platform_resolved_locale_callback = |
1999 | [ptr = args->compute_platform_resolved_locale_callback]( |
2000 | const std::vector<std::string>& supported_locales_data) { |
2001 | const size_t number_of_strings_per_locale = 3; |
2002 | size_t locale_count = |
2003 | supported_locales_data.size() / number_of_strings_per_locale; |
2004 | std::vector<FlutterLocale> supported_locales; |
2005 | std::vector<const FlutterLocale*> supported_locales_ptr; |
2006 | for (size_t i = 0; i < locale_count; ++i) { |
2007 | supported_locales.push_back( |
2008 | x: {.struct_size = sizeof(FlutterLocale), |
2009 | .language_code = |
2010 | supported_locales_data[i * number_of_strings_per_locale + |
2011 | 0] |
2012 | .c_str(), |
2013 | .country_code = |
2014 | supported_locales_data[i * number_of_strings_per_locale + |
2015 | 1] |
2016 | .c_str(), |
2017 | .script_code = |
2018 | supported_locales_data[i * number_of_strings_per_locale + |
2019 | 2] |
2020 | .c_str(), |
2021 | .variant_code = nullptr}); |
2022 | supported_locales_ptr.push_back(x: &supported_locales[i]); |
2023 | } |
2024 | |
2025 | const FlutterLocale* result = |
2026 | ptr(supported_locales_ptr.data(), locale_count); |
2027 | |
2028 | std::unique_ptr<std::vector<std::string>> out = |
2029 | std::make_unique<std::vector<std::string>>(); |
2030 | if (result) { |
2031 | std::string language_code(SAFE_ACCESS(result, language_code, "")); |
2032 | if (language_code != "") { |
2033 | out->push_back(x: language_code); |
2034 | out->emplace_back(SAFE_ACCESS(result, country_code, "")); |
2035 | out->emplace_back(SAFE_ACCESS(result, script_code, "")); |
2036 | } |
2037 | } |
2038 | return out; |
2039 | }; |
2040 | } |
2041 | |
2042 | flutter::PlatformViewEmbedder::OnPreEngineRestartCallback |
2043 | on_pre_engine_restart_callback = nullptr; |
2044 | if (SAFE_ACCESS(args, on_pre_engine_restart_callback, nullptr) != nullptr) { |
2045 | on_pre_engine_restart_callback = [ptr = |
2046 | args->on_pre_engine_restart_callback, |
2047 | user_data]() { return ptr(user_data); }; |
2048 | } |
2049 | |
2050 | auto external_view_embedder_result = InferExternalViewEmbedderFromArgs( |
2051 | SAFE_ACCESS(args, compositor, nullptr), enable_impeller: settings.enable_impeller); |
2052 | if (external_view_embedder_result.second) { |
2053 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
2054 | "Compositor arguments were invalid."); |
2055 | } |
2056 | |
2057 | flutter::PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table = |
2058 | { |
2059 | .update_semantics_callback: update_semantics_callback, // |
2060 | .platform_message_response_callback: platform_message_response_callback, // |
2061 | .vsync_callback: vsync_callback, // |
2062 | .compute_platform_resolved_locale_callback: compute_platform_resolved_locale_callback, // |
2063 | .on_pre_engine_restart_callback: on_pre_engine_restart_callback, // |
2064 | }; |
2065 | |
2066 | auto on_create_platform_view = InferPlatformViewCreationCallback( |
2067 | config, user_data, platform_dispatch_table, |
2068 | external_view_embedder: std::move(external_view_embedder_result.first), enable_impeller: settings.enable_impeller); |
2069 | |
2070 | if (!on_create_platform_view) { |
2071 | return LOG_EMBEDDER_ERROR( |
2072 | kInternalInconsistency, |
2073 | "Could not infer platform view creation callback."); |
2074 | } |
2075 | |
2076 | flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer = |
2077 | [](flutter::Shell& shell) { |
2078 | return std::make_unique<flutter::Rasterizer>(args&: shell); |
2079 | }; |
2080 | |
2081 | using ExternalTextureResolver = flutter::EmbedderExternalTextureResolver; |
2082 | std::unique_ptr<ExternalTextureResolver> external_texture_resolver; |
2083 | external_texture_resolver = std::make_unique<ExternalTextureResolver>(); |
2084 | |
2085 | #ifdef SHELL_ENABLE_GL |
2086 | flutter::EmbedderExternalTextureGL::ExternalTextureCallback |
2087 | external_texture_callback; |
2088 | if (config->type == kOpenGL) { |
2089 | const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl; |
2090 | if (SAFE_ACCESS(open_gl_config, gl_external_texture_frame_callback, |
2091 | nullptr) != nullptr) { |
2092 | external_texture_callback = |
2093 | [ptr = open_gl_config->gl_external_texture_frame_callback, user_data]( |
2094 | int64_t texture_identifier, size_t width, |
2095 | size_t height) -> std::unique_ptr<FlutterOpenGLTexture> { |
2096 | std::unique_ptr<FlutterOpenGLTexture> texture = |
2097 | std::make_unique<FlutterOpenGLTexture>(); |
2098 | if (!ptr(user_data, texture_identifier, width, height, texture.get())) { |
2099 | return nullptr; |
2100 | } |
2101 | return texture; |
2102 | }; |
2103 | external_texture_resolver = |
2104 | std::make_unique<ExternalTextureResolver>(args&: external_texture_callback); |
2105 | } |
2106 | } |
2107 | #endif |
2108 | #ifdef SHELL_ENABLE_METAL |
2109 | flutter::EmbedderExternalTextureMetal::ExternalTextureCallback |
2110 | external_texture_metal_callback; |
2111 | if (config->type == kMetal) { |
2112 | const FlutterMetalRendererConfig* metal_config = &config->metal; |
2113 | if (SAFE_ACCESS(metal_config, external_texture_frame_callback, nullptr)) { |
2114 | external_texture_metal_callback = |
2115 | [ptr = metal_config->external_texture_frame_callback, user_data]( |
2116 | int64_t texture_identifier, size_t width, |
2117 | size_t height) -> std::unique_ptr<FlutterMetalExternalTexture> { |
2118 | std::unique_ptr<FlutterMetalExternalTexture> texture = |
2119 | std::make_unique<FlutterMetalExternalTexture>(); |
2120 | texture->struct_size = sizeof(FlutterMetalExternalTexture); |
2121 | if (!ptr(user_data, texture_identifier, width, height, texture.get())) { |
2122 | return nullptr; |
2123 | } |
2124 | return texture; |
2125 | }; |
2126 | external_texture_resolver = std::make_unique<ExternalTextureResolver>( |
2127 | external_texture_metal_callback); |
2128 | } |
2129 | } |
2130 | #endif |
2131 | auto custom_task_runners = SAFE_ACCESS(args, custom_task_runners, nullptr); |
2132 | auto thread_config_callback = [&custom_task_runners]( |
2133 | const fml::Thread::ThreadConfig& config) { |
2134 | fml::Thread::SetCurrentThreadName(config); |
2135 | if (!custom_task_runners || !custom_task_runners->thread_priority_setter) { |
2136 | return; |
2137 | } |
2138 | FlutterThreadPriority priority = FlutterThreadPriority::kNormal; |
2139 | switch (config.priority) { |
2140 | case fml::Thread::ThreadPriority::BACKGROUND: |
2141 | priority = FlutterThreadPriority::kBackground; |
2142 | break; |
2143 | case fml::Thread::ThreadPriority::NORMAL: |
2144 | priority = FlutterThreadPriority::kNormal; |
2145 | break; |
2146 | case fml::Thread::ThreadPriority::DISPLAY: |
2147 | priority = FlutterThreadPriority::kDisplay; |
2148 | break; |
2149 | case fml::Thread::ThreadPriority::RASTER: |
2150 | priority = FlutterThreadPriority::kRaster; |
2151 | break; |
2152 | } |
2153 | custom_task_runners->thread_priority_setter(priority); |
2154 | }; |
2155 | auto thread_host = |
2156 | flutter::EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost( |
2157 | custom_task_runners, config_setter: thread_config_callback); |
2158 | |
2159 | if (!thread_host || !thread_host->IsValid()) { |
2160 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
2161 | "Could not set up or infer thread configuration " |
2162 | "to run the Flutter engine on."); |
2163 | } |
2164 | |
2165 | auto task_runners = thread_host->GetTaskRunners(); |
2166 | |
2167 | if (!task_runners.IsValid()) { |
2168 | return LOG_EMBEDDER_ERROR(kInternalInconsistency, |
2169 | "Task runner configuration was invalid."); |
2170 | } |
2171 | |
2172 | auto run_configuration = |
2173 | flutter::RunConfiguration::InferFromSettings(settings); |
2174 | |
2175 | if (SAFE_ACCESS(args, custom_dart_entrypoint, nullptr) != nullptr) { |
2176 | auto dart_entrypoint = std::string{args->custom_dart_entrypoint}; |
2177 | if (!dart_entrypoint.empty()) { |
2178 | run_configuration.SetEntrypoint(std::move(dart_entrypoint)); |
2179 | } |
2180 | } |
2181 | |
2182 | if (SAFE_ACCESS(args, dart_entrypoint_argc, 0) > 0) { |
2183 | if (SAFE_ACCESS(args, dart_entrypoint_argv, nullptr) == nullptr) { |
2184 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
2185 | "Could not determine Dart entrypoint arguments " |
2186 | "as dart_entrypoint_argc " |
2187 | "was set, but dart_entrypoint_argv was null."); |
2188 | } |
2189 | std::vector<std::string> arguments(args->dart_entrypoint_argc); |
2190 | for (int i = 0; i < args->dart_entrypoint_argc; ++i) { |
2191 | arguments[i] = std::string{args->dart_entrypoint_argv[i]}; |
2192 | } |
2193 | run_configuration.SetEntrypointArgs(std::move(arguments)); |
2194 | } |
2195 | |
2196 | if (!run_configuration.IsValid()) { |
2197 | return LOG_EMBEDDER_ERROR( |
2198 | kInvalidArguments, |
2199 | "Could not infer the Flutter project to run from given arguments."); |
2200 | } |
2201 | |
2202 | // Create the engine but don't launch the shell or run the root isolate. |
2203 | auto embedder_engine = std::make_unique<flutter::EmbedderEngine>( |
2204 | args: std::move(thread_host), // |
2205 | args: std::move(task_runners), // |
2206 | args: std::move(settings), // |
2207 | args: std::move(run_configuration), // |
2208 | args&: on_create_platform_view, // |
2209 | args&: on_create_rasterizer, // |
2210 | args: std::move(external_texture_resolver) // |
2211 | ); |
2212 | |
2213 | // Release the ownership of the embedder engine to the caller. |
2214 | *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>( |
2215 | embedder_engine.release()); |
2216 | return kSuccess; |
2217 | } |
2218 | |
2219 | FlutterEngineResult FlutterEngineRunInitialized( |
2220 | FLUTTER_API_SYMBOL(FlutterEngine) engine) { |
2221 | if (!engine) { |
2222 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); |
2223 | } |
2224 | |
2225 | auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine); |
2226 | |
2227 | // The engine must not already be running. Initialize may only be called |
2228 | // once on an engine instance. |
2229 | if (embedder_engine->IsValid()) { |
2230 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); |
2231 | } |
2232 | |
2233 | // Step 1: Launch the shell. |
2234 | if (!embedder_engine->LaunchShell()) { |
2235 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
2236 | "Could not launch the engine using supplied " |
2237 | "initialization arguments."); |
2238 | } |
2239 | |
2240 | // Step 2: Tell the platform view to initialize itself. |
2241 | if (!embedder_engine->NotifyCreated()) { |
2242 | return LOG_EMBEDDER_ERROR(kInternalInconsistency, |
2243 | "Could not create platform view components."); |
2244 | } |
2245 | |
2246 | // Step 3: Launch the root isolate. |
2247 | if (!embedder_engine->RunRootIsolate()) { |
2248 | return LOG_EMBEDDER_ERROR( |
2249 | kInvalidArguments, |
2250 | "Could not run the root isolate of the Flutter application using the " |
2251 | "project arguments specified."); |
2252 | } |
2253 | |
2254 | return kSuccess; |
2255 | } |
2256 | |
2257 | FLUTTER_EXPORT |
2258 | FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine) |
2259 | engine) { |
2260 | if (engine == nullptr) { |
2261 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); |
2262 | } |
2263 | |
2264 | auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine); |
2265 | embedder_engine->NotifyDestroyed(); |
2266 | embedder_engine->CollectShell(); |
2267 | return kSuccess; |
2268 | } |
2269 | |
2270 | FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) |
2271 | engine) { |
2272 | auto result = FlutterEngineDeinitialize(engine); |
2273 | if (result != kSuccess) { |
2274 | return result; |
2275 | } |
2276 | auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine); |
2277 | delete embedder_engine; |
2278 | return kSuccess; |
2279 | } |
2280 | |
2281 | FlutterEngineResult FlutterEngineSendWindowMetricsEvent( |
2282 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2283 | const FlutterWindowMetricsEvent* flutter_metrics) { |
2284 | if (engine == nullptr || flutter_metrics == nullptr) { |
2285 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); |
2286 | } |
2287 | // TODO(dkwingsmt): Use a real view ID when multiview is supported. |
2288 | int64_t view_id = kFlutterImplicitViewId; |
2289 | |
2290 | flutter::ViewportMetrics metrics; |
2291 | |
2292 | metrics.physical_width = SAFE_ACCESS(flutter_metrics, width, 0.0); |
2293 | metrics.physical_height = SAFE_ACCESS(flutter_metrics, height, 0.0); |
2294 | metrics.device_pixel_ratio = SAFE_ACCESS(flutter_metrics, pixel_ratio, 1.0); |
2295 | metrics.physical_view_inset_top = |
2296 | SAFE_ACCESS(flutter_metrics, physical_view_inset_top, 0.0); |
2297 | metrics.physical_view_inset_right = |
2298 | SAFE_ACCESS(flutter_metrics, physical_view_inset_right, 0.0); |
2299 | metrics.physical_view_inset_bottom = |
2300 | SAFE_ACCESS(flutter_metrics, physical_view_inset_bottom, 0.0); |
2301 | metrics.physical_view_inset_left = |
2302 | SAFE_ACCESS(flutter_metrics, physical_view_inset_left, 0.0); |
2303 | metrics.display_id = SAFE_ACCESS(flutter_metrics, display_id, 0); |
2304 | |
2305 | if (metrics.device_pixel_ratio <= 0.0) { |
2306 | return LOG_EMBEDDER_ERROR( |
2307 | kInvalidArguments, |
2308 | "Device pixel ratio was invalid. It must be greater than zero."); |
2309 | } |
2310 | |
2311 | if (metrics.physical_view_inset_top < 0 || |
2312 | metrics.physical_view_inset_right < 0 || |
2313 | metrics.physical_view_inset_bottom < 0 || |
2314 | metrics.physical_view_inset_left < 0) { |
2315 | return LOG_EMBEDDER_ERROR( |
2316 | kInvalidArguments, |
2317 | "Physical view insets are invalid. They must be non-negative."); |
2318 | } |
2319 | |
2320 | if (metrics.physical_view_inset_top > metrics.physical_height || |
2321 | metrics.physical_view_inset_right > metrics.physical_width || |
2322 | metrics.physical_view_inset_bottom > metrics.physical_height || |
2323 | metrics.physical_view_inset_left > metrics.physical_width) { |
2324 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
2325 | "Physical view insets are invalid. They cannot " |
2326 | "be greater than physical height or width."); |
2327 | } |
2328 | |
2329 | return reinterpret_cast<flutter::EmbedderEngine*>(engine)->SetViewportMetrics( |
2330 | view_id, metrics) |
2331 | ? kSuccess |
2332 | : LOG_EMBEDDER_ERROR(kInvalidArguments, |
2333 | "Viewport metrics were invalid."); |
2334 | } |
2335 | |
2336 | // Returns the flutter::PointerData::Change for the given FlutterPointerPhase. |
2337 | inline flutter::PointerData::Change ToPointerDataChange( |
2338 | FlutterPointerPhase phase) { |
2339 | switch (phase) { |
2340 | case kCancel: |
2341 | return flutter::PointerData::Change::kCancel; |
2342 | case kUp: |
2343 | return flutter::PointerData::Change::kUp; |
2344 | case kDown: |
2345 | return flutter::PointerData::Change::kDown; |
2346 | case kMove: |
2347 | return flutter::PointerData::Change::kMove; |
2348 | case kAdd: |
2349 | return flutter::PointerData::Change::kAdd; |
2350 | case kRemove: |
2351 | return flutter::PointerData::Change::kRemove; |
2352 | case kHover: |
2353 | return flutter::PointerData::Change::kHover; |
2354 | case kPanZoomStart: |
2355 | return flutter::PointerData::Change::kPanZoomStart; |
2356 | case kPanZoomUpdate: |
2357 | return flutter::PointerData::Change::kPanZoomUpdate; |
2358 | case kPanZoomEnd: |
2359 | return flutter::PointerData::Change::kPanZoomEnd; |
2360 | } |
2361 | return flutter::PointerData::Change::kCancel; |
2362 | } |
2363 | |
2364 | // Returns the flutter::PointerData::DeviceKind for the given |
2365 | // FlutterPointerDeviceKind. |
2366 | inline flutter::PointerData::DeviceKind ToPointerDataKind( |
2367 | FlutterPointerDeviceKind device_kind) { |
2368 | switch (device_kind) { |
2369 | case kFlutterPointerDeviceKindMouse: |
2370 | return flutter::PointerData::DeviceKind::kMouse; |
2371 | case kFlutterPointerDeviceKindTouch: |
2372 | return flutter::PointerData::DeviceKind::kTouch; |
2373 | case kFlutterPointerDeviceKindStylus: |
2374 | return flutter::PointerData::DeviceKind::kStylus; |
2375 | case kFlutterPointerDeviceKindTrackpad: |
2376 | return flutter::PointerData::DeviceKind::kTrackpad; |
2377 | } |
2378 | return flutter::PointerData::DeviceKind::kMouse; |
2379 | } |
2380 | |
2381 | // Returns the flutter::PointerData::SignalKind for the given |
2382 | // FlutterPointerSignaKind. |
2383 | inline flutter::PointerData::SignalKind ToPointerDataSignalKind( |
2384 | FlutterPointerSignalKind kind) { |
2385 | switch (kind) { |
2386 | case kFlutterPointerSignalKindNone: |
2387 | return flutter::PointerData::SignalKind::kNone; |
2388 | case kFlutterPointerSignalKindScroll: |
2389 | return flutter::PointerData::SignalKind::kScroll; |
2390 | case kFlutterPointerSignalKindScrollInertiaCancel: |
2391 | return flutter::PointerData::SignalKind::kScrollInertiaCancel; |
2392 | case kFlutterPointerSignalKindScale: |
2393 | return flutter::PointerData::SignalKind::kScale; |
2394 | } |
2395 | return flutter::PointerData::SignalKind::kNone; |
2396 | } |
2397 | |
2398 | // Returns the buttons to synthesize for a PointerData from a |
2399 | // FlutterPointerEvent with no type or buttons set. |
2400 | inline int64_t PointerDataButtonsForLegacyEvent( |
2401 | flutter::PointerData::Change change) { |
2402 | switch (change) { |
2403 | case flutter::PointerData::Change::kDown: |
2404 | case flutter::PointerData::Change::kMove: |
2405 | // These kinds of change must have a non-zero `buttons`, otherwise |
2406 | // gesture recognizers will ignore these events. |
2407 | return flutter::kPointerButtonMousePrimary; |
2408 | case flutter::PointerData::Change::kCancel: |
2409 | case flutter::PointerData::Change::kAdd: |
2410 | case flutter::PointerData::Change::kRemove: |
2411 | case flutter::PointerData::Change::kHover: |
2412 | case flutter::PointerData::Change::kUp: |
2413 | case flutter::PointerData::Change::kPanZoomStart: |
2414 | case flutter::PointerData::Change::kPanZoomUpdate: |
2415 | case flutter::PointerData::Change::kPanZoomEnd: |
2416 | return 0; |
2417 | } |
2418 | return 0; |
2419 | } |
2420 | |
2421 | FlutterEngineResult FlutterEngineSendPointerEvent( |
2422 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2423 | const FlutterPointerEvent* pointers, |
2424 | size_t events_count) { |
2425 | if (engine == nullptr) { |
2426 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); |
2427 | } |
2428 | |
2429 | if (pointers == nullptr || events_count == 0) { |
2430 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid pointer events."); |
2431 | } |
2432 | |
2433 | auto packet = std::make_unique<flutter::PointerDataPacket>(args&: events_count); |
2434 | |
2435 | const FlutterPointerEvent* current = pointers; |
2436 | |
2437 | for (size_t i = 0; i < events_count; ++i) { |
2438 | flutter::PointerData pointer_data; |
2439 | pointer_data.Clear(); |
2440 | // this is currely in use only on android embedding. |
2441 | pointer_data.embedder_id = 0; |
2442 | pointer_data.time_stamp = SAFE_ACCESS(current, timestamp, 0); |
2443 | pointer_data.change = ToPointerDataChange( |
2444 | SAFE_ACCESS(current, phase, FlutterPointerPhase::kCancel)); |
2445 | pointer_data.physical_x = SAFE_ACCESS(current, x, 0.0); |
2446 | pointer_data.physical_y = SAFE_ACCESS(current, y, 0.0); |
2447 | // Delta will be generated in pointer_data_packet_converter.cc. |
2448 | pointer_data.physical_delta_x = 0.0; |
2449 | pointer_data.physical_delta_y = 0.0; |
2450 | pointer_data.device = SAFE_ACCESS(current, device, 0); |
2451 | // Pointer identifier will be generated in |
2452 | // pointer_data_packet_converter.cc. |
2453 | pointer_data.pointer_identifier = 0; |
2454 | pointer_data.signal_kind = ToPointerDataSignalKind( |
2455 | SAFE_ACCESS(current, signal_kind, kFlutterPointerSignalKindNone)); |
2456 | pointer_data.scroll_delta_x = SAFE_ACCESS(current, scroll_delta_x, 0.0); |
2457 | pointer_data.scroll_delta_y = SAFE_ACCESS(current, scroll_delta_y, 0.0); |
2458 | FlutterPointerDeviceKind device_kind = SAFE_ACCESS(current, device_kind, 0); |
2459 | // For backwards compatibility with embedders written before the device |
2460 | // kind and buttons were exposed, if the device kind is not set treat it |
2461 | // as a mouse, with a synthesized primary button state based on the phase. |
2462 | if (device_kind == 0) { |
2463 | pointer_data.kind = flutter::PointerData::DeviceKind::kMouse; |
2464 | pointer_data.buttons = |
2465 | PointerDataButtonsForLegacyEvent(change: pointer_data.change); |
2466 | |
2467 | } else { |
2468 | pointer_data.kind = ToPointerDataKind(device_kind); |
2469 | if (pointer_data.kind == flutter::PointerData::DeviceKind::kTouch) { |
2470 | // For touch events, set the button internally rather than requiring |
2471 | // it at the API level, since it's a confusing construction to expose. |
2472 | if (pointer_data.change == flutter::PointerData::Change::kDown || |
2473 | pointer_data.change == flutter::PointerData::Change::kMove) { |
2474 | pointer_data.buttons = flutter::kPointerButtonTouchContact; |
2475 | } |
2476 | } else { |
2477 | // Buttons use the same mask values, so pass them through directly. |
2478 | pointer_data.buttons = SAFE_ACCESS(current, buttons, 0); |
2479 | } |
2480 | } |
2481 | pointer_data.pan_x = SAFE_ACCESS(current, pan_x, 0.0); |
2482 | pointer_data.pan_y = SAFE_ACCESS(current, pan_y, 0.0); |
2483 | // Delta will be generated in pointer_data_packet_converter.cc. |
2484 | pointer_data.pan_delta_x = 0.0; |
2485 | pointer_data.pan_delta_y = 0.0; |
2486 | pointer_data.scale = SAFE_ACCESS(current, scale, 0.0); |
2487 | pointer_data.rotation = SAFE_ACCESS(current, rotation, 0.0); |
2488 | packet->SetPointerData(i, data: pointer_data); |
2489 | current = reinterpret_cast<const FlutterPointerEvent*>( |
2490 | reinterpret_cast<const uint8_t*>(current) + current->struct_size); |
2491 | } |
2492 | |
2493 | return reinterpret_cast<flutter::EmbedderEngine*>(engine) |
2494 | ->DispatchPointerDataPacket(packet: std::move(packet)) |
2495 | ? kSuccess |
2496 | : LOG_EMBEDDER_ERROR(kInternalInconsistency, |
2497 | "Could not dispatch pointer events to the " |
2498 | "running Flutter application."); |
2499 | } |
2500 | |
2501 | static inline flutter::KeyEventType MapKeyEventType( |
2502 | FlutterKeyEventType event_kind) { |
2503 | switch (event_kind) { |
2504 | case kFlutterKeyEventTypeUp: |
2505 | return flutter::KeyEventType::kUp; |
2506 | case kFlutterKeyEventTypeDown: |
2507 | return flutter::KeyEventType::kDown; |
2508 | case kFlutterKeyEventTypeRepeat: |
2509 | return flutter::KeyEventType::kRepeat; |
2510 | } |
2511 | return flutter::KeyEventType::kUp; |
2512 | } |
2513 | |
2514 | // Send a platform message to the framework. |
2515 | // |
2516 | // The `data_callback` will be invoked with `user_data`, and must not be empty. |
2517 | static FlutterEngineResult InternalSendPlatformMessage( |
2518 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2519 | const char* channel, |
2520 | const uint8_t* data, |
2521 | size_t size, |
2522 | FlutterDataCallback data_callback, |
2523 | void* user_data) { |
2524 | FlutterEngineResult result; |
2525 | |
2526 | FlutterPlatformMessageResponseHandle* response_handle; |
2527 | result = FlutterPlatformMessageCreateResponseHandle( |
2528 | engine, data_callback, user_data, response_out: &response_handle); |
2529 | if (result != kSuccess) { |
2530 | return result; |
2531 | } |
2532 | |
2533 | const FlutterPlatformMessage message = { |
2534 | .struct_size: sizeof(FlutterPlatformMessage), // struct_size |
2535 | .channel: channel, // channel |
2536 | .message: data, // message |
2537 | .message_size: size, // message_size |
2538 | .response_handle: response_handle, // response_handle |
2539 | }; |
2540 | |
2541 | result = FlutterEngineSendPlatformMessage(engine, message: &message); |
2542 | // Whether `SendPlatformMessage` succeeds or not, the response handle must be |
2543 | // released. |
2544 | FlutterEngineResult release_result = |
2545 | FlutterPlatformMessageReleaseResponseHandle(engine, response: response_handle); |
2546 | if (result != kSuccess) { |
2547 | return result; |
2548 | } |
2549 | |
2550 | return release_result; |
2551 | } |
2552 | |
2553 | FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) |
2554 | engine, |
2555 | const FlutterKeyEvent* event, |
2556 | FlutterKeyEventCallback callback, |
2557 | void* user_data) { |
2558 | if (engine == nullptr) { |
2559 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); |
2560 | } |
2561 | |
2562 | if (event == nullptr) { |
2563 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid key event."); |
2564 | } |
2565 | |
2566 | const char* character = SAFE_ACCESS(event, character, nullptr); |
2567 | |
2568 | flutter::KeyData key_data; |
2569 | key_data.Clear(); |
2570 | key_data.timestamp = static_cast<uint64_t>(SAFE_ACCESS(event, timestamp, 0)); |
2571 | key_data.type = MapKeyEventType( |
2572 | SAFE_ACCESS(event, type, FlutterKeyEventType::kFlutterKeyEventTypeUp)); |
2573 | key_data.physical = SAFE_ACCESS(event, physical, 0); |
2574 | key_data.logical = SAFE_ACCESS(event, logical, 0); |
2575 | key_data.synthesized = SAFE_ACCESS(event, synthesized, false); |
2576 | |
2577 | auto packet = std::make_unique<flutter::KeyDataPacket>(args&: key_data, args&: character); |
2578 | |
2579 | struct MessageData { |
2580 | FlutterKeyEventCallback callback; |
2581 | void* user_data; |
2582 | }; |
2583 | |
2584 | MessageData* message_data = |
2585 | new MessageData{.callback = callback, .user_data = user_data}; |
2586 | |
2587 | return InternalSendPlatformMessage( |
2588 | engine, channel: kFlutterKeyDataChannel, data: packet->data().data(), |
2589 | size: packet->data().size(), |
2590 | data_callback: [](const uint8_t* data, size_t size, void* user_data) { |
2591 | auto message_data = std::unique_ptr<MessageData>( |
2592 | reinterpret_cast<MessageData*>(user_data)); |
2593 | if (message_data->callback == nullptr) { |
2594 | return; |
2595 | } |
2596 | bool handled = false; |
2597 | if (size == 1) { |
2598 | handled = *data != 0; |
2599 | } |
2600 | message_data->callback(handled, message_data->user_data); |
2601 | }, |
2602 | user_data: message_data); |
2603 | } |
2604 | |
2605 | FlutterEngineResult FlutterEngineSendPlatformMessage( |
2606 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2607 | const FlutterPlatformMessage* flutter_message) { |
2608 | if (engine == nullptr) { |
2609 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
2610 | } |
2611 | |
2612 | if (flutter_message == nullptr) { |
2613 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid message argument."); |
2614 | } |
2615 | |
2616 | if (SAFE_ACCESS(flutter_message, channel, nullptr) == nullptr) { |
2617 | return LOG_EMBEDDER_ERROR( |
2618 | kInvalidArguments, "Message argument did not specify a valid channel."); |
2619 | } |
2620 | |
2621 | size_t message_size = SAFE_ACCESS(flutter_message, message_size, 0); |
2622 | const uint8_t* message_data = SAFE_ACCESS(flutter_message, message, nullptr); |
2623 | |
2624 | if (message_size != 0 && message_data == nullptr) { |
2625 | return LOG_EMBEDDER_ERROR( |
2626 | kInvalidArguments, |
2627 | "Message size was non-zero but the message data was nullptr."); |
2628 | } |
2629 | |
2630 | const FlutterPlatformMessageResponseHandle* response_handle = |
2631 | SAFE_ACCESS(flutter_message, response_handle, nullptr); |
2632 | |
2633 | fml::RefPtr<flutter::PlatformMessageResponse> response; |
2634 | if (response_handle && response_handle->message) { |
2635 | response = response_handle->message->response(); |
2636 | } |
2637 | |
2638 | std::unique_ptr<flutter::PlatformMessage> message; |
2639 | if (message_size == 0) { |
2640 | message = std::make_unique<flutter::PlatformMessage>( |
2641 | args: flutter_message->channel, args&: response); |
2642 | } else { |
2643 | message = std::make_unique<flutter::PlatformMessage>( |
2644 | args: flutter_message->channel, |
2645 | args: fml::MallocMapping::Copy(begin: message_data, length: message_size), args&: response); |
2646 | } |
2647 | |
2648 | return reinterpret_cast<flutter::EmbedderEngine*>(engine) |
2649 | ->SendPlatformMessage(message: std::move(message)) |
2650 | ? kSuccess |
2651 | : LOG_EMBEDDER_ERROR(kInternalInconsistency, |
2652 | "Could not send a message to the running " |
2653 | "Flutter application."); |
2654 | } |
2655 | |
2656 | FlutterEngineResult FlutterPlatformMessageCreateResponseHandle( |
2657 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2658 | FlutterDataCallback data_callback, |
2659 | void* user_data, |
2660 | FlutterPlatformMessageResponseHandle** response_out) { |
2661 | if (engine == nullptr) { |
2662 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); |
2663 | } |
2664 | |
2665 | if (data_callback == nullptr || response_out == nullptr) { |
2666 | return LOG_EMBEDDER_ERROR( |
2667 | kInvalidArguments, "Data callback or the response handle was invalid."); |
2668 | } |
2669 | |
2670 | flutter::EmbedderPlatformMessageResponse::Callback response_callback = |
2671 | [user_data, data_callback](const uint8_t* data, size_t size) { |
2672 | data_callback(data, size, user_data); |
2673 | }; |
2674 | |
2675 | auto platform_task_runner = reinterpret_cast<flutter::EmbedderEngine*>(engine) |
2676 | ->GetTaskRunners() |
2677 | .GetPlatformTaskRunner(); |
2678 | |
2679 | auto handle = new FlutterPlatformMessageResponseHandle(); |
2680 | |
2681 | handle->message = std::make_unique<flutter::PlatformMessage>( |
2682 | args: "", // The channel is empty and unused as the response handle is going |
2683 | // to referenced directly in the |FlutterEngineSendPlatformMessage| |
2684 | // with the container message discarded. |
2685 | args: fml::MakeRefCounted<flutter::EmbedderPlatformMessageResponse>( |
2686 | args: std::move(platform_task_runner), args&: response_callback)); |
2687 | *response_out = handle; |
2688 | return kSuccess; |
2689 | } |
2690 | |
2691 | FlutterEngineResult FlutterPlatformMessageReleaseResponseHandle( |
2692 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2693 | FlutterPlatformMessageResponseHandle* response) { |
2694 | if (engine == nullptr) { |
2695 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
2696 | } |
2697 | |
2698 | if (response == nullptr) { |
2699 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid response handle."); |
2700 | } |
2701 | delete response; |
2702 | return kSuccess; |
2703 | } |
2704 | |
2705 | // Note: This can execute on any thread. |
2706 | FlutterEngineResult FlutterEngineSendPlatformMessageResponse( |
2707 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2708 | const FlutterPlatformMessageResponseHandle* handle, |
2709 | const uint8_t* data, |
2710 | size_t data_length) { |
2711 | if (data_length != 0 && data == nullptr) { |
2712 | return LOG_EMBEDDER_ERROR( |
2713 | kInvalidArguments, |
2714 | "Data size was non zero but the pointer to the data was null."); |
2715 | } |
2716 | |
2717 | auto response = handle->message->response(); |
2718 | |
2719 | if (response) { |
2720 | if (data_length == 0) { |
2721 | response->CompleteEmpty(); |
2722 | } else { |
2723 | response->Complete(data: std::make_unique<fml::DataMapping>( |
2724 | args: std::vector<uint8_t>({data, data + data_length}))); |
2725 | } |
2726 | } |
2727 | |
2728 | delete handle; |
2729 | |
2730 | return kSuccess; |
2731 | } |
2732 | |
2733 | FlutterEngineResult __FlutterEngineFlushPendingTasksNow() { |
2734 | fml::MessageLoop::GetCurrent().RunExpiredTasksNow(); |
2735 | return kSuccess; |
2736 | } |
2737 | |
2738 | FlutterEngineResult FlutterEngineRegisterExternalTexture( |
2739 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2740 | int64_t texture_identifier) { |
2741 | if (engine == nullptr) { |
2742 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); |
2743 | } |
2744 | |
2745 | if (texture_identifier == 0) { |
2746 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
2747 | "Texture identifier was invalid."); |
2748 | } |
2749 | if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->RegisterTexture( |
2750 | texture: texture_identifier)) { |
2751 | return LOG_EMBEDDER_ERROR(kInternalInconsistency, |
2752 | "Could not register the specified texture."); |
2753 | } |
2754 | return kSuccess; |
2755 | } |
2756 | |
2757 | FlutterEngineResult FlutterEngineUnregisterExternalTexture( |
2758 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2759 | int64_t texture_identifier) { |
2760 | if (engine == nullptr) { |
2761 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); |
2762 | } |
2763 | |
2764 | if (texture_identifier == 0) { |
2765 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
2766 | "Texture identifier was invalid."); |
2767 | } |
2768 | |
2769 | if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->UnregisterTexture( |
2770 | texture: texture_identifier)) { |
2771 | return LOG_EMBEDDER_ERROR(kInternalInconsistency, |
2772 | "Could not un-register the specified texture."); |
2773 | } |
2774 | |
2775 | return kSuccess; |
2776 | } |
2777 | |
2778 | FlutterEngineResult FlutterEngineMarkExternalTextureFrameAvailable( |
2779 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2780 | int64_t texture_identifier) { |
2781 | if (engine == nullptr) { |
2782 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
2783 | } |
2784 | if (texture_identifier == 0) { |
2785 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid texture identifier."); |
2786 | } |
2787 | if (!reinterpret_cast<flutter::EmbedderEngine*>(engine) |
2788 | ->MarkTextureFrameAvailable(texture: texture_identifier)) { |
2789 | return LOG_EMBEDDER_ERROR( |
2790 | kInternalInconsistency, |
2791 | "Could not mark the texture frame as being available."); |
2792 | } |
2793 | return kSuccess; |
2794 | } |
2795 | |
2796 | FlutterEngineResult FlutterEngineUpdateSemanticsEnabled( |
2797 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2798 | bool enabled) { |
2799 | if (engine == nullptr) { |
2800 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
2801 | } |
2802 | if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->SetSemanticsEnabled( |
2803 | enabled)) { |
2804 | return LOG_EMBEDDER_ERROR(kInternalInconsistency, |
2805 | "Could not update semantics state."); |
2806 | } |
2807 | return kSuccess; |
2808 | } |
2809 | |
2810 | FlutterEngineResult FlutterEngineUpdateAccessibilityFeatures( |
2811 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2812 | FlutterAccessibilityFeature flags) { |
2813 | if (engine == nullptr) { |
2814 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
2815 | } |
2816 | if (!reinterpret_cast<flutter::EmbedderEngine*>(engine) |
2817 | ->SetAccessibilityFeatures(flags)) { |
2818 | return LOG_EMBEDDER_ERROR(kInternalInconsistency, |
2819 | "Could not update accessibility features."); |
2820 | } |
2821 | return kSuccess; |
2822 | } |
2823 | |
2824 | FlutterEngineResult FlutterEngineDispatchSemanticsAction( |
2825 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2826 | uint64_t node_id, |
2827 | FlutterSemanticsAction action, |
2828 | const uint8_t* data, |
2829 | size_t data_length) { |
2830 | if (engine == nullptr) { |
2831 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
2832 | } |
2833 | auto engine_action = static_cast<flutter::SemanticsAction>(action); |
2834 | if (!reinterpret_cast<flutter::EmbedderEngine*>(engine) |
2835 | ->DispatchSemanticsAction( |
2836 | node_id, action: engine_action, |
2837 | args: fml::MallocMapping::Copy(begin: data, length: data_length))) { |
2838 | return LOG_EMBEDDER_ERROR(kInternalInconsistency, |
2839 | "Could not dispatch semantics action."); |
2840 | } |
2841 | return kSuccess; |
2842 | } |
2843 | |
2844 | FlutterEngineResult FlutterEngineOnVsync(FLUTTER_API_SYMBOL(FlutterEngine) |
2845 | engine, |
2846 | intptr_t baton, |
2847 | uint64_t frame_start_time_nanos, |
2848 | uint64_t frame_target_time_nanos) { |
2849 | if (engine == nullptr) { |
2850 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
2851 | } |
2852 | |
2853 | TRACE_EVENT0("flutter", "FlutterEngineOnVsync"); |
2854 | |
2855 | auto start_time = fml::TimePoint::FromEpochDelta( |
2856 | ticks: fml::TimeDelta::FromNanoseconds(nanos: frame_start_time_nanos)); |
2857 | |
2858 | auto target_time = fml::TimePoint::FromEpochDelta( |
2859 | ticks: fml::TimeDelta::FromNanoseconds(nanos: frame_target_time_nanos)); |
2860 | |
2861 | if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->OnVsyncEvent( |
2862 | baton, frame_start_time: start_time, frame_target_time: target_time)) { |
2863 | return LOG_EMBEDDER_ERROR( |
2864 | kInternalInconsistency, |
2865 | "Could not notify the running engine instance of a Vsync event."); |
2866 | } |
2867 | |
2868 | return kSuccess; |
2869 | } |
2870 | |
2871 | FlutterEngineResult FlutterEngineReloadSystemFonts( |
2872 | FLUTTER_API_SYMBOL(FlutterEngine) engine) { |
2873 | if (engine == nullptr) { |
2874 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
2875 | } |
2876 | |
2877 | TRACE_EVENT0("flutter", "FlutterEngineReloadSystemFonts"); |
2878 | |
2879 | if (!reinterpret_cast<flutter::EmbedderEngine*>(engine) |
2880 | ->ReloadSystemFonts()) { |
2881 | return LOG_EMBEDDER_ERROR(kInternalInconsistency, |
2882 | "Could not reload system fonts."); |
2883 | } |
2884 | |
2885 | return kSuccess; |
2886 | } |
2887 | |
2888 | void FlutterEngineTraceEventDurationBegin(const char* name) { |
2889 | fml::tracing::TraceEvent0(category_group: "flutter", name, /*flow_id_count=*/0, |
2890 | /*flow_id=*/flow_ids: nullptr); |
2891 | } |
2892 | |
2893 | void FlutterEngineTraceEventDurationEnd(const char* name) { |
2894 | fml::tracing::TraceEventEnd(name); |
2895 | } |
2896 | |
2897 | void FlutterEngineTraceEventInstant(const char* name) { |
2898 | fml::tracing::TraceEventInstant0(category_group: "flutter", name, /*flow_id_count=*/0, |
2899 | /*flow_id=*/flow_ids: nullptr); |
2900 | } |
2901 | |
2902 | FlutterEngineResult FlutterEnginePostRenderThreadTask( |
2903 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
2904 | VoidCallback callback, |
2905 | void* baton) { |
2906 | if (engine == nullptr) { |
2907 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
2908 | } |
2909 | |
2910 | if (callback == nullptr) { |
2911 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
2912 | "Render thread callback was null."); |
2913 | } |
2914 | |
2915 | auto task = [callback, baton]() { callback(baton); }; |
2916 | |
2917 | return reinterpret_cast<flutter::EmbedderEngine*>(engine) |
2918 | ->PostRenderThreadTask(task) |
2919 | ? kSuccess |
2920 | : LOG_EMBEDDER_ERROR(kInternalInconsistency, |
2921 | "Could not post the render thread task."); |
2922 | } |
2923 | |
2924 | uint64_t FlutterEngineGetCurrentTime() { |
2925 | return fml::TimePoint::Now().ToEpochDelta().ToNanoseconds(); |
2926 | } |
2927 | |
2928 | FlutterEngineResult FlutterEngineRunTask(FLUTTER_API_SYMBOL(FlutterEngine) |
2929 | engine, |
2930 | const FlutterTask* task) { |
2931 | if (engine == nullptr) { |
2932 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
2933 | } |
2934 | |
2935 | return reinterpret_cast<flutter::EmbedderEngine*>(engine)->RunTask(task) |
2936 | ? kSuccess |
2937 | : LOG_EMBEDDER_ERROR(kInvalidArguments, |
2938 | "Could not run the specified task."); |
2939 | } |
2940 | |
2941 | static bool DispatchJSONPlatformMessage(FLUTTER_API_SYMBOL(FlutterEngine) |
2942 | engine, |
2943 | const rapidjson::Document& document, |
2944 | const std::string& channel_name) { |
2945 | if (channel_name.empty()) { |
2946 | return false; |
2947 | } |
2948 | |
2949 | rapidjson::StringBuffer buffer; |
2950 | rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); |
2951 | |
2952 | if (!document.Accept(handler&: writer)) { |
2953 | return false; |
2954 | } |
2955 | |
2956 | const char* message = buffer.GetString(); |
2957 | |
2958 | if (message == nullptr || buffer.GetSize() == 0) { |
2959 | return false; |
2960 | } |
2961 | |
2962 | auto platform_message = std::make_unique<flutter::PlatformMessage>( |
2963 | args: channel_name.c_str(), // channel |
2964 | args: fml::MallocMapping::Copy(begin: message, |
2965 | length: buffer.GetSize()), // message |
2966 | args: nullptr // response |
2967 | ); |
2968 | |
2969 | return reinterpret_cast<flutter::EmbedderEngine*>(engine) |
2970 | ->SendPlatformMessage(message: std::move(platform_message)); |
2971 | } |
2972 | |
2973 | FlutterEngineResult FlutterEngineUpdateLocales(FLUTTER_API_SYMBOL(FlutterEngine) |
2974 | engine, |
2975 | const FlutterLocale** locales, |
2976 | size_t locales_count) { |
2977 | if (engine == nullptr) { |
2978 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
2979 | } |
2980 | |
2981 | if (locales_count == 0) { |
2982 | return kSuccess; |
2983 | } |
2984 | |
2985 | if (locales == nullptr) { |
2986 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "No locales were specified."); |
2987 | } |
2988 | |
2989 | rapidjson::Document document; |
2990 | auto& allocator = document.GetAllocator(); |
2991 | |
2992 | document.SetObject(); |
2993 | document.AddMember(name: "method", value: "setLocale", allocator); |
2994 | |
2995 | rapidjson::Value args(rapidjson::kArrayType); |
2996 | args.Reserve(newCapacity: locales_count * 4, allocator); |
2997 | for (size_t i = 0; i < locales_count; ++i) { |
2998 | const FlutterLocale* locale = locales[i]; |
2999 | const char* language_code_str = SAFE_ACCESS(locale, language_code, nullptr); |
3000 | if (language_code_str == nullptr || ::strlen(s: language_code_str) == 0) { |
3001 | return LOG_EMBEDDER_ERROR( |
3002 | kInvalidArguments, |
3003 | "Language code is required but not present in FlutterLocale."); |
3004 | } |
3005 | |
3006 | const char* country_code_str = SAFE_ACCESS(locale, country_code, ""); |
3007 | const char* script_code_str = SAFE_ACCESS(locale, script_code, ""); |
3008 | const char* variant_code_str = SAFE_ACCESS(locale, variant_code, ""); |
3009 | |
3010 | rapidjson::Value language_code, country_code, script_code, variant_code; |
3011 | |
3012 | language_code.SetString(s: language_code_str, allocator); |
3013 | country_code.SetString(s: country_code_str ? country_code_str : "", allocator); |
3014 | script_code.SetString(s: script_code_str ? script_code_str : "", allocator); |
3015 | variant_code.SetString(s: variant_code_str ? variant_code_str : "", allocator); |
3016 | |
3017 | // Required. |
3018 | args.PushBack(value&: language_code, allocator); |
3019 | args.PushBack(value&: country_code, allocator); |
3020 | args.PushBack(value&: script_code, allocator); |
3021 | args.PushBack(value&: variant_code, allocator); |
3022 | } |
3023 | document.AddMember(name: "args", value&: args, allocator); |
3024 | |
3025 | return DispatchJSONPlatformMessage(engine, document, channel_name: "flutter/localization") |
3026 | ? kSuccess |
3027 | : LOG_EMBEDDER_ERROR(kInternalInconsistency, |
3028 | "Could not send message to update locale of " |
3029 | "a running Flutter application."); |
3030 | } |
3031 | |
3032 | bool FlutterEngineRunsAOTCompiledDartCode(void) { |
3033 | return flutter::DartVM::IsRunningPrecompiledCode(); |
3034 | } |
3035 | |
3036 | FlutterEngineResult FlutterEnginePostDartObject( |
3037 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
3038 | FlutterEngineDartPort port, |
3039 | const FlutterEngineDartObject* object) { |
3040 | if (engine == nullptr) { |
3041 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
3042 | } |
3043 | |
3044 | if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->IsValid()) { |
3045 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine not running."); |
3046 | } |
3047 | |
3048 | if (port == ILLEGAL_PORT) { |
3049 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
3050 | "Attempted to post to an illegal port."); |
3051 | } |
3052 | |
3053 | if (object == nullptr) { |
3054 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
3055 | "Invalid Dart object to post."); |
3056 | } |
3057 | |
3058 | Dart_CObject dart_object = {}; |
3059 | fml::ScopedCleanupClosure typed_data_finalizer; |
3060 | |
3061 | switch (object->type) { |
3062 | case kFlutterEngineDartObjectTypeNull: |
3063 | dart_object.type = Dart_CObject_kNull; |
3064 | break; |
3065 | case kFlutterEngineDartObjectTypeBool: |
3066 | dart_object.type = Dart_CObject_kBool; |
3067 | dart_object.value.as_bool = object->bool_value; |
3068 | break; |
3069 | case kFlutterEngineDartObjectTypeInt32: |
3070 | dart_object.type = Dart_CObject_kInt32; |
3071 | dart_object.value.as_int32 = object->int32_value; |
3072 | break; |
3073 | case kFlutterEngineDartObjectTypeInt64: |
3074 | dart_object.type = Dart_CObject_kInt64; |
3075 | dart_object.value.as_int64 = object->int64_value; |
3076 | break; |
3077 | case kFlutterEngineDartObjectTypeDouble: |
3078 | dart_object.type = Dart_CObject_kDouble; |
3079 | dart_object.value.as_double = object->double_value; |
3080 | break; |
3081 | case kFlutterEngineDartObjectTypeString: |
3082 | if (object->string_value == nullptr) { |
3083 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
3084 | "kFlutterEngineDartObjectTypeString must be " |
3085 | "a null terminated string but was null."); |
3086 | } |
3087 | dart_object.type = Dart_CObject_kString; |
3088 | dart_object.value.as_string = const_cast<char*>(object->string_value); |
3089 | break; |
3090 | case kFlutterEngineDartObjectTypeBuffer: { |
3091 | auto* buffer = SAFE_ACCESS(object->buffer_value, buffer, nullptr); |
3092 | if (buffer == nullptr) { |
3093 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
3094 | "kFlutterEngineDartObjectTypeBuffer must " |
3095 | "specify a buffer but found nullptr."); |
3096 | } |
3097 | auto buffer_size = SAFE_ACCESS(object->buffer_value, buffer_size, 0); |
3098 | auto callback = |
3099 | SAFE_ACCESS(object->buffer_value, buffer_collect_callback, nullptr); |
3100 | auto user_data = SAFE_ACCESS(object->buffer_value, user_data, nullptr); |
3101 | |
3102 | // The user has provided a callback, let them manage the lifecycle of |
3103 | // the underlying data. If not, copy it out from the provided buffer. |
3104 | |
3105 | if (callback == nullptr) { |
3106 | dart_object.type = Dart_CObject_kTypedData; |
3107 | dart_object.value.as_typed_data.type = Dart_TypedData_kUint8; |
3108 | dart_object.value.as_typed_data.length = buffer_size; |
3109 | dart_object.value.as_typed_data.values = buffer; |
3110 | } else { |
3111 | struct ExternalTypedDataPeer { |
3112 | void* user_data = nullptr; |
3113 | VoidCallback trampoline = nullptr; |
3114 | }; |
3115 | auto peer = new ExternalTypedDataPeer(); |
3116 | peer->user_data = user_data; |
3117 | peer->trampoline = callback; |
3118 | // This finalizer is set so that in case of failure of the |
3119 | // Dart_PostCObject below, we collect the peer. The embedder is still |
3120 | // responsible for collecting the buffer in case of non-kSuccess |
3121 | // returns from this method. This finalizer must be released in case |
3122 | // of kSuccess returns from this method. |
3123 | typed_data_finalizer.SetClosure([peer]() { |
3124 | // This is the tiny object we use as the peer to the Dart call so |
3125 | // that we can attach the a trampoline to the embedder supplied |
3126 | // callback. In case of error, we need to collect this object lest |
3127 | // we introduce a tiny leak. |
3128 | delete peer; |
3129 | }); |
3130 | dart_object.type = Dart_CObject_kExternalTypedData; |
3131 | dart_object.value.as_external_typed_data.type = Dart_TypedData_kUint8; |
3132 | dart_object.value.as_external_typed_data.length = buffer_size; |
3133 | dart_object.value.as_external_typed_data.data = buffer; |
3134 | dart_object.value.as_external_typed_data.peer = peer; |
3135 | dart_object.value.as_external_typed_data.callback = |
3136 | +[](void* unused_isolate_callback_data, void* peer) { |
3137 | auto typed_peer = reinterpret_cast<ExternalTypedDataPeer*>(peer); |
3138 | typed_peer->trampoline(typed_peer->user_data); |
3139 | delete typed_peer; |
3140 | }; |
3141 | } |
3142 | } break; |
3143 | default: |
3144 | return LOG_EMBEDDER_ERROR( |
3145 | kInvalidArguments, |
3146 | "Invalid FlutterEngineDartObjectType type specified."); |
3147 | } |
3148 | |
3149 | if (!Dart_PostCObject(port_id: port, message: &dart_object)) { |
3150 | return LOG_EMBEDDER_ERROR(kInternalInconsistency, |
3151 | "Could not post the object to the Dart VM."); |
3152 | } |
3153 | |
3154 | // On a successful call, the VM takes ownership of and is responsible for |
3155 | // invoking the finalizer. |
3156 | typed_data_finalizer.Release(); |
3157 | return kSuccess; |
3158 | } |
3159 | |
3160 | FlutterEngineResult FlutterEngineNotifyLowMemoryWarning( |
3161 | FLUTTER_API_SYMBOL(FlutterEngine) raw_engine) { |
3162 | auto engine = reinterpret_cast<flutter::EmbedderEngine*>(raw_engine); |
3163 | if (engine == nullptr || !engine->IsValid()) { |
3164 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine was invalid."); |
3165 | } |
3166 | |
3167 | engine->GetShell().NotifyLowMemoryWarning(); |
3168 | |
3169 | rapidjson::Document document; |
3170 | auto& allocator = document.GetAllocator(); |
3171 | |
3172 | document.SetObject(); |
3173 | document.AddMember(name: "type", value: "memoryPressure", allocator); |
3174 | |
3175 | return DispatchJSONPlatformMessage(engine: raw_engine, document, channel_name: "flutter/system") |
3176 | ? kSuccess |
3177 | : LOG_EMBEDDER_ERROR( |
3178 | kInternalInconsistency, |
3179 | "Could not dispatch the low memory notification message."); |
3180 | } |
3181 | |
3182 | FlutterEngineResult FlutterEnginePostCallbackOnAllNativeThreads( |
3183 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
3184 | FlutterNativeThreadCallback callback, |
3185 | void* user_data) { |
3186 | if (engine == nullptr) { |
3187 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
3188 | } |
3189 | |
3190 | if (callback == nullptr) { |
3191 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
3192 | "Invalid native thread callback."); |
3193 | } |
3194 | |
3195 | return reinterpret_cast<flutter::EmbedderEngine*>(engine) |
3196 | ->PostTaskOnEngineManagedNativeThreads( |
3197 | closure: [callback, user_data](FlutterNativeThreadType type) { |
3198 | callback(type, user_data); |
3199 | }) |
3200 | ? kSuccess |
3201 | : LOG_EMBEDDER_ERROR(kInvalidArguments, |
3202 | "Internal error while attempting to post " |
3203 | "tasks to all threads."); |
3204 | } |
3205 | |
3206 | namespace { |
3207 | static bool ValidDisplayConfiguration(const FlutterEngineDisplay* displays, |
3208 | size_t display_count) { |
3209 | std::set<FlutterEngineDisplayId> display_ids; |
3210 | for (size_t i = 0; i < display_count; i++) { |
3211 | if (displays[i].single_display && display_count != 1) { |
3212 | return false; |
3213 | } |
3214 | display_ids.insert(v: displays[i].display_id); |
3215 | } |
3216 | |
3217 | return display_ids.size() == display_count; |
3218 | } |
3219 | } // namespace |
3220 | |
3221 | FlutterEngineResult FlutterEngineNotifyDisplayUpdate( |
3222 | FLUTTER_API_SYMBOL(FlutterEngine) raw_engine, |
3223 | const FlutterEngineDisplaysUpdateType update_type, |
3224 | const FlutterEngineDisplay* embedder_displays, |
3225 | size_t display_count) { |
3226 | if (raw_engine == nullptr) { |
3227 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
3228 | } |
3229 | |
3230 | if (!ValidDisplayConfiguration(displays: embedder_displays, display_count)) { |
3231 | return LOG_EMBEDDER_ERROR( |
3232 | kInvalidArguments, |
3233 | "Invalid FlutterEngineDisplay configuration specified."); |
3234 | } |
3235 | |
3236 | auto engine = reinterpret_cast<flutter::EmbedderEngine*>(raw_engine); |
3237 | |
3238 | switch (update_type) { |
3239 | case kFlutterEngineDisplaysUpdateTypeStartup: { |
3240 | std::vector<std::unique_ptr<flutter::Display>> displays; |
3241 | const auto* display = embedder_displays; |
3242 | for (size_t i = 0; i < display_count; i++) { |
3243 | displays.push_back(x: std::make_unique<flutter::Display>( |
3244 | SAFE_ACCESS(display, display_id, i), // |
3245 | SAFE_ACCESS(display, refresh_rate, 0), // |
3246 | SAFE_ACCESS(display, width, 0), // |
3247 | SAFE_ACCESS(display, height, 0), // |
3248 | SAFE_ACCESS(display, device_pixel_ratio, 1))); |
3249 | display = reinterpret_cast<const FlutterEngineDisplay*>( |
3250 | reinterpret_cast<const uint8_t*>(display) + display->struct_size); |
3251 | } |
3252 | engine->GetShell().OnDisplayUpdates(displays: std::move(displays)); |
3253 | return kSuccess; |
3254 | } |
3255 | default: |
3256 | return LOG_EMBEDDER_ERROR( |
3257 | kInvalidArguments, |
3258 | "Invalid FlutterEngineDisplaysUpdateType type specified."); |
3259 | } |
3260 | } |
3261 | |
3262 | FlutterEngineResult FlutterEngineScheduleFrame(FLUTTER_API_SYMBOL(FlutterEngine) |
3263 | engine) { |
3264 | if (engine == nullptr) { |
3265 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
3266 | } |
3267 | |
3268 | return reinterpret_cast<flutter::EmbedderEngine*>(engine)->ScheduleFrame() |
3269 | ? kSuccess |
3270 | : LOG_EMBEDDER_ERROR(kInvalidArguments, |
3271 | "Could not schedule frame."); |
3272 | } |
3273 | |
3274 | FlutterEngineResult FlutterEngineSetNextFrameCallback( |
3275 | FLUTTER_API_SYMBOL(FlutterEngine) engine, |
3276 | VoidCallback callback, |
3277 | void* user_data) { |
3278 | if (engine == nullptr) { |
3279 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); |
3280 | } |
3281 | |
3282 | if (callback == nullptr) { |
3283 | return LOG_EMBEDDER_ERROR(kInvalidArguments, |
3284 | "Next frame callback was null."); |
3285 | } |
3286 | |
3287 | flutter::EmbedderEngine* embedder_engine = |
3288 | reinterpret_cast<flutter::EmbedderEngine*>(engine); |
3289 | |
3290 | fml::WeakPtr<flutter::PlatformView> weak_platform_view = |
3291 | embedder_engine->GetShell().GetPlatformView(); |
3292 | |
3293 | if (!weak_platform_view) { |
3294 | return LOG_EMBEDDER_ERROR(kInternalInconsistency, |
3295 | "Platform view unavailable."); |
3296 | } |
3297 | |
3298 | weak_platform_view->SetNextFrameCallback( |
3299 | [callback, user_data]() { callback(user_data); }); |
3300 | |
3301 | return kSuccess; |
3302 | } |
3303 | |
3304 | FlutterEngineResult FlutterEngineGetProcAddresses( |
3305 | FlutterEngineProcTable* table) { |
3306 | if (!table) { |
3307 | return LOG_EMBEDDER_ERROR(kInvalidArguments, "Null table specified."); |
3308 | } |
3309 | #define SET_PROC(member, function) \ |
3310 | if (STRUCT_HAS_MEMBER(table, member)) { \ |
3311 | table->member = &function; \ |
3312 | } |
3313 | |
3314 | SET_PROC(CreateAOTData, FlutterEngineCreateAOTData); |
3315 | SET_PROC(CollectAOTData, FlutterEngineCollectAOTData); |
3316 | SET_PROC(Run, FlutterEngineRun); |
3317 | SET_PROC(Shutdown, FlutterEngineShutdown); |
3318 | SET_PROC(Initialize, FlutterEngineInitialize); |
3319 | SET_PROC(Deinitialize, FlutterEngineDeinitialize); |
3320 | SET_PROC(RunInitialized, FlutterEngineRunInitialized); |
3321 | SET_PROC(SendWindowMetricsEvent, FlutterEngineSendWindowMetricsEvent); |
3322 | SET_PROC(SendPointerEvent, FlutterEngineSendPointerEvent); |
3323 | SET_PROC(SendKeyEvent, FlutterEngineSendKeyEvent); |
3324 | SET_PROC(SendPlatformMessage, FlutterEngineSendPlatformMessage); |
3325 | SET_PROC(PlatformMessageCreateResponseHandle, |
3326 | FlutterPlatformMessageCreateResponseHandle); |
3327 | SET_PROC(PlatformMessageReleaseResponseHandle, |
3328 | FlutterPlatformMessageReleaseResponseHandle); |
3329 | SET_PROC(SendPlatformMessageResponse, |
3330 | FlutterEngineSendPlatformMessageResponse); |
3331 | SET_PROC(RegisterExternalTexture, FlutterEngineRegisterExternalTexture); |
3332 | SET_PROC(UnregisterExternalTexture, FlutterEngineUnregisterExternalTexture); |
3333 | SET_PROC(MarkExternalTextureFrameAvailable, |
3334 | FlutterEngineMarkExternalTextureFrameAvailable); |
3335 | SET_PROC(UpdateSemanticsEnabled, FlutterEngineUpdateSemanticsEnabled); |
3336 | SET_PROC(UpdateAccessibilityFeatures, |
3337 | FlutterEngineUpdateAccessibilityFeatures); |
3338 | SET_PROC(DispatchSemanticsAction, FlutterEngineDispatchSemanticsAction); |
3339 | SET_PROC(OnVsync, FlutterEngineOnVsync); |
3340 | SET_PROC(ReloadSystemFonts, FlutterEngineReloadSystemFonts); |
3341 | SET_PROC(TraceEventDurationBegin, FlutterEngineTraceEventDurationBegin); |
3342 | SET_PROC(TraceEventDurationEnd, FlutterEngineTraceEventDurationEnd); |
3343 | SET_PROC(TraceEventInstant, FlutterEngineTraceEventInstant); |
3344 | SET_PROC(PostRenderThreadTask, FlutterEnginePostRenderThreadTask); |
3345 | SET_PROC(GetCurrentTime, FlutterEngineGetCurrentTime); |
3346 | SET_PROC(RunTask, FlutterEngineRunTask); |
3347 | SET_PROC(UpdateLocales, FlutterEngineUpdateLocales); |
3348 | SET_PROC(RunsAOTCompiledDartCode, FlutterEngineRunsAOTCompiledDartCode); |
3349 | SET_PROC(PostDartObject, FlutterEnginePostDartObject); |
3350 | SET_PROC(NotifyLowMemoryWarning, FlutterEngineNotifyLowMemoryWarning); |
3351 | SET_PROC(PostCallbackOnAllNativeThreads, |
3352 | FlutterEnginePostCallbackOnAllNativeThreads); |
3353 | SET_PROC(NotifyDisplayUpdate, FlutterEngineNotifyDisplayUpdate); |
3354 | SET_PROC(ScheduleFrame, FlutterEngineScheduleFrame); |
3355 | SET_PROC(SetNextFrameCallback, FlutterEngineSetNextFrameCallback); |
3356 | #undef SET_PROC |
3357 | |
3358 | return kSuccess; |
3359 | } |
3360 |
Definitions
- kFlutterSemanticsNodeIdBatchEnd
- kFlutterSemanticsCustomActionIdBatchEnd
- kFlutterImplicitViewId
- kFlutterKeyDataChannel
- LogEmbedderError
- IsOpenGLRendererConfigValid
- IsSoftwareRendererConfigValid
- IsMetalRendererConfigValid
- IsVulkanRendererConfigValid
- IsRendererValid
- DefaultGLProcResolver
- SkIRectToFlutterRect
- FlutterRectToSkIRect
- InferOpenGLPlatformViewCreationCallback
- InferMetalPlatformViewCreationCallback
- InferVulkanPlatformViewCreationCallback
- InferSoftwarePlatformViewCreationCallback
- InferPlatformViewCreationCallback
- MakeSkSurfaceFromBackingStore
- MakeSkSurfaceFromBackingStore
- MakeSkSurfaceFromBackingStore
- MakeSkSurfaceFromBackingStore
- MakeSkSurfaceFromBackingStore
- MakeRenderTargetFromBackingStoreImpeller
- MakeRenderTargetFromBackingStoreImpeller
- MakeSkSurfaceFromBackingStore
- MakeRenderTargetFromSkSurface
- CreateEmbedderRenderTarget
- InferExternalViewEmbedderFromArgs
- _FlutterPlatformMessageResponseHandle
- LoadedElfDeleter
- operator()
- _FlutterEngineAOTData
- FlutterEngineCreateAOTData
- FlutterEngineCollectAOTData
- PopulateJITSnapshotMappingCallbacks
- PopulateAOTSnapshotMappingCallbacks
- CreateEmbedderSemanticsNode
- CreateEmbedderSemanticsNode2
- CreateEmbedderSemanticsCustomAction
- CreateEmbedderSemanticsCustomAction2
- CreateNewEmbedderSemanticsUpdateCallback
- CreateNewEmbedderSemanticsUpdateCallback2
- CreateLegacyEmbedderSemanticsUpdateCallback
- CreateEmbedderSemanticsUpdateCallback
- FlutterEngineRun
- FlutterEngineInitialize
- FlutterEngineRunInitialized
- FlutterEngineDeinitialize
- FlutterEngineShutdown
- FlutterEngineSendWindowMetricsEvent
- ToPointerDataChange
- ToPointerDataKind
- ToPointerDataSignalKind
- PointerDataButtonsForLegacyEvent
- FlutterEngineSendPointerEvent
- MapKeyEventType
- InternalSendPlatformMessage
- FlutterEngineSendKeyEvent
- FlutterEngineSendPlatformMessage
- FlutterPlatformMessageCreateResponseHandle
- FlutterPlatformMessageReleaseResponseHandle
- FlutterEngineSendPlatformMessageResponse
- __FlutterEngineFlushPendingTasksNow
- FlutterEngineRegisterExternalTexture
- FlutterEngineUnregisterExternalTexture
- FlutterEngineMarkExternalTextureFrameAvailable
- FlutterEngineUpdateSemanticsEnabled
- FlutterEngineUpdateAccessibilityFeatures
- FlutterEngineDispatchSemanticsAction
- FlutterEngineOnVsync
- FlutterEngineReloadSystemFonts
- FlutterEngineTraceEventDurationBegin
- FlutterEngineTraceEventDurationEnd
- FlutterEngineTraceEventInstant
- FlutterEnginePostRenderThreadTask
- FlutterEngineGetCurrentTime
- FlutterEngineRunTask
- DispatchJSONPlatformMessage
- FlutterEngineUpdateLocales
- FlutterEngineRunsAOTCompiledDartCode
- FlutterEnginePostDartObject
- FlutterEngineNotifyLowMemoryWarning
- FlutterEnginePostCallbackOnAllNativeThreads
- ValidDisplayConfiguration
- FlutterEngineNotifyDisplayUpdate
- FlutterEngineScheduleFrame
- FlutterEngineSetNextFrameCallback
Learn more about Flutter for embedded and desktop on industrialflutter.com